Skip to content

Commit 614f7db

Browse files
author
Tomas Lycken
committed
Merge pull request #50 from tlycken/more-extraps
RFC: More extrapolation behaviors
2 parents 883501d + e763408 commit 614f7db

17 files changed

+451
-71
lines changed

src/Interpolations.jl

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,14 @@ lbound{T,N}(itp::AbstractInterpolation{T,N}, d) = convert(T, 1)
6767
ubound{T,N}(itp::AbstractInterpolation{T,N}, d) = convert(T, size(itp, d))
6868
itptype{T,N,IT,GT}(itp::AbstractInterpolation{T,N,IT,GT}) = IT
6969
gridtype{T,N,IT,GT}(itp::AbstractInterpolation{T,N,IT,GT}) = GT
70-
71-
@inline gradient{T,N}(itp::AbstractInterpolation{T,N}, xs...) = gradient!(Array(T,N), itp, xs...)
70+
count_interp_dims{T<:AbstractInterpolation}(::Type{T}, N) = N
71+
72+
@generated function gradient{T,N}(itp::AbstractInterpolation{T,N}, xs...)
73+
n = count_interp_dims(itp, N)
74+
Tg = promote_type(T, [x <: AbstractArray ? eltype(x) : x for x in xs]...)
75+
xargs = [:(xs[$d]) for d in 1:length(xs)]
76+
:(gradient!(Array($Tg,$n), itp, $(xargs...)))
77+
end
7278

7379
include("nointerp/nointerp.jl")
7480
include("b-splines/b-splines.jl")

src/b-splines/b-splines.jl

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ ubound{T,N,TCoefs,IT}(itp::BSplineInterpolation{T,N,TCoefs,IT,OnGrid}, d) = conv
4141
lbound{T,N,TCoefs,IT}(itp::BSplineInterpolation{T,N,TCoefs,IT,OnCell}, d) = convert(T, .5)
4242
ubound{T,N,TCoefs,IT}(itp::BSplineInterpolation{T,N,TCoefs,IT,OnCell}, d) = convert(T, size(itp, d) + .5)
4343

