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