chiark / gitweb /
beginnings of Python wrapper (not tested, not compiled, memory leak still in callback...
authorstevenj <stevenj@alum.mit.edu>
Fri, 4 Jun 2010 22:51:47 +0000 (18:51 -0400)
committerstevenj <stevenj@alum.mit.edu>
Fri, 4 Jun 2010 22:51:47 +0000 (18:51 -0400)
darcs-hash:20100604225147-c8de0-110484f6dd1de418e3e3db7cfb7c16bc2988e29b.gz

swig/Makefile.am
swig/nlopt-python.i [new file with mode: 0644]
swig/nlopt.i

index 3700643ccff56f2bc7a958ab1da6b9eccf1a18fd..74c061985030f2da4af522da6c3072eb4d399b0f 100644 (file)
@@ -1,5 +1,5 @@
 SWIG_SRC = nlopt.i nlopt-exceptions.i nlopt-enum-renames.i 
-EXTRA_DIST = $(SWIG_SRC) nlopt-guile.i nlopt.scm.in
+EXTRA_DIST = $(SWIG_SRC) nlopt-guile.i nlopt-python.i nlopt.scm.in
 
 BUILT_SOURCES = nlopt-guile.cpp nlopt-enum-renames.i nlopt.scm.in
 
@@ -30,11 +30,14 @@ lib_LTLIBRARIES = $(guilelib)
 
 if MAINTAINER_MODE
 
-nlopt-guile.cpp: $(SWIG_SRC) nlopt-guile.i $(HDR)
+nlopt-guile.cpp nlopt.scm.in: $(SWIG_SRC) nlopt-guile.i $(HDR)
        swig -I$(top_builddir)/api -outdir $(builddir) -c++ -guile -scmstub -o $@ nlopt.i
        rm -f nlopt.scm.in
        mv nlopt.scm nlopt.scm.in
 
+nlopt-python.cpp nlopt.py: $(SWIG_SRC) nlopt-python.i $(HDR)
+       swig -I$(top_builddir)/api -outdir $(builddir) -c++ -python -o $@ nlopt.i
+
 nlopt-enum-renames.i: $(top_srcdir)/api/nlopt.h
        (echo "// AUTOMATICALLY GENERATED -- DO NOT EDIT"; egrep 'NLOPT_[LG][DN]|NLOPT_NUM_ALGORITHMS' $(top_srcdir)/api/nlopt.h | sed 's/NLOPT_//g' |tr -d ' ' |tr '/' ',' |tr '=' ',' |cut -d, -f1 |while read name; do echo "%rename(NLOPT_$$name) nlopt::$$name;"; done; egrep 'NLOPT_[A-Z_]* =' $(top_srcdir)/api/nlopt.h | egrep -v 'NLOPT_[LG][DN]' | sed 's/NLOPT_//g' |tr -d ' ' |cut -d'=' -f1 | while read name; do echo "%rename(NLOPT_$$name) nlopt::$$name;"; done) > $@
 
diff --git a/swig/nlopt-python.i b/swig/nlopt-python.i
new file mode 100644 (file)
index 0000000..32e3305
--- /dev/null
@@ -0,0 +1,95 @@
+// -*- C++ -*-
+
+//////////////////////////////////////////////////////////////////////////////
+
+%{
+#define SWIG_FILE_WITH_INIT
+#define array_stride(a,i)        (((PyArrayObject *)a)->strides[i])
+%}
+%include "numpy.i"
+%init %{
+  import_array();
+%}
+%numpy_typemaps(double, NPY_DOUBLE, unsigned)
+
+//////////////////////////////////////////////////////////////////////////////
+// numpy.i does not include maps for std::vector<double>, so I add them here,
+// taking advantage of the conversion functions provided by numpy.i
+
+// Typemap for input arguments of type const std::vector<double> &
+%typecheck(SWIG_TYPECHECK_POINTER, fragment="NumPy_Macros")
+  const std::vector<double> &
+{
+  $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in, fragment="NumPy_Fragments")
+  const std::vector<double> &
+(PyArrayObject* array=NULL, int is_new_object=0, std::vector<double> arrayv)
+{
+  npy_intp size[1] = { -1 };
+  array = obj_to_array_allow_conversion($input, DATA_TYPECODE, &is_new_object);
+  if (!array || !require_dimensions(array, 1) ||
+      !require_size(array, size, 1)) SWIG_fail;
+  arrayv = std::vector<double>(array_size(array,0));
+  $1 = &arrayv;
+  {
+    double *arr_data = (double *) array_data(array);
+    int arr_i, arr_s = array_stride(array,0), arr_sz = array_size(array,0);
+    for (arr_i = 0; arr_i < arr_sz; ++arr_i)
+      arrayv[arr_i] = arr_data[arr_i * arr_s];
+  }
+}
+%typemap(freearg)
+  const std::vector<double> &
+{
+  if (is_new_object$argnum && array$argnum)
+    { Py_DECREF(array$argnum); }
+}
+
+// Typemap for return values of type std::vector<double>
+%typemap(out, fragment="NumPy_Fragments") std::vector<double>
+{
+  npy_intp sz = $1.size();
+  $result = PyArray_SimpleNew(1, &sz, NPY_DOUBLE);
+  std::memcpy(array_data($result), $1.empty() ? NULL : &$1[0],
+             sizeof(double) * sz);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Wrapper for objective function callbacks
+
+%{
+static double func_python(unsigned n, const double *x, double *grad, void *f)
+{
+  npy_intp sz = npy_intp(n), sz0 = 0;
+  PyObject *xpy = PyArray_SimpleNewFromData(1, &sz, NPY_DOUBLE, x);
+  PyObject *gradpy = grad ? PyArray_SimpleNew(1, &sz0, NPY_DOUBLE)
+    : PyArray_SimpleNewFromData(1, &sz, NPY_DOUBLE, grad);
+  
+  PyObject *arglist = Py_BuildValue("OO", xpy, gradpy);
+  PyObject *result = PyEval_CallObject((PyObject *) f, arglist);
+  Py_DECREF(arglist);
+
+  Py_DECREF(gradpy);
+  Py_DECREF(xpy);
+
+  double val = HUGE_VAL;
+  if (SWIG_IsOK(SWIG_AsVal_double(result, &val))) {
+    Py_DECREF(result);
+  }
+  else {
+    Py_DECREF(result);
+    throw std::invalid_argument("invalid result passed to nlopt");
+  }
+  return val;
+}
+%}
+
+%typemap(in)(nlopt::func f, void *f_data) {
+  Py_INCREF($input);
+  $1 = func_python;
+  $2 = (void*) $input;
+}
+%typecheck(SWIG_TYPECHECK_POINTER)(nlopt::func f, void *f_data) {
+  $1 = PyCallable_Check($input);
+}
index 6f6f0b4c629840e55e50a877e77754d23e11ec53..c5054e2fc1372b71390a4587171bbfcf1bdd155e 100644 (file)
@@ -24,4 +24,8 @@ namespace std {
 %include "nlopt-guile.i"
 #endif
 
+#ifdef SWIGPYTHON
+%include "nlopt-python.i"
+#endif
+
 %include "nlopt.hpp"