Skip to content

Commit 227d588

Browse files
SummedOp updates & optimization converters to use Opflow (qiskit-community/qiskit-aqua#1059)
* simplify and reduce, add equals to SummedOp, update tests * directly use new opflow, no need to go via WPO * update comments and docstrings * directly use opflow * don't do equality check in add * directly use opflow * change order in reduce * fix qaoa * add short test on summed op equality * rm prints * use set comparison, rename simplify to collapse_summands * fix expected value, should be sqrt(2), not 2 * cast coeffs to complex * add reno on equals * fix mypy * fix spell * fix lint * dont cast coefficient to complex leads to problems if the coeff is exponentitated and not supposed to be complex * use sum instead of reduce * rm unused import * move __hash__ to primitive op and base on repr * use != over not == * add summed op test for different primitives * check for opbase, not summedop * adress changes from review * explicitly raise an error upon ListOp input * return identity op instead of the int 0 * fix spell * add note that equals is not mathematically sound Co-authored-by: Manoel Marques <[email protected]>
1 parent 1b78f33 commit 227d588

File tree

3 files changed

+66
-13
lines changed

3 files changed

+66
-13
lines changed

qiskit/aqua/operators/list_ops/summed_op.py

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
""" SummedOp Class """
1616

17-
from functools import reduce
1817
from typing import List, Union, cast
1918

2019
import numpy as np
@@ -72,9 +71,6 @@ def add(self, other: OperatorBase) -> OperatorBase:
7271
Returns:
7372
A ``SummedOp`` equivalent to the sum of self and other.
7473
"""
75-
if self == other:
76-
return self.mul(2.0)
77-
7874
self_new_ops = self.oplist if self.coeff == 1 \
7975
else [op.mul(self.coeff) for op in self.oplist]
8076
if isinstance(other, SummedOp):
@@ -84,10 +80,10 @@ def add(self, other: OperatorBase) -> OperatorBase:
8480
other_new_ops = [other]
8581
return SummedOp(self_new_ops + other_new_ops)
8682

87-
def simplify(self) -> 'SummedOp':
83+
def collapse_summands(self) -> 'SummedOp':
8884
"""Return Operator by simplifying duplicate operators.
8985
90-
E.g., ``SummedOp([2 * X ^ Y, X ^ Y]).simplify() -> SummedOp([3 * X ^ Y])``.
86+
E.g., ``SummedOp([2 * X ^ Y, X ^ Y]).collapse_summands() -> SummedOp([3 * X ^ Y])``.
9187
9288
Returns:
9389
A simplified ``SummedOp`` equivalent to self.
@@ -113,11 +109,23 @@ def simplify(self) -> 'SummedOp':
113109
coeffs.append(self.coeff)
114110
return SummedOp([op * coeff for op, coeff in zip(oplist, coeffs)]) # type: ignore
115111

116-
# Try collapsing list or trees of Sums.
117112
# TODO be smarter about the fact that any two ops in oplist could be evaluated for sum.
118113
def reduce(self) -> OperatorBase:
119-
reduced_ops = [op.reduce() for op in self.oplist]
120-
reduced_ops = reduce(lambda x, y: x.add(y), reduced_ops) * self.coeff
114+
"""Try collapsing list or trees of sums.
115+
116+
Tries to sum up duplicate operators and reduces the operators
117+
in the sum.
118+
119+
Returns:
120+
A collapsed version of self, if possible.
121+
"""
122+
# reduce constituents
123+
reduced_ops = sum(op.reduce() for op in self.oplist) * self.coeff
124+
125+
# group duplicate operators
126+
if isinstance(reduced_ops, SummedOp):
127+
reduced_ops = reduced_ops.collapse_summands()
128+
121129
if isinstance(reduced_ops, SummedOp) and len(reduced_ops.oplist) == 1:
122130
return reduced_ops.oplist[0]
123131
else:
@@ -142,3 +150,49 @@ def to_legacy_op(self, massive: bool = False) -> LegacyBaseOperator:
142150
coeff = cast(float, self.coeff)
143151

144152
return self.combo_fn(legacy_ops) * coeff
153+
154+
def equals(self, other: OperatorBase) -> bool:
155+
"""Check if other is equal to self.
156+
157+
Note:
158+
This is not a mathematical check for equality.
159+
If ``self`` and ``other`` implement the same operation but differ
160+
in the representation (e.g. different type of summands)
161+
``equals`` will evaluate to ``False``.
162+
163+
Args:
164+
other: The other operator to check for equality.
165+
166+
Returns:
167+
True, if other and self are equal, otherwise False.
168+
169+
Examples:
170+
>>> from qiskit.aqua.operators import X, Z
171+
>>> 2 * X == X + X
172+
True
173+
>>> X + Z == Z + X
174+
True
175+
"""
176+
self_reduced, other_reduced = self.reduce(), other.reduce()
177+
if not isinstance(other_reduced, type(self_reduced)):
178+
return False
179+
180+
# check if reduced op is still a SummedOp
181+
if not isinstance(self_reduced, SummedOp):
182+
return self_reduced == other_reduced
183+
184+
self_reduced = cast(SummedOp, self_reduced)
185+
other_reduced = cast(SummedOp, other_reduced)
186+
if len(self_reduced.oplist) != len(other_reduced.oplist):
187+
return False
188+
189+
# absorb coeffs into the operators
190+
if self_reduced.coeff != 1:
191+
self_reduced = SummedOp(
192+
[op * self_reduced.coeff for op in self_reduced.oplist]) # type: ignore
193+
if other_reduced.coeff != 1:
194+
other_reduced = SummedOp(
195+
[op * other_reduced.coeff for op in other_reduced.oplist]) # type: ignore
196+
197+
# compare independent of order
198+
return set(self_reduced) == set(other_reduced)

qiskit/aqua/operators/primitive_ops/pauli_op.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,10 +231,6 @@ def exp_i(self) -> OperatorBase:
231231
from ..evolutions.evolved_op import EvolvedOp
232232
return EvolvedOp(self)
233233

234-
def __hash__(self) -> int:
235-
# Need this to be able to easily construct AbelianGraphs
236-
return hash(str(self))
237-
238234
def commutes(self, other_op: OperatorBase) -> bool:
239235
""" Returns whether self commutes with other_op.
240236

qiskit/aqua/operators/primitive_ops/primitive_op.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,9 @@ def log_i(self, massive: bool = False) -> OperatorBase:
198198
def __str__(self) -> str:
199199
raise NotImplementedError
200200

201+
def __hash__(self) -> int:
202+
return hash(repr(self))
203+
201204
def __repr__(self) -> str:
202205
return "{}({}, coeff={})".format(type(self).__name__, repr(self.primitive), self.coeff)
203206

0 commit comments

Comments
 (0)