16
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

numpy.ndarrayをc++から操作する小技 -イテレータを使いたい-

Posted at

#目的
boost.pythonやboost.numpyを利用することで、c++でnumpy.ndarrayを扱えるpythonのモジュールを簡単に作成することができます。
boost.numpyは良くできたライブラリですが、イテレータが使えないなどデータへのアクセスにやや不便な点があります。そのため、データへ簡単にアクセスできるようにします。

背景

numpy.ndarrayとは

pythonのパッケージの一つnumpyが提供する多次元配列です。これなしにpythonでの数値計算があり得ないくらい広く使われています。

boost.numpyとは

numpyをc++から操作できるようにするライブラリです。以下のような感じでデータにアクセスします。

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

void func(np::ndarray &data2d) {
  // data2d は二次元配列としてi,j要素に0を代入
  for (int i = 0; i < data2d.shape(0); ++i) {
    for (int j = 0; j < data2d.shape(1); ++j) {
      data2d[bp::make_tuple(i, j)] = 0.0;
    }
  }
}

小技の条件

この小技を利用するためには、以下の条件が満たされる必要があります。

  • 配列の次元数、要素型がコンパイル時定数である
  • numpy.ndarrayのデータのメモリが連続である

一つ目の条件は多くの場合で満たされるを思います。また、二つ目に関しても、元々は大きな配列から一部を切り出した変数でない限りは、満たされるはずです。

イテレータを使えるようにする

利用するのはboost.multi_array_refです。
numpy.ndarrayがpythonの多次元配列なので、c++でも多次元配列のライブラリを利用します。
boost.multi_array_refはboost.multi_arrayの自分でメモリを確保しないバージョンです。boost.multi_arrayの詳しい説明はこちらをどうぞ。(boost::multi_array - Kmonos.net) ちなみに、boost.const_multi_array_refという読み込みのみで、書き込みできないバージョンもあります。

具体例

void func(np:ndarray &data2d) {
  const std::array<int, 2> shape = {data2d.shape(0), data2d.shape(1)};
  boost::multi_array_ref<double, 2> wrapper(reinterpret_cast<double*>(data2d.get_data()), shape);
  // i,j要素へはwrapper[i][j]でアクセスできるがイテレータを利用
  for (boost::multi_array_ref_double, 2>::subarray<1>::type &&sub : wrapper) {
    boost::fill(sub, 0.0);
  }
}

そうすると、こんな感じにアクセスできます。

もちろん、boost.multi_array_refを利用することによるオーバーヘッドはあります。また、ポインタを直接操作する方法(C++でPythonを拡張するためのBoost.NumPyチュートリアル(実践編))もありますが、多少のオーバヘッドは気にせず安全策でいきたいと思います。

ちなみに、上記の例ではわかりやすさや、紹介の意味もあるのでboost::multi_array_ref<double, 2>と2次元配列のラッパーを用意していますが、

const std::array<int, 1> shape = {data2d.shape(0) * data2d.shape(1)};
  boost::multi_array_ref<double, 1> wrapper(reinterpret_cast<double*>(data2d.get_data()), shape); 
boost::fill(wrapper, 0.0);

とすることで、一発で処理が完了します。

最後に

やっぱりここでもboostライブラリは頼りになります。
他にいい方法(boost.multi_array_ref以外のライブラリ、boost.numpyに代わる何かなど)をご存知の方は、是非ともお知らせください。

16
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?