linsolve.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885
  1. from warnings import warn, catch_warnings, simplefilter
  2. import numpy as np
  3. from numpy import asarray
  4. from scipy.sparse import (issparse, SparseEfficiencyWarning,
  5. csr_array, csc_array, eye_array, diags_array)
  6. from scipy.sparse._sputils import (is_pydata_spmatrix, convert_pydata_sparse_to_scipy,
  7. get_index_dtype, safely_cast_index_arrays)
  8. from scipy.linalg import LinAlgError
  9. import copy
  10. import threading
  11. from . import _superlu
  12. noScikit = False
  13. try:
  14. import scikits.umfpack as umfpack
  15. except ImportError:
  16. noScikit = True
  17. useUmfpack = threading.local()
  18. __all__ = ['use_solver', 'spsolve', 'splu', 'spilu', 'factorized',
  19. 'MatrixRankWarning', 'spsolve_triangular', 'is_sptriangular', 'spbandwidth']
  20. class MatrixRankWarning(UserWarning):
  21. """Warning for exactly singular matrices."""
  22. pass
  23. def use_solver(**kwargs):
  24. """
  25. Select default sparse direct solver to be used.
  26. Parameters
  27. ----------
  28. useUmfpack : bool, optional
  29. Use UMFPACK [1]_, [2]_, [3]_, [4]_. over SuperLU. Has effect only
  30. if ``scikits.umfpack`` is installed. Default: True
  31. assumeSortedIndices : bool, optional
  32. Allow UMFPACK to skip the step of sorting indices for a CSR/CSC matrix.
  33. Has effect only if useUmfpack is True and ``scikits.umfpack`` is
  34. installed. Default: False
  35. Notes
  36. -----
  37. The default sparse solver is UMFPACK when available
  38. (``scikits.umfpack`` is installed). This can be changed by passing
  39. useUmfpack = False, which then causes the always present SuperLU
  40. based solver to be used.
  41. UMFPACK requires a CSR/CSC matrix to have sorted column/row indices. If
  42. sure that the matrix fulfills this, pass ``assumeSortedIndices=True``
  43. to gain some speed.
  44. References
  45. ----------
  46. .. [1] T. A. Davis, Algorithm 832: UMFPACK - an unsymmetric-pattern
  47. multifrontal method with a column pre-ordering strategy, ACM
  48. Trans. on Mathematical Software, 30(2), 2004, pp. 196--199.
  49. https://dl.acm.org/doi/abs/10.1145/992200.992206
  50. .. [2] T. A. Davis, A column pre-ordering strategy for the
  51. unsymmetric-pattern multifrontal method, ACM Trans.
  52. on Mathematical Software, 30(2), 2004, pp. 165--195.
  53. https://dl.acm.org/doi/abs/10.1145/992200.992205
  54. .. [3] T. A. Davis and I. S. Duff, A combined unifrontal/multifrontal
  55. method for unsymmetric sparse matrices, ACM Trans. on
  56. Mathematical Software, 25(1), 1999, pp. 1--19.
  57. https://doi.org/10.1145/305658.287640
  58. .. [4] T. A. Davis and I. S. Duff, An unsymmetric-pattern multifrontal
  59. method for sparse LU factorization, SIAM J. Matrix Analysis and
  60. Computations, 18(1), 1997, pp. 140--158.
  61. https://doi.org/10.1137/S0895479894246905T.
  62. Examples
  63. --------
  64. >>> import numpy as np
  65. >>> from scipy.sparse.linalg import use_solver, spsolve
  66. >>> from scipy.sparse import csc_array
  67. >>> R = np.random.randn(5, 5)
  68. >>> A = csc_array(R)
  69. >>> b = np.random.randn(5)
  70. >>> use_solver(useUmfpack=False) # enforce superLU over UMFPACK
  71. >>> x = spsolve(A, b)
  72. >>> np.allclose(A.dot(x), b)
  73. True
  74. >>> use_solver(useUmfpack=True) # reset umfPack usage to default
  75. """
  76. global useUmfpack
  77. if 'useUmfpack' in kwargs:
  78. useUmfpack.u = kwargs['useUmfpack']
  79. if useUmfpack.u and 'assumeSortedIndices' in kwargs:
  80. umfpack.configure(assumeSortedIndices=kwargs['assumeSortedIndices'])
  81. def _get_umf_family(A):
  82. """Get umfpack family string given the sparse matrix dtype."""
  83. _families = {
  84. (np.float64, np.int32): 'di',
  85. (np.complex128, np.int32): 'zi',
  86. (np.float64, np.int64): 'dl',
  87. (np.complex128, np.int64): 'zl'
  88. }
  89. # A.dtype.name can only be "float64" or
  90. # "complex128" in control flow
  91. f_type = getattr(np, A.dtype.name)
  92. # control flow may allow for more index
  93. # types to get through here
  94. i_type = getattr(np, A.indices.dtype.name)
  95. try:
  96. family = _families[(f_type, i_type)]
  97. except KeyError as e:
  98. msg = ('only float64 or complex128 matrices with int32 or int64 '
  99. f'indices are supported! (got: matrix: {f_type}, indices: {i_type})')
  100. raise ValueError(msg) from e
  101. # See gh-8278. Considered converting only if
  102. # A.shape[0]*A.shape[1] > np.iinfo(np.int32).max,
  103. # but that didn't always fix the issue.
  104. family = family[0] + "l"
  105. A_new = copy.copy(A)
  106. A_new.indptr = np.asarray(A.indptr, dtype=np.int64)
  107. A_new.indices = np.asarray(A.indices, dtype=np.int64)
  108. return family, A_new
  109. def spsolve(A, b, permc_spec=None, use_umfpack=True):
  110. """Solve the sparse linear system Ax=b, where b may be a vector or a matrix.
  111. Parameters
  112. ----------
  113. A : ndarray or sparse array or matrix
  114. The square matrix A will be converted into CSC or CSR form
  115. b : ndarray or sparse array or matrix
  116. The matrix or vector representing the right hand side of the equation.
  117. If a vector, b.shape must be (n,) or (n, 1).
  118. permc_spec : str, optional
  119. How to permute the columns of the matrix for sparsity preservation.
  120. (default: 'COLAMD')
  121. - ``NATURAL``: natural ordering.
  122. - ``MMD_ATA``: minimum degree ordering on the structure of A^T A.
  123. - ``MMD_AT_PLUS_A``: minimum degree ordering on the structure of A^T+A.
  124. - ``COLAMD``: approximate minimum degree column ordering [1]_, [2]_.
  125. use_umfpack : bool, optional
  126. if True (default) then use UMFPACK for the solution [3]_, [4]_, [5]_,
  127. [6]_ . This is only referenced if b is a vector and
  128. ``scikits.umfpack`` is installed.
  129. Returns
  130. -------
  131. x : ndarray or sparse array or matrix
  132. the solution of the sparse linear equation.
  133. If b is a vector, then x is a vector of size A.shape[1]
  134. If b is a matrix, then x is a matrix of size (A.shape[1], b.shape[1])
  135. Notes
  136. -----
  137. For solving the matrix expression AX = B, this solver assumes the resulting
  138. matrix X is sparse, as is often the case for very sparse inputs. If the
  139. resulting X is dense, the construction of this sparse result will be
  140. relatively expensive. In that case, consider converting A to a dense
  141. matrix and using scipy.linalg.solve or its variants.
  142. References
  143. ----------
  144. .. [1] T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, Algorithm 836:
  145. COLAMD, an approximate column minimum degree ordering algorithm,
  146. ACM Trans. on Mathematical Software, 30(3), 2004, pp. 377--380.
  147. :doi:`10.1145/1024074.1024080`
  148. .. [2] T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, A column approximate
  149. minimum degree ordering algorithm, ACM Trans. on Mathematical
  150. Software, 30(3), 2004, pp. 353--376. :doi:`10.1145/1024074.1024079`
  151. .. [3] T. A. Davis, Algorithm 832: UMFPACK - an unsymmetric-pattern
  152. multifrontal method with a column pre-ordering strategy, ACM
  153. Trans. on Mathematical Software, 30(2), 2004, pp. 196--199.
  154. https://dl.acm.org/doi/abs/10.1145/992200.992206
  155. .. [4] T. A. Davis, A column pre-ordering strategy for the
  156. unsymmetric-pattern multifrontal method, ACM Trans.
  157. on Mathematical Software, 30(2), 2004, pp. 165--195.
  158. https://dl.acm.org/doi/abs/10.1145/992200.992205
  159. .. [5] T. A. Davis and I. S. Duff, A combined unifrontal/multifrontal
  160. method for unsymmetric sparse matrices, ACM Trans. on
  161. Mathematical Software, 25(1), 1999, pp. 1--19.
  162. https://doi.org/10.1145/305658.287640
  163. .. [6] T. A. Davis and I. S. Duff, An unsymmetric-pattern multifrontal
  164. method for sparse LU factorization, SIAM J. Matrix Analysis and
  165. Computations, 18(1), 1997, pp. 140--158.
  166. https://doi.org/10.1137/S0895479894246905T.
  167. Examples
  168. --------
  169. >>> import numpy as np
  170. >>> from scipy.sparse import csc_array
  171. >>> from scipy.sparse.linalg import spsolve
  172. >>> A = csc_array([[3, 2, 0], [1, -1, 0], [0, 5, 1]], dtype=float)
  173. >>> B = csc_array([[2, 0], [-1, 0], [2, 0]], dtype=float)
  174. >>> x = spsolve(A, B)
  175. >>> np.allclose(A.dot(x).toarray(), B.toarray())
  176. True
  177. """
  178. is_pydata_sparse = is_pydata_spmatrix(b)
  179. pydata_sparse_cls = b.__class__ if is_pydata_sparse else None
  180. A = convert_pydata_sparse_to_scipy(A)
  181. b = convert_pydata_sparse_to_scipy(b)
  182. if not (issparse(A) and A.format in ("csc", "csr")):
  183. A = csc_array(A)
  184. warn('spsolve requires A be CSC or CSR matrix format',
  185. SparseEfficiencyWarning, stacklevel=2)
  186. # b is a vector only if b have shape (n,) or (n, 1)
  187. b_is_sparse = issparse(b)
  188. if not b_is_sparse:
  189. b = asarray(b)
  190. b_is_vector = ((b.ndim == 1) or (b.ndim == 2 and b.shape[1] == 1))
  191. # sum duplicates for non-canonical format
  192. A.sum_duplicates()
  193. A = A._asfptype() # upcast to a floating point format
  194. result_dtype = np.promote_types(A.dtype, b.dtype)
  195. if A.dtype != result_dtype:
  196. A = A.astype(result_dtype)
  197. if b.dtype != result_dtype:
  198. b = b.astype(result_dtype)
  199. # validate input shapes
  200. M, N = A.shape
  201. if (M != N):
  202. raise ValueError(f"matrix must be square (has shape {(M, N)})")
  203. if M != b.shape[0]:
  204. raise ValueError(f"matrix - rhs dimension mismatch ({A.shape} - {b.shape[0]})")
  205. if not hasattr(useUmfpack, 'u'):
  206. useUmfpack.u = not noScikit
  207. use_umfpack = use_umfpack and useUmfpack.u
  208. if b_is_vector and use_umfpack:
  209. if b_is_sparse:
  210. b_vec = b.toarray()
  211. else:
  212. b_vec = b
  213. b_vec = asarray(b_vec, dtype=A.dtype).ravel()
  214. if noScikit:
  215. raise RuntimeError('Scikits.umfpack not installed.')
  216. if A.dtype.char not in 'dD':
  217. raise ValueError("convert matrix data to double, please, using"
  218. " .astype(), or set linsolve.useUmfpack.u = False")
  219. umf_family, A = _get_umf_family(A)
  220. umf = umfpack.UmfpackContext(umf_family)
  221. x = umf.linsolve(umfpack.UMFPACK_A, A, b_vec,
  222. autoTranspose=True)
  223. else:
  224. if b_is_vector and b_is_sparse:
  225. b = b.toarray()
  226. b_is_sparse = False
  227. if not b_is_sparse:
  228. if A.format == "csc":
  229. flag = 1 # CSC format
  230. else:
  231. flag = 0 # CSR format
  232. indices = A.indices.astype(np.intc, copy=False)
  233. indptr = A.indptr.astype(np.intc, copy=False)
  234. options = dict(ColPerm=permc_spec)
  235. x, info = _superlu.gssv(N, A.nnz, A.data, indices, indptr,
  236. b, flag, options=options)
  237. if info != 0:
  238. warn("Matrix is exactly singular", MatrixRankWarning, stacklevel=2)
  239. x.fill(np.nan)
  240. if b_is_vector:
  241. x = x.ravel()
  242. else:
  243. # b is sparse
  244. Afactsolve = factorized(A)
  245. if not (b.format == "csc" or is_pydata_spmatrix(b)):
  246. warn('spsolve is more efficient when sparse b '
  247. 'is in the CSC matrix format',
  248. SparseEfficiencyWarning, stacklevel=2)
  249. b = csc_array(b)
  250. # Create a sparse output matrix by repeatedly applying
  251. # the sparse factorization to solve columns of b.
  252. data_segs = []
  253. row_segs = []
  254. col_segs = []
  255. for j in range(b.shape[1]):
  256. bj = b[:, j].toarray().ravel()
  257. xj = Afactsolve(bj)
  258. w = np.flatnonzero(xj)
  259. segment_length = w.shape[0]
  260. row_segs.append(w)
  261. col_segs.append(np.full(segment_length, j, dtype=int))
  262. data_segs.append(np.asarray(xj[w], dtype=A.dtype))
  263. sparse_data = np.concatenate(data_segs)
  264. idx_dtype = get_index_dtype(maxval=max(b.shape))
  265. sparse_row = np.concatenate(row_segs, dtype=idx_dtype)
  266. sparse_col = np.concatenate(col_segs, dtype=idx_dtype)
  267. x = A.__class__((sparse_data, (sparse_row, sparse_col)),
  268. shape=b.shape, dtype=A.dtype)
  269. if is_pydata_sparse:
  270. x = pydata_sparse_cls.from_scipy_sparse(x)
  271. return x
  272. def splu(A, permc_spec=None, diag_pivot_thresh=None,
  273. relax=None, panel_size=None, options=None):
  274. """
  275. Compute the LU decomposition of a sparse, square matrix.
  276. Parameters
  277. ----------
  278. A : sparse array or matrix
  279. Sparse array to factorize. Most efficient when provided in CSC
  280. format. Other formats will be converted to CSC before factorization.
  281. permc_spec : str, optional
  282. How to permute the columns of the matrix for sparsity preservation.
  283. (default: 'COLAMD')
  284. - ``NATURAL``: natural ordering.
  285. - ``MMD_ATA``: minimum degree ordering on the structure of A^T A.
  286. - ``MMD_AT_PLUS_A``: minimum degree ordering on the structure of A^T+A.
  287. - ``COLAMD``: approximate minimum degree column ordering
  288. diag_pivot_thresh : float, optional
  289. Threshold used for a diagonal entry to be an acceptable pivot.
  290. See SuperLU user's guide for details [1]_
  291. relax : int, optional
  292. Expert option for customizing the degree of relaxing supernodes.
  293. See SuperLU user's guide for details [1]_
  294. panel_size : int, optional
  295. Expert option for customizing the panel size.
  296. See SuperLU user's guide for details [1]_
  297. options : dict, optional
  298. Dictionary containing additional expert options to SuperLU.
  299. See SuperLU user guide [1]_ (section 2.4 on the 'Options' argument)
  300. for more details. For example, you can specify
  301. ``options=dict(Equil=False, IterRefine='SINGLE'))``
  302. to turn equilibration off and perform a single iterative refinement.
  303. Returns
  304. -------
  305. invA : scipy.sparse.linalg.SuperLU
  306. Object, which has a ``solve`` method.
  307. See also
  308. --------
  309. spilu : incomplete LU decomposition
  310. Notes
  311. -----
  312. When a real array is factorized and the returned SuperLU object's ``solve()``
  313. method is used with complex arguments an error is generated. Instead, cast the
  314. initial array to complex and then factorize.
  315. This function uses the SuperLU library.
  316. References
  317. ----------
  318. .. [1] SuperLU https://portal.nersc.gov/project/sparse/superlu/
  319. Examples
  320. --------
  321. >>> import numpy as np
  322. >>> from scipy.sparse import csc_array
  323. >>> from scipy.sparse.linalg import splu
  324. >>> A = csc_array([[1., 0., 0.], [5., 0., 2.], [0., -1., 0.]], dtype=float)
  325. >>> B = splu(A)
  326. >>> x = np.array([1., 2., 3.], dtype=float)
  327. >>> B.solve(x)
  328. array([ 1. , -3. , -1.5])
  329. >>> A.dot(B.solve(x))
  330. array([ 1., 2., 3.])
  331. >>> B.solve(A.dot(x))
  332. array([ 1., 2., 3.])
  333. """
  334. if is_pydata_spmatrix(A):
  335. A_cls = type(A)
  336. def csc_construct_func(*a, cls=A_cls):
  337. return cls.from_scipy_sparse(csc_array(*a))
  338. A = A.to_scipy_sparse().tocsc()
  339. else:
  340. csc_construct_func = csc_array
  341. if not (issparse(A) and A.format == "csc"):
  342. A = csc_array(A)
  343. warn('splu converted its input to CSC format',
  344. SparseEfficiencyWarning, stacklevel=2)
  345. # sum duplicates for non-canonical format
  346. A.sum_duplicates()
  347. A = A._asfptype() # upcast to a floating point format
  348. M, N = A.shape
  349. if (M != N):
  350. raise ValueError("can only factor square matrices") # is this true?
  351. indices, indptr = safely_cast_index_arrays(A, np.intc, "SuperLU")
  352. _options = dict(DiagPivotThresh=diag_pivot_thresh, ColPerm=permc_spec,
  353. PanelSize=panel_size, Relax=relax)
  354. if options is not None:
  355. _options.update(options)
  356. # Ensure that no column permutations are applied
  357. if (_options["ColPerm"] == "NATURAL"):
  358. _options["SymmetricMode"] = True
  359. return _superlu.gstrf(N, A.nnz, A.data, indices, indptr,
  360. csc_construct_func=csc_construct_func,
  361. ilu=False, options=_options)
  362. def spilu(A, drop_tol=None, fill_factor=None, drop_rule=None, permc_spec=None,
  363. diag_pivot_thresh=None, relax=None, panel_size=None, options=None):
  364. """
  365. Compute an incomplete LU decomposition for a sparse, square matrix.
  366. The resulting object is an approximation to the inverse of `A`.
  367. Parameters
  368. ----------
  369. A : (N, N) array_like
  370. Sparse array to factorize. Most efficient when provided in CSC format.
  371. Other formats will be converted to CSC before factorization.
  372. drop_tol : float, optional
  373. Drop tolerance (0 <= tol <= 1) for an incomplete LU decomposition.
  374. (default: 1e-4)
  375. fill_factor : float, optional
  376. Specifies the fill ratio upper bound (>= 1.0) for ILU. (default: 10)
  377. drop_rule : str, optional
  378. Comma-separated string of drop rules to use.
  379. Available rules: ``basic``, ``prows``, ``column``, ``area``,
  380. ``secondary``, ``dynamic``, ``interp``. (Default: ``basic,area``)
  381. See SuperLU documentation for details.
  382. Remaining other options
  383. Same as for `splu`
  384. Returns
  385. -------
  386. invA_approx : scipy.sparse.linalg.SuperLU
  387. Object, which has a ``solve`` method.
  388. See also
  389. --------
  390. splu : complete LU decomposition
  391. Notes
  392. -----
  393. When a real array is factorized and the returned SuperLU object's ``solve()`` method
  394. is used with complex arguments an error is generated. Instead, cast the initial
  395. array to complex and then factorize.
  396. To improve the better approximation to the inverse, you may need to
  397. increase `fill_factor` AND decrease `drop_tol`.
  398. This function uses the SuperLU library.
  399. Examples
  400. --------
  401. >>> import numpy as np
  402. >>> from scipy.sparse import csc_array
  403. >>> from scipy.sparse.linalg import spilu
  404. >>> A = csc_array([[1., 0., 0.], [5., 0., 2.], [0., -1., 0.]], dtype=float)
  405. >>> B = spilu(A)
  406. >>> x = np.array([1., 2., 3.], dtype=float)
  407. >>> B.solve(x)
  408. array([ 1. , -3. , -1.5])
  409. >>> A.dot(B.solve(x))
  410. array([ 1., 2., 3.])
  411. >>> B.solve(A.dot(x))
  412. array([ 1., 2., 3.])
  413. """
  414. if is_pydata_spmatrix(A):
  415. A_cls = type(A)
  416. def csc_construct_func(*a, cls=A_cls):
  417. return cls.from_scipy_sparse(csc_array(*a))
  418. A = A.to_scipy_sparse().tocsc()
  419. else:
  420. csc_construct_func = csc_array
  421. if not (issparse(A) and A.format == "csc"):
  422. A = csc_array(A)
  423. warn('spilu converted its input to CSC format',
  424. SparseEfficiencyWarning, stacklevel=2)
  425. # sum duplicates for non-canonical format
  426. A.sum_duplicates()
  427. A = A._asfptype() # upcast to a floating point format
  428. M, N = A.shape
  429. if (M != N):
  430. raise ValueError("can only factor square matrices") # is this true?
  431. indices, indptr = safely_cast_index_arrays(A, np.intc, "SuperLU")
  432. _options = dict(ILU_DropRule=drop_rule, ILU_DropTol=drop_tol,
  433. ILU_FillFactor=fill_factor,
  434. DiagPivotThresh=diag_pivot_thresh, ColPerm=permc_spec,
  435. PanelSize=panel_size, Relax=relax)
  436. if options is not None:
  437. _options.update(options)
  438. # Ensure that no column permutations are applied
  439. if (_options["ColPerm"] == "NATURAL"):
  440. _options["SymmetricMode"] = True
  441. return _superlu.gstrf(N, A.nnz, A.data, indices, indptr,
  442. csc_construct_func=csc_construct_func,
  443. ilu=True, options=_options)
  444. def factorized(A):
  445. """
  446. Return a function for solving a sparse linear system, with A pre-factorized.
  447. Parameters
  448. ----------
  449. A : (N, N) array_like
  450. Input. A in CSC format is most efficient. A CSR format matrix will
  451. be converted to CSC before factorization.
  452. Returns
  453. -------
  454. solve : callable
  455. To solve the linear system of equations given in `A`, the `solve`
  456. callable should be passed an ndarray of shape (N,).
  457. Examples
  458. --------
  459. >>> import numpy as np
  460. >>> from scipy.sparse.linalg import factorized
  461. >>> from scipy.sparse import csc_array
  462. >>> A = np.array([[ 3. , 2. , -1. ],
  463. ... [ 2. , -2. , 4. ],
  464. ... [-1. , 0.5, -1. ]])
  465. >>> solve = factorized(csc_array(A)) # Makes LU decomposition.
  466. >>> rhs1 = np.array([1, -2, 0])
  467. >>> solve(rhs1) # Uses the LU factors.
  468. array([ 1., -2., -2.])
  469. """
  470. if is_pydata_spmatrix(A):
  471. A = A.to_scipy_sparse().tocsc()
  472. if not hasattr(useUmfpack, 'u'):
  473. useUmfpack.u = not noScikit
  474. if useUmfpack.u:
  475. if noScikit:
  476. raise RuntimeError('Scikits.umfpack not installed.')
  477. if not (issparse(A) and A.format == "csc"):
  478. A = csc_array(A)
  479. warn('splu converted its input to CSC format',
  480. SparseEfficiencyWarning, stacklevel=2)
  481. A = A._asfptype() # upcast to a floating point format
  482. if A.dtype.char not in 'dD':
  483. raise ValueError("convert matrix data to double, please, using"
  484. " .astype(), or set linsolve.useUmfpack.u = False")
  485. umf_family, A = _get_umf_family(A)
  486. umf = umfpack.UmfpackContext(umf_family)
  487. # Make LU decomposition.
  488. umf.numeric(A)
  489. def solve(b):
  490. with np.errstate(divide="ignore", invalid="ignore"):
  491. # Ignoring warnings with numpy >= 1.23.0, see gh-16523
  492. result = umf.solve(umfpack.UMFPACK_A, A, b, autoTranspose=True)
  493. return result
  494. return solve
  495. else:
  496. return splu(A).solve
  497. def spsolve_triangular(A, b, lower=True, overwrite_A=False, overwrite_b=False,
  498. unit_diagonal=False):
  499. """
  500. Solve the equation ``A x = b`` for `x`, assuming A is a triangular matrix.
  501. Parameters
  502. ----------
  503. A : (M, M) sparse array or matrix
  504. A sparse square triangular matrix. Should be in CSR or CSC format.
  505. b : (M,) or (M, N) array_like
  506. Right-hand side matrix in ``A x = b``
  507. lower : bool, optional
  508. Whether `A` is a lower or upper triangular matrix.
  509. Default is lower triangular matrix.
  510. overwrite_A : bool, optional
  511. Allow changing `A`.
  512. Enabling gives a performance gain. Default is False.
  513. overwrite_b : bool, optional
  514. Allow overwriting data in `b`.
  515. Enabling gives a performance gain. Default is False.
  516. If `overwrite_b` is True, it should be ensured that
  517. `b` has an appropriate dtype to be able to store the result.
  518. unit_diagonal : bool, optional
  519. If True, diagonal elements of `a` are assumed to be 1.
  520. .. versionadded:: 1.4.0
  521. Returns
  522. -------
  523. x : (M,) or (M, N) ndarray
  524. Solution to the system ``A x = b``. Shape of return matches shape
  525. of `b`.
  526. Raises
  527. ------
  528. LinAlgError
  529. If `A` is singular or not triangular.
  530. ValueError
  531. If shape of `A` or shape of `b` do not match the requirements.
  532. Notes
  533. -----
  534. .. versionadded:: 0.19.0
  535. Examples
  536. --------
  537. >>> import numpy as np
  538. >>> from scipy.sparse import csc_array
  539. >>> from scipy.sparse.linalg import spsolve_triangular
  540. >>> A = csc_array([[3, 0, 0], [1, -1, 0], [2, 0, 1]], dtype=float)
  541. >>> B = np.array([[2, 0], [-1, 0], [2, 0]], dtype=float)
  542. >>> x = spsolve_triangular(A, B)
  543. >>> np.allclose(A.dot(x), B)
  544. True
  545. """
  546. if is_pydata_spmatrix(A):
  547. A = A.to_scipy_sparse().tocsc()
  548. trans = "N"
  549. if issparse(A) and A.format == "csr":
  550. A = A.T
  551. trans = "T"
  552. lower = not lower
  553. if not (issparse(A) and A.format == "csc"):
  554. warn('CSC or CSR matrix format is required. Converting to CSC matrix.',
  555. SparseEfficiencyWarning, stacklevel=2)
  556. A = csc_array(A)
  557. elif not overwrite_A:
  558. A = A.copy()
  559. M, N = A.shape
  560. if M != N:
  561. raise ValueError(
  562. f'A must be a square matrix but its shape is {A.shape}.')
  563. if unit_diagonal:
  564. with catch_warnings():
  565. simplefilter('ignore', SparseEfficiencyWarning)
  566. A.setdiag(1)
  567. else:
  568. diag = A.diagonal()
  569. if np.any(diag == 0):
  570. raise LinAlgError(
  571. 'A is singular: zero entry on diagonal.')
  572. invdiag = 1/diag
  573. if trans == "N":
  574. A = A @ diags_array(invdiag)
  575. else:
  576. A = (A.T @ diags_array(invdiag)).T
  577. # sum duplicates for non-canonical format
  578. A.sum_duplicates()
  579. b = np.asanyarray(b)
  580. if b.ndim not in [1, 2]:
  581. raise ValueError(
  582. f'b must have 1 or 2 dims but its shape is {b.shape}.')
  583. if M != b.shape[0]:
  584. raise ValueError(
  585. 'The size of the dimensions of A must be equal to '
  586. 'the size of the first dimension of b but the shape of A is '
  587. f'{A.shape} and the shape of b is {b.shape}.'
  588. )
  589. result_dtype = np.promote_types(np.promote_types(A.dtype, np.float32), b.dtype)
  590. if A.dtype != result_dtype:
  591. A = A.astype(result_dtype)
  592. if b.dtype != result_dtype:
  593. b = b.astype(result_dtype)
  594. elif not overwrite_b:
  595. b = b.copy()
  596. if lower:
  597. L = A
  598. U = csc_array((N, N), dtype=result_dtype)
  599. else:
  600. L = eye_array(N, dtype=result_dtype, format='csc')
  601. U = A
  602. U.setdiag(0)
  603. L_indices, L_indptr = safely_cast_index_arrays(L, np.intc, "SuperLU")
  604. U_indices, U_indptr = safely_cast_index_arrays(U, np.intc, "SuperLU")
  605. x, info = _superlu.gstrs(trans,
  606. N, L.nnz, L.data, L_indices, L_indptr,
  607. N, U.nnz, U.data, U_indices, U_indptr,
  608. b)
  609. if info:
  610. raise LinAlgError('A is singular.')
  611. if not unit_diagonal:
  612. invdiag = invdiag.reshape(-1, *([1] * (len(x.shape) - 1)))
  613. x = x * invdiag
  614. return x
  615. def is_sptriangular(A):
  616. """Returns 2-tuple indicating lower/upper triangular structure for sparse ``A``
  617. Checks for triangular structure in ``A``. The result is summarized in
  618. two boolean values ``lower`` and ``upper`` to designate whether ``A`` is
  619. lower triangular or upper triangular respectively. Diagonal ``A`` will
  620. result in both being True. Non-triangular structure results in False for both.
  621. Only the sparse structure is used here. Values are not checked for zeros.
  622. This function will convert a copy of ``A`` to CSC format if it is not already
  623. CSR or CSC format. So it may be more efficient to convert it yourself if you
  624. have other uses for the CSR/CSC version.
  625. If ``A`` is not square, the portions outside the upper left square of the
  626. matrix do not affect its triangular structure. You probably want to work
  627. with the square portion of the matrix, though it is not requred here.
  628. Parameters
  629. ----------
  630. A : SciPy sparse array or matrix
  631. A sparse matrix preferrably in CSR or CSC format.
  632. Returns
  633. -------
  634. lower, upper : 2-tuple of bool
  635. .. versionadded:: 1.15.0
  636. Examples
  637. --------
  638. >>> import numpy as np
  639. >>> from scipy.sparse import csc_array, eye_array
  640. >>> from scipy.sparse.linalg import is_sptriangular
  641. >>> A = csc_array([[3, 0, 0], [1, -1, 0], [2, 0, 1]], dtype=float)
  642. >>> is_sptriangular(A)
  643. (True, False)
  644. >>> D = eye_array(3, format='csr')
  645. >>> is_sptriangular(D)
  646. (True, True)
  647. """
  648. if not (issparse(A) and A.format in ("csc", "csr", "coo", "dia", "dok", "lil")):
  649. warn('is_sptriangular needs sparse and not BSR format. Converting to CSR.',
  650. SparseEfficiencyWarning, stacklevel=2)
  651. A = csr_array(A)
  652. # bsr is better off converting to csr
  653. if A.format == "dia":
  654. return A.offsets.max() <= 0, A.offsets.min() >= 0
  655. elif A.format == "coo":
  656. rows, cols = A.coords
  657. return (cols <= rows).all(), (cols >= rows).all()
  658. elif A.format == "dok":
  659. return all(c <= r for r, c in A.keys()), all(c >= r for r, c in A.keys())
  660. elif A.format == "lil":
  661. lower = all(col <= row for row, cols in enumerate(A.rows) for col in cols)
  662. upper = all(col >= row for row, cols in enumerate(A.rows) for col in cols)
  663. return lower, upper
  664. # format in ("csc", "csr")
  665. indptr, indices = A.indptr, A.indices
  666. N = len(indptr) - 1
  667. lower, upper = True, True
  668. # check middle, 1st, last col (treat as CSC and switch at end if CSR)
  669. for col in [N // 2, 0, -1]:
  670. rows = indices[indptr[col]:indptr[col + 1]]
  671. upper = upper and (col >= rows).all()
  672. lower = lower and (col <= rows).all()
  673. if not upper and not lower:
  674. return False, False
  675. # check all cols
  676. cols = np.repeat(np.arange(N), np.diff(indptr))
  677. rows = indices
  678. upper = upper and (cols >= rows).all()
  679. lower = lower and (cols <= rows).all()
  680. if A.format == 'csr':
  681. return upper, lower
  682. return lower, upper
  683. def spbandwidth(A):
  684. """Return the lower and upper bandwidth of a 2D numeric array.
  685. Computes the lower and upper limits on the bandwidth of the
  686. sparse 2D array ``A``. The result is summarized as a 2-tuple
  687. of positive integers ``(lo, hi)``. A zero denotes no sub/super
  688. diagonal entries on that side (triangular). The maximum value
  689. for ``lo`` (``hi``) is one less than the number of rows(cols).
  690. Only the sparse structure is used here. Values are not checked for zeros.
  691. Parameters
  692. ----------
  693. A : SciPy sparse array or matrix
  694. A sparse matrix preferrably in CSR or CSC format.
  695. Returns
  696. -------
  697. below, above : 2-tuple of int
  698. The distance to the farthest non-zero diagonal below/above the
  699. main diagonal.
  700. .. versionadded:: 1.15.0
  701. Examples
  702. --------
  703. >>> import numpy as np
  704. >>> from scipy.sparse.linalg import spbandwidth
  705. >>> from scipy.sparse import csc_array, eye_array
  706. >>> A = csc_array([[3, 0, 0], [1, -1, 0], [2, 0, 1]], dtype=float)
  707. >>> spbandwidth(A)
  708. (2, 0)
  709. >>> D = eye_array(3, format='csr')
  710. >>> spbandwidth(D)
  711. (0, 0)
  712. """
  713. if not (issparse(A) and A.format in ("csc", "csr", "coo", "dia", "dok")):
  714. warn('spbandwidth needs sparse format not LIL and BSR. Converting to CSR.',
  715. SparseEfficiencyWarning, stacklevel=2)
  716. A = csr_array(A)
  717. # bsr and lil are better off converting to csr
  718. if A.format == "dia":
  719. return max(0, -A.offsets.min().item()), max(0, A.offsets.max().item())
  720. if A.format in ("csc", "csr"):
  721. indptr, indices = A.indptr, A.indices
  722. N = len(indptr) - 1
  723. gap = np.repeat(np.arange(N), np.diff(indptr)) - indices
  724. if A.format == 'csr':
  725. gap = -gap
  726. elif A.format == "coo":
  727. gap = A.coords[1] - A.coords[0]
  728. elif A.format == "dok":
  729. gap = [(c - r) for r, c in A.keys()] + [0]
  730. return -min(gap), max(gap)
  731. return max(-np.min(gap).item(), 0), max(np.max(gap).item(), 0)