Example #1
0
def _solve_P_Q(U, V, structure=None):
    """
    A helper function for expm_2009.

    Parameters
    ----------
    U : ndarray
        Pade numerator.
    V : ndarray
        Pade denominator.
    structure : str, optional
        A string describing the structure of both matrices `U` and `V`.
        Only `upper_triangular` is currently supported.

    Notes
    -----
    The `structure` argument is inspired by similar args
    for theano and cvxopt functions.

    """
    P = U + V
    Q = -U + V
    if isspmatrix(U):
        return spsolve(Q, P)
    elif structure is None:
        return solve(Q, P)
    elif structure == UPPER_TRIANGULAR:
        return solve_triangular(Q, P)
    else:
        raise ValueError('unsupported matrix structure: ' + str(structure))
Example #2
0
def _count_nonzero(A):
    # A compatibility function which should eventually disappear.
    #XXX There should be a better way to do this when A is sparse
    #    in the traditional sense.
    if isspmatrix(A):
        return np.sum(A.toarray() != 0)
    else:
        return np.sum(A != 0)
Example #3
0
def _is_upper_triangular(A):
    # This function could possibly be of wider interest.
    if isspmatrix(A):
        lower_part = scipy.sparse.tril(A, -1)
        # Check structural upper triangularity,
        # then coincidental upper triangularity if needed.
        return lower_part.nnz == 0 or lower_part.count_nonzero() == 0
    else:
        return not np.tril(A, -1).any()
Example #4
0
def _solve_P_Q(U, V):
    """
    A helper function for expm_2009.
    """
    P = U + V
    Q = -U + V
    if isspmatrix(U):
        R = spsolve(Q, P)
    else:
        R = solve(Q, P)
    return R
Example #5
0
def _smart_matrix_product(A, B, alpha=None, structure=None):
    """
    A matrix product that knows about sparse and structured matrices.

    Parameters
    ----------
    A : 2d ndarray
        First matrix.
    B : 2d ndarray
        Second matrix.
    alpha : float
        The matrix product will be scaled by this constant.
    structure : str, optional
        A string describing the structure of both matrices `A` and `B`.
        Only `upper_triangular` is currently supported.

    Returns
    -------
    M : 2d ndarray
        Matrix product of A and B.

    """
    if len(A.shape) != 2:
        raise ValueError('expected A to be a rectangular matrix')
    if len(B.shape) != 2:
        raise ValueError('expected B to be a rectangular matrix')
    f = None
    if structure == UPPER_TRIANGULAR:
        if (not isspmatrix(A) and not isspmatrix(B)
                and not is_pydata_spmatrix(A) and not is_pydata_spmatrix(B)):
            f, = scipy.linalg.get_blas_funcs(('trmm', ), (A, B))
    if f is not None:
        if alpha is None:
            alpha = 1.
        out = f(alpha, A, B)
    else:
        if alpha is None:
            out = A.dot(B)
        else:
            out = alpha * A.dot(B)
    return out
Example #6
0
def _is_upper_triangular(A):
    # This function could possibly be of wider interest.
    if isspmatrix(A):
        lower_part = scipy.sparse.tril(A, -1)
        if lower_part.nnz == 0:
            # structural upper triangularity
            return True
        else:
            # coincidental upper triangularity
            return _count_nonzero(lower_part) == 0
    else:
        return _count_nonzero(np.tril(A, -1)) == 0
Example #7
0
def _is_upper_triangular(A):
    # This function could possibly be of wider interest.
    if isspmatrix(A):
        lower_part = scipy.sparse.tril(A, -1)
        if lower_part.nnz == 0:
            # structural upper triangularity
            return True
        else:
            # coincidental upper triangularity
            return _count_nonzero(lower_part) == 0
    else:
        return _count_nonzero(np.tril(A, -1)) == 0
Example #8
0
def _smart_matrix_product(A, B, alpha=None, structure=None):
    """
    A matrix product that knows about sparse and structured matrices.

    Parameters
    ----------
    A : 2d ndarray
        First matrix.
    B : 2d ndarray
        Second matrix.
    alpha : float
        The matrix product will be scaled by this constant.
    structure : str, optional
        A string describing the structure of both matrices `A` and `B`.
        Only `upper_triangular` is currently supported.

    Returns
    -------
    M : 2d ndarray
        Matrix product of A and B.

    """
    if len(A.shape) != 2:
        raise ValueError('expected A to be a rectangular matrix')
    if len(B.shape) != 2:
        raise ValueError('expected B to be a rectangular matrix')
    f = None
    if structure == UPPER_TRIANGULAR:
        if not isspmatrix(A) and not isspmatrix(B):
            f, = scipy.linalg.get_blas_funcs(('trmm',), (A, B))
    if f is not None:
        if alpha is None:
            alpha = 1.
        out = f(alpha, A, B)
    else:
        if alpha is None:
            out = A.dot(B)
        else:
            out = alpha * A.dot(B)
    return out
Example #9
0
def _is_upper_triangular(A):
    # This function could possibly be of wider interest.
    if isspmatrix(A):
        lower_part = scipy.sparse.tril(A, -1)
        # Check structural upper triangularity,
        # then coincidental upper triangularity if needed.
        return lower_part.nnz == 0 or lower_part.count_nonzero() == 0
    elif is_pydata_spmatrix(A):
        import sparse
        lower_part = sparse.tril(A, -1)
        return lower_part.nnz == 0
    else:
        return not np.tril(A, -1).any()
Example #10
0
    def __sub__(self,other):
        # First check if argument is a scalar
        if isscalarlike(other):
            if other == 0:
                return self.copy()
            else:  # Now we would add this scalar to every element.
                raise NotImplementedError('adding a nonzero scalar to a '
                                          'sparse matrix is not supported')
        elif isspmatrix(other):
            if (other.shape != self.shape):
                raise ValueError("inconsistent shapes")

            return self._binopt(other,'_minus_')
        elif isdense(other):
            # Convert this matrix to a dense matrix and subtract them
            return self.todense() - other
        else:
            return NotImplemented
