『Python数値計算ノート』ではアフィリエイトプログラムを利用して商品を紹介しています。

転置行列と実対称行列

【Pythonで学ぶ線形代数学講座(20)】転置行列と実対称行列

転置行列

$m\times n$ の行列
 \[A=\begin{bmatrix}a_{11}&a_{12} &\cdots &a_{1n}\\a_{21}&a_{22} &\cdots &a_{2n}\\\vdots & \vdots &\ddots &\vdots\\a_{m1}&a_{m2} &\cdots &a_{mn}\end{bmatrix}\]
について、$a_{ij}$ と $a_{ji}$ の位置を入れ替えた行列、すなわち主対角線 (左上から右下に伸びる対角線) で成分を折り返した行列
 \[\begin{bmatrix}a_{11}&a_{21} &\cdots &a_{m1}\\a_{12}&a_{22} &\cdots &a_{m2}\\\vdots & \vdots &\ddots &\vdots\\a_{1n}&a_{2n} &\cdots &a_{mn}\end{bmatrix}\]
を $A$ の 転置行列 (transpose) と定義し、$^tA$ または $A^T$ のように表します。また、行列 $A$ の転置行列を得ることを「$A$ を転置する」と言います。
 
Python 転置行列 (transpose)
NumPy で適当な行列を作って、転置してみましょう。ndarray の転置行列は T 属性で参照できます。

# python transpose

# In[1]

# ランダムな3×3行列を生成
rng = np.random.default_rng(117)
matrix = rng.integers(0, 10, (3, 3))

print(matrix)
# [[1 8 2]
#  [1 1 6]
#  [8 3 6]]
# In[2]

# 行列を転置
print(matrix_a.T)

# [[1 1 8]
#  [8 1 3]
#  [2 6 6]]

転置行列の性質

行列 $A$ の転置は線形操作です。
すなわち、任意の行列 $A,\ B$ とスカラー$k$ について
 \[\begin{align*}&(A+B)^T=A^T+B^T\tag{1}\\[6pt]&(kA)^T=k(A^T)\tag{2}\end{align*}\]
が成り立ちます。$A$ の転置行列を再び転置すると $A$ を得ます。
 \[(A^T)^T=A\tag{3}\]
行列積 $(AB)$ の転置は
 \[(AB)^T=B^TA^T\tag{4}\]
で与えられます。

# In[3]

matrix_b = rng.integers(0, 10, (3, 3))

print(matrix_b)
# [[5 1 3]
#  [9 6 2]
#  [3 3 1]]
# In[4]

# ABの転置
print((matrix_a @ matrix_b).T)

# [[83 32 85]
#  [55 25 44]
#  [21 11 36]]
# In[5]

# (Bの転置)(Aの転置)
print(matrix_b.T @ matrix_a.T)

# [[83 32 85]
#  [55 25 44]
#  [21 11 36]]

行列式は行列の転置によって不変です。つまり、$A$ の行列式 $\mathrm{det}A$ について、
 \[\mathrm{det}A=\mathrm{det}(A^T)\tag{5}\]
が成り立ちます。

# In[6]

# Aの行列式
det_a = np.linalg.det(matrix_a)
det_at = np.linalg.det(matrix_a.T)

print(f"Aの行列式: {np.round(det_a, 2)}")
print(f"Aの転置行列の行列式: {np.round(det_at, 2)}")

# Aの行列式: 314.0
# Aの転置行列の行列式: 314.0

逆行列の転置は転置行列の逆行列です:
 \[(A^{-1})^T=(A^T)^{-1}\tag{6}\]
$A$ が $m\times n$ の行列であるとき、$AA^T$ は $m\times m$, $A^TA$ は $n\times n$ の正方対称行列となります (対称行列については記事の後半を参照してください)。たとえば、$A$ が $2\times 3$ の矩形行列
 \[A=\begin{bmatrix}a&b&c\\d&e&f\end{bmatrix}\tag{7}\]
であるとき、$AA^T$ は $2\times 2$ の正方対称行列です。
 \[AA^T=\begin{bmatrix}a&b&c\\d&e&f\end{bmatrix}\begin{bmatrix}a&d\\b&e\\c&f\end{bmatrix}=\begin{bmatrix}a^2+b^2+c^2&ad+be+cf\\ad+be+cf&d^2+c^2+f^2\end{bmatrix}\tag{8}\]
$A^TA$ は $3\times 3$ 正方対称行列です。
 \[A^TA=\begin{bmatrix}a^2+d^2&ab+de&ac+df\\ab+de&b^2+e^2&bc+ef\\ac+df&bc+ef&c^2+f^2\end{bmatrix}\tag{9}\]

ndarray.T

NumPy で配列 (ndarray) を生成すると、自動的に転置配列もデータ属性として付与されます。転置配列は ndarray.T で参照します。

# ndarray_transpose

# In[1]

import numpy as np

# 乱数を初期化
np.random.seed(11)

# 要素を無作為に生成して4×4行列を定義
# [[1 2 8 2]
#  [8 3 9 1]
#  [1 5 3 2]
#  [6 6 8 5]]
a = np.random.randint(1, 10, (4, 4))

# aの転置行列を表示
print(a.T)

# [[1 8 1 6]
#  [2 3 5 6]
#  [8 9 3 8]
#  [2 1 2 5]]

