首页 文章

将cython中的numpy数据分配给视图

提问于
浏览
1

我试图将linalg反函数(la.inv)的输出分配给cython中的视图 . 不幸的是,这不起作用 . 我总是可以将la.inv()的输出分配给临时的ndarray对象,然后将其内容复制到视图中 .

有没有更好的方法来做到这一点 .

cpdef int testfunc1(np.ndarray[np.float_t, ndim=2] A,
                    double [:,:] B) except -1:

    print("inverse of A:", la.inv(A))
    if np.isnan(A).any():
        return -1
    else:
        B = la.inv(A)
        return 1


cpdef int testfunc2(np.ndarray[np.float_t, ndim=2] A) except -1:
    cdef long p = np.shape(A)[0], status
    cdef B = np.zeros(shape=(p, p), dtype=float)
    cdef double[:,:] BView = B
    print("before inverse. B: ", B)
    status = testfunc1(A, BView)
    print("after inverse. B: ", B)
    if status == -1:
        return -1
    else:
        return 1

输出:

A = np.random.ranf(4).reshape(2, 2)
        status = testfunc2(A)
        if status == -1:
            raise ValueError("nan cell.")
        else:
            print("pass")

('before inverse. B: ', array([[ 0.,  0.],
       [ 0.,  0.]]))
('inverse of A:', array([[ 4.4407987 , -0.10307341],
       [-2.26088593,  1.19604499]]))
('after inverse. B: ', array([[ 0.,  0.],
       [ 0.,  0.]]))

3 回答

  • 1

    这不是由视图或Cython引起的 . B = la.inv(A) 创建一个新数组,并在 testfunc1 范围内为其命名 B . 这不会影响 testfunc2 中名称为 B 的数组 .

    请注意,您的代码(通过NumPy函数完成繁重的工作)不太可能从Cython中受益 .

    使这项工作的一种方法是:

    np.copyto(B, la.inv(A))
    

    testfunc1 . @SaulloCastro提到这在Cython中不起作用,因为 B 具有内存视图类型,但是你可以通过将参数 B 声明为ndarray(不确定这一点)来使其工作 . 否则没有Cython:

    >>> import numpy as np
    >>> X = np.zeros((5, 5))
    >>> B = X[:3, :3]
    >>> A = np.ones((3, 3))
    >>> np.copyto(B, A)
    >>> X
    array([[ 1.,  1.,  1.,  0.,  0.],
           [ 1.,  1.,  1.,  0.,  0.],
           [ 1.,  1.,  1.,  0.,  0.],
           [ 0.,  0.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  0.,  0.]])
    >>>
    
  • 0

    您可以创建一个临时缓冲区,它将接收 la.inv() 的值,然后填充内存视图:

    import numpy as np
    cimport numpy as np
    import numpy.linalg as la
    
    cpdef int testfunc1(np.ndarray[np.float_t, ndim=2] A,
                        double [:,:] B) except -1:
        cdef np.ndarray[np.float_t, ndim=2] buff
        cdef int i, j
    
        print("inverse of A:", la.inv(A))
        if np.isnan(A).any():
            return -1
        else:
            buff = la.inv(A)
            for i in range(buff.shape[0]):
                for j in range(buff.shape[1]):
                    B[i, j] = buff[i, j]
            return 1
    
    cpdef int testfunc2(np.ndarray[np.float_t, ndim=2] A) except -1:
        cdef long p = np.shape(A)[0], status
        cdef B = np.zeros(shape=(p, p), dtype=float)
        cdef double[:,:] BView = B
        print("before inverse. B: ", B)
        status = testfunc1(A, BView)
        print("after inverse. B: ", B)
        if status == -1:
            return -1
        else:
            return 1
    

    正如@MrE所指出的,如果使用 np.ndarray 而不是MemoryView,则可以使用 np.copyto()

    cpdef int testfunc1(np.ndarray[np.float_t, ndim=2] A,
                        np.ndarray[np.float_t, ndim=2] B) except -1:
        cdef int i, j
        print("inverse of A:", la.inv(A))
        if np.isnan(A).any():
            return -1
        else:
            np.copyto(B, la.inv(A))
            return 1
    
    cpdef int testfunc2(np.ndarray[np.float_t, ndim=2] A) except -1:
        cdef long p = np.shape(A)[0], status
        cdef np.ndarray[np.float_t, ndim=2] B, BView
        B = np.zeros(shape=(p, p), dtype=float)
        BView = B
        print("before inverse. B: ", B)
        status = testfunc1(A, BView)
        print("after inverse. B: ", B)
        if status == -1:
            return -1
        else:
            return 1
    
  • 2

    如果我在 la.inv(A) 上创建一个mememoryview,我可以对memoryview副本执行一步,并且可能是高效的memoryview:

    cpdef int testfunc1c(np.ndarray[np.float_t, ndim=2] A,
                        double [:,:] BView) except -1:
        cdef double[:,:] CView
        print("inverse of A:", la.inv(A))
        if np.isnan(A).any():
            return -1
        else:
            CView = la.inv(A)
            BView[...] = CView
            return 1
    

    生产环境 :

    In [4]: so23827902.testfunc2(A)
    ('before inverse. B: ', array([[ 0.,  0.],
           [ 0.,  0.]]))
    ('inverse of A:', array([[ 1.04082818, -0.14530117],
           [-0.24050511,  1.13292585]]))
    ('after inverse. B: ', array([[ 1.04082818, -0.14530117],
           [-0.24050511,  1.13292585]]))
    Out[4]: 1
    

    我猜测内存视图副本会更快,但样本数组太小而无法进行有意义的时间测试 .

    我在https://stackoverflow.com/a/30418448/901925作为回复的一部分进行了测试


    Python 中,您可以重新分配数组的 data 缓冲区(尽管存在一定风险):

    B = np.zeros_like(A)
    C = la.inv(A)
    B.data = C.data
    

    cython 使用此语句在编译阶段引发有关不安全指针的错误 .

    灵感来自于https://stackoverflow.com/a/28855962/901925使用 np.PyArray_SimpleNewFromData 找到的示例,我尝试使用其他 PyArray... 函数来执行相同类型的 base 重新分配:

    np.PyArray_SetBaseObject(B, np.PyArray_BASE(la.inv(A)))
    

    目前我正在尝试解决 AttributeError: 'module' object has no attribute 'PyArray_SetBaseObject' 错误 .

相关问题