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 negativity(rho: np.ndarray, dim: Union[List[int], int] = None) -> float:
    r"""
    Compute the negativity of a bipartite quantum state.

    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`.

    References:
        [1] 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.º 4
0
def realignment(input_mat: np.ndarray, dim=None) -> np.ndarray:
    r"""
    Compute the realignment of a bipartite operator.

    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`.

    References:
        [1] 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.º 5
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.º 6
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.º 7
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.º 8
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.º 9
0
def is_ppt(mat: np.ndarray,
           sys: int = 2,
           dim: Union[int, List[int]] = None,
           tol: float = None) -> bool:
    """
    Determine whether or not a matrix has positive partial transpose.

    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 2 ⊗ 2 or 2 ⊗ 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.

    References:
        [1] 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)
def ppt_distinguishability(states: List[np.ndarray],
                           probs: List[float] = None) -> float:
    r"""
    Compute probability of distinguishing a state via PPT measurements.

    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
    [1].

    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}

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

    :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.º 11
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.º 12
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)