NumPy C-API: `int PyArray_ISFLEXIBLE()` 関数詳細解説


PyArray_ISFLEXIBLE() は、NumPy C-API において、NumPy 配列が柔軟性を持つかどうかを判定する関数です。柔軟な配列とは、形状を変更できる配列を指します。

関数引数

  • pyarr: 判定対象の NumPy 配列へのポインタ

戻り値

  • 配列が柔軟でない場合: 0
  • 配列が柔軟な場合: 1

詳細解説

NumPy 配列は、データ型と形状情報を持つ多次元データ構造です。形状情報は、各次元における要素数を表す整数のリストとして定義されます。

NumPy 配列には、2 種類の柔軟性があります。

  1. 形状変更可能
    PyArray_SHAPE() 関数を使用して、形状を変更できる配列
  2. コンティグアス
    メモリ上の要素が連続して配置されている配列

PyArray_ISFLEXIBLE() 関数は、上記の 2 種類の柔軟性を区別せず、形状変更が可能かどうかのみを判定します。コンティグアスな配列であっても、形状変更が不可能な場合は、この関数は 0 を返します。

#include <numpy/ndarray.h>

int main() {
  // 1 次元の固定形状配列を作成
  npy_intp shape[] = {5};
  PyArrayObject *arr = PyArray_SimpleNew(NDIM(shape), NPY_INT32, shape);

  // 配列が柔軟かどうか判定
  int is_flexible = PyArray_ISFLEXIBLE(arr);

  // 結果を出力
  if (is_flexible) {
    printf("配列は柔軟です\n");
  } else {
    printf("配列は柔軟ではありません\n");
  }

  // メモリ解放
  Py_DECREF(arr);

  return 0;
}

この例では、1 次元の固定形状配列を作成し、PyArray_ISFLEXIBLE() 関数を使用して柔軟性を判定しています。固定形状配列は形状変更が不可能なため、この関数は 0 を返します。

  • PyArray_ISCARGO() 関数は、配列がコピーが必要かどうかを判定します。
  • PyArray_ISCONTIGUOUS() 関数は、配列がコンティグアスかどうかを判定します。


例 1: 固定形状配列

#include <numpy/ndarray.h>

int main() {
  // 1 次元の固定形状配列を作成
  npy_intp shape[] = {5};
  PyArrayObject *arr = PyArray_SimpleNew(NDIM(shape), NPY_INT32, shape);

  // 配列が柔軟かどうか判定
  int is_flexible = PyArray_ISFLEXIBLE(arr);

  // 結果を出力
  printf("配列は柔軟: %d\n", is_flexible);

  // メモリ解放
  Py_DECREF(arr);

  return 0;
}

説明

例 2: 1 次元可変形状配列

#include <numpy/ndarray.h>

int main() {
  // 1 次元の可変形状配列を作成
  npy_intp shape[] = {0};  // 最初の長さは 0
  PyArrayObject *arr = PyArray_SimpleNew(NDIM(shape), NPY_INT32, shape);

  // 配列の長さを 5 に変更
  PyArray_SETSIZE(arr, 5);

  // 配列が柔軟かどうか判定
  int is_flexible = PyArray_ISFLEXIBLE(arr);

  // 結果を出力
  printf("配列は柔軟: %d\n", is_flexible);

  // メモリ解放
  Py_DECREF(arr);

  return 0;
}

説明

この例では、1 次元の可変形状配列を作成し、PyArray_SETSIZE() 関数を使用して長さを 5 に変更します。その後、PyArray_ISFLEXIBLE() 関数を使用して柔軟性を判定しています。可変形状配列は形状変更が可能なので、この関数は 1 を返します。

例 3: 2 次元固定形状配列

#include <numpy/ndarray.h>

int main() {
  // 2 次元の固定形状配列を作成
  npy_intp shape[2] = {2, 3};
  PyArrayObject *arr = PyArray_SimpleNew(NDIM(shape), NPY_INT32, shape);

  // 配列が柔軟かどうか判定
  int is_flexible = PyArray_ISFLEXIBLE(arr);

  // 結果を出力
  printf("配列は柔軟: %d\n", is_flexible);

  // メモリ解放
  Py_DECREF(arr);

  return 0;
}

説明

例 4: 2 次元可変形状配列

