faunus
equidistant_table.h
1 #pragma once
2 #include <doctest/doctest.h>
3 #include <vector>
4 #include <cmath>
5 #include <string>
6 #include <limits>
7 
8 namespace Faunus {
9 /*
10  * @brief Table for storing XY data with random access
11  *
12  * Functionality:
13  *
14  * - equidistant x-spacing
15  * - supports centered and lower bound (default) binning via `centerbin`
16  * - internal storage is `std::vector<Ty>`
17  * - lookup complexity: constant
18  * - range-based for loops
19  * - minimum x value and resolution must be given upon construction
20  * - dynamic memory allocation when adding x>=xmin
21  * - stream (de)serialization
22  * - unit tests
23  * - *much* faster, leaner than `std::map`-based Table2D
24  */
25 template <typename Tx = double, typename Ty = Tx, bool centerbin = false> class Equidistant2DTable
26 {
27  private:
28  Tx _dxinv, _xmin;
29  int offset;
30  std::vector<Ty> vec;
31 
32  inline int to_bin(Tx x) const
33  {
34  if constexpr (centerbin) {
35  return (x < 0) ? int(x * _dxinv - 0.5) : int(x * _dxinv + 0.5);
36  }
37  else {
38  return std::floor((x - _xmin) * _dxinv);
39  }
40  }
41 
42  Tx from_bin(int i) const
43  {
44  assert(i >= 0);
45  return i / _dxinv + _xmin;
46  }
47 
48  public:
49  class iterator
50  {
51  public:
53 
54  iterator(T& tbl, size_t i)
55  : tbl(tbl)
56  , i(i)
57  {
58  }
59 
60  iterator operator++()
61  {
62  ++i;
63  return *this;
64  }
65 
66  bool operator!=(const iterator& other) const { return i != other.i; }
67 
68  auto operator*() { return tbl[i]; }
69 
70  protected:
71  T& tbl;
72  size_t i;
73  }; // enable range-based for loops
74 
76  {
77  public:
79 
80  const_iterator(const T& tbl, size_t i)
81  : tbl(tbl)
82  , i(i)
83  {
84  }
85 
86  const_iterator operator++()
87  {
88  ++i;
89  return *this;
90  }
91 
92  bool operator!=(const const_iterator& other) const { return i != other.i; }
93 
94  auto operator*() const { return tbl[i]; }
95 
96  protected:
97  const T& tbl;
98  size_t i;
99  }; // enable range-based for loops
100 
101  iterator begin() { return iterator(*this, 0); }
102 
103  iterator end() { return iterator(*this, size()); }
104 
105  const_iterator begin() const { return const_iterator(*this, 0); }
106 
107  const_iterator end() const { return const_iterator(*this, size()); }
108 
109  Equidistant2DTable() = default;
110 
117  Equidistant2DTable(Tx dx, Tx xmin, Tx xmax = std::numeric_limits<Tx>::infinity())
118  {
119  setResolution(dx, xmin, xmax);
120  }
121 
122  void setResolution(Tx dx, Tx xmin, Tx xmax = std::numeric_limits<Tx>::infinity())
123  {
124  _xmin = xmin;
125  _dxinv = 1 / dx;
126  offset = -to_bin(_xmin);
127  if (xmax < std::numeric_limits<Tx>::infinity()) {
128  vec.reserve(to_bin(xmax) + offset);
129  }
130  }
131 
132  double sumy() const
133  {
134  double sum = 0;
135  for (auto& i : vec) {
136  sum += double(i);
137  }
138  return sum;
139  }
140 
141  const std::vector<Ty>& yvec() const { return vec; }
142 
143  std::vector<Tx> xvec() const
144  {
145  std::vector<Tx> v;
146  v.reserve(vec.size());
147  for (size_t i = 0; i < vec.size(); i++) {
148  v.push_back(from_bin(i));
149  }
150  return v;
151  }
152 
153  void clear() { vec.clear(); }
154 
155  size_t size() const { return vec.size(); }
156 
157  bool empty() const { return vec.empty(); }
158 
159  Tx dx() const { return 1 / _dxinv; }
160 
161  Tx xmin() const { return _xmin; }
162 
163  Tx xmax() const
164  {
165  return (vec.empty()) ? _xmin : from_bin(vec.size() - 1);
166  }
167 
168  Ty& operator()(Tx x)
169  {
170  assert(x >= _xmin);
171  int i = to_bin(x) + offset;
172  if (i >= vec.size()) {
173  vec.resize(i + 1, Ty());
174  }
175  return vec[i];
176  } // return y value for given x
177 
178  std::pair<Tx, Ty&> operator[](size_t index)
179  {
180  assert(index >= 0);
181  assert(index < size());
182  return {from_bin(index), vec[index]};
183  } // pair with x,y& value
184 
185  std::pair<Tx, const Ty&> operator[](size_t index) const
186  {
187  assert(size() > index);
188  return {from_bin(index), vec[index]};
189  } // const pair with x,y& value
190 
191  const Ty& operator()(Tx x) const
192  {
193  assert(x >= _xmin);
194  int i = to_bin(x) + offset;
195  assert(i < vec.size());
196  return vec.at(i);
197  } // return y value for given x
198 
199  // can be optinally used to customize streaming out, normalise etc.
200  std::function<void(std::ostream&, Tx, Ty)> stream_decorator = nullptr;
201 
202  friend std::ostream& operator<<(std::ostream& o,
204  {
205  o.precision(16);
206  for (auto d : tbl) {
207  if (tbl.stream_decorator == nullptr) {
208  o << d.first << " " << d.second << "\n";
209  }
210  else
211  tbl.stream_decorator(o, d.first, d.second);
212  }
213  return o;
214  } // write to stream
215 
216  auto& operator<<(std::istream& in)
217  {
218  assert(stream_decorator == nullptr && "you probably don't want to load a decorated file");
219  clear();
220  Tx x;
221  Ty y;
222  std::string line;
223  while (std::getline(in, line)) {
224  std::stringstream o(line);
225  while (o >> x) {
226  if (x >= _xmin) {
227  y << o;
228  operator()(x) = y;
229  }
230  else {
231  throw std::runtime_error("table load error: x smaller than xmin");
232  }
233  }
234  }
235  return *this;
236  } // load from stream
237 };
238 
239 TEST_CASE("[Faunus] Equidistant2DTable")
240 {
241  using doctest::Approx;
242 
243  SUBCASE("centered")
244  {
246  CHECK_EQ(y.xmin(), Approx(-3.0));
247  CHECK_EQ(y.dx(), Approx(0.5));
248  y(-2.51) = 0.15;
249  CHECK_EQ(y(-2.5), Approx(0.15));
250  y(-2.76) = 0.11;
251  CHECK_EQ(y(-3), Approx(0.11));
252  y(0.4) = 0.3;
253  CHECK_EQ(y(0.5), Approx(0.3));
254  y(1.3) = 0.5;
255  CHECK_EQ(y(1.5), Approx(0.5));
256  CHECK_EQ(y.xmax(), Approx(1.5));
257  }
258 
259  SUBCASE("lower bound")
260  {
261  Equidistant2DTable<double> y(0.5, -3.0);
262  CHECK_EQ(y.xmin(), Approx(-3.0));
263  CHECK_EQ(y.dx(), Approx(0.5));
264  y(-2.51) = 0.15;
265  CHECK_EQ(y(-3.0), Approx(0.15));
266  y(-2.76) = 0.11;
267  CHECK_EQ(y(-3), Approx(0.11));
268  y(0.4) = 0.3;
269  CHECK_EQ(y(0.0), Approx(0.3));
270  y(1.3) = 0.5;
271  CHECK_EQ(y(1.0), Approx(0.5));
272  CHECK_EQ(y.xmax(), Approx(1.0));
273  }
274 }
275 
276 } // namespace Faunus
Definition: equidistant_table.h:49
Tint to_bin(T x, T dx=1)
Round a floating point to integer for use with histogram binning.
Definition: auxiliary.h:118
double sumy() const
sum all y-values
Definition: equidistant_table.h:132
Definition: equidistant_table.h:25
Cell list class templates.
Definition: actions.cpp:11
Tx xmax() const
maximum stored x-value
Definition: equidistant_table.h:163
Definition: equidistant_table.h:75
Equidistant2DTable(Tx dx, Tx xmin, Tx xmax=std::numeric_limits< Tx >::infinity())
Constructor.
Definition: equidistant_table.h:117
Tx xmin() const
minimum x-value
Definition: equidistant_table.h:161
const std::vector< Ty > & yvec() const
vector with y-values
Definition: equidistant_table.h:141