Ejemplo n.º 1
0
def decompose_arbitrary_into_syc_analytic(qubit_a: ops.Qid, qubit_b: ops.Qid,
                                          op: ops.GateOperation):
    """Synthesize an arbitrary 2 qubit operation to a sycamore operation using
    the given Tabulation.

     Args:
            qubit_a: first qubit of the operation
            qubit_b: second qubit of the operation
            op: operation to decompose
            tabulation: A tabulation for the Sycamore gate.
        Returns:
            New operations iterable object
     """
    new_ops = optimizers.two_qubit_matrix_to_operations(op.qubits[0],
                                                        op.qubits[1],
                                                        op,
                                                        allow_partial_czs=True)
    gate_ops = []
    for new_op in new_ops:
        num_qubits = len(new_op.qubits)
        if num_qubits == 1:
            gate_ops.extend([
                term.on(new_op.qubits[0])
                for term in optimizers.single_qubit_matrix_to_gates(
                    protocols.unitary(new_op))
            ])
        elif num_qubits == 2:
            gate_ops.extend(
                ops.flatten_to_ops(
                    known_two_q_operations_to_sycamore_operations(
                        new_op.qubits[0], new_op.qubits[1],
                        cast(ops.GateOperation, new_op))))
    return gate_ops
def decompose_arbitrary_into_syc_analytic(qubit_a: ops.Qid, qubit_b: ops.Qid,
                                          op: ops.Operation) -> ops.OP_TREE:
    """Synthesize an arbitrary 2 qubit operation to a sycamore operation using
    the given Tabulation.

     Args:
            qubit_a: first qubit of the operation
            qubit_b: second qubit of the operation
            op: operation to decompose
            tabulation: A tabulation for the Sycamore gate.
        Returns:
            New operations iterable object
    """
    new_ops = optimizers.two_qubit_matrix_to_operations(qubit_a,
                                                        qubit_b,
                                                        op,
                                                        allow_partial_czs=True)
    for new_op in new_ops:
        num_qubits = len(new_op.qubits)
        if num_qubits == 1:
            (a, ) = new_op.qubits
            yield from _phased_x_z_ops(protocols.unitary(new_op), a)
        elif num_qubits == 2:
            a, b = op.qubits
            yield from ops.flatten_to_ops(
                known_two_q_operations_to_sycamore_operations(a, b, new_op))
Ejemplo n.º 3
0
    def _convert_one(self, op: 'cirq.Operation') -> 'cirq.OP_TREE':
        # Known matrix?
        mat = protocols.unitary(op, None) if len(op.qubits) <= 2 else None
        if mat is not None and len(op.qubits) == 1:
            gates = optimizers.single_qubit_matrix_to_phased_x_z(mat)
            return [g.on(op.qubits[0]) for g in gates]
        if mat is not None and len(op.qubits) == 2:
            return optimizers.two_qubit_matrix_to_operations(
                op.qubits[0], op.qubits[1], mat, allow_partial_czs=True, clean_operations=False
            )

        return NotImplemented
Ejemplo n.º 4
0
    def intercept_decompose_func(self, op: ops.Operation) -> ops.OP_TREE:
        # Drop measurements.
        if protocols.is_measurement(op):
            return []

        # Immediately completely decompose two qubit unitary operations.
        if len(op.qubits) == 2:
            m = protocols.unitary(op, None)
            if m is not None:
                return optimizers.two_qubit_matrix_to_operations(
                    op.qubits[0], op.qubits[1], mat=m, allow_partial_czs=False)

        # Fallback to default.
        return NotImplemented
