Ejemplo n.º 1
0
def test_unitary_eig(matrix):
    # np.linalg.eig(matrix) won't work for the perturbed matrix
    d, vecs = unitary_eig(matrix)

    # test both unitarity and correctness of decomposition
    np.testing.assert_allclose(matrix,
                               vecs @ np.diag(d) @ vecs.conj().T,
                               atol=1e-14)
Ejemplo n.º 2
0
def test_non_unitary_eig():
    with pytest.raises(Exception):
        unitary_eig(
            np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 0, 1, 2], [3, 4, 5, 6]]))
Ejemplo n.º 3
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