随伴行列

随伴行列

共役転置と随伴行列

 今回からベクトルおよび行列の要素を複素数に拡張します。$n$ 次元複素数空間は complex number の頭文字をとって $\mathbb{C}^n$ と表されます。内積やノルム、転置行列などが、実空間 $\mathbb{R}^n$ における定義を包含するような形で再定義され、エルミート行列やユニタリ行列などの新しい種類の行列が登場します。

随伴行列

 実数空間では、行列の転置とは各要素を主対角成分について折り返すだけでした。しかし複素数の線型代数においては、一般に 共役転置、すなわち各要素の複素共役をとって転置する操作が重要な意味をもちます。行列 $A$ を共役転置して得られる行列を $A^{\dagger},\ A^H,\ A^*$ などの記号で表し、$A$ の 随伴行列 (adjoint matrix) とよびます。

 数学では $*$ が用いられることが普通ですが、$*$ は単に複素共役をとる意味で使われることもあって紛らわしいので、このサイトでは物理や工学の分野で広く用いられる $\dagger$ (ダガー) の記号を採用します。たとえば、
 
\[A=\begin{bmatrix}1&1+i\\1-i&i\end{bmatrix}\tag{1}\]
の随伴行列は
 
\[A^{\dagger}=\begin{bmatrix}1&1+i\\1-i&-i\end{bmatrix}\tag{2}\]
で与えられます。NumPy で共役転置関数 hermitian() を実装しておきましょう。配列オブジェクト arr の転置は arr.T で、複素共役は np.cunjugate(arr) で取得できます。

# SLA_070-1

import numpy as np
from scipy import linalg

def hermitian(arr):
    return np.conjugate(arr.T)

 (1) で定義した $A$ の随伴行列を求めてみます。

# SLA_070-2

# 行列を定義
A = np.array([[1, 1 + 1j],
              [1 - 1j, 1j]])

# Aの随伴行列
A_h = hermitian(A)

print(A_h)
[[1.-0.j 1.+1.j]
 [1.-1.j 0.-1.j]]

 任意のサイズの $A$ について以下の基本性質が成り立ちます。
\[\begin{align*}
&(A^{\dagger}) ^{\dagger}=A\tag{3}\\[6pt]
&(A+B) ^{\dagger}=A^{\dagger}+B^{\dagger}\tag{4}\\[6pt]
&(AB)^{\dagger}=B^{\dagger} A^{\dagger}\tag{5}
\end{align*}\]
 行列 $A$ が正方行列であるときには、
 
\[\begin{align*}
&\det A^{\dagger}=(\det A)^{\dagger}\tag{6}\\[6pt]
&\mathrm{tr}(A^{\dagger})=(\mathrm{tr} A)^{\dagger}\tag{7}
\end{align*}\]
が成立します。行列 $A$ が正則であるときは、随伴行列 $A^{\dagger}$ も正則であって、
 
\[(A^{\dagger})^{-1}=(A^{-1})^{\dagger}\tag{8}\]
が成り立ちます。

# SLA_070-3

# 複素行列を定義
A = np.array([[1 + 2j, 1 - 1j],
              [2 + 3j, 2 + 1j]])

# Aの随伴行列の逆行列
B = linalg.inv(hermitian(A))

# Aの逆行列の随伴行列
C = hermitian(linalg.inv(A))

print("Aの随伴行列の逆行列:\n{}\n".format(B))
print("Aの逆行列の随伴行列:\n{}".format(C))
Aの随伴行列の逆行列:
[[-0.146+0.317j -0.049-0.561j]
 [ 0.22 +0.024j  0.073+0.341j]]

Aの逆行列の随伴行列:
[[-0.146+0.317j -0.049-0.561j]
 [ 0.22 +0.024j  0.073+0.341j]]

 

複素内積

 複素ベクトル $\boldsymbol{v},\ \boldsymbol{w}$ の内積 $(\boldsymbol{v},\boldsymbol{w})$ は $\boldsymbol{v}$ の共役転置ベクトルを使って
 
