14
14
eps_cbrt * max (one (T), abs (x))
15
15
end
16
16
17
- @inline function compute_epsilon {T<:Complex} (:: Type{Val{:complex }} , x:: T )
17
+ @inline function compute_epsilon {T<:Complex} (:: Type{Val{:Complex }} , x:: T )
18
18
eps (real (x))
19
19
end
20
20
21
+ @inline function compute_epsilon_factor {T<:Real} (fdtype:: DataType , :: Type{T} )
22
+ if fdtype== Val{:forward }
23
+ return sqrt (eps (T))
24
+ elseif fdtype== Val{:central }
25
+ return cbrt (eps (T))
26
+ else
27
+ error (" Unrecognized fdtype: must be Val{:forward} or Val{:central}." )
28
+ end
29
+ end
30
+
21
31
22
32
#=
23
33
Compute the derivative df of a real-valued callable f on a collection of points x.
24
34
Generic fallbacks for AbstractArrays that are not StridedArrays.
35
+ # TODO : test the fallbacks
25
36
=#
26
- function finite_difference {T<:Real} (f, x:: AbstractArray{T} , :: Type{Val{:central}} , :: Union{Void,AbstractArray{T}} = nothing )
27
- df = zeros (T, size (x))
28
- finite_difference! (df, f, x, Val{:central })
29
- end
30
-
31
- function finite_difference! {T<:Real} (df:: AbstractArray{T} , f, x:: AbstractArray{T} , :: Type{Val{:central}} , :: Union{Void,AbstractArray{T}} = nothing )
32
- eps_sqrt = sqrt (eps (T))
33
- epsilon = compute_epsilon .(Val{:central }, x, eps_sqrt)
34
- @. df = (f (x+ epsilon) - f (x- epsilon)) / (2 * epsilon)
35
- end
36
-
37
- function finite_difference {T<:Real} (f, x:: AbstractArray{T} , :: Type{Val{:forward}} , f_x:: AbstractArray{T} = f .(x))
37
+ function finite_difference {T<:Real} (f, x:: AbstractArray{T} , fdtype:: DataType , fx:: Union{Void,AbstractArray{T}} = nothing )
38
38
df = zeros (T, size (x))
39
- finite_difference! (df, f, x, Val{ :forward }, f_x )
39
+ finite_difference! (df, f, x, fdtype, fx )
40
40
end
41
41
42
42
function finite_difference! {T<:Real} (df:: AbstractArray{T} , f, x:: AbstractArray{T} , :: Type{Val{:forward}} , f_x:: AbstractArray{T} = f .(x))
43
- eps_cbrt = cbrt ( eps (T) )
44
- epsilon = compute_epsilon . (Val{:forward }, x, eps_cbrt )
43
+ epsilon_factor = compute_epsilon_factor (Val{ :forward }, T )
44
+ @. epsilon = compute_epsilon (Val{:forward }, x)
45
45
@. df = (f (x+ epsilon) - f_x) / epsilon
46
46
end
47
47
48
+ function finite_difference! {T<:Real} (df:: AbstractArray{T} , f, x:: AbstractArray{T} , :: Type{Val{:central}} , :: Union{Void,AbstractArray{T}} = nothing )
49
+ epsilon_factor = compute_epsilon_factor (Val{:central }, T)
50
+ @. epsilon = compute_epsilon (Val{:central }, x, epsilon_factor)
51
+ @. df = (f (x+ epsilon) - f (x- epsilon)) / (2 * epsilon)
52
+ end
48
53
49
54
#=
50
55
Compute the derivative df of a real-valued callable f on a collection of points x.
51
56
Optimized implementations for StridedArrays.
52
57
=#
53
- function finite_difference {T<:Real} (f, x:: StridedArray{T} , :: Type{Val{:central}} , :: Union{Void,StridedArray{T}} = nothing )
54
- df = zeros (T, size (x))
55
- finite_difference! (df, f, x, Val{:central })
56
- end
57
-
58
58
function finite_difference! {T<:Real} (df:: StridedArray{T} , f, x:: StridedArray{T} , :: Type{Val{:central}} , :: Union{Void,StridedArray{T}} = nothing )
59
- eps_sqrt = sqrt ( eps (T) )
59
+ epsilon_factor = compute_epsilon_factor (Val{ :central }, T )
60
60
@inbounds for i in 1 : length (x)
61
- epsilon = compute_epsilon (Val{:central }, x[i], eps_sqrt )
61
+ epsilon = compute_epsilon (Val{:central }, x[i], epsilon_factor )
62
62
epsilon_double_inv = one (T) / (2 * epsilon)
63
63
x_plus, x_minus = x[i]+ epsilon, x[i]- epsilon
64
64
df[i] = (f (x_plus) - f (x_minus)) * epsilon_double_inv
@@ -77,9 +77,9 @@ function finite_difference{T<:Real}(f, x::StridedArray{T}, ::Type{Val{:forward}}
77
77
end
78
78
79
79
function finite_difference! {T<:Real} (df:: StridedArray{T} , f, x:: StridedArray{T} , :: Type{Val{:forward}} )
80
- eps_cbrt = cbrt ( eps (T) )
80
+ epsilon_factor = compute_epsilon_factor (Val{ :forward }, T )
81
81
@inbounds for i in 1 : length (x)
82
- epsilon = compute_epsilon (Val{:forward }, x[i], eps_cbrt )
82
+ epsilon = compute_epsilon (Val{:forward }, x[i], epsilon_factor )
83
83
epsilon_inv = one (T) / epsilon
84
84
x_plus = x[i] + epsilon
85
85
df[i] = (f (x_plus) - f (x[i])) * epsilon_inv
@@ -88,9 +88,9 @@ function finite_difference!{T<:Real}(df::StridedArray{T}, f, x::StridedArray{T},
88
88
end
89
89
90
90
function finite_difference! {T<:Real} (df:: StridedArray{T} , f, x:: StridedArray{T} , :: Type{Val{:forward}} , fx:: StridedArray{T} )
91
- eps_cbrt = cbrt ( eps (T) )
91
+ epsilon_factor = compute_epsilon_factor (Val{ :forward }, T )
92
92
@inbounds for i in 1 : length (x)
93
- epsilon = compute_epsilon (Val{:forward }, x[i], eps_cbrt )
93
+ epsilon = compute_epsilon (Val{:forward }, x[i], epsilon_factor )
94
94
epsilon_inv = one (T) / epsilon
95
95
x_plus = x[i] + epsilon
96
96
df[i] = (f (x_plus) - fx[i]) * epsilon_inv
@@ -102,16 +102,16 @@ end
102
102
Compute the derivative df of a real-valued callable f on a collection of points x.
103
103
Single point implementations.
104
104
=#
105
- function finite_difference {T<:Real} (f, x:: T , t :: DataType , f_x:: Union{Void,T} = nothing )
106
- epsilon = compute_epsilon (t , x)
107
- finite_difference_kernel (f, x, t , epsilon, f_x)
105
+ function finite_difference {T<:Real} (f, x:: T , fdtype :: DataType , f_x:: Union{Void,T} = nothing )
106
+ epsilon = compute_epsilon (fdtype , x)
107
+ finite_difference_kernel (f, x, fdtype , epsilon, f_x)
108
108
end
109
109
110
- @inline function finite_difference_kernel {T<:Real} (f, x:: T , :: Type{Val{:forward}} , epsilon:: T , f_x :: Union{Void,T} )
111
- if typeof (f_x ) == Void
110
+ @inline function finite_difference_kernel {T<:Real} (f, x:: T , :: Type{Val{:forward}} , epsilon:: T , fx :: Union{Void,T} )
111
+ if typeof (fx ) == Void
112
112
return (f (x+ epsilon) - f (x)) / epsilon
113
113
else
114
- return (f (x+ epsilon) - f_x ) / epsilon
114
+ return (f (x+ epsilon) - fx ) / epsilon
115
115
end
116
116
end
117
117
@@ -125,53 +125,56 @@ end
125
125
#=
126
126
Compute the Jacobian matrix of a real-valued callable f: R^n -> R^m.
127
127
=#
128
- function finite_difference_jacobian {T<:Real} (f, x:: AbstractArray{T} , t:: DataType )
129
- fx = f .(x)
128
+ function finite_difference_jacobian {T<:Real} (f, x:: AbstractArray{T} , fdtype:: DataType = Val{:central }, funtype:: DataType = Val{:Default })
129
+ if funtype== Val{:Default }
130
+ fx = f .(x)
131
+ elseif funtype== Val{:DiffEqJacobianWrapper }
132
+ f (fx, x)
133
+ else
134
+ error (" Unrecognized funtype: must be Val{:Default} or Val{:DiffEqJacobianWrapper}." )
135
+ end
130
136
J = zeros (T, length (fx), length (x))
131
- finite_difference_jacobian! (J, f, x, t , fx)
137
+ finite_difference_jacobian! (J, f, x, fdtype , fx, funtype )
132
138
end
133
139
134
- function finite_difference_jacobian! {T<:Real} (J:: AbstractArray{T} , f, x:: AbstractArray{T} , t:: DataType , fx:: AbstractArray{T} )
140
+ function finite_difference_jacobian! {T<:Real} (J:: AbstractArray{T} , f, x:: AbstractArray{T} , fdtype:: DataType , fx:: AbstractArray{T} , :: DataType )
141
+ # This is an inefficient fallback that only makes sense if setindex/getindex are unavailable, e.g. GPUArrays etc.
135
142
m, n = size (J)
143
+ epsilon_factor = compute_epsilon_factor (fdtype, T)
136
144
if t == Val{:forward }
137
145
shifted_x = copy (x)
138
- eps_sqrt = sqrt (eps (T))
139
- for i in 1 : n
140
- epsilon = compute_epsilon (t, x, eps_sqrt)
146
+ for i in 1 : n
147
+ epsilon = compute_epsilon (t, x[i], epsilon_factor)
141
148
shifted_x[i] += epsilon
142
- @. J[:, i] = (f (shifted_x) - f_x) / epsilon
149
+ J[:, i] . = (f (shifted_x) - f_x) / epsilon
143
150
shifted_x[i] = x[i]
144
151
end
145
152
elseif t == Val{:central }
146
- shifted_x_plus = copy (x)
153
+ shifted_x_plus = copy (x)
147
154
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
155
+ for i in 1 : n
156
+ epsilon = compute_epsilon (t, x[i], epsilon_factor)
157
+ shifted_x_plus[i] += epsilon
152
158
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]
159
+ J[:, i] . = (f (shifted_x_plus) - f (shifted_x_minus)) / (epsilon + epsilon)
160
+ shifted_x_plus[i] = x[i]
155
161
shifted_x_minus[i] = x[i]
156
162
end
163
+ else
164
+ error (" Unrecognized fdtype: must be Val{:forward} or Val{:central}." )
157
165
end
158
166
J
159
167
end
160
168
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} )
169
+ function finite_difference_jacobian! {T<:Real} (J:: StridedArray{T} , f, x:: StridedArray{T} , :: Type{Val{:central}} , fx:: StridedArray{T} , :: Type{Val{:Default}} )
167
170
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
171
+ epsilon_factor = compute_epsilon_factor (Val{ :central }, T )
172
+ @inbounds for i in 1 : n
173
+ epsilon = compute_epsilon (Val{:central }, x[i], epsilon_factor )
174
+ epsilon_double_inv = one (T) / ( 2 * epsilon)
175
+ for j in 1 : m
176
+ if i== j
177
+ J[j,i] = (f (x[j]+ epsilon) - f (x [j]- epsilon)) * epsilon_double_inv
175
178
else
176
179
J[j,i] = zero (T)
177
180
end
@@ -180,15 +183,15 @@ function finite_difference_jacobian!{T<:Real}(J::StridedArray{T}, f, x::StridedA
180
183
J
181
184
end
182
185
183
- function finite_difference_jacobian! {T<:Real} (J:: StridedArray{T} , f, x:: StridedArray{T} , :: Type{Val{:central }} , :: Union{Void, StridedArray{T}} = nothing )
186
+ function finite_difference_jacobian! {T<:Real} (J:: StridedArray{T} , f, x:: StridedArray{T} , :: Type{Val{:forward }} , fx :: StridedArray{T} , :: Type{Val{:Default}} )
184
187
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
188
+ epsilon_factor = compute_epsilon_factor (Val{ :forward }, T )
189
+ @inbounds for i in 1 : n
190
+ epsilon = compute_epsilon (Val{:forward }, x[i], epsilon_factor )
191
+ epsilon_inv = one (T) / epsilon
192
+ for j in 1 : m
190
193
if i== j
191
- J[j,i] = (f (x[j]+ epsilon) - f (x [j]- epsilon)) * epsilon_double_inv
194
+ J[j,i] = (f (x[j]+ epsilon) - fx [j]) * epsilon_inv
192
195
else
193
196
J[j,i] = zero (T)
194
197
end
@@ -197,4 +200,44 @@ function finite_difference_jacobian!{T<:Real}(J::StridedArray{T}, f, x::StridedA
197
200
J
198
201
end
199
202
203
+ # efficient implementations for OrdinaryDiffEq Jacobian wrappers, assuming the system function supplies StridedArrays
204
+ function finite_difference_jacobian! {T<:Real} (J:: StridedArray{T} , f, x:: StridedArray{T} , :: Type{Val{:forward}} , fx:: StridedArray{T} , :: Type{Val{:JacobianWrapper}} )
205
+ m, n = size (J)
206
+ epsilon_factor = compute_epsilon_factor (Val{:forward }, T)
207
+ x1, fx1 = f. x1, f. fx1
208
+ copy! (x1, x)
209
+ copy! (fx1, fx)
210
+ @inbounds for i in 1 : n
211
+ epsilon = compute_epsilon (Val{:forward }, x[i], epsilon_factor)
212
+ epsilon_inv = one (T) / epsilon
213
+ x1[i] += epsilon
214
+ f (fx, x)
215
+ f (fx1, x1)
216
+ @. J[:,i] = (fx- fx1) * epsilon_inv
217
+ x1[i] -= epsilon
218
+ end
219
+ J
220
+ end
221
+
222
+ function finite_difference_jacobian! {T<:Real} (J:: StridedArray{T} , f, x:: StridedArray{T} , :: Type{Val{:central}} , fx:: StridedArray{T} , :: Type{Val{:JacobianWrapper}} )
223
+ m, n = size (J)
224
+ epsilon_factor = compute_epsilon_factor (Val{:central }, T)
225
+ x1, fx1 = f. x1, f. fx1
226
+ copy! (x1, x)
227
+ copy! (fx1, fx)
228
+ @inbounds for i in 1 : n
229
+ epsilon = compute_epsilon (Val{:central }, x[i], epsilon_factor)
230
+ epsilon_double_inv = one (T) / (2 * epsilon)
231
+ x[i] += epsilon
232
+ x1[i] -= epsilon
233
+ f (fx, x)
234
+ f (fx1, x1)
235
+ @. J[:,i] = (fx- fx1) * epsilon_double_inv
236
+ x[i] -= epsilon
237
+ x1[i] += epsilon
238
+ end
239
+ J
240
+ end
241
+
242
+
200
243
# TODO : Jacobians for complex-valued callables
0 commit comments