nonlin.py 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545
  1. r"""
  2. Nonlinear solvers
  3. -----------------
  4. .. currentmodule:: scipy.optimize
  5. This is a collection of general-purpose nonlinear multidimensional
  6. solvers. These solvers find *x* for which *F(x) = 0*. Both *x*
  7. and *F* can be multidimensional.
  8. Routines
  9. ~~~~~~~~
  10. Large-scale nonlinear solvers:
  11. .. autosummary::
  12. newton_krylov
  13. anderson
  14. General nonlinear solvers:
  15. .. autosummary::
  16. broyden1
  17. broyden2
  18. Simple iterations:
  19. .. autosummary::
  20. excitingmixing
  21. linearmixing
  22. diagbroyden
  23. Examples
  24. ~~~~~~~~
  25. **Small problem**
  26. >>> def F(x):
  27. ... return np.cos(x) + x[::-1] - [1, 2, 3, 4]
  28. >>> import scipy.optimize
  29. >>> x = scipy.optimize.broyden1(F, [1,1,1,1], f_tol=1e-14)
  30. >>> x
  31. array([ 4.04674914, 3.91158389, 2.71791677, 1.61756251])
  32. >>> np.cos(x) + x[::-1]
  33. array([ 1., 2., 3., 4.])
  34. **Large problem**
  35. Suppose that we needed to solve the following integrodifferential
  36. equation on the square :math:`[0,1]\times[0,1]`:
  37. .. math::
  38. \nabla^2 P = 10 \left(\int_0^1\int_0^1\cosh(P)\,dx\,dy\right)^2
  39. with :math:`P(x,1) = 1` and :math:`P=0` elsewhere on the boundary of
  40. the square.
  41. The solution can be found using the `newton_krylov` solver:
  42. .. plot::
  43. import numpy as np
  44. from scipy.optimize import newton_krylov
  45. from numpy import cosh, zeros_like, mgrid, zeros
  46. # parameters
  47. nx, ny = 75, 75
  48. hx, hy = 1./(nx-1), 1./(ny-1)
  49. P_left, P_right = 0, 0
  50. P_top, P_bottom = 1, 0
  51. def residual(P):
  52. d2x = zeros_like(P)
  53. d2y = zeros_like(P)
  54. d2x[1:-1] = (P[2:] - 2*P[1:-1] + P[:-2]) / hx/hx
  55. d2x[0] = (P[1] - 2*P[0] + P_left)/hx/hx
  56. d2x[-1] = (P_right - 2*P[-1] + P[-2])/hx/hx
  57. d2y[:,1:-1] = (P[:,2:] - 2*P[:,1:-1] + P[:,:-2])/hy/hy
  58. d2y[:,0] = (P[:,1] - 2*P[:,0] + P_bottom)/hy/hy
  59. d2y[:,-1] = (P_top - 2*P[:,-1] + P[:,-2])/hy/hy
  60. return d2x + d2y - 10*cosh(P).mean()**2
  61. # solve
  62. guess = zeros((nx, ny), float)
  63. sol = newton_krylov(residual, guess, method='lgmres', verbose=1)
  64. print('Residual: %g' % abs(residual(sol)).max())
  65. # visualize
  66. import matplotlib.pyplot as plt
  67. x, y = mgrid[0:1:(nx*1j), 0:1:(ny*1j)]
  68. plt.pcolor(x, y, sol)
  69. plt.colorbar()
  70. plt.show()
  71. """
  72. # Copyright (C) 2009, Pauli Virtanen <pav@iki.fi>
  73. # Distributed under the same license as Scipy.
  74. from __future__ import division, print_function, absolute_import
  75. import sys
  76. import numpy as np
  77. from scipy._lib.six import callable, exec_, xrange
  78. from scipy.linalg import norm, solve, inv, qr, svd, LinAlgError
  79. from numpy import asarray, dot, vdot
  80. import scipy.sparse.linalg
  81. import scipy.sparse
  82. from scipy.linalg import get_blas_funcs
  83. import inspect
  84. from scipy._lib._util import getargspec_no_self as _getargspec
  85. from .linesearch import scalar_search_wolfe1, scalar_search_armijo
  86. __all__ = [
  87. 'broyden1', 'broyden2', 'anderson', 'linearmixing',
  88. 'diagbroyden', 'excitingmixing', 'newton_krylov']
  89. #------------------------------------------------------------------------------
  90. # Utility functions
  91. #------------------------------------------------------------------------------
  92. class NoConvergence(Exception):
  93. pass
  94. def maxnorm(x):
  95. return np.absolute(x).max()
  96. def _as_inexact(x):
  97. """Return `x` as an array, of either floats or complex floats"""
  98. x = asarray(x)
  99. if not np.issubdtype(x.dtype, np.inexact):
  100. return asarray(x, dtype=np.float_)
  101. return x
  102. def _array_like(x, x0):
  103. """Return ndarray `x` as same array subclass and shape as `x0`"""
  104. x = np.reshape(x, np.shape(x0))
  105. wrap = getattr(x0, '__array_wrap__', x.__array_wrap__)
  106. return wrap(x)
  107. def _safe_norm(v):
  108. if not np.isfinite(v).all():
  109. return np.array(np.inf)
  110. return norm(v)
  111. #------------------------------------------------------------------------------
  112. # Generic nonlinear solver machinery
  113. #------------------------------------------------------------------------------
  114. _doc_parts = dict(
  115. params_basic="""
  116. F : function(x) -> f
  117. Function whose root to find; should take and return an array-like
  118. object.
  119. xin : array_like
  120. Initial guess for the solution
  121. """.strip(),
  122. params_extra="""
  123. iter : int, optional
  124. Number of iterations to make. If omitted (default), make as many
  125. as required to meet tolerances.
  126. verbose : bool, optional
  127. Print status to stdout on every iteration.
  128. maxiter : int, optional
  129. Maximum number of iterations to make. If more are needed to
  130. meet convergence, `NoConvergence` is raised.
  131. f_tol : float, optional
  132. Absolute tolerance (in max-norm) for the residual.
  133. If omitted, default is 6e-6.
  134. f_rtol : float, optional
  135. Relative tolerance for the residual. If omitted, not used.
  136. x_tol : float, optional
  137. Absolute minimum step size, as determined from the Jacobian
  138. approximation. If the step size is smaller than this, optimization
  139. is terminated as successful. If omitted, not used.
  140. x_rtol : float, optional
  141. Relative minimum step size. If omitted, not used.
  142. tol_norm : function(vector) -> scalar, optional
  143. Norm to use in convergence check. Default is the maximum norm.
  144. line_search : {None, 'armijo' (default), 'wolfe'}, optional
  145. Which type of a line search to use to determine the step size in the
  146. direction given by the Jacobian approximation. Defaults to 'armijo'.
  147. callback : function, optional
  148. Optional callback function. It is called on every iteration as
  149. ``callback(x, f)`` where `x` is the current solution and `f`
  150. the corresponding residual.
  151. Returns
  152. -------
  153. sol : ndarray
  154. An array (of similar array type as `x0`) containing the final solution.
  155. Raises
  156. ------
  157. NoConvergence
  158. When a solution was not found.
  159. """.strip()
  160. )
  161. def _set_doc(obj):
  162. if obj.__doc__:
  163. obj.__doc__ = obj.__doc__ % _doc_parts
  164. def nonlin_solve(F, x0, jacobian='krylov', iter=None, verbose=False,
  165. maxiter=None, f_tol=None, f_rtol=None, x_tol=None, x_rtol=None,
  166. tol_norm=None, line_search='armijo', callback=None,
  167. full_output=False, raise_exception=True):
  168. """
  169. Find a root of a function, in a way suitable for large-scale problems.
  170. Parameters
  171. ----------
  172. %(params_basic)s
  173. jacobian : Jacobian
  174. A Jacobian approximation: `Jacobian` object or something that
  175. `asjacobian` can transform to one. Alternatively, a string specifying
  176. which of the builtin Jacobian approximations to use:
  177. krylov, broyden1, broyden2, anderson
  178. diagbroyden, linearmixing, excitingmixing
  179. %(params_extra)s
  180. full_output : bool
  181. If true, returns a dictionary `info` containing convergence
  182. information.
  183. raise_exception : bool
  184. If True, a `NoConvergence` exception is raise if no solution is found.
  185. See Also
  186. --------
  187. asjacobian, Jacobian
  188. Notes
  189. -----
  190. This algorithm implements the inexact Newton method, with
  191. backtracking or full line searches. Several Jacobian
  192. approximations are available, including Krylov and Quasi-Newton
  193. methods.
  194. References
  195. ----------
  196. .. [KIM] C. T. Kelley, \"Iterative Methods for Linear and Nonlinear
  197. Equations\". Society for Industrial and Applied Mathematics. (1995)
  198. https://archive.siam.org/books/kelley/fr16/
  199. """
  200. # Can't use default parameters because it's being explicitly passed as None
  201. # from the calling function, so we need to set it here.
  202. tol_norm = maxnorm if tol_norm is None else tol_norm
  203. condition = TerminationCondition(f_tol=f_tol, f_rtol=f_rtol,
  204. x_tol=x_tol, x_rtol=x_rtol,
  205. iter=iter, norm=tol_norm)
  206. x0 = _as_inexact(x0)
  207. func = lambda z: _as_inexact(F(_array_like(z, x0))).flatten()
  208. x = x0.flatten()
  209. dx = np.inf
  210. Fx = func(x)
  211. Fx_norm = norm(Fx)
  212. jacobian = asjacobian(jacobian)
  213. jacobian.setup(x.copy(), Fx, func)
  214. if maxiter is None:
  215. if iter is not None:
  216. maxiter = iter + 1
  217. else:
  218. maxiter = 100*(x.size+1)
  219. if line_search is True:
  220. line_search = 'armijo'
  221. elif line_search is False:
  222. line_search = None
  223. if line_search not in (None, 'armijo', 'wolfe'):
  224. raise ValueError("Invalid line search")
  225. # Solver tolerance selection
  226. gamma = 0.9
  227. eta_max = 0.9999
  228. eta_treshold = 0.1
  229. eta = 1e-3
  230. for n in xrange(maxiter):
  231. status = condition.check(Fx, x, dx)
  232. if status:
  233. break
  234. # The tolerance, as computed for scipy.sparse.linalg.* routines
  235. tol = min(eta, eta*Fx_norm)
  236. dx = -jacobian.solve(Fx, tol=tol)
  237. if norm(dx) == 0:
  238. raise ValueError("Jacobian inversion yielded zero vector. "
  239. "This indicates a bug in the Jacobian "
  240. "approximation.")
  241. # Line search, or Newton step
  242. if line_search:
  243. s, x, Fx, Fx_norm_new = _nonlin_line_search(func, x, Fx, dx,
  244. line_search)
  245. else:
  246. s = 1.0
  247. x = x + dx
  248. Fx = func(x)
  249. Fx_norm_new = norm(Fx)
  250. jacobian.update(x.copy(), Fx)
  251. if callback:
  252. callback(x, Fx)
  253. # Adjust forcing parameters for inexact methods
  254. eta_A = gamma * Fx_norm_new**2 / Fx_norm**2
  255. if gamma * eta**2 < eta_treshold:
  256. eta = min(eta_max, eta_A)
  257. else:
  258. eta = min(eta_max, max(eta_A, gamma*eta**2))
  259. Fx_norm = Fx_norm_new
  260. # Print status
  261. if verbose:
  262. sys.stdout.write("%d: |F(x)| = %g; step %g\n" % (
  263. n, tol_norm(Fx), s))
  264. sys.stdout.flush()
  265. else:
  266. if raise_exception:
  267. raise NoConvergence(_array_like(x, x0))
  268. else:
  269. status = 2
  270. if full_output:
  271. info = {'nit': condition.iteration,
  272. 'fun': Fx,
  273. 'status': status,
  274. 'success': status == 1,
  275. 'message': {1: 'A solution was found at the specified '
  276. 'tolerance.',
  277. 2: 'The maximum number of iterations allowed '
  278. 'has been reached.'
  279. }[status]
  280. }
  281. return _array_like(x, x0), info
  282. else:
  283. return _array_like(x, x0)
  284. _set_doc(nonlin_solve)
  285. def _nonlin_line_search(func, x, Fx, dx, search_type='armijo', rdiff=1e-8,
  286. smin=1e-2):
  287. tmp_s = [0]
  288. tmp_Fx = [Fx]
  289. tmp_phi = [norm(Fx)**2]
  290. s_norm = norm(x) / norm(dx)
  291. def phi(s, store=True):
  292. if s == tmp_s[0]:
  293. return tmp_phi[0]
  294. xt = x + s*dx
  295. v = func(xt)
  296. p = _safe_norm(v)**2
  297. if store:
  298. tmp_s[0] = s
  299. tmp_phi[0] = p
  300. tmp_Fx[0] = v
  301. return p
  302. def derphi(s):
  303. ds = (abs(s) + s_norm + 1) * rdiff
  304. return (phi(s+ds, store=False) - phi(s)) / ds
  305. if search_type == 'wolfe':
  306. s, phi1, phi0 = scalar_search_wolfe1(phi, derphi, tmp_phi[0],
  307. xtol=1e-2, amin=smin)
  308. elif search_type == 'armijo':
  309. s, phi1 = scalar_search_armijo(phi, tmp_phi[0], -tmp_phi[0],
  310. amin=smin)
  311. if s is None:
  312. # XXX: No suitable step length found. Take the full Newton step,
  313. # and hope for the best.
  314. s = 1.0
  315. x = x + s*dx
  316. if s == tmp_s[0]:
  317. Fx = tmp_Fx[0]
  318. else:
  319. Fx = func(x)
  320. Fx_norm = norm(Fx)
  321. return s, x, Fx, Fx_norm
  322. class TerminationCondition(object):
  323. """
  324. Termination condition for an iteration. It is terminated if
  325. - |F| < f_rtol*|F_0|, AND
  326. - |F| < f_tol
  327. AND
  328. - |dx| < x_rtol*|x|, AND
  329. - |dx| < x_tol
  330. """
  331. def __init__(self, f_tol=None, f_rtol=None, x_tol=None, x_rtol=None,
  332. iter=None, norm=maxnorm):
  333. if f_tol is None:
  334. f_tol = np.finfo(np.float_).eps ** (1./3)
  335. if f_rtol is None:
  336. f_rtol = np.inf
  337. if x_tol is None:
  338. x_tol = np.inf
  339. if x_rtol is None:
  340. x_rtol = np.inf
  341. self.x_tol = x_tol
  342. self.x_rtol = x_rtol
  343. self.f_tol = f_tol
  344. self.f_rtol = f_rtol
  345. self.norm = norm
  346. self.iter = iter
  347. self.f0_norm = None
  348. self.iteration = 0
  349. def check(self, f, x, dx):
  350. self.iteration += 1
  351. f_norm = self.norm(f)
  352. x_norm = self.norm(x)
  353. dx_norm = self.norm(dx)
  354. if self.f0_norm is None:
  355. self.f0_norm = f_norm
  356. if f_norm == 0:
  357. return 1
  358. if self.iter is not None:
  359. # backwards compatibility with Scipy 0.6.0
  360. return 2 * (self.iteration > self.iter)
  361. # NB: condition must succeed for rtol=inf even if norm == 0
  362. return int((f_norm <= self.f_tol
  363. and f_norm/self.f_rtol <= self.f0_norm)
  364. and (dx_norm <= self.x_tol
  365. and dx_norm/self.x_rtol <= x_norm))
  366. #------------------------------------------------------------------------------
  367. # Generic Jacobian approximation
  368. #------------------------------------------------------------------------------
  369. class Jacobian(object):
  370. """
  371. Common interface for Jacobians or Jacobian approximations.
  372. The optional methods come useful when implementing trust region
  373. etc. algorithms that often require evaluating transposes of the
  374. Jacobian.
  375. Methods
  376. -------
  377. solve
  378. Returns J^-1 * v
  379. update
  380. Updates Jacobian to point `x` (where the function has residual `Fx`)
  381. matvec : optional
  382. Returns J * v
  383. rmatvec : optional
  384. Returns A^H * v
  385. rsolve : optional
  386. Returns A^-H * v
  387. matmat : optional
  388. Returns A * V, where V is a dense matrix with dimensions (N,K).
  389. todense : optional
  390. Form the dense Jacobian matrix. Necessary for dense trust region
  391. algorithms, and useful for testing.
  392. Attributes
  393. ----------
  394. shape
  395. Matrix dimensions (M, N)
  396. dtype
  397. Data type of the matrix.
  398. func : callable, optional
  399. Function the Jacobian corresponds to
  400. """
  401. def __init__(self, **kw):
  402. names = ["solve", "update", "matvec", "rmatvec", "rsolve",
  403. "matmat", "todense", "shape", "dtype"]
  404. for name, value in kw.items():
  405. if name not in names:
  406. raise ValueError("Unknown keyword argument %s" % name)
  407. if value is not None:
  408. setattr(self, name, kw[name])
  409. if hasattr(self, 'todense'):
  410. self.__array__ = lambda: self.todense()
  411. def aspreconditioner(self):
  412. return InverseJacobian(self)
  413. def solve(self, v, tol=0):
  414. raise NotImplementedError
  415. def update(self, x, F):
  416. pass
  417. def setup(self, x, F, func):
  418. self.func = func
  419. self.shape = (F.size, x.size)
  420. self.dtype = F.dtype
  421. if self.__class__.setup is Jacobian.setup:
  422. # Call on the first point unless overridden
  423. self.update(x, F)
  424. class InverseJacobian(object):
  425. def __init__(self, jacobian):
  426. self.jacobian = jacobian
  427. self.matvec = jacobian.solve
  428. self.update = jacobian.update
  429. if hasattr(jacobian, 'setup'):
  430. self.setup = jacobian.setup
  431. if hasattr(jacobian, 'rsolve'):
  432. self.rmatvec = jacobian.rsolve
  433. @property
  434. def shape(self):
  435. return self.jacobian.shape
  436. @property
  437. def dtype(self):
  438. return self.jacobian.dtype
  439. def asjacobian(J):
  440. """
  441. Convert given object to one suitable for use as a Jacobian.
  442. """
  443. spsolve = scipy.sparse.linalg.spsolve
  444. if isinstance(J, Jacobian):
  445. return J
  446. elif inspect.isclass(J) and issubclass(J, Jacobian):
  447. return J()
  448. elif isinstance(J, np.ndarray):
  449. if J.ndim > 2:
  450. raise ValueError('array must have rank <= 2')
  451. J = np.atleast_2d(np.asarray(J))
  452. if J.shape[0] != J.shape[1]:
  453. raise ValueError('array must be square')
  454. return Jacobian(matvec=lambda v: dot(J, v),
  455. rmatvec=lambda v: dot(J.conj().T, v),
  456. solve=lambda v: solve(J, v),
  457. rsolve=lambda v: solve(J.conj().T, v),
  458. dtype=J.dtype, shape=J.shape)
  459. elif scipy.sparse.isspmatrix(J):
  460. if J.shape[0] != J.shape[1]:
  461. raise ValueError('matrix must be square')
  462. return Jacobian(matvec=lambda v: J*v,
  463. rmatvec=lambda v: J.conj().T * v,
  464. solve=lambda v: spsolve(J, v),
  465. rsolve=lambda v: spsolve(J.conj().T, v),
  466. dtype=J.dtype, shape=J.shape)
  467. elif hasattr(J, 'shape') and hasattr(J, 'dtype') and hasattr(J, 'solve'):
  468. return Jacobian(matvec=getattr(J, 'matvec'),
  469. rmatvec=getattr(J, 'rmatvec'),
  470. solve=J.solve,
  471. rsolve=getattr(J, 'rsolve'),
  472. update=getattr(J, 'update'),
  473. setup=getattr(J, 'setup'),
  474. dtype=J.dtype,
  475. shape=J.shape)
  476. elif callable(J):
  477. # Assume it's a function J(x) that returns the Jacobian
  478. class Jac(Jacobian):
  479. def update(self, x, F):
  480. self.x = x
  481. def solve(self, v, tol=0):
  482. m = J(self.x)
  483. if isinstance(m, np.ndarray):
  484. return solve(m, v)
  485. elif scipy.sparse.isspmatrix(m):
  486. return spsolve(m, v)
  487. else:
  488. raise ValueError("Unknown matrix type")
  489. def matvec(self, v):
  490. m = J(self.x)
  491. if isinstance(m, np.ndarray):
  492. return dot(m, v)
  493. elif scipy.sparse.isspmatrix(m):
  494. return m*v
  495. else:
  496. raise ValueError("Unknown matrix type")
  497. def rsolve(self, v, tol=0):
  498. m = J(self.x)
  499. if isinstance(m, np.ndarray):
  500. return solve(m.conj().T, v)
  501. elif scipy.sparse.isspmatrix(m):
  502. return spsolve(m.conj().T, v)
  503. else:
  504. raise ValueError("Unknown matrix type")
  505. def rmatvec(self, v):
  506. m = J(self.x)
  507. if isinstance(m, np.ndarray):
  508. return dot(m.conj().T, v)
  509. elif scipy.sparse.isspmatrix(m):
  510. return m.conj().T * v
  511. else:
  512. raise ValueError("Unknown matrix type")
  513. return Jac()
  514. elif isinstance(J, str):
  515. return dict(broyden1=BroydenFirst,
  516. broyden2=BroydenSecond,
  517. anderson=Anderson,
  518. diagbroyden=DiagBroyden,
  519. linearmixing=LinearMixing,
  520. excitingmixing=ExcitingMixing,
  521. krylov=KrylovJacobian)[J]()
  522. else:
  523. raise TypeError('Cannot convert object to a Jacobian')
  524. #------------------------------------------------------------------------------
  525. # Broyden
  526. #------------------------------------------------------------------------------
  527. class GenericBroyden(Jacobian):
  528. def setup(self, x0, f0, func):
  529. Jacobian.setup(self, x0, f0, func)
  530. self.last_f = f0
  531. self.last_x = x0
  532. if hasattr(self, 'alpha') and self.alpha is None:
  533. # Autoscale the initial Jacobian parameter
  534. # unless we have already guessed the solution.
  535. normf0 = norm(f0)
  536. if normf0:
  537. self.alpha = 0.5*max(norm(x0), 1) / normf0
  538. else:
  539. self.alpha = 1.0
  540. def _update(self, x, f, dx, df, dx_norm, df_norm):
  541. raise NotImplementedError
  542. def update(self, x, f):
  543. df = f - self.last_f
  544. dx = x - self.last_x
  545. self._update(x, f, dx, df, norm(dx), norm(df))
  546. self.last_f = f
  547. self.last_x = x
  548. class LowRankMatrix(object):
  549. r"""
  550. A matrix represented as
  551. .. math:: \alpha I + \sum_{n=0}^{n=M} c_n d_n^\dagger
  552. However, if the rank of the matrix reaches the dimension of the vectors,
  553. full matrix representation will be used thereon.
  554. """
  555. def __init__(self, alpha, n, dtype):
  556. self.alpha = alpha
  557. self.cs = []
  558. self.ds = []
  559. self.n = n
  560. self.dtype = dtype
  561. self.collapsed = None
  562. @staticmethod
  563. def _matvec(v, alpha, cs, ds):
  564. axpy, scal, dotc = get_blas_funcs(['axpy', 'scal', 'dotc'],
  565. cs[:1] + [v])
  566. w = alpha * v
  567. for c, d in zip(cs, ds):
  568. a = dotc(d, v)
  569. w = axpy(c, w, w.size, a)
  570. return w
  571. @staticmethod
  572. def _solve(v, alpha, cs, ds):
  573. """Evaluate w = M^-1 v"""
  574. if len(cs) == 0:
  575. return v/alpha
  576. # (B + C D^H)^-1 = B^-1 - B^-1 C (I + D^H B^-1 C)^-1 D^H B^-1
  577. axpy, dotc = get_blas_funcs(['axpy', 'dotc'], cs[:1] + [v])
  578. c0 = cs[0]
  579. A = alpha * np.identity(len(cs), dtype=c0.dtype)
  580. for i, d in enumerate(ds):
  581. for j, c in enumerate(cs):
  582. A[i,j] += dotc(d, c)
  583. q = np.zeros(len(cs), dtype=c0.dtype)
  584. for j, d in enumerate(ds):
  585. q[j] = dotc(d, v)
  586. q /= alpha
  587. q = solve(A, q)
  588. w = v/alpha
  589. for c, qc in zip(cs, q):
  590. w = axpy(c, w, w.size, -qc)
  591. return w
  592. def matvec(self, v):
  593. """Evaluate w = M v"""
  594. if self.collapsed is not None:
  595. return np.dot(self.collapsed, v)
  596. return LowRankMatrix._matvec(v, self.alpha, self.cs, self.ds)
  597. def rmatvec(self, v):
  598. """Evaluate w = M^H v"""
  599. if self.collapsed is not None:
  600. return np.dot(self.collapsed.T.conj(), v)
  601. return LowRankMatrix._matvec(v, np.conj(self.alpha), self.ds, self.cs)
  602. def solve(self, v, tol=0):
  603. """Evaluate w = M^-1 v"""
  604. if self.collapsed is not None:
  605. return solve(self.collapsed, v)
  606. return LowRankMatrix._solve(v, self.alpha, self.cs, self.ds)
  607. def rsolve(self, v, tol=0):
  608. """Evaluate w = M^-H v"""
  609. if self.collapsed is not None:
  610. return solve(self.collapsed.T.conj(), v)
  611. return LowRankMatrix._solve(v, np.conj(self.alpha), self.ds, self.cs)
  612. def append(self, c, d):
  613. if self.collapsed is not None:
  614. self.collapsed += c[:,None] * d[None,:].conj()
  615. return
  616. self.cs.append(c)
  617. self.ds.append(d)
  618. if len(self.cs) > c.size:
  619. self.collapse()
  620. def __array__(self):
  621. if self.collapsed is not None:
  622. return self.collapsed
  623. Gm = self.alpha*np.identity(self.n, dtype=self.dtype)
  624. for c, d in zip(self.cs, self.ds):
  625. Gm += c[:,None]*d[None,:].conj()
  626. return Gm
  627. def collapse(self):
  628. """Collapse the low-rank matrix to a full-rank one."""
  629. self.collapsed = np.array(self)
  630. self.cs = None
  631. self.ds = None
  632. self.alpha = None
  633. def restart_reduce(self, rank):
  634. """
  635. Reduce the rank of the matrix by dropping all vectors.
  636. """
  637. if self.collapsed is not None:
  638. return
  639. assert rank > 0
  640. if len(self.cs) > rank:
  641. del self.cs[:]
  642. del self.ds[:]
  643. def simple_reduce(self, rank):
  644. """
  645. Reduce the rank of the matrix by dropping oldest vectors.
  646. """
  647. if self.collapsed is not None:
  648. return
  649. assert rank > 0
  650. while len(self.cs) > rank:
  651. del self.cs[0]
  652. del self.ds[0]
  653. def svd_reduce(self, max_rank, to_retain=None):
  654. """
  655. Reduce the rank of the matrix by retaining some SVD components.
  656. This corresponds to the \"Broyden Rank Reduction Inverse\"
  657. algorithm described in [1]_.
  658. Note that the SVD decomposition can be done by solving only a
  659. problem whose size is the effective rank of this matrix, which
  660. is viable even for large problems.
  661. Parameters
  662. ----------
  663. max_rank : int
  664. Maximum rank of this matrix after reduction.
  665. to_retain : int, optional
  666. Number of SVD components to retain when reduction is done
  667. (ie. rank > max_rank). Default is ``max_rank - 2``.
  668. References
  669. ----------
  670. .. [1] B.A. van der Rotten, PhD thesis,
  671. \"A limited memory Broyden method to solve high-dimensional
  672. systems of nonlinear equations\". Mathematisch Instituut,
  673. Universiteit Leiden, The Netherlands (2003).
  674. https://web.archive.org/web/20161022015821/http://www.math.leidenuniv.nl/scripties/Rotten.pdf
  675. """
  676. if self.collapsed is not None:
  677. return
  678. p = max_rank
  679. if to_retain is not None:
  680. q = to_retain
  681. else:
  682. q = p - 2
  683. if self.cs:
  684. p = min(p, len(self.cs[0]))
  685. q = max(0, min(q, p-1))
  686. m = len(self.cs)
  687. if m < p:
  688. # nothing to do
  689. return
  690. C = np.array(self.cs).T
  691. D = np.array(self.ds).T
  692. D, R = qr(D, mode='economic')
  693. C = dot(C, R.T.conj())
  694. U, S, WH = svd(C, full_matrices=False, compute_uv=True)
  695. C = dot(C, inv(WH))
  696. D = dot(D, WH.T.conj())
  697. for k in xrange(q):
  698. self.cs[k] = C[:,k].copy()
  699. self.ds[k] = D[:,k].copy()
  700. del self.cs[q:]
  701. del self.ds[q:]
  702. _doc_parts['broyden_params'] = """
  703. alpha : float, optional
  704. Initial guess for the Jacobian is ``(-1/alpha)``.
  705. reduction_method : str or tuple, optional
  706. Method used in ensuring that the rank of the Broyden matrix
  707. stays low. Can either be a string giving the name of the method,
  708. or a tuple of the form ``(method, param1, param2, ...)``
  709. that gives the name of the method and values for additional parameters.
  710. Methods available:
  711. - ``restart``: drop all matrix columns. Has no extra parameters.
  712. - ``simple``: drop oldest matrix column. Has no extra parameters.
  713. - ``svd``: keep only the most significant SVD components.
  714. Takes an extra parameter, ``to_retain``, which determines the
  715. number of SVD components to retain when rank reduction is done.
  716. Default is ``max_rank - 2``.
  717. max_rank : int, optional
  718. Maximum rank for the Broyden matrix.
  719. Default is infinity (ie., no rank reduction).
  720. """.strip()
  721. class BroydenFirst(GenericBroyden):
  722. r"""
  723. Find a root of a function, using Broyden's first Jacobian approximation.
  724. This method is also known as \"Broyden's good method\".
  725. Parameters
  726. ----------
  727. %(params_basic)s
  728. %(broyden_params)s
  729. %(params_extra)s
  730. Notes
  731. -----
  732. This algorithm implements the inverse Jacobian Quasi-Newton update
  733. .. math:: H_+ = H + (dx - H df) dx^\dagger H / ( dx^\dagger H df)
  734. which corresponds to Broyden's first Jacobian update
  735. .. math:: J_+ = J + (df - J dx) dx^\dagger / dx^\dagger dx
  736. References
  737. ----------
  738. .. [1] B.A. van der Rotten, PhD thesis,
  739. \"A limited memory Broyden method to solve high-dimensional
  740. systems of nonlinear equations\". Mathematisch Instituut,
  741. Universiteit Leiden, The Netherlands (2003).
  742. https://web.archive.org/web/20161022015821/http://www.math.leidenuniv.nl/scripties/Rotten.pdf
  743. """
  744. def __init__(self, alpha=None, reduction_method='restart', max_rank=None):
  745. GenericBroyden.__init__(self)
  746. self.alpha = alpha
  747. self.Gm = None
  748. if max_rank is None:
  749. max_rank = np.inf
  750. self.max_rank = max_rank
  751. if isinstance(reduction_method, str):
  752. reduce_params = ()
  753. else:
  754. reduce_params = reduction_method[1:]
  755. reduction_method = reduction_method[0]
  756. reduce_params = (max_rank - 1,) + reduce_params
  757. if reduction_method == 'svd':
  758. self._reduce = lambda: self.Gm.svd_reduce(*reduce_params)
  759. elif reduction_method == 'simple':
  760. self._reduce = lambda: self.Gm.simple_reduce(*reduce_params)
  761. elif reduction_method == 'restart':
  762. self._reduce = lambda: self.Gm.restart_reduce(*reduce_params)
  763. else:
  764. raise ValueError("Unknown rank reduction method '%s'" %
  765. reduction_method)
  766. def setup(self, x, F, func):
  767. GenericBroyden.setup(self, x, F, func)
  768. self.Gm = LowRankMatrix(-self.alpha, self.shape[0], self.dtype)
  769. def todense(self):
  770. return inv(self.Gm)
  771. def solve(self, f, tol=0):
  772. r = self.Gm.matvec(f)
  773. if not np.isfinite(r).all():
  774. # singular; reset the Jacobian approximation
  775. self.setup(self.last_x, self.last_f, self.func)
  776. return self.Gm.matvec(f)
  777. def matvec(self, f):
  778. return self.Gm.solve(f)
  779. def rsolve(self, f, tol=0):
  780. return self.Gm.rmatvec(f)
  781. def rmatvec(self, f):
  782. return self.Gm.rsolve(f)
  783. def _update(self, x, f, dx, df, dx_norm, df_norm):
  784. self._reduce() # reduce first to preserve secant condition
  785. v = self.Gm.rmatvec(dx)
  786. c = dx - self.Gm.matvec(df)
  787. d = v / vdot(df, v)
  788. self.Gm.append(c, d)
  789. class BroydenSecond(BroydenFirst):
  790. """
  791. Find a root of a function, using Broyden\'s second Jacobian approximation.
  792. This method is also known as \"Broyden's bad method\".
  793. Parameters
  794. ----------
  795. %(params_basic)s
  796. %(broyden_params)s
  797. %(params_extra)s
  798. Notes
  799. -----
  800. This algorithm implements the inverse Jacobian Quasi-Newton update
  801. .. math:: H_+ = H + (dx - H df) df^\\dagger / ( df^\\dagger df)
  802. corresponding to Broyden's second method.
  803. References
  804. ----------
  805. .. [1] B.A. van der Rotten, PhD thesis,
  806. \"A limited memory Broyden method to solve high-dimensional
  807. systems of nonlinear equations\". Mathematisch Instituut,
  808. Universiteit Leiden, The Netherlands (2003).
  809. https://web.archive.org/web/20161022015821/http://www.math.leidenuniv.nl/scripties/Rotten.pdf
  810. """
  811. def _update(self, x, f, dx, df, dx_norm, df_norm):
  812. self._reduce() # reduce first to preserve secant condition
  813. v = df
  814. c = dx - self.Gm.matvec(df)
  815. d = v / df_norm**2
  816. self.Gm.append(c, d)
  817. #------------------------------------------------------------------------------
  818. # Broyden-like (restricted memory)
  819. #------------------------------------------------------------------------------
  820. class Anderson(GenericBroyden):
  821. """
  822. Find a root of a function, using (extended) Anderson mixing.
  823. The Jacobian is formed by for a 'best' solution in the space
  824. spanned by last `M` vectors. As a result, only a MxM matrix
  825. inversions and MxN multiplications are required. [Ey]_
  826. Parameters
  827. ----------
  828. %(params_basic)s
  829. alpha : float, optional
  830. Initial guess for the Jacobian is (-1/alpha).
  831. M : float, optional
  832. Number of previous vectors to retain. Defaults to 5.
  833. w0 : float, optional
  834. Regularization parameter for numerical stability.
  835. Compared to unity, good values of the order of 0.01.
  836. %(params_extra)s
  837. References
  838. ----------
  839. .. [Ey] V. Eyert, J. Comp. Phys., 124, 271 (1996).
  840. """
  841. # Note:
  842. #
  843. # Anderson method maintains a rank M approximation of the inverse Jacobian,
  844. #
  845. # J^-1 v ~ -v*alpha + (dX + alpha dF) A^-1 dF^H v
  846. # A = W + dF^H dF
  847. # W = w0^2 diag(dF^H dF)
  848. #
  849. # so that for w0 = 0 the secant condition applies for last M iterates, ie.,
  850. #
  851. # J^-1 df_j = dx_j
  852. #
  853. # for all j = 0 ... M-1.
  854. #
  855. # Moreover, (from Sherman-Morrison-Woodbury formula)
  856. #
  857. # J v ~ [ b I - b^2 C (I + b dF^H A^-1 C)^-1 dF^H ] v
  858. # C = (dX + alpha dF) A^-1
  859. # b = -1/alpha
  860. #
  861. # and after simplification
  862. #
  863. # J v ~ -v/alpha + (dX/alpha + dF) (dF^H dX - alpha W)^-1 dF^H v
  864. #
  865. def __init__(self, alpha=None, w0=0.01, M=5):
  866. GenericBroyden.__init__(self)
  867. self.alpha = alpha
  868. self.M = M
  869. self.dx = []
  870. self.df = []
  871. self.gamma = None
  872. self.w0 = w0
  873. def solve(self, f, tol=0):
  874. dx = -self.alpha*f
  875. n = len(self.dx)
  876. if n == 0:
  877. return dx
  878. df_f = np.empty(n, dtype=f.dtype)
  879. for k in xrange(n):
  880. df_f[k] = vdot(self.df[k], f)
  881. try:
  882. gamma = solve(self.a, df_f)
  883. except LinAlgError:
  884. # singular; reset the Jacobian approximation
  885. del self.dx[:]
  886. del self.df[:]
  887. return dx
  888. for m in xrange(n):
  889. dx += gamma[m]*(self.dx[m] + self.alpha*self.df[m])
  890. return dx
  891. def matvec(self, f):
  892. dx = -f/self.alpha
  893. n = len(self.dx)
  894. if n == 0:
  895. return dx
  896. df_f = np.empty(n, dtype=f.dtype)
  897. for k in xrange(n):
  898. df_f[k] = vdot(self.df[k], f)
  899. b = np.empty((n, n), dtype=f.dtype)
  900. for i in xrange(n):
  901. for j in xrange(n):
  902. b[i,j] = vdot(self.df[i], self.dx[j])
  903. if i == j and self.w0 != 0:
  904. b[i,j] -= vdot(self.df[i], self.df[i])*self.w0**2*self.alpha
  905. gamma = solve(b, df_f)
  906. for m in xrange(n):
  907. dx += gamma[m]*(self.df[m] + self.dx[m]/self.alpha)
  908. return dx
  909. def _update(self, x, f, dx, df, dx_norm, df_norm):
  910. if self.M == 0:
  911. return
  912. self.dx.append(dx)
  913. self.df.append(df)
  914. while len(self.dx) > self.M:
  915. self.dx.pop(0)
  916. self.df.pop(0)
  917. n = len(self.dx)
  918. a = np.zeros((n, n), dtype=f.dtype)
  919. for i in xrange(n):
  920. for j in xrange(i, n):
  921. if i == j:
  922. wd = self.w0**2
  923. else:
  924. wd = 0
  925. a[i,j] = (1+wd)*vdot(self.df[i], self.df[j])
  926. a += np.triu(a, 1).T.conj()
  927. self.a = a
  928. #------------------------------------------------------------------------------
  929. # Simple iterations
  930. #------------------------------------------------------------------------------
  931. class DiagBroyden(GenericBroyden):
  932. """
  933. Find a root of a function, using diagonal Broyden Jacobian approximation.
  934. The Jacobian approximation is derived from previous iterations, by
  935. retaining only the diagonal of Broyden matrices.
  936. .. warning::
  937. This algorithm may be useful for specific problems, but whether
  938. it will work may depend strongly on the problem.
  939. Parameters
  940. ----------
  941. %(params_basic)s
  942. alpha : float, optional
  943. Initial guess for the Jacobian is (-1/alpha).
  944. %(params_extra)s
  945. """
  946. def __init__(self, alpha=None):
  947. GenericBroyden.__init__(self)
  948. self.alpha = alpha
  949. def setup(self, x, F, func):
  950. GenericBroyden.setup(self, x, F, func)
  951. self.d = np.ones((self.shape[0],), dtype=self.dtype) / self.alpha
  952. def solve(self, f, tol=0):
  953. return -f / self.d
  954. def matvec(self, f):
  955. return -f * self.d
  956. def rsolve(self, f, tol=0):
  957. return -f / self.d.conj()
  958. def rmatvec(self, f):
  959. return -f * self.d.conj()
  960. def todense(self):
  961. return np.diag(-self.d)
  962. def _update(self, x, f, dx, df, dx_norm, df_norm):
  963. self.d -= (df + self.d*dx)*dx/dx_norm**2
  964. class LinearMixing(GenericBroyden):
  965. """
  966. Find a root of a function, using a scalar Jacobian approximation.
  967. .. warning::
  968. This algorithm may be useful for specific problems, but whether
  969. it will work may depend strongly on the problem.
  970. Parameters
  971. ----------
  972. %(params_basic)s
  973. alpha : float, optional
  974. The Jacobian approximation is (-1/alpha).
  975. %(params_extra)s
  976. """
  977. def __init__(self, alpha=None):
  978. GenericBroyden.__init__(self)
  979. self.alpha = alpha
  980. def solve(self, f, tol=0):
  981. return -f*self.alpha
  982. def matvec(self, f):
  983. return -f/self.alpha
  984. def rsolve(self, f, tol=0):
  985. return -f*np.conj(self.alpha)
  986. def rmatvec(self, f):
  987. return -f/np.conj(self.alpha)
  988. def todense(self):
  989. return np.diag(np.full(self.shape[0], -1/self.alpha))
  990. def _update(self, x, f, dx, df, dx_norm, df_norm):
  991. pass
  992. class ExcitingMixing(GenericBroyden):
  993. """
  994. Find a root of a function, using a tuned diagonal Jacobian approximation.
  995. The Jacobian matrix is diagonal and is tuned on each iteration.
  996. .. warning::
  997. This algorithm may be useful for specific problems, but whether
  998. it will work may depend strongly on the problem.
  999. Parameters
  1000. ----------
  1001. %(params_basic)s
  1002. alpha : float, optional
  1003. Initial Jacobian approximation is (-1/alpha).
  1004. alphamax : float, optional
  1005. The entries of the diagonal Jacobian are kept in the range
  1006. ``[alpha, alphamax]``.
  1007. %(params_extra)s
  1008. """
  1009. def __init__(self, alpha=None, alphamax=1.0):
  1010. GenericBroyden.__init__(self)
  1011. self.alpha = alpha
  1012. self.alphamax = alphamax
  1013. self.beta = None
  1014. def setup(self, x, F, func):
  1015. GenericBroyden.setup(self, x, F, func)
  1016. self.beta = np.full((self.shape[0],), self.alpha, dtype=self.dtype)
  1017. def solve(self, f, tol=0):
  1018. return -f*self.beta
  1019. def matvec(self, f):
  1020. return -f/self.beta
  1021. def rsolve(self, f, tol=0):
  1022. return -f*self.beta.conj()
  1023. def rmatvec(self, f):
  1024. return -f/self.beta.conj()
  1025. def todense(self):
  1026. return np.diag(-1/self.beta)
  1027. def _update(self, x, f, dx, df, dx_norm, df_norm):
  1028. incr = f*self.last_f > 0
  1029. self.beta[incr] += self.alpha
  1030. self.beta[~incr] = self.alpha
  1031. np.clip(self.beta, 0, self.alphamax, out=self.beta)
  1032. #------------------------------------------------------------------------------
  1033. # Iterative/Krylov approximated Jacobians
  1034. #------------------------------------------------------------------------------
  1035. class KrylovJacobian(Jacobian):
  1036. r"""
  1037. Find a root of a function, using Krylov approximation for inverse Jacobian.
  1038. This method is suitable for solving large-scale problems.
  1039. Parameters
  1040. ----------
  1041. %(params_basic)s
  1042. rdiff : float, optional
  1043. Relative step size to use in numerical differentiation.
  1044. method : {'lgmres', 'gmres', 'bicgstab', 'cgs', 'minres'} or function
  1045. Krylov method to use to approximate the Jacobian.
  1046. Can be a string, or a function implementing the same interface as
  1047. the iterative solvers in `scipy.sparse.linalg`.
  1048. The default is `scipy.sparse.linalg.lgmres`.
  1049. inner_M : LinearOperator or InverseJacobian
  1050. Preconditioner for the inner Krylov iteration.
  1051. Note that you can use also inverse Jacobians as (adaptive)
  1052. preconditioners. For example,
  1053. >>> from scipy.optimize.nonlin import BroydenFirst, KrylovJacobian
  1054. >>> from scipy.optimize.nonlin import InverseJacobian
  1055. >>> jac = BroydenFirst()
  1056. >>> kjac = KrylovJacobian(inner_M=InverseJacobian(jac))
  1057. If the preconditioner has a method named 'update', it will be called
  1058. as ``update(x, f)`` after each nonlinear step, with ``x`` giving
  1059. the current point, and ``f`` the current function value.
  1060. inner_tol, inner_maxiter, ...
  1061. Parameters to pass on to the \"inner\" Krylov solver.
  1062. See `scipy.sparse.linalg.gmres` for details.
  1063. outer_k : int, optional
  1064. Size of the subspace kept across LGMRES nonlinear iterations.
  1065. See `scipy.sparse.linalg.lgmres` for details.
  1066. %(params_extra)s
  1067. See Also
  1068. --------
  1069. scipy.sparse.linalg.gmres
  1070. scipy.sparse.linalg.lgmres
  1071. Notes
  1072. -----
  1073. This function implements a Newton-Krylov solver. The basic idea is
  1074. to compute the inverse of the Jacobian with an iterative Krylov
  1075. method. These methods require only evaluating the Jacobian-vector
  1076. products, which are conveniently approximated by a finite difference:
  1077. .. math:: J v \approx (f(x + \omega*v/|v|) - f(x)) / \omega
  1078. Due to the use of iterative matrix inverses, these methods can
  1079. deal with large nonlinear problems.
  1080. Scipy's `scipy.sparse.linalg` module offers a selection of Krylov
  1081. solvers to choose from. The default here is `lgmres`, which is a
  1082. variant of restarted GMRES iteration that reuses some of the
  1083. information obtained in the previous Newton steps to invert
  1084. Jacobians in subsequent steps.
  1085. For a review on Newton-Krylov methods, see for example [1]_,
  1086. and for the LGMRES sparse inverse method, see [2]_.
  1087. References
  1088. ----------
  1089. .. [1] D.A. Knoll and D.E. Keyes, J. Comp. Phys. 193, 357 (2004).
  1090. :doi:`10.1016/j.jcp.2003.08.010`
  1091. .. [2] A.H. Baker and E.R. Jessup and T. Manteuffel,
  1092. SIAM J. Matrix Anal. Appl. 26, 962 (2005).
  1093. :doi:`10.1137/S0895479803422014`
  1094. """
  1095. def __init__(self, rdiff=None, method='lgmres', inner_maxiter=20,
  1096. inner_M=None, outer_k=10, **kw):
  1097. self.preconditioner = inner_M
  1098. self.rdiff = rdiff
  1099. self.method = dict(
  1100. bicgstab=scipy.sparse.linalg.bicgstab,
  1101. gmres=scipy.sparse.linalg.gmres,
  1102. lgmres=scipy.sparse.linalg.lgmres,
  1103. cgs=scipy.sparse.linalg.cgs,
  1104. minres=scipy.sparse.linalg.minres,
  1105. ).get(method, method)
  1106. self.method_kw = dict(maxiter=inner_maxiter, M=self.preconditioner)
  1107. if self.method is scipy.sparse.linalg.gmres:
  1108. # Replace GMRES's outer iteration with Newton steps
  1109. self.method_kw['restrt'] = inner_maxiter
  1110. self.method_kw['maxiter'] = 1
  1111. self.method_kw.setdefault('atol', 0)
  1112. elif self.method is scipy.sparse.linalg.gcrotmk:
  1113. self.method_kw.setdefault('atol', 0)
  1114. elif self.method is scipy.sparse.linalg.lgmres:
  1115. self.method_kw['outer_k'] = outer_k
  1116. # Replace LGMRES's outer iteration with Newton steps
  1117. self.method_kw['maxiter'] = 1
  1118. # Carry LGMRES's `outer_v` vectors across nonlinear iterations
  1119. self.method_kw.setdefault('outer_v', [])
  1120. self.method_kw.setdefault('prepend_outer_v', True)
  1121. # But don't carry the corresponding Jacobian*v products, in case
  1122. # the Jacobian changes a lot in the nonlinear step
  1123. #
  1124. # XXX: some trust-region inspired ideas might be more efficient...
  1125. # See eg. Brown & Saad. But needs to be implemented separately
  1126. # since it's not an inexact Newton method.
  1127. self.method_kw.setdefault('store_outer_Av', False)
  1128. self.method_kw.setdefault('atol', 0)
  1129. for key, value in kw.items():
  1130. if not key.startswith('inner_'):
  1131. raise ValueError("Unknown parameter %s" % key)
  1132. self.method_kw[key[6:]] = value
  1133. def _update_diff_step(self):
  1134. mx = abs(self.x0).max()
  1135. mf = abs(self.f0).max()
  1136. self.omega = self.rdiff * max(1, mx) / max(1, mf)
  1137. def matvec(self, v):
  1138. nv = norm(v)
  1139. if nv == 0:
  1140. return 0*v
  1141. sc = self.omega / nv
  1142. r = (self.func(self.x0 + sc*v) - self.f0) / sc
  1143. if not np.all(np.isfinite(r)) and np.all(np.isfinite(v)):
  1144. raise ValueError('Function returned non-finite results')
  1145. return r
  1146. def solve(self, rhs, tol=0):
  1147. if 'tol' in self.method_kw:
  1148. sol, info = self.method(self.op, rhs, **self.method_kw)
  1149. else:
  1150. sol, info = self.method(self.op, rhs, tol=tol, **self.method_kw)
  1151. return sol
  1152. def update(self, x, f):
  1153. self.x0 = x
  1154. self.f0 = f
  1155. self._update_diff_step()
  1156. # Update also the preconditioner, if possible
  1157. if self.preconditioner is not None:
  1158. if hasattr(self.preconditioner, 'update'):
  1159. self.preconditioner.update(x, f)
  1160. def setup(self, x, f, func):
  1161. Jacobian.setup(self, x, f, func)
  1162. self.x0 = x
  1163. self.f0 = f
  1164. self.op = scipy.sparse.linalg.aslinearoperator(self)
  1165. if self.rdiff is None:
  1166. self.rdiff = np.finfo(x.dtype).eps ** (1./2)
  1167. self._update_diff_step()
  1168. # Setup also the preconditioner, if possible
  1169. if self.preconditioner is not None:
  1170. if hasattr(self.preconditioner, 'setup'):
  1171. self.preconditioner.setup(x, f, func)
  1172. #------------------------------------------------------------------------------
  1173. # Wrapper functions
  1174. #------------------------------------------------------------------------------
  1175. def _nonlin_wrapper(name, jac):
  1176. """
  1177. Construct a solver wrapper with given name and jacobian approx.
  1178. It inspects the keyword arguments of ``jac.__init__``, and allows to
  1179. use the same arguments in the wrapper function, in addition to the
  1180. keyword arguments of `nonlin_solve`
  1181. """
  1182. args, varargs, varkw, defaults = _getargspec(jac.__init__)
  1183. kwargs = list(zip(args[-len(defaults):], defaults))
  1184. kw_str = ", ".join(["%s=%r" % (k, v) for k, v in kwargs])
  1185. if kw_str:
  1186. kw_str = ", " + kw_str
  1187. kwkw_str = ", ".join(["%s=%s" % (k, k) for k, v in kwargs])
  1188. if kwkw_str:
  1189. kwkw_str = kwkw_str + ", "
  1190. # Construct the wrapper function so that its keyword arguments
  1191. # are visible in pydoc.help etc.
  1192. wrapper = """
  1193. def %(name)s(F, xin, iter=None %(kw)s, verbose=False, maxiter=None,
  1194. f_tol=None, f_rtol=None, x_tol=None, x_rtol=None,
  1195. tol_norm=None, line_search='armijo', callback=None, **kw):
  1196. jac = %(jac)s(%(kwkw)s **kw)
  1197. return nonlin_solve(F, xin, jac, iter, verbose, maxiter,
  1198. f_tol, f_rtol, x_tol, x_rtol, tol_norm, line_search,
  1199. callback)
  1200. """
  1201. wrapper = wrapper % dict(name=name, kw=kw_str, jac=jac.__name__,
  1202. kwkw=kwkw_str)
  1203. ns = {}
  1204. ns.update(globals())
  1205. exec_(wrapper, ns)
  1206. func = ns[name]
  1207. func.__doc__ = jac.__doc__
  1208. _set_doc(func)
  1209. return func
  1210. broyden1 = _nonlin_wrapper('broyden1', BroydenFirst)
  1211. broyden2 = _nonlin_wrapper('broyden2', BroydenSecond)
  1212. anderson = _nonlin_wrapper('anderson', Anderson)
  1213. linearmixing = _nonlin_wrapper('linearmixing', LinearMixing)
  1214. diagbroyden = _nonlin_wrapper('diagbroyden', DiagBroyden)
  1215. excitingmixing = _nonlin_wrapper('excitingmixing', ExcitingMixing)
  1216. newton_krylov = _nonlin_wrapper('newton_krylov', KrylovJacobian)