def is_idempotent(mat: np.ndarray, rtol: float = 1e-05, atol: float = 1e-8) -> bool: r""" Check if matrix is the idempotent matrix [WikIdempotent]_. An *idempotent matrix* is a square matrix, which, when multiplied by itself, yields itself. That is, the matrix :math:`A` is idempotent if and only if :math:`A^2 = A`. Examples ========== The following is an example of a :math:`2 x 2` idempotent matrix: .. math:: A = \begin{pmatrix} 3 & -6 \\ 1 & -2 \end{pmatrix} >>> from toqito.matrix_props import is_idempotent >>> import numpy as np >>> mat = np.array([[3, -6], [1, -2]]) >>> is_idempotent(mat) Alternatively, the following matrix .. math:: B = \begin{pmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{pmatrix} is not idempotent. >>> from toqito.matrix_props import is_idempotent >>> import numpy as np >>> mat = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> is_idempotent(mat) False References ========== .. [WikIdempotent] Wikipedia: Idempotent matrix https://en.wikipedia.org/wiki/Idempotent_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 the idempotent matrix, and :code:`False` otherwise. """ if not is_square(mat): return False return np.allclose(mat, mat @ mat, rtol=rtol, atol=atol)
def is_psd(mat: np.ndarray, tol: float = 1e-8) -> bool: r""" Check if matrix is positive semidefinite (PSD) [WikPSD]_. Examples ========== Consider the following matrix .. math:: A = \begin{pmatrix} 1 & -1 \\ -1 & 1 \end{pmatrix} our function indicates that this is indeed a positive semidefinite matrix. >>> from toqito.matrix_props import is_psd >>> import numpy as np >>> A = np.array([[1, -1], [-1, 1]]) >>> is_psd(A) True Alternatively, the following example matrix :math:`B` defined as .. math:: B = \begin{pmatrix} -1 & -1 \\ -1 & -1 \end{pmatrix} is not positive semidefinite. >>> from toqito.matrix_props import is_psd >>> import numpy as np >>> B = np.array([[-1, -1], [-1, -1]]) >>> is_psd(B) False References ========== .. [WikPSD] Wikipedia: Definiteness of a matrix. https://en.wikipedia.org/wiki/Definiteness_of_a_matrix :param mat: Matrix to check. :param tol: Tolerance for numerical accuracy. :return: Return True if matrix is PSD, and False otherwise. """ if not is_square(mat): return False return np.all(np.linalg.eigvalsh(mat) > -tol)
def is_diagonal(mat: np.ndarray) -> bool: r""" Determine if a matrix is diagonal [WikDiag]_. A matrix is diagonal if the matrix is square and if the diagonal of the matrix is non-zero, while the off-diagonal elements are all zero. The following is an example of a 3-by-3 diagonal matrix: .. math:: \begin{equation} \begin{pmatrix} 1 & 0 & 0 \\ 0 & 2 & 0 \\ 0 & 0 & 3 \end{pmatrix} \end{equation} This quick implementation is given by Daniel F. from StackOverflow in [SODIA]_. Examples ========== Consider the following diagonal matrix: .. math:: A = \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix}. Our function indicates that this is indeed a diagonal matrix: >>> from toqito.matrix_props import is_diagonal >>> import numpy as np >>> A = np.array([[1, 0], [0, 1]]) >>> is_diagonal(A) True Alternatively, the following example matrix .. math:: B = \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix} is not diagonal, as shown using :code:`toqito`. >>> from toqito.matrix_props import is_diagonal >>> import numpy as np >>> B = np.array([[1, 2], [3, 4]]) >>> is_diagonal(B) False References ========== .. [WikDiag] Wikipedia: Diagonal matrix https://en.wikipedia.org/wiki/Diagonal_matrix .. [SODIA] StackOverflow post https://stackoverflow.com/questions/43884189/ :param mat: The matrix to check. :return: Returns :code:`True` if the matrix is diagonal and :code:`False` otherwise. """ if not is_square(mat): return False i, j = mat.shape test = mat.reshape(-1)[:-1].reshape(i - 1, j + 1) return ~np.any(test[:, 1:])
def is_hermitian(mat: np.ndarray, rtol: float = 1e-05, atol: float = 1e-08) -> bool: r""" Check if matrix is Hermitian [WikHerm]_. A Hermitian matrix is a complex square matrix that is equal to its own conjugate transpose. Examples ========== Consider the following matrix: .. math:: A = \begin{pmatrix} 2 & 2 +1j & 4 \\ 2 - 1j & 3 & 1j \\ 4 & -1j & 1 \end{pmatrix} our function indicates that this is indeed a Hermitian matrix as it holds that .. math:: A = A^*. >>> from toqito.matrix_props import is_hermitian >>> import numpy as np >>> mat = np.array([[2, 2 + 1j, 4], [2 - 1j, 3, 1j], [4, -1j, 1]]) >>> is_hermitian(mat) True Alternatively, the following example matrix :math:`B` defined as .. math:: B = \begin{pmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{pmatrix} is not Hermitian. >>> from toqito.matrix_props import is_hermitian >>> import numpy as np >>> mat = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> is_hermitian(mat) False References ========== .. [WikHerm] Wikipedia: Hermitian matrix. https://en.wikipedia.org/wiki/Hermitian_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 True if matrix is Hermitian, and False otherwise. """ if not is_square(mat): return False return np.allclose(mat, mat.conj().T, rtol=rtol, atol=atol)
def test_is_square(): """Test that square matrix returns True.""" mat = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) np.testing.assert_equal(is_square(mat), True)
def test_is_not_square(): """Test that non-square matrix returns False.""" mat = np.array([[1, 2, 3], [4, 5, 6]]) np.testing.assert_equal(is_square(mat), False)
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)
def is_unitary(mat: np.ndarray, rtol: float = 1e-05, atol: float = 1e-08) -> bool: r""" Check if matrix is unitary [WikUnitary]_. A matrix is unitary if its inverse is equal to its conjugate transpose. Alternatively, a complex square matrix :math:`U` is unitary if its conjugate transpose :math:`U^*` is also its inverse, that is, if .. math:: \begin{equation} U^* U = U U^* = \mathbb{I}, \end{equation} where :math:`\mathbb{I}` is the identity matrix. Examples ========== Consider the following matrix .. math:: X = \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix} our function indicates that this is indeed a unitary matrix. >>> from toqito.matrix_props import is_unitary >>> import numpy as np >>> A = np.array([[0, 1], [1, 0]]) >>> is_unitary(A) True We may also use the `random_unitary` function from `toqito`, and can verify that a randomly generated matrix is unitary >>> from toqito.matrix_props import is_unitary >>> from toqito.random import random_unitary >>> mat = random_unitary(2) >>> is_unitary(mat) True Alternatively, the following example matrix :math:`B` defined as .. math:: B = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix} is not unitary. >>> from toqito.matrix_props import is_unitary >>> import numpy as np >>> B = np.array([[1, 0], [1, 1]]) >>> is_unitary(B) False References ========== .. [WikUnitary] Wikipedia: Unitary matrix. https://en.wikipedia.org/wiki/Unitary_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 unitary, and :code:`False` otherwise. """ if not is_square(mat): return False uc_u_mat = mat.conj().T @ mat u_uc_mat = mat @ mat.conj().T id_mat = np.eye(len(mat)) # If U^* * U = I U * U^*, the matrix "U" is unitary. return np.allclose(uc_u_mat, id_mat, rtol=rtol, atol=atol) and np.allclose( u_uc_mat, id_mat, rtol=rtol, atol=atol)
def test_is_square_invalid(): """Input must be a matrix.""" with np.testing.assert_raises(ValueError): is_square(np.array([-1, 1]))
def is_symmetric(mat: np.ndarray, rtol: float = 1e-05, atol: float = 1e-08) -> bool: r""" Determine if a matrix is symmetric [WikSym]_. The following 3x3 matrix is an example of a symmetric matrix: .. math:: \begin{pmatrix} 1 & 7 & 3 \\ 7 & 4 & -5 \\ 3 &-5 & 6 \end{pmatrix} Examples ========== Consider the following matrix .. math:: A = \begin{pmatrix} 1 & 7 & 3 \\ 7 & 4 & -5 \\ 3 & -5 & 6 \end{pmatrix} our function indicates that this is indeed a symmetric matrix. >>> from toqito.matrix_props import is_symmetric >>> import numpy as np >>> A = np.array([[1, 7, 3], [7, 4, -5], [3, -5, 6]]) >>> is_symmetric(A) True Alternatively, the following example matrix :math:`B` defined as .. math:: B = \begin{pmatrix} 1 & 2 \\ 4 & 5 \end{pmatrix} is not symmetric. >>> from toqito.matrix_props import is_symmetric >>> import numpy as np >>> B = np.array([[1, 2], [3, 4]]) >>> is_symmetric(B) False References ========== .. [WikSym] Wikipedia: Symmetric matrix https://en.wikipedia.org/wiki/Symmetric_matrix :param mat: The matrix to check. :param rtol: The relative tolerance parameter (default 1e-05). :param atol: The absolute tolerance parameter (default 1e-08). :return: Returns :code:`True` if the matrix is symmetric and :code:`False` otherwise. """ if not is_square(mat): return False return np.allclose(mat, mat.T, rtol=rtol, atol=atol)
def is_normal(mat: np.ndarray, rtol: float = 1e-05, atol: float = 1e-08) -> bool: r""" Determine if a matrix is normal [WikNormal]_. A matrix is normal if it commutes with its adjoint .. math:: \begin{equation} [X, X^*] = 0, \end{equation} or, equivalently if .. math:: \begin{equation} X^* X = X X^* \end{equation}. Examples ========== Consider the following matrix .. math:: A = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} our function indicates that this is indeed a normal matrix. >>> from toqito.matrix_props import is_normal >>> import numpy as np >>> A = np.identity(4) >>> is_normal(A) True Alternatively, the following example matrix :math:`B` defined as .. math:: B = \begin{pmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{pmatrix} is not normal. >>> from toqito.matrix_props import is_normal >>> import numpy as np >>> B = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> is_normal(B) False References ========== .. [WikNormal] Wikipedia: Normal matrix. https://en.wikipedia.org/wiki/Normal_matrix :param mat: The matrix to check. :param rtol: The relative tolerance parameter (default 1e-05). :param atol: The absolute tolerance parameter (default 1e-08). :return: Returns :code:`True` if the matrix is normal and :code:`False` otherwise. """ if not is_square(mat): return False return np.allclose(mat @ mat.conj().T, mat.conj().T @ mat, rtol=rtol, atol=atol)
def dual_channel( phi_op: Union[np.ndarray, List[np.ndarray], List[List[np.ndarray]]], dims: List[int] = None ) -> Union[np.ndarray, List[List[np.ndarray]]]: r""" Compute the dual of a map (quantum channel) [WatDChan18]_. The map can be represented as a Choi matrix, with optional specification of input and output dimensions. In this case the Choi matrix of the dual channel is returned, obtained by swapping input and output (see :func:`toqito.perms.swap`), and complex conjugating all elements. The map can also be represented as a list of Kraus operators. A list of lists, each containing two elements, corresponds to the families of operators :math:`\{(A_a, B_a)\}` representing the map .. math:: \Phi(X) = \sum_a A_a X B^*_a. The dual map is obtained by taking the Hermitian adjoint of each operator. If :code:`phi_op` is given as a one-dimensional list, :math:`\{A_a\}`, it is interpreted as the completely positive map .. math:: \Phi(X) = \sum_a A_a X A^*_a. References ========== .. [WatDChan18] Watrous, John. The theory of quantum information. Section: Representations and characterizations of channels. Cambridge University Press, 2018. :param phi_op: A superoperator. It should be provided either as a Choi matrix, or as a (1d or 2d) list of numpy arrays whose entries are its Kraus operators. :param dims: Dimension of the input and output systems, for Choi matrix representation. If :code:`None`, try to infer them from :code:`phi_op.shape`. :return: The map dual to :code:`phi_op`, in the same representation. """ # If phi_op is a list, assume it contains couples of Kraus operators # and take the Hermitian conjugate. if isinstance(phi_op, list): if isinstance(phi_op[0], list): return [[a.conj().T for a in x] for x in phi_op] if isinstance(phi_op[0], np.ndarray): return [a.conj().T for a in phi_op] # If phi_op is a `ndarray`, assume it is a Choi matrix. if isinstance(phi_op, np.ndarray): if len(phi_op.shape) == 2: if not is_square(phi_op): raise ValueError("Invalid: `phi_op` is not a valid Choi matrix (not square).") if dims is None: sqr = np.sqrt(phi_op.shape[0]) if sqr.is_integer(): dims = [int(round(sqr))] * 2 else: raise ValueError( "The dimensions `dims` of the input and output should be specified." ) return swap(phi_op.conj(), dim=dims) raise ValueError( "Invalid: The variable `phi_op` must either be a list of " "Kraus operators or as a Choi matrix." )
def is_identity(mat: np.ndarray, rtol: float = 1e-05, atol: float = 1e-8) -> bool: r""" Check if matrix is the identity matrix [WikIdentity]_. For dimension :math:`n`, the :math:`n \times n` identity matrix is defined as .. math:: I_n = \begin{pmatrix} 1 & 0 & 0 & \ldots & 0 \\ 0 & 1 & 0 & \ldots & 0 \\ 0 & 0 & 1 & \ldots & 0 \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & 0 & \ldots & 1 \end{pmatrix}. Examples ========== Consider the following matrix: .. math:: A = \begin{pmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{pmatrix} our function indicates that this is indeed the identity matrix of dimension 3. >>> from toqito.matrix_props import is_identity >>> import numpy as np >>> mat = np.eye(3) >>> is_identity(mat) True Alternatively, the following example matrix :math:`B` defined as .. math:: B = \begin{pmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{pmatrix} is not an identity matrix. >>> from toqito.matrix_props import is_identity >>> import numpy as np >>> mat = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> is_identity(mat) False References ========== .. [WikIdentity] Wikipedia: Identity matrix https://en.wikipedia.org/wiki/Identity_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 the identity matrix, and :code:`False` otherwise. """ if not is_square(mat): return False id_mat = np.eye(len(mat)) return np.allclose(mat, id_mat, rtol=rtol, atol=atol)