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

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:
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.)

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:
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).

Related

fft normalization vs the norm-option

i have to often normalize the result of circular-convolution, so I 'borrowed'-and-modfied the following routine :
def fft_normalize(x):# Normalize a vector x in complex domain.
c = np.fft.rfft(x)
# Look at real and image as if they were real
ri = np.vstack([c.real, c.imag])
# Normalize magnitude of each complex/real pair
norm = np.linalg.norm(ri, axis=0)
if np.any(norm==0): norm[norm == 0] = np.float64(1e-308) #!fixme
ri= np.divide(ri,norm)
c_proj = ri[0,:] + 1j * ri[1,:]
rv = np.fft.irfft(c_proj, n=x.shape[-1])
return rv
def fft_convolution(a, b):
return np.fft.irfft(np.fft.rfft(a) * np.fft.rfft(b))
so that I do this :
fft_normalize(fft_convolution(a,b))
i see in the numpy docs there is a 'norm' option. Is this equivalent to what i'm doing ?
And if yes, which option should I use ?
def fft_convolution2(a, b):
return np.fft.irfft(np.fft.rfft(a) * np.fft.rfft(b), norm='ortho')
When I test it it behaves better when I do fft_normalize()
Second, i had to add this line, but it does not seem, right. any ideas ?
if np.any(norm==0): norm[norm == 0] = np.float64(1e-308) #!fixme
As a side note, if you know !! numpy docs mentions that they promote float32 to float64 and that scipy.fftpack does not !!
Would fftpack be faster ! because scipy says fftpack is obsolete and there is no info on the new scipy ?
#cris are you sugesting i do it this way :
def fft_normalize(x):# Normalize a vector x in complex domain.
c = np.fft.rfft(x)
ri = np.vstack([c.real, c.imag])
norm = np.abs(c)
if np.any(norm==0): norm[norm == 0] = MIN #!fixme
ri= np.divide(ri,norm)
c_proj = ri[0,:] + 1j * ri[1,:]
rv = np.fft.irfft(c_proj, n=x.shape[-1])
return rv
The norm argument to the FFT functions in NumPy determine whether the transform result is multiplied by 1, 1/N or 1/sqrt(N), with N the number of samples in the array. Normally, the inverse transform is normalized by dividing by N, and the forward transform is not. Specifying “ortho” here causes both transforms to be normalized by 1/sqrt(2). Specifying “forward” causes only the forward transform to be normalized by 1/N.
These normalizations are very different from the one you apply, where you normalize each element in the frequency domain.

Step by step time integrators in Python

