From cc54c90ddc7499ebac7af6bfe15f220fca0d2b31 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Wed, 19 Aug 2026 19:54:18 -0400 Subject: [PATCH] sComplex simd, also commit coarse space as lex lattice tests --- tests/Test_simd.cc | 192 +++++++++++++- tests/debug/Test_coarse_v2.cc | 252 +++++++++++++++++++ tests/debug/Test_coarse_v2_coarsen.cc | 242 ++++++++++++++++++ tests/debug/Test_mrhs_blockproject_import.cc | 224 +++++++++++++++++ tests/debug/Test_mrhs_promotion.cc | 157 ++++++++++++ 5 files changed, 1055 insertions(+), 12 deletions(-) create mode 100644 tests/debug/Test_coarse_v2.cc create mode 100644 tests/debug/Test_coarse_v2_coarsen.cc create mode 100644 tests/debug/Test_mrhs_blockproject_import.cc create mode 100644 tests/debug/Test_mrhs_promotion.cc diff --git a/tests/Test_simd.cc b/tests/Test_simd.cc index d8cd1dd6b..e6f8dd0b8 100644 --- a/tests/Test_simd.cc +++ b/tests/Test_simd.cc @@ -560,29 +560,198 @@ int main (int argc, char ** argv) auto latt_size = GridDefaultLatt(); auto simd_layout = GridDefaultSimd(4,vComplex::Nsimd()); auto mpi_layout = GridDefaultMpi(); - + Coordinate simd1_layout({1,1,1,1}); + { std::cout << " Constructing Test({1,2,3,4,5,6}) " << std::endl; Coordinate Test({1,2,3,4,5,6}); std::cout << " Test({1,2,3,4,5,6}) = " << Test < seeds({1,2,3,4}); // Insist that operations on random scalars gives // identical results to on vectors. + std::cout << GridLogMessage <<"==================================="<< std::endl; + std::cout << GridLogMessage <<"Testing sRealF "<(funcPlus()); + Tester(funcMinus()); + Tester(funcTimes()); + Tester(funcDivide()); + Tester(funcAdj()); + Tester(funcConj()); + Tester(funcInnerProduct()); + ReductionTester(funcReduce()); + std::cout<(funcPermute(i)); + } + std::cout<(funcExchange(i)); + } + + std::cout<(funcRotate(r)); + } + + + + std::cout << GridLogMessage <<"==================================="<< std::endl; + std::cout << GridLogMessage <<"Testing sRealD "<(funcPlus()); + Tester(funcMinus()); + Tester(funcTimes()); + Tester(funcDivide()); + Tester(funcAdj()); + Tester(funcConj()); + Tester(funcInnerProduct()); + ReductionTester(funcReduce()); + + + std::cout<(funcPermute(i)); + } + + std::cout<(funcExchange(i)); + } + + std::cout<(funcRotate(r)); + } + + + + std::cout << GridLogMessage <<"==================================="<< std::endl; + std::cout << GridLogMessage <<"Testing sComplexF "<(funcTimesI()); + Tester(funcTimesMinusI()); + Tester(funcPlus()); + Tester(funcMinus()); + Tester(funcTimes()); + Tester(funcConj()); + Tester(funcAdj()); + Tester(funcReal()); + Tester(funcImag()); + Tester(funcInnerProduct()); + ReductionTester(funcReduce()); + + + std::cout<(funcPermute(i)); + } + + + std::cout<(funcExchange(i)); + } + + + std::cout<(funcRotate(r)); + } + + std::cout<(funcTimesI()); + Tester(funcTimesMinusI()); + Tester(funcPlus()); + Tester(funcMinus()); + Tester(funcTimes()); + Tester(funcConj()); + Tester(funcAdj()); + Tester(funcReal()); + Tester(funcImag()); + + Tester(funcInnerProduct()); + ReductionTester(funcReduce()); + + std::cout << GridLogMessage + << "===================================" << std::endl; + std::cout << GridLogMessage << "Testing sComplexD permutes " << std::endl; + std::cout << GridLogMessage + << "===================================" << std::endl; + + // Log2 iteration + for (int i = 0; (1 << i) < sComplexD::Nsimd(); i++) { + PermTester(funcPermute(i)); + } + + + std::cout<(funcExchange(i)); + } + + + std::cout<(funcRotate(r)); + } + + std::cout<(funcReduce()); + + + + + std::cout << GridLogMessage <<"==================================="<< std::endl; std::cout << GridLogMessage <<"Testing vRealF "<(funcRotate(r)); } - std::cout << GridLogMessage <<"==================================="<< std::endl; std::cout << GridLogMessage <<"Testing vRealD "< + + 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 */ + +// +// MultiGeneralCoarsenedOperatorV2 against the existing mrhs coarse operator. +// +// V1 is constructed on the D+1 grid as now; V2 on the D dimensional grid, +// with SetGrid() adopting the caller owned D+1 grid and building its padded +// cell and neighbour table from the D dimensional stencil, with the Nrhs +// factor multiplied in. +// +// Both are given identical matrix elements, so any difference in the apply is +// the restructured neighbour table. The same geometry object is passed to +// both: V1 adds one to skip for the rhs direction, V2 uses it as is on the D +// dimensional grid, so both describe the same stencil over the D dimensions. +// +#include + +using namespace Grid; + +const int nbasis = 8; + +typedef vSpinColourVector FineObj; +typedef sTComplexD CComplexT; // unvectorised coarse space + +typedef MultiGeneralCoarsenedMatrix MrhsV1; +typedef MultiGeneralCoarsenedOperatorV2 MrhsV2; + +//////////////////////////////////////////////////////////////////////// +// Identical random matrix elements into both operators +//////////////////////////////////////////////////////////////////////// +template +void SeedMatrixElements(OpA &A,OpB &B,int npoint,GridSerialRNG &sRNG) +{ + typedef typename OpA::calcMatrix calcMatrix; + + for(int p=0;p host(sites); + + ComplexD *w = (ComplexD *)&host[0]; + int64_t words = sites*sizeof(calcMatrix)/sizeof(ComplexD); + for(int64_t i=0;i +RealD MatrixChecksum(Op &O,int npoint) +{ + typedef typename Op::calcMatrix calcMatrix; + RealD sum=0.0; + for(int p=0;p host(sites); + acceleratorCopyFromDevice(&O.BLAS_A[p][0],&host[0],sites*sizeof(calcMatrix)); + ComplexD *w = (ComplexD *)&host[0]; + int64_t words = sites*sizeof(calcMatrix)/sizeof(ComplexD); + for(int64_t i=0;iNsimd() << std::endl; + + //////////////////////////////////////////////// + // One geometry object for both, and one D+1 grid + // owned here and shared by both operators: fields + // conform only across a shared grid object. + //////////////////////////////////////////////// + NextToNearestStencilGeometry4D geom(CoarseD); + + Coordinate mlatt(1,nrhs), msimd(1,1), mmpi(1,1); + for(int d=0;dNsimd() << std::endl; + + std::cout << GridLogMessage << "npoint V1 " << OpV1.geom.npoint + << " npoint V2 " << OpV2.geom.npoint << std::endl; + GRID_ASSERT(OpV1.geom.npoint == OpV2.geom.npoint); + + int npoint = OpV1.geom.npoint; + + //////////////////////////////////////////////// + // Identical matrix elements + //////////////////////////////////////////////// + GridSerialRNG sRNG; sRNG.SeedFixedIntegers(std::vector({7,8,9,10})); + SeedMatrixElements(OpV1,OpV2,npoint,sRNG); + + RealD ckV1 = MatrixChecksum(OpV1,npoint); + RealD ckV2 = MatrixChecksum(OpV2,npoint); + std::cout << GridLogMessage << "matrix element checksum V1 " << ckV1 + << " V2 " << ckV2 << std::endl; + GRID_ASSERT( ckV1 == ckV2 ); + + //////////////////////////////////////////////// + // Same input, compare the applies + //////////////////////////////////////////////// + typedef MrhsV1::CoarseVector CoarseVector; + // RNG on the D dimensional grid fills any D+1 field: the rhs direction is + // undistributed and divides cleanly. One RNG serves every Nrhs. + GridParallelRNG pRNG(CoarseD); pRNG.SeedFixedIntegers(std::vector({1,2,3,4})); + + CoarseVector in (CoarseMulti); random(pRNG,in); + CoarseVector out1(CoarseMulti); + CoarseVector out2(CoarseMulti); + CoarseVector err (CoarseMulti); + + OpV1.M(in,out1); + OpV2.M(in,out2); + + err = out1 - out2; + std::cout << GridLogMessage << "|V1 out|^2 = " << norm2(out1) + << " |V2 out|^2 = " << norm2(out2) << std::endl; + std::cout << GridLogMessage << "|V1 - V2|^2 = " << norm2(err) << std::endl; + GRID_ASSERT( norm2(out1) > 0.0 ); + GRID_ASSERT( norm2(err) == 0.0 ); + + //////////////////////////////////////////////// + // SetGrid is idempotent on pointer identity + //////////////////////////////////////////////// + OpV2.SetGrid(CoarseMulti); + GRID_ASSERT( MatrixChecksum(OpV2,npoint) == ckV2 ); + OpV2.M(in,out2); + err = out1 - out2; + GRID_ASSERT( norm2(err) == 0.0 ); + std::cout << GridLogMessage << "SetGrid idempotent on identity" << std::endl; + + //////////////////////////////////////////////// + // Move to a different Nrhs and back. The matrix + // elements are Nrhs independent and must survive + // both the release and the rebuild. + //////////////////////////////////////////////// + // Nrhs 1 is the single RHS case through the multiRHS path, and each slice + // of the Nrhs 4 apply must come back unchanged. + OpV2.M(in,out2); + for(int nr=2;nr>=1;nr--){ + Coordinate latt2(1,nr), simd2(1,1), mpi2(1,1); + for(int d=0;d 2 -> 1 -> release -> 4, |V1 - V2|^2 = " + << norm2(err) << std::endl; + GRID_ASSERT( norm2(err) == 0.0 ); + + std::cout << GridLogMessage << "Test_coarse_v2: ALL PASS" << std::endl; + + Grid_finalize(); +} diff --git a/tests/debug/Test_coarse_v2_coarsen.cc b/tests/debug/Test_coarse_v2_coarsen.cc new file mode 100644 index 000000000..6eb2a4dd7 --- /dev/null +++ b/tests/debug/Test_coarse_v2_coarsen.cc @@ -0,0 +1,242 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./tests/debug/Test_coarse_v2_coarsen.cc + + Copyright (C) 2026 + +Author: Peter Boyle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory +*************************************************************************************/ +/* END LEGAL */ + +// +// Coarsen the same fine operator two ways and compare the matrix elements. +// +// V1 : existing mrhs CoarsenOperator, vectorised coarse space matched to +// the fine SIMD layout, single RHS fine applications +// V2 : D+1 CoarsenOperator, unvectorised sComplexD coarse space, the batch +// of phased basis vectors carried in the rhs direction and applied +// through MrhsPromotedOperator +// +// BLAS_A is written by GridtoBLAS in lSite order, which does not depend on +// the SIMD layout, so the two are directly comparable. +// +#include + +using namespace Grid; + +const int nbasis = 8; +const int batch = 9; + +typedef vSpinColourVector FineObj; +typedef vTComplex CComplexV; // vectorised coarse space, V1 reference +typedef sTComplexD CComplexS; // unvectorised coarse space, V2 + +typedef MultiGeneralCoarsenedMatrix MrhsV1; +typedef MultiGeneralCoarsenedOperatorV2 MrhsV2; + +template +void ReadMatrix(Op &O,int npoint,std::vector > &host) +{ + typedef typename Op::calcMatrix calcMatrix; + host.resize(npoint); + for(int p=0;pNsimd() + << " coarse V1 "<Nsimd() + << " coarse V2 "<Nsimd()<({1,2,3,4})); + LatticeGaugeFieldD Umu(FineGrid); SU::HotConfiguration(pRNG,Umu); + + RealD mass = 0.1; + WilsonFermionD Dw(Umu,*FineGrid,*FrbGrid,mass); + MdagMLinearOperator HermOp(Dw); + + //////////////////////////////////////////////// + // One random subspace, two copies: CoarsenOperator orthogonalises in place + //////////////////////////////////////////////// + Aggregation Subspace(CoarseV,FineGrid,0); + std::vector subspace(nbasis,FineGrid); + for(int i=0;i MrhsHermOp(HermOp,FineGrid,batch); + + std::cout << GridLogMessage << "V2 CoarsenOperator (D+1)" << std::endl; + OpV2.CoarsenOperator(MrhsHermOp,FineGridMulti,subspace,CoarseS); + + //////////////////////////////////////////////// + // Compare matrix elements + //////////////////////////////////////////////// + int npoint = OpV1.geom.npoint; + GRID_ASSERT(npoint == OpV2.geom.npoint); + typedef MrhsV1::calcMatrix calcMatrix; + std::vector > A1,A2; + ReadMatrix(OpV1,npoint,A1); + ReadMatrix(OpV2,npoint,A2); + + RealD num=0.0, den=0.0; + for(int p=0;p > A3; + ReadMatrix(OpV2s,npoint,A3); + + RealD nums=0.0; + for(int p=0;p + + 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 */ + +// +// ImportFineGridMrhsVectors / ExportCoarseGridMrhsVectors against the vector +// of single RHS routines they replace. The BLAS buffers must come out +// identical, so the mrhs path is a pure removal of the unpack and repack. +// +#include + +using namespace Grid; + +const int nbasis = 8; +const int nrhs = 4; + +typedef LatticeFermionD FineField; +typedef iVector CoarseSiteObj; // unvectorised coarse space +typedef Lattice CoarseField; + +template +RealD BufDiff(deviceVector &a,deviceVector &b) +{ + GRID_ASSERT(a.size()==b.size()); + std::vector ha(a.size()),hb(b.size()); + acceleratorCopyFromDevice(&a[0],&ha[0],a.size()*sizeof(T)); + acceleratorCopyFromDevice(&b[0],&hb[0],b.size()*sizeof(T)); + RealD num=0.0; + for(int64_t i=0;i +RealD BufNorm(deviceVector &a) +{ + std::vector ha(a.size()); + acceleratorCopyFromDevice(&a[0],&ha[0],a.size()*sizeof(T)); + RealD num=0.0; + for(int64_t i=0;iNsimd()<<" coarse Nsimd "<Nsimd()<({1,2,3,4})); + + MultiRHSBlockProject Projector; + Projector.Allocate(nbasis,FineGrid,CoarseGrid); + + /////////////////////////////////////////////////////// + // Same data as nrhs single RHS fields and as one D+1 + /////////////////////////////////////////////////////// + std::vector vecs(nrhs,FineGrid); + FineField mrhs(FineMulti); + for(int r=0;rlSites()*Projector.words*nrhs; + deviceVector Fa(fsz), Fb(fsz); + + Projector.ImportFineGridVectors (vecs,Fa); + Projector.ImportFineGridMrhsVectors(mrhs,Fb); + + RealD fn = BufNorm(Fa); + RealD fd = BufDiff(Fa,Fb); + std::cout << GridLogMessage << "fine import |vector|^2 = " << fn + << " |vector - mrhs|^2 = " << fd << std::endl; + GRID_ASSERT( fn > 0.0 ); + GRID_ASSERT( fd == 0.0 ); + + /////////////////////////////////////////////////////// + // Coarse export: fill the BLAS buffer, read it back + // both ways, and compare the resulting fields + /////////////////////////////////////////////////////// + int64_t csz = CoarseGrid->lSites()*nbasis*nrhs; + deviceVector Cbuf(csz); + { + std::vector host(csz); + GridSerialRNG sRNG; sRNG.SeedFixedIntegers(std::vector({9,8,7,6})); + for(int64_t i=0;i cvecs(nrhs,CoarseGrid); + CoarseField cmrhs(CoarseMulti); + + Projector.ExportCoarseGridVectors (cvecs,Cbuf); + Projector.ExportCoarseGridMrhsVectors(cmrhs,Cbuf); + + RealD cn=0.0, cd=0.0; + for(int r=0;rvec|^2 = " << ref << std::endl; + std::cout << GridLogMessage << " mrhs->vec diff " << d_mv + << " vec->mrhs diff " << d_vm + << " mrhs->mrhs diff " << d_mm << std::endl; + GRID_ASSERT( ref > 0.0 ); + GRID_ASSERT( d_mv == 0.0 ); + GRID_ASSERT( d_vm == 0.0 ); + GRID_ASSERT( d_mm == 0.0 ); + + /////////////////////////////////////////////////////// + // Promote must agree across orderings too + /////////////////////////////////////////////////////// + std::vector fvv(nrhs,FineGrid); + FineField fmm(FineMulti); + Projector.blockPromote(fvv,cvv); + Projector.blockPromote(fmm,cmm); + + RealD pref=0.0, pdiff=0.0; + for(int r=0;r 0.0 ); + GRID_ASSERT( pdiff == 0.0 ); + } + + std::cout << GridLogMessage << "Test_mrhs_blockproject_import: ALL PASS" << std::endl; + + Grid_finalize(); +} diff --git a/tests/debug/Test_mrhs_promotion.cc b/tests/debug/Test_mrhs_promotion.cc new file mode 100644 index 000000000..80f05e80a --- /dev/null +++ b/tests/debug/Test_mrhs_promotion.cc @@ -0,0 +1,157 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./tests/debug/Test_mrhs_promotion.cc + + Copyright (C) 2026 + +Author: Peter Boyle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory +*************************************************************************************/ +/* END LEGAL */ + +// +// MrhsPromotedOperator: a D dimensional operator presented as D+1 with Nrhs. +// +// Each output slice must equal the D dimensional operator applied to the +// corresponding input slice, bit for bit -- the promotion moves data, it does +// not change arithmetic. Slices are independent random fields so a mistake in +// the slice indexing cannot pass. +// +#include + +using namespace Grid; + +int main (int argc, char ** argv) +{ + Grid_init(&argc,&argv); + + const int Nrhs = 4; + + Coordinate latt = GridDefaultLatt(); + Coordinate simd = GridDefaultSimd(Nd,vComplexD::Nsimd()); + Coordinate mpi = GridDefaultMpi(); + + GridCartesian *UGrid = new GridCartesian(latt,simd,mpi); + GridRedBlackCartesian *UrbGrid= SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid); + + // D+1 grid with Nrhs in dimension 0 + GridCartesian *RGrid = SpaceTimeGrid::makeFiveDimGrid(Nrhs,UGrid); + + std::cout << GridLogMessage << "Nrhs = " << Nrhs + << " low grid " << UGrid->_ndimension << "d" + << " high grid " << RGrid->_ndimension << "d" << std::endl; + + GridParallelRNG pRNG(UGrid); pRNG.SeedFixedIntegers(std::vector({1,2,3,4})); + + LatticeGaugeFieldD Umu(UGrid); SU::HotConfiguration(pRNG,Umu); + + RealD mass = 0.1; + WilsonFermionD Dw(Umu,*UGrid,*UrbGrid,mass); + + MdagMLinearOperator HermOp(Dw); + + MrhsPromotedOperator MrhsOp(HermOp,UGrid,Nrhs); + + ////////////////////////////////////////////////// + // Independent random source on each slice + ////////////////////////////////////////////////// + LatticeFermionD hi_in (RGrid); + LatticeFermionD hi_out(RGrid); + std::vector lo_in (Nrhs,UGrid); + std::vector lo_ref(Nrhs,UGrid); + + for(int r=0;r 0.0 ); + } else { + std::cout << GridLogMessage << "untouched rhs["<