-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Resolve inconsistencies in using controlled gates & controlled operations #4167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Resolve inconsistencies in using controlled gates & controlled operations #4167
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two comments for discussion before we move forward:
- This is technically a breaking change as user code might depend on the type of the
controlled_by
method being aControlledGate
. So we should make it very explicit in our release notes and label this PR as a breaking change. - Is there really no value at all in subsequent layers of
ControlledGate
s? It feels like a very generic structure that maybe some decomposition code could leverage. But with jumping directly to "specialized" implementation we lose this structure. Maybe could have both? I.e. from a (multi-)controlled gate we could have a methodreduce_type
,simplify_type
orspecialize_type
that could return the equivalent type in the hierarchy as needed based on the underlying gate if there is one...I.e. we would do the delegation to the subgate only on demand - this pattern would be similar tosimplify
Sympy expressions.
Thanks for the feedback. I found some more cases and have opened #4172 to discuss the various issues related to controlled gates & operations. |
053138f
to
11ece2c
Compare
@balopat This is ready for review. I'm open to suggestions for a better breaking change description in the PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like that the consistency tests should follow the pattern in cirq.testing.assert_implements_consistent_protocols
-- i.e. I'd create an assertion in cirq.testing that takes a gate, and as sequence of pows, and then do the two consistency tests on the gate and the gate powers. Then I'd use this assert method for all of our gates to achieve full coverage across all our gates.
Re: breaking change note - looks good, but I would specifically mention in the breaking changes the consequent change in QFT and quirk diagrams controlled operations. Some people depend on diagrams for their tests... |
@balopat I've modified the PR description and added Also, do you want me to add 100% coverage across all Cirq gates for |
@balopat Ping, this is ready for re-review. PTAL. |
@MichaelBroughton Can you please take a look at this? |
c340eac
to
7c83f33
Compare
Also added a fix for #4515 in this PR to avoid breaking the diagrams twice. See #4529 (comment) for justification. This PR is again ready for re-review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ready to merge on this. @maffoo or @smitsanghavi , any final thoughts ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
…ers to support other operation types. (#4459) Proliferation of `isinstance(op, GateOperation)` checks results in many inconsistencies due to different available operation types like `ControlledOperations` and `TaggedOperations`. This PR fixes #4152 and is a first step towards fixing #3556 Note that `TaggedOperations` which were earlier ignored by the optimizers would now be considered, and hence this is potentially a breaking change if people were implicitly relying on TaggedOperations not getting compiled by the optimizers. Since the optimizer doesn't document / test this behavior, I consider it to be a bug rather than a feature and an explicit `NoCompile` tag should be implemented as part of #4253 This PR is blocked on submitting #4167 (tests will stop failing once the PR is submitted and this rebased). Update: This is now ready for review.
…lib#4462) `CX` and `CCX` gates currently print the exponent on the bottom most qubit in the circuit diagram. This PR modifies this behavior to always draw the exponent on the target qubit, which is also the expected behavior for other `ControlledOperations`. This came up in discussions on quantumlib#4167 (comment) Note that behavior of `CZ` and `CCZ` is left unchanged, i.e. draws exponent on the bottom most qubit, since these gates are symmetrical across all applied qubits. BREAKING CHANGE: Tests depending upon diagrams of `CX` and `CCX` can now fail.
…ions (quantumlib#4167) This PR tries to resolve the inconsistencies mentioned in quantumlib#4172. Specifically, - Equality b/w controlled `TOFFOLI`s with different qubit ordering on controls, i.e. `TOFFOLI(a,b,c).controlled_by(d) == TOFFOLI(d,b,c).controlled_by(a)` (and other 3Q controlled gates like CCZ, CSWAP). To achieve this, we override the `controlled` method on `CCX`, `CCZ` etc. to return a `ControlledGate` with `sub_gate = X` in case of `CCX` s.t. `TOFFOLI(a,b,c).controlled_by(d) == CCCX(a, b, d, c) == TOFFOLI(d,b,c).controlled_by(a)` instead of `CTOFFOLI` - `gate_operation.controlled_by` now forwards the request to `gate.controlled` to first create a controlled gate and then apply it on qubits to create a `ControlledOperation`. This solves the original use case of adding specialized controls, requested in quantumlib#2142, i.e. `cirq.Z(q0).controlled_by(q1) == cirq.CZ(q1, q0)`. - Fixes quantumlib#4515 Note, this is a breaking change because - `gate_operation.controlled_by()` can now return a `cirq.GateOperation` instead of `cirq.ControlledOperation` in cases where the underlying gates have specialized `gate.controlled()` implementations. - This also leads to a change in diagram of the controlled gates with specialized controlled implementations. For eg: Controlled S gate is now plotted as `CZPowGate` (`@ --- @ ** 0.5`) instead of `ControlledOperation` with Z ** 0.5 as subgate(`@ ---- S`) - `op.controlled_by` for 3Q gates like `CCX`, `CCZ`, `CSWAP` will now return `ControlledOperation` with `sub_operation = <underlying non-controlled gate>`. Eg: `CCCX` (i.e. `sub_gate = X`) instead of `CTOFFOLI` (i.e. `sub_gate = TOFFOLI`) etc. - Diagrams for `ControlledOperations` will now always have the exponent drawn on the target qubit (in case of multi qubit `sub_operation`, the exponent will always be on the first qubit if not the underlying gate does not explicitly specify a target index).
…ers to support other operation types. (quantumlib#4459) Proliferation of `isinstance(op, GateOperation)` checks results in many inconsistencies due to different available operation types like `ControlledOperations` and `TaggedOperations`. This PR fixes quantumlib#4152 and is a first step towards fixing quantumlib#3556 Note that `TaggedOperations` which were earlier ignored by the optimizers would now be considered, and hence this is potentially a breaking change if people were implicitly relying on TaggedOperations not getting compiled by the optimizers. Since the optimizer doesn't document / test this behavior, I consider it to be a bug rather than a feature and an explicit `NoCompile` tag should be implemented as part of quantumlib#4253 This PR is blocked on submitting quantumlib#4167 (tests will stop failing once the PR is submitted and this rebased). Update: This is now ready for review.
…lib#4462) `CX` and `CCX` gates currently print the exponent on the bottom most qubit in the circuit diagram. This PR modifies this behavior to always draw the exponent on the target qubit, which is also the expected behavior for other `ControlledOperations`. This came up in discussions on quantumlib#4167 (comment) Note that behavior of `CZ` and `CCZ` is left unchanged, i.e. draws exponent on the bottom most qubit, since these gates are symmetrical across all applied qubits. BREAKING CHANGE: Tests depending upon diagrams of `CX` and `CCX` can now fail.
…ions (quantumlib#4167) This PR tries to resolve the inconsistencies mentioned in quantumlib#4172. Specifically, - Equality b/w controlled `TOFFOLI`s with different qubit ordering on controls, i.e. `TOFFOLI(a,b,c).controlled_by(d) == TOFFOLI(d,b,c).controlled_by(a)` (and other 3Q controlled gates like CCZ, CSWAP). To achieve this, we override the `controlled` method on `CCX`, `CCZ` etc. to return a `ControlledGate` with `sub_gate = X` in case of `CCX` s.t. `TOFFOLI(a,b,c).controlled_by(d) == CCCX(a, b, d, c) == TOFFOLI(d,b,c).controlled_by(a)` instead of `CTOFFOLI` - `gate_operation.controlled_by` now forwards the request to `gate.controlled` to first create a controlled gate and then apply it on qubits to create a `ControlledOperation`. This solves the original use case of adding specialized controls, requested in quantumlib#2142, i.e. `cirq.Z(q0).controlled_by(q1) == cirq.CZ(q1, q0)`. - Fixes quantumlib#4515 Note, this is a breaking change because - `gate_operation.controlled_by()` can now return a `cirq.GateOperation` instead of `cirq.ControlledOperation` in cases where the underlying gates have specialized `gate.controlled()` implementations. - This also leads to a change in diagram of the controlled gates with specialized controlled implementations. For eg: Controlled S gate is now plotted as `CZPowGate` (`@ --- @ ** 0.5`) instead of `ControlledOperation` with Z ** 0.5 as subgate(`@ ---- S`) - `op.controlled_by` for 3Q gates like `CCX`, `CCZ`, `CSWAP` will now return `ControlledOperation` with `sub_operation = <underlying non-controlled gate>`. Eg: `CCCX` (i.e. `sub_gate = X`) instead of `CTOFFOLI` (i.e. `sub_gate = TOFFOLI`) etc. - Diagrams for `ControlledOperations` will now always have the exponent drawn on the target qubit (in case of multi qubit `sub_operation`, the exponent will always be on the first qubit if not the underlying gate does not explicitly specify a target index).
This PR tries to resolve the inconsistencies mentioned in #4172. Specifically,
TOFFOLI
s with different qubit ordering on controls, i.e.TOFFOLI(a,b,c).controlled_by(d) == TOFFOLI(d,b,c).controlled_by(a)
(and other 3Q controlled gates like CCZ, CSWAP). To achieve this, we override thecontrolled
method onCCX
,CCZ
etc. to return aControlledGate
withsub_gate = X
in case ofCCX
s.t.TOFFOLI(a,b,c).controlled_by(d) == CCCX(a, b, d, c) == TOFFOLI(d,b,c).controlled_by(a)
instead ofCTOFFOLI
gate_operation.controlled_by
now forwards the request togate.controlled
to first create a controlled gate and then apply it on qubits to create aControlledOperation
. This solves the original use case of adding specialized controls, requested in On PartialGate and cirq.Z(a).controlled_by(b) returning a CZ #2142, i.e.cirq.Z(q0).controlled_by(q1) == cirq.CZ(q1, q0)
.ControlledOperation
diagram draws exponent on the wrong qubits #4515Note, this is a breaking change because
gate_operation.controlled_by()
can now return acirq.GateOperation
instead ofcirq.ControlledOperation
in cases where the underlying gates have specializedgate.controlled()
implementations.CZPowGate
(@ --- @ ** 0.5
) instead ofControlledOperation
with Z ** 0.5 as subgate(@ ---- S
)op.controlled_by
for 3Q gates likeCCX
,CCZ
,CSWAP
will now returnControlledOperation
withsub_operation = <underlying non-controlled gate>
. Eg:CCCX
(i.e.sub_gate = X
) instead ofCTOFFOLI
(i.e.sub_gate = TOFFOLI
) etc.ControlledOperations
will now always have the exponent drawn on the target qubit (in case of multi qubitsub_operation
, the exponent will always be on the first qubit if not the underlying gate does not explicitly specify a target index).