【问题标题】:Cython: Passing multiple numpy arrays in one argument with fused typesCython:使用融合类型在一个参数中传递多个 numpy 数组
【发布时间】:2015-01-02 01:44:21
【问题描述】:

我已经将一个从 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")

我意识到我可以在元组中指定类型,但如果我理解正确的话,没有比“对象”更复杂的了。有谁知道一种更清洁的方式来做我想做的事情?感谢您提供任何其他 cython 提示。

还有其他参数传递,包括与数组相同类型的fill_value 参数。如果 test_dtype 可以在调用时通过数组或填充参数确定,代码会变得更简单,但我找不到保证 C 将接收正确类型的值的好方法。例如,传递numpy.nannumpy.float64(numpy.nan) 并不能保证数据类型。

【问题讨论】:

    标签: python numpy cython


    【解决方案1】:

    在 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++ 代码中使用相应的 C 和 C++ 类型定义:如果 PyArray_TYPE(x) == NPY_FLOAT64,则数据在 C 或 C++ 中使用的类型是 npy_float64。这样您就可以编写完全由您传入的 NumPy 数组定义的 C 或 C++ 代码。

    我通常在 PyArray_TYPE(x) 上使用 switch 语句,并使用 NPY_FLOAT64、NPY_FLOAT32 等进行 case。对于每种情况,我都调用具有正确模板类型的模板化 C++ 函数。这将我需要编写的代码量保持在最低限度。

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

    Cython 非常适合封装 C 和 C++ 并避免繁琐的 Python C API 编码,但这里限制了静态类型参数的数量。对于“真正的”数字代码,我认为使用纯 C++ 更好,但 Cython 是将其暴露给 Python 的绝佳工具。所以用 C++ 编写你的数字内容并使用 Cython 来调用你的 C++。这将是我能给的最好建议 Cython 是为 Python 编写 C 扩展的出色工具,但当您真正想要 C++ 时,它不能替代 C++。

    至于你的问题:你想做的事情是不可能的。因为在 Cython 发出的 C 或 C++ 中,numpy.ndarray 是 PyArrayObject*,无论 dtype 是什么。所以你需要手工编码 switch 语句。

    【讨论】:

    • 有道理。我曾考虑直接用 C/C++ 编写代码的主要部分,但认为我应该为将来可能的项目学习 cython。我正在使用的代码的实际工作部分是 cython 中的
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多