NumPy C-API: 高速な要素ごとの掛け合わせを実現! `PyArray_MultiplyList()` 関数の実装とサンプルコード


npy_intp PyArray_MultiplyList() は NumPy C-API の関数で、複数の配列を要素ごとに掛け合わせ、結果を新しい配列に格納します。これは、要素ごとの乗算操作を効率的に実行したい場合に役立ちます。

引数

  • out: 結果配列を格納する配列
  • dtype: 結果配列のデータ型
  • dimensions: 結果配列の各次元のサイズ
  • ndims: 結果配列の次元数
  • arrays: 掛け合わせる配列のリスト
  • n: 掛け合わせる配列の数

戻り値

成功した場合は、結果配列へのポインタが返されます。失敗した場合は NULL が返されます。

詳細

PyArray_MultiplyList() は、以下の処理を実行します。

  1. 各配列の要素数を比較します。要素数が異なる配列があれば、エラーが発生します。
  2. 結果配列のサイズを計算します。
  3. 結果配列を割り当てます。
  4. 各配列の要素を順にループし、要素ごとに掛け合わせます。
  5. 掛け合わせた結果を結果配列に格納します。
  6. 結果配列へのポインタを返します。

#include <numpy/arrayobject.h>

int main() {
  // 3つの配列を準備する
  npy_intp dims[2] = {2, 3};
  int array1[6] = {1, 2, 3, 4, 5, 6};
  int array2[6] = {2, 3, 4, 5, 6, 7};
  int array3[6] = {3, 4, 5, 6, 7, 8};

  // 結果配列を割り当てる
  PyArrayObject *result = PyArray_MultiplyList(3, (PyArrayObject **)&array1, 2, dims, NPY_INT32, NULL);

  // 結果配列の内容を出力する
  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
      printf("%d ", ((int *)PyArray_GetPtr(result))[i * 3 + j]);
    }
    printf("\n");
  }

  // 結果配列を解放する
  Py_DECREF(result);

  return 0;
}

この例では、3つの配列 array1, array2, array3 を要素ごとに掛け合わせ、結果を result 配列に格納します。result 配列の内容は以下のようになります。

6 12 18
15 24 33
  • 入力配列と結果配列のデータ型は一致する必要があります。
  • 結果配列は、関数内で割り当てられるため、呼び出し側で解放する必要があります。
  • PyArray_MultiplyList() は C-API 関数であり、Python コードから直接呼び出すことはできません。


#include <numpy/arrayobject.h>

int main() {
  // 3つの配列を準備する
  npy_intp dims[2] = {2, 3};
  int array1[6] = {1, 2, 3, 4, 5, 6};
  int array2[6] = {2, 3, 4, 5, 6, 7};
  int array3[6] = {3, 4, 5, 6, 7, 8};

  // 結果配列を割り当てる
  PyArrayObject *result = PyArray_MultiplyList(3, (PyArrayObject **)&array1, 2, dims, NPY_INT32, NULL);

  // 結果配列を NumPy 配列に変換する
  npy_ndarray *ndarray = PyArray_AsNumpyArray(result, NULL, NULL);

  // 結果配列の内容を出力する
  for (int i = 0; i < ndarray->ndims; i++) {
    printf("Dimension %d: ", i);
    for (int j = 0; j < ndarray->dimensions[i]; j++) {
      printf("%d ", ((int *)ndarray->data)[i * ndarray->strides[0] / sizeof(int) + j]);
    }
    printf("\n");
  }

  // 結果配列を解放する
  Py_DECREF(ndarray);
  Py_DECREF(result);

  return 0;
}

このコードでは、PyArray_AsNumpyArray() 関数を使用して、result 配列を NumPy 配列に変換しています。これにより、NumPy の関数を使用して結果配列を操作することができます。

  1. PyArray_MultiplyList() 関数を使用して、3つの配列 array1, array2, array3 を要素ごとに掛け合わせます。
  2. 結果配列 result が返されます。
  3. PyArray_AsNumpyArray() 関数を使用して、result 配列を NumPy 配列 ndarray に変換します。
  4. ndarray の各次元をループし、要素の内容を出力します。
  5. Py_DECREF() 関数を使用して、ndarrayresult 配列を解放します。
  • 他の NumPy 関数を使用して、結果配列を操作することもできます。
  • 結果配列のデータ型は、必要に応じて変更できます。
  • このコードは、NumPy C-API と NumPy 関数の両方を使いこなす方法を示しています。


代替方法

  • ufunc.reduceat()
    NumPy の ufunc モジュールには、reduceat 関数があり、複数の配列を指定した軸に沿って集約することができます。要素ごとの乗算を実行するには、乗算演算子 (*) を reduceat 関数に渡します。
import numpy as np

array1 = np.array([1, 2, 3, 4, 5, 6])
array2 = np.array([2, 3, 4, 5, 6, 7])
array3 = np.array([3, 4, 5, 6, 7, 8])

result = np.ufunc.reduceat(np.multiply, [array1, array2, array3], np.arange(len(array1)))

print(result)

上記のコードは、PyArray_MultiplyList() と同じ結果を出力します。

  • ループ
    単純なループを使用して、要素ごとに掛け合わせを実行することもできます。
import numpy as np

array1 = np.array([1, 2, 3, 4, 5, 6])
array2 = np.array([2, 3, 4, 5, 6, 7])
array3 = np.array([3, 4, 5, 6, 7, 8])

result = np.empty_like(array1)

for i in range(len(array1)):
  result[i] = array1[i] * array2[i] * array3[i]

print(result)

選択

どの方法を選択するかは、以下の要素によって異なります。

  • コードの簡潔さ
    ufunc.reduceat() はコードを簡潔に記述することができますが、ループはより分かりやすい場合があります。
  • 必要な機能
    PyArray_MultiplyList() は、結果配列を指定したデータ型で作成することができます。一方、ufunc.reduceat() とループは、入力配列と同じデータ型の結果配列を作成します。
  • 配列のサイズ
    配列が大きい場合、ufunc.reduceat() はループよりも効率的になる可能性があります。