Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
block_tensor_mapped_view.hpp
Go to the documentation of this file.
1
7#pragma once
8
11
12#include <algorithm>
13#include <array>
14#include <concepts>
15#include <cstddef>
16#include <memory>
17#include <optional>
18#include <span>
19#include <stdexcept>
20#include <tuple>
21#include <type_traits>
22#include <utility>
23#include <vector>
24
25namespace uni20::detail
26{
27
28template <std::size_t Offset, class Tuple, std::size_t... I>
29auto tuple_slice_impl(Tuple const& tuple, std::index_sequence<I...>)
30{
31 return std::tuple{std::get<Offset + I>(tuple)...};
32}
33
34template <std::size_t Offset, std::size_t Count, class Tuple> auto tuple_slice(Tuple const& tuple)
35{
36 return tuple_slice_impl<Offset>(tuple, std::make_index_sequence<Count>{});
37}
38
39template <class Tuple> auto domain_from_tuple(Tuple&& tuple)
40{
41 return std::apply([](auto&&... spaces) { return Domain{std::forward<decltype(spaces)>(spaces)...}; },
42 std::forward<Tuple>(tuple));
43}
44
45template <class Tuple> auto codomain_from_tuple(Tuple&& tuple)
46{
47 return std::apply([](auto&&... spaces) { return Codomain{std::forward<decltype(spaces)>(spaces)...}; },
48 std::forward<Tuple>(tuple));
49}
50
51template <class Tensor, std::size_t Axis,
52 bool DomainAxis = Axis<std::remove_cvref_t<Tensor>::domain_type::size()> struct TensorAxisSpace;
53
54template <class Tensor, std::size_t Axis> struct TensorAxisSpace<Tensor, Axis, true>
55{
56 using tensor_type = std::remove_cvref_t<Tensor>;
57 using type = typename tensor_type::domain_type::template space_type<Axis>;
58};
59
60template <class Tensor, std::size_t Axis> struct TensorAxisSpace<Tensor, Axis, false>
61{
62 using tensor_type = std::remove_cvref_t<Tensor>;
63 static constexpr std::size_t codomain_axis = Axis - tensor_type::domain_type::size();
64 using type = typename tensor_type::codomain_type::template space_type<codomain_axis>;
65};
66
67template <class Tensor, std::size_t Axis> using tensor_axis_space_t = typename TensorAxisSpace<Tensor, Axis>::type;
68
69template <std::size_t Size>
70consteval auto is_valid_axis_permutation(std::array<std::size_t, Size> const& permutation) -> bool
71{
72 std::array<bool, Size> seen{};
73 for (std::size_t const axis : permutation)
74 {
75 if (axis >= Size || seen[axis]) return false;
76 seen[axis] = true;
77 }
78 return true;
79}
80
81template <std::size_t Size> consteval auto make_identity_axis_permutation()
82{
83 std::array<std::size_t, Size> permutation{};
84 for (std::size_t axis = 0; axis < Size; ++axis)
85 permutation[axis] = axis;
86 return permutation;
87}
88
89template <class Tensor, std::size_t... Axis> consteval auto key_axis_flags_impl(std::index_sequence<Axis...>)
90{
91 return std::array<bool, sizeof...(Axis)>{
92 BlockTensorSpaceTraits<tensor_axis_space_t<Tensor, Axis>>::has_block_coordinate...};
93}
94
95template <class Tensor> consteval auto key_axis_flags()
96{
97 return key_axis_flags_impl<Tensor>(std::make_index_sequence<std::remove_cvref_t<Tensor>::order()>{});
98}
99
100template <class Tensor, std::size_t... Axis> consteval auto dense_axis_flags_impl(std::index_sequence<Axis...>)
101{
102 return std::array<bool, sizeof...(Axis)>{
103 BlockTensorSpaceTraits<tensor_axis_space_t<Tensor, Axis>>::has_dense_axis...};
104}
105
106template <class Tensor> consteval auto dense_axis_flags()
107{
108 return dense_axis_flags_impl<Tensor>(std::make_index_sequence<std::remove_cvref_t<Tensor>::order()>{});
109}
110
111template <class Tensor, auto FactorPermutation, bool DenseAxes> consteval auto make_filtered_axis_permutation()
112{
113 using tensor_type = std::remove_cvref_t<Tensor>;
114 constexpr std::size_t order = tensor_type::order();
115 constexpr std::size_t filtered_count =
116 DenseAxes ? tensor_type::dense_block_order() : tensor_type::key_coordinate_count();
117 static_assert(std::tuple_size_v<decltype(FactorPermutation)> == order);
118 static_assert(is_valid_axis_permutation(FactorPermutation));
119
120 constexpr auto flags = [] {
121 if constexpr (DenseAxes)
122 return dense_axis_flags<tensor_type>();
123 else
124 return key_axis_flags<tensor_type>();
125 }();
126
127 std::array<std::size_t, order> source_filtered_axis{};
128 std::size_t source_count = 0;
129 for (std::size_t axis = 0; axis < order; ++axis)
130 {
131 if (flags[axis]) source_filtered_axis[axis] = source_count++;
132 }
133
134 std::array<std::size_t, filtered_count> permutation{};
135 std::size_t output = 0;
136 for (std::size_t factor = 0; factor < order; ++factor)
137 {
138 std::size_t const source_factor = FactorPermutation[factor];
139 if (flags[source_factor]) permutation[output++] = source_filtered_axis[source_factor];
140 }
141 if (output != filtered_count) throw "invalid filtered BlockTensor axis permutation";
142 return permutation;
143}
144
145template <std::size_t CoordinateCount>
146auto permute_key(BlockKey<CoordinateCount> const& source, std::array<std::size_t, CoordinateCount> const& permutation)
147 -> BlockKey<CoordinateCount>
148{
149 std::array<std::size_t, CoordinateCount> coordinates{};
150 for (std::size_t axis = 0; axis < CoordinateCount; ++axis)
151 {
152 coordinates[axis] = source.coordinate(permutation[axis]);
153 }
154 return BlockKey<CoordinateCount>{coordinates};
155}
156
157template <class Block, bool Immediate = MdspanLike<std::remove_cvref_t<Block>>> struct StridedBlock;
158
159template <class Block> struct StridedBlock<Block, true>
160{
161 using source_type = std::remove_cvref_t<Block>;
162 using extents_type = stdex::dextents<typename source_type::index_type, source_type::rank()>;
163 using type = stdex::mdspan<typename source_type::element_type, extents_type, stdex::layout_stride,
164 typename source_type::accessor_type>;
165};
166
167template <class Block> struct StridedBlock<Block, false>
168{
169 using source_type = std::remove_cvref_t<Block>;
170 static_assert(MdspecLike<source_type>);
171 using extents_type = stdex::dextents<typename source_type::index_type, source_type::rank()>;
172 using type = mdspec<typename source_type::element_type, extents_type, stdex::layout_stride,
173 typename source_type::accessor_type, typename source_type::data_descriptor_type>;
174};
175
176template <class Block> using strided_block_t = typename StridedBlock<Block>::type;
177
178template <MdspecLike Block, std::size_t DenseBlockOrder>
179 requires(DenseBlockOrder == 0 || StridedMdspecLike<Block>)
180auto permute_block_mdspec(Block block, std::array<std::size_t, DenseBlockOrder> const& permutation)
181 -> strided_block_t<Block>
182{
183 using result_type = strided_block_t<Block>;
184 using extents_type = typename result_type::extents_type;
185 using mapping_type = typename result_type::mapping_type;
186 using index_type = typename extents_type::index_type;
187
188 std::array<std::size_t, DenseBlockOrder> extents{};
189 std::array<index_type, DenseBlockOrder> strides{};
190 if constexpr (DenseBlockOrder > 0)
191 {
192 for (std::size_t axis = 0; axis < DenseBlockOrder; ++axis)
193 {
194 extents[axis] = static_cast<std::size_t>(block.extent(permutation[axis]));
195 strides[axis] = static_cast<index_type>(block.stride(permutation[axis]));
196 }
197 }
198 auto const result_extents = make_extents<extents_type>(extents);
199 if constexpr (MdspanLike<Block>)
200 {
201 return result_type(block.data_handle(), mapping_type(result_extents, strides), block.accessor());
202 }
203 else
204 {
205 return result_type(std::move(block.data_descriptor()), mapping_type(result_extents, strides), block.accessor());
206 }
207}
208
209template <TensorView Block, std::size_t DenseBlockOrder>
210 requires(DenseBlockOrder == tensor_mdspec_t<Block>::rank() && (DenseBlockOrder == 0 || StridedTensorView<Block>))
211auto permute_block(Block block, std::array<std::size_t, DenseBlockOrder> const& permutation)
212{
213 auto mutable_mdspec = permute_block_mdspec(mdspec_of(block), permutation);
214 auto const_mdspec = permute_block_mdspec(mdspec_of(std::as_const(block)), permutation);
215 using mutable_mdspec_type = decltype(mutable_mdspec);
216 using const_mdspec_type = decltype(const_mdspec);
217 using storage_policy = tensor_storage_policy_t<Block>;
218 return MdspecTensorView<mutable_mdspec_type, const_mdspec_type, storage_policy>{std::move(mutable_mdspec),
219 std::move(const_mdspec)};
220}
221
222template <TensorView Block> auto make_read_only_block_view(Block const& block)
223{
224 auto const_mdspec = mdspec_of(block);
225 using mdspec_type = std::remove_cvref_t<decltype(const_mdspec)>;
226 using storage_policy = tensor_storage_policy_t<Block>;
227 return MdspecTensorView<mdspec_type, mdspec_type, storage_policy>{const_mdspec, const_mdspec};
228}
229
230// An identity map preserves the original mapping/accessor, including compressed
231// diagonal blocks which cannot be represented by a layout_stride descriptor.
232template <auto Permutation, TensorView Block> auto map_block(Block block)
233{
234 if constexpr (Permutation == make_identity_axis_permutation<std::tuple_size_v<decltype(Permutation)>>())
235 return block;
236 else
237 return permute_block(std::move(block), Permutation);
238}
239
240template <class SourceTensor, class = void> class BlockTensorAllocationContextBinding {
241 public:
242 explicit BlockTensorAllocationContextBinding(SourceTensor&) noexcept {}
243};
244
245template <class SourceTensor>
246class BlockTensorAllocationContextBinding<
247 SourceTensor, std::void_t<decltype(std::declval<SourceTensor const&>().allocation_context())>> {
248 public:
249 using context_type = std::remove_reference_t<decltype(std::declval<SourceTensor const&>().allocation_context())>;
250
251 explicit BlockTensorAllocationContextBinding(SourceTensor& source) noexcept
252 : context_(std::addressof(source.allocation_context()))
253 {}
254
255 [[nodiscard]] auto get() const noexcept -> context_type& { return *context_; }
256
257 private:
258 context_type* context_;
259};
260
269template <class SourceTensor, class DomainType, class CodomainType, auto KeyPermutation, auto DensePermutation>
270class BlockTensorMappedView {
271 public:
272 using source_type = SourceTensor;
273 using source_tensor_type = std::remove_cvref_t<SourceTensor>;
274 using borrowed_block_tensor_view_tag = void;
275 using element_type = typename source_tensor_type::element_type;
276 using value_type = typename source_tensor_type::value_type;
277 using storage_policy = typename source_tensor_type::storage_policy;
278 using storage_type = typename source_tensor_type::storage_type;
279 using backend_selector_type = typename source_tensor_type::backend_selector_type;
280 using key_type = typename source_tensor_type::key_type;
281 using domain_type = DomainType;
282 using codomain_type = CodomainType;
283 using source_mutable_access_block_type =
284 decltype(std::declval<SourceTensor&>().block_by_ordinal(std::size_t{}));
285 using mutable_block_type = decltype(map_block<DensePermutation>(std::declval<source_mutable_access_block_type>()));
286 using const_block_type = decltype(make_read_only_block_view(std::declval<mutable_block_type const&>()));
287 using allocation_context_binding_type = BlockTensorAllocationContextBinding<SourceTensor>;
288
289 static constexpr std::size_t static_order = source_tensor_type::order();
290 static constexpr std::size_t static_key_coordinate_count = source_tensor_type::key_coordinate_count();
291 static constexpr std::size_t static_dense_block_order = source_tensor_type::dense_block_order();
292
293 private:
294 static constexpr bool source_blocks_writable =
295 !std::is_const_v<typename std::remove_cvref_t<source_mutable_access_block_type>::element_type>;
296
297 static_assert(IsDomain<domain_type>::value);
298 static_assert(IsCodomain<codomain_type>::value);
299 static_assert(domain_type::size() + codomain_type::size() == static_order);
300 static_assert(BoundaryBlockShape<domain_type>::key_coordinate_count +
301 BoundaryBlockShape<codomain_type>::key_coordinate_count ==
302 static_key_coordinate_count);
303 static_assert(BoundaryBlockShape<domain_type>::dense_block_order +
304 BoundaryBlockShape<codomain_type>::dense_block_order ==
305 static_dense_block_order);
306 static_assert(std::tuple_size_v<decltype(KeyPermutation)> == static_key_coordinate_count);
307 static_assert(std::tuple_size_v<decltype(DensePermutation)> == static_dense_block_order);
308 static_assert(is_valid_axis_permutation(KeyPermutation));
309 static_assert(is_valid_axis_permutation(DensePermutation));
310
311 public:
316 explicit BlockTensorMappedView(SourceTensor& source, domain_type domain, codomain_type codomain)
317 : symmetry_(source.symmetry()), domain_(std::move(domain)), codomain_(std::move(codomain)),
318 allocation_context_(source)
319 {
320 this->build_key_index(source);
321 }
322
324 BlockTensorMappedView(BlockTensorMappedView const&) = default;
325
327 BlockTensorMappedView(BlockTensorMappedView&&) = default;
328
330 auto operator=(BlockTensorMappedView const&) -> BlockTensorMappedView& = delete;
331
333 auto operator=(BlockTensorMappedView&&) -> BlockTensorMappedView& = delete;
334
336 auto symmetry() const -> Symmetry { return symmetry_; }
337
339 auto domain() const -> domain_type const& { return domain_; }
340
342 auto codomain() const -> codomain_type const& { return codomain_; }
343
345 static constexpr auto order() noexcept -> std::size_t { return static_order; }
346
348 static constexpr auto key_coordinate_count() noexcept -> std::size_t { return static_key_coordinate_count; }
349
351 static constexpr auto dense_block_order() noexcept -> std::size_t { return static_dense_block_order; }
352
354 auto stored_block_count() const noexcept -> std::size_t { return keys_.size(); }
355
357 auto legal_block_count() const -> std::size_t
358 {
359 std::size_t count = 0;
360 this->for_each_possible_key([&](key_type const& key) {
361 if (this->is_legal(key)) ++count;
362 });
363 return count;
364 }
365
367 auto has_all_legal_blocks() const -> bool { return this->stored_block_count() == this->legal_block_count(); }
368
370 auto is_legal(key_type const& key) const -> bool
371 {
372 return is_legal_block_key(this->symmetry(), domain_, codomain_, key);
373 }
374
376 auto contains(key_type const& key) const -> bool { return this->ordinal(key).has_value(); }
377
379 auto find_block(key_type const& key) -> std::optional<mutable_block_type>
380 requires source_blocks_writable
381 {
382 auto const found = this->ordinal(key);
383 if (!found) return std::nullopt;
384 return blocks_[*found];
385 }
386
388 auto find_block(key_type const& key) const -> std::optional<const_block_type>
389 {
390 auto const found = this->ordinal(key);
391 if (!found) return std::nullopt;
392 return make_read_only_block_view(std::as_const(blocks_[*found]));
393 }
394
397 auto block(key_type const& key) -> mutable_block_type
398 requires source_blocks_writable
399 {
400 auto const found = this->find_block(key);
401 if (!found) throw std::out_of_range("mapped BlockTensor block key is not stored");
402 return *found;
403 }
404
407 auto block(key_type const& key) const -> const_block_type
408 {
409 auto const found = this->find_block(key);
410 if (!found) throw std::out_of_range("mapped BlockTensor block key is not stored");
411 return *found;
412 }
413
418 auto block_by_ordinal(std::size_t ordinal) -> mutable_block_type
419 requires source_blocks_writable
420 {
421 if (ordinal >= keys_.size()) throw std::out_of_range("mapped BlockTensor block ordinal is out of range");
422 return blocks_[ordinal];
423 }
424
429 auto block_by_ordinal(std::size_t ordinal) const -> const_block_type
430 {
431 if (ordinal >= keys_.size()) throw std::out_of_range("mapped BlockTensor block ordinal is out of range");
432 return make_read_only_block_view(std::as_const(blocks_[ordinal]));
433 }
434
436 auto stored_keys() const noexcept -> std::span<key_type const> { return keys_; }
437
439 static constexpr auto backend_selector() noexcept -> backend_selector_type
440 {
441 return source_tensor_type::backend_selector();
442 }
443
445 decltype(auto) allocation_context() const noexcept
446 requires requires(allocation_context_binding_type const& binding) { binding.get(); }
447 {
448 return allocation_context_.get();
449 }
450
451 private:
452 struct KeyBinding
453 {
454 key_type key;
455 std::size_t source_ordinal;
456 };
457
458 void build_key_index(SourceTensor& source)
459 {
460 std::vector<KeyBinding> bindings;
461 bindings.reserve(source.stored_block_count());
462 for (std::size_t ordinal = 0; ordinal < source.stored_block_count(); ++ordinal)
463 {
464 bindings.push_back({permute_key(source.stored_keys()[ordinal], KeyPermutation), ordinal});
465 }
466 std::ranges::sort(bindings, {}, &KeyBinding::key);
467
468 keys_.reserve(bindings.size());
469 blocks_.reserve(bindings.size());
470 for (auto const& binding : bindings)
471 {
472 keys_.push_back(binding.key);
473 auto source_block = source.block_by_ordinal(binding.source_ordinal);
474 blocks_.push_back(map_block<DensePermutation>(std::move(source_block)));
475 }
476 }
477
478 template <class Function> void for_each_possible_key(Function&& function) const
479 {
480 auto const sizes = boundary_factor_sizes<static_key_coordinate_count>(domain_, codomain_);
481 if (std::ranges::find(sizes, std::size_t{0}) != sizes.end()) return;
482
483 if constexpr (static_key_coordinate_count == 0)
484 {
485 function(key_type{});
486 }
487 else
488 {
489 std::array<std::size_t, static_key_coordinate_count> coordinates{};
490 while (true)
491 {
492 function(key_type{coordinates});
493 std::size_t axis = static_key_coordinate_count;
494 while (axis > 0)
495 {
496 --axis;
497 if (++coordinates[axis] < sizes[axis]) break;
498 coordinates[axis] = 0;
499 }
500 if (axis == 0 && coordinates[0] == 0) return;
501 }
502 }
503 }
504
505 auto ordinal(key_type const& key) const -> std::optional<std::size_t>
506 {
507 auto const found = std::ranges::lower_bound(keys_, key);
508 if (found == keys_.end() || *found != key) return std::nullopt;
509 return static_cast<std::size_t>(found - keys_.begin());
510 }
511
512 Symmetry symmetry_;
513 domain_type domain_;
514 codomain_type codomain_;
515 [[no_unique_address]] allocation_context_binding_type allocation_context_;
516 std::vector<key_type> keys_;
517 std::vector<mutable_block_type> blocks_;
518};
519
520} // namespace uni20::detail
Defines the first bosonic abelian BlockTensor container.
Domain
MKL computation domains accepted by domain-specific thread controls.
Definition backend_mkl.hpp:35
auto strides(S const &s)
Retrieve the strides associated with a strided mdspec-like type.
Definition mdspan/concepts.hpp:558
Mdspan-shaped metadata for storage whose data handle requires acquisition.
Definition qnum.hpp:403
typename optional_traits< T >::value_type value_type
Definition data_table.hpp:42
Codomain(Spaces &&...) -> Codomain< std::remove_cvref_t< Spaces >... >
Deduce a codomain's concrete value types.
constexpr decltype(auto) mdspec_of(Tensor &tensor) noexcept(noexcept(tensor.mdspec()))
Return a tensor's immediate or descriptor-backed multidimensional metadata.
Definition tensor/concepts.hpp:50
std::ptrdiff_t index_type
Definition types.hpp:19
Tensor(InputTensor const &) -> Tensor< tensor_element_t< InputTensor >, tensor_mdspec_t< InputTensor >::rank(), HostStorage, detail::materialized_layout_t< void, tensor_mdspec_t< InputTensor > >, DefaultAccessorFactory >
Deduce a runtime-extents host tensor that materializes a tensor view.
std::size_t span
Definition presentation.cpp:2017