I need help understanding why FFT-based computation of convolution on a finite domain, between two non-periodic functions often gets it wrong. Specific questions:
(1) why does FFT-based computation have error when interval [a, b] is short in the specific example below but no error when interval [a, b] is wider ;
(2) how can I anticipate this error when applying FFT-based approach to convolve functions?;
(3) Can this error be mitigated/reduced, e.g. different zero-padding options? Increasing discretisation size does not help at all.
Let's convolve two Gaussians on $x\in [a, b]$ for two choices of $[a, b]$: (1) $[a,b]=[0,5]$ and (2) $[a,b]=[-1, 5]$. We will compute convolution via DFT and compare against an exact result and a real-space evaluation.
Specific functions:
$$ f_1(x)=\exp{(-x^2)} $$
$$ f_2(x)=\exp{(-x^2)} $$
I want to compute the convolution on $[a, b]$ $$ f(x)=\int\limits_a^b f_1(x) f_2(x-y) dx $$
Implementation:
import numpy as np
import matplotlib.pyplot as plt
import scipy.fft as fft
from scipy.special import erf
N = 64
a = 0
b = 5
x = np.linspace(a, b, N)
dx = np.diff(x)[0]
f1 = np.exp(-x**2)
f2 = np.exp(-x**2)
# exact convolution
fe = .5*np.exp(-x**2/2)*\
np.sqrt(np.pi/2)*\
(-erf((2*a-x)/np.sqrt(2))+erf((2*b-x)/np.sqrt(2)))
# computation on real domain
c = np.zeros(x.size)
for i in range(x.size):
c[i]=np.trapz(np.exp(-x**2)*np.exp(-(x-x[i])**2), x)
f3 = fft.ifft(fft.fft(f1)*fft.fft(f2)).real*dx
f31 = fft.ifft(fft.fft(np.concatenate((f1, np.zeros(N-1))))*\
fft.fft(np.concatenate((f2, np.zeros(N-1))))
).real*dx
x1 = np.arange(x[0], x[0] + f31.size*dx, dx)
plt.figure()
plt.subplot(2,1,1)
plt.plot(x, f1, label = 'f1(x)');
plt.plot(x, f2, '.', label = 'f2(x)');
plt.plot(x, fe, label = 'Exact f1*f2 (x)');
plt.plot(x, c, '.', label = 'f1*f2 (x) computed directly');
plt.legend()
plt.title(f"Exact convolution on [a,b]=[{a}, {b}]");
plt.subplot(2,1,2)
plt.plot(x, fe,'.', label = 'f1*f2: exact result');
plt.plot(x1+a, f31, label = 'f1*f2: computed with FFT')
plt.axvline(a)
plt.axvline(b)
plt.legend()
plt.title('FFT computation of convolution');
plt.tight_layout();
Results for $[a, b]=[0, 5]$: NOTICE THAT FFT APPROACH HAS HUGE ERROR
Results for $[a, b]=[-2, 5]$: NOTICE THAT FFT APPROACH HAS ALMOST NO ERROR