Esempio n. 1
0
def test_max_ent_2():
    """Generate maximally entangled state: `1/sqrt(2) * (|00> + |11>)`."""
    e_0, e_1 = basis(2, 0), basis(2, 1)
    expected_res = 1 / np.sqrt(2) * (np.kron(e_0, e_0) + np.kron(e_1, e_1))
    res = max_entangled(2)

    bool_mat = np.isclose(res, expected_res)
    np.testing.assert_equal(np.all(bool_mat), True)
Esempio n. 2
0
def test_max_ent_2_0_0():
    """Generate maximally entangled state: `|00> + |11>`."""
    e_0, e_1 = basis(2, 0), basis(2, 1)
    expected_res = 1 * (np.kron(e_0, e_0) + np.kron(e_1, e_1))
    res = max_entangled(2, False, False)

    bool_mat = np.isclose(res, expected_res)
    np.testing.assert_equal(np.all(bool_mat), True)
Esempio n. 3
0
def isotropic(dim: int, alpha: float) -> np.ndarray:
    r"""
    Produce a isotropic state [HH99]_.

    Returns the isotropic state with parameter :code:`alpha` acting on
    (:code:`dim`-by-:code:`dim`)-dimensional space. The isotropic state has the following form

    .. math::
        \begin{equation}
            \rho_{\alpha} = \frac{1 - \alpha}{d^2} \mathbb{I} \otimes
            \mathbb{I} + \alpha |\psi_+ \rangle \langle \psi_+ | \in
            \mathbb{C}^d \otimes \mathbb{C}^2
        \end{equation}

    where :math:`|\psi_+ \rangle = \frac{1}{\sqrt{d}} \sum_j |j \rangle \otimes |j \rangle` is the
    maximally entangled state.

    Examples
    ==========

    To generate the isotropic state with parameter :math:`\alpha=1/2`, we can make the following
    call to :code:`toqito` as

    >>> from toqito.states import isotropic
    >>> isotropic(3, 1 / 2)
    [[0.22222222, 0.        , 0.        , 0.        , 0.16666667,
      0.        , 0.        , 0.        , 0.16666667],
     [0.        , 0.05555556, 0.        , 0.        , 0.        ,
      0.        , 0.        , 0.        , 0.        ],
     [0.        , 0.        , 0.05555556, 0.        , 0.        ,
      0.        , 0.        , 0.        , 0.        ],
     [0.        , 0.        , 0.        , 0.05555556, 0.        ,
      0.        , 0.        , 0.        , 0.        ],
     [0.16666667, 0.        , 0.        , 0.        , 0.22222222,
      0.        , 0.        , 0.        , 0.16666667],
     [0.        , 0.        , 0.        , 0.        , 0.        ,
      0.05555556, 0.        , 0.        , 0.        ],
     [0.        , 0.        , 0.        , 0.        , 0.        ,
      0.        , 0.05555556, 0.        , 0.        ],
     [0.        , 0.        , 0.        , 0.        , 0.        ,
      0.        , 0.        , 0.05555556, 0.        ],
     [0.16666667, 0.        , 0.        , 0.        , 0.16666667,
      0.        , 0.        , 0.        , 0.22222222]]

    References
    ==========
    .. [HH99] Horodecki, Michał, and Paweł Horodecki.
        "Reduction criterion of separability and limits for a class of
        distillation protocols." Physical Review A 59.6 (1999): 4206.

    :param dim: The local dimension.
    :param alpha: The parameter of the isotropic state.
    :return: Isotropic state of dimension :code:`dim`.
    """
    psi = max_entangled(dim, False, False)
    return (1 - alpha) * np.identity(
        dim**2) / dim**2 + alpha * psi * psi.conj().T / dim