I am solving a first order initial value problem of the form:
dy/dt = f(t,y(t)), y(0)=y0
I would like to obtain y(n+1) from a given numerical scheme, like for example :
using explicit Euler's scheme, we have
y(i) = y(i-1) + f(t-1,y(t-1)) * dt
Example code:
# Test code to evaluate different time integrators for the following equation:
# y' = (1/2) y + 2sin(3t) ; y(0) = -24/37
def dy_dt(y,t):
func = (1/2)*y + 2*np.sin(3*t)
return func
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
tmin = 0
tmax = 50
delt= 1e-2
t = np.arange(tmin,tmax,delt)
total_steps = len(t)
y_explicit=np.zeros(total_steps)
#y_ODEint=np.zeros(total_steps)
y0 = -24/37
y_explicit[0]=y0
#y_ODEint[0]=y0
# exact solution
y_exact = -(24/37)*np.cos(3*t)- (4/37)*np.sin(3*t) + (y0+24/37)*np.exp(0.5*t)
# Solution using ODEint Python
y_ODEint = odeint(dy_dt,y0,t)
for i in range(1,total_steps):
# Explicit scheme
y_explicit[i] = y_explicit[i-1] + (dy_dt(y_explicit[i-1],t[i-1]))*delt
# Update using ODEint
# y_ODEint[i] = odeint(dy_dt,y_ODEint[i-1],[0,delt])[-1]
plt.figure()
plt.plot(t,y_exact)
plt.plot(t,y_explicit)
# plt.plot(t,y_ODEint)
The current issue I am having is that the functions like ODEint in python provide the entire y(t) as opposed to y(i). like in the line "y_ODEint = odeint(dy_dt,y0,t)"
See in the code, how I have coded the explicit scheme, which gives y(i) for every time step. I want to do the same with ODEint, i tried something but didn't work (all commented lines)
I want to obtain y(i) rather than all ys using ODEint. Is that possible ?
Your system is time variant so you cannot translate the time step from (t[i-1], t[i]) to (0, delt).
The step by step integration will is unstable for your differential equation though
Here is what I get
def dy_dt(y,t):
func = (1/2)*y + 2*np.sin(3*t)
return func
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
tmin = 0
tmax = 40
delt= 1e-2
t = np.arange(tmin,tmax,delt)
total_steps = len(t)
y_explicit=np.zeros(total_steps)
#y_ODEint=np.zeros(total_steps)
y0 = -24/37
y_explicit[0]=y0
# exact solution
y_exact = -(24/37)*np.cos(3*t)- (4/37)*np.sin(3*t) + (y0+24/37)*np.exp(0.5*t)
# Solution using ODEint Python
y_ODEint = odeint(dy_dt,y0,t)
# To be filled step by step
y_ODEint_2 = np.zeros_like(y_ODEint)
y_ODEint_2[0] = y0
for i in range(1,len(y_ODEint_2)):
# update your code to run with the correct time interval
y_ODEint_2[i] = odeint(dy_dt,y_ODEint_2[i-1],[tmin+(i-1)*delt,tmin+i*delt])[-1]
plt.figure()
plt.plot(t,y_ODEint, label='single run')
plt.plot(t,y_ODEint_2, label='step-by-step')
plt.plot(t, y_exact, label='exact')
plt.legend()
plt.ylim([-20, 20])
plt.grid()
Important to notice that both methods are unstable, but the step-by-step explodes slightly before than the single odeint call.
With, for example dy_dt(y,t): -(1/2)*y + 2*np.sin(3*t) the integration becomes more stable, for instance, there is no noticeable error after integrating from zero to 200.

Scipy.Curve fit struggling with exponential function

