Exemple #1
0
def is_unital(
    phi: Union[np.ndarray, List[List[np.ndarray]]],
    rtol: float = 1e-05,
    atol: float = 1e-08,
) -> bool:
    r"""
    Determine whether the given channel is unital [WatUnital18]_.

    A map :math:`\Phi \in \text{T} \left(\mathcal{X}, \mathcal{Y} \right)` is *unital* if it
    holds that

    .. math::
        \Phi(\mathbb{I}_{\mathcal{X}}) = \mathbb{I}_{\mathcal{Y}}.

    Examples
    ==========

    Consider the channel whose Choi matrix is the swap operator. This channel is an example of a
    unital channel.

    >>> from toqito.perms import swap_operator
    >>> from toqito.channel_props import is_unital
    >>>
    >>> choi = swap_operator(3)
    >>> is_unital(choi)
    True

    Alternatively, the channel whose Choi matrix is the depolarizing channel is an example of a
    non-unital channel.

    >>> from toqito.channels import depolarizing
    >>> from toqito.channel_props import is_unital
    >>>
    >>> choi = depolarizing(4)
    >>> is_unital(choi)
    False

    References
    ==========
    .. [WatUnital18] Watrous, John.
        "The theory of quantum information."
        Chapter: Unital channels and majorization
        Cambridge University Press, 2018.

    :param phi: The channel provided as either a Choi matrix or a list of Kraus operators.
    :param rtol: The relative tolerance parameter (default 1e-05).
    :param atol: The absolute tolerance parameter (default 1e-08).
    :return: :code:`True` if the channel is unital, and :code:`False` otherwise.
    """
    # If the variable `phi` is provided as a list, we assume this is a list of Kraus operators.
    if isinstance(phi, list):
        phi = kraus_to_choi(phi)

    dim = int(np.sqrt(phi.shape[0]))

    # Channel is unital if :code:`mat` is the identity matrix.
    mat = apply_channel(np.identity(dim), phi)
    return is_identity(mat, rtol=rtol, atol=atol)
Exemple #2
0
def is_quantum_channel(
    phi: Union[np.ndarray, List[List[np.ndarray]]],
    rtol: float = 1e-05,
    atol: float = 1e-08,
) -> bool:
    r"""
    Determine whether the given input is a quantum channel [WatQC18]_.

    A map :math:`\Phi \in \text{T} \left(\mathcal{X}, \mathcal{Y} \right)` is a *quantum
    channel* for some choice of complex Euclidean spaces :math:`\mathcal{X}`
    and :math:`\mathcal{Y}`, if it holds that:

    1. :math:`\Phi` is completely positive.
    2. :math:`\Phi` is trace preserving.

    Examples
    ==========
    We can specify the input as a list of Kraus operators. Consider the map :math:`\Phi` defined as

    .. math::
        \Phi(X) = X - U X U^*

    where

    .. math::
        U = \frac{1}{\sqrt{2}}
        \begin{pmatrix}
            1 & 1 \\
            -1 & 1
        \end{pmatrix}.

    References
    ==========
    .. [WatQC18] Watrous, John.
        "The Theory of Quantum Information."
        Section: "2.2.1  Definitions and basic notions concerning channels".
        Cambridge University Press, 2018.

    :param phi: The channel provided as either a Choi matrix or a list of Kraus operators.
    :param rtol: The relative tolerance parameter (default 1e-05).
    :param atol: The absolute tolerance parameter (default 1e-08).
    :return: :code:`True` if the channel is a quantum channel, and :code:`False` otherwise.
    """
    # If the variable `phi` is provided as a list, we assume this is a list
    # of Kraus operators.
    if isinstance(phi, list):
        phi = kraus_to_choi(phi)

    # A valid quantum channel is a superoperator that is both completely
    # positive and trace-preserving.
    return is_completely_positive(phi, rtol, atol) and is_trace_preserving(
        phi, rtol, atol)
