[Cython] Passing numpy arrays to C using Cython

Dag Sverre Seljebotn dagss at student.matnat.uio.no
Sat Jan 17 19:15:54 CET 2009


First: This question (how to safely pass numpy arrays to C or Fortran 
code using Cython) actually comes up a lot. I'm wondering about perhaps 
making it a feature in Cython itself, borrowing a few syntax things from 
Fortran. (That would clean up a lot of code I'm using myself with Cython 
these days, so it could get realized in some months).

Basically, perhaps one should allow declaring functions like this:

cdef extern from "myheader.h":
   int myfunc(double[0:nrows, 0:ncols] data, int nrows, int ncols)

(here data is really "double* data" in the C header) and then call it 
like this:

cdef np.ndarray[double, ndim=2] arr = x
myfunc(arr)

nrows and ncols would be inferred from the shape of "arr", and Cython 
would transparently make sure contiguous memory is passed, making a copy 
if necesarry. (One could leave out the auto-nrows-ncols-bit for now though.)

This would be backed by the buffer PEP and not be NumPy-specific.

The thing lacking from the syntax above is how to specify whether data 
only needs to be passed in, or whether it is written to. "const 
double[...]" perhaps...

Thoughts?

Then to your question:

Ilmar Wilbers wrote:
> Dag Sverre Seljebotn wrote:
>> Hi Ilmar,
>>
>> Ilmar Wilbers wrote:
>>   
>>> Hi list,
>>>
>>> While trying to wrap an external C library, I have stumbled upon some
>>> question. A lot of them were answered by going through the mailing list
>>> archive, however a few remain.
>>>
>>> I have the following function with arguments:
>>> def tridag(np.ndarray[DTYPE_t, ndim=1, negative_indices=False] a)
>>>
>>> I want to do some assertions on the array a:
>>>     assert a.dtype == np.float32
>>>     assert a.flags['C_CONTIGUOUS']
>>> but keep getting an error  "Attempting to index non-array type 'int'".
>>> Checking the type of a.flags indicates that it is in int. What am I
>>> missing?
>>>     
>> You have encountered one of the less polished aspects of Cython I'm
>> afraid. The flags field is typed as int to give efficient access for
>> or-style flags checking, which precludes the object behaviour you use.
>>
>> To make a long story short, here's your current options:
>> - assert (<object>a).flags[....]
>> - The assertion for datatype is already made if DTYPE_t is float32_t. Use
>> mode="c" as a flag to ndarray to do the other assertion automatically (on
>> entering the function).
>>
>> This is definitely something that should be more done something about
>> (i.e. this should Just Work), I'll try to file an enhancement ticket for
>> it.
>>
>>   
> OK, thank you, Dag Sverre.
> 
> I realize that the mode is not really important for 1D arrays, but was 
> merely working my way up to matrices.

Mode is important for 1D arrays in this setting -- if you have strided 
memory, you cannot pass it on to C. I.e.

x = arange(10)
x = x[::2]

Here no memory is changed on the second line, only a stride is set 
skipping every other element.

> This requires the explicit use of a = zeros(n, numpy.float32) in the 
> calling Python code, which I was hoping to avoid.

You can also use arr.astype(), which will make a copy without filling 
with zeros. If that isn't flexible enough (cannot set C or Fortran 
mode?), one can also use np.empty, which only allocates memory and 
doesn't fill with zeros either.

> My question is, can I use an alternative to
> cdef float * p_a = <np.float32_t*>a.data
> 
> in case the arrays have dtype float64, for instance, for simply writing
> cdef float * p_a = <np.float64_t*>a.data
> 
> doesn't help. Aka: is it possible to not make assumptions on the array 
> dtype? Using np.float without 32 or 64 works fine, but the data sent to 
> the C function is wrong. I guess float in C has the same precision as 
> float32 in numpy and double in C has the same precision as float64 in numpy?

If you simply write "np.ndarray[double] arr", you WILL get a runtime 
exception if arr is assigned something that does not contain a C double. 
  So this is already handled automatically and you do not need to code 
the check yourself -- simply do

cdef np.ndarray[double] a = x
cdef double * p_a = <double*>a.data

Yes, usually double == float64.


-- 
Dag Sverre


More information about the Cython-dev mailing list