Example #1
0
def complete_acquaintance_strategy(
        qubit_order: Sequence['cirq.Qid'],
        acquaintance_size: int = 0,
        swap_gate: 'cirq.Gate' = ops.SWAP) -> 'cirq.Circuit':
    """Returns an acquaintance strategy with can handle the given number of qubits.

    Args:
        qubit_order: The qubits on which the strategy should be defined.
        acquaintance_size: The maximum number of qubits to be acted on by
        an operation.
        swap_gate: The gate used to swap logical indices.

    Returns:
        A circuit capable of implementing any set of k-local operations.

    Raises:
        ValueError: If `acquaintance_size` is negative.
    """
    if acquaintance_size < 0:
        raise ValueError('acquaintance_size must be non-negative.')
    if acquaintance_size == 0:
        return circuits.Circuit()

    if acquaintance_size > len(qubit_order):
        return circuits.Circuit()
    if acquaintance_size == len(qubit_order):
        return circuits.Circuit(acquaint(*qubit_order))

    strategy = circuits.Circuit((acquaint(q) for q in qubit_order))
    for size_to_acquaint in range(2, acquaintance_size + 1):
        expose_acquaintance_gates(strategy)
        replace_acquaintance_with_swap_network(strategy, qubit_order,
                                               size_to_acquaint, swap_gate)
    return strategy
Example #2
0
def _two_qubit_clifford_matrices(q_0: 'cirq.Qid', q_1: 'cirq.Qid',
                                 cliffords: Cliffords) -> np.ndarray:
    mats = []

    # Total number of different gates in the two-qubit Clifford group.
    clifford_group_size = 11520

    starters = []
    for idx_0 in range(24):
        subset = []
        for idx_1 in range(24):
            circuit = circuits.Circuit(
                _two_qubit_clifford_starters(q_0, q_1, idx_0, idx_1, cliffords))
            subset.append(protocols.unitary(circuit))
        starters.append(subset)
    mixers = []
    # Add the identity for the case where there is no mixer.
    mixers.append(np.eye(4))
    for idx_2 in range(1, 20):
        circuit = circuits.Circuit(
            _two_qubit_clifford_mixers(q_0, q_1, idx_2, cliffords))
        mixers.append(protocols.unitary(circuit))

    for i in range(clifford_group_size):
        idx_0, idx_1, idx_2 = _split_two_q_clifford_idx(i)
        mats.append(np.matmul(mixers[idx_2], starters[idx_0][idx_1]))

    return np.array(mats)
Example #3
0
def test_depolarizer_different_gate():
    q1 = ops.QubitId()
    q2 = ops.QubitId()
    cnot = Job(circuits.Circuit([
        circuits.Moment([ops.CNOT(q1, q2)]),
    ]))
    allerrors = DepolarizerChannel(
        probability=1.0,
        depolarizing_gates=[xmon_gates.ExpZGate(),
                            xmon_gates.ExpWGate()])
    p0 = Symbol(DepolarizerChannel._parameter_name + '0')
    p1 = Symbol(DepolarizerChannel._parameter_name + '1')
    p2 = Symbol(DepolarizerChannel._parameter_name + '2')
    p3 = Symbol(DepolarizerChannel._parameter_name + '3')

    error_sweep = (Points(p0.name, [1.0]) + Points(p1.name, [1.0]) +
                   Points(p2.name, [1.0]) + Points(p3.name, [1.0]))

    cnot_then_z = Job(
        circuits.Circuit([
            circuits.Moment([ops.CNOT(q1, q2)]),
            circuits.Moment([
                xmon_gates.ExpZGate(half_turns=p0).on(q1),
                xmon_gates.ExpZGate(half_turns=p1).on(q2)
            ]),
            circuits.Moment([
                xmon_gates.ExpWGate(half_turns=p2).on(q1),
                xmon_gates.ExpWGate(half_turns=p3).on(q2)
            ])
        ]), cnot.sweep * error_sweep)

    assert allerrors.transform_job(cnot) == cnot_then_z
