Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing Numpy arrays to a C function for input and output

Oh my word I'm a fool. I was simply omitting the second and third arguments when calling the function. Like a fool. Because that's what I am. Original silly question follows:

This seems like it must be a very common thing to do, but I can't find a relevant tutorial, and I'm too ignorant about Numpy and ctypes to figure it out myself.

I have a C function in file ctest.c.

#include <stdio.h>  void cfun(const void * indatav, int rowcount, int colcount, void * outdatav) {     //void cfun(const double * indata, int rowcount, int colcount, double * outdata) {     const double * indata = (double *) indatav;     double * outdata = (double *) outdatav;     int i;     puts("Here we go!");     for (i = 0; i < rowcount * colcount; ++i) {         outdata[i] = indata[i] * 2;     }     puts("Done!"); } 

(As you may guess, I originally had the arguments as double * rather than void *, but couldn't figure out what to do on the Python side. I'd certainly love to change them back, but I'm not picky as long as it works.)

I make a shared library out of it. gcc -fPIC -shared -o ctest.so ctest.c

Then in Python, I have a couple numpy arrays, and I'd like to pass them to the C function, one as input and one as output.

indata = numpy.ones((5,6), dtype=numpy.double) outdata = numpy.zeros((5,6), dtype=numpy.double) lib = ctypes.cdll.LoadLibrary('./ctest.so') fun = lib.cfun # Here comes the fool part. fun(ctypes.c_void_p(indata.ctypes.data), ctypes.c_void_p(outdata.ctypes.data))  print 'indata: %s' % indata print 'outdata: %s' % outdata 

This doesn't report any errors, but prints out

>>> Here we go! Done! indata: [[ 1.  1.  1.  1.  1.  1.]  [ 1.  1.  1.  1.  1.  1.]  [ 1.  1.  1.  1.  1.  1.]  [ 1.  1.  1.  1.  1.  1.]  [ 1.  1.  1.  1.  1.  1.]] outdata: [[ 0.  0.  0.  0.  0.  0.]  [ 0.  0.  0.  0.  0.  0.]  [ 0.  0.  0.  0.  0.  0.]  [ 0.  0.  0.  0.  0.  0.]  [ 0.  0.  0.  0.  0.  0.]] 

The outdata array is not modified. And in fact if I call the function again I get a segfault. Which doesn't surprise me -- I really don't know what I'm doing here. Can anyone point me in the right direction?

like image 790
Tom Future Avatar asked May 02 '11 21:05

Tom Future


People also ask

Are NumPy arrays passed by reference or value?

Python does not pass-by-reference nor pass-by-value.

Is NumPy as fast as C?

It is almost 7 times faster than straightforward C implementation. But still we are about 4 times slower than NumPy.


1 Answers

While not a direct answer to your original question, here's a much more convenient way to call your function. First, make the prototype of your C function exactly as you would do it in plain C. Since you don't need rowcount and colcount separately, I'll collapse them into a single size parameter:

void cfun(const double *indatav, size_t size, double *outdatav)  {     size_t i;     for (i = 0; i < size; ++i)         outdatav[i] = indatav[i] * 2.0; } 

Now define the ctypes prototype in the following way:

import ctypes from numpy.ctypeslib import ndpointer lib = ctypes.cdll.LoadLibrary("./ctest.so") fun = lib.cfun fun.restype = None fun.argtypes = [ndpointer(ctypes.c_double, flags="C_CONTIGUOUS"),                 ctypes.c_size_t,                 ndpointer(ctypes.c_double, flags="C_CONTIGUOUS")] 

Now, calls to your function will be really convenient:

indata = numpy.ones((5,6)) outdata = numpy.empty((5,6)) fun(indata, indata.size, outdata) 

You could also define a wrapper to make this even more convenient:

def wrap_fun(indata, outdata):     assert indata.size == outdata.size     fun(indata, indata.size, outdata) 
like image 95
Sven Marnach Avatar answered Oct 08 '22 14:10

Sven Marnach