Cython:使用融合类型将多个numpy数组传递给一个参数

4

我已经将一个算法从C语言重写为Cython,这样我就可以利用融合类型并且更容易从Python调用。该算法可以使用多个数组以及一些其他参数。这些数组被接受为指向指针的指针(例如)。我想通过提供多个数组作为numpy数组的元组,从Python中调用cython代码,但是由于融合类型的原因,这样做会变得有些混乱。以下是我现在的工作方式的简单示例:

import numpy
cimport numpy

ctypedef fused test_dtype:
    numpy.float32_t
    numpy.float64_t

cdef int do_stuff(test_dtype **some_arrays):
    if test_dtype is numpy.float32_t:
        return 1
    elif test_dtype is numpy.float64_t:
        return 2
    else:
        return -1

def call_do_stuff(tuple some_arrays):
    cdef unsigned int num_items = len(some_arrays)
    cdef void **the_pointer = <void **>malloc(num_items * sizeof(void *))
    if not the_pointer:
        raise MemoryError("Could not allocate memory")
    cdef unsigned int i
    cdef numpy.ndarray[numpy.float32_t, ndim=2] tmp_arr32
    cdef numpy.ndarray[numpy.float64_t, ndim=2] tmp_arr64
    if some_arrays[0].dtype == numpy.float32:
        for i in range(num_items):
            tmp_arr32 = some_arrays[i]
            the_pointer[i] = &tmp_arr32[0, 0]
        return do_stuff(<numpy.float32_t **>the_pointer)
    elif some_arrays[0].dtype == numpy.float64:
        for i in range(num_items):
            tmp_arr64 = some_arrays[i]
            the_pointer[i] = &tmp_arr64[0, 0]
        return do_stuff(<numpy.float64_t **>cols_pointer)
    else:
        raise ValueError("Array data type is unknown")

我知道我可以在元组中指定类型,但是如果我理解正确的话,不能指定比"object"更复杂的内容。有没有人知道更简洁的实现方式?欢迎分享其它Cython技巧。
还有其他参数需要传递,包括与数组相同类型的 "fill_value" 参数。如果可以通过数组或填充参数在调用时确定 "test_dtype",那么代码会更简单,但我找不到一个好的方法来保证 C 以正确的类型接收值。例如,传递 numpy.nan 或 numpy.float64(numpy.nan) 并不能保证数据类型。
1个回答

7

我已经使用Python和NumPy编程了10年(在此之前的10年,我使用的是C、C++、Matlab和Fortran),这是我的一般印象:

通常情况下,在C、C++或Fortran中编写数值代码比在Cython中编写更容易。我只能想到最小的代码段是个例外。在C++中,你可以使用模板和STL(如果你喜欢,还可以使用Boost)。

学习使用NumPy C API。PyArrayObject(即在C中称为NumPy数组的对象)有一个类型编号,您可以用它来派发任务。您可以通过在PyArrayObject*上使用宏PyArray_TYPE()获得它。numpy.float64映射到类型编号NPY_FLOAT64,numpy.float32映射到类型编号NPY_FLOAT32等。然后,您有相应的C和C++ typedefs可以在C或C++代码中使用:如果PyArray_TYPE(x)== NPY_FLOAT64,则C或C++中要使用的数据类型是npy_float64。这样,您可以编写仅由传递进来的NumPy数组定义的C或C++代码。

我通常在PyArray_TYPE(x)上使用switch语句,并针对NPY_FLOAT64、NPY_FLOAT32等情况调用具有正确模板类型的C++函数。这将使我需要编写的代码数量最少。

http://docs.scipy.org/doc/numpy/reference/c-api.html

Cython非常适合包装C和C++,避免繁琐的Python C API编码,但你可以静态类型参数是有限制的。对于“针对铁”数值代码,我认为最好使用普通的C++,但Cython是将其暴露给Python的优秀工具。因此,在C++中编写数值代码,并使用Cython调用您的C++会是我能给出的最佳建议。Cython是编写C扩展Python的优秀工具,但当您真正需要C++时,它并不能取代C++。

至于您的问题:您想要做的事情实际上并不可行。因为在C或C++中,Cython发出的numpy.ndarray都是PyArrayObject*,无论dtype如何。因此,您需要手动编写switch语句。


有道理。我曾考虑用纯C/C++编写代码的主要部分,但想到未来可能会有项目需要使用cython,所以决定学习一下。我正在使用的代码工作部分在cython中不到100行。它最初是用C语言编写的,并使用了类型的case语句(简单可执行文件,没有外部调用的API/库)。作为一次学习经历,我尝试使用cython融合类型来简化代码(更易于阅读)和openmp。我承认,直接使用C/C++扩展而不使用Cython可能是最清晰的解决方案。谢谢,我想我现在会坚持使用switch。 - djhoese

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接