Esempio n. 4
0
def breuer(dim: int, lam: float) -> np.ndarray:
    r"""
    Produce a Breuer state [HPBreuer]_.

    Gives a Breuer bound entangled state for two qudits of local dimension :code:`dim`, with the
    :code:`lam` parameter describing the weight of the singlet component as described in
    [HPBreuer]_.

    This function was adapted from the QETLAB package.

    Examples
    ==========

    We can generate a Breuer state of dimension :math:`4` with weight :math:`0.1`. For any weight
    above :math:`0`, the state will be bound entangled, that is, it will satisfy the PPT criterion,
    but it will be entangled.

    >>> from toqito.states import breuer
    >>> breuer(2, 0.1)
    [[ 0.3  0.  -0.   0. ]
     [ 0.   0.2  0.1  0. ]
     [-0.   0.1  0.2 -0. ]
     [ 0.   0.  -0.   0.3]]

    References
    ==========
    .. [HPBreuer] H-P. Breuer. Optimal entanglement criterion for mixed quantum states.
       E-print: arXiv:quant-ph/0605036, 2006.

    :param dim: Dimension of the Breuer state.
    :param lam: The weight of the singlet component.
    :return: Breuer state of dimension :code:`dim` with weight :code:`lam`.
    """
    if dim % 2 == 1 or dim <= 0:
        raise ValueError(f"The value {dim} must be an even positive integer.")

    v_mat = np.fliplr(np.diag((-1)**np.mod(np.arange(1, dim + 1), 2)))
    max_entangled(dim)
    psi = np.dot(np.kron(np.identity(dim), v_mat), max_entangled(dim))

    return lam * (psi * psi.conj().T) + (
        1 - lam) * 2 * symmetric_projection(dim) / (dim * (dim + 1))
Esempio n. 5
0
def test_schmidt_decomp_max_ent():
    """Schmidt decomposition of the 3-D maximally entangled state."""
    singular_vals, u_mat, vt_mat = schmidt_decomposition(max_entangled(3))

    expected_u_mat = np.identity(3)
    expected_vt_mat = np.identity(3)
    expected_singular_vals = 1 / np.sqrt(3) * np.array([[1], [1], [1]])

    bool_mat = np.isclose(expected_u_mat, u_mat)
    np.testing.assert_equal(np.all(bool_mat), True)

    bool_mat = np.isclose(expected_vt_mat, vt_mat)
    np.testing.assert_equal(np.all(bool_mat), True)

    bool_mat = np.isclose(expected_singular_vals, singular_vals)
    np.testing.assert_equal(np.all(bool_mat), True)
Esempio n. 6
0
def test_schmidt_decomp_dim_list():
    """Schmidt decomposition with list specifying dimension."""
    singular_vals, u_mat, vt_mat = schmidt_decomposition(max_entangled(3),
                                                         dim=[3, 3])

    expected_u_mat = np.identity(3)
    expected_vt_mat = np.identity(3)
    expected_singular_vals = 1 / np.sqrt(3) * np.array([[1], [1], [1]])

    bool_mat = np.isclose(expected_u_mat, u_mat)
    np.testing.assert_equal(np.all(bool_mat), True)

    bool_mat = np.isclose(expected_vt_mat, vt_mat)
    np.testing.assert_equal(np.all(bool_mat), True)

    bool_mat = np.isclose(expected_singular_vals, singular_vals)
    np.testing.assert_equal(np.all(bool_mat), True)
Esempio n. 7
0
def reduction(dim: int, k: int = 1) -> np.ndarray:
    r"""
    Produce the reduction map or reduction channel.

    If :code:`k = 1`, this returns the Choi matrix of the reduction map which is a positive map
    on :code:`dim`-by-:code:`dim` matrices. For a different value of :code:`k`, this yields the
    Choi matrix of the map defined by:

    .. math::
        R(X) = k * \text{Tr}(X) * \mathbb{I} - X,

    where :math:`\mathbb{I}` is the identity matrix. This map is :math:`k`-positive.

    Examples
    ==========

    Using :code:`toqito`, we can generate the :math:`3`-dimensional (or standard) reduction map
    as follows.

    >>> from toqito.channels import reduction
    >>> reduction(3)
    [[ 0.,  0.,  0.,  0., -1.,  0.,  0.,  0., -1.],
     [ 0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
     [ 0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.],
     [ 0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.],
     [-1.,  0.,  0.,  0.,  0.,  0.,  0.,  0., -1.],
     [ 0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.],
     [ 0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.],
     [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.],
     [-1.,  0.,  0.,  0., -1.,  0.,  0.,  0.,  0.]])

    :param dim: A positive integer (the dimension of the reduction map).
    :param k: If this positive integer is provided, the script will instead return the Choi
              matrix of the following linear map: Phi(X) := K * Tr(X)I - X.
    :return: The reduction map.
    """
    psi = max_entangled(dim, False, False)
    return k * identity(dim**2) - psi * psi.conj().T
