示例#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)
示例#2
0
def test_dephasing_partially_dephasing():
    """The partially dephasing channel for `p = 0.5`."""
    test_input_mat = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12],
                               [13, 14, 15, 16]])

    expected_res = np.array([[17.5, 0, 0, 0], [0, 20, 0, 0], [0, 0, 22.5, 0],
                             [0, 0, 0, 25]])

    res = apply_channel(test_input_mat, dephasing(4, 0.5))

    bool_mat = np.isclose(expected_res, res)
    np.testing.assert_equal(np.all(bool_mat), True)
示例#3
0
def test_dephasing_completely_dephasing():
    """The completely dephasing channel kills everything off diagonal."""
    test_input_mat = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12],
                               [13, 14, 15, 16]])

    expected_res = np.array([[1, 0, 0, 0], [0, 6, 0, 0], [0, 0, 11, 0],
                             [0, 0, 0, 16]])

    res = apply_channel(test_input_mat, dephasing(4))

    bool_mat = np.isclose(expected_res, res)
    np.testing.assert_equal(np.all(bool_mat), True)
示例#4
0
def test_depolarizing_complete_depolarizing():
    """Maps every density matrix to the maximally-mixed state."""
    test_input_mat = np.array(
        [[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]]
    )

    expected_res = (
        1 / 4 * np.array([[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]])
    )

    res = apply_channel(test_input_mat, depolarizing(4))

    bool_mat = np.isclose(expected_res, res)
    np.testing.assert_equal(np.all(bool_mat), True)
示例#5
0
def test_apply_channel_choi():
    """
    The swap operator is the Choi matrix of the transpose map.

    The following test is a (non-ideal, but illustrative) way of computing
    the transpose of a matrix.
    """
    test_input_mat = np.array([[1, 4, 7], [2, 5, 8], [3, 6, 9]])

    expected_res = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

    res = apply_channel(test_input_mat, swap_operator(3))

    bool_mat = np.isclose(res, expected_res)
    np.testing.assert_equal(np.all(bool_mat), True)
示例#6
0
def test_depolarizing_partially_depolarizing():
    """The partially depolarizing channel for `p = 0.5`."""
    test_input_mat = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])

    expected_res = np.array(
        [
            [17.125, 0.25, 0.375, 0.5],
            [0.625, 17.75, 0.875, 1],
            [1.125, 1.25, 18.375, 1.5],
            [1.625, 1.75, 1.875, 19],
        ]
    )

    res = apply_channel(test_input_mat, depolarizing(4, 0.5))

    bool_mat = np.isclose(expected_res, res)
    np.testing.assert_equal(np.all(bool_mat), True)
示例#7
0
def test_apply_channel_kraus():
    """
    Apply Kraus map.

    The following test computes PHI(X) where X = [[1, 2], [3, 4]] and
    where PHI is the superoperator defined by:
    Phi(X) = [[1,5],[1,0],[0,2]] X [[0,1][2,3][4,5]].conj().T -
    [[1,0],[0,0],[0,1]] X [[0,0][1,1],[0,0]].conj().T
    """
    test_input_mat = np.array([[1, 2], [3, 4]])

    kraus_1 = np.array([[1, 5], [1, 0], [0, 2]])
    kraus_2 = np.array([[0, 1], [2, 3], [4, 5]])
    kraus_3 = np.array([[-1, 0], [0, 0], [0, -1]])
    kraus_4 = np.array([[0, 0], [1, 1], [0, 0]])

    expected_res = np.array([[22, 95, 174], [2, 8, 14], [8, 29, 64]])

    res = apply_channel(test_input_mat,
                        [[kraus_1, kraus_2], [kraus_3, kraus_4]])

    bool_mat = np.isclose(res, expected_res)
    np.testing.assert_equal(np.all(bool_mat), True)
