chiark / gitweb /
guard against multiple inclusion in nlopt.hpp; thanks to Saul Thurrowgood for the...
[nlopt.git] / api / nlopt-in.hpp
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 // C++ style wrapper around NLopt API
24 // nlopt.hpp is AUTOMATICALLY GENERATED from nlopt-in.hpp - edit the latter!
25
26 #ifndef NLOPT_HPP
27 #define NLOPT_HPP
28
29 #include <nlopt.h>
30
31 #include <vector>
32 #include <stdexcept>
33 #include <new>
34 #include <cstdlib>
35 #include <cstring>
36 #include <cmath>
37
38 // convenience overloading for below (not in nlopt:: since has nlopt_ prefix)
39 inline nlopt_result nlopt_get_initial_step(const nlopt_opt opt, double *dx) {
40       return nlopt_get_initial_step(opt, (const double *) NULL, dx);
41 }
42
43 namespace nlopt {
44
45   //////////////////////////////////////////////////////////////////////
46   // nlopt::* namespace versions of the C enumerated types
47   //          AUTOMATICALLY GENERATED, DO NOT EDIT
48   // GEN_ENUMS_HERE
49   //////////////////////////////////////////////////////////////////////
50
51   typedef nlopt_func func; // nlopt::func synoynm
52
53   // alternative to nlopt_func that takes std::vector<double>
54   // ... unfortunately requires a data copy
55   typedef double (*vfunc)(const std::vector<double> &x,
56                           std::vector<double> &grad, void *data);
57
58   //////////////////////////////////////////////////////////////////////
59   
60   // NLopt-specific exceptions (corresponding to error codes):
61   class roundoff_limited : public std::runtime_error {
62   public:
63     roundoff_limited() : std::runtime_error("nlopt roundoff-limited") {}
64   };
65
66   class forced_stop : public std::runtime_error {
67   public:
68     forced_stop() : std::runtime_error("nlopt forced stop") {}
69   };
70
71   //////////////////////////////////////////////////////////////////////
72
73   class opt {
74   private:
75     nlopt_opt o;
76     
77     void mythrow(nlopt_result ret) const {
78       switch (ret) {
79       case NLOPT_FAILURE: throw std::runtime_error("nlopt failure");
80       case NLOPT_OUT_OF_MEMORY: throw std::bad_alloc();
81       case NLOPT_INVALID_ARGS: throw std::invalid_argument("nlopt invalid argument");
82       case NLOPT_ROUNDOFF_LIMITED: throw roundoff_limited();
83       case NLOPT_FORCED_STOP: throw forced_stop();
84       default: break;
85       }
86     }
87
88     typedef struct {
89       opt *o;
90       func f; void *f_data;
91       vfunc vf;
92       nlopt_munge munge_destroy, munge_copy; // non-NULL for SWIG wrappers
93     } myfunc_data;
94
95     // free/destroy f_data in nlopt_destroy and nlopt_copy, respectively
96     static void *free_myfunc_data(void *p) { 
97       myfunc_data *d = (myfunc_data *) p;
98       if (d) {
99         if (d->f_data && d->munge_destroy) d->munge_destroy(d->f_data);
100         delete d;
101       }
102       return NULL;
103     }
104     static void *dup_myfunc_data(void *p) {
105       myfunc_data *d = (myfunc_data *) p;
106       if (d) {
107         void *f_data;
108         if (d->f_data && d->munge_copy) {
109           f_data = d->munge_copy(d->f_data);
110           if (!f_data) return NULL;
111         }
112         else
113           f_data = d->f_data;
114         myfunc_data *dnew = new myfunc_data;
115         if (dnew) {
116           *dnew = *d;
117           dnew->f_data = f_data;
118         }
119         return (void*) dnew;
120       }
121       else return NULL;
122     }
123
124     // nlopt_func wrapper that catches exceptions
125     static double myfunc(unsigned n, const double *x, double *grad, void *d_) {
126       myfunc_data *d = reinterpret_cast<myfunc_data*>(d_);
127       try {
128         return d->f(n, x, grad, d->f_data);
129       }
130       catch (std::bad_alloc&)
131         { d->o->forced_stop_reason = NLOPT_OUT_OF_MEMORY; }
132       catch (std::invalid_argument&)
133         { d->o->forced_stop_reason = NLOPT_INVALID_ARGS; }
134       catch (roundoff_limited&)
135         { d->o->forced_stop_reason = NLOPT_ROUNDOFF_LIMITED; }
136       catch (forced_stop&)
137         { d->o->forced_stop_reason = NLOPT_FORCED_STOP; }
138       catch (...)
139         { d->o->forced_stop_reason = NLOPT_FAILURE; }
140       d->o->force_stop(); // stop gracefully, opt::optimize will re-throw
141       return HUGE_VAL;
142     }
143
144     std::vector<double> xtmp, gradtmp, gradtmp0; // scratch for myvfunc
145
146     // nlopt_func wrapper, using std::vector<double>
147     static double myvfunc(unsigned n, const double *x, double *grad, void *d_){
148       myfunc_data *d = reinterpret_cast<myfunc_data*>(d_);
149       try {
150         std::vector<double> &xv = d->o->xtmp;
151         if (n) std::memcpy(&xv[0], x, n * sizeof(double));
152         double val=d->vf(xv, grad ? d->o->gradtmp : d->o->gradtmp0, d->f_data);
153         if (grad && n) {
154           std::vector<double> &gradv = d->o->gradtmp;
155           std::memcpy(grad, &gradv[0], n * sizeof(double));
156         }
157         return val;
158       }
159       catch (std::bad_alloc&)
160         { d->o->forced_stop_reason = NLOPT_OUT_OF_MEMORY; }
161       catch (std::invalid_argument&)
162         { d->o->forced_stop_reason = NLOPT_INVALID_ARGS; }
163       catch (roundoff_limited&)
164         { d->o->forced_stop_reason = NLOPT_ROUNDOFF_LIMITED; }
165       catch (forced_stop&)
166         { d->o->forced_stop_reason = NLOPT_FORCED_STOP; }
167       catch (...)
168         { d->o->forced_stop_reason = NLOPT_FAILURE; }
169       d->o->force_stop(); // stop gracefully, opt::optimize will re-throw
170       return HUGE_VAL;
171     }
172
173     void alloc_tmp() {
174       if (xtmp.size() != nlopt_get_dimension(o)) {
175         xtmp = std::vector<double>(nlopt_get_dimension(o));
176         gradtmp = std::vector<double>(nlopt_get_dimension(o));
177       }
178     }
179
180     result last_result;
181     double last_optf;
182     nlopt_result forced_stop_reason;
183
184   public:
185     // Constructors etc.
186     opt() : o(NULL), xtmp(0), gradtmp(0), gradtmp0(0), 
187             last_result(nlopt::FAILURE), last_optf(HUGE_VAL),
188             forced_stop_reason(NLOPT_FORCED_STOP) {}
189     ~opt() { nlopt_destroy(o); }
190     opt(algorithm a, unsigned n) : 
191       o(nlopt_create(nlopt_algorithm(a), n)), 
192       xtmp(0), gradtmp(0), gradtmp0(0),
193       last_result(nlopt::FAILURE), last_optf(HUGE_VAL),
194       forced_stop_reason(NLOPT_FORCED_STOP) {
195       if (!o) throw std::bad_alloc();
196       nlopt_set_munge(o, free_myfunc_data, dup_myfunc_data);
197     }
198     opt(const opt& f) : o(nlopt_copy(f.o)), 
199                         xtmp(f.xtmp), gradtmp(f.gradtmp), gradtmp0(0),
200                         last_result(f.last_result), last_optf(f.last_optf),
201                         forced_stop_reason(f.forced_stop_reason) {
202       if (f.o && !o) throw std::bad_alloc();
203     }
204     opt& operator=(opt const& f) {
205       if (this == &f) return *this; // self-assignment
206       nlopt_destroy(o);
207       o = nlopt_copy(f.o);
208       if (f.o && !o) throw std::bad_alloc();
209       xtmp = f.xtmp; gradtmp = f.gradtmp;
210       last_result = f.last_result; last_optf = f.last_optf;
211       forced_stop_reason = f.forced_stop_reason;
212       return *this;
213     }
214
215     // Do the optimization:
216     result optimize(std::vector<double> &x, double &opt_f) {
217       if (o && nlopt_get_dimension(o) != x.size())
218         throw std::invalid_argument("dimension mismatch");
219       forced_stop_reason = NLOPT_FORCED_STOP;
220       nlopt_result ret = nlopt_optimize(o, x.empty() ? NULL : &x[0], &opt_f);
221       last_result = result(ret);
222       last_optf = opt_f;
223       if (ret == NLOPT_FORCED_STOP)
224         mythrow(forced_stop_reason);
225       mythrow(ret);
226       return last_result;
227     }
228
229     // variant mainly useful for SWIG wrappers:
230     std::vector<double> optimize(const std::vector<double> &x0) {
231       std::vector<double> x(x0);
232       last_result = optimize(x, last_optf);
233       return x;
234     }
235     result last_optimize_result() const { return last_result; }
236     double last_optimum_value() const { return last_optf; }
237
238     // accessors:
239     algorithm get_algorithm() const {
240       if (!o) throw std::runtime_error("uninitialized nlopt::opt");
241       return algorithm(nlopt_get_algorithm(o));
242     }
243     const char *get_algorithm_name() const {
244       if (!o) throw std::runtime_error("uninitialized nlopt::opt");
245       return nlopt_algorithm_name(nlopt_get_algorithm(o));
246     }
247     unsigned get_dimension() const {
248       if (!o) throw std::runtime_error("uninitialized nlopt::opt");
249       return nlopt_get_dimension(o);
250     }
251
252     // Set the objective function
253     void set_min_objective(func f, void *f_data) {
254       myfunc_data *d = new myfunc_data;
255       if (!d) throw std::bad_alloc();
256       d->o = this; d->f = f; d->f_data = f_data; d->vf = NULL;
257       d->munge_destroy = d->munge_copy = NULL;
258       mythrow(nlopt_set_min_objective(o, myfunc, d)); // d freed via o
259     }
260     void set_min_objective(vfunc vf, void *f_data) {
261       myfunc_data *d = new myfunc_data;
262       if (!d) throw std::bad_alloc();
263       d->o = this; d->f = NULL; d->f_data = f_data; d->vf = vf;
264       d->munge_destroy = d->munge_copy = NULL;
265       mythrow(nlopt_set_min_objective(o, myvfunc, d)); // d freed via o
266       alloc_tmp();
267     }
268     void set_max_objective(func f, void *f_data) {
269       myfunc_data *d = new myfunc_data;
270       if (!d) throw std::bad_alloc();
271       d->o = this; d->f = f; d->f_data = f_data; d->vf = NULL;
272       d->munge_destroy = d->munge_copy = NULL;
273       mythrow(nlopt_set_max_objective(o, myfunc, d)); // d freed via o
274     }
275     void set_max_objective(vfunc vf, void *f_data) {
276       myfunc_data *d = new myfunc_data;
277       if (!d) throw std::bad_alloc();
278       d->o = this; d->f = NULL; d->f_data = f_data; d->vf = vf;
279       d->munge_destroy = d->munge_copy = NULL;
280       mythrow(nlopt_set_max_objective(o, myvfunc, d)); // d freed via o
281       alloc_tmp();
282     }
283
284     // for internal use in SWIG wrappers -- variant that
285     // takes ownership of f_data, with munging for destroy/copy
286     void set_min_objective(func f, void *f_data,
287                            nlopt_munge md, nlopt_munge mc) {
288       myfunc_data *d = new myfunc_data;
289       if (!d) throw std::bad_alloc();
290       d->o = this; d->f = f; d->f_data = f_data; d->vf = NULL;
291       d->munge_destroy = md; d->munge_copy = mc;
292       mythrow(nlopt_set_min_objective(o, myfunc, d)); // d freed via o
293     }
294     void set_max_objective(func f, void *f_data,
295                            nlopt_munge md, nlopt_munge mc) {
296       myfunc_data *d = new myfunc_data;
297       if (!d) throw std::bad_alloc();
298       d->o = this; d->f = f; d->f_data = f_data; d->vf = NULL;
299       d->munge_destroy = md; d->munge_copy = mc;
300       mythrow(nlopt_set_max_objective(o, myfunc, d)); // d freed via o
301     }
302
303     // Nonlinear constraints:
304
305     void remove_inequality_constraints() {
306       nlopt_result ret = nlopt_remove_inequality_constraints(o);
307       mythrow(ret);
308     }
309     void add_inequality_constraint(func f, void *f_data, double tol=0) {
310       myfunc_data *d = new myfunc_data;
311       if (!d) throw std::bad_alloc();
312       d->o = this; d->f = f; d->f_data = f_data; d->vf = NULL;
313       d->munge_destroy = d->munge_copy = NULL;
314       mythrow(nlopt_add_inequality_constraint(o, myfunc, d, tol));
315     }
316     void add_inequality_constraint(vfunc vf, void *f_data, double tol=0) {
317       myfunc_data *d = new myfunc_data;
318       if (!d) throw std::bad_alloc();
319       d->o = this; d->f = NULL; d->f_data = f_data; d->vf = vf;
320       d->munge_destroy = d->munge_copy = NULL;
321       mythrow(nlopt_add_inequality_constraint(o, myvfunc, d, tol));
322       alloc_tmp();
323     }
324
325     void remove_equality_constraints() {
326       nlopt_result ret = nlopt_remove_equality_constraints(o);
327       mythrow(ret);
328     }
329     void add_equality_constraint(func f, void *f_data, double tol=0) {
330       myfunc_data *d = new myfunc_data;
331       if (!d) throw std::bad_alloc();
332       d->o = this; d->f = f; d->f_data = f_data; d->vf = NULL;
333       d->munge_destroy = d->munge_copy = NULL;
334       mythrow(nlopt_add_equality_constraint(o, myfunc, d, tol));
335     }
336     void add_equality_constraint(vfunc vf, void *f_data, double tol=0) {
337       myfunc_data *d = new myfunc_data;
338       if (!d) throw std::bad_alloc();
339       d->o = this; d->f = NULL; d->f_data = f_data; d->vf = vf;
340       d->munge_destroy = d->munge_copy = NULL;
341       mythrow(nlopt_add_equality_constraint(o, myvfunc, d, tol));
342       alloc_tmp();
343     }
344
345     // For internal use in SWIG wrappers (see also above)
346     void add_inequality_constraint(func f, void *f_data, 
347                                    nlopt_munge md, nlopt_munge mc,
348                                    double tol=0) {
349       myfunc_data *d = new myfunc_data;
350       if (!d) throw std::bad_alloc();
351       d->o = this; d->f = f; d->f_data = f_data; d->vf = NULL;
352       d->munge_destroy = md; d->munge_copy = mc;
353       mythrow(nlopt_add_inequality_constraint(o, myfunc, d, tol));
354     }
355     void add_equality_constraint(func f, void *f_data, 
356                                  nlopt_munge md, nlopt_munge mc,
357                                  double tol=0) {
358       myfunc_data *d = new myfunc_data;
359       if (!d) throw std::bad_alloc();
360       d->o = this; d->f = f; d->f_data = f_data; d->vf = NULL;
361       d->munge_destroy = md; d->munge_copy = mc;
362       mythrow(nlopt_add_equality_constraint(o, myfunc, d, tol));
363     }
364
365 #define NLOPT_GETSET_VEC(name)                                          \
366     void set_##name(double val) {                                       \
367       mythrow(nlopt_set_##name##1(o, val));                             \
368     }                                                                   \
369     void get_##name(std::vector<double> &v) const {                     \
370       if (o && nlopt_get_dimension(o) != v.size())                      \
371         throw std::invalid_argument("dimension mismatch");              \
372       mythrow(nlopt_get_##name(o, v.empty() ? NULL : &v[0]));           \
373     }                                                                   \
374     std::vector<double> get_##name() const {                    \
375       if (!o) throw std::runtime_error("uninitialized nlopt::opt");     \
376       std::vector<double> v(nlopt_get_dimension(o));                    \
377       get_##name(v);                                                    \
378       return v;                                                         \
379     }                                                                   \
380     void set_##name(const std::vector<double> &v) {                     \
381       if (o && nlopt_get_dimension(o) != v.size())                      \
382         throw std::invalid_argument("dimension mismatch");              \
383       mythrow(nlopt_set_##name(o, v.empty() ? NULL : &v[0]));           \
384     }
385
386     NLOPT_GETSET_VEC(lower_bounds)
387     NLOPT_GETSET_VEC(upper_bounds)
388
389     // stopping criteria:
390
391 #define NLOPT_GETSET(T, name)                                           \
392     T get_##name() const {                                              \
393       if (!o) throw std::runtime_error("uninitialized nlopt::opt");     \
394       return nlopt_get_##name(o);                                       \
395     }                                                                   \
396     void set_##name(T name) {                                           \
397       mythrow(nlopt_set_##name(o, name));                               \
398     }
399     NLOPT_GETSET(double, stopval)
400     NLOPT_GETSET(double, ftol_rel)
401     NLOPT_GETSET(double, ftol_abs)
402     NLOPT_GETSET(double, xtol_rel)
403     NLOPT_GETSET_VEC(xtol_abs)
404     NLOPT_GETSET(int, maxeval)
405     NLOPT_GETSET(double, maxtime)
406
407     NLOPT_GETSET(int, force_stop)
408     void force_stop() { set_force_stop(1); }
409
410     // algorithm-specific parameters:
411
412     void set_local_optimizer(const opt &lo) {
413       nlopt_result ret = nlopt_set_local_optimizer(o, lo.o);
414       mythrow(ret);
415     }
416
417     NLOPT_GETSET(unsigned, population)
418     NLOPT_GETSET_VEC(initial_step)
419
420     void set_default_initial_step(const std::vector<double> &x) {
421       nlopt_result ret 
422         = nlopt_set_default_initial_step(o, x.empty() ? NULL : &x[0]);
423       mythrow(ret);
424     }
425     void get_initial_step(const std::vector<double> &x, std::vector<double> &dx) const {
426       if (o && (nlopt_get_dimension(o) != x.size()
427                 || nlopt_get_dimension(o) != dx.size()))
428         throw std::invalid_argument("dimension mismatch");
429       nlopt_result ret = nlopt_get_initial_step(o, x.empty() ? NULL : &x[0],
430                                                 dx.empty() ? NULL : &dx[0]);
431       mythrow(ret);
432     }
433     std::vector<double> get_initial_step_(const std::vector<double> &x) const {
434       if (!o) throw std::runtime_error("uninitialized nlopt::opt");
435       std::vector<double> v(nlopt_get_dimension(o));
436       get_initial_step(x, v);
437       return v;
438     }
439   };
440
441 #undef NLOPT_GETSET
442 #undef NLOPT_GETSET_VEC
443
444   //////////////////////////////////////////////////////////////////////
445
446   void srand(unsigned long seed) { nlopt_srand(seed); }
447   void srand_time() { nlopt_srand_time(); }
448   void version(int &major, int &minor, int &bugfix) {
449     nlopt_version(&major, &minor, &bugfix);
450   }
451   int version_major() {
452     int major, minor, bugfix;
453     nlopt_version(&major, &minor, &bugfix);
454     return major;
455   }
456   int version_minor() {
457     int major, minor, bugfix;
458     nlopt_version(&major, &minor, &bugfix);
459     return minor;
460   }
461   int version_bugfix() {
462     int major, minor, bugfix;
463     nlopt_version(&major, &minor, &bugfix);
464     return bugfix;
465   }
466   const char *algorithm_name(algorithm a) {
467     return nlopt_algorithm_name(nlopt_algorithm(a));
468   }
469
470   //////////////////////////////////////////////////////////////////////
471
472 } // namespace nlopt
473
474 #endif /* NLOPT_HPP */