対称行列とは?意味・性質と例、NumPyでの判定・作り方とeighによる固有値

対称行列とは、転置しても元の行列と同じになる正方行列(Aᵀ=A)のことで、対角線をはさんで向かい合う成分がすべて等しい(aᵢⱼ=aⱼᵢ)行列です。NumPyでは np.allclose(A, A.T) で判定でき、固有値は np.linalg.eigh() で求めます。

この記事では、対称行列の意味と例、覚えておきたい性質を先に整理し、そのあとでNumPyを使った判定・作り方・固有値分解のコードを実行結果つきで紹介します。コードはPython 3.12、NumPy 2.5.4、SciPy 1.18.1で実行しました(2026年10月15日確認)。

対称行列とは

n行n列の正方行列Aの(i, j)成分をaᵢⱼと書くとき、すべてのi, jについて aᵢⱼ=aⱼᵢ が成り立つ行列を対称行列といいます。行と列を入れ替える操作(転置)をしても変わらないので、式では Aᵀ=A と書きます。

見た目でいうと、左上から右下への対角線(主対角線)を鏡にして、左下と右上が鏡写しになっている行列です。

例 対称か 理由
[[1, 2, 3], [2, 4, 5], [3, 5, 6]] 対称 a₁₂=a₂₁=2、a₁₃=a₃₁=3、a₂₃=a₃₂=5
[[1, 2], [3, 4]] 対称でない a₁₂=2 と a₂₁=3 が違う
単位行列・対角行列 対称 対角線の外がすべて0なので必ず等しい
2行3列の行列 対称でない 正方行列でないと、転置で形が変わる

対角成分(a₁₁、a₂₂、…)には条件がなく、どんな値でもかまいません。条件がかかるのは対角線の外側だけです。

交代行列(歪対称行列)との違い

Aᵀ=−A になる行列は交代行列(歪対称行列)と呼ばれます。こちらは向かい合う成分の符号が逆で、対角成分は必ず0です。そして、どんな正方行列Aも「対称行列+交代行列」に一通りに分けられます。

A = (A+Aᵀ)/2 + (A−Aᵀ)/2

前半が対称部分、後半が交代部分です。後で紹介する「非対称な行列を対称化する」操作は、この前半だけを取り出していることになります。

対称行列の主な性質

性質 内容
和とスカラー倍 対称行列どうしの和、対称行列の定数倍は対称行列
積 対称行列どうしの積は、一般には対称にならない(AB=BAのときだけ対称)
AᵀA と AAᵀ どんな形の行列Aでも、AᵀA と AAᵀ は対称行列になる
固有値 実数の対称行列の固有値はすべて実数
固有ベクトル 互いに直交するように選べるので、直交行列Vを使って A=VΛVᵀ と対角化できる
逆行列 逆行列が存在すれば、それも対称行列

「固有値が実数」「直交行列で対角化できる」という2つの性質は、統計や機械学習で対称行列がよく出てくる理由そのものです。共分散行列や、主成分分析(PCA)で固有値分解する行列はどれも対称行列です。

NumPyで対称行列かどうかを判定する

整数の行列なら array_equal で十分

import numpy as np
S = np.array([[1, 2, 3],
              [2, 4, 5],
              [3, 5, 6]])
N = np.array([[1, 2],
              [3, 4]])
print(np.array_equal(S, S.T))
print(np.array_equal(N, N.T))
True
False

計算で作った行列は allclose を使う

浮動小数点の計算を経た行列は、理論上は対称でも、ごく小さな誤差でぴったり一致しないことがあります。次の例では、対称行列Bを使って A B Aᵀ を計算しています。数学的には必ず対称になる式です。

import numpy as np
rng = np.random.default_rng(0)
A = rng.random((4, 4))
B = rng.random((4, 4))
B = B + B.T              # Bは対称
G = A @ B @ A.T          # 理論上は対称
print(np.array_equal(G, G.T))
print(np.allclose(G, G.T))
print(np.abs(G - G.T).max())
False
True
8.881784197001252e-16

差は10の−16乗ほどですが、array_equal(完全一致)では「対称でない」と判定されてしまいます。計算結果を判定するときは、許容誤差つきで比べるnp.allclose()を使ってください。

SciPyの issymmetric を使う

SciPyには対称かどうかを判定する専用の関数scipy.linalg.issymmetricがあり、正方の2次元配列が対称かを返します(scipy.linalg.issymmetric)。既定では完全一致で判定し、atolやrtolを渡すと誤差を許容します。

