From bc79bb2ca52af2dbd9ab612c38e248f6c9dd96f1 Mon Sep 17 00:00:00 2001 From: Jiacheng Xu <13862180016@163.com> Date: Fri, 23 Oct 2026 09:54:06 +0800 Subject: [PATCH 1/9] Fix scaled accumulation in GPU contractions Apply fac_add_Ds while merging downloaded GPU contributions through the existing tensor accumulation helper. Remove the unused GPU result map and locks; the result merge runs outside the OpenMP packing regions. Add regression coverage for signed, zero and fractional coefficients, seeded outputs, repeated calls and combined contraction labels. --- include/RI/ri/LRI-cal_loop3_GPU.hpp | 45 ++++++-------- include/RI/ri/gpu/GPU_Data_Output.h | 9 ++- unittests/ri/LRI-loop3-test.hpp | 93 ++++++++++++++++++++++++++++- 3 files changed, 115 insertions(+), 32 deletions(-) diff --git a/include/RI/ri/LRI-cal_loop3_GPU.hpp b/include/RI/ri/LRI-cal_loop3_GPU.hpp index d2b220f..30d7323 100644 --- a/include/RI/ri/LRI-cal_loop3_GPU.hpp +++ b/include/RI/ri/LRI-cal_loop3_GPU.hpp @@ -53,11 +53,7 @@ void LRI::cal_loop3_GPU( GPU_Backend::Context gpu_context(this->mpi_comm); GPU_Backend::Queue &queue = gpu_context.queue(); - std::map lock_Ds_result_add_map = LRI_Cal_Aux::init_lock_result(labels, this->parallel->list_A, Ds_result); - { - std::map>> Ds_result_thread; - for(const Label::ab_ab &label : labels) { const std::vector list_Aa01_Da = LRI_Cal_Aux::filter_list_map( this->parallel->list_A[Label_Tools::to_Aab_Aab(label)].a01, data_wrapper(Label::ab::a).Ds_ab ); @@ -202,7 +198,7 @@ void LRI::cal_loop3_GPU( rDs_output.h_array.size(), queue); GPU_Backend::sync(queue); - rDs_output.download(Ds_result, queue); + rDs_output.download(Ds_result, queue, fac_add_Ds); } break; // end case a0b0_a1b1 case Label::ab_ab::a0b1_a1b0: @@ -338,7 +334,7 @@ void LRI::cal_loop3_GPU( rDs_output.h_array.size(), queue); GPU_Backend::sync(queue); - rDs_output.download(Ds_result, queue); + rDs_output.download(Ds_result, queue, fac_add_Ds); } break; // end case a0b1_a1b0 // Aab_Aab::a01b01_a01b2 @@ -480,7 +476,7 @@ void LRI::cal_loop3_GPU( rDs_output.h_array.size(), rDs_tmp_segments_size, queue); GPU_Backend::sync(queue); - rDs_output.download(Ds_result, queue); + rDs_output.download(Ds_result, queue, fac_add_Ds); } break; // end case a0b0_a1b2 case Label::ab_ab::a0b1_a1b2: @@ -620,7 +616,7 @@ void LRI::cal_loop3_GPU( rDs_output.h_array.size(), rDs_tmp_segments_size, queue); GPU_Backend::sync(queue); - rDs_output.download(Ds_result, queue); + rDs_output.download(Ds_result, queue, fac_add_Ds); } break; // end case a0b1_a1b2 case Label::ab_ab::a0b2_a1b0: @@ -760,7 +756,7 @@ void LRI::cal_loop3_GPU( rDs_output.h_array.size(), rDs_tmp_segments_size, queue); GPU_Backend::sync(queue); - rDs_output.download(Ds_result, queue); + rDs_output.download(Ds_result, queue, fac_add_Ds); } break; // end case a0b2_a1b0 case Label::ab_ab::a0b2_a1b1: @@ -900,7 +896,7 @@ void LRI::cal_loop3_GPU( rDs_output.h_array.size(), rDs_tmp_segments_size, queue); GPU_Backend::sync(queue); - rDs_output.download(Ds_result, queue); + rDs_output.download(Ds_result, queue, fac_add_Ds); } break; // end case a0b2_a1b1 // Aab_Aab::a01b01_a2b01 @@ -1042,7 +1038,7 @@ void LRI::cal_loop3_GPU( rDs_output.h_array.size(), rDs_tmp_segments_size, queue); GPU_Backend::sync(queue); - rDs_output.download(Ds_result, queue); + rDs_output.download(Ds_result, queue, fac_add_Ds); } break; // end case a0b0_a2b1 case Label::ab_ab::a0b1_a2b0: @@ -1182,7 +1178,7 @@ void LRI::cal_loop3_GPU( rDs_output.h_array.size(), rDs_tmp_segments_size, queue); GPU_Backend::sync(queue); - rDs_output.download(Ds_result, queue); + rDs_output.download(Ds_result, queue, fac_add_Ds); } break; // end case a0b1_a2b0 case Label::ab_ab::a1b0_a2b1: @@ -1322,7 +1318,7 @@ void LRI::cal_loop3_GPU( rDs_output.h_array.size(), rDs_tmp_segments_size, queue); GPU_Backend::sync(queue); - rDs_output.download(Ds_result, queue); + rDs_output.download(Ds_result, queue, fac_add_Ds); } break; // end case a1b0_a2b1 case Label::ab_ab::a1b1_a2b0: @@ -1462,7 +1458,7 @@ void LRI::cal_loop3_GPU( rDs_output.h_array.size(), rDs_tmp_segments_size, queue); GPU_Backend::sync(queue); - rDs_output.download(Ds_result, queue); + rDs_output.download(Ds_result, queue, fac_add_Ds); } break; // end case a1b1_a2b0 // Aab_Aab::a01b01_a2b2 @@ -1603,7 +1599,7 @@ void LRI::cal_loop3_GPU( rDs_output.h_array.size(), rDs_tmp_segments_size, queue); GPU_Backend::sync(queue); - rDs_output.download(Ds_result, queue); + rDs_output.download(Ds_result, queue, fac_add_Ds); } break; // end case a0b0_a2b2 case Label::ab_ab::a0b1_a2b2: @@ -1742,7 +1738,7 @@ void LRI::cal_loop3_GPU( rDs_output.h_array.size(), rDs_tmp_segments_size, queue); GPU_Backend::sync(queue); - rDs_output.download(Ds_result, queue); + rDs_output.download(Ds_result, queue, fac_add_Ds); } break; // end case a0b1_a2b2 case Label::ab_ab::a1b0_a2b2: @@ -1881,7 +1877,7 @@ void LRI::cal_loop3_GPU( rDs_output.h_array.size(), rDs_tmp_segments_size, queue); GPU_Backend::sync(queue); - rDs_output.download(Ds_result, queue); + rDs_output.download(Ds_result, queue, fac_add_Ds); } break; // end case a1b0_a2b2 case Label::ab_ab::a1b1_a2b2: @@ -2020,7 +2016,7 @@ void LRI::cal_loop3_GPU( rDs_output.h_array.size(), rDs_tmp_segments_size, queue); GPU_Backend::sync(queue); - rDs_output.download(Ds_result, queue); + rDs_output.download(Ds_result, queue, fac_add_Ds); } break; // end case a1b1_a2b2 // Aab_Aab::a01b2_a2b01 @@ -2186,7 +2182,7 @@ void LRI::cal_loop3_GPU( rDs_output.h_array.size(), queue); GPU_Backend::sync(queue); - rDs_output.download(Ds_result, queue); + rDs_output.download(Ds_result, queue, fac_add_Ds); } break; // end case a1b2_a2b1 case Label::ab_ab::a0b2_a2b0: @@ -2350,7 +2346,7 @@ void LRI::cal_loop3_GPU( rDs_output.h_array.size(), queue); GPU_Backend::sync(queue); - rDs_output.download(Ds_result, queue); + rDs_output.download(Ds_result, queue, fac_add_Ds); } break; // end case a0b2_a2b0 case Label::ab_ab::a0b2_a2b1: @@ -2514,7 +2510,7 @@ void LRI::cal_loop3_GPU( rDs_output.h_array.size(), queue); GPU_Backend::sync(queue); - rDs_output.download(Ds_result, queue); + rDs_output.download(Ds_result, queue, fac_add_Ds); } break; // end case a0b2_a2b1 case Label::ab_ab::a1b2_a2b0: @@ -2678,7 +2674,7 @@ void LRI::cal_loop3_GPU( rDs_output.h_array.size(), queue); GPU_Backend::sync(queue); - rDs_output.download(Ds_result, queue); + rDs_output.download(Ds_result, queue, fac_add_Ds); } break; // end case a1b2_a2b0 default: @@ -2686,11 +2682,8 @@ void LRI::cal_loop3_GPU( } // end switch(label) } // end for label - // 最后结束前必须等待直至加成功 - LRI_Cal_Aux::add_Ds_omp_wait_map(Ds_result_thread, Ds_result, lock_Ds_result_add_map, fac_add_Ds); - } // end #pragma omp parallel + } - LRI_Cal_Aux::destroy_lock(lock_Ds_result_add_map); LRI_Cal_Aux::destroy_Ds_result(Ds_result); #ifdef __MKL_RI mkl_set_num_threads(mkl_threads); diff --git a/include/RI/ri/gpu/GPU_Data_Output.h b/include/RI/ri/gpu/GPU_Data_Output.h index c47001f..4c9056c 100644 --- a/include/RI/ri/gpu/GPU_Data_Output.h +++ b/include/RI/ri/gpu/GPU_Data_Output.h @@ -9,6 +9,7 @@ #include "../../global/gpu/GPU_Backend.h" #include "GPU_Data_Pack.h" +#include "../LRI_Cal_Aux.h" #include #include @@ -59,7 +60,8 @@ class Output void download( std::map>> &Ds_result, - GPU_Backend::Queue queue) const + GPU_Backend::Queue queue, + const double fac_add_Ds = 1.0) const { std::vector h_data(this->totalSize); GPU_Backend::download( @@ -74,10 +76,7 @@ class Output download_data_impl(D_output, h_data, pack, Same_data_layout()); Tensor &D_result = Ds_result[ptrList_A.first][ptrList_B.first]; - if(D_result.empty()) - D_result = std::move(D_output); - else - D_result += D_output; + LRI_Cal_Aux::add_Ds(std::move(D_output), D_result, fac_add_Ds); } } } diff --git a/unittests/ri/LRI-loop3-test.hpp b/unittests/ri/LRI-loop3-test.hpp index 3aae9f1..faf38e2 100644 --- a/unittests/ri/LRI-loop3-test.hpp +++ b/unittests/ri/LRI-loop3-test.hpp @@ -13,6 +13,11 @@ #include"unittests/print_stl.h" #include"unittests/global/Tensor-test.h" +#include +#include +#include +#include +#include #include #include #include @@ -396,6 +401,89 @@ namespace LRI_Loop3_Test } + template + void test_loop3_accumulation(const RI::LRI_Cal_Mode cal_mode) + { + using LRI = RI::LRI; + using Treal = typename LRI::Tdata_real; + using T_Ds = std::map>>; + const typename LRI::TAC key = {0,{0}}; + const auto labels = RI::Global_Func::to_vector(RI::Label::array_ab_ab); + const auto make_tensor = [](const RI::Shape_Vector &shape) + { + RI::Tensor D(shape); + for(std::size_t i=0; isize(); ++i) + (*D.data)[i] = RI::Global_Func::convert( + std::complex(0.5+0.07*i, 0.1+0.03*i)); + return D; + }; + + LRI lri; + lri.set_parallel(MPI_COMM_SELF, {{0,{0.0}}}, {{{1.0}}}, {1}, labels); + for(const RI::Label::ab label : RI::Label::array_ab) + { + const bool coefficient = label==RI::Label::ab::a || label==RI::Label::ab::b; + T_Ds Ds; + Ds[0][key] = make_tensor(coefficient ? RI::Shape_Vector{2,2,2} : RI::Shape_Vector{2,2}); + lri.set_tensors_map2(Ds, {label}); + } + + const RI::Tensor seed = make_tensor({2,2}); + const Treal tolerance = 128*std::numeric_limits::epsilon(); + std::vector> label_groups; + for(const RI::Label::ab_ab label : labels) + label_groups.push_back({label}); + label_groups.push_back(labels); + + for(const auto &cal_labels : label_groups) + { + T_Ds reference; + lri.cal_mode = RI::LRI_Cal_Mode::CPU; + lri.cal_loop3(cal_labels, reference); + const RI::Tensor contribution = reference.at(0).at(key).copy(); + const Treal norm = contribution.norm(2); + if(!std::isfinite(norm) || norm==0) + throw std::runtime_error("Invalid CPU reference contraction"); + + lri.cal_mode = cal_mode; + for(const bool seeded : {false, true}) + { + T_Ds result; + // Tensor copies share storage unless copy() is used explicitly. + if(seeded) + result[0][key] = seed.copy(); + Treal accumulated_factor = 0, accumulated_magnitude = 0; + for(const double factor : {0.0, -1.0, 1.0, 1.0, -1.0, 0.5, 0.0, -0.5, 0.3}) + { + lri.cal_loop3(cal_labels, result, factor); + accumulated_factor += Treal(factor); + accumulated_magnitude += std::abs(Treal(factor)); + const RI::Tensor &actual = result.at(0).at(key); + if(result.size()!=1 || result.at(0).size()!=1 || actual.shape.size()!=2 + || actual.shape[0]!=2 || actual.shape[1]!=2) + throw std::runtime_error("Unexpected contraction output shape"); + for(std::size_t i=0; isize(); ++i) + { + const Tdata initial = seeded ? (*seed.data)[i] : Tdata(0); + const Tdata expected = initial + accumulated_factor*(*contribution.data)[i]; + const Treal error = std::abs((*actual.data)[i] - expected); + const Treal scale = 1 + std::abs(initial) + + accumulated_magnitude*std::abs((*contribution.data)[i]); + if(!std::isfinite(error) || error>tolerance*scale) + { + std::cerr<> case Label::ab_ab::a1b0_a2b2: case Label::ab_ab::a1b1_a2b2: return !this->symmetry.in_irreducible_sector(A1, A3); + // GPU output is (Aa2, Ab2) or (Aa2, Ab01), respectively. + case Label::ab_ab::a0b0_a1b1: + case Label::ab_ab::a0b0_a1b2: + return !this->symmetry.in_irreducible_sector(A2, A3); // a01b2_a2b01: Aa01, Ab01, Aa2 default: return false; diff --git a/include/RI/ri/LRI-cal_loop3_GPU.hpp b/include/RI/ri/LRI-cal_loop3_GPU.hpp index 30d7323..c991bdf 100644 --- a/include/RI/ri/LRI-cal_loop3_GPU.hpp +++ b/include/RI/ri/LRI-cal_loop3_GPU.hpp @@ -1553,7 +1553,8 @@ void LRI::cal_loop3_GPU( for (std::size_t ib2 = 0; ib2 < list_Ab2.size(); ++ib2) { const TAC &Ab2 = list_Ab2[ib2]; - if (this->filter_atom->filter_for32(label, Aa01, Ab01, Ab2)) continue; + // The output block is (Aa01, Ab01); Ab2 is only an intermediate atom. + if (this->filter_atom->filter_for32(label, Aa01, Ab2, Ab01)) continue; const Tensor &D_b = Global_Func::find(Ds_b_transpose, Ab01.first, TAC{Ab2.first, (Ab2.second-Ab01.second)%this->period}); if(D_b.empty()) continue; const GPU_Data::Pack &D_mul = rDs_mul.find_2nd(Aa01, Ab2); diff --git a/unittests/physics/test_exx_gpu_symmetry.cpp b/unittests/physics/test_exx_gpu_symmetry.cpp new file mode 100644 index 0000000..0da1347 --- /dev/null +++ b/unittests/physics/test_exx_gpu_symmetry.cpp @@ -0,0 +1,46 @@ +// Standalone callback regression: g++ -std=c++11 -Iinclude unittests/physics/test_exx_gpu_symmetry.cpp -o /tmp/test_exx_gpu_symmetry +#include +#include "RI/physics/symmetry/Filter_Atom_Symmetry.h" + +#include +#include +#include +#include +#include + +int main() +{ + using Cell = std::array; + using AtomCell = std::pair; + using Label = RI::Label::ab_ab; + const Cell zero{{0}}; + const Cell period{{2}}; + const std::map, std::set> sector{ + {{1, 0}, {zero}} + }; + RI::Filter_Atom_Symmetry filter(period, sector); + const AtomCell left{1, zero}; + const AtomCell right{0, zero}; + const AtomCell intermediate{2, zero}; + const Cell one{{1}}; + const AtomCell shifted_right{0, one}; + + assert(!filter.filter_for1(Label::a0b0_a1b1, 2)); + assert(!filter.filter_for32(Label::a0b0_a1b1, 2, left, right)); + assert(filter.filter_for32(Label::a0b0_a1b1, 2, right, left)); + assert(filter.filter_for32(Label::a0b0_a1b1, 2, left, shifted_right)); + + assert(!filter.filter_for1(Label::a0b0_a1b2, 2)); + assert(!filter.filter_for1(Label::a0b0_a1b2, right)); + assert(filter.filter_for1(Label::a0b0_a1b2, left)); + assert(!filter.filter_for32(Label::a0b0_a1b2, 2, left, right)); + assert(filter.filter_for32(Label::a0b0_a1b2, 2, right, left)); + + assert(!filter.filter_for1(Label::a0b0_a2b1, 1)); + assert(!filter.filter_for32(Label::a0b0_a2b1, 1, intermediate, right)); + assert(filter.filter_for32(Label::a0b0_a2b1, 1, intermediate, left)); + + assert(!filter.filter_for1(Label::a0b0_a2b2, 1)); + assert(!filter.filter_for32(Label::a0b0_a2b2, 1, intermediate, right)); + assert(filter.filter_for32(Label::a0b0_a2b2, 1, intermediate, left)); +} From 4a41b91335013e36c8e623370389cc7243c1dc26 Mon Sep 17 00:00:00 2001 From: maki49 <1579492865@qq.com> Date: Wed, 21 Oct 2026 02:04:43 -0400 Subject: [PATCH 3/9] Fix GPU symmetry filtering for all LibRI contraction labels (cherry picked from commit a0db48f56ffadf448c8057788df57562b42cd3c8) (cherry picked from commit dc1084707f51615ca34e479bb15299e4b1da4540) --- .../physics/symmetry/Filter_Atom_Symmetry.h | 17 +++- include/RI/ri/LRI-cal_loop3_GPU.hpp | 6 +- unittests/physics/test_exx_gpu_symmetry.cpp | 85 +++++++++++++++---- 3 files changed, 87 insertions(+), 21 deletions(-) diff --git a/include/RI/physics/symmetry/Filter_Atom_Symmetry.h b/include/RI/physics/symmetry/Filter_Atom_Symmetry.h index c1f0b9a..77a0f1f 100644 --- a/include/RI/physics/symmetry/Filter_Atom_Symmetry.h +++ b/include/RI/physics/symmetry/Filter_Atom_Symmetry.h @@ -45,9 +45,13 @@ class Filter_Atom_Symmetry: public Filter_Atom> case Label::ab_ab::a1b2_a2b0: case Label::ab_ab::a1b2_a2b1: return !this->symmetry.is_Aa_in_irreducible_sector(A1); - // GPU computes these contractions before their output atoms are known. + // GPU visits Aa01 before the output Aa2 is known in these cases. case Label::ab_ab::a0b0_a1b1: + case Label::ab_ab::a0b1_a1b0: case Label::ab_ab::a0b0_a1b2: + case Label::ab_ab::a0b1_a1b2: + case Label::ab_ab::a0b2_a1b0: + case Label::ab_ab::a0b2_a1b1: return false; default: throw std::invalid_argument("label "+Label_Tools::get_name(label)+" error in "+std::string(__FILE__)+" line "+std::to_string(__LINE__)); @@ -66,6 +70,11 @@ class Filter_Atom_Symmetry: public Filter_Atom> case Label::ab_ab::a0b1_a1b2: case Label::ab_ab::a0b2_a1b0: case Label::ab_ab::a0b2_a1b1: + // a01b2_a2b01: Ab01 is the output right atom on GPU. + case Label::ab_ab::a0b2_a2b0: + case Label::ab_ab::a0b2_a2b1: + case Label::ab_ab::a1b2_a2b0: + case Label::ab_ab::a1b2_a2b1: return !this->symmetry.is_Ab_in_irreducible_sector(A1.first); default: throw std::invalid_argument("label "+Label_Tools::get_name(label)+" error in "+std::string(__FILE__)+" line "+std::to_string(__LINE__)); @@ -104,9 +113,13 @@ class Filter_Atom_Symmetry: public Filter_Atom> case Label::ab_ab::a1b0_a2b2: case Label::ab_ab::a1b1_a2b2: return !this->symmetry.in_irreducible_sector(A1, A3); - // GPU output is (Aa2, Ab2) or (Aa2, Ab01), respectively. + // GPU output is (Aa2, Ab2) or (Aa2, Ab01); Aa01 is intermediate. case Label::ab_ab::a0b0_a1b1: + case Label::ab_ab::a0b1_a1b0: case Label::ab_ab::a0b0_a1b2: + case Label::ab_ab::a0b1_a1b2: + case Label::ab_ab::a0b2_a1b0: + case Label::ab_ab::a0b2_a1b1: return !this->symmetry.in_irreducible_sector(A2, A3); // a01b2_a2b01: Aa01, Ab01, Aa2 default: diff --git a/include/RI/ri/LRI-cal_loop3_GPU.hpp b/include/RI/ri/LRI-cal_loop3_GPU.hpp index c991bdf..6fd4949 100644 --- a/include/RI/ri/LRI-cal_loop3_GPU.hpp +++ b/include/RI/ri/LRI-cal_loop3_GPU.hpp @@ -1693,7 +1693,7 @@ void LRI::cal_loop3_GPU( for (std::size_t ib2 = 0; ib2 < list_Ab2.size(); ++ib2) { const TAC &Ab2 = list_Ab2[ib2]; - if (this->filter_atom->filter_for32(label, Aa01, Ab01, Ab2)) continue; + if (this->filter_atom->filter_for32(label, Aa01, Ab2, Ab01)) continue; const Tensor &D_b = tools.get_Ds_ab(Label::ab::b, Ab01, Ab2); if(D_b.empty()) continue; const GPU_Data::Pack &D_mul = rDs_mul.find_2nd(Aa01, Ab2); @@ -1832,7 +1832,7 @@ void LRI::cal_loop3_GPU( for (std::size_t ib2 = 0; ib2 < list_Ab2.size(); ++ib2) { const TAC &Ab2 = list_Ab2[ib2]; - if (this->filter_atom->filter_for32(label, Aa01, Ab01, Ab2)) continue; + if (this->filter_atom->filter_for32(label, Aa01, Ab2, Ab01)) continue; const Tensor &D_b = Global_Func::find(Ds_b_transpose, Ab01.first, TAC{Ab2.first, (Ab2.second-Ab01.second)%this->period}); if(D_b.empty()) continue; const GPU_Data::Pack &D_mul = rDs_mul.find_2nd(Aa01, Ab2); @@ -1971,7 +1971,7 @@ void LRI::cal_loop3_GPU( for (std::size_t ib2 = 0; ib2 < list_Ab2.size(); ++ib2) { const TAC &Ab2 = list_Ab2[ib2]; - if (this->filter_atom->filter_for32(label, Aa01, Ab01, Ab2)) continue; + if (this->filter_atom->filter_for32(label, Aa01, Ab2, Ab01)) continue; const Tensor &D_b = tools.get_Ds_ab(Label::ab::b, Ab01, Ab2); if(D_b.empty()) continue; const GPU_Data::Pack &D_mul = rDs_mul.find_2nd(Aa01, Ab2); diff --git a/unittests/physics/test_exx_gpu_symmetry.cpp b/unittests/physics/test_exx_gpu_symmetry.cpp index 0da1347..309336a 100644 --- a/unittests/physics/test_exx_gpu_symmetry.cpp +++ b/unittests/physics/test_exx_gpu_symmetry.cpp @@ -15,6 +15,7 @@ int main() using Label = RI::Label::ab_ab; const Cell zero{{0}}; const Cell period{{2}}; + const Cell one{{1}}; const std::map, std::set> sector{ {{1, 0}, {zero}} }; @@ -22,25 +23,77 @@ int main() const AtomCell left{1, zero}; const AtomCell right{0, zero}; const AtomCell intermediate{2, zero}; - const Cell one{{1}}; const AtomCell shifted_right{0, one}; + std::set