Skip to content

Commit e9980d1

Browse files
committed
expressions: general products honor arbitrary result layouts via a streaming re-permute
A target that differs from the canonical (fused..., left-free..., right-free...) result layout cannot be folded into the batched tile op (BatchedContractReduce must be perm-free); evaluate canonically (Summa over a slab-replicated pmap) and re-permute to the target with a streaming UnaryEvalImpl. Honors the implicit-permute contract: when the consumer fuses the permutation into its own operation (transposed GEMM), only the tile ordinals/trange are remapped and contents stay canonical. Replaces the interleaved-target gate, enabling general products at inner expression-tree nodes and non-canonical root targets.
1 parent e5693a4 commit e9980d1

1 file changed

Lines changed: 81 additions & 14 deletions

File tree

src/TiledArray/expressions/cont_engine.h

Lines changed: 81 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
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>
@@ -36,6 +37,7 @@
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

4042
namespace TiledArray {
4143
namespace 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

Comments
 (0)