Skip to content

Commit 847ccf1

Browse files
Merge branch 'master' of github.com:johnmyleswhite/Calculus.jl
2 parents a5269ae + e66cee4 commit 847ccf1

File tree

3 files changed

+58
-77
lines changed

3 files changed

+58
-77
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*~
2+
*.kate-swp

src/finite_difference.jl

Lines changed: 49 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,9 @@
1616
## In the examples I've tested, this strategy is best when x is small
1717
## and becomes very bad when x is larger
1818
##
19-
## In previous iteration of derivative_numer, epsilon was calculated as
20-
##
21-
## epsilon = eps(max(1.0, abs(x)))^(1/3)
22-
##
23-
## In the examples I've tested, this strategy is bad when x is small
24-
## and becomes better when x is larger
25-
##
26-
## The current approach comes from minFunc
27-
##
28-
## epsilon = 2 * sqrt(1e-12) * (1 + norm(x))
29-
##
30-
## TODO: Perform a systematic comparison across several functions
31-
## and several ranges of inputs
19+
## The proper setting of epsilon depends on whether we're doing central
20+
## differencing or forward differencing. See, for example, section 5.7 of
21+
## Numerical Recipes.
3222
##
3323
##############################################################################
3424

@@ -41,12 +31,14 @@
4131
function finite_difference{T <: Number}(f::Function,
4232
x::T,
4333
dtype::Symbol)
44-
epsilon = 2 * sqrt(1e-12) * (1 + norm(x))
45-
4634
if dtype == :forward
47-
return (f(x + epsilon) - f(x)) / epsilon
35+
epsilon = sqrt(eps(max(one(T), abs(x))))
36+
xplusdx = x + epsilon
37+
return (f(xplusdx) - f(x)) / (xplusdx - x) # use machine-representable numbers
4838
elseif dtype == :central
49-
return (f(x + epsilon) - f(x - epsilon)) / (2 * epsilon)
39+
epsilon = cbrt(eps(max(one(T), abs(x))))
40+
xplusdx, xminusdx = x + epsilon, x - epsilon
41+
return (f(xplusdx) - f(xminusdx)) / (xplusdx - xminusdx)
5042
else
5143
error("dtype must be :forward or :central")
5244
end
@@ -62,8 +54,6 @@ finite_difference(f::Function, x::Number) = finite_difference(f, x, :central)
6254
function finite_difference{T <: Number}(f::Function,
6355
x::Vector{T},
6456
dtype::Symbol)
65-
epsilon = 2 * sqrt(1e-12) * (1 + norm(x))
66-
6757
# What is the dimension of x?
6858
n = length(x)
6959

