chiark / gitweb /
added first version of MLSL multistart-type algorithm
authorstevenj <stevenj@alum.mit.edu>
Thu, 13 Sep 2007 20:06:09 +0000 (16:06 -0400)
committerstevenj <stevenj@alum.mit.edu>
Thu, 13 Sep 2007 20:06:09 +0000 (16:06 -0400)
darcs-hash:20070913200609-c8de0-b0e9b03737e97fb885b5a7a4305e79f6cd21a18f.gz

16 files changed:
Makefile.am
api/Makefile.am
api/nlopt.c
api/nlopt.h
api/nlopt_minimize.3
configure.ac
mlsl/Makefile.am [new file with mode: 0644]
mlsl/README [new file with mode: 0644]
mlsl/mlsl.c [new file with mode: 0644]
mlsl/mlsl.h [new file with mode: 0644]
octave/Makefile.am
octave/NLOPT_GD_MLSL.m [new file with mode: 0644]
octave/NLOPT_GD_MLSL_LDS.m [new file with mode: 0644]
octave/NLOPT_GN_MLSL.m [new file with mode: 0644]
octave/NLOPT_GN_MLSL_LDS.m [new file with mode: 0644]
octave/nlopt_minimize.m

index d0949db3181c768c14f408a58748c8dc44d98085..e33c24b34cdd101316c3f0c5ffdbfe2ffb8b7b7d 100644 (file)
@@ -3,13 +3,14 @@ lib_LTLIBRARIES = libnlopt.la
 
 ACLOCAL_AMFLAGS=-I ./m4
 
-SUBDIRS= util subplex direct cdirect stogo praxis luksan crs api octave . test
+SUBDIRS= util subplex direct cdirect stogo praxis luksan crs mlsl api octave . test
 EXTRA_DIST=COPYRIGHT autogen.sh nlopt.pc.in m4
 
 libnlopt_la_SOURCES = 
 libnlopt_la_LIBADD = subplex/libsubplex.la direct/libdirect.la \
 cdirect/libcdirect.la stogo/libstogo.la praxis/libpraxis.la    \
-luksan/libluksan.la crs/libcrs.la api/libapi.la util/libutil.la
+luksan/libluksan.la crs/libcrs.la mlsl/libmlsl.la api/libapi.la        \
+util/libutil.la
 
 libnlopt_la_LDFLAGS = -no-undefined -version-info @SHARED_VERSION_INFO@
 
index 63634e0b0b84d1e25f7dbb10ccafc6da79cf1772..4bec3cdb863917ecd30f1f1105e2e19bce3c6183 100644 (file)
@@ -1,4 +1,4 @@
-AM_CPPFLAGS = -I$(top_srcdir)/cdirect -I$(top_srcdir)/direct -I$(top_srcdir)/stogo -I$(top_srcdir)/subplex -I$(top_srcdir)/praxis -I$(top_srcdir)/luksan -I$(top_srcdir)/crs -I$(top_srcdir)/util
+AM_CPPFLAGS = -I$(top_srcdir)/cdirect -I$(top_srcdir)/direct -I$(top_srcdir)/stogo -I$(top_srcdir)/subplex -I$(top_srcdir)/praxis -I$(top_srcdir)/luksan -I$(top_srcdir)/crs -I$(top_srcdir)/mlsl -I$(top_srcdir)/util
 
 include_HEADERS = nlopt.h
 noinst_LTLIBRARIES = libapi.la
index 732a5d8103b2964e66a8b7d686b2a73ca516ec07..f60575342b1126c3dece53e22e27fdc3ba514dca 100644 (file)
@@ -62,8 +62,10 @@ static const char nlopt_algorithm_names[NLOPT_NUM_ALGORITHMS][256] = {
      "Preconditioned truncated Newton (local, derivative-based)",
      "Preconditioned truncated Newton with restarting (local, derivative-based)",
      "Controlled random search (CRS2) with local mutation (global, no-derivative)",
-     "Controlled quasi-random search (CRS2) with local mutation (global, no-derivative), using Sobol' LDS",
-     
+     "Multi-level single-linkage (MLSL), random (global, no-derivative)",
+     "Multi-level single-linkage (MLSL), random (global, derivative)",
+     "Multi-level single-linkage (MLSL), quasi-random (global, no-derivative)",
+     "Multi-level single-linkage (MLSL), quasi-random (global, derivative)"
 };
 
 const char *nlopt_algorithm_name(nlopt_algorithm a)
