def test_get_moment_class(): q0, q1 = cirq.LineQubit.range(2) # Non-hardware assert get_moment_class(cirq.Moment([cirq.CNOT(q0, q1)])) is None # Non-homogenous assert get_moment_class(cirq.Moment([cirq.X(q0), cirq.Z(q1)])) is None # Both phasedxpow assert get_moment_class(cirq.Moment([ cirq.PhasedXPowGate(phase_exponent=0).on(q0), cirq.PhasedXPowGate(phase_exponent=0.5).on(q1)])) == cirq.PhasedXPowGate # Both Z assert get_moment_class(cirq.Moment([ cirq.ZPowGate(exponent=0.123).on(q0), cirq.ZPowGate(exponent=0.5).on(q1)])) == cirq.ZPowGate # SYC assert get_moment_class(cirq.Moment([cg.SYC(q0, q1)])) == cg.SycamoreGate # Measurement assert get_moment_class(cirq.Moment([cirq.measure(q0, q1)])) == cirq.MeasurementGate # Permutation assert get_moment_class(cirq.Moment([ QuirkQubitPermutationGate('', '', [1, 0]).on(q0, q1)])) == QuirkQubitPermutationGate
def swap_rzz(theta: float, q0: ops.Qid, q1: ops.Qid) -> ops.OP_TREE: """ An implementation of SWAP * EXP(1j theta ZZ) using three sycamore gates. This builds off of the zztheta method. A sycamore gate following the zz-gate is a SWAP EXP(1j (THETA - pi/24) ZZ). Args: theta: exp(1j * theta ) q0: First qubit id to operate on q1: Second qubit id to operate on Returns: The circuit that implements ZZ followed by a swap """ # Set interaction part. circuit = circuits.Circuit() angle_offset = np.pi / 24 - np.pi / 4 circuit.append(google.SYC(q0, q1)) circuit.append(rzz(theta - angle_offset, q0, q1)) # Get the intended circuit. intended_circuit = circuits.Circuit( ops.SWAP(q0, q1), ops.ZZPowGate(exponent=2 * theta / np.pi, global_shift=-0.5).on(q0, q1)) yield create_corrected_circuit(intended_circuit, circuit, q0, q1)
def test_syc_circuit_diagram(): a, b = cirq.LineQubit.range(2) circuit = cirq.Circuit(cg.SYC(a, b)) cirq.testing.assert_has_diagram(circuit, """ 0: ───SYC─── │ 1: ───SYC─── """)
def test_validate_well_structured(): q0, q1 = cirq.LineQubit.range(2) circuit = cirq.Circuit([ cirq.Moment([cirq.PhasedXPowGate(phase_exponent=0).on(q0)]), cirq.Moment([ cirq.ZPowGate(exponent=0.5).on(q0), ]), cirq.Moment([cg.SYC(q0, q1)]), cirq.measure(q0, q1, key='z'), ]) validate_well_structured(circuit)
def test_validate_well_structured_non_term_meas(): q0, q1 = cirq.LineQubit.range(2) circuit = cirq.Circuit([ cirq.Moment([cirq.PhasedXPowGate(phase_exponent=0).on(q0)]), cirq.Moment([ cirq.PhasedXPowGate(phase_exponent=0.5).on(q0), ]), cirq.measure(q0, q1, key='z'), cirq.Moment([cg.SYC(q0, q1)]), ]) with pytest.raises(BadlyStructuredCircuitError) as e: validate_well_structured(circuit) assert e.match('Measurements must be terminal')
def test_validate_well_structured_too_many(): q0, q1 = cirq.LineQubit.range(2) circuit = cirq.Circuit([ cirq.Moment([cirq.PhasedXPowGate(phase_exponent=0).on(q0)]), cirq.Moment([ cirq.PhasedXPowGate(phase_exponent=0.5).on(q0), ]), cirq.Moment([cg.SYC(q0, q1)]), cirq.measure(q0, q1, key='z'), ]) with pytest.raises(BadlyStructuredCircuitError) as e: validate_well_structured(circuit) assert e.match('Too many PhX')
def test_find_circuit_structure_violations(): q0, q1 = cirq.LineQubit.range(2) circuit = cirq.Circuit([ cirq.Moment([cirq.PhasedXPowGate(phase_exponent=0).on(q0)]), cirq.Moment([ cirq.PhasedXPowGate(phase_exponent=0.5).on(q0), cirq.ZPowGate(exponent=1).on(q1)]), cirq.Moment([cg.SYC(q0, q1)]), cirq.measure(q0, q1, key='z'), cirq.CNOT(q0, q1) ]) violations = find_circuit_structure_violations(circuit) np.testing.assert_array_equal(violations, [1, 4]) assert violations.tolist() == [1, 4]
def test_serialize_deserialize_syc(): proto = op_proto({ 'gate': { 'id': 'syc' }, 'args': {}, 'qubits': [{ 'id': '1_2' }, { 'id': '1_3' }] }) q0 = cirq.GridQubit(1, 2) q1 = cirq.GridQubit(1, 3) op = cg.SYC(q0, q1) assert cg.SYC_GATESET.serialize_op(op) == proto assert cg.SYC_GATESET.deserialize_op(proto) == op
def random_rotations_between_grid_interaction_layers_circuit( qubits: Iterable['cirq.GridQubit'], depth: int, *, # forces keyword arguments two_qubit_op_factory: Callable[ ['cirq.GridQubit', 'cirq.GridQubit', 'np.random.RandomState'], 'cirq.OP_TREE'] = lambda a, b, _: google.SYC(a, b), pattern: Sequence[GridInteractionLayer] = GRID_STAGGERED_PATTERN, single_qubit_gates: Sequence['cirq.Gate'] = (ops.X**0.5, ops.Y**0.5, ops.PhasedXPowGate( phase_exponent=0.25, exponent=0.5)), add_final_single_qubit_layer: bool = True, seed: value.RANDOM_STATE_LIKE = None, ) -> 'cirq.Circuit': """Generate a random quantum circuit. This construction is based on the circuits used in the paper https://www.nature.com/articles/s41586-019-1666-5. The generated circuit consists of a number of "cycles", this number being specified by `depth`. Each cycle is actually composed of two sub-layers: a layer of single-qubit gates followed by a layer of two-qubit gates. The single-qubit gates are chosen randomly from the gates specified by `single_qubit_gates`, but with the constraint that no qubit is acted upon by the same single-qubit gate in consecutive cycles. In the layer of two-qubit gates, which pairs of qubits undergo interaction is determined by `pattern`, which is a sequence of two-qubit interaction sets. The set of interactions in a two-qubit layer rotates through this sequence. The two-qubit operations themselves are generated by the call `two_qubit_op_factory(a, b, prng)`, where `a` and `b` are the qubits and `prng` is the pseudorandom number generator. At the end of the circuit, an additional layer of single-qubit gates is appended, subject to the same constraint regarding consecutive cycles described above. If only one choice of single-qubit gate is given, then the constraint that excludes repeating single-qubit gates in consecutive cycles is not enforced. Args: qubits: The qubits to use. depth: The number of cycles. two_qubit_op_factory: A factory to generate two-qubit operations. These operations will be generated with calls of the form `two_qubit_op_factory(a, b, prng)`, where `a` and `b` are the qubits to operate on and `prng` is the pseudorandom number generator. pattern: The pattern of grid interaction layers to use. single_qubit_gates: The single-qubit gates to use. add_final_single_qubit_layer: Whether to include a final layer of single-qubit gates after the last cycle. seed: A seed or random state to use for the pseudorandom number generator. """ prng = value.parse_random_state(seed) qubits = list(qubits) coupled_qubit_pairs = _coupled_qubit_pairs(qubits) circuit = circuits.Circuit() previous_single_qubit_layer = {} # type: Dict[cirq.GridQubit, cirq.Gate] if len(set(single_qubit_gates)) == 1: single_qubit_layer_factory = _FixedSingleQubitLayerFactory( {q: single_qubit_gates[0] for q in qubits}) # type: _SingleQubitLayerFactory else: single_qubit_layer_factory = _RandomSingleQubitLayerFactory( qubits, single_qubit_gates, prng) for i in range(depth): single_qubit_layer = single_qubit_layer_factory.new_layer( previous_single_qubit_layer) two_qubit_layer = _two_qubit_layer(coupled_qubit_pairs, two_qubit_op_factory, pattern[i % len(pattern)], prng) circuit.append([g.on(q) for q, g in single_qubit_layer.items()], strategy=circuits.InsertStrategy.NEW_THEN_INLINE) circuit.append(two_qubit_layer, strategy=circuits.InsertStrategy.EARLIEST) previous_single_qubit_layer = single_qubit_layer if add_final_single_qubit_layer: final_single_qubit_layer = single_qubit_layer_factory.new_layer( previous_single_qubit_layer) circuit.append([g.on(q) for q, g in final_single_qubit_layer.items()], strategy=circuits.InsertStrategy.NEW_THEN_INLINE) return circuit
def random_rotations_between_grid_interaction_layers_circuit( qubits: Iterable['cirq.GridQubit'], depth: int, *, # forces keyword arguments two_qubit_op_factory: Callable[ ['cirq.GridQubit', 'cirq.GridQubit', 'np.random.RandomState'], 'cirq.OP_TREE' ] = lambda a, b, _: google.SYC(a, b), pattern: Sequence[GridInteractionLayer] = GRID_STAGGERED_PATTERN, single_qubit_gates: Sequence['cirq.Gate'] = ( ops.X ** 0.5, ops.Y ** 0.5, ops.PhasedXPowGate(phase_exponent=0.25, exponent=0.5), ), add_final_single_qubit_layer: bool = True, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, ) -> 'cirq.Circuit': """Generate a random quantum circuit of a particular form. This construction is based on the circuits used in the paper https://www.nature.com/articles/s41586-019-1666-5. The generated circuit consists of a number of "cycles", this number being specified by `depth`. Each cycle is actually composed of two sub-layers: a layer of single-qubit gates followed by a layer of two-qubit gates, controlled by their respective arguments, see below. The pairs of qubits in a given entangling layer is controlled by the `pattern` argument, see below. Args: qubits: The qubits to use. depth: The number of cycles. two_qubit_op_factory: A callable that returns a two-qubit operation. These operations will be generated with calls of the form `two_qubit_op_factory(q0, q1, prng)`, where `prng` is the pseudorandom number generator. pattern: A sequence of GridInteractionLayers, each of which determine which pairs of qubits are entangled. The layers in a pattern are iterated through sequentially, repeating until `depth` is reached. single_qubit_gates: Single-qubit gates are selected randomly from this sequence. No qubit is acted upon by the same single-qubit gate in consecutive cycles. If only one choice of single-qubit gate is given, then this constraint is not enforced. add_final_single_qubit_layer: Whether to include a final layer of single-qubit gates after the last cycle. seed: A seed or random state to use for the pseudorandom number generator. """ prng = value.parse_random_state(seed) qubits = list(qubits) coupled_qubit_pairs = _coupled_qubit_pairs(qubits) circuit = circuits.Circuit() previous_single_qubit_layer = ops.Moment() single_qubit_layer_factory = _single_qubit_gates_arg_to_factory( single_qubit_gates=single_qubit_gates, qubits=qubits, prng=prng ) for i in range(depth): single_qubit_layer = single_qubit_layer_factory.new_layer(previous_single_qubit_layer) circuit += single_qubit_layer two_qubit_layer = _two_qubit_layer( coupled_qubit_pairs, two_qubit_op_factory, pattern[i % len(pattern)], prng ) circuit += two_qubit_layer previous_single_qubit_layer = single_qubit_layer if add_final_single_qubit_layer: circuit += single_qubit_layer_factory.new_layer(previous_single_qubit_layer) return circuit
def random_rotations_between_two_qubit_circuit( q0: 'cirq.Qid', q1: 'cirq.Qid', depth: int, two_qubit_op_factory: Callable[ ['cirq.Qid', 'cirq.Qid', 'np.random.RandomState'], 'cirq.OP_TREE' ] = lambda a, b, _: google.SYC(a, b), single_qubit_gates: Sequence['cirq.Gate'] = ( ops.X ** 0.5, ops.Y ** 0.5, ops.PhasedXPowGate(phase_exponent=0.25, exponent=0.5), ), add_final_single_qubit_layer: bool = True, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, ) -> 'cirq.Circuit': """Generate a random two-qubit quantum circuit. This construction uses a similar structure to those in the paper https://www.nature.com/articles/s41586-019-1666-5. The generated circuit consists of a number of "cycles", this number being specified by `depth`. Each cycle is actually composed of two sub-layers: a layer of single-qubit gates followed by a layer of two-qubit gates, controlled by their respective arguments, see below. Args: q0: The first qubit q1: The second qubit depth: The number of cycles. two_qubit_op_factory: A callable that returns a two-qubit operation. These operations will be generated with calls of the form `two_qubit_op_factory(q0, q1, prng)`, where `prng` is the pseudorandom number generator. single_qubit_gates: Single-qubit gates are selected randomly from this sequence. No qubit is acted upon by the same single-qubit gate in consecutive cycles. If only one choice of single-qubit gate is given, then this constraint is not enforced. add_final_single_qubit_layer: Whether to include a final layer of single-qubit gates after the last cycle (subject to the same non-consecutivity constraint). seed: A seed or random state to use for the pseudorandom number generator. """ prng = value.parse_random_state(seed) circuit = circuits.Circuit() previous_single_qubit_layer = ops.Moment() single_qubit_layer_factory = _single_qubit_gates_arg_to_factory( single_qubit_gates=single_qubit_gates, qubits=(q0, q1), prng=prng ) for _ in range(depth): single_qubit_layer = single_qubit_layer_factory.new_layer(previous_single_qubit_layer) circuit += single_qubit_layer circuit += two_qubit_op_factory(q0, q1, prng) previous_single_qubit_layer = single_qubit_layer if add_final_single_qubit_layer: circuit += single_qubit_layer_factory.new_layer(previous_single_qubit_layer) return circuit