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); }
/* given newpt, which presumably has just been added to our tree, update all pts with a greater function value in case newpt is closer to them than their previous closest_pt ... we can ignore already-minimized points since we never do local minimization from the same point twice */ static void pts_update_newpt(int n, rb_tree *pts, pt *newpt) { rb_node *node = rb_tree_find_gt(pts, (rb_key) newpt); while (node) { pt *p = (pt *) node->k; if (!p->minimized) { double d = distance2(n, newpt->x, p->x); if (d < p->closest_pt_d) p->closest_pt_d = d; } node = rb_tree_succ(node); } }
/* given newlm, which presumably has just been added to our tree, update all pts with a greater function value in case newlm is closer to them than their previous closest_lm ... we can ignore already-minimized points since we never do local minimization from the same point twice */ static void pts_update_newlm(int n, rb_tree *pts, double *newlm) { pt tmp_pt; rb_node *node; tmp_pt.f = newlm[0]; node = rb_tree_find_gt(pts, (rb_key) &tmp_pt); while (node) { pt *p = (pt *) node->k; if (!p->minimized) { double d = distance2(n, newlm+1, p->x); if (d < p->closest_lm_d) p->closest_lm_d = d; } node = rb_tree_succ(node); } }
/* 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; }
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; }