I'm trying to calculate Fourier Transform of some signals in Python. I want the result calculated by Fast Fourier Transform to coincide with the result calculated from definition. However, the result calculated using numpy.fft deviates from the expected value.
The signal does not reach a value below a certain number. In the graph below it is about 10^-16. For other signals these are comparable values (from 10^-9 to 10^-30). In my application I need higher accuracy.
Just to be sure I also tested scipy.fftpack. The same error appears there, although the incorrectly calculated values are slightly different. The problem does not depend on the signal parameters (length, sampling frequency, etc.).
What is the reason of this limitation? If it's Python/Numpy accuracy how can I improve it?
# Fourier Transform
import numpy as np
import scipy.fftpack as fp
def gaussian_distribution(x,mean=0,variance=1):
return (1 / (np.sqrt(2*np.pi)*variance) ) * np.exp( -((x-mean)**2) / (2 * variance**2) )
def gaussian_fourier_transform(omega, mean=0, variance=1):
# http://mathworld.wolfram.com/FourierTransformGaussian.html
return np.exp(-2 * np.pi**2 * variance**2 * omega**2) * np.exp(-(2j)*np.pi*mean*omega)
## signal generation
signal_range = [-2**4, 2**4]
N = 2**8
x = np.linspace(signal_range[0],signal_range[1],N, dtype='float64')
y = gaussian_distribution(x)
## calculating result
framerate = N / (signal_range[1] - signal_range[0])
frequency_axis = np.linspace(0,framerate,N)
numpy_v = np.abs( np.fft.fft(y) )
numpy_v = numpy_v / numpy_v[0] # normalization
scipy_v = np.abs( fp.fft(y) )
scipy_v = scipy_v / scipy_v[0]
symbolical_v = gaussian_fourier_transform(frequency_axis)
# ploting
import matplotlib.lines as mlines
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_subplot()
ax1.plot(frequency_axis[0: N//2], scipy_v[0: N//2], '.r')
ax1.plot(frequency_axis[0: N//2], numpy_v[0: N//2], '.b')
ax1.plot(frequency_axis[0: N//2], symbolical_v[0: N//2], 'g')
ax1.set_yscale('log')
ax1.grid(True)
blue_line = mlines.Line2D([], [], color='blue', marker='.', markersize=15, label='result calculated by numpy.fft')
red_line = mlines.Line2D([], [], color='red', marker='.', markersize=15, label='result calculated by scipy.fftpack')
green_line = mlines.Line2D([], [], color='green', marker='', markersize=15, label='result calculated by definition')
ax1.legend(handles=[blue_line, red_line, green_line])
fig.show()
It is a numerical issue.
Both NumPy and SciPy use different variants of PocketFFT which is based upon FFTPack, which belongs to the category of exact FFT whose error depends on the machine error ε and the number of samples N
.
I am not sure what is the exact dependency for these libraries but you could try pyFFTW which are Python bindings to FFTW and those might have a slightly better dependence over machine error.