コード例 #1
0
def test_partial_transpose_8_by_8_subsystems_4_2():
    """Partial transpose on a 8-by-8 matrix on 4 x 2 subsystems."""
    test_input_mat = np.arange(1, 65).reshape(8, 8)

    # Partial transpose on the first subsystem:
    pt_1 = partial_transpose(test_input_mat, [1], [4, 2])
    expected_pt_1 = np.array(
        [
            [1, 2, 17, 18, 33, 34, 49, 50],
            [9, 10, 25, 26, 41, 42, 57, 58],
            [3, 4, 19, 20, 35, 36, 51, 52],
            [11, 12, 27, 28, 43, 44, 59, 60],
            [5, 6, 21, 22, 37, 38, 53, 54],
            [13, 14, 29, 30, 45, 46, 61, 62],
            [7, 8, 23, 24, 39, 40, 55, 56],
            [15, 16, 31, 32, 47, 48, 63, 64],
        ]
    )
    bool_mat = np.isclose(expected_pt_1, pt_1)
    np.testing.assert_equal(np.all(bool_mat), True)

    # Partial transpose on the second subsystem:
    pt_2 = partial_transpose(test_input_mat, [2], [4, 2])
    expected_pt_2 = np.array(
        [
            [1, 9, 3, 11, 5, 13, 7, 15],
            [2, 10, 4, 12, 6, 14, 8, 16],
            [17, 25, 19, 27, 21, 29, 23, 31],
            [18, 26, 20, 28, 22, 30, 24, 32],
            [33, 41, 35, 43, 37, 45, 39, 47],
            [34, 42, 36, 44, 38, 46, 40, 48],
            [49, 57, 51, 59, 53, 61, 55, 63],
            [50, 58, 52, 60, 54, 62, 56, 64],
        ]
    )
    bool_mat = np.isclose(expected_pt_2, pt_2)
    np.testing.assert_equal(np.all(bool_mat), True)

    # Partial transpose on the first and second subsystem:
    pt_1_2 = partial_transpose(test_input_mat, [1, 2], [4, 2])
    expected_pt_1_2 = np.array(
        [
            [1, 9, 17, 25, 33, 41, 49, 57],
            [2, 10, 18, 26, 34, 42, 50, 58],
            [3, 11, 19, 27, 35, 43, 51, 59],
            [4, 12, 20, 28, 36, 44, 52, 60],
            [5, 13, 21, 29, 37, 45, 53, 61],
            [6, 14, 22, 30, 38, 46, 54, 62],
            [7, 15, 23, 31, 39, 47, 55, 63],
            [8, 16, 24, 32, 40, 48, 56, 64],
        ]
    )
    bool_mat = np.isclose(expected_pt_1_2, pt_1_2)
    np.testing.assert_equal(np.all(bool_mat), True)
コード例 #2
0
def test_partial_transpose_6_by_6_subsystems_3_2():
    """Partial transpose on a 6-by-6 matrix on 3 x 2 subsystems."""
    test_input_mat = np.arange(1, 37).reshape(6, 6)

    # Partial transpose on first subsystem:
    pt_1 = partial_transpose(test_input_mat, [1], [3, 2])
    expected_pt_1 = np.array(
        [
            [1, 2, 13, 14, 25, 26],
            [7, 8, 19, 20, 31, 32],
            [3, 4, 15, 16, 27, 28],
            [9, 10, 21, 22, 33, 34],
            [5, 6, 17, 18, 29, 30],
            [11, 12, 23, 24, 35, 36],
        ]
    )
    bool_mat = np.isclose(expected_pt_1, pt_1)
    np.testing.assert_equal(np.all(bool_mat), True)

    # Partial transpose on second subsystem:
    pt_2 = partial_transpose(test_input_mat, [2], [3, 2])
    expected_pt_2 = np.array(
        [
            [1, 7, 3, 9, 5, 11],
            [2, 8, 4, 10, 6, 12],
            [13, 19, 15, 21, 17, 23],
            [14, 20, 16, 22, 18, 24],
            [25, 31, 27, 33, 29, 35],
            [26, 32, 28, 34, 30, 36],
        ]
    )
    bool_mat = np.isclose(expected_pt_2, pt_2)
    np.testing.assert_equal(np.all(bool_mat), True)

    # Partial transpose on first and second subsystems:
    pt_1_2 = partial_transpose(test_input_mat, [1, 2], [3, 2])
    expected_pt_1_2 = np.array(
        [
            [1, 7, 13, 19, 25, 31],
            [2, 8, 14, 20, 26, 32],
            [3, 9, 15, 21, 27, 33],
            [4, 10, 16, 22, 28, 34],
            [5, 11, 17, 23, 29, 35],
            [6, 12, 18, 24, 30, 36],
        ]
    )
    bool_mat = np.isclose(expected_pt_1_2, pt_1_2)
    np.testing.assert_equal(np.all(bool_mat), True)
コード例 #3
0
def test_partial_transpose_bell_state():
    """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)
    np.testing.assert_equal(np.allclose(res, expected_res), True)
コード例 #4
0
def test_partial_transpose_sys_vec_dim_vec():
    """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)
    np.testing.assert_equal(np.all(bool_mat), True)
コード例 #5
0
def test_partial_transpose_bipartite():
    """Partial transpose of bipartite systems."""
    rho = np.arange(16).reshape(4, 4)

    # Partial transpose of first subsystem:
    res = partial_transpose(rho, [1], [2, 2])
    expected_res = np.array([[0, 1, 8, 9], [4, 5, 12, 13], [2, 3, 10, 11], [6, 7, 14, 15]])
    bool_mat = np.isclose(expected_res, res)
    np.testing.assert_equal(np.all(bool_mat), True)

    # Partial transpose of second subsystem:
    res = partial_transpose(rho, [2], [2, 2])
    expected_res = np.array([[0, 4, 2, 6], [1, 5, 3, 7], [8, 12, 10, 14], [9, 13, 11, 15]])
    bool_mat = np.isclose(expected_res, res)
    np.testing.assert_equal(np.all(bool_mat), True)

    # Full transpose:
    res = partial_transpose(rho, [1, 2], [2, 2])
    expected_res = np.array([[0, 4, 8, 12], [1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15]])
    bool_mat = np.isclose(expected_res, res)
    np.testing.assert_equal(np.all(bool_mat), True)
