Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Resolve inconsistencies in using controlled gates & controlled operat…
…ions (#4167) This PR tries to resolve the inconsistencies mentioned in #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 #2142, i.e. `cirq.Z(q0).controlled_by(q1) == cirq.CZ(q1, q0)`. - Fixes #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).
- Loading branch information
1 parent
9d0ac9c
commit f48efe0
Showing
15 changed files
with
316 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# Copyright 2021 The Cirq Developers | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# https://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
from typing import Sequence, Optional, Union, Collection | ||
|
||
from cirq import protocols, devices, ops | ||
|
||
|
||
def assert_controlled_and_controlled_by_identical( | ||
gate: ops.Gate, | ||
*, | ||
num_controls: Sequence[int] = (2, 1, 3, 10), | ||
control_values: Optional[Sequence[Optional[Sequence[Union[int, Collection[int]]]]]] = None, | ||
) -> None: | ||
"""Checks that gate.on().controlled_by() == gate.controlled().on()""" | ||
if control_values is not None: | ||
if len(num_controls) != len(control_values): | ||
raise ValueError(f"len(num_controls) != len(control_values)") | ||
for i, num_control in enumerate(num_controls): | ||
control_value = control_values[i] if control_values else None | ||
if control_value is not None and len(control_value) != num_control: | ||
raise ValueError(f"len(control_values[{i}]) != num_controls[{i}]") | ||
_assert_gate_consistent(gate, num_control, control_value) | ||
|
||
|
||
def _assert_gate_consistent( | ||
gate: ops.Gate, | ||
num_controls: int, | ||
control_values: Optional[Sequence[Union[int, Collection[int]]]], | ||
) -> None: | ||
if isinstance(gate, ops.DensePauliString) and protocols.is_parameterized(gate): | ||
# Parameterized `DensePauliString`s cannot be applied to qubits to produce valid operations. | ||
# TODO: This behavior should be fixed (https://github.com/quantumlib/Cirq/issues/4508) | ||
return None | ||
gate_controlled = gate.controlled(num_controls, control_values) | ||
qubits = devices.LineQid.for_gate(gate_controlled) | ||
control_qubits = qubits[:num_controls] | ||
gate_qubits = qubits[num_controls:] | ||
gate_controlled_on = gate_controlled.on(*control_qubits, *gate_qubits) | ||
gate_on_controlled_by = gate.on(*gate_qubits).controlled_by( | ||
*control_qubits, control_values=control_values | ||
) | ||
assert ( | ||
gate_controlled_on == gate_on_controlled_by | ||
), "gate.controlled().on() and gate.on().controlled() should return the same operations." |
Oops, something went wrong.