import numpy as np
from scipy.linalg import issymmetric
S = np.array([[1.0, 2.0], [2.0, 3.0]])
print(issymmetric(S))
T = S.copy(); T[0, 1] += 1e-12
print(issymmetric(T))
print(issymmetric(T, atol=1e-10))
True
False
True

対称行列を作る

非対称な行列から対称部分を取り出す

先ほどの (A+Aᵀ)/2 を計算すると、任意の正方行列から対称行列が作れます。上三角の値をそのまま下三角にコピーしたいときはnp.triu()を使います。

import numpy as np
A = np.array([[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9]])
sym = (A + A.T) / 2      # 対称部分
skew = (A - A.T) / 2     # 交代(歪対称)部分
print(sym)
print(skew)
print(np.array_equal(sym + skew, A))
print(np.triu(A) + np.triu(A, 1).T)   # 上三角をコピーして対称に
[[1. 3. 5.]
 [3. 5. 7.]
 [5. 7. 9.]]
[[ 0. -1. -2.]
 [ 1.  0. -1.]
 [ 2.  1.  0.]]
True
[[1 2 3]
 [2 5 6]
 [3 6 9]]

交代部分の対角成分がすべて0になっていることも確認できます。np.triu()のk引数の意味は「NumPyのtriu・tril・triの使い方」で解説しています。

AᵀA で作る(積の対称性も確認)

import numpy as np
A = np.array([[1, 2], [2, 3]])
B = np.array([[0, 1], [1, 0]])
print(A + B)
print(A @ B)
print(np.array_equal(A @ B, (A @ B).T))
M = np.array([[1, 2, 3], [4, 5, 6]])
print(M.T @ M)
print(M @ M.T)
[[1 3]
 [3 3]]
[[2 1]
 [3 2]]
False
[[17 22 27]
 [22 29 36]
 [27 36 45]]
[[14 32]
 [32 77]]

対称行列AとBの和は対称ですが、積A @ Bは対称になっていません。一方、2行3列の長方形の行列Mからでも、Mᵀ M(3行3列)と M Mᵀ(2行2列)はどちらも対称行列になります。

対称行列の固有値は eigh で求める

対称行列の固有値・固有ベクトルは、汎用のnp.linalg.eig()ではなくnp.linalg.eigh()で求めるのが基本です。NumPyの公式ドキュメントでは、eighはエルミート行列または実対称行列のための関数で、固有値を昇順で返すと説明されています(numpy.linalg.eigh、2026年10月15日確認)。

import numpy as np
S = np.array([[2.0, 1.0],
              [1.0, 2.0]])
w, V = np.linalg.eigh(S)
print(w)
print(V)
print(np.allclose(V.T @ V, np.eye(2)))         # 固有ベクトルは直交
print(np.allclose(V @ np.diag(w) @ V.T, S))    # S = V Λ V^T
[1. 3.]
[[-0.70710678  0.70710678]
 [ 0.70710678  0.70710678]]
True
True

固有値は昇順に1と3、固有ベクトルを並べた行列Vは直交行列(VᵀV=I)で、S=VΛVᵀ が成り立っています。前の節の性質表がそのまま確認できます。

対称でない行列では、固有値が実数にならないことがあります。90度回転の行列と、よく似た形の対称行列を比べます。

import numpy as np
R = np.array([[0.0, -1.0],
              [1.0,  0.0]])   # 90度回転(対称ではない)
print(np.linalg.eigvals(R))
S = np.array([[0.0, 1.0],
              [1.0, 0.0]])    # 対称行列
print(np.linalg.eigvals(S))
[0.+1.j 0.-1.j]
[ 1.+0.j -1.+0.j]

注意:eigh は対称かどうかをチェックしない

eighは入力が対称であることを前提にしていて、既定では下三角部分だけを使って計算します(引数UPLO='L')。対称でない行列を渡してもエラーにならず、別の行列の答えが返ってきます。

import numpy as np
N = np.array([[2.0, 5.0],
              [1.0, 2.0]])     # 対称ではない
print(np.linalg.eigh(N).eigenvalues)
print(np.linalg.eigvals(N))
[1. 3.]
[ 4.23606798+0.j -0.23606798+0.j]

eighは右上の5を無視し、[[2, 1], [1, 2]] として計算しています。eighを使う前に、対称であることをnp.allclose(A, A.T)で確かめておくと安全です。

正定値対称行列とコレスキー分解