コード例 #6
0
def test_partial_transpose_norm_diff():
    """
    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

    np.testing.assert_equal(np.isclose(res, expected_res), True)
コード例 #7
0
def test_partial_transpose_16_by_16():
    """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])

    np.testing.assert_equal(np.allclose(res[0, :], first_expected_row), True)
    np.testing.assert_equal(np.allclose(res[:, 0], first_expected_col), True)
コード例 #8
0
def test_partial_transpose_sys():
    """
    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)
    np.testing.assert_equal(np.all(bool_mat), True)
コード例 #9
0
def test_partial_transpose():
    """
    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)
    np.testing.assert_equal(np.all(bool_mat), True)
コード例 #10
0
def primal_problem(
    states: List[np.ndarray], probs: List[float] = None, dist_method="min-error"
) -> float:
    r"""
    Calculate primal problem for PPT distinguishability.

    The minimum-error semidefinite program implemented is defined as:

    .. math::
    \begin{equation}
        \begin{aligned}
            \text{maximize:} \quad & \sum_{j=1}^k \langle P_j, \rho_j \rangle \\
            \text{subject to:} \quad & P_1 + \cdots + P_k = \mathbb{I}_{\mathcal{A}}
                                        \otimes \mathbb{I}_{\mathcal{B}}, \\
                                     & P_1, \ldots, P_k \in \text{PPT}(\mathcal{A} : \mathcal{B}).
        \end{aligned}
    \end{equation}

    The unambiguous semidefinite program implemented is defined as:

    .. math::
    \begin{equation}
        \begin{aligned}
            \text{maximize:} \quad & \sum_{j=1}^k \langle P_j, \rho_j \rangle \\
            \text{subject to:} \quad & P_1 + \cdots + P_k = \mathbb{I}_{\mathcal{A}}
                                        \otimes \mathbb{I}_{\mathcal{B}}, \\
                                     & P_1, \ldots, P_k
                                      \in \text{PPT}(\mathcal{A} : \mathcal{B}), \\
                                     & \langle P_i, \rho_j \rangle = 0,
                                       \quad 1 \leq i, j \leq k, \quad i \not= j.
        \end{aligned}
    \end{equation}

    :param states: A list of states provided as either matrices or vectors.
    :param probs: Respective list of probabilities each state is selected.
    :param dist_method: Method of distinguishing to use.
    :return: The optimal value of the PPT primal problem SDP.
    """
    dim_x, _ = states[0].shape

    obj_func = []
    meas = []
    constraints = []

    dim = int(np.log2(dim_x))
    dim_list = [2] * int(np.log2(dim_x))

    sys_list = list(range(1, dim, 2))

    # Unambiguous consists of k + 1 operators, where the outcome of the k+1^st corresponds to the
    # inconclusive answer.
    if dist_method == "unambiguous":
        for i in range(len(states) + 1):
            meas.append(cvxpy.Variable((dim_x, dim_x), PSD=True))
            constraints.append(partial_transpose(meas[i], sys_list, dim_list) >> 0)

        for i, _ in enumerate(states):
            for j, _ in enumerate(states):
                if i != j:
                    constraints.append(probs[j] * cvxpy.trace(states[j].conj().T @ meas[i]) == 0)

    # Minimize error of distinguishing via PPT measurements.
    elif dist_method == "min-error":
        for i, _ in enumerate(states):
            meas.append(cvxpy.Variable((dim_x, dim_x), PSD=True))
            constraints.append(partial_transpose(meas[i], sys_list, dim_list) >> 0)

    for i, _ in enumerate(states):
        obj_func.append(probs[i] * cvxpy.trace(states[i].conj().T @ meas[i]))

    constraints.append(sum(meas) == np.identity(dim_x))

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

    return sol_default
