Skip to content

Commit 4597d98

Browse files
chiyahnsglyon
authored andcommitted
Function call syntax support (#206)
* Function call syntax support * README.md in favour of function calls
1 parent 615f9d7 commit 4597d98

16 files changed

+401
-15
lines changed

README.md

+15-13
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ from the Julia REPL.
3939

4040
## General usage
4141

42+
Note: the current version of `Interpolations` supports interpolation evaluation using index calls `[]`, but this feature will be deprecated in future. We highly recommend function calls with `()` as follows.
43+
4244
Given an `AbstractArray` `A`, construct an "interpolation object" `itp` as
4345
```jl
4446
itp = interpolate(A, options...)
@@ -49,7 +51,7 @@ samples in `A` are equally-spaced.
4951

5052
To evaluate the interpolation at position `(x, y, ...)`, simply do
5153
```jl
52-
v = itp[x, y, ...]
54+
v = itp(x, y, ...)
5355
```
5456

5557
Some interpolation objects support computation of the gradient, which
@@ -89,14 +91,14 @@ Julia's iterator objects, e.g.,
8991
```jl
9092
function ongrid!(dest, itp)
9193
for I in CartesianRange(size(itp))
92-
dest[I] = itp[I]
94+
dest[I] = itp(I)
9395
end
9496
end
9597
```
9698
would store the on-grid value at each grid point of `itp` in the output `dest`.
9799
Finally, courtesy of Julia's indexing rules, you can also use
98100
```jl
99-
fine = itp[linspace(1,10,1001), linspace(1,15,201)]
101+
fine = itp(linspace(1,10,1001), linspace(1,15,201))
100102
```
101103

102104

@@ -114,19 +116,19 @@ Some examples:
114116
```jl
115117
# Nearest-neighbor interpolation
116118
itp = interpolate(a, BSpline(Constant()), OnCell())
117-
v = itp[5.4] # returns a[5]
119+
v = itp(5.4) # returns a[5]
118120

119121
# (Multi)linear interpolation
120122
itp = interpolate(A, BSpline(Linear()), OnGrid())
121-
v = itp[3.2, 4.1] # returns 0.9*(0.8*A[3,4]+0.2*A[4,4]) + 0.1*(0.8*A[3,5]+0.2*A[4,5])
123+
v = itp(3.2, 4.1) # returns 0.9*(0.8*A[3,4]+0.2*A[4,4]) + 0.1*(0.8*A[3,5]+0.2*A[4,5])
122124

123125
# Quadratic interpolation with reflecting boundary conditions
124126
# Quadratic is the lowest order that has continuous gradient
125127
itp = interpolate(A, BSpline(Quadratic(Reflect())), OnCell())
126128

127129
# Linear interpolation in the first dimension, and no interpolation (just lookup) in the second
128130
itp = interpolate(A, (BSpline(Linear()), NoInterp()), OnGrid())
129-
v = itp[3.65, 5] # returns 0.35*A[3,5] + 0.65*A[4,5]
131+
v = itp(3.65, 5) # returns 0.35*A[3,5] + 0.65*A[4,5]
130132
```
131133
There are more options available, for example:
132134
```jl
@@ -145,8 +147,8 @@ A_x = 1.:2.:40.
145147
A = [log(x) for x in A_x]
146148
itp = interpolate(A, BSpline(Cubic(Line())), OnGrid())
147149
sitp = scale(itp, A_x)
148-
sitp[3.] # exactly log(3.)
149-
sitp[3.5] # approximately log(3.5)
150+
sitp(3.) # exactly log(3.)
151+
sitp(3.5) # approximately log(3.5)
150152
```
151153

152154
For multidimensional uniformly spaced grids
@@ -157,8 +159,8 @@ f(x1, x2) = log(x1+x2)
157159
A = [f(x1,x2) for x1 in A_x1, x2 in A_x2]
158160
itp = interpolate(A, BSpline(Cubic(Line())), OnGrid())
159161
sitp = scale(itp, A_x1, A_x2)
160-
sitp[5., 10.] # exactly log(5 + 10)
161-
sitp[5.6, 7.1] # approximately log(5.6 + 7.1)
162+
sitp(5., 10.) # exactly log(5 + 10)
163+
sitp(5.6, 7.1) # approximately log(5.6 + 7.1)
162164
```
163165
### Gridded interpolation
164166

@@ -173,7 +175,7 @@ A = rand(20)
173175
A_x = collect(1.0:2.0:40.0)
174176
knots = (A_x,)
175177
itp = interpolate(knots, A, Gridded(Linear()))
176-
itp[2.0]
178+
itp(2.0)
177179
```
178180

179181
The spacing between adjacent samples need not be constant, you can use the syntax
@@ -188,7 +190,7 @@ For example:
188190
A = rand(8,20)
189191
knots = ([x^2 for x = 1:8], [0.2y for y = 1:20])
190192
itp = interpolate(knots, A, Gridded(Linear()))
191-
itp[4,1.2] # approximately A[2,6]
193+
itp(4,1.2) # approximately A[2,6]
192194
```
193195
One may also mix modes, by specifying a mode vector in the form of an explicit tuple:
194196
```jl
@@ -225,7 +227,7 @@ A = hcat(x,y)
225227
itp = scale(interpolate(A, (BSpline(Cubic(Natural())), NoInterp()), OnGrid()), t, 1:2)
226228

227229
tfine = 0:.01:1
228-
xs, ys = [itp[t,1] for t in tfine], [itp[t,2] for t in tfine]
230+
xs, ys = [itp(t,1) for t in tfine], [itp(t,2) for t in tfine]
229231
```
230232

231233
We can then plot the spline with:

src/b-splines/indexing.jl

+5
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ end
5050
getindex_impl(itp)
5151
end
5252

53+
function (itp::BSplineInterpolation{T,N,TCoefs,IT,GT,pad})(args...) where {T,N,TCoefs,IT,GT,pad}
54+
# support function calls
55+
itp[args...]
56+
end
57+
5358
function gradient_impl(itp::Type{BSplineInterpolation{T,N,TCoefs,IT,GT,Pad}}) where {T,N,TCoefs,IT<:DimSpec{BSpline},GT<:DimSpec{GridType},Pad}
5459
meta = Expr(:meta, :inline)
5560
# For each component of the gradient, alternately calculate

src/extrapolation/extrapolation.jl

+5
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ end
6363
getindex_impl(etp, xs...)
6464
end
6565

66+
function (etp::Extrapolation{T,N,ITPT,IT,GT,ET})(args...) where {T,N,ITPT,IT,GT,ET}
67+
# support function calls
68+
etp[args...]
69+
end
70+
6671
checkbounds(::AbstractExtrapolation,I...) = nothing
6772

6873

src/extrapolation/filled.jl

+5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ extrapolate(itp::AbstractInterpolation{T,N,IT,GT}, fillvalue) where {T,N,IT,GT}
2525
convert(Tret, fitp.fillvalue)
2626
end
2727

28+
function (fitp::FilledExtrapolation{T,N,ITP,IT,GT,FT})(args...) where {T,N,ITP,IT,GT,FT}
29+
# support function calls
30+
fitp[args...]
31+
end
32+
2833
@inline Base.checkbounds(::Type{Bool}, A::FilledExtrapolation, I...) = _checkbounds(A, 1, indices(A), I)
2934
@inline _checkbounds(A, d::Int, IA::TT1, I::TT2) where {TT1,TT2} =
3035
(I[1] >= lbound(A, d, IA[1])) & (I[1] <= ubound(A, d, IA[1])) & _checkbounds(A, d+1, Base.tail(IA), Base.tail(I))

src/gridded/indexing.jl

+5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ end
3636
:(getindex(itp, $(args...)))
3737
end
3838

39+
function (itp::GriddedInterpolation{T,N,TCoefs,IT,K,pad})(args...) where {T,N,TCoefs,IT,K,pad}
40+
# support function calls
41+
itp[args...]
42+
end
43+
3944
# Indexing with vector inputs. Here, it pays to pre-process the input indexes,
4045
# because N*n is much smaller than n^N.
4146
# TODO: special-case N=1, because there is no reason to separately cache the indexes.

src/scaling/scaling.jl

+4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ end
4545

4646
getindex(sitp::ScaledInterpolation{T,1}, x::Number, y::Int) where {T} = y == 1 ? sitp[x] : throw(BoundsError())
4747

48+
function (sitp::ScaledInterpolation{T,N,ITPT,IT})(args...) where {T,N,ITPT,IT<:DimSpec}
49+
sitp[args...]
50+
end
51+
4852
size(sitp::ScaledInterpolation, d) = size(sitp.itp, d)
4953
lbound(sitp::ScaledInterpolation{T,N,ITPT,IT,OnGrid}, d) where {T,N,ITPT,IT} = 1 <= d <= N ? sitp.ranges[d][1] : throw(BoundsError())
5054
lbound(sitp::ScaledInterpolation{T,N,ITPT,IT,OnCell}, d) where {T,N,ITPT,IT} = 1 <= d <= N ? sitp.ranges[d][1] - boundstep(sitp.ranges[d]) : throw(BoundsError())
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
module ExtrapFunctionCallSyntax
2+
3+
using Base.Test, Interpolations, DualNumbers
4+
5+
# Test if b-spline interpolation by function syntax yields identical results
6+
f(x) = sin((x-3)*2pi/9 - 1)
7+
xmax = 10
8+
A = Float64[f(x) for x in 1:xmax]
9+
itpg = interpolate(A, BSpline(Linear()), OnGrid())
10+
schemes = (Flat,Line,Free)
11+
12+
for T in (Cubic, Quadratic), GC in (OnGrid, OnCell)
13+
for etp in map(S -> @inferred(interpolate(A, BSpline(T(S())), GC())), schemes),
14+
x in linspace(1,xmax,100)
15+
@test (getindex(etp, x)) == etp(x)
16+
end
17+
end
18+
19+
for T in (Constant, Linear), GC in (OnGrid, OnCell), x in linspace(1,xmax,100)
20+
etp = interpolate(A, BSpline(T()), GC())
21+
@test (getindex(etp, x)) == etp(x)
22+
end
23+
24+
end

test/b-splines/runtests.jl

+1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ include("cubic.jl")
77
include("mixed.jl")
88
include("multivalued.jl")
99
include("non1.jl")
10+
include("function-call-syntax.jl")
1011

1112
end
+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
module ExtrapFunctionCallSyntax
2+
3+
using Base.Test, Interpolations, DualNumbers
4+
5+
# Test if extrapolation by function syntax yields identical results
6+
f(x) = sin((x-3)*2pi/9 - 1)
7+
xmax = 10
8+
A = Float64[f(x) for x in 1:xmax]
9+
itpg = interpolate(A, BSpline(Linear()), OnGrid())
10+
11+
schemes = (
12+
Flat,
13+
Linear,
14+
Reflect,
15+
Periodic
16+
)
17+
18+
for etp in map(E -> @inferred(extrapolate(itpg, E())), schemes),
19+
x in [
20+
# In-bounds evaluation
21+
3.4, 3, dual(3.1),
22+
# Out-of-bounds evaluation
23+
-3.4, -3, dual(-3,1),
24+
13.4, 13, dual(13,1)
25+
]
26+
@test (getindex(etp, x)) == etp(x)
27+
end
28+
29+
etpg = extrapolate(itpg, Flat())
30+
@test typeof(etpg) <: AbstractExtrapolation
31+
32+
@test etpg(-3) == etpg(-4.5) == etpg(0.9) == etpg(1.0) == A[1]
33+
@test etpg(10.1) == etpg(11) == etpg(148.298452) == A[end]
34+
35+
etpf = @inferred(extrapolate(itpg, NaN))
36+
@test typeof(etpf) <: Interpolations.FilledExtrapolation
37+
@test parent(etpf) === itpg
38+
39+
@test @inferred(size(etpf)) == (xmax,)
40+
@test isnan(@inferred(etpf(-2.5)))
41+
@test isnan(etpf(0.999))
42+
@test @inferred(etpf(1)) f(1)
43+
@test etpf(10) f(10)
44+
@test isnan(@inferred(etpf(10.001)))
45+
46+
@test etpf(2.5,1) == etpf(2.5) # for show method
47+
@test_throws BoundsError etpf(2.5,2)
48+
@test_throws BoundsError etpf(2.5,2,1)
49+
50+
x = @inferred(etpf(dual(-2.5,1)))
51+
@test isa(x, Dual)
52+
53+
etpl = extrapolate(itpg, Linear())
54+
k_lo = A[2] - A[1]
55+
x_lo = -3.2
56+
@test etpl(x_lo) A[1] + k_lo * (x_lo - 1)
57+
k_hi = A[end] - A[end-1]
58+
x_hi = xmax + 5.7
59+
@test etpl(x_hi) A[end] + k_hi * (x_hi - xmax)
60+
61+
xmax, ymax = 8,8
62+
g(x, y) = (x^2 + 3x - 8) * (-2y^2 + y + 1)
63+
64+
itp2g = interpolate(Float64[g(x,y) for x in 1:xmax, y in 1:ymax], (BSpline(Quadratic(Free())), BSpline(Linear())), OnGrid())
65+
etp2g = extrapolate(itp2g, (Linear(), Flat()))
66+
67+
@test @inferred(etp2g(-0.5,4)) itp2g(1,4) - 1.5 * epsilon(etp2g(dual(1,1),4))
68+
@test @inferred(etp2g(5,100)) itp2g(5,ymax)
69+
70+
etp2ud = extrapolate(itp2g, ((Linear(), Flat()), Flat()))
71+
@test @inferred(etp2ud(-0.5,4)) itp2g(1,4) - 1.5 * epsilon(etp2g(dual(1,1),4))
72+
@test @inferred(etp2ud(5, -4)) == etp2ud(5,1)
73+
@test @inferred(etp2ud(100, 4)) == etp2ud(8,4)
74+
@test @inferred(etp2ud(-.5, 100)) == itp2g(1,8) - 1.5 * epsilon(etp2g(dual(1,1),8))
75+
76+
etp2ll = extrapolate(itp2g, Linear())
77+
@test @inferred(etp2ll(-0.5,100)) (itp2g(1,8) - 1.5 * epsilon(etp2ll(dual(1,1),8))) + (100 - 8) * epsilon(etp2ll(1,dual(8,1)))
78+
79+
# Allow element types that don't support conversion to Int (#87):
80+
etp87g = extrapolate(interpolate([1.0im, 2.0im, 3.0im], BSpline(Linear()), OnGrid()), 0.0im)
81+
@test @inferred(etp87g(1)) == 1.0im
82+
@test @inferred(etp87g(1.5)) == 1.5im
83+
@test @inferred(etp87g(0.75)) == 0.0im
84+
@test @inferred(etp87g(3.25)) == 0.0im
85+
86+
etp87c = extrapolate(interpolate([1.0im, 2.0im, 3.0im], BSpline(Linear()), OnCell()), 0.0im)
87+
@test @inferred(etp87c(1)) == 1.0im
88+
@test @inferred(etp87c(1.5)) == 1.5im
89+
@test @inferred(etp87c(0.75)) == 0.75im
90+
@test @inferred(etp87c(3.25)) == 3.25im
91+
@test @inferred(etp87g(0)) == 0.0im
92+
@test @inferred(etp87g(3.7)) == 0.0im
93+
94+
# Make sure it works with Gridded too
95+
etp100g = extrapolate(interpolate(([10;20],),[100;110], Gridded(Linear())), Flat())
96+
@test @inferred(etp100g(5)) == 100
97+
@test @inferred(etp100g(15)) == 105
98+
@test @inferred(etp100g(25)) == 110
99+
# issue #178
100+
a = randn(10,10) + im*rand(10,10)
101+
etp = @inferred(extrapolate(interpolate((1:10, 1:10), a, Gridded(Linear())), 0.0))
102+
@test @inferred(etp(-1,0)) === 0.0+0.0im
103+
104+
end

test/extrapolation/runtests.jl

+1
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,4 @@ end
103103

104104
include("type-stability.jl")
105105
include("non1.jl")
106+
include("function-call-syntax.jl")

test/gridded/function-call-syntax.jl

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
module GriddedFunctionCallSyntax
2+
3+
using Interpolations, Base.Test
4+
5+
for D in (Constant, Linear), G in (OnCell, OnGrid)
6+
## 1D
7+
a = rand(5)
8+
knots = (collect(linspace(1,length(a),length(a))),)
9+
itp = @inferred(interpolate(knots, a, Gridded(D())))
10+
@inferred(getindex(itp, 2))
11+
@inferred(getindex(itp, CartesianIndex((2,))))
12+
for i = 2:length(a)-1
13+
@test itp(i) a[i]
14+
@test itp(CartesianIndex((i,))) a[i]
15+
end
16+
@inferred(getindex(itp, knots...))
17+
@test itp[knots...] a
18+
# compare scalar indexing and vector indexing
19+
x = knots[1]+0.1
20+
v = itp(x)
21+
for i = 1:length(x)
22+
@test v[i] itp(x[i])
23+
end
24+
# check the fallback vector indexing
25+
x = [2.3,2.2] # non-increasing order
26+
v = itp[x]
27+
for i = 1:length(x)
28+
@test v[i] itp(x[i])
29+
end
30+
# compare against BSpline
31+
itpb = @inferred(interpolate(a, BSpline(D()), G()))
32+
for x in linspace(1.1,4.9,101)
33+
@test itp(x) itpb(x)
34+
end
35+
36+
## 2D
37+
A = rand(6,5)
38+
knots = (collect(linspace(1,size(A,1),size(A,1))),collect(linspace(1,size(A,2),size(A,2))))
39+
itp = @inferred(interpolate(knots, A, Gridded(D())))
40+
@test parent(itp) === A
41+
@inferred(getindex(itp, 2, 2))
42+
@inferred(getindex(itp, CartesianIndex((2,2))))
43+
for j = 2:size(A,2)-1, i = 2:size(A,1)-1
44+
@test itp(i,j) A[i,j]
45+
@test itp(CartesianIndex((i,j))) A[i,j]
46+
end
47+
@test itp[knots...] A
48+
@inferred(getindex(itp, knots...))
49+
# compare scalar indexing and vector indexing
50+
x, y = knots[1]+0.1, knots[2]+0.6
51+
v = itp(x,y)
52+
for j = 1:length(y), i = 1:length(x)
53+
@test v[i,j] itp(x[i],y[j])
54+
end
55+
# check the fallback vector indexing
56+
x = [2.3,2.2] # non-increasing order
57+
y = [3.5,2.8]
58+
v = itp[x,y]
59+
for j = 1:length(y), i = 1:length(x)
60+
@test v[i,j] itp(x[i],y[j])
61+
end
62+
# compare against BSpline
63+
itpb = @inferred(interpolate(A, BSpline(D()), G()))
64+
for y in linspace(1.1,5.9,101), x in linspace(1.1,4.9,101)
65+
@test itp(x,y) itpb(x,y)
66+
end
67+
68+
A = rand(8,20)
69+
knots = ([x^2 for x = 1:8], [0.2y for y = 1:20])
70+
itp = interpolate(knots, A, Gridded(D()))
71+
@test itp(4,1.2) A[2,6]
72+
end
73+
74+
end

test/gridded/runtests.jl

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ module GriddedTests
22

33
include("gridded.jl")
44
include("mixed.jl")
5+
include("function-call-syntax.jl")
56

6-
end
7+
end

0 commit comments

Comments
 (0)