我要处理的元素很少的稀疏矩阵。考虑一个向量:
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.linalg
和numpy.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 ++还能正确计算特征向量。
所以我有两个问题:
np.linalg.eigh(denseHam)
的频谱不同于np.linalg.eigvalsh(denseHam)
?非常感谢您!
---更新------
我在这里粘贴了一个最小的完整示例。请注意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])
答案 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
在缩放并缩小为三角形后会切换为DSTEDC
。 DSTEDC
使用分治法计算对称三对角矩阵的所有特征值和特征向量。
输入矩阵的小范数无关紧要,因为在这种情况下可能会应用缩放。由于实对称矩阵的特征值幅值相差很大(从3.16224604e- 63至3.16230928e-03),它是病态的。此属性极大地影响了大多数线性代数过程(包括特征值计算)的准确性。提供的矩阵已经是三角形式。
np.linalg.eigh()
失败。这可能与分而治之的策略有关。
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
。但这需要更多的工作和编译步骤。