NumPyで対称行列をマスター!生成・判定・活用法の完全ガイド

 

対称行列は線形代数、機械学習、科学計算において重要な役割を果たします。本記事では、NumPyを使った対称行列の生成方法、判定方法、そして実践的な活用例を詳しく解説します。初心者から上級者まで役立つ内容をお届けします。

対称行列とは?

対称行列は、転置行列が元の行列と等しい正方行列です。つまり、A = A.Tが成り立つ行列のことを指します。

数学的には:A[i,j] = A[j,i](すべてのi,jについて)

import numpy as np

# 対称行列の例
symmetric = np.array([[1, 2, 3],
                      [2, 4, 5],
                      [3, 5, 6]])
print("対称行列:")
print(symmetric)

1. 対称行列の判定方法

1-1. numpy.allclose()を使った基本的な判定

def is_symmetric(matrix):
    return np.allclose(matrix, matrix.T)

# テスト用行列
matrix1 = np.array([[1, 2], [2, 3]])  # 対称
matrix2 = np.array([[1, 2], [3, 4]])  # 非対称

print(is_symmetric(matrix1))  # True
print(is_symmetric(matrix2))  # False

1-2. 数値誤差を考慮した判定

def is_symmetric_robust(matrix, rtol=1e-5, atol=1e-8):
    """数値誤差に対してロバストな対称性判定"""
    return np.allclose(matrix, matrix.T, rtol=rtol, atol=atol)

# 浮動小数点誤差がある場合
noisy_symmetric = np.array([[1.0, 2.000001],
                           [2.000001, 3.0]])

print(is_symmetric_robust(noisy_symmetric))  # True

1-3. 正方行列チェック付きの判定

def is_symmetric_safe(matrix):
    """正方行列チェック付きの安全な対称性判定"""
    matrix = np.asarray(matrix)
    
    # 正方行列かチェック
    if matrix.shape[0] != matrix.shape[1]:
        return False
    
    return np.allclose(matrix, matrix.T)

# 非正方行列のテスト
rect_matrix = np.array([[1, 2, 3], [4, 5, 6]])
print(is_symmetric_safe(rect_matrix))  # False

2. 対称行列の生成方法

2-1. ランダム対称行列の生成

def generate_random_symmetric(n, seed=None):
    """nxnのランダム対称行列を生成"""
    if seed is not None:
        np.random.seed(seed)
    
    # ランダム行列を生成
    A = np.random.rand(n, n)
    # 対称化: (A + A.T) / 2
    return (A + A.T) / 2

# 4x4対称行列を生成
sym_matrix = generate_random_symmetric(4, seed=42)
print("ランダム対称行列:")
print(sym_matrix)
print("対称性確認:", is_symmetric(sym_matrix))

2-2. 正定値対称行列の生成

def generate_positive_definite(n, seed=None):
    """正定値対称行列を生成(A.T @ A形式)"""
    if seed is not None:
        np.random.seed(seed)
    
    A = np.random.rand(n, n)
    return A.T @ A

# 正定値対称行列を生成
pd_matrix = generate_positive_definite(3, seed=42)
print("正定値対称行列:")
print(pd_matrix)

# 固有値で正定値性確認
eigenvals = np.linalg.eigvals(pd_matrix)
print("固有値:", eigenvals)
print("すべて正:", np.all(eigenvals > 0))

2-3. 指定した固有値を持つ対称行列

def symmetric_from_eigenvalues(eigenvals, seed=None):
    """指定した固有値を持つ対称行列を生成"""
    if seed is not None:
        np.random.seed(seed)
    
    n = len(eigenvals)
    # ランダムな直交行列を生成
    Q, _ = np.linalg.qr(np.random.rand(n, n))
    D = np.diag(eigenvals)
    
    return Q @ D @ Q.T

# 固有値[5, 3, 1]を持つ対称行列
custom_sym = symmetric_from_eigenvalues([5, 3, 1], seed=42)
print("カスタム対称行列:")
print(custom_sym)

