Skip to content

Commit 90e7793

Browse files
authored
Merge branch 'master' into master
2 parents 54042f4 + 881a65a commit 90e7793

File tree

17 files changed

+356
-84
lines changed

17 files changed

+356
-84
lines changed

docs/src/NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../NEWS.md

docs/src/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![PkgEval Status](http://pkg.julialang.org/badges/Interpolations_0.4.svg)](http://pkg.julialang.org/?pkg=Interpolations)
66
[![Interpolations](http://pkg.julialang.org/badges/Interpolations_0.5.svg)](http://pkg.julialang.org/?pkg=Interpolations)
77

8-
**NEWS** v0.9 was a breaking release. See the [news](../../NEWS.md) for details on how to update.
8+
**NEWS** v0.9 was a breaking release. See the [news](NEWS.md) for details on how to update.
99

1010
This package implements a variety of interpolation schemes for the
1111
Julia language. It has the goals of ease-of-use, broad algorithmic

perf/benchmarks.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ end
3030
strip_prefix(str::AbstractString) = replace(str, "Interpolations."=>"")
3131
benchstr(::Type{T}) where {T<:Interpolations.GridType} = strip_prefix(string(T))
3232

33-
benchstr(::Type{Constant}) = "Constant()"
33+
benchstr(::Type{<:Constant}) = "Constant()"
3434
benchstr(::Type{Linear}) = "Linear()"
3535
benchstr(::Type{Quadratic{BC}}, ::Type{GT}) where {BC<:Interpolations.BoundaryCondition,GT<:Interpolations.GridType} =
3636
string("Quadratic(", strip_prefix(string(BC)), "(", strip_prefix(string(GT)), "()))")
3737
benchstr(::Type{Cubic{BC}}, ::Type{GT}) where {BC<:Interpolations.BoundaryCondition,GT<:Interpolations.GridType} =
3838
string("Cubic(", strip_prefix(string(BC)), "(", strip_prefix(string(GT)), "()))")
3939

40-
groupstr(::Type{Constant}) = "constant"
40+
groupstr(::Type{<:Constant}) = "constant"
4141
groupstr(::Type{Linear}) = "linear"
4242
groupstr(::Type{Quadratic}) = "quadratic"
4343
groupstr(::Type{Cubic}) = "cubic"

src/b-splines/b-splines.jl

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ function interpolate(::Type{TWeights}, ::Type{TC}, A, it::IT) where {TWeights,TC
161161
BSplineInterpolation(TWeights, Apad, it, axes(A))
162162
end
163163

164+
function interpolate(::Type{TWeights}, ::Type{TC}, A, it::IT, λ::Real, k::Int) where {TWeights,TC,IT<:DimSpec{BSpline}}
165+
Apad = prefilter(TWeights, TC, A, it, λ, k)
166+
BSplineInterpolation(TWeights, Apad, it, axes(A))
167+
end
168+
164169
"""
165170
itp = interpolate(A, interpmode)
166171
@@ -179,6 +184,33 @@ function interpolate(A::AbstractArray, it::IT) where {IT<:DimSpec{BSpline}}
179184
interpolate(tweight(A), tcoef(A), A, it)
180185
end
181186

187+
"""
188+
itp = interpolate(A, interpmode, gridstyle, λ, k)
189+
190+
Interpolate an array `A` in the mode determined by `interpmode` and `gridstyle`
191+
with regularization following [1], of order `k` and constant `λ`.
192+
`interpmode` may be one of
193+
194+
- `BSpline(NoInterp())`
195+
- `BSpline(Linear())`
196+
- `BSpline(Quadratic(BC()))` (see [`BoundaryCondition`](@ref))
197+
- `BSpline(Cubic(BC()))`
198+
199+
It may also be a tuple of such values, if you want to use different interpolation schemes along each axis.
200+
201+
`gridstyle` should be one of `OnGrid()` or `OnCell()`.
202+
203+
`k` corresponds to the derivative to penalize. In the limit λ->∞, the interpolation function is
204+
a polynomial of order `k-1`. A value of 2 is the most common.
205+
206+
`λ` is non-negative. If its value is zero, it falls back to non-regularized interpolation.
207+
208+
[1] https://projecteuclid.org/euclid.ss/1038425655.
209+
"""
210+
function interpolate(A::AbstractArray, it::IT, λ::Real, k::Int) where {IT<:DimSpec{BSpline}}
211+
interpolate(tweight(A), tcoef(A), A, it, λ, k)
212+
end
213+
182214
# We can't just return a tuple-of-types due to julia #12500
183215
tweight(A::AbstractArray) = Float64
184216
tweight(A::AbstractArray{T}) where T<:AbstractFloat = T
@@ -201,6 +233,16 @@ function interpolate!(A::AbstractArray, it::IT) where {IT<:DimSpec{BSpline}}
201233
interpolate!(tweight(A), A, it)
202234
end
203235

236+
function interpolate!(::Type{TWeights}, A::AbstractArray, it::IT, λ::Real, k::Int) where {TWeights,IT<:DimSpec{BSpline}}
237+
# Set the bounds of the interpolant inward, if necessary
238+
axsA = axes(A)
239+
axspad = padded_axes(axsA, it)
240+
BSplineInterpolation(TWeights, prefilter!(TWeights, A, it, λ, k), it, fix_axis.(padinset.(axsA, axspad)))
241+
end
242+
function interpolate!(A::AbstractArray, it::IT, λ::Real, k::Int) where {IT<:DimSpec{BSpline}}
243+
interpolate!(tweight(A), A, it, λ, k)
244+
end
245+
204246
lut!(dl, d, du) = lu!(Tridiagonal(dl, d, du), Val(false))
205247

206248
include("constant.jl")
@@ -211,6 +253,6 @@ include("indexing.jl")
211253
include("prefiltering.jl")
212254
include("../filter1d.jl")
213255

214-
Base.parent(A::BSplineInterpolation{T,N,TCoefs,UT}) where {T,N,TCoefs,UT<:Union{BSpline{Linear},BSpline{Constant}}} = A.coefs
256+
Base.parent(A::BSplineInterpolation{T,N,TCoefs,UT}) where {T,N,TCoefs,UT<:Union{BSpline{Linear},BSpline{<:Constant}}} = A.coefs
215257
Base.parent(A::BSplineInterpolation{T,N,TCoefs,UT}) where {T,N,TCoefs,UT} =
216258
throw(ArgumentError("The given BSplineInterpolation does not serve as a \"view\" for a parent array. This would only be true for Constant and Linear b-splines."))

src/b-splines/constant.jl

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,33 @@
1-
struct Constant <: Degree{0} end
1+
export Nearest, Previous, Next
2+
3+
abstract type ConstantInterpType end
4+
struct Nearest <: ConstantInterpType end
5+
struct Previous <: ConstantInterpType end
6+
struct Next <: ConstantInterpType end
7+
8+
struct Constant{T<:ConstantInterpType} <: Degree{0}
9+
Constant() = new{Nearest}()
10+
Constant{Previous}() = new{Previous}()
11+
Constant{Next}() = new{Next}()
12+
end
213

314
"""
415
Constant b-splines are *nearest-neighbor* interpolations, and effectively
516
return `A[round(Int,x)]` when interpolating.
617
"""
718
Constant
819

9-
function positions(::Constant, ax, x) # discontinuity occurs at half-integer locations
20+
function positions(c::Constant{Previous}, ax, x) # discontinuity occurs at integer locations
21+
xm = floorbounds(x, ax)
22+
δx = x - xm
23+
fast_trunc(Int, xm), δx
24+
end
25+
function positions(c::Constant{Next}, ax, x) # discontinuity occurs at integer locations
26+
xm = ceilbounds(x, ax)
27+
δx = x - xm
28+
fast_trunc(Int, xm), δx
29+
end
30+
function positions(c::Constant{Nearest}, ax, x) # discontinuity occurs at half-integer locations
1031
xm = roundbounds(x, ax)
1132
δx = x - xm
1233
fast_trunc(Int, xm), δx
@@ -16,4 +37,4 @@ value_weights(::Constant, δx) = (1,)
1637
gradient_weights(::Constant, δx) = (0,)
1738
hessian_weights(::Constant, δx) = (0,)
1839

19-
padded_axis(ax::AbstractUnitRange, ::BSpline{Constant}) = ax
40+
padded_axis(ax::AbstractUnitRange, ::BSpline{<:Constant}) = ax

src/b-splines/indexing.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,16 @@ function _floorbounds(x, ax::Union{Tuple{Real,Real}, AbstractUnitRange})
199199
ifelse(x < l, floor(x+h), floor(x+zero(h)))
200200
end
201201

202+
ceilbounds(x::Integer, ax::Tuple{Real,Real}) = x
203+
ceilbounds(x::Integer, ax::AbstractUnitRange) = x
204+
ceilbounds(x, ax::Tuple{Real,Real}) = _ceilbounds(x, ax)
205+
ceilbounds(x, ax::AbstractUnitRange) = _ceilbounds(x, ax)
206+
function _ceilbounds(x, ax::Union{Tuple{Real,Real}, AbstractUnitRange})
207+
u = last(ax)
208+
h = half(x)
209+
ifelse(x > u, ceil(x+h), ceil(x+zero(h)))
210+
end
211+
202212
half(x) = oneunit(x)/2
203213

204214
symmatrix(h::NTuple{1,Any}) = SMatrix{1,1}(h)

src/b-splines/prefiltering.jl

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,18 @@ function prefilter(
4040
prefilter!(TWeights, ret, it)
4141
end
4242

43+
function prefilter(
44+
::Type{TWeights}, ::Type{TC}, A::AbstractArray,
45+
it::Union{BSpline,Tuple{Vararg{Union{BSpline,NoInterp}}}},
46+
λ::Real, k::Int
47+
) where {TWeights,TC}
48+
ret = copy_with_padding(TC, A, it)
49+
prefilter!(TWeights, ret, it, λ, k)
50+
end
51+
4352
function prefilter!(
4453
::Type{TWeights}, ret::TCoefs, it::BSpline
4554
) where {TWeights,TCoefs<:AbstractArray}
46-
local buf, shape, retrs
4755
sz = size(ret)
4856
first = true
4957
for dim in 1:ndims(ret)
@@ -53,12 +61,57 @@ function prefilter!(
5361
ret
5462
end
5563

64+
function diffop(::Type{TWeights}, n::Int, k::Int) where {TWeights}
65+
D = spdiagm(0 => ones(TWeights,n))
66+
for i in 1:k
67+
D = diff(D; dims=1)
68+
end
69+
## TODO: Normalize by n?
70+
D' * D
71+
end
72+
diffop(n::Int, k::Int) = diffop(Float64, n, k)
73+
### TODO: add compiled constructor for most common operators of order k=1,2
74+
75+
76+
function prefilter!(
77+
::Type{TWeights}, ret::TCoefs, it::BSpline, λ::Real, k::Int
78+
) where {TWeights,TCoefs<:AbstractArray}
79+
80+
sz = size(ret)
81+
82+
# Test if regularizing is allowed
83+
fallback = if ndims(ret) > 1
84+
@warn "Smooth BSpline only available for Vectors, fallback to non-smoothed"
85+
true
86+
elseif λ < 0
87+
@warn "Smooth BSpline require a non-negative λ, fallback to non-smoothed: $(λ)"
88+
true
89+
elseif λ == 0
90+
true
91+
else
92+
false
93+
end
94+
95+
if fallback
96+
return prefilter!(TWeights, ret, it)
97+
end
98+
99+
M, b = prefiltering_system(TWeights, eltype(TCoefs), sz[1], degree(it))
100+
## Solve with regularization
101+
# Convert to dense Matrix because `*(Woodbury{Adjoint}, Woodbury)` is not defined
102+
Mt = Matrix(M)'
103+
Q = diffop(TWeights, sz[1], k)
104+
K = cholesky(Mt * Matrix(M) + λ * Q)
105+
B = Mt * popwrapper(ret)
106+
ldiv!(popwrapper(ret), K, B)
107+
108+
ret
109+
end
110+
56111
function prefilter!(
57112
::Type{TWeights}, ret::TCoefs, its::Tuple{Vararg{Union{BSpline,NoInterp}}}
58113
) where {TWeights,TCoefs<:AbstractArray}
59-
local buf, shape, retrs
60114
sz = size(ret)
61-
first = true
62115
for dim in 1:ndims(ret)
63116
it = iextract(its, dim)
64117
if it != NoInterp

src/extrapolation/filled.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ extrapolate(itp::AbstractInterpolation{T,N,IT}, fillvalue) where {T,N,IT} = Fill
3030
@inline function (etp::FilledExtrapolation{T,N})(x::Vararg{Number,N}) where {T,N}
3131
itp = parent(etp)
3232
coefs = coefficients(itp)
33-
Tret = typeof(prod(x) * zero(eltype(coefs)))
33+
Tret = promote_type(eltype(coefs), typeof.(x)...)
3434
if checkbounds(Bool, itp, x...)
3535
@inbounds itp(x...)
3636
else

src/gridded/gridded.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ end
2828

2929
function GriddedInterpolation(::Type{TWeights}, knots::NTuple{N,GridIndex}, A::AbstractArray{TCoefs,N}, it::IT) where {N,TCoefs,TWeights<:Real,IT<:DimSpec{Gridded},pad}
3030
isconcretetype(IT) || error("The b-spline type must be a leaf type (was $IT)")
31-
isconcretetype(TCoefs) || @warn("For performance reasons, consider using an array of a concrete type (eltype(A) == $(eltype(A)))")
3231

3332
check_gridded(it, knots, axes(A))
3433
c = zero(TWeights)

src/gridded/indexing.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ function gridded_floorbounds(x, knotvec::AbstractVector)
4040
max(i, first(axes1(knotvec)))
4141
end
4242

43+
ceilbounds(x::Integer, knotvec::AbstractVector) = gridded_ceilbounds(x, knotvec)
44+
ceilbounds(x, knotvec::AbstractVector) = gridded_ceilbounds(x, knotvec)
45+
function gridded_ceilbounds(x, knotvec::AbstractVector)
46+
i = find_knot_index(knotvec, x)
47+
iclamp = max(i, first(axes1(knotvec)))
48+
min(iclamp+1, last(axes1(knotvec)))
49+
end
50+
4351
@inline find_knot_index(knotv, x) = searchsortedfirst(knotv, x, Base.Order.ForwardOrdering()) - 1
4452
@inline find_knot_index(knotv, x::AbstractVector) = searchsortedfirst_vec(knotv, x) .- 1
4553

0 commit comments

Comments
 (0)