test_linalg.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. # TODO: don't use round
  2. from __future__ import division
  3. import pytest
  4. from mpmath import *
  5. xrange = libmp.backend.xrange
  6. # XXX: these shouldn't be visible(?)
  7. LU_decomp = mp.LU_decomp
  8. L_solve = mp.L_solve
  9. U_solve = mp.U_solve
  10. householder = mp.householder
  11. improve_solution = mp.improve_solution
  12. A1 = matrix([[3, 1, 6],
  13. [2, 1, 3],
  14. [1, 1, 1]])
  15. b1 = [2, 7, 4]
  16. A2 = matrix([[ 2, -1, -1, 2],
  17. [ 6, -2, 3, -1],
  18. [-4, 2, 3, -2],
  19. [ 2, 0, 4, -3]])
  20. b2 = [3, -3, -2, -1]
  21. A3 = matrix([[ 1, 0, -1, -1, 0],
  22. [ 0, 1, 1, 0, -1],
  23. [ 4, -5, 2, 0, 0],
  24. [ 0, 0, -2, 9,-12],
  25. [ 0, 5, 0, 0, 12]])
  26. b3 = [0, 0, 0, 0, 50]
  27. A4 = matrix([[10.235, -4.56, 0., -0.035, 5.67],
  28. [-2.463, 1.27, 3.97, -8.63, 1.08],
  29. [-6.58, 0.86, -0.257, 9.32, -43.6 ],
  30. [ 9.83, 7.39, -17.25, 0.036, 24.86],
  31. [-9.31, 34.9, 78.56, 1.07, 65.8 ]])
  32. b4 = [8.95, 20.54, 7.42, 5.60, 58.43]
  33. A5 = matrix([[ 1, 2, -4],
  34. [-2, -3, 5],
  35. [ 3, 5, -8]])
  36. A6 = matrix([[ 1.377360, 2.481400, 5.359190],
  37. [ 2.679280, -1.229560, 25.560210],
  38. [-1.225280+1.e6, 9.910180, -35.049900-1.e6]])
  39. b6 = [23.500000, -15.760000, 2.340000]
  40. A7 = matrix([[1, -0.5],
  41. [2, 1],
  42. [-2, 6]])
  43. b7 = [3, 2, -4]
  44. A8 = matrix([[1, 2, 3],
  45. [-1, 0, 1],
  46. [-1, -2, -1],
  47. [1, 0, -1]])
  48. b8 = [1, 2, 3, 4]
  49. A9 = matrix([[ 4, 2, -2],
  50. [ 2, 5, -4],
  51. [-2, -4, 5.5]])
  52. b9 = [10, 16, -15.5]
  53. A10 = matrix([[1.0 + 1.0j, 2.0, 2.0],
  54. [4.0, 5.0, 6.0],
  55. [7.0, 8.0, 9.0]])
  56. b10 = [1.0, 1.0 + 1.0j, 1.0]
  57. def test_LU_decomp():
  58. A = A3.copy()
  59. b = b3
  60. A, p = LU_decomp(A)
  61. y = L_solve(A, b, p)
  62. x = U_solve(A, y)
  63. assert p == [2, 1, 2, 3]
  64. assert [round(i, 14) for i in x] == [3.78953107960742, 2.9989094874591098,
  65. -0.081788440567070006, 3.8713195201744801, 2.9171210468920399]
  66. A = A4.copy()
  67. b = b4
  68. A, p = LU_decomp(A)
  69. y = L_solve(A, b, p)
  70. x = U_solve(A, y)
  71. assert p == [0, 3, 4, 3]
  72. assert [round(i, 14) for i in x] == [2.6383625899619201, 2.6643834462368399,
  73. 0.79208015947958998, -2.5088376454101899, -1.0567657691375001]
  74. A = randmatrix(3)
  75. bak = A.copy()
  76. LU_decomp(A, overwrite=1)
  77. assert A != bak
  78. def test_inverse():
  79. for A in [A1, A2, A5]:
  80. inv = inverse(A)
  81. assert mnorm(A*inv - eye(A.rows), 1) < 1.e-14
  82. def test_householder():
  83. mp.dps = 15
  84. A, b = A8, b8
  85. H, p, x, r = householder(extend(A, b))
  86. assert H == matrix(
  87. [[mpf('3.0'), mpf('-2.0'), mpf('-1.0'), 0],
  88. [-1.0,mpf('3.333333333333333'),mpf('-2.9999999999999991'),mpf('2.0')],
  89. [-1.0, mpf('-0.66666666666666674'),mpf('2.8142135623730948'),
  90. mpf('-2.8284271247461898')],
  91. [1.0, mpf('-1.3333333333333333'),mpf('-0.20000000000000018'),
  92. mpf('4.2426406871192857')]])
  93. assert p == [-2, -2, mpf('-1.4142135623730949')]
  94. assert round(norm(r, 2), 10) == 4.2426406870999998
  95. y = [102.102, 58.344, 36.463, 24.310, 17.017, 12.376, 9.282, 7.140, 5.610,
  96. 4.488, 3.6465, 3.003]
  97. def coeff(n):
  98. # similiar to Hilbert matrix
  99. A = []
  100. for i in range(1, 13):
  101. A.append([1. / (i + j - 1) for j in range(1, n + 1)])
  102. return matrix(A)
  103. residuals = []
  104. refres = []
  105. for n in range(2, 7):
  106. A = coeff(n)
  107. H, p, x, r = householder(extend(A, y))
  108. x = matrix(x)
  109. y = matrix(y)
  110. residuals.append(norm(r, 2))
  111. refres.append(norm(residual(A, x, y), 2))
  112. assert [round(res, 10) for res in residuals] == [15.1733888877,
  113. 0.82378073210000002, 0.302645887, 0.0260109244,
  114. 0.00058653999999999998]
  115. assert norm(matrix(residuals) - matrix(refres), inf) < 1.e-13
  116. def hilbert_cmplx(n):
  117. # Complexified Hilbert matrix
  118. A = hilbert(2*n,n)
  119. v = randmatrix(2*n, 2, min=-1, max=1)
  120. v = v.apply(lambda x: exp(1J*pi()*x))
  121. A = diag(v[:,0])*A*diag(v[:n,1])
  122. return A
  123. residuals_cmplx = []
  124. refres_cmplx = []
  125. for n in range(2, 10):
  126. A = hilbert_cmplx(n)
  127. H, p, x, r = householder(A.copy())
  128. residuals_cmplx.append(norm(r, 2))
  129. refres_cmplx.append(norm(residual(A[:,:n-1], x, A[:,n-1]), 2))
  130. assert norm(matrix(residuals_cmplx) - matrix(refres_cmplx), inf) < 1.e-13
  131. def test_factorization():
  132. A = randmatrix(5)
  133. P, L, U = lu(A)
  134. assert mnorm(P*A - L*U, 1) < 1.e-15
  135. def test_solve():
  136. assert norm(residual(A6, lu_solve(A6, b6), b6), inf) < 1.e-10
  137. assert norm(residual(A7, lu_solve(A7, b7), b7), inf) < 1.5
  138. assert norm(residual(A8, lu_solve(A8, b8), b8), inf) <= 3 + 1.e-10
  139. assert norm(residual(A6, qr_solve(A6, b6)[0], b6), inf) < 1.e-10
  140. assert norm(residual(A7, qr_solve(A7, b7)[0], b7), inf) < 1.5
  141. assert norm(residual(A8, qr_solve(A8, b8)[0], b8), 2) <= 4.3
  142. assert norm(residual(A10, lu_solve(A10, b10), b10), 2) < 1.e-10
  143. assert norm(residual(A10, qr_solve(A10, b10)[0], b10), 2) < 1.e-10
  144. def test_solve_overdet_complex():
  145. A = matrix([[1, 2j], [3, 4j], [5, 6]])
  146. b = matrix([1 + j, 2, -j])
  147. assert norm(residual(A, lu_solve(A, b), b)) < 1.0208
  148. def test_singular():
  149. mp.dps = 15
  150. A = [[5.6, 1.2], [7./15, .1]]
  151. B = repr(zeros(2))
  152. b = [1, 2]
  153. for i in ['lu_solve(%s, %s)' % (A, b), 'lu_solve(%s, %s)' % (B, b),
  154. 'qr_solve(%s, %s)' % (A, b), 'qr_solve(%s, %s)' % (B, b)]:
  155. pytest.raises((ZeroDivisionError, ValueError), lambda: eval(i))
  156. def test_cholesky():
  157. assert fp.cholesky(fp.matrix(A9)) == fp.matrix([[2, 0, 0], [1, 2, 0], [-1, -3/2, 3/2]])
  158. x = fp.cholesky_solve(A9, b9)
  159. assert fp.norm(fp.residual(A9, x, b9), fp.inf) == 0
  160. def test_det():
  161. assert det(A1) == 1
  162. assert round(det(A2), 14) == 8
  163. assert round(det(A3)) == 1834
  164. assert round(det(A4)) == 4443376
  165. assert det(A5) == 1
  166. assert round(det(A6)) == 78356463
  167. assert det(zeros(3)) == 0
  168. def test_cond():
  169. mp.dps = 15
  170. A = matrix([[1.2969, 0.8648], [0.2161, 0.1441]])
  171. assert cond(A, lambda x: mnorm(x,1)) == mpf('327065209.73817754')
  172. assert cond(A, lambda x: mnorm(x,inf)) == mpf('327065209.73817754')
  173. assert cond(A, lambda x: mnorm(x,'F')) == mpf('249729266.80008656')
  174. @extradps(50)
  175. def test_precision():
  176. A = randmatrix(10, 10)
  177. assert mnorm(inverse(inverse(A)) - A, 1) < 1.e-45
  178. def test_interval_matrix():
  179. mp.dps = 15
  180. iv.dps = 15
  181. a = iv.matrix([['0.1','0.3','1.0'],['7.1','5.5','4.8'],['3.2','4.4','5.6']])
  182. b = iv.matrix(['4','0.6','0.5'])
  183. c = iv.lu_solve(a, b)
  184. assert c[0].delta < 1e-13
  185. assert c[1].delta < 1e-13
  186. assert c[2].delta < 1e-13
  187. assert 5.25823271130625686059275 in c[0]
  188. assert -13.155049396267837541163 in c[1]
  189. assert 7.42069154774972557628979 in c[2]
  190. def test_LU_cache():
  191. A = randmatrix(3)
  192. LU = LU_decomp(A)
  193. assert A._LU == LU_decomp(A)
  194. A[0,0] = -1000
  195. assert A._LU is None
  196. def test_improve_solution():
  197. A = randmatrix(5, min=1e-20, max=1e20)
  198. b = randmatrix(5, 1, min=-1000, max=1000)
  199. x1 = lu_solve(A, b) + randmatrix(5, 1, min=-1e-5, max=1.e-5)
  200. x2 = improve_solution(A, x1, b)
  201. assert norm(residual(A, x2, b), 2) < norm(residual(A, x1, b), 2)
  202. def test_exp_pade():
  203. for i in range(3):
  204. dps = 15
  205. extra = 15
  206. mp.dps = dps + extra
  207. dm = 0
  208. N = 3
  209. dg = range(1,N+1)
  210. a = diag(dg)
  211. expa = diag([exp(x) for x in dg])
  212. # choose a random matrix not close to be singular
  213. # to avoid adding too much extra precision in computing
  214. # m**-1 * M * m
  215. while abs(dm) < 0.01:
  216. m = randmatrix(N)
  217. dm = det(m)
  218. m = m/dm
  219. a1 = m**-1 * a * m
  220. e2 = m**-1 * expa * m
  221. mp.dps = dps
  222. e1 = expm(a1, method='pade')
  223. mp.dps = dps + extra
  224. d = e2 - e1
  225. #print d
  226. mp.dps = dps
  227. assert norm(d, inf).ae(0)
  228. mp.dps = 15
  229. def test_qr():
  230. mp.dps = 15 # used default value for dps
  231. lowlimit = -9 # lower limit of matrix element value
  232. uplimit = 9 # uppter limit of matrix element value
  233. maxm = 4 # max matrix size
  234. flg = False # toggle to create real vs complex matrix
  235. zero = mpf('0.0')
  236. for k in xrange(0,10):
  237. exdps = 0
  238. mode = 'full'
  239. flg = bool(k % 2)
  240. # generate arbitrary matrix size (2 to maxm)
  241. num1 = nint(2 + (maxm-2)*rand())
  242. num2 = nint(2 + (maxm-2)*rand())
  243. m = int(max(num1, num2))
  244. n = int(min(num1, num2))
  245. # create matrix
  246. A = mp.matrix(m,n)
  247. # populate matrix values with arbitrary integers
  248. if flg:
  249. flg = False
  250. dtype = 'complex'
  251. for j in xrange(0,n):
  252. for i in xrange(0,m):
  253. val = nint(lowlimit + (uplimit-lowlimit)*rand())
  254. val2 = nint(lowlimit + (uplimit-lowlimit)*rand())
  255. A[i,j] = mpc(val, val2)
  256. else:
  257. flg = True
  258. dtype = 'real'
  259. for j in xrange(0,n):
  260. for i in xrange(0,m):
  261. val = nint(lowlimit + (uplimit-lowlimit)*rand())
  262. A[i,j] = mpf(val)
  263. # perform A -> QR decomposition
  264. Q, R = qr(A, mode, edps = exdps)
  265. #print('\n\n A = \n', nstr(A, 4))
  266. #print('\n Q = \n', nstr(Q, 4))
  267. #print('\n R = \n', nstr(R, 4))
  268. #print('\n Q*R = \n', nstr(Q*R, 4))
  269. maxnorm = mpf('1.0E-11')
  270. n1 = norm(A - Q * R)
  271. #print '\n Norm of A - Q * R = ', n1
  272. assert n1 <= maxnorm
  273. if dtype == 'real':
  274. n1 = norm(eye(m) - Q.T * Q)
  275. #print ' Norm of I - Q.T * Q = ', n1
  276. assert n1 <= maxnorm
  277. n1 = norm(eye(m) - Q * Q.T)
  278. #print ' Norm of I - Q * Q.T = ', n1
  279. assert n1 <= maxnorm
  280. if dtype == 'complex':
  281. n1 = norm(eye(m) - Q.T * Q.conjugate())
  282. #print ' Norm of I - Q.T * Q.conjugate() = ', n1
  283. assert n1 <= maxnorm
  284. n1 = norm(eye(m) - Q.conjugate() * Q.T)
  285. #print ' Norm of I - Q.conjugate() * Q.T = ', n1
  286. assert n1 <= maxnorm