共役勾配法(きょうやくこうばいほう、英: conjugate gradient method、CG法とも呼ばれる)は対称正定値行列を係数とする連立一次方程式を解くためのアルゴリズムである[1][2][3][4]。反復法として利用され[1][2][3][4]、コレスキー分解のような直接法では大きすぎて取り扱えない、大規模な疎行列を解くために利用される。そのような問題は偏微分方程式などを数値的に解く際に常に現れる[1][5][6][7]。
共役勾配法は、エネルギー最小化などの最適化問題を解くために用いることもできる[8][9][10]。
双共役勾配法(英語版)は、共役勾配法の非対称問題への拡張である[11]。
また、非線形問題を解くために、さまざまな非線形共役勾配法が提案されている[12][13][14][15]。
対称正定値行列Aを係数とするn元連立一次方程式
Ax = b
の解をx*とする。
非零ベクトルu、vが
u T A v = 0 {\displaystyle \mathbf {u} ^{\mathrm {T} }\mathbf {A} \mathbf {v} =\mathbf {0} }
を満たすとき、u、vはAに関して共役であるという[2][3][4]。Aは対称正定値なので、左辺から内積
⟨ u , v ⟩ A := ⟨ A T u , v ⟩ = ⟨ A u , v ⟩ = ⟨ u , A v ⟩ = u T A v {\displaystyle \langle \mathbf {u} ,\mathbf {v} \rangle _{\mathbf {A} }:=\langle \mathbf {A} ^{\mathrm {T} }\mathbf {u} ,\mathbf {v} \rangle =\langle \mathbf {A} \mathbf {u} ,\mathbf {v} \rangle =\langle \mathbf {u} ,\mathbf {A} \mathbf {v} \rangle =\mathbf {u} ^{\mathrm {T} }\mathbf {A} \mathbf {v} }
を定義することができる。この内積に関して2つのベクトルが直交するなら、それらのベクトルは互いに共役である。 この関係は対称で、uがvに対して共役なら、vもuに対して共役である(この場合の「共役」は複素共役と無関係であることに注意)。
{pk}をn個の互いに共役なベクトル列とする。pkは基底Rnを構成するので、Ax = bの解x*をこの基底で展開すると、
x ∗ = ∑ i = 1 n α i p i {\displaystyle \mathbf {x} _{*}=\sum _{i=1}^{n}\alpha _{i}\mathbf {p} _{i}}
と書ける。ただし係数は
A x ∗ = ∑ i = 1 n α i A p i = b . {\displaystyle \mathbf {A} \mathbf {x} _{*}=\sum _{i=1}^{n}\alpha _{i}\mathbf {A} \mathbf {p} _{i}=\mathbf {b} .} p k T A x ∗ = ∑ i = 1 n α i p k T A p i = p k T b . {\displaystyle \mathbf {p} _{k}^{\mathrm {T} }\mathbf {A} \mathbf {x} _{*}=\sum _{i=1}^{n}\alpha _{i}\mathbf {p} _{k}^{\mathrm {T} }\mathbf {A} \mathbf {p} _{i}=\mathbf {p} _{k}^{\mathrm {T} }\mathbf {b} .} α k = p k T b p k T A p k = ⟨ p k , b ⟩ ⟨ p k , p k ⟩ A = ⟨ p k , b ⟩ ‖ p k ‖ A 2 . {\displaystyle \alpha _{k}={\frac {\mathbf {p} _{k}^{\mathrm {T} }\mathbf {b} }{\mathbf {p} _{k}^{\mathrm {T} }\mathbf {A} \mathbf {p} _{k}}}={\frac {\langle \mathbf {p} _{k},\mathbf {b} \rangle }{\,\,\,\langle \mathbf {p} _{k},\mathbf {p} _{k}\rangle _{\mathbf {A} }}}={\frac {\langle \mathbf {p} _{k},\mathbf {b} \rangle }{\,\,\,\|\mathbf {p} _{k}\|_{\mathbf {A} }^{2}}}.}
で与えられる。
この結果は、上で定義した内積を考えるのが最も分かりやすいと思われる。
以上から、Ax = bを解くための方法が得られる。すなわち、まずn個の共役な方向を見つけ、それから係数αkを計算すればよい。
共役なベクトル列pkを注意深く選ぶことにより、一部のベクトルからx*の良い近似を得られる可能性がある。そこで、共役勾配法を反復法として利用することを考える[2][3][4]。こうすることで、nが非常に大きく、直接法では解くのに時間がかかりすぎるような問題にも適用することができる。
x*の初期値をx0 = 0 とする。x*が二次形式
f ( x ) = 1 2 x T A x − b T x , x ∈ R n . {\displaystyle f(\mathbf {x} )={\frac {1}{2}}\mathbf {x} ^{\mathrm {T} }\mathbf {A} \mathbf {x} -\mathbf {b} ^{\mathrm {T} }\mathbf {x} ,\quad \mathbf {x} \in \mathbf {R} ^{n}.}
を最小化する一意な解であることに注意し、最初の基底ベクトルp1をx = x0でのfの勾配Ax0−b=−bとなるように取る。このとき、基底の他のベクトルは勾配に共役である。そこで、この方法を共役勾配法と呼ぶ[2][3][4]。
rkをkステップ目での残差
r k = b − A x k {\displaystyle \mathbf {r} _{k}=\mathbf {b} -\mathbf {Ax} _{k}}
とする。rkはx = xkでのfの負の勾配であることに注意されたい。最急降下法はrkの方向に進む解法である。pkは互いに共役でなければならないので、rkに最も近い方向を共役性を満たすように取る。これは
p k + 1 = r k + 1 − p k T A r k + 1 p k T A p k p k {\displaystyle \mathbf {p} _{k+1}=\mathbf {r} _{k+1}-{\frac {\mathbf {p} _{k}^{\mathrm {T} }\mathbf {A} \mathbf {r} _{k+1}}{\mathbf {p} _{k}^{\mathrm {T} }\mathbf {A} \mathbf {p} _{k}}}\mathbf {p} _{k}}
のように表すことができる(記事冒頭の図を参照)。
以上の方法を簡素化することにより、Aが実対称正定値である場合にAx = bを解くための以下のアルゴリズムを得る[4]。初期ベクトルx0は近似解もしくは0とする。
r 0 = b − A x 0 {\displaystyle r_{0}=b-Ax_{0}} p 0 = r 0 {\displaystyle p_{0}=r_{0}} for (k = 0; ; k++) α k = r k T p k p k T A p k {\displaystyle \alpha _{k}={\frac {r_{k}^{\rm {T}}p_{k}}{p_{k}^{\rm {T}}Ap_{k}}}} x k + 1 = x k + α k p k {\displaystyle x_{k+1}=x_{k}+\alpha _{k}p_{k}} r k + 1 = r k − α k A p k {\displaystyle r_{k+1}=r_{k}-\alpha _{k}Ap_{k}} if r k + 1 {\displaystyle r_{k+1}} が十分に小さい then break β k = r k + 1 T r k + 1 r k T r k {\displaystyle \beta _{k}={\frac {r_{k+1}^{\rm {T}}r_{k+1}}{r_{k}^{\rm {T}}r_{k}}}} p k + 1 = r k + 1 + β k p k {\displaystyle p_{k+1}=r_{k+1}+\beta _{k}p_{k}} 結果は x k + 1 {\displaystyle x_{k+1}}
Gnu Octaveで書くと以下のようになる。
function [x] = conjgrad(A,b,x0) r = b - A*x0; w = -r; z = A*w; a = (r'*w)/(w'*z); x = x0 + a*w; B = 0; for i = 1:size(A)(1); r = r - a*z; if( norm(r) < 1e-10 ) break; endif B = (r'*z)/(w'*z); w = -r + B*w; z = A*w; a = (r'*w)/(w'*z); x = x + a*w; end end
前処理行列とは、Aと同値なP-1A (PT)-1の条件数がAより小さく、Ax=bよりP-1A (PT)-1 x′ =P-1b′の方が容易に解けるような正定値行列 P.PTを指す[4]。前処理行列の生成には、ヤコビ法、ガウス・ザイデル法、対称SOR法などが用いられる[16][17]。
最も単純な前処理行列は、Aの対角要素のみからなる対角行列である。これはヤコビ前処理または対角スケーリングとして知られている。対角行列は逆行列の計算が容易かつメモリも消費しない点で、入門用として優れた方法である。より洗練された方法では、κ(A)の減少による収束の高速化とP-1の計算に要する時間とのトレードオフを考えることになる。
任意の実行列Aに対してATAは対称(半)正定値となるので、係数行列をATA、右辺をATbとする正規方程式を解くことにより、共役勾配法を任意のn×m行列に対して適用することができる(CGNR法[18])。
ATAx = ATb
反復法としては、ATAを明示的に保持する必要がなく、行列ベクトル積、転置行列ベクトル積を計算すればよいので、Aが疎行列である場合にはCGNR法は特に有効である。ただし、条件数κ(ATA)がκ(A2)に等しいことから収束は遅くなる傾向があり、前処理行列を使用するCGLS (Conjugate Gradient Least Squares[19])、LSQRなどの解法が提案されている。LSQRはAが悪条件である場合に最も数値的に安定な解法である[20][21]。