Example #11
0
    def __setitem__(self, index, x):
        # Process arrays from IndexMixin
        i, j = self._unpack_index(index)
        i, j = self._index_to_arrays(i, j)

        if isspmatrix(x):
            x = x.toarray()

        # Make x and i into the same shape
        x = np.asarray(x, dtype=self.dtype)
        x, _ = np.broadcast_arrays(x, i)

        if x.shape != i.shape:
            raise ValueError("shape mismatch in assignment")

        if np.size(x) == 0:
            return
        i, j = self._swap((i.ravel(), j.ravel()))
        self._set_many(i, j, x.ravel())
Example #12
0
    def __eq__(self, other):
        # Scalar other.
        if isscalarlike(other):
            if np.isnan(other):
                return self.__class__(self.shape, dtype=np.bool_)

            if other == 0:
                warn(
                    "Comparing a sparse matrix with 0 using == is inefficient"
                    ", try using != instead.",
                    SparseEfficiencyWarning,
                    stacklevel=3)
                all_true = self.__class__(np.ones(self.shape, dtype=np.bool_))
                inv = self._scalar_binopt(other, operator.ne)
                return all_true - inv
            else:
                return self._scalar_binopt(other, operator.eq)
        # Dense other.
        elif isdense(other):
            return self.todense() == other
        # Pydata sparse other.
        elif is_pydata_spmatrix(other):
            return NotImplemented
        # Sparse other.
        elif isspmatrix(other):
            warn(
                "Comparing sparse matrices using == is inefficient, try using"
                " != instead.",
                SparseEfficiencyWarning,
                stacklevel=3)
            # TODO sparse broadcasting
            if self.shape != other.shape:
                return False
            elif self.format != other.format:
                other = other.asformat(self.format)
            res = self._binopt(other, '_ne_')
            all_true = self.__class__(np.ones(self.shape, dtype=np.bool_))
            return all_true - res
        else:
            return False
Example #13
0
    def __init__(self,
                 matrix,
                 impl,
                 exponent=2.0,
                 dist_using_inner=False,
                 **kwargs):
        """Initialize a new instance.

        Parameters
        ----------
        matrix :  ``scipy.sparse.spmatrix`` or `array-like`, 2-dim.
            Square weighting matrix of the inner product
        impl : `str`
            Specifier for the implementation backend
        exponent : positive `float`, optional
            Exponent of the norm. For values other than 2.0, the inner
            product is not defined.
            If ``matrix`` is a sparse matrix, only 1.0, 2.0 and ``inf``
            are allowed.
        dist_using_inner : `bool`, optional
            Calculate `dist` using the following formula::

                ||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re <x, y>

            This avoids the creation of new arrays and is thus faster
            for large arrays. On the downside, it will not evaluate to
            exactly zero for equal (but not identical) ``x`` and ``y``.

            This option can only be used if ``exponent`` is 2.0.
        precomp_mat_pow : `bool`, optional
            If `True`, precompute the matrix power ``W ** (1/p)``
            during initialization. This has no effect if ``exponent``
            is 1.0, 2.0 or ``inf``.

            Default: `False`

        cache_mat_pow : `bool`, optional
            If `True`, cache the matrix power ``W ** (1/p)``. This can
            happen either during initialization or in the first call to
            ``norm`` or ``dist``, resp. This has no effect if
            ``exponent`` is 1.0, 2.0 or ``inf``.

            Default: `True`

        cache_mat_decomp : `bool`, optional
            If `True`, cache the eigenbasis decomposition of the
            matrix. This can happen either during initialization or in
            the first call to ``norm`` or ``dist``, resp. This has no
            effect if ``exponent`` is 1.0, 2.0 or ``inf``.

            Default: `False`

        Notes
        -----
        The matrix power ``W ** (1/p)`` is computed by eigenbasis
        decomposition::

            eigval, eigvec = scipy.linalg.eigh(matrix)
            mat_pow = (eigval ** p * eigvec).dot(eigvec.conj().T)

        Depending on the matrix size, this can be rather expensive.
        """
        precomp_mat_pow = kwargs.pop('precomp_mat_pow', False)
        self._cache_mat_pow = bool(kwargs.pop('cache_mat_pow', True))
        self._cache_mat_decomp = bool(kwargs.pop('cache_mat_decomp', False))
        super().__init__(impl=impl,
                         exponent=exponent,
                         dist_using_inner=dist_using_inner)

        # Check and set matrix
        if isspmatrix(matrix):
            self._matrix = matrix
        else:
            self._matrix = np.asarray(matrix)
            if self._matrix.dtype == object:
                raise ValueError('invalid matrix {}'.format(matrix))
            elif self._matrix.ndim != 2:
                raise ValueError('matrix {} is {}-dimensional instead of '
                                 '2-dimensional'
                                 ''.format(matrix, self._matrix.ndim))

        if self._matrix.shape[0] != self._matrix.shape[1]:
            raise ValueError('matrix has shape {}, expected a square matrix'
                             ''.format(self._matrix.shape))

        if (self.matrix_issparse
                and self.exponent not in (1.0, 2.0, float('inf'))):
            raise NotImplementedError('sparse matrices only supported for '
                                      'exponent 1.0, 2.0 or `inf`')

        # Compute the power and decomposition if desired
        self._eigval = self._eigvec = None
        if self.exponent in (1.0, float('inf')):
            self._mat_pow = self.matrix
        elif precomp_mat_pow and self.exponent != 2.0:
            eigval, eigvec = self.matrix_decomp()
            if self._cache_mat_decomp:
                self._eigval, self._eigvec = eigval, eigvec
                eigval_pow = eigval**(1.0 / self.exponent)
            else:
                eigval_pow = eigval
                eigval_pow **= 1.0 / self.exponent
            self._mat_pow = (eigval_pow * eigvec).dot(eigvec.conj().T)
        else:
            self._mat_pow = None
