Python scipy.linalg.schur() Examples

The following are 5 code examples of scipy.linalg.schur(). You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may also want to check out all available functions/classes of the module scipy.linalg , or try the search function .
Example #1
Source File: matrixtools.py    From pyGSTi with Apache License 2.0 5 votes vote down vote up
def matrix_sign(M):
    """ The "sign" matrix of `M` """
    #Notes: sign(M) defined s.t. eigvecs of sign(M) are evecs of M
    # and evals of sign(M) are +/-1 or 0 based on sign of eigenvalues of M

    #Using the extremely numerically stable (but expensive) Schur method
    # see http://www.maths.manchester.ac.uk/~higham/fm/OT104HighamChapter5.pdf
    N = M.shape[0]; assert(M.shape == (N, N)), "M must be square!"
    T, Z = _spl.schur(M, 'complex')  # M = Z T Z^H where Z is unitary and T is upper-triangular
    U = _np.zeros(T.shape, 'complex')  # will be sign(T), which is easy to compute
    # (U is also upper triangular), and then sign(M) = Z U Z^H

    # diagonals are easy
    U[_np.diag_indices_from(U)] = _np.sign(_np.diagonal(T))

    #Off diagonals: use U^2 = I or TU = UT
    # Note: Tij = Uij = 0 when i > j and i==j easy so just consider i<j case
    # 0 = sum_k Uik Ukj =  (i!=j b/c off-diag)
    # FUTURE: speed this up by using np.dot instead of sums below
    for j in range(1, N):
        for i in range(j - 1, -1, -1):
            S = U[i, i] + U[j, j]
            if _np.isclose(S, 0):  # then use TU = UT
                if _np.isclose(T[i, i] - T[j, j], 0):  # then just set to zero
                    U[i, j] = 0.0  # TODO: check correctness of this case
                else:
                    U[i, j] = T[i, j] * (U[i, i] - U[j, j]) / (T[i, i] - T[j, j]) + \
                        sum([U[i, k] * T[k, j] - T[i, k] * U[k, j] for k in range(i + 1, j)]) \
                        / (T[i, i] - T[j, j])
            else:  # use U^2 = I
                U[i, j] = - sum([U[i, k] * U[k, j] for k in range(i + 1, j)]) / S
    return _np.dot(Z, _np.dot(U, _np.conjugate(Z.T)))

    #Quick & dirty - not always stable:
    #U,_,Vt = _np.linalg.svd(M)
    #return _np.dot(U,Vt) 
Example #2
Source File: gate.py    From qiskit-terra with Apache License 2.0 5 votes vote down vote up
def power(self, exponent: float):
        """Creates a unitary gate as `gate^exponent`.

        Args:
            exponent (float): Gate^exponent

        Returns:
            qiskit.extensions.UnitaryGate: To which `to_matrix` is self.to_matrix^exponent.

        Raises:
            CircuitError: If Gate is not unitary
        """
        from qiskit.quantum_info.operators import Operator  # pylint: disable=cyclic-import
        from qiskit.extensions.unitary import UnitaryGate  # pylint: disable=cyclic-import
        # Should be diagonalized because it's a unitary.
        decomposition, unitary = schur(Operator(self).data, output='complex')
        # Raise the diagonal entries to the specified power
        decomposition_power = list()

        decomposition_diagonal = decomposition.diagonal()
        # assert off-diagonal are 0
        if not np.allclose(np.diag(decomposition_diagonal), decomposition):
            raise CircuitError('The matrix is not diagonal')

        for element in decomposition_diagonal:
            decomposition_power.append(pow(element, exponent))
        # Then reconstruct the resulting gate.
        unitary_power = unitary @ np.diag(decomposition_power) @ unitary.conj().T
        return UnitaryGate(unitary_power, label='%s^%s' % (self.name, exponent)) 
