import numpy as np
from matplotlib import pyplot as plt
def jacobi(A, b, tolerance):
''' 連立方程式をヤコビ法(反復法)で解く '''
# 初期化(適当な解と残差)
x0 = np.random.random(len(b))
residual = 1e20
# A = D + R
D = np.diag(A)
R = A - np.diag(D)
# 反復計算=>残差がトレランスより小さくなったら終了
i = 0
print('----------Start iteration----------')
res = []
while residual > tolerance:
# 解と残差を計算
x = (b - (R @ x0)) / D
residual = np.sum(np.sqrt((x - x0) ** 2)) / np.sum(np.sqrt(x ** 2))
res.append(residual)
if i % 100 == 0:
print('Iteration=', i)
print('Residual=', residual)
print('x=', x)
i += 1
x0 = x
print('----------End iteration----------')
print('Iteration=', i)
print('Residual=', residual)
print('x=', x, '\n')
return x, res
def gauss_seidel(A, b, tolerance):
''' 連立方程式をガウス・ザイデル法(反復法)で解く '''
# 検証のために乱数シードを固定
np.random.seed(314)
# 係数行列を分解:D=対角成分ベクトル、R=剰余行列
D = np.diag(A)
R = A - np.diag(D)
# 初期化(適当な解と残差)
x = np.random.random(len(b))
residual = 1e20
# 反復計算(逐次求めた解を次のループで使うため、forを使っている)
iteration = 0
print('----------Start iteration----------')
res = []
while residual > tolerance:
# 収束判定用の1ステップ前の解を保持
x0 = x.copy()
# 逐次計算
for i in range(len(x)):
x[i] = (b[i] - R[i] @ x) / D[i]
# 収束判定
residual = np.sum(np.sqrt((x - x0) ** 2)) / np.sum(np.sqrt(x ** 2))
res.append(residual)
if iteration % 100 == 0:
print('Iteration=', iteration)
print('Residual=', residual)
print('x=', x)
iteration += 1
print('----------End iteration----------')
print('Iteration=', iteration)
print('Residual=', residual)
print('x=', x, '\n')
return x, res
def sor(A, b, omega, tolerance):
''' 連立方程式をSOR法(反復法)で解く '''
# 検証のために乱数シードを固定
np.random.seed(314)
# 係数行列を分解:D=対角成分ベクトル、R=剰余行列
D = np.diag(A)
R = A - np.diag(D)
# 初期化(適当な解と残差)
x = np.random.random(len(b))
residual = 1e20
# 反復計算(逐次求めた解を次のループで使うため、forを使っている)
iteration = 0
print('----------Start iteration----------')
res = []
while residual > tolerance:
# 収束判定用の1ステップ前の解を保持
x0 = x.copy()
# 逐次計算
for i in range(len(x)):
x[i] = (1 - omega) * x0[i] + (omega * (b[i] - R[i] @ x) / D[i])
# 収束判定
residual = np.sum(np.sqrt((x - x0) ** 2)) / np.sum(np.sqrt(x ** 2))
res.append(residual)
if iteration % 100 == 0:
print('Iteration=', iteration)
print('Residual=', residual)
print('x=', x)
iteration += 1
print('----------End iteration----------')
print('Iteration=', iteration)
print('Residual=', residual)
print('x=', x, '\n')
return x, res
def plot(r_jacobi, r_gauss_seidel, r_sor_i, w):
''' Residualの変化をプロットする '''
# フォントの種類とサイズを設定する。
plt.rcParams['font.size'] = 14
plt.rcParams['font.family'] = 'Times New Roman'
# 目盛を内側にする。
plt.rcParams['xtick.direction'] = 'in'
plt.rcParams['ytick.direction'] = 'in'
# グラフの上下左右に目盛線を付ける。
fig = plt.figure(figsize=(8, 5))
ax1 = fig.add_subplot(111)
ax1.yaxis.set_ticks_position('both')
ax1.xaxis.set_ticks_position('both')
# 軸のラベルを設定する。
ax1.set_xlabel('Step')
ax1.set_ylabel('Residual')
# スケールの設定をする。
ax1.set_xlim(0, 60)
ax1.set_yscale('log')
# プロットを行う。
x1 = np.arange(0, len(r_jacobi), 1)
x2 = np.arange(0, len(r_gauss_seidel), 1)
ax1.plot(x1, r_jacobi, label='Jacobi', lw=4, color='red')
ax1.plot(x2, r_gauss_seidel, label='Gauss-Seidel', lw=4, color='black')
for i in range(len(r_sor_i)):
label = str(w[i])
x = np.arange(0, len(r_sor_i[i]), 1)
if i == 0:
ax1.plot(x, r_sor_i[i], label='SOR =' + label, lw=1, color='yellow')
elif i == len(w) - 1:
ax1.plot(x, r_sor_i[i], label='SOR =' + label, lw=1, linestyle='--')
else:
ax1.plot(x, r_sor_i[i], label='SOR =' + label, lw=1)
ax1.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0)
# レイアウト設定
fig.tight_layout()
# グラフを表示する。
plt.show()
plt.close()
return
def optimal_acc_param(A):
''' ヤコビ法のスペクトル半径から最適な緩和係数を計算する '''
# ヤコビ法の反復行列
D = np.diag(np.diag(A))
R = A - D
M = - np.linalg.inv(D) @ R
# スペクトル半径
eig, v = np.linalg.eig(M)
rho = np.abs(np.max(eig))
# 最適な緩和係数
omega_opt = np.round(2 / (1 + np.sqrt(1 - rho ** 2)), 3)
return omega_opt
if __name__ == '__main__':
# 係数行列と定数ベクトル
A = np.array([[2, 1, 1],
[2, 3, 1],
[1, 1, 3]])
b = np.array([2, 4, -1])
# ヤコビ法で解を反復計算
x_jacobi, res1 = jacobi(A, b, 1e-8)
print('Jacobi method x=', x_jacobi, 'Validated b=', A @ x_jacobi)
# ガウス・ザイデル法で解を反復計算
x_gs, res2 = gauss_seidel(A, b, 1e-8)
print('Gauss-Seidel method x=', x_gs, 'Validated b=', A @ x_gs)
# SOR法で解を反復計算
omega_opt = optimal_acc_param(A)
print('optimal acc. param.=', omega_opt)
omega = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0]
omega.append(omega_opt)
res = []
for i in range(len(omega)):
x_sor, res_i = sor(A, b, omega[i], 1e-8)
res.append(res_i)
print('SOR method x=', x_sor, 'Validated b=', A @ x_sor)
# プロット
plot(res1, res2, res, omega)