Example #14
0
 def matrix_issparse(self):
     """Whether the representing matrix is sparse or not."""
     return isspmatrix(self.matrix)
Example #15
0
def cs_graph_components(x):
    """
    Determine connected components of a graph stored as a compressed
    sparse row or column matrix.

    For speed reasons, the symmetry of the matrix x is not checked. A
    nonzero at index `(i, j)` means that node `i` is connected to node
    `j` by an edge. The number of rows/columns of the matrix thus
    corresponds to the number of nodes in the graph.

    Parameters
    -----------
    x : array_like or sparse matrix, 2 dimensions
        The adjacency matrix of the graph. Only the upper triangular part
        is used.

    Returns
    --------
    n_comp : int
        The number of connected components.
    label : ndarray (ints, 1 dimension):
        The label array of each connected component (-2 is used to
        indicate empty rows in the matrix: 0 everywhere, including
        diagonal). This array has the length of the number of nodes,
        i.e. one label for each node of the graph. Nodes having the same
        label belong to the same connected component.

    Notes
    ------
    The matrix is assumed to be symmetric and the upper triangular part
    of the matrix is used. The matrix is converted to a CSR matrix unless
    it is already a CSR.

    Examples
    --------
    >>> from scipy.sparse.csgraph import connected_components
    >>> D = np.eye(4)
    >>> D[0,1] = D[1,0] = 1
    >>> cs_graph_components(D)
    (3, array([0, 0, 1, 2]))
    >>> from scipy.sparse import dok_matrix
    >>> cs_graph_components(dok_matrix(D))
    (3, array([0, 0, 1, 2]))

    """
    try:
        shape = x.shape
    except AttributeError:
        raise ValueError(_msg0)

    if not ((len(x.shape) == 2) and (x.shape[0] == x.shape[1])):
        raise ValueError(_msg1 % x.shape)

    if isspmatrix(x):
        x = x.tocsr()
    else:
        x = csr_matrix(x)

    label = np.empty((shape[0],), dtype=x.indptr.dtype)

    n_comp = _cs_graph_components(shape[0], x.indptr, x.indices, label)

    return n_comp, label
Example #16
0
    def __init__(self, arg1, shape=None, dtype=None, copy=False):
        _data_matrix.__init__(self)

        if isspmatrix(arg1):
            if arg1.format == self.format and copy:
                arg1 = arg1.copy()
            else:
                arg1 = arg1.asformat(self.format)
            self._set_self(arg1)

        elif isinstance(arg1, tuple):
            if isshape(arg1):
                # It's a tuple of matrix dimensions (M, N)
                # create empty matrix
                self._shape = check_shape(arg1)
                M, N = self.shape
                # Select index dtype large enough to pass array and
                # scalar parameters to sparsetools
                idx_dtype = get_index_dtype(maxval=max(M, N))
                self.data = np.zeros(0, getdtype(dtype, default=float))
                self.indices = np.zeros(0, idx_dtype)
                self.indptr = np.zeros(self._swap((M, N))[0] + 1,
                                       dtype=idx_dtype)
            else:
                if len(arg1) == 2:
                    # (data, ij) format
                    from scipy.sparse.coo import coo_matrix
                    other = self.__class__(coo_matrix(arg1, shape=shape))
                    self._set_self(other)
                elif len(arg1) == 3:
                    # (data, indices, indptr) format
                    (data, indices, indptr) = arg1

                    # Select index dtype large enough to pass array and
                    # scalar parameters to sparsetools
                    maxval = None
                    if shape is not None:
                        maxval = max(shape)
                    idx_dtype = get_index_dtype((indices, indptr),
                                                maxval=maxval,
                                                check_contents=True)

                    self.indices = np.array(indices,
                                            copy=copy,
                                            dtype=idx_dtype)
                    self.indptr = np.array(indptr, copy=copy, dtype=idx_dtype)
                    self.data = np.array(data, copy=copy, dtype=dtype)
                else:
                    raise ValueError("unrecognized {}_matrix "
                                     "constructor usage".format(self.format))

        else:
            # must be dense
            try:
                arg1 = np.asarray(arg1)
            except Exception:
                raise ValueError("unrecognized {}_matrix constructor usage"
                                 "".format(self.format))
            from scipy.sparse.coo import coo_matrix
            self._set_self(self.__class__(coo_matrix(arg1, dtype=dtype)))

        # Read matrix dimensions given, if any
        if shape is not None:
            self._shape = check_shape(shape)
        else:
            if self.shape is None:
                # shape not already set, try to infer dimensions
                try:
                    major_dim = len(self.indptr) - 1
                    minor_dim = self.indices.max() + 1
                except Exception:
                    raise ValueError('unable to infer matrix dimensions')
                else:
                    self._shape = check_shape(
                        self._swap((major_dim, minor_dim)))

        if dtype is not None:
            self.data = self.data.astype(dtype, copy=False)

        self.check_format(full_check=False)
