はじめに
Pythonで作ったnumpy.ndarrayをC++側で加工して、Pythonに返す方法を試します。 PythonからCの関数を呼び出す方法は色々あるようですが、 今回は、numpyを操作したいので、Boost C++ Librariesを使ってみます。
準備
Boost C++ Librariesをインストールします。
https://www.boost.org/doc/libs/1_67_0/more/getting_started/unix-variants.html
の第1節に従ってダウンロードし、第5節に従ってビルドします。
方法(動的リンク版)
まず、C++のコードです。ファイル名は test1.cpp とします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <boost/python/numpy.hpp>
namespace p = boost::python;
namespace np = boost::python::numpy;
void plus_scalar(np::ndarray &a, float b)
{
auto N = a.shape(0);
auto *p = reinterpret_cast<float*>(a.get_data());
for(auto v=p; v!=p+N; ++v){*v=*v+b;}
}
BOOST_PYTHON_MODULE(test1)
{
Py_Initialize();
np::initialize();
p::def("plus_scalar", plus_scalar);
}
|
$ g++ --std=c++14 --shared -fPIC -I$BOOST_PATH/include/ -I/usr/include/python2.7/ test1.cpp -L$BOOST_PATH/lib -lboost_numpy27 -lboost_python27 -o test1.so$BOOST_PATHはBoost C++ Librariesをインストールしたパスです。 ライブラリのパスが通っていないので、
$ export LD_LIBRARY_PATH=$BOOST_PATH/libを実行し、.soをロードできるようにしておきます。 これをPythonから呼び出します。次のようにPythonスクリプト t1.py を作成します。
1 2 3 4 5 6 7 8 9 | import sys
sys.path.append('.')
import test1
import numpy as np
a = np.array([1,2,3], dtype=np.float32)
print(a)
test1.plus_scalar(a, 3.0)
print(a)
|
python t1.pyすると、
[ 1. 2. 3.] [ 4. 5. 6.]が出力されます。確かに、3.0が足されています。
方法(静的リンク版)
動的リンク版では LD_LIBRARY_PATH を設定しましたが、使いにくいのでBoost関連のライブラリだけでも静的リンクしておきましょう。次のようにすることで.soを作ることができます。
g++ --std=c++14 --shared -fPIC -I$BOOST_PATH/include/ -I/usr/include/python2.7/ test2.cpp $BOOST_PATH/lib/libboost_numpy27.a $BOOST_PATH/lib/libboost_python27.a -o test2.soもし、次のエラー
/usr/bin/ld: $BOOST_PATH/lib/libboost_numpy27.a(ndarray.o): 再配置 R_X86_64_32 (`.rodata.str1.8' に対する) は共有オブジェクト作成時には使用できません。-fPIC を付けて再コンパイルしてください。 $BOOST_PATH/lib/libboost_numpy27.a: error adding symbols: 不正な値です collect2: error: ld returned 1 exit statusが出て、リンクできないときは、エラーメッセージの通りにBoostをビルドし直します。 具体的には、[4]を参考に、
$ ./b2 --clear $ ./b2 cxxflags=-fPIC cflags=-fPIC installのように、cxxflagsを追加してビルドし直します。cflagsは不要かもしれません。なお、ここで追加したオプション名は、--cxxflagsでも、-cxxflagsでもなく、ハイフン無しの単なるcxxflagsです。 test2.cppは
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include <boost/python/numpy.hpp>
namespace p = boost::python;
namespace np = boost::python::numpy;
void plus_scalar(np::ndarray &a, float b)
{
auto N = a.shape(0);
auto *p = reinterpret_cast<float*>(a.get_data());
for(auto v=p; v!=p+N; ++v){*v=*v+b;}
}
BOOST_PYTHON_MODULE(test2)
{
Py_Initialize();
np::initialize();
p::def("plus_scalar", plus_scalar);
}
|
1 2 3 4 5 6 7 8 9 | import sys
sys.path.append('.')
import test2
import numpy as np
a = np.array([1,2,3], dtype=np.float32)
print(a)
test2.plus_scalar(a, 3.0)
print(a)
|
$ python t2.pyを実行すると、動的リンク版のときと同様に
[ 1. 2. 3.] [ 4. 5. 6.]と出力されます。
参考
[1] http://d.hatena.ne.jp/nihohi/20120306/1331002942
[2] http://tadaoyamaoka.hatenablog.com/entry/2017/05/25/234934
[3] https://stackoverflow.com/questions/10968309/how-to-import-python-module-from-so-file
[4] https://stackoverflow.com/questions/27848105/how-to-force-compilation-of-boost-to-use-fpic
0 件のコメント :
コメントを投稿