Numpy计算半正定矩阵特征值异常:eig与eigh如何选择?
解决Numpy计算半正定非对称矩阵特征值的复数与精度问题
问题根源
numpy.linalg.eigh仅适用于实对称/复厄米特矩阵,你的随机游走拉普拉斯矩阵laplacian_rw是半正定但非对称的,用eigh会直接基于矩阵下三角部分计算,完全偏离真实特征值。numpy.linalg.eig对实矩阵计算时,理论上半正定矩阵的特征值应为非负实数,但浮点计算误差会产生极小的虚部(如0j),这是数值计算的正常现象。
解决方案
1. 对称半正定矩阵(如标准拉普拉斯laplacian)
继续使用eigh,仅需修正浮点误差带来的极小值:
eigs2, _ = np.linalg.eigh(laplacian) # 将接近0的极小值修正为0 eigs2_corrected = np.where(np.isclose(eigs2, 0, atol=1e-12), 0.0, eigs2) print(np.sort(eigs2_corrected))
2. 非对称半正定矩阵(如随机游走拉普拉斯laplacian_rw)
必须用eig计算,再提取实部并修正数值误差:
eigs1, _ = np.linalg.eig(laplacian_rw) # 提取实部(虚部为浮点误差导致的极小值) eigs1_real = np.real(eigs1) # 修正极小负值与接近0的值 eigs1_corrected = np.where(np.isclose(eigs1_real, 0, atol=1e-12), 0.0, eigs1_real) eigs1_corrected[eigs1_corrected < 0] = 0.0 print(np.sort(eigs1_corrected))
完整修正后的MWE
import numpy as np from numpy.random import RandomState rng = RandomState(123456) num_v = 4 weights = rng.uniform(0, 1, num_v * num_v).reshape((num_v, num_v)) weights = np.tril(weights) + np.triu(weights.T, 1) np.fill_diagonal(weights, 0) degrees = weights.sum(axis=0) degrees_rw = 1 / degrees D_rw = np.diag(degrees_rw) degrees = 1 / np.sqrt(degrees) D = np.diag(degrees) I = np.eye(num_v) laplacian = I - np.matmul(np.dot(D, weights), D) laplacian_rw = I - np.matmul(D_rw, weights) # 处理对称拉普拉斯矩阵 eigs2, _ = np.linalg.eigh(laplacian) eigs2_corrected = np.where(np.isclose(eigs2, 0, atol=1e-12), 0.0, eigs2) print("对称拉普拉斯特征值(修正后):", np.sort(eigs2_corrected)) # 处理随机游走拉普拉斯矩阵 eigs1, _ = np.linalg.eig(laplacian_rw) eigs1_real = np.real(eigs1) eigs1_corrected = np.where(np.isclose(eigs1_real, 0, atol=1e-12), 0.0, eigs1_real) eigs1_corrected[eigs1_corrected < 0] = 0.0 print("\n随机游走拉普拉斯特征值(修正后):", np.sort(eigs1_corrected))
总结
- 对称半正定矩阵:优先用
eigh,结果更稳定,仅需修正浮点误差。 - 非对称半正定矩阵:只能用
eig,提取实部后过滤极小虚部与负值,即可得到符合理论预期的非负特征值。
内容的提问来源于stack exchange,提问作者vpk
相关产品推荐
相关产品推荐

