Pass 3d arrays to C functions

204 views
Skip to first unread message

NY-CU

unread,
Nov 25, 2015, 3:28:30 PM11/25/15
to python-cffi
I am trying to extend the code on this post http://arjones6.blogspot.com/2013/05/passing-multidimensional-numpy-arrays.html to 3d arrays of float numbers. But I did not succeeded. Can you help me?

This is an example of what I tried:

ffi.cdef('''
struct data_t {
  float ***data;
  unsigned t;
  unsigned x;
  unsigned y;
};

void analyze(struct data_t *data);
''')


def _LocalNMF(x):
      data = ffi.new("struct data_t *")
      data.t = x.shape[0]
      data.x = x.shape[1]
      data.y = x.shape[2]
      print x
      data.data = ffi.new("float** [%d]" % x.shape[0])
      for i in range(x.shape[0]):
        data.data[i] = ffi.cast("float**", x[i].ctypes.data)
      for i in range(x.shape[0]):
        for j in range(x.shape[1]):
          for k in range(x.shape[2]):
            data.data[i][j] = ffi.cast("float*", x[i][j].ctypes.data) 
      lib.analyze(data)


Thank you

Armin Rigo

unread,
Nov 26, 2015, 1:56:45 AM11/26/15
to pytho...@googlegroups.com
Hi,

On Wed, Nov 25, 2015 at 9:28 PM, NY-CU <giuseppe.d...@gmail.com> wrote:
> struct data_t {
> float ***data;

> data.data = ffi.new("float** [%d]" % x.shape[0])

There are two problems here. One is that the line "c_struct.somefield
= ffi.new(...)" is always bogus: it allocates a new C object, then
copies a pointer to it into the specified field, and then the object
is immediately freed---and with it, the allocated memory is gone and
the pointer points to garbage.

Another problem is that you only allocate an array containing ``t``
times a ``float **``. If I'm following correctly, you allocate this
array containing ``t`` times a ``float **``, i.e. each item is a
further array of ``float *``. However you set every item to point to
numpy(?) data from ``x[i].ctypes.data``. My guess is that this
doesn't give you an array of float pointers, but only an array of
float.

In other words, numpy gives a compact 3d array as a big array of
floats, with offsets used internally; but what you seem to need here
is the opposite way to do it in C: an array of t pointers, each
pointing to an array of x pointers, each pointing to an array of y
floats. Numpy can be used only for the last array of y floats; the
other two levels are array of pointers not present in numpy.

So, guessing a bit, here is how I would do it:


# the list of each ``x``-sized array of pointer, one for each ``t``
sub_arrays = [ffi.new("float *[]", data.x) for t in range(data.t)]

# fill each item of each array with a pointer to the x[t][x] item from numpy
for t in range(data.t):
for x in range(data.x):
sub_arrays[t][x] = ffi.cast("float*", x[t][x].ctypes.data)

# the main ``t``-sized array of pointer to pointer,
# initialized immediately with the sub_arrays list
main_array = ffi.new("float **[]", sub_arrays)

data.data = main_array

# here, you must keep alive both sub_arrays and main_array
# until the call to lib.analyze(data) is done (this is automatic if
# these are local variables that are not overwritten until the
# end of the function).


Hope this helps!
Armin
Reply all
Reply to author
Forward
0 new messages