Ejemplo n.º 1
0
def two_qubit_matrix_to_operations(
    q0: ops.QubitId,
    q1: ops.QubitId,
    mat: np.ndarray,
    allow_partial_czs: bool,
    tolerance: float = 1e-8,
    clean_operations: bool = True,
) -> List[ops.Operation]:
    """Decomposes a two-qubit operation into Z/XY/CZ gates.

    Args:
        q0: The first qubit being operated on.
        q1: The other qubit being operated on.
        mat: Defines the operation to apply to the pair of qubits.
        allow_partial_czs: Enables the use of Partial-CZ gates.
        tolerance: A limit on the amount of error introduced by the
            construction.
        clean_operations: Enables optimizing resulting operation list by
            merging operations and ejecting phased Paulis and Z operations.

    Returns:
        A list of operations implementing the matrix.
    """
    kak = linalg.kak_decomposition(mat, linalg.Tolerance(atol=tolerance))
    operations = _kak_decomposition_to_operations(q0, q1, kak,
                                                  allow_partial_czs, tolerance)
    if clean_operations:
        return _cleanup_operations(operations)
    else:
        return operations
Ejemplo n.º 2
0
def two_qubit_matrix_to_operations(q0: ops.QubitId,
                                   q1: ops.QubitId,
                                   mat: np.ndarray,
                                   allow_partial_czs: bool,
                                   tolerance: float = 1e-8
                                   ) -> List[ops.Operation]:
    """Decomposes a two-qubit operation into Z/XY/CZ gates.

    Args:
        q0: The first qubit being operated on.
        q1: The other qubit being operated on.
        mat: Defines the operation to apply to the pair of qubits.
        allow_partial_czs: Enables the use of Partial-CZ gates.
        tolerance: A limit on the amount of error introduced by the
            construction.

    Returns:
        A list of operations implementing the matrix.
    """
    _, (a0, a1), (x, y, z), (b0, b1) = linalg.kak_decomposition(
        mat,
        linalg.Tolerance(atol=tolerance))
    # TODO: Clean up angles before returning
    return _kak_decomposition_to_operations(q0, q1,
                                            a0, a1, x, y, z, b0, b1,
                                            allow_partial_czs, tolerance)
Ejemplo n.º 3
0
 def from_matrix(mat: np.array, tolerance=1e-8) -> 'QasmTwoQubitGate':
     _, (a1, a0), (x, y, z), (b1, b0) = linalg.kak_decomposition(
         mat, linalg.Tolerance(atol=tolerance))
     before0 = QasmUGate.from_matrix(b0)
     before1 = QasmUGate.from_matrix(b1)
     after0 = QasmUGate.from_matrix(a0)
     after1 = QasmUGate.from_matrix(a1)
     return QasmTwoQubitGate(before0, before1, x, y, z, after0, after1)
Ejemplo n.º 4
0
    def from_matrix(mat: np.array, atol=1e-8) -> 'QasmTwoQubitGate':
        """Creates a QasmTwoQubitGate from the given matrix.

        Args:
            mat: The unitary matrix of the two qubit gate.
            atol: Absolute error tolerance when decomposing.

        Returns:
            A QasmTwoQubitGate implementing the matrix.
        """
        kak = linalg.kak_decomposition(mat, linalg.Tolerance(atol=atol))
        return QasmTwoQubitGate(kak)
Ejemplo n.º 5
0
    def from_matrix(mat: np.array, tolerance=1e-8) -> 'QasmTwoQubitGate':
        kak = linalg.kak_decomposition(mat, linalg.Tolerance(atol=tolerance))

        a1, a0 = kak.single_qubit_operations_after
        b1, b0 = kak.single_qubit_operations_before
        x, y, z = kak.interaction_coefficients

        before0 = QasmUGate.from_matrix(b0)
        before1 = QasmUGate.from_matrix(b1)
        after0 = QasmUGate.from_matrix(a0)
        after1 = QasmUGate.from_matrix(a1)
        return QasmTwoQubitGate(before0, before1,
                                x, y, z,
                                after0, after1)
Ejemplo n.º 6
0
 def __init__(self,
              ignore_failures: bool = False,
              tolerance: float = 0) -> None:
     """
     Args:
         ignore_failures: If set, gates that fail to convert are forwarded
             unchanged. If not set, conversion failures raise a TypeError.
         tolerance: Maximum absolute error tolerance. The optimization is
             permitted to round angles with a threshold determined by this
             tolerance.
     """
     super().__init__()
     self.ignore_failures = ignore_failures
     self.tolerance = tolerance
     self._tol = linalg.Tolerance(atol=tolerance)
Ejemplo n.º 7
0
 def __init__(self,
              ignore_failures: bool = False,
              tolerance: float = 0,
              extensions: extension.Extensions = None) -> None:
     """
     Args:
         ignore_failures: If set, gates that fail to convert are forwarded
             unchanged. If not set, conversion failures raise a TypeError.
         tolerance: Maximum absolute error tolerance. The optimization is
             permitted to round angles with a threshold determined by this
             tolerance.
         extensions: The extensions instance to use when trying to
             cast gates to known types.
     """
     self.extensions = extensions or extension.Extensions()
     self.ignore_failures = ignore_failures
     self.tolerance = tolerance
     self._tol = linalg.Tolerance(atol=tolerance)
Ejemplo n.º 8
0
def single_qubit_matrix_to_pauli_rotations(
        mat: np.ndarray,
        tolerance: float = 0) -> List[Tuple[ops.Pauli, float]]:
    """Implements a single-qubit operation with few rotations.

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

    Returns:
        A list of (Pauli, half_turns) tuples that, when applied in order,
        perform the desired operation.
    """

    tol = linalg.Tolerance(atol=tolerance)

    def is_clifford_rotation(half_turns):
        return tol.all_near_zero_mod(half_turns, 0.5)

    def to_quarter_turns(half_turns):
        return round(2 * half_turns) % 4

    def is_quarter_turn(half_turns):
        return (is_clifford_rotation(half_turns)
                and to_quarter_turns(half_turns) % 2 == 1)

    def is_half_turn(half_turns):
        return (is_clifford_rotation(half_turns)
                and to_quarter_turns(half_turns) == 2)

    def is_no_turn(half_turns):
        return (is_clifford_rotation(half_turns)
                and to_quarter_turns(half_turns) == 0)

    # Decompose matrix
    z_rad_before, y_rad, z_rad_after = (
        linalg.deconstruct_single_qubit_matrix_into_angles(mat))
    z_ht_before = z_rad_before / np.pi - 0.5
    m_ht = y_rad / np.pi
    m_pauli = ops.Pauli.X
    z_ht_after = z_rad_after / np.pi + 0.5

    # Clean up angles
    if is_clifford_rotation(z_ht_before):
        if ((is_quarter_turn(z_ht_before) or is_quarter_turn(z_ht_after)) ^
            (is_half_turn(m_ht) and is_no_turn(z_ht_before - z_ht_after))):
            z_ht_before += 0.5
            z_ht_after -= 0.5
            m_pauli = ops.Pauli.Y
        if is_half_turn(z_ht_before) or is_half_turn(z_ht_after):
            z_ht_before -= 1
            z_ht_after += 1
            m_ht = -m_ht
    if is_no_turn(m_ht):
        z_ht_before += z_ht_after
        z_ht_after = 0
    elif is_half_turn(m_ht):
        z_ht_after -= z_ht_before
        z_ht_before = 0

    # Generate operations
    rotation_list = [(ops.Pauli.Z, z_ht_before), (m_pauli, m_ht),
                     (ops.Pauli.Z, z_ht_after)]
    return [(pauli, ht) for pauli, ht in rotation_list if not is_no_turn(ht)]
