def is_element(self, U): I = gpt.identity(U) I_s = gpt.identity(gpt.complex(U.grid)) err2 = gpt.norm2(U * gpt.adj(U) - I) / gpt.norm2(I) err2 += gpt.norm2(gpt.matrix.det(U) - I_s) / gpt.norm2(I_s) # consider additional determinant check return err2**0.5 < U.grid.precision.eps * 10.0
def __call__(self, mat, src, t): dst, tmp = g.lattice(src), g.copy(src) tmp /= g.norm2(tmp) ** 0.5 ev_prev = None for it in range(self.maxit): t("matrix") mat(dst, tmp) t("inner_product") ev = g.inner_product(tmp, dst) t("other") if self.real: ev = ev.real self.log_convergence(it, ev) t("normalize") tmp @= dst / g.norm2(dst) ** 0.5 t("other") if ev_prev is not None: if abs(ev - ev_prev) < self.tol * abs(ev): self.log(f"converged in iteration {it}") return (ev, tmp, True) ev_prev = ev return (ev, tmp, False)
def check_unitarity(U, eps_ref): eye = g.lattice(U) eye[:] = np.eye(U.otype.shape[0], dtype=U.grid.precision.complex_dtype) eps = (g.norm2(U * g.adj(U) - eye) / g.norm2(eye))**0.5 g.message(f"Test unitarity: {eps}") assert eps < eps_ref U.otype.is_element(U)
def log(i, convergence_threshold=0.5): # i = n*(1 + x), log(i) = log(n) + log(1+x) # x = i/n - 1, |x|^2 = <i/n - 1, i/n - 1> = |i|^2/n^2 + |1|^2 - (<i,1> + <1,i>)/n # d/dn |x|^2 = -2 |i|^2/n^3 + (<i,1> + <1,i>)/n^2 = 0 -> 2|i|^2 == n (<i,1> + <1,i>) if i.grid.precision != gpt.double: x = gpt.convert(i, gpt.double) else: x = gpt.copy(i) I = numpy.identity(x.otype.shape[0], x.grid.precision.complex_dtype) lI = gpt.lattice(x) lI[:] = I n = gpt.norm2(x) / gpt.inner_product(x, lI).real x /= n x -= lI n2 = gpt.norm2(x)**0.5 / x.grid.gsites order = 8 * int(16 / (-numpy.log10(n2))) assert n2 < convergence_threshold o = gpt.copy(x) xn = gpt.copy(x) for j in range(2, order + 1): xn @= xn * x o -= xn * ((-1.0)**j / j) o += lI * numpy.log(n) if i.grid.precision != gpt.double: r = gpt.lattice(i) gpt.convert(r, o) o = r return o
def inv(dst, src): for i in range(len(dst)): eps = g.norm2(mat * dst[i] - src[i]) ** 0.5 nrm = g.norm2(src[i]) ** 0.5 g.message( f"{self.tag}| mat * dst[{i}] - src[{i}] | / | src | = {eps/nrm}, | src[{i}] | = {nrm}" )
def __call__(self, mat, src, psi): assert (src != psi) self.history = [] verbose = g.default.is_verbose("cg") t0 = g.time() p, mmp, r = g.copy(src), g.copy(src), g.copy(src) guess = g.norm2(psi) mat(psi, mmp) # in, out d = g.innerProduct(psi, mmp).real b = g.norm2(mmp) r @= src - mmp p @= r a = g.norm2(p) cp = a ssq = g.norm2(src) rsq = self.eps**2. * ssq for k in range(1, self.maxiter + 1): c = cp mat(p, mmp) dc = g.innerProduct(p, mmp) d = dc.real a = c / d cp = g.axpy_norm2(r, -a, mmp, r) b = cp / c psi += a * p p @= b * p + r self.history.append(cp) if verbose: g.message("res^2[ %d ] = %g" % (k, cp)) if cp <= rsq: if verbose: t1 = g.time() g.message("Converged in %g s" % (t1 - t0)) break
def test(slv, name): t0 = g.time() dst = g.eval(slv * src) t1 = g.time() eps2 = g.norm2(dst_cg - dst) / g.norm2(dst_cg) g.message("%s finished: eps^2(CG) = %g" % (name, eps2)) timings[name] = t1 - t0 resid[name] = eps2**0.5 assert eps2 < 5e-7
def __call__(self, link, staple, mask): verbose = g.default.is_verbose( "metropolis" ) # need verbosity categories [ performance, progress ] project_method = self.params["project_method"] step_size = self.params["step_size"] number_accept = 0 possible_accept = 0 t = g.timer("metropolis") t("action") action = g.component.real(g.eval(-g.trace(link * g.adj(staple)) * mask)) t("lattice") V = g.lattice(link) V_eye = g.identity(link) t("random") self.rng.element(V, scale=step_size, normal=True) t("update") V = g.where(mask, V, V_eye) link_prime = g.eval(V * link) action_prime = g.component.real( g.eval(-g.trace(link_prime * g.adj(staple)) * mask)) dp = g.component.exp(g.eval(action - action_prime)) rn = g.lattice(dp) t("random") self.rng.uniform_real(rn) t("random") accept = dp > rn accept *= mask number_accept += g.norm2(accept) possible_accept += g.norm2(mask) link @= g.where(accept, link_prime, link) t() g.project(link, project_method) # g.message(t) if verbose: g.message( f"Metropolis acceptance rate: {number_accept / possible_accept}" )
def inv(psi, src): self.history = [] verbose = g.default.is_verbose("mr") t = g.timer("mr") t("setup") r, mmr = g.copy(src), g.copy(src) mat(mmr, psi) r @= src - mmr r2 = g.norm2(r) ssq = g.norm2(src) # if ssq == 0.0: # assert r2 != 0.0 # need either source or psi to not be zero # ssq = r2 rsq = self.eps ** 2.0 * ssq for k in range(self.maxiter): t("mat") mat(mmr, r) t("inner") ip, mmr2 = g.inner_product_norm2(mmr, r) if mmr2 == 0.0: continue t("linearcomb") alpha = ip.real / mmr2 * self.relax psi += alpha * r t("axpy_norm") r2 = g.axpy_norm2(r, -alpha, mmr, r) t("other") self.history.append(r2) if verbose: g.message("mr: res^2[ %d ] = %g, target = %g" % (k, r2, rsq)) if r2 <= rsq: if verbose: t() g.message( "mr: converged in %d iterations, took %g s" % (k + 1, t.dt["total"]) ) g.message(t) break
def opt(x, dx, t): x = g.util.to_list(x) dx = g.util.to_list(dx) for i in range(self.maxiter): d = f.gradient(x, dx) c = self.line_search(d, x, dx, d, f.gradient, -self.step) for nu, x_mu in enumerate(dx): x_mu @= g.group.compose(-self.step * c * d[nu], x_mu) rs = (sum(g.norm2(d)) / sum([s.grid.gsites * s.otype.nfloats for s in d]))**0.5 self.log_convergence(i, rs, self.eps) if i % self.nf == 0: self.log( f"iteration {i}: f(x) = {f(x):.15e}, |df|/sqrt(dof) = {rs:e}, step = {c*self.step}" ) if rs <= self.eps: self.log( f"converged in {i+1} iterations: f(x) = {f(x):.15e}, |df|/sqrt(dof) = {rs:e}" ) return True self.log( f"NOT converged in {i+1} iterations; |df|/sqrt(dof) = {rs:e} / {self.eps:e}" ) return False
def verify_projected_even_odd(M, Meo, dst_p, src_p, src): src_proj_p = g.lattice(src) src_proj_p[:] = 0 g.set_checkerboard(src_proj_p, src_p) dst_proj_p = g.eval(M * src_proj_p) g.pick_checkerboard(dst_p.checkerboard(), dst_p, dst_proj_p) reference = g.copy(dst_p) dst_p @= Meo * src_p eps = (g.norm2(dst_p - reference) / g.norm2(reference)) ** 0.5 eps_ref = src.grid.precision.eps * finger_print_tolerance g.message(f"Test projected full matrix result versus eo matrix: {eps}") assert eps < eps_ref
def test_matrix_application(rp): rp_src = g(rp(mat) * src) rp_src /= rp.norm for p in rp.poles: g.message(p) rp_src @= mat * rp_src - p * rp_src dst = g.copy(src) for z in rp.zeros: g.message(z) dst @= mat * dst - z * dst eps2 = g.norm2(dst - rp_src) / g.norm2(dst) g.message(f"Test of matrix application: {eps2}") assert eps2 < 1e-9
def implicit_restart(self, H, evals, p): n = len(self.H) k = n - p Q = np.identity(n, np.complex128) eye = np.identity(n, np.complex128) t0 = g.time() for i in range(p): Qi, Ri = np.linalg.qr(H - evals[i] * eye) H = Ri @ Qi + evals[i] * eye Q = Q @ Qi t1 = g.time() if self.verbose: g.message(f"Arnoldi: QR in {t1-t0} s") r = g.eval(self.basis[k] * H[k, k - 1] + self.basis[-1] * self.H[-1][-1] * Q[n - 1, k - 1]) rn = g.norm2(r)**0.5 t0 = g.time() g.rotate(self.basis, np.ascontiguousarray(Q.T), 0, k, 0, n) t1 = g.time() if self.verbose: g.message(f"Arnoldi: rotate in {t1-t0} s") self.basis = self.basis[0:k] self.basis.append(g.eval(r / rn)) self.H = [[H[j, i] for j in range(i + 2)] for i in range(k)] self.H[-1][-1] = rn
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 __call__(self, mat, src, psi): verbose = g.default.is_verbose("mr") t0 = time() r, mmr = g.copy(src), g.copy(src) mat(psi, mmr) r @= src - mmr ssq = g.norm2(src) rsq = self.eps**2. * ssq for k in range(self.maxiter): mat(r, mmr) ip, mmr2 = g.innerProductNorm2(mmr, r) if mmr2 == 0.: continue alpha = ip.real / mmr2 * self.relax psi += alpha * r r2 = g.axpy_norm2(r, -alpha, mmr, r) if verbose: g.message("res^2[ %d ] = %g" % (k, r2)) if r2 <= rsq: if verbose: t1 = time() g.message("Converged in %g s" % (t1 - t0)) break
def perform(self, root): global basis_size, T, current_config if current_config is not None and current_config.conf_file != self.conf_file: current_config = None if current_config is None: current_config = config(self.conf_file) c = None vcj = [ g.vcolor(current_config.l_exact.U_grid) for jr in range(basis_size) ] for vcjj in vcj: vcjj[:] = 0 for tprime in range(T): basis_evec, basis_evals = g.load(self.basis_fmt % (self.conf, tprime)) plan = g.copy_plan(vcj[0], basis_evec[0], embed_in_communicator=vcj[0].grid) c = g.coordinates(basis_evec[0]) plan.destination += vcj[0].view[np.hstack( (c, np.ones((len(c), 1), dtype=np.int32) * tprime))] plan.source += basis_evec[0].view[c] plan = plan() for l in range(basis_size): plan(vcj[l], basis_evec[l]) for l in range(basis_size): g.message("Check norm:", l, g.norm2(vcj[l])) g.save(f"{root}/{self.name}/basis", vcj)
def exp(i): if i.grid.precision != gpt.double: x = gpt.convert(i, gpt.double) else: x = gpt.copy(i) n = gpt.norm2(x)**0.5 / x.grid.gsites order = 19 maxn = 0.05 ns = 0 if n > maxn: ns = int(numpy.log2(n / maxn)) x /= 2**ns o = gpt.lattice(x) o[:] = 0 nfac = 1.0 xn = gpt.copy(x) o[:] = numpy.identity(o.otype.shape[0], o.grid.precision.complex_dtype) o += xn for j in range(2, order + 1): nfac /= j xn @= xn * x o += xn * nfac for j in range(ns): o @= o * o if i.grid.precision != gpt.double: r = gpt.lattice(i) gpt.convert(r, o) o = r return o
def evals(matrix, evec, check_eps2 = None): assert(len(evec) > 0) tmp=g.lattice(evec[0]) ev=[] for i,v in enumerate(evec): matrix(v,tmp) # M |v> = l |v> -> <v|M|v> / <v|v> l=g.innerProduct(v,tmp).real / g.norm2(v) ev.append(l) if not check_eps2 is None: eps2=g.norm2(tmp - l*v) g.message("eval[ %d ] = %g, eps^2 = %g" % (i,l,eps2)) if eps2 > check_eps2: assert(0) return ev
def inv(psi, src, t): t("setup") # inner source n = len(src) _s = [g.lattice(x) for x in src] # norm of source norm2_of_source = g.norm2(src) for j in range(n): if norm2_of_source[j] == 0.0: norm2_of_source[j] = g.norm2(outer_mat * psi[j]) if norm2_of_source[j] == 0.0: norm2_of_source[j] = 1.0 for i in range(self.maxiter): t("outer matrix") for j in range(n): _s[j] @= src[j] - outer_mat * psi[j] # remaining src t("norm2") norm2_of_defect = g.norm2(_s) # true resid t("norm2") eps = max([(norm2_of_defect[j] / norm2_of_source[j])**0.5 for j in range(n)]) self.log_convergence(i, eps, self.eps) if eps < self.eps: self.log(f"converged in {i+1} iterations") break # normalize _s to avoid floating-point underflow in inner_inv_mat t("linear algebra") for j in range(n): _s[j] /= norm2_of_source[j]**0.5 # correction step t("inner inverter") _d = inner_inv_mat(_s) t("linear algebra") for j in range(n): psi[j] += _d[j] * norm2_of_source[j]**0.5
def draw(self, fields, rng): M, U, phi = self._updated(fields) eta = g.lattice(phi) rng.cnormal(eta, sigma=2.0**-0.5) # 1/sqrt(2) phi @= self.operator.M(M) * eta return g.norm2(eta)
def multi_shift_test(ms, name): dst_ms = g(ms(mat) * src) for i, s in enumerate(shifts): g.message( f"General multi-shift vs multi_shift_{name} for shift {i} = {s}") for jsrc in range(2): eps2 = g.norm2(dst_all[2 * i + jsrc] - dst_ms[2 * i + jsrc]) / g.norm2( dst_ms[2 * i + jsrc]) g.message( f"Test general solution versus ms{name} solution: {eps2}") assert eps2 < 1e-14 eps2 = g.norm2(mat * dst_ms[2 * i + jsrc] + s * dst_ms[2 * i + jsrc] - src[jsrc]) / g.norm2( src[jsrc]) g.message(f"Test ms{name} inverter solution: {eps2}") assert eps2 < 1e-14
def inv(psi, src): assert src != psi self.history = [] verbose = g.default.is_verbose("cg") t = g.timer("cg") t("setup") p, mmp, r = g.copy(src), g.copy(src), g.copy(src) mat(mmp, psi) # in, out d = g.inner_product(psi, mmp).real b = g.norm2(mmp) r @= src - mmp p @= r a = g.norm2(p) cp = a ssq = g.norm2(src) if ssq == 0.0: assert a != 0.0 # need either source or psi to not be zero ssq = a rsq = self.eps ** 2.0 * ssq for k in range(1, self.maxiter + 1): c = cp t("mat") mat(mmp, p) t("inner") dc = g.inner_product(p, mmp) d = dc.real a = c / d t("axpy_norm") cp = g.axpy_norm2(r, -a, mmp, r) t("linearcomb") b = cp / c psi += a * p p @= b * p + r t("other") self.history.append(cp) if verbose: g.message("cg: res^2[ %d ] = %g, target = %g" % (k, cp, rsq)) if cp <= rsq: if verbose: t() g.message( "cg: converged in %d iterations, took %g s" % (k, t.dt["total"]) ) g.message(t) break
def fields_agree_openqcd(ref, res): """ Implements the correctness check as it is used in openqcd, sarchive.c:614 """ if type(ref) == g.lattice and type(res) == g.lattice: assert ref.grid.precision == g.double and res.grid.precision == g.double norm2_ref = g.norm2(ref) if type(ref) == g.lattice else ref norm2_res = g.norm2(res) if type(res) == g.lattice else res diff = abs(norm2_res - norm2_ref) tol = 64.0 * (norm2_ref + norm2_res) * sys.float_info.epsilon g.message( f"openqcd residual check: reference = {norm2_ref:25.20e}, result = {norm2_res:25.20e}, difference = {diff:25.20e}, tol = {tol:25.20e} -> check {'passed' if diff <= tol else 'failed'}" ) return diff <= tol
def inv(psi, src, t): if len(self.solution_space) == 0: return t("orthonormalize") v = g.orthonormalize(g.copy(self.solution_space)) # Idea is to minimize # # res = | M a_i v_i - src |^2 # = v_i^dag a_i^dag M^dag M a_j v_j + src^dag src - src^dag M a_i v_i - v_i^dag a_i^dag M^dag src # # by selecting an optimal a_i, i.e., to compute # # d res/d a_i^dag = v_i^dag M^dag M a_j v_j - v_i^dag M^dag src = 0 # # Therefore # # G_ij a_j = b_i # # with b_i = v_i^dag M^dag src, G_ij = v_i^dag M^dag M v_j # t("mat v") mat_v = [mat(x) for x in v] t("projected source") b = g.inner_product(mat_v, src)[:, 0] t("projected matrix") G_ij = np.matrix([ g.inner_product(mat_v, mat_v[j])[:, 0] for j in range(len(v)) ]).T t("solve") a = np.linalg.solve(G_ij, b) t("linear combination") g.linear_combination(psi, v, a) eps2 = g.norm2(mat(psi) - src) / g.norm2(src) self.log( f"minimal residual with {len(v)}-dimensional solution space has eps^2 = {eps2}" )
def fields_agree(ref, res, tol): """ Implements the standard correctness check between two fields with their relative deviation """ norm2_ref = g.norm2(ref) if type(ref) == g.lattice else ref norm2_res = g.norm2(res) if type(res) == g.lattice else res if type(ref) == g.lattice and type(res) == g.lattice: diff = g.norm2(res - ref) else: diff = abs(norm2_ref - norm2_res) rel_dev = diff / norm2_ref g.message( f"default residual check: reference = {norm2_ref:25.20e}, result = {norm2_res:25.20e}, rel_dev = {rel_dev:25.20e}, tol = {tol:25.20e} -> check {'passed' if rel_dev <= tol else 'failed'}" ) return rel_dev <= tol
def divergence(f, current): resN = g.lattice(f) resN[:] = 0 for mu in range(4): c_mu = current(f, f, mu) resN += c_mu - g.cshift(c_mu, mu, -1) return g.norm2(resN)
def inv(psi, src, t): t("setup") r, mmr = g.copy(src), g.copy(src) mat(mmr, psi) r @= src - mmr r2 = g.norm2(r) ssq = g.norm2(src) # if ssq == 0.0: # assert r2 != 0.0 # need either source or psi to not be zero # ssq = r2 rsq = self.eps ** 2.0 * ssq for k in range(self.maxiter): t("mat") mat(mmr, r) t("inner") ip, mmr2 = g.inner_product_norm2(mmr, r) if mmr2 == 0.0: continue t("linearcomb") alpha = ip.real / mmr2 * self.relax psi += alpha * r t("axpy_norm") r2 = g.axpy_norm2(r, -alpha, mmr, r) t("other") self.log_convergence(k, r2, rsq) if r2 <= rsq: self.log(f"converged in {k+1} iterations") return self.log( f"NOT converged in {k+1} iterations; squared residual {r2:e} / {rsq:e}" )
def divergence(f, current): resN = g.lattice(f) resN[:] = 0 b = g(g.gamma[5] * g.adj(f) * g.gamma[5]) for mu in range(4): c_mu = current(f, b, mu) resN += c_mu - g.cshift(c_mu, mu, -1) return g.norm2(resN)
def verify_matrix_element(mat, dst, src, tag): src_prime = g.eval(mat * src) dst.checkerboard(src_prime.checkerboard()) X = g.inner_product(dst, src_prime) eps_ref = src.grid.precision.eps * 50.0 if mat.adj_mat is not None: X_from_adj = g.inner_product(src, g.adj(mat) * dst).conjugate() eps = abs(X - X_from_adj) / abs(X) g.message(f"Test adj({tag}): {eps}") assert eps < eps_ref if mat.inv_mat is not None: eps = (g.norm2(src - mat * g.inv(mat) * src) / g.norm2(src))**0.5 g.message(f"Test inv({tag}): {eps}") assert eps < eps_ref Y = g.inner_product(dst, g.inv(g.adj(mat)) * src) Y_from_adj = g.inner_product(src, g.inv(mat) * dst).conjugate() eps = abs(Y - Y_from_adj) / abs(Y) g.message(f"Test adj(inv({tag})): {eps}") assert eps < eps_ref return X
def check_representation(U, eps_ref): algebra = g.convert(U, U.otype.cartesian()) # then test coordinates function algebra2 = g.lattice(algebra) algebra2[:] = 0 algebra2.otype.coordinates(algebra2, algebra.otype.coordinates(algebra)) eps = (g.norm2(algebra2 - algebra) / g.norm2(algebra))**0.5 g.message(f"Test coordinates: {eps}") assert eps < eps_ref # now project to algebra and make sure it is a linear combination of # the provided generators n0 = g.norm2(algebra) algebra2.otype.coordinates( algebra2, g.component.real(algebra.otype.coordinates(algebra))) algebra -= algebra2 eps = (g.norm2(algebra) / n0)**0.5 g.message(f"Test representation: {eps}") assert eps < eps_ref