Exemple #3
0
def test_kraus_to_choi_dephasing_channel():
    """Kraus operators for dephasing channel should yield the proper Choi matrix."""
    kraus_1 = np.array([[1, 0], [0, 0]])
    kraus_2 = np.array([[1, 0], [0, 0]])
    kraus_3 = np.array([[0, 0], [0, 1]])
    kraus_4 = np.array([[0, 0], [0, 1]])

    kraus_ops = [[kraus_1, kraus_2], [kraus_3, kraus_4]]

    choi_res = kraus_to_choi(kraus_ops)
    expected_choi_res = dephasing(2)

    bool_mat = np.isclose(choi_res, expected_choi_res)
    np.testing.assert_equal(np.all(bool_mat), True)
Exemple #4
0
def test_kraus_to_choi_depolarizing_channel():
    """Kraus operators for depolarizing channel should yield the proper Choi matrix."""
    kraus_1 = np.array([[1 / np.sqrt(2), 0], [0, 0]])
    kraus_2 = np.array([[1 / np.sqrt(2), 0], [0, 0]])
    kraus_3 = np.array([[0, 0], [1 / np.sqrt(2), 0]])
    kraus_4 = np.array([[0, 0], [1 / np.sqrt(2), 0]])
    kraus_5 = np.array([[0, 1 / np.sqrt(2)], [0, 0]])
    kraus_6 = np.array([[0, 1 / np.sqrt(2)], [0, 0]])
    kraus_7 = np.array([[0, 0], [0, 1 / np.sqrt(2)]])
    kraus_8 = np.array([[0, 0], [0, 1 / np.sqrt(2)]])

    kraus_ops = [
        [kraus_1, kraus_2],
        [kraus_3, kraus_4],
        [kraus_5, kraus_6],
        [kraus_7, kraus_8],
    ]

    choi_res = kraus_to_choi(kraus_ops)
    expected_choi_res = depolarizing(2)

    bool_mat = np.isclose(choi_res, expected_choi_res)
    np.testing.assert_equal(np.all(bool_mat), True)
def test_kraus_to_choi_max_ent_2():
    """Choi matrix of the transpose map is the swap operator."""
    kraus_1 = np.array([[1, 0], [0, 0]])
    kraus_2 = np.array([[1, 0], [0, 0]]).conj().T
    kraus_3 = np.array([[0, 1], [0, 0]])
    kraus_4 = np.array([[0, 1], [0, 0]]).conj().T
    kraus_5 = np.array([[0, 0], [1, 0]])
    kraus_6 = np.array([[0, 0], [1, 0]]).conj().T
    kraus_7 = np.array([[0, 0], [0, 1]])
    kraus_8 = np.array([[0, 0], [0, 1]]).conj().T

    kraus_ops = [
        [kraus_1, kraus_2],
        [kraus_3, kraus_4],
        [kraus_5, kraus_6],
        [kraus_7, kraus_8],
    ]

    choi_res = kraus_to_choi(kraus_ops)
    expected_choi_res = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0],
                                  [0, 0, 0, 1]])

    bool_mat = np.isclose(choi_res, expected_choi_res)
    np.testing.assert_equal(np.all(bool_mat), True)
