Exemplo n.º 1
0
 def test_bell_state_pt(self):
     """Test partial transpose on a Bell state."""
     rho = bell(2) * bell(2).conj().T
     expected_res = np.array([[0, 0, 0, 1 / 2], [0, 1 / 2, 0, 0],
                              [0, 0, 1 / 2, 0], [1 / 2, 0, 0, 0]])
     res = partial_transpose(rho)
     self.assertEqual(np.allclose(res, expected_res), True)
Exemplo n.º 2
0
    def test_partial_transpose_sys_vec_dim_vec(self):
        """Variables `sys` and `dim` defined as vector."""
        test_input_mat = np.arange(1, 17).reshape(4, 4)

        expected_res = np.array([[1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15],
                                 [4, 8, 12, 16]])

        res = partial_transpose(test_input_mat, [1, 2], [2, 2])

        bool_mat = np.isclose(res, expected_res)
        self.assertEqual(np.all(bool_mat), True)
Exemplo n.º 3
0
    def test_partial_transpose_16_by_16(self):
        """Partial transpose on a 16-by-16 matrix."""
        test_input_mat = np.arange(1, 257).reshape(16, 16)
        res = partial_transpose(test_input_mat, [1, 3], [2, 2, 2, 2])
        first_expected_row = np.array([
            1, 2, 33, 34, 5, 6, 37, 38, 129, 130, 161, 162, 133, 134, 165, 166
        ])

        first_expected_col = np.array(
            [1, 17, 3, 19, 65, 81, 67, 83, 9, 25, 11, 27, 73, 89, 75, 91])

        self.assertEqual(np.allclose(res[0, :], first_expected_row), True)
        self.assertEqual(np.allclose(res[:, 0], first_expected_col), True)
Exemplo n.º 4
0
    def test_partial_transpose_norm_diff(self):
        """
        Apply partial transpose to first and second subsystem.

        Applying the transpose to both the first and second subsystems results
        in the standard transpose of the matrix.
        """
        test_input_mat = np.arange(1, 17).reshape(4, 4)
        res = np.linalg.norm(
            partial_transpose(test_input_mat, [1, 2]) -
            test_input_mat.conj().T)
        expected_res = 0

        self.assertEqual(np.isclose(res, expected_res), True)
Exemplo n.º 5
0
    def test_partial_transpose(self):
        """
        Default partial_transpose.

        By default, the partial_transpose function performs the transposition
        on the second subsystem.
        """
        test_input_mat = np.arange(1, 17).reshape(4, 4)

        expected_res = np.array([[1, 5, 3, 7], [2, 6, 4, 8], [9, 13, 11, 15],
                                 [10, 14, 12, 16]])

        res = partial_transpose(test_input_mat)

        bool_mat = np.isclose(expected_res, res)
        self.assertEqual(np.all(bool_mat), True)
Exemplo n.º 6
0
    def test_partial_transpose_sys(self):
        """
        Default partial transpose `sys` argument.

        By specifying the `sys` argument, you can perform the transposition on
        the first subsystem instead:
        """
        test_input_mat = np.arange(1, 17).reshape(4, 4)

        expected_res = np.array([[1, 2, 9, 10], [5, 6, 13, 14], [3, 4, 11, 12],
                                 [7, 8, 15, 16]])

        res = partial_transpose(test_input_mat, 1)

        bool_mat = np.isclose(res, expected_res)
        self.assertEqual(np.all(bool_mat), True)