Example #4
0
def _strat_commutes_from_operation(
    v1: Any,
    v2: Any,
    *,
    atol: float,
) -> Union[bool, NotImplementedType, None]:
    if not isinstance(v1, ops.Operation) or not isinstance(v2, ops.Operation):
        return NotImplemented

    if set(v1.qubits).isdisjoint(v2.qubits):
        return True

    from cirq import circuits
    circuit12 = circuits.Circuit(v1, v2)
    circuit21 = circuits.Circuit(v2, v1)

    # Don't create gigantic matrices.
    if np.product(qid_shape_protocol.qid_shape(circuit12)) > 2**10:
        return NotImplemented  # coverage: ignore

    m12 = unitary_protocol.unitary(circuit12, default=None)
    m21 = unitary_protocol.unitary(circuit21, default=None)
    if m12 is None:
        return NotImplemented
    return np.allclose(m12, m21, atol=atol)
Example #5
0
def _cleanup_operations(operations: Sequence[ops.Operation]):
    circuit = circuits.Circuit(operations)
    circuit = merge_single_qubit_gates_to_phased_x_and_z(circuit)
    circuit = eject_phased_paulis(circuit)
    circuit = eject_z(circuit)
    circuit = circuits.Circuit(circuit.all_operations(), strategy=circuits.InsertStrategy.EARLIEST)
    return list(circuit.all_operations())
Example #6
0
    def _commutes_(
        self,
        other: Any,
        *,
        atol: Union[int,
                    float] = 1e-8) -> Union[bool, NotImplementedType, None]:
        """Determine if this Operation commutes with the object"""
        if not isinstance(other, Operation):
            return NotImplemented

        if hasattr(other, 'qubits') and set(self.qubits).isdisjoint(
                other.qubits):
            return True

        from cirq import circuits

        circuit12 = circuits.Circuit(self, other)
        circuit21 = circuits.Circuit(other, self)

        # Don't create gigantic matrices.
        shape = protocols.qid_shape_protocol.qid_shape(circuit12)
        if np.product(shape) > 2**10:
            return NotImplemented  # coverage: ignore

        m12 = protocols.unitary_protocol.unitary(circuit12, default=None)
        m21 = protocols.unitary_protocol.unitary(circuit21, default=None)
        if m12 is None or m21 is None:
            return NotImplemented

        return np.allclose(m12, m21, atol=atol)
Example #7
0
def complete_acquaintance_strategy(qubit_order: Sequence[ops.Qid],
                                   acquaintance_size: int=0,
                                   ) -> circuits.Circuit:
    """
    Returns an acquaintance strategy capable of executing a gate corresponding
    to any set of at most acquaintance_size qubits.

    Args:
        qubit_order: The qubits on which the strategy should be defined.
        acquaintance_size: The maximum number of qubits to be acted on by
        an operation.

    Returns:
        An circuit capable of implementing any set of k-local
        operation.
    """
    if acquaintance_size < 0:
        raise ValueError('acquaintance_size must be non-negative.')
    elif acquaintance_size == 0:
        return circuits.Circuit(device=UnconstrainedAcquaintanceDevice)

    if acquaintance_size > len(qubit_order):
        return circuits.Circuit(device=UnconstrainedAcquaintanceDevice)
    if acquaintance_size == len(qubit_order):
        return circuits.Circuit.from_ops(
                acquaint(*qubit_order), device=UnconstrainedAcquaintanceDevice)

    strategy = circuits.Circuit.from_ops(
            (acquaint(q) for q in qubit_order),
            device=UnconstrainedAcquaintanceDevice)
    for size_to_acquaint in range(2, acquaintance_size + 1):
        expose_acquaintance_gates(strategy)
        replace_acquaintance_with_swap_network(
                strategy, qubit_order, size_to_acquaint)
    return strategy
