任何偏斜对称矩阵( A ^ T = -A )都可以变成厄米特矩阵( iA < / strong>)并用复数对齐。但是also possible将它带入block-diagonal form with a special orthogonal transformation并仅使用真实算术找到它的eigevalues。这是在numpy的任何地方实现的吗?
答案 0 :(得分:1)
我们来看看LAPACK librarie的dgeev()
功能。该例程计算任何实际双精度方阵的特征值。此外,这个例程紧跟在numpy库的python函数numpy.linalg.eigvals()
之后。
dgeev()
中描述了A
使用的方法。它需要将矩阵S
缩减为documentation of LAPACK A
。
任何实矩阵A=QSQ^t
都可表示为:
Q
其中:
QQ^t=I
是一个真正的正交矩阵:S
A
是一个实际的块上三角矩阵。 S对角线上的块大小为1×1或2×2。实际上,如果A
是偏对称的,那么这种分解似乎非常接近S
的{{1}}。而且,真正看到偏斜对称矩阵A
的Schur形式S
是......偏斜对称的!
确实,让我们计算S^t=(Q^tAQ)^t
S^t=Q^t(Q^tA)^t
S^t=Q^tA^tQ
S^t=Q^t(-A)Q
S^t=-Q^tAQ
S^t=-S
的转置:
Q
因此,如果det(Q)=1
是特殊正交(S
),则P
是通过特殊正交变换获得的块对角线形式。否则,可以通过置换Q
的前两列来计算特殊正交矩阵Sd
,并通过改变符号来获得矩阵A
的另一个Schur形式S_{12}
。 S_{21}
和A=PSdP^t
。确实,Sd
。然后,A
是通过特殊正交变换获得的output='real'
的块对角线形式。
最后,即使应用于实数矩阵的real Schur form返回复数,也不会有很复杂的计算过程!
如果您只想计算真实的Schur表格,请使用带有参数import numpy as np
import scipy.linalg as la
a=np.random.rand(4,4)
a=a-np.transpose(a)
print "a= "
print a
#eigenvalue
w, v =np.linalg.eig(a)
print "eigenvalue "
print w
print "eigenvector "
print v
# Schur decomposition
#import scipy
#print scipy.version.version
t,z=la.schur(a, output='real', lwork=None, overwrite_a=True, sort=None, check_finite=True)
print "schur form "
print t
print "orthogonal matrix "
print z
的函数 block diagonal form obtained by a special orthogonal transformation。
只需要一段代码来检查:
catch
答案 1 :(得分:1)
是的,你可以通过在产品中间坚持单一转换来实现,因此我们得到了
A = V * U * V ^ -1 = V * T'* T * U * T'* T * V ^ { - 1} 。
一旦你明白了,你可以通过平铺事物来优化代码,但是让我们通过明确地形成T来实现天真的方式。
如果矩阵是偶数大小,则所有块都是复共轭。否则我们得到零作为特征值。特征值保证零实部,所以首先要清理噪声然后排序,使零点位于左上角(任意选择)。
n = 5
a = np.random.rand(n,n)
a=a-np.transpose(a)
[u,v] = np.linalg.eig(a)
perm = np.argsort(np.abs(np.imag(u)))
unew = 1j*np.imag(u[perm])
显然,我们需要对特征向量矩阵进行重新排序以保持等价。
vnew = v[:,perm]
到目前为止,我们除了在特征值分解中重新排序中间特征值矩阵之外什么也没做。现在我们从复杂的形式切换到实际的块对角形式。
首先,我们必须知道有多少零特征值
numblocks = np.flatnonzero(unew).size // 2
num_zeros = n - (2 * numblocks)
然后我们基本上,形成另一个单一的转换(这次复杂)并以同样的方式坚持
T = sp.linalg.block_diag(*[1.]*num_zeros,np.kron(1/np.sqrt(2)*np.eye(numblocks),np.array([[1.,1j],[1,-1j]])))
Eigs = np.real(T.conj().T.dot(np.diag(unew).dot(T)))
Evecs = np.real(vnew.dot(T))
这为您提供了新的实值分解。所以代码全部在一个地方
n = 5
a = np.random.rand(n,n)
a=a-np.transpose(a)
[u,v] = np.linalg.eig(a)
perm = np.argsort(np.abs(np.imag(u)))
unew = 1j*np.imag(u[perm])
vnew = v[perm,:]
numblocks = np.flatnonzero(unew).size // 2
num_zeros = n - (2 * numblocks)
T = sp.linalg.block_diag(*[1.]*num_zeros,np.kron(1/np.sqrt(2)*np.eye(numblocks),np.array([[1.,1j],[1,-1j]])))
Eigs = np.real(T.conj().T.dot(np.diag(unew).dot(T)))
Evecs = np.real(vnew.dot(T))
print(np.allclose(Evecs.dot(Eigs.dot(np.linalg.inv(Evecs))) - a,np.zeros((n,n))))
给出True
。请注意,这是获得真实频谱分解的天真方式。有很多地方需要跟踪数值误差累积。
示例输出
Eigs
Out[379]:
array([[ 0. , 0. , 0. , 0. , 0. ],
[ 0. , 0. , -0.61882847, 0. , 0. ],
[ 0. , 0.61882847, 0. , 0. , 0. ],
[ 0. , 0. , 0. , 0. , -1.05097581],
[ 0. , 0. , 0. , 1.05097581, 0. ]])
Evecs
Out[380]:
array([[-0.15419078, -0.27710323, -0.39594838, 0.05427001, -0.51566173],
[-0.22985364, 0.0834649 , 0.23147553, -0.085043 , -0.74279915],
[ 0.63465436, 0.49265672, 0. , 0.20226271, -0.38686576],
[-0.02610706, 0.60684296, -0.17832525, 0.23822511, 0.18076858],
[-0.14115513, -0.23511356, 0.08856671, 0.94454277, 0. ]])