示例#1
0
def is_ensemble(states: List[np.ndarray]) -> bool:
    r"""
    Determine if a set of states constitute an ensemble [WatEns18]_.

    An ensemble of quantum states is defined by a function

    .. math::
        \eta : \Gamma \rightarrow \text{Pos}(\mathcal{X})

    that satisfies

    .. math::
        \text{Tr}\left( \sum_{a \in \Gamma} \eta(a) \right) = 1.

    Examples
    ==========

    Consider the following set of matrices

    .. math::
        \eta = \left\{ \rho_0, \rho_1 \right\}

    where

    .. math::
        \rho_0 = \frac{1}{2} \begin{pmatrix} 1 & 0 \\ 0 & 0 \end{pmatrix}, \quad
        \rho_1 = \frac{1}{2} \begin{pmatrix} 0 & 0 \\ 0 & 1 \end{pmatrix}.

    The set :math:`\eta` constitutes a valid ensemble.

    >>> from toqito.state_props import is_ensemble
    >>> import numpy as np
    >>> rho_0 = np.array([[0.5, 0], [0, 0]])
    >>> rho_1 = np.array([[0, 0], [0, 0.5]])
    >>> states = [rho_0, rho_1]
    >>> is_ensemble(states)
    True

    References
    ==========
    .. [WatEns18] Watrous, John.
        "The theory of quantum information."
        Section: "Ensemble of quantum states".
        Cambridge University Press, 2018.

    :param states: The list of states to check.
    :return: :code:`True` if states form an ensemble and :code:`False` otherwise.
    """
    trace_sum = 0
    for state in states:
        trace_sum += np.trace(state)
        # Constraint: All states in ensemble must be positive semidefinite.
        if not is_positive_semidefinite(state):
            return False
    # Constraint: The sum of the traces of all states within the ensemble must
    # be equal to 1.
    return np.allclose(trace_sum, 1)
示例#2
0
def is_completely_positive(
    phi: Union[np.ndarray, List[List[np.ndarray]]],
    rtol: float = 1e-05,
    atol: float = 1e-08,
) -> bool:
    r"""
    Determine whether the given channel is completely positive [WatCP18]_.

    A map :math:`\Phi \in \text{T} \left(\mathcal{X}, \mathcal{Y} \right)` is *completely
    positive* if it holds that

    .. math::
        \Phi \otimes \mathbb{I}_{\text{L}(\mathcal{Z})}

    is a positive map for every complex Euclidean space :math:`\mathcal{Z}`.

    Alternatively, a channel is completely positive if the corresponding Choi matrix of the
    channel is both Hermitian-preserving and positive semidefinite.

    Examples
    ==========

    We can specify the input as a list of Kraus operators. Consider the map :math:`\Phi` defined as

    .. math::
        \Phi(X) = X - U X U^*

    where

    .. math::
        U = \frac{1}{\sqrt{2}}
        \begin{pmatrix}
            1 & 1 \\
            -1 & 1
        \end{pmatrix}.

    This map is not completely positive, as we can verify as follows.

    >>> from toqito.channel_props import is_completely_positive
    >>> import numpy as np
    >>> unitary_mat = np.array([[1, 1], [-1, 1]]) / np.sqrt(2)
    >>> kraus_ops = [[np.identity(2), np.identity(2)], [unitary_mat, -unitary_mat]]
    >>> is_completely_positive(kraus_ops)
    False

    We can also specify the input as a Choi matrix. For instance, consider the Choi matrix
    corresponding to the :math:`2`-dimensional completely depolarizing channel

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

    We may verify that this channel is completely positive

    >>> from toqito.channels import depolarizing
    >>> from toqito.channel_props import is_completely_positive
    >>> is_completely_positive(depolarizing(2))
    True

    References
    ==========
    .. [WatCP18] Watrous, John.
        "The Theory of Quantum Information."
        Section: "Linear maps of square operators".
        Cambridge University Press, 2018.

    :param phi: The channel provided as either a Choi matrix or a list of Kraus operators.
    :param rtol: The relative tolerance parameter (default 1e-05).
    :param atol: The absolute tolerance parameter (default 1e-08).
    :return: True if the channel is completely positive, and False otherwise.
    """
    # If the variable `phi` is provided as a list, we assume this is a list
    # of Kraus operators.
    if isinstance(phi, list):
        phi = kraus_to_choi(phi)

    # Use Choi's theorem to determine whether :code:`phi` is completely positive.
    return is_herm_preserving(phi, rtol, atol) and is_positive_semidefinite(phi, rtol, atol)
