Boost.numpy ことはじめ
以前から「Boost.numpy」というのが便利というのを聞いていたがなかなか使って見る機会が無く。。
他のブログ等検索したが、自分にちょうど良さそうな記事が見つからなかったので整理しつつ事始めしてみる。
Ubuntu16.04で環境構築から始めて、簡単なサンプルをcmakeビルドし、Pythonで呼び出すところまで。
Boost.numpy
Boost 1.63 辺りから追加された機能。
C++ boost code ⇔ Python 間で numpy 配列を受け渡し、
Python だと遅いところは C++ で操作
C++ で記述が面倒なところは Python で操作
することができる。
環境
- Ubuntu16.04
- Python3.5
- ( boost は 1.58 が apt-get により既に導入済み )
- その他
- CMake 3.5 ( ソースコードからビルドで導入 )
環境構築
最新版 Boost
/opt 上に boost 環境を構築した。書いた時点で最新は1.67。
デフォルトだとPython2.7の方に行ってしまうので、bootstrap にてPython3を使うようにしてあげる。
環境汚さないように、ビルドしたものは /opt 内に。
sudo su - cd /opt/ wget https://dl.bintray.com/boostorg/release/1.67.0/source/boost_1_67_0.tar.gz tar zxvf boost_1_67_0.tar.gz cd boost_1_67_0 ./bootstrap.sh --with-python-version=3.5 ./b2 --prefix=/opt/boost_1_67_0 install
確認
find / -name *boost_numpy* ( 中略 ) /opt/boost_1_67_0/lib/libboost_numpy35.so
使ってみる
サンプルソースコード
参照1のコードを使わせてもらう。インクルード等ちょっと修正。
ポイント :
- BOOST_PYTHON_MODULE の第一引数にはPythonで実行時のモジュール名。
- 後で cmake ビルドで「lib~」となるので見越した名前を付ける。
sample.cpp
#include "boost/python/numpy.hpp" #include <stdexcept> #include <algorithm> namespace p = boost::python; namespace np = boost::python::numpy; /* 2倍にする */ void mult_two(np::ndarray a) { int nd = a.get_nd(); if (nd != 1) throw std::runtime_error("a must be 1-dimensional"); size_t N = a.shape(0); if (a.get_dtype() != np::dtype::get_builtin<double>()) throw std::runtime_error("a must be float64 array"); double *p = reinterpret_cast<double *>(a.get_data()); std::transform(p, p + N, p, [](double x) { return 2 * x; }); } /* BOOST_PYTHON_MODULE の引数は .so 名 */ BOOST_PYTHON_MODULE(libsample) { Py_Initialize(); np::initialize(); p::def("mult_two", mult_two); }
ビルド設定
CMake でビルド。
ポイント :
- boost は独自インストールなので、それに対応した読み込み。
CMakeLists.txt
project(sample) cmake_minimum_required(VERSION 3.0) set(BOOST_ROOT /opt/boost_1_67_0) ### C++11 add_compile_options(-std=c++11) ### pkgconfig (for pkg_check_modules) find_package(PkgConfig REQUIRED) ### Python includes pkg_check_modules(PYTHON3 python3 REQUIRED) include_directories(${PYTHON3_INCLUDE_DIRS}) ### Boost includes include_directories(${BOOST_ROOT}/include) link_directories(${BOOST_ROOT}/lib) ### Build add_library(sample SHARED sample.cpp) set_target_properties(sample PROPERTIES SUFFIX ".so") target_link_libraries(sample boost_numpy35 boost_python35)
ちなみに、ここまでのディレクトリ構成は
<root directory> - sample.cpp - CMakeLists.txt
となっている想定。
root directoryのところからビルドする。
mkdir build cd build cmake .. make
正常にビルドされると libsample.so ができる。
実行
読み込んでみる。 (ipython3)
ipython3 In [1]: import libsample as s In [2]: import numpy as np In [3]: a = np.array([1.0, 2.0], np.float64) In [4]: s.mult_two(a) In [5]: a Out[5]: array([2., 4.])
まとめ
Boost.Numpy の実行試せた。
CMake とのつなぎ方も分かったし、これで複雑なコードも気楽にビルドできるでしょう。
( そこまで複雑な処理を作れるかどうかはともかく。。 )
参照
- 1. https://qiita.com/termoshtt/items/81eeb0467d9087958f7f
- C++でPythonを拡張するためのBoost.NumPyチュートリアル(実践編)