mirror of
https://github.com/paboyle/Grid.git
synced 2026-10-12 02:38:06 +01:00
Clean up and unification of PVdagM and HDCG, mixed precision support
This commit is contained in:
1 parent
2043072d9c
commit
eeb56e6b39
46 files changed
+1404
-3815
No files matched your search
@@ -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: `CoarsenedMatrix.h` (coarse operator), `GeneralCoarsenedMatrix.h` and `GeneralCoarsenedMatrixMultiRHS.h` (general coarsening supporting multi-RHS solves), `Aggregates.h` (near-null vector construction), `Geometry.h` (coarse-grid geometry). `MultiGrid.h` is the top-level include.
|
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.
|
||||||
|
|
||||||
### GPU acceleration
|
### GPU acceleration
|
||||||
|
|
||||||
|
|||||||
@@ -214,6 +214,29 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Present any LinearOperatorBase as Hermitian: Op = AdjOp = HermOp.
|
||||||
|
// For coarsening (CoarsenOperator applies Op) an HPD operator whose
|
||||||
|
// Op is a factor and HermOp the product, e.g. SchurDiagMooee or MdagM.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
template<class Field>
|
||||||
|
class HermOpAdaptor : public LinearOperatorBase<Field> {
|
||||||
|
LinearOperatorBase<Field> &_Mat;
|
||||||
|
public:
|
||||||
|
HermOpAdaptor(LinearOperatorBase<Field> &Mat): _Mat(Mat){};
|
||||||
|
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){ _Mat.HermOp(in,out); }
|
||||||
|
void AdjOp (const Field &in, Field &out){ _Mat.HermOp(in,out); }
|
||||||
|
void HermOp (const Field &in, Field &out){ _Mat.HermOp(in,out); }
|
||||||
|
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){
|
||||||
|
HermOp(in,out);
|
||||||
|
ComplexD dot = innerProduct(in,out);
|
||||||
|
n1=real(dot);
|
||||||
|
n2=norm2(out);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Wrap an already herm matrix
|
// Wrap an already herm matrix
|
||||||
|
|||||||
@@ -1942,6 +1942,10 @@ public:
|
|||||||
Cs);
|
Cs);
|
||||||
synchronise();
|
synchronise();
|
||||||
|
|
||||||
|
// Synchronise ONCE, after the whole train of calls. A synchronise inside
|
||||||
|
// the loop measures launch and completion latency, which at small shapes
|
||||||
|
// exceeds the kernel: at M=K=60, N=1, batch 1024 it hid a factor of two
|
||||||
|
// between the precisions and reported them level.
|
||||||
RealD t0 = usecond();
|
RealD t0 = usecond();
|
||||||
for(int i=0;i<ncall;i++){
|
for(int i=0;i<ncall;i++){
|
||||||
gemmBatched(M,N,K,
|
gemmBatched(M,N,K,
|
||||||
@@ -1950,8 +1954,8 @@ public:
|
|||||||
Bs, // k x n
|
Bs, // k x n
|
||||||
beta,
|
beta,
|
||||||
Cs);
|
Cs);
|
||||||
synchronise();
|
|
||||||
}
|
}
|
||||||
|
synchronise();
|
||||||
RealD t1 = usecond();
|
RealD t1 = usecond();
|
||||||
RealD bytes = 1.0*sizeof(CComplex)*(M*N*2+N*K+M*K)*BATCH;
|
RealD bytes = 1.0*sizeof(CComplex)*(M*N*2+N*K+M*K)*BATCH;
|
||||||
flops = 8.0*M*N*K*BATCH*ncall;
|
flops = 8.0*M*N*K*BATCH*ncall;
|
||||||
|
|||||||
@@ -111,7 +111,9 @@ public:
|
|||||||
// ranks wait on it. The _64 getrf/getrs pair is the path proven in the
|
// ranks wait on it. The _64 getrf/getrs pair is the path proven in the
|
||||||
// 1-rank dense coarse-coarse setup at N=69120 (DenseCoarseMatrix.h).
|
// 1-rank dense coarse-coarse setup at N=69120 (DenseCoarseMatrix.h).
|
||||||
// Same in-place, column-major, lda=N contract as inverseBatched.
|
// Same in-place, column-major, lda=N contract as inverseBatched.
|
||||||
// NB: written to rocSOLVER's documented z*_64 signatures; not compiled on
|
// ComplexD and ComplexF overloads (z*_64 / c*_64), so the distributed
|
||||||
|
// dense inverse can run in either precision (DenseInverseScalar).
|
||||||
|
// NB: written to rocSOLVER's documented *_64 signatures; not compiled on
|
||||||
// HIP in the air-gapped loop -- verify on first hipcc build.
|
// HIP in the air-gapped loop -- verify on first hipcc build.
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
double lastGetrfUs = 0.0, lastGetrsUs = 0.0; // split timing of the last inverseLU (HIP path)
|
double lastGetrfUs = 0.0, lastGetrsUs = 0.0; // split timing of the last inverseLU (HIP path)
|
||||||
@@ -145,6 +147,37 @@ public:
|
|||||||
inverseBatched(N, bp);
|
inverseBatched(N, bp);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
// fp32 overload: the Schur-recursion leaf under --enable-dense-inverse-precision=single
|
||||||
|
void inverseLU(int64_t N, ComplexF *A)
|
||||||
|
{
|
||||||
|
#ifdef GRID_HIP
|
||||||
|
rocblas_handle handle = Handle();
|
||||||
|
deviceVector<int64_t> ipiv((uint64_t)N);
|
||||||
|
deviceVector<int64_t> info(1);
|
||||||
|
double t0 = usecond();
|
||||||
|
auto st1 = rocsolver_cgetrf_64(handle, N, N, (rocblas_float_complex *)A, N, &ipiv[0], &info[0]);
|
||||||
|
GRID_ASSERT(st1 == rocblas_status_success);
|
||||||
|
accelerator_barrier();
|
||||||
|
lastGetrfUs = usecond()-t0;
|
||||||
|
int64_t info_h = -1; acceleratorCopyFromDevice(&info[0], &info_h, sizeof(int64_t));
|
||||||
|
GRID_ASSERT(info_h == 0);
|
||||||
|
deviceVector<ComplexF> X((uint64_t)N*N);
|
||||||
|
{ ComplexF *x = &X[0]; const int64_t NN = N;
|
||||||
|
accelerator_for(idx, (uint64_t)N*N, 1, { int64_t j = idx/NN, i = idx - j*NN; x[idx] = (i==j) ? ComplexF(1.0,0.0) : ComplexF(0.0,0.0); });
|
||||||
|
accelerator_barrier(); }
|
||||||
|
auto st2 = rocsolver_cgetrs_64(handle, rocblas_operation_none, N, N,
|
||||||
|
(rocblas_float_complex *)A, N, &ipiv[0],
|
||||||
|
(rocblas_float_complex *)&X[0], N);
|
||||||
|
GRID_ASSERT(st2 == rocblas_status_success);
|
||||||
|
accelerator_barrier();
|
||||||
|
lastGetrsUs = usecond()-t0-lastGetrfUs;
|
||||||
|
acceleratorCopyDeviceToDevice((void *)&X[0], (void *)A, (uint64_t)N*N*sizeof(ComplexF));
|
||||||
|
#else
|
||||||
|
deviceVector<ComplexF*> bp(1); std::vector<ComplexF*> ptr(1); ptr[0] = A;
|
||||||
|
acceleratorCopyToDevice(&ptr[0], &bp[0], sizeof(ComplexF*));
|
||||||
|
inverseBatched(N, bp);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void inverseBatched(int64_t N, deviceVector<ComplexF*> &Amat)
|
void inverseBatched(int64_t N, deviceVector<ComplexF*> &Amat)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -93,6 +93,25 @@ public:
|
|||||||
deviceVector<scalar> BLAS_F; // nrhs x fine_vol * words -- the sources
|
deviceVector<scalar> BLAS_F; // nrhs x fine_vol * words -- the sources
|
||||||
deviceVector<scalar> BLAS_C; // nrhs x coarse_vol * nbasis -- the coarse coeffs
|
deviceVector<scalar> BLAS_C; // nrhs x coarse_vol * nbasis -- the coarse coeffs
|
||||||
|
|
||||||
|
// Resident device memory this object holds. deviceVector is not
|
||||||
|
// evictable, so this counts against the hard budget, not the cache.
|
||||||
|
// Capacity, not size: a call with fewer right-hand sides shrinks the size
|
||||||
|
// and frees nothing.
|
||||||
|
uint64_t DeviceBytes(void)
|
||||||
|
{
|
||||||
|
return (uint64_t)(BLAS_V.capacity()+BLAS_F.capacity()+BLAS_C.capacity())*sizeof(scalar);
|
||||||
|
}
|
||||||
|
// The import/export scratch grows to the largest right-hand-side count
|
||||||
|
// any call has used and keeps that capacity. Setup projects the whole
|
||||||
|
// basis in one call, so unless it is released the scratch stays as large
|
||||||
|
// as the basis store for the rest of the run. The next call regrows it
|
||||||
|
// to the size that call needs.
|
||||||
|
void ReleaseScratch(void)
|
||||||
|
{
|
||||||
|
deviceVector<scalar>().swap(BLAS_F);
|
||||||
|
deviceVector<scalar>().swap(BLAS_C);
|
||||||
|
}
|
||||||
|
|
||||||
RealD blasNorm2(deviceVector<scalar> &blas)
|
RealD blasNorm2(deviceVector<scalar> &blas)
|
||||||
{
|
{
|
||||||
scalar ss(0.0);
|
scalar ss(0.0);
|
||||||
@@ -117,9 +136,14 @@ public:
|
|||||||
block_vol=0;
|
block_vol=0;
|
||||||
coarse_vol=0;
|
coarse_vol=0;
|
||||||
words=0;
|
words=0;
|
||||||
BLAS_V.resize(0);
|
// deviceVector is a std::vector with a device allocator, so resize(0)
|
||||||
BLAS_F.resize(0);
|
// drops the SIZE and keeps the CAPACITY: it returns no device memory at
|
||||||
BLAS_C.resize(0);
|
// all. The basis store is nbasis fine vectors (10.9 GB at nbasis 64 on
|
||||||
|
// a 48^3x96 Ls=24 rank), so that has to be a real free. Swap with an
|
||||||
|
// empty vector, which destroys the buffer.
|
||||||
|
deviceVector<scalar>().swap(BLAS_V);
|
||||||
|
deviceVector<scalar>().swap(BLAS_F);
|
||||||
|
deviceVector<scalar>().swap(BLAS_C);
|
||||||
}
|
}
|
||||||
void Allocate(int _nbasis,GridBase *_fgrid,GridBase *_cgrid)
|
void Allocate(int _nbasis,GridBase *_fgrid,GridBase *_cgrid)
|
||||||
{
|
{
|
||||||
@@ -153,25 +177,39 @@ public:
|
|||||||
|
|
||||||
BLAS_V.resize (fine_vol * words * nbasis );
|
BLAS_V.resize (fine_vol * words * nbasis );
|
||||||
}
|
}
|
||||||
void ImportFineGridVectors(std::vector <Field > &vecs, deviceVector<scalar> &blas)
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// The fine side is templated on the INCOMING vector object, not fixed to
|
||||||
|
// Field. Field fixes only the STORAGE: the BLAS scalar, the word count and
|
||||||
|
// the full local geometry. Import is already a layout transformation --
|
||||||
|
// (oSite,lane) is unpacked to full local coordinates and regathered in
|
||||||
|
// block order -- so a precision change costs nothing here, and neither does
|
||||||
|
// a different SIMD layout. That is what lets one projector serve an fp64
|
||||||
|
// outer level and an fp32 coarse sector with no second basis store.
|
||||||
|
//
|
||||||
|
// The full LOCAL dimensions must agree; the SIMD layout and the precision
|
||||||
|
// need not.
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
template<class vobj>
|
||||||
|
void ImportFineGridVectors(std::vector <Lattice<vobj> > &vecs, deviceVector<scalar> &blas)
|
||||||
{
|
{
|
||||||
GRID_TRACE("ImportFineGridVectors");
|
GRID_TRACE("ImportFineGridVectors");
|
||||||
int nvec = vecs.size();
|
int nvec = vecs.size();
|
||||||
typedef typename Field::vector_object vobj;
|
typedef typename vobj::scalar_object fine_scalar_object;
|
||||||
// std::cout << GridLogMessage <<" BlockProjector importing "<<nvec<< " fine grid vectors" <<std::endl;
|
typedef typename vobj::scalar_type fine_scalar;
|
||||||
|
|
||||||
GRID_ASSERT(vecs[0].Grid()==fine_grid);
|
|
||||||
|
|
||||||
|
GridBase *fgrid = vecs[0].Grid();
|
||||||
int _ndimension = coarse_grid->_ndimension;
|
int _ndimension = coarse_grid->_ndimension;
|
||||||
|
for(int d=0;d<_ndimension;d++) GRID_ASSERT(fgrid->_ldimensions[d] == fine_ldimensions[d]);
|
||||||
|
GRID_ASSERT(sizeof(fine_scalar_object)/sizeof(fine_scalar) == words);
|
||||||
|
|
||||||
uint64_t sz = blas.size();
|
uint64_t sz = blas.size();
|
||||||
|
|
||||||
acceleratorMemSet(&blas[0],0,blas.size()*sizeof(scalar));
|
acceleratorMemSet(&blas[0],0,blas.size()*sizeof(scalar));
|
||||||
|
|
||||||
Coordinate fine_rdimensions = fine_grid->_rdimensions;
|
Coordinate fine_rdimensions = fgrid->_rdimensions;
|
||||||
Coordinate coarse_l = coarse_ldimensions;
|
Coordinate coarse_l = coarse_ldimensions;
|
||||||
Coordinate block_l = block_ldimensions;
|
Coordinate block_l = block_ldimensions;
|
||||||
Coordinate fsimd = fine_simd;
|
Coordinate fsimd = fgrid->_simd_layout;
|
||||||
int64_t bv= block_vol;
|
int64_t bv= block_vol;
|
||||||
for(int v=0;v<vecs.size();v++){
|
for(int v=0;v<vecs.size();v++){
|
||||||
|
|
||||||
@@ -181,7 +219,7 @@ public:
|
|||||||
auto blasData_p = &blas[0];
|
auto blasData_p = &blas[0];
|
||||||
auto fineData_p = &fineData[0];
|
auto fineData_p = &fineData[0];
|
||||||
|
|
||||||
int64_t osites = fine_grid->oSites();
|
int64_t osites = fgrid->oSites();
|
||||||
|
|
||||||
// loop over fine sites
|
// loop over fine sites
|
||||||
const int Nsimd = vobj::Nsimd();
|
const int Nsimd = vobj::Nsimd();
|
||||||
@@ -213,7 +251,7 @@ public:
|
|||||||
Lexicographic::IndexFromCoor(coor_c,sc,coarse_l);
|
Lexicographic::IndexFromCoor(coor_c,sc,coarse_l);
|
||||||
Lexicographic::IndexFromCoor(coor_b,sb,block_l);
|
Lexicographic::IndexFromCoor(coor_b,sb,block_l);
|
||||||
|
|
||||||
scalar_object data = extractLane(lane,fineData[sf]);
|
fine_scalar_object data = extractLane(lane,fineData[sf]);
|
||||||
|
|
||||||
// BLAS_F[coarse_vol][nvec][block_vol][words]
|
// BLAS_F[coarse_vol][nvec][block_vol][words]
|
||||||
int64_t site = (sc*nvec + v)*bv
|
int64_t site = (sc*nvec + v)*bv
|
||||||
@@ -221,9 +259,9 @@ public:
|
|||||||
|
|
||||||
// GRID_ASSERT(site*lwords<sz);
|
// GRID_ASSERT(site*lwords<sz);
|
||||||
|
|
||||||
scalar_object * ptr = (scalar_object *)&blasData_p[site*lwords];
|
// element-wise: the store may differ in precision from the field
|
||||||
|
const fine_scalar *dp = (const fine_scalar *)&data;
|
||||||
*ptr = data;
|
for(uint64_t w=0;w<lwords;w++) blasData_p[site*lwords+w] = scalar(dp[w]);
|
||||||
#ifdef GRID_SIMT
|
#ifdef GRID_SIMT
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@@ -245,9 +283,11 @@ public:
|
|||||||
// The gather of block_vol from fine_vol, and the transpose of nrhs against
|
// The gather of block_vol from fine_vol, and the transpose of nrhs against
|
||||||
// block_vol, are the irreducible part: this is not the identity.
|
// block_vol, are the irreducible part: this is not the identity.
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
void ImportFineGridMrhsVectors(Field &vec_mrhs, deviceVector<scalar> &blas)
|
template<class vobj>
|
||||||
|
void ImportFineGridMrhsVectors(Lattice<vobj> &vec_mrhs, deviceVector<scalar> &blas)
|
||||||
{
|
{
|
||||||
typedef typename Field::vector_object vobj;
|
typedef typename vobj::scalar_object fine_scalar_object;
|
||||||
|
typedef typename vobj::scalar_type fine_scalar;
|
||||||
|
|
||||||
GridBase *fine_mrhs_grid = vec_mrhs.Grid();
|
GridBase *fine_mrhs_grid = vec_mrhs.Grid();
|
||||||
int _ndimension = coarse_grid->_ndimension;
|
int _ndimension = coarse_grid->_ndimension;
|
||||||
@@ -255,27 +295,31 @@ public:
|
|||||||
GRID_ASSERT(fine_mrhs_grid->_ndimension == _ndimension+1);
|
GRID_ASSERT(fine_mrhs_grid->_ndimension == _ndimension+1);
|
||||||
GRID_ASSERT(fine_mrhs_grid->_simd_layout[0] == 1);
|
GRID_ASSERT(fine_mrhs_grid->_simd_layout[0] == 1);
|
||||||
GRID_ASSERT(fine_mrhs_grid->_processors[0] == 1);
|
GRID_ASSERT(fine_mrhs_grid->_processors[0] == 1);
|
||||||
|
// Full LOCAL dimensions must agree; the SIMD layout and precision need not
|
||||||
for(int d=0;d<_ndimension;d++){
|
for(int d=0;d<_ndimension;d++){
|
||||||
GRID_ASSERT(fine_mrhs_grid->_rdimensions[d+1] == fine_grid->_rdimensions[d]);
|
GRID_ASSERT(fine_mrhs_grid->_ldimensions[d+1] == fine_ldimensions[d]);
|
||||||
GRID_ASSERT(fine_mrhs_grid->_simd_layout[d+1] == fine_grid->_simd_layout[d]);
|
|
||||||
}
|
}
|
||||||
|
GRID_ASSERT(sizeof(fine_scalar_object)/sizeof(fine_scalar) == words);
|
||||||
int nvec = fine_mrhs_grid->_rdimensions[0]; // nrhs
|
int nvec = fine_mrhs_grid->_rdimensions[0]; // nrhs
|
||||||
|
|
||||||
uint64_t sz = blas.size();
|
uint64_t sz = blas.size();
|
||||||
acceleratorMemSet(&blas[0],0,blas.size()*sizeof(scalar));
|
acceleratorMemSet(&blas[0],0,blas.size()*sizeof(scalar));
|
||||||
|
|
||||||
Coordinate fine_mrhs_rdimensions = fine_mrhs_grid->_rdimensions;
|
Coordinate fine_mrhs_rdimensions = fine_mrhs_grid->_rdimensions;
|
||||||
Coordinate fine_rdimensions = fine_grid->_rdimensions;
|
Coordinate fine_rdimensions(_ndimension);
|
||||||
|
Coordinate fsimd(_ndimension);
|
||||||
|
for(int d=0;d<_ndimension;d++){
|
||||||
|
fine_rdimensions[d] = fine_mrhs_grid->_rdimensions[d+1];
|
||||||
|
fsimd[d] = fine_mrhs_grid->_simd_layout[d+1];
|
||||||
|
}
|
||||||
Coordinate coarse_l = coarse_ldimensions;
|
Coordinate coarse_l = coarse_ldimensions;
|
||||||
Coordinate block_l = block_ldimensions;
|
Coordinate block_l = block_ldimensions;
|
||||||
Coordinate fsimd = fine_simd;
|
|
||||||
int64_t bv= block_vol;
|
int64_t bv= block_vol;
|
||||||
|
|
||||||
autoView( fineData , vec_mrhs, AcceleratorRead);
|
autoView( fineData , vec_mrhs, AcceleratorRead);
|
||||||
auto blasData_p = &blas[0];
|
auto blasData_p = &blas[0];
|
||||||
auto fineData_p = &fineData[0];
|
auto fineData_p = &fineData[0];
|
||||||
|
|
||||||
int64_t osites = fine_grid->oSites(); // D dimensional
|
|
||||||
int64_t osites_hi = fine_mrhs_grid->oSites(); // nvec * osites
|
int64_t osites_hi = fine_mrhs_grid->oSites(); // nvec * osites
|
||||||
|
|
||||||
const int Nsimd = vobj::Nsimd();
|
const int Nsimd = vobj::Nsimd();
|
||||||
@@ -312,13 +356,14 @@ public:
|
|||||||
Lexicographic::IndexFromCoor(coor_c,sc,coarse_l);
|
Lexicographic::IndexFromCoor(coor_c,sc,coarse_l);
|
||||||
Lexicographic::IndexFromCoor(coor_b,sb,block_l);
|
Lexicographic::IndexFromCoor(coor_b,sb,block_l);
|
||||||
|
|
||||||
scalar_object data = extractLane(lane,fineData[sfr]);
|
fine_scalar_object data = extractLane(lane,fineData[sfr]);
|
||||||
|
|
||||||
int64_t site = (sc*lnvec + v)*bv
|
int64_t site = (sc*lnvec + v)*bv
|
||||||
+ sb;
|
+ sb;
|
||||||
|
|
||||||
scalar_object * ptr = (scalar_object *)&blasData_p[site*lwords];
|
// element-wise: the store may differ in precision from the field
|
||||||
*ptr = data;
|
const fine_scalar *dp = (const fine_scalar *)&data;
|
||||||
|
for(uint64_t w=0;w<lwords;w++) blasData_p[site*lwords+w] = scalar(dp[w]);
|
||||||
#ifdef GRID_SIMT
|
#ifdef GRID_SIMT
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@@ -339,6 +384,7 @@ public:
|
|||||||
void ExportCoarseGridMrhsVectors(Lattice<vobj> &vec_mrhs, deviceVector<scalar> &blas)
|
void ExportCoarseGridMrhsVectors(Lattice<vobj> &vec_mrhs, deviceVector<scalar> &blas)
|
||||||
{
|
{
|
||||||
typedef typename vobj::scalar_object coarse_scalar_object;
|
typedef typename vobj::scalar_object coarse_scalar_object;
|
||||||
|
typedef typename vobj::scalar_type coarse_scalar; // may differ in precision from the BLAS scalar
|
||||||
|
|
||||||
GridBase *coarse_mrhs_grid = vec_mrhs.Grid();
|
GridBase *coarse_mrhs_grid = vec_mrhs.Grid();
|
||||||
int _ndimension = coarse_grid->_ndimension;
|
int _ndimension = coarse_grid->_ndimension;
|
||||||
@@ -365,7 +411,7 @@ public:
|
|||||||
int64_t osites_hi = coarse_mrhs_grid->oSites(); // nvec * osites
|
int64_t osites_hi = coarse_mrhs_grid->oSites(); // nvec * osites
|
||||||
|
|
||||||
const int Nsimd = vobj::Nsimd();
|
const int Nsimd = vobj::Nsimd();
|
||||||
uint64_t cwords=sizeof(typename vobj::scalar_object)/sizeof(scalar);
|
uint64_t cwords=sizeof(coarse_scalar_object)/sizeof(coarse_scalar);
|
||||||
GRID_ASSERT(cwords==nbasis);
|
GRID_ASSERT(cwords==nbasis);
|
||||||
int64_t lnvec = nvec;
|
int64_t lnvec = nvec;
|
||||||
|
|
||||||
@@ -391,8 +437,11 @@ public:
|
|||||||
Lexicographic::IndexFromCoor(coor_c,sc,coarse_l);
|
Lexicographic::IndexFromCoor(coor_c,sc,coarse_l);
|
||||||
|
|
||||||
int64_t blas_site = (sc*lnvec + v)*cwords;
|
int64_t blas_site = (sc*lnvec + v)*cwords;
|
||||||
coarse_scalar_object * ptr = (coarse_scalar_object *)&blasData_p[blas_site];
|
// Element-wise, converting: the BLAS buffer is in the FINE precision,
|
||||||
coarse_scalar_object data = *ptr;
|
// the coarse field in its own.
|
||||||
|
coarse_scalar_object data;
|
||||||
|
coarse_scalar *dp = (coarse_scalar *)&data;
|
||||||
|
for(uint64_t b=0;b<cwords;b++) dp[b] = coarse_scalar(blasData_p[blas_site+b]);
|
||||||
insertLane(lane,coarseData[scr],data);
|
insertLane(lane,coarseData[scr],data);
|
||||||
#ifdef GRID_SIMT
|
#ifdef GRID_SIMT
|
||||||
}
|
}
|
||||||
@@ -405,33 +454,39 @@ public:
|
|||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// Reverse directions: BLAS_F -> fine mrhs field, coarse mrhs field -> BLAS_C
|
// Reverse directions: BLAS_F -> fine mrhs field, coarse mrhs field -> BLAS_C
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
void ExportFineGridMrhsVectors(Field &vec_mrhs, deviceVector<scalar> &blas)
|
template<class vobj>
|
||||||
|
void ExportFineGridMrhsVectors(Lattice<vobj> &vec_mrhs, deviceVector<scalar> &blas)
|
||||||
{
|
{
|
||||||
typedef typename Field::vector_object vobj;
|
typedef typename vobj::scalar_object fine_scalar_object;
|
||||||
|
typedef typename vobj::scalar_type fine_scalar;
|
||||||
|
|
||||||
GridBase *fine_mrhs_grid = vec_mrhs.Grid();
|
GridBase *fine_mrhs_grid = vec_mrhs.Grid();
|
||||||
int _ndimension = coarse_grid->_ndimension;
|
int _ndimension = coarse_grid->_ndimension;
|
||||||
|
|
||||||
GRID_ASSERT(fine_mrhs_grid->_ndimension == _ndimension+1);
|
GRID_ASSERT(fine_mrhs_grid->_ndimension == _ndimension+1);
|
||||||
GRID_ASSERT(fine_mrhs_grid->_simd_layout[0] == 1);
|
GRID_ASSERT(fine_mrhs_grid->_simd_layout[0] == 1);
|
||||||
|
// Full LOCAL dimensions must agree; the SIMD layout and precision need not
|
||||||
for(int d=0;d<_ndimension;d++){
|
for(int d=0;d<_ndimension;d++){
|
||||||
GRID_ASSERT(fine_mrhs_grid->_rdimensions[d+1] == fine_grid->_rdimensions[d]);
|
GRID_ASSERT(fine_mrhs_grid->_ldimensions[d+1] == fine_ldimensions[d]);
|
||||||
GRID_ASSERT(fine_mrhs_grid->_simd_layout[d+1] == fine_grid->_simd_layout[d]);
|
|
||||||
}
|
}
|
||||||
|
GRID_ASSERT(sizeof(fine_scalar_object)/sizeof(fine_scalar) == words);
|
||||||
int nvec = fine_mrhs_grid->_rdimensions[0];
|
int nvec = fine_mrhs_grid->_rdimensions[0];
|
||||||
|
|
||||||
Coordinate fine_mrhs_rdimensions = fine_mrhs_grid->_rdimensions;
|
Coordinate fine_mrhs_rdimensions = fine_mrhs_grid->_rdimensions;
|
||||||
Coordinate fine_rdimensions = fine_grid->_rdimensions;
|
Coordinate fine_rdimensions(_ndimension);
|
||||||
|
Coordinate fsimd(_ndimension);
|
||||||
|
for(int d=0;d<_ndimension;d++){
|
||||||
|
fine_rdimensions[d] = fine_mrhs_grid->_rdimensions[d+1];
|
||||||
|
fsimd[d] = fine_mrhs_grid->_simd_layout[d+1];
|
||||||
|
}
|
||||||
Coordinate coarse_l = coarse_ldimensions;
|
Coordinate coarse_l = coarse_ldimensions;
|
||||||
Coordinate block_l = block_ldimensions;
|
Coordinate block_l = block_ldimensions;
|
||||||
Coordinate fsimd = fine_simd;
|
|
||||||
int64_t bv= block_vol;
|
int64_t bv= block_vol;
|
||||||
|
|
||||||
autoView( fineData , vec_mrhs, AcceleratorWrite);
|
autoView( fineData , vec_mrhs, AcceleratorWrite);
|
||||||
auto blasData_p = &blas[0];
|
auto blasData_p = &blas[0];
|
||||||
auto fineData_p = &fineData[0];
|
auto fineData_p = &fineData[0];
|
||||||
|
|
||||||
int64_t osites = fine_grid->oSites();
|
|
||||||
int64_t osites_hi = fine_mrhs_grid->oSites();
|
int64_t osites_hi = fine_mrhs_grid->oSites();
|
||||||
|
|
||||||
const int Nsimd = vobj::Nsimd();
|
const int Nsimd = vobj::Nsimd();
|
||||||
@@ -468,8 +523,10 @@ public:
|
|||||||
int64_t site = (sc*lnvec + v)*bv
|
int64_t site = (sc*lnvec + v)*bv
|
||||||
+ sb;
|
+ sb;
|
||||||
|
|
||||||
scalar_object * ptr = (scalar_object *)&blasData_p[site*lwords];
|
// element-wise: the store may differ in precision from the field
|
||||||
scalar_object data = *ptr;
|
fine_scalar_object data;
|
||||||
|
fine_scalar *dp = (fine_scalar *)&data;
|
||||||
|
for(uint64_t w=0;w<lwords;w++) dp[w] = fine_scalar(blasData_p[site*lwords+w]);
|
||||||
insertLane(lane,fineData[sfr],data);
|
insertLane(lane,fineData[sfr],data);
|
||||||
#ifdef GRID_SIMT
|
#ifdef GRID_SIMT
|
||||||
}
|
}
|
||||||
@@ -483,6 +540,7 @@ public:
|
|||||||
void ImportCoarseGridMrhsVectors(Lattice<vobj> &vec_mrhs, deviceVector<scalar> &blas)
|
void ImportCoarseGridMrhsVectors(Lattice<vobj> &vec_mrhs, deviceVector<scalar> &blas)
|
||||||
{
|
{
|
||||||
typedef typename vobj::scalar_object coarse_scalar_object;
|
typedef typename vobj::scalar_object coarse_scalar_object;
|
||||||
|
typedef typename vobj::scalar_type coarse_scalar; // may differ in precision from the BLAS scalar
|
||||||
|
|
||||||
GridBase *coarse_mrhs_grid = vec_mrhs.Grid();
|
GridBase *coarse_mrhs_grid = vec_mrhs.Grid();
|
||||||
int _ndimension = coarse_grid->_ndimension;
|
int _ndimension = coarse_grid->_ndimension;
|
||||||
@@ -508,7 +566,7 @@ public:
|
|||||||
int64_t osites_hi = coarse_mrhs_grid->oSites();
|
int64_t osites_hi = coarse_mrhs_grid->oSites();
|
||||||
|
|
||||||
const int Nsimd = vobj::Nsimd();
|
const int Nsimd = vobj::Nsimd();
|
||||||
uint64_t cwords=sizeof(typename vobj::scalar_object)/sizeof(scalar);
|
uint64_t cwords=sizeof(coarse_scalar_object)/sizeof(coarse_scalar);
|
||||||
GRID_ASSERT(cwords==nbasis);
|
GRID_ASSERT(cwords==nbasis);
|
||||||
int64_t lnvec = nvec;
|
int64_t lnvec = nvec;
|
||||||
|
|
||||||
@@ -536,8 +594,8 @@ public:
|
|||||||
coarse_scalar_object data = extractLane(lane,coarseData[scr]);
|
coarse_scalar_object data = extractLane(lane,coarseData[scr]);
|
||||||
|
|
||||||
int64_t blas_site = (sc*lnvec + v)*cwords;
|
int64_t blas_site = (sc*lnvec + v)*cwords;
|
||||||
coarse_scalar_object * ptr = (coarse_scalar_object *)&blasData_p[blas_site];
|
const coarse_scalar *dp = (const coarse_scalar *)&data;
|
||||||
*ptr = data;
|
for(uint64_t b=0;b<cwords;b++) blasData_p[blas_site+b] = scalar(dp[b]);
|
||||||
#ifdef GRID_SIMT
|
#ifdef GRID_SIMT
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@@ -546,21 +604,25 @@ public:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportFineGridVectors(std::vector <Field> &vecs, deviceVector<scalar> &blas)
|
template<class vobj>
|
||||||
|
void ExportFineGridVectors(std::vector <Lattice<vobj> > &vecs, deviceVector<scalar> &blas)
|
||||||
{
|
{
|
||||||
GRID_TRACE("ExportFineGridVectors");
|
GRID_TRACE("ExportFineGridVectors");
|
||||||
typedef typename Field::vector_object vobj;
|
typedef typename vobj::scalar_object fine_scalar_object;
|
||||||
|
typedef typename vobj::scalar_type fine_scalar;
|
||||||
|
|
||||||
int nvec = vecs.size();
|
int nvec = vecs.size();
|
||||||
|
|
||||||
GRID_ASSERT(vecs[0].Grid()==fine_grid);
|
GridBase *fgrid = vecs[0].Grid();
|
||||||
|
|
||||||
int _ndimension = coarse_grid->_ndimension;
|
int _ndimension = coarse_grid->_ndimension;
|
||||||
|
// Full LOCAL dimensions must agree; the SIMD layout and precision need not
|
||||||
|
for(int d=0;d<_ndimension;d++) GRID_ASSERT(fgrid->_ldimensions[d] == fine_ldimensions[d]);
|
||||||
|
GRID_ASSERT(sizeof(fine_scalar_object)/sizeof(fine_scalar) == words);
|
||||||
|
|
||||||
Coordinate fine_rdimensions = fine_grid->_rdimensions;
|
Coordinate fine_rdimensions = fgrid->_rdimensions;
|
||||||
Coordinate coarse_l = coarse_ldimensions;
|
Coordinate coarse_l = coarse_ldimensions;
|
||||||
Coordinate block_l = block_ldimensions;
|
Coordinate block_l = block_ldimensions;
|
||||||
Coordinate fsimd = fine_simd;
|
Coordinate fsimd = fgrid->_simd_layout;
|
||||||
|
|
||||||
// std::cout << " export fine Blas norm "<<blasNorm2(blas)<<std::endl;
|
// std::cout << " export fine Blas norm "<<blasNorm2(blas)<<std::endl;
|
||||||
|
|
||||||
@@ -572,7 +634,7 @@ public:
|
|||||||
auto blasData_p = &blas[0];
|
auto blasData_p = &blas[0];
|
||||||
auto fineData_p = &fineData[0];
|
auto fineData_p = &fineData[0];
|
||||||
|
|
||||||
int64_t osites = fine_grid->oSites();
|
int64_t osites = fgrid->oSites();
|
||||||
uint64_t lwords = words;
|
uint64_t lwords = words;
|
||||||
// std::cout << " Nsimd is "<<vobj::Nsimd() << std::endl;
|
// std::cout << " Nsimd is "<<vobj::Nsimd() << std::endl;
|
||||||
// std::cout << " lwords is "<<lwords << std::endl;
|
// std::cout << " lwords is "<<lwords << std::endl;
|
||||||
@@ -608,9 +670,10 @@ public:
|
|||||||
int64_t site = (sc*nvec + v)*bv
|
int64_t site = (sc*nvec + v)*bv
|
||||||
+ sb;
|
+ sb;
|
||||||
|
|
||||||
scalar_object * ptr = (scalar_object *)&blasData_p[site*lwords];
|
// element-wise: the store may differ in precision from the field
|
||||||
|
fine_scalar_object data;
|
||||||
scalar_object data = *ptr;
|
fine_scalar *dp = (fine_scalar *)&data;
|
||||||
|
for(uint64_t w=0;w<lwords;w++) dp[w] = fine_scalar(blasData_p[site*lwords+w]);
|
||||||
|
|
||||||
insertLane(lane,fineData[sf],data);
|
insertLane(lane,fineData[sf],data);
|
||||||
#ifdef GRID_SIMT
|
#ifdef GRID_SIMT
|
||||||
@@ -627,6 +690,7 @@ public:
|
|||||||
GRID_TRACE("ImportCoarseGridVectors");
|
GRID_TRACE("ImportCoarseGridVectors");
|
||||||
int nvec = vecs.size();
|
int nvec = vecs.size();
|
||||||
typedef typename vobj::scalar_object coarse_scalar_object;
|
typedef typename vobj::scalar_object coarse_scalar_object;
|
||||||
|
typedef typename vobj::scalar_type coarse_scalar; // may differ in precision from the BLAS scalar
|
||||||
|
|
||||||
// std::cout << " BlockProjector importing "<<nvec<< " coarse grid vectors" <<std::endl;
|
// std::cout << " BlockProjector importing "<<nvec<< " coarse grid vectors" <<std::endl;
|
||||||
|
|
||||||
@@ -652,7 +716,7 @@ public:
|
|||||||
|
|
||||||
// loop over fine sites
|
// loop over fine sites
|
||||||
const int Nsimd = vobj::Nsimd();
|
const int Nsimd = vobj::Nsimd();
|
||||||
uint64_t cwords=sizeof(typename vobj::scalar_object)/sizeof(scalar);
|
uint64_t cwords=sizeof(coarse_scalar_object)/sizeof(coarse_scalar);
|
||||||
GRID_ASSERT(cwords==nbasis);
|
GRID_ASSERT(cwords==nbasis);
|
||||||
|
|
||||||
accelerator_for(sc,osites,Nsimd,{
|
accelerator_for(sc,osites,Nsimd,{
|
||||||
@@ -676,9 +740,8 @@ public:
|
|||||||
|
|
||||||
coarse_scalar_object data = extractLane(lane,coarseData[sc]);
|
coarse_scalar_object data = extractLane(lane,coarseData[sc]);
|
||||||
|
|
||||||
coarse_scalar_object * ptr = (coarse_scalar_object *)&blasData_p[blas_site];
|
const coarse_scalar *dp = (const coarse_scalar *)&data;
|
||||||
|
for(uint64_t b=0;b<cwords;b++) blasData_p[blas_site+b] = scalar(dp[b]);
|
||||||
*ptr = data;
|
|
||||||
#ifdef GRID_SIMT
|
#ifdef GRID_SIMT
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@@ -694,6 +757,7 @@ public:
|
|||||||
GRID_TRACE("ExportCoarseGridVectors");
|
GRID_TRACE("ExportCoarseGridVectors");
|
||||||
int nvec = vecs.size();
|
int nvec = vecs.size();
|
||||||
typedef typename vobj::scalar_object coarse_scalar_object;
|
typedef typename vobj::scalar_object coarse_scalar_object;
|
||||||
|
typedef typename vobj::scalar_type coarse_scalar; // may differ in precision from the BLAS scalar
|
||||||
// std::cout << GridLogMessage<<" BlockProjector exporting "<<nvec<< " coarse grid vectors" <<std::endl;
|
// std::cout << GridLogMessage<<" BlockProjector exporting "<<nvec<< " coarse grid vectors" <<std::endl;
|
||||||
|
|
||||||
GRID_ASSERT(vecs[0].Grid()==coarse_grid);
|
GRID_ASSERT(vecs[0].Grid()==coarse_grid);
|
||||||
@@ -719,7 +783,7 @@ public:
|
|||||||
|
|
||||||
// loop over fine sites
|
// loop over fine sites
|
||||||
const int Nsimd = vobj::Nsimd();
|
const int Nsimd = vobj::Nsimd();
|
||||||
uint64_t cwords=sizeof(typename vobj::scalar_object)/sizeof(scalar);
|
uint64_t cwords=sizeof(coarse_scalar_object)/sizeof(coarse_scalar);
|
||||||
GRID_ASSERT(cwords==nbasis);
|
GRID_ASSERT(cwords==nbasis);
|
||||||
|
|
||||||
accelerator_for(sc,osites,Nsimd,{
|
accelerator_for(sc,osites,Nsimd,{
|
||||||
@@ -740,8 +804,9 @@ public:
|
|||||||
Lexicographic::IndexFromCoor(coor_c,scl,coarse_l);
|
Lexicographic::IndexFromCoor(coor_c,scl,coarse_l);
|
||||||
|
|
||||||
int64_t blas_site = (scl*nvec + v)*cwords;
|
int64_t blas_site = (scl*nvec + v)*cwords;
|
||||||
coarse_scalar_object * ptr = (coarse_scalar_object *)&blasData_p[blas_site];
|
coarse_scalar_object data;
|
||||||
coarse_scalar_object data = *ptr;
|
coarse_scalar *dp = (coarse_scalar *)&data;
|
||||||
|
for(uint64_t b=0;b<cwords;b++) dp[b] = coarse_scalar(blasData_p[blas_site+b]);
|
||||||
insertLane(lane,coarseData[sc],data);
|
insertLane(lane,coarseData[sc],data);
|
||||||
#ifdef GRID_SIMT
|
#ifdef GRID_SIMT
|
||||||
}
|
}
|
||||||
@@ -751,18 +816,19 @@ public:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void ImportBasis(std::vector < Field > &vecs)
|
template<class fobj>
|
||||||
|
void ImportBasis(std::vector < Lattice<fobj> > &vecs)
|
||||||
{
|
{
|
||||||
// std::cout << " BlockProjector Import basis size "<<vecs.size()<<std::endl;
|
// std::cout << " BlockProjector Import basis size "<<vecs.size()<<std::endl;
|
||||||
ImportFineGridVectors(vecs,BLAS_V);
|
ImportFineGridVectors(vecs,BLAS_V);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class cobj>
|
template<class fobj,class cobj>
|
||||||
void blockProject(std::vector<Field> &fine,std::vector< Lattice<cobj> > & coarse)
|
void blockProject(std::vector<Lattice<fobj> > &fine,std::vector< Lattice<cobj> > & coarse)
|
||||||
{
|
{
|
||||||
GRID_TRACE("BlockProject");
|
GRID_TRACE("BlockProject");
|
||||||
int nrhs=fine.size();
|
int nrhs=fine.size();
|
||||||
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
|
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(typename cobj::scalar_type);
|
||||||
// std::cout << "blockProject nbasis " <<nbasis<<" " << _nbasis<<std::endl;
|
// std::cout << "blockProject nbasis " <<nbasis<<" " << _nbasis<<std::endl;
|
||||||
GRID_ASSERT(nbasis==_nbasis);
|
GRID_ASSERT(nbasis==_nbasis);
|
||||||
|
|
||||||
@@ -817,12 +883,12 @@ public:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class cobj>
|
template<class fobj,class cobj>
|
||||||
void blockPromote(std::vector<Field> &fine,std::vector<Lattice<cobj> > & coarse)
|
void blockPromote(std::vector<Lattice<fobj> > &fine,std::vector<Lattice<cobj> > & coarse)
|
||||||
{
|
{
|
||||||
GRID_TRACE("BlockPromote");
|
GRID_TRACE("BlockPromote");
|
||||||
int nrhs=fine.size();
|
int nrhs=fine.size();
|
||||||
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
|
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(typename cobj::scalar_type);
|
||||||
GRID_ASSERT(nbasis==_nbasis);
|
GRID_ASSERT(nbasis==_nbasis);
|
||||||
|
|
||||||
BLAS_F.resize (fine_vol * words * nrhs );
|
BLAS_F.resize (fine_vol * words * nrhs );
|
||||||
@@ -876,12 +942,12 @@ public:
|
|||||||
// multiRHS ordered interfaces. The GEMM is identical; only the import and
|
// multiRHS ordered interfaces. The GEMM is identical; only the import and
|
||||||
// export differ, so those are the whole of the layout question.
|
// export differ, so those are the whole of the layout question.
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
template<class cobj>
|
template<class fobj,class cobj>
|
||||||
void blockProject(Field &fine_mrhs,Lattice<cobj> &coarse_mrhs)
|
void blockProject(Lattice<fobj> &fine_mrhs,Lattice<cobj> &coarse_mrhs)
|
||||||
{
|
{
|
||||||
GRID_TRACE("BlockProjectMrhs");
|
GRID_TRACE("BlockProjectMrhs");
|
||||||
int nrhs = fine_mrhs.Grid()->_rdimensions[0];
|
int nrhs = fine_mrhs.Grid()->_rdimensions[0];
|
||||||
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
|
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(typename cobj::scalar_type);
|
||||||
GRID_ASSERT(nbasis==_nbasis);
|
GRID_ASSERT(nbasis==_nbasis);
|
||||||
GRID_ASSERT(coarse_mrhs.Grid()->_rdimensions[0]==nrhs);
|
GRID_ASSERT(coarse_mrhs.Grid()->_rdimensions[0]==nrhs);
|
||||||
|
|
||||||
@@ -893,12 +959,12 @@ public:
|
|||||||
ExportCoarseGridMrhsVectors(coarse_mrhs,BLAS_C);
|
ExportCoarseGridMrhsVectors(coarse_mrhs,BLAS_C);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class cobj>
|
template<class fobj,class cobj>
|
||||||
void blockPromote(Field &fine_mrhs,Lattice<cobj> &coarse_mrhs)
|
void blockPromote(Lattice<fobj> &fine_mrhs,Lattice<cobj> &coarse_mrhs)
|
||||||
{
|
{
|
||||||
GRID_TRACE("BlockPromoteMrhs");
|
GRID_TRACE("BlockPromoteMrhs");
|
||||||
int nrhs = fine_mrhs.Grid()->_rdimensions[0];
|
int nrhs = fine_mrhs.Grid()->_rdimensions[0];
|
||||||
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
|
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(typename cobj::scalar_type);
|
||||||
GRID_ASSERT(nbasis==_nbasis);
|
GRID_ASSERT(nbasis==_nbasis);
|
||||||
GRID_ASSERT(coarse_mrhs.Grid()->_rdimensions[0]==nrhs);
|
GRID_ASSERT(coarse_mrhs.Grid()->_rdimensions[0]==nrhs);
|
||||||
|
|
||||||
@@ -914,12 +980,12 @@ public:
|
|||||||
// Mixed orderings. A single RHS fine operator produces a vector of fine
|
// Mixed orderings. A single RHS fine operator produces a vector of fine
|
||||||
// fields with no packing; the coarse side is still wanted in mrhs order.
|
// fields with no packing; the coarse side is still wanted in mrhs order.
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
template<class cobj>
|
template<class fobj,class cobj>
|
||||||
void blockProject(std::vector<Field> &fine,Lattice<cobj> &coarse_mrhs)
|
void blockProject(std::vector<Lattice<fobj> > &fine,Lattice<cobj> &coarse_mrhs)
|
||||||
{
|
{
|
||||||
GRID_TRACE("BlockProjectMixed");
|
GRID_TRACE("BlockProjectMixed");
|
||||||
int nrhs = fine.size();
|
int nrhs = fine.size();
|
||||||
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
|
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(typename cobj::scalar_type);
|
||||||
GRID_ASSERT(nbasis==_nbasis);
|
GRID_ASSERT(nbasis==_nbasis);
|
||||||
GRID_ASSERT(coarse_mrhs.Grid()->_rdimensions[0]==nrhs);
|
GRID_ASSERT(coarse_mrhs.Grid()->_rdimensions[0]==nrhs);
|
||||||
|
|
||||||
@@ -931,12 +997,12 @@ public:
|
|||||||
ExportCoarseGridMrhsVectors(coarse_mrhs,BLAS_C);
|
ExportCoarseGridMrhsVectors(coarse_mrhs,BLAS_C);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class cobj>
|
template<class fobj,class cobj>
|
||||||
void blockProject(Field &fine_mrhs,std::vector< Lattice<cobj> > &coarse)
|
void blockProject(Lattice<fobj> &fine_mrhs,std::vector< Lattice<cobj> > &coarse)
|
||||||
{
|
{
|
||||||
GRID_TRACE("BlockProjectMixed");
|
GRID_TRACE("BlockProjectMixed");
|
||||||
int nrhs = fine_mrhs.Grid()->_rdimensions[0];
|
int nrhs = fine_mrhs.Grid()->_rdimensions[0];
|
||||||
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
|
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(typename cobj::scalar_type);
|
||||||
GRID_ASSERT(nbasis==_nbasis);
|
GRID_ASSERT(nbasis==_nbasis);
|
||||||
GRID_ASSERT(coarse.size()==nrhs);
|
GRID_ASSERT(coarse.size()==nrhs);
|
||||||
|
|
||||||
@@ -948,12 +1014,12 @@ public:
|
|||||||
ExportCoarseGridVectors(coarse,BLAS_C);
|
ExportCoarseGridVectors(coarse,BLAS_C);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class cobj>
|
template<class fobj,class cobj>
|
||||||
void blockPromote(std::vector<Field> &fine,Lattice<cobj> &coarse_mrhs)
|
void blockPromote(std::vector<Lattice<fobj> > &fine,Lattice<cobj> &coarse_mrhs)
|
||||||
{
|
{
|
||||||
GRID_TRACE("BlockPromoteMixed");
|
GRID_TRACE("BlockPromoteMixed");
|
||||||
int nrhs = fine.size();
|
int nrhs = fine.size();
|
||||||
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
|
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(typename cobj::scalar_type);
|
||||||
GRID_ASSERT(nbasis==_nbasis);
|
GRID_ASSERT(nbasis==_nbasis);
|
||||||
GRID_ASSERT(coarse_mrhs.Grid()->_rdimensions[0]==nrhs);
|
GRID_ASSERT(coarse_mrhs.Grid()->_rdimensions[0]==nrhs);
|
||||||
|
|
||||||
@@ -965,12 +1031,12 @@ public:
|
|||||||
ExportFineGridVectors(fine,BLAS_F);
|
ExportFineGridVectors(fine,BLAS_F);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class cobj>
|
template<class fobj,class cobj>
|
||||||
void blockPromote(Field &fine_mrhs,std::vector< Lattice<cobj> > &coarse)
|
void blockPromote(Lattice<fobj> &fine_mrhs,std::vector< Lattice<cobj> > &coarse)
|
||||||
{
|
{
|
||||||
GRID_TRACE("BlockPromoteMixed");
|
GRID_TRACE("BlockPromoteMixed");
|
||||||
int nrhs = fine_mrhs.Grid()->_rdimensions[0];
|
int nrhs = fine_mrhs.Grid()->_rdimensions[0];
|
||||||
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
|
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(typename cobj::scalar_type);
|
||||||
GRID_ASSERT(nbasis==_nbasis);
|
GRID_ASSERT(nbasis==_nbasis);
|
||||||
GRID_ASSERT(coarse.size()==nrhs);
|
GRID_ASSERT(coarse.size()==nrhs);
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ public:
|
|||||||
|
|
||||||
typedef typename Field::scalar_type scalar;
|
typedef typename Field::scalar_type scalar;
|
||||||
typedef typename Field::scalar_object scalar_object;
|
typedef typename Field::scalar_object scalar_object;
|
||||||
|
typedef typename Field::vector_object vobj;
|
||||||
|
|
||||||
int nev;
|
int nev;
|
||||||
std::vector<RealD> eval;
|
std::vector<RealD> eval;
|
||||||
@@ -80,10 +81,18 @@ public:
|
|||||||
grid=nullptr;
|
grid=nullptr;
|
||||||
vol=0;
|
vol=0;
|
||||||
words=0;
|
words=0;
|
||||||
BLAS_E.resize(0);
|
// deviceVector is a std::vector with a device allocator: resize(0) drops
|
||||||
BLAS_R.resize(0);
|
// the size and keeps the capacity, returning no device memory. Swapping
|
||||||
BLAS_C.resize(0);
|
// with an empty vector destroys the buffer.
|
||||||
BLAS_G.resize(0);
|
deviceVector<scalar>().swap(BLAS_E);
|
||||||
|
deviceVector<scalar>().swap(BLAS_R);
|
||||||
|
deviceVector<scalar>().swap(BLAS_C);
|
||||||
|
deviceVector<scalar>().swap(BLAS_G);
|
||||||
|
}
|
||||||
|
// Resident (non-evictable) device memory held by this deflator.
|
||||||
|
uint64_t DeviceBytes(void)
|
||||||
|
{
|
||||||
|
return (BLAS_E.capacity()+BLAS_R.capacity()+BLAS_C.capacity()+BLAS_G.capacity())*sizeof(scalar);
|
||||||
}
|
}
|
||||||
void Allocate(int _nev,GridBase *_grid)
|
void Allocate(int _nev,GridBase *_grid)
|
||||||
{
|
{
|
||||||
@@ -123,31 +132,98 @@ public:
|
|||||||
ImportEigenVector(evec[_ev0+e],_eval[_ev0+e],e);
|
ImportEigenVector(evec[_ev0+e],_eval[_ev0+e],e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
// Sources as a vector of D-dimensional fields: each is one contiguous
|
||||||
|
// column of BLAS_R already, so import/export are straight copies.
|
||||||
|
// The eigenvectors may live on a D grid or on the Nrhs=1 D+1 grid of the
|
||||||
|
// same local volume; the two are the same bytes, so only the site count
|
||||||
|
// is checked, not grid identity.
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
void DeflateSources(std::vector<Field> &source,std::vector<Field> & guess)
|
void DeflateSources(std::vector<Field> &source,std::vector<Field> & guess)
|
||||||
{
|
{
|
||||||
int nrhs = source.size();
|
int nrhs = source.size();
|
||||||
GRID_ASSERT(source.size()==guess.size());
|
GRID_ASSERT(source.size()==guess.size());
|
||||||
GRID_ASSERT(grid == guess[0].Grid());
|
GRID_ASSERT(grid->lSites() == guess[0].Grid()->lSites());
|
||||||
conformable(guess[0],source[0]);
|
conformable(guess[0],source[0]);
|
||||||
|
|
||||||
int64_t vw = vol * words;
|
int64_t vw = vol * words;
|
||||||
|
|
||||||
RealD t0 = usecond();
|
|
||||||
BLAS_R.resize(nrhs * vw); // cost free if size doesn't change
|
BLAS_R.resize(nrhs * vw); // cost free if size doesn't change
|
||||||
BLAS_G.resize(nrhs * vw); // cost free if size doesn't change
|
BLAS_G.resize(nrhs * vw); // cost free if size doesn't change
|
||||||
BLAS_C.resize(nev * nrhs);// cost free if size doesn't change
|
BLAS_C.resize(nev * nrhs);// cost free if size doesn't change
|
||||||
|
|
||||||
/////////////////////////////////////////////
|
|
||||||
// Copy in the multi-rhs sources
|
|
||||||
/////////////////////////////////////////////
|
|
||||||
// for(int r=0;r<nrhs;r++){
|
|
||||||
// std::cout << " source["<<r<<"] = "<<norm2(source[r])<<std::endl;
|
|
||||||
// }
|
|
||||||
for(int r=0;r<nrhs;r++){
|
for(int r=0;r<nrhs;r++){
|
||||||
int64_t offset = r*vw;
|
int64_t offset = r*vw;
|
||||||
autoView(v,source[r],AcceleratorRead);
|
autoView(v,source[r],AcceleratorRead);
|
||||||
acceleratorCopyDeviceToDevice(&v[0],&BLAS_R[offset],sizeof(scalar_object)*vol);
|
acceleratorCopyDeviceToDevice(&v[0],&BLAS_R[offset],sizeof(scalar_object)*vol);
|
||||||
}
|
}
|
||||||
|
DeflateBLAS(nrhs);
|
||||||
|
for(int r=0;r<nrhs;r++){
|
||||||
|
int64_t offset = r*vw;
|
||||||
|
autoView(v,guess[r],AcceleratorWrite);
|
||||||
|
acceleratorCopyDeviceToDevice(&BLAS_G[offset],&v[0],sizeof(scalar_object)*vol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
// Sources as ONE D+1 dimensional multiRHS field, rhs innermost
|
||||||
|
// (dimension 0, undistributed) on an UNVECTORISED coarse space, so the
|
||||||
|
// site index is rhs + nrhs*x with x the D-dimensional site. The GEMM
|
||||||
|
// wants x contiguous per rhs, so the import is a permutation, one fused
|
||||||
|
// pass over the field; likewise the export. No slices.
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
void DeflateSources(const Field &source_mrhs, Field &guess_mrhs)
|
||||||
|
{
|
||||||
|
conformable(source_mrhs,guess_mrhs);
|
||||||
|
GridBase *hi = source_mrhs.Grid();
|
||||||
|
GRID_ASSERT(hi->_ndimension == grid->_ndimension+1);
|
||||||
|
GRID_ASSERT(hi->_processors[0] == 1);
|
||||||
|
GRID_ASSERT(hi->_simd_layout[0]== 1);
|
||||||
|
GRID_ASSERT(vobj::Nsimd() == 1);
|
||||||
|
int nrhs = hi->_fdimensions[0];
|
||||||
|
GRID_ASSERT(hi->lSites() == nrhs*vol);
|
||||||
|
|
||||||
|
int64_t vw = vol * words;
|
||||||
|
BLAS_R.resize(nrhs * vw);
|
||||||
|
BLAS_G.resize(nrhs * vw);
|
||||||
|
BLAS_C.resize(nev * nrhs);
|
||||||
|
|
||||||
|
ImportSourcesMrhs(source_mrhs,nrhs);
|
||||||
|
DeflateBLAS(nrhs);
|
||||||
|
ExportGuessMrhs(guess_mrhs,nrhs);
|
||||||
|
}
|
||||||
|
void ImportSourcesMrhs(const Field &source_mrhs,int nrhs)
|
||||||
|
{
|
||||||
|
const int64_t lwords = words, lnrhs = nrhs, vw = vol*words;
|
||||||
|
autoView(v,source_mrhs,AcceleratorRead);
|
||||||
|
auto vp = &v[0];
|
||||||
|
scalar *R = &BLAS_R[0];
|
||||||
|
accelerator_for(scr, lnrhs*vol, 1, {
|
||||||
|
int64_t r = scr % lnrhs;
|
||||||
|
int64_t x = scr / lnrhs;
|
||||||
|
const scalar *s = (const scalar *)&vp[scr];
|
||||||
|
for(int64_t w=0;w<lwords;w++) R[r*vw + x*lwords + w] = s[w];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
void ExportGuessMrhs(Field &guess_mrhs,int nrhs)
|
||||||
|
{
|
||||||
|
const int64_t lwords = words, lnrhs = nrhs, vw = vol*words;
|
||||||
|
autoView(v,guess_mrhs,AcceleratorWrite);
|
||||||
|
auto vp = &v[0];
|
||||||
|
scalar *G = &BLAS_G[0];
|
||||||
|
accelerator_for(scr, lnrhs*vol, 1, {
|
||||||
|
int64_t r = scr % lnrhs;
|
||||||
|
int64_t x = scr / lnrhs;
|
||||||
|
scalar *g = (scalar *)&vp[scr];
|
||||||
|
for(int64_t w=0;w<lwords;w++) g[w] = G[r*vw + x*lwords + w];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
// BLAS_R (nrhs columns of vol*words) -> BLAS_G, through the imported
|
||||||
|
// eigenbasis: C = E^dag R / lambda, G = E C.
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
void DeflateBLAS(int nrhs)
|
||||||
|
{
|
||||||
|
int64_t vw = vol * words;
|
||||||
|
RealD t0 = usecond();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* in Fortran column major notation (cuBlas order)
|
* in Fortran column major notation (cuBlas order)
|
||||||
@@ -195,7 +271,7 @@ public:
|
|||||||
acceleratorCopyFromDevice(&BLAS_C[0],&HOST_C[0],BLAS_C.size()*sizeof(scalar));
|
acceleratorCopyFromDevice(&BLAS_C[0],&HOST_C[0],BLAS_C.size()*sizeof(scalar));
|
||||||
grid->GlobalSumVector(&HOST_C[0],nev*nrhs);
|
grid->GlobalSumVector(&HOST_C[0],nev*nrhs);
|
||||||
for(int e=0;e<nev;e++){
|
for(int e=0;e<nev;e++){
|
||||||
RealD lam(1.0/eval[e]);
|
scalar lam(1.0/eval[e]); // in the coarse scalar: fp32 coarse spaces too
|
||||||
for(int r=0;r<nrhs;r++){
|
for(int r=0;r<nrhs;r++){
|
||||||
int off = e+nev*r;
|
int off = e+nev*r;
|
||||||
HOST_C[off]=HOST_C[off] * lam;
|
HOST_C[off]=HOST_C[off] * lam;
|
||||||
@@ -216,15 +292,6 @@ public:
|
|||||||
scalar(0.0),
|
scalar(0.0),
|
||||||
Gd);
|
Gd);
|
||||||
BLAS.synchronise();
|
BLAS.synchronise();
|
||||||
|
|
||||||
///////////////////////////////////////
|
|
||||||
// Copy out the multirhs
|
|
||||||
///////////////////////////////////////
|
|
||||||
for(int r=0;r<nrhs;r++){
|
|
||||||
int64_t offset = r*vw;
|
|
||||||
autoView(v,guess[r],AcceleratorWrite);
|
|
||||||
acceleratorCopyDeviceToDevice(&BLAS_G[offset],&v[0],sizeof(scalar_object)*vol);
|
|
||||||
}
|
|
||||||
RealD t1 = usecond();
|
RealD t1 = usecond();
|
||||||
std::cout << GridLogMessage << "MultiRHSDeflation for "<<nrhs<<" sources with "<<nev<<" eigenvectors took " << (t1-t0)/1e3 <<" ms"<<std::endl;
|
std::cout << GridLogMessage << "MultiRHSDeflation for "<<nrhs<<" sources with "<<nev<<" eigenvectors took " << (t1-t0)/1e3 <<" ms"<<std::endl;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -480,104 +480,6 @@ class TwoLevelCG : public LinearFunction<Field>
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class Field, class CoarseField, class Aggregation>
|
|
||||||
class TwoLevelADEF2 : public TwoLevelCG<Field>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Need something that knows how to get from Coarse to fine and back again
|
|
||||||
// void ProjectToSubspace(CoarseVector &CoarseVec,const FineField &FineVec){
|
|
||||||
// void PromoteFromSubspace(const CoarseVector &CoarseVec,FineField &FineVec){
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////
|
|
||||||
GridBase *coarsegrid;
|
|
||||||
Aggregation &_Aggregates;
|
|
||||||
LinearFunction<CoarseField> &_CoarseSolver;
|
|
||||||
LinearFunction<CoarseField> &_CoarseSolverPrecise;
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// more most opertor functions
|
|
||||||
TwoLevelADEF2(RealD tol,
|
|
||||||
Integer maxit,
|
|
||||||
LinearOperatorBase<Field> &FineLinop,
|
|
||||||
LinearFunction<Field> &Smoother,
|
|
||||||
LinearFunction<CoarseField> &CoarseSolver,
|
|
||||||
LinearFunction<CoarseField> &CoarseSolverPrecise,
|
|
||||||
Aggregation &Aggregates
|
|
||||||
) :
|
|
||||||
TwoLevelCG<Field>(tol,maxit,FineLinop,Smoother,Aggregates.FineGrid),
|
|
||||||
_CoarseSolver(CoarseSolver),
|
|
||||||
_CoarseSolverPrecise(CoarseSolverPrecise),
|
|
||||||
_Aggregates(Aggregates)
|
|
||||||
{
|
|
||||||
coarsegrid = Aggregates.CoarseGrid;
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual void PcgM1(Field & in, Field & out)
|
|
||||||
{
|
|
||||||
GRID_TRACE("MultiGridPreconditioner ");
|
|
||||||
// [PTM+Q] in = [1 - Q A] M in + Q in = Min + Q [ in -A Min]
|
|
||||||
|
|
||||||
Field tmp(this->grid);
|
|
||||||
Field Min(this->grid);
|
|
||||||
CoarseField PleftProj(this->coarsegrid);
|
|
||||||
CoarseField PleftMss_proj(this->coarsegrid);
|
|
||||||
|
|
||||||
this->SmoothTimer.Start();
|
|
||||||
this->_Smoother(in,Min);
|
|
||||||
this->SmoothTimer.Stop();
|
|
||||||
this->SmoothCalls++;
|
|
||||||
|
|
||||||
this->MatrixTimer.Start();
|
|
||||||
this->_FineLinop.HermOp(Min,out);
|
|
||||||
this->MatrixTimer.Stop();
|
|
||||||
this->MatrixCalls++;
|
|
||||||
axpy(tmp,-1.0,out,in); // tmp = in - A Min
|
|
||||||
|
|
||||||
this->ProjectTimer.Start();
|
|
||||||
this->_Aggregates.ProjectToSubspace(PleftProj,tmp);
|
|
||||||
this->ProjectTimer.Stop();
|
|
||||||
this->ProjectCalls++;
|
|
||||||
this->CoarseTimer.Start();
|
|
||||||
this->_CoarseSolver(PleftProj,PleftMss_proj); // Ass^{-1} [in - A Min]_s
|
|
||||||
this->CoarseTimer.Stop();
|
|
||||||
this->CoarseCalls++;
|
|
||||||
this->PromoteTimer.Start();
|
|
||||||
this->_Aggregates.PromoteFromSubspace(PleftMss_proj,tmp);// tmp = Q[in - A Min]
|
|
||||||
this->PromoteTimer.Stop();
|
|
||||||
this->PromoteCalls++;
|
|
||||||
|
|
||||||
axpy(out,1.0,Min,tmp); // Min+tmp
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Vstart(Field & x,const Field & src)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg Vstart "<<std::endl;
|
|
||||||
///////////////////////////////////
|
|
||||||
// Choose x_0 such that
|
|
||||||
// x_0 = guess + (A_ss^inv) r_s = guess + Ass_inv [src -Aguess]
|
|
||||||
// = [1 - Ass_inv A] Guess + Assinv src
|
|
||||||
// = P^T guess + Assinv src
|
|
||||||
// = Vstart [Tang notation]
|
|
||||||
// This gives:
|
|
||||||
// W^T (src - A x_0) = src_s - A guess_s - r_s
|
|
||||||
// = src_s - (A guess)_s - src_s + (A guess)_s
|
|
||||||
// = 0
|
|
||||||
///////////////////////////////////
|
|
||||||
Field r(this->grid);
|
|
||||||
Field mmp(this->grid);
|
|
||||||
CoarseField PleftProj(this->coarsegrid);
|
|
||||||
CoarseField PleftMss_proj(this->coarsegrid);
|
|
||||||
|
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg Vstart projecting "<<std::endl;
|
|
||||||
this->_Aggregates.ProjectToSubspace(PleftProj,src);
|
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg Vstart coarse solve "<<std::endl;
|
|
||||||
this->_CoarseSolverPrecise(PleftProj,PleftMss_proj); // Ass^{-1} r_s
|
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg Vstart promote "<<std::endl;
|
|
||||||
this->_Aggregates.PromoteFromSubspace(PleftMss_proj,x);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template<class Field>
|
template<class Field>
|
||||||
|
|||||||
@@ -27,9 +27,10 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <Grid/algorithms/multigrid/MrhsPreconditioner.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compared to Tang-2009: P=Pleft. P^T = PRight Q=MssInv.
|
* Compared to Tang-2009: P=Pleft. P^T = PRight Q=MssInv.
|
||||||
* Script A = SolverMatrix
|
* Script A = SolverMatrix
|
||||||
* Script P = Preconditioner
|
* Script P = Preconditioner
|
||||||
*
|
*
|
||||||
@@ -42,58 +43,64 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
NAMESPACE_BEGIN(Grid);
|
NAMESPACE_BEGIN(Grid);
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Two-level CG family on a vector of right-hand sides, with the
|
||||||
|
// preconditioner (M1 and Vstart) as an MrhsPreconditioner object:
|
||||||
|
// fPcg flexible PCG, one Krylov per rhs, shared M1 call
|
||||||
|
// PrecBlockCGrQ preconditioned BlockCGrQ (arXiv:2409.03904 s2.2);
|
||||||
|
// assumes a stationary preconditioner; not the
|
||||||
|
// production choice (its linalg grows as nrhs^2)
|
||||||
|
// Also a LinearFunction<Field>: one rhs is the nrhs=1 case.
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
enum class MrhsCGAlgorithm { fPcg, PrecBlockCGrQ };
|
||||||
|
|
||||||
template<class Field>
|
template<class Field>
|
||||||
class TwoLevelCGmrhs
|
class TwoLevelCGmrhs : public LinearFunction<Field>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
using LinearFunction<Field>::operator();
|
||||||
RealD Tolerance;
|
RealD Tolerance;
|
||||||
Integer MaxIterations;
|
Integer MaxIterations;
|
||||||
GridBase *grid;
|
GridBase *grid;
|
||||||
|
MrhsCGAlgorithm Algorithm;
|
||||||
|
|
||||||
// Fine operator, Smoother, CoarseSolver
|
LinearOperatorBase<Field> &_FineLinop;
|
||||||
LinearOperatorBase<Field> &_FineLinop;
|
MrhsPreconditioner<Field> &_Precon;
|
||||||
LinearFunction<Field> &_Smoother;
|
MultiRHSBlockCGLinalg<Field> _BlockCGLinalg;
|
||||||
MultiRHSBlockCGLinalg<Field> _BlockCGLinalg;
|
|
||||||
|
|
||||||
GridStopWatch ProjectTimer;
|
|
||||||
GridStopWatch PromoteTimer;
|
|
||||||
GridStopWatch DeflateTimer;
|
|
||||||
GridStopWatch CoarseTimer;
|
|
||||||
GridStopWatch FineTimer;
|
|
||||||
GridStopWatch SmoothTimer;
|
|
||||||
GridStopWatch InsertTimer;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Field rrr;
|
|
||||||
Field sss;
|
|
||||||
Field qqq;
|
|
||||||
Field zzz;
|
|
||||||
*/
|
|
||||||
// more most opertor functions
|
|
||||||
TwoLevelCGmrhs(RealD tol,
|
TwoLevelCGmrhs(RealD tol,
|
||||||
Integer maxit,
|
Integer maxit,
|
||||||
LinearOperatorBase<Field> &FineLinop,
|
LinearOperatorBase<Field> &FineLinop,
|
||||||
LinearFunction<Field> &Smoother,
|
MrhsPreconditioner<Field> &Precon,
|
||||||
GridBase *fine) :
|
GridBase *fine,
|
||||||
Tolerance(tol),
|
MrhsCGAlgorithm alg = MrhsCGAlgorithm::fPcg) :
|
||||||
|
Tolerance(tol),
|
||||||
MaxIterations(maxit),
|
MaxIterations(maxit),
|
||||||
|
Algorithm(alg),
|
||||||
_FineLinop(FineLinop),
|
_FineLinop(FineLinop),
|
||||||
_Smoother(Smoother)
|
_Precon(Precon)
|
||||||
/*
|
|
||||||
rrr(fine),
|
|
||||||
sss(fine),
|
|
||||||
qqq(fine),
|
|
||||||
zzz(fine)
|
|
||||||
*/
|
|
||||||
{
|
{
|
||||||
grid = fine;
|
grid = fine;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Vector case
|
// mrhs entry point
|
||||||
virtual void operator() (std::vector<Field> &src, std::vector<Field> &x)
|
virtual void operator() (std::vector<Field> &src, std::vector<Field> &x)
|
||||||
{
|
{
|
||||||
SolveSingleSystem(src,x);
|
if ( Algorithm == MrhsCGAlgorithm::PrecBlockCGrQ ) SolvePrecBlockCG(src,x);
|
||||||
// SolvePrecBlockCG(src,x);
|
else SolveSingleSystem(src,x);
|
||||||
|
}
|
||||||
|
// LinearFunction entry points
|
||||||
|
virtual void operator() (const Field &src, Field &x)
|
||||||
|
{
|
||||||
|
std::vector<Field> S(1,src);
|
||||||
|
std::vector<Field> X(1,x);
|
||||||
|
(*this)(S,X);
|
||||||
|
x = X[0];
|
||||||
|
}
|
||||||
|
virtual void operator() (const std::vector<Field> &src, std::vector<Field> &x)
|
||||||
|
{
|
||||||
|
std::vector<Field> S(src);
|
||||||
|
(*this)(S,x);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -220,7 +227,7 @@ class TwoLevelCGmrhs
|
|||||||
//////////////////////////
|
//////////////////////////
|
||||||
// x0 = Vstart -- possibly modify guess
|
// x0 = Vstart -- possibly modify guess
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
Vstart(X,src);
|
_Precon.Vstart(X,src);
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
// R = B-AX
|
// R = B-AX
|
||||||
@@ -234,7 +241,7 @@ class TwoLevelCGmrhs
|
|||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
// Compute MZ = M1 Z = M1 B - M1 A x0
|
// Compute MZ = M1 Z = M1 B - M1 A x0
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
PcgM1(Z,MZ);
|
_Precon(Z,MZ);
|
||||||
|
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
// QC = Z
|
// QC = Z
|
||||||
@@ -248,13 +255,7 @@ class TwoLevelCGmrhs
|
|||||||
|
|
||||||
std::cout << GridLogMessage<<"PrecBlockCGrQ vec computed initial residual and QR fact " <<std::endl;
|
std::cout << GridLogMessage<<"PrecBlockCGrQ vec computed initial residual and QR fact " <<std::endl;
|
||||||
|
|
||||||
ProjectTimer.Reset();
|
_Precon.ResetTimers();
|
||||||
PromoteTimer.Reset();
|
|
||||||
DeflateTimer.Reset();
|
|
||||||
CoarseTimer.Reset();
|
|
||||||
SmoothTimer.Reset();
|
|
||||||
FineTimer.Reset();
|
|
||||||
InsertTimer.Reset();
|
|
||||||
|
|
||||||
GridStopWatch M1Timer;
|
GridStopWatch M1Timer;
|
||||||
GridStopWatch M2Timer;
|
GridStopWatch M2Timer;
|
||||||
@@ -278,10 +279,9 @@ class TwoLevelCGmrhs
|
|||||||
// MZ = M1 Z <==== the Multigrid preconditioner
|
// MZ = M1 Z <==== the Multigrid preconditioner
|
||||||
////////////////////
|
////////////////////
|
||||||
M1Timer.Start();
|
M1Timer.Start();
|
||||||
PcgM1(Z,MZ);
|
_Precon(Z,MZ);
|
||||||
M1Timer.Stop();
|
M1Timer.Stop();
|
||||||
|
|
||||||
FineTimer.Start();
|
|
||||||
////////////////////
|
////////////////////
|
||||||
// M = [D^dag Z]^{-1} = (<Ddag MZ>_M)^{-1} inner prod, generalising Saad derivation of Precon CG
|
// M = [D^dag Z]^{-1} = (<Ddag MZ>_M)^{-1} inner prod, generalising Saad derivation of Precon CG
|
||||||
////////////////////
|
////////////////////
|
||||||
@@ -326,7 +326,6 @@ class TwoLevelCGmrhs
|
|||||||
////////////////////////////
|
////////////////////////////
|
||||||
m_rr = m_C.adjoint() * m_C;
|
m_rr = m_C.adjoint() * m_C;
|
||||||
|
|
||||||
FineTimer.Stop();
|
|
||||||
|
|
||||||
RealD max_resid=0;
|
RealD max_resid=0;
|
||||||
RealD rrsum=0;
|
RealD rrsum=0;
|
||||||
@@ -350,14 +349,7 @@ class TwoLevelCGmrhs
|
|||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Linalg "<<LinalgTimer.Elapsed()<<std::endl;;
|
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Linalg "<<LinalgTimer.Elapsed()<<std::endl;;
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : fine H "<<M3Timer.Elapsed()<<std::endl;;
|
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : fine H "<<M3Timer.Elapsed()<<std::endl;;
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : prec M1 "<<M1Timer.Elapsed()<<std::endl;;
|
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : prec M1 "<<M1Timer.Elapsed()<<std::endl;;
|
||||||
std::cout<<GridLogMessage<<"**** M1 breakdown:"<<std::endl;
|
_Precon.ReportTimers("HDCG: mrhs PrecBlockCGrQ : ");
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Project "<<ProjectTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Promote "<<PromoteTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Deflate "<<DeflateTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Coarse "<<CoarseTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Fine "<<FineTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Smooth "<<SmoothTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Insert "<<InsertTimer.Elapsed()<<std::endl;;
|
|
||||||
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++){
|
for(int rhs=0;rhs<nrhs;rhs++){
|
||||||
|
|
||||||
@@ -423,7 +415,7 @@ class TwoLevelCGmrhs
|
|||||||
//////////////////////////
|
//////////////////////////
|
||||||
// x0 = Vstart -- possibly modify guess
|
// x0 = Vstart -- possibly modify guess
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
Vstart(x,src);
|
_Precon.Vstart(x,src);
|
||||||
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++){
|
for(int rhs=0;rhs<nrhs;rhs++){
|
||||||
// r0 = b -A x0
|
// r0 = b -A x0
|
||||||
@@ -435,7 +427,7 @@ class TwoLevelCGmrhs
|
|||||||
// Compute z = M1 x
|
// Compute z = M1 x
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
// This needs a multiRHS version for acceleration
|
// This needs a multiRHS version for acceleration
|
||||||
PcgM1(r,z);
|
_Precon(r,z);
|
||||||
|
|
||||||
std::vector<RealD> ssq(nrhs);
|
std::vector<RealD> ssq(nrhs);
|
||||||
std::vector<RealD> rsq(nrhs);
|
std::vector<RealD> rsq(nrhs);
|
||||||
@@ -449,13 +441,7 @@ class TwoLevelCGmrhs
|
|||||||
// std::cout << GridLogMessage<<"mrhs HDCG: "<<rhs<<" k=0 residual "<<rtzp[rhs]<<" rsq "<<rsq[rhs]<<"\n";
|
// std::cout << GridLogMessage<<"mrhs HDCG: "<<rhs<<" k=0 residual "<<rtzp[rhs]<<" rsq "<<rsq[rhs]<<"\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectTimer.Reset();
|
_Precon.ResetTimers();
|
||||||
PromoteTimer.Reset();
|
|
||||||
DeflateTimer.Reset();
|
|
||||||
CoarseTimer.Reset();
|
|
||||||
SmoothTimer.Reset();
|
|
||||||
FineTimer.Reset();
|
|
||||||
InsertTimer.Reset();
|
|
||||||
|
|
||||||
GridStopWatch M1Timer;
|
GridStopWatch M1Timer;
|
||||||
GridStopWatch M2Timer;
|
GridStopWatch M2Timer;
|
||||||
@@ -488,7 +474,7 @@ class TwoLevelCGmrhs
|
|||||||
|
|
||||||
// Compute z = M x (for *all* RHS)
|
// Compute z = M x (for *all* RHS)
|
||||||
M1Timer.Start();
|
M1Timer.Start();
|
||||||
PcgM1(r,z);
|
_Precon(r,z);
|
||||||
M1Timer.Stop();
|
M1Timer.Stop();
|
||||||
|
|
||||||
RealD max_rn=0.0;
|
RealD max_rn=0.0;
|
||||||
@@ -530,14 +516,7 @@ class TwoLevelCGmrhs
|
|||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Linalg "<<LinalgTimer.Elapsed()<<std::endl;;
|
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Linalg "<<LinalgTimer.Elapsed()<<std::endl;;
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : fine M3 "<<M3Timer.Elapsed()<<std::endl;;
|
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : fine M3 "<<M3Timer.Elapsed()<<std::endl;;
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : prec M1 "<<M1Timer.Elapsed()<<std::endl;;
|
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : prec M1 "<<M1Timer.Elapsed()<<std::endl;;
|
||||||
std::cout<<GridLogMessage<<"**** M1 breakdown:"<<std::endl;
|
_Precon.ReportTimers("HDCG: mrhs fPcg : ");
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Project "<<ProjectTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Promote "<<PromoteTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Deflate "<<DeflateTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Coarse "<<CoarseTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Fine "<<FineTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Smooth "<<SmoothTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Insert "<<InsertTimer.Elapsed()<<std::endl;;
|
|
||||||
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++){
|
for(int rhs=0;rhs<nrhs;rhs++){
|
||||||
_FineLinop.HermOp(x[rhs],mmp[rhs][0]);
|
_FineLinop.HermOp(x[rhs],mmp[rhs][0]);
|
||||||
@@ -568,167 +547,114 @@ class TwoLevelCGmrhs
|
|||||||
std::cout<<GridLogMessage<<"HDCG: non-converged solution "<<xnorm<<" source "<<srcnorm<<std::endl;
|
std::cout<<GridLogMessage<<"HDCG: non-converged solution "<<xnorm<<" source "<<srcnorm<<std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public:
|
RealD PcgM3(const Field & p, Field & mmp){
|
||||||
|
|
||||||
virtual void PcgM1(std::vector<Field> & in,std::vector<Field> & out) = 0;
|
|
||||||
virtual void Vstart(std::vector<Field> & x,std::vector<Field> & src) = 0;
|
|
||||||
virtual void PcgM2(const Field & in, Field & out) {
|
|
||||||
out=in;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual RealD PcgM3(const Field & p, Field & mmp){
|
|
||||||
RealD dd;
|
RealD dd;
|
||||||
_FineLinop.HermOp(p,mmp);
|
_FineLinop.HermOp(p,mmp);
|
||||||
ComplexD dot = innerProduct(p,mmp);
|
ComplexD dot = innerProduct(p,mmp);
|
||||||
dd=real(dot);
|
dd=real(dot);
|
||||||
return dd;
|
return dd;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class Field, class CoarseField>
|
//////////////////////////////////////////////////////////////////////
|
||||||
class TwoLevelADEF2mrhs : public TwoLevelCGmrhs<Field>
|
// ADEF-2 as an mrhs preconditioner object (Tang, Nabben, Vuik, Erlangga):
|
||||||
|
// M1 = [1 - Q A] M + Q with Q = P A_c^{-1} P^dag (deflated coarse solve)
|
||||||
|
// Vstart = Q b the coarse-corrected start (precise coarse solve)
|
||||||
|
// The coarse space is the D+1 multiRHS field throughout: the projector's
|
||||||
|
// mixed overloads take the vector of fine fields straight to it, and the
|
||||||
|
// deflator and coarse solver act on it. No per-rhs slices anywhere.
|
||||||
|
// The D+1 grid follows the solver's Nrhs through SetCoarseGridMrhs.
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Projector_t defaults to a transfer operator whose STORE matches Field.
|
||||||
|
template<class Field, class CoarseField, class Projector_t = MultiRHSBlockProject<Field> >
|
||||||
|
class MrhsADEF2Preconditioner : public MrhsPreconditioner<Field>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GridBase *coarsegrid;
|
GridBase *grid; // fine
|
||||||
GridBase *coarsegridmrhs;
|
GridBase *coarsegridmrhs; // D+1, rhs innermost
|
||||||
LinearFunction<CoarseField> &_CoarseSolverMrhs;
|
LinearOperatorBase<Field> &_FineLinop;
|
||||||
LinearFunction<CoarseField> &_CoarseSolverPreciseMrhs;
|
LinearFunction<Field> &_Smoother;
|
||||||
MultiRHSBlockProject<Field> &_Projector;
|
LinearFunction<CoarseField> &_CoarseSolver; // in M1
|
||||||
MultiRHSDeflation<CoarseField> &_Deflator;
|
LinearFunction<CoarseField> &_CoarseSolverPrecise; // in Vstart
|
||||||
|
Projector_t &_Projector; // store precision need not match Field
|
||||||
|
MultiRHSDeflation<CoarseField> &_Deflator; // nev==0: no deflation, zero coarse guess
|
||||||
|
|
||||||
|
MrhsADEF2Preconditioner(LinearOperatorBase<Field> &FineLinop,
|
||||||
TwoLevelADEF2mrhs(RealD tol,
|
LinearFunction<Field> &Smoother,
|
||||||
Integer maxit,
|
LinearFunction<CoarseField> &CoarseSolver,
|
||||||
LinearOperatorBase<Field> &FineLinop,
|
LinearFunction<CoarseField> &CoarseSolverPrecise,
|
||||||
LinearFunction<Field> &Smoother,
|
Projector_t &Projector,
|
||||||
LinearFunction<CoarseField> &CoarseSolverMrhs,
|
MultiRHSDeflation<CoarseField> &Deflator,
|
||||||
LinearFunction<CoarseField> &CoarseSolverPreciseMrhs,
|
GridBase *CoarseGridMrhs,
|
||||||
MultiRHSBlockProject<Field> &Projector,
|
GridBase *FineGrid) :
|
||||||
MultiRHSDeflation<CoarseField> &Deflator,
|
_FineLinop(FineLinop), _Smoother(Smoother),
|
||||||
GridBase *_coarsemrhsgrid) :
|
_CoarseSolver(CoarseSolver), _CoarseSolverPrecise(CoarseSolverPrecise),
|
||||||
TwoLevelCGmrhs<Field>(tol, maxit,FineLinop,Smoother,Projector.fine_grid),
|
_Projector(Projector), _Deflator(Deflator)
|
||||||
_CoarseSolverMrhs(CoarseSolverMrhs),
|
|
||||||
_CoarseSolverPreciseMrhs(CoarseSolverPreciseMrhs),
|
|
||||||
_Projector(Projector),
|
|
||||||
_Deflator(Deflator)
|
|
||||||
{
|
{
|
||||||
coarsegrid = Projector.coarse_grid;
|
// The fine grid is given, NOT taken from the projector: the projector's
|
||||||
coarsegridmrhs = _coarsemrhsgrid;// Thi could be in projector
|
// grid carries its STORE's layout, which need not be this Field's (an
|
||||||
|
// fp32 preconditioner may project through an fp64 store, or the reverse).
|
||||||
|
grid = FineGrid;
|
||||||
|
coarsegridmrhs = CoarseGridMrhs;
|
||||||
};
|
};
|
||||||
|
// Store matches Field: the projector's grid IS this Field's grid.
|
||||||
|
MrhsADEF2Preconditioner(LinearOperatorBase<Field> &FineLinop,
|
||||||
|
LinearFunction<Field> &Smoother,
|
||||||
|
LinearFunction<CoarseField> &CoarseSolver,
|
||||||
|
LinearFunction<CoarseField> &CoarseSolverPrecise,
|
||||||
|
Projector_t &Projector,
|
||||||
|
MultiRHSDeflation<CoarseField> &Deflator,
|
||||||
|
GridBase *CoarseGridMrhs) :
|
||||||
|
MrhsADEF2Preconditioner(FineLinop,Smoother,CoarseSolver,CoarseSolverPrecise,
|
||||||
|
Projector,Deflator,CoarseGridMrhs,Projector.fine_grid) {};
|
||||||
|
|
||||||
// Override Vstart
|
void SetCoarseGridMrhs(GridBase *CoarseGridMrhs) { coarsegridmrhs = CoarseGridMrhs; }
|
||||||
virtual void Vstart(std::vector<Field> & x,std::vector<Field> & src)
|
|
||||||
|
// The coarse solver's starting guess: deflated, or zero
|
||||||
|
void CoarseGuess(CoarseField &src,CoarseField &guess)
|
||||||
{
|
{
|
||||||
int nrhs=x.size();
|
|
||||||
///////////////////////////////////
|
|
||||||
// Choose x_0 such that
|
|
||||||
// x_0 = guess + (A_ss^inv) r_s = guess + Ass_inv [src -Aguess]
|
|
||||||
// = [1 - Ass_inv A] Guess + Assinv src
|
|
||||||
// = P^T guess + Assinv src
|
|
||||||
// = Vstart [Tang notation]
|
|
||||||
// This gives:
|
|
||||||
// W^T (src - A x_0) = src_s - A guess_s - r_s
|
|
||||||
// = src_s - (A guess)_s - src_s + (A guess)_s
|
|
||||||
// = 0
|
|
||||||
///////////////////////////////////
|
|
||||||
std::vector<CoarseField> PleftProj(nrhs,this->coarsegrid);
|
|
||||||
std::vector<CoarseField> PleftMss_proj(nrhs,this->coarsegrid);
|
|
||||||
CoarseField PleftProjMrhs(this->coarsegridmrhs);
|
|
||||||
CoarseField PleftMss_projMrhs(this->coarsegridmrhs);
|
|
||||||
|
|
||||||
this->_Projector.blockProject(src,PleftProj);
|
|
||||||
this->_Deflator.DeflateSources(PleftProj,PleftMss_proj);
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
|
||||||
InsertSliceFast(PleftProj[rhs],PleftProjMrhs,rhs,0);
|
|
||||||
InsertSliceFast(PleftMss_proj[rhs],PleftMss_projMrhs,rhs,0); // the guess
|
|
||||||
}
|
|
||||||
|
|
||||||
this->_CoarseSolverPreciseMrhs(PleftProjMrhs,PleftMss_projMrhs); // Ass^{-1} r_s
|
|
||||||
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
|
||||||
ExtractSliceFast(PleftMss_proj[rhs],PleftMss_projMrhs,rhs,0);
|
|
||||||
}
|
|
||||||
this->_Projector.blockPromote(x,PleftMss_proj);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void PcgM1(std::vector<Field> & in,std::vector<Field> & out){
|
|
||||||
|
|
||||||
int nrhs=in.size();
|
|
||||||
|
|
||||||
// [PTM+Q] in = [1 - Q A] M in + Q in = Min + Q [ in -A Min]
|
|
||||||
std::vector<Field> tmp(nrhs,this->grid);
|
|
||||||
std::vector<Field> Min(nrhs,this->grid);
|
|
||||||
|
|
||||||
std::vector<CoarseField> PleftProj(nrhs,this->coarsegrid);
|
|
||||||
std::vector<CoarseField> PleftMss_proj(nrhs,this->coarsegrid);
|
|
||||||
|
|
||||||
CoarseField PleftProjMrhs(this->coarsegridmrhs);
|
|
||||||
CoarseField PleftMss_projMrhs(this->coarsegridmrhs);
|
|
||||||
|
|
||||||
// this->rrr=in[0];
|
|
||||||
|
|
||||||
#undef SMOOTHER_BLOCK_SOLVE
|
|
||||||
#if SMOOTHER_BLOCK_SOLVE
|
|
||||||
this->SmoothTimer.Start();
|
|
||||||
this->_Smoother(in,Min);
|
|
||||||
this->SmoothTimer.Stop();
|
|
||||||
#else
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
|
||||||
this->SmoothTimer.Start();
|
|
||||||
this->_Smoother(in[rhs],Min[rhs]);
|
|
||||||
this->SmoothTimer.Stop();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// this->sss=Min[0];
|
|
||||||
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
|
||||||
|
|
||||||
this->FineTimer.Start();
|
|
||||||
this->_FineLinop.HermOp(Min[rhs],out[rhs]);
|
|
||||||
axpy(tmp[rhs],-1.0,out[rhs],in[rhs]); // resid = in - A Min
|
|
||||||
this->FineTimer.Stop();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
this->ProjectTimer.Start();
|
|
||||||
this->_Projector.blockProject(tmp,PleftProj);
|
|
||||||
this->ProjectTimer.Stop();
|
|
||||||
this->DeflateTimer.Start();
|
this->DeflateTimer.Start();
|
||||||
this->_Deflator.DeflateSources(PleftProj,PleftMss_proj);
|
if ( _Deflator.nev > 0 ) _Deflator.DeflateSources(src,guess);
|
||||||
|
else guess = Zero();
|
||||||
this->DeflateTimer.Stop();
|
this->DeflateTimer.Stop();
|
||||||
this->InsertTimer.Start();
|
}
|
||||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
// x_0 = Q b
|
||||||
InsertSliceFast(PleftProj[rhs],PleftProjMrhs,rhs,0);
|
virtual void Vstart(std::vector<Field> &x,std::vector<Field> &src)
|
||||||
InsertSliceFast(PleftMss_proj[rhs],PleftMss_projMrhs,rhs,0); // the guess
|
{
|
||||||
}
|
CoarseField Csrc(coarsegridmrhs), Csol(coarsegridmrhs);
|
||||||
this->InsertTimer.Stop();
|
this->ProjectTimer.Start(); _Projector.blockProject(src,Csrc); this->ProjectTimer.Stop();
|
||||||
|
CoarseGuess(Csrc,Csol);
|
||||||
|
this->CoarseTimer.Start(); _CoarseSolverPrecise(Csrc,Csol); this->CoarseTimer.Stop();
|
||||||
|
this->PromoteTimer.Start(); _Projector.blockPromote(x,Csol); this->PromoteTimer.Stop();
|
||||||
|
}
|
||||||
|
// [1 - Q A] M in + Q in = Min + Q [in - A Min]
|
||||||
|
virtual void operator()(std::vector<Field> &in,std::vector<Field> &out)
|
||||||
|
{
|
||||||
|
int nrhs=in.size();
|
||||||
|
std::vector<Field> tmp(nrhs,grid), Min(nrhs,grid);
|
||||||
|
CoarseField Csrc(coarsegridmrhs), Csol(coarsegridmrhs);
|
||||||
|
|
||||||
this->CoarseTimer.Start();
|
this->SmoothTimer.Start();
|
||||||
this->_CoarseSolverMrhs(PleftProjMrhs,PleftMss_projMrhs); // Ass^{-1} [in - A Min]_s
|
for(int r=0;r<nrhs;r++) _Smoother(in[r],Min[r]);
|
||||||
this->CoarseTimer.Stop();
|
this->SmoothTimer.Stop();
|
||||||
|
|
||||||
this->InsertTimer.Start();
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
|
||||||
ExtractSliceFast(PleftMss_proj[rhs],PleftMss_projMrhs,rhs,0);
|
|
||||||
}
|
|
||||||
this->InsertTimer.Stop();
|
|
||||||
this->PromoteTimer.Start();
|
|
||||||
this->_Projector.blockPromote(tmp,PleftMss_proj);// tmp= Q[in - A Min]
|
|
||||||
this->PromoteTimer.Stop();
|
|
||||||
this->FineTimer.Start();
|
this->FineTimer.Start();
|
||||||
// this->qqq=tmp[0];
|
for(int r=0;r<nrhs;r++){
|
||||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
_FineLinop.HermOp(Min[r],out[r]);
|
||||||
axpy(out[rhs],1.0,Min[rhs],tmp[rhs]); // Min+tmp
|
axpy(tmp[r],-1.0,out[r],in[r]); // in - A Min
|
||||||
}
|
}
|
||||||
// this->zzz=out[0];
|
this->FineTimer.Stop();
|
||||||
|
|
||||||
|
this->ProjectTimer.Start(); _Projector.blockProject(tmp,Csrc); this->ProjectTimer.Stop();
|
||||||
|
CoarseGuess(Csrc,Csol);
|
||||||
|
this->CoarseTimer.Start(); _CoarseSolver(Csrc,Csol); this->CoarseTimer.Stop();
|
||||||
|
this->PromoteTimer.Start(); _Projector.blockPromote(tmp,Csol); this->PromoteTimer.Stop();
|
||||||
|
|
||||||
|
this->FineTimer.Start();
|
||||||
|
for(int r=0;r<nrhs;r++) axpy(out[r],1.0,Min[r],tmp[r]); // Min + Q[in - A Min]
|
||||||
this->FineTimer.Stop();
|
this->FineTimer.Stop();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
NAMESPACE_END(Grid);
|
||||||
|
|
||||||
|
|
||||||
@@ -57,6 +57,7 @@ public:
|
|||||||
GridStopWatch MatTimer;
|
GridStopWatch MatTimer;
|
||||||
GridStopWatch LinalgTimer;
|
GridStopWatch LinalgTimer;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
std::string trace_step = "PGCR_step"; // name + step; see Name()
|
||||||
int ZeroGuess = 0; // caller contract: guess is always zero => first-cycle r0 = src, skip the apply
|
int ZeroGuess = 0; // caller contract: guess is always zero => first-cycle r0 = src, skip the apply
|
||||||
// persistent GCR history (see GCRnStep)
|
// persistent GCR history (see GCRnStep)
|
||||||
GridBase *hist_grid = nullptr;
|
GridBase *hist_grid = nullptr;
|
||||||
@@ -68,7 +69,10 @@ public:
|
|||||||
LinearFunction<Field> &Preconditioner;
|
LinearFunction<Field> &Preconditioner;
|
||||||
LinearOperatorBase<Field> &Linop;
|
LinearOperatorBase<Field> &Linop;
|
||||||
|
|
||||||
void Name(std::string _name) { name = _name; };
|
// 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 Level(int n) { Name("Level " + std::to_string(n)); level = n; }
|
||||||
|
|
||||||
@@ -222,7 +226,7 @@ public:
|
|||||||
GCRLogLevel<< "PGCR true residual "<< sqrt(cp/SSQ) <<std::endl;
|
GCRLogLevel<< "PGCR true residual "<< sqrt(cp/SSQ) <<std::endl;
|
||||||
|
|
||||||
for(int k=0;k<nstep;k++){
|
for(int k=0;k<nstep;k++){
|
||||||
GRID_TRACE("PGCR_step");
|
GRID_TRACE(trace_step.c_str());
|
||||||
steps++;
|
steps++;
|
||||||
|
|
||||||
int kp = k+1;
|
int kp = k+1;
|
||||||
|
|||||||
@@ -592,7 +592,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void RefineSubspaceHDCG(LinearOperatorBase<FineField> &hermop,
|
virtual void RefineSubspaceHDCG(LinearOperatorBase<FineField> &hermop,
|
||||||
TwoLevelADEF2mrhs<FineField,CoarseVector> & theHDCG,
|
TwoLevelCGmrhs<FineField> & theHDCG,
|
||||||
int nrhs)
|
int nrhs)
|
||||||
{
|
{
|
||||||
std::vector<FineField> src_mrhs(nrhs,FineGrid);
|
std::vector<FineField> src_mrhs(nrhs,FineGrid);
|
||||||
|
|||||||
@@ -21,6 +21,20 @@ Author: Peter Boyle <pboyle@bnl.gov>
|
|||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
NAMESPACE_BEGIN(Grid);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Scalar of the distributed dense inversion: block-cyclic redistribution,
|
||||||
|
// SUMMA, the Schur recursion and its LU leaf. A configure-time choice
|
||||||
|
// (--enable-dense-inverse-precision=double|single), INDEPENDENT of the
|
||||||
|
// coarse-space precision: the matrix elements arrive in the coarse scalar
|
||||||
|
// and the apply slab is fp32 either way; only the factorisation's
|
||||||
|
// arithmetic and footprint follow this type.
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
#ifdef GRID_DENSE_INVERSE_SINGLE
|
||||||
|
typedef ComplexF DenseInverseScalar;
|
||||||
|
#else
|
||||||
|
typedef ComplexD DenseInverseScalar;
|
||||||
|
#endif
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// 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.
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
class BlockRows
|
class BlockRows
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
deviceVector<ComplexD> data;
|
deviceVector<DenseInverseScalar> data;
|
||||||
int64_t rows;
|
int64_t rows;
|
||||||
int64_t cols;
|
int64_t cols;
|
||||||
int64_t ld;
|
int64_t ld;
|
||||||
@@ -52,7 +52,7 @@ public:
|
|||||||
ld = r;
|
ld = r;
|
||||||
data.resize((uint64_t)r*c);
|
data.resize((uint64_t)r*c);
|
||||||
}
|
}
|
||||||
ComplexD *ColumnWindow(int64_t col0)
|
DenseInverseScalar *ColumnWindow(int64_t col0)
|
||||||
{
|
{
|
||||||
GRID_ASSERT( col0 >= 0 );
|
GRID_ASSERT( col0 >= 0 );
|
||||||
GRID_ASSERT( col0 <= cols );
|
GRID_ASSERT( col0 <= cols );
|
||||||
@@ -128,10 +128,10 @@ public:
|
|||||||
// buffer(a,b) = elem(rows[a], cols[b]), a fastest.
|
// buffer(a,b) = elem(rows[a], cols[b]), a fastest.
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
static void MoveEdge(int toBuffer,
|
static void MoveEdge(int toBuffer,
|
||||||
ComplexD *mat, int64_t ld,
|
DenseInverseScalar *mat, int64_t ld,
|
||||||
const std::vector<int64_t> &roff, // per-row offset in mat
|
const std::vector<int64_t> &roff, // per-row offset in mat
|
||||||
const std::vector<int64_t> &coff, // per-col offset in mat
|
const std::vector<int64_t> &coff, // per-col offset in mat
|
||||||
ComplexD *buf)
|
DenseInverseScalar *buf)
|
||||||
{
|
{
|
||||||
int64_t nr = roff.size();
|
int64_t nr = roff.size();
|
||||||
int64_t nc = coff.size();
|
int64_t nc = coff.size();
|
||||||
@@ -193,7 +193,7 @@ public:
|
|||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
static void Redistribute(int dir, GridBase *grid,
|
static void Redistribute(int dir, GridBase *grid,
|
||||||
const std::vector<int64_t> &rowStart,
|
const std::vector<int64_t> &rowStart,
|
||||||
ComplexD *rows1d, int64_t myrows,
|
DenseInverseScalar *rows1d, int64_t myrows,
|
||||||
BlockCyclicMatrix &A)
|
BlockCyclicMatrix &A)
|
||||||
{
|
{
|
||||||
BlockCyclicLayout &L = A.layout;
|
BlockCyclicLayout &L = A.layout;
|
||||||
@@ -206,7 +206,7 @@ public:
|
|||||||
int64_t ld1 = myrows ? myrows : 1;
|
int64_t ld1 = myrows ? myrows : 1;
|
||||||
|
|
||||||
std::vector<int64_t> rows, cols, roff, coff;
|
std::vector<int64_t> rows, cols, roff, coff;
|
||||||
deviceVector<ComplexD> sbuf(1), rbuf(1);
|
deviceVector<DenseInverseScalar> sbuf(1), rbuf(1);
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
// Self edge: purely local, via a bounce buffer (shares all the code).
|
// Self edge: purely local, via a bounce buffer (shares all the code).
|
||||||
@@ -261,7 +261,7 @@ public:
|
|||||||
}
|
}
|
||||||
grid->SendToRecvFrom((void *)&sbuf[0], partner,
|
grid->SendToRecvFrom((void *)&sbuf[0], partner,
|
||||||
(void *)&rbuf[0], partner,
|
(void *)&rbuf[0], partner,
|
||||||
nmax*sizeof(ComplexD));
|
nmax*sizeof(DenseInverseScalar));
|
||||||
if ( nin ){
|
if ( nin ){
|
||||||
if ( dir > 0 ) { Offsets2D(L, irow, icol, roff, coff);
|
if ( dir > 0 ) { Offsets2D(L, irow, icol, roff, coff);
|
||||||
MoveEdge(0, &A.data[0], L.mloc, roff, coff, &rbuf[0]); }
|
MoveEdge(0, &A.data[0], L.mloc, roff, coff, &rbuf[0]); }
|
||||||
@@ -272,11 +272,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void RowsToCyclic(GridBase *grid, const std::vector<int64_t> &rowStart,
|
static void RowsToCyclic(GridBase *grid, const std::vector<int64_t> &rowStart,
|
||||||
ComplexD *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, ComplexD *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); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ public:
|
|||||||
// Window copy-scale: Dst[i0:i1, j0:j1] = alpha * Src[same window].
|
// Window copy-scale: Dst[i0:i1, j0:j1] = alpha * Src[same window].
|
||||||
// Both share one layout, so the local bands coincide; pure local kernel.
|
// Both share one layout, so the local bands coincide; pure local kernel.
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
void WindowCopyScale(ComplexD alpha,
|
void WindowCopyScale(DenseInverseScalar alpha,
|
||||||
BlockCyclicMatrix &Src, BlockCyclicMatrix &Dst,
|
BlockCyclicMatrix &Src, BlockCyclicMatrix &Dst,
|
||||||
int64_t i0, int64_t i1, int64_t j0, int64_t j1)
|
int64_t i0, int64_t i1, int64_t j0, int64_t j1)
|
||||||
{
|
{
|
||||||
@@ -127,8 +127,8 @@ public:
|
|||||||
L.ColRange(j0,j1, lj0,lj1);
|
L.ColRange(j0,j1, lj0,lj1);
|
||||||
int64_t m = li1-li0, n = lj1-lj0;
|
int64_t m = li1-li0, n = lj1-lj0;
|
||||||
if ( !(m && n) ) return;
|
if ( !(m && n) ) return;
|
||||||
ComplexD *src = Src.LocalWindow(li0,lj0);
|
DenseInverseScalar *src = Src.LocalWindow(li0,lj0);
|
||||||
ComplexD *dst = Dst.LocalWindow(li0,lj0);
|
DenseInverseScalar *dst = Dst.LocalWindow(li0,lj0);
|
||||||
int64_t ldS = Src.layout.mloc;
|
int64_t ldS = Src.layout.mloc;
|
||||||
int64_t ldD = L.mloc;
|
int64_t ldD = L.mloc;
|
||||||
tCopy -= usecond();
|
tCopy -= usecond();
|
||||||
@@ -164,10 +164,10 @@ public:
|
|||||||
GRID_ASSERT( lc1-lc0 == w );
|
GRID_ASSERT( lc1-lc0 == w );
|
||||||
|
|
||||||
// Pack the strided block dense (inverseBatched assumes lda == w).
|
// Pack the strided block dense (inverseBatched assumes lda == w).
|
||||||
deviceVector<ComplexD> dense((uint64_t)w*w);
|
deviceVector<DenseInverseScalar> dense((uint64_t)w*w);
|
||||||
{
|
{
|
||||||
ComplexD *src = A.LocalWindow(lr0,lc0);
|
DenseInverseScalar *src = A.LocalWindow(lr0,lc0);
|
||||||
ComplexD *dst = &dense[0];
|
DenseInverseScalar *dst = &dense[0];
|
||||||
int64_t ld = L.mloc;
|
int64_t ld = L.mloc;
|
||||||
accelerator_for(idx, (uint64_t)(w*w), 1, {
|
accelerator_for(idx, (uint64_t)(w*w), 1, {
|
||||||
int64_t jj = idx / w;
|
int64_t jj = idx / w;
|
||||||
@@ -176,15 +176,15 @@ public:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
deviceVector<ComplexD*> bp(1);
|
deviceVector<DenseInverseScalar*> bp(1);
|
||||||
std::vector<ComplexD*> ptr(1);
|
std::vector<DenseInverseScalar*> ptr(1);
|
||||||
ptr[0] = &dense[0];
|
ptr[0] = &dense[0];
|
||||||
acceleratorCopyToDevice(&ptr[0], &bp[0], sizeof(ComplexD*));
|
acceleratorCopyToDevice(&ptr[0], &bp[0], sizeof(DenseInverseScalar*));
|
||||||
INV.inverseBatched(w, bp);
|
INV.inverseBatched(w, bp);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ComplexD *src = &dense[0];
|
DenseInverseScalar *src = &dense[0];
|
||||||
ComplexD *dst = A.LocalWindow(lr0,lc0);
|
DenseInverseScalar *dst = A.LocalWindow(lr0,lc0);
|
||||||
int64_t ld = L.mloc;
|
int64_t ld = L.mloc;
|
||||||
accelerator_for(idx, (uint64_t)(w*w), 1, {
|
accelerator_for(idx, (uint64_t)(w*w), 1, {
|
||||||
int64_t jj = idx / w;
|
int64_t jj = idx / w;
|
||||||
@@ -194,8 +194,8 @@ public:
|
|||||||
}
|
}
|
||||||
// Growth telemetry, local only.
|
// Growth telemetry, local only.
|
||||||
{
|
{
|
||||||
std::vector<ComplexD> h((uint64_t)w*w);
|
std::vector<DenseInverseScalar> h((uint64_t)w*w);
|
||||||
acceleratorCopyFromDevice(&dense[0], &h[0], h.size()*sizeof(ComplexD));
|
acceleratorCopyFromDevice(&dense[0], &h[0], h.size()*sizeof(DenseInverseScalar));
|
||||||
double mx = 0.0;
|
double mx = 0.0;
|
||||||
for(auto &z : h){
|
for(auto &z : h){
|
||||||
double re=z.real(), im=z.imag();
|
double re=z.real(), im=z.imag();
|
||||||
@@ -232,23 +232,23 @@ public:
|
|||||||
int64_t lr0,lr1,lc0,lc1; L.RowRange(c0,c1,lr0,lr1); L.ColRange(c0,c1,lc0,lc1);
|
int64_t lr0,lr1,lc0,lc1; L.RowRange(c0,c1,lr0,lr1); L.ColRange(c0,c1,lc0,lc1);
|
||||||
const int64_t mq = lr1-lr0, nq = lc1-lc0;
|
const int64_t mq = lr1-lr0, nq = lc1-lc0;
|
||||||
|
|
||||||
deviceVector<ComplexD> dense; // root only: W x W column major
|
deviceVector<DenseInverseScalar> dense; // root only: W x W column major
|
||||||
deviceVector<ComplexD> piece, dummy; // piece: my mq x nq contiguous; dummy: reverse-direction filler
|
deviceVector<DenseInverseScalar> piece, dummy; // piece: my mq x nq contiguous; dummy: reverse-direction filler
|
||||||
if ( me == root ) dense.resize((uint64_t)W*W);
|
if ( me == root ) dense.resize((uint64_t)W*W);
|
||||||
|
|
||||||
auto pack_piece = [&](ComplexD *dst, int64_t m, int64_t n, int64_t r0, int64_t cc0){
|
auto pack_piece = [&](DenseInverseScalar *dst, int64_t m, int64_t n, int64_t r0, int64_t cc0){
|
||||||
ComplexD *src = A.LocalWindow(r0,cc0); const int64_t ld = L.mloc;
|
DenseInverseScalar *src = A.LocalWindow(r0,cc0); const int64_t ld = L.mloc;
|
||||||
accelerator_for(idx,(uint64_t)(m*n),1,{ int64_t jj=idx/m, ii=idx-jj*m; dst[ii+jj*m] = src[ii+jj*ld]; });
|
accelerator_for(idx,(uint64_t)(m*n),1,{ int64_t jj=idx/m, ii=idx-jj*m; dst[ii+jj*m] = src[ii+jj*ld]; });
|
||||||
};
|
};
|
||||||
auto unpack_piece = [&](ComplexD *src, int64_t m, int64_t n, int64_t r0, int64_t cc0){
|
auto unpack_piece = [&](DenseInverseScalar *src, int64_t m, int64_t n, int64_t r0, int64_t cc0){
|
||||||
ComplexD *dst = A.LocalWindow(r0,cc0); const int64_t ld = L.mloc;
|
DenseInverseScalar *dst = A.LocalWindow(r0,cc0); const int64_t ld = L.mloc;
|
||||||
accelerator_for(idx,(uint64_t)(m*n),1,{ int64_t jj=idx/m, ii=idx-jj*m; dst[ii+jj*ld] = src[ii+jj*m]; });
|
accelerator_for(idx,(uint64_t)(m*n),1,{ int64_t jj=idx/m, ii=idx-jj*m; dst[ii+jj*ld] = src[ii+jj*m]; });
|
||||||
};
|
};
|
||||||
// root: piece of rank q <-> dense, via the closed-form block map
|
// root: piece of rank q <-> dense, via the closed-form block map
|
||||||
auto root_place = [&](ComplexD *pc, int64_t m, int64_t n, int q, int to_dense){
|
auto root_place = [&](DenseInverseScalar *pc, int64_t m, int64_t n, int q, int to_dense){
|
||||||
const int pq=q/Pc, cq=q%Pc;
|
const int pq=q/Pc, cq=q%Pc;
|
||||||
const int64_t brq0=FirstBlock(b0,pq,Pr), bcq0=FirstBlock(b0,cq,Pc);
|
const int64_t brq0=FirstBlock(b0,pq,Pr), bcq0=FirstBlock(b0,cq,Pc);
|
||||||
ComplexD *dn = &dense[0]; const int64_t WW=W, NB=nb, PR=Pr, PC=Pc, C0=c0;
|
DenseInverseScalar *dn = &dense[0]; const int64_t WW=W, NB=nb, PR=Pr, PC=Pc, C0=c0;
|
||||||
accelerator_for(idx,(uint64_t)(m*n),1,{
|
accelerator_for(idx,(uint64_t)(m*n),1,{
|
||||||
int64_t jj=idx/m, ii=idx-jj*m;
|
int64_t jj=idx/m, ii=idx-jj*m;
|
||||||
int64_t gr = (brq0 + (ii/NB)*PR)*NB + ii%NB - C0;
|
int64_t gr = (brq0 + (ii/NB)*PR)*NB + ii%NB - C0;
|
||||||
@@ -266,18 +266,18 @@ public:
|
|||||||
tBigGather -= usecond();
|
tBigGather -= usecond();
|
||||||
if ( mq*nq ) { piece.resize((uint64_t)mq*nq); dummy.resize((uint64_t)mq*nq); pack_piece(&piece[0],mq,nq,lr0,lc0); accelerator_barrier(); }
|
if ( mq*nq ) { piece.resize((uint64_t)mq*nq); dummy.resize((uint64_t)mq*nq); pack_piece(&piece[0],mq,nq,lr0,lc0); accelerator_barrier(); }
|
||||||
if ( me == root ) {
|
if ( me == root ) {
|
||||||
deviceVector<ComplexD> stage;
|
deviceVector<DenseInverseScalar> stage;
|
||||||
for(int q=0;q<Pr*Pc;q++){
|
for(int q=0;q<Pr*Pc;q++){
|
||||||
int64_t m,n; piece_dims(q,m,n); if ( !(m*n) ) continue;
|
int64_t m,n; piece_dims(q,m,n); if ( !(m*n) ) continue;
|
||||||
if ( q == root ) { root_place(&piece[0],m,n,q,1); continue; }
|
if ( q == root ) { root_place(&piece[0],m,n,q,1); continue; }
|
||||||
if ( stage.size() < (uint64_t)(m*n) ) stage.resize((uint64_t)m*n);
|
if ( stage.size() < (uint64_t)(m*n) ) stage.resize((uint64_t)m*n);
|
||||||
deviceVector<ComplexD> junk((uint64_t)m*n);
|
deviceVector<DenseInverseScalar> junk((uint64_t)m*n);
|
||||||
grid->SendToRecvFrom((void *)&junk[0], q, (void *)&stage[0], q, (uint64_t)m*n*sizeof(ComplexD));
|
grid->SendToRecvFrom((void *)&junk[0], q, (void *)&stage[0], q, (uint64_t)m*n*sizeof(DenseInverseScalar));
|
||||||
root_place(&stage[0],m,n,q,1);
|
root_place(&stage[0],m,n,q,1);
|
||||||
}
|
}
|
||||||
accelerator_barrier();
|
accelerator_barrier();
|
||||||
} else if ( mq*nq ) {
|
} else if ( mq*nq ) {
|
||||||
grid->SendToRecvFrom((void *)&piece[0], root, (void *)&dummy[0], root, (uint64_t)mq*nq*sizeof(ComplexD));
|
grid->SendToRecvFrom((void *)&piece[0], root, (void *)&dummy[0], root, (uint64_t)mq*nq*sizeof(DenseInverseScalar));
|
||||||
}
|
}
|
||||||
tBigGather += usecond();
|
tBigGather += usecond();
|
||||||
|
|
||||||
@@ -289,17 +289,17 @@ public:
|
|||||||
// ---- scatter ----
|
// ---- scatter ----
|
||||||
tBigScatter -= usecond();
|
tBigScatter -= usecond();
|
||||||
if ( me == root ) {
|
if ( me == root ) {
|
||||||
deviceVector<ComplexD> stage;
|
deviceVector<DenseInverseScalar> stage;
|
||||||
for(int q=0;q<Pr*Pc;q++){
|
for(int q=0;q<Pr*Pc;q++){
|
||||||
int64_t m,n; piece_dims(q,m,n); if ( !(m*n) ) continue;
|
int64_t m,n; piece_dims(q,m,n); if ( !(m*n) ) continue;
|
||||||
if ( q == root ) { root_place(&piece[0],m,n,q,0); accelerator_barrier(); continue; }
|
if ( q == root ) { root_place(&piece[0],m,n,q,0); accelerator_barrier(); continue; }
|
||||||
if ( stage.size() < (uint64_t)(m*n) ) stage.resize((uint64_t)m*n);
|
if ( stage.size() < (uint64_t)(m*n) ) stage.resize((uint64_t)m*n);
|
||||||
deviceVector<ComplexD> junk((uint64_t)m*n);
|
deviceVector<DenseInverseScalar> junk((uint64_t)m*n);
|
||||||
root_place(&stage[0],m,n,q,0); accelerator_barrier();
|
root_place(&stage[0],m,n,q,0); accelerator_barrier();
|
||||||
grid->SendToRecvFrom((void *)&stage[0], q, (void *)&junk[0], q, (uint64_t)m*n*sizeof(ComplexD));
|
grid->SendToRecvFrom((void *)&stage[0], q, (void *)&junk[0], q, (uint64_t)m*n*sizeof(DenseInverseScalar));
|
||||||
}
|
}
|
||||||
} else if ( mq*nq ) {
|
} else if ( mq*nq ) {
|
||||||
grid->SendToRecvFrom((void *)&dummy[0], root, (void *)&piece[0], root, (uint64_t)mq*nq*sizeof(ComplexD));
|
grid->SendToRecvFrom((void *)&dummy[0], root, (void *)&piece[0], root, (uint64_t)mq*nq*sizeof(DenseInverseScalar));
|
||||||
}
|
}
|
||||||
if ( mq*nq ) { unpack_piece(&piece[0],mq,nq,lr0,lc0); accelerator_barrier(); }
|
if ( mq*nq ) { unpack_piece(&piece[0],mq,nq,lr0,lc0); accelerator_barrier(); }
|
||||||
tBigScatter += usecond();
|
tBigScatter += usecond();
|
||||||
@@ -326,7 +326,7 @@ public:
|
|||||||
int64_t c0 = b0*L.nb;
|
int64_t c0 = b0*L.nb;
|
||||||
int64_t m = bm*L.nb;
|
int64_t m = bm*L.nb;
|
||||||
int64_t c1 = std::min(L.N, b1*L.nb);
|
int64_t c1 = std::min(L.N, b1*L.nb);
|
||||||
ComplexD one (1.0,0.0), mone(-1.0,0.0), zero(0.0,0.0);
|
DenseInverseScalar one (1.0,0.0), mone(-1.0,0.0), zero(0.0,0.0);
|
||||||
|
|
||||||
// 1. A11 -> A11inv
|
// 1. A11 -> A11inv
|
||||||
SchurNode(A,Bt,Ct,Tt,Ut, b0,bm);
|
SchurNode(A,Bt,Ct,Tt,Ut, b0,bm);
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ class BlockCyclicMatrix
|
|||||||
public:
|
public:
|
||||||
GridBase *grid; // borrowed, never owned
|
GridBase *grid; // borrowed, never owned
|
||||||
BlockCyclicLayout layout;
|
BlockCyclicLayout layout;
|
||||||
deviceVector<ComplexD> data; // column major, ld = layout.mloc
|
deviceVector<DenseInverseScalar> data; // column major, ld = layout.mloc
|
||||||
|
|
||||||
BlockCyclicMatrix(GridBase *g, int64_t N, int64_t nb, int Pr, int Pc)
|
BlockCyclicMatrix(GridBase *g, int64_t N, int64_t nb, int Pr, int Pc)
|
||||||
: grid(g),
|
: grid(g),
|
||||||
@@ -82,7 +82,7 @@ public:
|
|||||||
data.resize( sz ? sz : 1 );
|
data.resize( sz ? sz : 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
ComplexD *LocalWindow(int64_t li, int64_t lj)
|
DenseInverseScalar *LocalWindow(int64_t li, int64_t lj)
|
||||||
{
|
{
|
||||||
return &data[0] + li + lj*layout.mloc;
|
return &data[0] + li + lj*layout.mloc;
|
||||||
}
|
}
|
||||||
@@ -93,32 +93,32 @@ public:
|
|||||||
// oracle only; production data enters through the direct block-cyclic
|
// oracle only; production data enters through the direct block-cyclic
|
||||||
// import, never through these.
|
// import, never through these.
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
void ImportGlobal(const std::vector<ComplexD> &G)
|
void ImportGlobal(const std::vector<DenseInverseScalar> &G)
|
||||||
{
|
{
|
||||||
int64_t N = layout.N;
|
int64_t N = layout.N;
|
||||||
GRID_ASSERT( (int64_t)G.size() == N*N );
|
GRID_ASSERT( (int64_t)G.size() == N*N );
|
||||||
std::vector<ComplexD> h((uint64_t)layout.mloc*layout.nloc, ComplexD(0.0,0.0));
|
std::vector<DenseInverseScalar> h((uint64_t)layout.mloc*layout.nloc, DenseInverseScalar(0.0,0.0));
|
||||||
for(int64_t j=0;j<N;j++){
|
for(int64_t j=0;j<N;j++){
|
||||||
for(int64_t i=0;i<N;i++){
|
for(int64_t i=0;i<N;i++){
|
||||||
if ( layout.Owns(i,j) ) h[layout.LocalOffset(i,j)] = G[i + j*N];
|
if ( layout.Owns(i,j) ) h[layout.LocalOffset(i,j)] = G[i + j*N];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( h.size() )
|
if ( h.size() )
|
||||||
acceleratorCopyToDevice(&h[0], &data[0], h.size()*sizeof(ComplexD));
|
acceleratorCopyToDevice(&h[0], &data[0], h.size()*sizeof(DenseInverseScalar));
|
||||||
}
|
}
|
||||||
void ExportGlobal(std::vector<ComplexD> &G)
|
void ExportGlobal(std::vector<DenseInverseScalar> &G)
|
||||||
{
|
{
|
||||||
int64_t N = layout.N;
|
int64_t N = layout.N;
|
||||||
G.assign((uint64_t)N*N, ComplexD(0.0,0.0));
|
G.assign((uint64_t)N*N, DenseInverseScalar(0.0,0.0));
|
||||||
std::vector<ComplexD> h((uint64_t)layout.mloc*layout.nloc);
|
std::vector<DenseInverseScalar> h((uint64_t)layout.mloc*layout.nloc);
|
||||||
if ( h.size() )
|
if ( h.size() )
|
||||||
acceleratorCopyFromDevice(&data[0], &h[0], h.size()*sizeof(ComplexD));
|
acceleratorCopyFromDevice(&data[0], &h[0], h.size()*sizeof(DenseInverseScalar));
|
||||||
for(int64_t j=0;j<N;j++){
|
for(int64_t j=0;j<N;j++){
|
||||||
for(int64_t i=0;i<N;i++){
|
for(int64_t i=0;i<N;i++){
|
||||||
if ( layout.Owns(i,j) ) G[i + j*N] = h[layout.LocalOffset(i,j)];
|
if ( layout.Owns(i,j) ) G[i + j*N] = h[layout.LocalOffset(i,j)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( N ) grid->GlobalSumVector((ComplexD *)&G[0], (int)(N*N)); // zero-fill: exact
|
if ( N ) grid->GlobalSumVector((DenseInverseScalar *)&G[0], (int)(N*N)); // zero-fill: exact
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -139,8 +139,8 @@ public:
|
|||||||
// per message: measured 1.26 GB/s/rank on 13 MB ring messages (production,
|
// per message: measured 1.26 GB/s/rank on 13 MB ring messages (production,
|
||||||
// GRID_ALLOC_NCACHE_LARGE=64) against 62 s total for the same inverse when
|
// GRID_ALLOC_NCACHE_LARGE=64) against 62 s total for the same inverse when
|
||||||
// hipMalloc returned a stable address.
|
// hipMalloc returned a stable address.
|
||||||
deviceVector<ComplexD> Abuf;
|
deviceVector<DenseInverseScalar> Abuf;
|
||||||
deviceVector<ComplexD> Bbuf;
|
deviceVector<DenseInverseScalar> Bbuf;
|
||||||
double tAlloc=0, tPack=0, tRingA=0, tRingB=0, tGemm=0;
|
double tAlloc=0, tPack=0, tRingA=0, tRingB=0, tGemm=0;
|
||||||
uint64_t bytesRing=0, nRingMsg=0, nMultiply=0, nGemm=0;
|
uint64_t bytesRing=0, nRingMsg=0, nMultiply=0, nGemm=0;
|
||||||
// Per-message-size histogram (bucket = floor(log2 bytes)): count, bytes,
|
// Per-message-size histogram (bucket = floor(log2 bytes)): count, bytes,
|
||||||
@@ -156,10 +156,10 @@ public:
|
|||||||
static int Overlap(int64_t a0,int64_t a1,int64_t b0,int64_t b1)
|
static int Overlap(int64_t a0,int64_t a1,int64_t b0,int64_t b1)
|
||||||
{ return (a0 < b1) && (b0 < a1); }
|
{ return (a0 < b1) && (b0 < a1); }
|
||||||
|
|
||||||
void Multiply(ComplexD alpha,
|
void Multiply(DenseInverseScalar alpha,
|
||||||
BlockCyclicMatrix &A,
|
BlockCyclicMatrix &A,
|
||||||
BlockCyclicMatrix &B,
|
BlockCyclicMatrix &B,
|
||||||
ComplexD beta,
|
DenseInverseScalar beta,
|
||||||
BlockCyclicMatrix &C,
|
BlockCyclicMatrix &C,
|
||||||
int64_t i0, int64_t i1,
|
int64_t i0, int64_t i1,
|
||||||
int64_t j0, int64_t j1,
|
int64_t j0, int64_t j1,
|
||||||
@@ -220,8 +220,8 @@ public:
|
|||||||
if ( Bbuf.size() < std::max<uint64_t>(slotB*Pr,1) ) Bbuf.resize( std::max<uint64_t>(slotB*Pr,1) );
|
if ( Bbuf.size() < std::max<uint64_t>(slotB*Pr,1) ) Bbuf.resize( std::max<uint64_t>(slotB*Pr,1) );
|
||||||
tAlloc += usecond();
|
tAlloc += usecond();
|
||||||
|
|
||||||
deviceVector<ComplexD *> ap(1), bp(1), cp(1);
|
deviceVector<DenseInverseScalar *> ap(1), bp(1), cp(1);
|
||||||
std::vector<ComplexD *> ptr(1);
|
std::vector<DenseInverseScalar *> ptr(1);
|
||||||
|
|
||||||
int firstblock = 1;
|
int firstblock = 1;
|
||||||
for(int64_t r0=kb0; r0<kb1; r0+=Pc){ // rounds of Pc k-blocks
|
for(int64_t r0=kb0; r0<kb1; r0+=Pc){ // rounds of Pc k-blocks
|
||||||
@@ -238,8 +238,8 @@ public:
|
|||||||
int64_t lc0, lc1;
|
int64_t lc0, lc1;
|
||||||
L.ColRange(s*nb, std::min(N,(s+1)*nb), lc0, lc1);
|
L.ColRange(s*nb, std::min(N,(s+1)*nb), lc0, lc1);
|
||||||
GRID_ASSERT( lc1-lc0 == nb_s );
|
GRID_ASSERT( lc1-lc0 == nb_s );
|
||||||
ComplexD *src = A.LocalWindow(li0, lc0);
|
DenseInverseScalar *src = A.LocalWindow(li0, lc0);
|
||||||
ComplexD *dst = &Abuf[0] + slotA*pcol;
|
DenseInverseScalar *dst = &Abuf[0] + slotA*pcol;
|
||||||
int64_t ld = A.layout.mloc;
|
int64_t ld = A.layout.mloc;
|
||||||
int64_t m = mloc_i;
|
int64_t m = mloc_i;
|
||||||
accelerator_for(idx, (uint64_t)(m*nb_s), 1, {
|
accelerator_for(idx, (uint64_t)(m*nb_s), 1, {
|
||||||
@@ -254,8 +254,8 @@ public:
|
|||||||
GRID_ASSERT( lr1-lr0 == nb_s );
|
GRID_ASSERT( lr1-lr0 == nb_s );
|
||||||
int64_t idxs = (s - r0 - ((prow - r0%Pr + Pr) % Pr)) / Pr; // my panel # in round
|
int64_t idxs = (s - r0 - ((prow - r0%Pr + Pr) % Pr)) / Pr; // my panel # in round
|
||||||
GRID_ASSERT( idxs >= 0 ); GRID_ASSERT( idxs < S );
|
GRID_ASSERT( idxs >= 0 ); GRID_ASSERT( idxs < S );
|
||||||
ComplexD *src = B.LocalWindow(lr0, lj0);
|
DenseInverseScalar *src = B.LocalWindow(lr0, lj0);
|
||||||
ComplexD *dst = &Bbuf[0] + slotB*prow + slotB1*idxs;
|
DenseInverseScalar *dst = &Bbuf[0] + slotB*prow + slotB1*idxs;
|
||||||
int64_t ld = B.layout.mloc;
|
int64_t ld = B.layout.mloc;
|
||||||
int64_t nn = nloc_j;
|
int64_t nn = nloc_j;
|
||||||
accelerator_for(idx, (uint64_t)(nb_s*nn), 1, {
|
accelerator_for(idx, (uint64_t)(nb_s*nn), 1, {
|
||||||
@@ -284,9 +284,9 @@ public:
|
|||||||
double tm = usecond();
|
double tm = usecond();
|
||||||
grid->SendToRecvFrom((void *)(&Abuf[0]+slotA*cs), dest,
|
grid->SendToRecvFrom((void *)(&Abuf[0]+slotA*cs), dest,
|
||||||
(void *)(&Abuf[0]+slotA*cr), src,
|
(void *)(&Abuf[0]+slotA*cr), src,
|
||||||
slotA*sizeof(ComplexD));
|
slotA*sizeof(DenseInverseScalar));
|
||||||
HistAdd(slotA*sizeof(ComplexD), usecond()-tm);
|
HistAdd(slotA*sizeof(DenseInverseScalar), usecond()-tm);
|
||||||
bytesRing += slotA*sizeof(ComplexD); nRingMsg++;
|
bytesRing += slotA*sizeof(DenseInverseScalar); nRingMsg++;
|
||||||
}
|
}
|
||||||
tRingA += usecond();
|
tRingA += usecond();
|
||||||
}
|
}
|
||||||
@@ -304,9 +304,9 @@ public:
|
|||||||
double tm = usecond();
|
double tm = usecond();
|
||||||
grid->SendToRecvFrom((void *)(&Bbuf[0]+slotB*rs), dest,
|
grid->SendToRecvFrom((void *)(&Bbuf[0]+slotB*rs), dest,
|
||||||
(void *)(&Bbuf[0]+slotB*rr), src,
|
(void *)(&Bbuf[0]+slotB*rr), src,
|
||||||
slotB*sizeof(ComplexD));
|
slotB*sizeof(DenseInverseScalar));
|
||||||
HistAdd(slotB*sizeof(ComplexD), usecond()-tm);
|
HistAdd(slotB*sizeof(DenseInverseScalar), usecond()-tm);
|
||||||
bytesRing += slotB*sizeof(ComplexD); nRingMsg++;
|
bytesRing += slotB*sizeof(DenseInverseScalar); nRingMsg++;
|
||||||
}
|
}
|
||||||
tRingB += usecond();
|
tRingB += usecond();
|
||||||
}
|
}
|
||||||
@@ -322,15 +322,15 @@ public:
|
|||||||
int cA = (int)(s%Pc);
|
int cA = (int)(s%Pc);
|
||||||
int rB = (int)(s%Pr);
|
int rB = (int)(s%Pr);
|
||||||
int64_t idxs = (s - r0 - ((rB - r0%Pr + Pr) % Pr)) / Pr;
|
int64_t idxs = (s - r0 - ((rB - r0%Pr + Pr) % Pr)) / Pr;
|
||||||
ComplexD beta_use = firstblock ? beta : ComplexD(1.0,0.0);
|
DenseInverseScalar beta_use = firstblock ? beta : DenseInverseScalar(1.0,0.0);
|
||||||
firstblock = 0;
|
firstblock = 0;
|
||||||
|
|
||||||
ptr[0] = &Abuf[0] + slotA*cA;
|
ptr[0] = &Abuf[0] + slotA*cA;
|
||||||
acceleratorCopyToDevice(&ptr[0], &ap[0], sizeof(ComplexD *));
|
acceleratorCopyToDevice(&ptr[0], &ap[0], sizeof(DenseInverseScalar *));
|
||||||
ptr[0] = &Bbuf[0] + slotB*rB + slotB1*idxs;
|
ptr[0] = &Bbuf[0] + slotB*rB + slotB1*idxs;
|
||||||
acceleratorCopyToDevice(&ptr[0], &bp[0], sizeof(ComplexD *));
|
acceleratorCopyToDevice(&ptr[0], &bp[0], sizeof(DenseInverseScalar *));
|
||||||
ptr[0] = C.LocalWindow(li0, lj0);
|
ptr[0] = C.LocalWindow(li0, lj0);
|
||||||
acceleratorCopyToDevice(&ptr[0], &cp[0], sizeof(ComplexD *));
|
acceleratorCopyToDevice(&ptr[0], &cp[0], sizeof(DenseInverseScalar *));
|
||||||
|
|
||||||
BLAS.gemmBatched(GridBLAS_OP_N, GridBLAS_OP_N,
|
BLAS.gemmBatched(GridBLAS_OP_N, GridBLAS_OP_N,
|
||||||
(int)mloc_i, (int)nloc_j, (int)nb_s,
|
(int)mloc_i, (int)nloc_j, (int)nb_s,
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
// VERIFY ||A Ainv x - x||/||x|| certifies the DEVICE slab + split-K path at the
|
// VERIFY ||A Ainv x - x||/||x|| certifies the DEVICE slab + split-K path at the
|
||||||
// end of Import, since the single-RHS apply routes through the same core.
|
// end of Import, since the single-RHS apply routes through the same core.
|
||||||
//
|
//
|
||||||
// Tensor-depth agnostic: site scalar objects treated as contiguous ComplexD
|
// Tensor-depth agnostic: site scalar objects treated as contiguous coarse
|
||||||
|
// scalars (ComplexF or ComplexD, following the coefficient precision)
|
||||||
// (iScalar wrappers add no data), so any MG level's coarse operator imports.
|
// (iScalar wrappers add no data), so any MG level's coarse operator imports.
|
||||||
//////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
@@ -85,6 +86,10 @@ public:
|
|||||||
typedef typename vobj::scalar_object sobj;
|
typedef typename vobj::scalar_object sobj;
|
||||||
typedef typename CoarseMatrix::vector_object Mvobj;
|
typedef typename CoarseMatrix::vector_object Mvobj;
|
||||||
typedef typename Mvobj::scalar_object Msobj;
|
typedef typename Mvobj::scalar_object Msobj;
|
||||||
|
// Scalar of the coarse site objects (ComplexF or ComplexD): the apply slab
|
||||||
|
// is always fp32 and the inversion always fp64, but the SOURCE of both --
|
||||||
|
// the coarse operator's matrix elements -- carries this precision.
|
||||||
|
typedef typename GridTypeMapper<CComplex>::scalar_type CoarseScalar;
|
||||||
|
|
||||||
GridBase *grid;
|
GridBase *grid;
|
||||||
int nd;
|
int nd;
|
||||||
@@ -115,11 +120,20 @@ public:
|
|||||||
std::vector<ComplexF> hY;
|
std::vector<ComplexF> hY;
|
||||||
int NK; // split-K chunk count (divides N)
|
int NK; // split-K chunk count (divides N)
|
||||||
|
|
||||||
|
// Resident device memory: the apply slab and its staging. deviceVector is
|
||||||
|
// not evictable, so this counts against the hard budget, not the cache.
|
||||||
|
uint64_t DeviceBytes(void)
|
||||||
|
{
|
||||||
|
return (uint64_t)(dSlab.capacity()+dX.capacity()+dY.capacity()+dG.capacity()+dPartial.capacity())*sizeof(ComplexF)
|
||||||
|
+ (uint64_t)dLex2Rank.capacity()*sizeof(int)
|
||||||
|
+ (uint64_t)dRm2G.capacity()*sizeof(int64_t);
|
||||||
|
}
|
||||||
|
|
||||||
DenseCoarseMatrix(GridBase *g)
|
DenseCoarseMatrix(GridBase *g)
|
||||||
: grid(g)
|
: grid(g)
|
||||||
{
|
{
|
||||||
GRID_ASSERT( sizeof(sobj) == nbasis*sizeof(ComplexD) );
|
GRID_ASSERT( sizeof(sobj) == nbasis*sizeof(CoarseScalar) );
|
||||||
GRID_ASSERT( sizeof(Msobj) == nbasis*nbasis*sizeof(ComplexD) );
|
GRID_ASSERT( sizeof(Msobj) == nbasis*nbasis*sizeof(CoarseScalar) );
|
||||||
nd = grid->_ndimension;
|
nd = grid->_ndimension;
|
||||||
N = grid->gSites() * nbasis;
|
N = grid->gSites() * nbasis;
|
||||||
lsites = grid->lSites();
|
lsites = grid->lSites();
|
||||||
@@ -228,7 +242,7 @@ public:
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
{
|
{
|
||||||
Field x(grid); Field y(grid); Field z(grid);
|
Field x(grid); Field y(grid); Field z(grid);
|
||||||
x = ComplexD(1.0,0.0);
|
x = CoarseScalar(1.0,0.0);
|
||||||
double ta = usecond();
|
double ta = usecond();
|
||||||
(*this)(x, y);
|
(*this)(x, y);
|
||||||
double tb = usecond();
|
double tb = usecond();
|
||||||
@@ -263,7 +277,7 @@ public:
|
|||||||
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 = ComplexD(r+1.0,0.0)*in;
|
scaled = CoarseScalar(r+1.0,0.0)*in;
|
||||||
InsertSliceFast(scaled,min,r,0);
|
InsertSliceFast(scaled,min,r,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,13 +287,18 @@ public:
|
|||||||
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 - ComplexD(r+1.0,0.0)*out;
|
d = sr - CoarseScalar(r+1.0,0.0)*out;
|
||||||
RealD rel = std::sqrt(norm2(d)/norm2(sr));
|
RealD rel = std::sqrt(norm2(d)/norm2(sr));
|
||||||
if ( rel >= 1.0e-6 ) {
|
// Slices differ by rounding amplified by the operator's conditioning
|
||||||
|
// 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;
|
||||||
|
if ( rel >= otol ) {
|
||||||
std::cout << GridLogMessage << "DenseCoarseMatrix: oracle rhs "<<r
|
std::cout << GridLogMessage << "DenseCoarseMatrix: oracle rhs "<<r
|
||||||
<<" inconsistent with rhs 0, rel "<<rel<<std::endl;
|
<<" inconsistent with rhs 0, rel "<<rel<<std::endl;
|
||||||
}
|
}
|
||||||
GRID_ASSERT( rel < 1.0e-6 );
|
GRID_ASSERT( rel < otol );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,7 +332,7 @@ public:
|
|||||||
Lexicographic::IndexFromCoor(ncoor, nsite, gdims);
|
Lexicographic::IndexFromCoor(ncoor, nsite, gdims);
|
||||||
Msobj m;
|
Msobj m;
|
||||||
peekLocalSite(m, Av, myLcoor[ss]);
|
peekLocalSite(m, Av, myLcoor[ss]);
|
||||||
ComplexD *md = (ComplexD *)&m;
|
CoarseScalar *md = (CoarseScalar *)&m;
|
||||||
// The operator contracts out(s,b) = sum_a A[p](s)(a,b) in(nbr,a)
|
// The operator contracts out(s,b) = sum_a A[p](s)(a,b) in(nbr,a)
|
||||||
// (GeneralCoarsenedMatrix.h Mult kernel): the stored site matrix
|
// (GeneralCoarsenedMatrix.h Mult kernel): the stored site matrix
|
||||||
// acts TRANSPOSED, so element (a,b) lands at dense row (s,b),
|
// acts TRANSPOSED, so element (a,b) lands at dense row (s,b),
|
||||||
@@ -382,7 +401,7 @@ public:
|
|||||||
sobj s;
|
sobj s;
|
||||||
for(int b=0; b<nbasis; b++){
|
for(int b=0; b<nbasis; b++){
|
||||||
double ph = 0.37*(double)(myGsite[ss]*nbasis+b);
|
double ph = 0.37*(double)(myGsite[ss]*nbasis+b);
|
||||||
((ComplexD *)&s)[b] = ComplexD(std::cos(ph),std::sin(0.61*ph));
|
((CoarseScalar *)&s)[b] = CoarseScalar(std::cos(ph),std::sin(0.61*ph));
|
||||||
}
|
}
|
||||||
pokeLocalSite(s, x, myLcoor[ss]);
|
pokeLocalSite(s, x, myLcoor[ss]);
|
||||||
}
|
}
|
||||||
@@ -391,7 +410,7 @@ public:
|
|||||||
for(int ss=0; ss<lsites; ss++){
|
for(int ss=0; ss<lsites; ss++){
|
||||||
sobj s;
|
sobj s;
|
||||||
peekLocalSite(s, x, myLcoor[ss]);
|
peekLocalSite(s, x, myLcoor[ss]);
|
||||||
for(int b=0; b<nbasis; b++) xh[ myGsite[ss]*nbasis + b ] = ((ComplexD *)&s)[b];
|
for(int b=0; b<nbasis; b++) xh[ myGsite[ss]*nbasis + b ] = ComplexD(((CoarseScalar *)&s)[b]);
|
||||||
}
|
}
|
||||||
grid->GlobalSumVector(&xh[0], (int)N);
|
grid->GlobalSumVector(&xh[0], (int)N);
|
||||||
std::vector<ComplexD> yh(nrows);
|
std::vector<ComplexD> yh(nrows);
|
||||||
@@ -403,7 +422,7 @@ public:
|
|||||||
});
|
});
|
||||||
for(int ss=0; ss<lsites; ss++){
|
for(int ss=0; ss<lsites; ss++){
|
||||||
sobj s;
|
sobj s;
|
||||||
for(int b=0; b<nbasis; b++) ((ComplexD *)&s)[b] = yh[ss*nbasis+b];
|
for(int b=0; b<nbasis; b++) ((CoarseScalar *)&s)[b] = CoarseScalar(yh[ss*nbasis+b]);
|
||||||
pokeLocalSite(s, Dx, myLcoor[ss]);
|
pokeLocalSite(s, Dx, myLcoor[ss]);
|
||||||
}
|
}
|
||||||
ApplyOracle(Op, x, Ax);
|
ApplyOracle(Op, x, Ax);
|
||||||
@@ -414,7 +433,7 @@ public:
|
|||||||
if ( rel >= 1.0e-3 ) {
|
if ( rel >= 1.0e-3 ) {
|
||||||
std::cout << GridLogMessage << "DenseCoarseMatrix: IMPORT CERTIFICATE FAILED. If O(1), the "
|
std::cout << GridLogMessage << "DenseCoarseMatrix: IMPORT CERTIFICATE FAILED. If O(1), the "
|
||||||
<< "stencil shift-sign convention of the coarse operator has changed: "
|
<< "stencil shift-sign convention of the coarse operator has changed: "
|
||||||
<< "the import in ImportDense/ImportDenseFP64 must change with it"
|
<< "the import in ImportDense/ImportDenseForInversion must change with it"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
GRID_ASSERT(rel < 1.0e-3);
|
GRID_ASSERT(rel < 1.0e-3);
|
||||||
@@ -468,24 +487,25 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// 3b. Direct stencil -> fp64 rank-major import of MY ROWS of A (the
|
// 3b. Direct stencil -> rank-major import of MY ROWS of A in the
|
||||||
// end-to-end fp64 path: the stencil source IS ComplexD; nothing is
|
// inversion precision (DenseInverseScalar): the stencil source is
|
||||||
// rounded through fp32 on the way into the inversion). Same
|
// read once, straight into the buffer the Schur recursion factorises,
|
||||||
|
// never via the fp32 apply slab. Same
|
||||||
// loop/sign/accumulate/transposed-contraction discipline as
|
// loop/sign/accumulate/transposed-contraction discipline as
|
||||||
// ImportDense; output is column-major rows x N with columns in
|
// ImportDense; output is column-major rows x N with columns in
|
||||||
// rank-major order (g2rm).
|
// rank-major order (g2rm).
|
||||||
// ALWAYS-ON CERTIFICATE: the fp64 import, rounded, must agree with
|
// ALWAYS-ON CERTIFICATE: this import, rounded, must agree with
|
||||||
// the fp32 slab entry at the corresponding global column, over the
|
// the fp32 slab entry at the corresponding global column, over the
|
||||||
// WHOLE of my rows (few ulp: wrapped-shift collisions accumulate in
|
// WHOLE of my rows (few ulp: wrapped-shift collisions accumulate in
|
||||||
// different precision order). NaN-proof: non-finite entries are
|
// different precision order). NaN-proof: non-finite entries are
|
||||||
// counted explicitly since max() silently masks NaN.
|
// counted explicitly since max() silently masks NaN.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
template<class CoarseOp>
|
template<class CoarseOp>
|
||||||
void ImportDenseFP64(CoarseOp &Op, BlockRows &S, std::vector<int64_t> &g2rm)
|
void ImportDenseForInversion(CoarseOp &Op, BlockRows &S, std::vector<int64_t> &g2rm)
|
||||||
{
|
{
|
||||||
Coordinate gdims = grid->GlobalDimensions();
|
Coordinate gdims = grid->GlobalDimensions();
|
||||||
|
|
||||||
std::vector<ComplexD> h((uint64_t)nrows*N, ComplexD(0.0,0.0));
|
std::vector<DenseInverseScalar> h((uint64_t)nrows*N, DenseInverseScalar(0.0,0.0));
|
||||||
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];
|
||||||
@@ -502,7 +522,7 @@ public:
|
|||||||
Lexicographic::IndexFromCoor(ncoor, nsite, gdims);
|
Lexicographic::IndexFromCoor(ncoor, nsite, gdims);
|
||||||
Msobj m;
|
Msobj m;
|
||||||
peekLocalSite(m, Av, myLcoor[ss]);
|
peekLocalSite(m, Av, myLcoor[ss]);
|
||||||
ComplexD *md = (ComplexD *)&m;
|
CoarseScalar *md = (CoarseScalar *)&m;
|
||||||
// Transposed contraction as ImportDense: (a,b) lands at
|
// Transposed contraction as ImportDense: (a,b) lands at
|
||||||
// row (s,b), column (nbr,a); column index in rank-major order.
|
// row (s,b), column (nbr,a); column index in rank-major order.
|
||||||
for(int a=0; a<nbasis; a++)
|
for(int a=0; a<nbasis; a++)
|
||||||
@@ -510,7 +530,7 @@ public:
|
|||||||
int64_t jj = g2rm[ nsite*nbasis + a ];
|
int64_t jj = g2rm[ nsite*nbasis + a ];
|
||||||
for(int b=0; b<nbasis; b++)
|
for(int b=0; b<nbasis; b++)
|
||||||
{
|
{
|
||||||
h[(uint64_t)(ss*nbasis+b) + (uint64_t)jj*nrows] += md[a*nbasis+b];
|
h[(uint64_t)(ss*nbasis+b) + (uint64_t)jj*nrows] += DenseInverseScalar(md[a*nbasis+b]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -523,7 +543,7 @@ public:
|
|||||||
{
|
{
|
||||||
for(int64_t gcol=0; gcol<N; gcol++)
|
for(int64_t gcol=0; gcol<N; gcol++)
|
||||||
{
|
{
|
||||||
ComplexD d64 = h[(uint64_t)(i + g2rm[gcol]*nrows)];
|
ComplexD d64 = ComplexD(h[(uint64_t)(i + g2rm[gcol]*nrows)]);
|
||||||
ComplexF f32 = slab[(uint64_t)i*N + gcol];
|
ComplexF f32 = slab[(uint64_t)i*N + gcol];
|
||||||
double dev = abs(ComplexD(f32) - d64);
|
double dev = abs(ComplexD(f32) - d64);
|
||||||
if ( !std::isfinite(dev) ) nbad++;
|
if ( !std::isfinite(dev) ) nbad++;
|
||||||
@@ -534,21 +554,22 @@ public:
|
|||||||
RealD gbad = (RealD)nbad;
|
RealD gbad = (RealD)nbad;
|
||||||
grid->GlobalMax(gmx);
|
grid->GlobalMax(gmx);
|
||||||
grid->GlobalSumVector(&gbad, 1);
|
grid->GlobalSumVector(&gbad, 1);
|
||||||
std::cout << GridLogMessage << "DenseCoarseMatrix: fp64 import certificate "
|
std::cout << GridLogMessage << "DenseCoarseMatrix: inversion-source import certificate "
|
||||||
<< "max|A64 - A32| = " << gmx
|
<< "max|A_inv - A_slab| = " << gmx
|
||||||
<< " non-finite entries " << (int64_t)gbad << std::endl;
|
<< " non-finite entries " << (int64_t)gbad << std::endl;
|
||||||
GRID_ASSERT( gbad == 0 );
|
GRID_ASSERT( gbad == 0 );
|
||||||
GRID_ASSERT( gmx < 1.0e-5 );
|
GRID_ASSERT( gmx < 1.0e-5 );
|
||||||
|
|
||||||
S.Resize(nrows, N);
|
S.Resize(nrows, N);
|
||||||
acceleratorCopyToDevice(&h[0], &S.data[0], (uint64_t)nrows*N*sizeof(ComplexD));
|
acceleratorCopyToDevice(&h[0], &S.data[0], (uint64_t)nrows*N*sizeof(DenseInverseScalar));
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// 3c. The inverse: distributed recursive Schur, END-TO-END fp64.
|
// 3c. The inverse: distributed recursive Schur, end to end in the
|
||||||
// stencil (ComplexD) -> fp64 rank-major import -> fp64 recursion ->
|
// inversion precision (DenseInverseScalar, a configure-time choice):
|
||||||
// ONE terminal rounding into the fp32 apply slab. Everything
|
// stencil -> rank-major import -> recursion -> ONE terminal rounding
|
||||||
// downstream (device residency, split-K apply, VERIFY) is fp32.
|
// into the fp32 apply slab. Everything downstream (device
|
||||||
|
// residency, split-K apply, VERIFY) is fp32 regardless.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
template<class CoarseOp>
|
template<class CoarseOp>
|
||||||
void InvertDense(CoarseOp &Op)
|
void InvertDense(CoarseOp &Op)
|
||||||
@@ -578,7 +599,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
BlockRows S;
|
BlockRows S;
|
||||||
ImportDenseFP64(Op, S, g2rm);
|
ImportDenseForInversion(Op, S, g2rm);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
// The 2D block-cyclic recursion (BlockCyclicSchurInverse).
|
// The 2D block-cyclic recursion (BlockCyclicSchurInverse).
|
||||||
@@ -609,8 +630,8 @@ public:
|
|||||||
// The single terminal rounding: fp64 inverse -> fp32 apply slab
|
// The single terminal rounding: fp64 inverse -> fp32 apply slab
|
||||||
// (row-major, global columns)
|
// (row-major, global columns)
|
||||||
{
|
{
|
||||||
std::vector<ComplexD> h((uint64_t)nrows*N);
|
std::vector<DenseInverseScalar> h((uint64_t)nrows*N);
|
||||||
acceleratorCopyFromDevice(&S.data[0], &h[0], (uint64_t)nrows*N*sizeof(ComplexD));
|
acceleratorCopyFromDevice(&S.data[0], &h[0], (uint64_t)nrows*N*sizeof(DenseInverseScalar));
|
||||||
thread_for(gcol, N, {
|
thread_for(gcol, N, {
|
||||||
int64_t jj = g2rm[gcol];
|
int64_t jj = g2rm[gcol];
|
||||||
for(int64_t i=0; i<nrows; i++)
|
for(int64_t i=0; i<nrows; i++)
|
||||||
@@ -620,7 +641,9 @@ public:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
double t4 = usecond();
|
double t4 = usecond();
|
||||||
std::cout << GridLogMessage << "DenseCoarseMatrix: SCHUR fp64 distributed invert took "
|
std::cout << GridLogMessage << "DenseCoarseMatrix: SCHUR "
|
||||||
|
<< (sizeof(DenseInverseScalar)==sizeof(ComplexF) ? "fp32" : "fp64")
|
||||||
|
<< " distributed invert took "
|
||||||
<< (t4-t1)/1.0e6 << " s (recursion " << (t3-t2)/1.0e6 << " s)" << std::endl;
|
<< (t4-t1)/1.0e6 << " s (recursion " << (t3-t2)/1.0e6 << " s)" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -717,7 +740,7 @@ public:
|
|||||||
sobj s;
|
sobj s;
|
||||||
peekLocalSite(s, src, myLcoor[ss]);
|
peekLocalSite(s, src, myLcoor[ss]);
|
||||||
for(int b=0; b<nbasis; b++)
|
for(int b=0; b<nbasis; b++)
|
||||||
hX[ myGsite[ss]*nbasis + b ] = ComplexF(((ComplexD *)&s)[b]);
|
hX[ myGsite[ss]*nbasis + b ] = ComplexF(((CoarseScalar *)&s)[b]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SlabApplyPacked(1, nullptr);
|
SlabApplyPacked(1, nullptr);
|
||||||
@@ -725,7 +748,7 @@ public:
|
|||||||
for(int ss=0; ss<lsites; ss++){
|
for(int ss=0; ss<lsites; ss++){
|
||||||
sobj s;
|
sobj s;
|
||||||
for(int b=0; b<nbasis; b++)
|
for(int b=0; b<nbasis; b++)
|
||||||
((ComplexD *)&s)[b] = ComplexD(hY[ss*nbasis + b]);
|
((CoarseScalar *)&s)[b] = CoarseScalar(hY[ss*nbasis + b]);
|
||||||
pokeLocalSite(s, psi, myLcoor[ss]);
|
pokeLocalSite(s, psi, myLcoor[ss]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -762,7 +785,7 @@ public:
|
|||||||
sobj s;
|
sobj s;
|
||||||
peekLocalSite(s, src[rr], myLcoor[ss]);
|
peekLocalSite(s, src[rr], myLcoor[ss]);
|
||||||
for(int b=0; b<nbasis; b++)
|
for(int b=0; b<nbasis; b++)
|
||||||
hX[ (uint64_t)rr*N + myGsite[ss]*nbasis + b ] = ComplexF(((ComplexD *)&s)[b]);
|
hX[ (uint64_t)rr*N + myGsite[ss]*nbasis + b ] = ComplexF(((CoarseScalar *)&s)[b]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -772,7 +795,7 @@ public:
|
|||||||
for(int ss=0; ss<lsites; ss++){
|
for(int ss=0; ss<lsites; ss++){
|
||||||
sobj s;
|
sobj s;
|
||||||
for(int b=0; b<nbasis; b++)
|
for(int b=0; b<nbasis; b++)
|
||||||
((ComplexD *)&s)[b] = ComplexD(hY[(uint64_t)rr*nrows + (ss*nbasis+b)]);
|
((CoarseScalar *)&s)[b] = CoarseScalar(hY[(uint64_t)rr*nrows + (ss*nbasis+b)]);
|
||||||
pokeLocalSite(s, psi[rr], myLcoor[ss]);
|
pokeLocalSite(s, psi[rr], myLcoor[ss]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -815,7 +838,7 @@ public:
|
|||||||
sobj s;
|
sobj s;
|
||||||
peekLocalSite(s, iv, c6);
|
peekLocalSite(s, iv, c6);
|
||||||
for(int b=0; b<nbasis; b++)
|
for(int b=0; b<nbasis; b++)
|
||||||
hX[(uint64_t)rr*N + myGsite[ss]*nbasis + b] = ComplexF(((ComplexD *)&s)[b]);
|
hX[(uint64_t)rr*N + myGsite[ss]*nbasis + b] = ComplexF(((CoarseScalar *)&s)[b]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -832,7 +855,7 @@ public:
|
|||||||
c6[0] = rr;
|
c6[0] = rr;
|
||||||
sobj s;
|
sobj s;
|
||||||
for(int b=0; b<nbasis; b++)
|
for(int b=0; b<nbasis; b++)
|
||||||
((ComplexD *)&s)[b] = ComplexD(hY[(uint64_t)rr*nrows + (ss*nbasis+b)]); // Y col-major
|
((CoarseScalar *)&s)[b] = CoarseScalar(hY[(uint64_t)rr*nrows + (ss*nbasis+b)]); // Y col-major
|
||||||
pokeLocalSite(s, ov, c6);
|
pokeLocalSite(s, ov, c6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,704 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/GeneralCoarsenedMatrix.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Peter Boyle <pboyle@bnl.gov>
|
|
||||||
|
|
||||||
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 <Grid/qcd/QCD.h> // needed for Dagger(Yes|No), Inverse(Yes|No)
|
|
||||||
|
|
||||||
#include <Grid/lattice/PaddedCell.h>
|
|
||||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
|
||||||
#include <Grid/algorithms/deflation/MultiRHSBlockProject.h>
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
// Fine Object == (per site) type of fine field
|
|
||||||
// nbasis == number of deflation vectors
|
|
||||||
template<class Fobj,class CComplex,int nbasis>
|
|
||||||
class GeneralCoarsenedMatrix : public SparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > {
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef GeneralCoarsenedMatrix<Fobj,CComplex,nbasis> GeneralCoarseOp;
|
|
||||||
typedef iVector<CComplex,nbasis > siteVector;
|
|
||||||
typedef iMatrix<CComplex,nbasis > siteMatrix;
|
|
||||||
typedef Lattice<iScalar<CComplex> > CoarseComplexField;
|
|
||||||
typedef Lattice<siteVector> CoarseVector;
|
|
||||||
typedef Lattice<iMatrix<CComplex,nbasis > > CoarseMatrix;
|
|
||||||
typedef iMatrix<CComplex,nbasis > Cobj;
|
|
||||||
typedef iVector<CComplex,nbasis > Cvec;
|
|
||||||
typedef Lattice< CComplex > CoarseScalar; // used for inner products on fine field
|
|
||||||
typedef Lattice<Fobj > FineField;
|
|
||||||
typedef Lattice<CComplex > FineComplexField;
|
|
||||||
typedef CoarseVector Field;
|
|
||||||
////////////////////
|
|
||||||
// Data members
|
|
||||||
////////////////////
|
|
||||||
int hermitian;
|
|
||||||
GridBase * _FineGrid;
|
|
||||||
GridCartesian * _CoarseGrid;
|
|
||||||
NonLocalStencilGeometry &geom;
|
|
||||||
PaddedCell Cell;
|
|
||||||
GeneralLocalStencil Stencil;
|
|
||||||
|
|
||||||
std::vector<CoarseMatrix> _A;
|
|
||||||
std::vector<CoarseMatrix> _Adag;
|
|
||||||
std::vector<CoarseVector> MultTemporaries;
|
|
||||||
|
|
||||||
int64_t MultCalls;
|
|
||||||
double MultFlopsAccum;
|
|
||||||
double MultUsecAccum;
|
|
||||||
|
|
||||||
///////////////////////
|
|
||||||
// Interface
|
|
||||||
///////////////////////
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Bilingual accessors: everything a consumer needs to read the operator
|
|
||||||
// without knowing which of the three coarse classes it holds. The D
|
|
||||||
// dimensional grid the elements live on, the geometry they are indexed by,
|
|
||||||
// and one unpadded point at a time (a whole npoint vector is too much
|
|
||||||
// memory at production nbasis).
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
GridCartesian * CoarseGridD(void) { return _CoarseGrid; };
|
|
||||||
NonLocalStencilGeometry & Geometry(void) { return geom; };
|
|
||||||
void ExtractMatrix(int p,CoarseMatrix &A) { A = Cell.Extract(_A[p]); };
|
|
||||||
|
|
||||||
GridBase * Grid(void) { return _CoarseGrid; }; // this is all the linalg routines need to know
|
|
||||||
GridBase * FineGrid(void) { return _FineGrid; }; // this is all the linalg routines need to know
|
|
||||||
GridCartesian * CoarseGrid(void) { return _CoarseGrid; }; // this is all the linalg routines need to know
|
|
||||||
|
|
||||||
/* void ShiftMatrix(RealD shift)
|
|
||||||
{
|
|
||||||
int Nd=_FineGrid->Nd();
|
|
||||||
Coordinate zero_shift(Nd,0);
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
if ( zero_shift==geom.shifts[p] ) {
|
|
||||||
_A[p] = _A[p]+shift;
|
|
||||||
// _Adag[p] = _Adag[p]+shift;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void ProjectNearestNeighbour(RealD shift, GeneralCoarseOp &CopyMe)
|
|
||||||
{
|
|
||||||
int nfound=0;
|
|
||||||
std::cout << GridLogMessage <<"GeneralCoarsenedMatrix::ProjectNearestNeighbour "<< CopyMe._A[0].Grid()<<std::endl;
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
for(int pp=0;pp<CopyMe.geom.npoint;pp++){
|
|
||||||
// Search for the same relative shift
|
|
||||||
// Avoids brutal handling of Grid pointers
|
|
||||||
if ( CopyMe.geom.shifts[pp]==geom.shifts[p] ) {
|
|
||||||
_A[p] = CopyMe.Cell.Extract(CopyMe._A[pp]);
|
|
||||||
// _Adag[p] = CopyMe.Cell.Extract(CopyMe._Adag[pp]);
|
|
||||||
nfound++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GRID_ASSERT(nfound==geom.npoint);
|
|
||||||
ExchangeCoarseLinks();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
GeneralCoarsenedMatrix(NonLocalStencilGeometry &_geom,GridBase *FineGrid, GridCartesian * CoarseGrid,int _herm=1)
|
|
||||||
: geom(_geom),
|
|
||||||
_FineGrid(FineGrid),
|
|
||||||
_CoarseGrid(CoarseGrid),
|
|
||||||
hermitian(_herm),
|
|
||||||
Cell(_geom.Depth(),_CoarseGrid),
|
|
||||||
Stencil(Cell.grids.back(),geom.shifts),
|
|
||||||
MultCalls(0), MultFlopsAccum(0.0), MultUsecAccum(0.0)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
int npoint = _geom.npoint;
|
|
||||||
}
|
|
||||||
_A.resize(geom.npoint,CoarseGrid);
|
|
||||||
if ( !hermitian ) _Adag.resize(geom.npoint,CoarseGrid);
|
|
||||||
}
|
|
||||||
void M (const CoarseVector &in, CoarseVector &out)
|
|
||||||
{
|
|
||||||
Mult(_A,in,out);
|
|
||||||
}
|
|
||||||
void Mdag (const CoarseVector &in, CoarseVector &out)
|
|
||||||
{
|
|
||||||
if(hermitian)
|
|
||||||
Mult(_A,in,out);
|
|
||||||
else
|
|
||||||
Mult(_Adag,in,out);
|
|
||||||
}
|
|
||||||
void Mult (std::vector<CoarseMatrix> &A,const CoarseVector &in, CoarseVector &out)
|
|
||||||
{
|
|
||||||
RealD tviews=0; RealD ttot=0; RealD tmult=0; RealD texch=0; RealD text=0; RealD ttemps=0; RealD tcopy=0;
|
|
||||||
RealD tmult2=0;
|
|
||||||
|
|
||||||
ttot=-usecond();
|
|
||||||
conformable(CoarseGrid(),in.Grid());
|
|
||||||
conformable(in.Grid(),out.Grid());
|
|
||||||
out.Checkerboard() = in.Checkerboard();
|
|
||||||
CoarseVector tin=in;
|
|
||||||
|
|
||||||
texch-=usecond();
|
|
||||||
CoarseVector pin = Cell.ExchangePeriodic(tin);
|
|
||||||
texch+=usecond();
|
|
||||||
|
|
||||||
CoarseVector pout(pin.Grid());
|
|
||||||
|
|
||||||
int npoint = geom.npoint;
|
|
||||||
typedef LatticeView<Cobj> Aview;
|
|
||||||
typedef LatticeView<Cvec> Vview;
|
|
||||||
|
|
||||||
const int Nsimd = CComplex::Nsimd();
|
|
||||||
|
|
||||||
int64_t osites=pin.Grid()->oSites();
|
|
||||||
|
|
||||||
RealD flops = 1.0* npoint * nbasis * nbasis * 8.0 * osites * CComplex::Nsimd();
|
|
||||||
RealD bytes = 1.0*osites*sizeof(siteMatrix)*npoint
|
|
||||||
+ 2.0*osites*sizeof(siteVector)*npoint;
|
|
||||||
|
|
||||||
{
|
|
||||||
tviews-=usecond();
|
|
||||||
autoView( in_v , pin, AcceleratorRead);
|
|
||||||
autoView( out_v , pout, AcceleratorWriteDiscard);
|
|
||||||
autoView( Stencil_v , Stencil, AcceleratorRead);
|
|
||||||
tviews+=usecond();
|
|
||||||
|
|
||||||
// Static and prereserve to keep UVM region live and not resized across multiple calls
|
|
||||||
ttemps-=usecond();
|
|
||||||
MultTemporaries.resize(npoint,pin.Grid());
|
|
||||||
ttemps+=usecond();
|
|
||||||
std::vector<Aview> AcceleratorViewContainer_h;
|
|
||||||
std::vector<Vview> AcceleratorVecViewContainer_h;
|
|
||||||
|
|
||||||
tviews-=usecond();
|
|
||||||
for(int p=0;p<npoint;p++) {
|
|
||||||
AcceleratorViewContainer_h.push_back( A[p].View(AcceleratorRead));
|
|
||||||
AcceleratorVecViewContainer_h.push_back(MultTemporaries[p].View(AcceleratorWrite));
|
|
||||||
}
|
|
||||||
tviews+=usecond();
|
|
||||||
|
|
||||||
static deviceVector<Aview> AcceleratorViewContainer; AcceleratorViewContainer.resize(npoint);
|
|
||||||
static deviceVector<Vview> AcceleratorVecViewContainer; AcceleratorVecViewContainer.resize(npoint);
|
|
||||||
|
|
||||||
auto Aview_p = &AcceleratorViewContainer[0];
|
|
||||||
auto Vview_p = &AcceleratorVecViewContainer[0];
|
|
||||||
tcopy-=usecond();
|
|
||||||
acceleratorCopyToDevice(&AcceleratorViewContainer_h[0],&AcceleratorViewContainer[0],npoint *sizeof(Aview));
|
|
||||||
acceleratorCopyToDevice(&AcceleratorVecViewContainer_h[0],&AcceleratorVecViewContainer[0],npoint *sizeof(Vview));
|
|
||||||
tcopy+=usecond();
|
|
||||||
|
|
||||||
tmult-=usecond();
|
|
||||||
accelerator_for(spb, osites*nbasis*npoint, Nsimd, {
|
|
||||||
typedef decltype(coalescedRead(in_v[0](0))) calcComplex;
|
|
||||||
int32_t ss = spb/(nbasis*npoint);
|
|
||||||
int32_t bp = spb%(nbasis*npoint);
|
|
||||||
int32_t point= bp/nbasis;
|
|
||||||
int32_t b = bp%nbasis;
|
|
||||||
auto SE = Stencil_v.GetEntry(point,ss);
|
|
||||||
auto nbr = coalescedReadGeneralPermute(in_v[SE->_offset],SE->_permute,Nd);
|
|
||||||
auto res = coalescedRead(Aview_p[point][ss](0,b))*nbr(0);
|
|
||||||
for(int bb=1;bb<nbasis;bb++) {
|
|
||||||
res = res + coalescedRead(Aview_p[point][ss](bb,b))*nbr(bb);
|
|
||||||
}
|
|
||||||
coalescedWrite(Vview_p[point][ss](b),res);
|
|
||||||
});
|
|
||||||
tmult2-=usecond();
|
|
||||||
accelerator_for(sb, osites*nbasis, Nsimd, {
|
|
||||||
int ss = sb/nbasis;
|
|
||||||
int b = sb%nbasis;
|
|
||||||
auto res = coalescedRead(Vview_p[0][ss](b));
|
|
||||||
for(int point=1;point<npoint;point++){
|
|
||||||
res = res + coalescedRead(Vview_p[point][ss](b));
|
|
||||||
}
|
|
||||||
coalescedWrite(out_v[ss](b),res);
|
|
||||||
});
|
|
||||||
tmult2+=usecond();
|
|
||||||
tmult+=usecond();
|
|
||||||
for(int p=0;p<npoint;p++) {
|
|
||||||
AcceleratorViewContainer_h[p].ViewClose();
|
|
||||||
AcceleratorVecViewContainer_h[p].ViewClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
text-=usecond();
|
|
||||||
out = Cell.Extract(pout);
|
|
||||||
text+=usecond();
|
|
||||||
ttot+=usecond();
|
|
||||||
|
|
||||||
MultCalls++;
|
|
||||||
MultFlopsAccum += flops;
|
|
||||||
MultUsecAccum += ttot;
|
|
||||||
std::cout << GridLogPerformance
|
|
||||||
<< "Coarse Mult call " << MultCalls
|
|
||||||
<< " tot " << ttot << " us"
|
|
||||||
<< " kernel " << tmult << " us"
|
|
||||||
<< " kernel " << flops/tmult*1e-3 << " GFlop/s"
|
|
||||||
<< " overall " << MultFlopsAccum/MultUsecAccum*1e-3 << " GFlop/s (cumul)"
|
|
||||||
<< " bw " << bytes/tmult*1e-3 << " GB/s"
|
|
||||||
<< std::endl;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
void PopulateAdag(void)
|
|
||||||
{
|
|
||||||
#if 0
|
|
||||||
// Serial global peek/poke reference implementation
|
|
||||||
for(int64_t bidx=0;bidx<CoarseGrid()->gSites() ;bidx++){
|
|
||||||
Coordinate bcoor;
|
|
||||||
CoarseGrid()->GlobalIndexToGlobalCoor(bidx,bcoor);
|
|
||||||
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
Coordinate scoor = bcoor;
|
|
||||||
for(int mu=0;mu<bcoor.size();mu++){
|
|
||||||
int L = CoarseGrid()->GlobalDimensions()[mu];
|
|
||||||
scoor[mu] = (bcoor[mu] - geom.shifts[p][mu] + L) % L; // Modulo arithmetic
|
|
||||||
}
|
|
||||||
// Flip to poke/peekLocalSite and not too bad
|
|
||||||
auto link = peekSite(_A[p],scoor);
|
|
||||||
int pp = geom.Reverse(p);
|
|
||||||
pokeSite(adj(link),_Adag[pp],bcoor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
// Parallel: _Adag[pp](x) = adj( _A[p](x + s_pp) ), pp = Reverse(p), s_pp = -s_p.
|
|
||||||
// The neighbour fetch reuses the same padded-cell + stencil machinery as Mult,
|
|
||||||
// reading one matrix element per coalesced access so no whole site matrix
|
|
||||||
// (230KB at nbasis=60) ever lands on a GPU thread stack (HIP limit 128KB).
|
|
||||||
// Halo sites compute garbage neighbours; Cell.Extract discards them.
|
|
||||||
// Must run on the unpadded _A, i.e. before ExchangeCoarseLinks.
|
|
||||||
const int Nsimd = CComplex::Nsimd();
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
int pp = geom.Reverse(p);
|
|
||||||
CoarseMatrix Apad = Cell.ExchangePeriodic(_A[p]);
|
|
||||||
CoarseMatrix Dpad(Apad.Grid());
|
|
||||||
int64_t osites = Apad.Grid()->oSites();
|
|
||||||
{
|
|
||||||
autoView( Apad_v , Apad, AcceleratorRead);
|
|
||||||
autoView( Dpad_v , Dpad, AcceleratorWriteDiscard);
|
|
||||||
autoView( Stencil_v, Stencil, AcceleratorRead);
|
|
||||||
accelerator_for(sj, osites*nbasis, Nsimd, {
|
|
||||||
int32_t ss = sj/nbasis;
|
|
||||||
int32_t j = sj%nbasis;
|
|
||||||
auto SE = Stencil_v.GetEntry(pp,ss);
|
|
||||||
for(int i=0;i<nbasis;i++){
|
|
||||||
auto z = coalescedReadGeneralPermute(Apad_v[SE->_offset](i,j),SE->_permute,Nd);
|
|
||||||
coalescedWrite(Dpad_v[ss](j,i),conjugate(z));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_Adag[pp] = Cell.Extract(Dpad);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// A) Only reduced flops option is to use a padded cell of depth 4
|
|
||||||
// and apply MpcDagMpc in the padded cell.
|
|
||||||
//
|
|
||||||
// Makes for ONE application of MpcDagMpc per vector instead of 30 or 80.
|
|
||||||
// With the effective cell size around (B+8)^4 perhaps 12^4/4^4 ratio
|
|
||||||
// Cost is 81x more, same as stencil size.
|
|
||||||
//
|
|
||||||
// But: can eliminate comms and do as local dirichlet.
|
|
||||||
//
|
|
||||||
// Local exchange gauge field once.
|
|
||||||
// Apply to all vectors, local only computation.
|
|
||||||
// Must exchange ghost subcells in reverse process of PaddedCell to take inner products
|
|
||||||
//
|
|
||||||
// B) Can reduce cost: pad by 1, apply Deo (4^4+6^4+8^4+8^4 )/ (4x 4^4)
|
|
||||||
// pad by 2, apply Doe
|
|
||||||
// pad by 3, apply Deo
|
|
||||||
// then break out 8x directions; cost is ~10x MpcDagMpc per vector
|
|
||||||
//
|
|
||||||
// => almost factor of 10 in setup cost, excluding data rearrangement
|
|
||||||
//
|
|
||||||
// Intermediates -- ignore the corner terms, leave approximate and force Hermitian
|
|
||||||
// Intermediates -- pad by 2 and apply 1+8+24 = 33 times.
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////
|
|
||||||
// BFM HDCG style approach: Solve a system of equations to get Aij
|
|
||||||
//////////////////////////////////////////////////////////
|
|
||||||
/*
|
|
||||||
* Here, k,l index which possible shift within the 3^Nd "ball" connected by MdagM.
|
|
||||||
*
|
|
||||||
* conj(phases[block]) proj[k][ block*Nvec+j ] = \sum_ball e^{i q_k . delta} < phi_{block,j} | MdagM | phi_{(block+delta),i} >
|
|
||||||
* = \sum_ball e^{iqk.delta} A_ji
|
|
||||||
*
|
|
||||||
* Must invert matrix M_k,l = e^[i q_k . delta_l]
|
|
||||||
*
|
|
||||||
* Where q_k = delta_k . (2*M_PI/global_nb[mu])
|
|
||||||
*/
|
|
||||||
#if 0
|
|
||||||
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
|
|
||||||
Aggregation<Fobj,CComplex,nbasis> & Subspace)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage<< "GeneralCoarsenMatrix "<< std::endl;
|
|
||||||
GridBase *grid = FineGrid();
|
|
||||||
|
|
||||||
RealD tproj=0.0;
|
|
||||||
RealD teigen=0.0;
|
|
||||||
RealD tmat=0.0;
|
|
||||||
RealD tphase=0.0;
|
|
||||||
RealD tinv=0.0;
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
// Orthogonalise the subblocks over the basis
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
CoarseScalar InnerProd(CoarseGrid());
|
|
||||||
blockOrthogonalise(InnerProd,Subspace.subspace);
|
|
||||||
|
|
||||||
const int npoint = geom.npoint;
|
|
||||||
|
|
||||||
Coordinate clatt = CoarseGrid()->GlobalDimensions();
|
|
||||||
int Nd = CoarseGrid()->Nd();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Here, k,l index which possible momentum/shift within the N-points connected by MdagM.
|
|
||||||
* Matrix index i is mapped to this shift via
|
|
||||||
* geom.shifts[i]
|
|
||||||
*
|
|
||||||
* conj(pha[block]) proj[k (which mom)][j (basis vec cpt)][block]
|
|
||||||
* = \sum_{l in ball} e^{i q_k . delta_l} < phi_{block,j} | MdagM | phi_{(block+delta_l),i} >
|
|
||||||
* = \sum_{l in ball} e^{iqk.delta_l} A_ji^{b.b+l}
|
|
||||||
* = M_{kl} A_ji^{b.b+l}
|
|
||||||
*
|
|
||||||
* Must assemble and invert matrix M_k,l = e^[i q_k . delta_l]
|
|
||||||
*
|
|
||||||
* Where q_k = delta_k . (2*M_PI/global_nb[mu])
|
|
||||||
*
|
|
||||||
* Then A{ji}^{b,b+l} = M^{-1}_{lm} ComputeProj_{m,b,i,j}
|
|
||||||
*/
|
|
||||||
teigen-=usecond();
|
|
||||||
Eigen::MatrixXcd Mkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
|
||||||
Eigen::MatrixXcd invMkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
|
||||||
ComplexD ci(0.0,1.0);
|
|
||||||
for(int k=0;k<npoint;k++){ // Loop over momenta
|
|
||||||
|
|
||||||
for(int l=0;l<npoint;l++){ // Loop over nbr relative
|
|
||||||
ComplexD phase(0.0,0.0);
|
|
||||||
for(int mu=0;mu<Nd;mu++){
|
|
||||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
|
||||||
phase=phase+TwoPiL*geom.shifts[k][mu]*geom.shifts[l][mu];
|
|
||||||
}
|
|
||||||
phase=exp(phase*ci);
|
|
||||||
Mkl(k,l) = phase;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
invMkl = Mkl.inverse();
|
|
||||||
teigen+=usecond();
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// Now compute the matrix elements of linop between the orthonormal
|
|
||||||
// set of vectors.
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
FineField phaV(grid); // Phased block basis vector
|
|
||||||
FineField MphaV(grid);// Matrix applied
|
|
||||||
CoarseVector coarseInner(CoarseGrid());
|
|
||||||
|
|
||||||
std::vector<CoarseVector> ComputeProj(npoint,CoarseGrid());
|
|
||||||
std::vector<CoarseVector> FT(npoint,CoarseGrid());
|
|
||||||
for(int i=0;i<nbasis;i++){// Loop over basis vectors
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<"/"<<nbasis<< std::endl;
|
|
||||||
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
// Stick a phase on every block
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
tphase-=usecond();
|
|
||||||
CoarseComplexField coor(CoarseGrid());
|
|
||||||
CoarseComplexField pha(CoarseGrid()); pha=Zero();
|
|
||||||
for(int mu=0;mu<Nd;mu++){
|
|
||||||
LatticeCoordinate(coor,mu);
|
|
||||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
|
||||||
pha = pha + (TwoPiL * geom.shifts[p][mu]) * coor;
|
|
||||||
}
|
|
||||||
pha =exp(pha*ci);
|
|
||||||
phaV=Zero();
|
|
||||||
blockZAXPY(phaV,pha,Subspace.subspace[i],phaV);
|
|
||||||
tphase+=usecond();
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// Multiple phased subspace vector by matrix and project to subspace
|
|
||||||
// Remove local bulk phase to leave relative phases
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
tmat-=usecond();
|
|
||||||
linop.Op(phaV,MphaV);
|
|
||||||
tmat+=usecond();
|
|
||||||
|
|
||||||
tproj-=usecond();
|
|
||||||
blockProject(coarseInner,MphaV,Subspace.subspace);
|
|
||||||
coarseInner = conjugate(pha) * coarseInner;
|
|
||||||
|
|
||||||
ComputeProj[p] = coarseInner;
|
|
||||||
tproj+=usecond();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
tinv-=usecond();
|
|
||||||
for(int k=0;k<npoint;k++){
|
|
||||||
FT[k] = Zero();
|
|
||||||
for(int l=0;l<npoint;l++){
|
|
||||||
FT[k]= FT[k]+ invMkl(l,k)*ComputeProj[l];
|
|
||||||
}
|
|
||||||
|
|
||||||
int osites=CoarseGrid()->oSites();
|
|
||||||
autoView( A_v , _A[k], AcceleratorWrite);
|
|
||||||
autoView( FT_v , FT[k], AcceleratorRead);
|
|
||||||
accelerator_for(sss, osites, nbasis, {
|
|
||||||
#ifdef GRID_SIMT
|
|
||||||
int j = acceleratorSIMTlane(nbasis);
|
|
||||||
A_v[sss](i,j) = FT_v[sss](j);
|
|
||||||
#else
|
|
||||||
// CPU build: acceleratorSIMTlane()==0 -- an un-looped SIMT tensor
|
|
||||||
// index writes ONLY j=0 and silently drops the other nbasis-1
|
|
||||||
// columns (caught by Test_schur_dense_coarse import certificate,
|
|
||||||
// 2026-08-14). Loop explicitly.
|
|
||||||
for(int j=0;j<nbasis;j++){
|
|
||||||
A_v[sss](i,j) = FT_v[sss](j);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
});
|
|
||||||
}
|
|
||||||
tinv+=usecond();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only needed if nonhermitian
|
|
||||||
if ( ! hermitian ) {
|
|
||||||
std::cout << GridLogMessage<<"PopulateAdag "<<std::endl;
|
|
||||||
PopulateAdag();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Need to write something to populate Adag from A
|
|
||||||
ExchangeCoarseLinks();
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator eigen "<<teigen<<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator phase "<<tphase<<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator mat "<<tmat <<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator proj "<<tproj<<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator inv "<<tinv<<" us"<<std::endl;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// Galerkin projection of matrix
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
|
|
||||||
Aggregation<Fobj,CComplex,nbasis> & Subspace)
|
|
||||||
{
|
|
||||||
CoarsenOperator(linop,Subspace,Subspace);
|
|
||||||
}
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// Petrov - Galerkin projection of matrix
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
|
|
||||||
Aggregation<Fobj,CComplex,nbasis> & U,
|
|
||||||
Aggregation<Fobj,CComplex,nbasis> & V)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage<< "GeneralCoarsenMatrix "<< std::endl;
|
|
||||||
GridBase *grid = FineGrid();
|
|
||||||
|
|
||||||
RealD tproj=0.0;
|
|
||||||
RealD teigen=0.0;
|
|
||||||
RealD tmat=0.0;
|
|
||||||
RealD tphase=0.0;
|
|
||||||
RealD tphaseBZ=0.0;
|
|
||||||
RealD tinv=0.0;
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
// Orthogonalise the subblocks over the basis
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
CoarseScalar InnerProd(CoarseGrid());
|
|
||||||
blockOrthogonalise(InnerProd,V.subspace);
|
|
||||||
blockOrthogonalise(InnerProd,U.subspace);
|
|
||||||
|
|
||||||
const int npoint = geom.npoint;
|
|
||||||
|
|
||||||
Coordinate clatt = CoarseGrid()->GlobalDimensions();
|
|
||||||
int Nd = CoarseGrid()->Nd();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Here, k,l index which possible momentum/shift within the N-points connected by MdagM.
|
|
||||||
* Matrix index i is mapped to this shift via
|
|
||||||
* geom.shifts[i]
|
|
||||||
*
|
|
||||||
* conj(pha[block]) proj[k (which mom)][j (basis vec cpt)][block]
|
|
||||||
* = \sum_{l in ball} e^{i q_k . delta_l} < phi_{block,j} | MdagM | phi_{(block+delta_l),i} >
|
|
||||||
* = \sum_{l in ball} e^{iqk.delta_l} A_ji^{b.b+l}
|
|
||||||
* = M_{kl} A_ji^{b.b+l}
|
|
||||||
*
|
|
||||||
* Must assemble and invert matrix M_k,l = e^[i q_k . delta_l]
|
|
||||||
*
|
|
||||||
* Where q_k = delta_k . (2*M_PI/global_nb[mu])
|
|
||||||
*
|
|
||||||
* Then A{ji}^{b,b+l} = M^{-1}_{lm} ComputeProj_{m,b,i,j}
|
|
||||||
*/
|
|
||||||
teigen-=usecond();
|
|
||||||
Eigen::MatrixXcd Mkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
|
||||||
Eigen::MatrixXcd invMkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
|
||||||
ComplexD ci(0.0,1.0);
|
|
||||||
for(int k=0;k<npoint;k++){ // Loop over momenta
|
|
||||||
|
|
||||||
for(int l=0;l<npoint;l++){ // Loop over nbr relative
|
|
||||||
ComplexD phase(0.0,0.0);
|
|
||||||
for(int mu=0;mu<Nd;mu++){
|
|
||||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
|
||||||
phase=phase+TwoPiL*geom.shifts[k][mu]*geom.shifts[l][mu];
|
|
||||||
}
|
|
||||||
phase=exp(phase*ci);
|
|
||||||
Mkl(k,l) = phase;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
invMkl = Mkl.inverse();
|
|
||||||
teigen+=usecond();
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// Now compute the matrix elements of linop between the orthonormal
|
|
||||||
// set of vectors.
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
std::vector<FineComplexField> phaF(npoint,grid);
|
|
||||||
std::vector<CoarseComplexField> pha(npoint,CoarseGrid());
|
|
||||||
|
|
||||||
typedef typename CComplex::scalar_type SComplex;
|
|
||||||
FineComplexField one(grid); one=SComplex(1.0);
|
|
||||||
FineComplexField zz(grid); zz = Zero();
|
|
||||||
tphase=-usecond();
|
|
||||||
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
// Stick a phase on every block
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
CoarseComplexField coor(CoarseGrid());
|
|
||||||
pha[p]=Zero();
|
|
||||||
for(int mu=0;mu<Nd;mu++){
|
|
||||||
LatticeCoordinate(coor,mu);
|
|
||||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
|
||||||
pha[p] = pha[p] + (TwoPiL * geom.shifts[p][mu]) * coor;
|
|
||||||
}
|
|
||||||
pha[p] =exp(pha[p]*ci);
|
|
||||||
|
|
||||||
blockZAXPY(phaF[p],pha[p],one,zz);
|
|
||||||
|
|
||||||
}
|
|
||||||
tphase+=usecond();
|
|
||||||
|
|
||||||
// Import basis into BLAS layout once; blockProject then reads it once per
|
|
||||||
// basis vector rather than once per (i,p) as in scalar blockProject.
|
|
||||||
// Process all npoint in a single batch.
|
|
||||||
MultiRHSBlockProject<FineField> Projector;
|
|
||||||
Projector.Allocate(nbasis, grid, CoarseGrid());
|
|
||||||
Projector.ImportBasis(U.subspace);
|
|
||||||
|
|
||||||
std::vector<FineField> phaV_batch(npoint, grid);
|
|
||||||
std::vector<FineField> MphaV_batch(npoint, grid);
|
|
||||||
std::vector<CoarseVector> proj_batch(npoint, CoarseGrid());
|
|
||||||
std::vector<CoarseVector> ComputeProj(npoint, CoarseGrid());
|
|
||||||
std::vector<CoarseVector> FT(npoint, CoarseGrid());
|
|
||||||
|
|
||||||
// Pre-allocate BLAS_F and BLAS_C to avoid repeated hipMalloc/hipFree of
|
|
||||||
// ~5.6 GB per blockProject call, which hangs on ROCm for large allocations.
|
|
||||||
Projector.BLAS_F.resize(Projector.fine_vol * Projector.words * npoint);
|
|
||||||
Projector.BLAS_C.resize(Projector.coarse_vol * nbasis * npoint);
|
|
||||||
|
|
||||||
for(int i=0;i<nbasis;i++){// Loop over basis vectors
|
|
||||||
accelerator_barrier(); // ensure prior iteration's async writes are retired
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<"/"<<nbasis<< std::endl;
|
|
||||||
|
|
||||||
tphaseBZ-=usecond();
|
|
||||||
for(int p=0;p<npoint;p++)
|
|
||||||
phaV_batch[p] = phaF[p] * V.subspace[i];
|
|
||||||
tphaseBZ+=usecond();
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<" phaseBZ done"<< std::endl;
|
|
||||||
|
|
||||||
tmat-=usecond();
|
|
||||||
for(int p=0;p<npoint;p++)
|
|
||||||
linop.Op(phaV_batch[p], MphaV_batch[p]);
|
|
||||||
tmat+=usecond();
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<" mat done"<< std::endl;
|
|
||||||
|
|
||||||
// One batched GEMM reads BLAS_V once for all npoint vectors.
|
|
||||||
tproj-=usecond();
|
|
||||||
Projector.blockProject(MphaV_batch, proj_batch);
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<" blockProject done"<< std::endl;
|
|
||||||
for(int p=0;p<npoint;p++)
|
|
||||||
ComputeProj[p] = conjugate(pha[p]) * proj_batch[p];
|
|
||||||
tproj+=usecond();
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<" proj done"<< std::endl;
|
|
||||||
|
|
||||||
tinv-=usecond();
|
|
||||||
for(int k=0;k<npoint;k++){
|
|
||||||
FT[k] = Zero();
|
|
||||||
for(int l=0;l<npoint;l++){
|
|
||||||
FT[k]= FT[k]+ invMkl(l,k)*ComputeProj[l];
|
|
||||||
}
|
|
||||||
|
|
||||||
int osites=CoarseGrid()->oSites();
|
|
||||||
autoView( A_v , _A[k], AcceleratorWrite);
|
|
||||||
autoView( FT_v , FT[k], AcceleratorRead);
|
|
||||||
accelerator_for(sss, osites, nbasis, {
|
|
||||||
#ifdef GRID_SIMT
|
|
||||||
int j = acceleratorSIMTlane(nbasis);
|
|
||||||
A_v[sss](i,j) = FT_v[sss](j);
|
|
||||||
#else
|
|
||||||
// CPU build: acceleratorSIMTlane()==0 -- an un-looped SIMT tensor
|
|
||||||
// index writes ONLY j=0 and silently drops the other nbasis-1
|
|
||||||
// columns (caught by Test_schur_dense_coarse import certificate,
|
|
||||||
// 2026-08-14). Loop explicitly.
|
|
||||||
for(int j=0;j<nbasis;j++){
|
|
||||||
A_v[sss](i,j) = FT_v[sss](j);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
});
|
|
||||||
}
|
|
||||||
tinv+=usecond();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only needed if nonhermitian
|
|
||||||
if ( ! hermitian ) {
|
|
||||||
std::cout << GridLogMessage<<"PopulateAdag "<<std::endl;
|
|
||||||
PopulateAdag();
|
|
||||||
}
|
|
||||||
|
|
||||||
// for(int p=0;p<geom.npoint;p++){
|
|
||||||
// std::cout << " _A["<<p<<"] "<<norm2(_A[p])<<std::endl;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Need to write something to populate Adag from A
|
|
||||||
ExchangeCoarseLinks();
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator eigen "<<teigen<<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator phase "<<tphase<<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator phaseBZ "<<tphaseBZ<<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator mat "<<tmat <<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator proj "<<tproj<<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator inv "<<tinv<<" us"<<std::endl;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
void ExchangeCoarseLinks(void){
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
_A[p] = Cell.ExchangePeriodic(_A[p]);
|
|
||||||
if ( !hermitian ) _Adag[p]= Cell.ExchangePeriodic(_Adag[p]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtual void Mdiag (const Field &in, Field &out){ GRID_ASSERT(0);};
|
|
||||||
virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);};
|
|
||||||
virtual void MdirAll (const Field &in, std::vector<Field> &out){assert(0);};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
@@ -1,738 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/GeneralCoarsenedMatrixMultiRHS.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Peter Boyle <pboyle@bnl.gov>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
|
||||||
*************************************************************************************/
|
|
||||||
/* END LEGAL */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
|
|
||||||
// Fine Object == (per site) type of fine field
|
|
||||||
// nbasis == number of deflation vectors
|
|
||||||
template<class Fobj,class CComplex,int nbasis>
|
|
||||||
class MultiGeneralCoarsenedMatrix : public SparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > {
|
|
||||||
public:
|
|
||||||
typedef typename CComplex::scalar_object SComplex;
|
|
||||||
typedef GeneralCoarsenedMatrix<Fobj,CComplex,nbasis> GeneralCoarseOp;
|
|
||||||
typedef MultiGeneralCoarsenedMatrix<Fobj,CComplex,nbasis> MultiGeneralCoarseOp;
|
|
||||||
|
|
||||||
typedef iVector<CComplex,nbasis > siteVector;
|
|
||||||
typedef iMatrix<CComplex,nbasis > siteMatrix;
|
|
||||||
typedef iVector<SComplex,nbasis > calcVector;
|
|
||||||
typedef iMatrix<SComplex,nbasis > calcMatrix;
|
|
||||||
typedef Lattice<iScalar<CComplex> > CoarseComplexField;
|
|
||||||
typedef Lattice<siteVector> CoarseVector;
|
|
||||||
typedef Lattice<iMatrix<CComplex,nbasis > > CoarseMatrix;
|
|
||||||
typedef iMatrix<CComplex,nbasis > Cobj;
|
|
||||||
typedef iVector<CComplex,nbasis > Cvec;
|
|
||||||
typedef Lattice< CComplex > CoarseScalar; // used for inner products on fine field
|
|
||||||
typedef Lattice<Fobj > FineField;
|
|
||||||
typedef Lattice<CComplex > FineComplexField;
|
|
||||||
typedef CoarseVector Field;
|
|
||||||
|
|
||||||
////////////////////
|
|
||||||
// Data members
|
|
||||||
////////////////////
|
|
||||||
GridCartesian * _CoarseGridMulti;
|
|
||||||
NonLocalStencilGeometry geom;
|
|
||||||
NonLocalStencilGeometry geom_srhs;
|
|
||||||
PaddedCell Cell;
|
|
||||||
GeneralLocalStencil Stencil;
|
|
||||||
|
|
||||||
deviceVector<calcVector> BLAS_B;
|
|
||||||
deviceVector<calcVector> BLAS_C;
|
|
||||||
std::vector<deviceVector<calcMatrix> > BLAS_A;
|
|
||||||
|
|
||||||
std::vector<deviceVector<ComplexD *> > BLAS_AP;
|
|
||||||
std::vector<deviceVector<ComplexD *> > BLAS_BP;
|
|
||||||
deviceVector<ComplexD *> BLAS_CP;
|
|
||||||
|
|
||||||
///////////////////////
|
|
||||||
// Interface
|
|
||||||
///////////////////////
|
|
||||||
GridBase * Grid(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
|
|
||||||
// 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.
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
NonLocalStencilGeometry & Geometry(void) { return geom_srhs; };
|
|
||||||
void ExtractMatrix(int p,CoarseMatrix &A) { BLAStoGrid(A,BLAS_A[p]); };
|
|
||||||
|
|
||||||
// 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].
|
|
||||||
void SetMatrix (int p,std::vector<CoarseMatrix> & A)
|
|
||||||
{
|
|
||||||
GRID_ASSERT(A.size()==geom_srhs.npoint);
|
|
||||||
GridtoBLAS(A[p],BLAS_A[p]);
|
|
||||||
}
|
|
||||||
void GetMatrix (int p,std::vector<CoarseMatrix> & A)
|
|
||||||
{
|
|
||||||
GRID_ASSERT(A.size()==geom_srhs.npoint);
|
|
||||||
BLAStoGrid(A[p],BLAS_A[p]);
|
|
||||||
}
|
|
||||||
void CopyMatrix (GeneralCoarseOp &_Op)
|
|
||||||
{
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
auto Aup = _Op.Cell.Extract(_Op._A[p]);
|
|
||||||
//Unpadded
|
|
||||||
GridtoBLAS(Aup,BLAS_A[p]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
void CheckMatrix (GeneralCoarseOp &_Op)
|
|
||||||
{
|
|
||||||
std::cout <<"************* Checking the little direc operator mRHS"<<std::endl;
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
//Unpadded
|
|
||||||
auto Aup = _Op.Cell.Extract(_Op._A[p]);
|
|
||||||
auto Ack = Aup;
|
|
||||||
BLAStoGrid(Ack,BLAS_A[p]);
|
|
||||||
std::cout << p<<" Ack "<<norm2(Ack)<<std::endl;
|
|
||||||
std::cout << p<<" Aup "<<norm2(Aup)<<std::endl;
|
|
||||||
}
|
|
||||||
std::cout <<"************* "<<std::endl;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
MultiGeneralCoarsenedMatrix(NonLocalStencilGeometry &_geom,GridCartesian *CoarseGridMulti) :
|
|
||||||
_CoarseGridMulti(CoarseGridMulti),
|
|
||||||
geom_srhs(_geom),
|
|
||||||
geom(_CoarseGridMulti,_geom.hops,_geom.skip+1),
|
|
||||||
Cell(geom.Depth(),_CoarseGridMulti),
|
|
||||||
Stencil(Cell.grids.back(),geom.shifts) // padded cell stencil
|
|
||||||
{
|
|
||||||
int32_t padded_sites = Cell.grids.back()->lSites();
|
|
||||||
int32_t unpadded_sites = CoarseGridMulti->lSites();
|
|
||||||
|
|
||||||
int32_t nrhs = CoarseGridMulti->FullDimensions()[0]; // # RHS
|
|
||||||
int32_t orhs = nrhs/CComplex::Nsimd();
|
|
||||||
|
|
||||||
padded_sites = padded_sites/nrhs;
|
|
||||||
unpadded_sites = unpadded_sites/nrhs;
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
// Device data vector storage
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
BLAS_A.resize(geom.npoint);
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
BLAS_A[p].resize (unpadded_sites); // no ghost zone, npoint elements
|
|
||||||
}
|
|
||||||
|
|
||||||
BLAS_B.resize(nrhs *padded_sites); // includes ghost zone
|
|
||||||
BLAS_C.resize(nrhs *unpadded_sites); // no ghost zone
|
|
||||||
BLAS_AP.resize(geom.npoint);
|
|
||||||
BLAS_BP.resize(geom.npoint);
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
BLAS_AP[p].resize(unpadded_sites);
|
|
||||||
BLAS_BP[p].resize(unpadded_sites);
|
|
||||||
}
|
|
||||||
BLAS_CP.resize(unpadded_sites);
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
// Pointers to data
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Site identity mapping for A
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
for(int ss=0;ss<unpadded_sites;ss++){
|
|
||||||
ComplexD *ptr = (ComplexD *)&BLAS_A[p][ss];
|
|
||||||
acceleratorPut(BLAS_AP[p][ss],ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Site identity mapping for C
|
|
||||||
for(int ss=0;ss<unpadded_sites;ss++){
|
|
||||||
ComplexD *ptr = (ComplexD *)&BLAS_C[ss*nrhs];
|
|
||||||
acceleratorPut(BLAS_CP[ss],ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Neighbour table is more complicated
|
|
||||||
int32_t j=0; // Interior point counter (unpadded)
|
|
||||||
for(int32_t s=0;s<padded_sites;s++){ // 4 volume, padded
|
|
||||||
int ghost_zone=0;
|
|
||||||
for(int32_t point = 0 ; point < geom.npoint; point++){
|
|
||||||
int i=s*orhs*geom.npoint+point;
|
|
||||||
if( Stencil._entries[i]._wrap ) { // stencil is indexed by the oSite of the CoarseGridMulti, hence orhs factor
|
|
||||||
ghost_zone=1; // If general stencil wrapped in any direction, wrap=1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( ghost_zone==0) {
|
|
||||||
for(int32_t point = 0 ; point < geom.npoint; point++){
|
|
||||||
int i=s*orhs*geom.npoint+point;
|
|
||||||
int32_t nbr = Stencil._entries[i]._offset*CComplex::Nsimd(); // oSite -> lSite
|
|
||||||
GRID_ASSERT(nbr<BLAS_B.size());
|
|
||||||
ComplexD * ptr = (ComplexD *)&BLAS_B[nbr];
|
|
||||||
acceleratorPut(BLAS_BP[point][j],ptr); // neighbour indexing in ghost zone volume
|
|
||||||
}
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GRID_ASSERT(j==unpadded_sites);
|
|
||||||
}
|
|
||||||
template<class vobj> void GridtoBLAS(const Lattice<vobj> &from,deviceVector<typename vobj::scalar_object> &to)
|
|
||||||
{
|
|
||||||
typedef typename vobj::scalar_object sobj;
|
|
||||||
typedef typename vobj::scalar_type scalar_type;
|
|
||||||
typedef typename vobj::vector_type vector_type;
|
|
||||||
|
|
||||||
GridBase *Fg = from.Grid();
|
|
||||||
GRID_ASSERT(!Fg->_isCheckerBoarded);
|
|
||||||
int nd = Fg->_ndimension;
|
|
||||||
|
|
||||||
to.resize(Fg->lSites());
|
|
||||||
|
|
||||||
Coordinate LocalLatt = Fg->LocalDimensions();
|
|
||||||
size_t nsite = 1;
|
|
||||||
for(int i=0;i<nd;i++) nsite *= LocalLatt[i];
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// do the index calc on the GPU
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Coordinate f_ostride = Fg->_ostride;
|
|
||||||
Coordinate f_istride = Fg->_istride;
|
|
||||||
Coordinate f_rdimensions = Fg->_rdimensions;
|
|
||||||
|
|
||||||
autoView(from_v,from,AcceleratorRead);
|
|
||||||
auto to_v = &to[0];
|
|
||||||
|
|
||||||
const int words=sizeof(vobj)/sizeof(vector_type);
|
|
||||||
accelerator_for(idx,nsite,1,{
|
|
||||||
|
|
||||||
Coordinate from_coor, base;
|
|
||||||
Lexicographic::CoorFromIndex(base,idx,LocalLatt);
|
|
||||||
for(int i=0;i<nd;i++){
|
|
||||||
from_coor[i] = base[i];
|
|
||||||
}
|
|
||||||
int from_oidx = 0; for(int d=0;d<nd;d++) from_oidx+=f_ostride[d]*(from_coor[d]%f_rdimensions[d]);
|
|
||||||
int from_lane = 0; for(int d=0;d<nd;d++) from_lane+=f_istride[d]*(from_coor[d]/f_rdimensions[d]);
|
|
||||||
|
|
||||||
const vector_type* from = (const vector_type *)&from_v[from_oidx];
|
|
||||||
scalar_type* to = (scalar_type *)&to_v[idx];
|
|
||||||
|
|
||||||
scalar_type stmp;
|
|
||||||
for(int w=0;w<words;w++){
|
|
||||||
stmp = getlane(from[w], from_lane);
|
|
||||||
to[w] = stmp;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
template<class vobj> void BLAStoGrid(Lattice<vobj> &grid,deviceVector<typename vobj::scalar_object> &in)
|
|
||||||
{
|
|
||||||
typedef typename vobj::scalar_object sobj;
|
|
||||||
typedef typename vobj::scalar_type scalar_type;
|
|
||||||
typedef typename vobj::vector_type vector_type;
|
|
||||||
|
|
||||||
GridBase *Tg = grid.Grid();
|
|
||||||
GRID_ASSERT(!Tg->_isCheckerBoarded);
|
|
||||||
int nd = Tg->_ndimension;
|
|
||||||
|
|
||||||
GRID_ASSERT(in.size()==Tg->lSites());
|
|
||||||
|
|
||||||
Coordinate LocalLatt = Tg->LocalDimensions();
|
|
||||||
size_t nsite = 1;
|
|
||||||
for(int i=0;i<nd;i++) nsite *= LocalLatt[i];
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// do the index calc on the GPU
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Coordinate t_ostride = Tg->_ostride;
|
|
||||||
Coordinate t_istride = Tg->_istride;
|
|
||||||
Coordinate t_rdimensions = Tg->_rdimensions;
|
|
||||||
|
|
||||||
autoView(to_v,grid,AcceleratorWrite);
|
|
||||||
auto from_v = &in[0];
|
|
||||||
|
|
||||||
const int words=sizeof(vobj)/sizeof(vector_type);
|
|
||||||
accelerator_for(idx,nsite,1,{
|
|
||||||
|
|
||||||
Coordinate to_coor, base;
|
|
||||||
Lexicographic::CoorFromIndex(base,idx,LocalLatt);
|
|
||||||
for(int i=0;i<nd;i++){
|
|
||||||
to_coor[i] = base[i];
|
|
||||||
}
|
|
||||||
int to_oidx = 0; for(int d=0;d<nd;d++) to_oidx+=t_ostride[d]*(to_coor[d]%t_rdimensions[d]);
|
|
||||||
int to_lane = 0; for(int d=0;d<nd;d++) to_lane+=t_istride[d]*(to_coor[d]/t_rdimensions[d]);
|
|
||||||
|
|
||||||
vector_type* to = (vector_type *)&to_v[to_oidx];
|
|
||||||
scalar_type* from = (scalar_type *)&from_v[idx];
|
|
||||||
|
|
||||||
scalar_type stmp;
|
|
||||||
for(int w=0;w<words;w++){
|
|
||||||
stmp=from[w];
|
|
||||||
putlane(to[w], stmp, to_lane);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
|
|
||||||
Aggregation<Fobj,CComplex,nbasis> & Subspace,
|
|
||||||
GridBase *CoarseGrid)
|
|
||||||
{
|
|
||||||
#if 0
|
|
||||||
std::cout << GridLogMessage<< "GeneralCoarsenMatrixMrhs "<< std::endl;
|
|
||||||
|
|
||||||
GridBase *grid = Subspace.FineGrid;
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
// Orthogonalise the subblocks over the basis
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
CoarseScalar InnerProd(CoarseGrid);
|
|
||||||
blockOrthogonalise(InnerProd,Subspace.subspace);
|
|
||||||
|
|
||||||
const int npoint = geom_srhs.npoint;
|
|
||||||
|
|
||||||
Coordinate clatt = CoarseGrid->GlobalDimensions();
|
|
||||||
int Nd = CoarseGrid->Nd();
|
|
||||||
/*
|
|
||||||
* Here, k,l index which possible momentum/shift within the N-points connected by MdagM.
|
|
||||||
* Matrix index i is mapped to this shift via
|
|
||||||
* geom.shifts[i]
|
|
||||||
*
|
|
||||||
* conj(pha[block]) proj[k (which mom)][j (basis vec cpt)][block]
|
|
||||||
* = \sum_{l in ball} e^{i q_k . delta_l} < phi_{block,j} | MdagM | phi_{(block+delta_l),i} >
|
|
||||||
* = \sum_{l in ball} e^{iqk.delta_l} A_ji^{b.b+l}
|
|
||||||
* = M_{kl} A_ji^{b.b+l}
|
|
||||||
*
|
|
||||||
* Must assemble and invert matrix M_k,l = e^[i q_k . delta_l]
|
|
||||||
*
|
|
||||||
* Where q_k = delta_k . (2*M_PI/global_nb[mu])
|
|
||||||
*
|
|
||||||
* Then A{ji}^{b,b+l} = M^{-1}_{lm} ComputeProj_{m,b,i,j}
|
|
||||||
*/
|
|
||||||
Eigen::MatrixXcd Mkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
|
||||||
Eigen::MatrixXcd invMkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
|
||||||
ComplexD ci(0.0,1.0);
|
|
||||||
for(int k=0;k<npoint;k++){ // Loop over momenta
|
|
||||||
|
|
||||||
for(int l=0;l<npoint;l++){ // Loop over nbr relative
|
|
||||||
ComplexD phase(0.0,0.0);
|
|
||||||
for(int mu=0;mu<Nd;mu++){
|
|
||||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
|
||||||
phase=phase+TwoPiL*geom_srhs.shifts[k][mu]*geom_srhs.shifts[l][mu];
|
|
||||||
}
|
|
||||||
phase=exp(phase*ci);
|
|
||||||
Mkl(k,l) = phase;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
invMkl = Mkl.inverse();
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// Now compute the matrix elements of linop between the orthonormal
|
|
||||||
// set of vectors.
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
FineField phaV(grid); // Phased block basis vector
|
|
||||||
FineField MphaV(grid);// Matrix applied
|
|
||||||
std::vector<FineComplexField> phaF(npoint,grid);
|
|
||||||
std::vector<CoarseComplexField> pha(npoint,CoarseGrid);
|
|
||||||
|
|
||||||
CoarseVector coarseInner(CoarseGrid);
|
|
||||||
|
|
||||||
typedef typename CComplex::scalar_type SComplex;
|
|
||||||
FineComplexField one(grid); one=SComplex(1.0);
|
|
||||||
FineComplexField zz(grid); zz = Zero();
|
|
||||||
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
// Stick a phase on every block
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
CoarseComplexField coor(CoarseGrid);
|
|
||||||
pha[p]=Zero();
|
|
||||||
for(int mu=0;mu<Nd;mu++){
|
|
||||||
LatticeCoordinate(coor,mu);
|
|
||||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
|
||||||
pha[p] = pha[p] + (TwoPiL * geom_srhs.shifts[p][mu]) * coor;
|
|
||||||
}
|
|
||||||
pha[p] =exp(pha[p]*ci);
|
|
||||||
|
|
||||||
blockZAXPY(phaF[p],pha[p],one,zz);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Could save on temporary storage here
|
|
||||||
std::vector<CoarseMatrix> _A;
|
|
||||||
_A.resize(geom_srhs.npoint,CoarseGrid);
|
|
||||||
|
|
||||||
std::vector<CoarseVector> ComputeProj(npoint,CoarseGrid);
|
|
||||||
CoarseVector FT(CoarseGrid);
|
|
||||||
for(int i=0;i<nbasis;i++){// Loop over basis vectors
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<"/"<<nbasis<< std::endl;
|
|
||||||
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
|
|
||||||
|
|
||||||
phaV = phaF[p]*Subspace.subspace[i];
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// Multiple phased subspace vector by matrix and project to subspace
|
|
||||||
// Remove local bulk phase to leave relative phases
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
linop.Op(phaV,MphaV);
|
|
||||||
|
|
||||||
// Fixme, could use batched block projector here
|
|
||||||
blockProject(coarseInner,MphaV,Subspace.subspace);
|
|
||||||
|
|
||||||
coarseInner = conjugate(pha[p]) * coarseInner;
|
|
||||||
|
|
||||||
ComputeProj[p] = coarseInner;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Could do this with a block promote or similar BLAS call via the MultiRHSBlockProjector with a const matrix.
|
|
||||||
for(int k=0;k<npoint;k++){
|
|
||||||
|
|
||||||
FT = Zero();
|
|
||||||
for(int l=0;l<npoint;l++){
|
|
||||||
FT= FT+ invMkl(l,k)*ComputeProj[l];
|
|
||||||
}
|
|
||||||
|
|
||||||
int osites=CoarseGrid->oSites();
|
|
||||||
autoView( A_v , _A[k], AcceleratorWrite);
|
|
||||||
autoView( FT_v , FT, AcceleratorRead);
|
|
||||||
accelerator_for(sss, osites, 1, {
|
|
||||||
for(int j=0;j<nbasis;j++){
|
|
||||||
A_v[sss](i,j) = FT_v[sss](j);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only needed if nonhermitian
|
|
||||||
// if ( ! hermitian ) {
|
|
||||||
// std::cout << GridLogMessage<<"PopulateAdag "<<std::endl;
|
|
||||||
// PopulateAdag();
|
|
||||||
// }
|
|
||||||
// Need to write something to populate Adag from A
|
|
||||||
|
|
||||||
for(int p=0;p<geom_srhs.npoint;p++){
|
|
||||||
GridtoBLAS(_A[p],BLAS_A[p]);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
Grid : Message : 11698.730546 s : CoarsenOperator eigen 1334 us
|
|
||||||
Grid : Message : 11698.730563 s : CoarsenOperator phase 34729 us
|
|
||||||
Grid : Message : 11698.730565 s : CoarsenOperator phaseBZ 2423814 us
|
|
||||||
Grid : Message : 11698.730566 s : CoarsenOperator mat 127890998 us
|
|
||||||
Grid : Message : 11698.730567 s : CoarsenOperator proj 515840840 us
|
|
||||||
Grid : Message : 11698.730568 s : CoarsenOperator inv 103948313 us
|
|
||||||
Takes 600s to compute matrix elements, DOMINATED by the block project.
|
|
||||||
Easy to speed up with the batched block project.
|
|
||||||
Store npoint vectors, get npoint x Nbasis block projection, and 81 fold faster.
|
|
||||||
|
|
||||||
// Block project below taks to 240s
|
|
||||||
Grid : Message : 328.193418 s : CoarsenOperator phase 38338 us
|
|
||||||
Grid : Message : 328.193434 s : CoarsenOperator phaseBZ 1711226 us
|
|
||||||
Grid : Message : 328.193436 s : CoarsenOperator mat 122213270 us
|
|
||||||
//Grid : Message : 328.193438 s : CoarsenOperator proj 1181154 us <-- this is mistimed
|
|
||||||
//Grid : Message : 11698.730568 s : CoarsenOperator inv 103948313 us <-- Cut this ~10x if lucky by loop fusion
|
|
||||||
*/
|
|
||||||
#else
|
|
||||||
RealD tproj=0.0;
|
|
||||||
RealD tmat=0.0;
|
|
||||||
RealD tphase=0.0;
|
|
||||||
RealD tphaseBZ=0.0;
|
|
||||||
RealD tinv=0.0;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage<< "GeneralCoarsenMatrixMrhs "<< std::endl;
|
|
||||||
|
|
||||||
GridBase *grid = Subspace.FineGrid;
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
// Orthogonalise the subblocks over the basis
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
CoarseScalar InnerProd(CoarseGrid);
|
|
||||||
blockOrthogonalise(InnerProd,Subspace.subspace);
|
|
||||||
|
|
||||||
|
|
||||||
MultiRHSBlockProject<Lattice<Fobj> > Projector;
|
|
||||||
Projector.Allocate(nbasis,grid,CoarseGrid);
|
|
||||||
Projector.ImportBasis(Subspace.subspace);
|
|
||||||
|
|
||||||
const int npoint = geom_srhs.npoint;
|
|
||||||
|
|
||||||
Coordinate clatt = CoarseGrid->GlobalDimensions();
|
|
||||||
int Nd = CoarseGrid->Nd();
|
|
||||||
/*
|
|
||||||
* Here, k,l index which possible momentum/shift within the N-points connected by MdagM.
|
|
||||||
* Matrix index i is mapped to this shift via
|
|
||||||
* geom.shifts[i]
|
|
||||||
*
|
|
||||||
* conj(pha[block]) proj[k (which mom)][j (basis vec cpt)][block]
|
|
||||||
* = \sum_{l in ball} e^{i q_k . delta_l} < phi_{block,j} | MdagM | phi_{(block+delta_l),i} >
|
|
||||||
* = \sum_{l in ball} e^{iqk.delta_l} A_ji^{b.b+l}
|
|
||||||
* = M_{kl} A_ji^{b.b+l}
|
|
||||||
*
|
|
||||||
* Must assemble and invert matrix M_k,l = e^[i q_k . delta_l]
|
|
||||||
*
|
|
||||||
* Where q_k = delta_k . (2*M_PI/global_nb[mu])
|
|
||||||
*
|
|
||||||
* Then A{ji}^{b,b+l} = M^{-1}_{lm} ComputeProj_{m,b,i,j}
|
|
||||||
*/
|
|
||||||
Eigen::MatrixXcd Mkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
|
||||||
Eigen::MatrixXcd invMkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
|
||||||
ComplexD ci(0.0,1.0);
|
|
||||||
for(int k=0;k<npoint;k++){ // Loop over momenta
|
|
||||||
|
|
||||||
for(int l=0;l<npoint;l++){ // Loop over nbr relative
|
|
||||||
ComplexD phase(0.0,0.0);
|
|
||||||
for(int mu=0;mu<Nd;mu++){
|
|
||||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
|
||||||
phase=phase+TwoPiL*geom_srhs.shifts[k][mu]*geom_srhs.shifts[l][mu];
|
|
||||||
}
|
|
||||||
phase=exp(phase*ci);
|
|
||||||
Mkl(k,l) = phase;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
invMkl = Mkl.inverse();
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// Now compute the matrix elements of linop between the orthonormal
|
|
||||||
// set of vectors.
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
FineField phaV(grid); // Phased block basis vector
|
|
||||||
FineField MphaV(grid);// Matrix applied
|
|
||||||
std::vector<FineComplexField> phaF(npoint,grid);
|
|
||||||
std::vector<CoarseComplexField> pha(npoint,CoarseGrid);
|
|
||||||
|
|
||||||
CoarseVector coarseInner(CoarseGrid);
|
|
||||||
|
|
||||||
tphase=-usecond();
|
|
||||||
typedef typename CComplex::scalar_type SComplex;
|
|
||||||
FineComplexField one(grid); one=SComplex(1.0);
|
|
||||||
FineComplexField zz(grid); zz = Zero();
|
|
||||||
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
// Stick a phase on every block
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
CoarseComplexField coor(CoarseGrid);
|
|
||||||
pha[p]=Zero();
|
|
||||||
for(int mu=0;mu<Nd;mu++){
|
|
||||||
LatticeCoordinate(coor,mu);
|
|
||||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
|
||||||
pha[p] = pha[p] + (TwoPiL * geom_srhs.shifts[p][mu]) * coor;
|
|
||||||
}
|
|
||||||
pha[p] =exp(pha[p]*ci);
|
|
||||||
|
|
||||||
blockZAXPY(phaF[p],pha[p],one,zz);
|
|
||||||
}
|
|
||||||
tphase+=usecond();
|
|
||||||
|
|
||||||
// Could save on temporary storage here
|
|
||||||
std::vector<CoarseMatrix> _A;
|
|
||||||
_A.resize(geom_srhs.npoint,CoarseGrid);
|
|
||||||
|
|
||||||
// Count use small chunks than npoint == 81 and save memory
|
|
||||||
int batch = 9;
|
|
||||||
std::vector<FineField> _MphaV(batch,grid);
|
|
||||||
std::vector<CoarseVector> TmpProj(batch,CoarseGrid);
|
|
||||||
|
|
||||||
std::vector<CoarseVector> ComputeProj(npoint,CoarseGrid);
|
|
||||||
CoarseVector FT(CoarseGrid);
|
|
||||||
for(int i=0;i<nbasis;i++){// Loop over basis vectors
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<"/"<<nbasis<< std::endl;
|
|
||||||
|
|
||||||
// std::cout << GridLogMessage << " phasing the fine vector "<<std::endl;
|
|
||||||
// Fixme : do this in batches
|
|
||||||
for(int p=0;p<npoint;p+=batch){ // Loop over momenta in npoint
|
|
||||||
|
|
||||||
for(int b=0;b<MIN(batch,npoint-p);b++){
|
|
||||||
tphaseBZ-=usecond();
|
|
||||||
phaV = phaF[p+b]*Subspace.subspace[i];
|
|
||||||
tphaseBZ+=usecond();
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// Multiple phased subspace vector by matrix and project to subspace
|
|
||||||
// Remove local bulk phase to leave relative phases
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// Memory footprint was an issue
|
|
||||||
tmat-=usecond();
|
|
||||||
linop.Op(phaV,MphaV);
|
|
||||||
_MphaV[b] = MphaV;
|
|
||||||
tmat+=usecond();
|
|
||||||
}
|
|
||||||
|
|
||||||
// std::cout << GridLogMessage << " Calling block project "<<std::endl;
|
|
||||||
tproj-=usecond();
|
|
||||||
Projector.blockProject(_MphaV,TmpProj);
|
|
||||||
tproj+=usecond();
|
|
||||||
|
|
||||||
// std::cout << GridLogMessage << " conj phasing the coarse vectors "<<std::endl;
|
|
||||||
for(int b=0;b<MIN(batch,npoint-p);b++){
|
|
||||||
ComputeProj[p+b] = conjugate(pha[p+b])*TmpProj[b];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Could do this with a block promote or similar BLAS call via the MultiRHSBlockProjector with a const matrix.
|
|
||||||
|
|
||||||
// std::cout << GridLogMessage << " Starting FT inv "<<std::endl;
|
|
||||||
tinv-=usecond();
|
|
||||||
for(int k=0;k<npoint;k++){
|
|
||||||
FT = Zero();
|
|
||||||
// 81 kernel calls as many ComputeProj vectors
|
|
||||||
// Could fuse with a vector of views, but ugly
|
|
||||||
// Could unroll the expression and run fewer kernels -- much more attractive
|
|
||||||
// Could also do non blocking.
|
|
||||||
#if 0
|
|
||||||
for(int l=0;l<npoint;l++){
|
|
||||||
FT= FT+ invMkl(l,k)*ComputeProj[l];
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
const int radix = 9;
|
|
||||||
int ll;
|
|
||||||
for(ll=0;ll+radix-1<npoint;ll+=radix){
|
|
||||||
// When ll = npoint-radix, ll+radix-1 = npoint-1, and we do it all.
|
|
||||||
FT = FT
|
|
||||||
+ invMkl(ll+0,k)*ComputeProj[ll+0]
|
|
||||||
+ invMkl(ll+1,k)*ComputeProj[ll+1]
|
|
||||||
+ invMkl(ll+2,k)*ComputeProj[ll+2]
|
|
||||||
+ invMkl(ll+3,k)*ComputeProj[ll+3]
|
|
||||||
+ invMkl(ll+4,k)*ComputeProj[ll+4]
|
|
||||||
+ invMkl(ll+5,k)*ComputeProj[ll+5]
|
|
||||||
+ invMkl(ll+6,k)*ComputeProj[ll+6]
|
|
||||||
+ invMkl(ll+7,k)*ComputeProj[ll+7]
|
|
||||||
+ invMkl(ll+8,k)*ComputeProj[ll+8];
|
|
||||||
}
|
|
||||||
for(int l=ll;l<npoint;l++){
|
|
||||||
FT= FT+ invMkl(l,k)*ComputeProj[l];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// 1 kernel call -- must be cheaper
|
|
||||||
int osites=CoarseGrid->oSites();
|
|
||||||
autoView( A_v , _A[k], AcceleratorWrite);
|
|
||||||
autoView( FT_v , FT, AcceleratorRead);
|
|
||||||
accelerator_for(sss, osites, 1, {
|
|
||||||
for(int j=0;j<nbasis;j++){
|
|
||||||
A_v[sss](i,j) = FT_v[sss](j);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
tinv+=usecond();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only needed if nonhermitian
|
|
||||||
// if ( ! hermitian ) {
|
|
||||||
// std::cout << GridLogMessage<<"PopulateAdag "<<std::endl;
|
|
||||||
// PopulateAdag();
|
|
||||||
// }
|
|
||||||
// Need to write something to populate Adag from A
|
|
||||||
// std::cout << GridLogMessage << " Calling GridtoBLAS "<<std::endl;
|
|
||||||
for(int p=0;p<geom_srhs.npoint;p++){
|
|
||||||
GridtoBLAS(_A[p],BLAS_A[p]);
|
|
||||||
}
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator phase "<<tphase<<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator phaseBZ "<<tphaseBZ<<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator mat "<<tmat <<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator proj "<<tproj<<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator inv "<<tinv<<" us"<<std::endl;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
void Mdag(const CoarseVector &in, CoarseVector &out)
|
|
||||||
{
|
|
||||||
this->M(in,out);
|
|
||||||
}
|
|
||||||
void M (const CoarseVector &in, CoarseVector &out)
|
|
||||||
{
|
|
||||||
// std::cout << GridLogMessage << "New Mrhs coarse"<<std::endl;
|
|
||||||
conformable(CoarseGrid(),in.Grid());
|
|
||||||
conformable(in.Grid(),out.Grid());
|
|
||||||
out.Checkerboard() = in.Checkerboard();
|
|
||||||
|
|
||||||
RealD t_tot;
|
|
||||||
RealD t_exch;
|
|
||||||
RealD t_GtoB;
|
|
||||||
RealD t_BtoG;
|
|
||||||
RealD t_mult;
|
|
||||||
|
|
||||||
t_tot=-usecond();
|
|
||||||
CoarseVector tin=in;
|
|
||||||
t_exch=-usecond();
|
|
||||||
CoarseVector pin = Cell.ExchangePeriodic(tin); //padded input
|
|
||||||
t_exch+=usecond();
|
|
||||||
|
|
||||||
CoarseVector pout(pin.Grid());
|
|
||||||
|
|
||||||
int npoint = geom.npoint;
|
|
||||||
typedef calcMatrix* Aview;
|
|
||||||
typedef LatticeView<Cvec> Vview;
|
|
||||||
|
|
||||||
const int Nsimd = CComplex::Nsimd();
|
|
||||||
|
|
||||||
int64_t nrhs =pin.Grid()->GlobalDimensions()[0];
|
|
||||||
GRID_ASSERT(nrhs>=1);
|
|
||||||
|
|
||||||
RealD flops,bytes;
|
|
||||||
int64_t osites=in.Grid()->oSites(); // unpadded
|
|
||||||
int64_t unpadded_vol = CoarseGrid()->lSites()/nrhs;
|
|
||||||
|
|
||||||
flops = 1.0* npoint * nbasis * nbasis * 8.0 * osites * CComplex::Nsimd();
|
|
||||||
bytes = 1.0*osites*sizeof(siteMatrix)*npoint/pin.Grid()->GlobalDimensions()[0]
|
|
||||||
+ 2.0*osites*sizeof(siteVector)*npoint;
|
|
||||||
|
|
||||||
|
|
||||||
t_GtoB=-usecond();
|
|
||||||
GridtoBLAS(pin,BLAS_B);
|
|
||||||
t_GtoB+=usecond();
|
|
||||||
|
|
||||||
GridBLAS BLAS;
|
|
||||||
|
|
||||||
t_mult=-usecond();
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
RealD c = 1.0;
|
|
||||||
if (p==0) c = 0.0;
|
|
||||||
ComplexD beta(c);
|
|
||||||
|
|
||||||
BLAS.gemmBatched(nbasis,nrhs,nbasis,
|
|
||||||
ComplexD(1.0),
|
|
||||||
BLAS_AP[p],
|
|
||||||
BLAS_BP[p],
|
|
||||||
ComplexD(c),
|
|
||||||
BLAS_CP);
|
|
||||||
}
|
|
||||||
BLAS.synchronise();
|
|
||||||
t_mult+=usecond();
|
|
||||||
|
|
||||||
t_BtoG=-usecond();
|
|
||||||
BLAStoGrid(out,BLAS_C);
|
|
||||||
t_BtoG+=usecond();
|
|
||||||
t_tot+=usecond();
|
|
||||||
/*
|
|
||||||
std::cout << GridLogMessage << "New Mrhs coarse DONE "<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"Coarse Mult exch "<<t_exch<<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"Coarse Mult mult "<<t_mult<<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"Coarse Mult GtoB "<<t_GtoB<<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"Coarse Mult BtoG "<<t_BtoG<<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"Coarse Mult tot "<<t_tot<<" us"<<std::endl;
|
|
||||||
*/
|
|
||||||
// std::cout << GridLogMessage<<std::endl;
|
|
||||||
// std::cout << GridLogMessage<<"Coarse Kernel flops "<< flops<<std::endl;
|
|
||||||
// std::cout << GridLogMessage<<"Coarse Kernel flop/s "<< flops/t_mult<<" mflop/s"<<std::endl;
|
|
||||||
// std::cout << GridLogMessage<<"Coarse Kernel bytes/s "<< bytes/t_mult/1000<<" GB/s"<<std::endl;
|
|
||||||
// std::cout << GridLogMessage<<"Coarse overall flops/s "<< flops/t_tot<<" mflop/s"<<std::endl;
|
|
||||||
// std::cout << GridLogMessage<<"Coarse total bytes "<< bytes/1e6<<" MB"<<std::endl;
|
|
||||||
};
|
|
||||||
virtual void Mdiag (const Field &in, Field &out){ GRID_ASSERT(0);};
|
|
||||||
virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);};
|
|
||||||
virtual void MdirAll (const Field &in, std::vector<Field> &out){assert(0);};
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
@@ -37,7 +37,9 @@ template<class Fobj,class CComplex,int nbasis>
|
|||||||
class MultiGeneralCoarsenedOperatorV2 : public SparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > {
|
class MultiGeneralCoarsenedOperatorV2 : 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;
|
// The BLAS scalar of this level follows the coefficient precision:
|
||||||
|
// ComplexF for an fp32 coarse space, ComplexD for fp64.
|
||||||
|
typedef typename GridTypeMapper<CComplex>::scalar_type CoarseBLASScalar;
|
||||||
typedef MultiGeneralCoarsenedOperatorV2<Fobj,CComplex,nbasis> MultiGeneralCoarseOp;
|
typedef MultiGeneralCoarsenedOperatorV2<Fobj,CComplex,nbasis> MultiGeneralCoarseOp;
|
||||||
|
|
||||||
typedef iVector<CComplex,nbasis > siteVector;
|
typedef iVector<CComplex,nbasis > siteVector;
|
||||||
@@ -82,9 +84,80 @@ public:
|
|||||||
deviceVector<calcVector> BLAS_C;
|
deviceVector<calcVector> BLAS_C;
|
||||||
std::vector<deviceVector<calcMatrix> > BLAS_A;
|
std::vector<deviceVector<calcMatrix> > BLAS_A;
|
||||||
|
|
||||||
std::vector<deviceVector<ComplexD *> > BLAS_AP;
|
std::vector<deviceVector<CoarseBLASScalar *> > BLAS_AP;
|
||||||
std::vector<deviceVector<ComplexD *> > BLAS_BP;
|
std::vector<deviceVector<CoarseBLASScalar *> > BLAS_BP;
|
||||||
deviceVector<ComplexD *> BLAS_CP;
|
deviceVector<CoarseBLASScalar *> BLAS_CP;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Stencil legs carried in the BATCH dimension, LegGroup at a time.
|
||||||
|
//
|
||||||
|
// One call per stencil point batches over the local coarse volume alone,
|
||||||
|
// which at the production point is 1024 sites. That is too few for the
|
||||||
|
// single-precision kernel: it then runs at half the bandwidth the double
|
||||||
|
// one reaches and takes the same time, so an fp32 coarse space buys
|
||||||
|
// nothing in the operator. The same shape at four times the batch runs
|
||||||
|
// 1.7x faster in fp32 than fp64, so the fix is more batch, not fewer
|
||||||
|
// bytes. Grouping G legs multiplies the batch by G and divides the launch
|
||||||
|
// count by G, at the price of G partial outputs summed at the end.
|
||||||
|
//
|
||||||
|
// G must divide npoint; 3 divides both the 33 point (next-to-nearest) and
|
||||||
|
// 81 point (full box) stencils. G=1 is the ungrouped form. The only
|
||||||
|
// numerical difference is the ORDER of the sum over stencil points.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// The group need not divide npoint: the last call carries the remainder,
|
||||||
|
// with its own smaller tables. The batch a call presents is LegGroup times
|
||||||
|
// the local coarse volume, so the group that saturates the kernel depends
|
||||||
|
// on the decomposition, and the default is chosen from it rather than
|
||||||
|
// fixed. TargetBatch is where the measured single-precision curve flattens
|
||||||
|
// (530 GB/s at 1024, 695 at 9216, 728 at 33792 for 60x12x60 on MI250X).
|
||||||
|
//
|
||||||
|
// 9216 is nine times the production local coarse volume, so the default
|
||||||
|
// lands on nine legs per call: 81 = 9x9 for the full-box stencil and
|
||||||
|
// 33 = 9+9+9+6 for the next-to-nearest one, which is the grouping the
|
||||||
|
// earlier HDCG coarse operator used.
|
||||||
|
static const int TargetBatch = 9216;
|
||||||
|
|
||||||
|
int LegGroup = 1;
|
||||||
|
std::vector<deviceVector<CoarseBLASScalar *> > BLAS_APg; // per group
|
||||||
|
std::vector<deviceVector<CoarseBLASScalar *> > BLAS_BPg;
|
||||||
|
deviceVector<CoarseBLASScalar *> BLAS_CPg; // LegGroup*sites
|
||||||
|
deviceVector<CoarseBLASScalar *> BLAS_CPr; // remainder*sites
|
||||||
|
deviceVector<calcVector> BLAS_Cg; // LegGroup partials
|
||||||
|
|
||||||
|
int LegGroups(void) const { return geom.npoint/LegGroup; } // full
|
||||||
|
int LegRemainder(void) const { return geom.npoint%LegGroup; }
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Matrix pointer table for the grouped call. Group g holds legs
|
||||||
|
// g*LegGroup .. and the batch index runs site fastest within a leg. The
|
||||||
|
// last group is short when LegGroup does not divide npoint.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void BuildGroupedA(void)
|
||||||
|
{
|
||||||
|
int32_t sites = _CoarseGrid->lSites();
|
||||||
|
int ng = LegGroups() + (LegRemainder()?1:0);
|
||||||
|
BLAS_APg.resize(ng);
|
||||||
|
for(int g=0;g<ng;g++){
|
||||||
|
int legs = (g<LegGroups()) ? LegGroup : LegRemainder();
|
||||||
|
BLAS_APg[g].resize(legs*sites);
|
||||||
|
for(int l=0;l<legs;l++){
|
||||||
|
int p = g*LegGroup+l;
|
||||||
|
for(int32_t ss=0;ss<sites;ss++){
|
||||||
|
CoarseBLASScalar *ptr = (CoarseBLASScalar *)&BLAS_A[p][ss];
|
||||||
|
acceleratorPut(BLAS_APg[g][l*sites+ss],ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetLegGroup(int G)
|
||||||
|
{
|
||||||
|
GRID_ASSERT( G>=1 );
|
||||||
|
GRID_ASSERT( G<=geom.npoint ); // every partial must get an initialising call
|
||||||
|
LegGroup = G;
|
||||||
|
BuildGroupedA();
|
||||||
|
if ( _CoarseGridMulti ) SetGrid(_CoarseGridMulti); // rebuild the rest
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////
|
///////////////////////
|
||||||
// Interface
|
// Interface
|
||||||
@@ -115,6 +188,15 @@ public:
|
|||||||
// D+1 multiRHS grid here, so a consumer wanting the space the elements live
|
// D+1 multiRHS grid here, so a consumer wanting the space the elements live
|
||||||
// on must ask for CoarseGridD().
|
// on must ask for CoarseGridD().
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Resident device memory this operator holds (matrix elements and the
|
||||||
|
// Nrhs-dependent BLAS buffers). Not evictable.
|
||||||
|
uint64_t DeviceBytes(void)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
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]); };
|
void ExtractMatrix(int p,CoarseMatrix &A) { BLAStoGrid(A,BLAS_A[p]); };
|
||||||
|
|
||||||
@@ -130,30 +212,7 @@ public:
|
|||||||
GRID_ASSERT(A.size()==geom_srhs.npoint);
|
GRID_ASSERT(A.size()==geom_srhs.npoint);
|
||||||
BLAStoGrid(A[p],BLAS_A[p]);
|
BLAStoGrid(A[p],BLAS_A[p]);
|
||||||
}
|
}
|
||||||
void CopyMatrix (GeneralCoarseOp &_Op)
|
|
||||||
{
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
auto Aup = _Op.Cell.Extract(_Op._A[p]);
|
|
||||||
//Unpadded
|
|
||||||
GridtoBLAS(Aup,BLAS_A[p]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
void CheckMatrix (GeneralCoarseOp &_Op)
|
|
||||||
{
|
|
||||||
std::cout <<"************* Checking the little direc operator mRHS"<<std::endl;
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
//Unpadded
|
|
||||||
auto Aup = _Op.Cell.Extract(_Op._A[p]);
|
|
||||||
auto Ack = Aup;
|
|
||||||
BLAStoGrid(Ack,BLAS_A[p]);
|
|
||||||
std::cout << p<<" Ack "<<norm2(Ack)<<std::endl;
|
|
||||||
std::cout << p<<" Aup "<<norm2(Aup)<<std::endl;
|
|
||||||
}
|
|
||||||
std::cout <<"************* "<<std::endl;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Constructor takes the D dimensional coarse grid. Everything built here
|
// Constructor takes the D dimensional coarse grid. Everything built here
|
||||||
// is independent of Nrhs, in particular the matrix elements, which must
|
// is independent of Nrhs, in particular the matrix elements, which must
|
||||||
@@ -184,10 +243,24 @@ public:
|
|||||||
// Site identity mapping for A
|
// Site identity mapping for A
|
||||||
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++){
|
||||||
ComplexD *ptr = (ComplexD *)&BLAS_A[p][ss];
|
CoarseBLASScalar *ptr = (CoarseBLASScalar *)&BLAS_A[p][ss];
|
||||||
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
|
||||||
|
// never more legs than the stencil has.
|
||||||
|
LegGroup = (TargetBatch + unpadded_sites - 1)/unpadded_sites;
|
||||||
|
if ( LegGroup < 1 ) LegGroup = 1;
|
||||||
|
if ( LegGroup > geom.npoint ) LegGroup = geom.npoint;
|
||||||
|
BuildGroupedA();
|
||||||
|
std::cout << GridLogMessage << "MultiGeneralCoarsenedOperatorV2: stencil "
|
||||||
|
<< geom.npoint << " points, local coarse volume " << unpadded_sites
|
||||||
|
<< ", legs per GEMM " << LegGroup
|
||||||
|
<< " (batch " << LegGroup*unpadded_sites << ", "
|
||||||
|
<< LegGroups() << " full call(s)"
|
||||||
|
<< (LegRemainder() ? " + a remainder of "+std::to_string(LegRemainder()) : "")
|
||||||
|
<< ")" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~MultiGeneralCoarsenedOperatorV2()
|
virtual ~MultiGeneralCoarsenedOperatorV2()
|
||||||
@@ -209,6 +282,11 @@ public:
|
|||||||
|
|
||||||
BLAS_B.resize(0);
|
BLAS_B.resize(0);
|
||||||
BLAS_C.resize(0);
|
BLAS_C.resize(0);
|
||||||
|
BLAS_Cg.resize(0);
|
||||||
|
BLAS_CPg.resize(0);
|
||||||
|
BLAS_CPr.resize(0);
|
||||||
|
for(int g=0;g<(int)BLAS_BPg.size();g++){ BLAS_BPg[g].resize(0); }
|
||||||
|
BLAS_BPg.resize(0);
|
||||||
for(int p=0;p<BLAS_BP.size();p++){
|
for(int p=0;p<BLAS_BP.size();p++){
|
||||||
BLAS_BP[p].resize(0);
|
BLAS_BP[p].resize(0);
|
||||||
}
|
}
|
||||||
@@ -277,10 +355,33 @@ public:
|
|||||||
|
|
||||||
// Site identity mapping for C
|
// Site identity mapping for C
|
||||||
for(int ss=0;ss<unpadded_sites;ss++){
|
for(int ss=0;ss<unpadded_sites;ss++){
|
||||||
ComplexD *ptr = (ComplexD *)&BLAS_C[ss*nrhs];
|
CoarseBLASScalar *ptr = (CoarseBLASScalar *)&BLAS_C[ss*nrhs];
|
||||||
acceleratorPut(BLAS_CP[ss],ptr);
|
acceleratorPut(BLAS_CP[ss],ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Grouped output: LegGroup partial results, each the full C volume, and
|
||||||
|
// their pointer table. The batch index runs site fastest within a leg,
|
||||||
|
// matching the grouped A and B tables. The remainder call writes into
|
||||||
|
// the first few partials, so it needs a truncated copy of the table.
|
||||||
|
int nfull = LegGroups();
|
||||||
|
int rem = LegRemainder();
|
||||||
|
int ng = nfull + (rem?1:0);
|
||||||
|
BLAS_Cg.resize (LegGroup*nrhs*unpadded_sites);
|
||||||
|
BLAS_CPg.resize(LegGroup*unpadded_sites);
|
||||||
|
BLAS_CPr.resize(rem*unpadded_sites);
|
||||||
|
for(int l=0;l<LegGroup;l++){
|
||||||
|
for(int ss=0;ss<unpadded_sites;ss++){
|
||||||
|
CoarseBLASScalar *ptr = (CoarseBLASScalar *)&BLAS_Cg[(l*unpadded_sites+ss)*nrhs];
|
||||||
|
acceleratorPut(BLAS_CPg[l*unpadded_sites+ss],ptr);
|
||||||
|
if ( l<rem ) acceleratorPut(BLAS_CPr[l*unpadded_sites+ss],ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BLAS_BPg.resize(ng);
|
||||||
|
for(int g=0;g<ng;g++){
|
||||||
|
int legs = (g<nfull) ? LegGroup : rem;
|
||||||
|
BLAS_BPg[g].resize(legs*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
|
||||||
@@ -298,8 +399,9 @@ public:
|
|||||||
int32_t nbr = Stencil._entries[i]._offset*CComplex::Nsimd(); // oSite -> lSite, D dim
|
int32_t nbr = Stencil._entries[i]._offset*CComplex::Nsimd(); // oSite -> lSite, D dim
|
||||||
nbr = nbr*nrhs; // D -> D+1, rhs innermost
|
nbr = nbr*nrhs; // D -> D+1, rhs innermost
|
||||||
GRID_ASSERT(nbr<BLAS_B.size());
|
GRID_ASSERT(nbr<BLAS_B.size());
|
||||||
ComplexD * ptr = (ComplexD *)&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);
|
||||||
}
|
}
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
@@ -411,11 +513,43 @@ public:
|
|||||||
// Where q_k = delta_k . (2*M_PI/global_nb[mu])
|
// Where q_k = delta_k . (2*M_PI/global_nb[mu])
|
||||||
// Then A{ji}^{b,b+l} = M^{-1}_{lm} ComputeProj_{m,b,i,j}
|
// Then A{ji}^{b,b+l} = M^{-1}_{lm} ComputeProj_{m,b,i,j}
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Probe momenta. The stencil DISPLACEMENTS are the +-1 box; the momenta
|
||||||
|
// used to separate them are free, and are chosen here to span the
|
||||||
|
// Brillouin zone: k_mu = K_mu * shift_mu with K_mu ~ L_mu/3, so a phase
|
||||||
|
// is ~2pi/3 per unit displacement rather than 2pi/L_mu.
|
||||||
|
//
|
||||||
|
// This is what conditions the extraction. With K_mu = 1 every entry of
|
||||||
|
// the phase matrix tends to 1 as the coarse lattice grows, the matrix
|
||||||
|
// tends to rank one, and its inverse amplifies any error in the measured
|
||||||
|
// projections: at 24.24.16.32 the condition number is 2.7e5, so fp32
|
||||||
|
// projections give coarse matrix elements wrong by ~1e-3. Spread momenta
|
||||||
|
// bring it to ~20, independent of the lattice size.
|
||||||
|
//
|
||||||
|
// K_mu is stepped away from L_mu/2, where +k and -k alias into the same
|
||||||
|
// momentum and the matrix is singular.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void CoarsenMomenta(GridBase *CoarseGrid,Coordinate &K)
|
||||||
|
{
|
||||||
|
Coordinate clatt = CoarseGrid->GlobalDimensions();
|
||||||
|
int Nd = CoarseGrid->Nd();
|
||||||
|
K.resize(Nd);
|
||||||
|
for(int mu=0;mu<Nd;mu++){
|
||||||
|
int L = clatt[mu];
|
||||||
|
int k = (L>=3) ? (int)std::lround(L/3.0) : 1;
|
||||||
|
if ( k < 1 ) k = 1;
|
||||||
|
if ( (L>2) && ((2*k)%L == 0) ) k = k-1; // +k and -k must differ
|
||||||
|
if ( k < 1 ) k = 1;
|
||||||
|
K[mu] = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CoarsenFourierMatrix(GridBase *CoarseGrid,Eigen::MatrixXcd &invMkl)
|
void CoarsenFourierMatrix(GridBase *CoarseGrid,Eigen::MatrixXcd &invMkl)
|
||||||
{
|
{
|
||||||
const int npoint = geom_srhs.npoint;
|
const int npoint = geom_srhs.npoint;
|
||||||
Coordinate clatt = CoarseGrid->GlobalDimensions();
|
Coordinate clatt = CoarseGrid->GlobalDimensions();
|
||||||
int Nd = CoarseGrid->Nd();
|
int Nd = CoarseGrid->Nd();
|
||||||
|
Coordinate K; CoarsenMomenta(CoarseGrid,K);
|
||||||
|
|
||||||
Eigen::MatrixXcd Mkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
Eigen::MatrixXcd Mkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
||||||
ComplexD ci(0.0,1.0);
|
ComplexD ci(0.0,1.0);
|
||||||
@@ -424,13 +558,21 @@ public:
|
|||||||
ComplexD phase(0.0,0.0);
|
ComplexD phase(0.0,0.0);
|
||||||
for(int mu=0;mu<Nd;mu++){
|
for(int mu=0;mu<Nd;mu++){
|
||||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
||||||
phase=phase+TwoPiL*geom_srhs.shifts[k][mu]*geom_srhs.shifts[l][mu];
|
phase=phase+TwoPiL*K[mu]*geom_srhs.shifts[k][mu]*geom_srhs.shifts[l][mu];
|
||||||
}
|
}
|
||||||
phase=exp(phase*ci);
|
phase=exp(phase*ci);
|
||||||
Mkl(k,l) = phase;
|
Mkl(k,l) = phase;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
invMkl = Mkl.inverse();
|
invMkl = Mkl.inverse();
|
||||||
|
|
||||||
|
// The extraction's error amplification, printed because it is the thing
|
||||||
|
// that decides whether an fp32 coarse sector is usable.
|
||||||
|
Eigen::JacobiSVD<Eigen::MatrixXcd> svd(Mkl);
|
||||||
|
RealD cond = svd.singularValues()(0)/svd.singularValues()(npoint-1);
|
||||||
|
std::cout << GridLogMessage << "CoarsenOperator: probe momenta "<<K
|
||||||
|
<<" on coarse lattice "<<clatt
|
||||||
|
<<" ; Fourier matrix condition number "<<cond<<std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
@@ -477,10 +619,13 @@ public:
|
|||||||
const int npoint = geom_srhs.npoint;
|
const int npoint = geom_srhs.npoint;
|
||||||
Coordinate clatt = CoarseGrid->GlobalDimensions();
|
Coordinate clatt = CoarseGrid->GlobalDimensions();
|
||||||
int Nd = CoarseGrid->Nd();
|
int Nd = CoarseGrid->Nd();
|
||||||
|
Coordinate K; CoarsenMomenta(CoarseGrid,K);
|
||||||
ComplexD ci(0.0,1.0);
|
ComplexD ci(0.0,1.0);
|
||||||
|
|
||||||
typedef typename CComplex::scalar_type SComplex;
|
// The fine-side scratch carries the FINE level's precision, which need
|
||||||
FineComplexField one(grid); one=SComplex(1.0);
|
// not be the coarse one (fp64 fine, fp32 coarse at L1).
|
||||||
|
typedef typename GridTypeMapper<FineInner>::scalar_type FineScalar;
|
||||||
|
FineComplexField one(grid); one=FineScalar(1.0);
|
||||||
FineComplexField zz(grid); zz = Zero();
|
FineComplexField zz(grid); zz = Zero();
|
||||||
BlockComplexField pha_blk (BlockGrid);
|
BlockComplexField pha_blk (BlockGrid);
|
||||||
BlockComplexField blk_coor(BlockGrid);
|
BlockComplexField blk_coor(BlockGrid);
|
||||||
@@ -492,9 +637,9 @@ public:
|
|||||||
for(int mu=0;mu<Nd;mu++){
|
for(int mu=0;mu<Nd;mu++){
|
||||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
||||||
LatticeCoordinate(coor,mu);
|
LatticeCoordinate(coor,mu);
|
||||||
pha[p] = pha[p] + (TwoPiL * geom_srhs.shifts[p][mu]) * coor;
|
pha[p] = pha[p] + (TwoPiL * K[mu] * geom_srhs.shifts[p][mu]) * coor;
|
||||||
LatticeCoordinate(blk_coor,mu);
|
LatticeCoordinate(blk_coor,mu);
|
||||||
pha_blk = pha_blk + (TwoPiL * geom_srhs.shifts[p][mu]) * blk_coor;
|
pha_blk = pha_blk + (TwoPiL * K[mu] * geom_srhs.shifts[p][mu]) * blk_coor;
|
||||||
}
|
}
|
||||||
pha[p] =exp(pha[p] *ci);
|
pha[p] =exp(pha[p] *ci);
|
||||||
pha_blk=exp(pha_blk*ci);
|
pha_blk=exp(pha_blk*ci);
|
||||||
@@ -558,10 +703,25 @@ public:
|
|||||||
// ExtractSlice/InsertSlice pair per rhs; prefer the single RHS variant
|
// ExtractSlice/InsertSlice pair per rhs; prefer the single RHS variant
|
||||||
// below in that case.
|
// below in that case.
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Owning the transfer operator: the caller passes one, this imports the
|
||||||
|
// 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;
|
||||||
|
// it costs a second full basis store, which is why nothing new should use
|
||||||
|
// it (documentation/MultiGridMemoryAudit.md).
|
||||||
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
|
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
|
||||||
GridCartesian *FineGridMulti,
|
GridCartesian *FineGridMulti,
|
||||||
std::vector<FineField> &Subspace,
|
std::vector<FineField> &Subspace,
|
||||||
GridBase *CoarseGrid)
|
GridBase *CoarseGrid)
|
||||||
|
{
|
||||||
|
MultiRHSBlockProject<Lattice<Fobj> > Projector;
|
||||||
|
CoarsenOperator(linop,FineGridMulti,Subspace,CoarseGrid,Projector);
|
||||||
|
}
|
||||||
|
template<class Projector_t>
|
||||||
|
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
|
||||||
|
GridCartesian *FineGridMulti,
|
||||||
|
std::vector<FineField> &Subspace,
|
||||||
|
GridBase *CoarseGrid,
|
||||||
|
Projector_t &Projector)
|
||||||
{
|
{
|
||||||
RealD tproj=0.0, tmat=0.0, tphase=0.0, tphaseBZ=0.0, tslice=0.0, tinv=0.0;
|
RealD tproj=0.0, tmat=0.0, tphase=0.0, tphaseBZ=0.0, tslice=0.0, tinv=0.0;
|
||||||
|
|
||||||
@@ -585,7 +745,7 @@ public:
|
|||||||
BlockComplexField InnerProd(&BlockGrid);
|
BlockComplexField InnerProd(&BlockGrid);
|
||||||
blockOrthogonalise(InnerProd,Subspace);
|
blockOrthogonalise(InnerProd,Subspace);
|
||||||
|
|
||||||
MultiRHSBlockProject<Lattice<Fobj> > Projector;
|
// the caller owns it; import the basis we have just orthonormalised
|
||||||
Projector.Allocate(nbasis,grid,CoarseGrid);
|
Projector.Allocate(nbasis,grid,CoarseGrid);
|
||||||
Projector.ImportBasis(Subspace);
|
Projector.ImportBasis(Subspace);
|
||||||
|
|
||||||
@@ -674,6 +834,16 @@ public:
|
|||||||
std::vector<FineField> &Subspace,
|
std::vector<FineField> &Subspace,
|
||||||
GridBase *CoarseGrid,
|
GridBase *CoarseGrid,
|
||||||
int batch)
|
int batch)
|
||||||
|
{
|
||||||
|
MultiRHSBlockProject<Lattice<Fobj> > Projector;
|
||||||
|
CoarsenOperator(linop,Subspace,CoarseGrid,batch,Projector);
|
||||||
|
}
|
||||||
|
template<class Projector_t>
|
||||||
|
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
|
||||||
|
std::vector<FineField> &Subspace,
|
||||||
|
GridBase *CoarseGrid,
|
||||||
|
int batch,
|
||||||
|
Projector_t &Projector)
|
||||||
{
|
{
|
||||||
RealD tproj=0.0, tmat=0.0, tphase=0.0, tphaseBZ=0.0, tslice=0.0, tinv=0.0;
|
RealD tproj=0.0, tmat=0.0, tphase=0.0, tphaseBZ=0.0, tslice=0.0, tinv=0.0;
|
||||||
|
|
||||||
@@ -690,7 +860,7 @@ public:
|
|||||||
BlockComplexField InnerProd(&BlockGrid);
|
BlockComplexField InnerProd(&BlockGrid);
|
||||||
blockOrthogonalise(InnerProd,Subspace);
|
blockOrthogonalise(InnerProd,Subspace);
|
||||||
|
|
||||||
MultiRHSBlockProject<Lattice<Fobj> > Projector;
|
// the caller owns it; import the basis we have just orthonormalised
|
||||||
Projector.Allocate(nbasis,grid,CoarseGrid);
|
Projector.Allocate(nbasis,grid,CoarseGrid);
|
||||||
Projector.ImportBasis(Subspace);
|
Projector.ImportBasis(Subspace);
|
||||||
|
|
||||||
@@ -787,16 +957,15 @@ public:
|
|||||||
|
|
||||||
GRID_TRACE("CoarseV2Mult");
|
GRID_TRACE("CoarseV2Mult");
|
||||||
t_tot=-usecond();
|
t_tot=-usecond();
|
||||||
CoarseVector tin=in;
|
|
||||||
t_exch=-usecond();
|
t_exch=-usecond();
|
||||||
// lambda scope so the roctx range covers exactly the exchange; the
|
// The exchange takes its input by const reference, so it reads the
|
||||||
// PaddedCellFwd/BwdMPI markers inside it then nest properly.
|
// caller's field directly. lambda scope so the roctx range covers
|
||||||
|
// exactly the exchange; the PaddedCellFwd/BwdMPI markers inside it then
|
||||||
|
// nest properly.
|
||||||
CoarseVector pin = [&](){ GRID_TRACE("CoarseV2Exchange");
|
CoarseVector pin = [&](){ GRID_TRACE("CoarseV2Exchange");
|
||||||
return CellMulti->ExchangePeriodic(tin); }(); //padded input
|
return CellMulti->ExchangePeriodic(in); }(); //padded input
|
||||||
t_exch+=usecond();
|
t_exch+=usecond();
|
||||||
|
|
||||||
CoarseVector pout(pin.Grid());
|
|
||||||
|
|
||||||
int npoint = geom.npoint;
|
int npoint = geom.npoint;
|
||||||
typedef calcMatrix* Aview;
|
typedef calcMatrix* Aview;
|
||||||
typedef LatticeView<Cvec> Vview;
|
typedef LatticeView<Cvec> Vview;
|
||||||
@@ -825,22 +994,64 @@ public:
|
|||||||
|
|
||||||
t_mult=-usecond();
|
t_mult=-usecond();
|
||||||
{ GRID_TRACE("CoarseV2StencilGEMM");
|
{ GRID_TRACE("CoarseV2StencilGEMM");
|
||||||
for(int p=0;p<geom.npoint;p++){
|
// The scalar type selects the GEMM: Cgemm for an fp32 coarse space,
|
||||||
RealD c = 1.0;
|
// Zgemm for fp64.
|
||||||
if (p==0) c = 0.0;
|
if ( LegGroup == 1 ) {
|
||||||
ComplexD beta(c);
|
for(int p=0;p<geom.npoint;p++){
|
||||||
|
RealD c = (p==0) ? 0.0 : 1.0;
|
||||||
BLAS.gemmBatched(nbasis,nrhs,nbasis,
|
BLAS.gemmBatched(nbasis,nrhs,nbasis,
|
||||||
ComplexD(1.0),
|
CoarseBLASScalar(1.0),
|
||||||
BLAS_AP[p],
|
BLAS_AP[p],
|
||||||
BLAS_BP[p],
|
BLAS_BP[p],
|
||||||
ComplexD(c),
|
CoarseBLASScalar(c),
|
||||||
BLAS_CP);
|
BLAS_CP);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// LegGroup legs per call: the batch is LegGroup times the local volume
|
||||||
|
// and there are LegGroup partial outputs, accumulated across the
|
||||||
|
// groups and summed below. A final short call carries the remainder
|
||||||
|
// when the group does not divide the stencil; it writes into the first
|
||||||
|
// few partials, which the full calls have already initialised.
|
||||||
|
int nfull = LegGroups();
|
||||||
|
int rem = LegRemainder();
|
||||||
|
for(int g=0;g<nfull;g++){
|
||||||
|
RealD c = (g==0) ? 0.0 : 1.0;
|
||||||
|
BLAS.gemmBatched(nbasis,nrhs,nbasis,
|
||||||
|
CoarseBLASScalar(1.0),
|
||||||
|
BLAS_APg[g],
|
||||||
|
BLAS_BPg[g],
|
||||||
|
CoarseBLASScalar(c),
|
||||||
|
BLAS_CPg);
|
||||||
|
}
|
||||||
|
if ( rem ) {
|
||||||
|
RealD c = (nfull==0) ? 0.0 : 1.0;
|
||||||
|
BLAS.gemmBatched(nbasis,nrhs,nbasis,
|
||||||
|
CoarseBLASScalar(1.0),
|
||||||
|
BLAS_APg[nfull],
|
||||||
|
BLAS_BPg[nfull],
|
||||||
|
CoarseBLASScalar(c),
|
||||||
|
BLAS_CPr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
BLAS.synchronise();
|
BLAS.synchronise();
|
||||||
}
|
}
|
||||||
t_mult+=usecond();
|
t_mult+=usecond();
|
||||||
|
|
||||||
|
if ( LegGroup > 1 ) { GRID_TRACE("CoarseV2LegSum");
|
||||||
|
// Sum the LegGroup partial results into the single output buffer.
|
||||||
|
// Flat over scalars: a calcVector is nbasis of them and the partials
|
||||||
|
// are contiguous, one whole C volume after another.
|
||||||
|
int64_t nscalar = (int64_t)BLAS_C.size()*nbasis; // one whole C volume
|
||||||
|
const int G = LegGroup;
|
||||||
|
CoarseBLASScalar *dst = (CoarseBLASScalar *)&BLAS_C[0];
|
||||||
|
CoarseBLASScalar *src = (CoarseBLASScalar *)&BLAS_Cg[0];
|
||||||
|
accelerator_for(i,nscalar,1,{
|
||||||
|
CoarseBLASScalar sum = src[i];
|
||||||
|
for(int l=1;l<G;l++) sum = sum + src[(int64_t)l*nscalar + i];
|
||||||
|
dst[i] = sum;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
t_BtoG=-usecond();
|
t_BtoG=-usecond();
|
||||||
{ GRID_TRACE("CoarseV2BLASToGrid");
|
{ GRID_TRACE("CoarseV2BLASToGrid");
|
||||||
BLAStoGrid(out,BLAS_C);
|
BLAStoGrid(out,BLAS_C);
|
||||||
|
|||||||
@@ -20,20 +20,10 @@ Author: Peter Boyle <pboyle@bnl.gov>
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Grid/algorithms/deflation/MultiRHSBlockProject.h>
|
#include <Grid/algorithms/deflation/MultiRHSBlockProject.h>
|
||||||
|
#include <Grid/algorithms/multigrid/MrhsPreconditioner.h>
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
NAMESPACE_BEGIN(Grid);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// mrhs LinearFunction interface: vector-of-fields in, vector out.
|
|
||||||
// The outer level carries mrhs as std::vector<Field>; below it mrhs
|
|
||||||
// is PACKED into a single D+1 field (rhs = dim 0) and the coarse
|
|
||||||
// classes are plain LinearFunctions on that.
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class Field>
|
|
||||||
class MrhsLinearFunction {
|
|
||||||
public:
|
|
||||||
virtual void operator()(std::vector<Field> &in, std::vector<Field> &out) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Single-polynomial mrhs PGCR: one step length and one set of
|
// Single-polynomial mrhs PGCR: one step length and one set of
|
||||||
@@ -54,18 +44,21 @@ public:
|
|||||||
RealD Tolerance; Integer MaxIterations; int mmax,nstep,steps,level;
|
RealD Tolerance; Integer MaxIterations; int mmax,nstep,steps,level;
|
||||||
int ZeroGuess = 0; int FirstCycle = 0;
|
int ZeroGuess = 0; int FirstCycle = 0;
|
||||||
std::string name = "Level 1";
|
std::string name = "Level 1";
|
||||||
|
// Trace range names carry the instance name, so a profile separates the
|
||||||
|
// solvers that otherwise nest as one shared range.
|
||||||
|
std::string trace_op = "MrhsPGCR::vOp", trace_orthog = "MrhsPGCR orthog";
|
||||||
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 Level(int lv){ name = "Level " + std::to_string(lv); level=lv; }
|
||||||
void Name(std::string n){ name = n; }
|
void Name(std::string n){ name = n; trace_op = name+" MrhsPGCR::vOp"; trace_orthog = name+" MrhsPGCR orthog"; }
|
||||||
void SetZeroGuess(int z){ ZeroGuess=z; }
|
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){ level=1; }
|
||||||
static RealD vnorm2(std::vector<Field> &x){ RealD s=0; for(auto &f:x) s+=norm2(f); return s; }
|
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 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]); }
|
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("MrhsPGCR::vOp"); for(int r=0;r<(int)in.size();r++) Linop.Op(in[r],out[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; int nrhs=src.size(); GridBase *grid=src[0].Grid();
|
||||||
ssq=vnorm2(src); rsq=Tolerance*Tolerance*ssq;
|
ssq=vnorm2(src); rsq=Tolerance*Tolerance*ssq;
|
||||||
@@ -108,7 +101,7 @@ public:
|
|||||||
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("MrhsPGCR orthog");
|
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
|
||||||
// (independent, batchable), then apply. Complex coefficient: the
|
// (independent, batchable), then apply. Complex coefficient: the
|
||||||
// operator is non-Hermitian, real(<q_j,Aq>) alone left q's non-orthogonal.
|
// operator is non-Hermitian, real(<q_j,Aq>) alone left q's non-orthogonal.
|
||||||
@@ -203,19 +196,23 @@ public:
|
|||||||
// is wired by the composer to PVdagMLinearOperator::SloppyComms (a
|
// is wired by the composer to PVdagMLinearOperator::SloppyComms (a
|
||||||
// no-op by default), replacing the file-scope global the example used.
|
// no-op by default), replacing the file-scope global the example used.
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
template<class FineField, class MrhsCoarseVector, class FineSmoother>
|
// Projector_t defaults to a transfer operator whose STORE matches FineField,
|
||||||
class MrhsTwoLevelMG : public MrhsLinearFunction<FineField> {
|
// which is the case whenever the coarse sector and the fine level share a
|
||||||
|
// precision; pass it explicitly when they differ.
|
||||||
|
template<class FineField, class MrhsCoarseVector, class FineSmoother,
|
||||||
|
class Projector_t = MultiRHSBlockProject<FineField> >
|
||||||
|
class MrhsTwoLevelMG : public MrhsPreconditioner<FineField> {
|
||||||
public:
|
public:
|
||||||
typedef MrhsCoarseVector CoarseVector;
|
typedef MrhsCoarseVector CoarseVector;
|
||||||
LinearOperatorBase<FineField> &_FineOperator;
|
LinearOperatorBase<FineField> &_FineOperator;
|
||||||
FineSmoother &_PostSmoother;
|
FineSmoother &_PostSmoother;
|
||||||
MultiRHSBlockProject<FineField> &_Projector;
|
Projector_t &_Projector; // store precision need not match FineField
|
||||||
LinearFunction<CoarseVector> &_CoarseSolve;
|
LinearFunction<CoarseVector> &_CoarseSolve;
|
||||||
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,
|
||||||
MultiRHSBlockProject<FineField> &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){}
|
||||||
@@ -253,4 +250,52 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// The fp64/fp32 seam of the solve chain. The outer Krylov hands fp64
|
||||||
|
// residuals to its preconditioner; this adapter converts them to fp32,
|
||||||
|
// runs an fp32 preconditioner (the whole V-cycle), and converts the
|
||||||
|
// correction back. Two precisionChange per outer step per rhs, on
|
||||||
|
// workspaces built once. The outer operator never sees fp32.
|
||||||
|
//
|
||||||
|
// The fp32 scratch is allocated ON FIRST USE, not in the constructor: the
|
||||||
|
// solver builds this seam whichever precision is selected, and at Nrhs 12
|
||||||
|
// on a 48^3x96 Ls=24 rank the scratch is 2 GB of device memory. A seam
|
||||||
|
// that is never called must cost nothing. Same idiom as the GCR history
|
||||||
|
// vectors: re-made only if the grid or the rhs count changes.
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
template<class FieldD, class FieldF>
|
||||||
|
class MrhsMixedPrecPreconditioner : public MrhsPreconditioner<FieldD> {
|
||||||
|
public:
|
||||||
|
MrhsPreconditioner<FieldF> &_Inner;
|
||||||
|
precisionChangeWorkspace _ws_d2f; // out fp32, in fp64
|
||||||
|
precisionChangeWorkspace _ws_f2d; // out fp64, in fp32
|
||||||
|
GridBase *_gridF;
|
||||||
|
std::vector<FieldF> _in_f, _out_f;
|
||||||
|
MrhsMixedPrecPreconditioner(MrhsPreconditioner<FieldF> &Inner, GridBase *gridD, GridBase *gridF, int nrhs)
|
||||||
|
: _Inner(Inner), _ws_d2f(gridF,gridD), _ws_f2d(gridD,gridF), _gridF(gridF) {}
|
||||||
|
void Scratch(int nrhs){
|
||||||
|
if ( (int)_in_f.size() == nrhs ) return;
|
||||||
|
_in_f.clear(); _in_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); }
|
||||||
|
}
|
||||||
|
virtual void operator()(std::vector<FieldD> &in, std::vector<FieldD> &out){
|
||||||
|
GRID_TRACE("MGPrecisionSeam");
|
||||||
|
int nrhs=in.size(); Scratch(nrhs);
|
||||||
|
for(int r=0;r<nrhs;r++) precisionChange(_in_f[r],in[r],_ws_d2f);
|
||||||
|
_Inner(_in_f,_out_f);
|
||||||
|
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
|
||||||
|
virtual void Vstart(std::vector<FieldD> &x, std::vector<FieldD> &src){
|
||||||
|
GRID_TRACE("MGPrecisionSeamVstart");
|
||||||
|
int nrhs=src.size(); Scratch(nrhs);
|
||||||
|
for(int r=0;r<nrhs;r++) precisionChange(_in_f[r],src[r],_ws_d2f);
|
||||||
|
_Inner.Vstart(_out_f,_in_f);
|
||||||
|
for(int r=0;r<nrhs;r++) precisionChange(x[r],_out_f[r],_ws_f2d);
|
||||||
|
}
|
||||||
|
virtual void ResetTimers(void) { _Inner.ResetTimers(); }
|
||||||
|
virtual void ReportTimers(const std::string &prefix) { _Inner.ReportTimers(prefix); }
|
||||||
|
};
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
NAMESPACE_END(Grid);
|
||||||
@@ -30,13 +30,22 @@ 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/GeneralCoarsenedMatrix.h>
|
|
||||||
#include <Grid/algorithms/multigrid/GeneralCoarsenedMatrixMultiRHS.h>
|
|
||||||
#include <Grid/algorithms/multigrid/GeneralCoarsenedMatrixMultiRHSV2.h>
|
#include <Grid/algorithms/multigrid/GeneralCoarsenedMatrixMultiRHSV2.h>
|
||||||
|
// DEPRECATED: the V1 coarse operators and the Aggregation-based single-RHS
|
||||||
|
// ADEF-2. Nothing in the library uses them; the PVdagM and HDCG chains are
|
||||||
|
// on V2 (PVdagMMultiGrid.h, HDCGMultiGrid.h). Kept so the pre-2026 drivers
|
||||||
|
// in tests/debug and examples still build; removing this block is the
|
||||||
|
// deletion gate for them.
|
||||||
|
#include <Grid/algorithms/multigrid/deprecated/GeneralCoarsenedMatrix.h>
|
||||||
|
#include <Grid/algorithms/multigrid/deprecated/GeneralCoarsenedMatrixMultiRHS.h>
|
||||||
|
#include <Grid/algorithms/multigrid/deprecated/TwoLevelADEF2.h>
|
||||||
#include <Grid/algorithms/multigrid/MrhsPromotedOperator.h>
|
#include <Grid/algorithms/multigrid/MrhsPromotedOperator.h>
|
||||||
#include <Grid/algorithms/multigrid/Smoothers.h>
|
#include <Grid/algorithms/multigrid/Smoothers.h>
|
||||||
#include <Grid/algorithms/multigrid/PVdagMMultiGridParams.h>
|
#include <Grid/algorithms/multigrid/PVdagMMultiGridParams.h>
|
||||||
// PVdagMOperators.h / MrhsMultiGrid.h / PVdagMMultiGrid.h /
|
// PVdagMOperators.h / MrhsMultiGrid.h / PVdagMMultiGrid.h /
|
||||||
// DenseCoarseMatrix.h are NOT in this umbrella: consumers of the PVdagM
|
// DenseCoarseMatrix.h / MultiGridIO.h / HDCGMultiGrid.h are NOT in this
|
||||||
// chain include PVdagMMultiGrid.h explicitly (it pulls the dense stack
|
// umbrella: consumers include PVdagMMultiGrid.h or HDCGMultiGrid.h
|
||||||
// and BLAS).
|
// explicitly (they pull the dense stack, BLAS, and the scidac I/O, which
|
||||||
|
// is declared after Algorithms.h in Grid.h). Keeping MrhsMultiGrid.h out
|
||||||
|
// of the Algorithms.h chain also keeps its class names away from the
|
||||||
|
// pre-2026 drivers that define their own copies.
|
||||||
@@ -20,6 +20,7 @@ Author: Peter Boyle <pboyle@bnl.gov>
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Grid/algorithms/multigrid/DenseCoarseMatrix.h>
|
#include <Grid/algorithms/multigrid/DenseCoarseMatrix.h>
|
||||||
|
#include <Grid/algorithms/multigrid/MultiGridIO.h>
|
||||||
#include <Grid/algorithms/multigrid/PVdagMOperators.h>
|
#include <Grid/algorithms/multigrid/PVdagMOperators.h>
|
||||||
#include <Grid/algorithms/multigrid/MrhsMultiGrid.h>
|
#include <Grid/algorithms/multigrid/MrhsMultiGrid.h>
|
||||||
|
|
||||||
@@ -48,36 +49,13 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
//////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Subspace I/O: bare-vector scidac records. Loaded vectors are RAW --
|
// Subspace I/O is in MultiGridIO.h (shared with the HDCG chain).
|
||||||
// deliberately NOT re-orthogonalised: CoarsenOperator block
|
// Loaded vectors are RAW -- deliberately NOT re-orthogonalised:
|
||||||
// orthonormalises in place, and projecting a block-orthonormal vector
|
// CoarsenOperator block orthonormalises in place, and projecting a
|
||||||
// onto its own block-orthonormalised aggregation gives e_k with the
|
// block-orthonormal vector onto its own block-orthonormalised
|
||||||
// near-null content silently gone. GramGuard below catches that.
|
// aggregation gives e_k with the near-null content silently gone.
|
||||||
|
// GramGuard below catches that.
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
template <class Field>
|
|
||||||
void saveSubspace(std::vector<Field> &subspace, std::string const fname){
|
|
||||||
#ifdef HAVE_LIME
|
|
||||||
Grid::emptyUserRecord record;
|
|
||||||
Grid::ScidacWriter SW(subspace[0].Grid()->IsBoss());
|
|
||||||
SW.open(fname);
|
|
||||||
for (int k = 0; k < (int)subspace.size(); k++) {
|
|
||||||
SW.writeScidacFieldRecord(subspace[k], record);
|
|
||||||
}
|
|
||||||
SW.close();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
template <class Field>
|
|
||||||
void loadSubspace(std::vector<Field> &subspace, std::string const fname){
|
|
||||||
#ifdef HAVE_LIME
|
|
||||||
Grid::emptyUserRecord record;
|
|
||||||
Grid::ScidacReader SR;
|
|
||||||
SR.open(fname);
|
|
||||||
for (int k = 0; k < (int)subspace.size(); k++) {
|
|
||||||
SR.readScidacFieldRecord(subspace[k], record);
|
|
||||||
}
|
|
||||||
SR.close();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// ||<v|v> - I||_F over a set of coarse vectors. Small means the raw
|
// ||<v|v> - I||_F over a set of coarse vectors. Small means the raw
|
||||||
@@ -146,14 +124,14 @@ public:
|
|||||||
|
|
||||||
clatt.resize(4); cclatt.resize(4);
|
clatt.resize(4); cclatt.resize(4);
|
||||||
for(int d=0;d<4;d++){
|
for(int d=0;d<4;d++){
|
||||||
GRID_ASSERT( fdims[d+1] % P.Block[d] == 0 );
|
GRID_ASSERT( fdims[d+1] % P.Block1[d] == 0 );
|
||||||
clatt[d] = fdims[d+1] / P.Block[d];
|
clatt[d] = fdims[d+1] / P.Block1[d];
|
||||||
}
|
}
|
||||||
for(int d=0;d<4;d++){
|
for(int d=0;d<4;d++){
|
||||||
GRID_ASSERT( clatt[d] % P.Block2[d] == 0 );
|
GRID_ASSERT( clatt[d] % P.Block2[d] == 0 );
|
||||||
cclatt[d] = clatt[d] / P.Block2[d];
|
cclatt[d] = clatt[d] / P.Block2[d];
|
||||||
}
|
}
|
||||||
std::cout << GridLogMessage << "MGCoarseGrids: Block " << P.Block << " coarse lattice " << clatt << std::endl;
|
std::cout << GridLogMessage << "MGCoarseGrids: Block1 " << P.Block1 << " coarse lattice " << clatt << std::endl;
|
||||||
std::cout << GridLogMessage << "MGCoarseGrids: Block2 " << P.Block2 << " coarse-coarse lattice " << cclatt << std::endl;
|
std::cout << GridLogMessage << "MGCoarseGrids: Block2 " << P.Block2 << " coarse-coarse lattice " << cclatt << std::endl;
|
||||||
|
|
||||||
Coordinate c5latt({1,clatt[0],clatt[1],clatt[2],clatt[3]});
|
Coordinate c5latt({1,clatt[0],clatt[1],clatt[2],clatt[3]});
|
||||||
@@ -181,6 +159,41 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// The fp32 fine grids: same lattice and decomposition as the fp64 fine
|
||||||
|
// grid, vComplexF SIMD layout. For the fp32 fine level inside the
|
||||||
|
// preconditioner (the fp32 fermion operators and the fp32 transfer
|
||||||
|
// operator borrow these). Owned here; declare BEFORE the coarsening.
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
class MGFineGridsF {
|
||||||
|
public:
|
||||||
|
GridCartesian *UGridF;
|
||||||
|
GridRedBlackCartesian *UrbGridF;
|
||||||
|
GridCartesian *FGridF;
|
||||||
|
GridRedBlackCartesian *FrbGridF;
|
||||||
|
|
||||||
|
MGFineGridsF(GridCartesian *FGrid)
|
||||||
|
{
|
||||||
|
Coordinate fdims = FGrid->FullDimensions(); // {Ls, x,y,z,t}
|
||||||
|
Coordinate fmpi = FGrid->_processors;
|
||||||
|
GRID_ASSERT( fdims.size() == 5 );
|
||||||
|
int Ls = fdims[0];
|
||||||
|
Coordinate latt4({fdims[1],fdims[2],fdims[3],fdims[4]});
|
||||||
|
Coordinate mpi4 ({fmpi[1], fmpi[2], fmpi[3], fmpi[4]});
|
||||||
|
UGridF = SpaceTimeGrid::makeFourDimGrid(latt4,GridDefaultSimd(Nd,vComplexF::Nsimd()),mpi4);
|
||||||
|
UrbGridF = SpaceTimeGrid::makeFourDimRedBlackGrid(UGridF);
|
||||||
|
FGridF = SpaceTimeGrid::makeFiveDimGrid(Ls,UGridF);
|
||||||
|
FrbGridF = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGridF);
|
||||||
|
}
|
||||||
|
~MGFineGridsF()
|
||||||
|
{
|
||||||
|
delete FrbGridF;
|
||||||
|
delete FGridF;
|
||||||
|
delete UrbGridF;
|
||||||
|
delete UGridF;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// ALL the coarsening information for one gauge configuration.
|
// ALL the coarsening information for one gauge configuration.
|
||||||
//
|
//
|
||||||
@@ -202,19 +215,41 @@ public:
|
|||||||
typedef MultiGeneralCoarsenedOperatorV2<Fobj,CComplex,nbasis> CoarseOperator;
|
typedef MultiGeneralCoarsenedOperatorV2<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;
|
||||||
typedef iScalar<CComplex> CComplex2; // coarsening deepens the nest by one iScalar
|
// Every coarse level carries the same site type iVector<CComplex,nbasis>:
|
||||||
typedef MultiGeneralCoarsenedOperatorV2<CoarseSiteObj,CComplex2,nbasis> CoarseCoarseOperator;
|
// the coefficient scalar does not deepen with the level (the operator keeps
|
||||||
|
// its own deeper scratch type for the block inner products). Levels are
|
||||||
|
// told apart by their grids, not their C++ types.
|
||||||
|
typedef MultiGeneralCoarsenedOperatorV2<CoarseSiteObj,CComplex,nbasis> CoarseCoarseOperator;
|
||||||
typedef typename CoarseCoarseOperator::CoarseVector CoarseCoarseVector;
|
typedef typename CoarseCoarseOperator::CoarseVector CoarseCoarseVector;
|
||||||
typedef DenseCoarseMatrix<CComplex2,nbasis> DenseBottom;
|
typedef DenseCoarseMatrix<CComplex,nbasis> DenseBottom;
|
||||||
|
// The fp32 fine field, derived from the fp64 one: the fp32 fine level of
|
||||||
|
// the preconditioner projects through the SAME basis in fp32 layout.
|
||||||
|
typedef typename GridTypeMapper<Fobj>::SinglePrecision FobjF;
|
||||||
|
typedef Lattice<FobjF> FineFieldF;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// ONE fine transfer operator. Its STORE follows the coarse sector --
|
||||||
|
// the coarse space is what it feeds -- while its import and export
|
||||||
|
// accept EITHER fine precision, because they are already a layout
|
||||||
|
// transformation and a scalar conversion inside one costs nothing.
|
||||||
|
// So an fp64 outer level and an fp32 V-cycle share this single object,
|
||||||
|
// and no second basis store, handover or retained basis is needed.
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
static const bool CoarseIsSingle =
|
||||||
|
( sizeof(typename GridTypeMapper<CComplex>::scalar_type) == sizeof(ComplexF) );
|
||||||
|
typedef typename std::conditional<CoarseIsSingle,FineFieldF,FineField>::type ProjectorField;
|
||||||
|
typedef MultiRHSBlockProject<ProjectorField> ProjectorL1_t;
|
||||||
|
typedef MultiRHSBlockProject<CoarseVector> ProjectorL2_t;
|
||||||
|
|
||||||
MGCoarseGrids &Grids; // borrowed
|
MGCoarseGrids &Grids; // borrowed
|
||||||
|
MGFineGridsF &GridsF; // borrowed
|
||||||
MGSetupParams Params;
|
MGSetupParams Params;
|
||||||
NextToNearestStencilGeometry5D geom;
|
NextToNearestStencilGeometry5D geom;
|
||||||
NextToNearestStencilGeometry5D geom2;
|
NextToNearestStencilGeometry5D geom2;
|
||||||
CoarseOperator CoarseOpPV;
|
CoarseOperator CoarseOpPV;
|
||||||
CoarseCoarseOperator CoarseOpL2;
|
CoarseCoarseOperator CoarseOpL2;
|
||||||
MultiRHSBlockProject<FineField> MrhsProjector;
|
ProjectorL1_t MrhsProjectorL1; // store follows the coarse sector
|
||||||
MultiRHSBlockProject<CoarseVector> MrhsProjectorL2;
|
ProjectorL2_t MrhsProjectorL2;
|
||||||
DenseBottom *DenseCC;
|
DenseBottom *DenseCC;
|
||||||
std::vector<FineField> rawNull; // RAW fine near-null basis
|
std::vector<FineField> rawNull; // RAW fine near-null basis
|
||||||
std::vector<CoarseVector> rawPsi; // RAW coarse near-null basis
|
std::vector<CoarseVector> rawPsi; // RAW coarse near-null basis
|
||||||
@@ -222,8 +257,9 @@ public:
|
|||||||
GridCartesian *CCMrhs;
|
GridCartesian *CCMrhs;
|
||||||
int nrhs;
|
int nrhs;
|
||||||
|
|
||||||
PVdagMMultiGridCoarsening(MGCoarseGrids &_Grids, const MGSetupParams &P)
|
PVdagMMultiGridCoarsening(MGCoarseGrids &_Grids, MGFineGridsF &_GridsF, const MGSetupParams &P)
|
||||||
: Grids(_Grids),
|
: Grids(_Grids),
|
||||||
|
GridsF(_GridsF),
|
||||||
Params(P),
|
Params(P),
|
||||||
geom (_Grids.Coarse5d),
|
geom (_Grids.Coarse5d),
|
||||||
geom2(_Grids.CoarseCoarse5d),
|
geom2(_Grids.CoarseCoarse5d),
|
||||||
@@ -289,7 +325,7 @@ public:
|
|||||||
// halos follow the FineSloppyComms policy, restored EXACT on exit).
|
// halos follow the FineSloppyComms policy, restored EXACT on exit).
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
template<class PVdagMOp>
|
template<class PVdagMOp>
|
||||||
void Coarsen(PVdagMOp &FineOp)
|
void Coarsen(PVdagMOp &FineOp, int retain_basis=0)
|
||||||
{
|
{
|
||||||
GRID_ASSERT( rawNull.size() == nbasis );
|
GRID_ASSERT( rawNull.size() == nbasis );
|
||||||
|
|
||||||
@@ -301,18 +337,22 @@ public:
|
|||||||
std::cout << GridLogMessage << "PVdagMMultiGridCoarsening: L1 CoarsenOperator, batch "
|
std::cout << GridLogMessage << "PVdagMMultiGridCoarsening: L1 CoarsenOperator, batch "
|
||||||
<< Grids.batch << std::endl;
|
<< Grids.batch << std::endl;
|
||||||
|
|
||||||
|
// CoarsenOperator orthonormalises sub in place and imports it into OUR
|
||||||
|
// transfer operator, which it then uses. One basis store, imported once.
|
||||||
|
// The projector's grid carries the STORE's SIMD layout, which is the fp32
|
||||||
|
// fine grid when the coarse sector is fp32; fp64 vectors convert on import.
|
||||||
|
MrhsProjectorL1.Allocate(nbasis,
|
||||||
|
CoarseIsSingle ? (GridBase *)GridsF.FGridF : (GridBase *)Grids.FGrid,
|
||||||
|
Grids.Coarse5d);
|
||||||
FineOp.SloppyComms(Params.FineSloppyComms);
|
FineOp.SloppyComms(Params.FineSloppyComms);
|
||||||
CoarseOpPV.CoarsenOperator(FineOp,sub,Grids.Coarse5d,Grids.batch);
|
CoarseOpPV.CoarsenOperator(FineOp,sub,Grids.Coarse5d,Grids.batch,MrhsProjectorL1);
|
||||||
FineOp.SloppyComms(0);
|
FineOp.SloppyComms(0);
|
||||||
|
// The Lattice copy of the basis has served its purpose: the transfer
|
||||||
// Transfer operators from the orthonormalised basis; then the
|
// operator holds it in BLAS layout from here on.
|
||||||
// Galerkin images of the RAW basis define the L2 null space.
|
|
||||||
MrhsProjector.Allocate(nbasis,Grids.FGrid,Grids.Coarse5d);
|
|
||||||
MrhsProjector.ImportBasis(sub);
|
|
||||||
sub.clear(); sub.shrink_to_fit();
|
sub.clear(); sub.shrink_to_fit();
|
||||||
|
|
||||||
std::vector<CoarseVector> psi(nbasis,Grids.Coarse5d);
|
std::vector<CoarseVector> psi(nbasis,Grids.Coarse5d);
|
||||||
MrhsProjector.blockProject(rawNull,psi);
|
MrhsProjectorL1.blockProject(rawNull,psi);
|
||||||
GramGuard("psi_coarse",psi,Grids.Coarse5d);
|
GramGuard("psi_coarse",psi,Grids.Coarse5d);
|
||||||
|
|
||||||
rawPsi.clear();
|
rawPsi.clear();
|
||||||
@@ -324,16 +364,25 @@ public:
|
|||||||
NonHermitianLinearOperator<CoarseOperator,CoarseVector> LinOpCoarse(CoarseOpPV);
|
NonHermitianLinearOperator<CoarseOperator,CoarseVector> LinOpCoarse(CoarseOpPV);
|
||||||
std::cout << GridLogMessage << "PVdagMMultiGridCoarsening: L2 CoarsenOperator, batch "
|
std::cout << GridLogMessage << "PVdagMMultiGridCoarsening: L2 CoarsenOperator, batch "
|
||||||
<< Grids.batch << std::endl;
|
<< Grids.batch << std::endl;
|
||||||
CoarseOpL2.CoarsenOperator(LinOpCoarse,Grids.CoarseBatch,psi,Grids.CoarseCoarse5d);
|
|
||||||
|
|
||||||
MrhsProjectorL2.Allocate(nbasis,Grids.Coarse5d,Grids.CoarseCoarse5d);
|
MrhsProjectorL2.Allocate(nbasis,Grids.Coarse5d,Grids.CoarseCoarse5d);
|
||||||
MrhsProjectorL2.ImportBasis(psi); // now block orthonormal
|
CoarseOpL2.CoarsenOperator(LinOpCoarse,Grids.CoarseBatch,psi,Grids.CoarseCoarse5d,MrhsProjectorL2);
|
||||||
{
|
{
|
||||||
std::vector<CoarseCoarseVector> psi_cc(nbasis,Grids.CoarseCoarse5d);
|
std::vector<CoarseCoarseVector> psi_cc(nbasis,Grids.CoarseCoarse5d);
|
||||||
MrhsProjectorL2.blockProject(rawPsi,psi_cc); // RAW vectors in
|
MrhsProjectorL2.blockProject(rawPsi,psi_cc); // RAW vectors in
|
||||||
GramGuard("psi_cc",psi_cc,Grids.CoarseCoarse5d);
|
GramGuard("psi_cc",psi_cc,Grids.CoarseCoarse5d);
|
||||||
}
|
}
|
||||||
|
// The RAW basis has done its work (the L2 null space above was its last
|
||||||
|
// use) and is the single largest resident block of the setup, so it is
|
||||||
|
// freed. retain_basis keeps it for a caller that coarsens again from the
|
||||||
|
// same basis; Setup.RetainSubspace keeps it for the object's whole life,
|
||||||
|
// which is what a fixed-basis rebuild on a changed gauge field (HMC)
|
||||||
|
// needs. A valence solve never rebuilds.
|
||||||
|
if ( !retain_basis && !Params.RetainSubspace ) DiscardBasis();
|
||||||
|
MrhsProjectorL1.ReleaseScratch();
|
||||||
|
MrhsProjectorL2.ReleaseScratch();
|
||||||
|
|
||||||
nrhs = -1; // both operators left at the batch grids
|
nrhs = -1; // both operators left at the batch grids
|
||||||
|
ReportDeviceFootprint("after Coarsen");
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@@ -373,6 +422,7 @@ public:
|
|||||||
CoarseOpL2.ReleaseGrid(); // let go before the grid dies
|
CoarseOpL2.ReleaseGrid(); // let go before the grid dies
|
||||||
delete CoarseCoarseOne;
|
delete CoarseCoarseOne;
|
||||||
nrhs = -1;
|
nrhs = -1;
|
||||||
|
ReportDeviceFootprint("after BuildDenseBottom");
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@@ -398,6 +448,66 @@ public:
|
|||||||
std::cout << GridLogMessage << "PVdagMMultiGridCoarsening: operators at Nrhs " << nr << std::endl;
|
std::cout << GridLogMessage << "PVdagMMultiGridCoarsening: operators at Nrhs " << nr << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Galerkin certificate for the L1 coarsening: ||A_c x - P^dag A P x||
|
||||||
|
// over ||A_c x|| on a random coarse vector, through the SAME transfer
|
||||||
|
// operators the solve uses. The coarse operator is compared with its
|
||||||
|
// own definition, so this reads the ROUNDING level of the coarsening
|
||||||
|
// (fp64 coarse ~1e-13, fp32 coarse ~1e-6) independently of how the
|
||||||
|
// solve converges. Drives the fine operator with EXACT halos; at more
|
||||||
|
// than one rank the sloppy-halo coarsening error is included in the
|
||||||
|
// reading. Nrhs 1; the caller's Nrhs is restored by its own SetNrhs.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
template<class PVdagMOp>
|
||||||
|
RealD CertifyCoarsening(PVdagMOp &FineOp)
|
||||||
|
{
|
||||||
|
SetNrhs(1);
|
||||||
|
CoarseVector xc(CMrhs), Acx(CMrhs), PtAPx(CMrhs);
|
||||||
|
GridParallelRNG cRNG(CMrhs); cRNG.SeedFixedIntegers(std::vector<int>({21,22,23,24}));
|
||||||
|
random(cRNG,xc);
|
||||||
|
|
||||||
|
CoarseOpPV.M(xc,Acx); // A_c x
|
||||||
|
|
||||||
|
std::vector<FineField> Px(1,Grids.FGrid), APx(1,Grids.FGrid);
|
||||||
|
MrhsProjectorL1.blockPromote(Px,xc); // P x
|
||||||
|
FineOp.SloppyComms(0);
|
||||||
|
FineOp.Op(Px[0],APx[0]); // A P x
|
||||||
|
MrhsProjectorL1.blockProject(APx,PtAPx); // P^dag A P x
|
||||||
|
|
||||||
|
CoarseVector d(CMrhs); d = Acx - PtAPx;
|
||||||
|
RealD rel = std::sqrt(norm2(d)/norm2(Acx));
|
||||||
|
std::cout << GridLogMessage << "PVdagMMultiGridCoarsening: L1 GALERKIN CERTIFICATE "
|
||||||
|
<< "||A_c x - P^dag A P x||/||A_c x|| = " << rel << std::endl;
|
||||||
|
return rel;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// The device memory this coarsening holds that the memory manager
|
||||||
|
// CANNOT evict: BLAS stores, which are plain device allocations.
|
||||||
|
// Lattice fields are omitted on purpose -- they are evictable, so they
|
||||||
|
// cost eviction traffic, not failure. What is reported is what has to
|
||||||
|
// fit alongside the manager's cache, the comms buffers and the shm
|
||||||
|
// segment, which is the budget an out-of-memory run actually breaks.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void ReportDeviceFootprint(const std::string &when)
|
||||||
|
{
|
||||||
|
uint64_t p1 = MrhsProjectorL1.DeviceBytes();
|
||||||
|
uint64_t p2 = MrhsProjectorL2.DeviceBytes();
|
||||||
|
uint64_t o1 = CoarseOpPV.DeviceBytes();
|
||||||
|
uint64_t o2 = CoarseOpL2.DeviceBytes();
|
||||||
|
uint64_t dn = DenseCC ? DenseCC->DeviceBytes() : 0;
|
||||||
|
uint64_t tot = p1+p2+o1+o2+dn;
|
||||||
|
const double G = 1024.*1024.*1024.;
|
||||||
|
std::cout << GridLogMessage << "PVdagMMultiGridCoarsening (" << when << "): resident device memory/rank "
|
||||||
|
<< tot/G << " GB = transfer L1 " << p1/G << " + L2 " << p2/G
|
||||||
|
<< " + operator L1 " << o1/G << " + L2 " << o2/G
|
||||||
|
<< " + dense " << dn/G << " GB" << std::endl;
|
||||||
|
std::cout << GridLogMessage << "PVdagMMultiGridCoarsening (" << when << "): not evictable; must fit alongside the "
|
||||||
|
<< MemoryManager::DeviceMaxBytes/G
|
||||||
|
<< " GB manager cache, the comms buffers and the shm segment" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Free the retained RAW bases (valence use: coarsening is final for
|
// Free the retained RAW bases (valence use: coarsening is final for
|
||||||
// this configuration and the fine basis is ~GB-scale host memory).
|
// this configuration and the fine basis is ~GB-scale host memory).
|
||||||
@@ -420,16 +530,18 @@ public:
|
|||||||
// (its applications define what "converged" means), asserted by the
|
// (its applications define what "converged" means), asserted by the
|
||||||
// FINAL true-residual report in Solve.
|
// FINAL true-residual report in Solve.
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
template<class Matrix,class Coarsening>
|
template<class Matrix,class MatrixF,class Coarsening>
|
||||||
class PVdagMMultiGridSolver {
|
class PVdagMMultiGridSolver {
|
||||||
public:
|
public:
|
||||||
typedef typename Coarsening::FineField FineField;
|
typedef typename Coarsening::FineField FineField;
|
||||||
|
typedef typename Coarsening::FineFieldF FineFieldF;
|
||||||
typedef typename Coarsening::CoarseOperator CoarseOperator;
|
typedef typename Coarsening::CoarseOperator CoarseOperator;
|
||||||
typedef typename Coarsening::CoarseVector CoarseVector;
|
typedef typename Coarsening::CoarseVector CoarseVector;
|
||||||
typedef typename Coarsening::CoarseCoarseOperator CoarseCoarseOperator;
|
typedef typename Coarsening::CoarseCoarseOperator CoarseCoarseOperator;
|
||||||
typedef typename Coarsening::CoarseCoarseVector CoarseCoarseVector;
|
typedef typename Coarsening::CoarseCoarseVector CoarseCoarseVector;
|
||||||
typedef typename Coarsening::DenseBottom DenseBottom;
|
typedef typename Coarsening::DenseBottom DenseBottom;
|
||||||
typedef PrecGeneralisedConjugateResidualNonHermitian<FineField> FineSmoother_t;
|
typedef PrecGeneralisedConjugateResidualNonHermitian<FineField> FineSmoother_t;
|
||||||
|
typedef PrecGeneralisedConjugateResidualNonHermitian<FineFieldF> FineSmootherF_t;
|
||||||
typedef PrecGeneralisedConjugateResidualNonHermitian<CoarseVector> CoarseKrylov_t;
|
typedef PrecGeneralisedConjugateResidualNonHermitian<CoarseVector> CoarseKrylov_t;
|
||||||
|
|
||||||
Coarsening &C;
|
Coarsening &C;
|
||||||
@@ -447,11 +559,20 @@ public:
|
|||||||
CoarseKrylov_t CoarseSmootherGCR;
|
CoarseKrylov_t CoarseSmootherGCR;
|
||||||
MrhsCoarseThreeLevelPrec<CoarseVector,CoarseCoarseVector> L2to3Precon;
|
MrhsCoarseThreeLevelPrec<CoarseVector,CoarseCoarseVector> L2to3Precon;
|
||||||
CoarseKrylov_t L2PGCR;
|
CoarseKrylov_t L2PGCR;
|
||||||
|
// The fp64 fine level of the preconditioner
|
||||||
FineSmoother_t SmootherGCR;
|
FineSmoother_t SmootherGCR;
|
||||||
MrhsTwoLevelMG<FineField,CoarseVector,FineSmoother_t> ThreeLevelPrecon;
|
MrhsTwoLevelMG<FineField,CoarseVector,FineSmoother_t,typename Coarsening::ProjectorL1_t> ThreeLevelPrecon;
|
||||||
|
// The fp32 fine level of the preconditioner, behind the fp64/fp32 seam.
|
||||||
|
// Both share the coarse chain (L2PGCR); the outer Krylov picks one.
|
||||||
|
PVdagMLinearOperator<MatrixF,FineFieldF> PVdagMF;
|
||||||
|
ShiftedPVdagMLinearOperator<MatrixF,FineFieldF> ShiftedPVdagMF;
|
||||||
|
TrivialPrecon<FineFieldF> simple_fine_f;
|
||||||
|
FineSmootherF_t SmootherGCRF;
|
||||||
|
MrhsTwoLevelMG<FineFieldF,CoarseVector,FineSmootherF_t,typename Coarsening::ProjectorL1_t> ThreeLevelPreconF;
|
||||||
|
MrhsMixedPrecPreconditioner<FineField,FineFieldF> PreconSeam;
|
||||||
MrhsPGCRNonHermitian<FineField> L1PGCR;
|
MrhsPGCRNonHermitian<FineField> L1PGCR;
|
||||||
|
|
||||||
PVdagMMultiGridSolver(Matrix &Ddwf, Matrix &Dpv,
|
PVdagMMultiGridSolver(Matrix &Ddwf, Matrix &Dpv, MatrixF &DdwfF, MatrixF &DpvF,
|
||||||
Coarsening &_C, const PVdagMMultiGridParams &P, int nr)
|
Coarsening &_C, const PVdagMMultiGridParams &P, int nr)
|
||||||
: C(_C), Params(P), nrhs(nr),
|
: C(_C), Params(P), nrhs(nr),
|
||||||
_regrid((C.SetNrhs(nr),0)), // members below capture C.CMrhs/C.CCMrhs
|
_regrid((C.SetNrhs(nr),0)), // members below capture C.CMrhs/C.CCMrhs
|
||||||
@@ -468,11 +589,20 @@ public:
|
|||||||
L2PGCR(P.CoarseSolver.Tol,P.CoarseSolver.Order/16,LinOpC,L2to3Precon,P.CoarseSolver.Mmax,16),
|
L2PGCR(P.CoarseSolver.Tol,P.CoarseSolver.Order/16,LinOpC,L2to3Precon,P.CoarseSolver.Mmax,16),
|
||||||
// Fine smoother: one restart of nstep GCR steps, tolerance 0 = fixed work.
|
// Fine smoother: one restart of nstep GCR steps, tolerance 0 = fixed work.
|
||||||
SmootherGCR(0.0,1,ShiftedPVdagM,simple_fine,P.FineSmoother.Mmax,P.FineSmoother.Nstep),
|
SmootherGCR(0.0,1,ShiftedPVdagM,simple_fine,P.FineSmoother.Mmax,P.FineSmoother.Nstep),
|
||||||
ThreeLevelPrecon(PVdagM,SmootherGCR,C.MrhsProjector,L2PGCR,C.Grids.Coarse5d,C.CMrhs),
|
ThreeLevelPrecon(PVdagM,SmootherGCR,C.MrhsProjectorL1,L2PGCR,C.Grids.Coarse5d,C.CMrhs),
|
||||||
L1PGCR(P.Outer.Tol,P.Outer.MaxIterations,PVdagM,ThreeLevelPrecon,P.Outer.Mmax,P.Outer.Nstep)
|
PVdagMF(DdwfF,DpvF),
|
||||||
|
ShiftedPVdagMF(P.FineSmoother.Shift,DdwfF,DpvF),
|
||||||
|
SmootherGCRF(0.0,1,ShiftedPVdagMF,simple_fine_f,P.FineSmoother.Mmax,P.FineSmoother.Nstep),
|
||||||
|
ThreeLevelPreconF(PVdagMF,SmootherGCRF,C.MrhsProjectorL1,L2PGCR,C.Grids.Coarse5d,C.CMrhs),
|
||||||
|
PreconSeam(ThreeLevelPreconF,Ddwf.FermionGrid(),DdwfF.FermionGrid(),nr),
|
||||||
|
L1PGCR(P.Outer.Tol,P.Outer.MaxIterations,PVdagM,
|
||||||
|
(P.Setup.FinePrecision==MGPrecision::fp32)
|
||||||
|
? static_cast<MrhsLinearFunction<FineField>&>(PreconSeam)
|
||||||
|
: static_cast<MrhsLinearFunction<FineField>&>(ThreeLevelPrecon),
|
||||||
|
P.Outer.Mmax,P.Outer.Nstep)
|
||||||
{
|
{
|
||||||
GRID_ASSERT( C.DenseCC != nullptr );
|
GRID_ASSERT( C.DenseCC != nullptr );
|
||||||
|
|
||||||
CoarseSmootherGCR.Level(2);
|
CoarseSmootherGCR.Level(2);
|
||||||
CoarseSmootherGCR.Name("Csmoother");
|
CoarseSmootherGCR.Name("Csmoother");
|
||||||
CoarseSmootherGCR.SetZeroGuess(1);
|
CoarseSmootherGCR.SetZeroGuess(1);
|
||||||
@@ -480,23 +610,39 @@ public:
|
|||||||
L2PGCR.Level(2);
|
L2PGCR.Level(2);
|
||||||
L2PGCR.Name("Couter");
|
L2PGCR.Name("Couter");
|
||||||
L2PGCR.SetZeroGuess(1);
|
L2PGCR.SetZeroGuess(1);
|
||||||
|
|
||||||
SmootherGCR.Level(1);
|
SmootherGCR.Level(1);
|
||||||
SmootherGCR.Name("Fsmoother");
|
SmootherGCR.Name("Fsmoother");
|
||||||
SmootherGCR.SetZeroGuess(1);
|
SmootherGCR.SetZeroGuess(1);
|
||||||
|
|
||||||
|
SmootherGCRF.Level(1);
|
||||||
|
SmootherGCRF.Name("Fsmoother");
|
||||||
|
SmootherGCRF.SetZeroGuess(1);
|
||||||
|
|
||||||
L1PGCR.Level(1);
|
L1PGCR.Level(1);
|
||||||
L1PGCR.Name("Fouter");
|
L1PGCR.Name("Fouter");
|
||||||
L1PGCR.SetZeroGuess(1);
|
L1PGCR.SetZeroGuess(1);
|
||||||
|
|
||||||
// The V-cycle is preconditioner: sloppy halos inside, exact restored on exit.
|
// The V-cycle is preconditioner: sloppy halos inside, exact restored on exit.
|
||||||
ThreeLevelPrecon.SetSloppy = [this](int s){ PVdagM.SloppyComms(s); };
|
ThreeLevelPrecon.SetSloppy = [this](int s){ PVdagM.SloppyComms(s); };
|
||||||
ThreeLevelPrecon.SloppyComms = Params.Setup.FineSloppyComms;
|
ThreeLevelPrecon.SloppyComms = Params.Setup.FineSloppyComms;
|
||||||
|
ThreeLevelPreconF.SetSloppy = [this](int s){ PVdagMF.SloppyComms(s); };
|
||||||
|
ThreeLevelPreconF.SloppyComms = Params.Setup.FineSloppyComms;
|
||||||
PVdagM.SloppyComms(0);
|
PVdagM.SloppyComms(0);
|
||||||
std::cout << GridLogMessage << "PVdagMMultiGridSolver: Nrhs " << nr
|
PVdagMF.SloppyComms(0);
|
||||||
<< ", fine halo policy: preconditioner+coarsening "
|
// Three stages, three arithmetics, and a wire format that follows the
|
||||||
<< (Params.Setup.FineSloppyComms ? "SLOPPY (fp32 wire)" : "exact")
|
// ARITHMETIC of the stage it serves: fp32 on an fp64 operator, bf16 on
|
||||||
<< ", outer Krylov EXACT" << std::endl;
|
// an fp32 one. The coarsening always applies the fp64 operator, so its
|
||||||
|
// wire is fp32 whatever the preconditioner runs in.
|
||||||
|
const bool sloppy = Params.Setup.FineSloppyComms;
|
||||||
|
const bool pfp32 = (Params.Setup.FinePrecision==MGPrecision::fp32);
|
||||||
|
std::cout << GridLogMessage << "PVdagMMultiGridSolver: Nrhs " << nr << std::endl;
|
||||||
|
std::cout << GridLogMessage << " outer Krylov : fp64, halos EXACT (the true residual is measured here)" << std::endl;
|
||||||
|
std::cout << GridLogMessage << " preconditioner fine: " << (pfp32 ? "fp32 (seam at the outer Krylov)" : "fp64")
|
||||||
|
<< ", halos " << (sloppy ? (pfp32 ? "SLOPPY bf16 wire" : "SLOPPY fp32 wire") : "exact") << std::endl;
|
||||||
|
std::cout << GridLogMessage << " coarsening (done) : fp64 operator, halos "
|
||||||
|
<< (sloppy ? "SLOPPY fp32 wire" : "exact")
|
||||||
|
<< " -- the coarse operator does not follow FinePrecision" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Solve(std::vector<FineField> &src, std::vector<FineField> &sol)
|
void Solve(std::vector<FineField> &src, std::vector<FineField> &sol)
|
||||||
|
|||||||
@@ -42,6 +42,12 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
// not consumer knobs.
|
// not consumer knobs.
|
||||||
//////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Arithmetic precision of the fine level INSIDE the preconditioner -- the
|
||||||
|
// smoother and the V-cycle's own fine residuals. The outer Krylov is
|
||||||
|
// always fp64: the fp64/fp32 seam sits at its preconditioner call.
|
||||||
|
// Orthogonal to FineSloppyComms, which is the halo WIRE format.
|
||||||
|
GRID_SERIALIZABLE_ENUM(MGPrecision, undef, fp64, 1, fp32, 2);
|
||||||
|
|
||||||
// The smoother is the adaptive shifted PGCR -- the one correct route for
|
// The smoother is the adaptive shifted PGCR -- the one correct route for
|
||||||
// this non-Hermitian chain (stationary replay and Chebyshev were explored
|
// this non-Hermitian chain (stationary replay and Chebyshev were explored
|
||||||
// and did not win here; Chebyshev remains the HERMITIAN chain's smoother).
|
// and did not win here; Chebyshev remains the HERMITIAN chain's smoother).
|
||||||
@@ -79,14 +85,17 @@ struct MGDenseParams : Serializable {
|
|||||||
|
|
||||||
struct MGSetupParams : Serializable {
|
struct MGSetupParams : Serializable {
|
||||||
GRID_SERIALIZABLE_CLASS_MEMBERS(MGSetupParams,
|
GRID_SERIALIZABLE_CLASS_MEMBERS(MGSetupParams,
|
||||||
std::vector<int>, Block, // fine -> coarse blocking
|
std::vector<int>, Block1, // fine -> coarse blocking
|
||||||
std::vector<int>, Block2, // coarse -> coarse-coarse blocking
|
std::vector<int>, Block2, // coarse -> coarse-coarse blocking
|
||||||
int, CoarsenBatch,
|
int, CoarsenBatch,
|
||||||
std::string, SubspaceFile, // scidac; empty = create from noise, no I/O
|
std::string, SubspaceFile, // scidac; empty = create from noise, no I/O
|
||||||
int, FineSloppyComms);// fp32 wire INSIDE the preconditioner only
|
int, FineSloppyComms, // reduced-precision halo WIRE inside the preconditioner only: fp32 on the fp64 operator, bf16 on the fp32 operator
|
||||||
|
MGPrecision, FinePrecision, // fine-level ARITHMETIC inside the preconditioner
|
||||||
|
int, RetainSubspace); // keep the RAW basis after setup (nbasis fine vectors); needed ONLY for a fixed-basis rebuild on a changed gauge field (HMC). A valence solve never rebuilds, so 0 frees it.
|
||||||
MGSetupParams()
|
MGSetupParams()
|
||||||
: Block({2,2,3,3}), Block2({4,4,2,4}), CoarsenBatch(9),
|
: Block1({2,2,3,3}), Block2({4,4,2,4}), CoarsenBatch(9),
|
||||||
SubspaceFile(""), FineSloppyComms(1) {};
|
SubspaceFile(""), FineSloppyComms(1), FinePrecision(MGPrecision::fp64),
|
||||||
|
RetainSubspace(0) {};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PVdagMMultiGridParams : Serializable {
|
struct PVdagMMultiGridParams : Serializable {
|
||||||
@@ -104,9 +113,10 @@ struct PVdagMMultiGridParams : Serializable {
|
|||||||
|
|
||||||
inline void CheckValidity(const PVdagMMultiGridParams &P)
|
inline void CheckValidity(const PVdagMMultiGridParams &P)
|
||||||
{
|
{
|
||||||
GRID_ASSERT( P.Setup.Block.size() == 4 );
|
GRID_ASSERT( P.Setup.Block1.size() == 4 );
|
||||||
GRID_ASSERT( P.Setup.Block2.size() == 4 );
|
GRID_ASSERT( P.Setup.Block2.size() == 4 );
|
||||||
GRID_ASSERT( P.Setup.CoarsenBatch >= 1 );
|
GRID_ASSERT( P.Setup.CoarsenBatch >= 1 );
|
||||||
|
GRID_ASSERT( P.Setup.FinePrecision != MGPrecision::undef );
|
||||||
GRID_ASSERT( P.FineSmoother.Nstep > 0 );
|
GRID_ASSERT( P.FineSmoother.Nstep > 0 );
|
||||||
GRID_ASSERT( P.CoarseSmoother.Nstep > 0 );
|
GRID_ASSERT( P.CoarseSmoother.Nstep > 0 );
|
||||||
GRID_ASSERT( P.CoarseSolver.Tol > 0.0 );
|
GRID_ASSERT( P.CoarseSolver.Tol > 0.0 );
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
// smoother operator such as shifted PVdagM.
|
// smoother operator such as shifted PVdagM.
|
||||||
// ChebyshevInverter one Chebyshev-corrected step with residual print.
|
// ChebyshevInverter one Chebyshev-corrected step with residual print.
|
||||||
// MirsSmoother shifted-MdagM CG, HDCG arXiv:1402.2585.
|
// MirsSmoother shifted-MdagM CG, HDCG arXiv:1402.2585.
|
||||||
|
// CGSmoother fixed-iteration CG on an already-shifted
|
||||||
|
// Hermitian operator: the mrhs HDCG smoother.
|
||||||
// GCRReplaySmoother replays a GCR's recorded step lengths a_k and
|
// GCRReplaySmoother replays a GCR's recorded step lengths a_k and
|
||||||
// orthogonalisation coefficients b_kj with NO
|
// orthogonalisation coefficients b_kj with NO
|
||||||
// inner products: one matvec per step, zero
|
// inner products: one matvec per step, zero
|
||||||
@@ -191,6 +193,30 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Fixed-iteration CG on an (already shifted) Hermitian operator, tolerance
|
||||||
|
// zero so the work is fixed. NOT a stationary preconditioner: the CG
|
||||||
|
// coefficients depend on the input, so the map is nonlinear. Use under a
|
||||||
|
// flexible outer (fPcg); BlockCGrQ needs the Chebyshev smoother.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
template<class Field> class CGSmoother : public LinearFunction<Field>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using LinearFunction<Field>::operator();
|
||||||
|
LinearOperatorBase<Field> &_Op;
|
||||||
|
int iters;
|
||||||
|
CGSmoother(int _iters, LinearOperatorBase<Field> &Op) : _Op(Op), iters(_iters)
|
||||||
|
{
|
||||||
|
std::cout << GridLogMessage << "CGSmoother order " << iters << std::endl;
|
||||||
|
}
|
||||||
|
void operator() (const Field &in, Field &out)
|
||||||
|
{
|
||||||
|
ConjugateGradient<Field> CG(0.0,iters,false); // never converges: by design
|
||||||
|
out = Zero();
|
||||||
|
CG(_Op,in,out);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
// Replay of a recorded GCR with a trivial preconditioner:
|
// Replay of a recorded GCR with a trivial preconditioner:
|
||||||
// p_0 = r_0 ; x_{k+1} = x_k + a_k p_k ; r_{k+1} = r_k - a_k A p_k ;
|
// p_0 = r_0 ; x_{k+1} = x_k + a_k p_k ; r_{k+1} = r_k - a_k A p_k ;
|
||||||
|
|||||||
@@ -210,6 +210,14 @@ accelerator_inline void convertType(vComplexD2 & out, const vComplexF & in) {
|
|||||||
precisionChange(out,in);
|
precisionChange(out,in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Precision change in the lex (unvectorised) chart: one lane, so a plain
|
||||||
|
// scalar conversion. Reached by the block operations on an fp32 coarse
|
||||||
|
// space, whose inner products accumulate in double and convert back.
|
||||||
|
accelerator_inline void convertType(sComplexF & out, const sComplexD & in) { out.v = ComplexF(in.v); }
|
||||||
|
accelerator_inline void convertType(sComplexD & out, const sComplexF & in) { out.v = ComplexD(in.v); }
|
||||||
|
accelerator_inline void convertType(sRealF & out, const sRealD & in) { out.v = RealF(in.v); }
|
||||||
|
accelerator_inline void convertType(sRealD & out, const sRealF & in) { out.v = RealD(in.v); }
|
||||||
|
|
||||||
template<typename T1,typename T2>
|
template<typename T1,typename T2>
|
||||||
accelerator_inline void convertType(iScalar<T1> & out, const iScalar<T2> & in) {
|
accelerator_inline void convertType(iScalar<T1> & out, const iScalar<T2> & in) {
|
||||||
convertType(out._internal,in._internal);
|
convertType(out._internal,in._internal);
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef Integer Integerified;
|
typedef Integer Integerified;
|
||||||
typedef RealD DoublePrecision;
|
typedef RealD DoublePrecision;
|
||||||
typedef RealD DoublePrecision2;
|
typedef RealD DoublePrecision2;
|
||||||
|
typedef RealF SinglePrecision;
|
||||||
};
|
};
|
||||||
template<> struct GridTypeMapper<RealD> : public GridTypeMapper_Base {
|
template<> struct GridTypeMapper<RealD> : public GridTypeMapper_Base {
|
||||||
typedef RealD scalar_type;
|
typedef RealD scalar_type;
|
||||||
@@ -124,6 +125,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef Integer Integerified;
|
typedef Integer Integerified;
|
||||||
typedef RealD DoublePrecision;
|
typedef RealD DoublePrecision;
|
||||||
typedef RealD DoublePrecision2;
|
typedef RealD DoublePrecision2;
|
||||||
|
typedef RealF SinglePrecision;
|
||||||
};
|
};
|
||||||
template<> struct GridTypeMapper<ComplexF> : public GridTypeMapper_Base {
|
template<> struct GridTypeMapper<ComplexF> : public GridTypeMapper_Base {
|
||||||
typedef ComplexF scalar_type;
|
typedef ComplexF scalar_type;
|
||||||
@@ -138,6 +140,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef Integer Integerified;
|
typedef Integer Integerified;
|
||||||
typedef ComplexD DoublePrecision;
|
typedef ComplexD DoublePrecision;
|
||||||
typedef ComplexD DoublePrecision2;
|
typedef ComplexD DoublePrecision2;
|
||||||
|
typedef ComplexF SinglePrecision;
|
||||||
};
|
};
|
||||||
template<> struct GridTypeMapper<ComplexD> : public GridTypeMapper_Base {
|
template<> struct GridTypeMapper<ComplexD> : public GridTypeMapper_Base {
|
||||||
typedef ComplexD scalar_type;
|
typedef ComplexD scalar_type;
|
||||||
@@ -152,6 +155,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef Integer Integerified;
|
typedef Integer Integerified;
|
||||||
typedef ComplexD DoublePrecision;
|
typedef ComplexD DoublePrecision;
|
||||||
typedef ComplexD DoublePrecision2;
|
typedef ComplexD DoublePrecision2;
|
||||||
|
typedef ComplexF SinglePrecision;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(GRID_CUDA) || defined(GRID_HIP)
|
#if defined(GRID_CUDA) || defined(GRID_HIP)
|
||||||
@@ -168,6 +172,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef Integer Integerified;
|
typedef Integer Integerified;
|
||||||
typedef scalar_typeD DoublePrecision;
|
typedef scalar_typeD DoublePrecision;
|
||||||
typedef scalar_typeD DoublePrecision2;
|
typedef scalar_typeD DoublePrecision2;
|
||||||
|
typedef std::complex<float> SinglePrecision;
|
||||||
};
|
};
|
||||||
template<> struct GridTypeMapper<std::complex<double> > : public GridTypeMapper_Base {
|
template<> struct GridTypeMapper<std::complex<double> > : public GridTypeMapper_Base {
|
||||||
typedef std::complex<double> scalar_type;
|
typedef std::complex<double> scalar_type;
|
||||||
@@ -182,6 +187,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef Integer Integerified;
|
typedef Integer Integerified;
|
||||||
typedef scalar_typeD DoublePrecision;
|
typedef scalar_typeD DoublePrecision;
|
||||||
typedef scalar_typeD DoublePrecision2;
|
typedef scalar_typeD DoublePrecision2;
|
||||||
|
typedef std::complex<float> SinglePrecision;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -198,6 +204,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef Integer Integerified;
|
typedef Integer Integerified;
|
||||||
typedef void DoublePrecision;
|
typedef void DoublePrecision;
|
||||||
typedef void DoublePrecision2;
|
typedef void DoublePrecision2;
|
||||||
|
typedef void SinglePrecision;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<> struct GridTypeMapper<vRealF> : public GridTypeMapper_Base {
|
template<> struct GridTypeMapper<vRealF> : public GridTypeMapper_Base {
|
||||||
@@ -213,6 +220,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef vInteger Integerified;
|
typedef vInteger Integerified;
|
||||||
typedef vRealD DoublePrecision;
|
typedef vRealD DoublePrecision;
|
||||||
typedef vRealD2 DoublePrecision2;
|
typedef vRealD2 DoublePrecision2;
|
||||||
|
typedef vRealF SinglePrecision;
|
||||||
};
|
};
|
||||||
template<> struct GridTypeMapper<vRealD> : public GridTypeMapper_Base {
|
template<> struct GridTypeMapper<vRealD> : public GridTypeMapper_Base {
|
||||||
typedef RealD scalar_type;
|
typedef RealD scalar_type;
|
||||||
@@ -227,6 +235,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef vInteger Integerified;
|
typedef vInteger Integerified;
|
||||||
typedef vRealD DoublePrecision;
|
typedef vRealD DoublePrecision;
|
||||||
typedef vRealD DoublePrecision2;
|
typedef vRealD DoublePrecision2;
|
||||||
|
typedef vRealF SinglePrecision;
|
||||||
};
|
};
|
||||||
template<> struct GridTypeMapper<vRealD2> : public GridTypeMapper_Base {
|
template<> struct GridTypeMapper<vRealD2> : public GridTypeMapper_Base {
|
||||||
typedef RealD scalar_type;
|
typedef RealD scalar_type;
|
||||||
@@ -241,6 +250,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef vInteger Integerified;
|
typedef vInteger Integerified;
|
||||||
typedef vRealD2 DoublePrecision;
|
typedef vRealD2 DoublePrecision;
|
||||||
typedef vRealD2 DoublePrecision2;
|
typedef vRealD2 DoublePrecision2;
|
||||||
|
typedef vRealF SinglePrecision;
|
||||||
};
|
};
|
||||||
template<> struct GridTypeMapper<vRealH> : public GridTypeMapper_Base {
|
template<> struct GridTypeMapper<vRealH> : public GridTypeMapper_Base {
|
||||||
// Fixme this is incomplete until Grid supports fp16 or bfp16 arithmetic types
|
// Fixme this is incomplete until Grid supports fp16 or bfp16 arithmetic types
|
||||||
@@ -256,6 +266,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef vInteger Integerified;
|
typedef vInteger Integerified;
|
||||||
typedef vRealD DoublePrecision;
|
typedef vRealD DoublePrecision;
|
||||||
typedef vRealD DoublePrecision2;
|
typedef vRealD DoublePrecision2;
|
||||||
|
typedef vRealF SinglePrecision;
|
||||||
};
|
};
|
||||||
template<> struct GridTypeMapper<vComplexH> : public GridTypeMapper_Base {
|
template<> struct GridTypeMapper<vComplexH> : public GridTypeMapper_Base {
|
||||||
// Fixme this is incomplete until Grid supports fp16 or bfp16 arithmetic types
|
// Fixme this is incomplete until Grid supports fp16 or bfp16 arithmetic types
|
||||||
@@ -271,6 +282,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef vInteger Integerified;
|
typedef vInteger Integerified;
|
||||||
typedef vComplexD DoublePrecision;
|
typedef vComplexD DoublePrecision;
|
||||||
typedef vComplexD DoublePrecision2;
|
typedef vComplexD DoublePrecision2;
|
||||||
|
typedef vComplexF SinglePrecision;
|
||||||
};
|
};
|
||||||
template<> struct GridTypeMapper<vComplexF> : public GridTypeMapper_Base {
|
template<> struct GridTypeMapper<vComplexF> : public GridTypeMapper_Base {
|
||||||
typedef ComplexF scalar_type;
|
typedef ComplexF scalar_type;
|
||||||
@@ -285,6 +297,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef vInteger Integerified;
|
typedef vInteger Integerified;
|
||||||
typedef vComplexD DoublePrecision;
|
typedef vComplexD DoublePrecision;
|
||||||
typedef vComplexD2 DoublePrecision2;
|
typedef vComplexD2 DoublePrecision2;
|
||||||
|
typedef vComplexF SinglePrecision;
|
||||||
};
|
};
|
||||||
template<> struct GridTypeMapper<vComplexD> : public GridTypeMapper_Base {
|
template<> struct GridTypeMapper<vComplexD> : public GridTypeMapper_Base {
|
||||||
typedef ComplexD scalar_type;
|
typedef ComplexD scalar_type;
|
||||||
@@ -299,6 +312,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef vInteger Integerified;
|
typedef vInteger Integerified;
|
||||||
typedef vComplexD DoublePrecision;
|
typedef vComplexD DoublePrecision;
|
||||||
typedef vComplexD DoublePrecision2;
|
typedef vComplexD DoublePrecision2;
|
||||||
|
typedef vComplexF SinglePrecision;
|
||||||
};
|
};
|
||||||
template<> struct GridTypeMapper<vComplexD2> : public GridTypeMapper_Base {
|
template<> struct GridTypeMapper<vComplexD2> : public GridTypeMapper_Base {
|
||||||
typedef ComplexD scalar_type;
|
typedef ComplexD scalar_type;
|
||||||
@@ -313,6 +327,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef vInteger Integerified;
|
typedef vInteger Integerified;
|
||||||
typedef vComplexD2 DoublePrecision;
|
typedef vComplexD2 DoublePrecision;
|
||||||
typedef vComplexD2 DoublePrecision2;
|
typedef vComplexD2 DoublePrecision2;
|
||||||
|
typedef vComplexF SinglePrecision;
|
||||||
};
|
};
|
||||||
template<> struct GridTypeMapper<vInteger> : public GridTypeMapper_Base {
|
template<> struct GridTypeMapper<vInteger> : public GridTypeMapper_Base {
|
||||||
typedef Integer scalar_type;
|
typedef Integer scalar_type;
|
||||||
@@ -327,6 +342,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef vInteger Integerified;
|
typedef vInteger Integerified;
|
||||||
typedef void DoublePrecision;
|
typedef void DoublePrecision;
|
||||||
typedef void DoublePrecision2;
|
typedef void DoublePrecision2;
|
||||||
|
typedef void SinglePrecision;
|
||||||
};
|
};
|
||||||
template<> struct GridTypeMapper<sRealF> : public GridTypeMapper_Base {
|
template<> struct GridTypeMapper<sRealF> : public GridTypeMapper_Base {
|
||||||
typedef RealF scalar_type;
|
typedef RealF scalar_type;
|
||||||
@@ -341,6 +357,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef sInteger Integerified;
|
typedef sInteger Integerified;
|
||||||
typedef sRealD DoublePrecision;
|
typedef sRealD DoublePrecision;
|
||||||
typedef sRealD DoublePrecision2;
|
typedef sRealD DoublePrecision2;
|
||||||
|
typedef sRealF SinglePrecision;
|
||||||
};
|
};
|
||||||
template<> struct GridTypeMapper<sRealD> : public GridTypeMapper_Base {
|
template<> struct GridTypeMapper<sRealD> : public GridTypeMapper_Base {
|
||||||
typedef RealD scalar_type;
|
typedef RealD scalar_type;
|
||||||
@@ -355,6 +372,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef sInteger Integerified;
|
typedef sInteger Integerified;
|
||||||
typedef sRealD DoublePrecision;
|
typedef sRealD DoublePrecision;
|
||||||
typedef sRealD DoublePrecision2;
|
typedef sRealD DoublePrecision2;
|
||||||
|
typedef sRealF SinglePrecision;
|
||||||
};
|
};
|
||||||
template<> struct GridTypeMapper<sComplexF> : public GridTypeMapper_Base {
|
template<> struct GridTypeMapper<sComplexF> : public GridTypeMapper_Base {
|
||||||
typedef ComplexF scalar_type;
|
typedef ComplexF scalar_type;
|
||||||
@@ -369,6 +387,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef sInteger Integerified;
|
typedef sInteger Integerified;
|
||||||
typedef sComplexD DoublePrecision;
|
typedef sComplexD DoublePrecision;
|
||||||
typedef sComplexD DoublePrecision2;
|
typedef sComplexD DoublePrecision2;
|
||||||
|
typedef sComplexF SinglePrecision;
|
||||||
};
|
};
|
||||||
template<> struct GridTypeMapper<sComplexD> : public GridTypeMapper_Base {
|
template<> struct GridTypeMapper<sComplexD> : public GridTypeMapper_Base {
|
||||||
typedef ComplexD scalar_type;
|
typedef ComplexD scalar_type;
|
||||||
@@ -383,6 +402,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef sInteger Integerified;
|
typedef sInteger Integerified;
|
||||||
typedef sComplexD DoublePrecision;
|
typedef sComplexD DoublePrecision;
|
||||||
typedef sComplexD DoublePrecision2;
|
typedef sComplexD DoublePrecision2;
|
||||||
|
typedef sComplexF SinglePrecision;
|
||||||
};
|
};
|
||||||
template<> struct GridTypeMapper<sInteger> : public GridTypeMapper_Base {
|
template<> struct GridTypeMapper<sInteger> : public GridTypeMapper_Base {
|
||||||
typedef Integer scalar_type;
|
typedef Integer scalar_type;
|
||||||
@@ -397,6 +417,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef sInteger Integerified;
|
typedef sInteger Integerified;
|
||||||
typedef void DoublePrecision;
|
typedef void DoublePrecision;
|
||||||
typedef void DoublePrecision2;
|
typedef void DoublePrecision2;
|
||||||
|
typedef void SinglePrecision;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define GridTypeMapper_RepeatedTypes \
|
#define GridTypeMapper_RepeatedTypes \
|
||||||
@@ -417,6 +438,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
using Integerified = iScalar<typename BaseTraits::Integerified>;
|
using Integerified = iScalar<typename BaseTraits::Integerified>;
|
||||||
using DoublePrecision = iScalar<typename BaseTraits::DoublePrecision>;
|
using DoublePrecision = iScalar<typename BaseTraits::DoublePrecision>;
|
||||||
using DoublePrecision2= iScalar<typename BaseTraits::DoublePrecision2>;
|
using DoublePrecision2= iScalar<typename BaseTraits::DoublePrecision2>;
|
||||||
|
using SinglePrecision = iScalar<typename BaseTraits::SinglePrecision>;
|
||||||
static constexpr int Rank = BaseTraits::Rank + 1;
|
static constexpr int Rank = BaseTraits::Rank + 1;
|
||||||
static constexpr std::size_t count = BaseTraits::count;
|
static constexpr std::size_t count = BaseTraits::count;
|
||||||
static constexpr int Dimension(int dim) {
|
static constexpr int Dimension(int dim) {
|
||||||
@@ -433,6 +455,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
using Integerified = iVector<typename BaseTraits::Integerified, N>;
|
using Integerified = iVector<typename BaseTraits::Integerified, N>;
|
||||||
using DoublePrecision = iVector<typename BaseTraits::DoublePrecision, N>;
|
using DoublePrecision = iVector<typename BaseTraits::DoublePrecision, N>;
|
||||||
using DoublePrecision2= iVector<typename BaseTraits::DoublePrecision2, N>;
|
using DoublePrecision2= iVector<typename BaseTraits::DoublePrecision2, N>;
|
||||||
|
using SinglePrecision = iVector<typename BaseTraits::SinglePrecision, N>;
|
||||||
static constexpr int Rank = BaseTraits::Rank + 1;
|
static constexpr int Rank = BaseTraits::Rank + 1;
|
||||||
static constexpr std::size_t count = BaseTraits::count * N;
|
static constexpr std::size_t count = BaseTraits::count * N;
|
||||||
static constexpr int Dimension(int dim) {
|
static constexpr int Dimension(int dim) {
|
||||||
@@ -449,6 +472,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
using Integerified = iMatrix<typename BaseTraits::Integerified, N>;
|
using Integerified = iMatrix<typename BaseTraits::Integerified, N>;
|
||||||
using DoublePrecision = iMatrix<typename BaseTraits::DoublePrecision, N>;
|
using DoublePrecision = iMatrix<typename BaseTraits::DoublePrecision, N>;
|
||||||
using DoublePrecision2= iMatrix<typename BaseTraits::DoublePrecision2, N>;
|
using DoublePrecision2= iMatrix<typename BaseTraits::DoublePrecision2, N>;
|
||||||
|
using SinglePrecision = iMatrix<typename BaseTraits::SinglePrecision, N>;
|
||||||
static constexpr int Rank = BaseTraits::Rank + 2;
|
static constexpr int Rank = BaseTraits::Rank + 2;
|
||||||
static constexpr std::size_t count = BaseTraits::count * N * N;
|
static constexpr std::size_t count = BaseTraits::count * N * N;
|
||||||
static constexpr int Dimension(int dim) {
|
static constexpr int Dimension(int dim) {
|
||||||
|
|||||||
@@ -276,14 +276,31 @@ public:
|
|||||||
|
|
||||||
GridBLAS blas;
|
GridBLAS blas;
|
||||||
|
|
||||||
|
// The batched GEMMs of a coarse multigrid level are memory bound, not
|
||||||
|
// flop bound: each call streams the matrices once and does O(1) flops per
|
||||||
|
// element. So report the effective bandwidth beside the flop rate --
|
||||||
|
// measured against the device's peak it says whether a shape is running
|
||||||
|
// well, which the flop rate alone does not. beta=1, so C is read as well
|
||||||
|
// as written.
|
||||||
|
auto Report = [&](int M,int N,int K,int BATCH)
|
||||||
|
{
|
||||||
|
double gf = blas.benchmark<CComplex>(M,N,K,BATCH);
|
||||||
|
double flops = 8.0*M*N*K*BATCH;
|
||||||
|
double bytes = 1.0*sizeof(CComplex)*(1.0*M*K + 1.0*K*N + 2.0*M*N)*BATCH;
|
||||||
|
double gbs = gf*bytes/flops;
|
||||||
|
fprintf(FP,"%d, %d, %d, %d, %f, %f\n", M, N, K, BATCH, gf, gbs);
|
||||||
|
std::cout<<GridLogMessage<<std::setprecision(3)
|
||||||
|
<< M<<"\t\t"<<N<<"\t\t"<<K<<"\t\t"<<BATCH<<"\t\t"<<gf<<"\t\t"<<gbs<<std::endl;
|
||||||
|
};
|
||||||
|
|
||||||
int fpbits = sizeof(CComplex)*4;
|
int fpbits = sizeof(CComplex)*4;
|
||||||
std::cout<<GridLogMessage << "=================================================================================="<<std::endl;
|
std::cout<<GridLogMessage << "=================================================================================="<<std::endl;
|
||||||
std::cout<<GridLogMessage << "= batched GEMM fp"<<fpbits<<std::endl;
|
std::cout<<GridLogMessage << "= batched GEMM fp"<<fpbits<<std::endl;
|
||||||
std::cout<<GridLogMessage << "=================================================================================="<<std::endl;
|
std::cout<<GridLogMessage << "=================================================================================="<<std::endl;
|
||||||
std::cout<<GridLogMessage << " M "<<"\t\t"<<"N"<<"\t\t\t"<<"K"<<"\t\t"<<"Gflop/s / rank (coarse mrhs)"<<std::endl;
|
std::cout<<GridLogMessage << " M "<<"\t\t"<<"N"<<"\t\t\t"<<"K"<<"\t\t"<<"BATCH"<<"\t\t"<<"Gflop/s"<<"\t\t"<<"GB/s / rank (coarse mrhs)"<<std::endl;
|
||||||
std::cout<<GridLogMessage << "----------------------------------------------------------"<<std::endl;
|
std::cout<<GridLogMessage << "----------------------------------------------------------"<<std::endl;
|
||||||
|
|
||||||
fprintf(FP,"GEMM\n\n M, N, K, BATCH, GF/s per rank fp%d\n",fpbits);
|
fprintf(FP,"GEMM\n\n M, N, K, BATCH, GF/s per rank, GB/s per rank fp%d\n",fpbits);
|
||||||
|
|
||||||
for(int b=0;b<3;b++){
|
for(int b=0;b<3;b++){
|
||||||
for(int r=0;r<3;r++){
|
for(int r=0;r<3;r++){
|
||||||
@@ -291,15 +308,10 @@ public:
|
|||||||
int N=rhs[r];
|
int N=rhs[r];
|
||||||
int K=basis[b];
|
int K=basis[b];
|
||||||
int BATCH=vol;
|
int BATCH=vol;
|
||||||
double p=blas.benchmark<CComplex>(M,N,K,BATCH);
|
Report(M,N,K,BATCH);
|
||||||
|
|
||||||
fprintf(FP,"%d, %d, %d, %d, %f\n", M, N, K, BATCH, p);
|
|
||||||
|
|
||||||
std::cout<<GridLogMessage<<std::setprecision(3)
|
|
||||||
<< M<<"\t\t"<<N<<"\t\t"<<K<<"\t\t"<<BATCH<<"\t\t"<<p<<std::endl;
|
|
||||||
}}
|
}}
|
||||||
std::cout<<GridLogMessage << "----------------------------------------------------------"<<std::endl;
|
std::cout<<GridLogMessage << "----------------------------------------------------------"<<std::endl;
|
||||||
std::cout<<GridLogMessage << " M "<<"\t\t"<<"N"<<"\t\t\t"<<"K"<<"\t\t"<<"Gflop/s / rank (block project)"<<std::endl;
|
std::cout<<GridLogMessage << " M "<<"\t\t"<<"N"<<"\t\t\t"<<"K"<<"\t\t"<<"BATCH"<<"\t\t"<<"Gflop/s"<<"\t\t"<<"GB/s / rank (block project)"<<std::endl;
|
||||||
std::cout<<GridLogMessage << "----------------------------------------------------------"<<std::endl;
|
std::cout<<GridLogMessage << "----------------------------------------------------------"<<std::endl;
|
||||||
for(int b=0;b<3;b++){
|
for(int b=0;b<3;b++){
|
||||||
for(int r=0;r<3;r++){
|
for(int r=0;r<3;r++){
|
||||||
@@ -307,14 +319,10 @@ public:
|
|||||||
int N=rhs[r];
|
int N=rhs[r];
|
||||||
int K=blk;
|
int K=blk;
|
||||||
int BATCH=vol;
|
int BATCH=vol;
|
||||||
double p=blas.benchmark<CComplex>(M,N,K,BATCH);
|
Report(M,N,K,BATCH);
|
||||||
|
|
||||||
fprintf(FP,"%d, %d, %d, %d, %f\n", M, N, K, BATCH, p);
|
|
||||||
std::cout<<GridLogMessage<<std::setprecision(3)
|
|
||||||
<< M<<"\t\t"<<N<<"\t\t"<<K<<"\t\t"<<BATCH<<"\t\t"<<p<<std::endl;
|
|
||||||
}}
|
}}
|
||||||
std::cout<<GridLogMessage << "----------------------------------------------------------"<<std::endl;
|
std::cout<<GridLogMessage << "----------------------------------------------------------"<<std::endl;
|
||||||
std::cout<<GridLogMessage << " M "<<"\t\t"<<"N"<<"\t\t\t"<<"K"<<"\t\t"<<"Gflop/s / rank (block promote)"<<std::endl;
|
std::cout<<GridLogMessage << " M "<<"\t\t"<<"N"<<"\t\t\t"<<"K"<<"\t\t"<<"BATCH"<<"\t\t"<<"Gflop/s"<<"\t\t"<<"GB/s / rank (block promote)"<<std::endl;
|
||||||
std::cout<<GridLogMessage << "----------------------------------------------------------"<<std::endl;
|
std::cout<<GridLogMessage << "----------------------------------------------------------"<<std::endl;
|
||||||
for(int b=0;b<3;b++){
|
for(int b=0;b<3;b++){
|
||||||
for(int r=0;r<3;r++){
|
for(int r=0;r<3;r++){
|
||||||
@@ -322,12 +330,54 @@ public:
|
|||||||
int N=blk;
|
int N=blk;
|
||||||
int K=basis[b];
|
int K=basis[b];
|
||||||
int BATCH=vol;
|
int BATCH=vol;
|
||||||
double p=blas.benchmark<CComplex>(M,N,K,BATCH);
|
Report(M,N,K,BATCH);
|
||||||
|
|
||||||
fprintf(FP,"%d, %d, %d, %d, %f\n", M, N, K, BATCH, p);
|
|
||||||
std::cout<<GridLogMessage<<std::setprecision(3)
|
|
||||||
<< M<<"\t\t"<<N<<"\t\t"<<K<<"\t\t"<<BATCH<<"\t\t"<<p<<std::endl;
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// The coarse stencil apply as the multigrid actually issues it, swept in
|
||||||
|
// the batch count. One call per stencil point, M = K = nbasis, N = Nrhs,
|
||||||
|
// batch = the LOCAL coarse volume: at 48^3x96 over 288 GCDs with a
|
||||||
|
// {2,2,3,3} block that is 1024 sites, which is small, and the trace shows
|
||||||
|
// the single-precision kernel taking as long as the double one. The
|
||||||
|
// sweep spans that point and the alternative organisation in which the
|
||||||
|
// stencil legs ride in the batch dimension instead of being separate
|
||||||
|
// calls: 9x and 33x the sites, with the partial results summed after.
|
||||||
|
// Nrhs 1 is the latency-bound case that a single-right-hand-side solve
|
||||||
|
// pays throughout.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
{
|
||||||
|
int nb[] = { 60, 64 };
|
||||||
|
int nrhs_[] = { 1, 12, 24, 32 };
|
||||||
|
// 1024 is the local coarse volume at the production point; the rest are
|
||||||
|
// that times the number of stencil legs folded into the batch. 3 is
|
||||||
|
// the common divisor of the 33-point (PVdagM) and 81-point (HDCG)
|
||||||
|
// stencils, so x3 is the grouping that serves both and leaves only a
|
||||||
|
// three-way linear combination at the end.
|
||||||
|
int batches[] = { 256, 1024, 3072, 4096, 9216, 33792 };
|
||||||
|
std::cout<<GridLogMessage << "----------------------------------------------------------"<<std::endl;
|
||||||
|
std::cout<<GridLogMessage << " M "<<"\t\t"<<"N"<<"\t\t\t"<<"K"<<"\t\t"<<"BATCH"<<"\t\t"<<"Gflop/s"<<"\t\t"<<"GB/s / rank (coarse stencil, production shapes)"<<std::endl;
|
||||||
|
std::cout<<GridLogMessage << "----------------------------------------------------------"<<std::endl;
|
||||||
|
for(int b=0;b<2;b++){
|
||||||
|
for(int r=0;r<4;r++){
|
||||||
|
for(int v=0;v<6;v++){
|
||||||
|
Report(nb[b],nrhs_[r],nb[b],batches[v]);
|
||||||
|
}}}
|
||||||
|
|
||||||
|
// The same work with the stencil sum folded along the CONTRACTION
|
||||||
|
// instead of the batch: one GEMM per site with K = npoint*nbasis. Both
|
||||||
|
// precisions reach their best rates at large K (the block-project
|
||||||
|
// shape, K=256), which this pushes further, at the price of gathering
|
||||||
|
// the neighbour vectors. 33 points is the next-to-nearest stencil,
|
||||||
|
// 81 the full box.
|
||||||
|
std::cout<<GridLogMessage << "----------------------------------------------------------"<<std::endl;
|
||||||
|
std::cout<<GridLogMessage << " M "<<"\t\t"<<"N"<<"\t\t\t"<<"K"<<"\t\t"<<"BATCH"<<"\t\t"<<"Gflop/s"<<"\t\t"<<"GB/s / rank (stencil folded along K)"<<std::endl;
|
||||||
|
std::cout<<GridLogMessage << "----------------------------------------------------------"<<std::endl;
|
||||||
|
for(int b=0;b<2;b++){
|
||||||
|
for(int r=0;r<4;r++){
|
||||||
|
Report(nb[b],nrhs_[r],33*nb[b],1024);
|
||||||
|
Report(nb[b],nrhs_[r],81*nb[b],1024);
|
||||||
|
}}
|
||||||
|
}
|
||||||
fprintf(FP,"\n\n\n");
|
fprintf(FP,"\n\n\n");
|
||||||
std::cout<<GridLogMessage << "=================================================================================="<<std::endl;
|
std::cout<<GridLogMessage << "=================================================================================="<<std::endl;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -192,6 +192,18 @@ case ${ac_TRACING} in
|
|||||||
AC_DEFINE([GRID_TRACING_NONE],[1],[no tracing]);;
|
AC_DEFINE([GRID_TRACING_NONE],[1],[no tracing]);;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
############### distributed dense inverse precision (multigrid coarse-coarse bottom)
|
||||||
|
AC_ARG_ENABLE([dense-inverse-precision],
|
||||||
|
[AS_HELP_STRING([--enable-dense-inverse-precision=double|single],[precision of the distributed dense coarse-coarse inversion (default double); independent of the coarse-space precision])],
|
||||||
|
[ac_DENSE_INVERSE=${enable_dense_inverse_precision}], [ac_DENSE_INVERSE=double])
|
||||||
|
|
||||||
|
case ${ac_DENSE_INVERSE} in
|
||||||
|
single)
|
||||||
|
AC_DEFINE([GRID_DENSE_INVERSE_SINGLE],[1],[fp32 distributed dense inversion]);;
|
||||||
|
*)
|
||||||
|
AC_DEFINE([GRID_DENSE_INVERSE_DOUBLE],[1],[fp64 distributed dense inversion]);;
|
||||||
|
esac
|
||||||
|
|
||||||
############### fermions
|
############### fermions
|
||||||
AC_ARG_ENABLE([fermion-instantiations],
|
AC_ARG_ENABLE([fermion-instantiations],
|
||||||
[AS_HELP_STRING([--enable-fermion-instantiations=yes|no],[enable fermion instantiations])],
|
[AS_HELP_STRING([--enable-fermion-instantiations=yes|no],[enable fermion instantiations])],
|
||||||
|
|||||||
@@ -32,50 +32,7 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace Grid;
|
using namespace Grid;
|
||||||
|
|
||||||
// Routes Op/AdjOp -> HermOp so that CoarsenOperator and CreateSubspace
|
|
||||||
// both see the HPD operator M†M rather than bare M.
|
|
||||||
template<class Field>
|
|
||||||
class HermOpAdaptor : public LinearOperatorBase<Field>
|
|
||||||
{
|
|
||||||
LinearOperatorBase<Field> &wrapped;
|
|
||||||
public:
|
|
||||||
HermOpAdaptor(LinearOperatorBase<Field> &wrapme) : wrapped(wrapme) {};
|
|
||||||
void Op (const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void HermOp (const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void AdjOp (const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void OpDiag (const Field &in, Field &out) { GRID_ASSERT(0); }
|
|
||||||
void OpDir (const Field &in, Field &out,int dir,int disp) { GRID_ASSERT(0); }
|
|
||||||
void OpDirAll(const Field &in, std::vector<Field> &out) { GRID_ASSERT(0); }
|
|
||||||
void HermOpAndNorm(const Field &in, Field &out, RealD &n1, RealD &n2) {
|
|
||||||
wrapped.HermOp(in, out);
|
|
||||||
ComplexD dot = innerProduct(in, out);
|
|
||||||
n1 = real(dot);
|
|
||||||
n2 = norm2(out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fixed-iteration CG smoother: runs exactly `iters` steps of CG on the
|
|
||||||
// shifted operator. tolerance=0 so CG never exits early.
|
|
||||||
template<class Field>
|
|
||||||
class CGSmoother : public LinearFunction<Field>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using LinearFunction<Field>::operator();
|
|
||||||
typedef LinearOperatorBase<Field> FineOperator;
|
|
||||||
FineOperator &_SmootherOperator;
|
|
||||||
int iters;
|
|
||||||
CGSmoother(int _iters, FineOperator &SmootherOperator)
|
|
||||||
: _SmootherOperator(SmootherOperator), iters(_iters)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage << " CGSmoother order " << iters << std::endl;
|
|
||||||
}
|
|
||||||
void operator()(const Field &in, Field &out)
|
|
||||||
{
|
|
||||||
ConjugateGradient<Field> CG(0.0, iters, false);
|
|
||||||
out = Zero();
|
|
||||||
CG(_SmootherOperator, in, out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int main (int argc, char ** argv)
|
int main (int argc, char ** argv)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -57,46 +57,7 @@ Author: Peter Boyle <pboyle@bnl.gov>
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace Grid;
|
using namespace Grid;
|
||||||
|
|
||||||
// Wraps any LinearOperatorBase so that Op = AdjOp = HermOp.
|
|
||||||
// Required when coarsening an HPD operator whose Op != HermOp
|
|
||||||
// (e.g. MdagMLinearOperator where Op=M, HermOp=M†M).
|
|
||||||
template<class Field>
|
|
||||||
class HermOpAdaptor : public LinearOperatorBase<Field>
|
|
||||||
{
|
|
||||||
LinearOperatorBase<Field> &wrapped;
|
|
||||||
public:
|
|
||||||
HermOpAdaptor(LinearOperatorBase<Field> &wrapme) : wrapped(wrapme) {}
|
|
||||||
void Op (const Field &in, Field &out) { wrapped.HermOp(in, out); }
|
|
||||||
void HermOp (const Field &in, Field &out) { wrapped.HermOp(in, out); }
|
|
||||||
void AdjOp (const Field &in, Field &out) { wrapped.HermOp(in, out); }
|
|
||||||
void OpDiag (const Field &in, Field &out) { GRID_ASSERT(0); }
|
|
||||||
void OpDir (const Field &in, Field &out, int dir, int disp) { GRID_ASSERT(0); }
|
|
||||||
void OpDirAll(const Field &in, std::vector<Field> &out) { GRID_ASSERT(0); }
|
|
||||||
void HermOpAndNorm(const Field &in, Field &out, RealD &n1, RealD &n2) {
|
|
||||||
wrapped.HermOp(in, out);
|
|
||||||
ComplexD dot = innerProduct(in, out);
|
|
||||||
n1 = real(dot); n2 = norm2(out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fixed-iteration CG as a smoother (LinearFunction).
|
|
||||||
// Used as the IR-shifted smoother: solves (M†M + lo*I) x = b approximately.
|
|
||||||
template<class Field>
|
|
||||||
class CGSmoother : public LinearFunction<Field>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using LinearFunction<Field>::operator();
|
|
||||||
LinearOperatorBase<Field> &_op;
|
|
||||||
int iters;
|
|
||||||
CGSmoother(int _iters, LinearOperatorBase<Field> &op) : _op(op), iters(_iters) {
|
|
||||||
std::cout << GridLogMessage << "CGSmoother order " << iters << std::endl;
|
|
||||||
}
|
|
||||||
void operator()(const Field &in, Field &out) {
|
|
||||||
ConjugateGradient<Field> CG(0.0, iters, false);
|
|
||||||
out = Zero();
|
|
||||||
CG(_op, in, out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Two-level V-cycle preconditioner (LinearFunction).
|
// Two-level V-cycle preconditioner (LinearFunction).
|
||||||
template<class Fobj, class CComplex, int nbasis>
|
template<class Fobj, class CComplex, int nbasis>
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
#include <Grid/lattice/PaddedCell.h>
|
#include <Grid/lattice/PaddedCell.h>
|
||||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
#include <Grid/stencil/GeneralLocalStencil.h>
|
||||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
||||||
|
#include <Grid/algorithms/multigrid/MrhsMultiGrid.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace Grid;
|
using namespace Grid;
|
||||||
@@ -183,194 +184,6 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// Minimal multi-RHS function interface (preconditioner slot)
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class Field>
|
|
||||||
class MrhsLinearFunction {
|
|
||||||
public:
|
|
||||||
virtual void operator()(std::vector<Field> &in, std::vector<Field> &out) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// Single-polynomial multi-RHS PGCR (non-Hermitian).
|
|
||||||
//
|
|
||||||
// Verbatim adaptation of PrecGeneralisedConjugateResidualNonHermitian
|
|
||||||
// to std::vector<Field>: every innerProduct / norm2 is SUMMED over the
|
|
||||||
// RHS index, so one alpha/beta per step is shared by all RHS -- the
|
|
||||||
// single GCR on the enlarged block-diagonal system.
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class Field>
|
|
||||||
class MrhsPGCRNonHermitian {
|
|
||||||
public:
|
|
||||||
RealD Tolerance;
|
|
||||||
Integer MaxIterations;
|
|
||||||
int mmax;
|
|
||||||
int nstep;
|
|
||||||
int steps;
|
|
||||||
int level;
|
|
||||||
int ZeroGuess = 0; int FirstCycle = 0; // caller contract: zero guess => first-cycle r0 = src
|
|
||||||
std::string name = "Level 1";
|
|
||||||
LinearOperatorBase<Field> &Linop;
|
|
||||||
MrhsLinearFunction<Field> &Preconditioner;
|
|
||||||
|
|
||||||
void Level(int lv) { name = "Level " + std::to_string(lv); level=lv; };
|
|
||||||
void Name(std::string n) { name = n; };
|
|
||||||
void SetZeroGuess(int z) { ZeroGuess=z; };
|
|
||||||
|
|
||||||
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; }
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////
|
|
||||||
// vector-of-fields linear algebra, reductions summed over rhs
|
|
||||||
///////////////////////////////////////////////////////////////
|
|
||||||
static RealD vnorm2(std::vector<Field> &x){
|
|
||||||
RealD s=0.0; for(auto &f : x) s+=norm2(f); return s;
|
|
||||||
}
|
|
||||||
static ComplexD vinnerProduct(std::vector<Field> &x, std::vector<Field> &y){
|
|
||||||
ComplexD s(0.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){
|
|
||||||
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){
|
|
||||||
RealD cp, ssq, rsq;
|
|
||||||
int nrhs = src.size();
|
|
||||||
GridBase *grid = src[0].Grid();
|
|
||||||
|
|
||||||
ssq=vnorm2(src);
|
|
||||||
rsq=Tolerance*Tolerance*ssq;
|
|
||||||
|
|
||||||
std::vector<Field> r(nrhs,grid);
|
|
||||||
|
|
||||||
GridStopWatch SolverTimer;
|
|
||||||
SolverTimer.Start();
|
|
||||||
|
|
||||||
steps=0;
|
|
||||||
FirstCycle=1;
|
|
||||||
for(int k=0;k<MaxIterations;k++){
|
|
||||||
|
|
||||||
cp=GCRnStep(src,psi,rsq);
|
|
||||||
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name
|
|
||||||
<<" MrhsPGCR("<<mmax<<","<<nstep<<") "<<steps<<" steps cp = "<<cp<<" target "<<rsq<<std::endl;
|
|
||||||
|
|
||||||
if(cp<rsq){
|
|
||||||
SolverTimer.Stop();
|
|
||||||
vOp(psi,r);
|
|
||||||
for(int rr=0;rr<nrhs;rr++) axpy(r[rr],-1.0,src[rr],r[rr]);
|
|
||||||
RealD tr=vnorm2(r);
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name
|
|
||||||
<<" MrhsPGCR: Converged on iteration "<<steps
|
|
||||||
<<" computed residual "<<std::sqrt(cp/ssq)
|
|
||||||
<<" true residual "<<std::sqrt(tr/ssq)
|
|
||||||
<<" target "<<Tolerance<<std::endl;
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name
|
|
||||||
<<" MrhsPGCR Time elapsed: Total "<<SolverTimer.Elapsed()<<std::endl;
|
|
||||||
// Per-RHS true residuals: the honest metric under the summed norm
|
|
||||||
for(int rr=0;rr<nrhs;rr++){
|
|
||||||
RealD rn = std::sqrt(norm2(r[rr])/norm2(src[rr]));
|
|
||||||
std::cout<<GridLogMessage<<"MrhsPGCR per-rhs true residual["<<rr<<"] = "<<rn<<std::endl;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::cout<<GridLogMessage<<"MrhsPGCR: did not converge"<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
RealD GCRnStep(std::vector<Field> &src, std::vector<Field> &psi, RealD rsq){
|
|
||||||
|
|
||||||
RealD cp;
|
|
||||||
ComplexD a, b, rq;
|
|
||||||
RealD zAAz;
|
|
||||||
|
|
||||||
int nrhs = src.size();
|
|
||||||
GridBase *grid = src[0].Grid();
|
|
||||||
|
|
||||||
std::vector<Field> r (nrhs,grid);
|
|
||||||
std::vector<Field> z (nrhs,grid);
|
|
||||||
std::vector<Field> Az(nrhs,grid);
|
|
||||||
|
|
||||||
////////////////////////////////
|
|
||||||
// history for flexible orthog: [mmax][nrhs]
|
|
||||||
////////////////////////////////
|
|
||||||
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<RealD> qq(mmax);
|
|
||||||
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR nStep("<<nstep<<")"<<std::endl;
|
|
||||||
|
|
||||||
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];
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name
|
|
||||||
<<" MrhsPGCR true residual r = src - A psi "<<vnorm2(r)<<std::endl;
|
|
||||||
}
|
|
||||||
FirstCycle=0;
|
|
||||||
|
|
||||||
Preconditioner(r,z);
|
|
||||||
vOp(z,Az);
|
|
||||||
zAAz=vnorm2(Az);
|
|
||||||
|
|
||||||
p[0]=z;
|
|
||||||
q[0]=Az;
|
|
||||||
qq[0]=zAAz;
|
|
||||||
|
|
||||||
cp=vnorm2(r);
|
|
||||||
|
|
||||||
for(int k=0;k<nstep;k++){
|
|
||||||
|
|
||||||
steps++;
|
|
||||||
|
|
||||||
int kp = k+1;
|
|
||||||
int peri_k = k %mmax;
|
|
||||||
int peri_kp= kp%mmax;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
if((k==nstep-1)||(cp<rsq)){
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
|
|
||||||
Preconditioner(r,z);
|
|
||||||
vOp(z,Az);
|
|
||||||
zAAz=vnorm2(Az);
|
|
||||||
|
|
||||||
q[peri_kp]=Az;
|
|
||||||
p[peri_kp]=z;
|
|
||||||
|
|
||||||
int northog = ((kp)>(mmax-1))?(mmax-1):(kp);
|
|
||||||
for(int back=0;back<northog;back++){
|
|
||||||
int peri_back=(k-back)%mmax; GRID_ASSERT((k-back)>=0);
|
|
||||||
b = -real(vinnerProduct(q[peri_back],Az))/qq[peri_back];
|
|
||||||
vaxpy(p[peri_kp],b,p[peri_back],p[peri_kp]);
|
|
||||||
vaxpy(q[peri_kp],b,q[peri_back],q[peri_kp]);
|
|
||||||
}
|
|
||||||
qq[peri_kp]=vnorm2(q[peri_kp]);
|
|
||||||
}
|
|
||||||
GRID_ASSERT(0); // never reached
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Trivial multi-RHS preconditioner
|
// Trivial multi-RHS preconditioner
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
@@ -382,98 +195,6 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// MultiRHS two-level V-cycle.
|
|
||||||
//
|
|
||||||
// Mirrors MGPreconditioner in Example_pvdagm:
|
|
||||||
// out = in (trivial pre) [per rhs]
|
|
||||||
// r1 = in - A out [per rhs]
|
|
||||||
// batched blockProject -> pack -> ONE mrhs coarse PGCR -> unpack
|
|
||||||
// -> batched blockPromote; out += correction
|
|
||||||
// r2 = in - A out [per rhs]
|
|
||||||
// per-RHS fine post-smoother; out += smooth(r2)
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class FineField, class MrhsCoarseVector, class FineSmoother>
|
|
||||||
class MrhsTwoLevelMG : public MrhsLinearFunction<FineField> {
|
|
||||||
public:
|
|
||||||
typedef MrhsCoarseVector CoarseVector; // same lattice type on Coarse5d and CoarseMrhs
|
|
||||||
|
|
||||||
LinearOperatorBase<FineField> &_FineOperator;
|
|
||||||
FineSmoother &_PostSmoother; // single-RHS smoother, looped
|
|
||||||
MultiRHSBlockProject<FineField> &_Projector;
|
|
||||||
LinearFunction<CoarseVector> &_CoarseSolve; // PGCR on the 6D mrhs field
|
|
||||||
GridBase *_CoarseGrid; // Coarse5d (single rhs)
|
|
||||||
GridBase *_CoarseGridMrhs; // 6D
|
|
||||||
|
|
||||||
MrhsTwoLevelMG(LinearOperatorBase<FineField> &FineOp,
|
|
||||||
FineSmoother &Post,
|
|
||||||
MultiRHSBlockProject<FineField> &Projector,
|
|
||||||
LinearFunction<CoarseVector> &CoarseSolve,
|
|
||||||
GridBase *CoarseGrid, GridBase *CoarseGridMrhs)
|
|
||||||
: _FineOperator(FineOp), _PostSmoother(Post), _Projector(Projector),
|
|
||||||
_CoarseSolve(CoarseSolve), _CoarseGrid(CoarseGrid), _CoarseGridMrhs(CoarseGridMrhs) {}
|
|
||||||
|
|
||||||
virtual void operator()(std::vector<FineField> &in, std::vector<FineField> &out){
|
|
||||||
int nrhs = in.size();
|
|
||||||
GridBase *fgrid = in[0].Grid();
|
|
||||||
double t;
|
|
||||||
|
|
||||||
std::vector<FineField> vec1(nrhs,fgrid);
|
|
||||||
std::vector<FineField> vec2(nrhs,fgrid);
|
|
||||||
|
|
||||||
// Trivial pre-smoother: out = in (as in Example_pvdagm with simple_fine)
|
|
||||||
for(int r=0;r<nrhs;r++) out[r]=in[r];
|
|
||||||
|
|
||||||
// Residual
|
|
||||||
for(int r=0;r<nrhs;r++){
|
|
||||||
_FineOperator.Op(out[r],vec1[r]);
|
|
||||||
sub(vec1[r],in[r],vec1[r]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Batched fine->coarse, pack rhs into 6D field
|
|
||||||
std::vector<CoarseVector> Csrc_split(nrhs,_CoarseGrid);
|
|
||||||
std::vector<CoarseVector> Csol_split(nrhs,_CoarseGrid);
|
|
||||||
CoarseVector CsrcMrhs(_CoarseGridMrhs);
|
|
||||||
CoarseVector CsolMrhs(_CoarseGridMrhs);
|
|
||||||
|
|
||||||
t=-usecond();
|
|
||||||
_Projector.blockProject(vec1,Csrc_split);
|
|
||||||
for(int r=0;r<nrhs;r++) InsertSliceFast(Csrc_split[r],CsrcMrhs,r,0);
|
|
||||||
t+=usecond();
|
|
||||||
std::cout<<GridLogMessage<<"Mrhs project+pack took "<<t/1000.0<<"ms"<<std::endl;
|
|
||||||
|
|
||||||
// ONE coarse solve for all rhs (GEMM coarse mults)
|
|
||||||
t=-usecond();
|
|
||||||
CsolMrhs=Zero();
|
|
||||||
_CoarseSolve(CsrcMrhs,CsolMrhs);
|
|
||||||
t+=usecond();
|
|
||||||
std::cout<<GridLogMessage<<"Mrhs coarse solve took "<<t/1000.0<<"ms ("<<t/1000.0/nrhs<<"ms/rhs)"<<std::endl;
|
|
||||||
|
|
||||||
// Unpack, batched coarse->fine, add correction
|
|
||||||
t=-usecond();
|
|
||||||
for(int r=0;r<nrhs;r++) ExtractSliceFast(Csol_split[r],CsolMrhs,r,0);
|
|
||||||
_Projector.blockPromote(vec1,Csol_split);
|
|
||||||
for(int r=0;r<nrhs;r++) add(out[r],out[r],vec1[r]);
|
|
||||||
t+=usecond();
|
|
||||||
std::cout<<GridLogMessage<<"Mrhs unpack+promote took "<<t/1000.0<<"ms"<<std::endl;
|
|
||||||
|
|
||||||
// Residual
|
|
||||||
for(int r=0;r<nrhs;r++){
|
|
||||||
_FineOperator.Op(out[r],vec1[r]);
|
|
||||||
sub(vec1[r],in[r],vec1[r]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Per-RHS post-smoother (fine level has no batching win; memory-light)
|
|
||||||
t=-usecond();
|
|
||||||
for(int r=0;r<nrhs;r++){
|
|
||||||
vec2[r]=Zero();
|
|
||||||
_PostSmoother(vec1[r],vec2[r]);
|
|
||||||
add(out[r],out[r],vec2[r]);
|
|
||||||
}
|
|
||||||
t+=usecond();
|
|
||||||
std::cout<<GridLogMessage<<"Mrhs post-smooth took "<<t/1000.0<<"ms ("<<t/1000.0/nrhs<<"ms/rhs)"<<std::endl;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int main (int argc, char ** argv)
|
int main (int argc, char ** argv)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
#include <Grid/lattice/PaddedCell.h>
|
#include <Grid/lattice/PaddedCell.h>
|
||||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
#include <Grid/stencil/GeneralLocalStencil.h>
|
||||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
||||||
|
#include <Grid/algorithms/multigrid/MrhsMultiGrid.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace Grid;
|
using namespace Grid;
|
||||||
@@ -167,206 +168,6 @@ public:
|
|||||||
void HermOp (const Field &in, Field &out) { Field tmp(in.Grid()); Op(in,tmp); AdjOp(tmp,out); }
|
void HermOp (const Field &in, Field &out) { Field tmp(in.Grid()); Op(in,tmp); AdjOp(tmp,out); }
|
||||||
};
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// mrhs interfaces + single-polynomial mrhs PGCR (verbatim from Example_pvdagm_mrhs.cc):
|
|
||||||
// reductions summed over rhs -> one alpha/beta per step for the enlarged system.
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class Field>
|
|
||||||
class MrhsLinearFunction {
|
|
||||||
public:
|
|
||||||
virtual void operator()(std::vector<Field> &in, std::vector<Field> &out) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Field>
|
|
||||||
class MrhsPGCRNonHermitian {
|
|
||||||
public:
|
|
||||||
RealD Tolerance; Integer MaxIterations; int mmax,nstep,steps,level;
|
|
||||||
int ZeroGuess = 0; int FirstCycle = 0; // caller contract: zero guess => first-cycle r0 = src
|
|
||||||
std::string name = "Level 1";
|
|
||||||
LinearOperatorBase<Field> &Linop;
|
|
||||||
MrhsLinearFunction<Field> &Preconditioner;
|
|
||||||
void Level(int lv){ name = "Level " + std::to_string(lv); level=lv; }
|
|
||||||
void Name(std::string n){ name = n; }
|
|
||||||
void SetZeroGuess(int z){ ZeroGuess=z; }
|
|
||||||
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; }
|
|
||||||
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){ 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){
|
|
||||||
RealD cp,ssq,rsq; int nrhs=src.size(); GridBase *grid=src[0].Grid();
|
|
||||||
ssq=vnorm2(src); rsq=Tolerance*Tolerance*ssq;
|
|
||||||
std::vector<Field> r(nrhs,grid);
|
|
||||||
GridStopWatch T; T.Start(); steps=0; FirstCycle=1;
|
|
||||||
for(int k=0;k<MaxIterations;k++){
|
|
||||||
cp=GCRnStep(src,psi,rsq);
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR("<<mmax<<","<<nstep<<") "<<steps<<" steps cp = "<<cp<<" target "<<rsq<<std::endl;
|
|
||||||
if(cp<rsq){
|
|
||||||
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);
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR: Converged on iteration "<<steps
|
|
||||||
<<" computed residual "<<std::sqrt(cp/ssq)<<" true residual "<<std::sqrt(tr/ssq)<<" target "<<Tolerance<<std::endl;
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR Time elapsed: Total "<<T.Elapsed()<<std::endl;
|
|
||||||
for(int rr=0;rr<nrhs;rr++){ RealD rn=std::sqrt(norm2(r[rr])/norm2(src[rr])); std::cout<<GridLogMessage<<"MrhsPGCR per-rhs true residual["<<rr<<"] = "<<rn<<std::endl; }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::cout<<GridLogMessage<<"MrhsPGCR: did not converge"<<std::endl;
|
|
||||||
}
|
|
||||||
RealD GCRnStep(std::vector<Field> &src,std::vector<Field> &psi,RealD rsq){
|
|
||||||
RealD cp; ComplexD a,b,rq; RealD zAAz; int nrhs=src.size(); GridBase *grid=src[0].Grid();
|
|
||||||
std::vector<Field> r(nrhs,grid),z(nrhs,grid),Az(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<RealD> qq(mmax);
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR nStep("<<nstep<<")"<<std::endl;
|
|
||||||
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;
|
|
||||||
Preconditioner(r,z); vOp(z,Az); zAAz=vnorm2(Az);
|
|
||||||
p[0]=z; q[0]=Az; qq[0]=zAAz; cp=vnorm2(r);
|
|
||||||
for(int k=0;k<nstep;k++){
|
|
||||||
steps++; int kp=k+1, peri_k=k%mmax, peri_kp=kp%mmax;
|
|
||||||
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;
|
|
||||||
if((k==nstep-1)||(cp<rsq)) return cp;
|
|
||||||
Preconditioner(r,z); vOp(z,Az); zAAz=vnorm2(Az);
|
|
||||||
q[peri_kp]=Az; p[peri_kp]=z;
|
|
||||||
int northog=((kp)>(mmax-1))?(mmax-1):(kp);
|
|
||||||
for(int back=0;back<northog;back++){ int peri_back=(k-back)%mmax; GRID_ASSERT((k-back)>=0);
|
|
||||||
b=-real(vinnerProduct(q[peri_back],Az))/qq[peri_back];
|
|
||||||
vaxpy(p[peri_kp],b,p[peri_back],p[peri_kp]); vaxpy(q[peri_kp],b,q[peri_back],q[peri_kp]); }
|
|
||||||
qq[peri_kp]=vnorm2(q[peri_kp]);
|
|
||||||
}
|
|
||||||
GRID_ASSERT(0); return cp;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// L2->L3 mrhs V-cycle: a LinearFunction on the 6D mrhs COARSE field.
|
|
||||||
// Mirrors Example_pvdagm_mrhs.cc's MrhsTwoLevelMG one level down, and the
|
|
||||||
// single-RHS MGPreconditioner of Example_pvdagm_3level_SVDdefl.cc:
|
|
||||||
// out = in (trivial pre)
|
|
||||||
// r = in - A_coarse out
|
|
||||||
// restrict (unpack 6D coarse -> blockProject -> pack 6D coarse-coarse)
|
|
||||||
// ONE coarse-coarse solve (L3, GEMM)
|
|
||||||
// prolong (unpack -> blockPromote -> pack); out += correction
|
|
||||||
// r = in - A_coarse out
|
|
||||||
// coarse smoother (shifted 6D coarse op); out += smooth(r)
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class CoarseField, class CoarseCoarseField>
|
|
||||||
class MrhsCoarseThreeLevelPrec : public LinearFunction<CoarseField> {
|
|
||||||
public:
|
|
||||||
LinearOperatorBase<CoarseField> &_CoarseOp; // mrhs coarse op (6D)
|
|
||||||
LinearFunction<CoarseField> &_CoarseSmoother; // shifted 6D coarse smoother
|
|
||||||
MultiRHSBlockProject<CoarseField> &_Projector; // L2->L3 (vector-based)
|
|
||||||
LinearFunction<CoarseCoarseField> &_CoarseCoarseSolve; // L3 solve (6D cc)
|
|
||||||
GridBase *_Coarse5d, *_CoarseCoarse5d, *_CoarseCoarseMrhs;
|
|
||||||
int _nrhs;
|
|
||||||
|
|
||||||
MrhsCoarseThreeLevelPrec(LinearOperatorBase<CoarseField> &CoarseOp,
|
|
||||||
LinearFunction<CoarseField> &CoarseSmoother,
|
|
||||||
MultiRHSBlockProject<CoarseField> &Projector,
|
|
||||||
LinearFunction<CoarseCoarseField> &CoarseCoarseSolve,
|
|
||||||
GridBase *Coarse5d, GridBase *CoarseCoarse5d, GridBase *CoarseCoarseMrhs, int nrhs)
|
|
||||||
: _CoarseOp(CoarseOp), _CoarseSmoother(CoarseSmoother), _Projector(Projector),
|
|
||||||
_CoarseCoarseSolve(CoarseCoarseSolve),
|
|
||||||
_Coarse5d(Coarse5d), _CoarseCoarse5d(CoarseCoarse5d), _CoarseCoarseMrhs(CoarseCoarseMrhs), _nrhs(nrhs) {}
|
|
||||||
|
|
||||||
using LinearFunction<CoarseField>::operator();
|
|
||||||
virtual void operator()(const CoarseField &in, CoarseField &out) {
|
|
||||||
int nrhs=_nrhs; double t;
|
|
||||||
CoarseField vec1(in.Grid());
|
|
||||||
CoarseField vec2(in.Grid());
|
|
||||||
|
|
||||||
// trivial pre-smoother
|
|
||||||
out = in;
|
|
||||||
|
|
||||||
// residual (6D coarse)
|
|
||||||
_CoarseOp.Op(out,vec1); sub(vec1,in,vec1);
|
|
||||||
|
|
||||||
// restrict: unpack 6D coarse -> vector<Coarse> -> blockProject -> vector<CoarseCoarse> -> pack 6D cc
|
|
||||||
std::vector<CoarseField> csplit(nrhs,_Coarse5d);
|
|
||||||
std::vector<CoarseCoarseField> ccsplit(nrhs,_CoarseCoarse5d);
|
|
||||||
CoarseCoarseField CCsrc(_CoarseCoarseMrhs);
|
|
||||||
CoarseCoarseField CCsol(_CoarseCoarseMrhs);
|
|
||||||
|
|
||||||
t=-usecond();
|
|
||||||
for(int r=0;r<nrhs;r++) ExtractSliceFast(csplit[r],vec1,r,0);
|
|
||||||
_Projector.blockProject(csplit,ccsplit);
|
|
||||||
for(int r=0;r<nrhs;r++) InsertSliceFast(ccsplit[r],CCsrc,r,0);
|
|
||||||
t+=usecond();
|
|
||||||
std::cout<<GridLogMessage<<"L2->L3 restrict took "<<t/1000.0<<"ms"<<std::endl;
|
|
||||||
|
|
||||||
// L3 solve (6D coarse-coarse, GEMM)
|
|
||||||
t=-usecond();
|
|
||||||
CCsol=Zero();
|
|
||||||
_CoarseCoarseSolve(CCsrc,CCsol);
|
|
||||||
t+=usecond();
|
|
||||||
std::cout<<GridLogMessage<<"L3 coarse-coarse solve took "<<t/1000.0<<"ms ("<<t/1000.0/nrhs<<"ms/rhs)"<<std::endl;
|
|
||||||
|
|
||||||
// prolong: unpack 6D cc -> blockPromote -> pack 6D coarse; add correction
|
|
||||||
t=-usecond();
|
|
||||||
for(int r=0;r<nrhs;r++) ExtractSliceFast(ccsplit[r],CCsol,r,0);
|
|
||||||
_Projector.blockPromote(csplit,ccsplit);
|
|
||||||
for(int r=0;r<nrhs;r++) InsertSliceFast(csplit[r],vec1,r,0);
|
|
||||||
add(out,out,vec1);
|
|
||||||
t+=usecond();
|
|
||||||
std::cout<<GridLogMessage<<"L2->L3 prolong took "<<t/1000.0<<"ms"<<std::endl;
|
|
||||||
|
|
||||||
// residual + coarse smoother (6D coarse)
|
|
||||||
_CoarseOp.Op(out,vec1); sub(vec1,in,vec1);
|
|
||||||
vec2=Zero();
|
|
||||||
_CoarseSmoother(vec1,vec2);
|
|
||||||
add(out,out,vec2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// L1->L2 mrhs V-cycle (verbatim from Example_pvdagm_mrhs.cc):
|
|
||||||
// per-rhs fine smoother + batched restriction + ONE coarse solve + batched prolong.
|
|
||||||
// The coarse solve passed in is now itself three-level (preconditioned by L2->L3).
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class FineField, class MrhsCoarseVector, class FineSmoother>
|
|
||||||
class MrhsTwoLevelMG : public MrhsLinearFunction<FineField> {
|
|
||||||
public:
|
|
||||||
typedef MrhsCoarseVector CoarseVector;
|
|
||||||
LinearOperatorBase<FineField> &_FineOperator;
|
|
||||||
FineSmoother &_PostSmoother;
|
|
||||||
MultiRHSBlockProject<FineField> &_Projector;
|
|
||||||
LinearFunction<CoarseVector> &_CoarseSolve;
|
|
||||||
GridBase *_CoarseGrid, *_CoarseGridMrhs;
|
|
||||||
MrhsTwoLevelMG(LinearOperatorBase<FineField> &FineOp, FineSmoother &Post,
|
|
||||||
MultiRHSBlockProject<FineField> &Projector, LinearFunction<CoarseVector> &CoarseSolve,
|
|
||||||
GridBase *CoarseGrid, GridBase *CoarseGridMrhs)
|
|
||||||
: _FineOperator(FineOp),_PostSmoother(Post),_Projector(Projector),_CoarseSolve(CoarseSolve),
|
|
||||||
_CoarseGrid(CoarseGrid),_CoarseGridMrhs(CoarseGridMrhs){}
|
|
||||||
virtual void operator()(std::vector<FineField> &in, std::vector<FineField> &out){
|
|
||||||
int nrhs=in.size(); GridBase *fgrid=in[0].Grid(); double t;
|
|
||||||
std::vector<FineField> vec1(nrhs,fgrid),vec2(nrhs,fgrid);
|
|
||||||
for(int r=0;r<nrhs;r++) out[r]=in[r];
|
|
||||||
for(int r=0;r<nrhs;r++){ _FineOperator.Op(out[r],vec1[r]); sub(vec1[r],in[r],vec1[r]); }
|
|
||||||
std::vector<CoarseVector> Csrc_split(nrhs,_CoarseGrid), Csol_split(nrhs,_CoarseGrid);
|
|
||||||
CoarseVector CsrcMrhs(_CoarseGridMrhs), CsolMrhs(_CoarseGridMrhs);
|
|
||||||
t=-usecond();
|
|
||||||
_Projector.blockProject(vec1,Csrc_split);
|
|
||||||
for(int r=0;r<nrhs;r++) InsertSliceFast(Csrc_split[r],CsrcMrhs,r,0);
|
|
||||||
t+=usecond(); std::cout<<GridLogMessage<<"Mrhs project+pack took "<<t/1000.0<<"ms"<<std::endl;
|
|
||||||
t=-usecond(); CsolMrhs=Zero(); _CoarseSolve(CsrcMrhs,CsolMrhs); t+=usecond();
|
|
||||||
std::cout<<GridLogMessage<<"Mrhs coarse solve took "<<t/1000.0<<"ms ("<<t/1000.0/nrhs<<"ms/rhs)"<<std::endl;
|
|
||||||
t=-usecond();
|
|
||||||
for(int r=0;r<nrhs;r++) ExtractSliceFast(Csol_split[r],CsolMrhs,r,0);
|
|
||||||
_Projector.blockPromote(vec1,Csol_split);
|
|
||||||
for(int r=0;r<nrhs;r++) add(out[r],out[r],vec1[r]);
|
|
||||||
t+=usecond(); std::cout<<GridLogMessage<<"Mrhs unpack+promote took "<<t/1000.0<<"ms"<<std::endl;
|
|
||||||
for(int r=0;r<nrhs;r++){ _FineOperator.Op(out[r],vec1[r]); sub(vec1[r],in[r],vec1[r]); }
|
|
||||||
t=-usecond();
|
|
||||||
for(int r=0;r<nrhs;r++){ vec2[r]=Zero(); _PostSmoother(vec1[r],vec2[r]); add(out[r],out[r],vec2[r]); }
|
|
||||||
t+=usecond(); std::cout<<GridLogMessage<<"Mrhs post-smooth took "<<t/1000.0<<"ms ("<<t/1000.0/nrhs<<"ms/rhs)"<<std::endl;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int main (int argc, char ** argv)
|
int main (int argc, char ** argv)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ Author: Peter Boyle <pboyle@bnl.gov>
|
|||||||
#include <Grid/lattice/PaddedCell.h>
|
#include <Grid/lattice/PaddedCell.h>
|
||||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
#include <Grid/stencil/GeneralLocalStencil.h>
|
||||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
||||||
|
#include <Grid/algorithms/multigrid/MrhsMultiGrid.h>
|
||||||
#include <Grid/algorithms/multigrid/DenseCoarseMatrix.h>
|
#include <Grid/algorithms/multigrid/DenseCoarseMatrix.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -173,281 +174,6 @@ public:
|
|||||||
void HermOp (const Field &in, Field &out) { Field tmp(in.Grid()); Op(in,tmp); AdjOp(tmp,out); }
|
void HermOp (const Field &in, Field &out) { Field tmp(in.Grid()); Op(in,tmp); AdjOp(tmp,out); }
|
||||||
};
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// Dense CC solve on the PACKED 6D mrhs coarse-coarse field: drop-in
|
|
||||||
// for the L3 PGCR, delegating to the library DenseCoarseMatrix.
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class DenseType, class CoarseCoarseField>
|
|
||||||
class MrhsDenseCCSolve : public LinearFunction<CoarseCoarseField> {
|
|
||||||
public:
|
|
||||||
DenseType &_Dense;
|
|
||||||
GridBase *_CoarseCoarse5d;
|
|
||||||
int _nrhs;
|
|
||||||
MrhsDenseCCSolve(DenseType &D, GridBase *cc5d, int nrhs)
|
|
||||||
: _Dense(D), _CoarseCoarse5d(cc5d), _nrhs(nrhs) {}
|
|
||||||
using LinearFunction<CoarseCoarseField>::operator();
|
|
||||||
virtual void operator()(const CoarseCoarseField &in, CoarseCoarseField &out){
|
|
||||||
if ( getenv("DENSE_CC_CHECK") ) {
|
|
||||||
// Audit path: per-rhs 5D unpack so ApplyBatch can run the _Op defect
|
|
||||||
// check per rhs. ~50ms/call of slice/split overhead -- audit only.
|
|
||||||
CoarseCoarseField tmp(in.Grid());
|
|
||||||
tmp = in;
|
|
||||||
std::vector<CoarseCoarseField> split_in (_nrhs,_CoarseCoarse5d);
|
|
||||||
std::vector<CoarseCoarseField> split_out(_nrhs,_CoarseCoarse5d);
|
|
||||||
for(int r=0;r<_nrhs;r++) ExtractSliceFast(split_in[r], tmp, r, 0);
|
|
||||||
_Dense.ApplyBatch(split_in, split_out);
|
|
||||||
for(int r=0;r<_nrhs;r++) InsertSliceFast(split_out[r], out, r, 0);
|
|
||||||
} else {
|
|
||||||
GRID_TRACE("MrhsDensCCSolve::ApplyBatch6D");
|
|
||||||
_Dense.ApplyBatch6D(in, out, _nrhs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// mrhs interfaces + single-polynomial mrhs PGCR (verbatim from the
|
|
||||||
// frozen Example_pvdagm_mrhs_3level_dense.cc)
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class Field>
|
|
||||||
class MrhsLinearFunction {
|
|
||||||
public:
|
|
||||||
virtual void operator()(std::vector<Field> &in, std::vector<Field> &out) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Field>
|
|
||||||
class MrhsPGCRNonHermitian {
|
|
||||||
public:
|
|
||||||
RealD Tolerance; Integer MaxIterations; int mmax,nstep,steps,level;
|
|
||||||
int ZeroGuess = 0; int FirstCycle = 0; // caller contract: zero guess => first-cycle r0 = src
|
|
||||||
std::string name = "Level 1";
|
|
||||||
LinearOperatorBase<Field> &Linop;
|
|
||||||
MrhsLinearFunction<Field> &Preconditioner;
|
|
||||||
void Level(int lv){ name = "Level " + std::to_string(lv); level=lv; }
|
|
||||||
void Name(std::string n){ name = n; }
|
|
||||||
void SetZeroGuess(int z){ ZeroGuess=z; }
|
|
||||||
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; }
|
|
||||||
static RealD vnorm2(std::vector<Field> &x){ GRID_TRACE("GCR-vnorm2"); RealD s=0; for(auto &f:x) s+=norm2(f); return s; }
|
|
||||||
static ComplexD vinnerProduct(std::vector<Field> &x,std::vector<Field> &y){ GRID_TRACE("GCR-vinnerProduct"); 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){ GRID_TRACE("GCR-vaxpy"); 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("GCR-vOp"); 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){
|
|
||||||
GRID_TRACE((name+"MrhsPGCRNonHermitian").c_str());
|
|
||||||
RealD cp,ssq,rsq; int nrhs=src.size(); GridBase *grid=src[0].Grid();
|
|
||||||
ssq=vnorm2(src); rsq=Tolerance*Tolerance*ssq;
|
|
||||||
std::vector<Field> r(nrhs,grid);
|
|
||||||
GridStopWatch T; T.Start(); steps=0; FirstCycle=1;
|
|
||||||
for(int k=0;k<MaxIterations;k++){
|
|
||||||
cp=GCRnStep(src,psi,rsq);
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR("<<mmax<<","<<nstep<<") "<<steps<<" steps cp = "<<cp<<" target "<<rsq<<std::endl;
|
|
||||||
if(cp<rsq){
|
|
||||||
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);
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR: Converged on iteration "<<steps
|
|
||||||
<<" computed residual "<<std::sqrt(cp/ssq)<<" true residual "<<std::sqrt(tr/ssq)<<" target "<<Tolerance<<std::endl;
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR Time elapsed: Total "<<T.Elapsed()<<std::endl;
|
|
||||||
for(int rr=0;rr<nrhs;rr++){ RealD rn=std::sqrt(norm2(r[rr])/norm2(src[rr])); std::cout<<GridLogMessage<<"MrhsPGCR per-rhs true residual["<<rr<<"] = "<<rn<<std::endl; }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::cout<<GridLogMessage<<"MrhsPGCR: did not converge"<<std::endl;
|
|
||||||
}
|
|
||||||
RealD GCRnStep(std::vector<Field> &src,std::vector<Field> &psi,RealD rsq){
|
|
||||||
RealD cp; ComplexD a,b,rq; RealD zAAz; int nrhs=src.size(); GridBase *grid=src[0].Grid();
|
|
||||||
std::vector<Field> r(nrhs,grid),z(nrhs,grid),Az(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<RealD> qq(mmax);
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR nStep("<<nstep<<")"<<std::endl;
|
|
||||||
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;
|
|
||||||
Preconditioner(r,z); vOp(z,Az); zAAz=vnorm2(Az);
|
|
||||||
p[0]=z; q[0]=Az; qq[0]=zAAz; cp=vnorm2(r);
|
|
||||||
for(int k=0;k<nstep;k++){
|
|
||||||
steps++; int kp=k+1, peri_k=k%mmax, peri_kp=kp%mmax;
|
|
||||||
{
|
|
||||||
GRID_TRACE("GCR-project");
|
|
||||||
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;
|
|
||||||
if((k==nstep-1)||(cp<rsq)) return cp;
|
|
||||||
Preconditioner(r,z); vOp(z,Az); zAAz=vnorm2(Az);
|
|
||||||
|
|
||||||
{
|
|
||||||
GRID_TRACE("GCR-orthog");
|
|
||||||
q[peri_kp]=Az; p[peri_kp]=z;
|
|
||||||
int northog=((kp)>(mmax-1))?(mmax-1):(kp);
|
|
||||||
for(int back=0;back<northog;back++){ int peri_back=(k-back)%mmax; GRID_ASSERT((k-back)>=0);
|
|
||||||
b=-real(vinnerProduct(q[peri_back],Az))/qq[peri_back];
|
|
||||||
vaxpy(p[peri_kp],b,p[peri_back],p[peri_kp]); vaxpy(q[peri_kp],b,q[peri_back],q[peri_kp]); }
|
|
||||||
qq[peri_kp]=vnorm2(q[peri_kp]);
|
|
||||||
}
|
|
||||||
// q[peri_kp]=Az; p[peri_kp]=z;
|
|
||||||
// int northog=((kp)>(mmax-1))?(mmax-1):(kp);
|
|
||||||
// for(int back=0;back<northog;back++){ int peri_back=(k-back)%mmax; GRID_ASSERT((k-back)>=0);
|
|
||||||
// b=-real(vinnerProduct(q[peri_back],Az))/qq[peri_back];
|
|
||||||
// vaxpy(p[peri_kp],b,p[peri_back],p[peri_kp]); vaxpy(q[peri_kp],b,q[peri_back],q[peri_kp]); }
|
|
||||||
// qq[peri_kp]=vnorm2(q[peri_kp]);
|
|
||||||
}
|
|
||||||
GRID_ASSERT(0); return cp;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// L2->L3 mrhs V-cycle: LinearFunction on the 6D mrhs COARSE field.
|
|
||||||
// The coarse-coarse solve slot takes EITHER the dense mrhs solve
|
|
||||||
// (DENSE_CC=1) or the L3 PGCR (DENSE_CC=0).
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class CoarseField, class CoarseCoarseField>
|
|
||||||
class MrhsCoarseThreeLevelPrec : public LinearFunction<CoarseField> {
|
|
||||||
public:
|
|
||||||
LinearOperatorBase<CoarseField> &_CoarseOp; // mrhs coarse op (6D)
|
|
||||||
LinearFunction<CoarseField> &_CoarseSmoother; // shifted 6D coarse smoother
|
|
||||||
MultiRHSBlockProject<CoarseField> &_Projector; // L2->L3 (vector-based)
|
|
||||||
LinearFunction<CoarseCoarseField> &_CoarseCoarseSolve; // L3 solve (6D cc)
|
|
||||||
GridBase *_Coarse5d, *_CoarseCoarse5d, *_CoarseCoarseMrhs;
|
|
||||||
int _nrhs;
|
|
||||||
|
|
||||||
MrhsCoarseThreeLevelPrec(LinearOperatorBase<CoarseField> &CoarseOp,
|
|
||||||
LinearFunction<CoarseField> &CoarseSmoother,
|
|
||||||
MultiRHSBlockProject<CoarseField> &Projector,
|
|
||||||
LinearFunction<CoarseCoarseField> &CoarseCoarseSolve,
|
|
||||||
GridBase *Coarse5d, GridBase *CoarseCoarse5d, GridBase *CoarseCoarseMrhs, int nrhs)
|
|
||||||
: _CoarseOp(CoarseOp), _CoarseSmoother(CoarseSmoother), _Projector(Projector),
|
|
||||||
_CoarseCoarseSolve(CoarseCoarseSolve),
|
|
||||||
_Coarse5d(Coarse5d), _CoarseCoarse5d(CoarseCoarse5d), _CoarseCoarseMrhs(CoarseCoarseMrhs), _nrhs(nrhs) {}
|
|
||||||
|
|
||||||
using LinearFunction<CoarseField>::operator();
|
|
||||||
virtual void operator()(const CoarseField &in, CoarseField &out) {
|
|
||||||
int nrhs=_nrhs; double t;
|
|
||||||
CoarseField vec1(in.Grid());
|
|
||||||
CoarseField vec2(in.Grid());
|
|
||||||
|
|
||||||
// trivial pre-smoother
|
|
||||||
out = in;
|
|
||||||
|
|
||||||
// residual (6D coarse)
|
|
||||||
_CoarseOp.Op(out,vec1); sub(vec1,in,vec1);
|
|
||||||
|
|
||||||
// restrict: unpack 6D coarse -> vector<Coarse> -> blockProject -> vector<CoarseCoarse> -> pack 6D cc
|
|
||||||
std::vector<CoarseField> csplit(nrhs,_Coarse5d);
|
|
||||||
std::vector<CoarseCoarseField> ccsplit(nrhs,_CoarseCoarse5d);
|
|
||||||
CoarseCoarseField CCsrc(_CoarseCoarseMrhs);
|
|
||||||
CoarseCoarseField CCsol(_CoarseCoarseMrhs);
|
|
||||||
|
|
||||||
t=-usecond();
|
|
||||||
{
|
|
||||||
GRID_TRACE("L2L3-Vcycle - Extract/blockProject/Insert");
|
|
||||||
for(int r=0;r<nrhs;r++) ExtractSliceFast(csplit[r],vec1,r,0);
|
|
||||||
_Projector.blockProject(csplit,ccsplit);
|
|
||||||
for(int r=0;r<nrhs;r++) InsertSliceFast(ccsplit[r],CCsrc,r,0);
|
|
||||||
}
|
|
||||||
t+=usecond();
|
|
||||||
std::cout<<GridLogMessage<<"L2->L3 restrict took "<<t/1000.0<<"ms"<<std::endl;
|
|
||||||
|
|
||||||
// L3 solve (dense mrhs GEMM, or PGCR)
|
|
||||||
t=-usecond();
|
|
||||||
{
|
|
||||||
GRID_TRACE("L2L3-Vcycle - CoarseCoarseSolve");
|
|
||||||
CCsol=Zero();
|
|
||||||
_CoarseCoarseSolve(CCsrc,CCsol);
|
|
||||||
}
|
|
||||||
t+=usecond();
|
|
||||||
std::cout<<GridLogMessage<<"L3 coarse-coarse solve took "<<t/1000.0<<"ms ("<<t/1000.0/nrhs<<"ms/rhs)"<<std::endl;
|
|
||||||
|
|
||||||
// prolong: unpack 6D cc -> blockPromote -> pack 6D coarse; add correction
|
|
||||||
t=-usecond();
|
|
||||||
{
|
|
||||||
GRID_TRACE("L2L3-Vcycle - Ext/blockPromote/Ins");
|
|
||||||
for(int r=0;r<nrhs;r++) ExtractSliceFast(ccsplit[r],CCsol,r,0);
|
|
||||||
_Projector.blockPromote(csplit,ccsplit);
|
|
||||||
for(int r=0;r<nrhs;r++) InsertSliceFast(csplit[r],vec1,r,0);
|
|
||||||
add(out,out,vec1);
|
|
||||||
}
|
|
||||||
t+=usecond();
|
|
||||||
std::cout<<GridLogMessage<<"L2->L3 prolong took "<<t/1000.0<<"ms"<<std::endl;
|
|
||||||
|
|
||||||
// residual + coarse smoother (6D coarse)
|
|
||||||
{
|
|
||||||
GRID_TRACE("L2L3-Vcycle - Resid+CoarseSmooth");
|
|
||||||
_CoarseOp.Op(out,vec1); sub(vec1,in,vec1);
|
|
||||||
vec2=Zero();
|
|
||||||
_CoarseSmoother(vec1,vec2);
|
|
||||||
add(out,out,vec2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// L1->L2 mrhs V-cycle (verbatim from the frozen example)
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class FineField, class MrhsCoarseVector, class FineSmoother>
|
|
||||||
class MrhsTwoLevelMG : public MrhsLinearFunction<FineField> {
|
|
||||||
public:
|
|
||||||
typedef MrhsCoarseVector CoarseVector;
|
|
||||||
LinearOperatorBase<FineField> &_FineOperator;
|
|
||||||
FineSmoother &_PostSmoother;
|
|
||||||
MultiRHSBlockProject<FineField> &_Projector;
|
|
||||||
LinearFunction<CoarseVector> &_CoarseSolve;
|
|
||||||
GridBase *_CoarseGrid, *_CoarseGridMrhs;
|
|
||||||
MrhsTwoLevelMG(LinearOperatorBase<FineField> &FineOp, FineSmoother &Post,
|
|
||||||
MultiRHSBlockProject<FineField> &Projector, LinearFunction<CoarseVector> &CoarseSolve,
|
|
||||||
GridBase *CoarseGrid, GridBase *CoarseGridMrhs)
|
|
||||||
: _FineOperator(FineOp),_PostSmoother(Post),_Projector(Projector),_CoarseSolve(CoarseSolve),
|
|
||||||
_CoarseGrid(CoarseGrid),_CoarseGridMrhs(CoarseGridMrhs){}
|
|
||||||
virtual void operator()(std::vector<FineField> &in, std::vector<FineField> &out){
|
|
||||||
int nrhs=in.size(); GridBase *fgrid=in[0].Grid(); double t;
|
|
||||||
|
|
||||||
std::vector<FineField> vec1(nrhs,fgrid),vec2(nrhs,fgrid);
|
|
||||||
{
|
|
||||||
GRID_TRACE("L1L2-Vcycle - Resid");
|
|
||||||
for(int r=0;r<nrhs;r++) out[r]=in[r];
|
|
||||||
for(int r=0;r<nrhs;r++){
|
|
||||||
_FineOperator.Op(out[r],vec1[r]);
|
|
||||||
sub(vec1[r],in[r],vec1[r]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::vector<CoarseVector> Csrc_split(nrhs,_CoarseGrid), Csol_split(nrhs,_CoarseGrid);
|
|
||||||
CoarseVector CsrcMrhs(_CoarseGridMrhs), CsolMrhs(_CoarseGridMrhs);
|
|
||||||
t=-usecond();
|
|
||||||
{
|
|
||||||
GRID_TRACE("L1L2-Vcycle - blockProject");
|
|
||||||
_Projector.blockProject(vec1,Csrc_split);
|
|
||||||
for(int r=0;r<nrhs;r++) InsertSliceFast(Csrc_split[r],CsrcMrhs,r,0);
|
|
||||||
}
|
|
||||||
t+=usecond(); std::cout<<GridLogMessage<<"Mrhs project+pack took "<<t/1000.0<<"ms"<<std::endl;
|
|
||||||
t=-usecond();
|
|
||||||
{
|
|
||||||
GRID_TRACE("L1L2-Vcycle - CoarseSolve");
|
|
||||||
CsolMrhs=Zero();
|
|
||||||
_CoarseSolve(CsrcMrhs,CsolMrhs);
|
|
||||||
}
|
|
||||||
t+=usecond();
|
|
||||||
std::cout<<GridLogMessage<<"Mrhs coarse solve took "<<t/1000.0<<"ms ("<<t/1000.0/nrhs<<"ms/rhs)"<<std::endl;
|
|
||||||
t=-usecond();
|
|
||||||
{
|
|
||||||
GRID_TRACE("L1L2-Vcycle - ExtractSlice/blockPromote/add delta");
|
|
||||||
for(int r=0;r<nrhs;r++) ExtractSliceFast(Csol_split[r],CsolMrhs,r,0);
|
|
||||||
_Projector.blockPromote(vec1,Csol_split);
|
|
||||||
for(int r=0;r<nrhs;r++) add(out[r],out[r],vec1[r]);
|
|
||||||
}
|
|
||||||
t+=usecond(); std::cout<<GridLogMessage<<"Mrhs unpack+promote took "<<t/1000.0<<"ms"<<std::endl;
|
|
||||||
{
|
|
||||||
GRID_TRACE("L1L2-Vcycle - Residuals");
|
|
||||||
for(int r=0;r<nrhs;r++){ _FineOperator.Op(out[r],vec1[r]); sub(vec1[r],in[r],vec1[r]); }
|
|
||||||
}
|
|
||||||
t=-usecond();
|
|
||||||
{
|
|
||||||
GRID_TRACE("L1L2-Vcycle - Smoothers");
|
|
||||||
for(int r=0;r<nrhs;r++){ vec2[r]=Zero(); _PostSmoother(vec1[r],vec2[r]); add(out[r],out[r],vec2[r]); }
|
|
||||||
}
|
|
||||||
t+=usecond(); std::cout<<GridLogMessage<<"Mrhs post-smooth took "<<t/1000.0<<"ms ("<<t/1000.0/nrhs<<"ms/rhs)"<<std::endl;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int main (int argc, char ** argv)
|
int main (int argc, char ** argv)
|
||||||
{
|
{
|
||||||
@@ -629,7 +355,7 @@ int main (int argc, char ** argv)
|
|||||||
std::cout << GridLogMessage << "**********************************************" << std::endl;
|
std::cout << GridLogMessage << "**********************************************" << std::endl;
|
||||||
DenseCC.reset(new DenseCC_t(CoarseCoarse5d));
|
DenseCC.reset(new DenseCC_t(CoarseCoarse5d));
|
||||||
DenseCC->Import(LittleDiracOpL2);
|
DenseCC->Import(LittleDiracOpL2);
|
||||||
MrhsDenseCC.reset(new MrhsDenseCCSolve<DenseCC_t,CoarseCoarseVector>(*DenseCC, CoarseCoarse5d, nrhs));
|
MrhsDenseCC.reset(new MrhsDenseCCSolve<DenseCC_t,CoarseCoarseVector>(*DenseCC, nrhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
#include <Grid/lattice/PaddedCell.h>
|
#include <Grid/lattice/PaddedCell.h>
|
||||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
#include <Grid/stencil/GeneralLocalStencil.h>
|
||||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
||||||
|
#include <Grid/algorithms/multigrid/MrhsMultiGrid.h>
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -830,226 +831,6 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// Dense CC solve on the PACKED 6D mrhs coarse-coarse field: unpack per
|
|
||||||
// rhs, ONE batched dense apply, repack. Drop-in for the L3 PGCR.
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class CoarseCoarseField>
|
|
||||||
class MrhsDenseCCSolve : public LinearFunction<CoarseCoarseField> {
|
|
||||||
public:
|
|
||||||
DistributedDenseInverse<CoarseCoarseField> &_Dense;
|
|
||||||
GridBase *_CoarseCoarse5d;
|
|
||||||
int _nrhs;
|
|
||||||
MrhsDenseCCSolve(DistributedDenseInverse<CoarseCoarseField> &D, GridBase *cc5d, int nrhs)
|
|
||||||
: _Dense(D), _CoarseCoarse5d(cc5d), _nrhs(nrhs) {}
|
|
||||||
using LinearFunction<CoarseCoarseField>::operator();
|
|
||||||
virtual void operator()(const CoarseCoarseField &in, CoarseCoarseField &out){
|
|
||||||
if ( getenv("DENSE_CC_CHECK") ) {
|
|
||||||
// Audit path: per-rhs 5D unpack so ApplyBatch can run the _Op defect
|
|
||||||
// check per rhs. ~50ms/call of slice/split overhead -- audit only.
|
|
||||||
CoarseCoarseField tmp(in.Grid());
|
|
||||||
tmp = in;
|
|
||||||
std::vector<CoarseCoarseField> split_in (_nrhs,_CoarseCoarse5d);
|
|
||||||
std::vector<CoarseCoarseField> split_out(_nrhs,_CoarseCoarse5d);
|
|
||||||
for(int r=0;r<_nrhs;r++) ExtractSliceFast(split_in[r], tmp, r, 0);
|
|
||||||
_Dense.ApplyBatch(split_in, split_out);
|
|
||||||
for(int r=0;r<_nrhs;r++) InsertSliceFast(split_out[r], out, r, 0);
|
|
||||||
} else {
|
|
||||||
_Dense.ApplyBatch6D(in, out, _nrhs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// mrhs interfaces + single-polynomial mrhs PGCR (verbatim from Example_pvdagm_mrhs.cc)
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class Field>
|
|
||||||
class MrhsLinearFunction {
|
|
||||||
public:
|
|
||||||
virtual void operator()(std::vector<Field> &in, std::vector<Field> &out) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Field>
|
|
||||||
class MrhsPGCRNonHermitian {
|
|
||||||
public:
|
|
||||||
RealD Tolerance; Integer MaxIterations; int mmax,nstep,steps,level;
|
|
||||||
int ZeroGuess = 0; int FirstCycle = 0; // caller contract: zero guess => first-cycle r0 = src
|
|
||||||
std::string name = "Level 1";
|
|
||||||
LinearOperatorBase<Field> &Linop;
|
|
||||||
MrhsLinearFunction<Field> &Preconditioner;
|
|
||||||
void Level(int lv){ name = "Level " + std::to_string(lv); level=lv; }
|
|
||||||
void Name(std::string n){ name = n; }
|
|
||||||
void SetZeroGuess(int z){ ZeroGuess=z; }
|
|
||||||
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; }
|
|
||||||
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){ 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){
|
|
||||||
RealD cp,ssq,rsq; int nrhs=src.size(); GridBase *grid=src[0].Grid();
|
|
||||||
ssq=vnorm2(src); rsq=Tolerance*Tolerance*ssq;
|
|
||||||
std::vector<Field> r(nrhs,grid);
|
|
||||||
GridStopWatch T; T.Start(); steps=0; FirstCycle=1;
|
|
||||||
for(int k=0;k<MaxIterations;k++){
|
|
||||||
cp=GCRnStep(src,psi,rsq);
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR("<<mmax<<","<<nstep<<") "<<steps<<" steps cp = "<<cp<<" target "<<rsq<<std::endl;
|
|
||||||
if(cp<rsq){
|
|
||||||
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);
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR: Converged on iteration "<<steps
|
|
||||||
<<" computed residual "<<std::sqrt(cp/ssq)<<" true residual "<<std::sqrt(tr/ssq)<<" target "<<Tolerance<<std::endl;
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR Time elapsed: Total "<<T.Elapsed()<<std::endl;
|
|
||||||
for(int rr=0;rr<nrhs;rr++){ RealD rn=std::sqrt(norm2(r[rr])/norm2(src[rr])); std::cout<<GridLogMessage<<"MrhsPGCR per-rhs true residual["<<rr<<"] = "<<rn<<std::endl; }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::cout<<GridLogMessage<<"MrhsPGCR: did not converge"<<std::endl;
|
|
||||||
}
|
|
||||||
RealD GCRnStep(std::vector<Field> &src,std::vector<Field> &psi,RealD rsq){
|
|
||||||
RealD cp; ComplexD a,b,rq; RealD zAAz; int nrhs=src.size(); GridBase *grid=src[0].Grid();
|
|
||||||
std::vector<Field> r(nrhs,grid),z(nrhs,grid),Az(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<RealD> qq(mmax);
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR nStep("<<nstep<<")"<<std::endl;
|
|
||||||
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;
|
|
||||||
Preconditioner(r,z); vOp(z,Az); zAAz=vnorm2(Az);
|
|
||||||
p[0]=z; q[0]=Az; qq[0]=zAAz; cp=vnorm2(r);
|
|
||||||
for(int k=0;k<nstep;k++){
|
|
||||||
steps++; int kp=k+1, peri_k=k%mmax, peri_kp=kp%mmax;
|
|
||||||
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;
|
|
||||||
if((k==nstep-1)||(cp<rsq)) return cp;
|
|
||||||
Preconditioner(r,z); vOp(z,Az); zAAz=vnorm2(Az);
|
|
||||||
q[peri_kp]=Az; p[peri_kp]=z;
|
|
||||||
int northog=((kp)>(mmax-1))?(mmax-1):(kp);
|
|
||||||
for(int back=0;back<northog;back++){ int peri_back=(k-back)%mmax; GRID_ASSERT((k-back)>=0);
|
|
||||||
b=-real(vinnerProduct(q[peri_back],Az))/qq[peri_back];
|
|
||||||
vaxpy(p[peri_kp],b,p[peri_back],p[peri_kp]); vaxpy(q[peri_kp],b,q[peri_back],q[peri_kp]); }
|
|
||||||
qq[peri_kp]=vnorm2(q[peri_kp]);
|
|
||||||
}
|
|
||||||
GRID_ASSERT(0); return cp;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// L2->L3 mrhs V-cycle: LinearFunction on the 6D mrhs COARSE field.
|
|
||||||
// The coarse-coarse solve slot now takes EITHER the dense mrhs solve
|
|
||||||
// (DENSE_CC=1) or the L3 PGCR (DENSE_CC=0).
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class CoarseField, class CoarseCoarseField>
|
|
||||||
class MrhsCoarseThreeLevelPrec : public LinearFunction<CoarseField> {
|
|
||||||
public:
|
|
||||||
LinearOperatorBase<CoarseField> &_CoarseOp; // mrhs coarse op (6D)
|
|
||||||
LinearFunction<CoarseField> &_CoarseSmoother; // shifted 6D coarse smoother
|
|
||||||
MultiRHSBlockProject<CoarseField> &_Projector; // L2->L3 (vector-based)
|
|
||||||
LinearFunction<CoarseCoarseField> &_CoarseCoarseSolve; // L3 solve (6D cc)
|
|
||||||
GridBase *_Coarse5d, *_CoarseCoarse5d, *_CoarseCoarseMrhs;
|
|
||||||
int _nrhs;
|
|
||||||
|
|
||||||
MrhsCoarseThreeLevelPrec(LinearOperatorBase<CoarseField> &CoarseOp,
|
|
||||||
LinearFunction<CoarseField> &CoarseSmoother,
|
|
||||||
MultiRHSBlockProject<CoarseField> &Projector,
|
|
||||||
LinearFunction<CoarseCoarseField> &CoarseCoarseSolve,
|
|
||||||
GridBase *Coarse5d, GridBase *CoarseCoarse5d, GridBase *CoarseCoarseMrhs, int nrhs)
|
|
||||||
: _CoarseOp(CoarseOp), _CoarseSmoother(CoarseSmoother), _Projector(Projector),
|
|
||||||
_CoarseCoarseSolve(CoarseCoarseSolve),
|
|
||||||
_Coarse5d(Coarse5d), _CoarseCoarse5d(CoarseCoarse5d), _CoarseCoarseMrhs(CoarseCoarseMrhs), _nrhs(nrhs) {}
|
|
||||||
|
|
||||||
using LinearFunction<CoarseField>::operator();
|
|
||||||
virtual void operator()(const CoarseField &in, CoarseField &out) {
|
|
||||||
int nrhs=_nrhs; double t;
|
|
||||||
CoarseField vec1(in.Grid());
|
|
||||||
CoarseField vec2(in.Grid());
|
|
||||||
|
|
||||||
// trivial pre-smoother
|
|
||||||
out = in;
|
|
||||||
|
|
||||||
// residual (6D coarse)
|
|
||||||
_CoarseOp.Op(out,vec1); sub(vec1,in,vec1);
|
|
||||||
|
|
||||||
// restrict: unpack 6D coarse -> vector<Coarse> -> blockProject -> vector<CoarseCoarse> -> pack 6D cc
|
|
||||||
std::vector<CoarseField> csplit(nrhs,_Coarse5d);
|
|
||||||
std::vector<CoarseCoarseField> ccsplit(nrhs,_CoarseCoarse5d);
|
|
||||||
CoarseCoarseField CCsrc(_CoarseCoarseMrhs);
|
|
||||||
CoarseCoarseField CCsol(_CoarseCoarseMrhs);
|
|
||||||
|
|
||||||
t=-usecond();
|
|
||||||
for(int r=0;r<nrhs;r++) ExtractSliceFast(csplit[r],vec1,r,0);
|
|
||||||
_Projector.blockProject(csplit,ccsplit);
|
|
||||||
for(int r=0;r<nrhs;r++) InsertSliceFast(ccsplit[r],CCsrc,r,0);
|
|
||||||
t+=usecond();
|
|
||||||
std::cout<<GridLogMessage<<"L2->L3 restrict took "<<t/1000.0<<"ms"<<std::endl;
|
|
||||||
|
|
||||||
// L3 solve (dense mrhs GEMM, or PGCR)
|
|
||||||
t=-usecond();
|
|
||||||
CCsol=Zero();
|
|
||||||
_CoarseCoarseSolve(CCsrc,CCsol);
|
|
||||||
t+=usecond();
|
|
||||||
std::cout<<GridLogMessage<<"L3 coarse-coarse solve took "<<t/1000.0<<"ms ("<<t/1000.0/nrhs<<"ms/rhs)"<<std::endl;
|
|
||||||
|
|
||||||
// prolong: unpack 6D cc -> blockPromote -> pack 6D coarse; add correction
|
|
||||||
t=-usecond();
|
|
||||||
for(int r=0;r<nrhs;r++) ExtractSliceFast(ccsplit[r],CCsol,r,0);
|
|
||||||
_Projector.blockPromote(csplit,ccsplit);
|
|
||||||
for(int r=0;r<nrhs;r++) InsertSliceFast(csplit[r],vec1,r,0);
|
|
||||||
add(out,out,vec1);
|
|
||||||
t+=usecond();
|
|
||||||
std::cout<<GridLogMessage<<"L2->L3 prolong took "<<t/1000.0<<"ms"<<std::endl;
|
|
||||||
|
|
||||||
// residual + coarse smoother (6D coarse)
|
|
||||||
_CoarseOp.Op(out,vec1); sub(vec1,in,vec1);
|
|
||||||
vec2=Zero();
|
|
||||||
_CoarseSmoother(vec1,vec2);
|
|
||||||
add(out,out,vec2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// L1->L2 mrhs V-cycle (verbatim from Example_pvdagm_mrhs.cc)
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class FineField, class MrhsCoarseVector, class FineSmoother>
|
|
||||||
class MrhsTwoLevelMG : public MrhsLinearFunction<FineField> {
|
|
||||||
public:
|
|
||||||
typedef MrhsCoarseVector CoarseVector;
|
|
||||||
LinearOperatorBase<FineField> &_FineOperator;
|
|
||||||
FineSmoother &_PostSmoother;
|
|
||||||
MultiRHSBlockProject<FineField> &_Projector;
|
|
||||||
LinearFunction<CoarseVector> &_CoarseSolve;
|
|
||||||
GridBase *_CoarseGrid, *_CoarseGridMrhs;
|
|
||||||
MrhsTwoLevelMG(LinearOperatorBase<FineField> &FineOp, FineSmoother &Post,
|
|
||||||
MultiRHSBlockProject<FineField> &Projector, LinearFunction<CoarseVector> &CoarseSolve,
|
|
||||||
GridBase *CoarseGrid, GridBase *CoarseGridMrhs)
|
|
||||||
: _FineOperator(FineOp),_PostSmoother(Post),_Projector(Projector),_CoarseSolve(CoarseSolve),
|
|
||||||
_CoarseGrid(CoarseGrid),_CoarseGridMrhs(CoarseGridMrhs){}
|
|
||||||
virtual void operator()(std::vector<FineField> &in, std::vector<FineField> &out){
|
|
||||||
int nrhs=in.size(); GridBase *fgrid=in[0].Grid(); double t;
|
|
||||||
std::vector<FineField> vec1(nrhs,fgrid),vec2(nrhs,fgrid);
|
|
||||||
for(int r=0;r<nrhs;r++) out[r]=in[r];
|
|
||||||
for(int r=0;r<nrhs;r++){ _FineOperator.Op(out[r],vec1[r]); sub(vec1[r],in[r],vec1[r]); }
|
|
||||||
std::vector<CoarseVector> Csrc_split(nrhs,_CoarseGrid), Csol_split(nrhs,_CoarseGrid);
|
|
||||||
CoarseVector CsrcMrhs(_CoarseGridMrhs), CsolMrhs(_CoarseGridMrhs);
|
|
||||||
t=-usecond();
|
|
||||||
_Projector.blockProject(vec1,Csrc_split);
|
|
||||||
for(int r=0;r<nrhs;r++) InsertSliceFast(Csrc_split[r],CsrcMrhs,r,0);
|
|
||||||
t+=usecond(); std::cout<<GridLogMessage<<"Mrhs project+pack took "<<t/1000.0<<"ms"<<std::endl;
|
|
||||||
t=-usecond(); CsolMrhs=Zero(); _CoarseSolve(CsrcMrhs,CsolMrhs); t+=usecond();
|
|
||||||
std::cout<<GridLogMessage<<"Mrhs coarse solve took "<<t/1000.0<<"ms ("<<t/1000.0/nrhs<<"ms/rhs)"<<std::endl;
|
|
||||||
t=-usecond();
|
|
||||||
for(int r=0;r<nrhs;r++) ExtractSliceFast(Csol_split[r],CsolMrhs,r,0);
|
|
||||||
_Projector.blockPromote(vec1,Csol_split);
|
|
||||||
for(int r=0;r<nrhs;r++) add(out[r],out[r],vec1[r]);
|
|
||||||
t+=usecond(); std::cout<<GridLogMessage<<"Mrhs unpack+promote took "<<t/1000.0<<"ms"<<std::endl;
|
|
||||||
for(int r=0;r<nrhs;r++){ _FineOperator.Op(out[r],vec1[r]); sub(vec1[r],in[r],vec1[r]); }
|
|
||||||
t=-usecond();
|
|
||||||
for(int r=0;r<nrhs;r++){ vec2[r]=Zero(); _PostSmoother(vec1[r],vec2[r]); add(out[r],out[r],vec2[r]); }
|
|
||||||
t+=usecond(); std::cout<<GridLogMessage<<"Mrhs post-smooth took "<<t/1000.0<<"ms ("<<t/1000.0/nrhs<<"ms/rhs)"<<std::endl;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int main (int argc, char ** argv)
|
int main (int argc, char ** argv)
|
||||||
{
|
{
|
||||||
@@ -1221,13 +1002,13 @@ int main (int argc, char ** argv)
|
|||||||
// its memory peak; needs only the hoisted single-RHS LinOpCC5d).
|
// its memory peak; needs only the hoisted single-RHS LinOpCC5d).
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
std::unique_ptr<DistributedDenseInverse<CoarseCoarseVector>> DenseCC;
|
std::unique_ptr<DistributedDenseInverse<CoarseCoarseVector>> DenseCC;
|
||||||
std::unique_ptr<MrhsDenseCCSolve<CoarseCoarseVector>> MrhsDenseCC;
|
std::unique_ptr<MrhsDenseCCSolve<DistributedDenseInverse<CoarseCoarseVector>,CoarseCoarseVector>> MrhsDenseCC;
|
||||||
if (UseDenseCC) {
|
if (UseDenseCC) {
|
||||||
std::cout << GridLogMessage << "**********************************************" << std::endl;
|
std::cout << GridLogMessage << "**********************************************" << std::endl;
|
||||||
std::cout << GridLogMessage << " Dense CC inverse setup (mrhs bottom)" << std::endl;
|
std::cout << GridLogMessage << " Dense CC inverse setup (mrhs bottom)" << std::endl;
|
||||||
std::cout << GridLogMessage << "**********************************************" << std::endl;
|
std::cout << GridLogMessage << "**********************************************" << std::endl;
|
||||||
DenseCC.reset(new DistributedDenseInverse<CoarseCoarseVector>(LinOpCC5d, CoarseCoarse5d, nbasis));
|
DenseCC.reset(new DistributedDenseInverse<CoarseCoarseVector>(LinOpCC5d, CoarseCoarse5d, nbasis));
|
||||||
MrhsDenseCC.reset(new MrhsDenseCCSolve<CoarseCoarseVector>(*DenseCC, CoarseCoarse5d, nrhs));
|
MrhsDenseCC.reset(new MrhsDenseCCSolve<DistributedDenseInverse<CoarseCoarseVector>,CoarseCoarseVector>(*DenseCC, nrhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -33,7 +33,11 @@ Author: Peter Boyle <pboyle@bnl.gov>
|
|||||||
// parameter. The effective parameters are always printed, so the log
|
// parameter. The effective parameters are always printed, so the log
|
||||||
// describes its own run.
|
// describes its own run.
|
||||||
//
|
//
|
||||||
// Compile-time: -DNBASIS=8 cuts the basis down for laptop runs.
|
// Compile-time: NBASIS defaults to 60, the production basis; a subspace file
|
||||||
|
// may hold more vectors, the load reads only the first NBASIS. -DNBASIS=8
|
||||||
|
// cuts it down for laptop runs.
|
||||||
|
// examples/Makefile.am builds the fp32-coarse and fp32-dense-inversion
|
||||||
|
// variants below as their own binaries.
|
||||||
//
|
//
|
||||||
#include <Grid/Grid.h>
|
#include <Grid/Grid.h>
|
||||||
#include <Grid/lattice/PaddedCell.h>
|
#include <Grid/lattice/PaddedCell.h>
|
||||||
@@ -48,6 +52,16 @@ using namespace Grid;
|
|||||||
#define NBASIS 60
|
#define NBASIS 60
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Precision of the coarse + coarse-coarse sector is a compile-time
|
||||||
|
// instantiation: -DCOARSE_SINGLE builds the fp32 coarse space (the dense
|
||||||
|
// bottom's apply slab is fp32 either way; its inversion is a configure
|
||||||
|
// option). Both levels carry the same site type iVector<CoarseScalar,NBASIS>.
|
||||||
|
#ifdef COARSE_SINGLE
|
||||||
|
typedef sTComplexF CoarseScalar_t;
|
||||||
|
#else
|
||||||
|
typedef sTComplexD CoarseScalar_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
struct PVdagMDriverParams : Serializable {
|
struct PVdagMDriverParams : Serializable {
|
||||||
GRID_SERIALIZABLE_CLASS_MEMBERS(PVdagMDriverParams,
|
GRID_SERIALIZABLE_CLASS_MEMBERS(PVdagMDriverParams,
|
||||||
int, Ls,
|
int, Ls,
|
||||||
@@ -123,22 +137,46 @@ int main (int argc, char ** argv)
|
|||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Grids -> coarsening -> dense bottom. Scope order is lifetime order.
|
// Grids -> coarsening -> dense bottom. Scope order is lifetime order.
|
||||||
|
//
|
||||||
|
// The fine operator applied during setup is always fp64, on the fp64
|
||||||
|
// basis. Everything the coarsening produces -- the Galerkin matrix
|
||||||
|
// elements and the transfer operator's store -- takes the coarse precision
|
||||||
|
// (CoarseScalar_t, a compile-time choice).
|
||||||
|
//
|
||||||
|
// There is exactly ONE fine transfer operator. Its STORE follows the
|
||||||
|
// coarse sector, since the coarse space is what it feeds, while its import
|
||||||
|
// and export accept either fine precision: they are already a layout
|
||||||
|
// transformation, and a scalar conversion inside one is free. So the fp64
|
||||||
|
// setup and an fp32 V-cycle share the same object and the same basis store.
|
||||||
|
// FinePrecision selects only the fine operator and smoother; the outer
|
||||||
|
// Krylov and its true-residual check stay fp64 throughout.
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
typedef PVdagMMultiGridCoarsening<vSpinColourVector,sTComplexD,NBASIS> Coarsening_t;
|
typedef PVdagMMultiGridCoarsening<vSpinColourVector,CoarseScalar_t,NBASIS> Coarsening_t;
|
||||||
|
std::cout << GridLogMessage << "Coarse sector precision (compiled): "
|
||||||
|
<< (sizeof(typename GridTypeMapper<CoarseScalar_t>::scalar_type)==sizeof(ComplexF) ? "fp32" : "fp64")
|
||||||
|
<< ", nbasis " << NBASIS << std::endl;
|
||||||
|
|
||||||
MGCoarseGrids CGrids(FGrid, P.MultiGrid.Setup);
|
MGCoarseGrids CGrids(FGrid, P.MultiGrid.Setup);
|
||||||
Coarsening_t Coarsening(CGrids, P.MultiGrid.Setup);
|
MGFineGridsF FGridsF(FGrid);
|
||||||
|
|
||||||
|
LatticeGaugeFieldF UmuF(FGridsF.UGridF);
|
||||||
|
precisionChange(UmuF,Umu);
|
||||||
|
MobiusFermionF DdwfF(UmuF,*FGridsF.FGridF,*FGridsF.FrbGridF,*FGridsF.UGridF,*FGridsF.UrbGridF,P.Mass,P.M5,P.MobiusB,P.MobiusC);
|
||||||
|
MobiusFermionF DpvF (UmuF,*FGridsF.FGridF,*FGridsF.FrbGridF,*FGridsF.UGridF,*FGridsF.UrbGridF,1.0, P.M5,P.MobiusB,P.MobiusC);
|
||||||
|
|
||||||
|
Coarsening_t Coarsening(CGrids, FGridsF, P.MultiGrid.Setup);
|
||||||
|
|
||||||
Coarsening.GetSubspace(RNG5, PVdagM);
|
Coarsening.GetSubspace(RNG5, PVdagM);
|
||||||
Coarsening.Coarsen(PVdagM);
|
Coarsening.Coarsen(PVdagM);
|
||||||
Coarsening.BuildDenseBottom();
|
Coarsening.BuildDenseBottom();
|
||||||
|
Coarsening.CertifyCoarsening(PVdagM);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Solves: mrhs then (optionally) single RHS through the SAME objects.
|
// Solves: mrhs then (optionally) single RHS through the SAME objects.
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
auto RunSolve = [&](int nr)
|
auto RunSolve = [&](int nr)
|
||||||
{
|
{
|
||||||
PVdagMMultiGridSolver<MobiusFermionD,Coarsening_t> Solver(Ddwf,Dpv,Coarsening,P.MultiGrid,nr);
|
PVdagMMultiGridSolver<MobiusFermionD,MobiusFermionF,Coarsening_t> Solver(Ddwf,Dpv,DdwfF,DpvF,Coarsening,P.MultiGrid,nr);
|
||||||
std::vector<LatticeFermionD> src(nr,FGrid), sol(nr,FGrid);
|
std::vector<LatticeFermionD> src(nr,FGrid), sol(nr,FGrid);
|
||||||
for(int r=0;r<nr;r++){ gaussian(RNG5,src[r]); sol[r]=Zero(); }
|
for(int r=0;r<nr;r++){ gaussian(RNG5,src[r]); sol[r]=Zero(); }
|
||||||
Solver.Solve(src,sol);
|
Solver.Solve(src,sol);
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ Author: Peter Boyle <pboyle@bnl.gov>
|
|||||||
#include <Grid/lattice/PaddedCell.h>
|
#include <Grid/lattice/PaddedCell.h>
|
||||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
#include <Grid/stencil/GeneralLocalStencil.h>
|
||||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
||||||
|
#include <Grid/algorithms/multigrid/MrhsMultiGrid.h>
|
||||||
#include <Grid/algorithms/multigrid/DenseCoarseMatrix.h>
|
#include <Grid/algorithms/multigrid/DenseCoarseMatrix.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -396,220 +397,6 @@ void PowerIteration(const std::string &name, LinearOperatorBase<Field> &Op, Grid
|
|||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// Dense L3 solve on the packed D+1 coarse-coarse field
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class DenseType, class CoarseCoarseField>
|
|
||||||
class MrhsDenseCCSolve : public LinearFunction<CoarseCoarseField> {
|
|
||||||
public:
|
|
||||||
DenseType &_Dense;
|
|
||||||
int _nrhs;
|
|
||||||
MrhsDenseCCSolve(DenseType &D, int nrhs) : _Dense(D), _nrhs(nrhs) {}
|
|
||||||
using LinearFunction<CoarseCoarseField>::operator();
|
|
||||||
virtual void operator()(const CoarseCoarseField &in, CoarseCoarseField &out){
|
|
||||||
_Dense.ApplyBatch6D(in, out, _nrhs);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// mrhs interfaces + single-polynomial mrhs PGCR
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class Field>
|
|
||||||
class MrhsLinearFunction {
|
|
||||||
public:
|
|
||||||
virtual void operator()(std::vector<Field> &in, std::vector<Field> &out) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Field>
|
|
||||||
class MrhsPGCRNonHermitian {
|
|
||||||
public:
|
|
||||||
RealD Tolerance; Integer MaxIterations; int mmax,nstep,steps,level;
|
|
||||||
int ZeroGuess = 0; int FirstCycle = 0;
|
|
||||||
std::string name = "Level 1";
|
|
||||||
LinearOperatorBase<Field> &Linop;
|
|
||||||
MrhsLinearFunction<Field> &Preconditioner;
|
|
||||||
std::function<void(int)> OnStep; // called with the outer step count after every step (smoother switching)
|
|
||||||
void Level(int lv){ name = "Level " + std::to_string(lv); level=lv; }
|
|
||||||
void Name(std::string n){ name = n; }
|
|
||||||
void SetZeroGuess(int z){ ZeroGuess=z; }
|
|
||||||
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; }
|
|
||||||
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("MrhsPGCR::vOp"); 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){
|
|
||||||
RealD cp,ssq,rsq; int nrhs=src.size(); GridBase *grid=src[0].Grid();
|
|
||||||
ssq=vnorm2(src); rsq=Tolerance*Tolerance*ssq;
|
|
||||||
std::vector<Field> r(nrhs,grid);
|
|
||||||
GridStopWatch T; T.Start(); steps=0; FirstCycle=1;
|
|
||||||
for(int k=0;k<MaxIterations;k++){
|
|
||||||
cp=GCRnStep(src,psi,rsq);
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR("<<mmax<<","<<nstep<<") "<<steps<<" steps cp = "<<cp<<" target "<<rsq<<std::endl;
|
|
||||||
if(cp<rsq){
|
|
||||||
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);
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR: Converged on iteration "<<steps
|
|
||||||
<<" computed residual "<<std::sqrt(cp/ssq)<<" true residual "<<std::sqrt(tr/ssq)<<" target "<<Tolerance<<std::endl;
|
|
||||||
std::cout<<GridLogMessage<<std::string(level,'\t')<<" "<<name<<" MrhsPGCR Time elapsed: Total "<<T.Elapsed()<<std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::cout<<GridLogMessage<<"MrhsPGCR: did not converge"<<std::endl;
|
|
||||||
}
|
|
||||||
RealD GCRnStep(std::vector<Field> &src,std::vector<Field> &psi,RealD rsq){
|
|
||||||
RealD cp; ComplexD a,b,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< std::vector<Field> > q(mmax,std::vector<Field>(nrhs,grid));
|
|
||||||
std::vector< std::vector<Field> > p(mmax,std::vector<Field>(nrhs,grid));
|
|
||||||
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]; }
|
|
||||||
FirstCycle=0;
|
|
||||||
// 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);
|
|
||||||
for(int k=0;k<nstep;k++){
|
|
||||||
steps++; int kp=k+1, peri_k=k%mmax, peri_kp=kp%mmax;
|
|
||||||
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);
|
|
||||||
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;
|
|
||||||
// New direction straight into its history slot: p=Prec(r), q=A p.
|
|
||||||
Preconditioner(r,p[peri_kp]);
|
|
||||||
vOp(p[peri_kp],q[peri_kp]);
|
|
||||||
int northog=((kp)>(mmax-1))?(mmax-1):(kp);
|
|
||||||
MemoryManager::Snapshot(name+" orthog begin step "+std::to_string(steps));
|
|
||||||
{
|
|
||||||
GRID_TRACE("MrhsPGCR orthog");
|
|
||||||
// Classical Gram-Schmidt: all coefficients against the UN-updated new q
|
|
||||||
// (independent, batchable), then apply. Complex coefficient: the
|
|
||||||
// operator is non-Hermitian, real(<q_j,Aq>) alone left q's non-orthogonal.
|
|
||||||
// Batched per rhs (one fused kernel + one reduction each), the shared
|
|
||||||
// coefficient summed over rhs on the host, ONE GlobalSumVector.
|
|
||||||
std::vector<ComplexD> bcoef(northog,ComplexD(0.0)), part;
|
|
||||||
for(int rr=0;rr<nrhs;rr++){
|
|
||||||
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]; }
|
|
||||||
rankInnerProductMulti(part,qwin,q[peri_kp][rr]);
|
|
||||||
for(int back=0;back<northog;back++) bcoef[back]+=part[back];
|
|
||||||
}
|
|
||||||
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 rr=0;rr<nrhs;rr++){
|
|
||||||
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]; }
|
|
||||||
axpyMulti(p[peri_kp][rr],bcoef,pwin);
|
|
||||||
axpyMulti(q[peri_kp][rr],bcoef,qwin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
qq[peri_kp]=vnorm2(q[peri_kp]);
|
|
||||||
MemoryManager::Snapshot(name+" orthog+vnorm2 end step "+std::to_string(steps));
|
|
||||||
}
|
|
||||||
GRID_ASSERT(0); return cp;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// L2->L3 mrhs V-cycle on the D+1 coarse field
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class CoarseField, class CoarseCoarseField>
|
|
||||||
class MrhsCoarseThreeLevelPrec : public LinearFunction<CoarseField> {
|
|
||||||
public:
|
|
||||||
LinearOperatorBase<CoarseField> &_CoarseOp;
|
|
||||||
LinearFunction<CoarseField> &_CoarseSmoother;
|
|
||||||
MultiRHSBlockProject<CoarseField> &_Projector;
|
|
||||||
LinearFunction<CoarseCoarseField> &_CoarseCoarseSolve;
|
|
||||||
GridBase *_Coarse5d, *_CoarseCoarse5d, *_CoarseCoarseMrhs;
|
|
||||||
int _nrhs;
|
|
||||||
MrhsCoarseThreeLevelPrec(LinearOperatorBase<CoarseField> &CoarseOp,
|
|
||||||
LinearFunction<CoarseField> &CoarseSmoother,
|
|
||||||
MultiRHSBlockProject<CoarseField> &Projector,
|
|
||||||
LinearFunction<CoarseCoarseField> &CoarseCoarseSolve,
|
|
||||||
GridBase *Coarse5d, GridBase *CoarseCoarse5d, GridBase *CoarseCoarseMrhs, int nrhs)
|
|
||||||
: _CoarseOp(CoarseOp), _CoarseSmoother(CoarseSmoother), _Projector(Projector),
|
|
||||||
_CoarseCoarseSolve(CoarseCoarseSolve),
|
|
||||||
_Coarse5d(Coarse5d), _CoarseCoarse5d(CoarseCoarse5d), _CoarseCoarseMrhs(CoarseCoarseMrhs), _nrhs(nrhs) {}
|
|
||||||
using LinearFunction<CoarseField>::operator();
|
|
||||||
virtual void operator()(const CoarseField &in, CoarseField &out) {
|
|
||||||
int nrhs=_nrhs;
|
|
||||||
CoarseField vec1(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 CCsol(_CoarseCoarseMrhs);
|
|
||||||
_Projector.blockProject(vec1,CCsrc);
|
|
||||||
|
|
||||||
// CCsol=Zero(); // Is this necessary?
|
|
||||||
_CoarseCoarseSolve(CCsrc,CCsol);
|
|
||||||
|
|
||||||
_Projector.blockPromote(vec1,CCsol);
|
|
||||||
add(out,out,vec1);
|
|
||||||
|
|
||||||
_CoarseOp.Op(out,vec1); sub(vec1,in,vec1);
|
|
||||||
// vec2=Zero(); // Zero guess
|
|
||||||
_CoarseSmoother(vec1,vec2);
|
|
||||||
add(out,out,vec2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// L1->L2 mrhs V-cycle
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
template<class FineField, class MrhsCoarseVector, class FineSmoother>
|
|
||||||
class MrhsTwoLevelMG : public MrhsLinearFunction<FineField> {
|
|
||||||
public:
|
|
||||||
typedef MrhsCoarseVector CoarseVector;
|
|
||||||
LinearOperatorBase<FineField> &_FineOperator;
|
|
||||||
FineSmoother &_PostSmoother;
|
|
||||||
MultiRHSBlockProject<FineField> &_Projector;
|
|
||||||
LinearFunction<CoarseVector> &_CoarseSolve;
|
|
||||||
GridBase *_CoarseGrid, *_CoarseGridMrhs;
|
|
||||||
MrhsTwoLevelMG(LinearOperatorBase<FineField> &FineOp, FineSmoother &Post,
|
|
||||||
MultiRHSBlockProject<FineField> &Projector, LinearFunction<CoarseVector> &CoarseSolve,
|
|
||||||
GridBase *CoarseGrid, GridBase *CoarseGridMrhs)
|
|
||||||
: _FineOperator(FineOp),_PostSmoother(Post),_Projector(Projector),_CoarseSolve(CoarseSolve),
|
|
||||||
_CoarseGrid(CoarseGrid),_CoarseGridMrhs(CoarseGridMrhs){}
|
|
||||||
virtual void operator()(std::vector<FineField> &in, std::vector<FineField> &out){
|
|
||||||
// The whole V-cycle is preconditioner: its fine residuals and the
|
|
||||||
// smoother run with sloppy halos; the caller (the outer Krylov) gets
|
|
||||||
// the exact operator back on exit.
|
|
||||||
GRID_TRACE("MGVcycle");
|
|
||||||
SetFineSloppy(FineSloppyComms);
|
|
||||||
int nrhs=in.size(); GridBase *fgrid=in[0].Grid();
|
|
||||||
std::vector<FineField> vec1(nrhs,fgrid),vec2(nrhs,fgrid);
|
|
||||||
for(int r=0;r<nrhs;r++) out[r]=in[r];
|
|
||||||
{ GRID_TRACE("MGFineResidual");
|
|
||||||
for(int r=0;r<nrhs;r++){ _FineOperator.Op(out[r],vec1[r]); sub(vec1[r],in[r],vec1[r]); }
|
|
||||||
}
|
|
||||||
// fine vector -> D+1 coarse, via the mixed blockProject
|
|
||||||
CoarseVector CsrcMrhs(_CoarseGridMrhs), CsolMrhs(_CoarseGridMrhs);
|
|
||||||
{ GRID_TRACE("MGProject");
|
|
||||||
_Projector.blockProject(vec1,CsrcMrhs);
|
|
||||||
}
|
|
||||||
CsolMrhs=Zero();
|
|
||||||
{ GRID_TRACE("MGCoarseSolve");
|
|
||||||
_CoarseSolve(CsrcMrhs,CsolMrhs);
|
|
||||||
}
|
|
||||||
{ GRID_TRACE("MGPromote");
|
|
||||||
_Projector.blockPromote(vec1,CsolMrhs);
|
|
||||||
for(int r=0;r<nrhs;r++) add(out[r],out[r],vec1[r]);
|
|
||||||
}
|
|
||||||
{ GRID_TRACE("MGFineResidual2");
|
|
||||||
for(int r=0;r<nrhs;r++){ _FineOperator.Op(out[r],vec1[r]); sub(vec1[r],in[r],vec1[r]); }
|
|
||||||
}
|
|
||||||
{ GRID_TRACE("MGPostSmooth");
|
|
||||||
for(int r=0;r<nrhs;r++){
|
|
||||||
// vec2[r]=Zero();
|
|
||||||
_PostSmoother(vec1[r],vec2[r]); add(out[r],out[r],vec2[r]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SetFineSloppy(0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int main (int argc, char ** argv)
|
int main (int argc, char ** argv)
|
||||||
{
|
{
|
||||||
@@ -1074,6 +861,8 @@ int main (int argc, char ** argv)
|
|||||||
SwitchableSmoother<LatticeFermionD> FineSmootherSlot(SmootherGCR,"Fsmoother GCR");
|
SwitchableSmoother<LatticeFermionD> FineSmootherSlot(SmootherGCR,"Fsmoother GCR");
|
||||||
MrhsTwoLevelMG<LatticeFermionD,CoarseVector,SwitchableSmoother<LatticeFermionD> >
|
MrhsTwoLevelMG<LatticeFermionD,CoarseVector,SwitchableSmoother<LatticeFermionD> >
|
||||||
ThreeLevelPrecon(PVdagM, FineSmootherSlot, MrhsProjector, L2PGCR, Coarse5d, CMrhs);
|
ThreeLevelPrecon(PVdagM, FineSmootherSlot, MrhsProjector, L2PGCR, Coarse5d, CMrhs);
|
||||||
|
ThreeLevelPrecon.SetSloppy = SetFineSloppy;
|
||||||
|
ThreeLevelPrecon.SloppyComms = FineSloppyComms;
|
||||||
|
|
||||||
MrhsPGCRNonHermitian<LatticeFermionD>
|
MrhsPGCRNonHermitian<LatticeFermionD>
|
||||||
L1PGCR(OuterTol,1000,PVdagM,ThreeLevelPrecon,OuterMmax,OuterNstep);
|
L1PGCR(OuterTol,1000,PVdagM,ThreeLevelPrecon,OuterMmax,OuterNstep);
|
||||||
|
|||||||
@@ -2,5 +2,23 @@ SUBDIRS = .
|
|||||||
|
|
||||||
include Make.inc
|
include Make.inc
|
||||||
|
|
||||||
|
# Precision variants of the two multigrid drivers. The coarse-sector and
|
||||||
|
# dense-inversion precisions are compile-time instantiations, so each is its
|
||||||
|
# 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
|
||||||
|
# variant its own object file.
|
||||||
|
bin_PROGRAMS += Example_pvdagm_multigrid_fp32coarse \
|
||||||
|
Example_pvdagm_multigrid_fp32coarse_fp32dense \
|
||||||
|
Example_hdcg_multigrid_fp32coarse
|
||||||
|
|
||||||
|
Example_pvdagm_multigrid_fp32coarse_SOURCES = Example_pvdagm_multigrid.cc
|
||||||
|
Example_pvdagm_multigrid_fp32coarse_CPPFLAGS = -DCOARSE_SINGLE
|
||||||
|
Example_pvdagm_multigrid_fp32coarse_LDADD = $(top_builddir)/Grid/libGrid.a
|
||||||
|
|
||||||
|
Example_pvdagm_multigrid_fp32coarse_fp32dense_SOURCES = Example_pvdagm_multigrid.cc
|
||||||
|
Example_pvdagm_multigrid_fp32coarse_fp32dense_CPPFLAGS = -DCOARSE_SINGLE -DGRID_DENSE_INVERSE_SINGLE
|
||||||
|
Example_pvdagm_multigrid_fp32coarse_fp32dense_LDADD = $(top_builddir)/Grid/libGrid.a
|
||||||
|
|
||||||
|
Example_hdcg_multigrid_fp32coarse_SOURCES = Example_hdcg_multigrid.cc
|
||||||
|
Example_hdcg_multigrid_fp32coarse_CPPFLAGS = -DCOARSE_SINGLE
|
||||||
|
Example_hdcg_multigrid_fp32coarse_LDADD = $(top_builddir)/Grid/libGrid.a
|
||||||
@@ -21,10 +21,14 @@
|
|||||||
# only code path (2D block-cyclic dense inverse, ring-allgather apply,
|
# only code path (2D block-cyclic dense inverse, ring-allgather apply,
|
||||||
# inverseLU big leaves) and are gone from the environment.
|
# inverseLU big leaves) and are gone from the environment.
|
||||||
#
|
#
|
||||||
# The binary must be built with -DNBASIS=64 to match the subspace file.
|
# The binary is a plain `make` target with the default basis, 60. The
|
||||||
|
# subspace load reads the first 60 vectors of the file.
|
||||||
|
# <FinePrecision> selects the arithmetic of the fine level
|
||||||
|
# INSIDE the preconditioner (fp64 here; the outer Krylov is fp64 and exact
|
||||||
|
# either way). The precision sweep is in pvdagm_mixed_precision.job.
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
root=$HOME/PVdagM/Grid/systems/Frontier
|
root=/lustre/orion/phy157/proj-shared/phy157_dwf/paboyle/MGrewrite/Grid/systems/Frontier
|
||||||
source $root/sourceme-rocm7.2.sh
|
source $root/sourceme-rocm7.2.sh
|
||||||
|
|
||||||
# Paths substituted into the XML below.
|
# Paths substituted into the XML below.
|
||||||
@@ -48,6 +52,8 @@ exec numactl -m \$NUMA -N \$NUMA \$*
|
|||||||
EOF
|
EOF
|
||||||
chmod +x ./select_gpu
|
chmod +x ./select_gpu
|
||||||
|
|
||||||
|
# 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
|
export OMP_NUM_THREADS=7
|
||||||
ulimit -c 0 # no 22 GB GPU core dumps
|
ulimit -c 0 # no 22 GB GPU core dumps
|
||||||
@@ -89,11 +95,13 @@ cat << EOF > params.xml
|
|||||||
<SolveSingleRHS>1</SolveSingleRHS>
|
<SolveSingleRHS>1</SolveSingleRHS>
|
||||||
<MultiGrid>
|
<MultiGrid>
|
||||||
<Setup>
|
<Setup>
|
||||||
<Block><elem>2</elem><elem>2</elem><elem>3</elem><elem>3</elem></Block>
|
<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>
|
<Block2><elem>4</elem><elem>4</elem><elem>2</elem><elem>4</elem></Block2>
|
||||||
<CoarsenBatch>9</CoarsenBatch>
|
<CoarsenBatch>9</CoarsenBatch>
|
||||||
<SubspaceFile>$SUBSPACE</SubspaceFile>
|
<SubspaceFile>$SUBSPACE</SubspaceFile>
|
||||||
<FineSloppyComms>1</FineSloppyComms>
|
<FineSloppyComms>1</FineSloppyComms>
|
||||||
|
<FinePrecision>fp64</FinePrecision>
|
||||||
|
<RetainSubspace>0</RetainSubspace>
|
||||||
</Setup>
|
</Setup>
|
||||||
<FineSmoother>
|
<FineSmoother>
|
||||||
<Shift>0.1</Shift><Nstep>6</Nstep><Mmax>4</Mmax>
|
<Shift>0.1</Shift><Nstep>6</Nstep><Mmax>4</Mmax>
|
||||||
|
|||||||
@@ -40,29 +40,6 @@ using namespace Grid;
|
|||||||
// a second definition here is a duplicate-symbol link error the moment the
|
// a second definition here is a duplicate-symbol link error the moment the
|
||||||
// archive member is pulled in.
|
// archive member is pulled in.
|
||||||
|
|
||||||
///////////////////////
|
|
||||||
// Tells little dirac op to use MdagM as the .Op()
|
|
||||||
///////////////////////
|
|
||||||
template<class Field>
|
|
||||||
class HermOpAdaptor : public LinearOperatorBase<Field>
|
|
||||||
{
|
|
||||||
LinearOperatorBase<Field> & wrapped;
|
|
||||||
public:
|
|
||||||
HermOpAdaptor(LinearOperatorBase<Field> &wrapme) : wrapped(wrapme) {};
|
|
||||||
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){
|
|
||||||
wrapped.HermOp(in,out);
|
|
||||||
}
|
|
||||||
void AdjOp (const Field &in, Field &out){
|
|
||||||
wrapped.HermOp(in,out);
|
|
||||||
}
|
|
||||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ GRID_ASSERT(0); }
|
|
||||||
void HermOp(const Field &in, Field &out){
|
|
||||||
wrapped.HermOp(in,out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
int main (int argc, char ** argv)
|
int main (int argc, char ** argv)
|
||||||
|
|||||||
@@ -33,45 +33,6 @@ Author: Peter Boyle <pboyle@bnl.gov>
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace Grid;
|
using namespace Grid;
|
||||||
|
|
||||||
// Want Op in CoarsenOp to call MatPcDagMatPc
|
|
||||||
template<class Field>
|
|
||||||
class HermOpAdaptor : public LinearOperatorBase<Field>
|
|
||||||
{
|
|
||||||
LinearOperatorBase<Field> & wrapped;
|
|
||||||
public:
|
|
||||||
HermOpAdaptor(LinearOperatorBase<Field> &wrapme) : wrapped(wrapme) {};
|
|
||||||
void Op (const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void HermOp(const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void AdjOp (const Field &in, Field &out){ wrapped.HermOp(in,out); }
|
|
||||||
void OpDiag (const Field &in, Field &out) { GRID_ASSERT(0); }
|
|
||||||
void OpDir (const Field &in, Field &out,int dir,int disp) { GRID_ASSERT(0); }
|
|
||||||
void OpDirAll (const Field &in, std::vector<Field> &out) { GRID_ASSERT(0); };
|
|
||||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ GRID_ASSERT(0); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Field> class CGSmoother : public LinearFunction<Field>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using LinearFunction<Field>::operator();
|
|
||||||
typedef LinearOperatorBase<Field> FineOperator;
|
|
||||||
FineOperator & _SmootherOperator;
|
|
||||||
int iters;
|
|
||||||
CGSmoother(int _iters, FineOperator &SmootherOperator) :
|
|
||||||
_SmootherOperator(SmootherOperator),
|
|
||||||
iters(_iters)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage<<" Mirs smoother order "<<iters<<std::endl;
|
|
||||||
};
|
|
||||||
void operator() (const Field &in, Field &out)
|
|
||||||
{
|
|
||||||
ConjugateGradient<Field> CG(0.0,iters,false); // non-converge is just fine in a smoother
|
|
||||||
|
|
||||||
out=Zero();
|
|
||||||
|
|
||||||
CG(_SmootherOperator,in,out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
int main (int argc, char ** argv)
|
int main (int argc, char ** argv)
|
||||||
{
|
{
|
||||||
@@ -270,15 +231,9 @@ int main (int argc, char ** argv)
|
|||||||
ShiftedHermOpLinearOperator<LatticeFermionD> ShiftedFineHermOp(HermOpEO,MirsShift);
|
ShiftedHermOpLinearOperator<LatticeFermionD> ShiftedFineHermOp(HermOpEO,MirsShift);
|
||||||
CGSmoother<LatticeFermionD> CGsmooth(ord,ShiftedFineHermOp) ;
|
CGSmoother<LatticeFermionD> CGsmooth(ord,ShiftedFineHermOp) ;
|
||||||
|
|
||||||
TwoLevelADEF2mrhs<LatticeFermion,CoarseVector>
|
MrhsADEF2Preconditioner<LatticeFermion,CoarseVector>
|
||||||
HDCGmrhs(1.0e-8, 500,
|
ADEF2(FineHermOp, CGsmooth, HPDSolveMrhs, HPDSolveMrhs, MrhsProjector, MrhsGuesser, CoarseMrhs);
|
||||||
FineHermOp,
|
TwoLevelCGmrhs<LatticeFermion> HDCGmrhs(1.0e-8, 500, FineHermOp, ADEF2, FrbGrid);
|
||||||
CGsmooth,
|
|
||||||
HPDSolveMrhs, // Used in M1
|
|
||||||
HPDSolveMrhs, // Used in Vstart
|
|
||||||
MrhsProjector,
|
|
||||||
MrhsGuesser,
|
|
||||||
CoarseMrhs);
|
|
||||||
|
|
||||||
std::vector<LatticeFermionD> src_mrhs(nrhs,FrbGrid);
|
std::vector<LatticeFermionD> src_mrhs(nrhs,FrbGrid);
|
||||||
std::vector<LatticeFermionD> res_mrhs(nrhs,FrbGrid);
|
std::vector<LatticeFermionD> res_mrhs(nrhs,FrbGrid);
|
||||||
|
|||||||
@@ -113,21 +113,6 @@ void LoadBasis(aggregation &Agg, std::string file)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Want Op in CoarsenOp to call MatPcDagMatPc
|
|
||||||
template<class Field>
|
|
||||||
class HermOpAdaptor : public LinearOperatorBase<Field>
|
|
||||||
{
|
|
||||||
LinearOperatorBase<Field> & wrapped;
|
|
||||||
public:
|
|
||||||
HermOpAdaptor(LinearOperatorBase<Field> &wrapme) : wrapped(wrapme) {};
|
|
||||||
void Op (const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void HermOp(const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void AdjOp (const Field &in, Field &out){ wrapped.HermOp(in,out); }
|
|
||||||
void OpDiag (const Field &in, Field &out) { GRID_ASSERT(0); }
|
|
||||||
void OpDir (const Field &in, Field &out,int dir,int disp) { GRID_ASSERT(0); }
|
|
||||||
void OpDirAll (const Field &in, std::vector<Field> &out) { GRID_ASSERT(0); };
|
|
||||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ GRID_ASSERT(0); }
|
|
||||||
};
|
|
||||||
/*
|
/*
|
||||||
void operator() (const Field &in, Field &out)
|
void operator() (const Field &in, Field &out)
|
||||||
{
|
{
|
||||||
@@ -137,25 +122,6 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
template<class Field> class CGSmoother : public LinearFunction<Field>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using LinearFunction<Field>::operator();
|
|
||||||
typedef LinearOperatorBase<Field> FineOperator;
|
|
||||||
FineOperator & _SmootherOperator;
|
|
||||||
int iters;
|
|
||||||
CGSmoother(int _iters, FineOperator &SmootherOperator) :
|
|
||||||
_SmootherOperator(SmootherOperator),
|
|
||||||
iters(_iters)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage<<" Mirs smoother order "<<iters<<std::endl;
|
|
||||||
};
|
|
||||||
void operator() (const Field &in, Field &out)
|
|
||||||
{
|
|
||||||
ConjugateGradient<Field> CG(0.0,iters,false); // non-converge is just fine in a smoother
|
|
||||||
CG(_SmootherOperator,in,out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
int main (int argc, char ** argv)
|
int main (int argc, char ** argv)
|
||||||
|
|||||||
@@ -155,44 +155,7 @@ void LoadEigenvectors(std::vector<RealD> &eval,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Want Op in CoarsenOp to call MatPcDagMatPc
|
|
||||||
template<class Field>
|
|
||||||
class HermOpAdaptor : public LinearOperatorBase<Field>
|
|
||||||
{
|
|
||||||
LinearOperatorBase<Field> & wrapped;
|
|
||||||
public:
|
|
||||||
HermOpAdaptor(LinearOperatorBase<Field> &wrapme) : wrapped(wrapme) {};
|
|
||||||
void Op (const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void HermOp(const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void AdjOp (const Field &in, Field &out){ wrapped.HermOp(in,out); }
|
|
||||||
void OpDiag (const Field &in, Field &out) { GRID_ASSERT(0); }
|
|
||||||
void OpDir (const Field &in, Field &out,int dir,int disp) { GRID_ASSERT(0); }
|
|
||||||
void OpDirAll (const Field &in, std::vector<Field> &out) { GRID_ASSERT(0); };
|
|
||||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ GRID_ASSERT(0); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Field> class CGSmoother : public LinearFunction<Field>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using LinearFunction<Field>::operator();
|
|
||||||
typedef LinearOperatorBase<Field> FineOperator;
|
|
||||||
FineOperator & _SmootherOperator;
|
|
||||||
int iters;
|
|
||||||
CGSmoother(int _iters, FineOperator &SmootherOperator) :
|
|
||||||
_SmootherOperator(SmootherOperator),
|
|
||||||
iters(_iters)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage<<" Mirs smoother order "<<iters<<std::endl;
|
|
||||||
};
|
|
||||||
void operator() (const Field &in, Field &out)
|
|
||||||
{
|
|
||||||
ConjugateGradient<Field> CG(0.0,iters,false); // non-converge is just fine in a smoother
|
|
||||||
|
|
||||||
out=Zero();
|
|
||||||
|
|
||||||
CG(_SmootherOperator,in,out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
int main (int argc, char ** argv)
|
int main (int argc, char ** argv)
|
||||||
@@ -408,15 +371,9 @@ int main (int argc, char ** argv)
|
|||||||
MrhsGuesser.ImportEigenBasis(evec,eval);
|
MrhsGuesser.ImportEigenBasis(evec,eval);
|
||||||
|
|
||||||
CGSmoother<LatticeFermionD> CGsmooth(Refineord,ShiftedFineHermOp) ;
|
CGSmoother<LatticeFermionD> CGsmooth(Refineord,ShiftedFineHermOp) ;
|
||||||
TwoLevelADEF2mrhs<LatticeFermion,CoarseVector>
|
MrhsADEF2Preconditioner<LatticeFermion,CoarseVector>
|
||||||
HDCGmrhsRefine(RefineTol, 500,
|
HDCGmrhsRefine_ADEF2(RefineFineHermOp, CGsmooth, HPDSolveMrhs, HPDSolveMrhs, MrhsProjector, MrhsGuesser, CoarseMrhs);
|
||||||
RefineFineHermOp,
|
TwoLevelCGmrhs<LatticeFermion> HDCGmrhsRefine(RefineTol, 500, RefineFineHermOp, HDCGmrhsRefine_ADEF2, MrhsProjector.fine_grid);
|
||||||
CGsmooth,
|
|
||||||
HPDSolveMrhs, // Used in M1
|
|
||||||
HPDSolveMrhs, // Used in Vstart
|
|
||||||
MrhsProjector,
|
|
||||||
MrhsGuesser,
|
|
||||||
CoarseMrhs);
|
|
||||||
|
|
||||||
// Reload the first pass aggregates, because we orthogonalised them
|
// Reload the first pass aggregates, because we orthogonalised them
|
||||||
LoadBasis(Aggregates,subspace_file);
|
LoadBasis(Aggregates,subspace_file);
|
||||||
@@ -474,15 +431,9 @@ int main (int argc, char ** argv)
|
|||||||
MrhsProjector.Allocate(nbasis,FrbGrid,Coarse5d);
|
MrhsProjector.Allocate(nbasis,FrbGrid,Coarse5d);
|
||||||
MrhsProjector.ImportBasis(Aggregates.subspace);
|
MrhsProjector.ImportBasis(Aggregates.subspace);
|
||||||
|
|
||||||
TwoLevelADEF2mrhs<LatticeFermion,CoarseVector>
|
MrhsADEF2Preconditioner<LatticeFermion,CoarseVector>
|
||||||
HDCGmrhs(1.0e-8, 500,
|
HDCGmrhs_ADEF2(FineHermOp, CGsmooth, HPDSolveMrhs, HPDSolveMrhs, MrhsProjector, MrhsGuesser, CoarseMrhs);
|
||||||
FineHermOp,
|
TwoLevelCGmrhs<LatticeFermion> HDCGmrhs(1.0e-8, 500, FineHermOp, HDCGmrhs_ADEF2, MrhsProjector.fine_grid);
|
||||||
CGsmooth,
|
|
||||||
HPDSolveMrhs, // Used in M1
|
|
||||||
HPDSolveMrhs, // Used in Vstart
|
|
||||||
MrhsProjector,
|
|
||||||
MrhsGuesser,
|
|
||||||
CoarseMrhs);
|
|
||||||
|
|
||||||
std::vector<LatticeFermionD> src_mrhs(nrhs,FrbGrid);
|
std::vector<LatticeFermionD> src_mrhs(nrhs,FrbGrid);
|
||||||
std::vector<LatticeFermionD> res_mrhs(nrhs,FrbGrid);
|
std::vector<LatticeFermionD> res_mrhs(nrhs,FrbGrid);
|
||||||
|
|||||||
@@ -156,21 +156,6 @@ void LoadEigenvectors(std::vector<RealD> &eval,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Want Op in CoarsenOp to call MatPcDagMatPc
|
|
||||||
template<class Field>
|
|
||||||
class HermOpAdaptor : public LinearOperatorBase<Field>
|
|
||||||
{
|
|
||||||
LinearOperatorBase<Field> & wrapped;
|
|
||||||
public:
|
|
||||||
HermOpAdaptor(LinearOperatorBase<Field> &wrapme) : wrapped(wrapme) {};
|
|
||||||
void Op (const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void HermOp(const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void AdjOp (const Field &in, Field &out){ wrapped.HermOp(in,out); }
|
|
||||||
void OpDiag (const Field &in, Field &out) { GRID_ASSERT(0); }
|
|
||||||
void OpDir (const Field &in, Field &out,int dir,int disp) { GRID_ASSERT(0); }
|
|
||||||
void OpDirAll (const Field &in, std::vector<Field> &out) { GRID_ASSERT(0); };
|
|
||||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ GRID_ASSERT(0); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Field> class FixedCGPolynomial : public LinearFunction<Field>
|
template<class Field> class FixedCGPolynomial : public LinearFunction<Field>
|
||||||
{
|
{
|
||||||
@@ -267,28 +252,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
template<class Field> class CGSmoother : public LinearFunction<Field>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using LinearFunction<Field>::operator();
|
|
||||||
typedef LinearOperatorBase<Field> FineOperator;
|
|
||||||
FineOperator & _SmootherOperator;
|
|
||||||
int iters;
|
|
||||||
CGSmoother(int _iters, FineOperator &SmootherOperator) :
|
|
||||||
_SmootherOperator(SmootherOperator),
|
|
||||||
iters(_iters)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage<<" Mirs smoother order "<<iters<<std::endl;
|
|
||||||
};
|
|
||||||
void operator() (const Field &in, Field &out)
|
|
||||||
{
|
|
||||||
ConjugateGradient<Field> CG(0.0,iters,false); // non-converge is just fine in a smoother
|
|
||||||
|
|
||||||
out=Zero();
|
|
||||||
|
|
||||||
CG(_SmootherOperator,in,out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -615,15 +578,9 @@ int main (int argc, char ** argv)
|
|||||||
std::cout << "**************************************"<<std::endl;
|
std::cout << "**************************************"<<std::endl;
|
||||||
std::cout << "Calling mRHS HDCG"<<std::endl;
|
std::cout << "Calling mRHS HDCG"<<std::endl;
|
||||||
std::cout << "**************************************"<<std::endl;
|
std::cout << "**************************************"<<std::endl;
|
||||||
TwoLevelADEF2mrhs<LatticeFermion,CoarseVector>
|
MrhsADEF2Preconditioner<LatticeFermion,CoarseVector>
|
||||||
HDCGmrhs(1.0e-8, 300,
|
HDCGmrhs_ADEF2(FineHermOp, CGsmooth, HPDSolveMrhs, HPDSolveMrhs, MrhsProjector, MrhsGuesser, CoarseMrhs);
|
||||||
FineHermOp,
|
TwoLevelCGmrhs<LatticeFermion> HDCGmrhs(1.0e-8, 300, FineHermOp, HDCGmrhs_ADEF2, MrhsProjector.fine_grid);
|
||||||
CGsmooth,
|
|
||||||
HPDSolveMrhs, // Used in M1
|
|
||||||
HPDSolveMrhs, // Used in Vstart
|
|
||||||
MrhsProjector,
|
|
||||||
MrhsGuesser,
|
|
||||||
CoarseMrhs);
|
|
||||||
|
|
||||||
std::vector<LatticeFermionD> src_mrhs(nrhs,FrbGrid);
|
std::vector<LatticeFermionD> src_mrhs(nrhs,FrbGrid);
|
||||||
std::vector<LatticeFermionD> res_mrhs(nrhs,FrbGrid);
|
std::vector<LatticeFermionD> res_mrhs(nrhs,FrbGrid);
|
||||||
@@ -667,15 +624,9 @@ int main (int argc, char ** argv)
|
|||||||
|
|
||||||
for(auto tol : tols) {
|
for(auto tol : tols) {
|
||||||
|
|
||||||
TwoLevelADEF2mrhs<LatticeFermion,CoarseVector>
|
MrhsADEF2Preconditioner<LatticeFermion,CoarseVector>
|
||||||
HDCGmrhsSloppy(tol, 500,
|
HDCGmrhsSloppy_ADEF2(FineHermOp, CGsmooth, HPDSolveMrhs, HPDSolveMrhs, MrhsProjector, MrhsGuesser, CoarseMrhs);
|
||||||
FineHermOp,
|
TwoLevelCGmrhs<LatticeFermion> HDCGmrhsSloppy(tol, 500, FineHermOp, HDCGmrhsSloppy_ADEF2, MrhsProjector.fine_grid);
|
||||||
CGsmooth,
|
|
||||||
HPDSolveMrhs, // Used in M1
|
|
||||||
HPDSolveMrhs, // Used in Vstart
|
|
||||||
MrhsProjector,
|
|
||||||
MrhsGuesser,
|
|
||||||
CoarseMrhs);
|
|
||||||
|
|
||||||
// Solve again to 10^-5
|
// Solve again to 10^-5
|
||||||
for(int r=0;r<nrhs;r++){
|
for(int r=0;r<nrhs;r++){
|
||||||
|
|||||||
@@ -156,21 +156,6 @@ void LoadEigenvectors(std::vector<RealD> &eval,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Want Op in CoarsenOp to call MatPcDagMatPc
|
|
||||||
template<class Field>
|
|
||||||
class HermOpAdaptor : public LinearOperatorBase<Field>
|
|
||||||
{
|
|
||||||
LinearOperatorBase<Field> & wrapped;
|
|
||||||
public:
|
|
||||||
HermOpAdaptor(LinearOperatorBase<Field> &wrapme) : wrapped(wrapme) {};
|
|
||||||
void Op (const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void HermOp(const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void AdjOp (const Field &in, Field &out){ wrapped.HermOp(in,out); }
|
|
||||||
void OpDiag (const Field &in, Field &out) { GRID_ASSERT(0); }
|
|
||||||
void OpDir (const Field &in, Field &out,int dir,int disp) { GRID_ASSERT(0); }
|
|
||||||
void OpDirAll (const Field &in, std::vector<Field> &out) { GRID_ASSERT(0); };
|
|
||||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ GRID_ASSERT(0); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Field> class FixedCGPolynomial : public LinearFunction<Field>
|
template<class Field> class FixedCGPolynomial : public LinearFunction<Field>
|
||||||
{
|
{
|
||||||
@@ -267,28 +252,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
template<class Field> class CGSmoother : public LinearFunction<Field>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using LinearFunction<Field>::operator();
|
|
||||||
typedef LinearOperatorBase<Field> FineOperator;
|
|
||||||
FineOperator & _SmootherOperator;
|
|
||||||
int iters;
|
|
||||||
CGSmoother(int _iters, FineOperator &SmootherOperator) :
|
|
||||||
_SmootherOperator(SmootherOperator),
|
|
||||||
iters(_iters)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage<<" Mirs smoother order "<<iters<<std::endl;
|
|
||||||
};
|
|
||||||
void operator() (const Field &in, Field &out)
|
|
||||||
{
|
|
||||||
ConjugateGradient<Field> CG(0.0,iters,false); // non-converge is just fine in a smoother
|
|
||||||
|
|
||||||
out=Zero();
|
|
||||||
|
|
||||||
CG(_SmootherOperator,in,out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -614,15 +577,9 @@ int main (int argc, char ** argv)
|
|||||||
std::cout << "**************************************"<<std::endl;
|
std::cout << "**************************************"<<std::endl;
|
||||||
std::cout << "Calling mRHS HDCG"<<std::endl;
|
std::cout << "Calling mRHS HDCG"<<std::endl;
|
||||||
std::cout << "**************************************"<<std::endl;
|
std::cout << "**************************************"<<std::endl;
|
||||||
TwoLevelADEF2mrhs<LatticeFermion,CoarseVector>
|
MrhsADEF2Preconditioner<LatticeFermion,CoarseVector>
|
||||||
HDCGmrhs(1.0e-8, 300,
|
HDCGmrhs_ADEF2(FineHermOp, CGsmooth, HPDSolveMrhs, HPDSolveMrhs, MrhsProjector, MrhsGuesser, CoarseMrhs);
|
||||||
FineHermOp,
|
TwoLevelCGmrhs<LatticeFermion> HDCGmrhs(1.0e-8, 300, FineHermOp, HDCGmrhs_ADEF2, MrhsProjector.fine_grid);
|
||||||
CGsmooth,
|
|
||||||
HPDSolveMrhs, // Used in M1
|
|
||||||
HPDSolveMrhs, // Used in Vstart
|
|
||||||
MrhsProjector,
|
|
||||||
MrhsGuesser,
|
|
||||||
CoarseMrhs);
|
|
||||||
|
|
||||||
std::vector<LatticeFermionD> src_mrhs(nrhs,FrbGrid);
|
std::vector<LatticeFermionD> src_mrhs(nrhs,FrbGrid);
|
||||||
std::vector<LatticeFermionD> res_mrhs(nrhs,FrbGrid);
|
std::vector<LatticeFermionD> res_mrhs(nrhs,FrbGrid);
|
||||||
@@ -647,15 +604,9 @@ int main (int argc, char ** argv)
|
|||||||
|
|
||||||
for(auto tol : tols) {
|
for(auto tol : tols) {
|
||||||
|
|
||||||
TwoLevelADEF2mrhs<LatticeFermion,CoarseVector>
|
MrhsADEF2Preconditioner<LatticeFermion,CoarseVector>
|
||||||
HDCGmrhsSloppy(tol, 500,
|
HDCGmrhsSloppy_ADEF2(FineHermOp, CGsmooth, HPDSolveMrhs, HPDSolveMrhs, MrhsProjector, MrhsGuesser, CoarseMrhs);
|
||||||
FineHermOp,
|
TwoLevelCGmrhs<LatticeFermion> HDCGmrhsSloppy(tol, 500, FineHermOp, HDCGmrhsSloppy_ADEF2, MrhsProjector.fine_grid);
|
||||||
CGsmooth,
|
|
||||||
HPDSolveMrhs, // Used in M1
|
|
||||||
HPDSolveMrhs, // Used in Vstart
|
|
||||||
MrhsProjector,
|
|
||||||
MrhsGuesser,
|
|
||||||
CoarseMrhs);
|
|
||||||
|
|
||||||
// Solve again to 10^-5
|
// Solve again to 10^-5
|
||||||
for(int r=0;r<nrhs;r++){
|
for(int r=0;r<nrhs;r++){
|
||||||
|
|||||||
@@ -127,44 +127,7 @@ void LoadEigenvectors(std::vector<RealD> &eval,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Want Op in CoarsenOp to call MatPcDagMatPc
|
|
||||||
template<class Field>
|
|
||||||
class HermOpAdaptor : public LinearOperatorBase<Field>
|
|
||||||
{
|
|
||||||
LinearOperatorBase<Field> & wrapped;
|
|
||||||
public:
|
|
||||||
HermOpAdaptor(LinearOperatorBase<Field> &wrapme) : wrapped(wrapme) {};
|
|
||||||
void Op (const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void HermOp(const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void AdjOp (const Field &in, Field &out){ wrapped.HermOp(in,out); }
|
|
||||||
void OpDiag (const Field &in, Field &out) { GRID_ASSERT(0); }
|
|
||||||
void OpDir (const Field &in, Field &out,int dir,int disp) { GRID_ASSERT(0); }
|
|
||||||
void OpDirAll (const Field &in, std::vector<Field> &out) { GRID_ASSERT(0); };
|
|
||||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ GRID_ASSERT(0); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Field> class CGSmoother : public LinearFunction<Field>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using LinearFunction<Field>::operator();
|
|
||||||
typedef LinearOperatorBase<Field> FineOperator;
|
|
||||||
FineOperator & _SmootherOperator;
|
|
||||||
int iters;
|
|
||||||
CGSmoother(int _iters, FineOperator &SmootherOperator) :
|
|
||||||
_SmootherOperator(SmootherOperator),
|
|
||||||
iters(_iters)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage<<" Mirs smoother order "<<iters<<std::endl;
|
|
||||||
};
|
|
||||||
void operator() (const Field &in, Field &out)
|
|
||||||
{
|
|
||||||
ConjugateGradient<Field> CG(0.0,iters,false); // non-converge is just fine in a smoother
|
|
||||||
|
|
||||||
out=Zero();
|
|
||||||
|
|
||||||
CG(_SmootherOperator,in,out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
int main (int argc, char ** argv)
|
int main (int argc, char ** argv)
|
||||||
|
|||||||
@@ -100,44 +100,7 @@ void LoadEigenvectors(std::vector<RealD> &eval,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Want Op in CoarsenOp to call MatPcDagMatPc
|
|
||||||
template<class Field>
|
|
||||||
class HermOpAdaptor : public LinearOperatorBase<Field>
|
|
||||||
{
|
|
||||||
LinearOperatorBase<Field> & wrapped;
|
|
||||||
public:
|
|
||||||
HermOpAdaptor(LinearOperatorBase<Field> &wrapme) : wrapped(wrapme) {};
|
|
||||||
void Op (const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void HermOp(const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void AdjOp (const Field &in, Field &out){ wrapped.HermOp(in,out); }
|
|
||||||
void OpDiag (const Field &in, Field &out) { GRID_ASSERT(0); }
|
|
||||||
void OpDir (const Field &in, Field &out,int dir,int disp) { GRID_ASSERT(0); }
|
|
||||||
void OpDirAll (const Field &in, std::vector<Field> &out) { GRID_ASSERT(0); };
|
|
||||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ GRID_ASSERT(0); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Field> class CGSmoother : public LinearFunction<Field>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using LinearFunction<Field>::operator();
|
|
||||||
typedef LinearOperatorBase<Field> FineOperator;
|
|
||||||
FineOperator & _SmootherOperator;
|
|
||||||
int iters;
|
|
||||||
CGSmoother(int _iters, FineOperator &SmootherOperator) :
|
|
||||||
_SmootherOperator(SmootherOperator),
|
|
||||||
iters(_iters)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage<<" Mirs smoother order "<<iters<<std::endl;
|
|
||||||
};
|
|
||||||
void operator() (const Field &in, Field &out)
|
|
||||||
{
|
|
||||||
ConjugateGradient<Field> CG(0.0,iters,false); // non-converge is just fine in a smoother
|
|
||||||
|
|
||||||
out=Zero();
|
|
||||||
|
|
||||||
CG(_SmootherOperator,in,out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
int main (int argc, char ** argv)
|
int main (int argc, char ** argv)
|
||||||
@@ -348,15 +311,9 @@ int main (int argc, char ** argv)
|
|||||||
MrhsProjector.Allocate(nbasis,FrbGrid,Coarse5d);
|
MrhsProjector.Allocate(nbasis,FrbGrid,Coarse5d);
|
||||||
MrhsProjector.ImportBasis(Aggregates.subspace);
|
MrhsProjector.ImportBasis(Aggregates.subspace);
|
||||||
|
|
||||||
TwoLevelADEF2mrhs<LatticeFermion,CoarseVector>
|
MrhsADEF2Preconditioner<LatticeFermion,CoarseVector>
|
||||||
HDCGmrhs(1.0e-8, 500,
|
HDCGmrhs_ADEF2(FineHermOp, CGsmooth, HPDSolveMrhs, HPDSolveMrhs, MrhsProjector, MrhsGuesser, CoarseMrhs);
|
||||||
FineHermOp,
|
TwoLevelCGmrhs<LatticeFermion> HDCGmrhs(1.0e-8, 500, FineHermOp, HDCGmrhs_ADEF2, MrhsProjector.fine_grid);
|
||||||
CGsmooth,
|
|
||||||
HPDSolveMrhs, // Used in M1
|
|
||||||
HPDSolveMrhs, // Used in Vstart
|
|
||||||
MrhsProjector,
|
|
||||||
MrhsGuesser,
|
|
||||||
CoarseMrhs);
|
|
||||||
|
|
||||||
std::vector<LatticeFermionD> src_mrhs(nrhs,FrbGrid);
|
std::vector<LatticeFermionD> src_mrhs(nrhs,FrbGrid);
|
||||||
std::vector<LatticeFermionD> res_mrhs(nrhs,FrbGrid);
|
std::vector<LatticeFermionD> res_mrhs(nrhs,FrbGrid);
|
||||||
|
|||||||
@@ -100,44 +100,7 @@ void LoadEigenvectors(std::vector<RealD> &eval,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Want Op in CoarsenOp to call MatPcDagMatPc
|
|
||||||
template<class Field>
|
|
||||||
class HermOpAdaptor : public LinearOperatorBase<Field>
|
|
||||||
{
|
|
||||||
LinearOperatorBase<Field> & wrapped;
|
|
||||||
public:
|
|
||||||
HermOpAdaptor(LinearOperatorBase<Field> &wrapme) : wrapped(wrapme) {};
|
|
||||||
void Op (const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void HermOp(const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
||||||
void AdjOp (const Field &in, Field &out){ wrapped.HermOp(in,out); }
|
|
||||||
void OpDiag (const Field &in, Field &out) { GRID_ASSERT(0); }
|
|
||||||
void OpDir (const Field &in, Field &out,int dir,int disp) { GRID_ASSERT(0); }
|
|
||||||
void OpDirAll (const Field &in, std::vector<Field> &out) { GRID_ASSERT(0); };
|
|
||||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ GRID_ASSERT(0); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Field> class CGSmoother : public LinearFunction<Field>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using LinearFunction<Field>::operator();
|
|
||||||
typedef LinearOperatorBase<Field> FineOperator;
|
|
||||||
FineOperator & _SmootherOperator;
|
|
||||||
int iters;
|
|
||||||
CGSmoother(int _iters, FineOperator &SmootherOperator) :
|
|
||||||
_SmootherOperator(SmootherOperator),
|
|
||||||
iters(_iters)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage<<" Mirs smoother order "<<iters<<std::endl;
|
|
||||||
};
|
|
||||||
void operator() (const Field &in, Field &out)
|
|
||||||
{
|
|
||||||
ConjugateGradient<Field> CG(0.0,iters,false); // non-converge is just fine in a smoother
|
|
||||||
|
|
||||||
out=Zero();
|
|
||||||
|
|
||||||
CG(_SmootherOperator,in,out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
int main (int argc, char ** argv)
|
int main (int argc, char ** argv)
|
||||||
@@ -356,15 +319,9 @@ int main (int argc, char ** argv)
|
|||||||
CGSmoother<LatticeFermionD> CGsmooth(ord,ShiftedFineHermOp) ;
|
CGSmoother<LatticeFermionD> CGsmooth(ord,ShiftedFineHermOp) ;
|
||||||
|
|
||||||
|
|
||||||
TwoLevelADEF2mrhs<LatticeFermion,CoarseVector>
|
MrhsADEF2Preconditioner<LatticeFermion,CoarseVector>
|
||||||
HDCGmrhs(1.0e-8, 500,
|
HDCGmrhs_ADEF2(FineHermOp, CGsmooth, HPDSolveMrhs, HPDSolveMrhs, MrhsProjector, MrhsGuesser, CoarseMrhs);
|
||||||
FineHermOp,
|
TwoLevelCGmrhs<LatticeFermion> HDCGmrhs(1.0e-8, 500, FineHermOp, HDCGmrhs_ADEF2, MrhsProjector.fine_grid);
|
||||||
CGsmooth,
|
|
||||||
HPDSolveMrhs, // Used in M1
|
|
||||||
HPDSolveMrhs, // Used in Vstart
|
|
||||||
MrhsProjector,
|
|
||||||
MrhsGuesser,
|
|
||||||
CoarseMrhs);
|
|
||||||
|
|
||||||
std::vector<LatticeFermionD> src_mrhs(nrhs,FrbGrid);
|
std::vector<LatticeFermionD> src_mrhs(nrhs,FrbGrid);
|
||||||
std::vector<LatticeFermionD> res_mrhs(nrhs,FrbGrid);
|
std::vector<LatticeFermionD> res_mrhs(nrhs,FrbGrid);
|
||||||
|
|||||||
Reference in new issue
Block a user