TRIQS/nda 1.3.0
Multi-dimensional array library for C++
Loading...
Searching...
No Matches
basic_array.hpp
Go to the documentation of this file.
1// Copyright (c) 2018 Commissariat à l'énergie atomique et aux énergies alternatives (CEA)
2// Copyright (c) 2018 Centre national de la recherche scientifique (CNRS)
3// Copyright (c) 2018-2023 Simons Foundation
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0.txt
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16//
17// Authors: Olivier Parcollet, Nils Wentzell
18
19/**
20 * @file
21 * @brief Provides the generic class for arrays.
22 */
23
24#pragma once
25
26#include "./accessors.hpp"
27#include "./basic_array_view.hpp"
28#include "./basic_functions.hpp"
29#include "./concepts.hpp"
30#include "./exceptions.hpp"
31#include "./iterators.hpp"
32#include "./layout/for_each.hpp"
33#include "./layout/permutation.hpp"
34#include "./layout/range.hpp"
35#include "./layout_transforms.hpp"
36#include "./macros.hpp"
37#include "./mem/address_space.hpp"
38#include "./mem/memcpy.hpp"
39#include "./mem/policies.hpp"
40#include "./stdutil/array.hpp"
41#include "./traits.hpp"
42
43#include <algorithm>
44#include <array>
45#include <complex>
46#include <concepts>
47#include <initializer_list>
48#include <random>
49#include <ranges>
50#include <type_traits>
51#include <utility>
52
53#ifdef NDA_ENFORCE_BOUNDCHECK
54#include <exception>
55#include <iostream>
56#endif // NDA_ENFORCE_BOUNDCHECK
57
58namespace nda {
59
60 /**
61 * @ingroup arrays_views
62 * @brief A generic multi-dimensional array.
63 *
64 * @details Together with nda::basic_array_view, this class forms the backbone of the **nda** library. It is templated
65 * with the following parameters:
66 *
67 * - `ValueType`: This is the type of the elements stored in the array. Most of the time, this will be a scalar type
68 * like an int, double or std::complex<double>, but it can also be a more complex type like a custom class or a
69 * another nda::basic_array.
70 * - `Rank`: Integer specifying the number of dimensions of the array. This is a compile-time constant.
71 * - `LayoutPolicy`: The layout policy specifies how the array views the memory it uses and how it accesses its
72 * elements. It provides a mapping from multi-dimensional to linear indices and vice versa (see @ref layout_pols).
73 * - `Algebra`: The algebra specifies how an array behaves when it is used in an expression. Possible values are 'A'
74 * (array), 'M' (matrix) and 'V' (vector) (see nda::get_algebra).
75 * - `ContainerPolicy`: The container policy specifies how and where the data is stored. It is responsible for
76 * allocating/deallocating the memory (see @ref mem_handles).
77 *
78 * In contrast to views (see nda::basic_array_view), regular arrays own the memory they use for data storage.
79 *
80 * @code{.cpp}
81 * // create a regular 3x2 array of ones
82 * auto arr = nda::ones<int>(3, 2);
83 * std::cout << arr << std::endl;
84 *
85 * // assign the value 42 to the first row
86 * arr(0, nda::ellipsis{}) = 42;
87 * std::cout << arr << std::endl;
88 * @endcode
89 *
90 * Output:
91 *
92 * @code{bash}
93 *
94 * [[1,1]
95 * [1,1]
96 * [1,1]]
97 *
98 * [[42,42]
99 * [1,1]
100 * [1,1]]
101 * @endcode
102 *
103 * Arrays and views share a lot of the same operations and functionalities. To turn a view into a regular array, use
104 * nda::make_regular.
105 *
106 * @tparam ValueType Type stored in the array.
107 * @tparam Rank Number of dimensions of the array.
108 * @tparam LayoutPolicy Policy determining the memory layout.
109 * @tparam Algebra Algebra of the array.
110 * @tparam ContainerPolicy Policy determining how and where the data is stored.
111 */
112 template <typename ValueType, int Rank, typename LayoutPolicy, char Algebra, typename ContainerPolicy>
114 // Compile-time checks.
115 static_assert(!std::is_const_v<ValueType>, "Error in nda::basic_array: ValueType cannot be const");
116 static_assert((Algebra != 'N'), "Internal error in nda::basic_array: Algebra 'N' not supported");
117 static_assert((Algebra != 'M') or (Rank == 2), "Internal error in nda::basic_array: Algebra 'M' requires a rank 2 array");
118 static_assert((Algebra != 'V') or (Rank == 1), "Internal error in nda::basic_array: Algebra 'V' requires a rank 1 array");
119
120 public:
121 /// Type of the values in the array (can not be const).
122 using value_type = ValueType;
123
124 /// Type of the memory layout policy (see @ref layout_pols).
125 using layout_policy_t = LayoutPolicy;
126
127 /// Type of the memory layout (an nda::idx_map).
128 using layout_t = typename LayoutPolicy::template mapping<Rank>;
129
130 /// Type of the container policy (see @ref mem_pols).
131 using container_policy_t = ContainerPolicy;
132
133 /// Type of the memory handle (see @ref mem_handles).
134 using storage_t = typename ContainerPolicy::template handle<ValueType>;
135
136 /// The associated regular type.
137 using regular_type = basic_array;
138
139 /// Number of dimensions of the array.
140 static constexpr int rank = Rank;
141
142 // Compile-time check.
143 static_assert(has_contiguous(layout_t::layout_prop), "Error in nda::basic_array: Memory layout has to be contiguous");
144
145 private:
146 // Type of the array itself.
147 using self_t = basic_array;
148
149 // Type of the accessor policy for views (no no_alias_accessor).
150 using AccessorPolicy = default_accessor;
151
152 // Type of the owning policy for views.
153 using OwningPolicy = borrowed<storage_t::address_space>;
154
155 // Constexpr variable that is true if the value_type is const (never for basic_array).
156 static constexpr bool is_const = false;
157
158 // Constexpr variable that is true if the array is a view (never for basic_array).
159 static constexpr bool is_view = false;
160
161 // Memory layout of the array, i.e. the nda::idx_map.
162 layout_t lay;
163
164 // Memory handle of the array.
165 storage_t sto;
166
167 // Construct an array with a given shape and initialize the memory with zeros.
168 template <std::integral Int = long>
169 basic_array(std::array<Int, Rank> const &shape, mem::init_zero_t) : lay{shape}, sto{lay.size(), mem::init_zero} {}
170
171 public:
172 /**
173 * @brief Convert the current array to a view with an 'A' (array) algebra.
174 * @return An nda::basic_array_view of the current array.
175 */
176 auto as_array_view() { return basic_array_view<ValueType, Rank, LayoutPolicy, 'A', AccessorPolicy, OwningPolicy>{*this}; };
177
178 /**
179 * @brief Convert the current array to a view with an 'A' (array) algebra.
180 * @return An nda::basic_array_view of the current array with const value type.
181 */
182 auto as_array_view() const { return basic_array_view<const ValueType, Rank, LayoutPolicy, 'A', AccessorPolicy, OwningPolicy>{*this}; };
183
184 /// @deprecated Create the transpose of a 2-dimensional array. Use nda::transpose instead.
185 [[deprecated]] auto transpose()
186 requires(Rank == 2)
187 {
188 return permuted_indices_view<encode(std::array<int, 2>{1, 0})>(*this);
189 }
190
191 /// @deprecated Create the transpose of a 2-dimensional array. Use nda::transpose instead.
192 [[deprecated]] auto transpose() const
193 requires(Rank == 2)
194 {
195 return permuted_indices_view<encode(std::array<int, 2>{1, 0})>(*this);
196 }
197
198 /// Default constructor constructs an empty array with a default constructed memory handle and layout.
199 basic_array() {}; // NOLINT (user-defined constructor to avoid value initialization of the sso buffer)
200
201 /// Default move constructor moves the memory handle and layout.
203
204 /// Default copy constructor copies the memory handle and layout.
205 explicit basic_array(basic_array const &a) = default;
206
207 /**
208 * @brief Construct an array from another array with a different algebra and/or container policy.
209 *
210 * @details It takes the memory layout of the other array and copies the data from the memory handle.
211 *
212 * @tparam A Algebra of the other array.
213 * @tparam CP Container policy of the other array.
214 * @param a Other array.
215 */
216 template <char A, typename CP>
217 explicit basic_array(basic_array<ValueType, Rank, LayoutPolicy, A, CP> a) noexcept : lay(a.indexmap()), sto(std::move(a.storage())) {}
218
219 /**
220 * @brief Construct an array with the given dimensions.
221 *
222 * @details The integer type must be convertible to long and there must be exactly `Rank` arguments. It depends on
223 * the value type and the container policy whether the data is initialized with zeros or not.
224 *
225 * @tparam Ints Integer types.
226 * @param is Extent (number of elements) along each dimension.
227 */
228 template <std::integral... Ints>
229 requires(sizeof...(Ints) == Rank)
230 explicit basic_array(Ints... is) {
231 // setting the layout and storage in the constructor body improves error messages for wrong # of args
232 lay = layout_t{std::array{long(is)...}}; // NOLINT (for better error messages)
233 sto = storage_t{lay.size()}; // NOLINT (for better error messages)
234 }
235
236 /**
237 * @brief Construct a 1-dimensional array with the given size and initialize each element to the given scalar value.
238 *
239 * @tparam Int Integer type.
240 * @tparam RHS Type of the scalar value to initialize the array with.
241 * @param sz Size of the array.
242 * @param val Value to initialize the array with.
243 */
244 template <std::integral Int, typename RHS>
245 explicit basic_array(Int sz, RHS const &val)
246 requires((Rank == 1 and is_scalar_for_v<RHS, basic_array>))
247 : lay(layout_t{std::array{long(sz)}}), sto{lay.size()} {
248 assign_from_scalar(val);
249 }
250
251 /**
252 * @brief Construct an array with the given shape.
253 *
254 * @details It depends on the value type and the container policy whether the data is initialized with zeros or not.
255 *
256 * @tparam Int Integer type.
257 * @param shape Shape of the array.
258 */
259 template <std::integral Int = long>
260 explicit basic_array(std::array<Int, Rank> const &shape)
261 requires(std::is_default_constructible_v<ValueType>)
262 : lay(shape), sto(lay.size()) {}
263
264 /**
265 * @brief Construct an array with the given memory layout.
266 *
267 * @details It depends on the value type and the container policy whether the data is initialized with zeros or not.
268 *
269 * @param layout Memory layout.
270 */
271 explicit basic_array(layout_t const &layout)
272 requires(std::is_default_constructible_v<ValueType>)
273 : lay{layout}, sto{lay.size()} {}
274
275 /**
276 * @brief Construct an array with the given memory layout and with an existing memory handle/storage.
277 *
278 * @details The memory handle is moved and the layout is copied into the array.
279 *
280 * @param layout Memory layout.
281 * @param storage Memory handle/storage.
282 */
283 explicit basic_array(layout_t const &layout, storage_t &&storage) noexcept : lay{layout}, sto{std::move(storage)} {}
284
285 /**
286 * @brief Construct an array from an nda::ArrayOfRank object with the same rank by copying each element.
287 *
288 * @tparam A nda::ArrayOfRank type.
289 * @param a nda::ArrayOfRank object.
290 */
291 template <ArrayOfRank<Rank> A>
292 requires(HasValueTypeConstructibleFrom<A, value_type>)
293 basic_array(A const &a) : lay(a.shape()), sto{lay.size(), mem::do_not_initialize} {
294 static_assert(std::is_constructible_v<value_type, get_value_t<A>>, "Error in nda::basic_array: Incompatible value types in constructor");
295 if constexpr (std::is_trivial_v<ValueType> or is_complex_v<ValueType>) {
296 // trivial and complex value types can use the optimized assign_from_ndarray
297 assign_from_ndarray(a);
298 } else {
299 // general value types may not be default constructible -> use placement new
300 nda::for_each(lay.lengths(), [&](auto const &...is) { new (sto.data() + lay(is...)) ValueType{a(is...)}; });
301 }
302 }
303
304 /**
305 * @brief Construct an array from an nda::ArrayInitializer object.
306 *
307 * @details An nda::ArrayInitializer is typically returned by delayed operations (see e.g. nda::mpi_gather).
308 * The constructor can then be used to create the resulting array.
309 *
310 * @tparam Initializer nda::ArrayInitializer type.
311 * @param initializer nda::ArrayInitializer object.
312 */
313 template <ArrayInitializer<basic_array> Initializer> // can not be explicit
314 basic_array(Initializer const &initializer) : basic_array{initializer.shape()} {
315 initializer.invoke(*this);
316 }
317
318 private:
319 // Get the corresponding shape from an initializer list.
320 static std::array<long, 1> shape_from_init_list(std::initializer_list<ValueType> const &l) noexcept { return {long(l.size())}; }
321
322 // Get the corresponding shape from a nested initializer list.
323 template <typename L>
324 static auto shape_from_init_list(std::initializer_list<L> const &l) noexcept {
325 const auto [min, max] = std::minmax_element(std::begin(l), std::end(l), [](auto &&x, auto &&y) { return x.size() < y.size(); });
326 EXPECTS_WITH_MESSAGE(min->size() == max->size(),
327 "Error in nda::basic_array: Arrays can only be initialized with rectangular initializer lists");
328 return stdutil::front_append(shape_from_init_list(*max), long(l.size()));
329 }
330
331 public:
332 /**
333 * @brief Construct a 1-dimensional array from an initializer list.
334 * @param l Initializer list.
335 */
336 basic_array(std::initializer_list<ValueType> const &l)
337 requires(Rank == 1)
338 : lay(std::array<long, 1>{long(l.size())}), sto{lay.size(), mem::do_not_initialize} {
339 long i = 0;
340 for (auto const &x : l) { new (sto.data() + lay(i++)) ValueType{x}; }
341 }
342
343 /**
344 * @brief Construct a 2-dimensional array from a double nested initializer list.
345 * @param l2 Initializer list.
346 */
347 basic_array(std::initializer_list<std::initializer_list<ValueType>> const &l2)
348 requires(Rank == 2)
349 : lay(shape_from_init_list(l2)), sto{lay.size(), mem::do_not_initialize} {
350 long i = 0, j = 0;
351 for (auto const &l1 : l2) {
352 for (auto const &x : l1) { new (sto.data() + lay(i, j++)) ValueType{x}; }
353 j = 0;
354 ++i;
355 }
356 }
357
358 /**
359 * @brief Construct a 3-dimensional array from a triple nested initializer list.
360 * @param l3 Initializer list.
361 */
362 basic_array(std::initializer_list<std::initializer_list<std::initializer_list<ValueType>>> const &l3)
363 requires(Rank == 3)
364 : lay(shape_from_init_list(l3)), sto{lay.size(), mem::do_not_initialize} {
365 long i = 0, j = 0, k = 0;
366 for (auto const &l2 : l3) {
367 for (auto const &l1 : l2) {
368 for (auto const &x : l1) { new (sto.data() + lay(i, j, k++)) ValueType{x}; }
369 k = 0;
370 ++j;
371 }
372 j = 0;
373 ++i;
374 }
375 }
376
377 /**
378 * @brief Construct a 2-dimensional array from another 2-dimensional array with a different algebra.
379 *
380 * @details The given array is moved into the constructed array. Note that for nda::stack_array or other array
381 * types, this might still involve a copy of the data.
382 *
383 * @tparam A2 Algebra of the given array.
384 * @param a Other array.
385 */
386 template <char A2>
387 explicit basic_array(basic_array<ValueType, 2, LayoutPolicy, A2, ContainerPolicy> &&a) noexcept
388 requires(Rank == 2)
389 : basic_array{a.indexmap(), std::move(a).storage()} {}
390
391 /**
392 * @brief Make a zero-initialized array with the given shape.
393 *
394 * @tparam Int Integer type.
395 * @param shape Shape of the array.
396 * @return Zero-initialized array.
397 */
398 template <std::integral Int = long>
399 static basic_array zeros(std::array<Int, Rank> const &shape)
400 requires(std::is_standard_layout_v<ValueType> && std::is_trivially_copyable_v<ValueType>)
401 {
402 return basic_array{stdutil::make_std_array<long>(shape), mem::init_zero};
403 }
404
405 /**
406 * @brief Make a zero-initialized array with the given dimensions.
407 *
408 * @tparam Ints Integer types.
409 * @param is Extent (number of elements) along each dimension.
410 * @return Zero-initialized array.
411 */
412 template <std::integral... Ints>
413 static basic_array zeros(Ints... is)
414 requires(sizeof...(Ints) == Rank)
415 {
416 return zeros(std::array<long, Rank>{is...});
417 }
418
419 /**
420 * @brief Make a one-initialized array with the given shape.
421 *
422 * @tparam Int Integer type.
423 * @param shape Shape of the array.
424 * @return One-initialized array.
425 */
426 template <std::integral Int = long>
427 static basic_array ones(std::array<Int, Rank> const &shape)
428 requires(nda::is_scalar_v<ValueType>)
429 {
430 auto res = basic_array{stdutil::make_std_array<long>(shape)};
431 res() = ValueType{1};
432 return res;
433 }
434
435 /**
436 * @brief Make a one-initialized array with the given dimensions.
437 *
438 * @tparam Ints Integer types.
439 * @param is Extent (number of elements) along each dimension.
440 * @return One-initialized array.
441 */
442 template <std::integral... Ints>
443 static basic_array ones(Ints... is)
444 requires(sizeof...(Ints) == Rank)
445 {
446 return ones(std::array<long, Rank>{is...});
447 }
448
449 /**
450 * @brief Make a random-initialized array with the given shape.
451 *
452 * @details The random values are take from a uniform distribution over [0, 1). For a complex array, both real and
453 * imaginary parts are initialized with random values.
454 *
455 * @tparam Int Integer type.
456 * @param shape Shape of the array.
457 * @return Random-initialized array.
458 */
459 template <std::integral Int = long>
460 static basic_array rand(std::array<Int, Rank> const &shape)
461 requires(std::is_floating_point_v<ValueType> or nda::is_complex_v<ValueType>)
462 {
463 using namespace std::complex_literals;
464 auto static gen = std::mt19937(std::random_device{}());
465 auto static dist = std::uniform_real_distribution<>(0.0, 1.0);
466 auto res = basic_array{shape};
467 if constexpr (nda::is_complex_v<ValueType>)
468 for (auto &x : res) x = dist(gen) + 1i * dist(gen);
469 else
470 for (auto &x : res) x = dist(gen);
471 return res;
472 }
473
474 /**
475 * @brief Make a random-initialized array with the given dimensions.
476 *
477 * @details The random values are take from a uniform distribution over [0, 1). For a complex array, both real and
478 * imaginary parts are initialized with random values.
479 *
480 * @tparam Ints Integer types.
481 * @param is Extent (number of elements) along each dimension.
482 * @return Random-initialized array.
483 */
484 template <std::integral... Ints>
485 static basic_array rand(Ints... is)
486 requires(sizeof...(Ints) == Rank)
487 {
488 return rand(std::array<long, Rank>{is...});
489 }
490
491 /// Default move assignment moves the memory handle and layout from the right hand side array.
493
494 /// Default copy assignment copies the memory handle and layout from the right hand side array.
495 basic_array &operator=(basic_array const &) = default;
496
497 /**
498 * @brief Assignment operator makes a deep copy of another array with a different algebra and/or container policy.
499 *
500 * @details A new array object is constructed from the right hand side array and then moved into the current array.
501 * This will invalidate all references/views to the existing storage.
502 *
503 * @tparam A Algebra of the other array.
504 * @tparam CP Container policy of the other array.
505 * @param rhs Right hand side of the assignment operation.
506 */
507 template <char A, typename CP>
508 basic_array &operator=(basic_array<ValueType, Rank, LayoutPolicy, A, CP> const &rhs) {
509 *this = basic_array{rhs};
510 return *this;
511 }
512
513 /**
514 * @brief Assignment operator makes a deep copy of an nda::ArrayOfRank object.
515 *
516 * @details The array is first resized to the shape of the right hand side and the elements are copied. This might
517 * invalidate all references/views to the existing storage.
518 *
519 * @tparam RHS nda::ArrayOfRank type.
520 * @param rhs Right hand side of the assignment operation.
521 */
522 template <ArrayOfRank<Rank> RHS>
523 basic_array &operator=(RHS const &rhs) {
524 resize(rhs.shape());
525 assign_from_ndarray(rhs);
526 return *this;
527 }
528
529 /**
530 * @brief Assignment operator assigns a scalar to the array.
531 *
532 * @details The behavior depends on the algebra of the array:
533 * - 'A' (array) and 'V' (vector): The scalar is assigned to all elements of the array.
534 * - 'M' (matrix): The scalar is assigned to the diagonal elements of the shorter dimension.
535 *
536 * @tparam RHS Type of the scalar.
537 * @param rhs Right hand side of the assignment operation.
538 */
539 template <typename RHS>
540 basic_array &operator=(RHS const &rhs) noexcept
541 requires(is_scalar_for_v<RHS, basic_array>)
542 {
543 assign_from_scalar(rhs);
544 return *this;
545 }
546
547 /**
548 * @brief Assignment operator uses an nda::ArrayInitializer to assign to the array.
549 *
550 * @details The array is resized to the shape of the initializer. This might invalidate all references/views to the
551 * existing storage.
552 *
553 * @tparam Initializer nda::ArrayInitializer type.
554 * @param initializer Initializer object.
555 */
556 template <ArrayInitializer<basic_array> Initializer>
557 basic_array &operator=(Initializer const &initializer) {
558 resize(initializer.shape());
559 initializer.invoke(*this);
560 return *this;
561 }
562
563 /**
564 * @brief Resize the array to a new shape.
565 *
566 * @details Resizing is only performed if the storage is not null and if the new size is different from the previous
567 * size. If resizing is performed, the content of the resulting array is undefined since it makes no copy of the
568 * previous data and all references/views to the existing storage will be invalidated.
569 *
570 * @tparam Ints Integer types.
571 * @param is New extent (number of elements) along each dimension.
572 */
573 template <std::integral... Ints>
574 void resize(Ints const &...is) {
575 static_assert(std::is_copy_constructible_v<ValueType>, "Error in nda::basic_array: Resizing requires the value_type to be copy constructible");
576 static_assert(sizeof...(is) == Rank, "Error in nda::basic_array: Resizing requires exactly Rank arguments");
577 resize(std::array<long, Rank>{long(is)...});
578 }
579
580 /**
581 * @brief Resize the array to a new shape.
582 *
583 * @details Resizing is only performed if the storage is not null and if the new size is different from the previous
584 * size. If resizing is performed, the content of the resulting array is undefined since it makes no copy of the
585 * previous data and all references/views to the existing storage will be invalidated.
586 *
587 * @param shape New shape of the array.
588 */
589 [[gnu::noinline]] void resize(std::array<long, Rank> const &shape) {
590 lay = layout_t(shape);
591 if (sto.is_null() or (sto.size() != lay.size())) sto = storage_t{lay.size()};
592 }
593
594// include common functionality of arrays and views
595// Copyright (c) 2019-2024 Simons Foundation
596//
597// Licensed under the Apache License, Version 2.0 (the "License");
598// you may not use this file except in compliance with the License.
599// You may obtain a copy of the License at
600//
601// http://www.apache.org/licenses/LICENSE-2.0.txt
602//
603// Unless required by applicable law or agreed to in writing, software
604// distributed under the License is distributed on an "AS IS" BASIS,
605// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
606// See the License for the specific language governing permissions and
607// limitations under the License.
608//
609// Authors: Thomas Hahn, Miguel Morales, Olivier Parcollet, Nils Wentzell
610
611/**
612 * @brief Get the memory layout of the view/array.
613 * @return nda::idx_map specifying the layout of the view/array.
614 */
615[[nodiscard]] constexpr auto const &indexmap() const noexcept { return lay; }
616
617/**
618 * @brief Get the data storage of the view/array.
619 * @return A const reference to the memory handle of the view/array.
620 */
621[[nodiscard]] storage_t const &storage() const & noexcept { return sto; }
622
623/**
624 * @brief Get the data storage of the view/array.
625 * @return A reference to the memory handle of the view/array.
626 */
627[[nodiscard]] storage_t &storage() & noexcept { return sto; }
628
629/**
630 * @brief Get the data storage of the view/array.
631 * @return A copy of the memory handle of the view/array.
632 */
633[[nodiscard]] storage_t storage() && noexcept { return std::move(sto); }
634
635/**
636 * @brief Get the stride order of the memory layout of the view/array (see nda::idx_map for more details on how we
637 * define stride orders).
638 *
639 * @return `std::array<int, Rank>` object specifying the stride order.
640 */
641[[nodiscard]] constexpr auto stride_order() const noexcept { return lay.stride_order; }
642
643/**
644 * @brief Get a pointer to the actual data (in general this is not the beginning of the memory block for a view).
645 * @return Const pointer to the first element of the view/array.
646 */
647[[nodiscard]] ValueType const *data() const noexcept { return sto.data(); }
648
649/**
650 * @brief Get a pointer to the actual data (in general this is not the beginning of thr memory block for a view).
651 * @return Pointer to the first element of the view/array.
652 */
653[[nodiscard]] ValueType *data() noexcept { return sto.data(); }
654
655/**
656 * @brief Get the shape of the view/array.
657 * @return `std::array<long, Rank>` object specifying the shape of the view/array.
658 */
659[[nodiscard]] auto const &shape() const noexcept { return lay.lengths(); }
660
661/**
662 * @brief Get the strides of the view/array (see nda::idx_map for more details on how we define strides).
663 * @return `std::array<long, Rank>` object specifying the strides of the view/array.
664 */
665[[nodiscard]] auto const &strides() const noexcept { return lay.strides(); }
666
667/**
668 * @brief Get the total size of the view/array.
669 * @return Number of elements contained in the view/array.
670 */
671[[nodiscard]] long size() const noexcept { return lay.size(); }
672
673/**
674 * @brief Is the memory layout of the view/array contiguous?
675 * @return True if the nda::idx_map is contiguous, false otherwise.
676 */
677[[nodiscard]] long is_contiguous() const noexcept { return lay.is_contiguous(); }
678
679/**
680 * @brief Is the view/array empty?
681 * @return True if the view/array does not contain any elements.
682 */
683[[nodiscard]] bool empty() const { return sto.is_null(); }
684
685/// @deprecated Use empty() instead.
686[[nodiscard]] bool is_empty() const noexcept { return sto.is_null(); }
687
688/**
689 * @brief Get the extent of the i<sup>th</sup> dimension.
690 * @return Number of elements along the i<sup>th</sup> dimension.
691 */
692[[nodiscard]] long extent(int i) const noexcept {
693#ifdef NDA_ENFORCE_BOUNDCHECK
694 if (i < 0 || i >= rank) {
695 std::cerr << "Error in extent: Dimension " << i << " is incompatible with array of rank " << rank << std::endl;
696 std::terminate();
697 }
698#endif
699 return lay.lengths()[i];
700}
701
702/// @deprecated Use `extent(i)` or `shape()[i]` instead.
703[[nodiscard]] long shape(int i) const noexcept { return extent(i); }
704
705/**
706 * @brief Get a range that generates all valid index tuples.
707 * @return An `itertools::multiplied` range that can be used to iterate over all valid index tuples.
708 */
709[[nodiscard]] auto indices() const noexcept { return itertools::product_range(shape()); }
710
711/**
712 * @brief Is the stride order of the view/array in C-order?
713 * @return True if the stride order of the nda::idx_map is C-order, false otherwise.
714 */
715static constexpr bool is_stride_order_C() noexcept { return layout_t::is_stride_order_C(); }
716
717/**
718 * @brief Is the stride order of the view/array in Fortran-order?
719 * @return True if the stride order of the nda::idx_map is Fortran-order, false otherwise.
720 */
721static constexpr bool is_stride_order_Fortran() noexcept { return layout_t::is_stride_order_Fortran(); }
722
723/**
724 * @brief Access the element of the view/array at the given nda::_linear_index_t.
725 *
726 * @details The linear index specifies the position of the element in the view/array and not the position of the
727 * element w.r.t. to the data pointer (i.e. any possible strides should not be taken into account).
728 *
729 * @param idx nda::_linear_index_t object.
730 * @return Const reference to the element at the given linear index.
731 */
732decltype(auto) operator()(_linear_index_t idx) const noexcept {
733 if constexpr (layout_t::layout_prop == layout_prop_e::strided_1d)
734 return sto[idx.value * lay.min_stride()];
735 else if constexpr (layout_t::layout_prop == layout_prop_e::contiguous)
736 return sto[idx.value];
737 else
738 static_assert(always_false<layout_t>, "Internal error in array/view: Calling this type with a _linear_index_t is not allowed");
739}
740
741/// Non-const overload of @ref nda::basic_array_view::operator()(_linear_index_t) const.
742decltype(auto) operator()(_linear_index_t idx) noexcept {
743 if constexpr (layout_t::layout_prop == layout_prop_e::strided_1d)
744 return sto[idx.value * lay.min_stride()];
745 else if constexpr (layout_t::layout_prop == layout_prop_e::contiguous)
746 return sto[idx.value];
747 else
748 static_assert(always_false<layout_t>, "Internal error in array/view: Calling this type with a _linear_index_t is not allowed");
749}
750
751private:
752// Constexpr variable that is true if bounds checking is disabled.
753#ifdef NDA_ENFORCE_BOUNDCHECK
754static constexpr bool has_no_boundcheck = false;
755#else
756static constexpr bool has_no_boundcheck = true;
757#endif
758
759public:
760/**
761 * @brief Implementation of the function call operator.
762 *
763 * @details This function is an implementation detail an should be private. Since the Green's function library in
764 * TRIQS uses this function, it is kept public (for now).
765 *
766 * @tparam ResultAlgebra Algebra of the resulting view/array.
767 * @tparam SelfIsRvalue True if the view/array is an rvalue.
768 * @tparam Self Type of the calling view/array.
769 * @tparam T Types of the arguments.
770 *
771 * @param self Calling view.
772 * @param idxs Multi-dimensional index consisting of `long`, `nda::range`, `nda::range::all_t`, nda::ellipsis or lazy
773 * arguments.
774 * @return Result of the function call depending on the given arguments and type of the view/array.
775 */
776template <char ResultAlgebra, bool SelfIsRvalue, typename Self, typename... Ts>
777FORCEINLINE static decltype(auto) call(Self &&self, Ts const &...idxs) noexcept(has_no_boundcheck) {
778 // resulting value type
779 using r_v_t = std::conditional_t<std::is_const_v<std::remove_reference_t<Self>>, ValueType const, ValueType>;
780
781 // behavior depends on the given arguments
782 if constexpr (clef::is_any_lazy<Ts...>) {
783 // if there are lazy arguments, e.g. as in A(i_) << i_, a lazy expression is returned
784 return clef::make_expr_call(std::forward<Self>(self), idxs...);
785 } else if constexpr (sizeof...(Ts) == 0) {
786 // if no arguments are given, a full view is returned
787 return basic_array_view<r_v_t, Rank, LayoutPolicy, Algebra, AccessorPolicy, OwningPolicy>{self.lay, self.sto};
788 } else {
789 // otherwise we check the arguments and either access a single element or make a slice
790 static_assert(((layout_t::template argument_is_allowed_for_call_or_slice<Ts> + ...) > 0),
791 "Error in array/view: Slice arguments must be convertible to range, ellipsis, or long (or string if the layout permits it)");
792
793 // number of arguments convertible to long
794 static constexpr int n_args_long = (layout_t::template argument_is_allowed_for_call<Ts> + ...);
795
796 if constexpr (n_args_long == rank) {
797 // access a single element
798 long offset = self.lay(idxs...);
799 if constexpr (is_view or not SelfIsRvalue) {
800 // if the calling object is a view or an lvalue, we return a reference
801 return AccessorPolicy::template accessor<ValueType>::access(self.sto.data(), offset);
802 } else {
803 // otherwise, we return a copy of the value
804 return ValueType{self.sto[offset]};
805 }
806 } else {
807 // access a slice of the view/array
808 auto const [offset, idxm] = self.lay.slice(idxs...);
809 static constexpr auto res_rank = decltype(idxm)::rank();
810 // resulting algebra
811 static constexpr char newAlgebra = (ResultAlgebra == 'M' and (res_rank == 1) ? 'V' : ResultAlgebra);
812 // resulting layout policy
813 using r_layout_p = typename detail::layout_to_policy<std::decay_t<decltype(idxm)>>::type;
814 return basic_array_view<ValueType, res_rank, r_layout_p, newAlgebra, AccessorPolicy, OwningPolicy>{std::move(idxm), {self.sto, offset}};
815 }
816 }
817}
818
819public:
820/**
821 * @brief Function call operator to access the view/array.
822 *
823 * @details Depending on the type of the calling object and the given arguments, this function call does the following:
824 * - If any of the arguments is lazy, an nda::clef::expr with the nda::clef::tags::function tag is returned.
825 * - If no arguments are given, a full view of the calling object is returned:
826 * - If the calling object itself or its value type is const, a view with a const value type is returned.
827 * - Otherwise, a view with a non-const value type is returned.
828 * - If the number of arguments is equal to the rank of the calling object and all arguments are convertible to `long`,
829 * a single element is accessed:
830 * - If the calling object is a view or an lvalue, a (const) reference to the element is returned.
831 * - Otherwise, a copy of the element is returned.
832 * - Otherwise a slice of the calling object is returned with the same value type and accessor and owning policies as
833 * the calling object. The algebra of the slice is the same as well, except if a 1-dimensional slice of a matrix is
834 * taken. In this case, the algebra is changed to 'V'.
835 *
836 * @tparam Ts Types of the function arguments.
837 * @param idxs Multi-dimensional index consisting of `long`, `nda::range`, `nda::range::all_t`, nda::ellipsis or lazy
838 * arguments.
839 * @return Result of the function call depending on the given arguments and type of the view/array.
840 */
841template <typename... Ts>
842FORCEINLINE decltype(auto) operator()(Ts const &...idxs) const & noexcept(has_no_boundcheck) {
843 static_assert((rank == -1) or (sizeof...(Ts) == rank) or (sizeof...(Ts) == 0) or (ellipsis_is_present<Ts...> and (sizeof...(Ts) <= rank + 1)),
844 "Error in array/view: Incorrect number of parameters in call operator");
845 return call<Algebra, false>(*this, idxs...);
846}
847
848/// Non-const overload of `nda::basic_array_view::operator()(Ts const &...) const &`.
849template <typename... Ts>
850FORCEINLINE decltype(auto) operator()(Ts const &...idxs) & noexcept(has_no_boundcheck) {
851 static_assert((rank == -1) or (sizeof...(Ts) == rank) or (sizeof...(Ts) == 0) or (ellipsis_is_present<Ts...> and (sizeof...(Ts) <= rank + 1)),
852 "Error in array/view: Incorrect number of parameters in call operator");
853 return call<Algebra, false>(*this, idxs...);
854}
855
856/// Rvalue overload of `nda::basic_array_view::operator()(Ts const &...) const &`.
857template <typename... Ts>
858FORCEINLINE decltype(auto) operator()(Ts const &...idxs) && noexcept(has_no_boundcheck) {
859 static_assert((rank == -1) or (sizeof...(Ts) == rank) or (sizeof...(Ts) == 0) or (ellipsis_is_present<Ts...> and (sizeof...(Ts) <= rank + 1)),
860 "Error in array/view: Incorrect number of parameters in call operator");
861 return call<Algebra, true>(*this, idxs...);
862}
863
864/**
865 * @brief Subscript operator to access the 1-dimensional view/array.
866 *
867 * @details Depending on the type of the calling object and the given argument, this subscript operation does the
868 * following:
869 * - If the argument is lazy, an nda::clef::expr with the nda::clef::tags::function tag is returned.
870 * - If the argument is convertible to `long`, a single element is accessed:
871 * - If the calling object is a view or an lvalue, a (const) reference to the element is returned.
872 * - Otherwise, a copy of the element is returned.
873 * - Otherwise a slice of the calling object is returned with the same value type, algebra and accessor and owning
874 * policies as the calling object.
875 *
876 * @tparam T Type of the argument.
877 * @param idx 1-dimensional index that is either a `long`, `nda::range`, `nda::range::all_t`, nda::ellipsis or a lazy
878 * argument.
879 * @return Result of the subscript operation depending on the given argument and type of the view/array.
880 */
881template <typename T>
882decltype(auto) operator[](T const &idx) const & noexcept(has_no_boundcheck) {
883 static_assert((rank == 1), "Error in array/view: Subscript operator is only available for rank 1 views/arrays in C++17/20");
884 return call<Algebra, false>(*this, idx);
885}
886
887/// Non-const overload of `nda::basic_array_view::operator[](T const &) const &`.
888template <typename T>
889decltype(auto) operator[](T const &x) & noexcept(has_no_boundcheck) {
890 static_assert((rank == 1), "Error in array/view: Subscript operator is only available for rank 1 views/arrays in C++17/20");
891 return call<Algebra, false>(*this, x);
892}
893
894/// Rvalue overload of `nda::basic_array_view::operator[](T const &) const &`.
895template <typename T>
896decltype(auto) operator[](T const &x) && noexcept(has_no_boundcheck) {
897 static_assert((rank == 1), "Error in array/view: Subscript operator is only available for rank 1 views/arrays in C++17/20");
898 return call<Algebra, true>(*this, x);
899}
900
901/// Rank of the nda::array_iterator for the view/array.
902static constexpr int iterator_rank = (has_strided_1d(layout_t::layout_prop) ? 1 : Rank);
903
904/// Const iterator type of the view/array.
905using const_iterator = array_iterator<iterator_rank, ValueType const, typename AccessorPolicy::template accessor<ValueType>::pointer>;
906
907/// Iterator type of the view/array.
908using iterator = array_iterator<iterator_rank, ValueType, typename AccessorPolicy::template accessor<ValueType>::pointer>;
909
910private:
911// Make an iterator for the view/array depending on its type.
912template <typename Iterator>
913[[nodiscard]] auto make_iterator(bool at_end) const noexcept {
914 if constexpr (iterator_rank == Rank) {
915 // multi-dimensional iterator
916 if constexpr (layout_t::is_stride_order_C()) {
917 // C-order case (array_iterator already traverses the data in C-order)
918 return Iterator{indexmap().lengths(), indexmap().strides(), sto.data(), at_end};
919 } else {
920 // general case (we need to permute the shape and the strides according to the stride order of the layout)
921 return Iterator{nda::permutations::apply(layout_t::stride_order, indexmap().lengths()),
922 nda::permutations::apply(layout_t::stride_order, indexmap().strides()), sto.data(), at_end};
923 }
924 } else {
925 // 1-dimensional iterator
926 return Iterator{std::array<long, 1>{size()}, std::array<long, 1>{indexmap().min_stride()}, sto.data(), at_end};
927 }
928}
929
930public:
931/// Get a const iterator to the beginning of the view/array.
932[[nodiscard]] const_iterator begin() const noexcept { return make_iterator<const_iterator>(false); }
933
934/// Get a const iterator to the beginning of the view/array.
935[[nodiscard]] const_iterator cbegin() const noexcept { return make_iterator<const_iterator>(false); }
936
937/// Get an iterator to the beginning of the view/array.
938iterator begin() noexcept { return make_iterator<iterator>(false); }
939
940/// Get a const iterator to the end of the view/array.
941[[nodiscard]] const_iterator end() const noexcept { return make_iterator<const_iterator>(true); }
942
943/// Get a const iterator to the end of the view/array.
944[[nodiscard]] const_iterator cend() const noexcept { return make_iterator<const_iterator>(true); }
945
946/// Get an iterator to the end of the view/array.
947iterator end() noexcept { return make_iterator<iterator>(true); }
948
949/**
950 * @brief Addition assignment operator.
951 *
952 * @details It first performs the (lazy) addition with the right hand side operand and then assigns the result to the
953 * left hand side view/array.
954 *
955 * See nda::operator+(L &&, R &&) and nda::operator+(A &&, S &&) for more details.
956 *
957 * @tparam RHS nda::Scalar or nda::Array type.
958 * @param rhs Right hand side operand of the addition assignment operation.
959 * @return Reference to this object.
960 */
961template <typename RHS>
962auto &operator+=(RHS const &rhs) noexcept {
963 static_assert(not is_const, "Error in array/view: Can not assign to a const view");
964 return operator=(*this + rhs);
965}
966
967/**
968 * @brief Subtraction assignment operator.
969 *
970 * @details It first performs the (lazy) subtraction with the right hand side operand and then assigns the result to
971 * the left hand side view/array.
972 *
973 * See nda::operator-(L &&, R &&) and nda::operator-(A &&, S &&) for more details.
974 *
975 * @tparam RHS nda::Scalar or nda::Array type.
976 * @param rhs Right hand side operand of the subtraction assignment operation.
977 * @return Reference to this object.
978 */
979template <typename RHS>
980auto &operator-=(RHS const &rhs) noexcept {
981 static_assert(not is_const, "Error in array/view: Can not assign to a const view");
982 return operator=(*this - rhs);
983}
984
985/**
986 * @brief Multiplication assignment operator.
987 *
988 * @details It first performs the (lazy) multiplication with the right hand side operand and then assigns the result
989 * to the left hand side view/array.
990 *
991 * See nda::operator*(L &&, R &&) and nda::operator*(A &&, S &&) for more details.
992 *
993 * @tparam RHS nda::Scalar or nda::Array type.
994 * @param rhs Right hand side operand of the multiplication assignment operation.
995 * @return Reference to this object.
996 */
997template <typename RHS>
998auto &operator*=(RHS const &rhs) noexcept {
999 static_assert(not is_const, "Error in array/view: Can not assign to a const view");
1000 return operator=((*this) * rhs);
1001}
1002
1003/**
1004 * @brief Division assignment operator.
1005 *
1006 * @details It first performs the (lazy) division with the right hand side operand and then assigns the result to the
1007 * left hand side view/array.
1008 *
1009 * See nda::operator/(L &&, R &&) and nda::operator/(A &&, S &&) for more details.
1010 *
1011 * @tparam RHS nda::Scalar or nda::Array type.
1012 * @param rhs Right hand side operand of the division assignment operation.
1013 * @return Reference to this object.
1014 */
1015template <typename RHS>
1016auto &operator/=(RHS const &rhs) noexcept {
1017 static_assert(not is_const, "Error in array/view: Can not assign to a const view");
1018 return operator=(*this / rhs);
1019}
1020
1021/**
1022 * @brief Assignment operator makes a deep copy of a general contiguous range and assigns it to the 1-dimensional
1023 * view/array.
1024 *
1025 * @tparam R Range type.
1026 * @param rhs Right hand side range object.
1027 * @return Reference to this object.
1028 */
1029template <std::ranges::contiguous_range R>
1030auto &operator=(R const &rhs) noexcept
1031 requires(Rank == 1 and not MemoryArray<R>)
1032{
1033 *this = basic_array_view{rhs};
1034 return *this;
1035}
1036
1037private:
1038// Implementation of the assignment from an n-dimensional array type.
1039template <typename RHS>
1040void assign_from_ndarray(RHS const &rhs) { // FIXME noexcept {
1041#ifdef NDA_ENFORCE_BOUNDCHECK
1042 if (this->shape() != rhs.shape())
1043 NDA_RUNTIME_ERROR << "Error in assign_from_ndarray: Size mismatch:"
1044 << "\n LHS.shape() = " << this->shape() << "\n RHS.shape() = " << rhs.shape();
1045#endif
1046 // compile-time check if assignment is possible
1047 static_assert(std::is_assignable_v<value_type &, get_value_t<RHS>>, "Error in assign_from_ndarray: Incompatible value types");
1048
1049 // are both operands nda::MemoryArray types?
1050 static constexpr bool both_in_memory = MemoryArray<self_t> and MemoryArray<RHS>;
1051
1052 // do both operands have the same stride order?
1053 static constexpr bool same_stride_order = get_layout_info<self_t>.stride_order == get_layout_info<RHS>.stride_order;
1054
1055 // prefer optimized options if possible
1056 if constexpr (both_in_memory and same_stride_order) {
1057 if (rhs.empty()) return;
1058 // are both operands strided in 1d?
1059 static constexpr bool both_1d_strided = has_layout_strided_1d<self_t> and has_layout_strided_1d<RHS>;
1060 if constexpr (mem::on_host<self_t, RHS> and both_1d_strided) {
1061 // vectorizable copy on host
1062 for (long i = 0; i < size(); ++i) (*this)(_linear_index_t{i}) = rhs(_linear_index_t{i});
1063 return;
1064 } else if constexpr (!mem::on_host<self_t, RHS> and have_same_value_type_v<self_t, RHS>) {
1065 // check for block-layout and use mem::memcpy2D if possible
1066 auto bl_layout_dst = get_block_layout(*this);
1067 auto bl_layout_src = get_block_layout(rhs);
1068 if (bl_layout_dst && bl_layout_src) {
1069 auto [n_bl_dst, bl_size_dst, bl_str_dst] = *bl_layout_dst;
1070 auto [n_bl_src, bl_size_src, bl_str_src] = *bl_layout_src;
1071 // check that the total memory size is the same
1072 if (n_bl_dst * bl_size_dst != n_bl_src * bl_size_src) NDA_RUNTIME_ERROR << "Error in assign_from_ndarray: Incompatible block sizes";
1073 // if either destination or source consists of a single block, we can chunk it up to make the layouts compatible
1074 if (n_bl_dst == 1 && n_bl_src > 1) {
1075 n_bl_dst = n_bl_src;
1076 bl_size_dst /= n_bl_src;
1077 bl_str_dst = bl_size_dst;
1078 }
1079 if (n_bl_src == 1 && n_bl_dst > 1) {
1080 n_bl_src = n_bl_dst;
1081 bl_size_src /= n_bl_dst;
1082 bl_str_src = bl_size_src;
1083 }
1084 // copy only if block-layouts are compatible, otherwise continue to fallback
1085 if (n_bl_dst == n_bl_src && bl_size_dst == bl_size_src) {
1086 mem::memcpy2D<mem::get_addr_space<self_t>, mem::get_addr_space<RHS>>((void *)data(), bl_str_dst * sizeof(value_type), (void *)rhs.data(),
1087 bl_str_src * sizeof(value_type), bl_size_src * sizeof(value_type),
1088 n_bl_src);
1089 return;
1090 }
1091 }
1092 }
1093 }
1094 // otherwise fallback to elementwise assignment
1095 if constexpr (mem::on_device<self_t> || mem::on_device<RHS>) {
1096 NDA_RUNTIME_ERROR << "Error in assign_from_ndarray: Fallback to elementwise assignment not implemented for arrays/views on the GPU";
1097 }
1098 nda::for_each(shape(), [this, &rhs](auto const &...args) { (*this)(args...) = rhs(args...); });
1099}
1100
1101// Implementation to fill a view/array with a constant scalar value.
1102template <typename Scalar>
1103void fill_with_scalar(Scalar const &scalar) noexcept {
1104 // we make a special implementation if the array is strided in 1d or contiguous
1105 if constexpr (has_layout_strided_1d<self_t>) {
1106 const long L = size();
1107 auto *__restrict const p = data(); // no alias possible here!
1108 if constexpr (has_contiguous_layout<self_t>) {
1109 for (long i = 0; i < L; ++i) p[i] = scalar;
1110 } else {
1111 const long stri = indexmap().min_stride();
1112 const long Lstri = L * stri;
1113 for (long i = 0; i < Lstri; i += stri) p[i] = scalar;
1114 }
1115 } else {
1116 // no compile-time memory layout guarantees
1117 for (auto &x : *this) x = scalar;
1118 }
1119}
1120
1121// Implementation of the assignment from a scalar value.
1122template <typename Scalar>
1123void assign_from_scalar(Scalar const &scalar) noexcept {
1124 static_assert(!is_const, "Error in assign_from_ndarray: Cannot assign to a const view");
1125 if constexpr (Algebra != 'M') {
1126 // element-wise assignment for non-matrix algebras
1127 fill_with_scalar(scalar);
1128 } else {
1129 // a scalar has to be interpreted as a unit matrix for matrix algebras (the scalar in the shortest diagonal)
1130 // FIXME : A priori faster to put 0 everywhere and then change the diag to avoid the if.
1131 // FIXME : Benchmark and confirm.
1132 if constexpr (is_scalar_or_convertible_v<Scalar>)
1133 fill_with_scalar(0);
1134 else
1135 fill_with_scalar(Scalar{0 * scalar}); // FIXME : improve this
1136 const long imax = std::min(extent(0), extent(1));
1137 for (long i = 0; i < imax; ++i) operator()(i, i) = scalar;
1138 }
1139}
1140 };
1141
1142 // Class template argument deduction guides.
1143 template <MemoryArray A>
1144 basic_array(A &&a) -> basic_array<get_value_t<A>, get_rank<A>, get_contiguous_layout_policy<get_rank<A>, get_layout_info<A>.stride_order>,
1145 get_algebra<A>, heap<mem::get_addr_space<A>>>;
1146
1147 template <Array A>
1148 basic_array(A &&a) -> basic_array<get_value_t<A>, get_rank<A>, C_layout, get_algebra<A>, heap<>>;
1149
1150} // namespace nda
Iterator for nda::basic_array and nda::basic_array_view types.
A generic view of a multi-dimensional array.
A generic multi-dimensional array.
auto & operator+=(RHS const &rhs) noexcept
Addition assignment operator.
decltype(auto) operator[](T const &idx) const &noexcept(has_no_boundcheck)
Subscript operator to access the 1-dimensional view/array.
basic_array(basic_array< ValueType, 2, LayoutPolicy, A2, ContainerPolicy > &&a) noexcept
Construct a 2-dimensional array from another 2-dimensional array with a different algebra.
basic_array & operator=(RHS const &rhs)
Assignment operator makes a deep copy of an nda::ArrayOfRank object.
static constexpr bool is_stride_order_Fortran() noexcept
Is the stride order of the view/array in Fortran-order?
ValueType const * data() const noexcept
Get a pointer to the actual data (in general this is not the beginning of the memory block for a view...
ValueType * data() noexcept
Get a pointer to the actual data (in general this is not the beginning of thr memory block for a view...
long shape(int i) const noexcept
const_iterator cbegin() const noexcept
Get a const iterator to the beginning of the view/array.
static constexpr int rank
Number of dimensions of the array.
static constexpr bool is_stride_order_C() noexcept
Is the stride order of the view/array in C-order?
storage_t & storage() &noexcept
Get the data storage of the view/array.
basic_array(basic_array< ValueType, Rank, LayoutPolicy, A, CP > a) noexcept
Construct an array from another array with a different algebra and/or container policy.
auto const & strides() const noexcept
Get the strides of the view/array (see nda::idx_map for more details on how we define strides).
basic_array(Ints... is)
Construct an array with the given dimensions.
static basic_array ones(Ints... is)
Make a one-initialized array with the given dimensions.
const_iterator begin() const noexcept
Get a const iterator to the beginning of the view/array.
auto as_array_view() const
Convert the current array to a view with an 'A' (array) algebra.
long extent(int i) const noexcept
Get the extent of the ith dimension.
basic_array(std::initializer_list< ValueType > const &l)
Construct a 1-dimensional array from an initializer list.
decltype(auto) operator[](T const &x) &&noexcept(has_no_boundcheck)
Rvalue overload of nda::basic_array_view::operator[](T const &) const &.
long is_contiguous() const noexcept
Is the memory layout of the view/array contiguous?
basic_array(std::initializer_list< std::initializer_list< ValueType > > const &l2)
Construct a 2-dimensional array from a double nested initializer list.
decltype(auto) operator()(_linear_index_t idx) noexcept
Non-const overload of nda::basic_array_view::operator()(_linear_index_t) const.
__inline__ decltype(auto) operator()(Ts const &...idxs) &noexcept(has_no_boundcheck)
Non-const overload of nda::basic_array_view::operator()(Ts const &...) const &.
bool empty() const
Is the view/array empty?
void resize(std::array< long, Rank > const &shape)
Resize the array to a new shape.
basic_array(Int sz, RHS const &val)
Construct a 1-dimensional array with the given size and initialize each element to the given scalar v...
static basic_array ones(std::array< Int, Rank > const &shape)
Make a one-initialized array with the given shape.
constexpr auto stride_order() const noexcept
Get the stride order of the memory layout of the view/array (see nda::idx_map for more details on how...
auto & operator/=(RHS const &rhs) noexcept
Division assignment operator.
auto as_array_view()
Convert the current array to a view with an 'A' (array) algebra.
basic_array(A const &a)
Construct an array from an nda::ArrayOfRank object with the same rank by copying each element.
static basic_array zeros(Ints... is)
Make a zero-initialized array with the given dimensions.
auto indices() const noexcept
Get a range that generates all valid index tuples.
static __inline__ decltype(auto) call(Self &&self, Ts const &...idxs) noexcept(has_no_boundcheck)
Implementation of the function call operator.
void resize(Ints const &...is)
Resize the array to a new shape.
storage_t storage() &&noexcept
Get the data storage of the view/array.
basic_array()
Default constructor constructs an empty array with a default constructed memory handle and layout.
bool is_empty() const noexcept
auto & operator=(R const &rhs) noexcept
Assignment operator makes a deep copy of a general contiguous range and assigns it to the 1-dimension...
storage_t const & storage() const &noexcept
Get the data storage of the view/array.
basic_array & operator=(basic_array const &)=default
Default copy assignment copies the memory handle and layout from the right hand side array.
basic_array & operator=(basic_array< ValueType, Rank, LayoutPolicy, A, CP > const &rhs)
Assignment operator makes a deep copy of another array with a different algebra and/or container poli...
basic_array(basic_array const &a)=default
Default copy constructor copies the memory handle and layout.
basic_array(std::initializer_list< std::initializer_list< std::initializer_list< ValueType > > > const &l3)
Construct a 3-dimensional array from a triple nested initializer list.
iterator begin() noexcept
Get an iterator to the beginning of the view/array.
basic_array(layout_t const &layout)
Construct an array with the given memory layout.
static basic_array rand(Ints... is)
Make a random-initialized array with the given dimensions.
basic_array(layout_t const &layout, storage_t &&storage) noexcept
Construct an array with the given memory layout and with an existing memory handle/storage.
static constexpr int iterator_rank
Rank of the nda::array_iterator for the view/array.
auto & operator-=(RHS const &rhs) noexcept
Subtraction assignment operator.
const_iterator end() const noexcept
Get a const iterator to the end of the view/array.
basic_array(basic_array &&)=default
Default move constructor moves the memory handle and layout.
__inline__ decltype(auto) operator()(Ts const &...idxs) &&noexcept(has_no_boundcheck)
Rvalue overload of nda::basic_array_view::operator()(Ts const &...) const &.
basic_array(std::array< Int, Rank > const &shape)
Construct an array with the given shape.
const_iterator cend() const noexcept
Get a const iterator to the end of the view/array.
auto const & shape() const noexcept
Get the shape of the view/array.
long size() const noexcept
Get the total size of the view/array.
decltype(auto) operator[](T const &x) &noexcept(has_no_boundcheck)
Non-const overload of nda::basic_array_view::operator[](T const &) const &.
auto & operator*=(RHS const &rhs) noexcept
Multiplication assignment operator.
basic_array & operator=(basic_array &&)=default
Default move assignment moves the memory handle and layout from the right hand side array.
static basic_array zeros(std::array< Int, Rank > const &shape)
Make a zero-initialized array with the given shape.
__inline__ decltype(auto) operator()(Ts const &...idxs) const &noexcept(has_no_boundcheck)
Function call operator to access the view/array.
constexpr auto const & indexmap() const noexcept
Get the memory layout of the view/array.
iterator end() noexcept
Get an iterator to the end of the view/array.
decltype(auto) operator()(_linear_index_t idx) const noexcept
Access the element of the view/array at the given nda::_linear_index_t.
static basic_array rand(std::array< Int, Rank > const &shape)
Make a random-initialized array with the given shape.
#define CUSOLVER_CHECK(X, info,...)
#define NDA_RUNTIME_ERROR
layout_prop_e
Compile-time guarantees of the memory layout of an array/view.
Definition traits.hpp:222
static constexpr do_not_initialize_t do_not_initialize
Instance of nda::mem::do_not_initialize_t.
Definition handle.hpp:69
static constexpr init_zero_t init_zero
Instance of nda::mem::init_zero_t.
Definition handle.hpp:75
constexpr uint64_t encode(std::array< int, N > const &a)
Encode a std::array<int, N> in a uint64_t.
#define FORCEINLINE
Definition macros.hpp:41
#define EXPECTS_WITH_MESSAGE(X,...)
Definition macros.hpp:75
#define AS_STRING(...)
Definition macros.hpp:31
Contiguous layout policy with C-order (row-major order).
Definition policies.hpp:47
A small wrapper around a single long integer to be used as a linear index.
Definition traits.hpp:343
long value
Linear index.
Definition traits.hpp:345
Memory policy using an nda::mem::handle_borrowed.
Definition policies.hpp:108
Accessor type of the nda::default_accessor.
Definition accessors.hpp:42
Default accessor for various array and view types.
Definition accessors.hpp:36
Tag used in constructors to indicate that the memory should be initialized to zero.
Definition handle.hpp:72