def arnoldi(self, mat, V, rlen, mmp, sfgmres, prec, idx, t): H = [] for i in range(rlen): if prec is not None: t("prec") Z = [sfgmres[j].Z[i] for j in idx] + [mmp] for z in Z: z[:] = 0 rhos = prec(Z, [V[i]]) for j, rho in zip(idx, rhos[0:-1]): sfgmres[j].gamma[i] = rho / rhos[-1] t("arnoldi") ips = np.zeros(i + 2, np.complex128) zv = mmp if prec is not None else V[i] mat(V[i + 1], zv) g.orthogonalize( V[i + 1], V[0:i + 1], ips[0:-1], nblock=10, ) ips[-1] = g.norm2(V[i + 1])**0.5 V[i + 1] /= ips[-1] H.append(ips) return H
def arnoldi(self, mat, V, rlen): H = [] for i in range(rlen): ips = np.zeros(i + 2, np.complex128) mat(V[i + 1], V[i]) g.orthogonalize( V[i + 1], V[0:i + 1], ips[0:-1], nblock=10, ) ips[-1] = g.norm2(V[i + 1])**0.5 V[i + 1] /= ips[-1] H.append(ips) return H
def __call__(self): t0 = g.time() new = g.lattice(self.basis[-1]) self.mat(new, self.basis[-1]) t1 = g.time() ips = np.zeros((len(self.basis) + 1, ), np.complex128) g.orthogonalize(new, self.basis, ips[0:-1]) ips[-1] = g.norm2(new)**0.5 new /= ips[-1] self.basis.append(new) self.H.append(ips) t2 = g.time() if self.verbose: g.message( f"Arnoldi: len(H) = {len(self.H)} took {t1-t0} s for matrix and {t2-t1} s for linear algebra" )
def __call__(self, second_orthogonalization=True): t0 = g.time() new = g.lattice(self.basis[-1]) self.mat(new, self.basis[-1]) t1 = g.time() ips = np.zeros((len(self.basis) + 1, ), np.complex128) g.orthogonalize(new, self.basis, ips[0:-1]) if second_orthogonalization: delta_ips = np.zeros((len(self.basis) + 1, ), np.complex128) g.orthogonalize(new, self.basis, delta_ips[0:-1]) # the following line may be omitted ips += delta_ips ips[-1] = g.norm2(new)**0.5 new /= ips[-1] self.basis.append(new) self.H.append(ips) t2 = g.time() if self.verbose: g.message( f"Arnoldi: len(H) = {len(self.H)} took {t1-t0} s for matrix and {t2-t1} s for linear algebra" )
def __call__(self, mat, src, psi, prec=None): # verbosity self.verbose = g.default.is_verbose("fgmres") checkres = True # for now # total time tt0 = time() # parameters rlen = self.restartlen # tensors dtype = np.complex128 H = np.zeros((rlen + 1, rlen), dtype) c = np.zeros((rlen + 1), dtype) s = np.zeros((rlen + 1), dtype) y = np.zeros((rlen + 1), dtype) gamma = np.zeros((rlen + 1), dtype) # fields mmpsi, r = g.copy(src), g.copy(src), V = [g.lattice(src) for i in range(rlen + 1)] if not prec is None: # save vectors if unpreconditioned Z = [g.lattice(src) for i in range(rlen + 1)] # residual ssq = g.norm2(src) rsq = self.eps**2. * ssq # initial values r2 = self.restart(mat, psi, mmpsi, src, r, V, gamma) for k in range(self.maxiter): # iteration within current krylov space i = k % rlen # iteration criteria reached_maxiter = (k + 1 == self.maxiter) need_restart = (i + 1 == rlen) t0 = time() if not prec is None: prec(V[i], Z[i]) t1 = time() t2 = time() if not prec is None: mat(Z[i], V[i + 1]) else: mat(V[i], V[i + 1]) t3 = time() t4 = time() g.orthogonalize(V[i + 1], V[0:i + 1], H[:, i]) t5 = time() t6 = time() H[i + 1, i] = g.norm2(V[i + 1])**0.5 if H[i + 1, i] == 0.: g.message("fgmres breakdown, H[%d, %d] = 0" % (i + 1, i)) break V[i + 1] /= H[i + 1, i] t7 = time() t8 = time() self.qr_update(s, c, H, gamma, i) r2 = np.absolute(gamma[i + 1])**2 t9 = time() if self.verbose: g.message( "Timing[s]: Prec = %g, Matrix = %g, Orthog = %g, RestArnoldi = %g, QR = %g" % (t1 - t0, t3 - t2, t5 - t4, t7 - t6, t9 - t8)) g.message("res^2[ %d, %d ] = %g" % (k, i, r2)) if r2 <= rsq or need_restart or reached_maxiter: if not prec is None: self.update_psi(psi, gamma, H, y, Z, i) else: self.update_psi(psi, gamma, H, y, V, i) if r2 <= rsq: if self.verbose: tt1 = time() g.message("Converged in %g s" % (tt1 - tt0)) if checkres: res = self.calc_res(mat, psi, mmpsi, src, r) / ssq g.message( "Computed res = %g, true res = %g, target = %g" % (r2**0.5, res**0.5, self.eps)) break if reached_maxiter: if verbose: tt1 = time() g.message("Did NOT converge in %g s" % (tt1 - tt0)) if checkres: res = self.calc_res(mat, psi, mmpsi, src, r) / ssq g.message( "Computed res = %g, true res = %g, target = %g" % (r2**0.5, res**0.5, self.eps)) if need_restart: r2 = self.restart(mat, psi, mmpsi, src, r, V, gamma) if self.verbose: g.message("Performed restart")
def __call__(self, which_lvls=None): if which_lvls is not None: assert type(which_lvls) == list for elem in which_lvls: assert elem >= self.finest and elem <= self.coarsest else: which_lvls = self.lvl for lvl in which_lvls: if lvl == self.coarsest: continue # aliases t = self.t[lvl] pp = self.print_prefix[lvl] # start clocks t("misc") # neighbors nc_lvl = self.nc_lvl[lvl] # aliases basis = self.basis[lvl] blockmap = self.blockmap[lvl] nb = self.nb[lvl] vector_type = self.vector_type[lvl] # pre-orthonormalize basis vectors globally t("pre_ortho") g.default.push_verbose("orthogonalize", False) for n in range(self.npreortho[lvl]): if self.verbose: g.message("%s pre ortho step %d" % (pp, n)) for i, v in enumerate(basis[0:nb]): v /= g.norm2(v)**0.5 g.orthogonalize(v, basis[:i]) g.default.pop_verbose() if self.verbose: g.message("%s done pre-orthonormalizing basis vectors" % pp) # find near-null vectors t("find_null_vecs") src, psi = g.copy(basis[0]), g.copy(basis[0]) for i, v in enumerate(basis[0:nb]): if vector_type == "test": psi[:] = 0.0 src @= v elif vector_type == "null": src[:] = 0.0 psi @= v else: assert 0 g.default.push_verbose(get_slv_name(self.solver[lvl]), False) self.solver[lvl](self.mat[lvl])(psi, src) g.default.pop_verbose() self.history[lvl].append(get_slv_history(self.solver[lvl])) v @= psi if self.verbose: g.message("%s done finding null-space vectors" % pp) # post-orthonormalize basis vectors globally t("post_ortho") g.default.push_verbose("orthogonalize", False) for n in range(self.npostortho[lvl]): if self.verbose: g.message("%s post ortho step %d" % (pp, n)) for i, v in enumerate(basis[0:nb]): v /= g.norm2(v)**0.5 g.orthogonalize(v, basis[:i]) g.default.pop_verbose() if self.verbose: g.message("%s done post-orthonormalizing basis vectors" % pp) # chiral doubling t("chiral_split") g.coarse.split_chiral(basis) if self.verbose: g.message("%s done doing chiral doubling" % pp) # orthonormalize blocks t("block_ortho") for i in range(self.nblockortho[lvl]): if self.verbose: g.message("%s block ortho step %d" % (pp, i)) blockmap.orthonormalize() if self.check_blockortho: t("block_ortho_check") blockmap.check_orthogonality() if self.verbose: g.message("%s done block-orthonormalizing" % pp) # create coarse links + operator t("create_operator") g.coarse.create_links( self.A[nc_lvl], self.mat[lvl], self.basis[lvl], { "make_hermitian": self.make_hermitian[lvl], "save_links": self.save_links[lvl], }, ) self.mat[nc_lvl] = g.qcd.fermion.coarse(self.A[nc_lvl], {"level": nc_lvl}) if self.verbose: g.message("%s done setting up next coarser operator" % pp) t() if self.verbose: g.message("%s done with entire setup" % pp)
#!/usr/bin/env python3 # # Authors: Christoph Lehner 2020 # # Desc.: Illustrate core concepts and features # import gpt as g import numpy as np import sys # load configuration fine_grid = g.grid([8, 8, 8, 16], g.single) # basis n = 31 basis = [g.vcomplex(fine_grid, 30) for i in range(n)] rng = g.random("block_seed_string_13") rng.cnormal(basis) # gram-schmidt for i in range(n): basis[i] /= g.norm2(basis[i]) ** 0.5 g.orthogonalize(basis[i], basis[:i]) for j in range(i): eps = g.inner_product(basis[j], basis[i]) g.message(" <%d|%d> =" % (j, i), eps) assert abs(eps) < 1e-6
def step(self, mat, lmd, lme, evec, w, Nm, k): assert k < Nm verbose = g.default.is_verbose("irl") ckpt = self.ckpt alph = 0.0 beta = 0.0 evec_k = evec[k] results = [w, alph, beta] if ckpt.load(results): w, alph, beta = results # use checkpoint if verbose: g.message("%-65s %-45s" % ("alpha[ %d ] = %s" % (k, alph), "beta[ %d ] = %s" % (k, beta))) else: if self.params["mem_report"]: g.mem_report(details=False) # compute t0 = g.time() mat(w, evec_k) t1 = g.time() # allow to restrict maximal number of applications within run self.napply += 1 if "maxapply" in self.params: if self.napply == self.params["maxapply"]: if verbose: g.message( "Maximal number of matrix applications reached") sys.exit(0) if k > 0: w -= lme[k - 1] * evec[k - 1] zalph = g.inner_product(evec_k, w) alph = zalph.real w -= alph * evec_k beta = g.norm2(w)**0.5 w /= beta t2 = g.time() if k > 0: g.orthogonalize(w, evec[0:k]) t3 = g.time() ckpt.save([w, alph, beta]) if verbose: g.message("%-65s %-45s %-50s" % ( "alpha[ %d ] = %s" % (k, zalph), "beta[ %d ] = %s" % (k, beta), " timing: %g s (matrix), %g s (ortho)" % (t1 - t0, t3 - t2), )) lmd[k] = alph lme[k] = beta if k < Nm - 1: evec[k + 1] @= w
def __call__(self, mat, src, ckpt=None): # verbosity verbose = g.default.is_verbose("irl") # checkpointer if ckpt is None: ckpt = g.checkpointer_none() ckpt.grid = src.grid self.ckpt = ckpt # first approximate largest eigenvalue pit = g.algorithms.eigen.power_iteration(eps=0.05, maxiter=10, real=True) lambda_max = pit(mat, src)[0] # parameters Nm = self.params["Nm"] Nu = self.params["Nu"] Nk = self.params["Nk"] Nstop = self.params["Nstop"] Np = Nm-Nk MaxIter=self.params["maxiter"] Np /= MaxIter assert Nm >= Nk and Nstop <= Nk print ( 'Nm=',Nm,'Nu=',Nu,'Nk=',Nk ) # tensors dtype = np.float64 ctype = np.complex128 lme = np.zeros((Nu,Nm), ctype) lmd = np.zeros((Nu,Nm), ctype) lme2 = np.zeros((Nu,Nm), ctype) lmd2 = np.empty((Nu,Nm), ctype) Qt = np.zeros((Nm,Nm),ctype) Q = np.zeros((Nm,Nm),ctype) ev = np.empty((Nm,), dtype) ev2_copy = np.empty((Nm,), dtype) # fields f = g.lattice(src) v = g.lattice(src) evec = [g.lattice(src) for i in range(Nm)] w = [g.lattice(src) for i in range(Nu)] w_copy = [g.lattice(src) for i in range(Nu)] # advice memory storage if not self.params["advise"] is None: g.advise(evec, self.params["advise"]) # scalars k1 = 1 k2 = Nk beta_k = 0.0 rng=g.random("test") # set initial vector # rng.zn(w) for i in range(Nu): rng.zn(w[i]) if i > 0: g.orthogonalize(w[i],evec[0:i]) evec[i]=g.copy(w[i]) evec[i] *= 1.0/ g.norm2(evec[i]) ** 0.5 g.message("norm(evec[%d]=%e "%(i,g.norm2(evec[i]))) if i > 0: for j in range(i): ip=g.innerProduct(evec[j],w[i]) if np.abs(ip) >1e-6: g.message("inner(evec[%d],w[%d])=%e %e"% (j,i,ip.real,ip.imag)) # evec[i] @= src[i] / g.norm2(src[i]) ** 0.5 # initial Nk steps Nblock_k = int(Nk/Nu) for b in range(Nblock_k): self.blockStep(mat, lmd, lme, evec, w, w_copy, Nm, b,Nu) Nblock_p = int(Np/Nu) # restarting loop # for it in range(self.params["maxiter"]): for it in range(MaxIter): if verbose: g.message("Restart iteration %d" % it) Nblock_l = Nblock_k + it*Nblock_p; Nblock_r = Nblock_l + Nblock_p; Nl = Nblock_l*Nu Nr = Nblock_r*Nu # ev2.resize(Nr) ev2 = np.empty((Nr,), dtype) for b in range(Nblock_l, Nblock_r): self.blockStep(mat, lmd, lme, evec, w, w_copy, Nm, b,Nu) for u in range(Nu): for k in range(Nr): lmd2[u,k]=lmd[u,k] lme2[u,k]=lme[u,k] Qt = np.identity(Nr, ctype) # diagonalize t0 = g.time() # self.diagonalize(ev2, lme2, Nm, Qt) self.diagonalize(ev2,lmd2,lme2,Nu,Nr,Qt) # def diagonalize(self, eval, lmd, lme, Nu, Nk, Nm, Qt): t1 = g.time() if verbose: g.message("Diagonalization took %g s" % (t1 - t0)) # sort ev2_copy = ev2.copy() ev2 = list(reversed(sorted(ev2))) for i in range(Nr): g.message("Rval[%d]= %e"%(i,ev2[i])) # rotate # t0 = g.time() # g.rotate(evec, Qt, k1 - 1, k2 + 1, 0, Nm) # t1 = g.time() # if verbose: # g.message("Basis rotation took %g s" % (t1 - t0)) # convergence test if it >= self.params["Nminres"]: if verbose: g.message("Rotation to test convergence") # diagonalize for k in range(Nr): ev2[k] = ev[k] # lme2[k] = lme[k] for u in range(Nu): for k in range(Nr): lmd2[u,k]=lmd[u,k] lme2[u,k]=lme[u,k] Qt = np.identity(Nm, ctype) t0 = g.time() # self.diagonalize(ev2, lme2, Nk, Qt) self.diagonalize(ev2,lmd2,lme2,Nu,Nr,Qt) t1 = g.time() if verbose: g.message("Diagonalization took %g s" % (t1 - t0)) B = g.copy(evec[0]) allconv = True if beta_k >= self.params["betastp"]: jj = 1 while jj <= Nstop: j = Nstop - jj g.linear_combination(B, evec[0:Nr], Qt[j, 0:Nr]) g.message("norm=%e"%(g.norm2(B))) B *= 1.0 / g.norm2(B) ** 0.5 if not ckpt.load(v): mat(v, B) ckpt.save(v) ev_test = g.innerProduct(B, v).real eps2 = g.norm2(v - ev_test * B) / lambda_max ** 2.0 if verbose: g.message( "%-65s %-45s %-50s" % ( "ev[ %d ] = %s" % (j, ev2_copy[j]), "<B|M|B> = %s" % (ev_test), "|M B - ev B|^2 / ev_max^2 = %s" % (eps2), ) ) if eps2 > self.params["resid"]: allconv = False if jj == Nstop: break jj = min([Nstop, 2 * jj]) if allconv: if verbose: g.message("Converged in %d iterations" % it) break t0 = g.time() g.rotate(evec, Qt, 0, Nstop, 0, Nk) t1 = g.time() if verbose: g.message("Final basis rotation took %g s" % (t1 - t0)) return (evec[0:Nstop], ev2_copy[0:Nstop])
def blockStep(self, mat, lmd, lme, evec, w, w_copy, Nm, b, Nu): assert (b+1)*Nu <= Nm verbose = g.default.is_verbose("irl") ckpt = self.ckpt alph = 0.0 beta = 0.0 L= b*Nu R= (b+1)*Nu for k in range (L,R): if self.params["mem_report"]: g.mem_report(details=False) # compute t0 = g.time() if not ckpt.load(w[k-L]): mat(w[k-L], evec[k]) # mat(v, B) ckpt.save(w[k-L]) t1 = g.time() # allow to restrict maximal number of applications within run self.napply += 1 if "maxapply" in self.params: if self.napply == self.params["maxapply"]: if verbose: g.message("Maximal number of matrix applications reached") sys.exit(0) for u in range (Nu): for k in range (u,Nu): ip=g.innerProduct(evec[L+k],evec[L+u]) if np.abs(ip) >1e-6: g.message("inner(evec[%d],evec[%d])=%e %e"% (L+k,L+u,ip.real,ip.imag)) if b > 0: for u in range (Nu): for k in range (L-Nu+u,L): w[u] -= np.conjugate(lme[u,k]) * evec[k] for k in range (L-Nu+u,L): ip=g.innerProduct(evec[k],w[u]) # if g.norm2(ip)>1e-6: if np.abs(ip) >1e-6: g.message("inner(evec[%d],w[%d])=%e %e"% (k,u,ip.real,ip.imag)) else: for u in range (Nu): g.message("norm(evec[%d])=%e"%(u,g.norm2(evec[u]))) for u in range (Nu): for k in range (L+u,R): lmd[u][k] = g.innerProduct(evec[k],w[u]) lmd[k-L][L+u]=np.conjugate(lmd[u][k]) lmd[u][L+u]=np.real(lmd[u][L+u]) for u in range (Nu): for k in range (L,R): w[u] -= lmd[u][k]*evec[k] for k in range (L,R): ip=g.innerProduct(evec[k],w[u]) if np.abs(ip) >1e-6: g.message("inner(evec[%d],w[%d])=%e %e"% (k,u,ip.real,ip.imag)) w_copy[u] = g.copy(w[u]); for u in range (Nu): for k in range (L,R): lme[u][k]=0.; for u in range (Nu): g.orthogonalize(w[u],evec[0:R]) w[u] *= 1.0 / g.norm2(w[u]) ** 0.5 for k in range (R): ip=g.innerProduct(evec[k],w[u]) if np.abs(ip) >1e-6: g.message("inner(evec[%d],w[%d])=%e %e"% (k,u,ip.real,ip.imag)) for u in range (Nu): g.orthogonalize(w[u],evec[0:R]) w[u] *= 1.0 / g.norm2(w[u]) ** 0.5 for k in range (R): ip=g.innerProduct(evec[k],w[u]) if np.abs(ip) >1e-6: g.message("inner(evec[%d],w[%d])=%e %e"% (k,u,ip.real,ip.imag)) for u in range (0,Nu): if u >0: g.orthogonalize(w[u],w[0:u]) w[u] *= 1.0 / g.norm2(w[u]) ** 0.5 for k in range (u): ip=g.innerProduct(w[k],w[u]) if np.abs(ip) >1e-6: g.message("inner(w[%d],w[%d])=%e %e"% (k,u,ip.real,ip.imag)) ip=g.innerProduct(w[u],w[u]) g.message("inner(w[%d],w[%d])=%e %e"% (u,u,ip.real,ip.imag)) for u in range (Nu): for v in range (u,Nu): lme[u][L+v] = g.innerProduct(w[u],w_copy[v]) lme[u][L+u] = np.real(lme[u][L+u]) t3 = g.time() for u in range (Nu): for k in range (L+u,R): g.message( " In block %d, beta[%d][%d]=%e %e" %( b, u, k-b*Nu,lme[u][k].real,lme[u][k].imag ) ) # ckpt.save([w, alph, beta]) if b < (Nm/Nu - 1): for u in range (Nu): evec[R+u] = g.copy(w[u]) ip=g.innerProduct(evec[R+u],evec[R+u]) if np.abs(ip) >1e-6: g.message("inner(evec[%d],evec[%d])=%e %e"% (R+u,R+u,ip.real,ip.imag))
def inv(psi, src, t): t("setup") # parameters rlen = self.restartlen # tensors dtype_r, dtype_c = g.double.real_dtype, g.double.complex_dtype alpha = np.empty((rlen), dtype_c) beta = np.empty((rlen, rlen), dtype_c) gamma = np.empty((rlen), dtype_r) chi = np.empty((rlen), dtype_c) # fields r, mmpsi = g.copy(src), g.copy(src) p = [g.lattice(src) for i in range(rlen)] z = [g.lattice(src) for i in range(rlen)] # initial residual r2 = self.restart(mat, psi, mmpsi, src, r, p) # source ssq = g.norm2(src) if ssq == 0.0: assert r2 != 0.0 # need either source or psi to not be zero ssq = r2 # target residual rsq = self.eps ** 2.0 * ssq for k in range(self.maxiter): # iteration within current krylov space i = k % rlen # iteration criteria need_restart = i + 1 == rlen t("prec") if prec is not None: prec(p[i], r) else: p[i] @= r t("mat") mat(z[i], p[i]) t("ortho") g.orthogonalize(z[i], z[0:i], beta[:, i]) t("linalg") ip, z2 = g.inner_product_norm2(z[i], r) gamma[i] = z2 ** 0.5 if gamma[i] == 0.0: self.debug(f"breakdown, gamma[{i:d}] = 0") break z[i] /= gamma[i] alpha[i] = ip / gamma[i] r2 = g.axpy_norm2(r, -alpha[i], z[i], r) t("other") self.log_convergence((k, i), r2, rsq) if r2 <= rsq or need_restart: t("update_psi") self.update_psi(psi, alpha, beta, gamma, chi, p, i) if r2 <= rsq: msg = f"converged in {k+1} iterations; computed squared residual {r2:e} / {rsq:e}" if self.checkres: res = self.calc_res(mat, psi, mmpsi, src, r) msg += f"; true squared residual {res:e} / {rsq:e}" self.log(msg) return if need_restart: t("restart") r2 = self.restart(mat, psi, mmpsi, src, r, p) self.debug("performed restart") msg = f"NOT converged in {k+1} iterations; computed squared residual {r2:e} / {rsq:e}" if self.checkres: res = self.calc_res(mat, psi, mmpsi, src, r) msg += f"; true squared residual {res:e} / {rsq:e}" self.log(msg)
def inv(psi, src, t): # timing t("setup") # parameters rlen = self.restartlen # tensors dtype = g.double.complex_dtype H = np.zeros((rlen + 1, rlen), dtype) c = np.zeros((rlen + 1), dtype) s = np.zeros((rlen + 1), dtype) y = np.zeros((rlen + 1), dtype) gamma = np.zeros((rlen + 1), dtype) # fields mmpsi, r = ( g.copy(src), g.copy(src), ) V = [g.lattice(src) for i in range(rlen + 1)] Z = ( [g.lattice(src) for i in range(rlen + 1)] if prec is not None else None ) # save vectors if unpreconditioned ZV = Z if prec is not None else V # initial residual t("restart") r2 = self.restart(mat, psi, mmpsi, src, r, V, Z, gamma, t) t("setup") # source ssq = g.norm2(src) if ssq == 0.0: assert r2 != 0.0 # need either source or psi to not be zero ssq = r2 # target residual rsq = self.eps ** 2.0 * ssq for k in range(self.maxiter): # iteration within current krylov space i = k % rlen # iteration criteria need_restart = i + 1 == rlen t("prec") if prec is not None: prec(ZV[i], V[i]) t("mat") mat(V[i + 1], ZV[i]) t("ortho") g.orthogonalize(V[i + 1], V[0 : i + 1], H[:, i], nblock=10) t("linalg norm2") H[i + 1, i] = g.norm2(V[i + 1]) ** 0.5 if H[i + 1, i] == 0.0: self.debug(f"breakdown, H[{i+1:d}, {i:d}] = 0") break t("linalg div") V[i + 1] /= H[i + 1, i] t("qr") self.qr_update(s, c, H, gamma, i) t("other") r2 = np.absolute(gamma[i + 1]) ** 2 self.log_convergence((k, i), r2, rsq) if r2 <= rsq or need_restart: t("update_psi") self.update_psi(psi, gamma, H, y, ZV, i) if r2 <= rsq: msg = f"converged in {k+1} iterations; computed squared residual {r2:e} / {rsq:e}" if self.checkres: res = self.calc_res(mat, psi, mmpsi, src, r, t) msg += f"; true squared residual {res:e} / {rsq:e}" self.log(msg) return if need_restart: t("restart") r2 = self.restart(mat, psi, mmpsi, src, r, V, Z, gamma, t) self.debug("performed restart") msg = f"NOT converged in {k+1} iterations; computed squared residual {r2:e} / {rsq:e}" if self.checkres: res = self.calc_res(mat, psi, mmpsi, src, r, t) msg += f"; true squared residual {res:e} / {rsq:e}" self.log(msg)
def inv(psi, src): self.history = [] # verbosity verbose = g.default.is_verbose("fgcr") # timing t = g.timer("fgcr") t("setup") # parameters rlen = self.restartlen # tensors dtype_r, dtype_c = g.double.real_dtype, g.double.complex_dtype alpha = np.empty((rlen), dtype_c) beta = np.empty((rlen, rlen), dtype_c) gamma = np.empty((rlen), dtype_r) chi = np.empty((rlen), dtype_c) # fields r, mmpsi = g.copy(src), g.copy(src) p = [g.lattice(src) for i in range(rlen)] z = [g.lattice(src) for i in range(rlen)] # initial residual r2 = self.restart(mat, psi, mmpsi, src, r, p) # source ssq = g.norm2(src) if ssq == 0.0: assert r2 != 0.0 # need either source or psi to not be zero ssq = r2 # target residual rsq = self.eps**2.0 * ssq for k in range(self.maxiter): # iteration within current krylov space i = k % rlen # iteration criteria reached_maxiter = k + 1 == self.maxiter need_restart = i + 1 == rlen t("prec") if prec is not None: prec(p[i], r) else: p[i] @= r t("mat") mat(z[i], p[i]) t("ortho") g.default.push_verbose("orthogonalize", False) g.orthogonalize(z[i], z[0:i], beta[:, i]) g.default.pop_verbose() t("linalg") ip, z2 = g.inner_product_norm2(z[i], r) gamma[i] = z2**0.5 if gamma[i] == 0.0: g.message("fgcr: breakdown, gamma[%d] = 0" % (i)) break z[i] /= gamma[i] alpha[i] = ip / gamma[i] r2 = g.axpy_norm2(r, -alpha[i], z[i], r) t("other") self.history.append(r2) if verbose: g.message("fgcr: res^2[ %d, %d ] = %g, target = %g" % (k, i, r2, rsq)) if r2 <= rsq or need_restart or reached_maxiter: t("update_psi") self.update_psi(psi, alpha, beta, gamma, chi, p, i) comp_res = r2 / ssq if r2 <= rsq: if verbose: t() g.message( "fgcr: converged in %d iterations, took %g s" % (k + 1, t.total)) g.message(t) if self.checkres: res = self.calc_res(mat, psi, mmpsi, src, r) / ssq g.message( "fgcr: computed res = %g, true res = %g, target = %g" % (comp_res**0.5, res**0.5, self.eps)) else: g.message( "fgcr: computed res = %g, target = %g" % (comp_res**0.5, self.eps)) break if reached_maxiter: if verbose: t() g.message( "fgcr: did NOT converge in %d iterations, took %g s" % (k + 1, t.dt["total"])) g.message(t) if self.checkres: res = self.calc_res(mat, psi, mmpsi, src, r) / ssq g.message( "fgcr: computed res = %g, true res = %g, target = %g" % (comp_res**0.5, res**0.5, self.eps)) else: g.message( "fgcr: computed res = %g, target = %g" % (comp_res**0.5, self.eps)) break if need_restart: t("restart") r2 = self.restart(mat, psi, mmpsi, src, r, p) if verbose: g.message("fgcr: performed restart")
def orthonormalize(basis, nblock=4): for i, v in enumerate(basis): gpt.orthogonalize(v, basis[:i], nblock=nblock) v /= gpt.norm2(v) ** 0.5 return basis
def inv(psi, src): self.history = [] # verbosity verbose = g.default.is_verbose("fgmres") # timing t = g.timer("fgmres") t("setup") # parameters rlen = self.restartlen # tensors dtype = g.double.complex_dtype H = np.zeros((rlen + 1, rlen), dtype) c = np.zeros((rlen + 1), dtype) s = np.zeros((rlen + 1), dtype) y = np.zeros((rlen + 1), dtype) gamma = np.zeros((rlen + 1), dtype) # fields mmpsi, r = ( g.copy(src), g.copy(src), ) V = [g.lattice(src) for i in range(rlen + 1)] Z = ( [g.lattice(src) for i in range(rlen + 1)] if prec is not None else None ) # save vectors if unpreconditioned # initial residual r2 = self.restart(mat, psi, mmpsi, src, r, V, Z, gamma) # source ssq = g.norm2(src) if ssq == 0.0: assert r2 != 0.0 # need either source or psi to not be zero ssq = r2 # target residual rsq = self.eps ** 2.0 * ssq for k in range(self.maxiter): # iteration within current krylov space i = k % rlen # iteration criteria reached_maxiter = k + 1 == self.maxiter need_restart = i + 1 == rlen t("prec") if prec is not None: prec(Z[i], V[i]) t("mat") if prec is not None: mat(V[i + 1], Z[i]) else: mat(V[i + 1], V[i]) t("ortho") g.default.push_verbose("orthogonalize", False) g.orthogonalize(V[i + 1], V[0 : i + 1], H[:, i]) g.default.pop_verbose() t("linalg") H[i + 1, i] = g.norm2(V[i + 1]) ** 0.5 if H[i + 1, i] == 0.0: g.message("fgmres: breakdown, H[%d, %d] = 0" % (i + 1, i)) break V[i + 1] /= H[i + 1, i] t("qr") self.qr_update(s, c, H, gamma, i) t("other") r2 = np.absolute(gamma[i + 1]) ** 2 self.history.append(r2) if verbose: g.message( "fgmres: res^2[ %d, %d ] = %g, target = %g" % (k, i, r2, rsq) ) if r2 <= rsq or need_restart or reached_maxiter: t("update_psi") if prec is not None: self.update_psi(psi, gamma, H, y, Z, i) else: self.update_psi(psi, gamma, H, y, V, i) comp_res = r2 / ssq if r2 <= rsq: if verbose: t() g.message( "fgmres: converged in %d iterations, took %g s" % (k + 1, t.dt["total"]) ) g.message(t) if self.checkres: res = self.calc_res(mat, psi, mmpsi, src, r) / ssq g.message( "fgmres: computed res = %g, true res = %g, target = %g" % (comp_res ** 0.5, res ** 0.5, self.eps) ) else: g.message( "fgmres: computed res = %g, target = %g" % (comp_res ** 0.5, self.eps) ) break if reached_maxiter: if verbose: t() g.message( "fgmres: did NOT converge in %d iterations, took %g s" % (k + 1, t.dt["total"]) ) g.message(t) if self.checkres: res = self.calc_res(mat, psi, mmpsi, src, r) / ssq g.message( "fgmres: computed res = %g, true res = %g, target = %g" % (comp_res ** 0.5, res ** 0.5, self.eps) ) else: g.message( "fgmres: computed res = %g, target = %g" % (comp_res ** 0.5, self.eps) ) break if need_restart: t("restart") r2 = self.restart(mat, psi, mmpsi, src, r, V, Z, gamma) if verbose: g.message("fgmres: performed restart")
def __call__(self, mat, src, psi, prec=None): # verbosity verbose = g.default.is_verbose("fgcr") checkres = True # for now # total time tt0 = time() # parameters rlen = self.restartlen # tensors dtype_r, dtype_c = np.float64, np.complex128 alpha = np.empty((rlen), dtype_c) beta = np.empty((rlen, rlen), dtype_c) gamma = np.empty((rlen), dtype_r) delta = np.empty((rlen), dtype_c) # fields r, mmr, mmpsi = g.copy(src), g.copy(src), g.copy(src) p = [g.lattice(src) for i in range(rlen)] mmp = [g.lattice(src) for i in range(rlen)] # residual target ssq = g.norm2(src) rsq = self.eps**2. * ssq # initial values r2 = self.restart(mat, psi, mmpsi, src, r) for k in range(self.maxiter): # iteration within current krylov space i = k % rlen # iteration criteria reached_maxiter = k + 1 == self.maxiter need_restart = i + 1 == rlen t0 = time() if not prec is None: prec(r, p[i]) else: p[i] @= r t1 = time() t2 = time() mat(p[i], mmp[i]) t3 = time() t4 = time() g.orthogonalize(mmp[i], mmp[0:i], beta[:, i]) t5 = time() t6 = time() ip, mmp2 = g.innerProductNorm2(mmp[i], r) gamma[i] = mmp2**0.5 if gamma[i] == 0.: g.message("fgcr breakdown, gamma[%d] = 0" % (i)) break mmp[i] /= gamma[i] alpha[i] = ip / gamma[i] r2 = g.axpy_norm2(r, -alpha[i], mmp[i], r) t7 = time() if verbose: g.message( "Timing[s]: Prec = %g, Matrix = %g, Orthog = %g, Rest = %g" % (t1 - t0, t3 - t2, t5 - t4, t7 - t6)) g.message("res^2[ %d, %d ] = %g" % (k, i, r2)) if r2 <= rsq or need_restart or reached_maxiter: self.update_psi(psi, alpha, beta, gamma, delta, p, i) if r2 <= rsq: if verbose: tt1 = time() g.message("Converged in %g s" % (tt1 - tt0)) if checkres: res = self.calc_res(mat, psi, mmpsi, src, r) / ssq g.message( "Computed res = %g, true res = %g, target = %g" % (r2**0.5, res**0.5, self.eps)) break if reached_maxiter: if verbose: tt1 = time() g.message("Did NOT converge in %g s" % (tt1 - tt0)) if checkres: res = self.calc_res(mat, psi, mmpsi, src, r) / ssq g.message( "Computed res = %g, true res = %g, target = %g" % (r2**0.5, res**0.5, self.eps)) if need_restart: r2 = self.restart(mat, psi, mmpsi, src, r) if verbose: g.message("Performed restart")