/************************************************************************************* Grid physics library, www.github.com/paboyle/Grid Source file: ./tests/debug/Test_batched_blas.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 */ // // Unit tests for the blas-layer dense primitives: // // T1 : GridBLASInverse::inverseBatched (ComplexF and ComplexD) -- // || A A^{-1} - I ||_max over a random well-conditioned batch. // On a CPU build this exercises the Eigen reference (the semantic // oracle); the SAME binary exercises rocSOLVER/cuBLAS/oneMKL on a // device build. // T2 : explicit-leading-dimension gemmBatched == SOFTWARE SPLIT-K in // miniature. Y = op(A,T).X computed (a) in one compact batch=1 call // and (b) as NK K-chunks by POINTER OFFSET with lda = full K, partials // reduced on the host. (a)==(b) validates the strided overload AND // the production dense-slab apply pattern (arXiv:2409.03904 fig 11). // // Hard asserts throughout: regression gate for Grid/algorithms/blas. // #include #include #include using namespace std; using namespace Grid; int main (int argc, char ** argv) { Grid_init(&argc,&argv); GridBLAS blas; GridBLASInverse inverse; std::mt19937 rng(12345); std::uniform_real_distribution dist(-1.0,1.0); //////////////////////////////////////////////////////////////// // T1a : batched inversion, ComplexF //////////////////////////////////////////////////////////////// { const int64_t N = 64; const int batch = 4; const uint64_t elems = (uint64_t)batch*N*N; // Random diagonally-dominant batch: A = N*I + R, |R_ij| <= 1 std::vector Ahost(elems); for(uint64_t i=0;i Adev(elems); // gets inverted in place deviceVector Aorig(elems); // untouched copy for the residual deviceVector Cdev(elems); acceleratorCopyToDevice(&Ahost[0],&Adev[0], elems*sizeof(ComplexF)); acceleratorCopyToDevice(&Ahost[0],&Aorig[0],elems*sizeof(ComplexF)); deviceVector Ap(batch); deviceVector Op(batch); deviceVector Cp(batch); std::vector ptr_h(batch); for(int b=0;b Chost(elems); acceleratorCopyFromDevice(&Cdev[0],&Chost[0],elems*sizeof(ComplexF)); double maxdev = 0.0; for(int b=0;b Ahost(elems); for(uint64_t i=0;i Adev(elems); deviceVector Aorig(elems); deviceVector Cdev(elems); acceleratorCopyToDevice(&Ahost[0],&Adev[0], elems*sizeof(ComplexD)); acceleratorCopyToDevice(&Ahost[0],&Aorig[0],elems*sizeof(ComplexD)); deviceVector Ap(batch); deviceVector Op(batch); deviceVector Cp(batch); std::vector ptr_h(batch); for(int b=0;b Chost(elems); acceleratorCopyFromDevice(&Cdev[0],&Chost[0],elems*sizeof(ComplexD)); double maxdev = 0.0; for(int b=0;b Ahost((uint64_t)K*nrows); std::vector Xhost((uint64_t)K*nrhs); for(auto &z : Ahost) z = ComplexF(dist(rng),dist(rng)); for(auto &z : Xhost) z = ComplexF(dist(rng),dist(rng)); deviceVector Adev(Ahost.size()); deviceVector Xdev(Xhost.size()); deviceVector Yref((uint64_t)nrows*nrhs); deviceVector Ypart((uint64_t)NK*nrows*nrhs); acceleratorCopyToDevice(&Ahost[0],&Adev[0],Ahost.size()*sizeof(ComplexF)); acceleratorCopyToDevice(&Xhost[0],&Xdev[0],Xhost.size()*sizeof(ComplexF)); ComplexF one (1.0,0.0); ComplexF zero(0.0,0.0); // (a) reference: one compact batch=1 call (compact lda == K for OP_T) { deviceVector Ap(1), Xp(1), Yp(1); std::vector h(1); h[0]=&Adev[0]; acceleratorCopyToDevice(&h[0],&Ap[0],sizeof(ComplexF*)); h[0]=&Xdev[0]; acceleratorCopyToDevice(&h[0],&Xp[0],sizeof(ComplexF*)); h[0]=&Yref[0]; acceleratorCopyToDevice(&h[0],&Yp[0],sizeof(ComplexF*)); blas.gemmBatched(GridBLAS_OP_T,GridBLAS_OP_N, nrows,nrhs,K, one, Ap, Xp, zero, Yp); blas.synchronise(); } // (b) split-K: NK chunk-pointers into the SAME allocations, lda/ldb = K { deviceVector Ap(NK), Xp(NK), Yp(NK); std::vector h(NK); for(int j=0;j Yref_h((uint64_t)nrows*nrhs); std::vector Ypart_h((uint64_t)NK*nrows*nrhs); acceleratorCopyFromDevice(&Yref[0], &Yref_h[0], Yref_h.size()*sizeof(ComplexF)); acceleratorCopyFromDevice(&Ypart[0],&Ypart_h[0],Ypart_h.size()*sizeof(ComplexF)); double maxdev = 0.0; double maxval = 0.0; for(int i=0;i Ahost((uint64_t)K*nrows); std::vector Xhost((uint64_t)K*nrhs); for(auto &z : Ahost) z = ComplexD(dist(rng),dist(rng)); for(auto &z : Xhost) z = ComplexD(dist(rng),dist(rng)); deviceVector Adev(Ahost.size()); deviceVector Xdev(Xhost.size()); deviceVector Yref((uint64_t)nrows*nrhs); deviceVector Ypart((uint64_t)NK*nrows*nrhs); acceleratorCopyToDevice(&Ahost[0],&Adev[0],Ahost.size()*sizeof(ComplexD)); acceleratorCopyToDevice(&Xhost[0],&Xdev[0],Xhost.size()*sizeof(ComplexD)); ComplexD one (1.0,0.0); ComplexD zero(0.0,0.0); // (a) reference: one compact batch=1 call (compact lda == K for OP_T) { deviceVector Ap(1), Xp(1), Yp(1); std::vector h(1); h[0]=&Adev[0]; acceleratorCopyToDevice(&h[0],&Ap[0],sizeof(ComplexD*)); h[0]=&Xdev[0]; acceleratorCopyToDevice(&h[0],&Xp[0],sizeof(ComplexD*)); h[0]=&Yref[0]; acceleratorCopyToDevice(&h[0],&Yp[0],sizeof(ComplexD*)); blas.gemmBatched(GridBLAS_OP_T,GridBLAS_OP_N, nrows,nrhs,K, one, Ap, Xp, zero, Yp); blas.synchronise(); } // (b) split-K: NK chunk-pointers into the SAME allocations, lda/ldb = K { deviceVector Ap(NK), Xp(NK), Yp(NK); std::vector h(NK); for(int j=0;j Yref_h((uint64_t)nrows*nrhs); std::vector Ypart_h((uint64_t)NK*nrows*nrhs); acceleratorCopyFromDevice(&Yref[0], &Yref_h[0], Yref_h.size()*sizeof(ComplexD)); acceleratorCopyFromDevice(&Ypart[0],&Ypart_h[0],Ypart_h.size()*sizeof(ComplexD)); double maxdev = 0.0; double maxval = 0.0; for(int i=0;i