Esempio n. 1
0
def single_qubit_matrix_to_phased_x_z(mat: np.ndarray,
                                      atol: float = 0
                                      ) -> List[ops.SingleQubitGate]:
    """Implements a single-qubit operation with a PhasedX and Z gate.

    If one of the gates isn't needed, it will be omitted.

    Args:
        mat: The 2x2 unitary matrix of the operation to implement.
        atol: A limit on the amount of error introduced by the
            construction.

    Returns:
        A list of gates that, when applied in order, perform the desired
            operation.
    """

    xy_turn, xy_phase_turn, total_z_turn = (
        _deconstruct_single_qubit_matrix_into_gate_turns(mat))

    # Build the intended operation out of non-negligible XY and Z rotations.
    result = [
        ops.PhasedXPowGate(exponent=2 * xy_turn,
                           phase_exponent=2 * xy_phase_turn),
        ops.Z**(2 * total_z_turn)
    ]
    result = [g for g in result if protocols.trace_distance_bound(g) > atol]

    # Special case: XY half-turns can absorb Z rotations.
    if len(result) == 2 and abs(xy_turn) >= 0.5 - atol:
        return [
            ops.PhasedXPowGate(phase_exponent=2 * xy_phase_turn + total_z_turn)
        ]

    return result
Esempio n. 2
0
def decompose_cz_into_syc(a: ops.Qid, b: ops.Qid):
    """Decompose CZ into sycamore gates using precomputed coefficients"""
    yield ops.PhasedXPowGate(phase_exponent=0.5678998743900456, exponent=0.5863459345743176).on(a)
    yield ops.PhasedXPowGate(phase_exponent=0.3549946157441739).on(b)
    yield google.SYC.on(a, b)
    yield ops.PhasedXPowGate(phase_exponent=-0.5154334589432878, exponent=0.5228733015013345).on(b)
    yield ops.PhasedXPowGate(phase_exponent=0.06774925307475355).on(a)
    yield google.SYC.on(a, b),
    yield ops.PhasedXPowGate(phase_exponent=-0.5987667922766213, exponent=0.4136540654256824).on(a),
    yield (ops.Z ** -0.9255092746611595).on(b),
    yield (ops.Z ** -1.333333333333333).on(a),
Esempio n. 3
0
    def __init__(self,
                 qubits: Sequence['cirq.Qid'],
                 prerotations: Optional[Sequence[Tuple[float, float]]] = None):
        """Initializes the rotation protocol and matrix for system.

        Args:
            qubits: Qubits to do the tomography on.
            prerotations: Tuples of (phase_exponent, exponent) parameters for
                gates to apply to the qubits before measurement. The actual
                rotation applied will be `cirq.PhasedXPowGate` with the
                specified values of phase_exponent and exponent. If None,
                we use [(0, 0), (0, 0.5), (0.5, 0.5)], which corresponds
                to rotation gates [I, X**0.5, Y**0.5].
        """
        if prerotations is None:
            prerotations = [(0, 0), (0, 0.5), (0.5, 0.5)]
        self.num_qubits = len(qubits)

        phase_exp_vals, exp_vals = zip(*prerotations)

        operations: List['cirq.Operation'] = []
        sweeps: List['cirq.Sweep'] = []
        for i, qubit in enumerate(qubits):
            phase_exp = sympy.Symbol(f'phase_exp_{i}')
            exp = sympy.Symbol(f'exp_{i}')
            gate = ops.PhasedXPowGate(phase_exponent=phase_exp, exponent=exp)
            operations.append(gate.on(qubit))
            sweeps.append(
                study.Points(phase_exp, phase_exp_vals) +
                study.Points(exp, exp_vals))

        self.rot_circuit = circuits.Circuit(operations)
        self.rot_sweep = study.Product(*sweeps)
        self.mat = self._make_state_tomography_matrix()
Esempio n. 4
0
def _potential_cross_partial_w(moment_index: int,
                               op: ops.Operation,
                               state: _OptimizerState) -> None:
    """Cross the held W over a partial W gate.

    [Where W(a) is shorthand for PhasedX(phase_exponent=a).]

    Uses the following identity:
        ───W(a)───W(b)^t───
        ≡ ───Z^-a───X───Z^a───W(b)^t────── (expand W(a))
        ≡ ───Z^-a───X───W(b-a)^t───Z^a──── (move Z^a across, phasing axis)
        ≡ ───Z^-a───W(a-b)^t───X───Z^a──── (move X across, negating axis angle)
        ≡ ───W(2a-b)^t───Z^-a───X───Z^a─── (move Z^-a across, phasing axis)
        ≡ ───W(2a-b)^t───W(a)───
    """
    a = state.held_w_phases.get(op.qubits[0])
    if a is None:
        return
    exponent, phase_exponent = cast(Tuple[float, float],
                                    _try_get_known_phased_pauli(op))
    new_op = ops.PhasedXPowGate(
        exponent=exponent,
        phase_exponent=2 * a - phase_exponent).on(op.qubits[0])
    state.deletions.append((moment_index, op))
    state.inline_intos.append((moment_index, new_op))
