Ubuntu 16.04 LTSのaptでインストールできるBoostのバージョンは1.58なのでBoost.Numpyが使えない。
そこで、最新バージョンのBoostをソースからビルドした。
手順は、
Boost Getting Started on Unix Variants - 1.66.0
を参照した。
ソースダウンロード
wget https://dl.bintray.com/boostorg/release/1.66.0/source/boost_1_66_0.tar.bz2
解凍
tar --bzip2 -xf boost_1_66_0.tar.bz2
Pythonインストール
Boostをビルドする前にPythonをインストールする。
pyenvでAnaconda3 5.0.1をインストールしてデフォルトに設定しておく。
参考:Ubuntu 16.04 LTSにChainerをインストールする - TadaoYamaokaの開発日記
$ git clone git://github.com/yyuu/pyenv.git ~/.pyenv $ echo 'export PYENV_ROOT="${HOME}/.pyenv"' >> ~/.bashrc $ echo 'export PATH="${PYENV_ROOT}/bin:$PATH"' >> ~/.bashrc $ echo 'eval "$(pyenv init -)"' >> ~/.bashrc
再ログイン
$ pyenv install --list $ pyenv install anaconda3-5.0.1 $ pyenv versions $ pyenv global anaconda3-5.0.1 $ pyenv versions
Ubuntu 16.4 LTSのgccより、Anacondaがコンパイルされたgccが古いとリンク時にpython3のライブラリの競合が起きる。
Anaconda 4.2.0のpython3を使うとリンク時に以下のエラーが発生した。
g++ -std=c++11 -I/home/xxx/.pyenv/versions/anaconda3-4.2.0/include/python3.5m -o obj/a.o -c a.cpp g++ -o bin/a obj/a.o -L/home/xxx/.pyenv/versions/anaconda3-4.2.0/lib -lboost_python3 -lboost_numpy3 -lpython3.5m -std=c++11 //usr/local/lib/libboost_python3.so: `std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_create(unsigned long&, unsigned long)@GLIBCXX_3.4.21' に対する定義されていない参照です //usr/local/lib/libboost_python3.so: `std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_append(char const*, unsigned long)@GLIBCXX_3.4.21' に対する定義されていない参照です //usr/local/lib/libboost_python3.so: `std::runtime_error::runtime_error(std::runtime_error const&)@GLIBCXX_3.4.21' に対する定義 されていない参照です //usr/local/lib/libboost_python3.so: `std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct(unsigned long, char)@GLIBCXX_3.4.21' に対する定義されていない参照です //usr/local/lib/libboost_python3.so: `std::range_error::range_error(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@GLIBCXX_3.4.21' に対する定義されていない参照です //usr/local/lib/libboost_python3.so: `std::runtime_error::runtime_error(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@GLIBCXX_3.4.21' に対する定義されていない参照です //usr/local/lib/libboost_python3.so: `std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >::_M_construct(unsigned long, wchar_t)@GLIBCXX_3.4.21' に対する定義されていない参照です //usr/local/lib/libboost_python3.so: `std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >::_M_create(unsigned long&, unsigned long)@GLIBCXX_3.4.21' に対する定義されていない参照です //usr/local/lib/libboost_python3.so: `std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::compare(char const*) const@GLIBCXX_3.4.21' に対する定義されていない参照です collect2: error: ld returned 1 exit status Makefile:12: ターゲット 'bin/a' のレシピで失敗しました make: *** [bin/a] エラー 1
最新のAnaconda3 5.0.1を使用するとコンパイルされたgccのバージョンはUtuntuより新しいが、エラーなしでリンクできた。
Anaconda3 4.2.0のgccのバージョン
$ python Python 3.5.2 |Anaconda 4.2.0 (64-bit)| (default, Jul 2 2016, 17:53:06) [GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] on linux
Anaconda3 5.0.1のgccのバージョン
$ python Python 3.6.3 |Anaconda, Inc.| (default, Oct 13 2017, 12:02:49) [GCC 7.2.0] on linux
Boost.Pythonを有効化
ホームディレクトリに「user-config.jam」を作成し、以下のように編集する。
using python : : /home/xxx/.pyenv/versions/anaconda3-5.0.1/bin/python : /home/xxx/.pyenv/versions/anaconda3-5.0.1/include/python3.6m : /home/xxx/.pyenv/versions/anaconda3-5.0.1/lib ;
:で区切られた4つ目はインクルードディレクトリを指定し、5つ目はライブラリディレクトリを指定している。
なお、user-config.jam内では環境変数は使えない。
ビルド
cd boost_1_66_0/ ./bootstrap.sh sudo ./b2 install
/usr/local/include/boost/
/usr/local/lib/
にヘッダーとライブラリがインストールされる。
ビルドをやり直すには、
sudo ./b2 clean
を実行してから、やり直す。
Boost.PythonとBoost.Numpyを使用したソース作成
以下ようなソースを作成する。
a/a.pyで作成したNumpyのndarrayをa.cpp側で取得して、ndarrayから値を取り出して表示する。
a.cpp
#include <iostream> #include <boost/python/numpy.hpp> using namespace std; namespace py = boost::python; namespace np = boost::python::numpy; void call_py_a() { // Boost.PythonとBoost.Numpyの初期化 Py_Initialize(); np::initialize(); // Pythonモジュール読み込み py::object a_ns = py::import("a.a").attr("__dict__"); // 関数取得 py::object py_a = a_ns["a"]; auto ret = py_a(); np::ndarray data = py::extract<np::ndarray>(ret); float *val = reinterpret_cast<float*>(data.get_data()); cout << val[0] << endl; } int main() { call_py_a(); return 0; }
a/a.py
import numpy as np def a(): return np.array([123], dtype=np.float32)
setup.py
import setuptools setuptools.setup( name = 'a', version = '0.0.1', author = 'a', packages = ['a'], scripts = [], )
Pythonモジュールとしてインストールしておく。
pip install -e . --user
コンパイル
以下のようなMakefileを作成する。
Makefile
CC = g++ CFLAGS = -std=c++11 LDFLAGS = -lboost_python3 -lboost_numpy3 -lpython3.6m INCLUDE = -I $(PYENV_ROOT)/versions/anaconda3-5.0.1/include/python3.6m LIB = -L$(PYENV_ROOT)/versions/anaconda3-5.0.1/lib target = bin/a sources = a.cpp objects = $(addprefix obj/, $(sources:.cpp=.o)) $(target): $(objects) @[ -d bin ] || mkdir -p bin $(CC) -o $@ $^ $(LIB) $(LDFLAGS) $(CFLAGS) obj/%.o: %.cpp @[ -d obj ] || mkdir -p obj $(CC) $(CFLAGS) $(INCLUDE) -o $@ -c $< all: $(target) clean: rm -f $(objects) $(target)
コンパイルする。
make
実行時に共有ライブラリのパスを設定
コンパイルした実行可能ファイルを実行する際には、共有ライブラリのパスを環境変数LD_LIBRARY_PATHに設定する。
export LD_LIBRARY_PATH=/usr/local/lib:$PYENV_ROOT/versions/anaconda3-5.0.1/lib:$LD_LIBRARY_PATH
実行
$ bin/a
123
成功すれば、「123」と表示される。