2727#define TILEDARRAY_EXPRESSIONS_CONT_ENGINE_H__INCLUDED
2828
2929#include < TiledArray/dist_eval/contraction_eval.h>
30+ #include < TiledArray/dist_eval/unary_eval.h>
3031#include < TiledArray/expressions/binary_engine.h>
3132#include < TiledArray/expressions/permopt.h>
3233#include < TiledArray/pmap/slabbed_pmap.h>
3637#include < TiledArray/tile_op/batched_contract_reduce.h>
3738#include < TiledArray/tile_op/contract_reduce.h>
3839#include < TiledArray/tile_op/mult.h>
40+ #include < TiledArray/tile_op/noop.h>
3941
4042namespace TiledArray {
4143namespace expressions {
@@ -179,6 +181,10 @@ class ContEngine : public BinaryEngine<Derived> {
179181 // General (fused + contracted + free indices) products only:
180182 unsigned int n_fused_modes_ = 0 ; // /< # of leading fused (outer) modes
181183 size_type n_slabs_ = 1 ; // /< # of fused-index tile slabs
184+ bool general_repermute_ = false ; // /< whether the target layout differs
185+ // /< from the canonical result layout, so
186+ // /< the evaluated result is re-permuted
187+ // /< by a streaming unary eval
182188
183189 static unsigned int find (const BipartiteIndexList& indices,
184190 const std::string& index_label, unsigned int i,
@@ -660,16 +666,13 @@ class ContEngine : public BinaryEngine<Derived> {
660666 TA_ASSERT (nh > 0u ); // else this is a pure contraction
661667 n_fused_modes_ = nh;
662668
663- // initialize perm_; an interleaved target (a result permutation that
664- // mixes fused and free modes) is not yet supported -- the canonical
665- // result layout must equal the target
669+ // initialize perm_; a target that differs from the canonical (fused...,
670+ // left-free..., right-free...) result layout cannot be folded into the
671+ // batched tile op (BatchedContractReduce must be perm-free), so the
672+ // product is evaluated in its canonical layout and re-permuted to the
673+ // target by a streaming unary eval (see make_dist_eval_general)
666674 this ->init_perm (target_indices);
667- if (outer (target_indices) != outer (indices_))
668- TA_EXCEPTION (
669- " general products (fused + contracted + free indices): targets "
670- " that interleave fused and free indices are not yet supported; "
671- " reorder the result annotation to (fused..., left-free..., "
672- " right-free...)" );
675+ general_repermute_ = (outer (target_indices) != outer (indices_));
673676
674677 // the tile op operates on the folded (fused-mode-free) shapes
675678 const auto left_op = to_cblas_op (left_outer_permtype_);
@@ -712,6 +715,12 @@ class ContEngine : public BinaryEngine<Derived> {
712715
713716 trange_ = make_trange_general ();
714717 shape_ = make_shape_general ();
718+ if (general_repermute_) {
719+ // consumers see the target layout; the canonical structures are
720+ // recomputed in make_dist_eval_general for the inner Summa
721+ trange_ = outer (perm_) * trange_;
722+ shape_ = shape_.perm (outer (perm_));
723+ }
715724
716725 if (ExprEngine_::override_ptr_ && ExprEngine_::override_ptr_->shape ) {
717726 shape_ = shape_.mask (*ExprEngine_::override_ptr_->shape );
@@ -834,9 +843,32 @@ class ContEngine : public BinaryEngine<Derived> {
834843 }
835844 }
836845
846+ // / Streaming tile re-permute op for general products whose target layout
847+ // / differs from the canonical (fused..., free...) result layout: the
848+ // / batched tile op must stay perm-free, so the consumer-side unary eval
849+ // / applies the result permutation per tile instead
850+ struct GeneralRepermuteOp {
851+ typedef value_type result_type;
852+ typedef value_type argument_type;
853+ static constexpr bool is_consumable = false ;
854+ BipartitePermutation perm;
855+ // / false when the consumer fuses the permutation into its own operation
856+ // / (implicit permute, e.g. a transposed GEMM): then only the tile
857+ // / ordinals/trange are remapped (by the host UnaryEvalImpl) and the tile
858+ // / contents are delivered in the canonical layout
859+ bool permute_contents = true ;
860+ result_type operator ()(const argument_type& tile) const {
861+ if (!permute_contents) return tile;
862+ TiledArray::detail::Noop<value_type, value_type, false > noop;
863+ return noop (tile, perm);
864+ }
865+ };
866+
837867 // / Construct the distributed evaluator of a general product
838868
839- // / \return The batched-Summa distributed evaluator for this expression
869+ // / \return The batched-Summa distributed evaluator for this expression,
870+ // / wrapped in a streaming re-permute when the target layout differs from
871+ // / the canonical result layout
840872 dist_eval_type make_dist_eval_general () const {
841873 typedef TiledArray::detail::BatchedContractReduce<op_type> batched_op_type;
842874 typedef TiledArray::detail::Summa<typename left_type::dist_eval_type,
@@ -847,11 +879,46 @@ class ContEngine : public BinaryEngine<Derived> {
847879 typename left_type::dist_eval_type left = left_.make_dist_eval ();
848880 typename right_type::dist_eval_type right = right_.make_dist_eval ();
849881
850- std::shared_ptr<impl_type> pimpl = std::make_shared<impl_type>(
851- left, right, *world_, trange_, shape_, pmap_, perm_,
852- batched_op_type (op_, n_fused_modes_), K_ , proc_grid_, n_slabs_);
882+ if (!general_repermute_) {
883+ std::shared_ptr<impl_type> pimpl = std::make_shared<impl_type>(
884+ left, right, *world_, trange_, shape_, pmap_, perm_,
885+ batched_op_type (op_, n_fused_modes_), K_ , proc_grid_, n_slabs_);
886+ return dist_eval_type (pimpl);
887+ }
853888
854- return dist_eval_type (pimpl);
889+ // evaluate in the canonical layout (Summa with perm-free op), then
890+ // re-permute tiles to the target layout with a streaming unary eval;
891+ // trange_/shape_ hold the target-layout structures (see
892+ // init_struct_general), the canonical ones are recomputed here
893+ auto const canonical_trange = make_trange_general ();
894+ auto const canonical_shape = [this ]() {
895+ auto s = make_shape_general ();
896+ if (ExprEngine_::override_ptr_ && ExprEngine_::override_ptr_->shape ) {
897+ // the consumer-supplied mask is expressed in the target layout
898+ auto const inv_perm = outer (perm_).inv ();
899+ s = s.mask (ExprEngine_::override_ptr_->shape ->perm (inv_perm));
900+ }
901+ return s;
902+ }();
903+ // the inner Summa's result placement must be slab-replicated (the owner
904+ // of a tile independent of its slab index), regardless of the
905+ // (target-layout) pmap the consumer supplied for this node
906+ auto canonical_pmap = std::make_shared<TiledArray::detail::SlabbedPmap>(
907+ *world_, proc_grid_.make_pmap (), n_slabs_);
908+ std::shared_ptr<impl_type> pimpl = std::make_shared<impl_type>(
909+ left, right, *world_, canonical_trange, canonical_shape, canonical_pmap,
910+ BipartitePermutation{}, batched_op_type (op_, n_fused_modes_), K_ ,
911+ proc_grid_, n_slabs_);
912+ dist_eval_type canonical (pimpl);
913+
914+ typedef TiledArray::detail::UnaryEvalImpl<
915+ dist_eval_type, GeneralRepermuteOp, typename Derived::policy>
916+ repermute_impl_type;
917+ std::shared_ptr<repermute_impl_type> wrapper =
918+ std::make_shared<repermute_impl_type>(
919+ canonical, *world_, trange_, shape_, pmap_, perm_,
920+ GeneralRepermuteOp{perm_, !this ->implicit_permute_outer_ });
921+ return dist_eval_type (wrapper);
855922 }
856923
857924 // / Expression identification tag
0 commit comments