在 Python 中对角化矩阵时保持对角线元素的顺序

Keep order of diagonal elements when diagonalizing a matrix in Python

提问人:Silviu 提问时间:11/9/2023 更新时间:11/18/2023 访问量:176

问:

我有一个非对角线矩阵,但非对角线元素比对角线元素小得多,因此在对角线化时,我预计对角线元素会发生变化,但只是一点点。我目前只是用来获取特征值,但是我希望这些值的顺序与对角化前的(相似)值相同。因此,如果原始矩阵中的对角线是 (100,200,300),那么在对角化后,我希望输出 (101,202,303) 而不是 (202,303,101)。我需要这个来跟踪对角化后哪个值映射到哪个值。我该怎么做?似乎对 ?谢谢!numpy.linalg.eignumpy.linalg.eig

Python 矩阵 对角线

评论

0赞 Code-Apprentice 11/17/2023
特定特征值与矩阵对角线上的特定元素具有关联的理由是什么?
0赞 Code-Apprentice 11/17/2023
也许您还需要特征向量,因为每个特征值都与一个特征向量相关联。
0赞 Silviu 11/17/2023
就我而言,这是一个物理系统,事实上,我也需要跟踪特征向量。在 python 中将特征向量与特征函数相关联很容易,问题又回到了原始的非对角矩阵。我正在为物理系统哈密顿量做这件事,举个例子,假设原始基基于量子数 X(即基是 X 的特征向量)。如果我们有一个选择规则,我只能连接 X 最多变化 1 的状态,那么 X=0 和 X=2 不会混合。
0赞 Silviu 11/17/2023
但是,如果在对角化时,最初 X=2 的状态得到一些 X=1 的混合物,现在 X=0 状态可以与它混合。但为了做到这一点,我需要跟踪特征值的组成(即它们由原始矩阵中的哪些元素组成,以及它们有多少 X=2 和 X=1 字符)

答:

0赞 ashah 11/9/2023 #1

用于计算矩阵的特征值时,返回的特征值不一定以任何特定方式排序。您可以参考 numpy 手册 https://numpy.org/doc/stable/reference/generated/numpy.linalg.eig.htmlnumpy.linalg.eig

现在,为了解决这个问题,我们可以举一个示例矩阵,其中非对角线元素不为零但很小,然后尝试将它们映射到它们的原始顺序

example_matrix = np.array([[100, 0.2, 0.3], [0.2, 200, 0.5], [0.3, 0.5, 300]])

# Compute eigenvalues and eigenvectors
eigenvalues, eigenvectors = np.linalg.eig(example_matrix)

# Original diagonal elements
original_diagonal = np.diag(example_matrix)

# Sort eigenvalues by how close they are to the original diagonal elements
sorted_indices = np.argsort(np.abs(eigenvalues - original_diagonal))

# Sorted eigenvalues and eigenvectors
sorted_eigenvalues = eigenvalues[sorted_indices]
sorted_eigenvectors = eigenvectors[:, sorted_indices]

# Output the sorted eigenvalues and their corresponding eigenvectors
sorted_eigenvalues, sorted_eigenvectors

结果

(array([ 99.99915299, 199.99789408, 300.00295293]),
 array([[-0.9999969 , -0.001985  ,  0.00150496],
        [ 0.0019925 , -0.9999855 ,  0.00500279],
        [ 0.00149501,  0.00500578,  0.99998635]]))

基本上,您需要根据与原始对角线的接近程度对特征值及其相应的特征向量进行排序。这样,您可以跟踪对特征值进行排序的排列,以便将它们映射回其原始顺序。

评论

0赞 Silviu 11/9/2023
但这并不能解决我的问题。我举的例子显然是一个非常简单的例子,可以澄清我用文字表达的意思。在实践中,我可以在对角线上(对角线化之前)有两个相等(或非常接近)的数字,比如 (100,100)。对角化后,它们会变成,比如说,(99,101)。除非我确定顺序被保留,否则我无法知道哪个来自哪里(而且我的矩阵很大,所以我也不能手动完成)。
0赞 ashah 11/9/2023
我同意。您需要保留一些订单,以确保哪个来自哪个元素。没有它,你肯定会失去轨道。
2赞 LSerni 11/17/2023 #2

框架挑战

我不认为这个问题可以有严格的解决方案,肯定不会使用直接的numpy。

通过对源代码的粗略检查,我认为要计算特征值 numpy 首先要计算参数矩阵行列式的根的神圣方法。这是最快、最准确的方法,可以保证有效

然而,它不能保持特征值顺序。

为简单起见,请考虑一个 2 x 2 矩阵:

[[ 10 .1 ] [.1 20]]

这有 lambda-det 的L^2-30L+199.99

但是,如果您考虑另一个矩阵,通过交换前一个对角线获得的相反顺序的相同值......

[[ 20 .1 ] [.1 10]]

