static void get_minf(mlsl_data *d, double *minf, double *x) { rb_node *node = rb_tree_min(&d->pts); if (node) { *minf = ((pt *) node->k)->f; memcpy(x, ((pt *) node->k)->x, sizeof(double) * d->n); } node = rb_tree_min(&d->lms); if (node && node->k[0] < *minf) { *minf = node->k[0]; memcpy(x, node->k + 1, sizeof(double) * d->n); } }
static nlopt_result crs_trial(crs_data *d) { rb_node *best = rb_tree_min(&d->t); rb_node *worst = rb_tree_max(&d->t); int mutation = NUM_MUTATION; int i, n = d->n; random_trial(d, d->p + 1, best); do { d->p[0] = d->f(n, d->p + 1, NULL, d->f_data); d->stop->nevals++; if (nlopt_stop_forced(d->stop)) return NLOPT_FORCED_STOP; if (d->p[0] < worst->k[0]) break; if (nlopt_stop_evals(d->stop)) return NLOPT_MAXEVAL_REACHED; if (nlopt_stop_time(d->stop)) return NLOPT_MAXTIME_REACHED; if (mutation) { for (i = 0; i < n; ++i) { double w = nlopt_urand(0.,1.); d->p[1+i] = best->k[1+i] * (1 + w) - w * d->p[1+i]; if (d->p[1+i] > d->ub[i]) d->p[1+i] = d->ub[i]; else if (d->p[1+i] < d->lb[i]) d->p[1+i] = d->lb[i]; } mutation--; } else { random_trial(d, d->p + 1, best); mutation = NUM_MUTATION; } } while (1); memcpy(worst->k, d->p, sizeof(double) * (n+1)); rb_tree_resort(&d->t, worst); return NLOPT_SUCCESS; }
void rb_tree_destroy_with_keys(rb_tree *t) { rb_node *n = rb_tree_min(t); while (n) { free(n->k); n->k = NULL; n = rb_tree_succ(n); } rb_tree_destroy(t); }
nlopt_result crs_minimize(int n, nlopt_func f, void *f_data, const double *lb, const double *ub, /* bounds */ double *x, /* in: initial guess, out: minimizer */ double *minf, nlopt_stopping *stop, int population, /* initial population (0=default) */ int lds) /* random or low-discrepancy seq. (lds) */ { nlopt_result ret; crs_data d; rb_node *best; ret = crs_init(&d, n, x, lb, ub, stop, f, f_data, population, lds); if (ret < 0) return ret; best = rb_tree_min(&d.t); *minf = best->k[0]; memcpy(x, best->k + 1, sizeof(double) * n); while (ret == NLOPT_SUCCESS) { if (NLOPT_SUCCESS == (ret = crs_trial(&d))) { best = rb_tree_min(&d.t); if (best->k[0] < *minf) { if (best->k[0] < stop->minf_max) ret = NLOPT_MINF_MAX_REACHED; else if (nlopt_stop_f(stop, best->k[0], *minf)) ret = NLOPT_FTOL_REACHED; else if (nlopt_stop_x(stop, best->k + 1, x)) ret = NLOPT_XTOL_REACHED; *minf = best->k[0]; memcpy(x, best->k + 1, sizeof(double) * n); } if (ret != NLOPT_SUCCESS) { if (nlopt_stop_evals(stop)) ret = NLOPT_MAXEVAL_REACHED; else if (nlopt_stop_time(stop)) ret = NLOPT_MAXTIME_REACHED; } } } crs_destroy(&d); return ret; }
/* 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, 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, int allow_dups) { int nhull = 0; double minslope; double xmin, xmax, yminmin, ymaxmin; rb_node *n, *nmax; /* Monotone chain algorithm [Andrew, 1979]. */ n = rb_tree_min(t); if (!n) return 0; nmax = rb_tree_max(t); xmin = n->k[0]; yminmin = n->k[1]; xmax = nmax->k[0]; 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; if (xmin == xmax) return nhull; /* set nmax = min mode with x == xmax */ #if 0 while (nmax->k[0] == xmax) nmax = rb_tree_pred(nmax); /* non-NULL since xmin != xmax */ nmax = rb_tree_succ(nmax); #else /* performance hack (see also below) */ { double kshift[2]; kshift[0] = xmax * (1 - 1e-13); kshift[1] = -HUGE_VAL; nmax = rb_tree_find_gt(t, kshift); /* non-NULL since xmin != xmax */ } #endif ymaxmin = nmax->k[1]; minslope = (ymaxmin - yminmin) / (xmax - xmin); /* set n = first node with x != xmin */ #if 0 while (n->k[0] == xmin) n = rb_tree_succ(n); /* non-NULL since xmin != xmax */ #else /* performance hack (see also below) */ { double kshift[2]; kshift[0] = xmin * (1 + 1e-13); kshift[1] = -HUGE_VAL; n = rb_tree_find_gt(t, kshift); /* non-NULL since xmin != xmax */ } #endif for (; n != nmax; n = rb_tree_succ(n)) { double *k = n->k; if (k[1] > yminmin + (k[0] - xmin) * minslope) continue; /* performance hack: most of the points in DIRECT lie along vertical lines at a few x values, and we can exploit this */ if (nhull && k[0] == hull[nhull - 1][0]) { /* x == previous x */ if (k[1] > hull[nhull - 1][1]) { double kshift[2]; /* because of the round to float in rect_diameter, above, it shouldn't be possible for two diameters (x values) to have a fractional difference < 1e-13. Note that k[0] > 0 always in DIRECT */ kshift[0] = k[0] * (1 + 1e-13); kshift[1] = -HUGE_VAL; n = rb_tree_pred(rb_tree_find_gt(t, kshift)); continue; } else { /* equal y values, add to hull */ if (allow_dups) hull[nhull++] = k; continue; } } /* remove points until we are making a "left turn" to k */ while (nhull > 1) { double *t1 = hull[nhull - 1], *t2; /* because we allow equal points in our hull, we have to modify the standard convex-hull algorithm slightly: we need to look backwards in the hull list until we find a point t2 != t1 */ int it2 = nhull - 2; do { t2 = hull[it2--]; } while (it2 >= 0 && t2[0] == t1[0] && t2[1] == t1[1]); if (it2 < 0) break; /* cross product (t1-t2) x (k-t2) > 0 for a left turn: */ if ((t1[0]-t2[0]) * (k[1]-t2[1]) - (t1[1]-t2[1]) * (k[0]-t2[0]) >= 0) break; --nhull; } hull[nhull++] = k; } 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; return nhull; }
/* Internal version of nldrmd_minimize, intended to be used as a subroutine for the subplex method. Three differences compared to nldrmd_minimize: *minf should contain the value of f(x) (so that we don't have to re-evaluate f at the starting x). if psi > 0, then it *replaces* xtol and ftol in stop with the condition that the simplex diameter |xl - xh| must be reduced by a factor of psi ... this is for when nldrmd is used within the subplex method; for ordinary termination tests, set psi = 0. scratch should contain an array of length >= (n+1)*(n+1) + 2*n, used as scratch workspace. On output, *fdiff will contain the difference between the high and low function values of the last simplex. */ nlopt_result nldrmd_minimize_(int n, nlopt_func f, void *f_data, const double *lb, const double *ub, /* bounds */ double *x, /* in: initial guess, out: minimizer */ double *minf, const double *xstep, /* initial step sizes */ nlopt_stopping *stop, double psi, double *scratch, double *fdiff) { double *pts; /* (n+1) x (n+1) array of n+1 points plus function val [0] */ double *c; /* centroid * n */ double *xcur; /* current point */ rb_tree t; /* red-black tree of simplex, sorted by f(x) */ int i, j; double ninv = 1.0 / n; nlopt_result ret = NLOPT_SUCCESS; double init_diam = 0; pts = scratch; c = scratch + (n+1)*(n+1); xcur = c + n; rb_tree_init(&t, simplex_compare); *fdiff = HUGE_VAL; /* initialize the simplex based on the starting xstep */ memcpy(pts+1, x, sizeof(double)*n); pts[0] = *minf; if (*minf < stop->minf_max) { ret=NLOPT_MINF_MAX_REACHED; goto done; } for (i = 0; i < n; ++i) { double *pt = pts + (i+1)*(n+1); memcpy(pt+1, x, sizeof(double)*n); pt[1+i] += xstep[i]; if (pt[1+i] > ub[i]) { if (ub[i] - x[i] > fabs(xstep[i]) * 0.1) pt[1+i] = ub[i]; else /* ub is too close to pt, go in other direction */ pt[1+i] = x[i] - fabs(xstep[i]); } if (pt[1+i] < lb[i]) { if (x[i] - lb[i] > fabs(xstep[i]) * 0.1) pt[1+i] = lb[i]; else {/* lb is too close to pt, go in other direction */ pt[1+i] = x[i] + fabs(xstep[i]); if (pt[1+i] > ub[i]) /* go towards further of lb, ub */ pt[1+i] = 0.5 * ((ub[i] - x[i] > x[i] - lb[i] ? ub[i] : lb[i]) + x[i]); } } if (close(pt[1+i], x[i])) { ret=NLOPT_FAILURE; goto done; } pt[0] = f(n, pt+1, NULL, f_data); CHECK_EVAL(pt+1, pt[0]); } restart: for (i = 0; i < n + 1; ++i) if (!rb_tree_insert(&t, pts + i*(n+1))) { ret = NLOPT_OUT_OF_MEMORY; goto done; } while (1) { rb_node *low = rb_tree_min(&t); rb_node *high = rb_tree_max(&t); double fl = low->k[0], *xl = low->k + 1; double fh = high->k[0], *xh = high->k + 1; double fr; *fdiff = fh - fl; if (init_diam == 0) /* initialize diam. for psi convergence test */ for (i = 0; i < n; ++i) init_diam += fabs(xl[i] - xh[i]); if (psi <= 0 && nlopt_stop_ftol(stop, fl, fh)) { ret = NLOPT_FTOL_REACHED; goto done; } /* compute centroid ... if we cared about the perfomance of this, we could do it iteratively by updating the centroid on each step, but then we would have to be more careful about accumulation of rounding errors... anyway n is unlikely to be very large for Nelder-Mead in practical cases */ memset(c, 0, sizeof(double)*n); for (i = 0; i < n + 1; ++i) { double *xi = pts + i*(n+1) + 1; if (xi != xh) for (j = 0; j < n; ++j) c[j] += xi[j]; } for (i = 0; i < n; ++i) c[i] *= ninv; /* x convergence check: find xcur = max radius from centroid */ memset(xcur, 0, sizeof(double)*n); for (i = 0; i < n + 1; ++i) { double *xi = pts + i*(n+1) + 1; for (j = 0; j < n; ++j) { double dx = fabs(xi[j] - c[j]); if (dx > xcur[j]) xcur[j] = dx; } } for (i = 0; i < n; ++i) xcur[i] += c[i]; if (psi > 0) { double diam = 0; for (i = 0; i < n; ++i) diam += fabs(xl[i] - xh[i]); if (diam < psi * init_diam) { ret = NLOPT_XTOL_REACHED; goto done; } } else if (nlopt_stop_x(stop, c, xcur)) { ret = NLOPT_XTOL_REACHED; goto done; } /* reflection */ if (!reflectpt(n, xcur, c, alpha, xh, lb, ub)) { ret=NLOPT_XTOL_REACHED; goto done; } fr = f(n, xcur, NULL, f_data); CHECK_EVAL(xcur, fr); if (fr < fl) { /* new best point, expand simplex */ if (!reflectpt(n, xh, c, gamm, xh, lb, ub)) { ret=NLOPT_XTOL_REACHED; goto done; } fh = f(n, xh, NULL, f_data); CHECK_EVAL(xh, fh); if (fh >= fr) { /* expanding didn't improve */ fh = fr; memcpy(xh, xcur, sizeof(double)*n); } } else if (fr < rb_tree_pred(high)->k[0]) { /* accept new point */ memcpy(xh, xcur, sizeof(double)*n); fh = fr; } else { /* new worst point, contract */ double fc; if (!reflectpt(n,xcur,c, fh <= fr ? -beta : beta, xh, lb,ub)) { ret=NLOPT_XTOL_REACHED; goto done; } fc = f(n, xcur, NULL, f_data); CHECK_EVAL(xcur, fc); if (fc < fr && fc < fh) { /* successful contraction */ memcpy(xh, xcur, sizeof(double)*n); fh = fc; } else { /* failed contraction, shrink simplex */ rb_tree_destroy(&t); rb_tree_init(&t, simplex_compare); for (i = 0; i < n+1; ++i) { double *pt = pts + i * (n+1); if (pt+1 != xl) { if (!reflectpt(n,pt+1, xl,-delta,pt+1, lb,ub)) { ret = NLOPT_XTOL_REACHED; goto done; } pt[0] = f(n, pt+1, NULL, f_data); CHECK_EVAL(pt+1, pt[0]); } } goto restart; } } high->k[0] = fh; rb_tree_resort(&t, high); } done: rb_tree_destroy(&t); return ret; }
nlopt_result mlsl_minimize(int n, nlopt_func f, void *f_data, const double *lb, const double *ub, /* bounds */ double *x, /* in: initial guess, out: minimizer */ double *minf, nlopt_stopping *stop, nlopt_opt local_opt, int Nsamples, /* #samples/iteration (0=default) */ int lds) /* random or low-discrepancy seq. (lds) */ { nlopt_result ret = NLOPT_SUCCESS; mlsl_data d; int i; pt *p; if (!Nsamples) d.N = 4; /* FIXME: what is good number of samples per iteration? */ else d.N = Nsamples; if (d.N < 1) return NLOPT_INVALID_ARGS; d.n = n; d.lb = lb; d.ub = ub; d.stop = stop; d.f = f; d.f_data = f_data; rb_tree_init(&d.pts, pt_compare); rb_tree_init(&d.lms, lm_compare); d.s = lds ? nlopt_sobol_create((unsigned) n) : NULL; nlopt_set_min_objective(local_opt, fcount, &d); nlopt_set_lower_bounds(local_opt, lb); nlopt_set_upper_bounds(local_opt, ub); nlopt_set_stopval(local_opt, stop->minf_max); d.gamma = MLSL_GAMMA; d.R_prefactor = sqrt(2./K2PI) * pow(gam(n) * MLSL_SIGMA, 1.0/n); for (i = 0; i < n; ++i) d.R_prefactor *= pow(ub[i] - lb[i], 1.0/n); /* MLSL also suggests setting some minimum distance from points to previous local minimiza and to the boundaries; I don't know how to choose this as a fixed number, so I set it proportional to R; see also the comments at top. dlm and dbound are the proportionality constants. */ d.dlm = 1.0; /* min distance/R to local minima (FIXME: good value?) */ d.dbound = 1e-6; /* min distance/R to ub/lb boundaries (good value?) */ p = alloc_pt(n); if (!p) { ret = NLOPT_OUT_OF_MEMORY; goto done; } /* FIXME: how many sobol points to skip, if any? */ nlopt_sobol_skip(d.s, (unsigned) (10*n+d.N), p->x); memcpy(p->x, x, n * sizeof(double)); p->f = f(n, x, NULL, f_data); stop->nevals++; if (!rb_tree_insert(&d.pts, (rb_key) p)) { free(p); ret = NLOPT_OUT_OF_MEMORY; } if (nlopt_stop_forced(stop)) ret = NLOPT_FORCED_STOP; else if (nlopt_stop_evals(stop)) ret = NLOPT_MAXEVAL_REACHED; else if (nlopt_stop_time(stop)) ret = NLOPT_MAXTIME_REACHED; else if (p->f < stop->minf_max) ret = NLOPT_MINF_MAX_REACHED; while (ret == NLOPT_SUCCESS) { rb_node *node; double R; get_minf(&d, minf, x); /* sampling phase: add random/quasi-random points */ for (i = 0; i < d.N && ret == NLOPT_SUCCESS; ++i) { p = alloc_pt(n); if (!p) { ret = NLOPT_OUT_OF_MEMORY; goto done; } if (d.s) nlopt_sobol_next(d.s, p->x, lb, ub); else { /* use random points instead of LDS */ int j; for (j = 0; j < n; ++j) p->x[j] = nlopt_urand(lb[j],ub[j]); } p->f = f(n, p->x, NULL, f_data); stop->nevals++; if (!rb_tree_insert(&d.pts, (rb_key) p)) { free(p); ret = NLOPT_OUT_OF_MEMORY; } if (nlopt_stop_forced(stop)) ret = NLOPT_FORCED_STOP; else if (nlopt_stop_evals(stop)) ret = NLOPT_MAXEVAL_REACHED; else if (nlopt_stop_time(stop)) ret = NLOPT_MAXTIME_REACHED; else if (p->f < stop->minf_max) ret = NLOPT_MINF_MAX_REACHED; else { find_closest_pt(n, &d.pts, p); find_closest_lm(n, &d.lms, p); pts_update_newpt(n, &d.pts, p); } } /* distance threshhold parameter R in MLSL */ R = d.R_prefactor * pow(log((double) d.pts.N) / d.pts.N, 1.0 / n); /* local search phase: do local opt. for promising points */ node = rb_tree_min(&d.pts); for (i = (int) (ceil(d.gamma * d.pts.N) + 0.5); node && i > 0 && ret == NLOPT_SUCCESS; --i) { p = (pt *) node->k; if (is_potential_minimizer(&d, p, R, d.dlm*R, d.dbound*R)) { nlopt_result lret; double *lm; double t = nlopt_seconds(); if (nlopt_stop_forced(stop)) { ret = NLOPT_FORCED_STOP; break; } if (nlopt_stop_evals(stop)) { ret = NLOPT_MAXEVAL_REACHED; break; } if (stop->maxtime > 0 && t - stop->start >= stop->maxtime) { ret = NLOPT_MAXTIME_REACHED; break; } lm = (double *) malloc(sizeof(double) * (n+1)); if (!lm) { ret = NLOPT_OUT_OF_MEMORY; goto done; } memcpy(lm+1, p->x, sizeof(double) * n); lret = nlopt_optimize_limited(local_opt, lm+1, lm, stop->maxeval - stop->nevals, stop->maxtime - (t - stop->start)); p->minimized = 1; if (lret < 0) { free(lm); ret = lret; goto done; } if (!rb_tree_insert(&d.lms, lm)) { free(lm); ret = NLOPT_OUT_OF_MEMORY; } else if (nlopt_stop_forced(stop)) ret = NLOPT_FORCED_STOP; else if (*lm < stop->minf_max) ret = NLOPT_MINF_MAX_REACHED; else if (nlopt_stop_evals(stop)) ret = NLOPT_MAXEVAL_REACHED; else if (nlopt_stop_time(stop)) ret = NLOPT_MAXTIME_REACHED; else pts_update_newlm(n, &d.pts, lm); } /* TODO: additional stopping criteria based e.g. on improvement in function values, etc? */ node = rb_tree_succ(node); } } get_minf(&d, minf, x); done: nlopt_sobol_destroy(d.s); rb_tree_destroy_with_keys(&d.lms); rb_tree_destroy_with_keys(&d.pts); return ret; }
int main(int argc, char **argv) { int N, M; int *k; double kd; rb_tree t; rb_node *n; int i, j; if (argc < 2) { fprintf(stderr, "Usage: redblack_test Ntest [rand seed]\n"); return 1; } N = atoi(argv[1]); k = (int *) malloc(N * sizeof(int)); rb_tree_init(&t, comp); srand((unsigned) (argc > 2 ? atoi(argv[2]) : time(NULL))); for (i = 0; i < N; ++i) { double *newk = (double *) malloc(sizeof(double)); *newk = (k[i] = rand() % N); if (!rb_tree_insert(&t, newk)) { fprintf(stderr, "error in rb_tree_insert\n"); return 1; } if (!rb_tree_check(&t)) { fprintf(stderr, "rb_tree_check_failed after insert!\n"); return 1; } } if (t.N != N) { fprintf(stderr, "incorrect N (%d) in tree (vs. %d)\n", t.N, N); return 1; } for (i = 0; i < N; ++i) { kd = k[i]; if (!rb_tree_find(&t, &kd)) { fprintf(stderr, "rb_tree_find lost %d!\n", k[i]); return 1; } } n = rb_tree_min(&t); for (i = 0; i < N; ++i) { if (!n) { fprintf(stderr, "not enough successors %d\n!", i); return 1; } printf("%d: %g\n", i, n->k[0]); n = rb_tree_succ(n); } if (n) { fprintf(stderr, "too many successors!\n"); return 1; } n = rb_tree_max(&t); for (i = 0; i < N; ++i) { if (!n) { fprintf(stderr, "not enough predecessors %d\n!", i); return 1; } printf("%d: %g\n", i, n->k[0]); n = rb_tree_pred(n); } if (n) { fprintf(stderr, "too many predecessors!\n"); return 1; } for (M = N; M > 0; --M) { int knew = rand() % N; /* random new key */ j = rand() % M; /* random original key to replace */ for (i = 0; i < N; ++i) if (k[i] >= 0) if (j-- == 0) break; if (i >= N) abort(); kd = k[i]; if (!(n = rb_tree_find(&t, &kd))) { fprintf(stderr, "rb_tree_find lost %d!\n", k[i]); return 1; } n->k[0] = knew; if (!rb_tree_resort(&t, n)) { fprintf(stderr, "error in rb_tree_resort\n"); return 1; } if (!rb_tree_check(&t)) { fprintf(stderr, "rb_tree_check_failed after change %d!\n", N - M + 1); return 1; } k[i] = -1 - knew; } if (t.N != N) { fprintf(stderr, "incorrect N (%d) in tree (vs. %d)\n", t.N, N); return 1; } for (i = 0; i < N; ++i) k[i] = -1 - k[i]; /* undo negation above */ for (i = 0; i < N; ++i) { rb_node *le, *gt; double lek, gtk; kd = 0.01 * (rand() % (N * 150) - N * 25); le = rb_tree_find_le(&t, &kd); gt = rb_tree_find_gt(&t, &kd); n = rb_tree_min(&t); lek = le ? le->k[0] : -HUGE_VAL; gtk = gt ? gt->k[0] : +HUGE_VAL; printf("%g <= %g < %g\n", lek, kd, gtk); if (n->k[0] > kd) { if (le) { fprintf(stderr, "found invalid le %g for %g\n", lek, kd); return 1; } if (gt != n) { fprintf(stderr, "gt is not first node for k=%g\n", kd); return 1; } } else { rb_node *succ = n; do { n = succ; succ = rb_tree_succ(n); } while (succ && succ->k[0] <= kd); if (n != le) { fprintf(stderr, "rb_tree_find_le gave wrong result for k=%g\n", kd); return 1; } if (succ != gt) { fprintf(stderr, "rb_tree_find_gt gave wrong result for k=%g\n", kd); return 1; } } } for (M = N; M > 0; --M) { j = rand() % M; for (i = 0; i < N; ++i) if (k[i] >= 0) if (j-- == 0) break; if (i >= N) abort(); kd = k[i]; if (!(n = rb_tree_find(&t, &kd))) { fprintf(stderr, "rb_tree_find lost %d!\n", k[i]); return 1; } n = rb_tree_remove(&t, n); free(n->k); free(n); if (!rb_tree_check(&t)) { fprintf(stderr, "rb_tree_check_failed after remove!\n"); return 1; } k[i] = -1 - k[i]; } if (t.N != 0) { fprintf(stderr, "nonzero N (%d) in tree at end\n", t.N); return 1; } rb_tree_destroy(&t); free(k); printf("SUCCESS.\n"); return 0; }