Pythonエンジニア必見!NumPy C-API `PyArray_CGT()` 関数で共役転置を極める


PyArray_CGT() は、NumPy C-API における重要な関数の一つであり、複素数配列の共役転置を取得するために使用されます。

説明

PyArray_CGT() 関数は、入力として PyArrayObject * 型の複素数配列を受け取り、その共役転置を返す PyArrayObject * 型の配列を生成します。

共役転置とは、複素数の符号を反転し、実部と虚部を入れ替える操作です。

#include <numpy/arrayobject.h>

int main() {
  // 複素数配列を作成
  npy_intp dims[] = {2, 3};
  PyArrayObject *arr = PyArray_ZEROS(2, dims, NPY_CDOUBLE);

  // 配列の要素に値を設定
  double real[] = {1, 2, 3, 4, 5, 6};
  double imag[] = {7, 8, 9, 10, 11, 12};
  PyArray_FillWithScalar(arr, (void*)real, NPY_REAL);
  PyArray_FillWithScalar(arr, (void*)imag, NPY_IMAG);

  // 共役転置を取得
  PyArrayObject *cgt = PyArray_CGT(arr);

  // 共役転置の要素を表示
  for (int i = 0; i < PyArray_NDIM(cgt); i++) {
    for (int j = 0; j < PyArray_DIMS(cgt)[i]; j++) {
      npy_cdouble *ptr = (npy_cdouble*)PyArray_GetPtr(cgt, i, j);
      printf("(%f, %f) ", ptr->real, ptr->imag);
    }
    printf("\n");
  }

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

  return 0;
}

出力例

(1, -7) (2, -8) (3, -9)
(4, -10) (5, -11) (6, -12)
  • 共役転置を取得した配列は、入力配列とは別のメモリ領域に割り当てられます。そのため、共役転置を取得した配列を使用する前に、Py_DECREF() でメモリ解放を行う必要があります。
  • PyArray_CGT() は、入力配列が NPY_CDOUBLE 型であることを前提としています。他の複素数型配列を使用する場合は、適切な型変換を行う必要があります。


#include <numpy/arrayobject.h>

int main() {
  // 複素数配列を作成
  npy_intp dims[] = {2, 2};
  PyArrayObject *arr = PyArray_ZEROS(2, dims, NPY_CDOUBLE);

  // 配列の要素に値を設定
  double real[] = {1, 2, 3, 4};
  double imag[] = {5, 6, 7, 8};
  PyArray_FillWithScalar(arr, (void*)real, NPY_REAL);
  PyArray_FillWithScalar(arr, (void*)imag, NPY_IMAG);

  // 共役転置を取得
  PyArrayObject *cgt = PyArray_CGT(arr);

  // 共役転置の要素を表示
  for (int i = 0; i < PyArray_NDIM(cgt); i++) {
    for (int j = 0; j < PyArray_DIMS(cgt)[i]; j++) {
      npy_cdouble *ptr = (npy_cdouble*)PyArray_GetPtr(cgt, i, j);
      printf("(%f, %f) ", ptr->real, ptr->imag);
    }
    printf("\n");
  }

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

  return 0;
}

説明

この例では、2x2 の複素数配列を作成し、その共役転置を取得して要素を表示しています。

例 2: 条件付きで共役転置を取得する

#include <numpy/arrayobject.h>

int main() {
  // 複素数配列を作成
  npy_intp dims[] = {2, 2};
  PyArrayObject *arr = PyArray_ZEROS(2, dims, NPY_CDOUBLE);

  // 配列の要素に値を設定
  double real[] = {1, 2, 3, 4};
  double imag[] = {5, 6, 7, 8};
  PyArray_FillWithScalar(arr, (void*)real, NPY_REAL);
  PyArray_FillWithScalar(arr, (void*)imag, NPY_IMAG);

  // 共役転置を取得するかどうかを判定
  int is_transpose = 1; // 1: 共役転置を取得, 0: 共役転置を取得しない

  PyArrayObject *cgt = NULL;
  if (is_transpose) {
    cgt = PyArray_CGT(arr);
  } else {
    cgt = PyArray_View(arr, NULL, NULL);
  }

  // 共役転置の要素を表示
  for (int i = 0; i < PyArray_NDIM(cgt); i++) {
    for (int j = 0; j < PyArray_DIMS(cgt)[i]; j++) {
      npy_cdouble *ptr = (npy_cdouble*)PyArray_GetPtr(cgt, i, j);
      printf("(%f, %f) ", ptr->real, ptr->imag);
    }
    printf("\n");
  }

  // メモリ解放
  Py_DECREF(arr);
  Py_XDECREF(cgt); // 共役転置を取得しなかった場合は、`Py_DECREF()` ではなく `Py_XDECREF()` を使用する

  return 0;
}

説明

この例では、is_transpose 変数の値によって、共役転置を取得するかどうかを判定しています。

#include <numpy/arrayobject.h>

int main() {
  // 複素数配列を作成
  npy_intp dims[] = {2, 2};
  PyArrayObject *arr = PyArray_ZEROS(2, dims, NPY_CDOUBLE);

  // 配列の要素に値を設定
  double real[] = {1, 2, 3, 4};
  double imag[] = {5, 6, 7, 8};
  PyArray_FillWithScalar(arr, (void*)real, NPY_REAL);
  PyArray_FillWithScalar(arr, (


代替方法

  • conj() 関数
    • NumPy の numpy.conj() 関数を使用すると、複素数スカラーや配列の共役を計算できます。
    • PyArray_CGT() と異なり、conj() は入力配列を変更しません。
    • 以下は、conj() 関数を使用して共役転置を取得する例です。
import numpy as np

# 複素数配列を作成
arr = np.array([[1 + 2j, 3 + 4j], [5 + 6j, 7 + 8j]], dtype=np.complex128)

# 共役転置を取得
cgt = np.conj(arr.T)

# 共役転置の要素を表示
print(cgt)
  • 手動で計算
    • 共役転置は、以下の式で手動で計算できます。
cgt[i, j] = conj(arr[j, i])
* この方法は、より高度な制御が可能ですが、コードが複雑になる可能性があります。
  • 柔軟性
    • 手動で計算する場合は、より高度な制御が可能ですが、コードが複雑になる可能性があります。
  • パフォーマンス
    • 計算量が多い場合は、PyArray_CGT() の方が高速な場合があります。
    • ただし、conj() 関数も十分に高速であり、多くの場合でパフォーマンス上の違いは顕著ではありません。
  • シンプルさ
    • conj() 関数は、簡潔でわかりやすいコードを書くために適しています。
  • 詳細については、NumPy のドキュメントを参照してください。
  • 上記以外にも、状況によっては np.einsum()np.vconj() などの関数を使用することもできます。