5 #include "nlopt-util.h"
10 #define MIN(a,b) ((a) < (b) ? (a) : (b))
11 #define MAX(a,b) ((a) > (b) ? (a) : (b))
13 /***************************************************************************/
14 /* basic data structure:
16 * a hyper-rectangle is stored as an array of length 2n+2, where [0]
17 * is the value of the function at the center, [1] is the "size"
18 * measure of the rectangle, [2..n+1] are the coordinates of the
19 * center, and [n+2..2n+1] are the widths of the sides.
21 * a list of rectangles is just an array of N hyper-rectangles
22 * stored as an N x (2n+1) in row-major order. Generally,
23 * we allocate more than we need, allocating Na hyper-rectangles.
28 #define RECT_LEN(n) (2*(n)+2) /* number of double values in a hyperrect */
30 /* parameters of the search algorithm and various information that
31 needs to be passed around */
33 double magic_eps; /* Jones' epsilon parameter (1e-4 is recommended) */
34 int which_diam; /* which measure of hyper-rectangle diam to use:
35 0 = Jones, 1 = Gablonsky */
36 int which_div; /* which way to divide rects:
37 0: Gablonsky (cubes divide all, rects longest)
38 1: orig. Jones (divide all longest sides)
39 2: Jones Encyc. Opt.: pick random longest side */
40 const double *lb, *ub;
41 nlopt_stopping *stop; /* stopping criteria */
43 nlopt_func f; void *f_data;
44 double *work; /* workspace, of length >= 2*n */
45 int *iwork, iwork_len; /* workspace, of length iwork_len >= n */
46 double fmin, *xmin; /* minimum so far */
49 /***************************************************************************/
51 /* evaluate the "diameter" (d) of a rectangle of widths w[n] */
52 static double rect_diameter(int n, const double *w, const params *p)
55 if (p->which_diam == 0) { /* Jones measure */
57 for (i = 0; i < n; ++i)
59 return sqrt(sum) * 0.5; /* distance from center to a vertex */
61 else { /* Gablonsky measure */
63 for (i = 0; i < n; ++i)
66 return maxw * 0.5; /* half-width of longest side */
70 static double *alloc_rects(int n, int *Na, double *rects, int newN)
76 return realloc(rects, sizeof(double) * RECT_LEN(n) * (*Na));
79 #define ALLOC_RECTS(n, Nap, rects, newN) if (!(rects = alloc_rects(n, Nap, rects, newN))) return NLOPT_OUT_OF_MEMORY
81 static double *fv_qsort = 0;
82 static int sort_fv_compare(const void *a_, const void *b_)
84 int a = *((const int *) a_), b = *((const int *) b_);
85 double fa = MIN(fv_qsort[2*a], fv_qsort[2*a+1]);
86 double fb = MIN(fv_qsort[2*b], fv_qsort[2*b+1]);
94 static void sort_fv(int n, double *fv, int *isort)
97 for (i = 0; i < n; ++i) isort[i] = i;
98 fv_qsort = fv; /* not re-entrant, sigh... */
99 qsort(isort, (unsigned) n, sizeof(int), sort_fv_compare);
103 static double function_eval(const double *x, params *p) {
104 double f = p->f(p->n, x, NULL, p->f_data);
107 memcpy(p->xmin, x, sizeof(double) * p->n);
112 #define FUNCTION_EVAL(fv,x,p) fv = function_eval(x, p); if (p->fmin < p->stop->fmin_max) return NLOPT_FMIN_MAX_REACHED; else if (nlopt_stop_evals((p)->stop)) return NLOPT_MAXEVAL_REACHED; else if (nlopt_stop_time((p)->stop)) return NLOPT_MAXTIME_REACHED
114 #define THIRD (0.3333333333333333333333)
116 #define EQUAL_SIDE_TOL 5e-2 /* tolerance to equate side sizes */
118 /* divide rectangle idiv in the list rects */
119 static nlopt_result divide_rect(int *N, int *Na, double **rects, int idiv,
123 const const int n = p->n;
124 const int L = RECT_LEN(n);
126 double *c = r + L*idiv + 2; /* center of rect to divide */
127 double *w = c + n; /* widths of rect to divide */
129 int imax = 0, nlongest = 0;
131 for (i = 1; i < n; ++i)
134 for (i = 0; i < n; ++i)
135 if (wmax - w[i] <= wmax * EQUAL_SIDE_TOL)
137 if (p->which_div == 1 || (p->which_div == 0 && nlongest == n)) {
138 /* trisect all longest sides, in increasing order of the average
139 function value along that direction */
140 double *fv = p->work;
141 int *isort = p->iwork;
142 for (i = 0; i < n; ++i) {
143 if (wmax - w[i] <= wmax * EQUAL_SIDE_TOL) {
145 c[i] = csave - w[i] * THIRD;
146 FUNCTION_EVAL(fv[2*i], c, p);
147 c[i] = csave + w[i] * THIRD;
148 FUNCTION_EVAL(fv[2*i+1], c, p);
152 fv[2*i] = fv[2*i+1] = HUGE_VAL;
155 sort_fv(n, fv, isort);
156 ALLOC_RECTS(n, Na, r, (*N)+2*nlongest);
157 *rects = r; c = r + L*idiv + 2; w = c + n;
158 for (i = 0; i < nlongest; ++i) {
160 w[isort[i]] *= THIRD;
161 r[L*idiv + 1] = rect_diameter(n, w, p);
162 for (k = 0; k <= 1; ++k) {
163 memcpy(r + L*(*N) + 1, c-1, sizeof(double) * (2*n+1));
164 r[L*(*N) + 2 + isort[i]] += w[isort[i]] * (2*k-1);
165 r[L*(*N)] = fv[2*isort[i]+k];
172 if (nlongest > 1 && p->which_div == 2) {
173 /* randomly choose longest side */
174 i = nlopt_iurand(nlongest);
175 for (k = 0; k < n; ++k)
176 if (wmax - w[k] <= wmax * EQUAL_SIDE_TOL) {
177 if (!i) { i = k; break; }
182 i = imax; /* trisect longest side */
183 ALLOC_RECTS(n, Na, r, (*N)+2);
184 *rects = r; c = r + L*idiv + 2; w = c + n;
186 r[L*idiv + 1] = rect_diameter(n, w, p);
187 for (k = 0; k <= 1; ++k) {
188 memcpy(r + L*(*N) + 1, c-1, sizeof(double) * (2*n+1));
189 r[L*(*N) + 2 + i] += w[i] * (2*k-1);
190 FUNCTION_EVAL(r[L*(*N)], r + L*(*N) + 2, p);
194 return NLOPT_SUCCESS;
197 /***************************************************************************/
198 /* O(N log N) convex hull algorithm, used later to find the potentially
201 /* sort ihull by xy in lexographic order by x,y */
202 static int s_qsort = 1; static double *xy_qsort = 0;
203 static int sort_xy_compare(const void *a_, const void *b_)
205 int a = *((const int *) a_), b = *((const int *) b_);
206 double xa = xy_qsort[a*s_qsort+1], xb = xy_qsort[b*s_qsort+1];
207 if (xa < xb) return -1;
208 else if (xb < xa) return +1;
210 double ya = xy_qsort[a*s_qsort], yb = xy_qsort[b*s_qsort];
211 if (ya < yb) return -1;
212 else if (ya > yb) return +1;
216 static void sort_xy(int N, double *xy, int s, int *isort)
220 for (i = 0; i < N; ++i) isort[i] = i;
221 s_qsort = s; xy_qsort = xy;
222 qsort(isort, (unsigned) N, sizeof(int), sort_xy_compare);
226 /* Find the lower convex hull of a set of points (xy[s*i+1], xy[s*i]), where
227 0 <= i < N and s >= 2.
229 The return value is the number of points in the hull, with indices
230 stored in ihull. ihull and is should point to arrays of length >= N.
232 Note that we don't allow redundant points along the same line in the
233 hull, similar to Gablonsky's version of DIRECT and differing from
235 static int convex_hull(int N, double *xy, int s, int *ihull, int *is)
237 int minmin; /* first index (smallest y) with min x */
238 int minmax; /* last index (largest y) with min x */
239 int maxmin; /* first index (smallest y) with max x */
240 int maxmax; /* last index (largest y) with max x */
245 /* Monotone chain algorithm [Andrew, 1979]. */
247 sort_xy(N, xy, s, is);
249 xmin = xy[s*is[minmin=0]+1]; xmax = xy[s*is[maxmax=N-1]+1];
251 if (xmin == xmax) { /* degenerate case */
252 ihull[nhull++] = is[minmin];
256 for (minmax = minmin; minmax+1 < N && xy[s*is[minmax+1]+1]==xmin;
258 for (maxmin = maxmax; maxmin-1>=0 && xy[s*is[maxmin-1]+1]==xmax;
261 minslope = (xy[s*is[maxmin]] - xy[s*is[minmin]]) / (xmax - xmin);
263 ihull[nhull++] = is[minmin];
264 for (i = minmax + 1; i < maxmin; ++i) {
266 if (xy[s*k] > xy[s*is[minmin]] + (xy[s*k+1] - xmin) * minslope)
268 /* remove points until we are making a "left turn" to k */
270 int t1 = ihull[nhull - 1], t2 = ihull[nhull - 2];
271 /* cross product (t1-t2) x (k-t2) > 0 for a left turn: */
272 if ((xy[s*t1+1]-xy[s*t2+1]) * (xy[s*k]-xy[s*t2])
273 - (xy[s*t1]-xy[s*t2]) * (xy[s*k+1]-xy[s*t2+1]) > 0)
279 ihull[nhull++] = is[maxmin];
283 /***************************************************************************/
285 static int small(double *w, params *p)
288 for (i = 0; i < p->n; ++i)
289 if (w[i] > p->stop->xtol_abs[i] &&
290 w[i] > (p->ub[i] - p->lb[i]) * p->stop->xtol_rel)
295 static nlopt_result divide_good_rects(int *N, int *Na, double **rects,
299 const int L = RECT_LEN(n);
300 int *ihull, nhull, i, xtol_reached = 1, divided_some = 0;
302 double magic_eps = p->magic_eps;
304 if (p->iwork_len < n + 2*(*N)) {
305 p->iwork_len = p->iwork_len + n + 2*(*N);
306 p->iwork = (int *) realloc(p->iwork, sizeof(int) * p->iwork_len);
308 return NLOPT_OUT_OF_MEMORY;
311 nhull = convex_hull(*N, r, L, ihull, ihull + *N);
313 for (i = 0; i < nhull; ++i) {
314 double K1 = -HUGE_VAL, K2 = -HUGE_VAL, K;
316 K1 = (r[L*ihull[i]] - r[L*ihull[i-1]]) /
317 (r[L*ihull[i]+1] - r[L*ihull[i-1]+1]);
319 K1 = (r[L*ihull[i]] - r[L*ihull[i+1]]) /
320 (r[L*ihull[i]+1] - r[L*ihull[i+1]+1]);
322 if (r[L*ihull[i]] - K * r[L*ihull[i]+1]
323 <= p->fmin - magic_eps * fabs(p->fmin)) {
324 /* "potentially optimal" rectangle, so subdivide */
327 ret = divide_rect(N, Na, rects, ihull[i], p);
328 r = *rects; /* may have grown */
329 if (ret != NLOPT_SUCCESS) return ret;
330 xtol_reached = xtol_reached && small(r + L*ihull[i] + 2+n, p);
334 if (magic_eps != 0) {
336 goto divisions; /* try again */
338 else { /* WTF? divide largest rectangle */
341 for (i = 1; i < *N; ++i)
343 wmax = r[L*(imax=i)+1];
344 return divide_rect(N, Na, rects, imax, p);
347 return xtol_reached ? NLOPT_XTOL_REACHED : NLOPT_SUCCESS;
350 /***************************************************************************/
352 nlopt_result cdirect_unscaled(int n, nlopt_func f, void *f_data,
353 const double *lb, const double *ub,
356 nlopt_stopping *stop,
357 double magic_eps, int which_alg)
361 int Na = 100, N = 1, i, x_center = 1;
362 nlopt_result ret = NLOPT_OUT_OF_MEMORY;
364 p.magic_eps = magic_eps;
365 p.which_diam = which_alg % 2;
367 p.lb = lb; p.ub = ub;
373 p.fmin = f(n, x, NULL, f_data); stop->nevals++;
377 p.work = (double *) malloc(sizeof(double) * 2*n);
378 if (!p.work) goto done;
379 rects = (double *) malloc(sizeof(double) * Na * RECT_LEN(n));
380 if (!rects) goto done;
381 p.iwork = (int *) malloc(sizeof(int) * (p.iwork_len = 2*Na + n));
382 if (!p.iwork) goto done;
384 for (i = 0; i < n; ++i) {
385 rects[2+i] = 0.5 * (lb[i] + ub[i]);
387 && (fabs(rects[2+i]-x[i]) < 1e-13*(1+fabs(x[i])));
388 rects[2+n+i] = ub[i] - lb[i];
390 rects[1] = rect_diameter(n, rects+2+n, &p);
392 rects[0] = p.fmin; /* avoid computing f(center) twice */
394 rects[0] = function_eval(rects+2, &p);
396 ret = divide_rect(&N, &Na, &rects, 0, &p);
397 if (ret != NLOPT_SUCCESS) goto done;
400 double fmin0 = p.fmin;
401 ret = divide_good_rects(&N, &Na, &rects, &p);
402 if (ret != NLOPT_SUCCESS) goto done;
403 if (nlopt_stop_f(p.stop, p.fmin, fmin0)) {
404 ret = NLOPT_FTOL_REACHED;
418 /* in the conventional DIRECT-type algorithm, we first rescale our
419 coordinates to a unit hypercube ... we do this simply by
420 wrapping cdirect() around cdirect_unscaled(). */
426 const double *lb, *ub;
428 static double uf(int n, const double *xu, double *grad, void *d_)
430 uf_data *d = (uf_data *) d_;
433 for (i = 0; i < n; ++i)
434 d->x[i] = d->lb[i] + xu[i] * (d->ub[i] - d->lb[i]);
435 f = d->f(n, d->x, grad, d->f_data);
437 for (i = 0; i < n; ++i)
438 grad[i] *= d->ub[i] - d->lb[i];
442 nlopt_result cdirect(int n, nlopt_func f, void *f_data,
443 const double *lb, const double *ub,
446 nlopt_stopping *stop,
447 double magic_eps, int which_alg)
451 const double *xtol_abs_save;
454 d.f = f; d.f_data = f_data; d.lb = lb; d.ub = ub;
455 d.x = (double *) malloc(sizeof(double) * n*4);
456 if (!d.x) return NLOPT_OUT_OF_MEMORY;
458 for (i = 0; i < n; ++i) {
459 x[i] = (x[i] - lb[i]) / (ub[i] - lb[i]);
462 d.x[3*n+i] = stop->xtol_abs[i] / (ub[i] - lb[i]);
464 xtol_abs_save = stop->xtol_abs;
465 stop->xtol_abs = d.x + 3*n;
466 ret = cdirect_unscaled(n, uf, &d, d.x+n, d.x+2*n, x, fmin, stop,
467 magic_eps, which_alg);
468 stop->xtol_abs = xtol_abs_save;
469 for (i = 0; i < n; ++i)
470 x[i] = lb[i]+ x[i] * (ub[i] - lb[i]);