/************************************************************************************* Grid physics library, www.github.com/paboyle/Grid Source file: ./tests/debug/Test_schur2d.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 BlockCyclicSchurInverse -- stage 3 of the 2D // distributed dense inverse. CPU build under mpirun: // // mpirun -n 1 ./Test_schur2d --grid 8.8.8.8 --mpi 1.1.1.1 // mpirun -n 2 ./Test_schur2d --grid 8.8.8.8 --mpi 1.1.1.2 // mpirun -n 3 ./Test_schur2d --grid 8.8.8.12 --mpi 1.1.1.3 // mpirun -n 4 ./Test_schur2d --grid 8.8.8.8 --mpi 1.1.1.4 // // Sweeps all process-grid factorisations of P and a battery of (N,nb) // including ragged trailing blocks, a single-leaf matrix (nblocks==1), // and nb=3 with many blocks. The matrices are diagonally dominant -- // the recursion does not pivot, exactly like the 1D implementation, and // the test respects that contract. // // T1 : certificate max|A . Ainv - I| with the product computed by the // (independently validated) distributed SUMMA on an untouched copy. // T2 : element-wise against a host Gauss-Jordan reference inverse // (partial pivoting, fp64). // T3 : repeated inversion bitwise identical -- the P2P determinism // property, which a collective-reduce implementation cannot offer. ////////////////////////////////////////////////////////////////////////////// #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); } // Diagonally dominant test matrix: no pivoting required at any depth. 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;jProcessorCount(); 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}, {8,8}, {33,3}, {40,5} }; BlockCyclicSchurInverse RSI; BlockCyclicSumma SUMMA; std::cout << GridLogMessage << "BlockCyclicSchurInverse regression: P=" << P << ", " << grids.size() << " process grids, " << cfgs.size() << " layouts" << std::endl; //////////////////////////////////////////////////////////////////////// // T1 + T2, one pass: invert, certify with distributed SUMMA on an // untouched copy, and compare against the host reference inverse. //////////////////////////////////////////////////////////////////////// { bool okC = true, okR = true; double worstC = 0.0, worstR = 0.0; for(auto &g : grids){ for(auto &c : cfgs){ int64_t N = c.N; std::vector Ag, Ref, Ainv, Cert; MakeMatrix(Ag, N, 12); HostInverse(Ag, Ref, N); BlockCyclicMatrix A (grid,N,c.nb,g.first,g.second); BlockCyclicMatrix A0(grid,N,c.nb,g.first,g.second); BlockCyclicMatrix Ce(grid,N,c.nb,g.first,g.second); A.ImportGlobal(Ag); A0.ImportGlobal(Ag); RSI.Invert(A); // in place: A now holds Ainv // certificate: Ce = A0 . Ainv, distributed SUMMA.Multiply(ComplexD(1.0,0.0),A0,A,ComplexD(0.0,0.0),Ce, 0,N,0,N,0,N); Ce.ExportGlobal(Cert); double dc = 0.0; for(int64_t j=0;j 1.0e-10 ) okC = false; // reference: element-wise, scaled by the largest inverse entry A.ExportGlobal(Ainv); double mxref = 0.0, dr = 0.0; for(uint64_t i=0;i 1.0e-9 ) okR = false; } } Report("T1 certificate max|A.Ainv - I|, all grids x layouts", okC, "worst "+std::to_string(worstC)); Report("T2 vs host Gauss-Jordan reference (relative)", okR, "worst "+std::to_string(worstR)); } //////////////////////////////////////////////////////////////////////// // T3 : determinism. Same import, two inversions, bitwise comparison. //////////////////////////////////////////////////////////////////////// { bool ok = true; for(auto &g : grids){ int64_t N = 30, nb = 7; std::vector Ag, X1, X2; MakeMatrix(Ag, N, 13); BlockCyclicMatrix A(grid,N,nb,g.first,g.second); A.ImportGlobal(Ag); RSI.Invert(A); A.ExportGlobal(X1); A.ImportGlobal(Ag); RSI.Invert(A); A.ExportGlobal(X2); for(uint64_t i=0;iGlobalSum(f); if ( f && !failures ) std::cout << GridLogMessage << " ** failures on OTHER ranks: " << f << " **" << std::endl; failures = (int)f; } std::cout << GridLogMessage << (failures ? "Test_schur2d: FAILURES" : "Test_schur2d: ALL PASS") << std::endl; Grid_finalize(); return failures ? 1 : 0; }