numpy.transpose()

numpy.transpose() に行列 a を渡すと a の転置行列を返します。

# numpy_transpose

# In[1]

import numpy as np

# 乱数を初期化
np.random.seed(12)

# 要素を無作為に生成して3×5行列を定義
# [[7 2 3 4 4]
#  [1 7 2 5 6]
#  [3 7 1 6 9]]
a = np.random.randint(1, 10, (3, 5))

# aの転置行列
at = np.transpose(a)

print(at)

# [[7 1 3]
#  [2 7 7]
#  [3 2 1]
#  [4 5 6]
#  [4 6 9]]

実対称行列

転置によって自身を変えない正方行列、すなわち
 \[A^T=A\tag{10}\]
を満たす行列 $A$ を実対称行列(symmetric matrix)といいます。対称行列は主対角線に関して折り返すと重なるような行列です。たとえば、$3$ 次対称行列は
 \[\begin{bmatrix}d_1&a&b\\a&d_2&c\\b&c&d_3\end{bmatrix}\]
のように表せます。一般に対称行列の成分は $a_{ij}=a_{ji}$ という関係を満たしています。
 
以下は任意サイズの整数要素をもつ対称行列を生成する関数です。

# In[1]

# 対称正方行列自動生成関数
def symmetric_matrix_geterator(low, high, size, seed=0):
    rng = np.random.default_rng(seed)
    matrix = np.diag(rng.integers(low, high, size))
    for i in range(size):
        for j in range(size):
            if i != j:
                matrix[i][j] = matrix[j][i] = rng.integers(low=low, high=high)
    return matrix

引数 low と high には、それぞれ行列要素の下限値と上限値を指定します(ただし、high から 1 を引いた値が実際の上限値となります)。size は行列の大きさ、seed は乱数を固定するためのシード値です。3 × 3 の対称行列を作成してみましょう。

# In[2]

# 0~9の整数成分をもつ3×3の対称行列
matrix = symmetric_matrix_geterator(0, 10, 3, seed=1)

print(matrix)
# [[4 1 9]
#  [1 5 2]
#  [9 2 7]]

転置しても同じ行列になることを確認してみましょう。

# In[3]

# 対称行列は転置操作に対して不変であることを確認
print(matrix.T)

# [[4 1 9]
#  [1 5 2]
#  [9 2 7]]

対称行列の逆行列

対称行列の逆行列を転置すると
 \[(A^{-1})^T=(A^T)^{-1}=A^{-1}\tag{11}\]
となるので、対称行列の逆行列もまた対称行列です。先ほど生成した対称行列の逆行列を求めて、それが対称行列となっていることを確認してみます。

# In[4]

np.set_printoptions(precision=3)

# 対称行列の逆行列もまた対称行列であることを確認
matrix_inv = np.linalg.inv(matrix)

print(matrix_inv)
# [[-0.123 -0.044  0.171]
#  [-0.044  0.21  -0.004]
#  [ 0.171 -0.004 -0.075]]

任意の行列 $X$ について、
 \[(X^TX)^T=X^T(X^T)^T=X^TX\tag{12}\]
が成り立つので、$X^TX$ および $XX^T$ は対称行列となります。$X$ は正方行列でなくても構いません。たとえば
 \[X=\begin{bmatrix}a&b&c\\d&e&f\end{bmatrix}\tag{13}\]
とした場合、$XX^T$ と $X^TX$ は異なるサイズの行列ですが、どちらも対称行列となります。
 \[XX^T=\begin{bmatrix}a^2+b^2+c^2&ad+be+cf\\ad+be+cf&a^2+b^2+c^2\end{bmatrix}\tag{14}\]
\[X^TX=\begin{bmatrix}a^2+d^2&ab+de&ac+df\\ab+de&b^2+e^2&bc+ef\\ac+df&bc+ef&c^2+f^2\end{bmatrix}\tag{15}\]
以下のコードで具体的な例を計算させているので、確かめてみてください。

# In[5]

# 2×3サイズの行列を生成
rng = np.random.default_rng(0)
x = rng.integers(0, 10, size=(2, 3))

print(x)
# [[8 6 5]
#  [2 3 0]]
# In[6]

# X(X^T)を計算
xxt = x @ x.T

print(xxt)
# [[125  34]
#  [ 34  13]]
# In[7]

# (X^T)Xを計算
xtx= matrix.T @ matrix

print(xtx)
# [[ 98  27 101]
#  [ 27  30  33]
#  [101  33 134]]

講座の後半で、対称行列を複素数範囲まで拡張したエルミート行列について学びます。エルミート行列は対称行列を完全に包含する形で定義されるので、(固有ベクトルが直交するなどの)エルミート行列の性質は、そのまま対称行列に当てはまります。ですから、対称行列の重要な性質は「エルミート行列」でまとめて説明することにします。

 

コメント

  1. HNaito より:

    下記は誤植と思われますので、ご確認ください。
    転置行列の定義の最右下の要素で、a_nm → a_mn
    (1)式で、A+B^T → (A+B)^T
    (10)式で、A^T=T → A^T=A
    (10)式の下の行で、主対線 → 主対角線

    • あとりえこばと より:

      ありがとうございます。
      本当に助かります。
      ご指摘いただいた箇所はすべて訂正しておきました。
      また何か見つけたらよろしくお願いします m(_ _)m