Reputation: 595
I am new to convolution and would therefore like to prove convolution theorem to myself by convolving two 1D signals together using FFT. However, my code is not consistent with the result obtained from np.convolve
.
I have used the naive implementation from this solution:
import numpy as np
def Convolution(array,kernel):
return np.real(np.fft.ifft( np.fft.fft(array)*np.fft.fft(kernel) ))
a_flat = [ 1., 2., 3., 0., 0., 4., 5., 6., 0., 0. , 7., 8., 9.]
k_flat = [ 1,2 ,1 ,0,0 ,0 ,0,0 ,0 ,0,-1,-2,-1]
my_convolution = Convolution(a_flat, k_flat)
np_convolution = np.convolve(a_flat, k_flat)
print(my_convolution)
print("")
print(np_convolution)
The output reads:
[ 19. 10. 4. -5. -17. -13. 7. 13. -5. -26. -20. 9. 24.]
[ 1. 4. 8. 8. 3. 4. 13. 20. 17. 6. 6. 18. 24. 18. 6. -4. -13. -20. -17. -6. -7. -22. -32. -26. -9.]
I am clearly missing something. Can someone point me to my oversight?
Upvotes: 1
Views: 1036
Reputation: 70743
The result of a convolution is N+M-1, e.g. longer than either of the inputs. So your FFT needs to be that long or longer.
An FFT/IFFT will wrap the fast convolution result around, and mix it up into a circular convolution. But if you pad the data with lots of zeros on the end(s), the mix will be easy to unmix.
Upvotes: 1
Reputation: 7941
What @hotpaw2 said. Always better to graph it:
import numpy as np
import matplotlib.pyplot as p
%matplotlib inline
def Convolution(array,kernel):
return np.real(np.fft.ifft( np.fft.fft(array)*np.fft.fft(kernel) ))
a_flat = [ 1., 2., 3., 0., 0., 4., 5., 6., 0., 0. , 7., 8., 9.]
k_flat = [ 1,2,1,0,0,0,0,0,0,0,-1,-2,-1]
a_flat= np.pad(a_flat, (25, 25), 'constant', constant_values=(0, 0)).tolist()
k_flat= np.pad(k_flat, (25, 25), 'constant', constant_values=(0, 0)).tolist()
my_convolution = Convolution(a_flat, k_flat)
np_convolution = np.convolve(a_flat, k_flat)
fig,ax = p.subplots(3,figsize=(12,5))
ax[0].plot(a_flat)
ax[1].plot(k_flat)
ax[2].plot(np.roll(my_convolution, 30),'.-',lw=0.5,label='myconv'); # arbitrary shift here
ax[2].plot(np.roll(np_convolution, 0),'.-',lw=3,alpha=0.3,label='npconv');
p.legend()
Nice example, btw.
Upvotes: 2
Reputation: 1651
Be aware of where your anchor is in your kernel, usually it's the median, in your case the 0 in the middle, you should still ensure it's correct. Referring to @Paul R 's answer, make up your mind what kind of padding you want to use (zero padding, replicate the border regions, etc.) as this will influence your output.
Upvotes: 1