-
Notifications
You must be signed in to change notification settings - Fork 224
/
newton.jl
147 lines (121 loc) · 4.54 KB
/
newton.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
macro newtontrace()
quote
if tracing
dt = Dict()
if extended_trace
dt["x"] = copy(x)
dt["g(x)"] = copy(gr)
dt["h(x)"] = copy(H)
end
grnorm = norm(gr, Inf)
update!(tr,
iteration,
f_x,
grnorm,
dt,
store_trace,
show_trace,
show_every,
callback)
end
end
end
function newton{T}(d::TwiceDifferentiableFunction,
initial_x::Vector{T};
xtol::Real = 1e-32,
ftol::Real = 1e-8,
grtol::Real = 1e-8,
iterations::Integer = 1_000,
store_trace::Bool = false,
show_trace::Bool = false,
extended_trace::Bool = false,
callback = nothing,
show_every = 1,
linesearch!::Function = hz_linesearch!)
# Maintain current state in x and previous state in x_previous
x, x_previous = copy(initial_x), copy(initial_x)
# Count the total number of iterations
iteration = 0
# Track calls to function and gradient
f_calls, g_calls = 0, 0
# Count number of parameters
n = length(x)
# Maintain current gradient in gr
gr = Array(T, n)
# The current search direction
# TODO: Try to avoid re-allocating s
s = Array(T, n)
# Buffers for use in line search
x_ls, gr_ls = Array(T, n), Array(T, n)
# Store f(x) in f_x
f_x_previous, f_x = NaN, d.fg!(x, gr)
f_calls, g_calls = f_calls + 1, g_calls + 1
# Store h(x) in H
H = Array(T, n, n)
d.h!(x, H)
# Keep track of step-sizes
alpha = alphainit(one(T), x, gr, f_x)
# TODO: How should this flag be set?
mayterminate = false
# Maintain a cache for line search results
lsr = LineSearchResults(T)
# Trace the history of states visited
tr = OptimizationTrace()
tracing = store_trace || show_trace || extended_trace || callback != nothing
@newtontrace
# Assess multiple types of convergence
x_converged, f_converged, gr_converged = false, false, false
# Iterate until convergence
converged = false
while !converged && iteration < iterations
# Increment the number of steps we've had to perform
iteration += 1
# Search direction is always the negative gradient divided by H
# TODO: Do this calculation in place
@inbounds s[:] = -(H \ gr)
# Refresh the line search cache
dphi0 = vecdot(gr, s)
clear!(lsr)
push!(lsr, zero(T), f_x, dphi0)
# Determine the distance of movement along the search line
alpha, f_update, g_update =
linesearch!(d, x, s, x_ls, gr_ls, lsr, alpha, mayterminate)
f_calls, g_calls = f_calls + f_update, g_calls + g_update
# Maintain a record of previous position
copy!(x_previous, x)
# Update current position # x = x + alpha * s
LinAlg.axpy!(alpha, s, x)
# Update the function value and gradient
f_x_previous, f_x = f_x, d.fg!(x, gr)
f_calls, g_calls = f_calls + 1, g_calls + 1
# Update the Hessian
d.h!(x, H)
x_converged,
f_converged,
gr_converged,
converged = assess_convergence(x,
x_previous,
f_x,
f_x_previous,
gr,
xtol,
ftol,
grtol)
@newtontrace
end
return MultivariateOptimizationResults("Newton's Method",
initial_x,
x,
Float64(f_x),
iteration,
iteration == iterations,
x_converged,
xtol,
f_converged,
ftol,
gr_converged,
grtol,
tr,
f_calls,
g_calls)
end