test_onenormest.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. """Test functions for the sparse.linalg._onenormest module
  2. """
  3. from __future__ import division, print_function, absolute_import
  4. import numpy as np
  5. from numpy.testing import assert_allclose, assert_equal, assert_
  6. import pytest
  7. import scipy.linalg
  8. import scipy.sparse.linalg
  9. from scipy.sparse.linalg._onenormest import _onenormest_core, _algorithm_2_2
  10. class MatrixProductOperator(scipy.sparse.linalg.LinearOperator):
  11. """
  12. This is purely for onenormest testing.
  13. """
  14. def __init__(self, A, B):
  15. if A.ndim != 2 or B.ndim != 2:
  16. raise ValueError('expected ndarrays representing matrices')
  17. if A.shape[1] != B.shape[0]:
  18. raise ValueError('incompatible shapes')
  19. self.A = A
  20. self.B = B
  21. self.ndim = 2
  22. self.shape = (A.shape[0], B.shape[1])
  23. def _matvec(self, x):
  24. return np.dot(self.A, np.dot(self.B, x))
  25. def _rmatvec(self, x):
  26. return np.dot(np.dot(x, self.A), self.B)
  27. def _matmat(self, X):
  28. return np.dot(self.A, np.dot(self.B, X))
  29. @property
  30. def T(self):
  31. return MatrixProductOperator(self.B.T, self.A.T)
  32. class TestOnenormest(object):
  33. @pytest.mark.xslow
  34. def test_onenormest_table_3_t_2(self):
  35. # This will take multiple seconds if your computer is slow like mine.
  36. # It is stochastic, so the tolerance could be too strict.
  37. np.random.seed(1234)
  38. t = 2
  39. n = 100
  40. itmax = 5
  41. nsamples = 5000
  42. observed = []
  43. expected = []
  44. nmult_list = []
  45. nresample_list = []
  46. for i in range(nsamples):
  47. A = scipy.linalg.inv(np.random.randn(n, n))
  48. est, v, w, nmults, nresamples = _onenormest_core(A, A.T, t, itmax)
  49. observed.append(est)
  50. expected.append(scipy.linalg.norm(A, 1))
  51. nmult_list.append(nmults)
  52. nresample_list.append(nresamples)
  53. observed = np.array(observed, dtype=float)
  54. expected = np.array(expected, dtype=float)
  55. relative_errors = np.abs(observed - expected) / expected
  56. # check the mean underestimation ratio
  57. underestimation_ratio = observed / expected
  58. assert_(0.99 < np.mean(underestimation_ratio) < 1.0)
  59. # check the max and mean required column resamples
  60. assert_equal(np.max(nresample_list), 2)
  61. assert_(0.05 < np.mean(nresample_list) < 0.2)
  62. # check the proportion of norms computed exactly correctly
  63. nexact = np.count_nonzero(relative_errors < 1e-14)
  64. proportion_exact = nexact / float(nsamples)
  65. assert_(0.9 < proportion_exact < 0.95)
  66. # check the average number of matrix*vector multiplications
  67. assert_(3.5 < np.mean(nmult_list) < 4.5)
  68. @pytest.mark.xslow
  69. def test_onenormest_table_4_t_7(self):
  70. # This will take multiple seconds if your computer is slow like mine.
  71. # It is stochastic, so the tolerance could be too strict.
  72. np.random.seed(1234)
  73. t = 7
  74. n = 100
  75. itmax = 5
  76. nsamples = 5000
  77. observed = []
  78. expected = []
  79. nmult_list = []
  80. nresample_list = []
  81. for i in range(nsamples):
  82. A = np.random.randint(-1, 2, size=(n, n))
  83. est, v, w, nmults, nresamples = _onenormest_core(A, A.T, t, itmax)
  84. observed.append(est)
  85. expected.append(scipy.linalg.norm(A, 1))
  86. nmult_list.append(nmults)
  87. nresample_list.append(nresamples)
  88. observed = np.array(observed, dtype=float)
  89. expected = np.array(expected, dtype=float)
  90. relative_errors = np.abs(observed - expected) / expected
  91. # check the mean underestimation ratio
  92. underestimation_ratio = observed / expected
  93. assert_(0.90 < np.mean(underestimation_ratio) < 0.99)
  94. # check the required column resamples
  95. assert_equal(np.max(nresample_list), 0)
  96. # check the proportion of norms computed exactly correctly
  97. nexact = np.count_nonzero(relative_errors < 1e-14)
  98. proportion_exact = nexact / float(nsamples)
  99. assert_(0.15 < proportion_exact < 0.25)
  100. # check the average number of matrix*vector multiplications
  101. assert_(3.5 < np.mean(nmult_list) < 4.5)
  102. def test_onenormest_table_5_t_1(self):
  103. # "note that there is no randomness and hence only one estimate for t=1"
  104. t = 1
  105. n = 100
  106. itmax = 5
  107. alpha = 1 - 1e-6
  108. A = -scipy.linalg.inv(np.identity(n) + alpha*np.eye(n, k=1))
  109. first_col = np.array([1] + [0]*(n-1))
  110. first_row = np.array([(-alpha)**i for i in range(n)])
  111. B = -scipy.linalg.toeplitz(first_col, first_row)
  112. assert_allclose(A, B)
  113. est, v, w, nmults, nresamples = _onenormest_core(B, B.T, t, itmax)
  114. exact_value = scipy.linalg.norm(B, 1)
  115. underest_ratio = est / exact_value
  116. assert_allclose(underest_ratio, 0.05, rtol=1e-4)
  117. assert_equal(nmults, 11)
  118. assert_equal(nresamples, 0)
  119. # check the non-underscored version of onenormest
  120. est_plain = scipy.sparse.linalg.onenormest(B, t=t, itmax=itmax)
  121. assert_allclose(est, est_plain)
  122. @pytest.mark.xslow
  123. def test_onenormest_table_6_t_1(self):
  124. #TODO this test seems to give estimates that match the table,
  125. #TODO even though no attempt has been made to deal with
  126. #TODO complex numbers in the one-norm estimation.
  127. # This will take multiple seconds if your computer is slow like mine.
  128. # It is stochastic, so the tolerance could be too strict.
  129. np.random.seed(1234)
  130. t = 1
  131. n = 100
  132. itmax = 5
  133. nsamples = 5000
  134. observed = []
  135. expected = []
  136. nmult_list = []
  137. nresample_list = []
  138. for i in range(nsamples):
  139. A_inv = np.random.rand(n, n) + 1j * np.random.rand(n, n)
  140. A = scipy.linalg.inv(A_inv)
  141. est, v, w, nmults, nresamples = _onenormest_core(A, A.T, t, itmax)
  142. observed.append(est)
  143. expected.append(scipy.linalg.norm(A, 1))
  144. nmult_list.append(nmults)
  145. nresample_list.append(nresamples)
  146. observed = np.array(observed, dtype=float)
  147. expected = np.array(expected, dtype=float)
  148. relative_errors = np.abs(observed - expected) / expected
  149. # check the mean underestimation ratio
  150. underestimation_ratio = observed / expected
  151. underestimation_ratio_mean = np.mean(underestimation_ratio)
  152. assert_(0.90 < underestimation_ratio_mean < 0.99)
  153. # check the required column resamples
  154. max_nresamples = np.max(nresample_list)
  155. assert_equal(max_nresamples, 0)
  156. # check the proportion of norms computed exactly correctly
  157. nexact = np.count_nonzero(relative_errors < 1e-14)
  158. proportion_exact = nexact / float(nsamples)
  159. assert_(0.7 < proportion_exact < 0.8)
  160. # check the average number of matrix*vector multiplications
  161. mean_nmult = np.mean(nmult_list)
  162. assert_(4 < mean_nmult < 5)
  163. def _help_product_norm_slow(self, A, B):
  164. # for profiling
  165. C = np.dot(A, B)
  166. return scipy.linalg.norm(C, 1)
  167. def _help_product_norm_fast(self, A, B):
  168. # for profiling
  169. t = 2
  170. itmax = 5
  171. D = MatrixProductOperator(A, B)
  172. est, v, w, nmults, nresamples = _onenormest_core(D, D.T, t, itmax)
  173. return est
  174. @pytest.mark.slow
  175. def test_onenormest_linear_operator(self):
  176. # Define a matrix through its product A B.
  177. # Depending on the shapes of A and B,
  178. # it could be easy to multiply this product by a small matrix,
  179. # but it could be annoying to look at all of
  180. # the entries of the product explicitly.
  181. np.random.seed(1234)
  182. n = 6000
  183. k = 3
  184. A = np.random.randn(n, k)
  185. B = np.random.randn(k, n)
  186. fast_estimate = self._help_product_norm_fast(A, B)
  187. exact_value = self._help_product_norm_slow(A, B)
  188. assert_(fast_estimate <= exact_value <= 3*fast_estimate,
  189. 'fast: %g\nexact:%g' % (fast_estimate, exact_value))
  190. def test_returns(self):
  191. np.random.seed(1234)
  192. A = scipy.sparse.rand(50, 50, 0.1)
  193. s0 = scipy.linalg.norm(A.todense(), 1)
  194. s1, v = scipy.sparse.linalg.onenormest(A, compute_v=True)
  195. s2, w = scipy.sparse.linalg.onenormest(A, compute_w=True)
  196. s3, v2, w2 = scipy.sparse.linalg.onenormest(A, compute_w=True, compute_v=True)
  197. assert_allclose(s1, s0, rtol=1e-9)
  198. assert_allclose(np.linalg.norm(A.dot(v), 1), s0*np.linalg.norm(v, 1), rtol=1e-9)
  199. assert_allclose(A.dot(v), w, rtol=1e-9)
  200. class TestAlgorithm_2_2(object):
  201. def test_randn_inv(self):
  202. np.random.seed(1234)
  203. n = 20
  204. nsamples = 100
  205. for i in range(nsamples):
  206. # Choose integer t uniformly between 1 and 3 inclusive.
  207. t = np.random.randint(1, 4)
  208. # Choose n uniformly between 10 and 40 inclusive.
  209. n = np.random.randint(10, 41)
  210. # Sample the inverse of a matrix with random normal entries.
  211. A = scipy.linalg.inv(np.random.randn(n, n))
  212. # Compute the 1-norm bounds.
  213. g, ind = _algorithm_2_2(A, A.T, t)