NumPy C-API: マルチイテレータで配列を飛び回る! `PyArray_MultiIter_GOTO()` 関数の使い方


void PyArray_MultiIter_GOTO(PyObject *multi, npy_intp *destination);

引数

  • destination: 各イテレータが移動するN次元座標を表す配列へのポインタ
  • multi: マルチイテレータオブジェクトへのポインタ

動作

  • いずれかのイテレータが指定された座標に到達できない場合、関数はエラーを返します。
  • すべてのイテレータが指定された座標に到達すると、関数は正常終了します。
  • 各座標値は、対応するイテレータが処理する配列要素のインデックスを表します。
  • PyArray_MultiIter_GOTO() 関数は、multi で指定されたマルチイテレータ内の各イテレータを、destination で指定されたN次元座標まで移動させます。

戻り値

なし

#include <numpy/arrayobject.h>

int main() {
  // 2つの配列を作成
  npy_intp dims1[] = {3, 4};
  npy_intp dims2[] = {4, 3};
  PyArrayObject *arr1 = PyArray_SimpleNewFromIntp(2, dims1, NPY_INT32);
  PyArrayObject *arr2 = PyArray_SimpleNewFromIntp(2, dims2, NPY_INT32);

  // マルチイテレータを作成
  PyArrayMultiIter *multi = PyArray_MultiIterNew(2, arr1, arr2);

  // 座標を指定
  npy_intp destination[] = {2, 1};

  // マルチイテレータ内の各イテレータを指定された座標まで移動
  PyArray_MultiIter_GOTO(multi, destination);

  // マルチイテレータ内の現在の要素を取得
  npy_int32 *data1 = (npy_int32 *)PyArray_MultiIter_DATA(multi, 0);
  npy_int32 *data2 = (npy_int32 *)PyArray_MultiIter_DATA(multi, 1);

  // 現在の要素の値を出力
  printf("arr1[%d, %d] = %d, arr2[%d, %d] = %d\n",
         multi->index[0], multi->index[1], *data1, multi->index[0], multi->index[1], *data2);

  // マルチイテレータを解放
  PyArray_MultiIter_Free(multi);

  // 配列を解放
  Py_DECREF(arr1);
  Py_DECREF(arr2);

  return 0;
}

この例では、2つの配列 arr1arr2 を作成し、マルチイテレータ multi を使用してそれらをイテレートします。その後、PyArray_MultiIter_GOTO() 関数を使用して、マルチイテレータ内の各イテレータを指定された座標 [2, 1] まで移動します。最後に、現在の要素の値を出力して、マルチイテレータと配列を解放します。

  • PyArray_MultiIter_GOTO() 関数は、NumPy C-APIの一部であり、高度な操作にのみ使用することをお勧めします。
  • 多くの場合、PyArray_MultiIter_NEXT() 関数を使用して、マルチイテレータ内の各イテレータを順次移動させる方が効率的です。
  • PyArray_MultiIter_GOTO() 関数は、マルチイテレータ内の各イテレータを個別に制御する必要がある場合に役立ちます。


マルチイテレータを使用して2つの配列の要素を足し合わせる

#include <numpy/arrayobject.h>

int main() {
  // 2つの配列を作成
  npy_intp dims1[] = {3, 4};
  npy_intp dims2[] = {4, 3};
  PyArrayObject *arr1 = PyArray_SimpleNewFromIntp(2, dims1, NPY_INT32);
  PyArrayObject *arr2 = PyArray_SimpleNewFromIntp(2, dims2, NPY_INT32);

  // 配列に値を割り当てる
  for (int i = 0; i < arr1->size; i++) {
    ((npy_int32 *)arr1->data)[i] = i;
  }
  for (int i = 0; i < arr2->size; i++) {
    ((npy_int32 *)arr2->data)[i] = i + 10;
  }

  // マルチイテレータを作成
  PyArrayMultiIter *multi = PyArray_MultiIterNew(2, arr1, arr2);

  // 座標を指定
  npy_intp destination[] = {1, 2};

  // マルチイテレータ内の各イテレータを指定された座標まで移動
  PyArray_MultiIter_GOTO(multi, destination);

  // 対応する要素を足し合わせて結果を出力
  npy_int32 *data1 = (npy_int32 *)PyArray_MultiIter_DATA(multi, 0);
  npy_int32 *data2 = (npy_int32 *)PyArray_MultiIter_DATA(multi, 1);
  printf("arr1[%d, %d] + arr2[%d, %d] = %d\n",
         multi->index[0], multi->index[1], multi->index[0], multi->index[1], *data1 + *data2);

  // マルチイテレータを解放
  PyArray_MultiIter_Free(multi);

  // 配列を解放
  Py_DECREF(arr1);
  Py_DECREF(arr2);

  return 0;
}

