简体   繁体   English

通过Cython将numpy数组传递给C ++方法并返回

[英]Passing and returning numpy arrays to C++ methods via Cython

There are lots of questions about using numpy in cython on this site, a particularly useful one being Simple wrapping of C code with cython . 在这个网站上有很多关于在cython中使用numpy的问题,一个特别有用的问题是使用cython 简单包装C代码

However, the cython/numpy interface api seems to have changed a bit , in particular with ensuring the passing of memory-contiguous arrays. 但是,cython / numpy接口api 似乎有所改变 ,特别是在确保内存连续数组的传递方面。

What is the best way to write a wrapper function in cython that: 在cython中编写包装函数的最佳方法是:

  • takes a numpy array that is likely but not necessarily contiguous 采用一个可能但不一定是连续的numpy数组
  • calls a C++ class method with the signature double* data_in, double* data_out 使用签名double* data_in, double* data_out调用C ++类方法
  • returns a numpy array of the double* that the method wrote to? 返回该方法写入的double*的numpy数组?

My try is below: 我的尝试如下:

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)

This code compiles but is not necessarily optimal. 此代码编译但不一定是最佳的。 Do you have any suggestions on improving the snippet above? 您对改进上面的代码段有什么建议吗?

and (2) throws and "np is not defined on line X_c = ... ") when calling it at runtime. (2)在运行时调用它时抛出并且“np未在X_c = ...行上定义”。 The exact testing code and error message are the following: 确切的测试代码和错误消息如下:

 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! 

You've basically got it right. 你基本上做对了。 First, hopefully optimization shouldn't be a big deal. 首先,希望优化不应该是一个大问题。 Ideally, most of the time is spent inside your C++ kernel, not in the cythnon wrapper code. 理想情况下,大部分时间都花在C ++内核中,而不是在cythnon包装器代码中。

There are a few stylistic changes you can make that will simplify your code. 您可以进行一些风格上的更改,以简化您的代码。 (1) Reshaping between 1D and 2D arrays is not necessary. (1)不需要在1D和2D阵列之间进行重塑。 When you know the memory layout of your data (C-order vs. fortran order, striding, etc), you can see the array as just a chunk of memory that you're going to index yourself in C++, so numpy's ndim doesn't matter on the C++ side -- it's just seeing that pointer. 当你知道你的数据的内存布局(C顺序与fortran顺序,跨越等)时,你可以看到数组只是你要用C ++索引自己的一块内存,所以numpy的ndim不会在C ++方面很重要 - 它只是看到了指针。 (2) Using cython's address-of operator & , you can get the pointer to the start of the array in a little cleaner way -- no explicit cast necessary -- using &X[0,0] . (2)使用cython的address-of运算符& ,您可以使用&X[0,0]以更清晰的方式获取指向数组开头的指针 - 无需显式&X[0,0]

So this is my edited version of your original snippet: 这是我原始代码段的编辑版本:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM