dune-common 2.12-git
Loading...
Searching...
No Matches
impl/submdspan.hh
Go to the documentation of this file.
1// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2// vi: set et ts=4 sw=2 sts=2:
3// SPDX-FileCopyrightInfo: Copyright © DUNE Project contributors, see file LICENSE.md in module root
4// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception
5#ifndef DUNE_COMMON_STD_IMPL_SUBMDSPAN_HH
6#define DUNE_COMMON_STD_IMPL_SUBMDSPAN_HH
7
8#include <array>
9#include <span>
10#include <tuple>
11#include <type_traits>
12#include <utility>
13
23
24namespace Dune::Std::Impl {
25
26// Treat std::integral_constant slice values as their encoded value and all
27// other slice values as runtime values.
28template <class T>
29inline constexpr bool is_integral_constant_v = Dune::IsIntegralConstant<T>::value;
30
31template <class T>
32constexpr auto slice_index (T value)
33{
34 if constexpr(is_integral_constant_v<T>)
35 return T::value;
36 else
37 return value;
38}
39
40// Classification of the slice specifiers supported by the Dune backport.
41// "Kept" slices contribute one dimension to the result; integral slices fix a
42// coordinate and remove that dimension.
43template <class T>
44struct IsFullExtent : std::is_same<std::remove_cv_t<T>,full_extent_t> {};
45
46template <class T>
47inline constexpr bool is_full_extent_v = IsFullExtent<T>::value;
48
49template <class T>
50struct IsExtentSlice : std::false_type {};
51
52template <class O, class E, class S>
53struct IsExtentSlice<extent_slice<O,E,S>> : std::true_type {};
54
55template <class T>
56inline constexpr bool is_extent_slice_v = IsExtentSlice<std::remove_cv_t<T>>::value;
57
58template <class T>
59struct IsRangeSlice : std::false_type {};
60
61template <class F, class L, class S>
62struct IsRangeSlice<range_slice<F,L,S>> : std::true_type {};
63
64template <class T>
65inline constexpr bool is_range_slice_v = IsRangeSlice<std::remove_cv_t<T>>::value;
66
67template <class T, class = void>
68struct IsPairLike : std::false_type {};
69
70template <class T>
71struct IsPairLike<T, std::void_t<
72 decltype(std::tuple_size<std::remove_cv_t<T>>::value),
73 typename std::tuple_element<0,std::remove_cv_t<T>>::type,
74 typename std::tuple_element<1,std::remove_cv_t<T>>::type>>
75 : std::bool_constant<(std::tuple_size<std::remove_cv_t<T>>::value == 2)> {};
76
77template <class T>
78inline constexpr bool is_pair_like_v = IsPairLike<T>::value;
79
80template <class Slice>
81inline constexpr bool is_kept_slice_v =
82 is_full_extent_v<Slice> || is_extent_slice_v<Slice> || is_range_slice_v<Slice> || is_pair_like_v<Slice>;
83
84// Slices that keep a dimension and do not change the stride of that dimension.
85// These are the slice forms that can preserve a padded layout for submatrix views.
86template <class Slice, class = void>
87struct IsContiguousKeptSlice : IsFullExtent<Slice> {};
88
89template <class T>
90struct IsContiguousKeptSlice<T, std::enable_if_t<is_pair_like_v<T>>> : std::true_type {};
91
92template <class Stride, bool = is_integral_constant_v<Stride>>
93struct IsUnitStride : std::false_type {};
94
95template <class Stride>
96struct IsUnitStride<Stride,true> : std::bool_constant<(Stride::value == 1)> {};
97
98template <class First, class Last, class Stride>
99struct IsContiguousKeptSlice<range_slice<First,Last,Stride>>
100 : IsUnitStride<Stride>
101{};
102
103template <class Offset, class Extent, class Stride>
104struct IsContiguousKeptSlice<extent_slice<Offset,Extent,Stride>>
105 : IsUnitStride<Stride>
106{};
107
108template <class Slice>
109inline constexpr bool is_contiguous_kept_slice_v =
110 IsContiguousKeptSlice<std::remove_cv_t<std::remove_reference_t<Slice>>>::value;
111
112// Compute the source index selected by a slice at relative result index zero.
113template <class Slice, class IndexType>
114constexpr IndexType slice_offset (const Slice& slice)
115{
116 if constexpr(is_full_extent_v<Slice>)
117 return 0;
118 else if constexpr(is_extent_slice_v<Slice>)
119 return IndexType(slice_index(slice.offset));
120 else if constexpr(is_range_slice_v<Slice>)
121 return IndexType(slice_index(slice.first));
122 else if constexpr(is_pair_like_v<Slice>)
123 return IndexType(std::get<0>(slice));
124 else
125 return IndexType(slice_index(slice));
126}
127
128// Compute the source-index stride represented by one increment in a kept slice.
129template <class Slice, class IndexType>
130constexpr IndexType slice_stride (const Slice& slice)
131{
132 if constexpr(is_extent_slice_v<Slice>)
133 return IndexType(slice_index(slice.stride));
134 else if constexpr(is_range_slice_v<Slice>)
135 return IndexType(slice_index(slice.stride));
136 else
137 return 1;
138}
139
140// Compute the runtime extent of a kept slice.
141template <std::size_t k, class Extents, class Slice>
142constexpr typename Extents::index_type slice_extent (const Extents& extents, const Slice& slice)
143{
144 using index_type = typename Extents::index_type;
145 if constexpr(is_full_extent_v<Slice>)
146 return extents.extent(k);
147 else if constexpr(is_extent_slice_v<Slice>)
148 return index_type(slice_index(slice.extent));
149 else if constexpr(is_range_slice_v<Slice>)
150 return (index_type(slice_index(slice.last)) - index_type(slice_index(slice.first))
151 + index_type(slice_index(slice.stride)) - 1) / index_type(slice_index(slice.stride));
152 else
153 return index_type(std::get<1>(slice)) - index_type(std::get<0>(slice));
154}
155
156// Compute the static extent of a kept slice. Dynamic slice bounds or extents
157// produce std::dynamic_extent.
158template <std::size_t k, class Extents, class Slice>
159struct StaticSliceExtent
160 : std::integral_constant<std::size_t,std::dynamic_extent>
161{};
162
163template <std::size_t k, class Extents>
164struct StaticSliceExtent<k,Extents,full_extent_t>
165 : std::integral_constant<std::size_t,Extents::static_extent(k)>
166{};
167
168template <class T>
169struct StaticValueOrDynamic
170 : std::integral_constant<std::size_t,std::dynamic_extent>
171{};
172
173template <class T, T value>
174struct StaticValueOrDynamic<std::integral_constant<T,value>>
175 : std::integral_constant<std::size_t,std::size_t(value)>
176{};
177
178template <class First, class Last, class Stride, bool = is_integral_constant_v<First> && is_integral_constant_v<Last> && is_integral_constant_v<Stride>>
179struct StaticRangeExtent
180 : std::integral_constant<std::size_t,std::dynamic_extent>
181{};
182
183template <class First, class Last, class Stride>
184struct StaticRangeExtent<First,Last,Stride,true>
185 : std::integral_constant<std::size_t,
186 std::size_t((Last::value - First::value + Stride::value - 1) / Stride::value)>
187{};
188
189template <std::size_t k, class Extents, class Offset, class SliceExtent, class Stride>
190struct StaticSliceExtent<k,Extents,extent_slice<Offset,SliceExtent,Stride>>
191 : StaticValueOrDynamic<SliceExtent>
192{};
193
194template <std::size_t k, class Extents, class First, class Last, class Stride>
195struct StaticSliceExtent<k,Extents,range_slice<First,Last,Stride>>
196 : StaticRangeExtent<First,Last,Stride>
197{};
198
199// Build the result extents type by walking source dimensions and appending only
200// the dimensions represented by kept slices.
201template <class IndexType, class ExtentsSeq, class SrcExtents, std::size_t k, class... Slices>
202struct SubextentsImpl;
203
204template <class IndexType, std::size_t... exts, class SrcExtents, std::size_t k>
205struct SubextentsImpl<IndexType, std::integer_sequence<std::size_t,exts...>, SrcExtents, k>
206{
207 using type = Std::extents<IndexType,exts...>;
208};
209
210template <class IndexType, std::size_t... exts, class SrcExtents, std::size_t k, class Slice0, class... Slices>
211struct SubextentsImpl<IndexType, std::integer_sequence<std::size_t,exts...>, SrcExtents, k, Slice0, Slices...>
212{
213 using next_seq = std::conditional_t<
214 is_kept_slice_v<Slice0>,
215 std::integer_sequence<std::size_t,exts...,StaticSliceExtent<k,SrcExtents,Slice0>::value>,
217
218 using type = typename SubextentsImpl<IndexType,next_seq,SrcExtents,k+1,Slices...>::type;
219};
220
221template <class Extents, class... Slices>
222using SubextentsType = typename SubextentsImpl<
223 typename Extents::index_type,
225 Extents,
226 0,
228
229template <class Layout>
230struct LayoutLeftPadding
231 : std::integral_constant<std::size_t,std::dynamic_extent>
232{};
233
234template <std::size_t PaddingValue>
235struct LayoutLeftPadding<layout_left_padded<PaddingValue>>
236 : std::integral_constant<std::size_t,PaddingValue>
237{};
238
239template <class Layout>
240struct LayoutRightPadding
241 : std::integral_constant<std::size_t,std::dynamic_extent>
242{};
243
244template <std::size_t PaddingValue>
245struct LayoutRightPadding<layout_right_padded<PaddingValue>>
246 : std::integral_constant<std::size_t,PaddingValue>
247{};
248
249template <class Layout>
250inline constexpr bool is_padded_layout_v =
251 is_layout_left_padded_v<Layout> || is_layout_right_padded_v<Layout>;
252
253// Conservative layout preservation: keep layout_right only for fixed leading
254// dimensions followed by full dimensions, and layout_left for full dimensions
255// followed by fixed trailing dimensions.
256template <class Layout, class SlicesTuple, std::size_t... i>
257constexpr bool preserve_layout_right (std::index_sequence<i...>)
258{
259 if constexpr(!std::is_same_v<Layout,layout_right>)
260 return false;
261 else {
262 constexpr bool kept[] = {is_kept_slice_v<std::tuple_element_t<i,SlicesTuple>>...};
263 constexpr bool full[] = {is_full_extent_v<std::tuple_element_t<i,SlicesTuple>>...};
264 bool seenKept = false;
265 for (std::size_t k = 0; k < sizeof...(i); ++k) {
266 if (kept[k]) {
267 seenKept = true;
268 if (!full[k])
269 return false;
270 } else if (seenKept)
271 return false;
272 }
273 return true;
274 }
275}
276
277template <class Layout, class SlicesTuple, std::size_t... i>
278constexpr bool preserve_layout_left (std::index_sequence<i...>)
279{
280 if constexpr(!std::is_same_v<Layout,layout_left>)
281 return false;
282 else {
283 constexpr bool kept[] = {is_kept_slice_v<std::tuple_element_t<i,SlicesTuple>>...};
284 constexpr bool full[] = {is_full_extent_v<std::tuple_element_t<i,SlicesTuple>>...};
285 bool seenFixed = false;
286 for (std::size_t k = 0; k < sizeof...(i); ++k) {
287 if (kept[k]) {
288 if (seenFixed || !full[k])
289 return false;
290 } else
291 seenFixed = true;
292 }
293 return true;
294 }
295}
296
297// Padded-left submatrix preservation from P2642. For rank two, contiguous
298// ranges in both dimensions are representable. For higher ranks, changing the
299// second dimension would also change the stride of later dimensions, so only
300// the first dimension may be a proper range while the others remain full.
301template <class Layout, class SlicesTuple, std::size_t... i>
302constexpr bool preserve_layout_left_padded (std::index_sequence<i...>)
303{
304 constexpr std::size_t rank = sizeof...(i);
305 if constexpr(!(std::is_same_v<Layout,layout_left> || is_layout_left_padded_v<Layout>) || rank < 2)
306 return false;
307 else {
308 constexpr bool contiguous[] = {is_contiguous_kept_slice_v<std::tuple_element_t<i,SlicesTuple>>...};
309 constexpr bool full[] = {is_full_extent_v<std::tuple_element_t<i,SlicesTuple>>...};
310 if (!contiguous[0] || !contiguous[1])
311 return false;
312 if constexpr(rank > 2 && !full[1])
313 return false;
314 for (std::size_t k = 2; k < rank; ++k)
315 if (!full[k])
316 return false;
317 return true;
318 }
319}
320
321// Padded-right counterpart. For rank two, contiguous ranges in both dimensions
322// are representable. For higher ranks, the next-to-last dimension must remain
323// full so that strides of earlier dimensions stay unchanged.
324template <class Layout, class SlicesTuple, std::size_t... i>
325constexpr bool preserve_layout_right_padded (std::index_sequence<i...>)
326{
327 constexpr std::size_t rank = sizeof...(i);
328 if constexpr(!(std::is_same_v<Layout,layout_right> || is_layout_right_padded_v<Layout>) || rank < 2)
329 return false;
330 else {
331 constexpr bool contiguous[] = {is_contiguous_kept_slice_v<std::tuple_element_t<i,SlicesTuple>>...};
332 constexpr bool full[] = {is_full_extent_v<std::tuple_element_t<i,SlicesTuple>>...};
333 for (std::size_t k = 0; k + 2 < rank; ++k)
334 if (!full[k])
335 return false;
336 if constexpr(rank > 2 && !full[rank-2])
337 return false;
338 return contiguous[rank-2] && contiguous[rank-1];
339 }
340}
341
342template <class Layout, class Extents>
343struct SubLayoutLeftPadded
344{
345 static constexpr std::size_t padding = [] {
346 if constexpr(is_layout_left_padded_v<Layout>)
347 return LayoutLeftPadding<Layout>::value;
348 else if constexpr(Extents::rank() > 0 && Extents::static_extent(0) != std::dynamic_extent)
349 return Extents::static_extent(0);
350 else
351 return std::dynamic_extent;
352 }();
353
354 using type = layout_left_padded<padding>;
355};
356
357template <class Layout, class Extents>
358struct SubLayoutRightPadded
359{
360 static constexpr std::size_t padding = [] {
361 if constexpr(is_layout_right_padded_v<Layout>)
362 return LayoutRightPadding<Layout>::value;
363 else if constexpr(Extents::rank() > 0 && Extents::static_extent(Extents::rank()-1) != std::dynamic_extent)
364 return Extents::static_extent(Extents::rank()-1);
365 else
366 return std::dynamic_extent;
367 }();
368
369 using type = layout_right_padded<padding>;
370};
371
372template <class Layout, class Extents, class... Slices>
373struct SubLayout
374{
376 static constexpr bool right = preserve_layout_right<Layout,slices_tuple>(std::index_sequence_for<Slices...>{});
377 static constexpr bool left = preserve_layout_left<Layout,slices_tuple>(std::index_sequence_for<Slices...>{});
378 static constexpr bool right_padded = preserve_layout_right_padded<Layout,slices_tuple>(std::index_sequence_for<Slices...>{});
379 static constexpr bool left_padded = preserve_layout_left_padded<Layout,slices_tuple>(std::index_sequence_for<Slices...>{});
380
381 using type = std::conditional_t<right, layout_right,
382 std::conditional_t<left, layout_left,
385 layout_stride>>>>;
386};
387
388// Construct the result mapping plus the source data offset. This is the central
389// implementation behind both submdspan_mapping and submdspan.
390template <class Mapping, class... Slices, std::size_t... k>
391constexpr auto make_sub_mapping (const Mapping& mapping, const std::tuple<Slices...>& slices, std::index_sequence<k...>)
392{
393 using src_extents_type = typename Mapping::extents_type;
394 using index_type = typename Mapping::index_type;
395 using sub_extents_type = SubextentsType<src_extents_type,Slices...>;
396 using sub_layout_type = typename SubLayout<typename Mapping::layout_type,src_extents_type,Slices...>::type;
397 using sub_mapping_type = typename sub_layout_type::template mapping<sub_extents_type>;
398
399 constexpr std::size_t rank = src_extents_type::rank();
400 constexpr std::size_t subrank = sub_extents_type::rank();
401
402 const auto& src_extents = mapping.extents();
403 std::array<index_type,rank> offsets{slice_offset<std::tuple_element_t<k,std::tuple<Slices...>>,index_type>(std::get<k>(slices))...};
405 std::array<index_type,subrank> sub_strides{};
406
407 std::size_t j = 0;
408 auto add_kept = [&](auto kk) {
409 constexpr std::size_t i = decltype(kk)::value;
410 using slice_type = std::tuple_element_t<i,std::tuple<Slices...>>;
411 if constexpr(is_kept_slice_v<slice_type>) {
412 const auto& slice = std::get<i>(slices);
413 sub_exts[j] = slice_extent<i>(src_extents, slice);
414 sub_strides[j] = mapping.stride(i) * slice_stride<slice_type,index_type>(slice);
415 ++j;
416 }
417 };
418 (add_kept(std::integral_constant<std::size_t,k>{}), ...);
419
420 const index_type offset = unpackIntegerSequence([&](auto... i) {
421 return mapping(offsets[i]...);
423
424 sub_extents_type sub_extents{sub_exts};
425
426 if constexpr(std::is_same_v<sub_layout_type,layout_stride>) {
427 return submdspan_mapping_result<sub_mapping_type>{
428 sub_mapping_type{sub_extents, sub_strides}, offset};
429 } else if constexpr(is_padded_layout_v<sub_layout_type>) {
430 using strided_mapping_type = typename layout_stride::template mapping<sub_extents_type>;
431 return submdspan_mapping_result<sub_mapping_type>{
432 sub_mapping_type{strided_mapping_type{sub_extents, sub_strides}}, offset};
433 } else {
434 return submdspan_mapping_result<sub_mapping_type>{
435 sub_mapping_type{sub_extents}, offset};
436 }
437}
438
439} // end namespace Dune::Std::Impl
440
441#endif // DUNE_COMMON_STD_IMPL_SUBMDSPAN_HH
Traits for type conversions and type information.
decltype(auto) constexpr unpackIntegerSequence(F &&f, std::integer_sequence< I, i... > sequence)
Unpack an std::integer_sequence<I,i...> to std::integral_constant<I,i>...
Definition indices.hh:124
typename Impl::voider< Types... >::type void_t
Is void for all valid input types. The workhorse for C++11 SFINAE-techniques.
Definition typetraits.hh:40
STL namespace.
extent_slice(OffsetType, ExtentType, StrideType) -> extent_slice< OffsetType, ExtentType, StrideType >
range_slice(FirstType, LastType) -> range_slice< FirstType, LastType >
Check if T is an std::integral_constant<I, i>
Definition typetraits.hh:384
T right(T... args)