def _split_into_unitary_then_general(
    circuit: 'cirq.Circuit',
) -> Tuple['cirq.Circuit', 'cirq.Circuit']:
    """Splits the circuit into a unitary prefix and non-unitary suffix.

    The splitting happens in a per-qubit fashion. A non-unitary operation on
    qubit A will cause later operations on A to be part of the non-unitary
    suffix, but later operations on other qubits will continue to be put into
    the unitary part (as long as those qubits have had no non-unitary operation
    up to that point).
    """
    blocked_qubits: Set[cirq.Qid] = set()
    unitary_prefix = circuits.Circuit()
    general_suffix = circuits.Circuit()
    for moment in circuit:
        unitary_part = []
        general_part = []
        for op in moment:
            qs = set(op.qubits)
            if not protocols.has_unitary(op) or not qs.isdisjoint(blocked_qubits):
                blocked_qubits |= qs

            if qs.isdisjoint(blocked_qubits):
                unitary_part.append(op)
            else:
                general_part.append(op)
        if unitary_part:
            unitary_prefix.append(ops.Moment(unitary_part))
        if general_part:
            general_suffix.append(ops.Moment(general_part))
    return unitary_prefix, general_suffix
Example #9
0
def _cleanup_operations(operations: List[ops.Operation]):
    circuit = circuits.Circuit(operations)
    optimizers.merge_single_qubit_gates.merge_single_qubit_gates_into_phased_x_z(circuit)
    optimizers.eject_phased_paulis.EjectPhasedPaulis().optimize_circuit(circuit)
    optimizers.eject_z.EjectZ().optimize_circuit(circuit)
    circuit = circuits.Circuit(circuit.all_operations(), strategy=circuits.InsertStrategy.EARLIEST)
    return list(circuit.all_operations())
Example #10
0
def split_into_matching_protocol_then_general(
    circuit: 'cirq.Circuit',
    predicate: Callable[['cirq.Operation'], bool],
) -> Tuple['cirq.Circuit', 'cirq.Circuit']:
    """Splits the circuit into a matching prefix and non-matching suffix.

    The splitting happens in a per-qubit fashion. A non-matching operation on
    qubit A will cause later operations on A to be part of the non-matching
    suffix, but later operations on other qubits will continue to be put into
    the matching part (as long as those qubits have had no non-matching operation
    up to that point).
    """
    blocked_qubits: Set[cirq.Qid] = set()
    matching_prefix = circuits.Circuit()
    general_suffix = circuits.Circuit()
    for moment in circuit:
        matching_part = []
        general_part = []
        for op in moment:
            qs = set(op.qubits)
            if not predicate(op) or not qs.isdisjoint(blocked_qubits):
                blocked_qubits |= qs

            if qs.isdisjoint(blocked_qubits):
                matching_part.append(op)
            else:
                general_part.append(op)
        if matching_part:
            matching_prefix.append(ops.Moment(matching_part))
        if general_part:
            general_suffix.append(ops.Moment(general_part))
    return matching_prefix, general_suffix
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)
Example #12
0
def test_leaves_singleton():
    m = MergeRotations(0.000001)
    q = ops.QubitId()
    c = circuits.Circuit([circuits.Moment([ops.X(q)])])

    m.optimization_at(c, 0, c.operation_at(q, 0))

    assert c == circuits.Circuit([circuits.Moment([ops.X(q)])])
Example #13
0
def test_removes_identity_sequence():
    q = ops.QubitId()
    assert_optimizes(before=circuits.Circuit([
        circuits.Moment([ops.Z(q)]),
        circuits.Moment([ops.H(q)]),
        circuits.Moment([ops.X(q)]),
        circuits.Moment([ops.H(q)]),
    ]),
                     after=circuits.Circuit())