Example #17
0
def _expm(A, use_exact_onenorm):
    # Core of expm, separated to allow testing exact and approximate
    # algorithms.

    # Avoid indiscriminate asarray() to allow sparse or other strange arrays.
    if isinstance(A, (list, tuple)):
        A = np.asarray(A)
    if len(A.shape) != 2 or A.shape[0] != A.shape[1]:
        raise ValueError('expected a square matrix')

    # Trivial case
    if A.shape == (1, 1):
        out = [[np.exp(A[0, 0])]]

        # Avoid indiscriminate casting to ndarray to
        # allow for sparse or other strange arrays
        if isspmatrix(A):
            return A.__class__(out)

        return np.array(out)

    # Detect upper triangularity.
    structure = UPPER_TRIANGULAR if _is_upper_triangular(A) else None

    if use_exact_onenorm == "auto":
        # Hardcode a matrix order threshold for exact vs. estimated one-norms.
        use_exact_onenorm = A.shape[0] < 200

    # Track functions of A to help compute the matrix exponential.
    h = _ExpmPadeHelper(A,
                        structure=structure,
                        use_exact_onenorm=use_exact_onenorm)

    # Try Pade order 3.
    eta_1 = max(h.d4_loose, h.d6_loose)
    if eta_1 < 1.495585217958292e-002 and _ell(h.A, 3) == 0:
        U, V = h.pade3()
        return _solve_P_Q(U, V, structure=structure)

    # Try Pade order 5.
    eta_2 = max(h.d4_tight, h.d6_loose)
    if eta_2 < 2.539398330063230e-001 and _ell(h.A, 5) == 0:
        U, V = h.pade5()
        return _solve_P_Q(U, V, structure=structure)

    # Try Pade orders 7 and 9.
    eta_3 = max(h.d6_tight, h.d8_loose)
    if eta_3 < 9.504178996162932e-001 and _ell(h.A, 7) == 0:
        U, V = h.pade7()
        return _solve_P_Q(U, V, structure=structure)
    if eta_3 < 2.097847961257068e+000 and _ell(h.A, 9) == 0:
        U, V = h.pade9()
        return _solve_P_Q(U, V, structure=structure)

    # Use Pade order 13.
    eta_4 = max(h.d8_loose, h.d10_loose)
    eta_5 = min(eta_3, eta_4)
    theta_13 = 4.25

    # Choose smallest s>=0 such that 2**(-s) eta_5 <= theta_13
    if eta_5 == 0:
        # Nilpotent special case
        s = 0
    else:
        s = max(int(np.ceil(np.log2(eta_5 / theta_13))), 0)
    s = s + _ell(2**-s * h.A, 13)
    U, V = h.pade13_scaled(s)
    X = _solve_P_Q(U, V, structure=structure)
    if structure == UPPER_TRIANGULAR:
        # Invoke Code Fragment 2.1.
        X = _fragment_2_1(X, h.A, s)
    else:
        # X = r_13(A)^(2^s) by repeated squaring.
        for i in range(s):
            X = X.dot(X)
    return X
Example #18
0
    def multiply(self, other):
        """Point-wise multiplication by another matrix, vector, or
        scalar.
        """
        # print("multiply")
        # Scalar multiplication.
        if isscalarlike(other):
            return self._mul_scalar(other)
        # Sparse matrix or vector.
        if isspmatrix(other):
            if self.shape == other.shape:
                other = self.__class__(other)
                return self._binopt(other, '_elmul_')
            # Single element.
            elif other.shape == (1, 1):
                return self._mul_scalar(other.toarray()[0, 0])
            elif self.shape == (1, 1):
                return other._mul_scalar(self.toarray()[0, 0])
            # A row times a column.
            elif self.shape[1] == 1 and other.shape[0] == 1:
                return self._mul_sparse_matrix(other.tocsc())
            elif self.shape[0] == 1 and other.shape[1] == 1:
                return other._mul_sparse_matrix(self.tocsc())
            # Row vector times matrix. other is a row.
            elif other.shape[0] == 1 and self.shape[1] == other.shape[1]:
                other = dia_matrix((other.toarray().ravel(), [0]),
                                   shape=(other.shape[1], other.shape[1]))
                return self._mul_sparse_matrix(other)
            # self is a row.
            elif self.shape[0] == 1 and self.shape[1] == other.shape[1]:
                copy = dia_matrix((self.toarray().ravel(), [0]),
                                  shape=(self.shape[1], self.shape[1]))
                return other._mul_sparse_matrix(copy)
            # Column vector times matrix. other is a column.
            elif other.shape[1] == 1 and self.shape[0] == other.shape[0]:
                other = dia_matrix((other.toarray().ravel(), [0]),
                                   shape=(other.shape[0], other.shape[0]))
                return other._mul_sparse_matrix(self)
            # self is a column.
            elif self.shape[1] == 1 and self.shape[0] == other.shape[0]:
                copy = dia_matrix((self.toarray().ravel(), [0]),
                                  shape=(self.shape[0], self.shape[0]))
                return copy._mul_sparse_matrix(other)
            else:
                raise ValueError("inconsistent shapes")

        # Assume other is a dense matrix/array, which produces a single-item
        # object array if other isn't convertible to ndarray.
        other = np.atleast_2d(other)

        if other.ndim != 2:
            return np.multiply(self.toarray(), other)
        # Single element / wrapped object.
        if other.size == 1:
            return self._mul_scalar(other.flat[0])
        # Fast case for trivial sparse matrix.
        elif self.shape == (1, 1):
            return np.multiply(self.toarray()[0, 0], other)

        from scipy.sparse.coo import coo_matrix
        ret = self.tocoo()
        # Matching shapes.
        if self.shape == other.shape:
            data = np.multiply(ret.data, other[ret.row, ret.col])
        # Sparse row vector times...
        elif self.shape[0] == 1:
            if other.shape[1] == 1:  # Dense column vector.
                data = np.multiply(ret.data, other)
            elif other.shape[1] == self.shape[1]:  # Dense matrix.
                data = np.multiply(ret.data, other[:, ret.col])
            else:
                raise ValueError("inconsistent shapes")
            row = np.repeat(np.arange(other.shape[0]), len(ret.row))
            col = np.tile(ret.col, other.shape[0])
            return coo_matrix((data.view(np.ndarray).ravel(), (row, col)),
                              shape=(other.shape[0], self.shape[1]),
                              copy=False)
        # Sparse column vector times...
        elif self.shape[1] == 1:
            if other.shape[0] == 1:  # Dense row vector.
                data = np.multiply(ret.data[:, None], other)
            elif other.shape[0] == self.shape[0]:  # Dense matrix.
                data = np.multiply(ret.data[:, None], other[ret.row])
            else:
                raise ValueError("inconsistent shapes")
            row = np.repeat(ret.row, other.shape[1])
            col = np.tile(np.arange(other.shape[1]), len(ret.col))
            return coo_matrix((data.view(np.ndarray).ravel(), (row, col)),
                              shape=(self.shape[0], other.shape[1]),
                              copy=False)
        # Sparse matrix times dense row vector.
        elif other.shape[0] == 1 and self.shape[1] == other.shape[1]:
            data = np.multiply(ret.data, other[:, ret.col].ravel())
        # Sparse matrix times dense column vector.
        elif other.shape[1] == 1 and self.shape[0] == other.shape[0]:
            data = np.multiply(ret.data, other[ret.row].ravel())
        else:
            raise ValueError("inconsistent shapes")
        ret.data = data.view(np.ndarray).ravel()
        return ret
