Skip to content

Commit 501b3c9

Browse files
Merge pull request #22 from JuliaDiffEq/inplace
add inplace jacobian calculations
2 parents baac872 + bc3ec49 commit 501b3c9

File tree

2 files changed

+134
-23
lines changed

2 files changed

+134
-23
lines changed

src/jacobians.jl

Lines changed: 128 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,152 @@
22
function finite_difference_jacobian(f, x::AbstractArray{<:Number},
33
fdtype::DataType=Val{:central}, funtype::DataType=Val{:Real},
44
wrappertype::DataType=Val{:Default},
5-
fx::Union{Void,AbstractArray{<:Number}}=nothing, epsilon::Union{Void,AbstractArray{<:Real}}=nothing, returntype=eltype(x))
6-
5+
fx::Union{Void,AbstractArray{<:Number}}=nothing,
6+
epsilon::Union{Void,AbstractArray{<:Number}}=nothing, returntype=eltype(x),
7+
inplace::DataType=Val{true})
78
J = zeros(returntype, length(x), length(x))
89
finite_difference_jacobian!(J, f, x, fdtype, funtype, wrappertype, fx,
9-
epsilon, returntype)
10+
epsilon, returntype, inplace)
1011
end
1112

1213
function finite_difference_jacobian!(J::AbstractMatrix{<:Number}, f,
1314
x::AbstractArray{<:Number},
1415
fdtype::DataType=Val{:central}, funtype::DataType=Val{:Real},
1516
wrappertype::DataType=Val{:Default},
1617
fx::Union{Void,AbstractArray{<:Number}}=nothing,
17-
epsilon::Union{Void,AbstractArray{<:Number}}=nothing, returntype=eltype(x))
18+
epsilon::Union{Void,AbstractArray{<:Number}}=nothing, returntype=eltype(x),
19+
inplace::DataType=Val{true})
1820

19-
finite_difference_jacobian!(J, f, x, fdtype, funtype, wrappertype, fx, epsilon, returntype)
21+
_finite_difference_jacobian!(J, f, x, fdtype, funtype, wrappertype, fx,
22+
epsilon, returntype, inplace)
2023
end
2124

22-
function finite_difference_jacobian!(J::AbstractMatrix{<:Real}, f,
25+
function _finite_difference_jacobian!(J::AbstractMatrix{<:Real}, f,
2326
x::AbstractArray{<:Real},
2427
fdtype::DataType, ::Type{Val{:Real}}, ::Type{Val{:Default}},
25-
fx::Union{Void,AbstractArray{<:Real}}=nothing,
26-
epsilon::Union{Void,AbstractArray{<:Real}}=nothing, returntype=eltype(x))
28+
fx, epsilon, returntype, inplace::Type{Val{true}})
29+
30+
# TODO: test and rework this to support GPUArrays and non-indexable types, if possible
31+
m, n = size(J)
32+
epsilon_elemtype = compute_epsilon_elemtype(epsilon, x)
33+
if fdtype == Val{:forward}
34+
if typeof(fx) == Void
35+
fx = similar(x,returntype)
36+
end
37+
# TODO: Remove these allocations
38+
fx2 = similar(x,returntype)
39+
shifted_x = copy(x)
40+
epsilon_factor = compute_epsilon_factor(Val{:forward}, epsilon_elemtype)
41+
f(fx,x)
42+
@inbounds for i in 1:n
43+
epsilon = compute_epsilon(Val{:forward}, x[i], epsilon_factor)
44+
shifted_x[i] += epsilon
45+
f(fx2,shifted_x)
46+
J[:, i] .= (fx2 - fx) / epsilon
47+
shifted_x[i] = x[i]
48+
end
49+
elseif fdtype == Val{:central}
50+
epsilon_factor = compute_epsilon_factor(Val{:central}, epsilon_elemtype)
51+
if typeof(fx) == Void
52+
fx1 = similar(x,returntype)
53+
else
54+
fx1 = fx
55+
end
56+
# TODO: Remove these allocations
57+
fx2 = similar(x,returntype)
58+
shifted_x_plus = copy(x)
59+
shifted_x_minus = copy(x)
60+
@inbounds for i in 1:n
61+
epsilon = compute_epsilon(Val{:central}, x[i], epsilon_factor)
62+
shifted_x_plus[i] += epsilon
63+
shifted_x_minus[i] -= epsilon
64+
f(fx1,shifted_x_plus)
65+
f(fx2,shifted_x_minus)
66+
J[:, i] .= (fx1 - fx2) / (epsilon + epsilon)
67+
shifted_x_plus[i] = x[i]
68+
shifted_x_minus[i] = x[i]
69+
end
70+
elseif fdtype == Val{:complex}
71+
x0 = Vector{Complex{eltype(x)}}(x)
72+
epsilon = eps(eltype(x))
73+
fx1 = similar(x,Complex{eltype(x)})
74+
@inbounds for i in 1:n
75+
x0[i] += im * epsilon
76+
f(fx1,x0)
77+
J[:,i] .= imag.(fx1) / epsilon
78+
x0[i] -= im * epsilon
79+
end
80+
else
81+
fdtype_error(Val{:Real})
82+
end
83+
J
84+
end
85+
86+
function _finite_difference_jacobian!(J::AbstractMatrix{<:Number}, f,
87+
x::AbstractArray{<:Number},
88+
fdtype::DataType, ::Type{Val{:Complex}}, ::Type{Val{:Default}},
89+
fx, epsilon, returntype, inplace::Type{Val{true}})
2790

