nlopt.c 11.9 KB
Newer Older
S
stevenj 已提交
1 2
#include <stdlib.h>
#include <math.h>
3
#include <string.h>
4
#include <float.h>
S
stevenj 已提交
5 6

#include "nlopt.h"
7
#include "nlopt-util.h"
S
stevenj 已提交
8 9
#include "config.h"

S
stevenj 已提交
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
#define MIN(a,b) ((a) < (b) ? (a) : (b))

/*************************************************************************/

#ifdef INFINITY
#  define MY_INF INFINITY
#else
#  define MY_INF HUGE_VAL
#endif

static int my_isinf(double x) {
     return fabs(x) >= HUGE_VAL * 0.99
#ifdef HAVE_ISINF
	  || isinf(x)
#endif
	  ;
}

#ifndef HAVE_ISNAN
static int my_isnan(double x) { return x != x; }
#  define isnan my_isnan
#endif

/*************************************************************************/
34

S
stevenj 已提交
35 36 37 38 39 40 41
void nlopt_version(int *major, int *minor, int *bugfix)
{
     *major = MAJOR_VERSION;
     *minor = MINOR_VERSION;
     *bugfix = BUGFIX_VERSION;
}

S
stevenj 已提交
42 43
/*************************************************************************/

44
static const char nlopt_algorithm_names[NLOPT_NUM_ALGORITHMS][256] = {
45 46 47 48 49 50 51 52 53
     "DIRECT (global, no-derivative)",
     "DIRECT-L (global, no-derivative)",
     "Randomized DIRECT-L (global, no-derivative)",
     "Unscaled DIRECT (global, no-derivative)",
     "Unscaled DIRECT-L (global, no-derivative)",
     "Unscaled Randomized DIRECT-L (global, no-derivative)",
     "Original DIRECT version (global, no-derivative)",
     "Original DIRECT-L version (global, no-derivative)",
     "Subplex (local, no-derivative)",
54
#ifdef WITH_CXX
55 56
     "StoGO (global, derivative-based)",
     "StoGO with randomized search (global, derivative-based)",
57 58 59 60
#else
     "StoGO (NOT COMPILED)",
     "StoGO randomized (NOT COMPILED)",
#endif
61
     "Low-storage BFGS (LBFGS) (local, derivative-based)",
62
     "Principal-axis, praxis (local, no-derivative)",
S
stevenj 已提交
63 64
     "Limited-memory variable-metric, rank 1 (local, derivative-based)",
     "Limited-memory variable-metric, rank 2 (local, derivative-based)",
S
stevenj 已提交
65 66 67 68
     "Truncated Newton (local, derivative-based)",
     "Truncated Newton with restarting (local, derivative-based)",
     "Preconditioned truncated Newton (local, derivative-based)",
     "Preconditioned truncated Newton with restarting (local, derivative-based)",
69
     "Controlled random search (CRS2) with local mutation (global, no-derivative)",
70 71 72 73
     "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)"
S
stevenj 已提交
74 75
};

76 77 78 79 80 81
const char *nlopt_algorithm_name(nlopt_algorithm a)
{
     if (a < 0 || a >= NLOPT_NUM_ALGORITHMS) return "UNKNOWN";
     return nlopt_algorithm_names[a];
}

S
stevenj 已提交
82 83
/*************************************************************************/

84 85 86 87 88 89 90 91 92 93
static int nlopt_srand_called = 0;
void nlopt_srand(unsigned long seed) {
     nlopt_srand_called = 1;
     nlopt_init_genrand(seed);
}

void nlopt_srand_time() {
     nlopt_srand(nlopt_time_seed());
}

S
stevenj 已提交
94
/*************************************************************************/
S
stevenj 已提交
95 96 97 98

typedef struct {
     nlopt_func f;
     void *f_data;
99
     const double *lb, *ub;
S
stevenj 已提交
100 101 102
} nlopt_data;

#include "subplex.h"
103
#include "praxis.h"
S
stevenj 已提交
104

S
stevenj 已提交
105
static double f_subplex(int n, const double *x, void *data_)
S
stevenj 已提交
106
{
107
     int i;
S
stevenj 已提交
108
     nlopt_data *data = (nlopt_data *) data_;
S
stevenj 已提交
109
     double f;
110 111 112 113 114

     /* subplex does not support bound constraints, but it supports
	discontinuous objectives so we can just return Inf for invalid x */
     for (i = 0; i < n; ++i)
	  if (x[i] < data->lb[i] || x[i] > data->ub[i])
S
stevenj 已提交
115
	       return MY_INF;
116

S
stevenj 已提交
117 118
     f = data->f(n, x, NULL, data->f_data);
     return (isnan(f) ? MY_INF : f);
S
stevenj 已提交
119 120 121 122
}

