chiark / gitweb /
octave4.4
[nlopt.git] / auglag / auglag.c
1 #include <stdlib.h>
2 #include <math.h>
3 #include <string.h>
4 #include <stdio.h>
5
6 #include "auglag.h"
7
8 int auglag_verbose = 0;
9
10 #define MIN(a,b) ((a) < (b) ? (a) : (b))
11 #define MAX(a,b) ((a) > (b) ? (a) : (b))
12
13 /***************************************************************************/
14
15 typedef struct {
16      nlopt_func f; void *f_data;
17      int m, mm; nlopt_constraint *fc;
18      int p, pp; nlopt_constraint *h;
19      double rho, *lambda, *mu;
20      double *restmp, *gradtmp;
21      nlopt_stopping *stop;
22 } auglag_data;
23
24 /* the augmented lagrangian objective function */
25 static double auglag(unsigned n, const double *x, double *grad, void *data)
26 {
27      auglag_data *d = (auglag_data *) data;
28      double *gradtmp = grad ? d->gradtmp : NULL;
29      double *restmp = d->restmp;
30      double rho = d->rho;
31      const double *lambda = d->lambda, *mu = d->mu;
32      double L;
33      int i, ii;
34      unsigned j, k;
35
36      L = d->f(n, x, grad, d->f_data);
37      d->stop->nevals++;
38      if (nlopt_stop_forced(d->stop)) return L;
39
40      for (ii = i = 0; i < d->p; ++i) {
41           nlopt_eval_constraint(restmp, gradtmp, d->h + i, n, x);
42           if (nlopt_stop_forced(d->stop)) return L;
43           for (k = 0; k < d->h[i].m; ++k) {
44                double h = restmp[k] + lambda[ii++] / rho;
45                L += 0.5 * rho * h*h;
46                if (grad) for (j = 0; j < n; ++j) 
47                               grad[j] += (rho * h) * gradtmp[k*n + j];
48           }
49      }
50
51      for (ii = i = 0; i < d->m; ++i) {
52           nlopt_eval_constraint(restmp, gradtmp, d->fc + i, n, x);
53           if (nlopt_stop_forced(d->stop)) return L;
54           for (k = 0; k < d->fc[i].m; ++k) {
55                double fc = restmp[k] + mu[ii++] / rho;
56                if (fc > 0) {
57                     L += 0.5 * rho * fc*fc;
58                     if (grad) for (j = 0; j < n; ++j) 
59                                    grad[j] += (rho * fc) * gradtmp[k*n + j];
60                }
61           }
62      }
63
64      return L;
65 }
66
67 /***************************************************************************/
68
69 nlopt_result auglag_minimize(int n, nlopt_func f, void *f_data,
70                              int m, nlopt_constraint *fc,
71                              int p, nlopt_constraint *h,
72                              const double *lb, const double *ub, /* bounds */
73                              double *x, /* in: initial guess, out: minimizer */
74                              double *minf,
75                              nlopt_stopping *stop,
76                              nlopt_opt sub_opt, int sub_has_fc)
77 {
78      auglag_data d;
79      nlopt_result ret = NLOPT_SUCCESS;
80      double ICM = HUGE_VAL, minf_penalty = HUGE_VAL, penalty;
81      double *xcur = NULL, fcur;
82      int i, ii, k, feasible, minf_feasible = 0;
83      int auglag_iters = 0;
84      int max_constraint_dim;
85
86      /* magic parameters from Birgin & Martinez */
87      const double tau = 0.5, gam = 10;
88      const double lam_min = -1e20, lam_max = 1e20, mu_max = 1e20;
89
90      d.f = f; d.f_data = f_data;
91      d.m = m; d.fc = fc;
92      d.p = p; d.h = h;
93      d.stop = stop;
94
95      /* whether we handle inequality constraints via the augmented
96         Lagrangian penalty function, or directly in the sub-algorithm */
97      if (sub_has_fc)
98           d.m = 0;
99      else
100           m = 0;
101
102      max_constraint_dim = MAX(nlopt_max_constraint_dim(d.m, fc),
103                               nlopt_max_constraint_dim(d.p, h));
104
105      d.mm = nlopt_count_constraints(d.m, fc);
106      d.pp = nlopt_count_constraints(d.p, h);
107
108      ret = nlopt_set_min_objective(sub_opt, auglag, &d); if (ret<0) return ret;
109      ret = nlopt_set_lower_bounds(sub_opt, lb); if (ret<0) return ret;
110      ret = nlopt_set_upper_bounds(sub_opt, ub); if (ret<0) return ret;
111      ret = nlopt_set_stopval(sub_opt, 
112                              d.m==0 && d.p==0 ? stop->minf_max : -HUGE_VAL);
113      if (ret<0) return ret;
114      ret = nlopt_remove_inequality_constraints(sub_opt); if (ret<0) return ret;
115      ret = nlopt_remove_equality_constraints(sub_opt); if (ret<0) return ret;
116      for (i = 0; i < m; ++i) {
117           if (fc[i].f)
118                ret = nlopt_add_inequality_constraint(sub_opt,
119                                                      fc[i].f, fc[i].f_data,
120                                                      fc[i].tol[0]);
121           else
122                ret = nlopt_add_inequality_mconstraint(sub_opt, fc[i].m, 
123                                                       fc[i].mf, fc[i].f_data,
124                                                       fc[i].tol);
125           if (ret < 0) return ret;
126      }
127
128      xcur = (double *) malloc(sizeof(double) * (n
129                                                 + max_constraint_dim * (1 + n)
130                                                 + d.pp + d.mm));
131      if (!xcur) return NLOPT_OUT_OF_MEMORY;
132      memcpy(xcur, x, sizeof(double) * n);
133
134      d.restmp = xcur + n;
135      d.gradtmp = d.restmp + max_constraint_dim;
136      memset(d.gradtmp, 0, sizeof(double) * (n*max_constraint_dim + d.pp+d.mm));
137      d.lambda = d.gradtmp + n * max_constraint_dim;
138      d.mu = d.lambda + d.pp;
139
140      *minf = HUGE_VAL;
141
142      /* starting rho suggested by B & M */
143      if (d.p > 0 || d.m > 0) {
144           double con2 = 0;
145           d.stop->nevals++;
146           fcur = f(n, xcur, NULL, f_data);
147           if (nlopt_stop_forced(stop)) {
148                ret = NLOPT_FORCED_STOP; goto done; }
149           penalty = 0;
150           feasible = 1;
151           for (i = 0; i < d.p; ++i) {
152                nlopt_eval_constraint(d.restmp, NULL, d.h + i, n, xcur);
153                if (nlopt_stop_forced(stop)) {
154                     ret = NLOPT_FORCED_STOP; goto done; }
155                for (k = 0; k < d.h[i].m; ++k) {
156                     double hi = d.restmp[k];
157                     penalty += fabs(hi);
158                     feasible = feasible && fabs(hi) <= h[i].tol[k];
159                     con2 += hi * hi;
160                }
161           }
162           for (i = 0; i < d.m; ++i) {
163                nlopt_eval_constraint(d.restmp, NULL, d.fc + i, n, xcur);
164                if (nlopt_stop_forced(stop)) {
165                     ret = NLOPT_FORCED_STOP; goto done; }
166                for (k = 0; k < d.fc[i].m; ++k) {
167                     double fci = d.restmp[k];
168                     penalty += fci > 0 ? fci : 0;
169                     feasible = feasible && fci <= fc[i].tol[k];
170                     if (fci > 0) con2 += fci * fci;
171                }
172           }
173           *minf = fcur;
174           minf_penalty = penalty;
175           minf_feasible = feasible;
176           d.rho = MAX(1e-6, MIN(10, 2 * fabs(*minf) / con2));
177      }
178      else
179           d.rho = 1; /* whatever, doesn't matter */
180
181      if (auglag_verbose) {
182           printf("auglag: initial rho=%g\nauglag initial lambda=", d.rho);
183           for (i = 0; i < d.pp; ++i) printf(" %g", d.lambda[i]);
184           printf("\nauglag initial mu = ");
185           for (i = 0; i < d.mm; ++i) printf(" %g", d.mu[i]);
186           printf("\n");
187      }
188
189      do {
190           double prev_ICM = ICM;
191           
192           ret = nlopt_optimize_limited(sub_opt, xcur, &fcur,
193                                        stop->maxeval - stop->nevals,
194                                        stop->maxtime - (nlopt_seconds() 
195                                                         - stop->start));
196           if (auglag_verbose)
197                printf("auglag: subopt return code %d\n", ret);
198           if (ret < 0) break;
199           
200           d.stop->nevals++;
201           fcur = f(n, xcur, NULL, f_data);
202           if (nlopt_stop_forced(stop)) {
203                ret = NLOPT_FORCED_STOP; goto done; }
204           if (auglag_verbose)
205                printf("auglag: fcur = %g\n", fcur);
206           
207           ICM = 0;
208           penalty = 0;
209           feasible = 1;
210           for (i = ii = 0; i < d.p; ++i) {
211                nlopt_eval_constraint(d.restmp, NULL, d.h + i, n, xcur);
212                if (nlopt_stop_forced(stop)) {
213                     ret = NLOPT_FORCED_STOP; goto done; }
214                for (k = 0; k < d.h[i].m; ++k) {
215                     double hi = d.restmp[k];
216                     double newlam = d.lambda[ii] + d.rho * hi;
217                     penalty += fabs(hi);
218                     feasible = feasible && fabs(hi) <= h[i].tol[k];
219                     ICM = MAX(ICM, fabs(hi));
220                     d.lambda[ii++] = MIN(MAX(lam_min, newlam), lam_max);
221                }
222           }
223           for (i = ii = 0; i < d.m; ++i) {
224                nlopt_eval_constraint(d.restmp, NULL, d.fc + i, n, xcur);
225                if (nlopt_stop_forced(stop)) {
226                     ret = NLOPT_FORCED_STOP; goto done; }
227                for (k = 0; k < d.fc[i].m; ++k) {
228                     double fci = d.restmp[k];
229                     double newmu = d.mu[ii] + d.rho * fci;
230                     penalty += fci > 0 ? fci : 0;
231                     feasible = feasible && fci <= fc[i].tol[k];
232                     ICM = MAX(ICM, fabs(MAX(fci, -d.mu[ii] / d.rho)));
233                     d.mu[ii++] = MIN(MAX(0.0, newmu), mu_max);
234                }
235           }
236           if (ICM > tau * prev_ICM) {
237                d.rho *= gam;
238           }
239
240           auglag_iters++;
241           
242           if (auglag_verbose) {
243                printf("auglag %d: ICM=%g (%sfeasible), rho=%g\nauglag lambda=",
244                       auglag_iters, ICM, feasible ? "" : "not ", d.rho);
245                for (i = 0; i < d.pp; ++i) printf(" %g", d.lambda[i]);
246                printf("\nauglag %d: mu = ", auglag_iters);
247                for (i = 0; i < d.mm; ++i) printf(" %g", d.mu[i]);
248                printf("\n");
249           }
250
251           if ((feasible && (!minf_feasible || penalty < minf_penalty
252                             || fcur < *minf)) || 
253               (!minf_feasible && penalty < minf_penalty)) {
254                ret = NLOPT_SUCCESS;
255                if (feasible) {
256                     if (fcur < stop->minf_max) 
257                          ret = NLOPT_MINF_MAX_REACHED;
258                     else if (nlopt_stop_ftol(stop, fcur, *minf)) 
259                          ret = NLOPT_FTOL_REACHED;
260                     else if (nlopt_stop_x(stop, xcur, x))
261                          ret = NLOPT_XTOL_REACHED;
262                }
263                *minf = fcur;
264                minf_penalty = penalty;
265                minf_feasible = feasible;
266                memcpy(x, xcur, sizeof(double) * n);
267                if (ret != NLOPT_SUCCESS) break;
268           }
269
270           if (nlopt_stop_forced(stop)) {ret = NLOPT_FORCED_STOP; break;}
271           if (nlopt_stop_evals(stop)) {ret = NLOPT_MAXEVAL_REACHED; break;}
272           if (nlopt_stop_time(stop)) {ret = NLOPT_MAXTIME_REACHED; break;}
273
274           /* TODO: use some other stopping criterion on ICM? */
275           /* The paper uses ICM <= epsilon and DFM <= epsilon, where
276              DFM is a measure of the size of the Lagrangian gradient.
277              Besides the fact that these kinds of absolute tolerances
278              (non-scale-invariant) are unsatisfying and it is not
279              clear how the user should specify it, the ICM <= epsilon
280              condition seems not too different from requiring feasibility,
281              especially now that the user can provide constraint-specific
282              tolerances analogous to epsilon. */
283           if (ICM == 0) return NLOPT_FTOL_REACHED;
284      } while (1);
285
286 done:
287      free(xcur);
288      return ret;
289 }