5

I have code in C, which I would like to use in python, I used SWIG to wrap the C-code, and got successfully a python module imported in my python code.

Now I have the following code:

import flame
import numpy as np

data = np.random.rand(3,2).astype(np.float32, copy=False)
N = 3
M = 2
print data

flameobject = flame.Flame_New()
flame.Flame_SetDataMatrix( flameobject, data, N, M, 0 )

and this gives error:

TypeError: in method 'Flame_SetDataMatrix', argument 2 of type 'float *[]'

I understand that I should pass a float array pointer to the method, but how can I convert my Numpy multi-dimensional array into the correct type?

Niki van Stein
  • 10,564
  • 3
  • 29
  • 62

2 Answers2

1

There is SciPy documentation on exactly this: How to pass numpy arrays to C code via SWIG (and vice versa). Have a look here.

Basically, there is a swig interface file numpy.i that you use in the following way. In your swig interface file you include:

%{
#define SWIG_FILE_WITH_INIT
%}
%include "numpy.i"
%init %{
import_array();
%}

and then add in your interface file, before mentioning your C functions:

%apply ( float* IN_ARRAY2, int DIM1, int DIM2 ) {
    (float* your_array_parameter_name, int N_parameter_name, int M_parameter_name)
};

This works for ordinary C float arrays. I am not quite sure what a float* [] is. You may need to write your own typemaps for this, for which you can use the utility macros provided by numpy.i. But it is all explained in the numpy.i documentation mentioned above, or in the relevant swig typemap docs

m7thon
  • 3,033
  • 1
  • 11
  • 17
  • Thank you! though I do not get yet how to do it for the float*[], which is basically a 2 dimensional array of floats.. – Niki van Stein Sep 21 '15 at 12:33
  • The above example should work for 2D float arrays. Are these not also referred to by a `float *` to the first element? Sorry, I am not a "C" person. – m7thon Sep 21 '15 at 12:43
  • It is an array of pointers, and unfortunately the code does not work for me. I also tried to put `float* data[]` in the apply function, which seems to pass the reference, but created I think a pointer error, since it crashes without message. – Niki van Stein Sep 21 '15 at 12:55
  • That's what I thought. The provided functions are for arrays (also 2D) of float. That is also the internal data structure of numpy arrays. If you need an array of pointers to float, then you need to write your own conversion functions (by writing C code for swig typemaps). – m7thon Sep 21 '15 at 13:10
  • Ok thank you very much, you brought me at least on the right track. I did it the dirty way, I added in the c code `float **data2 = &data;` and changed the header to `float *data`, so that it could match the normal typemap. – Niki van Stein Sep 21 '15 at 13:50
0

Ok for everyone that might face the same problem as I did, here is how I finally solved it.

First I changed the header in the .h file and the .c file of the function from

void Flame_SetDataMatrix( Flame *self, float *data[], int N, int M, int T );

to

void Flame_SetDataMatrix( Flame *self, float *data, int N, int M, int T );

After that, I added

%apply (float* IN_ARRAY2, int DIM1, int DIM2) {
    (float *data, int N, int M)
};

To the interface file (flame.i). This makes it possible to call the function in Python like this: flame.Flame_SetDataMatrix( flameobject, data, T), where data is a numpy array with two dimensions.

The "problem" now is that the array that arrives in the C function is in the wrong format, because we want a double array (which in this case is a pointer to a pointer to a float).

The solution is to convert this array, which is wrapped in a single dimension, to reconstruct the double array in the c code like this:

//n = number of rows, m= number of columns columns
void Flame_SetDataMatrix( Flame *self, float *data, int n, int m, int dt )
{
    //convert data to float** for later use
    int i=0, j=0;
    float ** data2 = (float**) calloc( n, sizeof(float*) ); 
    for (i=0; i<n; i++){
        data2[i] = calloc( m, sizeof(float) ); 
        for (j=0; j<m; j++){
            //the data is in a single array row after row, so i*columns+j
            data2[i][j] = data[i * m + j];
        }
    }

In the end I could use the same "trick" to also get a two dimensional float array back into a numpy array, which I did have to reschape but in numpy that is easy.

Hope it will help someone.

Niki van Stein
  • 10,564
  • 3
  • 29
  • 62