def nearest_neighbor_operator(fine_matrix, coarse_grid, basis, params): A = [gpt.mcomplex(coarse_grid, len(basis)) for i in range(9)] create_links( A, fine_matrix, basis, make_hermitian=params["make_hermitian"], save_links=True ) level = ( 1 if isinstance(fine_matrix.otype, gpt.ot_matrix_complex_additive_group) else 0 ) return gpt.qcd.fermion.coarse_fermion(A, level=level)
mask[0, 1, 2, 3] = 1 vc[:] = vc[0, 0, 0, 0] vcmask = g.eval(mask * vc) assert g.norm2(vcmask[0, 0, 0, 0]) < 1e-13 assert g.norm2(vcmask[0, 1, 2, 3] - vc_comp) < 1e-13 # demonstrate sign flip needed for MG sign = g.vcomplex([1] * 15 + [-1] * 15, 30) vc_comp = g.vcomplex([1] + [1.5] * 14 + [-1.5] * 14 + [-2], 30) vc @= sign * vc eps2 = g.norm2(vc[0, 0, 0, 0] - vc_comp) assert eps2 < 1e-13 # demonstrate matrix * vector ntest = 30 mc_comp = g.mcomplex([[rng.cnormal() for i in range(ntest)] for j in range(ntest)], ntest) mc = g.mcomplex(grid, ntest) mc[:] = mc_comp vc_comp = g.vcomplex([rng.cnormal() for i in range(ntest)], ntest) vc = g.vcomplex(grid, ntest) vc[:] = vc_comp assert g.norm2(mc[0, 0, 0, 0] - mc_comp) < 1e-10 vc2 = g.eval(mc * vc) vc2_comp = mc_comp * vc_comp vc3 = g.eval(mc_comp * vc) assert g.norm2(vc2[0, 0, 0, 0] - vc2_comp) < 1e-10 eps2 = g.norm2(vc2 - vc3) / g.norm2(vc2) assert eps2 < 1e-10 # test transpose and adjoint of mcomplex
def __init__(self, mat_f, params): # save parameters self.params = params # fine grid from fine matrix if issubclass(type(mat_f), g.matrix_operator): self.grid = [mat_f.grid[1]] else: self.grid = [mat_f.grid] # grid sizes - allow specifying in two ways if "grid" in params: self.grid.extend(params["grid"]) elif "blocksize" in params: for i, bs in enumerate(params["blocksize"]): assert type(bs) == list self.grid.append(g.block.grid(self.grid[i], bs)) else: assert 0 # dependent sizes self.nlevel = len(self.grid) self.ncoarselevel = self.nlevel - 1 self.finest = 0 self.coarsest = self.nlevel - 1 # other parameters self.nblockortho = g.util.to_list(params["nblockortho"], self.nlevel - 1) self.check_blockortho = g.util.to_list(params["check_blockortho"], self.nlevel - 1) self.nbasis = g.util.to_list(params["nbasis"], self.nlevel - 1) self.make_hermitian = g.util.to_list(params["make_hermitian"], self.nlevel - 1) self.save_links = g.util.to_list(params["save_links"], self.nlevel - 1) self.npreortho = g.util.to_list(params["npreortho"], self.nlevel - 1) self.npostortho = g.util.to_list(params["npostortho"], self.nlevel - 1) self.vector_type = g.util.to_list(params["vector_type"], self.nlevel - 1) self.distribution = g.util.to_list(params["distribution"], self.nlevel - 1) self.solver = g.util.to_list(params["solver"], self.nlevel - 1) # verbosity self.verbose = g.default.is_verbose("multi_grid_setup") # print prefix self.print_prefix = [ "mg_setup: level %d:" % i for i in range(self.nlevel) ] # easy access to current level and neighbors self.lvl = [i for i in range(self.nlevel)] self.nf_lvl = [i - 1 for i in range(self.nlevel)] self.nc_lvl = [i + 1 for i in range(self.nlevel)] self.nf_lvl[self.finest] = None self.nc_lvl[self.coarsest] = None # halved nbasis self.nb = [] for lvl, b in enumerate(self.nbasis): assert b % 2 == 0 self.nb.append(b // 2) # assertions assert self.nlevel >= 2 assert g.util.entries_have_length( [ self.nblockortho, self.nbasis, self.make_hermitian, self.save_links, self.npreortho, self.npostortho, self.vector_type, self.distribution, self.solver, self.nb, ], self.nlevel - 1, ) # timing self.t = [ g.timer("mg_setup_lvl_%d" % (lvl)) for lvl in range(self.nlevel) ] # matrices (coarse ones initialized later) self.mat = [mat_f] + [None] * (self.nlevel - 1) # setup random basis vectors on all levels but coarsest self.basis = [None] * self.nlevel for lvl, grid in enumerate(self.grid): if lvl == self.coarsest: continue elif lvl == self.finest: self.basis[lvl] = [ g.vspincolor(grid) for __ in range(self.nbasis[lvl]) ] else: self.basis[lvl] = [ g.vcomplex(grid, self.nbasis[self.nf_lvl[lvl]]) for __ in range(self.nbasis[lvl]) ] self.distribution[lvl](self.basis[lvl][0:self.nb[lvl]]) # setup a block map on all levels but coarsest self.blockmap = [None] * self.nlevel for lvl in self.lvl: if lvl == self.coarsest: continue else: self.blockmap[lvl] = g.block.map(self.grid[self.nc_lvl[lvl]], self.basis[lvl]) # setup coarse link fields on all levels but finest self.A = [None] * self.nlevel for lvl in range(self.finest + 1, self.nlevel): self.A[lvl] = [ g.mcomplex(self.grid[lvl], self.nbasis[self.nf_lvl[lvl]]) for __ in range(9) ] # setup a solver history self.history = [[None]] * (self.nlevel - 1) # rest of setup self.__call__()
################################################################################ # Test all other representations ################################################################################ for eps_ref, grid in [(1e-6, grid_sp), (1e-12, grid_dp)]: for representation in [ g.matrix_su2_fundamental, g.matrix_su2_adjoint, g.matrix_su3_fundamental, g.u1, g.complex, g.real, lambda grid: g.vreal(grid, 8), lambda grid: g.mreal(grid, 8), lambda grid: g.vcomplex(grid, 8), lambda grid: g.mcomplex(grid, 8), ]: U = representation(grid) g.message(f"Test {U.otype.__name__} on grid {grid.precision.__name__}") rng.element(U) check_element(U) check_representation(U, eps_ref) for method in ["defect_left", "defect_right"]: g.project(U, method) check_element(U) V = representation(grid) rng.element(V) check_inner_product(U, V, eps_ref) ################################################################################
basis_f = [g.vspincolor(grid_f) for __ in range(nbasis_f // 2)] rng.cnormal(basis_f) # split fine basis into chiral halfs g.qcd.fermion.coarse.split_chiral(basis_f) # setup fine block map bm_f = g.block.map(grid_c, basis_f) # orthonormalize fine basis for i in range(nblockortho): g.message("Block ortho step %d" % i) bm_f.orthonormalize() # create coarse link fields A_c = [g.mcomplex(grid_c, nbasis_f) for __ in range(9)] g.qcd.fermion.coarse.create_links(A_c, mat_f, basis_f, { "make_hermitian": False, "save_links": True }) # create coarse operator from links mat_c = g.qcd.fermion.coarse_fermion(A_c, level=0) # save typing def vec_c_full(): return g.vcomplex(mat_c.F_grid, nbasis_f) def vec_c_half():
b = op[1](m[0, 0, 0, 0, 1, 2]) eps2 = (abs(a - b) / abs(a))**2.0 g.message( f"Test {op[1].__name__}: {a} == {b} with argument {m[0, 0, 0, 0, 1, 2]}: {eps2}" ) assert eps2 < eps**2.0 # test inv for grid, eps in [(grid_dp, 1e-14), (grid_sp, 1e-6)]: g.message(f""" Test log,exp,det,tr for {grid.precision.__name__} """) for dtype in [ g.mspincolor, g.mcolor, g.mspin, lambda grid: g.mcomplex(grid, 8) ]: rng = g.random("test") m = rng.cnormal(dtype(grid)) minv = g.matrix.inv(m) eye = g.identity(m) eps2 = g.norm2(m * minv - eye) / (12 * grid.fsites) g.message(f"test M*M^-1 = 1 for {m.otype.__name__}: {eps2}") assert eps2 < eps**2 # make logarithm well defined m @= eye + 0.01 * m m2 = g.matrix.exp(g.matrix.log(m)) eps2 = g.norm2(m - m2) / g.norm2(m) g.message(f"exp(log(m)) == m: {eps2}") assert eps2 < eps**2.0
basis_f = [g.vspincolor(grid_f) for __ in range(nbasis_f // 2)] rng.cnormal(basis_f) # split fine basis into chiral halfs g.qcd.fermion.coarse.split_chiral(basis_f) # setup fine block map bm_f = g.block.map(grid_c, basis_f) # orthonormalize fine basis for i in range(nblockortho): g.message("Block ortho step %d" % i) bm_f.orthonormalize() # create coarse link fields A_c = [g.mcomplex(grid_c, nbasis_f) for __ in range(9)] Asaved_c = [g.mcomplex(grid_c, nbasis_f) for __ in range(9)] g.qcd.fermion.coarse.create_links( A_c, mat_f, basis_f, {"make_hermitian": False, "save_links": False} ) g.qcd.fermion.coarse.create_links( Asaved_c, mat_f, basis_f, {"make_hermitian": False, "save_links": True} ) # compare link fields for p in range(9): err2 = g.norm2(A_c[p] - Asaved_c[p]) / g.norm2(A_c[p]) g.message( f"Relative deviation of Asaved_c[{p}] from A_c[{p}] = {err2:e}", ) assert err2 <= tol_links
N = g.default.get_int("--N", 1000) nbasis = g.default.get_int("--nbasis", 40) level = g.default.get_int("--level", 0) for precision in [g.single, g.double]: grid = g.grid(g.default.get_ivec("--grid", [6, 6, 6, 6], 4), precision) g.message(f""" Coarse Operator Benchmark with fdimensions : {grid.fdimensions} precision : {precision.__name__} nbasis : {nbasis} level : {level} """) # Coarse operator A = [g.mcomplex(grid, nbasis) for __ in range(9)] rng.cnormal(A) co = g.qcd.fermion.coarse(A, {"level": level}) # Source and destination src = g.vcomplex(grid, nbasis) dst = g.vcomplex(grid, nbasis) rng.cnormal(src) # Flops flops_per_site = 2 * nbasis * (36 * nbasis - 1) flops = flops_per_site * src.grid.gsites * N nbytes = ((9 * 2 * nbasis + 9 * 2 * nbasis * nbasis + 2 * nbasis) * precision.nbytes * src.grid.gsites * N) # Warmup