\[\begin{align*}
\boldsymbol{v}^{\dagger}\boldsymbol{w}
&=\begin{bmatrix}
\bar{v}_1&\bar{v}_2&\cdots&\bar{v}_n\end{bmatrix}
\begin{bmatrix}
w_1\\w_2\\\vdots\\w_n\end{bmatrix}\\[6pt]
&=\bar{v}_1w_1+\bar{v}_2w_2+\ \cdots\ +\bar{v}_nw_n\tag{9}
\end{align*}\]
と定義されます。たとえば、
 
\[\boldsymbol{v}=\begin{bmatrix}
1+i\\2-i\end{bmatrix},\quad
\boldsymbol{w}=\begin{bmatrix}
2+5i\\1-i\end{bmatrix}\tag{10}\]
であるとき、
 
\[\boldsymbol{v}^{\dagger}\boldsymbol{w}
=(1-i)(2+5i)+(2+i)(1-i)=10+2i\tag{11}\]
となります。計算間違いがあるといけないので、念のために Python でも確認しておきましょう。

# SLA_070-4

# 複素ベクトルを定義
v = np.array([1+1j, 2-1j])
w = np.array([2+5j, 1-1j])

# 内積(v,w)
vw = np.dot(hermitian(v), w)

print(vw)
(10+2j)

 実数ベクトルとは異なり、複素ベクトルの内積は順序によって値が変わります。

# SLA_070-5

# 内積(w,v)
wv = np.dot(hermitian(w), v)

print(wv)
(10-2j)

 実行結果を比較するとわかるように、$\boldsymbol{w}^{\dagger}\boldsymbol{v}$ は $\boldsymbol{v}^{\dagger}\boldsymbol{w}$ の複素共役となっています。これは任意のベクトル同士の内積でも成り立ちます。
 
\[(\boldsymbol{v},\boldsymbol{w})
=\overline{(\boldsymbol{w},\boldsymbol{v})}\tag{11}\]
 他にも内積について以下の定理が成立します。
 
\[\begin{align*}
&(\boldsymbol{u}+\boldsymbol{v},\boldsymbol{w})
=(\boldsymbol{u},\boldsymbol{w})
+(\boldsymbol{v},\boldsymbol{w})\tag{12}\\[6pt]
&(c\boldsymbol{v},\boldsymbol{w})
=c(\boldsymbol{v},\boldsymbol{w})
=(\boldsymbol{v},\bar{c}\boldsymbol{w})\tag{13}\\[6pt]
&(\boldsymbol{v},\boldsymbol{v})\geq 0\tag{14}\end{align*}\]
 また、$A\boldsymbol{v}$ と $\boldsymbol{w}$ の内積について、以下の重要な定理が成り立ちます。
 
\[(A\boldsymbol{v}, \boldsymbol{w})
=(\boldsymbol{v},A\boldsymbol{w})\tag{15}\]
 

複素空間におけるノルム

 複素ベクトル $\boldsymbol{v}$ の自身との内積 $\boldsymbol{v}^{\dagger}\boldsymbol{v}$ を $\mathbb{C}^n$ におけるノルムの $2$ 乗と定義します。
 
\[\parallel \boldsymbol{v} \parallel^2
=\boldsymbol{v}^{\dagger}\boldsymbol{v}
=\bar{v}_1v_1+\bar{v}_2v_2+\ \cdots\ +\bar{v}_nv_n\tag{16}\]
 $\bar{v}_kv_k$ は $v_k$ の絶対値の $2$ 乗 $|v_k|^2$ なので、
 
\[\parallel \boldsymbol{v} \parallel^2
=|v_1|^2+|v_2|^2+\ +\cdots\ +|v_n|^2\tag{17}\]
と表せます。もちろん上式は $\mathbb{R}^n$ におけるノルムの定義を包含しています。たとえば複素ベクトル
 
\[\boldsymbol{v}=\begin{bmatrix}
3+2i\\1-i\end{bmatrix}\tag{18}\]
のノルムは次のように計算できます:
 
\[\parallel \boldsymbol{v} \parallel
=\sqrt{9+4+1+1}=\sqrt{15}=3.87298...\tag{19}\]
 Python でも確認しておきます。
 scipy.linalg.norm() は複素ベクトルにも対応しています。

# SLA_070-6

# 複素ベクトルを定義
v = np.array([3+2j, 1-1j])

# vのノルムを計算
v_norm = linalg.norm(v)

print(v_norm)
3.872983346207417

[参考文献] Wikipedia, 『ストラング:線形代数イントロダクション』