ufunc

ufunc またはユニバーサル関数は ndarray を要素ごとに操作する。またブロードキャスト1、型キャスト、その他機能をサポートする。

二項及び単項の ufunc メソッドについて使い方を見ていこう。

必要なファイルのインクルードを行った後、

#include <boost/python/numpy.hpp>
#include <iostream>

namespace p = boost::python;
namespace np = boost::python::numpy;

ufunc の実装に必要な構造体を作成する。typedef は、ufunc 生成器がこれらの typedef を入力にとりそれ以外の場合にエラーを返すようにしなければならない

struct UnarySquare
{
  typedef double argument_type;
  typedef double result_type;

  double operator()(double r) const { return r * r;}
};

struct BinarySquare
{
  typedef double first_argument_type;
  typedef double second_argument_type;
  typedef double result_type;

  double operator()(double a,double b) const { return (a*a + b*b) ; }
};

Python ランタイムと numpy モジュールを初期化する。

int main(int argc, char **argv)
{
  Py_Initialize();
  np::initialize();

UnarySquare 構造体をクラスとして Python にエクスポートし、ud をクラスオブジェクトとする。

p::object ud = p::class_<UnarySquare, boost::shared_ptr<UnarySquare> >("UnarySquare");
ud.def("__call__", np::unary_ufunc<UnarySquare>::make());

instud クラスのインスタンスとする。

p::object inst = ud();

__call__ メソッドを使って多重定義した () 演算子を呼び出し、値を印字する。

std::cout << "単項スカラ 1.0 の正方行列は " << p::extract<char const *>(p::str(inst.attr("__call__")(1.0))) << std::endl;

C++ で配列を作成する。

int arr[] = {1,2,3,4} ;

この配列を使って、Python で ndarray を作成する。

np::ndarray demo_array = np::from_data(arr, np::dtype::get_builtin<int>(),
                                       p::make_tuple(4),
                                       p::make_tuple(4),
                                       p::object());

このデモ配列を印字する。

std::cout << "デモ配列は " << p::extract<char const *>(p::str(demo_array)) << std::endl;

__call__ メソッドを呼び出して、演算と result_array に代入する。

p::object result_array = inst.attr("__call__")(demo_array);

結果の配列を印字する。

std::cout << "デモ配列の正方行列は " << p::extract<char const *>(p::str(result_array)) << std::endl;

リストで同じことをしてみよう。

p::list li;
li.append(3);
li.append(7);

デモリストを印字する。

std::cout << "デモリストは " << p::extract<char const *>(p::str(li)) << std::endl;

このリストに対して ufunc を呼び出す。

result_array = inst.attr("__call__")(li);

そしてリストを印字する。

std::cout << "デモリストの正方行列は " << p::extract<char const *>(p::str(result_array)) << std::endl;

今度は二項 ufunc を試してみよう。同様に BinarySquare 構造体をクラスとして Python へエクスポートし、ud をクラスオブジェクトとする。

ud = p::class_<BinarySquare, boost::shared_ptr<BinarySquare> >("BinarySquare");
ud.def("__call__", np::binary_ufunc<BinarySquare>::make());

そして ud を初期化する。

inst = ud();

2 つの入力リストを印字する。

std::cout << "二項 ufunc に対する 2 つの入力リストは " << std::endl
          << p::extract<char const *>(p::str(demo_array)) << std::endl
          << p::extract<char const *>(p::str(demo_array)) << std::endl;

二項 ufunc を呼び出し、両方の入力として demo_array を与える。

result_array = inst.attr("__call__")(demo_array,demo_array);

最後に出力を印字する。

  std::cout << "リストと二項 ufunc の正方行列は " << p::extract<char const *>(p::str(result_array)) << std::endl;
}
1

訳注:型や次元の異なる ndarray 同士を計算する仕組み