diff --git a/qiskit/opflow/expectations/aer_pauli_expectation.py b/qiskit/opflow/expectations/aer_pauli_expectation.py index 9cfec58ad3bb..33790531592a 100644 --- a/qiskit/opflow/expectations/aer_pauli_expectation.py +++ b/qiskit/opflow/expectations/aer_pauli_expectation.py @@ -43,13 +43,36 @@ def convert(self, operator: OperatorBase) -> OperatorBase: AerSnapshot-based expectation circuits. Args: - operator: The operator to convert. + operator: The operator to convert. If it contains non-hermitian terms, the + operator is decomposed into hermitian and anti-hermitian parts. Returns: The converted operator. """ + if isinstance(operator, OperatorStateFn) and operator.is_measurement: - return self._replace_pauli_sums(operator.primitive) * operator.coeff + if isinstance(operator.primitive, ListOp): + is_herm = all((op.is_hermitian() for op in operator.primitive.oplist)) + else: + is_herm = operator.primitive.is_hermitian() + + if not is_herm: + pauli_sum_re = ( + self._replace_pauli_sums( + 1 / 2 * (operator.primitive + operator.primitive.adjoint()).reduce() + ) + * operator.coeff + ) + pauli_sum_im = ( + self._replace_pauli_sums( + 1 / 2j * (operator.primitive - operator.primitive.adjoint()).reduce() + ) + * operator.coeff + ) + pauli_sum = (pauli_sum_re + 1j * pauli_sum_im).reduce() + else: + pauli_sum = self._replace_pauli_sums(operator.primitive) * operator.coeff + return pauli_sum elif isinstance(operator, ListOp): return operator.traverse(self.convert) else: diff --git a/releasenotes/notes/add-support-non-hermitian-op-aerpauliexpectation-653d8e16de4eca07.yaml b/releasenotes/notes/add-support-non-hermitian-op-aerpauliexpectation-653d8e16de4eca07.yaml new file mode 100644 index 000000000000..c56d0f62312d --- /dev/null +++ b/releasenotes/notes/add-support-non-hermitian-op-aerpauliexpectation-653d8e16de4eca07.yaml @@ -0,0 +1,5 @@ +features: + - | + Added the support of non-hermitian operators for :class:`~qiskit.opflow.AerPauliExpectation`. + This for instance enables using Aer's fast snapshot expectation value in algorithms such as + :class:`~qiskit.algorithms.QEOM`. \ No newline at end of file diff --git a/test/python/opflow/test_aer_pauli_expectation.py b/test/python/opflow/test_aer_pauli_expectation.py index 7363372b1ce1..34834289ca7c 100644 --- a/test/python/opflow/test_aer_pauli_expectation.py +++ b/test/python/opflow/test_aer_pauli_expectation.py @@ -38,6 +38,7 @@ Y, Z, Zero, + MatrixOp, ) from qiskit.utils import QuantumInstance @@ -120,6 +121,29 @@ def test_pauli_expect_state_vector(self): np.testing.assert_array_almost_equal(sampled.eval(), [0, 0, 1, -1], decimal=1) + def test_pauli_expect_non_hermitian_matrixop(self): + """pauli expect state vector with non hermitian operator test""" + states_op = ListOp([One, Zero, Plus, Minus]) + + op_mat = np.array([[0, 1], [2, 3]]) + op = MatrixOp(op_mat) + + converted_meas = self.expect.convert(StateFn(op, is_measurement=True) @ states_op) + sampled = self.sampler.convert(converted_meas) + + np.testing.assert_array_almost_equal(sampled.eval(), [3, 0, 3, 0], decimal=1) + + def test_pauli_expect_non_hermitian_pauliop(self): + """pauli expect state vector with non hermitian operator test""" + states_op = ListOp([One, Zero, Plus, Minus]) + + op = 1j * X + + converted_meas = self.expect.convert(StateFn(op, is_measurement=True) @ states_op) + sampled = self.sampler.convert(converted_meas) + + np.testing.assert_array_almost_equal(sampled.eval(), [0, 0, 1j, -1j], decimal=1) + def test_pauli_expect_op_vector_state_vector(self): """pauli expect op vector state vector test""" paulis_op = ListOp([X, Y, Z, I])