Ejemplo n.º 9
0
def converted_gate_set(circuit: circuits.Circuit,
                       atol: float = 1e-7) -> circuits.Circuit:
    """Returns a new, equivalent circuit using the gate set {CliffordGate,
    PauliInteractionGate, PauliStringPhasor}.

    The circuit structure may differ because it is optimized during conversion.
    """
    xmon_circuit = google.optimized_for_xmon(circuit, allow_partial_czs=False)

    qubits = circuit.all_qubits()
    tol = linalg.Tolerance(atol=atol)

    def is_clifford_rotation(half_turns):
        return tol.all_near_zero_mod(half_turns, 0.5)

    def to_quarter_turns(half_turns):
        return round(2 * half_turns) % 4

    def is_quarter_turn(half_turns):
        return (is_clifford_rotation(half_turns)
                and to_quarter_turns(half_turns) % 2 == 1)

    def is_half_turn(half_turns):
        return (is_clifford_rotation(half_turns)
                and to_quarter_turns(half_turns) == 2)

    def is_no_turn(half_turns):
        return (is_clifford_rotation(half_turns)
                and to_quarter_turns(half_turns) == 0)

    def rotation_to_clifford_op(pauli, qubit, half_turns):
        quarter_turns = to_quarter_turns(half_turns)
        if quarter_turns == 0:
            return ops.CliffordGate.I(qubit)
        elif quarter_turns == 2:
            return ops.CliffordGate.from_pauli(pauli)(qubit)
        else:
            gate = ops.CliffordGate.from_pauli(pauli, True)(qubit)
            if quarter_turns == 3:
                gate = gate.inverse()
            return gate

    def rotation_to_non_clifford_op(pauli, qubit, half_turns):
        return PauliStringPhasor(ops.PauliString.from_single(qubit, pauli),
                                 half_turns=half_turns)

    def single_qubit_matrix_to_ops(mat, qubit):
        # Decompose matrix
        z_rad_before, y_rad, z_rad_after = (
            linalg.deconstruct_single_qubit_matrix_into_angles(mat))
        z_ht_before = z_rad_before / np.pi - 0.5
        m_ht = y_rad / np.pi
        m_pauli = ops.Pauli.X
        z_ht_after = z_rad_after / np.pi + 0.5

        # Clean up angles
        if is_clifford_rotation(z_ht_before):
            if is_quarter_turn(z_ht_before) or is_quarter_turn(z_ht_after):
                z_ht_before += 0.5
                z_ht_after -= 0.5
                m_pauli = ops.Pauli.Y
            if is_half_turn(z_ht_before) or is_half_turn(z_ht_after):
                z_ht_before -= 1
                z_ht_after += 1
                m_ht = -m_ht
        if is_no_turn(m_ht):
            z_ht_before += z_ht_after
            z_ht_after = 0
        elif is_half_turn(m_ht):
            z_ht_after -= z_ht_before
            z_ht_before = 0

        # Generate operations
        rotation_list = [(ops.Pauli.Z, z_ht_before), (m_pauli, m_ht),
                         (ops.Pauli.Z, z_ht_after)]
        is_clifford_list = [
            is_clifford_rotation(ht) for pauli, ht in rotation_list
        ]
        op_list = [
            rotation_to_clifford_op(pauli, qubit, ht)
            if is_clifford else rotation_to_non_clifford_op(pauli, qubit, ht)
            for is_clifford, (pauli,
                              ht) in zip(is_clifford_list, rotation_list)
        ]

        # Merge adjacent Clifford gates
        for i in reversed(range(len(op_list) - 1)):
            if is_clifford_list[i] and is_clifford_list[i + 1]:
                op_list[i] = op_list[i].gate.merged_with(
                    op_list[i + 1].gate)(qubit)
                is_clifford_list.pop(i + 1)
                op_list.pop(i + 1)

        # Yield non-identity ops
        for is_clifford, op in zip(is_clifford_list, op_list):
            if is_clifford and op.gate == ops.CliffordGate.I:
                continue
            yield op

    def generate_ops():
        conv_cz = ops.PauliInteractionGate(ops.Pauli.Z, False, ops.Pauli.Z,
                                           False)
        matrices = {qubit: np.eye(2) for qubit in qubits}

        def clear_matrices(qubits):
            for qubit in qubits:
                yield single_qubit_matrix_to_ops(matrices[qubit], qubit)
                matrices[qubit] = np.eye(2)

        for op in xmon_circuit.all_operations():
            # Assumes all Xmon operations are GateOperation
            gate = op.gate
            if isinstance(gate, google.XmonMeasurementGate):
                yield clear_matrices(op.qubits)
                yield ops.MeasurementGate(gate.key,
                                          gate.invert_mask)(*op.qubits)
            elif len(op.qubits) == 1:
                # Collect all one qubit rotations
                # Assumes all Xmon gates implement KnownMatrix
                qubit, = op.qubits
                matrices[qubit] = op.matrix().dot(matrices[qubit])
            elif isinstance(gate, google.Exp11Gate):
                yield clear_matrices(op.qubits)
                if gate.half_turns != 1:
                    # coverage: ignore
                    raise ValueError('Unexpected partial CZ: {}'.format(op))
                yield conv_cz(*op.qubits)
            else:
                # coverage: ignore
                raise TypeError('Unknown Xmon operation: {}'.format(op))

        yield clear_matrices(qubits)

    return circuits.Circuit.from_ops(generate_ops(),
                                     strategy=circuits.InsertStrategy.EARLIEST)