44+
count_interp_dims{T,N,TCoefs,IT<:DimSpec{InterpolationType},GT<:DimSpec{GridType},pad}(::Type{BSplineInterpolation{T,N,TCoefs,IT,GT,pad}}, n) = count_interp_dims(IT, n)
45+
4446
@generated function size{T,N,TCoefs,IT,GT,pad}(itp::BSplineInterpolation{T,N,TCoefs,IT,GT,pad}, d)
4547
quote
4648
d <= $N ? size(itp.coefs, d) - 2*padextract($pad, d) : 1
@@ -71,23 +73,6 @@ function interpolate!{IT<:DimSpec{BSpline},GT<:DimSpec{GridType}}(A::AbstractArr
7173
interpolate!(tweight(A), A, IT, GT)
7274
end
7375

74-
define_indices{IT}(::Type{IT}, N, pad) = Expr(:block, Expr[define_indices_d(iextract(IT, d), d, padextract(pad, d)) for d = 1:N]...)
75-
76-
coefficients{IT}(::Type{IT}, N) = Expr(:block, Expr[coefficients(iextract(IT, d), N, d) for d = 1:N]...)
77-
78-
function gradient_coefficients{IT<:DimSpec{BSpline}}(::Type{IT}, N, dim)
79-
exs = Expr[d==dim ? gradient_coefficients(iextract(IT, dim), d) :
80-
coefficients(iextract(IT, d), N, d) for d = 1:N]
81-
Expr(:block, exs...)
82-
end
83-
84-
index_gen{IT}(::Type{IT}, N::Integer, offsets...) = index_gen(iextract(IT, min(length(offsets)+1, N)), IT, N, offsets...)
85-
86-
@generated function gradient{T,N,TCoefs,IT,GT,pad}(itp::BSplineInterpolation{T,N,TCoefs,IT,GT,pad}, xs...)
87-
n = count_interp_dims(IT, N)
88-
:(gradient!(Array(T,$n), itp, xs...))
89-
end
90-
9176
include("constant.jl")
9277
include("linear.jl")
9378
include("quadratic.jl")

src/b-splines/indexing.jl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ import Base.getindex
44

55
Base.linearindexing{T<:AbstractInterpolation}(::Type{T}) = Base.LinearSlow()
66

7+
define_indices{IT}(::Type{IT}, N, pad) = Expr(:block, Expr[define_indices_d(iextract(IT, d), d, padextract(pad, d)) for d = 1:N]...)
8+
9+
coefficients{IT}(::Type{IT}, N) = Expr(:block, Expr[coefficients(iextract(IT, d), N, d) for d = 1:N]...)
10+
11+
function gradient_coefficients{IT<:DimSpec{BSpline}}(::Type{IT}, N, dim)
12+
exs = Expr[d==dim ? gradient_coefficients(iextract(IT, dim), d) :
13+
coefficients(iextract(IT, d), N, d) for d = 1:N]
14+
Expr(:block, exs...)
15+
end
16+
17+
index_gen{IT}(::Type{IT}, N::Integer, offsets...) = index_gen(iextract(IT, min(length(offsets)+1, N)), IT, N, offsets...)
18+
719
function getindex_impl{T,N,TCoefs,IT<:DimSpec{BSpline},GT<:DimSpec{GridType},Pad}(itp::Type{BSplineInterpolation{T,N,TCoefs,IT,GT,Pad}})
820
meta = Expr(:meta, :inline)
921
quote
@@ -57,7 +69,6 @@ function gradient_impl{T,N,TCoefs,IT<:DimSpec{BSpline},GT<:DimSpec{GridType},Pad
5769
end
5870
end
5971

60-
6172
@generated function gradient!{T,N}(g::AbstractVector, itp::BSplineInterpolation{T,N}, xs::Number...)
6273
length(xs) == N || error("Can only be called with $N indexes")
6374
gradient_impl(itp)

src/extrapolation/constant.jl

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/extrapolation/error.jl

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/extrapolation/extrap_prep.jl

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
"""
2+
The `extrap_prep` function is used by `getindex_impl` to generate the body of
3+
the `getindex` function for extrapolation objects.
4+
5+
The methods of `extrap_prep` work in "layers", iteratively working out the exact
6+
expression needed.
7+
8+
The first layer takes a specification of the extrapolation scheme(s) to be used
9+
and a `Val` object that specifies the dimensionality of the extrapolation object:
10+
`extrap_prep{T,N}(::Type{T}, Val{N})`. These methods only dispatch to the second
11+
layer, and need not be extended for new schemes.
12+
13+
The second layer also takes a `Val` object that specifies a single dimension on
14+
which to work: `extrap_prep{T,N,d}(::Type{T}, ::Val{N}, ::Val{d}). The methods
15+
with this signature in src/extrapolation/extrap_prep.jl simply expand into a
16+
block with sub-expressions for handling too-low and too-high values separately
17+
(the third layer), but specific interpolation schemes can provide more specific
18+
methods for this layer that handle both ends simultaneously. For example, the
19+
`Flat` scheme has a layer-2 method that uses `clamp` to restrict the coordinate
20+
when used in both directions, but uses `min` and `max` when handling each end
21+
separately.
22+
23+
The third layer, to which the second dispatches if no scheme-specific method is
24+
found, adds a final `Val` object with a symbol `:lo` or `:hi`:
25+
`extrap_prep{T,N,d,l}(::Type{T}, ::Val{N}, ::Val{d}, ::Val{l})`. These methods
26+
must be specified for each extrapolation scheme. However, the general framework
27+
takes care of expanding all possible tuple combinations, so individual schemes
28+
need only care about e.g. `T==Flat`.
29+
30+
In addition to these methods, there is a similar three-layer method hierarchy
31+
for gradient evaluation, in which a `Val{:gradient}` is prepended to the other
32+
arguments:
33+
`extrap_prep{T,N,d,l}(::Val{:gradient}`, ::Type{T}, ::Val{N}, ::Val{d}, ::Val{l})`
34+
If nothing else is specified for the individual schemes, these methods forward
35+
to the same methods without the `:gradient` argument, i.e. the same behavior as
36+
for value extrapolation. This works well with all schemes that are simple
37+
coordinate transformations, but for anything else methods for the low- and high-
38+
value cases need to be implemented for each scheme.
39+
""" extrap_prep
40+
41+
extrap_prep{T}(::Type{T}, n::Val{1}) = extrap_prep(T, n, Val{1}())
42+
extrap_prep{T}(::Type{Tuple{T}}, n::Val{1}) = extrap_prep(T, n)
43+
extrap_prep{T}(::Type{Tuple{T,T}}, n::Val{1}) = extrap_prep(T, n)
44+
extrap_prep{T}(::Type{Tuple{Tuple{T,T}}}, n::Val{1}) = extrap_prep(T, n)
45+
function extrap_prep{S,T}(::Type{Tuple{S,T}}, n::Val{1})
46+
quote
47+
$(extrap_prep(S, n, Val{1}(), Val{:lo}()))
48+
$(extrap_prep(T, n, Val{1}(), Val{:hi}()))
49+
end
50+
end
51+
extrap_prep{S,T}(::Type{Tuple{Tuple{S,T}}}, n::Val{1}) = extrap_prep(Tuple{S,T}, n)
52+
53+
# needed for ambiguity resolution
54+
extrap_prep{T<:Tuple}(::Type{T}, ::Val{1}) = :(throw(ArgumentError("The 1-dimensional extrap configuration $T is not supported")))
55+
56+
function extrap_prep{T,N}(::Type{T}, n::Val{N})
57+
exprs = Expr[]
58+
for d in 1:N
59+
push!(exprs, extrap_prep(T, n, Val{d}()))
60+
end
61+
return Expr(:block, exprs...)
62+
end
63+
function extrap_prep{N,T<:Tuple}(::Type{T}, n::Val{N})
64+
length(T.parameters) == N || return :(throw(ArgumentError("The $N-dimensional extrap configuration $T is not supported - must be a tuple of length $N (was length $(lenght(T.parameters)))")))
65+
exprs = Expr[]
66+
for d in 1:N
67+
Tdim = T.parameters[d]
68+
if Tdim <: Tuple
69+
length(Tdim.parameters) == 2 || return :(throw(ArgumentError("The extrap configuration $Tdim for dimension $d is not supported - must be a tuple of length 2 or a simple configuration type")))
70+
if Tdim.parameters[1] != Tdim.parameters[2]
71+
push!(exprs, extrap_prep(Tdim, n, Val{d}()))
72+
else
73+
push!(exprs, extrap_prep(Tdim.parameters[1], n, Val{d}()))
74+
end
75+
else
76+
push!(exprs, extrap_prep(Tdim, n, Val{d}()))
77+
end
78+
end
79+
return Expr(:block, exprs...)
80+
end
81+
extrap_prep{T,N,d}(::Type{T}, n::Val{N}, dim::Val{d}) = extrap_prep(Tuple{T,T}, n, dim)
82+
function extrap_prep{S,T,N,d}(::Type{Tuple{S,T}}, n::Val{N}, dim::Val{d})
83+
quote
84+
$(extrap_prep(S, n, dim, Val{:lo}()))
85+
$(extrap_prep(T, n, dim, Val{:hi}()))
86+
end
87+
end
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# See ?extrap_prep for documentation for all these methods
2+
3+
extrap_prep{T}(g::Val{:gradient}, ::Type{T}, n::Val{1}) = extrap_prep(g, T, n, Val{1}())
4+
extrap_prep{T}(g::Val{:gradient}, ::Type{Tuple{T}}, n::Val{1}) = extrap_prep(g, T, n)
5+
extrap_prep{T}(g::Val{:gradient}, ::Type{Tuple{T,T}}, n::Val{1}) = extrap_prep(g, T, n)
6+
extrap_prep{T}(g::Val{:gradient}, ::Type{Tuple{Tuple{T,T}}}, n::Val{1}) = extrap_prep(g, T, n)
7+
function extrap_prep{S,T}(g::Val{:gradient}, ::Type{Tuple{S,T}}, ::Val{1})
8+
quote
9+
$(extrap_prep(g, S, n, Val{1}(), Val{:lo}()))
10+
$(extrap_prep(g, T, n, Val{1}(), Val{:hi}()))
11+
end
12+
end
13+
extrap_prep{S,T}(g::Val{:gradient}, ::Type{Tuple{Tuple{S,T}}}, n::Val{1}) = extrap_prep(g, Tuple{S,T}, n)
14+
# needed for ambiguity resolution
15+
extrap_prep{T<:Tuple}(::Val{:gradient}, ::Type{T}, ::Val{1}) = :(throw(ArgumentError("The 1-dimensional extrap configuration $T is not supported")))
16+
17+
18+
function extrap_prep{T,N}(g::Val{:gradient}, ::Type{T}, n::Val{N})
19+
Expr(:block, [extrap_prep(g, T, n, Val{d}()) for d in 1:N]...)
20+
end
21+
22+
function extrap_prep{T<:Tuple,N}(g::Val{:gradient}, ::Type{T}, n::Val{N})
23+
length(T.parameters) == N || return :(throw(ArgumentError("The $N-dimensional extrap configuration $T is not supported")))
24+
exprs = Expr[]
25+
for d in 1:N
26+
Tdim = T.parameters[d]
27+
if Tdim <: Tuple
28+
length(Tdim.parameters) == 2 || return :(throw(ArgumentError("The extrap configuration $Tdim for dimension $d is not supported - must be a tuple of length 2 or a simple configuration type"))
29+
)
30+
if Tdim.parameters[1] != Tdim.parameters[2]
31+
push!(exprs, extrap_prep(g, Tdim, n, Val{d}()))
32+
else
33+
push!(exprs, extrap_prep(g, Tdim.parameters[1], n, Val{d}()))
34+
end
35+
else
36+
push!(exprs, extrap_prep(g, Tdim, n, Val{d}()))
37+
end
38+
end
39+
return Expr(:block, exprs...)
40+
end
41+
42+
function extrap_prep{S,T,N,d}(g::Val{:gradient}, ::Type{Tuple{S,T}}, n::Val{N}, dim::Val{d})
43+
quote
44+
$(extrap_prep(g, S, n, dim, Val{:lo}()))
45+
$(extrap_prep(g, T, n, dim, Val{:hi}()))
46+
end
47+
end
48+
49+
extrap_prep{T,N,d}(g::Val{:gradient}, ::Type{T}, n::Val{N}, dim::Val{d}) = extrap_prep(g, Tuple{T,T}, n, dim)
50+
extrap_prep(g::Val{:gradient}, args...) = extrap_prep(args...)

src/extrapolation/extrapolation.jl

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,77 @@
11
export Throw,
22
FilledExtrapolation # for direct control over typeof(fillvalue)
33

4-
include("error.jl")
4+
type Extrapolation{T,N,ITPT,IT,GT,ET} <: AbstractInterpolationWrapper{T,N,ITPT,IT,GT}
5+
itp::ITPT
6+
end
7+
Extrapolation{T,ITPT,IT,GT,ET}(::Type{T}, N, itp::ITPT, ::Type{IT}, ::Type{GT}, ::Type{ET}) =
8+
Extrapolation{T,N,ITPT,IT,GT,ET}(itp)
59

6-
include("constant.jl")
10+
"""
11+
`extrapolate(itp, scheme)` adds extrapolation behavior to an interpolation object, according to the provided scheme.
712
8-
include("indexing.jl")
13+
The scheme can take any of these values:
14+
15+
* `Throw` - throws a BoundsError for out-of-bounds indices
16+
* `Flat` - for constant extrapolation, taking the closest in-bounds value
17+
* `Linear` - linear extrapolation (the wrapped interpolation object must support gradient)
18+
* `Reflect` - reflecting extrapolation
19+
* `Periodic` - periodic extrapolation
20+
21+
You can also combine schemes in tuples. For example, the scheme `Tuple{Linear, Flat}` will use linear extrapolation in the first dimension, and constant in the second.
22+
23+
Finally, you can specify different extrapolation behavior in different direction. `Tuple{Tuple{Linear,Flat}, Flat}` will extrapolate linearly in the first dimension if the index is too small, but use constant etrapolation if it is too large, and always use constant extrapolation in the second dimension.
24+
"""
25+
extrapolate{T,N,IT,GT,ET}(itp::AbstractInterpolation{T,N,IT,GT}, ::Type{ET}) =
26+
Extrapolation(T,N,itp,IT,GT,ET)
27+
28+
include("throw.jl")
29+
include("flat.jl")
30+
include("linear.jl")
31+
include("reflect.jl")
32+
include("periodic.jl")
33+
34+
include("extrap_prep.jl")
35+
include("extrap_prep_gradient.jl")
36+
37+
"""
38+
`getindex_impl(::Type{E<:Extrapolation}, xs...)`
39+
40+
Generates an expression to be used
41+
as the function body of the getindex method for the given type of extrapolation
42+
and indices. The heavy lifting is done by the `extrap_prep` function; see
43+
`?extrap_prep` for details.
44+
"""
45+
function getindex_impl{T,N,ITPT,IT,GT,ET}(etp::Type{Extrapolation{T,N,ITPT,IT,GT,ET}}, xs...)
46+
coords = [symbol("xs_",d) for d in 1:N]
47+
quote
48+
@nexprs $N d->(xs_d = xs[d])
49+
$(extrap_prep(ET, Val{N}()))
50+
etp.itp[$(coords...)]
51+
end
52+
end
53+
54+
@generated function getindex{T,N,ITPT,IT,GT,ET}(etp::Extrapolation{T,N,ITPT,IT,GT,ET}, xs...)
55+
getindex_impl(etp, xs...)
56+
end
57+
58+
59+
function gradient!_impl{T,N,ITPT,IT,GT,ET}(g, etp::Type{Extrapolation{T,N,ITPT,IT,GT,ET}}, xs...)
60+
coords = [symbol("xs_", d) for d in 1:N]
61+
quote
62+
@nexprs $N d->(xs_d = xs[d])
63+
$(extrap_prep(Val{:gradient}(), ET, Val{N}()))
64+
gradient!(g, etp.itp, $(coords...))
65+
end
66+
end
67+
68+
69+
@generated function gradient!{T,N,ITPT,IT,GT,ET}(g::AbstractVector, etp::Extrapolation{T,N,ITPT,IT,GT,ET}, xs...)
70+
gradient!_impl(g, etp, xs...)
71+
end
72+
73+
lbound(etp::Extrapolation, d) = lbound(etp.itp, d)
74+
ubound(etp::Extrapolation, d) = ubound(etp.itp, d)
75+
size(etp::Extrapolation, d) = size(etp.itp, d)
976

1077
include("filled.jl")

src/extrapolation/flat.jl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
function extrap_prep{N,d}(::Type{Flat}, ::Val{N}, ::Val{d})
2+
xs_d = symbol("xs_", d)
3+
:($xs_d = clamp($xs_d, lbound(etp, $d), ubound(etp, $d)))
4+
end
5+
6+
function extrap_prep{N,d}(::Type{Flat}, ::Val{N}, ::Val{d}, ::Val{:lo})
7+
xs_d = symbol("xs_", d)
8+
:($xs_d = max($xs_d, lbound(etp, $d)))
9+
end
10+
11+
function extrap_prep{N,d}(::Type{Flat}, ::Val{N}, ::Val{d}, ::Val{:hi})
12+
xs_d = symbol("xs_", d)
13+
:($xs_d = min($xs_d, ubound(etp, $d)))
14+
end
15+
16+
function extrap_prep{N,d}(::Val{:gradient}, ::Type{Flat}, ::Val{N}, ::Val{d}, ::Val{:lo})
17+
coords = [symbol("xs_", k) for k in 1:N]
18+
xs_d = coords[d]
19+
20+
quote
21+
if $xs_d < lbound(etp, $d)
22+
$xs_d = lbound(etp, $d)
23+
gradient!(g, etp.itp, $(coords...))
24+
g[$d] = 0
25+
return g
26+
end
27+
end
28+
end
29+
30+
function extrap_prep{N,d}(::Val{:gradient}, ::Type{Flat}, ::Val{N}, ::Val{d}, ::Val{:hi})
31+
coords = [symbol("xs_", k) for k in 1:N]
32+
xs_d = coords[d]
33+
34+
quote
35+
if $xs_d > ubound(etp, $d)
36+
$xs_d = ubound(etp, $d)
37+
gradient!(g, etp.itp, $(coords...))
38+
g[$d] = 0
39+
return g
40+
end
41+
end
42+
end

src/extrapolation/indexing.jl

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +0,0 @@
1-
@generated function getindex{T}(etp::AbstractExtrapolation{T,1}, x)
2-
quote
3-
$(extrap_prep(etp, x))
4-
etp.itp[x]
5-
end
6-
end
7-
8-
@generated function getindex{T,N,ITP,GT}(etp::AbstractExtrapolation{T,N,ITP,GT}, xs...)
9-
quote
10-
$(extrap_prep(etp, xs...))
11-
etp.itp[xs...]
12-
end
13-
end

0 commit comments

Comments
 (0)