# 固有値確認
actual_eigenvals = np.linalg.eigvals(custom_sym)
print("実際の固有値:", np.sort(actual_eigenvals)[::-1])

2-4. 対称トープリッツ行列

def symmetric_toeplitz(first_row):
    """対称トープリッツ行列を生成"""
    from scipy.linalg import toeplitz
    return toeplitz(first_row)

# 対称トープリッツ行列
toep_sym = symmetric_toeplitz([1, 0.5, 0.25, 0.125])
print("対称トープリッツ行列:")
print(toep_sym)

3. 既存行列の対称化

3-1. 基本的な対称化

def symmetrize(matrix):
    """行列を対称化(上三角優先)"""
    return (matrix + matrix.T) / 2

# 非対称行列を対称化
asymmetric = np.array([[1, 2, 3],
                       [4, 5, 6],
                       [7, 8, 9]])

symmetric = symmetrize(asymmetric)
print("対称化後:")
print(symmetric)

3-2. 上三角または下三角優先の対称化

def symmetrize_upper(matrix):
    """上三角部分を使用して対称化"""
    return np.triu(matrix) + np.triu(matrix, 1).T

def symmetrize_lower(matrix):
    """下三角部分を使用して対称化"""
    return np.tril(matrix) + np.tril(matrix, -1).T

# テスト
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

sym_upper = symmetrize_upper(matrix)
sym_lower = symmetrize_lower(matrix)

print("上三角優先:")
print(sym_upper)
print("下三角優先:")
print(sym_lower)

4. 対称行列の性質と活用

4-1. 固有値分解の効率化

# 対称行列の固有値分解(より効率的)
sym_matrix = generate_random_symmetric(4, seed=42)

# 一般的な固有値分解
eigenvals1, eigenvecs1 = np.linalg.eig(sym_matrix)

# 対称行列専用(より高速・安定)
eigenvals2, eigenvecs2 = np.linalg.eigh(sym_matrix)

print("eig()の固有値:", eigenvals1)
print("eigh()の固有値:", eigenvals2)
print("結果の一致:", np.allclose(np.sort(eigenvals1), np.sort(eigenvals2)))

4-2. 二次形式の計算

def quadratic_form(A, x):
    """二次形式 x^T A x を計算"""
    return x.T @ A @ x

# 対称行列での二次形式
A = generate_positive_definite(3, seed=42)
x = np.array([1, 2, 3])

result = quadratic_form(A, x)
print(f"二次形式 x^T A x = {result}")

4-3. コレスキー分解

# 正定値対称行列のコレスキー分解
pd_matrix = generate_positive_definite(3, seed=42)

try:
    L = np.linalg.cholesky(pd_matrix)
    print("コレスキー分解 L:")
    print(L)
    
    # 復元確認
    reconstructed = L @ L.T
    print("復元確認:", np.allclose(pd_matrix, reconstructed))
    
except np.linalg.LinAlgError:
    print("行列が正定値ではありません")

5. 実践的な応用例

5-1. 共分散行列の生成

def generate_covariance_matrix(n, correlation_strength=0.3, seed=None):
    """相関を持つ共分散行列を生成"""
    if seed is not None:
        np.random.seed(seed)
    
    # 対角成分(分散)
    variances = np.random.uniform(0.5, 2.0, n)
    
    # 相関行列を生成
    corr = np.eye(n)
    for i in range(n):
        for j in range(i+1, n):
            corr_val = np.random.uniform(-correlation_strength, 
                                       correlation_strength)
            corr[i,j] = corr[j,i] = corr_val
    
    # 共分散行列 = D^(1/2) * R * D^(1/2)
    D_sqrt = np.diag(np.sqrt(variances))
    return D_sqrt @ corr @ D_sqrt

# 共分散行列を生成
cov_matrix = generate_covariance_matrix(4, seed=42)
print("共分散行列:")
print(cov_matrix)
print("対称性:", is_symmetric(cov_matrix))

5-2. カーネル行列の対称化