Exemplo n.º 7
0
def realignment(input_mat: np.ndarray, dim=None) -> np.ndarray:
    r"""
    Compute the realignment of a bipartite operator [REALIGN]_.

    Gives the realignment of the matrix `input_mat`, where it is assumed that
    the number of rows and columns of `input_mat` are both perfect squares and
    both subsystems have equal dimension. The realignment is defined by mapping
    the operator :math:`|ij \rangle \langle kl |` to :math:`|ik \rangle \langle
    jl |` and extending linearly.

    If `input_mat` is non-square, different row and column dimensions can be
    specified by putting the row dimensions in the first row of `dim` and the
    column dimensions in the second row of `dim`.

    Examples
    ==========

    The standard realignment map

    Using `toqito`, we can generate the standard realignment map as follows.
    When viewed as a map on block matrices, the realignment map takes each block
    of the original matrix and makes its vectorization the rows of the
    realignment matrix. This is illustrated by the following small example:

    >>> from toqito.channels.channels.realignment import realignment
    >>> 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]]
    >>> )
    >>> realignment(test_input_mat)
    [[ 1  2  5  6]
     [ 3  4  7  8]
     [ 9 10 13 14]
     [11 12 15 16]]

    References
    ==========
    .. [REALIGN] Lupo, Cosmo, Paolo Aniello, and Antonello Scardicchio.
        "Bipartite quantum systems: on the realignment criterion and beyond."
        Journal of Physics A: Mathematical and Theoretical
        41.41 (2008): 415301.
        https://arxiv.org/abs/0802.2019

    :param input_mat: The input matrix.
    :param dim: Default has all equal dimensions.
    :return: The realignment map matrix.
    """
    eps = np.finfo(float).eps
    dim_mat = input_mat.shape
    round_dim = np.round(np.sqrt(dim_mat))
    if dim is None:
        dim = np.transpose(np.array([round_dim]))
    if isinstance(dim, list):
        dim = np.array(dim)

    if isinstance(dim, int):
        dim = np.array([[dim], [dim_mat[0] / dim]])
        if np.abs(dim[1] - np.round(dim[1])) >= 2 * dim_mat[0] * eps:
            raise ValueError("InvalidDim:")
        dim[1] = np.round(dim[1])
        dim = np.array([[1], [4]])

    if min(dim.shape) == 1:
        dim = dim[:].T
        dim = functools.reduce(operator.iconcat, dim, [])
        dim = np.array([dim, dim])
        # dim = functools.reduce(operator.iconcat, dim, [])

    dim_x = np.array([[dim[0][1], dim[0][0]], [dim[1][0], dim[1][1]]])
    dim_y = np.array([[dim[1][0], dim[0][0]], [dim[0][1], dim[1][1]]])

    x_tmp = swap(input_mat, [1, 2], dim, True)
    y_tmp = partial_transpose(x_tmp, sys=1, dim=dim_x)
    return swap(y_tmp, [1, 2], dim_y, True)
Exemplo n.º 8
0
 def test_non_square_matrix_2(self):
     """Matrix must be square."""
     with self.assertRaises(ValueError):
         rho = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
         partial_transpose(rho, 2, [2])
Exemplo n.º 9
0
 def test_non_square_matrix(self):
     """Matrix must be square."""
     with self.assertRaises(ValueError):
         test_input_mat = np.array([[1, 2, 3, 4], [5, 6, 7, 8],
                                    [13, 14, 15, 16]])
         partial_transpose(test_input_mat)
Exemplo n.º 10
0
def negativity(rho: np.ndarray, dim: Union[List[int], int] = None) -> float:
    r"""
    Compute the negativity of a bipartite quantum state [WIKNEG]_.

    The negativity of a subsystem can be defined in terms of a density matrix
    :math:`\rho`:

    .. math::
        \mathcal{N}(\rho) \equiv \frac{||\rho^{\Gamma_A}||_1-1}{2}

    Calculate the negativity of the quantum state `rho`, assuming that the two
    subsystems on which `rho` acts are of equal dimension (if the local
    dimensions are unequal, specify them in the optional `dim` argument). The
    negativity of `rho` is the sum of the absolute value of the negative
    eigenvalues of the partial transpose of `rho`.

    Examples
    ==========

    Example of the negativity of density matrix of Bell state.

    >>> from toqito.states.states.bell import bell
    >>> from toqito.states.entanglement.negativity import negativity
    >>> rho = bell(0) * bell(0).conj().T
    >>> negativity(rho)
    0.4999999999999998

    References
    ==========
    .. [WIKNEG] Wikipedia page for negativity (quantum mechanics):
        https://en.wikipedia.org/wiki/Negativity_(quantum_mechanics)

    :param rho: A density matrix of a pure state vector.
    :param dim: The default has both subsystems of equal dimension.
    :return: A value between 0 and 1 that corresponds to the negativity of
            :math:`\rho`.
    """
    # Allow the user to input either a pure state vector or a density matrix.
    rho = pure_to_mixed(rho)
    rho_dims = rho.shape
    round_dim = np.round(np.sqrt(rho_dims))

    if dim is None:
        dim = np.array([round_dim])
        dim = dim.T
    if isinstance(dim, list):
        dim = np.array(dim)

    # Allow the user to enter a single number for dim.
    if isinstance(dim, int):
        dim = np.array([dim, rho_dims[0] / dim])
        if abs(dim[1] - np.round(dim[1])) >= 2 * rho_dims[0] * np.finfo(float).eps:
            raise ValueError(
                "InvalidDim: If `dim` is a scalar, `rho` must be "
                "square and `dim` must evenly divide `len(rho)`. "
                "Please provide the `dim` array containing the "
                "dimensions of the subsystems."
            )
        dim[1] = np.round(dim[1])

    if np.prod(dim) != rho_dims[0]:
        raise ValueError(
            "InvalidDim: Please provide local dimensions in the "
            "argument `dim` that match the size of `rho`."
        )

    # Compute the negativity.
    return (lin_alg.norm(partial_transpose(rho, 2, dim), ord="nuc") - 1) / 2
