boost-python ではじめる大規模機械学習(6)- boost::multi_array のエクスポート・前編
あらすじ
boost-python を使用して、Python と C 言語両方を活用する方法を説明しています。
本記事では、boost::multi_array を Python 側にエクスポートする方法を解説します。
こんなものがつくりたい
Python でデータを読み込み、C で計算を行い、Python で途中経過を出力し、Python で結果を出力するプログラムを目標とします。
これは、たとえば次のようなコードになります。
import mylib, wrap from matplotlib import pyplot as pl from scipy import array, float64 # データを読み込む x = array(..., dtype = float64) # boost::multi_array 配列を確保 # x は入力、y は出力とします wrap_x = wrap.make_vector(len(x)) wrap_y = wrap.make_vector(5) # データを C 言語側に転送 wrap.copy_vector(x, wrap_x) # (Python では不可能なほど大変な処理を) 実行します for i in xrange(100): # 処理をおこなう mylib.execute(wrap_x, wrap_y) # 途中結果を表示 y = wrap.scipy_vector(wrap_y) pl.plot(y) # 結果をとりだす y = wrap.scipy_vector(wrap_y)
mylib の中身は C 言語なので並列化などの最適化が容易です。
しかも、mylib の execute 関数にあたえる引数は boost::multi_array なので、実装もとても簡単です。
はじめに、wrap モジュールの概要を説明し、これを実装する方法を順番に紹介していきます。
おもな機能
wrap モジュールは大きく分けて、make (配列の確保)、reset (配列の初期化)、copy (SciPy -> C のデータ転送)、scipy (C -> SciPy のデータ転送) の 4 種類の機能を持ちます。
各関数はさらに、データ型と配列タイプをサフィックスとして持ちます。たとえば、reset_bool_matrix であれば、boolean 型の 2 次元配列を初期化する関数です。
さらに簡単のため、データ型が double の場合のみ型を省略することにします。
今回・次回合わせて、以下の関数の実装方法を説明します。
- make_vector
- make_matrix
- make_bool_vector
- make_bool_matrix
- reset_vector
- reset_matrix
- reset_bool_vector
- reset_bool_matrix
お急ぎの方は、ページ最後のソースコードをコピペして貼り付けるでも OK です。
共通モジュール
C 言語側でひろく利用する機能を、共通モジュールとして作り込みます。
ソースコードの名前は cmath.h とします。
インクルード、using 宣言
#include <boost/multi_array.hpp> #include <boost/shared_ptr.hpp> using boost::extents; using boost::multi_array; using boost::multi_array_ref; using boost::shared_ptr;
型宣言
以下の型は、C 言語モジュールで繰り返し使うので、短縮名を与えておきます。
Python とのやりとりには multi_array に対するスマートポインタを使用するため、こちらも同時に定義しておきます。
typedef multi_array<double, 1> _wrap_vector; typedef multi_array<double, 2> _wrap_matrix; typedef multi_array<bool, 1> _wrap_bool_vector; typedef multi_array<bool, 2> _wrap_bool_matrix; typedef shared_ptr<_wrap_vector> wrap_vector; typedef shared_ptr<_wrap_matrix> wrap_matrix; typedef shared_ptr<_wrap_bool_vector> wrap_bool_vector; typedef shared_ptr<_wrap_bool_matrix> wrap_bool_matrix;
dim, rdim
multi_array, multi_array_ref の次元サイズを取得するための補助関数を定義します。
template<class T> static inline size_t dim1(T x) { return x->size(); } template<class T> static inline size_t dim2(T x) { return (*x)[0].size(); } template<class T> static inline size_t rdim1(T x) { return x.size(); } template <class T> static inline size_t rdim2(T x) { return x[0].size(); }
rnelem
multi_array_ref が何要素のデータ領域を指しているかを取得する補助関数を定義します。
template<class T> static inline size_t rnelem(multi_array_ref<T, 1> x) { return rdim1(x); } template<class T> static inline size_t rnelem(multi_array_ref<T, 2> x) { return rdim1(x) * rdim2(x); }
ref 関数
C 言語側で、スマートポインタをはがすための関数を実装します。
template<class T> multi_array_ref<T, 1> ref(shared_ptr<multi_array<T, 1> > x) { multi_array_ref<T, 1> r(x->data(), extents[dim1(x)]); return r; } template<class T> multi_array_ref<T, 2> ref(shared_ptr<multi_array<T, 2> > x) { multi_array_ref<T, 2> r(x->data(), extents[dim1(x)][dim2(x)]); return r; }
ソースコード
今回はここまで。
cmath.h はこれで完成です。次回は wrap モジュールを組み立てます。
cmath.h
#include <boost/multi_array.hpp> #include <boost/shared_ptr.hpp> #ifndef PYLIB_CMATH_INCLUDED #define PYLIB_CMATH_INCLUDED namespace cmath { using boost::extents; using boost::multi_array; using boost::multi_array_ref; using boost::shared_ptr; typedef multi_array<double, 1> _wrap_vector; typedef multi_array<double, 2> _wrap_matrix; typedef multi_array<bool, 1> _wrap_bool_vector; typedef multi_array<bool, 2> _wrap_bool_matrix; typedef shared_ptr<_wrap_vector> wrap_vector; typedef shared_ptr<_wrap_matrix> wrap_matrix; typedef shared_ptr<_wrap_bool_vector> wrap_bool_vector; typedef shared_ptr<_wrap_bool_matrix> wrap_bool_matrix; // ========== ========== ========== ========== // // dim // // ========== ========== ========== ========== template<class T> static inline size_t dim1(T x) { return x->size(); } template<class T> static inline size_t dim2(T x) { return (*x)[0].size(); } // ========== ========== ========== ========== // // rdim // // ========== ========== ========== ========== template<class T> static inline size_t rdim1(T x) { return x.size(); } template <class T> static inline size_t rdim2(T x) { return x[0].size(); } // ========== ========== ========== ========== // // rnelem // // ========== ========== ========== ========== template<class T> static inline size_t rnelem(multi_array_ref<T, 1> x) { return rdim1(x); } template<class T> static inline size_t rnelem(multi_array_ref<T, 2> x) { return rdim1(x) * rdim2(x); } // ========== ========== ========== ========== // // ref // // ========== ========== ========== ========== template<class T> multi_array_ref<T, 1> ref(shared_ptr<multi_array<T, 1> > x) { multi_array_ref<T, 1> r(x->data(), extents[dim1(x)]); return r; } template<class T> multi_array_ref<T, 2> ref(shared_ptr<multi_array<T, 2> > x) { multi_array_ref<T, 2> r(x->data(), extents[dim1(x)][dim2(x)]); return r; } } #endif