Cython:在具有融合类型的一个参数中传递多个 numpy 数组
Cython: Passing multiple numpy arrays in one argument with fused types
我已将算法从 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
可以在调用时通过数组或 fill 参数确定,代码会变得更简单,但我找不到保证 C 将接收正确类型的值的好方法。例如,传递 numpy.nan
或 numpy.float64(numpy.nan)
并不能保证数据类型。
经过 Python 和 NumPy 编程 10 年(以及之前 10 年的 C、C++、Matlab 和 Fortran),这是我的总体印象:
用 C、C++ 或 Fortran 编写数值代码通常比用 Cython 更容易。我能想到的唯一例外是最小的代码片段。在 C++ 中,您可以奢侈地使用模板和 STL(如果您愿意,还可以使用 Boost)。
学习使用 NumPy C API。 PyArrayObject(这是 NumPy 数组在 C 中的名称)有一个类型编号,您可以使用它进行分派。您可以使用 PyArrayObject* 上的宏 PyArray_TYPE() 获取它。 numpy.float64 映射到类型编号 NPY_FLOAT64,numpy.float32 映射到类型编号 NPY_FLOAT32,等等。然后你就有了相应的 C 和 C++ typedef,你可以在你的 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 编码,但这里有一个限制,即您可以静态键入参数的数量。对于 "down-to-the-iron" 数字代码,我认为使用纯 C++ 更好,但 Cython 是将其暴露给 Python 的绝佳工具。所以用 C++ 编写你的数字内容并使用 Cython 调用你的 C++。这将是我能给的最好的建议 Cython 是编写 C 扩展 Python 的优秀工具,但当您真正需要 C++ 时,它不能替代 C++。
至于你的问题:你想做的事情其实是不可能的。因为在 C 或 C++ 中,这是 Cython 发出的, numpy.ndarray 是 PyArrayObject* 而不管 dtype。所以你需要手写 switch 语句。
我已将算法从 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
可以在调用时通过数组或 fill 参数确定,代码会变得更简单,但我找不到保证 C 将接收正确类型的值的好方法。例如,传递 numpy.nan
或 numpy.float64(numpy.nan)
并不能保证数据类型。
经过 Python 和 NumPy 编程 10 年(以及之前 10 年的 C、C++、Matlab 和 Fortran),这是我的总体印象:
用 C、C++ 或 Fortran 编写数值代码通常比用 Cython 更容易。我能想到的唯一例外是最小的代码片段。在 C++ 中,您可以奢侈地使用模板和 STL(如果您愿意,还可以使用 Boost)。
学习使用 NumPy C API。 PyArrayObject(这是 NumPy 数组在 C 中的名称)有一个类型编号,您可以使用它进行分派。您可以使用 PyArrayObject* 上的宏 PyArray_TYPE() 获取它。 numpy.float64 映射到类型编号 NPY_FLOAT64,numpy.float32 映射到类型编号 NPY_FLOAT32,等等。然后你就有了相应的 C 和 C++ typedef,你可以在你的 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 编码,但这里有一个限制,即您可以静态键入参数的数量。对于 "down-to-the-iron" 数字代码,我认为使用纯 C++ 更好,但 Cython 是将其暴露给 Python 的绝佳工具。所以用 C++ 编写你的数字内容并使用 Cython 调用你的 C++。这将是我能给的最好的建议 Cython 是编写 C 扩展 Python 的优秀工具,但当您真正需要 C++ 时,它不能替代 C++。
至于你的问题:你想做的事情其实是不可能的。因为在 C 或 C++ 中,这是 Cython 发出的, numpy.ndarray 是 PyArrayObject* 而不管 dtype。所以你需要手写 switch 语句。