示例#3
0
def is_projection(mat: np.ndarray, rtol: float = 1e-05, atol: float = 1e-08) -> bool:
    r"""
    Check if matrix is a projection matrix [WikProj]_.

    A matrix is a projection matrix if it is positive semidefinite (PSD) and if

    .. math::
        \begin{equation}
            X^2 = X
        \end{equation}

    where :math:`X` is the matrix in question.

    Examples
    ==========

    Consider the following matrix

    .. math::
        A = \begin{pmatrix}
                0 & 1 \\
                0 & 1
            \end{pmatrix}

    our function indicates that this is indeed a projection matrix.

    >>> from toqito.matrix_props import is_projection
    >>> import numpy as np
    >>> A = np.array([[0, 1], [0, 1]])
    >>> is_projection(A)
    True

    Alternatively, the following example matrix :math:`B` defined as

    .. math::
        B = \begin{pmatrix}
                -1 & -1 \\
                -1 & -1
            \end{pmatrix}

    is not positive definite.

    >>> from toqito.matrix_props import is_projection
    >>> import numpy as np
    >>> B = np.array([[-1, -1], [-1, -1]])
    >>> is_projection(B)
    False

    References
    ==========
    .. [WikProj] Wikipedia: Projection matrix.
        https://en.wikipedia.org/wiki/Projection_matrix

    :param mat: Matrix to check.
    :param rtol: The relative tolerance parameter (default 1e-05).
    :param atol: The absolute tolerance parameter (default 1e-08).
    :return: Return :code:`True` if matrix is a projection matrix, and :code:`False` otherwise.
    """
    if not is_square(mat):
        return False

    if not is_positive_semidefinite(mat):
        return False
    return np.allclose(np.linalg.matrix_power(mat, 2), mat, rtol=rtol, atol=atol)
示例#4
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)
示例#5
0
def is_density(mat: np.ndarray) -> bool:
    r"""
    Check if matrix is a density matrix [WikDensity]_.

    A matrix is a density matrix if its trace is equal to one and it has the
    property of being positive semidefinite (PSD).

    Examples
    ==========

    Consider the Bell state:

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

    Constructing the matrix :math:`\rho = u u^*` defined as

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

    our function indicates that this is indeed a density operator as the trace
    of :math:`\rho` is equal to :math:`1` and the matrix is positive
    semidefinite:

    >>> from toqito.matrix_props import is_density
    >>> from toqito.states import bell
    >>> import numpy as np
    >>> rho = bell(0) * bell(0).conj().T
    >>> is_density(rho)
    True

    Alternatively, the following example matrix :math:`\sigma` defined as

    .. math::
        \sigma = \frac{1}{2} \begin{pmatrix}
                                1 & 2 \\
                                3 & 1
                             \end{pmatrix}

    does satisfy :math:`\text{Tr}(\sigma) = 1`, however fails to be positive
    semidefinite, and is therefore not a density operator. This can be
    illustrated using :code:`toqito` as follows.

    >>> from toqito.matrix_props import is_density
    >>> from toqito.states import bell
    >>> import numpy as np
    >>> sigma = 1/2 * np.array([[1, 2], [3, 1]])
    >>> is_density(sigma)
    False

    References
    ==========
    .. [WikDensity] Wikipedia: Density matrix
        https://en.wikipedia.org/wiki/Density_matrix

    :param mat: Matrix to check.
    :return: Return `True` if matrix is a density matrix, and `False`
             otherwise.
    """
    return is_positive_semidefinite(mat) and np.isclose(np.trace(mat), 1)
