chiark / gitweb /
c3ae1b46dc3fd3572e63900fc432caf7acc68840
[nlopt.git] / api / options.c
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 <stdio.h>
24 #include <stdlib.h>
25 #include <math.h>
26 #include <string.h>
27 #include <float.h>
28
29 #include "nlopt-internal.h"
30
31 /*************************************************************************/
32
33 void NLOPT_STDCALL nlopt_destroy(nlopt_opt opt)
34 {
35      if (opt) {
36           if (opt->munge_on_destroy) {
37                nlopt_munge munge = opt->munge_on_destroy;
38                unsigned i;
39                munge(opt->f_data);
40                for (i = 0; i < opt->m; ++i)
41                     munge(opt->fc[i].f_data);
42                for (i = 0; i < opt->p; ++i)
43                     munge(opt->h[i].f_data);
44           }
45           free(opt->lb); free(opt->ub);
46           free(opt->xtol_abs);
47           free(opt->fc);
48           free(opt->h);
49           nlopt_destroy(opt->local_opt);
50           free(opt->dx);
51           free(opt);
52      }
53 }
54
55 nlopt_opt NLOPT_STDCALL nlopt_create(nlopt_algorithm algorithm, unsigned n)
56 {
57      nlopt_opt opt;
58
59      if (((int) algorithm) < 0 || algorithm >= NLOPT_NUM_ALGORITHMS)
60           return NULL;
61
62      opt = (nlopt_opt) malloc(sizeof(struct nlopt_opt_s));
63      if (opt) {
64           opt->algorithm = algorithm;
65           opt->n = n;
66           opt->f = NULL; opt->f_data = NULL;
67           opt->maximize = 0;
68           opt->munge_on_destroy = opt->munge_on_copy = NULL;
69
70           opt->lb = opt->ub = NULL;
71           opt->m = opt->m_alloc = 0;
72           opt->fc = NULL;
73           opt->p = opt->p_alloc = 0;
74           opt->h = NULL;
75
76           opt->stopval = -HUGE_VAL;
77           opt->ftol_rel = opt->ftol_abs = 0;
78           opt->xtol_rel = 0; opt->xtol_abs = NULL;
79           opt->maxeval = 0;
80           opt->maxtime = 0;
81           opt->force_stop = 0;
82           opt->force_stop_child = NULL;
83
84           opt->local_opt = NULL;
85           opt->stochastic_population = 0;
86           opt->dx = NULL;
87
88           if (n > 0) {
89                opt->lb = (double *) malloc(sizeof(double) * (n));
90                if (!opt->lb) goto oom;
91                opt->ub = (double *) malloc(sizeof(double) * (n));
92                if (!opt->ub) goto oom;
93                opt->xtol_abs = (double *) malloc(sizeof(double) * (n));
94                if (!opt->xtol_abs) goto oom;
95                nlopt_set_lower_bounds1(opt, -HUGE_VAL);
96                nlopt_set_upper_bounds1(opt, +HUGE_VAL);
97                nlopt_set_xtol_abs1(opt, 0.0);
98           }
99      }
100
101      return opt;
102
103 oom:
104      nlopt_destroy(opt);
105      return NULL;
106 }
107
108 nlopt_opt NLOPT_STDCALL nlopt_copy(const nlopt_opt opt)
109 {
110      nlopt_opt nopt = NULL;
111      if (opt) {
112           nopt = (nlopt_opt) malloc(sizeof(struct nlopt_opt_s));
113           *nopt = *opt;
114           nopt->lb = nopt->ub = nopt->xtol_abs = NULL;
115           nopt->fc = nopt->h = NULL;
116           nopt->m_alloc = nopt->p_alloc = 0;
117           nopt->local_opt = NULL;
118           nopt->dx = NULL;
119           opt->force_stop_child = NULL;
120
121           nlopt_munge munge = nopt->munge_on_copy;
122           if (munge && nopt->f_data)
123                if (!(nopt->f_data = munge(nopt->f_data))) goto oom;
124
125           if (opt->n > 0) {
126                nopt->lb = (double *) malloc(sizeof(double) * (opt->n));
127                if (!opt->lb) goto oom;
128                nopt->ub = (double *) malloc(sizeof(double) * (opt->n));
129                if (!opt->ub) goto oom;
130                nopt->xtol_abs = (double *) malloc(sizeof(double) * (opt->n));
131                if (!opt->xtol_abs) goto oom;
132                
133                memcpy(nopt->lb, opt->lb, sizeof(double) * (opt->n));
134                memcpy(nopt->ub, opt->ub, sizeof(double) * (opt->n));
135                memcpy(nopt->xtol_abs, opt->xtol_abs, sizeof(double) * (opt->n));
136           }
137
138           if (opt->m) {
139                nopt->m_alloc = opt->m;
140                nopt->fc = (nlopt_constraint *) malloc(sizeof(nlopt_constraint)
141                                                       * (opt->m));
142                if (!nopt->fc) goto oom;
143                memcpy(nopt->fc, opt->fc, sizeof(nlopt_constraint) * (opt->m));
144                if (munge)
145                     for (unsigned i = 0; i < opt->m; ++i)
146                          if (nopt->fc[i].f_data &&
147                              !(nopt->fc[i].f_data
148                                = munge(nopt->fc[i].f_data)))
149                               goto oom;
150           }
151
152           if (opt->p) {
153                nopt->p_alloc = opt->p;
154                nopt->h = (nlopt_constraint *) malloc(sizeof(nlopt_constraint)
155                                                      * (opt->p));
156                if (!nopt->h) goto oom;
157                memcpy(nopt->h, opt->h, sizeof(nlopt_constraint) * (opt->p));
158                if (munge)
159                     for (unsigned i = 0; i < opt->p; ++i)
160                          if (nopt->h[i].f_data &&
161                              !(nopt->h[i].f_data
162                                = munge(nopt->h[i].f_data)))
163                               goto oom;
164           }
165
166           if (opt->local_opt) {
167                nopt->local_opt = nlopt_copy(opt->local_opt);
168                if (!nopt->local_opt) goto oom;
169           }
170
171           if (opt->dx) {
172                nopt->dx = (double *) malloc(sizeof(double) * (opt->n));
173                if (!nopt->dx) goto oom;
174                memcpy(nopt->dx, opt->dx, sizeof(double) * (opt->n));
175           }
176      }
177      return nopt;
178
179 oom:
180      nopt->munge_on_destroy = NULL; // better to leak mem than crash
181      nlopt_destroy(nopt);
182      return NULL;
183 }
184
185 /*************************************************************************/
186
187 nlopt_result NLOPT_STDCALL nlopt_set_min_objective(nlopt_opt opt,
188                                                    nlopt_func f, void *f_data)
189 {
190      if (opt) {
191           opt->f = f; opt->f_data = f_data;
192           opt->maximize = 0;
193           if (nlopt_isinf(opt->stopval) && opt->stopval > 0)
194                opt->stopval = -HUGE_VAL; /* switch default from max to min */
195           return NLOPT_SUCCESS;
196      }
197      return NLOPT_INVALID_ARGS;
198 }
199
200 nlopt_result NLOPT_STDCALL nlopt_set_max_objective(nlopt_opt opt, 
201                                                    nlopt_func f, void *f_data)
202 {
203      if (opt) {
204           opt->f = f; opt->f_data = f_data;
205           opt->maximize = 1;
206           if (nlopt_isinf(opt->stopval) && opt->stopval < 0)
207                opt->stopval = +HUGE_VAL; /* switch default from min to max */
208           return NLOPT_SUCCESS;
209      }
210      return NLOPT_INVALID_ARGS;
211 }
212
213 /*************************************************************************/
214
215 nlopt_result
216 NLOPT_STDCALL nlopt_set_lower_bounds(nlopt_opt opt, const double *lb)
217 {
218      if (opt && (opt->n == 0 || lb)) {
219           memcpy(opt->lb, lb, sizeof(double) * (opt->n));
220           return NLOPT_SUCCESS;
221      }
222      return NLOPT_INVALID_ARGS;
223 }
224
225 nlopt_result
226 NLOPT_STDCALL nlopt_set_lower_bounds1(nlopt_opt opt, double lb)
227 {
228      if (opt) {
229           unsigned i;
230           for (i = 0; i < opt->n; ++i)
231                opt->lb[i] = lb;
232           return NLOPT_SUCCESS;
233      }
234      return NLOPT_INVALID_ARGS;
235 }
236
237 nlopt_result
238 NLOPT_STDCALL nlopt_get_lower_bounds(nlopt_opt opt, double *lb)
239 {
240      if (opt && (opt->n == 0 || lb)) {
241           memcpy(lb, opt->lb, sizeof(double) * (opt->n));
242           return NLOPT_SUCCESS;
243      }
244      return NLOPT_INVALID_ARGS;
245 }
246
247 nlopt_result
248 NLOPT_STDCALL nlopt_set_upper_bounds(nlopt_opt opt, const double *ub)
249 {
250      if (opt && (opt->n == 0 || ub)) {
251           memcpy(opt->ub, ub, sizeof(double) * (opt->n));
252           return NLOPT_SUCCESS;
253      }
254      return NLOPT_INVALID_ARGS;
255 }
256
257 nlopt_result
258 NLOPT_STDCALL nlopt_set_upper_bounds1(nlopt_opt opt, double ub)
259 {
260      if (opt) {
261           unsigned i;
262           for (i = 0; i < opt->n; ++i)
263                opt->ub[i] = ub;
264           return NLOPT_SUCCESS;
265      }
266      return NLOPT_INVALID_ARGS;
267 }
268
269 nlopt_result
270 NLOPT_STDCALL nlopt_get_upper_bounds(nlopt_opt opt, double *ub)
271 {
272      if (opt && (opt->n == 0 || ub)) {
273           memcpy(ub, opt->ub, sizeof(double) * (opt->n));
274           return NLOPT_SUCCESS;
275      }
276      return NLOPT_INVALID_ARGS;
277 }
278
279 /*************************************************************************/
280
281 #define AUGLAG_ALG(a) ((a) == NLOPT_AUGLAG ||           \
282                        (a) == NLOPT_AUGLAG_EQ ||        \
283                        (a) == NLOPT_LN_AUGLAG ||        \
284                        (a) == NLOPT_LN_AUGLAG_EQ ||     \
285                        (a) == NLOPT_LD_AUGLAG ||        \
286                        (a) == NLOPT_LD_AUGLAG_EQ)
287
288 nlopt_result
289 NLOPT_STDCALL nlopt_remove_inequality_constraints(nlopt_opt opt)
290 {
291      if (!opt) return NLOPT_INVALID_ARGS;
292      if (opt->munge_on_destroy) {
293           nlopt_munge munge = opt->munge_on_destroy;
294           for (unsigned i = 0; i < opt->m; ++i)
295                munge(opt->fc[i].f_data);
296      }
297      free(opt->fc);
298      opt->fc = NULL;
299      opt->m = opt->m_alloc = 0;
300      return NLOPT_SUCCESS;
301 }
302
303 static nlopt_result add_constraint(unsigned *m, unsigned *m_alloc,
304                                    nlopt_constraint **c,
305                                    nlopt_func fc, void *fc_data,
306                                    double tol)
307 {
308      *m += 1;
309      if (*m > *m_alloc) {
310           /* allocate by repeated doubling so that 
311              we end up with O(log m) mallocs rather than O(m). */
312           *m_alloc = 2 * (*m);
313           *c = (nlopt_constraint *) realloc(*c,
314                                             sizeof(nlopt_constraint)
315                                             * (*m_alloc));
316           if (!*c) {
317                *m_alloc = *m = 0;
318                return NLOPT_OUT_OF_MEMORY;
319           }
320      }
321      
322      (*c)[*m - 1].f = fc;
323      (*c)[*m - 1].f_data = fc_data;
324      (*c)[*m - 1].tol = tol;
325      return NLOPT_SUCCESS;
326 }
327
328 nlopt_result
329 NLOPT_STDCALL nlopt_add_inequality_constraint(nlopt_opt opt,
330                                               nlopt_func fc, void *fc_data,
331                                               double tol)
332 {
333      if (opt && fc && tol >= 0) {
334           /* nonlinear constraints are only supported with some algorithms */
335           if (opt->algorithm != NLOPT_LD_MMA 
336               && opt->algorithm != NLOPT_LN_COBYLA
337               && !AUGLAG_ALG(opt->algorithm) 
338               && opt->algorithm != NLOPT_GN_ISRES
339               && opt->algorithm != NLOPT_GN_ORIG_DIRECT
340               && opt->algorithm != NLOPT_GN_ORIG_DIRECT_L)
341                return NLOPT_INVALID_ARGS;
342           return add_constraint(&opt->m, &opt->m_alloc, &opt->fc,
343                                 fc, fc_data, tol);
344      }
345      return NLOPT_INVALID_ARGS;
346 }
347
348 nlopt_result
349 NLOPT_STDCALL nlopt_remove_equality_constraints(nlopt_opt opt)
350 {
351      if (!opt) return NLOPT_INVALID_ARGS;
352      if (opt->munge_on_destroy) {
353           nlopt_munge munge = opt->munge_on_destroy;
354           for (unsigned i = 0; i < opt->p; ++i)
355                munge(opt->h[i].f_data);
356      }
357      free(opt->h);
358      opt->h = NULL;
359      opt->p = opt->p_alloc = 0;
360      return NLOPT_SUCCESS;
361 }
362
363 nlopt_result
364 NLOPT_STDCALL nlopt_add_equality_constraint(nlopt_opt opt,
365                                             nlopt_func h, void *h_data,
366                                             double tol)
367 {
368      if (opt && h && tol >= 0) {
369           /* equality constraints (h(x) = 0) only via some algorithms */
370           if (!AUGLAG_ALG(opt->algorithm) && opt->algorithm != NLOPT_GN_ISRES)
371                return NLOPT_INVALID_ARGS;
372           return add_constraint(&opt->p, &opt->p_alloc, &opt->h,
373                                 h, h_data, tol);
374      }
375      return NLOPT_INVALID_ARGS;
376 }
377
378 /*************************************************************************/
379
380 #define SET(param, T, arg)                                              \
381    nlopt_result NLOPT_STDCALL nlopt_set_##param(nlopt_opt opt, T arg)   \
382    {                                                                    \
383         if (opt) {                                                      \
384              opt->arg = arg;                                            \
385              return NLOPT_SUCCESS;                                      \
386         }                                                               \
387         return NLOPT_INVALID_ARGS;                                      \
388    }
389
390
391 #define GET(param, T, arg) T NLOPT_STDCALL      \
392    nlopt_get_##param(const nlopt_opt opt) {     \
393         return opt->arg;                        \
394    }
395
396 #define GETSET(param, T, arg) GET(param, T, arg) SET(param, T, arg)
397
398 GETSET(stopval, double, stopval)
399
400 GETSET(ftol_rel, double, ftol_rel)
401 GETSET(ftol_abs, double, ftol_abs)
402 GETSET(xtol_rel, double, xtol_rel)
403
404 nlopt_result
405 NLOPT_STDCALL nlopt_set_xtol_abs(nlopt_opt opt, const double *xtol_abs)
406 {
407      if (opt) {
408           memcpy(opt->xtol_abs, xtol_abs, opt->n & sizeof(double));
409           return NLOPT_SUCCESS;
410      }
411      return NLOPT_INVALID_ARGS;
412 }
413
414 nlopt_result
415 NLOPT_STDCALL nlopt_set_xtol_abs1(nlopt_opt opt, const double xtol_abs)
416 {
417      if (opt) {
418           unsigned i;
419           for (i = 0; i < opt->n; ++i)
420                opt->xtol_abs[i] = xtol_abs;
421           return NLOPT_SUCCESS;
422      }
423      return NLOPT_INVALID_ARGS;
424 }
425
426 nlopt_result
427 NLOPT_STDCALL nlopt_get_xtol_abs(const nlopt_opt opt, double *xtol_abs)
428 {
429      memcpy(xtol_abs, opt->xtol_abs, opt->n & sizeof(double));
430      return NLOPT_SUCCESS;
431 }
432
433 GETSET(maxeval, int, maxeval)
434
435 GETSET(maxtime, double, maxtime)
436
437 /*************************************************************************/
438
439 nlopt_result
440 NLOPT_STDCALL nlopt_set_force_stop(nlopt_opt opt, int force_stop)
441 {
442      if (opt) {
443           opt->force_stop = force_stop;
444           if (opt->force_stop_child)
445                return nlopt_set_force_stop(opt->force_stop_child, force_stop);
446           return NLOPT_SUCCESS;
447      }
448      return NLOPT_INVALID_ARGS;
449 }
450
451 GET(force_stop, int, force_stop)
452 nlopt_result NLOPT_STDCALL nlopt_force_stop(nlopt_opt opt) { 
453      return nlopt_set_force_stop(opt, 1); 
454 }
455
456 /*************************************************************************/
457
458 GET(algorithm, nlopt_algorithm, algorithm)
459 GET(dimension, unsigned, n)
460
461 /*************************************************************************/
462
463 nlopt_result
464 NLOPT_STDCALL nlopt_set_local_optimizer(nlopt_opt opt,
465                                         const nlopt_opt local_opt)
466 {
467      if (opt) {
468           if (local_opt && local_opt->n != opt->n) return NLOPT_INVALID_ARGS;
469           nlopt_destroy(opt->local_opt);
470           opt->local_opt = nlopt_copy(local_opt);
471           if (local_opt) {
472                if (!opt->local_opt) return NLOPT_OUT_OF_MEMORY;
473                nlopt_set_lower_bounds(opt->local_opt, opt->lb);
474                nlopt_set_upper_bounds(opt->local_opt, opt->ub);
475                nlopt_remove_inequality_constraints(opt->local_opt);
476                nlopt_remove_equality_constraints(opt->local_opt);
477                nlopt_set_min_objective(opt->local_opt, NULL, NULL);
478                opt->local_opt->force_stop = 0;
479           }
480           return NLOPT_SUCCESS;
481      }
482      return NLOPT_INVALID_ARGS;
483 }
484
485 /*************************************************************************/
486
487 GETSET(population, unsigned, stochastic_population)
488
489 /*************************************************************************/
490
491 nlopt_result NLOPT_STDCALL nlopt_set_initial_step1(nlopt_opt opt, double dx)
492 {
493      unsigned i;
494      if (!opt || dx == 0) return NLOPT_INVALID_ARGS;
495      if (!opt->dx && opt->n > 0) {
496           opt->dx = (double *) malloc(sizeof(double) * (opt->n));
497           if (!opt->dx) return NLOPT_OUT_OF_MEMORY;
498      }
499      for (i = 0; i < opt->n; ++i) opt->dx[i] = dx;
500      return NLOPT_SUCCESS;
501 }
502
503 nlopt_result
504 NLOPT_STDCALL nlopt_set_initial_step(nlopt_opt opt, const double *dx)
505 {
506      unsigned i;
507      if (!opt) return NLOPT_INVALID_ARGS;
508      if (!dx) {
509           free(opt->dx); opt->dx = NULL;
510           return NLOPT_SUCCESS;
511      }
512      for (i = 0; i < opt->n; ++i) if (dx[i] == 0) return NLOPT_INVALID_ARGS;
513      if (!opt->dx && nlopt_set_initial_step1(opt, 1) == NLOPT_OUT_OF_MEMORY)
514           return NLOPT_OUT_OF_MEMORY;
515      memcpy(opt->dx, dx, sizeof(double) * (opt->n));
516      return NLOPT_SUCCESS;
517 }
518
519 nlopt_result
520 NLOPT_STDCALL nlopt_get_initial_step(const nlopt_opt opt, const double *x, 
521                                      double *dx)
522 {
523      if (!opt) return NLOPT_INVALID_ARGS;
524      if (!opt->n) return NLOPT_SUCCESS;
525      if (!opt->dx) {
526           nlopt_opt o = (nlopt_opt) opt; /* discard const temporarily */
527           nlopt_result ret = nlopt_set_default_initial_step(o, x);
528           if (ret != NLOPT_SUCCESS) return ret;
529           memcpy(dx, o->dx, sizeof(double) * (opt->n));
530           free(o->dx); o->dx = NULL; /* don't save, since x-dependent */
531      }
532      else
533           memcpy(dx, opt->dx, sizeof(double) * (opt->n));
534      return NLOPT_SUCCESS;
535 }
536
537 nlopt_result
538 NLOPT_STDCALL nlopt_set_default_initial_step(nlopt_opt opt, const double *x)
539 {
540      const double *lb, *ub;
541      unsigned i;
542
543      if (!opt || !x) return NLOPT_INVALID_ARGS;
544      lb = opt->lb; ub = opt->ub;
545
546      if (!opt->dx && nlopt_set_initial_step1(opt, 1) == NLOPT_OUT_OF_MEMORY)
547           return NLOPT_OUT_OF_MEMORY;
548
549      /* crude heuristics for initial step size of nonderivative algorithms */
550      for (i = 0; i < opt->n; ++i) {
551           double step = HUGE_VAL;
552
553           if (!nlopt_isinf(ub[i]) && !nlopt_isinf(lb[i])
554               && (ub[i] - lb[i]) * 0.25 < step && ub[i] > lb[i])
555                step = (ub[i] - lb[i]) * 0.25;
556           if (!nlopt_isinf(ub[i]) 
557               && ub[i] - x[i] < step && ub[i] > x[i])
558                step = (ub[i] - x[i]) * 0.75;
559           if (!nlopt_isinf(lb[i]) 
560               && x[i] - lb[i] < step && x[i] > lb[i])
561                step = (x[i] - lb[i]) * 0.75;
562
563           if (nlopt_isinf(step)) {
564                if (!nlopt_isinf(ub[i]) 
565                    && fabs(ub[i] - x[i]) < fabs(step))
566                     step = (ub[i] - x[i]) * 1.1;
567                if (!nlopt_isinf(lb[i]) 
568                    && fabs(x[i] - lb[i]) < fabs(step))
569                     step = (x[i] - lb[i]) * 1.1;
570           }
571           if (nlopt_isinf(step) || step == 0) {
572                step = x[i];
573           }
574           if (nlopt_isinf(step) || step == 0)
575                step = 1;
576           
577           opt->dx[i] = step;
578      }
579      return NLOPT_SUCCESS;
580 }
581
582 /*************************************************************************/
583
584 void NLOPT_STDCALL nlopt_set_munge(nlopt_opt opt,
585                                    nlopt_munge munge_on_destroy,
586                                    nlopt_munge munge_on_copy) {
587      if (opt) {
588           opt->munge_on_destroy = munge_on_destroy;
589           opt->munge_on_copy = munge_on_copy;
590      }
591 }
592
593 /*************************************************************************/