chiark / gitweb /
Use trusty
[nlopt.git] / api / options.c
index f3a432030709a13a8bda174a6691c03a8e1ddf47..66c0881faa98923ca77b94fb41380de176eda1fe 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007-2011 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"
 
+#define ERR(err, opt, msg) (nlopt_set_errmsg(opt, msg) ? err : err)
+
 /*************************************************************************/
 
 void NLOPT_STDCALL nlopt_destroy(nlopt_opt opt)
@@ -53,6 +56,7 @@ void NLOPT_STDCALL nlopt_destroy(nlopt_opt opt)
          nlopt_destroy(opt->local_opt);
          free(opt->dx);
          free(opt->work);
+         free(opt->errmsg);
          free(opt);
      }
 }
@@ -91,13 +95,14 @@ nlopt_opt NLOPT_STDCALL nlopt_create(nlopt_algorithm algorithm, unsigned n)
          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);
@@ -126,7 +131,8 @@ nlopt_opt NLOPT_STDCALL nlopt_copy(const nlopt_opt opt)
          nopt->local_opt = NULL;
          nopt->dx = NULL;
          nopt->work = NULL;
-         opt->force_stop_child = NULL;
+         nopt->errmsg = NULL;
+         nopt->force_stop_child = NULL;
 
          munge = nopt->munge_on_copy;
          if (munge && nopt->f_data)
@@ -205,7 +211,7 @@ nlopt_opt NLOPT_STDCALL nlopt_copy(const nlopt_opt opt)
      return nopt;
 
 oom:
-     nopt->munge_on_destroy = NULL; // better to leak mem than crash
+     nopt->munge_on_destroy = NULL; /* better to leak mem than crash */
      nlopt_destroy(nopt);
      return NULL;
 }
