如何使用通用浮点类型在cython中声明一个ndarray

Mag*_*n88 8 python numpy cython

如果它应该能够同时处理float和double,那么在cython中声明numpy数组的最佳方法是什么?

我想用内存视图是不可能的,因为数据类型是至关重要的,但是对于ndarray有什么方法可以给它一个通用浮点类型,这仍然有利于cython的快速性?

所以这就是我通常会做的事情:

def F( np.ndarray A):
    A += 10
Run Code Online (Sandbox Code Playgroud)

我已经看到还有:

def F( np.ndarray[np.float32_t, ndim=2] A):
    A += 10
Run Code Online (Sandbox Code Playgroud)

但这又会给这个类型增加一点尺寸.我还想过根据位大小(32或64)在函数内部创建内存视图.

任何想法都受到高度赞赏


非常感谢您对该floating类型的提示.我试过这样的

import numpy as np
cimport numpy as np
import cython
cimport cython
from libc.math cimport sqrt, abs
from cython cimport floating

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
def Rot_Matrix(np.ndarray[floating, ndim=3] Fit_X,
               np.ndarray[floating, ndim=3] Ref_X,
               weight = None):
    cdef:
        unsigned int t, T = Fit_X.shape[0]
        unsigned int n, N = Fit_X.shape[1]
        np.ndarray[floating, ndim=3] Rot = np.empty((T,3,3))

    return Rot
Run Code Online (Sandbox Code Playgroud)

当我现在用两个np.float32数组调用该函数时,我得到了错误

ValueError:Buffer dtype不匹配,预期'float'但得到'double'

如果我不在制动器中使用类型定义,Rot那么它会读取np.ndarray[floating, ndim=3] Rot = np.empty((T,3,3))然后我得到ndarray,它工作正常.你碰巧有一个指针,告诉我我做错了什么吗?

Vee*_*rac 12

Well this is actually really easy with fused types support:

This goes inside your code.

from cython cimport floating

def cythonfloating_memoryview(floating[:, :] array):
    cdef int i, j

    for i in range(array.shape[0]):
        for j in range(array.shape[1]):
            array[i, j] += 10
Run Code Online (Sandbox Code Playgroud)

Of course, there are loads of ways of doing this:

Name this fuzed.pyx. There's no need to compile or run cython on it; it's handled by pyximport. Don't use pyximport for production code, though, as you should typically only ship .c files.

from cython cimport floating
from numpy import float32_t, float64_t, ndarray

ctypedef fused myfloating:
    float32_t
    float64_t

def cythonfloating_memoryview(floating[:, :] array):
    # ...

def cythonfloating_buffer(ndarray[floating, ndim=2] array):
    # ...

def myfloating_memoryview(myfloating[:, :] array):
    # ...

def myfloating_buffer(ndarray[myfloating, ndim=2] array):
    # ...
Run Code Online (Sandbox Code Playgroud)

and here's a little test script:

Name this test.py and run it as a normal Python script:

import pyximport
pyximport.install()

import fuzed
import numpy

functions = [
    fuzed.cythonfloating_memoryview,
    fuzed.cythonfloating_buffer,
    fuzed.myfloating_memoryview,
    fuzed.myfloating_buffer,
]

for function in functions:
    floats_32 = numpy.zeros((100, 100), "float32")
    doubles_32 = numpy.zeros((100, 100), "float64")

    function(floats_32)
    function(doubles_32)

    print(repr(floats_32))
    print(repr(doubles_32))
Run Code Online (Sandbox Code Playgroud)

It's worth noting that fused types are specialised at compilation, and are constant for a particular function invocation. The empty Numpy array you make is always of a double type, but you assign it to either a 32-bit float or a 64-bit float. Here's what you should do:

from cython cimport floating
import numpy

def do_some_things(floating[:] input):
    cdef floating[:] output

    if floating is float:
        output = numpy.empty(10, dtype="float32")
    elif floating is double:
        output = numpy.empty(10, dtype="float64")
    else:
        raise ValueError("Unknown floating type.")

    return output
Run Code Online (Sandbox Code Playgroud)

and some tests to prove the point:

import pyximport
pyximport.install()
#>>> (None, None)

import floatingtest
import numpy

floatingtest.do_some_things(numpy.empty(10, dtype="float32"))
#>>> <MemoryView of 'ndarray' at 0x7f0a514b3d88>
floatingtest.do_some_things(numpy.empty(10, dtype="float32")).itemsize
#>>> 4

floatingtest.do_some_things(numpy.empty(10, dtype="float64"))
#>>> <MemoryView of 'ndarray' at 0x7f0a514b3d88>
floatingtest.do_some_things(numpy.empty(10, dtype="float64")).itemsize
#>>> 8
Run Code Online (Sandbox Code Playgroud)