Example #14
0
def test_leaves_big():
    m = circuits.DropNegligible(0.001)
    q = ops.QubitId()
    c = circuits.Circuit([circuits.Moment([ops.Z(q)**0.1])])
    assert m.optimization_at(c, 0, c.operation_at(q, 0)) is None

    d = circuits.Circuit(c.moments)
    m.optimize_circuit(d)
    assert d == c
Example #15
0
def test_clears_paired_cnot():
    q0 = ops.QubitId()
    q1 = ops.QubitId()
    assert_optimizes(
        before=circuits.Circuit([
            circuits.Moment([ops.CNOT(q0, q1)]),
            circuits.Moment([ops.CNOT(q0, q1)]),
        ]),
        after=circuits.Circuit())
Example #16
0
    def mapped_circuit(self, deep: bool = False) -> 'cirq.Circuit':
        """Applies all maps to the contained circuit and returns the result.

        Args:
            deep: If true, this will also call mapped_circuit on any
                CircuitOperations this object contains.

        Returns:
            The contained circuit with all other member variables (repetitions,
            qubit mapping, parameterization, etc.) applied to it. This behaves
            like `cirq.decompose(self)`, but preserving moment structure.
        """
        circuit = self.circuit.unfreeze()
        circuit = circuit.transform_qubits(lambda q: self.qubit_map.get(q, q))
        if self.repetitions < 0:
            circuit = circuit**-1
        has_measurements = protocols.is_measurement(circuit)
        if has_measurements:
            circuit = protocols.with_measurement_key_mapping(
                circuit, self.measurement_key_map)
        circuit = protocols.resolve_parameters(circuit,
                                               self.param_resolver,
                                               recursive=False)
        if deep:

            def map_deep(op: 'cirq.Operation') -> 'cirq.OP_TREE':
                return op.mapped_circuit(
                    deep=True) if isinstance(op, CircuitOperation) else op

            if self.repetition_ids is None:
                return circuit.map_operations(map_deep)
            if not has_measurements:
                return circuit.map_operations(map_deep) * abs(self.repetitions)

            # Path must be constructed from the top down.
            rekeyed_circuit = circuits.Circuit(
                protocols.with_key_path(circuit, self.parent_path + (rep, ))
                for rep in self.repetition_ids)
            return rekeyed_circuit.map_operations(map_deep)

        if self.repetition_ids is None:
            return circuit
        if not has_measurements:
            return circuit * abs(self.repetitions)

        def rekey_op(op: 'cirq.Operation', rep: str):
            """Update measurement keys in `op` to include repetition ID `rep`."""
            rekeyed_op = protocols.with_key_path(op,
                                                 self.parent_path + (rep, ))
            if rekeyed_op is NotImplemented:
                return op
            return rekeyed_op

        return circuits.Circuit(
            circuit.map_operations(lambda op: rekey_op(op, rep))
            for rep in self.repetition_ids)
Example #17
0
def test_ignores_2qubit_target():
    m = MergeRotations(0.000001)
    q = ops.QubitId()
    q2 = ops.QubitId()
    c = circuits.Circuit([
        circuits.Moment([ops.CZ(q, q2)]),
    ])

    m.optimization_at(c, 0, c.operation_at(q, 0))

    assert c == circuits.Circuit([circuits.Moment([ops.CZ(q, q2)])])
Example #18
0
def _to_circuit(program: 'cirq.CIRCUIT_LIKE') -> 'cirq.Circuit':
    result = None
    if isinstance(program, circuits.Circuit):
        # No change needed.
        result = program
    elif isinstance(program, ops.Gate):
        result = circuits.Circuit(program.on(*devices.LineQid.for_gate(program)))
    else:
        # It should be an OP_TREE.
        result = circuits.Circuit(program)
    return cast('cirq.Circuit', result)
Example #19
0
def test_clears_small():
    m = circuits.DropNegligible(0.001)
    q = ops.QubitId()
    c = circuits.Circuit([circuits.Moment([ops.Z(q)**0.000001])])

    assert (m.optimization_at(c, 0, c.operation_at(q, 0)) ==
            circuits.PointOptimizationSummary(clear_span=1,
                                              clear_qubits=[q],
                                              new_operations=[]))

    m.optimize_circuit(c)
    assert c == circuits.Circuit([circuits.Moment()])
