Exemple #1
0
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)
Exemple #3
0
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───
""")
Exemple #4
0
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)
Exemple #5
0
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')
Exemple #6
0
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')
Exemple #7
0
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
Exemple #9
0
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
Exemple #10
0
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
Exemple #11
0
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