Skip to content
This repository was archived by the owner on Dec 7, 2021. It is now read-only.

Commit

Permalink
Correct coefficient propagation in the operators (#1077)
Browse files Browse the repository at this point in the history
* factor in the coeff of ListOp

* fix coeff in composedop reduce

* state coeff must be squared (applied left and right)

* fix norm

* add test

* add reno
  • Loading branch information
Cryoris authored Jun 29, 2020
1 parent 78bcfaa commit bbb083f
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 6 deletions.
3 changes: 2 additions & 1 deletion qiskit/aqua/operators/converters/circuit_sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions qiskit/aqua/operators/list_ops/composed_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
3 changes: 2 additions & 1 deletion qiskit/aqua/operators/state_fns/operator_state_fn.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
9 changes: 9 additions & 0 deletions releasenotes/notes/operator-coeff-eval-95efb26e95f6421a.yaml
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion test/aqua/operators/test_pauli_expectation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
23 changes: 22 additions & 1 deletion test/aqua/operators/test_state_op_meas_evals.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()

0 comments on commit bbb083f

Please sign in to comment.