25template <std::
size_t LhsRank, std::
size_t RhsRank, std::
size_t ContractedRank>
struct ContractionAxes
27 static_assert(ContractedRank <= LhsRank);
28 static_assert(ContractedRank <= RhsRank);
30 static constexpr std::size_t
lhs_rank = LhsRank;
31 static constexpr std::size_t
rhs_rank = RhsRank;
44template <std::
size_t LhsRank, std::
size_t RhsRank, std::
size_t ContractedRank>
45[[nodiscard]]
constexpr bool
48 std::array<bool, LhsRank> lhs_present{};
49 std::array<bool, RhsRank> rhs_present{};
51 for (std::size_t index = 0; index < ContractedRank; ++index)
54 auto const rhs_axis = axes.rhs_contracted[index];
55 if (lhs_axis >= LhsRank || rhs_axis >= RhsRank || lhs_present[lhs_axis] || rhs_present[rhs_axis])
return false;
56 if (index > 0 && axes.lhs_contracted[index - 1] >= lhs_axis)
return false;
57 lhs_present[lhs_axis] =
true;
58 rhs_present[rhs_axis] =
true;
61 for (std::size_t index = 0; index < axes.lhs_surviving.size(); ++index)
63 auto const axis = axes.lhs_surviving[index];
64 if (axis >= LhsRank || lhs_present[axis])
return false;
65 if (index > 0 && axes.lhs_surviving[index - 1] >= axis)
return false;
66 lhs_present[axis] =
true;
69 for (std::size_t index = 0; index < axes.rhs_surviving.size(); ++index)
71 auto const axis = axes.rhs_surviving[index];
72 if (axis >= RhsRank || rhs_present[axis])
return false;
73 if (index > 0 && axes.rhs_surviving[index - 1] >= axis)
return false;
74 rhs_present[axis] =
true;
77 return std::ranges::all_of(lhs_present, [](
bool present) {
return present; }) &&
78 std::ranges::all_of(rhs_present, [](
bool present) {
return present; });
85template <std::
size_t LhsRank, std::
size_t RhsRank, std::
size_t ContractedRank>
86[[nodiscard]]
auto make_contraction_axes(std::array<std::pair<std::size_t, std::size_t>, ContractedRank> requested_axes)
89 for (
auto const& [lhs_axis, rhs_axis] : requested_axes)
91 ERROR_IF(lhs_axis >= LhsRank,
"left contraction axis is out of range", lhs_axis, LhsRank);
92 ERROR_IF(rhs_axis >= RhsRank,
"right contraction axis is out of range", rhs_axis, RhsRank);
95 std::ranges::sort(requested_axes);
97 std::array<bool, RhsRank> rhs_contracted{};
99 for (std::size_t index = 0; index < ContractedRank; ++index)
101 auto const [lhs_axis, rhs_axis] = requested_axes[index];
102 if (index > 0)
ERROR_IF(result.
lhs_contracted[index - 1] == lhs_axis,
"left contraction axes must be unique");
103 ERROR_IF(rhs_contracted[rhs_axis],
"right contraction axes must be unique");
106 rhs_contracted[rhs_axis] =
true;
109 std::size_t lhs_contracted_index = 0;
110 std::size_t lhs_surviving_index = 0;
111 for (std::size_t axis = 0; axis < LhsRank; ++axis)
113 if (lhs_contracted_index < ContractedRank && result.
lhs_contracted[lhs_contracted_index] == axis)
114 ++lhs_contracted_index;
119 std::size_t rhs_surviving_index = 0;
120 for (std::size_t axis = 0; axis < RhsRank; ++axis)
122 if (!rhs_contracted[axis]) result.
rhs_surviving[rhs_surviving_index++] = axis;
131template <std::size_t Rank, std::size_t... Axis>
132[[nodiscard]]
auto make_contraction_extents(std::array<uni20::index_type, Rank>
const& values,
133 std::index_sequence<Axis...>) -> stdex::dextents<uni20::index_type, Rank>
135 return stdex::dextents<uni20::index_type, Rank>{values[Axis]...};
140template <
class Lhs,
class Rhs, std::
size_t LhsRank, std::
size_t RhsRank, std::
size_t ContractedRank>
143 -> stdex::dextents<uni20::index_type, ContractionAxes<LhsRank, RhsRank, ContractedRank>::output_rank>
146 for (std::size_t index = 0; index < ContractedRank; ++index)
148 auto const lhs_axis = axes.lhs_contracted[index];
149 auto const rhs_axis = axes.rhs_contracted[index];
150 ERROR_IF(lhs.extent(lhs_axis) != rhs.extent(rhs_axis),
"paired contraction extents do not agree", lhs_axis,
151 rhs_axis, lhs.extent(lhs_axis), rhs.extent(rhs_axis));
155 std::array<uni20::index_type, output_rank> values{};
156 std::size_t output_axis = 0;
157 for (
auto const axis : axes.lhs_surviving)
159 for (
auto const axis : axes.rhs_surviving)
162 return detail::make_contraction_extents(values, std::make_index_sequence<output_rank>{});
Configured gateway to Uni20's mdspan implementation.
Definition linalg/async.hpp:28
auto make_contraction_axes(std::array< std::pair< std::size_t, std::size_t >, ContractedRank > requested_axes) -> ContractionAxes< LhsRank, RhsRank, ContractedRank >
Normalize and validate a fixed-size array of contraction-axis pairs.
Definition contraction_axes.hpp:86
auto contraction_output_extents(Lhs const &lhs, Rhs const &rhs, ContractionAxes< LhsRank, RhsRank, ContractedRank > const &axes) -> stdex::dextents< uni20::index_type, ContractionAxes< LhsRank, RhsRank, ContractedRank >::output_rank >
Validate paired extents and return the canonical contraction output shape.
Definition contraction_axes.hpp:141
constexpr bool contraction_axes_are_valid(ContractionAxes< LhsRank, RhsRank, ContractedRank > const &axes) noexcept
Return whether contraction axes form canonical partitions of both inputs.
Definition contraction_axes.hpp:46
std::ptrdiff_t index_type
Definition types.hpp:19
Canonical contracted and surviving axes for two fixed-rank operands.
Definition contraction_axes.hpp:26
static constexpr std::size_t rhs_rank
Definition contraction_axes.hpp:31
std::array< std::size_t, rhs_surviving_rank > rhs_surviving
Definition contraction_axes.hpp:40
std::array< std::size_t, contracted_rank > lhs_contracted
Definition contraction_axes.hpp:37
static constexpr std::size_t output_rank
Definition contraction_axes.hpp:35
std::array< std::size_t, contracted_rank > rhs_contracted
Definition contraction_axes.hpp:38
static constexpr std::size_t lhs_surviving_rank
Definition contraction_axes.hpp:33
static constexpr std::size_t rhs_surviving_rank
Definition contraction_axes.hpp:34
static constexpr std::size_t lhs_rank
Definition contraction_axes.hpp:30
std::array< std::size_t, lhs_surviving_rank > lhs_surviving
Definition contraction_axes.hpp:39
static constexpr std::size_t contracted_rank
Definition contraction_axes.hpp:32
#define CHECK(cond,...)
Definition trace.hpp:299
#define ERROR_IF(cond,...)
Definition trace.hpp:389