@@ -331,6 +333,18 @@ static nlopt_result nlopt_minimize_(
         case NLOPT_GN_CRS2_LM:
              return crs_minimize(n, f, f_data, lb, ub, x, minf, &stop, 0);
 
+        case NLOPT_GN_MLSL:
+        case NLOPT_GD_MLSL:
+        case NLOPT_GN_MLSL_LDS:
+        case NLOPT_GD_MLSL_LDS:
+             return mlsl_minimize(n, f, f_data, lb, ub, x, minf, &stop,
+                                  (algorithm == NLOPT_GN_MLSL ||
+                                   algorithm == NLOPT_GN_MLSL_LDS)
+                                  ? local_search_alg_nonderiv
+                                  : local_search_alg_deriv,
+                                  local_search_maxeval,
+                                  algorithm >= NLOPT_GN_MLSL_LDS);
+
         default:
              return NLOPT_INVALID_ARGS;
      }
index 750d308effdcfca2e550c289d6c030a714272698..a6f129b0702b417fd05fd346e490bc6b2544e250 100644 (file)
@@ -52,6 +52,11 @@ typedef enum {
 
      NLOPT_GN_CRS2_LM,
 
+     NLOPT_GN_MLSL,
+     NLOPT_GD_MLSL,
+     NLOPT_GN_MLSL_LDS,
+     NLOPT_GD_MLSL_LDS,
+
      NLOPT_NUM_ALGORITHMS /* not an algorithm, just the number of them */
 } nlopt_algorithm;
 
index b208d02d0c7f9b2ab2138da378c983c0a909b332..22f795f374e2dac6881a5c20bd6e9c80274421c6 100644 (file)
@@ -283,6 +283,22 @@ other variants of this algorithm:
 Global (G) derivative-free (N) optimization using controlled random
 search (CRS2) algorithm of Price, with the "local mutation" (LM)
 modification suggested by Kaelo and Ali.
+.TP
+\fBNLOPT_GD_MLSL_LDS\fR, \fBNLOPT_GN_MLSL_LDS\fR
+Global (G) derivative-based (D) or derivative-free (N) optimization
+using the multi-level single-linkage (MLSL) algorithm with a
+low-discrepancy sequence (LDS).  This algorithm executes a quasi-random
+(LDS) sequence of local searches, with a clustering heuristic to
+avoid multiple local searches for the same local minimum.  The local
+search uses the derivative/nonderivative algorithm set by
+.I nlopt_set_local_search_algorithm
+(currently defaulting to
+.I NLOPT_LD_LBFGS
+and
+.I NLOPT_LN_SUBPLEX
+for derivative/nonderivative searches, respectively).  There are also
+two other variants, \fBNLOPT_GD_MLSL\fR and \fBNLOPT_GN_MLSL\fR, which use
+pseudo-random numbers (instead of an LDS) as in the original MLSL algorithm.
 .SH STOPPING CRITERIA
 Multiple stopping criteria for the optimization are supported, as
 specified by the following arguments to
@@ -410,6 +426,10 @@ you want to use deterministic random numbers, you can set the seed by
 calling:
 .sp
 .BI "            void nlopt_srand(unsigned long " "seed" );
+.sp
+Some of the algorithms also support using low-discrepancy sequences (LDS),
+sometimes known as quasi-random numbers.  NLopt uses the Sobol LDS, which
+is implemented for up to 1111 dimensions.
 .SH BUGS
 Currently the NLopt library is in pre-alpha stage.  Most algorithms
 currently do not support all termination conditions: the only
index e4ac3d7454347afcf176482cf69d6d62c2a752f5..ef97129e134b2c6a859fff4630ac9436b9874274 100644 (file)
@@ -182,6 +182,7 @@ AC_CONFIG_FILES([
    praxis/Makefile
    luksan/Makefile
    crs/Makefile
+   mlsl/Makefile
    test/Makefile
 ])
 