...你最终会得到相同的决定因素。因此,从这个行列式中,你无法判断你从哪个矩阵开始,因此你无法确定原始对角线元素的顺序。继续进行计算,从这里开始,该信息将不再存在。您确实获得了特征值,但没有得到它们的原始顺序。

当你构建行列式时,就像你乘以 x*y 一样,关于因子顺序的信息会丢失(在乘法的情况下,它总是会完全丢失)。

因此,特征值似乎相对于原始对角线进行了“洗牌”。

但实际发生的并不是原始值“略有偏移”;每次它们都完全丢失了,其他类似的值会在它们的位置上重新创建。它们“看起来像”以前的值这一事实是一条红鲱鱼。因此,不能保证比赛是可能的。

这有点像泰迪和狮子等“消失的幻觉”谜题的数学版本:

People Vanishing illusion

您可以尝试做的是匹配“后验”值,即选择最接近原始对角线的特征值排列。

有一些算法可以做到这一点,使用不同的指标来确定哪种排列“更接近”(通常是最小二乘法)。

一种快速方法是按升序对初始对角线值进行排序,记录使用的排列(这是 O(n log n)),然后按升序对特征值进行排序并反向应用排列。这会让你得到一个“势均力敌”。

但是,仍然可以使用不同的算法(例如 Jacobi 方法或其他迭代方法)执行类似于您描述的内容。

请注意,此类算法需要在本地保留顺序。一个地方的价值仍然存在。但它仍然可能会发生变化,以便您以与预期不同的顺序获得值,例如,您从对角线 [2.1, 1.9, 12] 开始,第一个元素缩小到 1.999,第二个元素增长到 2.001,您可能会相信“两个元素被交换了”。

在Interwebs周围发现的实现

正如我所说,我的线性代数时代已经过去很久了,但我记得雅可比方法是迭代的,所以可能适合你的要求。我寻找它并停在第一个结果上。在我看来是正确的。如果不是这样,更集中的搜索可能仍然会产生一个有效的 Python 实现;该理论得到证实。

GitHub 页面

a = array([[2.0,0.1,0.1],[0.1,7.0,0.1],[0.1, 0.1, 11.0]])
lam,x = jacobi(a,tol = 1.0e-9)
print(lam)

# Expected value: close to [2 7 11] in this order
[1.99693421  6.99940092 11.00366487]

运行其他几个随机值产生了一致的结果。

评论

0赞 Silviu 11/17/2023
但是有一些程序可以做到这一点(不是在 python 中,所以我需要深入挖掘才能理解细节),以对角化物理系统的哈密顿量的形式。为了正确地做到这一点,需要跟踪初始矩阵(在对角化之前),例如,不要显示与禁止跃迁相对应的能量差(即涉及的 2 个水平/特征值具有错误的量子数)。我可以更详细地介绍这些软件,但关键是它们完全符合我的需求,所以应该是可能的。
0赞 Silviu 11/17/2023
这是这样一个程序:pgopher.chm.bris.ac.uk/index.html。如果你转到该页面的底部,就我而言,我需要做的是获得类似于该图底部的内容,即峰值(在我的情况下,我不需要它们具有宽度),它们对应于哈密顿量特征值之间的差异。但为了做到这一点,我需要跟踪原始矩阵,例如,峰的高度取决于特征向量包含的原始矩阵中的内容。
0赞 LSerni 11/17/2023
是的,可以按照您的要求做一些事情。似乎有一个适用于 Python 的 Jacobi 实现。试着研究一下。我的线性代数时代早已一去不复返了,但我相信这应该有效。
0赞 cards 11/18/2023
@Silviu您能否添加一个工作示例,您期望什么?顺序的东西是相当混乱的(碱基的变化 - 相似性转换 - 在一些组合学值上是唯一的)。进一步注意,通过多项式方法(特征多项式)进行对角化并不是唯一的方法,您可以使用带有扩展矩阵的高斯消元法并保持“顺序”:[M | I ] -> [I | D ]
1赞 LSerni 11/18/2023
正如@cards所说,你不能用numpy做到这一点,但你应该能够非常接近Gauss或Jacobi或迭代对角化。我包括了我找到的实现,它似乎符合您的要求。但请广泛审查和测试它。
0赞 Bipin Sunar 11/17/2023 #3
import numpy as np

# Your non-diagonal matrix
A = np.array([[10, 0.1, 0.2],[0.3, 20, 0.4], [0.5, 0.6, 30]])

# Compute eigenvalues and eigenvectors
eigenvalues, eigenvectors = np.linalg.eig(A)

# Sort eigenvalues and corresponding eigenvectors based on magnitudes
sorted_indices = np.argsort(np.abs(eigenvalues))
sorted_eigenvalues = eigenvalues[sorted_indices]
sorted_eigenvectors = eigenvectors[:, sorted_indices]

