ampsci
c++ program for high-precision atomic structure calculations of single-valence systems
Array.hpp
1 #pragma once
2 #include "Template.hpp"
3 #include <array>
4 #include <cassert>
5 #include <numeric>
6 #include <vector>
7 
8 namespace qip {
9 
10 //==============================================================================
12 
17 template <typename T>
18 class StrideIterator : public qip::Comparison<StrideIterator<T>> {
19 protected:
20  T *m_ptr;
21  long m_stride;
22 
23 public:
24  StrideIterator(T *ptr, long stride) : m_ptr(ptr), m_stride(stride) {}
25 
26  T &operator*() { return *m_ptr; }
27 
28  const T &operator*() const { return *m_ptr; }
29 
30  bool operator==(const StrideIterator &other) const {
31  return m_ptr == other.m_ptr;
32  }
33  bool operator<(const StrideIterator &other) const {
34  return m_ptr < other.m_ptr;
35  }
36 
37  StrideIterator &operator++() {
38  m_ptr += m_stride;
39  return *this;
40  }
41  StrideIterator &operator--() {
42  m_ptr -= m_stride;
43  return *this;
44  }
45  StrideIterator operator++(int) {
46  auto temp = *this;
47  ++*this;
48  return temp;
49  }
50  StrideIterator operator--(int) {
51  auto temp = *this;
52  --*this;
53  return temp;
54  }
55 
56  StrideIterator &operator+=(long n) {
57  m_ptr += n * m_stride;
58  return *this;
59  }
60 
61  StrideIterator &operator-=(long n) {
62  m_ptr -= n * m_stride;
63  return *this;
64  }
65 
66  StrideIterator operator+(long n) const {
67  auto out_iter = *this;
68  return out_iter += n;
69  }
70 
71  StrideIterator operator-(long n) const {
72  auto out_iter = *this;
73  return out_iter -= n;
74  }
75 };
76 
78 template <typename T>
79 class ConstStrideIterator : public qip::Comparison<ConstStrideIterator<T>> {
80 protected:
81  T *m_ptr;
82  long m_stride;
83 
84 public:
85  ConstStrideIterator(T *ptr, long stride) : m_ptr(ptr), m_stride(stride) {}
86 
87  const T &operator*() const { return *m_ptr; }
88 
89  bool operator==(const ConstStrideIterator &other) const {
90  return m_ptr == other.m_ptr;
91  }
92  bool operator<(const ConstStrideIterator &other) const {
93  return m_ptr < other.m_ptr;
94  }
95 
96  ConstStrideIterator &operator++() {
97  m_ptr += m_stride;
98  return *this;
99  }
100  ConstStrideIterator &operator--() {
101  m_ptr -= m_stride;
102  return *this;
103  }
104  ConstStrideIterator operator++(int) {
105  auto temp = *this;
106  ++*this;
107  return temp;
108  }
109  ConstStrideIterator operator--(int) {
110  auto temp = *this;
111  --*this;
112  return temp;
113  }
114 
115  ConstStrideIterator &operator+=(long n) {
116  m_ptr += n * m_stride;
117  return *this;
118  }
119 
120  ConstStrideIterator &operator-=(long n) {
121  m_ptr -= n * m_stride;
122  return *this;
123  }
124 
125  ConstStrideIterator operator+(long n) const {
126  auto out_iter = *this;
127  return out_iter += n;
128  }
129 
130  ConstStrideIterator operator-(long n) const {
131  auto out_iter = *this;
132  return out_iter -= n;
133  }
134 };
135 
136 //==============================================================================
138 
143 template <typename T = double>
144 class ArrayView {
145 
146 private:
147  std::size_t m_size; // number of elements
148  std::size_t m_stride;
149  T *m_data;
150 
151 public:
152  ArrayView(T *data, std::size_t size, std::size_t stride = 1)
153  : m_size(size), m_stride(stride), m_data(data) {}
154 
155  std::size_t size() const { return m_size; }
156 
157  T &operator[](std::size_t i) { return m_data[i * m_stride]; }
158 
159  T operator[](std::size_t i) const { return m_data[i * m_stride]; }
160 
161  T &at(std::size_t i) {
162  assert(i < m_size);
163  return m_data[i * m_stride];
164  }
165 
166  T at(std::size_t i) const {
167  assert(i < m_size);
168  return m_data[i * m_stride];
169  }
170 
171  T &operator()(std::size_t i) { return at(i); }
172  T operator()(std::size_t i) const { return at(i); }
173 
174  T &front() { return at(0); }
175  T front() const { return at(0); }
176  T &back() { return at(m_size - 1); }
177  T back() const { return at(m_size - 1); }
178 
179  T *data() { return m_data; }
180 
182  auto begin() { return StrideIterator(m_data, long(m_stride)); }
183  auto cbegin() const { return ConstStrideIterator(m_data, long(m_stride)); }
184 
185  auto end() {
186  return StrideIterator(m_data + long(m_size * m_stride), long(m_stride));
187  }
188  auto cend() const {
189  return ConstStrideIterator(m_data + long(m_size * m_stride),
190  long(m_stride));
191  }
192 
193  auto rbegin() {
194  return StrideIterator(m_data + long(m_size * m_stride) - long(m_stride),
195  -long(m_stride));
196  }
197  auto crbegin() const {
198  return ConstStrideIterator(
199  m_data + long(m_size * m_stride) - long(m_stride), -long(m_stride));
200  }
201 
202  auto rend() {
203  return StrideIterator(m_data - long(m_stride), -long(m_stride));
204  }
205  auto crend() const {
206  return ConstStrideIterator(m_data - long(m_stride), -long(m_stride));
207  }
208 
210  std::vector<T> vector() {
211  std::vector<T> out;
212  for (std::size_t i = 0; i < m_size; ++i) {
213  out.push_back(m_data[i * m_stride]);
214  }
215  return out;
216  }
217 };
218 
219 //==============================================================================
220 
222 template <typename T, typename... Args>
223 T product(T first, Args... rest) {
224  if constexpr (sizeof...(rest) == 0) {
225  return first;
226  } else {
227  return first * product(rest...);
228  }
229 }
230 
231 template <std::size_t N>
232 void NDrange_impl(std::vector<std::array<std::size_t, N>> &result,
233  std::array<std::size_t, N> &current,
234  const std::array<std::size_t, N> &maxValues,
235  std::size_t index) {
236  if (index == N) {
237  result.push_back(current);
238  return;
239  }
240 
241  for (std::size_t i = 0; i < maxValues[index]; ++i) {
242  current[index] = i;
243  NDrange_impl<N>(result, current, maxValues, index + 1);
244  }
245 }
246 
248 
255 template <typename... Args>
256 auto NDrange(std::size_t first, Args... rest) {
257  constexpr std::size_t N = sizeof...(rest) + 1;
258 
259  const std::array<std::size_t, N> maxValues = {
260  first, static_cast<std::size_t>(rest)...};
261 
262  std::vector<std::array<std::size_t, N>> result;
263  result.reserve(product(first, static_cast<std::size_t>(rest)...));
264 
265  std::array<std::size_t, N> current = {0};
266 
267  NDrange_impl<N>(result, current, maxValues, 0);
268  return result;
269 }
270 
271 //==============================================================================
272 template <typename T = double>
273 class Array : public Arithmetic<Array<T>>, Arithmetic2<Array<T>, T> {
274 
275 private:
276  // List of sizes for each array dimension
277  std::vector<std::size_t> m_sizes;
278  // Number of array dimensions
279  std::size_t m_Ndim;
280  // Cumulative sizes (used to index into data)
281  std::vector<std::size_t> m_cumulative_sizes;
282  // Total number of elements
283  std::size_t m_total_size;
284  // Raw data
285  std::vector<T> m_data;
286 
287 public:
290  template <typename... Args>
291  Array(std::size_t first, Args... rest);
292 
294  template <typename... Args>
295  void resize(std::size_t first, Args... rest);
296 
298  std::size_t size() const { return m_total_size; }
299 
301  std::size_t size(std::size_t dim) const { return m_sizes.at(dim); }
302 
304  std::size_t dimensions() const { return m_Ndim; }
305 
307  const std::vector<std::size_t> &shape() const { return m_sizes; }
308 
310  template <typename... Args>
311  T &at(std::size_t first, Args... rest);
312 
314  template <typename... Args>
315  T at(std::size_t first, Args... rest) const;
316 
318  template <typename... Args>
319  T &operator()(std::size_t first, Args... rest);
320 
322  template <typename... Args>
323  T operator()(std::size_t first, Args... rest) const;
324 
327  T *data() { return m_data.data(); }
328  const T *data() const { return m_data.data(); }
329 
331  const std::vector<T> &vector() const { return m_data; }
332 
334  std::size_t rows() const { return size(0); }
336  std::size_t cols() const { return size(1); }
337 
339  ArrayView<T> row(std::size_t i);
340  ArrayView<const T> row(std::size_t i) const;
342  ArrayView<T> col(std::size_t j);
343  ArrayView<const T> col(std::size_t j) const;
344 
346  auto begin() { return m_data.begin(); }
348  auto cbegin() const { return m_data.cbegin(); }
350  auto end() { return m_data.end(); }
352  auto cend() const { return m_data.cend(); }
353 
355  auto rbegin() { return m_data.rbegin(); }
357  auto crbegin() const { return m_data.crbegin(); }
359  auto rend() { return m_data.rend(); }
361  auto crend() const { return m_data.crend(); }
362 
365  Array<T> &operator+=(const Array<T> &other);
366  Array<T> &operator-=(const Array<T> &other);
367  Array<T> &operator*=(const Array<T> &other);
368  Array<T> &operator/=(const Array<T> &other);
369 
371 
374  Array<T> &operator+=(const T &t);
375  Array<T> &operator-=(const T &t);
376  Array<T> &operator*=(const T &t);
377  Array<T> &operator/=(const T &t);
378 
379 private:
380  // Calculates the cumulative_sizes array
381  std::vector<std::size_t> calc_cumulative_size() const;
382 
383  // Unchecked index calculation
384  template <typename... Args>
385  std::size_t unchecked_index(std::size_t first, Args... rest) const;
386 
387  // Helper for unchecked index calculation
388  template <typename... Args>
389  std::size_t unchecked_index_impl(std::size_t dim, std::size_t first,
390  Args... rest) const;
391 
392  // Checked index calculation
393  template <typename... Args>
394  std::size_t checked_index(std::size_t first, Args... rest) const;
395 
396  // Helper for checked index calculation
397  template <typename... Args>
398  std::size_t checked_index_impl(std::size_t dim, std::size_t first,
399  Args... rest) const;
400 };
401 
402 //==============================================================================
403 // Implementations
404 //==============================================================================
405 template <typename T>
406 std::vector<std::size_t> Array<T>::calc_cumulative_size() const {
407  std::vector<std::size_t> cumulative_size;
408  cumulative_size.reserve(m_sizes.size());
409  for (std::size_t i = 0; i < m_Ndim; ++i) {
410  cumulative_size.push_back(std::accumulate(m_sizes.cbegin() + long(i) + 1,
411  m_sizes.cend(), 1ul,
412  std::multiplies<std::size_t>()));
413  }
414  return cumulative_size;
415 }
416 
417 template <typename T>
418 template <typename... Args>
419 Array<T>::Array(std::size_t first, Args... rest)
420  : m_sizes({first, static_cast<std::size_t>(rest)...}),
421  m_Ndim(m_sizes.size()),
422  m_cumulative_sizes(calc_cumulative_size()),
423  m_total_size(std::accumulate(m_sizes.cbegin(), m_sizes.cend(), 1ul,
424  std::multiplies<std::size_t>())),
425  m_data(m_total_size) {}
426 
427 template <typename T>
428 template <typename... Args>
429 void Array<T>::resize(std::size_t first, Args... rest) {
430  m_sizes = std::vector{first, static_cast<std::size_t>(rest)...};
431  m_Ndim = m_sizes.size();
432  m_cumulative_sizes = calc_cumulative_size();
433  m_total_size = std::accumulate(m_sizes.cbegin(), m_sizes.cend(), 1ul,
434  std::multiplies<std::size_t>());
435  m_data.resize(m_total_size);
436 }
437 
438 template <typename T>
439 template <typename... Args>
440 std::size_t Array<T>::unchecked_index(std::size_t first, Args... rest) const {
441  return unchecked_index_impl(0, first, rest...);
442 }
443 
444 template <typename T>
445 template <typename... Args>
446 std::size_t Array<T>::unchecked_index_impl(std::size_t dim, std::size_t first,
447  Args... rest) const {
448  if constexpr (sizeof...(rest) == 0) {
449  return first * m_cumulative_sizes[dim];
450  } else {
451  return first * m_cumulative_sizes[dim] +
452  unchecked_index_impl(dim + 1, rest...);
453  }
454 }
455 
456 template <typename T>
457 template <typename... Args>
458 std::size_t Array<T>::checked_index(std::size_t first, Args... rest) const {
459  assert(sizeof...(rest) + 1 == m_Ndim &&
460  "Number of arguments must match number of dimensions");
461  return checked_index_impl(0, first, rest...);
462 }
463 
464 template <typename T>
465 template <typename... Args>
466 std::size_t Array<T>::checked_index_impl(std::size_t dim, std::size_t first,
467  Args... rest) const {
468  assert(first < m_sizes[dim]);
469  if constexpr (sizeof...(rest) == 0) {
470  return first * m_cumulative_sizes[dim];
471  } else {
472  return first * m_cumulative_sizes[dim] +
473  checked_index_impl(dim + 1, rest...);
474  }
475 }
476 
477 template <typename T>
478 template <typename... Args>
479 T &Array<T>::at(std::size_t first, Args... rest) {
480  return m_data.at(checked_index(first, rest...));
481 }
482 
483 template <typename T>
484 template <typename... Args>
485 T Array<T>::at(std::size_t first, Args... rest) const {
486  return m_data.at(checked_index(first, rest...));
487 }
488 
489 template <typename T>
490 template <typename... Args>
491 T &Array<T>::operator()(std::size_t first, Args... rest) {
492  return m_data[unchecked_index(first, rest...)];
493 }
494 
495 template <typename T>
496 template <typename... Args>
497 T Array<T>::operator()(std::size_t first, Args... rest) const {
498  return m_data[unchecked_index(first, rest...)];
499 }
500 
501 template <typename T>
502 Array<T> &Array<T>::operator+=(const Array<T> &other) {
503  assert(m_sizes == other.m_sizes &&
504  "Arithmetic only defined for equal-dimension arrays");
505  for (std::size_t i = 0; i < m_data.size(); ++i) {
506  this->m_data[i] += other.m_data[i];
507  }
508  return *this;
509 }
510 
511 template <typename T>
512 Array<T> &Array<T>::operator-=(const Array<T> &other) {
513  assert(m_sizes == other.m_sizes &&
514  "Arithmetic only defined for equal-dimension arrays");
515  for (std::size_t i = 0; i < m_data.size(); ++i) {
516  this->m_data[i] -= other.m_data[i];
517  }
518  return *this;
519 }
520 
521 template <typename T>
522 Array<T> &Array<T>::operator*=(const Array<T> &other) {
523  assert(m_sizes == other.m_sizes &&
524  "Arithmetic only defined for equal-dimension arrays");
525  for (std::size_t i = 0; i < m_data.size(); ++i) {
526  this->m_data[i] *= other.m_data[i];
527  }
528  return *this;
529 }
530 
531 template <typename T>
532 Array<T> &Array<T>::operator/=(const Array<T> &other) {
533  assert(m_sizes == other.m_sizes &&
534  "Arithmetic only defined for equal-dimension arrays");
535  for (std::size_t i = 0; i < m_data.size(); ++i) {
536  this->m_data[i] /= other.m_data[i];
537  }
538  return *this;
539 }
540 
541 template <typename T>
542 Array<T> &Array<T>::operator+=(const T &t) {
543  for (std::size_t i = 0; i < m_data.size(); ++i) {
544  this->m_data[i] += t;
545  }
546  return *this;
547 }
548 
549 template <typename T>
550 Array<T> &Array<T>::operator-=(const T &t) {
551  for (std::size_t i = 0; i < m_data.size(); ++i) {
552  this->m_data[i] -= t;
553  }
554  return *this;
555 }
556 
557 template <typename T>
558 Array<T> &Array<T>::operator*=(const T &t) {
559  for (std::size_t i = 0; i < m_data.size(); ++i) {
560  this->m_data[i] *= t;
561  }
562  return *this;
563 }
564 
565 template <typename T>
566 Array<T> &Array<T>::operator/=(const T &t) {
567  for (std::size_t i = 0; i < m_data.size(); ++i) {
568  this->m_data[i] /= t;
569  }
570  return *this;
571 }
572 
573 template <typename T>
574 ArrayView<T> Array<T>::row(std::size_t i) {
575  assert(m_Ndim == 2 && "Row only defined for 2D array");
576  return ArrayView(m_data.data() + i * m_sizes[1], m_sizes[1]);
577 }
578 template <typename T>
579 ArrayView<T> Array<T>::col(std::size_t j) {
580  assert(m_Ndim == 2 && "Col only defined for 2D array");
581  return ArrayView(m_data.data() + j, m_sizes[0], m_sizes[1]);
582 }
583 template <typename T>
584 ArrayView<const T> Array<T>::row(std::size_t i) const {
585  assert(m_Ndim == 2 && "Row only defined for 2D array");
586  return ArrayView(m_data.data() + i * m_sizes[1], m_sizes[1]);
587 }
588 template <typename T>
589 ArrayView<const T> Array<T>::col(std::size_t j) const {
590  assert(m_Ndim == 2 && "Col only defined for 2D array");
591  return ArrayView<const T>(m_data.data() + j, m_sizes[0], m_sizes[1]);
592 }
593 
594 } // namespace qip
A view onto a 1D array; used for rows/collumns of ND array. Can have a stride.
Definition: Array.hpp:144
auto begin()
Iterator to the beginning.
Definition: Array.hpp:182
std::vector< T > vector()
Returns a copy of the array as a std::vector.
Definition: Array.hpp:210
Helper template for comparisons. Derive from this to provide !=,>,<=,>=, given == and <.
Definition: Template.hpp:32
A constant iterator accounting for a stride.
Definition: Array.hpp:79
An iterator accounting for a stride.
Definition: Array.hpp:18
std::vector< T > & operator+=(std::vector< T > &a, const std::vector< T > &b)
Provide addition of two vectors:
Definition: Vector.hpp:454
qip library: A collection of useful functions
Definition: Array.hpp:8
auto NDrange(std::size_t first, Args... rest)
Variadic array of all possible indexes.
Definition: Array.hpp:256
T product(T first, Args... rest)
Variadic product - helper function.
Definition: Array.hpp:223