Example #19
0
def cs_graph_components(x):
    """
    Determine connected components of a graph stored as a compressed
    sparse row or column matrix.

    For speed reasons, the symmetry of the matrix x is not checked. A
    nonzero at index `(i, j)` means that node `i` is connected to node
    `j` by an edge. The number of rows/columns of the matrix thus
    corresponds to the number of nodes in the graph.

    Parameters
    -----------
    x : array_like or sparse matrix, 2 dimensions
        The adjacency matrix of the graph. Only the upper triangular part
        is used.

    Returns
    --------
    n_comp : int
        The number of connected components.
    label : ndarray (ints, 1 dimension):
        The label array of each connected component (-2 is used to
        indicate empty rows in the matrix: 0 everywhere, including
        diagonal). This array has the length of the number of nodes,
        i.e. one label for each node of the graph. Nodes having the same
        label belong to the same connected component.

    Notes
    ------
    The matrix is assumed to be symmetric and the upper triangular part
    of the matrix is used. The matrix is converted to a CSR matrix unless
    it is already a CSR.

    Examples
    --------
    >>> from scipy.sparse.csgraph import connected_components
    >>> D = np.eye(4)
    >>> D[0,1] = D[1,0] = 1
    >>> cs_graph_components(D)
    (3, array([0, 0, 1, 2]))
    >>> from scipy.sparse import dok_matrix
    >>> cs_graph_components(dok_matrix(D))
    (3, array([0, 0, 1, 2]))

    """
    try:
        shape = x.shape
    except AttributeError:
        raise ValueError(_msg0)

    if not ((len(x.shape) == 2) and (x.shape[0] == x.shape[1])):
        raise ValueError(_msg1 % x.shape)

    if isspmatrix(x):
        x = x.tocsr()
    else:
        x = csr_matrix(x)

    label = np.empty((shape[0], ), dtype=x.indptr.dtype)

    n_comp = _cs_graph_components(shape[0], x.indptr, x.indices, label)

    return n_comp, label
Example #20
0
def expm(A, t=None ):
    """Compute the matrix exponential - exp(t*A) - using Padé approximation.

    Parameters
    ----------
    A : array or sparse matrix, shape(M,M)
        2D Array or Matrix (sparse or dense) to be exponentiated
    t : array, shape(1)
        Exponent multiplier. Default=1.

    Returns
    -------
    exp(A*t) : array, shape(M,M)
        Matrix exponential of A

    References
    ----------
    Roger B. Sidje,
    "EXPOKIT: A Software Package for Computing Matrix Exponentials",
    ACM Trans. Math. Softw. 24(1), 130-156 (1998).

    http://www.maths.uq.edu.au/expokit/

    """
    # dgpadm does an internal check for square matrices,
    #  so assume ldh == m
    # constant input variables
    if hasattr( A, 'shape' ):
        m   = A.shape[0]
        #ldh = A.shape[1] 
        dtype = A.dtype
    else:
        m   = len(A)
        #ldh = len(A[0])
        dtype = type( A[0][0] )
    #if t is None:
    #    t = np.array([1.])[0]

    # output integers needed to get result:
    iexph = np.array([0])
    ns    = np.array([0])
    iflag = np.array([0])
    if isspmatrix(A):
        # Sparse matrix routines.
        matvec = lambda v : A.dot(v)
        # Please check usage of LinearOperator:-
        Av = LinearOperator( (m, m), matvec=matvec)
        itrace = np.array([0])
        tol = np.array([1e-7])
        v = np.ones(m, dtype=dtype)
        anorm = np.linalg.norm(A.todense(), np.inf) 
        if dtype in ('float64', 'float32', float,):
            # See src/expokit.f for documentation
            w, wsp = dgexpv(v, tol, anorm, Av.matvec, itrace, iflag, t=t)
        elif dtype in ('complex128', 'complex64', complex):
            w, wsp = zgexpv(v * 1 + 1j, tol, anorm, Av.matvec, itrace, iflag, t=t) 
        #print('\nA {0}: {1}'.format(A.shape, type(A)))
        #print('w {0}: {1}'.format(w.shape, type(w)))
        return np.reshape(w, (m,m), order='F')
        #return wsp[m*(m+1)+m+(m+2)**2:]
        #return np.reshape(wsp[m*(m+1)+m+(m+2)**2:], (m,m), order='F')
    else:
        iexph = np.array([0])
        ns = np.array([0])
        if dtype in ('float64', 'float32', float,):
            wsp = dgpadm(A, iexph, ns, iflag, t)
        elif dtype in ('complex128', 'complex64', complex):
            wsp = zgpadm(A, iexph, ns, iflag, t=t)
    if iflag[0] != 0:
        raise IOError("Expokit error ({0}) in routine {1}".format(iflag[0]))
    return np.reshape(wsp[iexph[0]-1 : iexph[0] + m * m - 1], (m,m), order='F')