Esempio n. 8
0
def depolarizing(dim: int, param_p: float = 0) -> np.ndarray:
    r"""
    Produce the partially depolarizing channel [WikDepo]_, [WatDepo18]_.

    The Choi matrix of the completely depolarizing channel that acts on
    :code:`dim`-by-:code:`dim` matrices.

    The *completely depolarizing channel* is defined as

    .. math::
        \Omega(X) = \text{Tr}(X) \omega

    for all :math:`X \in \text{L}(\mathcal{X})`, where

    .. math::
        \omega = \frac{\mathbb{I}_{\mathcal{X}}}{\text{dim}(\mathcal{X})}

    denotes the completely mixed stated defined with respect to the space :math:`\mathcal{X}`.

    Examples
    ==========

    The completely depolarizing channel maps every density matrix to the maximally-mixed state.
    For example, consider the density operator

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

    corresponding to one of the Bell states. Applying the depolarizing channel to :math:`\rho` we
    have that

    .. math::
        \Phi(\rho) = \frac{1}{4} \begin{pmatrix}
                                    \frac{1}{2} & 0 & 0 & \frac{1}{2} \\
                                    0 & 0 & 0 & 0 \\
                                    0 & 0 & 0 & 0 \\
                                    \frac{1}{2} & 0 & 0 & \frac{1}{2}
                                 \end{pmatrix}.

    This can be observed in :code:`toqito` as follows.

    >>> from toqito.channel_ops import apply_channel
    >>> from toqito.channels import depolarizing
    >>> import numpy as np
    >>> 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]]
    >>> )
    >>> apply_channel(test_input_mat, depolarizing(4))
    [[0.125 0.    0.    0.125]
     [0.    0.    0.    0.   ]
     [0.    0.    0.    0.   ]
     [0.125 0.    0.    0.125]]

    >>> from toqito.channel_ops import apply_channel
    >>> from toqito.channels import depolarizing
    >>> import numpy as np
    >>> test_input_mat = np.array(
    >>>     [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
    >>> )
    >>> apply_channel(test_input_mat, depolarizing(4, 0.5))
    [[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.   ]]


    References
    ==========
    .. [WikDepo] Wikipedia: Quantum depolarizing channel
        https://en.wikipedia.org/wiki/Quantum_depolarizing

    .. [WatDepo18] Watrous, John.
        "The theory of quantum information."
        Section: "Replacement channels and the completely depolarizing channel".
        Cambridge University Press, 2018.

    :param dim: The dimensionality on which the channel acts.
    :param param_p: Default 0.
    :return: The Choi matrix of the completely depolarizing channel.
    """
    # Compute the Choi matrix of the depolarizing channel.

    # Gives a sparse non-normalized state.
    psi = max_entangled(dim=dim, is_sparse=False, is_normalized=False)
    return (1 - param_p) * np.identity(
        dim**2) / dim + param_p * (psi * psi.conj().T)
Esempio n. 9
0
def random_state_vector(dim: Union[List[int], int],
                        is_real: bool = False,
                        k_param: int = 0) -> np.ndarray:
    r"""Generate a random pure state vector.

    Examples
    ==========

    We may generate a random state vector. For instance, here is an example where we can generate a
    :math:`2`-dimensional random state vector.

    >>> from toqito.random import random_state_vector
    >>> vec = random_state_vector(2)
    >>> vec
    [[0.50993973+0.15292408j],
     [0.27787332+0.79960122j]]

    We can verify that this is in fact a valid state vector by computing the corresponding density
    matrix of the vector and checking if the density matrix is pure.

    >>> from toqito.state_props import is_pure
    >>> dm = vec.conj().T * vec
    >>> is_pure(dm)
    True

    :param dim: The number of rows (and columns) of the unitary matrix.
    :param is_real: Boolean denoting whether the returned matrix has real
                    entries or not. Default is :code:`False`.
    :param k_param: Default 0.
    :return: A :code:`dim`-by-:code:`dim` random unitary matrix.
    """
    # Schmidt rank plays a role.
    if 0 < k_param < np.min(dim):
        # Allow the user to enter a single number for dim.
        if isinstance(dim, int):
            dim = [dim, dim]

        # If you start with a separable state on a larger space and multiply
        # the extra `k_param` dimensions by a maximally entangled state, you
        # get a Schmidt rank `<= k_param` state.
        psi = max_entangled(k_param, True, False).toarray()

        a_param = np.random.rand(dim[0] * k_param, 1)
        b_param = np.random.rand(dim[1] * k_param, 1)

        if not is_real:
            a_param = a_param + 1j * np.random.rand(dim[0] * k_param, 1)
            b_param = b_param + 1j * np.random.rand(dim[1] * k_param, 1)

        mat_1 = np.kron(psi.conj().T, np.identity(int(np.prod(dim))))
        mat_2 = swap(
            np.kron(a_param, b_param),
            sys=[2, 3],
            dim=[k_param, dim[0], k_param, dim[1]],
        )

        ret_vec = mat_1 * mat_2
        return np.divide(ret_vec, np.linalg.norm(ret_vec))

    # Schmidt rank is full, so ignore it.
    ret_vec = np.random.rand(dim, 1)
    if not is_real:
        ret_vec = ret_vec + 1j * np.random.rand(dim, 1)
    return np.divide(ret_vec, np.linalg.norm(ret_vec))
Esempio n. 10
0
def test_is_product_entangled_state():
    """Check that is_product_vector returns False for an entangled state."""
    ent_vec = max_entangled(3)
    np.testing.assert_equal(is_product_vector(ent_vec), False)
Esempio n. 11
0
def test_is_product_entangled_state_3_sys():
    """Check that dimension argument as list is supported."""
    ent_vec = max_entangled(4)
    np.testing.assert_equal(is_product_vector(ent_vec, dim=[2, 2, 2, 2]),
                            False)
Esempio n. 12
0
def test_majorizes_max_entangled():
    """Test that max entangled partial trace returns False."""
    v_vec = max_entangled(3)
    rho = v_vec * v_vec.conj().T
    np.testing.assert_equal(majorizes(partial_trace(rho), rho), False)
Esempio n. 13
0
def kraus_to_choi(kraus_ops: List[List[np.ndarray]], sys: int = 2) -> np.ndarray:
    r"""
    Compute the Choi matrix of a list of Kraus operators [WatKraus18]_.

    The Choi matrix of the list of Kraus operators, :code:`kraus_ops`. The default convention is
    that the Choi matrix is the result of applying the map to the second subsystem of the
    standard maximally entangled (unnormalized) state. The Kraus operators are expected to be
    input as a list of numpy arrays.

    This function was adapted from the QETLAB package.

    Examples
    ==========

    The transpose map:

    The Choi matrix of the transpose map is the swap operator.

    >>> import numpy as np
    >>> from toqito.channel_ops import kraus_to_choi
    >>> 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],
    >>> ]
    >>> kraus_to_choi(kraus_ops)
    [[1. 0. 0. 0.]
     [0. 0. 1. 0.]
     [0. 1. 0. 0.]
     [0. 0. 0. 1.]]

    See Also
    ========
    choi_to_kraus

    References
    ==========
    .. [WatKraus18] Watrous, John.
        "The theory of quantum information."
        Section: "Kraus representations".
        Cambridge University Press, 2018.

    :param kraus_ops: A list of Kraus operators.
    :param sys: The dimension of the system (default is 2).
    :return: The corresponding Choi matrix of the provided Kraus operators.
    """
    dim_op_1 = kraus_ops[0][0].shape[0]
    dim_op_2 = kraus_ops[0][0].shape[1]

    choi_mat = partial_channel(
        max_entangled(dim_op_1, False, False) * max_entangled(dim_op_2, False, False).conj().T,
        kraus_ops,
        sys,
        np.array([[dim_op_1, dim_op_1], [dim_op_2, dim_op_2]]),
    )

    return choi_mat