Esempio n. 5
0
def xmon_op_from_proto(proto: operations_pb2.Operation) -> 'cirq.Operation':
    """Convert the proto to the corresponding operation.

    See protos in api/google/v1 for specification of the protos.

    Args:
        proto: Operation proto.

    Returns:
        The operation.
    """
    param = _parameterized_value_from_proto
    qubit = _qubit_from_proto
    if proto.HasField('exp_w'):
        exp_w = proto.exp_w
        return ops.PhasedXPowGate(
            exponent=param(exp_w.half_turns),
            phase_exponent=param(exp_w.axis_half_turns),
        ).on(qubit(exp_w.target))
    if proto.HasField('exp_z'):
        exp_z = proto.exp_z
        return ops.Z(qubit(exp_z.target))**param(exp_z.half_turns)
    if proto.HasField('exp_11'):
        exp_11 = proto.exp_11
        return ops.CZ(qubit(exp_11.target1),
                      qubit(exp_11.target2))**param(exp_11.half_turns)
    if proto.HasField('measurement'):
        meas = proto.measurement
        return ops.MeasurementGate(num_qubits=len(meas.targets),
                                   key=meas.key,
                                   invert_mask=tuple(meas.invert_mask)).on(
                                       *[qubit(q) for q in meas.targets])

    raise ValueError(f'invalid operation: {proto}')
Esempio n. 6
0
def _potential_cross_partial_w(
    op: ops.Operation,
    held_w_phases: Dict[ops.Qid, value.TParamVal],
) -> 'cirq.OP_TREE':
    """Cross the held W over a partial W gate.

    [Where W(a) is shorthand for PhasedX(phase_exponent=a).]

    Uses the following identity:
        ───W(a)───W(b)^t───
        ≡ ───Z^-a───X───Z^a───W(b)^t────── (expand W(a))
        ≡ ───Z^-a───X───W(b-a)^t───Z^a──── (move Z^a across, phasing axis)
        ≡ ───Z^-a───W(a-b)^t───X───Z^a──── (move X across, negating axis angle)
        ≡ ───W(2a-b)^t───Z^-a───X───Z^a─── (move Z^-a across, phasing axis)
        ≡ ───W(2a-b)^t───W(a)───
    """
    a = held_w_phases.get(op.qubits[0], None)
    if a is None:
        return op
    exponent, phase_exponent = cast(
        Tuple[value.TParamVal, value.TParamVal], _try_get_known_phased_pauli(op)
    )
    new_op = ops.PhasedXPowGate(exponent=exponent, phase_exponent=2 * a - phase_exponent).on(
        op.qubits[0]
    )
    return new_op
def _random_half_rotations(qubits: Sequence[ops.Qid], num_layers: int) -> List[List[ops.OP_TREE]]:
    rot_ops = [ops.X ** 0.5, ops.Y ** 0.5, ops.PhasedXPowGate(phase_exponent=0.25, exponent=0.5)]
    num_qubits = len(qubits)
    rand_nums = np.random.choice(3, (num_qubits, num_layers))
    single_q_layers = []  # type: List[List[ops.OP_TREE]]
    for i in range(num_layers):
        single_q_layers.append([rot_ops[rand_nums[j, i]](qubits[j]) for j in range(num_qubits)])
    return single_q_layers
Esempio n. 8
0
def _dump_held(qubits: Iterable[ops.Qid], moment_index: int, state: _OptimizerState):
    # Note: sorting is to avoid non-determinism in the insertion order.
    for q in sorted(qubits):
        p = state.held_w_phases.get(q)
        if p is not None:
            dump_op = ops.PhasedXPowGate(phase_exponent=p).on(q)
            state.insertions.append((moment_index, dump_op))
        state.held_w_phases.pop(q, None)