Example #20
0
def test_drop():
    q1 = ops.QubitId()
    q2 = ops.QubitId()
    assert_optimizes(before=circuits.Circuit([
        circuits.Moment(),
        circuits.Moment(),
        circuits.Moment([ops.CNOT(q1, q2)]),
        circuits.Moment(),
    ]),
                     after=circuits.Circuit([
                         circuits.Moment([ops.CNOT(q1, q2)]),
                     ]))
Example #21
0
 def circuit(self) -> 'cirq.Circuit':
     result = circuits.Circuit()
     for col in self._sub_cell_cols_sealed():
         body = circuits.Circuit(cell.operations() for cell in col
                                 if cell is not None)
         if body:
             basis_change = circuits.Circuit(cell.basis_change()
                                             for cell in col
                                             if cell is not None)
             result += basis_change
             result += body
             result += basis_change**-1
     return result
def estimate_single_qubit_readout_errors(
        sampler: 'cirq.Sampler',
        *,
        qubits: Iterable['cirq.Qid'],
        repetitions: int = 1000) -> SingleQubitReadoutCalibrationResult:
    """Estimate single-qubit readout error.

    For each qubit, prepare the |0⟩ state and measure. Calculate how often a 1
    is measured. Also, prepare the |1⟩ state and calculate how often a 0 is
    measured. The state preparations and measurements are done in parallel,
    i.e., for the first experiment, we actually prepare every qubit in the |0⟩
    state and measure them simultaneously.

    Args:
        sampler: The quantum engine or simulator to run the circuits.
        qubits: The qubits being tested.
        repetitions: The number of measurement repetitions to perform.

    Returns:
        A SingleQubitReadoutCalibrationResult storing the readout error
        probabilities as well as the number of repetitions used to estimate
        the probabilities. Also stores a timestamp indicating the time when
        data was finished being collected from the sampler.
    """
    qubits = list(qubits)

    zeros_circuit = circuits.Circuit(ops.measure_each(*qubits, key_func=repr))
    ones_circuit = circuits.Circuit(ops.X.on_each(*qubits),
                                    ops.measure_each(*qubits, key_func=repr))

    zeros_result = sampler.run(zeros_circuit, repetitions=repetitions)
    ones_result = sampler.run(ones_circuit, repetitions=repetitions)
    timestamp = time.time()

    zero_state_errors = {
        q: np.mean(zeros_result.measurements[repr(q)])
        for q in qubits
    }
    one_state_errors = {
        q: 1 - np.mean(ones_result.measurements[repr(q)])
        for q in qubits
    }

    return SingleQubitReadoutCalibrationResult(
        zero_state_errors=zero_state_errors,
        one_state_errors=one_state_errors,
        repetitions=repetitions,
        timestamp=timestamp,
    )
Example #23
0
def test_ignores_czs_separated_by_outer_cz():
    q00 = ops.QubitId()
    q01 = ops.QubitId()
    q10 = ops.QubitId()
    assert_optimizes(
        before=circuits.Circuit([
            circuits.Moment([ops.CZ(q00, q01)]),
            circuits.Moment([ops.CZ(q00, q10)]),
            circuits.Moment([ops.CZ(q00, q01)]),
        ]),
        after=circuits.Circuit([
            circuits.Moment([ops.CZ(q00, q01)]),
            circuits.Moment([ops.CZ(q00, q10)]),
            circuits.Moment([ops.CZ(q00, q01)]),
        ]))
