3 Local search - A trust region algorithm with BFGS update.
9 #include "stogo_config.h"
15 # define IF_NLOPT_CHECK_EVALS stop->nevals++; \
16 if (nlopt_stop_evalstime(stop)) \
19 # define IF_NLOPT_CHECK_EVALS
22 ////////////////////////////////////////////////////////////////////////
23 // SGJ, 2007: allow local to use local optimizers in NLopt, to compare
24 // to the BFGS code below
34 static double f_local(int n, const double *x, double *grad, void *data_)
36 f_local_data *data = (f_local_data *) data_;
39 // hack to avoid pointless copy of x and grad
41 xv.elements = const_cast<double *>(x);
43 f=data->glob->ObjectiveGradient(xv, gv,
44 grad?OBJECTIVE_AND_GRADIENT:OBJECTIVE_ONLY);
45 if (grad) data->maxgrad = max(data->maxgrad, normInf(gv));
46 xv.elements = gv.elements = 0; // prevent deallocation
51 ////////////////////////////////////////////////////////////////////////
53 int local(Trial &T, TBox &box, TBox &domain, double eps_cl, double *mgr,
54 Global &glob, int axis, RCRVector x_av
56 , nlopt_stopping *stop
67 cout << "Local Search, x=" << x << endl;
70 if (box.OutsideBox(x, domain) != 0) {
71 cout << "Starting point is not inside the boundary. Exiting...\n" ;
76 // Check if we are close to a stationary point located previously
77 if (box.CloseToMin(x, &tmp, eps_cl)) {
79 cout << "Close to a previously located stationary point, exiting" << endl;
88 cout << "NLopt code only works with axis == -1, exiting...\n" ;
95 nlopt_result ret = nlopt_minimize(NLOPT_LOCAL_LBFGS, n, f_local, &data,
96 box.lb.raw_data(), box.ub.raw_data(),
99 stop->ftol_rel, stop->ftol_abs,
100 stop->xtol_rel, stop->xtol_abs,
101 stop->maxeval - stop->nevals,
102 stop->maxtime - stop->start);
104 T.xvals=x ; T.objval=f ;
105 if (ret == NLOPT_MAXEVAL_REACHED || ret == NLOPT_MAXTIME_REACHED)
106 return LS_MaxEvalTime;
110 return LS_Out; // failure
112 #else /* not using NLopt local optimizer ... use original STOgo BFGS code */
114 int k_max, info, outside = 0;
115 int k, i, good_enough, iTmp ;
117 double maxgrad, delta, f_new;
118 double alpha, gamma, beta, d2, s2, nom, den, ro ;
119 double nrm_sd, nrm_hn, snrm_hn, nrm_dl ;
120 RVector g(n), h_sd(n), h_dl(n), h_n(n), x_new(n), g_new(n) ;
121 RVector s(n),y(n),z(n),w(n) ; // Temporary vectors
122 RMatrix B(n), H(n) ; // Hessian and it's inverse
126 // Initially B and H are equal to the identity matrix
128 for (i=0 ; i<n ; i++) {
133 RVector g_av(x_av.GetLength());
135 f=glob.ObjectiveGradient(x,g,OBJECTIVE_AND_GRADIENT);
139 f=glob.ObjectiveGradient(x_av,g_av,OBJECTIVE_AND_GRADIENT);
142 IF_NLOPT_CHECK_EVALS;
148 // Elaborate scheme to initalize delta
149 delta=delta_coef*norm2(g) ;
152 if (!box.InsideBox(z)) {
153 if (box.Intersection(x,g,z)==TRUE) {
155 delta=min(delta,delta_coef*norm2(z)) ;
158 // Algorithm broke down, use INI1
159 delta = (1.0/7)*box.ShortestSide(&iTmp) ;
165 delta = box.ClosestSide(x)*delta_coef ;
166 if (delta<MacEpsilon)
167 // Patch to avoid trust region with radius close to zero
168 delta = (1.0/7)*box.ShortestSide(&iTmp) ;
171 delta = delta_coef*box.ShortestSide(&iTmp) ;
175 // Use a simple scheme for the 1D minimization (INI1)
176 delta = (1.0/7.0)*box.ShortestSide(&iTmp) ;
179 k=0 ; good_enough = 0 ; info=LS_New ; outside=0 ;
181 while (good_enough == 0) {
185 cout << "Maximum number of iterations reached\n" ;
191 // Update maximal gradient value
192 maxgrad=max(maxgrad,normInf(g)) ;
194 // Steepest descent, h_sd = -g
199 if (nrm_sd < epsilon) {
200 // Stop criterion (gradient) fullfilled
202 cout << "Gradient small enough" << endl ;
208 // Compute Newton step, h_n = -H*g
209 gemv('N',-1.0, H, g, 0.0, h_n) ;
210 nrm_hn = norm2(h_n) ;
212 if (nrm_hn < delta) {
216 cout << "[Newton step] " ;
220 gemv('N',1.0,B,g,0.0,z) ;
226 alpha=(nrm_sd*nrm_sd)/tmp ; // Normalization (N38,eq. 3.30)
228 nrm_sd=fabs(alpha)*nrm_sd ;
230 if (nrm_sd >= delta) {
231 gamma = delta/nrm_sd ; // Normalization (N38, eq. 3.33)
235 cout << "[Steepest descent] " ;
239 // Combination of Newton and SD steps
244 snrm_hn=nrm_hn*nrm_hn ;
246 den = tmp-s2 + sqrt((tmp-d2)*(tmp-d2)+(snrm_hn-d2)*(d2-s2)) ;
251 // Normalization (N38, eq. 3.31)
255 axpy((1-beta),h_sd,h_dl) ;
257 cout << "[Mixed step] " ;
265 axpy(1.0,h_dl,x_new) ;
267 // Check if x_new is inside the box
268 iTmp=box.OutsideBox(x_new, domain) ;
271 cout << "x_new is outside the box " << endl ;
274 if (outside>max_outside_steps) {
275 // Previous point was also outside, exit
279 else if (iTmp == 2) {
281 cout << " x_new is outside the domain" << endl ;
292 f_new=glob.ObjectiveGradient(x_new,g_new,OBJECTIVE_AND_GRADIENT);
295 f_new=glob.ObjectiveGradient(x_av,g_av,OBJECTIVE_AND_GRADIENT);
297 IF_NLOPT_CHECK_EVALS;
299 gemv('N',0.5,B,h_dl,0.0,z);
300 ro = (f_new-f) / (dot(g,h_dl) + dot(h_dl,z)); // Quadratic model
308 // Update the Hessian and it's inverse using the BFGS formula
309 #if 0 // changed by SGJ to compute OBJECTIVE_AND_GRADIENT above
311 glob.ObjectiveGradient(x_new,g_new,GRADIENT_ONLY);
314 glob.ObjectiveGradient(x_av,g_av,GRADIENT_ONLY);
318 IF_NLOPT_CHECK_EVALS;
328 // Check curvature condition
330 if (alpha <= sqrt(MacEpsilon)*nrm_dl*norm2(y)) {
332 cout << "Curvature condition violated " ;
337 gemv('N',1.0,B,h_dl,0.0,z) ; // z=Bh_dl
338 beta=-1/dot(h_dl,z) ;
342 // Update Hessian inverse
343 gemv('N',1.0,H,y,0.0,z) ; // z=H*y
344 gemv('T',1.0,H,y,0.0,w) ; // w=y'*H
346 beta=(1+beta/alpha)/alpha ;
348 // It should be possible to do this updating more efficiently, by
349 // exploiting the fact that (h_dl*y'*H) = transpose(H*y*h_dl')
350 ger(beta,h_dl,h_dl,H) ;
351 ger(-1/alpha,z,h_dl,H) ;
352 ger(-1/alpha,h_dl,w,H) ;
355 if (nrm_dl < norm2(x)*epsilon) {
356 // Stop criterion (iteration progress) fullfilled
358 cout << "Progress is marginal" ;
363 // Check if we are close to a stationary point located previously
364 if (box.CloseToMin(x_new, &f_new, eps_cl)) {
365 // Note that x_new and f_new may be overwritten on exit from CloseToMin
367 cout << "Close to a previously located stationary point, exiting" << endl;
374 copy(x_new,x) ; copy(g_new,g) ; f=f_new ;
377 cout << " x=" << x << endl ;
383 cout << "Step is no good, ro=" << ro << " delta=" << delta << endl ;
389 // Make sure the routine returns correctly...
390 // Check if last iterate is outside the boundary
391 if (box.OutsideBox(x, domain) != 0) {
392 info=LS_Out; f=DBL_MAX;
395 if (info == LS_Unstable) {
396 cout << "Local search became unstable. No big deal but exiting anyway\n" ;
402 T.xvals=x ; T.objval=f ;