Python Numpy:将集成二维阵列的函数矢量化
我需要对2D阵列执行以下集成: 也就是说,网格中的每个点都得到值RC,它是整个场与特定点(x,y)处的场值U之间的差值在2D上的积分,乘以标准化核,在1D版本中为:Python Numpy:将集成二维阵列的函数矢量化,python,arrays,numpy,convolution,integral,Python,Arrays,Numpy,Convolution,Integral,我需要对2D阵列执行以下集成: 也就是说,网格中的每个点都得到值RC,它是整个场与特定点(x,y)处的场值U之间的差值在2D上的积分,乘以标准化核,在1D版本中为: 到目前为止,我所做的是对索引进行低效的迭代: 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
到目前为止,我所做的是对索引进行低效的迭代:
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
您可以在此处看到居中化
功能的结果:
U=np.eye(20)
plt.imshow(centerlizing(U,10,1)[10,10])
我确信我还有其他的bug,所以任何反馈都会受到热烈欢迎,但我真正感兴趣的是了解如何以矢量化的方式更快地执行此操作。假设
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
中对循环进行矢量化是一个添加更多维度的问题。你在一个2DU
向量上做了两个循环,所以要将其矢量化,只需将其转换为4D。在两个嵌套循环中调用规范化的双模内核2D
,每个循环只偏移一小步。这重复了许多计算
centerzing_2D
的一种优化方法是计算一次较大范围的内核,然后定义UB
以将移动的视图带入其中。这可以使用跨步技巧
,不幸的是,这是相当高级的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
接下来,我们通过使用优化的中心化\u 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)
的性能:
为了符合您的公式,让
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)
实际使用中的U形是什么?我想这个问题在Nah会得到更好的答案,更多的numpy人会在这里闲逛。这是一个纯粹的
numpy
矢量化问题。最大的效率改进是在(nx/dx,ny/dx)
meshgrid
上创建和运行计算,但只对(nx,ny)
切片求和。或者这是一个很难说的错误。输出是您期望的吗?非常有趣的解决方案-但是如果输入是给定的2D数组,如何使其工作。在我的例子中,我对一个变量进行时间积分,这个变量是2D中的一个字段,所以在每次迭代中,U都是不同的。
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)