【问题标题】:Assigning numpy data in cython to a view将 cython 中的 numpy 数据分配给视图
【发布时间】:2014-07-12 17:38:28
【问题描述】:

我正在尝试将 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.]]))

【问题讨论】:

    标签: python arrays numpy cython memoryview


    【解决方案1】:

    您可以创建一个临时缓冲区来接收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】:

      这不是由视图或 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.]])
      >>> 
      

      【讨论】:

      • +1 我喜欢你的方法,但是如何让np.copyto() 与 memoryvew 一起工作?它在这里返回:TypeError: argument 1 must be numpy.ndarray, not _stack._memoryviewslice
      • 感谢这两种方法。我使用临时缓冲区实现了它并将其复制回 B,但我想节省复制所需的时间。但是,我希望将 B 重定向到指向 la.inv() 返回的“内容/数据指针”。在这种情况下,我不介意将 B 定义为指向双精度数组的指针,但我想知道如何仅访问 la.inv() 的“数据指针”。知道怎么做吗?
      • @SaulloCastro 在描述方面稍微更新了答案,但我不知道它是否适用于内存视图。感谢“指点”,如果问题没有用,将删除该问题
      【解决方案3】:

      如果我在 la.inv(A) 上创建 mememoryview,我可以执行 1 步,并且可能是高效的,从 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
      

      我猜 memoryview 副本会更快,但样本数组对于有意义的时间测试来说太小了。

      我在回复https://stackoverflow.com/a/30418448/901925时对此进行了测试


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

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

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

      受我使用np.PyArray_SimpleNewFromDatahttps://stackoverflow.com/a/28855962/901925 找到的示例的启发,我尝试使用其他PyArray... 函数来执行相同类型的base 重新分配:

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

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

      【讨论】:

        猜你喜欢
        • 2021-07-07
        • 1970-01-01
        • 2017-04-06
        • 1970-01-01
        • 2014-06-30
        • 2014-01-25
        • 1970-01-01
        • 2019-09-21
        相关资源
        最近更新 更多