chiark / gitweb /
support exceptions in SWIG wrappers
[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 #include <nlopt.h>
27
28 #include <vector>
29 #include <stdexcept>
30 #include <new>
31 #include <cstdlib>
32 #include <cstring>
33 #include <cmath>
34
35 // convenience overloading for below (not in nlopt:: since has nlopt_ prefix)
36 inline nlopt_result nlopt_get_initial_step(const nlopt_opt opt, double *dx) {
37       return nlopt_get_initial_step(opt, (const double *) NULL, dx);
38 }
39
40 namespace nlopt {
41
42   //////////////////////////////////////////////////////////////////////
43   // nlopt::* namespace versions of the C enumerated types
44   //          AUTOMATICALLY GENERATED, DO NOT EDIT
45   // GEN_ENUMS_HERE
46   //////////////////////////////////////////////////////////////////////
47
48   typedef nlopt_func func; // nlopt::func synoynm
49
50   // alternative to nlopt_func that takes std::vector<double>
51   // ... unfortunately requires a data copy
52   typedef double (*vfunc)(const std::vector<double> &x,
53                           std::vector<double> &grad, void *data);
54
55   //////////////////////////////////////////////////////////////////////
56   
57   // NLopt-specific exceptions (corresponding to error codes):
58   class roundoff_limited : public std::runtime_error {
59   public:
60     roundoff_limited() : std::runtime_error("nlopt roundoff-limited") {}
61   };
62
63   class forced_stop : public std::runtime_error {
64   public:
65     forced_stop() : std::runtime_error("nlopt forced stop") {}
66   };
67
68   //////////////////////////////////////////////////////////////////////
69
70   class opt {
71   private:
72     nlopt_opt o;
73     result last_result;
74     double last_optf;
75     
76     void mythrow(nlopt_result ret) const {
77       switch (ret) {
78       case NLOPT_FAILURE: throw std::runtime_error("nlopt failure");
79       case NLOPT_OUT_OF_MEMORY: throw std::bad_alloc();
80       case NLOPT_INVALID_ARGS: throw std::invalid_argument("nlopt invalid argument");
81       case NLOPT_ROUNDOFF_LIMITED: throw roundoff_limited();
82       case NLOPT_FORCED_STOP: throw forced_stop();
83       default: break;
84       }
85     }
86
87     typedef struct {
88       opt *o;
89       func f; void *f_data;
90       vfunc vf;
91     } myfunc_data;
92
93     // nlopt_func wrapper that catches exceptions
94     static double myfunc(unsigned n, const double *x, double *grad, void *d_) {
95       myfunc_data *d = reinterpret_cast<myfunc_data*>(d_);
96       try {
97         return d->f(n, x, grad, d->f_data);
98       }
99       catch (...) {
100         d->o->force_stop(); // stop gracefully, opt::optimize will re-throw
101         return HUGE_VAL;
102       }
103     }
104
105     std::vector<double> xtmp, gradtmp, gradtmp0; // scratch for myvfunc
106
107     // nlopt_func wrapper, using std::vector<double>
108     static double myvfunc(unsigned n, const double *x, double *grad, void *d_){
109       myfunc_data *d = reinterpret_cast<myfunc_data*>(d_);
110       try {
111         std::vector<double> &xv = d->o->xtmp;
112         if (n) std::memcpy(&xv[0], x, n * sizeof(double));
113         double val=d->vf(xv, grad ? d->o->gradtmp : d->o->gradtmp0, d->f_data);
114         if (grad && n) {
115           std::vector<double> &gradv = d->o->gradtmp;
116           std::memcpy(grad, &gradv[0], n * sizeof(double));
117         }
118         return val;
119       }
120       catch (...) {
121         d->o->force_stop(); // stop gracefully, opt::optimize will re-throw
122         return HUGE_VAL;
123       }
124     }
125
126     void alloc_tmp() {
127       if (xtmp.size() != nlopt_get_dimension(o)) {
128         xtmp = std::vector<double>(nlopt_get_dimension(o));
129         gradtmp = std::vector<double>(nlopt_get_dimension(o));
130       }
131     }
132
133   public:
134     // Constructors etc.
135     opt() : o(NULL), xtmp(0), gradtmp(0), gradtmp0(0), 
136             last_result(nlopt::FAILURE), last_optf(HUGE_VAL) {}
137     ~opt() { nlopt_destroy(o); }
138     opt(algorithm a, unsigned n) : 
139       o(nlopt_create(nlopt_algorithm(a), n)), 
140       xtmp(0), gradtmp(0), gradtmp0(0),
141       last_result(nlopt::FAILURE), last_optf(HUGE_VAL) {
142       if (!o) throw std::bad_alloc();
143       nlopt_set_free_f_data(o, 1);
144     }
145     opt(const opt& f) : o(nlopt_copy(f.o)), 
146                         xtmp(f.xtmp), gradtmp(f.gradtmp), gradtmp0(0),
147                         last_result(f.last_result), last_optf(f.last_optf) {
148       if (f.o && !o) throw std::bad_alloc();
149       mythrow(nlopt_dup_f_data(o, sizeof(myfunc_data)));
150     }
151     opt& operator=(opt const& f) {
152       if (this == &f) return *this; // self-assignment
153       nlopt_destroy(o);
154       o = nlopt_copy(f.o);
155       if (f.o && !o) throw std::bad_alloc();
156       mythrow(nlopt_dup_f_data(o, sizeof(myfunc_data)));
157       xtmp = f.xtmp; gradtmp = f.gradtmp;
158       last_result = f.last_result; last_optf = f.last_optf;
159       return *this;
160     }
161
162     // Do the optimization:
163     result optimize(std::vector<double> &x, double &opt_f) {
164       if (o && nlopt_get_dimension(o) != x.size())
165         throw std::invalid_argument("dimension mismatch");
166       nlopt_result ret = nlopt_optimize(o, x.empty() ? NULL : &x[0], &opt_f);
167       last_result = result(ret);
168       last_optf = opt_f;
169       mythrow(ret);
170       return last_result;
171     }
172
173     // variant mainly useful for SWIG wrappers:
174     std::vector<double> optimize(const std::vector<double> &x0) {
175       std::vector<double> x(x0);
176       last_result = optimize(x, last_optf);
177       return x;
178     }
179     result last_optimize_result() const { return last_result; }
180     double last_optimum_value() const { return last_optf; }
181
182     // accessors:
183     algorithm get_algorithm() const {
184       if (!o) throw std::runtime_error("uninitialized nlopt::opt");
185       return algorithm(nlopt_get_algorithm(o));
186     }
187     const char *get_algorithm_name() const {
188       if (!o) throw std::runtime_error("uninitialized nlopt::opt");
189       return nlopt_algorithm_name(nlopt_get_algorithm(o));
190     }
191     unsigned get_dimension() const {
192       if (!o) throw std::runtime_error("uninitialized nlopt::opt");
193       return nlopt_get_dimension(o);
194     }
195
196     // Set the objective function
197     void set_min_objective(func f, void *f_data) {
198       myfunc_data *d = (myfunc_data *) std::malloc(sizeof(myfunc_data));
199       if (!d) throw std::bad_alloc();
200       d->o = this; d->f = f; d->f_data = f_data; d->vf = NULL;
201       mythrow(nlopt_set_min_objective(o, myfunc, d)); // d freed via o
202     }
203     void set_min_objective(vfunc vf, void *f_data) {
204       myfunc_data *d = (myfunc_data *) std::malloc(sizeof(myfunc_data));
205       if (!d) throw std::bad_alloc();
206       d->o = this; d->f = NULL; d->f_data = f_data; d->vf = vf;
207       mythrow(nlopt_set_min_objective(o, myvfunc, d)); // d freed via o
208       alloc_tmp();
209     }
210     void set_max_objective(func f, void *f_data) {
211       myfunc_data *d = (myfunc_data *) std::malloc(sizeof(myfunc_data));
212       if (!d) throw std::bad_alloc();
213       d->o = this; d->f = f; d->f_data = f_data; d->vf = NULL;
214       mythrow(nlopt_set_max_objective(o, myfunc, d)); // d freed via o
215     }
216     void set_max_objective(vfunc vf, void *f_data) {
217       myfunc_data *d = (myfunc_data *) std::malloc(sizeof(myfunc_data));
218       if (!d) throw std::bad_alloc();
219       d->o = this; d->f = NULL; d->f_data = f_data; d->vf = vf;
220       mythrow(nlopt_set_max_objective(o, myvfunc, d)); // d freed via o
221       alloc_tmp();
222     }
223
224     // Nonlinear constraints:
225
226     void remove_inequality_constraints() {
227       nlopt_result ret = nlopt_remove_inequality_constraints(o);
228       mythrow(ret);
229     }
230     void add_inequality_constraint(func f, void *f_data, double tol=0) {
231       myfunc_data *d = (myfunc_data *) std::malloc(sizeof(myfunc_data));
232       if (!d) throw std::bad_alloc();
233       d->o = this; d->f = f; d->f_data = f_data; d->vf = NULL;
234       mythrow(nlopt_add_inequality_constraint(o, myfunc, d, tol));
235     }
236     void add_inequality_constraint(vfunc vf, void *f_data, double tol=0) {
237       myfunc_data *d = (myfunc_data *) std::malloc(sizeof(myfunc_data));
238       if (!d) throw std::bad_alloc();
239       d->o = this; d->f = NULL; d->f_data = f_data; d->vf = vf;
240       mythrow(nlopt_add_inequality_constraint(o, myvfunc, d, tol));
241       alloc_tmp();
242     }
243
244     void remove_equality_constraints() {
245       nlopt_result ret = nlopt_remove_equality_constraints(o);
246       mythrow(ret);
247     }
248     void add_equality_constraint(func f, void *f_data, double tol=0) {
249       myfunc_data *d = (myfunc_data *) std::malloc(sizeof(myfunc_data));
250       if (!d) throw std::bad_alloc();
251       d->o = this; d->f = f; d->f_data = f_data; d->vf = NULL;
252       mythrow(nlopt_add_equality_constraint(o, myfunc, d, tol));
253     }
254     void add_equality_constraint(vfunc vf, void *f_data, double tol=0) {
255       myfunc_data *d = (myfunc_data *) std::malloc(sizeof(myfunc_data));
256       if (!d) throw std::bad_alloc();
257       d->o = this; d->f = NULL; d->f_data = f_data; d->vf = vf;
258       mythrow(nlopt_add_equality_constraint(o, myvfunc, d, tol));
259       alloc_tmp();
260     }
261
262 #define NLOPT_GETSET_VEC(name)                                          \
263     void set_##name(double val) {                                       \
264       mythrow(nlopt_set_##name##1(o, val));                             \
265     }                                                                   \
266     void get_##name(std::vector<double> &v) const {                     \
267       if (o && nlopt_get_dimension(o) != v.size())                      \
268         throw std::invalid_argument("dimension mismatch");              \
269       mythrow(nlopt_get_##name(o, v.empty() ? NULL : &v[0]));           \
270     }                                                                   \
271     std::vector<double> get_##name() const {                    \
272       if (!o) throw std::runtime_error("uninitialized nlopt::opt");     \
273       std::vector<double> v(nlopt_get_dimension(o));                    \
274       get_##name(v);                                                    \
275       return v;                                                         \
276     }                                                                   \
277     void set_##name(const std::vector<double> &v) {                     \
278       if (o && nlopt_get_dimension(o) != v.size())                      \
279         throw std::invalid_argument("dimension mismatch");              \
280       mythrow(nlopt_set_##name(o, v.empty() ? NULL : &v[0]));           \
281     }
282
283     NLOPT_GETSET_VEC(lower_bounds)
284     NLOPT_GETSET_VEC(upper_bounds)
285
286     // stopping criteria:
287
288 #define NLOPT_GETSET(T, name)                                           \
289     T get_##name() const {                                              \
290       if (!o) throw std::runtime_error("uninitialized nlopt::opt");     \
291       return nlopt_get_##name(o);                                       \
292     }                                                                   \
293     void set_##name(T name) {                                           \
294       mythrow(nlopt_set_##name(o, name));                               \
295     }
296     NLOPT_GETSET(double, stopval)
297     NLOPT_GETSET(double, ftol_rel)
298     NLOPT_GETSET(double, ftol_abs)
299     NLOPT_GETSET(double, xtol_rel)
300     NLOPT_GETSET_VEC(xtol_abs)
301     NLOPT_GETSET(int, maxeval)
302     NLOPT_GETSET(double, maxtime)
303
304     NLOPT_GETSET(int, force_stop)
305     void force_stop() { set_force_stop(1); }
306
307     // algorithm-specific parameters:
308
309     void set_local_optimizer(const opt &lo) {
310       nlopt_result ret = nlopt_set_local_optimizer(o, lo.o);
311       mythrow(ret);
312     }
313
314     NLOPT_GETSET(unsigned, population)
315     NLOPT_GETSET_VEC(initial_step)
316
317     void set_default_initial_step(const std::vector<double> &x) {
318       nlopt_result ret 
319         = nlopt_set_default_initial_step(o, x.empty() ? NULL : &x[0]);
320       mythrow(ret);
321     }
322     void get_initial_step(const std::vector<double> &x, std::vector<double> &dx) const {
323       if (o && (nlopt_get_dimension(o) != x.size()
324                 || nlopt_get_dimension(o) != dx.size()))
325         throw std::invalid_argument("dimension mismatch");
326       nlopt_result ret = nlopt_get_initial_step(o, x.empty() ? NULL : &x[0],
327                                                 dx.empty() ? NULL : &dx[0]);
328       mythrow(ret);
329     }
330     std::vector<double> get_initial_step(const std::vector<double> &x) const {
331       if (!o) throw std::runtime_error("uninitialized nlopt::opt");
332       std::vector<double> v(nlopt_get_dimension(o));
333       get_initial_step(x, v);
334       return v;
335     }
336   };
337
338 #undef NLOPT_GETSET
339 #undef NLOPT_GETSET_VEC
340
341   //////////////////////////////////////////////////////////////////////
342
343   void srand(unsigned long seed) { nlopt_srand(seed); }
344   void srand_time() { nlopt_srand_time(); }
345   void version(int &major, int &minor, int &bugfix) {
346     nlopt_version(&major, &minor, &bugfix);
347   }
348   int version_major() {
349     int major, minor, bugfix;
350     nlopt_version(&major, &minor, &bugfix);
351     return major;
352   }
353   int version_minor() {
354     int major, minor, bugfix;
355     nlopt_version(&major, &minor, &bugfix);
356     return minor;
357   }
358   int version_bugfix() {
359     int major, minor, bugfix;
360     nlopt_version(&major, &minor, &bugfix);
361     return bugfix;
362   }
363   const char *algorithm_name(algorithm a) {
364     return nlopt_algorithm_name(nlopt_algorithm(a));
365   }
366
367   //////////////////////////////////////////////////////////////////////
368
369 } // namespace nlopt