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) or is_pydata_spmatrix(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))
def _exact_1_norm(A): # A compatibility function which should eventually disappear. if scipy.sparse.isspmatrix(A): return max(abs(A).sum(axis=0).flat) elif is_pydata_spmatrix(A): return max(abs(A).sum(axis=0)) else: return np.linalg.norm(A, 1)
def factorized(A): """ Return a function for solving a sparse linear system, with A pre-factorized. Parameters ---------- A : (N, N) array_like Input. A in CSC format is most efficient. A CSR format matrix will be converted to CSC before factorization. Returns ------- solve : callable To solve the linear system of equations given in `A`, the `solve` callable should be passed an ndarray of shape (N,). Examples -------- >>> from scipy.sparse.linalg import factorized >>> A = np.array([[ 3. , 2. , -1. ], ... [ 2. , -2. , 4. ], ... [-1. , 0.5, -1. ]]) >>> solve = factorized(A) # Makes LU decomposition. >>> rhs1 = np.array([1, -2, 0]) >>> solve(rhs1) # Uses the LU factors. array([ 1., -2., -2.]) """ if is_pydata_spmatrix(A): A = A.to_scipy_sparse().tocsc() if useUmfpack: if noScikit: raise RuntimeError('Scikits.umfpack not installed.') if not isspmatrix_csc(A): A = csc_matrix(A) warn('splu converted its input to CSC format', SparseEfficiencyWarning) A = A.asfptype() # upcast to a floating point format if A.dtype.char not in 'dD': raise ValueError("convert matrix data to double, please, using" " .astype(), or set linsolve.useUmfpack = False") umf_family, A = _get_umf_family(A) umf = umfpack.UmfpackContext(umf_family) # Make LU decomposition. umf.numeric(A) def solve(b): return umf.solve(umfpack.UMFPACK_A, A, b, autoTranspose=True) return solve else: return splu(A).solve
def aslinearoperator(A): """Return A as a LinearOperator. 'A' may be any of the following types: - ndarray - matrix - sparse matrix (e.g. csr_matrix, lil_matrix, etc.) - LinearOperator - An object with .shape and .matvec attributes See the LinearOperator documentation for additional information. Notes ----- If 'A' has no .dtype attribute, the data type is determined by calling :func:`LinearOperator.matvec()` - set the .dtype attribute to prevent this call upon the linear operator creation. Examples -------- >>> from scipy.sparse.linalg import aslinearoperator >>> M = np.array([[1,2,3],[4,5,6]], dtype=np.int32) >>> aslinearoperator(M) <2x3 MatrixLinearOperator with dtype=int32> """ if isinstance(A, LinearOperator): return A elif isinstance(A, np.ndarray) or isinstance(A, np.matrix): if A.ndim > 2: raise ValueError('array must have ndim <= 2') A = np.atleast_2d(np.asarray(A)) return MatrixLinearOperator(A) elif isspmatrix(A) or is_pydata_spmatrix(A): return MatrixLinearOperator(A) else: if hasattr(A, 'shape') and hasattr(A, 'matvec'): rmatvec = None rmatmat = None dtype = None if hasattr(A, 'rmatvec'): rmatvec = A.rmatvec if hasattr(A, 'rmatmat'): rmatmat = A.rmatmat if hasattr(A, 'dtype'): dtype = A.dtype return LinearOperator(A.shape, A.matvec, rmatvec=rmatvec, rmatmat=rmatmat, dtype=dtype) else: raise TypeError('type not understood')
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
def _ident_like(A): # A compatibility function which should eventually disappear. if scipy.sparse.isspmatrix(A): return scipy.sparse._construct.eye(A.shape[0], A.shape[1], dtype=A.dtype, format=A.format) elif is_pydata_spmatrix(A): import sparse return sparse.eye(A.shape[0], A.shape[1], dtype=A.dtype) else: return np.eye(A.shape[0], A.shape[1], dtype=A.dtype)
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()
def inv(A): """ Compute the inverse of a sparse matrix Parameters ---------- A : (M, M) sparse matrix square matrix to be inverted Returns ------- Ainv : (M, M) sparse matrix inverse of `A` Notes ----- This computes the sparse inverse of `A`. If the inverse of `A` is expected to be non-sparse, it will likely be faster to convert `A` to dense and use `scipy.linalg.inv`. Examples -------- >>> from scipy.sparse import csc_matrix >>> from scipy.sparse.linalg import inv >>> A = csc_matrix([[1., 0.], [1., 2.]]) >>> Ainv = inv(A) >>> Ainv <2x2 sparse matrix of type '<class 'numpy.float64'>' with 3 stored elements in Compressed Sparse Column format> >>> A.dot(Ainv) <2x2 sparse matrix of type '<class 'numpy.float64'>' with 2 stored elements in Compressed Sparse Column format> >>> A.dot(Ainv).toarray() array([[ 1., 0.], [ 0., 1.]]) .. versionadded:: 0.12.0 """ # Check input if not (scipy.sparse.isspmatrix(A) or is_pydata_spmatrix(A)): raise TypeError('Input must be a sparse matrix') # Use sparse direct solver to solve "AX = I" accurately I = _ident_like(A) Ainv = spsolve(A, I) return Ainv
def spsolve_triangular(A, b, lower=True, overwrite_A=False, overwrite_b=False, unit_diagonal=False): """ Solve the equation ``A x = b`` for `x`, assuming A is a triangular matrix. Parameters ---------- A : (M, M) sparse matrix A sparse square triangular matrix. Should be in CSR format. b : (M,) or (M, N) array_like Right-hand side matrix in ``A x = b`` lower : bool, optional Whether `A` is a lower or upper triangular matrix. Default is lower triangular matrix. overwrite_A : bool, optional Allow changing `A`. The indices of `A` are going to be sorted and zero entries are going to be removed. Enabling gives a performance gain. Default is False. overwrite_b : bool, optional Allow overwriting data in `b`. Enabling gives a performance gain. Default is False. If `overwrite_b` is True, it should be ensured that `b` has an appropriate dtype to be able to store the result. unit_diagonal : bool, optional If True, diagonal elements of `a` are assumed to be 1 and will not be referenced. .. versionadded:: 1.4.0 Returns ------- x : (M,) or (M, N) ndarray Solution to the system ``A x = b``. Shape of return matches shape of `b`. Raises ------ LinAlgError If `A` is singular or not triangular. ValueError If shape of `A` or shape of `b` do not match the requirements. Notes ----- .. versionadded:: 0.19.0 Examples -------- >>> from scipy.sparse import csr_matrix >>> from scipy.sparse.linalg import spsolve_triangular >>> A = csr_matrix([[3, 0, 0], [1, -1, 0], [2, 0, 1]], dtype=float) >>> B = np.array([[2, 0], [-1, 0], [2, 0]], dtype=float) >>> x = spsolve_triangular(A, B) >>> np.allclose(A.dot(x), B) True """ if is_pydata_spmatrix(A): A = A.to_scipy_sparse().tocsr() # Check the input for correct type and format. if not isspmatrix_csr(A): warn('CSR matrix format is required. Converting to CSR matrix.', SparseEfficiencyWarning) A = csr_matrix(A) elif not overwrite_A: A = A.copy() if A.shape[0] != A.shape[1]: raise ValueError( 'A must be a square matrix but its shape is {}.'.format(A.shape)) # sum duplicates for non-canonical format A.sum_duplicates() b = np.asanyarray(b) if b.ndim not in [1, 2]: raise ValueError('b must have 1 or 2 dims but its shape is {}.'.format( b.shape)) if A.shape[0] != b.shape[0]: raise ValueError( 'The size of the dimensions of A must be equal to ' 'the size of the first dimension of b but the shape of A is ' '{} and the shape of b is {}.'.format(A.shape, b.shape)) # Init x as (a copy of) b. x_dtype = np.result_type(A.data, b, np.float64) if overwrite_b: if np.can_cast(b.dtype, x_dtype, casting='same_kind'): x = b else: raise ValueError('Cannot overwrite b (dtype {}) with result ' 'of type {}.'.format(b.dtype, x_dtype)) else: x = b.astype(x_dtype, copy=True) # Choose forward or backward order. if lower: row_indices = range(len(b)) else: row_indices = range(len(b) - 1, -1, -1) # Fill x iteratively. for i in row_indices: # Get indices for i-th row. indptr_start = A.indptr[i] indptr_stop = A.indptr[i + 1] if lower: A_diagonal_index_row_i = indptr_stop - 1 A_off_diagonal_indices_row_i = slice(indptr_start, indptr_stop - 1) else: A_diagonal_index_row_i = indptr_start A_off_diagonal_indices_row_i = slice(indptr_start + 1, indptr_stop) # Check regularity and triangularity of A. if not unit_diagonal and (indptr_stop <= indptr_start or A.indices[A_diagonal_index_row_i] < i): raise LinAlgError('A is singular: diagonal {} is zero.'.format(i)) if not unit_diagonal and A.indices[A_diagonal_index_row_i] > i: raise LinAlgError('A is not triangular: A[{}, {}] is nonzero.' ''.format(i, A.indices[A_diagonal_index_row_i])) # Incorporate off-diagonal entries. A_column_indices_in_row_i = A.indices[A_off_diagonal_indices_row_i] A_values_in_row_i = A.data[A_off_diagonal_indices_row_i] x[i] -= np.dot(x[A_column_indices_in_row_i].T, A_values_in_row_i) # Compute i-th entry of x. if not unit_diagonal: x[i] /= A.data[A_diagonal_index_row_i] return x
def spilu(A, drop_tol=None, fill_factor=None, drop_rule=None, permc_spec=None, diag_pivot_thresh=None, relax=None, panel_size=None, options=None): """ Compute an incomplete LU decomposition for a sparse, square matrix. The resulting object is an approximation to the inverse of `A`. Parameters ---------- A : (N, N) array_like Sparse matrix to factorize. Most efficient when provided in CSC format. Other formats will be converted to CSC before factorization. drop_tol : float, optional Drop tolerance (0 <= tol <= 1) for an incomplete LU decomposition. (default: 1e-4) fill_factor : float, optional Specifies the fill ratio upper bound (>= 1.0) for ILU. (default: 10) drop_rule : str, optional Comma-separated string of drop rules to use. Available rules: ``basic``, ``prows``, ``column``, ``area``, ``secondary``, ``dynamic``, ``interp``. (Default: ``basic,area``) See SuperLU documentation for details. Remaining other options Same as for `splu` Returns ------- invA_approx : scipy.sparse.linalg.SuperLU Object, which has a ``solve`` method. See also -------- splu : complete LU decomposition Notes ----- To improve the better approximation to the inverse, you may need to increase `fill_factor` AND decrease `drop_tol`. This function uses the SuperLU library. Examples -------- >>> from scipy.sparse import csc_matrix >>> from scipy.sparse.linalg import spilu >>> A = csc_matrix([[1., 0., 0.], [5., 0., 2.], [0., -1., 0.]], dtype=float) >>> B = spilu(A) >>> x = np.array([1., 2., 3.], dtype=float) >>> B.solve(x) array([ 1. , -3. , -1.5]) >>> A.dot(B.solve(x)) array([ 1., 2., 3.]) >>> B.solve(A.dot(x)) array([ 1., 2., 3.]) """ if is_pydata_spmatrix(A): csc_construct_func = lambda *a, cls=type(A): cls(csc_matrix(*a)) A = A.to_scipy_sparse().tocsc() else: csc_construct_func = csc_matrix if not isspmatrix_csc(A): A = csc_matrix(A) warn('spilu converted its input to CSC format', SparseEfficiencyWarning) # sum duplicates for non-canonical format A.sum_duplicates() A = A.asfptype() # upcast to a floating point format M, N = A.shape if (M != N): raise ValueError("can only factor square matrices") # is this true? _options = dict(ILU_DropRule=drop_rule, ILU_DropTol=drop_tol, ILU_FillFactor=fill_factor, DiagPivotThresh=diag_pivot_thresh, ColPerm=permc_spec, PanelSize=panel_size, Relax=relax) if options is not None: _options.update(options) # Ensure that no column permutations are applied if (_options["ColPerm"] == "NATURAL"): _options["SymmetricMode"] = True return _superlu.gstrf(N, A.nnz, A.data, A.indices, A.indptr, csc_construct_func=csc_construct_func, ilu=True, options=_options)
def splu(A, permc_spec=None, diag_pivot_thresh=None, relax=None, panel_size=None, options=dict()): """ Compute the LU decomposition of a sparse, square matrix. Parameters ---------- A : sparse matrix Sparse matrix to factorize. Most efficient when provided in CSC format. Other formats will be converted to CSC before factorization. permc_spec : str, optional How to permute the columns of the matrix for sparsity preservation. (default: 'COLAMD') - ``NATURAL``: natural ordering. - ``MMD_ATA``: minimum degree ordering on the structure of A^T A. - ``MMD_AT_PLUS_A``: minimum degree ordering on the structure of A^T+A. - ``COLAMD``: approximate minimum degree column ordering diag_pivot_thresh : float, optional Threshold used for a diagonal entry to be an acceptable pivot. See SuperLU user's guide for details [1]_ relax : int, optional Expert option for customizing the degree of relaxing supernodes. See SuperLU user's guide for details [1]_ panel_size : int, optional Expert option for customizing the panel size. See SuperLU user's guide for details [1]_ options : dict, optional Dictionary containing additional expert options to SuperLU. See SuperLU user guide [1]_ (section 2.4 on the 'Options' argument) for more details. For example, you can specify ``options=dict(Equil=False, IterRefine='SINGLE'))`` to turn equilibration off and perform a single iterative refinement. Returns ------- invA : scipy.sparse.linalg.SuperLU Object, which has a ``solve`` method. See also -------- spilu : incomplete LU decomposition Notes ----- This function uses the SuperLU library. References ---------- .. [1] SuperLU https://portal.nersc.gov/project/sparse/superlu/ Examples -------- >>> from scipy.sparse import csc_matrix >>> from scipy.sparse.linalg import splu >>> A = csc_matrix([[1., 0., 0.], [5., 0., 2.], [0., -1., 0.]], dtype=float) >>> B = splu(A) >>> x = np.array([1., 2., 3.], dtype=float) >>> B.solve(x) array([ 1. , -3. , -1.5]) >>> A.dot(B.solve(x)) array([ 1., 2., 3.]) >>> B.solve(A.dot(x)) array([ 1., 2., 3.]) """ if is_pydata_spmatrix(A): csc_construct_func = lambda *a, cls=type(A): cls(csc_matrix(*a)) A = A.to_scipy_sparse().tocsc() else: csc_construct_func = csc_matrix if not isspmatrix_csc(A): A = csc_matrix(A) warn('splu converted its input to CSC format', SparseEfficiencyWarning) # sum duplicates for non-canonical format A.sum_duplicates() A = A.asfptype() # upcast to a floating point format M, N = A.shape if (M != N): raise ValueError("can only factor square matrices") # is this true? _options = dict(DiagPivotThresh=diag_pivot_thresh, ColPerm=permc_spec, PanelSize=panel_size, Relax=relax) if options is not None: _options.update(options) # Ensure that no column permutations are applied if (_options["ColPerm"] == "NATURAL"): _options["SymmetricMode"] = True return _superlu.gstrf(N, A.nnz, A.data, A.indices, A.indptr, csc_construct_func=csc_construct_func, ilu=False, options=_options)
def spsolve(A, b, permc_spec=None, use_umfpack=True): """Solve the sparse linear system Ax=b, where b may be a vector or a matrix. Parameters ---------- A : ndarray or sparse matrix The square matrix A will be converted into CSC or CSR form b : ndarray or sparse matrix The matrix or vector representing the right hand side of the equation. If a vector, b.shape must be (n,) or (n, 1). permc_spec : str, optional How to permute the columns of the matrix for sparsity preservation. (default: 'COLAMD') - ``NATURAL``: natural ordering. - ``MMD_ATA``: minimum degree ordering on the structure of A^T A. - ``MMD_AT_PLUS_A``: minimum degree ordering on the structure of A^T+A. - ``COLAMD``: approximate minimum degree column ordering [1]_, [2]_. use_umfpack : bool, optional if True (default) then use UMFPACK for the solution [3]_, [4]_, [5]_, [6]_ . This is only referenced if b is a vector and ``scikits.umfpack`` is installed. Returns ------- x : ndarray or sparse matrix the solution of the sparse linear equation. If b is a vector, then x is a vector of size A.shape[1] If b is a matrix, then x is a matrix of size (A.shape[1], b.shape[1]) Notes ----- For solving the matrix expression AX = B, this solver assumes the resulting matrix X is sparse, as is often the case for very sparse inputs. If the resulting X is dense, the construction of this sparse result will be relatively expensive. In that case, consider converting A to a dense matrix and using scipy.linalg.solve or its variants. References ---------- .. [1] T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, Algorithm 836: COLAMD, an approximate column minimum degree ordering algorithm, ACM Trans. on Mathematical Software, 30(3), 2004, pp. 377--380. :doi:`10.1145/1024074.1024080` .. [2] T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, A column approximate minimum degree ordering algorithm, ACM Trans. on Mathematical Software, 30(3), 2004, pp. 353--376. :doi:`10.1145/1024074.1024079` .. [3] T. A. Davis, Algorithm 832: UMFPACK - an unsymmetric-pattern multifrontal method with a column pre-ordering strategy, ACM Trans. on Mathematical Software, 30(2), 2004, pp. 196--199. https://dl.acm.org/doi/abs/10.1145/992200.992206 .. [4] T. A. Davis, A column pre-ordering strategy for the unsymmetric-pattern multifrontal method, ACM Trans. on Mathematical Software, 30(2), 2004, pp. 165--195. https://dl.acm.org/doi/abs/10.1145/992200.992205 .. [5] T. A. Davis and I. S. Duff, A combined unifrontal/multifrontal method for unsymmetric sparse matrices, ACM Trans. on Mathematical Software, 25(1), 1999, pp. 1--19. https://doi.org/10.1145/305658.287640 .. [6] T. A. Davis and I. S. Duff, An unsymmetric-pattern multifrontal method for sparse LU factorization, SIAM J. Matrix Analysis and Computations, 18(1), 1997, pp. 140--158. https://doi.org/10.1137/S0895479894246905T. Examples -------- >>> from scipy.sparse import csc_matrix >>> from scipy.sparse.linalg import spsolve >>> A = csc_matrix([[3, 2, 0], [1, -1, 0], [0, 5, 1]], dtype=float) >>> B = csc_matrix([[2, 0], [-1, 0], [2, 0]], dtype=float) >>> x = spsolve(A, B) >>> np.allclose(A.dot(x).toarray(), B.toarray()) True """ if is_pydata_spmatrix(A): A = A.to_scipy_sparse().tocsc() if not (isspmatrix_csc(A) or isspmatrix_csr(A)): A = csc_matrix(A) warn('spsolve requires A be CSC or CSR matrix format', SparseEfficiencyWarning) # b is a vector only if b have shape (n,) or (n, 1) b_is_sparse = isspmatrix(b) or is_pydata_spmatrix(b) if not b_is_sparse: b = asarray(b) b_is_vector = ((b.ndim == 1) or (b.ndim == 2 and b.shape[1] == 1)) # sum duplicates for non-canonical format A.sum_duplicates() A = A.asfptype() # upcast to a floating point format result_dtype = np.promote_types(A.dtype, b.dtype) if A.dtype != result_dtype: A = A.astype(result_dtype) if b.dtype != result_dtype: b = b.astype(result_dtype) # validate input shapes M, N = A.shape if (M != N): raise ValueError("matrix must be square (has shape %s)" % ((M, N), )) if M != b.shape[0]: raise ValueError("matrix - rhs dimension mismatch (%s - %s)" % (A.shape, b.shape[0])) use_umfpack = use_umfpack and useUmfpack if b_is_vector and use_umfpack: if b_is_sparse: b_vec = b.toarray() else: b_vec = b b_vec = asarray(b_vec, dtype=A.dtype).ravel() if noScikit: raise RuntimeError('Scikits.umfpack not installed.') if A.dtype.char not in 'dD': raise ValueError("convert matrix data to double, please, using" " .astype(), or set linsolve.useUmfpack = False") umf_family, A = _get_umf_family(A) umf = umfpack.UmfpackContext(umf_family) x = umf.linsolve(umfpack.UMFPACK_A, A, b_vec, autoTranspose=True) else: if b_is_vector and b_is_sparse: b = b.toarray() b_is_sparse = False if not b_is_sparse: if isspmatrix_csc(A): flag = 1 # CSC format else: flag = 0 # CSR format options = dict(ColPerm=permc_spec) x, info = _superlu.gssv(N, A.nnz, A.data, A.indices, A.indptr, b, flag, options=options) if info != 0: warn("Matrix is exactly singular", MatrixRankWarning) x.fill(np.nan) if b_is_vector: x = x.ravel() else: # b is sparse Afactsolve = factorized(A) if not (isspmatrix_csc(b) or is_pydata_spmatrix(b)): warn( 'spsolve is more efficient when sparse b ' 'is in the CSC matrix format', SparseEfficiencyWarning) b = csc_matrix(b) # Create a sparse output matrix by repeatedly applying # the sparse factorization to solve columns of b. data_segs = [] row_segs = [] col_segs = [] for j in range(b.shape[1]): # TODO: replace this with # bj = b[:, j].toarray().ravel() # once 1D sparse arrays are supported. # That is a slightly faster code path. bj = b[:, [j]].toarray().ravel() xj = Afactsolve(bj) w = np.flatnonzero(xj) segment_length = w.shape[0] row_segs.append(w) col_segs.append(np.full(segment_length, j, dtype=int)) data_segs.append(np.asarray(xj[w], dtype=A.dtype)) sparse_data = np.concatenate(data_segs) sparse_row = np.concatenate(row_segs) sparse_col = np.concatenate(col_segs) x = A.__class__((sparse_data, (sparse_row, sparse_col)), shape=b.shape, dtype=A.dtype) if is_pydata_spmatrix(b): x = b.__class__(x) return x
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, np.matrix)): A = np.asarray(A) if len(A.shape) != 2 or A.shape[0] != A.shape[1]: raise ValueError('expected a square matrix') # gracefully handle size-0 input, # carefully handling sparse scenario if A.shape == (0, 0): out = np.zeros([0, 0], dtype=A.dtype) if isspmatrix(A) or is_pydata_spmatrix(A): return A.__class__(out) return out # 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) or is_pydata_spmatrix(A): return A.__class__(out) return np.array(out) # Ensure input is of float type, to avoid integer overflows etc. if ((isinstance(A, np.ndarray) or isspmatrix(A) or is_pydata_spmatrix(A)) and not np.issubdtype(A.dtype, np.inexact)): A = A.astype(float) # 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
def spsolve(A, b, permc_spec=None, use_umfpack=True): """Solve the sparse linear system Ax=b, where b may be a vector or a matrix. Parameters ---------- A : ndarray or sparse matrix The square matrix A will be converted into CSC or CSR form b : ndarray or sparse matrix The matrix or vector representing the right hand side of the equation. If a vector, b.shape must be (n,) or (n, 1). permc_spec : str, optional How to permute the columns of the matrix for sparsity preservation. (default: 'COLAMD') - ``NATURAL``: natural ordering. - ``MMD_ATA``: minimum degree ordering on the structure of A^T A. - ``MMD_AT_PLUS_A``: minimum degree ordering on the structure of A^T+A. - ``COLAMD``: approximate minimum degree column ordering use_umfpack : bool, optional if True (default) then use umfpack for the solution. This is only referenced if b is a vector and ``scikit-umfpack`` is installed. Returns ------- x : ndarray or sparse matrix the solution of the sparse linear equation. If b is a vector, then x is a vector of size A.shape[1] If b is a matrix, then x is a matrix of size (A.shape[1], b.shape[1]) Notes ----- For solving the matrix expression AX = B, this solver assumes the resulting matrix X is sparse, as is often the case for very sparse inputs. If the resulting X is dense, the construction of this sparse result will be relatively expensive. In that case, consider converting A to a dense matrix and using scipy.linalg.solve or its variants. Examples -------- >>> from scipy.sparse import csc_matrix >>> from scipy.sparse.linalg import spsolve >>> A = csc_matrix([[3, 2, 0], [1, -1, 0], [0, 5, 1]], dtype=float) >>> B = csc_matrix([[2, 0], [-1, 0], [2, 0]], dtype=float) >>> x = spsolve(A, B) >>> np.allclose(A.dot(x).toarray(), B.toarray()) True """ if is_pydata_spmatrix(A): A = A.to_scipy_sparse().tocsc() if not (isspmatrix_csc(A) or isspmatrix_csr(A)): A = csc_matrix(A) warn('spsolve requires A be CSC or CSR matrix format', SparseEfficiencyWarning) # b is a vector only if b have shape (n,) or (n, 1) b_is_sparse = isspmatrix(b) or is_pydata_spmatrix(b) if not b_is_sparse: b = asarray(b) b_is_vector = ((b.ndim == 1) or (b.ndim == 2 and b.shape[1] == 1)) # sum duplicates for non-canonical format A.sum_duplicates() A = A.asfptype() # upcast to a floating point format result_dtype = np.promote_types(A.dtype, b.dtype) if A.dtype != result_dtype: A = A.astype(result_dtype) if b.dtype != result_dtype: b = b.astype(result_dtype) # validate input shapes M, N = A.shape if (M != N): raise ValueError("matrix must be square (has shape %s)" % ((M, N), )) if M != b.shape[0]: raise ValueError("matrix - rhs dimension mismatch (%s - %s)" % (A.shape, b.shape[0])) use_umfpack = use_umfpack and useUmfpack if b_is_vector and use_umfpack: if b_is_sparse: b_vec = b.toarray() else: b_vec = b b_vec = asarray(b_vec, dtype=A.dtype).ravel() if noScikit: raise RuntimeError('Scikits.umfpack not installed.') if A.dtype.char not in 'dD': raise ValueError("convert matrix data to double, please, using" " .astype(), or set linsolve.useUmfpack = False") umf_family, A = _get_umf_family(A) umf = umfpack.UmfpackContext(umf_family) x = umf.linsolve(umfpack.UMFPACK_A, A, b_vec, autoTranspose=True) else: if b_is_vector and b_is_sparse: b = b.toarray() b_is_sparse = False if not b_is_sparse: if isspmatrix_csc(A): flag = 1 # CSC format else: flag = 0 # CSR format options = dict(ColPerm=permc_spec) x, info = _superlu.gssv(N, A.nnz, A.data, A.indices, A.indptr, b, flag, options=options) if info != 0: warn("Matrix is exactly singular", MatrixRankWarning) x.fill(np.nan) if b_is_vector: x = x.ravel() else: # b is sparse Afactsolve = factorized(A) if not (isspmatrix_csc(b) or is_pydata_spmatrix(b)): warn( 'spsolve is more efficient when sparse b ' 'is in the CSC matrix format', SparseEfficiencyWarning) b = csc_matrix(b) # Create a sparse output matrix by repeatedly applying # the sparse factorization to solve columns of b. data_segs = [] row_segs = [] col_segs = [] for j in range(b.shape[1]): bj = b[:, j].toarray().ravel() xj = Afactsolve(bj) w = np.flatnonzero(xj) segment_length = w.shape[0] row_segs.append(w) col_segs.append(np.full(segment_length, j, dtype=int)) data_segs.append(np.asarray(xj[w], dtype=A.dtype)) sparse_data = np.concatenate(data_segs) sparse_row = np.concatenate(row_segs) sparse_col = np.concatenate(col_segs) x = A.__class__((sparse_data, (sparse_row, sparse_col)), shape=b.shape, dtype=A.dtype) if is_pydata_spmatrix(b): x = b.__class__(x) return x
def _trace(A): # A compatibility function which should eventually disappear. if is_pydata_spmatrix(A): return A.to_scipy_sparse().trace() else: return A.trace()