【问题标题】:Pass a 2d numpy array to c using ctypes使用 ctypes 将 2d numpy 数组传递给 c
【发布时间】:2014-04-20 23:30:05
【问题描述】:

使用 ctypes 将 numpy 2d 数组传递给 c 函数的正确方法是什么? 到目前为止我目前的方法(导致段错误):

C 代码:

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");
    }
}

Python 代码:

from ctypes import *
import numpy.ctypeslib as npct

array_2d_double = npct.ndpointer(dtype=np.double,ndim=2, flags='CONTIGUOUS')
liblr = npct.load_library('libtest.so', './src')

liblr.test.restype = None
liblr.test.argtypes = [array_2d_double, c_int]

x = np.arange(100).reshape((10,10)).astype(np.double)
liblr.test(x, 10)

【问题讨论】:

  • 您知道double **double [N][N] 在C 中不能互换,不是吗?
  • 我的问题是我在我的 c 代码中使用双 ** 有什么解决方案吗?
  • 好吧,我不知道python也不知道numpy,但是如果是NxN数组,你应该将in_array声明为double (*in_array)[N],其中N是第二维的大小。
  • 如果 N 在运行时不固定,这将如何工作?
  • 我想你的意思是如果它在编译时没有固定。在这种情况下,如果您有支持 VLA 的 C99 编译器,您可以将 test 声明为 void test(int width, double in_array[][width]),但我怀疑这是否适用于 numpy。也许你最好等熟悉的人,我真的不知道你是否可以使用numpy

标签: python c numpy ctypes


【解决方案1】:

这可能是一个迟到的答案,但我终于让它工作了。所有功劳归于this link 的 Sturla Molden。

关键是,注意double** 是一个np.uintp 类型的数组。因此,我们有

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

然后使用doublepp作为类型,传入xpp。完整代码见附件。

C 代码:

// 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]; 
} 

Python 代码:

# 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) 

希望对你有帮助,

肖恩

【讨论】:

  • 谢谢你。但是我真的不明白这里发生了什么。这如何扩展到 3d 及以上?
  • @navjotk 对于更复杂的情况,我真的推荐Cython,以便于维护:)
  • 警告:检查 x.flags['C_CONTIGUOUS']==True。如果没有,请先执行 x = np.ascontiguousarray(x, dtype= x.dtype) 。否则,如果 x 在传递给 foobar 之前被转置,xpp 就会出错
【解决方案2】:
#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;
}

如果你想在你的函数中使用double **,你必须将一个指针数组传递给double(不是二维数组):

#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;
}

另一个(如@eryksun 所建议的):传递一个指针并做一些算术来获取索引:

#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;
}

【讨论】:

  • @DavidHeffernan,我不知道 ctypes 代码部分,我试图解释为什么 double ** 在传递二维数组时不起作用
  • 问题是N在运行时不固定
  • @jrsm:如果你真的想要double ** 方法,你可以像这样创建指针数组:c_double_p = POINTER(c_double); in_array_ptrs = (c_double_p * len(in_array))(*(r.ctypes.data_as(c_double_p) for r in in_array))。我不推荐这个,因为它效率低。
【解决方案3】:

虽然回复可能比较晚,但我希望它可以帮助其他有同样问题的人。

由于 numpy 数组在内部保存为 1d 数组,因此可以简单地在 C 中重建 2d 形状。这是一个小型 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);
}

# 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)

如果您使用gcc -shared -fPIC libtest2d.c -o libtest2d.so 编译代码,然后运行python test2d.py,它应该会打印数组。

我希望这个例子或多或少是不言自明的。这个想法是,形状也被赋予 C 代码,然后创建一个 double ** 指针,为该指针保留额外指针的空间。然后将这些设置为指向原始数组的正确部分。

PS:我是 C 的初学者,所以如果有理由不这样做,请发表评论。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-28
    • 1970-01-01
    • 2022-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多