diff --git a/qiskit/aqua/operators/converters/circuit_sampler.py b/qiskit/aqua/operators/converters/circuit_sampler.py index 04da7e7256..3216fe909c 100644 --- a/qiskit/aqua/operators/converters/circuit_sampler.py +++ b/qiskit/aqua/operators/converters/circuit_sampler.py @@ -47,6 +47,7 @@ class CircuitSampler(ConverterBase): the same circuit efficiently. If you are converting multiple different Operators, you are better off using a different CircuitSampler for each Operator to avoid cache thrashing. """ + def __init__(self, backend: Union[BaseBackend, QuantumInstance] = None, statevector: Optional[bool] = None, @@ -302,7 +303,7 @@ def sample_circuits(self, result_sfn = StateFn(op_c.coeff * results.get_statevector(circ_index)) else: shots = self.quantum_instance._run_config.shots - result_sfn = StateFn({b: (v * op_c.coeff / shots) ** .5 + result_sfn = StateFn({b: (v / shots) ** 0.5 * op_c.coeff for (b, v) in results.get_counts(circ_index).items()}) if self._attach_results: result_sfn.execution_results = circ_results diff --git a/qiskit/aqua/operators/list_ops/composed_op.py b/qiskit/aqua/operators/list_ops/composed_op.py index 8f153d83cf..170c1ae284 100644 --- a/qiskit/aqua/operators/list_ops/composed_op.py +++ b/qiskit/aqua/operators/list_ops/composed_op.py @@ -123,9 +123,9 @@ def reduce(self) -> OperatorBase: def distribute_compose(l, r): if isinstance(l, ListOp) and l.distributive: # Either ListOp or SummedOp, returns correct type - return l.__class__([distribute_compose(l_op, r) for l_op in l.oplist]) + return l.__class__([distribute_compose(l_op * l.coeff, r) for l_op in l.oplist]) if isinstance(r, ListOp) and r.distributive: - return r.__class__([distribute_compose(l, r_op) for r_op in r.oplist]) + return r.__class__([distribute_compose(l, r_op * r.coeff) for r_op in r.oplist]) else: return l.compose(r) diff --git a/qiskit/aqua/operators/state_fns/operator_state_fn.py b/qiskit/aqua/operators/state_fns/operator_state_fn.py index d4ce67735e..507f733966 100644 --- a/qiskit/aqua/operators/state_fns/operator_state_fn.py +++ b/qiskit/aqua/operators/state_fns/operator_state_fn.py @@ -190,7 +190,8 @@ def eval(self, front = StateFn(front) if isinstance(self.primitive, ListOp) and self.primitive.distributive: - evals = [OperatorStateFn(op, coeff=self.coeff, is_measurement=self.is_measurement).eval( + coeff = self.coeff * self.primitive.coeff + evals = [OperatorStateFn(op, coeff=coeff, is_measurement=self.is_measurement).eval( front) for op in self.primitive.oplist] return self.primitive.combo_fn(evals) diff --git a/releasenotes/notes/operator-coeff-eval-95efb26e95f6421a.yaml b/releasenotes/notes/operator-coeff-eval-95efb26e95f6421a.yaml new file mode 100644 index 0000000000..bee89af198 --- /dev/null +++ b/releasenotes/notes/operator-coeff-eval-95efb26e95f6421a.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + The evaluation of some operator expressions, such as of ``SummedOp``s + and evaluations with the ``CircuitSampler`` did not treat coefficients + correctly or ignored them completely. E.g. evaluating + ``~StateFn(0 * (I + Z)) @ Plus`` did not yield 0 or the normalization + of ``~StateFn(I) @ ((Plus + Minus) / sqrt(2))`` missed a factor + of ``sqrt(2)``. This has been fixed. diff --git a/test/aqua/operators/test_pauli_expectation.py b/test/aqua/operators/test_pauli_expectation.py index 78cda1334e..94c9bf2458 100644 --- a/test/aqua/operators/test_pauli_expectation.py +++ b/test/aqua/operators/test_pauli_expectation.py @@ -89,7 +89,7 @@ def test_pauli_expect_op_vector(self): # !!NOTE!!: Depolarizing channel (Sampling) means interference # does not happen between circuits in sum, so expectation does # not equal expectation for Zero!! - np.testing.assert_array_almost_equal(sampled_zero_mean.eval(), [0, 0, 0, 2], decimal=1) + np.testing.assert_array_almost_equal(sampled_zero_mean.eval(), [0, 0, 0, 1], decimal=1) for i, op in enumerate(paulis_op.oplist): mat_op = op.to_matrix() diff --git a/test/aqua/operators/test_state_op_meas_evals.py b/test/aqua/operators/test_state_op_meas_evals.py index 529d45a130..bfc509a1a1 100644 --- a/test/aqua/operators/test_state_op_meas_evals.py +++ b/test/aqua/operators/test_state_op_meas_evals.py @@ -16,7 +16,9 @@ import unittest from test.aqua import QiskitAquaTestCase -from qiskit.aqua.operators import StateFn, Zero, One, H, X +import numpy +from qiskit import Aer +from qiskit.aqua.operators import StateFn, Zero, One, H, X, I, Z, Plus, Minus, CircuitSampler # pylint: disable=invalid-name @@ -56,6 +58,25 @@ def test_wf_evals_x(self): self.assertAlmostEqual(wf.adjoint().eval(op.eval(wf_vec)), .25) self.assertAlmostEqual(wf_vec.adjoint().eval(op.eval(wf_vec)), .25) + def test_coefficients_correctly_propagated(self): + """Test that the coefficients in SummedOp and states are correctly used.""" + with self.subTest('zero coeff in SummedOp'): + op = 0 * (I + Z) + state = Plus + self.assertEqual((~StateFn(op) @ state).eval(), 0j) + + backend = Aer.get_backend('qasm_simulator') + op = I + with self.subTest('zero coeff in summed StateFn and CircuitSampler'): + state = 0 * (Plus + Minus) + sampler = CircuitSampler(backend).convert(~StateFn(op) @ state) + self.assertEqual(sampler.eval(), 0j) + + with self.subTest('coeff gets squared in CircuitSampler shot-based readout'): + state = (Plus + Minus) / numpy.sqrt(2) + sampler = CircuitSampler(backend).convert(~StateFn(op) @ state) + self.assertAlmostEqual(sampler.eval(), 1+0j) + if __name__ == '__main__': unittest.main()