diff --git a/mlsl/Makefile.am b/mlsl/Makefile.am
new file mode 100644 (file)
index 0000000..b019c2c
--- /dev/null
@@ -0,0 +1,6 @@
+AM_CPPFLAGS = -I$(top_srcdir)/util -I$(top_srcdir)/api
+
+noinst_LTLIBRARIES = libmlsl.la
+libmlsl_la_SOURCES = mlsl.c mlsl.h
+
+EXTRA_DIST = README
diff --git a/mlsl/README b/mlsl/README
new file mode 100644 (file)
index 0000000..feeb9cd
--- /dev/null
@@ -0,0 +1,22 @@
+This is my implementation of the "Multi-Level Single-Linkage" (MLSL)
+algorithm for global optimization by random local optimizations (a
+multistart algorithm with "clustering" to avoid repeated detection of
+the same local minimum), modified to optionally use a Sobol'
+low-discrepancy sequence (LDS) instead of pseudorandom numbers.  See:
+
+   A. H. G. Rinnooy Kan and G. T. Timmer, "Stochastic global optimization
+   methods," Mathematical Programming, vol. 39, p. 27-78 (1978).
+       [ actually 2 papers -- part I: clustering methods (p. 27), then 
+                              part II: multilevel methods (p. 57) ]
+
+and also:
+
+   Sergei Kucherenko and Yury Sytsko, "Application of deterministic
+   low-discrepancy sequences in global optimization," Computational
+   Optimization and Applications, vol. 30, p. 297-318 (2005).
+
+It is under the same MIT license as the rest of my code in NLopt (see
+../COPYRIGHT).
+
+Steven G. Johnson
+September 2007
diff --git a/mlsl/mlsl.c b/mlsl/mlsl.c
new file mode 100644 (file)
index 0000000..f1b90a4
--- /dev/null
@@ -0,0 +1,406 @@
+/* Multi-Level Single-Linkage (MLSL) algorithm for
+   global optimization by random local optimizations (a multistart algorithm
+   with "clustering" to avoid repeated detection of the same local minimum), 
+   modified to optionally use a Sobol' low-discrepancy sequence (LDS) instead 
+   of pseudorandom numbers.  See:
+
+   A. H. G. Rinnooy Kan and G. T. Timmer, "Stochastic global optimization
+   methods," Mathematical Programming, vol. 39, p. 27-78 (1978).
+       [ actually 2 papers -- part I: clustering methods (p. 27), then 
+                              part II: multilevel methods (p. 57) ]
+
+   and also:
+
+   Sergei Kucherenko and Yury Sytsko, "Application of deterministic
+   low-discrepancy sequences in global optimization," Computational
+   Optimization and Applications, vol. 30, p. 297-318 (2005).
+
+   Compared to the above papers, I made a couple other modifications
+   to the algorithm besides the use of a LDS.
+
+   1) first, the original algorithm suggests discarding points
+      within a *fixed* distance of the boundaries or of previous
+      local minima; I changed this to a distance that decreases with,
+      iteration, proportional to the same threshold radius used
+      for clustering.  (In the case of the boundaries, I make
+      the proportionality constant rather small as well.)
+
+   2) Kan and Timmer suggest using fancy "spiral-search" algorithms
+      to quickly locate nearest-neighbor points, reducing the
+      overall time for N sample points from O(N^2) to O(N log N)
+      However, recent papers seem to show that such schemes (kd trees,
+      etcetera) become inefficient for high-dimensional spaces (d > 20),
+      and finding a better approach seems to be an open question.  Therefore,
+      since I am mainly interested in MLSL for high-dimensional problems
+      (in low dimensions we have DIRECT etc.), I punted on this question
+      for now.  In practice, O(N^2) (which does not count the #points
+      evaluated in local searches) does not seem too bad if the objective
+      function is expensive.
+
+*/
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "redblack.h"
+#include "mlsl.h"
+
+/* data structure for each random/quasirandom point in the population */
+typedef struct {
+     double f; /* function value at x */
+     int minimized; /* if we have already minimized starting from x */
+     double closest_pt_d; /* distance^2 to closest pt with smaller f */
+     double closest_lm_d; /* distance^2 to closest lm with smaller f*/
+     double x[1]; /* array of length n (K&R struct hack) */
+} pt;
+
+/* all of the data used by the various mlsl routines...it's
+   not clear in hindsight that we need to put all of this in a data
+   structure since most of the work occurs in a single routine,
+   but it doesn't hurt us */
+typedef struct {
+     int n; /* # dimensions */
+     const double *lb, *ub;
+     nlopt_stopping *stop; /* stopping criteria */
+     nlopt_func f; void *f_data;
+
+     rb_tree pts; /* tree of points (k == pt), sorted by f */
+     rb_tree lms; /* tree of local minimizers, sorted by function value
+                    (k = array of length d+1, [0] = f, [1..d] = x) */
+
+     nlopt_sobol s; /* sobol data for LDS point generation, or NULL
+                      to use pseudo-random numbers */
+
+     nlopt_algorithm local_alg; /* local search algorithm */
+     int local_maxeval; /* max # local iterations (0 if unlimited) */
+
+     double R_prefactor, dlm, dbound, gamma; /* parameters of MLSL */
+     int N; /* number of pts to add per iteration */
+} mlsl_data;
+
+/* comparison routines to sort the red-black trees by function value */
+static int pt_compare(rb_key p1_, rb_key p2_)
+{
+     pt *p1 = (pt *) p1_;
+     pt *p2 = (pt *) p2_;
+     if (p1->f < p2->f) return -1;
+     if (p1->f > p2->f) return +1;
+     return 0;
+}
+static int lm_compare(double *k1, double *k2)
+{
+     if (*k1 < *k2) return -1;
+     if (*k1 > *k2) return +1;
+     return 0;
+}
+
+/* Euclidean distance |x1 - x2|^2 */
+static double distance2(int n, const double *x1, const double *x2)
+{
+     int i;
+     double d = 0.;
+     for (i = 0; i < n; ++i) {
+         double dx = x1[i] - x2[i];
+         d += dx * dx;
+     }
+     return d;
+}
+
+/* find the closest pt to p with a smaller function value;
+   this function is called when p is first added to our tree */
+static void find_closest_pt(int n, rb_tree *pts, pt *p)
+{
+     rb_node *node = rb_tree_find_lt(pts, (rb_key) p);
+     double closest_d = HUGE_VAL;
+     while (node) {
+         double d = distance2(n, p->x, ((pt *) node->k)->x);
+         if (d < closest_d) closest_d = d;
+         node = rb_tree_pred(node);
+     }
+     p->closest_pt_d = closest_d;
+}
+
+/* find the closest local minimizer (lm) to p with a smaller function value;
+   this function is called when p is first added to our tree */
+static void find_closest_lm(int n, rb_tree *lms, pt *p)
+{
+     rb_node *node = rb_tree_find_lt(lms, &p->f);
+     double closest_d = HUGE_VAL;
+     while (node) {
+         double d = distance2(n, p->x, node->k+1);
+         if (d < closest_d) closest_d = d;
+         node = rb_tree_pred(node);
+     }
+     p->closest_lm_d = closest_d;
+}
+
+/* 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);
+     }
+}
+
+static int is_potential_minimizer(mlsl_data *mlsl, pt *p,
+                                 double dpt_min,
+                                 double dlm_min,
+                                 double dbound_min)
+{
+     int i, n = mlsl->n;
+     const double *lb = mlsl->lb;
+     const double *ub = mlsl->ub;
+     const double *x = p->x;
+
+     if (p->minimized)
+         return 0;
+
+     if (p->closest_pt_d <= dpt_min*dpt_min)
+         return 0;
+
+     if (p->closest_lm_d <= dlm_min*dlm_min)
+         return 0;
+
+     for (i = 0; i < n; ++i)
+         if (x[i] - lb[i] <= dbound_min || ub[i] - x[i] <= dbound_min)
+              return 0;
+
+     return 1;
+}
+
+#define K2PI (6.2831853071795864769252867665590057683943388)
+
+/* compute Gamma(1 + n/2)^{1/n} ... this is a constant factor
+   used in MLSL as part of the minimum-distance cutoff*/
+static double gam(int n)
+{
+     /* use Stirling's approximation:
+       Gamma(1 + z) ~ sqrt(2*pi*z) * z^z / exp(z)
+        ... this is more than accurate enough for our purposes
+            (error in gam < 15% for d=1, < 4% for d=2, < 2% for d=3, ...)
+            and avoids overflow problems because we can combine it with
+           the ^{1/n} exponent */
+     double z = n/2;
+     return sqrt(pow(K2PI * z, 1.0/n) * z) * exp(-0.5);
+}
+
+static pt *alloc_pt(int n)
+{
+     pt *p = (pt *) malloc(sizeof(pt) + (n-1) * sizeof(double));
+     if (p) {
+         p->minimized = 0;
+         p->closest_pt_d = HUGE_VAL;
+         p->closest_lm_d = HUGE_VAL;
+     }
+     return p;
+}
+
+/* wrapper around objective function that increments evaluation count */
+static double fcount(int n, const double *x, double *grad, void *p_)
+{
+     mlsl_data *p = (mlsl_data *) p_;
+     p->stop->nevals++;
+     return p->f(n, x, grad, p->f_data);
+}
+
+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);
+     }
+}
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+#define MLSL_SIGMA 2. /* MLSL sigma parameter, using value from the papers */
+#define MLSL_GAMMA 0.3 /* MLSL gamma parameter (FIXME: what should it be?) */
+
+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_algorithm local_alg,
+                          int local_maxeval,
+                          int lds) /* random or low-discrepancy seq. (lds) */
+{
+     nlopt_result ret = NLOPT_SUCCESS;
+     mlsl_data d;
+     int i;
+     pt *p;
+
+     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;
+     d.local_alg = local_alg; d.local_maxeval = local_maxeval;
+
+     d.gamma = MLSL_GAMMA;
+     d.N = 4; /* FIXME: what is good number of samples per iteration? */
+
+     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+1), 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_evals(stop)) ret = NLOPT_MAXEVAL_REACHED;
+     else if (nlopt_stop_time(stop)) ret = NLOPT_MAXEVAL_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_evals(stop)) ret = NLOPT_MAXEVAL_REACHED;
+              else if (nlopt_stop_time(stop)) ret = NLOPT_MAXEVAL_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_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_minimize(local_alg, n, fcount, &d,
+                                         lb, ub, lm+1, lm,
+                                         stop->minf_max, 
+                                         stop->ftol_rel, stop->ftol_abs,
+                                         stop->xtol_rel, stop->xtol_abs,
+                                         local_maxeval > 0 ?
+                                         MIN(local_maxeval,
+                                             stop->maxeval - stop->nevals)
+                                         : 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 (*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;
+}
diff --git a/mlsl/mlsl.h b/mlsl/mlsl.h
new file mode 100644 (file)
index 0000000..e748b9c
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef MLSL_H
+#define MLSL_H
+
+#include "nlopt.h"
+#include "nlopt-util.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+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_algorithm local_alg,
+                           int local_maxeval,
+                           int lds);
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif /* __cplusplus */
+
+#endif
+
index 2fd66bf0280bd415ff344f17383dde32c54bf3bd..18265d184c4b5411aebf59141c0baa1672947e7e 100644 (file)
@@ -1,6 +1,6 @@
 AM_CPPFLAGS = -I$(top_srcdir)/api 
 
