From 618ff01ef808d54abc3cff54fd6eb63ea246c5d8 Mon Sep 17 00:00:00 2001 From: stevenj Date: Fri, 4 Jun 2010 18:51:47 -0400 Subject: [PATCH] beginnings of Python wrapper (not tested, not compiled, memory leak still in callback reference) darcs-hash:20100604225147-c8de0-110484f6dd1de418e3e3db7cfb7c16bc2988e29b.gz --- swig/Makefile.am | 7 +++- swig/nlopt-python.i | 95 +++++++++++++++++++++++++++++++++++++++++++++ swig/nlopt.i | 4 ++ 3 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 swig/nlopt-python.i diff --git a/swig/Makefile.am b/swig/Makefile.am index 3700643..74c0619 100644 --- a/swig/Makefile.am +++ b/swig/Makefile.am @@ -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 index 0000000..32e3305 --- /dev/null +++ b/swig/nlopt-python.i @@ -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, so I add them here, +// taking advantage of the conversion functions provided by numpy.i + +// Typemap for input arguments of type const std::vector & +%typecheck(SWIG_TYPECHECK_POINTER, fragment="NumPy_Macros") + const std::vector & +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, fragment="NumPy_Fragments") + const std::vector & +(PyArrayObject* array=NULL, int is_new_object=0, std::vector 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(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 & +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +// Typemap for return values of type std::vector +%typemap(out, fragment="NumPy_Fragments") std::vector +{ + 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); +} diff --git a/swig/nlopt.i b/swig/nlopt.i index 6f6f0b4..c5054e2 100644 --- a/swig/nlopt.i +++ b/swig/nlopt.i @@ -24,4 +24,8 @@ namespace std { %include "nlopt-guile.i" #endif +#ifdef SWIGPYTHON +%include "nlopt-python.i" +#endif + %include "nlopt.hpp" -- 2.30.2