1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 from LinearAlgebra import cholesky_decomposition, eigenvectors, inverse, solve_linear_equations
25 from Numeric import diagonal, dot, identity, matrixmultiply, outerproduct, sort, sqrt, transpose
26 from re import match
27
28 from bfgs import Bfgs
29 from newton import Newton
30 from base_classes import Hessian_mods, Min, Trust_region
31
32
33 -def exact_trust_region(func=None, dfunc=None, d2func=None, args=(), x0=None, min_options=(), func_tol=1e-25, grad_tol=None, maxiter=1e6, lambda0=0.0, delta_max=1e5, delta0=1.0, eta=0.2, mach_acc=1e-16, full_output=0, print_flag=0, print_prefix=""):
34 """Exact trust region algorithm."""
35
36 if print_flag:
37 if print_flag >= 2:
38 print print_prefix
39 print print_prefix
40 print print_prefix + "Exact trust region minimisation"
41 print print_prefix + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
42 min = Exact_trust_region(func, dfunc, d2func, args, x0, min_options, func_tol, grad_tol, maxiter, lambda0, delta_max, delta0, eta, mach_acc, full_output, print_flag, print_prefix)
43 if min.init_failure:
44 print print_prefix + "Initialisation of minimisation has failed."
45 return None
46 results = min.minimise()
47 return results
48
49
51 - def __init__(self, func, dfunc, d2func, args, x0, min_options, func_tol, grad_tol, maxiter, lambda0, delta_max, delta0, eta, mach_acc, full_output, print_flag, print_prefix):
52 """Class for Exact trust region minimisation specific functions.
53
54 Unless you know what you are doing, you should call the function 'exact_trust_region' rather
55 than using this class.
56 """
57
58
59 self.func = func
60 self.dfunc = dfunc
61 self.d2func = d2func
62 self.args = args
63 self.xk = x0
64 self.func_tol = func_tol
65 self.grad_tol = grad_tol
66 self.maxiter = maxiter
67 self.full_output = full_output
68 self.print_flag = print_flag
69 self.print_prefix = print_prefix
70 self.mach_acc = mach_acc
71 self.lambda0 = lambda0
72 self.delta_max = delta_max
73 self.delta = delta0
74 self.eta = eta
75
76
77 self.init_failure = 0
78
79
80 self.hessian_type_and_mod(min_options)
81 self.setup_hessian_mod()
82
83
84 self.f_count = 0
85 self.g_count = 0
86 self.h_count = 0
87
88
89 self.warning = None
90
91
92 self.n = len(self.xk)
93 self.I = identity(len(self.xk))
94
95
96 self.setup_conv_tests()
97
98
99 if match('[Bb][Ff][Gg][Ss]', self.hessian_type):
100 self.setup_bfgs()
101 self.specific_update = self.update_bfgs
102 self.d2fk = inverse(self.Hk)
103 self.warning = "Incomplete code."
104 elif match('[Nn]ewton', self.hessian_type):
105 self.setup_newton()
106 self.specific_update = self.update_newton
107
108
110 """Find the exact trust region solution.
111
112 Algorithm 4.4 from page 81 of 'Numerical Optimization' by Jorge Nocedal and Stephen J.
113 Wright, 1999, 2nd ed.
114
115 This is only implemented for positive definite matrices.
116 """
117
118
119 pB, matrix = self.get_pk(return_matrix=1)
120
121
122 l = 0
123 lambda_l = self.lambda0
124
125 while l < 3:
126
127 B = matrix + lambda_l * self.I
128
129
130 R = cholesky_decomposition(B)
131
132
133 y = solve_linear_equations(R, self.dfk)
134 y = -solve_linear_equations(transpose(R), y)
135 dot_pl = dot(y, y)
136
137
138 y = solve_linear_equations(transpose(R), y)
139
140
141 lambda_l = lambda_l + (dot_pl / dot(y, y)) * ((sqrt(dot_pl) - self.delta) / self.delta)
142
143
144 lambda_l = max(0.0, lambda_l)
145
146 if self.print_flag >= 2:
147 print self.print_prefix + "\tl: " + `l` + ", lambda(l) fin: " + `lambda_l`
148
149 l = l + 1
150
151
152 R = cholesky_decomposition(matrix + lambda_l * self.I)
153 y = solve_linear_equations(R, self.dfk)
154 self.pk = -solve_linear_equations(transpose(R), y)
155
156 if self.print_flag >= 2:
157 print self.print_prefix + "Step: " + `self.pk`
158
159
160 self.xk_new = self.xk + self.pk
161 self.fk_new, self.f_count = apply(self.func, (self.xk_new,)+self.args), self.f_count + 1
162 self.dfk_new, self.g_count = apply(self.dfunc, (self.xk_new,)+self.args), self.g_count + 1
163
164
166 """Find the exact trust region solution.
167
168 More, J. J., and Sorensen D. C. 1983, Computing a trust region step. SIAM J. Sci. Stat.
169 Comput. 4, 553-572.
170
171 This function is incomplete.
172 """
173
174 self.warning = "Incomplete code, minimisation bypassed."
175 print self.print_prefix + "Incomplete code, minimisation bypassed."
176 return
177
178
179 iter = 0
180 self.l = self.lambda0
181 self.I = identity(len(self.dfk))
182
183
184 self.lS = -1e99
185 b = 0.0
186 for j in xrange(len(self.d2fk)):
187 self.lS = max(self.lS, -self.d2fk[j, j])
188 sum = 0.0
189 for i in xrange(len(self.d2fk[j])):
190 sum = sum + abs(self.d2fk[i, j])
191 b = max(b, sum)
192 a = sqrt(dot(self.dfk, self.dfk)) / self.delta
193 self.lL = max(0.0, self.lS, a - b)
194 self.lU = a + b
195
196
197 if self.print_flag >= 2:
198 print self.print_prefix + "Initialisation."
199 eigen = eigenvectors(self.d2fk)
200 eigenvals = sort(eigen[0])
201 for i in xrange(len(self.d2fk)):
202 print self.print_prefix + "\tB[" + `i` + ", " + `i` + "] = " + `self.d2fk[i, i]`
203 print self.print_prefix + "\tEigenvalues: " + `eigenvals`
204 print self.print_prefix + "\t||g||/delta: " + `a`
205 print self.print_prefix + "\t||B||1: " + `b`
206 print self.print_prefix + "\tl: " + `self.l`
207 print self.print_prefix + "\tlL: " + `self.lL`
208 print self.print_prefix + "\tlU: " + `self.lU`
209 print self.print_prefix + "\tlS: " + `self.lS`
210
211
212 return
213 while 1:
214
215 if self.print_flag >= 2:
216 print self.print_prefix + "\n< Iteration " + `iter` + " >"
217 print self.print_prefix + "Safeguarding lambda."
218 print self.print_prefix + "\tInit l: " + `self.l`
219 print self.print_prefix + "\tlL: " + `self.lL`
220 print self.print_prefix + "\tlU: " + `self.lU`
221 print self.print_prefix + "\tlS: " + `self.lS`
222 self.l = max(self.l, self.lL)
223 self.l = min(self.l, self.lU)
224 if self.l <= self.lS:
225 if self.print_flag >= 2:
226 print self.print_prefix + "\tself.l <= self.lS"
227 self.l = max(0.001*self.lU, sqrt(self.lL*self.lU))
228 if self.print_flag >= 2:
229 print self.print_prefix + "\tFinal l: " + `self.l`
230
231
232 matrix = self.d2fk + self.l * self.I
233 pos_def = 1
234 if self.print_flag >= 2:
235 print self.print_prefix + "Cholesky decomp."
236 print self.print_prefix + "\tB + lambda.I: " + `matrix`
237 eigen = eigenvectors(matrix)
238 eigenvals = sort(eigen[0])
239 print self.print_prefix + "\tEigenvalues: " + `eigenvals`
240 try:
241 func = cholesky_decomposition
242 R = func(matrix)
243 if self.print_flag >= 2:
244 print self.print_prefix + "\tCholesky matrix R: " + `R`
245 except "LinearAlgebraError":
246 if self.print_flag >= 2:
247 print self.print_prefix + "\tLinearAlgebraError, matrix is not positive definite."
248 pos_def = 0
249 if self.print_flag >= 2:
250 print self.print_prefix + "\tPos def: " + `pos_def`
251
252 if pos_def:
253
254 p = -matrixmultiply(inverse(matrix), self.dfk)
255 if self.print_flag >= 2:
256 print self.print_prefix + "Solve p = -inverse(RT.R).g"
257 print self.print_prefix + "\tp: " + `p`
258
259
260 dot_p = dot(p, p)
261 len_p = sqrt(dot_p)
262 if len_p < self.delta:
263
264
265
266
267 delta2_len_p2 = self.delta**2 - dot_p
268 dot_p_z = dot(p, z)
269 tau = delta2_len_p2 / (dot_p_z + sign(dot_p_z) * sqrt(dot_p_z**2 + delta2_len_p2**2))
270
271 if self.print_flag >= 2:
272 print self.print_prefix + "||p|| < delta"
273 print self.print_prefix + "\tz: " + `z`
274 print self.print_prefix + "\ttau: " + `tau`
275 else:
276 if self.print_flag >= 2:
277 print self.print_prefix + "||p|| >= delta"
278 print self.print_prefix + "\tNo doing anything???"
279
280
281 q = matrixmultiply(inverse(transpose(R)), p)
282
283
284 if len_p < self.delta:
285 self.lU = min(self.lU, self.l)
286 else:
287 self.lL = max(self.lL, self.l)
288
289
290 self.l_corr = dot_p / dot(q, q) * ((len_p - self.delta) / self.delta)
291
292 else:
293
294 self.lS = max(self.lS, self.l)
295
296 self.l_corr = -self.l
297
298
299 self.lL = max(self.lL, self.lS)
300 if self.print_flag >= 2:
301 print self.print_prefix + "Update lL: " + `self.lL`
302
303
304
305
306 self.l = self.l + self.l_corr
307
308 iter = iter + 1
309
310
311 self.xk_new = self.xk + self.pk
312 self.fk_new, self.f_count = apply(self.func, (self.xk_new,)+self.args), self.f_count + 1
313 self.dfk_new, self.g_count = apply(self.dfunc, (self.xk_new,)+self.args), self.g_count + 1
314
315
317 """Safeguarding function."""
318
319 if self.lambda_l < -eigenvals[0]:
320 self.lambda_l = -eigenvals[0] + 1.0
321 if self.print_flag >= 2:
322 print self.print_prefix + "\tSafeguarding. lambda(l) = " + `self.lambda_l`
323 elif self.lambda_l <= 0.0:
324 if self.print_flag >= 2:
325 print self.print_prefix + "\tSafeguarding. lambda(l)=0"
326 self.lambda_l = 0.0
327
328
330 """Update function.
331
332 Run the trust region update. If this update decides to shift xk+1 to xk, then run the
333 Newton update.
334 """
335
336 self.trust_region_update()
337 if self.shift_flag:
338 self.specific_update()
339