将Cython中的numpy数组传递给需要动态分配数组的C函数

我有一些C代码具有以下声明:

int myfunc(int m, int n, const double **a, double **b, double *c); 

所以a是一个常数的二维数组, b是一个二维数组, c是一维数组,都是动态分配的。 bc在传递给myfunc之前不需要任何具体的东西,应该被理解为输出信息。 出于这个问题的目的, 我不允许更改myfunc的声明。

问题1:如何将给定的numpy数组a_np转换为具有此C函数所需格式的数组a ,以便我可以在Cython中使用?调用此C函数?

问题2:下面的bc的声明是否正确,或者它们是否需要采用其他格式才能使C函数将其理解为2D和1D数组(分别)?

我的尝试:

myfile.pxd

 cdef extern from "myfile.h": int myfunc(int p, int q, const double **a, double **b, double *c) 

mytest.pyx

 cimport cython cimport myfile import numpy as np cimport numpy as np p = 3 q = 4 cdef: double** a = np.random.random([p,q]) double** b double* c myfile.myfunc(p, q, a, b, c) 

然后在iPython中运行

 import pyximport; pyximport.install() import mytest 

带有a定义的行给出了错误消息Cannot convert Python object to 'double **' 。 我没有得到关于bc任何错误消息,但由于此时我无法运行C函数,我不确定bc的声明是否正确写入(即,以某种方式将使C函数分别输出2D和1Darrays。

其他尝试:我也尝试过这里的解决方案,但这不适用于我在myfunc声明中的双星号类型的数组。 这里的解决方案不适用于我的任务,因为我无法更改myfunc的声明。

在cython中创建一个辅助数组

要从numpy数组中获取double** ,可以在* .pyx文件中创建一个辅助数组指针。 此外,您必须确保numpy数组具有正确的内存布局。 (可能涉及创建副本)

Fortran订单

如果您的C函数需要fortran顺序(一个列表中的所有x坐标,另一个列表中的所有y坐标,第三个列表中的所有z坐标,如果您的数组a对应于3D空间中的点列表)

 N,M = a.shape # Make sure the array a has the correct memory layout (here F-order) cdef np.ndarray[double, ndim=2, mode="fortran"] a_cython = np.asarray(a, dtype = float, order="F") #Create our helper array cdef double** point_to_a = malloc(M * sizeof(double*)) if not point_to_a: raise MemoryError try: #Fillup the array with pointers for i in range(M): point_to_a[i] = &a_cython[0, i] # Call the C function that expects a double** myfunc(... &point_to_a[0], ...) finally: free(point_to_a) 

C-订单

如果你的C函数需要C顺序([x1,y1,z1]是第一个列表,[x2,y2,z2]是3D点列表的第二个列表):

 N,M = a.shape # Make sure the array a has the correct memory layout (here C-order) cdef np.ndarray[double, ndim=2, mode="c"] a_cython = np.asarray(a, dtype = float, order="C") #Create our helper array cdef double** point_to_a = malloc(N * sizeof(double*)) if not point_to_a: raise MemoryError try: for i in range(N): point_to_a[i] = &a_cython[i, 0] # Call the C function that expects a double** myfunc(... &point_to_a[0], ...) finally: free(point_to_a) 

回复1:你可以使用数组开头的位置通过Cython将NumPy数组传递给C(参见下面的代码)。

回复2:您的声明似乎正确但我没有使用这种显式内存管理方法。 您可以使用NumPy声明cdef -ed数组。

使用

 cdef double[:,::1] a = np.random.random([p, q]) cdef double[:,::1] b = np.empty([p, q]) cdef double[::1] b = np.empty(q) 

然后将数组开始位置&a[0]传递给C函数。 ::1是为了确保连续性。

一个很好的参考是Jake Vanderplas的博客: https ://jakevdp.github.io/blog/2012/08/08/memoryview-benchmarks/

最后,通常一个在Cython中创建函数并在Python中调用它们,因此您的Python代码将是:

 import pyximport; pyximport.install() import mytest mytest.mywrappedfunc() 

其中mywrappedfunc是模块中定义的Python( def而不是cdef )函数,可以执行上面的数组声明。