+/* Copyright (c) 2007-2014 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "nlopt.h"
#include "cdirect.h"
#include "redblack.h"
-#include "config.h"
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) > (b) ? (a) : (b))
/***************************************************************************/
/* basic data structure:
*
- * a hyper-rectangle is stored as an array of length L = 2n+2, where [1]
+ * a hyper-rectangle is stored as an array of length L = 2n+3, where [1]
* is the value (f) of the function at the center, [0] is the "size"
- * measure (d) of the rectangle, [2..n+1] are the coordinates of the
- * center (c), and [n+2..2n+1] are the widths of the sides (w).
+ * measure (d) of the rectangle, [3..n+2] are the coordinates of the
+ * center (c), [n+3..2n+2] are the widths of the sides (w), and [2]
+ * is an "age" measure for tie-breaking purposes.
*
* we store the hyper-rectangles in a red-black tree, sorted by (d,f)
* in lexographic order, to allow us to perform quick convex-hull
* calculations (in the future, we might make this data structure
* more sophisticated based on the dynamic convex-hull literature).
*
- * n > 0 always
+ * n > 0 always, of course.
*/
-#define RECT_LEN(n) (2*(n)+2) /* number of double values in a hyperrect */
-
/* parameters of the search algorithm and various information that
needs to be passed around */
typedef struct {
int n; /* dimension */
- int L; /* RECT_LEN(n) */
+ int L; /* size of each rectangle (2n+3) */
double magic_eps; /* Jones' epsilon parameter (1e-4 is recommended) */
int which_diam; /* which measure of hyper-rectangle diam to use:
0 = Jones, 1 = Gablonsky */
int which_div; /* which way to divide rects:
- 0: Gablonsky (cubes divide all, rects longest)
- 1: orig. Jones (divide all longest sides)
+ 0: orig. Jones (divide all longest sides)
+ 1: Gablonsky (cubes divide all, rects longest)
2: Jones Encyc. Opt.: pick random longest side */
+ int which_opt; /* which rects are considered "potentially optimal"
+ 0: Jones (all pts on cvx hull, even equal pts)
+ 1: Gablonsky DIRECT-L (pick one pt, if equal pts)
+ 2: ~ 1, but pick points randomly if equal pts
+ ... 2 seems to suck compared to just picking oldest pt */
+
const double *lb, *ub;
nlopt_stopping *stop; /* stopping criteria */
nlopt_func f; void *f_data;
double *work; /* workspace, of length >= 2*n */
int *iwork; /* workspace, length >= n */
- double fmin, *xmin; /* minimum so far */
+ double minf, *xmin; /* minimum so far */
- /* red-black tree of hyperrects, sorted by (d,f) in
+ /* red-black tree of hyperrects, sorted by (d,f,age) in
lexographical order */
rb_tree rtree;
+ int age; /* age for next new rect */
double **hull; /* array to store convex hull */
int hull_len; /* allocated length of hull array */
} params;
#define ALLOC_RECT(rect, L) if (!(rect = (double*) malloc(sizeof(double)*(L)))) return NLOPT_OUT_OF_MEMORY
-static double *fv_qsort = 0;
-static int sort_fv_compare(const void *a_, const void *b_)
+static int sort_fv_compare(void *fv_, const void *a_, const void *b_)
{
+ const double *fv = (const double *) fv_;
int a = *((const int *) a_), b = *((const int *) b_);
- double fa = MIN(fv_qsort[2*a], fv_qsort[2*a+1]);
- double fb = MIN(fv_qsort[2*b], fv_qsort[2*b+1]);
+ double fa = MIN(fv[2*a], fv[2*a+1]);
+ double fb = MIN(fv[2*b], fv[2*b+1]);
if (fa < fb)
return -1;
else if (fa > fb)
{
int i;
for (i = 0; i < n; ++i) isort[i] = i;
- fv_qsort = fv; /* not re-entrant, sigh... */
- qsort(isort, (unsigned) n, sizeof(int), sort_fv_compare);
- fv_qsort = 0;
+ nlopt_qsort_r(isort, (unsigned) n, sizeof(int), fv, sort_fv_compare);
}
static double function_eval(const double *x, params *p) {
double f = p->f(p->n, x, NULL, p->f_data);
- if (f < p->fmin) {
- p->fmin = f;
+ if (f < p->minf) {
+ p->minf = f;
memcpy(p->xmin, x, sizeof(double) * p->n);
}
p->stop->nevals++;
return f;
}
-#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; }
+#define FUNCTION_EVAL(fv,x,p,freeonerr) fv = function_eval(x, p); if (nlopt_stop_forced((p)->stop)) { free(freeonerr); return NLOPT_FORCED_STOP; } else if (p->minf < p->stop->minf_max) { free(freeonerr); return NLOPT_MINF_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; }
#define THIRD (0.3333333333333333333333)
static nlopt_result divide_rect(double *rdiv, params *p)
{
int i;
- const const int n = p->n;
+ const int n = p->n;
const int L = p->L;
- double *c = rdiv + 2; /* center of rect to divide */
+ double *c = rdiv + 3; /* center of rect to divide */
double *w = c + n; /* widths of rect to divide */
double wmax = w[0];
int imax = 0, nlongest = 0;
int k;
w[isort[i]] *= THIRD;
rdiv[0] = rect_diameter(n, w, p);
+ rdiv[2] = p->age++;
node = rb_tree_resort(&p->rtree, node);
for (k = 0; k <= 1; ++k) {
double *rnew;
ALLOC_RECT(rnew, L);
memcpy(rnew, rdiv, sizeof(double) * L);
- rnew[2 + isort[i]] += w[isort[i]] * (2*k-1);
+ rnew[3 + isort[i]] += w[isort[i]] * (2*k-1);
rnew[1] = fv[2*isort[i]+k];
+ rnew[2] = p->age++;
if (!rb_tree_insert(&p->rtree, rnew)) {
free(rnew);
return NLOPT_OUT_OF_MEMORY;
return NLOPT_FAILURE;
w[i] *= THIRD;
rdiv[0] = rect_diameter(n, w, p);
+ rdiv[2] = p->age++;
node = rb_tree_resort(&p->rtree, node);
for (k = 0; k <= 1; ++k) {
double *rnew;
ALLOC_RECT(rnew, L);
memcpy(rnew, rdiv, sizeof(double) * L);
- rnew[2 + i] += w[i] * (2*k-1);
- FUNCTION_EVAL(rnew[1], rnew + 2, p, rnew);
+ rnew[3 + i] += w[i] * (2*k-1);
+ FUNCTION_EVAL(rnew[1], rnew + 3, p, rnew);
+ rnew[2] = p->age++;
if (!rb_tree_insert(&p->rtree, rnew)) {
free(rnew);
return NLOPT_OUT_OF_MEMORY;
/* Find the lower convex hull of a set of points (x,y) stored in a rb-tree
of pointers to {x,y} arrays sorted in lexographic order by (x,y).
- Unlike standard convex hulls, we allow redundant points on the hull.
+ Unlike standard convex hulls, we allow redundant points on the hull,
+ and even allow duplicate points if allow_dups is nonzero.
The return value is the number of points in the hull, with pointers
stored in hull[i] (should be an array of length >= t->N).
*/
-static int convex_hull(rb_tree *t, double **hull)
+static int convex_hull(rb_tree *t, double **hull, int allow_dups)
{
int nhull = 0;
double minslope;
yminmin = n->k[1];
xmax = nmax->k[0];
- do { /* include any duplicate points at (xmin,yminmin) */
+ if (allow_dups)
+ do { /* include any duplicate points at (xmin,yminmin) */
+ hull[nhull++] = n->k;
+ n = rb_tree_succ(n);
+ } while (n && n->k[0] == xmin && n->k[1] == yminmin);
+ else
hull[nhull++] = n->k;
- n = rb_tree_succ(n);
- } while (n && n->k[0] == xmin && n->k[1] == yminmin);
+
if (xmin == xmax) return nhull;
/* set nmax = min mode with x == xmax */
continue;
}
else { /* equal y values, add to hull */
- hull[nhull++] = k;
+ if (allow_dups)
+ hull[nhull++] = k;
continue;
}
}
hull[nhull++] = k;
}
- do { /* include any duplicate points at (xmax,ymaxmin) */
+ if (allow_dups)
+ do { /* include any duplicate points at (xmax,ymaxmin) */
+ hull[nhull++] = nmax->k;
+ nmax = rb_tree_succ(nmax);
+ } while (nmax && nmax->k[0] == xmax && nmax->k[1] == ymaxmin);
+ else
hull[nhull++] = nmax->k;
- nmax = rb_tree_succ(nmax);
- } while (nmax && nmax->k[0] == xmax && n->k[1] == ymaxmin);
return nhull;
}
p->hull = (double **) realloc(p->hull, sizeof(double*)*p->hull_len);
if (!p->hull) return NLOPT_OUT_OF_MEMORY;
}
- nhull = convex_hull(&p->rtree, hull = p->hull);
+ nhull = convex_hull(&p->rtree, hull = p->hull, p->which_opt != 1);
divisions:
for (i = 0; i < nhull; ++i) {
double K1 = -HUGE_VAL, K2 = -HUGE_VAL, K;
int im, ip;
- for (im = i-1; im >= 0 && hull[im][0] == hull[i][0]; --im);
- for (ip = i+1; ip < nhull && hull[ip][0] == hull[i][0]; ++ip);
+
+ /* find unequal points before (im) and after (ip) to get slope */
+ for (im = i-1; im >= 0 && hull[im][0] == hull[i][0]; --im) ;
+ for (ip = i+1; ip < nhull && hull[ip][0] == hull[i][0]; ++ip) ;
+
if (im >= 0)
K1 = (hull[i][1] - hull[im][1]) / (hull[i][0] - hull[im][0]);
if (ip < nhull)
- K1 = (hull[i][1] - hull[ip][1]) / (hull[i][0] - hull[ip][0]);
+ K2 = (hull[i][1] - hull[ip][1]) / (hull[i][0] - hull[ip][0]);
K = MAX(K1, K2);
if (hull[i][1] - K * hull[i][0]
- <= p->fmin - magic_eps * fabs(p->fmin) || ip == nhull) {
+ <= p->minf - magic_eps * fabs(p->minf) || ip == nhull) {
/* "potentially optimal" rectangle, so subdivide */
nlopt_result ret = divide_rect(hull[i], p);
divided_some = 1;
if (ret != NLOPT_SUCCESS) return ret;
- xtol_reached = xtol_reached && small(hull[i] + 2+n, p);
+ xtol_reached = xtol_reached && small(hull[i] + 3+n, p);
}
+
+ /* for the DIRECT-L variant, we only divide one rectangle out
+ of all points with equal diameter and function values
+ ... note that for p->which_opt == 1, i == ip-1 should be a no-op
+ anyway, since we set allow_dups=0 in convex_hull above */
+ if (p->which_opt == 1)
+ i = ip - 1; /* skip to next unequal point for next iteration */
+ else if (p->which_opt == 2) /* like DIRECT-L but randomized */
+ i += nlopt_iurand(ip - i); /* possibly do another equal pt */
}
if (!divided_some) {
if (magic_eps != 0) {
/***************************************************************************/
-/* lexographic sort order (d,f) of hyper-rects, for red-black tree */
-static int hyperrect_compare(double *a, double *b)
+/* lexographic sort order (d,f,age) of hyper-rects, for red-black tree */
+int cdirect_hyperrect_compare(double *a, double *b)
{
if (a[0] < b[0]) return -1;
if (a[0] > b[0]) return +1;
if (a[1] < b[1]) return -1;
if (a[1] > b[1]) return +1;
- return (int) (a - b); /* tie-breaker */
+ if (a[2] < b[2]) return -1;
+ if (a[2] > b[2]) return +1;
+ return (int) (a - b); /* tie-breaker, shouldn't be needed */
}
/***************************************************************************/
nlopt_result cdirect_unscaled(int n, nlopt_func f, void *f_data,
const double *lb, const double *ub,
double *x,
- double *fmin,
+ double *minf,
nlopt_stopping *stop,
double magic_eps, int which_alg)
{
params p;
- int i, x_center = 1;
+ int i;
double *rnew;
nlopt_result ret = NLOPT_OUT_OF_MEMORY;
p.magic_eps = magic_eps;
- p.which_diam = which_alg % 10;
- p.which_div = (which_alg / 10) % 10;
+ p.which_diam = which_alg % 3;
+ p.which_div = (which_alg / 3) % 3;
+ p.which_opt = (which_alg / (3*3)) % 3;
p.lb = lb; p.ub = ub;
p.stop = stop;
p.n = n;
- p.L = RECT_LEN(n);
+ p.L = 2*n+3;
p.f = f;
p.f_data = f_data;
p.xmin = x;
- p.fmin = f(n, x, NULL, f_data); stop->nevals++;
+ p.minf = HUGE_VAL;
p.work = 0;
p.iwork = 0;
p.hull = 0;
+ p.age = 0;
- rb_tree_init(&p.rtree, hyperrect_compare);
+ rb_tree_init(&p.rtree, cdirect_hyperrect_compare);
p.work = (double *) malloc(sizeof(double) * (2*n));
if (!p.work) goto done;
if (!(rnew = (double *) malloc(sizeof(double) * p.L))) goto done;
for (i = 0; i < n; ++i) {
- rnew[2+i] = 0.5 * (lb[i] + ub[i]);
- x_center = x_center
- && (fabs(rnew[2+i]-x[i]) < 1e-13*(1+fabs(x[i])));
- rnew[2+n+i] = ub[i] - lb[i];
+ rnew[3+i] = 0.5 * (lb[i] + ub[i]);
+ rnew[3+n+i] = ub[i] - lb[i];
}
- rnew[0] = rect_diameter(n, rnew+2+n, &p);
- if (x_center)
- rnew[1] = p.fmin; /* avoid computing f(center) twice */
- else
- rnew[1] = function_eval(rnew+2, &p);
+ rnew[0] = rect_diameter(n, rnew+3+n, &p);
+ rnew[1] = function_eval(rnew+3, &p);
+ rnew[2] = p.age++;
if (!rb_tree_insert(&p.rtree, rnew)) {
free(rnew);
goto done;
if (ret != NLOPT_SUCCESS) goto done;
while (1) {
- double fmin0 = p.fmin;
+ double minf0 = p.minf;
ret = divide_good_rects(&p);
if (ret != NLOPT_SUCCESS) goto done;
- if (p.fmin < fmin0 && nlopt_stop_f(p.stop, p.fmin, fmin0)) {
+ if (p.minf < minf0 && nlopt_stop_f(p.stop, p.minf, minf0)) {
ret = NLOPT_FTOL_REACHED;
goto done;
}
free(p.iwork);
free(p.work);
- *fmin = p.fmin;
+ *minf = p.minf;
return ret;
}
coordinates to a unit hypercube ... we do this simply by
wrapping cdirect() around cdirect_unscaled(). */
-typedef struct {
- nlopt_func f;
- void *f_data;
- double *x;
- const double *lb, *ub;
-} uf_data;
-static double uf(int n, const double *xu, double *grad, void *d_)
+double cdirect_uf(unsigned n, const double *xu, double *grad, void *d_)
{
- uf_data *d = (uf_data *) d_;
+ cdirect_uf_data *d = (cdirect_uf_data *) d_;
double f;
- int i;
+ unsigned i;
for (i = 0; i < n; ++i)
d->x[i] = d->lb[i] + xu[i] * (d->ub[i] - d->lb[i]);
f = d->f(n, d->x, grad, d->f_data);
nlopt_result cdirect(int n, nlopt_func f, void *f_data,
const double *lb, const double *ub,
double *x,
- double *fmin,
+ double *minf,
nlopt_stopping *stop,
double magic_eps, int which_alg)
{
- uf_data d;
+ cdirect_uf_data d;
nlopt_result ret;
const double *xtol_abs_save;
int i;
}
xtol_abs_save = stop->xtol_abs;
stop->xtol_abs = d.x + 3*n;
- ret = cdirect_unscaled(n, uf, &d, d.x+n, d.x+2*n, x, fmin, stop,
+ ret = cdirect_unscaled(n, cdirect_uf, &d, d.x+n, d.x+2*n, x, minf, stop,
magic_eps, which_alg);
stop->xtol_abs = xtol_abs_save;
for (i = 0; i < n; ++i)