Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
contraction_axes.hpp
Go to the documentation of this file.
1#pragma once
2
10#include <uni20/core/types.hpp>
12
13#include <algorithm>
14#include <array>
15#include <cstddef>
16#include <utility>
17
18namespace uni20::linalg
19{
20
25template <std::size_t LhsRank, std::size_t RhsRank, std::size_t ContractedRank> struct ContractionAxes
26{
27 static_assert(ContractedRank <= LhsRank);
28 static_assert(ContractedRank <= RhsRank);
29
30 static constexpr std::size_t lhs_rank = LhsRank;
31 static constexpr std::size_t rhs_rank = RhsRank;
32 static constexpr std::size_t contracted_rank = ContractedRank;
33 static constexpr std::size_t lhs_surviving_rank = lhs_rank - contracted_rank;
34 static constexpr std::size_t rhs_surviving_rank = rhs_rank - contracted_rank;
35 static constexpr std::size_t output_rank = lhs_surviving_rank + rhs_surviving_rank;
36
37 std::array<std::size_t, contracted_rank> lhs_contracted{};
38 std::array<std::size_t, contracted_rank> rhs_contracted{};
39 std::array<std::size_t, lhs_surviving_rank> lhs_surviving{};
40 std::array<std::size_t, rhs_surviving_rank> rhs_surviving{};
41};
42
44template <std::size_t LhsRank, std::size_t RhsRank, std::size_t ContractedRank>
45[[nodiscard]] constexpr bool
47{
48 std::array<bool, LhsRank> lhs_present{};
49 std::array<bool, RhsRank> rhs_present{};
50
51 for (std::size_t index = 0; index < ContractedRank; ++index)
52 {
53 auto const lhs_axis = axes.lhs_contracted[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;
59 }
60
61 for (std::size_t index = 0; index < axes.lhs_surviving.size(); ++index)
62 {
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;
67 }
68
69 for (std::size_t index = 0; index < axes.rhs_surviving.size(); ++index)
70 {
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;
75 }
76
77 return std::ranges::all_of(lhs_present, [](bool present) { return present; }) &&
78 std::ranges::all_of(rhs_present, [](bool present) { return present; });
79}
80
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)
88{
89 for (auto const& [lhs_axis, rhs_axis] : requested_axes)
90 {
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);
93 }
94
95 std::ranges::sort(requested_axes);
97 std::array<bool, RhsRank> rhs_contracted{};
98
99 for (std::size_t index = 0; index < ContractedRank; ++index)
100 {
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");
104 result.lhs_contracted[index] = lhs_axis;
105 result.rhs_contracted[index] = rhs_axis;
106 rhs_contracted[rhs_axis] = true;
107 }
108
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)
112 {
113 if (lhs_contracted_index < ContractedRank && result.lhs_contracted[lhs_contracted_index] == axis)
114 ++lhs_contracted_index;
115 else
116 result.lhs_surviving[lhs_surviving_index++] = axis;
117 }
118
119 std::size_t rhs_surviving_index = 0;
120 for (std::size_t axis = 0; axis < RhsRank; ++axis)
121 {
122 if (!rhs_contracted[axis]) result.rhs_surviving[rhs_surviving_index++] = axis;
123 }
124
126 return result;
127}
128
129namespace detail
130{
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>
134{
135 return stdex::dextents<uni20::index_type, Rank>{values[Axis]...};
136}
137} // namespace detail
138
140template <class Lhs, class Rhs, std::size_t LhsRank, std::size_t RhsRank, std::size_t ContractedRank>
141[[nodiscard]] auto contraction_output_extents(Lhs const& lhs, Rhs const& rhs,
143 -> stdex::dextents<uni20::index_type, ContractionAxes<LhsRank, RhsRank, ContractedRank>::output_rank>
144{
146 for (std::size_t index = 0; index < ContractedRank; ++index)
147 {
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));
152 }
153
154 constexpr std::size_t output_rank = ContractionAxes<LhsRank, RhsRank, ContractedRank>::output_rank;
155 std::array<uni20::index_type, output_rank> values{};
156 std::size_t output_axis = 0;
157 for (auto const axis : axes.lhs_surviving)
158 values[output_axis++] = static_cast<uni20::index_type>(lhs.extent(axis));
159 for (auto const axis : axes.rhs_surviving)
160 values[output_axis++] = static_cast<uni20::index_type>(rhs.extent(axis));
161
162 return detail::make_contraction_extents(values, std::make_index_sequence<output_rank>{});
163}
164
165} // namespace uni20::linalg
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