Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why `numpy.fft.irfft` is so imprecise?

Tags:

python

numpy

fft

I understand that most FFT/IFFT routines have an error floor. I was expecting NumPy's FFT to have an error floor in the same orders as FFTW (say 1e-15), but the following experiment shows errors in the order of 1e-5.

Consider calculating the IDFT of a box. It is well-known that the result is the sinc-like Dirichlet kernel. But that is not what I get from numpy.fft.irfft. In fact even the first sample that should simply equal the width of the box divided by the number of FFT points is off by an amount around 4e-5 as the following example shows:

import numpy as np
import matplotlib.pyplot as plt

from scipy.special import diric

N = 40960
K = 513

X = np.ones(K, dtype=np.complex)
x = np.fft.irfft(X, N)

print("x[0] = %g: expected %g - error = %g" % (x[0], (2*K+1)/N, x[0]-(2*K+1)/N))

# expected IDFT of a box is Dirichlet function (see
# https://en.wikipedia.org/wiki/Discrete_Fourier_transform#Some_discrete_Fourier_transform_pairs)

y = diric(2*np.pi*np.arange(N)/N, 2*K+1) * (2*K+1) / N

plt.figure()
plt.plot(x[:1024] - y[:1024])
plt.title('error')

plt.show(block=True)

It looks like the error is of sinusoidal form: enter image description here

Has anybody experience same issue? Am I misunderstanding something about the NumPy's FFT pack or it is just not accurate?


Update

Here is the equivalent of part of the script in Octave:

N = 40960;
K = 513;

X = zeros(1, N);


X(1:K) = 1;
X(N-K:N) = 1;

x = ifft(X);

fprintf("x[0] = %g, expected = %g - error = %g\n", x(1), (2*K+1)/N, x(1)-(2*K+1)/N);

The error on x[0] is practically zero in Octave. (I did not check other samples because I am not aware of equivalent of diric function in Octave.)

like image 206
MikeL Avatar asked May 31 '26 16:05

MikeL


1 Answers

Thanks to MarkDickinson, I realized that my math was wrong. The correct comparison would be carried out by:

import numpy as np
import matplotlib.pyplot as plt

from scipy.special import diric

N = 40960
K = 513

X = np.ones(K+1, dtype=np.complex)
x = np.fft.irfft(X, N)

print("x[0] = %g: expected %g - error = %g" % (x[0], (2*K+1)/N, x[0]-(2*K+1)/N))

# expected IDFT of a box is Dirichlet function (see
# https://en.wikipedia.org/wiki/Discrete_Fourier_transform#Some_discrete_Fourier_transform_pairs)

y = diric(2*np.pi*np.arange(N)/N, 2*K+1) * (2*K+1) / N

plt.figure()
plt.plot(x[:1024] - y[:1024])
plt.title('error')

plt.show(block=True)

that shows irfft is accurate. Here is the error plot: enter image description here

Numpy is correct, my math was incorrect. I am sorry for posting this misleading question. I don't know what is the standard procedure in these cases. Should I delete my question or leave it here with this answer? I just don't want it to be undermining NumPy or challanging its accuracy (as this was clearly a false alarm).

like image 162
MikeL Avatar answered Jun 02 '26 04:06

MikeL