Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
zip_layout.hpp
Go to the documentation of this file.
1#pragma once
2
12
13#include <algorithm>
14#include <array>
15#include <cassert>
16#include <cstddef>
17#include <initializer_list>
18#include <tuple>
19#include <type_traits>
20#include <utility>
21
22namespace uni20
23{
24
25namespace detail
26{
27
28// Helper function to merge extents. If the extents are different, then one of them must be dynamic,
29// otherwise they are not compatible. If one is dynamic and one is static, then return the static extent.
30
31template <std::size_t A, std::size_t B> static constexpr std::size_t merge_extent()
32{
33 if constexpr (A == B)
34 return A;
35 else if constexpr (A == stdex::dynamic_extent)
36 return B;
37 else if constexpr (B == stdex::dynamic_extent)
38 return A;
39 else
40 static_assert(A == B || A == stdex::dynamic_extent || B == stdex::dynamic_extent, "Incompatible static extents");
41 return 0;
42}
43
44// Primary template: merge_pack over a pack of N extents
45template <std::size_t... Ns> struct merge_pack;
46
47// Base case: single value
48template <std::size_t A> struct merge_pack<A>
49{
50 static constexpr std::size_t value = A;
51};
52
53// Recursive case: merge first two, then fold in the rest
54template <std::size_t A, std::size_t B, std::size_t... Rest> struct merge_pack<A, B, Rest...>
55{
56 static constexpr std::size_t value = merge_pack<merge_extent<A, B>(), Rest...>::value;
57};
58
59// Given a set of mdspans, determine the common extents using the merge_extent() function above
60template <typename FirstSpan, typename... OtherSpans> struct common_extents
61{
62 static constexpr std::size_t NumSpans = 1 + sizeof...(OtherSpans);
63
64 // 1) All spans must have the same rank R
65 static constexpr std::size_t R = FirstSpan::rank();
66 static_assert((... && (OtherSpans::rank() == R)), "common_extents: ranks must match");
67
68 using index_type = FirstSpan::index_type;
69
70 private:
71 // Fold-merge the static extents at dimension I
72 template <std::size_t I> static constexpr std::size_t merged_static_extent()
73 {
74 // build a pack of the static-extent<I> of each Span
75 return merge_pack<FirstSpan::extents_type::static_extent(I),
76 OtherSpans::extents_type::static_extent(I)...>::value;
77 }
78
79 // Build the extents type by expanding merged_static_extent<Is>()...
80 template <std::size_t... Is> static constexpr auto make_extents(std::index_sequence<Is...>)
81 {
82 return stdex::extents<index_type,
83 merged_static_extent<Is>()... // one per dimension
84 >{};
85 }
86
87 public:
89 using type = decltype(make_extents(std::make_index_sequence<R>{}));
90
91 private:
92 template <std::size_t... Is>
93
94 static type make_impl(std::index_sequence<Is...>, FirstSpan const& first, OtherSpans const&... otherspans)
95 {
96 // Construct the extents object. All of the extents of each span must agree.
97 type ext{first.extent(Is)...};
98
99 auto do_check = [&](auto const& sp) { assert(ext == sp.extents()); };
100
101 (do_check(otherspans), ...);
102
103 return ext;
104 }
105
106 public:
109 static type make(FirstSpan const& firstspan, OtherSpans const&... otherspans)
110 {
111 return make_impl(std::make_index_sequence<R>{}, firstspan, otherspans...);
112 }
113};
114
115} // namespace detail
116
120template <typename... Spans> using common_extents_t = typename detail::common_extents<Spans...>::type;
121
127template <typename... Spans> auto make_common_extents(Spans... spans)
128{
129 return detail::common_extents<Spans...>::make(spans...);
130}
131
132namespace detail
133{
134
135template <typename T, std::size_t N1, std::size_t N2, std::size_t... I, std::size_t... J>
136UNI20_HOST_DEVICE constexpr std::array<T, N1 + N2> concat_impl(const std::array<T, N1>& a1, const std::array<T, N2>& a2,
137 std::index_sequence<I...>, std::index_sequence<J...>)
138{
139 // pack-expand a1[I]..., then a2[J]...
140 return {{a1[I]..., a2[J]...}};
141}
142
143template <typename T, std::size_t N, std::size_t... I>
144UNI20_HOST_DEVICE constexpr std::array<T, N + 1> concat_impl(const std::array<T, N>& a, const T& x,
145 std::index_sequence<I...>)
146{
147 return {{a[I]..., x}};
148}
149
150template <typename T, std::size_t N, std::size_t... I>
151UNI20_HOST_DEVICE constexpr std::array<T, N + 1> concat_impl(const T& x, const std::array<T, N>& a,
152 std::index_sequence<I...>)
153{
154 return {{x, a[I]...}};
155}
156
157} // namespace detail
158
167template <typename T, std::size_t N1, std::size_t N2>
168UNI20_HOST_DEVICE constexpr std::array<T, N1 + N2> concat(const std::array<T, N1>& a1, const std::array<T, N2>& a2)
169{
170 return detail::concat_impl(a1, a2, std::make_index_sequence<N1>{}, std::make_index_sequence<N2>{});
171}
172
180template <typename T, std::size_t N>
181UNI20_HOST_DEVICE constexpr std::array<T, N + 1> concat(const T& x, const std::array<T, N>& a)
182{
183 return detail::concat_impl(x, a, std::make_index_sequence<N>{});
184}
185
193template <typename T, std::size_t N>
194UNI20_HOST_DEVICE constexpr std::array<T, N + 1> concat(const std::array<T, N>& a, const T& x)
195{
196 return detail::concat_impl(a, x, std::make_index_sequence<N>{});
197}
198
199namespace detail
200{
201
202template <typename IndexType, std::size_t Rank, typename Mapping, std::size_t... I>
203UNI20_HOST_DEVICE constexpr std::array<IndexType, Rank> mapping_strides_impl(Mapping const& mapping,
204 std::index_sequence<I...>)
205{
206 return {static_cast<IndexType>(mapping.stride(I))...};
207}
208
209template <typename IndexType, std::size_t Rank, typename Mapping>
210UNI20_HOST_DEVICE constexpr std::array<IndexType, Rank> mapping_strides(Mapping const& mapping)
211{
212 return mapping_strides_impl<IndexType, Rank>(mapping, std::make_index_sequence<Rank>{});
213}
214
215// build a std::tuple of N default-constructed T’s
216template <typename T, std::size_t... I> constexpr auto make_tuple_n_impl(std::index_sequence<I...>)
217{
218 // the comma-expression (static_cast<void>(I), T{}) ensures we produce T{} N times
219 return std::tuple{(static_cast<void>(I), T{})...};
220}
221
222} // namespace detail
223
228template <typename T, std::size_t N>
229using tuple_n = decltype(detail::make_tuple_n_impl<T>(std::make_index_sequence<N>{}));
230
234template <std::size_t NumSpans> struct StridedZipLayout
235{
236 static_assert(NumSpans >= 1, "StridedZipLayout requires at least one span");
237
239 static constexpr std::size_t num_spans = NumSpans;
240
244 template <typename Ext> struct mapping
245 {
247 using extents_type = Ext;
248 using index_type = typename Ext::index_type;
249 using rank_type = typename Ext::rank_type;
250
252
254
257 static constexpr std::size_t num_spans = NumSpans;
258
261 UNI20_HOST_DEVICE static constexpr bool is_always_unique() noexcept { return true; }
264 UNI20_HOST_DEVICE static constexpr bool is_always_exhaustive() noexcept { return false; }
267 UNI20_HOST_DEVICE static constexpr bool is_always_strided() noexcept { return false; }
268
271 [[nodiscard]] UNI20_HOST_DEVICE constexpr bool is_unique() const noexcept { return true; }
274 [[nodiscard]] UNI20_HOST_DEVICE constexpr bool is_exhaustive() const noexcept { return false; }
275
276 // std::size_t required_span_size() const does not make sense for a StridedZipLayout
277
281 [[nodiscard]] UNI20_HOST_DEVICE constexpr bool is_strided() const noexcept
282 {
283 for (std::size_t d = 0; d < Ext::rank(); ++d)
284 {
285 auto s0 = all_strides_[0][d];
286 for (std::size_t i = 1; i < NumSpans; ++i)
287 {
288 if (all_strides_[i][d] != s0) return false;
289 }
290 }
291 return true;
292 }
293
297 [[nodiscard]] UNI20_HOST_DEVICE constexpr std::array<index_type, Ext::rank()> strides() const noexcept
298 {
299 assert(this->is_strided());
300 return all_strides_[0];
301 }
302
308 [[nodiscard]] UNI20_HOST_DEVICE constexpr index_type stride(rank_type r) const noexcept
309 {
310 assert(this->is_strided());
311 return all_strides_[0][r];
312 }
313
317 [[nodiscard]] UNI20_HOST_DEVICE std::array<std::array<index_type, Ext::rank()>, num_spans> const&
318 all_strides() const noexcept
319 {
320 return all_strides_;
321 }
322
323 template <typename... Mappings>
328 UNI20_HOST_DEVICE constexpr mapping(extents_type const& exts, Mappings... maps) noexcept
329 : extents_(exts), all_strides_{detail::mapping_strides<index_type, Ext::rank()>(maps)...}
330 {}
331
337 extents_type const& exts,
338 std::array<std::array<index_type, Ext::rank()>, NumSpans> const& strides_pack) noexcept
339 : extents_(exts), all_strides_(strides_pack)
340 {}
341
345 template <typename OtherExt>
347 std::array<index_type, Ext::rank()> const& new_strides,
349 : extents_(other.extents()), all_strides_(concat(new_strides, other.all_strides()))
350 {}
351
355 template <typename OtherExt>
358 std::array<index_type, Ext::rank()> const& new_strides) noexcept
359 : extents_(other.extents()), all_strides_(concat(other.all_strides(), new_strides))
360 {}
361
366 template <typename LeftMap, typename RightMap>
367 requires(LeftMap::num_spans + RightMap::num_spans == num_spans)
368 UNI20_HOST_DEVICE constexpr mapping(LeftMap const& left, RightMap const& right) noexcept
369 : extents_(left.extents()), all_strides_(concat(left.all_strides(), right.all_strides()))
370 {}
371
375 [[nodiscard]] UNI20_HOST_DEVICE constexpr extents_type const& extents() const noexcept { return extents_; }
376
382 template <typename... Idx> UNI20_HOST_DEVICE constexpr offset_type operator()(Idx... idxs) const noexcept
383 {
384 static_assert(sizeof...(Idx) == Ext::rank(), "Wrong number of indices");
385 // pack the multi-index into an array
386 std::array<index_type, Ext::rank()> ix{static_cast<index_type>(idxs)...};
387 // dispatch over spans
388 return offset_impl(ix, std::make_index_sequence<NumSpans>{});
389 }
390
391 private:
392 // For a given span S, compute its offset by folding over all dims D:
393 template <std::size_t S, std::size_t... D>
394 [[nodiscard]] UNI20_HOST_DEVICE constexpr index_type span_offset(std::array<index_type, Ext::rank()> const& ix,
395 std::index_sequence<D...>) const noexcept
396 {
397 // fold: 0 + (stride[S][0]*ix[0]) + (stride[S][1]*ix[1]) + …
398 return ((all_strides_[S][D] * ix[D]) + ... + index_type(0));
399 }
400
401 // Build the tuple of all span-offsets in one go:
402 template <std::size_t... S>
403 [[nodiscard]] UNI20_HOST_DEVICE constexpr offset_type offset_impl(std::array<index_type, Ext::rank()> const& ix,
404 std::index_sequence<S...>) const noexcept
405 {
406 // pack-expand: span_offset<0>(ix), span_offset<1>(ix), …
407 return offset_type{span_offset<S>(ix, std::make_index_sequence<Ext::rank()>{})...};
408 }
409
410 extents_type extents_;
411 std::array<std::array<index_type, Ext::rank()>, num_spans> all_strides_;
412 };
413};
414
419template <typename... Layouts> struct GeneralZipLayout
420{
421 template <typename Extents> struct mapping
422 {
424 using extents_type = Extents;
425 using index_type = typename Extents::index_type;
426 using rank_type = typename Extents::rank_type;
427 UNI20_HOST_DEVICE static constexpr rank_type rank() noexcept { return Extents::rank(); }
428 UNI20_HOST_DEVICE static constexpr rank_type rank_dynamic() noexcept { return Extents::rank_dynamic(); }
429 using offset_type = std::tuple<span_offset_t<typename Layouts::template mapping<Extents>>...>;
431
434 UNI20_HOST_DEVICE static constexpr bool is_always_unique() noexcept { return true; }
437 UNI20_HOST_DEVICE static constexpr bool is_always_exhaustive() noexcept { return false; }
440 UNI20_HOST_DEVICE static constexpr bool is_always_strided() noexcept { return false; }
441
446 UNI20_HOST_DEVICE explicit constexpr mapping(Extents const& ext,
447 typename Layouts::template mapping<Extents> const&... m) noexcept
448 : extents_(ext), impls_{m...}
449 {}
450
454 [[nodiscard]] UNI20_HOST_DEVICE constexpr extents_type const& extents() const noexcept { return extents_; }
455
459 [[nodiscard]] UNI20_HOST_DEVICE constexpr std::size_t required_span_size() const noexcept
460 {
461 std::size_t max_sz = 0;
462 std::apply(
463 [&](auto const&... m) {
464 (void)std::initializer_list<int>{
465 (max_sz = std::max<std::size_t>(max_sz, m.required_span_size()), 0)...};
466 },
467 impls_);
468 return max_sz;
469 }
470
476 template <typename... Idx> UNI20_HOST_DEVICE constexpr offset_type operator()(Idx... idxs) const noexcept
477 {
478 return std::apply([&](auto const&... m) { return offset_type{m(idxs...)...}; }, impls_);
479 }
480
485 UNI20_HOST_DEVICE constexpr bool operator==(mapping const& o) const noexcept
486 {
487 return extents_ == o.extents_ && impls_ == o.impls_;
488 }
493 UNI20_HOST_DEVICE constexpr bool operator!=(mapping const& o) const noexcept { return !(*this == o); }
494
495 private:
496 extents_type extents_;
497 std::tuple<typename Layouts::template mapping<Extents>...> impls_;
498 };
499};
500
504template <typename... Spans> struct zip_layout_selector
505{
506 using type = GeneralZipLayout<typename Spans::layout_type...>;
507};
508
512template <StridedMdspanLike... Spans> struct zip_layout_selector<Spans...>
513{
514 using type = StridedZipLayout<sizeof...(Spans)>;
515};
516
520template <typename... Spans> using zip_layout_t = typename zip_layout_selector<Spans...>::type;
521
522} // namespace uni20
Project compiler annotations shared by host and device code.
#define UNI20_HOST_DEVICE
Definition compiler_attributes.hpp:12
An mdspan-like type whose mapping is always strided and exposes stride observers.
Definition mdspan/concepts.hpp:406
locale_t value
Definition data_table.cpp:49
auto make_common_extents(Spans... spans)
Build an extents object representing the common shape of the given spans.
Definition zip_layout.hpp:127
decltype(detail::make_tuple_n_impl< T >(std::make_index_sequence< N >{})) tuple_n
Alias: tuple_n<T,N> is std::tuple<T,T,…> with N repetitions of T.
Definition zip_layout.hpp:229
typename detail::common_extents< Spans... >::type common_extents_t
Convenience alias that represents the common extents of a set of mdspans.
Definition zip_layout.hpp:120
UNI20_HOST_DEVICE constexpr std::array< T, N1+N2 > concat(const std::array< T, N1 > &a1, const std::array< T, N2 > &a2)
Concatenate two std::array instances into one (constexpr helper).
Definition zip_layout.hpp:168
UNI20_HOST_DEVICE constexpr mapping(LeftMap const &left, RightMap const &right) noexcept
Merge two sub-mappings whose span counts sum to NumSpans.
Definition zip_layout.hpp:368
static UNI20_HOST_DEVICE constexpr bool is_always_unique() noexcept
Always unique for uni20 tensors.
Definition zip_layout.hpp:261
UNI20_HOST_DEVICE constexpr extents_type const & extents() const noexcept
Return the extents of the layout.
Definition zip_layout.hpp:375
UNI20_HOST_DEVICE constexpr mapping(extents_type const &exts, std::array< std::array< index_type, Ext::rank()>, NumSpans > const &strides_pack) noexcept
Construct from shared extents and per-span raw strides.
Definition zip_layout.hpp:336
UNI20_HOST_DEVICE constexpr index_type stride(rank_type r) const noexcept
Return the common stride in dimension r.
Definition zip_layout.hpp:308
UNI20_HOST_DEVICE constexpr bool is_strided() const noexcept
True if every dimension uses the same stride across all spans.
Definition zip_layout.hpp:281
UNI20_HOST_DEVICE constexpr mapping(extents_type const &exts, Mappings... maps) noexcept
Construct from extents and existing child mappings.
Definition zip_layout.hpp:328
UNI20_HOST_DEVICE constexpr bool operator!=(mapping const &o) const noexcept
Compare two mappings for inequality.
Definition zip_layout.hpp:493
UNI20_HOST_DEVICE constexpr bool is_exhaustive() const noexcept
Never exhaustive at runtime.
Definition zip_layout.hpp:274
static UNI20_HOST_DEVICE constexpr bool is_always_exhaustive() noexcept
Never exhaustive: no single contiguous backing buffer.
Definition zip_layout.hpp:437
UNI20_HOST_DEVICE constexpr std::array< index_type, Ext::rank()> strides() const noexcept
Return the common strides for each dimension.
Definition zip_layout.hpp:297
UNI20_HOST_DEVICE constexpr extents_type const & extents() const noexcept
Access the common extents.
Definition zip_layout.hpp:454
UNI20_HOST_DEVICE constexpr offset_type operator()(Idx... idxs) const noexcept
Compute per-span linear offsets for a multi-index.
Definition zip_layout.hpp:382
UNI20_HOST_DEVICE std::array< std::array< index_type, Ext::rank()>, num_spans > const & all_strides() const noexcept
Retrieve the 2-D array of all per-span strides.
Definition zip_layout.hpp:318
UNI20_HOST_DEVICE constexpr bool is_unique() const noexcept
Always unique at runtime.
Definition zip_layout.hpp:271
static UNI20_HOST_DEVICE constexpr bool is_always_unique() noexcept
Always unique for uni20 tensors.
Definition zip_layout.hpp:434
UNI20_HOST_DEVICE constexpr mapping(typename StridedZipLayout< num_spans - 1 >::template mapping< OtherExt > const &other, std::array< index_type, Ext::rank()> const &new_strides) noexcept
Append one span’s strides after an existing mapping of N–1 spans.
Definition zip_layout.hpp:356
UNI20_HOST_DEVICE constexpr offset_type operator()(Idx... idxs) const noexcept
Compute the per-layout offsets for a given multi-index.
Definition zip_layout.hpp:476
static UNI20_HOST_DEVICE constexpr bool is_always_strided() noexcept
Statically unknown if strided: may differ per dimension.
Definition zip_layout.hpp:267
static UNI20_HOST_DEVICE constexpr bool is_always_exhaustive() noexcept
Never exhaustive: no single contiguous backing buffer.
Definition zip_layout.hpp:264
UNI20_HOST_DEVICE constexpr std::size_t required_span_size() const noexcept
Return the maximum required span size among the child mappings.
Definition zip_layout.hpp:459
UNI20_HOST_DEVICE constexpr mapping(Extents const &ext, typename Layouts::template mapping< Extents > const &... m) noexcept
Construct child mappings.
Definition zip_layout.hpp:446
static UNI20_HOST_DEVICE constexpr bool is_always_strided() noexcept
Not strided.
Definition zip_layout.hpp:440
UNI20_HOST_DEVICE constexpr bool operator==(mapping const &o) const noexcept
Compare two mappings for equality.
Definition zip_layout.hpp:485
typename zip_layout_selector< Spans... >::type zip_layout_t
Alias to extract the chosen layout policy for the given spans.
Definition zip_layout.hpp:520
static constexpr std::size_t num_spans
Number of spans in this zip layout.
Definition zip_layout.hpp:257
UNI20_HOST_DEVICE constexpr mapping(std::array< index_type, Ext::rank()> const &new_strides, typename StridedZipLayout< num_spans - 1 >::template mapping< OtherExt > const &other) noexcept
Prepend one span’s strides before an existing mapping of N–1 spans.
Definition zip_layout.hpp:346
Mdspan concept and accessor extensions for Uni20.
Configured gateway to Uni20's mdspan implementation.
Definition async/async.hpp:26
std::ptrdiff_t index_type
Definition types.hpp:19
Definition zip_layout.hpp:422
std::tuple< span_offset_t< typename Layouts::template mapping< Extents > >... > offset_type
Definition zip_layout.hpp:429
static UNI20_HOST_DEVICE constexpr rank_type rank_dynamic() noexcept
Definition zip_layout.hpp:428
typename Extents::rank_type rank_type
Definition zip_layout.hpp:426
Extents extents_type
Definition zip_layout.hpp:424
static UNI20_HOST_DEVICE constexpr rank_type rank() noexcept
Definition zip_layout.hpp:427
typename Extents::index_type index_type
Definition zip_layout.hpp:425
Fallback zip layout for arbitrary mapping policies.
Definition zip_layout.hpp:420
Nested mapping policy for extents Ext, modeling LayoutMappingPolicy.
Definition zip_layout.hpp:245
typename Ext::rank_type rank_type
Definition zip_layout.hpp:249
tuple_n< index_type, NumSpans > offset_type
Definition zip_layout.hpp:251
Ext extents_type
multidimensional shape
Definition zip_layout.hpp:247
typename Ext::index_type index_type
Definition zip_layout.hpp:248
Zip layout for NumSpans strided spans (all using layout_stride).
Definition zip_layout.hpp:235
static constexpr std::size_t num_spans
Number of spans in this zip layout.
Definition zip_layout.hpp:239
Picks the right zip layout for a pack of mdspan types.
Definition zip_layout.hpp:505