Is there a way to improve the precision of the output of numpy.linalg.eig() and scipy.linalg.eig()?
I'm diagonalizing a non-symmetric matrix, yet I expect on physical grounds to get a real spectrum of pairs of positive and negative eigenvalues. Indeed, the eigenvalues do come in pairs, and I have verified by an independent analytical calculation that two of the pairs are correct. The problematic pair is the one with eigenvalues close to zero, which appear to have small imaginary parts. I am expecting this pair to be degenerate at zero, so the imaginary parts can at most be at machine precision, but they are much larger. I think this leads to a small error in the eigenvectors, which however can propagate in subsequent manipulations.
The example below shows that there are fictitious imaginary parts leftovers, by checking the validity of the transformation.
import numpy as np
import scipy.linalg as sla
H = np.array(
[[ 11.52, -1., -1., 9.52, 0., 0. ],
[ -1., 11.52, -1., 0., 9.52, 0., ],
[ -1., -1., 11.52, 0., 0., 9.52,],
[ -9.52, 0., 0., -11.52, 1., 1., ],
[ 0., -9.52, 0., 1., -11.52, 1., ],
[ 0., 0., -9.52, 1., 1., -11.52 ]],
dtype=np.float64
)
#print(H)
E,V = np.linalg.eig(H)
#E,V = sla.eig(H)
H2=reduce(np.dot,[V,np.diag(E),np.linalg.inv(V)])
#print(H2)
print(np.linalg.norm(H-H2))
which gives
3.93435308362e-09
a number of the order of the fictitious imaginary part of the zero eigenvalues.