/************************************************************************************* Grid physics library, www.github.com/paboyle/Grid Source file: ./tests/debug/Test_summa.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 BlockCyclicSumma -- stage 2 of the 2D distributed // dense inverse. CPU build under mpirun: // // mpirun -n 1 ./Test_summa --grid 16.16.16.32 --mpi 1.1.1.1 // mpirun -n 2 ./Test_summa --grid 16.16.16.32 --mpi 1.1.1.2 // mpirun -n 3 ./Test_summa --grid 16.16.16.48 --mpi 1.1.1.3 // mpirun -n 4 ./Test_summa --grid 16.16.16.32 --mpi 1.1.1.4 // // Every stage sweeps all process-grid factorisations of P (including the // degenerate 1xP and Px1 rings) and a battery of (N,nb) with ragged // trailing blocks, nb>N, and more processes than blocks. Reference is a // host triple loop on the replicated global matrix; tolerance 1e-11 on // max element error (the distributed and reference summation orders // differ, so bitwise equality is not expected AGAINST THE REFERENCE -- // but IS expected between repeated distributed runs, which is T5). // // T1 : C = A.B, full range, all layouts x all grids. // T2 : C = beta C + alpha A.B, preloaded C, complex alpha/beta. // T3 : windowed products, block-aligned sub-ranges incl. ragged N end. // T4 : in-place windows: same matrix as A, B and C on disjoint windows. // T5 : determinism: repeated product bitwise identical. ////////////////////////////////////////////////////////////////////////////// #include #include using namespace Grid; static int failures = 0; // Portable |z|: ComplexD is std::complex on CPU builds and thrust::complex // under HIP, where std::abs does not resolve (same trap RecursiveSchurInverse // documents at FrobNorm2Local). Member real()/imag() work on both. static double Cabs(const ComplexD &z) { double re = z.real(), im = z.imag(); return std::sqrt(re*re + im*im); } 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++; } // Deterministic pseudo-random fill: identical on every rank, no RNG state. 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); } // Host reference: C[i0:i1,j0:j1] = beta C + alpha A[i-,k-].B[k-,j-] static void RefGemm(ComplexD alpha, const std::vector &A, const std::vector &B, ComplexD beta, std::vector &C, int64_t N, int64_t i0,int64_t i1,int64_t j0,int64_t j1,int64_t k0,int64_t k1) { for(int64_t j=j0;j &X, const std::vector &Y) { double m = 0.0; for(uint64_t i=0;iProcessorCount(); // All factorisations Pr*Pc == P: squarest, plus both degenerate rings. 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}, {8,8}, {30,7}, {3,4}, {33,3} }; const double tol = 1.0e-11; BlockCyclicSumma SUMMA; std::cout << GridLogMessage << "BlockCyclicSumma regression: P=" << P << ", " << grids.size() << " process grids, " << cfgs.size() << " layouts" << std::endl; //////////////////////////////////////////////////////////////////////// // T1 : full product, alpha=1 beta=0. //////////////////////////////////////////////////////////////////////// { bool ok = true; double worst = 0.0; for(auto &g : grids){ for(auto &c : cfgs){ int64_t N = c.N; std::vector Ag(N*N), Bg(N*N), Cg(N*N, ComplexD(0.0,0.0)), Cd; for(int64_t j=0;j tol ) ok = false; } } Report("T1 full C = A.B, all grids x layouts", ok, "max err "+std::to_string(worst)); } //////////////////////////////////////////////////////////////////////// // T2 : beta C + alpha A.B with preloaded C, complex coefficients. //////////////////////////////////////////////////////////////////////// { bool ok = true; double worst = 0.0; ComplexD alpha(0.5,-0.25), beta(-1.0,0.75); for(auto &g : grids){ for(auto &c : cfgs){ int64_t N = c.N; std::vector Ag(N*N), Bg(N*N), Cg(N*N), Cd; for(int64_t j=0;j tol ) ok = false; } } Report("T2 C = beta C + alpha A.B, preloaded C", ok, "max err "+std::to_string(worst)); } //////////////////////////////////////////////////////////////////////// // T3 : windowed products. Block-aligned sub-ranges, including the // ragged top end g1==N, on a layout with a partial trailing block. //////////////////////////////////////////////////////////////////////// { bool ok = true; double worst = 0.0; int64_t N = 26, nb = 4; // 6 full blocks + ragged 2 struct Rng { int64_t i0,i1,j0,j1,k0,k1; }; std::vector rngs = { { 0,8, 8,16, 16,24 }, // interior windows { 4,12, 0,4, 12,26 }, // ragged k end { 16,26, 20,26, 0,8 }, // ragged i and j ends { 0,4, 0,4, 4,8 }, // minimal one-block windows { 0,26, 0,26, 8,12 }, // full ij, thin k }; for(auto &g : grids){ std::vector Ag(N*N), Bg(N*N), Cg(N*N), Cd; for(int64_t j=0;j tol ) ok = false; } } Report("T3 windowed products, ragged ends", ok, "max err "+std::to_string(worst)); } //////////////////////////////////////////////////////////////////////// // T4 : in-place windows of ONE matrix, exactly the stage-3 usage: // M[0:b, 2b:3b] = M[0:b, b:2b] . M[b:2b, 2b:3b] // C window disjoint from both operand windows (asserted in Multiply). //////////////////////////////////////////////////////////////////////// { bool ok = true; double worst = 0.0; int64_t nb = 4, N = 4*nb; for(auto &g : grids){ std::vector Mg(N*N), Md, Mr; for(int64_t j=0;j tol ) ok = false; } Report("T4 in-place disjoint windows (stage-3 usage)", ok, "max err "+std::to_string(worst)); } //////////////////////////////////////////////////////////////////////// // T5 : determinism. The summation order is fixed (ascending k-block), // so repeated distributed products must agree BITWISE -- the property // the P2P design buys and a collective reduce cannot promise. //////////////////////////////////////////////////////////////////////// { bool ok = true; for(auto &g : grids){ int64_t N = 30, nb = 7; std::vector Ag(N*N), Bg(N*N), C1, C2; for(int64_t j=0;jGlobalSum(f); if ( f && !failures ) std::cout << GridLogMessage << " ** failures on OTHER ranks: " << f << " **" << std::endl; failures = (int)f; } std::cout << GridLogMessage << (failures ? "Test_summa: FAILURES" : "Test_summa: ALL PASS") << std::endl; Grid_finalize(); return failures ? 1 : 0; }