cppqed-multidiagonal 0.4.0
Multidiagonal sparse operator algebra for C++QED
Loading...
Searching...
No Matches
MultiArray.h
Go to the documentation of this file.
1// Copyright András Vukics 2022–2026. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE.txt)
2#pragma once
3
4#include "Traits.h"
5
6#include <boost/serialization/array.hpp>
7#include <boost/serialization/vector.hpp>
8
9#include <array>
10#include <concepts>
11#include <span>
12#include <stdexcept>
13#include <ranges>
14#include <vector>
15
16
17namespace cppqedutils {
18
32template <typename Type, std::size_t... sizes>
33constexpr auto concatenate(const std::array<Type, sizes>&... arrays)
34{
35 std::array<Type, (sizes + ...)> result;
36 std::size_t index{};
37
38 ((std::copy_n(arrays.begin(), sizes, result.begin() + index), index += sizes), ...);
39
40 return result;
41}
42
43
45template <size_t RANK>
46using Extents=std::array<size_t,RANK>;
47
48
60template <size_t RANK>
62{
63 const auto increment=[&](auto n, auto inc)
64 {
65 using namespace hana::literals;
66 if constexpr (n!=0_c) {
67 if (idx[n]==extents[n]-1) {idx[n]=0; inc(n-1_c,inc);}
68 else idx[n]++;
69 }
70 else idx[0]++; // This will eventually put the index into an illegal state, but this is how all (unchecked) iterators work.
71 };
72
73 increment(hana::llong_c<RANK-1>,increment);
74
75 return idx;
76}
77
79inline auto& incrementMultiIndex(Extents<0>& idx, Extents<0>) {return idx;}
80
81
82namespace multiarray {
83
86inline constexpr struct Placeholder {} _{};
87
88} // multiarray
89
90
92
104template <typename T, size_t RANK>
106{
107public:
108 MultiArrayView(const MultiArrayView&) = default; MultiArrayView(MultiArrayView&&) = default; MultiArrayView() = default;
110
114
117
119 void checkBounds(std::convertible_to<size_t> auto ... i) const
120 {
121#ifndef NDEBUG
122 auto e=extents.begin();
123 (... , [&] {
124 if (auto eComp=e++; static_cast<size_t>(i) >= *eComp)
125 throw std::range_error("Index position: "+std::to_string(eComp-extents.begin())+", index value: "+std::to_string(i)+", extent: "+std::to_string(*eComp));
126 } () );
127#else
128 (... , (void)i); // silence unused warning
129#endif // NDEBUG
130 }
131
133
138 {
139 std::apply([this] (auto ... args) { this->checkBounds(args ...); },idx);
140
141 return dataView[std::ranges::fold_left( std::views::zip(idx,strides), offset,
142 [&] (auto init, auto ids) {return init+get<0>(ids)*get<1>(ids);} ) ];
143 }
144
146 T& operator() (std::convertible_to<size_t> auto ... i) const requires (sizeof...(i)==RANK)
147 {
148 checkBounds(i...);
149 size_t idx=0;
150 auto s=strides.begin();
151 return dataView[ offset + ( ... , [&] { return idx+=(*s++)*i;} () ) ];
152 }
153
155 T& operator() (std::convertible_to<size_t> auto i) const requires (RANK==1) {checkBounds(i); return dataView[offset+strides[0]*i];}
156
158 T& operator() (std::convertible_to<size_t> auto i, std::convertible_to<size_t> auto j) const requires (RANK==2) {
159 checkBounds(i,j); return dataView[offset+strides[0]*i+strides[1]*j];
160 }
161
163 auto operator() (std::convertible_to<size_t> auto i, multiarray::Placeholder) const requires (RANK==2) {
164 return MultiArrayView<T,1>{ {extents[1]}, {strides[1]}, offset+i*strides[0], dataView};
165 }
166
168 auto operator() (multiarray::Placeholder, std::convertible_to<size_t> auto j) const requires (RANK==2) {
169 return MultiArrayView<T,1>{ {extents[0]}, {strides[0]}, offset+j*strides[1], dataView};
170 }
171
173
174 size_t offset;
175
176 std::span<T> dataView;
177
178};
179
180
182template <typename T, size_t RANK>
184
185
187template <typename T, size_t RANK> requires ( !std::is_const_v<T> )
189
190
191// MultiArrayView is itself a reference — passing it by value is equivalent to passing by reference.
192template <typename T, size_t RANK> requires ( !std::is_const_v<T> )
193struct ReferenceMF<MultiArrayView<T,RANK>> : std::type_identity<MultiArrayView<T,RANK>> {};
194
195template <typename T, size_t RANK> requires ( !std::is_const_v<T> )
197
198
200template <typename T1, typename T2, size_t RANK>
202{
203#ifndef NDEBUG
204 if (m1.extents!=m2.extents) throw std::runtime_error("Extent mismatch in "+message+": "+toStringJSON(m1.extents)+" "+toStringJSON(m2.extents));
205#else
206 (void)m1; (void)m2; (void)message; // silence unused warnings
207#endif // NDEBUG
208}
209
212template <typename T1, typename T2, size_t RANK>
214{
215 checkExtents(m1,m2,"MultiArrayView comparison");
216 bool res=true;
218 res &= ( m1(idx)==m2(idx) );
219 return res;
220}
221
222
223namespace multiarray {
224
230template <size_t RANK>
232{
233 Extents<RANK> strides; strides[0]=1;
234 for (size_t i=1; i<RANK; ++i) strides[i]=strides[i-1]*extents[i-1];
235 return strides;
236}
237
239template <size_t RANK>
241{
242 return std::ranges::fold_left(extents,1uz,std::multiplies{});
243}
244
248template <typename T>
249inline constexpr auto noInit = [] (size_t e) -> std::vector<T> {return std::vector<T>(e);};
250
253template <typename T>
254inline constexpr auto zeroInit = [] (size_t e) -> std::vector<T> {return std::vector<T>(e,T(0.));};
255
256
258template <typename T>
259using StorageType = std::vector<T>;
260
261
263template <typename T>
264auto copyInit(const std::ranges::sized_range auto& input) {return [&] (size_t e) {
265 if (size(input) != e) throw std::runtime_error("Extent mismatch in MultiArray copyInit: "+std::to_string(size(input))+" "+std::to_string(e));
267};}
268
269
270} // multiarray
271
272
276template <typename T, size_t RANK>
278{
279#ifndef NDEBUG
281 throw std::invalid_argument(message+" needs a full, contiguous array; got offset "+std::to_string(mav.offset)+" and strides "+toStringJSON(mav.strides));
282#else
283 (void)mav; (void)message; // silence unused warnings
284#endif // NDEBUG
285}
286
287
289
296template <typename T, size_t RANK>
297requires ( !std::is_const_v<T> )
299{
300public:
302
303 MultiArray(const MultiArray&) = delete; MultiArray& operator=(const MultiArray&) = delete;
304
305 MultiArray(MultiArray&&) = default; MultiArray& operator=(MultiArray&&) = default;
306
308 MultiArray(Extents<RANK> extents, auto initializer) requires requires (size_t e) { { initializer(e) } -> std::convertible_to<StorageType>;}
309 : MultiArrayConstView<T,RANK>{extents,multiarray::calculateStrides(extents),0}, data_{initializer(multiarray::calculateExtent(extents))}
310 {
311 this->dataView=std::span<T>(data_);
312 }
313
315 explicit MultiArray(Extents<RANK> extents) : MultiArray{extents,multiarray::noInit<T>} {}
316
317 // @brief Rank-1 constructor from an existing storage vector (copies the data).
318 // explicit MultiArray(const StorageType& st) requires (RANK==1) : MultiArray{{st.size()},multiarray::copyInit<T>(st)} {}
319 // removed: It contradicts the library's own semantics, as this constructor is the one place where a silent deep copy hides behind constructor syntax.
320
322 friend MultiArray copy(const MultiArray& ma) {return MultiArray{ma.extents, [&] (size_t) {return ma.dataStorage();}};}
323
327 template <typename TT>
329 {
330#ifndef NDEBUG
331 if (this->dataView.data()==macv.dataView.data())
332 throw std::runtime_error("Self assignment attempted in MultiArray");
333#endif // NDEBUG
334 checkExtents(*this,macv,"MultiArrayView assignment");
335
336 for (Extents<RANK> idx{}; idx[0]!=this->extents[0]; incrementMultiIndex(idx,this->extents)) (*this)(idx)=macv(idx);
337 }
338
341
343 auto mutableView() {return MultiArrayView<T, RANK>{this->extents,this->strides,0,data_};}
344
346 operator MultiArrayView<T,RANK>() {return mutableView();}
347
349 T& operator()(auto&&... i) {return const_cast<T&>(static_cast<MultiArrayConstView<T,RANK>>(*this)(FWD(i)...)) ;}
350
352 const T& operator()(auto&&... i) const {return static_cast<MultiArrayConstView<T,RANK>>(*this)(FWD(i)...) ;}
353
355 StorageType& dataStorage() {return data_;}
356 const StorageType& dataStorage() const {return data_;}
357
359 friend void tag_invoke( const json::value_from_tag&, json::value& jv, const MultiArray& ma )
360 {
361 jv = json::object{
362 { "extents" , json::value_from(ma.extents) },
363 // { "strides" , ma.strides },
364 { "data", json::value_from(ma.data_) }
365 };
366 }
367
369 friend MultiArray tag_invoke( const json::value_to_tag< MultiArray >&, const json::value& jv )
370 {
371 return MultiArray{
372 json::value_to<Extents<RANK>>(jv.as_object().at("extents")),
373 multiarray::copyInit<T>( json::value_to<StorageType>(jv.as_object().at("data") ) ) };
374 }
375
376private:
377 StorageType data_;
378
379 friend class boost::serialization::access;
380
382 template<class Archive> void save(Archive& ar, const unsigned int) const {ar & this->extents & data_;}
383
385 template<class Archive> void load(Archive& ar, const unsigned int)
386 {
387 ar & this->extents & data_; this->strides=multiarray::calculateStrides(this->extents); this->offset=0;
388 this->dataView=std::span<T>(data_);
389 }
390
391 BOOST_SERIALIZATION_SPLIT_MEMBER()
392
393};
394
395
396namespace multiarray {
397
399template <typename T>
400MultiArray<T,1> fromStorage(std::vector<T> v)
401{
402 const size_t s = v.size();
403 return MultiArray<T,1>{ {s}, [&] (size_t) {return std::move(v);} };
404}
405
406} // multiarray
407
409template <typename T, size_t RANK>
410constexpr auto passByValue_v<MultiArray<T,RANK>> = false;
411
412
413} // cppqedutils
#define FWD(x)
Perfect-forwarding macro. Expands to std::forward<decltype(x)>(x), avoiding the need to repeat the ty...
Definition Traits.h:5
A non-owning, strided view over a contiguous buffer. Should be passed by value.
Definition MultiArray.h:106
Extents< RANK > extents
Definition MultiArray.h:172
void checkBounds(std::convertible_to< size_t > auto ... i) const
Bounds-check all indices against extents. Active only in debug builds (NDEBUG not defined).
Definition MultiArray.h:119
MultiArrayView(Extents< RANK > extents, Extents< RANK > strides, size_t offset, auto &&... dataView)
Constructs a view with explicit extents, strides, offset, and data span. The offset accumulates from ...
Definition MultiArray.h:112
size_t offset
Definition MultiArray.h:174
T & operator()(Extents< RANK > idx) const
Subscript by Extents<RANK> multi-index. Always const — does not modify the view, only potentially the...
Definition MultiArray.h:137
std::span< T > dataView
Definition MultiArray.h:176
MultiArrayView(const MultiArrayView &)=default
MultiArrayView & operator=(const MultiArrayView &)=default
Extents< RANK > strides
Definition MultiArray.h:172
Owning, move-only, rank-RANK array. Non-copyable; follows value semantics regarding constness.
Definition MultiArray.h:299
MultiArray(Extents< RANK > extents, auto initializer)
Primary constructor. initializer is a callable (size_t totalElements) -> StorageType.
Definition MultiArray.h:308
multiarray::StorageType< T > StorageType
Definition MultiArray.h:301
const T & operator()(auto &&... i) const
Const element access. This overload is needed to prevent the non-const overload from shadowing the ba...
Definition MultiArray.h:352
void assignTo(MultiArrayConstView< TT, RANK > macv)
Element-by-element assignment from a const view of compatible type.
Definition MultiArray.h:328
friend void tag_invoke(const json::value_from_tag &, json::value &jv, const MultiArray &ma)
Boost.JSON serialization: emits {"extents": [...], "data": [...]}.
Definition MultiArray.h:359
const StorageType & dataStorage() const
Definition MultiArray.h:356
MultiArrayConstView< T, RANK > constView() const
Explicit const view, useful where implicit conversion is suppressed.
Definition MultiArray.h:340
MultiArray(const MultiArray &)=delete
MultiArray(MultiArray &&)=default
T & operator()(auto &&... i)
Non-const element access, forwarding to the base const overload via const_cast.
Definition MultiArray.h:349
MultiArray(Extents< RANK > extents)
Constructs an array with given extents and unspecified contents (see multiarray::noInit).
Definition MultiArray.h:315
auto mutableView()
Explicit mutable view.
Definition MultiArray.h:343
StorageType & dataStorage()
Direct access to the underlying storage vector.
Definition MultiArray.h:355
friend MultiArray copy(const MultiArray &ma)
Named copy: constructs a new owning array with a deep copy of ma's data.
Definition MultiArray.h:322
friend MultiArray tag_invoke(const json::value_to_tag< MultiArray > &, const json::value &jv)
Boost.JSON deserialization: reconstructs from {"extents": [...], "data": [...]}.
Definition MultiArray.h:369
constexpr struct cppqedutils::multiarray::Placeholder _
auto calculateExtent(Extents< RANK > extents)
Returns the total number of elements: the product of all extents.
Definition MultiArray.h:240
auto copyInit(const std::ranges::sized_range auto &input)
Initializer that copies from a sized range, checking that its size matches the requested extent.
Definition MultiArray.h:264
std::vector< T > StorageType
Underlying storage type: std::vector<T>. May eventually be replaced by kokkos::View or std::valarray.
Definition MultiArray.h:259
constexpr auto zeroInit
Initializer that allocates storage of size e and zero-initializes all elements via T(0....
Definition MultiArray.h:254
auto calculateStrides(Extents< RANK > extents)
Computes first-index-fastest strides from extents (std::layout_left; column-major for rank 2): stride...
Definition MultiArray.h:231
constexpr auto noInit
Initializer for storage of size e whose contents the caller overwrites before reading them.
Definition MultiArray.h:249
Namespace alias for Boost.Hana, used for compile-time sequences and heterogeneous containers.
Definition MultiArray.h:17
constexpr auto concatenate(const std::array< Type, sizes > &... arrays)
Concatenates multiple std::array instances of different sizes into one.
Definition MultiArray.h:33
std::array< size_t, RANK > Extents
Compile-time–fixed shape and stride descriptor for a rank-RANK array. Should be passed by value,...
Definition MultiArray.h:46
std::string toStringJSON(auto &&v)
Serializes any JSON-convertible value to a string via json::value_from.
Definition Traits.h:27
auto & incrementMultiIndex(Extents< RANK > &idx, Extents< RANK > extents)
Increments a multi-index in last-index-fastest order, in-place.
Definition MultiArray.h:61
void checkExtents(MultiArrayConstView< T1, RANK > m1, MultiArrayConstView< T2, RANK > m2, std::string message)
Debug-mode extent check between two views. Throws std::runtime_error on mismatch.
Definition MultiArray.h:201
bool isEqual(MultiArrayConstView< T1, RANK > m1, MultiArrayConstView< T2, RANK > m2)
Element-by-element equality test. Traverses all elements via incrementMultiIndex.
Definition MultiArray.h:213
void checkContiguous(MultiArrayView< T, RANK > mav, std::string message)
Debug-mode check that a view covers its storage as an owning MultiArray of the same extents would: of...
Definition MultiArray.h:277
Metafunction yielding the const-reference type of State. Defaults to const State&....
Definition Traits.h:51
Metafunction yielding the reference type of State. Defaults to State&. Specialized for view types to ...
Definition Traits.h:44
Sentinel type for rank-2 slice notation: mav(i, _) returns the i-th row as a rank-1 view.
Definition MultiArray.h:86