`cirq.approx_eq` is inconsistent for sets
Description of the issue
cirq.approx_eq(val1, val2) delegates to _approx_eq_iterables if the two values are iterable, which in turn compares the elements of each value in order. This is a problem if the values are sets, which are not guaranteed to be iterated in a fixed order
a downstream effect of this is that cirq.approx_eq is inconsistent for operations with interchangeable qubits, due to the comparison of the frozensets returned by GateOperation._group_interchangeable_qubits()
How to reproduce the issue
unfortunately this is difficult to reproduce consistently because the iteration order of sets is inconsistent. But some variation of the following will probably fail:
for n in range(10, 20):
assert cirq.approx_eq(frozenset(cirq.LineQubit.range(n)), frozenset({*cirq.LineQubit.range(n)}))
Cirq version
1.4.0.dev20231205195308
csynkque discussion - options
(a) sort when comparing two sets (b) raise exception on comparison of two unordered sequences
Needs investigation what are impacts of these choices on the rest of cirq functionality; would there be backwards incompatible change?
(b) raise exception on comparison of two unordered sequences
wouldn't this break approximate comparison of operations constructed from InterchangeableQubitGates (because their _value_equality_approximate_values_ contains a frozenset)?
the downstream effect of this issue (which is perhaps more important than the example i posted) is that cirq.approx_eq is also inconsistent for these operations, e.g. when i run the following:
for i in range(10):
op1 = cirq.CZ(cirq.q(i), cirq.q(10))
op2 = cirq.CZ(cirq.q(10), cirq.q(i))
if cirq.approx_eq(op1, op2):
print(f"{op1} == {op2}")
else:
print(f"{op1} != {op2}")
i get
CZ(q(0), q(10)) == CZ(q(10), q(0))
CZ(q(1), q(10)) == CZ(q(10), q(1))
CZ(q(2), q(10)) == CZ(q(10), q(2))
CZ(q(3), q(10)) == CZ(q(10), q(3))
CZ(q(4), q(10)) != CZ(q(10), q(4))
CZ(q(5), q(10)) == CZ(q(10), q(5))
CZ(q(6), q(10)) == CZ(q(10), q(6))
CZ(q(7), q(10)) != CZ(q(10), q(7))
CZ(q(8), q(10)) == CZ(q(10), q(8))
CZ(q(9), q(10)) == CZ(q(10), q(9))
it does seem like sorting makes sense though, or even just falling back on exact (==) equality instead of _approx_eq_iterables when comparing unordered sequences?
I'm interested in looking into this issue. Will start getting more context on what's wrong.