この例では、マスクされた配列 mask を作成し、マルチイテレータを使用して arr1arr2 の要素を処理します。その後、PyArray_MultiIter_GOTO() 関数を使用して、マルチイテレータ内の各イテレータを指定された座標まで移動します。最後に、マスクされた要素のみを処理し、結果を出力します。

#include <numpy/arrayobject.h>

int main() {
  // 3つの配列を作成
  npy_intp dims1[] = {3, 4};
  npy_intp dims2[] = {4, 3};
  npy_intp dims3[] = {3, 4};
  PyArrayObject *arr1 = PyArray_SimpleNewFromIntp(2, dims1, NPY_INT32);
  PyArrayObject *arr2 = PyArray_SimpleNewFromIntp(2, dims2, NPY_INT32);
  PyArrayObject *mask = PyArray_SimpleNewFromIntp(2, dims3, NPY_BOOL);

  // 配列に値を割り当てる
  for (int i = 0; i < arr1->size; i++) {
    ((npy_int32 *)arr1->data)[i] = i;
  }
  for (int i = 0; i < arr2->size; i++) {
    ((npy_int32 *)arr2->data)[i] = i + 10;
  }
  for (int i = 0; i < mask->size; i++) {
    ((char *)mask->data)[i] = (i % 2 == 0);
  }

  // マルチイテ


代替方法

  1. PyArray_MultiIter_NEXT() 関数を使用する

PyArray_MultiIter_NEXT() 関数は、マルチイテレータ内の各イテレータを次の要素に移動させます。この関数を繰り返し呼び出すことで、マルチイテレータ内のすべての要素を順次処理することができます。

PyArrayMultiIter *multi = PyArray_MultiIterNew(2, arr1, arr2);

while (PyArray_MultiIter_NEXT(multi)) {
  // マルチイテレータ内の現在の要素を取得
  npy_int32 *data1 = (npy_int32 *)PyArray_MultiIter_DATA(multi, 0);
  npy_int32 *data2 = (npy_int32 *)PyArray_MultiIter_DATA(multi, 1);

  // 現在の要素を処理
  printf("arr1[%d, %d] = %d, arr2[%d, %d] = %d\n",
         multi->index[0], multi->index[1], *data1, multi->index[0], multi->index[1], *data2);
}

PyArray_MultiIter_Free(multi);
  1. ループを使用して各イテレータを個別に制御する

各イテレータを個別に制御する必要がある場合は、ループを使用して各イテレータを指定された座標まで移動させることができます。

for (int i = 0; i < multi->itersize; i++) {
  // イテレータを指定された座標まで移動
  PyArray_Iter_GOTO(multi->iters[i], destination[i]);
}
  1. 条件付き処理を使用して必要な要素のみを処理する

必要な要素のみを処理する必要がある場合は、条件付き処理を使用してマルチイテレータ内の要素を検査することができます。

PyArrayMultiIter *multi = PyArray_MultiIterNew(2, arr1, arr2);

while (PyArray_MultiIter_NEXT(multi)) {
  // マルチイテレータ内の現在の要素を取得
  npy_int32 *data1 = (npy_int32 *)PyArray_MultiIter_DATA(multi, 0);
  npy_int32 *data2 = (npy_int32 *)PyArray_MultiIter_DATA(multi, 1);

  // 必要な条件を満たす場合のみ処理
  if (/* 条件 */) {
    printf("arr1[%d, %d] = %d, arr2[%d, %d] = %d\n",
           multi->index[0], multi->index[1], *data1, multi->index[0], multi->index[1], *data2);
  }
}

PyArray_MultiIter_Free(multi);
  • 条件付き処理を使用して必要な要素のみを処理する
    • 利点: 効率的
    • 欠点: コードが複雑になる
  • ループを使用して各イテレータを個別に制御する
    • 利点: 柔軟性が高い
    • 欠点: コードが複雑になる
  • PyArray_MultiIter_NEXT() 関数を使用する
    • 利点: シンプルでわかりやすい
    • 欠点: 順番にしか処理できない