chiark / gitweb /
Use trusty
[nlopt.git] / api / nlopt-in.hpp
index 65cfde9202c19102498d6cb3f403b8cf46943114..582c676fd9704e7dd073d6e8a043356f4ae9bf2a 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007-2010 Massachusetts Institute of Technology
+/* Copyright (c) 2007-2011 Massachusetts Institute of Technology
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files (the
 // C++ style wrapper around NLopt API
 // nlopt.hpp is AUTOMATICALLY GENERATED from nlopt-in.hpp - edit the latter!
 
+#ifndef NLOPT_HPP
+#define NLOPT_HPP
+
 #include <nlopt.h>
 
 #include <vector>
 #include <stdexcept>
 #include <new>
 #include <cstdlib>
+#include <cstring>
+#include <cmath>
 
 // convenience overloading for below (not in nlopt:: since has nlopt_ prefix)
 inline nlopt_result nlopt_get_initial_step(const nlopt_opt opt, double *dx) {
@@ -44,6 +49,7 @@ namespace nlopt {
   //////////////////////////////////////////////////////////////////////
 
   typedef nlopt_func func; // nlopt::func synoynm
+  typedef nlopt_mfunc mfunc; // nlopt::mfunc synoynm
 
   // alternative to nlopt_func that takes std::vector<double>
   // ... unfortunately requires a data copy
@@ -68,13 +74,12 @@ namespace nlopt {
   class opt {
   private:
     nlopt_opt o;
-    bool stopped_by_exception;
     
     void mythrow(nlopt_result ret) const {
       switch (ret) {
-      case NLOPT_FAILURE: throw std::runtime_error("nlopt failure");
+      case NLOPT_FAILURE: throw std::runtime_error(get_errmsg() ? get_errmsg() : "nlopt failure");
       case NLOPT_OUT_OF_MEMORY: throw std::bad_alloc();
-      case NLOPT_INVALID_ARGS: throw std::invalid_argument("nlopt invalid argument");
+      case NLOPT_INVALID_ARGS: throw std::invalid_argument(get_errmsg() ? get_errmsg() : "nlopt invalid argument");
       case NLOPT_ROUNDOFF_LIMITED: throw roundoff_limited();
       case NLOPT_FORCED_STOP: throw forced_stop();
       default: break;
@@ -83,64 +88,150 @@ namespace nlopt {
 
     typedef struct {
       opt *o;
-      func f; void *f_data;
+      mfunc mf; func f; void *f_data;
       vfunc vf;
+      nlopt_munge munge_destroy, munge_copy; // non-NULL for SWIG wrappers
     } myfunc_data;
 
+    // free/destroy f_data in nlopt_destroy and nlopt_copy, respectively
+    static void *free_myfunc_data(void *p) { 
+      myfunc_data *d = (myfunc_data *) p;
+      if (d) {
+       if (d->f_data && d->munge_destroy) d->munge_destroy(d->f_data);
+       delete d;
+      }
+      return NULL;
+    }
+    static void *dup_myfunc_data(void *p) {
+      myfunc_data *d = (myfunc_data *) p;
+      if (d) {
+       void *f_data;
+       if (d->f_data && d->munge_copy) {
+         f_data = d->munge_copy(d->f_data);
+         if (!f_data) return NULL;
+       }
+       else
+         f_data = d->f_data;
+       myfunc_data *dnew = new myfunc_data;
+       if (dnew) {
+         *dnew = *d;
+         dnew->f_data = f_data;
+       }
+       return (void*) dnew;
+      }
+      else return NULL;
+    }
+
     // nlopt_func wrapper that catches exceptions
     static double myfunc(unsigned n, const double *x, double *grad, void *d_) {
       myfunc_data *d = reinterpret_cast<myfunc_data*>(d_);
       try {
        return d->f(n, x, grad, d->f_data);
       }
-      catch (...) {
-       d->o->stopped_by_exception = true;
-       d->o->force_stop(); // stop gracefully, opt::optimize will re-throw
+      catch (std::bad_alloc&)
+       { d->o->forced_stop_reason = NLOPT_OUT_OF_MEMORY; }
+      catch (std::invalid_argument&)
+       { d->o->forced_stop_reason = NLOPT_INVALID_ARGS; }
+      catch (roundoff_limited&)
+       { d->o->forced_stop_reason = NLOPT_ROUNDOFF_LIMITED; }
+      catch (forced_stop&)
+       { d->o->forced_stop_reason = NLOPT_FORCED_STOP; }
+      catch (...)
+       { d->o->forced_stop_reason = NLOPT_FAILURE; }
+      d->o->force_stop(); // stop gracefully, opt::optimize will re-throw
+      return HUGE_VAL;
+    }
+
+    // nlopt_mfunc wrapper that catches exceptions
+    static void mymfunc(unsigned m, double *result,
+                       unsigned n, const double *x, double *grad, void *d_) {
+      myfunc_data *d = reinterpret_cast<myfunc_data*>(d_);
+      try {
+       d->mf(m, result, n, x, grad, d->f_data);
+       return;
       }
+      catch (std::bad_alloc&)
+       { d->o->forced_stop_reason = NLOPT_OUT_OF_MEMORY; }
+      catch (std::invalid_argument&)
+       { d->o->forced_stop_reason = NLOPT_INVALID_ARGS; }
+      catch (roundoff_limited&)
+       { d->o->forced_stop_reason = NLOPT_ROUNDOFF_LIMITED; }
+      catch (forced_stop&)
+       { d->o->forced_stop_reason = NLOPT_FORCED_STOP; }
+      catch (...)
+       { d->o->forced_stop_reason = NLOPT_FAILURE; }
+      d->o->force_stop(); // stop gracefully, opt::optimize will re-throw
+      for (unsigned i = 0; i < m; ++i) result[i] = HUGE_VAL;
     }
 
+    std::vector<double> xtmp, gradtmp, gradtmp0; // scratch for myvfunc
+
     // nlopt_func wrapper, using std::vector<double>
     static double myvfunc(unsigned n, const double *x, double *grad, void *d_){
       myfunc_data *d = reinterpret_cast<myfunc_data*>(d_);
       try {
-       std::vector<double> xv(n);
-       for (unsigned i = 0; i < n; ++i) xv[i] = x[i];
-       std::vector<double> gradv(grad ? n : 0);
-       double val = d->vf(xv, gradv, d->f_data);
-       if (grad) for (unsigned i = 0; i < n; ++i) grad[i] = gradv[i];
+       std::vector<double> &xv = d->o->xtmp;
+       if (n) std::memcpy(&xv[0], x, n * sizeof(double));
+       double val=d->vf(xv, grad ? d->o->gradtmp : d->o->gradtmp0, d->f_data);
+       if (grad && n) {
+         std::vector<double> &gradv = d->o->gradtmp;
+         std::memcpy(grad, &gradv[0], n * sizeof(double));
+       }
        return val;
       }
-      catch (...) {
-       d->o->stopped_by_exception = true;
-       d->o->force_stop(); // stop gracefully, opt::optimize will re-throw
+      catch (std::bad_alloc&)
+       { d->o->forced_stop_reason = NLOPT_OUT_OF_MEMORY; }
+      catch (std::invalid_argument&)
+       { d->o->forced_stop_reason = NLOPT_INVALID_ARGS; }
+      catch (roundoff_limited&)
+       { d->o->forced_stop_reason = NLOPT_ROUNDOFF_LIMITED; }
+      catch (forced_stop&)
+       { d->o->forced_stop_reason = NLOPT_FORCED_STOP; }
+      catch (...)
+       { d->o->forced_stop_reason = NLOPT_FAILURE; }
+      d->o->force_stop(); // stop gracefully, opt::optimize will re-throw
+      return HUGE_VAL;
+    }
+
+    void alloc_tmp() {
+      if (xtmp.size() != nlopt_get_dimension(o)) {
+       xtmp = std::vector<double>(nlopt_get_dimension(o));
+       gradtmp = std::vector<double>(nlopt_get_dimension(o));
       }
     }
 
+    result last_result;
+    double last_optf;
+    nlopt_result forced_stop_reason;
+
   public:
     // Constructors etc.
-    opt() : o(NULL), stopped_by_exception(false) {}
+    opt() : o(NULL), xtmp(0), gradtmp(0), gradtmp0(0), 
+           last_result(nlopt::FAILURE), last_optf(HUGE_VAL),
+           forced_stop_reason(NLOPT_FORCED_STOP) {}
     ~opt() { nlopt_destroy(o); }
     opt(algorithm a, unsigned n) : 
-      o(nlopt_create(nlopt_algorithm(a), n)), stopped_by_exception(false) {
-      if (!o) throw std::bad_alloc();
-      nlopt_set_free_f_data(o, 1);
-    }
-    void reinit(algorithm a, unsigned n) {
-      if (o) nlopt_destroy(o);
-      o = nlopt_create(nlopt_algorithm(a), n);
+      o(nlopt_create(nlopt_algorithm(a), n)), 
+      xtmp(0), gradtmp(0), gradtmp0(0),
+      last_result(nlopt::FAILURE), last_optf(HUGE_VAL),
+      forced_stop_reason(NLOPT_FORCED_STOP) {
       if (!o) throw std::bad_alloc();
-      nlopt_set_free_f_data(o, 1);
+      nlopt_set_munge(o, free_myfunc_data, dup_myfunc_data);
     }
-    opt(const opt& from) : o(nlopt_copy(from.o)) {
-      if (from.o && !o) throw std::bad_alloc();
-      mythrow(nlopt_dup_f_data(o, sizeof(myfunc_data)));
+    opt(const opt& f) : o(nlopt_copy(f.o)), 
+                       xtmp(f.xtmp), gradtmp(f.gradtmp), gradtmp0(0),
+                       last_result(f.last_result), last_optf(f.last_optf),
+                       forced_stop_reason(f.forced_stop_reason) {
+      if (f.o && !o) throw std::bad_alloc();
     }
     opt& operator=(opt const& f) {
       if (this == &f) return *this; // self-assignment
       nlopt_destroy(o);
       o = nlopt_copy(f.o);
       if (f.o && !o) throw std::bad_alloc();
-      mythrow(nlopt_dup_f_data(o, sizeof(myfunc_data)));
+      xtmp = f.xtmp; gradtmp = f.gradtmp;
+      last_result = f.last_result; last_optf = f.last_optf;
+      forced_stop_reason = f.forced_stop_reason;
       return *this;
     }
 
@@ -148,13 +239,24 @@ namespace nlopt {
     result optimize(std::vector<double> &x, double &opt_f) {
       if (o && nlopt_get_dimension(o) != x.size())
         throw std::invalid_argument("dimension mismatch");
-      stopped_by_exception = false;
+      forced_stop_reason = NLOPT_FORCED_STOP;
       nlopt_result ret = nlopt_optimize(o, x.empty() ? NULL : &x[0], &opt_f);
-      if (ret == NLOPT_FORCED_STOP && stopped_by_exception)
-       throw; // re-throw last-caught exception
+      last_result = result(ret);
+      last_optf = opt_f;
+      if (ret == NLOPT_FORCED_STOP)
+       mythrow(forced_stop_reason);
       mythrow(ret);
-      return result(ret);
+      return last_result;
+    }
+
+    // variant mainly useful for SWIG wrappers:
+    std::vector<double> optimize(const std::vector<double> &x0) {
+      std::vector<double> x(x0);
+      last_result = optimize(x, last_optf);
+      return x;
     }
+    result last_optimize_result() const { return last_result; }
+    double last_optimum_value() const { return last_optf; }
 
     // accessors:
     algorithm get_algorithm() const {
@@ -172,78 +274,165 @@ namespace nlopt {
 
     // Set the objective function
     void set_min_objective(func f, void *f_data) {
-      myfunc_data *d = (myfunc_data *) std::malloc(sizeof(myfunc_data));
+      myfunc_data *d = new myfunc_data;
       if (!d) throw std::bad_alloc();
-      d->o = this; d->f = f; d->f_data = f_data; d->vf = NULL;
+      d->o = this; d->f = f; d->f_data = f_data; d->mf = NULL; d->vf = NULL;
+      d->munge_destroy = d->munge_copy = NULL;
       mythrow(nlopt_set_min_objective(o, myfunc, d)); // d freed via o
     }
     void set_min_objective(vfunc vf, void *f_data) {
-      myfunc_data *d = (myfunc_data *) std::malloc(sizeof(myfunc_data));
+      myfunc_data *d = new myfunc_data;
       if (!d) throw std::bad_alloc();
-      d->o = this; d->f = NULL; d->f_data = f_data; d->vf = vf;
+      d->o = this; d->f = NULL; d->f_data = f_data; d->mf = NULL; d->vf = vf;
+      d->munge_destroy = d->munge_copy = NULL;
       mythrow(nlopt_set_min_objective(o, myvfunc, d)); // d freed via o
+      alloc_tmp();
     }
     void set_max_objective(func f, void *f_data) {
-      myfunc_data *d = (myfunc_data *) std::malloc(sizeof(myfunc_data));
+      myfunc_data *d = new myfunc_data;
       if (!d) throw std::bad_alloc();
-      d->o = this; d->f = f; d->f_data = f_data; d->vf = NULL;
+      d->o = this; d->f = f; d->f_data = f_data; d->mf = NULL; d->vf = NULL;
+      d->munge_destroy = d->munge_copy = NULL;
       mythrow(nlopt_set_max_objective(o, myfunc, d)); // d freed via o
     }
     void set_max_objective(vfunc vf, void *f_data) {
-      myfunc_data *d = (myfunc_data *) std::malloc(sizeof(myfunc_data));
+      myfunc_data *d = new myfunc_data;
       if (!d) throw std::bad_alloc();
-      d->o = this; d->f = NULL; d->f_data = f_data; d->vf = vf;
+      d->o = this; d->f = NULL; d->f_data = f_data; d->mf = NULL; d->vf = vf;
+      d->munge_destroy = d->munge_copy = NULL;
       mythrow(nlopt_set_max_objective(o, myvfunc, d)); // d freed via o
+      alloc_tmp();
+    }
+
+    // for internal use in SWIG wrappers -- variant that
+    // takes ownership of f_data, with munging for destroy/copy
+    void set_min_objective(func f, void *f_data,
+                          nlopt_munge md, nlopt_munge mc) {
+      myfunc_data *d = new myfunc_data;
+      if (!d) throw std::bad_alloc();
+      d->o = this; d->f = f; d->f_data = f_data; d->mf = NULL; d->vf = NULL;
+      d->munge_destroy = md; d->munge_copy = mc;
+      mythrow(nlopt_set_min_objective(o, myfunc, d)); // d freed via o
+    }
+    void set_max_objective(func f, void *f_data,
+                          nlopt_munge md, nlopt_munge mc) {
+      myfunc_data *d = new myfunc_data;
+      if (!d) throw std::bad_alloc();
+      d->o = this; d->f = f; d->f_data = f_data; d->mf = NULL; d->vf = NULL;
+      d->munge_destroy = md; d->munge_copy = mc;
+      mythrow(nlopt_set_max_objective(o, myfunc, d)); // d freed via o
     }
 
     // Nonlinear constraints:
 
-    void remove_inequality_constraints(void) {
+    void remove_inequality_constraints() {
       nlopt_result ret = nlopt_remove_inequality_constraints(o);
       mythrow(ret);
     }
     void add_inequality_constraint(func f, void *f_data, double tol=0) {
-      myfunc_data *d = (myfunc_data *) std::malloc(sizeof(myfunc_data));
+      myfunc_data *d = new myfunc_data;
       if (!d) throw std::bad_alloc();
-      d->o = this; d->f = f; d->f_data = f_data; d->vf = NULL;
+      d->o = this; d->f = f; d->f_data = f_data; d->mf = NULL; d->vf = NULL;
+      d->munge_destroy = d->munge_copy = NULL;
       mythrow(nlopt_add_inequality_constraint(o, myfunc, d, tol));
     }
     void add_inequality_constraint(vfunc vf, void *f_data, double tol=0) {
-      myfunc_data *d = (myfunc_data *) std::malloc(sizeof(myfunc_data));
+      myfunc_data *d = new myfunc_data;
       if (!d) throw std::bad_alloc();
-      d->o = this; d->f = NULL; d->f_data = f_data; d->vf = vf;
+      d->o = this; d->f = NULL; d->f_data = f_data; d->mf = NULL; d->vf = vf;
+      d->munge_destroy = d->munge_copy = NULL;
       mythrow(nlopt_add_inequality_constraint(o, myvfunc, d, tol));
+      alloc_tmp();
+    }
+    void add_inequality_mconstraint(mfunc mf, void *f_data, 
+                                   const std::vector<double> &tol) {
+      myfunc_data *d = new myfunc_data;
+      if (!d) throw std::bad_alloc();
+      d->o = this; d->mf = mf; d->f_data = f_data; d->f = NULL; d->vf = NULL;
+      d->munge_destroy = d->munge_copy = NULL;
+      mythrow(nlopt_add_inequality_mconstraint(o, tol.size(), mymfunc, d, 
+                                              tol.empty() ? NULL : &tol[0]));
     }
 
-    void remove_equality_constraints(void) {
+    void remove_equality_constraints() {
       nlopt_result ret = nlopt_remove_equality_constraints(o);
       mythrow(ret);
     }
     void add_equality_constraint(func f, void *f_data, double tol=0) {
-      myfunc_data *d = (myfunc_data *) std::malloc(sizeof(myfunc_data));
+      myfunc_data *d = new myfunc_data;
       if (!d) throw std::bad_alloc();
-      d->o = this; d->f = f; d->f_data = f_data; d->vf = NULL;
+      d->o = this; d->f = f; d->f_data = f_data; d->mf = NULL; d->vf = NULL;
+      d->munge_destroy = d->munge_copy = NULL;
       mythrow(nlopt_add_equality_constraint(o, myfunc, d, tol));
     }
     void add_equality_constraint(vfunc vf, void *f_data, double tol=0) {
-      myfunc_data *d = (myfunc_data *) std::malloc(sizeof(myfunc_data));
+      myfunc_data *d = new myfunc_data;
       if (!d) throw std::bad_alloc();
-      d->o = this; d->f = NULL; d->f_data = f_data; d->vf = vf;
+      d->o = this; d->f = NULL; d->f_data = f_data; d->mf = NULL; d->vf = vf;
+      d->munge_destroy = d->munge_copy = NULL;
       mythrow(nlopt_add_equality_constraint(o, myvfunc, d, tol));
+      alloc_tmp();
+    }
+    void add_equality_mconstraint(mfunc mf, void *f_data, 
+                                 const std::vector<double> &tol) {
+      myfunc_data *d = new myfunc_data;
+      if (!d) throw std::bad_alloc();
+      d->o = this; d->mf = mf; d->f_data = f_data; d->f = NULL; d->vf = NULL;
+      d->munge_destroy = d->munge_copy = NULL;
+      mythrow(nlopt_add_equality_mconstraint(o, tol.size(), mymfunc, d, 
+                                            tol.empty() ? NULL : &tol[0]));
+    }
+
+    // For internal use in SWIG wrappers (see also above)
+    void add_inequality_constraint(func f, void *f_data, 
+                                  nlopt_munge md, nlopt_munge mc,
+                                  double tol=0) {
+      myfunc_data *d = new myfunc_data;
+      if (!d) throw std::bad_alloc();
+      d->o = this; d->f = f; d->f_data = f_data; d->mf = NULL; d->vf = NULL;
+      d->munge_destroy = md; d->munge_copy = mc;
+      mythrow(nlopt_add_inequality_constraint(o, myfunc, d, tol));
+    }
+    void add_equality_constraint(func f, void *f_data, 
+                                nlopt_munge md, nlopt_munge mc,
+                                double tol=0) {
+      myfunc_data *d = new myfunc_data;
+      if (!d) throw std::bad_alloc();
+      d->o = this; d->f = f; d->f_data = f_data; d->mf = NULL; d->vf = NULL;
+      d->munge_destroy = md; d->munge_copy = mc;
+      mythrow(nlopt_add_equality_constraint(o, myfunc, d, tol));
+    }
+    void add_inequality_mconstraint(mfunc mf, void *f_data, 
+                                   nlopt_munge md, nlopt_munge mc,
+                                   const std::vector<double> &tol) {
+      myfunc_data *d = new myfunc_data;
+      if (!d) throw std::bad_alloc();
+      d->o = this; d->mf = mf; d->f_data = f_data; d->f = NULL; d->vf = NULL;
+      d->munge_destroy = md; d->munge_copy = mc;
+      mythrow(nlopt_add_inequality_mconstraint(o, tol.size(), mymfunc, d, 
+                                              tol.empty() ? NULL : &tol[0]));
+    }
+    void add_equality_mconstraint(mfunc mf, void *f_data, 
+                                 nlopt_munge md, nlopt_munge mc,
+                                 const std::vector<double> &tol) {
+      myfunc_data *d = new myfunc_data;
+      if (!d) throw std::bad_alloc();
+      d->o = this; d->mf = mf; d->f_data = f_data; d->f = NULL; d->vf = NULL;
+      d->munge_destroy = md; d->munge_copy = mc;
+      mythrow(nlopt_add_equality_mconstraint(o, tol.size(), mymfunc, d, 
+                                            tol.empty() ? NULL : &tol[0]));
     }
 
 #define NLOPT_GETSET_VEC(name)                                         \
     void set_##name(double val) {                                      \
-      nlopt_result ret = nlopt_set_##name##1(o, val);                  \
-      mythrow(ret);                                                    \
+      mythrow(nlopt_set_##name##1(o, val));                            \
     }                                                                  \
     void get_##name(std::vector<double> &v) const {                    \
       if (o && nlopt_get_dimension(o) != v.size())                     \
         throw std::invalid_argument("dimension mismatch");             \
-      nlopt_result ret = nlopt_get_##name(o, v.empty() ? NULL : &v[0]);        \
-      mythrow(ret);                                                    \
+      mythrow(nlopt_get_##name(o, v.empty() ? NULL : &v[0]));          \
     }                                                                  \
-    std::vector<double> get_##name(void) const {                       \
+    std::vector<double> get_##name() const {                   \
       if (!o) throw std::runtime_error("uninitialized nlopt::opt");    \
       std::vector<double> v(nlopt_get_dimension(o));                   \
       get_##name(v);                                                   \
@@ -252,8 +441,7 @@ namespace nlopt {
     void set_##name(const std::vector<double> &v) {                    \
       if (o && nlopt_get_dimension(o) != v.size())                     \
         throw std::invalid_argument("dimension mismatch");             \
-      nlopt_result ret = nlopt_set_##name(o, v.empty() ? NULL : &v[0]);        \
-      mythrow(ret);                                                    \
+      mythrow(nlopt_set_##name(o, v.empty() ? NULL : &v[0]));          \
     }
 
     NLOPT_GETSET_VEC(lower_bounds)
@@ -267,8 +455,7 @@ namespace nlopt {
       return nlopt_get_##name(o);                                      \
     }                                                                  \
     void set_##name(T name) {                                          \
-      nlopt_result ret = nlopt_set_##name(o, name);                    \
-      mythrow(ret);                                                    \
+      mythrow(nlopt_set_##name(o, name));                              \
     }
     NLOPT_GETSET(double, stopval)
     NLOPT_GETSET(double, ftol_rel)
@@ -281,6 +468,11 @@ namespace nlopt {
     NLOPT_GETSET(int, force_stop)
     void force_stop() { set_force_stop(1); }
 
+    const char *get_errmsg() const { 
+        if (!o) throw std::runtime_error("uninitialized nlopt::opt");
+        return nlopt_get_errmsg(o);
+    }
+
     // algorithm-specific parameters:
 
     void set_local_optimizer(const opt &lo) {
@@ -289,6 +481,7 @@ namespace nlopt {
     }
 
     NLOPT_GETSET(unsigned, population)
+    NLOPT_GETSET(unsigned, vector_storage)
     NLOPT_GETSET_VEC(initial_step)
 
     void set_default_initial_step(const std::vector<double> &x) {
@@ -304,7 +497,7 @@ namespace nlopt {
                                                dx.empty() ? NULL : &dx[0]);
       mythrow(ret);
     }
-    std::vector<double> get_initial_step(const std::vector<double> &x) const {
+    std::vector<double> get_initial_step_(const std::vector<double> &x) const {
       if (!o) throw std::runtime_error("uninitialized nlopt::opt");
       std::vector<double> v(nlopt_get_dimension(o));
       get_initial_step(x, v);
@@ -318,10 +511,25 @@ namespace nlopt {
   //////////////////////////////////////////////////////////////////////
 
   inline void srand(unsigned long seed) { nlopt_srand(seed); }
-  inline void srand_time(void) { nlopt_srand_time(); }
+  inline void srand_time() { nlopt_srand_time(); }
   inline void version(int &major, int &minor, int &bugfix) {
     nlopt_version(&major, &minor, &bugfix);
   }
+  inline int version_major() {
+    int major, minor, bugfix;
+    nlopt_version(&major, &minor, &bugfix);
+    return major;
+  }
+  inline int version_minor() {
+    int major, minor, bugfix;
+    nlopt_version(&major, &minor, &bugfix);
+    return minor;
+  }
+  inline int version_bugfix() {
+    int major, minor, bugfix;
+    nlopt_version(&major, &minor, &bugfix);
+    return bugfix;
+  }
   inline const char *algorithm_name(algorithm a) {
     return nlopt_algorithm_name(nlopt_algorithm(a));
   }
@@ -329,3 +537,5 @@ namespace nlopt {
   //////////////////////////////////////////////////////////////////////
 
 } // namespace nlopt
+
+#endif /* NLOPT_HPP */