https://mooseframework.inl.gov
MultiIndex.h
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #pragma once
11 
12 #include "Moose.h"
13 #include "MooseError.h"
14 #include "DataIO.h"
15 #include <algorithm>
16 
21 template <class T>
23 {
24 public:
26  template <bool is_const = false>
27  class const_noconst_iterator;
28 
30  typedef T value_type;
31  typedef std::vector<unsigned int> size_type;
32  using iterator = const_noconst_iterator<false>;
33  using const_iterator = const_noconst_iterator<true>;
35 
37  MultiIndex(const size_type & shape);
38 
40  MultiIndex(const size_type & shape, const std::vector<T> & data);
41 
43  T & operator()(const size_type & indices);
44  const T & operator()(const size_type & indices) const;
46 
48  T & operator[](unsigned int j) { return _data[j]; }
49  const T & operator[](unsigned int j) const { return _data[j]; }
50  T & at(unsigned int j) { return _data.at(j); }
52 
54  MultiIndex<T> slice(unsigned int dimension, unsigned int index) const;
55  MultiIndex<T> slice(size_type dimension, size_type index) const;
57 
59  const size_type & size() const { return _shape; }
60 
62  unsigned int dim() const { return _shape.size(); }
63 
65  unsigned int nEntries() const { return _nentries; }
66 
68  std::vector<T> getRawData() const { return _data; }
69 
71  void resize(const size_type & shape);
72 
74  void assign(const size_type & shape, T value);
75 
77  unsigned int stride(unsigned int j) const;
78  const size_type & stride() const { return _stride; }
80 
82  void dataStore(std::ostream & stream, void * context);
83  void dataLoad(std::istream & stream, void * context);
85 
87  iterator begin() { return const_noconst_iterator<false>(*this, 0); }
88  iterator end() { return const_noconst_iterator<false>(*this, _nentries); }
89  const_iterator begin() const { return const_noconst_iterator<true>(*this, 0); }
90  const_iterator end() const { return const_noconst_iterator<true>(*this, _nentries); }
92 
94  unsigned int flatIndex(const size_type & indices) const;
95 
96 protected:
98  void findIndexVector(unsigned int flat_index, size_type & indices) const;
99 
102 
104  unsigned int _dim;
105 
107  unsigned int _nentries;
108 
111 
113  std::vector<T> _data;
114 
115 private:
117  void buildAccumulatedShape();
118 
120  void reshape(const size_type & shape);
121 };
122 
126 template <class T>
127 template <bool is_const>
128 class MultiIndex<T>::const_noconst_iterator
129 {
130 public:
131  typedef typename std::conditional<is_const, const MultiIndex<T> &, MultiIndex<T> &>::type
133 
134  const_noconst_iterator(reference_type multi_index, unsigned int position)
135  : _multi_index(multi_index), _flat_index(position), _shape(multi_index.size())
136  {
137  _multi_index.findIndexVector(position, _indices);
138  }
139 
140  // Simple data getters
141  unsigned int flatIndex() const { return _flat_index; }
142  reference_type getMultiIndexObject() const { return _multi_index; }
143 
144  // assignment =
146  {
147  _multi_index = other.getMultiIndexObject();
148  _flat_index = other.flatIndex();
149  _multi_index.findIndexVector(_flat_index, _indices);
150  return *this;
151  }
152 
153  // Compiler generated copy constructor
155 
156  // prefix ++
158  {
159  ++_flat_index;
160  // increment indices
161  for (unsigned int j = 0; j < _indices.size(); ++j)
162  {
163  _indices[_indices.size() - j - 1] =
164  (_indices[_indices.size() - j - 1] + 1) % _shape[_indices.size() - j - 1];
165  if (_indices[_indices.size() - j - 1] != 0)
166  break;
167  }
168  return *this;
169  }
170 
171  // postfix ++
173  {
174  const_noconst_iterator clone(*this);
175  ++_flat_index;
176  // increment indices
177  for (unsigned int j = 0; j < _indices.size(); ++j)
178  {
179  _indices[_indices.size() - j - 1] =
180  (_indices[_indices.size() - j - 1] + 1) % _shape[_indices.size() - j - 1];
181  if (_indices[_indices.size() - j - 1] != 0)
182  break;
183  }
184  return clone;
185  }
186 
187  // prefix --
189  {
190  --_flat_index;
191  // decrement indices
192  for (unsigned int j = 0; j < _indices.size(); ++j)
193  {
194  if (_indices[_indices.size() - j - 1] == 0)
195  _indices[_indices.size() - j - 1] = _shape[_indices.size() - j - 1] - 1;
196  else
197  {
198  --_indices[_indices.size() - j - 1];
199  break;
200  }
201  }
202  return *this;
203  }
204 
205  // postfix --
207  {
208  const_noconst_iterator clone(*this);
209  --_flat_index;
210  // decrement indices
211  for (unsigned int j = 0; j < _indices.size(); ++j)
212  {
213  if (_indices[_indices.size() - j - 1] == 0)
214  _indices[_indices.size() - j - 1] = _shape[_indices.size() - j - 1] - 1;
215  else
216  {
217  --_indices[_indices.size() - j - 1];
218  break;
219  }
220  }
221  return clone;
222  }
223 
225  bool operator==(const const_noconst_iterator & other) const
226  {
227  return _flat_index == other.flatIndex() && &_multi_index == &other.getMultiIndexObject();
228  }
229  bool operator!=(const const_noconst_iterator & other) const { return !(*this == other); }
230 
232  std::pair<const size_type &, T &> operator*()
233  {
234  return std::pair<const size_type &, T &>(_indices, _multi_index._data[_flat_index]);
235  }
236  std::pair<const size_type &, const T &> operator*() const
237  {
238  return std::pair<const size_type &, const T &>(_indices, _multi_index._data[_flat_index]);
239  }
240 
241 protected:
243  unsigned int _flat_index;
246 };
247 
248 template <class T>
250 {
251  reshape(shape);
252  _data.resize(_nentries);
253 }
254 
255 template <class T>
256 MultiIndex<T>::MultiIndex(const size_type & shape, const std::vector<T> & data)
257 {
258  reshape(shape);
259 
260  if (data.size() != _nentries)
261  mooseError("shape and data arguments' sizes are inconsistent.");
262  _data = data;
263 }
264 
265 template <class T>
266 T &
268 {
269  return _data[flatIndex(indices)];
270 }
271 
272 template <class T>
273 const T &
274 MultiIndex<T>::operator()(const size_type & indices) const
275 {
276  return _data[flatIndex(indices)];
277 }
278 
279 template <class T>
281 MultiIndex<T>::slice(unsigned int dimension, unsigned int index) const
282 {
283  size_type dim(1, dimension);
284  size_type ind(1, index);
285  return slice(dim, ind);
286 }
287 
288 template <class T>
290 MultiIndex<T>::slice(size_type dimension, size_type index) const
291 {
292  // input checks
293  if (dimension.size() != index.size())
294  mooseError("dimension and index must have the same size.");
295  if (dimension.size() > _dim - 1)
296  mooseError("The result of slice must be at least of dimension 1.");
297 
298 #if DEBUG
299  for (unsigned int d = 0; d < dimension.size(); ++d)
300  {
301  if (dimension[d] >= _dim)
302  mooseError("dimension is set to ", dimension[d], " which is larger than _dim ", _dim);
303  if (index[d] >= _shape[dimension[d]])
304  mooseError("index= ",
305  index[d],
306  " at dimension=",
307  dimension[d],
308  " is larger than ",
309  _shape[dimension[d]]);
310  }
311 #endif // DEBUG
312 
313  // create a MultiIndex object with new dim = dim - dimension.size()
314  size_type new_shape;
315  for (unsigned int j = 0; j < _dim; ++j)
316  if (std::find(dimension.begin(), dimension.end(), j) == dimension.end())
317  new_shape.push_back(_shape[j]);
318  MultiIndex<T> multi_index = MultiIndex(new_shape);
319 
320  // copy the data
321  MultiIndex<T>::iterator it = multi_index.begin();
322  for (unsigned int n = 0; n < _nentries; ++n)
323  {
324  size_type indices;
325  findIndexVector(n, indices);
326  bool addTo = true;
327  for (unsigned int d = 0; d < dimension.size(); ++d)
328  if (indices[dimension[d]] != index[d])
329  {
330  addTo = false;
331  break;
332  }
333 
334  if (addTo)
335  {
336  (*it).second = _data[n];
337  ++it;
338  }
339  }
340  return multi_index;
341 }
342 
343 template <class T>
344 void
346 {
347  if (shape.size() != _shape.size())
348  mooseError("resize cannot change the dimensionality of MultiIndex.");
349 
350  // first copy the old shape and data
351  size_type old_shape = _shape;
352  size_type old_stride = _stride;
353  std::vector<T> old_data = _data;
354 
355  // reset _shape & recompute meta data
356  reshape(shape);
357 
358  // fill in _data to all possible indices
359  _data.assign(_nentries, T(0));
360  for (unsigned int j = 0; j < _nentries; ++j)
361  {
362  size_type indices;
363  findIndexVector(j, indices);
364 
365  // check if indices existed in the old version of _data
366  bool existed_in_old = true;
367  for (unsigned int d = 0; d < _dim; ++d)
368  if (indices[d] >= old_shape[d])
369  {
370  existed_in_old = false;
371  break;
372  }
373 
374  // find the corresponding old_j
375  if (existed_in_old)
376  {
377  unsigned int old_j = 0;
378  for (unsigned int d = 0; d < _dim; ++d)
379  old_j += indices[d] * old_stride[d];
380 
381  // finally set the data entry
382  _data[j] = old_data[old_j];
383  }
384  }
385 }
386 
387 template <class T>
388 unsigned int
389 MultiIndex<T>::stride(unsigned int j) const
390 {
391  mooseAssert(j < _dim, "Dimension is" << _dim << ", stride(j) called with j = " << j);
392  return _stride[j];
393 }
394 
395 template <class T>
396 void
397 MultiIndex<T>::assign(const size_type & shape, T value)
398 {
399  reshape(shape);
400  _data.assign(_nentries, value);
401 }
402 
403 template <class T>
404 void
405 MultiIndex<T>::dataStore(std::ostream & stream, void * context)
406 {
407  ::dataStore(stream, _shape, context);
408  ::dataStore(stream, _data, context);
409 }
410 
411 template <class T>
412 void
413 MultiIndex<T>::dataLoad(std::istream & stream, void * context)
414 {
415  ::dataLoad(stream, _shape, context);
416  ::dataLoad(stream, _data, context);
417  _dim = _shape.size();
418  _nentries = _data.size();
419  buildAccumulatedShape();
420 }
421 
422 template <class T>
423 void
424 MultiIndex<T>::findIndexVector(unsigned int flat_index, MultiIndex<T>::size_type & indices) const
425 {
426  indices.resize(_dim);
427  for (unsigned int d = 0; d < _dim; ++d)
428  {
429  unsigned int i = flat_index / _stride[d];
430  indices[d] = i;
431  flat_index -= i * _stride[d];
432  }
433 }
434 
435 // compute the accumulated shapes as:
436 // as[0] = I_1 * I_2 ...* I_{M}, as[1] = I_2 * I_3 ...* I_{M} ...
437 template <class T>
438 void
440 {
441  // TODO: simplify - this is needlessly complicated - can be done in a single loop
442  _stride.resize(_dim);
443  for (unsigned int d = 0; d < _dim; ++d)
444  {
445  unsigned int k = 1;
446  for (unsigned int j = d + 1; j < _dim; ++j)
447  k *= _shape[j];
448  _stride[d] = k;
449  }
450 }
451 
452 template <class T>
453 void
455 {
456  if (shape.size() == 0)
457  {
458  _shape = {};
459  _dim = 0;
460  _stride = {};
461  _nentries = 0;
462  return;
463  }
464 
465  _shape = shape;
466  _dim = shape.size();
467 
468  _nentries = 1;
469  for (unsigned int d = 0; d < _dim; ++d)
470  _nentries *= _shape[d];
471 
472  buildAccumulatedShape();
473 }
474 
475 template <class T>
476 unsigned int
477 MultiIndex<T>::flatIndex(const size_type & indices) const
478 {
479  mooseAssert(indices.size() == _dim,
480  "Indices vector has wrong size. size=" << indices.size() << " vs. dim=" << _dim);
481 #if DEBUG
482  for (unsigned int j = 0; j < indices.size(); ++j)
483  if (indices[j] >= _shape[j])
484  mooseError("Indices vector at entry ", j, " is ", indices[j], " vs. shape ", _shape[j]);
485 #endif // DEBUG
486 
487  // implement the index
488  // index = i_M + i_{M-1} * I_M + i_{M-1} * I_M * I_{M-1} ...
489  unsigned int index = 0;
490  for (unsigned int d = 0; d < _dim; ++d)
491  index += indices[d] * _stride[d];
492 
493  return index;
494 }
495 
496 template <class T>
497 void
498 dataStore(std::ostream & stream, MultiIndex<T> & mi, void * context)
499 {
500  mi.dataStore(stream, context);
501 }
502 
503 template <class T>
504 void
505 dataLoad(std::istream & stream, MultiIndex<T> & mi, void * context)
506 {
507  mi.dataLoad(stream, context);
508 }
iterator begin()
iterators for begin and end of this container
Definition: MultiIndex.h:87
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:302
unsigned int nEntries() const
total number of values stored in the container
Definition: MultiIndex.h:65
const_noconst_iterator< true > const_iterator
Definition: MultiIndex.h:33
const_noconst_iterator(reference_type multi_index, unsigned int position)
Definition: MultiIndex.h:134
const T & operator[](unsigned int j) const
Definition: MultiIndex.h:49
std::conditional< is_const, const MultiIndex< T > &, MultiIndex< T > & >::type reference_type
Definition: MultiIndex.h:132
T & operator()(const size_type &indices)
element access operators
Definition: MultiIndex.h:267
static constexpr std::size_t dim
This is the dimension of all vector and tensor datastructures used in MOOSE.
Definition: Moose.h:153
void dataLoad(std::istream &stream, MultiIndex< T > &mi, void *context)
Definition: MultiIndex.h:505
std::vector< T > _data
the data unrolled into a vector
Definition: MultiIndex.h:113
unsigned int _dim
the number of dimensions TODO: get rid of this -> _shape.size()
Definition: MultiIndex.h:104
unsigned int dim() const
dimension of the container
Definition: MultiIndex.h:62
unsigned int _nentries
the number of entries TODO: get rid of this -> _data.size()
Definition: MultiIndex.h:107
void resize(const size_type &shape)
Resize container. Must keep dimensionality constant.
Definition: MultiIndex.h:345
const_noconst_iterator< false > iterator
Definition: MultiIndex.h:32
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
MultiIndex container iterator.
Definition: MultiIndex.h:27
const size_type & size() const
container size as dim dimensional vector
Definition: MultiIndex.h:59
MultiIndex(const size_type &shape)
construct zero initialized container of a given shape
Definition: MultiIndex.h:249
void reshape(const size_type &shape)
change the container shape and reset meta data
Definition: MultiIndex.h:454
std::pair< const size_type &, const T & > operator*() const
Definition: MultiIndex.h:236
const_iterator begin() const
Definition: MultiIndex.h:89
const size_type & stride() const
Definition: MultiIndex.h:78
reference_type getMultiIndexObject() const
Definition: MultiIndex.h:142
const_iterator end() const
Definition: MultiIndex.h:90
std::vector< unsigned int > size_type
Definition: MultiIndex.h:31
const_noconst_iterator & operator++()
Definition: MultiIndex.h:157
const_noconst_iterator & operator--(int)
Definition: MultiIndex.h:206
const_noconst_iterator & operator=(const const_noconst_iterator &other)
Definition: MultiIndex.h:145
void findIndexVector(unsigned int flat_index, size_type &indices) const
given a flat index computes the vector of indices i0, i1, i2, ...
Definition: MultiIndex.h:424
void buildAccumulatedShape()
build accumulated shape vector for flat index calculation
Definition: MultiIndex.h:439
const_noconst_iterator & operator++(int)
Definition: MultiIndex.h:172
void assign(const size_type &shape, T value)
Reshape container arbitrarily and initialize with value.
Definition: MultiIndex.h:397
std::pair< const size_type &, T & > operator*()
dereferencing operator
Definition: MultiIndex.h:232
unsigned int flatIndex(const size_type &indices) const
compute the flat index for a size_type index
Definition: MultiIndex.h:477
void dataStore(std::ostream &stream, MultiIndex< T > &mi, void *context)
Definition: MultiIndex.h:498
iterator end()
Definition: MultiIndex.h:88
const_noconst_iterator & operator--()
Definition: MultiIndex.h:188
T & at(unsigned int j)
Definition: MultiIndex.h:50
MultiIndex< T > slice(unsigned int dimension, unsigned int index) const
accesses a slice of the multi index object
Definition: MultiIndex.h:281
bool operator!=(const const_noconst_iterator &other) const
Definition: MultiIndex.h:229
T & operator[](unsigned int j)
direct data access via bracket operator
Definition: MultiIndex.h:48
unsigned int flatIndex() const
Definition: MultiIndex.h:141
bool operator==(const const_noconst_iterator &other) const
to be equal both iterators must hold a reference to the same MultiIndexObject and be at the same _fla...
Definition: MultiIndex.h:225
std::vector< T > getRawData() const
get the raw data vector
Definition: MultiIndex.h:68
void dataLoad(std::istream &stream, void *context)
Definition: MultiIndex.h:413
size_type _shape
the size along each index
Definition: MultiIndex.h:101
T value_type
container related types and categories
Definition: MultiIndex.h:27
size_type _stride
stride for each index, e.g. if you know {i, j, k} -> flat_index, {i, j + 1, k} = flat_index + stride[...
Definition: MultiIndex.h:110
Implements a container class for multi-indexed objects with an arbitrary number of indices...
Definition: MultiIndex.h:22
void dataStore(std::ostream &stream, void *context)
Implement loadHelper and storeHelper for easier data (de)serialization.
Definition: MultiIndex.h:405