Example #21
0
def _expm(A, use_exact_onenorm):
    # Core of expm, separated to allow testing exact and approximate
    # algorithms.

    # Avoid indiscriminate asarray() to allow sparse or other strange arrays.
    if isinstance(A, (list, tuple)):
        A = np.asarray(A)
    if len(A.shape) != 2 or A.shape[0] != A.shape[1]:
        raise ValueError('expected a square matrix')

    # Trivial case
    if A.shape == (1, 1):
        out = [[np.exp(A[0, 0])]]

        # Avoid indiscriminate casting to ndarray to
        # allow for sparse or other strange arrays
        if isspmatrix(A):
            return A.__class__(out)

        return np.array(out)

    # Detect upper triangularity.
    structure = UPPER_TRIANGULAR if _is_upper_triangular(A) else None

    if use_exact_onenorm == "auto":
        # Hardcode a matrix order threshold for exact vs. estimated one-norms.
        use_exact_onenorm = A.shape[0] < 200

    # Track functions of A to help compute the matrix exponential.
    h = _ExpmPadeHelper(
            A, structure=structure, use_exact_onenorm=use_exact_onenorm)

    # Try Pade order 3.
    eta_1 = max(h.d4_loose, h.d6_loose)
    if eta_1 < 1.495585217958292e-002 and _ell(h.A, 3) == 0:
        U, V = h.pade3()
        return _solve_P_Q(U, V, structure=structure)

    # Try Pade order 5.
    eta_2 = max(h.d4_tight, h.d6_loose)
    if eta_2 < 2.539398330063230e-001 and _ell(h.A, 5) == 0:
        U, V = h.pade5()
        return _solve_P_Q(U, V, structure=structure)

    # Try Pade orders 7 and 9.
    eta_3 = max(h.d6_tight, h.d8_loose)
    if eta_3 < 9.504178996162932e-001 and _ell(h.A, 7) == 0:
        U, V = h.pade7()
        return _solve_P_Q(U, V, structure=structure)
    if eta_3 < 2.097847961257068e+000 and _ell(h.A, 9) == 0:
        U, V = h.pade9()
        return _solve_P_Q(U, V, structure=structure)

    # Use Pade order 13.
    eta_4 = max(h.d8_loose, h.d10_loose)
    eta_5 = min(eta_3, eta_4)
    theta_13 = 4.25

    # Choose smallest s>=0 such that 2**(-s) eta_5 <= theta_13
    if eta_5 == 0:
        # Nilpotent special case
        s = 0
    else:
        s = max(int(np.ceil(np.log2(eta_5 / theta_13))), 0)
    s = s + _ell(2**-s * h.A, 13)
    U, V = h.pade13_scaled(s)
    X = _solve_P_Q(U, V, structure=structure)
    if structure == UPPER_TRIANGULAR:
        # Invoke Code Fragment 2.1.
        X = _fragment_2_1(X, h.A, s)
    else:
        # X = r_13(A)^(2^s) by repeated squaring.
        for i in range(s):
            X = X.dot(X)
    return X
Example #22
0
def expm(A):
    """
    Compute the matrix exponential using Pade approximation.

    .. versionadded:: 0.12.0

    Parameters
    ----------
    A : (M,M) array or sparse matrix
        2D Array or Matrix (sparse or dense) to be exponentiated

    Returns
    -------
    expA : (M,M) ndarray
        Matrix exponential of `A`

    References
    ----------
    N. J. Higham,
    "The Scaling and Squaring Method for the Matrix Exponential Revisited",
    SIAM. J. Matrix Anal. & Appl. 26, 1179 (2005).

    """
    n_squarings = 0
    Aissparse = isspmatrix(A)

    if Aissparse:
        A_L1 = max(abs(A).sum(axis=0).flat)
        ident = speye(A.shape[0], A.shape[1], dtype=A.dtype, format=A.format)
    else:
        A = asarray(A)
        A_L1 = norm(A, 1)
        ident = eye(A.shape[0], A.shape[1], dtype=A.dtype)

    if A.dtype == 'float64' or A.dtype == 'complex128':
        if A_L1 < 1.495585217958292e-002:
            U, V = _pade3(A, ident)
        elif A_L1 < 2.539398330063230e-001:
            U, V = _pade5(A, ident)
        elif A_L1 < 9.504178996162932e-001:
            U, V = _pade7(A, ident)
        elif A_L1 < 2.097847961257068e+000:
            U, V = _pade9(A, ident)
        else:
            maxnorm = 5.371920351148152
            n_squarings = max(0, int(ceil(log2(A_L1 / maxnorm))))
            A = A / 2**n_squarings
            U, V = _pade13(A, ident)
    elif A.dtype == 'float32' or A.dtype == 'complex64':
        if A_L1 < 4.258730016922831e-001:
            U, V = _pade3(A, ident)
        elif A_L1 < 1.880152677804762e+000:
            U, V = _pade5(A, ident)
        else:
            maxnorm = 3.925724783138660
            n_squarings = max(0, int(ceil(log2(A_L1 / maxnorm))))
            A = A / 2**n_squarings
            U, V = _pade7(A, ident)
    else:
        raise ValueError("invalid type: " + str(A.dtype))

    P = U + V  # p_m(A) : numerator
    Q = -U + V  # q_m(A) : denominator

    if Aissparse:
        from scipy.sparse.linalg import spsolve
        R = spsolve(Q, P)
    else:
        R = solve(Q, P)

    # squaring step to undo scaling
    for i in range(n_squarings):
        R = R.dot(R)

    return R
