chiark / gitweb /
Use trusty
[nlopt.git] / api / options.c
index e766ca4bd1c9bde0cc86b9d3ac33d4d6678ec999..66c0881faa98923ca77b94fb41380de176eda1fe 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007-2010 Massachusetts Institute of Technology
+/* 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
 #include <math.h>
 #include <string.h>
 #include <float.h>
+#include <stdarg.h>
 
 #include "nlopt-internal.h"
-#include "nlopt-util.h"
+
+#define ERR(err, opt, msg) (nlopt_set_errmsg(opt, msg) ? err : err)
 
 /*************************************************************************/
 
-void nlopt_destroy(nlopt_opt opt)
+void NLOPT_STDCALL nlopt_destroy(nlopt_opt opt)
 {
      if (opt) {
-         if (opt->free_f_data) {
-              unsigned i;
-              free(opt->f_data);
+         unsigned i;
+         if (opt->munge_on_destroy) {
+              nlopt_munge munge = opt->munge_on_destroy;
+              munge(opt->f_data);
               for (i = 0; i < opt->m; ++i)
-                   free(opt->fc[i].f_data);
+                   munge(opt->fc[i].f_data);
               for (i = 0; i < opt->p; ++i)
-                   free(opt->h[i].f_data);
+                   munge(opt->h[i].f_data);
          }
+         for (i = 0; i < opt->m; ++i)
+              free(opt->fc[i].tol);
+         for (i = 0; i < opt->p; ++i)
+              free(opt->h[i].tol);
          free(opt->lb); free(opt->ub);
          free(opt->xtol_abs);
          free(opt->fc);
          free(opt->h);
          nlopt_destroy(opt->local_opt);
          free(opt->dx);
+         free(opt->work);
+         free(opt->errmsg);
          free(opt);
      }
 }
 
-nlopt_opt nlopt_create(nlopt_algorithm algorithm, unsigned n)
+nlopt_opt NLOPT_STDCALL nlopt_create(nlopt_algorithm algorithm, unsigned n)
 {
      nlopt_opt opt;
 
@@ -63,9 +72,9 @@ nlopt_opt nlopt_create(nlopt_algorithm algorithm, unsigned n)
      if (opt) {
          opt->algorithm = algorithm;
          opt->n = n;
-         opt->f = NULL; opt->f_data = NULL;
+         opt->f = NULL; opt->f_data = NULL; opt->pre = NULL;
          opt->maximize = 0;
-         opt->free_f_data = 0;
+         opt->munge_on_destroy = opt->munge_on_copy = NULL;
 
          opt->lb = opt->ub = NULL;
          opt->m = opt->m_alloc = 0;
@@ -83,14 +92,17 @@ nlopt_opt nlopt_create(nlopt_algorithm algorithm, unsigned n)
 
          opt->local_opt = NULL;
          opt->stochastic_population = 0;
+         opt->vector_storage = 0;
          opt->dx = NULL;
+         opt->work = NULL;
+         opt->errmsg = NULL;
 
          if (n > 0) {
-              opt->lb = (double *) malloc(sizeof(double) * (n));
+              opt->lb = (double *) calloc(n, sizeof(double));
               if (!opt->lb) goto oom;
-              opt->ub = (double *) malloc(sizeof(double) * (n));
+              opt->ub = (double *) calloc(n, sizeof(double));
               if (!opt->ub) goto oom;
-              opt->xtol_abs = (double *) malloc(sizeof(double) * (n));
+              opt->xtol_abs = (double *) calloc(n, sizeof(double));
               if (!opt->xtol_abs) goto oom;
               nlopt_set_lower_bounds1(opt, -HUGE_VAL);
               nlopt_set_upper_bounds1(opt, +HUGE_VAL);
@@ -105,10 +117,12 @@ oom:
      return NULL;
 }
 
-nlopt_opt nlopt_copy(const nlopt_opt opt)
+nlopt_opt NLOPT_STDCALL nlopt_copy(const nlopt_opt opt)
 {
      nlopt_opt nopt = NULL;
+     unsigned i;
      if (opt) {
+         nlopt_munge munge;
          nopt = (nlopt_opt) malloc(sizeof(struct nlopt_opt_s));
          *nopt = *opt;
          nopt->lb = nopt->ub = nopt->xtol_abs = NULL;
@@ -116,8 +130,13 @@ nlopt_opt nlopt_copy(const nlopt_opt opt)
          nopt->m_alloc = nopt->p_alloc = 0;
          nopt->local_opt = NULL;
          nopt->dx = NULL;
-         opt->force_stop_child = NULL;
-         opt->free_f_data = 0;
+         nopt->work = NULL;
+         nopt->errmsg = NULL;
+         nopt->force_stop_child = NULL;
+
+         munge = nopt->munge_on_copy;
+         if (munge && nopt->f_data)
+              if (!(nopt->f_data = munge(nopt->f_data))) goto oom;
 
          if (opt->n > 0) {
               nopt->lb = (double *) malloc(sizeof(double) * (opt->n));
@@ -138,6 +157,21 @@ nlopt_opt nlopt_copy(const nlopt_opt opt)
                                                      * (opt->m));
               if (!nopt->fc) goto oom;
               memcpy(nopt->fc, opt->fc, sizeof(nlopt_constraint) * (opt->m));
+              for (i = 0; i < opt->m; ++i) nopt->fc[i].tol = NULL;
+              if (munge)
+                   for (i = 0; i < opt->m; ++i)
+                        if (nopt->fc[i].f_data &&
+                            !(nopt->fc[i].f_data
+                              = munge(nopt->fc[i].f_data)))
+                             goto oom;
+              for (i = 0; i < opt->m; ++i)
+                   if (opt->fc[i].tol) {
+                        nopt->fc[i].tol = (double *) malloc(sizeof(double)
+                                                            * nopt->fc[i].m);
+                        if (!nopt->fc[i].tol) goto oom;
+                        memcpy(nopt->fc[i].tol, opt->fc[i].tol,
+                               sizeof(double) * nopt->fc[i].m);
+                   }
          }
 
          if (opt->p) {
@@ -146,6 +180,21 @@ nlopt_opt nlopt_copy(const nlopt_opt opt)
                                                     * (opt->p));
               if (!nopt->h) goto oom;
               memcpy(nopt->h, opt->h, sizeof(nlopt_constraint) * (opt->p));
+              for (i = 0; i < opt->p; ++i) nopt->h[i].tol = NULL;
+              if (munge)
+                   for (i = 0; i < opt->p; ++i)
+                        if (nopt->h[i].f_data &&
+                            !(nopt->h[i].f_data
+                              = munge(nopt->h[i].f_data)))
+                             goto oom;
+              for (i = 0; i < opt->p; ++i)
+                   if (opt->h[i].tol) {
+                        nopt->h[i].tol = (double *) malloc(sizeof(double)
+                                                            * nopt->h[i].m);
+                        if (!nopt->h[i].tol) goto oom;
+                        memcpy(nopt->h[i].tol, opt->h[i].tol,
+                               sizeof(double) * nopt->h[i].m);
+                   }
          }
 
          if (opt->local_opt) {
@@ -162,16 +211,22 @@ nlopt_opt nlopt_copy(const nlopt_opt opt)
      return nopt;
 
 oom:
+     nopt->munge_on_destroy = NULL; /* better to leak mem than crash */
      nlopt_destroy(nopt);
      return NULL;
 }
 
 /*************************************************************************/
 
-nlopt_result nlopt_set_min_objective(nlopt_opt opt, nlopt_func f, void *f_data)
+nlopt_result NLOPT_STDCALL nlopt_set_precond_min_objective(nlopt_opt opt,
+                                                          nlopt_func f, 
+                                                          nlopt_precond pre,
+                                                          void *f_data)
 {
      if (opt) {
-         opt->f = f; opt->f_data = f_data;
+          nlopt_unset_errmsg(opt);
+         if (opt->munge_on_destroy) opt->munge_on_destroy(opt->f_data);
+         opt->f = f; opt->f_data = f_data; opt->pre = pre;
          opt->maximize = 0;
          if (nlopt_isinf(opt->stopval) && opt->stopval > 0)
               opt->stopval = -HUGE_VAL; /* switch default from max to min */
@@ -180,10 +235,21 @@ nlopt_result nlopt_set_min_objective(nlopt_opt opt, nlopt_func f, void *f_data)
      return NLOPT_INVALID_ARGS;
 }
 
-nlopt_result nlopt_set_max_objective(nlopt_opt opt, nlopt_func f, void *f_data)
+nlopt_result NLOPT_STDCALL nlopt_set_min_objective(nlopt_opt opt,
+                                                  nlopt_func f, void *f_data)
+{
+     return nlopt_set_precond_min_objective(opt, f, NULL, f_data);
+}
+
+nlopt_result NLOPT_STDCALL nlopt_set_precond_max_objective(nlopt_opt opt, 
+                                                          nlopt_func f, 
+                                                          nlopt_precond pre,
+                                                          void *f_data)
 {
      if (opt) {
-         opt->f = f; opt->f_data = f_data;
+          nlopt_unset_errmsg(opt);
+         if (opt->munge_on_destroy) opt->munge_on_destroy(opt->f_data);
+         opt->f = f; opt->f_data = f_data; opt->pre = pre;
          opt->maximize = 1;
          if (nlopt_isinf(opt->stopval) && opt->stopval < 0)
               opt->stopval = +HUGE_VAL; /* switch default from min to max */
@@ -192,30 +258,49 @@ nlopt_result nlopt_set_max_objective(nlopt_opt opt, nlopt_func f, void *f_data)
      return NLOPT_INVALID_ARGS;
 }
 
+nlopt_result NLOPT_STDCALL nlopt_set_max_objective(nlopt_opt opt,
+                                                  nlopt_func f, void *f_data)
+{
+     return nlopt_set_precond_max_objective(opt, f, NULL, f_data);
+}
+
 /*************************************************************************/
 
-nlopt_result nlopt_set_lower_bounds(nlopt_opt opt, const double *lb)
+nlopt_result
+NLOPT_STDCALL nlopt_set_lower_bounds(nlopt_opt opt, const double *lb)
 {
+     nlopt_unset_errmsg(opt);
      if (opt && (opt->n == 0 || lb)) {
+         unsigned int i;
          memcpy(opt->lb, lb, sizeof(double) * (opt->n));
+         for (i = 0; i < opt->n; ++i)
+              if (opt->lb[i] < opt->ub[i] && nlopt_istiny(opt->ub[i] - opt->lb[i]))
+                  opt->lb[i] = opt->ub[i];
          return NLOPT_SUCCESS;
      }
      return NLOPT_INVALID_ARGS;
 }
 
-nlopt_result nlopt_set_lower_bounds1(nlopt_opt opt, double lb)
+nlopt_result
+NLOPT_STDCALL nlopt_set_lower_bounds1(nlopt_opt opt, double lb)
 {
+     nlopt_unset_errmsg(opt);
      if (opt) {
          unsigned i;
-         for (i = 0; i < opt->n; ++i)
-              opt->lb[i] = lb;
+         for (i = 0; i < opt->n; ++i) {
+              opt->lb[i] = lb;
+              if (opt->lb[i] < opt->ub[i] && nlopt_istiny(opt->ub[i] - opt->lb[i]))
+                  opt->lb[i] = opt->ub[i];
+          }
          return NLOPT_SUCCESS;
      }
      return NLOPT_INVALID_ARGS;
 }
 
-nlopt_result nlopt_get_lower_bounds(nlopt_opt opt, double *lb)
+nlopt_result
+NLOPT_STDCALL nlopt_get_lower_bounds(const nlopt_opt opt, double *lb)
 {
+     nlopt_unset_errmsg(opt);
      if (opt && (opt->n == 0 || lb)) {
          memcpy(lb, opt->lb, sizeof(double) * (opt->n));
          return NLOPT_SUCCESS;
@@ -223,28 +308,41 @@ nlopt_result nlopt_get_lower_bounds(nlopt_opt opt, double *lb)
      return NLOPT_INVALID_ARGS;
 }
 
-nlopt_result nlopt_set_upper_bounds(nlopt_opt opt, const double *ub)
+nlopt_result
+NLOPT_STDCALL nlopt_set_upper_bounds(nlopt_opt opt, const double *ub)
 {
+     nlopt_unset_errmsg(opt);
      if (opt && (opt->n == 0 || ub)) {
+         unsigned int i;
          memcpy(opt->ub, ub, sizeof(double) * (opt->n));
+         for (i = 0; i < opt->n; ++i)
+              if (opt->lb[i] < opt->ub[i] && nlopt_istiny(opt->ub[i] - opt->lb[i]))
+                  opt->ub[i] = opt->lb[i];
          return NLOPT_SUCCESS;
      }
      return NLOPT_INVALID_ARGS;
 }
 
-nlopt_result nlopt_set_upper_bounds1(nlopt_opt opt, double ub)
+nlopt_result
+NLOPT_STDCALL nlopt_set_upper_bounds1(nlopt_opt opt, double ub)
 {
+     nlopt_unset_errmsg(opt);
      if (opt) {
          unsigned i;
-         for (i = 0; i < opt->n; ++i)
-              opt->ub[i] = ub;
+         for (i = 0; i < opt->n; ++i) {
+              opt->ub[i] = ub;
+              if (opt->lb[i] < opt->ub[i] && nlopt_istiny(opt->ub[i] - opt->lb[i]))
+                  opt->ub[i] = opt->lb[i];
+          }
          return NLOPT_SUCCESS;
      }
      return NLOPT_INVALID_ARGS;
 }
 
-nlopt_result nlopt_get_upper_bounds(nlopt_opt opt, double *ub)
+nlopt_result
+NLOPT_STDCALL nlopt_get_upper_bounds(const nlopt_opt opt, double *ub)
 {
+     nlopt_unset_errmsg(opt);
      if (opt && (opt->n == 0 || ub)) {
          memcpy(ub, opt->ub, sizeof(double) * (opt->n));
          return NLOPT_SUCCESS;
@@ -254,25 +352,56 @@ nlopt_result nlopt_get_upper_bounds(nlopt_opt opt, double *ub)
 
 /*************************************************************************/
 
-#define AUGLAG_ALG(a) ((a) == NLOPT_LN_AUGLAG ||        \
+#define AUGLAG_ALG(a) ((a) == NLOPT_AUGLAG ||          \
+                      (a) == NLOPT_AUGLAG_EQ ||        \
+                      (a) == NLOPT_LN_AUGLAG ||        \
                       (a) == NLOPT_LN_AUGLAG_EQ ||     \
                       (a) == NLOPT_LD_AUGLAG ||        \
                       (a) == NLOPT_LD_AUGLAG_EQ)
 
-nlopt_result nlopt_remove_inequality_constraints(nlopt_opt opt)
+nlopt_result
+NLOPT_STDCALL nlopt_remove_inequality_constraints(nlopt_opt opt)
 {
+     unsigned i;
+     nlopt_unset_errmsg(opt);
      if (!opt) return NLOPT_INVALID_ARGS;
+     if (opt->munge_on_destroy) {
+         nlopt_munge munge = opt->munge_on_destroy;
+         for (i = 0; i < opt->m; ++i)
+              munge(opt->fc[i].f_data);
+     }
+     for (i = 0; i < opt->m; ++i)
+         free(opt->fc[i].tol);
      free(opt->fc);
      opt->fc = NULL;
      opt->m = opt->m_alloc = 0;
      return NLOPT_SUCCESS;
 }
 
-static nlopt_result add_constraint(unsigned *m, unsigned *m_alloc,
+static nlopt_result add_constraint(nlopt_opt opt,
+                                   unsigned *m, unsigned *m_alloc,
                                   nlopt_constraint **c,
-                                  nlopt_func fc, void *fc_data,
-                                  double tol)
+                                  unsigned fm, nlopt_func fc, nlopt_mfunc mfc,
+                                  nlopt_precond pre,
+                                  void *fc_data,
+                                  const double *tol)
 {
+     double *tolcopy;
+     unsigned i;
+
+     if ((fc && mfc) || (fc && fm != 1) || (!fc && !mfc))
+         return NLOPT_INVALID_ARGS;
+     if (tol) 
+         for (i = 0; i < fm; ++i) if (tol[i] < 0)
+             return ERR(NLOPT_INVALID_ARGS, opt, "negative constraint tolerance");
+
+     tolcopy = (double *) malloc(sizeof(double) * fm);
+     if (fm && !tolcopy) return NLOPT_OUT_OF_MEMORY;
+     if (tol)
+         memcpy(tolcopy, tol, sizeof(double) * fm);
+     else
+         for (i = 0; i < fm; ++i) tolcopy[i] = 0;
+
      *m += 1;
      if (*m > *m_alloc) {
          /* allocate by repeated doubling so that 
@@ -283,73 +412,176 @@ static nlopt_result add_constraint(unsigned *m, unsigned *m_alloc,
                                            * (*m_alloc));
          if (!*c) {
               *m_alloc = *m = 0;
+              free(tolcopy);
               return NLOPT_OUT_OF_MEMORY;
          }
      }
      
+     (*c)[*m - 1].m = fm;
      (*c)[*m - 1].f = fc;
+     (*c)[*m - 1].pre = pre;
+     (*c)[*m - 1].mf = mfc;
      (*c)[*m - 1].f_data = fc_data;
-     (*c)[*m - 1].tol = tol;
+     (*c)[*m - 1].tol = tolcopy;
      return NLOPT_SUCCESS;
 }
 
-nlopt_result nlopt_add_inequality_constraint(nlopt_opt opt,
-                                            nlopt_func fc, void *fc_data,
-                                            double tol)
-{
-     if (opt && fc && tol >= 0) {
-         /* nonlinear constraints are only supported with some algorithms */
-         if (opt->algorithm != NLOPT_LD_MMA 
-             && opt->algorithm != NLOPT_LN_COBYLA
-             && !AUGLAG_ALG(opt->algorithm) 
-             && opt->algorithm != NLOPT_GN_ISRES
-             && opt->algorithm != NLOPT_GN_ORIG_DIRECT
-             && opt->algorithm != NLOPT_GN_ORIG_DIRECT_L)
-              return NLOPT_INVALID_ARGS;
-         return add_constraint(&opt->m, &opt->m_alloc, &opt->fc,
-                               fc, fc_data, tol);
+static int inequality_ok(nlopt_algorithm algorithm) {
+     /* nonlinear constraints are only supported with some algorithms */
+     return (algorithm == NLOPT_LD_MMA || algorithm == NLOPT_LD_CCSAQ 
+            || algorithm == NLOPT_LD_SLSQP
+            || algorithm == NLOPT_LN_COBYLA
+            || AUGLAG_ALG(algorithm) 
+            || algorithm == NLOPT_GN_ISRES
+            || algorithm == NLOPT_GN_ORIG_DIRECT
+            || algorithm == NLOPT_GN_ORIG_DIRECT_L);
+}
+
+nlopt_result
+NLOPT_STDCALL nlopt_add_inequality_mconstraint(nlopt_opt opt, unsigned m,
+                                              nlopt_mfunc fc, void *fc_data,
+                                              const double *tol)
+{
+     nlopt_result ret;
+     nlopt_unset_errmsg(opt);
+     if (!m) { /* empty constraints are always ok */
+         if (opt && opt->munge_on_destroy) opt->munge_on_destroy(fc_data);
+         return NLOPT_SUCCESS;
      }
-     return NLOPT_INVALID_ARGS;
+     if (!opt) ret = NLOPT_INVALID_ARGS;
+     else if (!inequality_ok(opt->algorithm))
+         ret = ERR(NLOPT_INVALID_ARGS, opt, "invalid algorithm for constraints");
+     else ret = add_constraint(opt, &opt->m, &opt->m_alloc, &opt->fc,
+                              m, NULL, fc, NULL, fc_data, tol);
+     if (ret < 0 && opt && opt->munge_on_destroy)
+         opt->munge_on_destroy(fc_data);
+     return ret;
+}
+
+nlopt_result
+NLOPT_STDCALL nlopt_add_precond_inequality_constraint(nlopt_opt opt,
+                                                     nlopt_func fc, 
+                                                     nlopt_precond pre,
+                                                     void *fc_data,
+                                                     double tol)
+{
+     nlopt_result ret;
+     nlopt_unset_errmsg(opt);
+     if (!opt) ret = NLOPT_INVALID_ARGS;
+     else if (!inequality_ok(opt->algorithm))
+         ret = ERR(NLOPT_INVALID_ARGS, opt, "invalid algorithm for constraints");
+     else ret = add_constraint(opt, &opt->m, &opt->m_alloc, &opt->fc,
+                              1, fc, NULL, pre, fc_data, &tol);
+     if (ret < 0 && opt && opt->munge_on_destroy)
+         opt->munge_on_destroy(fc_data);
+     return ret;
+}
+
+nlopt_result
+NLOPT_STDCALL nlopt_add_inequality_constraint(nlopt_opt opt,
+                                             nlopt_func fc, void *fc_data,
+                                             double tol)
+{
+     return nlopt_add_precond_inequality_constraint(opt, fc, NULL, fc_data,
+                                                   tol);
 }
 
-nlopt_result nlopt_remove_equality_constraints(nlopt_opt opt)
+nlopt_result
+NLOPT_STDCALL nlopt_remove_equality_constraints(nlopt_opt opt)
 {
+     unsigned i;
+     nlopt_unset_errmsg(opt);
      if (!opt) return NLOPT_INVALID_ARGS;
+     if (opt->munge_on_destroy) {
+         nlopt_munge munge = opt->munge_on_destroy;
+         for (i = 0; i < opt->p; ++i)
+              munge(opt->h[i].f_data);
+     }
+     for (i = 0; i < opt->p; ++i)
+         free(opt->h[i].tol);
      free(opt->h);
      opt->h = NULL;
      opt->p = opt->p_alloc = 0;
      return NLOPT_SUCCESS;
 }
 
-nlopt_result nlopt_add_equality_constraint(nlopt_opt opt,
-                                          nlopt_func h, void *h_data,
-                                          double tol)
+static int equality_ok(nlopt_algorithm algorithm) {
+     /* equality constraints (h(x) = 0) only via some algorithms */
+     return (AUGLAG_ALG(algorithm) 
+            || algorithm == NLOPT_LD_SLSQP
+            || algorithm == NLOPT_GN_ISRES
+            || algorithm == NLOPT_LN_COBYLA);
+}
+
+nlopt_result
+NLOPT_STDCALL nlopt_add_equality_mconstraint(nlopt_opt opt, unsigned m,
+                                            nlopt_mfunc fc, void *fc_data,
+                                            const double *tol)
 {
-     if (opt && h && tol >= 0) {
-         /* equality constraints (h(x) = 0) only via some algorithms */
-         if (!AUGLAG_ALG(opt->algorithm) && opt->algorithm != NLOPT_GN_ISRES)
-              return NLOPT_INVALID_ARGS;
-         return add_constraint(&opt->p, &opt->p_alloc, &opt->h,
-                               h, h_data, tol);
+     nlopt_result ret;
+     nlopt_unset_errmsg(opt);
+     if (!m) { /* empty constraints are always ok */
+         if (opt && opt->munge_on_destroy) opt->munge_on_destroy(fc_data);
+         return NLOPT_SUCCESS;
      }
-     return NLOPT_INVALID_ARGS;
+     if (!opt) ret = NLOPT_INVALID_ARGS;
+     else if (!equality_ok(opt->algorithm))
+         ret = ERR(NLOPT_INVALID_ARGS, opt, "invalid algorithm for constraints");
+     else if (nlopt_count_constraints(opt->p, opt->h) + m > opt->n) 
+         ret = ERR(NLOPT_INVALID_ARGS, opt, "too many equality constraints");
+     else ret = add_constraint(opt, &opt->p, &opt->p_alloc, &opt->h,
+                              m, NULL, fc, NULL, fc_data, tol);
+     if (ret < 0 && opt && opt->munge_on_destroy)
+         opt->munge_on_destroy(fc_data);
+     return ret;
+}
+
+nlopt_result
+NLOPT_STDCALL nlopt_add_precond_equality_constraint(nlopt_opt opt,
+                                                   nlopt_func fc,
+                                                   nlopt_precond pre,
+                                                   void *fc_data,
+                                                   double tol)
+{
+     nlopt_result ret;
+     nlopt_unset_errmsg(opt);
+     if (!opt) ret = NLOPT_INVALID_ARGS;
+     else if (!equality_ok(opt->algorithm))
+         ret = ERR(NLOPT_INVALID_ARGS, opt, "invalid algorithm for constraints");
+     else if (nlopt_count_constraints(opt->p, opt->h) + 1 > opt->n)
+         ret = ERR(NLOPT_INVALID_ARGS, opt, "too many equality constraints");
+     else ret = add_constraint(opt, &opt->p, &opt->p_alloc, &opt->h,
+                              1, fc, NULL, pre, fc_data, &tol);
+     if (ret < 0 && opt && opt->munge_on_destroy)
+         opt->munge_on_destroy(fc_data);
+     return ret;
+}
+
+nlopt_result
+NLOPT_STDCALL nlopt_add_equality_constraint(nlopt_opt opt,
+                                           nlopt_func fc, void *fc_data,
+                                           double tol)
+{
+     return nlopt_add_precond_equality_constraint(opt, fc, NULL, fc_data, tol);
 }
 
 /*************************************************************************/
 
-#define SET(param, T, arg)                             \
-   nlopt_result nlopt_set_##param(nlopt_opt opt, T arg)        \
-   {                                                   \
-       if (opt) {                                      \
-            opt->arg = arg;                            \
-            return NLOPT_SUCCESS;                      \
-       }                                               \
-       return NLOPT_INVALID_ARGS;                      \
+#define SET(param, T, arg)                                             \
+   nlopt_result NLOPT_STDCALL nlopt_set_##param(nlopt_opt opt, T arg)  \
+   {                                                                   \
+       if (opt) {                                                      \
+             nlopt_unset_errmsg(opt);                                   \
+            opt->arg = arg;                                            \
+            return NLOPT_SUCCESS;                                      \
+       }                                                               \
+       return NLOPT_INVALID_ARGS;                                      \
    }
 
 
-#define GET(param, T, arg) T nlopt_get_##param(const nlopt_opt opt) {  \
-        return opt->arg;                                               \
+#define GET(param, T, arg) T NLOPT_STDCALL     \
+   nlopt_get_##param(const nlopt_opt opt) {    \
+        return opt->arg;                       \
    }
 
 #define GETSET(param, T, arg) GET(param, T, arg) SET(param, T, arg)
@@ -360,19 +592,23 @@ GETSET(ftol_rel, double, ftol_rel)
 GETSET(ftol_abs, double, ftol_abs)
 GETSET(xtol_rel, double, xtol_rel)
 
-nlopt_result nlopt_set_xtol_abs(nlopt_opt opt, const double *xtol_abs)
+nlopt_result
+NLOPT_STDCALL nlopt_set_xtol_abs(nlopt_opt opt, const double *xtol_abs)
 {
      if (opt) {
-         memcpy(opt->xtol_abs, xtol_abs, opt->n & sizeof(double));
+          nlopt_unset_errmsg(opt);
+         memcpy(opt->xtol_abs, xtol_abs, opt->n * sizeof(double));
          return NLOPT_SUCCESS;
      }
      return NLOPT_INVALID_ARGS;
 }
 
-nlopt_result nlopt_set_xtol_abs1(nlopt_opt opt, const double xtol_abs)
+nlopt_result
+NLOPT_STDCALL nlopt_set_xtol_abs1(nlopt_opt opt, double xtol_abs)
 {
      if (opt) {
          unsigned i;
+          nlopt_unset_errmsg(opt);
          for (i = 0; i < opt->n; ++i)
               opt->xtol_abs[i] = xtol_abs;
          return NLOPT_SUCCESS;
@@ -380,9 +616,10 @@ nlopt_result nlopt_set_xtol_abs1(nlopt_opt opt, const double xtol_abs)
      return NLOPT_INVALID_ARGS;
 }
 
-nlopt_result nlopt_get_xtol_abs(const nlopt_opt opt, double *xtol_abs)
+nlopt_result
+NLOPT_STDCALL nlopt_get_xtol_abs(const nlopt_opt opt, double *xtol_abs)
 {
-     memcpy(xtol_abs, opt->xtol_abs, opt->n & sizeof(double));
+     memcpy(xtol_abs, opt->xtol_abs, opt->n * sizeof(double));
      return NLOPT_SUCCESS;
 }
 
@@ -392,9 +629,11 @@ GETSET(maxtime, double, maxtime)
 
 /*************************************************************************/
 
-nlopt_result nlopt_set_force_stop(nlopt_opt opt, int force_stop)
+nlopt_result
+NLOPT_STDCALL nlopt_set_force_stop(nlopt_opt opt, int force_stop)
 {
      if (opt) {
+          nlopt_unset_errmsg(opt);
          opt->force_stop = force_stop;
          if (opt->force_stop_child)
               return nlopt_set_force_stop(opt->force_stop_child, force_stop);
@@ -404,7 +643,7 @@ nlopt_result nlopt_set_force_stop(nlopt_opt opt, int force_stop)
 }
 
 GET(force_stop, int, force_stop)
-nlopt_result nlopt_force_stop(nlopt_opt opt) { 
+nlopt_result NLOPT_STDCALL nlopt_force_stop(nlopt_opt opt) { 
      return nlopt_set_force_stop(opt, 1); 
 }
 
@@ -415,11 +654,14 @@ GET(dimension, unsigned, n)
 
 /*************************************************************************/
 
-nlopt_result nlopt_set_local_optimizer(nlopt_opt opt,
-                                      const nlopt_opt local_opt)
+nlopt_result
+NLOPT_STDCALL nlopt_set_local_optimizer(nlopt_opt opt,
+                                       const nlopt_opt local_opt)
 {
      if (opt) {
-         if (local_opt && local_opt->n != opt->n) return NLOPT_INVALID_ARGS;
+          nlopt_unset_errmsg(opt);
+         if (local_opt && local_opt->n != opt->n)
+              return ERR(NLOPT_INVALID_ARGS, opt, "dimension mismatch in local optimizer");
          nlopt_destroy(opt->local_opt);
          opt->local_opt = nlopt_copy(local_opt);
          if (local_opt) {
@@ -429,6 +671,7 @@ nlopt_result nlopt_set_local_optimizer(nlopt_opt opt,
               nlopt_remove_inequality_constraints(opt->local_opt);
               nlopt_remove_equality_constraints(opt->local_opt);
               nlopt_set_min_objective(opt->local_opt, NULL, NULL);
+              nlopt_set_munge(opt->local_opt, NULL, NULL);
               opt->local_opt->force_stop = 0;
          }
          return NLOPT_SUCCESS;
@@ -439,13 +682,16 @@ nlopt_result nlopt_set_local_optimizer(nlopt_opt opt,
 /*************************************************************************/
 
 GETSET(population, unsigned, stochastic_population)
+GETSET(vector_storage, unsigned, vector_storage)
 
 /*************************************************************************/
 
-nlopt_result nlopt_set_initial_step1(nlopt_opt opt, double dx)
+nlopt_result NLOPT_STDCALL nlopt_set_initial_step1(nlopt_opt opt, double dx)
 {
      unsigned i;
-     if (!opt || dx == 0) return NLOPT_INVALID_ARGS;
+     if (!opt) return NLOPT_INVALID_ARGS;
+     nlopt_unset_errmsg(opt);
+     if (dx == 0) return ERR(NLOPT_INVALID_ARGS, opt, "zero step size");
      if (!opt->dx && opt->n > 0) {
          opt->dx = (double *) malloc(sizeof(double) * (opt->n));
          if (!opt->dx) return NLOPT_OUT_OF_MEMORY;
@@ -454,21 +700,30 @@ nlopt_result nlopt_set_initial_step1(nlopt_opt opt, double dx)
      return NLOPT_SUCCESS;
 }
 
-nlopt_result nlopt_set_initial_step(nlopt_opt opt, const double *dx)
+nlopt_result
+NLOPT_STDCALL nlopt_set_initial_step(nlopt_opt opt, const double *dx)
 {
      unsigned i;
-     if (!opt || !dx) return NLOPT_INVALID_ARGS;
-     for (i = 0; i < opt->n; ++i) if (dx[i] == 0) return NLOPT_INVALID_ARGS;
+     if (!opt) return NLOPT_INVALID_ARGS;
+     nlopt_unset_errmsg(opt);
+     if (!dx) {
+         free(opt->dx); opt->dx = NULL;
+         return NLOPT_SUCCESS;
+     }
+     for (i = 0; i < opt->n; ++i)
+         if (dx[i] == 0) return ERR(NLOPT_INVALID_ARGS, opt, "zero step size");
      if (!opt->dx && nlopt_set_initial_step1(opt, 1) == NLOPT_OUT_OF_MEMORY)
           return NLOPT_OUT_OF_MEMORY;
      memcpy(opt->dx, dx, sizeof(double) * (opt->n));
      return NLOPT_SUCCESS;
 }
 
-nlopt_result nlopt_get_initial_step(const nlopt_opt opt, const double *x, 
-                                   double *dx)
+nlopt_result
+NLOPT_STDCALL nlopt_get_initial_step(const nlopt_opt opt, const double *x, 
+                                    double *dx)
 {
      if (!opt) return NLOPT_INVALID_ARGS;
+     nlopt_unset_errmsg(opt);
      if (!opt->n) return NLOPT_SUCCESS;
      if (!opt->dx) {
          nlopt_opt o = (nlopt_opt) opt; /* discard const temporarily */
@@ -482,11 +737,13 @@ nlopt_result nlopt_get_initial_step(const nlopt_opt opt, const double *x,
      return NLOPT_SUCCESS;
 }
 
-nlopt_result nlopt_set_default_initial_step(nlopt_opt opt, const double *x)
+nlopt_result
+NLOPT_STDCALL nlopt_set_default_initial_step(nlopt_opt opt, const double *x)
 {
      const double *lb, *ub;
      unsigned i;
 
+     nlopt_unset_errmsg(opt);
      if (!opt || !x) return NLOPT_INVALID_ARGS;
      lb = opt->lb; ub = opt->ub;
 
@@ -515,10 +772,10 @@ nlopt_result nlopt_set_default_initial_step(nlopt_opt opt, const double *x)
                   && fabs(x[i] - lb[i]) < fabs(step))
                    step = (x[i] - lb[i]) * 1.1;
          }
-         if (nlopt_isinf(step) || step == 0) {
+         if (nlopt_isinf(step) || nlopt_istiny(step)) {
               step = x[i];
          }
-         if (nlopt_isinf(step) || step == 0)
+         if (nlopt_isinf(step) || step == 0.0)
               step = 1;
          
          opt->dx[i] = step;
@@ -528,40 +785,49 @@ nlopt_result nlopt_set_default_initial_step(nlopt_opt opt, const double *x)
 
 /*************************************************************************/
 
-GETSET(free_f_data, int, free_f_data)
-
-/* the dup_f_data function replaces all f_data pointers with a new
-   pointer to a duplicate block of memory, assuming all non-NULL
-   f_data pointers point to a block of sz bytes...  this is pretty
-   exclusively intended for internal use (e.g. it may lead to a
-   double-free if one subsequently calles add_inequality_constraint
-   etc.), e.g. in the C++ API */
-
-static int dup(void **p, size_t sz) {
-     if (*p) {
-         void *pdup = malloc(sz);
-         if (pdup) {
-              memcpy(pdup, *p, sz);
-              *p = pdup;
-              return 1;
-         }
-         else return 0;
+void NLOPT_STDCALL nlopt_set_munge(nlopt_opt opt,
+                                  nlopt_munge munge_on_destroy,
+                                  nlopt_munge munge_on_copy) {
+     if (opt) {
+         opt->munge_on_destroy = munge_on_destroy;
+         opt->munge_on_copy = munge_on_copy;
      }
-     else return 1;
 }
 
-nlopt_result nlopt_dup_f_data(nlopt_opt opt, size_t sz) {
-     if (opt) {
-         unsigned i;
-         if (!dup(&opt->f_data, sz)) return NLOPT_OUT_OF_MEMORY;
-         for (i = 0; i < opt->m; ++i)
-              if (!dup(&opt->fc[i].f_data, sz)) return NLOPT_OUT_OF_MEMORY;
-         for (i = 0; i < opt->p; ++i)
-              if (!dup(&opt->h[i].f_data, sz)) return NLOPT_OUT_OF_MEMORY;
-         nlopt_set_free_f_data(opt, 1); // nlopt_destroy must now free f_data!
-         return NLOPT_SUCCESS;
+void NLOPT_STDCALL nlopt_munge_data(nlopt_opt opt,
+                                    nlopt_munge2 munge, void *data) {
+     if (opt && munge) {
+          unsigned i;
+          opt->f_data = munge(opt->f_data, data);
+          for (i = 0; i < opt->m; ++i)
+               opt->fc[i].f_data = munge(opt->fc[i].f_data, data);
+          for (i = 0; i < opt->p; ++i)
+               opt->h[i].f_data = munge(opt->h[i].f_data, data);
      }
-     return NLOPT_INVALID_ARGS;
+}
+
+/*************************************************************************/
+
+const char *nlopt_set_errmsg(nlopt_opt opt, const char *format, ...)
+{
+    va_list ap;
+    va_start(ap, format);
+    opt->errmsg = nlopt_vsprintf(opt->errmsg, format, ap);
+    va_end(ap);
+    return opt->errmsg;
+}
+
+void nlopt_unset_errmsg(nlopt_opt opt)
+{
+    if (opt) {
+        free(opt->errmsg);
+        opt->errmsg = NULL;
+    }
+}
+
+const char *nlopt_get_errmsg(nlopt_opt opt)
+{
+    return opt->errmsg;
 }
 
 /*************************************************************************/