40#include <itertools/itertools.hpp>
50#ifdef NDA_ENFORCE_BOUNDCHECK
62 template <
typename V1,
int R1,
typename LP1,
char A1,
typename AP1,
typename OP1,
typename V2,
int R2,
typename LP2,
char A2,
typename AP2,
64 void swap(
nda::basic_array_view<V1, R1, LP1, A1, AP1, OP1> &a,
nda::basic_array_view<V2, R2, LP2, A2, AP2, OP2> &b) =
delete;
126 template <
typename ValueType,
int Rank,
typename LayoutPolicy,
char Algebra,
typename AccessorPolicy,
typename OwningPolicy>
129 static_assert((Algebra !=
'N'),
"Internal error in nda::basic_array_view: Algebra 'N' not supported");
130 static_assert((Algebra !=
'M') or (Rank == 2),
"Internal error in nda::basic_array_view: Algebra 'M' requires a rank 2 view");
131 static_assert((Algebra !=
'V') or (Rank == 1),
"Internal error in nda::basic_array_view: Algebra 'V' requires a rank 1 view");
141 using layout_t =
typename LayoutPolicy::template mapping<Rank>;
150 using storage_t =
typename OwningPolicy::template handle<ValueType>;
156 static constexpr int rank = Rank;
163 static constexpr bool is_view =
true;
166 static constexpr bool is_const = std::is_const_v<ValueType>;
175 template <
typename T,
int R,
typename L,
char A,
typename CP>
179 template <
typename T,
int R,
typename L,
char A,
typename AP,
typename OP>
183 template <
typename L>
184 static constexpr bool requires_runtime_check = not
layout_property_compatible(L::template mapping<Rank>::layout_prop, layout_t::layout_prop);
206 return basic_array_view<
const ValueType, Rank, LayoutPolicy,
'A', AccessorPolicy, OwningPolicy>{*
this};
227 template <MemoryArrayOfRank<Rank> A>
228 requires((get_layout_info<A>.stride_order == layout_t::stride_order_encoded)
229 and (std::is_same_v<std::remove_const_t<ValueType>,
get_value_t<A>>)
230 and (std::is_const_v<ValueType> or !std::is_const_v<typename std::decay_t<A>::
value_type>))
231 explicit(requires_runtime_check<typename std::decay_t<A>::
layout_policy_t>)
274 requires(Rank == 1 and std::is_const_v<ValueType>)
283 template <std::ranges::contiguous_range R>
284 requires(Rank == 1 and not MemoryArray<R>
285 and (std::is_same_v<std::ranges::range_value_t<R>, ValueType> or std::is_same_v<
const std::ranges::range_value_t<R>, ValueType>))
297 assign_from_ndarray(rhs);
310 template <ArrayOfRank<Rank> RHS>
312 static_assert(!is_const,
"Cannot assign to an nda::basic_array_view with const value_type");
313 assign_from_ndarray(rhs);
327 template <
typename RHS>
331 static_assert(!is_const,
"Cannot assign to an nda::basic_array_view with const value_type");
332 assign_from_scalar(rhs);
344 template <ArrayInitializer<basic_array_view> Initializer>
346 EXPECTS(
shape() == initializer.shape());
347 initializer.invoke(*
this);
365 template <
typename T,
int R,
typename LP,
char A,
typename AP,
typename OP>
367 static_assert(R == Rank,
"Cannot rebind a view to another view of a different rank");
368 static_assert(std::is_same_v<std::remove_const_t<T>, std::remove_const_t<ValueType>>,
369 "Cannot rebind a view to another view with a different value_type (except for const)");
370 static constexpr bool same_type = std::is_same_v<T, ValueType>;
371 static_assert(same_type or is_const,
"Cannot rebind a view with non-const value_type to another view with const value_type");
372 if constexpr (same_type) {
376 }
else if constexpr (is_const) {
393 std::swap(a.lay, b.lay);
394 std::swap(a.sto, b.sto);
432[[nodiscard]]
constexpr auto const &
indexmap() const noexcept {
return lay; }
458[[nodiscard]]
constexpr auto stride_order() const noexcept {
return lay.stride_order; }
464[[nodiscard]] ValueType
const *
data() const noexcept {
return sto.data(); }
470[[nodiscard]] ValueType *
data() noexcept {
return sto.data(); }
476[[nodiscard]]
auto const &
shape() const noexcept {
return lay.lengths(); }
482[[nodiscard]]
auto const &
strides() const noexcept {
return lay.strides(); }
488[[nodiscard]]
long size() const noexcept {
return lay.size(); }
494[[nodiscard]]
long is_contiguous() const noexcept {
return lay.is_contiguous(); }
506[[nodiscard]]
bool empty()
const {
return sto.is_null(); }
509[[nodiscard]]
bool is_empty() const noexcept {
return sto.is_null(); }
515[[nodiscard]]
long extent(
int i)
const noexcept {
516#ifdef NDA_ENFORCE_BOUNDCHECK
517 if (i < 0 || i >=
rank) {
518 std::cerr <<
"Error in extent: Dimension " << i <<
" is incompatible with array of rank " <<
rank << std::endl;
522 return lay.lengths()[i];
526[[nodiscard]]
long shape(
int i)
const noexcept {
return extent(i); }
532[[nodiscard]]
auto indices() const noexcept {
return itertools::product_range(
shape()); }
556 if constexpr (layout_t::layout_prop == layout_prop_e::strided_1d)
557 return sto[idx.value * lay.min_stride()];
558 else if constexpr (layout_t::layout_prop == layout_prop_e::contiguous)
559 return sto[idx.value];
561 static_assert(
always_false<layout_t>,
"Internal error in array/view: Calling this type with a _linear_index_t is not allowed");
566 if constexpr (layout_t::layout_prop == layout_prop_e::strided_1d)
567 return sto[idx.value * lay.min_stride()];
568 else if constexpr (layout_t::layout_prop == layout_prop_e::contiguous)
569 return sto[idx.value];
571 static_assert(
always_false<layout_t>,
"Internal error in array/view: Calling this type with a _linear_index_t is not allowed");
576#ifdef NDA_ENFORCE_BOUNDCHECK
577static constexpr bool has_no_boundcheck =
false;
579static constexpr bool has_no_boundcheck =
true;
599template <
char ResultAlgebra,
bool SelfIsRvalue,
typename Self,
typename... Ts>
600FORCEINLINE
static decltype(
auto)
call(Self &&self, Ts
const &...idxs)
noexcept(has_no_boundcheck) {
602 using r_v_t = std::conditional_t<std::is_const_v<std::remove_reference_t<Self>>, ValueType
const, ValueType>;
608 }
else if constexpr (
sizeof...(Ts) == 0) {
613 static_assert(((layout_t::template argument_is_allowed_for_call_or_slice<Ts> + ...) > 0),
614 "Error in array/view: Slice arguments must be convertible to range, ellipsis, or long (or string if the layout permits it)");
617 static constexpr int n_args_long = (layout_t::template argument_is_allowed_for_call<Ts> + ...);
619 if constexpr (n_args_long ==
rank) {
621 long offset = self.lay(idxs...);
622 if constexpr (is_view or not SelfIsRvalue) {
624 return AccessorPolicy::template accessor<r_v_t>::access(self.sto.data(), offset);
627 return ValueType{self.sto[offset]};
631 auto const [offset, idxm] = self.lay.slice(idxs...);
632 static constexpr auto res_rank =
decltype(idxm)
::rank();
634 static constexpr char newAlgebra = (ResultAlgebra ==
'M' and (res_rank == 1) ?
'V' : ResultAlgebra);
636 using r_layout_p =
typename detail::layout_to_policy<std::decay_t<
decltype(idxm)>>::type;
664template <
typename... Ts>
665FORCEINLINE
decltype(
auto)
operator()(Ts
const &...idxs)
const &
noexcept(has_no_boundcheck) {
667 "Error in array/view: Incorrect number of parameters in call operator");
672template <
typename... Ts>
673FORCEINLINE
decltype(
auto)
operator()(Ts
const &...idxs) &
noexcept(has_no_boundcheck) {
675 "Error in array/view: Incorrect number of parameters in call operator");
680template <
typename... Ts>
681FORCEINLINE
decltype(
auto)
operator()(Ts
const &...idxs) &&
noexcept(has_no_boundcheck) {
683 "Error in array/view: Incorrect number of parameters in call operator");
705decltype(
auto)
operator[](T
const &idx)
const &
noexcept(has_no_boundcheck) {
706 static_assert((
rank == 1),
"Error in array/view: Subscript operator is only available for rank 1 views/arrays in C++17/20");
712decltype(
auto)
operator[](T
const &x) &
noexcept(has_no_boundcheck) {
713 static_assert((
rank == 1),
"Error in array/view: Subscript operator is only available for rank 1 views/arrays in C++17/20");
719decltype(
auto)
operator[](T
const &x) &&
noexcept(has_no_boundcheck) {
720 static_assert((
rank == 1),
"Error in array/view: Subscript operator is only available for rank 1 views/arrays in C++17/20");
735template <
typename Iterator>
736[[nodiscard]]
auto make_iterator(
bool at_end)
const noexcept {
739 if constexpr (layout_t::is_stride_order_C()) {
741 return Iterator{
indexmap().lengths(),
indexmap().strides(), sto.data(), at_end};
749 return Iterator{std::array<long, 1>{
size()}, std::array<long, 1>{
indexmap().min_stride()}, sto.data(), at_end};
764[[nodiscard]]
const_iterator end() const noexcept {
return make_iterator<const_iterator>(
true); }
784template <
typename RHS>
786 static_assert(not is_const,
"Error in array/view: Can not assign to a const view");
802template <
typename RHS>
804 static_assert(not is_const,
"Error in array/view: Can not assign to a const view");
820template <
typename RHS>
822 static_assert(not is_const,
"Error in array/view: Can not assign to a const view");
838template <
typename RHS>
840 static_assert(not is_const,
"Error in array/view: Can not assign to a const view");
852template <std::ranges::contiguous_range R>
862template <
typename RHS>
863void assign_from_ndarray(RHS
const &rhs) {
864#ifdef NDA_ENFORCE_BOUNDCHECK
865 if (this->
shape() != rhs.shape())
866 NDA_RUNTIME_ERROR <<
"Error in assign_from_ndarray: Size mismatch:"
867 <<
"\n LHS.shape() = " << this->
shape() <<
"\n RHS.shape() = " << rhs.shape();
870 static_assert(std::is_assignable_v<value_type &, get_value_t<RHS>>,
"Error in assign_from_ndarray: Incompatible value types");
879 if constexpr (both_in_memory and same_stride_order) {
880 if (rhs.empty())
return;
891 if (bl_layout_dst && bl_layout_src) {
892 auto [n_bl_dst, bl_size_dst, bl_str_dst] = *bl_layout_dst;
893 auto [n_bl_src, bl_size_src, bl_str_src] = *bl_layout_src;
895 if (n_bl_dst * bl_size_dst != n_bl_src * bl_size_src) NDA_RUNTIME_ERROR <<
"Error in assign_from_ndarray: Incompatible block sizes";
897 if (n_bl_dst == 1 && n_bl_src > 1) {
899 bl_size_dst /= n_bl_src;
900 bl_str_dst = bl_size_dst;
902 if (n_bl_src == 1 && n_bl_dst > 1) {
904 bl_size_src /= n_bl_dst;
905 bl_str_src = bl_size_src;
908 if (n_bl_dst == n_bl_src && bl_size_dst == bl_size_src) {
919 NDA_RUNTIME_ERROR <<
"Error in assign_from_ndarray: Fallback to elementwise assignment not implemented for arrays/views on the GPU";
921 nda::for_each(
shape(), [
this, &rhs](
auto const &...args) { (*this)(args...) = rhs(args...); });
925template <
typename Scalar>
926void fill_with_scalar(Scalar
const &scalar)
noexcept {
929 const long L =
size();
930 auto *__restrict
const p =
data();
932 for (
long i = 0; i < L; ++i) p[i] = scalar;
934 const long stri =
indexmap().min_stride();
935 const long Lstri = L * stri;
936 for (
long i = 0; i != Lstri; i += stri) p[i] = scalar;
940 for (
auto &x : *
this) x = scalar;
945template <
typename Scalar>
946void assign_from_scalar(Scalar
const &scalar)
noexcept {
947 static_assert(!is_const,
"Error in assign_from_ndarray: Cannot assign to a const view");
948 if constexpr (Algebra !=
'M') {
950 fill_with_scalar(scalar);
958 fill_with_scalar(Scalar{0 * scalar});
960 for (
long i = 0; i < imax; ++i)
operator()(i, i) = scalar;
966 template <MemoryArray A>
967 basic_array_view(A &&a)
968 -> basic_array_view<std::conditional_t<std::is_const_v<std::remove_reference_t<A>>,
const typename std::decay_t<A>::value_type,
969 typename std::decay_t<A>::value_type>,
970 get_rank<A>,
typename std::decay_t<A>::layout_policy_t, get_algebra<A>, default_accessor, borrowed<mem::get_addr_space<A>>>;
972 template <
typename V,
size_t R>
973 basic_array_view(std::array<V, R> &a)
975 default_accessor, borrowed<>>;
977 template <
typename V,
size_t R>
978 basic_array_view(std::array<V, R>
const &a)
980 default_accessor, borrowed<>>;
982 template <std::ranges::contiguous_range R>
983 basic_array_view(R &r) -> basic_array_view<std::conditional_t<std::is_const_v<R>,
const typename R::value_type,
typename R::value_type>, 1,
984 C_layout,
'V', default_accessor, borrowed<>>;
Provides definitions and type traits involving the different memory address spaces supported by nda.
void swap(nda::basic_array_view< V1, R1, LP1, A1, AP1, OP1 > &a, nda::basic_array_view< V2, R2, LP2, A2, AP2, OP2 > &b)=delete
std::swap is deleted for nda::basic_array_view.
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=(R const &rhs) noexcept
Assignment operator makes a deep copy of a general contiguous range and assigns it to the 1-dimension...
const_iterator end() const noexcept
Get a const iterator to the end of the view/array.
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...
auto as_array_view() const
static constexpr bool is_stride_order_C() noexcept
Is the stride order of the view/array in C-order?
iterator begin() noexcept
Get an iterator to the beginning of the view/array.
ValueType * data() noexcept
Get a pointer to the actual data (in general this is not the beginning of thr memory block for a view...
basic_array_view & operator=(RHS const &rhs) noexcept
Assignment operator makes a deep copy of the contents of an nda::ArrayOfRank object.
long has_positive_strides() const noexcept
Are all the strides of the memory layout of the view/array positive?
typename OwningPolicy::template handle< ValueType > storage_t
Type of the memory handle (see Handles).
auto & operator/=(RHS const &rhs) noexcept
Division assignment operator.
static constexpr int rank
Number of dimensions of the view.
auto const & shape() const noexcept
Get the shape of the view/array.
static __inline__ decltype(auto) call(Self &&self, Ts const &...idxs) noexcept(has_no_boundcheck)
Implementation of the function call operator.
basic_array_view(layout_t const &idxm, ValueType *p) noexcept
Construct a view from a bare pointer to some contiguous data and a memory layout.
basic_array_view(R &rg) noexcept
Construct a 1-dimensional view of a general contiguous range.
basic_array_view & operator=(basic_array_view const &rhs) noexcept
Copy assignment operator makes a deep copy of the contents of the view.
basic_array_view(std::array< ValueType, N > &a) noexcept
Construct a 1-dimensional view of a std::array.
basic_array_view & operator=(Initializer const &initializer) noexcept
Assignment operator uses an nda::ArrayInitializer to assign to the view.
auto const & strides() const noexcept
Get the strides of the view/array (see nda::idx_map for more details on how we define strides).
long shape(int i) const noexcept
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).
auto & operator+=(RHS const &rhs) noexcept
Addition assignment operator.
auto indices() const noexcept
Get a range that generates all valid index tuples.
basic_array_view()=default
Default constructor constructs an empty view with a default constructed memory handle and layout.
long is_contiguous() const noexcept
Is the memory layout of the view/array contiguous?
storage_t & storage() &noexcept
Get the data storage of the view/array.
static constexpr bool is_stride_order_Fortran() noexcept
Is the stride order of the view/array in Fortran-order?
auto & operator*=(RHS const &rhs) noexcept
Multiplication assignment operator.
basic_array_view(std::array< std::remove_const_t< ValueType >, N > const &a) noexcept
Construct a 1-dimensional view of a std::array.
friend void deep_swap(basic_array_view a, basic_array_view b) noexcept
Swap two views by swapping their data.
iterator end() noexcept
Get an iterator to the end of the view/array.
ValueType value_type
Type of the values in the view (might be const).
long size() const noexcept
Get the total size of the view/array.
auto & operator-=(RHS const &rhs) noexcept
Subtraction assignment operator.
bool empty() const
Is the view/array empty?
basic_array_view & operator=(RHS const &rhs) noexcept
Assignment operator assigns a scalar to the view.
basic_array_view(std::array< long, Rank > const &shape, ValueType *p) noexcept
Construct a view from a bare pointer to some contiguous data and a shape.
OwningPolicy owning_policy_t
Type of the owning policy (see Memory policies).
LayoutPolicy layout_policy_t
Type of the memory layout policy (see Layout policies).
bool is_empty() const noexcept
friend void swap(basic_array_view &a, basic_array_view &b) noexcept
Swap two views by swapping their memory handles and layouts.
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...
const_iterator cbegin() const noexcept
Get a const iterator to the beginning of the view/array.
basic_array_view(layout_t const &idxm, storage_t st)
Construct a view from a given layout and memory handle.
static constexpr int iterator_rank
Rank of the nda::array_iterator for the view/array.
AccessorPolicy accessor_policy_t
Type of the accessor policy (see e.g. nda::default_accessor).
basic_array_view(basic_array_view &&)=default
Default move constructor moves the memory handle and layout.
void rebind(basic_array_view< T, R, LP, A, AP, OP > v) noexcept
Rebind the current view to another view.
basic_array_view(basic_array_view const &)=default
Default copy constructor copies the memory handle and layout.
long extent(int i) const noexcept
Get the extent of the ith dimension.
constexpr auto const & indexmap() const noexcept
Get the memory layout of the view/array.
const_iterator begin() const noexcept
Get a const iterator to the beginning of the view/array.
const_iterator cend() const noexcept
Get a const iterator to the end of the view/array.
storage_t const & storage() const &noexcept
Get the data storage of the view/array.
A generic multi-dimensional array.
Includes all clef relevant headers.
Check if a given type satisfies the memory array concept.
Provides concepts for the nda library.
Provides various convenient aliases and helper functions for nda::basic_array and nda::basic_array_vi...
Provides a custom runtime error class and macros to assert conditions and throw exceptions.
Provides for_each functions for multi-dimensional arrays/views.
decltype(auto) make_regular(A &&a)
Make a given object regular.
constexpr uint64_t static_extents(int i0, Is... is)
Encode the given shape into a single integer using the nda::encode function.
constexpr bool have_same_value_type_v
Constexpr variable that is true if all types in As have the same value type as A0.
constexpr int get_rank
Constexpr variable that specifies the rank of an nda::Array or of a contiguous 1-dimensional range.
std::decay_t< decltype(get_first_element(std::declval< A const >()))> get_value_t
Get the value type of an array/view or a scalar type.
auto make_expr_call(F &&f, Args &&...args)
Create a function call expression from a callable object and a list of arguments.
constexpr bool is_any_lazy
Constexpr variable that is true if any of the given types is lazy.
constexpr uint64_t C_stride_order
C/Row-major stride order.
constexpr bool layout_property_compatible(layout_prop_e from, layout_prop_e to)
Checks if two layout properties are compatible with each other.
constexpr bool ellipsis_is_present
Constexpr variable that is true if the parameter pack Args contains an nda::ellipsis.
constexpr bool has_strided_1d(layout_prop_e lp)
Checks if a layout property has the strided_1d property.
__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.
constexpr bool has_layout_strided_1d
Constexpr variable that is true if type A has the strided_1d nda::layout_prop_e guarantee.
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.
constexpr bool has_contiguous_layout
Constexpr variable that is true if type A has the contiguous nda::layout_prop_e guarantee.
static constexpr bool on_device
Constexpr variable that is true if all given types have a Device 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...
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.
static constexpr bool always_false
Constexpr variable that is always false regardless of the types in Ts (used to trigger static_assert)...
constexpr bool is_scalar_for_v
Constexpr variable used to check requirements when initializing an nda::basic_array or nda::basic_arr...
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...
Provides a class that maps multi-dimensional indices to a linear index and vice versa.
Provides an iterator for nda::basic_array and nda::basic_array_view types.
Macros used in the nda library.
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.
A small wrapper around a single long integer to be used as a linear index.
Contiguous layout policy with C-order (row-major order).
Generic layout policy with arbitrary order.
Memory policy using an nda::mem::handle_heap.
uint64_t stride_order
Stride order of the array/view.
Provides type traits for the nda library.