Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
tensor/output.hpp
Go to the documentation of this file.
1#pragma once
2
14
15#include <concepts>
16#include <cstddef>
17#include <type_traits>
18#include <utility>
19
20namespace uni20
21{
23template <class Extents>
24concept TensorExtentsLike = requires(Extents const& extents) {
25 { std::remove_cvref_t<Extents>::rank() } -> std::convertible_to<std::size_t>;
26 extents.extent(std::size_t{});
27};
28
30template <TensorExtentsLike LhsExtents, TensorExtentsLike RhsExtents>
31[[nodiscard]] constexpr bool tensor_extents_equal(LhsExtents const& lhs, RhsExtents const& rhs) noexcept
32{
33 if constexpr (std::remove_cvref_t<LhsExtents>::rank() != std::remove_cvref_t<RhsExtents>::rank())
34 {
35 return false;
36 }
37 else
38 {
39 for (std::size_t axis = 0; axis < std::remove_cvref_t<LhsExtents>::rank(); ++axis)
40 {
41 if (lhs.extent(axis) != rhs.extent(axis)) return false;
42 }
43 return true;
44 }
45}
46
47namespace detail
48{
49template <class TargetExtents, TensorExtentsLike SourceExtents, std::size_t... Axis>
50[[nodiscard]] TargetExtents convert_tensor_extents_impl(SourceExtents const& source, std::index_sequence<Axis...>)
51{
52 for (std::size_t axis = 0; axis < TargetExtents::rank(); ++axis)
53 {
54 auto const fixed_extent = TargetExtents::static_extent(axis);
55 ERROR_IF(fixed_extent != stdex::dynamic_extent && !std::cmp_equal(source.extent(axis), fixed_extent),
56 "tensor extent does not match the destination's static extent");
57 }
58 using index_type = typename TargetExtents::index_type;
59 return TargetExtents(static_cast<index_type>(source.extent(Axis))...);
60}
61} // namespace detail
62
64template <class TargetExtents, TensorExtentsLike SourceExtents>
65[[nodiscard]] TargetExtents convert_tensor_extents(SourceExtents const& source)
66{
67 static_assert(TargetExtents::rank() == std::remove_cvref_t<SourceExtents>::rank(),
68 "tensor output rank does not match the required shape");
69 return detail::convert_tensor_extents_impl<TargetExtents>(source, std::make_index_sequence<TargetExtents::rank()>{});
70}
71
75template <class T>
78 requires(std::remove_reference_t<T>& output, tensor_extents_t<T> const& extents) { output.reset_shape(extents); };
79
83template <TensorView Output, TensorExtentsLike RequiredExtents>
84void require_output(Output const& output, RequiredExtents const& required)
85{
86 static_assert(tensor_extents_t<Output>::rank() == std::remove_cvref_t<RequiredExtents>::rank(),
87 "tensor output rank does not match the required shape");
88 ERROR_IF(!tensor_extents_equal(output.extents(), required),
89 "tensor output shape does not match the required extents");
90}
91
103template <MutableTensorView Output, TensorExtentsLike RequiredExtents>
104Output& prepare_output(Output& output, RequiredExtents const& required)
105{
106 static_assert(tensor_extents_t<Output>::rank() == std::remove_cvref_t<RequiredExtents>::rank(),
107 "tensor output rank does not match the required shape");
108 if (tensor_extents_equal(output.extents(), required)) return output;
109
110 if constexpr (ResizableTensorOutput<Output>)
111 {
112 auto const converted = convert_tensor_extents<tensor_extents_t<Output>>(required);
113 if constexpr (requires { output.reset_shape(uninitialized, converted); })
114 output.reset_shape(uninitialized, converted);
115 else
116 output.reset_shape(converted);
117 CHECK(tensor_extents_equal(output.extents(), required));
118 }
119 else
120 {
121 require_output(output, required);
122 }
123 return output;
124}
125
136template <MutableTensorView Output, TensorExtentsLike RequiredExtents, class Placement>
137 requires requires(Output& output, tensor_extents_t<Output> const& extents, Placement const& placement) {
138 { output.storage_is_compatible(placement) } -> std::convertible_to<bool>;
139 output.reset_shape(extents);
140 output.replace(extents, placement);
141 }
142Output& prepare_output(Output& output, RequiredExtents const& required, Placement const& placement)
143{
144 static_assert(tensor_extents_t<Output>::rank() == std::remove_cvref_t<RequiredExtents>::rank(),
145 "tensor output rank does not match the required shape");
146 if (tensor_extents_equal(output.extents(), required) && output.storage_is_compatible(placement)) return output;
147
148 auto const converted = convert_tensor_extents<tensor_extents_t<Output>>(required);
149 if (output.storage_is_compatible(placement))
150 {
151 if constexpr (requires { output.reset_shape(uninitialized, converted); })
152 output.reset_shape(uninitialized, converted);
153 else
154 output.reset_shape(converted);
155 }
156 else
157 {
158 if constexpr (requires { output.replace(uninitialized, converted, placement); })
159 output.replace(uninitialized, converted, placement);
160 else
161 output.replace(converted, placement);
162 }
163
164 CHECK(tensor_extents_equal(output.extents(), required));
165 CHECK(output.storage_is_compatible(placement));
166 return output;
167}
168
180template <MutableTensorView Output, TensorExtentsLike RequiredExtents>
181Output& prepare_output(async::shared_storage<Output>& storage, RequiredExtents const& required)
182{
183 if (storage.constructed()) return prepare_output(*storage, required);
184
185 if constexpr (std::constructible_from<Output, tensor_extents_t<Output> const&>)
186 {
187 auto const converted = convert_tensor_extents<tensor_extents_t<Output>>(required);
188 if constexpr (std::constructible_from<Output, uninitialized_t, tensor_extents_t<Output> const&>)
189 return storage.emplace(uninitialized, converted);
190 else
191 return storage.emplace(converted);
192 }
193 else
194 {
195 ERROR("unconstructed tensor output cannot be initialized from the required extents");
196 }
197}
198
207template <MutableTensorView Output, TensorExtentsLike RequiredExtents, class Placement>
208 requires std::constructible_from<Output, Placement const&, tensor_extents_t<Output> const&> &&
209 requires(Output& output, tensor_extents_t<Output> const& extents, Placement const& placement) {
210 prepare_output(output, extents, placement);
211 }
212Output& prepare_output(async::shared_storage<Output>& storage, RequiredExtents const& required,
213 Placement const& placement)
214{
215 if (storage.constructed()) return prepare_output(*storage, required, placement);
216
217 auto const converted = convert_tensor_extents<tensor_extents_t<Output>>(required);
218 if constexpr (std::constructible_from<Output, uninitialized_t, Placement const&, tensor_extents_t<Output> const&>)
219 return storage.emplace(uninitialized, placement, converted);
220 else
221 return storage.emplace(placement, converted);
222}
223
224namespace detail
225{
226// Only outputs with independent writable defaults record operation precision.
227// Structural views preserve their parent's metadata; no elements are converted.
228template <MutableTensorView Output> void record_output_precision(Output& output, Precision p)
229{
230 require_precision<tensor_element_t<Output>>(p);
231 if constexpr (requires { output.default_precision(p); }) output.default_precision(p);
232}
233} // namespace detail
234
240template <MutableTensorView Output, TensorExtentsLike RequiredExtents>
241Output& prepare_output(Output& output, RequiredExtents const& required, Precision p)
242{
243 detail::record_output_precision(output, p);
244 return prepare_output(output, required);
245}
246
248template <MutableTensorView Output, TensorExtentsLike RequiredExtents>
249 requires(!has_runtime_precision_v<tensor_element_t<Output>> ||
250 std::constructible_from<Output, uninitialized_t, tensor_extents_t<Output> const&, Precision>)
251Output& prepare_output(async::shared_storage<Output>& storage, RequiredExtents const& required, Precision p)
252{
253 require_precision<tensor_element_t<Output>>(p);
254 if (storage.constructed()) return prepare_output(*storage, required, p);
255 if constexpr (has_runtime_precision_v<tensor_element_t<Output>>)
256 {
257 auto const converted = convert_tensor_extents<tensor_extents_t<Output>>(required);
258 return storage.emplace(uninitialized, converted, p);
259 }
260 else
261 return prepare_output(storage, required);
262}
263
264} // namespace uni20
Exact arithmetic or an explicit binary working precision.
Definition core/precision.hpp:13
A lightweight, thread-safe, reference-counted storage for a single object.
Definition shared_storage.hpp:94
bool constructed() const noexcept
Reports whether a value is currently constructed.
Definition shared_storage.hpp:234
T & emplace(Args &&... args)
Destroy any existing value and construct a new one in place.
Definition shared_storage.hpp:270
TensorView whose writable mdspec supports eventual element assignment.
Definition tensor/concepts.hpp:133
Mutable tensor output that can replace its shape and storage.
Definition tensor/output.hpp:76
Extents-like shape accepted by tensor output preparation.
Definition tensor/output.hpp:24
Definition data_table.cpp:8
Definition async/async.hpp:26
constexpr uninitialized_t uninitialized
Explicit allocation tag for overwrite outputs and scratch space.
Definition common/initialization.hpp:22
TargetExtents convert_tensor_extents(SourceExtents const &source)
Convert tensor extents while validating the target's static extents.
Definition tensor/output.hpp:65
Output & prepare_output(Output &output, RequiredExtents const &required)
Prepare a mutable tensor output with the specified shape.
Definition tensor/output.hpp:104
void require_output(Output const &output, RequiredExtents const &required)
Require an existing tensor output to have the specified shape.
Definition tensor/output.hpp:84
typename tensor_mdspec_t< T >::extents_type tensor_extents_t
Extents type exposed by a tensor-level object.
Definition tensor/concepts.hpp:129
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
Reference-counted optional in-place storage used by async buffers.
Concepts for tensor-level objects that resolve mdspan kernel operands.
#define ERROR(...)
Definition trace.hpp:387
#define CHECK(cond,...)
Definition trace.hpp:299
#define ERROR_IF(cond,...)
Definition trace.hpp:389