-MFILES = NLOPT_GN_DIRECT.m NLOPT_GN_DIRECT_L.m NLOPT_GN_DIRECT_L_RAND.m NLOPT_GN_DIRECT_NOSCAL.m NLOPT_GN_DIRECT_L_NOSCAL.m NLOPT_GN_DIRECT_L_RAND_NOSCAL.m NLOPT_GN_ORIG_DIRECT.m NLOPT_GN_ORIG_DIRECT_L.m NLOPT_LN_SUBPLEX.m NLOPT_GD_STOGO.m NLOPT_GD_STOGO_RAND.m NLOPT_LD_LBFGS.m NLOPT_LN_PRAXIS.m NLOPT_LD_VAR1.m NLOPT_LD_VAR2.m NLOPT_LD_TNEWTON.m NLOPT_LD_TNEWTON_RESTART.m NLOPT_LD_TNEWTON_PRECOND.m NLOPT_LD_TNEWTON_PRECOND_RESTART.m NLOPT_GN_CRS2_LM.m 
+MFILES = NLOPT_GN_DIRECT.m NLOPT_GN_DIRECT_L.m NLOPT_GN_DIRECT_L_RAND.m NLOPT_GN_DIRECT_NOSCAL.m NLOPT_GN_DIRECT_L_NOSCAL.m NLOPT_GN_DIRECT_L_RAND_NOSCAL.m NLOPT_GN_ORIG_DIRECT.m NLOPT_GN_ORIG_DIRECT_L.m NLOPT_LN_SUBPLEX.m NLOPT_GD_STOGO.m NLOPT_GD_STOGO_RAND.m NLOPT_LD_LBFGS.m NLOPT_LN_PRAXIS.m NLOPT_LD_VAR1.m NLOPT_LD_VAR2.m NLOPT_LD_TNEWTON.m NLOPT_LD_TNEWTON_RESTART.m NLOPT_LD_TNEWTON_PRECOND.m NLOPT_LD_TNEWTON_PRECOND_RESTART.m NLOPT_GN_CRS2_LM.m NLOPT_GN_MLSL.m NLOPT_GD_MLSL.m NLOPT_GN_MLSL_LDS.m NLOPT_GD_MLSL_LDS.m 
 
 #######################################################################
 octdir = $(OCT_INSTALL_DIR)
