Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/scipy/sparse/linalg/_isolve/minres.py: 4%

198 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-23 06:43 +0000

1from numpy import inner, zeros, inf, finfo 

2from numpy.linalg import norm 

3from math import sqrt 

4 

5from .utils import make_system 

6from scipy._lib.deprecation import _deprecate_positional_args 

7 

8__all__ = ['minres'] 

9 

10 

11@_deprecate_positional_args(version="1.14.0") 

12def minres(A, b, x0=None, *, shift=0.0, tol=1e-5, maxiter=None, 

13 M=None, callback=None, show=False, check=False): 

14 """ 

15 Use MINimum RESidual iteration to solve Ax=b 

16 

17 MINRES minimizes norm(Ax - b) for a real symmetric matrix A. Unlike 

18 the Conjugate Gradient method, A can be indefinite or singular. 

19 

20 If shift != 0 then the method solves (A - shift*I)x = b 

21 

22 Parameters 

23 ---------- 

24 A : {sparse matrix, ndarray, LinearOperator} 

25 The real symmetric N-by-N matrix of the linear system 

26 Alternatively, ``A`` can be a linear operator which can 

27 produce ``Ax`` using, e.g., 

28 ``scipy.sparse.linalg.LinearOperator``. 

29 b : ndarray 

30 Right hand side of the linear system. Has shape (N,) or (N,1). 

31 

32 Returns 

33 ------- 

34 x : ndarray 

35 The converged solution. 

36 info : integer 

37 Provides convergence information: 

38 0 : successful exit 

39 >0 : convergence to tolerance not achieved, number of iterations 

40 <0 : illegal input or breakdown 

41 

42 Other Parameters 

43 ---------------- 

44 x0 : ndarray 

45 Starting guess for the solution. 

46 shift : float 

47 Value to apply to the system ``(A - shift * I)x = b``. Default is 0. 

48 tol : float 

49 Tolerance to achieve. The algorithm terminates when the relative 

50 residual is below `tol`. 

51 maxiter : integer 

52 Maximum number of iterations. Iteration will stop after maxiter 

53 steps even if the specified tolerance has not been achieved. 

54 M : {sparse matrix, ndarray, LinearOperator} 

55 Preconditioner for A. The preconditioner should approximate the 

56 inverse of A. Effective preconditioning dramatically improves the 

57 rate of convergence, which implies that fewer iterations are needed 

58 to reach a given error tolerance. 

59 callback : function 

60 User-supplied function to call after each iteration. It is called 

61 as callback(xk), where xk is the current solution vector. 

62 show : bool 

63 If ``True``, print out a summary and metrics related to the solution 

64 during iterations. Default is ``False``. 

65 check : bool 

66 If ``True``, run additional input validation to check that `A` and 

67 `M` (if specified) are symmetric. Default is ``False``. 

68 

69 Examples 

70 -------- 

71 >>> import numpy as np 

72 >>> from scipy.sparse import csc_matrix 

73 >>> from scipy.sparse.linalg import minres 

74 >>> A = csc_matrix([[3, 2, 0], [1, -1, 0], [0, 5, 1]], dtype=float) 

75 >>> A = A + A.T 

76 >>> b = np.array([2, 4, -1], dtype=float) 

77 >>> x, exitCode = minres(A, b) 

78 >>> print(exitCode) # 0 indicates successful convergence 

79 0 

80 >>> np.allclose(A.dot(x), b) 

81 True 

82 

83 References 

84 ---------- 

85 Solution of sparse indefinite systems of linear equations, 

86 C. C. Paige and M. A. Saunders (1975), 

87 SIAM J. Numer. Anal. 12(4), pp. 617-629. 

88 https://web.stanford.edu/group/SOL/software/minres/ 

89 

90 This file is a translation of the following MATLAB implementation: 

91 https://web.stanford.edu/group/SOL/software/minres/minres-matlab.zip 

92 

93 """ 

94 A, M, x, b, postprocess = make_system(A, M, x0, b) 

95 

96 matvec = A.matvec 

97 psolve = M.matvec 

98 

99 first = 'Enter minres. ' 

100 last = 'Exit minres. ' 

101 

102 n = A.shape[0] 

103 

104 if maxiter is None: 

105 maxiter = 5 * n 

106 

