def is_unital( phi: Union[np.ndarray, List[List[np.ndarray]]], rtol: float = 1e-05, atol: float = 1e-08, ) -> bool: r""" Determine whether the given channel is unital [WatUnital18]_. A map :math:`\Phi \in \text{T} \left(\mathcal{X}, \mathcal{Y} \right)` is *unital* if it holds that .. math:: \Phi(\mathbb{I}_{\mathcal{X}}) = \mathbb{I}_{\mathcal{Y}}. Examples ========== Consider the channel whose Choi matrix is the swap operator. This channel is an example of a unital channel. >>> from toqito.perms import swap_operator >>> from toqito.channel_props import is_unital >>> >>> choi = swap_operator(3) >>> is_unital(choi) True Alternatively, the channel whose Choi matrix is the depolarizing channel is an example of a non-unital channel. >>> from toqito.channels import depolarizing >>> from toqito.channel_props import is_unital >>> >>> choi = depolarizing(4) >>> is_unital(choi) False References ========== .. [WatUnital18] Watrous, John. "The theory of quantum information." Chapter: Unital channels and majorization 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: :code:`True` if the channel is unital, and :code:`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) dim = int(np.sqrt(phi.shape[0])) # Channel is unital if :code:`mat` is the identity matrix. mat = apply_channel(np.identity(dim), phi) return is_identity(mat, rtol=rtol, atol=atol)
def is_quantum_channel( phi: Union[np.ndarray, List[List[np.ndarray]]], rtol: float = 1e-05, atol: float = 1e-08, ) -> bool: r""" Determine whether the given input is a quantum channel [WatQC18]_. A map :math:`\Phi \in \text{T} \left(\mathcal{X}, \mathcal{Y} \right)` is a *quantum channel* for some choice of complex Euclidean spaces :math:`\mathcal{X}` and :math:`\mathcal{Y}`, if it holds that: 1. :math:`\Phi` is completely positive. 2. :math:`\Phi` is trace preserving. 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}. References ========== .. [WatQC18] Watrous, John. "The Theory of Quantum Information." Section: "2.2.1 Definitions and basic notions concerning channels". 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: :code:`True` if the channel is a quantum channel, and :code:`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) # A valid quantum channel is a superoperator that is both completely # positive and trace-preserving. return is_completely_positive(phi, rtol, atol) and is_trace_preserving( phi, rtol, atol)
def test_kraus_to_choi_dephasing_channel(): """Kraus operators for dephasing channel should yield the proper Choi matrix.""" kraus_1 = np.array([[1, 0], [0, 0]]) kraus_2 = np.array([[1, 0], [0, 0]]) kraus_3 = np.array([[0, 0], [0, 1]]) kraus_4 = np.array([[0, 0], [0, 1]]) kraus_ops = [[kraus_1, kraus_2], [kraus_3, kraus_4]] choi_res = kraus_to_choi(kraus_ops) expected_choi_res = dephasing(2) bool_mat = np.isclose(choi_res, expected_choi_res) np.testing.assert_equal(np.all(bool_mat), True)
def test_kraus_to_choi_depolarizing_channel(): """Kraus operators for depolarizing channel should yield the proper Choi matrix.""" kraus_1 = np.array([[1 / np.sqrt(2), 0], [0, 0]]) kraus_2 = np.array([[1 / np.sqrt(2), 0], [0, 0]]) kraus_3 = np.array([[0, 0], [1 / np.sqrt(2), 0]]) kraus_4 = np.array([[0, 0], [1 / np.sqrt(2), 0]]) kraus_5 = np.array([[0, 1 / np.sqrt(2)], [0, 0]]) kraus_6 = np.array([[0, 1 / np.sqrt(2)], [0, 0]]) kraus_7 = np.array([[0, 0], [0, 1 / np.sqrt(2)]]) kraus_8 = np.array([[0, 0], [0, 1 / np.sqrt(2)]]) kraus_ops = [ [kraus_1, kraus_2], [kraus_3, kraus_4], [kraus_5, kraus_6], [kraus_7, kraus_8], ] choi_res = kraus_to_choi(kraus_ops) expected_choi_res = depolarizing(2) bool_mat = np.isclose(choi_res, expected_choi_res) np.testing.assert_equal(np.all(bool_mat), True)
def test_kraus_to_choi_max_ent_2(): """Choi matrix of the transpose map is the swap operator.""" kraus_1 = np.array([[1, 0], [0, 0]]) kraus_2 = np.array([[1, 0], [0, 0]]).conj().T kraus_3 = np.array([[0, 1], [0, 0]]) kraus_4 = np.array([[0, 1], [0, 0]]).conj().T kraus_5 = np.array([[0, 0], [1, 0]]) kraus_6 = np.array([[0, 0], [1, 0]]).conj().T kraus_7 = np.array([[0, 0], [0, 1]]) kraus_8 = np.array([[0, 0], [0, 1]]).conj().T kraus_ops = [ [kraus_1, kraus_2], [kraus_3, kraus_4], [kraus_5, kraus_6], [kraus_7, kraus_8], ] choi_res = kraus_to_choi(kraus_ops) expected_choi_res = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) bool_mat = np.isclose(choi_res, expected_choi_res) np.testing.assert_equal(np.all(bool_mat), True)
def test_kraus_to_choi_swap_operator_non_unique(): """As Kraus operators are non-unique, these also should yield the swap operator.""" kraus_1 = np.array([[0, 1 / np.sqrt(2)], [1 / np.sqrt(2), 0]]) kraus_2 = np.array([[0, 1 / np.sqrt(2)], [1 / np.sqrt(2), 0]]).conj().T kraus_3 = np.array([[1, 0], [0, 0]]) kraus_4 = np.array([[1, 0], [0, 0]]).conj().T kraus_5 = np.array([[0, 0], [0, 1]]) kraus_6 = np.array([[0, 0], [0, 1]]).conj().T kraus_7 = np.array([[0, 1 / np.sqrt(2)], [-1 / np.sqrt(2), 0]]) kraus_8 = np.array([[0, 1 / np.sqrt(2)], [-1 / np.sqrt(2), 0]]).conj().T kraus_ops = [ [kraus_1, kraus_2], [kraus_3, kraus_4], [kraus_5, kraus_6], [kraus_7, kraus_8], ] choi_res = kraus_to_choi(kraus_ops) expected_choi_res = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) bool_mat = np.isclose(choi_res, expected_choi_res) np.testing.assert_equal(np.all(bool_mat), True)
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)
def is_herm_preserving( phi: Union[np.ndarray, List[List[np.ndarray]]], rtol: float = 1e-05, atol: float = 1e-08, ) -> bool: r""" Determine whether the given channel is Hermitian-preserving [WatH18]_. A map :math:`\Phi \in \text{T} \left(\mathcal{X}, \mathcal{Y} \right)` is *Hermitian-preserving* if it holds that .. math:: \Phi(H) \in \text{Herm}(\mathcal{Y}) for every Hermitian operator :math:`H \in \text{Herm}(\mathcal{X})`. Examples ========== The map :math:`\Phi` defined as .. math:: \Phi(X) = X - U X U^* is Hermitian-preserving, where .. math:: U = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\ -1 & 1 \end{pmatrix}. >>> import numpy as np >>> from toqito.channel_props import is_herm_preserving >>> unitary_mat = np.array([[1, 1], [-1, 1]]) / np.sqrt(2) >>> kraus_ops = [[np.identity(2), np.identity(2)], [unitary_mat, -unitary_mat]] >>> is_herm_preserving(kraus_ops) True We may also verify whether the corresponding Choi matrix of a given map is Hermitian-preserving. The swap operator is the Choi matrix of the transpose map, which is Hermitian-preserving as can be seen as follows: >>> import numpy as np >>> from toqito.perms import swap_operator >>> from toqito.channel_props import is_herm_preserving >>> unitary_mat = np.array([[1, 1], [-1, 1]]) / np.sqrt(2) >>> choi_mat = swap_operator(3) >>> is_herm_preserving(choi_mat) True References ========== .. [WatH18] 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 Hermitian-preserving, 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) # Phi is Hermiticity-preserving if and only if its Choi matrix is Hermitian. if phi.shape[0] != phi.shape[1]: return False return is_hermitian(phi, rtol=rtol, atol=atol)
def is_positive(phi: Union[np.ndarray, List[List[np.ndarray]]], tol: float = 1e-05) -> bool: r""" Determine whether the given channel is positive [WatPM18]_. A map :math:`\Phi \in \text{T} \left(\mathcal{X}, \mathcal{Y} \right)` is *positive* if it holds that .. math:: \Phi(P) \in \text{Pos}(\mathcal{Y}) for every positive semidefinite operator :math:`P \in \text{Pos}(\mathcal{X})`. Alternatively, a channel is 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_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_positive(kraus_ops) False We can also specify the input as a Choi matrix. For instance, consider the Choi matrix corresponding to the :math:`4`-dimensional completely depolarizing channel and may verify that this channel is positive. >>> from toqito.channels import depolarizing >>> from toqito.channel_props import is_positive >>> is_positive(depolarizing(4)) True References ========== .. [WatPM18] 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 tol: The tolerance parameter to determine positivity. :return: True if the channel is 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) return is_psd(phi, tol)
def choi_rank(phi: Union[np.ndarray, List[List[np.ndarray]]]) -> int: r""" Calculate the rank of the Choi representation of a quantum channel [WatChoiRank18]_. Examples ========== The transpose map can be written either in Choi representation (as a SWAP operator) or in Kraus representation. If we choose the latter, it will be given by the following matrices: .. math:: \begin{equation} \begin{aligned} \frac{1}{\sqrt{2}} \begin{pmatrix} 0 & i \\ -i & 0 \end{pmatrix}, &\quad \frac{1}{\sqrt{2}} \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix}, \\ \begin{pmatrix} 1 & 0 \\ 0 & 0 \end{pmatrix}, &\quad \begin{pmatrix} 0 & 0 \\ 0 & 1 \end{pmatrix}. \end{aligned} \end{equation} and can be generated in :code:`toqito` with the following list: >>> import numpy as np >>> kraus_1 = np.array([[1, 0], [0, 0]]) >>> kraus_2 = np.array([[1, 0], [0, 0]]).conj().T >>> kraus_3 = np.array([[0, 1], [0, 0]]) >>> kraus_4 = np.array([[0, 1], [0, 0]]).conj().T >>> kraus_5 = np.array([[0, 0], [1, 0]]) >>> kraus_6 = np.array([[0, 0], [1, 0]]).conj().T >>> kraus_7 = np.array([[0, 0], [0, 1]]) >>> kraus_8 = np.array([[0, 0], [0, 1]]).conj().T >>> kraus_ops = [ >>> [kraus_1, kraus_2], >>> [kraus_3, kraus_4], >>> [kraus_5, kraus_6], >>> [kraus_7, kraus_8], >>> ] To calculate its Choi rank, we proceed in the following way: >>> from toqito.channel_props import choi_rank >>> choi_rank(kraus_ops) 4 We can the verify the associated Choi representation (the SWAP gate) gets the same Choi rank: >>> choi_matrix = np.array([[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]]) >>> choi_rank(choi_matrix) 4 References ========== .. [WatChoiRank18] Watrous, John. "The Theory of Quantum Information." Section: "2.2 Quantum Channels". Cambridge University Press, 2018. :param phi: Either a Choi matrix or a list of Kraus operators :return: The Choi rank of the provided channel representation. """ if isinstance(phi, list): phi = kraus_to_choi(phi) elif not isinstance(phi, np.ndarray): raise ValueError("Not a valid Choi matrix.") return np.linalg.matrix_rank(phi)