プログラミング言語Pythonの数値計算ライブラリNumPyで行列、その計算をあつかう方法を見てみたいと思います。行列といえば連立一次方程式が思い浮かびますが、NumPyで方程式を解く方法は次の過去記事で解説しています。あわせて読んでみてください。
- 行列の入力
- ゼロ行列の入力
- 単位行列の入力
- 対角行列の入力
- 行列の線形和・積・冪乗
- 行列の転置
- 複素係数の行列
- 行列の複素共役
- 行列の随伴共役
- ベクトルの入力
- ベクトルの線形和
- ベクトルの内積
- ベクトルのノルム
- 行列の固有値・固有ベクトル
- 行列式
- 行列の固有和(トレース)
- 逆行列
- LU分解
- コレスキー分解
- QR分解
- 特異値分解
行列の入力
次のような行列は以下のようにして入力します。
import numpy as np A = np.array([ [2, 4, 5], [1, 3, 2], [4, 2, 1]]) B = np.array([ [2, 5, 4], [1, 2, 3], [4, 1, 2]])
注意:NumPyの『行列』をあつかうには2次元配列の numpy.array と行列の numpy.matrix とがあります。ここではサブモジュール linalg でのあつかいを可能にする前者(numpy.array)によって行列を記述することとします。
ゼロ行列の入力
要素がすべてゼロである次のような行列は以下のようにして入力します。
O = np.zeros((3,2)) # 引数はタプル型 print(O)
対角行列の入力
対角行列は以下のようにして入力します。
D = np.diag([1,2,3]) print(D)
行列の線形和・積・冪乗
例えば
は
-2*A+2*B
によって計算できます。行列の積(例えば )については
A@B
として計算します。冪乗(例えば )は
np.linalg.matrix_power(A, 3)
です。実際
np.linalg.matrix_power(A, 3)-A@A@A
です。
行列の転置
B.transpose()
行列の対称部分、反対称部分はそれぞれ
によって取り出すことができます。
1/2*(B+B.transpose())
1/2*(B-B.transpose())
複素係数の行列
C = np.array([ [2, 1+2j], [1-2j, 3]]) T2 = np.array([ [0, -1j], [1j, 0]])
T2@C@T2@C.transpose()
行列の随伴共役
C.conjugate().transpose()
ベクトルの入力
ベクトルは行列の列(または行)が1つしかない特殊な場合です。例えば
は
u = np.array([ [3],[2],[1]]) v = np.array([ [-1],[3],[-3]])
と入力します。
ベクトルの線形和
2*u+v
ベクトルのノルム
np.linalg.norm(u)
行列の固有値・固有ベクトル
r, S = np.linalg.eig(A)
により、行列 の固有値が
と求まり、対角化行列が
と求まります。よって対応する固有ベクトルが
とわかります。固有値ベクトルの定義から なので、
を計算してみると
r1 = 7.78728013 u1 = np.array([ [-0.7411868],[-0.38466593],[-0.55015838]]) 1/r1*A@u1
などとして を数値的に確かめることができます。
行列式
行列式は
np.linalg.det(A)
で計算されます。行列式の性質から、行列式の値は固有値の積に等しいことが知られています。
これを確かめてみましょう。
r1 = 7.78728013 r2 = -2.86354908 r3 = 1.07626895 np.linalg.det(A)/(r1*r2*r3)
より、公式が数値的に成り立っていることがわかります。
行列の固有和(トレース)
行列の固有和(対角成分の和、トレース)は
np.trace(A)
で計算されます。トレースの性質から、トレースの値は固有値の和に等しいことが知られています。
確かめてみると
np.trace(A)/(r1+r2+r3)
より、公式が成り立っていることが数値的にわかります。
逆行列
逆行列は
np.linalg.inv(A)
で計算されます。検算してみると
A@np.linalg.inv(A)
np.linalg.inv(A)@A
より、確かに逆行列を与えます。また、上でみた行列 の対角化行列
について
を計算してみると
np.linalg.inv(S)@A@S
より
となって、対角化されることがわかります。
LU分解
行列を下三角形行列 と上三角形行列
(と並び替え行列
)の積として表すのがLU分解です。
import scipy.linalg as la P, L, U = la.lu(A)
いまの具体例の場合には
と求まります。検算すると
A-(P@L@U)
確かに となっていることがわかります。さらに上三角行列
の対角成分を1とするように、対角行列との積
で表すと
がわかります。実際
D = np.diag([4,3,-2]) U0 = np.array([ [1,0.5,0.25], [0,1,1.5], [0,0,1]]) U-(D@U0)
により確かめられます。このように、行列を対角成分が1の下三角形行列 と上三角形行列
、そして対角行列
の積として
と表したとき、これを行列のLDU分解と言います。
コレスキー分解
コレスキー分解(Cholesky decomposition)は、正定値(固有値がすべて正値)エルミート行列に対するLU分解の特別の場合で、行列を下三角行列 とそのエルミート随伴によって得られる上三角形行列
の積によって表したものです。
H = np.array([ [3,1,1], [1,3,-1], [1,-1,2]]) Lh = np.linalg.cholesky(H)
実際
Lh@Lh.conjugate().transpose()
によって確かめられます。
QR分解
QR分解は、-実行列を、
次直交行列
と
-上三角行列
との積として表すことです。
Q, R = np.linalg.qr(B)
より
と求まります。確かめてみましょう。
Q.transpose()@Q
Q@Q.transpose()
より、行列 が直交行列であることがわかります。さらに
Q@R
より、 が確認できます。
特異値分解
特異値分解は、-行列を、
次ユニタリ行列
と
次ユニタリ行列
、そして以下で説明する行列の特異値によって定まる
-行列
の積として表すことです。
PythonのNumPyライブラリでの組み込み関数
K = np.array([ [3,1,1], [1,3,-1]]) U, sigma, V = np.linalg.svd(K)
により
の特異値分解 が
によって与えられると計算されます。確認すると
U@U.conjugate().transpose()
U.conjugate().transpose()@U
V@V.conjugate().transpose()
V.conjugate().transpose()@V
より と
がユニタリ行列であることがわかります。さらに、特異値ですが、これは正値行列
の固有値の正のルートになっています。実際、
M = K@K.conjugate().transpose() np.linalg.eig(M)
より の固有値が
と求まりますが、その正のルートは
です。すなわち、行列 は
という、対角成分に特異値が並んだ形をとります。最後に分解の検算として
Sigma = np.array([ [sigma[0],0,0], [0,sigma[1],0]]) U@Sigma@V
より、 が確認できます。
今回は、Python(NumPy)で行列をあつかう方法についてまとめて解説しました。