def _trace(A): # A compatibility function which should eventually disappear. if scipy.sparse.isspmatrix(A): return A.diagonal().sum() elif is_pydata_spmatrix(A): return A.to_scipy_sparse().diagonal().sum() else: return np.trace(A)
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. 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 requires CSC matrix format', SparseEfficiencyWarning) A = A.asfptype() # upcast to a floating point format if A.dtype.char not in 'dD': raise ValueError( "convert matrix area_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 _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 __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
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. Should be in CSR or CSC format. 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 http://crd.lbl.gov/~xiaoye/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 requires CSC matrix 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) return _superlu.gstrf(N, A.nnz, A.data, A.indices, A.indptr, csc_construct_func=csc_construct_func, ilu=False, options=_options)
def svds(A, k=6, ncv=None, tol=0, which='LM', v0=None, maxiter=None, return_singular_vectors=True, solver='arpack', options=None): """ Partial singular value decomposition of a sparse matrix. Compute the largest or smallest `k` singular values and corresponding singular vectors of a sparse matrix `A`. The order in which the singular values are returned is not guaranteed. In the descriptions below, let ``M, N = A.shape``. Parameters ---------- A : sparse matrix or LinearOperator Matrix to decompose. k : int, default: 6 Number of singular values and singular vectors to compute. Must satisfy ``1 <= k < min(M, N)``. ncv : int, optional When ``solver='arpack'``, this is the number of Lanczos vectors generated. See :ref:`'arpack' <sparse.linalg.svds-arpack>` for details. When ``solver='lobpcg'``, this parameter is ignored. tol : float, optional Tolerance for singular values. Zero (default) means machine precision. which : {'LM', 'SM'} Which `k` singular values to find: either the largest magnitude ('LM') or smallest magnitude ('SM') singular values. v0 : ndarray, optional The starting vector for iteration; see method-specific documentation (:ref:`'arpack' <sparse.linalg.svds-arpack>` or :ref:`'lobpcg' <sparse.linalg.svds-lobpcg>`) for details. maxiter : int, optional Maximum number of iterations; see method-specific documentation (:ref:`'arpack' <sparse.linalg.svds-arpack>` or :ref:`'lobpcg' <sparse.linalg.svds-lobpcg>`) for details. return_singular_vectors : bool or str, optional Singular values are always computed and returned; this parameter controls the computation and return of singular vectors. - ``True``: return singular vectors. - ``False``: do not return singular vectors. - ``"u"``: only return the left singular values, without computing the right singular vectors (if ``N > M``). - ``"vh"``: only return the right singular values, without computing the left singular vectors (if ``N <= M``). solver : str, optional The solver used. :ref:`'arpack' <sparse.linalg.svds-arpack>` and :ref:`'lobpcg' <sparse.linalg.svds-lobpcg>` are supported. Default: `'arpack'`. options : dict, optional A dictionary of solver-specific options. No solver-specific options are currently supported; this parameter is reserved for future use. Returns ------- u : ndarray, shape=(M, k) Unitary matrix having left singular vectors as columns. If `return_singular_vectors` is ``"vh"``, this variable is not computed, and ``None`` is returned instead. s : ndarray, shape=(k,) The singular values. vh : ndarray, shape=(k, N) Unitary matrix having right singular vectors as rows. If `return_singular_vectors` is ``"u"``, this variable is not computed, and ``None`` is returned instead. Notes ----- This is a naive implementation using ARPACK or LOBPCG as an eigensolver on ``A.conj().T @ A`` or ``A @ A.conj().T``, depending on which one is more efficient. Examples -------- Construct a matrix ``A`` from singular values and vectors. >>> from scipy.stats import ortho_group >>> from scipy.sparse import csc_matrix, diags >>> from scipy.sparse.linalg import svds >>> rng = np.random.default_rng() >>> orthogonal = csc_matrix(ortho_group.rvs(10, random_state=rng)) >>> s = [0.0001, 0.001, 3, 4, 5] # singular values >>> u = orthogonal[:, :5] # left singular vectors >>> vT = orthogonal[:, 5:].T # right singular vectors >>> A = u @ diags(s) @ vT With only three singular values/vectors, the SVD approximates the original matrix. >>> u2, s2, vT2 = svds(A, k=3) >>> A2 = u2 @ np.diag(s2) @ vT2 >>> np.allclose(A2, A.todense(), atol=1e-3) True With all five singular values/vectors, we can reproduce the original matrix. >>> u3, s3, vT3 = svds(A, k=5) >>> A3 = u3 @ np.diag(s3) @ vT3 >>> np.allclose(A3, A.todense()) True The singular values match the expected singular values, and the singular values are as expected up to a difference in sign. Consequently, the returned arrays of singular vectors must also be orthogonal. >>> (np.allclose(s3, s) and ... np.allclose(np.abs(u3), np.abs(u.todense())) and ... np.allclose(np.abs(vT3), np.abs(vT.todense()))) True """ if which == 'LM': largest = True elif which == 'SM': largest = False else: raise ValueError("which must be either 'LM' or 'SM'.") if (not (isinstance(A, LinearOperator) or isspmatrix(A) or is_pydata_spmatrix(A))): A = np.asarray(A) n, m = A.shape if k <= 0 or k >= min(n, m): raise ValueError("k must be between 1 and min(A.shape), k=%d" % k) if isinstance(A, LinearOperator): if n > m: X_dot = A.matvec X_matmat = A.matmat XH_dot = A.rmatvec XH_mat = A.rmatmat else: X_dot = A.rmatvec X_matmat = A.rmatmat XH_dot = A.matvec XH_mat = A.matmat dtype = getattr(A, 'dtype', None) if dtype is None: dtype = A.dot(np.zeros([m, 1])).dtype else: if n > m: X_dot = X_matmat = A.dot XH_dot = XH_mat = _herm(A).dot else: XH_dot = XH_mat = A.dot X_dot = X_matmat = _herm(A).dot def matvec_XH_X(x): return XH_dot(X_dot(x)) def matmat_XH_X(x): return XH_mat(X_matmat(x)) XH_X = LinearOperator(matvec=matvec_XH_X, dtype=A.dtype, matmat=matmat_XH_X, shape=(min(A.shape), min(A.shape))) # Get a low rank approximation of the implicitly defined gramian matrix. # This is not a stable way to approach the problem. if solver == 'lobpcg': if k == 1 and v0 is not None: X = np.reshape(v0, (-1, 1)) else: X = np.random.RandomState(52).randn(min(A.shape), k) eigvals, eigvec = lobpcg(XH_X, X, tol=tol ** 2, maxiter=maxiter, largest=largest) elif solver == 'arpack' or solver is None: eigvals, eigvec = eigsh(XH_X, k=k, tol=tol ** 2, maxiter=maxiter, ncv=ncv, which=which, v0=v0) else: raise ValueError("solver must be either 'arpack', or 'lobpcg'.") # Gramian matrices have real non-negative eigenvalues. eigvals = np.maximum(eigvals.real, 0) # Use the sophisticated detection of small eigenvalues from pinvh. t = eigvec.dtype.char.lower() factor = {'f': 1E3, 'd': 1E6} cond = factor[t] * np.finfo(t).eps cutoff = cond * np.max(eigvals) # Get a mask indicating which eigenpairs are not degenerately tiny, # and create the re-ordered array of thresholded singular values. above_cutoff = (eigvals > cutoff) nlarge = above_cutoff.sum() nsmall = k - nlarge slarge = np.sqrt(eigvals[above_cutoff]) s = np.zeros_like(eigvals) s[:nlarge] = slarge if not return_singular_vectors: return np.sort(s) if n > m: vlarge = eigvec[:, above_cutoff] ularge = (X_matmat(vlarge) / slarge if return_singular_vectors != 'vh' else None) vhlarge = _herm(vlarge) else: ularge = eigvec[:, above_cutoff] vhlarge = (_herm(X_matmat(ularge) / slarge) if return_singular_vectors != 'u' else None) u = (_augmented_orthonormal_cols(ularge, nsmall) if ularge is not None else None) vh = (_augmented_orthonormal_rows(vhlarge, nsmall) if vhlarge is not None else None) indexes_sorted = np.argsort(s) s = s[indexes_sorted] if u is not None: u = u[:, indexes_sorted] if vh is not None: vh = vh[indexes_sorted] return u, s, vh