Example #23
0
    def __init__(self, matrix, impl, exponent=2.0, dist_using_inner=False,
                 **kwargs):
        """Initialize a new instance.

        Parameters
        ----------
        matrix :  ``scipy.sparse.spmatrix`` or `array-like`, 2-dim.
            Square weighting matrix of the inner product
        impl : `str`
            Specifier for the implementation backend
        exponent : positive `float`, optional
            Exponent of the norm. For values other than 2.0, the inner
            product is not defined.
            If ``matrix`` is a sparse matrix, only 1.0, 2.0 and ``inf``
            are allowed.
        dist_using_inner : `bool`, optional
            Calculate `dist` using the following formula::

                ||x - y||^2 = ||x||^2 + ||y||^2 - 2 * Re <x, y>

            This avoids the creation of new arrays and is thus faster
            for large arrays. On the downside, it will not evaluate to
            exactly zero for equal (but not identical) ``x`` and ``y``.

            This option can only be used if ``exponent`` is 2.0.
        precomp_mat_pow : `bool`, optional
            If `True`, precompute the matrix power ``W ** (1/p)``
            during initialization. This has no effect if ``exponent``
            is 1.0, 2.0 or ``inf``.

            Default: `False`

        cache_mat_pow : `bool`, optional
            If `True`, cache the matrix power ``W ** (1/p)``. This can
            happen either during initialization or in the first call to
            ``norm`` or ``dist``, resp. This has no effect if
            ``exponent`` is 1.0, 2.0 or ``inf``.

            Default: `True`

        cache_mat_decomp : `bool`, optional
            If `True`, cache the eigenbasis decomposition of the
            matrix. This can happen either during initialization or in
            the first call to ``norm`` or ``dist``, resp. This has no
            effect if ``exponent`` is 1.0, 2.0 or ``inf``.

            Default: `False`

        Notes
        -----
        The matrix power ``W ** (1/p)`` is computed by eigenbasis
        decomposition::

            eigval, eigvec = scipy.linalg.eigh(matrix)
            mat_pow = (eigval ** p * eigvec).dot(eigvec.conj().T)

        Depending on the matrix size, this can be rather expensive.
        """
        precomp_mat_pow = kwargs.pop('precomp_mat_pow', False)
        self._cache_mat_pow = bool(kwargs.pop('cache_mat_pow', True))
        self._cache_mat_decomp = bool(kwargs.pop('cache_mat_decomp', False))
        super().__init__(impl=impl, exponent=exponent,
                         dist_using_inner=dist_using_inner)

        # Check and set matrix
        if isspmatrix(matrix):
            self._matrix = matrix
        else:
            self._matrix = np.asarray(matrix)
            if self._matrix.dtype == object:
                raise ValueError('invalid matrix {}.'.format(matrix))
            elif self._matrix.ndim != 2:
                raise ValueError('matrix {} is {}-dimensional instead of '
                                 '2-dimensional.'
                                 ''.format(matrix, self._matrix.ndim))

        if self._matrix.shape[0] != self._matrix.shape[1]:
            raise ValueError('matrix has shape {}, expected a square matrix.'
                             ''.format(self._matrix.shape))

        if (self.matrix_issparse and
                self.exponent not in (1.0, 2.0, float('inf'))):
            raise NotImplementedError('sparse matrices only supported for '
                                      'exponent 1.0, 2.0 or `inf`.')

        # Compute the power and decomposition if desired
        self._eigval = self._eigvec = None
        if self.exponent in (1.0, float('inf')):
            self._mat_pow = self.matrix
        elif precomp_mat_pow and self.exponent != 2.0:
            eigval, eigvec = self.matrix_decomp()
            if self._cache_mat_decomp:
                self._eigval, self._eigvec = eigval, eigvec
                eigval_pow = eigval ** (1.0 / self.exponent)
            else:
                eigval_pow = eigval
                eigval_pow **= 1.0 / self.exponent
            self._mat_pow = (eigval_pow * eigvec).dot(eigvec.conj().T)
        else:
            self._mat_pow = None
Example #24
0
def expm(A):
    """Compute the matrix exponential using Pade approximation.

    .. versionadded:: 0.12.0

    Parameters
    ----------
    A : array or sparse matrix, shape(M,M)
        2D Array or Matrix (sparse or dense) to be exponentiated

    Returns
    -------
    expA : array, shape(M,M)
        Matrix exponential of A

    References
    ----------
    N. J. Higham,
    "The Scaling and Squaring Method for the Matrix Exponential Revisited",
    SIAM. J. Matrix Anal. & Appl. 26, 1179 (2005).

    """
    n_squarings = 0
    Aissparse = isspmatrix(A)

    if Aissparse:
        A_L1 = max(abs(A).sum(axis=0).flat)
        ident = speye(A.shape[0], A.shape[1], dtype=A.dtype, format=A.format)
    else:
        A = asarray(A)
        A_L1 = norm(A,1)
        ident = eye(A.shape[0], A.shape[1], dtype=A.dtype)

    if A.dtype == 'float64' or A.dtype == 'complex128':
        if A_L1 < 1.495585217958292e-002:
            U,V = _pade3(A, ident)
        elif A_L1 < 2.539398330063230e-001:
            U,V = _pade5(A, ident)
        elif A_L1 < 9.504178996162932e-001:
            U,V = _pade7(A, ident)
        elif A_L1 < 2.097847961257068e+000:
            U,V = _pade9(A, ident)
        else:
            maxnorm = 5.371920351148152
            n_squarings = max(0, int(ceil(log2(A_L1 / maxnorm))))
            A = A / 2**n_squarings
            U,V = _pade13(A, ident)
    elif A.dtype == 'float32' or A.dtype == 'complex64':
        if A_L1 < 4.258730016922831e-001:
            U,V = _pade3(A, ident)
        elif A_L1 < 1.880152677804762e+000:
            U,V = _pade5(A, ident)
        else:
            maxnorm = 3.925724783138660
            n_squarings = max(0, int(ceil(log2(A_L1 / maxnorm))))
            A = A / 2**n_squarings
            U,V = _pade7(A, ident)
    else:
        raise ValueError("invalid type: "+str(A.dtype))

    P = U + V  # p_m(A) : numerator
    Q = -U + V # q_m(A) : denominator

    if Aissparse:
        from scipy.sparse.linalg import spsolve
        R = spsolve(Q, P)
    else:
        R = solve(Q,P)

    # squaring step to undo scaling
    for i in range(n_squarings):
        R = R.dot(R)

    return R