Esempio n. 9
0
def xmon_op_from_proto_dict(proto_dict: Dict) -> 'cirq.Operation':
    """Convert the proto dictionary to the corresponding operation.

    See protos in api/google/v1 for specification of the protos.

    Args:
        proto_dict: Dictionary representing the proto. Keys are always
            strings, but values may be types correspond to a raw proto type
            or another dictionary (for messages).

    Returns:
        The operation.

    Raises:
        ValueError if the dictionary does not contain required values
        corresponding to the proto.
    """
    def raise_missing_fields(gate_name: str):
        raise ValueError('{} missing required fields: {}'.format(
            gate_name, proto_dict))

    param = _parameterized_value_from_proto_dict
    qubit = _qubit_from_proto_dict
    if 'exp_w' in proto_dict:
        exp_w = proto_dict['exp_w']
        if ('half_turns' not in exp_w or 'axis_half_turns' not in exp_w
                or 'target' not in exp_w):
            raise_missing_fields('ExpW')
        return ops.PhasedXPowGate(
            exponent=param(exp_w['half_turns']),
            phase_exponent=param(exp_w['axis_half_turns']),
        ).on(qubit(exp_w['target']))
    if 'exp_z' in proto_dict:
        exp_z = proto_dict['exp_z']
        if 'half_turns' not in exp_z or 'target' not in exp_z:
            raise_missing_fields('ExpZ')
        return ops.Z(qubit(exp_z['target']))**param(exp_z['half_turns'])
    if 'exp_11' in proto_dict:
        exp_11 = proto_dict['exp_11']
        if ('half_turns' not in exp_11 or 'target1' not in exp_11
                or 'target2' not in exp_11):
            raise_missing_fields('Exp11')
        return ops.CZ(qubit(exp_11['target1']),
                      qubit(exp_11['target2']))**param(exp_11['half_turns'])
    if 'measurement' in proto_dict:
        meas = proto_dict['measurement']
        invert_mask = cast(Tuple[Any, ...], ())
        if 'invert_mask' in meas:
            invert_mask = tuple(
                _load_json_bool(x) for x in meas['invert_mask'])
        if 'key' not in meas or 'targets' not in meas:
            raise_missing_fields('Measurement')
        return ops.MeasurementGate(
            num_qubits=len(meas['targets']),
            key=meas['key'],
            invert_mask=invert_mask).on(*[qubit(q) for q in meas['targets']])

    raise ValueError('invalid operation: {}'.format(proto_dict))
Esempio n. 10
0
def test_expw_matrix(half_turns, axis_half_turns):
    m1 = xmon_gates.ExpWGate(half_turns=half_turns,
                                axis_half_turns=axis_half_turns).matrix
    if (version[0] == 0 and version[1] <= 3):
        m2 = google.ExpWGate(half_turns=half_turns,
                            axis_half_turns=axis_half_turns).matrix()
    else:
         m2 = unitary(ops.PhasedXPowGate(exponent=half_turns,
                            phase_exponent=axis_half_turns))
    nptest.assert_array_almost_equal(m1, m2)
Esempio n. 11
0
def _dump_held(
        qubits: Iterable[ops.Qid],
        held_w_phases: Dict[ops.Qid, value.TParamVal]) -> 'cirq.OP_TREE':
    # Note: sorting is to avoid non-determinism in the insertion order.
    for q in sorted(qubits):
        p = held_w_phases.get(q)
        if p is not None:
            dump_op = ops.PhasedXPowGate(phase_exponent=p).on(q)
            yield dump_op
        held_w_phases.pop(q, None)
Esempio n. 12
0
def decompose_swap_into_syc(a: ops.Qid, b: ops.Qid):
    """Decompose SWAP into sycamore gates using precomputed coefficients"""
    yield ops.PhasedXPowGate(phase_exponent=0.44650378384076217, exponent=0.8817921214052824).on(a)
    yield ops.PhasedXPowGate(phase_exponent=-0.7656774060816165, exponent=0.6628666504604785).on(b)
    yield google.SYC.on(a, b)
    yield ops.PhasedXPowGate(phase_exponent=-0.6277589946716742, exponent=0.5659160932099687).on(a)
    yield google.SYC.on(a, b)
    yield ops.PhasedXPowGate(phase_exponent=0.28890767199499257, exponent=0.4340839067900317).on(b)
    yield ops.PhasedXPowGate(phase_exponent=-0.22592784059288928).on(a)
    yield google.SYC.on(a, b)
    yield ops.PhasedXPowGate(phase_exponent=-0.4691261557936808, exponent=0.7728525693920243).on(a)
    yield ops.PhasedXPowGate(phase_exponent=-0.8150261316932077, exponent=0.11820787859471782).on(b)
    yield (ops.Z ** -0.7384700844660306).on(b)
    yield (ops.Z ** -0.7034535141382525).on(a)
Esempio n. 13
0
def decompose_iswap_into_syc(a: ops.Qid, b: ops.Qid):
    """Decompose ISWAP into sycamore gates using precomputed coefficients"""
    yield ops.PhasedXPowGate(phase_exponent=-0.27250925776964596, exponent=0.2893438375555899).on(a)
    yield google.SYC.on(a, b)
    yield ops.PhasedXPowGate(phase_exponent=0.8487591858680898, exponent=0.9749387200813147).on(b),
    yield ops.PhasedXPowGate(phase_exponent=-0.3582574564210601).on(a),
    yield google.SYC.on(a, b)
    yield ops.PhasedXPowGate(phase_exponent=0.9675022326694225, exponent=0.6908986856555526).on(a),
    yield google.SYC.on(a, b),
    yield ops.PhasedXPowGate(phase_exponent=0.9161706861686068, exponent=0.14818318325264102).on(b),
    yield ops.PhasedXPowGate(phase_exponent=0.9408341606787907).on(a),
    yield (ops.Z ** -1.1551880579397293).on(b),
    yield (ops.Z ** 0.31848343246696365).on(a),
