Chi-Tech
ndarray.h
Go to the documentation of this file.
1#ifndef CHITECH_NDARRAY_H
2#define CHITECH_NDARRAY_H
3
4#include <cstddef>
5#include <type_traits>
6#include <cassert>
7#include <vector>
8#include <array>
9#include <stdexcept>
10#include <string>
11
12namespace chi_data_types
13{
14
15template<typename T>
17{
18private:
19 size_t rank_;
20 size_t* dimensions_;
21 size_t* strides_;
22 size_t size_;
24
25 template<bool...>
26 struct bool_pack{};
27
28 template<class... U>
29 using conjunction = std::is_same<
30 bool_pack<true,U::value...>,
31 bool_pack<U::value..., true>
32 >;
33
34 template<typename... U>
35 using AllIntegral = typename conjunction<std::is_integral<U>...>::type;
36
37public:
38 /** Creates an array with the specified number of elements in each dimension,
39 * from a vector-list.
40 *
41 * \param dims `std::vector` list of the number of elements in each
42 * dimension.
43 * \throw std::bad_alloc if memory allocation fails.
44 *
45 * This constructor creates an array with the specified size.
46 */
47 template<typename D>
48 explicit
49 NDArray(const std::vector<D>& dims) :
50 rank_(0), dimensions_(nullptr), strides_(nullptr),
51 size_(0), base_(nullptr)
52 {
53 static_assert(std::is_integral<D>::value,
54 "NDArray dims argument must have vector of integral types.");
55 const size_t N = dims.size();
56
57 rank_ = N;
58
59 strides_ = new size_t[N];
60 dimensions_ = new size_t[N];
61
62 //Populate dimensions
63 for(size_t i = 0; i < N; ++i)
64 dimensions_[i] = dims[i];
65
66 //Populate strides and size
67 size_ = 1;
68 for(size_t i = 0; i < N; ++i)
69 {
70 assert(dimensions_[i] > 0);
71 size_ *= dimensions_[i];
72 strides_[i] = 1;
73 for(size_t j = i+1; j < N; ++j)
74 strides_[i] *= dimensions_[j];
75 }
76
77 base_ = new T[size_];
78 for (size_t i=0; i < size_; ++i)
79 base_[i] = 0.0;
80 }
81
82 /** Creates an array with the specified number of elements in each dimension,
83 * from an array.
84 *
85 * \param dims `std::array` list of the number of elements in each
86 * dimension.
87 * \throw std::bad_alloc if memory allocation fails.
88 *
89 * This constructor creates an array with the specified size.
90 */
91 template<typename D, size_t N>
92 explicit
93 NDArray(const std::array<D, N>& dims) :
94 rank_(0), dimensions_(nullptr), strides_(nullptr),
95 size_(0), base_(nullptr)
96 {
97 static_assert(std::is_integral<D>::value,
98 "NDArray dims argument must have array of integral types.");
99 rank_ = N;
100
101 strides_ = new size_t[N];
102 dimensions_ = new size_t[N];
103
104 //Populate dimensions
105 for(size_t i = 0; i < N; ++i)
106 dimensions_[i] = dims[i];
107
108 //Populate strides and size
109 size_ = 1;
110 for(size_t i = 0; i < N; ++i)
111 {
112 assert(dimensions_[i] > 0);
113 size_ *= dimensions_[i];
114 strides_[i] = 1;
115 for(size_t j = i+1; j < N; ++j)
116 strides_[i] *= dimensions_[j];
117 }
118
119 base_ = new T[size_];
120 for (size_t i=0; i < size_; ++i)
121 base_[i] = 0.0;
122 }
123
124 /** Creates an array with the specified number of elements in each dimension,
125 * from an initializer-list.
126 *
127 * \param dims `std::vector` list of the number of elements in each
128 * dimension.
129 * \throw std::bad_alloc if memory allocation fails.
130 *
131 * This constructor creates an array with the specified size.
132 */
133 template<typename D>
134 NDArray(const std::initializer_list<D>& dims) :
135 rank_(0), dimensions_(nullptr), strides_(nullptr),
136 size_(0), base_(nullptr)
137 {
138 static_assert(std::is_integral<D>::value,
139 "NDArray dims argument must have vector of integral types.");
140 const size_t N = dims.size();
141
142 rank_ = N;
143
144 strides_ = new size_t[N];
145 dimensions_ = new size_t[N];
146
147 //Populate dimensions
148 {
149 size_t i=0;
150 for (size_t val : dims)
151 {
152 dimensions_[i] = val; ++i;
153 if (i >= N) break;
154 }
155 }
156
157 //Populate strides and size
158 size_ = 1;
159 for(size_t i = 0; i < N; ++i)
160 {
161 assert(dimensions_[i] > 0);
162 size_ *= dimensions_[i];
163 strides_[i] = 1;
164 for(size_t j = i+1; j < N; ++j)
165 strides_[i] *= dimensions_[j];
166 }
167
168 base_ = new T[size_];
169 for (size_t i=0; i < size_; ++i)
170 base_[i] = 0.0;
171 }
172
173 /** Creates an array with the specified number of elements in each dimension,
174 * from a vector. Each entry in the array is assigned the designated value.
175 *
176 * \param dims `std::vector` list of the number of elements in each
177 * dimension.
178 * \param value The value to assing to each element.
179 * \throw std::bad_alloc if memory allocation fails.
180 *
181 * This constructor creates an array with the specified size.
182 */
183 template<typename D>
184 explicit
185 NDArray(const std::vector<D>& dims, T value) :
186 rank_(0), dimensions_(nullptr), strides_(nullptr),
187 size_(0), base_(nullptr)
188 {
189 static_assert(std::is_integral<D>::value,
190 "NDArray dims argument must have vector of integral types.");
191 const size_t N = dims.size();
192
193 rank_ = N;
194
195 strides_ = new size_t[N];
196 dimensions_ = new size_t[N];
197
198 //Populate dimensions
199 for(size_t i = 0; i < N; ++i)
200 dimensions_[i] = dims[i];
201
202 //Populate strides and size
203 size_ = 1;
204 for(size_t i = 0; i < N; ++i)
205 {
206 assert(dimensions_[i] > 0);
207 size_ *= dimensions_[i];
208 strides_[i] = 1;
209 for(size_t j = i+1; j < N; ++j)
210 strides_[i] *= dimensions_[j];
211 }
212
213 base_ = new T[size_];
214 for (size_t i=0; i < size_; ++i)
215 base_[i] = value;
216 }
217
218 /** Creates an array with the specified number of elements in each dimension,
219 * from an array. Each entry in the array is assigned the designated value.
220 *
221 * \param dims `std::array` list of the number of elements in each
222 * dimension.
223 * \param value The value to assing to each element.
224 * \throw std::bad_alloc if memory allocation fails.
225 *
226 * This constructor creates an array with the specified size.
227 */
228 template<typename D, size_t N>
229 explicit
230 NDArray(const std::array<D, N>& dims, T value) :
231 rank_(0), dimensions_(nullptr), strides_(nullptr),
232 size_(0), base_(nullptr)
233 {
234 static_assert(std::is_integral<D>::value,
235 "NDArray dims argument must have vector of integral types.");
236 rank_ = N;
237
238 strides_ = new size_t[N];
239 dimensions_ = new size_t[N];
240
241 //Populate dimensions
242 for(size_t i = 0; i < N; ++i)
243 dimensions_[i] = dims[i];
244
245 //Populate strides and size
246 size_ = 1;
247 for(size_t i = 0; i < N; ++i)
248 {
249 assert(dimensions_[i] > 0);
250 size_ *= dimensions_[i];
251 strides_[i] = 1;
252 for(size_t j = i+1; j < N; ++j)
253 strides_[i] *= dimensions_[j];
254 }
255
256 base_ = new T[size_];
257 for (size_t i=0; i < size_; ++i)
258 base_[i] = value;
259 }
260
261 /** Creates an array with the specified number of elements in each dimension,
262 * from an initializer-list. Each entry in the array is assigned the
263 * designated value.
264 *
265 * \param dims `std::initializer` list of the number of elements in each
266 * dimension.
267 * \param value The value to assing to each element.
268 * \throw std::bad_alloc if memory allocation fails.
269 *
270 * This constructor creates an array with the specified size.
271 */
272 template<typename D>
273 NDArray(const std::initializer_list<D>& dims, T value) :
274 rank_(0), dimensions_(nullptr), strides_(nullptr),
275 size_(0), base_(nullptr)
276 {
277 static_assert(std::is_integral<D>::value,
278 "NDArray dims argument must have vector of integral types.");
279 const size_t N = dims.size();
280
281 rank_ = N;
282
283 strides_ = new size_t[N];
284 dimensions_ = new size_t[N];
285
286 //Populate dimensions
287 {
288 size_t i=0;
289 for (size_t val : dims)
290 {
291 dimensions_[i] = val; ++i;
292 if (i >= N) break;
293 }
294 }
295
296 //Populate strides and size
297 size_ = 1;
298 for(size_t i = 0; i < N; ++i)
299 {
300 assert(dimensions_[i] > 0);
301 size_ *= dimensions_[i];
302 strides_[i] = 1;
303 for(size_t j = i+1; j < N; ++j)
304 strides_[i] *= dimensions_[j];
305 }
306
307 base_ = new T[size_];
308 for (size_t i=0; i < size_; ++i)
309 base_[i] = value;
310 }
311
312 /** Creates an empty array.
313 * \throw std::bad_alloc if memory allocation fails.
314 *
315 * This constructor creates an empty array and initializes the reference count to one.
316 */
318 rank_(0), dimensions_(nullptr), strides_(nullptr),
319 size_(0), base_(nullptr)
320 {
321 }
322
323 /** Copy construct from another array.
324 * \param other The array to copy.
325 */
326 NDArray(NDArray<T> const &other) :
327 rank_(other.rank_),
328 dimensions_(nullptr),
329 strides_(nullptr),
330 size_(other.size_),
331 base_(nullptr)
332 {
333
334 const size_t N = rank_;
335 strides_ = new size_t[N];
336 dimensions_ = new size_t[N];
337 base_ = new T[size_];
338
339 for(size_t i = 0; i < N; ++i)
340 {
341 dimensions_[i] = other.dimensions_[i];
342 strides_[i] = other.strides_[i];
343 }
344
345 for (size_t i=0; i < size_; ++i)
346 base_[i] = other.base_[i];
347 }
348
349 /** Assign from another array.
350 * \param other The array to copy.
351 */
353 {
354 NDArray<T>(other).swap(*this);
355 return *this;
356 }
357
358 /**Move constructor.*/
359 NDArray(NDArray<T>&& other) noexcept :
360 rank_(std::move(other.rank_)),
361 dimensions_(std::move(other.dimensions_)),
362 strides_(std::move(other.strides_)),
363 size_(std::move(other.size_)),
364 base_(std::move(other.base_))
365 {
366 }
367
368 /**Deleted move assignment operator*/
370
371 /**Sets a value to all the items in the array.*/
372 void set(T value)
373 {
374 for (size_t i = 0; i < size_; ++i)
375 base_[i] = value;
376 }
377
378 /** Swap the contents of this array with another array.
379 * \param other The array to swap with.
380 */
381 void swap(NDArray<T> &other)
382 {
383 std::swap(rank_, other.rank_);
384 std::swap(dimensions_, other.dimensions_);
385 std::swap(strides_, other.strides_);
386 std::swap(size_, other.size_);
387 std::swap(base_, other.base_);
388 }
389
390 /** Returns the number of elements in the array.*/
391 size_t size() const noexcept { return size_; }
392
393 /** Returns true if the array has no elements.*/
394 bool empty() const noexcept { return size_ == 0; }
395
396 /** Returns an iterator pointing to the beginning of the array.*/
397 T * begin() const noexcept { return base_; }
398
399 /** Returns a constant iterator pointing to the beginning of the array.*/
400 const T * cbegin() const noexcept { return base_; }
401
402 /** Returns an iterator pointing to the end of the array.*/
403 T * end() const noexcept { return base_ + size_; }
404
405 /** Returns a constant iterator pointing to the end of the array*/
406 const T * cend() const noexcept { return base_ + size_; }
407
408 /** Returns a pointer to the underlying array data.*/
409 T * data() const noexcept { return base_; }
410
411 /** Returns the rank of the array.*/
412 size_t rank() const noexcept { return rank_; }
413
414 /**Returns the dimension of the array.*/
415 std::vector<size_t> dimension() const
416 {
417 std::vector<size_t> dim(rank_, 0);
418 for (size_t i=0; i < rank_; ++i)
419 dim[i] = dimensions_[i];
420
421 return dim;
422 }
423
424 /** Resizes the array with a vector.
425 *
426 * \param dims std::vector of the number of elements in each
427 * dimension.
428 * \throw std::bad_alloc if memory allocation fails.
429 *
430 * This method resizes the array to the specified number of elements. If the
431 * current size is equal to the new size, no memory allocation occurs.
432 */
433 template<typename D>
434 void resize(const std::vector<D>& dims)
435 {
436 static_assert(std::is_integral<D>::value,
437 "NDArray dims argument must have vector of integral types.");
438
439 delete [] dimensions_;
440 dimensions_ = nullptr;
441 delete [] strides_;
442 strides_ = nullptr;
443 delete [] base_;
444 base_ = nullptr;
445
446 NDArray<T>(dims).swap(*this);
447 }
448
449 /** Resizes the array with an array.
450 *
451 * \param dims std::array of the number of elements in each
452 * dimension.
453 * \throw std::bad_alloc if memory allocation fails.
454 *
455 * This method resizes the array to the specified number of elements. If the
456 * current size is equal to the new size, no memory allocation occurs.
457 */
458 template<typename D, size_t N>
459 void resize(const std::array<D, N>& dims)
460 {
461 static_assert(std::is_integral<D>::value,
462 "NDArray dims argument must have vector of integral types.");
463
464 delete [] dimensions_;
465 dimensions_ = nullptr;
466 delete [] strides_;
467 strides_ = nullptr;
468 delete [] base_;
469 base_ = nullptr;
470
471 NDArray<T>(dims).swap(*this);
472 }
473
474 /** Resizes the array with an initializer_list.
475 *
476 * \param dims std::initializer_list of the number of elements in each
477 * dimension.
478 * \throw std::bad_alloc if memory allocation fails.
479 *
480 * This method resizes the array to the specified number of elements. If the
481 * current size is equal to the new size, no memory allocation occurs.
482 */
483 template<typename D>
484 void resize(const std::initializer_list<D>& dims)
485 {
486 static_assert(std::is_integral<D>::value,
487 "NDArray dims argument must have vector of integral types.");
488
489 delete [] dimensions_;
490 dimensions_ = nullptr;
491 delete [] strides_;
492 strides_ = nullptr;
493 delete [] base_;
494 base_ = nullptr;
495
496 NDArray<T>(dims).swap(*this);
497 }
498
499 /** Accesses the specified element.
500 * \param args The indices of the desired element.
501 * \return Read/write reference to the element.
502 */
503 template<typename... Args>
504 T& operator()(Args... args) noexcept
505 {
506 static_assert(AllIntegral<Args...>::value,
507 "NDArray::operator[]: All parameters must be of integral type");
508
509 size_t indices[] { static_cast<size_t>(args)... };
510
511 const size_t N = rank_;
512
513 T *address = base_ + indices[N - 1];
514 for(size_t i = 0;i < N-1;++i)
515 address += strides_[i] * indices[i];
516 return *(address);
517 }
518
519 /** Accesses the specified element.
520 * \param args The indices of the desired element.
521 * \return Read reference to the element.
522 */
523 template<typename... Args>
524 T const& operator()(Args... args) const noexcept
525 {
526 static_assert(AllIntegral<Args...>::value,
527 "NDArray::operator[]: All parameters must be of integral type");
528
529 size_t indices[] { static_cast<size_t>(args)... };
530
531 const size_t N = rank_;
532
533 T *address = base_ + indices[N - 1];
534 for(size_t i = 0;i < N-1;++i)
535 address += strides_[i] * indices[i];
536 return *(address);
537 }
538
539 /** Accesses the specified element with safety checks.
540 *
541 * \param args The indices of the desired element.
542 * \throw std::invalid_argument if the number of arguments are incorrect and
543 * std::out_of_range if one of the dimension-indices are out of range.
544 * \return Read/write reference to the element.
545 */
546 template<typename... Args>
547 T& at(Args... args) noexcept
548 {
549 static_assert(AllIntegral<Args...>::value,
550 "NDArray::at(): All parameters must be of integral type");
551
552 if (sizeof...(args) != rank_)
553 throw std::invalid_argument("NDArray::at(): Number of arguments " +
554 std::to_string(sizeof...(args)) + " not equal to rank " +
555 std::to_string(rank_));
556
557 const size_t N = rank_;
558 size_t indices[] { static_cast<size_t>(args)... };
559 for (size_t i=0; i<N; ++i)
560 if (indices[i] >= dimensions_[i])
561 throw std::out_of_range("NDArray::at(): Index " + std::to_string(i) +
562 " out of range " + std::to_string(indices[i]) +
563 " must be <" + std::to_string(dimensions_[i]));
564
565 T *address = base_ + indices[N - 1];
566 for(size_t i = 0;i < N-1;++i)
567 address += strides_[i] * indices[i];
568 return *(address);
569 }
570
571 /** Returns a linear index to the specified element with safety checks.
572 *
573 * \param args The indices of the desired element.
574 * \throw std::invalid_argument if the number of arguments are incorrect and
575 * std::out_of_range if one of the dimension-indices are out of range.
576 * \return Linear index to the specified element.
577 */
578 template<typename... Args>
579 size_t MapNDtoLin(Args... args) const
580 {
581 static_assert(AllIntegral<Args...>::value,
582 "NDArray::at(): All parameters must be of integral type");
583
584 if (sizeof...(args) != rank_)
585 throw std::invalid_argument("NDArray::at(): Number of arguments " +
586 std::to_string(sizeof...(args)) + " not equal to rank " +
587 std::to_string(rank_));
588
589 const size_t N = rank_;
590 size_t indices[] { static_cast<size_t>(args)... };
591 for (size_t i=0; i<N; ++i)
592 if (indices[i] >= dimensions_[i])
593 throw std::out_of_range("NDArray::at(): Index " + std::to_string(i) +
594 " out of range " + std::to_string(indices[i]) +
595 " must be <" + std::to_string(dimensions_[i]));
596
597 size_t index = indices[N-1];
598 for(size_t i = 0;i < N-1;++i)
599 index += strides_[i] * indices[i];
600 return index;
601 }
602
603 /** Deletes the array.
604 *
605 * The destructor deletes the underlying array data.
606 */
608 {
609 delete [] dimensions_;
610 dimensions_ = nullptr;
611 delete [] strides_;
612 strides_ = nullptr;
613 delete [] base_;
614 base_ = nullptr;
615 }
616};
617
618}//namespace chi_data_types
619
620#endif //CHITECH_NDARRAY_H
bool empty() const noexcept
Definition: ndarray.h:394
NDArray(const std::array< D, N > &dims)
Definition: ndarray.h:93
NDArray(NDArray< T > &&other) noexcept
Definition: ndarray.h:359
typename conjunction< std::is_integral< U >... >::type AllIntegral
Definition: ndarray.h:35
T * data() const noexcept
Definition: ndarray.h:409
NDArray(const std::vector< D > &dims, T value)
Definition: ndarray.h:185
T & at(Args... args) noexcept
Definition: ndarray.h:547
void swap(NDArray< T > &other)
Definition: ndarray.h:381
std::vector< size_t > dimension() const
Definition: ndarray.h:415
T const & operator()(Args... args) const noexcept
Definition: ndarray.h:524
void resize(const std::vector< D > &dims)
Definition: ndarray.h:434
const T * cend() const noexcept
Definition: ndarray.h:406
T & operator()(Args... args) noexcept
Definition: ndarray.h:504
NDArray(NDArray< T > const &other)
Definition: ndarray.h:326
NDArray(const std::initializer_list< D > &dims, T value)
Definition: ndarray.h:273
NDArray(const std::array< D, N > &dims, T value)
Definition: ndarray.h:230
std::is_same< bool_pack< true, U::value... >, bool_pack< U::value..., true > > conjunction
Definition: ndarray.h:32
size_t rank() const noexcept
Definition: ndarray.h:412
NDArray< T > & operator=(NDArray< T > const &other)
Definition: ndarray.h:352
size_t MapNDtoLin(Args... args) const
Definition: ndarray.h:579
NDArray(const std::vector< D > &dims)
Definition: ndarray.h:49
size_t * dimensions_
Definition: ndarray.h:20
void resize(const std::initializer_list< D > &dims)
Definition: ndarray.h:484
T * begin() const noexcept
Definition: ndarray.h:397
NDArray< T > & operator=(NDArray< T > &&)=delete
void set(T value)
Definition: ndarray.h:372
const T * cbegin() const noexcept
Definition: ndarray.h:400
T * end() const noexcept
Definition: ndarray.h:403
NDArray(const std::initializer_list< D > &dims)
Definition: ndarray.h:134
size_t size() const noexcept
Definition: ndarray.h:391
void resize(const std::array< D, N > &dims)
Definition: ndarray.h:459