Skip to content

Commit 98f931b

Browse files
committed
Jacobians for real-valued callables
1 parent af6c8e3 commit 98f931b

File tree

2 files changed

+84
-4
lines changed

2 files changed

+84
-4
lines changed

src/finitediff.jl

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ end
7878

7979
function finite_difference!{T<:Real}(df::StridedArray{T}, f, x::StridedArray{T}, ::Type{Val{:forward}})
8080
eps_cbrt = cbrt(eps(T))
81-
@fastmath @inbounds for i in 1 : length(x)
81+
@inbounds for i in 1 : length(x)
8282
epsilon = compute_epsilon(Val{:forward}, x[i], eps_cbrt)
8383
epsilon_inv = one(T) / epsilon
8484
x_plus = x[i] + epsilon
@@ -89,7 +89,7 @@ end
8989

9090
function finite_difference!{T<:Real}(df::StridedArray{T}, f, x::StridedArray{T}, ::Type{Val{:forward}}, fx::StridedArray{T})
9191
eps_cbrt = cbrt(eps(T))
92-
@fastmath @inbounds for i in 1 : length(x)
92+
@inbounds for i in 1 : length(x)
9393
epsilon = compute_epsilon(Val{:forward}, x[i], eps_cbrt)
9494
epsilon_inv = one(T) / epsilon
9595
x_plus = x[i] + epsilon
@@ -123,6 +123,78 @@ end
123123

124124

125125
#=
126-
Compute the Jacobian matrix of a real-valued callable f.
126+
Compute the Jacobian matrix of a real-valued callable f: R^n -> R^m.
127127
=#
128-
# TODO
128+
function finite_difference_jacobian{T<:Real}(f, x::AbstractArray{T}, t::DataType)
129+
fx = f.(x)
130+
J = zeros(T, length(fx), length(x))
131+
finite_difference_jacobian!(J, f, x, t, fx)
132+
end
133+
134+
function finite_difference_jacobian!{T<:Real}(J::AbstractArray{T}, f, x::AbstractArray{T}, t::DataType, fx::AbstractArray{T})
135+
m, n = size(J)
136+
if t == Val{:forward}
137+
shifted_x = copy(x)
138+
eps_sqrt = sqrt(eps(T))
139+
for i in 1 : n
140+
epsilon = compute_epsilon(t, x, eps_sqrt)
141+
shifted_x[i] += epsilon
142+
@. J[:, i] = (f(shifted_x) - f_x) / epsilon
143+
shifted_x[i] = x[i]
144+
end
145+
elseif t == Val{:central}
146+
shifted_x_plus = copy(x)
147+
shifted_x_minus = copy(x)
148+
eps_cbrt = cbrt(eps(T))
149+
for i in 1 : n
150+
epsilon = compute_epsilon(t, x, eps_cbrt)
151+
shifted_x_plus[i] += epsilon
152+
shifted_x_minus[i] -= epsilon
153+
@. J[:, i] = (f(shifted_x_plus) - f(shifted_x_minus)) / (epsilon + epsilon)
154+
shifted_x_plus[i] = x[i]
155+
shifted_x_minus[i] = x[i]
156+
end
157+
end
158+
J
159+
end
160+
161+
function finite_difference_jacobian{T<:Real}(f, x::StridedArray{T}, t::DataType, fx::StridedArray{T})
162+
J = zeros(T, length(fx), length(x))
163+
finite_difference_jacobian!(J, f, x, t, fx)
164+
end
165+
166+
function finite_difference_jacobian!{T<:Real}(J::StridedArray{T}, f, x::StridedArray{T}, ::Type{Val{:forward}}, fx::StridedArray{T})
167+
m, n = size(J)
168+
eps_sqrt = sqrt(eps(T))
169+
@inbounds for i = 1 : n
170+
epsilon = compute_epsilon(Val{:forward}, x[i], eps_sqrt)
171+
epsilon_inv = one(T) / epsilon
172+
for j in 1 : m
173+
if i == j
174+
J[j,i] = (f(x[j]+epsilon) - fx[j]) * epsilon_inv
175+
else
176+
J[j,i] = zero(T)
177+
end
178+
end
179+
end
180+
J
181+
end
182+
183+
function finite_difference_jacobian!{T<:Real}(J::StridedArray{T}, f, x::StridedArray{T}, ::Type{Val{:central}}, ::Union{Void,StridedArray{T}}=nothing)
184+
m, n = size(J)
185+
eps_cbrt = cbrt(eps(T))
186+
@inbounds for i = 1 : n
187+
epsilon = compute_epsilon(Val{:central}, x[i], eps_cbrt)
188+
epsilon_double_inv = one(T) / (2 * epsilon)
189+
for j in 1 : m
190+
if i==j
191+
J[j,i] = (f(x[j]+epsilon) - f(x[j]-epsilon)) * epsilon_double_inv
192+
else
193+
J[j,i] = zero(T)
194+
end
195+
end
196+
end
197+
J
198+
end
199+
200+
# TODO: Jacobians for complex-valued callables

test/finitedifftests.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ y = sin.(x)
33
df = zeros(100)
44
df_ref = cos.(x)
55

6+
# TODO: add tests for non-StridedArrays and with more complicated functions
7+
8+
# derivative tests
69
@test maximum(abs.(DiffEqDiffTools.finite_difference!(df, sin, x, Val{:central}) - df_ref)) < 1e-8
710
@test maximum(abs.(DiffEqDiffTools.finite_difference!(df, sin, x, Val{:forward}) - df_ref)) < 1e-4
811
@test maximum(abs.(DiffEqDiffTools.finite_difference!(df, sin, x, Val{:forward}, y) - df_ref)) < 1e-4
12+
13+
# Jacobian tests
14+
using Calculus
15+
@test DiffEqDiffTools.finite_difference_jacobian(sin, x, Val{:central}) Calculus.finite_difference_jacobian(sin, x, :central)
16+
@test DiffEqDiffTools.finite_difference_jacobian(sin, x, Val{:forward}) Calculus.finite_difference_jacobian(sin, x, :forward)

0 commit comments

Comments
 (0)