cython:将2D numpy数组传递给cdef函数

时间:2017-10-12 16:02:33

标签: python arrays numpy multidimensional-array cython

我想将2D numpy数组传递给cdef函数,其中数组的尺寸可以变化。这是我试过的:

cimport numpy as cnp

input = numpy.array([[3.34, 2.2],[1.1, -0.6]])
input = input[:,:].astype(np.double)
cdef int nrows = 2
cdef int ncols = 2

# output of function
cdef cnp.ndarray[cnp.uint8_t, ndim=2] output = np.zeros((2,2), dtype=np.uint8)  

test_array(nrows, ncols, &input[0], <unsigned char**>output.data)

我的test_array函数是:

cdef void test_array(Py_ssize_t nrows, Py_ssize_t ncols, double **x, unsigned char **output) nogil:

output[0][0]=1
output[1][0]=0
output[1][1]=1
output[0][1]=0

我的函数原型是:

cdef void test_array(Py_ssize_t nrows, Py_ssize_t ncols, double **x, unsigned char **output) nogil

当我编译时,我收到一条错误,上面写着“无法获取Python对象的地址”并指向&input[0]。该语法适用于一维数组,但我不确定二维数组的语法是什么。我也试过&input[0][0],但这也错了。

1 个答案:

答案 0 :(得分:3)

目前尚不清楚您希望实现的目标:

A:如果它应该是纯粹的cython函数,那么你应该使用typed memory view,这意味着你的函数签名应该是

cdef void test_array(double[:,:] x, unsigned char[:,:] output) nogil:

没有nrowsncols,因为输入的内存视图包含此信息(类似于std::vector)。

B: array_test实际上是c函数的包装器,它需要double **unsigned char **,那么你应该看看这个{ {3}}

实际上,我想解释一下,为什么你的尝试不起作用。

首先,为什么&input[0]没有工作?真正的问题是什么是input[0]

import numpy as np
input=np.zeros((3,3))
type(input[0])
<type 'numpy.ndarray'>
type(input[:,0])
<type 'numpy.ndarray'>
type(input[0,0])
<type 'numpy.float64'>

所以input是一个numpy.ndarray,意思是一个python对象,而cython拒绝接受它的地址。 input[0,0]的情况也是如此 - 它是一个python对象。到目前为止没有运气。

要使它工作,你需要input成为一个cython-numpy数组(我不知道如何更好地表达它 - 看看这个例子):

import numpy as np
cimport numpy as np #that the way it is usually imported

def try_me():
    cdef np.ndarray[double, ndim=2] input = np.array([[3.34, 2.2],[1.1, -0.6]])
    cdef double *ptr1=&input[0,0]
    cdef double *ptr2=&input[1,0]
    print ptr1[0], ptr2[1] #prints 3.34 and -0.6

重要的部分:input不再被视为/解释为python对象,而是类型为cython-type np.ndarray[double, ndim=2],这使得语法&input[0,0]成为可能地点。

可能更精确地看待它:cimport numpy为处理numpy数组提供了额外的工具,因此我们可以访问纯python中无法访问的内部。

但是,&input[0,0]不是double **类型,而是double *类型,因为numpy.ndarray只是一个连续的内存块,只有运算符[i,j]嘲笑2d的感觉:

How it feels:
    A[0] -> A00 A01 A02
    A[1] -> A10 A11 A12

The real layout in the memory:
     A00 A01 A02 A10 A11 A12

没有指向行的指针,但您可以通过cdef double *ptr2=&input[row_id,0]创建它们,SO-question中讨论了如何处理它们。

要说numpy.ndarray只是一段连续的记忆是一种简化 - numpy.ndarray是一个非常复杂的野兽!请考虑以下示例:

import numpy as np
cimport numpy as np

def try_me2():
    cdef np.ndarray[double, ndim=2] input = np.array([[1.0, 2.0],
                                                      [3.0, 4.0]])
    cdef np.ndarray[double, ndim=1] column = input[:,1]  
    cdef double *ptr = &column[0]
    print column          #prints column "2 4"
    print ptr[0],ptr[1]   #prints "2 3" and not "2 4"!

现在,此处inputcolumn共享相同的内存,内存input[1][0]会在input[0][1]=column[0]之后保存,然后才会input[1][1]=column[1]ptr[1]获取input[0][1]旁边的内存单元格,这是input[1][0]=3而不是input[1][1]=4