Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
basic_tensor.hpp
Go to the documentation of this file.
1#pragma once
2
9#include "concepts.hpp"
10#include "layout.hpp"
11
18
19#include <array>
20#include <concepts>
21#include <cstddef>
22#include <ranges>
23#include <tuple>
24#include <type_traits>
25#include <utility>
26
27namespace uni20
28{
29namespace detail
30{
31
32template <class RequestedLayout, class InputMdspan>
33using materialized_layout_t =
34 std::conditional_t<std::is_void_v<RequestedLayout>,
35 std::conditional_t<std::same_as<typename InputMdspan::layout_type, stdex::layout_left> ||
36 std::same_as<typename InputMdspan::layout_type, stdex::layout_right>,
37 typename InputMdspan::layout_type, stdex::layout_left>,
38 RequestedLayout>;
39
40template <class Storage>
41concept DefaultTensorStorage =
42 (std::default_initializable<Storage> && requires(Storage& storage) { storage.resize(std::size_t{}); }) ||
43 std::constructible_from<Storage, std::size_t> || std::constructible_from<Storage, size_type>;
44
45template <typename Tuple, std::size_t... I> constexpr bool integral_prefix(std::index_sequence<I...>)
46{
47 return (std::integral<std::tuple_element_t<I, Tuple>> && ...);
48}
49
50} // namespace detail
51
54{
55 template <typename ElementType> using accessor_t = stdex::default_accessor<ElementType>;
56
57 template <typename ElementType, typename Storage>
58 [[nodiscard]] constexpr auto make_accessor(Storage const&) const noexcept -> accessor_t<ElementType>
59 {
61 }
62};
63
64namespace detail
65{
66
67template <class StoragePolicy, class = void> struct TensorAccessorFactory
68{
69 using type = DefaultAccessorFactory;
70};
71
72template <class StoragePolicy>
73struct TensorAccessorFactory<StoragePolicy, std::void_t<typename StoragePolicy::accessor_factory_type>>
74{
75 using type = typename StoragePolicy::accessor_factory_type;
76};
77
78template <class StoragePolicy> using tensor_accessor_factory_t = typename TensorAccessorFactory<StoragePolicy>::type;
79
80template <class AccessorFactory, class ElementType, class = void> struct TensorDeviceAccessor
81{
82 using type = typename AccessorFactory::template accessor_t<ElementType>;
83};
84
85template <class AccessorFactory, class ElementType>
86struct TensorDeviceAccessor<AccessorFactory, ElementType,
87 std::void_t<typename AccessorFactory::template device_accessor_t<ElementType>>>
88{
89 using type = typename AccessorFactory::template device_accessor_t<ElementType>;
90};
91
92template <class AccessorFactory, class ElementType>
93using tensor_device_accessor_t = typename TensorDeviceAccessor<AccessorFactory, ElementType>::type;
94
95} // namespace detail
96
105template <typename ElementType, std::size_t Rank, typename StoragePolicy = HostStorage,
106 typename LayoutPolicy = stdex::layout_left,
107 typename AccessorFactory = detail::tensor_accessor_factory_t<StoragePolicy>,
108 typename Extents = stdex::dextents<index_type, Rank>>
109class Tensor {
110 public:
111 static_assert(Rank == Extents::rank());
112
113 using element_type = ElementType;
114 using value_type = std::remove_cv_t<element_type>;
115 using storage_policy = StoragePolicy;
116 using layout_policy = LayoutPolicy;
118 using accessor_factory_type = AccessorFactory;
119 using accessor_type = typename accessor_factory_type::template accessor_t<element_type>;
120 using const_accessor_type = typename accessor_factory_type::template accessor_t<element_type const>;
121 using device_accessor_type = detail::tensor_device_accessor_t<accessor_factory_type, element_type>;
122 using const_device_accessor_type = detail::tensor_device_accessor_t<accessor_factory_type, element_type const>;
123 using extents_type = Extents;
124 using handle_type = typename const_accessor_type::data_handle_type;
125 using mutable_handle_type = typename accessor_type::data_handle_type;
126 using index_type = typename extents_type::index_type;
127 using mapping_type = typename layout_policy::template mapping<extents_type>;
129 using backend_selector_type = typename storage_policy::backend_selector_type;
130 using mdspan_type = stdex::mdspan<element_type, extents_type, layout_policy, accessor_type>;
131 using const_mdspan_type = stdex::mdspan<element_type const, extents_type, layout_policy, const_accessor_type>;
132
133 using storage_type = typename storage_policy::template storage_t<element_type>;
134
136 static constexpr bool immediately_readable = requires(storage_type const& storage) {
137 { storage_policy::make_handle(storage) } -> std::convertible_to<handle_type>;
138 };
139
141 static constexpr bool immediately_writable = requires(storage_type& storage) {
142 { storage_policy::make_handle(storage) } -> std::convertible_to<mutable_handle_type>;
143 };
144
146 static constexpr bool deferred_readable =
147 requires(storage_type const& storage) { storage_policy::make_data_descriptor(storage); };
148
150 static constexpr bool deferred_writable =
151 requires(storage_type& storage) { storage_policy::make_data_descriptor(storage); };
152
154 template <typename NewLayoutPolicy>
157
159 template <typename NewExtents>
161 Tensor<element_type, NewExtents::rank(), storage_policy, layout_policy, accessor_factory_type, NewExtents>;
162
165 requires(Rank > 0)
166 = default;
167
170 requires(Rank == 0 && detail::DefaultTensorStorage<storage_type>)
172 {}
173
176 requires(Rank == 0 && detail::DefaultTensorStorage<storage_type>)
178 {}
179
185 Tensor(Tensor const& other) = default;
186
191 Tensor(Tensor&& other) = default;
192
196 Tensor& operator=(Tensor const& other) = default;
197
203 Tensor& operator=(Tensor&& other) = default;
204
211 template <TensorView InputTensor>
212 requires(tensor_mdspec_t<InputTensor>::rank() == extents_type::rank() &&
213 std::default_initializable<accessor_factory_type> && detail::DefaultTensorStorage<storage_type>)
214 explicit Tensor(InputTensor const& input)
215 : Tensor(uninitialized, convert_tensor_extents<extents_type>(input.extents()))
216 {
217#if UNI20_HAS_MPREAL
218 if constexpr (has_runtime_precision_v<value_type> && requires { input.default_precision_if_set(); })
219 if (auto p = input.default_precision_if_set()) data_.default_precision(*p);
220#endif
221 copy(*this, input);
222 }
223
227 explicit Tensor(extents_type const& exts, accessor_factory_type accessor_factory = accessor_factory_type{})
228 requires detail::DefaultTensorStorage<storage_type>
229 : Tensor(internal_tag{}, make_payload(make_default_mapping(exts), std::move(accessor_factory)))
230 {}
231
234 explicit Tensor(extents_type const& exts, Precision p)
235 requires(std::same_as<storage_policy, HostStorage> && requires(Precision p) { require_precision<value_type>(p); })
236 : Tensor(internal_tag{}, make_precision_payload(exts, p, StorageInitialization::Zero))
237 {}
238
242 requires(std::same_as<storage_policy, HostStorage> && requires(Precision p) { require_precision<value_type>(p); })
243 : Tensor(internal_tag{}, make_precision_payload(exts, p, StorageInitialization::Uninitialized))
244 {}
245
247 template <class... Args>
248 requires(std::same_as<storage_policy, HostStorage> &&
249 requires(Precision p) { require_precision<value_type>(p); } && extents_type::rank_dynamic() == Rank &&
250 sizeof...(Args) == Rank + 1 &&
251 std::same_as<std::tuple_element_t<Rank, std::tuple<Args...>>, Precision> &&
252 detail::integral_prefix<std::tuple<Args...>>(std::make_index_sequence<Rank>{}))
253 explicit Tensor(Args... args)
254 : Tensor(precision_extents(std::tuple{args...}, std::make_index_sequence<Rank>{}),
255 std::get<Rank>(std::tuple{args...}))
256 {}
257
258 template <class... Args>
259 requires(std::same_as<storage_policy, HostStorage> &&
260 requires(Precision p) { require_precision<value_type>(p); } && extents_type::rank_dynamic() == Rank &&
261 sizeof...(Args) == Rank + 1 &&
262 std::same_as<std::tuple_element_t<Rank, std::tuple<Args...>>, Precision> &&
263 detail::integral_prefix<std::tuple<Args...>>(std::make_index_sequence<Rank>{}))
264 explicit Tensor(uninitialized_t, Args... args)
265 : Tensor(uninitialized, precision_extents(std::tuple{args...}, std::make_index_sequence<Rank>{}),
266 std::get<Rank>(std::tuple{args...}))
267 {}
268
269#if UNI20_HAS_MPREAL
271 Precision default_precision() const
273 {
274 return data_.default_precision();
275 }
276 std::optional<Precision> default_precision_if_set() const noexcept
278 {
279 return data_.default_precision_if_set();
280 }
282 void default_precision(Precision p)
283 requires has_runtime_precision_v<value_type>
284 {
285 data_.default_precision(p);
286 }
287#endif
288
290 explicit Tensor(uninitialized_t, extents_type const& exts,
291 accessor_factory_type accessor_factory = accessor_factory_type{})
292 requires detail::DefaultTensorStorage<storage_type>
293 : Tensor(internal_tag{}, make_payload(make_default_mapping(exts), std::move(accessor_factory),
295 {}
296
304 template <class Policy = storage_policy>
305 requires requires(typename Policy::context_type& context, std::size_t count) {
306 {
307 Policy::template make_storage<element_type>(context, count, StorageInitialization::Uninitialized)
308 } -> std::same_as<storage_type>;
309 }
310 explicit Tensor(typename Policy::context_type& context, extents_type const& exts,
311 accessor_factory_type accessor_factory = accessor_factory_type{})
312 : Tensor(internal_tag{}, make_payload(context, make_default_mapping(exts), std::move(accessor_factory)))
313 {}
314
316 template <class Policy = storage_policy>
317 requires requires(typename Policy::context_type& context, std::size_t count) {
318 {
319 Policy::template make_storage<element_type>(context, count, StorageInitialization::Uninitialized)
320 } -> std::same_as<storage_type>;
321 }
322 explicit Tensor(uninitialized_t, typename Policy::context_type& context, extents_type const& exts,
323 accessor_factory_type accessor_factory = accessor_factory_type{})
324 : Tensor(internal_tag{}, make_payload(context, make_default_mapping(exts), std::move(accessor_factory),
326 {}
327
335 template <class Placement>
336 requires requires(Placement const& placement, std::size_t count) {
337 {
338 storage_policy::template make_storage<element_type>(placement, count, StorageInitialization::Uninitialized)
339 } -> std::same_as<storage_type>;
340 }
341 explicit Tensor(Placement const& placement, extents_type const& exts,
342 accessor_factory_type accessor_factory = accessor_factory_type{})
343 : Tensor(internal_tag{}, make_payload(placement, make_default_mapping(exts), std::move(accessor_factory)))
344 {}
345
347 template <class Placement>
348 requires requires(Placement const& placement, std::size_t count) {
349 {
350 storage_policy::template make_storage<element_type>(placement, count, StorageInitialization::Uninitialized)
351 } -> std::same_as<storage_type>;
352 }
353 explicit Tensor(uninitialized_t, Placement const& placement, extents_type const& exts,
354 accessor_factory_type accessor_factory = accessor_factory_type{})
355 : Tensor(internal_tag{}, make_payload(placement, make_default_mapping(exts), std::move(accessor_factory),
357 {}
358
365 template <std::integral... DynamicExtents>
366 requires(extents_type::rank() > 0 && extents_type::rank_dynamic() == extents_type::rank() &&
367 sizeof...(DynamicExtents) == extents_type::rank() && detail::DefaultTensorStorage<storage_type>)
368 explicit Tensor(DynamicExtents... dynamic_extents)
369 : Tensor(extents_type{static_cast<index_type>(dynamic_extents)...})
370 {}
371
373 template <std::integral... DynamicExtents>
374 requires(extents_type::rank() > 0 && extents_type::rank_dynamic() == extents_type::rank() &&
375 sizeof...(DynamicExtents) == extents_type::rank() && detail::DefaultTensorStorage<storage_type>)
376 explicit Tensor(uninitialized_t, DynamicExtents... dynamic_extents)
377 : Tensor(uninitialized, extents_type{static_cast<index_type>(dynamic_extents)...})
378 {}
379
385 template <class Policy = storage_policy, std::integral... DynamicExtents>
386 requires(extents_type::rank() > 0 && extents_type::rank_dynamic() == extents_type::rank() &&
387 sizeof...(DynamicExtents) == extents_type::rank() &&
388 requires(typename Policy::context_type& context, std::size_t count) {
389 {
390 Policy::template make_storage<element_type>(context, count, StorageInitialization::Uninitialized)
391 } -> std::same_as<storage_type>;
392 })
393 explicit Tensor(typename Policy::context_type& context, DynamicExtents... dynamic_extents)
394 : Tensor(context, extents_type{static_cast<index_type>(dynamic_extents)...})
395 {}
396
398 template <class Policy = storage_policy, std::integral... DynamicExtents>
399 requires(extents_type::rank() > 0 && extents_type::rank_dynamic() == extents_type::rank() &&
400 sizeof...(DynamicExtents) == extents_type::rank() &&
401 requires(typename Policy::context_type& context, std::size_t count) {
402 {
403 Policy::template make_storage<element_type>(context, count, StorageInitialization::Uninitialized)
404 } -> std::same_as<storage_type>;
405 })
406 explicit Tensor(uninitialized_t, typename Policy::context_type& context, DynamicExtents... dynamic_extents)
407 : Tensor(uninitialized, context, extents_type{static_cast<index_type>(dynamic_extents)...})
408 {}
409
415 template <typename MappingBuilder>
416 requires(layout::mapping_builder_for<MappingBuilder, layout_policy, extents_type> &&
417 (!std::same_as<std::remove_cvref_t<MappingBuilder>, accessor_factory_type>) &&
418 detail::DefaultTensorStorage<storage_type>)
419 explicit Tensor(extents_type const& exts, MappingBuilder&& mapping_builder,
420 accessor_factory_type accessor_factory = accessor_factory_type{})
421 : Tensor(internal_tag{},
422 make_payload(std::forward<MappingBuilder>(mapping_builder)(exts), std::move(accessor_factory)))
423 {}
424
426 template <typename MappingBuilder>
427 requires(layout::mapping_builder_for<MappingBuilder, layout_policy, extents_type> &&
428 (!std::same_as<std::remove_cvref_t<MappingBuilder>, accessor_factory_type>) &&
429 detail::DefaultTensorStorage<storage_type>)
430 explicit Tensor(uninitialized_t, extents_type const& exts, MappingBuilder&& mapping_builder,
431 accessor_factory_type accessor_factory = accessor_factory_type{})
432 : Tensor(internal_tag{}, make_payload(std::forward<MappingBuilder>(mapping_builder)(exts),
433 std::move(accessor_factory), StorageInitialization::Uninitialized))
434 {}
435
440 explicit Tensor(extents_type const& exts, std::array<index_type, extents_type::rank()> const& strides,
441 accessor_factory_type accessor_factory = accessor_factory_type{})
442 requires(std::constructible_from<mapping_type, extents_type const&,
443 std::array<index_type, extents_type::rank()> const&> &&
444 detail::DefaultTensorStorage<storage_type>)
445 : Tensor(internal_tag{}, make_payload(mapping_type{exts, strides}, std::move(accessor_factory)))
446 {}
447
449 explicit Tensor(uninitialized_t, extents_type const& exts,
450 std::array<index_type, extents_type::rank()> const& strides,
451 accessor_factory_type accessor_factory = accessor_factory_type{})
452 requires(std::constructible_from<mapping_type, extents_type const&,
453 std::array<index_type, extents_type::rank()> const&> &&
454 detail::DefaultTensorStorage<storage_type>)
455 : Tensor(internal_tag{}, make_payload(mapping_type{exts, strides}, std::move(accessor_factory),
457 {}
458
466 void reset_shape(extents_type const& exts)
467 requires(std::copy_constructible<accessor_factory_type> && std::is_nothrow_swappable_v<mapping_type> &&
468 std::is_nothrow_swappable_v<storage_type> && std::is_nothrow_swappable_v<accessor_factory_type>)
469 {
470 auto mapping = make_default_mapping(exts);
471 Tensor replacement(internal_tag{}, make_payload_like(data_, std::move(mapping), accessor_factory_));
472 this->swap_state(replacement);
473 }
474
477 requires(std::copy_constructible<accessor_factory_type> && std::is_nothrow_swappable_v<mapping_type> &&
478 std::is_nothrow_swappable_v<storage_type> && std::is_nothrow_swappable_v<accessor_factory_type>)
479 {
480 auto mapping = make_default_mapping(exts);
481 Tensor replacement(internal_tag{}, make_payload_like(data_, std::move(mapping), accessor_factory_,
483 this->swap_state(replacement);
484 }
485
490 template <class Placement>
491 requires requires(storage_type const& storage, Placement const& placement) {
492 { storage_policy::storage_is_compatible(storage, placement) } -> std::convertible_to<bool>;
493 }
494 [[nodiscard]] bool storage_is_compatible(Placement const& placement) const
495 {
496 return storage_policy::storage_is_compatible(data_, placement);
497 }
498
506 template <class Placement>
507 requires(std::copy_constructible<accessor_factory_type> && std::is_nothrow_swappable_v<mapping_type> &&
508 std::is_nothrow_swappable_v<storage_type> && std::is_nothrow_swappable_v<accessor_factory_type> &&
509 requires(Placement const& required_placement, std::size_t count) {
510 {
511 storage_policy::template make_storage<element_type>(required_placement, count,
513 } -> std::same_as<storage_type>;
514 })
515 void replace(extents_type const& exts, Placement const& placement)
516 {
517 auto mapping = make_default_mapping(exts);
518 Tensor replacement(internal_tag{}, make_payload(placement, std::move(mapping), accessor_factory_));
519 this->swap_state(replacement);
520 }
521
523 template <class Placement>
524 requires(std::copy_constructible<accessor_factory_type> && std::is_nothrow_swappable_v<mapping_type> &&
525 std::is_nothrow_swappable_v<storage_type> && std::is_nothrow_swappable_v<accessor_factory_type> &&
526 requires(Placement const& required_placement, std::size_t count) {
527 {
528 storage_policy::template make_storage<element_type>(required_placement, count,
530 } -> std::same_as<storage_type>;
531 })
532 void replace(uninitialized_t, extents_type const& exts, Placement const& placement)
533 {
534 auto mapping = make_default_mapping(exts);
535 Tensor replacement(internal_tag{}, make_payload(placement, std::move(mapping), accessor_factory_,
537 this->swap_state(replacement);
538 }
539
542 [[nodiscard]] storage_type& storage() noexcept { return data_; }
543
546 [[nodiscard]] storage_type const& storage() const noexcept { return data_; }
547
553 [[nodiscard]] storage_type release_storage() && noexcept(std::is_nothrow_move_constructible_v<storage_type>)
554 {
555 return std::move(data_);
556 }
557
562 [[nodiscard]] accessor_factory_type
563 release_accessor_factory() && noexcept(std::is_nothrow_move_constructible_v<accessor_factory_type>)
564 {
565 return std::move(accessor_factory_);
566 }
567
578 accessor_factory_type accessor_factory = accessor_factory_type{})
579 requires requires(storage_type const& value) { value.size(); }
580 {
581 ERROR_IF(std::cmp_less(storage.size(), mapping.required_span_size()),
582 "adopted tensor storage is smaller than the mapping's required span");
583 return Tensor(internal_tag{}, ctor_payload{std::move(mapping), std::move(storage), std::move(accessor_factory)});
584 }
585
593 requires requires(storage_type const& value) { value.size(); }
594 {
595 ERROR_IF(std::cmp_less(data_.size(), mapping.required_span_size()),
596 "replacement tensor mapping exceeds the owned storage");
597 mapping_ = std::move(mapping);
598 }
599
602 [[nodiscard]] static constexpr auto backend_selector() noexcept -> backend_selector_type
603 {
604 return storage_policy::backend_selector();
605 }
606
609 [[nodiscard]] auto mdspan() noexcept -> mdspan_type
610 requires immediately_writable
611 {
612 return mdspan_type(this->mutable_handle(), mapping_, this->accessor());
613 }
614
617 [[nodiscard]] auto mdspan() const noexcept -> const_mdspan_type
618 requires immediately_readable
619 {
620 return const_mdspan_type(this->handle(), mapping_, this->accessor());
621 }
622
626 [[nodiscard]] auto mdspec() noexcept
628 {
629 if constexpr (immediately_writable)
630 {
631 return this->mdspan();
632 }
633 else
634 {
635 using descriptor_type = decltype(storage_policy::make_data_descriptor(data_));
636 using span_type =
638 return span_type{storage_policy::make_data_descriptor(data_), mapping_, this->device_accessor()};
639 }
640 }
641
645 [[nodiscard]] auto mdspec() const noexcept
647 {
648 if constexpr (immediately_readable)
649 {
650 return this->mdspan();
651 }
652 else
653 {
654 using descriptor_type = decltype(storage_policy::make_data_descriptor(data_));
655 using span_type =
657 return span_type{storage_policy::make_data_descriptor(data_), mapping_, this->device_accessor()};
658 }
659 }
660
663 [[nodiscard]] auto handle() const noexcept -> handle_type
664 requires immediately_readable
665 {
666 return storage_policy::make_handle(data_);
667 }
668
671 [[nodiscard]] auto mutable_handle() noexcept -> mutable_handle_type
672 requires immediately_writable
673 {
674 return storage_policy::make_handle(data_);
675 }
676
684 [[nodiscard]] auto data() noexcept -> element_type* requires(immediately_writable &&
685 (std::same_as<layout_type, stdex::layout_left> ||
686 std::same_as<layout_type, stdex::layout_right>) &&
689 return this->mutable_handle();
690 }
691
699 [[nodiscard]] auto data() const noexcept
700 -> element_type const* requires(immediately_readable &&
701 (std::same_as<layout_type, stdex::layout_left> ||
702 std::same_as<layout_type, stdex::layout_right>) &&
704 std::same_as<handle_type, element_type const*>) { return this->handle(); }
705
708 [[nodiscard]] auto mapping() const noexcept -> mapping_type const&
709 {
710 return mapping_;
711 }
712
715 [[nodiscard]] auto extents() const noexcept -> extents_type const& { return mapping_.extents(); }
716
720 [[nodiscard]] auto extent(size_type axis) const noexcept { return this->extents().extent(axis); }
721
724 [[nodiscard]] auto accessor() noexcept -> accessor_type
725 {
726 return accessor_factory_.template make_accessor<element_type>(data_);
727 }
728
731 [[nodiscard]] auto accessor() const noexcept -> const_accessor_type
732 {
733 return accessor_factory_.template make_accessor<element_type const>(data_);
734 }
735
737 [[nodiscard]] auto device_accessor() noexcept -> device_accessor_type
738 {
739 if constexpr (requires { accessor_factory_.template make_device_accessor<element_type>(data_); })
740 return accessor_factory_.template make_device_accessor<element_type>(data_);
741 else
742 return this->accessor();
743 }
744
746 [[nodiscard]] auto device_accessor() const noexcept -> const_device_accessor_type
747 {
748 if constexpr (requires { accessor_factory_.template make_device_accessor<element_type const>(data_); })
749 return accessor_factory_.template make_device_accessor<element_type const>(data_);
750 else
751 return this->accessor();
752 }
753
756 [[nodiscard]] auto size() const noexcept -> size_type { return mapping_.required_span_size(); }
757
760 static constexpr size_type rank() noexcept { return extents_type::rank(); }
761
764 static constexpr size_type rank_dynamic() noexcept { return extents_type::rank_dynamic(); }
765
768 [[nodiscard]] auto rows() const noexcept
769 requires(extents_type::rank() == 2)
770 {
771 return this->extent(0);
772 }
773
776 [[nodiscard]] auto cols() const noexcept
777 requires(extents_type::rank() == 2)
778 {
779 return this->extent(1);
780 }
781
783 template <class... Index>
784 requires(sizeof...(Index) == extents_type::rank() && immediately_writable)
785 decltype(auto) operator[](Index... indices) noexcept
786 {
787 return this->mdspan()[indices...];
788 }
789
791 template <class... Index>
792 requires(sizeof...(Index) == extents_type::rank() && immediately_readable)
793 decltype(auto) operator[](Index... indices) const noexcept
794 {
795 return this->mdspan()[indices...];
796 }
797
801 decltype(auto) operator[](std::array<index_type, extents_type::rank()> const& indices) noexcept
802 requires immediately_writable
803 {
804 auto span = this->mdspan();
805 return [&]<std::size_t... I>(std::index_sequence<I...>) -> decltype(auto) {
806 return span[indices[I]...];
807 }(std::make_index_sequence<extents_type::rank()>{});
808 }
809
813 decltype(auto) operator[](std::array<index_type, extents_type::rank()> const& indices) const noexcept
814 requires immediately_readable
815 {
816 auto span = this->mdspan();
817 return [&]<std::size_t... I>(std::index_sequence<I...>) -> decltype(auto) {
818 return span[indices[I]...];
819 }(std::make_index_sequence<extents_type::rank()>{});
820 }
821
822 private:
823 struct internal_tag
824 {};
825
826 struct ctor_payload
827 {
828 mapping_type mapping;
829 storage_type storage;
830 accessor_factory_type accessor_factory;
831 };
832
833 Tensor(internal_tag, ctor_payload payload)
834 : mapping_(std::move(payload.mapping)), data_(std::move(payload.storage)),
835 accessor_factory_(std::move(payload.accessor_factory))
836 {}
837
838 template <class Tuple, std::size_t... I>
839 static extents_type precision_extents(Tuple const& args, std::index_sequence<I...>)
840 {
841 return extents_type{static_cast<index_type>(std::get<I>(args))...};
842 }
843 static ctor_payload make_precision_payload(extents_type const& exts, Precision p,
844 StorageInitialization initialization)
845 requires(std::same_as<storage_policy, HostStorage> && requires(Precision p) { require_precision<value_type>(p); })
846 {
847 require_precision<value_type>(p);
848 auto mapping = make_default_mapping(exts);
849 if constexpr (!has_runtime_precision_v<value_type>)
850 return make_payload(std::move(mapping), accessor_factory_type{}, initialization);
851 else
852 {
853 auto storage = storage_policy::template make_storage<element_type>(
854 static_cast<std::size_t>(mapping.required_span_size()), initialization, p);
855 return ctor_payload{std::move(mapping), std::move(storage), accessor_factory_type{}};
856 }
857 }
858
859 static ctor_payload make_payload(mapping_type mapping, accessor_factory_type accessor_factory,
861 {
862 auto storage = create_storage(static_cast<std::size_t>(mapping.required_span_size()), initialization);
863 return ctor_payload{std::move(mapping), std::move(storage), std::move(accessor_factory)};
864 }
865
866 template <class Policy = storage_policy>
867 static ctor_payload make_payload(typename Policy::context_type& context, mapping_type mapping,
868 accessor_factory_type accessor_factory,
870 requires requires(typename Policy::context_type& storage_context, std::size_t count) {
871 {
872 Policy::template make_storage<element_type>(storage_context, count, initialization)
873 } -> std::same_as<storage_type>;
874 }
875 {
876 auto const count = static_cast<std::size_t>(mapping.required_span_size());
877 auto storage = Policy::template make_storage<element_type>(context, count, initialization);
878 return ctor_payload{std::move(mapping), std::move(storage), std::move(accessor_factory)};
879 }
880
881 template <class Placement>
882 static ctor_payload make_payload(Placement const& placement, mapping_type mapping,
883 accessor_factory_type accessor_factory,
885 requires requires(Placement const& required_placement, std::size_t count) {
886 {
887 storage_policy::template make_storage<element_type>(required_placement, count, initialization)
888 } -> std::same_as<storage_type>;
889 }
890 {
891 auto const count = static_cast<std::size_t>(mapping.required_span_size());
892 auto storage = storage_policy::template make_storage<element_type>(placement, count, initialization);
893 return ctor_payload{std::move(mapping), std::move(storage), std::move(accessor_factory)};
894 }
895
896 static ctor_payload make_payload_like(storage_type const& source, mapping_type mapping,
897 accessor_factory_type accessor_factory,
899 {
900 auto const count = static_cast<std::size_t>(mapping.required_span_size());
901 if constexpr (requires {
902 storage_policy::template make_storage_like<element_type>(source, count, initialization);
903 })
904 {
905 auto storage = storage_policy::template make_storage_like<element_type>(source, count, initialization);
906 return ctor_payload{std::move(mapping), std::move(storage), std::move(accessor_factory)};
907 }
908 else
909 {
910 auto storage = create_storage(count, initialization);
911 return ctor_payload{std::move(mapping), std::move(storage), std::move(accessor_factory)};
912 }
913 }
914
915 static storage_type create_storage(std::size_t count, StorageInitialization initialization)
916 {
917 if constexpr (requires { storage_policy::template make_storage<element_type>(count, initialization); })
918 {
919 return storage_policy::template make_storage<element_type>(count, initialization);
920 }
921 else
922 {
923 // Ordinary contiguous host containers can implement the policy directly.
924 // Opaque/device storage must provide its own initialization-aware factory.
925 static_assert(std::ranges::contiguous_range<storage_type>,
926 "Non-contiguous storage requires an initialization-aware make_storage factory");
927 auto storage = [count] {
928 if constexpr (requires(storage_type& value) { value.resize(count); })
929 {
930 storage_type result{};
931 result.resize(count);
932 return result;
933 }
934 else
935 {
936 return storage_type(count);
937 }
938 }();
939 detail::initialize_host_elements(std::ranges::data(storage), count, initialization);
940 return storage;
941 }
942 }
943
944 static constexpr auto make_default_mapping(extents_type const& exts)
945 {
946 if constexpr (requires { typename storage_policy::default_mapping_builder; })
947 {
948 using builder_type = typename storage_policy::default_mapping_builder;
949 if constexpr (layout::mapping_builder_for<builder_type, layout_policy, extents_type>)
950 {
951 return builder_type{}(exts);
952 }
953 else
954 {
955 return layout::make_mapping<layout_policy>(exts);
956 }
957 }
958 else
959 {
960 return layout::make_mapping<layout_policy>(exts);
961 }
962 }
963
964 void swap_state(Tensor& other) noexcept
965 requires(std::is_nothrow_swappable_v<mapping_type> && std::is_nothrow_swappable_v<storage_type> &&
966 std::is_nothrow_swappable_v<accessor_factory_type>)
967 {
968 using std::swap;
969 swap(mapping_, other.mapping_);
970 swap(data_, other.data_);
971 swap(accessor_factory_, other.accessor_factory_);
972 }
973
974 [[no_unique_address]] mapping_type mapping_{};
975 storage_type data_;
976 [[no_unique_address]] accessor_factory_type accessor_factory_{};
977};
978
979template <typename ElementType, std::size_t Rank, typename StoragePolicy, typename LayoutPolicy,
980 typename AccessorFactory, typename Extents>
981inline constexpr bool
982 enable_owning_tensor<Tensor<ElementType, Rank, StoragePolicy, LayoutPolicy, AccessorFactory, Extents>> = true;
983
987template <TensorView InputTensor>
988Tensor(InputTensor const&)
990 detail::materialized_layout_t<void, tensor_mdspec_t<InputTensor>>, DefaultAccessorFactory>;
991
999template <typename ElementType, typename Extents, typename StoragePolicy = HostStorage,
1000 typename LayoutPolicy = stdex::layout_left,
1001 typename AccessorFactory = detail::tensor_accessor_factory_t<StoragePolicy>>
1002using BasicTensor = Tensor<ElementType, Extents::rank(), StoragePolicy, LayoutPolicy, AccessorFactory, Extents>;
1003
1004} // namespace uni20
Exact arithmetic or an explicit binary working precision.
Definition core/precision.hpp:13
General-purpose owning tensor with immediate or deferred mdspan metadata.
Definition basic_tensor.hpp:109
auto device_accessor() noexcept -> device_accessor_type
Construct the accessor stored in a mutable mdspec.
Definition basic_tensor.hpp:737
static Tensor adopt_storage(mapping_type mapping, storage_type storage, accessor_factory_type accessor_factory=accessor_factory_type{})
Adopt an existing storage container without reallocating it.
Definition basic_tensor.hpp:577
typename accessor_factory_type::template accessor_t< element_type const > const_accessor_type
Definition basic_tensor.hpp:120
auto mapping() const noexcept -> mapping_type const &
Return the tensor mapping.
Definition basic_tensor.hpp:708
Tensor(typename Policy::context_type &context, DynamicExtents... dynamic_extents)
Construct a fully dynamic tensor in an explicit storage context.
Definition basic_tensor.hpp:393
AccessorFactory accessor_factory_type
Definition basic_tensor.hpp:118
auto mdspan() const noexcept -> const_mdspan_type
Resolve a read-only mdspan over the owned storage.
Definition basic_tensor.hpp:617
static constexpr auto backend_selector() noexcept -> backend_selector_type
Return the default backend selector associated with this tensor's storage.
Definition basic_tensor.hpp:602
typename layout_policy::template mapping< extents_type > mapping_type
Definition basic_tensor.hpp:127
storage_type release_storage() &&noexcept(std::is_nothrow_move_constructible_v< storage_type >)
Transfer the underlying storage container out of this tensor.
Definition basic_tensor.hpp:553
static constexpr bool immediately_writable
Whether mutable storage provides a writable data handle without acquisition.
Definition basic_tensor.hpp:141
static constexpr bool immediately_readable
Whether const storage provides a readable data handle without acquisition.
Definition basic_tensor.hpp:136
typename const_accessor_type::data_handle_type handle_type
Definition basic_tensor.hpp:124
Tensor & operator=(Tensor &&other)=default
Move-assign tensor storage and descriptor state.
Tensor(extents_type const &exts, accessor_factory_type accessor_factory=accessor_factory_type{})
Construct a zero-initialized tensor with default layout and accessor factory.
Definition basic_tensor.hpp:227
static constexpr bool deferred_readable
Whether const storage provides deferred read metadata.
Definition basic_tensor.hpp:146
Tensor(uninitialized_t, DynamicExtents... dynamic_extents)
Allocate the requested tensor shape without numerical initialization.
Definition basic_tensor.hpp:376
bool storage_is_compatible(Placement const &placement) const
Report whether owned storage satisfies a placement requirement.
Definition basic_tensor.hpp:494
void reset_shape(uninitialized_t, extents_type const &exts)
Replace the shape with explicitly uninitialized storage.
Definition basic_tensor.hpp:476
auto extent(size_type axis) const noexcept
Return one tensor extent.
Definition basic_tensor.hpp:720
auto rows() const noexcept
Return the matrix row count for rank-two tensors.
Definition basic_tensor.hpp:768
auto data() noexcept -> element_type *requires(immediately_writable &&(std::same_as< layout_type, stdex::layout_left >||std::same_as< layout_type, stdex::layout_right >) &&is_default_accessor_v< accessor_type > &&std::same_as< mutable_handle_type, element_type * >)
Return contiguous mutable element storage for a canonical tensor.
Definition basic_tensor.hpp:684
decltype(auto) operator[](Index... indices) const noexcept
Access a const tensor element.
Definition basic_tensor.hpp:793
layout_policy layout_type
Definition basic_tensor.hpp:117
auto device_accessor() const noexcept -> const_device_accessor_type
Construct the accessor stored in a read-only mdspec.
Definition basic_tensor.hpp:746
static constexpr bool deferred_writable
Whether mutable storage provides deferred write metadata.
Definition basic_tensor.hpp:150
auto mdspec() noexcept
Return mutable mdspan metadata for explicit data-handle acquisition.
Definition basic_tensor.hpp:626
Tensor(uninitialized_t, extents_type const &exts, std::array< index_type, extents_type::rank()> const &strides, accessor_factory_type accessor_factory=accessor_factory_type{})
Allocate the requested tensor shape without numerical initialization.
Definition basic_tensor.hpp:449
auto size() const noexcept -> size_type
Return the number of elements in the mapped storage span.
Definition basic_tensor.hpp:756
static constexpr size_type rank_dynamic() noexcept
Return the number of dynamic extents.
Definition basic_tensor.hpp:764
auto mdspec() const noexcept
Return read-only mdspan metadata for explicit data-handle acquisition.
Definition basic_tensor.hpp:645
std::remove_cv_t< element_type > value_type
Definition basic_tensor.hpp:114
decltype(auto) operator[](Index... indices) noexcept
Access a mutable tensor element.
Definition basic_tensor.hpp:785
Tensor(uninitialized_t, typename Policy::context_type &context, extents_type const &exts, accessor_factory_type accessor_factory=accessor_factory_type{})
Allocate the requested tensor shape without numerical initialization.
Definition basic_tensor.hpp:322
storage_type const & storage() const noexcept
Access the owned storage container.
Definition basic_tensor.hpp:546
Tensor & operator=(Tensor const &other)=default
Copy-assign tensor storage and descriptor state.
Tensor(Tensor &&other)=default
Move-construct an owning tensor.
uni20::size_type size_type
Definition basic_tensor.hpp:128
typename accessor_factory_type::template accessor_t< element_type > accessor_type
Definition basic_tensor.hpp:119
stdex::mdspan< element_type, extents_type, layout_policy, accessor_type > mdspan_type
Definition basic_tensor.hpp:130
void replace(extents_type const &exts, Placement const &placement)
Replace tensor shape and storage to satisfy a placement requirement.
Definition basic_tensor.hpp:515
static constexpr size_type rank() noexcept
Return the tensor rank.
Definition basic_tensor.hpp:760
accessor_factory_type release_accessor_factory() &&noexcept(std::is_nothrow_move_constructible_v< accessor_factory_type >)
Transfer the accessor-factory state out of this tensor.
Definition basic_tensor.hpp:563
auto accessor() noexcept -> accessor_type
Construct the tensor's mutable accessor.
Definition basic_tensor.hpp:724
StoragePolicy storage_policy
Definition basic_tensor.hpp:115
Tensor(Tensor const &other)=default
Copy-construct a tensor with independent owned storage.
typename storage_policy::template storage_t< element_type > storage_type
Definition basic_tensor.hpp:133
Tensor(uninitialized_t, extents_type const &exts, accessor_factory_type accessor_factory=accessor_factory_type{})
Allocate the requested tensor shape without numerical initialization.
Definition basic_tensor.hpp:290
storage_type & storage() noexcept
Access the owned storage container.
Definition basic_tensor.hpp:542
typename extents_type::index_type index_type
Definition basic_tensor.hpp:126
void replace(uninitialized_t, extents_type const &exts, Placement const &placement)
Replace shape and placement with explicitly uninitialized storage.
Definition basic_tensor.hpp:532
Tensor(uninitialized_t, typename Policy::context_type &context, DynamicExtents... dynamic_extents)
Allocate the requested tensor shape without numerical initialization.
Definition basic_tensor.hpp:406
Tensor(extents_type const &exts, Precision p)
Construct actual zeros at the supplied default working precision.
Definition basic_tensor.hpp:234
Tensor(DynamicExtents... dynamic_extents)
Construct a fully dynamic tensor from one extent per axis.
Definition basic_tensor.hpp:368
Tensor(extents_type const &exts, MappingBuilder &&mapping_builder, accessor_factory_type accessor_factory=accessor_factory_type{})
Construct a tensor using a custom mapping builder.
Definition basic_tensor.hpp:419
auto accessor() const noexcept -> const_accessor_type
Construct the tensor's read-only accessor.
Definition basic_tensor.hpp:731
Tensor(Placement const &placement, extents_type const &exts, accessor_factory_type accessor_factory=accessor_factory_type{})
Construct a tensor with storage satisfying a placement value.
Definition basic_tensor.hpp:341
void replace_mapping(mapping_type mapping)
Replace the tensor mapping without moving or reallocating storage.
Definition basic_tensor.hpp:592
detail::tensor_device_accessor_t< accessor_factory_type, element_type const > const_device_accessor_type
Definition basic_tensor.hpp:122
Tensor(uninitialized_t, extents_type const &exts, MappingBuilder &&mapping_builder, accessor_factory_type accessor_factory=accessor_factory_type{})
Allocate the requested tensor shape without numerical initialization.
Definition basic_tensor.hpp:430
auto handle() const noexcept -> handle_type
Return the tensor's const storage handle.
Definition basic_tensor.hpp:663
Extents extents_type
Definition basic_tensor.hpp:123
auto data() const noexcept -> element_type const *requires(immediately_readable &&(std::same_as< layout_type, stdex::layout_left >||std::same_as< layout_type, stdex::layout_right >) &&is_default_accessor_v< const_accessor_type > &&std::same_as< handle_type, element_type const * >)
Return contiguous read-only element storage for a canonical tensor.
Definition basic_tensor.hpp:699
Tensor()
Default-construct a rank-zero tensor with its one logical element set to zero.
Definition basic_tensor.hpp:169
auto mdspan() noexcept -> mdspan_type
Resolve a writable mdspan over the owned storage.
Definition basic_tensor.hpp:609
Tensor(uninitialized_t, extents_type const &exts, Precision p)
Allocate uninitialized elements with a compatible working precision.
Definition basic_tensor.hpp:241
auto extents() const noexcept -> extents_type const &
Return the tensor extents.
Definition basic_tensor.hpp:715
Tensor()=default
Default-construct an empty positive-rank tensor without allocated storage.
detail::tensor_device_accessor_t< accessor_factory_type, element_type > device_accessor_type
Definition basic_tensor.hpp:121
Tensor(uninitialized_t, Placement const &placement, extents_type const &exts, accessor_factory_type accessor_factory=accessor_factory_type{})
Allocate the requested tensor shape without numerical initialization.
Definition basic_tensor.hpp:353
auto mutable_handle() noexcept -> mutable_handle_type
Return the tensor's mutable storage handle.
Definition basic_tensor.hpp:671
Tensor(extents_type const &exts, std::array< index_type, extents_type::rank()> const &strides, accessor_factory_type accessor_factory=accessor_factory_type{})
Construct a tensor from explicit extents and strides.
Definition basic_tensor.hpp:440
Tensor(InputTensor const &input)
Materialize an independent owning tensor from a readable tensor view.
Definition basic_tensor.hpp:214
typename accessor_type::data_handle_type mutable_handle_type
Definition basic_tensor.hpp:125
LayoutPolicy layout_policy
Definition basic_tensor.hpp:116
typename storage_policy::backend_selector_type backend_selector_type
Definition basic_tensor.hpp:129
void reset_shape(extents_type const &exts)
Replace the tensor shape and initialize its new elements to zero.
Definition basic_tensor.hpp:466
stdex::mdspan< element_type const, extents_type, layout_policy, const_accessor_type > const_mdspan_type
Definition basic_tensor.hpp:131
Tensor(typename Policy::context_type &context, extents_type const &exts, accessor_factory_type accessor_factory=accessor_factory_type{})
Construct a tensor in an explicit storage context.
Definition basic_tensor.hpp:310
Tensor(uninitialized_t)
Allocate the single rank-zero element without numerical initialization.
Definition basic_tensor.hpp:175
ElementType element_type
Definition basic_tensor.hpp:113
auto cols() const noexcept
Return the matrix column count for rank-two tensors.
Definition basic_tensor.hpp:776
Store a data descriptor with the mapping and accessor for an eventual mdspan.
Definition mdspec.hpp:31
Backend-dispatched copies into existing mdspan and tensor outputs.
locale_t value
Definition data_table.cpp:49
auto strides(S const &s)
Retrieve the strides associated with a strided mdspec-like type.
Definition mdspan/concepts.hpp:558
constexpr bool is_default_accessor_v
True when an accessor is stdex::default_accessor<T>.
Definition mdspan/concepts.hpp:182
Configured gateway to Uni20's mdspan implementation.
Mdspan-shaped metadata for storage whose data handle requires acquisition.
Definition qnum.hpp:403
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
constexpr bool has_runtime_precision_v
Whether numerical values of this scalar family carry runtime precision.
Definition runtime_precision.hpp:10
void swap(HostBuffer< ElementType > &lhs, HostBuffer< ElementType > &rhs) noexcept
Definition host_storage.hpp:307
std::ptrdiff_t size_type
Signed size and index type, used for tensor extents and indexing.
Definition types.hpp:18
void copy(BackendSelector selector, async::Async< OutputTensor > &output, async::Async< InputTensor > const &input)
Schedule a Tensor copy through an explicit backend selector.
Definition linalg/async/copy.hpp:174
std::ptrdiff_t index_type
Definition types.hpp:19
std::remove_cvref_t< detail::normalized_const_mdspec_t< T > > tensor_mdspec_t
Normalized readable MdspecLike type exposed by a tensor-level object.
Definition tensor/concepts.hpp:123
StorageInitialization
Numerical initialization requested from a storage implementation.
Definition common/initialization.hpp:26
std::size_t span
Definition presentation.cpp:2017
Factory that provides default accessors for tensor storage containers.
Definition basic_tensor.hpp:54
constexpr auto make_accessor(Storage const &) const noexcept -> accessor_t< ElementType >
Definition basic_tensor.hpp:58
stdex::default_accessor< ElementType > accessor_t
Definition basic_tensor.hpp:55
Default pageable-host tensor storage policy.
Definition host_storage.hpp:316
Request storage whose numerical values must be written before being read.
Definition common/initialization.hpp:17
Concepts for tensor-level objects that resolve mdspan kernel operands.
#define ERROR_IF(cond,...)
Definition trace.hpp:389