示例#6
0
def has_symmetric_extension(rho, level=2, dim=None, ppt=True, tol=1e-4) -> bool:
    r"""
    Determine whether there exists a symmetric extension for a given quantum state. [DPS02]_.

    Determining whether an operator possesses a symmetric extension at some level :code:`level`
    can be used as a check to determine if the operator is entangled or not.

    This function was adapted from QETLAB.

    Examples
    ==========

    2-qubit symmetric extension:

    In [CJKLZB14]_, it was shown that a 2-qubit state :math:`\rho_{AB}` has a
    symmetric extension if and only if

    .. math::
        \text{Tr}(\rho_B^2) \geq \text{Tr}(\rho_{AB}^2) - 4 \sqrt{\text{det}(\rho_{AB})}.

    This closed-form equation is much quicker to check than running the semidefinite program.

    >>> import numpy as np
    >>> from toqito.state_props import has_symmetric_extension
    >>> rho = np.array([[1, 0, 0, -1],
    >>>                 [0, 1, 1/2, 0],
    >>>                 [0, 1/2, 1, 0],
    >>>                 [-1, 0, 0, 1]])
    >>> # Show the closed-form equation holds
    >>> np.trace(partial_trace(rho, 1)**2) >= np.trace(rho**2) - 4 * np.sqrt(np.linalg.det(rho))
    True
    >>> # Now show that the `has_symmetric_extension` function recognizes this case.
    >>> has_symmetric_extension(rho)
    True

    Higher qubit systems:

    Consider a density operator corresponding to one of the Bell states.

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

    To make this state over more than just two qubits, let's construct the following state

    .. math::
        \sigma = \rho \otimes \rho.

    As the state :math:`\sigma` is entangled, there should not exist a symmetric extension at some
    level. We see this being the case for a relatively low level of the hierachy.

    >>> import numpy as np
    >>> from toqito.states import bell
    >>> from toqito.state_props import has_symmetric_extension
    >>>
    >>> rho = bell(0) * bell(0).conj().T
    >>> sigma = np.kron(rho, rho)
    >>> has_symmetric_extension(sigma)
    False

    References
    ==========
    .. [DPS02] Doherty, Andrew C., Pablo A. Parrilo, and Federico M. Spedalieri.
        "Distinguishing separable and entangled states."
        Physical Review Letters 88.18 (2002): 187904.
        https://arxiv.org/abs/quant-ph/0112007

    .. [CJKLZB14] Chen, J., Ji, Z., Kribs, D., Lütkenhaus, N., & Zeng, B.
        "Symmetric extension of two-qubit states."
        Physical Review A 90.3 (2014): 032318.
        https://arxiv.org/abs/1310.3530

    :param rho: A matrix or vector.
    :param level: Level of the hierarchy to compute.
    :param dim: The default has both subsystems of equal dimension.
    :param ppt: If :code:`True`, this enforces that the symmetric extension must be PPT.
    :param tol: Tolerance when determining whether a symmetric extension exists.
    :return: :code:`True` if :code:`mat` has a symmetric extension; :code:`False` otherwise.
    """
    len_mat = rho.shape[1]

    # Set default dimension if none was provided.
    if dim is None:
        dim = int(np.round(np.sqrt(len_mat)))

    # Allow the user to enter in a single integer for dimension.
    if isinstance(dim, int):
        dim = np.array([dim, len_mat / dim])
        if np.abs(dim[1] - np.round(dim[1])) >= 2 * len_mat * np.finfo(float).eps:
            raise ValueError(
                "If `dim` is a scalar, it must evenly divide the length of the matrix."
            )
        dim[1] = int(np.round(dim[1]))

    dim_x, dim_y = int(dim[0]), int(dim[1])
    # In certain situations, we don't need semidefinite programming.
    if level == 1 or len_mat <= 6 and ppt:
        if not ppt:
            # In some cases, the problem is *really* trivial.
            return is_positive_semidefinite(rho)

        # In this case, all they asked for is a 1-copy PPT symmetric extension
        # (i.e., they're asking if the state is PPT).
        return is_ppt(rho, 2, dim) and is_positive_semidefinite(rho)

    # In the 2-qubit case, an analytic formula is known for whether or not a state has a
    # (2-copy, non-PPT) symmetric extension that is much faster to use than semidefinite
    # programming [CJKLZB14]_.
    if level == 2 and not ppt and dim_x == 2 and dim_y == 2:
        return np.trace(np.linalg.matrix_power(partial_trace(rho, 1), 2)) >= np.trace(
            np.linalg.matrix_power(rho, 2)
        ) - 4 * np.sqrt(np.linalg.det(rho))

    # Otherwise, use semidefinite programming to find a symmetric extension.
    # If the optimal value of the symmetric extension hierarchy is equal to 1,
    # this indicates that there does not exist a symmetric extension at
    # level :code:`level`.
    return not np.isclose(
        (1 - min(symmetric_extension_hierarchy([rho], probs=None, level=level), 1)),
        0,
        atol=tol,
    )
