5 #include "nlopt-util.h"
11 #define MIN(a,b) ((a) < (b) ? (a) : (b))
12 #define MAX(a,b) ((a) > (b) ? (a) : (b))
14 /***************************************************************************/
15 /* basic data structure:
17 * a hyper-rectangle is stored as an array of length L = 2n+2, where [1]
18 * is the value (f) of the function at the center, [0] is the "size"
19 * measure (d) of the rectangle, [2..n+1] are the coordinates of the
20 * center (c), and [n+2..2n+1] are the widths of the sides (w).
22 * a list of rectangles is just an array of N hyper-rectangles
23 * stored as an N x L in row-major order. Generally,
24 * we allocate more than we need, allocating Na hyper-rectangles.
29 #define RECT_LEN(n) (2*(n)+2) /* number of double values in a hyperrect */
31 /* parameters of the search algorithm and various information that
32 needs to be passed around */
34 int n; /* dimension */
35 int L; /* RECT_LEN(n) */
36 double magic_eps; /* Jones' epsilon parameter (1e-4 is recommended) */
37 int which_diam; /* which measure of hyper-rectangle diam to use:
38 0 = Jones, 1 = Gablonsky */
39 int which_div; /* which way to divide rects:
40 0: Gablonsky (cubes divide all, rects longest)
41 1: orig. Jones (divide all longest sides)
42 2: Jones Encyc. Opt.: pick random longest side */
43 const double *lb, *ub;
44 nlopt_stopping *stop; /* stopping criteria */
45 nlopt_func f; void *f_data;
46 double *work; /* workspace, of length >= 2*n */
47 int *iwork; /* workspace, length >= n */
48 double fmin, *xmin; /* minimum so far */
50 /* red-black tree of hyperrects, sorted by (d,f) in
51 lexographical order */
53 double **hull; /* array to store convex hull */
54 int hull_len; /* allocated length of hull array */
57 /***************************************************************************/
59 /* evaluate the "diameter" (d) of a rectangle of widths w[n] */
60 static double rect_diameter(int n, const double *w, const params *p)
63 if (p->which_diam == 0) { /* Jones measure */
65 for (i = 0; i < n; ++i)
67 return sqrt(sum) * 0.5; /* distance from center to a vertex */
69 else { /* Gablonsky measure */
71 for (i = 0; i < n; ++i)
74 return maxw * 0.5; /* half-width of longest side */
78 #define ALLOC_RECT(rect, L) if (!(rect = (double*) malloc(sizeof(double)*(L)))) return NLOPT_OUT_OF_MEMORY
80 static double *fv_qsort = 0;
81 static int sort_fv_compare(const void *a_, const void *b_)
83 int a = *((const int *) a_), b = *((const int *) b_);
84 double fa = MIN(fv_qsort[2*a], fv_qsort[2*a+1]);
85 double fb = MIN(fv_qsort[2*b], fv_qsort[2*b+1]);
93 static void sort_fv(int n, double *fv, int *isort)
96 for (i = 0; i < n; ++i) isort[i] = i;
97 fv_qsort = fv; /* not re-entrant, sigh... */
98 qsort(isort, (unsigned) n, sizeof(int), sort_fv_compare);
102 static double function_eval(const double *x, params *p) {
103 double f = p->f(p->n, x, NULL, p->f_data);
106 memcpy(p->xmin, x, sizeof(double) * p->n);
111 #define FUNCTION_EVAL(fv,x,p,freeonerr) fv = function_eval(x, p); if (p->fmin < p->stop->fmin_max) { free(freeonerr); return NLOPT_FMIN_MAX_REACHED; } else if (nlopt_stop_evals((p)->stop)) { free(freeonerr); return NLOPT_MAXEVAL_REACHED; } else if (nlopt_stop_time((p)->stop)) { free(freeonerr); return NLOPT_MAXTIME_REACHED; }
113 #define THIRD (0.3333333333333333333333)
115 #define EQUAL_SIDE_TOL 5e-2 /* tolerance to equate side sizes */
117 /* divide rectangle idiv in the list p->rects */
118 static nlopt_result divide_rect(double *rdiv, params *p)
121 const const int n = p->n;
123 double *c = rdiv + 2; /* center of rect to divide */
124 double *w = c + n; /* widths of rect to divide */
126 int imax = 0, nlongest = 0;
129 for (i = 1; i < n; ++i)
132 for (i = 0; i < n; ++i)
133 if (wmax - w[i] <= wmax * EQUAL_SIDE_TOL)
135 if (p->which_div == 1 || (p->which_div == 0 && nlongest == n)) {
136 /* trisect all longest sides, in increasing order of the average
137 function value along that direction */
138 double *fv = p->work;
139 int *isort = p->iwork;
140 for (i = 0; i < n; ++i) {
141 if (wmax - w[i] <= wmax * EQUAL_SIDE_TOL) {
143 c[i] = csave - w[i] * THIRD;
144 FUNCTION_EVAL(fv[2*i], c, p, 0);
145 c[i] = csave + w[i] * THIRD;
146 FUNCTION_EVAL(fv[2*i+1], c, p, 0);
150 fv[2*i] = fv[2*i+1] = HUGE_VAL;
153 sort_fv(n, fv, isort);
154 if (!(node = rb_tree_find(&p->rtree, rdiv)))
155 return NLOPT_FAILURE;
156 for (i = 0; i < nlongest; ++i) {
158 w[isort[i]] *= THIRD;
159 rdiv[0] = rect_diameter(n, w, p);
160 node = rb_tree_resort(&p->rtree, node);
161 for (k = 0; k <= 1; ++k) {
164 memcpy(rnew, rdiv, sizeof(double) * L);
165 rnew[2 + isort[i]] += w[isort[i]] * (2*k-1);
166 rnew[1] = fv[2*isort[i]+k];
167 if (!rb_tree_insert(&p->rtree, rnew)) {
169 return NLOPT_FAILURE;
176 if (nlongest > 1 && p->which_div == 2) {
177 /* randomly choose longest side */
178 i = nlopt_iurand(nlongest);
179 for (k = 0; k < n; ++k)
180 if (wmax - w[k] <= wmax * EQUAL_SIDE_TOL) {
181 if (!i) { i = k; break; }
186 i = imax; /* trisect longest side */
187 if (!(node = rb_tree_find(&p->rtree, rdiv)))
188 return NLOPT_FAILURE;
190 rdiv[0] = rect_diameter(n, w, p);
191 node = rb_tree_resort(&p->rtree, node);
192 for (k = 0; k <= 1; ++k) {
195 memcpy(rnew, rdiv, sizeof(double) * L);
196 rnew[2 + i] += w[i] * (2*k-1);
197 FUNCTION_EVAL(rnew[1], rnew + 2, p, rnew);
198 if (!rb_tree_insert(&p->rtree, rnew)) {
200 return NLOPT_FAILURE;
204 return NLOPT_SUCCESS;
207 /***************************************************************************/
208 /* O(N log N) convex hull algorithm, used later to find the potentially
211 /* Find the lower convex hull of a set of points (x,y) stored in a rb-tree
212 of pointers to {x,y} arrays sorted in lexographic order by (x,y).
214 Unlike standard convex hulls, we allow redundant points on the hull.
216 The return value is the number of points in the hull, with pointers
217 stored in hull[i] (should be an array of length >= t->N).
219 static int convex_hull(rb_tree *t, double **hull)
223 double xmin, xmax, yminmin, ymaxmin;
226 /* Monotone chain algorithm [Andrew, 1979]. */
230 nmax = rb_tree_max(t);
236 hull[nhull++] = n->k;
237 if (xmin == xmax) return nhull;
239 /* set nmax = min mode with x == xmax */
240 while (nmax->k[0] == xmax)
241 nmax = rb_tree_pred(nmax); /* non-NULL since xmin != xmax */
242 nmax = rb_tree_succ(nmax);
244 ymaxmin = nmax->k[1];
245 minslope = (ymaxmin - yminmin) / (xmax - xmin);
247 /* set n = first node with x != xmin */
248 while (n->k[0] == xmin)
249 n = rb_tree_succ(n); /* non-NULL since xmin != xmax */
251 for (; n != nmax; n = rb_tree_succ(n)) {
253 if (k[1] > yminmin + (k[0] - xmin) * minslope)
255 /* remove points until we are making a "left turn" to k */
257 double *t1 = hull[nhull - 1], *t2 = hull[nhull - 2];
258 /* cross product (t1-t2) x (k-t2) > 0 for a left turn: */
259 if ((t1[0]-t2[0]) * (k[1]-t2[1])
260 - (t1[1]-t2[1]) * (k[0]-t2[0]) >= 0)
266 hull[nhull++] = nmax->k;
270 /***************************************************************************/
272 static int small(double *w, params *p)
275 for (i = 0; i < p->n; ++i)
276 if (w[i] > p->stop->xtol_abs[i] &&
277 w[i] > (p->ub[i] - p->lb[i]) * p->stop->xtol_rel)
282 static nlopt_result divide_good_rects(params *p)
286 int nhull, i, xtol_reached = 1, divided_some = 0;
287 double magic_eps = p->magic_eps;
289 if (p->hull_len < p->rtree.N) {
290 p->hull_len += p->rtree.N;
291 p->hull = (double **) realloc(p->hull, sizeof(double*)*p->hull_len);
292 if (!p->hull) return NLOPT_OUT_OF_MEMORY;
294 nhull = convex_hull(&p->rtree, hull = p->hull);
296 for (i = 0; i < nhull; ++i) {
297 double K1 = -HUGE_VAL, K2 = -HUGE_VAL, K;
299 K1 = (hull[i][1] - hull[i-1][1]) / (hull[i][0] - hull[i-1][0]);
301 K1 = (hull[i][1] - hull[i+1][1]) / (hull[i][0] - hull[i+1][0]);
303 if (hull[i][1] - K * hull[i][0]
304 <= p->fmin - magic_eps * fabs(p->fmin)) {
305 /* "potentially optimal" rectangle, so subdivide */
307 nlopt_result ret = divide_rect(hull[i], p);
308 if (ret != NLOPT_SUCCESS) return ret;
309 xtol_reached = xtol_reached && small(hull[i] + 2+n, p);
313 if (magic_eps != 0) {
315 goto divisions; /* try again */
317 else { /* WTF? divide largest rectangle with smallest f */
318 /* (note that this code actually gets called from time
319 to time, and the heuristic here seems to work well,
320 but I don't recall this situation being discussed in
322 rb_node *max = rb_tree_max(&p->rtree);
324 double wmax = max->k[0];
325 do { /* note: this loop is O(N) worst-case time */
327 pred = rb_tree_pred(max);
328 } while (pred && pred->k[0] == wmax);
329 return divide_rect(max->k, p);
332 return xtol_reached ? NLOPT_XTOL_REACHED : NLOPT_SUCCESS;
335 /***************************************************************************/
337 /* lexographic sort order (d,f) of hyper-rects, for red-black tree */
338 static int hyperrect_compare(double *a, double *b)
340 if (a[0] < b[0]) return -1;
341 if (a[0] > b[0]) return +1;
342 if (a[1] < b[1]) return -1;
343 if (a[1] > b[1]) return +1;
344 return (int) (a - b); /* tie-breaker */
347 /***************************************************************************/
349 nlopt_result cdirect_unscaled(int n, nlopt_func f, void *f_data,
350 const double *lb, const double *ub,
353 nlopt_stopping *stop,
354 double magic_eps, int which_alg)
359 nlopt_result ret = NLOPT_OUT_OF_MEMORY;
361 p.magic_eps = magic_eps;
362 p.which_diam = which_alg % 2;
364 p.lb = lb; p.ub = ub;
371 p.fmin = f(n, x, NULL, f_data); stop->nevals++;
376 rb_tree_init(&p.rtree, hyperrect_compare);
378 p.work = (double *) malloc(sizeof(double) * (2*n));
379 if (!p.work) goto done;
380 p.iwork = (int *) malloc(sizeof(int) * n);
381 if (!p.iwork) goto done;
382 p.hull_len = 128; /* start with a reasonable number */
383 p.hull = (double **) malloc(sizeof(double *) * p.hull_len);
384 if (!p.hull) goto done;
386 if (!(rnew = (double *) malloc(sizeof(double) * p.L))) goto done;
387 for (i = 0; i < n; ++i) {
388 rnew[2+i] = 0.5 * (lb[i] + ub[i]);
390 && (fabs(rnew[2+i]-x[i]) < 1e-13*(1+fabs(x[i])));
391 rnew[2+n+i] = ub[i] - lb[i];
393 rnew[0] = rect_diameter(n, rnew+2+n, &p);
395 rnew[1] = p.fmin; /* avoid computing f(center) twice */
397 rnew[1] = function_eval(rnew+2, &p);
398 if (!rb_tree_insert(&p.rtree, rnew)) {
403 ret = divide_rect(rnew, &p);
404 if (ret != NLOPT_SUCCESS) goto done;
407 double fmin0 = p.fmin;
408 ret = divide_good_rects(&p);
409 if (ret != NLOPT_SUCCESS) goto done;
410 if (nlopt_stop_f(p.stop, p.fmin, fmin0)) {
411 ret = NLOPT_FTOL_REACHED;
417 rb_tree_destroy_with_keys(&p.rtree);
426 /* in the conventional DIRECT-type algorithm, we first rescale our
427 coordinates to a unit hypercube ... we do this simply by
428 wrapping cdirect() around cdirect_unscaled(). */
434 const double *lb, *ub;
436 static double uf(int n, const double *xu, double *grad, void *d_)
438 uf_data *d = (uf_data *) d_;
441 for (i = 0; i < n; ++i)
442 d->x[i] = d->lb[i] + xu[i] * (d->ub[i] - d->lb[i]);
443 f = d->f(n, d->x, grad, d->f_data);
445 for (i = 0; i < n; ++i)
446 grad[i] *= d->ub[i] - d->lb[i];
450 nlopt_result cdirect(int n, nlopt_func f, void *f_data,
451 const double *lb, const double *ub,
454 nlopt_stopping *stop,
455 double magic_eps, int which_alg)
459 const double *xtol_abs_save;
462 d.f = f; d.f_data = f_data; d.lb = lb; d.ub = ub;
463 d.x = (double *) malloc(sizeof(double) * n*4);
464 if (!d.x) return NLOPT_OUT_OF_MEMORY;
466 for (i = 0; i < n; ++i) {
467 x[i] = (x[i] - lb[i]) / (ub[i] - lb[i]);
470 d.x[3*n+i] = stop->xtol_abs[i] / (ub[i] - lb[i]);
472 xtol_abs_save = stop->xtol_abs;
473 stop->xtol_abs = d.x + 3*n;
474 ret = cdirect_unscaled(n, uf, &d, d.x+n, d.x+2*n, x, fmin, stop,
475 magic_eps, which_alg);
476 stop->xtol_abs = xtol_abs_save;
477 for (i = 0; i < n; ++i)
478 x[i] = lb[i]+ x[i] * (ub[i] - lb[i]);