From 4e6c1c3219f4c9932ee03536484367af7d7fe616 Mon Sep 17 00:00:00 2001 From: Jesus Sistos Date: Tue, 2 Nov 2021 17:20:55 -0400 Subject: [PATCH 01/23] Parameter-friendly usage of Numpy methods --- .../operators/symplectic/sparse_pauli_op.py | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index e000aedd0724..38435e9e8bfb 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -28,6 +28,7 @@ from qiskit.quantum_info.operators.symplectic.pauli_table import PauliTable from qiskit.quantum_info.operators.symplectic.pauli_utils import pauli_basis from qiskit.utils.deprecation import deprecate_function +from qiskit.circuit import Parameter, ParameterExpression class SparsePauliOp(LinearOp): @@ -85,10 +86,14 @@ def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True): pauli_list = PauliList(data.copy() if copy and hasattr(data, "copy") else data) + #import pdb; pdb.set_trace() if coeffs is None: coeffs = np.ones(pauli_list.size, dtype=complex) else: - coeffs = np.array(coeffs, copy=copy, dtype=complex) + try: + coeffs = np.array(coeffs, copy=copy, dtype=complex) + except: + coeffs = np.array(coeffs, copy=copy, dtype=object) if ignore_pauli_phase: # Fast path used in copy operations, where the phase of the PauliList is already known @@ -99,7 +104,10 @@ def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True): else: # move the phase of `pauli_list` to `self._coeffs` phase = pauli_list.phase - self._coeffs = np.asarray((-1j) ** phase * coeffs, dtype=complex) + try: + self._coeffs = np.asarray((-1j) ** phase * coeffs, dtype=complex) + except: + self._coeffs = np.asarray((-1j) ** phase * coeffs, dtype=object) pauli_list._phase = np.mod(pauli_list._phase - phase, 4) self._pauli_list = pauli_list @@ -388,10 +396,15 @@ def simplify(self, atol=None, rtol=None): # Pack bool vectors into np.uint8 vectors by np.packbits array = np.packbits(self.paulis.x, axis=1) * 256 + np.packbits(self.paulis.z, axis=1) _, indexes, inverses = np.unique(array, return_index=True, return_inverse=True, axis=0) - coeffs = np.zeros(indexes.shape[0], dtype=complex) + coeffs = np.zeros(indexes.shape[0], dtype=object) np.add.at(coeffs, inverses, self.coeffs) - # Delete zero coefficient rows - is_zero = np.isclose(coeffs, 0, atol=atol, rtol=rtol) + # Delete zero coefficient rows (Ignore if dealing with Parameters) + is_zero = [] + for coeff in coeffs: + if isinstance(coeff, (Parameter, ParameterExpression)): + is_zero.append(False) + else: + is_zero.append(np.isclose(coeff, 0, atol=atol, rtol=rtol)) # Check edge case that we deleted all Paulis # In this case we return an identity Pauli with a zero coefficient if np.all(is_zero): From b48e41160f0cbd0d9187c41a06564f6883db41ba Mon Sep 17 00:00:00 2001 From: Jesus Sistos Date: Tue, 2 Nov 2021 19:55:02 -0400 Subject: [PATCH 02/23] remove pdb.set_trace() --- qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index 38435e9e8bfb..2c5ed64a08a5 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -86,7 +86,6 @@ def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True): pauli_list = PauliList(data.copy() if copy and hasattr(data, "copy") else data) - #import pdb; pdb.set_trace() if coeffs is None: coeffs = np.ones(pauli_list.size, dtype=complex) else: From e47f856d2d38c85e48ae28b524526f73f9bcdb8c Mon Sep 17 00:00:00 2001 From: Jesus Sistos Date: Tue, 2 Nov 2021 20:20:00 -0400 Subject: [PATCH 03/23] Updated target image in mpl backend visualization tests --- .../references/20_plot_circuit_layout.png | Bin 21430 -> 9311 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test/python/visualization/references/20_plot_circuit_layout.png b/test/python/visualization/references/20_plot_circuit_layout.png index 6938935224d4d80443647ffe766ec8f1a809cd99..4b94bb60ed1eec48a90c2d6059fd1d595b711797 100644 GIT binary patch literal 9311 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 literal 21430 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 Date: Thu, 4 Nov 2021 22:54:12 -0400 Subject: [PATCH 04/23] Fixed methods causing failing tests --- qiskit/circuit/gate.py | 6 ++++++ .../operators/symplectic/sparse_pauli_op.py | 18 ++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index 566f054ab22e..f0720c05164d 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -237,5 +237,11 @@ def validate_parameter(self, parameter): 3, ) return parameter + elif isinstance(parameter, (complex, np.complex)): + if np.isclose(np.imag(parameter), 0): + return np.real(parameter) + else: + msg = f"Bound parameter is complex in gate {self.name}" + raise CircuitError(msg) else: raise CircuitError(f"Invalid param type {type(parameter)} for gate {self.name}.") diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index 2c5ed64a08a5..f07451195960 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -91,7 +91,9 @@ def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True): else: try: coeffs = np.array(coeffs, copy=copy, dtype=complex) - except: + except TypeError: + #Initialize as array of objects if there are parameters. + #This is generally avoided since it makes numpy slower. coeffs = np.array(coeffs, copy=copy, dtype=object) if ignore_pauli_phase: @@ -105,7 +107,7 @@ def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True): phase = pauli_list.phase try: self._coeffs = np.asarray((-1j) ** phase * coeffs, dtype=complex) - except: + except TypeError: self._coeffs = np.asarray((-1j) ** phase * coeffs, dtype=object) pauli_list._phase = np.mod(pauli_list._phase - phase, 4) self._pauli_list = pauli_list @@ -135,9 +137,17 @@ def __repr__(self): def __eq__(self, other): """Check if two SparsePauliOp operators are equal""" + closeCoeffs = [] + for i in range(self.coeffs.shape[0]): + #Check for Parameters separately + if isinstance(self.coeffs[i], ParameterExpression): + closeCoeffs.append(self._coeffs[i] == other._coeffs[i]) + else: + closeCoeffs.append(np.isclose(self.coeffs[i], other.coeffs[i])) + return ( super().__eq__(other) - and np.allclose(self.coeffs, other.coeffs) + and np.all(closeCoeffs) and self.paulis == other.paulis ) @@ -395,7 +405,7 @@ def simplify(self, atol=None, rtol=None): # Pack bool vectors into np.uint8 vectors by np.packbits array = np.packbits(self.paulis.x, axis=1) * 256 + np.packbits(self.paulis.z, axis=1) _, indexes, inverses = np.unique(array, return_index=True, return_inverse=True, axis=0) - coeffs = np.zeros(indexes.shape[0], dtype=object) + coeffs = np.zeros(indexes.shape[0], dtype=self.coeffs.dtype) np.add.at(coeffs, inverses, self.coeffs) # Delete zero coefficient rows (Ignore if dealing with Parameters) is_zero = [] From 43f8ce0460918124f6981b1fde1010934260d8e5 Mon Sep 17 00:00:00 2001 From: Jesus Sistos Date: Thu, 4 Nov 2021 23:11:31 -0400 Subject: [PATCH 05/23] fixed lint --- .../operators/symplectic/sparse_pauli_op.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index f07451195960..0c4dc9feb999 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -92,8 +92,8 @@ def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True): try: coeffs = np.array(coeffs, copy=copy, dtype=complex) except TypeError: - #Initialize as array of objects if there are parameters. - #This is generally avoided since it makes numpy slower. + # Initialize as array of objects if there are parameters. + # This is generally avoided since it makes numpy slower. coeffs = np.array(coeffs, copy=copy, dtype=object) if ignore_pauli_phase: @@ -107,7 +107,7 @@ def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True): phase = pauli_list.phase try: self._coeffs = np.asarray((-1j) ** phase * coeffs, dtype=complex) - except TypeError: + except TypeError: self._coeffs = np.asarray((-1j) ** phase * coeffs, dtype=object) pauli_list._phase = np.mod(pauli_list._phase - phase, 4) self._pauli_list = pauli_list @@ -139,17 +139,13 @@ def __eq__(self, other): """Check if two SparsePauliOp operators are equal""" closeCoeffs = [] for i in range(self.coeffs.shape[0]): - #Check for Parameters separately + # Check for Parameters separately if isinstance(self.coeffs[i], ParameterExpression): closeCoeffs.append(self._coeffs[i] == other._coeffs[i]) else: closeCoeffs.append(np.isclose(self.coeffs[i], other.coeffs[i])) - return ( - super().__eq__(other) - and np.all(closeCoeffs) - and self.paulis == other.paulis - ) + return super().__eq__(other) and np.all(closeCoeffs) and self.paulis == other.paulis @property def settings(self) -> Dict: From 09cf891c6d8cf12b01dac11e64357625c32a3fb2 Mon Sep 17 00:00:00 2001 From: Jesus Sistos Date: Fri, 5 Nov 2021 10:51:15 -0400 Subject: [PATCH 06/23] Fixed formatting of variable in SparsePauliOP --- qiskit/circuit/gate.py | 2 +- .../operators/symplectic/sparse_pauli_op.py | 8 ++++---- .../references/20_plot_circuit_layout.png | Bin 9311 -> 21430 bytes 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index f0720c05164d..4202b5b4d4aa 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -237,7 +237,7 @@ def validate_parameter(self, parameter): 3, ) return parameter - elif isinstance(parameter, (complex, np.complex)): + elif isinstance(parameter, complex): if np.isclose(np.imag(parameter), 0): return np.real(parameter) else: diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index 0c4dc9feb999..a53d934e47c7 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -137,15 +137,15 @@ def __repr__(self): def __eq__(self, other): """Check if two SparsePauliOp operators are equal""" - closeCoeffs = [] + close_coeffs = [] for i in range(self.coeffs.shape[0]): # Check for Parameters separately if isinstance(self.coeffs[i], ParameterExpression): - closeCoeffs.append(self._coeffs[i] == other._coeffs[i]) + close_coeffs.append(self._coeffs[i] == other._coeffs[i]) else: - closeCoeffs.append(np.isclose(self.coeffs[i], other.coeffs[i])) + close_coeffs.append(np.isclose(self.coeffs[i], other.coeffs[i])) - return super().__eq__(other) and np.all(closeCoeffs) and self.paulis == other.paulis + return super().__eq__(other) and np.all(close_coeffs) and self.paulis == other.paulis @property def settings(self) -> Dict: diff --git a/test/python/visualization/references/20_plot_circuit_layout.png b/test/python/visualization/references/20_plot_circuit_layout.png index 4b94bb60ed1eec48a90c2d6059fd1d595b711797..6938935224d4d80443647ffe766ec8f1a809cd99 100644 GIT binary patch literal 21430 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 From fe4c2f3d2ce4c8351241994a583666f3829ad36c Mon Sep 17 00:00:00 2001 From: Jesus Sistos Date: Mon, 8 Nov 2021 16:07:25 -0500 Subject: [PATCH 07/23] Fixed dtype casting (object->float, complex->float) tests --- qiskit/opflow/evolutions/trotterizations/qdrift.py | 2 +- qiskit/opflow/primitive_ops/pauli_sum_op.py | 12 +++++++++++- .../operators/symplectic/sparse_pauli_op.py | 5 +---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/qiskit/opflow/evolutions/trotterizations/qdrift.py b/qiskit/opflow/evolutions/trotterizations/qdrift.py index 5f805112aa78..37f0c5453f56 100644 --- a/qiskit/opflow/evolutions/trotterizations/qdrift.py +++ b/qiskit/opflow/evolutions/trotterizations/qdrift.py @@ -73,7 +73,7 @@ def convert(self, operator: OperatorBase) -> OperatorBase: # and multiplication by a constant factor. scaled_ops = [(op * (factor / op.coeff)).exp_i() for op in operator_iter] sampled_ops = algorithm_globals.random.choice( - scaled_ops, size=(int(N * self.reps),), p=weights / lambd + scaled_ops, size=(int(N * self.reps),), p=np.array(weights / lambd, dtype=float) ) return ComposedOp(sampled_ops).reduce() diff --git a/qiskit/opflow/primitive_ops/pauli_sum_op.py b/qiskit/opflow/primitive_ops/pauli_sum_op.py index 8310d39d8a5f..86907156c9e0 100644 --- a/qiskit/opflow/primitive_ops/pauli_sum_op.py +++ b/qiskit/opflow/primitive_ops/pauli_sum_op.py @@ -16,6 +16,7 @@ from typing import Dict, List, Optional, Set, Tuple, Union, cast import numpy as np +from qiskit.circuit.parameter import Parameter from scipy.sparse import spmatrix from qiskit.circuit import Instruction, ParameterExpression @@ -357,7 +358,16 @@ def to_native(x): Pauli((self.primitive.paulis.z[0], self.primitive.paulis.x[0])), to_native(np.real_if_close(self.primitive.coeffs[0])) * self.coeff, ) - coeffs = np.real_if_close(self.primitive.coeffs) + if not self.primitive.coeffs.dtype==object: + coeffs = np.real_if_close(self.primitive.coeffs) + else: + coeffs = [] + for coeff in self.primitive.coeffs: + if not isinstance(coeff, (Parameter, ParameterExpression)): + coeffs.append(np.real_if_close(coeff)) + else: + coeffs.append(coeff) + return SummedOp( [ PauliOp(pauli, to_native(coeff)) diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index a53d934e47c7..13c8bf355343 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -105,10 +105,7 @@ def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True): else: # move the phase of `pauli_list` to `self._coeffs` phase = pauli_list.phase - try: - self._coeffs = np.asarray((-1j) ** phase * coeffs, dtype=complex) - except TypeError: - self._coeffs = np.asarray((-1j) ** phase * coeffs, dtype=object) + self._coeffs = np.asarray((-1j) ** phase * coeffs, dtype=coeffs.dtype) pauli_list._phase = np.mod(pauli_list._phase - phase, 4) self._pauli_list = pauli_list From 3ea8a9da8a9b6da218c5d5ab02e89f9e495cff0a Mon Sep 17 00:00:00 2001 From: Jesus Sistos Date: Thu, 11 Nov 2021 13:36:24 -0500 Subject: [PATCH 08/23] Fixed failing tests --- qiskit/opflow/primitive_ops/pauli_sum_op.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/qiskit/opflow/primitive_ops/pauli_sum_op.py b/qiskit/opflow/primitive_ops/pauli_sum_op.py index 86907156c9e0..572b08848a01 100644 --- a/qiskit/opflow/primitive_ops/pauli_sum_op.py +++ b/qiskit/opflow/primitive_ops/pauli_sum_op.py @@ -358,16 +358,16 @@ def to_native(x): Pauli((self.primitive.paulis.z[0], self.primitive.paulis.x[0])), to_native(np.real_if_close(self.primitive.coeffs[0])) * self.coeff, ) + coeffs = np.real_if_close(self.primitive.coeffs) if not self.primitive.coeffs.dtype==object: coeffs = np.real_if_close(self.primitive.coeffs) else: coeffs = [] for coeff in self.primitive.coeffs: if not isinstance(coeff, (Parameter, ParameterExpression)): - coeffs.append(np.real_if_close(coeff)) + coeffs.append(np.real_if_close(coeff).item()) else: coeffs.append(coeff) - return SummedOp( [ PauliOp(pauli, to_native(coeff)) @@ -452,4 +452,10 @@ def is_zero(self) -> bool: return op.coeff == 1 and len(op) == 1 and primitive.coeffs[0] == 0 def is_hermitian(self): - return np.isreal(self.coeffs).all() and np.all(self.primitive.paulis.phase == 0) + if not self.coeffs.dtype == object: + return np.isreal(self.coeffs).all() and np.all(self.primitive.paulis.phase == 0) + else: + is_real = [] + for coeff in self.coeffs: + is_real.append(np.isreal(coeff)) + return np.all(is_real) and np.all(self.primitive.paulis.phase == 0) From 8cec77732cb9e4f8c6df39685448e83e5bded332 Mon Sep 17 00:00:00 2001 From: Jesus Sistos Date: Thu, 11 Nov 2021 13:48:43 -0500 Subject: [PATCH 09/23] Fixed lint --- qiskit/opflow/primitive_ops/pauli_sum_op.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/opflow/primitive_ops/pauli_sum_op.py b/qiskit/opflow/primitive_ops/pauli_sum_op.py index 572b08848a01..9b4e3bd9f5e3 100644 --- a/qiskit/opflow/primitive_ops/pauli_sum_op.py +++ b/qiskit/opflow/primitive_ops/pauli_sum_op.py @@ -359,7 +359,7 @@ def to_native(x): to_native(np.real_if_close(self.primitive.coeffs[0])) * self.coeff, ) coeffs = np.real_if_close(self.primitive.coeffs) - if not self.primitive.coeffs.dtype==object: + if not self.primitive.coeffs.dtype == object: coeffs = np.real_if_close(self.primitive.coeffs) else: coeffs = [] From 4dd2e02b9a6fa8a0f5349ca821812f7cc5dc06fd Mon Sep 17 00:00:00 2001 From: Jesus Sistos Date: Thu, 11 Nov 2021 15:01:18 -0500 Subject: [PATCH 10/23] Fixed import order for linter --- qiskit/opflow/primitive_ops/pauli_sum_op.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qiskit/opflow/primitive_ops/pauli_sum_op.py b/qiskit/opflow/primitive_ops/pauli_sum_op.py index 9b4e3bd9f5e3..ba558763999e 100644 --- a/qiskit/opflow/primitive_ops/pauli_sum_op.py +++ b/qiskit/opflow/primitive_ops/pauli_sum_op.py @@ -16,10 +16,9 @@ from typing import Dict, List, Optional, Set, Tuple, Union, cast import numpy as np -from qiskit.circuit.parameter import Parameter from scipy.sparse import spmatrix -from qiskit.circuit import Instruction, ParameterExpression +from qiskit.circuit import Instruction, Parameter, ParameterExpression from qiskit.opflow.exceptions import OpflowError from qiskit.opflow.list_ops.summed_op import SummedOp from qiskit.opflow.list_ops.tensored_op import TensoredOp From 38ff348bf49a43404bdef3202cdcc355d0b4167b Mon Sep 17 00:00:00 2001 From: ikkoham Date: Fri, 26 Aug 2022 10:47:14 +0900 Subject: [PATCH 11/23] revert gate.py --- qiskit/circuit/gate.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index 434ff0d888c2..b271aa161c82 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -226,11 +226,5 @@ def validate_parameter(self, parameter): return parameter elif isinstance(parameter, (np.integer, np.floating)): return parameter.item() - elif isinstance(parameter, complex): - if np.isclose(np.imag(parameter), 0): - return np.real(parameter) - else: - msg = f"Bound parameter is complex in gate {self.name}" - raise CircuitError(msg) else: raise CircuitError(f"Invalid param type {type(parameter)} for gate {self.name}.") From 1ce842db15198eef8c6f81a0c7e667cc3b8624c6 Mon Sep 17 00:00:00 2001 From: ikkoham Date: Fri, 26 Aug 2022 10:48:52 +0900 Subject: [PATCH 12/23] revert qdrift.py --- qiskit/opflow/evolutions/trotterizations/qdrift.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/opflow/evolutions/trotterizations/qdrift.py b/qiskit/opflow/evolutions/trotterizations/qdrift.py index 18e2e16a06cb..89c6ced0b144 100644 --- a/qiskit/opflow/evolutions/trotterizations/qdrift.py +++ b/qiskit/opflow/evolutions/trotterizations/qdrift.py @@ -73,7 +73,7 @@ def convert(self, operator: OperatorBase) -> OperatorBase: # and multiplication by a constant factor. scaled_ops = [(op * (factor / op.coeff)).exp_i() for op in operator_iter] sampled_ops = algorithm_globals.random.choice( - scaled_ops, size=(int(N * self.reps),), p=np.array(weights / lambd, dtype=float) + scaled_ops, size=(int(N * self.reps),), p=weights / lambd ) return ComposedOp(sampled_ops).reduce() From 8721f125aec025268db03eb01450111c8b236533 Mon Sep 17 00:00:00 2001 From: ikkoham Date: Fri, 26 Aug 2022 14:27:37 +0900 Subject: [PATCH 13/23] revert PauliSumOp --- qiskit/opflow/primitive_ops/pauli_sum_op.py | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/qiskit/opflow/primitive_ops/pauli_sum_op.py b/qiskit/opflow/primitive_ops/pauli_sum_op.py index 0b1fe426f77e..d0c55ee391d4 100644 --- a/qiskit/opflow/primitive_ops/pauli_sum_op.py +++ b/qiskit/opflow/primitive_ops/pauli_sum_op.py @@ -18,7 +18,7 @@ import numpy as np from scipy.sparse import spmatrix -from qiskit.circuit import Instruction, Parameter, ParameterExpression +from qiskit.circuit import Instruction, ParameterExpression from qiskit.opflow.exceptions import OpflowError from qiskit.opflow.list_ops.summed_op import SummedOp from qiskit.opflow.list_ops.tensored_op import TensoredOp @@ -370,15 +370,6 @@ def to_native(x): to_native(np.real_if_close(self.primitive.coeffs[0])) * self.coeff, ) coeffs = np.real_if_close(self.primitive.coeffs) - if not self.primitive.coeffs.dtype == object: - coeffs = np.real_if_close(self.primitive.coeffs) - else: - coeffs = [] - for coeff in self.primitive.coeffs: - if not isinstance(coeff, (Parameter, ParameterExpression)): - coeffs.append(np.real_if_close(coeff).item()) - else: - coeffs.append(coeff) return SummedOp( [ PauliOp(pauli, to_native(coeff)) @@ -463,10 +454,4 @@ def is_zero(self) -> bool: return op.coeff == 1 and len(op) == 1 and primitive.coeffs[0] == 0 def is_hermitian(self): - if not self.coeffs.dtype == object: - return np.isreal(self.coeffs).all() and np.all(self.primitive.paulis.phase == 0) - else: - is_real = [] - for coeff in self.coeffs: - is_real.append(np.isreal(coeff)) - return np.all(is_real) and np.all(self.primitive.paulis.phase == 0) + return np.isreal(self.coeffs).all() and np.all(self.primitive.paulis.phase == 0) From fbdb649c0c83c9a773bf8021551755562792e684 Mon Sep 17 00:00:00 2001 From: ikkoham Date: Fri, 26 Aug 2022 14:38:03 +0900 Subject: [PATCH 14/23] update SparsePauliOp --- .../operators/symplectic/sparse_pauli_op.py | 68 +++++++++++-------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index e7623ba46c75..2b416c47e0db 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -21,7 +21,6 @@ import retworkx as rx from qiskit._accelerate.sparse_pauli_op import unordered_unique # pylint: disable=import-error -from qiskit.circuit import Parameter, ParameterExpression from qiskit.exceptions import QiskitError from qiskit.quantum_info.operators.custom_iterator import CustomIterator from qiskit.quantum_info.operators.linear_op import LinearOp @@ -51,7 +50,7 @@ class SparsePauliOp(LinearOp): the :attr:`~SparsePauliOp.coeffs` attribute. """ - def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True): + def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True, dtype=None): """Initialize an operator object. Args: @@ -72,6 +71,7 @@ def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True): this option when giving :obj:`~PauliList` data. (Default: False) copy (bool): copy the input data if True, otherwise assign it directly, if possible. (Default: True) + dtype (type | None): dtype for coeffs. Raises: QiskitError: If the input data or coeffs are invalid. @@ -89,15 +89,13 @@ def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True): pauli_list = PauliList(data.copy() if copy and hasattr(data, "copy") else data) + if dtype is None: + dtype = coeffs.dtype if isinstance(coeffs, np.ndarray) else complex + if coeffs is None: - coeffs = np.ones(pauli_list.size, dtype=complex) + coeffs = np.ones(pauli_list.size, dtype=dtype) else: - try: - coeffs = np.array(coeffs, copy=copy, dtype=complex) - except TypeError: - # Initialize as array of objects if there are parameters. - # This is generally avoided since it makes numpy slower. - coeffs = np.array(coeffs, copy=copy, dtype=object) + coeffs = np.array(coeffs, copy=copy, dtype=dtype) if ignore_pauli_phase: # Fast path used in copy operations, where the phase of the PauliList is already known @@ -138,19 +136,16 @@ def __repr__(self): def __eq__(self, other): """Entrywise comparison of two SparsePauliOp operators""" - close_coeffs = [] - for i in range(self.coeffs.shape[0]): - # Check for Parameters separately - if isinstance(self.coeffs[i], ParameterExpression): - close_coeffs.append(self._coeffs[i] == other._coeffs[i]) - else: - close_coeffs.append(np.isclose(self.coeffs[i], other.coeffs[i])) - return ( super().__eq__(other) + and self.coeffs.dtype == other.coeffs.dtype and self.coeffs.shape == other.coeffs.shape - and np.all(close_coeffs) and self.paulis == other.paulis + and ( + np.allclose(self.coeffs, other.coeffs) + if self.coeffs.dtype != object + else (self.coeffs == other.coeffs).all() + ) ) def equiv(self, other, atol: Optional[float] = None): @@ -424,7 +419,19 @@ def simplify(self, atol=None, rtol=None): rtol = self.rtol # Filter non-zero coefficients - non_zero = np.logical_not(np.isclose(self.coeffs, 0, atol=atol, rtol=rtol)) + if self.coeffs.dtype == object: + non_zero = np.logical_not( + [ + np.isclose(coeff, 0, atol=atol, rtol=rtol) + if not hasattr(coeff, "sympify") + else complex(coeff.sympify()) + if coeff.sympify().is_Number + else False # Symbol is not zero. + for coeff in self.coeffs + ] + ) + else: + non_zero = np.logical_not(np.isclose(self.coeffs, 0, atol=atol, rtol=rtol)) paulis_x = self.paulis.x[non_zero] paulis_z = self.paulis.z[non_zero] nz_coeffs = self.coeffs[non_zero] @@ -437,21 +444,28 @@ def simplify(self, atol=None, rtol=None): # No zero operator or duplicate operator return self.copy() - coeffs = np.zeros(indexes.shape[0], dtype=complex) + coeffs = np.zeros(indexes.shape[0], dtype=self.coeffs.dtype) np.add.at(coeffs, inverses, nz_coeffs) # Delete zero coefficient rows - is_zero = [] - for coeff in coeffs: - if isinstance(coeff, (Parameter, ParameterExpression)): - is_zero.append(False) - else: - is_zero.append(np.isclose(coeff, 0, atol=atol, rtol=rtol)) + if self.coeffs.dtype == object: + is_zero = np.array( + [ + np.isclose(coeff, 0, atol=atol, rtol=rtol) + if not hasattr(coeff, "sympify") + else complex(coeff.sympify()) + if coeff.sympify().is_Number + else False # Symbol is not zero. + for coeff in coeffs + ] + ) + else: + is_zero = np.isclose(coeffs, 0, atol=atol, rtol=rtol) # Check edge case that we deleted all Paulis # In this case we return an identity Pauli with a zero coefficient if np.all(is_zero): x = np.zeros((1, self.num_qubits), dtype=bool) z = np.zeros((1, self.num_qubits), dtype=bool) - coeffs = np.array([0j], dtype=complex) + coeffs = np.array([0j], dtype=self.coeffs.dtype) else: non_zero = np.logical_not(is_zero) non_zero_indexes = indexes[non_zero] From d01b52e4b0b476ecb3034e77de64972130305870 Mon Sep 17 00:00:00 2001 From: ikkoham Date: Wed, 21 Sep 2022 01:01:44 +0900 Subject: [PATCH 15/23] readable code (Jake's suggestion) --- .../operators/symplectic/sparse_pauli_op.py | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index 2b416c47e0db..ab8e58b0ec49 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -420,15 +420,15 @@ def simplify(self, atol=None, rtol=None): # Filter non-zero coefficients if self.coeffs.dtype == object: + + def to_complex(coeff): + if not hasattr(coeff, "sympify"): + return coeff + sympified = coeff.sympify() + return complex(sympified) if sympified.is_Number else np.nan + non_zero = np.logical_not( - [ - np.isclose(coeff, 0, atol=atol, rtol=rtol) - if not hasattr(coeff, "sympify") - else complex(coeff.sympify()) - if coeff.sympify().is_Number - else False # Symbol is not zero. - for coeff in self.coeffs - ] + np.isclose([to_complex(x) for x in self.coeffs], 0, atol=atol, rtol=rtol) ) else: non_zero = np.logical_not(np.isclose(self.coeffs, 0, atol=atol, rtol=rtol)) @@ -449,14 +449,7 @@ def simplify(self, atol=None, rtol=None): # Delete zero coefficient rows if self.coeffs.dtype == object: is_zero = np.array( - [ - np.isclose(coeff, 0, atol=atol, rtol=rtol) - if not hasattr(coeff, "sympify") - else complex(coeff.sympify()) - if coeff.sympify().is_Number - else False # Symbol is not zero. - for coeff in coeffs - ] + [np.isclose(to_complex(coeff), 0, atol=atol, rtol=rtol) for coeff in coeffs] ) else: is_zero = np.isclose(coeffs, 0, atol=atol, rtol=rtol) From 823931854b12d465cf9e49958fc0cb093f0e38f2 Mon Sep 17 00:00:00 2001 From: ikkoham Date: Sat, 24 Sep 2022 10:54:51 +0900 Subject: [PATCH 16/23] add tests --- .../operators/symplectic/sparse_pauli_op.py | 20 +- .../symplectic/test_sparse_pauli_op.py | 355 +++++++++++++----- 2 files changed, 267 insertions(+), 108 deletions(-) diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index ab8e58b0ec49..e99cbc49521c 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -50,7 +50,7 @@ class SparsePauliOp(LinearOp): the :attr:`~SparsePauliOp.coeffs` attribute. """ - def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True, dtype=None): + def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True): """Initialize an operator object. Args: @@ -71,7 +71,6 @@ def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True, dt this option when giving :obj:`~PauliList` data. (Default: False) copy (bool): copy the input data if True, otherwise assign it directly, if possible. (Default: True) - dtype (type | None): dtype for coeffs. Raises: QiskitError: If the input data or coeffs are invalid. @@ -89,8 +88,7 @@ def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True, dt pauli_list = PauliList(data.copy() if copy and hasattr(data, "copy") else data) - if dtype is None: - dtype = coeffs.dtype if isinstance(coeffs, np.ndarray) else complex + dtype = coeffs.dtype if isinstance(coeffs, np.ndarray) else complex if coeffs is None: coeffs = np.ones(pauli_list.size, dtype=dtype) @@ -710,7 +708,7 @@ def from_operator(obj, atol=None, rtol=None): return SparsePauliOp(paulis, coeffs, copy=False) @staticmethod - def from_list(obj): + def from_list(obj, dtype=complex): """Construct from a list of Pauli strings and coefficients. For example, the 5-qubit Hamiltonian @@ -728,6 +726,7 @@ def from_list(obj): Args: obj (Iterable[Tuple[str, complex]]): The list of 2-tuples specifying the Pauli terms. + dtype (type): The dtype of coeffs (Default complex). Returns: SparsePauliOp: The SparsePauliOp representation of the Pauli terms. @@ -744,7 +743,7 @@ def from_list(obj): # determine the number of qubits num_qubits = len(obj[0][0]) - coeffs = np.zeros(size, dtype=complex) + coeffs = np.zeros(size, dtype=dtype) labels = np.zeros(size, dtype=f" bool: @@ -747,8 +901,11 @@ def commutes(left: Pauli, right: Pauli) -> bool: input_labels = ["IX", "IY", "IZ", "XX", "YY", "ZZ", "XY", "YX", "ZX", "ZY", "XZ", "YZ"] np.random.shuffle(input_labels) - coefs = np.random.random(len(input_labels)) + np.random.random(len(input_labels)) * 1j - sparse_pauli_list = SparsePauliOp(input_labels, coefs) + if parameterized: + coeffs = np.array(ParameterVector("a", len(input_labels))) + else: + coeffs = np.random.random(len(input_labels)) + np.random.random(len(input_labels)) * 1j + sparse_pauli_list = SparsePauliOp(input_labels, coeffs) groups = sparse_pauli_list.group_commuting() # checking that every input Pauli in sparse_pauli_list is in a group in the ouput output_labels = [pauli.to_label() for group in groups for pauli in group.paulis] @@ -757,7 +914,7 @@ def commutes(left: Pauli, right: Pauli) -> bool: paulis_coeff_dict = dict( sum([list(zip(group.paulis.to_labels(), group.coeffs)) for group in groups], []) ) - self.assertDictEqual(dict(zip(input_labels, coefs)), paulis_coeff_dict) + self.assertDictEqual(dict(zip(input_labels, coeffs)), paulis_coeff_dict) # Within each group, every operator commutes with every other operator. for group in groups: From 23a37d583be0f0a18f94a8d20059f30a86017753 Mon Sep 17 00:00:00 2001 From: ikkoham Date: Mon, 26 Sep 2022 11:24:52 +0900 Subject: [PATCH 17/23] add docs --- .../operators/symplectic/sparse_pauli_op.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index e99cbc49521c..df66642d2a60 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -48,6 +48,34 @@ class SparsePauliOp(LinearOp): using the :attr:`~SparsePauliOp.paulis` attribute. The coefficients are stored as a complex Numpy array vector and can be accessed using the :attr:`~SparsePauliOp.coeffs` attribute. + + ------------- + dtype + ------------- + + The default dtype of coeffs is complex. + User can configure dtype by passing ``np.ndarray`` with different dtype. + For example, parameterized SparsePauliOp can be made as follows: + + .. code-block:: python + + >>> import numpy as np + >>> from qiskit.circuit import ParameterVector + >>> from qiskit.quantum_info import SparsePauliOp + + >>> SparsePauliOp(["II", "XZ"], np.array(ParameterVector("a", 2))) + SparsePauliOp(['II', 'XZ'], + coeffs=[ParameterExpression(1.0*a[0]), ParameterExpression(1.0*a[1])]) + + .. note:: + + Parameterized SparasePauliOp does not supprot the following methods: + + - ``to_matrix(sparse=True)`` since scipy.sparse cannot have objects as elements. + - ``to_operator()`` since Operator does not support objects. + - ``sort``, ``argsort`` since Parameter does not support comparison. + - ``equiv`` since Parameter cannot be converted into complex. + """ def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True): From ab6392b673631019069acf546d3ffd87087618d8 Mon Sep 17 00:00:00 2001 From: ikkoham Date: Mon, 26 Sep 2022 11:58:11 +0900 Subject: [PATCH 18/23] fix docs --- qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index df66642d2a60..24c557c0990c 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -49,9 +49,7 @@ class SparsePauliOp(LinearOp): are stored as a complex Numpy array vector and can be accessed using the :attr:`~SparsePauliOp.coeffs` attribute. - ------------- - dtype - ------------- + **dtype of coefficients** The default dtype of coeffs is complex. User can configure dtype by passing ``np.ndarray`` with different dtype. From 6c6219c37a3720a838cdb51023f9e8c50bd6e73c Mon Sep 17 00:00:00 2001 From: ikkoham Date: Mon, 26 Sep 2022 12:34:57 +0900 Subject: [PATCH 19/23] fix typo --- qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index 1e544ffead4c..6870ac59c0e0 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -65,7 +65,7 @@ class SparsePauliOp(LinearOp): .. note:: - Parameterized SparasePauliOp does not supprot the following methods: + Parameterized SparasePauliOp does not support the following methods: - ``to_matrix(sparse=True)`` since scipy.sparse cannot have objects as elements. - ``to_operator()`` since Operator does not support objects. From 2833a2710d1a28d15c83585f7bc90a333b917600 Mon Sep 17 00:00:00 2001 From: ikkoham Date: Mon, 26 Sep 2022 12:35:13 +0900 Subject: [PATCH 20/23] add reno --- releasenotes/notes/operator-parameters-c81b7c05bffb740b.yaml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 releasenotes/notes/operator-parameters-c81b7c05bffb740b.yaml diff --git a/releasenotes/notes/operator-parameters-c81b7c05bffb740b.yaml b/releasenotes/notes/operator-parameters-c81b7c05bffb740b.yaml new file mode 100644 index 000000000000..67f948b3b2d1 --- /dev/null +++ b/releasenotes/notes/operator-parameters-c81b7c05bffb740b.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Flexible dtype for :class:`~SparsePauliOp` is supported. From d51ce56cc25287b06bd727f9129868062e4c0007 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Tue, 27 Sep 2022 19:19:52 +0100 Subject: [PATCH 21/23] Update documentation --- .../operators/symplectic/sparse_pauli_op.py | 19 ++++++++------- .../operator-parameters-c81b7c05bffb740b.yaml | 23 ++++++++++++++++++- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index 6870ac59c0e0..e3459bae173e 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -47,11 +47,11 @@ class SparsePauliOp(LinearOp): are stored as a complex Numpy array vector and can be accessed using the :attr:`~SparsePauliOp.coeffs` attribute. - **dtype of coefficients** + .. rubric:: Data type of coefficients - The default dtype of coeffs is complex. - User can configure dtype by passing ``np.ndarray`` with different dtype. - For example, parameterized SparsePauliOp can be made as follows: + The default ``dtype`` of the internal ``coeffs`` Numpy array is ``complex128``. Users can + configure this by passing ``np.ndarray`` with a different dtype. For example, a parameterized + :class:`SparsePauliOp` can be made as follows: .. code-block:: python @@ -65,13 +65,12 @@ class SparsePauliOp(LinearOp): .. note:: - Parameterized SparasePauliOp does not support the following methods: - - - ``to_matrix(sparse=True)`` since scipy.sparse cannot have objects as elements. - - ``to_operator()`` since Operator does not support objects. - - ``sort``, ``argsort`` since Parameter does not support comparison. - - ``equiv`` since Parameter cannot be converted into complex. + Parameterized :class:`SparsePauliOp` does not support the following methods: + - ``to_matrix(sparse=True)`` since ``scipy.sparse`` cannot have objects as elements. + - ``to_operator()`` since :class:`~.quantum_info.Operator` does not support objects. + - ``sort``, ``argsort`` since :class:`.ParameterExpression` does not support comparison. + - ``equiv`` since :class:`.ParameterExpression`. cannot be converted into complex. """ def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True): diff --git a/releasenotes/notes/operator-parameters-c81b7c05bffb740b.yaml b/releasenotes/notes/operator-parameters-c81b7c05bffb740b.yaml index 67f948b3b2d1..5d6edf00bad7 100644 --- a/releasenotes/notes/operator-parameters-c81b7c05bffb740b.yaml +++ b/releasenotes/notes/operator-parameters-c81b7c05bffb740b.yaml @@ -1,4 +1,25 @@ --- features: - | - Flexible dtype for :class:`~SparsePauliOp` is supported. + :class:`.SparsePauliOp`\ s can now be constructed with coefficient arrays + that are general Python objects. This is purely intended for use with Terra's + :class:`.ParameterExpression` objects; other objects may work, but do not + have first-class support. Some :class:`.SparsePauliOp` methods (such as + conversion to other class representations) may not work when using ``object`` + arrays, if the desired target cannot represent these general arrays. + + For example, a :class:`.ParameterExpression` :class:`.SparsePauliOp` could + be constructed by:: + + import numpy as np + from qiskit.circuit import Parameter + from qiskit.quantum_info import SparsePauliOp + + print(SparsePauliOp(["II", "XZ"], np.array([Parameter("a"), Parameter("b")]))) + + which gives + + .. code-block:: text + + SparsePauliOp(['II', 'XZ'], + coeffs=[ParameterExpression(1.0*a), ParameterExpression(1.0*b)]) From a28d543cf7eeb246f056e7a2314be52e4abad84e Mon Sep 17 00:00:00 2001 From: ikkoham Date: Wed, 28 Sep 2022 11:07:20 +0900 Subject: [PATCH 22/23] use-parameters --- .../symplectic/test_sparse_pauli_op.py | 101 +++++++++--------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py b/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py index 0d4c6de59d37..62ac3841f60b 100644 --- a/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py +++ b/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py @@ -364,61 +364,62 @@ def bind_one(a): return complex(a.bind(dict(zip(parameters, [1] * len(parameters))))) self.vbind_one = np.vectorize(bind_one, otypes=[complex]) + self.parameter_names = (f"param_{x}" for x in it.count()) - def random_spp_op(self, num_qubits, num_terms, params=None): + def random_spp_op(self, num_qubits, num_terms, use_parameters=False): """Generate a pseudo-random SparsePauliOp""" - if params is None: + if use_parameters: + coeffs = np.array(ParameterVector(next(self.parameter_names), num_terms)) + else: coeffs = self.RNG.uniform(-1, 1, size=num_terms) + 1j * self.RNG.uniform( -1, 1, size=num_terms ) - else: - coeffs = np.array(ParameterVector(params, num_terms)) labels = [ "".join(self.RNG.choice(["I", "X", "Y", "Z"], size=num_qubits)) for _ in range(num_terms) ] return SparsePauliOp(labels, coeffs) - @combine(num_qubits=[1, 2, 3, 4], param=[None, "a"]) - def test_conjugate(self, num_qubits, param): + @combine(num_qubits=[1, 2, 3, 4], use_parameters=[True, False]) + def test_conjugate(self, num_qubits, use_parameters): """Test conjugate method for {num_qubits}-qubits.""" - spp_op = self.random_spp_op(num_qubits, 2**num_qubits, param) + spp_op = self.random_spp_op(num_qubits, 2**num_qubits, use_parameters) target = spp_op.to_matrix().conjugate() op = spp_op.conjugate() value = op.to_matrix() np.testing.assert_array_equal(value, target) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) - @combine(num_qubits=[1, 2, 3, 4], param=[None, "a"]) - def test_transpose(self, num_qubits, param): + @combine(num_qubits=[1, 2, 3, 4], use_parameters=[True, False]) + def test_transpose(self, num_qubits, use_parameters): """Test transpose method for {num_qubits}-qubits.""" - spp_op = self.random_spp_op(num_qubits, 2**num_qubits, param) + spp_op = self.random_spp_op(num_qubits, 2**num_qubits, use_parameters) target = spp_op.to_matrix().transpose() op = spp_op.transpose() value = op.to_matrix() np.testing.assert_array_equal(value, target) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) - @combine(num_qubits=[1, 2, 3, 4], param=[None, "a"]) - def test_adjoint(self, num_qubits, param): + @combine(num_qubits=[1, 2, 3, 4], use_parameters=[True, False]) + def test_adjoint(self, num_qubits, use_parameters): """Test adjoint method for {num_qubits}-qubits.""" - spp_op = self.random_spp_op(num_qubits, 2**num_qubits, param) + spp_op = self.random_spp_op(num_qubits, 2**num_qubits, use_parameters) target = spp_op.to_matrix().transpose().conjugate() op = spp_op.adjoint() value = op.to_matrix() np.testing.assert_array_equal(value, target) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) - @combine(num_qubits=[1, 2, 3, 4], params=[(None, None), ("a", "b")]) - def test_compose(self, num_qubits, params): + @combine(num_qubits=[1, 2, 3, 4], use_parameters=[True, False]) + def test_compose(self, num_qubits, use_parameters): """Test {num_qubits}-qubit compose methods.""" - spp_op1 = self.random_spp_op(num_qubits, 2**num_qubits, params[0]) - spp_op2 = self.random_spp_op(num_qubits, 2**num_qubits, params[1]) + spp_op1 = self.random_spp_op(num_qubits, 2**num_qubits, use_parameters) + spp_op2 = self.random_spp_op(num_qubits, 2**num_qubits, use_parameters) target = spp_op2.to_matrix() @ spp_op1.to_matrix() op = spp_op1.compose(spp_op2) value = op.to_matrix() - if params[0] is not None: + if use_parameters: value = self.vbind_one(value) target = self.vbind_one(target) np.testing.assert_allclose(value, target, atol=1e-8) @@ -426,21 +427,21 @@ def test_compose(self, num_qubits, params): op = spp_op1 & spp_op2 value = op.to_matrix() - if params[0] is not None: + if use_parameters: value = self.vbind_one(value) np.testing.assert_allclose(value, target, atol=1e-8) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) - @combine(num_qubits=[1, 2, 3, 4], params=[(None, None), ("a", "b")]) - def test_dot(self, num_qubits, params): + @combine(num_qubits=[1, 2, 3, 4], use_parameters=[True, False]) + def test_dot(self, num_qubits, use_parameters): """Test {num_qubits}-qubit dot methods.""" - spp_op1 = self.random_spp_op(num_qubits, 2**num_qubits, params[0]) - spp_op2 = self.random_spp_op(num_qubits, 2**num_qubits, params[1]) + spp_op1 = self.random_spp_op(num_qubits, 2**num_qubits, use_parameters) + spp_op2 = self.random_spp_op(num_qubits, 2**num_qubits, use_parameters) target = spp_op1.to_matrix() @ spp_op2.to_matrix() op = spp_op1.dot(spp_op2) value = op.to_matrix() - if params[0] is not None: + if use_parameters: value = self.vbind_one(value) target = self.vbind_one(target) np.testing.assert_allclose(value, target, atol=1e-8) @@ -448,7 +449,7 @@ def test_dot(self, num_qubits, params): op = spp_op1 @ spp_op2 value = op.to_matrix() - if params[0] is not None: + if use_parameters: value = self.vbind_one(value) np.testing.assert_allclose(value, target, atol=1e-8) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) @@ -484,57 +485,57 @@ def test_qargs_dot(self, num_qubits): self.assertEqual(value, target) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) - @combine(num_qubits1=[1, 2, 3], num_qubits2=[1, 2, 3], params=[(None, None), ("a", "b")]) - def test_tensor(self, num_qubits1, num_qubits2, params): + @combine(num_qubits1=[1, 2, 3], num_qubits2=[1, 2, 3], use_parameters=[True, False]) + def test_tensor(self, num_qubits1, num_qubits2, use_parameters): """Test tensor method for {num_qubits1} and {num_qubits2} qubits.""" - spp_op1 = self.random_spp_op(num_qubits1, 2**num_qubits1, params[0]) - spp_op2 = self.random_spp_op(num_qubits2, 2**num_qubits2, params[1]) + spp_op1 = self.random_spp_op(num_qubits1, 2**num_qubits1, use_parameters) + spp_op2 = self.random_spp_op(num_qubits2, 2**num_qubits2, use_parameters) target = np.kron(spp_op1.to_matrix(), spp_op2.to_matrix()) op = spp_op1.tensor(spp_op2) value = op.to_matrix() - if params[0] is not None: + if use_parameters: value = self.vbind_one(value) target = self.vbind_one(target) np.testing.assert_allclose(value, target, atol=1e-8) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) - @combine(num_qubits1=[1, 2, 3], num_qubits2=[1, 2, 3], params=[(None, None), ("a", "b")]) - def test_expand(self, num_qubits1, num_qubits2, params): + @combine(num_qubits1=[1, 2, 3], num_qubits2=[1, 2, 3], use_parameters=[True, False]) + def test_expand(self, num_qubits1, num_qubits2, use_parameters): """Test expand method for {num_qubits1} and {num_qubits2} qubits.""" - spp_op1 = self.random_spp_op(num_qubits1, 2**num_qubits1, params[0]) - spp_op2 = self.random_spp_op(num_qubits2, 2**num_qubits2, params[1]) + spp_op1 = self.random_spp_op(num_qubits1, 2**num_qubits1, use_parameters) + spp_op2 = self.random_spp_op(num_qubits2, 2**num_qubits2, use_parameters) target = np.kron(spp_op2.to_matrix(), spp_op1.to_matrix()) op = spp_op1.expand(spp_op2) value = op.to_matrix() - if params[0] is not None: + if use_parameters: value = self.vbind_one(value) target = self.vbind_one(target) np.testing.assert_allclose(value, target, atol=1e-8) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) - @combine(num_qubits=[1, 2, 3, 4], params=[(None, None), ("a", "b")]) - def test_add(self, num_qubits, params): + @combine(num_qubits=[1, 2, 3, 4], use_parameters=[True, False]) + def test_add(self, num_qubits, use_parameters): """Test + method for {num_qubits} qubits.""" - spp_op1 = self.random_spp_op(num_qubits, 2**num_qubits, params[0]) - spp_op2 = self.random_spp_op(num_qubits, 2**num_qubits, params[1]) + spp_op1 = self.random_spp_op(num_qubits, 2**num_qubits, use_parameters) + spp_op2 = self.random_spp_op(num_qubits, 2**num_qubits, use_parameters) target = spp_op1.to_matrix() + spp_op2.to_matrix() op = spp_op1 + spp_op2 value = op.to_matrix() - if params[0] is not None: + if use_parameters: value = self.vbind_one(value) target = self.vbind_one(target) np.testing.assert_allclose(value, target, atol=1e-8) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) - @combine(num_qubits=[1, 2, 3, 4], params=[(None, None), ("a", "b")]) - def test_sub(self, num_qubits, params): + @combine(num_qubits=[1, 2, 3, 4], use_parameters=[True, False]) + def test_sub(self, num_qubits, use_parameters): """Test + method for {num_qubits} qubits.""" - spp_op1 = self.random_spp_op(num_qubits, 2**num_qubits, params[0]) - spp_op2 = self.random_spp_op(num_qubits, 2**num_qubits, params[1]) + spp_op1 = self.random_spp_op(num_qubits, 2**num_qubits, use_parameters) + spp_op2 = self.random_spp_op(num_qubits, 2**num_qubits, use_parameters) target = spp_op1.to_matrix() - spp_op2.to_matrix() op = spp_op1 - spp_op2 value = op.to_matrix() - if params[0] is not None: + if use_parameters: value = self.vbind_one(value) target = self.vbind_one(target) np.testing.assert_allclose(value, target, atol=1e-8) @@ -831,12 +832,12 @@ def test_sum_error(self): with self.assertRaises(QiskitError): SparsePauliOp.sum([1, 2]) - @combine(num_qubits=[1, 2, 3, 4], params=[(None, None, None), ("a", "b", "c")]) - def test_eq(self, num_qubits, params): + @combine(num_qubits=[1, 2, 3, 4], use_parameters=[True, False]) + def test_eq(self, num_qubits, use_parameters): """Test __eq__ method for {num_qubits} qubits.""" - spp_op1 = self.random_spp_op(num_qubits, 2**num_qubits, params[0]) - spp_op2 = self.random_spp_op(num_qubits, 2**num_qubits, params[1]) - spp_op3 = self.random_spp_op(num_qubits, 2**num_qubits, params[2]) + spp_op1 = self.random_spp_op(num_qubits, 2**num_qubits, use_parameters) + spp_op2 = self.random_spp_op(num_qubits, 2**num_qubits, use_parameters) + spp_op3 = self.random_spp_op(num_qubits, 2**num_qubits, use_parameters) zero = spp_op3 - spp_op3 self.assertEqual(spp_op1, spp_op1) self.assertEqual(spp_op2, spp_op2) From f8e418eac81393fce2bc3338882a367a2b1668d3 Mon Sep 17 00:00:00 2001 From: ikkoham Date: Wed, 28 Sep 2022 11:23:49 +0900 Subject: [PATCH 23/23] bind_parameters_to_one --- .../symplectic/test_sparse_pauli_op.py | 62 +++++++++++-------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py b/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py index 62ac3841f60b..bf58220d1d63 100644 --- a/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py +++ b/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py @@ -350,6 +350,19 @@ def test_matrix_iter_sparse(self): np.testing.assert_array_equal(i.toarray(), coeffs[idx] * pauli_mat(labels[idx])) +def bind_parameters_to_one(array): + """Bind parameters to one. The purpose of using this method is to bind some value and + use ``assert_allclose``, since it is impossible to verify equivalence in the case of + numerical errors with parameters existing. + """ + + def bind_one(a): + parameters = a.parameters + return complex(a.bind(dict(zip(parameters, [1] * len(parameters))))) + + return np.vectorize(bind_one, otypes=[complex])(array) + + @ddt class TestSparsePauliOpMethods(QiskitTestCase): """Tests for SparsePauliOp operator methods.""" @@ -359,11 +372,6 @@ class TestSparsePauliOpMethods(QiskitTestCase): def setUp(self): super().setUp() - def bind_one(a): - parameters = a.parameters - return complex(a.bind(dict(zip(parameters, [1] * len(parameters))))) - - self.vbind_one = np.vectorize(bind_one, otypes=[complex]) self.parameter_names = (f"param_{x}" for x in it.count()) def random_spp_op(self, num_qubits, num_terms, use_parameters=False): @@ -420,15 +428,15 @@ def test_compose(self, num_qubits, use_parameters): op = spp_op1.compose(spp_op2) value = op.to_matrix() if use_parameters: - value = self.vbind_one(value) - target = self.vbind_one(target) + value = bind_parameters_to_one(value) + target = bind_parameters_to_one(target) np.testing.assert_allclose(value, target, atol=1e-8) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) op = spp_op1 & spp_op2 value = op.to_matrix() if use_parameters: - value = self.vbind_one(value) + value = bind_parameters_to_one(value) np.testing.assert_allclose(value, target, atol=1e-8) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) @@ -442,15 +450,15 @@ def test_dot(self, num_qubits, use_parameters): op = spp_op1.dot(spp_op2) value = op.to_matrix() if use_parameters: - value = self.vbind_one(value) - target = self.vbind_one(target) + value = bind_parameters_to_one(value) + target = bind_parameters_to_one(target) np.testing.assert_allclose(value, target, atol=1e-8) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) op = spp_op1 @ spp_op2 value = op.to_matrix() if use_parameters: - value = self.vbind_one(value) + value = bind_parameters_to_one(value) np.testing.assert_allclose(value, target, atol=1e-8) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) @@ -494,8 +502,8 @@ def test_tensor(self, num_qubits1, num_qubits2, use_parameters): op = spp_op1.tensor(spp_op2) value = op.to_matrix() if use_parameters: - value = self.vbind_one(value) - target = self.vbind_one(target) + value = bind_parameters_to_one(value) + target = bind_parameters_to_one(target) np.testing.assert_allclose(value, target, atol=1e-8) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) @@ -508,8 +516,8 @@ def test_expand(self, num_qubits1, num_qubits2, use_parameters): op = spp_op1.expand(spp_op2) value = op.to_matrix() if use_parameters: - value = self.vbind_one(value) - target = self.vbind_one(target) + value = bind_parameters_to_one(value) + target = bind_parameters_to_one(target) np.testing.assert_allclose(value, target, atol=1e-8) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) @@ -522,8 +530,8 @@ def test_add(self, num_qubits, use_parameters): op = spp_op1 + spp_op2 value = op.to_matrix() if use_parameters: - value = self.vbind_one(value) - target = self.vbind_one(target) + value = bind_parameters_to_one(value) + target = bind_parameters_to_one(target) np.testing.assert_allclose(value, target, atol=1e-8) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) @@ -536,8 +544,8 @@ def test_sub(self, num_qubits, use_parameters): op = spp_op1 - spp_op2 value = op.to_matrix() if use_parameters: - value = self.vbind_one(value) - target = self.vbind_one(target) + value = bind_parameters_to_one(value) + target = bind_parameters_to_one(target) np.testing.assert_allclose(value, target, atol=1e-8) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) @@ -573,8 +581,8 @@ def test_mul(self, num_qubits, value, param): op = value * spp_op value_mat = op.to_matrix() if value != 0 and param is not None: - value_mat = self.vbind_one(value_mat) - target = self.vbind_one(target) + value_mat = bind_parameters_to_one(value_mat) + target = bind_parameters_to_one(target) if value == 0: np.testing.assert_array_equal(value_mat, target.astype(complex)) else: @@ -584,8 +592,8 @@ def test_mul(self, num_qubits, value, param): op = spp_op * value value_mat = op.to_matrix() if value != 0 and param is not None: - value_mat = self.vbind_one(value_mat) - target = self.vbind_one(target) + value_mat = bind_parameters_to_one(value_mat) + target = bind_parameters_to_one(target) if value == 0: np.testing.assert_array_equal(value_mat, target.astype(complex)) else: @@ -600,8 +608,8 @@ def test_div(self, num_qubits, value, param): op = spp_op / value value_mat = op.to_matrix() if param is not None: - value_mat = self.vbind_one(value_mat) - target = self.vbind_one(target) + value_mat = bind_parameters_to_one(value_mat) + target = bind_parameters_to_one(target) np.testing.assert_allclose(value_mat, target, atol=1e-8) np.testing.assert_array_equal(op.paulis.phase, np.zeros(op.size)) @@ -815,8 +823,8 @@ def test_sum(self, num_qubits, num_ops, param): value = sum_op.to_matrix() target_operator = sum((op.to_matrix() for op in ops[1:]), ops[0].to_matrix()) if param is not None: - value = self.vbind_one(value) - target_operator = self.vbind_one(target_operator) + value = bind_parameters_to_one(value) + target_operator = bind_parameters_to_one(target_operator) np.testing.assert_allclose(value, target_operator, atol=1e-8) target_spp_op = sum((op for op in ops[1:]), ops[0]) self.assertEqual(sum_op, target_spp_op)