雑食性雑感雑記

知識の整理場。ため込んだ知識をブログ記事として再構築します。

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 とのつなぎ方も分かったし、これで複雑なコードも気楽にビルドできるでしょう。
( そこまで複雑な処理を作れるかどうかはともかく。。 )

参照