Compare commits

..
20 Commits
Author SHA1 Message Date
Peter Boyle 3232b438f2 Merge branch 'feature/splitGridCGBatch' into develop 2026-10-05 11:47:36 -04:00
Peter Boyle f7c25052f4 Make any split operators have lifetime the lifetime of the BatchedCG object 2026-10-05 11:34:17 -04:00
Peter Boyle 38c6fa78b2 Split updates 2026-10-03 10:17:15 -04:00
Peter Boyle 259936a3ed Memor footprint monitor 2026-10-02 23:34:50 -04:00
Peter Boyle 7a9cdb45bc Profiling split grid 2026-10-02 23:34:06 -04:00
Peter Boyle 44f24f395e TIming info. Will need to revert in future. 2026-10-02 19:46:15 -04:00
Peter Boyle 964f4c1271 Force propagation convenience 2026-10-02 12:06:39 -04:00
Peter Boyle af9d829336 Split testing 2026-10-02 12:06:23 -04:00
Peter Boyle b07ccf3d5e Force test reorg 2026-10-02 12:02:54 -04:00
Peter Boyle 260d7d2600 Be able to split operators for split CG 2026-10-02 12:01:38 -04:00
Peter Boyle 809249caa9 Latent bug fix, never hit in existing code 2026-10-02 12:01:01 -04:00
Peter Boyle 94f529bd63 Split operators 2026-10-02 12:00:37 -04:00
Peter Boyle e6d4daf194 Split operators 2026-10-02 12:00:12 -04:00
Peter Boyle 402fa0aace Complete the derivative 2026-10-02 11:58:53 -04:00
Peter Boyle d31061cd49 Batched split grid 2026-10-02 11:57:27 -04:00
Peter Boyle 3aa03d3b3f Overlap commms compute 2026-09-30 14:36:57 -04:00
Peter Boyle 87baaf1d37 Updated workaround 2026-09-29 14:58:18 -04:00
Peter Boyle 45c1a525b9 Useful script and keep the mixed precision job 2026-09-29 13:47:38 -04:00
Peter Boyle 3a3a20b8c9 Updates 2026-09-29 13:46:09 -04:00
Peter Boyle 3bd5e8883f Synch multigrid rewrite to repository and
broad unicode elimination effort
2026-09-29 12:36:34 -04:00
116 changed files with 5946 additions and 1441 deletions

No files matched your search

+2 -2
View File
@@ -106,7 +106,7 @@ Tests and benchmarks that need optional fermion representations are guarded by `
### GPU acceleration and the view/memory-manager discipline ### GPU acceleration and the view/memory-manager discipline
### Multigrid (`Grid/algorithms/multigrid/`) ### Multigrid (`Grid/algorithms/multigrid/`)
Aggregation-based algebraic multigrid for Wilson-type fermions. Key files: `GeneralCoarsenedMatrixMultiRHSV2.h` (the multi-RHS coarse operator, "V2": coarsening by Fourier probing, batched-GEMM apply on a D+1 grid with rhs innermost and unvectorised), `MultiRHSBlockProject.h` (in `deflation/`; the batched-GEMM transfer operators), `PVdagMMultiGrid.h` (three-level non-Hermitian PVdagM chain with dense bottom) and `HDCGMultiGrid.h` (two-level Hermitian HDCG chain) with their `*Params.h` (XML-serialisable parameters), `MrhsMultiGrid.h` (mrhs V-cycle, preconditioner interface, fp64/fp32 seam), `Smoothers.h`, `MultiGridIO.h`, `Aggregates.h` (near-null vector construction), `Geometry.h` (coarse stencils: max-norm-1 boxes of Manhattan radius 1, 2 or 4), `CoarsenedMatrix.h` (the original nearest-neighbour Wilson multigrid). `deprecated/` holds the V1 coarse operators and the Aggregation-based single-RHS ADEF-2, kept only for the pre-2026 drivers. `MultiGrid.h` is the top-level include. Aggregation-based algebraic multigrid for Wilson-type fermions. Key files: `GeneralCoarsenedMatrixMultiRHS.h` (`MultiGeneralCoarsenedOperator`, the coarse operator for any number of right-hand sides: coarsening by Fourier probing, batched-GEMM apply on a D+1 grid with rhs innermost and unvectorised), `MultiRHSBlockProject.h` (in `deflation/`; the batched-GEMM transfer operators), `PVdagMMultiGrid.h` (three-level non-Hermitian PVdagM chain with dense bottom) and `HDCGMultiGrid.h` (two-level Hermitian HDCG chain) with their `*Params.h` (XML-serialisable parameters), `MrhsMultiGrid.h` (mrhs V-cycle, preconditioner interface, fp64/fp32 seam), `Smoothers.h`, `MultiGridIO.h`, `Aggregates.h` (near-null vector construction), `Geometry.h` (coarse stencils: max-norm-1 boxes of Manhattan radius 1, 2 or 4), `CoarsenedMatrix.h` (the original nearest-neighbour Wilson multigrid). `deprecated/` holds the superseded coarse operators and the Aggregation-based single-RHS ADEF-2, with class names prefixed `Deprecated` (`DeprecatedGeneralCoarsenedMatrix`, `DeprecatedMultiGeneralCoarsenedMatrix`, `DeprecatedTwoLevelADEF2`); nothing in the library uses them, only older drivers in `examples/` and `tests/debug/`. `MultiGrid.h` is the top-level include.
### GPU acceleration ### GPU acceleration
@@ -174,7 +174,7 @@ Every programme is wrapped in `Grid_init(&argc, &argv)` / `Grid_finalize()` (`Gr
- Template structure: most classes are templated on `<_FImpl>` (fermion impl) or `<Gimpl>` (gauge impl), which encode the representation and precision. Instantiation is controlled by `--enable-fermion-instantiations`. - Template structure: most classes are templated on `<_FImpl>` (fermion impl) or `<Gimpl>` (gauge impl), which encode the representation and precision. Instantiation is controlled by `--enable-fermion-instantiations`.
- **Tensor indices are positional, not labelled.** The `Grid/tensors/` arithmetic recurses structurally over the `iScalar`/`iVector`/`iMatrix` nest: each level defines only the {scalar,vector,matrix}² products at its own level, with element types resolved by automatic type deduction, so every colour/spin/lorentz combination composes from ~200 lines (versus the pre-C++11 QDP++/PETE approach of machine-generating every case). An index's meaning derives entirely from its nesting depth counted from the outside; `iScalar` is the identity/broadcast case at every level. Never insert or remove a nesting level casually — the multiplication tables contract by position. - **Tensor indices are positional, not labelled.** The `Grid/tensors/` arithmetic recurses structurally over the `iScalar`/`iVector`/`iMatrix` nest: each level defines only the {scalar,vector,matrix}² products at its own level, with element types resolved by automatic type deduction, so every colour/spin/lorentz combination composes from ~200 lines (versus the pre-C++11 QDP++/PETE approach of machine-generating every case). An index's meaning derives entirely from its nesting depth counted from the outside; `iScalar` is the identity/broadcast case at every level. Never insert or remove a nesting level casually — the multiplication tables contract by position.
- **Multigrid coarsening deepens the tensor nest by one level.** A coarse site vector is `iVector<CComplex,nbasis>`, and `innerProduct` on it returns `iScalar<CComplex>` — one level deeper than the fine block scalar. So the block-inner-product scalar type gains one `iScalar` wrapper per MG level (fine: `vTComplex`; level 2: `iScalar<vTComplex>`; see `examples/Example_pvdagm_3level.cc`). When calling `blockInnerProduct`/`blockZAXPY`/`blockOrthogonalise` on coarse fields, the coarse scalar type must match `decltype(innerProduct(siteVector(),siteVector()))` exactly; a wrong depth fails to compile (no viable `operator=` deep in the instantiation chain) rather than mis-contracting. - **Multigrid coarsening deepens the tensor nest by one level.** A coarse site vector is `iVector<CComplex,nbasis>`, and `innerProduct` on it returns `iScalar<CComplex>` — one level deeper than the fine block scalar. So the block-inner-product scalar type gains one `iScalar` wrapper per MG level (fine: `vTComplex`; level 2: `iScalar<vTComplex>`; see `examples/Example_pvdagm_3level.cc`). When calling `blockInnerProduct`/`blockZAXPY`/`blockOrthogonalise` on coarse fields, the coarse scalar type must match `decltype(innerProduct(siteVector(),siteVector()))` exactly; a wrong depth fails to compile (no viable `operator=` deep in the instantiation chain) rather than mis-contracting.
- **Grids are borrowed, never owned.** `conformable` is pointer identity, so every object that interoperates must hold the *same* `GridCartesian *`; a class that minted its own grid internally could never conform with anything else. Ownership is therefore not available, and lifetime is managed by scope discipline instead of reference counting: whoever creates a grid retains it beyond every object it handed a reference to. Anything *derived* from a grid inherits this — `~PaddedCell` dereferences its `unpadded_grid`, so a `PaddedCell` cannot even be **destroyed** after its parent grid, only used. Where a consumer must let go early, it offers an explicit hand-back (`MultiGeneralCoarsenedOperatorV2::ReleaseGrid()`) to be called *before* the grid is destroyed. - **Grids are borrowed, never owned.** `conformable` is pointer identity, so every object that interoperates must hold the *same* `GridCartesian *`; a class that minted its own grid internally could never conform with anything else. Ownership is therefore not available, and lifetime is managed by scope discipline instead of reference counting: whoever creates a grid retains it beyond every object it handed a reference to. Anything *derived* from a grid inherits this — `~PaddedCell` dereferences its `unpadded_grid`, so a `PaddedCell` cannot even be **destroyed** after its parent grid, only used. Where a consumer must let go early, it offers an explicit hand-back (`MultiGeneralCoarsenedOperator::ReleaseGrid()`) to be called *before* the grid is destroyed.
- The `RealD`/`RealF`/`ComplexD`/`ComplexF` typedefs are used everywhere; avoid raw `double`/`float`. - The `RealD`/`RealF`/`ComplexD`/`ComplexF` typedefs are used everywhere; avoid raw `double`/`float`.
- Use `GRID_ASSERT(cond)` (defined in `Grid/GridStd.h`), not bare `assert` — it prints a Grid-formatted message and aborts cleanly under MPI. - Use `GRID_ASSERT(cond)` (defined in `Grid/GridStd.h`), not bare `assert` — it prints a Grid-formatted message and aborts cleanly under MPI.
+1
View File
@@ -56,6 +56,7 @@ Author: paboyle <paboyle@ph.ed.ac.uk>
#include <Grid/communicator/Communicator.h> #include <Grid/communicator/Communicator.h>
#include <Grid/communicator/RingAllReduce.h> #include <Grid/communicator/RingAllReduce.h>
#include <Grid/cartesian/Cartesian.h> #include <Grid/cartesian/Cartesian.h>
#include <Grid/perfmon/HostMemory.h>
#include <Grid/tensors/Tensors.h> #include <Grid/tensors/Tensors.h>
#include <Grid/lattice/Lattice.h> #include <Grid/lattice/Lattice.h>
#include <Grid/cshift/Cshift.h> #include <Grid/cshift/Cshift.h>
+1
View File
@@ -33,6 +33,7 @@ NAMESPACE_CHECK(blas);
#include <Grid/algorithms/blas/BatchedBlas.h> #include <Grid/algorithms/blas/BatchedBlas.h>
NAMESPACE_CHECK(algorithms); NAMESPACE_CHECK(algorithms);
#include <Grid/algorithms/SplitOperator.h>
#include <Grid/algorithms/SparseMatrix.h> #include <Grid/algorithms/SparseMatrix.h>
#include <Grid/algorithms/LinearOperator.h> #include <Grid/algorithms/LinearOperator.h>
#include <Grid/algorithms/Preconditioner.h> #include <Grid/algorithms/Preconditioner.h>
+113
View File
@@ -52,9 +52,31 @@ public:
virtual void AdjOp (const Field &in, Field &out) = 0; // Abstract base 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 HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2)=0;
virtual void HermOp(const Field &in, Field &out)=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<Field> *SplitClone(const Coordinate &mpi_split)
{
return nullptr;
}
virtual ~LinearOperatorBase(){}; 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<class Field,class Matrix>
SplitOperator<Field> *SplitCloneMatrix(Matrix &Mat,const Coordinate &mpi_split)
{
if constexpr ( std::is_base_of<CheckerBoardedSparseMatrixBase<Field>,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 // 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){ void HermOp(const Field &in, Field &out){
_Mat.MdagM(in,out); _Mat.MdagM(in,out);
} }
virtual SplitOperator<Field> *SplitClone(const Coordinate &mpi_split)
{
SplitOperator<Field> *split = SplitCloneMatrix<Field>(_Mat,mpi_split);
if ( split == nullptr ) {
return nullptr;
}
split->Linop = new MdagMLinearOperator<CheckerBoardedSparseMatrixBase<Field>,Field>(*split->Matrix);
split->FieldGrid = split->Matrix->Grid();
return split;
}
}; };
template<class Matrix,class Field> template<class Matrix,class Field>
class MMdagLinearOperator : public LinearOperatorBase<Field> { class MMdagLinearOperator : public LinearOperatorBase<Field> {
@@ -401,6 +434,17 @@ template<class Matrix,class Field>
_Mat.MooeeDag(in,out); _Mat.MooeeDag(in,out);
axpy(out,-1.0,tmp,out); axpy(out,-1.0,tmp,out);
} }
virtual SplitOperator<Field> *SplitClone(const Coordinate &mpi_split)
{
SplitOperator<Field> *split = SplitCloneMatrix<Field>(_Mat,mpi_split);
if ( split == nullptr ) {
return nullptr;
}
split->Linop = new SchurDiagMooeeOperator<CheckerBoardedSparseMatrixBase<Field>,Field>(*split->Matrix);
split->FieldGrid = split->Matrix->RedBlackGrid();
return split;
}
}; };
template<class Matrix,class Field> template<class Matrix,class Field>
class SchurDiagOneOperator : public SchurOperatorBase<Field> { class SchurDiagOneOperator : public SchurOperatorBase<Field> {
@@ -427,6 +471,17 @@ template<class Matrix,class Field>
_Mat.MeooeDag(out,tmp); _Mat.MeooeDag(out,tmp);
axpy(out,-1.0,tmp,in); axpy(out,-1.0,tmp,in);
} }
virtual SplitOperator<Field> *SplitClone(const Coordinate &mpi_split)
{
SplitOperator<Field> *split = SplitCloneMatrix<Field>(_Mat,mpi_split);
if ( split == nullptr ) {
return nullptr;
}
split->Linop = new SchurDiagOneOperator<CheckerBoardedSparseMatrixBase<Field>,Field>(*split->Matrix);
split->FieldGrid = split->Matrix->RedBlackGrid();
return split;
}
}; };
template<class Matrix,class Field> template<class Matrix,class Field>
class SchurDiagTwoOperator : public SchurOperatorBase<Field> { class SchurDiagTwoOperator : public SchurOperatorBase<Field> {
@@ -455,6 +510,17 @@ template<class Matrix,class Field>
axpy(out,-1.0,tmp,in); axpy(out,-1.0,tmp,in);
} }
virtual SplitOperator<Field> *SplitClone(const Coordinate &mpi_split)
{
SplitOperator<Field> *split = SplitCloneMatrix<Field>(_Mat,mpi_split);
if ( split == nullptr ) {
return nullptr;
}
split->Linop = new SchurDiagTwoOperator<CheckerBoardedSparseMatrixBase<Field>,Field>(*split->Matrix);
split->FieldGrid = split->Matrix->RedBlackGrid();
return split;
}
}; };
template<class Field> template<class Field>
@@ -522,6 +588,17 @@ class NonHermitianSchurDiagMooeeOperator : public NonHermitianSchurOperatorBase
axpy(out, -1.0, tmp, out); axpy(out, -1.0, tmp, out);
} }
virtual SplitOperator<Field> *SplitClone(const Coordinate &mpi_split)
{
SplitOperator<Field> *split = SplitCloneMatrix<Field>(_Mat,mpi_split);
if ( split == nullptr ) {
return nullptr;
}
split->Linop = new NonHermitianSchurDiagMooeeOperator<CheckerBoardedSparseMatrixBase<Field>,Field>(*split->Matrix);
split->FieldGrid = split->Matrix->RedBlackGrid();
return split;
}
}; };
template<class Matrix,class Field> template<class Matrix,class Field>
@@ -552,6 +629,17 @@ class NonHermitianSchurDiagOneOperator : public NonHermitianSchurOperatorBase<Fi
axpy(out, -1.0, tmp, in); axpy(out, -1.0, tmp, in);
} }
virtual SplitOperator<Field> *SplitClone(const Coordinate &mpi_split)
{
SplitOperator<Field> *split = SplitCloneMatrix<Field>(_Mat,mpi_split);
if ( split == nullptr ) {
return nullptr;
}
split->Linop = new NonHermitianSchurDiagOneOperator<CheckerBoardedSparseMatrixBase<Field>,Field>(*split->Matrix);
split->FieldGrid = split->Matrix->RedBlackGrid();
return split;
}
}; };
template<class Matrix, class Field> template<class Matrix, class Field>
@@ -583,6 +671,17 @@ class NonHermitianSchurDiagTwoOperator : public NonHermitianSchurOperatorBase<Fi
axpy(out, -1.0, tmp, in); axpy(out, -1.0, tmp, in);
} }
virtual SplitOperator<Field> *SplitClone(const Coordinate &mpi_split)
{
SplitOperator<Field> *split = SplitCloneMatrix<Field>(_Mat,mpi_split);
if ( split == nullptr ) {
return nullptr;
}
split->Linop = new NonHermitianSchurDiagTwoOperator<CheckerBoardedSparseMatrixBase<Field>,Field>(*split->Matrix);
split->FieldGrid = split->Matrix->RedBlackGrid();
return split;
}
}; };
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -636,6 +735,17 @@ class SchurStaggeredOperator : public SchurOperatorBase<Field> {
virtual void MpcDagMpc(const Field &in, Field &out) { virtual void MpcDagMpc(const Field &in, Field &out) {
GRID_ASSERT(0);// Never need with staggered GRID_ASSERT(0);// Never need with staggered
} }
virtual SplitOperator<Field> *SplitClone(const Coordinate &mpi_split)
{
SplitOperator<Field> *split = SplitCloneMatrix<Field>(_Mat,mpi_split);
if ( split == nullptr ) {
return nullptr;
}
split->Linop = new SchurStaggeredOperator<CheckerBoardedSparseMatrixBase<Field>,Field>(*split->Matrix);
split->FieldGrid = split->Matrix->RedBlackGrid();
return split;
}
}; };
template<class Matrix,class Field> using SchurStagOperator = SchurStaggeredOperator<Matrix,Field>; template<class Matrix,class Field> using SchurStagOperator = SchurStaggeredOperator<Matrix,Field>;
@@ -667,6 +777,9 @@ public:
(*this)(in[i], out[i]); (*this)(in[i], out[i]);
} }
} }
// The identity, if a derived class says so. A caller can then use its input
// where it would otherwise have materialised the output, and skip the copy.
virtual int isTrivial(void) { return 0; };
virtual ~LinearFunction(){}; virtual ~LinearFunction(){};
}; };
+1
View File
@@ -45,6 +45,7 @@ public:
virtual void operator()(const Field &src, Field & psi){ virtual void operator()(const Field &src, Field & psi){
psi = src; psi = src;
} }
virtual int isTrivial(void) { return 1; };
TrivialPrecon(void){}; TrivialPrecon(void){};
}; };
+11
View File
@@ -78,6 +78,17 @@ public:
virtual void MeooeDag (const Field &in, Field &out)=0; virtual void MeooeDag (const Field &in, Field &out)=0;
virtual void MooeeDag (const Field &in, Field &out)=0; virtual void MooeeDag (const Field &in, Field &out)=0;
virtual void MooeeInvDag (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<Field> *SplitClone(const Coordinate &mpi_split)
{
return nullptr;
}
virtual ~CheckerBoardedSparseMatrixBase() {}; virtual ~CheckerBoardedSparseMatrixBase() {};
}; };
+172
View File
@@ -0,0 +1,172 @@
/*************************************************************************************
Grid physics library, www.github.com/paboyle/Grid
Source file: ./lib/algorithms/SplitOperator.h
Copyright (C) 2026
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
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);
template<class Field> class LinearOperatorBase;
template<class Field> class CheckerBoardedSparseMatrixBase;
/////////////////////////////////////////////////////////////////////////////////////////////
// A copy of an operator living on grids whose communicator is split into independent
// partitions, together with those grids. Produced by SplitClone().
//
// Owns everything it points to. Destruction is the reverse of creation: linear operator,
// matrix, then grids, so no object outlives a grid it references. For 4d operators the
// fermion grids are the gauge grids and are deleted once.
/////////////////////////////////////////////////////////////////////////////////////////////
template<class Field>
class SplitOperator
{
public:
GridCartesian *GaugeGrid = nullptr;
GridRedBlackCartesian *GaugeRBGrid = nullptr;
GridCartesian *FermionGrid = nullptr;
GridRedBlackCartesian *FermionRBGrid = nullptr;
int Partition = 0; // Grid_split vector index held by this rank's partition
int Partitions = 1; // number of partitions
CheckerBoardedSparseMatrixBase<Field> *Matrix = nullptr;
LinearOperatorBase<Field> *Linop = nullptr;
GridBase *FieldGrid = nullptr; // grid of Linop's fields
SplitOperator(void) {};
SplitOperator(const SplitOperator &) = delete;
SplitOperator &operator=(const SplitOperator &) = delete;
~SplitOperator(void)
{
delete Linop;
delete Matrix;
if ( FermionRBGrid != GaugeRBGrid ) {
delete FermionRBGrid;
}
if ( FermionGrid != GaugeGrid ) {
delete FermionGrid;
}
delete GaugeRBGrid;
delete GaugeGrid;
}
};
/////////////////////////////////////////////////////////////////////////////////////////////
// Index of the vector that Grid_split(std::vector<Field> full, Field split) delivers to this
// rank's partition. Grid_split orders partitions lexicographically with the first
// dimension fastest; the split communicator's own rank (srank) uses the reversed MPI
// convention, so the two differ whenever more than one dimension is split.
/////////////////////////////////////////////////////////////////////////////////////////////
inline int GridSplitVectorIndex(GridBase *full,GridBase *split)
{
int nd = full->_ndimension;
GRID_ASSERT(split->_ndimension == nd);
Coordinate scoor(nd);
Coordinate ssize(nd);
for(int d=0;d<nd;d++){
scoor[d] = full->ThisProcessorCoor()[d] / split->ProcessorGrid()[d];
ssize[d] = full->ProcessorGrid()[d] / split->ProcessorGrid()[d];
}
int index;
Lexicographic::IndexFromCoor(scoor,index,ssize);
return index;
}
/////////////////////////////////////////////////////////////////////////////////////////////
// Partition MPI layout for a batched solve on grid, from a request as given on the command
// line (--batched-solver-split). Uses the trailing dimensions of grid's processor and shm
// layouts, so it works for 4d and 5d grids. Returns grid's own processor layout (one
// partition) when no split is requested. Asserts divisibility; warns when partitions
// straddle nodes or when node boundaries are not visible.
/////////////////////////////////////////////////////////////////////////////////////////////
inline Coordinate BatchedSolverSplitLayout(GridBase *grid,
const Coordinate &request,
bool node,
int &partitions)
{
int nd = GridDefaultMpi().size();
int pad = grid->_ndimension - nd;
GRID_ASSERT(pad >= 0);
GRID_ASSERT(grid->ShmGrid().size() == grid->_ndimension);
Coordinate processors(nd);
Coordinate shm(nd);
for(int d=0;d<nd;d++){
processors[d] = grid->ProcessorGrid()[pad+d];
shm[d] = grid->ShmGrid()[pad+d];
}
Coordinate split(nd);
if ( node ) {
split = shm;
} else if ( request.size() == 0 ) {
split = processors;
} else {
GRID_ASSERT(request.size() == nd);
split = request;
}
partitions = 1;
for(int d=0;d<nd;d++){
GRID_ASSERT( (processors[d] % split[d]) == 0 );
partitions *= processors[d] / split[d];
}
if ( partitions == 1 ) {
return split;
}
std::cout << GridLogMessage << "BatchedSolverSplit: partition layout " << split
<< " of " << processors << " : " << partitions << " partitions" << std::endl;
int inside_node = 1;
int whole_nodes = 1;
int shm_trivial = 1;
for(int d=0;d<nd;d++){
if ( (shm[d] % split[d]) != 0 ) {
inside_node = 0;
}
if ( (split[d] % shm[d]) != 0 ) {
whole_nodes = 0;
}
if ( shm[d] != 1 ) {
shm_trivial = 0;
}
}
if ( shm_trivial ) {
std::cout << GridLogWarning << "BatchedSolverSplit: shm layout is trivial (one rank per node,"
<< " or shared memory disabled); node locality of partitions not checked" << std::endl;
} else if ( !inside_node && !whole_nodes ) {
std::cout << GridLogWarning << "BatchedSolverSplit: partitions " << split
<< " straddle node boundaries (node layout " << shm << "); inner solves will communicate off node" << std::endl;
}
return split;
}
NAMESPACE_END(Grid);
@@ -51,6 +51,20 @@ public:
LinearFunction<FieldF> *guesser; LinearFunction<FieldF> *guesser;
bool updateResidual; 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. Fixed at construction, default from the command line.
const Coordinate BatchedSplit;
const bool BatchedSplitNode;
// Copy of Linop_f on the partitions, made once at construction and owned; nullptr when
// there is no split or the operator cannot be split. It holds the gauge field as it was at
// construction: a solver must not outlive a change of the operator's gauge field. The
// outer defect correction uses Linop_d, so a stale copy would cost convergence, not
// correctness.
SplitOperator<FieldF> *split = nullptr;
int Partitions = 1;
MixedPrecisionConjugateGradientBatched(RealD tol, MixedPrecisionConjugateGradientBatched(RealD tol,
Integer maxinnerit, Integer maxinnerit,
Integer maxouterit, Integer maxouterit,
@@ -58,10 +72,37 @@ public:
GridBase* _sp_grid, GridBase* _sp_grid,
LinearOperatorBase<FieldF> &_Linop_f, LinearOperatorBase<FieldF> &_Linop_f,
LinearOperatorBase<FieldD> &_Linop_d, LinearOperatorBase<FieldD> &_Linop_d,
bool _updateResidual=true) : bool _updateResidual=true,
const Coordinate &_split=GridDefaultBatchedSolverSplit(),
bool _split_node=GridDefaultBatchedSolverSplitNode()) :
Linop_f(_Linop_f), Linop_d(_Linop_d), Linop_f(_Linop_f), Linop_d(_Linop_d),
Tolerance(tol), InnerTolerance(tol), MaxInnerIterations(maxinnerit), MaxOuterIterations(maxouterit), MaxPatchupIterations(maxpatchit), SinglePrecGrid(_sp_grid), 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(_split), BatchedSplitNode(_split_node)
{
Coordinate layout = BatchedSolverSplitLayout(SinglePrecGrid,BatchedSplit,BatchedSplitNode,Partitions);
if ( Partitions > 1 ) {
double t0 = usecond();
split = Linop_f.SplitClone(layout);
double t1 = usecond();
if ( split == nullptr ) {
std::cout << GridLogMessage << "MixedPrecisionConjugateGradientBatched: operator cannot be split; serial inner solves" << std::endl;
} else {
std::cout << GridLogMessage << "MixedPrecisionConjugateGradientBatched: split clone " << (t1-t0)/1.0e6 << " s" << std::endl;
HostMemoryReport(SinglePrecGrid,GridLogMessage,"MixedPrecisionConjugateGradientBatched: after split clone");
}
}
};
// Owns split
MixedPrecisionConjugateGradientBatched(const MixedPrecisionConjugateGradientBatched &) = delete;
MixedPrecisionConjugateGradientBatched &operator=(const MixedPrecisionConjugateGradientBatched &) = delete;
~MixedPrecisionConjugateGradientBatched(void)
{
delete split;
}
void useGuesser(LinearFunction<FieldF> &g){ void useGuesser(LinearFunction<FieldF> &g){
guesser = &g; guesser = &g;
@@ -91,6 +132,8 @@ public:
GridStopWatch InnerCGtimer; GridStopWatch InnerCGtimer;
GridStopWatch PrecChangeTimer; GridStopWatch PrecChangeTimer;
GridStopWatch OuterResidualTimer;
GridStopWatch PatchupTimer;
int cb = src_d_in[0].Checkerboard(); int cb = src_d_in[0].Checkerboard();
@@ -130,18 +173,27 @@ public:
ConjugateGradient<FieldF> CG_f(inner_tol, MaxInnerIterations); ConjugateGradient<FieldF> CG_f(inner_tol, MaxInnerIterations);
CG_f.ErrorOnNoConverge = false; CG_f.ErrorOnNoConverge = false;
SplitTimers splitTimers;
if ( split != nullptr && split->Partitions > NBatch ) {
std::cout << GridLogWarning << "MixedPrecisionConjugateGradientBatched: " << split->Partitions << " partitions for "
<< NBatch << " right-hand sides; the extra partitions only solve zero padding" << std::endl;
}
Integer &outer_iter = TotalOuterIterations; //so it will be equal to the final iteration count Integer &outer_iter = TotalOuterIterations; //so it will be equal to the final iteration count
for(outer_iter = 0; outer_iter < MaxOuterIterations; outer_iter++){ for(outer_iter = 0; outer_iter < MaxOuterIterations; outer_iter++){
std::cout << GridLogMessage << std::endl; std::cout << GridLogMessage << std::endl;
std::cout << GridLogMessage << "Outer iteration " << outer_iter << std::endl; std::cout << GridLogMessage << "Outer iteration " << outer_iter << std::endl;
HostMemoryReport(DoublePrecGrid,GridLogMessage,"MixedPrecisionConjugateGradientBatched: outer iteration "+std::to_string(outer_iter));
bool allConverged = true; bool allConverged = true;
for (int i=0; i<NBatch; i++) { for (int i=0; i<NBatch; i++) {
//Compute double precision rsd and also new RHS vector. //Compute double precision rsd and also new RHS vector.
OuterResidualTimer.Start();
Linop_d.HermOp(sol_d[i], tmp_d); Linop_d.HermOp(sol_d[i], tmp_d);
norm[i] = axpy_norm(src_d[i], -1., tmp_d, src_d_in[i]); //src_d is residual vector norm[i] = axpy_norm(src_d[i], -1., tmp_d, src_d_in[i]); //src_d is residual vector
OuterResidualTimer.Stop();
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: Outer iteration " << outer_iter <<" solve " << i << " residual "<< norm[i] << " target "<< stop[i] <<std::endl; std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: Outer iteration " << outer_iter <<" solve " << i << " residual "<< norm[i] << " target "<< stop[i] <<std::endl;
@@ -169,12 +221,18 @@ public:
(*guesser)(src_f, sol_f); (*guesser)(src_f, sol_f);
} }
if ( split != nullptr ) {
InnerSplitSolves(*split, CG_f, src_f, sol_f, TotalInnerIterations, InnerCGtimer, splitTimers);
}
for (int i=0; i<NBatch; i++) { for (int i=0; i<NBatch; i++) {
//Inner CG //Inner CG
InnerCGtimer.Start(); if ( split == nullptr ) {
CG_f(Linop_f, src_f[i], sol_f[i]); InnerCGtimer.Start();
InnerCGtimer.Stop(); CG_f(Linop_f, src_f[i], sol_f[i]);
TotalInnerIterations[i] += CG_f.IterationsToComplete; InnerCGtimer.Stop();
TotalInnerIterations[i] += CG_f.IterationsToComplete;
}
//Convert sol back to double and add to double prec solution //Convert sol back to double and add to double prec solution
PrecChangeTimer.Start(); PrecChangeTimer.Start();
@@ -185,16 +243,18 @@ public:
} }
} }
//Final trial CG //Final trial CG
std::cout << GridLogMessage << std::endl; std::cout << GridLogMessage << std::endl;
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: Starting final patch-up double-precision solve"<<std::endl; std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: Starting final patch-up double-precision solve"<<std::endl;
PatchupTimer.Start();
for (int i=0; i<NBatch; i++) { for (int i=0; i<NBatch; i++) {
ConjugateGradient<FieldD> CG_d(Tolerance, MaxPatchupIterations); ConjugateGradient<FieldD> CG_d(Tolerance, MaxPatchupIterations);
CG_d(Linop_d, src_d_in[i], sol_d[i]); CG_d(Linop_d, src_d_in[i], sol_d[i]);
TotalFinalStepIterations[i] += CG_d.IterationsToComplete; TotalFinalStepIterations[i] += CG_d.IterationsToComplete;
} }
PatchupTimer.Stop();
TotalTimer.Stop(); TotalTimer.Stop();
@@ -204,8 +264,118 @@ public:
} }
std::cout << GridLogMessage << std::endl; std::cout << GridLogMessage << std::endl;
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: Total time " << TotalTimer.Elapsed() << " Precision change " << PrecChangeTimer.Elapsed() << " Inner CG total " << InnerCGtimer.Elapsed() << std::endl; std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: Total time " << TotalTimer.Elapsed() << " Precision change " << PrecChangeTimer.Elapsed() << " Inner CG total " << InnerCGtimer.Elapsed() << std::endl;
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: Outer residual " << OuterResidualTimer.Elapsed() << " Patch-up " << PatchupTimer.Elapsed() << std::endl;
if ( split != nullptr ) {
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: Grid_split " << splitTimers.Split.Elapsed() << " (" << splitTimers.SplitCalls << " calls)"
<< " Grid_unsplit " << splitTimers.Unsplit.Elapsed() << " (" << splitTimers.UnsplitCalls << " calls)"
<< " Staging copies " << splitTimers.Staging.Elapsed()
<< " Host to device " << splitTimers.H2D.Elapsed()
<< " Iteration count sum " << splitTimers.Reduce.Elapsed() << std::endl;
}
double accounted = PrecChangeTimer.useconds() + InnerCGtimer.useconds() + OuterResidualTimer.useconds()
+ PatchupTimer.useconds() + splitTimers.Split.useconds()
+ splitTimers.Unsplit.useconds() + splitTimers.Staging.useconds() + splitTimers.H2D.useconds()
+ splitTimers.Reduce.useconds();
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: Unaccounted " << (TotalTimer.useconds()-accounted)/1.0e6 << " s" << std::endl;
} }
private:
// Coarse breakdown of the split path outside the inner CG
struct SplitTimers {
GridStopWatch Split;
GridStopWatch Unsplit;
GridStopWatch Staging;
GridStopWatch H2D;
GridStopWatch Reduce;
int SplitCalls = 0;
int UnsplitCalls = 0;
};
////////////////////////////////////////////////////////////////////////////////////////
// One inner solve per partition, in groups of Partitions right-hand sides. The last
// group is zero-padded; a zero source returns at once from CG. Collective.
////////////////////////////////////////////////////////////////////////////////////////
void InnerSplitSolves(SplitOperator<FieldF> &split,
ConjugateGradient<FieldF> &CG_f,
std::vector<FieldF> &src_f,
std::vector<FieldF> &sol_f,
std::vector<Integer> &TotalInnerIterations,
GridStopWatch &InnerCGtimer,
SplitTimers &timers)
{
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<FieldF> group_src(P,SinglePrecGrid);
std::vector<FieldF> group_sol(P,SinglePrecGrid);
std::vector<uint64_t> iters(P);
for(int g=0;g<NBatch;g+=P){
// Gather the group; zero-pad past the end of the batch
timers.Staging.Start();
for(int p=0;p<P;p++){
group_src[p].Checkerboard() = cb;
group_sol[p].Checkerboard() = cb;
if ( g+p < NBatch ) {
group_src[p] = src_f[g+p];
group_sol[p] = sol_f[g+p];
} else {
group_src[p] = Zero();
group_sol[p] = Zero();
}
}
timers.Staging.Stop();
// The initial guess (e.g. from the guesser) travels with the source
timers.Split.Start();
Grid_split(group_src,s_src);
Grid_split(group_sol,s_sol);
timers.Split.Stop();
timers.SplitCalls += 2;
// Grid_split leaves its output on the host; move it now so the copy is not in the CG time
timers.H2D.Start();
{
autoView(s_src_v, s_src, AcceleratorRead);
autoView(s_sol_v, s_sol, AcceleratorRead);
}
timers.H2D.Stop();
InnerCGtimer.Start();
CG_f(*split.Linop,s_src,s_sol);
InnerCGtimer.Stop();
timers.Unsplit.Start();
Grid_unsplit(group_sol,s_sol);
timers.Unsplit.Stop();
timers.UnsplitCalls += 1;
// One iteration count per partition, contributed by the partition's rank 0 only
timers.Reduce.Start();
for(int p=0;p<P;p++){
iters[p] = 0;
}
if ( split.FieldGrid->ThisRank() == 0 ) {
iters[split.Partition] = CG_f.IterationsToComplete;
}
SinglePrecGrid->GlobalSumVector(&iters[0],P);
timers.Reduce.Stop();
// Includes the host to device copy of group_sol left by Grid_unsplit
timers.Staging.Start();
for(int p=0;p<P && g+p<NBatch;p++){
sol_f[g+p] = group_sol[p];
TotalInnerIterations[g+p] += iters[p];
}
timers.Staging.Stop();
}
}
}; };
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
@@ -1,6 +1,6 @@
/************************************************************************************* /*************************************************************************************
Grid physics library, www.github.com/paboyle/Grid Grid physics library, www.github.com/paboyle/Grid
Source file: ./lib/algorithms/iterative/PrecGeneralisedConjugateResidual.h Source file: ./lib/algorithms/iterative/PrecGeneralisedConjugateResidual.h
@@ -28,213 +28,48 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
/* END LEGAL */ /* END LEGAL */
#ifndef GRID_PREC_GCR_H #ifndef GRID_PREC_GCR_H
#define GRID_PREC_GCR_H #define GRID_PREC_GCR_H
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
///////////////////////////////////////////////////////////////////////////////////////////////////////
//VPGCR Abe and Zhang, 2005.
//INTERNATIONAL JOURNAL OF NUMERICAL ANALYSIS AND MODELING
//Computing and Information Volume 2, Number 2, Pages 147-161
//NB. Likely not original reference since they are focussing on a preconditioner variant.
// but VPGCR was nicely written up in their paper
///////////////////////////////////////////////////////////////////////////////////////////////////////
NAMESPACE_BEGIN(Grid); NAMESPACE_BEGIN(Grid);
#define GCRLogLevel std::cout << GridLogMessage <<std::string(level,'\t')<< " Level "<<level<<" " ///////////////////////////////////////////////////////////////////////////////////////////////////////
// Hermitian spelling of PrecGeneralisedConjugateResidualNonHermitian.
//
// The algorithm is the same; only the operator entry point differs. The solver
// drives Linop.Op(), while a caller of this class means "invert HermOp", so the
// operator is wrapped in HermOpAdaptor. The wrap is what makes the two classes
// one: for an operator whose Op() already IS its HermOp() (HermitianLinearOperator,
// Gamma5R5HermitianLinearOperator, HermOpAdaptor) it changes nothing, and for one
// where they differ (MdagMLinearOperator: Op()=M, HermOp()=MdagM) it preserves the
// Hermitian meaning the caller asked for.
//
// A non-zero initial guess is respected. Callers that guarantee x0 = 0 can call
// SetZeroGuess(1) to skip the first r0 = src - A x0 apply.
///////////////////////////////////////////////////////////////////////////////////////////////////////
template<class Field> template<class Field>
class PrecGeneralisedConjugateResidual : public LinearFunction<Field> { class PrecGeneralisedConjugateResidual : public LinearFunction<Field> {
public: // Declaration order is initialisation order: the adaptor is complete before
// the solver binds its reference to it.
HermOpAdaptor<Field> HermLinop;
PrecGeneralisedConjugateResidualNonHermitian<Field> GCR;
public:
using LinearFunction<Field>::operator(); using LinearFunction<Field>::operator();
RealD Tolerance;
Integer MaxIterations;
int verbose;
int mmax;
int nstep;
int steps;
int level;
GridStopWatch PrecTimer;
GridStopWatch MatTimer;
GridStopWatch LinalgTimer;
LinearFunction<Field> &Preconditioner; PrecGeneralisedConjugateResidual(RealD tol,Integer maxit,LinearOperatorBase<Field> &Linop,
LinearOperatorBase<Field> &Linop; LinearFunction<Field> &Prec,int mmax,int nstep)
: HermLinop(Linop),
GCR(tol,maxit,HermLinop,Prec,mmax,nstep) {};
void Level(int lv) { level=lv; }; void operator() (const Field &src, Field &psi) { GCR(src,psi); };
PrecGeneralisedConjugateResidual(RealD tol,Integer maxit,LinearOperatorBase<Field> &_Linop,LinearFunction<Field> &Prec,int _mmax,int _nstep) : void Name(std::string n) { GCR.Name(n); };
Tolerance(tol), void Level(int n) { GCR.Level(n); };
MaxIterations(maxit), void SetZeroGuess(int z) { GCR.SetZeroGuess(z); };
Linop(_Linop), int Steps(void) const { return GCR.Steps(); };
Preconditioner(Prec), void LogCoefficients(int l) { GCR.LogCoefficients(l); };
mmax(_mmax), void SetCoefficientRecorder(GCRCoefficients *r){ GCR.SetCoefficientRecorder(r); };
nstep(_nstep) void ReleaseHistory(void) { GCR.ReleaseHistory(); };
{
level=1;
verbose=1;
};
void operator() (const Field &src, Field &psi){
psi=Zero();
RealD cp, ssq,rsq;
ssq=norm2(src);
rsq=Tolerance*Tolerance*ssq;
Field r(src.Grid());
PrecTimer.Reset();
MatTimer.Reset();
LinalgTimer.Reset();
GridStopWatch SolverTimer;
SolverTimer.Start();
steps=0;
for(int k=0;k<MaxIterations;k++){
cp=GCRnStep(src,psi,rsq);
GCRLogLevel <<"PGCR("<<mmax<<","<<nstep<<") "<< steps <<" steps cp = "<<cp<<" target "<<rsq <<std::endl;
if(cp<rsq) {
SolverTimer.Stop();
Linop.HermOp(psi,r);
axpy(r,-1.0,src,r);
RealD tr = norm2(r);
GCRLogLevel<<"PGCR: Converged on iteration " <<steps
<< " computed residual "<<sqrt(cp/ssq)
<< " true residual " <<sqrt(tr/ssq)
<< " target " <<Tolerance <<std::endl;
GCRLogLevel<<"PGCR Time elapsed: Total "<< SolverTimer.Elapsed() <<std::endl;
/*
GCRLogLevel<<"PGCR Time elapsed: Precon "<< PrecTimer.Elapsed() <<std::endl;
GCRLogLevel<<"PGCR Time elapsed: Matrix "<< MatTimer.Elapsed() <<std::endl;
GCRLogLevel<<"PGCR Time elapsed: Linalg "<< LinalgTimer.Elapsed() <<std::endl;
*/
return;
}
}
GCRLogLevel<<"Variable Preconditioned GCR did not converge"<<std::endl;
// GRID_ASSERT(0);
}
RealD GCRnStep(const Field &src, Field &psi,RealD rsq){
RealD cp;
RealD a, b;
RealD zAz, zAAz;
RealD rq;
GridBase *grid = src.Grid();
Field r(grid);
Field z(grid);
Field tmp(grid);
Field ttmp(grid);
Field Az(grid);
////////////////////////////////
// history for flexible orthog
////////////////////////////////
std::vector<Field> q(mmax,grid);
std::vector<Field> p(mmax,grid);
std::vector<RealD> qq(mmax);
GCRLogLevel<< "PGCR nStep("<<nstep<<")"<<std::endl;
//////////////////////////////////
// initial guess x0 is taken as nonzero.
// r0=src-A x0 = src
//////////////////////////////////
MatTimer.Start();
Linop.HermOpAndNorm(psi,Az,zAz,zAAz);
MatTimer.Stop();
LinalgTimer.Start();
r=src-Az;
LinalgTimer.Stop();
GCRLogLevel<< "PGCR true residual r = src - A psi "<<norm2(r) <<std::endl;
/////////////////////
// p = Prec(r)
/////////////////////
PrecTimer.Start();
Preconditioner(r,z);
PrecTimer.Stop();
MatTimer.Start();
Linop.HermOpAndNorm(z,Az,zAz,zAAz);
MatTimer.Stop();
LinalgTimer.Start();
//p[0],q[0],qq[0]
p[0]= z;
q[0]= Az;
qq[0]= zAAz;
cp =norm2(r);
LinalgTimer.Stop();
for(int k=0;k<nstep;k++){
steps++;
int kp = k+1;
int peri_k = k %mmax;
int peri_kp= kp%mmax;
LinalgTimer.Start();
rq= real(innerProduct(r,q[peri_k])); // what if rAr not real?
a = rq/qq[peri_k];
axpy(psi,a,p[peri_k],psi);
cp = axpy_norm(r,-a,q[peri_k],r);
LinalgTimer.Stop();
GCRLogLevel<< "PGCR step["<<steps<<"] resid " << cp << " target " <<rsq<<std::endl;
if((k==nstep-1)||(cp<rsq)){
return cp;
}
PrecTimer.Start();
Preconditioner(r,z);// solve Az = r
PrecTimer.Stop();
MatTimer.Start();
Linop.HermOpAndNorm(z,Az,zAz,zAAz);
MatTimer.Stop();
LinalgTimer.Start();
q[peri_kp]=Az;
p[peri_kp]=z;
int northog = ((kp)>(mmax-1))?(mmax-1):(kp); // if more than mmax done, we orthog all mmax history.
for(int back=0;back<northog;back++){
int peri_back=(k-back)%mmax; GRID_ASSERT((k-back)>=0);
b=-real(innerProduct(q[peri_back],Az))/qq[peri_back];
p[peri_kp]=p[peri_kp]+b*p[peri_back];
q[peri_kp]=q[peri_kp]+b*q[peri_back];
}
qq[peri_kp]=norm2(q[peri_kp]); // could use axpy_norm
LinalgTimer.Stop();
}
GRID_ASSERT(0); // never reached
return cp;
}
}; };
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
#undef GCRLogLevel
#endif #endif
@@ -43,8 +43,51 @@ NAMESPACE_BEGIN(Grid);
template<class Field> template<class Field>
class PrecGeneralisedConjugateResidualNonHermitian : public LinearFunction<Field> { class PrecGeneralisedConjugateResidualNonHermitian : public LinearFunction<Field> {
public: public:
using LinearFunction<Field>::operator(); using LinearFunction<Field>::operator();
PrecGeneralisedConjugateResidualNonHermitian(RealD tol,Integer maxit,LinearOperatorBase<Field> &_Linop,LinearFunction<Field> &Prec,int _mmax,int _nstep) :
Tolerance(tol),
MaxIterations(maxit),
mmax(_mmax),
nstep(_nstep),
Preconditioner(Prec),
Linop(_Linop)
{
Level(1);
verbose=1;
trivial_prec = Preconditioner.isTrivial();
};
// The name is also the trace range's, so a profile separates the four
// instances (Fouter, Fsmoother, Couter, Csmoother) that otherwise nest
// indistinguishably as one shared range.
void Name(std::string _name) { name = _name; trace_step = name + " PGCR_step"; };
void Level(int n) { Name("Level " + std::to_string(n)); level = n; }
void SetZeroGuess(int z) { ZeroGuess = z; };
// Steps taken by the last solve.
int Steps(void) const { return steps; };
// Coefficient logging: one line per step with the step length a_k and the
// orthogonalisation coefficients b_{k,j}. These are the data from which a
// FIXED polynomial smoother can be harvested: if they are stable from call
// to call, the adaptive GCR can be replaced by a stationary p(A) with the
// same applies and no reductions. Off by default; boss rank prints.
void LogCoefficients(int l) { LogCoeffs = l; };
// Optional recorder of the per-step coefficients (means over calls), for
// replay by GCRReplaySmoother (Smoothers.h). Records only; no effect on
// the iteration.
void SetCoefficientRecorder(GCRCoefficients *r) { Recorder = r; if(r) r->mmax = mmax; };
// Free the persistent history (e.g. when this solver is replaced by a
// replayed polynomial): re-made on the next call if ever needed again.
void ReleaseHistory(void) { q.clear(); p.clear(); qq.clear(); hist_grid = nullptr; };
private:
RealD Tolerance; RealD Tolerance;
RealD SSQ; RealD SSQ;
Integer MaxIterations; Integer MaxIterations;
@@ -65,45 +108,14 @@ public:
std::vector<Field> p; std::vector<Field> p;
std::vector<RealD> qq; std::vector<RealD> qq;
int FirstCycle = 0; int FirstCycle = 0;
int LogCoeffs = 0;
int trivial_prec; // Preconditioner is the identity: p == r, copies fold away
GCRCoefficients *Recorder = nullptr;
LinearFunction<Field> &Preconditioner; LinearFunction<Field> &Preconditioner;
LinearOperatorBase<Field> &Linop; LinearOperatorBase<Field> &Linop;
// The name is also the trace range's, so a profile separates the four public:
// instances (Fouter, Fsmoother, Couter, Csmoother) that otherwise nest
// indistinguishably as one shared range.
void Name(std::string _name) { name = _name; trace_step = name + " PGCR_step"; };
void Level(int n) { Name("Level " + std::to_string(n)); level = n; }
void SetZeroGuess(int z) { ZeroGuess = z; };
// Coefficient logging: one line per step with the step length a_k and the
// orthogonalisation coefficients b_{k,j}. These are the data from which a
// FIXED polynomial smoother can be harvested: if they are stable from call
// to call, the adaptive GCR can be replaced by a stationary p(A) with the
// same applies and no reductions. Off by default; boss rank prints.
int LogCoeffs = 0;
void LogCoefficients(int l) { LogCoeffs = l; };
// Optional recorder of the per-step coefficients (means over calls), for
// replay by GCRReplaySmoother (Smoothers.h). Records only; no effect on
// the iteration.
GCRCoefficients *Recorder = nullptr;
void SetCoefficientRecorder(GCRCoefficients *r) { Recorder = r; if(r) r->mmax = mmax; };
// Free the persistent history (e.g. when this solver is replaced by a
// replayed polynomial): re-made on the next call if ever needed again.
void ReleaseHistory(void) { q.clear(); p.clear(); qq.clear(); hist_grid = nullptr; };
PrecGeneralisedConjugateResidualNonHermitian(RealD tol,Integer maxit,LinearOperatorBase<Field> &_Linop,LinearFunction<Field> &Prec,int _mmax,int _nstep) :
Tolerance(tol),
MaxIterations(maxit),
Linop(_Linop),
Preconditioner(Prec),
mmax(_mmax),
nstep(_nstep)
{
Level(1);
verbose=1;
};
void operator() (const Field &src, Field &psi){ void operator() (const Field &src, Field &psi){
@@ -151,6 +163,7 @@ public:
// GRID_ASSERT(0); // GRID_ASSERT(0);
} }
private:
RealD GCRnStep(const Field &src, Field &psi,RealD rsq){ RealD GCRnStep(const Field &src, Field &psi,RealD rsq){
RealD cp; RealD cp;
@@ -189,18 +202,20 @@ public:
// contract (enforced here), so r0 = src exactly; skip the apply. // contract (enforced here), so r0 = src exactly; skip the apply.
// Restart cycles (psi!=0) always do the full computation. // Restart cycles (psi!=0) always do the full computation.
////////////////////////////////// //////////////////////////////////
if (ZeroGuess && FirstCycle) { // The residual is READ through rp and only materialised in r when it is
psi = Zero(); // first written, at the k=0 update. On a zero-guess first cycle r0 IS src,
LinalgTimer.Start(); // so no copy is made; a restart cycle computes r and rp points at it.
r = src; const Field *rp = &src;
LinalgTimer.Stop(); GRID_ASSERT(nstep>=1); // psi is first written at k=0; see zero_start below
} else { int zero_start = (ZeroGuess && FirstCycle);
if ( !zero_start ) {
MatTimer.Start(); MatTimer.Start();
Linop.Op(psi,Az); Linop.Op(psi,Az);
MatTimer.Stop(); MatTimer.Stop();
LinalgTimer.Start(); LinalgTimer.Start();
r=src-Az; r=src-Az;
LinalgTimer.Stop(); LinalgTimer.Stop();
rp = &r;
} }
FirstCycle=0; FirstCycle=0;
@@ -208,9 +223,11 @@ public:
// p = Prec(r) // p = Prec(r)
///////////////////// /////////////////////
// p[0] = Prec(r), q[0] = A p[0], written straight into the history slots // p[0] = Prec(r), q[0] = A p[0], written straight into the history slots.
// This copy is forced even for a trivial preconditioner: p[0] is history and
// must survive while the residual evolves.
PrecTimer.Start(); PrecTimer.Start();
Preconditioner(r,p[0]); Preconditioner(*rp,p[0]);
PrecTimer.Stop(); PrecTimer.Stop();
MatTimer.Start(); MatTimer.Start();
@@ -221,7 +238,7 @@ public:
qq[0]= norm2(q[0]); qq[0]= norm2(q[0]);
cp =norm2(r); cp =norm2(*rp);
LinalgTimer.Stop(); LinalgTimer.Stop();
GCRLogLevel<< "PGCR true residual "<< sqrt(cp/SSQ) <<std::endl; GCRLogLevel<< "PGCR true residual "<< sqrt(cp/SSQ) <<std::endl;
@@ -234,13 +251,32 @@ public:
int peri_kp= kp%mmax; int peri_kp= kp%mmax;
LinalgTimer.Start(); LinalgTimer.Start();
rq= innerProduct(q[peri_k],r); // what if rAr not real? rq= innerProduct(q[peri_k],*rp); // what if rAr not real?
a = rq/qq[peri_k]; a = rq/qq[peri_k];
if ( Recorder ) Recorder->RecordA(k,a); if ( Recorder ) Recorder->RecordA(k,a);
axpy(psi,a,p[peri_k],psi); // On a zero-guess first step psi is still unwritten, and the update would
// be a*p added to zero: assign it instead, so psi is never zeroed.
if ( zero_start && (k==0) ) psi = a*p[peri_k];
else axpy(psi,a,p[peri_k],psi);
cp = axpy_norm(r,-a,q[peri_k],r); #ifdef GRID_GCR_RESIDUAL_RECURRENCE
// |r - a q|^2 = |r|^2 - |<q,r>|^2/|q|^2 with a = <q,r>/|q|^2, so the new
// residual norm follows from scalars already in hand and the reduction in
// axpy_norm is not needed. cp then DRIFTS rather than being measured; the
// true residual is recomputed at restart and at convergence, which bounds
// the exposure. The drift only matters for an instance driven to a tight
// tolerance -- the outer solver -- and there it is the same computed-vs-true
// residual gap that CG lives with; the smoother and coarse instances run at
// fixed work or loose tolerance and are indifferent to it.
// Explicit re/im: ComplexD is thrust::complex under HIP.
axpy(r,-a,q[peri_k],*rp);
cp = cp - (real(rq)*real(rq)+imag(rq)*imag(rq))/qq[peri_k];
if ( cp < 0.0 ) cp = 0.0; // drift can undershoot; keeps the log finite
#else
cp = axpy_norm(r,-a,q[peri_k],*rp);
#endif
rp = &r; // the residual now lives in r; src is untouched from here on
LinalgTimer.Stop(); LinalgTimer.Stop();
if ( LogCoeffs ) { if ( LogCoeffs ) {
GCRLogLevel<<"coeff["<<k<<"] a=("<<real(a)<<","<<imag(a)<<")" GCRLogLevel<<"coeff["<<k<<"] a=("<<real(a)<<","<<imag(a)<<")"
@@ -254,13 +290,21 @@ public:
} }
// New direction written straight into its history slot: p = Prec(r), q = A p. // New direction written straight into its history slot: p = Prec(r), q = A p.
PrecTimer.Start(); // For a trivial preconditioner p[kp] would only be a copy of the residual,
Preconditioner(r,p[peri_kp]); // so the operator is applied to the residual directly and the copy folds
PrecTimer.Stop(); // into the orthogonalisation pass below.
if ( trivial_prec ) {
MatTimer.Start(); MatTimer.Start();
Linop.Op(p[peri_kp],q[peri_kp]); Linop.Op(*rp,q[peri_kp]);
MatTimer.Stop(); MatTimer.Stop();
} else {
PrecTimer.Start();
Preconditioner(*rp,p[peri_kp]);
PrecTimer.Stop();
MatTimer.Start();
Linop.Op(p[peri_kp],q[peri_kp]);
MatTimer.Stop();
}
LinalgTimer.Start(); LinalgTimer.Start();
@@ -289,16 +333,19 @@ public:
if ( LogCoeffs ) bs<<" b["<<back<<"]="<<bcoef[back]; if ( LogCoeffs ) bs<<" b["<<back<<"]="<<bcoef[back];
} }
if ( Recorder && northog ) Recorder->RecordB(k,bcoef); if ( Recorder && northog ) Recorder->RecordB(k,bcoef);
// axpyMultiNorm returns |q|^2 from the update pass, so qq is complete here:
// a separate norm2 would be a second reduction and a second GlobalSum.
if ( northog ) { if ( northog ) {
axpyMulti(p[peri_kp],bcoef,pwin); if ( trivial_prec ) axpyMultiFrom(p[peri_kp],*rp,bcoef,pwin);
qq[peri_kp]=axpyMultiNorm(q[peri_kp],bcoef,qwin); else axpyMulti(p[peri_kp],bcoef,pwin);
qq[peri_kp]=axpyMultiNorm(q[peri_kp],bcoef,qwin);
} else { } else {
qq[peri_kp]=norm2(q[peri_kp]); if ( trivial_prec ) p[peri_kp] = *rp; // no pass to fold the copy into
qq[peri_kp]=norm2(q[peri_kp]);
} }
if ( LogCoeffs && northog ) { if ( LogCoeffs && northog ) {
GCRLogLevel<<"coeff["<<k<<"]"<<bs.str()<<std::endl; GCRLogLevel<<"coeff["<<k<<"]"<<bs.str()<<std::endl;
} }
qq[peri_kp]=norm2(q[peri_kp]); // could use axpy_norm
LinalgTimer.Stop(); LinalgTimer.Stop();
} }
GRID_ASSERT(0); // never reached GRID_ASSERT(0); // never reached
+1 -2
View File
@@ -39,8 +39,7 @@ typedef ComplexD DenseInverseScalar;
// BlockCyclicLayout: the index arithmetic of a 2D block-cyclic distribution // BlockCyclicLayout: the index arithmetic of a 2D block-cyclic distribution
// of an N x N matrix over a Pr x Pc logical process grid with block size nb. // of an N x N matrix over a Pr x Pc logical process grid with block size nb.
// //
// This is stage 1 of the 2D distributed dense inverse // This is stage 1 of the 2D distributed dense inverse. It is deliberately
// (documentation/DistributedDenseInverse2D.tex). It is deliberately
// COMMUNICATOR-FREE: every mapping is a static pure function of // COMMUNICATOR-FREE: every mapping is a static pure function of
// (N, nb, Pr, Pc), so the whole layout is exhaustively unit-testable on one // (N, nb, Pr, Pc), so the whole layout is exhaustively unit-testable on one
// rank with no MPI in the loop (Test_blockcyclic). A thin instance layer // rank with no MPI in the loop (Test_blockcyclic). A thin instance layer
@@ -273,11 +273,15 @@ public:
static void RowsToCyclic(GridBase *grid, const std::vector<int64_t> &rowStart, static void RowsToCyclic(GridBase *grid, const std::vector<int64_t> &rowStart,
DenseInverseScalar *rows1d, int64_t myrows, BlockCyclicMatrix &A) DenseInverseScalar *rows1d, int64_t myrows, BlockCyclicMatrix &A)
{ Redistribute(+1, grid, rowStart, rows1d, myrows, A); } {
Redistribute(+1, grid, rowStart, rows1d, myrows, A);
}
static void CyclicToRows(GridBase *grid, const std::vector<int64_t> &rowStart, static void CyclicToRows(GridBase *grid, const std::vector<int64_t> &rowStart,
BlockCyclicMatrix &A, DenseInverseScalar *rows1d, int64_t myrows) BlockCyclicMatrix &A, DenseInverseScalar *rows1d, int64_t myrows)
{ Redistribute(-1, grid, rowStart, rows1d, myrows, A); } {
Redistribute(-1, grid, rowStart, rows1d, myrows, A);
}
}; };
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
@@ -215,7 +215,11 @@ public:
// SendToRecvFrom with root (symmetric byte count: the reverse direction // SendToRecvFrom with root (symmetric byte count: the reverse direction
// carries a same-size dummy -- a leaf-local cost, accepted for simplicity). // carries a same-size dummy -- a leaf-local cost, accepted for simplicity).
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
static int64_t FirstBlock(int64_t b0, int p, int Pg){ int64_t r = ((b0 % Pg) <= p) ? b0 - (b0 % Pg) + p : b0 - (b0 % Pg) + Pg + p; return r; } static int64_t FirstBlock(int64_t b0, int p, int Pg)
{
int64_t r = ((b0 % Pg) <= p) ? b0 - (b0 % Pg) + p : b0 - (b0 % Pg) + Pg + p;
return r;
}
void BigLeaf(BlockCyclicMatrix &A, int64_t b0, int64_t b1) void BigLeaf(BlockCyclicMatrix &A, int64_t b0, int64_t b1)
{ {
GRID_TRACE("SchurBigLeaf"); GRID_TRACE("SchurBigLeaf");
+48 -13
View File
@@ -38,7 +38,7 @@ NAMESPACE_BEGIN(Grid);
////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////
// DenseCoarseMatrix: a coarsened operator treated as a DENSE matrix -- explicit, // DenseCoarseMatrix: a coarsened operator treated as a DENSE matrix -- explicit,
// row-distributed A^{-1} of a GeneralCoarsenedMatrix. // row-distributed A^{-1} of a coarsened operator.
// //
// - Stencil -> dense DIRECT IMPORT. The coarse operator IS the dense matrix // - Stencil -> dense DIRECT IMPORT. The coarse operator IS the dense matrix
// unrolled: Dense[(s,a),(s+shift_p,b)] += A[p][s]_{a,b}. Rows of my sites are // unrolled: Dense[(s,a),(s+shift_p,b)] += A[p][s]_{a,b}. Rows of my sites are
@@ -176,6 +176,7 @@ public:
{ {
double t0 = usecond(); double t0 = usecond();
{ {
CheckMrhsSlices(Op); // the batched apply keeps its slices apart
ImportDense(Op); // slab <- my rows of A (LOCAL, no comms) ImportDense(Op); // slab <- my rows of A (LOCAL, no comms)
ImportCertificate(Op); // dense apply == Op.M, before inversion ImportCertificate(Op); // dense apply == Op.M, before inversion
InvertDense(Op); // slab <- my rows of A^{-1} InvertDense(Op); // slab <- my rows of A^{-1}
@@ -274,32 +275,67 @@ public:
GRID_ASSERT(mgrid->_ndimension == nd+1); GRID_ASSERT(mgrid->_ndimension == nd+1);
int nr = mgrid->_fdimensions[0]; int nr = mgrid->_fdimensions[0];
// Every slice carries the same vector; slice 0 is the answer. The
// slices are not a check here -- see CheckMrhsSlices, which does that
// once on a field chosen for the purpose.
Field min(mgrid), mout(mgrid);
for(int r=0;r<nr;r++) InsertSliceFast(in,min,r,0);
Op.M(min,mout);
ExtractSliceFast(out,mout,0,0);
}
////////////////////////////////////////////////////////////////////
// Do the multiRHS slices stay independent? Slice r carries (r+1) times
// one RANDOM field, so linearity says the results scale likewise, and a
// slot reading another slot's data is an O(1) error.
//
// The field is random on purpose. Run on the output of the inverse, as
// this check once was, it measures the cancellation in A applied to
// A^-1 x instead: at the production point that is 1.3e-4 in an fp32
// sector with an fp32 inversion, so the check then fires on any change
// of summation order while saying nothing about slice independence.
// Once per import, not per apply.
////////////////////////////////////////////////////////////////////
template<class CoarseOp>
void CheckMrhsSlices(CoarseOp &Op)
{
if ( Op.Grid() == grid ) return; // single rhs: nothing to mix
GridBase *mgrid = Op.Grid();
GRID_ASSERT(mgrid->_ndimension == nd+1);
int nr = mgrid->_fdimensions[0];
if ( nr < 2 ) return;
Field in(grid);
GridParallelRNG rng(grid); rng.SeedFixedIntegers(std::vector<int>({7,8,9,10}));
random(rng,in);
Field min(mgrid), mout(mgrid); Field min(mgrid), mout(mgrid);
for(int r=0;r<nr;r++){ for(int r=0;r<nr;r++){
Field scaled(grid); Field scaled(grid);
scaled = CoarseScalar(r+1.0,0.0)*in; scaled = CoarseScalar(r+1.0,0.0)*in;
InsertSliceFast(scaled,min,r,0); InsertSliceFast(scaled,min,r,0);
} }
Op.M(min,mout); Op.M(min,mout);
ExtractSliceFast(out,mout,0,0); Field s0(grid);
ExtractSliceFast(s0,mout,0,0);
RealD worst=0.0;
for(int r=1;r<nr;r++){ for(int r=1;r<nr;r++){
Field sr(grid),d(grid); Field sr(grid),d(grid);
ExtractSliceFast(sr,mout,r,0); ExtractSliceFast(sr,mout,r,0);
d = sr - CoarseScalar(r+1.0,0.0)*out; d = sr - CoarseScalar(r+1.0,0.0)*s0;
RealD rel = std::sqrt(norm2(d)/norm2(sr)); RealD rel = std::sqrt(norm2(d)/norm2(sr));
// Slices differ by rounding amplified by the operator's conditioning worst = std::max(worst,rel);
// when the product cancels (A applied to A^-1 x): the tolerance follows
// the coarse precision (fp32 measured ~5e-6 here). A genuine rhs
// mix-up is O(1).
const RealD otol = (sizeof(CoarseScalar)==sizeof(ComplexF)) ? 1.0e-4 : 1.0e-6; const RealD otol = (sizeof(CoarseScalar)==sizeof(ComplexF)) ? 1.0e-4 : 1.0e-6;
if ( rel >= otol ) { if ( rel >= otol ) {
std::cout << GridLogMessage << "DenseCoarseMatrix: oracle rhs "<<r std::cout << GridLogMessage << "DenseCoarseMatrix: mrhs slice "<<r
<<" inconsistent with rhs 0, rel "<<rel<<std::endl; <<" inconsistent with slice 0, rel "<<rel<<std::endl;
} }
GRID_ASSERT( rel < otol ); GRID_ASSERT( rel < otol );
} }
std::cout << GridLogMessage << "DenseCoarseMatrix: mrhs slices independent to "
<< worst << " over " << nr << " right hand sides" << std::endl;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@@ -317,9 +353,8 @@ public:
for(int p=0; p<Op.Geometry().npoint; p++){ for(int p=0; p<Op.Geometry().npoint; p++){
Coordinate shift = Op.Geometry().shifts[p]; Coordinate shift = Op.Geometry().shifts[p];
// _A[p] is PADDED after ExchangeCoarseLinks (end of CoarsenOperator): // ExtractMatrix hands back the unpadded matrix field, which is what the
// extract the unpadded field before peeking with unpadded coordinates // unpadded coordinates below index.
// (exactly as MultiGeneralCoarsenedMatrix::CopyMatrix does).
CoarseMatrix Aun(grid); Op.ExtractMatrix(p,Aun); CoarseMatrix Aun(grid); Op.ExtractMatrix(p,Aun);
autoView(Av, Aun, CpuRead); autoView(Av, Aun, CpuRead);
thread_for(ss, lsites, { thread_for(ss, lsites, {
@@ -34,13 +34,13 @@ NAMESPACE_BEGIN(Grid);
// Fine Object == (per site) type of fine field // Fine Object == (per site) type of fine field
// nbasis == number of deflation vectors // nbasis == number of deflation vectors
template<class Fobj,class CComplex,int nbasis> template<class Fobj,class CComplex,int nbasis>
class MultiGeneralCoarsenedOperatorV2 : public SparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > { class MultiGeneralCoarsenedOperator : public SparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > {
public: public:
typedef typename CComplex::scalar_object SComplex; typedef typename CComplex::scalar_object SComplex;
// The BLAS scalar of this level follows the coefficient precision: // The BLAS scalar of this level follows the coefficient precision:
// ComplexF for an fp32 coarse space, ComplexD for fp64. // ComplexF for an fp32 coarse space, ComplexD for fp64.
typedef typename GridTypeMapper<CComplex>::scalar_type CoarseBLASScalar; typedef typename GridTypeMapper<CComplex>::scalar_type CoarseBLASScalar;
typedef MultiGeneralCoarsenedOperatorV2<Fobj,CComplex,nbasis> MultiGeneralCoarseOp; typedef MultiGeneralCoarsenedOperator<Fobj,CComplex,nbasis> MultiGeneralCoarseOp;
typedef iVector<CComplex,nbasis > siteVector; typedef iVector<CComplex,nbasis > siteVector;
typedef iMatrix<CComplex,nbasis > siteMatrix; typedef iMatrix<CComplex,nbasis > siteMatrix;
@@ -82,7 +82,13 @@ public:
deviceVector<calcVector> BLAS_B; deviceVector<calcVector> BLAS_B;
deviceVector<calcVector> BLAS_C; deviceVector<calcVector> BLAS_C;
std::vector<deviceVector<calcMatrix> > BLAS_A; // The matrix, ONE buffer, site major: a site's npoint blocks are contiguous.
// Both organisations read it through their pointer tables, the folded one a
// site at a time and the unfolded one a point at a time with a stride of the
// whole stencil, so nothing is duplicated. Writing or reading one point
// goes through a single-point scratch.
deviceVector<calcMatrix> BLAS_Amat; // [site][point]
deviceVector<calcMatrix> BLAS_Ascr; // one point, for I/O
std::vector<deviceVector<CoarseBLASScalar *> > BLAS_AP; std::vector<deviceVector<CoarseBLASScalar *> > BLAS_AP;
std::vector<deviceVector<CoarseBLASScalar *> > BLAS_BP; std::vector<deviceVector<CoarseBLASScalar *> > BLAS_BP;
@@ -143,20 +149,106 @@ public:
for(int l=0;l<legs;l++){ for(int l=0;l<legs;l++){
int p = g*LegGroup+l; int p = g*LegGroup+l;
for(int32_t ss=0;ss<sites;ss++){ for(int32_t ss=0;ss<sites;ss++){
CoarseBLASScalar *ptr = (CoarseBLASScalar *)&BLAS_A[p][ss]; CoarseBLASScalar *ptr = (CoarseBLASScalar *)&BLAS_Amat[(uint64_t)ss*geom.npoint+p];
acceleratorPut(BLAS_APg[g][l*sites+ss],ptr); acceleratorPut(BLAS_APg[g][l*sites+ss],ptr);
} }
} }
} }
} }
void SetLegGroup(int G) ///////////////////////////////////////////////////////////////////////////
// Stencil legs folded along the CONTRACTION instead of the batch.
//
// One GEMM per site of [nbasis x (KFold*nbasis)] . [(KFold*nbasis) x nrhs],
// accumulating straight into C, so there are no partial results to combine.
// It needs the site's matrices contiguous, which is a repack done once, and
// the neighbour vectors gathered into one block per site, which replaces the
// copy the ungrouped path already does.
//
// Measured on MI250X at nbasis 60, batch 1024, single precision: the short
// form (K=60) reaches 487 GB/s at one right-hand side and 625 at twelve,
// the folded form (K=1980) 912 and 823. Beyond about K=2000 it collapses:
// K=4860, the full 81 point box, gives 304 GB/s. So the fold is chunked to
// keep K near 2000, and the chunks accumulate.
//
// The gather costs one extra write of nbasis*nrhs per leg per site, which is
// 3% of the traffic at one right-hand side and 20% at twelve; the fold wins
// at both, and by most at one, which is the HMC case.
///////////////////////////////////////////////////////////////////////////
static const int TargetK = 2048;
int KFold = 0; // legs per GEMM; 0 = off
deviceVector<CoarseBLASScalar> BLAS_BK; // [site][rhs][leg][nbasis]
std::vector<deviceVector<CoarseBLASScalar *> > BLAS_AKP; // per chunk, per site
std::vector<deviceVector<CoarseBLASScalar *> > BLAS_BKP; // per chunk, per site
deviceVector<CoarseBLASScalar *> BLAS_BPflat; // [point][site] neighbours
int KChunks(void) const { return (geom.npoint + KFold - 1)/KFold; }
int KLegs(int g) const { return std::min(KFold, geom.npoint - g*KFold); }
///////////////////////////////////////////////////////////////////////////
// Repack the matrix so a site's stencil points are contiguous: the folded
// GEMM reads each site's block as one column-major nbasis x (legs*nbasis)
// matrix, which is also a single sequential stream rather than npoint
// interleaved ones. Costs one pass over the matrix, once per solve setup.
///////////////////////////////////////////////////////////////////////////
void PackFoldedMatrix(void)
{ {
GRID_ASSERT( G>=1 ); // Nothing to pack: the matrix is already site major. Only the pointer
GRID_ASSERT( G<=geom.npoint ); // every partial must get an initialising call // table is built, one entry per site per chunk, at the chunk's first leg.
LegGroup = G; int32_t sites = _CoarseGrid->lSites();
BuildGroupedA(); const int np = geom.npoint;
if ( _CoarseGridMulti ) SetGrid(_CoarseGridMulti); // rebuild the rest BLAS_AKP.resize(KChunks());
for(int g=0;g<KChunks();g++){
BLAS_AKP[g].resize(sites);
for(int32_t ss=0;ss<sites;ss++){
CoarseBLASScalar *ptr = (CoarseBLASScalar *)&BLAS_Amat[(uint64_t)ss*np + (uint64_t)g*KFold];
acceleratorPut(BLAS_AKP[g][ss],ptr);
}
}
}
// Legs per GEMM that keeps K near where the kernel runs best.
int AutoKFold(void) const
{
int K = TargetK/nbasis;
if ( K < 1 ) K = 1;
if ( K > geom.npoint ) K = geom.npoint;
return K;
}
// Legs per call that brings the batch up to where the kernel saturates.
int AutoLegGroup(void) const
{
int32_t sites = _CoarseGrid->lSites();
int G = (TargetBatch + sites - 1)/sites;
if ( G < 1 ) G = 1;
if ( G > geom.npoint ) G = geom.npoint;
return G;
}
///////////////////////////////////////////////////////////////////////////
// The fold along K gathers the neighbours, which costs nbasis*nrhs per leg
// per site: 16 MB of traffic per apply at one right hand side and 195 at
// twelve, against a matrix of 973. Measured at the production point it is
// worth 7.6% at one right hand side and costs 7.6% at twelve, so the fold
// is used only for a small batch, and a large one carries its legs in the
// batch dimension instead.
///////////////////////////////////////////////////////////////////////////
static const int FoldNrhsMax = 4;
void ChooseOrganisation(int nrhs)
{
if ( nrhs <= FoldNrhsMax ) {
KFold = AutoKFold();
} else {
KFold = 0;
}
LegGroup = AutoLegGroup();
if ( KFold ) {
PackFoldedMatrix();
} else {
BuildGroupedA();
}
} }
/////////////////////// ///////////////////////
@@ -171,7 +263,7 @@ public:
{ {
if ( _CoarseGridMulti == nullptr ) { if ( _CoarseGridMulti == nullptr ) {
std::cout << GridLogError std::cout << GridLogError
<< "MultiGeneralCoarsenedOperatorV2: the multiRHS grid has not been set." << "MultiGeneralCoarsenedOperator: the multiRHS grid has not been set."
<< std::endl; << std::endl;
std::cout << GridLogError std::cout << GridLogError
<< " Call SetGrid(CoarseGridMulti) with the D+1 dimensional grid your" << " Call SetGrid(CoarseGridMulti) with the D+1 dimensional grid your"
@@ -184,33 +276,58 @@ public:
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Bilingual accessors, matching GeneralCoarsenedMatrix. Note Grid() is the // Accessors. Note Grid() is the D+1 multiRHS grid, so a consumer wanting
// D+1 multiRHS grid here, so a consumer wanting the space the elements live // the space the matrix elements live on must ask for CoarseGridD().
// on must ask for CoarseGridD().
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Resident device memory this operator holds (matrix elements and the // Resident device memory this operator holds (matrix elements and the
// Nrhs-dependent BLAS buffers). Not evictable. // Nrhs-dependent BLAS buffers). Not evictable.
uint64_t DeviceBytes(void) uint64_t DeviceBytes(void)
{ {
uint64_t b = (uint64_t)(BLAS_B.capacity()+BLAS_C.capacity()+BLAS_Cg.capacity())*sizeof(calcVector); uint64_t b = (uint64_t)(BLAS_B.capacity()+BLAS_C.capacity()+BLAS_Cg.capacity())*sizeof(calcVector)
for(int p=0;p<(int)BLAS_A.size();p++) b += (uint64_t)BLAS_A[p].capacity()*sizeof(calcMatrix); + (uint64_t)BLAS_BK.capacity()*sizeof(CoarseBLASScalar);
b += (uint64_t)(BLAS_Amat.capacity()+BLAS_Ascr.capacity())*sizeof(calcMatrix);
return b; return b;
} }
NonLocalStencilGeometry & Geometry(void) { return geom_srhs; }; NonLocalStencilGeometry & Geometry(void) { return geom_srhs; };
void ExtractMatrix(int p,CoarseMatrix &A) { BLAStoGrid(A,BLAS_A[p]); };
// One stencil point in or out of the site-major buffer.
void MatrixPointIn(int p,const deviceVector<calcMatrix> &src)
{
int32_t sites = _CoarseGrid->lSites();
const int np = geom.npoint;
calcMatrix *dst = &BLAS_Amat[0];
const calcMatrix *sp = &src[0];
accelerator_for(ss,sites,1,{ dst[(uint64_t)ss*np+p] = sp[ss]; });
}
void MatrixPointOut(int p,deviceVector<calcMatrix> &dst)
{
int32_t sites = _CoarseGrid->lSites();
const int np = geom.npoint;
const calcMatrix *sp = &BLAS_Amat[0];
dst.resize(sites);
calcMatrix *dp = &dst[0];
accelerator_for(ss,sites,1,{ dp[ss] = sp[(uint64_t)ss*np+p]; });
}
void ExtractMatrix(int p,CoarseMatrix &A)
{
MatrixPointOut(p,BLAS_Ascr);
BLAStoGrid(A,BLAS_Ascr);
}
// I/O on the operator matrices, via the BLAS layout array. The parameter is // I/O on the operator matrices, via the BLAS layout array. The parameter is
// a vector over the geometry points; the body indexes A[p]. // a vector over the geometry points; the body indexes A[p].
void SetMatrix (int p,std::vector<CoarseMatrix> & A) void SetMatrix (int p,std::vector<CoarseMatrix> & A)
{ {
GRID_ASSERT(A.size()==geom_srhs.npoint); GRID_ASSERT(A.size()==geom_srhs.npoint);
GridtoBLAS(A[p],BLAS_A[p]); GridtoBLAS(A[p],BLAS_Ascr);
MatrixPointIn(p,BLAS_Ascr);
} }
void GetMatrix (int p,std::vector<CoarseMatrix> & A) void GetMatrix (int p,std::vector<CoarseMatrix> & A)
{ {
GRID_ASSERT(A.size()==geom_srhs.npoint); GRID_ASSERT(A.size()==geom_srhs.npoint);
BLAStoGrid(A[p],BLAS_A[p]); MatrixPointOut(p,BLAS_Ascr);
BLAStoGrid(A[p],BLAS_Ascr);
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@@ -218,7 +335,7 @@ public:
// is independent of Nrhs, in particular the matrix elements, which must // is independent of Nrhs, in particular the matrix elements, which must
// survive a change of Nrhs untouched. // survive a change of Nrhs untouched.
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
MultiGeneralCoarsenedOperatorV2(NonLocalStencilGeometry &_geom,GridCartesian *CoarseGrid) : MultiGeneralCoarsenedOperator(NonLocalStencilGeometry &_geom,GridCartesian *CoarseGrid) :
_CoarseGrid(CoarseGrid), _CoarseGrid(CoarseGrid),
geom_srhs(_geom), geom_srhs(_geom),
geom(CoarseGrid,_geom.hops,_geom.skip), geom(CoarseGrid,_geom.hops,_geom.skip),
@@ -233,28 +350,26 @@ public:
///////////////////////////////////////////////// /////////////////////////////////////////////////
// Matrix elements and their pointer table // Matrix elements and their pointer table
///////////////////////////////////////////////// /////////////////////////////////////////////////
BLAS_A.resize(geom.npoint); BLAS_Amat.resize((uint64_t)unpadded_sites*geom.npoint);
BLAS_Ascr.resize(unpadded_sites);
BLAS_AP.resize(geom.npoint); BLAS_AP.resize(geom.npoint);
for(int p=0;p<geom.npoint;p++){ for(int p=0;p<geom.npoint;p++){
BLAS_A[p].resize (unpadded_sites); // no ghost zone, npoint elements
BLAS_AP[p].resize(unpadded_sites); BLAS_AP[p].resize(unpadded_sites);
} }
// Site identity mapping for A // Point p at site ss: the unfolded path strides by the whole stencil.
for(int p=0;p<geom.npoint;p++){ for(int p=0;p<geom.npoint;p++){
for(int ss=0;ss<unpadded_sites;ss++){ for(int ss=0;ss<unpadded_sites;ss++){
CoarseBLASScalar *ptr = (CoarseBLASScalar *)&BLAS_A[p][ss]; CoarseBLASScalar *ptr = (CoarseBLASScalar *)&BLAS_Amat[(uint64_t)ss*geom.npoint+p];
acceleratorPut(BLAS_AP[p][ss],ptr); acceleratorPut(BLAS_AP[p][ss],ptr);
} }
} }
// Enough legs to bring the batch up to where the kernel saturates, and // The organisation is chosen in SetGrid, where Nrhs is known.
// never more legs than the stencil has. KFold = 0;
LegGroup = (TargetBatch + unpadded_sites - 1)/unpadded_sites; LegGroup = AutoLegGroup();
if ( LegGroup < 1 ) LegGroup = 1;
if ( LegGroup > geom.npoint ) LegGroup = geom.npoint;
BuildGroupedA(); BuildGroupedA();
std::cout << GridLogMessage << "MultiGeneralCoarsenedOperatorV2: stencil " std::cout << GridLogMessage << "MultiGeneralCoarsenedOperator: stencil "
<< geom.npoint << " points, local coarse volume " << unpadded_sites << geom.npoint << " points, local coarse volume " << unpadded_sites
<< ", legs per GEMM " << LegGroup << ", legs per GEMM " << LegGroup
<< " (batch " << LegGroup*unpadded_sites << ", " << " (batch " << LegGroup*unpadded_sites << ", "
@@ -263,7 +378,7 @@ public:
<< ")" << std::endl; << ")" << std::endl;
} }
virtual ~MultiGeneralCoarsenedOperatorV2() virtual ~MultiGeneralCoarsenedOperator()
{ {
ReleaseGrid(); ReleaseGrid();
} }
@@ -285,6 +400,10 @@ public:
BLAS_Cg.resize(0); BLAS_Cg.resize(0);
BLAS_CPg.resize(0); BLAS_CPg.resize(0);
BLAS_CPr.resize(0); BLAS_CPr.resize(0);
BLAS_BK.resize(0);
BLAS_BPflat.resize(0);
for(int g=0;g<(int)BLAS_BKP.size();g++){ BLAS_BKP[g].resize(0); }
BLAS_BKP.resize(0);
for(int g=0;g<(int)BLAS_BPg.size();g++){ BLAS_BPg[g].resize(0); } for(int g=0;g<(int)BLAS_BPg.size();g++){ BLAS_BPg[g].resize(0); }
BLAS_BPg.resize(0); BLAS_BPg.resize(0);
for(int p=0;p<BLAS_BP.size();p++){ for(int p=0;p<BLAS_BP.size();p++){
@@ -342,6 +461,19 @@ public:
GRID_ASSERT(CellMulti->grids.back()->lSites() == nrhs*padded_sites); GRID_ASSERT(CellMulti->grids.back()->lSites() == nrhs*padded_sites);
GRID_ASSERT(_CoarseGridMulti->lSites() == nrhs*unpadded_sites); GRID_ASSERT(_CoarseGridMulti->lSites() == nrhs*unpadded_sites);
/////////////////////////////////////////////////
// Choose the organisation FIRST: every table below is sized from
// LegGroup or KFold, and is indexed with them in the apply, so the two
// must be decided before anything is allocated.
/////////////////////////////////////////////////
ChooseOrganisation(nrhs);
std::cout << GridLogMessage << "MultiGeneralCoarsenedOperator: Nrhs " << nrhs
<< ", coarse apply "
<< ( KFold ? "folds "+std::to_string(KFold)+" legs along K (K = "
+std::to_string(KFold*nbasis)+", "+std::to_string(KChunks())+" chunk(s))"
: "carries "+std::to_string(LegGroup)+" legs in the batch" )
<< std::endl;
///////////////////////////////////////////////// /////////////////////////////////////////////////
// Device data vector storage // Device data vector storage
///////////////////////////////////////////////// /////////////////////////////////////////////////
@@ -382,6 +514,24 @@ public:
BLAS_BPg[g].resize(legs*unpadded_sites); BLAS_BPg[g].resize(legs*unpadded_sites);
} }
// Folded-along-K working set: one chunk of gathered neighbour vectors,
// laid out per site as a column-major (legs*nbasis) x nrhs block, plus a
// pointer table per chunk because a short last chunk has a shorter site
// stride. BLAS_BPflat is the neighbour table the gather kernel reads.
if ( KFold ) {
BLAS_BK.resize((uint64_t)unpadded_sites*nrhs*KFold*nbasis);
BLAS_BKP.resize(KChunks());
for(int g=0;g<KChunks();g++){
int legs = KLegs(g);
BLAS_BKP[g].resize(unpadded_sites);
for(int32_t ss=0;ss<unpadded_sites;ss++){
CoarseBLASScalar *ptr = &BLAS_BK[(uint64_t)ss*nrhs*legs*nbasis];
acceleratorPut(BLAS_BKP[g][ss],ptr);
}
}
BLAS_BPflat.resize((uint64_t)geom.npoint*unpadded_sites);
}
// Neighbour table is more complicated // Neighbour table is more complicated
int32_t j=0; // Interior point counter (unpadded) int32_t j=0; // Interior point counter (unpadded)
for(int32_t s=0;s<padded_sites;s++){ // D volume, padded for(int32_t s=0;s<padded_sites;s++){ // D volume, padded
@@ -402,6 +552,7 @@ public:
CoarseBLASScalar * ptr = (CoarseBLASScalar *)&BLAS_B[nbr]; CoarseBLASScalar * ptr = (CoarseBLASScalar *)&BLAS_B[nbr];
acceleratorPut(BLAS_BP[point][j],ptr); // neighbour indexing in ghost zone volume acceleratorPut(BLAS_BP[point][j],ptr); // neighbour indexing in ghost zone volume
acceleratorPut(BLAS_BPg[point/LegGroup][(point%LegGroup)*unpadded_sites+j],ptr); acceleratorPut(BLAS_BPg[point/LegGroup][(point%LegGroup)*unpadded_sites+j],ptr);
if ( KFold ) acceleratorPut(BLAS_BPflat[(uint64_t)point*unpadded_sites+j],ptr);
} }
j++; j++;
} }
@@ -707,7 +858,7 @@ public:
// orthonormalised basis into it, and the caller keeps it for the solve. // orthonormalised basis into it, and the caller keeps it for the solve.
// The overload below makes its own and is kept for the pre-2026 drivers; // The overload below makes its own and is kept for the pre-2026 drivers;
// it costs a second full basis store, which is why nothing new should use // it costs a second full basis store, which is why nothing new should use
// it (documentation/MultiGridMemoryAudit.md). // it.
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop, void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
GridCartesian *FineGridMulti, GridCartesian *FineGridMulti,
std::vector<FineField> &Subspace, std::vector<FineField> &Subspace,
@@ -815,8 +966,10 @@ public:
} }
for(int p=0;p<npoint;p++){ for(int p=0;p<npoint;p++){
GridtoBLAS(_A[p],BLAS_A[p]); GridtoBLAS(_A[p],BLAS_Ascr);
MatrixPointIn(p,BLAS_Ascr);
} }
if ( KFold ) PackFoldedMatrix(); // the matrix is only valid from here
CoarsenReport(tphase,tphaseBZ,tslice,tmat,tproj,tinv); CoarsenReport(tphase,tphaseBZ,tslice,tmat,tproj,tinv);
} }
@@ -920,8 +1073,10 @@ public:
} }
for(int p=0;p<npoint;p++){ for(int p=0;p<npoint;p++){
GridtoBLAS(_A[p],BLAS_A[p]); GridtoBLAS(_A[p],BLAS_Ascr);
MatrixPointIn(p,BLAS_Ascr);
} }
if ( KFold ) PackFoldedMatrix(); // the matrix is only valid from here
CoarsenReport(tphase,tphaseBZ,tslice,tmat,tproj,tinv); CoarsenReport(tphase,tphaseBZ,tslice,tmat,tproj,tinv);
} }
void Mdag(const CoarseVector &in, CoarseVector &out) void Mdag(const CoarseVector &in, CoarseVector &out)
@@ -944,7 +1099,7 @@ public:
CheckGridSet(); CheckGridSet();
if ( in.Grid() != _CoarseGridMulti ) { if ( in.Grid() != _CoarseGridMulti ) {
std::cout << GridLogError std::cout << GridLogError
<< "MultiGeneralCoarsenedOperatorV2::M called with a field on a" << "MultiGeneralCoarsenedOperator::M called with a field on a"
<< std::endl; << std::endl;
std::cout << GridLogError std::cout << GridLogError
<< " different grid object from the one given to SetGrid(). Two" << " different grid object from the one given to SetGrid(). Two"
@@ -955,14 +1110,14 @@ public:
GRID_ASSERT(in.Grid() == _CoarseGridMulti); GRID_ASSERT(in.Grid() == _CoarseGridMulti);
} }
GRID_TRACE("CoarseV2Mult"); GRID_TRACE("CoarseMult");
t_tot=-usecond(); t_tot=-usecond();
t_exch=-usecond(); t_exch=-usecond();
// The exchange takes its input by const reference, so it reads the // The exchange takes its input by const reference, so it reads the
// caller's field directly. lambda scope so the roctx range covers // caller's field directly. lambda scope so the roctx range covers
// exactly the exchange; the PaddedCellFwd/BwdMPI markers inside it then // exactly the exchange; the PaddedCellFwd/BwdMPI markers inside it then
// nest properly. // nest properly.
CoarseVector pin = [&](){ GRID_TRACE("CoarseV2Exchange"); CoarseVector pin = [&](){ GRID_TRACE("CoarseExchange");
return CellMulti->ExchangePeriodic(in); }(); //padded input return CellMulti->ExchangePeriodic(in); }(); //padded input
t_exch+=usecond(); t_exch+=usecond();
@@ -985,7 +1140,7 @@ public:
t_GtoB=-usecond(); t_GtoB=-usecond();
{ GRID_TRACE("CoarseV2GridToBLAS"); { GRID_TRACE("CoarseGridToBLAS");
GridtoBLAS(pin,BLAS_B); GridtoBLAS(pin,BLAS_B);
} }
t_GtoB+=usecond(); t_GtoB+=usecond();
@@ -993,10 +1148,46 @@ public:
GridBLAS BLAS; GridBLAS BLAS;
t_mult=-usecond(); t_mult=-usecond();
{ GRID_TRACE("CoarseV2StencilGEMM"); { GRID_TRACE("CoarseStencilGEMM");
// The scalar type selects the GEMM: Cgemm for an fp32 coarse space, // The scalar type selects the GEMM: Cgemm for an fp32 coarse space,
// Zgemm for fp64. // Zgemm for fp64.
if ( LegGroup == 1 ) { if ( KFold ) {
// Gather one chunk of neighbour vectors per site, then one GEMM whose
// contraction runs over the chunk's legs, accumulating into C. No
// partial results, so nothing to combine afterwards.
const int nb = nbasis;
const int64_t ns = (int64_t)_CoarseGrid->lSites(); // D dimensional: CoarseGrid() is D+1
GRID_ASSERT( (int64_t)BLAS_BPflat.size() == (int64_t)geom.npoint*ns );
CoarseBLASScalar * const bk = &BLAS_BK[0];
CoarseBLASScalar * const * const bp = &BLAS_BPflat[0];
for(int g=0;g<KChunks();g++){
const int64_t legs = KLegs(g);
const int64_t leg0 = (int64_t)g*KFold;
{ GRID_TRACE("CoarseLegGather");
accelerator_for(idx, ns*legs*nrhs, 1, {
int64_t j = idx % nrhs; // right hand side (column)
int64_t t = idx / nrhs;
int64_t l = t % legs; // leg within the chunk
int64_t ss = t / legs; // site
const CoarseBLASScalar *src = bp[(leg0+l)*ns + ss] + j*nb;
CoarseBLASScalar *dst = bk + ((ss*nrhs + j)*legs + l)*nb;
for(int i=0;i<nb;i++) dst[i] = src[i];
});
}
RealD c = (g==0) ? 0.0 : 1.0;
BLAS.gemmBatched(nbasis,nrhs,legs*nbasis,
CoarseBLASScalar(1.0),
BLAS_AKP[g],
BLAS_BKP[g],
CoarseBLASScalar(c),
BLAS_CP);
// One gather buffer serves every chunk and the multiply is issued
// asynchronously, so the next chunk's gather must not start until this
// multiply has read it. One synchronise per chunk, none when the whole
// stencil folds into a single call.
if ( g+1 < KChunks() ) BLAS.synchronise();
}
} else if ( LegGroup == 1 ) {
for(int p=0;p<geom.npoint;p++){ for(int p=0;p<geom.npoint;p++){
RealD c = (p==0) ? 0.0 : 1.0; RealD c = (p==0) ? 0.0 : 1.0;
BLAS.gemmBatched(nbasis,nrhs,nbasis, BLAS.gemmBatched(nbasis,nrhs,nbasis,
@@ -1037,7 +1228,7 @@ public:
} }
t_mult+=usecond(); t_mult+=usecond();
if ( LegGroup > 1 ) { GRID_TRACE("CoarseV2LegSum"); if ( !KFold && LegGroup > 1 ) { GRID_TRACE("CoarseLegSum");
// Sum the LegGroup partial results into the single output buffer. // Sum the LegGroup partial results into the single output buffer.
// Flat over scalars: a calcVector is nbasis of them and the partials // Flat over scalars: a calcVector is nbasis of them and the partials
// are contiguous, one whole C volume after another. // are contiguous, one whole C volume after another.
@@ -1053,7 +1244,7 @@ public:
} }
t_BtoG=-usecond(); t_BtoG=-usecond();
{ GRID_TRACE("CoarseV2BLASToGrid"); { GRID_TRACE("CoarseBLASToGrid");
BLAStoGrid(out,BLAS_C); BLAStoGrid(out,BLAS_C);
} }
t_BtoG+=usecond(); t_BtoG+=usecond();
+2 -2
View File
@@ -34,7 +34,7 @@ NAMESPACE_BEGIN(Grid);
// HDCGCoarsening everything that is a function of the gauge // HDCGCoarsening everything that is a function of the gauge
// field: the raw near-null basis (and its // field: the raw near-null basis (and its
// refinement), the transfer operators, the // refinement), the transfer operators, the
// Galerkin coarse operator (V2, Hermitian), // Galerkin coarse operator (Hermitian),
// the coarse eigenvectors for deflation // the coarse eigenvectors for deflation
// HDCGSolver the solve chain on a borrowed coarsening: // HDCGSolver the solve chain on a borrowed coarsening:
// smoother, deflated coarse solve, ADEF-2 // smoother, deflated coarse solve, ADEF-2
@@ -51,7 +51,7 @@ template<class Fobj,class CComplex,int nbasis>
class HDCGCoarsening { class HDCGCoarsening {
public: public:
typedef Lattice<Fobj> FineField; typedef Lattice<Fobj> FineField;
typedef MultiGeneralCoarsenedOperatorV2<Fobj,CComplex,nbasis> CoarseOperator; typedef MultiGeneralCoarsenedOperator<Fobj,CComplex,nbasis> CoarseOperator;
typedef typename CoarseOperator::CoarseVector CoarseVector; typedef typename CoarseOperator::CoarseVector CoarseVector;
typedef typename GridTypeMapper<Fobj>::SinglePrecision FobjF; typedef typename GridTypeMapper<Fobj>::SinglePrecision FobjF;
typedef Lattice<FobjF> FineFieldF; typedef Lattice<FobjF> FineFieldF;
+181 -49
View File
@@ -50,23 +50,74 @@ public:
LinearOperatorBase<Field> &Linop; LinearOperatorBase<Field> &Linop;
MrhsLinearFunction<Field> &Preconditioner; MrhsLinearFunction<Field> &Preconditioner;
std::function<void(int)> OnStep; // called with the outer step count after every step std::function<void(int)> OnStep; // called with the outer step count after every step
void Level(int lv){ name = "Level " + std::to_string(lv); level=lv; }
void Name(std::string n){ name = n; trace_op = name+" MrhsPGCR::vOp"; trace_orthog = name+" MrhsPGCR orthog"; } void Level(int lv){
void SetZeroGuess(int z){ ZeroGuess=z; } name = "Level " + std::to_string(lv);
level=lv;
}
void Name(std::string n){
name = n;
trace_op = name+" MrhsPGCR::vOp";
trace_orthog = name+" MrhsPGCR orthog";
}
void SetZeroGuess(int z)
{
ZeroGuess=z;
}
MrhsPGCRNonHermitian(RealD tol,Integer maxit,LinearOperatorBase<Field> &_Linop,MrhsLinearFunction<Field> &Prec,int _mmax,int _nstep) MrhsPGCRNonHermitian(RealD tol,Integer maxit,LinearOperatorBase<Field> &_Linop,MrhsLinearFunction<Field> &Prec,int _mmax,int _nstep)
: Tolerance(tol),MaxIterations(maxit),Linop(_Linop),Preconditioner(Prec),mmax(_mmax),nstep(_nstep){ level=1; } : Tolerance(tol),MaxIterations(maxit),Linop(_Linop),Preconditioner(Prec),mmax(_mmax),nstep(_nstep)
static RealD vnorm2(std::vector<Field> &x){ RealD s=0; for(auto &f:x) s+=norm2(f); return s; } {
static ComplexD vinnerProduct(std::vector<Field> &x,std::vector<Field> &y){ ComplexD s(0); for(int r=0;r<(int)x.size();r++) s+=innerProduct(x[r],y[r]); return s; } level=1;
static void vaxpy(std::vector<Field> &z,ComplexD a,std::vector<Field> &x,std::vector<Field> &y){ for(int r=0;r<(int)z.size();r++) axpy(z[r],a,x[r],y[r]); } }
void vOp(std::vector<Field> &in,std::vector<Field> &out){ GRID_TRACE(trace_op.c_str()); for(int r=0;r<(int)in.size();r++) Linop.Op(in[r],out[r]); }
static RealD vnorm2(std::vector<Field> &x)
{
RealD s=0;
for(auto &f:x) s+=norm2(f);
return s;
}
static ComplexD vinnerProduct(std::vector<Field> &x,std::vector<Field> &y){
ComplexD s(0);
for(int r=0;r<(int)x.size();r++) {
s+=innerProduct(x[r],y[r]);
}
return s;
}
static void vaxpy(std::vector<Field> &z,ComplexD a,std::vector<Field> &x,std::vector<Field> &y)
{
for(int r=0;r<(int)z.size();r++) {
axpy(z[r],a,x[r],y[r]);
}
}
void vOp(std::vector<Field> &in,std::vector<Field> &out)
{
GRID_TRACE(trace_op.c_str());
for(int r=0;r<(int)in.size();r++) {
Linop.Op(in[r],out[r]);
}
}
void operator()(std::vector<Field> &src,std::vector<Field> &psi){ void operator()(std::vector<Field> &src,std::vector<Field> &psi){
RealD cp,ssq,rsq; int nrhs=src.size(); GridBase *grid=src[0].Grid(); RealD cp,ssq,rsq;
ssq=vnorm2(src); rsq=Tolerance*Tolerance*ssq; int nrhs=src.size();
GridBase *grid=src[0].Grid();
ssq=vnorm2(src);
rsq=Tolerance*Tolerance*ssq;
std::vector<Field> r(nrhs,grid); std::vector<Field> r(nrhs,grid);
GridStopWatch T; T.Start(); steps=0; FirstCycle=1; GridStopWatch T;
T.Start();
steps=0;
FirstCycle=1;
for(int k=0;k<MaxIterations;k++){ for(int k=0;k<MaxIterations;k++){
cp=GCRnStep(src,psi,rsq); cp=GCRnStep(src,psi,rsq);
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR("<<mmax<<","<<nstep<<") "<<steps<<" steps cp = "<<cp<<" target "<<rsq<<std::endl; std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR("<<mmax<<","<<nstep<<") "<<steps
<<" steps cp = "<<cp<<" target "<<rsq<<std::endl;
if(cp<rsq){ if(cp<rsq){
T.Stop(); vOp(psi,r); for(int rr=0;rr<nrhs;rr++) axpy(r[rr],-1.0,src[rr],r[rr]); T.Stop(); vOp(psi,r); for(int rr=0;rr<nrhs;rr++) axpy(r[rr],-1.0,src[rr],r[rr]);
RealD tr=vnorm2(r); RealD tr=vnorm2(r);
@@ -78,28 +129,61 @@ public:
} }
std::cout<<GridLogMessage<<"MrhsPGCR: did not converge"<<std::endl; std::cout<<GridLogMessage<<"MrhsPGCR: did not converge"<<std::endl;
} }
RealD GCRnStep(std::vector<Field> &src,std::vector<Field> &psi,RealD rsq){ RealD GCRnStep(std::vector<Field> &src,std::vector<Field> &psi,RealD rsq)
RealD cp; ComplexD a,rq; int nrhs=src.size(); GridBase *grid=src[0].Grid(); {
RealD cp;
ComplexD a,rq;
int nrhs=src.size();
GridBase *grid=src[0].Grid();
std::vector<Field> r(nrhs,grid),Az(nrhs,grid); // Az: restart residual scratch only std::vector<Field> r(nrhs,grid),Az(nrhs,grid); // Az: restart residual scratch only
std::vector< std::vector<Field> > q(mmax,std::vector<Field>(nrhs,grid)); std::vector< std::vector<Field> > q(mmax,std::vector<Field>(nrhs,grid));
std::vector< std::vector<Field> > p(mmax,std::vector<Field>(nrhs,grid)); std::vector< std::vector<Field> > p(mmax,std::vector<Field>(nrhs,grid));
std::vector<RealD> qq(mmax); std::vector<RealD> qq(mmax);
if (ZeroGuess && FirstCycle) { for(int rr=0;rr<nrhs;rr++){ psi[rr]=Zero(); r[rr]=src[rr]; } }
else { vOp(psi,Az); for(int rr=0;rr<nrhs;rr++) r[rr]=src[rr]-Az[rr]; } if (ZeroGuess && FirstCycle) {
for(int rr=0;rr<nrhs;rr++){
psi[rr]=Zero();
r[rr]=src[rr];
}
} else {
vOp(psi,Az);
for(int rr=0;rr<nrhs;rr++) {
r[rr]=src[rr]-Az[rr];
}
}
FirstCycle=0; FirstCycle=0;
// p[0]=Prec(r), q[0]=A p[0], produced directly in the history slots (no copies) // p[0]=Prec(r), q[0]=A p[0], produced directly in the history slots (no copies)
Preconditioner(r,p[0]); vOp(p[0],q[0]); qq[0]=vnorm2(q[0]); cp=vnorm2(r);
Preconditioner(r,p[0]);
vOp(p[0],q[0]);
qq[0]=vnorm2(q[0]);
cp=vnorm2(r);
for(int k=0;k<nstep;k++){ for(int k=0;k<nstep;k++){
steps++; int kp=k+1, peri_k=k%mmax, peri_kp=kp%mmax; steps++;
int kp=k+1, peri_k=k%mmax, peri_kp=kp%mmax;
if ( OnStep ) OnStep(steps); if ( OnStep ) OnStep(steps);
rq=vinnerProduct(q[peri_k],r); a=rq/qq[peri_k];
vaxpy(psi,a,p[peri_k],psi); vaxpy(r,-a,q[peri_k],r); cp=vnorm2(r); rq=vinnerProduct(q[peri_k],r);
a=rq/qq[peri_k];
vaxpy(psi,a,p[peri_k],psi);
vaxpy(r,-a,q[peri_k],r);
cp=vnorm2(r);
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR step["<<steps<<"] resid "<<cp<<" target "<<rsq<<std::endl; std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR step["<<steps<<"] resid "<<cp<<" target "<<rsq<<std::endl;
if((k==nstep-1)||(cp<rsq)) return cp; if((k==nstep-1)||(cp<rsq)) return cp;
// New direction straight into its history slot: p=Prec(r), q=A p. // New direction straight into its history slot: p=Prec(r), q=A p.
Preconditioner(r,p[peri_kp]); Preconditioner(r,p[peri_kp]);
vOp(p[peri_kp],q[peri_kp]); vOp(p[peri_kp],q[peri_kp]);
int northog=((kp)>(mmax-1))?(mmax-1):(kp); int northog=((kp)>(mmax-1))?(mmax-1):(kp);
{ {
GRID_TRACE(trace_orthog.c_str()); GRID_TRACE(trace_orthog.c_str());
// Classical Gram-Schmidt: all coefficients against the UN-updated new q // Classical Gram-Schmidt: all coefficients against the UN-updated new q
@@ -110,15 +194,30 @@ public:
std::vector<ComplexD> bcoef(northog,ComplexD(0.0)), part; std::vector<ComplexD> bcoef(northog,ComplexD(0.0)), part;
for(int rr=0;rr<nrhs;rr++){ for(int rr=0;rr<nrhs;rr++){
std::vector<const Field*> qwin(northog); std::vector<const Field*> qwin(northog);
for(int back=0;back<northog;back++){ int peri_back=(k-back)%mmax; GRID_ASSERT((k-back)>=0); qwin[back]=&q[peri_back][rr]; } for(int back=0;back<northog;back++){
int peri_back=(k-back)%mmax;
GRID_ASSERT((k-back)>=0);
qwin[back]=&q[peri_back][rr];
}
rankInnerProductMulti(part,qwin,q[peri_kp][rr]); rankInnerProductMulti(part,qwin,q[peri_kp][rr]);
for(int back=0;back<northog;back++) bcoef[back]+=part[back]; for(int back=0;back<northog;back++) bcoef[back]+=part[back];
} }
if(northog) grid->GlobalSumVector(&bcoef[0],northog); if(northog) grid->GlobalSumVector(&bcoef[0],northog);
for(int back=0;back<northog;back++){ int peri_back=(k-back)%mmax; bcoef[back]=-bcoef[back]/qq[peri_back]; }
for(int back=0;back<northog;back++){
int peri_back=(k-back)%mmax;
bcoef[back]=-bcoef[back]/qq[peri_back];
}
for(int rr=0;rr<nrhs;rr++){ for(int rr=0;rr<nrhs;rr++){
std::vector<const Field*> qwin(northog), pwin(northog); std::vector<const Field*> qwin(northog), pwin(northog);
for(int back=0;back<northog;back++){ int peri_back=(k-back)%mmax; qwin[back]=&q[peri_back][rr]; pwin[back]=&p[peri_back][rr]; } for(int back=0;back<northog;back++){
int peri_back=(k-back)%mmax;
qwin[back]=&q[peri_back][rr];
pwin[back]=&p[peri_back][rr];
}
axpyMulti(p[peri_kp][rr],bcoef,pwin); axpyMulti(p[peri_kp][rr],bcoef,pwin);
axpyMulti(q[peri_kp][rr],bcoef,qwin); axpyMulti(q[peri_kp][rr],bcoef,qwin);
} }
@@ -168,24 +267,42 @@ public:
_Coarse5d(Coarse5d), _CoarseCoarse5d(CoarseCoarse5d), _CoarseCoarseMrhs(CoarseCoarseMrhs), _nrhs(nrhs) {} _Coarse5d(Coarse5d), _CoarseCoarse5d(CoarseCoarse5d), _CoarseCoarseMrhs(CoarseCoarseMrhs), _nrhs(nrhs) {}
using LinearFunction<CoarseField>::operator(); using LinearFunction<CoarseField>::operator();
virtual void operator()(const CoarseField &in, CoarseField &out) { virtual void operator()(const CoarseField &in, CoarseField &out) {
GRID_TRACE("MGCoarseVcycle");
CoarseField vec1(in.Grid()); CoarseField vec1(in.Grid());
CoarseField vec2(in.Grid()); CoarseField vec2(in.Grid());
out = in;
_CoarseOp.Op(out,vec1); sub(vec1,in,vec1);
// restrict, through the mixed blockProject: D+1 coarse in, D+1 cc out
CoarseCoarseField CCsrc(_CoarseCoarseMrhs); CoarseCoarseField CCsrc(_CoarseCoarseMrhs);
CoarseCoarseField CCsol(_CoarseCoarseMrhs); CoarseCoarseField CCsol(_CoarseCoarseMrhs);
_Projector.blockProject(vec1,CCsrc); // The cycle starts from x0 = in, one unit-step Richardson iteration ahead of
// a zero start; measured, it pays for its apply by cutting Couter steps 22%.
_CoarseCoarseSolve(CCsrc,CCsol); // x0 is never materialised in out: the operator is applied to in directly and
// the copy folds into the add that lands the coarse-coarse correction.
_Projector.blockPromote(vec1,CCsol); {
add(out,out,vec1); GRID_TRACE("MGCoarseResidual");
_CoarseOp.Op(in,vec1); sub(vec1,in,vec1);
_CoarseOp.Op(out,vec1); sub(vec1,in,vec1); }
_CoarseSmoother(vec1,vec2); // restrict, through the mixed blockProject: D+1 coarse in, D+1 cc out
add(out,out,vec2); {
GRID_TRACE("MGCCProject");
_Projector.blockProject(vec1,CCsrc);
}
{
GRID_TRACE("MGCCSolve");
_CoarseCoarseSolve(CCsrc,CCsol);
}
{
GRID_TRACE("MGCCPromote");
_Projector.blockPromote(vec1,CCsol);
add(out,in,vec1);
}
{
GRID_TRACE("MGCoarseResidual2");
_CoarseOp.Op(out,vec1); sub(vec1,in,vec1);
}
{
GRID_TRACE("MGCoarseSmooth");
_CoarseSmoother(vec1,vec2);
add(out,out,vec2);
}
} }
}; };
@@ -211,37 +328,47 @@ public:
GridBase *_CoarseGrid, *_CoarseGridMrhs; GridBase *_CoarseGrid, *_CoarseGridMrhs;
std::function<void(int)> SetSloppy = [](int){}; std::function<void(int)> SetSloppy = [](int){};
int SloppyComms = 0; // value passed to SetSloppy on entry int SloppyComms = 0; // value passed to SetSloppy on entry
MrhsTwoLevelMG(LinearOperatorBase<FineField> &FineOp, FineSmoother &Post, MrhsTwoLevelMG(LinearOperatorBase<FineField> &FineOp, FineSmoother &Post,
Projector_t &Projector, LinearFunction<CoarseVector> &CoarseSolve, Projector_t &Projector, LinearFunction<CoarseVector> &CoarseSolve,
GridBase *CoarseGrid, GridBase *CoarseGridMrhs) GridBase *CoarseGrid, GridBase *CoarseGridMrhs)
: _FineOperator(FineOp),_PostSmoother(Post),_Projector(Projector),_CoarseSolve(CoarseSolve), : _FineOperator(FineOp),_PostSmoother(Post),_Projector(Projector),_CoarseSolve(CoarseSolve),
_CoarseGrid(CoarseGrid),_CoarseGridMrhs(CoarseGridMrhs){} _CoarseGrid(CoarseGrid),_CoarseGridMrhs(CoarseGridMrhs){}
virtual void operator()(std::vector<FineField> &in, std::vector<FineField> &out){
virtual void operator()(std::vector<FineField> &in, std::vector<FineField> &out)
{
GRID_TRACE("MGVcycle"); GRID_TRACE("MGVcycle");
SetSloppy(SloppyComms); SetSloppy(SloppyComms);
int nrhs=in.size(); GridBase *fgrid=in[0].Grid(); int nrhs=in.size(); GridBase *fgrid=in[0].Grid();
std::vector<FineField> vec1(nrhs,fgrid),vec2(nrhs,fgrid); std::vector<FineField> vec1(nrhs,fgrid),vec2(nrhs,fgrid);
for(int r=0;r<nrhs;r++) out[r]=in[r]; // x0 = in, never materialised in out: the operator is applied to in directly
{ GRID_TRACE("MGFineResidual"); // and the copy folds into the add that lands the coarse correction.
for(int r=0;r<nrhs;r++){ _FineOperator.Op(out[r],vec1[r]); sub(vec1[r],in[r],vec1[r]); } {
GRID_TRACE("MGFineResidual");
for(int r=0;r<nrhs;r++){ _FineOperator.Op(in[r],vec1[r]); sub(vec1[r],in[r],vec1[r]); }
} }
// fine vector -> D+1 coarse, via the mixed blockProject // fine vector -> D+1 coarse, via the mixed blockProject
CoarseVector CsrcMrhs(_CoarseGridMrhs), CsolMrhs(_CoarseGridMrhs); CoarseVector CsrcMrhs(_CoarseGridMrhs), CsolMrhs(_CoarseGridMrhs);
{ GRID_TRACE("MGProject"); {
GRID_TRACE("MGProject");
_Projector.blockProject(vec1,CsrcMrhs); _Projector.blockProject(vec1,CsrcMrhs);
} }
CsolMrhs=Zero(); CsolMrhs=Zero();
{ GRID_TRACE("MGCoarseSolve"); {
GRID_TRACE("MGCoarseSolve");
_CoarseSolve(CsrcMrhs,CsolMrhs); _CoarseSolve(CsrcMrhs,CsolMrhs);
} }
{ GRID_TRACE("MGPromote"); {
GRID_TRACE("MGPromote");
_Projector.blockPromote(vec1,CsolMrhs); _Projector.blockPromote(vec1,CsolMrhs);
for(int r=0;r<nrhs;r++) add(out[r],out[r],vec1[r]); for(int r=0;r<nrhs;r++) add(out[r],in[r],vec1[r]);
} }
{ GRID_TRACE("MGFineResidual2"); {
GRID_TRACE("MGFineResidual2");
for(int r=0;r<nrhs;r++){ _FineOperator.Op(out[r],vec1[r]); sub(vec1[r],in[r],vec1[r]); } for(int r=0;r<nrhs;r++){ _FineOperator.Op(out[r],vec1[r]); sub(vec1[r],in[r],vec1[r]); }
} }
{ GRID_TRACE("MGPostSmooth"); {
GRID_TRACE("MGPostSmooth");
for(int r=0;r<nrhs;r++){ for(int r=0;r<nrhs;r++){
_PostSmoother(vec1[r],vec2[r]); add(out[r],out[r],vec2[r]); _PostSmoother(vec1[r],vec2[r]); add(out[r],out[r],vec2[r]);
} }
@@ -271,15 +398,19 @@ public:
precisionChangeWorkspace _ws_f2d; // out fp64, in fp32 precisionChangeWorkspace _ws_f2d; // out fp64, in fp32
GridBase *_gridF; GridBase *_gridF;
std::vector<FieldF> _in_f, _out_f; std::vector<FieldF> _in_f, _out_f;
MrhsMixedPrecPreconditioner(MrhsPreconditioner<FieldF> &Inner, GridBase *gridD, GridBase *gridF, int nrhs) MrhsMixedPrecPreconditioner(MrhsPreconditioner<FieldF> &Inner, GridBase *gridD, GridBase *gridF, int nrhs)
: _Inner(Inner), _ws_d2f(gridF,gridD), _ws_f2d(gridD,gridF), _gridF(gridF) {} : _Inner(Inner), _ws_d2f(gridF,gridD), _ws_f2d(gridD,gridF), _gridF(gridF) {}
void Scratch(int nrhs){ void Scratch(int nrhs){
if ( (int)_in_f.size() == nrhs ) return; if ( (int)_in_f.size() == nrhs ) return;
_in_f.clear(); _in_f.reserve(nrhs); _in_f.clear(); _in_f.reserve(nrhs);
_out_f.clear(); _out_f.reserve(nrhs); _out_f.clear(); _out_f.reserve(nrhs);
for(int r=0;r<nrhs;r++){ _in_f.emplace_back(_gridF); _out_f.emplace_back(_gridF); } for(int r=0;r<nrhs;r++){ _in_f.emplace_back(_gridF); _out_f.emplace_back(_gridF); }
} }
virtual void operator()(std::vector<FieldD> &in, std::vector<FieldD> &out){
virtual void operator()(std::vector<FieldD> &in, std::vector<FieldD> &out)
{
GRID_TRACE("MGPrecisionSeam"); GRID_TRACE("MGPrecisionSeam");
int nrhs=in.size(); Scratch(nrhs); int nrhs=in.size(); Scratch(nrhs);
for(int r=0;r<nrhs;r++) precisionChange(_in_f[r],in[r],_ws_d2f); for(int r=0;r<nrhs;r++) precisionChange(_in_f[r],in[r],_ws_d2f);
@@ -287,7 +418,8 @@ public:
for(int r=0;r<nrhs;r++) precisionChange(out[r],_out_f[r],_ws_f2d); for(int r=0;r<nrhs;r++) precisionChange(out[r],_out_f[r],_ws_f2d);
} }
// The start and the timers are the inner preconditioner's // The start and the timers are the inner preconditioner's
virtual void Vstart(std::vector<FieldD> &x, std::vector<FieldD> &src){ virtual void Vstart(std::vector<FieldD> &x, std::vector<FieldD> &src)
{
GRID_TRACE("MGPrecisionSeamVstart"); GRID_TRACE("MGPrecisionSeamVstart");
int nrhs=src.size(); Scratch(nrhs); int nrhs=src.size(); Scratch(nrhs);
for(int r=0;r<nrhs;r++) precisionChange(_in_f[r],src[r],_ws_d2f); for(int r=0;r<nrhs;r++) precisionChange(_in_f[r],src[r],_ws_d2f);
+6 -6
View File
@@ -30,12 +30,12 @@ Author: Peter Boyle <pboyle@bnl.gov>
#include <Grid/algorithms/multigrid/Aggregates.h> #include <Grid/algorithms/multigrid/Aggregates.h>
#include <Grid/algorithms/multigrid/Geometry.h> #include <Grid/algorithms/multigrid/Geometry.h>
#include <Grid/algorithms/multigrid/CoarsenedMatrix.h> #include <Grid/algorithms/multigrid/CoarsenedMatrix.h>
#include <Grid/algorithms/multigrid/GeneralCoarsenedMatrixMultiRHSV2.h> #include <Grid/algorithms/multigrid/GeneralCoarsenedMatrixMultiRHS.h>
// DEPRECATED: the V1 coarse operators and the Aggregation-based single-RHS // DEPRECATED: the superseded coarse operators and the Aggregation-based
// ADEF-2. Nothing in the library uses them; the PVdagM and HDCG chains are // single-RHS ADEF-2. Nothing in the library uses them; the PVdagM and HDCG
// on V2 (PVdagMMultiGrid.h, HDCGMultiGrid.h). Kept so the pre-2026 drivers // chains are on MultiGeneralCoarsenedOperator (PVdagMMultiGrid.h,
// in tests/debug and examples still build; removing this block is the // HDCGMultiGrid.h). Kept so the pre-2026 drivers in tests/debug and
// deletion gate for them. // examples still build; removing this block is the deletion gate for them.
#include <Grid/algorithms/multigrid/deprecated/GeneralCoarsenedMatrix.h> #include <Grid/algorithms/multigrid/deprecated/GeneralCoarsenedMatrix.h>
#include <Grid/algorithms/multigrid/deprecated/GeneralCoarsenedMatrixMultiRHS.h> #include <Grid/algorithms/multigrid/deprecated/GeneralCoarsenedMatrixMultiRHS.h>
#include <Grid/algorithms/multigrid/deprecated/TwoLevelADEF2.h> #include <Grid/algorithms/multigrid/deprecated/TwoLevelADEF2.h>
+3 -3
View File
@@ -212,14 +212,14 @@ template<class Fobj,class CComplex,int nbasis>
class PVdagMMultiGridCoarsening { class PVdagMMultiGridCoarsening {
public: public:
typedef Lattice<Fobj> FineField; typedef Lattice<Fobj> FineField;
typedef MultiGeneralCoarsenedOperatorV2<Fobj,CComplex,nbasis> CoarseOperator; typedef MultiGeneralCoarsenedOperator<Fobj,CComplex,nbasis> CoarseOperator;
typedef typename CoarseOperator::CoarseVector CoarseVector; typedef typename CoarseOperator::CoarseVector CoarseVector;
typedef typename CoarseVector::vector_object CoarseSiteObj; typedef typename CoarseVector::vector_object CoarseSiteObj;
// Every coarse level carries the same site type iVector<CComplex,nbasis>: // Every coarse level carries the same site type iVector<CComplex,nbasis>:
// the coefficient scalar does not deepen with the level (the operator keeps // the coefficient scalar does not deepen with the level (the operator keeps
// its own deeper scratch type for the block inner products). Levels are // its own deeper scratch type for the block inner products). Levels are
// told apart by their grids, not their C++ types. // told apart by their grids, not their C++ types.
typedef MultiGeneralCoarsenedOperatorV2<CoarseSiteObj,CComplex,nbasis> CoarseCoarseOperator; typedef MultiGeneralCoarsenedOperator<CoarseSiteObj,CComplex,nbasis> CoarseCoarseOperator;
typedef typename CoarseCoarseOperator::CoarseVector CoarseCoarseVector; typedef typename CoarseCoarseOperator::CoarseVector CoarseCoarseVector;
typedef DenseCoarseMatrix<CComplex,nbasis> DenseBottom; typedef DenseCoarseMatrix<CComplex,nbasis> DenseBottom;
// The fp32 fine field, derived from the fp64 one: the fp32 fine level of // The fp32 fine field, derived from the fp64 one: the fp32 fine level of
@@ -359,7 +359,7 @@ public:
rawPsi.reserve(nbasis); rawPsi.reserve(nbasis);
for(int k=0;k<nbasis;k++) rawPsi.push_back(psi[k]); for(int k=0;k<nbasis;k++) rawPsi.push_back(psi[k]);
// L2: the V2 L1 operator is natively multiRHS at the batch grid // L2: the L1 operator is natively multiRHS at the batch grid
CoarseOpL2.SetGrid(Grids.CoarseCoarseBatch); CoarseOpL2.SetGrid(Grids.CoarseCoarseBatch);
NonHermitianLinearOperator<CoarseOperator,CoarseVector> LinOpCoarse(CoarseOpPV); NonHermitianLinearOperator<CoarseOperator,CoarseVector> LinOpCoarse(CoarseOpPV);
std::cout << GridLogMessage << "PVdagMMultiGridCoarsening: L2 CoarsenOperator, batch " std::cout << GridLogMessage << "PVdagMMultiGridCoarsening: L2 CoarsenOperator, batch "
+117 -26
View File
@@ -36,17 +36,54 @@ NAMESPACE_BEGIN(Grid);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
template<class Matrix,class Field> template<class Matrix,class Field>
class PVdagMLinearOperator : public LinearOperatorBase<Field> { class PVdagMLinearOperator : public LinearOperatorBase<Field> {
Matrix &_Mat; Matrix &_PV; Matrix &_Mat;
Matrix &_PV;
public: public:
PVdagMLinearOperator(Matrix &Mat,Matrix &PV): _Mat(Mat),_PV(PV) {}; PVdagMLinearOperator(Matrix &Mat,Matrix &PV):
void SloppyComms(int sloppy) { _Mat.SloppyComms(sloppy); _PV.SloppyComms(sloppy); } _Mat(Mat),_PV(PV)
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 SloppyComms(int sloppy)
void OpDirAll (const Field &in, std::vector<Field> &out){ GRID_ASSERT(0); }; {
void Op (const Field &in, Field &out){ Field tmp(in.Grid()); _Mat.M(in,tmp); _PV.Mdag(tmp,out); } _Mat.SloppyComms(sloppy);
void AdjOp (const Field &in, Field &out){ Field tmp(in.Grid()); _PV.M(in,tmp); _Mat.Mdag(tmp,out); } _PV.SloppyComms(sloppy);
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ HermOp(in,out); ComplexD d=innerProduct(in,out); n1=real(d); n2=norm2(out); } }
void HermOp(const Field &in, Field &out){ Field tmp(in.Grid()); Op(in,tmp); AdjOp(tmp,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<Field> &out)
{
GRID_ASSERT(0);
};
void Op (const Field &in, Field &out)
{
Field tmp(in.Grid());
_Mat.M(in,tmp);
_PV.Mdag(tmp,out);
}
void AdjOp (const Field &in, Field &out)
{
Field tmp(in.Grid());
_PV.M(in,tmp);
_Mat.Mdag(tmp,out);
}
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2)
{
HermOp(in,out);
ComplexD d=innerProduct(in,out);
n1=real(d);
n2=norm2(out);
}
void HermOp(const Field &in, Field &out)
{
Field tmp(in.Grid());
Op(in,tmp);
AdjOp(tmp,out);
}
}; };
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@@ -55,17 +92,46 @@ public:
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
template<class Matrix,class Field> template<class Matrix,class Field>
class ShiftedPVdagMLinearOperator : public LinearOperatorBase<Field> { class ShiftedPVdagMLinearOperator : public LinearOperatorBase<Field> {
Matrix &_Mat; Matrix &_PV; Matrix &_Mat;
Matrix &_PV;
public: public:
RealD shift; RealD shift;
ShiftedPVdagMLinearOperator(RealD _shift,Matrix &Mat,Matrix &PV): shift(_shift),_Mat(Mat),_PV(PV){}; ShiftedPVdagMLinearOperator(RealD _shift,Matrix &Mat,Matrix &PV): shift(_shift),_Mat(Mat),_PV(PV){};
void OpDiag (const Field &in, Field &out) { GRID_ASSERT(0); } void OpDiag (const Field &in, Field &out)
void OpDir (const Field &in, Field &out,int dir,int disp) { GRID_ASSERT(0); } {
void OpDirAll (const Field &in, std::vector<Field> &out){ GRID_ASSERT(0); }; GRID_ASSERT(0);
void Op (const Field &in, Field &out){ Field tmp(in.Grid()); _Mat.M(in,tmp); _PV.Mdag(tmp,out); out = out + shift*in; } }
void AdjOp (const Field &in, Field &out){ Field tmp(in.Grid()); _PV.M(in,tmp); _Mat.Mdag(tmp,out); out = out + shift*in; } void OpDir (const Field &in, Field &out,int dir,int disp)
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ GRID_ASSERT(0); } {
void HermOp(const Field &in, Field &out){ Field tmp(in.Grid()); Op(in,tmp); AdjOp(tmp,out); } GRID_ASSERT(0);
}
void OpDirAll (const Field &in, std::vector<Field> &out)
{
GRID_ASSERT(0);
};
void Op (const Field &in, Field &out)
{
Field tmp(in.Grid());
_Mat.M(in,tmp);
_PV.Mdag(tmp,out);
out = out + shift*in;
}
void AdjOp (const Field &in, Field &out)
{
Field tmp(in.Grid());
_PV.M(in,tmp);
_Mat.Mdag(tmp,out);
out = out + shift*in;
}
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2)
{
GRID_ASSERT(0);
}
void HermOp(const Field &in, Field &out){
Field tmp(in.Grid());
Op(in,tmp);
AdjOp(tmp,out);
}
}; };
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@@ -73,16 +139,41 @@ public:
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
template<class Field> template<class Field>
class ShiftedLinearOperator : public LinearOperatorBase<Field> { class ShiftedLinearOperator : public LinearOperatorBase<Field> {
LinearOperatorBase<Field> &_Op; RealD shift; LinearOperatorBase<Field> &_Op;
RealD shift;
public: public:
ShiftedLinearOperator(RealD _shift, LinearOperatorBase<Field> &Op) : _Op(Op), shift(_shift) {} ShiftedLinearOperator(RealD _shift, LinearOperatorBase<Field> &Op) : _Op(Op), shift(_shift) {}
void OpDiag (const Field &in, Field &out) { GRID_ASSERT(0); } void OpDiag (const Field &in, Field &out)
void OpDir (const Field &in, Field &out,int dir,int disp) { GRID_ASSERT(0); } {
void OpDirAll (const Field &in, std::vector<Field> &out) { GRID_ASSERT(0); } GRID_ASSERT(0);
void Op (const Field &in, Field &out) { _Op.Op(in,out); out = out + shift*in; } }
void AdjOp (const Field &in, Field &out) { _Op.AdjOp(in,out); out = out + shift*in; } void OpDir (const Field &in, Field &out,int dir,int disp)
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ GRID_ASSERT(0); } {
void HermOp (const Field &in, Field &out) { Field tmp(in.Grid()); Op(in,tmp); AdjOp(tmp,out); } GRID_ASSERT(0);
}
void OpDirAll (const Field &in, std::vector<Field> &out)
{
GRID_ASSERT(0);
}
void Op (const Field &in, Field &out)
{
_Op.Op(in,out);
out = out + shift*in;
}
void AdjOp (const Field &in, Field &out) {
_Op.AdjOp(in,out);
out = out + shift*in;
}
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2)
{
GRID_ASSERT(0);
}
void HermOp (const Field &in, Field &out)
{
Field tmp(in.Grid());
Op(in,tmp);
AdjOp(tmp,out);
}
}; };
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
@@ -38,10 +38,10 @@ NAMESPACE_BEGIN(Grid);
// Fine Object == (per site) type of fine field // Fine Object == (per site) type of fine field
// nbasis == number of deflation vectors // nbasis == number of deflation vectors
template<class Fobj,class CComplex,int nbasis> template<class Fobj,class CComplex,int nbasis>
class GeneralCoarsenedMatrix : public SparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > { class DeprecatedGeneralCoarsenedMatrix : public SparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > {
public: public:
typedef GeneralCoarsenedMatrix<Fobj,CComplex,nbasis> GeneralCoarseOp; typedef DeprecatedGeneralCoarsenedMatrix<Fobj,CComplex,nbasis> GeneralCoarseOp;
typedef iVector<CComplex,nbasis > siteVector; typedef iVector<CComplex,nbasis > siteVector;
typedef iMatrix<CComplex,nbasis > siteMatrix; typedef iMatrix<CComplex,nbasis > siteMatrix;
typedef Lattice<iScalar<CComplex> > CoarseComplexField; typedef Lattice<iScalar<CComplex> > CoarseComplexField;
@@ -103,7 +103,7 @@ public:
void ProjectNearestNeighbour(RealD shift, GeneralCoarseOp &CopyMe) void ProjectNearestNeighbour(RealD shift, GeneralCoarseOp &CopyMe)
{ {
int nfound=0; int nfound=0;
std::cout << GridLogMessage <<"GeneralCoarsenedMatrix::ProjectNearestNeighbour "<< CopyMe._A[0].Grid()<<std::endl; std::cout << GridLogMessage <<"DeprecatedGeneralCoarsenedMatrix::ProjectNearestNeighbour "<< CopyMe._A[0].Grid()<<std::endl;
for(int p=0;p<geom.npoint;p++){ for(int p=0;p<geom.npoint;p++){
for(int pp=0;pp<CopyMe.geom.npoint;pp++){ for(int pp=0;pp<CopyMe.geom.npoint;pp++){
// Search for the same relative shift // Search for the same relative shift
@@ -120,7 +120,7 @@ public:
} }
*/ */
GeneralCoarsenedMatrix(NonLocalStencilGeometry &_geom,GridBase *FineGrid, GridCartesian * CoarseGrid,int _herm=1) DeprecatedGeneralCoarsenedMatrix(NonLocalStencilGeometry &_geom,GridBase *FineGrid, GridCartesian * CoarseGrid,int _herm=1)
: geom(_geom), : geom(_geom),
_FineGrid(FineGrid), _FineGrid(FineGrid),
_CoarseGrid(CoarseGrid), _CoarseGrid(CoarseGrid),
@@ -34,11 +34,11 @@ NAMESPACE_BEGIN(Grid);
// Fine Object == (per site) type of fine field // Fine Object == (per site) type of fine field
// nbasis == number of deflation vectors // nbasis == number of deflation vectors
template<class Fobj,class CComplex,int nbasis> template<class Fobj,class CComplex,int nbasis>
class MultiGeneralCoarsenedMatrix : public SparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > { class DeprecatedMultiGeneralCoarsenedMatrix : public SparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > {
public: public:
typedef typename CComplex::scalar_object SComplex; typedef typename CComplex::scalar_object SComplex;
typedef GeneralCoarsenedMatrix<Fobj,CComplex,nbasis> GeneralCoarseOp; typedef DeprecatedGeneralCoarsenedMatrix<Fobj,CComplex,nbasis> GeneralCoarseOp;
typedef MultiGeneralCoarsenedMatrix<Fobj,CComplex,nbasis> MultiGeneralCoarseOp; typedef DeprecatedMultiGeneralCoarsenedMatrix<Fobj,CComplex,nbasis> MultiGeneralCoarseOp;
typedef iVector<CComplex,nbasis > siteVector; typedef iVector<CComplex,nbasis > siteVector;
typedef iMatrix<CComplex,nbasis > siteMatrix; typedef iMatrix<CComplex,nbasis > siteMatrix;
@@ -78,13 +78,33 @@ public:
GridCartesian * CoarseGrid(void) { return _CoarseGridMulti; }; // this is all the linalg routines need to know GridCartesian * CoarseGrid(void) { return _CoarseGridMulti; }; // this is all the linalg routines need to know
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Bilingual accessors, matching GeneralCoarsenedMatrix. Grid() here is the // Accessors shared with DeprecatedGeneralCoarsenedMatrix. Grid() here is the
// D+1 multiRHS grid and this class never holds the D dimensional one, so // D+1 multiRHS grid and this class never holds the D dimensional one, so
// ExtractMatrix writes into whatever grid the caller's lattice is on. // ExtractMatrix writes into whatever grid the caller's lattice is on.
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
NonLocalStencilGeometry & Geometry(void) { return geom_srhs; }; NonLocalStencilGeometry & Geometry(void) { return geom_srhs; };
void ExtractMatrix(int p,CoarseMatrix &A) { BLAStoGrid(A,BLAS_A[p]); }; void ExtractMatrix(int p,CoarseMatrix &A) { BLAStoGrid(A,BLAS_A[p]); };
// One stencil point in or out, matching MultiGeneralCoarsenedOperator, so a
// caller can read or write a point without knowing which of the two layouts
// (per-point here, site-major there) holds it.
void MatrixPointIn(int p,const deviceVector<calcMatrix> &src)
{
int64_t sites = BLAS_A[p].size();
GRID_ASSERT((int64_t)src.size() == sites);
calcMatrix *dst = &BLAS_A[p][0];
const calcMatrix *sp = &src[0];
accelerator_for(ss,sites,1,{ dst[ss] = sp[ss]; });
}
void MatrixPointOut(int p,deviceVector<calcMatrix> &dst)
{
int64_t sites = BLAS_A[p].size();
dst.resize(sites);
const calcMatrix *sp = &BLAS_A[p][0];
calcMatrix *dp = &dst[0];
accelerator_for(ss,sites,1,{ dp[ss] = sp[ss]; });
}
// I/O on the operator matrices, via the BLAS layout array. The parameter is // I/O on the operator matrices, via the BLAS layout array. The parameter is
// a vector over the geometry points; the body indexes A[p]. // a vector over the geometry points; the body indexes A[p].
void SetMatrix (int p,std::vector<CoarseMatrix> & A) void SetMatrix (int p,std::vector<CoarseMatrix> & A)
@@ -121,7 +141,7 @@ public:
} }
*/ */
MultiGeneralCoarsenedMatrix(NonLocalStencilGeometry &_geom,GridCartesian *CoarseGridMulti) : DeprecatedMultiGeneralCoarsenedMatrix(NonLocalStencilGeometry &_geom,GridCartesian *CoarseGridMulti) :
_CoarseGridMulti(CoarseGridMulti), _CoarseGridMulti(CoarseGridMulti),
geom_srhs(_geom), geom_srhs(_geom),
geom(_CoarseGridMulti,_geom.hops,_geom.skip+1), geom(_CoarseGridMulti,_geom.hops,_geom.skip+1),
@@ -27,15 +27,15 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
/* END LEGAL */ /* END LEGAL */
#pragma once #pragma once
// DEPRECATED with the V1 coarse operators: the single-RHS ADEF-2 on an // DEPRECATED with the other classes in deprecated/: the single-RHS ADEF-2 on an
// Aggregation (ProjectToSubspace/PromoteFromSubspace) and a D-dimensional // Aggregation (ProjectToSubspace/PromoteFromSubspace) and a D-dimensional
// coarse operator. The mrhs solver in AdefMrhs.h covers one right-hand // coarse operator. The mrhs solver in AdefMrhs.h covers one right-hand
// side through its LinearFunction interface, on the V2 coarse operator. // side through its LinearFunction interface, on MultiGeneralCoarsenedOperator.
NAMESPACE_BEGIN(Grid); NAMESPACE_BEGIN(Grid);
template<class Field, class CoarseField, class Aggregation> template<class Field, class CoarseField, class Aggregation>
class TwoLevelADEF2 : public TwoLevelCG<Field> class DeprecatedTwoLevelADEF2 : public TwoLevelCG<Field>
{ {
public: public:
/////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////
@@ -50,7 +50,7 @@ class TwoLevelADEF2 : public TwoLevelCG<Field>
/////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////
// more most opertor functions // more most opertor functions
TwoLevelADEF2(RealD tol, DeprecatedTwoLevelADEF2(RealD tol,
Integer maxit, Integer maxit,
LinearOperatorBase<Field> &FineLinop, LinearOperatorBase<Field> &FineLinop,
LinearFunction<Field> &Smoother, LinearFunction<Field> &Smoother,
+1
View File
@@ -133,6 +133,7 @@ public:
template<class obj> void GlobalSumP2P(obj &o) template<class obj> void GlobalSumP2P(obj &o)
{ {
GRID_TRACE("GlobalSumP2P");
std::vector<obj> column; std::vector<obj> column;
obj accum = o; obj accum = o;
int source,dest; int source,dest;
+1
View File
@@ -191,6 +191,7 @@ CartesianCommunicator::CartesianCommunicator(const Coordinate &processors,const
// Set up from the new split communicator // Set up from the new split communicator
////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////
InitFromMPICommunicator(processors,comm_split); InitFromMPICommunicator(processors,comm_split);
_shm_processors = shm_processors;
////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////
// Take the right SHM buffers // Take the right SHM buffers
+43 -13
View File
@@ -898,7 +898,8 @@ template<int B,class vobj>
void axpyMultiChunk(Lattice<vobj> &z,const ComplexD *b, void axpyMultiChunk(Lattice<vobj> &z,const ComplexD *b,
const std::vector<const Lattice<vobj>*> &x,int m, const std::vector<const Lattice<vobj>*> &x,int m,
int do_norm, int do_norm,
decltype(innerProduct(vobj(),vobj())) *inner_tmp_v) decltype(innerProduct(vobj(),vobj())) *inner_tmp_v,
const Lattice<vobj> *from=nullptr)
{ {
typedef decltype(z.View(AcceleratorRead)) View; typedef decltype(z.View(AcceleratorRead)) View;
GRID_ASSERT(m>=1 && m<=B); GRID_ASSERT(m>=1 && m<=B);
@@ -917,13 +918,29 @@ void axpyMultiChunk(Lattice<vobj> &z,const ComplexD *b,
} }
for(int j=m;j<B;j++){ pack.set(j,h_v[0]); pack.b[j] = ComplexD(0.0); } for(int j=m;j<B;j++){ pack.set(j,h_v[0]); pack.b[j] = ComplexD(0.0); }
autoView(z_v,z,AcceleratorWrite); if ( from == nullptr ) {
accelerator_for(ss,sites,nsimd,{ autoView(z_v,z,AcceleratorWrite);
auto acc = coalescedRead(z_v[ss]); accelerator_for(ss,sites,nsimd,{
for(int j=0;j<B;j++) if ( j<m ) acc = acc + pack.b[j]*coalescedRead(pack.v(j)[ss]); auto acc = coalescedRead(z_v[ss]);
coalescedWrite(z_v[ss],acc); for(int j=0;j<B;j++) if ( j<m ) acc = acc + pack.b[j]*coalescedRead(pack.v(j)[ss]);
if ( do_norm ) coalescedWrite(inner_tmp_v[ss],innerProduct(acc,acc)); coalescedWrite(z_v[ss],acc);
}); if ( do_norm ) coalescedWrite(inner_tmp_v[ss],innerProduct(acc,acc));
});
} else {
// z = from + sum_j b[j] x[j]. The accumulator is seeded from another field,
// so z is written without being read and the caller needs no copy into it.
conformable(*from,z);
GRID_ASSERT(from!=&z);
const Lattice<vobj> &y = *from; // named: autoView is a macro, *p.View() misparses
autoView(y_v,y,AcceleratorRead);
autoView(z_v,z,AcceleratorWriteDiscard);
accelerator_for(ss,sites,nsimd,{
auto acc = coalescedRead(y_v[ss]);
for(int j=0;j<B;j++) if ( j<m ) acc = acc + pack.b[j]*coalescedRead(pack.v(j)[ss]);
coalescedWrite(z_v[ss],acc);
if ( do_norm ) coalescedWrite(inner_tmp_v[ss],innerProduct(acc,acc));
});
}
for(int j=0;j<m;j++) h_v[j].ViewClose(); for(int j=0;j<m;j++) h_v[j].ViewClose();
} }
@@ -931,7 +948,8 @@ void axpyMultiChunk(Lattice<vobj> &z,const ComplexD *b,
// (last) pass. Chunks of 16 for windows longer than 16. // (last) pass. Chunks of 16 for windows longer than 16.
template<class vobj> template<class vobj>
RealD axpyMultiNormImpl(Lattice<vobj> &z,const std::vector<ComplexD> &b, RealD axpyMultiNormImpl(Lattice<vobj> &z,const std::vector<ComplexD> &b,
const std::vector<const Lattice<vobj>*> &x,int do_norm) const std::vector<const Lattice<vobj>*> &x,int do_norm,
const Lattice<vobj> *from=nullptr)
{ {
typedef decltype(innerProduct(vobj(),vobj())) inner_t; typedef decltype(innerProduct(vobj(),vobj())) inner_t;
int m = x.size(); int m = x.size();
@@ -943,6 +961,7 @@ RealD axpyMultiNormImpl(Lattice<vobj> &z,const std::vector<ComplexD> &b,
inner_t *inner_tmp_v = &inner_tmp[0]; inner_t *inner_tmp_v = &inner_tmp[0];
if ( m==0 ) { if ( m==0 ) {
if ( from ) z = *from;
if ( do_norm ) return norm2(z); if ( do_norm ) return norm2(z);
return 0.0; return 0.0;
} }
@@ -951,10 +970,12 @@ RealD axpyMultiNormImpl(Lattice<vobj> &z,const std::vector<ComplexD> &b,
int last = (j0+mm>=m); int last = (j0+mm>=m);
std::vector<const Lattice<vobj>*> sub(x.begin()+j0,x.begin()+j0+mm); std::vector<const Lattice<vobj>*> sub(x.begin()+j0,x.begin()+j0+mm);
int dn = do_norm && last; int dn = do_norm && last;
if ( mm<=2 ) axpyMultiChunk<2> (z,&b[j0],sub,mm,dn,inner_tmp_v); // Only the first chunk is seeded from `from`; later chunks accumulate into z.
else if ( mm<=4 ) axpyMultiChunk<4> (z,&b[j0],sub,mm,dn,inner_tmp_v); const Lattice<vobj> *f = (j0==0) ? from : nullptr;
else if ( mm<=8 ) axpyMultiChunk<8> (z,&b[j0],sub,mm,dn,inner_tmp_v); if ( mm<=2 ) axpyMultiChunk<2> (z,&b[j0],sub,mm,dn,inner_tmp_v,f);
else axpyMultiChunk<16>(z,&b[j0],sub,mm,dn,inner_tmp_v); else if ( mm<=4 ) axpyMultiChunk<4> (z,&b[j0],sub,mm,dn,inner_tmp_v,f);
else if ( mm<=8 ) axpyMultiChunk<8> (z,&b[j0],sub,mm,dn,inner_tmp_v,f);
else axpyMultiChunk<16>(z,&b[j0],sub,mm,dn,inner_tmp_v,f);
} }
RealD nrm = 0.0; RealD nrm = 0.0;
@@ -969,6 +990,15 @@ void axpyMulti(Lattice<vobj> &z,const std::vector<ComplexD> &b,const std::vector
{ {
axpyMultiNormImpl(z,b,x,0); axpyMultiNormImpl(z,b,x,0);
} }
// z = y + sum_j b[j] x[j]. Same single pass as axpyMulti, but the accumulator
// starts from y, so a caller whose z would otherwise have to be primed with a
// copy of y does not make that copy.
template<class vobj>
void axpyMultiFrom(Lattice<vobj> &z,const Lattice<vobj> &y,
const std::vector<ComplexD> &b,const std::vector<const Lattice<vobj>*> &x)
{
axpyMultiNormImpl(z,b,x,0,&y);
}
template<class vobj> template<class vobj>
RealD axpyMultiNorm(Lattice<vobj> &z,const std::vector<ComplexD> &b,const std::vector<const Lattice<vobj>*> &x) RealD axpyMultiNorm(Lattice<vobj> &z,const std::vector<ComplexD> &b,const std::vector<const Lattice<vobj>*> &x)
{ {
+323 -247
View File
@@ -800,6 +800,39 @@ void localCopyRegion(const Lattice<vobj> &From,Lattice<vobj> & To,Coordinate Fro
autoView(from_v,From,AcceleratorRead); autoView(from_v,From,AcceleratorRead);
autoView(to_v,To,AcceleratorWrite); autoView(to_v,To,AcceleratorWrite);
if constexpr ( vobj::Nsimd() == 1 ) {
// Unvectorised: there is one lane, so the lane indices are identically zero
// and the internal word index is free to carry the thread. Threading over
// (site,word) with the word fastest makes consecutive threads read and write
// consecutive elements; one thread per site instead leaves them `words`
// apart, which is what made this uncoalesced. The site count and the word
// count are folded into one range so the large product lands on the only
// block dimension that can hold it.
accelerator_for(ss,nsite*words,1,{
int w = ss % words;
uint64_t idx = ss / words;
Coordinate from_coor, to_coor, base;
Lexicographic::CoorFromIndex(base,idx,RegionSize);
for(int i=0;i<nd;i++){
from_coor[i] = base[i] + FromLowerLeft[i];
to_coor[i] = base[i] + ToLowerLeft[i];
}
// rdimensions == ldimensions when unvectorised and the region is local,
// so the oSite sums need no modulo and there is no lane term.
int from_oidx = 0; for(int d=0;d<nd;d++) from_oidx+=f_ostride[d]*from_coor[d];
int to_oidx = 0; for(int d=0;d<nd;d++) to_oidx +=t_ostride[d]*to_coor[d];
const vector_type* from = (const vector_type *)&from_v[from_oidx];
vector_type* to = (vector_type *)&to_v[to_oidx];
to[w] = from[w];
});
} else {
accelerator_for(idx,nsite,1,{ accelerator_for(idx,nsite,1,{
Coordinate from_coor, to_coor, base; Coordinate from_coor, to_coor, base;
@@ -815,13 +848,15 @@ void localCopyRegion(const Lattice<vobj> &From,Lattice<vobj> & To,Coordinate Fro
const vector_type* from = (const vector_type *)&from_v[from_oidx]; const vector_type* from = (const vector_type *)&from_v[from_oidx];
vector_type* to = (vector_type *)&to_v[to_oidx]; vector_type* to = (vector_type *)&to_v[to_oidx];
scalar_type stmp; scalar_type stmp;
for(int w=0;w<words;w++){ for(int w=0;w<words;w++){
stmp = getlane(from[w], from_lane); stmp = getlane(from[w], from_lane);
putlane(to[w], stmp, to_lane); putlane(to[w], stmp, to_lane);
} }
}); });
}
} }
template<class vobj> template<class vobj>
@@ -1515,174 +1550,277 @@ void precisionChange(Lattice<VobjOut> &out, const Lattice<VobjIn> &in){
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Communicate between grids // Communicate between grids
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////
// SIMPLE CASE: // Grid_split / Grid_unsplit
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// Mesh of nodes (2x2) ; subdivide to 1x1 subdivisions // The split grid divides the full grid's processor layout Pf into partitions of layout
// Ps; ratio R = Pf/Ps per dimension, nvector = prod R partitions. A rank at full processor
// coordinate c belongs to partition q = c/Ps at split coordinate s = c%Ps, and partition q
// holds vector index lex(q,R) (GridSplitVectorIndex). Local volumes satisfy
// Ls = Lf * R per dimension.
// //
// Lex ord: // Every full-grid rank's local block of field v lies wholly inside one split-grid rank's
// N0 va0 vb0 vc0 vd0 N1 va1 vb1 vc1 vd1 // local volume, in the sub-box at offset o = c%R (units of Lf), on the rank of partition
// N2 va2 vb2 vc2 vd2 N3 va3 vb3 vc3 vd3 // q(v) at split coordinate c/R. The redistribution is therefore nvector whole-block
// point-to-point exchanges, nothing finer, and no data moves twice. Step k (a coordinate in
// the R box) pairs every rank with one sender and one receiver:
// //
// Ratio = full[dim] / split[dim] // send field q = (c%R + k)%R to full coordinate q*Ps + c/R
// recv sub-box o = (c/Ps - k)%R from full coordinate (c%Ps)*R + o
// //
// For each dimension do an all to all; get Nvec -> Nvec / ratio // Within a step the send map is a permutation of the ranks and the receive map is its
// Ldim -> Ldim * ratio // inverse, so each step is one symmetric SendToRecvFrom per rank; a rank paired with
// LocalVol -> LocalVol * ratio // itself copies locally. Grid_unsplit runs the same steps with the roles exchanged.
// full AllToAll(0)
// N0 va0 vb0 va1 vb1 N1 vc0 vd0 vc1 vd1
// N2 va2 vb2 va3 vb3 N3 vc2 vd2 vc3 vd3
// //
// REARRANGE // Data stays on the device. A block on the full grid is the local volume in lexicographic
// N0 va01 vb01 N1 vc01 vd01 // order; the split grid's local volume is staged box-major (sub-box index, then
// N2 va23 vb23 N3 vc23 vd23 // lexicographic within the sub-box), so every exchanged block is contiguous. Packing and
// // unpacking are accelerator kernels. Buffers are deviceVectors: the staged split volume
// full AllToAll(1) // Not what is wanted. FIXME // plus one block. Without ACCELERATOR_AWARE_MPI each block is staged through host memory.
// N0 va01 va23 N1 vc01 vc23 /////////////////////////////////////////////////////////////////////////////////////////////
// N2 vb01 vb23 N3 vd01 vd23
// // Scalar sites of in, written to buf in box-major order: sub-boxes of extent bdims taken
// REARRANGE // lexicographically, sites lexicographically within each. bdims = local dimensions gives
// N0 va0123 N1 vc0123 // plain lexicographic order.
// N2 vb0123 N3 vd0123 template<class vobj>
// void GridSplitUnvectorise(const Lattice<vobj> &in,typename vobj::scalar_object *buf,const Coordinate &bdims)
// Must also rearrange data to get into the NEW lex order of grid at each stage. Some kind of "insert/extract". {
// NB: Easiest to programme if keep in lex order. GridBase *grid = in.Grid();
/* int nd = grid->_ndimension;
* Let chunk = (fvol*nvec)/sP be size of a chunk. ( Divide lexico vol * nvec into fP/sP = M chunks ) Coordinate rdims = grid->_rdimensions;
* Coordinate simd = grid->_simd_layout;
* 2nd A2A (over sP nodes; subdivide the fP into sP chunks of M) Coordinate nbox(nd);
* int64_t bvol = 1;
* node 0 1st chunk of node 0M..(1M-1); 2nd chunk of node 0M..(1M-1).. data chunk x M x sP = fL / sP * M * sP = fL * M growth for(int d=0;d<nd;d++){
* node 1 1st chunk of node 1M..(2M-1); 2nd chunk of node 1M..(2M-1).. GRID_ASSERT( (grid->_ldimensions[d] % bdims[d]) == 0 );
* node 2 1st chunk of node 2M..(3M-1); 2nd chunk of node 2M..(3M-1).. nbox[d] = grid->_ldimensions[d] / bdims[d];
* node 3 1st chunk of node 3M..(3M-1); 2nd chunk of node 2M..(3M-1).. bvol *= bdims[d];
* etc... }
*/ const int Nsimd = grid->Nsimd();
autoView( in_v , in, AcceleratorRead);
accelerator_for(ss, grid->oSites(), 1, {
Coordinate ocoor(nd);
Coordinate icoor(nd);
Coordinate bcoor(nd);
Coordinate wcoor(nd);
Lexicographic::CoorFromIndex(ocoor,ss,rdims);
for(int lane=0;lane<Nsimd;lane++){
Lexicographic::CoorFromIndex(icoor,lane,simd);
for(int d=0;d<nd;d++){
int l = ocoor[d] + rdims[d]*icoor[d];
bcoor[d] = l / bdims[d];
wcoor[d] = l % bdims[d];
}
int64_t b;
int64_t w;
Lexicographic::IndexFromCoor(bcoor,b,nbox);
Lexicographic::IndexFromCoor(wcoor,w,bdims);
buf[b*bvol+w] = extractLane(lane,in_v[ss]);
}
});
}
// Inverse of GridSplitUnvectorise: every site of out is written
template<class vobj>
void GridSplitVectorise(const typename vobj::scalar_object *buf,Lattice<vobj> &out,const Coordinate &bdims)
{
GridBase *grid = out.Grid();
int nd = grid->_ndimension;
Coordinate rdims = grid->_rdimensions;
Coordinate simd = grid->_simd_layout;
Coordinate nbox(nd);
int64_t bvol = 1;
for(int d=0;d<nd;d++){
GRID_ASSERT( (grid->_ldimensions[d] % bdims[d]) == 0 );
nbox[d] = grid->_ldimensions[d] / bdims[d];
bvol *= bdims[d];
}
const int Nsimd = grid->Nsimd();
autoView( out_v , out, AcceleratorWriteDiscard);
accelerator_for(ss, grid->oSites(), 1, {
Coordinate ocoor(nd);
Coordinate icoor(nd);
Coordinate bcoor(nd);
Coordinate wcoor(nd);
Lexicographic::CoorFromIndex(ocoor,ss,rdims);
for(int lane=0;lane<Nsimd;lane++){
Lexicographic::CoorFromIndex(icoor,lane,simd);
for(int d=0;d<nd;d++){
int l = ocoor[d] + rdims[d]*icoor[d];
bcoor[d] = l / bdims[d];
wcoor[d] = l % bdims[d];
}
int64_t b;
int64_t w;
Lexicographic::IndexFromCoor(bcoor,b,nbox);
Lexicographic::IndexFromCoor(wcoor,w,bdims);
insertLane(lane,out_v[ss],buf[b*bvol+w]);
}
});
}
// Step schedule shared by Grid_split and Grid_unsplit; see the description above
class GridSplitSchedule
{
public:
int nd;
int nvector;
Coordinate ratio; // R
Coordinate fldims; // full grid local dimensions = sub-box extent on the split grid
GridBase *full_grid;
// Step k: this rank sends field Field(k) to rank FieldRank(k) and receives sub-box
// Box(k) from rank BoxRank(k) (Grid_split); Grid_unsplit reverses both
std::vector<int> Field;
std::vector<int> FieldRank;
std::vector<int> Box;
std::vector<int> BoxRank;
GridSplitSchedule(GridBase *_full_grid,GridBase *split_grid) : full_grid(_full_grid)
{
nd = full_grid->_ndimension;
GRID_ASSERT(split_grid->_ndimension == nd);
ratio.resize(nd);
fldims = full_grid->_ldimensions;
Coordinate sprocs = split_grid->_processors;
Coordinate fcoor = full_grid->_processor_coor;
nvector = 1;
for(int d=0;d<nd;d++){
GRID_ASSERT(full_grid->_gdimensions[d] == split_grid->_gdimensions[d]);
GRID_ASSERT(full_grid->_fdimensions[d] == split_grid->_fdimensions[d]);
GRID_ASSERT( (full_grid->_processors[d] % sprocs[d]) == 0 );
ratio[d] = full_grid->_processors[d] / sprocs[d];
GRID_ASSERT(split_grid->_ldimensions[d] == fldims[d]*ratio[d]);
nvector *= ratio[d];
}
GRID_ASSERT(nvector*split_grid->_Nprocessors == full_grid->_Nprocessors);
Field.resize(nvector);
FieldRank.resize(nvector);
Box.resize(nvector);
BoxRank.resize(nvector);
for(int k=0;k<nvector;k++){
Coordinate kc(nd);
Coordinate q(nd);
Coordinate dest(nd);
Coordinate o(nd);
Coordinate from(nd);
Lexicographic::CoorFromIndex(kc,k,ratio);
for(int d=0;d<nd;d++){
q[d] = (fcoor[d] % ratio[d] + kc[d]) % ratio[d];
dest[d] = q[d]*sprocs[d] + fcoor[d] / ratio[d];
o[d] = (fcoor[d] / sprocs[d] - kc[d] + ratio[d]) % ratio[d];
from[d] = (fcoor[d] % sprocs[d])*ratio[d] + o[d];
}
Lexicographic::IndexFromCoor(q,Field[k],ratio);
Lexicographic::IndexFromCoor(o,Box[k],ratio);
FieldRank[k] = full_grid->RankFromProcessorCoor(dest);
BoxRank[k] = full_grid->RankFromProcessorCoor(from);
}
}
// One step's exchange; returns the bytes that left this rank
uint64_t Exchange(void *xmit,int dest,void *recv,int from,uint64_t bytes)
{
int me = full_grid->ThisRank();
// The send map is a permutation and the receive map its inverse: self pairs with self
GRID_ASSERT( (dest == me) == (from == me) );
if ( dest == me ) {
acceleratorCopyDeviceToDevice(xmit,recv,bytes);
return 0;
}
GRID_ASSERT( (bytes % sizeof(int32_t)) == 0 );
GRID_ASSERT( bytes/sizeof(int32_t) < (1ULL<<31) ); // SendToRecvFrom counts int32 words
#ifdef ACCELERATOR_AWARE_MPI
full_grid->SendToRecvFrom(xmit,dest,recv,from,bytes);
#else
std::vector<char> hxmit(bytes);
std::vector<char> hrecv(bytes);
acceleratorCopyFromDevice(xmit,&hxmit[0],bytes);
full_grid->SendToRecvFrom(&hxmit[0],dest,&hrecv[0],from,bytes);
acceleratorCopyToDevice(&hrecv[0],recv,bytes);
#endif
return bytes;
}
};
// Common body: full[v] may alias one another (the single field overload)
template<class Vobj> template<class Vobj>
void Grid_split(std::vector<Lattice<Vobj> > & full,Lattice<Vobj> & split) void Grid_split(const std::vector<const Lattice<Vobj> *> &full,Lattice<Vobj> &split)
{ {
typedef typename Vobj::scalar_object Sobj; typedef typename Vobj::scalar_object Sobj;
int full_vecs = full.size(); GRID_ASSERT(full.size()>=1);
GridBase *full_grid = full[0]->Grid();
GRID_ASSERT(full_vecs>=1);
GridBase * full_grid = full[0].Grid();
GridBase *split_grid = split.Grid(); GridBase *split_grid = split.Grid();
int ndim = full_grid->_ndimension; int cb = full[0]->Checkerboard();
int full_nproc = full_grid->_Nprocessors;
int split_nproc =split_grid->_Nprocessors;
////////////////////////////////
// Checkerboard management
////////////////////////////////
int cb = full[0].Checkerboard();
split.Checkerboard() = cb; split.Checkerboard() = cb;
for(int n=0;n<full.size();n++){
////////////////////////////// GRID_ASSERT(full[n]->Grid() == full_grid);
// Checks GRID_ASSERT(full[n]->Checkerboard() == cb);
//////////////////////////////
GRID_ASSERT(full_grid->_ndimension==split_grid->_ndimension);
for(int n=0;n<full_vecs;n++){
GRID_ASSERT(full[n].Checkerboard() == cb);
for(int d=0;d<ndim;d++){
GRID_ASSERT(full[n].Grid()->_gdimensions[d]==split.Grid()->_gdimensions[d]);
GRID_ASSERT(full[n].Grid()->_fdimensions[d]==split.Grid()->_fdimensions[d]);
}
} }
int nvector =full_nproc/split_nproc; double t_start = usecond();
GRID_ASSERT(nvector*split_nproc==full_nproc); GridSplitSchedule sched(full_grid,split_grid);
GRID_ASSERT(nvector == full_vecs); GRID_ASSERT(sched.nvector == full.size());
Coordinate ratio(ndim); uint64_t fvol = full_grid->lSites();
for(int d=0;d<ndim;d++){ uint64_t bytes = fvol*sizeof(Sobj);
ratio[d] = full_grid->_processors[d]/ split_grid->_processors[d]; deviceVector<Sobj> staged(split_grid->lSites());
deviceVector<Sobj> block(fvol);
Sobj *staged_p = &staged[0];
Sobj *block_p = &block[0];
double t_alloc = usecond() - t_start;
double t_pack = 0;
double t_comm = 0;
uint64_t offrank = 0;
for(int k=0;k<sched.nvector;k++){
double t0 = usecond();
GridSplitUnvectorise(*full[sched.Field[k]],block_p,full_grid->_ldimensions);
double t1 = usecond();
offrank += sched.Exchange((void *)block_p,sched.FieldRank[k],
(void *)&staged_p[sched.Box[k]*fvol],sched.BoxRank[k],bytes);
double t2 = usecond();
t_pack += t1-t0;
t_comm += t2-t1;
} }
uint64_t lsites = full_grid->lSites(); double t0 = usecond();
uint64_t sz = lsites * nvector; GridSplitVectorise(staged_p,split,sched.fldims);
std::vector<Sobj> tmpdata(sz); double t_unpack = usecond() - t0;
std::vector<Sobj> alldata(sz); double t_total = usecond() - t_start;
std::vector<Sobj> scalardata(lsites);
for(int v=0;v<nvector;v++){ std::cout << GridLogPerformance << "Grid_split: " << sched.nvector << " x " << bytes/1.0e6
unvectorizeToLexOrdArray(scalardata,full[v]); << " MB/rank total " << t_total/1.0e6 << " s : alloc " << t_alloc/1.0e6
thread_for(site,lsites,{ << " pack " << t_pack/1.0e6
alldata[v*lsites+site] = scalardata[site]; << " exchange " << t_comm/1.0e6 << " (" << offrank/1.0e6 << " MB off rank, "
}); << offrank/(t_comm+1.0e-9)/1.0e3 << " GB/s)"
<< " unpack " << t_unpack/1.0e6 << std::endl;
// Staging buffers are still live here, so this is the high-water point of the call
if ( GridLogPerformance.isActive() ) {
HostMemoryReport(full_grid,GridLogPerformance,"Grid_split");
} }
int nvec = nvector; // Counts down to 1 as we collapse dims
Coordinate ldims = full_grid->_ldimensions;
for(int d=ndim-1;d>=0;d--){
if ( ratio[d] != 1 ) {
full_grid ->AllToAll(d,alldata,tmpdata);
if ( split_grid->_processors[d] > 1 ) {
alldata=tmpdata;
split_grid->AllToAll(d,alldata,tmpdata);
}
auto rdims = ldims;
auto M = ratio[d];
auto rsites= lsites*M;// increases rsites by M
nvec /= M; // Reduce nvec by subdivision factor
rdims[d] *= M; // increase local dim by same factor
int sP = split_grid->_processors[d];
int fP = full_grid->_processors[d];
int fvol = lsites;
int chunk = (nvec*fvol)/sP; GRID_ASSERT(chunk*sP == nvec*fvol);
// Loop over reordered data post A2A
thread_for(c, chunk, {
Coordinate coor(ndim);
for(int m=0;m<M;m++){
for(int s=0;s<sP;s++){
// addressing; use lexico
int lex_r;
uint64_t lex_c = c+chunk*m+chunk*M*s;
uint64_t lex_fvol_vec = c+chunk*s;
uint64_t lex_fvol = lex_fvol_vec%fvol;
uint64_t lex_vec = lex_fvol_vec/fvol;
// which node sets an adder to the coordinate
Lexicographic::CoorFromIndex(coor, lex_fvol, ldims);
coor[d] += m*ldims[d];
Lexicographic::IndexFromCoor(coor, lex_r, rdims);
lex_r += lex_vec * rsites;
// LexicoFind coordinate & vector number within split lattice
alldata[lex_r] = tmpdata[lex_c];
}
}
});
ldims[d]*= ratio[d];
lsites *= ratio[d];
}
}
vectorizeFromLexOrdArray(alldata,split);
} }
template<class Vobj>
void Grid_split(std::vector<Lattice<Vobj> > & full,Lattice<Vobj> & split)
{
std::vector<const Lattice<Vobj> *> full_p(full.size());
for(int n=0;n<full.size();n++){
full_p[n] = &full[n];
}
Grid_split(full_p,split);
}
// The same field to every partition
template<class Vobj> template<class Vobj>
void Grid_split(Lattice<Vobj> &full,Lattice<Vobj> & split) void Grid_split(Lattice<Vobj> &full,Lattice<Vobj> & split)
{ {
int nvector = full.Grid()->_Nprocessors / split.Grid()->_Nprocessors; int nvector = full.Grid()->_Nprocessors / split.Grid()->_Nprocessors;
std::vector<Lattice<Vobj> > full_v(nvector,full.Grid()); std::vector<const Lattice<Vobj> *> full_p(nvector,&full);
for(int n=0;n<nvector;n++){ Grid_split(full_p,split);
full_v[n] = full;
}
Grid_split(full_v,split);
} }
template<class Vobj> template<class Vobj>
@@ -1690,119 +1828,57 @@ void Grid_unsplit(std::vector<Lattice<Vobj> > & full,Lattice<Vobj> & split)
{ {
typedef typename Vobj::scalar_object Sobj; typedef typename Vobj::scalar_object Sobj;
int full_vecs = full.size(); GRID_ASSERT(full.size()>=1);
GridBase *full_grid = full[0].Grid();
GRID_ASSERT(full_vecs>=1);
GridBase * full_grid = full[0].Grid();
GridBase *split_grid = split.Grid(); GridBase *split_grid = split.Grid();
int ndim = full_grid->_ndimension;
int full_nproc = full_grid->_Nprocessors;
int split_nproc =split_grid->_Nprocessors;
////////////////////////////////
// Checkerboard management
////////////////////////////////
int cb = full[0].Checkerboard(); int cb = full[0].Checkerboard();
split.Checkerboard() = cb; split.Checkerboard() = cb;
for(int n=0;n<full.size();n++){
////////////////////////////// GRID_ASSERT(full[n].Grid() == full_grid);
// Checks
//////////////////////////////
GRID_ASSERT(full_grid->_ndimension==split_grid->_ndimension);
for(int n=0;n<full_vecs;n++){
GRID_ASSERT(full[n].Checkerboard() == cb); GRID_ASSERT(full[n].Checkerboard() == cb);
for(int d=0;d<ndim;d++){
GRID_ASSERT(full[n].Grid()->_gdimensions[d]==split.Grid()->_gdimensions[d]);
GRID_ASSERT(full[n].Grid()->_fdimensions[d]==split.Grid()->_fdimensions[d]);
}
} }
int nvector =full_nproc/split_nproc; double t_start = usecond();
GRID_ASSERT(nvector*split_nproc==full_nproc); GridSplitSchedule sched(full_grid,split_grid);
GRID_ASSERT(nvector == full_vecs); GRID_ASSERT(sched.nvector == full.size());
Coordinate ratio(ndim); uint64_t fvol = full_grid->lSites();
for(int d=0;d<ndim;d++){ uint64_t bytes = fvol*sizeof(Sobj);
ratio[d] = full_grid->_processors[d]/ split_grid->_processors[d]; deviceVector<Sobj> staged(split_grid->lSites());
deviceVector<Sobj> block(fvol);
Sobj *staged_p = &staged[0];
Sobj *block_p = &block[0];
double t_alloc = usecond() - t_start;
double t0 = usecond();
GridSplitUnvectorise(split,staged_p,sched.fldims);
double t_pack = usecond() - t0;
double t_comm = 0;
double t_unpack = 0;
uint64_t offrank = 0;
for(int k=0;k<sched.nvector;k++){
double t1 = usecond();
offrank += sched.Exchange((void *)&staged_p[sched.Box[k]*fvol],sched.BoxRank[k],
(void *)block_p,sched.FieldRank[k],bytes);
double t2 = usecond();
GridSplitVectorise(block_p,full[sched.Field[k]],full_grid->_ldimensions);
double t3 = usecond();
t_comm += t2-t1;
t_unpack += t3-t2;
} }
double t_total = usecond() - t_start;
uint64_t lsites = full_grid->lSites(); std::cout << GridLogPerformance << "Grid_unsplit: " << sched.nvector << " x " << bytes/1.0e6
uint64_t sz = lsites * nvector; << " MB/rank total " << t_total/1.0e6 << " s : alloc " << t_alloc/1.0e6
std::vector<Sobj> tmpdata(sz); << " pack " << t_pack/1.0e6
std::vector<Sobj> alldata(sz); << " exchange " << t_comm/1.0e6 << " (" << offrank/1.0e6 << " MB off rank, "
std::vector<Sobj> scalardata(lsites); << offrank/(t_comm+1.0e-9)/1.0e3 << " GB/s)"
<< " unpack " << t_unpack/1.0e6 << std::endl;
unvectorizeToLexOrdArray(alldata,split); // Staging buffers are still live here, so this is the high-water point of the call
if ( GridLogPerformance.isActive() ) {
///////////////////////////////////////////////////////////////// HostMemoryReport(full_grid,GridLogPerformance,"Grid_unsplit");
// Start from split grid and work towards full grid
/////////////////////////////////////////////////////////////////
int nvec = 1;
uint64_t rsites = split_grid->lSites();
Coordinate rdims = split_grid->_ldimensions;
for(int d=0;d<ndim;d++){
if ( ratio[d] != 1 ) {
auto M = ratio[d];
int sP = split_grid->_processors[d];
int fP = full_grid->_processors[d];
auto ldims = rdims; ldims[d] /= M; // Decrease local dims by same factor
auto lsites= rsites/M; // Decreases rsites by M
int fvol = lsites;
int chunk = (nvec*fvol)/sP; GRID_ASSERT(chunk*sP == nvec*fvol);
{
// Loop over reordered data post A2A
thread_for(c, chunk,{
Coordinate coor(ndim);
for(int m=0;m<M;m++){
for(int s=0;s<sP;s++){
// addressing; use lexico
int lex_r;
uint64_t lex_c = c+chunk*m+chunk*M*s;
uint64_t lex_fvol_vec = c+chunk*s;
uint64_t lex_fvol = lex_fvol_vec%fvol;
uint64_t lex_vec = lex_fvol_vec/fvol;
// which node sets an adder to the coordinate
Lexicographic::CoorFromIndex(coor, lex_fvol, ldims);
coor[d] += m*ldims[d];
Lexicographic::IndexFromCoor(coor, lex_r, rdims);
lex_r += lex_vec * rsites;
// LexicoFind coordinate & vector number within split lattice
tmpdata[lex_c] = alldata[lex_r];
}
}
});
}
if ( split_grid->_processors[d] > 1 ) {
split_grid->AllToAll(d,tmpdata,alldata);
tmpdata=alldata;
}
full_grid ->AllToAll(d,tmpdata,alldata);
rdims[d]/= M;
rsites /= M;
nvec *= M; // Increase nvec by subdivision factor
}
}
lsites = full_grid->lSites();
for(int v=0;v<nvector;v++){
thread_for(site, lsites,{
scalardata[site] = alldata[v*lsites+site];
});
vectorizeFromLexOrdArray(scalardata,full[v]);
} }
} }
+37 -19
View File
@@ -94,7 +94,7 @@ template<class vobj> inline void ScatterSlice(const deviceVector<vobj> &buf,
// FIXME -- can put internal indices into thread loop // FIXME -- can put internal indices into thread loop
auto buf_p = & buf[0]; auto buf_p = & buf[0];
autoView(lat_v, lat, AcceleratorWrite); autoView(lat_v, lat, AcceleratorWrite);
accelerator_for(ss, face_ovol/simd[dim],Nsimd,{ accelerator_forNB(ss, face_ovol/simd[dim],Nsimd,{
// scalar layout won't coalesce // scalar layout won't coalesce
#ifdef GRID_SIMT #ifdef GRID_SIMT
@@ -180,7 +180,7 @@ template<class vobj> inline void GatherSlice(deviceVector<vobj> &buf,
//for cross platform //for cross platform
//For CPU perhaps just run a loop over Nsimd //For CPU perhaps just run a loop over Nsimd
auto buf_p = & buf[0]; auto buf_p = & buf[0];
accelerator_for(ss, face_ovol/simd[dim],Nsimd,{ accelerator_forNB(ss, face_ovol/simd[dim],Nsimd,{
// scalar layout won't coalesce // scalar layout won't coalesce
#ifdef GRID_SIMT #ifdef GRID_SIMT
@@ -303,10 +303,17 @@ public:
template<class vobj> template<class vobj>
inline Lattice<vobj> Exchange(const Lattice<vobj> &in, const CshiftImplBase<vobj> &cshift = CshiftImplDefault<vobj>()) const inline Lattice<vobj> Exchange(const Lattice<vobj> &in, const CshiftImplBase<vobj> &cshift = CshiftImplDefault<vobj>()) const
{ {
GridBase *old_grid = in.Grid(); Coordinate processors=unpadded_grid->_processors;
int dims = old_grid->Nd(); int dims = in.Grid()->Nd();
Lattice<vobj> tmp = in; // An undecomposed dimension is not padded, so Expand on it would only copy
for(int d=0;d<dims;d++){ // its input onto the same grid: those are skipped. The first decomposed
// dimension expands from `in` itself, so the chain needs no seed copy.
int first=-1;
for(int d=0;d<dims;d++) if ( processors[d] > 1 ) { first=d; break; }
if ( first < 0 ) return in; // nothing decomposed: the padded grid IS the input grid
Lattice<vobj> tmp = Expand(first,in,cshift);
for(int d=first+1;d<dims;d++){
if ( processors[d] == 1 ) continue;
tmp = Expand(d,tmp,cshift); // rvalue && assignment tmp = Expand(d,tmp,cshift); // rvalue && assignment
} }
return tmp; return tmp;
@@ -314,10 +321,18 @@ public:
template<class vobj> template<class vobj>
inline Lattice<vobj> ExchangePeriodic(const Lattice<vobj> &in) const inline Lattice<vobj> ExchangePeriodic(const Lattice<vobj> &in) const
{ {
GridBase *old_grid = in.Grid(); Coordinate processors=unpadded_grid->_processors;
int dims = old_grid->Nd(); int dims = in.Grid()->Nd();
Lattice<vobj> tmp = in; // As Exchange: undecomposed dimensions are pure copies and are skipped, and
for(int d=0;d<dims;d++){ // the first decomposed dimension expands from `in` so there is no seed copy.
// On the D+1 mrhs coarse grid (nrhs,1,x,y,z,t) this removes three full
// coarse-field copies per operator application.
int first=-1;
for(int d=0;d<dims;d++) if ( processors[d] > 1 ) { first=d; break; }
if ( first < 0 ) return in; // nothing decomposed: the padded grid IS the input grid
Lattice<vobj> tmp = ExpandPeriodic(first,in);
for(int d=first+1;d<dims;d++){
if ( processors[d] == 1 ) continue;
tmp = ExpandPeriodic(d,tmp); // rvalue && assignment tmp = ExpandPeriodic(d,tmp); // rvalue && assignment
} }
return tmp; return tmp;
@@ -513,12 +528,18 @@ public:
RealD t_tot=-usecond(); RealD t_tot=-usecond();
int plane=0; int plane=0;
for ( int d=0;d < depth ; d ++ ) { for ( int d=0;d < depth ; d ++ ) {
int tag = d*1024 + dimension*2+0;
t=usecond(); t=usecond();
GatherSlice(send_buf,from,d,dimension,plane*buffer_size); plane++; GatherSlice(send_buf,from,d,dimension,plane*buffer_size); plane++;
t_gather+=usecond()-t; t_gather+=usecond()-t;
}
for ( int d=0;d < depth ; d ++ ) {
t=usecond();
GatherSlice(send_buf,from,ld-depth+d,dimension,plane*buffer_size); plane++;
t_gather+= usecond() - t;
}
accelerator_barrier();
for ( int d=0;d < depth ; d ++ ) {
int tag = d*1024 + dimension*2+0;
t=usecond(); t=usecond();
if(d==0) fwd_trace = traceStart("PaddedCellFwdMPI"); if(d==0) fwd_trace = traceStart("PaddedCellFwdMPI");
@@ -533,14 +554,9 @@ public:
(void *)&hrecv_buf[d*buffer_size], recv_from_rank, bytes, tag); (void *)&hrecv_buf[d*buffer_size], recv_from_rank, bytes, tag);
#endif #endif
t_comms+=usecond()-t; t_comms+=usecond()-t;
} }
for ( int d=0;d < depth ; d ++ ) { for ( int d=0;d < depth ; d ++ ) {
int tag = d*1024 + dimension*2+1; int tag = d*1024 + dimension*2+1;
t=usecond();
GatherSlice(send_buf,from,ld-depth+d,dimension,plane*buffer_size); plane++;
t_gather+= usecond() - t;
t=usecond(); t=usecond();
if (d==0) bwd_trace = traceStart("PaddedCellBwdMPI"); if (d==0) bwd_trace = traceStart("PaddedCellBwdMPI");
#ifdef ACCELERATOR_AWARE_MPI #ifdef ACCELERATOR_AWARE_MPI
@@ -603,9 +619,11 @@ public:
for ( int d=0;d < depth ; d ++ ) { for ( int d=0;d < depth ; d ++ ) {
ScatterSlice(recv_buf,to,d,dimension,plane*buffer_size); plane++; ScatterSlice(recv_buf,to,d,dimension,plane*buffer_size); plane++;
} }
accelerator_barrier();
t_scatter+= usecond() - t; t_scatter+= usecond() - t;
t_tot+=usecond(); t_tot+=usecond();
std::cout << GridLogPerformance << "PaddedCell::Expand new timings: gather :" << t_gather/1000 << "ms"<<std::endl; std::cout << GridLogPerformance << "PaddedCell::Expand new timings: gather :" << t_gather/1000 << "ms"<<std::endl;
std::cout << GridLogPerformance << "PaddedCell::Expand new timings: scatter:" << t_scatter/1000 << "ms"<<std::endl; std::cout << GridLogPerformance << "PaddedCell::Expand new timings: scatter:" << t_scatter/1000 << "ms"<<std::endl;
std::cout << GridLogPerformance << "PaddedCell::Expand new timings: copy :" << t_copy/1000 << "ms"<<std::endl; std::cout << GridLogPerformance << "PaddedCell::Expand new timings: copy :" << t_copy/1000 << "ms"<<std::endl;
+227
View File
@@ -0,0 +1,227 @@
/*************************************************************************************
Grid physics library, www.github.com/paboyle/Grid
Source file: ./lib/perfmon/HostMemory.h
Copyright (C) 2026
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
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
#include <sys/resource.h>
#include <unistd.h>
#include <cctype>
#include <fstream>
#ifdef __APPLE__
#include <mach/mach.h>
#endif
NAMESPACE_BEGIN(Grid);
/////////////////////////////////////////////////////////////////////////////////////////////
// Host memory as the OOM killer sees it, plus the device allocator footprint.
// Targets are Apple or Linux; on Apple only the RSS and device fields are filled.
//
// RSS : this process, now and its high-water mark (getrusage).
// Node : MemAvailable and MemTotal from /proc/meminfo (Linux).
// Cgroup : the nearest enclosing memory cgroup with a finite limit (the one the batch
// system enforces), its usage now, its high-water mark and the limit (Linux,
// v2 or v1 hierarchy). Cgroup usage includes page cache, tmpfs and kernel memory
// charged to the job, none of which appear in any process RSS.
// Device : MemoryManager Lattice footprint on the device and its free-block cache. Device
// allocations outside the MemoryManager (comms buffers) are not included.
//
// All values in GB; -1 where the source does not exist on this platform or kernel.
/////////////////////////////////////////////////////////////////////////////////////////////
struct HostMemoryStatus
{
RealD RSS = -1;
RealD RSSPeak = -1;
RealD NodeAvailable = -1;
RealD NodeTotal = -1;
RealD CgroupCurrent = -1;
RealD CgroupPeak = -1;
RealD CgroupLimit = -1;
RealD DeviceLattice = -1;
RealD DeviceCache = -1;
};
#ifndef __APPLE__ // Linux
// One number from a file; -1 if the file is absent or holds no number (e.g. "max")
inline RealD HostMemoryReadBytes(const std::string &file)
{
std::ifstream f(file);
if ( !f.good() ) {
return -1;
}
std::string word;
f >> word;
if ( word.empty() || !isdigit(word[0]) ) {
return -1;
}
return std::stod(word);
}
// Value of a "Key: N kB" line in /proc/meminfo, in bytes; -1 if absent
inline RealD HostMemoryMeminfo(const std::string &key)
{
std::ifstream f("/proc/meminfo");
std::string name;
RealD value;
std::string unit;
while ( f >> name >> value >> unit ) {
if ( name == key + ":" ) {
return value*1024.0;
}
}
return -1;
}
// Fill the cgroup fields from the nearest enclosing memory cgroup with a finite limit, or
// from this process's own cgroup (limit left at -1) if no ancestor has one
inline void HostMemoryCgroup(HostMemoryStatus &s)
{
std::ifstream f("/proc/self/cgroup");
std::string line;
std::string root;
std::string path;
std::string current;
std::string peak;
std::string limit;
while ( std::getline(f,line) ) {
size_t c1 = line.find(':');
size_t c2 = line.find(':',c1+1);
if ( c1 == std::string::npos || c2 == std::string::npos ) {
continue;
}
std::string controllers = line.substr(c1+1,c2-c1-1);
// v2 unified hierarchy: "0::/path"
if ( controllers.empty() && root.empty() ) {
root = "/sys/fs/cgroup";
path = line.substr(c2+1);
current = "memory.current";
peak = "memory.peak";
limit = "memory.max";
}
// v1 memory controller: "N:memory:/path", takes precedence
if ( (","+controllers+",").find(",memory,") != std::string::npos ) {
root = "/sys/fs/cgroup/memory";
path = line.substr(c2+1);
current = "memory.usage_in_bytes";
peak = "memory.max_usage_in_bytes";
limit = "memory.limit_in_bytes";
break;
}
}
if ( root.empty() ) {
return;
}
// v1 reports "no limit" as a number near 2^63
const RealD unlimited = 1.0e18;
std::string dir = path;
while ( true ) {
std::string base = root + dir + "/";
RealD lim = HostMemoryReadBytes(base+limit);
if ( lim > 0 && lim < unlimited ) {
s.CgroupCurrent = HostMemoryReadBytes(base+current)/1.0e9;
s.CgroupPeak = HostMemoryReadBytes(base+peak)/1.0e9;
s.CgroupLimit = lim/1.0e9;
return;
}
if ( dir.empty() || dir == "/" ) {
break;
}
dir = dir.substr(0,dir.rfind('/'));
}
std::string base = root + path + "/";
s.CgroupCurrent = HostMemoryReadBytes(base+current)/1.0e9;
s.CgroupPeak = HostMemoryReadBytes(base+peak)/1.0e9;
}
#endif
// This rank only; no communication
inline HostMemoryStatus HostMemoryQuery(void)
{
HostMemoryStatus s;
struct rusage ru;
getrusage(RUSAGE_SELF,&ru);
#ifdef __APPLE__
s.RSSPeak = ru.ru_maxrss/1.0e9; // bytes on macOS
mach_task_basic_info_data_t info;
mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
if ( task_info(mach_task_self(),MACH_TASK_BASIC_INFO,(task_info_t)&info,&count) == KERN_SUCCESS ) {
s.RSS = info.resident_size/1.0e9;
}
#else // Linux
s.RSSPeak = ru.ru_maxrss*1024.0/1.0e9; // kilobytes on Linux
std::ifstream statm("/proc/self/statm");
long pages = 0;
long resident = 0;
if ( statm >> pages >> resident ) {
s.RSS = resident*(RealD)sysconf(_SC_PAGESIZE)/1.0e9;
}
RealD avail = HostMemoryMeminfo("MemAvailable");
RealD total = HostMemoryMeminfo("MemTotal");
if ( avail >= 0 ) {
s.NodeAvailable = avail/1.0e9;
}
if ( total >= 0 ) {
s.NodeTotal = total/1.0e9;
}
HostMemoryCgroup(s);
#endif
s.DeviceLattice = MemoryManager::DeviceBytes/1.0e9;
s.DeviceCache = MemoryManager::DeviceCacheBytes()/1.0e9;
return s;
}
/////////////////////////////////////////////////////////////////////////////////////////////
// Collective over grid. One line on log: maxima over ranks, except node MemAvailable which
// is the minimum over ranks (the tightest node).
/////////////////////////////////////////////////////////////////////////////////////////////
inline void HostMemoryReport(GridBase *grid,GridLogger &log,const std::string &label)
{
HostMemoryStatus s = HostMemoryQuery();
RealD minus_available = -s.NodeAvailable;
grid->GlobalMax(s.RSS);
grid->GlobalMax(s.RSSPeak);
grid->GlobalMax(minus_available);
grid->GlobalMax(s.NodeTotal);
grid->GlobalMax(s.CgroupCurrent);
grid->GlobalMax(s.CgroupPeak);
grid->GlobalMax(s.CgroupLimit);
grid->GlobalMax(s.DeviceLattice);
grid->GlobalMax(s.DeviceCache);
std::cout << log << "HOSTMEM " << label
<< " : rank RSS " << s.RSS << " peak " << s.RSSPeak
<< " ; node available " << -minus_available << " of " << s.NodeTotal
<< " ; cgroup " << s.CgroupCurrent << " peak " << s.CgroupPeak << " limit " << s.CgroupLimit
<< " ; device lattice " << s.DeviceLattice << " cache " << s.DeviceCache
<< " GB" << std::endl;
}
NAMESPACE_END(Grid);
+1
View File
@@ -53,6 +53,7 @@ struct WilsonImplParams {
AcceleratorVector<Real,Nd> twist_n_2pi_L; AcceleratorVector<Real,Nd> twist_n_2pi_L;
AcceleratorVector<Complex,Nd> boundary_phases; AcceleratorVector<Complex,Nd> boundary_phases;
WilsonImplParams() { WilsonImplParams() {
overlapCommsCompute=true;
dirichlet.resize(0); dirichlet.resize(0);
partialDirichlet=0; partialDirichlet=0;
boundary_phases.resize(Nd, 1.0); boundary_phases.resize(Nd, 1.0);
@@ -81,6 +81,15 @@ public:
mass_minus=_mass_minus; mass_minus=_mass_minus;
SetCoefficientsInternal(_zolo_hi,_gamma,_b,_c); // Reset coeffs SetCoefficientsInternal(_zolo_hi,_gamma,_b,_c); // Reset coeffs
} ; } ;
// Everything SetMass needs, plus the boost, then rebuild the coefficient arrays in clone
void CloneCoefficientsInto(CayleyFermion5D<Impl> &clone)
{
clone.mass_plus = mass_plus;
clone.mass_minus = mass_minus;
clone.qmu = qmu;
clone.SetCoefficientsInternal(_zolo_hi,_gamma,_b,_c);
}
void P(const FermionField &psi, FermionField &chi); void P(const FermionField &psi, FermionField &chi);
void Pdag(const FermionField &psi, FermionField &chi); void Pdag(const FermionField &psi, FermionField &chi);
@@ -189,6 +189,71 @@ public:
void ImportGauge(const GaugeField& _Umu) override; void ImportGauge(const GaugeField& _Umu) override;
// Install already computed clover fields and inverses, and derive the checkerboarded parts
void ImportCloverTerms(const CloverDiagonalField& D,
const CloverTriangleField& T,
const CloverDiagonalField& Dinv,
const CloverTriangleField& Tinv)
{
Diagonal = D;
Triangle = T;
DiagonalInv = Dinv;
TriangleInv = Tinv;
pickCheckerboard(Even, DiagonalEven, Diagonal);
pickCheckerboard(Even, TriangleEven, Triangle);
pickCheckerboard(Odd, DiagonalOdd, Diagonal);
pickCheckerboard(Odd, TriangleOdd, Triangle);
pickCheckerboard(Even, DiagonalInvEven, DiagonalInv);
pickCheckerboard(Even, TriangleInvEven, TriangleInv);
pickCheckerboard(Odd, DiagonalInvOdd, DiagonalInv);
pickCheckerboard(Odd, TriangleInvOdd, TriangleInv);
}
// Exact type only: derived operators must not inherit this
SplitOperator<FermionField> *SplitClone(const Coordinate& mpi_split) override
{
if ( typeid(*this) != typeid(CompactWilsonCloverFermion<Impl,CloverHelpers>) ) {
return nullptr;
}
if ( Impl::isGparity ) {
return nullptr;
}
SplitOperator<FermionField> *split = this->MakeSplitGrids(mpi_split);
// Placeholder links; the doubled field and clover fields are overwritten below.
// Boundary masks are rebuilt by the constructor from global coordinates.
GaugeField Uplaceholder(split->GaugeGrid);
Uplaceholder = Zero();
CompactWilsonCloverFermion<Impl,CloverHelpers> *clone =
new CompactWilsonCloverFermion<Impl,CloverHelpers>(Uplaceholder,
*split->GaugeGrid,
*split->GaugeRBGrid,
this->mass,
csw_r,
csw_t,
cF,
this->anisotropyCoeff,
this->Params);
// The stored coefficients are post-scaled; the constructor would scale them again
clone->csw_r = csw_r;
clone->csw_t = csw_t;
CloverDiagonalField sD(split->GaugeGrid);
CloverTriangleField sT(split->GaugeGrid);
CloverDiagonalField sDinv(split->GaugeGrid);
CloverTriangleField sTinv(split->GaugeGrid);
Grid_split(Diagonal,sD);
Grid_split(Triangle,sT);
Grid_split(DiagonalInv,sDinv);
Grid_split(TriangleInv,sTinv);
clone->ImportCloverTerms(sD,sT,sDinv,sTinv);
this->SplitDoubledGaugeInto(*clone);
split->Matrix = clone;
return split;
}
///////////////////////////////////////////// /////////////////////////////////////////////
// Helpers // Helpers
///////////////////////////////////////////// /////////////////////////////////////////////
+66 -8
View File
@@ -109,10 +109,69 @@ public:
//////////////////////// ////////////////////////
// Derivative interface // Derivative interface
//////////////////////// ////////////////////////
// Interface calls an internal routine // Force on every link of every s-slice, in the form WilsonFermion5D uses:
void DhopDeriv(GaugeField &mat,const FermionField &U,const FermionField &V,int dag) { GRID_ASSERT(0);}; // F5[mu](x,s) = tr_spin[ Btilde(x,s) A(x,s)^dag ]
void DhopDerivOE(GaugeField &mat,const FermionField &U,const FermionField &V,int dag){ GRID_ASSERT(0);}; // Btilde(x,s) = -1/2 U_mu(x,s) (1 -+ gamma_mu) B(x+mu,s), lower sign for dag
void DhopDerivEO(GaugeField &mat,const FermionField &U,const FermionField &V,int dag){ GRID_ASSERT(0);}; // Dhop5 carries no gauge links and contributes nothing.
void DhopDeriv5D(std::vector<GaugeLinkField> &F5,const FermionField &A,const FermionField &B,int dag)
{
RealD sgn= 1.0;
if (dag ) sgn=-1.0;
Gamma::Algebra Gmu [] = {
Gamma::Algebra::GammaX,
Gamma::Algebra::GammaY,
Gamma::Algebra::GammaZ,
Gamma::Algebra::GammaT
};
FermionField tmp(B.Grid());
FermionField Btilde(B.Grid());
for(int mu=0;mu<Nd;mu++){
tmp = Umu[mu] * Cshift(B,mu+1,1);
Btilde = tmp - Gamma(Gmu[mu])*tmp*sgn;
Btilde = -0.5*Btilde;
F5[mu] = TraceIndex<SpinIndex>(outerProduct(Btilde,A));
}
}
// Every slice holds the same 4D gauge field, so the 4D force is the sum over slices
void DhopDeriv(GaugeField &mat,const FermionField &A,const FermionField &B,int dag)
{
std::vector<GaugeLinkField> F5(Nd,_grid);
DhopDeriv5D(F5,A,B,dag);
GaugeLinkField slice(_grid4);
GaugeLinkField sum(_grid4);
for(int mu=0;mu<Nd;mu++){
sum = Zero();
for(int s=0;s<Ls;s++){
ExtractSlice(slice,F5[mu],s,0);
sum = sum + slice;
}
PokeIndex<LorentzIndex>(mat,sum,mu);
}
}
// The checkerboard here is 5D, so a field on one checkerboard touches links at both 4D
// parities: the even-odd forces are returned on the full 4D gauge grid.
int CheckerboardedForce(void) { return 0; };
// A and B on opposite 5D checkerboards, placed in zeroed full fields: the full-lattice
// derivative then picks up exactly the links Meooe uses between them.
void DhopDerivCB(GaugeField &mat,const FermionField &A,const FermionField &B,int dag)
{
GRID_ASSERT(A.Checkerboard() != B.Checkerboard());
FermionField Afull(_grid);
FermionField Bfull(_grid);
Afull = Zero();
Bfull = Zero();
setCheckerboard(Afull,A);
setCheckerboard(Bfull,B);
DhopDeriv(mat,Afull,Bfull,dag);
}
void DhopDerivOE(GaugeField &mat,const FermionField &U,const FermionField &V,int dag){ DhopDerivCB(mat,U,V,dag);};
void DhopDerivEO(GaugeField &mat,const FermionField &U,const FermionField &V,int dag){ DhopDerivCB(mat,U,V,dag);};
/////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////
// non-hermitian hopping term; half cb or both // non-hermitian hopping term; half cb or both
@@ -200,6 +259,7 @@ public:
_grid(&Fgrid), _grid(&Fgrid),
_cbgrid(&Hgrid), _cbgrid(&Hgrid),
_grid4(_Umu.Grid()), _grid4(_Umu.Grid()),
_cbgrid4(nullptr), // the checkerboard is 5D; there is no 4D red-black grid
Umu(Nd,&Fgrid), Umu(Nd,&Fgrid),
UmuEven(Nd,&Hgrid), UmuEven(Nd,&Hgrid),
UmuOdd(Nd,&Hgrid), UmuOdd(Nd,&Hgrid),
@@ -221,10 +281,6 @@ public:
MassField =scalar(-mass); MassField =scalar(-mass);
one =scalar(1.0); one =scalar(1.0);
MassField =where(coor==Integer(Ls-1),MassField,one); MassField =where(coor==Integer(Ls-1),MassField,one);
for(int mu=0;mu<Nd;mu++){
pickCheckerboard(Even,UmuEven[mu],Umu[mu]);
pickCheckerboard(Odd ,UmuOdd[mu],Umu[mu]);
}
pickCheckerboard(Even,MassFieldEven,MassField); pickCheckerboard(Even,MassFieldEven,MassField);
pickCheckerboard(Odd ,MassFieldOdd,MassField); pickCheckerboard(Odd ,MassFieldOdd,MassField);
@@ -239,6 +295,8 @@ public:
for(int s=0;s<this->Ls;s++){ for(int s=0;s<this->Ls;s++){
InsertSlice(U4,Umu[mu],s,0); InsertSlice(U4,Umu[mu],s,0);
} }
pickCheckerboard(Even,UmuEven[mu],Umu[mu]);
pickCheckerboard(Odd ,UmuOdd[mu],Umu[mu]);
} }
} }
@@ -132,6 +132,35 @@ public:
Approx::zolotarev_free(zdata); Approx::zolotarev_free(zdata);
} }
// Exact type only: derived operators must not inherit this
virtual SplitOperator<FermionField> *SplitClone(const Coordinate &mpi_split)
{
if ( typeid(*this) != typeid(DomainWallFermion<Impl>) ) {
return nullptr;
}
if ( this->Dirichlet || Impl::LsVectorised || Impl::isGparity ) {
return nullptr;
}
SplitOperator<FermionField> *split = this->MakeSplitGrids(mpi_split);
// Placeholder links; the doubled field is overwritten below
GaugeField Uplaceholder(split->GaugeGrid);
Uplaceholder = Zero();
DomainWallFermion<Impl> *clone = new DomainWallFermion<Impl>(Uplaceholder,
*split->FermionGrid,
*split->FermionRBGrid,
*split->GaugeGrid,
*split->GaugeRBGrid,
this->mass_plus,
this->M5,
this->Params);
this->CloneCoefficientsInto(*clone);
this->SplitDoubledGaugeInto(*clone);
split->Matrix = clone;
return split;
}
}; };
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
+36
View File
@@ -59,6 +59,38 @@ public:
virtual GridBase *GaugeGrid(void) =0; virtual GridBase *GaugeGrid(void) =0;
virtual GridBase *GaugeRedBlackGrid(void) =0; virtual GridBase *GaugeRedBlackGrid(void) =0;
////////////////////////////////////////////////////////////////
// Split-communicator copies of this operator's gauge and fermion
// grids, returned in a new bundle with no Matrix or Linop yet.
// Partitions have MPI layout mpi_split. Collective.
////////////////////////////////////////////////////////////////
SplitOperator<FermionField> *MakeSplitGrids(const Coordinate &mpi_split)
{
GridCartesian *U = dynamic_cast<GridCartesian *>(GaugeGrid());
GRID_ASSERT(U != nullptr);
GRID_ASSERT(mpi_split.size() == Nd);
double t0 = usecond();
SplitOperator<FermionField> *split = new SplitOperator<FermionField>();
split->GaugeGrid = new GridCartesian(U->FullDimensions(),U->_simd_layout,mpi_split,*U);
split->GaugeRBGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(split->GaugeGrid);
split->Partition = GridSplitVectorIndex(U,split->GaugeGrid);
split->Partitions = U->ProcessorCount()/split->GaugeGrid->ProcessorCount();
if ( FermionGrid()->Nd() == Nd+1 ) {
int Ls = FermionGrid()->_fdimensions[0];
split->FermionGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,split->GaugeGrid);
split->FermionRBGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,split->GaugeGrid);
} else {
split->FermionGrid = split->GaugeGrid;
split->FermionRBGrid = split->GaugeRBGrid;
}
std::cout << GridLogPerformance << "MakeSplitGrids: " << (usecond()-t0)/1.0e6 << " s" << std::endl;
return split;
}
// override multiply // override multiply
virtual void M (const FermionField &in, FermionField &out)=0; virtual void M (const FermionField &in, FermionField &out)=0;
virtual void Mdag (const FermionField &in, FermionField &out)=0; virtual void Mdag (const FermionField &in, FermionField &out)=0;
@@ -88,6 +120,10 @@ public:
virtual void DhopDerivEO(GaugeField &mat,const FermionField &U,const FermionField &V,int dag)=0; virtual void DhopDerivEO(GaugeField &mat,const FermionField &U,const FermionField &V,int dag)=0;
virtual void DhopDerivOE(GaugeField &mat,const FermionField &U,const FermionField &V,int dag)=0; virtual void DhopDerivOE(GaugeField &mat,const FermionField &U,const FermionField &V,int dag)=0;
// 1 if MoeDeriv/MeoDeriv return a force on a single 4D checkerboard (4D red-black),
// 0 if they return it on the full 4D gauge grid (a checkerboard that includes s)
virtual int CheckerboardedForce(void) { return 1; };
virtual void Mdiag (const FermionField &in, FermionField &out) { Mooee(in,out);}; // Same as Mooee applied to both CB's virtual void Mdiag (const FermionField &in, FermionField &out) { Mooee(in,out);}; // Same as Mooee applied to both CB's
virtual void Mdir (const FermionField &in, FermionField &out,int dir,int disp)=0; // case by case Wilson, Clover, Cayley, ContFrac, PartFrac virtual void Mdir (const FermionField &in, FermionField &out,int dir,int disp)=0; // case by case Wilson, Clover, Cayley, ContFrac, PartFrac
virtual void MdirAll(const FermionField &in, std::vector<FermionField> &out)=0; // case by case Wilson, Clover, Cayley, ContFrac, PartFrac virtual void MdirAll(const FermionField &in, std::vector<FermionField> &out)=0; // case by case Wilson, Clover, Cayley, ContFrac, PartFrac
+31
View File
@@ -70,6 +70,37 @@ public:
} }
// Exact type only: derived operators (e.g. ScaledShamirFermion) must not inherit this
virtual SplitOperator<FermionField> *SplitClone(const Coordinate &mpi_split)
{
if ( typeid(*this) != typeid(MobiusFermion<Impl>) ) {
return nullptr;
}
if ( this->Dirichlet || Impl::LsVectorised || Impl::isGparity ) {
return nullptr;
}
SplitOperator<FermionField> *split = this->MakeSplitGrids(mpi_split);
// Placeholder links; the doubled field is overwritten below
GaugeField Uplaceholder(split->GaugeGrid);
Uplaceholder = Zero();
MobiusFermion<Impl> *clone = new MobiusFermion<Impl>(Uplaceholder,
*split->FermionGrid,
*split->FermionRBGrid,
*split->GaugeGrid,
*split->GaugeRBGrid,
this->mass_plus,
this->M5,
this->_b,
this->_c,
this->Params);
this->CloneCoefficientsInto(*clone);
this->SplitDoubledGaugeInto(*clone);
split->Matrix = clone;
return split;
}
}; };
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
@@ -91,6 +91,60 @@ public:
// Derivative parts unpreconditioned pseudofermions // Derivative parts unpreconditioned pseudofermions
void MDeriv(GaugeField &force, const FermionField &X, const FermionField &Y, int dag); void MDeriv(GaugeField &force, const FermionField &X, const FermionField &Y, int dag);
// Install already computed clover term and inverse, and derive the checkerboarded parts
void ImportCloverTerms(const CloverField &C, const CloverField &Cinv)
{
CloverTerm = C;
CloverTermInv = Cinv;
pickCheckerboard(Even, CloverTermEven, CloverTerm);
pickCheckerboard(Odd, CloverTermOdd, CloverTerm);
pickCheckerboard(Even, CloverTermDagEven, adj(CloverTerm));
pickCheckerboard(Odd, CloverTermDagOdd, adj(CloverTerm));
pickCheckerboard(Even, CloverTermInvEven, CloverTermInv);
pickCheckerboard(Odd, CloverTermInvOdd, CloverTermInv);
pickCheckerboard(Even, CloverTermInvDagEven, adj(CloverTermInv));
pickCheckerboard(Odd, CloverTermInvDagOdd, adj(CloverTermInv));
}
// Exact type only: derived operators must not inherit this
virtual SplitOperator<FermionField> *SplitClone(const Coordinate &mpi_split)
{
if ( typeid(*this) != typeid(WilsonCloverFermion<Impl,CloverHelpers>) ) {
return nullptr;
}
if ( Impl::isGparity ) {
return nullptr;
}
SplitOperator<FermionField> *split = this->MakeSplitGrids(mpi_split);
// Placeholder links; the doubled field and clover terms are overwritten below
GaugeField Uplaceholder(split->GaugeGrid);
Uplaceholder = Zero();
WilsonCloverFermion<Impl,CloverHelpers> *clone =
new WilsonCloverFermion<Impl,CloverHelpers>(Uplaceholder,
*split->GaugeGrid,
*split->GaugeRBGrid,
this->mass,
csw_r,
csw_t,
this->anisotropyCoeff,
this->Params);
// The stored coefficients are post-scaled; the constructor would scale them again
clone->csw_r = csw_r;
clone->csw_t = csw_t;
CloverField sC(split->GaugeGrid);
CloverField sCinv(split->GaugeGrid);
Grid_split(CloverTerm,sC);
Grid_split(CloverTermInv,sCinv);
clone->ImportCloverTerms(sC,sCinv);
this->SplitDoubledGaugeInto(*clone);
split->Matrix = clone;
return split;
}
public: public:
// here fixing the 4 dimensions, make it more general? // here fixing the 4 dimensions, make it more general?
+42
View File
@@ -147,6 +147,48 @@ public:
// DoubleStore impl dependent // DoubleStore impl dependent
void ImportGauge(const GaugeField &_Umu); void ImportGauge(const GaugeField &_Umu);
// Install an already doubled gauge field (phases, twists, anisotropy and -1/2 applied)
void ImportDoubledGauge(const DoubledGaugeField &U)
{
Umu = U;
pickCheckerboard(Even,UmuEven,Umu);
pickCheckerboard(Odd ,UmuOdd ,Umu);
}
// Redistribute this operator's doubled gauge field onto clone's split grid. Collective.
void SplitDoubledGaugeInto(WilsonFermion<Impl> &clone)
{
DoubledGaugeField sUmu(clone.GaugeGrid());
Grid_split(Umu,sUmu);
clone.ImportDoubledGauge(sUmu);
}
// Exact type only: derived operators (e.g. WilsonTMFermion) must not inherit this
virtual SplitOperator<FermionField> *SplitClone(const Coordinate &mpi_split)
{
if ( typeid(*this) != typeid(WilsonFermion<Impl>) ) {
return nullptr;
}
if ( Impl::isGparity ) {
return nullptr;
}
SplitOperator<FermionField> *split = this->MakeSplitGrids(mpi_split);
// Placeholder links; the doubled field is overwritten below
GaugeField Uplaceholder(split->GaugeGrid);
Uplaceholder = Zero();
WilsonFermion<Impl> *clone = new WilsonFermion<Impl>(Uplaceholder,
*split->GaugeGrid,
*split->GaugeRBGrid,
mass,
this->Params,
anisotropyCoeff);
SplitDoubledGaugeInto(*clone);
split->Matrix = clone;
return split;
}
/////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////
// Data members require to support the functionality // Data members require to support the functionality
/////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////
+21
View File
@@ -181,6 +181,27 @@ public:
// DoubleStore // DoubleStore
void ImportGauge(const GaugeField &_Umu); void ImportGauge(const GaugeField &_Umu);
// Install an already doubled gauge field (phases, twists and -1/2 applied)
void ImportDoubledGauge(const DoubledGaugeField &U)
{
Umu = U;
pickCheckerboard(Even,UmuEven,Umu);
pickCheckerboard(Odd ,UmuOdd ,Umu);
}
// Redistribute this operator's doubled gauge field onto clone's split grid. Collective.
void SplitDoubledGaugeInto(WilsonFermion5D<Impl> &clone)
{
double t0 = usecond();
DoubledGaugeField sUmu(clone.GaugeGrid());
Grid_split(Umu,sUmu);
double t1 = usecond();
clone.ImportDoubledGauge(sUmu);
double t2 = usecond();
std::cout << GridLogPerformance << "SplitDoubledGaugeInto: Grid_split " << (t1-t0)/1.0e6
<< " s import " << (t2-t1)/1.0e6 << " s" << std::endl;
}
/////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////
// Data members require to support the functionality // Data members require to support the functionality
/////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////
+38
View File
@@ -67,6 +67,44 @@ public:
this->SetCoefficientsInternal(1.0,zgamma,b,c); this->SetCoefficientsInternal(1.0,zgamma,b,c);
} }
// Exact type only: derived operators must not inherit this
virtual SplitOperator<FermionField> *SplitClone(const Coordinate &mpi_split)
{
if ( typeid(*this) != typeid(ZMobiusFermion<Impl>) ) {
return nullptr;
}
if ( this->Dirichlet || Impl::LsVectorised || Impl::isGparity ) {
return nullptr;
}
SplitOperator<FermionField> *split = this->MakeSplitGrids(mpi_split);
// Placeholder links; the doubled field is overwritten below
GaugeField Uplaceholder(split->GaugeGrid);
Uplaceholder = Zero();
// Constructor argument only; CloneCoefficientsInto restores _gamma at full precision
std::vector<ComplexD> gamma(this->Ls);
for ( int s=0; s<this->Ls; s++ ) {
gamma[s] = this->_gamma[s];
}
ZMobiusFermion<Impl> *clone = new ZMobiusFermion<Impl>(Uplaceholder,
*split->FermionGrid,
*split->FermionRBGrid,
*split->GaugeGrid,
*split->GaugeRBGrid,
this->mass_plus,
this->M5,
gamma,
this->_b,
this->_c,
this->Params);
this->CloneCoefficientsInto(*clone);
this->SplitDoubledGaugeInto(*clone);
split->Matrix = clone;
return split;
}
}; };
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
@@ -64,6 +64,20 @@ public:
GRID_ASSERT(U.Checkerboard()==Odd); GRID_ASSERT(U.Checkerboard()==Odd);
GRID_ASSERT(V.Checkerboard()==U.Checkerboard()); GRID_ASSERT(V.Checkerboard()==U.Checkerboard());
// Forces returned on the full 4D grid (checkerboard including s): same sequence, summed
if ( !this->_Mat.CheckerboardedForce() ) {
GaugeField ForceO(Force.Grid());
GaugeField ForceE(Force.Grid());
this->_Mat.Meooe (V,tmp1);
this->_Mat.MooeeInv(tmp1,tmp2);
this->_Mat.MoeDeriv(ForceO,U,tmp2,DaggerNo);
this->_Mat.MeooeDag (U,tmp1);
this->_Mat.MooeeInvDag(tmp1,tmp2);
this->_Mat.MeoDeriv(ForceE,tmp2,V,DaggerNo);
Force = -(ForceO+ForceE);
return;
}
// NOTE Guido: WE DO NOT WANT TO USE THE ucbgrid GRID FOR THE FORCE // NOTE Guido: WE DO NOT WANT TO USE THE ucbgrid GRID FOR THE FORCE
// it is not conformable with the HMC force field // it is not conformable with the HMC force field
// Case: Ls vectorised fields // Case: Ls vectorised fields
@@ -109,6 +123,20 @@ public:
GRID_ASSERT(V.Checkerboard()==Odd); GRID_ASSERT(V.Checkerboard()==Odd);
GRID_ASSERT(V.Checkerboard()==V.Checkerboard()); GRID_ASSERT(V.Checkerboard()==V.Checkerboard());
// Forces returned on the full 4D grid (checkerboard including s): same sequence, summed
if ( !this->_Mat.CheckerboardedForce() ) {
GaugeField ForceO(Force.Grid());
GaugeField ForceE(Force.Grid());
this->_Mat.MeooeDag (V,tmp1);
this->_Mat.MooeeInvDag(tmp1,tmp2);
this->_Mat.MoeDeriv(ForceO,U,tmp2,DaggerYes);
this->_Mat.Meooe (U,tmp1);
this->_Mat.MooeeInv(tmp1,tmp2);
this->_Mat.MeoDeriv(ForceE,tmp2,V,DaggerYes);
Force = -(ForceO+ForceE);
return;
}
// NOTE Guido: WE DO NOT WANT TO USE THE ucbgrid GRID FOR THE FORCE // NOTE Guido: WE DO NOT WANT TO USE THE ucbgrid GRID FOR THE FORCE
// it is not conformable with the HMC force field // it is not conformable with the HMC force field
// INHERIT FROM THE Force field instead // INHERIT FROM THE Force field instead
+49
View File
@@ -125,6 +125,16 @@ public:
return SmearedSet[Level]; return SmearedSet[Level];
} }
/*! @brief Level n of the smearing chain: 0 is the thin links, n is n smearing steps */
const GaugeField &get_level(int n) const
{
GRID_ASSERT(n >= 0 && n <= (int)smearingLevels);
if ( n == 0 ) {
return *ThinLinks;
}
return SmearedSet[n-1];
}
//==================================================================== //====================================================================
void set_iLambda(GaugeLinkField& iLambda, GaugeLinkField& e_iQ, void set_iLambda(GaugeLinkField& iLambda, GaugeLinkField& e_iQ,
const GaugeLinkField& iQ, const GaugeLinkField& Sigmap, const GaugeLinkField& iQ, const GaugeLinkField& Sigmap,
@@ -277,6 +287,45 @@ public:
SigmaTilde=Gimpl::projectForce(SigmaTilde); // Ta SigmaTilde=Gimpl::projectForce(SigmaTilde); // Ta
} }
//====================================================================
// Force on the thin links from an action that depends on every level of the chain.
// LevelForce[n], n = 0..smearingLevels, is the force on level n in the same
// "U times dS/dU" form smeared_force takes (zero fields allowed). The chain rule is
// applied from the top level down, adding each level's own force on the way:
// Sigma_L = U_L^dag F_L
// Sigma_n = AnalyticSmearedForce(Sigma_{n+1}, U_n) + U_n^dag F_n
// ThinForce = Ta( U_0 Sigma_0 )
// With force on the top level only this is smeared_force.
// SmearedConfigurationMasked would need its own: its chain rule is level dependent.
virtual void smeared_force_levels(const std::vector<GaugeField> &LevelForce, GaugeField &ThinForce)
{
GRID_ASSERT(LevelForce.size() == smearingLevels+1);
GridBase *grid = LevelForce[0].Grid();
GaugeField Sigma(grid);
GaugeLinkField tmp_mu(grid);
// Sigma = U_L^dag F_L
for (int mu = 0; mu < Nd; mu++) {
tmp_mu = adj(peekLorentz(get_level(smearingLevels), mu)) * peekLorentz(LevelForce[smearingLevels], mu);
pokeLorentz(Sigma, tmp_mu, mu);
}
for (int n = (int)smearingLevels - 1; n >= 0; n--) {
Sigma = AnalyticSmearedForce(Sigma, get_level(n));
for (int mu = 0; mu < Nd; mu++) {
tmp_mu = peekLorentz(Sigma, mu) + adj(peekLorentz(get_level(n), mu)) * peekLorentz(LevelForce[n], mu);
pokeLorentz(Sigma, tmp_mu, mu);
}
}
for (int mu = 0; mu < Nd; mu++) {
tmp_mu = peekLorentz(*ThinLinks, mu) * peekLorentz(Sigma, mu);
pokeLorentz(ThinForce, tmp_mu, mu);
}
ThinForce = Gimpl::projectForce(ThinForce); // Ta
}
//==================================================================== //====================================================================
virtual GaugeField& get_SmearedU() { return SmearedSet[smearingLevels - 1]; } virtual GaugeField& get_SmearedU() { return SmearedSet[smearingLevels - 1]; }
+2 -2
View File
@@ -52,7 +52,7 @@ protected:
public: public:
//Define the action used to evolve the plaquettes //Define the action used to evolve the plaquettes
//(Lüscher: https://arxiv.org/pdf/1006.4518 eq. 1.4) //(Luscher: https://arxiv.org/pdf/1006.4518 eq. 1.4)
//V'(t) = -g^2 * ( d/dVt S[Vt](g) ) * Vt //V'(t) = -g^2 * ( d/dVt S[Vt](g) ) * Vt
// = -g^2 * ( d/dVt (1/g^2 * sum_p Re tr{ 1 - Vt(p) } ) ) * Vt // = -g^2 * ( d/dVt (1/g^2 * sum_p Re tr{ 1 - Vt(p) } ) ) * Vt
// = - d/dVt ( sum_p ( Nc - Re tr Vt(p) ) * Vt // = - d/dVt ( sum_p ( Nc - Re tr Vt(p) ) * Vt
@@ -157,7 +157,7 @@ public:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
//Compute t^2 <E(t)> for time from the plaquette form //Compute t^2 <E(t)> for time from the plaquette form
//(Lüscher: https://arxiv.org/pdf/1006.4518 eq. 3.1) //(Luscher: https://arxiv.org/pdf/1006.4518 eq. 3.1)
//E(t) = 2 * sum_p Retr{ 1 - Vt(p) } = //E(t) = 2 * sum_p Retr{ 1 - Vt(p) } =
// = 2 * sum_p ( Nc - Retr Vt(p) ) = // = 2 * sum_p ( Nc - Retr Vt(p) ) =
// = 2 * Nc * sum_p ( 1 - Retr Vt(p)/Nc ) // = 2 * Nc * sum_p ( 1 - Retr Vt(p)/Nc )
+3 -3
View File
@@ -1002,10 +1002,10 @@ accelerator_inline void precisionChange(vRealD *out,const vRealF *in,int n
Optimization::PrecisionChange::StoD(in[m].v,out[n].v,out[n+1].v); Optimization::PrecisionChange::StoD(in[m].v,out[n].v,out[n+1].v);
// Bug in gcc 10.0.1 and gcc 10.1 using fixed-size SVE ACLE data types CAS-159553-Y1K4C6 // Bug in gcc 10.0.1 and gcc 10.1 using fixed-size SVE ACLE data types CAS-159553-Y1K4C6
// function call results in compile-time error: // function call results in compile-time error:
// In function ‘void Grid::precisionChange(Grid::vRealD*, Grid::vRealF*, int)’: // In function 'void Grid::precisionChange(Grid::vRealD*, Grid::vRealF*, int)':
// .../Grid_vector_types.h:961:56: error: // .../Grid_vector_types.h:961:56: error:
// cannot bind non-const lvalue reference of type ‘vecd&’ {aka ‘svfloat64_t&’} // cannot bind non-const lvalue reference of type 'vecd&' {aka 'svfloat64_t&'}
// to an rvalue of type ‘vecd’ {aka ‘svfloat64_t’} // to an rvalue of type 'vecd' {aka 'svfloat64_t'}
// 961 | Optimization::PrecisionChange::StoD(in[m].v,out[n].v,out[n+1].v); // 961 | Optimization::PrecisionChange::StoD(in[m].v,out[n].v,out[n+1].v);
// | ~~~~~~~^ // | ~~~~~~~^
} }
+15
View File
@@ -95,6 +95,8 @@ NAMESPACE_BEGIN(Grid);
////////////////////////////////////////////////////// //////////////////////////////////////////////////////
static Coordinate Grid_default_latt; static Coordinate Grid_default_latt;
static Coordinate Grid_default_mpi; static Coordinate Grid_default_mpi;
static Coordinate Grid_default_batched_solver_split;
static bool Grid_default_batched_solver_split_node = false;
int GridThread::_threads =1; int GridThread::_threads =1;
int GridThread::_hyperthreads=1; int GridThread::_hyperthreads=1;
@@ -108,6 +110,8 @@ char *GridHostname(void)
} }
const Coordinate &GridDefaultLatt(void) {return Grid_default_latt;}; const Coordinate &GridDefaultLatt(void) {return Grid_default_latt;};
const Coordinate &GridDefaultMpi(void) {return Grid_default_mpi;}; const Coordinate &GridDefaultMpi(void) {return Grid_default_mpi;};
const Coordinate &GridDefaultBatchedSolverSplit(void) {return Grid_default_batched_solver_split;};
bool GridDefaultBatchedSolverSplitNode(void) {return Grid_default_batched_solver_split_node;};
const Coordinate GridDefaultSimd(int dims,int nsimd) const Coordinate GridDefaultSimd(int dims,int nsimd)
{ {
Coordinate layout(dims); Coordinate layout(dims);
@@ -562,6 +566,7 @@ void Grid_init(int *argc,char ***argv)
std::cout<<GridLogMessage<<"Performance:"<<std::endl; std::cout<<GridLogMessage<<"Performance:"<<std::endl;
std::cout<<GridLogMessage<<std::endl; std::cout<<GridLogMessage<<std::endl;
std::cout<<GridLogMessage<<" --comms-overlap : Overlap comms with compute "<<std::endl; std::cout<<GridLogMessage<<" --comms-overlap : Overlap comms with compute "<<std::endl;
std::cout<<GridLogMessage<<" --batched-solver-split a.b.c.d|node : batched solvers run independent solves on partitions of this MPI layout (node = one per node)"<<std::endl;
std::cout<<GridLogMessage<<std::endl; std::cout<<GridLogMessage<<std::endl;
std::cout<<GridLogMessage<<" --dslash-generic: Wilson kernel for generic Nc"<<std::endl; std::cout<<GridLogMessage<<" --dslash-generic: Wilson kernel for generic Nc"<<std::endl;
std::cout<<GridLogMessage<<" --dslash-unroll : Wilson kernel for Nc=3"<<std::endl; std::cout<<GridLogMessage<<" --dslash-unroll : Wilson kernel for Nc=3"<<std::endl;
@@ -613,6 +618,16 @@ void Grid_init(int *argc,char ***argv)
Grid_default_latt, Grid_default_latt,
Grid_default_mpi); Grid_default_mpi);
if( GridCmdOptionExists(*argv,*argv+*argc,"--batched-solver-split") ){
std::string arg = GridCmdOptionPayload(*argv,*argv+*argc,"--batched-solver-split");
if ( arg == "node" ) {
Grid_default_batched_solver_split_node = true;
} else {
GridCmdOptionIntVector(arg,Grid_default_batched_solver_split);
GRID_ASSERT(Grid_default_batched_solver_split.size() == Grid_default_mpi.size());
}
}
if( GridCmdOptionExists(*argv,*argv+*argc,"--decomposition") ){ if( GridCmdOptionExists(*argv,*argv+*argc,"--decomposition") ){
std::cout<<GridLogMessage<<"Grid Default Decomposition patterns\n"; std::cout<<GridLogMessage<<"Grid Default Decomposition patterns\n";
std::cout<<GridLogMessage<<"\tOpenMP threads : "<<GridThread::GetThreads()<<std::endl; std::cout<<GridLogMessage<<"\tOpenMP threads : "<<GridThread::GetThreads()<<std::endl;
+5
View File
@@ -49,6 +49,11 @@ void Grid_unquiesce_nodes(void);
const Coordinate GridDefaultSimd(int dims,int nsimd); const Coordinate GridDefaultSimd(int dims,int nsimd);
const Coordinate &GridDefaultLatt(void); const Coordinate &GridDefaultLatt(void);
const Coordinate &GridDefaultMpi(void); const Coordinate &GridDefaultMpi(void);
// --batched-solver-split a.b.c.d : MPI layout of each partition for batched solves.
// --batched-solver-split node : one partition per node.
// Not given: empty Coordinate and false, meaning no split.
const Coordinate &GridDefaultBatchedSolverSplit(void);
bool GridDefaultBatchedSolverSplitNode(void);
const int &GridThreads(void) ; const int &GridThreads(void) ;
void GridSetThreads(int t) ; void GridSetThreads(int t) ;
void GridLogTimestamp(int); void GridLogTimestamp(int);
+127
View File
@@ -104,6 +104,119 @@ inline double usecond(void) {
gettimeofday(&tv,NULL); gettimeofday(&tv,NULL);
return 1.0e6*tv.tv_sec + 1.0*tv.tv_usec; return 1.0e6*tv.tv_sec + 1.0*tv.tv_usec;
} }
/**************************************************************
* Point to point cost, per Cartesian direction.
*
* PaddedCell::Face_exchange issues one MPI_Sendrecv per dimension and waits,
* so the quantity that bounds a halo round is the sendrecv time for that
* direction, not a one way ping-pong. Sweeping the size gives the latency
* intercept and the bandwidth slope separately.
*
* Each direction is labelled on- or off-node, which is what decides whether a
* halo round rides the intra-node fabric or the network, and therefore which
* dimension ordering costs least. Times are reduced across ranks: the max is
* the one that bounds a collective halo round.
**************************************************************
*/
void PingPong(std::vector<int> cart_geom,bool use_device,int ncall)
{
int Nd=cart_geom.size();
std::vector<int> periodic(Nd,1);
std::vector<int> coor(Nd);
int rank;
MPI_Comm communicator;
MPI_Cart_create(WorldComm,Nd,&cart_geom[0],&periodic[0],0,&communicator);
MPI_Comm_rank(communicator,&rank);
MPI_Cart_coords(communicator,rank,Nd,&coor[0]);
// An identifier every rank on a node agrees on, so a neighbour can be
// classified by comparing it.
int node_id = WorldRank;
MPI_Bcast(&node_id,1,MPI_INT,0,WorldShmComm);
size_t max_bytes = 2*1024*1024;
void *xmit, *recv;
if ( use_device ) {
xmit = acceleratorAllocDevice(max_bytes);
recv = acceleratorAllocDevice(max_bytes);
} else {
xmit = malloc(max_bytes);
recv = malloc(max_bytes);
}
if ( !WorldRank ) {
printf("= dim dir bytes us(max) us(min) MB/s off-node ranks\n");
fflush(stdout);
}
for(int d=0;d<Nd;d++){
// An undecomposed dimension is a local wrap in PaddedCell and carries no
// message at all; a shift here would only time a rank talking to itself.
if ( cart_geom[d] == 1 ) continue;
for(int sign=-1;sign<=1;sign+=2){
int from,to;
MPI_Cart_shift(communicator,d,sign,&from,&to);
// Fetch the identifier of the neighbour we SEND to, which means sending
// ours the other way round the shift.
int peer_node=node_id;
MPI_Sendrecv(&node_id, 1,MPI_INT,from,rank,
&peer_node,1,MPI_INT,to, to,
communicator,MPI_STATUS_IGNORE);
int offnode = (peer_node != node_id) ? 1 : 0;
int offnode_count=0;
MPI_Reduce(&offnode,&offnode_count,1,MPI_INT,MPI_SUM,0,communicator);
for(size_t bytes=8; bytes<=max_bytes; bytes*=8){
// Latency needs the repeats; bandwidth does not, and large messages
// would otherwise dominate the run time.
int nc = (bytes<=32768) ? ncall : (ncall/20 > 10 ? ncall/20 : 10);
// Untimed warm-up: the first exchange on a fresh buffer pays connection
// setup and registration, and would otherwise land in the first row.
MPI_Sendrecv(xmit,bytes,MPI_CHAR,to,rank,
recv,bytes,MPI_CHAR,from,from,
communicator,MPI_STATUS_IGNORE);
MPI_Barrier(communicator);
double t0=usecond();
for(int i=0;i<nc;i++){
MPI_Sendrecv(xmit,bytes,MPI_CHAR,to,rank,
recv,bytes,MPI_CHAR,from,from,
communicator,MPI_STATUS_IGNORE);
}
double t1=usecond();
double us = (t1-t0)/(double)nc;
double us_max, us_min;
MPI_Reduce(&us,&us_max,1,MPI_DOUBLE,MPI_MAX,0,communicator);
MPI_Reduce(&us,&us_min,1,MPI_DOUBLE,MPI_MIN,0,communicator);
if ( !WorldRank ) {
printf("= %3d %s %10zu %11.2f %11.2f %10.1f %d\n",
d, sign>0?"+":"-", bytes, us_max, us_min,
(double)bytes/us_max, offnode_count);
fflush(stdout);
}
}
}
}
if ( use_device ) {
acceleratorFreeDevice(xmit);
acceleratorFreeDevice(recv);
} else {
free(xmit);
free(recv);
}
MPI_Comm_free(&communicator);
}
/************************************************************** /**************************************************************
* Main benchmark routine * Main benchmark routine
************************************************************** **************************************************************
@@ -303,6 +416,20 @@ int main(int argc, char **argv)
} }
if( !WorldRank ) {
printf("=========================================================\n");
printf("= Point to point cost per direction, HOST memory \n");
printf("=========================================================\n");fflush(stdout);
}
PingPong(mpi,false,1000);
if( !WorldRank ) {
printf("=========================================================\n");
printf("= Point to point cost per direction, DEVICE memory \n");
printf("=========================================================\n");fflush(stdout);
}
PingPong(mpi,true,1000);
if( !WorldRank ) { if( !WorldRank ) {
printf("=========================================================\n"); printf("=========================================================\n");
printf("= Benchmarking HOST memory MPI performance \n"); printf("= Benchmarking HOST memory MPI performance \n");
+214
View File
@@ -0,0 +1,214 @@
// Minimal reproducer for the CXI uncached-registration defect.
//
// Hypothesis: with the libfabric MR cache disabled, a device pointer at a non-zero offset
// into a hipMalloc'd allocation is registered as the whole allocation and the offset is
// discarded, so a send transmits from the base and a receive lands at the base.
//
// Each test therefore prints, for both the data and its landing place, what a correct
// implementation must give, what the hypothesis predicts instead, and what was observed.
//
// hipcc -O2 -o simple_reproducer_noncache simple_reproducer_noncache.cc \
// -I$MPICH_DIR/include -L$MPICH_DIR/lib -lmpi -L$MPICH_DIR/gtl/lib -lmpi_gtl_hsa
//
// MPICH_GPU_SUPPORT_ENABLED=1 FI_MR_CACHE_MAX_COUNT=0 \
// srun -N2 -n2 --ntasks-per-node=1 ./simple_reproducer_noncache
#include <mpi.h>
#include <hip/hip_runtime.h>
#include <cstdio>
#include <cstdint>
#include <cstdlib>
#include <vector>
// The message must be large enough to go rendezvous: an eager message is staged through a host
// bounce buffer with hipMemcpy, which honours the offset, and never registers the user pointer.
static const int NWORDS = 262144; // 2 MB buffers
static int NMSG = 16384; // 128 KB messages (argv[1], in words)
static int OFF = 16384; // offending offset in words (argv[2]); clearest at >= NMSG,
// which keeps the intended and predicted windows disjoint
static int rank, size, peer;
static uint64_t *A, *B;
static std::vector<uint64_t> h(NWORDS);
#define HIP(cmd) do { hipError_t e=(cmd); if ( e != hipSuccess ) { \
printf("hip error %s at line %d\n",hipGetErrorString(e),__LINE__); \
MPI_Abort(MPI_COMM_WORLD,1); } } while(0)
static uint64_t word(uint64_t tag,int r,int n)
{
return (tag<<60) | ((uint64_t)r<<32) | (uint64_t)n;
}
static const char *verdict(int observed,int correct,int predicted)
{
if ( observed == correct ) return "CORRECT";
if ( observed == predicted ) return "WRONG, as predicted";
return "WRONG, and not as predicted";
}
static void refresh(void)
{
for(int n=0;n<NWORDS;n++) h[n] = word(0xa,rank,n);
HIP(hipMemcpy(A,h.data(),NWORDS*sizeof(uint64_t),hipMemcpyHostToDevice));
for(int n=0;n<NWORDS;n++) h[n] = word(0xb,rank,n);
HIP(hipMemcpy(B,h.data(),NWORDS*sizeof(uint64_t),hipMemcpyHostToDevice));
MPI_Barrier(MPI_COMM_WORLD);
}
static void exchange(int sendoff,int recvoff)
{
MPI_Request req[2];
MPI_Irecv(B+recvoff,NMSG*sizeof(uint64_t),MPI_BYTE,peer,0,MPI_COMM_WORLD,&req[0]);
MPI_Isend(A+sendoff,NMSG*sizeof(uint64_t),MPI_BYTE,peer,0,MPI_COMM_WORLD,&req[1]);
MPI_Waitall(2,req,MPI_STATUSES_IGNORE);
}
// Report the state of one NMSG-word window of B: how much of it was overwritten, and by what.
static void window(const char *label,int at)
{
int mod=0, m0=-1;
for(int i=0;i<NMSG && at+i<NWORDS;i++) {
if ( h[at+i] != word(0xb,rank,at+i) ) { if ( m0 < 0 ) m0 = at+i; mod++; }
}
if ( mod == 0 ) {
printf("rank %d %-9s B[%d..%d] : untouched\n",rank,label,at,at+NMSG-1);
return;
}
uint64_t w = h[m0];
printf("rank %d %-9s B[%d..%d] : %d of %d words overwritten, B[%d] holds A[%d] from rank %d\n",
rank,label,at,at+NMSG-1,mod,NMSG,m0,(int)(w & 0xffffffff),(int)((w>>32) & 0xfffffff));
}
// Name a word: still the receive pattern, or a word of the neighbour's send buffer.
static const char *describe(uint64_t w,int idx)
{
static char s[64];
if ( w == word(0xb,rank,idx) ) snprintf(s,sizeof(s),"untouched");
else if ( (w>>60) == 0xa ) snprintf(s,sizeof(s),"A[%d] of rank %d",
(int)(w & 0xffffffff),(int)((w>>32) & 0xfffffff));
else snprintf(s,sizeof(s),"unrecognised");
return s;
}
static void report(const char *name,int sendoff,int recvoff)
{
HIP(hipMemcpy(h.data(),B,NWORDS*sizeof(uint64_t),hipMemcpyDeviceToHost));
int first=-1, last=-1;
for(int n=0;n<NWORDS;n++) {
if ( h[n] != word(0xb,rank,n) ) { if ( first < 0 ) first = n; last = n; }
}
printf("rank %d %s : send &A[%d] -> recv &B[%d]\n",rank,name,sendoff,recvoff);
if ( first < 0 ) { printf("rank %d nothing was written\n",rank); return; }
uint64_t w = h[first];
int src = (int)(w & 0xffffffff);
int sr = (int)((w>>32) & 0xfffffff);
// The hypothesis predicts both offsets are discarded: the bytes at A[0], landing at B[0].
printf("rank %d data : want A[%d] predict A[0] got A[%d] from rank %d %s\n",
rank,sendoff,src,sr,verdict(src,sendoff,0));
printf("rank %d place : want B[%d] predict B[0] got B[%d] %s\n",
rank,recvoff,first,verdict(first,recvoff,0));
if ( src == sendoff && first == recvoff ) return;
// Both candidate landing sites in our receive buffer, and both candidate source words in the
// neighbour's send buffer, which the fill pattern fixes by construction.
printf("rank %d Boff %d\n",rank,recvoff);
printf("rank %d B[0] = 0x%016llx %s\n",
rank,(unsigned long long)h[0],describe(h[0],0));
if ( recvoff != 0 )
printf("rank %d B[%d]\t= 0x%016llx %s\n",
rank,recvoff,(unsigned long long)h[recvoff],describe(h[recvoff],recvoff));
printf("rank %d Neighbour rank %d send buffer holds\n",rank,peer);
printf("rank %d A[0]\t= 0x%016llx\n",
rank,(unsigned long long)word(0xa,peer,0));
if ( sendoff != 0 )
printf("rank %d A[%d]\t= 0x%016llx\n",
rank,sendoff,(unsigned long long)word(0xa,peer,sendoff));
window("intended",recvoff);
if ( recvoff != 0 ) window("predicted",0);
if ( first < recvoff || last >= recvoff+NMSG ) {
if ( first != 0 || last != NMSG-1 ) {
printf("rank %d stray : modified words span B[%d..%d], outside both windows\n",
rank,first,last);
}
}
int bad=0;
for(int i=0;i<NMSG && first+i<NWORDS;i++) {
if ( h[first+i] != word(0xa,peer,src+i) ) bad++;
}
if ( bad ) printf("rank %d body : %d of %d words are not a contiguous run from A[%d]\n",
rank,bad,NMSG,src);
}
// Let each rank print in turn: flush, then a barrier so rank r is on the page before r+1 starts.
static void ordered(void (*print)(void))
{
for(int r=0;r<size;r++) {
if ( r == rank ) { print(); fflush(stdout); }
MPI_Barrier(MPI_COMM_WORLD);
}
}
static const char *test_name;
static int test_sendoff, test_recvoff;
static void print_report(void) { report(test_name,test_sendoff,test_recvoff); }
static void print_setup (void)
{
printf("rank %d A %p B %p message %d words (%d bytes) offset %d words (%d bytes)\n",
rank,(void *)A,(void *)B,NMSG,(int)(NMSG*sizeof(uint64_t)),OFF,(int)(OFF*sizeof(uint64_t)));
}
int main(int argc,char **argv)
{
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Comm_size(MPI_COMM_WORLD,&size);
if ( size != 2 ) {
if ( rank == 0 ) printf("run with two ranks, one per node\n");
MPI_Finalize();
return 1;
}
peer = 1-rank;
if ( argc > 1 ) NMSG = atoi(argv[1]);
if ( argc > 2 ) OFF = atoi(argv[2]);
if ( NMSG < 1 || OFF < 0 || OFF+NMSG > NWORDS ) {
if ( rank == 0 ) printf("message %d and offset %d words do not fit in %d\n",NMSG,OFF,NWORDS);
MPI_Finalize();
return 1;
}
int ndev;
HIP(hipGetDeviceCount(&ndev));
if ( ndev < 1 ) {
printf("rank %d sees no GPU\n",rank);
MPI_Abort(MPI_COMM_WORLD,1);
}
HIP(hipSetDevice(rank%ndev));
HIP(hipMalloc(&A,NWORDS*sizeof(uint64_t)));
HIP(hipMalloc(&B,NWORDS*sizeof(uint64_t)));
ordered(print_setup);
const int offsets[4][2] = { {0,0}, {OFF,0}, {0,OFF}, {OFF,OFF} };
const char *names[4] = { "test 1","test 2","test 3","test 4" };
for(int t=0;t<4;t++) {
refresh();
exchange(offsets[t][0],offsets[t][1]);
test_name = names[t]; test_sendoff = offsets[t][0]; test_recvoff = offsets[t][1];
ordered(print_report);
}
HIP(hipFree(A));
HIP(hipFree(B));
MPI_Finalize();
return 0;
}
+2 -2
View File
@@ -87,7 +87,7 @@ int main (int argc, char ** argv)
HermOpAdaptor<LatticeFermionD> HermFineOp(MdagMOp); HermOpAdaptor<LatticeFermionD> HermFineOp(MdagMOp);
// ── Coarse geometry ──────────────────────────────────────────────────── // ── Coarse geometry ────────────────────────────────────────────────────
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace; typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace;
@@ -171,7 +171,7 @@ int main (int argc, char ** argv)
LatticeFermionD src(FGrid); random(RNG5, src); LatticeFermionD src(FGrid); random(RNG5, src);
LatticeFermionD result(FGrid); result = Zero(); LatticeFermionD result(FGrid); result = Zero();
TwoLevelADEF2<LatticeFermionD, CoarseVector, Subspace> DeprecatedTwoLevelADEF2<LatticeFermionD, CoarseVector, Subspace>
HDCG(1.0e-8, 1000, HDCG(1.0e-8, 1000,
HermFineOp, HermFineOp,
Smoother, Smoother,
+2 -2
View File
@@ -31,7 +31,7 @@ Author: Peter Boyle <pboyle@bnl.gov>
// //
// Operator hierarchy: // Operator hierarchy:
// Fine: M†M, acted on via HermOpAdaptor so Op = HermOp = M†M // Fine: M†M, acted on via HermOpAdaptor so Op = HermOp = M†M
// Coarse: 33-point GeneralCoarsenedMatrix (NextToNearest, 2-hop M†M) // Coarse: 33-point DeprecatedGeneralCoarsenedMatrix (NextToNearest, 2-hop M†M)
// //
// Setup: // Setup:
// 1. Chebyshev filter (T_600 x T_2500) on M†M to build near-null subspace {ψᵢ} // 1. Chebyshev filter (T_600 x T_2500) on M†M to build near-null subspace {ψᵢ}
@@ -198,7 +198,7 @@ int main(int argc, char **argv)
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
// Coarse operator: 33-point stencil for 2-hop M†M // Coarse operator: 33-point stencil for 2-hop M†M
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
typedef GeneralCoarsenedMatrix<vSpinColourVector, vTComplex, nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector, vTComplex, nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
NextToNearestStencilGeometry5D geom(Coarse5d); NextToNearestStencilGeometry5D geom(Coarse5d);
+2 -2
View File
@@ -364,7 +364,7 @@ void runMG(
) { ) {
// typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace; // typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace;
// typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; // typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
// typedef LittleDiracOperator::CoarseVector CoarseVector; // typedef LittleDiracOperator::CoarseVector CoarseVector;
ParseEnvironment(); ParseEnvironment();
@@ -620,7 +620,7 @@ int main (int argc, char ** argv)
// assert(nbasis <= Nevecs); // need to have enough evecs // assert(nbasis <= Nevecs); // need to have enough evecs
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
NextToNearestStencilGeometry5D geom(Coarse5d); NextToNearestStencilGeometry5D geom(Coarse5d);
+23 -23
View File
@@ -190,8 +190,8 @@ public:
} }
}; };
// Lüscher deflated guesser (arXiv:0706.2298 Sec A.3) for a non-Hermitian solve. // Luscher deflated guesser (arXiv:0706.2298 Sec A.3) for a non-Hermitian solve.
// C_{st} = <psi[s] | LinOp | psi[t]>; guess = sum_s c_s psi[s] where c = C^{-1} psi† src. // C_{st} = <psi[s] | LinOp | psi[t]>; guess = sum_s c_s psi[s] where c = C^{-1} psi^dag src.
template<class Field> template<class Field>
class LuscherGuesser : public LinearFunction<Field> { class LuscherGuesser : public LinearFunction<Field> {
const std::vector<Field> &psi; const std::vector<Field> &psi;
@@ -342,7 +342,7 @@ void runMG(
TrivialPrecon<LatticeFermionD> simple_fine; TrivialPrecon<LatticeFermionD> simple_fine;
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 0→1: coarsen PVdagM, build LinOpCoarse // Level 0->1: coarsen PVdagM, build LinOpCoarse
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
LittleDiracOperator LittleDiracOpPV(geom, FGrid, Coarse5d); LittleDiracOperator LittleDiracOpPV(geom, FGrid, Coarse5d);
LittleDiracOpPV.CoarsenOperator(PVdagM, AggregatesPD); LittleDiracOpPV.CoarsenOperator(PVdagM, AggregatesPD);
@@ -366,7 +366,7 @@ void runMG(
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// psi_coarse: coarse projections of pre-GS fine null vectors. // psi_coarse: coarse projections of pre-GS fine null vectors.
// These are the Level 1 near-null vectors, promoted from Level 0. // These are the Level 1 near-null vectors, promoted from Level 0.
// Used as the aggregation basis for Level 1→2 coarsening. // Used as the aggregation basis for Level 1->2 coarsening.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
std::vector<CoarseVector> psi_coarse(nbasis, Coarse5d); std::vector<CoarseVector> psi_coarse(nbasis, Coarse5d);
for (int k = 0; k < nbasis; k++) for (int k = 0; k < nbasis; k++)
@@ -396,22 +396,22 @@ void runMG(
RealD normC = C.norm(); RealD normC = C.norm();
RealD normCmCdag = (C - C.adjoint()).norm(); RealD normCmCdag = (C - C.adjoint()).norm();
std::cout << GridLogMessage << "Coarse null matrix ||C|| = " << normC << std::endl; std::cout << GridLogMessage << "Coarse null matrix ||C|| = " << normC << std::endl;
std::cout << GridLogMessage << "Coarse null matrix ||C - C†||/||C|| = " << normCmCdag/normC << std::endl; std::cout << GridLogMessage << "Coarse null matrix ||C - C^dag||/||C|| = " << normCmCdag/normC << std::endl;
std::cout << GridLogMessage << "Galerkin check ||C||/||W|| = " << normC/normW << std::endl; std::cout << GridLogMessage << "Galerkin check ||C||/||W|| = " << normC/normW << std::endl;
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 1→2: set up aggregation using psi_coarse as subspace. // Level 1->2: set up aggregation using psi_coarse as subspace.
// Block factor 2,2,3,2 (removes odd local sublattice in z given MPI // Block factor 2,2,3,2 (removes odd local sublattice in z given MPI
// geometry 3×6×4×4 where z-local at Level 1 is 6). // geometry 3x6x4x4 where z-local at Level 1 is 6).
// psi_coarse are assigned directly; CoarsenOperator performs // psi_coarse are assigned directly; CoarsenOperator performs
// block-GS orthogonalisation before building LinOpCoarseCoarse. // block-GS orthogonalisation before building LinOpCoarseCoarse.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// innerProduct(CoarseSiteObj, CoarseSiteObj) returns iScalar<vTComplex>, so CComplex // innerProduct(CoarseSiteObj, CoarseSiteObj) returns iScalar<vTComplex>, so CComplex
// for the L1→L2 level must be iScalar<vTComplex>, not vTComplex. // for the L1->L2 level must be iScalar<vTComplex>, not vTComplex.
typedef typename CoarseVector::vector_object CoarseSiteObj; typedef typename CoarseVector::vector_object CoarseSiteObj;
typedef iScalar<vTComplex> vTTComplex; typedef iScalar<vTComplex> vTTComplex;
typedef GeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,NB> LittleDiracOperatorL2; typedef DeprecatedGeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,NB> LittleDiracOperatorL2;
typedef typename LittleDiracOperatorL2::CoarseVector CoarseCoarseVector; typedef typename LittleDiracOperatorL2::CoarseVector CoarseCoarseVector;
typedef Aggregation<CoarseSiteObj,vTTComplex,NB> SubspaceL2; typedef Aggregation<CoarseSiteObj,vTTComplex,NB> SubspaceL2;
typedef MGPreconditioner<CoarseSiteObj,vTTComplex,NB> L1to2MG; typedef MGPreconditioner<CoarseSiteObj,vTTComplex,NB> L1to2MG;
@@ -429,12 +429,12 @@ void runMG(
TrivialPrecon<CoarseCoarseVector> simpleCC; TrivialPrecon<CoarseCoarseVector> simpleCC;
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Lüscher deflation guesser for L3PGCR. // Luscher deflation guesser for L3PGCR.
// Step 1: project psi_coarse[k] (promoted fine null vectors) to // Step 1: project psi_coarse[k] (promoted fine null vectors) to
// CoarseCoarseVector space — these cover the zero-momentum // CoarseCoarseVector space -- these cover the zero-momentum
// component of the near-null space of LinOpCC. // component of the near-null space of LinOpCC.
// Step 2: breed Nextra additional null vectors directly on LinOpCC // Step 2: breed Nextra additional null vectors directly on LinOpCC
// using GCR with random sources — these pick up near-null // using GCR with random sources -- these pick up near-null
// modes at all spatial frequencies not spanned by step 1. // modes at all spatial frequencies not spanned by step 1.
// Step 3: build C_{st} = <psi_cc[s]|LinOpCC|psi_cc[t]> over the // Step 3: build C_{st} = <psi_cc[s]|LinOpCC|psi_cc[t]> over the
// full augmented basis and invert directly via Eigen LU. // full augmented basis and invert directly via Eigen LU.
@@ -476,7 +476,7 @@ void runMG(
RealD normCcc = Ccc.norm(); RealD normCcc = Ccc.norm();
RealD normCccmCdag = (Ccc - Ccc.adjoint()).norm(); RealD normCccmCdag = (Ccc - Ccc.adjoint()).norm();
std::cout << GridLogMessage << "Coarse-coarse deflation matrix ||Ccc|| = " << normCcc << std::endl; std::cout << GridLogMessage << "Coarse-coarse deflation matrix ||Ccc|| = " << normCcc << std::endl;
std::cout << GridLogMessage << "Coarse-coarse deflation matrix ||Ccc-Ccc†||/||Ccc|| = " << normCccmCdag/normCcc << std::endl; std::cout << GridLogMessage << "Coarse-coarse deflation matrix ||Ccc-Ccc^dag||/||Ccc|| = " << normCccmCdag/normCcc << std::endl;
} }
Eigen::MatrixXcd Ccc_inv = Ccc.inverse(); Eigen::MatrixXcd Ccc_inv = Ccc.inverse();
LuscherGuesser<CoarseCoarseVector> CCDeflGuesser(psi_cc, Ccc_inv); LuscherGuesser<CoarseCoarseVector> CCDeflGuesser(psi_cc, Ccc_inv);
@@ -489,7 +489,7 @@ void runMG(
L3PGCR.Name("CCouter"); L3PGCR.Name("CCouter");
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Coarse-level GCR smoother for Level 1→2 V-cycle. // Coarse-level GCR smoother for Level 1->2 V-cycle.
// Mirrors fine-grid SmootherGCR: shifted operator + fixed step count. // Mirrors fine-grid SmootherGCR: shifted operator + fixed step count.
// coarse_smoother_shift and coarse_smoother_nstep are the tuning knobs. // coarse_smoother_shift and coarse_smoother_nstep are the tuning knobs.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@@ -505,7 +505,7 @@ void runMG(
CoarseSmootherGCR.Name("Csmoother"); CoarseSmootherGCR.Name("Csmoother");
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 1→2 V-cycle preconditioner. // Level 1->2 V-cycle preconditioner.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
L1to2MG L1to2Precon(AggregatesL2, L1to2MG L1to2Precon(AggregatesL2,
LinOpCoarse, LinOpCoarse,
@@ -513,7 +513,7 @@ void runMG(
CoarseSmootherGCR, // post-smoother: 12 GCR steps CoarseSmootherGCR, // post-smoother: 12 GCR steps
LinOpCC, LinOpCC,
L3PGCR, L3PGCR,
CCDeflGuesser); // Lüscher guesser: psi_cc C^{-1} psi_cc† CCDeflGuesser); // Luscher guesser: psi_cc C^{-1} psi_cc^dag
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Standalone Level 1 two-level solve test. // Standalone Level 1 two-level solve test.
@@ -548,7 +548,7 @@ void runMG(
f_src = one; f_src = one;
// Pre-smoother: none (TrivialPrecon); post-smoother: shifted PGCR. // Pre-smoother: none (TrivialPrecon); post-smoother: shifted PGCR.
// Coarse solver: L2MGsolver (PGCR preconditioned by Level 1→2 V-cycle). // Coarse solver: L2MGsolver (PGCR preconditioned by Level 1->2 V-cycle).
TwoLevelMG ThreeLevelPrecon(AggregatesPD, TwoLevelMG ThreeLevelPrecon(AggregatesPD,
PVdagM, PVdagM,
simple_fine, simple_fine,
@@ -592,7 +592,7 @@ int main (int argc, char ** argv)
GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid); GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid);
GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid); GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid);
// Level 1 coarse grid: block 2^4 from fine (48×48×48×96 → 24×24×24×48, Ls=1) // Level 1 coarse grid: block 2^4 from fine (48x48x48x96 -> 24x24x24x48, Ls=1)
Coordinate clatt = lat_size; Coordinate clatt = lat_size;
for (int d = 0; d < 4; d++) clatt[d] /= 2; for (int d = 0; d < 4; d++) clatt[d] /= 2;
std::cout << GridLogMessage << "Level 1 coarse lattice: " << clatt << std::endl; std::cout << GridLogMessage << "Level 1 coarse lattice: " << clatt << std::endl;
@@ -600,13 +600,13 @@ int main (int argc, char ** argv)
GridCartesian *Coarse4d = SpaceTimeGrid::makeFourDimGrid(clatt, GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi()); GridCartesian *Coarse4d = SpaceTimeGrid::makeFourDimGrid(clatt, GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi());
GridCartesian *Coarse5d = SpaceTimeGrid::makeFiveDimGrid(1,Coarse4d); GridCartesian *Coarse5d = SpaceTimeGrid::makeFiveDimGrid(1,Coarse4d);
// Level 2 coarse-coarse grid: block 2,2,3,3 from Level 1 (24×24×24×48 → 12×12×8×16, Ls=1). // Level 2 coarse-coarse grid: block 2,2,3,3 from Level 1 (24x24x24x48 -> 12x12x8x16, Ls=1).
// MPI geometry 3.6.4.4 (288 ranks): fine local {16,8,12,24}. // MPI geometry 3.6.4.4 (288 ranks): fine local {16,8,12,24}.
// Level 1 local {8,4,6,12}; Level 2 local {4,2,2,4}. // Level 1 local {8,4,6,12}; Level 2 local {4,2,2,4}.
// z blocked by 3: z-Level1-local=6; 6/3=2 (even), 6/2=3 (odd) → must use 3. // z blocked by 3: z-Level1-local=6; 6/3=2 (even), 6/2=3 (odd) -> must use 3.
// t blocked by 3: t-Level1-local=12; 12/3=4 divisible by Nsimd=4 (gen-simd-width=64). // t blocked by 3: t-Level1-local=12; 12/3=4 divisible by Nsimd=4 (gen-simd-width=64).
// t-block=2 gives t2-local=6, 6 mod 4 ≠ 0, fails Grid SIMD assertion. ✓ // t-block=2 gives t2-local=6, 6 mod 4 != 0, fails Grid SIMD assertion.
// With {4,2,2,4}: Nsimd=4 goes into x or t (both =4). ✓ // With {4,2,2,4}: Nsimd=4 goes into x or t (both =4).
Coordinate clatt2 = clatt; Coordinate clatt2 = clatt;
clatt2[0] /= 2; clatt2[0] /= 2;
clatt2[1] /= 2; clatt2[1] /= 2;
@@ -635,7 +635,7 @@ int main (int argc, char ** argv)
typedef PVdagMLinearOperator<MobiusFermionD,LatticeFermionD> PVdagM_t; typedef PVdagMLinearOperator<MobiusFermionD,LatticeFermionD> PVdagM_t;
typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t; typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t;
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace; typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace;
typedef MGPreconditioner<vSpinColourVector,vTComplex,nbasis> TwoLevelMG; typedef MGPreconditioner<vSpinColourVector,vTComplex,nbasis> TwoLevelMG;
@@ -2,7 +2,7 @@
Grid physics library, www.github.com/paboyle/Grid Grid physics library, www.github.com/paboyle/Grid
Source file: ./examples/Example_pvdagm_v2_3level_DenseCoarseMatrix.cc Source file: ./examples/Example_pvdagm_3level_DenseCoarseMatrix.cc
Copyright (C) 2026 Copyright (C) 2026
@@ -27,7 +27,7 @@ Author: Peter Boyle <pboyle@bnl.gov>
/* END LEGAL */ /* END LEGAL */
// //
// PVdagM three level multigrid on the V2 coarse operator. // PVdagM three level multigrid on MultiGeneralCoarsenedOperator.
// //
// STAGES ONE AND TWO: grids, types, subspace, and the L1 and L2 coarsenings. // STAGES ONE AND TWO: grids, types, subspace, and the L1 and L2 coarsenings.
// The dense bottom and the solves are not here yet. // The dense bottom and the solves are not here yet.
@@ -37,12 +37,13 @@ Author: Peter Boyle <pboyle@bnl.gov>
// * The coarse space is UNVECTORISED (sComplexD). The fine space stays // * The coarse space is UNVECTORISED (sComplexD). The fine space stays
// vectorised. MultiRHSBlockProject carries the mixed layout. // vectorised. MultiRHSBlockProject carries the mixed layout.
// //
// * One operator, not two. V1 needed GeneralCoarsenedMatrix to coarsen and // * One operator, not two. The deprecated path needed
// MultiGeneralCoarsenedMatrix to apply, bridged by CopyMatrix. V2 does // DeprecatedGeneralCoarsenedMatrix to coarsen and
// both, and single versus multiRHS is SetGrid on the same object with the // DeprecatedMultiGeneralCoarsenedMatrix to apply, bridged by CopyMatrix.
// matrix elements built once. // This operator does both, and single versus multiRHS is SetGrid on the
// same object with the matrix elements built once.
// //
// * Nrhs is unconstrained. V1 required nrhs % vComplex::Nsimd() == 0 because // * Nrhs is unconstrained. The deprecated path required nrhs % vComplex::Nsimd() == 0 because
// its multiRHS grid carried the SIMD in the rhs direction. // its multiRHS grid carried the SIMD in the rhs direction.
// //
// * CoarsenOperator takes the subspace vectors, not an Aggregation. It block // * CoarsenOperator takes the subspace vectors, not an Aggregation. It block
@@ -54,7 +55,7 @@ Author: Peter Boyle <pboyle@bnl.gov>
// ||<psi|psi> - I||_F guard below is what catches that. // ||<psi|psi> - I||_F guard below is what catches that.
// //
// Env: LATT LS MASS NBASIS(compile time) NRHS BLOCK BLOCK2 COARSEN_BATCH // Env: LATT LS MASS NBASIS(compile time) NRHS BLOCK BLOCK2 COARSEN_BATCH
// HOT_START CONFIG SUBSPACE_FILE V1_CHECK MRHS_COARSEN // HOT_START CONFIG SUBSPACE_FILE DEPRECATED_CHECK MRHS_COARSEN
// //
#include <Grid/Grid.h> #include <Grid/Grid.h>
@@ -205,7 +206,7 @@ void ParseEnvironment(void)
P("PolyRecordSelect",PolyRecordSelect); P("PolyRefresh",PolyRefresh); P("PolyVerbose",PolyVerbose); P("PolyRecordSelect",PolyRecordSelect); P("PolyRefresh",PolyRefresh); P("PolyVerbose",PolyVerbose);
P("SmootherCoeffLog",SmootherCoeffLog); P("SmootherCoeffLog",SmootherCoeffLog);
// library-side knobs, read straight from the environment as the library will // library-side knobs, read straight from the environment as the library will
const char *envs[] = {"BLOCK","BLOCK2","SUBSPACE_FILE","CONFIG","HOT_START","MRHS_COARSEN","V1_CHECK", const char *envs[] = {"BLOCK","BLOCK2","SUBSPACE_FILE","CONFIG","HOT_START","MRHS_COARSEN","DEPRECATED_CHECK",
"DENSE_CC","DENSE_SCHUR","DENSE_SCHUR2D","DENSE_DEVICE_SUM","DENSE_SPLITK","DENSE_NB", "DENSE_CC","DENSE_SCHUR","DENSE_SCHUR2D","DENSE_DEVICE_SUM","DENSE_SPLITK","DENSE_NB",
"SCHUR2D_LEAF_SPAN","SCHUR2D_LEAF_LU","SCHUR2D_PROBE","SUMMA_HANDSHAKE","DENSE_APPLY_PROFILE","SLAB_FILE", "SCHUR2D_LEAF_SPAN","SCHUR2D_LEAF_LU","SCHUR2D_PROBE","SUMMA_HANDSHAKE","DENSE_APPLY_PROFILE","SLAB_FILE",
"SOLVE_SRHS"}; "SOLVE_SRHS"};
@@ -433,7 +434,7 @@ int main (int argc, char ** argv)
GridCartesian *Coarse5d = new GridCartesian(c5latt,c5simd,c5mpi); GridCartesian *Coarse5d = new GridCartesian(c5latt,c5simd,c5mpi);
// 6D coarse multiRHS grid: rhs is dim 0, undistributed and unvectorised. // 6D coarse multiRHS grid: rhs is dim 0, undistributed and unvectorised.
// No divisibility constraint on nrhs, unlike V1. // No divisibility constraint on nrhs, unlike the deprecated operators.
Coordinate cmlatt({nrhs,1,clatt[0],clatt[1],clatt[2],clatt[3]}); Coordinate cmlatt({nrhs,1,clatt[0],clatt[1],clatt[2],clatt[3]});
Coordinate cmsimd({1,1,1,1,1,1}); Coordinate cmsimd({1,1,1,1,1,1});
Coordinate cmmpi ({1,1,mpi[0],mpi[1],mpi[2],mpi[3]}); Coordinate cmmpi ({1,1,mpi[0],mpi[1],mpi[2],mpi[3]});
@@ -495,7 +496,7 @@ int main (int argc, char ** argv)
// Level 1 types: unvectorised coarse scalar // Level 1 types: unvectorised coarse scalar
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
typedef sTComplexD CComplexS; typedef sTComplexD CComplexS;
typedef MultiGeneralCoarsenedOperatorV2<vSpinColourVector,CComplexS,nbasis> CoarseOperator; typedef MultiGeneralCoarsenedOperator<vSpinColourVector,CComplexS,nbasis> CoarseOperator;
typedef CoarseOperator::CoarseVector CoarseVector; typedef CoarseOperator::CoarseVector CoarseVector;
typedef Aggregation<vSpinColourVector,CComplexS,nbasis> Subspace; typedef Aggregation<vSpinColourVector,CComplexS,nbasis> Subspace;
@@ -556,23 +557,23 @@ int main (int argc, char ** argv)
// the projector imports it below -- the 60 fine vectors are 20 GB of host memory per // the projector imports it below -- the 60 fine vectors are 20 GB of host memory per
// rank and 60 LRU-eligible device fields competing with the solver's working set) // rank and 60 LRU-eligible device fields competing with the solver's working set)
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Optional cross check of the coarse matrix elements against the V1 // Optional cross check of the coarse matrix elements against the
// path, which needs a vectorised coarse space. Block Gram-Schmidt is // deprecated path, which needs a vectorised coarse space. Block Gram-Schmidt
// idempotent, so V1 may re-orthonormalise the same vectors in place // is idempotent, so the deprecated path may re-orthonormalise the same vectors in place
// without a second copy of the subspace. // without a second copy of the subspace.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
if ( getenv("V1_CHECK") ) { if ( getenv("DEPRECATED_CHECK") ) {
typedef GeneralCoarsenedMatrix <vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix <vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef MultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MrhsLittleDiracOperator; typedef DeprecatedMultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MrhsLittleDiracOperator;
typedef Aggregation <vSpinColourVector,vTComplex,nbasis> SubspaceV; typedef Aggregation <vSpinColourVector,vTComplex,nbasis> SubspaceV;
Coordinate v5latt({1,clatt[0],clatt[1],clatt[2],clatt[3]}); Coordinate v5latt({1,clatt[0],clatt[1],clatt[2],clatt[3]});
Coordinate v5simd({1,fsimd[0],fsimd[1],fsimd[2],fsimd[3]}); Coordinate v5simd({1,fsimd[0],fsimd[1],fsimd[2],fsimd[3]});
GridCartesian *Coarse5dV = new GridCartesian(v5latt,v5simd,c5mpi); GridCartesian *Coarse5dV = new GridCartesian(v5latt,v5simd,c5mpi);
int nrhs_v1 = vComplex::Nsimd(); int nrhs_dep = vComplex::Nsimd();
Coordinate vmlatt({nrhs_v1,1,clatt[0],clatt[1],clatt[2],clatt[3]}); Coordinate vmlatt({nrhs_dep,1,clatt[0],clatt[1],clatt[2],clatt[3]});
Coordinate vmsimd({vComplex::Nsimd(),1,1,1,1,1}); Coordinate vmsimd({vComplex::Nsimd(),1,1,1,1,1});
GridCartesian *CoarseMrhsV = new GridCartesian(vmlatt,vmsimd,cmmpi); GridCartesian *CoarseMrhsV = new GridCartesian(vmlatt,vmsimd,cmmpi);
@@ -582,25 +583,30 @@ int main (int argc, char ** argv)
for(int k=0;k<nbasis;k++) AggV.subspace[k]=AggregatesGCR.subspace[k]; for(int k=0;k<nbasis;k++) AggV.subspace[k]=AggregatesGCR.subspace[k];
LittleDiracOperator LittleDiracOpPV(geomV,FGrid,Coarse5dV); LittleDiracOperator LittleDiracOpPV(geomV,FGrid,Coarse5dV);
std::cout << GridLogMessage << "*** V1 CoarsenOperator (cross check) ***" << std::endl; std::cout << GridLogMessage << "*** deprecated CoarsenOperator (cross check) ***" << std::endl;
SetFineSloppy(FineSloppyComms); SetFineSloppy(FineSloppyComms);
LittleDiracOpPV.CoarsenOperator(PVdagM,AggV); LittleDiracOpPV.CoarsenOperator(PVdagM,AggV);
SetFineSloppy(0); SetFineSloppy(0);
MrhsLittleDiracOperator mrhsV1(geomV,CoarseMrhsV); MrhsLittleDiracOperator mrhsDep(geomV,CoarseMrhsV);
mrhsV1.CopyMatrix(LittleDiracOpPV); mrhsDep.CopyMatrix(LittleDiracOpPV);
// BLAS_A is written by GridtoBLAS in lSite order and both sides carry // BLAS_A is written by GridtoBLAS in lSite order and both sides carry
// the same scalar_object, so the two are directly comparable. // the same scalar_object, so the two are directly comparable.
typedef MrhsLittleDiracOperator::calcMatrix calcMatrix; typedef MrhsLittleDiracOperator::calcMatrix calcMatrix;
int npoint = geom.npoint; int npoint = geom.npoint;
RealD num=0.0, den=0.0; RealD num=0.0, den=0.0;
// The operator holds ONE site-major matrix buffer; MatrixPointOut hands back
// stencil point p in the same lSite order as the deprecated per-point
// array, and sizes a2.
deviceVector<calcMatrix> a2;
for(int p=0;p<npoint;p++){ for(int p=0;p<npoint;p++){
int64_t sites = mrhsV1.BLAS_A[p].size(); int64_t sites = mrhsDep.BLAS_A[p].size();
GRID_ASSERT(sites == (int64_t)CoarseOpPV.BLAS_A[p].size()); CoarseOpPV.MatrixPointOut(p,a2);
GRID_ASSERT(sites == (int64_t)a2.size());
std::vector<calcMatrix> h1(sites),h2(sites); std::vector<calcMatrix> h1(sites),h2(sites);
acceleratorCopyFromDevice(&mrhsV1.BLAS_A[p][0], &h1[0],sites*sizeof(calcMatrix)); acceleratorCopyFromDevice(&mrhsDep.BLAS_A[p][0],&h1[0],sites*sizeof(calcMatrix));
acceleratorCopyFromDevice(&CoarseOpPV.BLAS_A[p][0],&h2[0],sites*sizeof(calcMatrix)); acceleratorCopyFromDevice(&a2[0], &h2[0],sites*sizeof(calcMatrix));
ComplexD *w1=(ComplexD *)&h1[0]; ComplexD *w1=(ComplexD *)&h1[0];
ComplexD *w2=(ComplexD *)&h2[0]; ComplexD *w2=(ComplexD *)&h2[0];
int64_t words = sites*sizeof(calcMatrix)/sizeof(ComplexD); int64_t words = sites*sizeof(calcMatrix)/sizeof(ComplexD);
@@ -610,8 +616,8 @@ int main (int argc, char ** argv)
den += real(w1[i])*real(w1[i])+imag(w1[i])*imag(w1[i]); den += real(w1[i])*real(w1[i])+imag(w1[i])*imag(w1[i]);
} }
} }
std::cout << GridLogMessage << "V1_CHECK: |A_V1|^2 = " << den << std::endl; std::cout << GridLogMessage << "DEPRECATED_CHECK: |A_dep|^2 = " << den << std::endl;
std::cout << GridLogMessage << "V1_CHECK: |A_V1 - A_V2|^2 / |A_V1|^2 = " << num/den << std::endl; std::cout << GridLogMessage << "DEPRECATED_CHECK: |A_dep - A|^2 / |A_dep|^2 = " << num/den << std::endl;
GRID_ASSERT( den > 0.0 ); GRID_ASSERT( den > 0.0 );
GRID_ASSERT( num/den < 1.0e-18 ); GRID_ASSERT( num/den < 1.0e-18 );
} }
@@ -627,7 +633,7 @@ int main (int argc, char ** argv)
MrhsProjector.Allocate(nbasis,FGrid,Coarse5d); MrhsProjector.Allocate(nbasis,FGrid,Coarse5d);
MrhsProjector.ImportBasis(AggregatesGCR.subspace); // block orthonormal basis MrhsProjector.ImportBasis(AggregatesGCR.subspace); // block orthonormal basis
// The basis now lives in the projector's BLAS_V (device); nothing after this // The basis now lives in the projector's BLAS_V (device); nothing after this
// point reads AggregatesGCR.subspace (V1_CHECK moved above). Free it: 60 fine // point reads AggregatesGCR.subspace (DEPRECATED_CHECK moved above). Free it: 60 fine
// fields = 20 GB host per rank, and 60 LRU-eligible device copies. // fields = 20 GB host per rank, and 60 LRU-eligible device copies.
AggregatesGCR.subspace.clear(); AggregatesGCR.subspace.shrink_to_fit(); AggregatesGCR.subspace.clear(); AggregatesGCR.subspace.shrink_to_fit();
@@ -640,7 +646,7 @@ int main (int argc, char ** argv)
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// STAGE TWO: L2 -> L3. // STAGE TWO: L2 -> L3.
// //
// The fine operator here is V2 at L1, which is natively multiRHS, so the // The fine operator here is the L1 operator, which is natively multiRHS, so the
// multiRHS driver applies with no promotion adapter: its D+1 grid IS the // multiRHS driver applies with no promotion adapter: its D+1 grid IS the
// batch grid the L1 operator is currently set to. // batch grid the L1 operator is currently set to.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@@ -662,7 +668,7 @@ int main (int argc, char ** argv)
// Coarsening deepens the tensor nest by one iScalar // Coarsening deepens the tensor nest by one iScalar
typedef CoarseVector::vector_object CoarseSiteObj; typedef CoarseVector::vector_object CoarseSiteObj;
typedef iScalar<CComplexS> CComplexS2; typedef iScalar<CComplexS> CComplexS2;
typedef MultiGeneralCoarsenedOperatorV2<CoarseSiteObj,CComplexS2,nbasis> CoarseCoarseOperator; typedef MultiGeneralCoarsenedOperator<CoarseSiteObj,CComplexS2,nbasis> CoarseCoarseOperator;
typedef CoarseCoarseOperator::CoarseVector CoarseCoarseVector; typedef CoarseCoarseOperator::CoarseVector CoarseCoarseVector;
NextToNearestStencilGeometry5D geom2(CoarseCoarse5d); NextToNearestStencilGeometry5D geom2(CoarseCoarse5d);
@@ -730,7 +736,7 @@ int main (int argc, char ** argv)
// STAGE THREE (part one): the dense bottom on L2. // STAGE THREE (part one): the dense bottom on L2.
// //
// DenseCoarseMatrix is bilingual: it takes the elements through // DenseCoarseMatrix is bilingual: it takes the elements through
// Geometry()/ExtractMatrix(), so the V2 operator serves directly. It does // Geometry()/ExtractMatrix(), so the L2 operator serves directly. It does
// detect that a multiRHS op cannot apply on the D dimensional grid and // detect that a multiRHS op cannot apply on the D dimensional grid and
// skips its own certificate and VERIFY, so the equivalent check is done // skips its own certificate and VERIFY, so the equivalent check is done
// here instead, driving the L2 operator at Nrhs 1 through a slice. // here instead, driving the L2 operator at Nrhs 1 through a slice.
@@ -740,7 +746,7 @@ int main (int argc, char ** argv)
if ( getenv("DENSE_CC")==nullptr || atoi(getenv("DENSE_CC")) ) { if ( getenv("DENSE_CC")==nullptr || atoi(getenv("DENSE_CC")) ) {
std::cout << GridLogMessage << "*** L3 dense bottom: import from the V2 L2 operator ***" << std::endl; std::cout << GridLogMessage << "*** L3 dense bottom: import from the L2 operator ***" << std::endl;
DenseCC.reset(new DenseCC_t(CoarseCoarse5d)); DenseCC.reset(new DenseCC_t(CoarseCoarse5d));
DenseCC->Import(CoarseOpL2); DenseCC->Import(CoarseOpL2);
@@ -790,7 +796,7 @@ int main (int argc, char ** argv)
auto RunSolve = [&](int nr) auto RunSolve = [&](int nr)
{ {
std::cout << GridLogMessage << "**********************************************" << std::endl; std::cout << GridLogMessage << "**********************************************" << std::endl;
std::cout << GridLogMessage << " V2 THREE-level solve, Nrhs = " << nr << std::endl; std::cout << GridLogMessage << " THREE-level solve, Nrhs = " << nr << std::endl;
// Device-memory budget BEFORE the solve (2026-08-28: NRHS=6 died in hipMalloc at the // Device-memory budget BEFORE the solve (2026-08-28: NRHS=6 died in hipMalloc at the
// first fine-smoother history allocation -- reported as an asynchronous "memory // first fine-smoother history allocation -- reported as an asynchronous "memory
// access fault" unless AMD_SERIALIZE_KERNEL/COPY made it a clean OOM). The outer // access fault" unless AMD_SERIALIZE_KERNEL/COPY made it a clean OOM). The outer
@@ -952,7 +958,7 @@ int main (int argc, char ** argv)
GridStopWatch w; w.Start(); GridStopWatch w; w.Start();
L1PGCR(src,sol); L1PGCR(src,sol);
w.Stop(); w.Stop();
std::cout << GridLogMessage << "V2 3-level solve Nrhs "<<nr<<" total " << w.Elapsed() std::cout << GridLogMessage << "3-level solve Nrhs "<<nr<<" total " << w.Elapsed()
<< " (per RHS: " << w.useconds()/1.0e6/nr << " s)" << std::endl; << " (per RHS: " << w.useconds()/1.0e6/nr << " s)" << std::endl;
// The outer operator is exact by policy; assert the state rather than // The outer operator is exact by policy; assert the state rather than
File diff suppressed because it is too large. Load diff
+12 -12
View File
@@ -212,8 +212,8 @@ public:
} }
}; };
// Lüscher deflated guesser (arXiv:0706.2298 Sec A.3) for a non-Hermitian solve. // Luscher deflated guesser (arXiv:0706.2298 Sec A.3) for a non-Hermitian solve.
// C_{st} = <psi[s] | LinOp | psi[t]>; guess = sum_s c_s psi[s] where c = C^{-1} psi† src. // C_{st} = <psi[s] | LinOp | psi[t]>; guess = sum_s c_s psi[s] where c = C^{-1} psi^dag src.
template<class Field> template<class Field>
class LuscherGuesser : public LinearFunction<Field> { class LuscherGuesser : public LinearFunction<Field> {
const std::vector<Field> &psi; const std::vector<Field> &psi;
@@ -704,7 +704,7 @@ void runMG(
TrivialPrecon<LatticeFermionD> simple_fine; TrivialPrecon<LatticeFermionD> simple_fine;
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 0→1: coarsen PVdagM, build LinOpCoarse // Level 0->1: coarsen PVdagM, build LinOpCoarse
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
LittleDiracOperator LittleDiracOpPV(geom, FGrid, Coarse5d); LittleDiracOperator LittleDiracOpPV(geom, FGrid, Coarse5d);
LittleDiracOpPV.CoarsenOperator(PVdagM, AggregatesPD); LittleDiracOpPV.CoarsenOperator(PVdagM, AggregatesPD);
@@ -736,7 +736,7 @@ void runMG(
// Optional sigma-ordering of psi_coarse (SVD_REORDER set): replace the crude // Optional sigma-ordering of psi_coarse (SVD_REORDER set): replace the crude
// first-NB_CC slice with the NB_CC most-null directions of span(psi_coarse) // first-NB_CC slice with the NB_CC most-null directions of span(psi_coarse)
// under LinOpCoarse. Nullness measure = singular values (eig of the // under LinOpCoarse. Nullness measure = singular values (eig of the
// Gram-whitened Psi†A†APsi), NOT the numerical range Q†AQ which // Gram-whitened Psi^dag A^dag APsi), NOT the numerical range Q^dag AQ which
// non-normality contaminates. The printed sigma spectrum shows where the // non-normality contaminates. The printed sigma spectrum shows where the
// truncation cliff sits. Unset => raw first-30 (crude GS-ordered slice). // truncation cliff sits. Unset => raw first-30 (crude GS-ordered slice).
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@@ -787,7 +787,7 @@ void runMG(
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 1→2: SUPERCOARSE aggregation using psi_coarse as subspace. // Level 1->2: SUPERCOARSE aggregation using psi_coarse as subspace.
// Maximal block {8,4,3,6}: CC = [3,6,8,8] = the dense-invertible floor. // Maximal block {8,4,3,6}: CC = [3,6,8,8] = the dense-invertible floor.
// //
// UNBLOCKING TRUNCATION NB_CC = 30 (first-30 slice of psi_coarse): // UNBLOCKING TRUNCATION NB_CC = 30 (first-30 slice of psi_coarse):
@@ -811,7 +811,7 @@ void runMG(
typedef typename CoarseVector::vector_object CoarseSiteObj; typedef typename CoarseVector::vector_object CoarseSiteObj;
typedef iScalar<vTComplex> vTTComplex; typedef iScalar<vTComplex> vTTComplex;
typedef GeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,NB_CC> LittleDiracOperatorL2; typedef DeprecatedGeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,NB_CC> LittleDiracOperatorL2;
typedef typename LittleDiracOperatorL2::CoarseVector CoarseCoarseVector; typedef typename LittleDiracOperatorL2::CoarseVector CoarseCoarseVector;
typedef Aggregation<CoarseSiteObj,vTTComplex,NB_CC> SubspaceL2; typedef Aggregation<CoarseSiteObj,vTTComplex,NB_CC> SubspaceL2;
typedef MGPreconditioner<CoarseSiteObj,vTTComplex,NB_CC> L1to2MG; typedef MGPreconditioner<CoarseSiteObj,vTTComplex,NB_CC> L1to2MG;
@@ -830,7 +830,7 @@ void runMG(
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// CC solve: DENSE (exact, non-iterative) by default; DENSE_CC=0 gives // CC solve: DENSE (exact, non-iterative) by default; DENSE_CC=0 gives
// the previous iterative L3PGCR + Lüscher-guesser path for A/B. // the previous iterative L3PGCR + Luscher-guesser path for A/B.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
int use_dense = 1; int use_dense = 1;
if (getenv("DENSE_CC")) use_dense = atoi(getenv("DENSE_CC")); if (getenv("DENSE_CC")) use_dense = atoi(getenv("DENSE_CC"));
@@ -859,7 +859,7 @@ void runMG(
ccGuess = &simpleCC; // exact solve ignores/overwrites any guess ccGuess = &simpleCC; // exact solve ignores/overwrites any guess
} else { } else {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Lüscher deflation guesser (arXiv:0706.2298 A.3) for the iterative CC // Luscher deflation guesser (arXiv:0706.2298 A.3) for the iterative CC
// solve, as in the earlier supercoarse configuration. // solve, as in the earlier supercoarse configuration.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
psi_cc.resize(nbasis, CoarseCoarse5d); psi_cc.resize(nbasis, CoarseCoarse5d);
@@ -900,7 +900,7 @@ void runMG(
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Coarse-level GCR smoother for Level 1→2 V-cycle. // Coarse-level GCR smoother for Level 1->2 V-cycle.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
RealD coarse_smoother_shift = 0.1; RealD coarse_smoother_shift = 0.1;
int coarse_smoother_nstep = 2; int coarse_smoother_nstep = 2;
@@ -914,7 +914,7 @@ void runMG(
CoarseSmootherGCR.SetZeroGuess(1); // post-smoother slot: caller zeroes vec2 (NOT L2MGsolver: it takes the Luscher guess) CoarseSmootherGCR.SetZeroGuess(1); // post-smoother slot: caller zeroes vec2 (NOT L2MGsolver: it takes the Luscher guess)
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 1→2 V-cycle preconditioner. // Level 1->2 V-cycle preconditioner.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
L1to2MG L1to2Precon(AggregatesL2, L1to2MG L1to2Precon(AggregatesL2,
LinOpCoarse, LinOpCoarse,
@@ -998,7 +998,7 @@ int main (int argc, char ** argv)
GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid); GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid);
GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid); GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid);
// Level 1 coarse grid: block 2^4 from fine (48×48×48×96 → 24×24×24×48, Ls=1) // Level 1 coarse grid: block 2^4 from fine (48x48x48x96 -> 24x24x24x48, Ls=1)
Coordinate clatt = lat_size; Coordinate clatt = lat_size;
// Coordinate Block1({2,2,2,2}); // Coordinate Block1({2,2,2,2});
// Coordinate Block2({8,4,3,6}); // Coordinate Block2({8,4,3,6});
@@ -1047,7 +1047,7 @@ int main (int argc, char ** argv)
typedef PVdagMLinearOperator<MobiusFermionD,LatticeFermionD> PVdagM_t; typedef PVdagMLinearOperator<MobiusFermionD,LatticeFermionD> PVdagM_t;
typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t; typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t;
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace; typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace;
typedef MGPreconditioner<vSpinColourVector,vTComplex,nbasis> TwoLevelMG; typedef MGPreconditioner<vSpinColourVector,vTComplex,nbasis> TwoLevelMG;
+17 -17
View File
@@ -323,7 +323,7 @@ void runMG(
TrivialPrecon<LatticeFermionD> simple_fine; TrivialPrecon<LatticeFermionD> simple_fine;
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 0→1: coarsen PVdagM, build LinOpCoarse // Level 0->1: coarsen PVdagM, build LinOpCoarse
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
LittleDiracOperator LittleDiracOpPV(geom, FGrid, Coarse5d); LittleDiracOperator LittleDiracOpPV(geom, FGrid, Coarse5d);
LittleDiracOpPV.CoarsenOperator(PVdagM, AggregatesPD); LittleDiracOpPV.CoarsenOperator(PVdagM, AggregatesPD);
@@ -348,7 +348,7 @@ void runMG(
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// psi_coarse: coarse projections of pre-GS fine null vectors. // psi_coarse: coarse projections of pre-GS fine null vectors.
// These are the Level 1 near-null vectors, promoted from Level 0. // These are the Level 1 near-null vectors, promoted from Level 0.
// Used as the aggregation basis for Level 1→2 coarsening. // Used as the aggregation basis for Level 1->2 coarsening.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
std::vector<CoarseVector> psi_coarse(nbasis, Coarse5d); std::vector<CoarseVector> psi_coarse(nbasis, Coarse5d);
for (int k = 0; k < nbasis; k++) for (int k = 0; k < nbasis; k++)
@@ -378,22 +378,22 @@ void runMG(
RealD normC = C.norm(); RealD normC = C.norm();
RealD normCmCdag = (C - C.adjoint()).norm(); RealD normCmCdag = (C - C.adjoint()).norm();
std::cout << GridLogMessage << "Coarse null matrix ||C|| = " << normC << std::endl; std::cout << GridLogMessage << "Coarse null matrix ||C|| = " << normC << std::endl;
std::cout << GridLogMessage << "Coarse null matrix ||C - C†||/||C|| = " << normCmCdag/normC << std::endl; std::cout << GridLogMessage << "Coarse null matrix ||C - C^dag||/||C|| = " << normCmCdag/normC << std::endl;
std::cout << GridLogMessage << "Galerkin check ||C||/||W|| = " << normC/normW << std::endl; std::cout << GridLogMessage << "Galerkin check ||C||/||W|| = " << normC/normW << std::endl;
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 1→2: set up aggregation using psi_coarse as subspace. // Level 1->2: set up aggregation using psi_coarse as subspace.
// Block factor 2,2,3,2 (removes odd local sublattice in z given MPI // Block factor 2,2,3,2 (removes odd local sublattice in z given MPI
// geometry 3×6×4×4 where z-local at Level 1 is 6). // geometry 3x6x4x4 where z-local at Level 1 is 6).
// psi_coarse are assigned directly; CoarsenOperator performs // psi_coarse are assigned directly; CoarsenOperator performs
// block-GS orthogonalisation before building LinOpCoarseCoarse. // block-GS orthogonalisation before building LinOpCoarseCoarse.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// innerProduct(CoarseSiteObj, CoarseSiteObj) returns iScalar<vTComplex>, so CComplex // innerProduct(CoarseSiteObj, CoarseSiteObj) returns iScalar<vTComplex>, so CComplex
// for the L1→L2 level must be iScalar<vTComplex>, not vTComplex. // for the L1->L2 level must be iScalar<vTComplex>, not vTComplex.
typedef typename CoarseVector::vector_object CoarseSiteObj; typedef typename CoarseVector::vector_object CoarseSiteObj;
typedef iScalar<vTComplex> vTTComplex; typedef iScalar<vTComplex> vTTComplex;
typedef GeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,NB> LittleDiracOperatorL2; typedef DeprecatedGeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,NB> LittleDiracOperatorL2;
typedef typename LittleDiracOperatorL2::CoarseVector CoarseCoarseVector; typedef typename LittleDiracOperatorL2::CoarseVector CoarseCoarseVector;
typedef Aggregation<CoarseSiteObj,vTTComplex,NB> SubspaceL2; typedef Aggregation<CoarseSiteObj,vTTComplex,NB> SubspaceL2;
typedef MGPreconditioner<CoarseSiteObj,vTTComplex,NB> L1to2MG; typedef MGPreconditioner<CoarseSiteObj,vTTComplex,NB> L1to2MG;
@@ -412,14 +412,14 @@ void runMG(
// Level 2 solver: plain GCR, no further coarsening // Level 2 solver: plain GCR, no further coarsening
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
TrivialPrecon<CoarseCoarseVector> simpleCC; TrivialPrecon<CoarseCoarseVector> simpleCC;
// L3PGCR is an inner solver inside the L1→2 V-cycle; does not need to converge // L3PGCR is an inner solver inside the L1->2 V-cycle; does not need to converge
// to fine-grid precision. Loose tolerance (3e-2) and large restart (64) to allow // to fine-grid precision. Loose tolerance (3e-2) and large restart (64) to allow
// the Krylov space to span enough of the near-null spectrum of LinOpCC per cycle. // the Krylov space to span enough of the near-null spectrum of LinOpCC per cycle.
PrecGeneralisedConjugateResidualNonHermitian<CoarseCoarseVector> L3PGCR(1.0e-4,5,LinOpCC,simpleCC,64,64); PrecGeneralisedConjugateResidualNonHermitian<CoarseCoarseVector> L3PGCR(1.0e-4,5,LinOpCC,simpleCC,64,64);
L3PGCR.Level(3); L3PGCR.Level(3);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Coarse-level GCR smoother for Level 1→2 V-cycle. // Coarse-level GCR smoother for Level 1->2 V-cycle.
// Mirrors fine-grid SmootherGCR: shifted operator + fixed step count. // Mirrors fine-grid SmootherGCR: shifted operator + fixed step count.
// coarse_smoother_shift and coarse_smoother_nstep are the tuning knobs. // coarse_smoother_shift and coarse_smoother_nstep are the tuning knobs.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@@ -433,7 +433,7 @@ void runMG(
CoarseSmootherGCR.Level(2); CoarseSmootherGCR.Level(2);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 1→2 V-cycle preconditioner. // Level 1->2 V-cycle preconditioner.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
L1to2MG L1to2Precon(AggregatesL2, L1to2MG L1to2Precon(AggregatesL2,
LinOpCoarse, LinOpCoarse,
@@ -474,7 +474,7 @@ void runMG(
f_src = one; f_src = one;
// Pre-smoother: none (TrivialPrecon); post-smoother: shifted PGCR. // Pre-smoother: none (TrivialPrecon); post-smoother: shifted PGCR.
// Coarse solver: L2MGsolver (PGCR preconditioned by Level 1→2 V-cycle). // Coarse solver: L2MGsolver (PGCR preconditioned by Level 1->2 V-cycle).
TwoLevelMG ThreeLevelPrecon(AggregatesPD, TwoLevelMG ThreeLevelPrecon(AggregatesPD,
PVdagM, PVdagM,
simple_fine, simple_fine,
@@ -518,7 +518,7 @@ int main (int argc, char ** argv)
GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid); GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid);
GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid); GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid);
// Level 1 coarse grid: block 2^4 from fine (48×48×48×96 → 24×24×24×48, Ls=1) // Level 1 coarse grid: block 2^4 from fine (48x48x48x96 -> 24x24x24x48, Ls=1)
Coordinate clatt = lat_size; Coordinate clatt = lat_size;
for (int d = 0; d < 4; d++) clatt[d] /= 2; for (int d = 0; d < 4; d++) clatt[d] /= 2;
std::cout << GridLogMessage << "Level 1 coarse lattice: " << clatt << std::endl; std::cout << GridLogMessage << "Level 1 coarse lattice: " << clatt << std::endl;
@@ -526,13 +526,13 @@ int main (int argc, char ** argv)
GridCartesian *Coarse4d = SpaceTimeGrid::makeFourDimGrid(clatt, GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi()); GridCartesian *Coarse4d = SpaceTimeGrid::makeFourDimGrid(clatt, GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi());
GridCartesian *Coarse5d = SpaceTimeGrid::makeFiveDimGrid(1,Coarse4d); GridCartesian *Coarse5d = SpaceTimeGrid::makeFiveDimGrid(1,Coarse4d);
// Level 2 coarse-coarse grid: block 2,2,3,3 from Level 1 (24×24×24×48 → 12×12×8×16, Ls=1). // Level 2 coarse-coarse grid: block 2,2,3,3 from Level 1 (24x24x24x48 -> 12x12x8x16, Ls=1).
// MPI geometry 3.6.4.4 (288 ranks): fine local {16,8,12,24}. // MPI geometry 3.6.4.4 (288 ranks): fine local {16,8,12,24}.
// Level 1 local {8,4,6,12}; Level 2 local {4,2,2,4}. // Level 1 local {8,4,6,12}; Level 2 local {4,2,2,4}.
// z blocked by 3: z-Level1-local=6; 6/3=2 (even), 6/2=3 (odd) → must use 3. // z blocked by 3: z-Level1-local=6; 6/3=2 (even), 6/2=3 (odd) -> must use 3.
// t blocked by 3: t-Level1-local=12; 12/3=4 divisible by Nsimd=4 (gen-simd-width=64). // t blocked by 3: t-Level1-local=12; 12/3=4 divisible by Nsimd=4 (gen-simd-width=64).
// t-block=2 gives t2-local=6, 6 mod 4 ≠ 0, fails Grid SIMD assertion. ✓ // t-block=2 gives t2-local=6, 6 mod 4 != 0, fails Grid SIMD assertion.
// With {4,2,2,4}: Nsimd=4 goes into x or t (both =4). ✓ // With {4,2,2,4}: Nsimd=4 goes into x or t (both =4).
Coordinate clatt2 = clatt; Coordinate clatt2 = clatt;
clatt2[0] /= 2; clatt2[0] /= 2;
clatt2[1] /= 2; clatt2[1] /= 2;
@@ -566,7 +566,7 @@ int main (int argc, char ** argv)
typedef PVdagMLinearOperator<MobiusFermionD,LatticeFermionD> PVdagM_t; typedef PVdagMLinearOperator<MobiusFermionD,LatticeFermionD> PVdagM_t;
typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t; typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t;
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace; typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace;
typedef MGPreconditioner<vSpinColourVector,vTComplex,nbasis> TwoLevelMG; typedef MGPreconditioner<vSpinColourVector,vTComplex,nbasis> TwoLevelMG;
+29 -29
View File
@@ -190,8 +190,8 @@ public:
} }
}; };
// Lüscher deflated guesser (arXiv:0706.2298 Sec A.3) for a non-Hermitian solve. // Luscher deflated guesser (arXiv:0706.2298 Sec A.3) for a non-Hermitian solve.
// C_{st} = <psi[s] | LinOp | psi[t]>; guess = sum_s c_s psi[s] where c = C^{-1} psi† src. // C_{st} = <psi[s] | LinOp | psi[t]>; guess = sum_s c_s psi[s] where c = C^{-1} psi^dag src.
template<class Field> template<class Field>
class LuscherGuesser : public LinearFunction<Field> { class LuscherGuesser : public LinearFunction<Field> {
const std::vector<Field> &psi; const std::vector<Field> &psi;
@@ -343,7 +343,7 @@ void runMG(
TrivialPrecon<LatticeFermionD> simple_fine; TrivialPrecon<LatticeFermionD> simple_fine;
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 0→1: coarsen PVdagM, build LinOpCoarse // Level 0->1: coarsen PVdagM, build LinOpCoarse
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
LittleDiracOperator LittleDiracOpPV(geom, FGrid, Coarse5d); LittleDiracOperator LittleDiracOpPV(geom, FGrid, Coarse5d);
LittleDiracOpPV.CoarsenOperator(PVdagM, AggregatesPD); LittleDiracOpPV.CoarsenOperator(PVdagM, AggregatesPD);
@@ -367,7 +367,7 @@ void runMG(
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// psi_coarse: coarse projections of pre-GS fine null vectors. // psi_coarse: coarse projections of pre-GS fine null vectors.
// These are the Level 1 near-null vectors, promoted from Level 0. // These are the Level 1 near-null vectors, promoted from Level 0.
// Used as the aggregation basis for Level 1→2 coarsening. // Used as the aggregation basis for Level 1->2 coarsening.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
std::vector<CoarseVector> psi_coarse(nbasis, Coarse5d); std::vector<CoarseVector> psi_coarse(nbasis, Coarse5d);
for (int k = 0; k < nbasis; k++) for (int k = 0; k < nbasis; k++)
@@ -397,22 +397,22 @@ void runMG(
RealD normC = C.norm(); RealD normC = C.norm();
RealD normCmCdag = (C - C.adjoint()).norm(); RealD normCmCdag = (C - C.adjoint()).norm();
std::cout << GridLogMessage << "Coarse null matrix ||C|| = " << normC << std::endl; std::cout << GridLogMessage << "Coarse null matrix ||C|| = " << normC << std::endl;
std::cout << GridLogMessage << "Coarse null matrix ||C - C†||/||C|| = " << normCmCdag/normC << std::endl; std::cout << GridLogMessage << "Coarse null matrix ||C - C^dag||/||C|| = " << normCmCdag/normC << std::endl;
std::cout << GridLogMessage << "Galerkin check ||C||/||W|| = " << normC/normW << std::endl; std::cout << GridLogMessage << "Galerkin check ||C||/||W|| = " << normC/normW << std::endl;
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 1→2: set up aggregation using psi_coarse as subspace. // Level 1->2: set up aggregation using psi_coarse as subspace.
// Block factor 2,2,3,2 (removes odd local sublattice in z given MPI // Block factor 2,2,3,2 (removes odd local sublattice in z given MPI
// geometry 3×6×4×4 where z-local at Level 1 is 6). // geometry 3x6x4x4 where z-local at Level 1 is 6).
// psi_coarse are assigned directly; CoarsenOperator performs // psi_coarse are assigned directly; CoarsenOperator performs
// block-GS orthogonalisation before building LinOpCoarseCoarse. // block-GS orthogonalisation before building LinOpCoarseCoarse.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// innerProduct(CoarseSiteObj, CoarseSiteObj) returns iScalar<vTComplex>, so CComplex // innerProduct(CoarseSiteObj, CoarseSiteObj) returns iScalar<vTComplex>, so CComplex
// for the L1→L2 level must be iScalar<vTComplex>, not vTComplex. // for the L1->L2 level must be iScalar<vTComplex>, not vTComplex.
typedef typename CoarseVector::vector_object CoarseSiteObj; typedef typename CoarseVector::vector_object CoarseSiteObj;
typedef iScalar<vTComplex> vTTComplex; typedef iScalar<vTComplex> vTTComplex;
typedef GeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,NB> LittleDiracOperatorL2; typedef DeprecatedGeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,NB> LittleDiracOperatorL2;
typedef typename LittleDiracOperatorL2::CoarseVector CoarseCoarseVector; typedef typename LittleDiracOperatorL2::CoarseVector CoarseCoarseVector;
typedef Aggregation<CoarseSiteObj,vTTComplex,NB> SubspaceL2; typedef Aggregation<CoarseSiteObj,vTTComplex,NB> SubspaceL2;
typedef MGPreconditioner<CoarseSiteObj,vTTComplex,NB> L1to2MG; typedef MGPreconditioner<CoarseSiteObj,vTTComplex,NB> L1to2MG;
@@ -430,12 +430,12 @@ void runMG(
TrivialPrecon<CoarseCoarseVector> simpleCC; TrivialPrecon<CoarseCoarseVector> simpleCC;
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Lüscher deflation guesser for L3PGCR. // Luscher deflation guesser for L3PGCR.
// Step 1: project psi_coarse[k] (promoted fine null vectors) to // Step 1: project psi_coarse[k] (promoted fine null vectors) to
// CoarseCoarseVector space — these cover the zero-momentum // CoarseCoarseVector space -- these cover the zero-momentum
// component of the near-null space of LinOpCC. // component of the near-null space of LinOpCC.
// Step 2: breed Nextra additional null vectors directly on LinOpCC // Step 2: breed Nextra additional null vectors directly on LinOpCC
// using GCR with random sources — these pick up near-null // using GCR with random sources -- these pick up near-null
// modes at all spatial frequencies not spanned by step 1. // modes at all spatial frequencies not spanned by step 1.
// Step 3: build C_{st} = <psi_cc[s]|LinOpCC|psi_cc[t]> over the // Step 3: build C_{st} = <psi_cc[s]|LinOpCC|psi_cc[t]> over the
// full augmented basis and invert directly via Eigen LU. // full augmented basis and invert directly via Eigen LU.
@@ -477,13 +477,13 @@ void runMG(
RealD normCcc = Ccc.norm(); RealD normCcc = Ccc.norm();
RealD normCccmCdag = (Ccc - Ccc.adjoint()).norm(); RealD normCccmCdag = (Ccc - Ccc.adjoint()).norm();
std::cout << GridLogMessage << "Coarse-coarse deflation matrix ||Ccc|| = " << normCcc << std::endl; std::cout << GridLogMessage << "Coarse-coarse deflation matrix ||Ccc|| = " << normCcc << std::endl;
std::cout << GridLogMessage << "Coarse-coarse deflation matrix ||Ccc-Ccc†||/||Ccc|| = " << normCccmCdag/normCcc << std::endl; std::cout << GridLogMessage << "Coarse-coarse deflation matrix ||Ccc-Ccc^dag||/||Ccc|| = " << normCccmCdag/normCcc << std::endl;
} }
Eigen::MatrixXcd Ccc_inv = Ccc.inverse(); Eigen::MatrixXcd Ccc_inv = Ccc.inverse();
LuscherGuesser<CoarseCoarseVector> CCDeflGuesser(psi_cc, Ccc_inv); LuscherGuesser<CoarseCoarseVector> CCDeflGuesser(psi_cc, Ccc_inv);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 2→3: coarsen LinOpCC using the RAW promoted psi_cc as aggregation // Level 2->3: coarsen LinOpCC using the RAW promoted psi_cc as aggregation
// to build the Level 4 (coarse-coarse-coarse) operator. // to build the Level 4 (coarse-coarse-coarse) operator.
// psi_cc[0..nbasis-1] are the coarse-coarse near-null vectors, projected // psi_cc[0..nbasis-1] are the coarse-coarse near-null vectors, projected
// from the RAW psi_coarse (themselves projected from the RAW fine null // from the RAW psi_coarse (themselves projected from the RAW fine null
@@ -492,11 +492,11 @@ void runMG(
// so assign COPIES of psi_cc and keep psi_cc itself raw. // so assign COPIES of psi_cc and keep psi_cc itself raw.
// //
// Tensor depth deepens once more: innerProduct(CoarseCoarseSiteObj,...) returns // Tensor depth deepens once more: innerProduct(CoarseCoarseSiteObj,...) returns
// iScalar<vTTComplex>, so CComplex for the L2→L3 level is iScalar<iScalar<vTComplex>>. // iScalar<vTTComplex>, so CComplex for the L2->L3 level is iScalar<iScalar<vTComplex>>.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
typedef typename CoarseCoarseVector::vector_object CoarseCoarseSiteObj; typedef typename CoarseCoarseVector::vector_object CoarseCoarseSiteObj;
typedef iScalar<vTTComplex> vTTTComplex; typedef iScalar<vTTComplex> vTTTComplex;
typedef GeneralCoarsenedMatrix<CoarseCoarseSiteObj,vTTTComplex,NB> LittleDiracOperatorL3; typedef DeprecatedGeneralCoarsenedMatrix<CoarseCoarseSiteObj,vTTTComplex,NB> LittleDiracOperatorL3;
typedef typename LittleDiracOperatorL3::CoarseVector CoarseCoarseCoarseVector; typedef typename LittleDiracOperatorL3::CoarseVector CoarseCoarseCoarseVector;
typedef Aggregation<CoarseCoarseSiteObj,vTTTComplex,NB> SubspaceL3; typedef Aggregation<CoarseCoarseSiteObj,vTTTComplex,NB> SubspaceL3;
typedef MGPreconditioner<CoarseCoarseSiteObj,vTTTComplex,NB> L2to3MG; typedef MGPreconditioner<CoarseCoarseSiteObj,vTTTComplex,NB> L2to3MG;
@@ -531,7 +531,7 @@ void runMG(
L4PGCR.Name("CCCouter"); L4PGCR.Name("CCCouter");
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 2→3 V-cycle: depth-2 SHIFTED smoother on LinOpCC + L4 bottom. // Level 2->3 V-cycle: depth-2 SHIFTED smoother on LinOpCC + L4 bottom.
// The shift slides the coarse-coarse field of values off the origin so a // The shift slides the coarse-coarse field of values off the origin so a
// 2-step smoother has something to bite on a non-normal operator (IRS idea). // 2-step smoother has something to bite on a non-normal operator (IRS idea).
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@@ -556,7 +556,7 @@ void runMG(
simpleCCC); // trivial guesser at the bottom simpleCCC); // trivial guesser at the bottom
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 3 (coarse-coarse) solve: GCR preconditioned by the L2→L3 V-cycle. // Level 3 (coarse-coarse) solve: GCR preconditioned by the L2->L3 V-cycle.
// Replaces the plain L3PGCR of the 3-level build -- the coarse-coarse level // Replaces the plain L3PGCR of the 3-level build -- the coarse-coarse level
// is now smoothed shallowly and recursed rather than solved deeply. // is now smoothed shallowly and recursed rather than solved deeply.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@@ -565,7 +565,7 @@ void runMG(
L3MGsolver.Name("CCouter"); L3MGsolver.Name("CCouter");
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Coarse-level GCR smoother for Level 1→2 V-cycle. // Coarse-level GCR smoother for Level 1->2 V-cycle.
// Mirrors fine-grid SmootherGCR: shifted operator + fixed step count. // Mirrors fine-grid SmootherGCR: shifted operator + fixed step count.
// coarse_smoother_shift and coarse_smoother_nstep are the tuning knobs. // coarse_smoother_shift and coarse_smoother_nstep are the tuning knobs.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@@ -581,15 +581,15 @@ void runMG(
CoarseSmootherGCR.Name("Csmoother"); CoarseSmootherGCR.Name("Csmoother");
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 1→2 V-cycle preconditioner. // Level 1->2 V-cycle preconditioner.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
L1to2MG L1to2Precon(AggregatesL2, L1to2MG L1to2Precon(AggregatesL2,
LinOpCoarse, LinOpCoarse,
simpleC, // no pre-smoother (matches fine-grid setup) simpleC, // no pre-smoother (matches fine-grid setup)
CoarseSmootherGCR, // post-smoother: depth-2 shifted GCR CoarseSmootherGCR, // post-smoother: depth-2 shifted GCR
LinOpCC, LinOpCC,
L3MGsolver, // coarse-coarse solve is now the L2→L3 V-cycle L3MGsolver, // coarse-coarse solve is now the L2->L3 V-cycle
CCDeflGuesser); // Lüscher guesser: psi_cc C^{-1} psi_cc† CCDeflGuesser); // Luscher guesser: psi_cc C^{-1} psi_cc^dag
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Standalone Level 1 two-level solve test. // Standalone Level 1 two-level solve test.
@@ -624,7 +624,7 @@ void runMG(
f_src = one; f_src = one;
// Pre-smoother: none (TrivialPrecon); post-smoother: shifted PGCR. // Pre-smoother: none (TrivialPrecon); post-smoother: shifted PGCR.
// Coarse solver: L2MGsolver (PGCR preconditioned by Level 1→2 V-cycle). // Coarse solver: L2MGsolver (PGCR preconditioned by Level 1->2 V-cycle).
TwoLevelMG ThreeLevelPrecon(AggregatesPD, TwoLevelMG ThreeLevelPrecon(AggregatesPD,
PVdagM, PVdagM,
simple_fine, simple_fine,
@@ -668,7 +668,7 @@ int main (int argc, char ** argv)
GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid); GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid);
GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid); GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid);
// Level 1 coarse grid: block 2^4 from fine (48×48×48×96 → 24×24×24×48, Ls=1) // Level 1 coarse grid: block 2^4 from fine (48x48x48x96 -> 24x24x24x48, Ls=1)
Coordinate clatt = lat_size; Coordinate clatt = lat_size;
for (int d = 0; d < 4; d++) clatt[d] /= 2; for (int d = 0; d < 4; d++) clatt[d] /= 2;
std::cout << GridLogMessage << "Level 1 coarse lattice: " << clatt << std::endl; std::cout << GridLogMessage << "Level 1 coarse lattice: " << clatt << std::endl;
@@ -676,13 +676,13 @@ int main (int argc, char ** argv)
GridCartesian *Coarse4d = SpaceTimeGrid::makeFourDimGrid(clatt, GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi()); GridCartesian *Coarse4d = SpaceTimeGrid::makeFourDimGrid(clatt, GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi());
GridCartesian *Coarse5d = SpaceTimeGrid::makeFiveDimGrid(1,Coarse4d); GridCartesian *Coarse5d = SpaceTimeGrid::makeFiveDimGrid(1,Coarse4d);
// Level 2 coarse-coarse grid: block 2,2,3,3 from Level 1 (24×24×24×48 → 12×12×8×16, Ls=1). // Level 2 coarse-coarse grid: block 2,2,3,3 from Level 1 (24x24x24x48 -> 12x12x8x16, Ls=1).
// MPI geometry 3.6.4.4 (288 ranks): fine local {16,8,12,24}. // MPI geometry 3.6.4.4 (288 ranks): fine local {16,8,12,24}.
// Level 1 local {8,4,6,12}; Level 2 local {4,2,2,4}. // Level 1 local {8,4,6,12}; Level 2 local {4,2,2,4}.
// z blocked by 3: z-Level1-local=6; 6/3=2 (even), 6/2=3 (odd) → must use 3. // z blocked by 3: z-Level1-local=6; 6/3=2 (even), 6/2=3 (odd) -> must use 3.
// t blocked by 3: t-Level1-local=12; 12/3=4 divisible by Nsimd=4 (gen-simd-width=64). // t blocked by 3: t-Level1-local=12; 12/3=4 divisible by Nsimd=4 (gen-simd-width=64).
// t-block=2 gives t2-local=6, 6 mod 4 ≠ 0, fails Grid SIMD assertion. ✓ // t-block=2 gives t2-local=6, 6 mod 4 != 0, fails Grid SIMD assertion.
// With {4,2,2,4}: Nsimd=4 goes into x or t (both =4). ✓ // With {4,2,2,4}: Nsimd=4 goes into x or t (both =4).
Coordinate clatt2 = clatt; Coordinate clatt2 = clatt;
clatt2[0] /= 2; clatt2[0] /= 2;
clatt2[1] /= 2; clatt2[1] /= 2;
@@ -728,7 +728,7 @@ int main (int argc, char ** argv)
typedef PVdagMLinearOperator<MobiusFermionD,LatticeFermionD> PVdagM_t; typedef PVdagMLinearOperator<MobiusFermionD,LatticeFermionD> PVdagM_t;
typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t; typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t;
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace; typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace;
typedef MGPreconditioner<vSpinColourVector,vTComplex,nbasis> TwoLevelMG; typedef MGPreconditioner<vSpinColourVector,vTComplex,nbasis> TwoLevelMG;
+38 -38
View File
@@ -190,8 +190,8 @@ public:
} }
}; };
// Lüscher deflated guesser (arXiv:0706.2298 Sec A.3) for a non-Hermitian solve. // Luscher deflated guesser (arXiv:0706.2298 Sec A.3) for a non-Hermitian solve.
// C_{st} = <psi[s] | LinOp | psi[t]>; guess = sum_s c_s psi[s] where c = C^{-1} psi† src. // C_{st} = <psi[s] | LinOp | psi[t]>; guess = sum_s c_s psi[s] where c = C^{-1} psi^dag src.
template<class Field> template<class Field>
class LuscherGuesser : public LinearFunction<Field> { class LuscherGuesser : public LinearFunction<Field> {
const std::vector<Field> &psi; const std::vector<Field> &psi;
@@ -344,7 +344,7 @@ void runMG(
TrivialPrecon<LatticeFermionD> simple_fine; TrivialPrecon<LatticeFermionD> simple_fine;
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 0→1: coarsen PVdagM, build LinOpCoarse // Level 0->1: coarsen PVdagM, build LinOpCoarse
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
LittleDiracOperator LittleDiracOpPV(geom, FGrid, Coarse5d); LittleDiracOperator LittleDiracOpPV(geom, FGrid, Coarse5d);
LittleDiracOpPV.CoarsenOperator(PVdagM, AggregatesPD); LittleDiracOpPV.CoarsenOperator(PVdagM, AggregatesPD);
@@ -368,7 +368,7 @@ void runMG(
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// psi_coarse: coarse projections of pre-GS fine null vectors. // psi_coarse: coarse projections of pre-GS fine null vectors.
// These are the Level 1 near-null vectors, promoted from Level 0. // These are the Level 1 near-null vectors, promoted from Level 0.
// Used as the aggregation basis for Level 1→2 coarsening. // Used as the aggregation basis for Level 1->2 coarsening.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
std::vector<CoarseVector> psi_coarse(nbasis, Coarse5d); std::vector<CoarseVector> psi_coarse(nbasis, Coarse5d);
for (int k = 0; k < nbasis; k++) for (int k = 0; k < nbasis; k++)
@@ -398,22 +398,22 @@ void runMG(
RealD normC = C.norm(); RealD normC = C.norm();
RealD normCmCdag = (C - C.adjoint()).norm(); RealD normCmCdag = (C - C.adjoint()).norm();
std::cout << GridLogMessage << "Coarse null matrix ||C|| = " << normC << std::endl; std::cout << GridLogMessage << "Coarse null matrix ||C|| = " << normC << std::endl;
std::cout << GridLogMessage << "Coarse null matrix ||C - C†||/||C|| = " << normCmCdag/normC << std::endl; std::cout << GridLogMessage << "Coarse null matrix ||C - C^dag||/||C|| = " << normCmCdag/normC << std::endl;
std::cout << GridLogMessage << "Galerkin check ||C||/||W|| = " << normC/normW << std::endl; std::cout << GridLogMessage << "Galerkin check ||C||/||W|| = " << normC/normW << std::endl;
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 1→2: set up aggregation using psi_coarse as subspace. // Level 1->2: set up aggregation using psi_coarse as subspace.
// Block factor 2,2,3,2 (removes odd local sublattice in z given MPI // Block factor 2,2,3,2 (removes odd local sublattice in z given MPI
// geometry 3×6×4×4 where z-local at Level 1 is 6). // geometry 3x6x4x4 where z-local at Level 1 is 6).
// psi_coarse are assigned directly; CoarsenOperator performs // psi_coarse are assigned directly; CoarsenOperator performs
// block-GS orthogonalisation before building LinOpCoarseCoarse. // block-GS orthogonalisation before building LinOpCoarseCoarse.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// innerProduct(CoarseSiteObj, CoarseSiteObj) returns iScalar<vTComplex>, so CComplex // innerProduct(CoarseSiteObj, CoarseSiteObj) returns iScalar<vTComplex>, so CComplex
// for the L1→L2 level must be iScalar<vTComplex>, not vTComplex. // for the L1->L2 level must be iScalar<vTComplex>, not vTComplex.
typedef typename CoarseVector::vector_object CoarseSiteObj; typedef typename CoarseVector::vector_object CoarseSiteObj;
typedef iScalar<vTComplex> vTTComplex; typedef iScalar<vTComplex> vTTComplex;
typedef GeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,NB> LittleDiracOperatorL2; typedef DeprecatedGeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,NB> LittleDiracOperatorL2;
typedef typename LittleDiracOperatorL2::CoarseVector CoarseCoarseVector; typedef typename LittleDiracOperatorL2::CoarseVector CoarseCoarseVector;
typedef Aggregation<CoarseSiteObj,vTTComplex,NB> SubspaceL2; typedef Aggregation<CoarseSiteObj,vTTComplex,NB> SubspaceL2;
typedef MGPreconditioner<CoarseSiteObj,vTTComplex,NB> L1to2MG; typedef MGPreconditioner<CoarseSiteObj,vTTComplex,NB> L1to2MG;
@@ -431,12 +431,12 @@ void runMG(
TrivialPrecon<CoarseCoarseVector> simpleCC; TrivialPrecon<CoarseCoarseVector> simpleCC;
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Lüscher deflation guesser for L3PGCR. // Luscher deflation guesser for L3PGCR.
// Step 1: project psi_coarse[k] (promoted fine null vectors) to // Step 1: project psi_coarse[k] (promoted fine null vectors) to
// CoarseCoarseVector space — these cover the zero-momentum // CoarseCoarseVector space -- these cover the zero-momentum
// component of the near-null space of LinOpCC. // component of the near-null space of LinOpCC.
// Step 2: breed Nextra additional null vectors directly on LinOpCC // Step 2: breed Nextra additional null vectors directly on LinOpCC
// using GCR with random sources — these pick up near-null // using GCR with random sources -- these pick up near-null
// modes at all spatial frequencies not spanned by step 1. // modes at all spatial frequencies not spanned by step 1.
// Step 3: build C_{st} = <psi_cc[s]|LinOpCC|psi_cc[t]> over the // Step 3: build C_{st} = <psi_cc[s]|LinOpCC|psi_cc[t]> over the
// full augmented basis and invert directly via Eigen LU. // full augmented basis and invert directly via Eigen LU.
@@ -478,13 +478,13 @@ void runMG(
RealD normCcc = Ccc.norm(); RealD normCcc = Ccc.norm();
RealD normCccmCdag = (Ccc - Ccc.adjoint()).norm(); RealD normCccmCdag = (Ccc - Ccc.adjoint()).norm();
std::cout << GridLogMessage << "Coarse-coarse deflation matrix ||Ccc|| = " << normCcc << std::endl; std::cout << GridLogMessage << "Coarse-coarse deflation matrix ||Ccc|| = " << normCcc << std::endl;
std::cout << GridLogMessage << "Coarse-coarse deflation matrix ||Ccc-Ccc†||/||Ccc|| = " << normCccmCdag/normCcc << std::endl; std::cout << GridLogMessage << "Coarse-coarse deflation matrix ||Ccc-Ccc^dag||/||Ccc|| = " << normCccmCdag/normCcc << std::endl;
} }
Eigen::MatrixXcd Ccc_inv = Ccc.inverse(); Eigen::MatrixXcd Ccc_inv = Ccc.inverse();
LuscherGuesser<CoarseCoarseVector> CCDeflGuesser(psi_cc, Ccc_inv); LuscherGuesser<CoarseCoarseVector> CCDeflGuesser(psi_cc, Ccc_inv);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 2→3: coarsen LinOpCC using the RAW promoted psi_cc as aggregation // Level 2->3: coarsen LinOpCC using the RAW promoted psi_cc as aggregation
// to build the Level 4 (coarse-coarse-coarse) operator. // to build the Level 4 (coarse-coarse-coarse) operator.
// psi_cc[0..nbasis-1] are the coarse-coarse near-null vectors, projected // psi_cc[0..nbasis-1] are the coarse-coarse near-null vectors, projected
// from the RAW psi_coarse (themselves projected from the RAW fine null // from the RAW psi_coarse (themselves projected from the RAW fine null
@@ -493,11 +493,11 @@ void runMG(
// so assign COPIES of psi_cc and keep psi_cc itself raw. // so assign COPIES of psi_cc and keep psi_cc itself raw.
// //
// Tensor depth deepens once more: innerProduct(CoarseCoarseSiteObj,...) returns // Tensor depth deepens once more: innerProduct(CoarseCoarseSiteObj,...) returns
// iScalar<vTTComplex>, so CComplex for the L2→L3 level is iScalar<iScalar<vTComplex>>. // iScalar<vTTComplex>, so CComplex for the L2->L3 level is iScalar<iScalar<vTComplex>>.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
typedef typename CoarseCoarseVector::vector_object CoarseCoarseSiteObj; typedef typename CoarseCoarseVector::vector_object CoarseCoarseSiteObj;
typedef iScalar<vTTComplex> vTTTComplex; typedef iScalar<vTTComplex> vTTTComplex;
typedef GeneralCoarsenedMatrix<CoarseCoarseSiteObj,vTTTComplex,NB> LittleDiracOperatorL3; typedef DeprecatedGeneralCoarsenedMatrix<CoarseCoarseSiteObj,vTTTComplex,NB> LittleDiracOperatorL3;
typedef typename LittleDiracOperatorL3::CoarseVector CoarseCoarseCoarseVector; typedef typename LittleDiracOperatorL3::CoarseVector CoarseCoarseCoarseVector;
typedef Aggregation<CoarseCoarseSiteObj,vTTTComplex,NB> SubspaceL3; typedef Aggregation<CoarseCoarseSiteObj,vTTTComplex,NB> SubspaceL3;
typedef MGPreconditioner<CoarseCoarseSiteObj,vTTTComplex,NB> L2to3MG; typedef MGPreconditioner<CoarseCoarseSiteObj,vTTTComplex,NB> L2to3MG;
@@ -514,7 +514,7 @@ void runMG(
TrivialPrecon<CoarseCoarseCoarseVector> simpleCCC; TrivialPrecon<CoarseCoarseCoarseVector> simpleCCC;
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 3→4: coarsen LinOpCCC to build the Level 5 operator, using a // Level 3->4: coarsen LinOpCCC to build the Level 5 operator, using a
// TRUNCATED basis of only the first NB5 (< nbasis) raw promoted null vectors. // TRUNCATED basis of only the first NB5 (< nbasis) raw promoted null vectors.
// psi_ccc[k] = raw psi_cc projected through the (block-GS'd) L3 aggregation // psi_ccc[k] = raw psi_cc projected through the (block-GS'd) L3 aggregation
// -- the pre-block-GS chain continued one level deeper. We keep only the // -- the pre-block-GS chain continued one level deeper. We keep only the
@@ -526,7 +526,7 @@ void runMG(
// NB: a positive result is conservative (sigma-ordering can only help); a // NB: a positive result is conservative (sigma-ordering can only help); a
// negative one is inconclusive until the sigma-ordered NB5 is tried. // negative one is inconclusive until the sigma-ordered NB5 is tried.
// //
// Tensor depth deepens once more: CComplex for the L3→L4 level is // Tensor depth deepens once more: CComplex for the L3->L4 level is
// iScalar<vTTTComplex>. NB5 (the coarse dimension) is independent of the // iScalar<vTTTComplex>. NB5 (the coarse dimension) is independent of the
// depth -- it just makes the coarsest site vector NB5-dimensional. // depth -- it just makes the coarsest site vector NB5-dimensional.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@@ -542,10 +542,10 @@ void runMG(
// Optional sigma-ordering of psi_ccc (SVD_REORDER set): replace the crude // Optional sigma-ordering of psi_ccc (SVD_REORDER set): replace the crude
// first-NB5 slice with the NB5 genuinely-most-null directions of span(psi_ccc) // first-NB5 slice with the NB5 genuinely-most-null directions of span(psi_ccc)
// under LinOpCCC. For a NON-NORMAL operator the nullness measure is the // under LinOpCCC. For a NON-NORMAL operator the nullness measure is the
// singular value of A restricted to the span -- eig of Q†A†AQ -- NOT the // singular value of A restricted to the span -- eig of Q^dag A^dag AQ -- NOT the
// numerical range Q†AQ (which non-normality contaminates). Robust route: // numerical range Q^dag AQ (which non-normality contaminates). Robust route:
// whiten by the Gram (drop near-dependent directions), Hermitian-eig the // whiten by the Gram (drop near-dependent directions), Hermitian-eig the
// whitened A†A, rotate. The printed singular spectrum IS the SVD study: where // whitened A^dag A, rotate. The printed singular spectrum IS the SVD study: where
// it falls off tells you the natural NB5, and the same numbers illuminate why // it falls off tells you the natural NB5, and the same numbers illuminate why
// the earlier singular-subspace deflation re-entered. Safe here because we // the earlier singular-subspace deflation re-entered. Safe here because we
// ORDER vectors that then feed a Galerkin projection, not REMOVE a subspace. // ORDER vectors that then feed a Galerkin projection, not REMOVE a subspace.
@@ -603,7 +603,7 @@ void runMG(
typedef typename CoarseCoarseCoarseVector::vector_object CoarseCoarseCoarseSiteObj; typedef typename CoarseCoarseCoarseVector::vector_object CoarseCoarseCoarseSiteObj;
typedef iScalar<vTTTComplex> vTTTTComplex; typedef iScalar<vTTTComplex> vTTTTComplex;
typedef GeneralCoarsenedMatrix<CoarseCoarseCoarseSiteObj,vTTTTComplex,NB5> LittleDiracOperatorL4; typedef DeprecatedGeneralCoarsenedMatrix<CoarseCoarseCoarseSiteObj,vTTTTComplex,NB5> LittleDiracOperatorL4;
typedef typename LittleDiracOperatorL4::CoarseVector CoarseCoarseCoarseCoarseVector; typedef typename LittleDiracOperatorL4::CoarseVector CoarseCoarseCoarseCoarseVector;
typedef Aggregation<CoarseCoarseCoarseSiteObj,vTTTTComplex,NB5> SubspaceL4; typedef Aggregation<CoarseCoarseCoarseSiteObj,vTTTTComplex,NB5> SubspaceL4;
typedef MGPreconditioner<CoarseCoarseCoarseSiteObj,vTTTTComplex,NB5> L3to4MG; typedef MGPreconditioner<CoarseCoarseCoarseSiteObj,vTTTTComplex,NB5> L3to4MG;
@@ -635,7 +635,7 @@ void runMG(
L5PGCR.Name("CCCCouter"); L5PGCR.Name("CCCCouter");
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 3→4 V-cycle: depth-2 SHIFTED smoother on LinOpCCC + Level 5 bottom. // Level 3->4 V-cycle: depth-2 SHIFTED smoother on LinOpCCC + Level 5 bottom.
// Level 4 is no longer the bottom -- it is smoothed shallowly and recursed to // Level 4 is no longer the bottom -- it is smoothed shallowly and recursed to
// Level 5, mirroring how Level 3 recurses to Level 4. // Level 5, mirroring how Level 3 recurses to Level 4.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@@ -660,14 +660,14 @@ void runMG(
simpleCCCC); // trivial guesser at the bottom simpleCCCC); // trivial guesser at the bottom
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 4 (coarse-coarse-coarse) solve: GCR preconditioned by the L3→L4 V-cycle. // Level 4 (coarse-coarse-coarse) solve: GCR preconditioned by the L3->L4 V-cycle.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
PrecGeneralisedConjugateResidualNonHermitian<CoarseCoarseCoarseVector> L4MGsolver(1.0e-1,200,LinOpCCC,L3to4Precon,16,16); PrecGeneralisedConjugateResidualNonHermitian<CoarseCoarseCoarseVector> L4MGsolver(1.0e-1,200,LinOpCCC,L3to4Precon,16,16);
L4MGsolver.Level(4); L4MGsolver.Level(4);
L4MGsolver.Name("CCCouter"); L4MGsolver.Name("CCCouter");
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 2→3 V-cycle: depth-2 SHIFTED smoother on LinOpCC + Level 4 solve. // Level 2->3 V-cycle: depth-2 SHIFTED smoother on LinOpCC + Level 4 solve.
// The shift slides the coarse-coarse field of values off the origin so a // The shift slides the coarse-coarse field of values off the origin so a
// 2-step smoother has something to bite on a non-normal operator (IRS idea). // 2-step smoother has something to bite on a non-normal operator (IRS idea).
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@@ -688,11 +688,11 @@ void runMG(
simpleCC, // no pre-smoother simpleCC, // no pre-smoother
CoarseCoarseSmootherGCR, // post-smoother: depth-2 shifted GCR CoarseCoarseSmootherGCR, // post-smoother: depth-2 shifted GCR
LinOpCCC, LinOpCCC,
L4MGsolver, // coarse solve is now the L3→L4 V-cycle L4MGsolver, // coarse solve is now the L3->L4 V-cycle
simpleCCC); // trivial guesser simpleCCC); // trivial guesser
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 3 (coarse-coarse) solve: GCR preconditioned by the L2→L3 V-cycle. // Level 3 (coarse-coarse) solve: GCR preconditioned by the L2->L3 V-cycle.
// Replaces the plain L3PGCR of the 3-level build -- the coarse-coarse level // Replaces the plain L3PGCR of the 3-level build -- the coarse-coarse level
// is now smoothed shallowly and recursed rather than solved deeply. // is now smoothed shallowly and recursed rather than solved deeply.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@@ -701,7 +701,7 @@ void runMG(
L3MGsolver.Name("CCouter"); L3MGsolver.Name("CCouter");
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Coarse-level GCR smoother for Level 1→2 V-cycle. // Coarse-level GCR smoother for Level 1->2 V-cycle.
// Mirrors fine-grid SmootherGCR: shifted operator + fixed step count. // Mirrors fine-grid SmootherGCR: shifted operator + fixed step count.
// coarse_smoother_shift and coarse_smoother_nstep are the tuning knobs. // coarse_smoother_shift and coarse_smoother_nstep are the tuning knobs.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@@ -717,15 +717,15 @@ void runMG(
CoarseSmootherGCR.Name("Csmoother"); CoarseSmootherGCR.Name("Csmoother");
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Level 1→2 V-cycle preconditioner. // Level 1->2 V-cycle preconditioner.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
L1to2MG L1to2Precon(AggregatesL2, L1to2MG L1to2Precon(AggregatesL2,
LinOpCoarse, LinOpCoarse,
simpleC, // no pre-smoother (matches fine-grid setup) simpleC, // no pre-smoother (matches fine-grid setup)
CoarseSmootherGCR, // post-smoother: depth-2 shifted GCR CoarseSmootherGCR, // post-smoother: depth-2 shifted GCR
LinOpCC, LinOpCC,
L3MGsolver, // coarse-coarse solve is now the L2→L3 V-cycle L3MGsolver, // coarse-coarse solve is now the L2->L3 V-cycle
CCDeflGuesser); // Lüscher guesser: psi_cc C^{-1} psi_cc† CCDeflGuesser); // Luscher guesser: psi_cc C^{-1} psi_cc^dag
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Standalone Level 1 two-level solve test. // Standalone Level 1 two-level solve test.
@@ -760,7 +760,7 @@ void runMG(
f_src = one; f_src = one;
// Pre-smoother: none (TrivialPrecon); post-smoother: shifted PGCR. // Pre-smoother: none (TrivialPrecon); post-smoother: shifted PGCR.
// Coarse solver: L2MGsolver (PGCR preconditioned by Level 1→2 V-cycle). // Coarse solver: L2MGsolver (PGCR preconditioned by Level 1->2 V-cycle).
TwoLevelMG ThreeLevelPrecon(AggregatesPD, TwoLevelMG ThreeLevelPrecon(AggregatesPD,
PVdagM, PVdagM,
simple_fine, simple_fine,
@@ -804,7 +804,7 @@ int main (int argc, char ** argv)
GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid); GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid);
GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid); GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid);
// Level 1 coarse grid: block 2^4 from fine (48×48×48×96 → 24×24×24×48, Ls=1) // Level 1 coarse grid: block 2^4 from fine (48x48x48x96 -> 24x24x24x48, Ls=1)
Coordinate clatt = lat_size; Coordinate clatt = lat_size;
for (int d = 0; d < 4; d++) clatt[d] /= 2; for (int d = 0; d < 4; d++) clatt[d] /= 2;
std::cout << GridLogMessage << "Level 1 coarse lattice: " << clatt << std::endl; std::cout << GridLogMessage << "Level 1 coarse lattice: " << clatt << std::endl;
@@ -812,13 +812,13 @@ int main (int argc, char ** argv)
GridCartesian *Coarse4d = SpaceTimeGrid::makeFourDimGrid(clatt, GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi()); GridCartesian *Coarse4d = SpaceTimeGrid::makeFourDimGrid(clatt, GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi());
GridCartesian *Coarse5d = SpaceTimeGrid::makeFiveDimGrid(1,Coarse4d); GridCartesian *Coarse5d = SpaceTimeGrid::makeFiveDimGrid(1,Coarse4d);
// Level 2 coarse-coarse grid: block 2,2,3,3 from Level 1 (24×24×24×48 → 12×12×8×16, Ls=1). // Level 2 coarse-coarse grid: block 2,2,3,3 from Level 1 (24x24x24x48 -> 12x12x8x16, Ls=1).
// MPI geometry 3.6.4.4 (288 ranks): fine local {16,8,12,24}. // MPI geometry 3.6.4.4 (288 ranks): fine local {16,8,12,24}.
// Level 1 local {8,4,6,12}; Level 2 local {4,2,2,4}. // Level 1 local {8,4,6,12}; Level 2 local {4,2,2,4}.
// z blocked by 3: z-Level1-local=6; 6/3=2 (even), 6/2=3 (odd) → must use 3. // z blocked by 3: z-Level1-local=6; 6/3=2 (even), 6/2=3 (odd) -> must use 3.
// t blocked by 3: t-Level1-local=12; 12/3=4 divisible by Nsimd=4 (gen-simd-width=64). // t blocked by 3: t-Level1-local=12; 12/3=4 divisible by Nsimd=4 (gen-simd-width=64).
// t-block=2 gives t2-local=6, 6 mod 4 ≠ 0, fails Grid SIMD assertion. ✓ // t-block=2 gives t2-local=6, 6 mod 4 != 0, fails Grid SIMD assertion.
// With {4,2,2,4}: Nsimd=4 goes into x or t (both =4). ✓ // With {4,2,2,4}: Nsimd=4 goes into x or t (both =4).
Coordinate clatt2 = clatt; Coordinate clatt2 = clatt;
clatt2[0] /= 2; clatt2[0] /= 2;
clatt2[1] /= 2; clatt2[1] /= 2;
@@ -880,7 +880,7 @@ int main (int argc, char ** argv)
typedef PVdagMLinearOperator<MobiusFermionD,LatticeFermionD> PVdagM_t; typedef PVdagMLinearOperator<MobiusFermionD,LatticeFermionD> PVdagM_t;
typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t; typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t;
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace; typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace;
typedef MGPreconditioner<vSpinColourVector,vTComplex,nbasis> TwoLevelMG; typedef MGPreconditioner<vSpinColourVector,vTComplex,nbasis> TwoLevelMG;
+2 -2
View File
@@ -49,7 +49,7 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
// sigma_min << min|lambda| : non-normal near origin // sigma_min << min|lambda| : non-normal near origin
// lambda_min(H) < 0 : half-plane condition violated // lambda_min(H) < 0 : half-plane condition violated
// //
// Requires the dagger code path in GeneralCoarsenedMatrix: // Requires the dagger code path in DeprecatedGeneralCoarsenedMatrix:
// _Adag allocated, PopulateAdag active, _Adag exchanged, hermitian=0. // _Adag allocated, PopulateAdag active, _Adag exchanged, hermitian=0.
// //
// Env vars: // Env vars:
@@ -308,7 +308,7 @@ int main (int argc, char ** argv)
typedef PVdagMLinearOperator<MobiusFermionD,LatticeFermionD> PVdagM_t; typedef PVdagMLinearOperator<MobiusFermionD,LatticeFermionD> PVdagM_t;
typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t; typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t;
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace; typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace;
+1 -1
View File
@@ -275,7 +275,7 @@ int main (int argc, char ** argv)
MobiusFermionD Dpv (Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,1.0, M5,b,c); MobiusFermionD Dpv (Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,1.0, M5,b,c);
typedef PVdagMLinearOperator<MobiusFermionD,LatticeFermionD> PVdagM_t; typedef PVdagMLinearOperator<MobiusFermionD,LatticeFermionD> PVdagM_t;
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
// Grid index contraction is positional (colour/spin/lorentz order is meaningful), // Grid index contraction is positional (colour/spin/lorentz order is meaningful),
// so each MG projection adds one index to the tensor nest rather than reusing a slot: // so each MG projection adds one index to the tensor nest rather than reusing a slot:
+3 -3
View File
@@ -38,7 +38,7 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
// precon: V-cycle -- per-RHS fine post-smoother (16-step shifted GCR), // precon: V-cycle -- per-RHS fine post-smoother (16-step shifted GCR),
// batched restriction (MultiRHSBlockProject / GEMM), // batched restriction (MultiRHSBlockProject / GEMM),
// ONE coarse PGCR on the 6D mrhs coarse operator // ONE coarse PGCR on the 6D mrhs coarse operator
// (MultiGeneralCoarsenedMatrix, GEMM mults -- the ~10x win), // (DeprecatedMultiGeneralCoarsenedMatrix, GEMM mults -- the ~10x win),
// batched prolongation. // batched prolongation.
// //
// The coarse operator is coarsened once with the standard single-RHS // The coarse operator is coarsened once with the standard single-RHS
@@ -260,8 +260,8 @@ int main (int argc, char ** argv)
typedef PVdagMLinearOperator<MobiusFermionD,LatticeFermionD> PVdagM_t; typedef PVdagMLinearOperator<MobiusFermionD,LatticeFermionD> PVdagM_t;
typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t; typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t;
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef MultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MrhsLittleDiracOperator; typedef DeprecatedMultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MrhsLittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace; typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace;
+5 -5
View File
@@ -23,7 +23,7 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
// with L3_DEFL=0 (NO deflation), applied to the enlarged block-diagonal mRHS system: // with L3_DEFL=0 (NO deflation), applied to the enlarged block-diagonal mRHS system:
// the coarse and coarse-coarse levels run a SINGLE Krylov (one GCR polynomial, inner // the coarse and coarse-coarse levels run a SINGLE Krylov (one GCR polynomial, inner
// products summed over rhs) on the packed 6D mrhs fields, so both coarse levels batch // products summed over rhs) on the packed 6D mrhs fields, so both coarse levels batch
// through GEMM (MultiGeneralCoarsenedMatrix) -- the valence throughput win at BOTH levels. // through GEMM (DeprecatedMultiGeneralCoarsenedMatrix) -- the valence throughput win at BOTH levels.
// //
// Level structure (each coarse level is a single-field PGCR on a packed 6D mrhs field): // Level structure (each coarse level is a single-field PGCR on a packed 6D mrhs field):
// L1 (fine) : std::vector<LatticeFermionD>, MrhsPGCRNonHermitian on PVdagM, // L1 (fine) : std::vector<LatticeFermionD>, MrhsPGCRNonHermitian on PVdagM,
@@ -227,16 +227,16 @@ int main (int argc, char ** argv)
typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t; typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t;
// Level 1 tensor types // Level 1 tensor types
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef MultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MrhsLittleDiracOperator; typedef DeprecatedMultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MrhsLittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace; typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace;
// Level 2 tensor types (coarsening deepens the nest by one iScalar -- see CLAUDE.md) // Level 2 tensor types (coarsening deepens the nest by one iScalar -- see CLAUDE.md)
typedef CoarseVector::vector_object CoarseSiteObj; typedef CoarseVector::vector_object CoarseSiteObj;
typedef iScalar<vTComplex> vTTComplex; typedef iScalar<vTComplex> vTTComplex;
typedef GeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,nbasis> LittleDiracOperatorL2; typedef DeprecatedGeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,nbasis> LittleDiracOperatorL2;
typedef MultiGeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,nbasis> MrhsLittleDiracOperatorL2; typedef DeprecatedMultiGeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,nbasis> MrhsLittleDiracOperatorL2;
typedef LittleDiracOperatorL2::CoarseVector CoarseCoarseVector; typedef LittleDiracOperatorL2::CoarseVector CoarseCoarseVector;
typedef Aggregation<CoarseSiteObj,vTTComplex,nbasis> SubspaceL2; typedef Aggregation<CoarseSiteObj,vTTComplex,nbasis> SubspaceL2;
@@ -233,16 +233,16 @@ int main (int argc, char ** argv)
typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t; typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t;
// Level 1 tensor types // Level 1 tensor types
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef MultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MrhsLittleDiracOperator; typedef DeprecatedMultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MrhsLittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace; typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace;
// Level 2 tensor types (coarsening deepens the nest by one iScalar) // Level 2 tensor types (coarsening deepens the nest by one iScalar)
typedef CoarseVector::vector_object CoarseSiteObj; typedef CoarseVector::vector_object CoarseSiteObj;
typedef iScalar<vTComplex> vTTComplex; typedef iScalar<vTComplex> vTTComplex;
typedef GeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,nbasis> LittleDiracOperatorL2; typedef DeprecatedGeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,nbasis> LittleDiracOperatorL2;
typedef MultiGeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,nbasis> MrhsLittleDiracOperatorL2; typedef DeprecatedMultiGeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,nbasis> MrhsLittleDiracOperatorL2;
typedef LittleDiracOperatorL2::CoarseVector CoarseCoarseVector; typedef LittleDiracOperatorL2::CoarseVector CoarseCoarseVector;
typedef Aggregation<CoarseSiteObj,vTTComplex,nbasis> SubspaceL2; typedef Aggregation<CoarseSiteObj,vTTComplex,nbasis> SubspaceL2;
+5 -5
View File
@@ -237,7 +237,7 @@ public:
#endif #endif
// NB: the apply GEMM (Y = slab^dag X) is a tiny-output/huge-K shape that // NB: the apply GEMM (Y = slab^dag X) is a tiny-output/huge-K shape that
// under-fills the GPU (~13ms). The fix is a software split-K via // under-fills the GPU (~13ms). The fix is a software split-K via
// GridBLAS.gemmBatched (see MultiRHSBlockCGLinalg.h / 2409.03904 Fig 11) — // GridBLAS.gemmBatched (see MultiRHSBlockCGLinalg.h / 2409.03904 Fig 11) --
// NOT a raw rocblas strided-batched batch, which hung on Frontier and was // NOT a raw rocblas strided-batched batch, which hung on Frontier and was
// removed. TODO: reimplement through GridBLAS when the ~1.4 s/RHS is wanted. // removed. TODO: reimplement through GridBLAS when the ~1.4 s/RHS is wanted.
@@ -891,16 +891,16 @@ int main (int argc, char ** argv)
typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t; typedef ShiftedPVdagMLinearOperator<MobiusFermionD,LatticeFermionD> ShiftedPVdagM_t;
// Level 1 tensor types // Level 1 tensor types
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef MultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MrhsLittleDiracOperator; typedef DeprecatedMultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MrhsLittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace; typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace;
// Level 2 tensor types (coarsening deepens the nest by one iScalar) // Level 2 tensor types (coarsening deepens the nest by one iScalar)
typedef CoarseVector::vector_object CoarseSiteObj; typedef CoarseVector::vector_object CoarseSiteObj;
typedef iScalar<vTComplex> vTTComplex; typedef iScalar<vTComplex> vTTComplex;
typedef GeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,nbasis> LittleDiracOperatorL2; typedef DeprecatedGeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,nbasis> LittleDiracOperatorL2;
typedef MultiGeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,nbasis> MrhsLittleDiracOperatorL2; typedef DeprecatedMultiGeneralCoarsenedMatrix<CoarseSiteObj,vTTComplex,nbasis> MrhsLittleDiracOperatorL2;
typedef LittleDiracOperatorL2::CoarseVector CoarseCoarseVector; typedef LittleDiracOperatorL2::CoarseVector CoarseCoarseVector;
typedef Aggregation<CoarseSiteObj,vTTComplex,nbasis> SubspaceL2; typedef Aggregation<CoarseSiteObj,vTTComplex,nbasis> SubspaceL2;
+9 -3
View File
@@ -2,13 +2,15 @@ SUBDIRS = .
include Make.inc include Make.inc
# Precision variants of the two multigrid drivers. The coarse-sector and # Compile-time variants of the two multigrid drivers. The coarse-sector and
# dense-inversion precisions are compile-time instantiations, so each is its # dense-inversion precisions are compile-time instantiations, so each is its
# own binary built from the same source; the fine-level precision is a # own binary built from the same source; the fine-level precision is a
# run-time parameter and needs no variant. Per-target flags give each # run-time parameter and needs no variant. The recur variant additionally
# variant its own object file. # takes the GCR residual norm from the recurrence rather than a reduction.
# Per-target flags give each variant its own object file.
bin_PROGRAMS += Example_pvdagm_multigrid_fp32coarse \ bin_PROGRAMS += Example_pvdagm_multigrid_fp32coarse \
Example_pvdagm_multigrid_fp32coarse_fp32dense \ Example_pvdagm_multigrid_fp32coarse_fp32dense \
Example_pvdagm_multigrid_fp32coarse_fp32dense_recur \
Example_hdcg_multigrid_fp32coarse Example_hdcg_multigrid_fp32coarse
Example_pvdagm_multigrid_fp32coarse_SOURCES = Example_pvdagm_multigrid.cc Example_pvdagm_multigrid_fp32coarse_SOURCES = Example_pvdagm_multigrid.cc
@@ -19,6 +21,10 @@ Example_pvdagm_multigrid_fp32coarse_fp32dense_SOURCES = Example_pvdagm_multigri
Example_pvdagm_multigrid_fp32coarse_fp32dense_CPPFLAGS = -DCOARSE_SINGLE -DGRID_DENSE_INVERSE_SINGLE Example_pvdagm_multigrid_fp32coarse_fp32dense_CPPFLAGS = -DCOARSE_SINGLE -DGRID_DENSE_INVERSE_SINGLE
Example_pvdagm_multigrid_fp32coarse_fp32dense_LDADD = $(top_builddir)/Grid/libGrid.a Example_pvdagm_multigrid_fp32coarse_fp32dense_LDADD = $(top_builddir)/Grid/libGrid.a
Example_pvdagm_multigrid_fp32coarse_fp32dense_recur_SOURCES = Example_pvdagm_multigrid.cc
Example_pvdagm_multigrid_fp32coarse_fp32dense_recur_CPPFLAGS = -DCOARSE_SINGLE -DGRID_DENSE_INVERSE_SINGLE -DGRID_GCR_RESIDUAL_RECURRENCE
Example_pvdagm_multigrid_fp32coarse_fp32dense_recur_LDADD = $(top_builddir)/Grid/libGrid.a
Example_hdcg_multigrid_fp32coarse_SOURCES = Example_hdcg_multigrid.cc Example_hdcg_multigrid_fp32coarse_SOURCES = Example_hdcg_multigrid.cc
Example_hdcg_multigrid_fp32coarse_CPPFLAGS = -DCOARSE_SINGLE Example_hdcg_multigrid_fp32coarse_CPPFLAGS = -DCOARSE_SINGLE
Example_hdcg_multigrid_fp32coarse_LDADD = $(top_builddir)/Grid/libGrid.a Example_hdcg_multigrid_fp32coarse_LDADD = $(top_builddir)/Grid/libGrid.a
+142
View File
@@ -0,0 +1,142 @@
#!/usr/bin/env python3
"""
Reconstruct the GCR smoother polynomial from PGCR coefficient logs
(SmootherCoeffLog=1 in Example_pvdagm_3level_DenseCoarseMatrix, or
LogCoefficients(1) on any PrecGeneralisedConjugateResidualNonHermitian).
GCR with z = r (trivial preconditioner) builds p_k = P_k(A) r0,
r_k = R_k(A) r0:
R_0 = P_0 = 1
R_{k+1}(l) = R_k(l) - a_k l P_k(l)
P_{k+1}(l) = R_{k+1}(l) + sum_j b_{k,j} P_{k-j}(l)
so after m steps x_m = S(A) r0 with S = sum_k a_k P_k and residual
polynomial R_m = 1 - l S(l), R_m(0) = 1.
Usage
gcr_polynomial.py LOG [--name Fsmoother] [--lmin 0] [--lmax 3] [--n 600]
writes <name>_poly.dat : l |R_mean(l)| Re R_mean Im R_mean |R|min |R|max |S_mean(l)|
where mean = polynomial built from the per-step MEAN coefficients over
all calls, and min/max are the envelope over the individual calls.
Prints the mean coefficients and their relative spread per step.
gcr_polynomial.py --selftest
runs GCR in numpy on a random non-normal matrix, logs a,b the same way,
and checks R_m(A) r0 == the actual residual to rounding.
gnuplot:
set logscale y; plot 'Fsmoother_poly.dat' u 1:2 w l t '|R_m| mean', \
'' u 1:5 w l t 'min', '' u 1:6 w l t 'max'
"""
import re, sys, argparse
import numpy as np
from numpy.polynomial import polynomial as Poly # coefficient arrays, low->high
A_RE = re.compile(r'(\S+)\s+coeff\[(\d+)\]\s+a=\(([^,]+),([^)]+)\)')
B_RE = re.compile(r'(\S+)\s+coeff\[(\d+)\]((?:\s+b\[\d+\]=\([^)]*\))+)')
BJ_RE = re.compile(r'b\[(\d+)\]=\(([^,]+),([^)]+)\)')
def parse(path, name):
"""-> list of calls; each call = list of (a_k, [b_k0, b_k1, ...]) in step order."""
calls, cur = [], None
for line in open(path, errors='replace'):
m = A_RE.search(line)
if m and m.group(1) == name:
k = int(m.group(2)); a = complex(float(m.group(3)), float(m.group(4)))
if k == 0:
if cur: calls.append(cur)
cur = []
if cur is not None:
cur.append([a, []])
continue
m = B_RE.search(line)
if m and m.group(1) == name and cur is not None:
k = int(m.group(2))
bs = [complex(float(x[1]), float(x[2])) for x in BJ_RE.findall(m.group(3))]
if k < len(cur): cur[k][1] = bs
if cur: calls.append(cur)
return calls
def polynomials(coeffs):
"""coeffs: list of (a_k, [b_kj]) -> (S, R) as low->high complex coefficient arrays."""
R = [np.array([1.0+0j])]; P = [np.array([1.0+0j])]
S = np.array([0.0+0j])
for k, (a, bs) in enumerate(coeffs):
S = Poly.polyadd(S, a*P[k])
Rn = Poly.polysub(R[k], a*Poly.polymulx(P[k])) # R_{k+1} = R_k - a l P_k
Pn = Rn.copy()
for j, b in enumerate(bs): # P_{k+1} = R_{k+1} + sum b_kj P_{k-j}
Pn = Poly.polyadd(Pn, b*P[k-j])
R.append(Rn); P.append(Pn)
return S, R[-1]
def mean_coeffs(calls):
m = min(len(c) for c in calls)
out, spread = [], []
for k in range(m):
a = np.array([c[k][0] for c in calls])
nb = min(len(c[k][1]) for c in calls)
bs = [np.array([c[k][1][j] for c in calls]) for j in range(nb)]
out.append((a.mean(), [b.mean() for b in bs]))
sa = a.std()/max(abs(a.mean()),1e-300)
sb = [b.std()/max(abs(b.mean()),1e-300) for b in bs]
spread.append((sa, sb))
return out, spread
def selftest(n=40, m=6, mmax=6, seed=1):
rng = np.random.default_rng(seed)
A = np.eye(n)*1.5 + 0.4*rng.standard_normal((n,n)) + 0.2j*rng.standard_normal((n,n)) # non-normal
r0 = rng.standard_normal(n) + 1j*rng.standard_normal(n)
r = r0.copy(); x = np.zeros(n, complex)
p = [r.copy()]; q = [A@r]; qq = [np.vdot(q[0],q[0]).real]
log = []
for k in range(m):
a = np.vdot(q[k], r)/qq[k] # <q,r>/<q,q>
x = x + a*p[k]; r = r - a*q[k]
z = r; Az = A@z; pn = z.copy(); qn = Az.copy(); bs = []
for back in range(min(k+1, mmax-1)):
b = -(np.vdot(q[k-back], Az)/qq[k-back]).real # real part, as Grid does
pn = pn + b*p[k-back]; qn = qn + b*q[k-back]; bs.append(complex(b))
p.append(pn); q.append(qn); qq.append(np.vdot(qn,qn).real)
log.append((a, bs))
S, R = polynomials(log)
def apply(poly, v):
out = np.zeros_like(v); Ak = v.copy()
for c in poly: out = out + c*Ak; Ak = A@Ak
return out
err_r = np.linalg.norm(apply(R, r0) - r)/np.linalg.norm(r)
err_x = np.linalg.norm(apply(S, r0) - x)/np.linalg.norm(x)
print(f"selftest: |R_m(A)r0 - r_m|/|r_m| = {err_r:.2e} |S(A)r0 - x_m|/|x_m| = {err_x:.2e}")
return err_r < 1e-10 and err_x < 1e-10
if __name__ == "__main__":
ap = argparse.ArgumentParser()
ap.add_argument("log", nargs="?")
ap.add_argument("--name", default="Fsmoother")
ap.add_argument("--lmin", type=float, default=0.0)
ap.add_argument("--lmax", type=float, default=3.0)
ap.add_argument("--n", type=int, default=600)
ap.add_argument("--selftest", action="store_true")
args = ap.parse_args()
if args.selftest:
sys.exit(0 if selftest() else 1)
calls = parse(args.log, args.name)
if not calls:
sys.exit(f"no '{args.name} coeff[...]' lines found in {args.log}")
mean, spread = mean_coeffs(calls)
print(f"{args.name}: {len(calls)} calls, {len(mean)} steps")
for k,(a,bs) in enumerate(mean):
print(f" step {k}: a = ({a.real:+.5f},{a.imag:+.5f}) rel spread {spread[k][0]:.3f} "
+ " ".join(f"b[{j}]={b.real:+.5f} (spread {spread[k][1][j]:.3f})" for j,b in enumerate(bs)))
S, R = polynomials(mean)
print(" S coefficients (low->high):", np.array2string(S, precision=5))
lam = np.linspace(args.lmin, args.lmax, args.n)
Rm = Poly.polyval(lam, R); Sm = Poly.polyval(lam, S)
per = np.array([np.abs(Poly.polyval(lam, polynomials(c)[1])) for c in calls])
out = f"{args.name}_poly.dat"
with open(out, "w") as f:
f.write("# lambda |R_mean| ReR ImR |R|min |R|max |S_mean|\n")
for i,l in enumerate(lam):
f.write(f"{l:.6f} {abs(Rm[i]):.6e} {Rm[i].real:.6e} {Rm[i].imag:.6e} "
f"{per[:,i].min():.6e} {per[:,i].max():.6e} {abs(Sm[i]):.6e}\n")
print(f" wrote {out}")
+3 -3
View File
@@ -221,11 +221,11 @@ judgement.
the exact inverse of whatever matrix it was handed. Both certify the import the exact inverse of whatever matrix it was handed. Both certify the import
and inversion, not the accuracy of `A_c`. and inversion, not the accuracy of `A_c`.
The instruments that DO see it: coarsen twice and compare `BLAS_A` element by The instruments that DO see it: coarsen twice and compare `BLAS_A` element by
element against an exact run (the comparison `Test_coarse_v2_coarsen` already element against an exact run (the comparison `Test_coarse_coarsen` already
performs between V1 and V2), or the outer iteration count and time to performs against the deprecated operator), or the outer iteration count and time to
solution. solution.
- **Does not reach the coarse levels.** `SloppyComms` sets a flag on - **Does not reach the coarse levels.** `SloppyComms` sets a flag on
`Stencil`/`StencilEven`/`StencilOdd`; V2's `M` uses `PaddedCell::Face_exchange`, `Stencil`/`StencilEven`/`StencilOdd`; the coarse operator.s `M` uses `PaddedCell::Face_exchange`,
which sends raw `vobj` bytes with no precision option. Coarse levels are less which sends raw `vobj` bytes with no precision option. Coarse levels are less
bandwidth sensitive, so this matters less than it sounds. bandwidth sensitive, so this matters less than it sounds.
- **Object sharing caveat:** the flag is per fermion operator. A single - **Object sharing caveat:** the flag is per fermion operator. A single
+3 -3
View File
@@ -66,7 +66,7 @@ export MPICH_SMP_SINGLE_COPY_MODE=CMA
export MPICH_OFI_NIC_POLICY=GPU export MPICH_OFI_NIC_POLICY=GPU
module load libfabric module load libfabric
BIN=$root/examples/Example_pvdagm_v2_3level_DenseCoarseMatrix BIN=$root/examples/Example_pvdagm_3level_DenseCoarseMatrix
OPTS1="--accelerator-threads 8 --shm 4096 --shm-mpi 1" OPTS1="--accelerator-threads 8 --shm 4096 --shm-mpi 1"
# LOCAL VOLUME MATCHED to production: 48.48.48.96 on 3.6.4.4 is local 16.8.12.24; on 8 GCDs # LOCAL VOLUME MATCHED to production: 48.48.48.96 on 3.6.4.4 is local 16.8.12.24; on 8 GCDs
# (1.1.2.4) that is global 16.8.24.96 with the production blockings 2.2.3.3 / 4.4.2.4: # (1.1.2.4) that is global 16.8.24.96 with the production blockings 2.2.3.3 / 4.4.2.4:
@@ -112,7 +112,7 @@ run_cell () {
logopt=""; [ -n "$GRID_LOG_EXTRA" ] && logopt="--log Error,Warning,Message,$GRID_LOG_EXTRA" logopt=""; [ -n "$GRID_LOG_EXTRA" ] && logopt="--log Error,Warning,Message,$GRID_LOG_EXTRA"
env "$@" srun -N1 -n8 --kill-on-bad-exit=1 ./select_gpu $BIN --mpi ${MPI_GEOM} --grid $vol $OPTS1 --device-mem $devmem --comms-overlap $logopt > $fname 2>&1 env "$@" srun -N1 -n8 --kill-on-bad-exit=1 ./select_gpu $BIN --mpi ${MPI_GEOM} --grid $vol $OPTS1 --device-mem $devmem --comms-overlap $logopt > $fname 2>&1
echo " exit $?"; sleep 10 echo " exit $?"; sleep 10
grep -h "V2 3-level solve\|Memory access fault\|GRID_ASSERT\|Converged on iteration" $fname | sed 's/^Grid : Message : [0-9.]* s : //' | tail -4 | cut -c1-140 grep -h "3-level solve\|Memory access fault\|GRID_ASSERT\|Converged on iteration" $fname | sed 's/^Grid : Message : [0-9.]* s : //' | tail -4 | cut -c1-140
grep -n -B3 "Memory access fault" $fname | tail -5 | cut -c1-160 grep -n -B3 "Memory access fault" $fname | tail -5 | cut -c1-160
} }
@@ -124,5 +124,5 @@ run_cell F3_nrhs6_nodense 16000 NRHS=6 DENSE_CC=0
run_cell F4_nrhs6_bigmem 48000 NRHS=6 # no MemoryManager eviction run_cell F4_nrhs6_bigmem 48000 NRHS=6 # no MemoryManager eviction
echo "=========================================================" echo "========================================================="
for f in log.fault.F*; do echo "$f: $(grep -c 'Memory access fault' $f) faults; $(grep -h 'V2 3-level solve' $f | tail -1 | sed 's/.*V2/V2/' | cut -c1-60)"; done for f in log.fault.F*; do echo "$f: $(grep -c 'Memory access fault' $f) faults; $(grep -h '3-level solve' $f | tail -1 | sed 's/.*3-level/3-level/' | cut -c1-60)"; done
echo "=========================================================" echo "========================================================="
+2 -2
View File
@@ -67,7 +67,7 @@ export MPICH_SMP_SINGLE_COPY_MODE=CMA
export MPICH_OFI_NIC_POLICY=GPU export MPICH_OFI_NIC_POLICY=GPU
module load libfabric module load libfabric
BIN=$root/examples/Example_pvdagm_v2_3level_DenseCoarseMatrix BIN=$root/examples/Example_pvdagm_3level_DenseCoarseMatrix
OPTS1="--accelerator-threads 8 --shm 4096 --shm-mpi 1 --device-mem 32000" OPTS1="--accelerator-threads 8 --shm 4096 --shm-mpi 1 --device-mem 32000"
OVERLAP="--comms-overlap" # set empty for a no-overlap job OVERLAP="--comms-overlap" # set empty for a no-overlap job
vol=48.48.48.96 vol=48.48.48.96
@@ -112,7 +112,7 @@ run_cell () {
tail -15 $(dirname $f)/Grid.stdout.$rank | cut -c1-160 tail -15 $(dirname $f)/Grid.stdout.$rank | cut -c1-160
else else
echo " no 'Memory access fault' in any Grid.stderr.*" echo " no 'Memory access fault' in any Grid.stderr.*"
grep -h "V2 3-level solve\|Fouter MrhsPGCR: Converged" $GRID_STDOUT_ROOT/0/Grid.stdout.0 | tail -3 | cut -c1-120 grep -h "3-level solve\|Fouter MrhsPGCR: Converged" $GRID_STDOUT_ROOT/0/Grid.stdout.0 | tail -3 | cut -c1-120
fi fi
} }
+206
View File
@@ -0,0 +1,206 @@
#!/bin/bash -l
#SBATCH --job-name=pvdagm-mixed-precision
#SBATCH --nodes=36
#SBATCH --ntasks-per-node=8
#SBATCH --cpus-per-task=7
#SBATCH --gpus-per-node=8
#SBATCH --time=2:00:00
#SBATCH --account=phy157_dwf
#SBATCH --gpu-bind=none
#SBATCH --exclusive
#SBATCH --mem=0
#SBATCH -S 0
##############################################################################
# The three precision axes of the PVdagM multigrid, on the banked point of
# pvdagm_multigrid.job (48^3x96, Ls=24, nbasis=60, 288 GCDs). The outer
# Krylov is fp64 and exact in every cell, so fp32 below it costs convergence
# rate only, never correctness -- the FINAL exact-halo true residual is the
# proof and is printed by every cell.
#
# 1. fine level inside the preconditioner run time <FinePrecision>
# 2. coarse + coarse-coarse sector compile -DCOARSE_SINGLE
# 3. distributed dense coarse-coarse invert compile -DGRID_DENSE_INVERSE_SINGLE
#
# The compile-time axes are separate binaries from the same source, all
# plain `make` targets (examples/Makefile.am):
# Example_pvdagm_multigrid fp64 coarse, fp64 dense
# Example_pvdagm_multigrid_fp32coarse fp32 coarse, fp64 dense
# Example_pvdagm_multigrid_fp32coarse_fp32dense fp32 coarse, fp32 dense
# The default basis is 60. The subspace load reads the first 60 vectors
# of the file.
##############################################################################
LUSTRE=/lustre/orion/phy157/proj-shared/phy157_dwf/paboyle
RUNDIR=$LUSTRE/runs/${SLURM_JOB_NAME}_${SLURM_JOB_ID}
mkdir -p $RUNDIR && cd $RUNDIR && echo "RUNDIR $RUNDIR"
cat << EOF > select_gpu
#!/bin/bash
export GPU_MAP=(0 1 2 3 7 6 5 4)
export NUMA_MAP=(3 3 1 1 2 2 0 0)
export GPU=\${GPU_MAP[\$SLURM_LOCALID]}
export NUMA=\${NUMA_MAP[\$SLURM_LOCALID]}
export HIP_VISIBLE_DEVICES=\$GPU
unset ROCR_VISIBLE_DEVICES
if [ \$SLURM_PROCID = "0" ]; then echo \$*; fi
exec numactl -m \$NUMA -N \$NUMA \$*
EOF
chmod +x ./select_gpu
root=/lustre/orion/phy157/proj-shared/phy157_dwf/paboyle/MGrewrite/Grid/systems/Frontier
source $root/sourceme-rocm7.2.sh
# sourceme sets FI_HMEM_ROCR_USE_DMABUF=0 -- the standing avoidance of the CXI
# NO_TRANSLATION fault at NRHS>=12 (libfabric #12775).
export OMP_NUM_THREADS=7
ulimit -c 0 # no 22 GB GPU core dumps
export FI_MR_CACHE_MONITOR=kdreg2 # site default; device-buffer MPI on Slingshot
export MPICH_GPU_SUPPORT_ENABLED=1
export MPICH_SMP_SINGLE_COPY_MODE=CMA
export MPICH_OFI_NIC_POLICY=GPU
export GRID_ALLOC_NCACHE_LARGE=64 # allocator large-ring depth, not a solver knob
module load libfabric
C64D64=$root/examples/Example_pvdagm_multigrid
C32D64=$root/examples/Example_pvdagm_multigrid_fp32coarse
C32D32=$root/examples/Example_pvdagm_multigrid_fp32coarse_fp32dense
# 40000, not 32000: at Nrhs 12 the working set is about 37 GB (outer restart
# history 24.5, fine smoother history 8.2, sources and solutions 4.1), so a
# 32 GB cache evicts. Measured: 808 ms of device-to-host copies in a 20 s
# window, and 9.706 s per rhs against 8.748 with the larger cache. The
# non-evictable total is 6.3 GiB in the fp32 sector, so 40 GB of cache plus
# comms and shm still fits a 64 GB device.
OPTS="--accelerator-threads 8 --shm 4096 --shm-mpi 1 --device-mem 40000 --comms-overlap"
vol=48.48.48.96
MPI_GEOM=3.6.4.4
SUBSPACE=$LUSTRE/subspace_nb64.scidac
CONFIG=/ccs/home/poare/ckpoint_lat.1000
##############################################################################
# write_params <file> <FinePrecision> <FineSloppyComms>
#
# Everything else is the banked adaptive operating point of
# pvdagm_multigrid.job, unchanged, so the cells differ only in precision.
##############################################################################
write_params () {
local f=$1 fprec=$2 sloppy=$3
cat << EOF > $f
<?xml version="1.0"?>
<grid>
<PVdagMDriver>
<Ls>24</Ls>
<Mass>0.00078</Mass>
<M5>1.8</M5>
<MobiusB>1.5</MobiusB>
<MobiusC>0.5</MobiusC>
<Config>$CONFIG</Config>
<Nrhs>12</Nrhs>
<SolveSingleRHS>1</SolveSingleRHS>
<MultiGrid>
<Setup>
<Block1><elem>2</elem><elem>2</elem><elem>3</elem><elem>3</elem></Block1>
<Block2><elem>4</elem><elem>4</elem><elem>2</elem><elem>4</elem></Block2>
<CoarsenBatch>9</CoarsenBatch>
<SubspaceFile>$SUBSPACE</SubspaceFile>
<FineSloppyComms>$sloppy</FineSloppyComms>
<FinePrecision>$fprec</FinePrecision>
<RetainSubspace>0</RetainSubspace>
</Setup>
<FineSmoother>
<Shift>0.1</Shift><Nstep>6</Nstep><Mmax>4</Mmax>
</FineSmoother>
<CoarseSmoother>
<Shift>2.0</Shift><Nstep>2</Nstep><Mmax>2</Mmax>
</CoarseSmoother>
<CoarseSolver>
<Tol>0.05</Tol><Order>200</Order><Mmax>8</Mmax>
</CoarseSolver>
<Outer>
<Tol>1e-8</Tol><MaxIterations>1000</MaxIterations><Mmax>6</Mmax><Nstep>12</Nstep>
</Outer>
<Dense>
<LeafSpan>9</LeafSpan>
</Dense>
</MultiGrid>
</PVdagMDriver>
</grid>
EOF
}
##############################################################################
# run_cell <name> <binary> <FinePrecision> <FineSloppyComms>
##############################################################################
run_cell () {
name=$1; bin=$2; fprec=$3; sloppy=$4
if [ ! -x "$bin" ]; then
echo "----- $name : SKIPPED, no binary $bin"
echo " build it with a plain make in examples/"
return
fi
write_params params.$name.xml $fprec $sloppy
echo "----- $name : fine=$fprec sloppy=$sloppy bin=$(basename $bin) -----"
export GRID_STDOUT_ROOT=$RUNDIR/mg_$name
srun -N36 -n288 --kill-on-bad-exit=1 ./select_gpu $bin --mpi ${MPI_GEOM} --grid $vol $OPTS \
--pvdagm-params params.$name.xml --debug-stdout --log Error,Warning,Message,Performance \
> log.mg.$name 2>&1
echo " exit $?"; sleep 30
# Device OOM is reported on stderr as "hipMalloc failed for <bytes> out of
# memory" and nowhere else, so it must be in this pattern or a cell that
# died of it looks like a cell that printed nothing.
f=$(grep -l "Memory access fault\|NO_TRANSLATION\|hipMalloc failed\|out of memory\|illegal memory access" $GRID_STDOUT_ROOT/*/Grid.stderr.* 2>/dev/null | head -1)
if [ -n "$f" ]; then
echo " FAULT in $f (rank $(basename $f | sed 's/Grid.stderr.//'))"; tail -20 $f | cut -c1-160
else
echo " certificates, convergence, timing:"
grep -h "Coarse sector precision\|GALERKIN CERTIFICATE\|IMPORT CERTIFICATE\|inversion-source import\|SCHUR .* distributed invert\|VERIFY\|PVdagMMultiGridSolver: Nrhs\|MrhsPGCR: Converged\|FINAL Nrhs .*worst" \
$GRID_STDOUT_ROOT/0/Grid.stdout.0 2>/dev/null | cut -c1-150
fi
}
##############################################################################
# Cells. P0 is the regression gate: with fp64 everywhere it must reproduce
# the banked outer count and residual of pvdagm_multigrid.job. If it does
# not, nothing downstream means anything, and the Galerkin certificate
# (2.3e-15 expected in fp64) separates a coarsening change from a solve one.
##############################################################################
# name binary fineprec sloppy
run_cell P0_fp64 $C64D64 fp64 1
run_cell P1_fp32fine $C64D64 fp32 1
run_cell P2_fp32coarse $C32D64 fp64 1
run_cell P3_fp32both $C32D64 fp32 1
run_cell P4_denseF32 $C32D32 fp32 1
# Halo wire format is orthogonal to arithmetic: on an fp32 operator
# FineSloppyComms is bf16 compression, on an fp64 operator it is fp32. One
# exact-halo cell to price it inside the preconditioner.
run_cell P5_exacthalo $C32D64 fp32 0
##############################################################################
# What to read.
#
# P0: the gate. Outer count and s/RHS against the banked numbers.
#
# P1 vs P0: fp32 fine level. Laptop 8^4 saw the same outer count and ~12%
# less wall; here the fine level is the dominant cost and the halo is real,
# so this is where the fp32 case is actually made or lost.
#
# P2 vs P0: fp32 coarse sector. Watch the Galerkin certificate move from
# ~2e-15 to ~3e-7 -- that is fp32 rounding in the coarsening, not an error --
# and check the outer count does not move with it. Halves the coarse
# operator's storage and its comms.
#
# P4 vs P3: fp32 dense coarse-coarse inversion. The reading is the VERIFY
# certificate ||A Ainv x - x||/||x||, which scales as kappa(A_cc) * eps32 and
# so measures the conditioning of the coarse-coarse operator at production
# size. The laptop's N=128 was too small to say anything (5.3e-6 -> 6.7e-6);
# at N=69120 it is a real test. If VERIFY degrades but the outer count holds,
# the fp32 factorisation is fine and halves that footprint; if the outer count
# moves, the fallback is fp32 factorise plus one fp64 refinement step, which
# is two extra dense applies and is not yet written.
#
# P5 vs P3: what the reduced-precision halo is worth once the arithmetic is
# already fp32 (bf16 wire on an fp32 operator).
##############################################################################
+1 -1
View File
@@ -70,7 +70,7 @@ export GRID_ALLOC_NCACHE_LARGE=0 # allocator large-ring depth (Grid alloca
module load libfabric module load libfabric
BIN=$root/examples/Example_pvdagm_multigrid BIN=$root/examples/Example_pvdagm_multigrid
OPTS="--accelerator-threads 8 --shm 4096 --shm-mpi 0 --device-mem 32000 --comms-overlap" OPTS="--accelerator-threads 8 --shm 4096 --shm-mpi 0 --device-mem 40000 --comms-overlap"
vol=48.48.48.96 vol=48.48.48.96
MPI_GEOM=3.6.4.4 MPI_GEOM=3.6.4.4
+2 -2
View File
@@ -131,14 +131,14 @@ export NRHS=4
echo "----- F4a: 1D baseline (DENSE_SCHUR2D unset) -----" echo "----- F4a: 1D baseline (DENSE_SCHUR2D unset) -----"
unset DENSE_SCHUR2D unset DENSE_SCHUR2D
srun -N36 -n288 ./select_gpu $root/examples/Example_pvdagm_v2_3level_DenseCoarseMatrix \ srun -N36 -n288 ./select_gpu $root/examples/Example_pvdagm_3level_DenseCoarseMatrix \
--mpi ${MPI_GEOM} --grid $vol $OPTS1 --comms-overlap --mpi ${MPI_GEOM} --grid $vol $OPTS1 --comms-overlap
if [ $F3RC -eq 0 ] if [ $F3RC -eq 0 ]
then then
echo "----- F4b: 2D block-cyclic (DENSE_SCHUR2D=1) -----" echo "----- F4b: 2D block-cyclic (DENSE_SCHUR2D=1) -----"
export DENSE_SCHUR2D=1 export DENSE_SCHUR2D=1
srun -N36 -n288 ./select_gpu $root/examples/Example_pvdagm_v2_3level_DenseCoarseMatrix \ srun -N36 -n288 ./select_gpu $root/examples/Example_pvdagm_3level_DenseCoarseMatrix \
--mpi ${MPI_GEOM} --grid $vol $OPTS1 --comms-overlap --mpi ${MPI_GEOM} --grid $vol $OPTS1 --comms-overlap
else else
echo "----- F4b SKIPPED: F3 failed (rc=$F3RC), not spending the setup on it -----" echo "----- F4b SKIPPED: F3 failed (rc=$F3RC), not spending the setup on it -----"
+2 -2
View File
@@ -121,10 +121,10 @@ run_cell () {
export FineSmootherMode=$5; export CoarseSmootherMode=$6 export FineSmootherMode=$5; export CoarseSmootherMode=$6
echo "----- $name : Fso=$FineSmootherOrder Fss=$FineSmootherShift Csn=$CoarseSmootherNstep fine=$FineSmootherMode coarse=$CoarseSmootherMode -----" echo "----- $name : Fso=$FineSmootherOrder Fss=$FineSmootherShift Csn=$CoarseSmootherNstep fine=$FineSmootherMode coarse=$CoarseSmootherMode -----"
fname=log.ladder.$name fname=log.ladder.$name
srun -N36 -n288 --kill-on-bad-exit=1 ./select_gpu $root/examples/Example_pvdagm_v2_3level_DenseCoarseMatrix \ srun -N36 -n288 --kill-on-bad-exit=1 ./select_gpu $root/examples/Example_pvdagm_3level_DenseCoarseMatrix \
--mpi ${MPI_GEOM} --grid $vol $OPTS1 --comms-overlap > $fname 2>&1 --mpi ${MPI_GEOM} --grid $vol $OPTS1 --comms-overlap > $fname 2>&1
echo " exit $?"; sleep 60 echo " exit $?"; sleep 60
echo " $(grep -h 'V2 3-level solve Nrhs' $fname | sed 's/.*V2/V2/' | tr '\n' ' ')" echo " $(grep -h '3-level solve Nrhs' $fname | sed 's/.*3-level/3-level/' | tr '\n' ' ')"
echo " $(grep -h 'Fouter MrhsPGCR: Converged' $fname | sed 's/.*Converged/Converged/' | cut -c1-60 | tr '\n' ' ')" echo " $(grep -h 'Fouter MrhsPGCR: Converged' $fname | sed 's/.*Converged/Converged/' | cut -c1-60 | tr '\n' ' ')"
echo " replay per-call |r|/|r0| (Nrhs=1 solve): $(awk '/THREE-level solve, Nrhs = 1/{s=1} s && /Fsmoother replay \|r\|/{v=$NF; n++; t+=v; if(v>mx)mx=v} END{if(n) printf "mean %.4f max %.4f over %d calls", t/n, mx, n}' $fname)" echo " replay per-call |r|/|r0| (Nrhs=1 solve): $(awk '/THREE-level solve, Nrhs = 1/{s=1} s && /Fsmoother replay \|r\|/{v=$NF; n++; t+=v; if(v>mx)mx=v} END{if(n) printf "mean %.4f max %.4f over %d calls", t/n, mx, n}' $fname)"
grep -h "SCHUR fp64 distributed invert took\|GB/s/rank\|BIG LEAVES\|ring histogram\|^Grid : Message : [0-9.]* s : >=" $fname | sed 's/^Grid : Message : [0-9.]* s : //' | cut -c1-150 | head -12 grep -h "SCHUR fp64 distributed invert took\|GB/s/rank\|BIG LEAVES\|ring histogram\|^Grid : Message : [0-9.]* s : >=" $fname | sed 's/^Grid : Message : [0-9.]* s : //' | cut -c1-150 | head -12
+119
View File
@@ -0,0 +1,119 @@
#!/bin/bash -l
#SBATCH --job-name=split-batched-mobius
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=8
#SBATCH --cpus-per-task=7
#SBATCH --gpus-per-node=8
#SBATCH --time=1:30:00
#SBATCH --account=phy157_dwf
#SBATCH --gpu-bind=none
#SBATCH --exclusive
#SBATCH --mem=0
#SBATCH -S 0
##SBATCH -q debug
##############################################################################
# MixedPrecisionConjugateGradientBatched with split inner solves, at production
# size: Mobius 64^3x128, Ls=12, mass 0.026, b=1.5 c=0.5 M5=1.8, antiperiodic
# in time, Hadrons default SchurDiagMooeeOperator.
#
# 4 nodes = 32 GCDs, --mpi 2.2.2.4 (local 32^4). --batched-solver-split node
# makes each node one partition (P=4), so Nbatch=4 is one round with no
# zero padding. The driver solves the same batch twice from zero guesses:
# UNSPLIT inner CG on all 32 GCDs, one rhs after another (existing path)
# SPLIT four independent inner CGs, one per node
# and prints per-rhs iterations, true residuals and wall clock for each.
#
# Build (in the Frontier build tree, after ./scripts/filelist in the source
# root so the new test is in tests/solver/Make.inc):
# make -C tests/solver Test_split_mobius_batched
#
# Memory, per GCD (evictable Lattice fields, so bounded by --device-mem):
# double RB 5d field 1.21 GB, float 0.60 GB
# driver src+sol 9.7, solver batch copies 9.7, split partition fields and
# inner CG on the 4x larger split local volume ~20, gauge copies ~5
# => ~45 GB, hence --device-mem 40000 as in pvdagm_mixed_precision.job.
# Host memory: the device is an inclusive cache, so host RSS per rank is the whole
# ~45 GB working set (plus transient Grid_split staging): ~400 GB of a node's 512 GB.
# The driver prints MEMORY lines (host RSS, peak, allocator caches) at each phase.
# Grid_split stages 2 x 4.8 GB per rank on the host per transfer.
##############################################################################
LUSTRE=/lustre/orion/phy157/proj-shared/phy157_dwf/paboyle
RUNDIR=$LUSTRE/runs/${SLURM_JOB_NAME}_${SLURM_JOB_ID}
mkdir -p $RUNDIR && cd $RUNDIR && echo "RUNDIR $RUNDIR"
cat << EOF > select_gpu
#!/bin/bash
export GPU_MAP=(0 1 2 3 7 6 5 4)
export NUMA_MAP=(3 3 1 1 2 2 0 0)
export GPU=\${GPU_MAP[\$SLURM_LOCALID]}
export NUMA=\${NUMA_MAP[\$SLURM_LOCALID]}
export HIP_VISIBLE_DEVICES=\$GPU
unset ROCR_VISIBLE_DEVICES
if [ \$SLURM_PROCID = "0" ]; then echo \$*; fi
exec numactl -m \$NUMA -N \$NUMA \$*
EOF
chmod +x ./select_gpu
# Source tree and build of THIS branch (feature/splitGridCGBatch) on Frontier
root=$LUSTRE/SplitCG/Grid/systems/Frontier
source $root/sourceme-rocm7.2.sh
export OMP_NUM_THREADS=7
ulimit -c 0 # no 22 GB GPU core dumps
export FI_MR_CACHE_MONITOR=kdreg2 # site default; device-buffer MPI on Slingshot
export MPICH_GPU_SUPPORT_ENABLED=1
export MPICH_SMP_SINGLE_COPY_MODE=CMA
export MPICH_OFI_NIC_POLICY=GPU
# No GRID_ALLOC_NCACHE_LARGE here. With --enable-unified=no every Lattice lives in host
# memory (the device is an inclusive cache), so a deep large-allocation cache keeps freed
# host blocks on top of the full working set: measured on the laptop at 64 entries it
# held about as much again as the working set, enough to OOM a 4-node run of this job.
module load libfabric
BIN=$root/tests/solver/Test_split_mobius_batched
OPTS="--accelerator-threads 8 --shm 4096 --shm-mpi 1 --device-mem 40000 --comms-overlap"
vol=64.64.64.128
MPI_GEOM=2.2.2.4
# A thermalised 64^3x128 NERSC configuration. Leave empty for a hot start:
# fine for plumbing, but mass 0.026 on a hot field is not a physical
# iteration count and may run long.
CONFIG=
PHYS="--Ls 12 --mass 0.026 --M5 1.8 --b 1.5 --c 0.5 --nbatch 4 --tol 1e-8"
if [ -n "$CONFIG" ]; then PHYS="$PHYS --config $CONFIG"; fi
export GRID_STDOUT_ROOT=$RUNDIR/split
srun -N4 -n32 --kill-on-bad-exit=1 ./select_gpu $BIN --mpi $MPI_GEOM --grid $vol $OPTS \
$PHYS --batched-solver-split node \
--debug-stdout --log Error,Warning,Message,Performance \
> log.split 2>&1
echo "exit $?"
##############################################################################
# Readout. Device OOM appears only on stderr as "hipMalloc failed".
##############################################################################
f=$(grep -l "Memory access fault\|NO_TRANSLATION\|hipMalloc failed\|out of memory\|illegal memory access\|GRID_ASSERT" $GRID_STDOUT_ROOT/*/Grid.stderr.* 2>/dev/null | head -1)
if [ -n "$f" ]; then
echo "FAULT in $f"; tail -20 $f | cut -c1-160
fi
grep -h "MEMORY\|BatchedSolverSplit\|Inner CG iterations\|Total time\|true residual\|SUMMARY" \
$GRID_STDOUT_ROOT/0/Grid.stdout.0 2>/dev/null | cut -c1-200
##############################################################################
# What to read.
#
# BatchedSolverSplit line: the partition layout must equal the node layout
# (ShmGrid), e.g. [1 2 2 2] of [2 2 2 4] : 4 partitions. A "straddle node
# boundaries" warning means the rank reordering did not give node-aligned
# blocks; the result is still correct but the inner CG talks off node.
#
# UNSPLIT vs SPLIT: per-rhs inner iterations should agree closely (the same
# solves, different reduction order); the SUMMARY lines give the wall clock.
# The gain is the strong-scaling loss of the 32-GCD inner CG against four
# 8-GCD CGs with node-local halos. "Split setup and transfer" in the Total
# time line prices the clone (grids, gauge split) and the Grid_split /
# Grid_unsplit traffic, which is host-staged.
##############################################################################
+168 -48
View File
@@ -9,7 +9,7 @@ Contents:
3. Profiling 3. Profiling
************************ ************************
* 1. INTERCONNECT + MPI * 0. MPI IO
************************ ************************
-------------------------------------------------------------------- --------------------------------------------------------------------
@@ -27,6 +27,89 @@ https://github.com/paboyle/Grid/issues/381
https://github.com/pmodels/mpich/commit/3a479ab0 https://github.com/pmodels/mpich/commit/3a479ab0
--------------------------------------------------------------------
MPI2-IO performance: introduced All to all based aggregation no changes required.
--------------------------------------------------------------------
https://github.com/pmodels/mpich/issues/7935
MPI_File_write_all / read_all collective buffering does not scale for 4D block-decomposed subarray views (Lustre): 6x slower than a user-level MPI_Alltoallv + POSIX transposition
/* phase 1: redistribute within the row subcommunicator.
sendcounts/senddispls derived analytically from the plan; iodata untouched. */
MPI_Alltoallv(iodata, sendcounts, senddispls, MPI_BYTE,
aggregated, recvcounts, recvdispls, MPI_BYTE, rowcomm);
/* phase 2: a handful of large contiguous writes per rank, no MPI-IO involved */
for (e = 0; e < nextent; e++) {
lseek(fd, offset + extentGsite[e]*sizeof(fobj), SEEK_SET);
write(fd, aggregated + extentLocal[e], extentSites[e]*sizeof(fobj));
}
************************
* 1. INTERCONNECT + MPI
************************
============================================================================
Piz Daint
============================================================================
https://github.com/ofiwg/libfabric/issues/11451
SEGFAULT in first MPI_Comm_dup on more than one node, after MPI_Init
Cause: symptom (ALPS/CSCS, aarch64 Grace/H200, cray-mpich 8.1.32, libfabric 1.22): the
memhooks monitor intercepts munmap by PATCHING THE PLT at MPI_Init
(ofi_memhooks_start -> ofi_write_patch with a garbage data_size); the write overruns
munmap@plt into the NEXT PLT entry (MPI_Comm_dup in Grid's binary)
leaving
`br x15` where an adrp belongs ->
Diagnosed with a hardware watchpoint on the PLT entry during MPI_Init; see the issue for the
gdb transcript. Reproduce on one node with MPICH_SINGLE_HOST_ENABLED=0.
Fix (either):
export FI_MR_CACHE_MONITOR=kdreg2
OR
export FI_MR_CACHE_MONITOR=disabled
export FI_MR_CACHE_MAX_COUNT=0
On ALPS this is a CORRECTNESS requirement for MPI on Slingshot, not a tuning.
============================================================================
Frontier
============================================================================
a) FI_MR_CACHE_MONITOR=kdreg2 (site default) leads to Runtime MPI errors with "NO_TRANSLATION"
userfaultfd and memhooks also fail.
https://github.com/ofiwg/libfabric/issues/12775
Symptom (Frontier, x86, Cray MPICH 8.1.x, ROCm 7.2): device-buffer MPI fails with
MPI_Waitall ... MPIDI_OFI_handle_cq_error: OFI poll failed
(ofi_events.c:MPIDI_OFI_handle_cq_error: Input/output error - NO_TRANSLATION)
on a LIVE, never-freed hipMalloc buffer
b) export FI_MR_CACHE_MONITOR=disabled
export FI_MR_CACHE_MAX_COUNT=0
Leads to INCORRECT RESULTS, see
https://github.com/ofiwg/libfabric/issues/12773
Fix:
export FI_HMEM_ROCR_USE_DMABUF=0
and use site default
FI_MR_CACHE_MONITOR=kdreg2
Every systems/Frontier job and sourceme now sets kdreg2 on OLCF's recommendation;
whether it resolves the Frontier NO_TRANSLATION is the pending test above.
-------------------------------------------------------------------- --------------------------------------------------------------------
Slingshot: Frontier and Perlmutter libfabric slow down Slingshot: Frontier and Perlmutter libfabric slow down
and physical memory fragmentation and physical memory fragmentation
@@ -187,7 +270,6 @@ unset MPIR_CVAR_COLL_SELECTION_TUNING_JSON_FILE
unset MPIR_CVAR_CH4_POSIX_COLL_SELECTION_TUNING_JSON_FILE unset MPIR_CVAR_CH4_POSIX_COLL_SELECTION_TUNING_JSON_FILE
************************ ************************
* 3. Visual profile tools * 3. Visual profile tools
************************ ************************
@@ -196,59 +278,97 @@ unset MPIR_CVAR_CH4_POSIX_COLL_SELECTION_TUNING_JSON_FILE
Frontier/rocprof Frontier/rocprof
-------------------------------------------------------------------- --------------------------------------------------------------------
cat << EOF > select_gpu
#!/bin/bash
export GPU_MAP=(0 1 2 3 7 6 5 4)
export NUMA_MAP=(3 3 1 1 2 2 0 0)
export GPU=\${GPU_MAP[\$SLURM_LOCALID]}
export NUMA=\${NUMA_MAP[\$SLURM_LOCALID]}
export HIP_VISIBLE_DEVICES=\$GPU
unset ROCR_VISIBLE_DEVICES
if [ \$SLURM_PROCID = "0" ]; then echo \$*; fi
if [ "1" = "1" ]
then
if [ \$SLURM_PROCID = "0" ]
then
exec numactl -m \$NUMA -N \$NUMA rocprofv3 -d $TRACE -o mgtrace.\$SLURM_JOBID \
-r -f csv pftrace --stats -D \
-P 140:20:1 \
-- \$*
else
exec numactl -m \$NUMA -N \$NUMA \$*
fi
else
exec numactl -m \$NUMA -N \$NUMA \$*
fi
EOF
chmod +x ./select_gpu
-------------------------------------------------------------------- --------------------------------------------------------------------
Aurora/unitrace Aurora/unitrace
-------------------------------------------------------------------- --------------------------------------------------------------------
select_gpu:
#!/bin/bash
export NUMA_PMAP=(0 0 0 1 1 1 0 0 0 1 1 1 );
export NUMA_HMAP=(2 2 2 3 3 3 3 2 2 2 2 3 3 3 );
export GPU_MAP=(0.0 1.0 2.0 3.0 4.0 5.0 0.1 1.1 2.1 3.1 4.1 5.1 )
export NUMAP=${NUMA_PMAP[$PALS_LOCAL_RANKID]}
export NUMAH=${NUMA_HMAP[$PALS_LOCAL_RANKID]}
export gpu_id=${GPU_MAP[$PALS_LOCAL_RANKID]}
unset EnableWalkerPartition
export EnableImplicitScaling=0
export ZE_AFFINITY_MASK=$gpu_id
export ONEAPI_DEVICE_FILTER=gpu,level_zero
export SYCL_PI_LEVEL_ZERO_DEVICE_SCOPE_EVENTS=0
export SYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS=1
export SYCL_PI_LEVEL_ZERO_USE_COPY_ENGINE=0:4
export SYCL_PI_LEVEL_ZERO_USE_COPY_ENGINE_FOR_D2D_COPY=1
echo "rank $PALS_RANKID ; local rank $PALS_LOCAL_RANKID ; ZE_AFFINITY_MASK=$ZE_AFFINITY_MASK ; NUMA $NUMA "
if [ $PALS_RANKID = "0" ]
then
numactl -p $NUMAP -N $NUMAP unitrace --chrome-kernel-logging --chrome-mpi-logging --chrome-sycl-logging --demangle "$@"
else
numactl -p $NUMAP -N $NUMAP "$@"
fi
-------------------------------------------------------------------- --------------------------------------------------------------------
Tursa/nsight-sys Tursa/nsight-sys
-------------------------------------------------------------------- --------------------------------------------------------------------
============================================================================ cat << EOF > mpiwrapper.sh
2026-08-28 libfabric memory-registration-cache "memhooks" monitor -- DEFECTIVE #!/bin/bash
(libfabric issue #11451, filed by PB, HPE JIRA opened) lrank=\$OMPI_COMM_WORLD_LOCAL_RANK
============================================================================ numa1=\$(( 2 * \$lrank ))
STATUS: the ALPS/aarch64 case below is DEMONSTRATED (issue + both fixes verified numa2=\$(( 2 * \$lrank + 1 ))
there). The Frontier case is a HYPOTHESIS as of 2026-08-28: the NO_TRANSLATION export CUDA_VISIBLE_DEVICES=\$lrank
failure is observed, its attribution to the memhooks MR cache is by mechanism and export UCX_NET_DEVICES=mlx5_\${lrank}:1
by analogy, and the fix is UNTESTED here. Test ladder, one knob per run, NRHS=12: if [ \$OMPI_COMM_WORLD_RANK = "0" ]; then echo \$*; fi
(1) FineSloppyComms=0 (2) FI_MR_CACHE_MONITOR=kdreg2 (3) FI_MR_CACHE_MAX_COUNT=0
(4) --disable-accelerator-aware-mpi build.
REVISIT this entry with the result; if (2) does not cure it, remove kdreg2 from the
job scripts' justification (it stays as OLCF's recommendation regardless).
2026-08-28 22:18, job 5372414: step (2) DID NOT cure it -- kdreg2 set (and the
environment already carried FI_MR_CACHE_MAX_COUNT=786432), sloppy comms OFF, still
NO_TRANSLATION at outer step 60 (Couter, Waitall count=2).
CORRECTION 2026-08-28 23:xx (fi_mr(3) man page): FI_MR_CACHE_MONITOR governs SYSTEM
memory only; device (HMEM_ROCR) registrations are monitored by
FI_MR_ROCR_CACHE_MONITOR_ENABLED=0|1. Grid's comms window is hipMalloc'd device
memory, so the kdreg2 step tested nothing relevant -- the ALPS PLT bug (#11451) and
the Frontier NO_TRANSLATION are the same *library* but different monitors.
Attributed by gdb on a hung rank (5371826): main thread in MPI_Waitall <- Grid
0x5d539c3 (StencilSendToRecvFromComplete/CommsComplete class), i.e. the HALO
EXCHANGE; the two aborts' request counts (16 = fine Dhop, 2 = coarse PaddedCell
direction) agree. Next: FI_MR_CACHE_MAX_COUNT=0 (all memory) and
FI_MR_ROCR_CACHE_MONITOR_ENABLED=0, one per cell.
Symptom (Frontier, x86, Cray MPICH 8.1.x, ROCm 7.2): device-buffer MPI fails if [ "1" = "1" ]
with then
MPI_Waitall ... MPIDI_OFI_handle_cq_error: OFI poll failed if [ \$OMPI_COMM_WORLD_RANK = "0" ]
(ofi_events.c:MPIDI_OFI_handle_cq_error: Input/output error - NO_TRANSLATION) then
on a LIVE, never-freed hipMalloc buffer (the sloppy-comms compressed halo buffer, exec numactl --interleave=\$numa1,\$numa2 nsys profile \
a static deviceVector) once the process does sustained hipMalloc/hipFree churn -o $TRACE/nsys.\$SLURM_JOB_ID --force-overwrite=true \
(MemoryManager eviction at NRHS>=12, EvictAll/DropCache). Translations cached by --trace=cuda,nvtx,mpi,osrt --mpi-impl=op
the provider go stale. --sample=none --cpuctxsw=none \
Symptom (ALPS/CSCS, aarch64 Grace/H200, cray-mpich 8.1.32, libfabric 1.22): the --delay=140 --duration=20 --stats=true \
memhooks monitor intercepts munmap by PATCHING THE PLT at MPI_Init \$*
(ofi_memhooks_start -> ofi_write_patch with a garbage data_size); the write overruns else
munmap@plt into the NEXT PLT entry (MPI_Comm_dup in Grid's binary), leaving exec numactl --interleave=\$numa1,\$numa2 \$*
`br x15` where an adrp belongs -> segfault on first MPI_Comm_dup. Diagnosed with fi
a hardware watchpoint on the PLT entry during MPI_Init; see the issue for the else
gdb transcript. Reproduce on one node with MPICH_SINGLE_HOST_ENABLED=0. exec numactl --interleave=\$numa1,\$numa2 \
Fix (either): export FI_MR_CACHE_MONITOR=kdreg2 (kernel-driven invalidation; OLCF's fi
own recommendation, NOT the default) EOF
export FI_MR_CACHE_MAX_COUNT=0 (no registration cache at all) chmod +x ./mpiwrapper.sh
On ALPS this is a CORRECTNESS requirement for MPI on Slingshot, not a tuning.
Every systems/Frontier job and sourceme now sets kdreg2 on OLCF's recommendation; mpirun -np $SLURM_NTASKS -x LD_LIBRARY_PATH --bind-to none ./mpiwrapper.sh <binary> <args>
whether it resolves the Frontier NO_TRANSLATION is the pending test above.
+7 -7
View File
@@ -8,7 +8,7 @@
then repeatedly applies FFT_all_dim to the same propagator 400 times. then repeatedly applies FFT_all_dim to the same propagator 400 times.
If PlannedFFT is working correctly the RSS should remain flat after the first If PlannedFFT is working correctly the RSS should remain flat after the first
iteration — no new plans, no new deviceVector allocations beyond the per-call iteration -- no new plans, no new deviceVector allocations beyond the per-call
pencil buffer which is freed at the end of each FFT_dim_execute call. pencil buffer which is freed at the end of each FFT_dim_execute call.
Build exactly like any other Grid test, e.g.: Build exactly like any other Grid test, e.g.:
@@ -63,7 +63,7 @@ static long getGPUUsedMb()
} }
// ============================================================ // ============================================================
// Convenience struct — one snapshot of both sides // Convenience struct -- one snapshot of both sides
// ============================================================ // ============================================================
struct MemSnapshot { struct MemSnapshot {
long cpu_rss_kb; // host RSS in kB (-1 if unavailable) long cpu_rss_kb; // host RSS in kB (-1 if unavailable)
@@ -113,7 +113,7 @@ int main(int argc, char **argv)
<< "Grid is setup to use " << threads << " threads" << std::endl; << "Grid is setup to use " << threads << " threads" << std::endl;
// ------------------------------------------------------------------ // ------------------------------------------------------------------
// Grid setup — use whatever lattice/mpi/simd was passed on the CLI, // Grid setup -- use whatever lattice/mpi/simd was passed on the CLI,
// e.g. --grid 8.8.8.8 --mpi 1.1.1.1 // e.g. --grid 8.8.8.8 --mpi 1.1.1.1
// ------------------------------------------------------------------ // ------------------------------------------------------------------
Coordinate latt_size = GridDefaultLatt(); Coordinate latt_size = GridDefaultLatt();
@@ -174,13 +174,13 @@ int main(int argc, char **argv)
<< std::endl; << std::endl;
// ------------------------------------------------------------------ // ------------------------------------------------------------------
// Create the PlannedFFT — plans are allocated here ONCE for all // Create the PlannedFFT -- plans are allocated here ONCE for all
// dimensions and stored inside the object. // dimensions and stored inside the object.
// ------------------------------------------------------------------ // ------------------------------------------------------------------
PlannedFFT<iSpinColourMatrix<vComplexD>> plannedFFT(&GRID); PlannedFFT<iSpinColourMatrix<vComplexD>> plannedFFT(&GRID);
// ------------------------------------------------------------------ // ------------------------------------------------------------------
// Snapshot AFTER plan construction — this is the true baseline // Snapshot AFTER plan construction -- this is the true baseline
// for the loop, because cufftPlanMany itself grabs device memory. // for the loop, because cufftPlanMany itself grabs device memory.
// ------------------------------------------------------------------ // ------------------------------------------------------------------
MemSnapshot snap_after_plan = takeSnapshot(); MemSnapshot snap_after_plan = takeSnapshot();
@@ -197,7 +197,7 @@ int main(int argc, char **argv)
// ------------------------------------------------------------------ // ------------------------------------------------------------------
// 400-iteration loop. // 400-iteration loop.
// Each iteration computes the full 4d forward FFT of `prop`. // Each iteration computes the full 4d forward FFT of `prop`.
// We deliberately do NOT cache the result — we always start from // We deliberately do NOT cache the result -- we always start from
// the same `prop` so the FFT is recomputed identically each time. // the same `prop` so the FFT is recomputed identically each time.
// The point is to watch memory, not correctness. // The point is to watch memory, not correctness.
// ------------------------------------------------------------------ // ------------------------------------------------------------------
@@ -229,7 +229,7 @@ int main(int argc, char **argv)
} }
// cudaMemGetInfo reflects the state *after* any pooled frees have // cudaMemGetInfo reflects the state *after* any pooled frees have
// been committed, so this is accurate without an explicit sync — // been committed, so this is accurate without an explicit sync --
// FFT_dim_execute already calls accelerator_barrier() internally. // FFT_dim_execute already calls accelerator_barrier() internally.
MemSnapshot snap_now = takeSnapshot(); MemSnapshot snap_now = takeSnapshot();
printRow(i, snap_now, snap_prev); printRow(i, snap_now, snap_prev);
+1 -1
View File
@@ -156,7 +156,7 @@ int main (int argc, char ** argv)
} }
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
// Dwf matrix — verify Fourier representation using PlannedFFT<LatticeFermionD> // Dwf matrix -- verify Fourier representation using PlannedFFT<LatticeFermionD>
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
{ {
std::cout<<"****************************************"<<std::endl; std::cout<<"****************************************"<<std::endl;
+1 -1
View File
@@ -20,7 +20,7 @@ Author: Peter Boyle <pboyle@bnl.gov>
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// Regression gate for BlockCyclicLayout -- stage 1 of the 2D distributed // Regression gate for BlockCyclicLayout -- stage 1 of the 2D distributed
// dense inverse (documentation/DistributedDenseInverse2D.tex). // dense inverse.
// //
// The layout is pure index arithmetic, so this test is EXHAUSTIVE rather // The layout is pure index arithmetic, so this test is EXHAUSTIVE rather
// than statistical: every stage sweeps a battery of (N, nb, Pr, Pc) // than statistical: every stage sweeps a battery of (N, nb, Pr, Pc)
@@ -2,7 +2,7 @@
Grid physics library, www.github.com/paboyle/Grid Grid physics library, www.github.com/paboyle/Grid
Source file: ./tests/debug/Test_coarse_v2.cc Source file: ./tests/debug/Test_coarse.cc
Copyright (C) 2026 Copyright (C) 2026
@@ -27,17 +27,18 @@ Author: Peter Boyle <pboyle@bnl.gov>
/* END LEGAL */ /* END LEGAL */
// //
// MultiGeneralCoarsenedOperatorV2 against the existing mrhs coarse operator. // MultiGeneralCoarsenedOperator against the existing mrhs coarse operator.
// //
// V1 is constructed on the D+1 grid as now; V2 on the D dimensional grid, // The reference (the deprecated operator) is constructed on the D+1 grid; the
// with SetGrid() adopting the caller owned D+1 grid and building its padded // operator under test on the D dimensional grid, with SetGrid() adopting the
// cell and neighbour table from the D dimensional stencil, with the Nrhs // caller owned D+1 grid and building its padded cell and neighbour table from
// factor multiplied in. // the D dimensional stencil, with the Nrhs factor multiplied in.
// //
// Both are given identical matrix elements, so any difference in the apply is // Both are given identical matrix elements, so any difference in the apply is
// the restructured neighbour table. The same geometry object is passed to // the restructured neighbour table. The same geometry object is passed to
// both: V1 adds one to skip for the rhs direction, V2 uses it as is on the D // both: the reference adds one to skip for the rhs direction, the operator
// dimensional grid, so both describe the same stencil over the D dimensions. // under test uses it as is on the D dimensional grid, so both describe the
// same stencil over the D dimensions.
// //
#include <Grid/Grid.h> #include <Grid/Grid.h>
@@ -48,8 +49,8 @@ const int nbasis = 8;
typedef vSpinColourVector FineObj; typedef vSpinColourVector FineObj;
typedef sTComplexD CComplexT; // unvectorised coarse space typedef sTComplexD CComplexT; // unvectorised coarse space
typedef MultiGeneralCoarsenedMatrix <FineObj,CComplexT,nbasis> MrhsV1; typedef DeprecatedMultiGeneralCoarsenedMatrix <FineObj,CComplexT,nbasis> RefOperator;
typedef MultiGeneralCoarsenedOperatorV2<FineObj,CComplexT,nbasis> MrhsV2; typedef MultiGeneralCoarsenedOperator<FineObj,CComplexT,nbasis> TestOperator;
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// Identical random matrix elements into both operators // Identical random matrix elements into both operators
@@ -59,11 +60,16 @@ void SeedMatrixElements(OpA &A,OpB &B,int npoint,GridSerialRNG &sRNG)
{ {
typedef typename OpA::calcMatrix calcMatrix; typedef typename OpA::calcMatrix calcMatrix;
deviceVector<calcMatrix> bufA,bufB;
for(int p=0;p<npoint;p++){ for(int p=0;p<npoint;p++){
GRID_ASSERT(A.BLAS_A[p].size() == B.BLAS_A[p].size()); // MatrixPointOut/In carry one stencil point in lSite order whichever
// internal layout the operator uses.
A.MatrixPointOut(p,bufA);
B.MatrixPointOut(p,bufB);
GRID_ASSERT(bufA.size() == bufB.size());
int64_t sites = A.BLAS_A[p].size(); int64_t sites = bufA.size();
std::vector<calcMatrix> host(sites); std::vector<calcMatrix> host(sites);
ComplexD *w = (ComplexD *)&host[0]; ComplexD *w = (ComplexD *)&host[0];
@@ -75,8 +81,9 @@ void SeedMatrixElements(OpA &A,OpB &B,int npoint,GridSerialRNG &sRNG)
w[i] = ComplexD(re-0.5,im-0.5); w[i] = ComplexD(re-0.5,im-0.5);
} }
acceleratorCopyToDevice(&host[0],&A.BLAS_A[p][0],sites*sizeof(calcMatrix)); acceleratorCopyToDevice(&host[0],&bufA[0],sites*sizeof(calcMatrix));
acceleratorCopyToDevice(&host[0],&B.BLAS_A[p][0],sites*sizeof(calcMatrix)); A.MatrixPointIn(p,bufA);
B.MatrixPointIn(p,bufA);
} }
} }
@@ -85,10 +92,12 @@ RealD MatrixChecksum(Op &O,int npoint)
{ {
typedef typename Op::calcMatrix calcMatrix; typedef typename Op::calcMatrix calcMatrix;
RealD sum=0.0; RealD sum=0.0;
deviceVector<calcMatrix> buf;
for(int p=0;p<npoint;p++){ for(int p=0;p<npoint;p++){
int64_t sites = O.BLAS_A[p].size(); O.MatrixPointOut(p,buf);
int64_t sites = buf.size();
std::vector<calcMatrix> host(sites); std::vector<calcMatrix> host(sites);
acceleratorCopyFromDevice(&O.BLAS_A[p][0],&host[0],sites*sizeof(calcMatrix)); acceleratorCopyFromDevice(&buf[0],&host[0],sites*sizeof(calcMatrix));
ComplexD *w = (ComplexD *)&host[0]; ComplexD *w = (ComplexD *)&host[0];
int64_t words = sites*sizeof(calcMatrix)/sizeof(ComplexD); int64_t words = sites*sizeof(calcMatrix)/sizeof(ComplexD);
for(int64_t i=0;i<words;i++) sum += real(w[i])*real(w[i]) + imag(w[i])*imag(w[i]); for(int64_t i=0;i<words;i++) sum += real(w[i])*real(w[i]) + imag(w[i])*imag(w[i]);
@@ -107,7 +116,7 @@ int main (int argc, char ** argv)
Coordinate cmpi = GridDefaultMpi(); Coordinate cmpi = GridDefaultMpi();
//////////////////////////////////////////////// ////////////////////////////////////////////////
// D dimensional coarse grid, and D+1 for V1 // D dimensional coarse grid, and D+1 for the reference
//////////////////////////////////////////////// ////////////////////////////////////////////////
GridCartesian *CoarseD = new GridCartesian(clatt,csimd,cmpi); GridCartesian *CoarseD = new GridCartesian(clatt,csimd,cmpi);
@@ -129,35 +138,35 @@ int main (int argc, char ** argv)
} }
GridCartesian *CoarseMulti = new GridCartesian(mlatt,msimd,mmpi); GridCartesian *CoarseMulti = new GridCartesian(mlatt,msimd,mmpi);
MrhsV2 OpV2(geom,CoarseD); TestOperator OpTest(geom,CoarseD);
MrhsV1 OpV1(geom,CoarseMulti); RefOperator OpRef(geom,CoarseMulti);
OpV2.SetGrid(CoarseMulti); OpTest.SetGrid(CoarseMulti);
std::cout << GridLogMessage << "coarse D+1 grid nrhs " << nrhs std::cout << GridLogMessage << "coarse D+1 grid nrhs " << nrhs
<< " Nsimd " << CoarseMulti->Nsimd() << std::endl; << " Nsimd " << CoarseMulti->Nsimd() << std::endl;
std::cout << GridLogMessage << "npoint V1 " << OpV1.geom.npoint std::cout << GridLogMessage << "npoint ref " << OpRef.geom.npoint
<< " npoint V2 " << OpV2.geom.npoint << std::endl; << " npoint test " << OpTest.geom.npoint << std::endl;
GRID_ASSERT(OpV1.geom.npoint == OpV2.geom.npoint); GRID_ASSERT(OpRef.geom.npoint == OpTest.geom.npoint);
int npoint = OpV1.geom.npoint; int npoint = OpRef.geom.npoint;
//////////////////////////////////////////////// ////////////////////////////////////////////////
// Identical matrix elements // Identical matrix elements
//////////////////////////////////////////////// ////////////////////////////////////////////////
GridSerialRNG sRNG; sRNG.SeedFixedIntegers(std::vector<int>({7,8,9,10})); GridSerialRNG sRNG; sRNG.SeedFixedIntegers(std::vector<int>({7,8,9,10}));
SeedMatrixElements(OpV1,OpV2,npoint,sRNG); SeedMatrixElements(OpRef,OpTest,npoint,sRNG);
RealD ckV1 = MatrixChecksum(OpV1,npoint); RealD ckRef = MatrixChecksum(OpRef,npoint);
RealD ckV2 = MatrixChecksum(OpV2,npoint); RealD ckTest = MatrixChecksum(OpTest,npoint);
std::cout << GridLogMessage << "matrix element checksum V1 " << ckV1 std::cout << GridLogMessage << "matrix element checksum ref " << ckRef
<< " V2 " << ckV2 << std::endl; << " test " << ckTest << std::endl;
GRID_ASSERT( ckV1 == ckV2 ); GRID_ASSERT( ckRef == ckTest );
//////////////////////////////////////////////// ////////////////////////////////////////////////
// Same input, compare the applies // Same input, compare the applies
//////////////////////////////////////////////// ////////////////////////////////////////////////
typedef MrhsV1::CoarseVector CoarseVector; typedef RefOperator::CoarseVector CoarseVector;
// RNG on the D dimensional grid fills any D+1 field: the rhs direction is // RNG on the D dimensional grid fills any D+1 field: the rhs direction is
// undistributed and divides cleanly. One RNG serves every Nrhs. // undistributed and divides cleanly. One RNG serves every Nrhs.
GridParallelRNG pRNG(CoarseD); pRNG.SeedFixedIntegers(std::vector<int>({1,2,3,4})); GridParallelRNG pRNG(CoarseD); pRNG.SeedFixedIntegers(std::vector<int>({1,2,3,4}));
@@ -167,22 +176,22 @@ int main (int argc, char ** argv)
CoarseVector out2(CoarseMulti); CoarseVector out2(CoarseMulti);
CoarseVector err (CoarseMulti); CoarseVector err (CoarseMulti);
OpV1.M(in,out1); OpRef.M(in,out1);
OpV2.M(in,out2); OpTest.M(in,out2);
err = out1 - out2; err = out1 - out2;
std::cout << GridLogMessage << "|V1 out|^2 = " << norm2(out1) std::cout << GridLogMessage << "|ref out|^2 = " << norm2(out1)
<< " |V2 out|^2 = " << norm2(out2) << std::endl; << " |test out|^2 = " << norm2(out2) << std::endl;
std::cout << GridLogMessage << "|V1 - V2|^2 = " << norm2(err) << std::endl; std::cout << GridLogMessage << "|ref - test|^2 = " << norm2(err) << std::endl;
GRID_ASSERT( norm2(out1) > 0.0 ); GRID_ASSERT( norm2(out1) > 0.0 );
GRID_ASSERT( norm2(err) == 0.0 ); GRID_ASSERT( norm2(err) == 0.0 );
//////////////////////////////////////////////// ////////////////////////////////////////////////
// SetGrid is idempotent on pointer identity // SetGrid is idempotent on pointer identity
//////////////////////////////////////////////// ////////////////////////////////////////////////
OpV2.SetGrid(CoarseMulti); OpTest.SetGrid(CoarseMulti);
GRID_ASSERT( MatrixChecksum(OpV2,npoint) == ckV2 ); GRID_ASSERT( MatrixChecksum(OpTest,npoint) == ckTest );
OpV2.M(in,out2); OpTest.M(in,out2);
err = out1 - out2; err = out1 - out2;
GRID_ASSERT( norm2(err) == 0.0 ); GRID_ASSERT( norm2(err) == 0.0 );
std::cout << GridLogMessage << "SetGrid idempotent on identity" << std::endl; std::cout << GridLogMessage << "SetGrid idempotent on identity" << std::endl;
@@ -194,7 +203,7 @@ int main (int argc, char ** argv)
//////////////////////////////////////////////// ////////////////////////////////////////////////
// Nrhs 1 is the single RHS case through the multiRHS path, and each slice // Nrhs 1 is the single RHS case through the multiRHS path, and each slice
// of the Nrhs 4 apply must come back unchanged. // of the Nrhs 4 apply must come back unchanged.
OpV2.M(in,out2); OpTest.M(in,out2);
for(int nr=2;nr>=1;nr--){ for(int nr=2;nr>=1;nr--){
Coordinate latt2(1,nr), simd2(1,1), mpi2(1,1); Coordinate latt2(1,nr), simd2(1,1), mpi2(1,1);
for(int d=0;d<Nd;d++){ for(int d=0;d<Nd;d++){
@@ -204,9 +213,9 @@ int main (int argc, char ** argv)
} }
GridCartesian *CoarseMulti2 = new GridCartesian(latt2,simd2,mpi2); GridCartesian *CoarseMulti2 = new GridCartesian(latt2,simd2,mpi2);
OpV2.SetGrid(CoarseMulti2); OpTest.SetGrid(CoarseMulti2);
GRID_ASSERT( OpV2.Nrhs() == nr ); GRID_ASSERT( OpTest.Nrhs() == nr );
GRID_ASSERT( MatrixChecksum(OpV2,npoint) == ckV2 ); GRID_ASSERT( MatrixChecksum(OpTest,npoint) == ckTest );
CoarseVector in2 (CoarseMulti2); CoarseVector in2 (CoarseMulti2);
CoarseVector out(CoarseMulti2); CoarseVector out(CoarseMulti2);
@@ -215,7 +224,7 @@ int main (int argc, char ** argv)
ExtractSliceFast(slice,in,r,0); ExtractSliceFast(slice,in,r,0);
InsertSliceFast(slice,in2,r,0); InsertSliceFast(slice,in2,r,0);
} }
OpV2.M(in2,out); OpTest.M(in2,out);
RealD sdiff=0.0; RealD sdiff=0.0;
for(int r=0;r<nr;r++){ for(int r=0;r<nr;r++){
@@ -231,22 +240,22 @@ int main (int argc, char ** argv)
GRID_ASSERT( norm2(out) > 0.0 ); GRID_ASSERT( norm2(out) > 0.0 );
GRID_ASSERT( sdiff/norm2(out) < 1.0e-20 ); GRID_ASSERT( sdiff/norm2(out) < 1.0e-20 );
OpV2.ReleaseGrid(); OpTest.ReleaseGrid();
GRID_ASSERT( MatrixChecksum(OpV2,npoint) == ckV2 ); // survives release GRID_ASSERT( MatrixChecksum(OpTest,npoint) == ckTest ); // survives release
delete CoarseMulti2; delete CoarseMulti2;
} }
OpV2.SetGrid(CoarseMulti); OpTest.SetGrid(CoarseMulti);
GRID_ASSERT( OpV2.Nrhs() == nrhs ); GRID_ASSERT( OpTest.Nrhs() == nrhs );
OpV2.M(in,out2); OpTest.M(in,out2);
err = out1 - out2; err = out1 - out2;
std::cout << GridLogMessage << "after Nrhs 4 -> 2 -> 1 -> release -> 4, |V1 - V2|^2 = " std::cout << GridLogMessage << "after Nrhs 4 -> 2 -> 1 -> release -> 4, |ref - test|^2 = "
<< norm2(err) << std::endl; << norm2(err) << std::endl;
GRID_ASSERT( norm2(err) == 0.0 ); GRID_ASSERT( norm2(err) == 0.0 );
std::cout << GridLogMessage << "Test_coarse_v2: ALL PASS" << std::endl; std::cout << GridLogMessage << "Test_coarse: ALL PASS" << std::endl;
Grid_finalize(); Grid_finalize();
} }
@@ -2,7 +2,7 @@
Grid physics library, www.github.com/paboyle/Grid Grid physics library, www.github.com/paboyle/Grid
Source file: ./tests/debug/Test_coarse_v2_coarsen.cc Source file: ./tests/debug/Test_coarse_coarsen.cc
Copyright (C) 2026 Copyright (C) 2026
@@ -29,11 +29,13 @@ Author: Peter Boyle <pboyle@bnl.gov>
// //
// Coarsen the same fine operator two ways and compare the matrix elements. // Coarsen the same fine operator two ways and compare the matrix elements.
// //
// V1 : existing mrhs CoarsenOperator, vectorised coarse space matched to // ref : DeprecatedMultiGeneralCoarsenedMatrix::CoarsenOperator, vectorised
// the fine SIMD layout, single RHS fine applications // coarse space matched to the fine SIMD layout, single RHS fine
// V2 : D+1 CoarsenOperator, unvectorised sComplexD coarse space, the batch // applications
// of phased basis vectors carried in the rhs direction and applied // test : MultiGeneralCoarsenedOperator::CoarsenOperator on the D+1 grid,
// through MrhsPromotedOperator // unvectorised sComplexD coarse space, the batch of phased basis
// vectors carried in the rhs direction and applied through
// MrhsPromotedOperator
// //
// BLAS_A is written by GridtoBLAS in lSite order, which does not depend on // BLAS_A is written by GridtoBLAS in lSite order, which does not depend on
// the SIMD layout, so the two are directly comparable. // the SIMD layout, so the two are directly comparable.
@@ -46,21 +48,25 @@ const int nbasis = 8;
const int batch = 9; const int batch = 9;
typedef vSpinColourVector FineObj; typedef vSpinColourVector FineObj;
typedef vTComplex CComplexV; // vectorised coarse space, V1 reference typedef vTComplex CComplexV; // vectorised coarse space, reference
typedef sTComplexD CComplexS; // unvectorised coarse space, V2 typedef sTComplexD CComplexS; // unvectorised coarse space, under test
typedef MultiGeneralCoarsenedMatrix <FineObj,CComplexV,nbasis> MrhsV1; typedef DeprecatedMultiGeneralCoarsenedMatrix <FineObj,CComplexV,nbasis> RefOperator;
typedef MultiGeneralCoarsenedOperatorV2<FineObj,CComplexS,nbasis> MrhsV2; typedef MultiGeneralCoarsenedOperator<FineObj,CComplexS,nbasis> TestOperator;
template<class Op> template<class Op>
void ReadMatrix(Op &O,int npoint,std::vector<std::vector<typename Op::calcMatrix> > &host) void ReadMatrix(Op &O,int npoint,std::vector<std::vector<typename Op::calcMatrix> > &host)
{ {
typedef typename Op::calcMatrix calcMatrix; typedef typename Op::calcMatrix calcMatrix;
host.resize(npoint); host.resize(npoint);
// MatrixPointOut carries one stencil point in lSite order whichever internal
// layout the operator uses.
deviceVector<calcMatrix> buf;
for(int p=0;p<npoint;p++){ for(int p=0;p<npoint;p++){
int64_t sites = O.BLAS_A[p].size(); O.MatrixPointOut(p,buf);
int64_t sites = buf.size();
host[p].resize(sites); host[p].resize(sites);
acceleratorCopyFromDevice(&O.BLAS_A[p][0],&host[p][0],sites*sizeof(calcMatrix)); acceleratorCopyFromDevice(&buf[0],&host[p][0],sites*sizeof(calcMatrix));
} }
} }
@@ -83,24 +89,24 @@ int main (int argc, char ** argv)
GridRedBlackCartesian *FrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(FineGrid); GridRedBlackCartesian *FrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(FineGrid);
//////////////////////////////////////////////// ////////////////////////////////////////////////
// V1 coarse space: SIMD layout matched to the fine // Reference coarse space: SIMD layout matched to the fine
//////////////////////////////////////////////// ////////////////////////////////////////////////
Coordinate cvsimd = GridDefaultSimd(Nd,CComplexV::Nsimd()); Coordinate cvsimd = GridDefaultSimd(Nd,CComplexV::Nsimd());
GridCartesian *CoarseV = new GridCartesian(clatt,cvsimd,fmpi); GridCartesian *CoarseV = new GridCartesian(clatt,cvsimd,fmpi);
// V1 puts all of the SIMD in the rhs direction. CoarsenOperator does not // The reference puts all of the SIMD in the rhs direction. CoarsenOperator
// use the multiRHS grid; it only sizes BLAS_A, so one lane of rhs suffices. // does not use the multiRHS grid; it only sizes BLAS_A, so one lane of rhs suffices.
int nrhs_v1 = CComplexV::Nsimd(); int nrhs_ref = CComplexV::Nsimd();
Coordinate v1latt(1,nrhs_v1),v1simd(1,CComplexV::Nsimd()),v1mpi(1,1); Coordinate reflatt(1,nrhs_ref),refsimd(1,CComplexV::Nsimd()),refmpi(1,1);
for(int d=0;d<Nd;d++){ for(int d=0;d<Nd;d++){
v1latt.push_back(clatt[d]); reflatt.push_back(clatt[d]);
v1simd.push_back(1); refsimd.push_back(1);
v1mpi .push_back(fmpi[d]); refmpi .push_back(fmpi[d]);
} }
GridCartesian *CoarseVMulti = new GridCartesian(v1latt,v1simd,v1mpi); GridCartesian *CoarseVMulti = new GridCartesian(reflatt,refsimd,refmpi);
//////////////////////////////////////////////// ////////////////////////////////////////////////
// V2 coarse space: unvectorised // Coarse space under test: unvectorised
//////////////////////////////////////////////// ////////////////////////////////////////////////
Coordinate cssimd(Nd,1); Coordinate cssimd(Nd,1);
GridCartesian *CoarseS = new GridCartesian(clatt,cssimd,fmpi); GridCartesian *CoarseS = new GridCartesian(clatt,cssimd,fmpi);
@@ -124,8 +130,8 @@ int main (int argc, char ** argv)
std::cout << GridLogMessage << "fine "<<flatt<<" coarse "<<clatt<<" batch "<<batch<<std::endl; std::cout << GridLogMessage << "fine "<<flatt<<" coarse "<<clatt<<" batch "<<batch<<std::endl;
std::cout << GridLogMessage << "Nsimd fine "<<FineGrid->Nsimd() std::cout << GridLogMessage << "Nsimd fine "<<FineGrid->Nsimd()
<< " coarse V1 "<<CoarseV->Nsimd() << " coarse ref "<<CoarseV->Nsimd()
<< " coarse V2 "<<CoarseS->Nsimd()<<std::endl; << " coarse test "<<CoarseS->Nsimd()<<std::endl;
//////////////////////////////////////////////// ////////////////////////////////////////////////
// Fine operator // Fine operator
@@ -151,32 +157,32 @@ int main (int argc, char ** argv)
NextToNearestStencilGeometry4D geomS(CoarseS); NextToNearestStencilGeometry4D geomS(CoarseS);
//////////////////////////////////////////////// ////////////////////////////////////////////////
// V1 coarsening, matched layouts // Reference coarsening, matched layouts
//////////////////////////////////////////////// ////////////////////////////////////////////////
MrhsV1 OpV1(geomV,CoarseVMulti); RefOperator OpRef(geomV,CoarseVMulti);
std::cout << GridLogMessage << "V1 CoarsenOperator" << std::endl; std::cout << GridLogMessage << "reference CoarsenOperator" << std::endl;
OpV1.CoarsenOperator(HermOp,Subspace,CoarseV); OpRef.CoarsenOperator(HermOp,Subspace,CoarseV);
//////////////////////////////////////////////// ////////////////////////////////////////////////
// V2 coarsening, D+1 fine applications, unvectorised coarse // Coarsening under test, D+1 fine applications, unvectorised coarse
//////////////////////////////////////////////// ////////////////////////////////////////////////
MrhsV2 OpV2(geomS,CoarseS); TestOperator OpTest(geomS,CoarseS);
OpV2.SetGrid(CoarseSMulti); OpTest.SetGrid(CoarseSMulti);
MrhsPromotedOperator<LatticeFermionD> MrhsHermOp(HermOp,FineGrid,batch); MrhsPromotedOperator<LatticeFermionD> MrhsHermOp(HermOp,FineGrid,batch);
std::cout << GridLogMessage << "V2 CoarsenOperator (D+1)" << std::endl; std::cout << GridLogMessage << "test CoarsenOperator (D+1)" << std::endl;
OpV2.CoarsenOperator(MrhsHermOp,FineGridMulti,subspace,CoarseS); OpTest.CoarsenOperator(MrhsHermOp,FineGridMulti,subspace,CoarseS);
//////////////////////////////////////////////// ////////////////////////////////////////////////
// Compare matrix elements // Compare matrix elements
//////////////////////////////////////////////// ////////////////////////////////////////////////
int npoint = OpV1.geom.npoint; int npoint = OpRef.geom.npoint;
GRID_ASSERT(npoint == OpV2.geom.npoint); GRID_ASSERT(npoint == OpTest.geom.npoint);
typedef MrhsV1::calcMatrix calcMatrix; typedef RefOperator::calcMatrix calcMatrix;
std::vector<std::vector<calcMatrix> > A1,A2; std::vector<std::vector<calcMatrix> > A1,A2;
ReadMatrix(OpV1,npoint,A1); ReadMatrix(OpRef,npoint,A1);
ReadMatrix(OpV2,npoint,A2); ReadMatrix(OpTest,npoint,A2);
RealD num=0.0, den=0.0; RealD num=0.0, den=0.0;
for(int p=0;p<npoint;p++){ for(int p=0;p<npoint;p++){
@@ -190,8 +196,8 @@ int main (int argc, char ** argv)
den += real(w1[i])*real(w1[i])+imag(w1[i])*imag(w1[i]); den += real(w1[i])*real(w1[i])+imag(w1[i])*imag(w1[i]);
} }
} }
std::cout << GridLogMessage << "|A_V1|^2 = " << den << std::endl; std::cout << GridLogMessage << "|A_ref|^2 = " << den << std::endl;
std::cout << GridLogMessage << "|A_V1 - A_V2|^2 / |A_V1|^2 = " << num/den << std::endl; std::cout << GridLogMessage << "|A_ref - A_test|^2 / |A_ref|^2 = " << num/den << std::endl;
GRID_ASSERT( den > 0.0 ); GRID_ASSERT( den > 0.0 );
GRID_ASSERT( num/den < 1.0e-20 ); GRID_ASSERT( num/den < 1.0e-20 );
@@ -200,14 +206,14 @@ int main (int argc, char ** argv)
// batch assembled on the coarse side by the mixed blockProject. Block // batch assembled on the coarse side by the mixed blockProject. Block
// Gram-Schmidt is idempotent so the subspace may be reused in place. // Gram-Schmidt is idempotent so the subspace may be reused in place.
//////////////////////////////////////////////// ////////////////////////////////////////////////
MrhsV2 OpV2s(geomS,CoarseS); TestOperator OpTestSrhs(geomS,CoarseS);
OpV2s.SetGrid(CoarseSMulti); OpTestSrhs.SetGrid(CoarseSMulti);
std::cout << GridLogMessage << "V2 CoarsenOperator (single RHS fine op)" << std::endl; std::cout << GridLogMessage << "test CoarsenOperator (single RHS fine op)" << std::endl;
OpV2s.CoarsenOperator(HermOp,subspace,CoarseS,batch); OpTestSrhs.CoarsenOperator(HermOp,subspace,CoarseS,batch);
std::vector<std::vector<calcMatrix> > A3; std::vector<std::vector<calcMatrix> > A3;
ReadMatrix(OpV2s,npoint,A3); ReadMatrix(OpTestSrhs,npoint,A3);
RealD nums=0.0; RealD nums=0.0;
for(int p=0;p<npoint;p++){ for(int p=0;p<npoint;p++){
@@ -220,7 +226,7 @@ int main (int argc, char ** argv)
nums += real(d)*real(d)+imag(d)*imag(d); nums += real(d)*real(d)+imag(d)*imag(d);
} }
} }
std::cout << GridLogMessage << "|A_V1 - A_V2srhs|^2 / |A_V1|^2 = " << nums/den << std::endl; std::cout << GridLogMessage << "|A_ref - A_test_srhs|^2 / |A_ref|^2 = " << nums/den << std::endl;
GRID_ASSERT( nums/den < 1.0e-20 ); GRID_ASSERT( nums/den < 1.0e-20 );
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -229,15 +235,15 @@ int main (int argc, char ** argv)
// needs no SetGrid since the matrix elements are Nrhs independent. // needs no SetGrid since the matrix elements are Nrhs independent.
//////////////////////////////////////////////// ////////////////////////////////////////////////
{ {
typedef MrhsV2::CoarseMatrix CoarseMatrixS; typedef TestOperator::CoarseMatrix CoarseMatrixS;
std::vector<CoarseMatrixS> Aget(npoint,CoarseS); std::vector<CoarseMatrixS> Aget(npoint,CoarseS);
for(int p=0;p<npoint;p++) OpV2.GetMatrix(p,Aget); for(int p=0;p<npoint;p++) OpTest.GetMatrix(p,Aget);
MrhsV2 OpV2rt(geomS,CoarseS); TestOperator OpTestRoundTrip(geomS,CoarseS);
for(int p=0;p<npoint;p++) OpV2rt.SetMatrix(p,Aget); for(int p=0;p<npoint;p++) OpTestRoundTrip.SetMatrix(p,Aget);
std::vector<std::vector<calcMatrix> > A4; std::vector<std::vector<calcMatrix> > A4;
ReadMatrix(OpV2rt,npoint,A4); ReadMatrix(OpTestRoundTrip,npoint,A4);
RealD numrt=0.0; RealD numrt=0.0;
for(int p=0;p<npoint;p++){ for(int p=0;p<npoint;p++){
@@ -255,19 +261,19 @@ int main (int argc, char ** argv)
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////
// and V2 applies the matrix it just built // and the operator under test applies the matrix it just built
//////////////////////////////////////////////// ////////////////////////////////////////////////
typedef MrhsV2::CoarseVector CoarseVectorS; typedef TestOperator::CoarseVector CoarseVectorS;
GridParallelRNG cRNG(CoarseS); cRNG.SeedFixedIntegers(std::vector<int>({5,6,7,8})); GridParallelRNG cRNG(CoarseS); cRNG.SeedFixedIntegers(std::vector<int>({5,6,7,8}));
CoarseVectorS in(CoarseSMulti); random(cRNG,in); CoarseVectorS in(CoarseSMulti); random(cRNG,in);
CoarseVectorS out(CoarseSMulti); CoarseVectorS out(CoarseSMulti);
OpV2.M(in,out); OpTest.M(in,out);
std::cout << GridLogMessage << "|in|^2 = " << norm2(in) std::cout << GridLogMessage << "|in|^2 = " << norm2(in)
<< " |M_V2 in|^2 = " << norm2(out) << std::endl; << " |M_test in|^2 = " << norm2(out) << std::endl;
GRID_ASSERT( norm2(out) > 0.0 ); GRID_ASSERT( norm2(out) > 0.0 );
std::cout << GridLogMessage << "Test_coarse_v2_coarsen: ALL PASS" << std::endl; std::cout << GridLogMessage << "Test_coarse_coarsen: ALL PASS" << std::endl;
Grid_finalize(); Grid_finalize();
} }
+2 -2
View File
@@ -114,7 +114,7 @@ int main (int argc, char ** argv)
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
std::cout<<GridLogMessage << "Building little Dirac operator"<< std::endl; std::cout<<GridLogMessage << "Building little Dirac operator"<< std::endl;
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
NextToNextToNextToNearestStencilGeometry5D geom(Coarse5d); NextToNextToNextToNearestStencilGeometry5D geom(Coarse5d);
@@ -223,7 +223,7 @@ int main (int argc, char ** argv)
GridCartesian *CoarseMrhs = new GridCartesian(rhLatt,rhSimd,rhMpi); GridCartesian *CoarseMrhs = new GridCartesian(rhLatt,rhSimd,rhMpi);
#if 0 #if 0
MultiGeneralCoarsenedMatrix mrhs(LittleDiracOp,CoarseMrhs); DeprecatedMultiGeneralCoarsenedMatrix mrhs(LittleDiracOp,CoarseMrhs);
typedef decltype(mrhs) MultiGeneralCoarsenedMatrix_t; typedef decltype(mrhs) MultiGeneralCoarsenedMatrix_t;
////////////////////////////////////////// //////////////////////////////////////////
+2 -2
View File
@@ -96,7 +96,7 @@ int main (int argc, char ** argv)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
///////////// Coarse basis and Little Dirac Operator /////// ///////////// Coarse basis and Little Dirac Operator ///////
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
NextToNextToNextToNearestStencilGeometry5D geom(Coarse5d); NextToNextToNextToNearestStencilGeometry5D geom(Coarse5d);
@@ -140,7 +140,7 @@ int main (int argc, char ** argv)
Coordinate rhSimd({vComplex::Nsimd(),1, 1,1,1,1}); Coordinate rhSimd({vComplex::Nsimd(),1, 1,1,1,1});
GridCartesian *CoarseMrhs = new GridCartesian(rhLatt,rhSimd,rhMpi); GridCartesian *CoarseMrhs = new GridCartesian(rhLatt,rhSimd,rhMpi);
typedef MultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MultiGeneralCoarsenedMatrix_t; typedef DeprecatedMultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MultiGeneralCoarsenedMatrix_t;
MultiGeneralCoarsenedMatrix_t mrhs(geom,CoarseMrhs); MultiGeneralCoarsenedMatrix_t mrhs(geom,CoarseMrhs);
mrhs.CoarsenOperator(FineHermOp,Aggregates,Coarse5d); mrhs.CoarsenOperator(FineHermOp,Aggregates,Coarse5d);
+2 -2
View File
@@ -189,7 +189,7 @@ int main (int argc, char ** argv)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
///////////// Coarse basis and Little Dirac Operator /////// ///////////// Coarse basis and Little Dirac Operator ///////
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
NextToNextToNextToNearestStencilGeometry5D geom(Coarse5d); NextToNextToNextToNearestStencilGeometry5D geom(Coarse5d);
@@ -371,7 +371,7 @@ slurm-1482367.out:Grid : Message : 6169.469330 s : HDCG: Pcg converged in 487 it
////////////////////////////////////////// //////////////////////////////////////////
// Build a HDCG solver // Build a HDCG solver
////////////////////////////////////////// //////////////////////////////////////////
TwoLevelADEF2<LatticeFermion,CoarseVector,Subspace> DeprecatedTwoLevelADEF2<LatticeFermion,CoarseVector,Subspace>
HDCG(1.0e-8, 700, HDCG(1.0e-8, 700,
FineHermOp, FineHermOp,
CGsmooth, CGsmooth,
@@ -218,7 +218,7 @@ int main (int argc, char ** argv)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
///////////// Coarse basis and Little Dirac Operator /////// ///////////// Coarse basis and Little Dirac Operator ///////
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
NextToNextToNextToNearestStencilGeometry5D geom(Coarse5d); NextToNextToNextToNearestStencilGeometry5D geom(Coarse5d);
@@ -269,7 +269,7 @@ int main (int argc, char ** argv)
Coordinate rhSimd({vComplex::Nsimd(),1, 1,1,1,1}); Coordinate rhSimd({vComplex::Nsimd(),1, 1,1,1,1});
GridCartesian *CoarseMrhs = new GridCartesian(rhLatt,rhSimd,rhMpi); GridCartesian *CoarseMrhs = new GridCartesian(rhLatt,rhSimd,rhMpi);
typedef MultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MultiGeneralCoarsenedMatrix_t; typedef DeprecatedMultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MultiGeneralCoarsenedMatrix_t;
MultiGeneralCoarsenedMatrix_t mrhs(geom,CoarseMrhs); MultiGeneralCoarsenedMatrix_t mrhs(geom,CoarseMrhs);
std::cout << "**************************************"<<std::endl; std::cout << "**************************************"<<std::endl;
@@ -407,7 +407,7 @@ int main (int argc, char ** argv)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
///////////// Coarse basis and Little Dirac Operator /////// ///////////// Coarse basis and Little Dirac Operator ///////
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
NextToNextToNextToNearestStencilGeometry5D geom(Coarse5d); NextToNextToNextToNearestStencilGeometry5D geom(Coarse5d);
@@ -456,7 +456,7 @@ int main (int argc, char ** argv)
Coordinate rhSimd({vComplex::Nsimd(),1, 1,1,1,1}); Coordinate rhSimd({vComplex::Nsimd(),1, 1,1,1,1});
GridCartesian *CoarseMrhs = new GridCartesian(rhLatt,rhSimd,rhMpi); GridCartesian *CoarseMrhs = new GridCartesian(rhLatt,rhSimd,rhMpi);
typedef MultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MultiGeneralCoarsenedMatrix_t; typedef DeprecatedMultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MultiGeneralCoarsenedMatrix_t;
MultiGeneralCoarsenedMatrix_t mrhs(geom,CoarseMrhs); MultiGeneralCoarsenedMatrix_t mrhs(geom,CoarseMrhs);
std::cout << "**************************************"<<std::endl; std::cout << "**************************************"<<std::endl;
@@ -406,7 +406,7 @@ int main (int argc, char ** argv)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
///////////// Coarse basis and Little Dirac Operator /////// ///////////// Coarse basis and Little Dirac Operator ///////
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
NextToNextToNextToNearestStencilGeometry5D geom(Coarse5d); NextToNextToNextToNearestStencilGeometry5D geom(Coarse5d);
@@ -455,7 +455,7 @@ int main (int argc, char ** argv)
Coordinate rhSimd({vComplex::Nsimd(),1, 1,1,1,1}); Coordinate rhSimd({vComplex::Nsimd(),1, 1,1,1,1});
GridCartesian *CoarseMrhs = new GridCartesian(rhLatt,rhSimd,rhMpi); GridCartesian *CoarseMrhs = new GridCartesian(rhLatt,rhSimd,rhMpi);
typedef MultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MultiGeneralCoarsenedMatrix_t; typedef DeprecatedMultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MultiGeneralCoarsenedMatrix_t;
MultiGeneralCoarsenedMatrix_t mrhs(geom,CoarseMrhs); MultiGeneralCoarsenedMatrix_t mrhs(geom,CoarseMrhs);
std::cout << "**************************************"<<std::endl; std::cout << "**************************************"<<std::endl;
@@ -215,7 +215,7 @@ int main (int argc, char ** argv)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
///////////// Coarse basis and Little Dirac Operator /////// ///////////// Coarse basis and Little Dirac Operator ///////
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
NextToNextToNextToNearestStencilGeometry5D geom(Coarse5d); NextToNextToNextToNearestStencilGeometry5D geom(Coarse5d);
@@ -161,7 +161,7 @@ int main (int argc, char ** argv)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
///////////// Coarse basis and Little Dirac Operator /////// ///////////// Coarse basis and Little Dirac Operator ///////
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
NextToNextToNextToNearestStencilGeometry5D geom(Coarse5d); NextToNextToNextToNearestStencilGeometry5D geom(Coarse5d);
@@ -210,7 +210,7 @@ int main (int argc, char ** argv)
Coordinate rhSimd({vComplex::Nsimd(),1, 1,1,1,1}); Coordinate rhSimd({vComplex::Nsimd(),1, 1,1,1,1});
GridCartesian *CoarseMrhs = new GridCartesian(rhLatt,rhSimd,rhMpi); GridCartesian *CoarseMrhs = new GridCartesian(rhLatt,rhSimd,rhMpi);
typedef MultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MultiGeneralCoarsenedMatrix_t; typedef DeprecatedMultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MultiGeneralCoarsenedMatrix_t;
MultiGeneralCoarsenedMatrix_t mrhs(geom,CoarseMrhs); MultiGeneralCoarsenedMatrix_t mrhs(geom,CoarseMrhs);
std::cout << "**************************************"<<std::endl; std::cout << "**************************************"<<std::endl;
@@ -162,7 +162,7 @@ int main (int argc, char ** argv)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
///////////// Coarse basis and Little Dirac Operator /////// ///////////// Coarse basis and Little Dirac Operator ///////
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
NextToNextToNextToNearestStencilGeometry5D geom(Coarse5d); NextToNextToNextToNearestStencilGeometry5D geom(Coarse5d);
@@ -217,7 +217,7 @@ int main (int argc, char ** argv)
Coordinate rhSimd({vComplex::Nsimd(),1, 1,1,1,1}); Coordinate rhSimd({vComplex::Nsimd(),1, 1,1,1,1});
GridCartesian *CoarseMrhs = new GridCartesian(rhLatt,rhSimd,rhMpi); GridCartesian *CoarseMrhs = new GridCartesian(rhLatt,rhSimd,rhMpi);
typedef MultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MultiGeneralCoarsenedMatrix_t; typedef DeprecatedMultiGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> MultiGeneralCoarsenedMatrix_t;
MultiGeneralCoarsenedMatrix_t mrhs(geom,CoarseMrhs); MultiGeneralCoarsenedMatrix_t mrhs(geom,CoarseMrhs);
/////////////////////// ///////////////////////
+1 -1
View File
@@ -254,7 +254,7 @@ int main (int argc, char ** argv)
const int cb = 0 ; const int cb = 0 ;
LatticeFermion prom(FGrid); LatticeFermion prom(FGrid);
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
NextToNearestStencilGeometry5D geom(Coarse5d); NextToNearestStencilGeometry5D geom(Coarse5d);
@@ -376,10 +376,10 @@ int main (int argc, char ** argv)
std::cout <<bl<<" "<<br<<"\t"<<innerProduct(U.subspace[bl],src)<<std::endl; std::cout <<bl<<" "<<br<<"\t"<<innerProduct(U.subspace[bl],src)<<std::endl;
}} }}
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperatorV; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperatorV;
typedef LittleDiracOperatorV::CoarseVector CoarseVectorV; typedef LittleDiracOperatorV::CoarseVector CoarseVectorV;
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,2*nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,2*nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
V.Orthogonalise(); V.Orthogonalise();
@@ -375,10 +375,10 @@ int main (int argc, char ** argv)
std::cout <<bl<<" "<<br<<"\t"<<innerProduct(U.subspace[bl],src)<<std::endl; std::cout <<bl<<" "<<br<<"\t"<<innerProduct(U.subspace[bl],src)<<std::endl;
}} }}
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperatorV; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperatorV;
typedef LittleDiracOperatorV::CoarseVector CoarseVectorV; typedef LittleDiracOperatorV::CoarseVector CoarseVectorV;
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,2*nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,2*nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
V.Orthogonalise(); V.Orthogonalise();
@@ -369,8 +369,8 @@ int main (int argc, char ** argv)
// } // }
// typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,2*nbasis> LittleDiracOperator; // typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,2*nbasis> LittleDiracOperator;
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
LittleDiracOperator LittleDiracOpPV(geom,FGrid,Coarse5d); LittleDiracOperator LittleDiracOpPV(geom,FGrid,Coarse5d);
LittleDiracOpPV.CoarsenOperator(PVdagM,V,V); LittleDiracOpPV.CoarsenOperator(PVdagM,V,V);
+1 -1
View File
@@ -185,7 +185,7 @@ int main (int argc, char ** argv)
const int cb = 0 ; const int cb = 0 ;
LatticeFermion prom(FGrid); LatticeFermion prom(FGrid);
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,2*nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,2*nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
NearestStencilGeometry4D geom(Coarse4d); NearestStencilGeometry4D geom(Coarse4d);
@@ -185,7 +185,7 @@ int main (int argc, char ** argv)
const int cb = 0 ; const int cb = 0 ;
LatticeFermion prom(FGrid); LatticeFermion prom(FGrid);
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
NearestStencilGeometry4D geom(Coarse4d); NearestStencilGeometry4D geom(Coarse4d);
@@ -182,7 +182,7 @@ int main (int argc, char ** argv)
const int cb = 0 ; const int cb = 0 ;
LatticeFermion prom(FGrid); LatticeFermion prom(FGrid);
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,2*nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,2*nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
NearestStencilGeometry4D geom(Coarse4d); NearestStencilGeometry4D geom(Coarse4d);
@@ -182,7 +182,7 @@ int main (int argc, char ** argv)
const int cb = 0 ; const int cb = 0 ;
LatticeFermion prom(FGrid); LatticeFermion prom(FGrid);
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator; typedef DeprecatedGeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
typedef LittleDiracOperator::CoarseVector CoarseVector; typedef LittleDiracOperator::CoarseVector CoarseVector;
NearestStencilGeometry4D geom(Coarse4d); NearestStencilGeometry4D geom(Coarse4d);
+5 -5
View File
@@ -3,9 +3,9 @@
* *
* Tests three orderings with an empty rocFFT cache to find which GPU * Tests three orderings with an empty rocFFT cache to find which GPU
* operation before plan creation triggers the failure: * operation before plan creation triggers the failure:
* A) hipMalloc only — hypothesis: passes (no async GPU work) * A) hipMalloc only -- hypothesis: passes (no async GPU work)
* B) hipMalloc + hipMemset — hypothesis: fails (async GPU work in flight) * B) hipMalloc + hipMemset -- hypothesis: fails (async GPU work in flight)
* C) hipMalloc + hipMemset — hypothesis: passes (work completed before plan) * C) hipMalloc + hipMemset -- hypothesis: passes (work completed before plan)
* + hipDeviceSynchronize * + hipDeviceSynchronize
* *
* Compile: * Compile:
@@ -59,13 +59,13 @@ int main(void) {
hipfftResult rvB = makePlan(G, howmany); hipfftResult rvB = makePlan(G, howmany);
printf("G=%-4d B) hipMalloc + hipMemset : %s\n", G, res(rvB)); printf("G=%-4d B) hipMalloc + hipMemset : %s\n", G, res(rvB));
// C: hipMalloc + hipMemset + sync — does syncing before plan creation fix it? // C: hipMalloc + hipMemset + sync -- does syncing before plan creation fix it?
hipMemset(buf, 0, nelems * sizeof(hipfftDoubleComplex)); hipMemset(buf, 0, nelems * sizeof(hipfftDoubleComplex));
hipDeviceSynchronize(); hipDeviceSynchronize();
hipfftResult rvC = makePlan(G, howmany); hipfftResult rvC = makePlan(G, howmany);
printf("G=%-4d C) hipMalloc + hipMemset + sync: %s\n", G, res(rvC)); printf("G=%-4d C) hipMalloc + hipMemset + sync: %s\n", G, res(rvC));
// A last: hipMalloc only, no async GPU work — should always pass // A last: hipMalloc only, no async GPU work -- should always pass
hipfftResult rvA = makePlan(G, howmany); hipfftResult rvA = makePlan(G, howmany);
printf("G=%-4d A) hipMalloc only : %s\n\n", G, res(rvA)); printf("G=%-4d A) hipMalloc only : %s\n\n", G, res(rvA));
+1 -1
View File
@@ -33,7 +33,7 @@ int main(void) {
int n[] = {G}; int n[] = {G};
long nelems = (long)G * howmany; long nelems = (long)G * howmany;
// Plan created BEFORE hipMalloc — succeeds for all G // Plan created BEFORE hipMalloc -- succeeds for all G
hipfftHandle p; hipfftHandle p;
size_t workSize = 0; size_t workSize = 0;
hipfftCreate(&p); hipfftCreate(&p);
+2 -2
View File
@@ -38,8 +38,8 @@ static const char *hipfftResultString(hipfftResult r) {
// Plan creation + execution for (G, howmany). // Plan creation + execution for (G, howmany).
// Tests two orderings to isolate whether a prior hipMalloc poisons hipfft // Tests two orderings to isolate whether a prior hipMalloc poisons hipfft
// plan creation for small G on ROCm 7: // plan creation for small G on ROCm 7:
// A) plan BEFORE hipMalloc — hypothesis: succeeds // A) plan BEFORE hipMalloc -- hypothesis: succeeds
// B) hipMalloc BEFORE plan — hypothesis: fails for G < 32 // B) hipMalloc BEFORE plan -- hypothesis: fails for G < 32
static void tryPlanAndExec(int G, long howmany) { static void tryPlanAndExec(int G, long howmany) {
int n[] = {G}; int n[] = {G};
long nelems = (long)G * howmany; long nelems = (long)G * howmany;
Loaded 100 of 116 files, more files were not shown because too many files have changed in this diff. Show more