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