我需要使用卷积定理计算卷积积。但是,我不明白为什么我需要在逆傅里叶变换上应用fftshift
来获得正确的结果。否则,交换结果(好吧,我知道这是fftshift的用途,但我不明白为什么我从反向fft获得交换结果)。这是一个最小的例子,它有两个快速减少的函数,所以我不会添加任何填充。结果将针对scipy.signal.convolve
:
import numpy as np
import scipy.signal as sig
Nx = 400
xp = np.arange(Nx) - Nx/2.
Lg = 20
Lb = 25
ff = np.exp(-(xp/Lg)**2) * xp/Lg # function (two bumps of opposite signs)
gg = np.zeros(Nx) # convolution kernel (just a box)
gg[abs(xp)<Lb] = 1
conv_pure = sig.convolve(ff, gg, mode="same") # that is the correct one
tff = np.fft.rfft(ff) # DFT of the function
tfg = np.fft.rfft(gg) # DFT of the kernel
conv_dfts = np.fft.irfft(tff*tfg).real # should be the convolution product
conv_dftshift = np.fft.fftshift(conv_dfts)
那么,为什么要交换conv_dfts
?
答案 0 :(得分:2)
要正确定义scipy.signal.convolve
mode='full'
或mode='same'
的计算,第一个参数中的数据(有效地)用零扩展。另一方面,您的FFT计算进行循环卷积,这对应于使用数据的周期性扩展。要了解这种差异的后果,请考虑如何计算结果的第一个点。
(记住卷积的常用“滑动窗口”视图很有帮助,例如http://mathworld.wolfram.com/Convolution.html或https://en.wikipedia.org/wiki/Convolution#Visual_explanation所示。在您的情况下,滑动窗口为gg
。)
对于scipy.signal.convolve
的{{1}},您可以通过将mode='same'
的右半部分对齐gg
的左端,并将总和进行求和来可视化第一个点的计算这两个信号的元素乘积。 ff
在其左端非常小,因此该计算非常接近0.卷积的后续点保持为零,直到滑动窗口开始遇到更大的ff
值。所以结果的“有趣”部分是在卷积的中间。
对于FFT计算的第一点,假设ff
的右端与gg
的左端对齐。再次取元素乘积的总和。这里有两个很大的不同。首先,ff
的长度不会像gg
中的mode='same'
那样移动一半。其次,scipy.signal.convolve
乘以的值并非都为零 - 它们是gg
的周期性扩展,因此在这个“滑动窗口”可视化中,我们将矩形窗口直接对齐在中心上方双脉冲的(在周期性延伸中)。由于ff
的对称性和gg
的反对称性,第一个值为0.当ff
向右滑动时,对称性被打破,正脉冲在计算中占主导地位,而非平凡值计算。一旦窗口通过双脉冲,卷积的值变得非常小。当矩形脉冲遇到双脉冲的另一侧时,它们在卷积结束时再次变得非常大。
要使FFT计算与gg
计算相匹配,您可以在scipy.signal.convolve
中调整矩形脉冲的相位。例如(假设gg
是偶数)。例如,如果添加此行
Nx
并在计算gg2 = np.roll(gg, -(Nx//2 - 1))
时使用gg2
代替gg
:
tfg
然后tfg = np.fft.rfft(gg2) # DFT of the kernel
和conv_dfts
同意。还有其他方法可以调整内容以使结果按预期对齐。这个答案的要点是解释为什么你计算的结果不同。