def solve(self, rhs, **kwargs): """ Solve a linear system with `rhs` as right-hand side by the CG method. The vector `rhs` should be a Numpy array. :Keywords: :guess: Initial guess (Numpy array). Default: 0. :matvec_max: Max. number of operator-vector produts. Default: 2n. :check_symmetric: Ensure operator is symmetric. Default: False. :check_curvature: Ensure operator is positive definite. Default: True. :store_resids: Store full residual vector history. Default: False. :store_iterates: Store full iterate history. Default: False. """ n = rhs.shape[0] nMatvec = 0 definite = True check_sym = kwargs.get('check_symmetric', False) check_curvature = kwargs.get('check_curvature', True) store_resids = kwargs.get('store_resids', False) store_iterates = kwargs.get('store_iterates', False) if check_sym: if not check_symmetric(self.op): self.logger.error('Coefficient operator is not symmetric') return # Initial guess result_type = np.result_type(self.op.dtype, rhs.dtype) guess_supplied = 'guess' in kwargs.keys() x = kwargs.get('guess', np.zeros(n)).astype(result_type) if store_iterates: self.iterates.append(x.copy()) matvec_max = kwargs.get('matvec_max', 2*n) # Initial residual vector r = -rhs if guess_supplied: r += self.op * x nMatvec += 1 # Initial preconditioned residual vector if self.precon is not None: y = self.precon * r else: y = r if store_resids: self.resids.append(y.copy()) ry = np.dot(r,y) self.residNorm0 = residNorm = np.abs(np.sqrt(ry)) self.residHistory.append(self.residNorm0) threshold = max(self.abstol, self.reltol * self.residNorm0) p = -r # Initial search direction (copy not to overwrite rhs if x=0) hdr_fmt = '%6s %7s %8s' hdr = hdr_fmt % ('Matvec', 'Resid', 'Curv') self.logger.info(hdr) self.logger.info('-' * len(hdr)) info = '%6d %7.1e' % (nMatvec, residNorm) self.logger.info(info) while residNorm > threshold and nMatvec < matvec_max and definite: Ap = self.op * p nMatvec += 1 pAp = np.dot(p, Ap) if check_curvature: if np.imag(pAp) > 1.0e-8 * np.abs(pAp) or np.real(pAp) <= 0: self.logger.error('Coefficient operator is not positive definite') self.infiniteDescent = p definite = False continue # Compute step length alpha = ry/pAp # Update estimate and residual x += alpha * p r += alpha * Ap if store_iterates: self.iterates.append(x.copy()) # Compute preconditioned residual if self.precon is not None: y = self.precon * r else: y = r if store_resids: self.resids.append(y.copy()) # Update preconditioned residual norm ry_next = np.dot(r,y) # Update search direction beta = ry_next/ry p *= beta p -= r ry = ry_next residNorm = np.abs(np.sqrt(ry)) self.residHistory.append(residNorm) info = '%6d %7.1e %8.1e' % (nMatvec, residNorm, np.real(pAp)) self.logger.info(info) self.converged = residNorm <= threshold self.definite = definite self.nMatvec = nMatvec self.bestSolution = self.x = x self.residNorm = residNorm
def solve(self, b, **kwargs): A = self.op n = b.shape[0] # Read keyword arguments precon = kwargs.get('precon', None) shift = kwargs.get('shift', 0.0) show = kwargs.get('show', True) check = kwargs.get('check', True) itnlim = kwargs.get('itnlim', 5*n) rtol = kwargs.get('rtol', 1.0e-12) etol = kwargs.get('etol', 1.0e-6) store_resids = kwargs.get('store_resids', False) store_iterates = kwargs.get('store_iterates', False) window = kwargs.get('window', 5) self.dir_errors_window = [] # Direct error estimates. self.iterates = [] result_type = np.result_type(A.dtype, b.dtype) x = zeros(n, dtype=result_type) xNrgNorm2 = 0.0 # Squared energy norm of final solution. dErr = zeros(window) # Truncated direct error terms. trncDirErr = 0 # Truncated direct error. if store_iterates: self.iterates.append(x.copy()) # Transfer some pointers for readability eps = self.eps if show: print self.first + 'Solution of symmetric Ax = b' print 'n = %3d precon = %4s shift = %23.14e'\ % (n, (precon != None), shift) print 'itnlim = %3d rtol = %11.2e\n' % (itnlim, rtol) istop = 0; itn = 0; Anorm = 0.0; Acond = 0.0; rnorm = 0.0; ynorm = 0.0; done = False; #------------------------------------------------------------------ # Set up y and v for the first Lanczos vector v1. # y = beta1 P' v1, where P = C**(-1). # v is really P' v1. #------------------------------------------------------------------ r1 = b if precon is not None: y = precon * b else: y = b.copy() beta1 = dot(b,y) # Test for an indefinite preconditioner. # If b = 0 exactly, stop with x = 0. if beta1 < 0: istop = 9 self.show = True done = True if beta1 == 0.0: self.show = True done = True if beta1 > 0: beta1 = sqrt(beta1); # Normalize y to get v1 later. self.residNorm0 = beta1 self.residNorm0 = beta1 # Initial residual norm. # See if A is symmetric. if check: if not check_symmetric(A): istop = 7 done = True self.show = True # See if preconditioner is symmetric. if check and (precon is not None): if not check_symmetric(precon): istop = 8 show = True done = True # ------------------------------------------------------------------- # Initialize other quantities. # ------------------------------------------------------------------ oldb = 0.0; beta = beta1; dbar = 0.0; epsln = 0.0 qrnorm = beta1; phibar = beta1; rhs1 = beta1; Arnorm = 0.0 rhs2 = 0.0; tnorm2 = 0.0; ynorm2 = 0.0 cs = -1.0; sn = 0.0 w = zeros(n, dtype=result_type) w2 = zeros(n, dtype=result_type) r2 = r1.copy() if show: print ' '*2 head1 = ' Itn x[0] Compatible LS' head2 = ' norm(A) cond(A) gbar/|A|' ###### Check gbar print head1 + head2 # --------------------------------------------------------------------- # Main iteration loop. # -------------------------------------------------------------------- if not done: # k = itn = 1 first time through while itn < itnlim: itn = itn + 1 # ------------------------------------------------------------- # Obtain quantities for the next Lanczos vector vk+1, k=1,2,... # The general iteration is similar to the case k=1 with v0 = 0: # # p1 = Operator * v1 - beta1 * v0, # alpha1 = v1'p1, # q2 = p2 - alpha1 * v1, # beta2^2 = q2'q2, # v2 = (1/beta2) q2. # # Again, y = betak P vk, where P = C**(-1). # .... more description needed. # ------------------------------------------------------------- s = 1.0/beta # Normalize previous vector (in y). v = s*y # v = vk if P = I y = A * v y -= shift*v if itn >= 2: y = y - (beta/oldb)*r1 alfa = dot(v,y) y = (- alfa/beta)*r2 + y r1 = r2.copy() r2 = y.copy() if precon is not None: y = precon * r2 oldb = beta beta = dot(r2,y) if beta < 0: istop = 6 break beta = sqrt(beta) tnorm2 = tnorm2 + alfa**2 + oldb**2 + beta**2 if itn==1: # Initialize a few things. if beta/beta1 <= 10*eps: # beta2 = 0 or ~ 0. istop = -1 # Terminate later. # tnorm2 = alfa**2 ?? gmax = abs(alfa) # alpha1 gmin = gmax # alpha1 # Apply previous rotation Qk-1 to get # [deltak epslnk+1] = [cs sn][dbark 0 ] # [gbar k dbar k+1] [sn -cs][alfak betak+1]. oldeps = epsln delta = cs * dbar + sn * alfa # delta1 = 0 deltak gbar = sn * dbar - cs * alfa # gbar 1 = alfa1 gbar k # Note: There is severe cancellation in the computation of gbar #print ' sn = %21.15e\n dbar = %21.15e\n cs = %21.15e\n alfa = %21.15e\n sn*dbar-cs*alfa = %21.15e\n gbar =%21.15e' % (sn, dbar, cs, alfa, sn*dbar-cs*alfa, gbar) epsln = sn * beta # epsln2 = 0 epslnk+1 dbar = - cs * beta # dbar 2 = beta2 dbar k+1 root = self.normof2(gbar, dbar) Arnorm = phibar * root # Compute the next plane rotation Qk gamma = self.normof2(gbar, beta) # gammak gamma = max(gamma, eps) cs = gbar / gamma # ck sn = beta / gamma # sk phi = cs * phibar # phik phibar = sn * phibar # phibark+1 # Update x. denom = 1.0/gamma w1 = w2.copy() w2 = w.copy() w = (v - oldeps*w1 - delta*w2) * denom x += phi*w #x = x + phi*w if store_iterates: self.iterates.append(x.copy()) # Update energy norm of x. xNrgNorm2 += phi*phi dErr[itn % window] = phi if itn > window: trncDirErr = norm(dErr) xNrgNorm = sqrt(xNrgNorm2) self.dir_errors_window.append(trncDirErr / xNrgNorm) if trncDirErr < etol * xNrgNorm: istop = 10 # Go round again. gmax = max(gmax, gamma) gmin = min(gmin, gamma) z = rhs1 / gamma ynorm2 = z**2 + ynorm2 rhs1 = rhs2 - delta*z rhs2 = - epsln*z # Estimate various norms and test for convergence. Anorm = sqrt(tnorm2) ynorm = sqrt(ynorm2) epsa = Anorm * eps epsx = Anorm * ynorm * eps epsr = Anorm * ynorm * rtol diag = gbar if diag==0: diag = epsa qrnorm = phibar rnorm = qrnorm test1 = rnorm / (Anorm*ynorm) # ||r|| / (||A|| ||x||) test2 = root / Anorm # ||Ar|| / (||A|| ||r||) self.residHistory.append(rnorm) # Estimate cond(A). # In this version we look at the diagonals of R in the # factorization of the lower Hessenberg matrix, Q * H = R, # where H is the tridiagonal matrix from Lanczos with one # extra row, beta(k+1) e_k^T. Acond = gmax/gmin # See if any of the stopping criteria are satisfied. # In rare cases istop is already -1 from above (Abar = const*I) if istop==0: t1 = 1 + test1 # These tests work if rtol < eps t2 = 1 + test2 if t2 <= 1: istop = 2 if t1 <= 1: istop = 1 if itn >= itnlim: istop = 6 if Acond >= 0.1/eps: istop = 4 if epsx >= beta1: istop = 3 # if rnorm <= epsx: istop = 2 # if rnorm <= epsr: istop = 1 if test2 <= rtol: istop = 2 if test1 <= rtol: istop = 1 # See if it is time to print something. prnt = False if n <= 40: prnt = True if itn <= 10: prnt = True if itn >= itnlim-10: prnt = True if (itn % 10)==0: prnt = True if qrnorm <= 10*epsx: prnt = True if qrnorm <= 10*epsr: prnt = True if Acond <= 1e-2/eps: prnt = True if istop != 0: prnt = True if show and prnt: str1 = '%6g %12.5e %10.3e' % (itn, x[0], test1) str2 = ' %10.3e' % test2 str3 = ' %8.1e %8.1e %8.1e' % (Anorm, Acond, gbar/Anorm) print str1 + str2 + str3 if istop > 0: break if (itn % 10)==0: print ' ' # Display final status. if show: last = self.last print last+' istop = %3g itn =%5g' % (istop,itn) print last+' Anorm = %12.4e Acond = %12.4e' %(Anorm,Acond) print last+' rnorm = %12.4e ynorm = %12.4e' % (rnorm,ynorm) print last+' Arnorm = %12.4e' % Arnorm print last+self.msg[istop+1] self.converged = istop in [1,2,3,4,10] if istop == 10: self.status = 'direct error small' self.bestSolution = self.x = x self.istop = istop self.itn = itn self.residNorm = self.rnorm = rnorm self.x = self.bestSolution = x self.istop = istop self.itn = self.nMatvec = itn self.rnorm = self.residNorm = rnorm self.Arnorm = Arnorm self.Anorm = Anorm self.Acond = Acond self.ynorm = ynorm return
def solve(self, b, **kwargs): A = self.op n = b.shape[0] # Read keyword arguments precon = kwargs.get('precon', None) shift = kwargs.get('shift', 0.0) show = kwargs.get('show', True) check = kwargs.get('check', True) itnlim = kwargs.get('itnlim', 5 * n) rtol = kwargs.get('rtol', 1.0e-12) etol = kwargs.get('etol', 1.0e-6) store_resids = kwargs.get('store_resids', False) store_iterates = kwargs.get('store_iterates', False) window = kwargs.get('window', 5) self.dir_errors_window = [] # Direct error estimates. self.iterates = [] result_type = np.result_type(A.dtype, b.dtype) x = zeros(n, dtype=result_type) xNrgNorm2 = 0.0 # Squared energy norm of final solution. dErr = zeros(window) # Truncated direct error terms. trncDirErr = 0 # Truncated direct error. if store_iterates: self.iterates.append(x.copy()) # Transfer some pointers for readability eps = self.eps if show: print self.first + 'Solution of symmetric Ax = b' print 'n = %3d precon = %4s shift = %23.14e'\ % (n, (precon != None), shift) print 'itnlim = %3d rtol = %11.2e\n' % (itnlim, rtol) istop = 0 itn = 0 Anorm = 0.0 Acond = 0.0 rnorm = 0.0 ynorm = 0.0 done = False #------------------------------------------------------------------ # Set up y and v for the first Lanczos vector v1. # y = beta1 P' v1, where P = C**(-1). # v is really P' v1. #------------------------------------------------------------------ r1 = b if precon is not None: y = precon * b else: y = b.copy() beta1 = dot(b, y) # Test for an indefinite preconditioner. # If b = 0 exactly, stop with x = 0. if beta1 < 0: istop = 9 self.show = True done = True if beta1 == 0.0: self.show = True done = True if beta1 > 0: beta1 = sqrt(beta1) # Normalize y to get v1 later. self.residNorm0 = beta1 self.residNorm0 = beta1 # Initial residual norm. # See if A is symmetric. if check: if not check_symmetric(A): istop = 7 done = True self.show = True # See if preconditioner is symmetric. if check and (precon is not None): if not check_symmetric(precon): istop = 8 show = True done = True # ------------------------------------------------------------------- # Initialize other quantities. # ------------------------------------------------------------------ oldb = 0.0 beta = beta1 dbar = 0.0 epsln = 0.0 qrnorm = beta1 phibar = beta1 rhs1 = beta1 Arnorm = 0.0 rhs2 = 0.0 tnorm2 = 0.0 ynorm2 = 0.0 cs = -1.0 sn = 0.0 w = zeros(n, dtype=result_type) w2 = zeros(n, dtype=result_type) r2 = r1.copy() if show: print ' ' * 2 head1 = ' Itn x[0] Compatible LS' head2 = ' norm(A) cond(A) gbar/|A|' ###### Check gbar print head1 + head2 # --------------------------------------------------------------------- # Main iteration loop. # -------------------------------------------------------------------- if not done: # k = itn = 1 first time through while itn < itnlim: itn = itn + 1 # ------------------------------------------------------------- # Obtain quantities for the next Lanczos vector vk+1, k=1,2,... # The general iteration is similar to the case k=1 with v0 = 0: # # p1 = Operator * v1 - beta1 * v0, # alpha1 = v1'p1, # q2 = p2 - alpha1 * v1, # beta2^2 = q2'q2, # v2 = (1/beta2) q2. # # Again, y = betak P vk, where P = C**(-1). # .... more description needed. # ------------------------------------------------------------- s = 1.0 / beta # Normalize previous vector (in y). v = s * y # v = vk if P = I y = A * v y -= shift * v if itn >= 2: y = y - (beta / oldb) * r1 alfa = dot(v, y) y = (-alfa / beta) * r2 + y r1 = r2.copy() r2 = y.copy() if precon is not None: y = precon * r2 oldb = beta beta = dot(r2, y) if beta < 0: istop = 6 break beta = sqrt(beta) tnorm2 = tnorm2 + alfa**2 + oldb**2 + beta**2 if itn == 1: # Initialize a few things. if beta / beta1 <= 10 * eps: # beta2 = 0 or ~ 0. istop = -1 # Terminate later. # tnorm2 = alfa**2 ?? gmax = abs(alfa) # alpha1 gmin = gmax # alpha1 # Apply previous rotation Qk-1 to get # [deltak epslnk+1] = [cs sn][dbark 0 ] # [gbar k dbar k+1] [sn -cs][alfak betak+1]. oldeps = epsln delta = cs * dbar + sn * alfa # delta1 = 0 deltak gbar = sn * dbar - cs * alfa # gbar 1 = alfa1 gbar k # Note: There is severe cancellation in the computation of gbar #print ' sn = %21.15e\n dbar = %21.15e\n cs = %21.15e\n alfa = %21.15e\n sn*dbar-cs*alfa = %21.15e\n gbar =%21.15e' % (sn, dbar, cs, alfa, sn*dbar-cs*alfa, gbar) epsln = sn * beta # epsln2 = 0 epslnk+1 dbar = -cs * beta # dbar 2 = beta2 dbar k+1 root = self.normof2(gbar, dbar) Arnorm = phibar * root # Compute the next plane rotation Qk gamma = self.normof2(gbar, beta) # gammak gamma = max(gamma, eps) cs = gbar / gamma # ck sn = beta / gamma # sk phi = cs * phibar # phik phibar = sn * phibar # phibark+1 # Update x. denom = 1.0 / gamma w1 = w2.copy() w2 = w.copy() w = (v - oldeps * w1 - delta * w2) * denom x += phi * w #x = x + phi*w if store_iterates: self.iterates.append(x.copy()) # Update energy norm of x. xNrgNorm2 += phi * phi dErr[itn % window] = phi if itn > window: trncDirErr = norm(dErr) xNrgNorm = sqrt(xNrgNorm2) self.dir_errors_window.append(trncDirErr / xNrgNorm) if trncDirErr < etol * xNrgNorm: istop = 10 # Go round again. gmax = max(gmax, gamma) gmin = min(gmin, gamma) z = rhs1 / gamma ynorm2 = z**2 + ynorm2 rhs1 = rhs2 - delta * z rhs2 = -epsln * z # Estimate various norms and test for convergence. Anorm = sqrt(tnorm2) ynorm = sqrt(ynorm2) epsa = Anorm * eps epsx = Anorm * ynorm * eps epsr = Anorm * ynorm * rtol diag = gbar if diag == 0: diag = epsa qrnorm = phibar rnorm = qrnorm test1 = rnorm / (Anorm * ynorm) # ||r|| / (||A|| ||x||) test2 = root / Anorm # ||Ar|| / (||A|| ||r||) self.residHistory.append(rnorm) # Estimate cond(A). # In this version we look at the diagonals of R in the # factorization of the lower Hessenberg matrix, Q * H = R, # where H is the tridiagonal matrix from Lanczos with one # extra row, beta(k+1) e_k^T. Acond = gmax / gmin # See if any of the stopping criteria are satisfied. # In rare cases istop is already -1 from above (Abar = const*I) if istop == 0: t1 = 1 + test1 # These tests work if rtol < eps t2 = 1 + test2 if t2 <= 1: istop = 2 if t1 <= 1: istop = 1 if itn >= itnlim: istop = 6 if Acond >= 0.1 / eps: istop = 4 if epsx >= beta1: istop = 3 # if rnorm <= epsx: istop = 2 # if rnorm <= epsr: istop = 1 if test2 <= rtol: istop = 2 if test1 <= rtol: istop = 1 # See if it is time to print something. prnt = False if n <= 40: prnt = True if itn <= 10: prnt = True if itn >= itnlim - 10: prnt = True if (itn % 10) == 0: prnt = True if qrnorm <= 10 * epsx: prnt = True if qrnorm <= 10 * epsr: prnt = True if Acond <= 1e-2 / eps: prnt = True if istop != 0: prnt = True if show and prnt: str1 = '%6g %12.5e %10.3e' % (itn, x[0], test1) str2 = ' %10.3e' % test2 str3 = ' %8.1e %8.1e %8.1e' % (Anorm, Acond, gbar / Anorm) print str1 + str2 + str3 if istop > 0: break if (itn % 10) == 0: print ' ' # Display final status. if show: last = self.last print last + ' istop = %3g itn =%5g' % (istop, itn) print last + ' Anorm = %12.4e Acond = %12.4e' % (Anorm, Acond) print last + ' rnorm = %12.4e ynorm = %12.4e' % (rnorm, ynorm) print last + ' Arnorm = %12.4e' % Arnorm print last + self.msg[istop + 1] self.converged = istop in [1, 2, 3, 4, 10] if istop == 10: self.status = 'direct error small' self.bestSolution = self.x = x self.istop = istop self.itn = itn self.residNorm = self.rnorm = rnorm self.x = self.bestSolution = x self.istop = istop self.itn = self.nMatvec = itn self.rnorm = self.residNorm = rnorm self.Arnorm = Arnorm self.Anorm = Anorm self.Acond = Acond self.ynorm = ynorm return
def solve(self, rhs, **kwargs): """ Solve a linear system with `rhs` as right-hand side by the CG method. The vector `rhs` should be a Numpy array. :Keywords: :guess: Initial guess (Numpy array). Default: 0. :matvec_max: Max. number of operator-vector produts. Default: 2n. :check_symmetric: Ensure operator is symmetric. Default: False. :check_curvature: Ensure operator is positive definite. Default: True. :store_resids: Store full residual vector history. Default: False. :store_iterates: Store full iterate history. Default: False. """ n = rhs.shape[0] nMatvec = 0 definite = True check_sym = kwargs.get('check_symmetric', False) check_curvature = kwargs.get('check_curvature', True) store_resids = kwargs.get('store_resids', False) store_iterates = kwargs.get('store_iterates', False) if check_sym: if not check_symmetric(self.op): self.logger.error('Coefficient operator is not symmetric') return # Initial guess result_type = np.result_type(self.op.dtype, rhs.dtype) guess_supplied = 'guess' in kwargs.keys() x = kwargs.get('guess', np.zeros(n)).astype(result_type) if store_iterates: self.iterates.append(x.copy()) matvec_max = kwargs.get('matvec_max', 2 * n) # Initial residual vector r = -rhs if guess_supplied: r += self.op * x nMatvec += 1 # Initial preconditioned residual vector if self.precon is not None: y = self.precon * r else: y = r if store_resids: self.resids.append(y.copy()) ry = np.dot(r, y) self.residNorm0 = residNorm = np.abs(np.sqrt(ry)) self.residHistory.append(self.residNorm0) threshold = max(self.abstol, self.reltol * self.residNorm0) p = -r # Initial search direction (copy not to overwrite rhs if x=0) hdr_fmt = '%6s %7s %8s' hdr = hdr_fmt % ('Matvec', 'Resid', 'Curv') self.logger.info(hdr) self.logger.info('-' * len(hdr)) info = '%6d %7.1e' % (nMatvec, residNorm) self.logger.info(info) while residNorm > threshold and nMatvec < matvec_max and definite: Ap = self.op * p nMatvec += 1 pAp = np.dot(p, Ap) if check_curvature: if np.imag(pAp) > 1.0e-8 * np.abs(pAp) or np.real(pAp) <= 0: self.logger.error( 'Coefficient operator is not positive definite') self.infiniteDescent = p definite = False continue # Compute step length alpha = ry / pAp # Update estimate and residual x += alpha * p r += alpha * Ap if store_iterates: self.iterates.append(x.copy()) # Compute preconditioned residual if self.precon is not None: y = self.precon * r else: y = r if store_resids: self.resids.append(y.copy()) # Update preconditioned residual norm ry_next = np.dot(r, y) # Update search direction beta = ry_next / ry p *= beta p -= r ry = ry_next residNorm = np.abs(np.sqrt(ry)) self.residHistory.append(residNorm) info = '%6d %7.1e %8.1e' % (nMatvec, residNorm, np.real(pAp)) self.logger.info(info) self.converged = residNorm <= threshold self.definite = definite self.nMatvec = nMatvec self.bestSolution = self.x = x self.residNorm = residNorm