Rには、qr()
LINPACKまたはLAPACKのいずれかを使用してQR分解を実行する関数があります(私の経験では、後者は5%高速です)。返される主なオブジェクトは、上三角行列 R (つまりR=qr[upper.tri(qr)]
) に含まれる行列 "qr" です。ここまでは順調ですね。qr の下三角部分には、Q が「コンパクトな形で」含まれています。を使用して、qr 分解から Q を抽出できqr.Q()
ます。の逆数を求めたいqr.Q()
. つまり、私は Q と R を持っていて、それらを「qr」オブジェクトに入れたいと考えています。R は自明ですが、Q はそうではありません。目標はそれに適用することqr.solve()
であり、大規模なシステムよりもはるかに高速solve()
です。
2 に答える
序章
R は、QR 分解を計算するために、既定ではLINPACKルーチンを使用するか、指定されている場合は LAPACKルーチンを使用します。どちらのルーチンも、ハウスホルダー反射を使用して分解を計算します。mxn 行列 A は、A = QR として mxn エコノミー サイズの直交行列 (Q) と nxn 上三角行列 (R) に分解されます。ここで、Q は、t ハウスホルダー反射行列の積によって計算できます。t は小さい方です。 m-1 と n: Q = H 1 H 2 ...H t .dqrdc
DGEQP3
各反射行列 H iは、長さ (m-i+1) のベクトルで表すことができます。たとえば、H 1は、コンパクト ストレージのために長さ m のベクトルを必要とします。このベクトルの 1 つを除くすべてのエントリは、入力行列の下三角の最初の列に配置されます (対角線は R 係数によって使用されます)。したがって、各リフレクションにはストレージのスカラーがもう 1 つ必要であり、これは補助ベクトル ( $qraux
R の結果で呼び出されるqr
) によって提供されます。
使用されるコンパクトな表現は、LINPACK ルーチンと LAPACK ルーチンの間で異なります。
LINPACK のやり方
ハウスホルダー反射は、H i = I - v i v i T /p iとして計算されます。ここで、I は恒等行列、p iは の対応するエントリ、$qraux
v iは次のとおりです。
- v i [1..i-1] = 0、
- v i [i] = p i
- v i [i+1:m] = A[i+1..m, i] (つまり、 を呼び出した後の A の下三角の列
qr
)
LINPACK の例
ウィキペディアの QR 分解記事の例を R で見てみましょう。
分解される行列は
> A <- matrix(c(12, 6, -4, -51, 167, 24, 4, -68, -41), nrow=3)
> A
[,1] [,2] [,3]
[1,] 12 -51 4
[2,] 6 167 -68
[3,] -4 24 -41
分解を行い、結果の最も関連性の高い部分を以下に示します。
> Aqr = qr(A)
> Aqr
$qr
[,1] [,2] [,3]
[1,] -14.0000000 -21.0000000 14
[2,] 0.4285714 -175.0000000 70
[3,] -0.2857143 0.1107692 -35
[snip...]
$qraux
[1] 1.857143 1.993846 35.000000
[snip...]
この分解は、2 つの Householder 反射を計算し、それらに A を掛けて R を取得することによって (隠れて) 行われました$qr
。
> p = Aqr$qraux # for convenience
> v1 <- matrix(c(p[1], Aqr$qr[2:3,1]))
> v1
[,1]
[1,] 1.8571429
[2,] 0.4285714
[3,] -0.2857143
> v2 <- matrix(c(0, p[2], Aqr$qr[3,2]))
> v2
[,1]
[1,] 0.0000000
[2,] 1.9938462
[3,] 0.1107692
> I = diag(3) # identity matrix
> H1 = I - v1 %*% t(v1)/p[1] # I - v1*v1^T/p[1]
> H2 = I - v2 %*% t(v2)/p[2] # I - v2*v2^T/p[2]
> Q = H1 %*% H2
> Q
[,1] [,2] [,3]
[1,] -0.8571429 0.3942857 0.33142857
[2,] -0.4285714 -0.9028571 -0.03428571
[3,] 0.2857143 -0.1714286 0.94285714
上記で計算された Q が正しいことを確認しましょう。
> qr.Q(Aqr)
[,1] [,2] [,3]
[1,] -0.8571429 0.3942857 0.33142857
[2,] -0.4285714 -0.9028571 -0.03428571
[3,] 0.2857143 -0.1714286 0.94285714
いいね!QR が A と等しいことも確認できます。
> R = qr.R(Aqr) # extract R from Aqr$qr
> Q %*% R
[,1] [,2] [,3]
[1,] 12 -51 4
[2,] 6 167 -68
[3,] -4 24 -41
LAPACKのやり方
ハウスホルダー反射は、H i = I - p i v i v i Tとして計算されます。ここで、I は恒等行列、p iは の対応するエントリ、$qraux
v iは次のとおりです。
- v i [1..i-1] = 0、
- v i [i] = 1
- v i [i+1:m] = A[i+1..m, i] (つまり、 を呼び出した後の A の下三角の列
qr
)
R で LAPACK ルーチンを使用する場合、別のひねりがあります。列のピボットが使用されるため、分解によって別の関連する問題が解決されます。AP = QR、ここで、P は順列行列です。
LAPACKの例
このセクションでは、前と同じ例を実行します。
> A <- matrix(c(12, 6, -4, -51, 167, 24, 4, -68, -41), nrow=3)
> Bqr = qr(A, LAPACK=TRUE)
> Bqr
$qr
[,1] [,2] [,3]
[1,] 176.2554964 -71.1694118 1.668033
[2,] -0.7348557 35.4388886 -2.180855
[3,] -0.1056080 0.6859203 -13.728129
[snip...]
$qraux
[1] 1.289353 1.360094 0.000000
$pivot
[1] 2 3 1
attr(,"useLAPACK")
[1] TRUE
[snip...]
$pivot
フィールドに注意してください。それに戻ります。ここで、情報から Q を生成しAqr
ます。
> p = Bqr$qraux # for convenience
> v1 = matrix(c(1, Bqr$qr[2:3,1]))
> v1
[,1]
[1,] 1.0000000
[2,] -0.7348557
[3,] -0.1056080
> v2 = matrix(c(0, 1, Bqr$qr[3,2]))
> v2
[,1]
[1,] 0.0000000
[2,] 1.0000000
[3,] 0.6859203
> H1 = I - p[1]*v1 %*% t(v1) # I - p[1]*v1*v1^T
> H2 = I - p[2]*v2 %*% t(v2) # I - p[2]*v2*v2^T
> Q = H1 %*% H2
[,1] [,2] [,3]
[1,] -0.2893527 -0.46821615 -0.8348944
[2,] 0.9474882 -0.01602261 -0.3193891
[3,] 0.1361660 -0.88346868 0.4482655
ここでも、上記で計算された Q は、R が提供する Q と一致します。
> qr.Q(Bqr)
[,1] [,2] [,3]
[1,] -0.2893527 -0.46821615 -0.8348944
[2,] 0.9474882 -0.01602261 -0.3193891
[3,] 0.1361660 -0.88346868 0.4482655
最後に、QR を計算しましょう。
> R = qr.R(Bqr)
> Q %*% R
[,1] [,2] [,3]
[1,] -51 4 12
[2,] 167 -68 6
[3,] 24 -41 -4
違いに気づきましたか?QR は、Bqr$pivot
上記の順序で列が並べ替えられた A です。
OPが尋ねるのと同じ問題を調査しましたが、それは可能ではないと思います。基本的に、OP の問題は、明示的に計算された Q があれば、H1 H2 ... Ht を復元できるかどうかです。QRをゼロから計算しなければこれが可能だとは思いませんが、そのような解決策があるかどうかも知りたいです.
OPと同様の問題がありますが、別のコンテキストでは、反復アルゴリズムで列や行を追加して行列 A を変更する必要があります。最初に、QR は DGEQRF を使用して計算されるため、コンパクトな LAPACK 形式になります。行列 A が新しい行などで変更された後、既存の R の最小対角のゼロ以外の要素を消滅させ、新しい R を構築するリフレクターまたはローテーターの新しいセットをすばやく構築できますが、今では H1_old のセットがありますH2_old ... Hn_old および H1_new H2_new ... Hn_new (および同様にタウ) は、単一の QR コンパクト表現に混同することはできません。私が持っている2つの可能性は、おそらくOPには同じ2つの可能性があります。
- 最初に計算するときでも、更新ごとに余分なフロップを犠牲にして計算するときでも、常に Q と R を明示的に分離して維持しますが、必要なメモリは十分に制限された状態に保ちます。
- コンパクトな LAPACK 形式に固執しますが、新しい更新が行われるたびに、これらすべての更新リフレクターのミニ セットのリストを保持します。システムを解く時点で、大きな Q'*c を実行します。つまり、H1_u3*H2_u3*...*Hn_u3*H1_u2*H2_u2*...*Hn_u2*H1_u1*H2_u1...*Hn_u1*H1*H2* です。 ...*Hn*c ここで、ui は QR 更新番号です。これは潜在的に多くの乗算を行う必要があり、メモリを追跡する必要がありますが、間違いなく最速の方法です。
David からの長い回答は基本的に、コンパクトな QR 形式とは何かを説明していますが、明示的に計算された Q と R を入力として持つこのコンパクトな QR 形式に到達する方法は説明していません。