Exemplo n.º 11
0
def ppt_distinguishability(states: List[np.ndarray],
                           probs: List[float] = None) -> float:
    r"""
    Compute probability of distinguishing a state via PPT measurements [COS13]_.

    Implements the semidefinite program (SDP) whose optimal value is equal to
    the maximum probability of perfectly distinguishing orthogonal maximally
    entangled states using any PPT measurement; a measurement whose operators
    are positive under partial transpose. This SDP was explicitly provided in
    [COS13]_.

    Specifically, the function implements the dual problem (as this is
    computationally more efficient) and is defined as:

    .. math::

        \begin{equation}
            \begin{aligned}
                \text{minimize:} \quad & \frac{1}{k} \text{Tr}(Y) \\
                \text{subject to:} \quad & Y \geq \text{T}_{\mathcal{A}}
                                          (\rho_j), \quad j = 1, \ldots, k, \\
                                         & Y \in \text{Herm}(\mathcal{A} \otimes
                                          \mathcal{B}).
            \end{aligned}
        \end{equation}

    Examples
    ==========

    Consider the following Bell states

    .. math::
        \begin{equation}
            |\psi_0 \rangle = \frac{|00\rangle + |11\rangle}{\sqrt{2}}, \quad
            |\psi_1 \rangle = \frac{|01\rangle + |10\rangle}{\sqrt{2}}, \quad
            |\psi_2 \rangle = \frac{|01\rangle - |10\rangle}{\sqrt{2}}, \quad
            |\psi_3 \rangle = \frac{|00\rangle - |11\rangle}{\sqrt{2}}. \quad
        \end{equation}

    It was illustrated in [YDY12]_ that for the following set of states:

    The PPT distinguishability of the following states

    .. math::
        \begin{equation}
            \rho_1^{(2)} = \psi_0 \otimes \psi_0, \quad
            \rho_2^{(2)} = \psi_1 \otimes \psi_1, \quad
        \end{equation}

    should yield :math:`7/8 ~ 0.875` as was proved in [YDY12]_.

    >>> from toqito.states.states.bell import bell
    >>> from toqito.states.optimizations.ppt_distinguishability import ppt_distinguishability
    >>> # Bell vectors:
    >>> psi_0 = bell(0)
    >>> psi_1 = bell(2)
    >>> psi_2 = bell(3)
    >>> psi_3 = bell(1)
    >>>
    >>> # YYD vectors from [YDY12]_.
    >>> x_1 = np.kron(psi_0, psi_0)
    >>> x_2 = np.kron(psi_1, psi_3)
    >>> x_3 = np.kron(psi_2, psi_3)
    >>> x_4 = np.kron(psi_3, psi_3)
    >>>
    >>> # YYD density matrices.
    >>> rho_1 = x_1 * x_1.conj().T
    >>> rho_2 = x_2 * x_2.conj().T
    >>> rho_3 = x_3 * x_3.conj().T
    >>> rho_4 = x_4 * x_4.conj().T
    >>>
    >>> states = [rho_1, rho_2, rho_3, rho_4]
    >>> probs = [1 / 4, 1 / 4, 1 / 4, 1 / 4]
    >>> res = ppt_distinguishability(states, probs)
    0.875

    References
    ==========
    .. [COS13] Cosentino, Alessandro.
        "Positive-partial-transpose-indistinguishable states via semidefinite
        programming."
        Physical Review A 87.1 (2013): 012321.
        https://arxiv.org/abs/1205.1031

    .. [YDY12] Yu, Nengkun, Runyao Duan, and Mingsheng Ying.
        "Four locally indistinguishable ququad-ququad orthogonal
        maximally entangled states."
        Physical review letters 109.2 (2012): 020506.
        https://arxiv.org/abs/1107.3224

    :param states: A list of density operators (matrices) corresponding to
                   quantum states.
    :param probs: A list of probabilities where `probs[i]` corresponds to the
                  probability that `states[i]` is selected by Alice.
    :return: The optimal probability with which the states can be distinguished
             via PPT measurements.
    """
    # Assume that at least one state is provided.
    if states is None or states == []:
        raise ValueError("InvalidStates: There must be at least one state "
                         "provided.")

    # Assume uniform probability if no specific distribution is given.
    if probs is None:
        probs = [1 / len(states)] * len(states)
    if not np.isclose(sum(probs), 1):
        raise ValueError("Invalid: Probabilities must sum to 1.")

    dim_x, dim_y = states[0].shape

    # The variable `states` is provided as a list of vectors. Transform them
    # into density matrices.
    if dim_y == 1:
        for i, state_ket in enumerate(states):
            states[i] = state_ket * state_ket.conj().T

    constraints = []
    y_var = cvxpy.Variable((dim_x, dim_x), hermitian=True)
    objective = 1 / len(states) * cvxpy.Minimize(cvxpy.trace(
        cvxpy.real(y_var)))

    dim = int(np.log2(dim_x))
    dim_list = [2] * int(np.log2(dim_x))
    sys_list = list(range(1, dim, 2))

    for i, _ in enumerate(states):
        constraints.append(
            cvxpy.real(y_var) >> partial_transpose(
                states[i], sys=sys_list, dim=dim_list))

    problem = cvxpy.Problem(objective, constraints)
    sol_default = problem.solve()

    return sol_default
