diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index 566f054ab22e..4202b5b4d4aa 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): + 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/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..ba558763999e 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, 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 @@ -358,6 +358,15 @@ 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)) @@ -442,4 +451,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) diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index e000aedd0724..13c8bf355343 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): @@ -88,7 +89,12 @@ def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True): 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 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: # Fast path used in copy operations, where the phase of the PauliList is already known @@ -99,7 +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 - self._coeffs = np.asarray((-1j) ** phase * coeffs, dtype=complex) + 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 @@ -128,11 +134,15 @@ def __repr__(self): def __eq__(self, other): """Check if two SparsePauliOp operators are equal""" - return ( - super().__eq__(other) - and np.allclose(self.coeffs, other.coeffs) - and self.paulis == other.paulis - ) + 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 np.all(close_coeffs) and self.paulis == other.paulis @property def settings(self) -> Dict: @@ -388,10 +398,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=self.coeffs.dtype) 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):