diff --git a/cirq-core/cirq/circuits/circuit_test.py b/cirq-core/cirq/circuits/circuit_test.py index eb24c353bce..00f326dd02c 100644 --- a/cirq-core/cirq/circuits/circuit_test.py +++ b/cirq-core/cirq/circuits/circuit_test.py @@ -1884,7 +1884,7 @@ def test_qid_shape_qubit(circuit_cls): @pytest.mark.parametrize('circuit_cls', [cirq.Circuit, cirq.FrozenCircuit]) def test_qid_shape_qudit(circuit_cls): - class PlusOneMod3Gate(cirq.SingleQubitGate): + class PlusOneMod3Gate(cirq.testing.SingleQubitGate): def _qid_shape_(self): return (3,) @@ -1892,7 +1892,7 @@ class C2NotGate(cirq.Gate): def _qid_shape_(self): return (3, 2) - class IdentityGate(cirq.SingleQubitGate): + class IdentityGate(cirq.testing.SingleQubitGate): def _qid_shape_(self): return (1,) @@ -1987,13 +1987,13 @@ def test_to_text_diagram_teleportation_to_diagram(circuit_cls): @pytest.mark.parametrize('circuit_cls', [cirq.Circuit, cirq.FrozenCircuit]) def test_diagram_with_unknown_exponent(circuit_cls): - class WeirdGate(cirq.SingleQubitGate): + class WeirdGate(cirq.testing.SingleQubitGate): def _circuit_diagram_info_( self, args: cirq.CircuitDiagramInfoArgs ) -> cirq.CircuitDiagramInfo: return cirq.CircuitDiagramInfo(wire_symbols=('B',), exponent='fancy') - class WeirderGate(cirq.SingleQubitGate): + class WeirderGate(cirq.testing.SingleQubitGate): def _circuit_diagram_info_( self, args: cirq.CircuitDiagramInfoArgs ) -> cirq.CircuitDiagramInfo: @@ -2103,7 +2103,7 @@ def _circuit_diagram_info_(self, args: cirq.CircuitDiagramInfoArgs) -> Tuple[str def test_to_text_diagram_parameterized_value(circuit_cls): q = cirq.NamedQubit('cube') - class PGate(cirq.SingleQubitGate): + class PGate(cirq.testing.SingleQubitGate): def __init__(self, val): self.val = val @@ -2266,10 +2266,10 @@ def test_diagram_global_phase(circuit_cls): @pytest.mark.parametrize('circuit_cls', [cirq.Circuit, cirq.FrozenCircuit]) def test_has_unitary(circuit_cls): - class NonUnitary(cirq.SingleQubitGate): + class NonUnitary(cirq.testing.SingleQubitGate): pass - class EventualUnitary(cirq.SingleQubitGate): + class EventualUnitary(cirq.testing.SingleQubitGate): def _decompose_(self, qubits): return cirq.X.on_each(*qubits) @@ -4098,7 +4098,7 @@ def test_indexing_by_numpy_integer(circuit_cls): @pytest.mark.parametrize('circuit_cls', [cirq.Circuit, cirq.FrozenCircuit]) def test_all_measurement_key_names(circuit_cls): - class Unknown(cirq.SingleQubitGate): + class Unknown(cirq.testing.SingleQubitGate): def _measurement_key_name_(self): return 'test' diff --git a/cirq-core/cirq/circuits/moment_test.py b/cirq-core/cirq/circuits/moment_test.py index a62a42b15f6..9ce069057bb 100644 --- a/cirq-core/cirq/circuits/moment_test.py +++ b/cirq-core/cirq/circuits/moment_test.py @@ -574,10 +574,7 @@ def test_moment_text_diagram(): xy_breakdown_func=lambda q: ('abc'[q.x], q.x), ) - class EmptyGate(cirq.Gate): - def _num_qubits_(self) -> int: - return 1 - + class EmptyGate(cirq.testing.SingleQubitGate): def __str__(self): return 'Empty' @@ -701,9 +698,8 @@ def test_kraus_too_big(): def test_op_has_no_kraus(): - class EmptyGate(cirq.Gate): - def _num_qubits_(self) -> int: - return 1 + class EmptyGate(cirq.testing.SingleQubitGate): + pass m = cirq.Moment(EmptyGate().on(cirq.NamedQubit("a"))) assert not cirq.has_kraus(m) diff --git a/cirq-core/cirq/circuits/qasm_output.py b/cirq-core/cirq/circuits/qasm_output.py index 25540ad167a..2d19138f47e 100644 --- a/cirq-core/cirq/circuits/qasm_output.py +++ b/cirq-core/cirq/circuits/qasm_output.py @@ -26,7 +26,7 @@ @value.value_equality(approximate=True) -class QasmUGate(ops.SingleQubitGate): +class QasmUGate(ops.Gate): def __init__(self, theta, phi, lmda) -> None: """A QASM gate representing any single qubit unitary with a series of three rotations, Z, Y, and Z. @@ -42,6 +42,9 @@ def __init__(self, theta, phi, lmda) -> None: self.theta = theta % 2 self.phi = phi % 2 + def _num_qubits_(self) -> int: + return 1 + @staticmethod def from_matrix(mat: np.ndarray) -> 'QasmUGate': pre_phase, rotation, post_phase = linalg.deconstruct_single_qubit_matrix_into_angles(mat) diff --git a/cirq-core/cirq/circuits/quil_output.py b/cirq-core/cirq/circuits/quil_output.py index d28cc15f36b..1954ce2eb8e 100644 --- a/cirq-core/cirq/circuits/quil_output.py +++ b/cirq-core/cirq/circuits/quil_output.py @@ -25,7 +25,7 @@ def to_quil_complex_format(num) -> str: @value.value_equality(approximate=True) -class QuilOneQubitGate(ops.SingleQubitGate): +class QuilOneQubitGate(ops.Gate): """A QUIL gate representing any single qubit unitary with a DEFGATE and 2x2 matrix in QUIL. """ @@ -38,6 +38,9 @@ def __init__(self, matrix: np.ndarray) -> None: """ self.matrix = matrix + def _num_qubits_(self) -> int: + return 1 + def _quil_(self, qubits: Tuple['cirq.Qid', ...], formatter: 'cirq.QuilFormatter') -> str: return ( f'DEFGATE USERGATE:\n ' diff --git a/cirq-core/cirq/contrib/qasm_import/_parser_test.py b/cirq-core/cirq/contrib/qasm_import/_parser_test.py index 451e30da4c0..ddd71ce9220 100644 --- a/cirq-core/cirq/contrib/qasm_import/_parser_test.py +++ b/cirq-core/cirq/contrib/qasm_import/_parser_test.py @@ -429,7 +429,7 @@ def test_unknown_function(): @pytest.mark.parametrize('qasm_gate,cirq_gate', rotation_gates) -def test_rotation_gates(qasm_gate: str, cirq_gate: cirq.SingleQubitGate): +def test_rotation_gates(qasm_gate: str, cirq_gate: cirq.Gate): qasm = """OPENQASM 2.0; include "qelib1.inc"; qreg q[2]; @@ -989,7 +989,7 @@ def test_three_qubit_gates_with_too_much_parameters(qasm_gate: str): @pytest.mark.parametrize('qasm_gate,cirq_gate', single_qubit_gates) -def test_single_qubit_gates(qasm_gate: str, cirq_gate: cirq.SingleQubitGate): +def test_single_qubit_gates(qasm_gate: str, cirq_gate: cirq.Gate): qasm = """OPENQASM 2.0; include "qelib1.inc"; qreg q[2]; diff --git a/cirq-core/cirq/contrib/quirk/export_to_quirk_test.py b/cirq-core/cirq/contrib/quirk/export_to_quirk_test.py index 49d18f75297..2656cb2a969 100644 --- a/cirq-core/cirq/contrib/quirk/export_to_quirk_test.py +++ b/cirq-core/cirq/contrib/quirk/export_to_quirk_test.py @@ -177,7 +177,7 @@ def with_qubits(self, *new_qubits): return MysteryOperation(*new_qubits) -class MysteryGate(cirq.SingleQubitGate): +class MysteryGate(cirq.testing.SingleQubitGate): pass @@ -279,7 +279,7 @@ def test_unrecognized_single_qubit_gate_with_matrix(): def test_unknown_gate(): - class UnknownGate(cirq.SingleQubitGate): + class UnknownGate(cirq.testing.SingleQubitGate): pass a = cirq.NamedQubit('a') diff --git a/cirq-core/cirq/devices/noise_model.py b/cirq-core/cirq/devices/noise_model.py index b30ded1ab5d..8300cde7003 100644 --- a/cirq-core/cirq/devices/noise_model.py +++ b/cirq-core/cirq/devices/noise_model.py @@ -264,7 +264,7 @@ def noisy_moment( """, ) -NOISE_MODEL_LIKE = Union[None, 'cirq.NoiseModel', 'cirq.SingleQubitGate'] +NOISE_MODEL_LIKE = Union[None, 'cirq.NoiseModel', 'cirq.Gate'] document( NOISE_MODEL_LIKE, # type: ignore """A `cirq.NoiseModel` or a value that can be trivially converted into one. diff --git a/cirq-core/cirq/experiments/cross_entropy_benchmarking.py b/cirq-core/cirq/experiments/cross_entropy_benchmarking.py index 2d6855ccc90..5c4a2060541 100644 --- a/cirq-core/cirq/experiments/cross_entropy_benchmarking.py +++ b/cirq-core/cirq/experiments/cross_entropy_benchmarking.py @@ -284,7 +284,7 @@ def cross_entropy_benchmarking( num_circuits: int = 20, repetitions: int = 1000, cycles: Union[int, Iterable[int]] = range(2, 103, 10), - scrambling_gates_per_cycle: List[List[ops.SingleQubitGate]] = None, + scrambling_gates_per_cycle: List[List[ops.Gate]] = None, simulator: sim.Simulator = None, ) -> CrossEntropyResult: r"""Cross-entropy benchmarking (XEB) of multiple qubits. @@ -482,7 +482,7 @@ def build_entangling_layers( def _build_xeb_circuits( qubits: Sequence[ops.Qid], cycles: Sequence[int], - single_qubit_gates: List[List[ops.SingleQubitGate]] = None, + single_qubit_gates: List[List[ops.Gate]] = None, benchmark_ops: Sequence[circuits.Moment] = None, ) -> List[circuits.Circuit]: if benchmark_ops is not None: @@ -570,7 +570,7 @@ def _random_half_rotations(qubits: Sequence[ops.Qid], num_layers: int) -> List[L def _random_any_gates( - qubits: Sequence[ops.Qid], op_list: List[List[ops.SingleQubitGate]], num_layers: int + qubits: Sequence[ops.Qid], op_list: List[List[ops.Gate]], num_layers: int ) -> List[List[ops.OP_TREE]]: num_ops = len(op_list) num_qubits = len(qubits) diff --git a/cirq-core/cirq/interop/quirk/cells/control_cells.py b/cirq-core/cirq/interop/quirk/cells/control_cells.py index dc0d8b85198..3bc23e210c3 100644 --- a/cirq-core/cirq/interop/quirk/cells/control_cells.py +++ b/cirq-core/cirq/interop/quirk/cells/control_cells.py @@ -132,7 +132,7 @@ def generate_all_control_cell_makers() -> Iterator[CellMaker]: yield _reg_parity_control("zpar", basis_change=None) -def _reg_control(identifier: str, *, basis_change: Optional['cirq.SingleQubitGate']) -> CellMaker: +def _reg_control(identifier: str, *, basis_change: Optional['cirq.Gate']) -> CellMaker: return CellMaker( identifier=identifier, size=1, @@ -143,7 +143,7 @@ def _reg_control(identifier: str, *, basis_change: Optional['cirq.SingleQubitGat def _reg_parity_control( - identifier: str, *, basis_change: Optional['cirq.SingleQubitGate'] = None + identifier: str, *, basis_change: Optional['cirq.Gate'] = None ) -> CellMaker: return CellMaker( identifier=identifier, @@ -155,7 +155,7 @@ def _reg_parity_control( def _basis_else_empty( - basis_change: Optional['cirq.SingleQubitGate'], qureg: Union['cirq.Qid', Iterable['cirq.Qid']] + basis_change: Optional['cirq.Gate'], qureg: Union['cirq.Qid', Iterable['cirq.Qid']] ) -> Iterable['cirq.Operation']: if basis_change is None: return () diff --git a/cirq-core/cirq/ion/convert_to_ion_gates_test.py b/cirq-core/cirq/ion/convert_to_ion_gates_test.py index 40970638e11..3f0979477d0 100644 --- a/cirq-core/cirq/ion/convert_to_ion_gates_test.py +++ b/cirq-core/cirq/ion/convert_to_ion_gates_test.py @@ -19,12 +19,12 @@ import cirq -class OtherX(cirq.SingleQubitGate): +class OtherX(cirq.testing.SingleQubitGate): def _unitary_(self) -> np.ndarray: return np.array([[0, 1], [1, 0]]) -class NoUnitary(cirq.SingleQubitGate): +class NoUnitary(cirq.testing.SingleQubitGate): pass diff --git a/cirq-core/cirq/ion/ion_device_test.py b/cirq-core/cirq/ion/ion_device_test.py index eec92851b6d..24f9e8a1b7e 100644 --- a/cirq-core/cirq/ion/ion_device_test.py +++ b/cirq-core/cirq/ion/ion_device_test.py @@ -53,8 +53,8 @@ def test_init(): assert d.duration_of(cirq.measure(q0)) == 100 * ms assert d.duration_of(cirq.measure(q0, q1)) == 100 * ms assert d.duration_of(cirq.ops.XX(q0, q1)) == 200 * ms - with pytest.raises(ValueError): - _ = d.duration_of(cirq.SingleQubitGate().on(q0)) + with pytest.raises(ValueError, match="Unsupported gate type"): + _ = d.duration_of(cirq.I(q0)) with pytest.raises(TypeError, match="NamedQubit"): _ = cirq.IonDevice( @@ -86,7 +86,7 @@ def test_init_timedelta(): assert d.duration_of(cirq.measure(q0, q1)) == 100 * ms assert d.duration_of(cirq.ops.XX(q0, q1)) == 200 * ms with pytest.raises(ValueError): - _ = d.duration_of(cirq.SingleQubitGate().on(q0)) + _ = d.duration_of(cirq.testing.SingleQubitGate().on(q0)) def test_decomposition_deprecated(): diff --git a/cirq-core/cirq/neutral_atoms/convert_to_neutral_atom_gates_test.py b/cirq-core/cirq/neutral_atoms/convert_to_neutral_atom_gates_test.py index 2e132535647..ae88e2d9656 100644 --- a/cirq-core/cirq/neutral_atoms/convert_to_neutral_atom_gates_test.py +++ b/cirq-core/cirq/neutral_atoms/convert_to_neutral_atom_gates_test.py @@ -50,11 +50,11 @@ def with_qubits(self, *new_qubits): def test_avoids_decompose_fallback_when_matrix_available_single_qubit(): - class OtherX(cirq.SingleQubitGate): + class OtherX(cirq.testing.SingleQubitGate): def _unitary_(self) -> np.ndarray: return np.array([[0, 1], [1, 0]]) - class OtherOtherX(cirq.SingleQubitGate): + class OtherOtherX(cirq.testing.SingleQubitGate): def _decompose_(self, qubits): return OtherX().on(*qubits) diff --git a/cirq-core/cirq/neutral_atoms/neutral_atom_devices_test.py b/cirq-core/cirq/neutral_atoms/neutral_atom_devices_test.py index fc8e37e427a..0561931ca16 100644 --- a/cirq-core/cirq/neutral_atoms/neutral_atom_devices_test.py +++ b/cirq-core/cirq/neutral_atoms/neutral_atom_devices_test.py @@ -53,7 +53,7 @@ def test_init(): assert d.duration_of(cirq.GateOperation(cirq.IdentityGate(1), [q00])) == 100 * us assert d.duration_of(cirq.measure(q00)) == 50 * ms with pytest.raises(ValueError): - _ = d.duration_of(cirq.SingleQubitGate().on(q00)) + _ = d.duration_of(cirq.testing.SingleQubitGate().on(q00)) def test_metadata(): @@ -83,7 +83,7 @@ def test_init_timedelta(): assert d.duration_of(cirq.GateOperation(cirq.IdentityGate(1), [q00])) == 100 * us assert d.duration_of(cirq.measure(q00)) == 50 * ms with pytest.raises(ValueError): - _ = d.duration_of(cirq.SingleQubitGate().on(q00)) + _ = d.duration_of(cirq.testing.SingleQubitGate().on(q00)) def test_init_errors(): @@ -126,7 +126,7 @@ def test_validate_gate_errors(): with pytest.raises(ValueError, match="controlled gates must have integer exponents"): d.validate_gate(cirq.CNotPowGate(exponent=0.5)) with pytest.raises(ValueError, match="Unsupported gate"): - d.validate_gate(cirq.SingleQubitGate()) + d.validate_gate(cirq.testing.SingleQubitGate()) def test_validate_operation_errors(): diff --git a/cirq-core/cirq/ops/clifford_gate.py b/cirq-core/cirq/ops/clifford_gate.py index 48906d89437..06cfcb9afc2 100644 --- a/cirq-core/cirq/ops/clifford_gate.py +++ b/cirq-core/cirq/ops/clifford_gate.py @@ -127,7 +127,7 @@ def _validate_map_input( def _pad_tableau( clifford_tableau: qis.CliffordTableau, num_qubits_after_padding: int, axes: List[int] ) -> qis.CliffordTableau: - """Roughly, this function copies self.tabluea into the "identity" matrix.""" + """Roughly, this function copies self.tableau into the "identity" matrix.""" # Sanity check if len(set(axes)) != clifford_tableau.n: raise ValueError( diff --git a/cirq-core/cirq/ops/common_channels.py b/cirq-core/cirq/ops/common_channels.py index 8b5ec1c5bc2..dda34bfb96f 100644 --- a/cirq-core/cirq/ops/common_channels.py +++ b/cirq-core/cirq/ops/common_channels.py @@ -20,7 +20,7 @@ import numpy as np from cirq import protocols, value -from cirq.ops import raw_types, common_gates, pauli_gates, gate_features, identity +from cirq.ops import raw_types, common_gates, pauli_gates, identity if TYPE_CHECKING: @@ -28,7 +28,7 @@ @value.value_equality -class AsymmetricDepolarizingChannel(gate_features.SingleQubitGate): +class AsymmetricDepolarizingChannel(raw_types.Gate): """A channel that depolarizes asymmetrically along different directions.""" def __init__( @@ -380,7 +380,7 @@ def depolarize(p: float, n_qubits: int = 1) -> DepolarizingChannel: @value.value_equality -class GeneralizedAmplitudeDampingChannel(gate_features.SingleQubitGate): +class GeneralizedAmplitudeDampingChannel(raw_types.Gate): """Dampen qubit amplitudes through non ideal dissipation. This channel models the effect of energy dissipation into the environment @@ -444,6 +444,9 @@ def __init__(self, p: float, gamma: float) -> None: self._p = value.validate_probability(p, 'p') self._gamma = value.validate_probability(gamma, 'gamma') + def _num_qubits_(self) -> int: + return 1 + def _kraus_(self) -> Iterable[np.ndarray]: p0 = np.sqrt(self._p) p1 = np.sqrt(1.0 - self._p) @@ -541,7 +544,7 @@ def generalized_amplitude_damp(p: float, gamma: float) -> GeneralizedAmplitudeDa @value.value_equality -class AmplitudeDampingChannel(gate_features.SingleQubitGate): +class AmplitudeDampingChannel(raw_types.Gate): """Dampen qubit amplitudes through dissipation. This channel models the effect of energy dissipation to the @@ -585,6 +588,9 @@ def __init__(self, gamma: float) -> None: self._gamma = value.validate_probability(gamma, 'gamma') self._delegate = GeneralizedAmplitudeDampingChannel(1.0, self._gamma) + def _num_qubits_(self) -> int: + return 1 + def _kraus_(self) -> Iterable[np.ndarray]: # just return first two kraus ops, we don't care about # the last two. @@ -655,7 +661,7 @@ def amplitude_damp(gamma: float) -> AmplitudeDampingChannel: @value.value_equality -class ResetChannel(gate_features.SingleQubitGate): +class ResetChannel(raw_types.Gate): """Reset a qubit back to its |0⟩ state. The reset channel is equivalent to performing an unobserved measurement @@ -782,7 +788,7 @@ def reset_each(*qubits: 'cirq.Qid') -> List[raw_types.Operation]: @value.value_equality -class PhaseDampingChannel(gate_features.SingleQubitGate): +class PhaseDampingChannel(raw_types.Gate): """Dampen qubit phase. This channel models phase damping which is the loss of quantum @@ -824,6 +830,9 @@ def __init__(self, gamma: float) -> None: """ self._gamma = value.validate_probability(gamma, 'gamma') + def _num_qubits_(self) -> int: + return 1 + def _kraus_(self) -> Iterable[np.ndarray]: return ( np.array([[1.0, 0.0], [0.0, np.sqrt(1.0 - self._gamma)]]), @@ -895,7 +904,7 @@ def phase_damp(gamma: float) -> PhaseDampingChannel: @value.value_equality -class PhaseFlipChannel(gate_features.SingleQubitGate): +class PhaseFlipChannel(raw_types.Gate): """Probabilistically flip the sign of the phase of a qubit.""" def __init__(self, p: float) -> None: @@ -934,6 +943,9 @@ def __init__(self, p: float) -> None: self._p = value.validate_probability(p, 'p') self._delegate = AsymmetricDepolarizingChannel(0.0, 0.0, p) + def _num_qubits_(self) -> int: + return 1 + def _mixture_(self) -> Sequence[Tuple[float, np.ndarray]]: mixture = self._delegate._mixture_() # just return identity and z term @@ -1048,7 +1060,7 @@ def phase_flip(p: Optional[float] = None) -> Union[common_gates.ZPowGate, PhaseF @value.value_equality -class BitFlipChannel(gate_features.SingleQubitGate): +class BitFlipChannel(raw_types.Gate): r"""Probabilistically flip a qubit from 1 to 0 state or vice versa.""" def __init__(self, p: float) -> None: @@ -1087,6 +1099,9 @@ def __init__(self, p: float) -> None: self._p = value.validate_probability(p, 'p') self._delegate = AsymmetricDepolarizingChannel(p, 0.0, 0.0) + def _num_qubits_(self) -> int: + return 1 + def _mixture_(self) -> Sequence[Tuple[float, np.ndarray]]: mixture = self._delegate._mixture_() # just return identity and x term diff --git a/cirq-core/cirq/ops/common_channels_test.py b/cirq-core/cirq/ops/common_channels_test.py index e3090beb7fb..0f87a522843 100644 --- a/cirq-core/cirq/ops/common_channels_test.py +++ b/cirq-core/cirq/ops/common_channels_test.py @@ -443,6 +443,8 @@ def test_reset_channel(): np.testing.assert_almost_equal( cirq.kraus(r), (np.array([[1.0, 0.0], [0.0, 0]]), np.array([[0.0, 1.0], [0.0, 0.0]])) ) + + assert cirq.num_qubits(r) == 1 assert cirq.has_kraus(r) assert not cirq.has_mixture(r) assert cirq.qid_shape(r) == (2,) diff --git a/cirq-core/cirq/ops/common_gate_families_test.py b/cirq-core/cirq/ops/common_gate_families_test.py index 5c2a25a72c4..06585285df2 100644 --- a/cirq-core/cirq/ops/common_gate_families_test.py +++ b/cirq-core/cirq/ops/common_gate_families_test.py @@ -51,12 +51,12 @@ def test_any_unitary_gate_family(): assert 'Any-Qubit' in gate_family.name assert 'any unitary' in gate_family.description - assert cirq.SingleQubitGate() not in cirq.AnyUnitaryGateFamily() + assert cirq.testing.SingleQubitGate() not in cirq.AnyUnitaryGateFamily() def test_any_integer_power_gate_family(): with pytest.raises(ValueError, match='subclass of `cirq.EigenGate`'): - cirq.AnyIntegerPowerGateFamily(gate=cirq.SingleQubitGate) + cirq.AnyIntegerPowerGateFamily(gate=cirq.testing.SingleQubitGate) with pytest.raises(ValueError, match='subclass of `cirq.EigenGate`'): cirq.AnyIntegerPowerGateFamily(gate=CustomXPowGate()) eq = cirq.testing.EqualsTester() diff --git a/cirq-core/cirq/ops/common_gates.py b/cirq-core/cirq/ops/common_gates.py index 0721b3d579e..d2f6d830f02 100644 --- a/cirq-core/cirq/ops/common_gates.py +++ b/cirq-core/cirq/ops/common_gates.py @@ -67,7 +67,7 @@ def _pi(rads): @value.value_equality -class XPowGate(eigen_gate.EigenGate, gate_features.SingleQubitGate): +class XPowGate(eigen_gate.EigenGate): """A gate that rotates around the X axis of the Bloch sphere. The unitary matrix of ``XPowGate(exponent=t)`` is: @@ -90,6 +90,9 @@ class XPowGate(eigen_gate.EigenGate, gate_features.SingleQubitGate): `cirq.X`, the Pauli X gate, is an instance of this gate at exponent=1. """ + def _num_qubits_(self) -> int: + return 1 + def _apply_unitary_(self, args: 'protocols.ApplyUnitaryArgs') -> Optional[np.ndarray]: if self._exponent != 1: return NotImplemented @@ -282,7 +285,7 @@ def _from_json_dict_(cls, rads, **kwargs) -> 'Rx': @value.value_equality -class YPowGate(eigen_gate.EigenGate, gate_features.SingleQubitGate): +class YPowGate(eigen_gate.EigenGate): """A gate that rotates around the Y axis of the Bloch sphere. The unitary matrix of ``YPowGate(exponent=t)`` is: @@ -305,6 +308,9 @@ class YPowGate(eigen_gate.EigenGate, gate_features.SingleQubitGate): `cirq.Y`, the Pauli Y gate, is an instance of this gate at exponent=1. """ + def _num_qubits_(self) -> int: + return 1 + def _apply_unitary_(self, args: 'protocols.ApplyUnitaryArgs') -> Optional[np.ndarray]: if self._exponent != 1: return NotImplemented @@ -450,7 +456,7 @@ def _from_json_dict_(cls, rads, **kwargs) -> 'Ry': @value.value_equality -class ZPowGate(eigen_gate.EigenGate, gate_features.SingleQubitGate): +class ZPowGate(eigen_gate.EigenGate): """A gate that rotates around the Z axis of the Bloch sphere. The unitary matrix of ``ZPowGate(exponent=t)`` is: @@ -471,6 +477,9 @@ class ZPowGate(eigen_gate.EigenGate, gate_features.SingleQubitGate): `cirq.Z`, the Pauli Z gate, is an instance of this gate at exponent=1. """ + def _num_qubits_(self) -> int: + return 1 + def _apply_unitary_(self, args: 'protocols.ApplyUnitaryArgs') -> Optional[np.ndarray]: if protocols.is_parameterized(self): return None @@ -688,7 +697,7 @@ def _from_json_dict_(cls, rads, **kwargs) -> 'Rz': return cls(rads=rads) -class HPowGate(eigen_gate.EigenGate, gate_features.SingleQubitGate): +class HPowGate(eigen_gate.EigenGate): """A Gate that performs a rotation around the X+Z axis of the Bloch sphere. The unitary matrix of ``HPowGate(exponent=t)`` is: @@ -716,6 +725,9 @@ def _eigen_components(self) -> List[Tuple[float, np.ndarray]]: return [(0, component0), (1, component1)] + def _num_qubits_(self) -> int: + return 1 + def _trace_distance_bound_(self) -> Optional[float]: if self._is_parameterized_(): return None diff --git a/cirq-core/cirq/ops/common_gates_test.py b/cirq-core/cirq/ops/common_gates_test.py index 803fa43e17d..d2fcb6f5de9 100644 --- a/cirq-core/cirq/ops/common_gates_test.py +++ b/cirq-core/cirq/ops/common_gates_test.py @@ -322,14 +322,14 @@ def test_x_act_on_tableau(): cirq.act_on(cirq.X**foo, args, [cirq.LineQubit(1)]) -class iZGate(cirq.SingleQubitGate): +class iZGate(cirq.testing.SingleQubitGate): """Equivalent to an iZ gate without _act_on_ defined on it.""" def _unitary_(self): return np.array([[1j, 0], [0, -1j]]) -class MinusOnePhaseGate(cirq.SingleQubitGate): +class MinusOnePhaseGate(cirq.testing.SingleQubitGate): """Equivalent to a -1 global phase without _act_on_ defined on it.""" def _unitary_(self): diff --git a/cirq-core/cirq/ops/controlled_gate_test.py b/cirq-core/cirq/ops/controlled_gate_test.py index d436c6f01a4..b8686ee7a9d 100644 --- a/cirq-core/cirq/ops/controlled_gate_test.py +++ b/cirq-core/cirq/ops/controlled_gate_test.py @@ -22,7 +22,7 @@ from cirq.type_workarounds import NotImplementedType -class GateUsingWorkspaceForApplyUnitary(cirq.SingleQubitGate): +class GateUsingWorkspaceForApplyUnitary(cirq.testing.SingleQubitGate): def _apply_unitary_(self, args: cirq.ApplyUnitaryArgs) -> Union[np.ndarray, NotImplementedType]: args.available_buffer[...] = args.target_tensor args.target_tensor[...] = 0 @@ -38,7 +38,7 @@ def __repr__(self): return 'cirq.ops.controlled_gate_test.GateUsingWorkspaceForApplyUnitary()' -class GateAllocatingNewSpaceForResult(cirq.SingleQubitGate): +class GateAllocatingNewSpaceForResult(cirq.testing.SingleQubitGate): def __init__(self): self._matrix = cirq.testing.random_unitary(2, random_state=4321) @@ -69,7 +69,7 @@ def __repr__(self): return 'cirq.ops.controlled_gate_test.GateAllocatingNewSpaceForResult()' -class RestrictedGate(cirq.SingleQubitGate): +class RestrictedGate(cirq.testing.SingleQubitGate): def __str__(self): return 'Restricted' @@ -247,7 +247,7 @@ def test_eq(): def test_control(): - g = cirq.SingleQubitGate() + g = cirq.testing.SingleQubitGate() # Ignores empty. assert g.controlled() == cirq.ControlledGate(g) @@ -423,7 +423,7 @@ def test_reversible(): ) -class UnphaseableGate(cirq.SingleQubitGate): +class UnphaseableGate(cirq.Gate): pass @@ -458,7 +458,7 @@ def test_circuit_diagram_info(): wire_symbols=('@', 'S'), exponent=1 ) - class UndiagrammableGate(cirq.SingleQubitGate): + class UndiagrammableGate(cirq.testing.SingleQubitGate): pass assert ( diff --git a/cirq-core/cirq/ops/controlled_operation_test.py b/cirq-core/cirq/ops/controlled_operation_test.py index ee9eb62adf8..33849c99804 100644 --- a/cirq-core/cirq/ops/controlled_operation_test.py +++ b/cirq-core/cirq/ops/controlled_operation_test.py @@ -23,7 +23,7 @@ from cirq.type_workarounds import NotImplementedType -class GateUsingWorkspaceForApplyUnitary(cirq.SingleQubitGate): +class GateUsingWorkspaceForApplyUnitary(cirq.testing.SingleQubitGate): def _apply_unitary_(self, args: cirq.ApplyUnitaryArgs) -> Union[np.ndarray, NotImplementedType]: args.available_buffer[...] = args.target_tensor args.target_tensor[...] = 0 @@ -39,7 +39,7 @@ def __repr__(self): return 'cirq.ops.controlled_operation_test.GateUsingWorkspaceForApplyUnitary()' -class GateAllocatingNewSpaceForResult(cirq.SingleQubitGate): +class GateAllocatingNewSpaceForResult(cirq.testing.SingleQubitGate): def __init__(self): self._matrix = cirq.testing.random_unitary(2, random_state=1234) @@ -73,7 +73,7 @@ def __repr__(self): def test_controlled_operation_init(): cb = cirq.NamedQubit('ctr') q = cirq.NamedQubit('q') - g = cirq.SingleQubitGate() + g = cirq.testing.SingleQubitGate() v = cirq.GateOperation(g, (q,)) c = cirq.ControlledOperation([cb], v) assert c.sub_operation == v @@ -296,7 +296,7 @@ def test_uninformed_circuit_diagram_info(): def test_non_diagrammable_subop(): qbits = cirq.LineQubit.range(2) - class UndiagrammableGate(cirq.SingleQubitGate): + class UndiagrammableGate(cirq.testing.SingleQubitGate): pass undiagrammable_op = UndiagrammableGate()(qbits[1]) diff --git a/cirq-core/cirq/ops/eigen_gate_test.py b/cirq-core/cirq/ops/eigen_gate_test.py index 60a95e3de58..ecd59ee5289 100644 --- a/cirq-core/cirq/ops/eigen_gate_test.py +++ b/cirq-core/cirq/ops/eigen_gate_test.py @@ -294,7 +294,7 @@ def test_resolve_parameters(resolve_fn): def test_diagram_period(): - class ShiftyGate(cirq.EigenGate, cirq.SingleQubitGate): + class ShiftyGate(cirq.EigenGate, cirq.testing.SingleQubitGate): def _eigen_components(self) -> List[Tuple[float, np.ndarray]]: raise NotImplementedError() @@ -327,7 +327,7 @@ def _eigen_shifts(self): assert ShiftyGate(505.2, 0, np.pi, np.e)._diagram_exponent(args) == 505.2 -class WeightedZPowGate(cirq.EigenGate, cirq.SingleQubitGate): +class WeightedZPowGate(cirq.EigenGate, cirq.testing.SingleQubitGate): def __init__(self, weight, **kwargs): self.weight = weight super().__init__(**kwargs) diff --git a/cirq-core/cirq/ops/gate_features.py b/cirq-core/cirq/ops/gate_features.py index 70c3150bf6d..77dc9faecf7 100644 --- a/cirq-core/cirq/ops/gate_features.py +++ b/cirq-core/cirq/ops/gate_features.py @@ -18,7 +18,10 @@ """ import abc +import warnings +from cirq import value +from cirq._compat import deprecated_class from cirq.ops import raw_types @@ -30,7 +33,18 @@ def qubit_index_to_equivalence_group_key(self, index: int) -> int: return 0 -class SingleQubitGate(raw_types.Gate, metaclass=abc.ABCMeta): +class _SingleQubitGateMeta(value.ABCMetaImplementAnyOneOf): + def __instancecheck__(cls, instance): + warnings.warn( + 'isinstance(gate, SingleQubitGate) is deprecated. ' + 'Use cirq.num_qubits(gate) == 1 instead', + DeprecationWarning, + ) + return isinstance(instance, raw_types.Gate) and instance._num_qubits_() == 1 + + +@deprecated_class(deadline='v1.0', fix='Define _num_qubits_ manually.') +class SingleQubitGate(raw_types.Gate, metaclass=_SingleQubitGateMeta): """A gate that must be applied to exactly one qubit.""" def _num_qubits_(self) -> int: diff --git a/cirq-core/cirq/ops/gate_features_test.py b/cirq-core/cirq/ops/gate_features_test.py index cdc0c5aa250..7f91a16865a 100644 --- a/cirq-core/cirq/ops/gate_features_test.py +++ b/cirq-core/cirq/ops/gate_features_test.py @@ -15,6 +15,7 @@ import pytest import cirq +from cirq.testing import assert_deprecated def test_single_qubit_gate_validate_args(): @@ -22,7 +23,11 @@ class Dummy(cirq.SingleQubitGate): def matrix(self): pass - g = Dummy() + with assert_deprecated(deadline="v1.0"): + g = Dummy() + + with assert_deprecated(deadline="isinstance(gate, SingleQubitGate) is deprecated"): + assert isinstance(g, cirq.SingleQubitGate) q1 = cirq.NamedQubit('q1') q2 = cirq.NamedQubit('q2') @@ -36,10 +41,13 @@ def matrix(self): def test_single_qubit_gate_validates_on_each(): - class Dummy(cirq.SingleQubitGate): + class Dummy(cirq.Gate): def matrix(self): pass + def _num_qubits_(self) -> int: + return 1 + g = Dummy() assert g.num_qubits() == 1 @@ -56,10 +64,13 @@ def matrix(self): def test_single_qubit_validates_on(): - class Dummy(cirq.SingleQubitGate): + class Dummy(cirq.Gate): def matrix(self): pass + def _num_qubits_(self) -> int: + return 1 + g = Dummy() assert g.num_qubits() == 1 @@ -108,7 +119,7 @@ def test_qasm_output_args_format(): def test_multi_qubit_gate_validate(): class Dummy(cirq.Gate): - def num_qubits(self) -> int: + def _num_qubits_(self) -> int: return self._num_qubits def __init__(self, num_qubits): diff --git a/cirq-core/cirq/ops/gate_operation_test.py b/cirq-core/cirq/ops/gate_operation_test.py index 3f2dd87073d..7f4c4b72351 100644 --- a/cirq-core/cirq/ops/gate_operation_test.py +++ b/cirq-core/cirq/ops/gate_operation_test.py @@ -20,7 +20,7 @@ def test_gate_operation_init(): q = cirq.NamedQubit('q') - g = cirq.SingleQubitGate() + g = cirq.testing.SingleQubitGate() v = cirq.GateOperation(g, (q,)) assert v.gate == g assert v.qubits == (q,) @@ -45,8 +45,8 @@ def test_immutable(): def test_gate_operation_eq(): - g1 = cirq.SingleQubitGate() - g2 = cirq.SingleQubitGate() + g1 = cirq.testing.SingleQubitGate() + g2 = cirq.testing.SingleQubitGate() g3 = cirq.testing.TwoQubitGate() r1 = [cirq.NamedQubit('r1')] r2 = [cirq.NamedQubit('r2')] @@ -157,7 +157,7 @@ def test_extrapolate(): q = cirq.NamedQubit('q') # If the gate isn't extrapolatable, you get a type error. - op0 = cirq.GateOperation(cirq.SingleQubitGate(), [q]) + op0 = cirq.GateOperation(cirq.testing.SingleQubitGate(), [q]) with pytest.raises(TypeError): _ = op0**0.5 @@ -170,7 +170,7 @@ def test_inverse(): q = cirq.NamedQubit('q') # If the gate isn't reversible, you get a type error. - op0 = cirq.GateOperation(cirq.SingleQubitGate(), [q]) + op0 = cirq.GateOperation(cirq.testing.SingleQubitGate(), [q]) assert cirq.inverse(op0, None) is None op1 = cirq.GateOperation(cirq.S, [q]) @@ -182,7 +182,7 @@ def test_text_diagrammable(): q = cirq.NamedQubit('q') # If the gate isn't diagrammable, you get a type error. - op0 = cirq.GateOperation(cirq.SingleQubitGate(), [q]) + op0 = cirq.GateOperation(cirq.testing.SingleQubitGate(), [q]) with pytest.raises(TypeError): _ = cirq.circuit_diagram_info(op0) @@ -196,7 +196,7 @@ def test_bounded_effect(): q = cirq.NamedQubit('q') # If the gate isn't bounded, you get a type error. - op0 = cirq.GateOperation(cirq.SingleQubitGate(), [q]) + op0 = cirq.GateOperation(cirq.testing.SingleQubitGate(), [q]) assert cirq.trace_distance_bound(op0) >= 1 op1 = cirq.GateOperation(cirq.Z**0.000001, [q]) op1_bound = cirq.trace_distance_bound(op1) @@ -253,8 +253,8 @@ def test_channel(): np.testing.assert_allclose(cirq.kraus(op), cirq.kraus(op.gate)) assert cirq.has_kraus(op) - assert cirq.kraus(cirq.SingleQubitGate()(a), None) is None - assert not cirq.has_kraus(cirq.SingleQubitGate()(a)) + assert cirq.kraus(cirq.testing.SingleQubitGate()(a), None) is None + assert not cirq.has_kraus(cirq.testing.SingleQubitGate()(a)) def test_measurement_key(): @@ -288,7 +288,7 @@ def test_repr(): repr(cirq.GateOperation(cirq.CZ, (a, b))) == 'cirq.CZ(cirq.LineQubit(0), cirq.LineQubit(1))' ) - class Inconsistent(cirq.SingleQubitGate): + class Inconsistent(cirq.testing.SingleQubitGate): def __repr__(self): return 'Inconsistent' @@ -422,7 +422,7 @@ def test_cannot_remap_non_measurement_gate(): def test_is_parameterized(): - class No1(cirq.Gate): + class No1(cirq.testing.SingleQubitGate): def num_qubits(self) -> int: return 1 diff --git a/cirq-core/cirq/ops/gateset_test.py b/cirq-core/cirq/ops/gateset_test.py index c050d62e5f5..7427ed35ae5 100644 --- a/cirq-core/cirq/ops/gateset_test.py +++ b/cirq-core/cirq/ops/gateset_test.py @@ -126,7 +126,7 @@ def test_gate_family_eq(): (CustomX**0.5, True), (CustomX ** sympy.Symbol('theta'), True), (CustomXPowGate(exponent=0.25, global_shift=0.15), True), - (cirq.SingleQubitGate(), False), + (cirq.testing.SingleQubitGate(), False), (cirq.X**0.5, False), (None, False), (cirq.global_phase_operation(1j), False), diff --git a/cirq-core/cirq/ops/identity_test.py b/cirq-core/cirq/ops/identity_test.py index e9a7892d280..c782cd39228 100644 --- a/cirq-core/cirq/ops/identity_test.py +++ b/cirq-core/cirq/ops/identity_test.py @@ -178,7 +178,7 @@ def test_identity_global(): def test_identity_mul(): - class UnknownGate(cirq.SingleQubitGate): + class UnknownGate(cirq.testing.SingleQubitGate): pass class UnknownOperation(cirq.Operation): diff --git a/cirq-core/cirq/ops/op_tree_test.py b/cirq-core/cirq/ops/op_tree_test.py index 60609e356c2..f2ccc87dfde 100644 --- a/cirq-core/cirq/ops/op_tree_test.py +++ b/cirq-core/cirq/ops/op_tree_test.py @@ -20,7 +20,8 @@ def test_flatten_op_tree(): operations = [ - cirq.GateOperation(cirq.SingleQubitGate(), [cirq.NamedQubit(str(i))]) for i in range(10) + cirq.GateOperation(cirq.testing.SingleQubitGate(), [cirq.NamedQubit(str(i))]) + for i in range(10) ] # Empty tree. @@ -54,7 +55,8 @@ def test_flatten_op_tree(): def test_flatten_to_ops_or_moments(): operations = [ - cirq.GateOperation(cirq.SingleQubitGate(), [cirq.NamedQubit(str(i))]) for i in range(10) + cirq.GateOperation(cirq.testing.SingleQubitGate(), [cirq.NamedQubit(str(i))]) + for i in range(10) ] op_tree = [operations[0], cirq.Moment(operations[1:5]), operations[5:]] output = [operations[0], cirq.Moment(operations[1:5])] + operations[5:] @@ -72,7 +74,8 @@ def test_flatten_to_ops_or_moments(): def test_freeze_op_tree(): operations = [ - cirq.GateOperation(cirq.SingleQubitGate(), [cirq.NamedQubit(str(i))]) for i in range(10) + cirq.GateOperation(cirq.testing.SingleQubitGate(), [cirq.NamedQubit(str(i))]) + for i in range(10) ] # Empty tree. @@ -114,7 +117,7 @@ def test_transform_bad_tree(): def test_transform_leaves(): - gs = [cirq.SingleQubitGate() for _ in range(10)] + gs = [cirq.testing.SingleQubitGate() for _ in range(10)] operations = [cirq.GateOperation(gs[i], [cirq.NamedQubit(str(i))]) for i in range(10)] expected = [cirq.GateOperation(gs[i], [cirq.NamedQubit(str(i) + 'a')]) for i in range(10)] @@ -145,7 +148,8 @@ def move_tree_left_freeze(root): def test_transform_internal_nodes(): operations = [ - cirq.GateOperation(cirq.SingleQubitGate(), [cirq.LineQubit(2 * i)]) for i in range(10) + cirq.GateOperation(cirq.testing.SingleQubitGate(), [cirq.LineQubit(2 * i)]) + for i in range(10) ] def skip_first(op): diff --git a/cirq-core/cirq/ops/parallel_gate_test.py b/cirq-core/cirq/ops/parallel_gate_test.py index 1e8255c78e4..dc2eb70601b 100644 --- a/cirq-core/cirq/ops/parallel_gate_test.py +++ b/cirq-core/cirq/ops/parallel_gate_test.py @@ -21,7 +21,7 @@ @pytest.mark.parametrize( 'gate, num_copies, qubits', [ - (cirq.SingleQubitGate(), 2, cirq.LineQubit.range(2)), + (cirq.testing.SingleQubitGate(), 2, cirq.LineQubit.range(2)), (cirq.X**0.5, 4, cirq.LineQubit.range(4)), ], ) @@ -35,9 +35,19 @@ def test_parallel_gate_operation_init(gate, num_copies, qubits): @pytest.mark.parametrize( 'gate, num_copies, qubits, error_msg', [ - (cirq.SingleQubitGate(), 3, cirq.LineQubit.range(2), "Wrong number of qubits"), - (cirq.SingleQubitGate(), 0, cirq.LineQubit.range(4), "gate must be applied at least once"), - (cirq.SingleQubitGate(), 2, [cirq.NamedQubit("a"), cirq.NamedQubit("a")], "Duplicate"), + (cirq.testing.SingleQubitGate(), 3, cirq.LineQubit.range(2), "Wrong number of qubits"), + ( + cirq.testing.SingleQubitGate(), + 0, + cirq.LineQubit.range(4), + "gate must be applied at least once", + ), + ( + cirq.testing.SingleQubitGate(), + 2, + [cirq.NamedQubit("a"), cirq.NamedQubit("a")], + "Duplicate", + ), (cirq.testing.TwoQubitGate(), 2, cirq.LineQubit.range(4), "must be a single qubit gate"), ], ) @@ -65,14 +75,14 @@ def test_decompose_raises(): def test_with_num_copies(): - g = cirq.SingleQubitGate() + g = cirq.testing.SingleQubitGate() pg = cirq.ParallelGate(g, 3) assert pg.with_num_copies(5) == cirq.ParallelGate(g, 5) def test_extrapolate(): # If the gate isn't extrapolatable, you get a type error. - g = cirq.ParallelGate(cirq.SingleQubitGate(), 2) + g = cirq.ParallelGate(cirq.testing.SingleQubitGate(), 2) with pytest.raises(TypeError): _ = g**0.5 # If the gate is extrapolatable, the effect is applied on the underlying gate. @@ -90,7 +100,7 @@ def test_parameterizable_gates(resolve_fn): assert not cirq.is_parameterized(g2) -@pytest.mark.parametrize('gate', [cirq.X ** sympy.Symbol("a"), cirq.SingleQubitGate()]) +@pytest.mark.parametrize('gate', [cirq.X ** sympy.Symbol("a"), cirq.testing.SingleQubitGate()]) def test_no_unitary(gate): g = cirq.ParallelGate(gate, 2) assert not cirq.has_unitary(g) @@ -115,10 +125,10 @@ def test_unitary(gate, num_copies, qubits): def test_not_implemented_diagram(): q = cirq.LineQubit.range(2) - g = cirq.SingleQubitGate() + g = cirq.testing.SingleQubitGate() c = cirq.Circuit() c.append(cirq.ParallelGate(g, 2)(*q)) - assert 'cirq.ops.gate_features.SingleQubitGate' in str(c) + assert 'cirq.testing.gate_features.SingleQubitGate ' in str(c) def test_repr(): diff --git a/cirq-core/cirq/ops/pauli_string_test.py b/cirq-core/cirq/ops/pauli_string_test.py index 659aa45750c..d372dcebe82 100644 --- a/cirq-core/cirq/ops/pauli_string_test.py +++ b/cirq-core/cirq/ops/pauli_string_test.py @@ -709,7 +709,7 @@ def test_pass_operations_over_cz(): def test_pass_operations_over_no_common_qubits(): - class DummyGate(cirq.SingleQubitGate): + class DummyGate(cirq.testing.SingleQubitGate): pass q0, q1 = _make_qubits(2) diff --git a/cirq-core/cirq/ops/phased_x_gate.py b/cirq-core/cirq/ops/phased_x_gate.py index ef17dfb583c..fa6723793a0 100644 --- a/cirq-core/cirq/ops/phased_x_gate.py +++ b/cirq-core/cirq/ops/phased_x_gate.py @@ -21,12 +21,12 @@ import cirq from cirq import value, protocols from cirq._compat import proper_repr -from cirq.ops import gate_features, common_gates +from cirq.ops import common_gates, raw_types from cirq.type_workarounds import NotImplementedType @value.value_equality(manual_cls=True, approximate=True) -class PhasedXPowGate(gate_features.SingleQubitGate): +class PhasedXPowGate(raw_types.Gate): """A gate equivalent to the circuit ───Z^-p───X^t───Z^p───.""" def __init__( @@ -124,6 +124,9 @@ def _unitary_(self) -> Union[np.ndarray, NotImplementedType]: p = np.exp(1j * np.pi * self._global_shift * self._exponent) return np.dot(np.dot(z, x), np.conj(z)) * p + def _num_qubits_(self) -> int: + return 1 + def _pauli_expansion_(self) -> value.LinearDict[str]: if self._is_parameterized_(): return NotImplemented diff --git a/cirq-core/cirq/ops/phased_x_z_gate.py b/cirq-core/cirq/ops/phased_x_z_gate.py index 053e3bffddb..300d6229d19 100644 --- a/cirq-core/cirq/ops/phased_x_z_gate.py +++ b/cirq-core/cirq/ops/phased_x_z_gate.py @@ -6,7 +6,7 @@ import sympy from cirq import value, ops, protocols, linalg -from cirq.ops import gate_features +from cirq.ops import raw_types from cirq._compat import proper_repr if TYPE_CHECKING: @@ -14,7 +14,7 @@ @value.value_equality(approximate=True) -class PhasedXZGate(gate_features.SingleQubitGate): +class PhasedXZGate(raw_types.Gate): """A single qubit operation expressed as $Z^z Z^a X^x Z^{-a}$. The above expression is a matrix multiplication with time going to the left. @@ -138,6 +138,9 @@ def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optio ) return protocols.qasm(qasm_gate, args=args, qubits=qubits) + def _num_qubits_(self) -> int: + return 1 + def _has_unitary_(self) -> bool: return not self._is_parameterized_() diff --git a/cirq-core/cirq/ops/raw_types_test.py b/cirq-core/cirq/ops/raw_types_test.py index daec1f3c0d8..92e1f6ee65d 100644 --- a/cirq-core/cirq/ops/raw_types_test.py +++ b/cirq-core/cirq/ops/raw_types_test.py @@ -765,7 +765,7 @@ def qubits(self): def test_single_qubit_gate_validates_on_each(): - class Dummy(cirq.SingleQubitGate): + class Dummy(cirq.testing.SingleQubitGate): def matrix(self): pass @@ -785,7 +785,7 @@ def matrix(self): def test_on_each(): - class CustomGate(cirq.SingleQubitGate): + class CustomGate(cirq.testing.SingleQubitGate): pass a = cirq.NamedQubit('a') diff --git a/cirq-core/cirq/optimizers/convert_to_cz_and_single_gates_test.py b/cirq-core/cirq/optimizers/convert_to_cz_and_single_gates_test.py index 58d53be2dbd..2c8f9f7e6ef 100644 --- a/cirq-core/cirq/optimizers/convert_to_cz_and_single_gates_test.py +++ b/cirq-core/cirq/optimizers/convert_to_cz_and_single_gates_test.py @@ -63,7 +63,7 @@ def test_kak_decomposes_unknown_two_qubit_gate(): def test_composite_gates_without_matrix(): - class CompositeDummy(cirq.SingleQubitGate): + class CompositeDummy(cirq.testing.SingleQubitGate): def _decompose_(self, qubits): yield cirq.X(qubits[0]) yield cirq.Y(qubits[0]) ** 0.5 diff --git a/cirq-core/cirq/optimizers/eject_z_test.py b/cirq-core/cirq/optimizers/eject_z_test.py index 8cd7c84b920..c5d1f28309f 100644 --- a/cirq-core/cirq/optimizers/eject_z_test.py +++ b/cirq-core/cirq/optimizers/eject_z_test.py @@ -159,7 +159,7 @@ def test_measurement_consumes_zs(): def test_unphaseable_causes_earlier_merge_without_size_increase(): - class UnknownGate(cirq.SingleQubitGate): + class UnknownGate(cirq.testing.SingleQubitGate): pass u = UnknownGate() diff --git a/cirq-core/cirq/optimizers/expand_composite_test.py b/cirq-core/cirq/optimizers/expand_composite_test.py index 1dae9a06ebb..d367fa19a4a 100644 --- a/cirq-core/cirq/optimizers/expand_composite_test.py +++ b/cirq-core/cirq/optimizers/expand_composite_test.py @@ -105,7 +105,7 @@ def test_recursive_composite(): def test_decompose_returns_not_flat_op_tree(): - class DummyGate(cirq.SingleQubitGate): + class DummyGate(cirq.testing.SingleQubitGate): def _decompose_(self, qubits): (q0,) = qubits # Yield a tuple of gates instead of yielding a gate diff --git a/cirq-core/cirq/optimizers/merge_single_qubit_gates_test.py b/cirq-core/cirq/optimizers/merge_single_qubit_gates_test.py index ce1d8c86bcc..a3a9ecf8451 100644 --- a/cirq-core/cirq/optimizers/merge_single_qubit_gates_test.py +++ b/cirq-core/cirq/optimizers/merge_single_qubit_gates_test.py @@ -129,7 +129,7 @@ def test_ignores_2qubit_target(): def test_ignore_unsupported_gate(): - class UnsupportedDummy(cirq.SingleQubitGate): + class UnsupportedDummy(cirq.testing.SingleQubitGate): pass q0 = cirq.LineQubit(0) diff --git a/cirq-core/cirq/protocols/apply_unitary_protocol_test.py b/cirq-core/cirq/protocols/apply_unitary_protocol_test.py index f3304cdc4c2..ef675180569 100644 --- a/cirq-core/cirq/protocols/apply_unitary_protocol_test.py +++ b/cirq-core/cirq/protocols/apply_unitary_protocol_test.py @@ -325,14 +325,14 @@ def test_apply_unitaries(): def test_apply_unitaries_mixed_qid_shapes(): - class PlusOneMod3Gate(cirq.SingleQubitGate): + class PlusOneMod3Gate(cirq.testing.SingleQubitGate): def _qid_shape_(self): return (3,) def _unitary_(self): return np.array([[0, 0, 1], [1, 0, 0], [0, 1, 0]]) # yapf: disable - class PlusOneMod4Gate(cirq.SingleQubitGate): + class PlusOneMod4Gate(cirq.testing.SingleQubitGate): def _qid_shape_(self): return (4,) diff --git a/cirq-core/cirq/protocols/has_stabilizer_effect_protocol_test.py b/cirq-core/cirq/protocols/has_stabilizer_effect_protocol_test.py index 321b73359a1..d54d00db305 100644 --- a/cirq-core/cirq/protocols/has_stabilizer_effect_protocol_test.py +++ b/cirq-core/cirq/protocols/has_stabilizer_effect_protocol_test.py @@ -98,7 +98,7 @@ def _unitary_(self): def test_inconclusive(): assert not cirq.has_stabilizer_effect(object()) assert not cirq.has_stabilizer_effect('boo') - assert not cirq.has_stabilizer_effect(cirq.SingleQubitGate()) + assert not cirq.has_stabilizer_effect(cirq.testing.SingleQubitGate()) assert not cirq.has_stabilizer_effect(No()) assert not cirq.has_stabilizer_effect(NoOp()) diff --git a/cirq-core/cirq/protocols/has_unitary_protocol_test.py b/cirq-core/cirq/protocols/has_unitary_protocol_test.py index 20530cb92b9..a91d3d971be 100644 --- a/cirq-core/cirq/protocols/has_unitary_protocol_test.py +++ b/cirq-core/cirq/protocols/has_unitary_protocol_test.py @@ -74,7 +74,7 @@ class No2(EmptyOp): def _apply_unitary_(self, args): return NotImplemented - class No3(cirq.SingleQubitGate): + class No3(cirq.testing.SingleQubitGate): def _apply_unitary_(self, args): return NotImplemented @@ -86,7 +86,7 @@ class Yes1(EmptyOp): def _apply_unitary_(self, args): return args.target_tensor - class Yes2(cirq.SingleQubitGate): + class Yes2(cirq.testing.SingleQubitGate): def _apply_unitary_(self, args): return args.target_tensor diff --git a/cirq-core/cirq/protocols/kraus_protocol_test.py b/cirq-core/cirq/protocols/kraus_protocol_test.py index 4fc9fc90ea5..a9da792fa7d 100644 --- a/cirq-core/cirq/protocols/kraus_protocol_test.py +++ b/cirq-core/cirq/protocols/kraus_protocol_test.py @@ -137,22 +137,22 @@ def _unitary_(self) -> np.ndarray: assert cirq.has_kraus(ReturnsUnitary()) -class HasKraus(cirq.SingleQubitGate): +class HasKraus(cirq.testing.SingleQubitGate): def _has_kraus_(self) -> bool: return True -class HasMixture(cirq.SingleQubitGate): +class HasMixture(cirq.testing.SingleQubitGate): def _has_mixture_(self) -> bool: return True -class HasUnitary(cirq.SingleQubitGate): +class HasUnitary(cirq.testing.SingleQubitGate): def _has_unitary_(self) -> bool: return True -class HasKrausWhenDecomposed(cirq.SingleQubitGate): +class HasKrausWhenDecomposed(cirq.testing.SingleQubitGate): def __init__(self, decomposed_cls): self.decomposed_cls = decomposed_cls diff --git a/cirq-core/cirq/sim/clifford/act_on_clifford_tableau_args_test.py b/cirq-core/cirq/sim/clifford/act_on_clifford_tableau_args_test.py index 29157766bc5..8135841841e 100644 --- a/cirq-core/cirq/sim/clifford/act_on_clifford_tableau_args_test.py +++ b/cirq-core/cirq/sim/clifford/act_on_clifford_tableau_args_test.py @@ -21,10 +21,7 @@ def test_unitary_fallback(): - class UnitaryXGate(cirq.Gate): - def _num_qubits_(self) -> int: - return 1 - + class UnitaryXGate(cirq.testing.SingleQubitGate): def _unitary_(self): return np.array([[0, 1], [1, 0]]) @@ -64,7 +61,7 @@ def test_cannot_act(): class NoDetails: pass - class NoDetailsSingleQubitGate(cirq.SingleQubitGate): + class NoDetailsSingleQubitGate(cirq.testing.SingleQubitGate): pass args = cirq.ActOnCliffordTableauArgs( diff --git a/cirq-core/cirq/sim/clifford/act_on_stabilizer_ch_form_args_test.py b/cirq-core/cirq/sim/clifford/act_on_stabilizer_ch_form_args_test.py index 2e655944a2a..32d6d7aa922 100644 --- a/cirq-core/cirq/sim/clifford/act_on_stabilizer_ch_form_args_test.py +++ b/cirq-core/cirq/sim/clifford/act_on_stabilizer_ch_form_args_test.py @@ -26,7 +26,7 @@ def test_init_state(): def test_cannot_act(): - class NoDetails(cirq.SingleQubitGate): + class NoDetails(cirq.testing.SingleQubitGate): pass args = cirq.ActOnStabilizerCHFormArgs(qubits=[], prng=np.random.RandomState()) @@ -36,7 +36,7 @@ class NoDetails(cirq.SingleQubitGate): def test_gate_with_act_on(): - class CustomGate(cirq.SingleQubitGate): + class CustomGate(cirq.testing.SingleQubitGate): def _act_on_(self, args, qubits): if isinstance(args, cirq.ActOnStabilizerCHFormArgs): qubit = args.qubit_map[qubits[0]] diff --git a/cirq-core/cirq/sim/sparse_simulator_test.py b/cirq-core/cirq/sim/sparse_simulator_test.py index 39b810c605a..cd962051148 100644 --- a/cirq-core/cirq/sim/sparse_simulator_test.py +++ b/cirq-core/cirq/sim/sparse_simulator_test.py @@ -693,7 +693,7 @@ def test_simulate_expectation_values_qubit_order(dtype: Type[np.number], split: def test_invalid_run_no_unitary(): - class NoUnitary(cirq.SingleQubitGate): + class NoUnitary(cirq.testing.SingleQubitGate): pass q0 = cirq.LineQubit(0) @@ -705,7 +705,7 @@ class NoUnitary(cirq.SingleQubitGate): def test_allocates_new_state(): - class NoUnitary(cirq.SingleQubitGate): + class NoUnitary(cirq.testing.SingleQubitGate): def _has_unitary_(self): return True @@ -726,7 +726,7 @@ def test_does_not_modify_initial_state(): q0 = cirq.LineQubit(0) simulator = cirq.Simulator() - class InPlaceUnitary(cirq.SingleQubitGate): + class InPlaceUnitary(cirq.testing.SingleQubitGate): def _has_unitary_(self): return True @@ -1275,7 +1275,7 @@ def test_separated_measurements(): def test_state_vector_copy(): sim = cirq.Simulator(split_untangled_states=False) - class InplaceGate(cirq.SingleQubitGate): + class InplaceGate(cirq.testing.SingleQubitGate): """A gate that modifies the target tensor in place, multiply by -1.""" def _apply_unitary_(self, args): diff --git a/cirq-core/cirq/testing/__init__.py b/cirq-core/cirq/testing/__init__.py index 5f2a3e20d98..9788ad1b98b 100644 --- a/cirq-core/cirq/testing/__init__.py +++ b/cirq-core/cirq/testing/__init__.py @@ -62,7 +62,7 @@ from cirq.testing.equivalent_repr_eval import assert_equivalent_repr -from cirq.testing.gate_features import TwoQubitGate, ThreeQubitGate +from cirq.testing.gate_features import SingleQubitGate, TwoQubitGate, ThreeQubitGate from cirq.testing.json import assert_json_roundtrip_works diff --git a/cirq-core/cirq/testing/consistent_act_on_test.py b/cirq-core/cirq/testing/consistent_act_on_test.py index 0e5500c0e12..34be8031b95 100644 --- a/cirq-core/cirq/testing/consistent_act_on_test.py +++ b/cirq-core/cirq/testing/consistent_act_on_test.py @@ -20,7 +20,7 @@ import cirq -class GoodGate(cirq.SingleQubitGate): +class GoodGate(cirq.testing.SingleQubitGate): def _unitary_(self): return np.array([[0, 1], [1, 0]]) @@ -33,7 +33,7 @@ def _act_on_(self, args: 'cirq.OperationTarget', qubits: Sequence['cirq.Qid']): return NotImplemented -class BadGate(cirq.SingleQubitGate): +class BadGate(cirq.testing.SingleQubitGate): def _unitary_(self): return np.array([[0, 1j], [1, 0]]) diff --git a/cirq-core/cirq/testing/consistent_controlled_gate_op_test.py b/cirq-core/cirq/testing/consistent_controlled_gate_op_test.py index 1e5b2d3c421..381a96aa22e 100644 --- a/cirq-core/cirq/testing/consistent_controlled_gate_op_test.py +++ b/cirq-core/cirq/testing/consistent_controlled_gate_op_test.py @@ -21,7 +21,7 @@ import cirq -class GoodGate(cirq.EigenGate, cirq.SingleQubitGate): +class GoodGate(cirq.EigenGate, cirq.testing.SingleQubitGate): def _eigen_components(self) -> List[Tuple[float, np.ndarray]]: # coverage: ignore return [(0, np.diag([1, 0])), (1, np.diag([0, 1]))] @@ -36,7 +36,7 @@ def controlled_by( return cirq.ControlledOperation(control_qubits, self, control_values) -class BadGate(cirq.EigenGate, cirq.SingleQubitGate): +class BadGate(cirq.EigenGate, cirq.testing.SingleQubitGate): def _eigen_components(self) -> List[Tuple[float, np.ndarray]]: # coverage: ignore return [(0, np.diag([1, 0])), (1, np.diag([0, 1]))] diff --git a/cirq-core/cirq/testing/consistent_decomposition_test.py b/cirq-core/cirq/testing/consistent_decomposition_test.py index 002ed0668fd..88c98ec437b 100644 --- a/cirq-core/cirq/testing/consistent_decomposition_test.py +++ b/cirq-core/cirq/testing/consistent_decomposition_test.py @@ -20,7 +20,7 @@ import cirq -class GoodGateDecompose(cirq.SingleQubitGate): +class GoodGateDecompose(cirq.testing.SingleQubitGate): def _decompose_(self, qubits): return cirq.X(qubits[0]) @@ -28,7 +28,7 @@ def _unitary_(self): return np.array([[0, 1], [1, 0]]) -class BadGateDecompose(cirq.SingleQubitGate): +class BadGateDecompose(cirq.testing.SingleQubitGate): def _decompose_(self, qubits): return cirq.Y(qubits[0]) @@ -68,12 +68,12 @@ def _decompose_(self, qubits): yield GateDecomposeNotImplemented().on_each(*qubits) -class GateDecomposeNotImplemented(cirq.SingleQubitGate): +class GateDecomposeNotImplemented(cirq.testing.SingleQubitGate): def _decompose_(self, qubits): return NotImplemented -class ParameterizedGate(cirq.SingleQubitGate): +class ParameterizedGate(cirq.Gate): def _num_qubits_(self): return 2 diff --git a/cirq-core/cirq/testing/consistent_pauli_expansion_test.py b/cirq-core/cirq/testing/consistent_pauli_expansion_test.py index 1e6d25ae115..d5b490b2424 100644 --- a/cirq-core/cirq/testing/consistent_pauli_expansion_test.py +++ b/cirq-core/cirq/testing/consistent_pauli_expansion_test.py @@ -22,7 +22,7 @@ Z = np.diag([1, -1]) -class GoodGateExplicitPauliExpansion(cirq.SingleQubitGate): +class GoodGateExplicitPauliExpansion(cirq.testing.SingleQubitGate): def _unitary_(self) -> np.ndarray: return np.sqrt(1 / 2) * X + np.sqrt(1 / 3) * Y + np.sqrt(1 / 6) * Z @@ -35,16 +35,16 @@ def num_qubits(self) -> int: return 4 -class GoodGateNoUnitary(cirq.SingleQubitGate): +class GoodGateNoUnitary(cirq.testing.SingleQubitGate): def _pauli_expansion_(self) -> cirq.LinearDict[str]: return cirq.LinearDict({'X': np.sqrt(1 / 2), 'Y': np.sqrt(1 / 2)}) -class GoodGateNoPauliExpansionNoUnitary(cirq.SingleQubitGate): +class GoodGateNoPauliExpansionNoUnitary(cirq.testing.SingleQubitGate): pass -class BadGateInconsistentPauliExpansion(cirq.SingleQubitGate): +class BadGateInconsistentPauliExpansion(cirq.testing.SingleQubitGate): def _unitary_(self) -> np.ndarray: return np.sqrt(1 / 2) * X + np.sqrt(1 / 3) * Y + np.sqrt(1 / 6) * Z diff --git a/cirq-core/cirq/testing/consistent_protocols_test.py b/cirq-core/cirq/testing/consistent_protocols_test.py index 893a79e51bd..8b9e275dfb8 100644 --- a/cirq-core/cirq/testing/consistent_protocols_test.py +++ b/cirq-core/cirq/testing/consistent_protocols_test.py @@ -25,7 +25,7 @@ import cirq.testing.consistent_controlled_gate_op_test as controlled_gate_op_test -class GoodGate(cirq.SingleQubitGate): +class GoodGate(cirq.testing.SingleQubitGate): def __init__( self, *, @@ -184,7 +184,7 @@ def __repr__(self): return f"BadGateRepr({', '.join(args)})" -class GoodEigenGate(cirq.EigenGate, cirq.SingleQubitGate): +class GoodEigenGate(cirq.EigenGate, cirq.testing.SingleQubitGate): def _eigen_components(self) -> List[Tuple[float, np.ndarray]]: return [(0, np.diag([1, 0])), (1, np.diag([0, 1]))] diff --git a/cirq-core/cirq/testing/gate_features.py b/cirq-core/cirq/testing/gate_features.py index ff0561c7dec..4e95b4d342c 100644 --- a/cirq-core/cirq/testing/gate_features.py +++ b/cirq-core/cirq/testing/gate_features.py @@ -17,6 +17,13 @@ from cirq.ops import raw_types +class SingleQubitGate(raw_types.Gate): + """A gate that must be applied to exactly one qubit.""" + + def _num_qubits_(self) -> int: + return 1 + + class TwoQubitGate(raw_types.Gate): """A gate that must be applied to exactly two qubits.""" diff --git a/cirq-core/cirq/transformers/analytical_decompositions/single_qubit_decompositions.py b/cirq-core/cirq/transformers/analytical_decompositions/single_qubit_decompositions.py index 289da0f959a..7c101555cbb 100644 --- a/cirq-core/cirq/transformers/analytical_decompositions/single_qubit_decompositions.py +++ b/cirq-core/cirq/transformers/analytical_decompositions/single_qubit_decompositions.py @@ -97,9 +97,7 @@ def is_no_turn(half_turns): return [(pauli, ht) for pauli, ht in rotation_list if not is_no_turn(ht)] -def single_qubit_matrix_to_gates( - mat: np.ndarray, tolerance: float = 0 -) -> List[ops.SingleQubitGate]: +def single_qubit_matrix_to_gates(mat: np.ndarray, tolerance: float = 0) -> List[ops.Gate]: """Implements a single-qubit operation with few gates. Args: @@ -166,9 +164,7 @@ def _deconstruct_single_qubit_matrix_into_gate_turns(mat: np.ndarray) -> Tuple[f return (_signed_mod_1(xy_turn), _signed_mod_1(xy_phase_turn), _signed_mod_1(total_z_turn)) -def single_qubit_matrix_to_phased_x_z( - mat: np.ndarray, atol: float = 0 -) -> List[ops.SingleQubitGate]: +def single_qubit_matrix_to_phased_x_z(mat: np.ndarray, atol: float = 0) -> List[ops.Gate]: """Implements a single-qubit operation with a PhasedX and Z gate. If one of the gates isn't needed, it will be omitted. diff --git a/cirq-core/cirq/transformers/analytical_decompositions/single_qubit_decompositions_test.py b/cirq-core/cirq/transformers/analytical_decompositions/single_qubit_decompositions_test.py index 9f82ae8c410..8ba7490cd3d 100644 --- a/cirq-core/cirq/transformers/analytical_decompositions/single_qubit_decompositions_test.py +++ b/cirq-core/cirq/transformers/analytical_decompositions/single_qubit_decompositions_test.py @@ -33,7 +33,7 @@ def test_deprecated_submodule(): def assert_gates_implement_unitary( - gates: Sequence[cirq.SingleQubitGate], intended_effect: np.ndarray, atol: float + gates: Sequence[cirq.testing.SingleQubitGate], intended_effect: np.ndarray, atol: float ): actual_effect = cirq.dot(*[cirq.unitary(g) for g in reversed(gates)]) cirq.testing.assert_allclose_up_to_global_phase(actual_effect, intended_effect, atol=atol) diff --git a/cirq-core/cirq/transformers/eject_z_test.py b/cirq-core/cirq/transformers/eject_z_test.py index 45059304b3b..623010a1e75 100644 --- a/cirq-core/cirq/transformers/eject_z_test.py +++ b/cirq-core/cirq/transformers/eject_z_test.py @@ -187,7 +187,7 @@ def test_measurement_consumes_zs(): def test_unphaseable_causes_earlier_merge_without_size_increase(): - class UnknownGate(cirq.SingleQubitGate): + class UnknownGate(cirq.testing.SingleQubitGate): pass u = UnknownGate() diff --git a/cirq-core/cirq/transformers/expand_composite_test.py b/cirq-core/cirq/transformers/expand_composite_test.py index dcbe31120d4..5eb145ecf62 100644 --- a/cirq-core/cirq/transformers/expand_composite_test.py +++ b/cirq-core/cirq/transformers/expand_composite_test.py @@ -105,7 +105,7 @@ def test_recursive_composite(): def test_decompose_returns_not_flat_op_tree(): - class DummyGate(cirq.SingleQubitGate): + class DummyGate(cirq.testing.SingleQubitGate): def _decompose_(self, qubits): (q0,) = qubits # Yield a tuple of gates instead of yielding a gate diff --git a/cirq-core/cirq/transformers/merge_k_qubit_gates_test.py b/cirq-core/cirq/transformers/merge_k_qubit_gates_test.py index 9f475326060..0803579ce7d 100644 --- a/cirq-core/cirq/transformers/merge_k_qubit_gates_test.py +++ b/cirq-core/cirq/transformers/merge_k_qubit_gates_test.py @@ -74,9 +74,8 @@ def test_ignores_2qubit_target(): def test_ignore_unsupported_gate(): - class UnsupportedDummy(cirq.Gate): - def _num_qubits_(self) -> int: - return 1 + class UnsupportedDummy(cirq.testing.SingleQubitGate): + pass c = cirq.Circuit(UnsupportedDummy()(cirq.LineQubit(0))) assert_optimizes(optimized=cirq.merge_k_qubit_unitaries(c, k=1), expected=c) diff --git a/cirq-core/cirq/transformers/target_gatesets/cz_gateset_test.py b/cirq-core/cirq/transformers/target_gatesets/cz_gateset_test.py index 3f3964d9385..40952cd895b 100644 --- a/cirq-core/cirq/transformers/target_gatesets/cz_gateset_test.py +++ b/cirq-core/cirq/transformers/target_gatesets/cz_gateset_test.py @@ -245,7 +245,7 @@ def _decompose_(self, qubits): def test_composite_gates_without_matrix(): - class CompositeDummy(cirq.SingleQubitGate): + class CompositeDummy(cirq.testing.SingleQubitGate): def _decompose_(self, qubits): yield cirq.X(qubits[0]) yield cirq.Y(qubits[0]) ** 0.5 diff --git a/cirq-google/cirq_google/calibration/engine_simulator.py b/cirq-google/cirq_google/calibration/engine_simulator.py index bc6708df676..69eba099950 100644 --- a/cirq-google/cirq_google/calibration/engine_simulator.py +++ b/cirq-google/cirq_google/calibration/engine_simulator.py @@ -472,10 +472,16 @@ def _convert_to_circuit_with_drift( simulator: PhasedFSimEngineSimulator, circuit: cirq.AbstractCircuit ) -> cirq.Circuit: def map_func(op: cirq.Operation, _) -> cirq.Operation: - if isinstance(op.gate, (cirq.MeasurementGate, cirq.SingleQubitGate, cirq.WaitGate)): - return op + if op.gate is None: raise IncompatibleMomentError(f'Operation {op} has a missing gate') + + if ( + isinstance(op.gate, (cirq.MeasurementGate, cirq.WaitGate)) + or cirq.num_qubits(op.gate) == 1 + ): + return op + translated = simulator.gates_translator(op.gate) if translated is None: raise IncompatibleMomentError( diff --git a/cirq-google/cirq_google/calibration/workflow.py b/cirq-google/cirq_google/calibration/workflow.py index fb793fa5f9a..e0882ebe0ad 100644 --- a/cirq-google/cirq_google/calibration/workflow.py +++ b/cirq-google/cirq_google/calibration/workflow.py @@ -39,7 +39,7 @@ from cirq_google.engine import Engine, QuantumEngineSampler, util from cirq_google.serialization.serializer import Serializer -_CALIBRATION_IRRELEVANT_GATES = cirq.MeasurementGate, cirq.SingleQubitGate, cirq.WaitGate +_CALIBRATION_IRRELEVANT_GATES = cirq.MeasurementGate, cirq.WaitGate @dataclasses.dataclass(frozen=True) @@ -184,7 +184,7 @@ def _list_moment_pairs_to_characterize( if isinstance(op.gate, cirq.GlobalPhaseGate): raise IncompatibleMomentError('Moment contains global phase gate') - if isinstance(op.gate, _CALIBRATION_IRRELEVANT_GATES): + if isinstance(op.gate, _CALIBRATION_IRRELEVANT_GATES) or cirq.num_qubits(op.gate) == 1: other_operation = True else: translated = gates_translator(op.gate) @@ -1079,7 +1079,7 @@ def _find_moment_zeta_chi_gamma_corrections( if isinstance(op.gate, cirq.GlobalPhaseGate): raise IncompatibleMomentError('Moment contains global phase gate') - if isinstance(op.gate, _CALIBRATION_IRRELEVANT_GATES): + if isinstance(op.gate, _CALIBRATION_IRRELEVANT_GATES) or cirq.num_qubits(op.gate) == 1: other.append(op) continue diff --git a/cirq-google/cirq_google/devices/known_devices.py b/cirq-google/cirq_google/devices/known_devices.py index f333bd133cc..d6f5b800fa5 100644 --- a/cirq-google/cirq_google/devices/known_devices.py +++ b/cirq-google/cirq_google/devices/known_devices.py @@ -109,6 +109,9 @@ def create_device_proto_for_qubits( # Create valid qubit list out.valid_qubits.extend(v2.qubit_to_proto_id(q) for q in qubits) + # Single qubit gates in this gateset + single_qubit_gates = (cirq.PhasedXPowGate, cirq.PhasedXZGate, cirq.ZPowGate) + # Set up a target set for measurement (any qubit permutation) meas_targets = out.valid_targets.add() meas_targets.name = _MEAS_TARGET_SET @@ -147,8 +150,8 @@ def create_device_proto_for_qubits( # Choose target set and number of qubits based on gate type. gate_type = internal_type - # Note: if it is not a measurement gate and doesn't inherit - # from SingleQubitGate, it's assumed to be a two qubit gate. + # Note: if it is not a measurement gate and it's type + # is not in the single_qubit_gates tuple, it's assumed to be a two qubit gate. if gate_type == cirq.MeasurementGate: gate.valid_targets.append(_MEAS_TARGET_SET) elif gate_type == cirq.WaitGate: @@ -157,7 +160,7 @@ def create_device_proto_for_qubits( # Github issue: # https://github.com/quantumlib/Cirq/issues/2537 gate.number_of_qubits = 1 - elif issubclass(gate_type, cirq.SingleQubitGate): + elif gate_type in single_qubit_gates: gate.number_of_qubits = 1 else: # This must be a two-qubit gate diff --git a/cirq-google/cirq_google/devices/xmon_device_test.py b/cirq-google/cirq_google/devices/xmon_device_test.py index b9b1413dc66..4ad483b67ae 100644 --- a/cirq-google/cirq_google/devices/xmon_device_test.py +++ b/cirq-google/cirq_google/devices/xmon_device_test.py @@ -98,7 +98,7 @@ def test_init(): assert d.duration_of(cirq.X(q00)) == 2 * ns assert d.duration_of(cirq.CZ(q00, q01)) == 3 * ns with pytest.raises(ValueError): - _ = d.duration_of(cirq.SingleQubitGate().on(q00)) + _ = d.duration_of(cirq.testing.SingleQubitGate().on(q00)) @mock.patch.dict(os.environ, clear='CIRQ_TESTING') diff --git a/cirq-google/cirq_google/optimizers/convert_to_xmon_gates_test.py b/cirq-google/cirq_google/optimizers/convert_to_xmon_gates_test.py index 9a6477ed430..e1d2313328a 100644 --- a/cirq-google/cirq_google/optimizers/convert_to_xmon_gates_test.py +++ b/cirq-google/cirq_google/optimizers/convert_to_xmon_gates_test.py @@ -18,7 +18,7 @@ import cirq_google -class OtherX(cirq.SingleQubitGate): +class OtherX(cirq.testing.SingleQubitGate): def _unitary_(self) -> np.ndarray: return np.array([[0, 1], [1, 0]]) @@ -28,12 +28,12 @@ def _decompose_(self, qubits): return OtherOtherX().on(*qubits) # coverage:ignore -class OtherOtherX(cirq.SingleQubitGate): +class OtherOtherX(cirq.testing.SingleQubitGate): def _decompose_(self, qubits): return OtherX().on(*qubits) -class NonNativeGate(cirq.SingleQubitGate): +class NonNativeGate(cirq.testing.SingleQubitGate): pass diff --git a/cirq-google/cirq_google/serialization/op_deserializer_test.py b/cirq-google/cirq_google/serialization/op_deserializer_test.py index c8f54040db1..b9ca8206c15 100644 --- a/cirq-google/cirq_google/serialization/op_deserializer_test.py +++ b/cirq-google/cirq_google/serialization/op_deserializer_test.py @@ -33,7 +33,7 @@ def op_proto(json_dict: Dict) -> v2.program_pb2.Operation: @cirq.value_equality -class GateWithAttribute(cirq.SingleQubitGate): +class GateWithAttribute(cirq.testing.SingleQubitGate): def __init__(self, val, not_req=None): self.val = val self.not_req = not_req diff --git a/cirq-google/cirq_google/serialization/op_serializer_test.py b/cirq-google/cirq_google/serialization/op_serializer_test.py index 2b32a1b76af..b96ff9a6ea6 100644 --- a/cirq-google/cirq_google/serialization/op_serializer_test.py +++ b/cirq-google/cirq_google/serialization/op_serializer_test.py @@ -35,12 +35,12 @@ def op_proto(json: Dict) -> v2.program_pb2.Operation: return op -class GateWithAttribute(cirq.SingleQubitGate): +class GateWithAttribute(cirq.testing.SingleQubitGate): def __init__(self, val): self.val = val -class GateWithProperty(cirq.SingleQubitGate): +class GateWithProperty(cirq.testing.SingleQubitGate): def __init__(self, val, not_req=None): self._val = val self._not_req = not_req @@ -50,7 +50,7 @@ def val(self): return self._val -class GateWithMethod(cirq.SingleQubitGate): +class GateWithMethod(cirq.testing.SingleQubitGate): def __init__(self, val): self._val = val diff --git a/cirq-web/cirq_web/circuits/symbols_test.py b/cirq-web/cirq_web/circuits/symbols_test.py index 72829f60bbf..79bb457bdda 100644 --- a/cirq-web/cirq_web/circuits/symbols_test.py +++ b/cirq-web/cirq_web/circuits/symbols_test.py @@ -16,12 +16,12 @@ import pytest -class MockGateNoDiagramInfo(cirq.SingleQubitGate): +class MockGateNoDiagramInfo(cirq.testing.SingleQubitGate): def __init__(self): super(MockGateNoDiagramInfo, self) -class MockGateUnimplementedDiagramInfo(cirq.SingleQubitGate): +class MockGateUnimplementedDiagramInfo(cirq.testing.SingleQubitGate): def __init__(self): super(MockGateUnimplementedDiagramInfo, self) diff --git a/examples/hhl.py b/examples/hhl.py index 8c9399b747b..2e17dd452ed 100644 --- a/examples/hhl.py +++ b/examples/hhl.py @@ -95,7 +95,7 @@ def _decompose_(self, qubits): yield cirq.qft(*qubits[:-1], without_reverse=True) ** -1 -class HamiltonianSimulation(cirq.EigenGate, cirq.SingleQubitGate): +class HamiltonianSimulation(cirq.EigenGate): """A gate that represents e^iAt. This EigenGate + np.linalg.eigh() implementation is used here purely for demonstrative @@ -104,7 +104,6 @@ class HamiltonianSimulation(cirq.EigenGate, cirq.SingleQubitGate): """ def __init__(self, A, t, exponent=1.0): - cirq.SingleQubitGate.__init__(self) cirq.EigenGate.__init__(self, exponent=exponent) self.A = A self.t = t @@ -115,6 +114,9 @@ def __init__(self, A, t, exponent=1.0): P = np.outer(v, np.conj(v)) self.eigen_components.append((theta, P)) + def _num_qubits_(self) -> int: + return 1 + def _with_exponent(self, exponent): return HamiltonianSimulation(self.A, self.t, exponent)