From 61fe5161861c7658c70563fbf3abf456fb3ce980 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 16 Apr 2015 22:26:50 -0400 Subject: [PATCH] add nlopt_get_errmsg(opt) for more informative error messages; automatically used in C++ and Python exceptions --- api/nlopt-in.hpp | 9 +++- api/nlopt-internal.h | 16 +++++++ api/nlopt.h | 2 + api/optimize.c | 102 +++++++++++++++++++++++++++++++------------ api/options.c | 28 +++++++++++- configure.ac | 4 +- crs/crs.c | 7 ++- esch/esch.c | 5 ++- isres/isres.c | 11 +++-- mlsl/mlsl.c | 5 ++- mma/ccsa_quadratic.c | 12 ++++- mma/mma.c | 6 ++- neldermead/nldrmd.c | 7 ++- newuoa/newuoa.c | 9 +++- slsqp/slsqp.c | 2 + util/nlopt-util.h | 10 +++++ util/stop.c | 31 +++++++++++++ 17 files changed, 221 insertions(+), 45 deletions(-) diff --git a/api/nlopt-in.hpp b/api/nlopt-in.hpp index d2ef93f..582c676 100644 --- a/api/nlopt-in.hpp +++ b/api/nlopt-in.hpp @@ -77,9 +77,9 @@ namespace nlopt { void mythrow(nlopt_result ret) const { switch (ret) { - case NLOPT_FAILURE: throw std::runtime_error("nlopt failure"); + case NLOPT_FAILURE: throw std::runtime_error(get_errmsg() ? get_errmsg() : "nlopt failure"); case NLOPT_OUT_OF_MEMORY: throw std::bad_alloc(); - case NLOPT_INVALID_ARGS: throw std::invalid_argument("nlopt invalid argument"); + case NLOPT_INVALID_ARGS: throw std::invalid_argument(get_errmsg() ? get_errmsg() : "nlopt invalid argument"); case NLOPT_ROUNDOFF_LIMITED: throw roundoff_limited(); case NLOPT_FORCED_STOP: throw forced_stop(); default: break; @@ -468,6 +468,11 @@ namespace nlopt { NLOPT_GETSET(int, force_stop) void force_stop() { set_force_stop(1); } + const char *get_errmsg() const { + if (!o) throw std::runtime_error("uninitialized nlopt::opt"); + return nlopt_get_errmsg(o); + } + // algorithm-specific parameters: void set_local_optimizer(const opt &lo) { diff --git a/api/nlopt-internal.h b/api/nlopt-internal.h index bc67e44..c4d13d1 100644 --- a/api/nlopt-internal.h +++ b/api/nlopt-internal.h @@ -73,6 +73,8 @@ struct nlopt_opt_s { unsigned vector_storage; /* max subspace dimension (0 for default) */ void *work; /* algorithm-specific workspace during optimization */ + + char *errmsg; /* description of most recent error */ }; /*********************************************************************/ @@ -88,6 +90,20 @@ extern unsigned nlopt_stochastic_population; /*********************************************************************/ +#define RETURN_ERR(err, opt, msg) do { \ + nlopt_set_errmsg(opt, msg); \ + return err; \ +} while (0) + +extern const char *nlopt_set_errmsg(nlopt_opt opt, const char *format, ...) +#ifdef __GNUC__ +__attribute__ ((format (printf, 2, 3))) +#endif +; +extern void nlopt_unset_errmsg(nlopt_opt opt); + +/*********************************************************************/ + #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ diff --git a/api/nlopt.h b/api/nlopt.h index b9bb619..a0e6b38 100644 --- a/api/nlopt.h +++ b/api/nlopt.h @@ -209,6 +209,8 @@ NLOPT_EXTERN(nlopt_result) nlopt_set_precond_max_objective(nlopt_opt opt, nlopt_ NLOPT_EXTERN(nlopt_algorithm) nlopt_get_algorithm(const nlopt_opt opt); NLOPT_EXTERN(unsigned) nlopt_get_dimension(const nlopt_opt opt); +NLOPT_EXTERN(const char*) nlopt_get_errmsg(nlopt_opt opt); + /* constraints: */ NLOPT_EXTERN(nlopt_result) nlopt_set_lower_bounds(nlopt_opt opt, diff --git a/api/optimize.c b/api/optimize.c index f6f1236..2087d8f 100644 --- a/api/optimize.c +++ b/api/optimize.c @@ -375,7 +375,8 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) nlopt_stopping stop; if (!opt || !x || !minf || !opt->f - || opt->maximize) return NLOPT_INVALID_ARGS; + || opt->maximize) RETURN_ERR(NLOPT_INVALID_ARGS, opt, + "NULL args to nlopt_optimize_"); /* reset stopping flag */ nlopt_set_force_stop(opt, 0); @@ -400,8 +401,11 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) /* check bound constraints */ for (i = 0; i < n; ++i) - if (lb[i] > ub[i] || x[i] < lb[i] || x[i] > ub[i]) - return NLOPT_INVALID_ARGS; + if (lb[i] > ub[i] || x[i] < lb[i] || x[i] > ub[i]) { + nlopt_set_errmsg(opt, "bounds %d fail %g <= %g <= %g", + i, lb[i], x[i], ub[i]); + return NLOPT_INVALID_ARGS; + } stop.n = n; stop.minf_max = opt->stopval; @@ -414,12 +418,15 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) stop.maxtime = opt->maxtime; stop.start = nlopt_seconds(); stop.force_stop = &(opt->force_stop); + stop.stop_msg = &(opt->errmsg); switch (algorithm) { case NLOPT_GN_DIRECT: case NLOPT_GN_DIRECT_L: case NLOPT_GN_DIRECT_L_RAND: - if (!finite_domain(n, lb, ub)) return NLOPT_INVALID_ARGS; + if (!finite_domain(n, lb, ub)) + RETURN_ERR(NLOPT_INVALID_ARGS, opt, + "finite domain required for global algorithm"); return cdirect(ni, f, f_data, lb, ub, x, minf, &stop, 0.0, (algorithm != NLOPT_GN_DIRECT) @@ -431,7 +438,9 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) case NLOPT_GN_DIRECT_NOSCAL: case NLOPT_GN_DIRECT_L_NOSCAL: case NLOPT_GN_DIRECT_L_RAND_NOSCAL: - if (!finite_domain(n, lb, ub)) return NLOPT_INVALID_ARGS; + if (!finite_domain(n, lb, ub)) + RETURN_ERR(NLOPT_INVALID_ARGS, opt, + "finite domain required for global algorithm"); return cdirect_unscaled(ni, f, f_data, lb, ub, x, minf, &stop, 0.0, (algorithm != NLOPT_GN_DIRECT) @@ -441,7 +450,9 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) case NLOPT_GN_ORIG_DIRECT: case NLOPT_GN_ORIG_DIRECT_L: { direct_return_code dret; - if (!finite_domain(n, lb, ub)) return NLOPT_INVALID_ARGS; + if (!finite_domain(n, lb, ub)) + RETURN_ERR(NLOPT_INVALID_ARGS, opt, + "finite domain required for global algorithm"); opt->work = malloc(sizeof(double) * nlopt_max_constraint_dim(opt->m, opt->fc)); @@ -460,13 +471,22 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) free(opt->work); opt->work = NULL; switch (dret) { case DIRECT_INVALID_BOUNDS: + RETURN_ERR(NLOPT_INVALID_ARGS, opt, + "invalid bounds for DIRECT"); case DIRECT_MAXFEVAL_TOOBIG: + RETURN_ERR(NLOPT_INVALID_ARGS, opt, + "maxeval too big for DIRECT"); case DIRECT_INVALID_ARGS: return NLOPT_INVALID_ARGS; case DIRECT_INIT_FAILED: + RETURN_ERR(NLOPT_FAILURE, opt, + "init failed for DIRECT"); case DIRECT_SAMPLEPOINTS_FAILED: + RETURN_ERR(NLOPT_FAILURE, opt, + "sample-points failed for DIRECT"); case DIRECT_SAMPLE_FAILED: - return NLOPT_FAILURE; + RETURN_ERR(NLOPT_FAILURE, opt, + "sample failed for DIRECT"); case DIRECT_MAXFEVAL_EXCEEDED: case DIRECT_MAXITER_EXCEEDED: return NLOPT_MAXEVAL_REACHED; @@ -488,7 +508,9 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) case NLOPT_GD_STOGO: case NLOPT_GD_STOGO_RAND: #ifdef WITH_CXX - if (!finite_domain(n, lb, ub)) return NLOPT_INVALID_ARGS; + if (!finite_domain(n, lb, ub)) + RETURN_ERR(NLOPT_INVALID_ARGS, opt, + "finite domain required for global algorithm"); if (!stogo_minimize(ni, f, f_data, x, minf, lb, ub, &stop, algorithm == NLOPT_GD_STOGO ? 0 : (int) POP(2*n))) @@ -554,7 +576,9 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) 1 + (algorithm - NLOPT_LD_TNEWTON) / 2); case NLOPT_GN_CRS2_LM: - if (!finite_domain(n, lb, ub)) return NLOPT_INVALID_ARGS; + if (!finite_domain(n, lb, ub)) + RETURN_ERR(NLOPT_INVALID_ARGS, opt, + "finite domain required for global algorithm"); return crs_minimize(ni, f, f_data, lb, ub, x, minf, &stop, (int) POP(0), 0); @@ -566,10 +590,13 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) case NLOPT_GD_MLSL_LDS: { nlopt_opt local_opt = opt->local_opt; nlopt_result ret; - if (!finite_domain(n, lb, ub)) return NLOPT_INVALID_ARGS; + if (!finite_domain(n, lb, ub)) + RETURN_ERR(NLOPT_INVALID_ARGS, opt, + "finite domain required for global algorithm"); if (!local_opt && (algorithm == NLOPT_G_MLSL || algorithm == NLOPT_G_MLSL_LDS)) - return NLOPT_INVALID_ARGS; + RETURN_ERR(NLOPT_INVALID_ARGS, opt, + "local optimizer must be specified for G_MLSL"); if (!local_opt) { /* default */ nlopt_algorithm local_alg = (algorithm == NLOPT_GN_MLSL || algorithm == NLOPT_GN_MLSL_LDS) @@ -582,7 +609,8 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) algorithm == NLOPT_GN_MLSL_LDS) ? NLOPT_LN_COBYLA : NLOPT_LD_MMA; local_opt = nlopt_create(local_alg, n); - if (!local_opt) return NLOPT_FAILURE; + if (!local_opt) RETURN_ERR(NLOPT_FAILURE, opt, + "failed to create local_opt"); nlopt_set_ftol_rel(local_opt, opt->ftol_rel); nlopt_set_ftol_abs(local_opt, opt->ftol_abs); nlopt_set_xtol_rel(local_opt, opt->xtol_rel); @@ -616,7 +644,8 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) nlopt_local_search_alg_deriv), nlopt_count_constraints(opt->m, opt->fc)); - if (!dual_opt) return NLOPT_FAILURE; + if (!dual_opt) RETURN_ERR(NLOPT_FAILURE, opt, + "failed creating dual optimizer"); nlopt_set_ftol_rel(dual_opt, LO(ftol_rel, 1e-14)); nlopt_set_ftol_abs(dual_opt, LO(ftol_abs, 0.0)); nlopt_set_maxeval(dual_opt, LO(maxeval, 100000)); @@ -639,7 +668,8 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) if (!opt->dx) { freedx = 1; if (nlopt_set_default_initial_step(opt, x) != NLOPT_SUCCESS) - return NLOPT_OUT_OF_MEMORY; + RETURN_ERR(NLOPT_OUT_OF_MEMORY, opt, + "failed to allocate initial step"); } ret = cobyla_minimize(n, f, f_data, opt->m, opt->fc, @@ -653,7 +683,8 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) case NLOPT_LN_NEWUOA: { double step; if (initial_step(opt, x, &step) != NLOPT_SUCCESS) - return NLOPT_OUT_OF_MEMORY; + RETURN_ERR(NLOPT_OUT_OF_MEMORY, opt, + "failed to allocate initial step"); return newuoa(ni, 2*n+1, x, 0, 0, step, &stop, minf, f_noderiv, opt); } @@ -661,7 +692,8 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) case NLOPT_LN_NEWUOA_BOUND: { double step; if (initial_step(opt, x, &step) != NLOPT_SUCCESS) - return NLOPT_OUT_OF_MEMORY; + RETURN_ERR(NLOPT_OUT_OF_MEMORY, opt, + "failed to allocate initial step"); return newuoa(ni, 2*n+1, x, lb, ub, step, &stop, minf, f_noderiv, opt); } @@ -672,7 +704,8 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) if (!opt->dx) { freedx = 1; if (nlopt_set_default_initial_step(opt, x) != NLOPT_SUCCESS) - return NLOPT_OUT_OF_MEMORY; + RETURN_ERR(NLOPT_OUT_OF_MEMORY, opt, + "failed to allocate initial step"); } ret = bobyqa(ni, 2*n+1, x, lb, ub, opt->dx, &stop, minf, opt->f, opt->f_data); @@ -688,12 +721,13 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) if (!opt->dx) { freedx = 1; if (nlopt_set_default_initial_step(opt, x) != NLOPT_SUCCESS) - return NLOPT_OUT_OF_MEMORY; + RETURN_ERR(NLOPT_OUT_OF_MEMORY, opt, + "failed to allocate initial step"); } if (algorithm == NLOPT_LN_NELDERMEAD) - ret= nldrmd_minimize(ni,f,f_data,lb,ub,x,minf,opt->dx,&stop); + ret=nldrmd_minimize(ni,f,f_data,lb,ub,x,minf,opt->dx,&stop); else - ret= sbplx_minimize(ni,f,f_data,lb,ub,x,minf,opt->dx,&stop); + ret=sbplx_minimize(ni,f,f_data,lb,ub,x,minf,opt->dx,&stop); if (freedx) { free(opt->dx); opt->dx = NULL; } return ret; } @@ -708,14 +742,16 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) nlopt_result ret; if ((algorithm == NLOPT_AUGLAG || algorithm == NLOPT_AUGLAG_EQ) && !local_opt) - return NLOPT_INVALID_ARGS; + RETURN_ERR(NLOPT_INVALID_ARGS, opt, + "local optimizer must be specified for AUGLAG"); if (!local_opt) { /* default */ local_opt = nlopt_create( algorithm == NLOPT_LN_AUGLAG || algorithm == NLOPT_LN_AUGLAG_EQ ? nlopt_local_search_alg_nonderiv : nlopt_local_search_alg_deriv, n); - if (!local_opt) return NLOPT_FAILURE; + if (!local_opt) RETURN_ERR(NLOPT_FAILURE, opt, + "failed to create local_opt"); nlopt_set_ftol_rel(local_opt, opt->ftol_rel); nlopt_set_ftol_abs(local_opt, opt->ftol_abs); nlopt_set_xtol_rel(local_opt, opt->xtol_rel); @@ -738,7 +774,9 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) } case NLOPT_GN_ISRES: - if (!finite_domain(n, lb, ub)) return NLOPT_INVALID_ARGS; + if (!finite_domain(n, lb, ub)) + RETURN_ERR(NLOPT_INVALID_ARGS, opt, + "finite domain required for global algorithm"); return isres_minimize(ni, f, f_data, (int) (opt->m), opt->fc, (int) (opt->p), opt->h, @@ -746,7 +784,9 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) (int) POP(0)); case NLOPT_GN_ESCH: - if (!finite_domain(n, lb, ub)) return NLOPT_INVALID_ARGS; + if (!finite_domain(n, lb, ub)) + RETURN_ERR(NLOPT_INVALID_ARGS, opt, + "finite domain required for global algorithm"); return chevolutionarystrategy(n, f, f_data, lb, ub, x, minf, &stop, (unsigned) POP(0), @@ -803,7 +843,9 @@ NLOPT_STDCALL nlopt_optimize(nlopt_opt opt, double *x, double *opt_f) int maximize; nlopt_result ret; - if (!opt || !opt_f || !opt->f) return NLOPT_INVALID_ARGS; + nlopt_unset_errmsg(opt); + if (!opt || !opt_f || !opt->f) RETURN_ERR(NLOPT_INVALID_ARGS, opt, + "NULL args to nlopt_optimize"); f = opt->f; f_data = opt->f_data; pre = opt->pre; /* for maximizing, just minimize the f_max wrapper, which @@ -820,7 +862,11 @@ NLOPT_STDCALL nlopt_optimize(nlopt_opt opt, double *x, double *opt_f) nlopt_opt elim_opt = opt; if (elimdim_wrapcheck(opt)) { elim_opt = elimdim_create(opt); - if (!elim_opt) { ret = NLOPT_OUT_OF_MEMORY; goto done; } + if (!elim_opt) { + nlopt_set_errmsg(opt, "failure allocating elim_opt"); + ret = NLOPT_OUT_OF_MEMORY; + goto done; + } elimdim_shrink(opt->n, x, opt->lb, opt->ub); } @@ -852,7 +898,9 @@ nlopt_result nlopt_optimize_limited(nlopt_opt opt, double *x, double *minf, double save_maxtime; nlopt_result ret; - if (!opt) return NLOPT_INVALID_ARGS; + nlopt_unset_errmsg(opt); + + if (!opt) RETURN_ERR(NLOPT_INVALID_ARGS, opt, "NULL opt arg"); save_maxeval = nlopt_get_maxeval(opt); save_maxtime = nlopt_get_maxtime(opt); diff --git a/api/options.c b/api/options.c index d6d1827..69c7693 100644 --- a/api/options.c +++ b/api/options.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "nlopt-internal.h" @@ -53,6 +54,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,6 +93,7 @@ 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)); @@ -126,7 +129,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) @@ -751,3 +755,25 @@ void NLOPT_STDCALL nlopt_munge_data(nlopt_opt opt, } /*************************************************************************/ + +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) +{ + free(opt->errmsg); + opt->errmsg = NULL; +} + +const char *nlopt_get_errmsg(nlopt_opt opt) +{ + return opt->errmsg; +} + +/*************************************************************************/ diff --git a/configure.ac b/configure.ac index 3b93080..cb8a4f9 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT(nlopt, 2.4.2, stevenj@alum.mit.edu) +AC_INIT(nlopt, 2.5, stevenj@alum.mit.edu) AC_CONFIG_SRCDIR(api/nlopt.h) -SHARED_VERSION_INFO="8:2:8" # CURRENT:REVISION:AGE +SHARED_VERSION_INFO="9:0:9" # CURRENT:REVISION:AGE AM_INIT_AUTOMAKE(1.7) AM_CONFIG_HEADER(config.h) diff --git a/crs/crs.c b/crs/crs.c index d6eb5e2..16f4ad5 100644 --- a/crs/crs.c +++ b/crs/crs.c @@ -177,8 +177,11 @@ static nlopt_result crs_init(crs_data *d, int n, const double *x, } else d->N = population; - if (d->N < n + 1) /* population must be big enough for a simplex */ - return NLOPT_INVALID_ARGS; + if (d->N < n + 1) { /* population must be big enough for a simplex */ + nlopt_stop_msg(stop, "population %d should be >= dimension + 1 = %d", + d->N, n+1); + return NLOPT_INVALID_ARGS; + } d->n = n; d->stop = stop; diff --git a/esch/esch.c b/esch/esch.c index e2c01d7..8a21cb9 100644 --- a/esch/esch.c +++ b/esch/esch.c @@ -95,7 +95,10 @@ nlopt_result chevolutionarystrategy( // ------------------------------- if (!np) np = 40; if (!no) no = 60; - if ((np < 1)||(no<1)) return NLOPT_INVALID_ARGS; + if ((np < 1)||(no<1)) { + nlopt_stop_msg(stop, "populations %d, %d are too small", np, no); + return NLOPT_INVALID_ARGS; + } esparents = (Individual*) malloc(sizeof(Individual) * np); esoffsprings = (Individual*) malloc(sizeof(Individual) * no); estotal = (Individual*) malloc(sizeof(Individual) * (np+no)); diff --git a/isres/isres.c b/isres/isres.c index 02b6f3c..0e40106 100644 --- a/isres/isres.c +++ b/isres/isres.c @@ -86,15 +86,20 @@ nlopt_result isres_minimize(int n, nlopt_func f, void *f_data, *minf = HUGE_VAL; if (!population) population = 20 * (n + 1); - if (population < 1) return NLOPT_INVALID_ARGS; + if (population < 1) { + nlopt_stop_msg(stop, "population %d is too small", population); + return NLOPT_INVALID_ARGS; + } survivors = ceil(population * SURVIVOR); taup = PHI / sqrt(2*n); tau = PHI / sqrt(2*sqrt(n)); /* we don't handle unbounded search regions */ - for (j = 0; j < n; ++j) if (nlopt_isinf(lb[j]) || nlopt_isinf(ub[j])) - return NLOPT_INVALID_ARGS; + for (j = 0; j < n; ++j) if (nlopt_isinf(lb[j]) || nlopt_isinf(ub[j])) { + nlopt_stop_msg(stop, "isres requires a finite search region"); + return NLOPT_INVALID_ARGS; + } ires = imax2(nlopt_max_constraint_dim(m, fc), nlopt_max_constraint_dim(p, h)); diff --git a/mlsl/mlsl.c b/mlsl/mlsl.c index da57c95..b90d690 100644 --- a/mlsl/mlsl.c +++ b/mlsl/mlsl.c @@ -292,7 +292,10 @@ nlopt_result mlsl_minimize(int n, nlopt_func f, void *f_data, d.N = 4; /* FIXME: what is good number of samples per iteration? */ else d.N = Nsamples; - if (d.N < 1) return NLOPT_INVALID_ARGS; + if (d.N < 1) { + nlopt_stop_msg(stop, "population %d is too small", d.N); + return NLOPT_INVALID_ARGS; + } d.n = n; d.lb = lb; d.ub = ub; diff --git a/mma/ccsa_quadratic.c b/mma/ccsa_quadratic.c index 199005e..ddbf77e 100644 --- a/mma/ccsa_quadratic.c +++ b/mma/ccsa_quadratic.c @@ -234,7 +234,11 @@ nlopt_result ccsa_quadratic_minimize( nlopt_opt pre_opt = NULL; m = nlopt_count_constraints(mfc = m, fc); - if (nlopt_get_dimension(dual_opt) != m) return NLOPT_INVALID_ARGS; + if (nlopt_get_dimension(dual_opt) != m) { + nlopt_stop_msg(stop, "dual optimizer has wrong dimension %d != %d", + nlopt_get_dimension(dual_opt), m); + return NLOPT_INVALID_ARGS; + } sigma = (double *) malloc(sizeof(double) * (6*n + 2*m*n + m*7)); if (!sigma) return NLOPT_OUT_OF_MEMORY; dfdx = sigma + n; @@ -298,7 +302,11 @@ nlopt_result ccsa_quadratic_minimize( pre_ub = pre_lb + n; pre_opt = nlopt_create(nlopt_get_algorithm(dual_opt), n); - if (!pre_opt) { ret = NLOPT_FAILURE; goto done; } + if (!pre_opt) { + nlopt_stop_msg(stop, "failure creating precond. optimizer"); + ret = NLOPT_FAILURE; + goto done; + } ret = nlopt_set_min_objective(pre_opt, g0, &dd); if (ret < 0) goto done; ret = nlopt_add_inequality_mconstraint(pre_opt, m, gi, &dd, NULL); diff --git a/mma/mma.c b/mma/mma.c index 3d03bfd..d7e9725 100644 --- a/mma/mma.c +++ b/mma/mma.c @@ -166,7 +166,11 @@ nlopt_result mma_minimize(unsigned n, nlopt_func f, void *f_data, unsigned mfc; m = nlopt_count_constraints(mfc = m, fc); - if (nlopt_get_dimension(dual_opt) != m) return NLOPT_INVALID_ARGS; + if (nlopt_get_dimension(dual_opt) != m) { + nlopt_stop_msg(stop, "dual optimizer has wrong dimension %d != %d", + nlopt_get_dimension(dual_opt), m); + return NLOPT_INVALID_ARGS; + } sigma = (double *) malloc(sizeof(double) * (6*n + 2*m*n + m*7)); if (!sigma) return NLOPT_OUT_OF_MEMORY; dfdx = sigma + n; diff --git a/neldermead/nldrmd.c b/neldermead/nldrmd.c index 5bdc1f3..a8e7ee4 100644 --- a/neldermead/nldrmd.c +++ b/neldermead/nldrmd.c @@ -153,7 +153,12 @@ nlopt_result nldrmd_minimize_(int n, nlopt_func f, void *f_data, ub[i] : lb[i]) + x[i]); } } - if (close(pt[1+i], x[i])) { ret=NLOPT_FAILURE; goto done; } + if (close(pt[1+i], x[i])) { + nlopt_stop_msg(stop, "starting step size led to simplex that was too small in dimension %d: %g is too close to x[%d]=%g", + i, pt[1+i], i, x[i]); + ret=NLOPT_FAILURE; + goto done; + } pt[0] = f(n, pt+1, NULL, f_data); CHECK_EVAL(pt+1, pt[0]); } diff --git a/newuoa/newuoa.c b/newuoa/newuoa.c index 711cf4f..f6ad2ce 100644 --- a/newuoa/newuoa.c +++ b/newuoa/newuoa.c @@ -2519,8 +2519,13 @@ nlopt_result newuoa(int n, int npt, double *x, /* Function Body */ np = n + 1; nptm = npt - np; - if (n < 2 || npt < n + 2 || npt > (n + 2) * np / 2) { - return NLOPT_INVALID_ARGS; + if (n < 2) { + nlopt_stop_msg(stop, "dimension %d must be >= 2", n); + return NLOPT_INVALID_ARGS; + } + if (npt < n + 2 || npt > (n + 2) * np / 2) { + nlopt_stop_msg(stop, "invalid # of interpolation conditions %d", npt); + return NLOPT_INVALID_ARGS; } ndim = npt + n; ixb = 1; diff --git a/slsqp/slsqp.c b/slsqp/slsqp.c index 7a341d6..2548e91 100644 --- a/slsqp/slsqp.c +++ b/slsqp/slsqp.c @@ -2580,10 +2580,12 @@ nlopt_result nlopt_slsqp(unsigned n, nlopt_func f, void *f_data, case 4: /* inequality constraints incompatible */ case 3: /* more than 3*n iterations in LSQ subproblem */ case 9: /* more than iter iterations in SQP */ + nlopt_stop_msg(stop, "bug: more than iter SQP iterations"); ret = NLOPT_FAILURE; goto done; case 2: /* number of equality constraints > n */ default: /* >= 10: working space w or jw too small */ + nlopt_stop_msg(stop, "bug: workspace is too small"); ret = NLOPT_INVALID_ARGS; goto done; } diff --git a/util/nlopt-util.h b/util/nlopt-util.h index d20a8b1..a2f5fd6 100644 --- a/util/nlopt-util.h +++ b/util/nlopt-util.h @@ -24,6 +24,7 @@ #define NLOPT_UTIL_H #include +#include #include #include "config.h" @@ -82,6 +83,7 @@ typedef struct { int nevals, maxeval; double maxtime, start; int *force_stop; + char **stop_msg; /* pointer to msg string to update */ } nlopt_stopping; extern int nlopt_stop_f(const nlopt_stopping *stop, double f, double oldf); extern int nlopt_stop_ftol(const nlopt_stopping *stop, double f, double oldf); @@ -98,6 +100,14 @@ extern int nlopt_stop_time(const nlopt_stopping *stop); extern int nlopt_stop_evalstime(const nlopt_stopping *stop); extern int nlopt_stop_forced(const nlopt_stopping *stop); +/* like vsprintf, but reallocs p to whatever size is needed */ +extern char *nlopt_vsprintf(char *p, const char *format, va_list ap); +extern void nlopt_stop_msg(const nlopt_stopping *s, const char *format, ...) +#ifdef __GNUC__ +__attribute__ ((format (printf, 2, 3))) +#endif +; + /* for local optimizations, temporarily setting eval/time limits */ extern nlopt_result nlopt_optimize_limited(nlopt_opt opt, double *x, double *minf, diff --git a/util/stop.c b/util/stop.c index 9e29a75..8d999bf 100644 --- a/util/stop.c +++ b/util/stop.c @@ -21,6 +21,9 @@ */ #include +#include +#include +#include #include "nlopt-util.h" /* utility routines to implement the various stopping criteria */ @@ -132,3 +135,31 @@ void nlopt_eval_constraint(double *result, double *grad, else c->mf(c->m, result, n, x, grad, c->f_data); } + +char *nlopt_vsprintf(char *p, const char *format, va_list ap) +{ + size_t len = strlen(format) + 128; + int ret; + + p = (char *) realloc(p, len); + if (!p) abort(); + + while ((ret = vsnprintf(p, len, format, ap)) < 0 || (size_t)ret >= len) { + /* C99 vsnprintf returns the required number of bytes (excluding \0) + if the buffer is too small; older versions (e.g. MS) return -1 */ + len = ret >= 0 ? (size_t)(ret + 1) : (len*3)>>1; + p = realloc(p, len); + if (!p) abort(); + } + return p; +} + +void nlopt_stop_msg(const nlopt_stopping *s, const char *format, ...) +{ + va_list ap; + if (s->stop_msg) { + va_start(ap, format); + *(s->stop_msg) = nlopt_vsprintf(*(s->stop_msg), format, ap); + va_end(ap); + } +} -- 2.30.2