_matfuncs.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940
  1. """
  2. Sparse matrix functions
  3. """
  4. #
  5. # Authors: Travis Oliphant, March 2002
  6. # Anthony Scopatz, August 2012 (Sparse Updates)
  7. # Jake Vanderplas, August 2012 (Sparse Updates)
  8. #
  9. __all__ = ['expm', 'inv', 'matrix_power']
  10. import numpy as np
  11. from scipy.linalg._basic import solve, solve_triangular
  12. from scipy.sparse._base import issparse
  13. from scipy.sparse.linalg import spsolve
  14. from scipy.sparse._sputils import is_pydata_spmatrix, isintlike
  15. import scipy.sparse
  16. import scipy.sparse.linalg
  17. from scipy.sparse.linalg._interface import LinearOperator
  18. from scipy.sparse._construct import eye_array
  19. from ._expm_multiply import _ident_like, _exact_1_norm as _onenorm
  20. UPPER_TRIANGULAR = 'upper_triangular'
  21. def inv(A):
  22. """
  23. Compute the inverse of a sparse arrays
  24. Parameters
  25. ----------
  26. A : (M, M) sparse arrays
  27. square matrix to be inverted
  28. Returns
  29. -------
  30. Ainv : (M, M) sparse arrays
  31. inverse of `A`
  32. Notes
  33. -----
  34. This computes the sparse inverse of `A`. If the inverse of `A` is expected
  35. to be non-sparse, it will likely be faster to convert `A` to dense and use
  36. `scipy.linalg.inv`.
  37. Examples
  38. --------
  39. >>> from scipy.sparse import csc_array
  40. >>> from scipy.sparse.linalg import inv
  41. >>> A = csc_array([[1., 0.], [1., 2.]])
  42. >>> Ainv = inv(A)
  43. >>> Ainv
  44. <Compressed Sparse Column sparse array of dtype 'float64'
  45. with 3 stored elements and shape (2, 2)>
  46. >>> A.dot(Ainv)
  47. <Compressed Sparse Column sparse array of dtype 'float64'
  48. with 2 stored elements and shape (2, 2)>
  49. >>> A.dot(Ainv).toarray()
  50. array([[ 1., 0.],
  51. [ 0., 1.]])
  52. .. versionadded:: 0.12.0
  53. """
  54. # Check input
  55. if not (issparse(A) or is_pydata_spmatrix(A)):
  56. raise TypeError('Input must be a sparse arrays')
  57. # Use sparse direct solver to solve "AX = I" accurately
  58. I = _ident_like(A)
  59. Ainv = spsolve(A, I)
  60. return Ainv
  61. def _onenorm_matrix_power_nnm(A, p):
  62. """
  63. Compute the 1-norm of a non-negative integer power of a non-negative matrix.
  64. Parameters
  65. ----------
  66. A : a square ndarray or matrix or sparse arrays
  67. Input matrix with non-negative entries.
  68. p : non-negative integer
  69. The power to which the matrix is to be raised.
  70. Returns
  71. -------
  72. out : float
  73. The 1-norm of the matrix power p of A.
  74. """
  75. # Check input
  76. if int(p) != p or p < 0:
  77. raise ValueError('expected non-negative integer p')
  78. p = int(p)
  79. if len(A.shape) != 2 or A.shape[0] != A.shape[1]:
  80. raise ValueError('expected A to be like a square matrix')
  81. # Explicitly make a column vector so that this works when A is a
  82. # numpy matrix (in addition to ndarray and sparse arrays).
  83. v = np.ones((A.shape[0], 1), dtype=float)
  84. M = A.T
  85. for i in range(p):
  86. v = M.dot(v)
  87. return np.max(v)
  88. def _is_upper_triangular(A):
  89. # This function could possibly be of wider interest.
  90. if issparse(A):
  91. lower_part = scipy.sparse.tril(A, -1)
  92. # Check structural upper triangularity,
  93. # then coincidental upper triangularity if needed.
  94. return lower_part.nnz == 0 or lower_part.count_nonzero() == 0
  95. elif is_pydata_spmatrix(A):
  96. import sparse
  97. lower_part = sparse.tril(A, -1)
  98. return lower_part.nnz == 0
  99. else:
  100. return not np.tril(A, -1).any()
  101. def _smart_matrix_product(A, B, alpha=None, structure=None):
  102. """
  103. A matrix product that knows about sparse and structured matrices.
  104. Parameters
  105. ----------
  106. A : 2d ndarray
  107. First matrix.
  108. B : 2d ndarray
  109. Second matrix.
  110. alpha : float
  111. The matrix product will be scaled by this constant.
  112. structure : str, optional
  113. A string describing the structure of both matrices `A` and `B`.
  114. Only `upper_triangular` is currently supported.
  115. Returns
  116. -------
  117. M : 2d ndarray
  118. Matrix product of A and B.
  119. """
  120. if len(A.shape) != 2:
  121. raise ValueError('expected A to be a rectangular matrix')
  122. if len(B.shape) != 2:
  123. raise ValueError('expected B to be a rectangular matrix')
  124. f = None
  125. if structure == UPPER_TRIANGULAR:
  126. if (not issparse(A) and not issparse(B)
  127. and not is_pydata_spmatrix(A) and not is_pydata_spmatrix(B)):
  128. f, = scipy.linalg.get_blas_funcs(('trmm',), (A, B))
  129. if f is not None:
  130. if alpha is None:
  131. alpha = 1.
  132. out = f(alpha, A, B)
  133. else:
  134. if alpha is None:
  135. out = A.dot(B)
  136. else:
  137. out = alpha * A.dot(B)
  138. return out
  139. class MatrixPowerOperator(LinearOperator):
  140. def __init__(self, A, p, structure=None):
  141. if A.ndim != 2 or A.shape[0] != A.shape[1]:
  142. raise ValueError('expected A to be like a square matrix')
  143. if p < 0:
  144. raise ValueError('expected p to be a non-negative integer')
  145. self._A = A
  146. self._p = p
  147. self._structure = structure
  148. self.dtype = A.dtype
  149. self.ndim = A.ndim
  150. self.shape = A.shape
  151. def _matvec(self, x):
  152. for i in range(self._p):
  153. x = self._A.dot(x)
  154. return x
  155. def _rmatvec(self, x):
  156. A_T = self._A.T
  157. x = x.ravel()
  158. for i in range(self._p):
  159. x = A_T.dot(x)
  160. return x
  161. def _matmat(self, X):
  162. for i in range(self._p):
  163. X = _smart_matrix_product(self._A, X, structure=self._structure)
  164. return X
  165. @property
  166. def T(self):
  167. return MatrixPowerOperator(self._A.T, self._p)
  168. class ProductOperator(LinearOperator):
  169. """
  170. For now, this is limited to products of multiple square matrices.
  171. """
  172. def __init__(self, *args, **kwargs):
  173. self._structure = kwargs.get('structure', None)
  174. for A in args:
  175. if len(A.shape) != 2 or A.shape[0] != A.shape[1]:
  176. raise ValueError(
  177. 'For now, the ProductOperator implementation is '
  178. 'limited to the product of multiple square matrices.')
  179. if args:
  180. n = args[0].shape[0]
  181. for A in args:
  182. for d in A.shape:
  183. if d != n:
  184. raise ValueError(
  185. 'The square matrices of the ProductOperator '
  186. 'must all have the same shape.')
  187. self.shape = (n, n)
  188. self.ndim = len(self.shape)
  189. self.dtype = np.result_type(*[x.dtype for x in args])
  190. self._operator_sequence = args
  191. def _matvec(self, x):
  192. for A in reversed(self._operator_sequence):
  193. x = A.dot(x)
  194. return x
  195. def _rmatvec(self, x):
  196. x = x.ravel()
  197. for A in self._operator_sequence:
  198. x = A.T.dot(x)
  199. return x
  200. def _matmat(self, X):
  201. for A in reversed(self._operator_sequence):
  202. X = _smart_matrix_product(A, X, structure=self._structure)
  203. return X
  204. @property
  205. def T(self):
  206. T_args = [A.T for A in reversed(self._operator_sequence)]
  207. return ProductOperator(*T_args)
  208. def _onenormest_matrix_power(A, p,
  209. t=2, itmax=5, compute_v=False, compute_w=False, structure=None):
  210. """
  211. Efficiently estimate the 1-norm of A^p.
  212. Parameters
  213. ----------
  214. A : ndarray
  215. Matrix whose 1-norm of a power is to be computed.
  216. p : int
  217. Non-negative integer power.
  218. t : int, optional
  219. A positive parameter controlling the tradeoff between
  220. accuracy versus time and memory usage.
  221. Larger values take longer and use more memory
  222. but give more accurate output.
  223. itmax : int, optional
  224. Use at most this many iterations.
  225. compute_v : bool, optional
  226. Request a norm-maximizing linear operator input vector if True.
  227. compute_w : bool, optional
  228. Request a norm-maximizing linear operator output vector if True.
  229. Returns
  230. -------
  231. est : float
  232. An underestimate of the 1-norm of the sparse arrays.
  233. v : ndarray, optional
  234. The vector such that ||Av||_1 == est*||v||_1.
  235. It can be thought of as an input to the linear operator
  236. that gives an output with particularly large norm.
  237. w : ndarray, optional
  238. The vector Av which has relatively large 1-norm.
  239. It can be thought of as an output of the linear operator
  240. that is relatively large in norm compared to the input.
  241. """
  242. return scipy.sparse.linalg.onenormest(
  243. MatrixPowerOperator(A, p, structure=structure))
  244. def _onenormest_product(operator_seq,
  245. t=2, itmax=5, compute_v=False, compute_w=False, structure=None):
  246. """
  247. Efficiently estimate the 1-norm of the matrix product of the args.
  248. Parameters
  249. ----------
  250. operator_seq : linear operator sequence
  251. Matrices whose 1-norm of product is to be computed.
  252. t : int, optional
  253. A positive parameter controlling the tradeoff between
  254. accuracy versus time and memory usage.
  255. Larger values take longer and use more memory
  256. but give more accurate output.
  257. itmax : int, optional
  258. Use at most this many iterations.
  259. compute_v : bool, optional
  260. Request a norm-maximizing linear operator input vector if True.
  261. compute_w : bool, optional
  262. Request a norm-maximizing linear operator output vector if True.
  263. structure : str, optional
  264. A string describing the structure of all operators.
  265. Only `upper_triangular` is currently supported.
  266. Returns
  267. -------
  268. est : float
  269. An underestimate of the 1-norm of the sparse arrays.
  270. v : ndarray, optional
  271. The vector such that ||Av||_1 == est*||v||_1.
  272. It can be thought of as an input to the linear operator
  273. that gives an output with particularly large norm.
  274. w : ndarray, optional
  275. The vector Av which has relatively large 1-norm.
  276. It can be thought of as an output of the linear operator
  277. that is relatively large in norm compared to the input.
  278. """
  279. return scipy.sparse.linalg.onenormest(
  280. ProductOperator(*operator_seq, structure=structure))
  281. class _ExpmPadeHelper:
  282. """
  283. Help lazily evaluate a matrix exponential.
  284. The idea is to not do more work than we need for high expm precision,
  285. so we lazily compute matrix powers and store or precompute
  286. other properties of the matrix.
  287. """
  288. def __init__(self, A, structure=None, use_exact_onenorm=False):
  289. """
  290. Initialize the object.
  291. Parameters
  292. ----------
  293. A : a dense or sparse square numpy matrix or ndarray
  294. The matrix to be exponentiated.
  295. structure : str, optional
  296. A string describing the structure of matrix `A`.
  297. Only `upper_triangular` is currently supported.
  298. use_exact_onenorm : bool, optional
  299. If True then only the exact one-norm of matrix powers and products
  300. will be used. Otherwise, the one-norm of powers and products
  301. may initially be estimated.
  302. """
  303. self.A = A
  304. self._A2 = None
  305. self._A4 = None
  306. self._A6 = None
  307. self._A8 = None
  308. self._A10 = None
  309. self._d4_exact = None
  310. self._d6_exact = None
  311. self._d8_exact = None
  312. self._d10_exact = None
  313. self._d4_approx = None
  314. self._d6_approx = None
  315. self._d8_approx = None
  316. self._d10_approx = None
  317. self.ident = _ident_like(A)
  318. self.structure = structure
  319. self.use_exact_onenorm = use_exact_onenorm
  320. @property
  321. def A2(self):
  322. if self._A2 is None:
  323. self._A2 = _smart_matrix_product(
  324. self.A, self.A, structure=self.structure)
  325. return self._A2
  326. @property
  327. def A4(self):
  328. if self._A4 is None:
  329. self._A4 = _smart_matrix_product(
  330. self.A2, self.A2, structure=self.structure)
  331. return self._A4
  332. @property
  333. def A6(self):
  334. if self._A6 is None:
  335. self._A6 = _smart_matrix_product(
  336. self.A4, self.A2, structure=self.structure)
  337. return self._A6
  338. @property
  339. def A8(self):
  340. if self._A8 is None:
  341. self._A8 = _smart_matrix_product(
  342. self.A6, self.A2, structure=self.structure)
  343. return self._A8
  344. @property
  345. def A10(self):
  346. if self._A10 is None:
  347. self._A10 = _smart_matrix_product(
  348. self.A4, self.A6, structure=self.structure)
  349. return self._A10
  350. @property
  351. def d4_tight(self):
  352. if self._d4_exact is None:
  353. self._d4_exact = _onenorm(self.A4)**(1/4.)
  354. return self._d4_exact
  355. @property
  356. def d6_tight(self):
  357. if self._d6_exact is None:
  358. self._d6_exact = _onenorm(self.A6)**(1/6.)
  359. return self._d6_exact
  360. @property
  361. def d8_tight(self):
  362. if self._d8_exact is None:
  363. self._d8_exact = _onenorm(self.A8)**(1/8.)
  364. return self._d8_exact
  365. @property
  366. def d10_tight(self):
  367. if self._d10_exact is None:
  368. self._d10_exact = _onenorm(self.A10)**(1/10.)
  369. return self._d10_exact
  370. @property
  371. def d4_loose(self):
  372. if self.use_exact_onenorm:
  373. return self.d4_tight
  374. if self._d4_exact is not None:
  375. return self._d4_exact
  376. else:
  377. if self._d4_approx is None:
  378. self._d4_approx = _onenormest_matrix_power(self.A2, 2,
  379. structure=self.structure)**(1/4.)
  380. return self._d4_approx
  381. @property
  382. def d6_loose(self):
  383. if self.use_exact_onenorm:
  384. return self.d6_tight
  385. if self._d6_exact is not None:
  386. return self._d6_exact
  387. else:
  388. if self._d6_approx is None:
  389. self._d6_approx = _onenormest_matrix_power(self.A2, 3,
  390. structure=self.structure)**(1/6.)
  391. return self._d6_approx
  392. @property
  393. def d8_loose(self):
  394. if self.use_exact_onenorm:
  395. return self.d8_tight
  396. if self._d8_exact is not None:
  397. return self._d8_exact
  398. else:
  399. if self._d8_approx is None:
  400. self._d8_approx = _onenormest_matrix_power(self.A4, 2,
  401. structure=self.structure)**(1/8.)
  402. return self._d8_approx
  403. @property
  404. def d10_loose(self):
  405. if self.use_exact_onenorm:
  406. return self.d10_tight
  407. if self._d10_exact is not None:
  408. return self._d10_exact
  409. else:
  410. if self._d10_approx is None:
  411. self._d10_approx = _onenormest_product((self.A4, self.A6),
  412. structure=self.structure)**(1/10.)
  413. return self._d10_approx
  414. def pade3(self):
  415. b = (120., 60., 12., 1.)
  416. U = _smart_matrix_product(self.A,
  417. b[3]*self.A2 + b[1]*self.ident,
  418. structure=self.structure)
  419. V = b[2]*self.A2 + b[0]*self.ident
  420. return U, V
  421. def pade5(self):
  422. b = (30240., 15120., 3360., 420., 30., 1.)
  423. U = _smart_matrix_product(self.A,
  424. b[5]*self.A4 + b[3]*self.A2 + b[1]*self.ident,
  425. structure=self.structure)
  426. V = b[4]*self.A4 + b[2]*self.A2 + b[0]*self.ident
  427. return U, V
  428. def pade7(self):
  429. b = (17297280., 8648640., 1995840., 277200., 25200., 1512., 56., 1.)
  430. U = _smart_matrix_product(self.A,
  431. b[7]*self.A6 + b[5]*self.A4 + b[3]*self.A2 + b[1]*self.ident,
  432. structure=self.structure)
  433. V = b[6]*self.A6 + b[4]*self.A4 + b[2]*self.A2 + b[0]*self.ident
  434. return U, V
  435. def pade9(self):
  436. b = (17643225600., 8821612800., 2075673600., 302702400., 30270240.,
  437. 2162160., 110880., 3960., 90., 1.)
  438. U = _smart_matrix_product(self.A,
  439. (b[9]*self.A8 + b[7]*self.A6 + b[5]*self.A4 +
  440. b[3]*self.A2 + b[1]*self.ident),
  441. structure=self.structure)
  442. V = (b[8]*self.A8 + b[6]*self.A6 + b[4]*self.A4 +
  443. b[2]*self.A2 + b[0]*self.ident)
  444. return U, V
  445. def pade13_scaled(self, s):
  446. b = (64764752532480000., 32382376266240000., 7771770303897600.,
  447. 1187353796428800., 129060195264000., 10559470521600.,
  448. 670442572800., 33522128640., 1323241920., 40840800., 960960.,
  449. 16380., 182., 1.)
  450. B = self.A * 2**-s
  451. B2 = self.A2 * 2**(-2*s)
  452. B4 = self.A4 * 2**(-4*s)
  453. B6 = self.A6 * 2**(-6*s)
  454. U2 = _smart_matrix_product(B6,
  455. b[13]*B6 + b[11]*B4 + b[9]*B2,
  456. structure=self.structure)
  457. U = _smart_matrix_product(B,
  458. (U2 + b[7]*B6 + b[5]*B4 +
  459. b[3]*B2 + b[1]*self.ident),
  460. structure=self.structure)
  461. V2 = _smart_matrix_product(B6,
  462. b[12]*B6 + b[10]*B4 + b[8]*B2,
  463. structure=self.structure)
  464. V = V2 + b[6]*B6 + b[4]*B4 + b[2]*B2 + b[0]*self.ident
  465. return U, V
  466. def expm(A):
  467. """
  468. Compute the matrix exponential using Pade approximation.
  469. Parameters
  470. ----------
  471. A : (M,M) array_like or sparse array
  472. 2D Array or Matrix (sparse or dense) to be exponentiated
  473. Returns
  474. -------
  475. expA : (M,M) ndarray
  476. Matrix exponential of `A`
  477. Notes
  478. -----
  479. This is algorithm (6.1) which is a simplification of algorithm (5.1).
  480. .. versionadded:: 0.12.0
  481. References
  482. ----------
  483. .. [1] Awad H. Al-Mohy and Nicholas J. Higham (2009)
  484. "A New Scaling and Squaring Algorithm for the Matrix Exponential."
  485. SIAM Journal on Matrix Analysis and Applications.
  486. 31 (3). pp. 970-989. ISSN 1095-7162
  487. Examples
  488. --------
  489. >>> from scipy.sparse import csc_array
  490. >>> from scipy.sparse.linalg import expm
  491. >>> A = csc_array([[1, 0, 0], [0, 2, 0], [0, 0, 3]])
  492. >>> A.toarray()
  493. array([[1, 0, 0],
  494. [0, 2, 0],
  495. [0, 0, 3]], dtype=int64)
  496. >>> Aexp = expm(A)
  497. >>> Aexp
  498. <Compressed Sparse Column sparse array of dtype 'float64'
  499. with 3 stored elements and shape (3, 3)>
  500. >>> Aexp.toarray()
  501. array([[ 2.71828183, 0. , 0. ],
  502. [ 0. , 7.3890561 , 0. ],
  503. [ 0. , 0. , 20.08553692]])
  504. """
  505. return _expm(A, use_exact_onenorm='auto')
  506. def _expm(A, use_exact_onenorm):
  507. # Core of expm, separated to allow testing exact and approximate
  508. # algorithms.
  509. # Avoid indiscriminate asarray() to allow sparse or other strange arrays.
  510. if isinstance(A, list | tuple | np.matrix):
  511. A = np.asarray(A)
  512. if len(A.shape) != 2 or A.shape[0] != A.shape[1]:
  513. raise ValueError('expected a square matrix')
  514. # gracefully handle size-0 input,
  515. # carefully handling sparse scenario
  516. if A.shape == (0, 0):
  517. out = np.zeros([0, 0], dtype=A.dtype)
  518. if issparse(A) or is_pydata_spmatrix(A):
  519. return A.__class__(out)
  520. return out
  521. # Trivial case
  522. if A.shape == (1, 1):
  523. out = [[np.exp(A[0, 0])]]
  524. # Avoid indiscriminate casting to ndarray to
  525. # allow for sparse or other strange arrays
  526. if issparse(A) or is_pydata_spmatrix(A):
  527. return A.__class__(out)
  528. return np.array(out)
  529. # Ensure input is of float type, to avoid integer overflows etc.
  530. if ((isinstance(A, np.ndarray) or issparse(A) or is_pydata_spmatrix(A))
  531. and not np.issubdtype(A.dtype, np.inexact)):
  532. A = A.astype(float)
  533. # Detect upper triangularity.
  534. structure = UPPER_TRIANGULAR if _is_upper_triangular(A) else None
  535. if use_exact_onenorm == "auto":
  536. # Hardcode a matrix order threshold for exact vs. estimated one-norms.
  537. use_exact_onenorm = A.shape[0] < 200
  538. # Track functions of A to help compute the matrix exponential.
  539. h = _ExpmPadeHelper(
  540. A, structure=structure, use_exact_onenorm=use_exact_onenorm)
  541. # Try Pade order 3.
  542. eta_1 = max(h.d4_loose, h.d6_loose)
  543. if eta_1 < 1.495585217958292e-002 and _ell(h.A, 3) == 0:
  544. U, V = h.pade3()
  545. return _solve_P_Q(U, V, structure=structure)
  546. # Try Pade order 5.
  547. eta_2 = max(h.d4_tight, h.d6_loose)
  548. if eta_2 < 2.539398330063230e-001 and _ell(h.A, 5) == 0:
  549. U, V = h.pade5()
  550. return _solve_P_Q(U, V, structure=structure)
  551. # Try Pade orders 7 and 9.
  552. eta_3 = max(h.d6_tight, h.d8_loose)
  553. if eta_3 < 9.504178996162932e-001 and _ell(h.A, 7) == 0:
  554. U, V = h.pade7()
  555. return _solve_P_Q(U, V, structure=structure)
  556. if eta_3 < 2.097847961257068e+000 and _ell(h.A, 9) == 0:
  557. U, V = h.pade9()
  558. return _solve_P_Q(U, V, structure=structure)
  559. # Use Pade order 13.
  560. eta_4 = max(h.d8_loose, h.d10_loose)
  561. eta_5 = min(eta_3, eta_4)
  562. theta_13 = 4.25
  563. # Choose smallest s>=0 such that 2**(-s) eta_5 <= theta_13
  564. if eta_5 == 0:
  565. # Nilpotent special case
  566. s = 0
  567. else:
  568. s = max(int(np.ceil(np.log2(eta_5 / theta_13))), 0)
  569. s = s + _ell(2**-s * h.A, 13)
  570. U, V = h.pade13_scaled(s)
  571. X = _solve_P_Q(U, V, structure=structure)
  572. if structure == UPPER_TRIANGULAR:
  573. # Invoke Code Fragment 2.1.
  574. X = _fragment_2_1(X, h.A, s)
  575. else:
  576. # X = r_13(A)^(2^s) by repeated squaring.
  577. for i in range(s):
  578. X = X.dot(X)
  579. return X
  580. def _solve_P_Q(U, V, structure=None):
  581. """
  582. A helper function for expm_2009.
  583. Parameters
  584. ----------
  585. U : ndarray
  586. Pade numerator.
  587. V : ndarray
  588. Pade denominator.
  589. structure : str, optional
  590. A string describing the structure of both matrices `U` and `V`.
  591. Only `upper_triangular` is currently supported.
  592. Notes
  593. -----
  594. The `structure` argument is inspired by similar args
  595. for theano and cvxopt functions.
  596. """
  597. P = U + V
  598. Q = -U + V
  599. if issparse(U) or is_pydata_spmatrix(U):
  600. return spsolve(Q, P)
  601. elif structure is None:
  602. return solve(Q, P)
  603. elif structure == UPPER_TRIANGULAR:
  604. return solve_triangular(Q, P)
  605. else:
  606. raise ValueError('unsupported matrix structure: ' + str(structure))
  607. def _exp_sinch(a, x):
  608. """
  609. Stably evaluate exp(a)*sinh(x)/x
  610. Notes
  611. -----
  612. The strategy of falling back to a sixth order Taylor expansion
  613. was suggested by the Spallation Neutron Source docs
  614. which was found on the internet by google search.
  615. http://www.ornl.gov/~t6p/resources/xal/javadoc/gov/sns/tools/math/ElementaryFunction.html
  616. The details of the cutoff point and the Horner-like evaluation
  617. was picked without reference to anything in particular.
  618. Note that sinch is not currently implemented in scipy.special,
  619. whereas the "engineer's" definition of sinc is implemented.
  620. The implementation of sinc involves a scaling factor of pi
  621. that distinguishes it from the "mathematician's" version of sinc.
  622. """
  623. # If x is small then use sixth order Taylor expansion.
  624. # How small is small? I am using the point where the relative error
  625. # of the approximation is less than 1e-14.
  626. # If x is large then directly evaluate sinh(x) / x.
  627. if abs(x) < 0.0135:
  628. x2 = x*x
  629. return np.exp(a) * (1 + (x2/6.)*(1 + (x2/20.)*(1 + (x2/42.))))
  630. else:
  631. return (np.exp(a + x) - np.exp(a - x)) / (2*x)
  632. def _eq_10_42(lam_1, lam_2, t_12):
  633. """
  634. Equation (10.42) of Functions of Matrices: Theory and Computation.
  635. Notes
  636. -----
  637. This is a helper function for _fragment_2_1 of expm_2009.
  638. Equation (10.42) is on page 251 in the section on Schur algorithms.
  639. In particular, section 10.4.3 explains the Schur-Parlett algorithm.
  640. expm([[lam_1, t_12], [0, lam_1])
  641. =
  642. [[exp(lam_1), t_12*exp((lam_1 + lam_2)/2)*sinch((lam_1 - lam_2)/2)],
  643. [0, exp(lam_2)]
  644. """
  645. # The plain formula t_12 * (exp(lam_2) - exp(lam_2)) / (lam_2 - lam_1)
  646. # apparently suffers from cancellation, according to Higham's textbook.
  647. # A nice implementation of sinch, defined as sinh(x)/x,
  648. # will apparently work around the cancellation.
  649. a = 0.5 * (lam_1 + lam_2)
  650. b = 0.5 * (lam_1 - lam_2)
  651. return t_12 * _exp_sinch(a, b)
  652. def _fragment_2_1(X, T, s):
  653. """
  654. A helper function for expm_2009.
  655. Notes
  656. -----
  657. The argument X is modified in-place, but this modification is not the same
  658. as the returned value of the function.
  659. This function also takes pains to do things in ways that are compatible
  660. with sparse arrays, for example by avoiding fancy indexing
  661. and by using methods of the matrices whenever possible instead of
  662. using functions of the numpy or scipy libraries themselves.
  663. """
  664. # Form X = r_m(2^-s T)
  665. # Replace diag(X) by exp(2^-s diag(T)).
  666. n = X.shape[0]
  667. diag_T = np.ravel(T.diagonal().copy())
  668. # Replace diag(X) by exp(2^-s diag(T)).
  669. scale = 2 ** -s
  670. exp_diag = np.exp(scale * diag_T)
  671. for k in range(n):
  672. X[k, k] = exp_diag[k]
  673. for i in range(s-1, -1, -1):
  674. X = X.dot(X)
  675. # Replace diag(X) by exp(2^-i diag(T)).
  676. scale = 2 ** -i
  677. exp_diag = np.exp(scale * diag_T)
  678. for k in range(n):
  679. X[k, k] = exp_diag[k]
  680. # Replace (first) superdiagonal of X by explicit formula
  681. # for superdiagonal of exp(2^-i T) from Eq (10.42) of
  682. # the author's 2008 textbook
  683. # Functions of Matrices: Theory and Computation.
  684. for k in range(n-1):
  685. lam_1 = scale * diag_T[k]
  686. lam_2 = scale * diag_T[k+1]
  687. t_12 = scale * T[k, k+1]
  688. value = _eq_10_42(lam_1, lam_2, t_12)
  689. X[k, k+1] = value
  690. # Return the updated X matrix.
  691. return X
  692. def _ell(A, m):
  693. """
  694. A helper function for expm_2009.
  695. Parameters
  696. ----------
  697. A : linear operator
  698. A linear operator whose norm of power we care about.
  699. m : int
  700. The power of the linear operator
  701. Returns
  702. -------
  703. value : int
  704. A value related to a bound.
  705. """
  706. if len(A.shape) != 2 or A.shape[0] != A.shape[1]:
  707. raise ValueError('expected A to be like a square matrix')
  708. # The c_i are explained in (2.2) and (2.6) of the 2005 expm paper.
  709. # They are coefficients of terms of a generating function series expansion.
  710. c_i = {3: 100800.,
  711. 5: 10059033600.,
  712. 7: 4487938430976000.,
  713. 9: 5914384781877411840000.,
  714. 13: 113250775606021113483283660800000000.
  715. }
  716. abs_c_recip = c_i[m]
  717. # This is explained after Eq. (1.2) of the 2009 expm paper.
  718. # It is the "unit roundoff" of IEEE double precision arithmetic.
  719. u = 2**-53
  720. # Compute the one-norm of matrix power p of abs(A).
  721. A_abs_onenorm = _onenorm_matrix_power_nnm(abs(A), 2*m + 1)
  722. # Treat zero norm as a special case.
  723. if not A_abs_onenorm:
  724. return 0
  725. alpha = A_abs_onenorm / (_onenorm(A) * abs_c_recip)
  726. log2_alpha_div_u = np.log2(alpha/u)
  727. value = int(np.ceil(log2_alpha_div_u / (2 * m)))
  728. return max(value, 0)
  729. def matrix_power(A, power):
  730. """
  731. Raise a square matrix to the integer power, `power`.
  732. For non-negative integers, ``A**power`` is computed using repeated
  733. matrix multiplications. Negative integers are not supported.
  734. Parameters
  735. ----------
  736. A : (M, M) square sparse array or matrix
  737. sparse array that will be raised to power `power`
  738. power : int
  739. Exponent used to raise sparse array `A`
  740. Returns
  741. -------
  742. A**power : (M, M) sparse array or matrix
  743. The output matrix will be the same shape as A, and will preserve
  744. the class of A, but the format of the output may be changed.
  745. Notes
  746. -----
  747. This uses a recursive implementation of the matrix power. For computing
  748. the matrix power using a reasonably large `power`, this may be less efficient
  749. than computing the product directly, using A @ A @ ... @ A.
  750. This is contingent upon the number of nonzero entries in the matrix.
  751. .. versionadded:: 1.12.0
  752. Examples
  753. --------
  754. >>> from scipy import sparse
  755. >>> A = sparse.csc_array([[0,1,0],[1,0,1],[0,1,0]])
  756. >>> A.todense()
  757. array([[0, 1, 0],
  758. [1, 0, 1],
  759. [0, 1, 0]])
  760. >>> (A @ A).todense()
  761. array([[1, 0, 1],
  762. [0, 2, 0],
  763. [1, 0, 1]])
  764. >>> A2 = sparse.linalg.matrix_power(A, 2)
  765. >>> A2.todense()
  766. array([[1, 0, 1],
  767. [0, 2, 0],
  768. [1, 0, 1]])
  769. >>> A4 = sparse.linalg.matrix_power(A, 4)
  770. >>> A4.todense()
  771. array([[2, 0, 2],
  772. [0, 4, 0],
  773. [2, 0, 2]])
  774. """
  775. M, N = A.shape
  776. if M != N:
  777. raise TypeError('sparse matrix is not square')
  778. if isintlike(power):
  779. power = int(power)
  780. if power < 0:
  781. raise ValueError('exponent must be >= 0')
  782. if power == 0:
  783. return eye_array(M, dtype=A.dtype)
  784. if power == 1:
  785. return A.copy()
  786. tmp = matrix_power(A, power // 2)
  787. if power % 2:
  788. return A @ tmp @ tmp
  789. else:
  790. return tmp @ tmp
  791. else:
  792. raise ValueError("exponent must be an integer")