@@ -639,6 +639,114 @@ BOOST_AUTO_TEST_CASE(expression_general_product_into_block) {
639639 BOOST_CHECK_SMALL (diff_norm (w, w_ref, " b,i,k" ), 1e-10 );
640640}
641641
642+ BOOST_AUTO_TEST_CASE (expression_general_product_block_in_tree) {
643+ // a BLOCK leaf under an inner general node of a deeper tree:
644+ // w("p,q,r2") = (x.block("p,r1") * y("q,r1")) * z("r1,r2")
645+ // r1 is fused at the inner (general) node, contracted at the root
646+ auto & world = TA::get_default_world ();
647+ TA ::TiledRange tr_x{{0 , 2 , 4 }, {0 , 3 , 5 }}; // p x r1
648+ TA ::TiledRange tr_z{{0 , 3 , 5 }, {0 , 3 , 5 }}; // r1 x r2
649+ auto x = make_patterned_array (world, tr_x, 1.0 );
650+ auto y = make_patterned_array (world, tr_x, 1.5 );
651+ auto z = make_patterned_array (world, tr_z, 2.0 );
652+
653+ TA ::TArrayD xb, i1, w_ref, w;
654+ xb (" p,r1" ) = x (" p,r1" ).block ({0 , 0 }, {1 , 2 });
655+ i1 (" r1,p,q" ) = xb (" p,r1" ) * y (" q,r1" );
656+ w_ref (" p,q,r2" ) = i1 (" r1,p,q" ) * z (" r1,r2" );
657+
658+ BOOST_REQUIRE_NO_THROW (
659+ w (" p,q,r2" ) = (x (" p,r1" ).block ({0 , 0 }, {1 , 2 }) * y (" q,r1" )) * z (" r1,r2" ));
660+ BOOST_CHECK_SMALL (diff_norm (w, w_ref, " p,q,r2" ), 1e-10 );
661+ }
662+
663+ BOOST_AUTO_TEST_CASE (expression_general_product_repermute_into_block) {
664+ // a general product with a NON-canonical target layout (streaming
665+ // re-permute) assigned INTO a block view of the result
666+ auto & world = TA::get_default_world ();
667+ TA ::TiledRange tr_x{{0 , 2 }, {0 , 3 , 5 }}; // p x r1
668+ TA ::TiledRange tr_w{{0 , 2 , 4 }, {0 , 2 , 4 }, {0 , 3 , 5 }}; // p, q, r1
669+ auto x = make_patterned_array (world, tr_x, 1.0 );
670+ auto y = make_patterned_array (world, tr_x, 1.5 );
671+
672+ TA ::TArrayD i1, w (world, tr_w), w_ref (world, tr_w);
673+ w.fill (0.0 );
674+ w_ref.fill (0.0 );
675+ i1 (" r1,p,q" ) = x (" p,r1" ) * y (" q,r1" ); // canonical evaluation
676+ w_ref (" p,q,r1" ).block ({0 , 0 , 0 }, {1 , 1 , 2 }) = i1 (" r1,p,q" );
677+
678+ BOOST_REQUIRE_NO_THROW (w (" p,q,r1" ).block ({0 , 0 , 0 }, {1 , 1 , 2 }) =
679+ x (" p,r1" ) * y (" q,r1" ));
680+ BOOST_CHECK_SMALL (diff_norm (w, w_ref, " p,q,r1" ), 1e-10 );
681+ }
682+
683+ BOOST_AUTO_TEST_CASE (expression_general_sum_under_product) {
684+ // a SUM nested under a product, with a general product as one summand:
685+ // F("i,j") = A("x,i") * (B("x,k") * C("x,k,j") + D("x,j"))
686+ // x is fused inside B*C (demanded by the sum's consumer), k is contracted
687+ // within the summand (never escapes), and x is contracted at the root.
688+ // The down-pass prunes k from the sum's demand automatically (it appears
689+ // in neither the target nor A) and hands (j,x) to BOTH summands.
690+ auto & world = TA::get_default_world ();
691+ TA ::TiledRange tr_a{{0 , 3 , 5 }, {0 , 2 , 4 }}; // x, i
692+ TA ::TiledRange tr_b{{0 , 3 , 5 }, {0 , 2 , 3 }}; // x, k
693+ TA ::TiledRange tr_c{{0 , 3 , 5 }, {0 , 2 , 3 }, {0 , 2 , 4 }}; // x, k, j
694+ TA ::TiledRange tr_d{{0 , 3 , 5 }, {0 , 2 , 4 }}; // x, j
695+ auto a = make_patterned_array (world, tr_a, 1.0 );
696+ auto b = make_patterned_array (world, tr_b, 2.0 );
697+ auto c = make_patterned_array (world, tr_c, 3.0 );
698+ auto d = make_patterned_array (world, tr_d, 4.0 );
699+
700+ TA ::TArrayD i1, s, f_ref, f;
701+ i1 (" x,j" ) = b (" x,k" ) * c (" x,k,j" ); // general: x fused, k contracted
702+ s (" x,j" ) = i1 (" x,j" ) + d (" x,j" );
703+ f_ref (" i,j" ) = a (" x,i" ) * s (" x,j" );
704+
705+ BOOST_REQUIRE_NO_THROW (f (" i,j" ) =
706+ a (" x,i" ) * (b (" x,k" ) * c (" x,k,j" ) + d (" x,j" )));
707+ BOOST_CHECK_SMALL (diff_norm (f, f_ref, " i,j" ), 1e-10 );
708+ }
709+
710+ BOOST_AUTO_TEST_CASE (expression_general_kitchen_sink) {
711+ // one expression combining a THC-like batching index (x: fused at the
712+ // inner node, contracted at the root), a mixed T x ToT general product,
713+ // a ToT x ToT general product with an inner outer-product, and a scalar
714+ // prefactor (ScalMult at the root):
715+ // W("i,j,m;a,b") = 2 * ((g("x,i") * cv("x,j;a")) * dv("x,i,m;b"))
716+ auto & world = TA::get_default_world ();
717+ TA ::TiledRange tr_g{{0 , 3 , 5 }, {0 , 2 , 4 }}; // x, i
718+ TA ::TiledRange tr_cv{{0 , 3 , 5 }, {0 , 2 , 3 }}; // x, j
719+ TA ::TiledRange tr_dv{{0 , 3 , 5 }, {0 , 2 , 4 }, {0 , 2 , 3 }}; // x, i, m
720+ auto g = make_patterned_array (world, tr_g, 1.0 );
721+ auto cv = make_patterned_tot_array (world, tr_cv, {2 }, 2.0 );
722+ auto dv = make_patterned_tot_array (world, tr_dv, {3 }, 3.0 );
723+
724+ TArrayToT i1, i2, w_ref, w;
725+ i1 (" x,i,j;a" ) = g (" x,i" ) * cv (" x,j;a" ); // T x ToT general
726+ i2 (" i,j,m;a,b" ) = i1 (" x,i,j;a" ) * dv (" x,i,m;b" ); // ToT x ToT, inner outer
727+ w_ref (" i,j,m;a,b" ) = 2.0 * i2 (" i,j,m;a,b" );
728+
729+ BOOST_REQUIRE_NO_THROW (w (" i,j,m;a,b" ) =
730+ 2.0 * ((g (" x,i" ) * cv (" x,j;a" )) * dv (" x,i,m;b" )));
731+ BOOST_CHECK_SMALL (tot_max_abs_diff (w, w_ref), 1e-10 );
732+ }
733+
734+ BOOST_AUTO_TEST_CASE (expression_general_product_tot_no_externals_gated) {
735+ // a ToT x ToT general product with NO external (free) outer indices --
736+ // every outer index fused or contracted -- is not supported by the
737+ // batched tile op (and used to segfault in the folded GEMM); the engine
738+ // must reject it with an informative error (einsum() handles this shape
739+ // natively via its no-external regime)
740+ auto & world = TA::get_default_world ();
741+ TA ::TiledRange tr{{0 , 3 , 5 }, {0 , 2 , 4 }, {0 , 2 , 3 }}; // x, i, j
742+ auto a = make_patterned_tot_array (world, tr, {2 }, 1.0 );
743+ auto b = make_patterned_tot_array (world, tr, {3 }, 2.0 );
744+
745+ TArrayToT c;
746+ BOOST_CHECK_THROW (c (" i,j;a,b" ) = a (" x,i,j;a" ) * b (" x,i,j;b" ),
747+ TiledArray::Exception);
748+ }
749+
642750BOOST_AUTO_TEST_CASE (expression_general_product_tot_inner_outer_product) {
643751 // the PNO-CC PPL building-block shape: ToT x ToT with an EMPTY right
644752 // outer-external set and an inner OUTER-product:
0 commit comments