病态矩阵的对角线化和无法计算特征向量。用numpy / scipy获得不同的结果

时间:2018-09-06 20:45:16

标签: python numpy scipy linear-algebra

我要处理的元素很少的稀疏矩阵。考虑一个向量:

vec=[-1.e-76 -1.e-72 -1.e-68 -1.e-64 -1.e-60 -1.e-56 -1.e-52 -1.e-48 -1.e-44
-1.e-40 -1.e-36 -1.e-32 -1.e-28 -1.e-24 -1.e-20 -1.e-16 -1.e-12 -1.e-08
-1.e-04 -1.e-02 -1.e-04 -1.e-08 -1.e-12 -1.e-16 -1.e-20 -1.e-24 -1.e-28
-1.e-32 -1.e-36 -1.e-40 -1.e-44 -1.e-48 -1.e-52 -1.e-56 -1.e-60 -1.e-64
-1.e-68 -1.e-72 -1.e-76]

对于那些感兴趣的人,这些数字代表一维系统的跳跃幅度。它们不为零。哈密​​尔顿由稀疏矩阵给出:

H=sps.diags([vec,vec],[-1,1],dtype='f8')

我对特征值感兴趣,但对特征向量更感兴趣 。据我所知,有两种处理对角化的方法:  scipy.linalgnumpy.linalg,前者更好。

 denseHam=H.toarray()

所有这些函数都给出了正确的特征值谱:

import numpy as np
import scipy.linalg as la
s1= la.eigvalsh(denseHam)
s2= np.linalg.eigvalsh(denseHam)
s3= np.linalg.eigvals(denseHam) #I did not expect that!

正确的光谱是:

spectrum=[-3.16230928e-03 -3.16227766e-08 -3.16227766e-13 -3.16227766e-18
-3.16227766e-23 -3.16227766e-28 -3.16227766e-33 -3.16227766e-38
-3.16227766e-43 -3.16227766e-48 -3.16227766e-53 -3.16227766e-58
-3.16224604e-63  3.16224604e-63  3.16227766e-58  3.16227766e-53
 3.16227766e-48  3.16227766e-43  3.16227766e-38  3.16227766e-33
 3.16227766e-28  3.16227766e-23  3.16227766e-18  3.16227766e-13
 3.16227766e-08  3.16230928e-03]

尽管如此,其他函数(也涉及特征向量的计算)都失败了,因为我需要特征向量所以我不能继续。

我不得不说C ++还能正确计算特征向量。

所以我有两个问题:

  1. 为什么函数np.linalg.eigh(denseHam)的频谱不同于np.linalg.eigvalsh(denseHam)
  2. 有什么方法可以用python正确计算特征向量吗?

非常感谢您!

---更新------ 我在这里粘贴了一个最小的完整示例。请注意numpy.linalg.eigh的简并性:

import numpy as np
import scipy.sparse as sps

vec=np.array([-1.e-76, -1.e-72, -1.e-68, -1.e-64, -1.e-60, -1.e-56, -1.e-52,
       -1.e-48, -1.e-44, -1.e-40, -1.e-36, -1.e-32, -1.e-28, -1.e-24,
       -1.e-20, -1.e-16, -1.e-12, -1.e-08, -1.e-04, -1.e-02, -1.e-04,
       -1.e-08, -1.e-12, -1.e-16, -1.e-20, -1.e-24, -1.e-28, -1.e-32,
       -1.e-36, -1.e-40, -1.e-44, -1.e-48, -1.e-52, -1.e-56, -1.e-60,
       -1.e-64, -1.e-68, -1.e-72, -1.e-76])
H=sps.diags([vec,vec],[-1,1],dtype='f8')
denseHam=H.toarray()

s1=np.linalg.eigvalsh(denseHam)
(s2,basis)=np.linalg.eigh(denseHam)

print("Note the difference between the eigenvalues computed with eigvalsh (1stcolumn) and eigh (2nd column)")
for elem in range(len(s1)):
    print (s1[elem],"     ",s2[elem])

2 个答案:

答案 0 :(得分:1)

它们给出不同的结果有些令人惊讶,因为我希望numpy和scipy都能调用LAPACK,但这通常也是我的经验。

