From e6d4daf194f603870a0fa173c13a77331a905f82 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Fri, 2 Oct 2026 12:00:12 -0400 Subject: [PATCH] Split operators --- Grid/algorithms/Algorithms.h | 1 + Grid/algorithms/LinearOperator.h | 110 ++++++++++++++++++ Grid/algorithms/SparseMatrix.h | 11 ++ .../ConjugateGradientMixedPrecBatched.h | 110 +++++++++++++++++- 4 files changed, 226 insertions(+), 6 deletions(-) diff --git a/Grid/algorithms/Algorithms.h b/Grid/algorithms/Algorithms.h index 6ebb1c58d..fb26692e3 100644 --- a/Grid/algorithms/Algorithms.h +++ b/Grid/algorithms/Algorithms.h @@ -33,6 +33,7 @@ NAMESPACE_CHECK(blas); #include NAMESPACE_CHECK(algorithms); +#include #include #include #include diff --git a/Grid/algorithms/LinearOperator.h b/Grid/algorithms/LinearOperator.h index 986a46d02..e0eacc420 100644 --- a/Grid/algorithms/LinearOperator.h +++ b/Grid/algorithms/LinearOperator.h @@ -52,9 +52,31 @@ public: virtual void AdjOp (const Field &in, Field &out) = 0; // Abstract base virtual void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2)=0; virtual void HermOp(const Field &in, Field &out)=0; + + // As CheckerBoardedSparseMatrixBase::SplitClone; the returned bundle's Linop is the + // same kind of wrapper as this one. Collective. nullptr if not supported. + virtual SplitOperator *SplitClone(const Coordinate &mpi_split) + { + return nullptr; + } + virtual ~LinearOperatorBase(){}; }; +///////////////////////////////////////////////////////////////////////////////////////////// +// The wrappers below are templates on an arbitrary Matrix, and their virtual SplitClone is +// instantiated with the class, so it must compile for Matrix types with no SplitClone. +///////////////////////////////////////////////////////////////////////////////////////////// +template +SplitOperator *SplitCloneMatrix(Matrix &Mat,const Coordinate &mpi_split) +{ + if constexpr ( std::is_base_of,Matrix>::value ) { + return Mat.SplitClone(mpi_split); + } else { + return nullptr; + } +} + ///////////////////////////////////////////////////////////////////////////////////////////// // By sharing the class for Sparse Matrix across multiple operator wrappers, we can share code @@ -102,6 +124,17 @@ public: void HermOp(const Field &in, Field &out){ _Mat.MdagM(in,out); } + + virtual SplitOperator *SplitClone(const Coordinate &mpi_split) + { + SplitOperator *split = SplitCloneMatrix(_Mat,mpi_split); + if ( split == nullptr ) { + return nullptr; + } + split->Linop = new MdagMLinearOperator,Field>(*split->Matrix); + split->FieldGrid = split->Matrix->Grid(); + return split; + } }; template class MMdagLinearOperator : public LinearOperatorBase { @@ -401,6 +434,17 @@ template _Mat.MooeeDag(in,out); axpy(out,-1.0,tmp,out); } + + virtual SplitOperator *SplitClone(const Coordinate &mpi_split) + { + SplitOperator *split = SplitCloneMatrix(_Mat,mpi_split); + if ( split == nullptr ) { + return nullptr; + } + split->Linop = new SchurDiagMooeeOperator,Field>(*split->Matrix); + split->FieldGrid = split->Matrix->RedBlackGrid(); + return split; + } }; template class SchurDiagOneOperator : public SchurOperatorBase { @@ -427,6 +471,17 @@ template _Mat.MeooeDag(out,tmp); axpy(out,-1.0,tmp,in); } + + virtual SplitOperator *SplitClone(const Coordinate &mpi_split) + { + SplitOperator *split = SplitCloneMatrix(_Mat,mpi_split); + if ( split == nullptr ) { + return nullptr; + } + split->Linop = new SchurDiagOneOperator,Field>(*split->Matrix); + split->FieldGrid = split->Matrix->RedBlackGrid(); + return split; + } }; template class SchurDiagTwoOperator : public SchurOperatorBase { @@ -455,6 +510,17 @@ template axpy(out,-1.0,tmp,in); } + + virtual SplitOperator *SplitClone(const Coordinate &mpi_split) + { + SplitOperator *split = SplitCloneMatrix(_Mat,mpi_split); + if ( split == nullptr ) { + return nullptr; + } + split->Linop = new SchurDiagTwoOperator,Field>(*split->Matrix); + split->FieldGrid = split->Matrix->RedBlackGrid(); + return split; + } }; template @@ -522,6 +588,17 @@ class NonHermitianSchurDiagMooeeOperator : public NonHermitianSchurOperatorBase axpy(out, -1.0, tmp, out); } + + virtual SplitOperator *SplitClone(const Coordinate &mpi_split) + { + SplitOperator *split = SplitCloneMatrix(_Mat,mpi_split); + if ( split == nullptr ) { + return nullptr; + } + split->Linop = new NonHermitianSchurDiagMooeeOperator,Field>(*split->Matrix); + split->FieldGrid = split->Matrix->RedBlackGrid(); + return split; + } }; template @@ -552,6 +629,17 @@ class NonHermitianSchurDiagOneOperator : public NonHermitianSchurOperatorBase *SplitClone(const Coordinate &mpi_split) + { + SplitOperator *split = SplitCloneMatrix(_Mat,mpi_split); + if ( split == nullptr ) { + return nullptr; + } + split->Linop = new NonHermitianSchurDiagOneOperator,Field>(*split->Matrix); + split->FieldGrid = split->Matrix->RedBlackGrid(); + return split; + } }; template @@ -583,6 +671,17 @@ class NonHermitianSchurDiagTwoOperator : public NonHermitianSchurOperatorBase *SplitClone(const Coordinate &mpi_split) + { + SplitOperator *split = SplitCloneMatrix(_Mat,mpi_split); + if ( split == nullptr ) { + return nullptr; + } + split->Linop = new NonHermitianSchurDiagTwoOperator,Field>(*split->Matrix); + split->FieldGrid = split->Matrix->RedBlackGrid(); + return split; + } }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -636,6 +735,17 @@ class SchurStaggeredOperator : public SchurOperatorBase { virtual void MpcDagMpc(const Field &in, Field &out) { GRID_ASSERT(0);// Never need with staggered } + + virtual SplitOperator *SplitClone(const Coordinate &mpi_split) + { + SplitOperator *split = SplitCloneMatrix(_Mat,mpi_split); + if ( split == nullptr ) { + return nullptr; + } + split->Linop = new SchurStaggeredOperator,Field>(*split->Matrix); + split->FieldGrid = split->Matrix->RedBlackGrid(); + return split; + } }; template using SchurStagOperator = SchurStaggeredOperator; diff --git a/Grid/algorithms/SparseMatrix.h b/Grid/algorithms/SparseMatrix.h index 2922b3e38..ccf750b04 100644 --- a/Grid/algorithms/SparseMatrix.h +++ b/Grid/algorithms/SparseMatrix.h @@ -78,6 +78,17 @@ public: virtual void MeooeDag (const Field &in, Field &out)=0; virtual void MooeeDag (const Field &in, Field &out)=0; virtual void MooeeInvDag (const Field &in, Field &out)=0; + + ////////////////////////////////////////////////////////////////////// + // Copy of this operator on grids whose communicator is split into + // partitions of MPI layout mpi_split. Collective over the full + // communicator. nullptr if this operator cannot be cloned. + ////////////////////////////////////////////////////////////////////// + virtual SplitOperator *SplitClone(const Coordinate &mpi_split) + { + return nullptr; + } + virtual ~CheckerBoardedSparseMatrixBase() {}; }; diff --git a/Grid/algorithms/iterative/ConjugateGradientMixedPrecBatched.h b/Grid/algorithms/iterative/ConjugateGradientMixedPrecBatched.h index 32df03020..27d901e2c 100644 --- a/Grid/algorithms/iterative/ConjugateGradientMixedPrecBatched.h +++ b/Grid/algorithms/iterative/ConjugateGradientMixedPrecBatched.h @@ -51,6 +51,12 @@ public: LinearFunction *guesser; bool updateResidual; + // Inner solves on independent partitions of the communicator (--batched-solver-split). + // BatchedSplit is the partition MPI layout, empty for no split; BatchedSplitNode selects + // one partition per node. Default from the command line; callers may override. + Coordinate BatchedSplit; + bool BatchedSplitNode; + MixedPrecisionConjugateGradientBatched(RealD tol, Integer maxinnerit, Integer maxouterit, @@ -61,7 +67,8 @@ public: bool _updateResidual=true) : Linop_f(_Linop_f), Linop_d(_Linop_d), Tolerance(tol), InnerTolerance(tol), MaxInnerIterations(maxinnerit), MaxOuterIterations(maxouterit), MaxPatchupIterations(maxpatchit), SinglePrecGrid(_sp_grid), - OuterLoopNormMult(100.), guesser(NULL), updateResidual(_updateResidual) { }; + OuterLoopNormMult(100.), guesser(NULL), updateResidual(_updateResidual), + BatchedSplit(GridDefaultBatchedSolverSplit()), BatchedSplitNode(GridDefaultBatchedSolverSplitNode()) { }; void useGuesser(LinearFunction &g){ guesser = &g; @@ -130,6 +137,20 @@ public: ConjugateGradient CG_f(inner_tol, MaxInnerIterations); CG_f.ErrorOnNoConverge = false; + //Optionally clone the single precision operator onto split-communicator partitions + GridStopWatch SplitTimer; + SplitOperator *split = nullptr; + int partitions; + Coordinate layout = BatchedSolverSplitLayout(SinglePrecGrid,BatchedSplit,BatchedSplitNode,NBatch,partitions); + if ( partitions > 1 ) { + SplitTimer.Start(); + split = Linop_f.SplitClone(layout); + SplitTimer.Stop(); + if ( split == nullptr ) { + std::cout << GridLogMessage << "MixedPrecisionConjugateGradientBatched: operator cannot be split; serial inner solves" << std::endl; + } + } + Integer &outer_iter = TotalOuterIterations; //so it will be equal to the final iteration count for(outer_iter = 0; outer_iter < MaxOuterIterations; outer_iter++){ @@ -169,12 +190,18 @@ public: (*guesser)(src_f, sol_f); } + if ( split != nullptr ) { + InnerSplitSolves(*split, CG_f, src_f, sol_f, TotalInnerIterations, InnerCGtimer, SplitTimer); + } + for (int i=0; i &split, + ConjugateGradient &CG_f, + std::vector &src_f, + std::vector &sol_f, + std::vector &TotalInnerIterations, + GridStopWatch &InnerCGtimer, + GridStopWatch &SplitTimer) + { + int NBatch = src_f.size(); + int P = split.Partitions; + int cb = src_f[0].Checkerboard(); + + FieldF s_src(split.FieldGrid); + FieldF s_sol(split.FieldGrid); + std::vector group_src(P,SinglePrecGrid); + std::vector group_sol(P,SinglePrecGrid); + std::vector iters(P); + + for(int g=0;gThisRank() == 0 ) { + iters[split.Partition] = CG_f.IterationsToComplete; + } + SinglePrecGrid->GlobalSumVector(&iters[0],P); + + for(int p=0;p