Python 不强制周期性边界的二维卷积
Python 2D convolution without forcing periodic boundaries
我正在为一个疾病问题建模,其中二维景观中的每个人都具有由(径向基)核函数描述的传递率。我的目标是将内核与人口密度进行卷积,以便输出捕获整个景观的传播风险。
我使用 NumPy 的 2D FFT 和反 FFT 函数执行了卷积。但是,这会在结果中强制使用 periodic/wrapped 边界条件,这不适合我的模型。有没有一种方法可以在原始固定边界的上下文中进行卷积?
import numpy as np
import random
from math import *
import matplotlib.pyplot as plt
''' Landscape parameters '''
L = 10.
nx = 100
dx = L/nx
hs = .5 * dx # half-step
ulist = np.linspace(hs, L-hs, nx)
''' Radial Basis Function Kernel '''
alpha = 1.
i, j = ulist.reshape(nx,1), ulist.reshape(1,nx)
r = np.minimum(i-ulist[0], L-i+ulist[0])**2 + np.minimum(j-ulist[0], L-j+ulist[0])**2
rbf = sqrt(1 / (2 * alpha ** 2))
ker = np.exp(-(rbf * r) ** 2)
ker = ker/np.sum(ker)
''' Population Density '''
ido = np.random.randint(nx, size=(1000,2)).astype(np.int)
og = np.zeros((nx,nx))
np.add.at(og, (ido[:,0], ido[:,1]), 1)
''' Convolution via FFT and inverse-FFT '''
v1 = np.fft.fft2(ker)
v2 = np.fft.fft2(og)
v0 = np.fft.ifft2(v1*v2)
dd = np.abs(v0)
plt.plot(ido[:,1], ido[:,0], 'ko', alpha=.5)
plt.imshow(dd, origin='origin')
plt.show()
为了做你想做的,你需要用零填充og
,并相应地扩展ker
(因为它已经循环移位,你只需要扩展它更多以适应og
).
的大小
填充 og
(300x300):
原始 ker
(100x100):
展开 ker
(300x300):
代码:
pad = 100
og = np.pad(og, pad, mode='constant')
new_ker = np.zeros_like(og)
new_ker[:nx//2,:nx//2] = ker[:nx//2,:nx//2]
new_ker[:nx//2,-nx//2:] = ker[:nx//2,-nx//2:]
new_ker[-nx//2:,:nx//2] = ker[-nx//2:,:nx//2]
new_ker[-nx//2:,-nx//2:] = ker[-nx//2:,-nx//2:]
ker = new_ker
# doesn't change
v1 = np.fft.fft2(ker)
v2 = np.fft.fft2(og)
v0 = np.fft.ifft2(v1*v2)
# unpadding the result
v0 = v0[pad:-pad,pad:-pad]
(展开比较乱,直接生成内核就可以了,我只是想把展开的部分分开。)
原结果:
填充结果:
如果您不介意使用 scipy
,它可以为您完成这一切(包括边界条件的其他变体)。有关更多选项,请参阅 fftconvolve
or convolve2d
。请注意,您 不必 必须 pre-roll 您的内核才能使用这些函数(也就是说,最大值应该在中心,而不是在角落)。示例代码(产生与上面手动填充相同的结果):
# unroll the kernel (again, can be done directly when generating `ker`)
ker = np.roll(ker, 50, 0)
ker = np.roll(ker, 50, 1)
from scipy.signal import fftconvolve
dd = fftconvolve(og, ker, mode='same')
每当你使用基于 fft
的方法时,你总是在问题中引入周期性,本质上,根据定义。
如果你不想要这个,你应该首先在计算级别之前在数学建模级别定义你想要的是什么。
记住一些根据我的经验在很多情况下都非常有用的技巧:
-- 将您的问题嵌入更大的 2d space 以减少周期性影响。换句话说,尽可能地推动周期性边界以模拟 'free-space' 情况。
-- 使用离散余弦变换代替。余弦变换具有使边界条件对称化的作用。它的工作原理就好像在每个边界之外都有您的数据的镜像反射。在某些问题中,这用于模拟反射边界条件。
我正在为一个疾病问题建模,其中二维景观中的每个人都具有由(径向基)核函数描述的传递率。我的目标是将内核与人口密度进行卷积,以便输出捕获整个景观的传播风险。
我使用 NumPy 的 2D FFT 和反 FFT 函数执行了卷积。但是,这会在结果中强制使用 periodic/wrapped 边界条件,这不适合我的模型。有没有一种方法可以在原始固定边界的上下文中进行卷积?
import numpy as np
import random
from math import *
import matplotlib.pyplot as plt
''' Landscape parameters '''
L = 10.
nx = 100
dx = L/nx
hs = .5 * dx # half-step
ulist = np.linspace(hs, L-hs, nx)
''' Radial Basis Function Kernel '''
alpha = 1.
i, j = ulist.reshape(nx,1), ulist.reshape(1,nx)
r = np.minimum(i-ulist[0], L-i+ulist[0])**2 + np.minimum(j-ulist[0], L-j+ulist[0])**2
rbf = sqrt(1 / (2 * alpha ** 2))
ker = np.exp(-(rbf * r) ** 2)
ker = ker/np.sum(ker)
''' Population Density '''
ido = np.random.randint(nx, size=(1000,2)).astype(np.int)
og = np.zeros((nx,nx))
np.add.at(og, (ido[:,0], ido[:,1]), 1)
''' Convolution via FFT and inverse-FFT '''
v1 = np.fft.fft2(ker)
v2 = np.fft.fft2(og)
v0 = np.fft.ifft2(v1*v2)
dd = np.abs(v0)
plt.plot(ido[:,1], ido[:,0], 'ko', alpha=.5)
plt.imshow(dd, origin='origin')
plt.show()
为了做你想做的,你需要用零填充og
,并相应地扩展ker
(因为它已经循环移位,你只需要扩展它更多以适应og
).
填充 og
(300x300):
ker
(100x100):
ker
(300x300):
代码:
pad = 100
og = np.pad(og, pad, mode='constant')
new_ker = np.zeros_like(og)
new_ker[:nx//2,:nx//2] = ker[:nx//2,:nx//2]
new_ker[:nx//2,-nx//2:] = ker[:nx//2,-nx//2:]
new_ker[-nx//2:,:nx//2] = ker[-nx//2:,:nx//2]
new_ker[-nx//2:,-nx//2:] = ker[-nx//2:,-nx//2:]
ker = new_ker
# doesn't change
v1 = np.fft.fft2(ker)
v2 = np.fft.fft2(og)
v0 = np.fft.ifft2(v1*v2)
# unpadding the result
v0 = v0[pad:-pad,pad:-pad]
(展开比较乱,直接生成内核就可以了,我只是想把展开的部分分开。)
原结果:
如果您不介意使用 scipy
,它可以为您完成这一切(包括边界条件的其他变体)。有关更多选项,请参阅 fftconvolve
or convolve2d
。请注意,您 不必 必须 pre-roll 您的内核才能使用这些函数(也就是说,最大值应该在中心,而不是在角落)。示例代码(产生与上面手动填充相同的结果):
# unroll the kernel (again, can be done directly when generating `ker`)
ker = np.roll(ker, 50, 0)
ker = np.roll(ker, 50, 1)
from scipy.signal import fftconvolve
dd = fftconvolve(og, ker, mode='same')
每当你使用基于 fft
的方法时,你总是在问题中引入周期性,本质上,根据定义。
如果你不想要这个,你应该首先在计算级别之前在数学建模级别定义你想要的是什么。
记住一些根据我的经验在很多情况下都非常有用的技巧:
-- 将您的问题嵌入更大的 2d space 以减少周期性影响。换句话说,尽可能地推动周期性边界以模拟 'free-space' 情况。
-- 使用离散余弦变换代替。余弦变换具有使边界条件对称化的作用。它的工作原理就好像在每个边界之外都有您的数据的镜像反射。在某些问题中,这用于模拟反射边界条件。