Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scipy/sparse/linalg/_isolve/minres.py: 3%

208 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-12 06:31 +0000

1from numpy import inner, zeros, inf, finfo 

2from numpy.linalg import norm 

3from math import sqrt 

4 

5from .utils import make_system 

6 

7__all__ = ['minres'] 

8 

9 

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

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

12 """ 

13 Use MINimum RESidual iteration to solve Ax=b 

14 

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

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

17 

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

19 

20 Parameters 

21 ---------- 

22 A : {sparse matrix, ndarray, LinearOperator} 

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

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

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

26 ``scipy.sparse.linalg.LinearOperator``. 

27 b : ndarray 

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

29 

30 Returns 

31 ------- 

32 x : ndarray 

33 The converged solution. 

34 info : 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 

39 

40 Other Parameters 

41 ---------------- 

42 x0 : ndarray 

43 Starting guess for the solution. 

44 shift : float 

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

46 tol : float 

47 Tolerance to achieve. The algorithm terminates when the relative 

48 residual is below `tol`. 

49 maxiter : integer 

50 Maximum number of iterations. Iteration will stop after maxiter 

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

52 M : {sparse matrix, ndarray, LinearOperator} 

53 Preconditioner for A. The preconditioner should approximate the 

54 inverse of A. Effective preconditioning dramatically improves the 

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

56 to reach a given error tolerance. 

57 callback : function 

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

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

60 show : bool 

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

62 during iterations. Default is ``False``. 

63 check : bool 

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

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

66 

67 Examples 

68 -------- 

69 >>> import numpy as np 

70 >>> from scipy.sparse import csc_matrix 

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

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

73 >>> A = A + A.T 

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

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

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

77 0 

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

79 True 

80 

81 References 

82 ---------- 

83 Solution of sparse indefinite systems of linear equations, 

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

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

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

87 

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

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

90 

91 """ 

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

93 

94 matvec = A.matvec 

95 psolve = M.matvec 

96 

97 first = 'Enter minres. ' 

98 last = 'Exit minres. ' 

99 

100 n = A.shape[0] 

101 

102 if maxiter is None: 

103 maxiter = 5 * n 

104 

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

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

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

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

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

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

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

112 ' The iteration limit was reached ', # 6 

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

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

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

116 

117 if show: 

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

119 print(first + 'n = %3g shift = %23.14e' % (n,shift)) 

120 print(first + 'itnlim = %3g rtol = %11.2e' % (maxiter,tol)) 

121 print() 

122 

123 istop = 0 

124 itn = 0 

125 Anorm = 0 

126 Acond = 0 

127 rnorm = 0 

128 ynorm = 0 

129 

130 xtype = x.dtype 

131 

132 eps = finfo(xtype).eps 

133 

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

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

136 # v is really P' v1. 

137 

138 if x0 is None: 

139 r1 = b.copy() 

140 else: 

141 r1 = b - A@x 

142 y = psolve(r1) 

143 

144 beta1 = inner(r1, y) 

145 

146 if beta1 < 0: 

147 raise ValueError('indefinite preconditioner') 

148 elif beta1 == 0: 

149 return (postprocess(x), 0) 

150 

151 bnorm = norm(b) 

152 if bnorm == 0: 

153 x = b 

154 return (postprocess(x), 0) 

155 

156 beta1 = sqrt(beta1) 

157 

158 if check: 

159 # are these too strict? 

160 

161 # see if A is symmetric 

162 w = matvec(y) 

163 r2 = matvec(w) 

164 s = inner(w,w) 

165 t = inner(y,r2) 

166 z = abs(s - t) 

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

168 if z > epsa: 

169 raise ValueError('non-symmetric matrix') 

170 

171 # see if M is symmetric 

172 r2 = psolve(y) 

173 s = inner(y,y) 

174 t = inner(r1,r2) 

175 z = abs(s - t) 

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

177 if z > epsa: 

178 raise ValueError('non-symmetric preconditioner') 

179 

180 # Initialize other quantities 

181 oldb = 0 

182 beta = beta1 

183 dbar = 0 

184 epsln = 0 

185 qrnorm = beta1 

186 phibar = beta1 

187 rhs1 = beta1 

188 rhs2 = 0 

189 tnorm2 = 0 

190 gmax = 0 

191 gmin = finfo(xtype).max 

192 cs = -1 

193 sn = 0 

194 w = zeros(n, dtype=xtype) 

195 w2 = zeros(n, dtype=xtype) 

196 r2 = r1 

197 

198 if show: 

199 print() 

200 print() 

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

202 

203 while itn < maxiter: 

204 itn += 1 

205 

206 s = 1.0/beta 

207 v = s*y 

208 

209 y = matvec(v) 

210 y = y - shift * v 

211 

212 if itn >= 2: 

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

214 

215 alfa = inner(v,y) 

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

217 r1 = r2 

218 r2 = y 

219 y = psolve(r2) 

220 oldb = beta 

