PythonInteropBoost

From Eigen
Jump to: navigation, search

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:

  1. 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.
  2. You write a bit more code to tell Boost about your class and the functions you are exposing to python.
  3. You build the module as a shared library
  4. 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>