diff --git a/octave/NLOPT_GD_MLSL.m b/octave/NLOPT_GD_MLSL.m
new file mode 100644 (file)
index 0000000..9cc25ea
--- /dev/null
@@ -0,0 +1,5 @@
+% NLOPT_GD_MLSL: Multi-level single-linkage (MLSL), random (global, derivative)
+%
+% See nlopt_minimize for more information.
+function val = NLOPT_GD_MLSL
+  val = 21;
diff --git a/octave/NLOPT_GD_MLSL_LDS.m b/octave/NLOPT_GD_MLSL_LDS.m
new file mode 100644 (file)
index 0000000..73c1ef1
--- /dev/null
@@ -0,0 +1,5 @@
+% NLOPT_GD_MLSL_LDS: Multi-level single-linkage (MLSL), quasi-random (global, derivative)
+%
+% See nlopt_minimize for more information.
+function val = NLOPT_GD_MLSL_LDS
+  val = 23;
diff --git a/octave/NLOPT_GN_MLSL.m b/octave/NLOPT_GN_MLSL.m
new file mode 100644 (file)
index 0000000..4642de0
--- /dev/null
@@ -0,0 +1,5 @@
+% NLOPT_GN_MLSL: Multi-level single-linkage (MLSL), random (global, no-derivative)
+%
+% See nlopt_minimize for more information.
+function val = NLOPT_GN_MLSL
+  val = 20;
diff --git a/octave/NLOPT_GN_MLSL_LDS.m b/octave/NLOPT_GN_MLSL_LDS.m
new file mode 100644 (file)
index 0000000..bdde5ba
--- /dev/null
@@ -0,0 +1,5 @@
+% NLOPT_GN_MLSL_LDS: Multi-level single-linkage (MLSL), quasi-random (global, no-derivative)
+%
+% See nlopt_minimize for more information.
+function val = NLOPT_GN_MLSL_LDS
+  val = 22;
index 43f69750d6dc6296287a0462077e65c7363b25a7..931381d3637672ebed533fd1c6dd8f67e9d2701b 100644 (file)
 % optimization.  Names with _*N_ are derivative-free, while names
 % with _*D_ are gradient-based algorithms.  Algorithms:
 %
