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