#include <numpy/ndarray.h>

int main() {
  // 2 次元の可変形状配列を作成
  npy_intp shape[2] = {0, 0};  // 最初のサイズは (0, 0)
  PyArrayObject *arr = PyArray_SimpleNew(NDIM(shape), NPY_INT32, shape);

  // 配列のサイズを (3, 4) に変更
  npy_intp new_dims[2] = {3, 4};
  PyArray_Dims dim;
  dim.ptr = new_dims;
  dim.len = 2;
  PyArray_Resize(arr, &dim, NPY_SAME_ORDER);

  // 配列が柔軟かどうか判定
  int is_flexible = PyArray_ISFLEXIBLE(arr);

  // 結果を出力
  printf("配列は柔軟: %d\n", is_flexible);

  // メモリ解放
  Py_DECREF(arr);

  return 0;
}


この関数は、NumPy C-API の一部であり、比較的低レベルな操作です。代替手段として、以下の方法が考えられます。

PyArray_Shape() 関数と PyArray_NDIM() 関数

  • PyArray_NDIM() 関数は、NumPy 配列の次元数を取得します。
  • PyArray_Shape() 関数は、NumPy 配列の形状情報を取得します。

これらの関数を組み合わせて、配列が固定形状かどうかを判定できます。固定形状配列は形状変更が不可能なので、柔軟ではありません。

#include <numpy/ndarray.h>

int is_flexible(PyArrayObject *arr) {
  // 配列の次元数を取得
  int ndim = PyArray_NDIM(arr);

  // 各次元の長さを確認
  for (int i = 0; i < ndim; i++) {
    npy_intp dim_size = PyArray_SHAPE(arr)[i];
    if (dim_size == 0) {
      // 0 長さの次元が存在する場合は柔軟
      return 1;
    }
  }

  // 0 長さの次元が存在しない場合は固定形状
  return 0;
}

PyArray_FLAGS() 属性

  • NPY_ARRAY_CARGO フラグは、配列がコピーが必要かどうかを示します。
  • NPY_ARRAY_CONTIGUOUS フラグは、配列がコンティグアスかどうかを示します。
  • PyArray_FLAGS() 属性は、NumPy 配列のフラグ情報を取得します。

これらのフラグを組み合わせて、配列が柔軟かどうかを判定できます。コンティグアスな配列は形状変更が不可能な場合があるので、柔軟ではありません。また、コピーが必要な配列は形状変更が不可能な場合があるので、柔軟ではありません。

#include <numpy/ndarray.h>

int is_flexible(PyArrayObject *arr) {
  // 配列のフラグを取得
  NPY_BITWISE_OR flags = PyArray_FLAGS(arr);

  // コンティグアスかどうか確認
  if (flags & NPY_ARRAY_CONTIGUOUS) {
    // コンティグアスな配列は固定形状
    return 0;
  }

  // コピーが必要かどうか確認
  if (flags & NPY_ARRAY_CARGO) {
    // コピーが必要な配列は固定形状
    return 0;
  }

  // コンティグアスでもコピー不要でもなければ柔軟
  return 1;
}

PyArray_CanCastTo() 関数

  • PyArray_CanCastTo() 関数は、ある NumPy 配列を別の NumPy 配列にキャストできるかどうかを判定します。

形状変更が可能な配列は、形状を変更せずに別の配列にキャストできる可能性が高いです。一方、形状変更が不可能な配列は、形状を変更せずに別の配列にキャストできない可能性が高いです。

#include <numpy/ndarray.h>

int is_flexible(PyArrayObject *arr) {
  // 1 次元の固定形状配列を作成
  npy_intp shape[] = {1};
  PyArrayObject *new_arr = PyArray_SimpleNew(NDIM(shape), NPY_INT32, shape);

  // キャストが可能かどうか確認
  int can_cast = PyArray_CanCastTo(arr, new_arr, NPY_SAME_KIND);

  // メモリ解放
  Py_DECREF(new_arr);

  // キャストが可能であれば柔軟
  return can_cast;
}
  • 代替方法は、PyArray_ISFLEXIBLE() 関数と同じ結果を保証するわけではありません。
  • 上記の代替方法は、PyArray_ISFLEXIBLE() 関数よりも低速な場合があります。
  • PyArray_FLAGS() 属性: