简体   繁体   中英

cython: pass a 2D numpy array to cdef function

I want to pass a 2D numpy array to a cdef function, where the dimensions of the array can vary. Here's what I tried:

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)

where my test_array function is:

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

and my function prototype is:

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

When I compile, I get an error saying " Cannot take address of Python object" and pointing to &input[0] . That syntax works for a 1D array, but I'm not sure what the syntax is for a 2d array. I also tried &input[0][0] but that's wrong too.

It is not clear what you would like to achieve:

A: if it should be a pure cython function then you should use typed memory view , that means your function signature should be

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

There are no nrows , ncols because the typed memory views have this information (similar to std::vector ).

B: array_test is actually a wrapper for a c-function, which expects double ** and unsigned char ** , then you should take a look at this SO-question .


Actually, I would like to explain, why your attempts didn't work.

First, why didn't &input[0] work? The real question is what is 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'>

so input is a numpy.ndarray that means a python object, and cython refuses to take its address. The same is the case for the input[0,0] - it is a python object. No luck so far.

To get it work, you need the input to be a cython-numpy array (I don't know how to express it better - take a look at the example):

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

The important part: input is no longer considered/interpreted as a python object but as of type cython-type np.ndarray[double, ndim=2] and this is what makes the syntax &input[0,0] possible in the first place.

Maybe a more precise way to see it like: cimport numpy gives us additional tools in handling of numpy arrays so we can access internals which are not accessible in pure python.

However, &input[0,0] is not of type double ** but of type double * , because numpy.ndarray is just a continuous chunk of memory and only the operator [i,j] mocks the feeling of 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

There are no pointers to rows, but you could create them via cdef double *ptr2=&input[row_id,0] , how it could be handled is discussed in the above mentioned question .


To say that numpy.ndarray is just a continuous piece of memory is a simplification - numpy.ndarray is quite a complicated beast! Please consider the following example:

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"!

Now, here input and column share the same memory and in the memory input[1][0] is saved after input[0][1]=column[0] and only then input[1][1]=column[1] . ptr[1] takes the memory cell next to input[0][1] and this is input[1][0]=3 and not input[1][1]=4 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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