Example #25
0
    def __init__(self, arg1, shape=None, dtype=None, copy=False):
        _data_matrix.__init__(self)

        if isspmatrix(arg1):
            if arg1.format == self.format and copy:
                arg1 = arg1.copy()
            else:
                arg1 = arg1.asformat(self.format)
            self._set_self(arg1)

        elif isinstance(arg1, tuple):
            if isshape(arg1):
                # It's a tuple of matrix dimensions (M, N)
                # create empty matrix
                self.shape = arg1   # spmatrix checks for errors here
                M, N = self.shape
                idx_dtype = get_index_dtype(maxval=self._swap((M,N))[1])
                self.data = da.zeros(0, getdtype(dtype, default=float))
                self.indices = da.zeros(0, idx_dtype)
                self.indptr = da.zeros(self._swap((M,N))[0] + 1, dtype=idx_dtype)
            else:
                if len(arg1) == 2:
                    # (data, ij) format
                    from .coo import coo_matrix
                    other = self.__class__(coo_matrix(arg1, shape=shape))
                    self._set_self(other)
                elif len(arg1) == 3:
                    # (data, indices, indptr) format
                    (data, indices, indptr) = arg1
                    idx_dtype = get_index_dtype((indices, indptr), check_contents=True)
                    chunks = (10,)
                    self.indices = da.from_array(indices, chunks=chunks)
                    self.indptr = da.from_array(indptr, chunks=chunks)
                    self.data = da.from_array(data, chunks=chunks)
                else:
                    raise ValueError("unrecognized %s_matrix constructor usage" %
                            self.format)

        else:
            # must be dense
            try:
                arg1 = np.asarray(arg1)
            except:
                raise ValueError("unrecognized %s_matrix constructor usage" %
                        self.format)
            from scipy.sparse.coo import coo_matrix
            self._set_self(self.__class__(coo_matrix(arg1, dtype=dtype)))

        # Read matrix dimensions given, if any
        if shape is not None:
            self.shape = shape   # spmatrix will check for errors
        else:
            if self.shape is None:
                # shape not already set, try to infer dimensions
                try:
                    major_dim = len(self.indptr) - 1
                    minor_dim = self.indices.max() + 1
                except:
                    raise ValueError('unable to infer matrix dimensions')
                else:
                    self.shape = self._swap((major_dim,minor_dim))

        if dtype is not None:
            self.data = np.asarray(self.data, dtype=dtype)

        self.check_format(full_check=False)
Example #26
0
    def __init__(self, arg1, shape=None, dtype=None, copy=False):
        _data_matrix.__init__(self)

        self.chunks = (10, 1)

        if isinstance(arg1, tuple):
            if isshape(arg1):
                M, N = arg1
                self.shape = (M, N)
                idx_dtype = get_index_dtype(maxval=max(M, N))
                self.row = np.array([], dtype=idx_dtype)
                self.col = np.array([], dtype=idx_dtype)
                self.data = np.array([], getdtype(dtype, default=float))
                self.has_canonical_format = True
            else:
                try:
                    obj, (row, col) = arg1
                except (TypeError, ValueError):
                    raise TypeError('invalid input format')

                if shape is None:
                    if len(row) == 0 or len(col) == 0:
                        raise ValueError('cannot infer dimensions from zero '
                                         'sized index arrays')
                    M = np.max(row) + 1
                    N = np.max(col) + 1
                    self.shape = (M, N)
                else:
                    # Use 2 steps to ensure shape has length 2.
                    M, N = shape
                    self.shape = (M, N)

                idx_dtype = get_index_dtype(maxval=max(self.shape))
                if isinstance(row, da.core.Array):
                    self.row = row
                else:
                    self.row = da.from_array(row, chunks=self.chunks)
                if isinstance(col, da.core.Array):
                    self.col = col
                else:
                    self.col = da.from_array(col, chunks=self.chunks)
                if isinstance(obj, da.core.Array):
                    self.data = obj
                else:
                    self.data = da.from_array(obj, chunks=self.chunks)

                self.has_canonical_format = False

        else:
            if isspmatrix(arg1):
                if isspmatrix_coo(arg1) and copy:
                    self.row = arg1.row.copy()
                    self.col = arg1.col.copy()
                    self.data = arg1.data.copy()
                    self.shape = arg1.shape
                else:
                    coo = arg1.tocoo()
                    self.row = coo.row
                    self.col = coo.col
                    self.data = coo.data
                    self.shape = coo.shape
                self.has_canonical_format = False
            else:
                #dense argument
                M = np.atleast_2d(np.asarray(arg1))

                if M.ndim != 2:
                    raise TypeError('expected dimension <= 2 array or matrix')
                else:
                    self.shape = M.shape

                self.row, self.col = M.nonzero()
                self.data = M[self.row, self.col]
                self.has_canonical_format = True

        if dtype is not None:
            self.data = self.data.astype(dtype)

        self._check()
Example #27
0
 def matrix_issparse(self):
     """Whether the representing matrix is sparse or not."""
     return isspmatrix(self.matrix)