Repository: Qiskit/qiskit

Issue:
PauliExpectation is inconsistent for non-hermitian operators
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Information

- **Qiskit Terra version**: main @ 2fd1531bd16c415cbb8b10dcf839ddec101de57a
- **Python version**: 3.7.9
- **Operating system**: macOS BigSur

### What is the current behavior?

As found by @Zoufalc, if the operator is an expectation value is non-hermitian, evaluating it with `PauliExpectation` yields different results (namely the conjugated result) than all other expectation converters.

### Steps to reproduce the problem

```python
from qiskit.providers.aer import QasmSimulator
from qiskit.opflow import *

exp = ~StateFn(1j * Z) @ One

result = exp.eval()
print('Plain eval:', result)  # 1j

result = PauliExpectation().convert(exp).eval()
print('PauliExpectation:', result)  # -1j

result = MatrixExpectation().convert(exp).eval()
print('MatrixExpectation:', result)  # 1j

sampler = CircuitSampler(QasmSimulator())
result = sampler.convert(AerPauliExpectation().convert(exp)).eval()
print('AerPauliExpectation:', result)  # 1j
```

### What is the expected behavior?

`PauliExpectation` gives the same result as the others.

### Suggested solutions

I assume that we need to remove an `adjoint()` call somewhere in `PauliExpectation`.

PauliExpectation is inconsistent for non-hermitian operators
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Information

- **Qiskit Terra version**: main @ 2fd1531bd16c415cbb8b10dcf839ddec101de57a
- **Python version**: 3.7.9
- **Operating system**: macOS BigSur

### What is the current behavior?

As found by @Zoufalc, if the operator is an expectation value is non-hermitian, evaluating it with `PauliExpectation` yields different results (namely the conjugated result) than all other expectation converters.

### Steps to reproduce the problem

```python
from qiskit.providers.aer import QasmSimulator
from qiskit.opflow import *

exp = ~StateFn(1j * Z) @ One

result = exp.eval()
print('Plain eval:', result)  # 1j

result = PauliExpectation().convert(exp).eval()
print('PauliExpectation:', result)  # -1j

result = MatrixExpectation().convert(exp).eval()
print('MatrixExpectation:', result)  # 1j

sampler = CircuitSampler(QasmSimulator())
result = sampler.convert(AerPauliExpectation().convert(exp)).eval()
print('AerPauliExpectation:', result)  # 1j
```

### What is the expected behavior?

`PauliExpectation` gives the same result as the others.

### Suggested solutions

I assume that we need to remove an `adjoint()` call somewhere in `PauliExpectation`.



Discussion:

Resolving patch:
diff --git a/qiskit/opflow/converters/pauli_basis_change.py b/qiskit/opflow/converters/pauli_basis_change.py
--- a/qiskit/opflow/converters/pauli_basis_change.py
+++ b/qiskit/opflow/converters/pauli_basis_change.py
@@ -230,7 +230,7 @@ def measurement_replacement_fn(
             The ``~StateFn @ CircuitOp`` composition equivalent to a measurement by the original
             ``PauliOp``.
         """
-        return PauliBasisChange.statefn_replacement_fn(cob_instr_op, dest_pauli_op).adjoint()
+        return ComposedOp([StateFn(dest_pauli_op, is_measurement=True), cob_instr_op])
 
     @staticmethod
     def statefn_replacement_fn(cob_instr_op: PrimitiveOp,


Test patch:
diff --git a/test/python/opflow/test_aer_pauli_expectation.py b/test/python/opflow/test_aer_pauli_expectation.py
--- a/test/python/opflow/test_aer_pauli_expectation.py
+++ b/test/python/opflow/test_aer_pauli_expectation.py
@@ -222,6 +222,11 @@ def test_pauli_expectation_param_qobj(self):
         np.testing.assert_array_almost_equal(val1, val3, decimal=2)
         np.testing.assert_array_almost_equal([val1] * 2, val4, decimal=2)
 
+    def test_pauli_expectation_non_hermite_op(self):
+        """ Test PauliExpectation for non hermitian operator """
+        exp = ~StateFn(1j * Z) @ One
+        self.assertEqual(self.sampler.convert(self.expect.convert(exp)).eval(), 1j)
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/test/python/opflow/test_gradients.py b/test/python/opflow/test_gradients.py
--- a/test/python/opflow/test_gradients.py
+++ b/test/python/opflow/test_gradients.py
@@ -692,7 +692,7 @@ def test_operator_coefficient_gradient(self, method):
         coeff_0 = Parameter('c_0')
         coeff_1 = Parameter('c_1')
         ham = coeff_0 * X + coeff_1 * Z
-        op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0)
+        op = StateFn(ham, is_measurement=True) @ CircuitStateFn(primitive=qc, coeff=1.0)
         gradient_coeffs = [coeff_0, coeff_1]
         coeff_grad = Gradient(grad_method=method).convert(op, gradient_coeffs)
         values_dict = [{coeff_0: 0.5, coeff_1: -1, a: np.pi / 4, b: np.pi},
@@ -727,7 +727,7 @@ def test_operator_coefficient_hessian(self, method):
         coeff_0 = Parameter('c_0')
         coeff_1 = Parameter('c_1')
         ham = coeff_0 * coeff_0 * X + coeff_1 * coeff_0 * Z
-        op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.)
+        op = StateFn(ham, is_measurement=True) @ CircuitStateFn(primitive=qc, coeff=1.)
         gradient_coeffs = [(coeff_0, coeff_0), (coeff_0, coeff_1), (coeff_1, coeff_1)]
         coeff_grad = Hessian(hess_method=method).convert(op, gradient_coeffs)
         values_dict = [{coeff_0: 0.5, coeff_1: -1, a: np.pi / 4, b: np.pi},
diff --git a/test/python/opflow/test_matrix_expectation.py b/test/python/opflow/test_matrix_expectation.py
--- a/test/python/opflow/test_matrix_expectation.py
+++ b/test/python/opflow/test_matrix_expectation.py
@@ -144,6 +144,11 @@ def test_multi_representation_ops(self):
                                              [1, .5**.5, (1 + .5**.5), 1],
                                              decimal=1)
 
+    def test_matrix_expectation_non_hermite_op(self):
+        """ Test MatrixExpectation for non hermitian operator """
+        exp = ~StateFn(1j * Z) @ One
+        self.assertEqual(self.expect.convert(exp).eval(), 1j)
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/test/python/opflow/test_pauli_expectation.py b/test/python/opflow/test_pauli_expectation.py
--- a/test/python/opflow/test_pauli_expectation.py
+++ b/test/python/opflow/test_pauli_expectation.py
@@ -233,6 +233,11 @@ def test_multi_representation_ops(self):
                                              [1, .5**.5, (1 + .5**.5), 1],
                                              decimal=1)
 
+    def test_pauli_expectation_non_hermite_op(self):
+        """ Test PauliExpectation for non hermitian operator """
+        exp = ~StateFn(1j * Z) @ One
+        self.assertEqual(self.expect.convert(exp).eval(), 1j)
+
 
 if __name__ == '__main__':
     unittest.main()