TRIQS/nda 1.3.0
Multi-dimensional array library for C++
Loading...
Searching...
No Matches
rect_str.hpp
Go to the documentation of this file.
1// Copyright (c) 2020-2023 Simons Foundation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0.txt
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// Authors: Olivier Parcollet, Nils Wentzell
16
17/**
18 * @file
19 * @brief Provides an extension to nda::idx_map to support string indices.
20 */
21
22#pragma once
23
24#include "./idx_map.hpp"
25#include "./policies.hpp"
26#include "../basic_array.hpp"
27#include "../basic_functions.hpp"
28#include "../declarations.hpp"
29#include "../exceptions.hpp"
30#include "../traits.hpp"
31
32#include <array>
33#include <cstdint>
34#include <memory>
35#include <string>
36#include <type_traits>
37#include <utility>
38
39namespace nda {
40
41 /**
42 * @addtogroup layout_idx
43 * @{
44 */
45
46 /// @cond
47 // Forward declaration.
48 template <int Rank, uint64_t StaticExtents, uint64_t StrideOrder, layout_prop_e LayoutProp>
49 class rect_str;
50 /// @endcond
51
52 namespace detail {
53
54 // Get the corresponding nda::rect_str type from a given nda::idx_map type.
55 template <typename T>
56 struct rect_str_from_base;
57
58 // Specialization of rect_str_from_base for nda::idx_map.
59 template <int Rank, uint64_t StaticExtents, uint64_t StrideOrder, layout_prop_e LayoutProp>
60 struct rect_str_from_base<idx_map<Rank, StaticExtents, StrideOrder, LayoutProp>> {
61 // nda::rect_str type.
62 using type = rect_str<Rank, StaticExtents, StrideOrder, LayoutProp>;
63 };
64
65 } // namespace detail
66
67 /**
68 * @brief Layout that specifies how to map multi-dimensional indices including possible string indices to a
69 * linear/flat index.
70 *
71 * @details It extends the functionality of nda::idx_map by supporting string indices.
72 *
73 * @tparam Rank Number of dimensions.
74 * @tparam StaticExtent Compile-time known shape (zero if fully dynamic).
75 * @tparam StrideOrder Order in which the dimensions are stored in memory.
76 * @tparam LayoutProp Compile-time guarantees about the layout of the data in memory.
77 */
78 template <int Rank, uint64_t StaticExtents, uint64_t StrideOrder, layout_prop_e LayoutProp>
79 class rect_str : public idx_map<Rank, StaticExtents, StrideOrder, LayoutProp> {
80 // Type for storing the string indices.
81 using ind_t = nda::array<nda::array<std::string, 1>, 1>;
82
83 // Type of the nda::idx_map base class.
84 using base_t = idx_map<Rank, StaticExtents, StrideOrder, LayoutProp>;
85
86 // String indices for each dimension.
87 mutable std::shared_ptr<ind_t const> s_indices;
88
89 // Number of dynamic dimensions/extents.
90 using base_t::n_dynamic_extents;
91
92 // Get the shape of the nda::rect_str from given string indices.
93 static std::array<long, Rank> make_shape_from_string_indices(ind_t const &str_indices) {
94 if (str_indices.size() != Rank)
95 NDA_RUNTIME_ERROR << "Error in rect_str::make_shape_from_string_indices: String indices do not have the correct rank";
96 std::array<long, Rank> sha;
97 for (int i = 0; i < Rank; ++i) sha[i] = str_indices[i].size();
98 return sha;
99 }
100
101 public:
102 /**
103 * @brief Get the string indices.
104 *
105 * @details If the string indices are not yet initialized, they are initialized with the default values, i.e. "0",
106 * "1", "2", ..., and so on.
107 *
108 * @return String indices.
109 */
110 auto const &get_string_indices() const {
111 if (not s_indices) {
112 // string indices are not initialized
113 auto ind = ind_t(Rank);
114 for (int i = 0; i < Rank; ++i) {
115 auto a = nda::array<std::string, 1>(this->lengths()[i]);
116 for (int j = 0; j < a.size(); ++j) a(j) = std::to_string(j);
117 ind(i) = std::move(a);
118 }
119 s_indices = std::make_shared<ind_t>(std::move(ind));
120 }
121 return *s_indices;
122 }
123
124 /// Alias template to check if type `T` can be used to access a specific element.
125 template <typename T>
127
128 /// Alias template to check if type `T` can be used to either access a specific element or a slice of elements.
129 template <typename T>
132
133 /**
134 * @brief Default constructor.
135 * @details The string indices are not initialized and the underlying nda::idx_map is default constructed.
136 */
137 rect_str() = default;
138
139 /**
140 * @brief Construct an nda::rect_str from a given nda::idx_map.
141 * @param idxm nda::idx_map object.
142 */
143 rect_str(base_t const &idxm) noexcept : base_t{idxm} {}
144
145 /**
146 * @brief Construct an nda::rect_str from a given nda::idx_map and string indices.
147 *
148 * @warning The shape of the string indices is not checked to be consistent with the shape of the nda::idx_map.
149 *
150 * @param idxm nda::idx_map object.
151 * @param str_indices String indices.
152 */
153 rect_str(base_t const &idxm, ind_t const &str_indices) noexcept : base_t{idxm}, s_indices{std::make_shared<ind_t>(std::move(str_indices))} {}
154
155 /**
156 * @brief Construct an nda::rect_str from another nda::rect_str with different layout properties.
157 *
158 * @tparam LP Layout properties of the other nda::rect_str.
159 * @param rstr Other nda::rect_str object.
160 */
161 template <layout_prop_e LP>
162 rect_str(rect_str<Rank, StaticExtents, StrideOrder, LP> const &rstr) noexcept
163 : base_t{rstr}, s_indices{std::make_shared<ind_t>(rstr.get_string_indices())} {}
164
165 /**
166 * @brief Construct an nda::rect_str from another nda::rect_str with different layout properties and static extents.
167 *
168 * @tparam SE Static extents of the other nda::rect_str.
169 * @tparam LP Layout properties of the other nda::rect_str.
170 * @param rstr Other nda::rect_str object.
171 */
172 template <uint64_t SE, layout_prop_e LP>
173 rect_str(rect_str<Rank, SE, StrideOrder, LP> const &rstr) noexcept(false)
174 : base_t{rstr}, s_indices{std::make_shared<ind_t>(rstr.get_string_indices())} {}
175
176 /**
177 * @brief Construct an nda::rect_str from a given shape and strides.
178 *
179 * @param shape Shape of the map.
180 * @param strides Strides of the map.
181 */
182 rect_str(std::array<long, Rank> const &shape, std::array<long, Rank> const &strides) noexcept : base_t{shape, strides} {}
183
184 /**
185 * @brief Construct an nda::rect_str from a given shape and with contiguous strides.
186 * @param shape Shape of the map.
187 */
188 rect_str(std::array<long, Rank> const &shape) noexcept : base_t{shape} {}
189
190 /**
191 * @brief Construct an nda::rect_str from given string indices and with contiguous strides.
192 * @param str_indices String indices.
193 */
194 rect_str(nda::array<nda::array<std::string, 1>, 1> str_indices) noexcept(false)
195 : base_t{make_shape_from_string_indices(str_indices)}, s_indices{std::make_shared<ind_t>(std::move(str_indices))} {}
196
197 /**
198 * @brief Construct an nda::rect_str from an array with its dynamic extents.
199 * @details The missing extents are taken from the static extents.
200 * @param shape sta::array containing the dynamic extents only.
201 */
202 rect_str(std::array<long, base_t::n_dynamic_extents> const &shape) noexcept
203 requires((base_t::n_dynamic_extents != Rank) and (base_t::n_dynamic_extents != 0))
204 : base_t{shape} {}
205
206 /// Default copy constructor.
207 rect_str(rect_str const &) = default;
208
209 /// Default move constructor.
210 rect_str(rect_str &&) = default;
211
212 /// Default copy assignment operator.
213 rect_str &operator=(rect_str const &) = default;
214
215 /// Default move assignment operator.
216 rect_str &operator=(rect_str &&) = default;
217
218 private:
219 // Convert a given string argument into a corresponding index. If the argument isn't a string, it is returned as is.
220 template <typename T>
221 auto peel_string(int pos, T const &arg) const {
222 if constexpr (not std::is_constructible_v<std::string, T>)
223 // argument is not a string, simply return it
224 return arg;
225 else {
226 // argument is a string, find its position in the string indices of the given dimension
227 auto const &sind = get_string_indices();
228 auto const &idx = sind[pos];
229 auto it = std::find(idx.begin(), idx.end(), arg);
230 if (it == idx.end()) NDA_RUNTIME_ERROR << "Error in nda::rect_str: Key " << arg << " at position " << pos << " does not match an index";
231 return it - idx.begin();
232 }
233 }
234
235 // Actual implementation of the function call operator.
236 template <typename... Args, size_t... Is>
237 [[nodiscard]] FORCEINLINE long call_impl(std::index_sequence<Is...>, Args... args) const {
238 // calls the underlying nda::idx_map::operator() after converting all string arguments to long indices
239 return base_t::operator()(peel_string(Is, args)...);
240 }
241
242 public:
243 /**
244 * @brief Function call operator to map a given multi-dimensional index to a linear index.
245 *
246 * @details See also nda::idx_map.
247 *
248 * @tparam Args Types of the arguments.
249 * @param args Multi-dimensional index, including possible string indices.
250 * @return Linear/Flat index.
251 */
252 template <typename... Args>
253 FORCEINLINE long operator()(Args const &...args) const {
254 return call_impl(std::make_index_sequence<sizeof...(Args)>{}, args...);
255 }
256
257 private:
258 // Helper function to get a new nda::rect_str by taking a slice of the current one.
259 template <typename... Args, auto... Is>
260 FORCEINLINE decltype(auto) slice_impl(std::index_sequence<Is...>, Args const &...args) const {
261 // convert string arguments to long indices and slice the underlying nda::idx_map
262 auto const [offset, idxm2] = base_t::slice(peel_string(Is, args)...);
263
264 // type of sliced nda::rect_str
265 using new_rect_str_t = typename detail::rect_str_from_base<std::decay_t<decltype(idxm2)>>::type;
266
267 // if the string indices have not been initialized, simply return a new nda::rect_str with the sliced nda::idx_map
268 if (not s_indices) return std::make_pair(offset, new_rect_str_t{idxm2});
269
270 // otherwise slice the string indices as well (not optimized but simple)
271 auto const &current_ind = get_string_indices();
272 ind_t ind2((not argument_is_allowed_for_call<Args> + ...)); // will not work for ellipsis that cover more than one dimension
273 auto add_string_indices = [p = 0, &current_ind, &ind2](int n, auto const &y) mutable -> void {
274 using U = std::decay_t<decltype(y)>;
275 if constexpr (not argument_is_allowed_for_call<U>) { ind2[p++] = current_ind[n](y); }
276 };
277 (add_string_indices(Is, args), ...);
278
279 return std::make_pair(offset, new_rect_str_t{idxm2, ind2});
280 }
281
282 public:
283 /**
284 * @brief Get a new nda::rect_str by taking a slice of the current one.
285 *
286 * @warning nda::ellipsis that cover more than 1 dimension will not work properly. Use `nda::range::all_t` instead.
287 *
288 * @tparam Args Types of the arguments.
289 * @param args Multi-dimensional index consisting of strings, `long`, `nda::range`, `nda::range::all_t` or
290 * nda::ellipsis objects.
291 * @return A std::pair containing the offset in memory, i.e. the flat index of the first element of the slice and
292 * the new nda::rect_str.
293 */
294 template <typename... Args>
295 auto slice(Args const &...args) const {
296 return slice_impl(std::make_index_sequence<sizeof...(args)>{}, args...);
297 }
298
299 /**
300 * @brief Equal-to operator for two nda::rect_str objects.
301 *
302 * @param rhs Right hand side nda::rect_str operand.
303 * @return True if the underlying nda::idx_map and the string indices are equal, false otherwise.
304 */
305 bool operator==(rect_str const &rhs) const {
306 return base_t::operator==(rhs) and (!s_indices or !rhs.s_indices or (*s_indices == *(rhs.s_indices)));
307 }
308
309 /**
310 * @brief Not-equal-to operator for two nda::rect_str objects.
311 *
312 * @param rhs Right hand side nda::rect_str operand.
313 * @return True if they are not equal, false otherwise.
314 */
315 bool operator!=(rect_str const &rhs) { return !(operator==(rhs)); }
316
317 /**
318 * @brief Create a new nda::rect_str by permuting the indices/dimensions with a given permutation.
319 *
320 * @details Let `A` be the current and ``A'`` the new, permuted map. `P` is the given permutation. We define the
321 * permuted nda::rect_str ``A'`` to be the one with the following properties:
322 * - ``A'(i_0,...,i_{n-1}) = A(i_{P[0]},...,i_{P[n-1]})``
323 * - ``A'.lengths()[k] == A.lengths()[P^{-1}[k]]``
324 * - ``A'.strides()[k] == A.strides()[P^{-1}[k]]``
325 * - The stride order of ``A'`` is the composition of `P` and the stride order of `A` (note that the stride order
326 * itself is a permutation).
327 *
328 * @tparam Permutation Permutation to apply.
329 * @return New nda::rect_str with permuted indices.
330 */
331 template <uint64_t Permutation>
332 auto transpose() const {
333 // transpose the underlying nda::idx_map
334 auto idxm2 = base_t::template transpose<Permutation>();
335
336 // type of transposed nda::rect_str
337 using new_rect_str_t = typename detail::rect_str_from_base<std::decay_t<decltype(idxm2)>>::type;
338
339 // if the string indices have not been initialized, simply return the transposed nda::rect_str
340 if (not s_indices) return new_rect_str_t{idxm2};
341
342 // otherwise transpose the string indices as well
343 ind_t ind2(s_indices->size());
344 static constexpr std::array<int, Rank> permu = decode<Rank>(Permutation);
345 for (int u = 0; u < Rank; ++u) { ind2[permu[u]] = (*s_indices)[u]; }
346 return new_rect_str_t{idxm2, ind2};
347 }
348 };
349
350 /** @} */
351
352 /**
353 * @addtogroup layout_pols
354 * @{
355 */
356
357 /// @cond
358 // Forward declarations.
359 struct C_stride_layout_str;
360 struct F_stride_layout_str;
361 /// @endcond
362
363 /**
364 * @brief Contiguous layout policy with C-order (row-major order) and possible string indices.
365 * @details The last dimension varies the fastest, the first dimension varies the slowest.
366 */
368 /// Multi-dimensional to flat index mapping.
369 template <int Rank>
370 using mapping = rect_str<Rank, 0, C_stride_order<Rank>, layout_prop_e::contiguous>;
371
372 /// The same layout policy, but with no guarantee of contiguity.
373 using with_lowest_guarantee_t = C_stride_layout_str;
374
375 /// The same layout policy, but with guarantee of contiguity.
376 using contiguous_t = C_layout_str;
377 };
378
379 /**
380 * @brief Contiguous layout policy with Fortran-order (column-major order) and possible string indices.
381 * @details The first dimension varies the fastest, the last dimension varies the slowest.
382 */
384 /// Multi-dimensional to flat index mapping.
385 template <int Rank>
386 using mapping = rect_str<Rank, 0, Fortran_stride_order<Rank>, layout_prop_e::contiguous>;
387
388 /// The same layout policy, but with no guarantee of contiguity.
389 using with_lowest_guarantee_t = F_stride_layout_str;
390
391 /// The same layout policy, but with guarantee of contiguity.
392 using contiguous_t = F_layout_str;
393 };
394
395 /**
396 * @brief Strided (non-contiguous) layout policy with C-order (row-major order) and possible string indices.
397 * @details The last dimension varies the fastest, the first dimension varies the slowest.
398 */
400 /// Multi-dimensional to flat index mapping.
401 template <int Rank>
402 using mapping = rect_str<Rank, 0, C_stride_order<Rank>, layout_prop_e::none>;
403
404 /// The same layout policy, but with no guarantee of contiguity.
405 using with_lowest_guarantee_t = C_stride_layout_str;
406
407 /// The same layout policy, but with guarantee of contiguity.
408 using contiguous_t = C_layout_str;
409 };
410
411 /**
412 * @brief Strided (non-contiguous) layout policy with Fortran-order (column-major order) and possible string indices.
413 * @details The first dimension varies the fastest, the last dimension varies the slowest.
414 */
416 /// Multi-dimensional to flat index mapping.
417 template <int Rank>
418 using mapping = rect_str<Rank, 0, Fortran_stride_order<Rank>, layout_prop_e::none>;
419
420 /// The same layout policy, but with no guarantee of contiguity.
421 using with_lowest_guarantee_t = F_stride_layout_str;
422
423 /// The same layout policy, but with guarantee of contiguity.
424 using contiguous_t = F_layout_str;
425 };
426
427 /**
428 * @brief Generic layout policy with arbitrary order and possible string indices.
429 *
430 * @tparam StaticExtent Compile-time known shape (zero if dynamic).
431 * @tparam StrideOrder Order in which the dimensions are stored in memory.
432 * @tparam LayoutProp Compile-time guarantees about the layout of the data in memory.
433 */
434 template <uint64_t StaticExtents, uint64_t StrideOrder, layout_prop_e LayoutProp>
436 // FIXME C++20 : StrideOrder will be a std::array<int, Rank> WITH SAME rank
437 /// Multi-dimensional to flat index mapping.
438 template <int Rank>
439 using mapping = rect_str<Rank, StaticExtents, StrideOrder, LayoutProp>;
440
441 /// The same layout policy, but with no guarantee of contiguity.
442 using with_lowest_guarantee_t = basic_layout_str<StaticExtents, StrideOrder, layout_prop_e::none>;
443
444 /// The same layout policy, but with guarantee of contiguity.
445 using contiguous_t = basic_layout_str<StaticExtents, StrideOrder, layout_prop_e::contiguous>;
446 };
447
448 namespace detail {
449
450 // Get the correct layout policy given a general nda::rect_str.
451 template <int Rank, uint64_t StaticExtents, uint64_t StrideOrder, layout_prop_e LayoutProp>
452 struct layout_to_policy<rect_str<Rank, StaticExtents, StrideOrder, LayoutProp>> {
453 using type = basic_layout_str<StaticExtents, StrideOrder, LayoutProp>;
454 };
455
456 // Get the correct layout policy given a general nda::rect_str.
457 template <int Rank>
458 struct layout_to_policy<rect_str<Rank, 0, C_stride_order<Rank>, layout_prop_e::contiguous>> {
459 using type = C_layout_str;
460 };
461
462 // Get the correct layout policy given a strided nda::rect_str with C-order.
463 template <int Rank>
464 struct layout_to_policy<rect_str<Rank, 0, C_stride_order<Rank>, layout_prop_e::none>> {
465 using type = C_stride_layout_str;
466 };
467
468 } // namespace detail
469
470 /** @} */
471
472} // namespace nda
long size() const noexcept
Get the total size of the view/array.
basic_array & operator=(basic_array &&)=default
Default move assignment moves the memory handle and layout from the right hand side array.
Layout that specifies how to map multi-dimensional indices to a linear/flat index.
Definition idx_map.hpp:103
Layout that specifies how to map multi-dimensional indices including possible string indices to a lin...
Definition rect_str.hpp:79
__inline__ long operator()(Args const &...args) const
Function call operator to map a given multi-dimensional index to a linear index.
Definition rect_str.hpp:253
bool operator!=(rect_str const &rhs)
Not-equal-to operator for two nda::rect_str objects.
Definition rect_str.hpp:315
rect_str(std::array< long, Rank > const &shape) noexcept
Construct an nda::rect_str from a given shape and with contiguous strides.
Definition rect_str.hpp:188
rect_str(rect_str< Rank, StaticExtents, StrideOrder, LP > const &rstr) noexcept
Construct an nda::rect_str from another nda::rect_str with different layout properties.
Definition rect_str.hpp:162
rect_str & operator=(rect_str &&)=default
Default move assignment operator.
rect_str(nda::array< nda::array< std::string, 1 >, 1 > str_indices) noexcept(false)
Construct an nda::rect_str from given string indices and with contiguous strides.
Definition rect_str.hpp:194
rect_str(rect_str< Rank, SE, StrideOrder, LP > const &rstr) noexcept(false)
Construct an nda::rect_str from another nda::rect_str with different layout properties and static ext...
Definition rect_str.hpp:173
auto slice(Args const &...args) const
Get a new nda::rect_str by taking a slice of the current one.
Definition rect_str.hpp:295
rect_str & operator=(rect_str const &)=default
Default copy assignment operator.
static constexpr int argument_is_allowed_for_call_or_slice
Alias template to check if type T can be used to either access a specific element or a slice of eleme...
Definition rect_str.hpp:130
static constexpr int argument_is_allowed_for_call
Alias template to check if type T can be used to access a specific element.
Definition rect_str.hpp:126
rect_str(base_t const &idxm) noexcept
Construct an nda::rect_str from a given nda::idx_map.
Definition rect_str.hpp:143
bool operator==(rect_str const &rhs) const
Equal-to operator for two nda::rect_str objects.
Definition rect_str.hpp:305
rect_str(rect_str const &)=default
Default copy constructor.
rect_str(rect_str &&)=default
Default move constructor.
rect_str(std::array< long, Rank > const &shape, std::array< long, Rank > const &strides) noexcept
Construct an nda::rect_str from a given shape and strides.
Definition rect_str.hpp:182
rect_str()=default
Default constructor.
auto const & get_string_indices() const
Get the string indices.
Definition rect_str.hpp:110
auto transpose() const
Create a new nda::rect_str by permuting the indices/dimensions with a given permutation.
Definition rect_str.hpp:332
rect_str(base_t const &idxm, ind_t const &str_indices) noexcept
Construct an nda::rect_str from a given nda::idx_map and string indices.
Definition rect_str.hpp:153
#define NDA_RUNTIME_ERROR
layout_prop_e
Compile-time guarantees of the memory layout of an array/view.
Definition traits.hpp:222
#define FORCEINLINE
Definition macros.hpp:41
Contiguous layout policy with C-order (row-major order) and possible string indices.
Definition rect_str.hpp:367
Strided (non-contiguous) layout policy with C-order (row-major order) and possible string indices.
Definition rect_str.hpp:399
Contiguous layout policy with Fortran-order (column-major order) and possible string indices.
Definition rect_str.hpp:383
Strided (non-contiguous) layout policy with Fortran-order (column-major order) and possible string in...
Definition rect_str.hpp:415
Generic layout policy with arbitrary order and possible string indices.
Definition rect_str.hpp:435