Commit 96c2004
[bugfix] Prevent GateOperation.add from superseding Circuit.radd (#7707)
Per issue #7706, the change in #7651 caused `Circuit.__radd__`, which
allows `+` to prepend operations to circuits, e.g. `H(q) + Circuit(...)
== Circuit(H(q), ...)`, to stop working. The root cause is that the
change added a `GateOperation.__add__`, which thus supersedes
`Circuit.__radd__` and changes the behavior of `+` in such cases.
The fix for this is to modify the new `GateOperation.__add__` (and
`GateOperation.__sub__`, for symmetry) so that it only applies in cases
where interpreting the sum as a `PauliString` is reasonable.
To do this, first, I updated `GateOperation.__add__` with a check, `if
not isinstance(other, (Operation, Number)): return NotImplemented`,
since ops and numbers are the only things that can be interpreted as a
`PauliString`. This change prevents `GateOperation.__add__` from
interfering with `Circuit.__radd__`, and constitutes the crux of the
fix.
That change in isolation breaks `GateOperation + PauliSum` because the
latter isn't an `Operation`. The simplest fix for *this* would be to add
`PauliSum` to the new isinstance check, e.g. `isinstance(other,
(Operation, Number, PauliSum))`, However that approach adds `PauliSum`
as a dependency of `GateOperation`, which seems unnatural. So instead, I
added logic in `PauliSum.__add__` to convert ops to `PauliString` first.
That approach doesn't create any new dependencies, and is also more
robust because it works for *all* operations, not just GateOperations.
(Note, an even simpler fix for the entire issue would have been, instead
of allowlisting `(Operation, Number)` in `GateOperation.__add__`, to
denylist `Circuit` instead, e.g. `if isinstance(other, Circuit): return
NotImplemented`. That would have fixed the issue and not required any
special change for `PauliSum`. But similarly, I chose against that route
because of the unnatural dependency it would create, as well as the fact
that that fix wouldn't help if any third-party classes used `radd` on
operations in the same way `Circuit` does.)
Finally, I updated the `Circuit.radd` unit test to include assertions
that would have broken under the old code. The existing test used `X`
gates everywhere, which didn't fail when prepending because of misc
internal logic for Pauli gates. So I parameterized the test on `gate`,
and added a non-Pauli gate `H` to the parameters, which will now fail if
a similar regression is made in the future. I updated some tests in
linear_combinations to test `__sub__` more thoroughly as well, for
symmetry.
Key takeaway: be careful when implementing `__add__` for an existing
class, and limit the scope of `other` to which it can apply, as
otherwise the change can break `__radd__` functionality in unrelated
classes.
Fixes #7706
---------
Co-authored-by: Pavol Juhas <pavol.juhas@gmail.com>1 parent 57f17ce commit 96c2004
File tree
4 files changed
+43
-11
lines changed- cirq-core/cirq
- circuits
- ops
4 files changed
+43
-11
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
356 | 356 | | |
357 | 357 | | |
358 | 358 | | |
359 | | - | |
| 359 | + | |
| 360 | + | |
360 | 361 | | |
361 | 362 | | |
362 | 363 | | |
363 | 364 | | |
364 | | - | |
| 365 | + | |
365 | 366 | | |
366 | | - | |
367 | | - | |
368 | | - | |
369 | | - | |
370 | | - | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
371 | 372 | | |
372 | | - | |
| 373 | + | |
373 | 374 | | |
374 | 375 | | |
375 | 376 | | |
| |||
380 | 381 | | |
381 | 382 | | |
382 | 383 | | |
383 | | - | |
384 | | - | |
385 | | - | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
386 | 387 | | |
387 | 388 | | |
388 | 389 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
| |||
323 | 324 | | |
324 | 325 | | |
325 | 326 | | |
| 327 | + | |
| 328 | + | |
326 | 329 | | |
327 | 330 | | |
328 | 331 | | |
329 | 332 | | |
330 | 333 | | |
331 | 334 | | |
| 335 | + | |
| 336 | + | |
332 | 337 | | |
333 | 338 | | |
334 | 339 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
743 | 743 | | |
744 | 744 | | |
745 | 745 | | |
| 746 | + | |
| 747 | + | |
746 | 748 | | |
747 | 749 | | |
748 | 750 | | |
| |||
767 | 769 | | |
768 | 770 | | |
769 | 771 | | |
| 772 | + | |
| 773 | + | |
770 | 774 | | |
771 | 775 | | |
772 | 776 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
975 | 975 | | |
976 | 976 | | |
977 | 977 | | |
| 978 | + | |
| 979 | + | |
| 980 | + | |
978 | 981 | | |
979 | 982 | | |
980 | 983 | | |
981 | 984 | | |
| 985 | + | |
| 986 | + | |
| 987 | + | |
982 | 988 | | |
983 | 989 | | |
984 | 990 | | |
| |||
1008 | 1014 | | |
1009 | 1015 | | |
1010 | 1016 | | |
| 1017 | + | |
| 1018 | + | |
| 1019 | + | |
| 1020 | + | |
| 1021 | + | |
| 1022 | + | |
| 1023 | + | |
| 1024 | + | |
| 1025 | + | |
| 1026 | + | |
| 1027 | + | |
| 1028 | + | |
| 1029 | + | |
| 1030 | + | |
| 1031 | + | |
| 1032 | + | |
1011 | 1033 | | |
1012 | 1034 | | |
1013 | 1035 | | |
| |||
0 commit comments