Example #24
0
def single_qubit_state_tomography(
    sampler: 'cirq.Sampler',
    qubit: 'cirq.Qid',
    circuit: 'cirq.AbstractCircuit',
    repetitions: int = 1000,
) -> TomographyResult:
    """Single-qubit state tomography.

    The density matrix of the output state of a circuit is measured by first
    doing projective measurements in the z-basis, which determine the
    diagonal elements of the matrix. A X/2 or Y/2 rotation is then added before
    the z-basis measurement, which determines the imaginary and real parts of
    the off-diagonal matrix elements, respectively.

    See Vandersypen and Chuang, Rev. Mod. Phys. 76, 1037 for details.

    Args:
        sampler: The quantum engine or simulator to run the circuits.
        qubit: The qubit under test.
        circuit: The circuit to execute on the qubit before tomography.
        repetitions: The number of measurements for each basis rotation.

    Returns:
        A TomographyResult object that stores and plots the density matrix.
    """
    circuit_z = circuit + circuits.Circuit(ops.measure(qubit, key='z'))
    results = sampler.run(circuit_z, repetitions=repetitions)
    rho_11 = np.mean(results.measurements['z'])
    rho_00 = 1.0 - rho_11

    circuit_x = circuits.Circuit(circuit,
                                 ops.X(qubit)**0.5, ops.measure(qubit,
                                                                key='z'))
    results = sampler.run(circuit_x, repetitions=repetitions)
    rho_01_im = np.mean(results.measurements['z']) - 0.5

    circuit_y = circuits.Circuit(circuit,
                                 ops.Y(qubit)**-0.5, ops.measure(qubit,
                                                                 key='z'))
    results = sampler.run(circuit_y, repetitions=repetitions)
    rho_01_re = 0.5 - np.mean(results.measurements['z'])

    rho_01 = rho_01_re + 1j * rho_01_im
    rho_10 = np.conj(rho_01)

    rho = np.array([[rho_00, rho_01], [rho_10, rho_11]])

    return TomographyResult(rho)
Example #25
0
def test_correct_mappings():
    a, b, c = cirq.LineQubit.range(3)
    cirq.testing.assert_equivalent_computational_basis_map(
        maps={
            0b01: 0b01,
            0b10: 0b10
        },
        circuit=circuits.Circuit(cirq.IdentityGate(num_qubits=2).on(a, b)))

    cirq.testing.assert_equivalent_computational_basis_map(
        maps={
            0b001: 0b100,
            0b010: 0b010,
            0b100: 0b001,
        },
        circuit=circuits.Circuit(cirq.SWAP(a, c), cirq.I(b)))
Example #26
0
 def __init__(self,
              circuit: circuits.Circuit = circuits.Circuit(),
              sweep: study.Sweep = study.UnitSweep,
              repetitions: int = 1) -> None:
     self.circuit = circuit
     self.sweep = sweep
     self.repetitions = repetitions
def rzz(theta: float, q0: ops.Qid, q1: ops.Qid) -> ops.OP_TREE:
    """Generate exp(-1j * theta * zz) from Sycamore gates.

    Args:
        theta: rotation parameter
        q0: First qubit id to operate on
        q1: Second qubit id to operate on
    Returns:
        a Cirq program implementing the Ising gate
    rtype:
        cirq.OP_Tree
    """
    phi = -np.pi / 24
    c_phi = np.cos(2 * phi)
    target_unitary = scipy.linalg.expm(-1j * theta * UNITARY_ZZ)

    if abs(np.cos(theta)) > c_phi:
        c2 = abs(np.sin(theta)) / c_phi
    else:
        c2 = abs(np.cos(theta)) / c_phi

    # Prepare program that has same Schmidt coeffs as exp(1j theta ZZ)
    program = circuits.Circuit(google.SYC.on(q0, q1),
                               ops.rx(2 * np.arccos(c2)).on(q1),
                               google.SYC.on(q0, q1))

    yield create_corrected_circuit(target_unitary, program, q0, q1)
Example #28
0
 def convert_circuit(self, circuit: circuits.Circuit) -> circuits.Circuit:
     new_circuit = circuits.Circuit()
     for moment in circuit:
         for op in moment.operations:
             new_circuit.append(self.convert_one(op))
     return transformers.merge_single_qubit_gates_to_phased_x_and_z(
         new_circuit)
