/************************************************************************************* Grid physics library, www.github.com/paboyle/Grid Source file: ./examples/Example_mdagm.cc Copyright (C) 2023 Author: Peter Boyle This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. See the full license in the file "LICENSE" in the top level distribution directory *************************************************************************************/ /* END LEGAL */ #include #include #include using namespace std; using namespace Grid; // Routes Op/AdjOp -> HermOp so that CoarsenOperator and CreateSubspace // both see the HPD operator M†M rather than bare M. template class HermOpAdaptor : public LinearOperatorBase { LinearOperatorBase &wrapped; public: HermOpAdaptor(LinearOperatorBase &wrapme) : wrapped(wrapme) {}; void Op (const Field &in, Field &out) { wrapped.HermOp(in,out); } void HermOp (const Field &in, Field &out) { wrapped.HermOp(in,out); } void AdjOp (const Field &in, Field &out) { wrapped.HermOp(in,out); } void OpDiag (const Field &in, Field &out) { GRID_ASSERT(0); } void OpDir (const Field &in, Field &out,int dir,int disp) { GRID_ASSERT(0); } void OpDirAll(const Field &in, std::vector &out) { GRID_ASSERT(0); } void HermOpAndNorm(const Field &in, Field &out, RealD &n1, RealD &n2) { wrapped.HermOp(in, out); ComplexD dot = innerProduct(in, out); n1 = real(dot); n2 = norm2(out); } }; // Fixed-iteration CG smoother: runs exactly `iters` steps of CG on the // shifted operator. tolerance=0 so CG never exits early. template class CGSmoother : public LinearFunction { public: using LinearFunction::operator(); typedef LinearOperatorBase FineOperator; FineOperator &_SmootherOperator; int iters; CGSmoother(int _iters, FineOperator &SmootherOperator) : _SmootherOperator(SmootherOperator), iters(_iters) { std::cout << GridLogMessage << " CGSmoother order " << iters << std::endl; } void operator()(const Field &in, Field &out) { ConjugateGradient CG(0.0, iters, false); out = Zero(); CG(_SmootherOperator, in, out); } }; int main (int argc, char ** argv) { Grid_init(&argc,&argv); const int Ls = 24; const int nbasis = 60; const int cb = 0; RealD M5 = 1.8; RealD b = 1.5; RealD c = 0.5; RealD mass = 0.00078; { const char *e = getenv("MASS"); if (e && *e) mass = atof(e); } std::cout << GridLogMessage << "Mass: " << mass << " Ls: " << Ls << " b=" << b << " c=" << c << std::endl; std::cout << GridLogMessage << "nbasis: " << nbasis << std::endl; // ── Grids ────────────────────────────────────────────────────────────── std::vector lat_size{48,48,48,96}; GridCartesian *UGrid = SpaceTimeGrid::makeFourDimGrid(lat_size, GridDefaultSimd(Nd,vComplex::Nsimd()), GridDefaultMpi()); GridRedBlackCartesian *UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid); GridCartesian *FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid); GridRedBlackCartesian *FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid); Coordinate Block({4,4,3,4}); Coordinate clatt = lat_size; for (int d = 0; d < (int)clatt.size(); d++) clatt[d] /= Block[d]; GridCartesian *Coarse4d = SpaceTimeGrid::makeFourDimGrid(clatt, GridDefaultSimd(Nd,vComplex::Nsimd()), GridDefaultMpi()); GridCartesian *Coarse5d = SpaceTimeGrid::makeFiveDimGrid(1,Coarse4d); // ── RNGs ─────────────────────────────────────────────────────────────── GridParallelRNG RNG5(FGrid); RNG5.SeedFixedIntegers({5,6,7,8}); GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers({1,2,3,4}); // ── Gauge field ──────────────────────────────────────────────────────── LatticeGaugeField Umu(UGrid); FieldMetaData header; NerscIO::readConfiguration(Umu, header, std::string("/ccs/home/poare/ckpoint_lat.1000")); // ── Fermion operator ─────────────────────────────────────────────────── MobiusFermionD Ddwf(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5,b,c); MdagMLinearOperator MdagMOp(Ddwf); HermOpAdaptor HermFineOp(MdagMOp); // ── Coarse geometry ──────────────────────────────────────────────────── typedef GeneralCoarsenedMatrix LittleDiracOperator; typedef LittleDiracOperator::CoarseVector CoarseVector; typedef Aggregation Subspace; NextToNearestStencilGeometry5D geom(Coarse5d); // ── Power method: estimate upper end of M†M spectrum ─────────────────── LatticeFermionD pm_src(FGrid); random(RNG5, pm_src); PowerMethod PM; RealD hi = PM(HermFineOp, pm_src); std::cout << GridLogMessage << "Power method: hi = " << hi << std::endl; // ── Smoother: fixed-iteration CG on (M†M + lo) ───────────────────────── // lo/hi ~ 2/95 matches the HDCG ratio; tune empirically. RealD lo = hi / 40.0; int ord = 12; std::cout << GridLogMessage << "Smoother shift lo = " << lo << " order = " << ord << std::endl; ShiftedHermOpLinearOperator ShiftedFineOp(HermFineOp, lo); CGSmoother Smoother(ord, ShiftedFineOp); // ── Subspace via CG inverse iteration ────────────────────────────────── Subspace Aggregates(Coarse5d, FGrid, cb); Aggregates.CreateSubspace(RNG5, HermFineOp, nbasis); // ── Cheap coarse deflation: diagonalise W BEFORE block-GS ────────────── // Orthogonalise() applies block-local Gram-Schmidt which rotates subspace[i] // into orthonormal block-local combinations, destroying the near-null // property of individual vectors. We must extract the coarse zero-mode // combinations χₖ = Σᵢ V[i,k] ψᵢ from the pre-GS near-null vectors first. std::vector chi(nbasis, FGrid); { std::vector &psi = Aggregates.subspace; LatticeFermionD tmp(FGrid); Eigen::MatrixXcd W = Eigen::MatrixXcd::Zero(nbasis, nbasis); for (int i = 0; i < nbasis; i++) { HermFineOp.Op(psi[i], tmp); for (int j = 0; j < nbasis; j++) W(j, i) = TensorRemove(innerProduct(psi[j], tmp)); } Eigen::SelfAdjointEigenSolver esolver(W); for (int k = 0; k < nbasis; k++) { chi[k] = Zero(); for (int i = 0; i < nbasis; i++) chi[k] += ComplexD(esolver.eigenvectors()(i, k)) * psi[i]; } } Aggregates.Orthogonalise(); // ── Coarse operator ──────────────────────────────────────────────────── LittleDiracOperator LittleDiracOp(geom, FGrid, Coarse5d); LittleDiracOp.CoarsenOperator(HermFineOp, Aggregates); // ── Coarse linear operator ───────────────────────────────────────────── HermitianLinearOperator LinOpCoarse(LittleDiracOp); // ── Project χₖ to coarse grid; Rayleigh quotients give deflation evals ─ // ProjectToSubspace uses the post-GS basis, correctly mapping the pre-GS // near-null combinations to coarse vectors via the block-local U†(x_c). std::vector coarse_deflation_vecs(nbasis, Coarse5d); std::vector coarse_deflation_evals(nbasis); { CoarseVector Ac(Coarse5d); for (int k = 0; k < nbasis; k++) { Aggregates.ProjectToSubspace(coarse_deflation_vecs[k], chi[k]); RealD n = norm2(coarse_deflation_vecs[k]); coarse_deflation_vecs[k] *= 1.0 / std::sqrt(n); LinOpCoarse.HermOp(coarse_deflation_vecs[k], Ac); coarse_deflation_evals[k] = real(TensorRemove(innerProduct(coarse_deflation_vecs[k], Ac))); std::cout << GridLogMessage << "Coarse deflation eval[" << k << "] = " << coarse_deflation_evals[k] << std::endl; } } // ── Coarse solve: CG + deflation guesser ────────────────────────────── ConjugateGradient coarseCG(5.0e-2, 10000, false); DeflatedGuesser coarseGuess(coarse_deflation_vecs, coarse_deflation_evals); HPDSolver CoarseSolve(LinOpCoarse, coarseCG, coarseGuess); // ── ADEF2 outer solve ────────────────────────────────────────────────── LatticeFermionD src(FGrid); random(RNG5, src); LatticeFermionD result(FGrid); result = Zero(); TwoLevelADEF2 HDCG(1.0e-8, 1000, HermFineOp, Smoother, CoarseSolve, // used in PcgM1 CoarseSolve, // used in Vstart Aggregates); HDCG(src, result); // ── Reference RBCG ───────────────────────────────────────────────────── #if 0 { SchurDiagMooeeOperator HermOpEO(Ddwf); LatticeFermionD rb_src(FrbGrid); random(RNG5, rb_src); LatticeFermionD rb_res(FrbGrid); rb_res = Zero(); ConjugateGradient CG(1.0e-8, 30000, false); CG(HermOpEO, rb_src, rb_res); } #endif Grid_finalize(); return 0; }