PythonInteropBoost
From Eigen
This is an example of one way to wrap an Eigen-based C++ class using Boost::Python for use with numpy.
Here is an overview of how it works:
- You write a wrapper function for any member functions that take Eigen Matrices,Vectors,etc... This function will:
- take PyObjects (your numpy arrays) as inputs,
- use a macro to get at the underlying data,
- use Eigen's Map functionality to "turn them into" Eigen arrays,
- and then call the wrapped function using these arrays.
- You write a bit more code to tell Boost about your class and the functions you are exposing to python.
- You build the module as a shared library
- You can either:
- import this directly in your code (you will crash hard if the inputs are incorrect) ~or~
- write some Python to do typechecking, bounds checking, etc... in __init__.py, exporting a "safe" version to the rest of your code. (the example does this)
All in all, this is definitely overkill for the included example, and hopefully some day there will be a much cleaner way of doing this, but this is a place to start, and may even be satisfactory if you are already familiar with Boost::Python.
Here is the code, tested on Debian with Eigen 2.0.12 on Apr. 29, 2010:
hg clone https://bitbucket.org/drewm1980/eigen_python_interop_boost/src/tip/python/boost_example/
It resides in a subdirectory of a clone of the Eigen sources. Changes are welcome!
Here is the most critical part, to give you so you have some idea what to expect:
#define WRAP_PYTHON 1 #if WRAP_PYTHON #include <Python.h> #include <boost/python.hpp> #include <numpy/arrayobject.h> #endif #include <iostream> using namespace std; #include <Eigen/Core> #include <Eigen/Array> using namespace Eigen; class FooClass { public: FooClass( int new_m ); ~FooClass(); template<typename Derived> int foo(const MatrixBase<Derived>& barIn, MatrixBase<Derived>& barOut); #if WRAP_PYTHON int foo_python(PyObject* barIn, PyObject* barOut); #endif private: int m; }; FooClass::FooClass( int new_m ){ m = new_m; } FooClass::~FooClass(){ } template<typename Derived> int FooClass::foo(const MatrixBase<Derived>& barIn, MatrixBase<Derived>& barOut){ barOut = barIn*3.0; // Some trivial placeholder computation. } #if WRAP_PYTHON int FooClass::foo_python(PyObject* barIn, PyObject* barOut){ Map<VectorXd> _barIn((double *) PyArray_DATA(barIn),m); Map<VectorXd> _barOut((double *) PyArray_DATA(barOut),m); return foo(_barIn, _barOut); } using namespace boost::python; BOOST_PYTHON_MODULE(_FooClass) { class_<FooClass>("FooClass", init<int>(args("m"))) .def("foo", &FooClass::foo_python) ; } #endif <\source>