mma.c 13.6 KB
Newer Older
S
stevenj 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/* Copyright (c) 2007-2008 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
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
 */

S
stevenj 已提交
23 24 25
#include <stdlib.h>
#include <math.h>
#include <string.h>
S
stevenj 已提交
26
#include <stdio.h>
S
stevenj 已提交
27 28

#include "mma.h"
S
stevenj 已提交
29
#include "nlopt-util.h"
S
stevenj 已提交
30

S
stevenj 已提交
31 32
int mma_verbose = 0; /* > 0 for verbose output */

S
stevenj 已提交
33 34 35
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) > (b) ? (a) : (b))

36 37 38 39 40
#ifndef HAVE_ISNAN
static int my_isnan(double x) { return x != x; }
#  define isnan my_isnan
#endif

S
stevenj 已提交
41 42 43
/* magic minimum value for rho in MMA ... the 2002 paper says it should
   be a "fixed, strictly positive `small' number, e.g. 1e-5"
   ... grrr, I hate these magic numbers, which seem like they
S
stevenj 已提交
44 45
   should depend on the objective function in some way ... in particular,
   note that rho is dimensionful (= dimensions of objective function) */
S
stevenj 已提交
46 47
#define MMA_RHOMIN 1e-5

S
stevenj 已提交
48
/***********************************************************************/
49
/* function for MMA's dual solution of the approximate problem */
S
stevenj 已提交
50 51

typedef struct {
52
     int count; /* evaluation count, incremented each call */
S
stevenj 已提交
53 54 55 56 57 58 59 60 61
     int n; /* must be set on input to dimension of x */
     const double *x, *lb, *ub, *sigma, *dfdx; /* arrays of length n */
     const double *dfcdx; /* m-by-n array of fc gradients */
     double fval, rho; /* must be set on input */
     const double *fcval, *rhoc; /* arrays of length m */
     double *xcur; /* array of length n, output each time */
     double gval, wval, *gcval; /* output each time (array length m) */
} dual_data;

62 63
static double sqr(double x) { return x * x; }