Ejemplo n.º 5
0
def _two_qubit_multiplexor_to_ops(
    q0: ops.Qid,
    q1: ops.Qid,
    q2: ops.Qid,
    u1: np.ndarray,
    u2: np.ndarray,
    shift_left: bool = True,
    diagonal: Optional[np.ndarray] = None,
    atol: float = 1e-8,
) -> Tuple[Optional[np.ndarray], List[ops.Operation]]:
    r"""Converts a two qubit double multiplexor to circuit.
    Input: U_1 ⊕ U_2, with select qubit a (i.e. a = |0> => U_1(b,c),
    a = |1> => U_2(b,c).

    We want this:
        $$
        U_1 ⊕ U_2 = (V ⊕ V) @ (D ⊕ D^{\dagger}) @ (W ⊕ W)
        $$
    We can get it via:
        $$
        U_1 = V @ D @ W       (1)
        U_2 = V @ D^{\dagger} @ W (2)
        $$

    We can derive
        $$
        U_1 U_2^{\dagger}= V @ D^2 @ V^{\dagger}, (3)
        $$

    i.e the eigendecomposition of $U_1 U_2^{\dagger}$ will give us D and V.
    W is easy to derive from (2).

    This function, after calculating V, D and W, also returns the circuit that
    implements these unitaries: V, W on qubits b, c and the middle diagonal
    multiplexer on a,b,c qubits.

    The resulting circuit will have only known two-qubit and one-qubit gates,
    namely CZ, CNOT and rx, ry, PhasedXPow gates.

    Args:
        q0: first qubit
        q1: second qubit
        q2: third qubit
        u1: two-qubit operation on b,c for a = |0>
        u2: two-qubit operation on b,c for a = |1>
        shift_left: return the extracted diagonal or not
        diagonal: an incoming diagonal to be merged with
    Returns:
        The circuit implementing the two qubit multiplexor consisting only of
        known two-qubit and single qubit gates
    """
    u1u2 = u1 @ u2.conj().T
    eigvals, v = cirq.unitary_eig(u1u2)
    d = np.diag(np.sqrt(eigvals))

    w = d @ v.conj().T @ u2

    circuit_u1u2_mid = _middle_multiplexor_to_ops(q0, q1, q2, eigvals)

    if diagonal is not None:
        v = diagonal @ v

    d_v, circuit_u1u2_r = opt.two_qubit_matrix_to_diagonal_and_operations(
        q1, q2, v, atol=atol)

    w = d_v @ w

    # if it's interesting to extract the diagonal then let's do it
    if shift_left:
        d_w, circuit_u1u2_l = opt.two_qubit_matrix_to_diagonal_and_operations(
            q1, q2, w, atol=atol)
    # if we are at the end of the circuit, then just fall back to KAK
    else:
        d_w = None
        circuit_u1u2_l = opt.two_qubit_matrix_to_operations(
            q1, q2, w, allow_partial_czs=False, atol=atol)

    return d_w, circuit_u1u2_l + circuit_u1u2_mid + circuit_u1u2_r
