Skip to content

Commit 7951f84

Browse files
authored
Deprecate Two/ThreeQubitGate (#4207)
Closes #2164 Copies `Two/ThreeQubitGate` to cirq.testing due to its convenience there, and deprecates the originals. BREAKING CHANGE: @balopat @95-martin-orion @tanujkhattar During the deprecation period, `isinstance(TwoQubitGate)` has been shimmed such that it will now return True iff _num_qubits_() returns 2, regardless of whether it's actually part of the class hierarchy (and same for ThreeQubitGate). It's not expected that this will cause real-world breakages, but possible. This can go before or after #4236, as they are disjoint problems.
1 parent 0dce2c1 commit 7951f84

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+366
-104
lines changed

cirq-core/cirq/circuits/circuit_test.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2227,7 +2227,7 @@ def test_to_text_diagram_multi_qubit_gate(circuit_cls):
22272227

22282228
@pytest.mark.parametrize('circuit_cls', [cirq.Circuit, cirq.FrozenCircuit])
22292229
def test_to_text_diagram_many_qubits_gate_but_multiple_wire_symbols(circuit_cls):
2230-
class BadGate(cirq.ThreeQubitGate):
2230+
class BadGate(cirq.testing.ThreeQubitGate):
22312231
def _circuit_diagram_info_(self, args: cirq.CircuitDiagramInfoArgs) -> Tuple[str, str]:
22322232
return 'a', 'a'
22332233

@@ -2547,7 +2547,7 @@ def test_circuit_to_unitary_matrix(circuit_cls):
25472547
_ = c.unitary()
25482548

25492549
# Gates without matrix or decomposition raise exception
2550-
class MysteryGate(cirq.TwoQubitGate):
2550+
class MysteryGate(cirq.testing.TwoQubitGate):
25512551
pass
25522552

25532553
c = circuit_cls(MysteryGate()(a, b))
@@ -2615,7 +2615,7 @@ def test_simple_circuits_to_unitary_matrix(circuit_cls):
26152615
# 2-qubit matrix matches when qubits in order.
26162616
for expected in [np.diag([1, 1j, -1, -1j]), cirq.unitary(cirq.CNOT)]:
26172617

2618-
class Passthrough(cirq.TwoQubitGate):
2618+
class Passthrough(cirq.testing.TwoQubitGate):
26192619
def _unitary_(self) -> np.ndarray:
26202620
return expected
26212621

@@ -2626,7 +2626,7 @@ def _unitary_(self) -> np.ndarray:
26262626

26272627
@pytest.mark.parametrize('circuit_cls', [cirq.Circuit, cirq.FrozenCircuit])
26282628
def test_composite_gate_to_unitary_matrix(circuit_cls):
2629-
class CnotComposite(cirq.TwoQubitGate):
2629+
class CnotComposite(cirq.testing.TwoQubitGate):
26302630
def _decompose_(self, qubits):
26312631
q0, q1 = qubits
26322632
return cirq.Y(q1) ** -0.5, cirq.CZ(q0, q1), cirq.Y(q1) ** 0.5

cirq-core/cirq/circuits/qasm_output.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def _value_equality_values_(self):
8585

8686

8787
@value.value_equality
88-
class QasmTwoQubitGate(ops.TwoQubitGate):
88+
class QasmTwoQubitGate(ops.Gate):
8989
def __init__(self, kak: linalg.KakDecomposition) -> None:
9090
"""A two qubit gate represented in QASM by the KAK decomposition.
9191
@@ -97,6 +97,9 @@ def __init__(self, kak: linalg.KakDecomposition) -> None:
9797
"""
9898
self.kak = kak
9999

100+
def _num_qubits_(self) -> int:
101+
return 2
102+
100103
def _value_equality_values_(self):
101104
return self.kak
102105

cirq-core/cirq/circuits/qasm_output_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ def test_output_unitary_same_as_qiskit():
322322

323323

324324
def test_fails_on_big_unknowns():
325-
class UnrecognizedGate(cirq.ThreeQubitGate):
325+
class UnrecognizedGate(cirq.testing.ThreeQubitGate):
326326
pass
327327

328328
c = cirq.Circuit(UnrecognizedGate().on(*cirq.LineQubit.range(3)))

cirq-core/cirq/circuits/quil_output.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def _value_equality_values_(self):
5656

5757

5858
@value.value_equality(approximate=True)
59-
class QuilTwoQubitGate(ops.TwoQubitGate):
59+
class QuilTwoQubitGate(ops.Gate):
6060
"""A two qubit gate represented in QUIL with a DEFGATE and it's 4x4
6161
unitary matrix.
6262
"""
@@ -69,6 +69,9 @@ def __init__(self, matrix: np.ndarray) -> None:
6969
"""
7070
self.matrix = matrix
7171

72+
def _num_qubits_(self) -> int:
73+
return 2
74+
7275
def _value_equality_values_(self):
7376
return self.matrix
7477

cirq-core/cirq/circuits/quil_output_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ def _all_operations(q0, q1, q2, q3, q4, include_measurements=True):
373373

374374

375375
def test_fails_on_big_unknowns():
376-
class UnrecognizedGate(cirq.ThreeQubitGate):
376+
class UnrecognizedGate(cirq.testing.ThreeQubitGate):
377377
pass
378378

379379
c = cirq.Circuit(UnrecognizedGate().on(*cirq.LineQubit.range(3)))

cirq-core/cirq/contrib/paulistring/convert_to_clifford_gates_test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def test_already_converted():
7373

7474

7575
def test_convert_composite():
76-
class CompositeDummy(cirq.TwoQubitGate):
76+
class CompositeDummy(cirq.testing.TwoQubitGate):
7777
def _decompose_(self, qubits):
7878
q0, q1 = qubits
7979
yield cirq.X(q0)
@@ -100,7 +100,7 @@ def _decompose_(self, qubits):
100100

101101

102102
def test_ignore_unsupported_gate():
103-
class UnsupportedDummy(cirq.TwoQubitGate):
103+
class UnsupportedDummy(cirq.testing.TwoQubitGate):
104104
pass
105105

106106
q0, q1 = cirq.LineQubit.range(2)
@@ -114,7 +114,7 @@ class UnsupportedDummy(cirq.TwoQubitGate):
114114

115115

116116
def test_fail_unsupported_gate():
117-
class UnsupportedDummy(cirq.TwoQubitGate):
117+
class UnsupportedDummy(cirq.testing.TwoQubitGate):
118118
pass
119119

120120
q0, q1 = cirq.LineQubit.range(2)

cirq-core/cirq/contrib/paulistring/convert_to_pauli_string_phasors_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def test_already_converted():
7272

7373

7474
def test_ignore_unsupported_gate():
75-
class UnsupportedDummy(cirq.TwoQubitGate):
75+
class UnsupportedDummy(cirq.testing.TwoQubitGate):
7676
pass
7777

7878
q0, q1 = cirq.LineQubit.range(2)
@@ -86,7 +86,7 @@ class UnsupportedDummy(cirq.TwoQubitGate):
8686

8787

8888
def test_fail_unsupported_gate():
89-
class UnsupportedDummy(cirq.TwoQubitGate):
89+
class UnsupportedDummy(cirq.testing.TwoQubitGate):
9090
pass
9191

9292
q0, q1 = cirq.LineQubit.range(2)

cirq-core/cirq/contrib/qasm_import/_parser_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -835,7 +835,7 @@ def test_standard_gates_wrong_params_error(qasm_gate: str, num_params: int):
835835

836836

837837
@pytest.mark.parametrize('qasm_gate,cirq_gate', two_qubit_gates)
838-
def test_two_qubit_gates(qasm_gate: str, cirq_gate: cirq.TwoQubitGate):
838+
def test_two_qubit_gates(qasm_gate: str, cirq_gate: cirq.testing.TwoQubitGate):
839839
qasm = """
840840
OPENQASM 2.0;
841841
include "qelib1.inc";
@@ -911,7 +911,7 @@ def test_two_qubit_gates_with_too_much_parameters(qasm_gate: str):
911911

912912

913913
@pytest.mark.parametrize('qasm_gate,cirq_gate', three_qubit_gates)
914-
def test_three_qubit_gates(qasm_gate: str, cirq_gate: cirq.TwoQubitGate):
914+
def test_three_qubit_gates(qasm_gate: str, cirq_gate: cirq.testing.TwoQubitGate):
915915
qasm = """
916916
OPENQASM 2.0;
917917
include "qelib1.inc";

cirq-core/cirq/contrib/qcircuit/qcircuit_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def assert_has_qcircuit_diagram(actual: cirq.Circuit, desired: str, **kwargs) ->
4848

4949

5050
def test_fallback_diagram():
51-
class MagicGate(cirq.ThreeQubitGate):
51+
class MagicGate(cirq.testing.ThreeQubitGate):
5252
def __str__(self):
5353
return 'MagicGate'
5454

cirq-core/cirq/experiments/cross_entropy_benchmarking.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ def cross_entropy_benchmarking(
418418

419419

420420
def build_entangling_layers(
421-
qubits: Sequence[devices.GridQubit], two_qubit_gate: ops.TwoQubitGate
421+
qubits: Sequence[devices.GridQubit], two_qubit_gate: ops.Gate
422422
) -> List[ops.Moment]:
423423
"""Builds a sequence of gates that entangle all pairs of qubits on a grid.
424424
@@ -465,7 +465,12 @@ def build_entangling_layers(
465465
Returns:
466466
A list of ops.Moment, with a maximum length of 4. Each ops.Moment
467467
includes two-qubit gates which can be performed at the same time.
468+
469+
Raises:
470+
ValueError: two-qubit gate is not used.
468471
"""
472+
if protocols.num_qubits(two_qubit_gate) != 2:
473+
raise ValueError('Input must be a two-qubit gate')
469474
interaction_sequence = _default_interaction_sequence(qubits)
470475
return [
471476
ops.Moment([two_qubit_gate(q_a, q_b) for (q_a, q_b) in pairs])

cirq-core/cirq/experiments/cross_entropy_benchmarking_test.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ def test_cross_entropy_benchmarking():
3333
simulator = cirq.Simulator()
3434
qubits = cirq.GridQubit.square(2)
3535

36+
# Sanity check single-qubit-gate causes error
37+
with pytest.raises(ValueError):
38+
build_entangling_layers(qubits, cirq.Z ** 0.91)
39+
3640
# Build a sequence of CZ gates.
3741
interleaved_ops = build_entangling_layers(qubits, cirq.CZ ** 0.91)
3842

cirq-core/cirq/ion/convert_to_ion_gates_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class NoUnitary(cirq.SingleQubitGate):
2828
pass
2929

3030

31-
class OtherCNOT(cirq.TwoQubitGate):
31+
class OtherCNOT(cirq.testing.TwoQubitGate):
3232
def _unitary_(self) -> np.ndarray:
3333
return np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]])
3434

cirq-core/cirq/neutral_atoms/convert_to_neutral_atom_gates_test.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
import pytest
1615
import numpy as np
16+
import pytest
1717

1818
import cirq
1919
from cirq import ops
2020

2121

2222
def test_coverage():
2323
q = cirq.LineQubit.range(3)
24-
g = cirq.ThreeQubitGate()
24+
g = cirq.testing.ThreeQubitGate()
2525

2626
class FakeOperation(ops.Operation):
2727
def __init__(self, gate, qubits):
@@ -64,11 +64,11 @@ def _decompose_(self, qubits):
6464

6565

6666
def test_avoids_decompose_fallback_when_matrix_available_two_qubit():
67-
class OtherCZ(cirq.TwoQubitGate):
67+
class OtherCZ(cirq.testing.TwoQubitGate):
6868
def _unitary_(self) -> np.ndarray:
6969
return np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]])
7070

71-
class OtherOtherCZ(cirq.TwoQubitGate):
71+
class OtherOtherCZ(cirq.testing.TwoQubitGate):
7272
def _decompose_(self, qubits):
7373
return OtherCZ().on(*qubits)
7474

cirq-core/cirq/ops/common_gates.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,9 +1009,7 @@ def __repr__(self) -> str:
10091009
)
10101010

10111011

1012-
class CZPowGate(
1013-
eigen_gate.EigenGate, gate_features.TwoQubitGate, gate_features.InterchangeableQubitsGate
1014-
):
1012+
class CZPowGate(gate_features.InterchangeableQubitsGate, eigen_gate.EigenGate):
10151013
"""A gate that applies a phase to the |11⟩ state of two qubits.
10161014
10171015
The unitary matrix of `CZPowGate(exponent=t)` is:
@@ -1029,6 +1027,9 @@ class CZPowGate(
10291027
`exponent=1`.
10301028
"""
10311029

1030+
def _num_qubits_(self) -> int:
1031+
return 2
1032+
10321033
def _decompose_into_clifford_with_qubits_(self, qubits):
10331034
from cirq.ops.pauli_interaction_gate import PauliInteractionGate
10341035

@@ -1216,7 +1217,7 @@ def __repr__(self) -> str:
12161217
)
12171218

12181219

1219-
class CXPowGate(eigen_gate.EigenGate, gate_features.TwoQubitGate):
1220+
class CXPowGate(eigen_gate.EigenGate):
12201221
"""A gate that applies a controlled power of an X gate.
12211222
12221223
When applying CNOT (controlled-not) to qubits, you can either use
@@ -1241,6 +1242,9 @@ class CXPowGate(eigen_gate.EigenGate, gate_features.TwoQubitGate):
12411242
`exponent=1`.
12421243
"""
12431244

1245+
def _num_qubits_(self) -> int:
1246+
return 2
1247+
12441248
def _decompose_into_clifford_with_qubits_(self, qubits):
12451249
from cirq.ops.pauli_interaction_gate import PauliInteractionGate
12461250

cirq-core/cirq/ops/controlled_gate_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ def test_circuit_diagram():
482482
)
483483

484484

485-
class MockGate(cirq.TwoQubitGate):
485+
class MockGate(cirq.testing.TwoQubitGate):
486486
def _circuit_diagram_info_(self, args: cirq.CircuitDiagramInfoArgs) -> cirq.CircuitDiagramInfo:
487487
self.captured_diagram_args = args
488488
return cirq.CircuitDiagramInfo(wire_symbols=tuple(['MOCK']), exponent=1, connected=True)

cirq-core/cirq/ops/controlled_operation_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ def test_circuit_diagram():
246246
)
247247

248248

249-
class MockGate(cirq.TwoQubitGate):
249+
class MockGate(cirq.testing.TwoQubitGate):
250250
def _circuit_diagram_info_(
251251
self, args: protocols.CircuitDiagramInfoArgs
252252
) -> protocols.CircuitDiagramInfo:

cirq-core/cirq/ops/eigen_gate_test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from cirq.testing import assert_has_consistent_trace_distance_bound
2525

2626

27-
class CExpZinGate(cirq.EigenGate, cirq.TwoQubitGate):
27+
class CExpZinGate(cirq.EigenGate, cirq.testing.TwoQubitGate):
2828
"""Two-qubit gate for the following matrix:
2929
[1 0 0 0]
3030
[0 1 0 0]
@@ -50,7 +50,7 @@ def _eigen_components(self) -> List[Tuple[float, np.ndarray]]:
5050
]
5151

5252

53-
class ZGateDef(cirq.EigenGate, cirq.TwoQubitGate):
53+
class ZGateDef(cirq.EigenGate, cirq.testing.TwoQubitGate):
5454
@property
5555
def exponent(self):
5656
return self._exponent
@@ -148,7 +148,7 @@ def test_approx_eq_periodic():
148148

149149

150150
def test_period():
151-
class Components(cirq.EigenGate, cirq.TwoQubitGate):
151+
class Components(cirq.EigenGate, cirq.testing.TwoQubitGate):
152152
def __init__(self, a, b, c, d):
153153
super().__init__()
154154
self.a = a

cirq-core/cirq/ops/fsim_gate.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
import cirq
3232
from cirq import protocols, value
3333
from cirq._compat import proper_repr
34-
from cirq.ops import gate_features
34+
from cirq.ops import gate_features, raw_types
3535

3636

3737
def _canonicalize(value: Union[float, sympy.Basic]) -> Union[float, sympy.Basic]:
@@ -53,7 +53,7 @@ def _half_pi_mod_pi(param: Union[float, sympy.Basic]) -> bool:
5353

5454

5555
@value.value_equality(approximate=True)
56-
class FSimGate(gate_features.TwoQubitGate, gate_features.InterchangeableQubitsGate):
56+
class FSimGate(gate_features.InterchangeableQubitsGate, raw_types.Gate):
5757
"""Fermionic simulation gate family.
5858
5959
Contains all two qubit interactions that preserve excitations, up to
@@ -93,6 +93,9 @@ def __init__(self, theta: float, phi: float) -> None:
9393
self.theta = _canonicalize(theta)
9494
self.phi = _canonicalize(phi)
9595

96+
def _num_qubits_(self) -> int:
97+
return 2
98+
9699
def _value_equality_values_(self) -> Any:
97100
return self.theta, self.phi
98101

@@ -188,7 +191,7 @@ def _json_dict_(self) -> Dict[str, Any]:
188191

189192

190193
@value.value_equality(approximate=True)
191-
class PhasedFSimGate(gate_features.TwoQubitGate, gate_features.InterchangeableQubitsGate):
194+
class PhasedFSimGate(gate_features.InterchangeableQubitsGate, raw_types.Gate):
192195
"""General excitation-preserving two-qubit gate.
193196
194197
The unitary matrix of PhasedFSimGate(θ, ζ, χ, γ, φ) is:
@@ -448,3 +451,6 @@ def __repr__(self) -> str:
448451

449452
def _json_dict_(self) -> Dict[str, Any]:
450453
return protocols.obj_to_dict_helper(self, ['theta', 'zeta', 'chi', 'gamma', 'phi'])
454+
455+
def _num_qubits_(self) -> int:
456+
return 2

0 commit comments

Comments
 (0)