请注意,scipy绑定提供了更多参数来使用; numpy可能使用不同的默认值。似乎需要进行一些实验。您的问题不只是具有很小的元素(如果导致下溢,可以通过简单的缩放来解决),但是您的问题也非常“僵硬”,特征值跨越70个数量级。 C ++可能会为您提供特征向量,但是如果它们被数值噪声污染到毫无用处,我不会感到惊讶。

听起来像是这样的问题,在某种变换/预处理的空间中解决它会更加可靠。 docstring并没有说明LAPACK函数是否可以处理128位浮点数。上次我尝试使用它们时没有,但是如果现在这样做,请确保至少使用它而不是64位。

答案 1 :(得分:1)

简短答案:尝试使用LAPACK的scipy.linalg.lapack.dsyev()

# LAPACK's dsyev instead of LAPACK's dsyevd
(s3,basis3,error)=scipy.linalg.lapack.dsyev(denseHam)

函数np.linalg.eigvalsh()np.linalg.eigh()都按照文档中的说明调用了LAPCK的DSYEVD。但是,它们提供了不同的特征值: eigh()失败。原因很可能刻在DSYEVD的源代码中。确实,如果不需要特征向量,则例程会缩放矩阵,将矩阵缩小为三角形式(DSYTRD),最后呼叫DSTERF。最后一个例程使用QL或QR算法的Pal-Walker-Kahan变体来计算对称三对角矩阵的所有特征值。相反,如果需要特征向量,则DSYEVD在缩放并缩小为三角形后会切换为DSTEDCDSTEDC使用分治法计算对称三对角矩阵的所有特征值和特征向量。

  1. 输入矩阵的小范数无关紧要,因为在这种情况下可能会应用缩放。由于实对称矩阵的特征值幅值相差很大(从3.16224604e- 63至3.16230928e-03),它是病态的。此属性极大地影响了大多数线性代数过程(包括特征值计算)的准确性。提供的矩阵已经是三角形式。

  2. np.linalg.eigh()失败。这可能与分而治之的策略有关。

  3. np.linalg.eigvalsh()似乎有效。因此,似乎DSTERF起作用了。但是,此例程仅提供特征值。

LAPACK提供了different algorithms to compute the eigenvalues and eigenvectors,您的C ++代码可能使用了另一个代码,例如dsyev()在将矩阵缩放并缩小为三角形式后,此例程首先调用{ {1}}生成正交矩阵,然后调用DORGTR获得特征向量。希望可以通过scipy的Low-level LAPACK functions (scipy.linalg.lapack)

调用此函数

我在您的代码中添加了几行来调用此函数。 DSTEQR为此病态矩阵计算与scipy.linalg.lapack.dsyev()相似的特征值。

np.linalg.eigvalsh()

由于矩阵已经是三角的,因此您也可以避免简化为三角形式。为了避免数值问题,可能需要缩放比例。然后可以通过LAPACK functions for Cython直接调用import numpy as np import scipy.sparse as sps import scipy.linalg.lapack vec=np.array([-1.e-76, -1.e-72, -1.e-68, -1.e-64, -1.e-60, -1.e-56, -1.e-52, -1.e-48, -1.e-44, -1.e-40, -1.e-36, -1.e-32, -1.e-28, -1.e-24, -1.e-20, -1.e-16, -1.e-12, -1.e-08, -1.e-04, -1.e-02, -1.e-04, -1.e-08, -1.e-12, -1.e-16, -1.e-20, -1.e-24, -1.e-28, -1.e-32, -1.e-36, -1.e-40, -1.e-44, -1.e-48, -1.e-52, -1.e-56, -1.e-60, -1.e-64, -1.e-68, -1.e-72, -1.e-76]) H=sps.diags([vec,vec],[-1,1],dtype='f8') denseHam=H.toarray() # eigenvalues only, boils down to LAPACK's DSTERF s1=np.linalg.eigvalsh(denseHam) # LAPACK's dsyevd, divide and conquer (s2,basis)=np.linalg.eigh(denseHam) # LAPACK's dsyev instead of LAPACK's dsyevd (s3,basis3,error)=scipy.linalg.lapack.dsyev(denseHam) print("Note the difference between the eigenvalues computed with eigvalsh (1stcolumn) and eigh (2nd column)") for elem in range(len(s1)): print (s1[elem]," ",s2[elem], s3[elem]) DORGTR。但这需要更多的工作和编译步骤。