示例#7
0
def test_is_positive_semidefinite():
    """Test that positive semidefinite matrix returns True."""
    mat = np.array([[1, -1], [-1, 1]])
    np.testing.assert_equal(is_positive_semidefinite(mat), True)
示例#8
0
def test_is_not_positive_semidefinite():
    """Test that non-positive semidefinite matrix returns False."""
    mat = np.array([[-1, -1], [-1, -1]])
    np.testing.assert_equal(is_positive_semidefinite(mat), False)
示例#9
0
def is_povm(mat_list: List[np.ndarray]) -> bool:
    r"""
    Determine if a list of matrices constitute a valid set of POVMs [WikPOVM]_.

    A valid set of measurements are defined by a set of positive semidefinite operators

    .. math::
         \{P_a : a \in \Gamma\} \subset \text{Pos}(\mathcal{X}),

    indexed by the alphabet :math:`\Gamma` of measurement outcomes satisfying the constraint that

    .. math::
        \sum_{a \in \Gamma} P_a = I_{\mathcal{X}}.

    Examples
    ==========

    Consider the following matrices:

    .. math::
        M_0 =
        \begin{pmatrix}
            1 & 0 \\
            0 & 0
        \end{pmatrix}
        \quad \text{and} \quad
        M_1 =
        \begin{pmatrix}
            0 & 0 \\
            0 & 1
        \end{pmatrix}.

    Our function indicates that this set of operators constitute a set of
    POVMs.

    >>> from toqito.measurement_props import is_povm
    >>> import numpy as np
    >>> meas_1 = np.array([[1, 0], [0, 0]])
    >>> meas_2 = np.array([[0, 0], [0, 1]])
    >>> meas = [meas_1, meas_2]
    >>> is_povm(meas)
    True

    We may also use the :code:`random_povm` function from :code:`toqito`, and can verify that a
    randomly generated set satisfies the criteria for being a POVM set.

    >>> from toqito.measurement_props import is_povm
    >>> from toqito.random import random_povm
    >>> import numpy as np
    >>> dim, num_inputs, num_outputs = 2, 2, 2
    >>> measurements = random_povm(dim, num_inputs, num_outputs)
    >>> is_povm([measurements[:, :, 0, 0], measurements[:, :, 0, 1]])
    True

    Alternatively, the following matrices

    .. math::
        M_0 =
        \begin{pmatrix}
            1 & 2 \\
            3 & 4
        \end{pmatrix}
        \quad \text{and} \quad
        M_1 =
        \begin{pmatrix}
            5 & 6 \\
            7 & 8
        \end{pmatrix},

    do not constitute a POVM set.

    >>> from toqito.measurement_props import is_povm
    >>> import numpy as np
    >>> non_meas_1 = np.array([[1, 2], [3, 4]])
    >>> non_meas_2 = np.array([[5, 6], [7, 8]])
    >>> non_meas = [non_meas_1, non_meas_2]
    >>> is_povm(non_meas)
    False

    References
    ==========
    .. [WikPOVM] Wikipedia: POVM
        https://en.wikipedia.org/wiki/POVM

    :param mat_list: A list of matrices.
    :return: Return :code:`True` if set of matrices constitutes a set of
             measurements, and :code:`False` otherwise.
    """
    dim = mat_list[0].shape[0]

    mat_sum = np.zeros((dim, dim), dtype=complex)
    for mat in mat_list:
        # Each measurement in the set must be positive semidefinite.
        if not is_positive_semidefinite(mat):
            return False
        mat_sum += mat
    # Summing all the measurements from the set must be equal to the identity.
    if not np.allclose(np.identity(dim), mat_sum):
        return False
    return True