Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
diagonal_accessor.hpp
Go to the documentation of this file.
1
7#pragma once
8
10#include <uni20/core/types.hpp>
13
14#include <array>
15#include <cassert>
16#include <concepts>
17#include <cstddef>
18#include <stdexcept>
19#include <type_traits>
20#include <utility>
21
22namespace uni20
23{
24
27template <class ElementType> struct diagonal_data_handle
28{
29 ElementType* data = nullptr;
30 std::size_t base_offset = 0;
31
32 UNI20_HOST_DEVICE constexpr diagonal_data_handle() noexcept = default;
33
34 UNI20_HOST_DEVICE constexpr diagonal_data_handle(ElementType* data, std::size_t base_offset) noexcept
36 {}
37
38 template <class OtherElementType>
39 requires std::convertible_to<OtherElementType*, ElementType*>
41 : data(other.data), base_offset(other.base_offset)
42 {}
43
45 diagonal_data_handle const&) = default;
46};
47
53template <class ElementType> class diagonal_reference {
54 public:
55 static_assert(!std::is_const_v<ElementType>);
56
57 UNI20_HOST_DEVICE constexpr diagonal_reference() noexcept = default;
58 UNI20_HOST_DEVICE constexpr explicit diagonal_reference(ElementType* element) noexcept : element_(element) {}
59
60 [[nodiscard]] UNI20_HOST_DEVICE constexpr operator ElementType() const noexcept
61 {
62 return element_ == nullptr ? ElementType{} : *element_;
63 }
64
65 UNI20_HOST_DEVICE constexpr auto operator=(ElementType const& value) noexcept -> diagonal_reference&
66 {
67 if (element_ == nullptr)
68 {
69 assert(value == ElementType{});
70 return *this;
71 }
72 *element_ = value;
73 return *this;
74 }
75
76 UNI20_HOST_DEVICE constexpr auto operator=(diagonal_reference const& other) noexcept -> diagonal_reference&
77 {
78 return *this = static_cast<ElementType>(other);
79 }
80
81 private:
82 ElementType* element_ = nullptr;
83};
84
92template <class ElementType, class Extents> class diagonal_accessor {
93 public:
94 using value_type = std::remove_cv_t<ElementType>;
95 using element_type = ElementType;
96 using reference = std::conditional_t<std::is_const_v<element_type>, value_type, diagonal_reference<value_type>>;
99 using offset_type = std::size_t;
100 using extents_type = Extents;
101 using index_type = typename extents_type::index_type;
102
105 {
106 std::size_t index = 0;
107 bool present = true;
108 };
109
111 requires std::default_initializable<extents_type>
112 = default;
113
118 : extents_(std::move(extents)), component_stride_(component_stride)
119 {}
120
123 {
124 auto const diagonal_index = this->component_index(handle, offset);
125 auto const component_offset = static_cast<index_type>(diagonal_index.index) * component_stride_;
126 if constexpr (std::is_const_v<element_type>)
127 {
128 return diagonal_index.present ? handle.data[component_offset] : value_type{};
129 }
130 else
131 {
132 return reference{diagonal_index.present ? handle.data + component_offset : nullptr};
133 }
134 }
135
138 offset_type offset) const noexcept
139 {
141 return handle;
142 }
143
145 [[nodiscard]] UNI20_HOST_DEVICE constexpr auto extents() const noexcept -> extents_type const& { return extents_; }
146
148 [[nodiscard]] UNI20_HOST_DEVICE constexpr auto component_stride() const noexcept -> index_type
149 {
150 return component_stride_;
151 }
152
154 [[nodiscard]] UNI20_HOST_DEVICE constexpr auto
156 {
157 return this->diagonal_index(handle.base_offset + offset);
158 }
159
160 private:
161 [[nodiscard]] UNI20_HOST_DEVICE constexpr auto
162 diagonal_index(std::size_t linear) const noexcept -> component_index_result
163 {
164 if constexpr (extents_type::rank() == 0)
165 {
166 return {};
167 }
168 else
169 {
170 std::array<index_type, extents_type::rank()> indices{};
171 for (std::size_t axis = extents_type::rank(); axis > 0; --axis)
172 {
173 auto const extent = static_cast<std::size_t>(extents_.extent(axis - 1));
174 indices[axis - 1] = static_cast<index_type>(linear % extent);
175 linear /= extent;
176 }
177 auto const first = indices[0];
178 for (std::size_t axis = 1; axis < extents_type::rank(); ++axis)
179 if (indices[axis] != first) return {.present = false};
180 return {.index = static_cast<std::size_t>(first), .present = true};
181 }
182 }
183
184 [[no_unique_address]] extents_type extents_{};
185 index_type component_stride_ = 1;
186};
187
189template <class ElementType, class Extents>
190 requires(!std::is_const_v<ElementType>)
192{
194}
195
197template <class ElementType, class Extents>
198 requires std::is_const_v<ElementType>
200{
201 return accessor;
202}
203
205template <class ElementType, class Extents>
206inline constexpr bool enable_accessor_in_domain<diagonal_accessor<ElementType, Extents>, host_access_domain> = true;
207
208namespace detail
209{
210
211template <class Extents>
212[[nodiscard]] UNI20_HOST_DEVICE constexpr auto diagonal_component_extent(Extents const& extents) noexcept ->
213 typename Extents::index_type
214{
215 using index_type = typename Extents::index_type;
216 if constexpr (Extents::rank() == 0)
217 {
218 return index_type{1};
219 }
220 else
221 {
222 index_type result = extents.extent(0);
223 for (std::size_t axis = 1; axis < Extents::rank(); ++axis)
224 if (extents.extent(axis) < result) result = extents.extent(axis);
225 return result;
226 }
227}
228
229template <class Mapping, std::size_t... Axis>
230[[nodiscard]] constexpr auto diagonal_mapping_step(Mapping const& mapping, std::index_sequence<Axis...>)
231{
232 using index_type = typename Mapping::extents_type::index_type;
233 return mapping(((void)Axis, index_type{1})...) - mapping(((void)Axis, index_type{0})...);
234}
235
236} // namespace detail
237
250template <class ElementType, class Extents, class LayoutPolicy, class AccessorExtents>
251 requires(Extents::rank() == AccessorExtents::rank())
252[[nodiscard]] constexpr auto diagonal_components(
253 stdex::mdspan<ElementType, Extents, LayoutPolicy, diagonal_accessor<ElementType, AccessorExtents>> const& span)
254{
255 using index_type = typename Extents::index_type;
256 using component_extents_type = stdex::dextents<index_type, 1>;
257 using component_mapping_type = stdex::layout_stride::mapping<component_extents_type>;
258 using component_mdspan_type = stdex::mdspan<ElementType, component_extents_type, stdex::layout_stride>;
259 auto const extents = component_extents_type{detail::diagonal_component_extent(span.extents())};
260 index_type component_stride = span.accessor().component_stride();
261 auto* data = span.data_handle().data;
262 if (extents.extent(0) > 0)
263 {
264 auto const origin = span.accessor().component_index(span.data_handle());
265 if (!origin.present)
266 throw std::invalid_argument("diagonal component view does not begin on the represented diagonal");
267 data += static_cast<index_type>(origin.index) * component_stride;
268 if (extents.extent(0) > 1)
269 {
270 auto const step = detail::diagonal_mapping_step(span.mapping(), std::make_index_sequence<Extents::rank()>{});
271 if constexpr (std::signed_integral<std::remove_cv_t<decltype(step)>>)
272 if (step < 0) throw std::invalid_argument("diagonal component view has a negative logical step");
273 auto const next = span.accessor().component_index(span.data_handle(), static_cast<std::size_t>(step));
274 if (!next.present || next.index < origin.index)
275 throw std::invalid_argument("diagonal component view does not preserve the represented diagonal");
276 component_stride *= static_cast<index_type>(next.index - origin.index);
277 }
278 }
279 auto const mapping = component_mapping_type{extents, std::array<index_type, 1>{component_stride}};
280 return component_mdspan_type{data, mapping};
281}
282
293template <class Extents, class Components>
294 requires RankedStridedMdspanLike<Components, 1> && DefaultAccessorMdspanLike<Components>
295[[nodiscard]] constexpr auto make_diagonal_mdspan(Extents logical_extents, Components const& components)
296{
297 using component_type = std::remove_cvref_t<Components>;
298 using element_type = typename component_type::element_type;
299 using index_type = typename Extents::index_type;
300 if (static_cast<index_type>(components.extent(0)) != detail::diagonal_component_extent(logical_extents))
301 {
302 throw std::invalid_argument("diagonal component extent does not match the logical tensor extents");
303 }
304 using accessor_type = diagonal_accessor<element_type, Extents>;
305 using result_type = stdex::mdspan<element_type, Extents, GeneratedLayout, accessor_type>;
306 return result_type{typename accessor_type::data_handle_type{components.data_handle(), 0},
307 typename result_type::mapping_type{logical_extents},
308 accessor_type{std::move(logical_extents), static_cast<index_type>(components.stride(0))}};
309}
310
312template <class ElementType> struct remove_proxy_reference<diagonal_reference<ElementType>>
313{
314 using type = ElementType;
315};
316
317} // namespace uni20
Mdspan accessor over a compressed rank-N generalized diagonal.
Definition diagonal_accessor.hpp:92
ElementType element_type
Definition diagonal_accessor.hpp:95
UNI20_HOST_DEVICE constexpr auto component_index(data_handle_type handle, offset_type offset=0) const noexcept -> component_index_result
Decode whether a logical offset addresses a stored component and which one.
Definition diagonal_accessor.hpp:155
UNI20_HOST_DEVICE constexpr auto extents() const noexcept -> extents_type const &
Return the extents used to decode logical offsets.
Definition diagonal_accessor.hpp:145
UNI20_HOST_DEVICE constexpr auto component_stride() const noexcept -> index_type
Return the physical offset between consecutive diagonal components.
Definition diagonal_accessor.hpp:148
UNI20_HOST_DEVICE constexpr reference access(data_handle_type handle, offset_type offset) const
Read or write the logical element represented by an encoded offset.
Definition diagonal_accessor.hpp:122
UNI20_HOST_DEVICE constexpr diagonal_accessor()=default
UNI20_HOST_DEVICE constexpr data_handle_type offset(data_handle_type handle, offset_type offset) const noexcept
Advance a logical handle without changing its original index space.
Definition diagonal_accessor.hpp:137
std::remove_cv_t< ElementType > value_type
Definition diagonal_accessor.hpp:94
typename extents_type::index_type index_type
Definition diagonal_accessor.hpp:101
std::conditional_t< std::is_const_v< element_type >, value_type, diagonal_reference< value_type > > reference
Definition diagonal_accessor.hpp:96
Extents extents_type
Definition diagonal_accessor.hpp:100
std::size_t offset_type
Definition diagonal_accessor.hpp:99
Writable proxy for one structural diagonal or off-diagonal element.
Definition diagonal_accessor.hpp:53
UNI20_HOST_DEVICE constexpr diagonal_reference() noexcept=default
UNI20_HOST_DEVICE constexpr auto operator=(ElementType const &value) noexcept -> diagonal_reference &
Definition diagonal_accessor.hpp:65
UNI20_HOST_DEVICE constexpr auto operator=(diagonal_reference const &other) noexcept -> diagonal_reference &
Definition diagonal_accessor.hpp:76
Project compiler annotations shared by host and device code.
#define UNI20_HOST_DEVICE
Definition compiler_attributes.hpp:12
cuda::ResourceLease< HandleSlot > handle
Definition cublas/execution.cpp:33
locale_t value
Definition data_table.cpp:49
Synthetic mdspan layout for values generated from logical indices.
constexpr stdex::default_accessor< T const > const_accessor(stdex::default_accessor< T > const &accessor_policy)
Wrap a default_accessor<T> into a const_default_accessor<T>.
Definition mdspan/concepts.hpp:160
Mdspan concept and accessor extensions for Uni20.
Definition qnum.hpp:403
Definition async/async.hpp:26
std::ptrdiff_t index_type
Definition types.hpp:19
constexpr auto diagonal_components(stdex::mdspan< ElementType, Extents, LayoutPolicy, diagonal_accessor< ElementType, AccessorExtents > > const &span)
Expose the physically stored values of a generalized-diagonal mdspan.
Definition diagonal_accessor.hpp:252
constexpr auto make_diagonal_mdspan(Extents logical_extents, Components const &components)
Present rank-one strided components as a full generalized-diagonal mdspan.
Definition diagonal_accessor.hpp:295
std::size_t span
Definition presentation.cpp:2017
Decoded component coordinate for one logical mapping offset.
Definition diagonal_accessor.hpp:105
std::size_t index
Definition diagonal_accessor.hpp:106
bool present
Definition diagonal_accessor.hpp:107
Handle for one compressed generalized diagonal value sequence.
Definition diagonal_accessor.hpp:28
std::size_t base_offset
Definition diagonal_accessor.hpp:30
friend UNI20_HOST_DEVICE constexpr bool operator==(diagonal_data_handle const &, diagonal_data_handle const &)=default
ElementType * data
Definition diagonal_accessor.hpp:29
UNI20_HOST_DEVICE constexpr diagonal_data_handle() noexcept=default
UNI20_HOST_DEVICE constexpr diagonal_data_handle(diagonal_data_handle< OtherElementType > const &other) noexcept
Definition diagonal_accessor.hpp:40
Execution domain in which ordinary host code may evaluate an accessor.
Definition mdspan/concepts.hpp:74
ElementType type
Definition diagonal_accessor.hpp:314
Trait for extracting the element type from a proxy reference.
Definition types.hpp:102