tfqmr.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import numpy as np
  2. from .utils import make_system
  3. __all__ = ['tfqmr']
  4. def tfqmr(A, b, x0=None, tol=1e-5, maxiter=None, M=None,
  5. callback=None, atol=None, show=False):
  6. """
  7. Use Transpose-Free Quasi-Minimal Residual iteration to solve ``Ax = b``.
  8. Parameters
  9. ----------
  10. A : {sparse matrix, ndarray, LinearOperator}
  11. The real or complex N-by-N matrix of the linear system.
  12. Alternatively, `A` can be a linear operator which can
  13. produce ``Ax`` using, e.g.,
  14. `scipy.sparse.linalg.LinearOperator`.
  15. b : {ndarray}
  16. Right hand side of the linear system. Has shape (N,) or (N,1).
  17. x0 : {ndarray}
  18. Starting guess for the solution.
  19. tol, atol : float, optional
  20. Tolerances for convergence, ``norm(residual) <= max(tol*norm(b-Ax0), atol)``.
  21. The default for `tol` is 1.0e-5.
  22. The default for `atol` is ``tol * norm(b-Ax0)``.
  23. .. warning::
  24. The default value for `atol` will be changed in a future release.
  25. For future compatibility, specify `atol` explicitly.
  26. maxiter : int, optional
  27. Maximum number of iterations. Iteration will stop after maxiter
  28. steps even if the specified tolerance has not been achieved.
  29. Default is ``min(10000, ndofs * 10)``, where ``ndofs = A.shape[0]``.
  30. M : {sparse matrix, ndarray, LinearOperator}
  31. Inverse of the preconditioner of A. M should approximate the
  32. inverse of A and be easy to solve for (see Notes). Effective
  33. preconditioning dramatically improves the rate of convergence,
  34. which implies that fewer iterations are needed to reach a given
  35. error tolerance. By default, no preconditioner is used.
  36. callback : function, optional
  37. User-supplied function to call after each iteration. It is called
  38. as `callback(xk)`, where `xk` is the current solution vector.
  39. show : bool, optional
  40. Specify ``show = True`` to show the convergence, ``show = False`` is
  41. to close the output of the convergence.
  42. Default is `False`.
  43. Returns
  44. -------
  45. x : ndarray
  46. The converged solution.
  47. info : int
  48. Provides convergence information:
  49. - 0 : successful exit
  50. - >0 : convergence to tolerance not achieved, number of iterations
  51. - <0 : illegal input or breakdown
  52. Notes
  53. -----
  54. The Transpose-Free QMR algorithm is derived from the CGS algorithm.
  55. However, unlike CGS, the convergence curves for the TFQMR method is
  56. smoothed by computing a quasi minimization of the residual norm. The
  57. implementation supports left preconditioner, and the "residual norm"
  58. to compute in convergence criterion is actually an upper bound on the
  59. actual residual norm ``||b - Axk||``.
  60. References
  61. ----------
  62. .. [1] R. W. Freund, A Transpose-Free Quasi-Minimal Residual Algorithm for
  63. Non-Hermitian Linear Systems, SIAM J. Sci. Comput., 14(2), 470-482,
  64. 1993.
  65. .. [2] Y. Saad, Iterative Methods for Sparse Linear Systems, 2nd edition,
  66. SIAM, Philadelphia, 2003.
  67. .. [3] C. T. Kelley, Iterative Methods for Linear and Nonlinear Equations,
  68. number 16 in Frontiers in Applied Mathematics, SIAM, Philadelphia,
  69. 1995.
  70. Examples
  71. --------
  72. >>> import numpy as np
  73. >>> from scipy.sparse import csc_matrix
  74. >>> from scipy.sparse.linalg import tfqmr
  75. >>> A = csc_matrix([[3, 2, 0], [1, -1, 0], [0, 5, 1]], dtype=float)
  76. >>> b = np.array([2, 4, -1], dtype=float)
  77. >>> x, exitCode = tfqmr(A, b)
  78. >>> print(exitCode) # 0 indicates successful convergence
  79. 0
  80. >>> np.allclose(A.dot(x), b)
  81. True
  82. """
  83. # Check data type
  84. dtype = A.dtype
  85. if np.issubdtype(dtype, np.int64):
  86. dtype = float
  87. A = A.astype(dtype)
  88. if np.issubdtype(b.dtype, np.int64):
  89. b = b.astype(dtype)
  90. A, M, x, b, postprocess = make_system(A, M, x0, b)
  91. # Check if the R.H.S is a zero vector
  92. if np.linalg.norm(b) == 0.:
  93. x = b.copy()
  94. return (postprocess(x), 0)
  95. ndofs = A.shape[0]
  96. if maxiter is None:
  97. maxiter = min(10000, ndofs * 10)
  98. if x0 is None:
  99. r = b.copy()
  100. else:
  101. r = b - A.matvec(x)
  102. u = r
  103. w = r.copy()
  104. # Take rstar as b - Ax0, that is rstar := r = b - Ax0 mathematically
  105. rstar = r
  106. v = M.matvec(A.matvec(r))
  107. uhat = v
  108. d = theta = eta = 0.
  109. rho = np.inner(rstar.conjugate(), r)
  110. rhoLast = rho
  111. r0norm = np.sqrt(rho)
  112. tau = r0norm
  113. if r0norm == 0:
  114. return (postprocess(x), 0)
  115. if atol is None:
  116. atol = tol * r0norm
  117. else:
  118. atol = max(atol, tol * r0norm)
  119. for iter in range(maxiter):
  120. even = iter % 2 == 0
  121. if (even):
  122. vtrstar = np.inner(rstar.conjugate(), v)
  123. # Check breakdown
  124. if vtrstar == 0.:
  125. return (postprocess(x), -1)
  126. alpha = rho / vtrstar
  127. uNext = u - alpha * v # [1]-(5.6)
  128. w -= alpha * uhat # [1]-(5.8)
  129. d = u + (theta**2 / alpha) * eta * d # [1]-(5.5)
  130. # [1]-(5.2)
  131. theta = np.linalg.norm(w) / tau
  132. c = np.sqrt(1. / (1 + theta**2))
  133. tau *= theta * c
  134. # Calculate step and direction [1]-(5.4)
  135. eta = (c**2) * alpha
  136. z = M.matvec(d)
  137. x += eta * z
  138. if callback is not None:
  139. callback(x)
  140. # Convergence criteron
  141. if tau * np.sqrt(iter+1) < atol:
  142. if (show):
  143. print("TFQMR: Linear solve converged due to reach TOL "
  144. "iterations {}".format(iter+1))
  145. return (postprocess(x), 0)
  146. if (not even):
  147. # [1]-(5.7)
  148. rho = np.inner(rstar.conjugate(), w)
  149. beta = rho / rhoLast
  150. u = w + beta * u
  151. v = beta * uhat + (beta**2) * v
  152. uhat = M.matvec(A.matvec(u))
  153. v += uhat
  154. else:
  155. uhat = M.matvec(A.matvec(uNext))
  156. u = uNext
  157. rhoLast = rho
  158. if (show):
  159. print("TFQMR: Linear solve not converged due to reach MAXIT "
  160. "iterations {}".format(iter+1))
  161. return (postprocess(x), maxiter)