test_matfuncs.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. #
  2. # Created by: Pearu Peterson, March 2002
  3. #
  4. """ Test functions for scipy.linalg._matfuncs module
  5. """
  6. import math
  7. import warnings
  8. import numpy as np
  9. from numpy import array, eye, exp, random
  10. from numpy.testing import (
  11. assert_allclose, assert_, assert_array_almost_equal, assert_equal,
  12. assert_array_almost_equal_nulp)
  13. from scipy.sparse import csc_array, SparseEfficiencyWarning
  14. from scipy.sparse._construct import eye_array
  15. from scipy.sparse.linalg._matfuncs import (expm, _expm,
  16. ProductOperator, MatrixPowerOperator,
  17. _onenorm_matrix_power_nnm, matrix_power)
  18. from scipy.sparse._sputils import matrix
  19. from scipy.linalg import logm
  20. from scipy.special import factorial, binom
  21. import scipy.sparse
  22. import scipy.sparse.linalg
  23. def _burkardt_13_power(n, p):
  24. """
  25. A helper function for testing matrix functions.
  26. Parameters
  27. ----------
  28. n : integer greater than 1
  29. Order of the square matrix to be returned.
  30. p : non-negative integer
  31. Power of the matrix.
  32. Returns
  33. -------
  34. out : ndarray representing a square matrix
  35. A Forsythe matrix of order n, raised to the power p.
  36. """
  37. # Input validation.
  38. if n != int(n) or n < 2:
  39. raise ValueError('n must be an integer greater than 1')
  40. n = int(n)
  41. if p != int(p) or p < 0:
  42. raise ValueError('p must be a non-negative integer')
  43. p = int(p)
  44. # Construct the matrix explicitly.
  45. a, b = divmod(p, n)
  46. large = np.power(10.0, -n*a)
  47. small = large * np.power(10.0, -n)
  48. return np.diag([large]*(n-b), b) + np.diag([small]*b, b-n)
  49. def test_onenorm_matrix_power_nnm():
  50. np.random.seed(1234)
  51. for n in range(1, 5):
  52. for p in range(5):
  53. M = np.random.random((n, n))
  54. Mp = np.linalg.matrix_power(M, p)
  55. observed = _onenorm_matrix_power_nnm(M, p)
  56. expected = np.linalg.norm(Mp, 1)
  57. assert_allclose(observed, expected)
  58. def test_matrix_power():
  59. np.random.seed(1234)
  60. row, col = np.random.randint(0, 4, size=(2, 6))
  61. data = np.random.random(size=(6,))
  62. Amat = csc_array((data, (row, col)), shape=(4, 4))
  63. A = csc_array((data, (row, col)), shape=(4, 4))
  64. Adense = A.toarray()
  65. for power in (2, 5, 6):
  66. Apow = matrix_power(A, power).toarray()
  67. Amat_pow = matrix_power(Amat, power).toarray()
  68. Adense_pow = np.linalg.matrix_power(Adense, power)
  69. assert_allclose(Apow, Adense_pow)
  70. assert_allclose(Apow, Amat_pow)
  71. class TestExpM:
  72. def test_zero_ndarray(self):
  73. a = array([[0.,0],[0,0]])
  74. assert_array_almost_equal(expm(a),[[1,0],[0,1]])
  75. def test_zero_sparse(self):
  76. a = csc_array([[0.,0],[0,0]])
  77. assert_array_almost_equal(expm(a).toarray(),[[1,0],[0,1]])
  78. def test_zero_matrix(self):
  79. a = matrix([[0.,0],[0,0]])
  80. assert_array_almost_equal(expm(a),[[1,0],[0,1]])
  81. def test_misc_types(self):
  82. A = expm(np.array([[1]]))
  83. assert_allclose(expm(((1,),)), A)
  84. assert_allclose(expm([[1]]), A)
  85. assert_allclose(expm(matrix([[1]])), A)
  86. assert_allclose(expm(np.array([[1]])), A)
  87. assert_allclose(expm(csc_array([[1]])).toarray(), A)
  88. B = expm(np.array([[1j]]))
  89. assert_allclose(expm(((1j,),)), B)
  90. assert_allclose(expm([[1j]]), B)
  91. assert_allclose(expm(matrix([[1j]])), B)
  92. assert_allclose(expm(csc_array([[1j]])).toarray(), B)
  93. def test_bidiagonal_sparse(self):
  94. A = csc_array([
  95. [1, 3, 0],
  96. [0, 1, 5],
  97. [0, 0, 2]], dtype=float)
  98. e1 = math.exp(1)
  99. e2 = math.exp(2)
  100. expected = np.array([
  101. [e1, 3*e1, 15*(e2 - 2*e1)],
  102. [0, e1, 5*(e2 - e1)],
  103. [0, 0, e2]], dtype=float)
  104. observed = expm(A).toarray()
  105. assert_array_almost_equal(observed, expected)
  106. def test_padecases_dtype_float(self):
  107. for dtype in [np.float32, np.float64]:
  108. for scale in [1e-2, 1e-1, 5e-1, 1, 10]:
  109. A = scale * eye(3, dtype=dtype)
  110. observed = expm(A)
  111. expected = exp(scale, dtype=dtype) * eye(3, dtype=dtype)
  112. assert_array_almost_equal_nulp(observed, expected, nulp=100)
  113. def test_padecases_dtype_complex(self):
  114. for dtype in [np.complex64, np.complex128]:
  115. for scale in [1e-2, 1e-1, 5e-1, 1, 10]:
  116. A = scale * eye(3, dtype=dtype)
  117. observed = expm(A)
  118. expected = exp(scale, dtype=dtype) * eye(3, dtype=dtype)
  119. assert_array_almost_equal_nulp(observed, expected, nulp=100)
  120. def test_padecases_dtype_sparse_float(self):
  121. # float32 and complex64 lead to errors in spsolve/UMFpack
  122. dtype = np.float64
  123. for scale in [1e-2, 1e-1, 5e-1, 1, 10]:
  124. a = scale * eye_array(3, 3, dtype=dtype, format='csc')
  125. e = exp(scale, dtype=dtype) * eye(3, dtype=dtype)
  126. with warnings.catch_warnings():
  127. msg = "Changing the sparsity structure"
  128. warnings.filterwarnings("ignore", msg, SparseEfficiencyWarning)
  129. exact_onenorm = _expm(a, use_exact_onenorm=True).toarray()
  130. inexact_onenorm = _expm(a, use_exact_onenorm=False).toarray()
  131. assert_array_almost_equal_nulp(exact_onenorm, e, nulp=100)
  132. assert_array_almost_equal_nulp(inexact_onenorm, e, nulp=100)
  133. def test_padecases_dtype_sparse_complex(self):
  134. # float32 and complex64 lead to errors in spsolve/UMFpack
  135. dtype = np.complex128
  136. for scale in [1e-2, 1e-1, 5e-1, 1, 10]:
  137. a = scale * eye_array(3, 3, dtype=dtype, format='csc')
  138. e = exp(scale) * eye(3, dtype=dtype)
  139. with warnings.catch_warnings():
  140. msg = "Changing the sparsity structure"
  141. warnings.filterwarnings("ignore", msg, SparseEfficiencyWarning)
  142. assert_array_almost_equal_nulp(expm(a).toarray(), e, nulp=100)
  143. def test_logm_consistency(self):
  144. random.seed(1234)
  145. for dtype in [np.float64, np.complex128]:
  146. for n in range(1, 10):
  147. for scale in [1e-4, 1e-3, 1e-2, 1e-1, 1, 1e1, 1e2]:
  148. # make logm(A) be of a given scale
  149. A = (eye(n) + random.rand(n, n) * scale).astype(dtype)
  150. if np.iscomplexobj(A):
  151. A = A + 1j * random.rand(n, n) * scale
  152. assert_array_almost_equal(expm(logm(A)), A)
  153. def test_integer_matrix(self):
  154. Q = np.array([
  155. [-3, 1, 1, 1],
  156. [1, -3, 1, 1],
  157. [1, 1, -3, 1],
  158. [1, 1, 1, -3]])
  159. assert_allclose(expm(Q), expm(1.0 * Q))
  160. def test_integer_matrix_2(self):
  161. # Check for integer overflows
  162. Q = np.array([[-500, 500, 0, 0],
  163. [0, -550, 360, 190],
  164. [0, 630, -630, 0],
  165. [0, 0, 0, 0]], dtype=np.int16)
  166. assert_allclose(expm(Q), expm(1.0 * Q))
  167. Q = csc_array(Q)
  168. assert_allclose(expm(Q).toarray(), expm(1.0 * Q).toarray())
  169. def test_triangularity_perturbation(self):
  170. # Experiment (1) of
  171. # Awad H. Al-Mohy and Nicholas J. Higham (2012)
  172. # Improved Inverse Scaling and Squaring Algorithms
  173. # for the Matrix Logarithm.
  174. A = np.array([
  175. [3.2346e-1, 3e4, 3e4, 3e4],
  176. [0, 3.0089e-1, 3e4, 3e4],
  177. [0, 0, 3.221e-1, 3e4],
  178. [0, 0, 0, 3.0744e-1]],
  179. dtype=float)
  180. A_logm = np.array([
  181. [-1.12867982029050462e+00, 9.61418377142025565e+04,
  182. -4.52485573953179264e+09, 2.92496941103871812e+14],
  183. [0.00000000000000000e+00, -1.20101052953082288e+00,
  184. 9.63469687211303099e+04, -4.68104828911105442e+09],
  185. [0.00000000000000000e+00, 0.00000000000000000e+00,
  186. -1.13289322264498393e+00, 9.53249183094775653e+04],
  187. [0.00000000000000000e+00, 0.00000000000000000e+00,
  188. 0.00000000000000000e+00, -1.17947533272554850e+00]],
  189. dtype=float)
  190. assert_allclose(expm(A_logm), A, rtol=1e-4)
  191. # Perturb the upper triangular matrix by tiny amounts,
  192. # so that it becomes technically not upper triangular.
  193. random.seed(1234)
  194. tiny = 1e-17
  195. A_logm_perturbed = A_logm.copy()
  196. A_logm_perturbed[1, 0] = tiny
  197. with warnings.catch_warnings():
  198. warnings.filterwarnings("ignore", "Ill-conditioned.*", RuntimeWarning)
  199. warnings.filterwarnings("ignore", "An ill-conditioned.*", RuntimeWarning)
  200. A_expm_logm_perturbed = expm(A_logm_perturbed)
  201. rtol = 1e-4
  202. atol = 100 * tiny
  203. assert_(not np.allclose(A_expm_logm_perturbed, A, rtol=rtol, atol=atol))
  204. def test_burkardt_1(self):
  205. # This matrix is diagonal.
  206. # The calculation of the matrix exponential is simple.
  207. #
  208. # This is the first of a series of matrix exponential tests
  209. # collected by John Burkardt from the following sources.
  210. #
  211. # Alan Laub,
  212. # Review of "Linear System Theory" by Joao Hespanha,
  213. # SIAM Review,
  214. # Volume 52, Number 4, December 2010, pages 779--781.
  215. #
  216. # Cleve Moler and Charles Van Loan,
  217. # Nineteen Dubious Ways to Compute the Exponential of a Matrix,
  218. # Twenty-Five Years Later,
  219. # SIAM Review,
  220. # Volume 45, Number 1, March 2003, pages 3--49.
  221. #
  222. # Cleve Moler,
  223. # Cleve's Corner: A Balancing Act for the Matrix Exponential,
  224. # 23 July 2012.
  225. #
  226. # Robert Ward,
  227. # Numerical computation of the matrix exponential
  228. # with accuracy estimate,
  229. # SIAM Journal on Numerical Analysis,
  230. # Volume 14, Number 4, September 1977, pages 600--610.
  231. exp1 = np.exp(1)
  232. exp2 = np.exp(2)
  233. A = np.array([
  234. [1, 0],
  235. [0, 2],
  236. ], dtype=float)
  237. desired = np.array([
  238. [exp1, 0],
  239. [0, exp2],
  240. ], dtype=float)
  241. actual = expm(A)
  242. assert_allclose(actual, desired)
  243. def test_burkardt_2(self):
  244. # This matrix is symmetric.
  245. # The calculation of the matrix exponential is straightforward.
  246. A = np.array([
  247. [1, 3],
  248. [3, 2],
  249. ], dtype=float)
  250. desired = np.array([
  251. [39.322809708033859, 46.166301438885753],
  252. [46.166301438885768, 54.711576854329110],
  253. ], dtype=float)
  254. actual = expm(A)
  255. assert_allclose(actual, desired)
  256. def test_burkardt_3(self):
  257. # This example is due to Laub.
  258. # This matrix is ill-suited for the Taylor series approach.
  259. # As powers of A are computed, the entries blow up too quickly.
  260. exp1 = np.exp(1)
  261. exp39 = np.exp(39)
  262. A = np.array([
  263. [0, 1],
  264. [-39, -40],
  265. ], dtype=float)
  266. desired = np.array([
  267. [
  268. 39/(38*exp1) - 1/(38*exp39),
  269. -np.expm1(-38) / (38*exp1)],
  270. [
  271. 39*np.expm1(-38) / (38*exp1),
  272. -1/(38*exp1) + 39/(38*exp39)],
  273. ], dtype=float)
  274. actual = expm(A)
  275. assert_allclose(actual, desired)
  276. def test_burkardt_4(self):
  277. # This example is due to Moler and Van Loan.
  278. # The example will cause problems for the series summation approach,
  279. # as well as for diagonal Pade approximations.
  280. A = np.array([
  281. [-49, 24],
  282. [-64, 31],
  283. ], dtype=float)
  284. U = np.array([[3, 1], [4, 2]], dtype=float)
  285. V = np.array([[1, -1/2], [-2, 3/2]], dtype=float)
  286. w = np.array([-17, -1], dtype=float)
  287. desired = np.dot(U * np.exp(w), V)
  288. actual = expm(A)
  289. assert_allclose(actual, desired)
  290. def test_burkardt_5(self):
  291. # This example is due to Moler and Van Loan.
  292. # This matrix is strictly upper triangular
  293. # All powers of A are zero beyond some (low) limit.
  294. # This example will cause problems for Pade approximations.
  295. A = np.array([
  296. [0, 6, 0, 0],
  297. [0, 0, 6, 0],
  298. [0, 0, 0, 6],
  299. [0, 0, 0, 0],
  300. ], dtype=float)
  301. desired = np.array([
  302. [1, 6, 18, 36],
  303. [0, 1, 6, 18],
  304. [0, 0, 1, 6],
  305. [0, 0, 0, 1],
  306. ], dtype=float)
  307. actual = expm(A)
  308. assert_allclose(actual, desired)
  309. def test_burkardt_6(self):
  310. # This example is due to Moler and Van Loan.
  311. # This matrix does not have a complete set of eigenvectors.
  312. # That means the eigenvector approach will fail.
  313. exp1 = np.exp(1)
  314. A = np.array([
  315. [1, 1],
  316. [0, 1],
  317. ], dtype=float)
  318. desired = np.array([
  319. [exp1, exp1],
  320. [0, exp1],
  321. ], dtype=float)
  322. actual = expm(A)
  323. assert_allclose(actual, desired)
  324. def test_burkardt_7(self):
  325. # This example is due to Moler and Van Loan.
  326. # This matrix is very close to example 5.
  327. # Mathematically, it has a complete set of eigenvectors.
  328. # Numerically, however, the calculation will be suspect.
  329. exp1 = np.exp(1)
  330. eps = np.spacing(1)
  331. A = np.array([
  332. [1 + eps, 1],
  333. [0, 1 - eps],
  334. ], dtype=float)
  335. desired = np.array([
  336. [exp1, exp1],
  337. [0, exp1],
  338. ], dtype=float)
  339. actual = expm(A)
  340. assert_allclose(actual, desired)
  341. def test_burkardt_8(self):
  342. # This matrix was an example in Wikipedia.
  343. exp4 = np.exp(4)
  344. exp16 = np.exp(16)
  345. A = np.array([
  346. [21, 17, 6],
  347. [-5, -1, -6],
  348. [4, 4, 16],
  349. ], dtype=float)
  350. desired = np.array([
  351. [13*exp16 - exp4, 13*exp16 - 5*exp4, 2*exp16 - 2*exp4],
  352. [-9*exp16 + exp4, -9*exp16 + 5*exp4, -2*exp16 + 2*exp4],
  353. [16*exp16, 16*exp16, 4*exp16],
  354. ], dtype=float) * 0.25
  355. actual = expm(A)
  356. assert_allclose(actual, desired)
  357. def test_burkardt_9(self):
  358. # This matrix is due to the NAG Library.
  359. # It is an example for function F01ECF.
  360. A = np.array([
  361. [1, 2, 2, 2],
  362. [3, 1, 1, 2],
  363. [3, 2, 1, 2],
  364. [3, 3, 3, 1],
  365. ], dtype=float)
  366. desired = np.array([
  367. [740.7038, 610.8500, 542.2743, 549.1753],
  368. [731.2510, 603.5524, 535.0884, 542.2743],
  369. [823.7630, 679.4257, 603.5524, 610.8500],
  370. [998.4355, 823.7630, 731.2510, 740.7038],
  371. ], dtype=float)
  372. actual = expm(A)
  373. assert_allclose(actual, desired)
  374. def test_burkardt_10(self):
  375. # This is Ward's example #1.
  376. # It is defective and nonderogatory.
  377. A = np.array([
  378. [4, 2, 0],
  379. [1, 4, 1],
  380. [1, 1, 4],
  381. ], dtype=float)
  382. assert_allclose(sorted(scipy.linalg.eigvals(A)), (3, 3, 6))
  383. desired = np.array([
  384. [147.8666224463699, 183.7651386463682, 71.79703239999647],
  385. [127.7810855231823, 183.7651386463682, 91.88256932318415],
  386. [127.7810855231824, 163.6796017231806, 111.9681062463718],
  387. ], dtype=float)
  388. actual = expm(A)
  389. assert_allclose(actual, desired)
  390. def test_burkardt_11(self):
  391. # This is Ward's example #2.
  392. # It is a symmetric matrix.
  393. A = np.array([
  394. [29.87942128909879, 0.7815750847907159, -2.289519314033932],
  395. [0.7815750847907159, 25.72656945571064, 8.680737820540137],
  396. [-2.289519314033932, 8.680737820540137, 34.39400925519054],
  397. ], dtype=float)
  398. assert_allclose(scipy.linalg.eigvalsh(A), (20, 30, 40))
  399. desired = np.array([
  400. [
  401. 5.496313853692378E+15,
  402. -1.823188097200898E+16,
  403. -3.047577080858001E+16],
  404. [
  405. -1.823188097200899E+16,
  406. 6.060522870222108E+16,
  407. 1.012918429302482E+17],
  408. [
  409. -3.047577080858001E+16,
  410. 1.012918429302482E+17,
  411. 1.692944112408493E+17],
  412. ], dtype=float)
  413. actual = expm(A)
  414. assert_allclose(actual, desired)
  415. def test_burkardt_12(self):
  416. # This is Ward's example #3.
  417. # Ward's algorithm has difficulty estimating the accuracy
  418. # of its results.
  419. A = np.array([
  420. [-131, 19, 18],
  421. [-390, 56, 54],
  422. [-387, 57, 52],
  423. ], dtype=float)
  424. assert_allclose(sorted(scipy.linalg.eigvals(A)), (-20, -2, -1))
  425. desired = np.array([
  426. [-1.509644158793135, 0.3678794391096522, 0.1353352811751005],
  427. [-5.632570799891469, 1.471517758499875, 0.4060058435250609],
  428. [-4.934938326088363, 1.103638317328798, 0.5413411267617766],
  429. ], dtype=float)
  430. actual = expm(A)
  431. assert_allclose(actual, desired)
  432. def test_burkardt_13(self):
  433. # This is Ward's example #4.
  434. # This is a version of the Forsythe matrix.
  435. # The eigenvector problem is badly conditioned.
  436. # Ward's algorithm has difficulty estimating the accuracy
  437. # of its results for this problem.
  438. #
  439. # Check the construction of one instance of this family of matrices.
  440. A4_actual = _burkardt_13_power(4, 1)
  441. A4_desired = [[0, 1, 0, 0],
  442. [0, 0, 1, 0],
  443. [0, 0, 0, 1],
  444. [1e-4, 0, 0, 0]]
  445. assert_allclose(A4_actual, A4_desired)
  446. # Check the expm for a few instances.
  447. for n in (2, 3, 4, 10):
  448. # Approximate expm using Taylor series.
  449. # This works well for this matrix family
  450. # because each matrix in the summation,
  451. # even before dividing by the factorial,
  452. # is entrywise positive with max entry 10**(-floor(p/n)*n).
  453. k = max(1, int(np.ceil(16/n)))
  454. desired = np.zeros((n, n), dtype=float)
  455. for p in range(n*k):
  456. Ap = _burkardt_13_power(n, p)
  457. assert_equal(np.min(Ap), 0)
  458. assert_allclose(np.max(Ap), np.power(10, -np.floor(p/n)*n))
  459. desired += Ap / factorial(p)
  460. actual = expm(_burkardt_13_power(n, 1))
  461. assert_allclose(actual, desired)
  462. def test_burkardt_14(self):
  463. # This is Moler's example.
  464. # This badly scaled matrix caused problems for MATLAB's expm().
  465. A = np.array([
  466. [0, 1e-8, 0],
  467. [-(2e10 + 4e8/6.), -3, 2e10],
  468. [200./3., 0, -200./3.],
  469. ], dtype=float)
  470. desired = np.array([
  471. [0.446849468283175, 1.54044157383952e-09, 0.462811453558774],
  472. [-5743067.77947947, -0.0152830038686819, -4526542.71278401],
  473. [0.447722977849494, 1.54270484519591e-09, 0.463480648837651],
  474. ], dtype=float)
  475. actual = expm(A)
  476. assert_allclose(actual, desired)
  477. def test_pascal(self):
  478. # Test pascal triangle.
  479. # Nilpotent exponential, used to trigger a failure (gh-8029)
  480. for scale in [1.0, 1e-3, 1e-6]:
  481. for n in range(0, 80, 3):
  482. sc = scale ** np.arange(n, -1, -1)
  483. if np.any(sc < 1e-300):
  484. break
  485. A = np.diag(np.arange(1, n + 1), -1) * scale
  486. B = expm(A)
  487. got = B
  488. expected = binom(np.arange(n + 1)[:,None],
  489. np.arange(n + 1)[None,:]) * sc[None,:] / sc[:,None]
  490. atol = 1e-13 * abs(expected).max()
  491. assert_allclose(got, expected, atol=atol)
  492. def test_matrix_input(self):
  493. # Large np.matrix inputs should work, gh-5546
  494. A = np.zeros((200, 200))
  495. A[-1,0] = 1
  496. B0 = expm(A)
  497. with warnings.catch_warnings():
  498. warnings.filterwarnings(
  499. "ignore", "the matrix subclass.*", DeprecationWarning)
  500. warnings.filterwarnings(
  501. "ignore", "the matrix subclass.*", PendingDeprecationWarning)
  502. B = expm(np.matrix(A))
  503. assert_allclose(B, B0)
  504. def test_exp_sinch_overflow(self):
  505. # Check overflow in intermediate steps is fixed (gh-11839)
  506. L = np.array([[1.0, -0.5, -0.5, 0.0, 0.0, 0.0, 0.0],
  507. [0.0, 1.0, 0.0, -0.5, -0.5, 0.0, 0.0],
  508. [0.0, 0.0, 1.0, 0.0, 0.0, -0.5, -0.5],
  509. [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
  510. [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
  511. [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
  512. [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]])
  513. E0 = expm(-L)
  514. E1 = expm(-2**11 * L)
  515. E2 = E0
  516. for j in range(11):
  517. E2 = E2 @ E2
  518. assert_allclose(E1, E2)
  519. class TestOperators:
  520. def test_product_operator(self):
  521. random.seed(1234)
  522. n = 5
  523. k = 2
  524. nsamples = 10
  525. for i in range(nsamples):
  526. A = np.random.randn(n, n)
  527. B = np.random.randn(n, n)
  528. C = np.random.randn(n, n)
  529. D = np.random.randn(n, k)
  530. op = ProductOperator(A, B, C)
  531. assert_allclose(op.matmat(D), A.dot(B).dot(C).dot(D))
  532. assert_allclose(op.T.matmat(D), (A.dot(B).dot(C)).T.dot(D))
  533. def test_matrix_power_operator(self):
  534. random.seed(1234)
  535. n = 5
  536. k = 2
  537. p = 3
  538. nsamples = 10
  539. for i in range(nsamples):
  540. A = np.random.randn(n, n)
  541. B = np.random.randn(n, k)
  542. op = MatrixPowerOperator(A, p)
  543. assert_allclose(op.matmat(B), np.linalg.matrix_power(A, p).dot(B))
  544. assert_allclose(op.T.matmat(B), np.linalg.matrix_power(A, p).T.dot(B))