Esempio n. 14
0
def choi(a_var: int = 1, b_var: int = 1, c_var: int = 0) -> np.ndarray:
    r"""
    Produce the Choi channel or one of its generalizations [Choi92]_.

    The *Choi channel* is a positive map on 3-by-3 matrices that is capable of detecting some
    entanglement that the transpose map is not.

    The standard Choi channel defined with :code:`a=1`, :code:`b=1`, and :code:`c=0` is the Choi
    matrix of the positive map defined in [Choi92]_. Many of these maps are capable of detecting
    PPT entanglement.

    Examples
    ==========

    The standard Choi channel is given as

    .. math::
        \Phi_{1, 1, 0} =
        \begin{pmatrix}
            1 & 0 & 0 & 0 & -1 & 0 & 0 & 0 & -1 \\
            0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\
            0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\
            0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\
            -1 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & -1 \\
            0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\
            0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\
            0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\
            -1 & 0 & 0 & 0 & -1 & 0 & 0 & 0 & 1
        \end{pmatrix}

    We can generate the Choi channel in :code:`toqito` as follows.

    >>> from toqito.channels import choi
    >>> import numpy as np
    >>> choi()
    [[ 1.,  0.,  0.,  0., -1.,  0.,  0.,  0., -1.],
     [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
     [ 0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.],
     [ 0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.],
     [-1.,  0.,  0.,  0.,  1.,  0.,  0.,  0., -1.],
     [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
     [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
     [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.],
     [-1.,  0.,  0.,  0., -1.,  0.,  0.,  0.,  1.]])

    The reduction channel is the map :math:`R` defined by:

    .. math::
        R(X) = \text{Tr}(X) \mathbb{I} - X.

    The matrix correspond to this is given as

    .. math::
        \Phi_{0, 1, 1} =
        \begin{pmatrix}
            0 & 0 & 0 & 0 & -1 & 0 & 0 & 0 & -1 \\
            0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\
            0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\
            0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\
            -1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & -1 \\
            0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\
            0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\
            0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\
            -1 & 0 & 0 & 0 & -1 & 0 & 0 & 0 & 0
        \end{pmatrix}

    The reduction channel is the Choi channel that arises when :code:`a = 0` and when :code:`b =
    c = 1`. We can obtain this matrix using :code:`toqito` as follows.

    >>> from toqito.channels import choi
    >>> import numpy as np
    >>> choi(0, 1, 1)
    [[ 0.,  0.,  0.,  0., -1.,  0.,  0.,  0., -1.],
     [ 0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
     [ 0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.],
     [ 0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.],
     [-1.,  0.,  0.,  0.,  0.,  0.,  0.,  0., -1.],
     [ 0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.],
     [ 0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.],
     [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.],
     [-1.,  0.,  0.,  0., -1.,  0.,  0.,  0.,  0.]])

    See Also
    ==========
    reduction

    References
    ==========
    .. [Choi92] Cho, Sung Je, Seung-Hyeok Kye, and Sa Ge Lee.
        "Generalized Choi maps in three-dimensional matrix algebra."
        Linear algebra and its applications 171 (1992): 213-224.
        https://www.sciencedirect.com/science/article/pii/002437959290260H

    :param a_var: Default integer for standard Choi map.
    :param b_var: Default integer for standard Choi map.
    :param c_var: Default integer for standard Choi map.
    :return: The Choi channel (or one of its  generalizations).
    """
    psi = max_entangled(3, False, False)
    return (np.diag([
        a_var + 1, c_var, b_var, b_var, a_var + 1, c_var, c_var, b_var,
        a_var + 1
    ]) - psi * psi.conj().T)
Esempio n. 15
0
def test_is_product_entangled_state_2_sys():
    """Check that dimension argument as list is supported."""
    ent_vec = max_entangled(4)
    res = is_product(ent_vec, dim=[4, 4])
    np.testing.assert_equal(res[0], False)
Esempio n. 16
0
def test_majorizes_max_entangled_flip():
    """Test that max entangled partial trace returns True (flipped args)."""
    v_vec = max_entangled(3)
    rho = v_vec * v_vec.conj().T
    np.testing.assert_equal(majorizes(rho, partial_trace(rho)), True)
Esempio n. 17
0
def brauer(dim: int, p_val: int) -> np.ndarray:
    r"""
    Produce all Brauer states [WikBrauer]_.

    Produce a matrix whose columns are all of the (unnormalized) "Brauer" states: states that are
    the :code:`p_val`-fold tensor product of the standard maximally-entangled pure state on
    :code:`dim` local dimensions. There are many such states, since there are many different ways to
    group the :code:`2 * p_val` parties into :code:`p_val` pairs (with each pair corresponding to
    one maximally-entangled state).

    The exact number of such states is:

    ```python
    np.factorial(2 * p_val) / (np.factorial(p_val) * 2**p_val)
    ```

    which is the number of columns of the returned matrix.

    This function has been adapted from QETLAB.

    Examples
    ==========

    Generate a matrix whose columns are all Brauer states on 4 qubits.

    >>> from toqito.states import brauer
    >>> brauer(2, 2)
    [[1. 1. 1.]
     [0. 0. 0.]
     [0. 0. 0.]
     [1. 0. 0.]
     [0. 0. 0.]
     [0. 1. 0.]
     [0. 0. 1.]
     [0. 0. 0.]
     [0. 0. 0.]
     [0. 0. 1.]
     [0. 1. 0.]
     [0. 0. 0.]
     [1. 0. 0.]
     [0. 0. 0.]
     [0. 0. 0.]
     [1. 1. 1.]]

    References
    ==========
    .. [WikBrauer] Wikipedia: Brauer algebra
        https://en.wikipedia.org/wiki/Brauer_algebra

    :param dim: Dimension of each local subsystem
    :param p_val: Half of the number of parties (i.e., the state that this function computes will
                  live in :math:`(\mathbb{C}^D)^{\otimes 2 P})`
    :return: Matrix whose columns are all of the unnormalized Brauer states.
    """
    # The Brauer states are computed from perfect matchings of the complete graph. So compute all
    # perfect matchings first.
    phi = tensor(max_entangled(dim, False, False), p_val)
    matchings = perfect_matchings(2 * p_val)
    num_matchings = matchings.shape[0]
    state = np.zeros((dim**(2 * p_val), num_matchings))

    # Turn these perfect matchings into the corresponding states.
    for i in range(num_matchings):
        state[:,
              i] = permute_systems(phi, matchings[i, :],
                                   dim * np.ones((1, 2 * p_val), dtype=int)[0])
    return state
