【问题标题】:Numpy: vectorizing a function that integrate 2D arrayNumpy:向量化集成二维数组的函数
【发布时间】:2017-08-10 07:42:17
【问题描述】:

我需要为二维数组执行以下集成: 也就是说,网格中的每个点都得到值 RC,它是整个字段与字段 U 在某一点 (x,y) 的值之间的差值的 2D 积分,乘以标准化内核,一维版本为:

到目前为止,我所做的是对索引的低效迭代:

def normalized_bimodal_kernel_2D(x,y,a,x0=0.0,y0=0.0):
    """ Gives a kernel that is zero in x=0, and its integral from -infty to 
    +infty is 1.0. The parameter a is a length scale where the peaks of the 
    function are."""
    dist = (x-x0)**2 + (y-y0)**2
    return (dist*np.exp(-(dist/a)))/(np.pi*a**2)


def RC_2D(U,a,dx):
    nx,ny=U.shape
    x,y = np.meshgrid(np.arange(0,nx, dx),np.arange(0,ny,dx), sparse=True)
    UB = np.zeros_like(U)
    for i in xrange(0,nx):
        for j in xrange(0,ny):
            field=(U-U[i,j])*normalized_bimodal_kernel_2D(x,y,a,x0=i*dx,y0=j*dx)
            UB[i,j]=np.sum(field)*dx**2
    return UB

def centerlizing_2D(U,a,dx):
    nx,ny=U.shape
    x,y = np.meshgrid(np.arange(0,nx, dx),np.arange(0,ny,dx), sparse=True)
    UB = np.zeros((nx,ny,nx,ny))
    for i in xrange(0,nx):
        for j in xrange(0,ny):
            UB[i,j]=normalized_bimodal_kernel_2D(x,y,a,x0=i*dx,y0=j*dx)
    return UB

您可以在此处查看centeralizing 函数的结果:

U=np.eye(20)
plt.imshow(centerlizing(U,10,1)[10,10])

我确定我还有其他错误,因此欢迎任何反馈,但我真正感兴趣的是了解如何以矢量化方式更快地执行此操作。

【问题讨论】:

  • 实际使用中U.shape是什么?
  • 我认为这个问题会在codereview.stackexchange.com得到更好的答案
  • 不,更多numpy 的人在这里闲逛。这是一个纯粹的numpy 向量化问题。
  • 最大的效率提升是您在(nx/dx, ny/dx) meshgrid 上创建和运行计算,但仅对其中的(nx, ny) 切片求和。或者也许这是一个错误,很难说。输出是否符合您的预期?

标签: python arrays numpy convolution integral


【解决方案1】:

normalized_bimodal_kernel_2D 在两个嵌套循环中调用,每个循环仅移动它的偏移量很小。这会重复许多计算。

centerlizing_2D 的优化是计算内核一次以获得更大的范围,然后定义 UB 以将移动视图纳入其中。这可以使用stride_tricks,不幸的是它是相当高级的numpy。

def centerlizing_2D_opt(U,a,dx):
    nx,ny=U.shape    
    x,y = np.meshgrid(np.arange(-nx//2, nx+nx//2, dx),
                      np.arange(-nx//2, ny+ny//2, dx),  # note the increased range
                      sparse=True)
    k = normalized_bimodal_kernel_2D(x, y, a, x0=nx//2, y0=ny//2)
    sx, sy = k.strides    
    UB = as_strided(k, shape=(nx, ny, nx*2, ny*2), strides=(sy, sx, sx, sy))
    return UB[:, :, nx:0:-1, ny:0:-1]

assert np.allclose(centerlizing_2D(U,10,1), centerlizing_2D_opt(U,10,1)) # verify it's correct

是的,它更快:

%timeit centerlizing_2D(U,10,1)      #   100 loops, best of 3:  9.88 ms per loop
%timeit centerlizing_2D_opt(U,10,1)  # 10000 loops, best of 3: 85.9  µs per loop

接下来,我们通过优化centerlizing_2D 例程来表达RC_2D

def RC_2D_opt(U,a,dx):
    UB_tmp = centerlizing_2D_opt(U, a, dx)
    U_tmp = U[:, :, None, None] - U[None, None, :, :]
    UB = np.sum(U_tmp * UB_tmp, axis=(0, 1))
    return UB

assert np.allclose(RC_2D(U,10,1), RC_2D_opt(U,10,1))

%timeit RC_2D(U,10, 1)的表现:

#original:    100 loops, best of 3: 13.8 ms per loop
#@DanielF's:  100 loops, best of 3:  6.98 ms per loop
#mine:       1000 loops, best of 3:  1.83 ms per loop

【讨论】:

    【解决方案2】:

    假设dx=1,因为我不确定你想用这种离散化做什么:

    def normalized_bimodal_kernel_2D(x, y, a):  
        #generating a 4-d tensor instead of 1d vector
        dist = (x[:,None,None,None] - x[None,None,:,None])**2 +\
               (y[None,:,None,None] - y[None,None,None,:])**2    
        return (dist * np.exp(-(dist / a))) / (np.pi * a**2)
    
    def RC_2D(U, a):
        nx, ny = U.shape
        x, y = np.arange(nx), np.arange(ny)
        U4 = U[:, :, None, None] - U[None, None, :, :] #Another 4d
        k = normalized_bimodal_kernel_2D(x, y, a)
        return np.einsum('ijkl,ijkl->ij', U4, k)
    
    
    def centerlizing_2D(U, a):
        nx, ny = U.shape
        x, y = np.arange(nx), np.arange(ny)
        return normalized_bimodal_kernel_2D(x, y, a)
    

    基本上,在numpy 中对for 循环进行循环是增加更多维度的问题。您在 2D U 向量上进行了两次循环,因此要进行向量化,只需将其转换为 4D。

    【讨论】:

      【解决方案3】:

      为了符合您的公式,让U 成为一个函数。

      然后您只需将x,y,x',y'np.ix_ 放在四个不同的维度上,然后仔细翻译您的公式。 Numpy 广播将完成剩下的工作。

      a=20
      x,y,xp,yp=np.ix_(*[np.linspace(0,1,a)]*4)
      
      def U(x,y) : return np.float32(x == y)  # function "eye"
      
      def f(x,y,xp,yp,a):
          r2=(x-xp)**2+(y-yp)**2
          return r2*np.exp(-r2/a)*(U(xp,yp) - U(x,y))/np.pi/a/a
      
      #f(x,y,xp,yp,a).shape is (20, 20, 20, 20)
      
      RC=f(x,y,xp,yp,a).sum(axis=(2,3))
      #RC.shape is (20, 20)
      

      【讨论】:

      • 非常有趣的解决方案 - 但是如果我的输入是给定的 2D 数组,我怎样才能使它工作。在我的例子中,我正在对作为 2D 字段的变量进行时间积分,因此在每次迭代中 U 都是不同的。
      猜你喜欢
      • 2016-12-07
      • 2018-01-09
      • 1970-01-01
      • 2015-09-04
      • 2023-02-08
      • 1970-01-01
      • 1970-01-01
      • 2021-08-10
      • 1970-01-01
      相关资源
      最近更新 更多