Exemple #6
0
def test_kraus_to_choi_swap_operator_non_unique():
    """As Kraus operators are non-unique, these also should yield the swap operator."""
    kraus_1 = np.array([[0, 1 / np.sqrt(2)], [1 / np.sqrt(2), 0]])
    kraus_2 = np.array([[0, 1 / np.sqrt(2)], [1 / np.sqrt(2), 0]]).conj().T
    kraus_3 = np.array([[1, 0], [0, 0]])
    kraus_4 = np.array([[1, 0], [0, 0]]).conj().T
    kraus_5 = np.array([[0, 0], [0, 1]])
    kraus_6 = np.array([[0, 0], [0, 1]]).conj().T
    kraus_7 = np.array([[0, 1 / np.sqrt(2)], [-1 / np.sqrt(2), 0]])
    kraus_8 = np.array([[0, 1 / np.sqrt(2)], [-1 / np.sqrt(2), 0]]).conj().T

    kraus_ops = [
        [kraus_1, kraus_2],
        [kraus_3, kraus_4],
        [kraus_5, kraus_6],
        [kraus_7, kraus_8],
    ]

    choi_res = kraus_to_choi(kraus_ops)
    expected_choi_res = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0],
                                  [0, 0, 0, 1]])

    bool_mat = np.isclose(choi_res, expected_choi_res)
    np.testing.assert_equal(np.all(bool_mat), True)
Exemple #7
0
def is_completely_positive(
    phi: Union[np.ndarray, List[List[np.ndarray]]],
    rtol: float = 1e-05,
    atol: float = 1e-08,
) -> bool:
    r"""
    Determine whether the given channel is completely positive [WatCP18]_.

    A map :math:`\Phi \in \text{T} \left(\mathcal{X}, \mathcal{Y} \right)` is *completely
    positive* if it holds that

    .. math::
        \Phi \otimes \mathbb{I}_{\text{L}(\mathcal{Z})}

    is a positive map for every complex Euclidean space :math:`\mathcal{Z}`.

    Alternatively, a channel is completely positive if the corresponding Choi matrix of the
    channel is both Hermitian-preserving and positive semidefinite.

    Examples
    ==========

    We can specify the input as a list of Kraus operators. Consider the map :math:`\Phi` defined as

    .. math::
        \Phi(X) = X - U X U^*

    where

    .. math::
        U = \frac{1}{\sqrt{2}}
        \begin{pmatrix}
            1 & 1 \\
            -1 & 1
        \end{pmatrix}.

    This map is not completely positive, as we can verify as follows.

    >>> from toqito.channel_props import is_completely_positive
    >>> import numpy as np
    >>> unitary_mat = np.array([[1, 1], [-1, 1]]) / np.sqrt(2)
    >>> kraus_ops = [[np.identity(2), np.identity(2)], [unitary_mat, -unitary_mat]]
    >>> is_completely_positive(kraus_ops)
    False

    We can also specify the input as a Choi matrix. For instance, consider the Choi matrix
    corresponding to the :math:`2`-dimensional completely depolarizing channel

    .. math::
        \Omega =
        \frac{1}{2}
        \begin{pmatrix}
            1 & 0 & 0 & 0 \\
            0 & 1 & 0 & 0 \\
            0 & 0 & 1 & 0 \\
            0 & 0 & 0 & 1
        \end{pmatrix}.

    We may verify that this channel is completely positive

    >>> from toqito.channels import depolarizing
    >>> from toqito.channel_props import is_completely_positive
    >>> is_completely_positive(depolarizing(2))
    True

    References
    ==========
    .. [WatCP18] Watrous, John.
        "The Theory of Quantum Information."
        Section: "Linear maps of square operators".
        Cambridge University Press, 2018.

    :param phi: The channel provided as either a Choi matrix or a list of Kraus operators.
    :param rtol: The relative tolerance parameter (default 1e-05).
    :param atol: The absolute tolerance parameter (default 1e-08).
    :return: True if the channel is completely positive, and False otherwise.
    """
    # If the variable `phi` is provided as a list, we assume this is a list
    # of Kraus operators.
    if isinstance(phi, list):
        phi = kraus_to_choi(phi)

    # Use Choi's theorem to determine whether :code:`phi` is completely positive.
    return is_herm_preserving(phi, rtol, atol) and is_positive_semidefinite(phi, rtol, atol)
Exemple #8
0
def is_herm_preserving(
    phi: Union[np.ndarray, List[List[np.ndarray]]],
    rtol: float = 1e-05,
    atol: float = 1e-08,
) -> bool:
    r"""
    Determine whether the given channel is Hermitian-preserving [WatH18]_.

    A map :math:`\Phi \in \text{T} \left(\mathcal{X}, \mathcal{Y} \right)` is
    *Hermitian-preserving* if it holds that

    .. math::
        \Phi(H) \in \text{Herm}(\mathcal{Y})

    for every Hermitian operator :math:`H \in \text{Herm}(\mathcal{X})`.

    Examples
    ==========

    The map :math:`\Phi` defined as

    .. math::
        \Phi(X) = X - U X U^*

    is Hermitian-preserving, where

    .. math::
        U = \frac{1}{\sqrt{2}}
        \begin{pmatrix}
            1 & 1 \\
            -1 & 1
        \end{pmatrix}.

    >>> import numpy as np
    >>> from toqito.channel_props import is_herm_preserving
    >>> unitary_mat = np.array([[1, 1], [-1, 1]]) / np.sqrt(2)
    >>> kraus_ops = [[np.identity(2), np.identity(2)], [unitary_mat, -unitary_mat]]
    >>> is_herm_preserving(kraus_ops)
    True

    We may also verify whether the corresponding Choi matrix of a given map is
    Hermitian-preserving. The swap operator is the Choi matrix of the transpose map, which is
    Hermitian-preserving as can be seen as follows:

    >>> import numpy as np
    >>> from toqito.perms import swap_operator
    >>> from toqito.channel_props import is_herm_preserving
    >>> unitary_mat = np.array([[1, 1], [-1, 1]]) / np.sqrt(2)
    >>> choi_mat = swap_operator(3)
    >>> is_herm_preserving(choi_mat)
    True

    References
    ==========
    .. [WatH18] Watrous, John.
        "The theory of quantum information."
        Section: "Linear maps of square operators".
        Cambridge University Press, 2018.

    :param phi: The channel provided as either a Choi matrix or a list of Kraus operators.
    :param rtol: The relative tolerance parameter (default 1e-05).
    :param atol: The absolute tolerance parameter (default 1e-08).
    :return: True if the channel is Hermitian-preserving, and False otherwise.
    """
    # If the variable `phi` is provided as a list, we assume this is a list
    # of Kraus operators.
    if isinstance(phi, list):
        phi = kraus_to_choi(phi)

    # Phi is Hermiticity-preserving if and only if its Choi matrix is Hermitian.
    if phi.shape[0] != phi.shape[1]:
        return False
    return is_hermitian(phi, rtol=rtol, atol=atol)
Exemple #9
0
def is_positive(phi: Union[np.ndarray, List[List[np.ndarray]]],
                tol: float = 1e-05) -> bool:
    r"""
    Determine whether the given channel is positive [WatPM18]_.

    A map :math:`\Phi \in \text{T} \left(\mathcal{X}, \mathcal{Y} \right)` is
    *positive* if it holds that

    .. math::
        \Phi(P) \in \text{Pos}(\mathcal{Y})

    for every positive semidefinite operator :math:`P \in \text{Pos}(\mathcal{X})`.

    Alternatively, a channel is positive if the corresponding Choi matrix of the
    channel is both Hermitian-preserving and positive semidefinite.

    Examples
    ==========

    We can specify the input as a list of Kraus operators. Consider the map
    :math:`\Phi` defined as

    .. math::
        \Phi(X) = X - U X U^*

    where

    .. math::
        U = \frac{1}{\sqrt{2}}
        \begin{pmatrix}
            1 & 1 \\
            -1 & -1
        \end{pmatrix}.

    This map is not completely positive, as we can verify as follows.

    >>> from toqito.channel_props import is_positive
    >>> import numpy as np
    >>> unitary_mat = np.array([[1, 1], [-1, -1]]) / np.sqrt(2)
    >>> kraus_ops = [[np.identity(2), np.identity(2)], [unitary_mat, -unitary_mat]]
    >>> is_positive(kraus_ops)
    False

    We can also specify the input as a Choi matrix. For instance, consider the
    Choi matrix corresponding to the :math:`4`-dimensional completely
    depolarizing channel and may verify that this channel is positive.

    >>> from toqito.channels import depolarizing
    >>> from toqito.channel_props import is_positive
    >>> is_positive(depolarizing(4))
    True

    References
    ==========
    .. [WatPM18] Watrous, John.
        "The theory of quantum information."
        Section: "Linear maps of square operators".
        Cambridge University Press, 2018.

    :param phi: The channel provided as either a Choi matrix or a list of
                Kraus operators.
    :param tol: The tolerance parameter to determine positivity.
    :return: True if the channel is positive, and False otherwise.
    """
    # If the variable `phi` is provided as a list, we assume this is a list
    # of Kraus operators.
    if isinstance(phi, list):
        phi = kraus_to_choi(phi)
    return is_psd(phi, tol)