# Print the sorted eigenvalues and corresponding eigenvectors
print("Original Eigenvalues:", eigenvalues)
print("Sorted Eigenvalues:", sorted_eigenvalues)
print("Corresponding Eigenvectors:", sorted_eigenvectors)

现在,您可以根据需要使用 sorted_eigenvalues 和 sorted_eigenvectors。

0赞 Kamil 11/17/2023 #4

基于特征向量的方法难以处理紧密值的对角线元素,影响了其后对角线化的精度。

制作了一个简洁的小方法,称为迭代调整方法。这种方法会一点一点地调整矩阵,以按照您想要的方式排列这些特征值。

试试这个:

import numpy as np

def iterative_adjustment(matrix, max_iterations=100, tolerance=1e-4):
    original_diagonal = np.diag(matrix)
    for _ in range(max_iterations):
        eigenvalues, eigenvectors = np.linalg.eig(matrix)
        sorted_indices = np.argsort(eigenvalues)
        eigenvalues = eigenvalues[sorted_indices]
        eigenvectors = eigenvectors[:, sorted_indices]

        if np.allclose(original_diagonal, eigenvalues, atol=tolerance):
            return eigenvalues, eigenvectors

        adjustment = np.diag(eigenvalues - original_diagonal)
        matrix -= adjustment * 0.01

    return eigenvalues, eigenvectors

用法:只需将你的矩阵插入这个函数,它就会为你带来魔力。这就像在这里和那里给你的矩阵一点点推动,直到一切都到位。

它是如何工作的?

初始对角化:首先,我们进行标准对角化以获得起点 - 矩阵的特征值和特征向量。

迭代调整

我们看看我们得到的特征值如何与原始对角线数字匹配。 然后,我们对您的矩阵进行小幅调整,使这些特征值更接近您的原始数字。 我们继续做这个小舞蹈 - 调整和对角线化 - 直到特征值与原始对角线数字的顺序对齐。

处理收盘数字

这种方法非常漂亮,因为它还可以处理您的对角线数字非常接近的情况。它轻轻地推动特征值,使它们保持正确的顺序。

评论

0赞 Silviu 11/17/2023
谢谢你,我会试一试。但是,我有点担心时间。np.linalg.eig 在我的情况下大约需要 1 分钟,仅对一个矩阵执行 100 次就需要几天时间才能获得一组值(最终我想优化,或者至少在某个参数空间上探索它们)。
0赞 LukeAtmi 11/18/2023 #5

让我们成为对角线元素的列表。对列表进行排序将使我们能够找到以下排列:diagsortedsorted_diagdiag

sorted_diag = sorted(diag)
perm = [sorted_diag.index(d) for d in diag]

perm会确实是排列,因为会回来.[sorted_diag[i] for i in perm]diag

现在,由于特征值集与对角线元素集“相似”,因此在排序的特征值数组上使用此排列应按我们想要的顺序生成特征值。如果是特征值的数组,那么我们可以eigens

sorted_eigens = sorted(eigens)
eigens_good = [sorted_eigens[i] for i in perm]

所以整个事情是这样的:

mat = np.array([[203, 0.4, 0.6], [0.3, 301, 0.7], [0.6, 0.1, 101]])

diag = np.diag(mat) # [203, 301, 101]
eigens, _ = np.linalg.ein(mat) # [203.00228619, 100.99612982, 301.00158399]

sorted_diag = sorted(diag) # [101, 203, 301]
perm = [sorted_diag.index(d) for d in diag] # [1, 2, 0]

sorted_eigens = sorted(eigens) # [100.99612982, 203.00228619, 301.00158399]
eigens_good = [sorted_eigens[i] for i in perm] # [203.00228619, 100.99612982, 301.00158399]

这正是我们想要的,就像最初的订单一样!eigens_good[203, 101, 301]

注意:可以通过使用与列表推导式不同的东西来对其进行更多优化,但我认为这可能已经足够了。sorted

对角线中的相同值:

如果对角线具有相同的元素,则上述方法不会立即起作用,但您可以获得类似的东西。您只需要更仔细地生成,例如,通过“弹出”排序数组副本的元素。perm

diag = np.diag(mat)
sorted_diag = sorted(diag)

perm = [None]  * len(diag)
temp = sorted_diag.copy()
for i, d in enumerate(diag):
    index_d = temp.index(d)
    perm[i] = index_d
    temp[index_d] = None

with ,正确地再次产生,因此以与之前相同的方式使用 with 应该没问题。diag = [203, 101, 404, 101][sorted_diag[i] for i in perm]diagpermeigens

评论

0赞 Silviu 11/18/2023
这似乎并没有解决主要问题,与对角线元素相等的情况有关
0赞 LukeAtmi 11/18/2023
编辑能解决这种情况吗?它可能找不到特征值和对角线元素之间的“完美”关系,但我怀疑这在技术上是不可能的。但至少对角线元素将与接近它们的特征值相匹配。