def add(self, other: OperatorBase) -> OperatorBase: if not self.num_qubits == other.num_qubits: raise ValueError( "Sum over operators with different numbers of qubits, {} and {}, is not well " "defined".format(self.num_qubits, other.num_qubits) ) if isinstance(other, PauliOp) and self.primitive == other.primitive: return PauliOp(self.primitive, coeff=self.coeff + other.coeff) # pylint: disable=cyclic-import from .pauli_sum_op import PauliSumOp if ( isinstance(other, PauliOp) and isinstance(self.coeff, (int, float, complex)) and isinstance(other.coeff, (int, float, complex)) ): return PauliSumOp( SparsePauliOp(self.primitive, coeffs=[self.coeff]) + SparsePauliOp(other.primitive, coeffs=[other.coeff]) ) if isinstance(other, PauliSumOp) and isinstance(self.coeff, (int, float, complex)): return PauliSumOp(SparsePauliOp(self.primitive, coeffs=[self.coeff])) + other return SummedOp([self, other])
def init_observable(observable: BaseOperator | PauliSumOp) -> SparsePauliOp: """Initialize observable by converting the input to a :class:`~qiskit.quantum_info.SparsePauliOp`. Args: observable: The observable. Returns: The observable as :class:`~qiskit.quantum_info.SparsePauliOp`. Raises: TypeError: If the observable is a :class:`~qiskit.opflow.PauliSumOp` and has a parameterized coefficient. """ if isinstance(observable, SparsePauliOp): return observable elif isinstance(observable, PauliSumOp): if isinstance(observable.coeff, ParameterExpression): raise TypeError( f"Observable must have numerical coefficient, not {type(observable.coeff)}." ) return observable.coeff * observable.primitive elif isinstance(observable, BasePauli): return SparsePauliOp(observable) elif isinstance(observable, BaseOperator): return SparsePauliOp.from_operator(observable) else: return SparsePauliOp(observable)
def _to_sparse_pauli_op(operator): """Cast the operator to a SparsePauliOp. For Opflow objects, return a global coefficient that must be multiplied to the evolution time. Since this coefficient might contain unbound parameters it cannot be absorbed into the coefficients of the SparsePauliOp. """ # pylint: disable=cyclic-import from qiskit.opflow import PauliSumOp, PauliOp if isinstance(operator, PauliSumOp): sparse_pauli = operator.primitive sparse_pauli._coeffs *= operator.coeff elif isinstance(operator, PauliOp): sparse_pauli = SparsePauliOp(operator.primitive) sparse_pauli._coeffs *= operator.coeff elif isinstance(operator, Pauli): sparse_pauli = SparsePauliOp(operator) elif isinstance(operator, SparsePauliOp): sparse_pauli = operator else: raise ValueError( f"Unsupported operator type for evolution: {type(operator)}.") return sparse_pauli
def _replace_pauli_sums(cls, operator): try: from qiskit.providers.aer.library import SaveExpectationValue except ImportError as ex: raise MissingOptionalLibraryError( libname="qiskit-aer", name="AerPauliExpectation", pip_install="pip install qiskit-aer", ) from ex # The 'expval_measurement' label on the save instruction is special - the # CircuitSampler will look for it to know that the circuit is a Expectation # measurement, and not simply a # circuit to replace with a DictStateFn if operator.__class__ == ListOp: return operator.traverse(cls._replace_pauli_sums) if isinstance(operator, PauliSumOp): save_instruction = SaveExpectationValue(operator.primitive, "expval_measurement") return CircuitStateFn(save_instruction, coeff=operator.coeff, is_measurement=True, from_operator=True) # Change to Pauli representation if necessary if {"Pauli"} != operator.primitive_strings(): logger.warning( "Measured Observable is not composed of only Paulis, converting to " "Pauli representation, which can be expensive.") # Setting massive=False because this conversion is implicit. User can perform this # action on the Observable with massive=True explicitly if they so choose. operator = operator.to_pauli_op(massive=False) if isinstance(operator, SummedOp): sparse_pauli = reduce(add, (meas.coeff * SparsePauliOp(meas.primitive) for meas in operator.oplist)) save_instruction = SaveExpectationValue(sparse_pauli, "expval_measurement") return CircuitStateFn(save_instruction, coeff=operator.coeff, is_measurement=True, from_operator=True) if isinstance(operator, PauliOp): sparse_pauli = operator.coeff * SparsePauliOp(operator.primitive) save_instruction = SaveExpectationValue(sparse_pauli, "expval_measurement") return CircuitStateFn(save_instruction, is_measurement=True, from_operator=True) raise TypeError( f"Conversion of OperatorStateFn of {operator.__class__.__name__} is not defined." )
def test_add(self): """ add test """ pauli_sum = 3 * X + Y self.assertIsInstance(pauli_sum, PauliSumOp) expected = PauliSumOp(3.0 * SparsePauliOp(Pauli("X")) + SparsePauliOp(Pauli("Y"))) self.assertEqual(pauli_sum, expected) pauli_sum = X + Y summed_op = SummedOp([X, Y]) self.assertEqual(pauli_sum, summed_op)
def test_adjoint(self): """ adjoint test """ pauli_sum = PauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[2]), coeff=3) expected = PauliSumOp(SparsePauliOp(Pauli("XYZX")), coeff=-6) self.assertEqual(pauli_sum.adjoint(), expected) pauli_sum = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=3j) expected = PauliSumOp(SparsePauliOp(Pauli("XYZY")), coeff=-6j) self.assertEqual(pauli_sum.adjoint(), expected)
def test_coder_operators(self): """Test runtime encoder and decoder for operators.""" x = Parameter("x") y = x + 1 qc = QuantumCircuit(1) qc.h(0) coeffs = np.array([1, 2, 3, 4, 5, 6]) table = PauliTable.from_labels( ["III", "IXI", "IYY", "YIZ", "XYZ", "III"]) op = (2.0 * I ^ I) z2_symmetries = Z2Symmetries( [Pauli("IIZI"), Pauli("ZIII")], [Pauli("IIXI"), Pauli("XIII")], [1, 3], [-1, 1]) isqrt2 = 1 / np.sqrt(2) sparse = scipy.sparse.csr_matrix([[0, isqrt2, 0, isqrt2]]) subtests = ( PauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[2]), coeff=3), PauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[1]), coeff=y), PauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[1 + 2j]), coeff=3 - 2j), PauliSumOp.from_list([("II", -1.052373245772859), ("IZ", 0.39793742484318045)]), PauliSumOp(SparsePauliOp(table, coeffs), coeff=10), MatrixOp(primitive=np.array([[0, -1j], [1j, 0]]), coeff=x), PauliOp(primitive=Pauli("Y"), coeff=x), CircuitOp(qc, coeff=x), EvolvedOp(op, coeff=x), TaperedPauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[2]), z2_symmetries), StateFn(qc, coeff=x), CircuitStateFn(qc, is_measurement=True), DictStateFn("1" * 3, is_measurement=True), VectorStateFn(np.ones(2**3, dtype=complex)), OperatorStateFn(CircuitOp(QuantumCircuit(1))), SparseVectorStateFn(sparse), Statevector([1, 0]), CVaRMeasurement(Z, 0.2), ComposedOp([(X ^ Y ^ Z), (Z ^ X ^ Y ^ Z).to_matrix_op()]), SummedOp([X ^ X * 2, Y ^ Y], 2), TensoredOp([(X ^ Y), (Z ^ I)]), (Z ^ Z) ^ (I ^ 2), ) for op in subtests: with self.subTest(op=op): encoded = json.dumps(op, cls=RuntimeEncoder) self.assertIsInstance(encoded, str) decoded = json.loads(encoded, cls=RuntimeDecoder) self.assertEqual(op, decoded)
def test_grouped_pauli_statefn(self): """grouped pauli test with statefn""" grouped_pauli = PauliSumOp(SparsePauliOp(["Y"]), grouping_type="TPB") observable = OperatorStateFn(grouped_pauli, is_measurement=True) converter = PauliBasisChange( replacement_fn=PauliBasisChange.measurement_replacement_fn) cob = converter.convert(observable) expected = PauliSumOp(SparsePauliOp(["Z"]), grouping_type="TPB") self.assertEqual(cob[0].primitive, expected) circuit = QuantumCircuit(1) circuit.sdg(0) circuit.h(0) self.assertEqual(cob[1].primitive, circuit)
def test_equals(self): """ equality test """ self.assertNotEqual((X ^ X) + (Y ^ Y), X + Y) self.assertEqual((X ^ X) + (Y ^ Y), (Y ^ Y) + (X ^ X)) theta = ParameterVector("theta", 2) pauli_sum0 = theta[0] * (X + Z) pauli_sum1 = theta[1] * (X + Z) expected = PauliSumOp( SparsePauliOp(Pauli("X")) + SparsePauliOp(Pauli("Z")), coeff=1.0 * theta[0], ) self.assertEqual(pauli_sum0, expected) self.assertNotEqual(pauli_sum1, expected)
def _expand_dim(self, num_qubits: int) -> "PauliSumOp": return PauliSumOp( self.primitive.tensor( # type:ignore SparsePauliOp(Pauli("I" * num_qubits)) ), coeff=self.coeff, )
def _expval_params(operator, variance=False): # Convert O to SparsePauliOp representation if isinstance(operator, Pauli): operator = SparsePauliOp(operator) elif not isinstance(operator, SparsePauliOp): operator = SparsePauliOp.from_operator(Operator(operator)) if not isinstance(operator, SparsePauliOp): raise ExtensionError("Invalid input operator") params = {} # Add Pauli basis components of O for pauli, coeff in operator.label_iter(): if pauli in params: coeff1 = params[pauli][0] params[pauli] = (coeff1 + coeff.real, 0) else: params[pauli] = (coeff.real, 0) # Add Pauli basis components of O^2 if variance: for pauli, coeff in operator.dot(operator).label_iter(): if pauli in params: coeff1, coeff2 = params[pauli] params[pauli] = (coeff1, coeff2 + coeff.real) else: params[pauli] = (0, coeff.real) # Convert to list return list(params.items())
def test_permute(self): """permute test""" pauli_sum = PauliSumOp(SparsePauliOp((X ^ Y ^ Z).primitive)) expected = PauliSumOp(SparsePauliOp((X ^ I ^ Y ^ Z ^ I).primitive)) self.assertEqual(pauli_sum.permute([1, 2, 4]), expected) pauli_sum = PauliSumOp(SparsePauliOp((X ^ Y ^ Z).primitive)) expected = PauliSumOp(SparsePauliOp((Z ^ Y ^ X).primitive)) self.assertEqual(pauli_sum.permute([2, 1, 0]), expected) with self.assertRaises(OpflowError): pauli_sum.permute([1, 2, 1]) with self.assertRaises(OpflowError): pauli_sum.permute([1, 2, -1])
def _sort_simplify(sparse_pauli): sparse_pauli = sparse_pauli.simplify() indices = sparse_pauli.paulis.argsort() table = sparse_pauli.paulis[indices] coeffs = sparse_pauli.coeffs[indices] sparse_pauli = SparsePauliOp(table, coeffs) return sparse_pauli
def compose( self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False ) -> OperatorBase: new_self, other = self._expand_shorter_operator_and_permute(other, permutation) new_self = cast(PauliOp, new_self) if front: return other.compose(new_self) # If self is identity, just return other. if not any(new_self.primitive.x + new_self.primitive.z): return other * new_self.coeff # Both Paulis if isinstance(other, PauliOp): product = new_self.primitive.dot(other.primitive) return PrimitiveOp(product, coeff=new_self.coeff * other.coeff) # pylint: disable=cyclic-import from .pauli_sum_op import PauliSumOp if isinstance(other, PauliSumOp): return PauliSumOp( SparsePauliOp(new_self.primitive).dot(other.primitive), coeff=new_self.coeff * other.coeff, ) # pylint: disable=cyclic-import from ..state_fns.circuit_state_fn import CircuitStateFn from .circuit_op import CircuitOp if isinstance(other, (CircuitOp, CircuitStateFn)): return new_self.to_circuit_op().compose(other) return super(PauliOp, new_self).compose(other)
def test_decoder_import(self): """Test runtime decoder importing modules.""" script = """ import sys import json from qiskit.providers.ibmq.runtime import RuntimeDecoder if __name__ == '__main__': obj = json.loads(sys.argv[1], cls=RuntimeDecoder) print(obj.__class__.__name__) """ temp_fp = tempfile.NamedTemporaryFile(mode='w', delete=False) self.addCleanup(os.remove, temp_fp.name) temp_fp.write(script) temp_fp.close() subtests = ( PauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[2]), coeff=3), DictStateFn("1" * 3, is_measurement=True), Statevector([1, 0]), ) for op in subtests: with self.subTest(op=op): encoded = json.dumps(op, cls=RuntimeEncoder) self.assertIsInstance(encoded, str) cmd = ["python", temp_fp.name, encoded] proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, check=True) self.assertIn(op.__class__.__name__, proc.stdout)
def permute(self, permutation: List[int]) -> "PauliSumOp": """Permutes the sequence of ``PauliSumOp``. Args: permutation: A list defining where each Pauli should be permuted. The Pauli at index j of the primitive should be permuted to position permutation[j]. Returns: A new PauliSumOp representing the permuted operator. For operator (X ^ Y ^ Z) and indices=[1,2,4], it returns (X ^ I ^ Y ^ Z ^ I). Raises: OpflowError: if indices do not define a new index for each qubit. """ if len(permutation) != self.num_qubits: raise OpflowError( "List of indices to permute must have the same size as Pauli Operator" ) length = max(permutation) + 1 spop = self.primitive.tensor( SparsePauliOp(Pauli("I" * (length - self.num_qubits)))) permutation = [i for i in range(length) if i not in permutation ] + permutation permu_arr = np.arange(length)[np.argsort(permutation)] spop.paulis.x = spop.paulis.x[:, permu_arr] spop.paulis.z = spop.paulis.z[:, permu_arr] return PauliSumOp(spop, self.coeff)
def save_expval_params(pauli=False): """Dictionary of labels and params, qubits for exp val snapshots.""" if pauli: X_wpo = Pauli('X') Y_wpo = Pauli('Y') Z_wpo = Pauli('Z') H_wpo = np.sqrt(0.5) * (SparsePauliOp('X') + SparsePauliOp('Z')) IX_wpo = Pauli('IX') IY_wpo = Pauli('IY') IZ_wpo = Pauli('IZ') IH_wpo = np.sqrt(0.5) * (SparsePauliOp('IX') + SparsePauliOp('IZ')) XX_wpo = Pauli('XX') YY_wpo = Pauli('YY') ZZ_wpo = Pauli('ZZ') else: X_wpo = np.array([[0, 1], [1, 0]], dtype=complex) Y_wpo = np.array([[0, -1j], [1j, 0]], dtype=complex) Z_wpo = np.array([[1, 0], [0, -1]], dtype=complex) H_wpo = np.array([[1, 1], [1, -1]], dtype=complex) / np.sqrt(2) IX_wpo = np.kron(np.eye(2), X_wpo) IY_wpo = np.kron(np.eye(2), Y_wpo) IZ_wpo = np.kron(np.eye(2), Z_wpo) IH_wpo = np.kron(np.eye(2), H_wpo) XX_wpo = np.kron(X_wpo, X_wpo) YY_wpo = np.kron(Y_wpo, Y_wpo) ZZ_wpo = np.kron(Z_wpo, Z_wpo) return { "<H[0]>": (H_wpo, [0]), "<H[1]>": (H_wpo, [1]), "<X[0]>": (X_wpo, [0]), "<X[1]>": (X_wpo, [1]), "<Y[1]>": (Y_wpo, [0]), "<Y[1]>": (Y_wpo, [1]), "<Z[0]>": (Z_wpo, [0]), "<Z[1]>": (Z_wpo, [1]), "<H[0], I[1]>": (IH_wpo, [0, 1]), "<I[0], H[1]>": (IH_wpo, [1, 0]), "<X[0], I[1]>": (IX_wpo, [0, 1]), "<I[0], X[1]>": (IX_wpo, [1, 0]), "<Y[0], I[1]>": (IY_wpo, [0, 1]), "<I[0], Y[1]>": (IY_wpo, [1, 0]), "<Z[0], I[1]>": (IZ_wpo, [0, 1]), "<I[0], Z[1]>": (IZ_wpo, [1, 0]), "<X[0], X[1]>": (XX_wpo, [0, 1]), "<Y[0], Y[1]>": (YY_wpo, [0, 1]), "<Z[0], Z[1]>": (ZZ_wpo, [0, 1]), }
def test_matrix_iter(self): """Test PauliSumOp dense matrix_iter method.""" labels = ["III", "IXI", "IYY", "YIZ", "XYZ", "III"] coeffs = np.array([1, 2, 3, 4, 5, 6]) table = PauliTable.from_labels(labels) coeff = 10 op = PauliSumOp(SparsePauliOp(table, coeffs), coeff) for idx, i in enumerate(op.matrix_iter()): self.assertTrue(np.array_equal(i, coeff * coeffs[idx] * Pauli(labels[idx]).to_matrix()))
def test_add(self): """add test""" pauli_sum = 3 * X + Y self.assertIsInstance(pauli_sum, PauliSumOp) expected = PauliSumOp(3.0 * SparsePauliOp(Pauli("X")) + SparsePauliOp(Pauli("Y"))) self.assertEqual(pauli_sum, expected) pauli_sum = X + Y summed_op = SummedOp([X, Y]) self.assertEqual(pauli_sum, summed_op) a = Parameter("a") b = Parameter("b") actual = a * PauliSumOp.from_list([("X", 2)]) + b * PauliSumOp.from_list([("Y", 1)]) expected = SummedOp( [PauliSumOp.from_list([("X", 2)], a), PauliSumOp.from_list([("Y", 1)], b)] ) self.assertEqual(actual, expected)
def test_construct(self): """ constructor test """ sparse_pauli = SparsePauliOp(Pauli("XYZX"), coeffs=[2.0]) coeff = 3.0 pauli_sum = PauliSumOp(sparse_pauli, coeff=coeff) self.assertIsInstance(pauli_sum, PauliSumOp) self.assertEqual(pauli_sum.primitive, sparse_pauli) self.assertEqual(pauli_sum.coeff, coeff) self.assertEqual(pauli_sum.num_qubits, 4)
def _convert_to_paulisumop(operator): """Attempt to convert the operator to a PauliSumOp.""" if isinstance(operator, PauliSumOp): return operator try: primitive = SparsePauliOp(operator.primitive) return PauliSumOp(primitive, operator.coeff) except Exception as exc: raise ValueError(f"Invalid type of the operator {type(operator)} " "must be PauliSumOp, or castable to one.") from exc
def test_matrix_iter_sparse(self): """Test PauliSumOp sparse matrix_iter method.""" labels = ['III', 'IXI', 'IYY', 'YIZ', 'XYZ', 'III'] coeffs = np.array([1, 2, 3, 4, 5, 6]) coeff = 10 table = PauliTable.from_labels(labels) op = PauliSumOp(SparsePauliOp(table, coeffs), coeff) for idx, i in enumerate(op.matrix_iter(sparse=True)): self.assertTrue( np.array_equal(i.toarray(), coeff * coeffs[idx] * Pauli(labels[idx]).to_matrix()))
def test_bksf_edge_op_aij(self): """Test BKSF mapping, edge operator aij""" edge_matrix = np.triu(np.ones((4, 4))) edge_list = np.array( np.nonzero(np.triu(edge_matrix) - np.diag(np.diag(edge_matrix)))) qterm_a01 = _edge_operator_aij(edge_list, 0, 1) qterm_a02 = _edge_operator_aij(edge_list, 0, 2) qterm_a03 = _edge_operator_aij(edge_list, 0, 3) qterm_a12 = _edge_operator_aij(edge_list, 1, 2) qterm_a13 = _edge_operator_aij(edge_list, 1, 3) qterm_a23 = _edge_operator_aij(edge_list, 2, 3) ref_qterm_a01 = SparsePauliOp("IIIIIX") ref_qterm_a02 = SparsePauliOp("IIIIXZ") ref_qterm_a03 = SparsePauliOp("IIIXZZ") ref_qterm_a12 = SparsePauliOp("IIXIZZ") ref_qterm_a13 = SparsePauliOp("IXZZIZ") ref_qterm_a23 = SparsePauliOp("XZZZZI") with self.subTest("Test edge operator a01"): self.assertEqual(qterm_a01, ref_qterm_a01) with self.subTest("Test edge operator a02"): self.assertEqual(qterm_a02, ref_qterm_a02) with self.subTest("Test edge operator a03"): self.assertEqual(qterm_a03, ref_qterm_a03) with self.subTest("Test edge operator a12"): self.assertEqual(qterm_a12, ref_qterm_a12) with self.subTest("Test edge operator a13"): self.assertEqual(qterm_a13, ref_qterm_a13) with self.subTest("Test edge operator a23"): self.assertEqual(qterm_a23, ref_qterm_a23)
def init_observable(observable: BaseOperator | PauliSumOp) -> SparsePauliOp: """Initialize observable""" if isinstance(observable, SparsePauliOp): return observable if isinstance(observable, PauliSumOp): if isinstance(observable.coeff, ParameterExpression): raise TypeError( f"observable must have numerical coefficient, not {type(observable.coeff)}" ) return observable.coeff * observable.primitive if isinstance(observable, BaseOperator): return SparsePauliOp.from_operator(observable) return SparsePauliOp(observable)
def test_bksf_edge_op_bi(self): """Test BKSF mapping, edge operator bi""" edge_matrix = np.triu(np.ones((4, 4))) edge_list = np.array( np.nonzero(np.triu(edge_matrix) - np.diag(np.diag(edge_matrix)))) qterm_b0 = _edge_operator_bi(edge_list, 0) qterm_b1 = _edge_operator_bi(edge_list, 1) qterm_b2 = _edge_operator_bi(edge_list, 2) qterm_b3 = _edge_operator_bi(edge_list, 3) ref_qterm_b0 = SparsePauliOp("IIIZZZ") ref_qterm_b1 = SparsePauliOp("IZZIIZ") ref_qterm_b2 = SparsePauliOp("ZIZIZI") ref_qterm_b3 = SparsePauliOp("ZZIZII") with self.subTest("Test edge operator b0"): self.assertEqual(qterm_b0, ref_qterm_b0) with self.subTest("Test edge operator b1"): self.assertEqual(qterm_b1, ref_qterm_b1) with self.subTest("Test edge operator b2"): self.assertEqual(qterm_b2, ref_qterm_b2) with self.subTest("Test edge operator b3"): self.assertEqual(qterm_b3, ref_qterm_b3)
def __init__(self, operator, label="expectation_value_variance", unnormalized=False, pershot=False, conditional=False): r"""Instruction to save the expectation value and variance of a Hermitian operator. The expectation value of a Hermitian operator :math:`H` for a simulator in quantum state :math`\rho`is given by :math:`\langle H\rangle = \mbox{Tr}[H.\rho]`. The variance is given by :math:`\sigma^2 = \langle H^2 \rangle - \langle H \rangle>^2`. Args: operator (Pauli or SparsePauliOp or Operator): a Hermitian operator. label (str): the key for retrieving saved data from results. unnormalized (bool): If True return save the unnormalized accumulated or conditional accumulated expectation value over all shot [Default: False]. pershot (bool): if True save a list of expectation values for each shot of the simulation rather than the average over all shots [Default: False]. conditional (bool): if True save the average or pershot data conditional on the current classical register values [Default: False]. Raises: ExtensionError: if the input operator is invalid or not Hermitian. .. note:: This instruction can be directly appended to a circuit using the :func:`save_expectation_value` circuit method. """ # Convert O to SparsePauliOp representation if isinstance(operator, Pauli): operator = SparsePauliOp(operator) elif not isinstance(operator, SparsePauliOp): operator = SparsePauliOp.from_operator(Operator(operator)) if not allclose(operator.coeffs.imag, 0): raise ExtensionError("Input operator is not Hermitian.") params = _expval_params(operator, variance=True) super().__init__('save_expval_var', operator.num_qubits, label, unnormalized=unnormalized, pershot=pershot, conditional=conditional, params=params)
def tensor(self, other: OperatorBase) -> OperatorBase: # Both Paulis if isinstance(other, PauliOp): return PauliOp(self.primitive.tensor(other.primitive), coeff=self.coeff * other.coeff) # pylint: disable=cyclic-import from .pauli_sum_op import PauliSumOp if isinstance(other, PauliSumOp): new_primitive = SparsePauliOp(self.primitive).tensor(other.primitive) return PauliSumOp(new_primitive, coeff=self.coeff * other.coeff) from .circuit_op import CircuitOp if isinstance(other, CircuitOp): return self.to_circuit_op().tensor(other) return TensoredOp([self, other])
def add(self, other: OperatorBase) -> OperatorBase: if not self.num_qubits == other.num_qubits: raise ValueError( f"Sum of operators with different numbers of qubits, {self.num_qubits} and " f"{other.num_qubits}, is not well defined" ) if isinstance(other, PauliSumOp): return PauliSumOp(self.coeff * self.primitive + other.coeff * other.primitive, coeff=1) if isinstance(other, PauliOp): return PauliSumOp( self.coeff * self.primitive + other.coeff * SparsePauliOp(other.primitive) ) return SummedOp([self, other])
def equals(self, other: OperatorBase) -> bool: self_reduced, other_reduced = self.reduce(), other.reduce() if isinstance(other_reduced, PauliOp): other_reduced = PauliSumOp( SparsePauliOp(other_reduced.primitive, coeffs=[other_reduced.coeff])) if not isinstance(other_reduced, PauliSumOp): return False if isinstance(self_reduced.coeff, ParameterExpression) or isinstance( other_reduced.coeff, ParameterExpression): return (self_reduced.coeff == other_reduced.coeff and self_reduced.primitive == other_reduced.primitive) return (len(self_reduced) == len(other_reduced) and self_reduced.primitive == other_reduced.primitive)
def _fix_qubits( operator: Union[int, PauliSumOp, PauliOp, OperatorBase], has_side_chain_second_bead: bool = False, ) -> Union[int, PauliSumOp, PauliOp, OperatorBase]: """ Assigns predefined values for turns qubits on positions 0, 1, 2, 3, 5 in the main chain without the loss of generality (see the paper https://arxiv.org/pdf/1908.02163.pdf). Qubits on these position are considered fixed and not subject to optimization. Args: operator: an operator whose qubits shall be fixed. Returns: An operator with relevant qubits changed to fixed values. """ # operator might be 0 (int) because it is initialized as operator = 0; then we should not # attempt fixing qubits if (not isinstance(operator, PauliOp) and not isinstance(operator, PauliSumOp) and not isinstance(operator, OperatorBase)): return operator operator = operator.reduce() new_tables = [] new_coeffs = [] if isinstance(operator, PauliOp): table_z = np.copy(operator.primitive.z) table_x = np.copy(operator.primitive.x) _preset_binary_vals(table_z, has_side_chain_second_bead) return PauliOp(Pauli((table_z, table_x))) for hamiltonian in operator: table_z = np.copy(hamiltonian.primitive.paulis.z[0]) table_x = np.copy(hamiltonian.primitive.paulis.x[0]) coeffs = _calc_updated_coeffs(hamiltonian, table_z, has_side_chain_second_bead) _preset_binary_vals(table_z, has_side_chain_second_bead) new_table = np.concatenate((table_x, table_z), axis=0) new_tables.append(new_table) new_coeffs.append(coeffs) new_pauli_table = PauliTable(data=new_tables) operator_updated = PauliSumOp( SparsePauliOp(data=new_pauli_table, coeffs=new_coeffs)) operator_updated = operator_updated.reduce() return operator_updated