@@ -185,9 +185,31 @@ template <typename ArrayT1, typename ArrayT2>
185185constexpr bool AreArraySame =
186186 AreArrayT<ArrayT1, ArrayT2> || AreArrayToT<ArrayT1, ArrayT2>;
187187
188+ // "Denested" companion of a ToT array: drops the inner-tile nesting, leaving
189+ // a regular (non-nested) DistArray. For ToT inputs, the outer tile of the
190+ // denested array is always TA::Tensor — nested inner-tile types (e.g.
191+ // btas::Tensor) are only valid as the *innermost* tile and don't support the
192+ // outer-tile operations einsum needs (permute/reshape/batch/range+lambda
193+ // ctor). So for ToT we drop the inner tile and re-wrap its numeric type in
194+ // TA::Tensor. For non-ToT inputs, the original "drop one level" behavior is
195+ // preserved.
196+ namespace detail_denested {
197+ template <typename Array, typename Enabler = void >
198+ struct denested {
199+ using type = DistArray<typename Array::value_type::value_type,
200+ typename Array::policy_type>;
201+ };
202+ template <typename Array>
203+ struct denested <Array,
204+ std::enable_if_t <TiledArray::detail::is_tensor_of_tensor_v<
205+ typename Array::value_type>>> {
206+ using type = DistArray<
207+ TA ::Tensor<typename Array::value_type::value_type::numeric_type>,
208+ typename Array::policy_type>;
209+ };
210+ } // namespace detail_denested
188211template <typename Array>
189- using DeNestedArray = DistArray<typename Array::value_type::value_type,
190- typename Array::policy_type>;
212+ using DeNestedArray = typename detail_denested::denested<Array>::type;
191213
192214template <typename Array1, typename Array2>
193215using MaxNestedArray = std::conditional_t <(detail::nested_rank<Array2> >
@@ -496,13 +518,21 @@ auto einsum(expressions::TsrExpr<ArrayA_> A, expressions::TsrExpr<ArrayB_> B,
496518 // Step III: C1(ijpqab) -> C2(ijpq)
497519 // Step IV: C2(ijpq) -> C(ipjq)
498520
521+ // Build a "denested" tile: one scalar per outer index, summed over the
522+ // inner tile. The result tile's outer type is TA::Tensor (inner tile
523+ // types like btas::Tensor are only valid as the innermost tile and don't
524+ // expose the range+lambda ctor used here).
499525 auto sum_tot_2_tos = [](auto const &tot) {
500526 using tot_t = std::remove_reference_t <decltype (tot)>;
501- typename tot_t ::value_type result (tot.range (), [tot](auto &&ix) {
527+ using numeric_type = typename tot_t ::numeric_type;
528+ TA ::Tensor<numeric_type> result (tot.range (), [tot](auto &&ix) {
529+ // unqualified `sum` so ADL finds the right overload for both
530+ // TA::Tensor inner (free fn in namespace TiledArray, calls .sum())
531+ // and btas::Tensor inner (free fn in namespace btas).
502532 if (!tot (ix).empty ())
503- return tot (ix). sum ( );
533+ return sum ( tot (ix));
504534 else
505- return typename tot_t :: numeric_type{};
535+ return numeric_type{};
506536 });
507537 return result;
508538 };
@@ -722,7 +752,7 @@ auto einsum(expressions::TsrExpr<ArrayA_> A, expressions::TsrExpr<ArrayB_> B,
722752 using TensorT = std::remove_reference_t <decltype (el)>;
723753
724754 for (auto i = 0 ; i < vol; ++i)
725- el. add_to (element_product_op (aik.data ()[i], bik.data ()[i]));
755+ add_to (el, element_product_op (aik.data ()[i], bik.data ()[i]));
726756
727757 } else if constexpr (!AreArraySame<ArrayA, ArrayB>) {
728758 auto aik = ai.batch (k);
@@ -734,9 +764,9 @@ auto einsum(expressions::TsrExpr<ArrayA_> A, expressions::TsrExpr<ArrayB_> B,
734764
735765 for (auto i = 0 ; i < vol; ++i)
736766 if constexpr (IsArrayToT<ArrayA>) {
737- el. add_to (aik.data ()[i]. scale ( bik.data ()[i]));
767+ add_to (el, scale ( aik.data ()[i], bik.data ()[i]));
738768 } else {
739- el. add_to (bik.data ()[i]. scale ( aik.data ()[i]));
769+ add_to (el, scale ( bik.data ()[i], aik.data ()[i]));
740770 }
741771
742772 } else {
0 commit comments