I'm trying to fit a curve of the equation:
y = ( (np.exp(-k2*(t+A))) - ((k1/v)*Co) )/ -k2
where A = (-np.log((k1/v)*Co))/k2
given to me by a supervisor to a dataset that looks like a rough exponential that flattens to a straight horizontal line at its top. When I fit the equation i am receiving only a straight line from the curve fit and a corresponding Warning:
<ipython-input-24-7e57039f2862>:36: RuntimeWarning: overflow encountered in exp
return ( (np.exp(-k2*(t+A))) - ((k1/v)*Co) )/ -k2
the code I am using looks like:
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy.optimize import differential_evolution
xData_forfit = [1.07683e+13, 1.16162e+13, 1.24611e+13, 1.31921e+13, 1.40400e+13, 2.65830e+13,
2.79396e+13, 2.86676e+13, 2.95155e+13, 3.03605e+13, 3.12055e+13, 3.20534e+13,
3.27814e+13, 3.36293e+13, 3.44772e+13, 3.53251e+13, 3.61730e+13, 3.77459e+13,
3.85909e+13, 3.94388e+13, 4.02838e+13, 4.11317e+13, 4.19767e+13, 4.27076e+13,
5.52477e+13, 5.64143e+13, 5.72622e+13, 5.81071e+13, 5.89550e+13, 5.98000e+13,
6.05280e+13, 6.13759e+13, 6.22209e+13, 6.30658e+13, 6.39137e+13, 6.46418e+13,
6.55101e+13, 6.63551e+13, 6.72030e+13, 6.80480e+13, 6.88929e+13, 6.97408e+13,
7.04688e+13, 7.13167e+13, 7.21617e+13, 8.50497e+13, 8.58947e+13, 8.67426e+13,
8.75876e+13, 8.83185e+13, 9.00114e+13, 9.08563e+13, 9.17013e+13]
yData_forfit = [1375.409524, 1378.095238, 1412.552381, 1382.904762, 1495.2, 1352.4,
1907.971429, 1953.52381, 1857.352381, 1873.990476, 1925.114286, 1957.085714,
2030.52381, 1989.8, 2042.733333, 2060.095238, 2134.361905, 2200.742857,
2342.72381, 2456.047619, 2604.542857, 2707.971429 ,2759.87619, 2880.52381,
3009.590476, 3118.771429, 3051.52381, 3019.771429, 3003.561905, 3083.0,
3082.885714, 2799.866667, 3012.419048, 3013.266667, 3106.714286, 3090.47619,
3216.638095, 3108.447619, 3199.304762, 3154.257143, 3112.419048, 3284.066667,
3185.942857, 3157.380952, 3158.47619, 3464.257143, 3434.67619, 3291.457143,
2851.371429, 3251.904762, 3056.152381, 3455.07619, 3386.942857]
def fnct_to_opt(t, k2, k1):
#EXPERIMENTAL CONSTANTS
v = 105
Co = 1500
A = (-np.log((k1/v)*Co))/k2
return ( (np.exp(-k2*(t+A))) - ((k1/v)*Co) )/ -k2
initial_k2k1 = [100, 1*10**-3]
constants = curve_fit(fnct_to_opt, xData_forfit, yData_forfit, p0=initial_k2k1)
k2_fit = constants[0][0]
k1_fit = constants[0][1]
fit = []
for i in xData_forfit:
fit.append(fnct_to_opt(i,k2_fit,k1_fit))
plt.plot(xData_forfit, yData_forfit, 'or', ms='2')
plt.plot(xData_forfit, fit)
this is giving me this plot as a result:
As far as i can tell, the code isn't producing a useful output due to a too large value for the np.exp term, but i don't know how to go about diagnosing where this overflow is coming from or how to fix the issue. any help would be appreciated, thanks.
The overflow is happening exactly where the error message tells you: in the return expression of fnct_to_opt. I asked you to print the offending values just before the error point; this would show you the problem.
At the point of error, the values in A are in the range e+13 to e+14. t is insignificant; k2 is a bit under -10000.0
Thus, the values in your argument to np.exp are well out of the domain that the function can handle. Just add a line to you function and watch the results:
def fnct_to_opt(t, k2, k1):
#EXPERIMENTAL CONSTANTS
v = 105
Co = 1500
A = (-np.log((k1/v)*Co))/k2
print("TRACE", "\nk2", k2, "\nt", t, "\nA", A, "\nother", k1, v, Co)
return ( (np.exp(-k2*(t+A))) - ((k1/v)*Co) )/ -k2
I think the problem maybe in the optimization function, in the sense that maybe a mistake.
For instance:
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy.optimize import differential_evolution
xData_forfit = [1.07683e+13, 1.16162e+13, 1.24611e+13, 1.31921e+13, 1.40400e+13, 2.65830e+13,
2.79396e+13, 2.86676e+13, 2.95155e+13, 3.03605e+13, 3.12055e+13, 3.20534e+13,
3.27814e+13, 3.36293e+13, 3.44772e+13, 3.53251e+13, 3.61730e+13, 3.77459e+13,
3.85909e+13, 3.94388e+13, 4.02838e+13, 4.11317e+13, 4.19767e+13, 4.27076e+13,
5.52477e+13, 5.64143e+13, 5.72622e+13, 5.81071e+13, 5.89550e+13, 5.98000e+13,
6.05280e+13, 6.13759e+13, 6.22209e+13, 6.30658e+13, 6.39137e+13, 6.46418e+13,
6.55101e+13, 6.63551e+13, 6.72030e+13, 6.80480e+13, 6.88929e+13, 6.97408e+13,
7.04688e+13, 7.13167e+13, 7.21617e+13, 8.50497e+13, 8.58947e+13, 8.67426e+13,
8.75876e+13, 8.83185e+13, 9.00114e+13, 9.08563e+13, 9.17013e+13]
yData_forfit = [1375.409524, 1378.095238, 1412.552381, 1382.904762, 1495.2, 1352.4,
1907.971429, 1953.52381, 1857.352381, 1873.990476, 1925.114286, 1957.085714,
2030.52381, 1989.8, 2042.733333, 2060.095238, 2134.361905, 2200.742857,
2342.72381, 2456.047619, 2604.542857, 2707.971429 ,2759.87619, 2880.52381,
3009.590476, 3118.771429, 3051.52381, 3019.771429, 3003.561905, 3083.0,
3082.885714, 2799.866667, 3012.419048, 3013.266667, 3106.714286, 3090.47619,
3216.638095, 3108.447619, 3199.304762, 3154.257143, 3112.419048, 3284.066667,
3185.942857, 3157.380952, 3158.47619, 3464.257143, 3434.67619, 3291.457143,
2851.371429, 3251.904762, 3056.152381, 3455.07619, 3386.942857]
def fnct_to_opt(t, k2, k1):
#EXPERIMENTAL CONSTANTS
v = 105
Co = 1500
#A = (-np.log((k1/v)*Co))/k2
#return ( (np.exp(-k2*(t+A))) - ((k1/v)*Co) )/ -k2
#A = (np.log((k1/v)*Co))/k2
return k2/np.log(t) + k1
initial_k2k1 = [10, 1]
constants = curve_fit(fnct_to_opt, xData_forfit, yData_forfit, p0=initial_k2k1)
k2_fit = constants[0][0]
k1_fit = constants[0][1]
#v_fit = constants[0][2]
#Co_fit = constants[0][3]
fit = []
for i in xData_forfit:
fit.append(fnct_to_opt(i,k2_fit,k1_fit))
plt.plot(xData_forfit, yData_forfit, 'or', ms='2')
plt.plot(xData_forfit, fit)
So I place a function simpler but with some clearer intuition behind. For instance in the original I do not think that with those signs and exponential the shape is going to be achieve at all. However looks to me that the exponential is misplaced so I change it for log. Add a constant and a scale parameter. I would suggest to check carefully the original function. Probably there is and issue with the derivation. I do not think is a computational problem.
This is something closer to what could be expected.

