繁体   English   中英

用数组成员包装 C 结构,以便在 python 中访问:SWIG? 赛通? 类型?

[英]Wrap C struct with array member for access in python: SWIG? cython? ctypes?

I want to access a C function that returns a struct containing double arrays (where the lengths of these arrays is given by other int members of the struct) from python. 声明是

typedef struct {
  int dim;
  int vertices;
  int quadrature_degree;
  int polynomial_degree;
  int ngi;
  int quadrature_familiy;
  double *weight; /* 1D: ngi */
  double *l;      /* 2D: ngi * dim */
  double *n;      /* 2D: ngi * vertices */
  double *dn;     /* 3D: ngi * vertices * dim */
} element;

extern void get_element(int dim, int vertices, int quad_degree, int poly_degree, element* e);

重要的一点是我希望能够以正确形状的 NumPy arrays 的形式访问所有double*成员(即dn应该可以作为 3D 数组访问)。

简单地 SWIG 包装这给了我结构就好了,但是所有的double*成员都是<Swig Object of type 'double *' at 0x348c8a0>这使得它们无用。 我玩弄了 NumPy SWIG 接口文件,但无法让任何类型映射(如( DATA_TYPE* INPLACE_ARRAY1, int DIM1 )工作(我认为在这种情况下不可能让它们匹配,但我很乐意被证明是错误的)。

我的猜测是我必须将 NumPy PyArrayObject的代码初始化为这些成员的 PyArrayObject 并且 SWIG 扩展我的结构以使它们在 ZA7F5F35426B927411FC9231B56382 中可访问看起来工作量很大。 谁能看到使用 SWIG 的更好方法? 如果这使事情变得更容易,则可以更改结构或返回它的方法。

或者,我查看了 cython 和 ctypes。 这些会更适合我想要实现的目标吗? 我没有使用过 cython,所以无法判断它的包装能力。 对于 ctypes,我可以粗略地想象如何去做,但这意味着手工编写我希望一个合理自动化的包装器可以为我做的事情。

任何建议都感激不尽!

赛通规则:

cdef extern from "the header.h":

ctypedef struct element:
  int dim
  int vertices
  int quadrature_degree
  int polynomial_degree
  int ngi
  int quadrature_familiy
  double *weight
  double *l
  double *n
  double *dn

void get_element(int dim, int vertices, int quad_degree, int poly_degree, element* e)

然后你可以连接它,从 python 空间

使用 SWIG 需要整个结构的类型映射。 仅指针成员的 Tyepmap 是不够的,因为它们没有上下文来知道用什么大小来初始化 NumPy arrays 。 我设法通过以下类型图获得了我想要的东西(基本上是从 numpy.i 复制和粘贴并适应我的需要,可能不是很强大):

%typemap (in,numinputs=0) element * (element temp) {
  $1 = &temp;
}

%typemap (argout) element * {
  /* weight */
  {
    npy_intp dims[1] = { $1->ngi };
    PyObject * array = PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE, (void*)($1->weight));
    if (!array) SWIG_fail;
    $result = SWIG_Python_AppendOutput($result,array);
  }
  /* l */
  {
    npy_intp dims[2] = { $1->ngi, $1->dim };
    PyObject * array = PyArray_SimpleNewFromData(2, dims, NPY_DOUBLE, (void*)($1->l));
    if (!array) SWIG_fail;
    $result = SWIG_Python_AppendOutput($result,array);
  }
  /* n */
  {
    npy_intp dims[2] = { $1->ngi, $1->vertices };
    PyObject * array = PyArray_SimpleNewFromData(2, dims, NPY_DOUBLE, (void*)($1->n));
    if (!array) SWIG_fail;
    $result = SWIG_Python_AppendOutput($result,array);
  }
  /* dn */
  {
    npy_intp dims[3] = { $1->ngi, $1->vertices, $1->dim };
    PyObject * array = PyArray_SimpleNewFromData(3, dims, NPY_DOUBLE, (void*)($1->dn));
    if (!array) SWIG_fail;
    $result = SWIG_Python_AppendOutput($result,array);
  }
}

This works different from the C function in that it returns a tuple of NumPy arrays with the data I want, which is more convenient than having to extract it from the element object later. 第一个 typemap 还消除了传入element类型的 object 的需要。 因此,我可以对 python 用户完全隐藏element结构。

python 接口最终看起来是这样的:

weight, l, n, dn = get_element(dim, vertices, quadrature_degree, polynomial_degree)

查看 SWIG 的类型图。 它们让您编写自己的代码来处理特定类型、特定实例(类型+名称)甚至 arguments 组。 我没有为结构做过,而是专门处理 C function 采用数组及其大小的情况:

%typemap(in) (int argc, Descriptor* argv) {
    /* Check if is a list */
    if (PyList_Check($input)) {
        int size = PyList_Size($input);
        $1 = size;
        ...
        $2 = ...;
    }
}

这将采用 arguments int argc, Descriptor* argv对(因为提供的名称也必须匹配)并将使用的 PyObject 传递给您,然后您编写进行转换所需的任何 C 代码。 您可以为double *dn做一个类型映射,它将使用 NumPy C API 进行转换。

您总是可以编写带有“元素*”并返回您寻找的元素的辅助函数:

double element_get_weight(const element *elt, unsigned n) {
    assert(n < elt->ngi);  /* or similar */
    return elt->weight[n];
}

如果您需要修改和阅读,当然需要单独的“getter”和“setter”。

SWIG 应该能够轻松地包装所有这些并将它们暴露给 Python。

性能可能不是很好,但可能不会比替代品差。

与使用ctypes创建的模块等效的 SWIG 如下所示:

from ctypes import *
from numpy import *

lib = cdll.LoadLibrary("_get_element.so")

class ELEMENT(Structure):
    _fields_ = [("dim", c_int),
                ("vertices", c_int),
                ("quadrature_degree", c_int),
                ("polynomial_degree", c_int),
                ("ngi", c_int),
                ("quadrature_familiy", c_int),
                ("weight", POINTER(c_double)),
                ("l", POINTER(c_double)),
                ("n", POINTER(c_double)),
                ("dn", POINTER(c_double))]

cget_element = lib.get_element
cget_element.argtypes = [c_int, c_int, c_int, c_int, POINTER(ELEMENT)]
cget_element.restype = None

def get_element(dim, vertices, quad_degree, poly_degree):
    e = ELEMENT()
    cget_element(dim, vertices, quad_degree, poly_degree, byref(e))
    weight = asarray([e.weight[i] for i in xrange(e.ngi)], dtype=float64)
    l = asarray([e.l[i] for i in xrange(e.ngi*e.dim)], dtype=float64).reshape((e.ngi,e.dim))
    n = asarray([e.n[i] for i in xrange(e.ngi*e.vertices)], dtype=float64).reshape((e.ngi,e.vertices))
    dn = asarray([e.dn[i] for i in xrange(e.ngi*e.vertices*e.dim)], dtype=float64).reshape((e.ngi,e.vertices,e.dim))
    return weight, l, n, dn

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM