Skip to content

Commit c3e4475

Browse files
author
Tomas Lycken
committed
Implement Hessian
1 parent c167b5a commit c3e4475

File tree

8 files changed

+160
-22
lines changed

8 files changed

+160
-22
lines changed

src/Interpolations.jl

Lines changed: 4 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,13 +78,6 @@ 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")

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: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,29 @@ function gradient_coefficients{C<:Cubic}(::Type{BSpline{C}}, d)
108108
end
109109
end
110110

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+
111134
function index_gen{C<:Cubic,IT<:DimSpec{BSpline}}(::Type{BSpline{C}}, ::Type{IT}, N::Integer, offsets...)
112135
if length(offsets) < N
113136
d = length(offsets)+1

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...)

src/b-splines/quadratic.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,26 @@ function gradient_coefficients{Q<:Quadratic}(::Type{BSpline{Q}}, d)
5959
end
6060
end
6161

62+
"""
63+
In `hessian_coefficients` for a quadratic b-spline we assume that `fx_d = x-ix_d`
64+
and we define `cX_d` for `X ⋹ {m, _, p}` such that
65+
66+
cm_d = p''(fx_d)
67+
c_d = q''(fx_d)
68+
cp_d = p''(1-fx_d)
69+
70+
where `p` and `q` are defined in the docstring entry for `Quadratic`, and
71+
`fx_d` in the docstring entry for `define_indices_d`.
72+
"""
73+
function hessian_coefficients{Q<:Quadratic}(::Type{BSpline{Q}}, d)
74+
symm, sym, symp = symbol("cm_",d), symbol("c_",d), symbol("cp_",d)
75+
quote
76+
$symm = 1
77+
$sym = -2
78+
$symp = 1
79+
end
80+
end
81+
6282
# This assumes integral values ixm_d, ix_d, and ixp_d,
6383
# coefficients cm_d, c_d, and cp_d, and an array itp.coefs
6484
function index_gen{Q<:Quadratic,IT<:DimSpec{BSpline}}(::Type{BSpline{Q}}, ::Type{IT}, N::Integer, offsets...)

src/cubic.jl

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

test/b-splines/cubic.jl

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# module CubicTests
1+
module CubicTests
22

33
using Base.Test
44
using Interpolations
@@ -50,4 +50,36 @@ for (constructor, copier) in ((interpolate, identity), (interpolate!, copy))
5050
end
5151
end
5252

53-
# end
53+
end
54+
55+
module CubicGradientTests
56+
57+
using Interpolations, Base.Test
58+
59+
ix = 1:15
60+
f(x) = cos((x-1)*2pi/(length(ix)-1))
61+
g(x) = -2pi/14 * sin((x-1)*2pi/(length(ix)-1))
62+
63+
A = f(ix)
64+
65+
for (constructor, copier) in ((interpolate, identity), (interpolate!, copy))
66+
67+
for BC in (Line, Flat, Free, Periodic), GT in (OnGrid,OnCell)
68+
69+
itp = constructor(copier(A), BSpline(Cubic(BC())), GT())
70+
# test that inner region is close to data
71+
for x in linspace(ix[5],ix[end-4],100)
72+
@test_approx_eq_eps g(x) gradient(itp,x)[1] cbrt(cbrt(eps(g(x))))
73+
end
74+
end
75+
end
76+
itp_flat_g = interpolate(A, BSpline(Cubic(Flat())), OnGrid())
77+
@test_approx_eq_eps gradient(itp_flat_g, 1)[1] 0 eps()
78+
@test_approx_eq_eps gradient(itp_flat_g, ix[end])[1] 0 eps()
79+
80+
itp_flat_c = interpolate(A, BSpline(Cubic(Flat())), OnCell())
81+
@test_approx_eq_eps gradient(itp_flat_c, .5)[1] 0 eps()
82+
@test_approx_eq_eps gradient(itp_flat_c, ix[end]+.5)[1] 0 eps()
83+
84+
85+
end

0 commit comments

Comments
 (0)