221 beta = inner(r2,y) 

222 if beta < 0: 

223 raise ValueError('non-symmetric matrix') 

224 beta = sqrt(beta) 

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

226 

227 if itn == 1: 

228 if beta/beta1 <= 10*eps: 

229 istop = -1 # Terminate later 

230 

231 # Apply previous rotation Qk-1 to get 

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

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

234 

235 oldeps = epsln 

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

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

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

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

240 root = norm([gbar, dbar]) 

241 Arnorm = phibar * root 

242 

243 # Compute the next plane rotation Qk 

244 

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

246 gamma = max(gamma, eps) 

247 cs = gbar / gamma # ck 

248 sn = beta / gamma # sk 

249 phi = cs * phibar # phik 

250 phibar = sn * phibar # phibark+1 

251 

252 # Update x. 

253 

254 denom = 1.0/gamma 

255 w1 = w2 

256 w2 = w 

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

258 x = x + phi*w 

259 

260 # Go round again. 

261 

262 gmax = max(gmax, gamma) 

263 gmin = min(gmin, gamma) 

264 z = rhs1 / gamma 

265 rhs1 = rhs2 - delta*z 

266 rhs2 = - epsln*z 

267 

268 # Estimate various norms and test for convergence. 

269 

270 Anorm = sqrt(tnorm2) 

271 ynorm = norm(x) 

272 epsa = Anorm * eps 

273 epsx = Anorm * ynorm * eps 

274 epsr = Anorm * ynorm * tol 

275 diag = gbar 

276 

277 if diag == 0: 

278 diag = epsa 

279 

280 qrnorm = phibar 

281 rnorm = qrnorm 

282 if ynorm == 0 or Anorm == 0: 

283 test1 = inf 

284 else: 

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

286 if Anorm == 0: 

287 test2 = inf 

288 else: 

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

290 

291 # Estimate cond(A). 

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

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

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

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

296 

297 Acond = gmax/gmin 

298 

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

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

301 

302 if istop == 0: 

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

304 t2 = 1 + test2 

305 if t2 <= 1: 

306 istop = 2 

307 if t1 <= 1: 

308 istop = 1 

309 

310 if itn >= maxiter: 

311 istop = 6 

312 if Acond >= 0.1/eps: 

313 istop = 4 

314 if epsx >= beta1: 

315 istop = 3 

316 # if rnorm <= epsx : istop = 2 

317 # if rnorm <= epsr : istop = 1 

318 if test2 <= tol: 

319 istop = 2 

320 if test1 <= tol: 

321 istop = 1 

322 

323 # See if it is time to print something. 

324 

325 prnt = False 

326 if n <= 40: 

327 prnt = True 

328 if itn <= 10: 

329 prnt = True 

330 if itn >= maxiter-10: 

331 prnt = True 

332 if itn % 10 == 0: 

333 prnt = True 

334 if qrnorm <= 10*epsx: 

335 prnt = True 

336 if qrnorm <= 10*epsr: 

337 prnt = True 

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

339 prnt = True 

340 if istop != 0: 

341 prnt = True 

342 

343 if show and prnt: 

344 str1 = '%6g %12.5e %10.3e' % (itn, x[0], test1) 

345 str2 = ' %10.3e' % (test2,) 

346 str3 = ' %8.1e %8.1e %8.1e' % (Anorm, Acond, gbar/Anorm) 

347 

348 print(str1 + str2 + str3) 

349 

350 if itn % 10 == 0: 

351 print() 

352 

353 if callback is not None: 

354 callback(x) 

355 

356 if istop != 0: 

357 break # TODO check this 

358 

359 if show: 

360 print() 

361 print(last + ' istop = %3g itn =%5g' % (istop,itn)) 

362 print(last + ' Anorm = %12.4e Acond = %12.4e' % (Anorm,Acond)) 

363 print(last + ' rnorm = %12.4e ynorm = %12.4e' % (rnorm,ynorm)) 

364 print(last + ' Arnorm = %12.4e' % (Arnorm,)) 

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

366 

367 if istop == 6: 

368 info = maxiter 

369 else: 

370 info = 0 

371 

372 return (postprocess(x),info) 

373 

374 

375if __name__ == '__main__': 

376 from numpy import arange 

377 from scipy.sparse import spdiags 

378 

379 n = 10 

380 

381 residuals = [] 

382 

383 def cb(x): 

384 residuals.append(norm(b - A@x)) 

385 

386 # A = poisson((10,),format='csr') 

387 A = spdiags([arange(1,n+1,dtype=float)], [0], n, n, format='csr') 

388 M = spdiags([1.0/arange(1,n+1,dtype=float)], [0], n, n, format='csr') 

389 A.psolve = M.matvec 

390 b = zeros(A.shape[0]) 

391 x = minres(A,b,tol=1e-12,maxiter=None,callback=cb) 

392 # x = cg(A,b,x0=b,tol=1e-12,maxiter=None,callback=cb)[0]