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--present, The Simons Foundation
2// This file is part of TRIQS/nda and is licensed under the Apache License, Version 2.0.
3// SPDX-License-Identifier: Apache-2.0
4// See LICENSE in the root of this distribution for details.
5
10
11#pragma once
12
13#include "./accessors.hpp"
15#include "./basic_functions.hpp"
16#include "./concepts.hpp"
17#include "./iterators.hpp"
18#include "./layout/for_each.hpp"
20#include "./layout/range.hpp"
23#include "./macros.hpp"
26#include "./mem/fill.hpp"
27#include "./mem/memcpy.hpp"
28#include "./mem/policies.hpp"
29#include "./stdutil/array.hpp"
30#include "./traits.hpp"
31
32#include <algorithm>
33#include <array>
34#include <complex>
35#include <concepts>
36#include <exception>
37#include <initializer_list>
38#include <iostream>
39#include <random>
40#include <ranges>
41#include <type_traits>
42#include <utility>
43
44namespace nda {
45
98 template <typename ValueType, int Rank, typename LayoutPolicy, char Algebra, typename ContainerPolicy>
99 class basic_array {
100 // Compile-time checks.
101 static_assert(!std::is_const_v<ValueType>, "Error in nda::basic_array: ValueType cannot be const");
102 static_assert((Algebra != 'N'), "Internal error in nda::basic_array: Algebra 'N' not supported");
103 static_assert((Algebra != 'M') or (Rank == 2), "Internal error in nda::basic_array: Algebra 'M' requires a rank 2 array");
104 static_assert((Algebra != 'V') or (Rank == 1), "Internal error in nda::basic_array: Algebra 'V' requires a rank 1 array");
105
106 public:
108 using value_type = ValueType;
109
111 using layout_policy_t = LayoutPolicy;
112
114 using layout_t = typename LayoutPolicy::template mapping<Rank>;
115
117 using container_policy_t = ContainerPolicy;
118
120 using storage_t = typename ContainerPolicy::template handle<ValueType>;
121
123 using regular_type = basic_array;
124
126 static constexpr int rank = Rank;
127
128 // Compile-time check.
129 static_assert(has_contiguous(layout_t::layout_prop), "Error in nda::basic_array: Memory layout has to be contiguous");
130
131 private:
132 // Type of the array itself.
133 using self_t = basic_array;
134
135 // Type of the accessor policy for views (no no_alias_accessor).
136 using AccessorPolicy = default_accessor;
137
138 // Type of the owning policy for views.
139 using OwningPolicy = borrowed<storage_t::address_space>;
140
141 // Constexpr variable that is true if the value_type is const (never for basic_array).
142 static constexpr bool is_const = false;
143
144 // Constexpr variable that is true if the array is a view (never for basic_array).
145 static constexpr bool is_view = false;
146
147 // Memory layout of the array, i.e. the nda::idx_map.
148 layout_t lay;
149
150 // Memory handle of the array.
151 storage_t sto;
152
153 // Construct an array with a given shape and initialize the memory with zeros.
154 template <std::integral Int = long>
155 basic_array(std::array<Int, Rank> const &shape, mem::init_zero_t) : lay{shape}, sto{lay.size(), mem::init_zero} {}
156
157 public:
162 auto as_array_view() { return basic_array_view<ValueType, Rank, LayoutPolicy, 'A', AccessorPolicy, OwningPolicy>{*this}; };
163
168 auto as_array_view() const { return basic_array_view<const ValueType, Rank, LayoutPolicy, 'A', AccessorPolicy, OwningPolicy>{*this}; };
169
171 [[deprecated]] auto transpose()
172 requires(Rank == 2)
173 {
174 return permuted_indices_view<encode(std::array<int, 2>{1, 0})>(*this);
175 }
176
178 [[deprecated]] auto transpose() const
179 requires(Rank == 2)
180 {
181 return permuted_indices_view<encode(std::array<int, 2>{1, 0})>(*this);
182 }
183
185 basic_array() {}; // NOLINT (user-defined constructor to avoid value initialization of the sso buffer)
186
188 basic_array(basic_array &&) = default;
189
191 explicit basic_array(basic_array const &a) = default;
192
202 template <char A, typename CP>
203 explicit basic_array(basic_array<ValueType, Rank, LayoutPolicy, A, CP> a) noexcept : lay(a.indexmap()), sto(std::move(a.storage())) {}
204
214 template <std::integral... Ints>
215 requires(sizeof...(Ints) == Rank)
216 explicit basic_array(Ints... is) {
217 // setting the layout and storage in the constructor body improves error messages for wrong # of args
218 lay = layout_t{std::array{long(is)...}}; // NOLINT (for better error messages)
219 sto = storage_t{lay.size()}; // NOLINT (for better error messages)
220 }
221
230 template <std::integral Int, typename RHS>
231 explicit basic_array(Int sz, RHS const &val)
232 requires((Rank == 1 and is_scalar_for_v<RHS, basic_array>))
233 : lay(layout_t{std::array{long(sz)}}), sto{lay.size()} {
234 assign_from_scalar(val);
235 }
236
245 template <std::integral Int = long>
246 explicit basic_array(std::array<Int, Rank> const &shape)
247 requires(std::is_default_constructible_v<ValueType>)
248 : lay(shape), sto(lay.size()) {}
249
257 explicit basic_array(layout_t const &layout)
258 requires(std::is_default_constructible_v<ValueType>)
259 : lay{layout}, sto{lay.size()} {}
260
269 explicit basic_array(layout_t const &layout, storage_t &&storage) noexcept : lay{layout}, sto{std::move(storage)} {}
270
277 template <ArrayOfRank<Rank> A>
278 requires(HasValueTypeConstructibleFrom<A, ValueType>)
279 basic_array(A const &a) : lay(a.shape()), sto{lay.size(), mem::do_not_initialize} {
280 static_assert(std::is_constructible_v<ValueType, get_value_t<A>>, "Error in nda::basic_array: Incompatible value types in constructor");
281 if constexpr (std::is_trivial_v<ValueType> or is_complex_v<ValueType>) {
282 // trivial and complex value types can use the optimized assign_from_ndarray
283 if constexpr (std::is_same_v<ValueType, get_value_t<A>>)
284 assign_from_ndarray(a);
285 else
286 assign_from_ndarray(nda::map([](auto const &val) { return ValueType(val); })(a));
287 } else {
288 // general value types may not be default constructible -> use placement new
289 nda::for_each(lay.lengths(), [&](auto const &...is) { new (sto.data() + lay(is...)) ValueType{a(is...)}; });
290 }
291 }
292
302 template <ArrayInitializer<basic_array> Initializer> // can not be explicit
303 basic_array(Initializer const &initializer) : basic_array{initializer.shape()} {
304 initializer.invoke(*this);
305 }
306
307 private:
308 // Get the corresponding shape from an initializer list.
309 static std::array<long, 1> shape_from_init_list(std::initializer_list<ValueType> const &l) noexcept { return {long(l.size())}; }
310
311 // Get the corresponding shape from a nested initializer list.
312 template <typename L>
313 static auto shape_from_init_list(std::initializer_list<L> const &l) noexcept {
314 const auto [min, max] = std::minmax_element(std::begin(l), std::end(l), [](auto &&x, auto &&y) { return x.size() < y.size(); });
315 EXPECTS_WITH_MESSAGE(min->size() == max->size(),
316 "Error in nda::basic_array: Arrays can only be initialized with rectangular initializer lists");
317 return stdutil::front_append(shape_from_init_list(*max), long(l.size()));
318 }
319
320 public:
325 basic_array(std::initializer_list<ValueType> const &l)
326 requires(Rank == 1)
327 : lay(std::array<long, 1>{long(l.size())}), sto{lay.size(), mem::do_not_initialize} {
328 long i = 0;
329 for (auto const &x : l) { new (sto.data() + lay(i++)) ValueType{x}; }
330 }
331
336 basic_array(std::initializer_list<std::initializer_list<ValueType>> const &l2)
337 requires(Rank == 2)
338 : lay(shape_from_init_list(l2)), sto{lay.size(), mem::do_not_initialize} {
339 long i = 0, j = 0;
340 for (auto const &l1 : l2) {
341 for (auto const &x : l1) { new (sto.data() + lay(i, j++)) ValueType{x}; }
342 j = 0;
343 ++i;
344 }
345 }
346
351 basic_array(std::initializer_list<std::initializer_list<std::initializer_list<ValueType>>> const &l3)
352 requires(Rank == 3)
353 : lay(shape_from_init_list(l3)), sto{lay.size(), mem::do_not_initialize} {
354 long i = 0, j = 0, k = 0;
355 for (auto const &l2 : l3) {
356 for (auto const &l1 : l2) {
357 for (auto const &x : l1) { new (sto.data() + lay(i, j, k++)) ValueType{x}; }
358 k = 0;
359 ++j;
360 }
361 j = 0;
362 ++i;
363 }
364 }
365
375 template <char A2>
376 explicit basic_array(basic_array<ValueType, 2, LayoutPolicy, A2, ContainerPolicy> &&a) noexcept
377 requires(Rank == 2)
378 : basic_array{a.indexmap(), std::move(a).storage()} {}
379
387 template <std::integral Int = long>
388 static basic_array zeros(std::array<Int, Rank> const &shape)
389 requires(std::is_standard_layout_v<ValueType> && std::is_trivially_copyable_v<ValueType>)
390 {
392 }
393
401 template <std::integral... Ints>
402 static basic_array zeros(Ints... is)
403 requires(sizeof...(Ints) == Rank)
404 {
405 return zeros(std::array<long, Rank>{is...});
406 }
407
415 template <std::integral Int = long>
416 static basic_array ones(std::array<Int, Rank> const &shape)
418 {
419 auto res = basic_array{stdutil::make_std_array<long>(shape)};
420 res() = ValueType{1};
421 return res;
422 }
423
431 template <std::integral... Ints>
432 static basic_array ones(Ints... is)
433 requires(sizeof...(Ints) == Rank)
434 {
435 return ones(std::array<long, Rank>{is...});
436 }
437
448 template <std::integral Int = long>
449 static basic_array rand(std::array<Int, Rank> const &shape)
450 requires(std::is_floating_point_v<ValueType> or nda::is_complex_v<ValueType>)
451 {
452 auto static gen = std::mt19937_64{};
453 auto res = basic_array{shape};
454 if constexpr (nda::is_complex_v<ValueType>) {
455 auto static dist = std::uniform_real_distribution<typename ValueType::value_type>(0.0, 1.0);
456 using namespace std::complex_literals;
457 for (auto &x : res) x = dist(gen) + 1i * dist(gen);
458 } else {
459 auto static dist = std::uniform_real_distribution<ValueType>(0.0, 1.0);
460 for (auto &x : res) x = dist(gen);
461 }
462 return res;
463 }
464
475 template <std::integral... Ints>
476 static basic_array rand(Ints... is)
477 requires(sizeof...(Ints) == Rank)
478 {
479 return rand(std::array<long, Rank>{is...});
480 }
481
483 basic_array &operator=(basic_array &&) = default;
484
486 basic_array &operator=(basic_array const &) = default;
487
498 template <char A, typename CP>
499 basic_array &operator=(basic_array<ValueType, Rank, LayoutPolicy, A, CP> const &rhs) {
500 *this = basic_array{rhs};
501 return *this;
502 }
503
513 template <ArrayOfRank<Rank> RHS>
514 basic_array &operator=(RHS const &rhs) {
515 resize(rhs.shape());
516 assign_from_ndarray(rhs);
517 return *this;
518 }
519
530 template <typename RHS>
531 basic_array &operator=(RHS const &rhs) noexcept
533 {
534 assign_from_scalar(rhs);
535 return *this;
536 }
537
547 template <ArrayInitializer<basic_array> Initializer>
548 basic_array &operator=(Initializer const &initializer) {
549 resize(initializer.shape());
550 initializer.invoke(*this);
551 return *this;
552 }
553
564 template <std::integral... Ints>
565 void resize(Ints const &...is) {
566 static_assert(std::is_copy_constructible_v<ValueType>, "Error in nda::basic_array: Resizing requires the value_type to be copy constructible");
567 static_assert(sizeof...(is) == Rank, "Error in nda::basic_array: Resizing requires exactly Rank arguments");
568 resize(std::array<long, Rank>{long(is)...});
569 }
570
580 [[gnu::noinline]] void resize(std::array<long, Rank> const &shape) {
581 lay = layout_t(shape);
582 if (sto.is_null() or (sto.size() != lay.size())) sto = storage_t{lay.size()};
583 }
584
585// include common functionality of arrays and views
586// Copyright (c) 2019--present, The Simons Foundation
587// This file is part of TRIQS/nda and is licensed under the Apache License, Version 2.0.
588// SPDX-License-Identifier: Apache-2.0
589// See LICENSE in the root of this distribution for details.
590
595[[nodiscard]] constexpr auto const &indexmap() const noexcept { return lay; }
596
601[[nodiscard]] storage_t const &storage() const & noexcept { return sto; }
602
607[[nodiscard]] storage_t &storage() & noexcept { return sto; }
608
613[[nodiscard]] storage_t storage() && noexcept { return std::move(sto); }
614
621[[nodiscard]] constexpr auto stride_order() const noexcept { return lay.stride_order; }
622
627[[nodiscard]] ValueType const *data() const noexcept { return sto.data(); }
628
633[[nodiscard]] ValueType *data() noexcept { return sto.data(); }
634
639[[nodiscard]] auto const &shape() const noexcept { return lay.lengths(); }
640
645[[nodiscard]] auto const &strides() const noexcept { return lay.strides(); }
646
651[[nodiscard]] long size() const noexcept { return lay.size(); }
652
657[[nodiscard]] long is_contiguous() const noexcept { return lay.is_contiguous(); }
658
663[[nodiscard]] long has_positive_strides() const noexcept { return lay.has_positive_strides(); }
664
669[[nodiscard]] bool empty() const { return sto.is_null(); }
670
672[[nodiscard]] bool is_empty() const noexcept { return sto.is_null(); }
673
678[[nodiscard]] long extent(int i) const noexcept {
679#ifdef NDA_ENFORCE_BOUNDCHECK
680 if (i < 0 || i >= rank) {
681 std::cerr << "Error in extent: Dimension " << i << " is incompatible with array of rank " << rank << std::endl;
682 std::terminate();
683 }
684#endif
685 return lay.lengths()[i];
686}
687
689[[nodiscard]] long shape(int i) const noexcept { return extent(i); }
690
695[[nodiscard]] auto indices() const noexcept { return itertools::product_range(shape()); }
696
701static constexpr bool is_stride_order_C() noexcept { return layout_t::is_stride_order_C(); }
702
707static constexpr bool is_stride_order_Fortran() noexcept { return layout_t::is_stride_order_Fortran(); }
708
718decltype(auto) operator()(_linear_index_t idx) const noexcept {
719 if constexpr (layout_t::layout_prop == layout_prop_e::strided_1d)
720 return sto[idx.value * lay.min_stride()];
721 else if constexpr (layout_t::layout_prop == layout_prop_e::contiguous)
722 return sto[idx.value];
723 else
724 static_assert(always_false<layout_t>, "Internal error in array/view: Calling this type with a _linear_index_t is not allowed");
725}
726
728decltype(auto) operator()(_linear_index_t idx) noexcept {
729 if constexpr (layout_t::layout_prop == layout_prop_e::strided_1d)
730 return sto[idx.value * lay.min_stride()];
731 else if constexpr (layout_t::layout_prop == layout_prop_e::contiguous)
732 return sto[idx.value];
733 else
734 static_assert(always_false<layout_t>, "Internal error in array/view: Calling this type with a _linear_index_t is not allowed");
735}
736
737private:
738// Constexpr variable that is true if bounds checking is disabled.
739#ifdef NDA_ENFORCE_BOUNDCHECK
740static constexpr bool has_no_boundcheck = false;
741#else
742static constexpr bool has_no_boundcheck = true;
743#endif
744
745public:
762template <char ResultAlgebra, bool SelfIsRvalue, typename Self, typename... Ts>
763FORCEINLINE static decltype(auto) call(Self &&self, Ts const &...idxs) noexcept(has_no_boundcheck) {
764 // resulting value type
765 using r_v_t = std::conditional_t<std::is_const_v<std::remove_reference_t<Self>>, ValueType const, ValueType>;
766
767 // behavior depends on the given arguments
768 if constexpr (clef::is_any_lazy<Ts...>) {
769 // if there are lazy arguments, e.g. as in A(i_) << i_, a lazy expression is returned
770 return clef::make_expr_call(std::forward<Self>(self), idxs...);
771 } else if constexpr (sizeof...(Ts) == 0) {
772 // if no arguments are given, a full view is returned
774 } else {
775 // otherwise we check the arguments and either access a single element or make a slice
776 static_assert(((layout_t::template argument_is_allowed_for_call_or_slice<Ts> + ...) > 0),
777 "Error in array/view: Slice arguments must be convertible to range, ellipsis, or long (or string if the layout permits it)");
778
779 // number of arguments convertible to long
780 static constexpr int n_args_long = (layout_t::template argument_is_allowed_for_call<Ts> + ...);
781
782 if constexpr (n_args_long == rank) {
783 // access a single element
784 long offset = self.lay(idxs...);
785 if constexpr (is_view or not SelfIsRvalue) {
786 // if the calling object is a view or an lvalue, we return a reference
787 return AccessorPolicy::template accessor<r_v_t>::access(self.sto.data(), offset);
788 } else {
789 // otherwise, we return a copy of the value
790 return ValueType{self.sto[offset]};
791 }
792 } else {
793 // access a slice of the view/array
794 auto const [offset, idxm] = self.lay.slice(idxs...);
795 static constexpr auto res_rank = decltype(idxm)::rank();
796 // resulting algebra
797 static constexpr char newAlgebra = (ResultAlgebra == 'M' and (res_rank == 1) ? 'V' : ResultAlgebra);
798 // resulting layout policy
799 using r_layout_p = typename detail::layout_to_policy<std::decay_t<decltype(idxm)>>::type;
801 }
802 }
803}
804
805public:
827template <typename... Ts>
828FORCEINLINE decltype(auto) operator()(Ts const &...idxs) const & noexcept(has_no_boundcheck) {
829 static_assert((rank == -1) or (sizeof...(Ts) == rank) or (sizeof...(Ts) == 0) or (ellipsis_is_present<Ts...> and (sizeof...(Ts) <= rank + 1)),
830 "Error in array/view: Incorrect number of parameters in call operator");
831 return call<Algebra, false>(*this, idxs...);
832}
833
835template <typename... Ts>
836FORCEINLINE decltype(auto) operator()(Ts const &...idxs) & noexcept(has_no_boundcheck) {
837 static_assert((rank == -1) or (sizeof...(Ts) == rank) or (sizeof...(Ts) == 0) or (ellipsis_is_present<Ts...> and (sizeof...(Ts) <= rank + 1)),
838 "Error in array/view: Incorrect number of parameters in call operator");
839 return call<Algebra, false>(*this, idxs...);
840}
841
843template <typename... Ts>
844FORCEINLINE decltype(auto) operator()(Ts const &...idxs) && noexcept(has_no_boundcheck) {
845 static_assert((rank == -1) or (sizeof...(Ts) == rank) or (sizeof...(Ts) == 0) or (ellipsis_is_present<Ts...> and (sizeof...(Ts) <= rank + 1)),
846 "Error in array/view: Incorrect number of parameters in call operator");
847 return call<Algebra, true>(*this, idxs...);
848}
849
867template <typename T>
868decltype(auto) operator[](T const &idx) const & noexcept(has_no_boundcheck) {
869 static_assert((rank == 1), "Error in array/view: Subscript operator is only available for rank 1 views/arrays in C++17/20");
870 return call<Algebra, false>(*this, idx);
871}
872
874template <typename T>
875decltype(auto) operator[](T const &x) & noexcept(has_no_boundcheck) {
876 static_assert((rank == 1), "Error in array/view: Subscript operator is only available for rank 1 views/arrays in C++17/20");
877 return call<Algebra, false>(*this, x);
878}
879
881template <typename T>
882decltype(auto) operator[](T const &x) && 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, true>(*this, x);
885}
886
888static constexpr int iterator_rank = (has_strided_1d(layout_t::layout_prop) ? 1 : Rank);
889
892
895
896private:
897// Make an iterator for the view/array depending on its type.
898template <typename Iterator>
899[[nodiscard]] auto make_iterator(bool at_end) const noexcept {
900 if constexpr (iterator_rank == Rank) {
901 // multi-dimensional iterator
902 if constexpr (layout_t::is_stride_order_C()) {
903 // C-order case (array_iterator already traverses the data in C-order)
904 return Iterator{indexmap().lengths(), indexmap().strides(), sto.data(), at_end};
905 } else {
906 // general case (we need to permute the shape and the strides according to the stride order of the layout)
907 return Iterator{nda::permutations::apply(layout_t::stride_order, indexmap().lengths()),
908 nda::permutations::apply(layout_t::stride_order, indexmap().strides()), sto.data(), at_end};
909 }
910 } else {
911 // 1-dimensional iterator
912 return Iterator{std::array<long, 1>{size()}, std::array<long, 1>{indexmap().min_stride()}, sto.data(), at_end};
913 }
914}
915
916public:
918[[nodiscard]] const_iterator begin() const noexcept { return make_iterator<const_iterator>(false); }
919
921[[nodiscard]] const_iterator cbegin() const noexcept { return make_iterator<const_iterator>(false); }
922
924iterator begin() noexcept { return make_iterator<iterator>(false); }
925
927[[nodiscard]] const_iterator end() const noexcept { return make_iterator<const_iterator>(true); }
928
930[[nodiscard]] const_iterator cend() const noexcept { return make_iterator<const_iterator>(true); }
931
933iterator end() noexcept { return make_iterator<iterator>(true); }
934
947template <typename RHS>
948auto &operator+=(RHS const &rhs) noexcept {
949 static_assert(not is_const, "Error in array/view: Can not assign to a const view");
950 return operator=(*this + rhs);
951}
952
965template <typename RHS>
966auto &operator-=(RHS const &rhs) noexcept {
967 static_assert(not is_const, "Error in array/view: Can not assign to a const view");
968 return operator=(*this - rhs);
969}
970
983template <typename RHS>
984auto &operator*=(RHS const &rhs) noexcept {
985 static_assert(not is_const, "Error in array/view: Can not assign to a const view");
986 return operator=((*this) * rhs);
987}
988
1001template <typename RHS>
1002auto &operator/=(RHS const &rhs) noexcept {
1003 static_assert(not is_const, "Error in array/view: Can not assign to a const view");
1004 return operator=(*this / rhs);
1005}
1006
1015template <std::ranges::contiguous_range R>
1016auto &operator=(R const &rhs) noexcept
1017 requires(Rank == 1 and not MemoryArray<R> and not is_scalar_for_v<R, self_t>)
1018{
1020 return *this;
1021}
1022
1023private:
1024// Implementation of the assignment from an n-dimensional array type.
1025template <typename RHS>
1026void assign_from_ndarray(RHS const &rhs) {
1027#ifdef NDA_ENFORCE_BOUNDCHECK
1028 if (this->shape() != rhs.shape()) {
1029 NDA_RUNTIME_ERROR << "Error in assign_from_ndarray: Size mismatch:"
1030 << "\n LHS.shape() = " << this->shape() << "\n RHS.shape() = " << rhs.shape();
1031 }
1032#endif
1033 // compile-time check if assignment is possible
1034 static_assert(std::is_assignable_v<value_type &, get_value_t<RHS>>, "Error in assign_from_ndarray: Incompatible value types");
1035
1036 // are both operands nda::MemoryArray types?
1037 static constexpr bool both_in_memory = MemoryArray<self_t> and MemoryArray<RHS>;
1038
1039 // do both operands have the same stride order?
1040 static constexpr bool same_stride_order = get_layout_info<self_t>.stride_order == get_layout_info<RHS>.stride_order;
1041
1042 // compile-time check for device arrays to avoid runtime errors
1043 static_assert(!(mem::on_device<self_t> or mem::on_device<RHS>) or (both_in_memory and same_stride_order and have_same_value_type_v<self_t, RHS>),
1044 "Error in assign_from_ndarray: Assignment to/from device arrays is not supported for the given types.");
1045
1046 // prefer optimized options if possible
1047 if constexpr (both_in_memory and same_stride_order) {
1048 if (rhs.empty()) return;
1049 // are both operands strided in 1d?
1050 static constexpr bool both_1d_strided = has_layout_strided_1d<self_t> and has_layout_strided_1d<RHS>;
1051 if constexpr (mem::on_host<self_t, RHS> and both_1d_strided) {
1052 // vectorizable copy on host
1053 for (long i = 0; i < size(); ++i) (*this)(_linear_index_t{i}) = rhs(_linear_index_t{i});
1054 return;
1056 // check for block-layout and use mem::memcpy2D if possible
1057 auto bl_layout_dst = get_block_layout(*this);
1058 auto bl_layout_src = get_block_layout(rhs);
1059 if (bl_layout_dst && bl_layout_src) {
1060 auto [n_bl_dst, bl_size_dst, bl_str_dst] = *bl_layout_dst;
1061 auto [n_bl_src, bl_size_src, bl_str_src] = *bl_layout_src;
1062 // check that the total memory size is the same
1063 if (n_bl_dst * bl_size_dst != n_bl_src * bl_size_src) NDA_RUNTIME_ERROR << "Error in assign_from_ndarray: Incompatible block sizes";
1064 // if either destination or source consists of a single block, we can chunk it up to make the layouts compatible
1065 if (n_bl_dst == 1 && n_bl_src > 1) {
1066 n_bl_dst = n_bl_src;
1067 bl_size_dst /= n_bl_src;
1068 bl_str_dst = bl_size_dst;
1069 }
1070 if (n_bl_src == 1 && n_bl_dst > 1) {
1071 n_bl_src = n_bl_dst;
1072 bl_size_src /= n_bl_dst;
1073 bl_str_src = bl_size_src;
1074 }
1075 // copy only if block-layouts are compatible, otherwise continue to fallback
1076 if (n_bl_dst == n_bl_src && bl_size_dst == bl_size_src) {
1077 mem::memcpy2D<mem::get_addr_space<self_t>, mem::get_addr_space<RHS>>((void *)data(), bl_str_dst * sizeof(value_type), (void *)rhs.data(),
1078 bl_str_src * sizeof(value_type), bl_size_src * sizeof(value_type),
1079 n_bl_src);
1080 return;
1081 }
1082 }
1083 }
1084 }
1085 // otherwise fallback to elementwise assignment
1087 NDA_RUNTIME_ERROR << "Error in assign_from_ndarray: Fallback to elementwise assignment not implemented for arrays/views on the GPU";
1088 }
1089 nda::for_each(shape(), [this, &rhs](auto const &...args) { (*this)(args...) = rhs(args...); });
1090}
1091
1092// Implementation to fill a view/array with a constant scalar value.
1093template <typename Scalar>
1094void fill_with_scalar(Scalar const &scalar) {
1095 if constexpr (mem::on_host<self_t>) {
1096 if constexpr (has_layout_strided_1d<self_t>) {
1097 const long L = size();
1098 auto *__restrict const p = data(); // no alias possible here!
1099 if constexpr (has_contiguous_layout<self_t>) {
1100 for (long i = 0; i < L; ++i) p[i] = scalar;
1101 } else {
1102 const long stri = indexmap().min_stride();
1103 const long Lstri = L * stri;
1104 for (long i = 0; i != Lstri; i += stri) p[i] = scalar;
1105 }
1106 } else {
1107 for (auto &x : *this) x = scalar;
1108 }
1109 } else if constexpr (mem::on_device<self_t> or mem::on_unified<self_t>) {
1110 if constexpr (has_layout_strided_1d<self_t>) {
1111 if constexpr (has_contiguous_layout<self_t>) {
1112 mem::fill_n<mem::get_addr_space<self_t>>(data(), size(), value_type(scalar));
1113 } else {
1114 const long stri = indexmap().min_stride();
1115 mem::fill2D_n<mem::get_addr_space<self_t>>(data(), stri, 1, size(), value_type(scalar));
1116 }
1117 } else {
1118 auto bl_layout = get_block_layout(*this);
1119 if (bl_layout) {
1120 auto [n_bl, bl_size, bl_str] = *bl_layout;
1121 mem::fill2D_n<mem::get_addr_space<self_t>>(data(), bl_str, bl_size, n_bl, value_type(scalar));
1122 } else {
1123 // MAM: implement recursive call to fill_with_scalar on (i,nda::ellipsis{})
1124 NDA_RUNTIME_ERROR << "fill_with_scalar: Not implemented on device for generic (non-blocked) layout.";
1125 }
1126 }
1127 }
1128}
1129
1130// Implementation of the assignment from a scalar value.
1131template <typename Scalar>
1132void assign_from_scalar(Scalar const &scalar) noexcept {
1133 static_assert(!is_const, "Error in assign_from_ndarray: Cannot assign to a const view");
1134 if constexpr (Algebra != 'M') {
1135 // element-wise assignment for non-matrix algebras
1136 fill_with_scalar(scalar);
1137 } else {
1138 // a scalar has to be interpreted as a unit matrix for matrix algebras (the scalar in the shortest diagonal)
1139 // FIXME : A priori faster to put 0 everywhere and then change the diag to avoid the if.
1140 // FIXME : Benchmark and confirm.
1142 fill_with_scalar(0);
1143 else
1144 fill_with_scalar(Scalar{0 * scalar}); // FIXME : improve this
1145 diagonal(*this).fill_with_scalar(scalar);
1146 }
1147}
1148 };
1149
1150 // Class template argument deduction guides.
1151 template <MemoryArray A>
1152 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>,
1153 get_algebra<A>, heap<mem::get_addr_space<A>>>;
1154
1155 template <Array A>
1156 basic_array(A &&a) -> basic_array<get_value_t<A>, get_rank<A>, C_layout, get_algebra<A>, heap<mem::get_addr_space<A>>>;
1157
1158} // namespace nda
Defines accessors for nda::array objects (cf. std::default_accessor).
Provides definitions and type traits involving the different memory address spaces supported by nda.
Provides utility functions for std::array.
Provides the generic class for views.
Provides basic functions to create and manipulate arrays and views.
Iterator for nda::basic_array and nda::basic_array_view types.
A generic view of a multi-dimensional array.
auto & operator+=(RHS const &rhs) noexcept
Addition assignment operator.
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.
typename ContainerPolicy::template handle< ValueType > storage_t
Type of the memory handle (see Handles).
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.
basic_array(Initializer const &initializer)
Construct an array from an nda::ArrayInitializer object.
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.
typename LayoutPolicy::template mapping< Rank > layout_t
Type of the memory layout (an nda::idx_map).
basic_array(A const &a)
Construct an array from an nda::ArrayOfRank object with the same rank by copying each element.
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.
basic_array & operator=(Initializer const &initializer)
Assignment operator uses an nda::ArrayInitializer to assign to the array.
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.
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.
basic_array regular_type
The associated regular type.
bool empty() const
Is the view/array empty?
void resize(std::array< long, Rank > const &shape)
Resize the array to a new shape.
long has_positive_strides() const noexcept
Are all the strides of the memory layout of the view/array positive?
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.
LayoutPolicy layout_policy_t
Type of the memory layout policy (see Layout policies).
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 & operator=(R const &rhs) noexcept
Assignment operator makes a deep copy of a general contiguous range and assigns it to the 1-dimension...
array_iterator< iterator_rank, ValueType, typename AccessorPolicy::template accessor< ValueType >::pointer > iterator
Iterator type of the view/array.
auto as_array_view()
Convert the current array to a view with an 'A' (array) algebra.
auto transpose() const
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.
ContainerPolicy container_policy_t
Type of the container policy (see Memory policies).
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
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=(RHS const &rhs) noexcept
Assignment operator assigns a scalar to the 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.
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.
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 & 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.
array_iterator< iterator_rank, ValueType const, typename AccessorPolicy::template accessor< ValueType >::pointer > const_iterator
Const iterator type of the view/array.
iterator end() noexcept
Get an iterator to the end of the view/array.
ValueType value_type
Type of the values in the array (can not be const).
static basic_array rand(std::array< Int, Rank > const &shape)
Make a random-initialized array with the given shape.
Check if a given type satisfies the memory array concept.
Definition concepts.hpp:223
Check if a given type is either an arithmetic or complex type.
Definition concepts.hpp:83
Provides concepts for the nda library.
Provides for_each functions for multi-dimensional arrays/views.
auto permuted_indices_view(A &&a)
Permute the indices/dimensions of an nda::basic_array or nda::basic_array_view.
auto zeros(std::array< Int, Rank > const &shape)
Make an array of the given shape on the given address space and zero-initialize it.
ArrayOfRank< 1 > auto diagonal(M &&m)
Get a view of the diagonal of a 2-dimensional array/view.
auto ones(std::array< Int, Rank > const &shape)
Make an array of the given shape and one-initialize it.
auto rand(std::array< Int, Rank > const &shape)
Make an array of the given shape and initialize it with random values from the uniform distribution o...
mapped< F > map(F f)
Create a lazy function call expression on arrays/views.
Definition map.hpp:202
basic_array_view< ValueType const, Rank, Layout, 'A', default_accessor, borrowed<> > array_const_view
Same as nda::array_view except for const value types.
basic_array< ValueType, Rank, Layout, 'A', ContainerPolicy > array
Alias template of an nda::basic_array with an 'A' algebra.
constexpr bool have_same_value_type_v
Constexpr variable that is true if all types in As have the same value type as A0.
Definition traits.hpp:186
auto make_expr_call(F &&f, Args &&...args)
Create a function call expression from a callable object and a list of arguments.
Definition make_lazy.hpp:63
constexpr bool is_any_lazy
Constexpr variable that is true if any of the given types is lazy.
Definition utils.hpp:145
constexpr bool ellipsis_is_present
Constexpr variable that is true if the parameter pack Args contains an nda::ellipsis.
Definition range.hpp:56
constexpr bool has_contiguous(layout_prop_e lp)
Checks if a layout property has the contiguous property.
Definition traits.hpp:272
constexpr bool has_strided_1d(layout_prop_e lp)
Checks if a layout property has the strided_1d property.
Definition traits.hpp:256
__inline__ void for_each(std::array< Int, R > const &shape, F &&f)
Loop over all possible index values of a given shape and apply a function to them.
Definition for_each.hpp:116
constexpr bool has_layout_strided_1d
Constexpr variable that is true if type A has the strided_1d nda::layout_prop_e guarantee.
Definition traits.hpp:324
auto get_block_layout(A const &a)
Check if a given nda::MemoryArray has a block-strided layout.
constexpr layout_info_t get_layout_info
Constexpr variable that specifies the nda::layout_info_t of type A.
Definition traits.hpp:311
constexpr bool has_contiguous_layout
Constexpr variable that is true if type A has the contiguous nda::layout_prop_e guarantee.
Definition traits.hpp:320
static constexpr bool on_device
Constexpr variable that is true if all given types have a Device address space.
static constexpr bool on_unified
Constexpr variable that is true if all given types have a Unified address space.
static constexpr AddressSpace get_addr_space
Variable template providing the address space for different types.
static constexpr bool on_host
Constexpr variable that is true if all given types have a Host address space.
void memcpy2D(void *dest, size_t dpitch, const void *src, size_t spitch, size_t width, size_t height)
Call CUDA's cudaMemcpy2D function or simulate its behavior on the Host based on the given address spa...
Definition memcpy.hpp:73
static constexpr do_not_initialize_t do_not_initialize
Instance of nda::mem::do_not_initialize_t.
Definition handle.hpp:56
static constexpr init_zero_t init_zero
Instance of nda::mem::init_zero_t.
Definition handle.hpp:62
constexpr uint64_t encode(std::array< int, N > const &a)
Encode a std::array<int, N> in a uint64_t.
constexpr std::array< T, N > apply(std::array< Int, N > const &p, std::array< T, N > const &a)
Apply a permutation to a std::array.
constexpr std::array< T, R+1 > front_append(std::array< T, R > const &a, U const &x)
Make a new std::array by prepending one element at the front to an existing std::array.
Definition array.hpp:222
constexpr std::array< T, R > make_std_array(std::array< U, R > const &a)
Convert a std::array with value type U to a std::array with value type T.
Definition array.hpp:171
constexpr bool is_complex_v
Constexpr variable that is true if type T is a std::complex type.
Definition traits.hpp:65
static constexpr bool always_false
Constexpr variable that is always false regardless of the types in Ts (used to trigger static_assert)...
Definition traits.hpp:61
constexpr bool is_scalar_for_v
Constexpr variable used to check requirements when initializing an nda::basic_array or nda::basic_arr...
Definition traits.hpp:83
constexpr bool is_scalar_v
Constexpr variable that is true if type S is a scalar type, i.e. arithmetic or complex.
Definition traits.hpp:69
constexpr bool is_scalar_or_convertible_v
Constexpr variable that is true if type S is a scalar type (see nda::is_scalar_v) or if a std::comple...
Definition traits.hpp:76
Provides an iterator for nda::basic_array and nda::basic_array_view types.
Provides functions to transform the memory layout of an nda::basic_array or nda::basic_array_view.
Macros used in the nda library.
Provides functions to create and manipulate matrices, i.e. arrays/view with 'M' algebra.
Defines various memory handling policies.
Provides a generic memcpy and memcpy2D function for different address spaces.
Provides utilities to work with permutations and to compactly encode/decode std::array objects.
Includes the itertools header and provides some additional utilities.
Provides utilities that determine the resulting nda::idx_map when taking a slice of an nda::idx_map.
A small wrapper around a single long integer to be used as a linear index.
Definition traits.hpp:333
Memory policy using an nda::mem::handle_borrowed.
Definition policies.hpp:97
Default accessor for various array and view types.
Definition accessors.hpp:25
uint64_t stride_order
Stride order of the array/view.
Definition traits.hpp:287
Tag used in constructors to indicate that the memory should be initialized to zero.
Definition handle.hpp:59
Provides type traits for the nda library.