/************************************************************************************* Grid physics library, www.github.com/paboyle/Grid Source file: ./tests/debug/Test_schur2d_redist.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. See the full license in the file "LICENSE" in the top level distribution directory *************************************************************************************/ /* END LEGAL */ ////////////////////////////////////////////////////////////////////////////// // Regression gate for BlockCyclicRedistribute and the full stage-4 path // // 1D rank-major rows -> block cyclic -> Invert -> back to 1D rows // // which is exactly what DENSE_SCHUR2D runs inside DenseCoarseMatrix. // CPU build under mpirun at n = 1,2,3,4. // // T1 : RowsToCyclic against a direct ImportGlobal of the same matrix -- // BITWISE (pure data movement, no arithmetic). // T2 : round trip rows -> 2D -> rows -- BITWISE, uniform AND non-uniform // rowStart, layouts with ragged trailing blocks. // T3 : full pipeline inverse against a host Gauss-Jordan reference. // T4 : CROSS-IMPLEMENTATION: the same matrix inverted by the 1D // RecursiveSchurInverse and by the 2D pipeline; results compared // element-wise. Two independent implementations, two independent // decompositions, one answer. ////////////////////////////////////////////////////////////////////////////// #include #include #include #include using namespace Grid; static int failures = 0; static void Report(const std::string &name, bool pass, const std::string &detail="") { std::cout << GridLogMessage << " " << name << (pass ? " PASS" : " ** FAIL **"); if ( detail.size() ) std::cout << " " << detail; std::cout << std::endl; if ( !pass ) failures++; } static ComplexD Fill(int64_t i, int64_t j, int salt) { double x = std::sin(0.7*i + 1.3*j + 0.31*salt); double y = std::cos(1.9*i - 0.4*j + 0.77*salt); return ComplexD(x,y); } static void MakeMatrix(std::vector &A, int64_t N, int salt) { A.resize((uint64_t)N*N); for(int64_t j=0;j A, std::vector &X, int64_t N) { X.assign((uint64_t)N*N, ComplexD(0.0,0.0)); for(int64_t i=0;i mx ){ mx=std::abs(A[r+c*N]); piv=r; } GRID_ASSERT( mx > 0.0 ); if ( piv != c ) for(int64_t j=0;j MakeRowStart(int64_t N, int P, int lopsided) { std::vector t(P+1); t[0]=0; for(int r=0;r1 ) n = std::min(N, base+extra+3); if ( r==P-1 ) n = N - t[r]; // remainder } t[r+1] = std::min(N, t[r]+n); } t[P]=N; return t; } int main(int argc, char **argv) { Grid_init(&argc, &argv); GridCartesian *grid = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), GridDefaultSimd(Nd, vComplexD::Nsimd()), GridDefaultMpi()); const int P = grid->ProcessorCount(); const int me = grid->ThisRank(); std::vector> grids; for(int r=1;r<=P;r++) if ( P%r==0 ) grids.push_back({r,P/r}); struct Cfg { int64_t N; int64_t nb; }; std::vector cfgs = { {24,4}, {26,4}, {17,5}, {30,7}, {33,3} }; std::cout << GridLogMessage << "BlockCyclicRedistribute regression: P=" << P << std::endl; //////////////////////////////////////////////////////////////////////// // T1 + T2 : distribution correctness and round trip, bitwise. //////////////////////////////////////////////////////////////////////// { bool ok1 = true, ok2 = true; for(int lop=0; lop<2; lop++){ for(auto &g : grids){ for(auto &c : cfgs){ int64_t N = c.N; std::vector rowStart = MakeRowStart(N,P,lop); int64_t myrows = rowStart[me+1]-rowStart[me]; std::vector Ag; MakeMatrix(Ag, N, 21); // my 1D rows, column major ld = myrows std::vector h((uint64_t)std::max(myrows,1)*N); for(int64_t j=0;j rows1d(h.size()); acceleratorCopyToDevice(&h[0], &rows1d[0], h.size()*sizeof(ComplexD)); BlockCyclicMatrix A(grid,N,c.nb,g.first,g.second); BlockCyclicMatrix R(grid,N,c.nb,g.first,g.second); BlockCyclicRedistribute::RowsToCyclic(grid,rowStart,&rows1d[0],myrows,A); R.ImportGlobal(Ag); // T1: bitwise against direct import { std::vector x((uint64_t)A.layout.mloc*A.layout.nloc); std::vector y(x.size()); if ( x.size() ){ acceleratorCopyFromDevice(&A.data[0], &x[0], x.size()*sizeof(ComplexD)); acceleratorCopyFromDevice(&R.data[0], &y[0], y.size()*sizeof(ComplexD)); } for(uint64_t i=0;i back(h.size()); std::vector hb(h.size(), ComplexD(0.0,0.0)); acceleratorCopyToDevice(&hb[0], &back[0], hb.size()*sizeof(ComplexD)); BlockCyclicRedistribute::CyclicToRows(grid,rowStart,A,&back[0],myrows); acceleratorCopyFromDevice(&back[0], &hb[0], hb.size()*sizeof(ComplexD)); for(int64_t j=0;j2D->rows, bitwise, incl. lopsided rowStart", ok2); } //////////////////////////////////////////////////////////////////////// // T3 + T4 : the DENSE_SCHUR2D pipeline against the host reference and // against the INDEPENDENT 1D RecursiveSchurInverse. //////////////////////////////////////////////////////////////////////// { bool ok3 = true, ok4 = true; double worst3 = 0.0, worst4 = 0.0; BlockCyclicSchurInverse RSI2; for(auto &g : grids){ for(auto &c : cfgs){ int64_t N = c.N; std::vector rowStart = MakeRowStart(N,P,0); int64_t myrows = rowStart[me+1]-rowStart[me]; std::vector Ag, Ref; MakeMatrix(Ag, N, 22); HostInverse(Ag, Ref, N); std::vector h((uint64_t)std::max(myrows,1)*N); for(int64_t j=0;j cyclic -> invert -> rows ---- std::vector h2d(h.size()); { deviceVector rows1d(h.size()); acceleratorCopyToDevice(&h[0], &rows1d[0], h.size()*sizeof(ComplexD)); BlockCyclicMatrix A(grid,N,c.nb,g.first,g.second); BlockCyclicRedistribute::RowsToCyclic(grid,rowStart,&rows1d[0],myrows,A); RSI2.Invert(A); BlockCyclicRedistribute::CyclicToRows(grid,rowStart,A,&rows1d[0],myrows); acceleratorCopyFromDevice(&rows1d[0], &h2d[0], h2d.size()*sizeof(ComplexD)); } for(int64_t j=0;j 1.0e-9 ) ok3 = false; } // ---- 1D RecursiveSchurInverse on the same matrix ---- { BlockRows Ar; Ar.Resize(myrows, N); acceleratorCopyToDevice(&h[0], &Ar.data[0], h.size()*sizeof(ComplexD)); std::vector rs = rowStart; RecursiveSchurInverse RSI1(grid, N, rs, 1<<20); RSI1.Invert(Ar); std::vector h1d(h.size()); acceleratorCopyFromDevice(&Ar.data[0], &h1d[0], h1d.size()*sizeof(ComplexD)); for(int64_t j=0;j 1.0e-9 ) ok4 = false; } } } } Report("T3 2D pipeline vs host reference", ok3, "worst "+std::to_string(worst3)); Report("T4 2D pipeline vs 1D RecursiveSchurInverse", ok4, "worst "+std::to_string(worst4)); } { uint64_t f = failures; grid->GlobalSum(f); if ( f && !failures ) std::cout << GridLogMessage << " ** failures on OTHER ranks: " << f << " **" << std::endl; failures = (int)f; } std::cout << GridLogMessage << (failures ? "Test_schur2d_redist: FAILURES" : "Test_schur2d_redist: ALL PASS") << std::endl; Grid_finalize(); return failures ? 1 : 0; }