chiark / gitweb /
0ceffdf7b3ceb711afe8f1d4c33aec9f4da71936
[nlopt.git] / mma / mma.c
1 /* Copyright (c) 2007-2008 Massachusetts Institute of Technology
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  * 
11  * The above copyright notice and this permission notice shall be
12  * included in all copies or substantial portions of the Software.
13  * 
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
21  */
22
23 #include <stdlib.h>
24 #include <math.h>
25 #include <string.h>
26 #include <stdio.h>
27
28 #include "mma.h"
29
30 int mma_verbose = 0; /* > 0 for verbose output */
31
32 #define MIN(a,b) ((a) < (b) ? (a) : (b))
33 #define MAX(a,b) ((a) > (b) ? (a) : (b))
34
35 /* magic minimum value for rho in MMA ... the 2002 paper says it should
36    be a "fixed, strictly positive `small' number, e.g. 1e-5"
37    ... grrr, I hate these magic numbers, which seem like they
38    should depend on the objective function in some way ... in particular,
39    note that rho is dimensionful (= dimensions of objective function) */
40 #define MMA_RHOMIN 1e-5
41
42 /***********************************************************************/
43 /* function for MMA's dual solution of the approximate problem */
44
45 typedef struct {
46      int count; /* evaluation count, incremented each call */
47      int n; /* must be set on input to dimension of x */
48      const double *x, *lb, *ub, *sigma, *dfdx; /* arrays of length n */
49      const double *dfcdx; /* m-by-n array of fc gradients */
50      double fval, rho; /* must be set on input */
51      const double *fcval, *rhoc; /* arrays of length m */
52      double *xcur; /* array of length n, output each time */
53      double gval, wval, *gcval; /* output each time (array length m) */
54 } dual_data;
55
56 static double sqr(double x) { return x * x; }
57
58 static double dual_func(int m, const double *y, double *grad, void *d_)
59 {
60      dual_data *d = (dual_data *) d_;
61      int n = d->n;
62      const double *x = d->x, *lb = d->lb, *ub = d->ub, *sigma = d->sigma, 
63           *dfdx = d->dfdx;
64      const double *dfcdx = d->dfcdx;
65      double rho = d->rho, fval = d->fval;
66      const double *rhoc = d->rhoc, *fcval = d->fcval;
67      double *xcur = d->xcur;
68      double *gcval = d->gcval;
69      int i, j;
70      double val;
71
72      d->count++;
73
74      val = d->gval = fval;
75      d->wval = 0;
76      for (i = 0; i < m; ++i) val += y[i] * (gcval[i] = fcval[i]);
77
78      for (j = 0; j < n; ++j) {
79           double u, v, dx, denominv, c, sigma2, dx2;
80
81           /* first, compute xcur[j] for y.  Because this objective is
82              separable, we can minimize over x analytically, and the minimum
83              dx is given by the solution of a quadratic equation:
84                      u dx^2 + 2 v sigma^2 dx + u sigma^2 = 0
85              where u and v are defined by the sums below.  Because of
86              the definitions, it is guaranteed that |u/v| <= sigma,
87              and it follows that the only dx solution with |dx| <= sigma
88              is given by:
89                      (v/u) sigma^2 (-1 + sqrt(1 - (u / v sigma)^2))
90              (which goes to zero as u -> 0). */
91
92           u = dfdx[j];
93           v = fabs(dfdx[j]) * sigma[j] + 0.5 * rho;
94           for (i = 0; i < m; ++i) {
95                u += dfcdx[i*n + j] * y[i];
96                v += (fabs(dfcdx[i*n + j]) * sigma[j] + 0.5 * rhoc[i]) * y[i];
97           }
98           u *= (sigma2 = sqr(sigma[j]));
99           if (fabs(u) < 1e-3 * (v*sigma[j])) { /* Taylor exp. for small u */
100                double a = u / (v*sigma[j]);
101                dx = -sigma[j] * (0.5 * a + 0.125 * a*a*a);
102           }
103           else
104                dx = (v/u)*sigma2 * (-1 + sqrt(fabs(1 - sqr(u/(v*sigma[j])))));
105           xcur[j] = x[j] + dx;
106           if (xcur[j] > ub[j]) xcur[j] = ub[j];
107           else if (xcur[j] < lb[j]) xcur[j] = lb[j];
108           if (xcur[j] > x[j]+0.9*sigma[j]) xcur[j] = x[j]+0.9*sigma[j];
109           else if (xcur[j] < x[j]-0.9*sigma[j]) xcur[j] = x[j]-0.9*sigma[j];
110           dx = xcur[j] - x[j];
111           
112           /* function value: */
113           dx2 = dx * dx;
114           denominv = 1.0 / (sigma2 - dx2);
115           val += (u * dx + v * dx2) * denominv;
116
117           /* update gval, wval, gcval (approximant functions) */
118           c = sigma2 * dx;
119           d->gval += (dfdx[j] * c + (fabs(dfdx[j])*sigma[j] + 0.5*rho) * dx2)
120                * denominv;
121           d->wval += 0.5 * dx2 * denominv;
122           for (i = 0; i < m; ++i)
123                gcval[i] += (dfcdx[i*n+j] * c + (fabs(dfcdx[i*n+j])*sigma[j] 
124                                                 + 0.5*rhoc[i]) * dx2)
125                     * denominv;
126      }
127
128      /* gradient is easy to compute: since we are at a minimum x (dval/dx=0),
129         we only need the partial derivative with respect to y, and
130         we negate because we are maximizing: */
131      if (grad) for (i = 0; i < m; ++i) grad[i] = -gcval[i];
132      return -val;
133 }
134
135 /***********************************************************************/
136
137 nlopt_result mma_minimize(int n, nlopt_func f, void *f_data,
138                           int m, nlopt_func fc,
139                           void *fc_data_, ptrdiff_t fc_datum_size,
140                           const double *lb, const double *ub, /* bounds */
141                           double *x, /* in: initial guess, out: minimizer */
142                           double *minf,
143                           nlopt_stopping *stop,
144                           nlopt_algorithm dual_alg, 
145                           double dual_tolrel, int dual_maxeval)
146 {
147      nlopt_result ret = NLOPT_SUCCESS;
148      double *xcur, rho, *sigma, *dfdx, *dfdx_cur, *xprev, *xprevprev, fcur;
149      double *dfcdx, *dfcdx_cur;
150      double *fcval, *fcval_cur, *rhoc, *gcval, *y, *dual_lb, *dual_ub;
151      int i, j, k = 0;
152      char *fc_data = (char *) fc_data_;
153      dual_data dd;
154      int feasible;
155      
156      sigma = (double *) malloc(sizeof(double) * (6*n + 2*m*n + m*7));
157      if (!sigma) return NLOPT_OUT_OF_MEMORY;
158      dfdx = sigma + n;
159      dfdx_cur = dfdx + n;
160      xcur = dfdx_cur + n;
161      xprev = xcur + n;
162      xprevprev = xprev + n;
163      fcval = xprevprev + n;
164      fcval_cur = fcval + m;
165      rhoc = fcval_cur + m;
166      gcval = rhoc + m;
167      dual_lb = gcval + m;
168      dual_ub = dual_lb + m;
169      y = dual_ub + m;
170      dfcdx = y + m;
171      dfcdx_cur = dfcdx + m*n;
172
173      dd.n = n;
174      dd.x = x;
175      dd.lb = lb;
176      dd.ub = ub;
177      dd.sigma = sigma;
178      dd.dfdx = dfdx;
179      dd.dfcdx = dfcdx;
180      dd.fcval = fcval;
181      dd.rhoc = rhoc;
182      dd.xcur = xcur;
183      dd.gcval = gcval;
184
185      for (j = 0; j < n; ++j) {
186           if (nlopt_isinf(ub[j]) || nlopt_isinf(lb[j]))
187                sigma[j] = 1.0; /* arbitrary default */
188           else
189                sigma[j] = 0.5 * (ub[j] - lb[j]);
190      }
191      rho = 1.0;
192      for (i = 0; i < m; ++i) {
193           rhoc[i] = 1.0;
194           dual_lb[i] = y[i] = 0.0;
195           dual_ub[i] = HUGE_VAL;
196      }
197
198      dd.fval = fcur = *minf = f(n, x, dfdx, f_data);
199      stop->nevals++;
200      memcpy(xcur, x, sizeof(double) * n);
201
202      feasible = 1;
203      for (i = 0; i < m; ++i) {
204           fcval[i] = fc(n, x, dfcdx + i*n, fc_data + fc_datum_size * i);
205           feasible = feasible && (fcval[i] <= 0);
206      }
207      if (!feasible) { ret = NLOPT_FAILURE; goto done; } /* TODO: handle this */
208
209      while (1) { /* outer iterations */
210           double fprev = fcur;
211           if (nlopt_stop_evals(stop)) ret = NLOPT_MAXEVAL_REACHED;
212           else if (nlopt_stop_time(stop)) ret = NLOPT_MAXTIME_REACHED;
213           else if (*minf < stop->minf_max) ret = NLOPT_MINF_MAX_REACHED;
214           if (ret != NLOPT_SUCCESS) goto done;
215           if (++k > 1) memcpy(xprevprev, xprev, sizeof(double) * n);
216           memcpy(xprev, xcur, sizeof(double) * n);
217
218           while (1) { /* inner iterations */
219                double min_dual;
220                int feasible_cur, inner_done;
221                nlopt_result reti;
222
223                /* solve dual problem */
224                dd.rho = rho; dd.count = 0;
225           dual_solution:
226                reti = nlopt_minimize(
227                     dual_alg, m, dual_func, &dd,
228                     dual_lb, dual_ub, y, &min_dual,
229                     -HUGE_VAL, dual_tolrel,0., 0.,NULL, dual_maxeval,
230                     stop->maxtime - (nlopt_seconds() - stop->start));
231                if (reti == NLOPT_FAILURE && dual_alg != NLOPT_LD_MMA) {
232                     /* LBFGS etc. converge quickly but are sometimes
233                        very finicky if there are any rounding errors in
234                        the gradient, etcetera; if it fails, try again
235                        with MMA called recursively for the dual */
236                     dual_alg = NLOPT_LD_MMA;
237                     if (mma_verbose)
238                          printf("MMA: switching to recursive MMA for dual\n");
239                     goto dual_solution;
240                }
241                if (reti < 0 || reti == NLOPT_MAXTIME_REACHED) {
242                     ret = reti;
243                     goto done;
244                }
245
246                dual_func(m, y, NULL, &dd); /* evaluate final xcur etc. */
247                if (mma_verbose) {
248                     printf("MMA dual converged in %d iterations to g=%g:\n",
249                            dd.count, dd.gval);
250                     for (i = 0; i < MIN(mma_verbose, m); ++i)
251                          printf("    MMA y[%d]=%g, gc[%d]=%g\n",
252                                 i, y[i], i, dd.gcval[i]);
253                }
254
255                fcur = f(n, xcur, dfdx_cur, f_data);
256                stop->nevals++;
257                feasible_cur = 1;
258                inner_done = dd.gval >= fcur;
259                for (i = 0; i < m; ++i) {
260                     fcval_cur[i] = fc(n, xcur, dfcdx_cur + i*n, 
261                                       fc_data + fc_datum_size * i);
262                     feasible_cur = feasible_cur && (fcval_cur[i] <= 0);
263                     inner_done = inner_done && (dd.gcval[i] >= fcval_cur[i]);
264                }
265
266                /* once we have reached a feasible solution, the
267                   algorithm should never make the solution infeasible
268                   again (if inner_done), although the constraints may
269                   be violated slightly by rounding errors etc. so we
270                   must be a little careful about checking feasibility */
271                if (feasible_cur) feasible = 1;
272
273                if (fcur < *minf && (inner_done || feasible_cur || !feasible)) {
274                     if (mma_verbose && !feasible_cur)
275                          printf("MMA - using infeasible point?\n");
276                     dd.fval = *minf = fcur;
277                     memcpy(fcval, fcval_cur, sizeof(double)*m);
278                     memcpy(x, xcur, sizeof(double)*n);
279                     memcpy(dfdx, dfdx_cur, sizeof(double)*n);
280                     memcpy(dfcdx, dfcdx_cur, sizeof(double)*n*m);
281                }
282                if (nlopt_stop_evals(stop)) ret = NLOPT_MAXEVAL_REACHED;
283                else if (nlopt_stop_time(stop)) ret = NLOPT_MAXTIME_REACHED;
284                else if (*minf < stop->minf_max) ret = NLOPT_MINF_MAX_REACHED;
285                if (ret != NLOPT_SUCCESS) goto done;
286
287                if (inner_done) break;
288
289                if (fcur > dd.gval)
290                     rho = MIN(10*rho, 1.1 * (rho + (fcur-dd.gval) / dd.wval));
291                for (i = 0; i < m; ++i)
292                     if (fcval_cur[i] > dd.gcval[i])
293                          rhoc[i] = 
294                               MIN(10*rhoc[i], 
295                                   1.1 * (rhoc[i] + (fcval_cur[i]-dd.gcval[i]) 
296                                          / dd.wval));
297                
298                if (mma_verbose)
299                     printf("MMA inner iteration: rho -> %g\n", rho);
300                for (i = 0; i < MIN(mma_verbose, m); ++i)
301                     printf("                 MMA rhoc[%d] -> %g\n", i,rhoc[i]);
302           }
303
304           if (nlopt_stop_ftol(stop, fcur, fprev))
305                ret = NLOPT_FTOL_REACHED;
306           if (nlopt_stop_x(stop, xcur, xprev))
307                ret = NLOPT_XTOL_REACHED;
308           if (ret != NLOPT_SUCCESS) goto done;
309                
310           /* update rho and sigma for iteration k+1 */
311           rho = MAX(0.1 * rho, MMA_RHOMIN);
312           if (mma_verbose)
313                printf("MMA outer iteration: rho -> %g\n", rho);
314           for (i = 0; i < m; ++i)
315                rhoc[i] = MAX(0.1 * rhoc[i], MMA_RHOMIN);
316           for (i = 0; i < MIN(mma_verbose, m); ++i)
317                printf("                 MMA rhoc[%d] -> %g\n", i, rhoc[i]);
318           if (k > 1) {
319                for (j = 0; j < n; ++j) {
320                     double dx2 = (xcur[j]-xprev[j]) * (xprev[j]-xprevprev[j]);
321                     double gam = dx2 < 0 ? 0.7 : (dx2 > 0 ? 1.2 : 1);
322                     sigma[j] *= gam;
323                     if (!nlopt_isinf(ub[j]) && !nlopt_isinf(lb[j])) {
324                          sigma[j] = MIN(sigma[j], 10*(ub[j]-lb[j]));
325                          sigma[j] = MAX(sigma[j], 0.01*(ub[j]-lb[j]));
326                     }
327                }
328                for (j = 0; j < MIN(mma_verbose, n); ++j)
329                     printf("                 MMA sigma[%d] -> %g\n", 
330                            j, sigma[j]);
331           }
332      }
333
334  done:
335      free(sigma);
336      return ret;
337 }