From 27cf27cff9724e99569d2edbf010758235f74d20 Mon Sep 17 00:00:00 2001 From: Daniel Puzzuoli Date: Tue, 14 Mar 2023 10:27:51 -0700 Subject: [PATCH 01/17] Fix error in schedule -> signal conversion for schedules with barrier instructions (#203) --- qiskit_dynamics/pulse/pulse_to_signals.py | 10 ++++----- .../0.4/0.4-summary-3de98711c3b7aa09.yaml | 3 +++ test/dynamics/pulse/test_pulse_to_signals.py | 22 +++++++++++++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/qiskit_dynamics/pulse/pulse_to_signals.py b/qiskit_dynamics/pulse/pulse_to_signals.py index b91f88bc9..1377d5941 100644 --- a/qiskit_dynamics/pulse/pulse_to_signals.py +++ b/qiskit_dynamics/pulse/pulse_to_signals.py @@ -163,9 +163,9 @@ def get_signals(self, schedule: Schedule) -> List[DiscreteSignal]: ) for start_sample, inst in schedule.instructions: - chan = inst.channel.name - phi = phases[chan] - freq = frequency_shifts[chan] + # get channel name if instruction has it + chan = inst.channel.name if hasattr(inst, "channel") else None + if isinstance(inst, Play): # get the instruction samples inst_samples = None @@ -178,8 +178,8 @@ def get_signals(self, schedule: Schedule) -> List[DiscreteSignal]: times = self._dt * (start_sample + np.arange(len(inst_samples))) samples = inst_samples * np.exp( Array( - 2.0j * np.pi * freq * times - + 1.0j * phi + 2.0j * np.pi * frequency_shifts[chan] * times + + 1.0j * phases[chan] + 2.0j * np.pi * phase_accumulations[chan] ) ) diff --git a/releasenotes/notes/0.4/0.4-summary-3de98711c3b7aa09.yaml b/releasenotes/notes/0.4/0.4-summary-3de98711c3b7aa09.yaml index 7fc784cf7..7fade71a3 100644 --- a/releasenotes/notes/0.4/0.4-summary-3de98711c3b7aa09.yaml +++ b/releasenotes/notes/0.4/0.4-summary-3de98711c3b7aa09.yaml @@ -83,6 +83,9 @@ fixes: :class:`~qiskit.pulse.instructions.SetFrequency` and :class:`~qiskit.pulse.instructions.ShiftFrequency` instructions has also been fixed. (`#140 `__) + - | + :class:`.InstructionToSignals` has been updated to fix an error when parsing schedules that + include barrier instructions. `#202 `__) - | Fixes a bug in the automatic jit-compilation of :meth:`Solver.solve` when using the ``t_eval`` kwarg with a JAX method and ``Array.default_backend() == 'jax'``. The bug is fixed by updating diff --git a/test/dynamics/pulse/test_pulse_to_signals.py b/test/dynamics/pulse/test_pulse_to_signals.py index 735079afe..8652ac60a 100644 --- a/test/dynamics/pulse/test_pulse_to_signals.py +++ b/test/dynamics/pulse/test_pulse_to_signals.py @@ -20,6 +20,7 @@ from qiskit import pulse from qiskit.pulse import Schedule from qiskit.pulse.transforms.canonicalization import block_to_schedule +from qiskit.providers.fake_provider import FakeQuito from qiskit import QiskitError from qiskit_dynamics.pulse import InstructionToSignals @@ -320,6 +321,27 @@ def test_InstructionToSignals(self): signals = converter.get_signals(block_to_schedule(schedule)) self.assertAllClose(signals[0].samples, gauss_get_waveform_samples, atol=1e-7, rtol=1e-7) + def test_barrier_instructions(self): + """Test correct parsing of schedule with barrier instructions.""" + + # this example needs any backend with at least 2 qubits + backend = FakeQuito() + + with pulse.build(backend) as sched_block: + pulse.play(pulse.Constant(duration=3, amp=0.5), pulse.DriveChannel(0)) + pulse.barrier(0, 1) + pulse.play(pulse.Constant(duration=3, amp=-0.5), pulse.DriveChannel(1)) + + converter = InstructionToSignals( + dt=1.0, carriers={"d0": 1.0, "d1": 1.0}, channels=["d0", "d1"] + ) + sched = block_to_schedule(sched_block) + + sigs = converter.get_signals(sched) + + self.assertAllClose(sigs[0].samples, np.array([0.5, 0.5, 0.5, 0.0, 0.0, 0.0])) + self.assertAllClose(sigs[1].samples, np.array([0.0, 0.0, 0.0, -0.5, -0.5, -0.5])) + class TestPulseToSignalsJAXTransformations(QiskitDynamicsTestCase, TestJaxBase): """Tests InstructionToSignals class by using Jax.""" From 460391c26b0658366298667460ad453a300edaed Mon Sep 17 00:00:00 2001 From: Daniel Puzzuoli Date: Tue, 14 Mar 2023 12:47:58 -0700 Subject: [PATCH 02/17] Increment version number to 0.5.0 (#204) --- docs/conf.py | 2 +- qiskit_dynamics/VERSION.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index f5a946aff..141262bdb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -25,7 +25,7 @@ # The short X.Y version version = '' # The full version, including alpha/beta/rc tags -release = '0.4.0' +release = '0.5.0' extensions = [ 'sphinx.ext.napoleon', diff --git a/qiskit_dynamics/VERSION.txt b/qiskit_dynamics/VERSION.txt index 1d0ba9ea1..8f0916f76 100644 --- a/qiskit_dynamics/VERSION.txt +++ b/qiskit_dynamics/VERSION.txt @@ -1 +1 @@ -0.4.0 +0.5.0 From 002f2a2a65e3947768dac08a762a1fdfb1579f0f Mon Sep 17 00:00:00 2001 From: Daniel Puzzuoli Date: Fri, 31 Mar 2023 07:33:39 -0700 Subject: [PATCH 03/17] Temporary upper bound on JAX version <=0.4.6 and update to DynamicsBackend tutorial (#210) --- docs/tutorials/dynamics_backend.rst | 12 ++++-------- setup.py | 4 ++-- tox.ini | 12 ++++++------ 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/docs/tutorials/dynamics_backend.rst b/docs/tutorials/dynamics_backend.rst index aca14ba86..371b365ac 100644 --- a/docs/tutorials/dynamics_backend.rst +++ b/docs/tutorials/dynamics_backend.rst @@ -318,8 +318,9 @@ To enable running of the single qubit experiments, we add the following to the ` backend. - Add definitions of ``RZ`` gates as phase shifts. These instructions control the phase of the drive channels, as well as any control channels acting on a given qubit. -- Add a ``CX`` gate which applies to all qubits. While this tutorial will not be utilizing it, this - ensures that validation steps checking that the device is fully connected will pass. +- Add a ``CX`` gate between qubits :math:`(0, 1)` and :math:`(1, 0)`. While this tutorial will not + be utilizing it, this ensures that validation steps checking that the device is fully connected + will pass. .. jupyter-execute:: @@ -336,7 +337,7 @@ To enable running of the single qubit experiments, we add the following to the ` target.add_instruction(XGate(), properties={(0,): None, (1,): None}) target.add_instruction(SXGate(), properties={(0,): None, (1,): None}) - target.add_instruction(CXGate()) + target.add_instruction(CXGate(), properties={(0, 1): None, (1, 0): None}) # Add RZ instruction as phase shift for drag cal phi = Parameter("phi") @@ -472,11 +473,6 @@ values for the single qubit gates calibrated above. from qiskit_experiments.library import CrossResonanceHamiltonian - backend.target.add_instruction( - instruction=CrossResonanceHamiltonian.CRPulseGate(width=Parameter("width")), - properties={(0, 1): None, (1, 0): None} - ) - cr_ham_experiment = CrossResonanceHamiltonian( qubits=(0, 1), flat_top_widths=np.linspace(0, 5000, 17), diff --git a/setup.py b/setup.py index 35c9f48de..0404a2bc7 100644 --- a/setup.py +++ b/setup.py @@ -23,8 +23,8 @@ "multiset>=3.0.1", ] -jax_extras = ['jax>=0.2.26', - 'jaxlib>=0.1.75'] +jax_extras = ['jax>=0.2.26, <= 0.4.6', + 'jaxlib>=0.1.75, <= 0.4.6'] PACKAGES = setuptools.find_packages(exclude=['test*']) diff --git a/tox.ini b/tox.ini index 6e65c3521..b66d0513d 100644 --- a/tox.ini +++ b/tox.ini @@ -15,15 +15,15 @@ commands = stestr run {posargs} [testenv:jax] deps = -r{toxinidir}/requirements-dev.txt - jax - jaxlib + jax<=0.4.6 + jaxlib<=0.4.6 diffrax [testenv:lint] deps = -r{toxinidir}/requirements-dev.txt - jax - jaxlib + jax<=0.4.6 + jaxlib<=0.4.6 diffrax commands = black --check {posargs} qiskit_dynamics test @@ -37,8 +37,8 @@ commands = black {posargs} qiskit_dynamics test [testenv:docs] deps = -r{toxinidir}/requirements-dev.txt - jax - jaxlib + jax<=0.4.6 + jaxlib<=0.4.6 diffrax commands = sphinx-build -b html -W {posargs} docs/ docs/_build/html From 885d8c36fed73f010286bb1a4bdf2d73844758af Mon Sep 17 00:00:00 2001 From: to24toro Date: Wed, 5 Apr 2023 23:02:57 +0900 Subject: [PATCH 04/17] start migrating rotating_frame --- qiskit_dynamics/arraylias_state.py | 22 +++++ qiskit_dynamics/models/rotating_frame.py | 112 ++++++++++++----------- qiskit_dynamics/type_utils.py | 39 +++----- 3 files changed, 93 insertions(+), 80 deletions(-) diff --git a/qiskit_dynamics/arraylias_state.py b/qiskit_dynamics/arraylias_state.py index 86ea48b4c..b36ab258b 100644 --- a/qiskit_dynamics/arraylias_state.py +++ b/qiskit_dynamics/arraylias_state.py @@ -16,7 +16,12 @@ from typing import Union from arraylias import numpy_alias +from scipy.sparse import csr_matrix from .array import Array +try: + from jax.experimental.sparse import BCOO +except ImportError: + pass DYNAMICS_ALIAS = numpy_alias() @@ -24,6 +29,23 @@ # Set qiskit_dynamics.array.Array to be dispatched to numpy DYNAMICS_ALIAS.register_type(Array, "numpy") +# register required custom versions of functions for csr type here +DYNAMICS_ALIAS.register_type(csr_matrix, lib="scipy_sparse") + +# register required custom versions of functions for BCOO type here +DYNAMICS_ALIAS.register_type(BCOO, lib="jax_sparse") + + +@DYNAMICS_ALIAS.register_function(lib="scipy_sparse", path="asarray") +def _(csr: csr_matrix): + return csr.toarray() + + +@DYNAMICS_ALIAS.register_function(lib="jax_sparse", path="asarray") +def _(bcoo: BCOO): + return bcoo.todense() + + DYNAMICS_NUMPY = DYNAMICS_ALIAS() ArrayLike = Union[DYNAMICS_ALIAS.registered_types()] diff --git a/qiskit_dynamics/models/rotating_frame.py b/qiskit_dynamics/models/rotating_frame.py index 4d4a4a097..a97419266 100644 --- a/qiskit_dynamics/models/rotating_frame.py +++ b/qiskit_dynamics/models/rotating_frame.py @@ -17,13 +17,15 @@ from typing import Union, List, Optional import numpy as np -from scipy.sparse import issparse, csr_matrix +from scipy.sparse import issparse from qiskit import QiskitError from qiskit.quantum_info.operators import Operator from qiskit.quantum_info.operators.predicates import is_hermitian_matrix from qiskit_dynamics.array import Array -from qiskit_dynamics.type_utils import to_array, to_BCOO, to_numeric_matrix_type +from qiskit_dynamics.type_utils import to_array, to_numeric_matrix_type +from qiskit_dynamics.arraylias_state import ArrayLike +from qiskit_dynamics.arraylias_state import DYNAMICS_NUMPY as unp try: import jax.numpy as jnp @@ -58,7 +60,7 @@ class RotatingFrame: """ def __init__( - self, frame_operator: Union[Array, Operator], atol: float = 1e-10, rtol: float = 1e-10 + self, frame_operator: Union[ArrayLike, Operator], atol: float = 1e-10, rtol: float = 1e-10 ): """Initialize with a frame operator. @@ -86,7 +88,8 @@ def __init__( # if Hermitian convert to anti-Hermitian frame_operator = _enforce_anti_herm(frame_operator, atol=atol, rtol=rtol) - self._frame_diag = Array(frame_operator) + # TODO: need to define asarray for Operator + self._frame_diag = unp.asarray(frame_operator) self._frame_basis = None self._frame_basis_adjoint = None self._dim = len(self._frame_diag) @@ -97,10 +100,12 @@ def __init__( frame_operator = _enforce_anti_herm(frame_operator, atol=atol, rtol=rtol) # diagonalize with eigh, utilizing assumption of anti-hermiticity - frame_diag, frame_basis = np.linalg.eigh(1j * frame_operator) - self._frame_diag = Array(-1j * frame_diag) - self._frame_basis = Array(frame_basis) + # assume a type of frame_operator is np.array or jnp.array + frame_diag, frame_basis = unp.linalg.eigh(1j * frame_operator) + + self._frame_diag = -1j * frame_diag + self._frame_basis = frame_basis self._frame_basis_adjoint = frame_basis.conj().transpose() self._dim = len(self._frame_diag) @@ -114,17 +119,17 @@ def dim(self) -> int: return self._dim @property - def frame_operator(self) -> Array: + def frame_operator(self) -> ArrayLike: """The original frame operator.""" return self._frame_operator @property - def frame_diag(self) -> Array: + def frame_diag(self) -> ArrayLike: """The diagonal of the frame operator.""" return self._frame_diag @property - def frame_basis(self) -> Array: + def frame_basis(self) -> ArrayLike: """The array containing diagonalizing unitary.""" return self._frame_basis @@ -133,14 +138,14 @@ def frame_basis_adjoint(self) -> Array: """The adjoint of the diagonalizing unitary.""" return self._frame_basis_adjoint - def state_into_frame_basis(self, y: Array) -> Array: + def state_into_frame_basis(self, y: ArrayLike) -> ArrayLike: r"""Take a state into the frame basis, i.e. return ``self.frame_basis_adjoint @ y``. Args: y: The state. Returns: - Array: The state in the frame basis. + ArrayLike: The state in the frame basis. """ y = to_numeric_matrix_type(y) if self.frame_basis_adjoint is None: @@ -148,14 +153,14 @@ def state_into_frame_basis(self, y: Array) -> Array: return self.frame_basis_adjoint @ y - def state_out_of_frame_basis(self, y: Array) -> Array: + def state_out_of_frame_basis(self, y: ArrayLike) -> ArrayLike: r"""Take a state out of the frame basis, i.e. ``return self.frame_basis @ y``. Args: y: The state. Returns: - Array: The state in the frame basis. + ArrayLike: The state in the frame basis. """ y = to_numeric_matrix_type(y) if self.frame_basis is None: @@ -165,9 +170,9 @@ def state_out_of_frame_basis(self, y: Array) -> Array: def operator_into_frame_basis( self, - op: Union[Operator, List[Operator], Array, csr_matrix, None], + op: Union[Operator, List[Operator], ArrayLike, None], convert_type: bool = True, - ) -> Array: + ) -> ArrayLike: r"""Take an operator into the frame basis, i.e. return ``self.frame_basis_adjoint @ A @ self.frame_basis`` @@ -178,7 +183,7 @@ def operator_into_frame_basis( ``op`` is a handled input type. Returns: - Array: The operator in the frame basis. + ArrayLike: The operator in the frame basis. """ if convert_type: op = to_numeric_matrix_type(op) @@ -188,6 +193,7 @@ def operator_into_frame_basis( if isinstance(op, list): return [self.operator_into_frame_basis(x, convert_type=False) for x in op] + # Is it unnecessary to separate cases if they are integrated into arraylias? elif type(op).__name__ == "BCOO": return self.frame_basis_adjoint @ jsparse_matmul(op, self.frame_basis.data) else: @@ -196,9 +202,9 @@ def operator_into_frame_basis( def operator_out_of_frame_basis( self, - op: Union[Operator, List[Operator], Array, csr_matrix, None], + op: Union[Operator, List[Operator], ArrayLike, None], convert_type: bool = True, - ) -> Array: + ) -> ArrayLike: r"""Take an operator out of the frame basis, i.e. return ``self.frame_basis @ to_array(op) @ self.frame_basis_adjoint``. @@ -209,7 +215,7 @@ def operator_out_of_frame_basis( that ``op`` is a handled input type. Returns: - Array: The operator in the frame basis. + ArrayLike: The operator in the frame basis. """ if convert_type: op = to_numeric_matrix_type(op) @@ -219,6 +225,7 @@ def operator_out_of_frame_basis( if isinstance(op, list): return [self.operator_out_of_frame_basis(x, convert_type=False) for x in op] + # Is it unnecessary to separate cases if they are integrated into arraylias? elif type(op).__name__ == "BCOO": return self.frame_basis @ jsparse_matmul(op, self.frame_basis_adjoint.data) else: @@ -228,7 +235,7 @@ def operator_out_of_frame_basis( def state_into_frame( self, t: float, - y: Array, + y: ArrayLike, y_in_frame_basis: Optional[bool] = False, return_in_frame_basis: Optional[bool] = False, ): @@ -242,7 +249,7 @@ def state_into_frame( return_in_frame_basis: Whether or not to return the result in the frame basis. Returns: - Array: The state in the rotating frame. + ArrayLike: The state in the rotating frame. """ y = to_numeric_matrix_type(y) if self._frame_operator is None: @@ -255,7 +262,7 @@ def state_into_frame( out = self.state_into_frame_basis(out) # go into the frame using fast diagonal matrix multiplication - out = (np.exp(self.frame_diag * (-t)) * out.transpose()).transpose() # = e^{tF}out + out = (unp.exp(self.frame_diag * (-t)) * out.transpose()).transpose() # = e^{tF}out # if output is requested to not be in the frame basis, convert it if not return_in_frame_basis: @@ -266,10 +273,10 @@ def state_into_frame( def state_out_of_frame( self, t: float, - y: Array, + y: ArrayLike, y_in_frame_basis: Optional[bool] = False, return_in_frame_basis: Optional[bool] = False, - ) -> Array: + ) -> ArrayLike: r"""Take a state out of the rotating frame, i.e. return ``exp(tF) @ y``. Calls ``self.state_into_frame`` with time reversed. @@ -282,19 +289,19 @@ def state_out_of_frame( return_in_frame_basis: Whether or not to return the result in the frame basis. Returns: - Array: The state out of the rotating frame. + ArrayLike: The state out of the rotating frame. """ return self.state_into_frame(-t, y, y_in_frame_basis, return_in_frame_basis) def _conjugate_and_add( self, t: float, - operator: Union[Array, csr_matrix], - op_to_add_in_fb: Optional[Array] = None, + operator: ArrayLike, + op_to_add_in_fb: Optional[ArrayLike] = None, operator_in_frame_basis: Optional[bool] = False, return_in_frame_basis: Optional[bool] = False, vectorized_operators: Optional[bool] = False, - ) -> Union[Array, csr_matrix]: + ) -> ArrayLike: r"""General helper function for computing :math:`\exp(-tF)G\exp(tF) + B`. Note: :math:`B` is added in the frame basis before any potential final change @@ -338,10 +345,7 @@ def _conjugate_and_add( return operator else: if op_to_add_in_fb is not None: - if issparse(operator): - op_to_add_in_fb = csr_matrix(op_to_add_in_fb) - elif type(operator).__name__ == "BCOO": - op_to_add_in_fb = to_BCOO(op_to_add_in_fb) + op_to_add_in_fb = unp.asarray(op_to_add_in_fb) return operator + op_to_add_in_fb @@ -354,8 +358,10 @@ def _conjugate_and_add( # get frame transformation matrix in diagonal basis # assumption that F is anti-Hermitian implies conjugation of # diagonal gives inversion - exp_freq = np.exp(self.frame_diag * t) + exp_freq = unp.exp(self.frame_diag * t) frame_mat = exp_freq.conj().reshape(self.dim, 1) * exp_freq + + # need to define unp.matmul if issparse(out): out = out.multiply(frame_mat) elif type(out).__name__ == "BCOO": @@ -364,10 +370,7 @@ def _conjugate_and_add( out = frame_mat * out if op_to_add_in_fb is not None: - if issparse(out): - op_to_add_in_fb = csr_matrix(op_to_add_in_fb) - elif type(out).__name__ == "BCOO": - op_to_add_in_fb = to_BCOO(op_to_add_in_fb) + op_to_add_in_fb = unp.asarray(op_to_add_in_fb) out = out + op_to_add_in_fb @@ -386,7 +389,7 @@ def _conjugate_and_add( def operator_into_frame( self, t: float, - operator: Union[Operator, Array, csr_matrix], + operator: Union[Operator, ArrayLike], operator_in_frame_basis: Optional[bool] = False, return_in_frame_basis: Optional[bool] = False, vectorized_operators: Optional[bool] = False, @@ -419,11 +422,11 @@ def operator_into_frame( def operator_out_of_frame( self, t: float, - operator: Union[Operator, Array, csr_matrix], + operator: Union[Operator, ArrayLike], operator_in_frame_basis: Optional[bool] = False, return_in_frame_basis: Optional[bool] = False, vectorized_operators: Optional[bool] = False, - ): + ) -> ArrayLike: r"""Bring an operator into the rotating frame, i.e. return ``exp(tF) @ operator @ exp(-tF)``. @@ -452,11 +455,11 @@ def operator_out_of_frame( def generator_into_frame( self, t: float, - operator: Union[Operator, Array, csr_matrix], + operator: Union[Operator, ArrayLike], operator_in_frame_basis: Optional[bool] = False, return_in_frame_basis: Optional[bool] = False, vectorized_operators: Optional[bool] = False, - ): + ) -> ArrayLike: r"""Take an generator into the rotating frame, i.e. return ``exp(-tF) @ operator @ exp(tF) - F``. @@ -481,7 +484,7 @@ def generator_into_frame( return self._conjugate_and_add( t, operator, - op_to_add_in_fb=-np.diag(self.frame_diag), + op_to_add_in_fb=-unp.diag(self.frame_diag), operator_in_frame_basis=operator_in_frame_basis, return_in_frame_basis=return_in_frame_basis, vectorized_operators=vectorized_operators, @@ -490,10 +493,10 @@ def generator_into_frame( def generator_out_of_frame( self, t: float, - operator: Union[Operator, Array, csr_matrix], + operator: Union[Operator, ArrayLike], operator_in_frame_basis: Optional[bool] = False, return_in_frame_basis: Optional[bool] = False, - ) -> Array: + ) -> ArrayLike: r"""Take an operator out of the frame using the generator transformaton rule, i.e. return ``exp(tF) @ operator @ exp(-tF) + F``. @@ -516,7 +519,7 @@ def generator_out_of_frame( return self._conjugate_and_add( -t, operator, - op_to_add_in_fb=Array(np.diag(self.frame_diag)), + op_to_add_in_fb=unp.diag(self.frame_diag), operator_in_frame_basis=operator_in_frame_basis, return_in_frame_basis=return_in_frame_basis, ) @@ -529,7 +532,7 @@ def vectorized_frame_basis(self): return None if self._vectorized_frame_basis is None: - self._vectorized_frame_basis = np.kron(self.frame_basis.conj(), self.frame_basis) + self._vectorized_frame_basis = unp.kron(self.frame_basis.conj(), self.frame_basis) self._vectorized_frame_basis_adjoint = self._vectorized_frame_basis.conj().transpose() return self._vectorized_frame_basis @@ -551,10 +554,10 @@ def vectorized_frame_basis_adjoint(self): def vectorized_map_into_frame( self, time: float, - op: Array, + op: ArrayLike, operator_in_frame_basis: Optional[bool] = False, return_in_frame_basis: Optional[bool] = False, - ) -> Array: + ) -> ArrayLike: r"""Given an operator ``op`` of dimension ``dim**2`` assumed to represent vectorized linear map in column stacking convention, returns: @@ -596,7 +599,9 @@ def vectorized_map_into_frame( return op -def _enforce_anti_herm(mat: Array, atol: Optional[float] = 1e-10, rtol: Optional[float] = 1e-10): +def _enforce_anti_herm( + mat: ArrayLike, atol: Optional[float] = 1e-10, rtol: Optional[float] = 1e-10 +): r"""Given ``mat``, the logic of this function is: - if ``mat`` is hermitian, return ``-1j * mat`` - if ``mat`` is anti-hermitian, return ``mat`` @@ -620,19 +625,16 @@ def _enforce_anti_herm(mat: Array, atol: Optional[float] = 1e-10, rtol: Optional QiskitError: If ``mat`` is not Hermitian or anti-Hermitian. """ mat = to_array(mat) - mat = Array(mat, dtype=complex) if mat.backend == "jax": from jax.lax import cond - mat = mat.data - if mat.ndim == 1: # this function checks if pure imaginary. If yes it returns the # array, otherwise it multiplies it by jnp.nan to raise an error # Note: pathways in conditionals in jax cannot raise Exceptions def anti_herm_conditional(b): - aherm_pred = jnp.allclose(b, -b.conj(), atol=atol, rtol=rtol) + aherm_pred = unp.allclose(b, -b.conj(), atol=atol, rtol=rtol) return cond(aherm_pred, lambda A: A, lambda A: jnp.nan * A, b) # Check if it is purely real, if not apply anti_herm_conditional diff --git a/qiskit_dynamics/type_utils.py b/qiskit_dynamics/type_utils.py index bd1d9c748..adb84a619 100644 --- a/qiskit_dynamics/type_utils.py +++ b/qiskit_dynamics/type_utils.py @@ -27,6 +27,8 @@ from qiskit.quantum_info.operators import Operator from qiskit_dynamics.array import Array from qiskit_dynamics.dispatch import requires_backend +from qiskit_dynamics.arraylias_state import ArrayLike +from qiskit_dynamics.arraylias_state import DYNAMICS_NUMPY as unp try: from jax.experimental import sparse as jsparse @@ -353,7 +355,7 @@ def isinstance_qutip_qobj(obj): # pylint: disable=too-many-return-statements -def to_array(op: Union[Operator, Array, List[Operator], List[Array], spmatrix], no_iter=False): +def to_array(op: Union[Operator, ArrayLike, List[Operator], List[ArrayLike]], no_iter=False): """Convert an operator or list of operators to an Array. Args: op: Either an Operator to be converted to an array, a list of Operators @@ -368,37 +370,26 @@ def to_array(op: Union[Operator, Array, List[Operator], List[Array], spmatrix], if op is None: return op - if isinstance(op, np.ndarray) and op.dtype != "O": - if Array.default_backend() in [None, "numpy"]: - return op - else: - return Array(op) + if hasattr(op, "__qiskit_array__"): + op = op.data - if isinstance(op, Array): - return op + if isinstance(op, np.ndarray) and op.dtype != "O": + return unp.asarray(op) if issparse(op): - return Array(op.toarray()) + return unp.asarray(op) if type(op).__name__ == "BCOO": - return Array(op.todense()) + return unp.asarray(op) if isinstance(op, Iterable) and not no_iter: - op = Array([to_array(sub_op, no_iter=True) for sub_op in op]) - elif isinstance(op, Iterable) and no_iter: - return op - else: - op = Array(op) - - if op.backend == "numpy": - return op.data - else: - return op + op = unp.asarray([to_array(sub_op, no_iter=True) for sub_op in op]) + return unp.asarray(op) # pylint: disable=too-many-return-statements def to_csr( - op: Union[Operator, Array, List[Operator], List[Array], spmatrix], no_iter=False + op: Union[Operator, ArrayLike, List[Operator], List[Array]], no_iter=False ) -> csr_matrix: """Convert an operator or list of operators to a sparse matrix. Args: @@ -430,7 +421,7 @@ def to_csr( @requires_backend("jax") -def to_BCOO(op: Union[Operator, Array, List[Operator], List[Array], spmatrix, "BCOO"]) -> "BCOO": +def to_BCOO(op: Union[Operator, ArrayLike, List[Operator], List[ArrayLike]]) -> "BCOO": """Convert input op or list of ops to a jax BCOO sparse array. Calls ``to_array`` to handle general conversion to a numpy or jax array, then @@ -448,7 +439,7 @@ def to_BCOO(op: Union[Operator, Array, List[Operator], List[Array], spmatrix, "B if type(op).__name__ == "BCOO": return op - return jsparse.BCOO.fromdense(to_array(op).data) + return jsparse.BCOO.fromdense(to_array(op)) def to_numeric_matrix_type( @@ -474,8 +465,6 @@ def to_numeric_matrix_type( elif isinstance_qutip_qobj(op): return to_csr(op.data) - elif isinstance(op, Array): - return op elif isinstance(op, spmatrix): return op elif type(op).__name__ == "BCOO": From e4c56e83b894f25fa31eefdffdbcc2c555f04e18 Mon Sep 17 00:00:00 2001 From: to24toro Date: Sun, 23 Apr 2023 04:02:25 +0900 Subject: [PATCH 05/17] temporary change ratating_frame --- qiskit_dynamics/arraylias_state.py | 92 ++++++++++++++-- qiskit_dynamics/models/rotating_frame.py | 129 +++++++++++++---------- qiskit_dynamics/type_utils.py | 44 ++++---- 3 files changed, 184 insertions(+), 81 deletions(-) diff --git a/qiskit_dynamics/arraylias_state.py b/qiskit_dynamics/arraylias_state.py index b36ab258b..e94090cc0 100644 --- a/qiskit_dynamics/arraylias_state.py +++ b/qiskit_dynamics/arraylias_state.py @@ -16,10 +16,16 @@ from typing import Union from arraylias import numpy_alias -from scipy.sparse import csr_matrix +from collections.abc import Iterable +import numpy as np +from scipy.sparse import spmatrix, csr_matrix from .array import Array +from qiskit.quantum_info.operators import Operator + + try: from jax.experimental.sparse import BCOO + import jax.numpy as jnp except ImportError: pass @@ -29,21 +35,89 @@ # Set qiskit_dynamics.array.Array to be dispatched to numpy DYNAMICS_ALIAS.register_type(Array, "numpy") -# register required custom versions of functions for csr type here -DYNAMICS_ALIAS.register_type(csr_matrix, lib="scipy_sparse") +# register required custom versions of functions for sparse type here +DYNAMICS_ALIAS.register_type(spmatrix, lib="scipy_sparse") # register required custom versions of functions for BCOO type here DYNAMICS_ALIAS.register_type(BCOO, lib="jax_sparse") +# register required custom versions of functions for Operator type here +DYNAMICS_ALIAS.register_type(Operator, lib="operator") + +# register required custom versions of functions for Iterable type here +# need to discuss registering Iterable type because the coverage of Iterable is too broad. +DYNAMICS_ALIAS.register_type(Iterable,lib="iterable") + +# asarray +@DYNAMICS_ALIAS.register_function(lib="iterable", path="asarray") +def _(arr): + return DYNAMICS_ALIAS(like=arr[0]).asarray(arr) +@DYNAMICS_ALIAS.register_fallback(lib="scipy_sparse", path="asarray") +def _(arr): + return np.asarray(arr) +@DYNAMICS_ALIAS.register_fallback(lib="jax_sparse", path="asarray") +def _(arr): + return jnp.asarray(arr) + + +# to_dense +@DYNAMICS_ALIAS.register_function(lib="numpy", path="to_dense") +def _(op): + return op +@DYNAMICS_ALIAS.register_function(lib="jax", path="to_dense") +def _(op): + return op +@DYNAMICS_ALIAS.register_function(lib="scipy_sparse", path="to_dense") +def _(op): + return op.toarray() +@DYNAMICS_ALIAS.register_function(lib="jax_sparse", path="to_dense") +def _(op): + return op.todense() +@DYNAMICS_ALIAS.register_fallback(path="to_dense") +def _(op): + return np.asarray(op) +@DYNAMICS_ALIAS.register_function(lib="iterable", path="to_dense") +def _(op): + return DYNAMICS_ALIAS().asarray([DYNAMICS_ALIAS().to_dense(sub_op) for sub_op in op]) + + +# to_sparse +@DYNAMICS_ALIAS.register_function(lib="numpy", path="to_sparse") +def _(op): + return csr_matrix(op) +@DYNAMICS_ALIAS.register_function(lib="jax", path="to_sparse") +def _(op): + return BCOO.fromdense(op) +@DYNAMICS_ALIAS.register_function(lib="scipy_sparse", path="to_sparse") +def _(op): + return op +@DYNAMICS_ALIAS.register_function(lib="jax_sparse", path="to_sparse") +def _(op): + return op +@DYNAMICS_ALIAS.register_fallback(path="to_sparse") +def _(op): + return csr_matrix(op) +@DYNAMICS_ALIAS.register_function(lib="iterable", path="to_sparse") +def _(op): + return DYNAMICS_ALIAS().asarray([DYNAMICS_ALIAS().to_sparse(sub_op) for sub_op in op]) + -@DYNAMICS_ALIAS.register_function(lib="scipy_sparse", path="asarray") -def _(csr: csr_matrix): - return csr.toarray() +# to_numeric_matrix_type +@DYNAMICS_ALIAS.register_function(lib="iterable", path="to_numeric_matrix_type") +def _(op): + return DYNAMICS_ALIAS().asarray([DYNAMICS_ALIAS().to_sparse(sub_op) for sub_op in op]) +@DYNAMICS_ALIAS.register_fallback(path="to_numeric_matrix_type") +def _(op): + return DYNAMICS_ALIAS().asarray(op) -@DYNAMICS_ALIAS.register_function(lib="jax_sparse", path="asarray") -def _(bcoo: BCOO): - return bcoo.todense() +# cond +@DYNAMICS_ALIAS.register_function(lib="numpy", path="cond") +def _(pred, true_fun, false_fun, *operands): + if pred: + return true_fun(*operands) + else: + return false_fun(*operands) DYNAMICS_NUMPY = DYNAMICS_ALIAS() diff --git a/qiskit_dynamics/models/rotating_frame.py b/qiskit_dynamics/models/rotating_frame.py index a97419266..1b172134c 100644 --- a/qiskit_dynamics/models/rotating_frame.py +++ b/qiskit_dynamics/models/rotating_frame.py @@ -75,7 +75,7 @@ def __init__( frame_operator = frame_operator.frame_operator self._frame_operator = frame_operator - frame_operator = to_array(frame_operator) + frame_operator = unp.to_dense(frame_operator) if frame_operator is None: self._dim = None @@ -134,7 +134,7 @@ def frame_basis(self) -> ArrayLike: return self._frame_basis @property - def frame_basis_adjoint(self) -> Array: + def frame_basis_adjoint(self) -> ArrayLike: """The adjoint of the diagonalizing unitary.""" return self._frame_basis_adjoint @@ -147,7 +147,7 @@ def state_into_frame_basis(self, y: ArrayLike) -> ArrayLike: Returns: ArrayLike: The state in the frame basis. """ - y = to_numeric_matrix_type(y) + y = unp.to_numeric_matrix_type(y) if self.frame_basis_adjoint is None: return y @@ -162,7 +162,7 @@ def state_out_of_frame_basis(self, y: ArrayLike) -> ArrayLike: Returns: ArrayLike: The state in the frame basis. """ - y = to_numeric_matrix_type(y) + y = unp.to_numeric_matrix_type(y) if self.frame_basis is None: return y @@ -186,7 +186,7 @@ def operator_into_frame_basis( ArrayLike: The operator in the frame basis. """ if convert_type: - op = to_numeric_matrix_type(op) + op = unp.to_numeric_matrix_type(op) if self.frame_basis is None or op is None: return op @@ -218,7 +218,7 @@ def operator_out_of_frame_basis( ArrayLike: The operator in the frame basis. """ if convert_type: - op = to_numeric_matrix_type(op) + op = unp.to_numeric_matrix_type(op) if self.frame_basis is None or op is None: return op @@ -251,7 +251,7 @@ def state_into_frame( Returns: ArrayLike: The state in the rotating frame. """ - y = to_numeric_matrix_type(y) + y = unp.to_numeric_matrix_type(y) if self._frame_operator is None: return y @@ -326,8 +326,8 @@ def _conjugate_and_add( Returns: Array of the newly conjugated operator. """ - operator = to_numeric_matrix_type(operator) - op_to_add_in_fb = to_numeric_matrix_type(op_to_add_in_fb) + operator = unp.to_numeric_matrix_type(operator) + op_to_add_in_fb = unp.to_numeric_matrix_type(op_to_add_in_fb) if vectorized_operators: # If passing vectorized operator, undo vectorization temporarily @@ -345,7 +345,7 @@ def _conjugate_and_add( return operator else: if op_to_add_in_fb is not None: - op_to_add_in_fb = unp.asarray(op_to_add_in_fb) + op_to_add_in_fb = unp.to_sparse(op_to_add_in_fb) return operator + op_to_add_in_fb @@ -370,7 +370,7 @@ def _conjugate_and_add( out = frame_mat * out if op_to_add_in_fb is not None: - op_to_add_in_fb = unp.asarray(op_to_add_in_fb) + op_to_add_in_fb = unp.to_sparse(op_to_add_in_fb) out = out + op_to_add_in_fb @@ -478,7 +478,7 @@ def generator_into_frame( Array: The generator in the rotating frame. """ if self.frame_operator is None: - return to_numeric_matrix_type(operator) + return unp.to_numeric_matrix_type(operator) else: # conjugate and subtract the frame diagonal return self._conjugate_and_add( @@ -513,7 +513,7 @@ def generator_out_of_frame( Array: The generator out of the rotating frame. """ if self.frame_operator is None: - return to_numeric_matrix_type(operator) + return unp.to_numeric_matrix_type(operator) else: # conjugate and add the frame diagonal return self._conjugate_and_add( @@ -624,45 +624,66 @@ def _enforce_anti_herm( ImportError: If the backend is jax and jax is not installed. QiskitError: If ``mat`` is not Hermitian or anti-Hermitian. """ - mat = to_array(mat) - - if mat.backend == "jax": - from jax.lax import cond - - if mat.ndim == 1: - # this function checks if pure imaginary. If yes it returns the - # array, otherwise it multiplies it by jnp.nan to raise an error - # Note: pathways in conditionals in jax cannot raise Exceptions - def anti_herm_conditional(b): - aherm_pred = unp.allclose(b, -b.conj(), atol=atol, rtol=rtol) - return cond(aherm_pred, lambda A: A, lambda A: jnp.nan * A, b) - - # Check if it is purely real, if not apply anti_herm_conditional - herm_pred = jnp.allclose(mat, mat.conj(), atol=atol, rtol=rtol) - return Array(cond(herm_pred, lambda A: -1j * A, anti_herm_conditional, mat)) - else: - # this function checks if anti-hermitian, if yes returns the array, - # otherwise it multiplies it by jnp.nan - def anti_herm_conditional(b): - aherm_pred = jnp.allclose(b, -b.conj().transpose(), atol=atol, rtol=rtol) - return cond(aherm_pred, lambda A: A, lambda A: jnp.nan * A, b) - - # the following lines check if a is hermitian, otherwise it feeds - # it into the anti_herm_conditional - herm_pred = jnp.allclose(mat, mat.conj().transpose(), atol=atol, rtol=rtol) - return Array(cond(herm_pred, lambda A: -1j * A, anti_herm_conditional, mat)) - + mat = unp.to_dense(mat) + if mat.ndim == 1: + # this function checks if pure imaginary. If yes it returns the + # array, otherwise it multiplies it by jnp.nan to raise an error + # Note: pathways in conditionals in jax cannot raise Exceptions + def anti_herm_conditional(b): + aherm_pred = unp.allclose(b, -b.conj(), atol=atol, rtol=rtol) + return unp.cond(aherm_pred, lambda A: A, lambda A: unp.nan * A, b) + + # Check if it is purely real, if not apply anti_herm_conditional + herm_pred = unp.allclose(mat, mat.conj(), atol=atol, rtol=rtol) + return unp.asarray(unp.cond(herm_pred, lambda A: -1j * A, anti_herm_conditional, mat)) else: - if mat.ndim == 1: - if np.allclose(mat, mat.conj(), atol=atol, rtol=rtol): - return -1j * mat - elif np.allclose(mat, -mat.conj(), atol=atol, rtol=rtol): - return mat - else: - if is_hermitian_matrix(mat, rtol=rtol, atol=atol): - return -1j * mat - elif is_hermitian_matrix(1j * mat, rtol=rtol, atol=atol): - return mat - - # raise error if execution has made it this far - raise QiskitError("""frame_operator must be either a Hermitian or anti-Hermitian matrix.""") + # this function checks if anti-hermitian, if yes returns the array, + # otherwise it multiplies it by jnp.nan + def anti_herm_conditional(b): + aherm_pred = unp.allclose(b, -b.conj().transpose(), atol=atol, rtol=rtol) + return unp.cond(aherm_pred, lambda A: A, lambda A: jnp.nan * A, b) + + # the following lines check if a is hermitian, otherwise it feeds + # it into the anti_herm_conditional + herm_pred = unp.allclose(mat, mat.conj().transpose(), atol=atol, rtol=rtol) + return unp.asarray(unp.cond(herm_pred, lambda A: -1j * A, anti_herm_conditional, mat)) + # if mat.backend == "jax": + # from jax.lax import cond + + # if mat.ndim == 1: + # # this function checks if pure imaginary. If yes it returns the + # # array, otherwise it multiplies it by jnp.nan to raise an error + # # Note: pathways in conditionals in jax cannot raise Exceptions + # def anti_herm_conditional(b): + # aherm_pred = unp.allclose(b, -b.conj(), atol=atol, rtol=rtol) + # return cond(aherm_pred, lambda A: A, lambda A: jnp.nan * A, b) + + # # Check if it is purely real, if not apply anti_herm_conditional + # herm_pred = jnp.allclose(mat, mat.conj(), atol=atol, rtol=rtol) + # return Array(cond(herm_pred, lambda A: -1j * A, anti_herm_conditional, mat)) + # else: + # # this function checks if anti-hermitian, if yes returns the array, + # # otherwise it multiplies it by jnp.nan + # def anti_herm_conditional(b): + # aherm_pred = jnp.allclose(b, -b.conj().transpose(), atol=atol, rtol=rtol) + # return cond(aherm_pred, lambda A: A, lambda A: jnp.nan * A, b) + + # # the following lines check if a is hermitian, otherwise it feeds + # # it into the anti_herm_conditional + # herm_pred = jnp.allclose(mat, mat.conj().transpose(), atol=atol, rtol=rtol) + # return Array(cond(herm_pred, lambda A: -1j * A, anti_herm_conditional, mat)) + + # else: + # if mat.ndim == 1: + # if np.allclose(mat, mat.conj(), atol=atol, rtol=rtol): + # return -1j * mat + # elif np.allclose(mat, -mat.conj(), atol=atol, rtol=rtol): + # return mat + # else: + # if is_hermitian_matrix(mat, rtol=rtol, atol=atol): + # return -1j * mat + # elif is_hermitian_matrix(1j * mat, rtol=rtol, atol=atol): + # return mat + + # # raise error if execution has made it this far + # raise QiskitError("""frame_operator must be either a Hermitian or anti-Hermitian matrix.""") diff --git a/qiskit_dynamics/type_utils.py b/qiskit_dynamics/type_utils.py index adb84a619..94ebb5f69 100644 --- a/qiskit_dynamics/type_utils.py +++ b/qiskit_dynamics/type_utils.py @@ -27,8 +27,6 @@ from qiskit.quantum_info.operators import Operator from qiskit_dynamics.array import Array from qiskit_dynamics.dispatch import requires_backend -from qiskit_dynamics.arraylias_state import ArrayLike -from qiskit_dynamics.arraylias_state import DYNAMICS_NUMPY as unp try: from jax.experimental import sparse as jsparse @@ -355,7 +353,7 @@ def isinstance_qutip_qobj(obj): # pylint: disable=too-many-return-statements -def to_array(op: Union[Operator, ArrayLike, List[Operator], List[ArrayLike]], no_iter=False): +def to_array(op: Union[Operator, Array, List[Operator], List[Array], spmatrix], no_iter=False): """Convert an operator or list of operators to an Array. Args: op: Either an Operator to be converted to an array, a list of Operators @@ -370,26 +368,37 @@ def to_array(op: Union[Operator, ArrayLike, List[Operator], List[ArrayLike]], no if op is None: return op - if hasattr(op, "__qiskit_array__"): - op = op.data - if isinstance(op, np.ndarray) and op.dtype != "O": - return unp.asarray(op) + if Array.default_backend() in [None, "numpy"]: + return op + else: + return Array(op) + + if isinstance(op, Array): + return op if issparse(op): - return unp.asarray(op) + return Array(op.toarray()) if type(op).__name__ == "BCOO": - return unp.asarray(op) + return Array(op.todense()) if isinstance(op, Iterable) and not no_iter: - op = unp.asarray([to_array(sub_op, no_iter=True) for sub_op in op]) - return unp.asarray(op) + op = Array([to_array(sub_op, no_iter=True) for sub_op in op]) + elif isinstance(op, Iterable) and no_iter: + return op + else: + op = Array(op) + + if op.backend == "numpy": + return op.data + else: + return op # pylint: disable=too-many-return-statements def to_csr( - op: Union[Operator, ArrayLike, List[Operator], List[Array]], no_iter=False + op: Union[Operator, Array, List[Operator], List[Array], spmatrix], no_iter=False ) -> csr_matrix: """Convert an operator or list of operators to a sparse matrix. Args: @@ -421,15 +430,12 @@ def to_csr( @requires_backend("jax") -def to_BCOO(op: Union[Operator, ArrayLike, List[Operator], List[ArrayLike]]) -> "BCOO": +def to_BCOO(op: Union[Operator, Array, List[Operator], List[Array], spmatrix, "BCOO"]) -> "BCOO": """Convert input op or list of ops to a jax BCOO sparse array. - Calls ``to_array`` to handle general conversion to a numpy or jax array, then builds the BCOO sparse array from the result. - Args: op: Operator or list of operators to convert. - Returns: BCOO: BCOO sparse version of the operator. """ @@ -439,7 +445,7 @@ def to_BCOO(op: Union[Operator, ArrayLike, List[Operator], List[ArrayLike]]) -> if type(op).__name__ == "BCOO": return op - return jsparse.BCOO.fromdense(to_array(op)) + return jsparse.BCOO.fromdense(to_array(op).data) def to_numeric_matrix_type( @@ -465,6 +471,8 @@ def to_numeric_matrix_type( elif isinstance_qutip_qobj(op): return to_csr(op.data) + elif isinstance(op, Array): + return op elif isinstance(op, spmatrix): return op elif type(op).__name__ == "BCOO": @@ -479,4 +487,4 @@ def to_numeric_matrix_type( return to_csr(op) else: - return to_array(op) + return to_array(op) \ No newline at end of file From 23966c8b2da138927bcb3eadb3e82863be969efe Mon Sep 17 00:00:00 2001 From: to24toro Date: Sun, 23 Apr 2023 05:35:55 +0900 Subject: [PATCH 06/17] test_to_dense --- test/dynamics/common.py | 5 +- test/dynamics/test_type_utils.py | 121 ++++++++++++++++--------------- 2 files changed, 66 insertions(+), 60 deletions(-) diff --git a/test/dynamics/common.py b/test/dynamics/common.py index 6483c0efb..ff03a199d 100644 --- a/test/dynamics/common.py +++ b/test/dynamics/common.py @@ -28,6 +28,7 @@ except ImportError: pass +from qiskit_dynamics.arraylias_state import DYNAMICS_NUMPY as unp from qiskit_dynamics.array import Array, wrap @@ -36,8 +37,8 @@ class QiskitDynamicsTestCase(unittest.TestCase): def assertAllClose(self, A, B, rtol=1e-8, atol=1e-8): """Call np.allclose and assert true.""" - A = Array(A) - B = Array(B) + A = unp.asarray(A) + B = unp.asarray(B) self.assertTrue(np.allclose(A, B, rtol=rtol, atol=atol)) diff --git a/test/dynamics/test_type_utils.py b/test/dynamics/test_type_utils.py index 177fa77d3..7c97eeb14 100644 --- a/test/dynamics/test_type_utils.py +++ b/test/dynamics/test_type_utils.py @@ -30,11 +30,16 @@ to_BCOO, to_numeric_matrix_type, ) +from qiskit_dynamics.arraylias_state import ArrayLike +from qiskit_dynamics.arraylias_state import DYNAMICS_NUMPY as unp -from .common import QiskitDynamicsTestCase, TestJaxBase, TestQutipBase + + +from .common import QiskitDynamicsTestCase, TestJaxBase, TestNumpyBase, TestQutipBase try: from jax.experimental import sparse as jsparse + import jax.numpy as jnp except ImportError: pass @@ -274,103 +279,103 @@ def test_sparse_commutator_dissipator(self): ) -class Test_to_array(QiskitDynamicsTestCase): - """Tests for to_array.""" +class Test_to_dense(QiskitDynamicsTestCase, TestNumpyBase): + """Tests for to_dense.""" def test_None_to_None(self): """Test that None input returns None.""" - self.assertTrue(to_array(None) is None) + self.assertTrue(unp.to_dense(None) is None) - def test_to_array_Operator(self): - """Tests for to_array with a single operator""" + def test_to_dense_Operator(self): + """Tests for to_dense with a single operator""" op = Operator.from_label("X") - self.assertAllClose(to_array(op), Array([[0, 1], [1, 0]])) + self.assertAllClose(unp.to_dense(op), np.array([[0, 1], [1, 0]])) - def test_to_array_nparray(self): - """Tests for to_array with a single numpy array""" + def test_to_dense_nparray(self): + """Tests for to_dense with a single numpy array""" ndarray = np.array([[0, 1], [1, 0]]) - self.assertAllClose(to_array(ndarray), ndarray) + self.assertAllClose(unp.to_dense(ndarray), ndarray) - def test_to_array_Array(self): - """Tests for to_array from a qiskit Array""" + def test_to_dense_Array(self): + """Tests for to_dense from a qiskit Array""" list_of_ops = [[[0, 1], [1, 0]], [[0, -1j], [1j, 0]], [[1, 0], [0, -1]]] - arr_in = Array(list_of_ops) - self.assertAllClose(to_array(arr_in), arr_in) + arr_in = unp.asarray(list_of_ops) + self.assertAllClose(unp.to_dense(arr_in), arr_in) - def test_to_array_sparse_matrix(self): - """Tests for to_array with a single sparse matrix""" + def test_to_dense_sparse_matrix(self): + """Tests for to_dense with a single sparse matrix""" op = Operator.from_label("X") spm = csr_matrix(op) - ar = Array(op) - self.assertAllClose(to_array(spm), ar) + ar = unp.asarray(op) + self.assertAllClose(unp.to_dense(spm), ar) - def test_to_array_Operator_list(self): - """Tests for to_array with a list of operators""" + def test_to_dense_Operator_list(self): + """Tests for to_dense with a list of operators""" list_of_ops = [[[0, 1], [1, 0]], [[0, -1j], [1j, 0]], [[1, 0], [0, -1]]] op_arr = [Operator.from_label(s) for s in "XYZ"] - normal_array = Array(np.array(list_of_ops)) - self.assertAllClose(to_array(op_arr), normal_array) + normal_array = unp.asarray(np.array(list_of_ops)) + self.assertAllClose(unp.to_dense(op_arr), normal_array) - def test_to_array_nparray_list(self): - """Tests for to_array with a list of numpy arrays""" + def test_to_dense_nparray_list(self): + """Tests for to_dense with a list of numpy arrays""" list_of_ops = [[[0, 1], [1, 0]], [[0, -1j], [1j, 0]], [[1, 0], [0, -1]]] ndarray_list = [np.array(op) for op in list_of_ops] - list_of_arrays = [Array(op) for op in list_of_ops] - self.assertAllClose(to_array(ndarray_list), list_of_arrays) + list_of_arrays = [unp.asarray(op) for op in list_of_ops] + self.assertAllClose(unp.to_dense(ndarray_list), list_of_arrays) - def test_to_array_list_of_arrays(self): - """Tests for to_array with a list of numpy arrays""" + def test_to_dense_list_of_arrays(self): + """Tests for to_dense with a list of numpy arrays""" list_of_ops = [[[0, 1], [1, 0]], [[0, -1j], [1j, 0]], [[1, 0], [0, -1]]] ndarray_list = [np.array(op) for op in list_of_ops] - list_of_arrays = [Array(op) for op in list_of_ops] - big_array = Array(list_of_arrays) - self.assertAllClose(to_array(ndarray_list), big_array) + list_of_arrays = [unp.asarray(op) for op in list_of_ops] + big_array = unp.asarray(list_of_arrays) + self.assertAllClose(unp.to_dense(ndarray_list), big_array) - def test_to_array_sparse_matrix_list(self): - """Tests for to_array with a list of sparse matrices""" + def test_to_dense_sparse_matrix_list(self): + """Tests for to_dense with a list of sparse matrices""" list_of_ops = [[[0, 1], [1, 0]], [[0, -1j], [1j, 0]], [[1, 0], [0, -1]]] - list_of_arrays = [Array(op) for op in list_of_ops] + list_of_arrays = [unp.asarray(op) for op in list_of_ops] sparse_matrices = [csr_matrix(op) for op in list_of_ops] - self.assertAllClose(to_array(sparse_matrices), list_of_arrays) + self.assertAllClose(unp.to_dense(sparse_matrices), list_of_arrays) - def test_to_array_types(self): - """Type conversion tests for to_array""" + def test_to_dense_types(self): + """Type conversion tests for to_dense""" list_of_ops = [[[0, 1], [1, 0]], [[0, -1j], [1j, 0]], [[1, 0], [0, -1]]] numpy_ops = np.array(list_of_ops) - normal_array = Array(np.array(list_of_ops)) + normal_array = unp.asarray(np.array(list_of_ops)) op_arr = [Operator.from_label(s) for s in "XYZ"] single_op = op_arr[0] list_of_arrays = [Array(op) for op in list_of_ops] - assert isinstance(to_array(numpy_ops), np.ndarray) - assert isinstance(to_array(normal_array), Array) - assert isinstance(to_array(op_arr), np.ndarray) - assert isinstance(to_array(single_op), np.ndarray) - assert isinstance(to_array(list_of_arrays), np.ndarray) + assert isinstance(unp.to_dense(numpy_ops), np.ndarray) + assert isinstance(unp.to_dense(normal_array), np.ndarray) + assert isinstance(unp.to_dense(op_arr), np.ndarray) + assert isinstance(unp.to_dense(single_op), np.ndarray) + assert isinstance(unp.to_dense(list_of_arrays), np.ndarray) -class Test_to_array_Jax(Test_to_array, TestJaxBase): - """Jax version of Test_to_array tests.""" +class Test_to_dense_Jax(TestJaxBase,Test_to_dense): + """Jax version of Test_to_dense tests.""" - def test_to_array_types(self): - """Type conversion tests for to_array with jax backend""" + def test_to_dense_types(self): + """Type conversion tests for to_dense with jax backend""" list_of_ops = [[[0, 1], [1, 0]], [[0, -1j], [1j, 0]], [[1, 0], [0, -1]]] numpy_ops = np.array(list_of_ops) - normal_array = Array(np.array(list_of_ops)) + normal_array = unp.asarray(np.array(list_of_ops)) op_arr = [Operator.from_label(s) for s in "XYZ"] single_op = op_arr[0] - list_of_arrays = [Array(op) for op in list_of_ops] - assert isinstance(to_array(numpy_ops), Array) - assert isinstance(to_array(normal_array), Array) - assert isinstance(to_array(op_arr), Array) - assert isinstance(to_array(single_op), Array) - assert isinstance(to_array(list_of_arrays), Array) - - def test_to_array_BCOO(self): + list_of_arrays = [unp.asarray(op) for op in list_of_ops] + assert isinstance(unp.to_dense(numpy_ops), np.ndarray) + assert isinstance(unp.to_dense(normal_array), np.ndarray) + assert isinstance(unp.to_dense(op_arr), np.ndarray) + assert isinstance(unp.to_dense(single_op), np.ndarray) + assert isinstance(unp.to_dense(list_of_arrays), np.ndarray) + + def test_to_dense_BCOO(self): """Convert BCOO type to array.""" bcoo = jsparse.BCOO.fromdense(np.array([[0.0, 1.0], [1.0, 0.0]])) - out = to_array(bcoo) - self.assertTrue(isinstance(out, Array)) + out = unp.to_dense(bcoo) + self.assertTrue(isinstance(out, jnp.ndarray)) self.assertAllClose(out, np.array([[0.0, 1.0], [1.0, 0.0]])) From 0c7a1fb13bc149e7eed6abbb84258cd9f3e2992c Mon Sep 17 00:00:00 2001 From: to24toro Date: Thu, 27 Apr 2023 09:38:41 -0400 Subject: [PATCH 07/17] define to_sparse to_dense in arraylias_state --- qiskit_dynamics/arraylias_state.py | 69 +++++++++++++++++++++---- qiskit_dynamics/type_utils.py | 2 +- test/dynamics/test_type_utils.py | 81 ++++++++++++++---------------- 3 files changed, 100 insertions(+), 52 deletions(-) diff --git a/qiskit_dynamics/arraylias_state.py b/qiskit_dynamics/arraylias_state.py index e94090cc0..7617da699 100644 --- a/qiskit_dynamics/arraylias_state.py +++ b/qiskit_dynamics/arraylias_state.py @@ -46,66 +46,117 @@ # register required custom versions of functions for Iterable type here # need to discuss registering Iterable type because the coverage of Iterable is too broad. -DYNAMICS_ALIAS.register_type(Iterable,lib="iterable") +DYNAMICS_ALIAS.register_type(Iterable, lib="iterable") + # asarray @DYNAMICS_ALIAS.register_function(lib="iterable", path="asarray") def _(arr): - return DYNAMICS_ALIAS(like=arr[0]).asarray(arr) -@DYNAMICS_ALIAS.register_fallback(lib="scipy_sparse", path="asarray") + if isinstance(arr[0], (list, tuple)): + return np.asarray(arr) + return DYNAMICS_ALIAS(like=arr[0]).asarray( + [DYNAMICS_ALIAS().asarray(sub_arr) for sub_arr in arr] + ) + + +@DYNAMICS_ALIAS.register_function(lib="scipy_sparse", path="asarray") def _(arr): return np.asarray(arr) -@DYNAMICS_ALIAS.register_fallback(lib="jax_sparse", path="asarray") + + +@DYNAMICS_ALIAS.register_function(lib="jax_sparse", path="asarray") def _(arr): return jnp.asarray(arr) +@DYNAMICS_ALIAS.register_fallback(path="asarray") +def _(arr): + return np.asarray(arr) + + # to_dense +@DYNAMICS_ALIAS.register_default(path="to_dense") +def _(op): + return None + + @DYNAMICS_ALIAS.register_function(lib="numpy", path="to_dense") def _(op): return op + + @DYNAMICS_ALIAS.register_function(lib="jax", path="to_dense") def _(op): return op + + @DYNAMICS_ALIAS.register_function(lib="scipy_sparse", path="to_dense") def _(op): return op.toarray() + + @DYNAMICS_ALIAS.register_function(lib="jax_sparse", path="to_dense") def _(op): return op.todense() + + @DYNAMICS_ALIAS.register_fallback(path="to_dense") def _(op): return np.asarray(op) + + @DYNAMICS_ALIAS.register_function(lib="iterable", path="to_dense") def _(op): return DYNAMICS_ALIAS().asarray([DYNAMICS_ALIAS().to_dense(sub_op) for sub_op in op]) # to_sparse +@DYNAMICS_ALIAS.register_default(path="to_sparse") +def _(op): + return None + + @DYNAMICS_ALIAS.register_function(lib="numpy", path="to_sparse") def _(op): return csr_matrix(op) + + @DYNAMICS_ALIAS.register_function(lib="jax", path="to_sparse") def _(op): return BCOO.fromdense(op) + + @DYNAMICS_ALIAS.register_function(lib="scipy_sparse", path="to_sparse") def _(op): return op + + @DYNAMICS_ALIAS.register_function(lib="jax_sparse", path="to_sparse") def _(op): return op + + @DYNAMICS_ALIAS.register_fallback(path="to_sparse") def _(op): return csr_matrix(op) + + @DYNAMICS_ALIAS.register_function(lib="iterable", path="to_sparse") def _(op): - return DYNAMICS_ALIAS().asarray([DYNAMICS_ALIAS().to_sparse(sub_op) for sub_op in op]) + try: + if isinstance(op[0], jnp.ndarray): + return BCOO.fromdense(op) + except ImportError: + return np.array([csr_matrix(sub_op) for sub_op in op]) + return np.array([csr_matrix(sub_op) for sub_op in op]) # to_numeric_matrix_type @DYNAMICS_ALIAS.register_function(lib="iterable", path="to_numeric_matrix_type") def _(op): return DYNAMICS_ALIAS().asarray([DYNAMICS_ALIAS().to_sparse(sub_op) for sub_op in op]) + + @DYNAMICS_ALIAS.register_fallback(path="to_numeric_matrix_type") def _(op): return DYNAMICS_ALIAS().asarray(op) @@ -114,10 +165,10 @@ def _(op): # cond @DYNAMICS_ALIAS.register_function(lib="numpy", path="cond") def _(pred, true_fun, false_fun, *operands): - if pred: - return true_fun(*operands) - else: - return false_fun(*operands) + if pred: + return true_fun(*operands) + else: + return false_fun(*operands) DYNAMICS_NUMPY = DYNAMICS_ALIAS() diff --git a/qiskit_dynamics/type_utils.py b/qiskit_dynamics/type_utils.py index 94ebb5f69..c7912416e 100644 --- a/qiskit_dynamics/type_utils.py +++ b/qiskit_dynamics/type_utils.py @@ -487,4 +487,4 @@ def to_numeric_matrix_type( return to_csr(op) else: - return to_array(op) \ No newline at end of file + return to_array(op) diff --git a/test/dynamics/test_type_utils.py b/test/dynamics/test_type_utils.py index 7c97eeb14..dbf810762 100644 --- a/test/dynamics/test_type_utils.py +++ b/test/dynamics/test_type_utils.py @@ -34,7 +34,6 @@ from qiskit_dynamics.arraylias_state import DYNAMICS_NUMPY as unp - from .common import QiskitDynamicsTestCase, TestJaxBase, TestNumpyBase, TestQutipBase try: @@ -353,7 +352,7 @@ def test_to_dense_types(self): assert isinstance(unp.to_dense(list_of_arrays), np.ndarray) -class Test_to_dense_Jax(TestJaxBase,Test_to_dense): +class Test_to_dense_Jax(TestJaxBase, Test_to_dense): """Jax version of Test_to_dense tests.""" def test_to_dense_types(self): @@ -379,77 +378,75 @@ def test_to_dense_BCOO(self): self.assertAllClose(out, np.array([[0.0, 1.0], [1.0, 0.0]])) -class Test_to_csr(QiskitDynamicsTestCase): - """Tests for to_csr.""" +class Test_to_sparse(QiskitDynamicsTestCase, TestNumpyBase): + """Tests for to_sparse.""" def test_None_to_None(self): """Test that None input returns None.""" - self.assertTrue(to_csr(None) is None) + self.assertTrue(unp.to_sparse(None) is None) - def test_to_csr_Operator(self): - """Tests for to_csr with a single operator""" + def test_to_sparse_Operator(self): + """Tests for to_sparse with a single operator""" op = Operator.from_label("X") - self.assertAllCloseSparse(to_csr(op), csr_matrix([[0, 1], [1, 0]])) + self.assertAllCloseSparse(unp.to_sparse(op), csr_matrix([[0, 1], [1, 0]])) - def test_to_csr_nparray(self): - """Tests for to_csr with a single numpy array""" + def test_to_sparse_nparray(self): + """Tests for to_sparse with a single numpy array""" nparray = np.array([[0, 1], [1, 0]]) - self.assertAllCloseSparse(to_csr(nparray), csr_matrix(nparray)) + self.assertAllCloseSparse(unp.to_sparse(nparray), csr_matrix(nparray)) - def test_to_csr_array(self): - """Tests for to_csr with a single sparse matrix""" + def test_to_sparse_array(self): + """Tests for to_sparse with a single sparse matrix""" op = Operator.from_label("X") spm = csr_matrix(op) ar = Array(op) - self.assertAllCloseSparse(to_csr(ar), spm) + self.assertAllCloseSparse(unp.to_sparse(ar), spm) - def test_to_csr_sparse_matrix(self): - """Tests for to_csr with a single sparse matrix""" + def test_to_sparse_sparse_matrix(self): + """Tests for to_sparse with a single sparse matrix""" op = Operator.from_label("X") spm = csr_matrix(op) - self.assertAllCloseSparse(to_csr(spm), spm) + self.assertAllCloseSparse(unp.to_sparse(spm), spm) - def test_to_csr_Operator_list(self): - """Tests for to_csr with a list of operators""" + def test_to_sparse_Operator_list(self): + """Tests for to_sparse with a list of operators""" list_of_ops = [[[0, 1], [1, 0]], [[0, -1j], [1j, 0]], [[1, 0], [0, -1]]] op_arr = [Operator.from_label(s) for s in "XYZ"] sparse_matrices = [csr_matrix(op) for op in list_of_ops] - self.assertAllCloseSparse(to_csr(op_arr), sparse_matrices) + self.assertAllCloseSparse(unp.to_sparse(op_arr), sparse_matrices) - def test_to_csr_nparray_list(self): - """Tests for to_csr with a list of numpy arrays""" + def test_to_sparse_nparray_list(self): + """Tests for to_sparse with a list of numpy arrays""" list_of_ops = [[[0, 1], [1, 0]], [[0, -1j], [1j, 0]], [[1, 0], [0, -1]]] - nparray_list = [np.array(op) for op in list_of_ops] + nparray_list = [unp.asarray(op) for op in list_of_ops] sparse_matrices = [csr_matrix(op) for op in list_of_ops] - self.assertAllCloseSparse(to_csr(nparray_list), sparse_matrices) + self.assertAllCloseSparse(unp.to_sparse(nparray_list), sparse_matrices) - def test_to_csr_sparse_matrix_list(self): - """Tests for to_csr with a list of sparse matrices""" + def test_to_sparse_sparse_matrix_list(self): + """Tests for to_sparse with a list of sparse matrices""" list_of_ops = [[[0, 1], [1, 0]], [[0, -1j], [1j, 0]], [[1, 0], [0, -1]]] sparse_matrices = [csr_matrix(op) for op in list_of_ops] - self.assertAllCloseSparse(to_csr(sparse_matrices), sparse_matrices) + self.assertAllCloseSparse(unp.to_sparse(sparse_matrices), sparse_matrices) - def test_to_csr_types(self): - """Type conversion tests for to_csr""" + def test_to_sparse_types(self): + """Type conversion tests for to_sparse""" list_of_ops = [[[0, 1], [1, 0]], [[0, -1j], [1j, 0]], [[1, 0], [0, -1]]] - numpy_ops = np.array(list_of_ops) - normal_array = Array(np.array(list_of_ops)) + normal_array = unp.asarray(list_of_ops) op_arr = [Operator.from_label(s) for s in "XYZ"] single_op = op_arr[0] - list_of_arrays = [Array(op) for op in list_of_ops] + list_of_arrays = [unp.asarray(op) for op in list_of_ops] single_array = list_of_arrays[0] sparse_matrices = [csr_matrix(op) for op in list_of_ops] - assert isinstance(to_csr(normal_array)[0], csr_matrix) - assert isinstance(to_csr(numpy_ops)[0], csr_matrix) - assert isinstance(to_csr(op_arr)[0], csr_matrix) - assert isinstance(to_csr(single_op), csr_matrix) - assert isinstance(to_csr(single_array), csr_matrix) - assert isinstance(to_csr(list_of_arrays[0]), csr_matrix) - assert isinstance(to_csr(sparse_matrices), Iterable) - assert isinstance(to_csr(sparse_matrices)[0], csr_matrix) + assert isinstance(unp.to_sparse(normal_array)[0], csr_matrix) + assert isinstance(unp.to_sparse(op_arr)[0], csr_matrix) + assert isinstance(unp.to_sparse(single_op), csr_matrix) + assert isinstance(unp.to_sparse(single_array), csr_matrix) + assert isinstance(unp.to_sparse(list_of_arrays[0]), csr_matrix) + assert isinstance(unp.to_sparse(sparse_matrices), Iterable) + assert isinstance(unp.to_sparse(sparse_matrices)[0], csr_matrix) -class Testto_BCOO(QiskitDynamicsTestCase, TestJaxBase): +class Testto_BCOO(QiskitDynamicsTestCase, TestNumpyBase, TestJaxBase): """Test the to_BCOO function.""" def test_None_to_None(self): @@ -563,5 +560,5 @@ def test_qutip_conversion(self): sparse_matrices = [csr_matrix(op) for op in list_of_ops] qutip_qobj = [Qobj(op) for op in list_of_ops] self.assertAllCloseSparse(to_numeric_matrix_type(qutip_qobj)[0], sparse_matrices[0]) - self.assertAllCloseSparse(to_csr(qutip_qobj)[0], sparse_matrices[0]) + self.assertAllCloseSparse(unp.to_sparse(qutip_qobj)[0], sparse_matrices[0]) self.assertAllClose(to_array(qutip_qobj)[0], normal_array[0]) From dde166e9f8771f4094010effa0289a4e30d26e91 Mon Sep 17 00:00:00 2001 From: to24toro Date: Wed, 3 May 2023 13:08:18 +0900 Subject: [PATCH 08/17] fix --- test/dynamics/test_type_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/dynamics/test_type_utils.py b/test/dynamics/test_type_utils.py index dbf810762..a14b6dd99 100644 --- a/test/dynamics/test_type_utils.py +++ b/test/dynamics/test_type_utils.py @@ -446,7 +446,7 @@ def test_to_sparse_types(self): assert isinstance(unp.to_sparse(sparse_matrices)[0], csr_matrix) -class Testto_BCOO(QiskitDynamicsTestCase, TestNumpyBase, TestJaxBase): +class Testto_BCOO(QiskitDynamicsTestCase, TestJaxBase): """Test the to_BCOO function.""" def test_None_to_None(self): From ed392d71bcf7c2394c9333826348549da31c1fb2 Mon Sep 17 00:00:00 2001 From: to24toro Date: Tue, 9 May 2023 07:32:53 +0900 Subject: [PATCH 09/17] unp.to_sparse unp.to_numeric_matrix_type --- qiskit_dynamics/arraylias_state.py | 48 ++++++++++++++++++++++-------- test/dynamics/test_type_utils.py | 22 +++++++------- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/qiskit_dynamics/arraylias_state.py b/qiskit_dynamics/arraylias_state.py index 7617da699..e4674efa5 100644 --- a/qiskit_dynamics/arraylias_state.py +++ b/qiskit_dynamics/arraylias_state.py @@ -15,12 +15,12 @@ """Configure custom instance of numpy alias for Dynamics.""" from typing import Union -from arraylias import numpy_alias from collections.abc import Iterable +from arraylias import numpy_alias import numpy as np from scipy.sparse import spmatrix, csr_matrix -from .array import Array from qiskit.quantum_info.operators import Operator +from .array import Array try: @@ -59,14 +59,14 @@ def _(arr): ) -@DYNAMICS_ALIAS.register_function(lib="scipy_sparse", path="asarray") -def _(arr): - return np.asarray(arr) +# @DYNAMICS_ALIAS.register_function(lib="scipy_sparse", path="asarray") +# def _(arr): +# return np.asarray(arr) -@DYNAMICS_ALIAS.register_function(lib="jax_sparse", path="asarray") -def _(arr): - return jnp.asarray(arr) +# @DYNAMICS_ALIAS.register_function(lib="jax_sparse", path="asarray") +# def _(arr): +# return jnp.asarray(arr) @DYNAMICS_ALIAS.register_fallback(path="asarray") @@ -76,7 +76,7 @@ def _(arr): # to_dense @DYNAMICS_ALIAS.register_default(path="to_dense") -def _(op): +def _(): return None @@ -118,7 +118,9 @@ def _(op): @DYNAMICS_ALIAS.register_function(lib="numpy", path="to_sparse") def _(op): - return csr_matrix(op) + if op.ndim < 3: + return csr_matrix(op) + return np.array([csr_matrix(sub_op) for sub_op in op]) @DYNAMICS_ALIAS.register_function(lib="jax", path="to_sparse") @@ -152,14 +154,36 @@ def _(op): # to_numeric_matrix_type +@DYNAMICS_ALIAS.register_function(lib="numpy", path="to_numeric_matrix_type") +def _(op): + return op + + +@DYNAMICS_ALIAS.register_function(lib="jax", path="to_numeric_matrix_type") +def _(op): + return op + + +@DYNAMICS_ALIAS.register_function(lib="scipy_sparse", path="to_numeric_matrix_type") +def _(op): + return op + + +@DYNAMICS_ALIAS.register_function(lib="jax_sparse", path="to_numeric_matrix_type") +def _(op): + return op + + @DYNAMICS_ALIAS.register_function(lib="iterable", path="to_numeric_matrix_type") def _(op): - return DYNAMICS_ALIAS().asarray([DYNAMICS_ALIAS().to_sparse(sub_op) for sub_op in op]) + if isinstance(op[0], spmatrix): + return np.array([DYNAMICS_ALIAS().to_sparse(sub_op) for sub_op in op]) + return DYNAMICS_ALIAS().asarray([DYNAMICS_ALIAS().to_dense(sub_op) for sub_op in op]) @DYNAMICS_ALIAS.register_fallback(path="to_numeric_matrix_type") def _(op): - return DYNAMICS_ALIAS().asarray(op) + return op # cond diff --git a/test/dynamics/test_type_utils.py b/test/dynamics/test_type_utils.py index a14b6dd99..40a141efa 100644 --- a/test/dynamics/test_type_utils.py +++ b/test/dynamics/test_type_utils.py @@ -26,11 +26,9 @@ vec_dissipator, vec_commutator, to_array, - to_csr, to_BCOO, to_numeric_matrix_type, ) -from qiskit_dynamics.arraylias_state import ArrayLike from qiskit_dynamics.arraylias_state import DYNAMICS_NUMPY as unp @@ -446,7 +444,7 @@ def test_to_sparse_types(self): assert isinstance(unp.to_sparse(sparse_matrices)[0], csr_matrix) -class Testto_BCOO(QiskitDynamicsTestCase, TestJaxBase): +class Test_to_BCOO(QiskitDynamicsTestCase, TestJaxBase): """Test the to_BCOO function.""" def test_None_to_None(self): @@ -523,16 +521,16 @@ class Test_to_numeric_matrix_type(QiskitDynamicsTestCase): def test_to_numeric_matrix_type(self): """Tests for to_numeric_matrix_type""" list_of_ops = [[[0, 1], [1, 0]], [[0, -1j], [1j, 0]], [[1, 0], [0, -1]]] - normal_array = Array(np.array(list_of_ops)) - list_of_arrays = [Array(op) for op in list_of_ops] + normal_array = unp.asarray(list_of_ops) + list_of_arrays = [unp.asarray(op) for op in list_of_ops] op_arr = [Operator.from_label(s) for s in "XYZ"] sparse_matrices = [csr_matrix(op) for op in list_of_ops] - self.assertAllClose(to_numeric_matrix_type(list_of_ops), normal_array) - self.assertAllClose(to_numeric_matrix_type(list_of_arrays), normal_array) - self.assertAllClose(to_numeric_matrix_type(op_arr), list_of_arrays) + self.assertAllClose(unp.to_numeric_matrix_type(list_of_ops), normal_array) + self.assertAllClose(unp.to_numeric_matrix_type(list_of_arrays), normal_array) + self.assertAllClose(unp.to_numeric_matrix_type(op_arr), list_of_arrays) for i in range(3): self.assertAllCloseSparse( - to_numeric_matrix_type(sparse_matrices)[i], sparse_matrices[i] + unp.to_numeric_matrix_type(sparse_matrices)[i], sparse_matrices[i] ) @@ -542,10 +540,10 @@ class Test_to_numeric_matrix_type_Jax(QiskitDynamicsTestCase, TestJaxBase): def test_to_numeric_matrix_type(self): """Tests for to_numeric_matrix_type""" list_of_ops = [[[0, 1], [1, 0]], [[0, -1j], [1j, 0]], [[1, 0], [0, -1]]] - bcoo = jsparse.BCOO.fromdense(to_array(list_of_ops)) - bcoo2 = to_numeric_matrix_type(bcoo) + bcoo = jsparse.BCOO.fromdense(unp.to_dense(list_of_ops)) + bcoo2 = unp.to_numeric_matrix_type(bcoo) self.assertTrue(type(bcoo2).__name__ == "BCOO") - self.assertAllClose(bcoo.todense(), bcoo2.todense()) + self.assertAllClose(bcoo.todense(), unp.to_dense(bcoo2)) class TestTypeUtilsQutip(QiskitDynamicsTestCase, TestQutipBase): From 4b700bc75690f017a31f65663d53d0dc4ca0c4c4 Mon Sep 17 00:00:00 2001 From: to24toro Date: Tue, 9 May 2023 10:32:03 +0900 Subject: [PATCH 10/17] rotating_Frame --- qiskit_dynamics/arraylias_state.py | 21 ++-- qiskit_dynamics/models/rotating_frame.py | 125 ++++++++++---------- test/dynamics/models/test_rotating_frame.py | 124 +++++++++---------- 3 files changed, 136 insertions(+), 134 deletions(-) diff --git a/qiskit_dynamics/arraylias_state.py b/qiskit_dynamics/arraylias_state.py index e4674efa5..2f67b0b60 100644 --- a/qiskit_dynamics/arraylias_state.py +++ b/qiskit_dynamics/arraylias_state.py @@ -76,7 +76,7 @@ def _(arr): # to_dense @DYNAMICS_ALIAS.register_default(path="to_dense") -def _(): +def _(op): return None @@ -154,6 +154,11 @@ def _(op): # to_numeric_matrix_type +@DYNAMICS_ALIAS.register_default(path="to_numeric_matrix_type") +def _(op): + return None + + @DYNAMICS_ALIAS.register_function(lib="numpy", path="to_numeric_matrix_type") def _(op): return op @@ -177,7 +182,7 @@ def _(op): @DYNAMICS_ALIAS.register_function(lib="iterable", path="to_numeric_matrix_type") def _(op): if isinstance(op[0], spmatrix): - return np.array([DYNAMICS_ALIAS().to_sparse(sub_op) for sub_op in op]) + return [DYNAMICS_ALIAS().to_sparse(sub_op) for sub_op in op] return DYNAMICS_ALIAS().asarray([DYNAMICS_ALIAS().to_dense(sub_op) for sub_op in op]) @@ -187,12 +192,12 @@ def _(op): # cond -@DYNAMICS_ALIAS.register_function(lib="numpy", path="cond") -def _(pred, true_fun, false_fun, *operands): - if pred: - return true_fun(*operands) - else: - return false_fun(*operands) +# @DYNAMICS_ALIAS.register_function(lib="numpy", path="cond") +# def _(pred, true_fun, false_fun, *operands): +# if pred: +# return true_fun(*operands) +# else: +# return false_fun(*operands) DYNAMICS_NUMPY = DYNAMICS_ALIAS() diff --git a/qiskit_dynamics/models/rotating_frame.py b/qiskit_dynamics/models/rotating_frame.py index 1b172134c..18a181b6f 100644 --- a/qiskit_dynamics/models/rotating_frame.py +++ b/qiskit_dynamics/models/rotating_frame.py @@ -22,8 +22,6 @@ from qiskit import QiskitError from qiskit.quantum_info.operators import Operator from qiskit.quantum_info.operators.predicates import is_hermitian_matrix -from qiskit_dynamics.array import Array -from qiskit_dynamics.type_utils import to_array, to_numeric_matrix_type from qiskit_dynamics.arraylias_state import ArrayLike from qiskit_dynamics.arraylias_state import DYNAMICS_NUMPY as unp @@ -219,7 +217,6 @@ def operator_out_of_frame_basis( """ if convert_type: op = unp.to_numeric_matrix_type(op) - if self.frame_basis is None or op is None: return op @@ -393,7 +390,7 @@ def operator_into_frame( operator_in_frame_basis: Optional[bool] = False, return_in_frame_basis: Optional[bool] = False, vectorized_operators: Optional[bool] = False, - ) -> Array: + ) -> ArrayLike: r"""Bring an operator into the frame, i.e. return ``exp(-tF) @ operator @ exp(tF)``. @@ -625,65 +622,65 @@ def _enforce_anti_herm( QiskitError: If ``mat`` is not Hermitian or anti-Hermitian. """ mat = unp.to_dense(mat) - if mat.ndim == 1: - # this function checks if pure imaginary. If yes it returns the - # array, otherwise it multiplies it by jnp.nan to raise an error - # Note: pathways in conditionals in jax cannot raise Exceptions - def anti_herm_conditional(b): - aherm_pred = unp.allclose(b, -b.conj(), atol=atol, rtol=rtol) - return unp.cond(aherm_pred, lambda A: A, lambda A: unp.nan * A, b) - - # Check if it is purely real, if not apply anti_herm_conditional - herm_pred = unp.allclose(mat, mat.conj(), atol=atol, rtol=rtol) - return unp.asarray(unp.cond(herm_pred, lambda A: -1j * A, anti_herm_conditional, mat)) + # if mat.ndim == 1: + # # this function checks if pure imaginary. If yes it returns the + # # array, otherwise it multiplies it by jnp.nan to raise an error + # # Note: pathways in conditionals in jax cannot raise Exceptions + # def anti_herm_conditional(b): + # aherm_pred = unp.allclose(b, -b.conj(), atol=atol, rtol=rtol) + # return unp.cond(aherm_pred, lambda A: A, lambda A: np.nan * A, b) + + # # Check if it is purely real, if not apply anti_herm_conditional + # herm_pred = unp.allclose(mat, mat.conj(), atol=atol, rtol=rtol) + # return unp.asarray(unp.cond(herm_pred, lambda A: -1j * A, anti_herm_conditional, mat)) + # else: + # # this function checks if anti-hermitian, if yes returns the array, + # # otherwise it multiplies it by jnp.nan + # def anti_herm_conditional(b): + # aherm_pred = unp.allclose(b, -b.conj().transpose(), atol=atol, rtol=rtol) + # return unp.cond(aherm_pred, lambda A: A, lambda A: np.nan * A, b) + + # # the following lines check if a is hermitian, otherwise it feeds + # # it into the anti_herm_conditional + # herm_pred = unp.allclose(mat, mat.conj().transpose(), atol=atol, rtol=rtol) + # return unp.asarray(unp.cond(herm_pred, lambda A: -1j * A, anti_herm_conditional, mat)) + if not isinstance(mat, np.ndarray): + from jax.lax import cond + + if mat.ndim == 1: + # this function checks if pure imaginary. If yes it returns the + # array, otherwise it multiplies it by jnp.nan to raise an error + # Note: pathways in conditionals in jax cannot raise Exceptions + def anti_herm_conditional(b): + aherm_pred = jnp.allclose(b, -b.conj(), atol=atol, rtol=rtol) + return cond(aherm_pred, lambda A: A, lambda A: jnp.nan * A, b) + + # Check if it is purely real, if not apply anti_herm_conditional + herm_pred = jnp.allclose(mat, mat.conj(), atol=atol, rtol=rtol) + return unp.asarray(cond(herm_pred, lambda A: -1j * A, anti_herm_conditional, mat)) + else: + # this function checks if anti-hermitian, if yes returns the array, + # otherwise it multiplies it by jnp.nan + def anti_herm_conditional(b): + aherm_pred = jnp.allclose(b, -b.conj().transpose(), atol=atol, rtol=rtol) + return cond(aherm_pred, lambda A: A, lambda A: jnp.nan * A, b) + + # the following lines check if a is hermitian, otherwise it feeds + # it into the anti_herm_conditional + herm_pred = jnp.allclose(mat, mat.conj().transpose(), atol=atol, rtol=rtol) + return unp.asarray(cond(herm_pred, lambda A: -1j * A, anti_herm_conditional, mat)) + else: - # this function checks if anti-hermitian, if yes returns the array, - # otherwise it multiplies it by jnp.nan - def anti_herm_conditional(b): - aherm_pred = unp.allclose(b, -b.conj().transpose(), atol=atol, rtol=rtol) - return unp.cond(aherm_pred, lambda A: A, lambda A: jnp.nan * A, b) - - # the following lines check if a is hermitian, otherwise it feeds - # it into the anti_herm_conditional - herm_pred = unp.allclose(mat, mat.conj().transpose(), atol=atol, rtol=rtol) - return unp.asarray(unp.cond(herm_pred, lambda A: -1j * A, anti_herm_conditional, mat)) - # if mat.backend == "jax": - # from jax.lax import cond - - # if mat.ndim == 1: - # # this function checks if pure imaginary. If yes it returns the - # # array, otherwise it multiplies it by jnp.nan to raise an error - # # Note: pathways in conditionals in jax cannot raise Exceptions - # def anti_herm_conditional(b): - # aherm_pred = unp.allclose(b, -b.conj(), atol=atol, rtol=rtol) - # return cond(aherm_pred, lambda A: A, lambda A: jnp.nan * A, b) - - # # Check if it is purely real, if not apply anti_herm_conditional - # herm_pred = jnp.allclose(mat, mat.conj(), atol=atol, rtol=rtol) - # return Array(cond(herm_pred, lambda A: -1j * A, anti_herm_conditional, mat)) - # else: - # # this function checks if anti-hermitian, if yes returns the array, - # # otherwise it multiplies it by jnp.nan - # def anti_herm_conditional(b): - # aherm_pred = jnp.allclose(b, -b.conj().transpose(), atol=atol, rtol=rtol) - # return cond(aherm_pred, lambda A: A, lambda A: jnp.nan * A, b) - - # # the following lines check if a is hermitian, otherwise it feeds - # # it into the anti_herm_conditional - # herm_pred = jnp.allclose(mat, mat.conj().transpose(), atol=atol, rtol=rtol) - # return Array(cond(herm_pred, lambda A: -1j * A, anti_herm_conditional, mat)) + if mat.ndim == 1: + if np.allclose(mat, mat.conj(), atol=atol, rtol=rtol): + return -1j * mat + elif np.allclose(mat, -mat.conj(), atol=atol, rtol=rtol): + return mat + else: + if is_hermitian_matrix(mat, rtol=rtol, atol=atol): + return -1j * mat + elif is_hermitian_matrix(1j * mat, rtol=rtol, atol=atol): + return mat - # else: - # if mat.ndim == 1: - # if np.allclose(mat, mat.conj(), atol=atol, rtol=rtol): - # return -1j * mat - # elif np.allclose(mat, -mat.conj(), atol=atol, rtol=rtol): - # return mat - # else: - # if is_hermitian_matrix(mat, rtol=rtol, atol=atol): - # return -1j * mat - # elif is_hermitian_matrix(1j * mat, rtol=rtol, atol=atol): - # return mat - - # # raise error if execution has made it this far - # raise QiskitError("""frame_operator must be either a Hermitian or anti-Hermitian matrix.""") + # raise error if execution has made it this far + raise QiskitError("""frame_operator must be either a Hermitian or anti-Hermitian matrix.""") diff --git a/test/dynamics/models/test_rotating_frame.py b/test/dynamics/models/test_rotating_frame.py index e17a83ad7..d76cbdf8d 100644 --- a/test/dynamics/models/test_rotating_frame.py +++ b/test/dynamics/models/test_rotating_frame.py @@ -19,27 +19,27 @@ from qiskit.quantum_info.operators import Operator from scipy.sparse import csr_matrix from qiskit_dynamics.models.rotating_frame import RotatingFrame -from qiskit_dynamics.array import Array -from qiskit_dynamics.type_utils import to_BCOO, to_array -from ..common import QiskitDynamicsTestCase, TestJaxBase +from qiskit_dynamics.arraylias_state import DYNAMICS_NUMPY as unp +from qiskit_dynamics.arraylias_state import ArrayLike +from ..common import QiskitDynamicsTestCase, TestJaxBase, TestNumpyBase -class TestRotatingFrame(QiskitDynamicsTestCase): +class TestRotatingFrame(QiskitDynamicsTestCase, TestNumpyBase): """Tests for RotatingFrame.""" def setUp(self): - self.X = Array(Operator.from_label("X").data) - self.Y = Array(Operator.from_label("Y").data) - self.Z = Array(Operator.from_label("Z").data) + self.X = self.asarray(Operator.from_label("X").data) + self.Y = self.asarray(Operator.from_label("Y").data) + self.Z = self.asarray(Operator.from_label("Z").data) def test_instantiation_errors(self): """Check different modes of error raising for frame setting.""" with self.assertRaisesRegex(QiskitError, "Hermitian or anti-Hermitian"): - RotatingFrame(Array([1.0, 1j])) + RotatingFrame(self.asarray([1.0, 1j])) with self.assertRaisesRegex(QiskitError, "Hermitian or anti-Hermitian"): - RotatingFrame(Array([[1.0, 0.0], [0.0, 1j]])) + RotatingFrame(self.asarray([[1.0, 0.0], [0.0, 1j]])) with self.assertRaisesRegex(QiskitError, "Hermitian or anti-Hermitian"): RotatingFrame(self.Z + 1j * self.X) @@ -52,12 +52,12 @@ def test_state_out_of_frame_basis(self): low=-10, high=10, size=(6, 6) ) - frame_op = Array(rand_op - rand_op.conj().transpose()) + frame_op = unp.asarray(rand_op - rand_op.conj().transpose()) rotating_frame = RotatingFrame(frame_op) _, U = np.linalg.eigh(1j * frame_op) - y0 = Array( + y0 = unp.asarray( rng.uniform(low=-10, high=10, size=(6, 6)) + 1j * rng.uniform(low=-10, high=10, size=(6, 6)) ) @@ -78,13 +78,13 @@ def test_operator_into_frame_basis(self): low=-10, high=10, size=(10, 10) ) - frame_op = Array(rand_op - rand_op.conj().transpose()) + frame_op = unp.asarray(rand_op - rand_op.conj().transpose()) rotating_frame = RotatingFrame(frame_op) _, U = np.linalg.eigh(1j * frame_op) Uadj = U.conj().transpose() - y0 = Array( + y0 = unp.asarray( rng.uniform(low=-10, high=10, size=(10, 10)) + 1j * rng.uniform(low=-10, high=10, size=(10, 10)) ) @@ -113,8 +113,7 @@ def test_operator_out_of_frame_basis_sparse_list(self): """Test state_out_of_frame_basis for a list of sparse arrays.""" ops = [csr_matrix([[0.0, 1.0], [1.0, 0.0]]), csr_matrix([[1.0, 0.0], [0.0, -1.0]])] - rotating_frame = RotatingFrame(np.array([[0.0, 1.0], [1.0, 0.0]])) - + rotating_frame = RotatingFrame(unp.asarray([[0.0, 1.0], [1.0, 0.0]])) val = rotating_frame.operator_out_of_frame_basis(ops) U = rotating_frame.frame_basis Uadj = rotating_frame.frame_basis_adjoint @@ -124,17 +123,17 @@ def test_operator_out_of_frame_basis_sparse_list(self): def test_state_transformations_no_frame(self): """Test frame transformations with no frame.""" - rotating_frame = RotatingFrame(Array(np.zeros(2))) + rotating_frame = RotatingFrame(unp.asarray(np.zeros(2))) t = 0.123 - y = Array([1.0, 1j]) + y = unp.asarray([1.0, 1j]) out = rotating_frame.state_into_frame(t, y) self.assertAllClose(out, y) out = rotating_frame.state_out_of_frame(t, y) self.assertAllClose(out, y) t = 100.12498 - y = Array(np.eye(2)) + y = unp.asarray(np.eye(2)) out = rotating_frame.state_into_frame(t, y) self.assertAllClose(out, y) out = rotating_frame.state_out_of_frame(t, y) @@ -142,9 +141,9 @@ def test_state_transformations_no_frame(self): def test_state_into_frame_2_level(self): """Test state_into_frame with a non-trival frame.""" - frame_op = -1j * np.pi * (self.X + 0.1 * self.Y + 12.0 * self.Z).data + frame_op = -1j * np.pi * (self.X + 0.1 * self.Y + 12.0 * self.Z) t = 1312.132 - y0 = Array([[1.0, 2.0], [3.0, 4.0]]) + y0 = unp.asarray([[1.0, 2.0], [3.0, 4.0]]) self._test_state_into_frame(t, frame_op, y0) self._test_state_into_frame(t, frame_op, y0, y_in_frame_basis=True) @@ -160,10 +159,10 @@ def test_state_into_frame_pseudo_random(self): low=-10, high=10, size=(5, 5) ) - frame_op = Array(rand_op - rand_op.conj().transpose()) + frame_op = unp.asarray(rand_op - rand_op.conj().transpose()) t = 1312.132 - y0 = Array( + y0 = unp.asarray( rng.uniform(low=-10, high=10, size=(5, 5)) + 1j * rng.uniform(low=-10, high=10, size=(5, 5)) ) @@ -189,7 +188,7 @@ def _test_state_into_frame( if not y_in_frame_basis: expected = U.conj().transpose() @ expected - expected = np.diag(np.exp(-t * Array(evals))) @ expected + expected = np.diag(np.exp(-t * unp.asarray(evals))) @ expected if not return_in_frame_basis: expected = U @ expected @@ -198,9 +197,9 @@ def _test_state_into_frame( def test_state_out_of_frame_2_level(self): """Test state_out_of_frame with a non-trival frame.""" - frame_op = -1j * np.pi * (3.1 * self.X + 1.1 * self.Y + 12.0 * self.Z).data + frame_op = -1j * np.pi * (3.1 * self.X + 1.1 * self.Y + 12.0 * self.Z) t = 122.132 - y0 = Array([[1.0, 2.0], [3.0, 4.0]]) + y0 = unp.asarray([[1.0, 2.0], [3.0, 4.0]]) self._test_state_out_of_frame(t, frame_op, y0) self._test_state_out_of_frame(t, frame_op, y0, y_in_frame_basis=True) self._test_state_out_of_frame(t, frame_op, y0, return_in_frame_basis=True) @@ -215,10 +214,10 @@ def test_state_out_of_frame_pseudo_random(self): low=-10, high=10, size=(6, 6) ) - frame_op = Array(rand_op - rand_op.conj().transpose()) + frame_op = unp.asarray(rand_op - rand_op.conj().transpose()) t = rng.uniform(low=-100, high=100) - y0 = Array( + y0 = unp.asarray( rng.uniform(low=-10, high=10, size=(6, 6)) + 1j * rng.uniform(low=-10, high=10, size=(6, 6)) ) @@ -235,7 +234,7 @@ def _test_state_out_of_frame( self, t, frame_op, y, y_in_frame_basis=False, return_in_frame_basis=False ): evals, U = np.linalg.eigh(1j * frame_op) - evals = -1j * Array(evals) + evals = -1j * unp.asarray(evals) rotating_frame = RotatingFrame(frame_op) @@ -258,10 +257,10 @@ def test_operator_into_frame(self): low=-10, high=10, size=(6, 6) ) - frame_op = Array(rand_op - rand_op.conj().transpose()) + frame_op = unp.asarray(rand_op - rand_op.conj().transpose()) t = rng.uniform(low=-100, high=100) - y0 = Array( + y0 = unp.asarray( rng.uniform(low=-10, high=10, size=(6, 6)) + 1j * rng.uniform(low=-10, high=10, size=(6, 6)) ) @@ -278,7 +277,7 @@ def _test_operator_into_frame( self, t, frame_op, y, y_in_frame_basis=False, return_in_frame_basis=False ): evals, U = np.linalg.eigh(1j * frame_op) - evals = -1j * Array(evals) + evals = -1j * unp.asarray(evals) Uadj = U.conj().transpose() rotating_frame = RotatingFrame(frame_op) @@ -298,7 +297,7 @@ def _test_operator_into_frame( def test_operator_out_of_frame(self): """Test operator_out_of_frame.""" rng = np.random.default_rng(37164093) - rand_op = Array( + rand_op = unp.asarray( rng.uniform(low=-10, high=10, size=(6, 6)) + 1j * rng.uniform(low=-10, high=10, size=(6, 6)) ) @@ -306,7 +305,7 @@ def test_operator_out_of_frame(self): frame_op = rand_op - rand_op.conj().transpose() t = rng.uniform(low=-100, high=100) - y0 = Array( + y0 = unp.asarray( rng.uniform(low=-10, high=10, size=(6, 6)) + 1j * rng.uniform(low=-10, high=10, size=(6, 6)) ) @@ -323,7 +322,7 @@ def _test_operator_out_of_frame( self, t, frame_op, y, y_in_frame_basis=False, return_in_frame_basis=False ): evals, U = np.linalg.eigh(1j * frame_op) - evals = -1j * Array(evals) + evals = -1j * unp.asarray(evals) Uadj = U.conj().transpose() rotating_frame = RotatingFrame(frame_op) @@ -343,7 +342,7 @@ def _test_operator_out_of_frame( def test_generator_into_frame(self): """Test operator_out_of_frame.""" rng = np.random.default_rng(111) - rand_op = Array( + rand_op = unp.asarray( rng.uniform(low=-10, high=10, size=(6, 6)) + 1j * rng.uniform(low=-10, high=10, size=(6, 6)) ) @@ -351,7 +350,7 @@ def test_generator_into_frame(self): frame_op = rand_op - rand_op.conj().transpose() t = rng.uniform(low=-100, high=100) - y0 = Array( + y0 = unp.asarray( rng.uniform(low=-10, high=10, size=(6, 6)) + 1j * rng.uniform(low=-10, high=10, size=(6, 6)) ) @@ -369,7 +368,7 @@ def _test_generator_into_frame( ): """Helper function for testing generator_into_frame.""" evals, U = np.linalg.eigh(1j * frame_op) - evals = -1j * Array(evals) + evals = -1j * unp.asarray(evals) Uadj = U.conj().transpose() rotating_frame = RotatingFrame(frame_op) @@ -394,10 +393,10 @@ def test_generator_out_of_frame(self): low=-10, high=10, size=(6, 6) ) - frame_op = Array(rand_op - rand_op.conj().transpose()) + frame_op = unp.asarray(rand_op - rand_op.conj().transpose()) t = rng.uniform(low=-100, high=100) - y0 = Array( + y0 = unp.asarray( rng.uniform(low=-10, high=10, size=(6, 6)) + 1j * rng.uniform(low=-10, high=10, size=(6, 6)) ) @@ -415,7 +414,7 @@ def _test_generator_out_of_frame( ): """Helper function for testing generator_into_frame.""" evals, U = np.linalg.eigh(1j * frame_op) - evals = -1j * Array(evals) + evals = -1j * unp.asarray(evals) Uadj = U.conj().transpose() rotating_frame = RotatingFrame(frame_op) @@ -437,12 +436,12 @@ def test_vectorized_conjugate_and_add_conventions(self): """Test whether passing a vectorized (dim**2, k) operator to _conjugate_and_add with vectorized_operators = True is the same as passing a (k,dim,dim) array of operators.""" - vectorized_rhos = np.array( + vectorized_rhos = self.asarray( [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0]] ).transpose() nonvectord_rhos = vectorized_rhos.reshape((2, 2, 3), order="F").transpose([2, 0, 1]) - rotating_frame = RotatingFrame(np.array([1j, 2j])) + rotating_frame = RotatingFrame(self.asarray([1j, 2j])) vectorized_result = rotating_frame._conjugate_and_add( 0.1, vectorized_rhos, vectorized_operators=True @@ -562,22 +561,22 @@ def test_state_transformations_no_frame_array_type(self): rotating_frame = RotatingFrame(None) t = 0.123 - y = Array([1.0, 1j]) + y = unp.asarray([1.0, 1j]) out = rotating_frame.state_into_frame(t, y) self.assertAllClose(out, y) - self.assertTrue(isinstance(out, Array)) + self.assertTrue(isinstance(out, ArrayLike)) out = rotating_frame.state_out_of_frame(t, y) self.assertAllClose(out, y) - self.assertTrue(isinstance(out, Array)) + self.assertTrue(isinstance(out, ArrayLike)) t = 100.12498 - y = Array(np.eye(2)) + y = unp.asarray(np.eye(2)) out = rotating_frame.state_into_frame(t, y) self.assertAllClose(out, y) - self.assertTrue(isinstance(out, Array)) + self.assertTrue(isinstance(out, ArrayLike)) out = rotating_frame.state_out_of_frame(t, y) self.assertAllClose(out, y) - self.assertTrue(isinstance(out, Array)) + self.assertTrue(isinstance(out, ArrayLike)) class TestRotatingJAXBCOO(QiskitDynamicsTestCase, TestJaxBase): @@ -586,16 +585,17 @@ class TestRotatingJAXBCOO(QiskitDynamicsTestCase, TestJaxBase): def test_conjugate_and_add_BCOO(self): """Test _conjugate_and_add with operator being BCOO.""" - rotating_frame = RotatingFrame(np.array([1.0, -1.0])) + rotating_frame = RotatingFrame(self.asarray([1.0, -1.0])) t = 0.123 - op = to_BCOO(np.array([[1.0, -1j], [0.0, 1.0]])) - op_to_add = to_BCOO(np.array([[0.0, -0.11j], [0.0, 1.0]])) + op = unp.to_sparse(self.asarray([[1.0, -1j], [0.0, 1.0]])) + op_to_add = unp.to_sparse(self.asarray([[0.0, -0.11j], [0.0, 1.0]])) out = rotating_frame._conjugate_and_add(t, op, op_to_add) self.assertTrue(type(out).__name__ == "BCOO") self.assertAllClose( - to_array(out), rotating_frame._conjugate_and_add(t, to_array(op), to_array(op_to_add)) + unp.to_dense(out), + rotating_frame._conjugate_and_add(t, unp.to_dense(op), unp.to_dense(op_to_add)), ) def test_operator_into_frame_basis(self): @@ -607,7 +607,7 @@ def test_operator_into_frame_basis(self): op = to_BCOO(np.array([[1.0, -1j], [0.0, 1.0]])) output = rotating_frame.operator_into_frame_basis(op) - expected = rotating_frame.operator_into_frame_basis(to_array(op)) + expected = rotating_frame.operator_into_frame_basis(unp.to_dense(op)) self.assertAllClose(output, expected) @@ -625,7 +625,7 @@ def test_operator_out_of_frame_basis(self): self.assertAllClose(output, expected) -class TestRotatingFrameJax(TestRotatingFrame, TestJaxBase): +class TestRotatingFrameJax(TestJaxBase, TestRotatingFrame): """Jax version of TestRotatingFrame tests. Note: This class has more tests due to inheritance. @@ -639,10 +639,10 @@ def test_instantiation_errors(self): # pylint: disable=import-outside-toplevel import jax.numpy as jnp - rotating_frame = RotatingFrame(Array([1.0, 1j])) + rotating_frame = RotatingFrame(jnp.asarray([1.0, 1j])) self.assertTrue(jnp.isnan(rotating_frame.frame_diag[0])) - rotating_frame = RotatingFrame(Array([[1.0, 0.0], [0.0, 1j]])) + rotating_frame = RotatingFrame(jnp.asarray([[1.0, 0.0], [0.0, 1j]])) self.assertTrue(jnp.isnan(rotating_frame.frame_diag[0])) rotating_frame = RotatingFrame(self.Z + 1j * self.X) @@ -651,19 +651,19 @@ def test_instantiation_errors(self): def test_jitting(self): """Test jitting of state_into_frame and _conjugate_and_add.""" - rotating_frame = RotatingFrame(Array([1.0, -1.0])) + rotating_frame = RotatingFrame(unp.asarray([1.0, -1.0])) - self.jit_wrap(rotating_frame.state_into_frame)(t=0.1, y=np.array([0.0, 1.0])) + self.jit_wrap(rotating_frame.state_into_frame)(t=0.1, y=unp.asarray([0.0, 1.0])) self.jit_wrap(rotating_frame._conjugate_and_add)( - t=0.1, operator=np.array([[0.0, 1.0], [1.0, 0.0]]) + t=0.1, operator=unp.asarray([[0.0, 1.0], [1.0, 0.0]]) ) def test_jit_and_grad(self): """Test jitting and gradding of state_into_frame and _conjugate_and_add.""" - rotating_frame = RotatingFrame(Array([1.0, -1.0])) + rotating_frame = RotatingFrame(unp.asarray([1.0, -1.0])) - self.jit_grad_wrap(rotating_frame.state_into_frame)(0.1, np.array([0.0, 1.0])) + self.jit_grad_wrap(rotating_frame.state_into_frame)(0.1, unp.asarray([0.0, 1.0])) self.jit_grad_wrap(rotating_frame._conjugate_and_add)( - 0.1, np.array([[0.0, 1.0], [1.0, 0.0]]) + 0.1, unp.asarray([[0.0, 1.0], [1.0, 0.0]]) ) From 3043f5b91280ff937ab170087614d29ba357f5bf Mon Sep 17 00:00:00 2001 From: to24toro Date: Tue, 9 May 2023 16:07:54 +0900 Subject: [PATCH 11/17] divide arraylias_state --- qiskit_dynamics/arraylias/arraylias_state.py | 46 ++++++++++++++++++ qiskit_dynamics/arraylias/asarray.py | 13 +++++ qiskit_dynamics/arraylias/to_dense.py | 36 ++++++++++++++ .../arraylias/to_numeric_matrix_type.py | 38 +++++++++++++++ qiskit_dynamics/arraylias/to_sparse.py | 47 +++++++++++++++++++ qiskit_dynamics/models/rotating_frame.py | 4 +- qiskit_dynamics/signals/signals.py | 6 +-- test/dynamics/common.py | 2 +- test/dynamics/models/test_rotating_frame.py | 5 +- test/dynamics/signals/test_signals.py | 2 +- test/dynamics/test_type_utils.py | 2 +- 11 files changed, 191 insertions(+), 10 deletions(-) create mode 100644 qiskit_dynamics/arraylias/arraylias_state.py create mode 100644 qiskit_dynamics/arraylias/asarray.py create mode 100644 qiskit_dynamics/arraylias/to_dense.py create mode 100644 qiskit_dynamics/arraylias/to_numeric_matrix_type.py create mode 100644 qiskit_dynamics/arraylias/to_sparse.py diff --git a/qiskit_dynamics/arraylias/arraylias_state.py b/qiskit_dynamics/arraylias/arraylias_state.py new file mode 100644 index 000000000..1ea319d91 --- /dev/null +++ b/qiskit_dynamics/arraylias/arraylias_state.py @@ -0,0 +1,46 @@ +from typing import Union +from collections.abc import Iterable +from arraylias import numpy_alias +from scipy.sparse import spmatrix +from qiskit.quantum_info.operators import Operator +from .asarray import register_to_asarray +from .to_dense import register_to_dense +from .to_numeric_matrix_type import register_to_numeric_matrix_type +from .to_sparse import register_to_sparse + +from ..array import Array + + +DYNAMICS_ALIAS = numpy_alias() + +# Set qiskit_dynamics.array.Array to be dispatched to numpy +DYNAMICS_ALIAS.register_type(Array, "numpy") + +# register required custom versions of functions for sparse type here +DYNAMICS_ALIAS.register_type(spmatrix, lib="scipy_sparse") + +try: + from jax.experimental.sparse import BCOO + + # register required custom versions of functions for BCOO type here + DYNAMICS_ALIAS.register_type(BCOO, lib="jax_sparse") +except ImportError: + pass + +# register required custom versions of functions for Operator type here +DYNAMICS_ALIAS.register_type(Operator, lib="operator") + +# register required custom versions of functions for Iterable type here +# need to discuss registering Iterable type because the coverage of Iterable is too broad. +DYNAMICS_ALIAS.register_type(Iterable, lib="iterable") + + +register_to_asarray(alias=DYNAMICS_ALIAS) +register_to_dense(alias=DYNAMICS_ALIAS) +register_to_numeric_matrix_type(alias=DYNAMICS_ALIAS) +register_to_sparse(alias=DYNAMICS_ALIAS) + +DYNAMICS_NUMPY = DYNAMICS_ALIAS() + + +ArrayLike = Union[DYNAMICS_ALIAS.registered_types()] diff --git a/qiskit_dynamics/arraylias/asarray.py b/qiskit_dynamics/arraylias/asarray.py new file mode 100644 index 000000000..1b96c778d --- /dev/null +++ b/qiskit_dynamics/arraylias/asarray.py @@ -0,0 +1,13 @@ +import numpy as np + + +def register_to_asarray(alias): + @alias.register_function(lib="iterable", path="asarray") + def _(arr): + if isinstance(arr[0], (list, tuple)): + return np.asarray(arr) + return alias(like=arr[0]).asarray([alias().asarray(sub_arr) for sub_arr in arr]) + + @alias.register_fallback(path="asarray") + def _(arr): + return np.asarray(arr) diff --git a/qiskit_dynamics/arraylias/to_dense.py b/qiskit_dynamics/arraylias/to_dense.py new file mode 100644 index 000000000..767569a5b --- /dev/null +++ b/qiskit_dynamics/arraylias/to_dense.py @@ -0,0 +1,36 @@ +import numpy as np + + +def register_to_dense(alias): + @alias.register_default(path="to_dense") + def _(op): + return None + + @alias.register_function(lib="numpy", path="to_dense") + def _(op): + return op + + try: + + @alias.register_function(lib="jax", path="to_dense") + def _(op): + return op + + @alias.register_function(lib="jax_sparse", path="to_dense") + def _(op): + return op.todense() + + except: + pass + + @alias.register_function(lib="scipy_sparse", path="to_dense") + def _(op): + return op.toarray() + + @alias.register_fallback(path="to_dense") + def _(op): + return np.asarray(op) + + @alias.register_function(lib="iterable", path="to_dense") + def _(op): + return alias().asarray([alias().to_dense(sub_op) for sub_op in op]) diff --git a/qiskit_dynamics/arraylias/to_numeric_matrix_type.py b/qiskit_dynamics/arraylias/to_numeric_matrix_type.py new file mode 100644 index 000000000..b43ad162b --- /dev/null +++ b/qiskit_dynamics/arraylias/to_numeric_matrix_type.py @@ -0,0 +1,38 @@ +from scipy.sparse import spmatrix + + +def register_to_numeric_matrix_type(alias): + @alias.register_default(path="to_numeric_matrix_type") + def _(op): + return None + + @alias.register_function(lib="numpy", path="to_numeric_matrix_type") + def _(op): + return op + + try: + + @alias.register_function(lib="jax", path="to_numeric_matrix_type") + def _(op): + return op + + @alias.register_function(lib="jax_sparse", path="to_numeric_matrix_type") + def _(op): + return op + + except: + pass + + @alias.register_function(lib="scipy_sparse", path="to_numeric_matrix_type") + def _(op): + return op + + @alias.register_function(lib="iterable", path="to_numeric_matrix_type") + def _(op): + if isinstance(op[0], spmatrix): + return [alias().to_sparse(sub_op) for sub_op in op] + return alias().asarray([alias().to_dense(sub_op) for sub_op in op]) + + @alias.register_fallback(path="to_numeric_matrix_type") + def _(op): + return op diff --git a/qiskit_dynamics/arraylias/to_sparse.py b/qiskit_dynamics/arraylias/to_sparse.py new file mode 100644 index 000000000..7f8c1b0bc --- /dev/null +++ b/qiskit_dynamics/arraylias/to_sparse.py @@ -0,0 +1,47 @@ +import numpy as np +from scipy.sparse import csr_matrix + + +def register_to_sparse(alias): + @alias.register_default(path="to_sparse") + def _(op): + return None + + @alias.register_function(lib="numpy", path="to_sparse") + def _(op): + if op.ndim < 3: + return csr_matrix(op) + return np.array([csr_matrix(sub_op) for sub_op in op]) + + try: + from jax.experimental.sparse import BCOO + + @alias.register_function(lib="jax", path="to_sparse") + def _(op): + return BCOO.fromdense(op) + + @alias.register_function(lib="jax_sparse", path="to_sparse") + def _(op): + return op + + except ImportError: + pass + + @alias.register_function(lib="scipy_sparse", path="to_sparse") + def _(op): + return op + + @alias.register_fallback(path="to_sparse") + def _(op): + return csr_matrix(op) + + @alias.register_function(lib="iterable", path="to_sparse") + def _(op): + try: + import jax.numpy as jnp + + if isinstance(op[0], jnp.ndarray): + return BCOO.fromdense(op) + except ImportError: + return np.array([csr_matrix(sub_op) for sub_op in op]) + return np.array([csr_matrix(sub_op) for sub_op in op]) diff --git a/qiskit_dynamics/models/rotating_frame.py b/qiskit_dynamics/models/rotating_frame.py index 18a181b6f..4150bba1f 100644 --- a/qiskit_dynamics/models/rotating_frame.py +++ b/qiskit_dynamics/models/rotating_frame.py @@ -22,8 +22,8 @@ from qiskit import QiskitError from qiskit.quantum_info.operators import Operator from qiskit.quantum_info.operators.predicates import is_hermitian_matrix -from qiskit_dynamics.arraylias_state import ArrayLike -from qiskit_dynamics.arraylias_state import DYNAMICS_NUMPY as unp +from qiskit_dynamics.arraylias.arraylias_state import ArrayLike +from qiskit_dynamics.arraylias.arraylias_state import DYNAMICS_NUMPY as unp try: import jax.numpy as jnp diff --git a/qiskit_dynamics/signals/signals.py b/qiskit_dynamics/signals/signals.py index ab72fe9e3..e6f308211 100644 --- a/qiskit_dynamics/signals/signals.py +++ b/qiskit_dynamics/signals/signals.py @@ -27,9 +27,9 @@ from matplotlib import pyplot as plt from qiskit import QiskitError -from qiskit_dynamics.arraylias_state import ArrayLike -from qiskit_dynamics.arraylias_state import DYNAMICS_ALIAS as alias -from qiskit_dynamics.arraylias_state import DYNAMICS_NUMPY as unp +from qiskit_dynamics.arraylias.arraylias_state import ArrayLike +from qiskit_dynamics.arraylias.arraylias_state import DYNAMICS_ALIAS as alias +from qiskit_dynamics.arraylias.arraylias_state import DYNAMICS_NUMPY as unp class Signal: diff --git a/test/dynamics/common.py b/test/dynamics/common.py index ff03a199d..496d848ad 100644 --- a/test/dynamics/common.py +++ b/test/dynamics/common.py @@ -28,7 +28,7 @@ except ImportError: pass -from qiskit_dynamics.arraylias_state import DYNAMICS_NUMPY as unp +from qiskit_dynamics.arraylias.arraylias_state import DYNAMICS_NUMPY as unp from qiskit_dynamics.array import Array, wrap diff --git a/test/dynamics/models/test_rotating_frame.py b/test/dynamics/models/test_rotating_frame.py index d76cbdf8d..805f1a67a 100644 --- a/test/dynamics/models/test_rotating_frame.py +++ b/test/dynamics/models/test_rotating_frame.py @@ -19,8 +19,8 @@ from qiskit.quantum_info.operators import Operator from scipy.sparse import csr_matrix from qiskit_dynamics.models.rotating_frame import RotatingFrame -from qiskit_dynamics.arraylias_state import DYNAMICS_NUMPY as unp -from qiskit_dynamics.arraylias_state import ArrayLike +from qiskit_dynamics.arraylias.arraylias_state import DYNAMICS_NUMPY as unp +from qiskit_dynamics.arraylias.arraylias_state import ArrayLike from ..common import QiskitDynamicsTestCase, TestJaxBase, TestNumpyBase @@ -521,6 +521,7 @@ def test_state_transformations_no_frame_qobj_type(self): t = 0.123 y = qutip.Qobj([[1.0, 1]]) out = rotating_frame.state_into_frame(t, y) + breakpoint() self.assertTrue(isinstance(out, csr_matrix)) out = rotating_frame.state_out_of_frame(t, y) self.assertTrue(isinstance(out, csr_matrix)) diff --git a/test/dynamics/signals/test_signals.py b/test/dynamics/signals/test_signals.py index 07653f9f3..e823eecbc 100644 --- a/test/dynamics/signals/test_signals.py +++ b/test/dynamics/signals/test_signals.py @@ -19,7 +19,7 @@ from qiskit_dynamics.signals import Signal, DiscreteSignal, DiscreteSignalSum, SignalList from qiskit_dynamics.signals.signals import to_SignalSum -from qiskit_dynamics.arraylias_state import DYNAMICS_NUMPY as unp +from qiskit_dynamics.arraylias.arraylias_state import DYNAMICS_NUMPY as unp from ..common import QiskitDynamicsTestCase, TestJaxBase, TestNumpyBase diff --git a/test/dynamics/test_type_utils.py b/test/dynamics/test_type_utils.py index 40a141efa..7e041ddce 100644 --- a/test/dynamics/test_type_utils.py +++ b/test/dynamics/test_type_utils.py @@ -29,7 +29,7 @@ to_BCOO, to_numeric_matrix_type, ) -from qiskit_dynamics.arraylias_state import DYNAMICS_NUMPY as unp +from qiskit_dynamics.arraylias.arraylias_state import DYNAMICS_NUMPY as unp from .common import QiskitDynamicsTestCase, TestJaxBase, TestNumpyBase, TestQutipBase From f57418f2c1aeacecad9dfe8af71b6b466c47aa68 Mon Sep 17 00:00:00 2001 From: to24toro Date: Tue, 9 May 2023 17:52:31 +0900 Subject: [PATCH 12/17] pass test_rotating_frame --- qiskit_dynamics/arraylias/to_dense.py | 4 +- .../arraylias/to_numeric_matrix_type.py | 13 +- qiskit_dynamics/arraylias/to_sparse.py | 7 +- qiskit_dynamics/arraylias_state.py | 205 ------------------ qiskit_dynamics/models/rotating_frame.py | 31 ++- test/dynamics/models/test_rotating_frame.py | 10 +- 6 files changed, 39 insertions(+), 231 deletions(-) delete mode 100644 qiskit_dynamics/arraylias_state.py diff --git a/qiskit_dynamics/arraylias/to_dense.py b/qiskit_dynamics/arraylias/to_dense.py index 767569a5b..95f71f606 100644 --- a/qiskit_dynamics/arraylias/to_dense.py +++ b/qiskit_dynamics/arraylias/to_dense.py @@ -4,7 +4,9 @@ def register_to_dense(alias): @alias.register_default(path="to_dense") def _(op): - return None + if op is None: + return None + return op @alias.register_function(lib="numpy", path="to_dense") def _(op): diff --git a/qiskit_dynamics/arraylias/to_numeric_matrix_type.py b/qiskit_dynamics/arraylias/to_numeric_matrix_type.py index b43ad162b..680d95962 100644 --- a/qiskit_dynamics/arraylias/to_numeric_matrix_type.py +++ b/qiskit_dynamics/arraylias/to_numeric_matrix_type.py @@ -1,15 +1,24 @@ from scipy.sparse import spmatrix +from qiskit_dynamics.type_utils import isinstance_qutip_qobj def register_to_numeric_matrix_type(alias): @alias.register_default(path="to_numeric_matrix_type") def _(op): - return None + if op is None: + return None + if isinstance_qutip_qobj(op): + return alias().to_sparse(op.data) + return op @alias.register_function(lib="numpy", path="to_numeric_matrix_type") def _(op): return op + @alias.register_function(lib="operator", path="to_numeric_matrix_type") + def _(op): + return alias().to_dense(op) + try: @alias.register_function(lib="jax", path="to_numeric_matrix_type") @@ -29,7 +38,7 @@ def _(op): @alias.register_function(lib="iterable", path="to_numeric_matrix_type") def _(op): - if isinstance(op[0], spmatrix): + if isinstance(op[0], spmatrix) or isinstance_qutip_qobj(op[0]): return [alias().to_sparse(sub_op) for sub_op in op] return alias().asarray([alias().to_dense(sub_op) for sub_op in op]) diff --git a/qiskit_dynamics/arraylias/to_sparse.py b/qiskit_dynamics/arraylias/to_sparse.py index 7f8c1b0bc..e31281a02 100644 --- a/qiskit_dynamics/arraylias/to_sparse.py +++ b/qiskit_dynamics/arraylias/to_sparse.py @@ -1,11 +1,16 @@ import numpy as np from scipy.sparse import csr_matrix +from qiskit_dynamics.type_utils import isinstance_qutip_qobj def register_to_sparse(alias): @alias.register_default(path="to_sparse") def _(op): - return None + if op is None: + return None + if isinstance_qutip_qobj(op): + return op.data + return op @alias.register_function(lib="numpy", path="to_sparse") def _(op): diff --git a/qiskit_dynamics/arraylias_state.py b/qiskit_dynamics/arraylias_state.py deleted file mode 100644 index 2f67b0b60..000000000 --- a/qiskit_dynamics/arraylias_state.py +++ /dev/null @@ -1,205 +0,0 @@ -# -*- coding: utf-8 -*- - -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Configure custom instance of numpy alias for Dynamics.""" - -from typing import Union -from collections.abc import Iterable -from arraylias import numpy_alias -import numpy as np -from scipy.sparse import spmatrix, csr_matrix -from qiskit.quantum_info.operators import Operator -from .array import Array - - -try: - from jax.experimental.sparse import BCOO - import jax.numpy as jnp -except ImportError: - pass - - -DYNAMICS_ALIAS = numpy_alias() - -# Set qiskit_dynamics.array.Array to be dispatched to numpy -DYNAMICS_ALIAS.register_type(Array, "numpy") - -# register required custom versions of functions for sparse type here -DYNAMICS_ALIAS.register_type(spmatrix, lib="scipy_sparse") - -# register required custom versions of functions for BCOO type here -DYNAMICS_ALIAS.register_type(BCOO, lib="jax_sparse") - -# register required custom versions of functions for Operator type here -DYNAMICS_ALIAS.register_type(Operator, lib="operator") - -# register required custom versions of functions for Iterable type here -# need to discuss registering Iterable type because the coverage of Iterable is too broad. -DYNAMICS_ALIAS.register_type(Iterable, lib="iterable") - - -# asarray -@DYNAMICS_ALIAS.register_function(lib="iterable", path="asarray") -def _(arr): - if isinstance(arr[0], (list, tuple)): - return np.asarray(arr) - return DYNAMICS_ALIAS(like=arr[0]).asarray( - [DYNAMICS_ALIAS().asarray(sub_arr) for sub_arr in arr] - ) - - -# @DYNAMICS_ALIAS.register_function(lib="scipy_sparse", path="asarray") -# def _(arr): -# return np.asarray(arr) - - -# @DYNAMICS_ALIAS.register_function(lib="jax_sparse", path="asarray") -# def _(arr): -# return jnp.asarray(arr) - - -@DYNAMICS_ALIAS.register_fallback(path="asarray") -def _(arr): - return np.asarray(arr) - - -# to_dense -@DYNAMICS_ALIAS.register_default(path="to_dense") -def _(op): - return None - - -@DYNAMICS_ALIAS.register_function(lib="numpy", path="to_dense") -def _(op): - return op - - -@DYNAMICS_ALIAS.register_function(lib="jax", path="to_dense") -def _(op): - return op - - -@DYNAMICS_ALIAS.register_function(lib="scipy_sparse", path="to_dense") -def _(op): - return op.toarray() - - -@DYNAMICS_ALIAS.register_function(lib="jax_sparse", path="to_dense") -def _(op): - return op.todense() - - -@DYNAMICS_ALIAS.register_fallback(path="to_dense") -def _(op): - return np.asarray(op) - - -@DYNAMICS_ALIAS.register_function(lib="iterable", path="to_dense") -def _(op): - return DYNAMICS_ALIAS().asarray([DYNAMICS_ALIAS().to_dense(sub_op) for sub_op in op]) - - -# to_sparse -@DYNAMICS_ALIAS.register_default(path="to_sparse") -def _(op): - return None - - -@DYNAMICS_ALIAS.register_function(lib="numpy", path="to_sparse") -def _(op): - if op.ndim < 3: - return csr_matrix(op) - return np.array([csr_matrix(sub_op) for sub_op in op]) - - -@DYNAMICS_ALIAS.register_function(lib="jax", path="to_sparse") -def _(op): - return BCOO.fromdense(op) - - -@DYNAMICS_ALIAS.register_function(lib="scipy_sparse", path="to_sparse") -def _(op): - return op - - -@DYNAMICS_ALIAS.register_function(lib="jax_sparse", path="to_sparse") -def _(op): - return op - - -@DYNAMICS_ALIAS.register_fallback(path="to_sparse") -def _(op): - return csr_matrix(op) - - -@DYNAMICS_ALIAS.register_function(lib="iterable", path="to_sparse") -def _(op): - try: - if isinstance(op[0], jnp.ndarray): - return BCOO.fromdense(op) - except ImportError: - return np.array([csr_matrix(sub_op) for sub_op in op]) - return np.array([csr_matrix(sub_op) for sub_op in op]) - - -# to_numeric_matrix_type -@DYNAMICS_ALIAS.register_default(path="to_numeric_matrix_type") -def _(op): - return None - - -@DYNAMICS_ALIAS.register_function(lib="numpy", path="to_numeric_matrix_type") -def _(op): - return op - - -@DYNAMICS_ALIAS.register_function(lib="jax", path="to_numeric_matrix_type") -def _(op): - return op - - -@DYNAMICS_ALIAS.register_function(lib="scipy_sparse", path="to_numeric_matrix_type") -def _(op): - return op - - -@DYNAMICS_ALIAS.register_function(lib="jax_sparse", path="to_numeric_matrix_type") -def _(op): - return op - - -@DYNAMICS_ALIAS.register_function(lib="iterable", path="to_numeric_matrix_type") -def _(op): - if isinstance(op[0], spmatrix): - return [DYNAMICS_ALIAS().to_sparse(sub_op) for sub_op in op] - return DYNAMICS_ALIAS().asarray([DYNAMICS_ALIAS().to_dense(sub_op) for sub_op in op]) - - -@DYNAMICS_ALIAS.register_fallback(path="to_numeric_matrix_type") -def _(op): - return op - - -# cond -# @DYNAMICS_ALIAS.register_function(lib="numpy", path="cond") -# def _(pred, true_fun, false_fun, *operands): -# if pred: -# return true_fun(*operands) -# else: -# return false_fun(*operands) - - -DYNAMICS_NUMPY = DYNAMICS_ALIAS() - -ArrayLike = Union[DYNAMICS_ALIAS.registered_types()] diff --git a/qiskit_dynamics/models/rotating_frame.py b/qiskit_dynamics/models/rotating_frame.py index 4150bba1f..b8bcf3a37 100644 --- a/qiskit_dynamics/models/rotating_frame.py +++ b/qiskit_dynamics/models/rotating_frame.py @@ -318,10 +318,10 @@ def _conjugate_and_add( operator_in_fame_basis: Whether ``operator`` is already in the basis in which the frame operator is diagonal. vectorized_operators: Whether ``operator`` is passed as a vectorized, ``(dim**2,)`` - Array, rather than a ``(dim,dim)`` Array. + array, rather than a ``(dim,dim)`` array. Returns: - Array of the newly conjugated operator. + ArrayLike: Array of the newly conjugated operator. """ operator = unp.to_numeric_matrix_type(operator) op_to_add_in_fb = unp.to_numeric_matrix_type(op_to_add_in_fb) @@ -357,7 +357,6 @@ def _conjugate_and_add( # diagonal gives inversion exp_freq = unp.exp(self.frame_diag * t) frame_mat = exp_freq.conj().reshape(self.dim, 1) * exp_freq - # need to define unp.matmul if issparse(out): out = out.multiply(frame_mat) @@ -365,9 +364,9 @@ def _conjugate_and_add( out = out * frame_mat.data else: out = frame_mat * out - if op_to_add_in_fb is not None: - op_to_add_in_fb = unp.to_sparse(op_to_add_in_fb) + if issparse(out) or type(out).__name__ == "BCOO": + op_to_add_in_fb = unp.to_sparse(op_to_add_in_fb) out = out + op_to_add_in_fb @@ -403,10 +402,10 @@ def operator_into_frame( the frame is diagonal. return_in_frame_basis: Whether or not to return the result in the frame basis. vectorized_operators: Whether ``operator`` is passed as a vectorized, - ``(dim**2,)`` Array, rather than a ``(dim,dim)`` Array. + ``(dim**2,)`` array, rather than a ``(dim,dim)`` array. Returns: - Array: The operator in the rotating frame. + ArrayLike: The operator in the rotating frame. """ return self._conjugate_and_add( t, @@ -436,10 +435,10 @@ def operator_out_of_frame( the frame is diagonal. return_in_frame_basis: Whether or not to return the result in the frame basis. vectorized_operators: Whether ``operator`` is passed as a vectorized, ``(dim**2,)`` - Array, rather than a ``(dim,dim)`` Array. + array, rather than a ``(dim,dim)`` array. Returns: - Array: The operator out of the rotating frame. + ArrayLike: The operator out of the rotating frame. """ return self.operator_into_frame( -t, @@ -469,10 +468,10 @@ def generator_into_frame( the frame is diagonal. return_in_frame_basis: Whether or not to return the result in the frame basis. vectorized_operators: Whether ``operator`` is passed as a vectorized, ``(dim**2,)`` - Array, rather than a ``(dim,dim)`` Array. + array, rather than a ``(dim,dim)`` array. Returns: - Array: The generator in the rotating frame. + ArrayLike: The generator in the rotating frame. """ if self.frame_operator is None: return unp.to_numeric_matrix_type(operator) @@ -507,7 +506,7 @@ def generator_out_of_frame( return_in_frame_basis: Whether or not to return the result in the frame basis. Returns: - Array: The generator out of the rotating frame. + ArrayLike: The generator out of the rotating frame. """ if self.frame_operator is None: return unp.to_numeric_matrix_type(operator) @@ -567,12 +566,12 @@ def vectorized_map_into_frame( Args: time: The time :math:`t`. - op: The ``(dim**2,dim**2)`` Array. + op: The ``(dim**2,dim**2)`` array. operator_in_frame_basis: Whether the operator is in the frame basis. return_in_frame_basis: Whether the operator should be returned in the frame basis. Returns: - ``op`` in the frame. + ArrayLike: ``op`` in the frame. """ if self.frame_diag is not None: # Put the vectorized operator into the frame basis @@ -598,7 +597,7 @@ def vectorized_map_into_frame( def _enforce_anti_herm( mat: ArrayLike, atol: Optional[float] = 1e-10, rtol: Optional[float] = 1e-10 -): +) -> ArrayLike: r"""Given ``mat``, the logic of this function is: - if ``mat`` is hermitian, return ``-1j * mat`` - if ``mat`` is anti-hermitian, return ``mat`` @@ -615,7 +614,7 @@ def _enforce_anti_herm( rtol: The relative tolerance. Returns: - Array: Anti-hermitian version of ``mat``, if applicable. + ArrayLike: Anti-hermitian version of ``mat``, if applicable. Raises: ImportError: If the backend is jax and jax is not installed. diff --git a/test/dynamics/models/test_rotating_frame.py b/test/dynamics/models/test_rotating_frame.py index 805f1a67a..bcdca280d 100644 --- a/test/dynamics/models/test_rotating_frame.py +++ b/test/dynamics/models/test_rotating_frame.py @@ -521,7 +521,6 @@ def test_state_transformations_no_frame_qobj_type(self): t = 0.123 y = qutip.Qobj([[1.0, 1]]) out = rotating_frame.state_into_frame(t, y) - breakpoint() self.assertTrue(isinstance(out, csr_matrix)) out = rotating_frame.state_out_of_frame(t, y) self.assertTrue(isinstance(out, csr_matrix)) @@ -586,14 +585,13 @@ class TestRotatingJAXBCOO(QiskitDynamicsTestCase, TestJaxBase): def test_conjugate_and_add_BCOO(self): """Test _conjugate_and_add with operator being BCOO.""" - rotating_frame = RotatingFrame(self.asarray([1.0, -1.0])) + rotating_frame = RotatingFrame(unp.asarray([1.0, -1.0])) t = 0.123 op = unp.to_sparse(self.asarray([[1.0, -1j], [0.0, 1.0]])) op_to_add = unp.to_sparse(self.asarray([[0.0, -0.11j], [0.0, 1.0]])) out = rotating_frame._conjugate_and_add(t, op, op_to_add) self.assertTrue(type(out).__name__ == "BCOO") - self.assertAllClose( unp.to_dense(out), rotating_frame._conjugate_and_add(t, unp.to_dense(op), unp.to_dense(op_to_add)), @@ -606,7 +604,7 @@ def test_operator_into_frame_basis(self): rotating_frame = RotatingFrame(np.array([[1.0, 0.0], [0.0, -1.0]])) - op = to_BCOO(np.array([[1.0, -1j], [0.0, 1.0]])) + op = unp.to_sparse(self.asarray([[1.0, -1j], [0.0, 1.0]])) output = rotating_frame.operator_into_frame_basis(op) expected = rotating_frame.operator_into_frame_basis(unp.to_dense(op)) @@ -619,9 +617,9 @@ def test_operator_out_of_frame_basis(self): rotating_frame = RotatingFrame(np.array([[1.0, 0.0], [0.0, -1.0]])) - op = to_BCOO(np.array([[1.0, -1j], [0.0, 1.0]])) + op = unp.to_sparse(self.asarray([[1.0, -1j], [0.0, 1.0]])) output = rotating_frame.operator_out_of_frame_basis(op) - expected = rotating_frame.operator_out_of_frame_basis(to_array(op)) + expected = rotating_frame.operator_out_of_frame_basis(unp.to_dense(op)) self.assertAllClose(output, expected) From e018abd770fe611df6870537f1f36c5352921ada Mon Sep 17 00:00:00 2001 From: to24toro Date: Tue, 9 May 2023 18:01:10 +0900 Subject: [PATCH 13/17] fix --- qiskit_dynamics/models/rotating_frame.py | 1 - test/dynamics/models/test_rotating_frame.py | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/qiskit_dynamics/models/rotating_frame.py b/qiskit_dynamics/models/rotating_frame.py index b8bcf3a37..debb25bb4 100644 --- a/qiskit_dynamics/models/rotating_frame.py +++ b/qiskit_dynamics/models/rotating_frame.py @@ -86,7 +86,6 @@ def __init__( # if Hermitian convert to anti-Hermitian frame_operator = _enforce_anti_herm(frame_operator, atol=atol, rtol=rtol) - # TODO: need to define asarray for Operator self._frame_diag = unp.asarray(frame_operator) self._frame_basis = None self._frame_basis_adjoint = None diff --git a/test/dynamics/models/test_rotating_frame.py b/test/dynamics/models/test_rotating_frame.py index bcdca280d..0cf12dd7d 100644 --- a/test/dynamics/models/test_rotating_frame.py +++ b/test/dynamics/models/test_rotating_frame.py @@ -101,7 +101,7 @@ def test_operator_into_frame_basis_sparse_list(self): """Test state_into_frame_basis for a list of sparse arrays.""" ops = [csr_matrix([[0.0, 1.0], [1.0, 0.0]]), csr_matrix([[1.0, 0.0], [0.0, -1.0]])] - rotating_frame = RotatingFrame(np.array([[0.0, 1.0], [1.0, 0.0]])) + rotating_frame = RotatingFrame(unp.asarray([[0.0, 1.0], [1.0, 0.0]])) val = rotating_frame.operator_into_frame_basis(ops) U = rotating_frame.frame_basis @@ -602,7 +602,7 @@ def test_operator_into_frame_basis(self): frame specified as full matrix. """ - rotating_frame = RotatingFrame(np.array([[1.0, 0.0], [0.0, -1.0]])) + rotating_frame = RotatingFrame(unp.asarray([[1.0, 0.0], [0.0, -1.0]])) op = unp.to_sparse(self.asarray([[1.0, -1j], [0.0, 1.0]])) output = rotating_frame.operator_into_frame_basis(op) @@ -615,7 +615,7 @@ def test_operator_out_of_frame_basis(self): frame specified as full matrix. """ - rotating_frame = RotatingFrame(np.array([[1.0, 0.0], [0.0, -1.0]])) + rotating_frame = RotatingFrame(unp.asarray([[1.0, 0.0], [0.0, -1.0]])) op = unp.to_sparse(self.asarray([[1.0, -1j], [0.0, 1.0]])) output = rotating_frame.operator_out_of_frame_basis(op) From 54078ced9ab80a1c865465976c4ea555241e715c Mon Sep 17 00:00:00 2001 From: to24toro Date: Wed, 10 May 2023 05:07:59 +0900 Subject: [PATCH 14/17] matmul multiply rmatmul --- qiskit_dynamics/arraylias/arraylias_state.py | 6 +++++ qiskit_dynamics/arraylias/matmul.py | 17 ++++++++++++ qiskit_dynamics/arraylias/multiply.py | 17 ++++++++++++ qiskit_dynamics/arraylias/rmatmul.py | 28 ++++++++++++++++++++ qiskit_dynamics/models/rotating_frame.py | 16 +++-------- test/dynamics/test_type_utils.py | 2 +- 6 files changed, 73 insertions(+), 13 deletions(-) create mode 100644 qiskit_dynamics/arraylias/matmul.py create mode 100644 qiskit_dynamics/arraylias/multiply.py create mode 100644 qiskit_dynamics/arraylias/rmatmul.py diff --git a/qiskit_dynamics/arraylias/arraylias_state.py b/qiskit_dynamics/arraylias/arraylias_state.py index 1ea319d91..de8de5160 100644 --- a/qiskit_dynamics/arraylias/arraylias_state.py +++ b/qiskit_dynamics/arraylias/arraylias_state.py @@ -7,6 +7,9 @@ from .to_dense import register_to_dense from .to_numeric_matrix_type import register_to_numeric_matrix_type from .to_sparse import register_to_sparse +from .matmul import register_matmul +from .rmatmul import register_rmatmul +from .multiply import register_multiply from ..array import Array @@ -39,6 +42,9 @@ register_to_dense(alias=DYNAMICS_ALIAS) register_to_numeric_matrix_type(alias=DYNAMICS_ALIAS) register_to_sparse(alias=DYNAMICS_ALIAS) +register_matmul(alias=DYNAMICS_ALIAS) +register_multiply(alias=DYNAMICS_ALIAS) +register_rmatmul(alias=DYNAMICS_ALIAS) DYNAMICS_NUMPY = DYNAMICS_ALIAS() diff --git a/qiskit_dynamics/arraylias/matmul.py b/qiskit_dynamics/arraylias/matmul.py new file mode 100644 index 000000000..bd2d87f3e --- /dev/null +++ b/qiskit_dynamics/arraylias/matmul.py @@ -0,0 +1,17 @@ +def register_matmul(alias): + @alias.register_function(lib="scipy_sparse", path="matmul") + def _(x, y): + return x * y + + try: + from jax.experimental import sparse as jsparse + import jax.numpy as jnp + + jsparse_matmul = jsparse.sparsify(jnp.matmul) + + @alias.register_function(lib="jax_sparse", path="matmul") + def _(x, y): + return jsparse_matmul(x, y) + + except: + pass diff --git a/qiskit_dynamics/arraylias/multiply.py b/qiskit_dynamics/arraylias/multiply.py new file mode 100644 index 000000000..bc671bcb1 --- /dev/null +++ b/qiskit_dynamics/arraylias/multiply.py @@ -0,0 +1,17 @@ +def register_multiply(alias): + @alias.register_function(lib="scipy_sparse", path="multiply") + def _(x, y): + return x.multiply(y) + + try: + from jax.experimental import sparse as jsparse + import jax.numpy as jnp + + jsparse_multiply = jsparse.sparsify(jnp.multiply) + + @alias.register_function(lib="jax_sparse", path="multiply") + def _(x, y): + return jsparse_multiply(x, y) + + except: + pass diff --git a/qiskit_dynamics/arraylias/rmatmul.py b/qiskit_dynamics/arraylias/rmatmul.py new file mode 100644 index 000000000..cc90e4ada --- /dev/null +++ b/qiskit_dynamics/arraylias/rmatmul.py @@ -0,0 +1,28 @@ +import numpy as np + + +def register_rmatmul(alias): + @alias.register_function(lib="numpy", path="rmatmul") + def _(x, y): + return np.matmul(y, x) + + @alias.register_function(lib="scipy_sparse", path="rmatmul") + def _(x, y): + return y * x + + try: + from jax.experimental import sparse as jsparse + import jax.numpy as jnp + + jsparse_matmul = jsparse.sparsify(jnp.matmul) + + @alias.register_function(lib="jax", path="rmatmul") + def _(x, y): + return jnp.matmul(y, x) + + @alias.register_function(lib="jax_sparse", path="rmatmul") + def _(x, y): + return jsparse_matmul(y, x) + + except: + pass diff --git a/qiskit_dynamics/models/rotating_frame.py b/qiskit_dynamics/models/rotating_frame.py index debb25bb4..7d570308b 100644 --- a/qiskit_dynamics/models/rotating_frame.py +++ b/qiskit_dynamics/models/rotating_frame.py @@ -190,12 +190,8 @@ def operator_into_frame_basis( if isinstance(op, list): return [self.operator_into_frame_basis(x, convert_type=False) for x in op] - # Is it unnecessary to separate cases if they are integrated into arraylias? - elif type(op).__name__ == "BCOO": - return self.frame_basis_adjoint @ jsparse_matmul(op, self.frame_basis.data) - else: - # parentheses are necessary for sparse op evaluation - return self.frame_basis_adjoint @ (op @ self.frame_basis) + + return unp.rmatmul(unp.matmul(op, self.frame_basis), self.frame_basis_adjoint) def operator_out_of_frame_basis( self, @@ -221,12 +217,8 @@ def operator_out_of_frame_basis( if isinstance(op, list): return [self.operator_out_of_frame_basis(x, convert_type=False) for x in op] - # Is it unnecessary to separate cases if they are integrated into arraylias? - elif type(op).__name__ == "BCOO": - return self.frame_basis @ jsparse_matmul(op, self.frame_basis_adjoint.data) - else: - # parentheses are necessary for sparse op evaluation - return self.frame_basis @ (op @ self.frame_basis_adjoint) + + return unp.rmatmul(unp.matmul(op, self.frame_basis_adjoint), self.frame_basis) def state_into_frame( self, diff --git a/test/dynamics/test_type_utils.py b/test/dynamics/test_type_utils.py index 7e041ddce..c5d68a875 100644 --- a/test/dynamics/test_type_utils.py +++ b/test/dynamics/test_type_utils.py @@ -397,7 +397,7 @@ def test_to_sparse_array(self): """Tests for to_sparse with a single sparse matrix""" op = Operator.from_label("X") spm = csr_matrix(op) - ar = Array(op) + ar = unp.asarray(op) self.assertAllCloseSparse(unp.to_sparse(ar), spm) def test_to_sparse_sparse_matrix(self): From 218a33a362742abfd654e80bfd4449d2efcc82d0 Mon Sep 17 00:00:00 2001 From: to24toro Date: Wed, 10 May 2023 14:19:52 +0900 Subject: [PATCH 15/17] delete codes in _enforce_anti_herm --- qiskit_dynamics/models/rotating_frame.py | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/qiskit_dynamics/models/rotating_frame.py b/qiskit_dynamics/models/rotating_frame.py index 7d570308b..13752d422 100644 --- a/qiskit_dynamics/models/rotating_frame.py +++ b/qiskit_dynamics/models/rotating_frame.py @@ -612,28 +612,7 @@ def _enforce_anti_herm( QiskitError: If ``mat`` is not Hermitian or anti-Hermitian. """ mat = unp.to_dense(mat) - # if mat.ndim == 1: - # # this function checks if pure imaginary. If yes it returns the - # # array, otherwise it multiplies it by jnp.nan to raise an error - # # Note: pathways in conditionals in jax cannot raise Exceptions - # def anti_herm_conditional(b): - # aherm_pred = unp.allclose(b, -b.conj(), atol=atol, rtol=rtol) - # return unp.cond(aherm_pred, lambda A: A, lambda A: np.nan * A, b) - - # # Check if it is purely real, if not apply anti_herm_conditional - # herm_pred = unp.allclose(mat, mat.conj(), atol=atol, rtol=rtol) - # return unp.asarray(unp.cond(herm_pred, lambda A: -1j * A, anti_herm_conditional, mat)) - # else: - # # this function checks if anti-hermitian, if yes returns the array, - # # otherwise it multiplies it by jnp.nan - # def anti_herm_conditional(b): - # aherm_pred = unp.allclose(b, -b.conj().transpose(), atol=atol, rtol=rtol) - # return unp.cond(aherm_pred, lambda A: A, lambda A: np.nan * A, b) - - # # the following lines check if a is hermitian, otherwise it feeds - # # it into the anti_herm_conditional - # herm_pred = unp.allclose(mat, mat.conj().transpose(), atol=atol, rtol=rtol) - # return unp.asarray(unp.cond(herm_pred, lambda A: -1j * A, anti_herm_conditional, mat)) + if not isinstance(mat, np.ndarray): from jax.lax import cond From b2c4ae7a1f7354dc8f7d06805b59a5ba68d0edfe Mon Sep 17 00:00:00 2001 From: to24toro Date: Thu, 11 May 2023 20:04:18 +0900 Subject: [PATCH 16/17] rename iterable to list in arraylias --- qiskit_dynamics/arraylias/arraylias_state.py | 6 +++--- qiskit_dynamics/arraylias/asarray.py | 4 ++-- qiskit_dynamics/arraylias/to_dense.py | 2 +- qiskit_dynamics/arraylias/to_numeric_matrix_type.py | 2 +- qiskit_dynamics/arraylias/to_sparse.py | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/qiskit_dynamics/arraylias/arraylias_state.py b/qiskit_dynamics/arraylias/arraylias_state.py index de8de5160..92bb29912 100644 --- a/qiskit_dynamics/arraylias/arraylias_state.py +++ b/qiskit_dynamics/arraylias/arraylias_state.py @@ -33,9 +33,9 @@ # register required custom versions of functions for Operator type here DYNAMICS_ALIAS.register_type(Operator, lib="operator") -# register required custom versions of functions for Iterable type here -# need to discuss registering Iterable type because the coverage of Iterable is too broad. -DYNAMICS_ALIAS.register_type(Iterable, lib="iterable") +# register required custom versions of functions for List type here +# need to discuss registering List type because the coverage of List is too broad. +DYNAMICS_ALIAS.register_type(list, lib="list") register_to_asarray(alias=DYNAMICS_ALIAS) diff --git a/qiskit_dynamics/arraylias/asarray.py b/qiskit_dynamics/arraylias/asarray.py index 1b96c778d..c5eefa7ba 100644 --- a/qiskit_dynamics/arraylias/asarray.py +++ b/qiskit_dynamics/arraylias/asarray.py @@ -2,9 +2,9 @@ def register_to_asarray(alias): - @alias.register_function(lib="iterable", path="asarray") + @alias.register_function(lib="list", path="asarray") def _(arr): - if isinstance(arr[0], (list, tuple)): + if len(arr) == 0 or isinstance(arr[0], (list, tuple)): return np.asarray(arr) return alias(like=arr[0]).asarray([alias().asarray(sub_arr) for sub_arr in arr]) diff --git a/qiskit_dynamics/arraylias/to_dense.py b/qiskit_dynamics/arraylias/to_dense.py index 95f71f606..9953f8cc3 100644 --- a/qiskit_dynamics/arraylias/to_dense.py +++ b/qiskit_dynamics/arraylias/to_dense.py @@ -33,6 +33,6 @@ def _(op): def _(op): return np.asarray(op) - @alias.register_function(lib="iterable", path="to_dense") + @alias.register_function(lib="list", path="to_dense") def _(op): return alias().asarray([alias().to_dense(sub_op) for sub_op in op]) diff --git a/qiskit_dynamics/arraylias/to_numeric_matrix_type.py b/qiskit_dynamics/arraylias/to_numeric_matrix_type.py index 680d95962..816bfc189 100644 --- a/qiskit_dynamics/arraylias/to_numeric_matrix_type.py +++ b/qiskit_dynamics/arraylias/to_numeric_matrix_type.py @@ -36,7 +36,7 @@ def _(op): def _(op): return op - @alias.register_function(lib="iterable", path="to_numeric_matrix_type") + @alias.register_function(lib="list", path="to_numeric_matrix_type") def _(op): if isinstance(op[0], spmatrix) or isinstance_qutip_qobj(op[0]): return [alias().to_sparse(sub_op) for sub_op in op] diff --git a/qiskit_dynamics/arraylias/to_sparse.py b/qiskit_dynamics/arraylias/to_sparse.py index e31281a02..cee9f5098 100644 --- a/qiskit_dynamics/arraylias/to_sparse.py +++ b/qiskit_dynamics/arraylias/to_sparse.py @@ -40,7 +40,7 @@ def _(op): def _(op): return csr_matrix(op) - @alias.register_function(lib="iterable", path="to_sparse") + @alias.register_function(lib="list", path="to_sparse") def _(op): try: import jax.numpy as jnp From 2986820a74135f680d435ce65b86cdfde4f33d96 Mon Sep 17 00:00:00 2001 From: to24toro Date: Wed, 7 Jun 2023 16:46:50 +0900 Subject: [PATCH 17/17] add asarray for BCOO and scipy_sparse --- qiskit_dynamics/arraylias/asarray.py | 19 +++++++++++++++++++ qiskit_dynamics/models/rotating_frame.py | 6 ++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/qiskit_dynamics/arraylias/asarray.py b/qiskit_dynamics/arraylias/asarray.py index c5eefa7ba..838e755be 100644 --- a/qiskit_dynamics/arraylias/asarray.py +++ b/qiskit_dynamics/arraylias/asarray.py @@ -1,7 +1,14 @@ import numpy as np +from scipy.sparse import csr_matrix, issparse def register_to_asarray(alias): + @alias.register_function(lib="scipy_sparse", path="asarray") + def _(arr): + if issparse(arr): + return arr + return csr_matrix(arr) + @alias.register_function(lib="list", path="asarray") def _(arr): if len(arr) == 0 or isinstance(arr[0], (list, tuple)): @@ -11,3 +18,15 @@ def _(arr): @alias.register_fallback(path="asarray") def _(arr): return np.asarray(arr) + + try: + from jax.experimental.sparse import BCOO + + @alias.register_function(lib="jax_sparse", path="asarray") + def _(arr): + if type(arr).__name__ == "BCOO": + return arr + return BCOO.fromdense(arr) + + except ImportError: + pass diff --git a/qiskit_dynamics/models/rotating_frame.py b/qiskit_dynamics/models/rotating_frame.py index 13752d422..71f63c920 100644 --- a/qiskit_dynamics/models/rotating_frame.py +++ b/qiskit_dynamics/models/rotating_frame.py @@ -23,6 +23,7 @@ from qiskit.quantum_info.operators import Operator from qiskit.quantum_info.operators.predicates import is_hermitian_matrix from qiskit_dynamics.arraylias.arraylias_state import ArrayLike +from qiskit_dynamics.arraylias.arraylias_state import DYNAMICS_ALIAS from qiskit_dynamics.arraylias.arraylias_state import DYNAMICS_NUMPY as unp try: @@ -333,7 +334,8 @@ def _conjugate_and_add( return operator else: if op_to_add_in_fb is not None: - op_to_add_in_fb = unp.to_sparse(op_to_add_in_fb) + if issparse(out) or type(out).__name__ == "BCOO": + op_to_add_in_fb = DYNAMICS_ALIAS(like=out).asarray(op_to_add_in_fb) return operator + op_to_add_in_fb @@ -357,7 +359,7 @@ def _conjugate_and_add( out = frame_mat * out if op_to_add_in_fb is not None: if issparse(out) or type(out).__name__ == "BCOO": - op_to_add_in_fb = unp.to_sparse(op_to_add_in_fb) + op_to_add_in_fb = DYNAMICS_ALIAS(like=out).asarray(op_to_add_in_fb) out = out + op_to_add_in_fb