2891
# TODO: test and rework this to support GPUArrays and non-indexable types, if possible
2992
m, n = size(J)
3093
epsilon_elemtype = compute_epsilon_elemtype(epsilon, x)
3194
if fdtype == Val{:forward}
95+
3296
if typeof(fx) == Void
33-
fx = f(x)
97+
fx = similar(x,returntype)
98+
end
99+
# TODO: Remove these allocations
100+
fx2 = similar(x,returntype)
101+
shifted_x = copy(x)
102+
103+
epsilon_factor = compute_epsilon_factor(Val{:forward}, epsilon_elemtype)
104+
f(fx,x)
105+
@inbounds for i in 1:n
106+
epsilon = compute_epsilon(Val{:forward}, real(x[i]), epsilon_factor)
107+
shifted_x[i] += epsilon
108+
f(fx2,shifted_x)
109+
@. J[:, i] = ( real(fx2 - fx ) + im*imag( fx2 - fx ) ) / epsilon
110+
shifted_x[i] = x[i]
111+
end
112+
elseif fdtype == Val{:central}
113+
epsilon_factor = compute_epsilon_factor(Val{:central}, epsilon_elemtype)
114+
115+
if typeof(fx) == Void
116+
fx1 = similar(x,returntype)
117+
else
118+
fx1 = fx
119+
end
120+
# TODO: Remove these allocations
121+
fx2 = similar(x,returntype)
122+
shifted_x_plus = copy(x)
123+
shifted_x_minus = copy(x)
124+
125+
@inbounds for i in 1:n
126+
epsilon = compute_epsilon(Val{:central}, real(x[i]), epsilon_factor)
127+
shifted_x_plus[i] += epsilon
128+
shifted_x_minus[i] -= epsilon
129+
f(fx1,shifted_x_plus)
130+
f(fx2,shifted_x_minus)
131+
@. J[:, i] = ( real(fx1 - fx2) + im*imag(fx1 - fx2) ) / (2 * epsilon)
132+
shifted_x_plus[i] = x[i]
133+
shifted_x_minus[i] = x[i]
34134
end
135+
else
136+
fdtype_error(Val{:Complex})
137+
end
138+
J
139+
end
140+
141+
function _finite_difference_jacobian!(J::AbstractMatrix{<:Real}, f,
142+
x::AbstractArray{<:Real},
143+
fdtype::DataType, ::Type{Val{:Real}}, ::Type{Val{:Default}},
144+
fx, epsilon, returntype, inplace::Type{Val{false}})
145+
146+
# TODO: test and rework this to support GPUArrays and non-indexable types, if possible
147+
m, n = size(J)
148+
epsilon_elemtype = compute_epsilon_elemtype(epsilon, x)
149+
if fdtype == Val{:forward}
150+
fx = f(x)
35151
epsilon_factor = compute_epsilon_factor(Val{:forward}, epsilon_elemtype)
36152
shifted_x = copy(x)
37153
@inbounds for i in 1:n
@@ -66,18 +182,16 @@ function finite_difference_jacobian!(J::AbstractMatrix{<:Real}, f,
66182
J
67183
end
68184

69-
function finite_difference_jacobian!(J::AbstractMatrix{<:Number}, f,
185+
function _finite_difference_jacobian!(J::AbstractMatrix{<:Number}, f,
70186
x::AbstractArray{<:Number},
71187
fdtype::DataType, ::Type{Val{:Complex}}, ::Type{Val{:Default}},
72-
fx::Union{Void,AbstractArray{<:Number}}=nothing, epsilon::Union{Void,AbstractArray{<:Real}}=nothing, returntype=eltype(x))
188+
fx, epsilon, returntype, inplace::Type{Val{false}})
73189

74190
# TODO: test and rework this to support GPUArrays and non-indexable types, if possible
75191
m, n = size(J)
76192
epsilon_elemtype = compute_epsilon_elemtype(epsilon, x)
77193
if fdtype == Val{:forward}
78-
if typeof(fx) == Void
79-
fx = f(x)
80-
end
194+
fx = f(x)
81195
epsilon_factor = compute_epsilon_factor(Val{:forward}, epsilon_elemtype)
82196
shifted_x = copy(x)
83197
@inbounds for i in 1:n

test/finitedifftests.jl

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,12 @@ df_ref = -sin.(real(x)) + im*cos.(imag(x))
6767
@test err_func(DiffEqDiffTools.finite_difference!(df, f, x, Val{:central}, Val{:Complex}, Val{:Default}, y, epsilon), df_ref) < 1e-8
6868
end
6969

70-
function f(x)
71-
fvec = zeros(x)
70+
function f(fvec,x)
7271
fvec[1] = (x[1]+3)*(x[2]^3-7)+18
7372
fvec[2] = sin(x[2]*exp(x[1])-1)
74-
fvec
7573
end
76-
x = rand(2)
77-
y = f(x)
74+
x = rand(2); y = rand(2)
75+
f(y,x)
7876
J_ref = [[-7+x[2]^3 3*(3+x[1])*x[2]^2]; [exp(x[1])*x[2]*cos(1-exp(x[1])*x[2]) exp(x[1])*cos(1-exp(x[1])*x[2])]]
7977
J = zeros(J_ref)
8078
df = zeros(x)
@@ -108,14 +106,13 @@ epsilon = zeros(x)
108106
@test err_func(DiffEqDiffTools.finite_difference_jacobian!(J, f, x, Val{:complex}, Val{:Real}, Val{:Default}, y, epsilon), J_ref) < 1e-14
109107
end
110108

111-
function f(x)
112-
fvec = zeros(x)
109+
function f(fvec,x)
113110
fvec[1] = (im*x[1]+3)*(x[2]^3-7)+18
114111
fvec[2] = sin(x[2]*exp(x[1])-1)
115-
fvec
116112
end
117113
x = rand(2) + im*rand(2)
118-
y = f(x)
114+
y = similar(x)
115+
f(y,x)
119116
J_ref = [[im*(-7+x[2]^3) 3*(3+im*x[1])*x[2]^2]; [exp(x[1])*x[2]*cos(1-exp(x[1])*x[2]) exp(x[1])*cos(1-exp(x[1])*x[2])]]
120117
J = zeros(J_ref)
121118
df = zeros(x)

0 commit comments

Comments
 (0)