Esempio n. 18
0
def partial_map(
    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 map 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.

    Examples
    ==========

    >>> from toqito.channel_ops import partial_map
    >>> 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_map(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_map
    >>> 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_map(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:]))

    # Note: In the case where the Kraus operators refer to a CP map, this
    # approach of appending to the list may not work.
    if isinstance(phi_map, list):
        # The `phi_map` variable is provided as a list of Kraus operators.
        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_map(rho, phi)
        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_map(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."
    )
Esempio n. 19
0
def dephasing(dim: int, param_p: float = 0) -> np.ndarray:
    r"""
    Produce the partially dephasing channel [WatDeph18]_.

    The Choi matrix of the completely dephasing channel that acts on :code:`dim`-by-:code:`dim`
    matrices.

    Let :math:`\Sigma` be an alphabet and let :math:`\mathcal{X} = \mathbb{C}^{\Sigma}`. The map
    :math:`\Delta \in \text{T}(\mathcal{X})` defined as

    .. math::
        \Delta(X) = \sum_{a \in \Sigma} X(a, a) E_{a,a}

    for every :math:`X \in \text{L}(\mathcal{X})` is defined as the *completely dephasing channel*.

    Examples
    ==========

    The completely dephasing channel maps kills everything off the diagonal. Consider the
    following matrix

    .. math::
        \rho = \begin{pmatrix}
                   1 & 2 & 3 & 4 \\
                   5 & 6 & 7 & 8 \\
                   9 & 10 & 11 & 12 \\
                   13 & 14 & 15 & 16
               \end{pmatrix}.

    Applying the dephasing channel to :math:`\rho` we have that

    .. math::
        \Phi(\rho) = \begin{pmatrix}
                         1 & 0 & 0 & 0 \\
                         0 & 6 & 0 & 0 \\
                         0 & 0 & 11 & 0 \\
                         0 & 0 & 0 & 16
                     \end{pmatrix}.

    This can be observed in :code:`toqito` as follows.

    >>> from toqito.channel_ops import apply_channel
    >>> from toqito.channels import dephasing
    >>> import numpy as np
    >>> test_input_mat = np.array(
    >>>     [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
    >>> )
    >>> apply_channel(test_input_mat, dephasing(4))
    [[ 1.,  0.,  0.,  0.],
     [ 0.,  6.,  0.,  0.],
     [ 0.,  0., 11.,  0.],
     [ 0.,  0.,  0., 16.]])

    We may also consider setting the parameter :code:`p = 0.5`.

    >>> from toqito.channel_ops import apply_channel
    >>> from toqito.channels import dephasing
    >>> import numpy as np
    >>> test_input_mat = np.array(
    >>>     [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
    >>> )
    >>> apply_channel(test_input_mat, dephasing(4, 0.5))
    [[17.5  0.   0.   0. ]
     [ 0.  20.   0.   0. ]
     [ 0.   0.  22.5  0. ]
     [ 0.   0.   0.  25. ]]

    References
    ==========
    .. [WatDeph18] Watrous, John.
        "The theory of quantum information."
        Section: "The completely dephasing channel".
        Cambridge University Press, 2018.

    :param dim: The dimensionality on which the channel acts.
    :param param_p: Default is 0.
    :return: The Choi matrix of the dephasing channel.
    """
    # Compute the Choi matrix of the dephasing channel.

    # Gives a sparse non-normalized state.
    psi = max_entangled(dim=dim, is_sparse=False, is_normalized=False)
    return (1 - param_p) * np.diag(np.diag(
        psi * psi.conj().T)) + param_p * (psi * psi.conj().T)