如何实现 Hessenberg QR 算法?

计算科学 线性代数 迭代法
2021-12-20 10:42:14

就上下文而言,我正在从头开始创建一个线性代数库,用于 C 语言的学习。现在我正在计算特征值,但我对 QR 算法的实现是不同的。以下是我对 QR 算法的理解,在第一块代码之后(我相信是)问题的核心:Givens 旋转。

请注意,给出的代码是类似 python 的伪代码,而不是 C(尽管给出的代码看起来与源代码没有太大不同)。

我无法理解这里出了什么问题以及为什么。详情如下:

我的实现一开始就出现了分歧,并且在 3 次迭代之后绝对是显而易见的。我目前对算法的理解(使用 Hessenberg)是这样的:

给定n×n海森堡矩阵H,我想用 QR 算法计算它的特征值。为此,我必须申请n1从左侧转置 Givens 旋转(即Gn1TGn2T...G1TH)。给定的旋转等价于QT. 因此,我们可以以相反的顺序相乘来完成 QR 算法(即H¯=HG1G2...Gn1)。这最终会聚成一个上三角形,在其对角线上可以找到相应的特征值。

在代码中:

for i = 0 until convergence:
    for k = 0 to n-1:       // rotation to make H upper triangular
        givensLeft(H, k)
    for k = 0 to n-1:       // back to Hessenberg
        givensRight(H, k)

吉文斯轮换是我主要担心的,因为我不完全确定它们是否正常工作。我知道只有行/列kk+1被影响到的。因此,我可以通过乘以子矩阵从左侧执行 Givens 旋转Hk:k+1, k:n由转置的 Givens 矩阵GT

[ckskskck]

要从右侧执行 Givens 旋转(在 QR 算法中,这会将 Hessenberg 从由左 Givens 旋转引起的上三角形重新返回其形式),我将乘以子矩阵H1:k+1, k:k+1由(未转置的)Givens 矩阵G

[ckskskck]

在代码中:

givensLeft(H, k):
    a = H[k][k]
    b = H[k+1][k]
    r = hypot(a, b)
    c = a/r
    s = -1*b/r

    // these could be more efficient, I know
    for i = k+1 to H.columns:
        a = H[k][i]
        b = H[k+1][i]
        H[k][i] = c*a - s*b

    for i = k+1 to H.columns:
        a = H[k][i]
        b = H[k+1][i]
        H[k][i] = s*a + c*b

givensRight(H, k):
    a = H[0][k]
    b = H[0][k+1]
    r = hypot(a, b)
    c = a/r
    s = -1*b/r

    for i = 0 to k+1:
        a = H[i][k]
        b = H[i][k+1]
        H[i][k] = c*a - s*b

    for i = 0 to k+1:
        a = H[i][k]
        b = H[i][k+1]
        H[i][k+1] = s*a + c*b

我能想到的唯一问题是 Givens 旋转问题,因为它们似乎构成了整个 QR 算法,但我不知道会出现什么问题。我在网上看了几件事,概述了带有 Hessenberg 的 QR 算法(例如thisthis),这个实现是直接从前者编写的(使用算法 4.1 和 4.2)。我在这里或其他地方找不到任何有类似问题的人。

我的问题是概念性的吗?还是只是错误的编程?提前谢谢了。

1个回答

是的,那些 Givens 轮换对我来说似乎没有正确实施。由于您将此作为一个学习项目:向大师学习http://www.netlib.org/lapack/explore-html/de/da4/group__double__blas__level1_ga54d516b6e0497df179c9831f2554b1f9.html

还要确保您从左侧应用的给定旋转与您从右侧应用的旋转相同。我自己没有检查过,但对我来说,在你当前的实现中情况并不明显。最简单的方法是将旋转存储在某个数组中,或者实现更流行的 QR 算法的凸出追逐版本。

最后,在 QR 算法的实现中还有许多其他细节需要解决:加速收敛的转变、通缩、上溢/下溢。再次,我建议您向大师学习:https ://www.netlib.org/lapack/explore-html/d3/d03/dlahqr_8f_source.html#l00209

另一个提示是您还可以计算矩阵Q. 然后您可以验证它是否正交(QTQI应该很小),您还可以检查向后错误(QTHQH¯应该很小)。如果其中任何一个变大,您甚至可以逐行检查此错误以准确找到错误发生的位置。