対称行列のうち、固有値がすべて正のものを正定値行列といいます。どんな0でないベクトルxに対しても xᵀAx>0 になる行列です。正定値かどうかは、np.linalg.eigvalsh()で固有値の符号を見るか、コレスキー分解(A=LLᵀ)が成功するかで確かめられます。

import numpy as np
S = np.array([[2.0, 1.0], [1.0, 2.0]])
x = np.array([1.0, -1.0])
print(x @ S @ x)
print(np.linalg.eigvalsh(S))
P = np.array([[1.0, 2.0], [2.0, 1.0]])
print(np.linalg.eigvalsh(P))
try:
    np.linalg.cholesky(P)
except np.linalg.LinAlgError as e:
    print("LinAlgError:", e)
2.0
[1. 3.]
[-1.  3.]
LinAlgError: Matrix is not positive definite

Pは対称行列ですが、固有値に−1があるため正定値ではなく、コレスキー分解は失敗します。「対称」と「正定値」は別の条件なので、混同しないようにしましょう。

データ分析で出てくる対称行列:共分散行列

身長と体重のような複数の変数の共分散行列は、変数Aと変数Bの共分散と、BとAの共分散が同じなので、必ず対称行列になります。

import numpy as np
data = np.array([[170, 65], [160, 55], [180, 75], [175, 68]], dtype=float)
C = np.cov(data, rowvar=False)
print(C)
print(np.allclose(C, C.T))
print(np.linalg.eigvalsh(C))
L = np.linalg.cholesky(C)
print(np.allclose(L @ L.T, C))
[[72.91666667 70.41666667]
 [70.41666667 68.91666667]]
True
[  0.47160336 141.36172997]
True

この共分散行列は固有値が2つとも正なので正定値で、コレスキー分解もできます。大きいほうの固有値(約141.4)に対応する固有ベクトルの方向が、データがもっとも大きく散らばっている方向で、これが主成分分析の第1主成分にあたります。

共分散行列やPCAを機械学習の中で使いこなしたい人には、テックジムのAIエンジニア養成コース(16章で機械学習とディープラーニングを学ぶコース)の第1章「教師なし学習と自然言語処理入門」でPCAを、第6章「AIのための統計学入門」で確率分布などの統計の基礎を学べます。

よくある質問

Q. 対称行列とは何ですか?

転置しても元と同じになる正方行列(Aᵀ=A)のことです。対角線をはさんで向かい合う成分がすべて等しく、aᵢⱼ=aⱼᵢ が成り立ちます。対角成分には条件がありません。

Q. 対称行列の固有値はなぜ実数になるのですか?

実数の対称行列はエルミート行列の特別な場合で、エルミート行列の固有値はすべて実数になることが証明されています。さらに固有ベクトルを互いに直交するように選べるため、直交行列で対角化できます。

Q. NumPyで対称行列かどうかを判定するには?

np.allclose(A, A.T) を使います。整数だけの行列なら np.array_equal(A, A.T) でもかまいませんが、計算で作った行列は誤差でぴったり一致しないことがあるため、許容誤差つきの allclose が安全です。SciPyの scipy.linalg.issymmetric も使えます。

Q. eig と eigh はどう使い分けますか?

対称行列(またはエルミート行列)には eigh を使います。固有値が実数の昇順で返り、固有ベクトルは直交します。ただし eigh は対称かどうかをチェックせず下三角だけで計算するため、対称でない行列には eig を使います。

Q. 対称行列どうしの積は対称行列になりますか?

一般にはなりません。AとBが対称でも、ABが対称になるのは AB=BA が成り立つ場合だけです。和や定数倍は必ず対称行列になります。

まとめ

対称行列は Aᵀ=A を満たす正方行列で、固有値がすべて実数になり、直交行列で対角化できるという扱いやすい性質を持っています。NumPyでは判定にnp.allclose(A, A.T)、対称化に(A + A.T) / 2、固有値分解にnp.linalg.eigh()を使うのが基本です。eighは対称性をチェックしないので、渡す前に判定しておくことを忘れないでください。行列の積や逆行列など、行列計算全般の書き方は「Pythonで行列計算|NumPyの積・転置・逆行列とエラーの直し方」にまとめています。


NumPy|行列・線形代数の関連記事

行列演算やブロードキャストなど、線形代数まわりの記事です。

NumPy全体の使い方は「NumPyの使い方」、「NumPy関数一覧」でまとめています。

プログラミング学習に興味がある方は、まずは無料体験会へ

無料体験会・お申し込みはこちら