Coverage for /usr/lib/python3/dist-packages/scipy/sparse/linalg/_isolve/iterative.py: 8%
435 statements
« prev ^ index » next coverage.py v7.4.4, created at 2025-06-14 15:25 +0200
« prev ^ index » next coverage.py v7.4.4, created at 2025-06-14 15:25 +0200
1"""Iterative methods for solving linear systems"""
3__all__ = ['bicg','bicgstab','cg','cgs','gmres','qmr']
5import warnings
6from textwrap import dedent
7import numpy as np
9from . import _iterative
11from scipy.sparse.linalg._interface import LinearOperator
12from .utils import make_system
13from scipy._lib._util import _aligned_zeros
14from scipy._lib._threadsafety import non_reentrant
16_type_conv = {'f':'s', 'd':'d', 'F':'c', 'D':'z'}
19# Part of the docstring common to all iterative solvers
20common_doc1 = \
21"""
22Parameters
23----------
24A : {sparse matrix, ndarray, LinearOperator}"""
26common_doc2 = \
27"""b : ndarray
28 Right hand side of the linear system. Has shape (N,) or (N,1).
30Returns
31-------
32x : ndarray
33 The converged solution.
34info : integer
35 Provides convergence information:
36 0 : successful exit
37 >0 : convergence to tolerance not achieved, number of iterations
38 <0 : illegal input or breakdown
40Other Parameters
41----------------
42x0 : ndarray
43 Starting guess for the solution.
44tol, atol : float, optional
45 Tolerances for convergence, ``norm(residual) <= max(tol*norm(b), atol)``.
46 The default for ``atol`` is ``'legacy'``, which emulates
47 a different legacy behavior.
49 .. warning::
51 The default value for `atol` will be changed in a future release.
52 For future compatibility, specify `atol` explicitly.
53maxiter : integer
54 Maximum number of iterations. Iteration will stop after maxiter
55 steps even if the specified tolerance has not been achieved.
56M : {sparse matrix, ndarray, LinearOperator}
57 Preconditioner for A. The preconditioner should approximate the
58 inverse of A. Effective preconditioning dramatically improves the
59 rate of convergence, which implies that fewer iterations are needed
60 to reach a given error tolerance.
61callback : function
62 User-supplied function to call after each iteration. It is called
63 as callback(xk), where xk is the current solution vector.
64"""
67def _stoptest(residual, atol):
68 """
69 Successful termination condition for the solvers.
70 """
71 resid = np.linalg.norm(residual)
72 if resid <= atol:
73 return resid, 1
74 else:
75 return resid, 0
78def _get_atol(tol, atol, bnrm2, get_residual, routine_name):
79 """
80 Parse arguments for absolute tolerance in termination condition.
82 Parameters
83 ----------
84 tol, atol : object
85 The arguments passed into the solver routine by user.
86 bnrm2 : float
87 2-norm of the rhs vector.
88 get_residual : callable
89 Callable ``get_residual()`` that returns the initial value of
90 the residual.
91 routine_name : str
92 Name of the routine.
93 """
95 if atol is None:
96 warnings.warn("scipy.sparse.linalg.{name} called without specifying `atol`. "
97 "The default value will be changed in a future release. "
98 "For compatibility, specify a value for `atol` explicitly, e.g., "
99 "``{name}(..., atol=0)``, or to retain the old behavior "
100 "``{name}(..., atol='legacy')``".format(name=routine_name),
101 category=DeprecationWarning, stacklevel=4)
102 atol = 'legacy'
104 tol = float(tol)
106 if atol == 'legacy':
107 # emulate old legacy behavior
108 resid = get_residual()
109 if resid <= tol:
110 return 'exit'
111 if bnrm2 == 0:
112 return tol
113 else:
114 return tol * float(bnrm2)
115 else:
116 return max(float(atol), tol * float(bnrm2))
119def set_docstring(header, Ainfo, footer='', atol_default='0'):
120 def combine(fn):
121 fn.__doc__ = '\n'.join((header, common_doc1,
122 ' ' + Ainfo.replace('\n', '\n '),
123 common_doc2, dedent(footer)))
124 return fn
125 return combine
128@set_docstring('Use BIConjugate Gradient iteration to solve ``Ax = b``.',
129 'The real or complex N-by-N matrix of the linear system.\n'
130 'Alternatively, ``A`` can be a linear operator which can\n'
131 'produce ``Ax`` and ``A^T x`` using, e.g.,\n'
132 '``scipy.sparse.linalg.LinearOperator``.',
133 footer="""\
134 Examples
135 --------
136 >>> import numpy as np
137 >>> from scipy.sparse import csc_matrix
138 >>> from scipy.sparse.linalg import bicg
139 >>> A = csc_matrix([[3, 2, 0], [1, -1, 0], [0, 5, 1]], dtype=float)
140 >>> b = np.array([2, 4, -1], dtype=float)
141 >>> x, exitCode = bicg(A, b)
142 >>> print(exitCode) # 0 indicates successful convergence
143 0
144 >>> np.allclose(A.dot(x), b)
145 True
147 """
148 )
149@non_reentrant()
150def bicg(A, b, x0=None, tol=1e-5, maxiter=None, M=None, callback=None, atol=None):
151 A,M,x,b,postprocess = make_system(A, M, x0, b)
153 n = len(b)
154 if maxiter is None:
155 maxiter = n*10
157 matvec, rmatvec = A.matvec, A.rmatvec
158 psolve, rpsolve = M.matvec, M.rmatvec
159 ltr = _type_conv[x.dtype.char]
160 revcom = getattr(_iterative, ltr + 'bicgrevcom')
162 def get_residual():
163 return np.linalg.norm(matvec(x) - b)
164 atol = _get_atol(tol, atol, np.linalg.norm(b), get_residual, 'bicg')
165 if atol == 'exit':
166 return postprocess(x), 0
168 resid = atol
169 ndx1 = 1
170 ndx2 = -1
171 # Use _aligned_zeros to work around a f2py bug in Numpy 1.9.1
172 work = _aligned_zeros(6*n,dtype=x.dtype)
173 ijob = 1
174 info = 0
175 ftflag = True
176 iter_ = maxiter
177 while True:
178 olditer = iter_
179 x, iter_, resid, info, ndx1, ndx2, sclr1, sclr2, ijob = \
180 revcom(b, x, work, iter_, resid, info, ndx1, ndx2, ijob)
181 if callback is not None and iter_ > olditer:
182 callback(x)
183 slice1 = slice(ndx1-1, ndx1-1+n)
184 slice2 = slice(ndx2-1, ndx2-1+n)
185 if (ijob == -1):
186 if callback is not None:
187 callback(x)
188 break
189 elif (ijob == 1):
190 work[slice2] *= sclr2
191 work[slice2] += sclr1*matvec(work[slice1])
192 elif (ijob == 2):
193 work[slice2] *= sclr2
194 work[slice2] += sclr1*rmatvec(work[slice1])
195 elif (ijob == 3):
196 work[slice1] = psolve(work[slice2])
197 elif (ijob == 4):
198 work[slice1] = rpsolve(work[slice2])
199 elif (ijob == 5):
200 work[slice2] *= sclr2
201 work[slice2] += sclr1*matvec(x)
202 elif (ijob == 6):
203 if ftflag:
204 info = -1
205 ftflag = False
206 resid, info = _stoptest(work[slice1], atol)
207 ijob = 2
209 if info > 0 and iter_ == maxiter and not (resid <= atol):
210 # info isn't set appropriately otherwise
211 info = iter_
213 return postprocess(x), info
216@set_docstring('Use BIConjugate Gradient STABilized iteration to solve '
217 '``Ax = b``.',
218 'The real or complex N-by-N matrix of the linear system.\n'
219 'Alternatively, ``A`` can be a linear operator which can\n'
220 'produce ``Ax`` using, e.g.,\n'
221 '``scipy.sparse.linalg.LinearOperator``.',
222 footer="""\
223 Examples
224 --------
225 >>> import numpy as np
226 >>> from scipy.sparse import csc_matrix
227 >>> from scipy.sparse.linalg import bicgstab
228 >>> R = np.array([[4, 2, 0, 1],
229 ... [3, 0, 0, 2],
230 ... [0, 1, 1, 1],
231 ... [0, 2, 1, 0]])
232 >>> A = csc_matrix(R)
233 >>> b = np.array([-1, -0.5, -1, 2])
234 >>> x, exit_code = bicgstab(A, b)
235 >>> print(exit_code) # 0 indicates successful convergence
236 0
237 >>> np.allclose(A.dot(x), b)
238 True
239 """)
240@non_reentrant()
241def bicgstab(A, b, x0=None, tol=1e-5, maxiter=None, M=None, callback=None, atol=None):
242 A, M, x, b, postprocess = make_system(A, M, x0, b)
244 n = len(b)
245 if maxiter is None:
246 maxiter = n*10
248 matvec = A.matvec
249 psolve = M.matvec
250 ltr = _type_conv[x.dtype.char]
251 revcom = getattr(_iterative, ltr + 'bicgstabrevcom')
253 def get_residual():
254 return np.linalg.norm(matvec(x) - b)
255 atol = _get_atol(tol, atol, np.linalg.norm(b), get_residual, 'bicgstab')
256 if atol == 'exit':
257 return postprocess(x), 0
259 resid = atol
260 ndx1 = 1
261 ndx2 = -1
262 # Use _aligned_zeros to work around a f2py bug in Numpy 1.9.1
263 work = _aligned_zeros(7*n,dtype=x.dtype)
264 ijob = 1
265 info = 0
266 ftflag = True
267 iter_ = maxiter
268 while True:
269 olditer = iter_
270 x, iter_, resid, info, ndx1, ndx2, sclr1, sclr2, ijob = \
271 revcom(b, x, work, iter_, resid, info, ndx1, ndx2, ijob)
272 if callback is not None and iter_ > olditer:
273 callback(x)
274 slice1 = slice(ndx1-1, ndx1-1+n)
275 slice2 = slice(ndx2-1, ndx2-1+n)
276 if (ijob == -1):
277 if callback is not None:
278 callback(x)
279 break
280 elif (ijob == 1):
281 work[slice2] *= sclr2
282 work[slice2] += sclr1*matvec(work[slice1])
283 elif (ijob == 2):
284 work[slice1] = psolve(work[slice2])
285 elif (ijob == 3):
286 work[slice2] *= sclr2
287 work[slice2] += sclr1*matvec(x)
288 elif (ijob == 4):
289 if ftflag:
290 info = -1
291 ftflag = False
292 resid, info = _stoptest(work[slice1], atol)
293 ijob = 2
295 if info > 0 and iter_ == maxiter and not (resid <= atol):
296 # info isn't set appropriately otherwise
297 info = iter_
299 return postprocess(x), info
302@set_docstring('Use Conjugate Gradient iteration to solve ``Ax = b``.',
303 'The real or complex N-by-N matrix of the linear system.\n'
304 '``A`` must represent a hermitian, positive definite matrix.\n'
305 'Alternatively, ``A`` can be a linear operator which can\n'
306 'produce ``Ax`` using, e.g.,\n'
307 '``scipy.sparse.linalg.LinearOperator``.',
308 footer="""\
309 Examples
310 --------
311 >>> import numpy as np
312 >>> from scipy.sparse import csc_matrix
313 >>> from scipy.sparse.linalg import cg
314 >>> P = np.array([[4, 0, 1, 0],
315 ... [0, 5, 0, 0],
316 ... [1, 0, 3, 2],
317 ... [0, 0, 2, 4]])
318 >>> A = csc_matrix(P)
319 >>> b = np.array([-1, -0.5, -1, 2])
320 >>> x, exit_code = cg(A, b)
321 >>> print(exit_code) # 0 indicates successful convergence
322 0
323 >>> np.allclose(A.dot(x), b)
324 True
326 """)
327@non_reentrant()
328def cg(A, b, x0=None, tol=1e-5, maxiter=None, M=None, callback=None, atol=None):
329 A, M, x, b, postprocess = make_system(A, M, x0, b)
331 n = len(b)
332 if maxiter is None:
333 maxiter = n*10
335 matvec = A.matvec
336 psolve = M.matvec
337 ltr = _type_conv[x.dtype.char]
338 revcom = getattr(_iterative, ltr + 'cgrevcom')
340 def get_residual():
341 return np.linalg.norm(matvec(x) - b)
342 atol = _get_atol(tol, atol, np.linalg.norm(b), get_residual, 'cg')
343 if atol == 'exit':
344 return postprocess(x), 0
346 resid = atol
347 ndx1 = 1
348 ndx2 = -1
349 # Use _aligned_zeros to work around a f2py bug in Numpy 1.9.1
350 work = _aligned_zeros(4*n,dtype=x.dtype)
351 ijob = 1
352 info = 0
353 ftflag = True
354 iter_ = maxiter
355 while True:
356 olditer = iter_
357 x, iter_, resid, info, ndx1, ndx2, sclr1, sclr2, ijob = \
358 revcom(b, x, work, iter_, resid, info, ndx1, ndx2, ijob)
359 if callback is not None and iter_ > olditer:
360 callback(x)
361 slice1 = slice(ndx1-1, ndx1-1+n)
362 slice2 = slice(ndx2-1, ndx2-1+n)
363 if (ijob == -1):
364 if callback is not None:
365 callback(x)
366 break
367 elif (ijob == 1):
368 work[slice2] *= sclr2
369 work[slice2] += sclr1*matvec(work[slice1])
370 elif (ijob == 2):
371 work[slice1] = psolve(work[slice2])
372 elif (ijob == 3):
373 work[slice2] *= sclr2
374 work[slice2] += sclr1*matvec(x)
375 elif (ijob == 4):
376 if ftflag:
377 info = -1
378 ftflag = False
379 resid, info = _stoptest(work[slice1], atol)
380 if info == 1 and iter_ > 1:
381 # recompute residual and recheck, to avoid
382 # accumulating rounding error
383 work[slice1] = b - matvec(x)
384 resid, info = _stoptest(work[slice1], atol)
385 ijob = 2
387 if info > 0 and iter_ == maxiter and not (resid <= atol):
388 # info isn't set appropriately otherwise
389 info = iter_
391 return postprocess(x), info
394@set_docstring('Use Conjugate Gradient Squared iteration to solve ``Ax = b``.',
395 'The real-valued N-by-N matrix of the linear system.\n'
396 'Alternatively, ``A`` can be a linear operator which can\n'
397 'produce ``Ax`` using, e.g.,\n'
398 '``scipy.sparse.linalg.LinearOperator``.',
399 footer="""\
400 Examples
401 --------
402 >>> import numpy as np
403 >>> from scipy.sparse import csc_matrix
404 >>> from scipy.sparse.linalg import cgs
405 >>> R = np.array([[4, 2, 0, 1],
406 ... [3, 0, 0, 2],
407 ... [0, 1, 1, 1],
408 ... [0, 2, 1, 0]])
409 >>> A = csc_matrix(R)
410 >>> b = np.array([-1, -0.5, -1, 2])
411 >>> x, exit_code = cgs(A, b)
412 >>> print(exit_code) # 0 indicates successful convergence
413 0
414 >>> np.allclose(A.dot(x), b)
415 True
416 """
417 )
418@non_reentrant()
419def cgs(A, b, x0=None, tol=1e-5, maxiter=None, M=None, callback=None, atol=None):
420 A, M, x, b, postprocess = make_system(A, M, x0, b)
422 n = len(b)
423 if maxiter is None:
424 maxiter = n*10
426 matvec = A.matvec
427 psolve = M.matvec
428 ltr = _type_conv[x.dtype.char]
429 revcom = getattr(_iterative, ltr + 'cgsrevcom')
431 def get_residual():
432 return np.linalg.norm(matvec(x) - b)
433 atol = _get_atol(tol, atol, np.linalg.norm(b), get_residual, 'cgs')
434 if atol == 'exit':
435 return postprocess(x), 0
437 resid = atol
438 ndx1 = 1
439 ndx2 = -1
440 # Use _aligned_zeros to work around a f2py bug in Numpy 1.9.1
441 work = _aligned_zeros(7*n,dtype=x.dtype)
442 ijob = 1
443 info = 0
444 ftflag = True
445 iter_ = maxiter
446 while True:
447 olditer = iter_
448 x, iter_, resid, info, ndx1, ndx2, sclr1, sclr2, ijob = \
449 revcom(b, x, work, iter_, resid, info, ndx1, ndx2, ijob)
450 if callback is not None and iter_ > olditer:
451 callback(x)
452 slice1 = slice(ndx1-1, ndx1-1+n)
453 slice2 = slice(ndx2-1, ndx2-1+n)
454 if (ijob == -1):
455 if callback is not None:
456 callback(x)
457 break
458 elif (ijob == 1):
459 work[slice2] *= sclr2
460 work[slice2] += sclr1*matvec(work[slice1])
461 elif (ijob == 2):
462 work[slice1] = psolve(work[slice2])
463 elif (ijob == 3):
464 work[slice2] *= sclr2
465 work[slice2] += sclr1*matvec(x)
466 elif (ijob == 4):
467 if ftflag:
468 info = -1
469 ftflag = False
470 resid, info = _stoptest(work[slice1], atol)
471 if info == 1 and iter_ > 1:
472 # recompute residual and recheck, to avoid
473 # accumulating rounding error
474 work[slice1] = b - matvec(x)
475 resid, info = _stoptest(work[slice1], atol)
476 ijob = 2
478 if info == -10:
479 # termination due to breakdown: check for convergence
480 resid, ok = _stoptest(b - matvec(x), atol)
481 if ok:
482 info = 0
484 if info > 0 and iter_ == maxiter and not (resid <= atol):
485 # info isn't set appropriately otherwise
486 info = iter_
488 return postprocess(x), info
491@non_reentrant()
492def gmres(A, b, x0=None, tol=1e-5, restart=None, maxiter=None, M=None, callback=None,
493 restrt=None, atol=None, callback_type=None):
494 """
495 Use Generalized Minimal RESidual iteration to solve ``Ax = b``.
497 Parameters
498 ----------
499 A : {sparse matrix, ndarray, LinearOperator}
500 The real or complex N-by-N matrix of the linear system.
501 Alternatively, ``A`` can be a linear operator which can
502 produce ``Ax`` using, e.g.,
503 ``scipy.sparse.linalg.LinearOperator``.
504 b : ndarray
505 Right hand side of the linear system. Has shape (N,) or (N,1).
507 Returns
508 -------
509 x : ndarray
510 The converged solution.
511 info : int
512 Provides convergence information:
513 * 0 : successful exit
514 * >0 : convergence to tolerance not achieved, number of iterations
515 * <0 : illegal input or breakdown
517 Other parameters
518 ----------------
519 x0 : ndarray
520 Starting guess for the solution (a vector of zeros by default).
521 tol, atol : float, optional
522 Tolerances for convergence, ``norm(residual) <= max(tol*norm(b), atol)``.
523 The default for ``atol`` is ``'legacy'``, which emulates
524 a different legacy behavior.
526 .. warning::
528 The default value for `atol` will be changed in a future release.
529 For future compatibility, specify `atol` explicitly.
530 restart : int, optional
531 Number of iterations between restarts. Larger values increase
532 iteration cost, but may be necessary for convergence.
533 Default is 20.
534 maxiter : int, optional
535 Maximum number of iterations (restart cycles). Iteration will stop
536 after maxiter steps even if the specified tolerance has not been
537 achieved.
538 M : {sparse matrix, ndarray, LinearOperator}
539 Inverse of the preconditioner of A. M should approximate the
540 inverse of A and be easy to solve for (see Notes). Effective
541 preconditioning dramatically improves the rate of convergence,
542 which implies that fewer iterations are needed to reach a given
543 error tolerance. By default, no preconditioner is used.
544 In this implementation, left preconditioning is used,
545 and the preconditioned residual is minimized.
546 callback : function
547 User-supplied function to call after each iteration. It is called
548 as `callback(args)`, where `args` are selected by `callback_type`.
549 callback_type : {'x', 'pr_norm', 'legacy'}, optional
550 Callback function argument requested:
551 - ``x``: current iterate (ndarray), called on every restart
552 - ``pr_norm``: relative (preconditioned) residual norm (float),
553 called on every inner iteration
554 - ``legacy`` (default): same as ``pr_norm``, but also changes the
555 meaning of 'maxiter' to count inner iterations instead of restart
556 cycles.
557 restrt : int, optional, deprecated
559 .. deprecated:: 0.11.0
560 `gmres` keyword argument `restrt` is deprecated infavour of
561 `restart` and will be removed in SciPy 1.12.0.
563 See Also
564 --------
565 LinearOperator
567 Notes
568 -----
569 A preconditioner, P, is chosen such that P is close to A but easy to solve
570 for. The preconditioner parameter required by this routine is
571 ``M = P^-1``. The inverse should preferably not be calculated
572 explicitly. Rather, use the following template to produce M::
574 # Construct a linear operator that computes P^-1 @ x.
575 import scipy.sparse.linalg as spla
576 M_x = lambda x: spla.spsolve(P, x)
577 M = spla.LinearOperator((n, n), M_x)
579 Examples
580 --------
581 >>> import numpy as np
582 >>> from scipy.sparse import csc_matrix
583 >>> from scipy.sparse.linalg import gmres
584 >>> A = csc_matrix([[3, 2, 0], [1, -1, 0], [0, 5, 1]], dtype=float)
585 >>> b = np.array([2, 4, -1], dtype=float)
586 >>> x, exitCode = gmres(A, b)
587 >>> print(exitCode) # 0 indicates successful convergence
588 0
589 >>> np.allclose(A.dot(x), b)
590 True
591 """
593 # Change 'restrt' keyword to 'restart'
594 if restrt is None:
595 restrt = restart
596 elif restart is not None:
597 raise ValueError("Cannot specify both restart and restrt keywords. "
598 "Preferably use 'restart' only.")
599 else:
600 msg = ("'gmres' keyword argument 'restrt' is deprecated infavour of "
601 "'restart' and will be removed in SciPy 1.12.0.")
602 warnings.warn(msg, DeprecationWarning, stacklevel=2)
604 if callback is not None and callback_type is None:
605 # Warn about 'callback_type' semantic changes.
606 # Probably should be removed only in far future, Scipy 2.0 or so.
607 warnings.warn("scipy.sparse.linalg.gmres called without specifying `callback_type`. "
608 "The default value will be changed in a future release. "
609 "For compatibility, specify a value for `callback_type` explicitly, e.g., "
610 "``{name}(..., callback_type='pr_norm')``, or to retain the old behavior "
611 "``{name}(..., callback_type='legacy')``",
612 category=DeprecationWarning, stacklevel=3)
614 if callback_type is None:
615 callback_type = 'legacy'
617 if callback_type not in ('x', 'pr_norm', 'legacy'):
618 raise ValueError(f"Unknown callback_type: {callback_type!r}")
620 if callback is None:
621 callback_type = 'none'
623 A, M, x, b,postprocess = make_system(A, M, x0, b)
625 n = len(b)
626 if maxiter is None:
627 maxiter = n*10
629 if restrt is None:
630 restrt = 20
631 restrt = min(restrt, n)
633 matvec = A.matvec
634 psolve = M.matvec
635 ltr = _type_conv[x.dtype.char]
636 revcom = getattr(_iterative, ltr + 'gmresrevcom')
638 bnrm2 = np.linalg.norm(b)
639 Mb_nrm2 = np.linalg.norm(psolve(b))
640 def get_residual():
641 return np.linalg.norm(matvec(x) - b)
642 atol = _get_atol(tol, atol, bnrm2, get_residual, 'gmres')
643 if atol == 'exit':
644 return postprocess(x), 0
646 if bnrm2 == 0:
647 return postprocess(b), 0
649 # Tolerance passed to GMRESREVCOM applies to the inner iteration
650 # and deals with the left-preconditioned residual.
651 ptol_max_factor = 1.0
652 ptol = Mb_nrm2 * min(ptol_max_factor, atol / bnrm2)
653 resid = np.nan
654 presid = np.nan
655 ndx1 = 1
656 ndx2 = -1
657 # Use _aligned_zeros to work around a f2py bug in Numpy 1.9.1
658 work = _aligned_zeros((6+restrt)*n,dtype=x.dtype)
659 work2 = _aligned_zeros((restrt+1)*(2*restrt+2),dtype=x.dtype)
660 ijob = 1
661 info = 0
662 ftflag = True
663 iter_ = maxiter
664 old_ijob = ijob
665 first_pass = True
666 resid_ready = False
667 iter_num = 1
668 while True:
669 olditer = iter_
670 x, iter_, presid, info, ndx1, ndx2, sclr1, sclr2, ijob = \
671 revcom(b, x, restrt, work, work2, iter_, presid, info, ndx1, ndx2, ijob, ptol)
672 if callback_type == 'x' and iter_ != olditer:
673 callback(x)
674 slice1 = slice(ndx1-1, ndx1-1+n)
675 slice2 = slice(ndx2-1, ndx2-1+n)
676 if (ijob == -1): # gmres success, update last residual
677 if callback_type in ('pr_norm', 'legacy'):
678 if resid_ready:
679 callback(presid / bnrm2)
680 elif callback_type == 'x':
681 callback(x)
682 break
683 elif (ijob == 1):
684 work[slice2] *= sclr2
685 work[slice2] += sclr1*matvec(x)
686 elif (ijob == 2):
687 work[slice1] = psolve(work[slice2])
688 if not first_pass and old_ijob == 3:
689 resid_ready = True
691 first_pass = False
692 elif (ijob == 3):
693 work[slice2] *= sclr2
694 work[slice2] += sclr1*matvec(work[slice1])
695 if resid_ready:
696 if callback_type in ('pr_norm', 'legacy'):
697 callback(presid / bnrm2)
698 resid_ready = False
699 iter_num = iter_num+1
701 elif (ijob == 4):
702 if ftflag:
703 info = -1
704 ftflag = False
705 resid, info = _stoptest(work[slice1], atol)
707 # Inner loop tolerance control
708 if info or presid > ptol:
709 ptol_max_factor = min(1.0, 1.5 * ptol_max_factor)
710 else:
711 # Inner loop tolerance OK, but outer loop not.
712 ptol_max_factor = max(1e-16, 0.25 * ptol_max_factor)
714 if resid != 0:
715 ptol = presid * min(ptol_max_factor, atol / resid)
716 else:
717 ptol = presid * ptol_max_factor
719 old_ijob = ijob
720 ijob = 2
722 if callback_type == 'legacy':
723 # Legacy behavior
724 if iter_num > maxiter:
725 info = maxiter
726 break
728 if info >= 0 and not (resid <= atol):
729 # info isn't set appropriately otherwise
730 info = maxiter
732 return postprocess(x), info
735@non_reentrant()
736def qmr(A, b, x0=None, tol=1e-5, maxiter=None, M1=None, M2=None, callback=None,
737 atol=None):
738 """Use Quasi-Minimal Residual iteration to solve ``Ax = b``.
740 Parameters
741 ----------
742 A : {sparse matrix, ndarray, LinearOperator}
743 The real-valued N-by-N matrix of the linear system.
744 Alternatively, ``A`` can be a linear operator which can
745 produce ``Ax`` and ``A^T x`` using, e.g.,
746 ``scipy.sparse.linalg.LinearOperator``.
747 b : ndarray
748 Right hand side of the linear system. Has shape (N,) or (N,1).
750 Returns
751 -------
752 x : ndarray
753 The converged solution.
754 info : integer
755 Provides convergence information:
756 0 : successful exit
757 >0 : convergence to tolerance not achieved, number of iterations
758 <0 : illegal input or breakdown
760 Other Parameters
761 ----------------
762 x0 : ndarray
763 Starting guess for the solution.
764 tol, atol : float, optional
765 Tolerances for convergence, ``norm(residual) <= max(tol*norm(b), atol)``.
766 The default for ``atol`` is ``'legacy'``, which emulates
767 a different legacy behavior.
769 .. warning::
771 The default value for `atol` will be changed in a future release.
772 For future compatibility, specify `atol` explicitly.
773 maxiter : integer
774 Maximum number of iterations. Iteration will stop after maxiter
775 steps even if the specified tolerance has not been achieved.
776 M1 : {sparse matrix, ndarray, LinearOperator}
777 Left preconditioner for A.
778 M2 : {sparse matrix, ndarray, LinearOperator}
779 Right preconditioner for A. Used together with the left
780 preconditioner M1. The matrix M1@A@M2 should have better
781 conditioned than A alone.
782 callback : function
783 User-supplied function to call after each iteration. It is called
784 as callback(xk), where xk is the current solution vector.
786 See Also
787 --------
788 LinearOperator
790 Examples
791 --------
792 >>> import numpy as np
793 >>> from scipy.sparse import csc_matrix
794 >>> from scipy.sparse.linalg import qmr
795 >>> A = csc_matrix([[3, 2, 0], [1, -1, 0], [0, 5, 1]], dtype=float)
796 >>> b = np.array([2, 4, -1], dtype=float)
797 >>> x, exitCode = qmr(A, b)
798 >>> print(exitCode) # 0 indicates successful convergence
799 0
800 >>> np.allclose(A.dot(x), b)
801 True
802 """
803 A_ = A
804 A, M, x, b, postprocess = make_system(A, None, x0, b)
806 if M1 is None and M2 is None:
807 if hasattr(A_,'psolve'):
808 def left_psolve(b):
809 return A_.psolve(b,'left')
811 def right_psolve(b):
812 return A_.psolve(b,'right')
814 def left_rpsolve(b):
815 return A_.rpsolve(b,'left')
817 def right_rpsolve(b):
818 return A_.rpsolve(b,'right')
819 M1 = LinearOperator(A.shape, matvec=left_psolve, rmatvec=left_rpsolve)
820 M2 = LinearOperator(A.shape, matvec=right_psolve, rmatvec=right_rpsolve)
821 else:
822 def id(b):
823 return b
824 M1 = LinearOperator(A.shape, matvec=id, rmatvec=id)
825 M2 = LinearOperator(A.shape, matvec=id, rmatvec=id)
827 n = len(b)
828 if maxiter is None:
829 maxiter = n*10
831 ltr = _type_conv[x.dtype.char]
832 revcom = getattr(_iterative, ltr + 'qmrrevcom')
834 def get_residual():
835 return np.linalg.norm(A.matvec(x) - b)
836 atol = _get_atol(tol, atol, np.linalg.norm(b), get_residual, 'qmr')
837 if atol == 'exit':
838 return postprocess(x), 0
840 resid = atol
841 ndx1 = 1
842 ndx2 = -1
843 # Use _aligned_zeros to work around a f2py bug in Numpy 1.9.1
844 work = _aligned_zeros(11*n,x.dtype)
845 ijob = 1
846 info = 0
847 ftflag = True
848 iter_ = maxiter
849 while True:
850 olditer = iter_
851 x, iter_, resid, info, ndx1, ndx2, sclr1, sclr2, ijob = \
852 revcom(b, x, work, iter_, resid, info, ndx1, ndx2, ijob)
853 if callback is not None and iter_ > olditer:
854 callback(x)
855 slice1 = slice(ndx1-1, ndx1-1+n)
856 slice2 = slice(ndx2-1, ndx2-1+n)
857 if (ijob == -1):
858 if callback is not None:
859 callback(x)
860 break
861 elif (ijob == 1):
862 work[slice2] *= sclr2
863 work[slice2] += sclr1*A.matvec(work[slice1])
864 elif (ijob == 2):
865 work[slice2] *= sclr2
866 work[slice2] += sclr1*A.rmatvec(work[slice1])
867 elif (ijob == 3):
868 work[slice1] = M1.matvec(work[slice2])
869 elif (ijob == 4):
870 work[slice1] = M2.matvec(work[slice2])
871 elif (ijob == 5):
872 work[slice1] = M1.rmatvec(work[slice2])
873 elif (ijob == 6):
874 work[slice1] = M2.rmatvec(work[slice2])
875 elif (ijob == 7):
876 work[slice2] *= sclr2
877 work[slice2] += sclr1*A.matvec(x)
878 elif (ijob == 8):
879 if ftflag:
880 info = -1
881 ftflag = False
882 resid, info = _stoptest(work[slice1], atol)
883 ijob = 2
885 if info > 0 and iter_ == maxiter and not (resid <= atol):
886 # info isn't set appropriately otherwise
887 info = iter_
889 return postprocess(x), info