#include "direct.h"

S
stevenj 已提交
123
static double f_direct(int n, const double *x, int *undefined, void *data_)
S
stevenj 已提交
124 125
{
     nlopt_data *data = (nlopt_data *) data_;
126
     double f;
127
     f = data->f(n, x, NULL, data->f_data);
S
stevenj 已提交
128
     *undefined = isnan(f) || my_isinf(f);
S
stevenj 已提交
129 130 131
     return f;
}

132 133 134
#ifdef WITH_CXX
#  include "stogo.h"
#endif
135

136 137
#include "cdirect.h"

138 139
#include "luksan.h"

140 141
#include "crs.h"

S
stevenj 已提交
142
/*************************************************************************/
S
stevenj 已提交
143

144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
/* for "hybrid" algorithms that combine global search with some
   local search algorithm, most of the time we anticipate that people
   will stick with the default local search algorithm, so we
   don't add this as a parameter to nlopt_minimize.  Instead, the user
   can call nlopt_{set/get}_hybrid_local_algorithm to get/set the defaults. */

/* default local-search algorithms */
static nlopt_algorithm local_search_alg_deriv = NLOPT_LD_LBFGS;
static nlopt_algorithm local_search_alg_nonderiv = NLOPT_LN_SUBPLEX;

static int local_search_maxeval = -1; /* no maximum by default */

void nlopt_get_local_search_algorithm(nlopt_algorithm *deriv,
				      nlopt_algorithm *nonderiv,
				      int *maxeval)
{
     *deriv = local_search_alg_deriv;
     *nonderiv = local_search_alg_nonderiv;
     *maxeval = local_search_maxeval;
}

void nlopt_set_local_search_algorithm(nlopt_algorithm deriv,
				      nlopt_algorithm nonderiv,
				      int maxeval)
{
     local_search_alg_deriv = deriv;
     local_search_alg_nonderiv = nonderiv;
     local_search_maxeval = maxeval;
}

/*************************************************************************/

