Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
contraction_strides.hpp
Go to the documentation of this file.
1#pragma once
2
11#include <uni20/core/types.hpp>
17
18#include <array>
19#include <cstddef>
20#include <optional>
21#include <type_traits>
22#include <utility>
23
24namespace uni20::linalg
25{
26
31template <class Mdspec, std::size_t Rank>
34
36template <class Mdspec, std::size_t Rank>
39
53
55template <
56 std::size_t LhsRank, std::size_t RhsRank, std::size_t ContractedRank,
59[[nodiscard]] auto make_contraction_stride_groups(OutputMdspec const& output, LhsMdspec const& lhs,
60 RhsMdspec const& rhs,
63{
65 auto const required_extents = contraction_output_extents(lhs, rhs, axes);
66 CHECK(uni20::tensor_extents_equal(output.extents(), required_extents),
67 "contraction output extents do not match the surviving input axes");
68
71 if constexpr (axes_type::lhs_surviving_rank > 0)
72 {
73 for (std::size_t index = 0; index < axes.lhs_surviving.size(); ++index)
74 {
75 auto const lhs_axis = axes.lhs_surviving[index];
76 result.m.emplace_back(lhs.extent(lhs_axis), lhs.stride(lhs_axis), output.stride(index));
77 }
78 }
79 if constexpr (axes_type::rhs_surviving_rank > 0)
80 {
81 for (std::size_t index = 0; index < axes.rhs_surviving.size(); ++index)
82 {
83 auto const rhs_axis = axes.rhs_surviving[index];
84 auto const output_axis = axes.lhs_surviving.size() + index;
85 result.n.emplace_back(rhs.extent(rhs_axis), rhs.stride(rhs_axis), output.stride(output_axis));
86 }
87 }
88 if constexpr (ContractedRank > 0)
89 {
90 for (std::size_t index = 0; index < ContractedRank; ++index)
91 {
92 auto const lhs_axis = axes.lhs_contracted[index];
93 auto const rhs_axis = axes.rhs_contracted[index];
94 result.k.emplace_back(lhs.extent(lhs_axis), lhs.stride(lhs_axis), rhs.stride(rhs_axis));
95 }
96 }
97
98 merge_strides_right(result.m);
99 merge_strides_right(result.n);
100 merge_strides_right(result.k);
101 return result;
102}
103
106{
107 std::array<uni20::index_type, 2> extents{};
108 std::array<uni20::index_type, 2> strides{};
109};
110
118
131
132namespace detail::contraction_strides
133{
134
135struct GroupedDimension
136{
137 uni20::index_type extent = 1;
138 std::array<uni20::index_type, 2> strides{1, 1};
139};
140
141template <std::size_t Capacity>
142[[nodiscard]] auto grouped_dimension(static_vector<extent_strides<2>, Capacity> const& group) -> GroupedDimension
143{
144 if constexpr (Capacity == 0)
145 {
146 return {};
147 }
148 else
149 {
150 if (group.empty()) return {};
151 auto result = GroupedDimension{.extent = group[0].extent, .strides = group[0].strides};
152 if (result.extent <= 1) result.strides = {1, 1};
153 return result;
154 }
155}
156
157[[nodiscard]] inline auto grouped_dimension(extent_strides<2> const& dimension) -> GroupedDimension
158{
159 auto result = GroupedDimension{.extent = dimension.extent, .strides = dimension.strides};
160 if (result.extent <= 1) result.strides = {1, 1};
161 return result;
162}
163
164[[nodiscard]] inline bool projection_is_directly_strided(ContractionMatrixProjection const& projection) noexcept
165{
166 // Current no-copy GEMM projections require strictly positive strides.
167 // Negative strides need origin rebasing, while zero normally represents a
168 // broadcast dimension. A zero-sized layout_right operand can also report a
169 // zero surviving-axis stride: K == 0 is mathematically valid, but this
170 // planner deliberately declines that representation. The current host
171 // backend list then falls through to CPU reference, which applies C = beta*C
172 // without reading either input.
173 for (std::size_t axis = 0; axis < 2; ++axis)
174 {
175 if (projection.extents[axis] < 0 || projection.strides[axis] <= 0) return false;
176 }
177 return projection.strides[0] == 1 || projection.strides[1] == 1;
178}
179
180[[nodiscard]] inline auto try_make_direct_gemm_plan(GroupedDimension const& m, GroupedDimension const& n,
181 GroupedDimension const& k)
182 -> std::optional<DirectContractionGemmPlan>
183{
184 DirectContractionGemmPlan plan{.output = {.extents = {m.extent, n.extent}, .strides = {m.strides[1], n.strides[1]}},
185 .lhs = {.extents = {m.extent, k.extent}, .strides = {m.strides[0], k.strides[0]}},
186 .rhs = {.extents = {k.extent, n.extent}, .strides = {k.strides[1], n.strides[0]}}};
187 if (!projection_is_directly_strided(plan.output) || !projection_is_directly_strided(plan.lhs) ||
188 !projection_is_directly_strided(plan.rhs))
189 return std::nullopt;
190 return plan;
191}
192
193template <class Mdspec> consteval bool can_offset_contraction_mdspec()
194{
195 using span_type = std::remove_cvref_t<Mdspec>;
196 if constexpr (uni20::MdspanLike<span_type>)
197 {
198 using accessor_type = typename span_type::accessor_type;
199 using offset_accessor_type = typename accessor_type::offset_policy;
200 return std::same_as<typename offset_accessor_type::element_type, typename span_type::element_type> &&
201 std::constructible_from<offset_accessor_type, accessor_type const&>&&
202 requires(span_type & span, span_offset_t<accessor_type> offset)
203 {
204 {span.accessor().offset(span.data_handle(), offset)}
205 ->std::convertible_to<typename offset_accessor_type::data_handle_type>;
206 };
207 }
208 else
209 {
210 using descriptor_type = typename span_type::data_descriptor_type;
211 return requires(descriptor_type const& descriptor, std::size_t offset) {
212 { descriptor.offset_by(offset) } -> std::same_as<descriptor_type>;
213 };
214 }
215}
216
217} // namespace detail::contraction_strides
218
223template <
224 std::size_t LhsRank, std::size_t RhsRank, std::size_t ContractedRank,
225 MutableRankedContractionMdspecLike<ContractionAxes<LhsRank, RhsRank, ContractedRank>::output_rank> OutputMdspec,
226 RankedContractionMdspecLike<LhsRank> LhsMdspec, RankedContractionMdspecLike<RhsRank> RhsMdspec>
227[[nodiscard]] auto try_make_direct_contraction_gemm_plan(OutputMdspec const& output, LhsMdspec const& lhs,
228 RhsMdspec const& rhs,
230 -> std::optional<DirectContractionGemmPlan>
231{
232 if (!output.mapping().is_unique() || !lhs.mapping().is_unique() || !rhs.mapping().is_unique()) return std::nullopt;
233
234 auto groups = make_contraction_stride_groups(output, lhs, rhs, axes);
235 if (groups.m.size() > 1 || groups.n.size() > 1 || groups.k.size() > 1) return std::nullopt;
236
237 auto const m = detail::contraction_strides::grouped_dimension(groups.m);
238 auto const n = detail::contraction_strides::grouped_dimension(groups.n);
239 auto const k = detail::contraction_strides::grouped_dimension(groups.k);
240
241 return detail::contraction_strides::try_make_direct_gemm_plan(m, n, k);
242}
243
252template <
253 std::size_t LhsRank, std::size_t RhsRank, std::size_t ContractedRank,
254 MutableRankedContractionMdspecLike<ContractionAxes<LhsRank, RhsRank, ContractedRank>::output_rank> OutputMdspec,
255 RankedContractionMdspecLike<LhsRank> LhsMdspec, RankedContractionMdspecLike<RhsRank> RhsMdspec>
256[[nodiscard]] auto try_make_looped_contraction_gemm_plan(OutputMdspec const& output, LhsMdspec const& lhs,
257 RhsMdspec const& rhs,
259 -> std::optional<LoopedContractionGemmPlan>
260{
261 if (!output.mapping().is_unique() || !lhs.mapping().is_unique() || !rhs.mapping().is_unique()) return std::nullopt;
262
263 auto groups = make_contraction_stride_groups(output, lhs, rhs, axes);
264 if (groups.k.size() > 1) return std::nullopt;
265
266 bool const loop_m = groups.m.size() == 2 && groups.n.size() <= 1;
267 bool const loop_n = groups.n.size() == 2 && groups.m.size() <= 1;
268 if (loop_m == loop_n) return std::nullopt;
269
270 auto const k = detail::contraction_strides::grouped_dimension(groups.k);
271 std::optional<LoopedContractionGemmPlan> result;
272 for (std::size_t loop_index = 0; loop_index < 2; ++loop_index)
273 {
274 std::size_t const matrix_index = 1 - loop_index;
275 auto const& loop = loop_m ? groups.m[loop_index] : groups.n[loop_index];
276 if (loop.extent < 0 || loop.strides[0] < 0 || loop.strides[1] < 0) continue;
277
278 auto const m = loop_m ? detail::contraction_strides::grouped_dimension(groups.m[matrix_index])
279 : detail::contraction_strides::grouped_dimension(groups.m);
280 auto const n = loop_n ? detail::contraction_strides::grouped_dimension(groups.n[matrix_index])
281 : detail::contraction_strides::grouped_dimension(groups.n);
282 auto gemm = detail::contraction_strides::try_make_direct_gemm_plan(m, n, k);
283 if (!gemm) continue;
284
286 .loop_extent = loop.extent,
287 .output_offset_stride = loop.strides[1],
288 .lhs_offset_stride = loop_m ? loop.strides[0] : 0,
289 .rhs_offset_stride = loop_n ? loop.strides[0] : 0,
290 .gemm = *gemm,
291 };
292 if (!result || candidate.loop_extent < result->loop_extent) result = std::move(candidate);
293 }
294 return result;
295}
296
298template <uni20::MdspanLike Mdspan>
300 stdex::mdspan<typename std::remove_cvref_t<Mdspan>::element_type,
301 stdex::dextents<typename std::remove_cvref_t<Mdspan>::index_type, 2>, stdex::layout_stride,
302 typename std::remove_cvref_t<Mdspan>::accessor_type>;
303
304namespace detail::contraction_strides
305{
306
307template <class Mdspec, bool = uni20::MdspanLike<Mdspec>> struct ContractionMatrixMdspec;
308
309template <class Mdspec> struct ContractionMatrixMdspec<Mdspec, true>
310{
312};
313
314template <class Mdspec> struct ContractionMatrixMdspec<Mdspec, false>
315{
316 using span_type = std::remove_cvref_t<Mdspec>;
318 stdex::layout_stride, typename span_type::accessor_type,
319 typename span_type::data_descriptor_type>;
320};
321
322template <class Mdspec, bool = uni20::MdspanLike<Mdspec>> struct OffsetContractionMatrixMdspec;
323
324template <class Mdspec> struct OffsetContractionMatrixMdspec<Mdspec, true>
325{
326 using span_type = std::remove_cvref_t<Mdspec>;
327 using accessor_type = typename span_type::accessor_type::offset_policy;
328 using type = stdex::mdspan<typename accessor_type::element_type, stdex::dextents<typename span_type::index_type, 2>,
329 stdex::layout_stride, accessor_type>;
330};
331
332template <class Mdspec> struct OffsetContractionMatrixMdspec<Mdspec, false>
333{
334 using type = typename ContractionMatrixMdspec<Mdspec, false>::type;
335};
336
337} // namespace detail::contraction_strides
338
340template <uni20::MdspecLike Mdspec>
342 typename detail::contraction_strides::ContractionMatrixMdspec<std::remove_cvref_t<Mdspec>>::type;
343
345template <uni20::MdspecLike Mdspec>
347 typename detail::contraction_strides::OffsetContractionMatrixMdspec<std::remove_cvref_t<Mdspec>>::type;
348
350template <uni20::MdspanLike Mdspan>
351[[nodiscard]] auto make_contraction_matrix_mdspan(Mdspan& span, ContractionMatrixProjection const& projection)
353{
354 using result_type = contraction_matrix_mdspan_t<Mdspan>;
355 using extents_type = typename result_type::extents_type;
356 using mapping_type = typename result_type::mapping_type;
357 auto const extents = extents_type{projection.extents[0], projection.extents[1]};
358 auto const mapping = mapping_type{extents, projection.strides};
359 return result_type{span.data_handle(), mapping, span.accessor()};
360}
361
366template <uni20::MdspecLike Mdspec>
367[[nodiscard]] auto make_contraction_matrix_mdspec(Mdspec& span, ContractionMatrixProjection const& projection)
369{
370 using result_type = contraction_matrix_mdspec_t<Mdspec>;
371 using extents_type = typename result_type::extents_type;
372 using mapping_type = typename result_type::mapping_type;
373 auto const extents = extents_type{projection.extents[0], projection.extents[1]};
374 auto const mapping = mapping_type{extents, projection.strides};
375 if constexpr (uni20::MdspanLike<Mdspec>)
376 return result_type{span.data_handle(), mapping, span.accessor()};
377 else
378 return result_type{span.data_descriptor(), mapping, span.accessor()};
379}
380
388template <uni20::MdspecLike Mdspec>
389 requires(detail::contraction_strides::can_offset_contraction_mdspec<Mdspec>())
390[[nodiscard]] auto make_offset_contraction_matrix_mdspec(Mdspec& span, ContractionMatrixProjection const& projection,
391 uni20::index_type element_offset)
393{
394 CHECK(element_offset >= 0);
396 using extents_type = typename result_type::extents_type;
397 using mapping_type = typename result_type::mapping_type;
398 auto const extents = extents_type{projection.extents[0], projection.extents[1]};
399 auto const mapping = mapping_type{extents, projection.strides};
400 if constexpr (uni20::MdspanLike<Mdspec>)
401 {
402 using source_accessor_type = typename std::remove_cvref_t<Mdspec>::accessor_type;
403 using offset_accessor_type = typename source_accessor_type::offset_policy;
404 auto const offset = static_cast<span_offset_t<source_accessor_type>>(element_offset);
405 return result_type{span.accessor().offset(span.data_handle(), offset), mapping,
406 offset_accessor_type{span.accessor()}};
407 }
408 else
409 {
410 auto descriptor = span.data_descriptor().offset_by(static_cast<std::size_t>(element_offset));
411 return result_type{std::move(descriptor), mapping, span.accessor()};
412 }
413}
414
415} // namespace uni20::linalg
A fixed-capacity vector with stack storage and std::vector-like API.
Definition static_vector.hpp:24
Store a data descriptor with the mapping and accessor for an eventual mdspan.
Definition mdspec.hpp:31
A readable type implementing the mdspan observers and multidimensional subscript contract.
Definition mdspan/concepts.hpp:291
Mutable mdspec-like type with a specified static rank.
Definition mdspan/concepts.hpp:487
Mutable mdspec-like type whose mapping is always strided.
Definition mdspan/concepts.hpp:440
An mdspec-like type with a specified static rank.
Definition mdspan/concepts.hpp:454
An mdspec-like type whose mapping exposes multidimensional strides.
Definition mdspan/concepts.hpp:427
Mutable ranked mdspec usable as a contraction output.
Definition contraction_strides.hpp:37
Ranked mdspec usable by contraction stride grouping.
Definition contraction_strides.hpp:32
Normalized axis metadata for fixed-rank pairwise tensor contractions.
auto strides(S const &s)
Retrieve the strides associated with a strided mdspec-like type.
Definition mdspan/concepts.hpp:558
static_vector< extent_strides< 2 >, R > merge_strides_right(Extents const &ext, std::array< std::ptrdiff_t, R > const &Stride1, std::array< std::ptrdiff_t, R > const &Stride2)
Build and merge stride metadata from two stride arrays favouring row-major (right) order.
Definition strides.hpp:240
typename span_offset_type< AP >::type span_offset_t
Convenience alias for accessor_offset_type<AP>::type.
Definition mdspan/concepts.hpp:48
Mdspan concept and accessor extensions for Uni20.
Configured gateway to Uni20's mdspan implementation.
Mdspan-shaped metadata for storage whose data handle requires acquisition.
Definition linalg/async.hpp:28
auto make_contraction_matrix_mdspec(Mdspec &span, ContractionMatrixProjection const &projection) -> contraction_matrix_mdspec_t< Mdspec >
Project an mdspec into one rank-two GEMM operand without acquiring its handle.
Definition contraction_strides.hpp:367
void gemm(BackendSelector selector, async::Async< OutputTensor > &output, Alpha &&alpha, async::Async< LhsTensor > const &lhs, async::Async< RhsTensor > const &rhs, Beta &&beta, std::optional< Precision > p={})
Schedule fixed-output GEMM with an explicit backend selector.
Definition async/matrix_product.hpp:162
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
auto make_contraction_stride_groups(OutputMdspec const &output, LhsMdspec const &lhs, RhsMdspec const &rhs, ContractionAxes< LhsRank, RhsRank, ContractedRank > const &axes) -> ContractionStrideGroups< LhsRank, RhsRank, ContractedRank >
Build jointly merged M, N, and K stride groups without acquiring data handles.
Definition contraction_strides.hpp:59
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
stdex::mdspan< typename std::remove_cvref_t< Mdspan >::element_type, stdex::dextents< typename std::remove_cvref_t< Mdspan >::index_type, 2 >, stdex::layout_stride, typename std::remove_cvref_t< Mdspan >::accessor_type > contraction_matrix_mdspan_t
Rank-two layout_stride mdspan type retaining a resolved operand accessor.
Definition contraction_strides.hpp:302
typename detail::contraction_strides::ContractionMatrixMdspec< std::remove_cvref_t< Mdspec > >::type contraction_matrix_mdspec_t
Rank-two strided metadata type retaining an operand descriptor or immediate handle.
Definition contraction_strides.hpp:342
auto try_make_looped_contraction_gemm_plan(OutputMdspec const &output, LhsMdspec const &lhs, RhsMdspec const &rhs, ContractionAxes< LhsRank, RhsRank, ContractedRank > const &axes) -> std::optional< LoopedContractionGemmPlan >
Try to leave one residual M or N dimension around a GEMM projection.
Definition contraction_strides.hpp:256
auto try_make_direct_contraction_gemm_plan(OutputMdspec const &output, LhsMdspec const &lhs, RhsMdspec const &rhs, ContractionAxes< LhsRank, RhsRank, ContractedRank > const &axes) -> std::optional< DirectContractionGemmPlan >
Try to collapse merged contraction groups to three rank-two GEMM operands.
Definition contraction_strides.hpp:227
auto make_offset_contraction_matrix_mdspec(Mdspec &span, ContractionMatrixProjection const &projection, uni20::index_type element_offset) -> offset_contraction_matrix_mdspec_t< Mdspec >
Project a rank-two mdspec slice at one nonnegative element offset.
Definition contraction_strides.hpp:390
typename detail::contraction_strides::OffsetContractionMatrixMdspec< std::remove_cvref_t< Mdspec > >::type offset_contraction_matrix_mdspec_t
Rank-two strided metadata type whose storage origin may be advanced.
Definition contraction_strides.hpp:347
auto make_contraction_matrix_mdspan(Mdspan &span, ContractionMatrixProjection const &projection) -> contraction_matrix_mdspan_t< Mdspan >
Project a resolved mdspan into one rank-two GEMM operand without changing storage.
Definition contraction_strides.hpp:351
std::ptrdiff_t index_type
Definition types.hpp:19
constexpr bool tensor_extents_equal(LhsExtents const &lhs, RhsExtents const &rhs) noexcept
Compare two tensor extent objects by rank and per-axis extent.
Definition tensor/output.hpp:31
std::size_t span
Definition presentation.cpp:2017
Defines static_vector, a fixed-capacity, stack-allocated vector with a std::vector-like API.
Stride utilities for mdspan-like tensors.
Structure to represent a common extent and an array of strides.
Definition strides.hpp:28
Canonical contracted and surviving axes for two fixed-rank operands.
Definition contraction_axes.hpp:26
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 contracted_rank
Definition contraction_axes.hpp:32
Extents and strides for one rank-two projection of a contraction operand.
Definition contraction_strides.hpp:106
std::array< uni20::index_type, 2 > strides
Definition contraction_strides.hpp:108
std::array< uni20::index_type, 2 > extents
Definition contraction_strides.hpp:107
M, N, and K stride groups for a normalized pairwise contraction.
Definition contraction_strides.hpp:46
static_vector< extent_strides< 2 >, axes_type::lhs_surviving_rank > m
Definition contraction_strides.hpp:49
static_vector< extent_strides< 2 >, axes_type::rhs_surviving_rank > n
Definition contraction_strides.hpp:50
static_vector< extent_strides< 2 >, axes_type::contracted_rank > k
Definition contraction_strides.hpp:51
Handle-independent projection of a contraction to one GEMM.
Definition contraction_strides.hpp:113
ContractionMatrixProjection rhs
Definition contraction_strides.hpp:116
ContractionMatrixProjection output
Definition contraction_strides.hpp:114
ContractionMatrixProjection lhs
Definition contraction_strides.hpp:115
One residual M or N loop around a rank-two GEMM projection.
Definition contraction_strides.hpp:124
uni20::index_type output_offset_stride
Definition contraction_strides.hpp:126
DirectContractionGemmPlan gemm
Definition contraction_strides.hpp:129
uni20::index_type rhs_offset_stride
Definition contraction_strides.hpp:128
uni20::index_type lhs_offset_stride
Definition contraction_strides.hpp:127
uni20::index_type loop_extent
Definition contraction_strides.hpp:125
#define CHECK(cond,...)
Definition trace.hpp:299