@@ -78,15 +68,17 @@ function finite_difference{T <: Number}(f::Function,
7868
f_x = f(x)
7969
xplusdx = copy(x)
8070
for i = 1:n
71+
epsilon = sqrt(eps(max(one(T), abs(x[i]))))
8172
xplusdx[i] = x[i] + epsilon
82-
differential[i] = (f(xplusdx) - f_x) / epsilon
73+
differential[i] = (f(xplusdx) - f_x) / (xplusdx[i] - x[i])
8374
xplusdx[i] = x[i]
8475
end
8576
elseif dtype == :central
8677
xplusdx, xminusdx = copy(x), copy(x)
8778
for i = 1:n
79+
epsilon = cbrt(eps(max(one(T), abs(x[i]))))
8880
xplusdx[i], xminusdx[i] = x[i] + epsilon, x[i] - epsilon
89-
differential[i] = (f(xplusdx) - f(xminusdx)) / (2 * epsilon)
81+
differential[i] = (f(xplusdx) - f(xminusdx)) / (xplusdx[i] - xminusdx[i])
9082
xplusdx[i], xminusdx[i] = x[i], x[i]
9183
end
9284
else
@@ -107,8 +99,6 @@ end
10799
##############################################################################
108100

109101
function finite_difference_jacobian{T <: Number}(f::Function, x::Vector{T}, dtype::Symbol)
110-
epsilon = 2 * sqrt(1e-12) * (1 + norm(x))
111-
112102
# What is the dimension of x?
113103
n = length(x)
114104

@@ -122,16 +112,18 @@ function finite_difference_jacobian{T <: Number}(f::Function, x::Vector{T}, dtyp
122112
if dtype == :forward
123113
xplusdx = copy(x)
124114
for i = 1:n
115+
epsilon = sqrt(eps(max(one(T), abs(x[i]))))
125116
xplusdx[i] = x[i] + epsilon
126-
J[:, i] = (f(xplusdx) - f_x) / epsilon
117+
J[:, i] = (f(xplusdx) - f_x) / (xplusdx[i] - x[i])
127118
xplusdx[i] = x[i]
128119
end
129120
return J
130121
elseif dtype == :central
131122
xplusdx, xminusdx = copy(x), copy(x)
132123
for i = 1:n
124+
epsilon = cbrt(eps(max(one(T), abs(x[i]))))
133125
xplusdx[i], xminusdx[i] = x[i] + epsilon, x[i] - epsilon
134-
J[:, i] = (f(xplusdx) - f(xminusdx)) / (2 * epsilon)
126+
J[:, i] = (f(xplusdx) - f(xminusdx)) / (xplusdx[i] - xminusdx[i])
135127
xplusdx[i], xminusdx[i] = x[i], x[i]
136128
end
137129
return J
@@ -142,72 +134,58 @@ end
142134

143135
##############################################################################
144136
##
145-
## Second derivative of f: R^n -> R
137+
## Second derivative of f: R -> R
146138
##
147139
##############################################################################
148140

149-
function finite_difference_hessian(f::Function, g::Function, x::Number, dtype::Symbol)
150-
epsilon = 2 * sqrt(1e-12) * (1 + norm(x))
151-
152-
if dtype == :forward
153-
return (g(x + epsilon) - g(x)) / epsilon
154-
elseif dtype == :central
155-
return (g(x + epsilon) - g(x - epsilon)) / (2 * epsilon)
156-
else
157-
error("dtype must :forward or :central")
158-
end
159-
end
160-
function finite_difference_hessian(f::Function, x::Number, dtype::Symbol)
161-
finite_difference_hessian(f, derivative(f), x, dtype)
162-
end
163-
function finite_difference_hessian(f::Function, x::Number)
164-
finite_difference_hessian(f, derivative(f), x, :central)
141+
function finite_difference_hessian{T <: Number}(f::Function, x::T)
142+
epsilon = eps(max(one(T), abs(x)))^(1/4)
143+
(f(x + epsilon) - 2*f(x) + f(x - epsilon))/epsilon^2
165144
end
145+
finite_difference_hessian(f::Function, g::Function, x::Number, dtype::Symbol) = finite_difference(g, x, dtype)
146+
finite_difference_hessian(f::Function, g::Function, x::Number) = finite_difference(g, x, :central)
147+
166148

167149
##############################################################################
168150
##
169151
## Hessian of f: R^n -> R
170152
##
171153
##############################################################################
172154

173-
function finite_difference_hessian{T <: Number}(f::Function, g::Function, x::Vector{T}, dtype::Symbol)
174-
epsilon = 2 * sqrt(1e-12) * (1 + norm(x))
175-
155+
function finite_difference_hessian{T <: Number}(f::Function, x::Vector{T})
176156
# What is the dimension of x?
177157
n = length(x)
178158

179159
# Initialize an empty Hessian
180160
H = Array(Float64, n, n)
181161

182-
if dtype == :forward
183-
g_x = g(x)
184-
xplusdx = copy(x)
185-
for i in 1:n
186-
xplusdx[i] = x[i] + epsilon
187-
H[:, i] = (g(xplusdx) - g_x) / epsilon
188-
xplusdx[i] = x[i]
162+
xpp, xpm, xmp, xmm = copy(x), copy(x), copy(x), copy(x)
163+
fx = f(x)
164+
for i = 1:n
165+
xi = x[i]
166+
epsilon = eps(max(one(T), abs(x[i])))^(1/4)
167+
xpp[i], xmm[i] = xi + epsilon, xi - epsilon
168+
H[i, i] = (f(xpp) - 2*fx + f(xmm)) / epsilon^2
169+
epsiloni = cbrt(eps(max(one(T), abs(x[i]))))
170+
xp = xi + epsiloni
171+
xm = xi - epsiloni
172+
xpp[i], xpm[i], xmp[i], xmm[i] = xp, xp, xm, xm
173+
for j = i+1:n
174+
xj = x[j]
175+
epsilonj = cbrt(eps(max(one(T), abs(x[j]))))
176+
xp = xj + epsilonj
177+
xm = xj - epsilonj
178+
xpp[j], xpm[j], xmp[j], xmm[j] = xp, xm, xp, xm
179+
H[i, j] = (f(xpp) - f(xpm) - f(xmp) + f(xmm))/(4*epsiloni*epsilonj)
180+
xpp[j], xpm[j], xmp[j], xmm[j] = xj, xj, xj, xj
189181
end
190-
symmetrize!(H)
191-
return H
192-
elseif dtype == :central
193-
xplusdx, xminusdx = copy(x), copy(x)
194-
for i = 1:n
195-
xplusdx[i], xminusdx[i] = x[i] + epsilon, x[i] - epsilon
196-
H[:, i] = (g(xplusdx) - g(xminusdx)) / (2 * epsilon)
197-
xplusdx[i], xminusdx[i] = x[i], x[i]
198-
end
199-
symmetrize!(H)
200-
return H
201-
else
202-
error("dtype must :forward or :central")
182+
xpp[i], xpm[i], xmp[i], xmm[i] = xi, xi, xi, xi
203183
end
184+
symmetrize!(H)
185+
H
204186
end
205-
function finite_difference_hessian{T <: Number}(f::Function, x::Vector{T}, dtype::Symbol)
206-
finite_difference_hessian(f, gradient(f), x, dtype)
207-
end
208-
function finite_difference_hessian{T <: Number}(f::Function, x::Vector{T})
209-
finite_difference_hessian(f, gradient(f), x, :central)
210-
end
187+
finite_difference_hessian{T}(f::Function, g::Function, x::Vector{T}, dtype::Symbol) = finite_difference_jacobian(g, x, dtype)
188+
finite_difference_hessian{T}(f::Function, g::Function, x::Vector{T}) = finite_difference_jacobian(g, x, :central)
211189

212190
##############################################################################
213191
##

test/finite_difference.jl

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@
3434
# Second derivatives of f: R -> R
3535
#
3636

37-
@assert norm(Calculus.finite_difference_hessian(x -> x^2, x -> 2 * x, 1.0, :forward) - 2.0) < 10e-4
38-
@assert norm(Calculus.finite_difference_hessian(x -> x^2, x -> 2 * x, 10.0, :forward) - 2.0) < 10e-4
39-
@assert norm(Calculus.finite_difference_hessian(x -> x^2, x -> 2 * x, 100.0, :forward) - 2.0) < 10e-4
37+
@assert norm(Calculus.finite_difference_hessian(x -> x^2, x -> 2 * x, 1.0) - 2.0) < 10e-4
38+
@assert norm(Calculus.finite_difference_hessian(x -> x^2, x -> 2 * x, 10.0) - 2.0) < 10e-4
39+
@assert norm(Calculus.finite_difference_hessian(x -> x^2, x -> 2 * x, 100.0) - 2.0) < 10e-4
4040

41-
@assert norm(Calculus.finite_difference_hessian(x -> x^2, 1.0, :forward) - 2.0) < 10e-4
42-
@assert norm(Calculus.finite_difference_hessian(x -> x^2, 10.0, :forward) - 2.0) < 10e-4
43-
@assert norm(Calculus.finite_difference_hessian(x -> x^2, 100.0, :forward) - 2.0) < 10e-4
41+
@assert norm(Calculus.finite_difference_hessian(x -> x^2, 1.0) - 2.0) < 10e-4
42+
@assert norm(Calculus.finite_difference_hessian(x -> x^2, 10.0) - 2.0) < 10e-4
43+
@assert norm(Calculus.finite_difference_hessian(x -> x^2, 100.0) - 2.0) < 10e-4
4444

4545
#
4646
# Hessians of f: R^n -> R
@@ -50,6 +50,7 @@ fx(x) = sin(x[1]) + cos(x[2])
5050
gx = gradient(fx)
5151
@assert norm(gx([0.0, 0.0]) - [cos(0.0), -sin(0.0)]) < 10e-4
5252
@assert norm(Calculus.finite_difference_hessian(fx, gx, [0.0, 0.0], :central) - [-sin(0.0) 0.0; 0.0 -cos(0.0)]) < 10e-4
53+
@assert norm(Calculus.finite_difference_hessian(fx, [0.0, 0.0]) - [-sin(0.0) 0.0; 0.0 -cos(0.0)]) < 10e-4
5354

5455
#
5556
# Taylor Series first derivatives

0 commit comments

Comments
 (0)