Skip to content

Commit 7b4319c

Browse files
committed
tests: distributed (np>1) general-product suite
Adds general_product_distributed_suite (UNLABELED, so the CI harness runs it at both np=1 and np=2; the existing general_product_suite is serial-labeled and never exercised the batched Summa across ranks). Seven differential cases vs the legacy sub-World einsum oracle: dense, sparse, mixed T x ToT, no-external (dense + ToT), the one-expression THC reconstruction, and dist_no_externals_3d_grid -- which engages the 3-d (proc_h > 1) grid and asserts the no-external result distributes across the h-planes rather than piling on one rank.
1 parent 0ac0881 commit 7b4319c

1 file changed

Lines changed: 141 additions & 0 deletions

File tree

tests/general_product.cpp

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include "tiledarray.h"
99
#include "unit_test_config.h"
1010

11+
#include <set>
12+
1113
BOOST_AUTO_TEST_SUITE(general_product_suite, TA_UT_LABEL_SERIAL)
1214

1315
namespace TA = TiledArray;
@@ -1342,3 +1344,142 @@ BOOST_AUTO_TEST_CASE(einsum_expression_route_matches_legacy) {
13421344
}
13431345

13441346
BOOST_AUTO_TEST_SUITE_END()
1347+
1348+
// Distributed (np > 1) coverage of general products. Unlike
1349+
// general_product_suite (serial-labeled: classification/optimizer unit tests
1350+
// + np=1 evaluation), this suite carries NO label, so the CI harness runs it
1351+
// at BOTH np=1 and np=2 (see tests/CMakeLists.txt: np-1 excludes @distributed,
1352+
// np-2 excludes @serial). It exercises the batched Summa across ranks -- a
1353+
// path the serial suite never covered. Each case differential-tests the
1354+
// expression route against the legacy sub-World einsum oracle.
1355+
//
1356+
// Most cases evaluate via the ordinary 2-d (proc_h == 1) batched Summa;
1357+
// dist_no_externals_3d_grid exercises the 3-d (proc_h > 1) grid, where the
1358+
// heuristic spreads ranks over the slab dimension because M == N == 1.
1359+
BOOST_AUTO_TEST_SUITE(general_product_distributed_suite)
1360+
1361+
// the fixtures/helpers live in general_product_suite's anonymous namespace
1362+
using general_product_suite::diff_norm;
1363+
using general_product_suite::diff_norm_sp;
1364+
using general_product_suite::ForceLegacyEinsum;
1365+
using general_product_suite::make_patterned_array;
1366+
using general_product_suite::make_patterned_sparse_array;
1367+
using general_product_suite::make_patterned_tot_array;
1368+
using general_product_suite::TArrayToT;
1369+
using general_product_suite::tot_max_abs_diff;
1370+
1371+
BOOST_AUTO_TEST_CASE(dist_dense) {
1372+
// batched general product: b fused, j contracted, i/k free
1373+
auto& world = TA::get_default_world();
1374+
ForceLegacyEinsum legacy_oracle;
1375+
TA::TiledRange tr_a{{0, 2, 4}, {0, 2, 3}, {0, 2, 5}}; // b, i, j
1376+
TA::TiledRange tr_b{{0, 2, 4}, {0, 2, 5}, {0, 3, 4}}; // b, j, k
1377+
auto a = make_patterned_array(world, tr_a, 1.0);
1378+
auto b = make_patterned_array(world, tr_b, 2.0);
1379+
TA::TArrayD c;
1380+
BOOST_REQUIRE_NO_THROW(c("b,i,k") = a("b,i,j") * b("b,j,k"));
1381+
auto c_ref = TA::einsum(a("b,i,j"), b("b,j,k"), "b,i,k");
1382+
BOOST_CHECK_SMALL(diff_norm(c, c_ref, "b,i,k"), 1e-10);
1383+
}
1384+
1385+
BOOST_AUTO_TEST_CASE(dist_sparse) {
1386+
// block-sparse batched general product (SparseShape::gemm_batched +
1387+
// the sparse (h,k)-keyed Summa groups, across ranks)
1388+
auto& world = TA::get_default_world();
1389+
ForceLegacyEinsum legacy_oracle;
1390+
TA::TiledRange tr_a{{0, 2, 5}, {0, 3, 4}, {0, 2, 6, 7}}; // b, i, j
1391+
TA::TiledRange tr_b{{0, 2, 5}, {0, 2, 6, 7}, {0, 4, 5}}; // b, j, k
1392+
auto a = make_patterned_sparse_array(world, tr_a, 1.0, 3);
1393+
auto b = make_patterned_sparse_array(world, tr_b, 2.0, 4);
1394+
TA::TSpArrayD c;
1395+
BOOST_REQUIRE_NO_THROW(c("b,i,k") = a("b,i,j") * b("b,j,k"));
1396+
auto c_ref = TA::einsum(a("b,i,j"), b("b,j,k"), "b,i,k");
1397+
BOOST_CHECK_SMALL(diff_norm_sp(c, c_ref, "b,i,k"), 1e-10);
1398+
}
1399+
1400+
BOOST_AUTO_TEST_CASE(dist_tot_mixed) {
1401+
// mixed T x ToT general product (inner Scale) across ranks
1402+
auto& world = TA::get_default_world();
1403+
ForceLegacyEinsum legacy_oracle;
1404+
TA::TiledRange tr_a{{0, 2, 4}, {0, 2, 3}, {0, 2, 5}}; // b, i, j
1405+
TA::TiledRange tr_b{{0, 2, 4}, {0, 2, 5}, {0, 3, 4}}; // b, j, k
1406+
auto a = make_patterned_array(world, tr_a, 1.0);
1407+
auto b = make_patterned_tot_array(world, tr_b, {3}, 2.0);
1408+
TArrayToT c;
1409+
BOOST_REQUIRE_NO_THROW(c("b,i,k;m") = a("b,i,j") * b("b,j,k;m"));
1410+
auto c_ref = TA::einsum(a("b,i,j"), b("b,j,k;m"), "b,i,k;m");
1411+
BOOST_CHECK_SMALL(tot_max_abs_diff(c, c_ref), 1e-10);
1412+
}
1413+
1414+
BOOST_AUTO_TEST_CASE(dist_no_externals_dense) {
1415+
// no-external general product (Hadamard reduction shape) across ranks;
1416+
// the synthetic unit left-external mode handling under distribution
1417+
auto& world = TA::get_default_world();
1418+
ForceLegacyEinsum legacy_oracle;
1419+
TA::TiledRange tr{{0, 2, 4}, {0, 3, 5}}; // i, j
1420+
auto a = make_patterned_array(world, tr, 1.0);
1421+
auto b = make_patterned_array(world, tr, 2.0);
1422+
TA::TArrayD c;
1423+
BOOST_REQUIRE_NO_THROW(c("i") = a("i,j") * b("i,j"));
1424+
auto c_ref = TA::einsum(a("i,j"), b("i,j"), "i");
1425+
BOOST_CHECK_SMALL(diff_norm(c, c_ref, "i"), 1e-10);
1426+
}
1427+
1428+
BOOST_AUTO_TEST_CASE(dist_no_externals_tot) {
1429+
// no-external ToT general product across ranks
1430+
auto& world = TA::get_default_world();
1431+
ForceLegacyEinsum legacy_oracle;
1432+
TA::TiledRange tr{{0, 3, 5}, {0, 2, 4}, {0, 2, 3}}; // x, i, j
1433+
auto a = make_patterned_tot_array(world, tr, {2}, 1.0);
1434+
auto b = make_patterned_tot_array(world, tr, {3}, 2.0);
1435+
TArrayToT c;
1436+
BOOST_REQUIRE_NO_THROW(c("i,j;a,b") = a("x,i,j;a") * b("x,i,j;b"));
1437+
auto c_ref = TA::einsum(a("x,i,j;a"), b("x,i,j;b"), "i,j;a,b");
1438+
BOOST_CHECK_SMALL(tot_max_abs_diff(c, c_ref), 1e-10);
1439+
}
1440+
1441+
BOOST_AUTO_TEST_CASE(dist_inner_node_thc) {
1442+
// THC reconstruction in one expression (general products at inner nodes)
1443+
// across ranks
1444+
auto& world = TA::get_default_world();
1445+
TA::TiledRange tr_x{{0, 2, 4}, {0, 3, 5}}; // orbital x auxiliary
1446+
TA::TiledRange tr_z{{0, 3, 5}, {0, 3, 5}}; // auxiliary x auxiliary
1447+
auto x = make_patterned_array(world, tr_x, 1.0);
1448+
auto z = make_patterned_array(world, tr_z, 2.0);
1449+
TA::TArrayD g;
1450+
BOOST_REQUIRE_NO_THROW(g("p,q,r,s") = x("p,r1") * x("q,r1") * z("r1,r2") *
1451+
x("r,r2") * x("s,r2"));
1452+
TA::TArrayD i1, i2, i3, g_ref;
1453+
i1("r1,p,q") = x("p,r1") * x("q,r1");
1454+
i2("p,q,r2") = i1("r1,p,q") * z("r1,r2");
1455+
i3("r2,p,q,r") = i2("p,q,r2") * x("r,r2");
1456+
g_ref("p,q,r,s") = i3("r2,p,q,r") * x("s,r2");
1457+
BOOST_CHECK_SMALL(diff_norm(g, g_ref, "p,q,r,s"), 1e-10);
1458+
}
1459+
1460+
BOOST_AUTO_TEST_CASE(dist_no_externals_3d_grid) {
1461+
// a no-external product with several slabs: at np>1 the heuristic engages
1462+
// the 3-d grid (M=N=1 ⇒ proc_h > 1, the ranks spread over the slab axis),
1463+
// so the result tiles distribute across the h-planes rather than piling on
1464+
// one rank. Exercises the cross-plane result-tile transfer.
1465+
auto& world = TA::get_default_world();
1466+
ForceLegacyEinsum legacy_oracle;
1467+
TA::TiledRange tr{{0, 2, 4, 6}, {0, 3, 5}}; // i (3 tiles), j
1468+
auto a = make_patterned_array(world, tr, 1.0);
1469+
auto b = make_patterned_array(world, tr, 2.0);
1470+
TA::TArrayD c;
1471+
BOOST_REQUIRE_NO_THROW(c("i") = a("i,j") * b("i,j"));
1472+
auto c_ref = TA::einsum(a("i,j"), b("i,j"), "i");
1473+
BOOST_CHECK_SMALL(diff_norm(c, c_ref, "i"), 1e-10);
1474+
1475+
// at np>1 the no-external result must NOT all live on one rank (the
1476+
// degeneracy the 3-d grid fixes)
1477+
if (world.size() > 1) {
1478+
std::set<std::size_t> owners;
1479+
for (std::size_t o = 0; o < c.trange().tiles_range().volume(); ++o)
1480+
owners.insert(c.pmap()->owner(o));
1481+
BOOST_CHECK_GT(owners.size(), 1u);
1482+
}
1483+
}
1484+
1485+
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)