Ejemplo n.º 6
0
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.
        .
    """
    num_of_qubits = len(target_qubits)

    if np.all(np.equal(matrix, np.eye(2**num_of_qubits))):
        # Simple Identity Check
        ops_list = []
        for qubit in target_qubits:
            op = cirq.Z(qubit)**0
            cirq.google.Sycamore.validate_operation(op)
            ops_list.append(cirq.Z(qubit)**0)
        return ops_list, []

    if num_of_qubits == 1:
        # single qubit gates
        gate = optimizers.single_qubit_matrix_to_phxz(matrix)
        cirq.google.Sycamore.validate_operation(gate(target_qubits[0]))
        return [gate(target_qubits[0])], []

    elif num_of_qubits == 2:
        # two qubit gates
        ops_list = optimizers.two_qubit_matrix_to_operations(
            target_qubits[0], target_qubits[1], matrix, allow_partial_czs=True)
        ConvertToSycamoreGates = cirq.google.ConvertToSycamoreGates()
        converted_ops_list = ConvertToSycamoreGates.convert(op=ops_list)
        return converted_ops_list, []

    elif is_incremental(matrix):
        ancilla = find_neighbor_available_qubit(target_qubits)
        return decompose_incrementer_matrix(target_qubits, ancilla), [ancilla]

    elif num_of_qubits == 3:
        # three qubit gates
        ops_list = optimizers.three_qubit_matrix_to_operations(
            target_qubits[0], target_qubits[1], target_qubits[2], matrix)

        return ops_list, []

    elif np.count_nonzero(matrix - np.diag(np.diagonal(matrix))) == 0:
        # diagonal gates with more than 3 qubits
        angle_list = []
        for i in np.arange(np.shape(matrix)[0]):
            angle_list.append(np.angle(matrix[i, i]))
        diagonal_gate = cirq.ops.DiagonalGate(angle_list)
        ops_list = diagonal_gate._decompose_(qubits=target_qubits)

        return ops_list, []

    elif num_of_qubits == 4:
        ancilla = find_neighbor_available_qubit(target_qubits)
        CTOFFLI_mat = cirq.unitary(
            cirq.ops.ControlledOperation(controls=[
                target_qubits[0], target_qubits[1], target_qubits[2]
            ],
                                         sub_operation=cirq.X(
                                             target_qubits[3])))
        if np.all(np.equal(matrix, CTOFFLI_mat)):
            ops_list = []
            ConvertToSycamoreGates = cirq.google.ConvertToSycamoreGates()
            decomposed_ops = cirq.optimizers.decompose_multi_controlled_x(
                controls=[
                    target_qubits[2], target_qubits[1], target_qubits[0]
                ],
                target=target_qubits[3],
                free_qubits=[ancilla])
            ops_list.append(ConvertToSycamoreGates.convert(decomposed_ops))
            return ops_list, [ancilla]

    else:
        ops_list = []
        for qubit in target_qubits:
            op = cirq.Z(qubit)**0
            cirq.google.Sycamore.validate_operation(op)
            ops_list.append(cirq.Z(qubit)**0)
        return ops_list, []
Ejemplo n.º 7
0
    def known_two_q_operations_to_sycamore_operations(
            self, qubit_a: ops.Qid, qubit_b: ops.Qid,
            op: ops.GateOperation) -> ops.OP_TREE:
        """
        Synthesize a known gate operation to a sycamore operation

        This function dispatches based on gate type

        Args:
            qubit_a: first qubit of GateOperation
            qubit_b: second qubit of GateOperation
            op: operation to decompose
        Returns:
            New operations iterable object
        """
        gate = op.gate
        if isinstance(gate, ops.CNotPowGate):
            return [
                ops.Y(qubit_b)**-0.5,
                cphase(
                    cast(ops.CNotPowGate, gate).exponent * np.pi, qubit_a,
                    qubit_b),
                ops.Y(qubit_b)**0.5,
            ]
        elif isinstance(gate, ops.CZPowGate):
            gate = cast(ops.CZPowGate, gate)
            if math.isclose(gate.exponent, 1.0):  # check if CZ or CPHASE
                return decompose_cz_into_syc(qubit_a, qubit_b)
            else:
                # because CZPowGate == diag([1, 1, 1, e^{i pi phi}])
                return cphase(gate.exponent * np.pi, qubit_a, qubit_b)
        elif isinstance(gate, ops.SwapPowGate) and math.isclose(
                cast(ops.SwapPowGate, gate).exponent, 1.0):
            return decompose_swap_into_syc(qubit_a, qubit_b)
        elif isinstance(gate, ops.ISwapPowGate) and math.isclose(
                cast(ops.ISwapPowGate, gate).exponent, 1.0):
            return decompose_iswap_into_syc(qubit_a, qubit_b)
        elif isinstance(gate, ops.ZZPowGate):
            return rzz(
                cast(ops.ZZPowGate, gate).exponent * np.pi / 2, *op.qubits)
        elif isinstance(gate, ops.MatrixGate) and len(op.qubits) == 2:
            new_ops = optimizers.two_qubit_matrix_to_operations(
                op.qubits[0], op.qubits[1], op, allow_partial_czs=True)
            gate_ops = []
            for new_op in new_ops:
                num_qubits = len(new_op.qubits)
                if num_qubits == 1:
                    gate_ops.extend([
                        term.on(new_op.qubits[0])
                        for term in optimizers.single_qubit_matrix_to_gates(
                            protocols.unitary(new_op))
                    ])
                elif num_qubits == 2:
                    gate_ops.extend(
                        ops.flatten_to_ops(
                            self.known_two_q_operations_to_sycamore_operations(
                                new_op.qubits[0], new_op.qubits[1],
                                cast(ops.GateOperation, new_op))))
            return gate_ops
        else:
            raise ValueError("Unrecognized gate: {!r}".format(op))