コード例 #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}
            \begin{aligned}
            |\psi_0 \rangle = \frac{|00\rangle + |11\rangle}{\sqrt{2}}, &\quad
            |\psi_1 \rangle = \frac{|01\rangle + |10\rangle}{\sqrt{2}}, \\
            |\psi_2 \rangle = \frac{|01\rangle - |10\rangle}{\sqrt{2}}, &\quad
            |\psi_3 \rangle = \frac{|00\rangle - |11\rangle}{\sqrt{2}}.
            \end{aligned}
        \end{equation}

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

    .. math::
        \begin{equation}
            \begin{aligned}
            \rho_1^{(2)} &= |\psi_0 \rangle | \psi_0 \rangle \langle \psi_0 | \langle \psi_0 |, \\
            \rho_2^{(2)} &= |\psi_1 \rangle | \psi_3 \rangle \langle \psi_1 | \langle \psi_3 |, \\
            \rho_3^{(2)} &= |\psi_2 \rangle | \psi_3 \rangle \langle \psi_2 | \langle \psi_3 |, \\
            \rho_4^{(2)} &= |\psi_3 \rangle | \psi_3 \rangle \langle \psi_3 | \langle \psi_3 |, \\
            \end{aligned}
        \end{equation}

    that the optimal probability of distinguishing via a PPT measurement should yield
    :math:`7/8 \approx 0.875` as was proved in [YDY12]_.

    >>> from toqito.states import bell
    >>> from toqito.state_opt import ppt_distinguishability
    >>> # Bell vectors:
    >>> psi_0 = bell(0)
    >>> psi_1 = bell(2)
    >>> psi_2 = bell(3)
    >>> psi_3 = bell(1)
    >>>
    >>> # YDY 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)
    >>>
    >>> # YDY 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]
    >>> 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

    :return: The optimal probability with which the states can be distinguished
             via PPT measurements.
    """
    constraints = []

    __is_states_valid(states)
    if probs is None:
        probs = [1 / len(states)] * len(states)
    __is_probs_valid(probs)

    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

    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
コード例 #12
0
def test_partial_transpose_cvxpy():
    """Test partial transpose on cvxpy objects."""
    x_var = cvxpy.Variable((4, 4), hermitian=True)
    x_pt = partial_transpose(x_var)
    np.testing.assert_equal(isinstance(x_pt, Vstack), True)
コード例 #13
0
def test_partial_transpose_non_square_matrix_2():
    """Matrix must be square."""
    with np.testing.assert_raises(ValueError):
        rho = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
        partial_transpose(rho, 2, [2])
コード例 #14
0
def test_partial_transpose_non_square_matrix():
    """Matrix must be square."""
    with np.testing.assert_raises(ValueError):
        test_input_mat = np.array([[1, 2, 3, 4], [5, 6, 7, 8],
                                   [13, 14, 15, 16]])
        partial_transpose(test_input_mat)
コード例 #15
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 :code:`True` or :code:`False`, indicating that :code:`mat` does or does not have
    positive partial transpose (within numerical error). The variable :code:`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 :code:`True` would indicate that the state is separable and a value
    of :code:`False` would indicate the state is entangled.

    Examples
    ==========

    Consider the following matrix

    .. math::
        X =
        \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
    :code:`toqito` package.

    >>> from toqito.state_props 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 :code:`toqito` package.

    >>> from toqito.states import bell
    >>> from toqito.state_props 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 :code:`mat` acts.
    :param tol: Tolerance with which to check whether `mat` is PPT.
    :return: Returns :code:`True` if :code:`mat` is PPT and :code:`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_positive_semidefinite(partial_transpose(mat, sys, dim), tol)
コード例 #16
0
def realignment(input_mat: np.ndarray, dim=None) -> np.ndarray:
    r"""
    Compute the realignment of a bipartite operator [LAS08]_.

    Gives the realignment of the matrix :code:`input_mat`, where it is assumed
    that the number of rows and columns of :code:`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 :code:`input_mat` is non-square, different row and column dimensions can
    be specified by putting the row dimensions in the first row of :code:`dim`
    and the column dimensions in the second row of :code:`dim`.

    Examples
    ==========

    The standard realignment map

    Using :code:`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 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
    ==========
    .. [LAS08] Lupo, Cosmo, Paolo, Aniello, and Scardicchio, Antonello.
        "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([int(dim), int(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])

    # Dimension if row vector.
    if len(dim.shape) == 1:
        dim = dim[:].T
        dim = np.array([dim, dim])

    # Dimension is column vector.
    if min(dim.shape) == 1:
        dim = dim[:].T[0]
        dim = np.array([dim, 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)
コード例 #17
0
def dual_problem(
    states: List[np.ndarray], probs: List[float] = None, dist_method="min-error"
) -> float:
    r"""
    Calculate dual problem for PPT distinguishability.

    The minimum-error semidefinite program implemented is defined as:

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

    The unambiguous semidefinite program implemented is defined as:

    .. math::
    \begin{equation}
        \begin{aligned}
            \text{minimize:} \quad & \frac{1}{k} \text{Tr}(Y) \\
            \text{subject to:} \quad & Y - \rho_j + \sum_{\substack{i \leq i \leq k \\ i \not= j}}
                                        y_{i,j} \rho_i \geq T_{\mathcal{A}}(Q_j),
                                        \quad j = 1, \ldots, k, \\
                                     & Y \geq T_{\mathcal{A}}(Q_{k+1}), \\
                                     & Y \in \text{Herm}(\mathcal{A} \otimes
                                        \mathcal{B}), \\
                                     & Q_1, \ldots, Q_k \in
                                        \text{Pos}(\mathcal{A} \otimes \mathcal{B}), \\
                                     & y_{i,j} \in \mathcal{R}. \quad 1 \leq i, j \leq k,
                                        \quad i \not= j.
        \end{aligned}
    \end{equation}

    :param states: A list of states provided as either matrices or vectors.
    :param probs: Respective list of probabilities each state is selected.
    :param dist_method: Method of distinguishing to use.
    :return: The optimal value of the PPT dual problem SDP.
    """
    constraints = []
    meas = []

    dim_x, _ = states[0].shape

    y_var = cvxpy.Variable((dim_x, dim_x), hermitian=True)
    objective = 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))
    # dim_list = [3, 3]

    if dist_method == "min-error":
        for i, _ in enumerate(states):
            meas.append(cvxpy.Variable((dim_x, dim_x), PSD=True))
            constraints.append(
                cvxpy.real(y_var - probs[i] * states[i])
                >> partial_transpose(meas[i], sys=sys_list, dim=dim_list)
            )

    if dist_method == "unambiguous":
        for j, _ in enumerate(states):
            sum_val = 0
            for i, _ in enumerate(states):
                if i != j:
                    sum_val += cvxpy.real(cvxpy.Variable()) * probs[i] * states[i]
            meas.append(cvxpy.Variable((dim_x, dim_x), PSD=True))
            constraints.append(
                cvxpy.real(y_var - probs[j] * states[j] + sum_val)
                >> partial_transpose(meas[j], sys=sys_list, dim=dim_list)
            )

        meas.append(cvxpy.Variable((dim_x, dim_x), PSD=True))
        constraints.append(
            cvxpy.real(y_var) >> partial_transpose(meas[-1], sys=sys_list, dim=dim_list)
        )

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

    # print(np.around(y_var.value, decimals=3))

    return sol_default