S
stevenj 已提交
64 65 66 67 68 69 70 71 72 73 74 75 76 77
static double dual_func(int m, const double *y, double *grad, void *d_)
{
     dual_data *d = (dual_data *) d_;
     int n = d->n;
     const double *x = d->x, *lb = d->lb, *ub = d->ub, *sigma = d->sigma, 
	  *dfdx = d->dfdx;
     const double *dfcdx = d->dfcdx;
     double rho = d->rho, fval = d->fval;
     const double *rhoc = d->rhoc, *fcval = d->fcval;
     double *xcur = d->xcur;
     double *gcval = d->gcval;
     int i, j;
     double val;

78 79
     d->count++;

S
stevenj 已提交
80 81
     val = d->gval = fval;
     d->wval = 0;
82 83
     for (i = 0; i < m; ++i) 
	  val += y[i] * (gcval[i] = isnan(fcval[i]) ? 0 : fcval[i]);
S
stevenj 已提交
84 85

     for (j = 0; j < n; ++j) {
86 87 88 89 90 91 92 93 94 95 96 97
	  double u, v, dx, denominv, c, sigma2, dx2;

	  /* first, compute xcur[j] for y.  Because this objective is
	     separable, we can minimize over x analytically, and the minimum
	     dx is given by the solution of a quadratic equation:
	             u dx^2 + 2 v sigma^2 dx + u sigma^2 = 0
	     where u and v are defined by the sums below.  Because of
	     the definitions, it is guaranteed that |u/v| <= sigma,
	     and it follows that the only dx solution with |dx| <= sigma
	     is given by:
	             (v/u) sigma^2 (-1 + sqrt(1 - (u / v sigma)^2))
             (which goes to zero as u -> 0). */
S
stevenj 已提交
98

99 100
	  u = dfdx[j];
	  v = fabs(dfdx[j]) * sigma[j] + 0.5 * rho;
101
	  for (i = 0; i < m; ++i) if (!isnan(fcval[i])) {
102 103
	       u += dfcdx[i*n + j] * y[i];
	       v += (fabs(dfcdx[i*n + j]) * sigma[j] + 0.5 * rhoc[i]) * y[i];
S
stevenj 已提交
104
	  }
105
	  u *= (sigma2 = sqr(sigma[j]));
106 107 108 109 110
	  if (fabs(u) < 1e-3 * (v*sigma[j])) { /* Taylor exp. for small u */
	       double a = u / (v*sigma[j]);
	       dx = -sigma[j] * (0.5 * a + 0.125 * a*a*a);
	  }
	  else
111
	       dx = (v/u)*sigma2 * (-1 + sqrt(fabs(1 - sqr(u/(v*sigma[j])))));
S
stevenj 已提交
112 113
	  xcur[j] = x[j] + dx;
	  if (xcur[j] > ub[j]) xcur[j] = ub[j];
114 115 116
	  else if (xcur[j] < lb[j]) xcur[j] = lb[j];
	  if (xcur[j] > x[j]+0.9*sigma[j]) xcur[j] = x[j]+0.9*sigma[j];
	  else if (xcur[j] < x[j]-0.9*sigma[j]) xcur[j] = x[j]-0.9*sigma[j];
S
stevenj 已提交
117 118 119
	  dx = xcur[j] - x[j];
	  
	  /* function value: */
120 121 122
	  dx2 = dx * dx;
	  denominv = 1.0 / (sigma2 - dx2);
	  val += (u * dx + v * dx2) * denominv;
S
stevenj 已提交
123

124 125 126
	  /* update gval, wval, gcval (approximant functions) */
	  c = sigma2 * dx;
	  d->gval += (dfdx[j] * c + (fabs(dfdx[j])*sigma[j] + 0.5*rho) * dx2)
S
stevenj 已提交
127
	       * denominv;
128
	  d->wval += 0.5 * dx2 * denominv;
129
	  for (i = 0; i < m; ++i) if (!isnan(fcval[i]))
130
	       gcval[i] += (dfcdx[i*n+j] * c + (fabs(dfcdx[i*n+j])*sigma[j] 
131
						+ 0.5*rhoc[i]) * dx2)
S
stevenj 已提交
132 133 134
		    * denominv;
     }

135 136 137 138
     /* gradient is easy to compute: since we are at a minimum x (dval/dx=0),
	we only need the partial derivative with respect to y, and
	we negate because we are maximizing: */
     if (grad) for (i = 0; i < m; ++i) grad[i] = -gcval[i];
S
stevenj 已提交
139 140 141 142 143
     return -val;
}

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

144 145 146 147
/* note that we implement a hidden feature not in the standard
   nlopt_minimize_constrained interface: whenever the constraint
   function returns NaN, that constraint becomes inactive. */

S
stevenj 已提交
148
nlopt_result mma_minimize(int n, nlopt_func f, void *f_data,
S
stevenj 已提交
149
			  int m, nlopt_func fc,
150
			  void *fc_data_, ptrdiff_t fc_datum_size,
S
stevenj 已提交
151 152 153
			  const double *lb, const double *ub, /* bounds */
			  double *x, /* in: initial guess, out: minimizer */
			  double *minf,
S
stevenj 已提交
154 155 156
			  nlopt_stopping *stop,
			  nlopt_algorithm dual_alg, 
			  double dual_tolrel, int dual_maxeval)
