web-dev-qa-db-ja.com

Cythonを介してnumpy配列をC ++メソッドに渡したり返したりする

このサイトのcythonでnumpyを使用することについては多くの質問がありますが、特に役立つのは Cコードをcythonで単純にラップする です。

ただし、cython/numpyインターフェイスapi 少し変更されたようです 、特にメモリに隣接する配列の受け渡しを確実にする場合。

Cythonでラッパー関数を書くための最良の方法は何ですか?

  • 可能性が高いが必ずしも連続しているとは限らないnumpy配列を取ります
  • 署名double* data_in, double* data_outを使用してC++クラスメソッドを呼び出します
  • メソッドが書き込んだdouble*のnumpy配列を返しますか?

私の試みは以下の通りです:

cimport numpy as np
import numpy as np # as suggested by jorgeca

cdef extern from "myclass.h":
    cdef cppclass MyClass:
        MyClass() except +
        void run(double* X, int N, int D, double* Y)

def run(np.ndarray[np.double_t, ndim=2] X):
    cdef int N, D
    N = X.shape[0]
    D = X.shape[1]

    cdef np.ndarray[np.double_t, ndim=1, mode="c"] X_c
    X_c = np.ascontiguousarray(X, dtype=np.double)

    cdef np.ndarray[np.double_t, ndim=1, mode="c"] Y_c
    Y_c = np.ascontiguousarray(np.zeros((N*D,)), dtype=np.double)

    cdef MyClass myclass
    myclass = MyClass()
    myclass.run(<double*> X_c.data, N, D, <double*> Y_c.data)

    return Y_c.reshape(N, 2)

このコードはコンパイルされますが、必ずしも最適ではありません。上記のスニペットを改善するための提案はありますか?

(2)実行時に呼び出すと、「npは行X_c = ...で定義されていません」)をスローします。正確なテストコードとエラーメッセージは次のとおりです。

import numpy as np
import mywrapper
mywrapper.run(np.array([[1,2],[3,4]], dtype=np.double))

# NameError: name 'np' is not defined [at mywrapper.pyx":X_c = ...]
# fixed!
25

あなたは基本的にそれを正しく持っています。まず、うまくいけば、最適化は大したことではないはずです。理想的には、ほとんどの時間はcythnonラッパーコードではなく、C++カーネル内で費やされます。

コードを簡素化するために行うことができるいくつかのスタイルの変更があります。 (1)1Dアレイと2Dアレイ間の再形成は必要ありません。データのメモリレイアウト(CオーダーとFortranオーダー、ストライドなど)がわかっている場合、配列はC++でインデックスを作成するメモリのチャンクとして表示されるため、numpyのndimはC++側では問題ではありません-それはそのポインタを見ているだけです。 (2)cythonのアドレス演算子&を使用すると、&X[0,0]を使用して、配列の先頭へのポインターを少しわかりやすく(明示的なキャストは不要)取得できます。

これが、元のスニペットを編集したバージョンです。

cimport numpy as np
import numpy as np

cdef extern from "myclass.h":
    cdef cppclass MyClass:
        MyClass() except +
        void run(double* X, int N, int D, double* Y)

def run(np.ndarray[np.double_t, ndim=2] X):
    X = np.ascontiguousarray(X)
    cdef np.ndarray[np.double_t, ndim=2, mode="c"] Y = np.zeros_like(X)

    cdef MyClass myclass
    myclass = MyClass()
    myclass.run(&X[0,0], X.shape[0], X.shape[1], &Y[0,0])

    return Y
17