Exemple #10
0
def choi_rank(phi: Union[np.ndarray, List[List[np.ndarray]]]) -> int:
    r"""
    Calculate the rank of the Choi representation of a quantum channel [WatChoiRank18]_.

    Examples
    ==========

    The transpose map can be written either in Choi representation (as a
    SWAP operator) or in Kraus representation. If we choose the latter, it
    will be given by the following matrices:

    .. math::
        \begin{equation}
            \begin{aligned}
                \frac{1}{\sqrt{2}}
                \begin{pmatrix}
                    0 & i \\ -i & 0
                \end{pmatrix}, &\quad
                \frac{1}{\sqrt{2}}
                \begin{pmatrix}
                    0 & 1 \\
                    1 & 0
                \end{pmatrix}, \\
                \begin{pmatrix}
                    1 & 0 \\
                    0 & 0
                \end{pmatrix}, &\quad
                \begin{pmatrix}
                    0 & 0 \\
                    0 & 1
                \end{pmatrix}.
            \end{aligned}
        \end{equation}

    and can be generated in :code:`toqito` with the following list:

    >>> import numpy as np
    >>> kraus_1 = np.array([[1, 0], [0, 0]])
    >>> kraus_2 = np.array([[1, 0], [0, 0]]).conj().T
    >>> kraus_3 = np.array([[0, 1], [0, 0]])
    >>> kraus_4 = np.array([[0, 1], [0, 0]]).conj().T
    >>> kraus_5 = np.array([[0, 0], [1, 0]])
    >>> kraus_6 = np.array([[0, 0], [1, 0]]).conj().T
    >>> kraus_7 = np.array([[0, 0], [0, 1]])
    >>> kraus_8 = np.array([[0, 0], [0, 1]]).conj().T
    >>> kraus_ops = [
    >>>     [kraus_1, kraus_2],
    >>>     [kraus_3, kraus_4],
    >>>     [kraus_5, kraus_6],
    >>>     [kraus_7, kraus_8],
    >>> ]

    To calculate its Choi rank, we proceed in the following way:

    >>> from toqito.channel_props import choi_rank
    >>> choi_rank(kraus_ops)
    4

    We can the verify the associated Choi representation (the SWAP gate)
    gets the same Choi rank:

    >>> choi_matrix = np.array([[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]])
    >>> choi_rank(choi_matrix)
    4

    References
    ==========

    .. [WatChoiRank18] Watrous, John.
        "The Theory of Quantum Information."
        Section: "2.2 Quantum Channels".
        Cambridge University Press, 2018.

    :param phi: Either a Choi matrix or a list of Kraus operators
    :return: The Choi rank of the provided channel representation.
    """
    if isinstance(phi, list):
        phi = kraus_to_choi(phi)
    elif not isinstance(phi, np.ndarray):
        raise ValueError("Not a valid Choi matrix.")

    return np.linalg.matrix_rank(phi)