コード例 #18
0
def test_partial_transpose_16_by_16_subsystems_2_2_2_2():
    """Partial transpose on a 16-by-16 matrix on 2 x 2 x 2 x 2 subsystems."""
    rho = np.arange(256).reshape(16, 16)

    # Partial transpose of first subsystem:
    res = partial_transpose(rho, [1], [2, 2, 2, 2])
    expected_res = np.array(
        [
            [0, 1, 2, 3, 4, 5, 6, 7, 128, 129, 130, 131, 132, 133, 134, 135],
            [16, 17, 18, 19, 20, 21, 22, 23, 144, 145, 146, 147, 148, 149, 150, 151],
            [32, 33, 34, 35, 36, 37, 38, 39, 160, 161, 162, 163, 164, 165, 166, 167],
            [48, 49, 50, 51, 52, 53, 54, 55, 176, 177, 178, 179, 180, 181, 182, 183],
            [64, 65, 66, 67, 68, 69, 70, 71, 192, 193, 194, 195, 196, 197, 198, 199],
            [80, 81, 82, 83, 84, 85, 86, 87, 208, 209, 210, 211, 212, 213, 214, 215],
            [96, 97, 98, 99, 100, 101, 102, 103, 224, 225, 226, 227, 228, 229, 230, 231],
            [112, 113, 114, 115, 116, 117, 118, 119, 240, 241, 242, 243, 244, 245, 246, 247],
            [8, 9, 10, 11, 12, 13, 14, 15, 136, 137, 138, 139, 140, 141, 142, 143],
            [24, 25, 26, 27, 28, 29, 30, 31, 152, 153, 154, 155, 156, 157, 158, 159],
            [40, 41, 42, 43, 44, 45, 46, 47, 168, 169, 170, 171, 172, 173, 174, 175],
            [56, 57, 58, 59, 60, 61, 62, 63, 184, 185, 186, 187, 188, 189, 190, 191],
            [72, 73, 74, 75, 76, 77, 78, 79, 200, 201, 202, 203, 204, 205, 206, 207],
            [88, 89, 90, 91, 92, 93, 94, 95, 216, 217, 218, 219, 220, 221, 222, 223],
            [104, 105, 106, 107, 108, 109, 110, 111, 232, 233, 234, 235, 236, 237, 238, 239],
            [120, 121, 122, 123, 124, 125, 126, 127, 248, 249, 250, 251, 252, 253, 254, 255],
        ]
    )
    bool_mat = np.isclose(expected_res, res)
    np.testing.assert_equal(np.all(bool_mat), True)

    # Partial transpose of second subsystem:
    res = partial_transpose(rho, [2], [2, 2, 2, 2])
    expected_res = np.array(
        [
            [0, 1, 2, 3, 64, 65, 66, 67, 8, 9, 10, 11, 72, 73, 74, 75],
            [16, 17, 18, 19, 80, 81, 82, 83, 24, 25, 26, 27, 88, 89, 90, 91],
            [32, 33, 34, 35, 96, 97, 98, 99, 40, 41, 42, 43, 104, 105, 106, 107],
            [48, 49, 50, 51, 112, 113, 114, 115, 56, 57, 58, 59, 120, 121, 122, 123],
            [4, 5, 6, 7, 68, 69, 70, 71, 12, 13, 14, 15, 76, 77, 78, 79],
            [20, 21, 22, 23, 84, 85, 86, 87, 28, 29, 30, 31, 92, 93, 94, 95],
            [36, 37, 38, 39, 100, 101, 102, 103, 44, 45, 46, 47, 108, 109, 110, 111],
            [52, 53, 54, 55, 116, 117, 118, 119, 60, 61, 62, 63, 124, 125, 126, 127],
            [128, 129, 130, 131, 192, 193, 194, 195, 136, 137, 138, 139, 200, 201, 202, 203],
            [144, 145, 146, 147, 208, 209, 210, 211, 152, 153, 154, 155, 216, 217, 218, 219],
            [160, 161, 162, 163, 224, 225, 226, 227, 168, 169, 170, 171, 232, 233, 234, 235],
            [176, 177, 178, 179, 240, 241, 242, 243, 184, 185, 186, 187, 248, 249, 250, 251],
            [132, 133, 134, 135, 196, 197, 198, 199, 140, 141, 142, 143, 204, 205, 206, 207],
            [148, 149, 150, 151, 212, 213, 214, 215, 156, 157, 158, 159, 220, 221, 222, 223],
            [164, 165, 166, 167, 228, 229, 230, 231, 172, 173, 174, 175, 236, 237, 238, 239],
            [180, 181, 182, 183, 244, 245, 246, 247, 188, 189, 190, 191, 252, 253, 254, 255],
        ]
    )
    bool_mat = np.isclose(expected_res, res)
    np.testing.assert_equal(np.all(bool_mat), True)

    # Partial transpose of third subsystem
    res = partial_transpose(rho, [3], [2, 2, 2, 2])
    expected_res = np.array(
        [
            [0, 1, 32, 33, 4, 5, 36, 37, 8, 9, 40, 41, 12, 13, 44, 45],
            [16, 17, 48, 49, 20, 21, 52, 53, 24, 25, 56, 57, 28, 29, 60, 61],
            [2, 3, 34, 35, 6, 7, 38, 39, 10, 11, 42, 43, 14, 15, 46, 47],
            [18, 19, 50, 51, 22, 23, 54, 55, 26, 27, 58, 59, 30, 31, 62, 63],
            [64, 65, 96, 97, 68, 69, 100, 101, 72, 73, 104, 105, 76, 77, 108, 109],
            [80, 81, 112, 113, 84, 85, 116, 117, 88, 89, 120, 121, 92, 93, 124, 125],
            [66, 67, 98, 99, 70, 71, 102, 103, 74, 75, 106, 107, 78, 79, 110, 111],
            [82, 83, 114, 115, 86, 87, 118, 119, 90, 91, 122, 123, 94, 95, 126, 127],
            [128, 129, 160, 161, 132, 133, 164, 165, 136, 137, 168, 169, 140, 141, 172, 173],
            [144, 145, 176, 177, 148, 149, 180, 181, 152, 153, 184, 185, 156, 157, 188, 189],
            [130, 131, 162, 163, 134, 135, 166, 167, 138, 139, 170, 171, 142, 143, 174, 175],
            [146, 147, 178, 179, 150, 151, 182, 183, 154, 155, 186, 187, 158, 159, 190, 191],
            [192, 193, 224, 225, 196, 197, 228, 229, 200, 201, 232, 233, 204, 205, 236, 237],
            [208, 209, 240, 241, 212, 213, 244, 245, 216, 217, 248, 249, 220, 221, 252, 253],
            [194, 195, 226, 227, 198, 199, 230, 231, 202, 203, 234, 235, 206, 207, 238, 239],
            [210, 211, 242, 243, 214, 215, 246, 247, 218, 219, 250, 251, 222, 223, 254, 255],
        ]
    )
    bool_mat = np.isclose(expected_res, res)
    np.testing.assert_equal(np.all(bool_mat), True)

    # Partial transpose of fourth subsystem
    res = partial_transpose(rho, [4], [2, 2, 2, 2])
    expected_res = np.array(
        [
            [0, 16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14, 30],
            [1, 17, 3, 19, 5, 21, 7, 23, 9, 25, 11, 27, 13, 29, 15, 31],
            [32, 48, 34, 50, 36, 52, 38, 54, 40, 56, 42, 58, 44, 60, 46, 62],
            [33, 49, 35, 51, 37, 53, 39, 55, 41, 57, 43, 59, 45, 61, 47, 63],
            [64, 80, 66, 82, 68, 84, 70, 86, 72, 88, 74, 90, 76, 92, 78, 94],
            [65, 81, 67, 83, 69, 85, 71, 87, 73, 89, 75, 91, 77, 93, 79, 95],
            [96, 112, 98, 114, 100, 116, 102, 118, 104, 120, 106, 122, 108, 124, 110, 126],
            [97, 113, 99, 115, 101, 117, 103, 119, 105, 121, 107, 123, 109, 125, 111, 127],
            [128, 144, 130, 146, 132, 148, 134, 150, 136, 152, 138, 154, 140, 156, 142, 158],
            [129, 145, 131, 147, 133, 149, 135, 151, 137, 153, 139, 155, 141, 157, 143, 159],
            [160, 176, 162, 178, 164, 180, 166, 182, 168, 184, 170, 186, 172, 188, 174, 190],
            [161, 177, 163, 179, 165, 181, 167, 183, 169, 185, 171, 187, 173, 189, 175, 191],
            [192, 208, 194, 210, 196, 212, 198, 214, 200, 216, 202, 218, 204, 220, 206, 222],
            [193, 209, 195, 211, 197, 213, 199, 215, 201, 217, 203, 219, 205, 221, 207, 223],
            [224, 240, 226, 242, 228, 244, 230, 246, 232, 248, 234, 250, 236, 252, 238, 254],
            [225, 241, 227, 243, 229, 245, 231, 247, 233, 249, 235, 251, 237, 253, 239, 255],
        ]
    )
    bool_mat = np.isclose(expected_res, res)
    np.testing.assert_equal(np.all(bool_mat), True)

    # Partial transpose of first and second subsystem:
    res = partial_transpose(rho, [1, 2], [2, 2, 2, 2])
    expected_res = np.array(
        [
            [0, 1, 2, 3, 64, 65, 66, 67, 128, 129, 130, 131, 192, 193, 194, 195],
            [16, 17, 18, 19, 80, 81, 82, 83, 144, 145, 146, 147, 208, 209, 210, 211],
            [32, 33, 34, 35, 96, 97, 98, 99, 160, 161, 162, 163, 224, 225, 226, 227],
            [48, 49, 50, 51, 112, 113, 114, 115, 176, 177, 178, 179, 240, 241, 242, 243],
            [4, 5, 6, 7, 68, 69, 70, 71, 132, 133, 134, 135, 196, 197, 198, 199],
            [20, 21, 22, 23, 84, 85, 86, 87, 148, 149, 150, 151, 212, 213, 214, 215],
            [36, 37, 38, 39, 100, 101, 102, 103, 164, 165, 166, 167, 228, 229, 230, 231],
            [52, 53, 54, 55, 116, 117, 118, 119, 180, 181, 182, 183, 244, 245, 246, 247],
            [8, 9, 10, 11, 72, 73, 74, 75, 136, 137, 138, 139, 200, 201, 202, 203],
            [24, 25, 26, 27, 88, 89, 90, 91, 152, 153, 154, 155, 216, 217, 218, 219],
            [40, 41, 42, 43, 104, 105, 106, 107, 168, 169, 170, 171, 232, 233, 234, 235],
            [56, 57, 58, 59, 120, 121, 122, 123, 184, 185, 186, 187, 248, 249, 250, 251],
            [12, 13, 14, 15, 76, 77, 78, 79, 140, 141, 142, 143, 204, 205, 206, 207],
            [28, 29, 30, 31, 92, 93, 94, 95, 156, 157, 158, 159, 220, 221, 222, 223],
            [44, 45, 46, 47, 108, 109, 110, 111, 172, 173, 174, 175, 236, 237, 238, 239],
            [60, 61, 62, 63, 124, 125, 126, 127, 188, 189, 190, 191, 252, 253, 254, 255],
        ]
    )
    bool_mat = np.isclose(expected_res, res)
    np.testing.assert_equal(np.all(bool_mat), True)

    # Partial transpose of first and third subsystem:
    res = partial_transpose(rho, [1, 3], [2, 2, 2, 2])
    expected_res = np.array(
        [
            [0, 1, 32, 33, 4, 5, 36, 37, 128, 129, 160, 161, 132, 133, 164, 165],
            [16, 17, 48, 49, 20, 21, 52, 53, 144, 145, 176, 177, 148, 149, 180, 181],
            [2, 3, 34, 35, 6, 7, 38, 39, 130, 131, 162, 163, 134, 135, 166, 167],
            [18, 19, 50, 51, 22, 23, 54, 55, 146, 147, 178, 179, 150, 151, 182, 183],
            [64, 65, 96, 97, 68, 69, 100, 101, 192, 193, 224, 225, 196, 197, 228, 229],
            [80, 81, 112, 113, 84, 85, 116, 117, 208, 209, 240, 241, 212, 213, 244, 245],
            [66, 67, 98, 99, 70, 71, 102, 103, 194, 195, 226, 227, 198, 199, 230, 231],
            [82, 83, 114, 115, 86, 87, 118, 119, 210, 211, 242, 243, 214, 215, 246, 247],
            [8, 9, 40, 41, 12, 13, 44, 45, 136, 137, 168, 169, 140, 141, 172, 173],
            [24, 25, 56, 57, 28, 29, 60, 61, 152, 153, 184, 185, 156, 157, 188, 189],
            [10, 11, 42, 43, 14, 15, 46, 47, 138, 139, 170, 171, 142, 143, 174, 175],
            [26, 27, 58, 59, 30, 31, 62, 63, 154, 155, 186, 187, 158, 159, 190, 191],
            [72, 73, 104, 105, 76, 77, 108, 109, 200, 201, 232, 233, 204, 205, 236, 237],
            [88, 89, 120, 121, 92, 93, 124, 125, 216, 217, 248, 249, 220, 221, 252, 253],
            [74, 75, 106, 107, 78, 79, 110, 111, 202, 203, 234, 235, 206, 207, 238, 239],
            [90, 91, 122, 123, 94, 95, 126, 127, 218, 219, 250, 251, 222, 223, 254, 255],
        ]
    )
    bool_mat = np.isclose(expected_res, res)
    np.testing.assert_equal(np.all(bool_mat), True)

    # Partial transpose of first and fourth subsystem
    res = partial_transpose(rho, [1, 4], [2, 2, 2, 2])
    expected_res = np.array(
        [
            [0, 16, 2, 18, 4, 20, 6, 22, 128, 144, 130, 146, 132, 148, 134, 150],
            [1, 17, 3, 19, 5, 21, 7, 23, 129, 145, 131, 147, 133, 149, 135, 151],
            [32, 48, 34, 50, 36, 52, 38, 54, 160, 176, 162, 178, 164, 180, 166, 182],
            [33, 49, 35, 51, 37, 53, 39, 55, 161, 177, 163, 179, 165, 181, 167, 183],
            [64, 80, 66, 82, 68, 84, 70, 86, 192, 208, 194, 210, 196, 212, 198, 214],
            [65, 81, 67, 83, 69, 85, 71, 87, 193, 209, 195, 211, 197, 213, 199, 215],
            [96, 112, 98, 114, 100, 116, 102, 118, 224, 240, 226, 242, 228, 244, 230, 246],
            [97, 113, 99, 115, 101, 117, 103, 119, 225, 241, 227, 243, 229, 245, 231, 247],
            [8, 24, 10, 26, 12, 28, 14, 30, 136, 152, 138, 154, 140, 156, 142, 158],
            [9, 25, 11, 27, 13, 29, 15, 31, 137, 153, 139, 155, 141, 157, 143, 159],
            [40, 56, 42, 58, 44, 60, 46, 62, 168, 184, 170, 186, 172, 188, 174, 190],
            [41, 57, 43, 59, 45, 61, 47, 63, 169, 185, 171, 187, 173, 189, 175, 191],
            [72, 88, 74, 90, 76, 92, 78, 94, 200, 216, 202, 218, 204, 220, 206, 222],
            [73, 89, 75, 91, 77, 93, 79, 95, 201, 217, 203, 219, 205, 221, 207, 223],
            [104, 120, 106, 122, 108, 124, 110, 126, 232, 248, 234, 250, 236, 252, 238, 254],
            [105, 121, 107, 123, 109, 125, 111, 127, 233, 249, 235, 251, 237, 253, 239, 255],
        ]
    )
    bool_mat = np.isclose(expected_res, res)
    np.testing.assert_equal(np.all(bool_mat), True)

    # Partial transpose of second and third subsystem:
    res = partial_transpose(rho, [2, 3], [2, 2, 2, 2])
    expected_res = np.array(
        [
            [0, 1, 32, 33, 64, 65, 96, 97, 8, 9, 40, 41, 72, 73, 104, 105],
            [16, 17, 48, 49, 80, 81, 112, 113, 24, 25, 56, 57, 88, 89, 120, 121],
            [2, 3, 34, 35, 66, 67, 98, 99, 10, 11, 42, 43, 74, 75, 106, 107],
            [18, 19, 50, 51, 82, 83, 114, 115, 26, 27, 58, 59, 90, 91, 122, 123],
            [4, 5, 36, 37, 68, 69, 100, 101, 12, 13, 44, 45, 76, 77, 108, 109],
            [20, 21, 52, 53, 84, 85, 116, 117, 28, 29, 60, 61, 92, 93, 124, 125],
            [6, 7, 38, 39, 70, 71, 102, 103, 14, 15, 46, 47, 78, 79, 110, 111],
            [22, 23, 54, 55, 86, 87, 118, 119, 30, 31, 62, 63, 94, 95, 126, 127],
            [128, 129, 160, 161, 192, 193, 224, 225, 136, 137, 168, 169, 200, 201, 232, 233],
            [144, 145, 176, 177, 208, 209, 240, 241, 152, 153, 184, 185, 216, 217, 248, 249],
            [130, 131, 162, 163, 194, 195, 226, 227, 138, 139, 170, 171, 202, 203, 234, 235],
            [146, 147, 178, 179, 210, 211, 242, 243, 154, 155, 186, 187, 218, 219, 250, 251],
            [132, 133, 164, 165, 196, 197, 228, 229, 140, 141, 172, 173, 204, 205, 236, 237],
            [148, 149, 180, 181, 212, 213, 244, 245, 156, 157, 188, 189, 220, 221, 252, 253],
            [134, 135, 166, 167, 198, 199, 230, 231, 142, 143, 174, 175, 206, 207, 238, 239],
            [150, 151, 182, 183, 214, 215, 246, 247, 158, 159, 190, 191, 222, 223, 254, 255],
        ]
    )
    bool_mat = np.isclose(expected_res, res)
    np.testing.assert_equal(np.all(bool_mat), True)

    # Partial transpose of second and fourth subsystem:
    res = partial_transpose(rho, [2, 4], [2, 2, 2, 2])
    expected_res = np.array(
        [
            [0, 16, 2, 18, 64, 80, 66, 82, 8, 24, 10, 26, 72, 88, 74, 90],
            [1, 17, 3, 19, 65, 81, 67, 83, 9, 25, 11, 27, 73, 89, 75, 91],
            [32, 48, 34, 50, 96, 112, 98, 114, 40, 56, 42, 58, 104, 120, 106, 122],
            [33, 49, 35, 51, 97, 113, 99, 115, 41, 57, 43, 59, 105, 121, 107, 123],
            [4, 20, 6, 22, 68, 84, 70, 86, 12, 28, 14, 30, 76, 92, 78, 94],
            [5, 21, 7, 23, 69, 85, 71, 87, 13, 29, 15, 31, 77, 93, 79, 95],
            [36, 52, 38, 54, 100, 116, 102, 118, 44, 60, 46, 62, 108, 124, 110, 126],
            [37, 53, 39, 55, 101, 117, 103, 119, 45, 61, 47, 63, 109, 125, 111, 127],
            [128, 144, 130, 146, 192, 208, 194, 210, 136, 152, 138, 154, 200, 216, 202, 218],
            [129, 145, 131, 147, 193, 209, 195, 211, 137, 153, 139, 155, 201, 217, 203, 219],
            [160, 176, 162, 178, 224, 240, 226, 242, 168, 184, 170, 186, 232, 248, 234, 250],
            [161, 177, 163, 179, 225, 241, 227, 243, 169, 185, 171, 187, 233, 249, 235, 251],
            [132, 148, 134, 150, 196, 212, 198, 214, 140, 156, 142, 158, 204, 220, 206, 222],
            [133, 149, 135, 151, 197, 213, 199, 215, 141, 157, 143, 159, 205, 221, 207, 223],
            [164, 180, 166, 182, 228, 244, 230, 246, 172, 188, 174, 190, 236, 252, 238, 254],
            [165, 181, 167, 183, 229, 245, 231, 247, 173, 189, 175, 191, 237, 253, 239, 255],
        ]
    )
    bool_mat = np.isclose(expected_res, res)
    np.testing.assert_equal(np.all(bool_mat), True)

    # Partial transpose of third and fourth subsystem
    res = partial_transpose(rho, [3, 4], [2, 2, 2, 2])
    expected_res = np.array(
        [
            [0, 16, 32, 48, 4, 20, 36, 52, 8, 24, 40, 56, 12, 28, 44, 60],
            [1, 17, 33, 49, 5, 21, 37, 53, 9, 25, 41, 57, 13, 29, 45, 61],
            [2, 18, 34, 50, 6, 22, 38, 54, 10, 26, 42, 58, 14, 30, 46, 62],
            [3, 19, 35, 51, 7, 23, 39, 55, 11, 27, 43, 59, 15, 31, 47, 63],
            [64, 80, 96, 112, 68, 84, 100, 116, 72, 88, 104, 120, 76, 92, 108, 124],
            [65, 81, 97, 113, 69, 85, 101, 117, 73, 89, 105, 121, 77, 93, 109, 125],
            [66, 82, 98, 114, 70, 86, 102, 118, 74, 90, 106, 122, 78, 94, 110, 126],
            [67, 83, 99, 115, 71, 87, 103, 119, 75, 91, 107, 123, 79, 95, 111, 127],
            [128, 144, 160, 176, 132, 148, 164, 180, 136, 152, 168, 184, 140, 156, 172, 188],
            [129, 145, 161, 177, 133, 149, 165, 181, 137, 153, 169, 185, 141, 157, 173, 189],
            [130, 146, 162, 178, 134, 150, 166, 182, 138, 154, 170, 186, 142, 158, 174, 190],
            [131, 147, 163, 179, 135, 151, 167, 183, 139, 155, 171, 187, 143, 159, 175, 191],
            [192, 208, 224, 240, 196, 212, 228, 244, 200, 216, 232, 248, 204, 220, 236, 252],
            [193, 209, 225, 241, 197, 213, 229, 245, 201, 217, 233, 249, 205, 221, 237, 253],
            [194, 210, 226, 242, 198, 214, 230, 246, 202, 218, 234, 250, 206, 222, 238, 254],
            [195, 211, 227, 243, 199, 215, 231, 247, 203, 219, 235, 251, 207, 223, 239, 255],
        ]
    )
    bool_mat = np.isclose(expected_res, res)
    np.testing.assert_equal(np.all(bool_mat), True)
