Skip to content

Commit 9068682

Browse files
author
Tomas Lycken
committed
Merge pull request #99 from tlycken/tl/cubic-gradient
gradient for cubic b-splines, and hessian for all b-splines
2 parents f2b5e04 + c77edcb commit 9068682

File tree

9 files changed

+265
-105
lines changed

9 files changed

+265
-105
lines changed

src/Interpolations.jl

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ export
77
scale,
88

99
gradient!,
10+
gradient1,
11+
hessian!,
12+
hessian,
13+
hessian1,
1014

1115
AbstractInterpolation,
1216
AbstractExtrapolation,
@@ -74,17 +78,11 @@ gridtype{ITP<:AbstractInterpolation}(::Type{ITP}) = gridtype(super(ITP))
7478
gridtype(itp::AbstractInterpolation) = gridtype(typeof(itp))
7579
count_interp_dims{T<:AbstractInterpolation}(::Type{T}, N) = N
7680

77-
@generated function gradient{T,N}(itp::AbstractInterpolation{T,N}, xs...)
78-
n = count_interp_dims(itp, N)
79-
Tg = promote_type(T, [x <: AbstractArray ? eltype(x) : x for x in xs]...)
80-
xargs = [:(xs[$d]) for d in 1:length(xs)]
81-
:(gradient!(Array($Tg,$n), itp, $(xargs...)))
82-
end
83-
8481
include("nointerp/nointerp.jl")
8582
include("b-splines/b-splines.jl")
8683
include("gridded/gridded.jl")
8784
include("extrapolation/extrapolation.jl")
8885
include("scaling/scaling.jl")
86+
include("utils.jl")
8987

9088
end # module

src/b-splines/constant.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ function gradient_coefficients(::Type{BSpline{Constant}}, d)
1515
:($sym = 0)
1616
end
1717

18+
function hessian_coefficients(::Type{BSpline{Constant}}, d)
19+
sym = symbol("c_",d)
20+
:($sym = 0)
21+
end
22+
1823
function index_gen{IT<:DimSpec{BSpline}}(::Type{BSpline{Constant}}, ::Type{IT}, N::Integer, offsets...)
1924
if (length(offsets) < N)
2025
d = length(offsets)+1

src/b-splines/cubic.jl

Lines changed: 73 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,56 @@ function coefficients{C<:Cubic}(::Type{BSpline{C}}, N, d)
8181
end
8282
end
8383

84+
"""
85+
In this function we assume that `fx_d = x-ix_d` and we produce `cX_d` for
86+
`X ⋹ {m, _, p, pp}` such that
87+
88+
cm_d = p'(fx_d)
89+
c_d = q'(fx_d)
90+
cp_d = q'(1-fx_d)
91+
cpp_d = p'(1-fx_d)
92+
93+
where `p` and `q` are defined in the docstring for `Cubic`.
94+
"""
95+
function gradient_coefficients{C<:Cubic}(::Type{BSpline{C}}, d)
96+
symm, sym, symp, sympp = symbol("cm_",d), symbol("c_",d), symbol("cp_",d), symbol("cpp_",d)
97+
symfx = symbol("fx_",d)
98+
symfx_sqr = symbol("fx_sqr_", d)
99+
sym_1m_fx_sqr = symbol("one_m_fx_sqr_", d)
100+
quote
101+
$symfx_sqr = sqr($symfx)
102+
$sym_1m_fx_sqr = sqr(1 - $symfx)
103+
104+
$symm = -SimpleRatio(1,2) * $sym_1m_fx_sqr
105+
$sym = SimpleRatio(3,2) * $symfx_sqr - 2 * $symfx
106+
$symp = -SimpleRatio(3,2) * $sym_1m_fx_sqr + 2 * (1 - $symfx)
107+
$sympp = SimpleRatio(1,2) * $symfx_sqr
108+
end
109+
end
110+
111+
"""
112+
In `hessian_coefficients` for a cubic b-spline we assume that `fx_d = x-ix_d`
113+
and we define `cX_d` for `X ⋹ {m, _, p, pp}` such that
114+
115+
cm_d = p''(fx_d)
116+
c_d = q''(fx_d)
117+
cp_d = q''(1-fx_d)
118+
cpp_d = p''(1-fx_d)
119+
120+
where `p` and `q` are defined in the docstring entry for `Cubic`, and
121+
`fx_d` in the docstring entry for `define_indices_d`.
122+
"""
123+
function hessian_coefficients{C<:Cubic}(::Type{BSpline{C}}, d)
124+
symm, sym, symp, sympp = symbol("cm_",d), symbol("c_",d), symbol("cp_",d), symbol("cpp_",d)
125+
symfx = symbol("fx_",d)
126+
quote
127+
$symm = 1 - $symfx
128+
$sym = 3 * $symfx - 2
129+
$symp = 1 - 3 * $symfx
130+
$sympp = $symfx
131+
end
132+
end
133+
84134
function index_gen{C<:Cubic,IT<:DimSpec{BSpline}}(::Type{BSpline{C}}, ::Type{IT}, N::Integer, offsets...)
85135
if length(offsets) < N
86136
d = length(offsets)+1
@@ -155,7 +205,7 @@ condition gives:
155205
"""
156206
function prefiltering_system{T,TC}(::Type{T}, ::Type{TC}, n::Int,
157207
::Type{Cubic{Line}}, ::Type{OnCell})
158-
dl,d,du = inner_system_diags(T,n,Cubic{Flat})
208+
dl,d,du = inner_system_diags(T,n,Cubic{Line})
159209
d[1] = d[end] = 3
160210
du[1] = dl[end] = -7
161211

@@ -181,18 +231,29 @@ condition gives:
181231
This is the same system as `Quadratic{Line}`, `OnGrid` so we reuse the
182232
implementation
183233
"""
184-
function prefiltering_system{T,TC,GT<:GridType}(::Type{T}, ::Type{TC}, n::Int,
185-
::Type{Cubic{Line}}, ::Type{GT})
186-
prefiltering_system(T, TC, n, Quadratic{Line}, OnGrid)
234+
function prefiltering_system{T,TC}(::Type{T}, ::Type{TC}, n::Int,
235+
::Type{Cubic{Line}}, ::Type{OnGrid})
236+
dl,d,du = inner_system_diags(T,n,Cubic{Line})
237+
d[1] = d[end] = 1
238+
du[1] = dl[end] = -2
239+
240+
# now need Woodbury correction to set :
241+
# - [1, 3] and [n, n-2] ==> 1
242+
specs = _build_woodbury_specs(T, n,
243+
(1, 3, one(T)),
244+
(n, n-2, one(T)),
245+
)
246+
247+
Woodbury(lufact!(Tridiagonal(dl, d, du), Val{false}), specs...), zeros(TC, n)
187248
end
188249

