chiark / gitweb /
fix memory leak for error condition
[nlopt.git] / test / testopt.cpp
1 /* Copyright (c) 2007-2010 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 <stdio.h>
25 #include <math.h>
26 #include <string.h>
27
28 #include "config.h"
29
30 #ifdef HAVE_UNISTD_H
31 #  include <unistd.h>
32 #endif
33 #ifdef HAVE_GETOPT_H
34 #  include <getopt.h>
35 #endif
36
37 #define USE_FEENABLEEXCEPT 0
38 #if USE_FEENABLEEXCEPT
39 #  include <fenv.h>
40 extern "C" int feenableexcept (int EXCEPTS);
41 #endif
42
43
44 #include "nlopt.h"
45 #include "nlopt-util.h"
46 #include "testfuncs.h"
47
48 static nlopt_algorithm algorithm = NLOPT_GN_DIRECT_L;
49 static double ftol_rel = 0, ftol_abs = 0, xtol_rel = 0, xtol_abs = 0, minf_max_delta = -HUGE_VAL;
50 static int maxeval = 1000, iterations = 1, center_start = 0;
51 static double maxtime = 0.0;
52 static double xinit_tol = -1;
53 static int force_constraints = 0;
54
55 static void listalgs(FILE *f)
56 {
57   int i;
58   fprintf(f, "Available algorithms:\n");
59   for (i = 0; i < NLOPT_NUM_ALGORITHMS; ++i)
60     fprintf(f, "  %2d: %s\n", i, nlopt_algorithm_name((nlopt_algorithm) i));
61 }
62
63 static void listfuncs(FILE *f)
64 {
65   int i;
66   fprintf(f, "Available objective functions:\n");
67   for (i = 0; i < NTESTFUNCS; ++i)
68     fprintf(f, "  %2d: %s (%d dims)\n", i, testfuncs[i].name, testfuncs[i].n);
69 }
70
71 typedef struct {
72   const double *lb, *ub;
73   nlopt_func f;
74   void *f_data;
75 } bounds_wrap_data;
76
77 static double bounds_wrap_func(int n, const double *x, double *grad, void *d_)
78 {
79   bounds_wrap_data *d = (bounds_wrap_data *) d_;
80   int i;
81   double b = 0;
82   for (i = 0; i < n; ++i) {
83     if (x[i] < d->lb[i]) {
84       b = d->lb[i];
85       break; 
86     }
87     else if (x[i] > d->ub[i]) {
88       b = d->ub[i];
89       break;
90     }
91   }
92   if (i < n)
93     fprintf(stderr, "WARNING: bounds violated by x[%d] = %g = %g + %g\n",
94             i, x[i], b, x[i] - b);
95   return d->f(n, x, grad, d->f_data);
96 }
97
98 static int test_function(int ifunc)
99 {
100   testfunc func;
101   int i, iter;
102   double *x, minf, minf_max, f0, *xtabs, *lb, *ub;
103   nlopt_result ret;
104   double start = nlopt_seconds();
105   int total_count = 0, max_count = 0, min_count = 1<<30;
106   double total_err = 0, max_err = 0;
107   bounds_wrap_data bw;
108   
109   if (ifunc < 0 || ifunc >= NTESTFUNCS) {
110     fprintf(stderr, "testopt: invalid function %d\n", ifunc);
111     listfuncs(stderr);
112     return 0;
113   }
114   func = testfuncs[ifunc];
115   x = (double *) malloc(sizeof(double) * func.n * 5);
116   if (!x) { fprintf(stderr, "testopt: Out of memory!\n"); return 0; }
117
118   lb = x + func.n * 3;
119   ub = lb + func.n;
120   xtabs = x + func.n * 2;
121   bw.lb = lb;
122   bw.ub = ub;
123   bw.f = func.f;
124   bw.f_data = func.f_data;
125
126   for (i = 0; i < func.n; ++i) xtabs[i] = xtol_abs;
127   minf_max = minf_max_delta > (-HUGE_VAL) ? minf_max_delta + func.minf : (-HUGE_VAL);
128   
129   printf("-----------------------------------------------------------\n");
130   printf("Optimizing %s (%d dims) using %s algorithm\n",
131          func.name, func.n, nlopt_algorithm_name(algorithm));
132   printf("lower bounds at lb = [");
133   for (i = 0; i < func.n; ++i) printf(" %g", func.lb[i]);
134   printf("]\n");
135   printf("upper bounds at ub = [");
136   for (i = 0; i < func.n; ++i) printf(" %g", func.ub[i]);
137   printf("]\n");
138   memcpy(lb, func.lb, func.n * sizeof(double));
139   memcpy(ub, func.ub, func.n * sizeof(double));
140   if (force_constraints) {
141     for (i = 0; i < func.n; ++i) {
142       if (nlopt_iurand(2) == 0)
143         ub[i] = nlopt_urand(lb[i], func.xmin[i]);
144       else
145         lb[i] = nlopt_urand(func.xmin[i], ub[i]);
146     }
147     printf("adjusted lower bounds at lb = [");
148     for (i = 0; i < func.n; ++i) printf(" %g", lb[i]);
149     printf("]\n");
150     printf("adjusted upper bounds at ub = [");
151     for (i = 0; i < func.n; ++i) printf(" %g", ub[i]);
152     printf("]\n");
153   }
154
155   if (fabs(func.f(func.n, func.xmin, 0, func.f_data) - func.minf) > 1e-8) {
156     fprintf(stderr, "BUG: function does not achieve given lower bound!\n");
157     fprintf(stderr, "f(%g", func.xmin[0]);
158     for (i = 1; i < func.n; ++i) fprintf(stderr, ", %g", func.xmin[i]);
159     fprintf(stderr, ") = %0.16g instead of %0.16g, |diff| = %g\n", 
160             func.f(func.n, func.xmin, 0, func.f_data), func.minf,
161             fabs(func.f(func.n, func.xmin, 0, func.f_data) - func.minf));
162     return 0;
163   }
164
165   for (iter = 0; iter < iterations; ++iter) {
166     double val;
167     testfuncs_counter = 0;
168
169     printf("Starting guess x = [");
170     for (i = 0; i < func.n; ++i) {
171       if (center_start)
172         x[i] = (ub[i] + lb[i]) * 0.5;
173       else if (xinit_tol < 0) { /* random starting point near center of box */
174         double dx = (ub[i] - lb[i]) * 0.25;
175         double xm = 0.5 * (ub[i] + lb[i]);
176         x[i] = nlopt_urand(xm - dx, xm + dx);
177       }
178       else {
179         x[i] = nlopt_urand(-xinit_tol, xinit_tol)
180           + (1 + nlopt_urand(-xinit_tol, xinit_tol)) * func.xmin[i];
181         if (x[i] > ub[i]) x[i] = ub[i];
182         else if (x[i] < lb[i]) x[i] = lb[i];
183       }
184       printf(" %g", x[i]);
185     }
186     printf("]\n");
187     f0 = func.f(func.n, x, x + func.n, func.f_data);
188     printf("Starting function value = %g\n", f0);
189     
190     if (iter == 0 && testfuncs_verbose && func.has_gradient) {
191       printf("checking gradient:\n");
192       for (i = 0; i < func.n; ++i) {
193         double f;
194         x[i] *= 1 + 1e-6;
195         f = func.f(func.n, x, NULL, func.f_data);
196         x[i] /= 1 + 1e-6;
197         printf("  grad[%d] = %g vs. numerical derivative %g\n",
198                i, x[i + func.n], (f - f0) / (x[i] * 1e-6));
199       }
200     }
201     
202     ret = nlopt_minimize(algorithm,
203                          func.n, bounds_wrap_func, &bw,
204                          lb, ub,
205                          x, &minf,
206                          minf_max, ftol_rel, ftol_abs, xtol_rel, xtabs,
207                          maxeval, maxtime);
208     printf("finished after %g seconds.\n", nlopt_seconds() - start);
209     printf("return code %d from nlopt_minimize\n", ret);
210     if (ret < 0 && ret != NLOPT_ROUNDOFF_LIMITED) {
211       fprintf(stderr, "testopt: error in nlopt_minimize\n");
212       free(x);
213       return 0;
214     }
215     printf("Found minimum f = %g after %d evaluations.\n", 
216            minf, testfuncs_counter);
217     total_count += testfuncs_counter;
218     if (testfuncs_counter > max_count) max_count = testfuncs_counter;
219     if (testfuncs_counter < min_count) min_count = testfuncs_counter;
220     printf("Minimum at x = [");
221     for (i = 0; i < func.n; ++i) printf(" %g", x[i]);
222     printf("]\n");
223     if (func.minf == 0)
224       printf("|f - minf| = %g\n", fabs(minf - func.minf));
225     else
226       printf("|f - minf| = %g, |f - minf| / |minf| = %e\n",
227              fabs(minf - func.minf), fabs(minf - func.minf) / fabs(func.minf));
228     total_err += fabs(minf - func.minf);
229     if (fabs(minf - func.minf) > max_err)
230       max_err = fabs(minf - func.minf);
231     printf("vs. global minimum f = %g at x = [", func.minf);
232     for (i = 0; i < func.n; ++i) printf(" %g", func.xmin[i]);
233     printf("]\n");
234
235     val = func.f(func.n, x, NULL, func.f_data);
236     if (val != minf) {
237       fprintf(stderr, "Mismatch %g between returned minf=%g and f(x) = %g\n", 
238               minf - val, minf, val);
239       return 0;
240     }
241   }
242   if (iterations > 1)
243     printf("average #evaluations = %g (%d-%d)\naverage |f-minf| = %g, max |f-minf| = %g\n", total_count * 1.0 / iterations, min_count, max_count, total_err / iterations, max_err);
244
245   free(x);
246   return 1;
247 }
248
249 static void usage(FILE *f)
250 {
251   fprintf(f, "Usage: testopt [OPTIONS]\n"
252           "Options:\n"
253           "     -h : print this help\n"
254           "     -L : list available algorithms and objective functions\n"
255           "     -v : verbose mode\n"
256           " -a <n> : use optimization algorithm <n>\n"
257           " -o <n> : use objective function <n>\n"
258           " -0 <x> : starting guess within <x> + (1+<x>) * optimum\n"
259           "     -c : starting guess at center of cell\n"
260           "     -C : put optimum outside of bound constraints\n"
261           " -e <n> : use at most <n> evals (default: %d, 0 to disable)\n"
262           " -t <t> : use at most <t> seconds (default: disabled)\n"
263           " -x <t> : relative tolerance <t> on x (default: disabled)\n"
264           " -X <t> : absolute tolerance <t> on x (default: disabled)\n"
265           " -f <t> : relative tolerance <t> on f (default: disabled)\n"
266           " -F <t> : absolute tolerance <t> on f (default: disabled)\n"
267           " -m <m> : stop when minf+<m> is reached (default: disabled)\n"
268           " -i <n> : iterate optimization <n> times (default: 1)\n"
269           " -r <s> : use random seed <s> for starting guesses\n"
270           , maxeval);
271 }
272
273 int main(int argc, char **argv)
274 {
275   int c;
276   
277   nlopt_srand_time();
278   testfuncs_verbose = 0;
279   
280   if (argc <= 1)
281     usage(stdout);
282
283 #if USE_FEENABLEEXCEPT
284   feenableexcept(FE_INVALID);
285 #endif
286   
287   while ((c = getopt(argc, argv, "hLvCc0:r:a:o:i:e:t:x:X:f:F:m:")) != -1)
288     switch (c) {
289     case 'h':
290       usage(stdout);
291       return EXIT_SUCCESS;
292     case 'L':
293       listalgs(stdout);
294       listfuncs(stdout);
295       return EXIT_SUCCESS;
296     case 'v':
297       testfuncs_verbose = 1;
298       break;
299     case 'C':
300       force_constraints = 1;
301       break;
302     case 'r':
303       nlopt_srand((unsigned long) atoi(optarg));
304       break;
305     case 'a':
306       c = atoi(optarg);
307       if (c < 0 || c >= NLOPT_NUM_ALGORITHMS) {
308         fprintf(stderr, "testopt: invalid algorithm %d\n", c);
309         listalgs(stderr);
310         return EXIT_FAILURE;
311       }
312       algorithm = (nlopt_algorithm) c;
313       break;
314     case 'o':
315       if (!test_function(atoi(optarg)))
316         return EXIT_FAILURE;
317       break;
318     case 'e':
319       maxeval = atoi(optarg);
320       break;
321     case 'i':
322       iterations = atoi(optarg);
323       break;
324     case 't':
325       maxtime = atof(optarg);
326       break;
327     case 'x':
328       xtol_rel = atof(optarg);
329       break;
330     case 'X':
331       xtol_abs = atof(optarg);
332       break;
333     case 'f':
334       ftol_rel = atof(optarg);
335       break;
336     case 'F':
337       ftol_abs = atof(optarg);
338       break;
339     case 'm':
340       minf_max_delta = atof(optarg);
341       break;
342     case 'c':
343       center_start = 1;
344       break;
345     case '0':
346       center_start = 0;
347       xinit_tol = atof(optarg);
348       break;
349     default:
350       fprintf(stderr, "harminv: invalid argument -%c\n", c);
351       usage(stderr);
352       return EXIT_FAILURE;
353     }
354   
355   return EXIT_SUCCESS;
356 }