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

Ale*_*lex 7 c python arrays numpy cython

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

int myfunc(int m, int n, const double **a, double **b, double *c);
Run Code Online (Sandbox Code Playgroud)

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

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

问题2:是的声明bc下方正确的,或者他们需要在其他格式的C函数将其理解为一个二维和一维数组(分别)?

我的尝试:

myfile.pxd

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

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)
Run Code Online (Sandbox Code Playgroud)

然后在iPython中我运行

import pyximport; pyximport.install()
import mytest
Run Code Online (Sandbox Code Playgroud)

带有定义的行a给出了错误消息Cannot convert Python object to 'double **'.我没有得到任何错误消息有关bc,但因为我不能在这个时候运行的C函数,我不知道的声明bc编写正确(即,在某种程度上,这将启用C功能分别输出2D和1D阵列).

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

Ber*_*ard 8

在cython中创建一个辅助数组

要从double**numpy数组中获取a ,可以在*.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 = <double **>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)
Run Code Online (Sandbox Code Playgroud)

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 = <double **>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)
Run Code Online (Sandbox Code Playgroud)

  • 非常有帮助的答案,谢谢+1!请注意,[Cython 文档](https://cython.readthedocs.io/en/latest/src/tutorial/memory_allocation.html) 建议使用 `PyMem_Malloc()` 和 `PyMem_Free()` 而不是 `malloc()`和`free()`。 (2认同)
  • 此外,使用更通用的[类型化内存视图](https://cython.readthedocs.io/en/latest/src/userguide/memoryviews.html)(例如`cdef [:,::1] a_view = np.ascontigiousarray( a)` 而不是 `cdef np.ndarray[.....] a_view = ...`) 具有改进的可读性和[其他优点](https://cython.readthedocs.io/en/latest/src/教程/numpy.html)。另请参阅[本教程](https://cython.readthedocs.io/en/latest/src/userguide/numpy_tutorial.html) 或[此(重复)帖子](/sf/ask/4502843761/) 。 (2认同)

Pie*_*uyl 0

回复 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)
Run Code Online (Sandbox Code Playgroud)

然后将&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()
Run Code Online (Sandbox Code Playgroud)

其中是模块中定义的mywrappedfuncPython(def而非)函数,可以执行上面所示的数组声明。cdef