176 177
/* same as nlopt_minimize, but xtol_abs is required to be non-NULL */
static nlopt_result nlopt_minimize_(
178
     nlopt_algorithm algorithm,
S
stevenj 已提交
179 180 181
     int n, nlopt_func f, void *f_data,
     const double *lb, const double *ub, /* bounds */
     double *x, /* in: initial guess, out: minimizer */
182 183
     double *minf, /* out: minimum */
     double minf_max, double ftol_rel, double ftol_abs,
S
stevenj 已提交
184 185 186
     double xtol_rel, const double *xtol_abs,
     int maxeval, double maxtime)
{
187
     int i;
S
stevenj 已提交
188
     nlopt_data d;
189 190
     nlopt_stopping stop;

191
     /* some basic argument checks */
192
     if (n <= 0 || !f || !lb || !ub || !x || !minf)
193 194
	  return NLOPT_INVALID_ARGS;

S
stevenj 已提交
195 196
     d.f = f;
     d.f_data = f_data;
197 198 199
     d.lb = lb;
     d.ub = ub;

200 201 202 203
     /* make sure rand generator is inited */
     if (!nlopt_srand_called)
	  nlopt_srand_time(); /* default is non-deterministic */

204 205 206 207
     /* 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;
S
stevenj 已提交
208

209
     stop.n = n;
210 211
     stop.minf_max = (isnan(minf_max) || (my_isinf(minf_max) && minf_max < 0))
	  ? -MY_INF : minf_max;
212 213 214 215 216 217 218 219 220
     stop.ftol_rel = ftol_rel;
     stop.ftol_abs = ftol_abs;
     stop.xtol_rel = xtol_rel;
     stop.xtol_abs = xtol_abs;
     stop.nevals = 0;
     stop.maxeval = maxeval;
     stop.maxtime = maxtime;
     stop.start = nlopt_seconds();

221
     switch (algorithm) {
222 223 224
	 case NLOPT_GN_DIRECT:
	 case NLOPT_GN_DIRECT_L: 
	 case NLOPT_GN_DIRECT_L_RAND: 
225
	      return cdirect(n, f, f_data, lb, ub, x, minf, &stop, 0.0, 
226 227 228 229 230 231 232
			     (algorithm != NLOPT_GN_DIRECT)
			     + 3 * (algorithm == NLOPT_GN_DIRECT_L_RAND ? 2 : (algorithm != NLOPT_GN_DIRECT))
			     + 9 * (algorithm == NLOPT_GN_DIRECT_L_RAND ? 1 : (algorithm != NLOPT_GN_DIRECT)));
	      
	 case NLOPT_GN_DIRECT_NOSCAL:
	 case NLOPT_GN_DIRECT_L_NOSCAL: 
	 case NLOPT_GN_DIRECT_L_RAND_NOSCAL: 
233
	      return cdirect_unscaled(n, f, f_data, lb, ub, x, minf, 
234 235 236 237
				      &stop, 0.0, 
				      (algorithm != NLOPT_GN_DIRECT)
				      + 3 * (algorithm == NLOPT_GN_DIRECT_L_RAND ? 2 : (algorithm != NLOPT_GN_DIRECT))
				      + 9 * (algorithm == NLOPT_GN_DIRECT_L_RAND ? 1 : (algorithm != NLOPT_GN_DIRECT)));
238
	      
239 240
	 case NLOPT_GN_ORIG_DIRECT:
	 case NLOPT_GN_ORIG_DIRECT_L: 
241
	      switch (direct_optimize(f_direct, &d, n, lb, ub, x, minf,
242 243
				      maxeval, -1, 0.0, 0.0,
				      pow(xtol_rel, (double) n), -1.0,
244
				      stop.minf_max, 0.0,
245
				      NULL, 
246
				      algorithm == NLOPT_GN_ORIG_DIRECT
247 248
				      ? DIRECT_ORIGINAL
				      : DIRECT_GABLONSKY)) {
S
stevenj 已提交
249 250 251 252 253 254 255 256 257 258 259 260
		  case DIRECT_INVALID_BOUNDS:
		  case DIRECT_MAXFEVAL_TOOBIG:
		  case DIRECT_INVALID_ARGS:
		       return NLOPT_INVALID_ARGS;
		  case DIRECT_INIT_FAILED:
		  case DIRECT_SAMPLEPOINTS_FAILED:
		  case DIRECT_SAMPLE_FAILED:
		       return NLOPT_FAILURE;
		  case DIRECT_MAXFEVAL_EXCEEDED:
		  case DIRECT_MAXITER_EXCEEDED:
		       return NLOPT_MAXEVAL_REACHED;
		  case DIRECT_GLOBAL_FOUND:
261
		       return NLOPT_MINF_MAX_REACHED;
S
stevenj 已提交
262 263 264 265 266
		  case DIRECT_VOLTOL:
		  case DIRECT_SIGMATOL:
		       return NLOPT_XTOL_REACHED;
		  case DIRECT_OUT_OF_MEMORY:
		       return NLOPT_OUT_OF_MEMORY;
S
stevenj 已提交
267
	      break;
268
	 }
S
stevenj 已提交
269

270 271
	 case NLOPT_GD_STOGO:
	 case NLOPT_GD_STOGO_RAND:
272
#ifdef WITH_CXX
273
	      if (!stogo_minimize(n, f, f_data, x, minf, lb, ub, &stop,
274
				  algorithm == NLOPT_GD_STOGO
275 276
				  ? 0 : 2*n))
		   return NLOPT_FAILURE;
S
stevenj 已提交
277
	      break;
278 279 280
#else
	      return NLOPT_FAILURE;
#endif
S
stevenj 已提交
281

282
	 case NLOPT_LN_SUBPLEX: {
283
	      int iret;
S
stevenj 已提交
284 285
	      double *scale = (double *) malloc(sizeof(double) * n);
	      if (!scale) return NLOPT_OUT_OF_MEMORY;
286 287 288 289 290 291 292 293 294 295
	      for (i = 0; i < n; ++i) {
		   if (!my_isinf(ub[i]) && !my_isinf(lb[i]))
			scale[i] = (ub[i] - lb[i]) * 0.01;
		   else if (!my_isinf(lb[i]) && x[i] > lb[i])
			scale[i] = (x[i] - lb[i]) * 0.01;
		   else if (!my_isinf(ub[i]) && x[i] < ub[i])
			scale[i] = (ub[i] - x[i]) * 0.01;
		   else
			scale[i] = 0.01 * x[i] + 0.0001;
	      }
296
	      iret = subplex(f_subplex, minf, x, n, &d, &stop, scale);
S
stevenj 已提交
297 298
	      free(scale);
	      switch (iret) {
S
stevenj 已提交
299
		  case -2: return NLOPT_INVALID_ARGS;
300
		  case -10: return NLOPT_MAXTIME_REACHED;
S
stevenj 已提交
301 302 303
		  case -1: return NLOPT_MAXEVAL_REACHED;
		  case 0: return NLOPT_XTOL_REACHED;
		  case 1: return NLOPT_SUCCESS;
304
		  case 2: return NLOPT_MINF_MAX_REACHED;
305 306 307
		  case 20: return NLOPT_FTOL_REACHED;
		  case -200: return NLOPT_OUT_OF_MEMORY;
		  default: return NLOPT_FAILURE; /* unknown return code */
S
stevenj 已提交
308 309 310 311
	      }
	      break;
	 }