def _expWGate(cmd, mapping, qubits):
    """
    Translate a ExpW gate into a Cirq gate.

    Args:
        - cmd (:class:`projectq.ops.Command`) - a projectq command instance
        - mapping (:class:`dict`) - a dictionary of qubit mappings
        - qubits (list of :class:cirq.QubitID`) - cirq qubits

    Returns:
        - :class:`cirq.Operation`
    """
    qb_pos = [mapping[qb.id] for qr in cmd.qubits for qb in qr]
    assert len(qb_pos) == 1
    cirqGate = cop.PhasedXPowGate(exponent=cmd.gate.angle / cmath.pi,
                                  phase_exponent=cmd.gate.axis_angle /
                                  cmath.pi)
    if get_control_count(cmd) > 0:
        ctrl_pos = [mapping[qb.id] for qb in cmd.control_qubits]
        return cop.ControlledGate(cirqGate)(
            *[qubits[c] for c in ctrl_pos + qb_pos])
    else:
        return cirqGate(*[qubits[idx] for idx in qb_pos])
def decompose_phased_iswap_into_syc_precomputed(theta: float, a: ops.Qid,
                                                b: ops.Qid) -> ops.OP_TREE:
    """Decompose PhasedISwap into sycamore gates using precomputed coefficients.

    This should only be called if the Gate has a phase_exponent of .25. If the
    gate has an exponent of 1, decompose_phased_iswap_into_syc should be used
    instead. Converting PhasedISwap gates to Sycamore is not supported if
    neither of these constraints are satisfied.

    This synthesize a PhasedISwap in terms of four sycamore gates.  This
    compilation converts the gate into a circuit involving two CZ gates, which
    themselves are each represented as two Sycamore gates and single-qubit
    rotations

    Args:
        theta: rotation parameter
        a: First qubit id to operate on
        b: Second qubit id to operate on
    Returns:
        a Cirq program implementing the Phased ISWAP gate

    """

    yield ops.PhasedXPowGate(phase_exponent=0.41175161497166024,
                             exponent=0.5653807577895922).on(a)
    yield ops.PhasedXPowGate(phase_exponent=1.0, exponent=0.5).on(b),
    yield (ops.Z**0.7099892314883478).on(b),
    yield (ops.Z**0.6746023442550453).on(a),
    yield SycamoreGate().on(a, b)
    yield ops.PhasedXPowGate(phase_exponent=-0.5154334589432878,
                             exponent=0.5228733015013345).on(b)
    yield ops.PhasedXPowGate(phase_exponent=0.06774925307475355).on(a)
    yield SycamoreGate().on(a, b),
    yield ops.PhasedXPowGate(phase_exponent=-0.5987667922766213,
                             exponent=0.4136540654256824).on(a)
    yield (ops.Z**-0.9255092746611595).on(b)
    yield (ops.Z**-1.333333333333333).on(a)
    yield ops.rx(-theta).on(a)
    yield ops.rx(-theta).on(b)

    yield ops.PhasedXPowGate(phase_exponent=0.5678998743900456,
                             exponent=0.5863459345743176).on(a)
    yield ops.PhasedXPowGate(phase_exponent=0.3549946157441739).on(b)
    yield SycamoreGate().on(a, b)
    yield ops.PhasedXPowGate(phase_exponent=-0.5154334589432878,
                             exponent=0.5228733015013345).on(b)
    yield ops.PhasedXPowGate(phase_exponent=0.06774925307475355).on(a)
    yield SycamoreGate().on(a, b)
    yield ops.PhasedXPowGate(phase_exponent=-0.8151665352515929,
                             exponent=0.8906746535691492).on(a)
    yield ops.PhasedXPowGate(phase_exponent=-0.07449072533884049,
                             exponent=0.5).on(b)
    yield (ops.Z**-0.9255092746611595).on(b)
    yield (ops.Z**-0.9777346353961884).on(a)
Esempio n. 16
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
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, _: ops.CZPowGate()(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 = circuits.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, _: ops.CZPowGate()(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 = circuits.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
Esempio n. 19
0
 def random_one_qubit_gate():
     return ops.PhasedXPowGate(phase_exponent=prng.rand(),
                               exponent=prng.rand())