コード例 #19
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 :math:`rho`, assuming that the two subsystems on
    which :math:`rho` acts are of equal dimension (if the local dimensions are unequal, specify them
    in the optional :code:`dim` argument). The negativity of :math:`rho` is the sum of the absolute
    value of the negative eigenvalues of the partial transpose of :math:`rho`.

    Examples
    ==========

    Example of the negativity of density matrix of Bell state.

    >>> from toqito.states import bell
    >>> from toqito.state_props 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 (np.linalg.norm(partial_transpose(rho, 2, dim), ord="nuc") - 1) / 2
コード例 #20
0
def symmetric_extension_hierarchy(states: List[np.ndarray],
                                  probs: List[float] = None,
                                  level: int = 2) -> float:
    r"""
    Compute optimal value of the symmetric extension hierarchy SDP [Nav08]_.

    The probability of distinguishing a given set of states via PPT measurements serves as a natural
    upper bound to the value of obtaining via separable measurements. Due to the nature of separable
    measurements, it is not possible to optimize directly over these objects via semidefinite
    programming techniques.

    We can, however, construct a hierarchy of semidefinite programs that attains closer and closer
    approximations at the separable value via the techniques described in [Nav08].

    The mathematical form of this hierarchy implemented here is explicitly given from equation 4.55
    in [Cos15]_.

    .. math::

        \begin{equation}
            \begin{aligned}
                \text{maximize:} \quad & \sum_{k=1}^N p_k \langle \rho_k, \mu(k) \rangle, \\
                \text{subject to:} \quad & \sum_{k=1}^N \mu(k) =
                                           \mathbb{I}_{\mathcal{X} \otimes \mathcal{Y}}, \\
                                        & \text{Tr}_{\mathcal{Y}_2 \otimes \ldots \otimes
                                          \mathcal{Y}_s}(X_k) = \mu(k), \\
                                        & \left( \mathbb{I}_{\mathcal{X}} \otimes
                                          \Pi_{\mathcal{Y} \ovee \mathcal{Y}_2 \ovee \ldots \ovee
                                          \mathcal{Y}_s} \right) X_k
                                          \left(\mathbb{I}_{\mathcal{X}} \otimes
                                          \Pi_{\mathcal{Y} \ovee \mathcal{Y}_2 \ovee \ldots \ovee
                                          \mathcal{Y}_s} \right)
                                          = X_k \\
                                        & \text{T}_{\mathcal{X}}(X_k) \in \text{Pos}\left(
                                            \mathcal{X} \otimes \mathcal{Y} \otimes \mathcal{Y}_2
                                            \otimes \ldots \otimes \mathcal{Y}_s \right), \\
                                        & \text{T}_{\mathcal{Y}_2 \otimes \ldots \otimes
                                            \mathcal{Y}_s}(X_k) \in \text{Pos}\left(
                                            \mathcal{X} \otimes \mathcal{Y} \otimes \mathcal{Y}_2
                                            \otimes \ldots \otimes \mathcal{Y}_s \right), \\
                                        & X_1, \ldots, X_N \in
                                          \text{Pos}\left(\mathcal{X} \otimes \mathcal{Y} \otimes
                                          \mathcal{Y}_2 \otimes \ldots \otimes \mathcal{Y}_s
                                          \right).
            \end{aligned}
        \end{equation}

    Examples
    ==========

    It is known from [Cos15]_ that distinguishing three Bell states along with a resource state
    :math:`|\tau_{\epsilon}\rangle` via separable measurements has the following closed form

    .. math::
        \frac{1}{3} \left(2 + \sqrt{1 - \epsilon^2} \right)

    where the resource state is defined as

    .. math::
        |\tau_{\epsilon} \rangle = \sqrt{\frac{1+\epsilon}{2}} |00\rangle +
                                   \sqrt{\frac{1-\epsilon}{2}} |11\rangle.

    The value of optimally distinguishing these states via PPT measurements is strictly larger than
    the value one obtains from separable measurements. Calculating the first level of the hierarchy
    provides for us the optimal value of PPT measurements.

    Consider a fixed value of :math:`\epsilon = 0.5`.

    >>> from toqito.states import basis, bell
    >>> from toqito.perms import swap
    >>> import numpy as np
    >>>
    >>> e_0, e_1 = basis(2, 0), basis(2, 1)
    >>> e_00, e_11 = np.kron(e_0, e_0), np.kron(e_1, e_1)
    >>>
    >>> # Define the resource state.
    >>> eps = 0.5
    >>> eps_state = np.sqrt((1+eps)/2) * e_00 + np.sqrt((1-eps)/2) * e_11
    >>> eps_dm = eps_state * eps_state.conj().T
    >>>
    >>> # Define the ensemble of states to be distinguished.
    >>> states = [
    >>>     np.kron(bell(0) * bell(0).conj().T, eps_dm),
    >>>     np.kron(bell(1) * bell(1).conj().T, eps_dm),
    >>>     np.kron(bell(2) * bell(2).conj().T, eps_dm),
    >>>     np.kron(bell(3) * bell(3).conj().T, eps_dm),
    >>> ]
    >>>
    >>> # Ensure the distinguishability is conducted on the proper spaces.
    >>> states = [
    >>>     swap(states[0], [2, 3], [2, 2, 2, 2])
    >>>     swap(states[1], [2, 3], [2, 2, 2, 2])
    >>>     swap(states[2], [2, 3], [2, 2, 2, 2])
    >>> ]
    >>>
    >>> # Calculate the first level of the symmetric extension hierarchy. This
    >>> # is simply the value of optimally distinguishing via PPT measurements.
    >>> symmetric_extension_hierarchy(states=states, probs=None, level=1)
    0.9915817434994775
    >>>
    >>> # Calculating the second value gets closer to the separable value.
    >>> symmetric_extension_hierarchy(states=states, probs=None, level=2)
    0.958305796189204
    >>>
    >>> # As proven in [Cos15]_, the true separable value of distinguishing the
    >>> # three Bell states is:
    >>> 1/3 * (2 + np.sqrt(1 - eps**2))
    0.9553418012614794
    >>>
    >>> # Computing further levels of the hierarchy would eventually converge to
    >>> # this value, however, the higher the level, the more computationally
    >>> # demanding the SDP becomes.

    References
    ==========
    .. [Nav08] Navascués, Miguel.
        "Pure state estimation and the characterization of entanglement."
        Physical review letters 100.7 (2008): 070503.
        https://arxiv.org/abs/0707.4398

    .. [Cos15] Cosentino, Alessandro.
        "Quantum State Local Distinguishability via Convex Optimization"
        The University of Waterloo, Ph.D. Dissertation, 2015.
        https://uwspace.uwaterloo.ca/handle/10012/9572

    :param states: A list of states provided as either matrices or vectors.
    :param probs: Respective list of probabilities each state is selected.
    :param level: Level of the hierarchy to compute.
    :return: The optimal probability of the symmetric extension hierarchy SDP for level
            :code:`level`.
    """
    obj_func = []
    meas = []
    x_var = []
    constraints = []

    __is_states_valid(states)
    if probs is None:
        probs = [1 / len(states)] * len(states)
    __is_probs_valid(probs)

    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

    dim = int(np.log2(dim_x))
    dim_list = [dim] * (level + 1)
    # The `sys_list` variable contains the numbering pertaining to the symmetrically extended
    # spaces.
    sys_list = list(range(3, 3 + level - 1))
    sym = symmetric_projection(dim, level)

    dim_xy = dim_x
    dim_xyy = np.prod(dim_list)
    for k, _ in enumerate(states):
        meas.append(cvxpy.Variable((dim_xy, dim_xy), PSD=True))
        x_var.append(cvxpy.Variable((dim_xyy, dim_xyy), PSD=True))
        constraints.append(
            partial_trace(x_var[k], sys_list, dim_list) == meas[k])
        constraints.append(
            np.kron(np.identity(dim), sym) @ x_var[k] @ np.kron(
                np.identity(dim), sym) == x_var[k])
        constraints.append(partial_transpose(x_var[k], 1, dim_list) >> 0)
        for sys in range(level - 1):
            constraints.append(
                partial_transpose(x_var[k], sys + 3, dim_list) >> 0)

        obj_func.append(probs[k] * cvxpy.trace(states[k].conj().T @ meas[k]))

    constraints.append(sum(meas) == np.identity(dim_xy))

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

    return sol_default