def test_with_qubits_and_transform_qubits(): g = cirq.Gate() op = cirq.GateOperation(g, cirq.LineQubit.range(3)) assert op.with_qubits(*cirq.LineQubit.range(2)) == cirq.GateOperation( g, cirq.LineQubit.range(2)) assert op.transform_qubits( lambda e: cirq.LineQubit(-e.x)) == cirq.GateOperation( g, [cirq.LineQubit(0), cirq.LineQubit(-1), cirq.LineQubit(-2)]) # The gate's constraints should be applied when changing the qubits. with pytest.raises(ValueError): _ = cirq.Y(cirq.LineQubit(0)).with_qubits(cirq.LineQubit(0), cirq.LineQubit(1))
def test_text_diagrammable(): q = cirq.NamedQubit('q') # If the gate isn't diagrammable, you get a type error. op0 = cirq.GateOperation(cirq.Gate(), [q]) assert not cirq.can_cast(cirq.TextDiagrammable, op0) with pytest.raises(TypeError): _ = op0.text_diagram_info(cirq.TextDiagramInfoArgs.UNINFORMED_DEFAULT) op1 = cirq.GateOperation(cirq.S, [q]) assert cirq.can_cast(cirq.TextDiagrammable, op1) actual = op1.text_diagram_info(cirq.TextDiagramInfoArgs.UNINFORMED_DEFAULT) expected = cirq.S.text_diagram_info( cirq.TextDiagramInfoArgs.UNINFORMED_DEFAULT) assert actual == expected
def test_repr(): a, b = cirq.LineQubit.range(2) assert (repr(cirq.GateOperation( cirq.CZ, (a, b))) == 'cirq.CZ(cirq.LineQubit(0), cirq.LineQubit(1))') class Inconsistent(cirq.SingleQubitGate): def __repr__(self): return 'Inconsistent' def on(self, *qubits): return cirq.GateOperation(Inconsistent(), qubits) assert (repr(cirq.GateOperation(Inconsistent(), [ a ])) == 'cirq.GateOperation(gate=Inconsistent, qubits=[cirq.LineQubit(0)])')
def test_validate_operation_supported_gate(): d = square_device(3, 3) class MyGate(cirq.Gate): def num_qubits(self): return 1 d.validate_operation(cirq.GateOperation(cirq.Z, [cirq.GridQubit(0, 0)])) assert MyGate().num_qubits() == 1 with pytest.raises(ValueError): d.validate_operation( cirq.GateOperation(MyGate(), [cirq.GridQubit(0, 0)])) with pytest.raises(ValueError): d.validate_operation(NotImplementedOperation())
def test_eval_repr(key): # Basic safeguard against repr-inequality. op = cirq.GateOperation( gate=cirq.MeasurementGate(1, key), qubits=[cirq.GridQubit(0, 1)], ) cirq.testing.assert_equivalent_repr(op)
def export_to_cirq(obj): """Imports a gate, operation or circuit from Cirq. Args: obj: the object to be exported to Cirq. Returns: the exported Cirq object (an instance of cirq.Circuit, cirq.GateOperation, or a subclass of cirq.Gate). Raises: TypeError: if export is not supported for the given type. ValueError: if the object cannot be exported successfully. """ if isinstance(obj, circuit.PhasedXGate): return cirq.PhasedXPowGate(exponent=obj.get_rotation_angle() / np.pi, phase_exponent=obj.get_phase_angle() / np.pi) elif isinstance(obj, circuit.RotZGate): return cirq.ZPowGate(exponent=obj.get_rotation_angle() / np.pi) elif isinstance(obj, circuit.ControlledZGate): return cirq.CZPowGate(exponent=1.0) elif isinstance(obj, circuit.MatrixGate): return cirq.MatrixGate(obj.get_operator()) elif isinstance(obj, circuit.Operation): return cirq.GateOperation( export_to_cirq(obj.get_gate()), [cirq.LineQubit(qubit) for qubit in obj.get_qubits()]) elif isinstance(obj, circuit.Circuit): return cirq.Circuit(export_to_cirq(operation) for operation in obj) else: raise TypeError('unknown type: %s' % type(obj).__name__)
def test_flatten_op_tree(): operations = [ cirq.GateOperation(cirq.SingleQubitGate(), [cirq.NamedQubit(str(i))]) for i in range(10) ] # Empty tree. assert list(cirq.flatten_op_tree([[[]]])) == [] # Just an item. assert list(cirq.flatten_op_tree(operations[0])) == operations[:1] # Flat list. assert list(cirq.flatten_op_tree(operations)) == operations # Tree. assert list( cirq.flatten_op_tree( (operations[0], operations[1:5], operations[5:]))) == operations # Flatten moment. assert list( cirq.flatten_op_tree((operations[0], cirq.Moment(operations[1:5]), operations[5:]))) == operations # Bad trees. with pytest.raises(TypeError): _ = list(cirq.flatten_op_tree(None)) with pytest.raises(TypeError): _ = list(cirq.flatten_op_tree(5)) with pytest.raises(TypeError): _ = list(cirq.flatten_op_tree([operations[0], (4, )]))
def test_validate_schedule_errors(): d = square_device(2, 2, max_controls=3) s = cirq.Schedule(device=cirq.UnconstrainedDevice) q00 = cirq.GridQubit(0, 0) q01 = cirq.GridQubit(0, 1) q10 = cirq.GridQubit(1, 0) q11 = cirq.GridQubit(1, 1) us = cirq.Duration(nanos=10**3) ms = cirq.Duration(nanos=10**6) msone = cirq.Timestamp(nanos=10**6) mstwo = cirq.Timestamp(nanos=2*10**6) msthree = cirq.Timestamp(nanos=3*10**6) for qubit in d.qubits: s.include(cirq.ScheduledOperation(cirq.Timestamp(nanos=0), 100*us, cirq.X.on(qubit))) s.include(cirq.ScheduledOperation(msone, 100*us, cirq.TOFFOLI.on(q00,q01,q10))) s.include(cirq.ScheduledOperation(mstwo, 100*us, cirq.ParallelGateOperation( cirq.X, [q00, q01]))) s.include(cirq.ScheduledOperation(mstwo, 100*us, cirq.ParallelGateOperation( cirq.Z, [q10, q11]))) for qubit in d.qubits: s.include(cirq.ScheduledOperation(msthree, 50*ms, cirq.GateOperation( cirq.MeasurementGate(1, qubit), [qubit]))) d.validate_schedule(s) s.include(cirq.ScheduledOperation(cirq.Timestamp(nanos=10**9), 100*us, cirq.X.on(q00))) with pytest.raises(ValueError, match="Non-measurement operation after " "measurement"): d.validate_schedule(s)
def test_transform_internal_nodes(): operations = [ cirq.GateOperation(cirq.SingleQubitGate(), [cirq.LineQubit(2 * i)]) for i in range(10) ] def skip_first(op): first = True for item in op: if not first: yield item first = False def skip_tree_freeze(root): return cirq.freeze_op_tree( cirq.transform_op_tree(root, iter_transformation=skip_first)) # Empty tree. assert skip_tree_freeze([[[]]]) == () assert skip_tree_freeze([[[]], [[], []]]) == (((), ), ) # Just an item. assert skip_tree_freeze(operations[0]) == operations[0] # Flat list. assert skip_tree_freeze(operations) == tuple(operations[1:]) # Tree. assert (skip_tree_freeze( (operations[1:5], operations[0], operations[5:])) == (operations[0], tuple(operations[6:])))
def test_freeze_op_tree(): operations = [ cirq.GateOperation(cirq.SingleQubitGate(), [cirq.NamedQubit(str(i))]) for i in range(10) ] # Empty tree. assert cirq.freeze_op_tree([[[]]]) == (((), ), ) # Just an item. assert cirq.freeze_op_tree(operations[0]) == operations[0] # Flat list. assert cirq.freeze_op_tree(operations) == tuple(operations) # Tree. assert (cirq.freeze_op_tree( (operations[0], (operations[i] for i in range(1, 5)), operations[5:])) == (operations[0], tuple(operations[1:5]), tuple(operations[5:]))) # Bad trees. with pytest.raises(TypeError): cirq.freeze_op_tree(None) with pytest.raises(TypeError): cirq.freeze_op_tree(5) with pytest.raises(TypeError): _ = cirq.freeze_op_tree([operations[0], (4, )])
def test_extrapolate(): q = cirq.NamedQubit('q') # If the gate isn't extrapolatable, you get a type error. op0 = cirq.GateOperation(cirq.Gate(), [q]) assert not cirq.can_cast(cirq.ExtrapolatableEffect, op0) with pytest.raises(TypeError): _ = op0.extrapolate_effect(0.5) with pytest.raises(TypeError): _ = op0**0.5 op1 = cirq.GateOperation(cirq.Y, [q]) assert cirq.can_cast(cirq.ExtrapolatableEffect, op1) assert op1**0.5 == op1.extrapolate_effect(0.5) == cirq.GateOperation( cirq.Y**0.5, [q]) assert (cirq.Y**0.5).on(q) == cirq.Y(q)**0.5
def test_controlled_operation_init(): cb = cirq.NamedQubit('ctr') q = cirq.NamedQubit('q') g = cirq.SingleQubitGate() v = cirq.GateOperation(g, (q,)) c = cirq.ControlledOperation([cb], v) assert c.sub_operation == v assert c.controls == (cb,) assert c.qubits == (cb, q) assert c == c.with_qubits(cb, q) assert c.control_values == ((1,),) assert cirq.qid_shape(c) == (2, 2) c = cirq.ControlledOperation([cb], v, control_values=[0]) assert c.sub_operation == v assert c.controls == (cb,) assert c.qubits == (cb, q) assert c == c.with_qubits(cb, q) assert c.control_values == ((0,),) assert cirq.qid_shape(c) == (2, 2) c = cirq.ControlledOperation([cb.with_dimension(3)], v) assert c.sub_operation == v assert c.controls == (cb.with_dimension(3),) assert c.qubits == (cb.with_dimension(3), q) assert c == c.with_qubits(cb.with_dimension(3), q) assert c.control_values == ((1,),) assert cirq.qid_shape(c) == (3, 2) with pytest.raises(ValueError, match=r'len\(control_values\) != len\(controls\)'): _ = cirq.ControlledOperation([cb], v, control_values=[1, 1]) with pytest.raises(ValueError, match='Control values .*outside of range'): _ = cirq.ControlledOperation([cb], v, control_values=[2]) with pytest.raises(ValueError, match='Control values .*outside of range'): _ = cirq.ControlledOperation([cb], v, control_values=[(1, -1)])
def test_validate_measurement_non_adjacent_qubits_ok(): d = square_device(3, 3) d.validate_operation( cirq.GateOperation( cirq.MeasurementGate(2, 'a'), (cirq.GridQubit(0, 0), cirq.GridQubit(2, 0)) ) )
def test_gate_operation_approx_eq(): a = [cirq.NamedQubit('r1')] b = [cirq.NamedQubit('r2')] assert cirq.approx_eq(cirq.GateOperation(cirq.XPowGate(), a), cirq.GateOperation(cirq.XPowGate(), a)) assert not cirq.approx_eq(cirq.GateOperation(cirq.XPowGate(), a), cirq.GateOperation(cirq.XPowGate(), b)) assert cirq.approx_eq(cirq.GateOperation(cirq.XPowGate(exponent=0), a), cirq.GateOperation(cirq.XPowGate(exponent=1e-9), a)) assert not cirq.approx_eq( cirq.GateOperation(cirq.XPowGate(exponent=0), a), cirq.GateOperation(cirq.XPowGate(exponent=1e-7), a)) assert cirq.approx_eq(cirq.GateOperation(cirq.XPowGate(exponent=0), a), cirq.GateOperation(cirq.XPowGate(exponent=1e-7), a), atol=1e-6)
def test_eval_repr(): # Basic safeguard against repr-inequality. op = cirq.GateOperation( gate=cirq.MeasurementGate(1, cirq.MeasurementKey(path=(), name='q0_1_0'), ()), qubits=[cirq.GridQubit(0, 1)], ) cirq.testing.assert_equivalent_repr(op)
def test_gate_operation_eq(): g1 = cirq.Gate() g2 = cirq.Gate() r1 = [cirq.NamedQubit('r1')] r2 = [cirq.NamedQubit('r2')] r12 = r1 + r2 r21 = r2 + r1 eq = cirq.testing.EqualsTester() eq.make_equality_group(lambda: cirq.GateOperation(g1, r1)) eq.make_equality_group(lambda: cirq.GateOperation(g2, r1)) eq.make_equality_group(lambda: cirq.GateOperation(g1, r2)) eq.make_equality_group(lambda: cirq.GateOperation(g1, r12)) eq.make_equality_group(lambda: cirq.GateOperation(g1, r21)) eq.add_equality_group(cirq.GateOperation(cirq.CZ, r21), cirq.GateOperation(cirq.CZ, r12)) # Interchangeable subsets. class PairGate(cirq.Gate, cirq.InterchangeableQubitsGate): def qubit_index_to_equivalence_group_key(self, index: int): return index // 2 p = PairGate() a0, a1, b0, b1, c0 = cirq.LineQubit.range(5) eq.add_equality_group(p(a0, a1, b0, b1), p(a1, a0, b1, b0)) eq.add_equality_group(p(b0, b1, a0, a1)) eq.add_equality_group(p(a0, a1, b0, b1, c0), p(a1, a0, b1, b0, c0)) eq.add_equality_group(p(a0, b0, a1, b1, c0)) eq.add_equality_group(p(a0, c0, b0, b1, a1)) eq.add_equality_group(p(b0, a1, a0, b1, c0))
def test_parameterizable_effect(): q = cirq.NamedQubit('q') r = cirq.ParamResolver({'a': 0.5}) op1 = cirq.GateOperation(cirq.Z**sympy.Symbol('a'), [q]) assert cirq.is_parameterized(op1) op2 = cirq.resolve_parameters(op1, r) assert not cirq.is_parameterized(op2) assert op2 == cirq.S.on(q)
def test_eval_repr(key): # Basic safeguard against repr-inequality. op = cirq.GateOperation( gate=cirq.PauliMeasurementGate([cirq.X, cirq.Y], key), qubits=[cirq.GridQubit(0, 1), cirq.GridQubit(1, 1)], ) cirq.testing.assert_equivalent_repr(op) assert cirq.is_measurement(op) assert cirq.measurement_key_name(op) == str(key)
def test_parameterizable_effect(): q = cirq.NamedQubit('q') r = cirq.ParamResolver({'a': 0.5}) # If the gate isn't parameterizable, you get a type error. op0 = cirq.GateOperation(cirq.Gate(), [q]) assert not cirq.can_cast(cirq.ParameterizableEffect, op0) with pytest.raises(TypeError): _ = op0.is_parameterized() with pytest.raises(TypeError): _ = op0.with_parameters_resolved_by(r) op1 = cirq.GateOperation(cirq.RotZGate(half_turns=cirq.Symbol('a')), [q]) assert cirq.can_cast(cirq.ParameterizableEffect, op1) assert op1.is_parameterized() op2 = op1.with_parameters_resolved_by(r) assert not op2.is_parameterized() assert op2 == cirq.S.on(q)
def test_transform_bad_tree(): with pytest.raises(TypeError): _ = list(cirq.transform_op_tree(None)) with pytest.raises(TypeError): _ = list(cirq.transform_op_tree(5)) with pytest.raises(TypeError): _ = list(cirq.flatten_op_tree(cirq.transform_op_tree([ cirq.GateOperation(cirq.Gate(), [cirq.NamedQubit('q')]), (4,) ])))
def test_controlled_operation_init(): cb = cirq.NamedQubit('ctr') q = cirq.NamedQubit('q') g = cirq.SingleQubitGate() v = cirq.GateOperation(g, (q, )) c = cirq.ControlledOperation(cb, v) assert c.sub_operation == v assert c.control == cb assert c.qubits == (cb, q) assert c == c.with_qubits(cb, q)
def test_validate_operation_existing_qubits(): d = square_device(3, 3, holes=[cirq.GridQubit(1, 1)]) d.validate_operation(cirq.GateOperation(cirq.CZ, (cirq.GridQubit(0, 0), cirq.GridQubit(1, 0)))) d.validate_operation(cirq.Z(cirq.GridQubit(0, 0))) with pytest.raises(ValueError): d.validate_operation(cirq.CZ(cirq.GridQubit(0, 0), cirq.GridQubit(-1, 0))) with pytest.raises(ValueError): d.validate_operation(cirq.Z(cirq.GridQubit(-1, 0))) with pytest.raises(ValueError): d.validate_operation(cirq.CZ(cirq.GridQubit(1, 0), cirq.GridQubit(1, 1)))
def test_init_timedelta(): d = square_device(2, 2, holes=[cirq.GridQubit(1, 1)], use_timedelta=True) us = cirq.Duration(nanos=10 ** 3) ms = cirq.Duration(nanos=10 ** 6) q00 = cirq.GridQubit(0, 0) q01 = cirq.GridQubit(0, 1) q10 = cirq.GridQubit(1, 0) assert d.qubits == {q10, q00, q01} 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))
def test_controlled_operation_init(): class G(cirq.testing.SingleQubitGate): def _has_mixture_(self): return True g = G() cb = cirq.NamedQubit('ctr') q = cirq.NamedQubit('q') v = cirq.GateOperation(g, (q, )) c = cirq.ControlledOperation([cb], v) assert c.sub_operation == v assert c.controls == (cb, ) assert c.qubits == (cb, q) assert c == c.with_qubits(cb, q) assert c.control_values == ((1, ), ) assert cirq.qid_shape(c) == (2, 2) c = cirq.ControlledOperation([cb], v, control_values=[0]) assert c.sub_operation == v assert c.controls == (cb, ) assert c.qubits == (cb, q) assert c == c.with_qubits(cb, q) assert c.control_values == ((0, ), ) assert cirq.qid_shape(c) == (2, 2) c = cirq.ControlledOperation([cb.with_dimension(3)], v) assert c.sub_operation == v assert c.controls == (cb.with_dimension(3), ) assert c.qubits == (cb.with_dimension(3), q) assert c == c.with_qubits(cb.with_dimension(3), q) assert c.control_values == ((1, ), ) assert cirq.qid_shape(c) == (3, 2) with pytest.raises(ValueError, match=r'len\(control_values\) != len\(controls\)'): _ = cirq.ControlledOperation([cb], v, control_values=[1, 1]) with pytest.raises(ValueError, match='Control values .*outside of range'): _ = cirq.ControlledOperation([cb], v, control_values=[2]) with pytest.raises(ValueError, match='Control values .*outside of range'): _ = cirq.ControlledOperation([cb], v, control_values=[(1, -1)]) with pytest.raises(ValueError, match=re.escape("Duplicate control qubits ['ctr'].")): _ = cirq.ControlledOperation([cb, cirq.LineQubit(0), cb], cirq.X(q)) with pytest.raises( ValueError, match=re.escape("Sub-op and controls share qubits ['ctr']")): _ = cirq.ControlledOperation([cb, cirq.LineQubit(0)], cirq.CX(cb, q)) with pytest.raises(ValueError, match='Cannot control measurement'): _ = cirq.ControlledOperation([cb], cirq.measure(q)) with pytest.raises(ValueError, match='Cannot control channel'): _ = cirq.ControlledOperation([cb], cirq.PhaseDampingChannel(1)(q))
def test_validate_operation_existing_qubits(): d = ion_device(3) d.validate_operation( cirq.GateOperation(cirq.XX, (cirq.LineQubit(0), cirq.LineQubit(1)))) d.validate_operation(cirq.Z(cirq.LineQubit(0))) with pytest.raises(ValueError): d.validate_operation(cirq.CZ(cirq.LineQubit(0), cirq.LineQubit(-1))) with pytest.raises(ValueError): d.validate_operation(cirq.Z(cirq.LineQubit(-1))) with pytest.raises(ValueError): d.validate_operation(cirq.CZ(cirq.LineQubit(1), cirq.LineQubit(1))) with pytest.raises(ValueError): d.validate_operation(cirq.X(cirq.NamedQubit("q1")))
def test_flatten_to_ops_or_moments(): operations = [ 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:] assert list(cirq.flatten_to_ops_or_moments(op_tree)) == output assert list(cirq.flatten_op_tree(op_tree, preserve_moments=True)) == output # Bad trees. with pytest.raises(TypeError): _ = list(cirq.flatten_to_ops_or_moments(None)) with pytest.raises(TypeError): _ = list(cirq.flatten_to_ops_or_moments(5)) with pytest.raises(TypeError): _ = list(cirq.flatten_to_ops_or_moments([operations[0], (4, )]))
def matrix_to_sycamore_operations( target_qubits: List[cirq.GridQubit], matrix: np.ndarray) -> Tuple[cirq.OP_TREE, List[cirq.GridQubit]]: """A method to convert a unitary matrix to a list of Sycamore operations. This method will return a list of `cirq.Operation`s using the qubits and (optionally) ancilla qubits to implement the unitary matrix `matrix` on the target qubits `qubits`. The operations are also supported by `cirq.google.gate_sets.SYC_GATESET`. Args: target_qubits: list of qubits the returned operations will act on. The qubit order defined by the list is assumed to be used by the operations to implement `matrix`. matrix: a matrix that is guaranteed to be unitary and of size (2**len(qs), 2**len(qs)). Returns: A tuple of operations and ancilla qubits allocated. Operations: In case the matrix is supported, a list of operations `ops` is returned. `ops` acts on `qs` qubits and for which `cirq.unitary(ops)` is equal to `matrix` up to certain tolerance. In case the matrix is not supported, it might return NotImplemented to reduce the noise in the judge output. Ancilla qubits: In case ancilla qubits are allocated a list of ancilla qubits. Otherwise an empty list. . """ ops = NotImplemented if len(target_qubits) == 1: ops = cirq.single_qubit_matrix_to_gates(matrix) for i, x in enumerate(ops): ops[i] = cirq.GateOperation(x, [target_qubits[0]]) elif len(target_qubits) == 2: ops = cirq.two_qubit_matrix_to_operations(target_qubits[0], target_qubits[1], matrix, allow_partial_czs=False, atol=1e-4) elif len(target_qubits) == 3: ops = cirq.three_qubit_matrix_to_operations(target_qubits[0], target_qubits[1], target_qubits[2], matrix, atol=1e-4) return ops, []
def test_op_equivalence(): a, b = cirq.LineQubit.range(2) various_x = [ cirq.X(a), cirq.PauliString({a: cirq.X}), cirq.PauliString.from_single(a, cirq.X), cirq.SingleQubitPauliStringGateOperation(cirq.X, a), cirq.GateOperation(cirq.X, [a]), ] for x in various_x: cirq.testing.assert_equivalent_repr(x) eq = cirq.testing.EqualsTester() eq.add_equality_group(*various_x) eq.add_equality_group(cirq.Y(a), cirq.PauliString({a: cirq.Y})) eq.add_equality_group(-cirq.PauliString({a: cirq.X})) eq.add_equality_group(cirq.Z(a), cirq.PauliString({a: cirq.Z})) eq.add_equality_group(cirq.Z(b), cirq.PauliString({b: cirq.Z}))
def test_validate_operation_existing_qubits(): d = ion_device(3) d.validate_operation( cirq.GateOperation(cirq.XX, (cirq.LineQubit(0), cirq.LineQubit(1)))) d.validate_operation(cirq.Z(cirq.LineQubit(0))) d.validate_operation( cirq.PhasedXPowGate(phase_exponent=0.75, exponent=0.25, global_shift=0.1).on(cirq.LineQubit(1))) with pytest.raises(ValueError): d.validate_operation(cirq.CZ(cirq.LineQubit(0), cirq.LineQubit(-1))) with pytest.raises(ValueError): d.validate_operation(cirq.Z(cirq.LineQubit(-1))) with pytest.raises(ValueError): d.validate_operation(cirq.CZ(cirq.LineQubit(1), cirq.LineQubit(1))) with pytest.raises(ValueError): d.validate_operation(cirq.X(cirq.NamedQubit("q1")))
def test_gate_operation_eq(): g1 = cirq.SingleQubitGate() g2 = cirq.SingleQubitGate() g3 = cirq.TwoQubitGate() r1 = [cirq.NamedQubit('r1')] r2 = [cirq.NamedQubit('r2')] r12 = r1 + r2 r21 = r2 + r1 eq = cirq.testing.EqualsTester() eq.make_equality_group(lambda: cirq.GateOperation(g1, r1)) eq.make_equality_group(lambda: cirq.GateOperation(g2, r1)) eq.make_equality_group(lambda: cirq.GateOperation(g1, r2)) eq.make_equality_group(lambda: cirq.GateOperation(g3, r12)) eq.make_equality_group(lambda: cirq.GateOperation(g3, r21)) eq.add_equality_group(cirq.GateOperation(cirq.CZ, r21), cirq.GateOperation(cirq.CZ, r12)) @cirq.value_equality class PairGate(cirq.Gate, cirq.InterchangeableQubitsGate): """Interchangeable substes.""" def __init__(self, num_qubits): self._num_qubits = num_qubits def num_qubits(self) -> int: return self._num_qubits def qubit_index_to_equivalence_group_key(self, index: int): return index // 2 def _value_equality_values_(self): return self.num_qubits(), def p(*q): return PairGate(len(q)).on(*q) a0, a1, b0, b1, c0 = cirq.LineQubit.range(5) eq.add_equality_group(p(a0, a1, b0, b1), p(a1, a0, b1, b0)) eq.add_equality_group(p(b0, b1, a0, a1)) eq.add_equality_group(p(a0, a1, b0, b1, c0), p(a1, a0, b1, b0, c0)) eq.add_equality_group(p(a0, b0, a1, b1, c0)) eq.add_equality_group(p(a0, c0, b0, b1, a1)) eq.add_equality_group(p(b0, a1, a0, b1, c0))