Exemplo n.º 12
0
def is_ppt(
    mat: np.ndarray, sys: int = 2, dim: Union[int, List[int]] = None, tol: float = None
) -> bool:
    r"""
    Determine whether or not a matrix has positive partial transpose [WIKPPT]_.

    Yields either `True` or `False`, indicating that `mat` does or does not
    have positive partial transpose (within numerical error). The variable
    `mat` is assumed to act on bipartite space.

    For shared systems of :math:`2 \otimes 2` or :math:`2 \otimes 3`, the PPT
    criterion serves as a method to determine whether a given state is entangled
    or separable. Therefore, for systems of this size, the return value "True"
    would indicate that the state is separable and a value of "False" would
    indicate the state is entangled.

    Examples
    ==========

    Consider the following matrix

    .. math::
        \begin{pmatrix}
            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 & 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 & 0 & 0 \\
            0 & 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 \\
        \end{pmatrix}.

    This matrix trivially satisfies the PPT criterion as can be seen using the
    `toqito` package.

    >>> from toqito.states.properties.is_ppt import is_ppt
    >>> import numpy as np
    >>> mat = np.identity(9)
    >>> is_ppt(mat)
    True

    Consider the following Bell state:

    .. math::
        u = \frac{1}{\sqrt{2}}\left( |01 \rangle + |10 \rangle \right)

    For the density matrix :math:`\rho = u u^*`, as this is an entangled state
    of dimension :math:`2`, it will violate the PPT criterion, which can be seen
    using the `toqito` package.

    >>> from toqito.states.states.bell import bell
    >>> from toqito.states.properties.is_ppt import is_ppt
    >>> rho = bell(2) * bell(2).conj().T
    >>> is_ppt(rho)
    False

    References
    ==========
    .. [WIKPPT] Quantiki: Positive partial transpose
        https://www.quantiki.org/wiki/positive-partial-transpose

    :param mat: A square matrix.
    :param sys: Scalar or vector indicating which subsystems the transpose
                should be applied on.
    :param dim: The dimension is a vector containing the dimensions of the
                subsystems on which `mat` acts.
    :param tol: Tolerance with which to check whether `mat` is PPT.
    :return: True if `mat` is PPT and False if not.
    """
    eps = np.finfo(float).eps

    sqrt_rho_dims = np.round(np.sqrt(list(mat.shape)))

    if dim is None:
        dim = np.array(
            [[sqrt_rho_dims[0], sqrt_rho_dims[0]], [sqrt_rho_dims[1], sqrt_rho_dims[1]]]
        )
    if tol is None:
        tol = np.sqrt(eps)
    return is_psd(partial_transpose(mat, sys, dim), tol)