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
 
 
 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    \
 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@
 
 
 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
 
 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)",
      "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)
 };
 
 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_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;
      }
         default:
              return NLOPT_INVALID_ARGS;
      }
index 750d308effdcfca2e550c289d6c030a714272698..a6f129b0702b417fd05fd346e490bc6b2544e250 100644 (file)
@@ -52,6 +52,11 @@ typedef enum {
 
      NLOPT_GN_CRS2_LM,
 
 
      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;
 
      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.
 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
 .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" );
 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
 .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
    praxis/Makefile
    luksan/Makefile
    crs/Makefile
+   mlsl/Makefile
    test/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 
 
 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)
 
 #######################################################################
 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:
 %
 % 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").
 %
 % For more information on individual functions, see their individual
 % help pages (e.g. "help NLOPT_LN_SUBPLEX").