def generate_boixo_2018_supremacy_circuits_v2(
    qubits: Iterable[devices.GridQubit], cz_depth: int, seed: int
) -> circuits.Circuit:
    """Generates Google Random Circuits v2 as in github.com/sboixo/GRCS cz_v2.

    See also https://arxiv.org/abs/1807.10749

    Args:
        qubits: qubit grid in which to generate the circuit.
        cz_depth: number of layers with CZ gates.
        seed: seed for the random instance.

    Returns:
        A circuit corresponding to instance
        inst_{n_rows}x{n_cols}_{cz_depth+1}_{seed}

    The mapping of qubits is cirq.GridQubit(j,k) -> q[j*n_cols+k]
    (as in the QASM mapping)
    """

    non_diagonal_gates = [ops.pauli_gates.X ** (1 / 2), ops.pauli_gates.Y ** (1 / 2)]
    rand_gen = random.Random(seed).random

    circuit = circuits.Circuit()

    # Add an initial moment of Hadamards
    circuit.append(ops.common_gates.H(qubit) for qubit in qubits)

    layer_index = 0
    if cz_depth:
        layer_index = _add_cz_layer(layer_index, circuit)
        # In the first moment, add T gates when possible
        for qubit in qubits:
            if not circuit.operation_at(qubit, 1):
                circuit.append(ops.common_gates.T(qubit), strategy=InsertStrategy.EARLIEST)

    for moment_index in range(2, cz_depth + 1):
        layer_index = _add_cz_layer(layer_index, circuit)
        # Add single qubit gates in the same moment
        for qubit in qubits:
            if not circuit.operation_at(qubit, moment_index):
                last_op = circuit.operation_at(qubit, moment_index - 1)
                if last_op:
                    gate = cast(ops.GateOperation, last_op).gate
                    # Add a random non diagonal gate after a CZ
                    if gate == ops.CZ:
                        circuit.append(
                            _choice(rand_gen, non_diagonal_gates).on(qubit),
                            strategy=InsertStrategy.EARLIEST,
                        )
                    # Add a T gate after a non diagonal gate
                    elif not gate == ops.T:
                        circuit.append(ops.common_gates.T(qubit), strategy=InsertStrategy.EARLIEST)

    # Add a final moment of Hadamards
    circuit.append(
        [ops.common_gates.H(qubit) for qubit in qubits], strategy=InsertStrategy.NEW_THEN_INLINE
    )

    return circuit
Example #30
0
def _fix_single_qubit_gates_around_kak_interaction(
    *,
    desired: 'cirq.KakDecomposition',
    operations: List['cirq.Operation'],
    qubits: Sequence['cirq.Qid'],
) -> Iterator['cirq.Operation']:
    """Adds single qubit operations to complete a desired interaction.

    Args:
        desired: The kak decomposition of the desired operation.
        qubits: The pair of qubits that is being operated on.
        operations: A list of operations that composes into the desired kak
            interaction coefficients, but may not have the desired before/after
            single qubit operations or the desired global phase.

    Returns:
        A list of operations whose kak decomposition approximately equals the
        desired kak decomposition.
    """
    actual = linalg.kak_decomposition(circuits.Circuit(operations))

    def dag(a: np.ndarray) -> np.ndarray:
        return np.transpose(np.conjugate(a))

    for k in range(2):
        g = ops.MatrixGate(
            dag(actual.single_qubit_operations_before[k])
            @ desired.single_qubit_operations_before[k])
        yield g(qubits[k])
    yield from operations
    for k in range(2):
        g = ops.MatrixGate(desired.single_qubit_operations_after[k] @ dag(
            actual.single_qubit_operations_after[k]))
        yield g(qubits[k])
    yield ops.GlobalPhaseOperation(desired.global_phase / actual.global_phase)