107 msg = [' beta2 = 0. If M = I, b and x are eigenvectors ', # -1 

108 ' beta1 = 0. The exact solution is x0 ', # 0 

109 ' A solution to Ax = b was found, given rtol ', # 1 

110 ' A least-squares solution was found, given rtol ', # 2 

111 ' Reasonable accuracy achieved, given eps ', # 3 

112 ' x has converged to an eigenvector ', # 4 

113 ' acond has exceeded 0.1/eps ', # 5 

114 ' The iteration limit was reached ', # 6 

115 ' A does not define a symmetric matrix ', # 7 

116 ' M does not define a symmetric matrix ', # 8 

117 ' M does not define a pos-def preconditioner '] # 9 

118 

119 if show: 

120 print(first + 'Solution of symmetric Ax = b') 

121 print(first + f'n = {n:3g} shift = {shift:23.14e}') 

122 print(first + f'itnlim = {maxiter:3g} rtol = {tol:11.2e}') 

123 print() 

124 

125 istop = 0 

126 itn = 0 

127 Anorm = 0 

128 Acond = 0 

129 rnorm = 0 

130 ynorm = 0 

131 

132 xtype = x.dtype 

133 

134 eps = finfo(xtype).eps 

135 

136 # Set up y and v for the first Lanczos vector v1. 

137 # y = beta1 P' v1, where P = C**(-1). 

138 # v is really P' v1. 

139 

140 if x0 is None: 

141 r1 = b.copy() 

142 else: 

143 r1 = b - A@x 

144 y = psolve(r1) 

145 

146 beta1 = inner(r1, y) 

147 

148 if beta1 < 0: 

149 raise ValueError('indefinite preconditioner') 

150 elif beta1 == 0: 

151 return (postprocess(x), 0) 

152 

153 bnorm = norm(b) 

154 if bnorm == 0: 

155 x = b 

156 return (postprocess(x), 0) 

157 

158 beta1 = sqrt(beta1) 

159 

160 if check: 

161 # are these too strict? 

162 

163 # see if A is symmetric 

164 w = matvec(y) 

165 r2 = matvec(w) 

166 s = inner(w,w) 

167 t = inner(y,r2) 

168 z = abs(s - t) 

169 epsa = (s + eps) * eps**(1.0/3.0) 

170 if z > epsa: 

171 raise ValueError('non-symmetric matrix') 

172 

173 # see if M is symmetric 

174 r2 = psolve(y) 

175 s = inner(y,y) 

176 t = inner(r1,r2) 

177 z = abs(s - t) 

178 epsa = (s + eps) * eps**(1.0/3.0) 

179 if z > epsa: 

180 raise ValueError('non-symmetric preconditioner') 

181 

182 # Initialize other quantities 

183 oldb = 0 

184 beta = beta1 

185 dbar = 0 

186 epsln = 0 

187 qrnorm = beta1 

188 phibar = beta1 

189 rhs1 = beta1 

190 rhs2 = 0 

191 tnorm2 = 0 

192 gmax = 0 

193 gmin = finfo(xtype).max 

194 cs = -1 

195 sn = 0 

196 w = zeros(n, dtype=xtype) 

197 w2 = zeros(n, dtype=xtype) 

198 r2 = r1 

199 

200 if show: 

201 print() 

202 print() 

203 print(' Itn x(1) Compatible LS norm(A) cond(A) gbar/|A|') 

204 

205 while itn < maxiter: 

206 itn += 1 

207 

208 s = 1.0/beta 

209 v = s*y 

210 

211 y = matvec(v) 

212 y = y - shift * v 

213 

214 if itn >= 2: 

215 y = y - (beta/oldb)*r1 

216 

217 alfa = inner(v,y) 

218 y = y - (alfa/beta)*r2 

219 r1 = r2 

220 r2 = y 

221 y = psolve(r2) 

222 oldb = beta 

223 beta = inner(r2,y) 

224 if beta < 0: 

225 raise ValueError('non-symmetric matrix') 

226 beta = sqrt(beta) 

227 tnorm2 += alfa**2 + oldb**2 + beta**2 

228 

229 if itn == 1: 

230 if beta/beta1 <= 10*eps: 

231 istop = -1 # Terminate later 

232 

233 # Apply previous rotation Qk-1 to get 

234 # [deltak epslnk+1] = [cs sn][dbark 0 ] 

235 # [gbar k dbar k+1] [sn -cs][alfak betak+1]. 

236 

237 oldeps = epsln 

238 delta = cs * dbar + sn * alfa # delta1 = 0 deltak 

239 gbar = sn * dbar - cs * alfa # gbar 1 = alfa1 gbar k 

240 epsln = sn * beta # epsln2 = 0 epslnk+1 

241 dbar = - cs * beta # dbar 2 = beta2 dbar k+1 

