Edited at

C++のEigenで作ったライブラリをBoost.NumpyでPythonから使えるようにする。

More than 1 year has passed since last update.


この記事は

札幌C++勉強会のオンラインもくもく会#36でやった内容をメモります。


インストール方法

他所をあたってください。がんばれ。


やること

簡単な例として、Pythonのnumpyで用意した2次元配列の和を

C++のEigenライブラリを使って計算し、それをまたPythonに返す、ということをします。


C++のソースコード


cpplib.cpp

#define EIGEN_DEFAULT_TO_ROW_MAJOR

#include<Eigen/Core>
#include<boost/numpy.hpp>
namespace py = boost::python;
namespace np = boost::numpy;

np::ndarray add_double(const np::ndarray lhs, const np::ndarray rhs) {
using Stride = Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic>;
const auto
lrows = lhs.shape(0),
lcols = lhs.shape(1),
rrows = rhs.shape(0),
rcols = rhs.shape(1);

const Stride
lstride(lhs.strides(0)/sizeof(double), lhs.strides(1)/sizeof(double)),
rstride(rhs.strides(0)/sizeof(double), rhs.strides(1)/sizeof(double));

const Eigen::Map<const Eigen::MatrixXd, Eigen::Unaligned, Stride>
lmat(reinterpret_cast<double*>(lhs.get_data()), lrows, lcols, lstride),
rmat(reinterpret_cast<double*>(rhs.get_data()), rrows, rcols, rstride);

np::ndarray ret = np::empty(
py::make_tuple(lmat.rows(),lmat.cols()),
np::dtype::get_builtin<double>());

Eigen::Map<Eigen::MatrixXd>
ret_mat(reinterpret_cast<double*>(ret.get_data()), lmat.rows(), lmat.cols());

ret_mat = lmat + rmat;
return ret;
}

BOOST_PYTHON_MODULE(cpplib) {
Py_Initialize();
np::initialize();
py::def("add_double",add_double);
}


行列の足し算を目的にするだけならもっと簡単に書けるかもしれませんが、応用が効くように書いてます。型が double 限定なのは仕方ないです。やるとしたらdtype 調べて分岐ですが、実行時判定するしかない。


コンパイル方法

SConsでやります。例えばこんなかんじです。mingw環境でやってます。


SConstruct

# vim: filetype=python

# SConstruct

env = Environment(
SHLIBPREFIX="",
SHLIBSUFFIX=".pyd",
CXX="/mingw64/bin/g++",
CXXFLAGS=["-std=c++14"],
LINKFLAGS=["-std=c++14"],
CPPPATH=[
"/mingw64/include/eigen3",
"/mingw64/include/python3.5m", ],
LIBPATH=["/mingw64/lib"])

env.SharedLibrary("cpplib", ["cpplib.cpp"],
LIBS=["boost_numpy", "python3.5.dll", "boost_python3-mt"])



Pythonコード


usecpp.py

import numpy as np

from cpplib import add_double

a = np.array(
[[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 1, 2, 3],
[4, 5, 6, 7], ], dtype=np.float64)
b = np.array(
[[5, 6],
[4, 5], ], dtype=np.float64)

result = add_double(a[1::2, 1::2], b[::1, ::1])
print(result)


結果は

[[6,8],[5,7]][[5,6],[4,5]]が足されるので

$ python3 usecpp.py

[[ 11. 14.]
[ 9. 12.]]

となります。ここでもくもく会時間切れ。


追記

https://github.com/ignisan/boost_numpy_project/blob/master/to_eigen_map.hpp