MINRES implementation in Python

Is there any python implementation of MINRES pseudoinversion algorithm that can deal with Hermitian matrices?
I have found a few sources, but all of them are only capable of working with real matrices and do not seem to be easily generalizable onto the complex case:
https://searchcode.com/codesearch/view/89958680/
https://github.com/pascanur/theano_optimize
(there are a couple of other links, but my reputation does not allow me to post them)
A Hermitian system of size $n$
$$\mathbf y = \mathbf H^{-1}\mathbf v$$
can be embedded in a real, symmetric system of size $2n$:
\begin{equation}
\begin{bmatrix}
\Re(\mathbf y)\\Im(\mathbf y)
\end{bmatrix}=
\begin{bmatrix}
\Re(\mathbf H)&-\Im(\mathbf H)\\Im(\mathbf H)&\Re(\mathbf H)
\end{bmatrix}^{-1}
\begin{bmatrix}
\Re(\mathbf v)\\Im(\mathbf v)
\end{bmatrix}.
\end{equation}
Minimum-residual methods are often used for large problems, where constructing $H$ is impractical. In which case we may have an operation which computes a matrix-vector product, $f: \mathbb C^n \to \mathbb C^n; ,, f(\mathbf v) = \mathbf H\mathbf v.$ This function can be wrapped to operate on $\mathbf x \in \mathbb R^{2n}$ by converting $\mathbf x$ back to a complex vector, applying $f$, and then embedding the result back in $\mathbb R^{2n}$.
Here is an example in python / numpy / scipy:
from scipy.sparse.linalg import minres, LinearOperator
from pylab import *
# Problem size
N = 100
# error helper
er = lambda t,a,b:print('%s error:'%t,mean(abs(a-b)))
# random Hermitian matrix
Q = randn(N,N) + 1j*randn(N,N)
H = Q#conj(Q.T)
# random complex vector
v = randn(N) + 1j*randn(N)
# ground-truth solution
x0 = inv(H)#v
# Pack/unpack complex vector as stacked real vector
c2r = lambda v:block([real(v),imag(v)])
r2c = lambda v:kron([1,1j],eye(N))#v
# Verify that we can embed C^n in R^(2N)
Hr = real(H)
Hi = imag(H)
Hs = block([[Hr,-Hi],[Hi,Hr]])
vs = c2r(v)
xs = inv(Hs)#vs
x1 = r2c(xs)
er('Embed',x0,x1)
# Verify that minres works as expected in R-embed
x2 = r2c(minres(Hs,vs,tol=1e-12)[0])
er('Minres 1',x0,x2)
# Demonstrate using operators
Av = lambda u:c2r( H # r2c(u) )
A = LinearOperator((N*2,)*2,Av,Av)
# Minres, converting input/output to/from complex/real
x3 = r2c(minres(Hs,vs,tol=1e-12)[0])
er('Minres 2',x0,x3)
>>> Embed error: 5.317184726020268e-12
>>> Minres 1 error: 6.641342200989796e-11
>>> Minres 2 error: 6.641342200989796e-11

Numerical Fourier Transform of rectangular function

The aim of this post is to properly understand Numerical Fourier Transform on Python or Matlab with an example in which the Analytical Fourier Transform is well known. For this purpose I choose the rectangular function, the analytical expression of it and its Fourier Transform are reported here
https://en.wikipedia.org/wiki/Rectangular_function
Here the code in Matlab
x = -3 : 0.01 : 3;
y = zeros(length(x));
y(200:400) = 1;
ffty = fft(y);
ffty = fftshift(ffty);
plot(real(ffty))
And here the code in Python
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-3, 3, 0.01)
y = np.zeros(len(x))
y[200:400] = 1
ffty = np.fft.fft(y)
ffty = np.fft.fftshift(ffty)
plt.plot(np.real(ffty))
In both the two programming langueages I have the some result with the some problems:
First of all the fourier transfrom is not real as expected, moreover even choosing the real part, the solution does not looks like the analytical solution: in fact the first plot reported here is as it should be at least in shape and the second plot is what I get from my calculations.
Is there anyone who could suggest me how to analytically calculate the fourier transform of the rectangular function?
There are two problems in your Matlab code:
First, y = zeros(length(x)); should be y = zeros(1,length(x));. Currently you create a square matrix, not a vector.
Second, the DFT (or FFT) will be real and symmetric if y is. Your y should be symmetric, and that means with respect to 0. So, instead of y(200:400) = 1; use y(1:100) = 1; y(end-98:end) = 1;. Recall that the DFT is like the Fourier series of a signal from which your input is just one period, and the first sample corresponds to time instant 0.
So:
x = -3 : 0.01 : 3;
y = zeros(1,length(x));
y(1:100) = 1; y(end-98:end) = 1;
ffty = fft(y);
ffty = fftshift(ffty);
plot(ffty)
gives
>> isreal(ffty)
ans =
1
The code in Python is
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(-3, 3, 0.01)
y = np.zeros(len(x))
y[200:400] = 1
yShift = np.fft.fftshift(y)
fftyShift = np.fft.fft(yShift)
ffty = np.fft.fftshift(fftyShift)
plt.plot(ffty)
plt.show()

Categories

Resources