Reputation: 41
I have a question regarding python's fftconvolve
. In my current research I've been required to calculate some convolution between two functions. To do so I'm calculating it using fourier transform (which I used numpy.fft
and normalize it) . The thing is that if I want to compare it using fftconvolve
package, it fails to give the correct results. Here is my code:
#!/usr/bin/python
import numpy as np
from scipy.signal import fftconvolve , convolve
def FFT(array , sign):
if sign==1:
return np.fft.fftshift(np.fft.fft(np.fft.fftshift(array))) * dw / (2.0 * np.pi)
elif sign==-1:
return np.fft.fftshift(np.fft.ifft(np.fft.fftshift(array))) * dt * len(array)
def convolve_arrays(array1,array2,sign):
sign = int(sign)
temp1 = FFT(array1 , sign,)
temp2 = FFT(array2 , sign,)
temp3 = np.multiply(temp1 , temp2)
return FFT(temp3 , -1 * sign) / (2. * np.pi)
""" EXAMPLE """
dt = .1
N = 2**17
t_max = N * dt / 2
time = dt * np.arange(-N / 2 , N / 2 , 1)
dw = 2. * np.pi / (N * dt)
w_max = N * dw / 2.
w = dw * np.arange(-N / 2 , N / 2 , 1)
eta_fourier = 1e-10
Gamma = 1.
epsilon = .5
omega = .5
G = zeros(N , complex)
G[:] = 1. / (w[:] - epsilon + 1j * eta_fourier)
D = zeros(N , complex)
D[:] = 1. / (w[:] - omega + 1j * eta_fourier) - 1. / (w[:] + omega + 1j * eta_fourier)
H = convolve_arrays(D , G , 1)
J = fftconvolve(D , G , mode = 'same') * np.pi / (2. * N)
If you plot the real/imaginary part of H
, J
you'll see a shift in the w
axes and also I had to multiply the J
's results in order to get somehow close (but still not) to the correct results.
Any suggestions?
Thanks!
Upvotes: 4
Views: 4175
Reputation: 2892
Boundary conditions are important when you compute convolutions.
When you convolve two signals, the edges of the result depend on what values you assume outside the edges of the inputs. fftconvolve
computes convolution assuming zero-padded boundaries.
Take a look at the source code of fftconvolve. Notice the shenanigans they go through to achieve zero-padded boundary conditions, in particular, these lines:
size = s1 + s2 - 1
...
fsize = 2 ** np.ceil(np.log2(size)).astype(int) #For speed; often suboptimal!
fslice = tuple([slice(0, int(sz)) for sz in size])
...
ret = ifftn(fftn(in1, fsize) * fftn(in2, fsize))[fslice].copy()
...
return _centered(ret, s1) #strips off padding
This is good stuff! It's probably worth reading fftconvolve
's code carefully, and a good education if you want to understand Fourier-based convolution.
Brief sketch
The forward FFT zero-pads each signal to prevent periodic boundary conditions:
a = np.array([3, 4, 5])
b = np.fft.ifftn(np.fft.fftn(a, (5,))).real
print b #[ 3. 4. 5. 0. 0.]
the inverse FFT of the product of the forward FFTs gives a padded result:
a = np.array([3, 4, 5])
b = np.array([0., 0.9, 0.1])
b = np.fft.ifftn(np.fft.fftn(a, (5,))*
np.fft.fftn(b, (5,))
).real
print b #[ 0. 2.7 3.9 4.9 0.5]
and the _centered
function strips off the extra padding pixels at the end (assuming you use the mode='same'
option).
Upvotes: 7