312
	 case NLOPT_LN_PRAXIS: {
313 314 315 316 317 318 319 320 321 322 323 324
	      double h0 = HUGE_VAL;
	      for (i = 0; i < n; ++i) {
		   if (!my_isinf(ub[i]) && !my_isinf(lb[i]))
			h0 = MIN(h0, (ub[i] - lb[i]) * 0.01);
		   else if (!my_isinf(lb[i]) && x[i] > lb[i])
			h0 = MIN(h0, (x[i] - lb[i]) * 0.01);
		   else if (!my_isinf(ub[i]) && x[i] < ub[i])
			h0 = MIN(h0, (ub[i] - x[i]) * 0.01);
		   else
			h0 = MIN(h0, 0.01 * x[i] + 0.0001);
	      }
	      return praxis_(0.0, DBL_EPSILON, h0, n, x, f_subplex, &d,
325
			     &stop, minf);
326 327
	 }

328 329
	 case NLOPT_LD_LBFGS: 
	      return luksan_plis(n, f, f_data, lb, ub, x, minf, &stop);
S
stevenj 已提交
330

S
stevenj 已提交
331 332 333 334 335
	 case NLOPT_LD_VAR1: 
	 case NLOPT_LD_VAR2: 
	      return luksan_plip(n, f, f_data, lb, ub, x, minf, &stop,
		   algorithm == NLOPT_LD_VAR1 ? 1 : 2);

S
stevenj 已提交
336 337 338 339 340 341 342 343
	 case NLOPT_LD_TNEWTON: 
	 case NLOPT_LD_TNEWTON_RESTART: 
	 case NLOPT_LD_TNEWTON_PRECOND: 
	 case NLOPT_LD_TNEWTON_PRECOND_RESTART: 
	      return luksan_pnet(n, f, f_data, lb, ub, x, minf, &stop,
				 1 + (algorithm - NLOPT_LD_TNEWTON) % 2,
				 1 + (algorithm - NLOPT_LD_TNEWTON) / 2);

344 345 346
	 case NLOPT_GN_CRS2_LM:
	      return crs_minimize(n, f, f_data, lb, ub, x, minf, &stop, 0);

347 348 349 350 351 352 353 354 355 356 357 358
	 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);

S
stevenj 已提交
359 360 361 362 363 364
	 default:
	      return NLOPT_INVALID_ARGS;
     }

     return NLOPT_SUCCESS;
}
365 366 367 368 369 370

nlopt_result nlopt_minimize(
     nlopt_algorithm algorithm,
     int n, nlopt_func f, void *f_data,
     const double *lb, const double *ub, /* bounds */
     double *x, /* in: initial guess, out: minimizer */
371 372
     double *minf, /* out: minimum */
     double minf_max, double ftol_rel, double ftol_abs,
373 374 375 376 377 378
     double xtol_rel, const double *xtol_abs,
     int maxeval, double maxtime)
{
     nlopt_result ret;
     if (xtol_abs)
	  ret = nlopt_minimize_(algorithm, n, f, f_data, lb, ub,
379
				x, minf, minf_max, ftol_rel, ftol_abs,
380 381 382 383 384 385 386
				xtol_rel, xtol_abs, maxeval, maxtime);
     else {
	  int i;
	  double *xtol = (double *) malloc(sizeof(double) * n);
	  if (!xtol) return NLOPT_OUT_OF_MEMORY;
	  for (i = 0; i < n; ++i) xtol[i] = -1;
	  ret = nlopt_minimize_(algorithm, n, f, f_data, lb, ub,
387
				x, minf, minf_max, ftol_rel, ftol_abs,
388 389 390 391 392
				xtol_rel, xtol, maxeval, maxtime);
	  free(xtol);
     }
     return ret;
}