S
stevenj 已提交
157 158 159
{
     nlopt_result ret = NLOPT_SUCCESS;
     double *xcur, rho, *sigma, *dfdx, *dfdx_cur, *xprev, *xprevprev, fcur;
160 161
     double *dfcdx, *dfcdx_cur;
     double *fcval, *fcval_cur, *rhoc, *gcval, *y, *dual_lb, *dual_ub;
S
stevenj 已提交
162 163 164 165
     int i, j, k = 0;
     char *fc_data = (char *) fc_data_;
     dual_data dd;
     int feasible;
166
     double infeasibility;
S
stevenj 已提交
167
     
168
     sigma = (double *) malloc(sizeof(double) * (6*n + 2*m*n + m*7));
S
stevenj 已提交
169 170 171 172 173 174
     if (!sigma) return NLOPT_OUT_OF_MEMORY;
     dfdx = sigma + n;
     dfdx_cur = dfdx + n;
     xcur = dfdx_cur + n;
     xprev = xcur + n;
     xprevprev = xprev + n;
S
stevenj 已提交
175
     fcval = xprevprev + n;
176 177
     fcval_cur = fcval + m;
     rhoc = fcval_cur + m;
S
stevenj 已提交
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
     gcval = rhoc + m;
     dual_lb = gcval + m;
     dual_ub = dual_lb + m;
     y = dual_ub + m;
     dfcdx = y + m;
     dfcdx_cur = dfcdx + m*n;

     dd.n = n;
     dd.x = x;
     dd.lb = lb;
     dd.ub = ub;
     dd.sigma = sigma;
     dd.dfdx = dfdx;
     dd.dfcdx = dfcdx;
     dd.fcval = fcval;
     dd.rhoc = rhoc;
     dd.xcur = xcur;
     dd.gcval = gcval;

     for (j = 0; j < n; ++j) {
	  if (nlopt_isinf(ub[j]) || nlopt_isinf(lb[j]))
	       sigma[j] = 1.0; /* arbitrary default */
	  else
	       sigma[j] = 0.5 * (ub[j] - lb[j]);
     }
S
stevenj 已提交
203
     rho = 1.0;
S
stevenj 已提交
204 205 206 207 208
     for (i = 0; i < m; ++i) {
	  rhoc[i] = 1.0;
	  dual_lb[i] = y[i] = 0.0;
	  dual_ub[i] = HUGE_VAL;
     }
S
stevenj 已提交
209

S
stevenj 已提交
210
     dd.fval = fcur = *minf = f(n, x, dfdx, f_data);
S
stevenj 已提交
211
     stop->nevals++;
212 213
     memcpy(xcur, x, sizeof(double) * n);

214
     feasible = 1; infeasibility = 0;
S
stevenj 已提交
215
     for (i = 0; i < m; ++i) {
216
	  fcval[i] = fc(n, x, dfcdx + i*n, fc_data + fc_datum_size * i);
217
	  feasible = feasible && (fcval[i] <= 0 || isnan(fcval[i]));
218
	  if (fcval[i] > infeasibility) infeasibility = fcval[i];
S
stevenj 已提交
219
     }
220 221 222 223 224 225 226 227 228 229 230 231 232
     /* For non-feasible initial points, set a finite (large)
	upper-bound on the dual variables.  What this means is that,
	if no feasible solution is found from the dual problem, it
	will minimize the dual objective with the unfeasible
	constraint weighted by 1e40 -- basically, minimizing the
	unfeasible constraint until it becomes feasible or until we at
	least obtain a step towards a feasible point.
	
	Svanberg suggested a different approach in his 1987 paper, basically
	introducing additional penalty variables for unfeasible constraints,
	but this is easier to implement and at least as efficient. */
     if (!feasible)
	  for (i = 0; i < m; ++i) dual_ub[i] = 1e40;
S
stevenj 已提交
233

S
stevenj 已提交
234 235 236
     while (1) { /* outer iterations */
	  double fprev = fcur;
	  if (nlopt_stop_evals(stop)) ret = NLOPT_MAXEVAL_REACHED;
S
stevenj 已提交
237
	  else if (nlopt_stop_time(stop)) ret = NLOPT_MAXTIME_REACHED;
S
stevenj 已提交
238 239 240 241 242 243
	  else if (*minf < stop->minf_max) ret = NLOPT_MINF_MAX_REACHED;
	  if (ret != NLOPT_SUCCESS) goto done;
	  if (++k > 1) memcpy(xprevprev, xprev, sizeof(double) * n);
	  memcpy(xprev, xcur, sizeof(double) * n);

	  while (1) { /* inner iterations */
244
	       double min_dual, infeasibility_cur;
245
	       int feasible_cur, inner_done, save_verbose;
246
	       int new_infeasible_constraint;
247
	       nlopt_result reti;
S
stevenj 已提交
248 249

	       /* solve dual problem */
250
	       dd.rho = rho; dd.count = 0;
251
	  dual_solution:
252 253
	       save_verbose = mma_verbose;
	       mma_verbose = 0;
254 255 256 257 258
	       reti = nlopt_minimize(
		    dual_alg, m, dual_func, &dd,
		    dual_lb, dual_ub, y, &min_dual,
		    -HUGE_VAL, dual_tolrel,0., 0.,NULL, dual_maxeval,
		    stop->maxtime - (nlopt_seconds() - stop->start));
259
	       mma_verbose = save_verbose;
260 261 262 263 264 265 266 267 268 269
	       if (reti == NLOPT_FAILURE && dual_alg != NLOPT_LD_MMA) {
		    /* LBFGS etc. converge quickly but are sometimes
		       very finicky if there are any rounding errors in
		       the gradient, etcetera; if it fails, try again
		       with MMA called recursively for the dual */
		    dual_alg = NLOPT_LD_MMA;
		    if (mma_verbose)
			 printf("MMA: switching to recursive MMA for dual\n");
		    goto dual_solution;
	       }
270 271 272 273 274
	       if (reti < 0 || reti == NLOPT_MAXTIME_REACHED) {
		    ret = reti;
		    goto done;
	       }

S
stevenj 已提交
275
	       dual_func(m, y, NULL, &dd); /* evaluate final xcur etc. */
276 277 278
	       if (mma_verbose) {
		    printf("MMA dual converged in %d iterations to g=%g:\n",
			   dd.count, dd.gval);
279
		    for (i = 0; i < MIN(mma_verbose, m); ++i)
280 281 282
			 printf("    MMA y[%d]=%g, gc[%d]=%g\n",
				i, y[i], i, dd.gcval[i]);
	       }
S
stevenj 已提交
283 284 285

	       fcur = f(n, xcur, dfdx_cur, f_data);
	       stop->nevals++;
286
	       feasible_cur = 1; infeasibility_cur = 0;
287
	       new_infeasible_constraint = 0;
288
	       inner_done = dd.gval >= fcur;
S
stevenj 已提交
289 290
	       for (i = 0; i < m; ++i) {
		    fcval_cur[i] = fc(n, xcur, dfcdx_cur + i*n, 
291
				      fc_data + fc_datum_size * i);
292 293 294 295 296 297 298
		    if (!isnan(fcval_cur[i])) {
			 feasible_cur = feasible_cur && (fcval_cur[i] <= 0);
			 if (!isnan(fcval[i]))
			      inner_done = inner_done && 
				   (dd.gcval[i] >= fcval_cur[i]);
			 else if (fcval_cur[i] > 0)
			      new_infeasible_constraint = 1;
299 300
			 if (fcval_cur[i] > infeasibility_cur)
			      infeasibility_cur = fcval_cur[i];
301
		    }
S
stevenj 已提交
302 303
	       }

304
	       if ((fcur < *minf && (inner_done || feasible_cur || !feasible))
305
		    || (!feasible && infeasibility_cur < infeasibility)) {
306 307
		    if (mma_verbose && !feasible_cur)
			 printf("MMA - using infeasible point?\n");
S
stevenj 已提交
308
		    dd.fval = *minf = fcur;
309
		    infeasibility = infeasibility_cur;
S
stevenj 已提交
310
		    memcpy(fcval, fcval_cur, sizeof(double)*m);
S
stevenj 已提交
311 312
		    memcpy(x, xcur, sizeof(double)*n);
		    memcpy(dfdx, dfdx_cur, sizeof(double)*n);
S
stevenj 已提交
313
		    memcpy(dfcdx, dfcdx_cur, sizeof(double)*n*m);
314 315 316 317 318 319
		    
		    /* once we have reached a feasible solution, the
		       algorithm should never make the solution infeasible
		       again (if inner_done), although the constraints may
		       be violated slightly by rounding errors etc. so we
		       must be a little careful about checking feasibility */
320 321 322 323 324
		    if (feasible_cur) {
			 if (!feasible) /* reset upper bounds to infinity */
			      for (i = 0; i < m; ++i) dual_ub[i] = HUGE_VAL;
			 feasible = 1;
		    }
325 326
		    else if (new_infeasible_constraint) feasible = 0;

S
stevenj 已提交
327 328
	       }
	       if (nlopt_stop_evals(stop)) ret = NLOPT_MAXEVAL_REACHED;
S
stevenj 已提交
329
	       else if (nlopt_stop_time(stop)) ret = NLOPT_MAXTIME_REACHED;
S
stevenj 已提交
330 331 332
	       else if (*minf < stop->minf_max) ret = NLOPT_MINF_MAX_REACHED;
	       if (ret != NLOPT_SUCCESS) goto done;

S
stevenj 已提交
333 334 335 336 337
	       if (inner_done) break;

	       if (fcur > dd.gval)
		    rho = MIN(10*rho, 1.1 * (rho + (fcur-dd.gval) / dd.wval));
	       for (i = 0; i < m; ++i)
338
		    if (!isnan(fcval_cur[i]) && fcval_cur[i] > dd.gcval[i])
S
stevenj 已提交
339 340 341 342 343
			 rhoc[i] = 
			      MIN(10*rhoc[i], 
				  1.1 * (rhoc[i] + (fcval_cur[i]-dd.gcval[i]) 
					 / dd.wval));
	       
S
stevenj 已提交
344 345
	       if (mma_verbose)
		    printf("MMA inner iteration: rho -> %g\n", rho);
346
	       for (i = 0; i < MIN(mma_verbose, m); ++i)
S
stevenj 已提交
347
		    printf("                 MMA rhoc[%d] -> %g\n", i,rhoc[i]);
S
stevenj 已提交
348 349 350 351 352 353 354 355 356 357
	  }

	  if (nlopt_stop_ftol(stop, fcur, fprev))
	       ret = NLOPT_FTOL_REACHED;
	  if (nlopt_stop_x(stop, xcur, xprev))
	       ret = NLOPT_XTOL_REACHED;
	  if (ret != NLOPT_SUCCESS) goto done;
	       
	  /* update rho and sigma for iteration k+1 */
	  rho = MAX(0.1 * rho, MMA_RHOMIN);
S
stevenj 已提交
358 359
	  if (mma_verbose)
	       printf("MMA outer iteration: rho -> %g\n", rho);
S
stevenj 已提交
360 361
	  for (i = 0; i < m; ++i)
	       rhoc[i] = MAX(0.1 * rhoc[i], MMA_RHOMIN);
362
	  for (i = 0; i < MIN(mma_verbose, m); ++i)
S
stevenj 已提交
363
	       printf("                 MMA rhoc[%d] -> %g\n", i, rhoc[i]);
364
	  if (k > 1) {
S
stevenj 已提交
365 366 367 368
	       for (j = 0; j < n; ++j) {
		    double dx2 = (xcur[j]-xprev[j]) * (xprev[j]-xprevprev[j]);
		    double gam = dx2 < 0 ? 0.7 : (dx2 > 0 ? 1.2 : 1);
		    sigma[j] *= gam;
369 370 371 372
		    if (!nlopt_isinf(ub[j]) && !nlopt_isinf(lb[j])) {
			 sigma[j] = MIN(sigma[j], 10*(ub[j]-lb[j]));
			 sigma[j] = MAX(sigma[j], 0.01*(ub[j]-lb[j]));
		    }
S
stevenj 已提交
373
	       }
374
	       for (j = 0; j < MIN(mma_verbose, n); ++j)
S
stevenj 已提交
375
		    printf("                 MMA sigma[%d] -> %g\n", 
376 377
			   j, sigma[j]);
	  }
S
stevenj 已提交
378 379 380 381 382 383
     }

 done:
     free(sigma);
     return ret;
}