From ab9603b28dd128f1a05fb8e745470d488d910dce Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Wed, 19 Aug 2026 19:58:35 -0400 Subject: [PATCH] Support for lexLattice in coarse space and eliminating layout changes in multigrid --- .../GeneralCoarsenedMatrixMultiRHSV2.h | 851 ++++++++++++++++++ .../multigrid/MrhsPromotedOperator.h | 161 ++++ 2 files changed, 1012 insertions(+) create mode 100644 Grid/algorithms/multigrid/GeneralCoarsenedMatrixMultiRHSV2.h create mode 100644 Grid/algorithms/multigrid/MrhsPromotedOperator.h diff --git a/Grid/algorithms/multigrid/GeneralCoarsenedMatrixMultiRHSV2.h b/Grid/algorithms/multigrid/GeneralCoarsenedMatrixMultiRHSV2.h new file mode 100644 index 000000000..1ed673a3c --- /dev/null +++ b/Grid/algorithms/multigrid/GeneralCoarsenedMatrixMultiRHSV2.h @@ -0,0 +1,851 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./lib/algorithms/GeneralCoarsenedMatrixMultiRHS.h + + Copyright (C) 2015 + +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 */ +#pragma once + + +NAMESPACE_BEGIN(Grid); + + +// Fine Object == (per site) type of fine field +// nbasis == number of deflation vectors +template +class MultiGeneralCoarsenedOperatorV2 : public SparseMatrixBase > > { +public: + typedef typename CComplex::scalar_object SComplex; + typedef GeneralCoarsenedMatrix GeneralCoarseOp; + typedef MultiGeneralCoarsenedOperatorV2 MultiGeneralCoarseOp; + + typedef iVector siteVector; + typedef iMatrix siteMatrix; + typedef iVector calcVector; + typedef iMatrix calcMatrix; + typedef Lattice > CoarseComplexField; + typedef Lattice CoarseVector; + typedef Lattice > CoarseMatrix; + typedef iMatrix Cobj; + typedef iVector Cvec; + typedef Lattice< CComplex > CoarseScalar; // used for inner products on fine field + typedef Lattice FineField; + typedef CoarseVector Field; + + // Block operations on the fine vectors carry the fine layout, which need + // not be the coarse one + typedef decltype(innerProduct(Fobj(),Fobj())) FineInner; + typedef Lattice FineComplexField; + typedef Lattice BlockComplexField; + + //////////////////// + // Data members + // + // Nrhs independent: the D dimensional coarse grid, the geometry, the padded + // cell that supplies the stencil grid, the stencil, and the matrix elements. + // + // Nrhs dependent: the D+1 grid, its padded cell, and the BLAS B/C buffers + // with their pointer tables. Owned by SetNRHS(). + //////////////////// + GridCartesian * _CoarseGrid; // D dimensional + NonLocalStencilGeometry geom; + NonLocalStencilGeometry geom_srhs; + PaddedCell CellD; // D dimensional, supplies stencil grid + GeneralLocalStencil Stencil; // D dimensional + + int _Nrhs; + GridCartesian * _CoarseGridMulti; // D+1 dimensional, SetNRHS + PaddedCell * CellMulti; // D+1 dimensional, SetNRHS + + deviceVector BLAS_B; + deviceVector BLAS_C; + std::vector > BLAS_A; + + std::vector > BLAS_AP; + std::vector > BLAS_BP; + deviceVector BLAS_CP; + + /////////////////////// + // Interface + /////////////////////// + GridBase * Grid(void) { CheckGridSet(); return _CoarseGridMulti; }; + GridCartesian * CoarseGrid(void) { CheckGridSet(); return _CoarseGridMulti; }; + GridCartesian * CoarseGridD(void) { return _CoarseGrid; }; // lower dimensional grid + int Nrhs(void) { CheckGridSet(); return _Nrhs; }; + + void CheckGridSet(void) + { + if ( _CoarseGridMulti == nullptr ) { + std::cout << GridLogError + << "MultiGeneralCoarsenedOperatorV2: the multiRHS grid has not been set." + << std::endl; + std::cout << GridLogError + << " Call SetGrid(CoarseGridMulti) with the D+1 dimensional grid your" + << std::endl; + std::cout << GridLogError + << " coarse vectors live on, before Grid(), Nrhs() or M()." + << std::endl; + GRID_ASSERT(_CoarseGridMulti != nullptr); + } + } + + // Can be used to do I/O on the operator matrices externally + void SetMatrix (int p,CoarseMatrix & A) + { + GRID_ASSERT(A.size()==geom_srhs.npoint); + GridtoBLAS(A[p],BLAS_A[p]); + } + void GetMatrix (int p,CoarseMatrix & A) + { + GRID_ASSERT(A.size()==geom_srhs.npoint); + BLAStoGrid(A[p],BLAS_A[p]); + } + void CopyMatrix (GeneralCoarseOp &_Op) + { + for(int p=0;plSites(); + + ///////////////////////////////////////////////// + // Matrix elements and their pointer table + ///////////////////////////////////////////////// + BLAS_A.resize(geom.npoint); + BLAS_AP.resize(geom.npoint); + for(int p=0;p_ndimension; + + GRID_ASSERT(CoarseGridMulti->_ndimension == nd+1); + GRID_ASSERT(CoarseGridMulti->_processors[0] == 1); // rhs is not distributed + for(int d=0;d_fdimensions[d+1] == _CoarseGrid->_fdimensions[d]); + GRID_ASSERT(CoarseGridMulti->_processors [d+1] == _CoarseGrid->_processors [d]); + GRID_ASSERT(CoarseGridMulti->_simd_layout[d+1] == _CoarseGrid->_simd_layout[d]); + } + + _CoarseGridMulti = CoarseGridMulti; + _Nrhs = CoarseGridMulti->_fdimensions[0]; + GRID_ASSERT(_Nrhs>=1); + + int nrhs = _Nrhs; + + CellMulti = new PaddedCell(geom.Depth(),_CoarseGridMulti); + + int32_t padded_sites = CellD.grids.back()->lSites(); // D dimensional + int32_t unpadded_sites = _CoarseGrid->lSites(); // D dimensional + + // The neighbour offset multiplication by nrhs is exact only if the D+1 + // padded volume is nrhs copies of the D dimensional one. Check it. + GRID_ASSERT(CellMulti->grids.back()->lSites() == nrhs*padded_sites); + GRID_ASSERT(_CoarseGridMulti->lSites() == nrhs*unpadded_sites); + + ///////////////////////////////////////////////// + // Device data vector storage + ///////////////////////////////////////////////// + BLAS_B.resize(nrhs *padded_sites); // includes ghost zone + BLAS_C.resize(nrhs *unpadded_sites); // no ghost zone + BLAS_BP.resize(geom.npoint); + for(int p=0;p lSite, D dim + nbr = nbr*nrhs; // D -> D+1, rhs innermost + GRID_ASSERT(nbr void GridtoBLAS(const Lattice &from,deviceVector &to) + { + typedef typename vobj::scalar_object sobj; + typedef typename vobj::scalar_type scalar_type; + typedef typename vobj::vector_type vector_type; + + GridBase *Fg = from.Grid(); + GRID_ASSERT(!Fg->_isCheckerBoarded); + int nd = Fg->_ndimension; + + to.resize(Fg->lSites()); + + Coordinate LocalLatt = Fg->LocalDimensions(); + size_t nsite = 1; + for(int i=0;i_ostride; + Coordinate f_istride = Fg->_istride; + Coordinate f_rdimensions = Fg->_rdimensions; + + autoView(from_v,from,AcceleratorRead); + auto to_v = &to[0]; + + const int words=sizeof(vobj)/sizeof(vector_type); + accelerator_for(idx,nsite,1,{ + + Coordinate from_coor, base; + Lexicographic::CoorFromIndex(base,idx,LocalLatt); + for(int i=0;i void BLAStoGrid(Lattice &grid,deviceVector &in) + { + typedef typename vobj::scalar_object sobj; + typedef typename vobj::scalar_type scalar_type; + typedef typename vobj::vector_type vector_type; + + GridBase *Tg = grid.Grid(); + GRID_ASSERT(!Tg->_isCheckerBoarded); + int nd = Tg->_ndimension; + + GRID_ASSERT(in.size()==Tg->lSites()); + + Coordinate LocalLatt = Tg->LocalDimensions(); + size_t nsite = 1; + for(int i=0;i_ostride; + Coordinate t_istride = Tg->_istride; + Coordinate t_rdimensions = Tg->_rdimensions; + + autoView(to_v,grid,AcceleratorWrite); + auto from_v = &in[0]; + + const int words=sizeof(vobj)/sizeof(vector_type); + accelerator_for(idx,nsite,1,{ + + Coordinate to_coor, base; + Lexicographic::CoorFromIndex(base,idx,LocalLatt); + for(int i=0;i + // = \sum_{l in ball} e^{iqk.delta_l} A_ji^{b.b+l} + // = M_{kl} A_ji^{b.b+l} + // + // Where q_k = delta_k . (2*M_PI/global_nb[mu]) + // Then A{ji}^{b,b+l} = M^{-1}_{lm} ComputeProj_{m,b,i,j} + /////////////////////////////////////////////////////////////////////////// + void CoarsenFourierMatrix(GridBase *CoarseGrid,Eigen::MatrixXcd &invMkl) + { + const int npoint = geom_srhs.npoint; + Coordinate clatt = CoarseGrid->GlobalDimensions(); + int Nd = CoarseGrid->Nd(); + + Eigen::MatrixXcd Mkl = Eigen::MatrixXcd::Zero(npoint,npoint); + ComplexD ci(0.0,1.0); + for(int k=0;k_ndimension; + latt.resize(nd); simd.resize(nd); mpi.resize(nd); + for(int d=0;d_fdimensions[d]; + simd[d] = grid->_simd_layout[d]; + mpi [d] = CoarseGrid->_processors[d]; + } + } + + // D+1 coarse grid holding the batch, rhs innermost and unvectorised + void CoarsenBatchGridLayout(GridBase *CoarseGrid,int batch, + Coordinate &latt,Coordinate &simd,Coordinate &mpi) + { + latt.resize(1,batch); simd.resize(1,1); mpi.resize(1,1); + latt[0]=batch; simd[0]=1; mpi[0]=1; + for(int d=0;d_ndimension;d++){ + latt.push_back(CoarseGrid->_fdimensions[d]); + simd.push_back(CoarseGrid->_simd_layout[d]); + mpi .push_back(CoarseGrid->_processors[d]); + } + } + + /////////////////////////////////////////////////////////////////////////// + // The Fourier inverse needs the phase in the coarse layout and the basis + // phasing needs it in the fine layout; each is built from its own + // coordinates rather than transferred. + /////////////////////////////////////////////////////////////////////////// + void CoarsenPhases(GridBase *grid,GridBase *CoarseGrid,GridCartesian *BlockGrid, + std::vector &pha, + std::vector &phaF) + { + const int npoint = geom_srhs.npoint; + Coordinate clatt = CoarseGrid->GlobalDimensions(); + int Nd = CoarseGrid->Nd(); + ComplexD ci(0.0,1.0); + + typedef typename CComplex::scalar_type SComplex; + FineComplexField one(grid); one=SComplex(1.0); + FineComplexField zz(grid); zz = Zero(); + BlockComplexField pha_blk (BlockGrid); + BlockComplexField blk_coor(BlockGrid); + + for(int p=0;p &pha, + CoarseComplexField &phaB, + CoarseVector &TmpProj, + std::vector &_A, + GridBase *CoarseGrid) + { + typedef typename CComplex::scalar_type SComplex; + const int npoint = geom_srhs.npoint; + + for(int b=0;boSites(); + for(int k=0;k > &linop, + GridCartesian *FineGridMulti, + std::vector &Subspace, + GridBase *CoarseGrid) + { + RealD tproj=0.0, tmat=0.0, tphase=0.0, tphaseBZ=0.0, tslice=0.0, tinv=0.0; + + std::cout << GridLogMessage<< "GeneralCoarsenMatrixMrhs (multiRHS fine operator)"<< std::endl; + + GRID_ASSERT(Subspace.size()==nbasis); + GridBase *grid = Subspace[0].Grid(); + + GRID_ASSERT(FineGridMulti->_ndimension == grid->_ndimension+1); + GRID_ASSERT(FineGridMulti->_processors[0] == 1); + for(int d=0;d_ndimension;d++){ + GRID_ASSERT(FineGridMulti->_fdimensions[d+1] == grid->_fdimensions[d]); + GRID_ASSERT(FineGridMulti->_processors [d+1] == grid->_processors [d]); + } + int batch = FineGridMulti->_fdimensions[0]; + + Coordinate blatt,bsimd,bmpi; + CoarsenBlockGridLayout(grid,CoarseGrid,blatt,bsimd,bmpi); + GridCartesian BlockGrid(blatt,bsimd,bmpi); + + BlockComplexField InnerProd(&BlockGrid); + blockOrthogonalise(InnerProd,Subspace); + + MultiRHSBlockProject > Projector; + Projector.Allocate(nbasis,grid,CoarseGrid); + Projector.ImportBasis(Subspace); + + const int npoint = geom_srhs.npoint; + + Eigen::MatrixXcd invMkl; + CoarsenFourierMatrix(CoarseGrid,invMkl); + + FineField phaV(grid); + std::vector phaF(npoint,grid); + std::vector pha (npoint,CoarseGrid); + + tphase=-usecond(); + CoarsenPhases(grid,CoarseGrid,&BlockGrid,pha,phaF); + tphase+=usecond(); + + std::vector _A; + _A.resize(npoint,CoarseGrid); + for(int k=0;k > &linop, + std::vector &Subspace, + GridBase *CoarseGrid, + int batch) + { + RealD tproj=0.0, tmat=0.0, tphase=0.0, tphaseBZ=0.0, tslice=0.0, tinv=0.0; + + std::cout << GridLogMessage<< "GeneralCoarsenMatrixMrhs (single RHS fine operator)"<< std::endl; + + GRID_ASSERT(Subspace.size()==nbasis); + GRID_ASSERT(batch>=1); + GridBase *grid = Subspace[0].Grid(); + + Coordinate blatt,bsimd,bmpi; + CoarsenBlockGridLayout(grid,CoarseGrid,blatt,bsimd,bmpi); + GridCartesian BlockGrid(blatt,bsimd,bmpi); + + BlockComplexField InnerProd(&BlockGrid); + blockOrthogonalise(InnerProd,Subspace); + + MultiRHSBlockProject > Projector; + Projector.Allocate(nbasis,grid,CoarseGrid); + Projector.ImportBasis(Subspace); + + const int npoint = geom_srhs.npoint; + + Eigen::MatrixXcd invMkl; + CoarsenFourierMatrix(CoarseGrid,invMkl); + + FineField phaV(grid); + std::vector phaF(npoint,grid); + std::vector pha (npoint,CoarseGrid); + + tphase=-usecond(); + CoarsenPhases(grid,CoarseGrid,&BlockGrid,pha,phaF); + tphase+=usecond(); + + std::vector _A; + _A.resize(npoint,CoarseGrid); + for(int k=0;k MphaV(batch,grid); + + for(int i0=0;i0M(in,out); + } + void M (const CoarseVector &in, CoarseVector &out) + { + // std::cout << GridLogMessage << "New Mrhs coarse"<ExchangePeriodic(tin); //padded input + t_exch+=usecond(); + + CoarseVector pout(pin.Grid()); + + int npoint = geom.npoint; + typedef calcMatrix* Aview; + typedef LatticeView Vview; + + const int Nsimd = CComplex::Nsimd(); + + int64_t nrhs =pin.Grid()->GlobalDimensions()[0]; + GRID_ASSERT(nrhs>=1); + + RealD flops,bytes; + int64_t osites=in.Grid()->oSites(); // unpadded + int64_t unpadded_vol = CoarseGrid()->lSites()/nrhs; + + flops = 1.0* npoint * nbasis * nbasis * 8.0 * osites * CComplex::Nsimd(); + bytes = 1.0*osites*sizeof(siteMatrix)*npoint/pin.Grid()->GlobalDimensions()[0] + + 2.0*osites*sizeof(siteVector)*npoint; + + + t_GtoB=-usecond(); + GridtoBLAS(pin,BLAS_B); + t_GtoB+=usecond(); + + GridBLAS BLAS; + + t_mult=-usecond(); + for(int p=0;p &out){assert(0);}; +}; + +NAMESPACE_END(Grid); diff --git a/Grid/algorithms/multigrid/MrhsPromotedOperator.h b/Grid/algorithms/multigrid/MrhsPromotedOperator.h new file mode 100644 index 000000000..9906ccc2d --- /dev/null +++ b/Grid/algorithms/multigrid/MrhsPromotedOperator.h @@ -0,0 +1,161 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./lib/algorithms/multigrid/MrhsPromotedOperator.h + + Copyright (C) 2026 + +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 */ +#pragma once + +NAMESPACE_BEGIN(Grid); + +/////////////////////////////////////////////////////////////////////////////// +// Present a D dimensional operator as a D+1 dimensional operator with Nrhs in +// dimension 0. Field type is unchanged; only the Grid differs, so this is a +// LinearOperatorBase and callers need no template parameter: a native mrhs +// operator derives from the same base and substitutes without a call site +// change. +// +// Slices in and out around the wrapped operator. No arithmetic beyond the +// wrapped call, but one ExtractSliceFast/InsertSliceFast pair per right hand +// side: data motion, not work. +// +// AdjOp is carried so that A^dag may be coarsened as a separate coarse +// operator when needed, rather than doubling coarse storage. +/////////////////////////////////////////////////////////////////////////////// +template +class MrhsPromotedOperator : public LinearOperatorBase +{ +private: + + LinearOperatorBase &_LinOp; + GridBase *_LowGrid; + int _Nrhs; + +public: + + MrhsPromotedOperator(LinearOperatorBase &LinOp,GridBase *LowGrid,int Nrhs) + : _LinOp(LinOp), _LowGrid(LowGrid), _Nrhs(Nrhs) + { + GRID_ASSERT(_Nrhs>=1); + } + + GridBase *LowGrid(void) { return _LowGrid; } + int Nrhs(void) { return _Nrhs; } + + // Reset on each call; retrieve and accumulate in the caller + RealD tslice; + RealD top; + + void OpDiag (const Field &in, Field &out) + { + SliceLoop(in,out,[&](Field &i,Field &o){ _LinOp.OpDiag(i,o); }); + } + + void Op (const Field &in, Field &out) + { + SliceLoop(in,out,[&](Field &i,Field &o){ _LinOp.Op(i,o); }); + } + + void AdjOp (const Field &in, Field &out) + { + SliceLoop(in,out,[&](Field &i,Field &o){ _LinOp.AdjOp(i,o); }); + } + + void HermOp (const Field &in, Field &out) + { + SliceLoop(in,out,[&](Field &i,Field &o){ _LinOp.HermOp(i,o); }); + } + + void OpDir (const Field &in, Field &out,int dir,int disp) + { + SliceLoop(in,out,[&](Field &i,Field &o){ _LinOp.OpDir(i,o,dir,disp); }); + } + + ////////////////////////////////////////////////////////////////// + // Norms of the D+1 field are the sums over slices + ////////////////////////////////////////////////////////////////// + void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2) + { + Conformable(in,out); + Field lo_in (_LowGrid); + Field lo_out(_LowGrid); + n1=0.0; + n2=0.0; + for(int r=0;r<_Nrhs;r++){ + RealD r1,r2; + ExtractSliceFast(lo_in,in,r,0); + _LinOp.HermOpAndNorm(lo_in,lo_out,r1,r2); + InsertSliceFast(lo_out,out,r,0); + n1=n1+r1; + n2=n2+r2; + } + } + + void OpDirAll(const Field &in, std::vector &out) + { + int npoint = out.size(); + Field lo_in(_LowGrid); + std::vector lo_out(npoint,_LowGrid); + for(int r=0;r<_Nrhs;r++){ + ExtractSliceFast(lo_in,in,r,0); + _LinOp.OpDirAll(lo_in,lo_out); + for(int p=0;p_ndimension == _LowGrid->_ndimension+1); + GRID_ASSERT(in.Grid()->_fdimensions[0] == _Nrhs); + } + + template + void SliceLoop(const Field &in,Field &out,Kernel K) + { + Conformable(in,out); + Field lo_in (_LowGrid); + Field lo_out(_LowGrid); + tslice=0.0; + top=0.0; + for(int r=0;r<_Nrhs;r++){ + tslice-=usecond(); + ExtractSliceFast(lo_in,in,r,0); + tslice+=usecond(); + top-=usecond(); + K(lo_in,lo_out); + top+=usecond(); + tslice-=usecond(); + InsertSliceFast(lo_out,out,r,0); + tslice+=usecond(); + } + } + +}; + +NAMESPACE_END(Grid);