示例#8
0
def partial_channel(
    rho: np.ndarray,
    phi_map: Union[np.ndarray, List[List[np.ndarray]]],
    sys: int = 2,
    dim: Union[List[int], np.ndarray] = None,
) -> np.ndarray:
    r"""Apply channel to a subsystem of an operator [WatPMap18]_.

    Applies the operator

    .. math::
        \left(\mathbb{I} \otimes \Phi \right) \left(\rho \right).

    In other words, it is the result of applying the channel :math:`\Phi` to the
    second subsystem of :math:`\rho`, which is assumed to act on two
    subsystems of equal dimension.

    The input :code:`phi_map` should be provided as a Choi matrix.

    This function is adapted from the QETLAB package.

    Examples
    ==========

    >>> from toqito.channel_ops import partial_channel
    >>> from toqito.channels import depolarizing
    >>> rho = np.array([[0.3101, -0.0220-0.0219*1j, -0.0671-0.0030*1j, -0.0170-0.0694*1j],
    >>>                 [-0.0220+0.0219*1j, 0.1008, -0.0775+0.0492*1j, -0.0613+0.0529*1j],
    >>>                 [-0.0671+0.0030*1j, -0.0775-0.0492*1j, 0.1361, 0.0602 + 0.0062*1j],
    >>>                 [-0.0170+0.0694*1j, -0.0613-0.0529*1j, 0.0602-0.0062*1j, 0.4530]])
    >>> phi_x = partial_channel(rho, depolarizing(2))
    [[ 0.20545+0.j       0.     +0.j      -0.0642 +0.02495j  0.     +0.j     ]
     [ 0.     +0.j       0.20545+0.j       0.     +0.j      -0.0642 +0.02495j]
     [-0.0642 -0.02495j  0.     +0.j       0.29455+0.j       0.     +0.j     ]
     [ 0.     +0.j      -0.0642 -0.02495j  0.     +0.j       0.29455+0.j     ]]

    >>> from toqito.channel_ops import partial_channel
    >>> from toqito.channels import depolarizing
    >>> rho = np.array([[0.3101, -0.0220-0.0219*1j, -0.0671-0.0030*1j, -0.0170-0.0694*1j],
    >>>                 [-0.0220+0.0219*1j, 0.1008, -0.0775+0.0492*1j, -0.0613+0.0529*1j],
    >>>                 [-0.0671+0.0030*1j, -0.0775-0.0492*1j, 0.1361, 0.0602 + 0.0062*1j],
    >>>                 [-0.0170+0.0694*1j, -0.0613-0.0529*1j, 0.0602-0.0062*1j, 0.4530]])
    >>> phi_x = partial_channel(rho, depolarizing(2), 1)
    [[0.2231+0.j      0.0191-0.00785j 0.    +0.j      0.    +0.j     ]
     [0.0191+0.00785j 0.2769+0.j      0.    +0.j      0.    +0.j     ]
     [0.    +0.j      0.    +0.j      0.2231+0.j      0.0191-0.00785j]
     [0.    +0.j      0.    +0.j      0.0191+0.00785j 0.2769+0.j     ]]

    References
    ==========
    .. [WatPMap18] Watrous, John.
        The theory of quantum information.
        Cambridge University Press, 2018.

    :param rho: A matrix.
    :param phi_map: The map to partially apply.
    :param sys: Scalar or vector specifying the size of the subsystems.
    :param dim: Dimension of the subsystems. If :code:`None`, all dimensions
                are assumed to be equal.
    :return: The partial map :code:`phi_map` applied to matrix :code:`rho`.
    """
    if dim is None:
        dim = np.round(np.sqrt(list(rho.shape))).conj().T * np.ones(2)
    if isinstance(dim, list):
        dim = np.array(dim)

    # Force dim to be a row vector.
    if dim.ndim == 1:
        dim = dim.T.flatten()
        dim = np.array([dim, dim])

    prod_dim_r1 = int(np.prod(dim[0, :sys - 1]))
    prod_dim_c1 = int(np.prod(dim[1, :sys - 1]))
    prod_dim_r2 = int(np.prod(dim[0, sys:]))
    prod_dim_c2 = int(np.prod(dim[1, sys:]))

    if isinstance(phi_map, list):
        # Compute the Kraus operators on the full system.
        s_phi_1, s_phi_2 = len(phi_map), len(phi_map[0])

        # Map is completely positive.
        if s_phi_2 == 1 or s_phi_1 == 1 and s_phi_2 > 2:
            phi = []
            for i, _ in enumerate(phi_map):
                phi.append(
                    np.kron(
                        np.kron(np.identity(prod_dim_r1), phi_map[i]),
                        np.identity(prod_dim_r2),
                    ))
            phi_x = apply_channel(rho, phi)
        else:
            phi_1 = []
            for i, _ in enumerate(phi_map):
                phi_1.append(
                    np.kron(
                        np.kron(np.identity(prod_dim_r1), phi_map[i][0]),
                        np.identity(prod_dim_r2),
                    ))
            phi_2 = []
            for i, _ in enumerate(phi_map):
                phi_2.append(
                    np.kron(
                        np.kron(np.identity(prod_dim_c1), phi_map[i][1]),
                        np.identity(prod_dim_c2),
                    ))

            phi_x = [list(l) for l in zip(phi_1, phi_2)]
            phi_x = apply_channel(rho, phi_x)
        return phi_x

    # The `phi_map` variable is provided as a Choi matrix.
    if isinstance(phi_map, np.ndarray):
        dim_phi = phi_map.shape

        dim = np.array([
            [
                prod_dim_r2,
                prod_dim_r2,
                int(dim[0, sys - 1]),
                int(dim_phi[0] / dim[0, sys - 1]),
                prod_dim_r1,
                prod_dim_r1,
            ],
            [
                prod_dim_c2,
                prod_dim_c2,
                int(dim[1, sys - 1]),
                int(dim_phi[1] / dim[1, sys - 1]),
                prod_dim_c1,
                prod_dim_c1,
            ],
        ])
        psi_r1 = max_entangled(prod_dim_r2, False, False)
        psi_c1 = max_entangled(prod_dim_c2, False, False)
        psi_r2 = max_entangled(prod_dim_r1, False, False)
        psi_c2 = max_entangled(prod_dim_c1, False, False)

        phi_map = permute_systems(
            np.kron(np.kron(psi_r1 * psi_c1.conj().T, phi_map),
                    psi_r2 * psi_c2.conj().T),
            [1, 3, 5, 2, 4, 6],
            dim,
        )

        phi_x = apply_channel(rho, phi_map)

        return phi_x

    raise ValueError("The `phi_map` variable is assumed to be provided as "
                     "either a Choi matrix or a list of Kraus operators.")
示例#9
0
def test_apply_channel_invalid_input():
    """Invalid input for apply map."""
    with np.testing.assert_raises(ValueError):
        apply_channel(np.array([[1, 2], [3, 4]]), 2)