icedb  version 0.5.1
Snow particle scattering database API
linterp.h
Go to the documentation of this file.
1 
2 //
3 // Copyright (c) 2012 Ronaldo Carpio
4 //
5 // Permission to use, copy, modify, distribute and sell this software
6 // and its documentation for any purpose is hereby granted without fee,
7 // provided that the above copyright notice appear in all copies and
8 // that both that copyright notice and this permission notice appear
9 // in supporting documentation. The authors make no representations
10 // about the suitability of this software for any purpose.
11 // It is provided "as is" without express or implied warranty.
12 //
13 
14 /*
15 This is a C++ header-only library for N-dimensional linear interpolation on a rectangular grid. Implements two methods:
16 * Multilinear: Interpolate using the N-dimensional hypercube containing the point. Interpolation step is O(2^N)
17 * Simplicial: Interpolate using the N-dimensional simplex containing the point. Interpolation step is O(N log N), but less accurate.
18 Requires boost/multi_array library.
19 
20 For a description of the algorithms, see:
21 * Weiser & Zarantonello (1988), "A Note on Piecewise Linear and Multilinear Table Interpolation in Many Dimensions", _Mathematics of Computation_ 50 (181), p. 189-196
22 * Davies (1996), "Multidimensional Triangulation and Interpolation for ReICEDB_LOG_INFOrcement Learning", _Proceedings of Neural ICEDB_LOG_INFOrmation Processing Systems 1996_
23 */
24 
25 #ifndef _linterp_h
26 #define _linterp_h
27 
28 #include <assert.h>
29 #include <math.h>
30 #include <stdarg.h>
31 #include <float.h>
32 #include <cstdarg>
33 #include <string>
34 #include <vector>
35 #include <array>
36 #include <functional>
37 
38 #include <boost/multi_array.hpp>
39 #include <boost/numeric/ublas/matrix.hpp>
40 #include <boost/numeric/ublas/matrix_proxy.hpp>
41 #include <boost/numeric/ublas/storage.hpp>
42 
43 using std::vector;
44 using std::array;
45 typedef unsigned int uint;
46 typedef vector<int> iVec;
47 typedef vector<double> dVec;
48 
49 
50 // TODO:
51 // - specify behavior past grid boundaries.
52 // 1) clamp
53 // 2) return a pre-determined value (e.g. NaN)
54 
55 // compile-time params:
56 // 1) number of dimensions
57 // 2) scalar type T
58 // 3) copy data or not (default: false). The grids will always be copied
59 // 4) ref count class (default: none)
60 // 5) continuous or not
61 
62 // run-time constructor params:
63 // 1) f
64 // 2) grids
65 // 3) behavior outside grid: default=clamp
66 // 4) value to return outside grid, defaut=nan
67 
68 struct EmptyClass {};
69 
70 template <int N, class T, bool CopyData = true, bool Continuous = true, class ArrayRefCountT = EmptyClass, class GridRefCountT = EmptyClass>
72 public:
73  typedef T value_type;
74  typedef ArrayRefCountT array_ref_count_type;
75  typedef GridRefCountT grid_ref_count_type;
76 
77  static const int m_N = N;
78  static const bool m_bCopyData = CopyData;
79  static const bool m_bContinuous = Continuous;
80 
81  typedef boost::numeric::ublas::array_adaptor<T> grid_type;
82  typedef boost::const_multi_array_ref<T, N> array_type;
83  typedef std::unique_ptr<array_type> array_type_ptr;
84 
85  array_type_ptr m_pF;
86  ArrayRefCountT m_ref_F; // reference count for m_pF
87  vector<T> m_F_copy; // if CopyData == true, this holds the copy of F
88 
89  vector<grid_type> m_grid_list;
90  vector<GridRefCountT> m_grid_ref_list; // reference counts for grids
91  vector<vector<T> > m_grid_copy_list; // if CopyData == true, this holds the copies of the grids
92 
93  // constructors assume that [f_begin, f_end) is a contiguous array in C-order
94  // non ref-counted constructor.
95  template <class IterT1, class IterT2, class IterT3>
96  NDInterpolator(IterT1 grids_begin, IterT2 grids_len_begin, IterT3 f_begin, IterT3 f_end) {
97  init(grids_begin, grids_len_begin, f_begin, f_end);
98  }
99 
100  // ref-counted constructor
101  template <class IterT1, class IterT2, class IterT3, class RefCountIterT>
102  NDInterpolator(IterT1 grids_begin, IterT2 grids_len_begin, IterT3 f_begin, IterT3 f_end, ArrayRefCountT &refF, RefCountIterT grid_refs_begin) {
103  init_refcount(grids_begin, grids_len_begin, f_begin, f_end, refF, grid_refs_begin);
104  }
105 
106  template <class IterT1, class IterT2, class IterT3>
107  void init(IterT1 grids_begin, IterT2 grids_len_begin, IterT3 f_begin, IterT3 f_end) {
108  set_grids(grids_begin, grids_len_begin, m_bCopyData);
109  set_f_array(f_begin, f_end, m_bCopyData);
110  }
111  template <class IterT1, class IterT2, class IterT3, class RefCountIterT>
112  void init_refcount(IterT1 grids_begin, IterT2 grids_len_begin, IterT3 f_begin, IterT3 f_end, ArrayRefCountT &refF, RefCountIterT grid_refs_begin) {
113  set_grids(grids_begin, grids_len_begin, m_bCopyData);
114  set_grids_refcount(grid_refs_begin, grid_refs_begin + N);
115  set_f_array(f_begin, f_end, m_bCopyData);
116  set_f_refcount(refF);
117  }
118 
119  template <class IterT1, class IterT2>
120  void set_grids(IterT1 grids_begin, IterT2 grids_len_begin, bool bCopy) {
121  m_grid_list.clear();
122  m_grid_ref_list.clear();
123  m_grid_copy_list.clear();
124  for (int i=0; i<N; i++) {
125  int gridLength = grids_len_begin[i];
126  if (bCopy == false) {
127  T const *grid_ptr = &(*grids_begin[i]);
128  m_grid_list.push_back(grid_type(gridLength, (T*) grid_ptr )); // use the given pointer
129  } else {
130  m_grid_copy_list.push_back( vector<T>(grids_begin[i], grids_begin[i] + grids_len_begin[i]) ); // make our own copy of the grid
131  T *begin = &(m_grid_copy_list[i][0]);
132  m_grid_list.push_back(grid_type(gridLength, begin)); // use our copy
133  }
134  }
135  }
136  template <class IterT1, class RefCountIterT>
137  void set_grids_refcount(RefCountIterT refs_begin, RefCountIterT refs_end) {
138  assert(refs_end - refs_begin == N);
139  m_grid_ref_list.assign(refs_begin, refs_begin + N);
140  }
141 
142  // assumes that [f_begin, f_end) is a contiguous array in C-order
143  template <class IterT>
144  void set_f_array(IterT f_begin, IterT f_end, bool bCopy) {
145  unsigned int nGridPoints = 1;
146  array<int,N> sizes;
147  for (unsigned int i=0; i<m_grid_list.size(); i++) {
148  sizes[i] = m_grid_list[i].size();
149  nGridPoints *= sizes[i];
150  }
151 
152  int f_len = f_end - f_begin;
153  if ( (m_bContinuous && f_len != nGridPoints) || (!m_bContinuous && f_len != 2 * nGridPoints) ) {
154  throw std::invalid_argument("f has wrong size");
155  }
156  for (unsigned int i=0; i<m_grid_list.size(); i++) {
157  if (!m_bContinuous) { sizes[i] *= 2; }
158  }
159 
160  m_F_copy.clear();
161  if (bCopy == false) {
162  m_pF.reset(new array_type(f_begin, sizes));
163  } else {
164  m_F_copy = vector<T>(f_begin, f_end);
165  m_pF.reset(new array_type(&m_F_copy[0], sizes));
166  }
167  }
168  void set_f_refcount(ArrayRefCountT &refF) {
169  m_ref_F = refF;
170  }
171 
172  // -1 is before the first grid point
173  // N-1 (where grid.size() == N) is after the last grid point
174  int find_cell(int dim, T x) const {
175  grid_type const &grid(m_grid_list[dim]);
176  if (x < *(grid.begin())) return -1;
177  else if (x >= *(grid.end()-1)) return grid.size()-1;
178  else {
179  auto i_upper = std::upper_bound(grid.begin(), grid.end(), x);
180  return i_upper - grid.begin() - 1;
181  }
182  }
183 
184  // return the value of f at the given cell and vertex
185  T get_f_val(array<int,N> const &cell_index, array<int,N> const &v_index) const {
186  array<int,N> f_index;
187 
188  if (m_bContinuous) {
189  for (int i=0; i<N; i++) {
190  if (cell_index[i] < 0) {
191  f_index[i] = 0;
192  } else if (cell_index[i] >= m_grid_list[i].size()-1) {
193  f_index[i] = m_grid_list[i].size()-1;
194  } else {
195  f_index[i] = cell_index[i] + v_index[i];
196  }
197  }
198  } else {
199  for (int i=0; i<N; i++) {
200  if (cell_index[i] < 0) {
201  f_index[i] = 0;
202  } else if (cell_index[i] >= m_grid_list[i].size()-1) {
203  f_index[i] = (2*m_grid_list[i].size())-1;
204  } else {
205  f_index[i] = 1 + (2*cell_index[i]) + v_index[i];
206  }
207  }
208  }
209  return (*m_pF)(f_index);
210  }
211 
212  T get_f_val(array<int,N> const &cell_index, int v) const {
213  array<int,N> v_index;
214  for (int dim=0; dim<N; dim++) {
215  v_index[dim] = (v >> (N-dim-1)) & 1; // test if the i-th bit is set
216  }
217  return get_f_val(cell_index, v_index);
218  }
219 };
220 
221 template <int N, class T, bool CopyData = true, bool Continuous = true, class ArrayRefCountT = EmptyClass, class GridRefCountT = EmptyClass>
222 class InterpSimplex : public NDInterpolator<N,T,CopyData,Continuous,ArrayRefCountT,GridRefCountT> {
223 public:
225 
226  template <class IterT1, class IterT2, class IterT3>
227  InterpSimplex(IterT1 grids_begin, IterT2 grids_len_begin, IterT3 f_begin, IterT3 f_end)
228  : super(grids_begin, grids_len_begin, f_begin, f_end)
229  {}
230  template <class IterT1, class IterT2, class IterT3, class RefCountIterT>
231  InterpSimplex(IterT1 grids_begin, IterT2 grids_len_begin, IterT3 f_begin, IterT3 f_end, ArrayRefCountT &refF, RefCountIterT ref_begins)
232  : super(grids_begin, grids_len_begin, f_begin, f_end, refF, ref_begins)
233  {}
234 
235  template <class IterT>
236  T interp(IterT x_begin) const {
237  array<T,1> result;
238  array< array<T,1>, N > coord_iter;
239  for (int i=0; i<N; i++) {
240  coord_iter[i][0] = x_begin[i];
241  }
242  interp_vec(1, coord_iter.begin(), coord_iter.end(), result.begin());
243  return result[0];
244  }
245 
246  template <class IterT1, class IterT2>
247  void interp_vec(int n, IterT1 coord_iter_begin, IterT1 coord_iter_end, IterT2 i_result) const {
248  assert(N == coord_iter_end - coord_iter_begin);
249 
250  array<int,N> cell_index, v_index;
251  array<std::pair<T, int>,N> xipair;
252  int c;
253  T y, v0, v1;
254  //mexPrintf("%d\n", n);
255  for (int i=0; i<n; i++) { // for each point
256  for (int dim=0; dim<N; dim++) {
257  typename super::grid_type const &grid(super::m_grid_list[dim]);
258  c = this->find_cell(dim, coord_iter_begin[dim][i]);
259  //mexPrintf("%d\n", c);
260  if (c == -1) { // before first grid point
261  y = 1.0;
262  } else if (c == grid.size()-1) { // after last grid point
263  y = 0.0;
264  } else {
265  //mexPrintf("%f %f\n", grid[c], grid[c+1]);
266  y = (coord_iter_begin[dim][i] - grid[c]) / (grid[c + 1] - grid[c]);
267  if (y < 0.0) y=0.0;
268  else if (y > 1.0) y=1.0;
269  }
270  xipair[dim].first = y;
271  xipair[dim].second = dim;
272  cell_index[dim] = c;
273  }
274  // sort xi's and get the permutation
275  std::sort(xipair.begin(), xipair.end(), [](std::pair<T, int> const &a, std::pair<T, int> const &b) {
276  return (a.first < b.first);
277  });
278  // walk the vertices of the simplex determined by the permutation
279  for (int j=0; j<N; j++) {
280  v_index[j] = 1;
281  }
282  v0 = this->get_f_val(cell_index, v_index);
283  y = v0;
284  for (int j=0; j<N; j++) {
285  v_index[xipair[j].second]--;
286  v1 = this->get_f_val(cell_index, v_index);
287  y += (1.0 - xipair[j].first) * (v1-v0); // interpolate
288  v0 = v1;
289  }
290  *i_result++ = y;
291  }
292  }
293 };
294 
295 template <int N, class T, bool CopyData = true, bool Continuous = true, class ArrayRefCountT = EmptyClass, class GridRefCountT = EmptyClass>
296 class InterpMultilinear : public NDInterpolator<N,T,CopyData,Continuous,ArrayRefCountT,GridRefCountT> {
297 public:
299 
300  template <class IterT1, class IterT2, class IterT3>
301  InterpMultilinear(IterT1 grids_begin, IterT2 grids_len_begin, IterT3 f_begin, IterT3 f_end)
302  : super(grids_begin, grids_len_begin, f_begin, f_end)
303  {}
304  template <class IterT1, class IterT2, class IterT3, class RefCountIterT>
305  InterpMultilinear(IterT1 grids_begin, IterT2 grids_len_begin, IterT3 f_begin, IterT3 f_end, ArrayRefCountT &refF, RefCountIterT ref_begins)
306  : super(grids_begin, grids_len_begin, f_begin, f_end, refF, ref_begins)
307  {}
308 
309  template <class IterT1, class IterT2>
310  static T linterp_nd_unitcube(IterT1 f_begin, IterT1 f_end, IterT2 xi_begin, IterT2 xi_end) {
311  int n = xi_end - xi_begin;
312  int f_len = f_end - f_begin;
313  assert(1 << n == f_len);
314  T sub_lower, sub_upper;
315  if (n == 1) {
316  sub_lower = f_begin[0];
317  sub_upper = f_begin[1];
318  } else {
319  sub_lower = linterp_nd_unitcube(f_begin, f_begin + (f_len/2), xi_begin + 1, xi_end);
320  sub_upper = linterp_nd_unitcube(f_begin + (f_len/2), f_end, xi_begin + 1, xi_end);
321  }
322  T result = sub_lower + (*xi_begin)*(sub_upper - sub_lower);
323  return result;
324  }
325 
326  template <class IterT>
327  T interp(IterT x_begin) const {
328  array<T,1> result;
329  array< array<T,1>, N > coord_iter;
330  for (int i=0; i<N; i++) {
331  coord_iter[i][0] = x_begin[i];
332  }
333  interp_vec(1, coord_iter.begin(), coord_iter.end(), result.begin());
334  return result[0];
335  }
336 
337  template <class IterT1, class IterT2>
338  void interp_vec(int n, IterT1 coord_iter_begin, IterT1 coord_iter_end, IterT2 i_result) const {
339  assert(N == coord_iter_end - coord_iter_begin);
340  array<int,N> index;
341  int c;
342  T y, xi;
343  vector<T> f(1 << N);
344  array<T,N> x;
345 
346  for (int i=0; i<n; i++) { // loop over each point
347  for (int dim=0; dim<N; dim++) { // loop over each dimension
348  auto const &grid(super::m_grid_list[dim]);
349  xi = coord_iter_begin[dim][i];
350  c = this->find_cell(dim, coord_iter_begin[dim][i]);
351  if (c == -1) { // before first grid point
352  y = 1.0;
353  } else if (c == grid.size()-1) { // after last grid point
354  y = 0.0;
355  } else {
356  y = (coord_iter_begin[dim][i] - grid[c]) / (grid[c + 1] - grid[c]);
357  if (y < 0.0) y=0.0;
358  else if (y > 1.0) y=1.0;
359  }
360  index[dim] = c;
361  x[dim] = y;
362  }
363  // copy f values at vertices
364  for (int v=0; v < (1 << N); v++) { // loop over each vertex of hypercube
365  f[v] = this->get_f_val(index, v);
366  }
367  *i_result++ = linterp_nd_unitcube(f.begin(), f.end(), x.begin(), x.end());
368  }
369  }
370 };
371 
382 
383 // C interface
384 extern "C" {
385  void linterp_simplex_1(double **grids_begin, int *grid_len_begin, double *pF, int xi_len, double **xi_begin, double *pResult);
386  void linterp_simplex_2(double **grids_begin, int *grid_len_begin, double *pF, int xi_len, double **xi_begin, double *pResult);
387  void linterp_simplex_3(double **grids_begin, int *grid_len_begin, double *pF, int xi_len, double **xi_begin, double *pResult);
388 }
389 
390 void linterp_simplex_1(double **grids_begin, int *grid_len_begin, double *pF, int xi_len, double **xi_begin, double *pResult) {
391  const int N=1;
392  size_t total_size = 1;
393  for (int i=0; i<N; i++) {
394  total_size *= grid_len_begin[i];
395  }
396  InterpSimplex<N, double, false> interp_obj(grids_begin, grid_len_begin, pF, pF + total_size);
397  interp_obj.interp_vec(xi_len, xi_begin, xi_begin + N, pResult);
398 }
399 
400 void linterp_simplex_2(double **grids_begin, int *grid_len_begin, double *pF, int xi_len, double **xi_begin, double *pResult) {
401  const int N=2;
402  size_t total_size = 1;
403  for (int i=0; i<N; i++) {
404  total_size *= grid_len_begin[i];
405  }
406  InterpSimplex<N, double, false> interp_obj(grids_begin, grid_len_begin, pF, pF + total_size);
407  interp_obj.interp_vec(xi_len, xi_begin, xi_begin + N, pResult);
408 }
409 
410 void linterp_simplex_3(double **grids_begin, int *grid_len_begin, double *pF, int xi_len, double **xi_begin, double *pResult) {
411  const int N=3;
412  size_t total_size = 1;
413  for (int i=0; i<N; i++) {
414  total_size *= grid_len_begin[i];
415  }
416  InterpSimplex<N, double, false> interp_obj(grids_begin, grid_len_begin, pF, pF + total_size);
417  interp_obj.interp_vec(xi_len, xi_begin, xi_begin + N, pResult);
418 }
419 
420 
421 
422 
423 #endif //_linterp_h
T get_f_val(array< int, N > const &cell_index, array< int, N > const &v_index) const
Definition: linterp.h:185
InterpMultilinear(IterT1 grids_begin, IterT2 grids_len_begin, IterT3 f_begin, IterT3 f_end, ArrayRefCountT &refF, RefCountIterT ref_begins)
Definition: linterp.h:305
void init_refcount(IterT1 grids_begin, IterT2 grids_len_begin, IterT3 f_begin, IterT3 f_end, ArrayRefCountT &refF, RefCountIterT grid_refs_begin)
Definition: linterp.h:112
boost::const_multi_array_ref< T, N > array_type
Definition: linterp.h:82
unsigned int uint
Definition: linterp.h:45
void set_grids_refcount(RefCountIterT refs_begin, RefCountIterT refs_end)
Definition: linterp.h:137
boost::numeric::ublas::array_adaptor< T > grid_type
Definition: linterp.h:81
InterpSimplex< 2, double > NDInterpolator_2_S
Definition: linterp.h:373
InterpMultilinear< 5, double > NDInterpolator_5_ML
Definition: linterp.h:381
void interp_vec(int n, IterT1 coord_iter_begin, IterT1 coord_iter_end, IterT2 i_result) const
Definition: linterp.h:247
int find_cell(int dim, T x) const
Definition: linterp.h:174
ArrayRefCountT m_ref_F
Definition: linterp.h:86
InterpSimplex< 4, double > NDInterpolator_4_S
Definition: linterp.h:375
vector< int > iVec
Definition: linterp.h:46
static T linterp_nd_unitcube(IterT1 f_begin, IterT1 f_end, IterT2 xi_begin, IterT2 xi_end)
Definition: linterp.h:310
T interp(IterT x_begin) const
Definition: linterp.h:236
void set_grids(IterT1 grids_begin, IterT2 grids_len_begin, bool bCopy)
Definition: linterp.h:120
std::unique_ptr< array_type > array_type_ptr
Definition: linterp.h:83
NDInterpolator(IterT1 grids_begin, IterT2 grids_len_begin, IterT3 f_begin, IterT3 f_end)
Definition: linterp.h:96
InterpSimplex< 3, double > NDInterpolator_3_S
Definition: linterp.h:374
array_type_ptr m_pF
Definition: linterp.h:85
InterpMultilinear< 1, double > NDInterpolator_1_ML
Definition: linterp.h:377
InterpMultilinear(IterT1 grids_begin, IterT2 grids_len_begin, IterT3 f_begin, IterT3 f_end)
Definition: linterp.h:301
void interp_vec(int n, IterT1 coord_iter_begin, IterT1 coord_iter_end, IterT2 i_result) const
Definition: linterp.h:338
GridRefCountT grid_ref_count_type
Definition: linterp.h:75
void linterp_simplex_1(double **grids_begin, int *grid_len_begin, double *pF, int xi_len, double **xi_begin, double *pResult)
Definition: linterp.h:390
vector< T > m_F_copy
Definition: linterp.h:87
void set_f_refcount(ArrayRefCountT &refF)
Definition: linterp.h:168
vector< GridRefCountT > m_grid_ref_list
Definition: linterp.h:90
void init(IterT1 grids_begin, IterT2 grids_len_begin, IterT3 f_begin, IterT3 f_end)
Definition: linterp.h:107
InterpMultilinear< 3, double > NDInterpolator_3_ML
Definition: linterp.h:379
void set_f_array(IterT f_begin, IterT f_end, bool bCopy)
Definition: linterp.h:144
vector< grid_type > m_grid_list
Definition: linterp.h:89
ArrayRefCountT array_ref_count_type
Definition: linterp.h:74
void linterp_simplex_3(double **grids_begin, int *grid_len_begin, double *pF, int xi_len, double **xi_begin, double *pResult)
Definition: linterp.h:410
vector< double > dVec
Definition: linterp.h:47
InterpSimplex(IterT1 grids_begin, IterT2 grids_len_begin, IterT3 f_begin, IterT3 f_end)
Definition: linterp.h:227
NDInterpolator< N, T, CopyData, Continuous, ArrayRefCountT, GridRefCountT > super
Definition: linterp.h:298
InterpSimplex< 1, double > NDInterpolator_1_S
Definition: linterp.h:372
InterpSimplex< 5, double > NDInterpolator_5_S
Definition: linterp.h:376
InterpMultilinear< 4, double > NDInterpolator_4_ML
Definition: linterp.h:380
NDInterpolator(IterT1 grids_begin, IterT2 grids_len_begin, IterT3 f_begin, IterT3 f_end, ArrayRefCountT &refF, RefCountIterT grid_refs_begin)
Definition: linterp.h:102
InterpSimplex(IterT1 grids_begin, IterT2 grids_len_begin, IterT3 f_begin, IterT3 f_end, ArrayRefCountT &refF, RefCountIterT ref_begins)
Definition: linterp.h:231
InterpMultilinear< 2, double > NDInterpolator_2_ML
Definition: linterp.h:378
vector< vector< T > > m_grid_copy_list
Definition: linterp.h:91
T interp(IterT x_begin) const
Definition: linterp.h:327
void linterp_simplex_2(double **grids_begin, int *grid_len_begin, double *pF, int xi_len, double **xi_begin, double *pResult)
Definition: linterp.h:400
NDInterpolator< N, T, CopyData, Continuous, ArrayRefCountT, GridRefCountT > super
Definition: linterp.h:224
T get_f_val(array< int, N > const &cell_index, int v) const
Definition: linterp.h:212