Pass a 2d numpy array to c using ctypes Pass a 2d numpy array to c using ctypes numpy numpy

Pass a 2d numpy array to c using ctypes


This is probably a late answer, but I finally got it working. All credit goes to Sturla Molden at this link.

The key is, note that double** is an array of type np.uintp. Therefore, we have

xpp = (x.ctypes.data + np.arange(x.shape[0]) * x.strides[0]).astype(np.uintp)doublepp = np.ctypeslib.ndpointer(dtype=np.uintp)

And then use doublepp as the type, pass xpp in. See full code attached.

The C code:

// dummy.c #include <stdlib.h> __declspec(dllexport) void foobar(const int m, const int n, const double **x, double **y) {     size_t i, j;     for(i=0; i<m; i++)         for(j=0; j<n; j++)             y[i][j] = x[i][j]; } 

The Python code:

# test.py import numpy as np from numpy.ctypeslib import ndpointer import ctypes _doublepp = ndpointer(dtype=np.uintp, ndim=1, flags='C') _dll = ctypes.CDLL('dummy.dll') _foobar = _dll.foobar _foobar.argtypes = [ctypes.c_int, ctypes.c_int, _doublepp, _doublepp] _foobar.restype = None def foobar(x):     y = np.zeros_like(x)     xpp = (x.__array_interface__['data'][0]       + np.arange(x.shape[0])*x.strides[0]).astype(np.uintp)     ypp = (y.__array_interface__['data'][0]       + np.arange(y.shape[0])*y.strides[0]).astype(np.uintp)     m = ctypes.c_int(x.shape[0])     n = ctypes.c_int(x.shape[1])     _foobar(m, n, xpp, ypp)     return y if __name__ == '__main__':     x = np.arange(9.).reshape((3, 3))     y = foobar(x) 

Hope it helps,

Shawn


#include <stdio.h>void test(double (*in_array)[3], int N){    int i, j;    for(i = 0; i < N; i++){        for(j = 0; j < N; j++){            printf("%e \t", in_array[i][j]);        }        printf("\n");    }}int main(void){    double a[][3] = {        {1., 2., 3.},        {4., 5., 6.},        {7., 8., 9.},    };    test(a, 3);    return 0;}

if you want to use a double ** in your function, you must pass an array of pointer to double (not a 2d array):

#include <stdio.h>void test(double **in_array, int N){    int i, j;    for(i = 0; i < N; i++){        for(j = 0; j< N; j++){            printf("%e \t", in_array[i][j]);        }        printf("\n");    }}int main(void){    double a[][3] = {        {1., 2., 3.},        {4., 5., 6.},        {7., 8., 9.},    };    double *p[] = {a[0], a[1], a[2]};    test(p, 3);    return 0;}

Another (as suggested by @eryksun): pass a single pointer and do some arithmetic to get the index:

#include <stdio.h>void test(double *in_array, int N){    int i, j;    for(i = 0; i < N; i++){        for(j = 0; j< N; j++){            printf("%e \t", in_array[i * N + j]);        }        printf("\n");    }}int main(void){    double a[][3] = {        {1., 2., 3.},        {4., 5., 6.},        {7., 8., 9.},    };    test(a[0], 3);    return 0;}


While the reply might be rather late, I hope it could help other people with the same problem.

As numpy arrays are internally saved as 1d arrays, one can simply rebuild 2d shape in C. Here is a small MWE:

// libtest2d.c#include <stdlib.h> // for malloc and free#include <stdio.h>  // for printf// create a 2d array from the 1d onedouble ** convert2d(unsigned long len1, unsigned long len2, double * arr) {    double ** ret_arr;    // allocate the additional memory for the additional pointers    ret_arr = (double **)malloc(sizeof(double*)*len1);    // set the pointers to the correct address within the array    for (int i = 0; i < len1; i++) {        ret_arr[i] = &arr[i*len2];    }    // return the 2d-array    return ret_arr;}// print the 2d arrayvoid print_2d_list(unsigned long len1,    unsigned long len2,    double * list) {    // call the 1d-to-2d-conversion function    double ** list2d = convert2d(len1, len2, list);    // print the array just to show it works    for (unsigned long index1 = 0; index1 < len1; index1++) {        for (unsigned long index2 = 0; index2 < len2; index2++) {            printf("%1.1f ", list2d[index1][index2]);        }        printf("\n");    }    // free the pointers (only)    free(list2d);}

and

# test2d.pyimport ctypes as ctimport numpy as nplibtest2d = ct.cdll.LoadLibrary("./libtest2d.so")libtest2d.print_2d_list.argtypes = (ct.c_ulong, ct.c_ulong,        np.ctypeslib.ndpointer(dtype=np.float64,            ndim=2,            flags='C_CONTIGUOUS'            )        )libtest2d.print_2d_list.restype = Nonearr2d = np.meshgrid(np.linspace(0, 1, 6), np.linspace(0, 1, 11))[0]libtest2d.print_2d_list(arr2d.shape[0], arr2d.shape[1], arr2d)

If you compile the code with gcc -shared -fPIC libtest2d.c -o libtest2d.so and then run python test2d.py it should print the array.

I hope the example is more or less self-explaining. The idea is, that the shape is also given to the C-Code which then creates a double ** pointer for which the space for the additional pointers is reserved. And these then are then set to point to the correct part of the original array.

PS: I am rather a beginner in C so please comment if there are reasons not to do this.