@@ -218,6 +224,7 @@ nlopt_result NLOPT_STDCALL nlopt_set_precond_min_objective(nlopt_opt opt,
                                                           void *f_data)
 {
      if (opt) {
+          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;
@@ -231,7 +238,7 @@ nlopt_result NLOPT_STDCALL nlopt_set_precond_min_objective(nlopt_opt opt,
 nlopt_result NLOPT_STDCALL nlopt_set_min_objective(nlopt_opt opt,
                                                   nlopt_func f, void *f_data)
 {
-     nlopt_set_precond_min_objective(opt, f, NULL, 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, 
@@ -240,6 +247,7 @@ nlopt_result NLOPT_STDCALL nlopt_set_precond_max_objective(nlopt_opt opt,
                                                           void *f_data)
 {
      if (opt) {
+          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;
@@ -253,7 +261,7 @@ nlopt_result NLOPT_STDCALL nlopt_set_precond_max_objective(nlopt_opt opt,
 nlopt_result NLOPT_STDCALL nlopt_set_max_objective(nlopt_opt opt,
                                                   nlopt_func f, void *f_data)
 {
-     nlopt_set_precond_max_objective(opt, f, NULL, f_data);
+     return nlopt_set_precond_max_objective(opt, f, NULL, f_data);
 }
 
 /*************************************************************************/
@@ -261,8 +269,13 @@ nlopt_result NLOPT_STDCALL nlopt_set_max_objective(nlopt_opt opt,
 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;
@@ -271,10 +284,14 @@ NLOPT_STDCALL nlopt_set_lower_bounds(nlopt_opt opt, const 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;
@@ -283,6 +300,7 @@ NLOPT_STDCALL nlopt_set_lower_bounds1(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;
@@ -293,8 +311,13 @@ NLOPT_STDCALL nlopt_get_lower_bounds(const nlopt_opt opt, double *lb)
 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;
@@ -303,10 +326,14 @@ NLOPT_STDCALL nlopt_set_upper_bounds(nlopt_opt opt, const 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;
@@ -315,6 +342,7 @@ NLOPT_STDCALL nlopt_set_upper_bounds1(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;
@@ -335,6 +363,7 @@ 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;
@@ -349,7 +378,8 @@ NLOPT_STDCALL nlopt_remove_inequality_constraints(nlopt_opt opt)
      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,
                                   unsigned fm, nlopt_func fc, nlopt_mfunc mfc,
                                   nlopt_precond pre,
@@ -362,8 +392,9 @@ static nlopt_result add_constraint(unsigned *m, unsigned *m_alloc,
      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 NLOPT_INVALID_ARGS;
-     
+         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)
@@ -412,12 +443,15 @@ NLOPT_STDCALL nlopt_add_inequality_mconstraint(nlopt_opt opt, unsigned m,
                                               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;
      }
-     if (!opt || !inequality_ok(opt->algorithm)) ret = NLOPT_INVALID_ARGS;
-     else ret = add_constraint(&opt->m, &opt->m_alloc, &opt->fc,
+     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);
@@ -432,8 +466,11 @@ NLOPT_STDCALL nlopt_add_precond_inequality_constraint(nlopt_opt opt,
                                                      double tol)
 {
      nlopt_result ret;
-     if (!opt || !inequality_ok(opt->algorithm)) ret = NLOPT_INVALID_ARGS;
-     else ret = add_constraint(&opt->m, &opt->m_alloc, &opt->fc,
+     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);
@@ -453,6 +490,7 @@ 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;
@@ -481,14 +519,17 @@ NLOPT_STDCALL nlopt_add_equality_mconstraint(nlopt_opt opt, unsigned m,
                                             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;
      }
-     if (!opt || !equality_ok(opt->algorithm)
-        || nlopt_count_constraints(opt->p, opt->h) + m > opt->n) 
-         ret = NLOPT_INVALID_ARGS;
-     else ret = add_constraint(&opt->p, &opt->p_alloc, &opt->h,
+     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);
@@ -503,10 +544,13 @@ NLOPT_STDCALL nlopt_add_precond_equality_constraint(nlopt_opt opt,
                                                    double tol)
 {
      nlopt_result ret;
-     if (!opt || !equality_ok(opt->algorithm)
-        || nlopt_count_constraints(opt->p, opt->h) + 1 > opt->n)
-         ret = NLOPT_INVALID_ARGS;
-     else ret = add_constraint(&opt->p, &opt->p_alloc, &opt->h,
+     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);
@@ -527,6 +571,7 @@ NLOPT_STDCALL nlopt_add_equality_constraint(nlopt_opt opt,
    nlopt_result NLOPT_STDCALL nlopt_set_##param(nlopt_opt opt, T arg)  \
    {                                                                   \
        if (opt) {                                                      \
+             nlopt_unset_errmsg(opt);                                   \
             opt->arg = arg;                                            \
             return NLOPT_SUCCESS;                                      \
        }                                                               \
@@ -551,6 +596,7 @@ nlopt_result
 NLOPT_STDCALL nlopt_set_xtol_abs(nlopt_opt opt, const double *xtol_abs)
 {
      if (opt) {
+          nlopt_unset_errmsg(opt);
          memcpy(opt->xtol_abs, xtol_abs, opt->n * sizeof(double));
          return NLOPT_SUCCESS;
      }
@@ -562,6 +608,7 @@ 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;
@@ -586,6 +633,7 @@ 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);
@@ -611,7 +659,9 @@ 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) {
@@ -639,7 +689,9 @@ GETSET(vector_storage, unsigned, vector_storage)
 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;
@@ -653,11 +705,13 @@ NLOPT_STDCALL nlopt_set_initial_step(nlopt_opt opt, const double *dx)
 {
      unsigned i;
      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 NLOPT_INVALID_ARGS;
+     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));
@@ -669,6 +723,7 @@ 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 */
@@ -688,6 +743,7 @@ 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;
 
@@ -716,10 +772,10 @@ NLOPT_STDCALL 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;
@@ -738,4 +794,40 @@ void NLOPT_STDCALL nlopt_set_munge(nlopt_opt opt,
      }
 }
 
+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);
+     }
+}
+
+/*************************************************************************/
+
+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;
+}
+
 /*************************************************************************/