Boost.Python on OS X with NumPy

Ur python program too slow at calculating the boiling point of tungsten under a 600bar atmosphere of francium fluoride ? Too baaad…
Well, let's implement some part of it in C++ using Boost.Python with its NumPy brand new module. That's easy, I promise.
This tuto uses Python 3.4, installed from https://www.python.org/downloads/mac-osx/. Should be easily adapted to other versions.

Build Boost.Python

  1. Download Boost (dev version) : git clone –recursive https://github.com/boostorg/boost.git modular-boost
  2. ./bootstrap.sh –with-libraries=python –with-python-version=3.4 –with-python-root=/Library/Frameworks/Python.framework/Versions/3.4/bin/python3.4
  3. sudo ./b2 toolset=clang cxxflags="-stdlib=libc++ -std=c++0x" linkflags="-stdlib=libc++" -j2 install
  4. If it works, you should see /usr/local/lib/libboost_python3.a and /usr/local/lib/libboost_numpy3.a.

Example Python module

Exemple Makefile :

NAME = gauss_seidel
CXX = clang++
CXXFLAGS += -Wall -std=c++1y
PYTHON_VER = 3.4
 
NUMPY_ROOT = $(shell pip$(PYTHON_VER) show numpy | grep "Location:" | cut -d" " -f2-)
CXXFLAGS += -fPIC $(shell python$(PYTHON_VER)-config --cflags) -I$(NUMPY_ROOT)/numpy/core/include
LDFLAGS += -fPIC $(shell python$(PYTHON_VER)-config --ldflags)
#LDFLAGS += -lboost_python3 -lboost_numpy3  # Fails at module import
LDFLAGS += /usr/local/lib/libboost_python3.a /usr/local/lib/libboost_numpy3.a
 
all: $(NAME).o
	$(CXX) -shared $^ $(LDFLAGS) -o $(NAME).so
 
$(NAME).o: $(NAME).cpp
 
%.o: %.cpp
	$(CXX) -o $@ -c $< $(CXXFLAGS) 

Exemple code (my_little_poney.cpp) :

#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
 
namespace py = boost::python;
namespace np = boost::python::numpy;
 
np::ndarray shift_col (int k, np::ndarray M) {
	size_t n = M.shape(0), p = M.shape(1);
	np::ndarray R = np::empty(py::make_tuple(n,p), M.get_dtype());
	for (size_t i = 0; i < n; ++i) {
		for (size_t j = 0; j < p; ++j) {
			R[i][(j+k)%p] = M[i][j];
		}
	}
	return R;
}
 
BOOST_PYTHON_MODULE(my_little_poney) {
	np::initialize();
	py::def("shift_col", shift_col);
}

Then build with make. It will create a my_little_poney.so dynamic lib which can be loaded as a python module with import my_little_poney.

NumPy arrays

Here is the issue : all operation made with array[i][j] are executed through the python interpreter and are very inefficient. You can use the xif::multiarr warper from xifutils to access directly and easily ndarrays :

template <typename T, size_t dim>
struct nparray_accessor : public xif::multiarr<T,dim> {
	nparray_accessor (np::ndarray& ndarr) : xif::multiarr<T,dim>( 
		(T*)ndarr.get_data(), 
		[&ndarr] (size_t d) -> size_t { return ndarr.shape(d); }
	) {}
};

nparray_accessor<T,dim> takes T as the data type (should match the ndarray's dtype) and dim the number of dimensions.

Documentation about Boost.Python.NumPy : http://boostorg.github.io/python/develop/doc/html/numpy/.