Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
shape.hpp
Go to the documentation of this file.
1#pragma once
2
11
12#include <array>
13#include <concepts>
14#include <cstddef>
15#include <limits>
16#include <type_traits>
17#include <utility>
18
19namespace uni20::detail
20{
21
22template <std::integral Extent> [[nodiscard]] constexpr bool tensor_extent_is_valid(Extent extent) noexcept
23{
24 if constexpr (std::signed_integral<Extent>)
25 if (extent < 0) return false;
26 return std::in_range<index_type>(extent);
27}
28
29template <std::integral... Extents> [[nodiscard]] auto make_tensor_extents(Extents... extents)
30{
31 ERROR_IF(!(tensor_extent_is_valid(extents) && ...),
32 "tensor extents must be nonnegative and representable by uni20::index_type");
33 using extents_type = stdex::dextents<index_type, sizeof...(Extents)>;
34 return extents_type{static_cast<index_type>(extents)...};
35}
36
37template <class Extents> [[nodiscard]] std::size_t checked_element_count(Extents const& extents)
38{
39 bool has_zero_extent = false;
40 for (std::size_t axis = 0; axis < Extents::rank(); ++axis)
41 {
42 auto const extent = extents.extent(axis);
43 ERROR_IF(!tensor_extent_is_valid(extent), "tensor extents must be nonnegative and representable");
44 has_zero_extent = has_zero_extent || extent == 0;
45 }
46 if (has_zero_extent) return 0;
47
48 std::size_t result = 1;
49 for (std::size_t axis = 0; axis < Extents::rank(); ++axis)
50 {
51 auto const value = static_cast<std::size_t>(extents.extent(axis));
52 ERROR_IF(result > std::numeric_limits<std::size_t>::max() / value, "tensor element count overflows size_t");
53 result *= value;
54 }
55 return result;
56}
57
58template <std::integral Extent> [[nodiscard]] constexpr bool is_inferred_extent(Extent extent) noexcept
59{
60 if constexpr (std::signed_integral<Extent>) return extent == static_cast<Extent>(-1);
61 return false;
62}
63
64template <std::integral... Extents>
65[[nodiscard]] auto make_reshape_extents(std::size_t source_size, Extents... requested_extents)
66{
67 constexpr std::size_t rank = sizeof...(Extents);
68 std::array<index_type, rank> values{};
69 std::array<bool, rank> inferred{};
70 std::size_t axis = 0;
71 auto record_extent = [&](auto requested) {
72 bool const infer = is_inferred_extent(requested);
73 ERROR_IF(!infer && !tensor_extent_is_valid(requested),
74 "reshape extents must be nonnegative, except for one inferred -1 extent");
75 inferred[axis] = infer;
76 values[axis] = infer ? index_type{0} : static_cast<index_type>(requested);
77 ++axis;
78 };
79 (record_extent(requested_extents), ...);
80
81 std::size_t inferred_axis = rank;
82 std::size_t known_size = 1;
83 for (axis = 0; axis < rank; ++axis)
84 {
85 if (inferred[axis])
86 {
87 ERROR_IF(inferred_axis != rank, "reshape accepts at most one inferred extent");
88 inferred_axis = axis;
89 continue;
90 }
91 auto const extent = static_cast<std::size_t>(values[axis]);
92 if (extent == 0)
93 {
94 known_size = 0;
95 continue;
96 }
97 ERROR_IF(known_size > std::numeric_limits<std::size_t>::max() / extent, "reshape element count overflows size_t");
98 known_size *= extent;
99 }
100
101 if (inferred_axis != rank)
102 {
103 ERROR_IF(known_size == 0, "cannot infer a reshape extent when the specified extents have zero product");
104 ERROR_IF(source_size % known_size != 0, "inferred reshape extent does not divide the source element count");
105 auto const inferred_value = source_size / known_size;
106 ERROR_IF(!std::in_range<index_type>(inferred_value), "inferred reshape extent is not representable");
107 values[inferred_axis] = static_cast<index_type>(inferred_value);
108 }
109 else
110 {
111 ERROR_IF(known_size != source_size, "reshape source and destination element counts differ");
112 }
113
114 using extents_type = stdex::dextents<index_type, rank>;
115 return [&]<std::size_t... Axis>(std::index_sequence<Axis...>) {
116 return extents_type{values[Axis]...};
117 }(std::make_index_sequence<rank>{});
118}
119
120template <class TargetExtents, class SourceExtents>
121[[nodiscard]] auto convert_reshape_extents(SourceExtents const& source) -> TargetExtents
122{
123 static_assert(TargetExtents::rank() == SourceExtents::rank());
124 for (std::size_t axis = 0; axis < TargetExtents::rank(); ++axis)
125 {
126 auto const fixed_extent = TargetExtents::static_extent(axis);
127 ERROR_IF(fixed_extent != stdex::dynamic_extent && static_cast<std::size_t>(source.extent(axis)) != fixed_extent,
128 "reshape extent does not match the tensor's static extent");
129 }
130
131 using target_index = typename TargetExtents::index_type;
132 return [&]<std::size_t... Axis>(std::index_sequence<Axis...>) {
133 return TargetExtents{static_cast<target_index>(source.extent(Axis))...};
134 }(std::make_index_sequence<TargetExtents::rank()>{});
135}
136
137} // namespace uni20::detail
locale_t value
Definition data_table.cpp:49
Configured gateway to Uni20's mdspan implementation.
auto values(Tensor &tensor)
Definition packed_matrix.hpp:29
std::ptrdiff_t index_type
Definition types.hpp:19
#define ERROR_IF(cond,...)
Definition trace.hpp:389