242 root = norm([gbar, dbar]) 

243 Arnorm = phibar * root 

244 

245 # Compute the next plane rotation Qk 

246 

247 gamma = norm([gbar, beta]) # gammak 

248 gamma = max(gamma, eps) 

249 cs = gbar / gamma # ck 

250 sn = beta / gamma # sk 

251 phi = cs * phibar # phik 

252 phibar = sn * phibar # phibark+1 

253 

254 # Update x. 

255 

256 denom = 1.0/gamma 

257 w1 = w2 

258 w2 = w 

259 w = (v - oldeps*w1 - delta*w2) * denom 

260 x = x + phi*w 

261 

262 # Go round again. 

263 

264 gmax = max(gmax, gamma) 

265 gmin = min(gmin, gamma) 

266 z = rhs1 / gamma 

267 rhs1 = rhs2 - delta*z 

268 rhs2 = - epsln*z 

269 

270 # Estimate various norms and test for convergence. 

271 

272 Anorm = sqrt(tnorm2) 

273 ynorm = norm(x) 

274 epsa = Anorm * eps 

275 epsx = Anorm * ynorm * eps 

276 epsr = Anorm * ynorm * tol 

277 diag = gbar 

278 

279 if diag == 0: 

280 diag = epsa 

281 

282 qrnorm = phibar 

283 rnorm = qrnorm 

284 if ynorm == 0 or Anorm == 0: 

285 test1 = inf 

286 else: 

287 test1 = rnorm / (Anorm*ynorm) # ||r|| / (||A|| ||x||) 

288 if Anorm == 0: 

289 test2 = inf 

290 else: 

291 test2 = root / Anorm # ||Ar|| / (||A|| ||r||) 

292 

293 # Estimate cond(A). 

294 # In this version we look at the diagonals of R in the 

295 # factorization of the lower Hessenberg matrix, Q @ H = R, 

296 # where H is the tridiagonal matrix from Lanczos with one 

297 # extra row, beta(k+1) e_k^T. 

298 

299 Acond = gmax/gmin 

300 

301 # See if any of the stopping criteria are satisfied. 

302 # In rare cases, istop is already -1 from above (Abar = const*I). 

303 

304 if istop == 0: 

305 t1 = 1 + test1 # These tests work if tol < eps 

306 t2 = 1 + test2 

307 if t2 <= 1: 

308 istop = 2 

309 if t1 <= 1: 

310 istop = 1 

311 

312 if itn >= maxiter: 

313 istop = 6 

314 if Acond >= 0.1/eps: 

315 istop = 4 

316 if epsx >= beta1: 

317 istop = 3 

318 # if rnorm <= epsx : istop = 2 

319 # if rnorm <= epsr : istop = 1 

320 if test2 <= tol: 

321 istop = 2 

322 if test1 <= tol: 

323 istop = 1 

324 

325 # See if it is time to print something. 

326 

327 prnt = False 

328 if n <= 40: 

329 prnt = True 

330 if itn <= 10: 

331 prnt = True 

332 if itn >= maxiter-10: 

333 prnt = True 

334 if itn % 10 == 0: 

335 prnt = True 

336 if qrnorm <= 10*epsx: 

337 prnt = True 

338 if qrnorm <= 10*epsr: 

339 prnt = True 

340 if Acond <= 1e-2/eps: 

341 prnt = True 

342 if istop != 0: 

343 prnt = True 

344 

345 if show and prnt: 

346 str1 = f'{itn:6g} {x[0]:12.5e} {test1:10.3e}' 

347 str2 = f' {test2:10.3e}' 

348 str3 = f' {Anorm:8.1e} {Acond:8.1e} {gbar/Anorm:8.1e}' 

349 

350 print(str1 + str2 + str3) 

351 

352 if itn % 10 == 0: 

353 print() 

354 

355 if callback is not None: 

356 callback(x) 

357 

358 if istop != 0: 

359 break # TODO check this 

360 

361 if show: 

362 print() 

363 print(last + f' istop = {istop:3g} itn ={itn:5g}') 

364 print(last + f' Anorm = {Anorm:12.4e} Acond = {Acond:12.4e}') 

365 print(last + f' rnorm = {rnorm:12.4e} ynorm = {ynorm:12.4e}') 

366 print(last + f' Arnorm = {Arnorm:12.4e}') 

367 print(last + msg[istop+1]) 

368 

369 if istop == 6: 

370 info = maxiter 

371 else: 

372 info = 0 

373 

374 return (postprocess(x),info)