-% NLOPT_GN_DIRECT, NLOPT_GN_DIRECT_L, NLOPT_GN_DIRECT_L_RAND, NLOPT_GN_DIRECT_NOSCAL, NLOPT_GN_DIRECT_L_NOSCAL, NLOPT_GN_DIRECT_L_RAND_NOSCAL, NLOPT_GN_ORIG_DIRECT, NLOPT_GN_ORIG_DIRECT_L, NLOPT_LN_SUBPLEX, NLOPT_GD_STOGO, NLOPT_GD_STOGO_RAND, NLOPT_LD_LBFGS, NLOPT_LN_PRAXIS, NLOPT_LD_VAR1, NLOPT_LD_VAR2, NLOPT_LD_TNEWTON, NLOPT_LD_TNEWTON_RESTART, NLOPT_LD_TNEWTON_PRECOND, NLOPT_LD_TNEWTON_PRECOND_RESTART, NLOPT_GN_CRS2_LM 
+%  NLOPT_GN_DIRECT, NLOPT_GN_DIRECT_L, NLOPT_GN_DIRECT_L_RAND, 
+%  NLOPT_GN_DIRECT_NOSCAL, NLOPT_GN_DIRECT_L_NOSCAL, 
+%  NLOPT_GN_DIRECT_L_RAND_NOSCAL, NLOPT_GN_ORIG_DIRECT, 
+%  NLOPT_GN_ORIG_DIRECT_L, NLOPT_LN_SUBPLEX, NLOPT_GD_STOGO, 
+%  NLOPT_GD_STOGO_RAND, NLOPT_LD_LBFGS, NLOPT_LN_PRAXIS, 
+%  NLOPT_LD_VAR1, NLOPT_LD_VAR2, NLOPT_LD_TNEWTON, 
+%  NLOPT_LD_TNEWTON_RESTART, NLOPT_LD_TNEWTON_PRECOND, 
+%  NLOPT_LD_TNEWTON_PRECOND_RESTART, NLOPT_GN_CRS2_LM, NLOPT_GN_MLSL, 
+%  NLOPT_GD_MLSL, NLOPT_GN_MLSL_LDS, NLOPT_GD_MLSL_LDS
 %
 % For more information on individual functions, see their individual
 % help pages (e.g. "help NLOPT_LN_SUBPLEX").