【问题标题】:Numpy array interface with ctypes function带有 ctypes 函数的 Numpy 数组接口
【发布时间】:2013-03-03 11:04:22
【问题描述】:

我正在尝试将共享 C 库与一些 python 代码接口。 与库的接口类似于

typedef struct{
    int v1;
    double* v2} input;

还有两种类似的类型:用于配置和输出类型。

我在 python 中使用ctypes Structures 像这样设置这些结构:

class input(Structure):
    _fields_ = [("v1",c_int),("v2",POINTER(c_double)]

C 代码有一些函数接收指向该结构的指针,并且 argtypes 定义如下:

fun.argtypes = [constraints,input,POINTER(input)]

constraints 是另一种结构,其中包含一些用于配置的int 字段。

首先我更新输入结构中的 v2 字段

input.v2 = generated_array.ctypes.data_as(POINTER(c_double))

那我叫它:

fun(constraints,input,byref(output))

函数原型要求 struct 和 * 到 struct(假设输出 struct 类型与输入 struct 类型相同)。

然后我想访问存储在输出的 v2 字段中的 fun 的结果。但我得到了意想不到的结果。有更好/正确的方法吗?

我在这里搜索了很多并阅读了文档,但我找不到问题所在。我没有任何错误消息,但我从共享库收到的警告似乎表明这些接口存在错误。


我想我发现了问题:

当我调用该方法时,会调用一个复杂的 numpy 数组。然后我创建了 4 个向量:

    out_real = ascontiguousarray(zeros(din.size,dtype=c_double))
    out_imag = ascontiguousarray(zeros(din.size,dtype=c_double))
    in_real  = ascontiguousarray(din.real,dtype = c_double)
    in_imag  = ascontiguousarray(din.imag,dtype = c_double) 

其中 din 是输入向量。我以这种方式测试了该方法:

    print in_real.ctypes.data_as(POINTER(c_double))    
    print in_imag.ctypes.data_as(POINTER(c_double))
    print out_real.ctypes.data_as(POINTER(c_double))
    print out_imag.ctypes.data_as(POINTER(c_double))

结果是:

    <model.LP_c_double object at 0x1d81f80>
    <model.LP_c_double object at 0x1d81f80>
    <model.LP_c_double object at 0x1d81f80>
    <model.LP_c_double object at 0x1d81f80>

他们似乎都指向同一个地方。

经过一些更改,它按预期工作......


经过几次测试,我发现代码第一次几乎是正确的。我创建了一次 Structure 实例并更新了它的字段。我改为在每次调用fun 时创建一个新实例。我还将所有数组类型更改为等效的 ctypes 类型;这似乎使该功能按预期工作。

打印行为仍然与上面的测试一样,但即使出现这种奇怪的行为,该功能似乎也能正常工作。 正如下面@ericsun 评论所指出的那样是正确的。

【问题讨论】:

  • 请贴出完整代码,c代码是做什么的?
  • 很遗憾我不能分享完整的代码,因为它是 nda 下的第三方库。但这只是一种科学算法,它接受两个向量并返回其他 2 个向量。如果没有完整的描述,您是否认为问题不清楚?
  • c函数接口不清楚,fun.argtypes = [...,POINTER(input)]fun(..,input,byref(output))不是同一个接口。
  • 我明白了。我会更新以说明清楚。
  • 关于 LP_c_double 对象,这是 Python 对象地址,而不是指针中的地址。您只打印了一个临时指针对象;在下一次打印时,它已经被释放并且相同的地址被重新使用。查看contents 属性,或者只是[0] 索引。

标签: python numpy ctypes


【解决方案1】:

struct 有一个 int 字段,可能是数组长度,尽管我只是猜测没有完整的函数原型。如果情况确实如此,这里有一个示例可能会有所帮助。

首先,我需要在共享库中编译一个测试函数。我将简单地将输入数组乘以 2:

import os
import numpy as np
from ctypes import *

open('tmp.c', 'w').write('''\
typedef struct {
    int v1; double *v2;
} darray;
int test(darray *input, darray *output) {
    int i;
    /* note: this should first test for compatible size */
    for (i=0; i < input->v1; i++)
        *(output->v2 + i) = *(input->v2 + i) * 2;
    return 0;
}
''')
os.system('gcc -shared -o tmp.so tmp.c')

接下来创建 ctypes 定义。我添加了一个classmethod 以从numpy.ndarray 制作一个darray

c_double_p = POINTER(c_double)

class darray(Structure):
    _fields_ = [
      ('v1', c_int),
      ('v2', c_double_p),
    ]
    @classmethod
    def fromnp(cls, a):
        return cls(len(a), a.ctypes.data_as(c_double_p))

lib = CDLL('./tmp.so')
lib.test.argtypes = POINTER(darray), POINTER(darray)

测试:

a1 = np.arange(3) + 1.0
a2 = np.zeros(3)
print 'before:', '\na1 =', a1, '\na2 =', a2 

lib.test(darray.fromnp(a1), darray.fromnp(a2))
print 'after:', '\na1 =', a1, '\na2 =', a2 

输出:

before: 
a1 = [ 1.  2.  3.] 
a2 = [ 0.  0.  0.]
after: 
a1 = [ 1.  2.  3.] 
a2 = [ 2.  4.  6.]

【讨论】:

  • 这段代码非常接近我的。但是,不是每次调用函数时都创建一个新实例,而是在调用 fun 方法时更新我需要的字段(我将 c 代码封装在一个类中)。我也没有代码中的 c_double_p 。我在需要时调用 POINTER(c_double) 。我会试试这个修改。
  • @EuripedesRochaFilho:您没有展示如何使用 v1 字段。此答案将其设置为fromnpclassmethod:cls(len(a), a.ctypes.data_as(c_double_p)) 中的数组长度。将其作为一种方法并分配 c_double_p 只是为了方便。我还展示了它正在修改ndarray 对象的缓冲区。您只需要这些 Structure 实例进行调用。通过 NumPy 数组访问数据。
  • 您猜对了 v1 字段作为 v2 的大小。我还使用 numpy 数组来访问结果。我只是在更新字段,因为在我的任务中我需要多次调用该函数。我正在测试每次调用时创建一个新的结构实例,但这只是因为我现在正在进行超自然测试:)
  • @eryksun:如何将这个很好的示例扩展为矩阵结构? typedef struct {int v1,v2;双** v3}矩阵;
  • @JonatasEduardo,NumPy 矩阵默认为 C 连续数组。这是一个以行为主的结构,即将其视为连接成单个数组的行。 (它不是double ** 指向行的指针的double * 数组。)所以使用带有嵌套for 循环的double *v3,其中i 是行索引,j 是列索引。如果v2 是列数,则[i, j] 元素是input-&gt;v3[i * input-&gt;v2 + j]*(input-&gt;v3 + i * input-&gt;v2 + j),无论您喜欢哪种语法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多