@@ -677,18 +677,20 @@ class ContEngine : public BinaryEngine<Derived> {
677677 this ->init_perm (target_indices);
678678 general_repermute_ = (outer (target_indices) != outer (indices_));
679679
680- // A product with NO external (free) outer indices (every outer index
681- // fused or contracted, e.g. C("i,j;a,b") = A("x,i,j;a") * B("x,i,j;b"))
682- // folds to a GEMM with no free modes, i.e. rank-0 tensors, which the
683- // tile kernels do not support. Evaluate it with a SYNTHETIC UNIT
684- // left-external mode instead: the folded product becomes
685- // (1,K) x (K) -> (1), the exact shape of the (supported) one-sided
686- // neB == 0 case. The unit mode lives only in the tile op's GemmHelper;
687- // tranges, shapes and tiles carry the true (external-free) ranks, and
688- // BatchedContractReduce / SparseShape::gemm_batched detect the
689- // synthetic mode from the one-rank mismatch and pad their folded views
680+ // Some degenerate folded shapes would carry a rank-0 tensor, which the
681+ // tile kernels do not support (see synthetic_unit_left_external()):
682+ // - a NO-EXTERNAL product (every outer index fused or contracted, e.g.
683+ // C("i,j;a,b") = A("x,i,j;a") * B("x,i,j;b")) folds to a rank-0 RESULT;
684+ // - a FUSED BROADCAST (the left operand is entirely fused, no contraction,
685+ // e.g. C("b,k") = A("b") * B("b,k")) folds to a rank-0 LEFT operand.
686+ // Evaluate both with a SYNTHETIC UNIT left-external mode: the folded
687+ // product becomes (1,K) x (K,N) -> (1,N), a supported shape (the no-
688+ // external case has N == 1, the broadcast has K == 1). The unit mode lives
689+ // only in the tile op's GemmHelper; tranges, shapes and tiles carry the
690+ // true ranks, and BatchedContractReduce / SparseShape::gemm_batched detect
691+ // the synthetic mode from the one-rank mismatch and pad their folded views
690692 // with a unit extent.
691- const unsigned int u = ( outer_size (indices_) == nh) ? 1u : 0u ;
693+ const unsigned int u = synthetic_unit_left_external () ;
692694
693695 // the tile op operates on the folded (fused-mode-free) shapes; the
694696 // synthetic unit mode leads the folded left operand, so it is NoTrans
@@ -749,17 +751,34 @@ class ContEngine : public BinaryEngine<Derived> {
749751 }
750752 }
751753
754+ // / \return 1 if the folded general product needs a SYNTHETIC unit
755+ // / left-external mode, else 0. The folded (fused-mode-free) GEMM cannot host
756+ // / a rank-0 tensor, which arises in two degenerate cases:
757+ // / - rank-0 RESULT: every outer index is fused or contracted (no external),
758+ // / i.e. outer_size(indices_) == n_fused_modes_;
759+ // / - rank-0 LEFT operand: the left argument is entirely fused with no
760+ // / contraction (a fused broadcast / per-fused-block scale), i.e.
761+ // / outer_size(left_indices_) == n_fused_modes_.
762+ // / A unit left-external mode (carried only in the GemmHelper) restores a
763+ // / supported (1,K) x (K,N) -> (1,N) shape in both cases.
764+ unsigned int synthetic_unit_left_external () const {
765+ return (outer_size (indices_) == n_fused_modes_ ||
766+ outer_size (left_indices_) == n_fused_modes_)
767+ ? 1u
768+ : 0u ;
769+ }
770+
752771 // / Tiled range factory function for a general product
753772
754773 // / \return The result tiled range: the fused mode ranges followed by the
755774 // / left- and right-external mode ranges
756775 trange_type make_trange_general () const {
757776 const unsigned int nh = n_fused_modes_;
758777 const unsigned int nc = op_.gemm_helper ().num_contract_ranks ();
759- // the no-external case carries a synthetic unit left-external mode in
760- // the GemmHelper only (see init_struct_general) ; the actual tranges do
761- // not have it
762- const unsigned int u = ( outer_size (indices_) == n_fused_modes_) ? 1u : 0u ;
778+ // degenerate folds carry a synthetic unit left-external mode in the
779+ // GemmHelper only (see synthetic_unit_left_external()) ; the actual tranges
780+ // do not have it
781+ const unsigned int u = synthetic_unit_left_external () ;
763782 const unsigned int neA = op_.gemm_helper ().left_rank () - nc - u;
764783 const unsigned int neB = op_.gemm_helper ().right_rank () - nc;
765784
@@ -812,10 +831,10 @@ class ContEngine : public BinaryEngine<Derived> {
812831 std::shared_ptr<const pmap_interface> pmap) {
813832 const unsigned int nh = n_fused_modes_;
814833 const unsigned int nc = op_.gemm_helper ().num_contract_ranks ();
815- // the no-external case carries a synthetic unit left-external mode in
816- // the GemmHelper only (see init_struct_general) ; the actual tranges do
817- // not have it
818- const unsigned int u = ( outer_size (indices_) == nh) ? 1u : 0u ;
834+ // degenerate folds carry a synthetic unit left-external mode in the
835+ // GemmHelper only (see synthetic_unit_left_external()) ; the actual tranges
836+ // do not have it
837+ const unsigned int u = synthetic_unit_left_external () ;
819838 const unsigned int neA = op_.gemm_helper ().left_rank () - nc - u;
820839 const unsigned int neB = op_.gemm_helper ().right_rank () - nc;
821840
0 commit comments