Skip to content

Commit d2a60ce

Browse files
committed
Fixes to object API + updated README.
1 parent 127fff9 commit d2a60ce

File tree

8 files changed

+203
-78
lines changed

8 files changed

+203
-78
lines changed

README.md

Lines changed: 139 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,56 +2,118 @@
22

33
[![Build Status](https://travis-ci.org/EconForge/interpolation.py.svg?branch=master)](https://travis-ci.org/EconForge/interpolation.py)
44

5-
This library provides fast numba-accelerated interpolation routines
6-
for the following cases:
5+
The library contains:
6+
- splines.*: fast numba-compatible multilinear and cubic interpolation
7+
- multilinear.*: fast numba-compatible multilinear interpolation (alternative implementation)
8+
- smolyak.*: smolyak polinomials
9+
- complete polynomials
710

8-
## multilinear and cubic splines in any dimension
11+
## multilinear and cubic interpolation
12+
13+
Fast numba-accelerated interpolation routines
14+
for multilinear and cubic interpolation, with any number of dimensions.
15+
Several interfaces are provided.
16+
17+
### eval_linear
18+
19+
Preferred interface for multilinear interpolation. It can interpolate on uniform
20+
and nonuniform cartesian grids. Several extrapolation options are available.
921

10-
Here is an example in 3 dimensions:
1122

1223
```python
13-
from interpolation.splines import LinearSpline, CubicSpline
14-
a = np.array([0.0,0.0,0.0]) # lower boundaries
15-
b = np.array([1.0,1.0,1.0]) # upper boundaries
16-
orders = np.array([50,50,50]) # 10 points along each dimension
17-
values = np.random.random(orders) # values at each node of the grid
18-
S = np.random.random((10^6,3)) # coordinates at which to evaluate the splines
24+
import numpy as np
1925

20-
# multilinear
21-
lin = LinearSpline(a,b,orders,values)
22-
V = lin(S)
23-
# cubic
24-
spline = CubicSpline(a,b,orders,values) # filter the coefficients
25-
V = spline(S) # interpolates -> (100000,) array
26+
from interpolation.splines import UCGrid, CGrid, nodes
2627

27-
```
28+
# we interpolate function
29+
f = lambda x,y: np.sin(np.sqrt(x**2+y**2+0.00001))/np.sqrt(x**2+y**2+0.00001)
2830

29-
Unfair timings: (from `misc/speed_comparison.py`)
30-
```
31-
# interpolate 10^6 points on a 50x50x50 grid.
32-
Cubic: 0.11488723754882812
33-
Linear: 0.03426337242126465
34-
Scipy (linear): 0.6502540111541748
35-
```
31+
# uniform cartesian grid
32+
grid = UCGrid((-1.0, 1.0, 10), (-1.0, 1.0, 10))
3633

37-
More details are available as an example [notebook](https://github.com/EconForge/interpolation.py/blob/master/examples/cubic_splines_python.ipynb)
34+
# get grid points
35+
gp = nodes(grid) # 100x2 matrix
36+
37+
# compute values on grid points
38+
values = f(gp[:,0], gp[:,1]).reshape((10,10))
39+
40+
from interpolation.splines import eval_linear
41+
# interpolate at one point
42+
point = np.array([0.1,0.45]) # 1d array
43+
val = eval_linear(grid, values, point) # float
44+
45+
# interpolate at many points:
46+
points = np.random.random((10000,2))
47+
eval_linear(grid, values, points) # 10000 vector
48+
49+
# output can be preallocated
50+
out = np.zeros(10000)
51+
eval_linear(grid, values, points, out) # 10000 vector
52+
53+
54+
# default calls extrapolate data by using the nearest value inside the grid
55+
# other extrapolation options can be chosen among NEAREST, LINEAR, CONSTANT
56+
57+
from interpolation.splines import extrap_options as xto
58+
points = np.random.random((100,2))*3-1
59+
eval_linear(grid, values, points, xto.NEAREST) # 100
60+
eval_linear(grid, values, points, xto.LINEAR) # 10000 vector
61+
eval_linear(grid, values, points, xto.CONSTANT) # 10000 vector
62+
63+
64+
# one can also approximate on nonuniform cartesian grids
65+
66+
grid = CGrid(np.linspace(-1,1,100)**3, (-1.0, 1.0, 10))
67+
68+
points = np.random.random((10000,2))
69+
eval_linear(grid, values, points) # 10000 vector
70+
71+
72+
# it is also possible to interpolate vector-valued functions in the following way
73+
74+
f = lambda x,y: np.sin(x**3+y**2+0.00001)/np.sqrt(x**2+y**2+0.00001)
75+
g = lambda x,y: np.sin(x**3+y**2+0.00001)/np.sqrt(x**2+y**2+0.00001)
76+
grid = UCGrid((-1.0, 1.0, 10), (-1.0, 1.0, 10))
77+
gp = nodes(grid) # 100x2 matrix
78+
mvalues = np.concatenate([
79+
f(gp[:,0], gp[:,1]).reshape((10,10))[:,:,None],
80+
g(gp[:,0], gp[:,1]).reshape((10,10))[:,:,None]
81+
],axis=2) # 10x10x2 array
82+
points = np.random.random((1000,2))
83+
eval_linear(grid, mvalues, points[:,1]) # 2 elements vector
84+
eval_linear(grid, mvalues, points) # 1000x2 matrix
85+
out = np.zeros((1000,2))
86+
eval_linear(grid, mvalues, points, out) # 1000x2 matrix
3887

39-
Other available features are:
40-
- linear extrapolation
41-
- computation of derivatives
42-
- interpolate many functions at once (multi-splines or vector valued splines)
4388

44-
Experimental
45-
- evaluation on the GPU (with numba.cuda)
46-
- parallel evaluation (with guvectorize)
4789

48-
In the near future:
49-
- JIT classes for all interpolation objects
90+
# finally, the same syntax can be used to interpolate using cubic splines
91+
# one just needs to prefilter the coefficients first
92+
# the same set of options apply but nonuniform grids are not supported (yet)
5093

94+
f = lambda x,y: np.sin(x**3+y**2+0.00001)/np.sqrt(x**2+y**2+0.00001)
95+
grid = UCGrid((-1.0, 1.0, 10), (-1.0, 1.0, 10))
96+
gp = nodes(grid) # 100x2 matrix
97+
values = f(gp[:,0], gp[:,1]).reshape((10,10))
5198

52-
## jitted, non-uniform multilinear interpolation
99+
# filter values
100+
from interpolation.splines import filter_cubic
101+
coeffs = filter_cubic(grid, values) # a 12x12 array
102+
103+
from interpolation.splines import eval_cubic
104+
points = np.random.random((1000,2))
105+
eval_cubic(grid, coeffs, points[:,1]) # 2 elements vector
106+
eval_cubic(grid, coeffs, points) # 1000x2 matrix
107+
out = np.zeros((1000,2))
108+
eval_cubic(grid, coeffs, points, out) # 1000x2 matrix
109+
110+
```
111+
112+
113+
### interp
114+
115+
Simpler interface. Mimmicks default `scipy.interp`: mutlilinear interpolation with constant extrapolation.
53116

54-
There is a simple `interp` function with a flexible API which does multinear on uniform or non uniform cartesian grids.
55117

56118
```
57119
### 1d grid
@@ -66,10 +128,51 @@ interp(x,y,0.5)
66128
# or at many points:
67129
u = np.linspace(0,1,1000) # points
68130
interp(x,y,u)
131+
132+
```
133+
134+
135+
### object interface
136+
137+
This is for compatibility purpose only, until a new jittable model object is found.
138+
139+
```python
140+
from interpolation.splines import LinearSpline, CubicSpline
141+
a = np.array([0.0,0.0,0.0]) # lower boundaries
142+
b = np.array([1.0,1.0,1.0]) # upper boundaries
143+
orders = np.array([50,50,50]) # 10 points along each dimension
144+
values = np.random.random(orders) # values at each node of the grid
145+
S = np.random.random((10^6,3)) # coordinates at which to evaluate the splines
146+
147+
# multilinear
148+
lin = LinearSpline(a,b,orders,values)
149+
V = lin(S)
150+
# cubic
151+
spline = CubicSpline(a,b,orders,values) # filter the coefficients
152+
V = spline(S) # interpolates -> (100000,) array
153+
69154
```
70155

156+
### development notes
71157

158+
Old, unfair timings: (from `misc/speed_comparison.py`)
72159

160+
```
161+
# interpolate 10^6 points on a 50x50x50 grid.
162+
Cubic: 0.11488723754882812
163+
Linear: 0.03426337242126465
164+
Scipy (linear): 0.6502540111541748
165+
```
166+
167+
More details are available as an example [notebook](https://github.com/EconForge/interpolation.py/blob/master/examples/cubic_splines_python.ipynb) (outdated)
168+
169+
Missing but available soon:
170+
- splines at any order
171+
- derivative
172+
173+
Feasible (some experiments)
174+
- evaluation on the GPU (with numba.cuda)
175+
- parallel evaluation (with guvectorize)
73176

74177

75178

interpolation/splines/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,13 @@
22
from .multilinear import LinearSpline, LinearSplines
33
from .eval_splines import options, eval_linear, eval_cubic
44
from .prefilter_cubic import filter_cubic
5+
from .option_types import options as extrap_options
6+
7+
# dummy functions
8+
def UCGrid(*args):
9+
return tuple(args)
10+
def CGrid(*args):
11+
return tuple(args)
12+
def nodes(grid):
13+
from interpolation.cartesian import mlinspace
14+
return mlinspace([g[0] for g in grid],[g[1] for g in grid],[g[2] for g in grid])

interpolation/splines/basis_splines.py

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,30 @@ def B(p, u, i, x):
1414
else:
1515
return (((x - u[i]) / (u[i + p] - u[i])) * B(p - 1, u, i, x) + ((u[i + p + 1] - x) / (u[i + p + 1] - u[i + 1])) * B(p - 1, u, i + 1, x))
1616

17-
18-
from pylab import *
19-
20-
xvec = linspace(-5, 5, 11)
21-
22-
tvec = linspace(-5, 5, 1000)
23-
24-
yvec = array([B0(xvec, 5, x) for x in tvec])
25-
y2vec = array([B(2, xvec, 5, x) for x in tvec])
26-
y3vec = array([B(3, xvec, 5, x) for x in tvec])
27-
28-
29-
print(B(3, xvec, 5, 0))
30-
print(B(3, xvec, 5, 1))
31-
print(B(3, xvec, 5, 2))
32-
print(B(3, xvec, 5, 3))
33-
print(B(3, xvec, 5, 4))
34-
35-
36-
plot(xvec, xvec * 0, 'o-')
37-
38-
plot(tvec, yvec)
39-
plot(tvec, y2vec)
40-
plot(tvec, y3vec)
41-
42-
43-
show()
17+
#
18+
# from pylab import *
19+
#
20+
# xvec = linspace(-5, 5, 11)
21+
#
22+
# tvec = linspace(-5, 5, 1000)
23+
#
24+
# yvec = array([B0(xvec, 5, x) for x in tvec])
25+
# y2vec = array([B(2, xvec, 5, x) for x in tvec])
26+
# y3vec = array([B(3, xvec, 5, x) for x in tvec])
27+
#
28+
#
29+
# print(B(3, xvec, 5, 0))
30+
# print(B(3, xvec, 5, 1))
31+
# print(B(3, xvec, 5, 2))
32+
# print(B(3, xvec, 5, 3))
33+
# print(B(3, xvec, 5, 4))
34+
#
35+
#
36+
# plot(xvec, xvec * 0, 'o-')
37+
#
38+
# plot(tvec, yvec)
39+
# plot(tvec, y2vec)
40+
# plot(tvec, y3vec)
41+
#
42+
#
43+
# show()

interpolation/splines/eval_cubic.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import numpy
22

3-
43
from .eval_splines import eval_cubic
5-
## the functions in this file work for any dimension (d<=4)
6-
## they can optionnally allocate memory for the result
74

5+
## the functions in this file provide backward compatibility calls
6+
##
7+
## they can optionnally allocate memory for the result
8+
## they work for any dimension, except the functions which compute the gradient
89

910
#######################
1011
# Compatibility calls #
@@ -21,7 +22,7 @@ def get_grid(a,b,n,C):
2122
print(txt)
2223
f = source_to_function(txt)
2324
return f
24-
25+
2526

2627
def eval_cubic_spline(a, b, orders, coefs, point):
2728
"""Evaluates a cubic spline at one point

interpolation/splines/multilinear.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,11 @@ def set_values(self,values):
7979

8080
def interpolate(self,s):
8181

82-
from .multilinear_numba import multilinear_interpolation
82+
from .eval_splines import eval_linear
83+
8384
s = numpy.ascontiguousarray(s, dtype=self.dtype)
84-
a = multilinear_interpolation(self.smin,self.smax,self.orders,self.values,s)
85+
grid = tuple((self.smin[i], self.smax[i], self.orders[i]) for i in range(len(self.smin)))
86+
a = eval_linear(grid,self.values,s)
8587
return a
8688

8789
def __call__(self,s):
@@ -162,9 +164,10 @@ def set_values(self,mvalues):
162164

163165
def interpolate(self,s):
164166

165-
from .multilinear_numba import vec_multilinear_interpolation
167+
from .multilinear_numba import eval_linear
168+
grid = tuple((self.smin[i], self.smax[i], self.orders[i]) for i in range(len(self.smin)))
166169
s = numpy.ascontiguousarray(s, dtype=self.dtype)
167-
a = vec_multilinear_interpolation(self.smin,self.smax,self.orders,self.mvalues,s)
170+
a = eval_linear(grid,self.mvalues,s)
168171
return a
169172

170173
def __call__(self,s):

interpolation/splines/multilinear_numba.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# these are compatibility calls
2+
13
import numpy as np
24
from numba import njit
35
from .eval_splines import eval_linear
@@ -20,4 +22,3 @@ def multilinear_interpolation(smin, smax, orders, values, s, out=None):
2022
return eval_linear(grid, values, s)
2123
else:
2224
eval_linear(grid, values, s, out)
23-

interpolation/splines/splines.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,23 +86,26 @@ def interpolate(self, points, values=None, with_derivatives=False):
8686

8787
import time
8888

89-
from .eval_cubic import vec_eval_cubic_spline, eval_cubic_spline
89+
from .eval_splines import eval_cubic
90+
91+
grid = tuple((self.a[i], self.b[i], self.orders[i]) for i in range(len(self.a)))
9092

9193
if not np.all( np.isfinite(points)):
9294
raise Exception('Spline interpolator evaluated at non-finite points.')
9395

9496
if not with_derivatives:
9597
if points.ndim == 1:
9698
# evaluate only on one point
97-
return eval_cubic_spline(self.a, self.b, self.orders, self.__coeffs__, points)
99+
return eval_cubic(grid, self.__coeffs__, points)
98100
else:
99101

100102
N, d = points.shape
101103
assert(d==self.d)
102104
if values is None:
103105
values = np.empty(N, dtype=self.dtype)
104-
vec_eval_cubic_spline(self.a, self.b, self.orders, self.__coeffs__, points, values)
106+
eval_cubic(grid, self.__coeffs__, points, values)
105107
return values
108+
106109
else:
107110
raise Exception("Not implemented.")
108111

@@ -182,10 +185,14 @@ def interpolate(self, points, diff=False):
182185
N = points.shape[0]
183186
d = points.shape[1]
184187

188+
from .eval_splines import eval_cubic
189+
190+
185191
if not diff:
186-
from .eval_cubic import vec_eval_cubic_splines
192+
grid = tuple((self.a[i], self.b[i], self.orders[i]) for i in range(len(self.a)))
193+
from .eval_splines import eval_cubic
187194
values = np.empty((N,n_sp), dtype=float)
188-
vec_eval_cubic_splines(self.a, self.b, self.orders, self.__mcoeffs__, points, values)
195+
eval_cubic(grid, self.__mcoeffs__, points, values)
189196
return values
190197
else:
191198
from .eval_cubic import vec_eval_cubic_splines_G

interpolation/splines/tests/test_object_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def ttest_object_api(Obj):
5858
def ttest_object_vector_api(Obj):
5959

6060
cs = Obj(a,b,orders,mvals)
61-
61+
6262
ii = cs(point)
6363
iii = cs(points)
6464
try:

0 commit comments

Comments
 (0)