def rbf_kernel(X, gamma=1.0):
    """RBFカーネル行列を計算(対称性保証版)"""
    n = X.shape[0]
    K = np.zeros((n, n))
    
    for i in range(n):
        for j in range(i, n):  # 上三角のみ計算
            dist_sq = np.sum((X[i] - X[j]) ** 2)
            K[i,j] = np.exp(-gamma * dist_sq)
            if i != j:
                K[j,i] = K[i,j]  # 対称性を利用
    
    return K

# テストデータ
X = np.random.rand(5, 2)
K = rbf_kernel(X)
print("RBFカーネル行列の対称性:", is_symmetric(K))

6. パフォーマンス最適化

6-1. 大規模行列での効率的な判定

def is_symmetric_fast(matrix, sample_ratio=0.1):
    """大規模行列での高速対称性判定(サンプリング)"""
    n = matrix.shape[0]
    sample_size = max(int(n * sample_ratio), 100)
    
    # ランダムサンプリング
    indices = np.random.choice(n, min(sample_size, n), replace=False)
    
    for i in indices:
        for j in indices:
            if not np.isclose(matrix[i,j], matrix[j,i]):
                return False
    return True

# 大規模行列でのテスト
large_sym = generate_random_symmetric(1000, seed=42)
print("高速判定結果:", is_symmetric_fast(large_sym))

6-2. メモリ効率的な対称行列保存

def save_symmetric_compressed(matrix, filename):
    """対称行列の上三角部分のみを保存"""
    upper_indices = np.triu_indices(matrix.shape[0])
    upper_values = matrix[upper_indices]
    
    np.savez_compressed(filename, 
                       values=upper_values,
                       shape=matrix.shape[0])

def load_symmetric_compressed(filename):
    """圧縮保存された対称行列を復元"""
    data = np.load(filename)
    n = data['shape']
    values = data['values']
    
    matrix = np.zeros((n, n))
    upper_indices = np.triu_indices(n)
    matrix[upper_indices] = values
    matrix = matrix + matrix.T - np.diag(np.diag(matrix))
    
    return matrix

# 使用例
sym_matrix = generate_random_symmetric(100, seed=42)
save_symmetric_compressed(sym_matrix, 'symmetric_matrix.npz')
loaded_matrix = load_symmetric_compressed('symmetric_matrix.npz')
print("復元確認:", np.allclose(sym_matrix, loaded_matrix))

7. よくあるエラーと対処法

7-1. 数値誤差による判定失敗

# 計算による数値誤差の例
A = np.random.rand(3, 3)
symmetric_calc = A @ A.T  # 理論上は対称

print("厳密判定:", np.array_equal(symmetric_calc, symmetric_calc.T))
print("誤差考慮判定:", np.allclose(symmetric_calc, symmetric_calc.T))

7-2. 非正方行列エラーの対処

def safe_symmetric_operation(matrix, operation='check'):
    """安全な対称行列操作"""
    matrix = np.asarray(matrix)
    
    if len(matrix.shape) != 2:
        raise ValueError("2次元配列である必要があります")
    
    if matrix.shape[0] != matrix.shape[1]:
        raise ValueError("正方行列である必要があります")
    
    if operation == 'check':
        return is_symmetric(matrix)
    elif operation == 'symmetrize':
        return symmetrize(matrix)

# 使用例
try:
    result = safe_symmetric_operation([[1, 2], [3, 4, 5]], 'check')
except ValueError as e:
    print(f"エラー: {e}")

まとめ

NumPyを使った対称行列の操作をマスターすることで、線形代数計算が大幅に効率化されます。

重要なポイント:

  • np.allclose(A, A.T)で数値誤差を考慮した判定
  • (A + A.T) / 2で簡単に対称化
  • np.linalg.eigh()で対称行列専用の高速固有値分解
  • 正定値対称行列はA.T @ Aで生成可能
  • 大規模行列では上三角保存でメモリ節約

これらの技術を活用して、機械学習や科学計算における対称行列を効果的に扱いましょう。

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座