chiark / gitweb /
more verbose output for auglag
[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 = 1;
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; nlopt_constraint *fc;
18      int p; nlopt_constraint *h;
19      double rho, *lambda, *mu;
20      double *gradtmp;
21      nlopt_stopping *stop;
22 } auglag_data;
23
24 /* the augmented lagrangian objective function */
25 static double auglag(int 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 rho = d->rho;
30      const double *lambda = d->lambda, *mu = d->mu;
31      double L;
32      int i, j;
33
34      L = d->f(n, x, grad, d->f_data);
35
36      for (i = 0; i < d->p; ++i) {
37           double h;
38           h = d->h[i].f(n, x, gradtmp, d->h[i].f_data) + lambda[i] / rho;
39           L += 0.5 * rho * h*h;
40           if (grad) for (j = 0; j < n; ++j) grad[j] += (rho * h) * gradtmp[j];
41      }
42
43      for (i = 0; i < d->m; ++i) {
44           double fc;
45           fc = d->fc[i].f(n, x, gradtmp, d->fc[i].f_data) + mu[i] / rho;
46           if (fc > 0) {
47                L += 0.5 * rho * fc*fc;
48                if (grad) for (j = 0; j < n; ++j) 
49                     grad[j] += (rho * fc) * gradtmp[j];
50           }
51      }
52      
53      d->stop->nevals++;
54
55      return L;
56 }
57
58 /***************************************************************************/
59
60 nlopt_result auglag_minimize(int n, nlopt_func f, void *f_data,
61                              int m, nlopt_constraint *fc,
62                              int p, nlopt_constraint *h,
63                              const double *lb, const double *ub, /* bounds */
64                              double *x, /* in: initial guess, out: minimizer */
65                              double *minf,
66                              nlopt_stopping *stop,
67                              nlopt_opt sub_opt, int sub_has_fc)
68 {
69      auglag_data d;
70      nlopt_result ret = NLOPT_SUCCESS;
71      double ICM = HUGE_VAL, minf_penalty = HUGE_VAL, penalty;
72      double *xcur = NULL, fcur;
73      int i, feasible, minf_feasible = 0;
74
75      /* magic parameters from Birgin & Martinez */
76      const double tau = 0.5, gam = 10;
77      const double lam_min = -1e20, lam_max = 1e20, mu_max = 1e20;
78
79      d.f = f; d.f_data = f_data;
80      d.m = m; d.fc = fc;
81      d.p = p; d.h = h;
82      d.stop = stop;
83
84      /* whether we handle inequality constraints via the augmented
85         Lagrangian penalty function, or directly in the sub-algorithm */
86      if (sub_has_fc)
87           d.m = 0;
88      else
89           m = 0;
90
91      ret = nlopt_set_min_objective(sub_opt, auglag, &d); if (ret<0) return ret;
92      ret = nlopt_set_lower_bounds(sub_opt, lb); if (ret<0) return ret;
93      ret = nlopt_set_upper_bounds(sub_opt, ub); if (ret<0) return ret;
94      ret = nlopt_remove_inequality_constraints(sub_opt); if (ret<0) return ret;
95      ret = nlopt_remove_equality_constraints(sub_opt); if (ret<0) return ret;
96      for (i = 0; i < m; ++i) {
97           ret = nlopt_add_inequality_constraint(sub_opt, fc[i].f, fc[i].f_data,
98                                                 fc[i].tol);
99           if (ret < 0) return ret;
100      }
101
102      xcur = (double *) malloc(sizeof(double) * (2*n + d.p + d.m));
103      if (!xcur) return NLOPT_OUT_OF_MEMORY;
104      memcpy(xcur, x, sizeof(double) * n);
105
106      d.gradtmp = xcur + n;
107      memset(d.gradtmp, 0, sizeof(double) * (n + d.p + d.m));
108      d.lambda = d.gradtmp + n;
109      d.mu = d.lambda + d.p;
110
111      *minf = HUGE_VAL;
112
113      /* starting rho suggested by B & M */
114      if (d.p > 0 || d.m > 0) {
115           double con2 = 0;
116           d.stop->nevals++;
117           fcur = f(n, xcur, NULL, f_data);
118           penalty = 0;
119           feasible = 1;
120           for (i = 0; i < d.p; ++i) {
121                double hi = h[i].f(n, xcur, NULL, d.h[i].f_data);
122                penalty += fabs(hi);
123                feasible = feasible && fabs(hi) <= h[i].tol;
124                con2 += hi * hi;
125           }
126           for (i = 0; i < d.m; ++i) {
127                double fci = fc[i].f(n, xcur, NULL, d.fc[i].f_data);
128                penalty += fci > 0 ? fci : 0;
129                feasible = feasible && fci <= fc[i].tol;
130                if (fci > 0) con2 += fci * fci;
131           }
132           *minf = fcur;
133           minf_penalty = penalty;
134           minf_feasible = feasible;
135           d.rho = MAX(1e-6, MIN(10, 2 * fabs(*minf) / con2));
136      }
137      else
138           d.rho = 1; /* whatever, doesn't matter */
139
140      if (auglag_verbose) {
141           printf("auglag: initial rho=%g\nauglag initial lambda=", d.rho);
142           for (i = 0; i < d.p; ++i) printf(" %g", d.lambda[i]);
143           printf("\nauglag initial mu = ");
144           for (i = 0; i < d.m; ++i) printf(" %g", d.mu[i]);
145           printf("\n");
146      }
147
148      do {
149           double prev_ICM = ICM;
150           
151           ret = nlopt_optimize_limited(sub_opt, xcur, &fcur,
152                                        stop->maxeval - stop->nevals,
153                                        stop->maxtime - (nlopt_seconds() 
154                                                         - stop->start));
155           if (ret < 0) break;
156           
157           d.stop->nevals++;
158           fcur = f(n, xcur, NULL, f_data);
159           
160           ICM = 0;
161           penalty = 0;
162           feasible = 1;
163           for (i = 0; i < d.p; ++i) {
164                double hi = h[i].f(n, xcur, NULL, d.h[i].f_data);
165                double newlam = d.lambda[i] + d.rho * hi;
166                penalty += fabs(hi);
167                feasible = feasible && fabs(hi) <= h[i].tol;
168                ICM = MAX(ICM, fabs(hi));
169                d.lambda[i] = MIN(MAX(lam_min, newlam), lam_max);
170           }
171           for (i = 0; i < d.m; ++i) {
172                double fci = fc[i].f(n, xcur, NULL, d.fc[i].f_data);
173                double newmu = d.mu[i] + d.rho * fci;
174                penalty += fci > 0 ? fci : 0;
175                feasible = feasible && fci <= fc[i].tol;
176                ICM = MAX(ICM, fabs(MAX(fci, -d.mu[i] / d.rho)));
177                d.mu[i] = MIN(MAX(0.0, newmu), mu_max);
178           }
179           if (ICM > tau * prev_ICM) {
180                d.rho *= gam;
181           }
182           
183           if (auglag_verbose) {
184                printf("auglag: ICM=%g, rho=%g\nauglag lambda=", ICM, d.rho);
185                for (i = 0; i < d.p; ++i) printf(" %g", d.lambda[i]);
186                printf("\nauglag mu = ");
187                for (i = 0; i < d.m; ++i) printf(" %g", d.mu[i]);
188                printf("\n");
189           }
190
191           if ((feasible && (!minf_feasible || penalty < minf_penalty
192                             || fcur <= *minf)) || 
193               (!minf_feasible && penalty <= minf_penalty)) {
194                ret = NLOPT_SUCCESS;
195                if (feasible) {
196                     if (fcur < stop->minf_max) 
197                          ret = NLOPT_MINF_MAX_REACHED;
198                     else if (nlopt_stop_ftol(stop, fcur, *minf)) 
199                          ret = NLOPT_FTOL_REACHED;
200                     else if (nlopt_stop_x(stop, xcur, x))
201                          ret = NLOPT_XTOL_REACHED;
202                }
203                else { /* check if no progress towards feasibility */
204                     if (nlopt_stop_ftol(stop, fcur, *minf)
205                         && nlopt_stop_ftol(stop, penalty, minf_penalty))
206                          ret = NLOPT_FTOL_REACHED;
207                     else if (nlopt_stop_x(stop, xcur, x))
208                          ret = NLOPT_XTOL_REACHED;
209                }
210                *minf = fcur;
211                minf_penalty = penalty;
212                minf_feasible = feasible;
213                memcpy(x, xcur, sizeof(double) * n);
214                if (ret != NLOPT_SUCCESS) break;
215           }
216
217           if (nlopt_stop_evals(stop)) {ret = NLOPT_MAXEVAL_REACHED; break;}
218           if (nlopt_stop_time(stop)) {ret = NLOPT_MAXTIME_REACHED; break;}
219
220           /* TODO: use some other stopping criterion on ICM? */
221           /* The paper uses ICM <= epsilon and DFM <= epsilon, where
222              DFM is a measure of the size of the Lagrangian gradient.
223              Besides the fact that these kinds of absolute tolerances
224              (non-scale-invariant) are unsatisfying and it is not
225              clear how the user should specify it, the ICM <= epsilon
226              condition seems not too different from requiring feasibility. */
227           if (ICM == 0) return NLOPT_FTOL_REACHED;
228      } while (1);
229
230      free(xcur);
231      return ret;
232 }