Esempio n. 1
0
def num_cnots_required(u: np.ndarray, atol: float = 1e-8) -> int:
    """Returns the min number of CNOT/CZ gates required by a two-qubit unitary.

    See Proposition III.1, III.2, III.3 in Shende et al. “Recognizing Small-
    Circuit Structure in Two-Qubit Operators and Timing Hamiltonians to Compute
    Controlled-Not Gates”.  https://arxiv.org/abs/quant-ph/0308045

    Args:
        u: A two-qubit unitary.
        atol: The absolute tolerance used to make this judgement.

    Returns:
        The number of CNOT or CZ gates required to implement the unitary.

    Raises:
        ValueError: If the shape of `u` is not 4 by 4.
    """
    if u.shape != (4, 4):
        raise ValueError(
            f"Expected unitary of shape (4,4), instead got {u.shape}")
    g = _gamma(transformations.to_special(u))
    # see Fadeev-LeVerrier formula
    a3 = -np.trace(g)
    # no need to check a2 = 6, as a3 = +-4 only happens if the eigenvalues are
    # either all +1 or -1, which unambiguously implies that a2 = 6
    if np.abs(a3 - 4) < atol or np.abs(a3 + 4) < atol:
        return 0
    # see Fadeev-LeVerrier formula
    a2 = (a3 * a3 - np.trace(g @ g)) / 2
    if np.abs(a3) < atol and np.abs(a2 - 2) < atol:
        return 1
    if np.abs(a3.imag) < atol:
        return 2
    return 3
Esempio n. 2
0
def extract_right_diag(u: np.ndarray) -> np.ndarray:
    """Extract a diagonal unitary from a 3-CNOT two-qubit unitary.

    Returns a 2-CNOT unitary D that is diagonal, so that U @ D needs only
    two CNOT gates in case the original unitary is a 3-CNOT unitary.

    See Proposition V.2 in Minimal Universal Two-Qubit CNOT-based Circuits.
    https://arxiv.org/abs/quant-ph/0308033

    Args:
        u: three-CNOT two-qubit unitary
    Returns:
        diagonal extracted from U
    """
    t = _gamma(transformations.to_special(u).T).diagonal()
    k = np.real(t[0] + t[3] - t[1] - t[2])
    psi = np.arctan2(np.imag(np.sum(t)), k)
    f = np.exp(1j * psi)
    return np.diag([1, f, f, 1])