Example #3
Source File: transformations.py    From qiskit-terra with Apache License 2.0 5 votes vote down vote up
def _choi_to_kraus(data, input_dim, output_dim, atol=ATOL_DEFAULT):
    """Transform Choi representation to Kraus representation."""
    # Check if hermitian matrix
    if is_hermitian_matrix(data, atol=atol):
        # Get eigen-decomposition of Choi-matrix
        # This should be a call to la.eigh, but there is an OpenBlas
        # threading issue that is causing segfaults.
        # Need schur here since la.eig does not
        # guarentee orthogonality in degenerate subspaces
        w, v = la.schur(data, output='complex')
        w = w.diagonal().real
        # Check eigenvalues are non-negative
        if len(w[w < -atol]) == 0:
            # CP-map Kraus representation
            kraus = []
            for val, vec in zip(w, v.T):
                if abs(val) > atol:
                    k = np.sqrt(val) * vec.reshape(
                        (output_dim, input_dim), order='F')
                    kraus.append(k)
            # If we are converting a zero matrix, we need to return a Kraus set
            # with a single zero-element Kraus matrix
            if not kraus:
                kraus.append(np.zeros((output_dim, input_dim), dtype=complex))
            return kraus, None
    # Non-CP-map generalized Kraus representation
    mat_u, svals, mat_vh = la.svd(data)
    kraus_l = []
    kraus_r = []
    for val, vec_l, vec_r in zip(svals, mat_u.T, mat_vh.conj()):
        kraus_l.append(
            np.sqrt(val) * vec_l.reshape((output_dim, input_dim), order='F'))
        kraus_r.append(
            np.sqrt(val) * vec_r.reshape((output_dim, input_dim), order='F'))
    return kraus_l, kraus_r 
Example #4
Source File: gateset_fitter.py    From qiskit-ignis with Apache License 2.0 5 votes vote down vote up
def get_cholesky_like_decomposition(mat: np.array) -> np.array:
    """Given a PSD matrix A, finds a matrix T such that TT^{dagger}
    is an approximation of A
    Args:
        mat: A nxn matrix, assumed to be positive semidefinite.
    Returns:
        A matrix T such that TT^{dagger} approximates A
    """
    decomposition, unitary = schur(mat, output='complex')
    eigenvals = np.array(decomposition.diagonal())
    # if a 0 eigenvalue is represented by infinitisimal negative float
    eigenvals[eigenvals < 0] = 0
    DD = np.diag(np.sqrt(eigenvals))
    return unitary @ DD 
Example #5
Source File: krylovutils.py    From sharpy with BSD 3-Clause "New" or "Revised" License 4 votes vote down vote up
def schur_ordered(A, ct=False):
    r"""Returns block ordered complex Schur form of matrix :math:`\mathbf{A}`

    .. math:: \mathbf{TAT}^H = \mathbf{A}_s = \begin{bmatrix} A_{11} & A_{12} \\ 0 & A_{22} \end{bmatrix}

    where :math:`A_{11}\in\mathbb{C}^{s\times s}` contains the :math:`s` stable
    eigenvalues of :math:`\mathbf{A}\in\mathbb{R}^{m\times m}`.

    Args:
        A (np.ndarray): Matrix to decompose.
        ct (bool): Continuous time system.

    Returns:
        tuple: Tuple containing the Schur decomposition of :math:`\mathbf{A}`, :math:`\mathbf{A}_s`; the transformation
        :math:`\mathbf{T}\in\mathbb{C}^{m\times m}`; and the number of stable eigenvalues of :math:`\mathbf{A}`.

    Notes:
        This function is a wrapper of ``scipy.linalg.schur`` imposing the settings required for this application.

    """
    if ct:
        sort_eigvals = 'lhp'
    else:
        sort_eigvals = 'iuc'

    # if A.dtype == complex:
    #     output_form = 'complex'
    # else:
    #     output_form = 'real'
    # issues when not using the complex form of the Schur decomposition

    output_form = 'complex'
    As, Tt, n_stable1 = sclalg.schur(A, output=output_form, sort=sort_eigvals)

    if sort_eigvals == 'lhp':
        n_stable = np.sum(np.linalg.eigvals(A).real <= 0)
    elif sort_eigvals == 'iuc':
        n_stable = np.sum(np.abs(np.linalg.eigvals(A)) <= 1.)
    else:
        raise NameError('Unknown sorting of eigenvalues. Either iuc or lhp')

    assert n_stable == n_stable1, 'Number of stable eigenvalues not equal in Schur output and manual calculation'

    assert (np.abs(As-np.conj(Tt.T).dot(A.dot(Tt))) < 1e-4).all(), 'Schur breakdown - A_schur != T^H A T'
    return As, Tt.T, n_stable