EpIC 1.1.0
Monte Carlo generator for exclusive processes
Loading...
Searching...
No Matches
linterp.h
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/*
15This 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.
18Requires boost/multi_array library.
19
20For 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 Reinforcement Learning", _Proceedings of Neural 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
43using std::vector;
44using std::array;
45typedef unsigned int uint;
46typedef vector<int> iVec;
47typedef 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
68struct EmptyClass {};
69
70template <int N, class T, bool CopyData = true, bool Continuous = true, class ArrayRefCountT = EmptyClass, class GridRefCountT = EmptyClass>
72public:
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
221template <int N, class T, bool CopyData = true, bool Continuous = true, class ArrayRefCountT = EmptyClass, class GridRefCountT = EmptyClass>
222class InterpSimplex : public NDInterpolator<N,T,CopyData,Continuous,ArrayRefCountT,GridRefCountT> {
223public:
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
295template <int N, class T, bool CopyData = true, bool Continuous = true, class ArrayRefCountT = EmptyClass, class GridRefCountT = EmptyClass>
296class InterpMultilinear : public NDInterpolator<N,T,CopyData,Continuous,ArrayRefCountT,GridRefCountT> {
297public:
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
384extern "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
390void inline 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
400void inline 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
410void inline 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
Definition: linterp.h:296
Definition: linterp.h:222
Definition: linterp.h:71
Definition: linterp.h:68