test__spectral.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import itertools
  2. import numpy as np
  3. from numpy import exp
  4. from numpy.testing import assert_, assert_equal
  5. from scipy.optimize import root
  6. def test_performance():
  7. # Compare performance results to those listed in
  8. # [Cheng & Li, IMA J. Num. An. 29, 814 (2008)]
  9. # and
  10. # [W. La Cruz, J.M. Martinez, M. Raydan, Math. Comp. 75, 1429 (2006)].
  11. # and those produced by dfsane.f from M. Raydan's website.
  12. #
  13. # Where the results disagree, the largest limits are taken.
  14. e_a = 1e-5
  15. e_r = 1e-4
  16. table_1 = [
  17. dict(F=F_1, x0=x0_1, n=1000, nit=5, nfev=5),
  18. dict(F=F_1, x0=x0_1, n=10000, nit=2, nfev=2),
  19. dict(F=F_2, x0=x0_2, n=500, nit=11, nfev=11),
  20. dict(F=F_2, x0=x0_2, n=2000, nit=11, nfev=11),
  21. # dict(F=F_4, x0=x0_4, n=999, nit=243, nfev=1188), removed: too sensitive to rounding errors
  22. dict(F=F_6, x0=x0_6, n=100, nit=6, nfev=6), # Results from dfsane.f; papers list nit=3, nfev=3
  23. dict(F=F_7, x0=x0_7, n=99, nit=23, nfev=29), # Must have n%3==0, typo in papers?
  24. dict(F=F_7, x0=x0_7, n=999, nit=23, nfev=29), # Must have n%3==0, typo in papers?
  25. dict(F=F_9, x0=x0_9, n=100, nit=12, nfev=18), # Results from dfsane.f; papers list nit=nfev=6?
  26. dict(F=F_9, x0=x0_9, n=1000, nit=12, nfev=18),
  27. dict(F=F_10, x0=x0_10, n=1000, nit=5, nfev=5), # Results from dfsane.f; papers list nit=2, nfev=12
  28. ]
  29. # Check also scaling invariance
  30. for xscale, yscale, line_search in itertools.product([1.0, 1e-10, 1e10], [1.0, 1e-10, 1e10],
  31. ['cruz', 'cheng']):
  32. for problem in table_1:
  33. n = problem['n']
  34. func = lambda x, n: yscale*problem['F'](x/xscale, n)
  35. args = (n,)
  36. x0 = problem['x0'](n) * xscale
  37. fatol = np.sqrt(n) * e_a * yscale + e_r * np.linalg.norm(func(x0, n))
  38. sigma_eps = 1e-10 * min(yscale/xscale, xscale/yscale)
  39. sigma_0 = xscale/yscale
  40. with np.errstate(over='ignore'):
  41. sol = root(func, x0, args=args,
  42. options=dict(ftol=0, fatol=fatol, maxfev=problem['nfev'] + 1,
  43. sigma_0=sigma_0, sigma_eps=sigma_eps,
  44. line_search=line_search),
  45. method='DF-SANE')
  46. err_msg = repr([xscale, yscale, line_search, problem, np.linalg.norm(func(sol.x, n)),
  47. fatol, sol.success, sol.nit, sol.nfev])
  48. assert_(sol.success, err_msg)
  49. assert_(sol.nfev <= problem['nfev'] + 1, err_msg) # nfev+1: dfsane.f doesn't count first eval
  50. assert_(sol.nit <= problem['nit'], err_msg)
  51. assert_(np.linalg.norm(func(sol.x, n)) <= fatol, err_msg)
  52. def test_complex():
  53. def func(z):
  54. return z**2 - 1 + 2j
  55. x0 = 2.0j
  56. ftol = 1e-4
  57. sol = root(func, x0, tol=ftol, method='DF-SANE')
  58. assert_(sol.success)
  59. f0 = np.linalg.norm(func(x0))
  60. fx = np.linalg.norm(func(sol.x))
  61. assert_(fx <= ftol*f0)
  62. def test_linear_definite():
  63. # The DF-SANE paper proves convergence for "strongly isolated"
  64. # solutions.
  65. #
  66. # For linear systems F(x) = A x - b = 0, with A positive or
  67. # negative definite, the solution is strongly isolated.
  68. def check_solvability(A, b, line_search='cruz'):
  69. func = lambda x: A.dot(x) - b
  70. xp = np.linalg.solve(A, b)
  71. eps = np.linalg.norm(func(xp)) * 1e3
  72. sol = root(func, b, options=dict(fatol=eps, ftol=0, maxfev=17523, line_search=line_search),
  73. method='DF-SANE')
  74. assert_(sol.success)
  75. assert_(np.linalg.norm(func(sol.x)) <= eps)
  76. n = 90
  77. # Test linear pos.def. system
  78. np.random.seed(1234)
  79. A = np.arange(n*n).reshape(n, n)
  80. A = A + n*n * np.diag(1 + np.arange(n))
  81. assert_(np.linalg.eigvals(A).min() > 0)
  82. b = np.arange(n) * 1.0
  83. check_solvability(A, b, 'cruz')
  84. check_solvability(A, b, 'cheng')
  85. # Test linear neg.def. system
  86. check_solvability(-A, b, 'cruz')
  87. check_solvability(-A, b, 'cheng')
  88. def test_shape():
  89. def f(x, arg):
  90. return x - arg
  91. for dt in [float, complex]:
  92. x = np.zeros([2,2])
  93. arg = np.ones([2,2], dtype=dt)
  94. sol = root(f, x, args=(arg,), method='DF-SANE')
  95. assert_(sol.success)
  96. assert_equal(sol.x.shape, x.shape)
  97. # Some of the test functions and initial guesses listed in
  98. # [W. La Cruz, M. Raydan. Optimization Methods and Software, 18, 583 (2003)]
  99. def F_1(x, n):
  100. g = np.zeros([n])
  101. i = np.arange(2, n+1)
  102. g[0] = exp(x[0] - 1) - 1
  103. g[1:] = i*(exp(x[1:] - 1) - x[1:])
  104. return g
  105. def x0_1(n):
  106. x0 = np.empty([n])
  107. x0.fill(n/(n-1))
  108. return x0
  109. def F_2(x, n):
  110. g = np.zeros([n])
  111. i = np.arange(2, n+1)
  112. g[0] = exp(x[0]) - 1
  113. g[1:] = 0.1*i*(exp(x[1:]) + x[:-1] - 1)
  114. return g
  115. def x0_2(n):
  116. x0 = np.empty([n])
  117. x0.fill(1/n**2)
  118. return x0
  119. def F_4(x, n):
  120. assert_equal(n % 3, 0)
  121. g = np.zeros([n])
  122. # Note: the first line is typoed in some of the references;
  123. # correct in original [Gasparo, Optimization Meth. 13, 79 (2000)]
  124. g[::3] = 0.6 * x[::3] + 1.6 * x[1::3]**3 - 7.2 * x[1::3]**2 + 9.6 * x[1::3] - 4.8
  125. g[1::3] = 0.48 * x[::3] - 0.72 * x[1::3]**3 + 3.24 * x[1::3]**2 - 4.32 * x[1::3] - x[2::3] + 0.2 * x[2::3]**3 + 2.16
  126. g[2::3] = 1.25 * x[2::3] - 0.25*x[2::3]**3
  127. return g
  128. def x0_4(n):
  129. assert_equal(n % 3, 0)
  130. x0 = np.array([-1, 1/2, -1] * (n//3))
  131. return x0
  132. def F_6(x, n):
  133. c = 0.9
  134. mu = (np.arange(1, n+1) - 0.5)/n
  135. return x - 1/(1 - c/(2*n) * (mu[:,None]*x / (mu[:,None] + mu)).sum(axis=1))
  136. def x0_6(n):
  137. return np.ones([n])
  138. def F_7(x, n):
  139. assert_equal(n % 3, 0)
  140. def phi(t):
  141. v = 0.5*t - 2
  142. v[t > -1] = ((-592*t**3 + 888*t**2 + 4551*t - 1924)/1998)[t > -1]
  143. v[t >= 2] = (0.5*t + 2)[t >= 2]
  144. return v
  145. g = np.zeros([n])
  146. g[::3] = 1e4 * x[1::3]**2 - 1
  147. g[1::3] = exp(-x[::3]) + exp(-x[1::3]) - 1.0001
  148. g[2::3] = phi(x[2::3])
  149. return g
  150. def x0_7(n):
  151. assert_equal(n % 3, 0)
  152. return np.array([1e-3, 18, 1] * (n//3))
  153. def F_9(x, n):
  154. g = np.zeros([n])
  155. i = np.arange(2, n)
  156. g[0] = x[0]**3/3 + x[1]**2/2
  157. g[1:-1] = -x[1:-1]**2/2 + i*x[1:-1]**3/3 + x[2:]**2/2
  158. g[-1] = -x[-1]**2/2 + n*x[-1]**3/3
  159. return g
  160. def x0_9(n):
  161. return np.ones([n])
  162. def F_10(x, n):
  163. return np.log(1 + x) - x/n
  164. def x0_10(n):
  165. return np.ones([n])