簡體   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