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 };
209 
210 //==============================================================================
211 
213 template <typename T, typename... Args>
214 T product(T first, Args... rest) {
215  if constexpr (sizeof...(rest) == 0) {
216  return first;
217  } else {
218  return first * product(rest...);
219  }
220 }
221 
222 template <std::size_t N>
223 void NDrange_impl(std::vector<std::array<std::size_t, N>> &result,
224  std::array<std::size_t, N> &current,
225  const std::array<std::size_t, N> &maxValues,
226  std::size_t index) {
227  if (index == N) {
228  result.push_back(current);
229  return;
230  }
231 
232  for (std::size_t i = 0; i < maxValues[index]; ++i) {
233  current[index] = i;
234  NDrange_impl<N>(result, current, maxValues, index + 1);
235  }
236 }
237 
239 
246 template <typename... Args>
247 auto NDrange(std::size_t first, Args... rest) {
248  constexpr std::size_t N = sizeof...(rest) + 1;
249 
250  const std::array<std::size_t, N> maxValues = {
251  first, static_cast<std::size_t>(rest)...};
252 
253  std::vector<std::array<std::size_t, N>> result;
254  result.reserve(product(first, static_cast<std::size_t>(rest)...));
255 
256  std::array<std::size_t, N> current = {0};
257 
258  NDrange_impl<N>(result, current, maxValues, 0);
259  return result;
260 }
261 
262 //==============================================================================
263 template <typename T = double>
264 class Array : public Arithmetic<Array<T>>, Arithmetic2<Array<T>, T> {
265 
266 private:
267  // List of sizes for each array dimension
268  std::vector<std::size_t> m_sizes;
269  // Number of array dimensions
270  std::size_t m_Ndim;
271  // Cumulative sizes (used to index into data)
272  std::vector<std::size_t> m_cumulative_sizes;
273  // Total number of elements
274  std::size_t m_total_size;
275  // Raw data
276  std::vector<T> m_data;
277 
278 public:
281  template <typename... Args>
282  Array(std::size_t first, Args... rest);
283 
285  template <typename... Args>
286  void resize(std::size_t first, Args... rest);
287 
289  std::size_t size() const { return m_total_size; }
290 
292  std::size_t size(std::size_t dim) const { return m_sizes.at(dim); }
293 
295  std::size_t dimensions() const { return m_Ndim; }
296 
298  const std::vector<std::size_t> &shape() const { return m_sizes; }
299 
301  template <typename... Args>
302  T &at(std::size_t first, Args... rest);
303 
305  template <typename... Args>
306  T at(std::size_t first, Args... rest) const;
307 
309  template <typename... Args>
310  T &operator()(std::size_t first, Args... rest);
311 
313  template <typename... Args>
314  T operator()(std::size_t first, Args... rest) const;
315 
318  T *data() { return m_data.data(); }
319  const T *data() const { return m_data.data(); }
320 
322  const std::vector<T> &vector() const { return m_data; }
323 
325  std::size_t rows() const { return size(0); }
327  std::size_t cols() const { return size(1); }
328 
330  ArrayView<T> row(std::size_t i);
331  ArrayView<const T> row(std::size_t i) const;
333  ArrayView<T> col(std::size_t j);
334  ArrayView<const T> col(std::size_t j) const;
335 
337  auto begin() { return m_data.begin(); }
339  auto cbegin() const { return m_data.cbegin(); }
341  auto end() { return m_data.end(); }
343  auto cend() const { return m_data.cend(); }
344 
346  auto rbegin() { return m_data.rbegin(); }
348  auto crbegin() const { return m_data.crbegin(); }
350  auto rend() { return m_data.rend(); }
352  auto crend() const { return m_data.crend(); }
353 
356  Array<T> &operator+=(const Array<T> &other);
357  Array<T> &operator-=(const Array<T> &other);
358  Array<T> &operator*=(const Array<T> &other);
359  Array<T> &operator/=(const Array<T> &other);
360 
362 
365  Array<T> &operator+=(const T &t);
366  Array<T> &operator-=(const T &t);
367  Array<T> &operator*=(const T &t);
368  Array<T> &operator/=(const T &t);
369 
370 private:
371  // Calculates the cumulative_sizes array
372  std::vector<std::size_t> calc_cumulative_size() const;
373 
374  // Unchecked index calculation
375  template <typename... Args>
376  std::size_t unchecked_index(std::size_t first, Args... rest) const;
377 
378  // Helper for unchecked index calculation
379  template <typename... Args>
380  std::size_t unchecked_index_impl(std::size_t dim, std::size_t first,
381  Args... rest) const;
382 
383  // Checked index calculation
384  template <typename... Args>
385  std::size_t checked_index(std::size_t first, Args... rest) const;
386 
387  // Helper for checked index calculation
388  template <typename... Args>
389  std::size_t checked_index_impl(std::size_t dim, std::size_t first,
390  Args... rest) const;
391 };
392 
393 //==============================================================================
394 // Implementations
395 //==============================================================================
396 template <typename T>
397 std::vector<std::size_t> Array<T>::calc_cumulative_size() const {
398  std::vector<std::size_t> cumulative_size;
399  cumulative_size.reserve(m_sizes.size());
400  for (std::size_t i = 0; i < m_Ndim; ++i) {
401  cumulative_size.push_back(std::accumulate(m_sizes.cbegin() + long(i) + 1,
402  m_sizes.cend(), 1ul,
403  std::multiplies<std::size_t>()));
404  }
405  return cumulative_size;
406 }
407 
408 template <typename T>
409 template <typename... Args>
410 Array<T>::Array(std::size_t first, Args... rest)
411  : m_sizes({first, static_cast<std::size_t>(rest)...}),
412  m_Ndim(m_sizes.size()),
413  m_cumulative_sizes(calc_cumulative_size()),
414  m_total_size(std::accumulate(m_sizes.cbegin(), m_sizes.cend(), 1ul,
415  std::multiplies<std::size_t>())),
416  m_data(m_total_size) {}
417 
418 template <typename T>
419 template <typename... Args>
420 void Array<T>::resize(std::size_t first, Args... rest) {
421  m_sizes = std::vector{first, static_cast<std::size_t>(rest)...};
422  m_Ndim = m_sizes.size();
423  m_cumulative_sizes = calc_cumulative_size();
424  m_total_size = std::accumulate(m_sizes.cbegin(), m_sizes.cend(), 1ul,
425  std::multiplies<std::size_t>());
426  m_data.resize(m_total_size);
427 }
428 
429 template <typename T>
430 template <typename... Args>
431 std::size_t Array<T>::unchecked_index(std::size_t first, Args... rest) const {
432  return unchecked_index_impl(0, first, rest...);
433 }
434 
435 template <typename T>
436 template <typename... Args>
437 std::size_t Array<T>::unchecked_index_impl(std::size_t dim, std::size_t first,
438  Args... rest) const {
439  if constexpr (sizeof...(rest) == 0) {
440  return first * m_cumulative_sizes[dim];
441  } else {
442  return first * m_cumulative_sizes[dim] +
443  unchecked_index_impl(dim + 1, rest...);
444  }
445 }
446 
447 template <typename T>
448 template <typename... Args>
449 std::size_t Array<T>::checked_index(std::size_t first, Args... rest) const {
450  assert(sizeof...(rest) + 1 == m_Ndim &&
451  "Number of arguments must match number of dimensions");
452  return checked_index_impl(0, first, rest...);
453 }
454 
455 template <typename T>
456 template <typename... Args>
457 std::size_t Array<T>::checked_index_impl(std::size_t dim, std::size_t first,
458  Args... rest) const {
459  assert(first < m_sizes[dim]);
460  if constexpr (sizeof...(rest) == 0) {
461  return first * m_cumulative_sizes[dim];
462  } else {
463  return first * m_cumulative_sizes[dim] +
464  checked_index_impl(dim + 1, rest...);
465  }
466 }
467 
468 template <typename T>
469 template <typename... Args>
470 T &Array<T>::at(std::size_t first, Args... rest) {
471  return m_data.at(checked_index(first, rest...));
472 }
473 
474 template <typename T>
475 template <typename... Args>
476 T Array<T>::at(std::size_t first, Args... rest) const {
477  return m_data.at(checked_index(first, rest...));
478 }
479 
480 template <typename T>
481 template <typename... Args>
482 T &Array<T>::operator()(std::size_t first, Args... rest) {
483  return m_data[unchecked_index(first, rest...)];
484 }
485 
486 template <typename T>
487 template <typename... Args>
488 T Array<T>::operator()(std::size_t first, Args... rest) const {
489  return m_data[unchecked_index(first, rest...)];
490 }
491 
492 template <typename T>
493 Array<T> &Array<T>::operator+=(const Array<T> &other) {
494  assert(m_sizes == other.m_sizes &&
495  "Arithmetic only defined for equal-dimension arrays");
496  for (std::size_t i = 0; i < m_data.size(); ++i) {
497  this->m_data[i] += other.m_data[i];
498  }
499  return *this;
500 }
501 
502 template <typename T>
503 Array<T> &Array<T>::operator-=(const Array<T> &other) {
504  assert(m_sizes == other.m_sizes &&
505  "Arithmetic only defined for equal-dimension arrays");
506  for (std::size_t i = 0; i < m_data.size(); ++i) {
507  this->m_data[i] -= other.m_data[i];
508  }
509  return *this;
510 }
511 
512 template <typename T>
513 Array<T> &Array<T>::operator*=(const Array<T> &other) {
514  assert(m_sizes == other.m_sizes &&
515  "Arithmetic only defined for equal-dimension arrays");
516  for (std::size_t i = 0; i < m_data.size(); ++i) {
517  this->m_data[i] *= other.m_data[i];
518  }
519  return *this;
520 }
521 
522 template <typename T>
523 Array<T> &Array<T>::operator/=(const Array<T> &other) {
524  assert(m_sizes == other.m_sizes &&
525  "Arithmetic only defined for equal-dimension arrays");
526  for (std::size_t i = 0; i < m_data.size(); ++i) {
527  this->m_data[i] /= other.m_data[i];
528  }
529  return *this;
530 }
531 
532 template <typename T>
533 Array<T> &Array<T>::operator+=(const T &t) {
534  for (std::size_t i = 0; i < m_data.size(); ++i) {
535  this->m_data[i] += t;
536  }
537  return *this;
538 }
539 
540 template <typename T>
541 Array<T> &Array<T>::operator-=(const T &t) {
542  for (std::size_t i = 0; i < m_data.size(); ++i) {
543  this->m_data[i] -= t;
544  }
545  return *this;
546 }
547 
548 template <typename T>
549 Array<T> &Array<T>::operator*=(const T &t) {
550  for (std::size_t i = 0; i < m_data.size(); ++i) {
551  this->m_data[i] *= t;
552  }
553  return *this;
554 }
555 
556 template <typename T>
557 Array<T> &Array<T>::operator/=(const T &t) {
558  for (std::size_t i = 0; i < m_data.size(); ++i) {
559  this->m_data[i] /= t;
560  }
561  return *this;
562 }
563 
564 template <typename T>
565 ArrayView<T> Array<T>::row(std::size_t i) {
566  assert(m_Ndim == 2 && "Row only defined for 2D array");
567  return ArrayView(m_data.data() + i * m_sizes[1], m_sizes[1]);
568 }
569 template <typename T>
570 ArrayView<T> Array<T>::col(std::size_t j) {
571  assert(m_Ndim == 2 && "Col only defined for 2D array");
572  return ArrayView(m_data.data() + j, m_sizes[0], m_sizes[1]);
573 }
574 template <typename T>
575 ArrayView<const T> Array<T>::row(std::size_t i) const {
576  assert(m_Ndim == 2 && "Row only defined for 2D array");
577  return ArrayView(m_data.data() + i * m_sizes[1], m_sizes[1]);
578 }
579 template <typename T>
580 ArrayView<const T> Array<T>::col(std::size_t j) const {
581  assert(m_Ndim == 2 && "Col only defined for 2D array");
582  return ArrayView<const T>(m_data.data() + j, m_sizes[0], m_sizes[1]);
583 }
584 
585 } // 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
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:396
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:247
T product(T first, Args... rest)
Variadic product - helper function.
Definition: Array.hpp:214