Alexandria  2.16
Please provide a description of the project.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
NdArray.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2012-2020 Euclid Science Ground Segment
3  *
4  * This library is free software; you can redistribute it and/or modify it under
5  * the terms of the GNU Lesser General Public License as published by the Free
6  * Software Foundation; either version 3.0 of the License, or (at your option)
7  * any later version.
8  *
9  * This library is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12  * details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
25 #ifndef ALEXANDRIA_NDARRAY_H
26 #define ALEXANDRIA_NDARRAY_H
27 
28 #include <iostream>
29 #include <numeric>
30 #include <stdexcept>
31 #include <vector>
32 #include <cassert>
33 
34 namespace Euclid {
35 namespace NdArray {
36 
44 template<typename T, template<class...> class Container = std::vector>
45 class NdArray {
46 public:
47  typedef typename Container<T>::iterator iterator;
48  typedef typename Container<T>::const_iterator const_iterator;
50 
54  virtual ~NdArray() = default;
55 
62  explicit NdArray(const std::vector<size_t> &shape) : m_shape{shape}, m_container(
65  }
66 
77  NdArray(const std::vector<size_t> &shape, const Container<T> &data) : m_shape{shape}, m_container{data} {
78  size_t expected_size = std::accumulate(m_shape.begin(), m_shape.end(), 1, std::multiplies<size_t>());
79  if (expected_size != m_container.size()) {
80  throw std::invalid_argument("Data size does not match the shape");
81  }
83  }
84 
97  NdArray(const std::vector<size_t> &shape, Container<T> &&data) : m_shape{shape}, m_container{std::move(data)} {
98  size_t expected_size = std::accumulate(m_shape.begin(), m_shape.end(), 1, std::multiplies<size_t>());
99  if (expected_size != m_container.size()) {
100  throw std::invalid_argument("Data size does not match the shape");
101  }
102  update_strides();
103  }
104 
117  template <typename Iterator>
118  NdArray(const std::vector<size_t> &shape, Iterator begin, Iterator end) : m_shape{shape}, m_container{begin, end} {
119  size_t expected_size = std::accumulate(m_shape.begin(), m_shape.end(), 1, std::multiplies<size_t>());
120  if (expected_size != m_container.size()) {
121  throw std::invalid_argument("Data size does not match the shape");
122  }
123  update_strides();
124  }
125 
132  NdArray(const std::initializer_list<size_t> &shape) : NdArray(std::vector<size_t>{shape}) {}
133 
137  NdArray(const self_type&) = default;
138 
142  NdArray(self_type&&) = default;
143 
147  NdArray& operator = (const NdArray&) = default;
148 
154  const std::vector<size_t> shape() const {
155  return m_shape;
156  }
157 
169  size_t new_size = std::accumulate(new_shape.begin(), new_shape.end(), 1, std::multiplies<size_t>());
170  if (new_size != m_container.size()) {
171  throw std::range_error("New shape does not match the number of contained elements");
172  }
173  m_shape = new_shape;
174  update_strides();
175  return *this;
176  }
177 
188  template <typename ...D>
189  self_type& reshape(size_t i, D... rest) {
190  std::vector<size_t> acc{i};
191  return reshape_helper(acc, rest...);
192  }
193 
201  T &at(const std::vector<size_t> &coords) {
202  auto offset = get_offset(coords);
203  return m_container[offset];
204  };
205 
213  const T &at(const std::vector<size_t> &coords) const {
214  auto offset = get_offset(coords);
215  return m_container[offset];
216  };
217 
228  template <typename ...D>
229  T &at(size_t i, D... rest) {
230  std::vector<size_t> acc{i};
231  return at_helper(acc, rest...);
232  }
233 
244  template <typename ...D>
245  const T &at(size_t i, D... rest) const {
246  std::vector<size_t> acc{i};
247  return at_helper(acc, rest...);
248  }
249 
255  return m_container.begin();
256  }
257 
263  return m_container.end();
264  }
265 
271  return m_container.begin();
272  }
273 
278  const_iterator end() const {
279  return m_container.end();
280  }
281 
285  const Container<T>& data() const {
286  return m_container;
287  }
288 
292  size_t size() const {
293  return m_container.size();
294  }
295 
299  bool operator == (const self_type& b) const {
300  return shape() == b.shape() && data() == b.data();
301  }
302 
306  bool operator != (const self_type& b) const {
307  return shape() != b.shape() || data() != b.data();
308  }
309 
310 private:
312  Container<T> m_container;
313 
319  size_t get_offset(const std::vector<size_t> &coords) const {
320  if (coords.size() != m_shape.size()) {
321  throw std::out_of_range(
322  "Invalid number of coordinates, got " + std::to_string(coords.size()) + ", expected " + std::to_string(m_shape.size())
323  );
324  }
325 
326  size_t offset = 0;
327  for (size_t i = 0; i < coords.size(); ++i) {
328  if (coords[i] >= m_shape[i]) {
329  throw std::out_of_range(
330  std::to_string(coords[i]) + " >= " + std::to_string(m_shape[i]) + " for axis " + std::to_string(i)
331  );
332  }
333  offset += coords[i] * m_stride_size[i];
334  }
335 
336  assert(offset < m_container.size());
337  return offset;
338  };
339 
343  void update_strides() {
345 
346  size_t acc = 1;
347  for (size_t i = m_stride_size.size(); i > 0; --i) {
348  m_stride_size[i - 1] = acc;
349  acc *= m_shape[i - 1];
350  }
351  }
352 
356  template <typename ...D>
357  T &at_helper(std::vector<size_t> &acc, size_t i, D... rest) {
358  acc.push_back(i);
359  return at_helper(acc, rest...);
360  }
361 
366  return at(acc);
367  }
368 
372  template <typename ...D>
373  const T &at_helper(std::vector<size_t> &acc, size_t i, D... rest) const {
374  acc.push_back(i);
375  return at_helper(acc, rest...);
376  }
377 
381  const T &at_helper(std::vector<size_t> &acc) const {
382  return at(acc);
383  }
384 
385  template <typename ...D>
386  self_type& reshape_helper(std::vector<size_t>& acc, size_t i, D... rest) {
387  acc.push_back(i);
388  return reshape_helper(acc, rest...);
389  }
390 
392  return reshape(acc);
393  }
394 };
395 
399 template<typename T, template<class...> class Container>
400 std::ostream& operator << (std::ostream &out, const NdArray<T, Container> &ndarray) {
401  size_t i;
402  auto shape = ndarray.shape();
403 
404  if (ndarray.size()) {
405  out << "<";
406  for (i = 0; i < shape.size() - 1; ++i) {
407  out << shape[i] << ",";
408  }
409  out << shape[i] << ">";
410  for (i = 0; i < ndarray.size() - 1; ++i) {
411  out << ndarray.data()[i] << ",";
412  }
413  out << ndarray.data()[i];
414  }
415  return out;
416 }
417 
418 } // end NdArray
419 } // end Euclid
420 
421 #endif // ALEXANDRIA_NDARRAY_H
T & at_helper(std::vector< size_t > &acc)
Definition: NdArray.h:365
const_iterator begin() const
Definition: NdArray.h:270
size_t size() const
Definition: NdArray.h:292
self_type & reshape_helper(std::vector< size_t > &acc)
Definition: NdArray.h:391
Container< T > m_container
Definition: NdArray.h:312
T to_string(T...args)
const_iterator end() const
Definition: NdArray.h:278
T & at(size_t i, D...rest)
Definition: NdArray.h:229
self_type & reshape_helper(std::vector< size_t > &acc, size_t i, D...rest)
Definition: NdArray.h:386
NdArray(const std::vector< size_t > &shape, Iterator begin, Iterator end)
Definition: NdArray.h:118
T end(T...args)
bool operator==(const self_type &b) const
Definition: NdArray.h:299
T & at_helper(std::vector< size_t > &acc, size_t i, D...rest)
Definition: NdArray.h:357
const T & at_helper(std::vector< size_t > &acc) const
Definition: NdArray.h:381
T & at(const std::vector< size_t > &coords)
Definition: NdArray.h:201
const std::vector< size_t > shape() const
Definition: NdArray.h:154
T resize(T...args)
const T & at(size_t i, D...rest) const
Definition: NdArray.h:245
const T & at(const std::vector< size_t > &coords) const
Definition: NdArray.h:213
NdArray & operator=(const NdArray &)=default
std::vector< size_t > m_stride_size
Definition: NdArray.h:311
T push_back(T...args)
NdArray(const std::vector< size_t > &shape)
Definition: NdArray.h:62
std::vector< size_t > m_shape
Definition: NdArray.h:311
NdArray(const std::vector< size_t > &shape, Container< T > &&data)
Definition: NdArray.h:97
T move(T...args)
size_t get_offset(const std::vector< size_t > &coords) const
Definition: NdArray.h:319
T size(T...args)
NdArray(const std::vector< size_t > &shape, const Container< T > &data)
Definition: NdArray.h:77
STL class.
T begin(T...args)
const T & at_helper(std::vector< size_t > &acc, size_t i, D...rest) const
Definition: NdArray.h:373
virtual ~NdArray()=default
const Container< T > & data() const
Definition: NdArray.h:285
NdArray< T, Container > self_type
Definition: NdArray.h:49
self_type & reshape(size_t i, D...rest)
Definition: NdArray.h:189
self_type & reshape(const std::vector< size_t > new_shape)
Definition: NdArray.h:168
T accumulate(T...args)
bool operator!=(const self_type &b) const
Definition: NdArray.h:306
Container< T >::iterator iterator
Definition: NdArray.h:47
STL class.
STL class.
Container< T >::const_iterator const_iterator
Definition: NdArray.h:48
NdArray(const std::initializer_list< size_t > &shape)
Definition: NdArray.h:132