189250
function prefiltering_system{T,TC,GT<:GridType}(::Type{T}, ::Type{TC}, n::Int,
190251
::Type{Cubic{Periodic}}, ::Type{GT})
191252
dl, d, du = inner_system_diags(T,n,Cubic{Periodic})
192253

193254
specs = _build_woodbury_specs(T, n,
194-
(1, n, SimpleRatio(1, 6)),
195-
(n, 1, SimpleRatio(1, 6))
255+
(1, n, du[1]),
256+
(n, 1, dl[end])
196257
)
197258

198259
Woodbury(lufact!(Tridiagonal(dl, d, du), Val{false}), specs...), zeros(TC, n)
@@ -210,38 +271,12 @@ This is the same system as `Quadratic{Free}` so we reuse the implementation
210271
"""
211272
function prefiltering_system{T,TC,GT<:GridType}(::Type{T}, ::Type{TC}, n::Int,
212273
::Type{Cubic{Free}}, ::Type{GT})
213-
prefiltering_system(T, TC, n, Quadratic{Free}, GT)
214-
end
215-
216-
@inline cub(x) = x*x*x
217-
218-
"""
219-
Build `rowspec`, `valspec`, `colspec` such that the product
220-
221-
`out = rowspec * valspec * colspec` will be equivalent to:
222-
223-
```julia
224-
out = zeros(n, n)
225-
226-
for (i, j, v) in args
227-
out[i, j] = v
228-
end
229-
```
230-
231-
"""
232-
function _build_woodbury_specs{T}(::Type{T}, n::Int, args::Tuple{Int, Int, Any}...)
233-
m = length(args)
234-
rowspec = spzeros(T, n, m)
235-
colspec = spzeros(T, m, n)
236-
valspec = zeros(T, m, m)
274+
dl, d, du = inner_system_diags(T,n,Cubic{Periodic})
237275

238-
ix = 1
239-
for (i, (row, col, val)) in enumerate(args)
240-
rowspec[row, ix] = 1
241-
colspec[ix, col] = 1
242-
valspec[ix, ix] = val
243-
ix += 1
244-
end
276+
specs = _build_woodbury_specs(T, n,
277+
(1, n, du[1]),
278+
(n, 1, dl[end])
279+
)
245280

246-
rowspec, valspec, colspec
281+
Woodbury(lufact!(Tridiagonal(dl, d, du), Val{false}), specs...), zeros(TC, n)
247282
end

src/b-splines/indexing.jl

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ function gradient_coefficients{IT<:DimSpec{BSpline}}(::Type{IT}, N, dim)
1313
coefficients(iextract(IT, d), N, d) for d = 1:N]
1414
Expr(:block, exs...)
1515
end
16+
function hessian_coefficients{IT<:DimSpec{BSpline}}(::Type{IT}, N, dim1, dim2)
17+
exs = if dim1 == dim2
18+
Expr[d==dim1==dim2 ? hessian_coefficients(iextract(IT, dim), d) :
19+
coefficients(iextract(IT, d), N, d) for d in 1:N]
20+
else
21+
Expr[d==dim1 || d==dim2 ? gradient_coefficients(iextract(IT, dim), d) :
22+
coefficients(iextract(IT, d), N, d) for d in 1:N]
23+
end
24+
Expr(:block, exs...)
25+
end
1626

1727
index_gen{IT}(::Type{IT}, N::Integer, offsets...) = index_gen(iextract(IT, min(length(offsets)+1, N)), IT, N, offsets...)
1828

@@ -88,6 +98,63 @@ end
8898
:(gradient!(g, itp, $(args...)))
8999
end
90100

101+
@generated function gradient{T,N}(itp::AbstractInterpolation{T,N}, xs...)
102+
n = count_interp_dims(itp, N)
103+
Tg = promote_type(T, [x <: AbstractArray ? eltype(x) : x for x in xs]...)
104+
xargs = [:(xs[$d]) for d in 1:length(xs)]
105+
:(gradient!(Array($Tg,$n), itp, $(xargs...)))
106+
end
107+
108+
gradient1{T}(itp::AbstractInterpolation{T,1}, x) = gradient(itp, x)[1]
109+
110+
function hessian_impl{T,N,TCoefs,IT<:DimSpec{BSpline},GT<:DimSpec{GridType},Pad}(itp::Type{BSplineInterpolation{T,N,TCoefs,IT,GT,Pad}})
111+
meta = Expr(:meta, :inline)
112+
# For each component of the hessian, alternately calculate
113+
# coefficients and set component
114+
n = count_interp_dims(IT, N)
115+
exs = Expr[]
116+
cntr = 0
117+
for d1 in 1:N, d2 in 1:N
118+
if count_interp_dims(iextract(IT,d1), 1) > 0 && count_interp_dims(iextract(IT,d2),1) > 0
119+
cntr += 1
120+
push!(exs, hessian_coefficients(IT, N, d1, d2))
121+
push!(exs, :(@inbounds H[$cntr] = $(index_gen(IT, N))))
122+
end
123+
end
124+
hessian_exprs = Expr(:block, exs...)
125+
126+
quote
127+
$meta
128+
size(H) == ($n,$n) || throw(ArgumentError(string("The size of the provided Hessian matrix wasn't a square matrix of size ", size(H))))
129+
@nexprs $N d->(x_d = xs[d])
130+
131+
$(define_indices(IT, N, Pad))
132+
133+
$hessian_exprs
134+
135+
H
136+
end
137+
end
138+
139+
@generated function hessian!{T,N}(H::AbstractMatrix, itp::BSplineInterpolation{T,N}, xs::Number...)
140+
length(xs) == N || throw(ArgumentError("Can only be called with $N indexes"))
141+
hessian_impl(itp)
142+
end
143+
144+
@generated function hessian!{T,N}(H::AbstractMatrix, itp::BSplineInterpolation{T,N}, index::CartesianIndex{N})
145+
args = [:(index[$d]) for d in 1:N]
146+
:(hessian!(H, itp, $(args...)))
147+
end
148+
149+
@generated function hessian{T,N}(itp::AbstractInterpolation{T,N}, xs...)
150+
n = count_interp_dims(itp,N)
151+
TH = promote_type(T, [x <: AbstractArray ? eltype(x) : x for x in xs]...)
152+
xargs = [:(xs[$d]) for d in 1:length(xs)]
153+
:(hessian!(Array($TH,$n,$n), itp, $(xargs...)))
154+
end
155+
156+
hessian1{T}(itp::AbstractInterpolation{T,1}, x) = hessian(itp, x)[1,1]
157+
91158
offsetsym(off, d) = off == -1 ? symbol("ixm_", d) :
92159
off == 0 ? symbol("ix_", d) :
93160
off == 1 ? symbol("ixp_", d) :

src/b-splines/linear.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ function gradient_coefficients(::Type{BSpline{Linear}}, d)
2525
end
2626
end
2727

28+
function hessian_coefficients(::Type{BSpline{Linear}}, d)
29+
sym, symp = symbol("c_",d), symbol("cp_",d)
30+
quote
31+
$sym = $symp = 0
32+
end
33+
end
34+
2835
# This assumes fractional values 0 <= fx_d <= 1, integral values ix_d and ixp_d (typically ixp_d = ix_d+1,
2936
#except at boundaries), and an array itp.coefs
3037
function index_gen{IT<:DimSpec{BSpline}}(::Type{BSpline{Linear}}, ::Type{IT}, N::Integer, offsets...)

0 commit comments

Comments
 (0)