Pass a 2d numpy array to c using ctypes

前端 未结 3 1071
天涯浪人
天涯浪人 2020-12-13 22:45

What is the correct way to pass a numpy 2d - array to a c function using ctypes ? My current approach so far (leads to a segfault):

C code :

void te         


        
相关标签:
3条回答
  • 2020-12-13 23:03
    #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;
    }
    
    0 讨论(0)
  • 2020-12-13 23:21

    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 one
    double ** 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 array
    void 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.py
    
    import ctypes as ct
    import numpy as np
    
    libtest2d = 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 = None
    
    arr2d = 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.

    0 讨论(0)
  • 2020-12-13 23:27

    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

    0 讨论(0)
提交回复
热议问题