cms.diff_order のソースコード

import csv
import numpy as np
from math import exp
from matplotlib import pyplot as plt

"""
数値微分における異なる近似法の誤差を評価するスクリプトです。

このスクリプトは、指数関数 `exp(x)` の導関数を、様々なステップサイズ `h` と異なる数値微分近似法(2点前方、2点後方、3点中心、5点中心、7点中心差分)を用いて計算します。
計算された数値微分結果とその解析解 `exp(x)` との誤差を比較し、その結果をコンソール、CSVファイル、およびMatplotlibグラフに出力します。

関連リンク: :doc:`diff_order_usage`
"""


#===================
# parameters
#===================
outfile = 'diff_order.csv'
h  = 0.5
kh = 0.8
nh = 101
x0 = 1.0


[ドキュメント] def func(x): """ 微分対象となる関数を定義します。 この関数は、指数関数 `exp(x)` を返します。 :param x: float - 関数の評価点。 :returns: float - `exp(x)` の計算結果。 """ return exp(x)
[ドキュメント] def dfdx(x): """ `func(x)` の解析的な導関数を定義します。 この関数は、`exp(x)` の導関数である `exp(x)` を返します。 :param x: float - 導関数の評価点。 :returns: float - `exp(x)` の導関数の計算結果。 """ return exp(x)
[ドキュメント] def diff2forward(func, x, h): """ 2点前方差分法を用いて関数の数値を微分します。 この方法は、`f'(x) = (f(x+h) - f(x)) / h` という公式に基づいています。 :param func: callable - 微分対象の関数。 :param x: float - 微分を評価する点。 :param h: float - 微小なステップサイズ。 :returns: float - `x` における関数の数値微分値。 """ return (func(x+h) - func(x)) / h;
[ドキュメント] def diff2backward(func, x, h): """ 2点後方差分法を用いて関数の数値を微分します。 この方法は、`f'(x) = (f(x) - f(x-h)) / h` という公式に基づいています。 :param func: callable - 微分対象の関数。 :param x: float - 微分を評価する点。 :param h: float - 微小なステップサイズ。 :returns: float - `x` における関数の数値微分値。 """ return (func(x) - func(x-h)) / h;
[ドキュメント] def diff3(func, x, h): """ 3点中心差分法を用いて関数の数値を微分します。 この方法は、`f'(x) = (f(x+h) - f(x-h)) / (2h)` という公式に基づいています。 2点差分法よりも高精度な近似を提供します。 :param func: callable - 微分対象の関数。 :param x: float - 微分を評価する点。 :param h: float - 微小なステップサイズ。 :returns: float - `x` における関数の数値微分値。 """ return (func(x+h) - func(x-h)) / 2.0 / h;
[ドキュメント] def diff5(func, x, h): """ 5点中心差分法を用いて関数の数値を微分します。 この方法は、より広範囲の点を使用することで、3点中心差分法よりもさらに高精度な近似を提供します。 :param func: callable - 微分対象の関数。 :param x: float - 微分を評価する点。 :param h: float - 微小なステップサイズ。 :returns: float - `x` における関数の数値微分値。 """ return (-func(x+2.0*h) + 8.0*func(x+h) - 8.0*func(x-h) + func(x-2.0*h)) / 12.0 / h;
[ドキュメント] def diff7(func, x, h): """ 7点中心差分法を用いて関数の数値を微分します。 この方法は、さらに多くの点を使用することで、非常に高精度な近似を提供します。 :param func: callable - 微分対象の関数。 :param x: float - 微分を評価する点。 :param h: float - 微小なステップサイズ。 :returns: float - `x` における関数の数値微分値。 """ return (func(x+3.0*h) - 9.0*func(x+2.0*h) + 45.0*func(x+h) - 45.0*func(x-h) + 9.0*func(x-2.0*h) - func(x-3.0*h)) / 60.0 / h;
#=================== # main routine #===================
[ドキュメント] def main(x0, h, nh): """ 異なる数値微分近似の誤差を計算し、結果をCSVに書き出し、グラフをプロットします。 指定された関数 `func` の `x0` における導関数を、様々なステップサイズ `h` と異なる差分近似法で計算し、 解析解との誤差を比較します。結果はコンソール、CSVファイル、およびMatplotlibグラフに出力されます。 :param x0: float - 微分を評価する中心点。 :param h: float - 初期ステップサイズ。この値から `kh` で減少させながら計算を繰り返します。 :param nh: int - ステップサイズを変化させる回数。 :returns: None """ print("Numerical differentiation using differnet approximations") print("Write to [{}]".format(outfile)) # open outfile to write a csv file f = open(outfile, 'w') fout = csv.writer(f, lineterminator='\n') fout.writerow([ 'Ndiv', 'h', 'Df/Dx(2-point)', 'Df/Dx(3-point)', 'Df/Dx(5-point)', 'Df/Dx(7-point)' ]) fx = func(x0) f1x = dfdx(x0) print("") print("Analytical values:") print(" f({})={}".format(x0, fx)) print(" df/dx({})={}".format(x0, f1x)) print("{:^3}:\t{:^10}\t" "{:^16}\t{:^16}\t{:^16}\t{:^16}" .format( 'Ndiv', 'h', 'Df/Dx(2-point)', 'Df/Dx(3-point)', 'Df/Dx(5-point)', 'Df/Dx(7-point)' ) ) xh = [h * kh**ih for ih in range(nh)] ydiff2 = [] ydiff3 = [] ydiff5 = [] ydiff7 = [] errdiff2 = [] errdiff3 = [] errdiff5 = [] errdiff7 = [] for ih in range(nh): print("{:^3}:\t{:^10.4e}\t".format(ih+1, xh[ih]), end = '') ydiff2.append(diff2forward(func, x0, xh[ih])) errdiff2.append(abs(ydiff2[ih] - f1x)) ydiff3.append(diff3(func, x0, xh[ih])) errdiff3.append(abs(ydiff3[ih] - f1x)) ydiff5.append(diff5(func, x0, xh[ih])) errdiff5.append(abs(ydiff5[ih] - f1x)) ydiff7.append(diff7(func, x0, xh[ih])) errdiff7.append(abs(ydiff7[ih] - f1x)) print("{:^16.12f}\t{:^16.12f}\t{:^16.12f}\t{:^16.12f}\t".format( ydiff2[ih], ydiff3[ih], ydiff5[ih], ydiff7[ih]), end = '') fout.writerow([ih+1, xh[ih], ydiff2[ih], ydiff3[ih], ydiff5[ih], ydiff7[ih]]) print("") f.close() #============================= # Plot graphs #============================= fig = plt.figure() ax1 = fig.add_subplot(2, 1, 1) ax2 = fig.add_subplot(2, 1, 2) ax1.plot(xh, ydiff2, label = '$\Delta$f/$\Delta$x (2-points)', linewidth = 0.5) ax1.plot(xh, ydiff3, label = '$\Delta$f/$\Delta$x (3-points)', linewidth = 0.5) ax1.plot(xh, ydiff5, label = '$\Delta$f/$\Delta$x (5-points)', linewidth = 0.5) ax1.plot(xh, ydiff7, label = '$\Delta$f/$\Delta$x (7-points)', linewidth = 0.5) ax1.plot(ax1.get_xlim(), [f1x, f1x], label = 'df/dx (exact)', linestyle = 'dashed', linewidth = 0.5) ax1.set_xlabel("h") ax1.set_ylabel("$\Delta$f/$\Delta$x") ax1.legend(bbox_to_anchor=(1.05, 1.0), loc='upper left', borderaxespad = 0) ax2.plot(xh, errdiff2, label = 'error (2-points)', linewidth = 0.5) ax2.plot(xh, errdiff3, label = 'error (3-points)', linewidth = 0.5) ax2.plot(xh, errdiff5, label = 'error (5-points)', linewidth = 0.5) ax2.plot(xh, errdiff7, label = 'error (7-points)', linewidth = 0.5) ax2.set_xscale('log') ax2.set_yscale('log') ax2.set_xlabel("h") ax2.set_ylabel("|$\Delta$f/$\Delta$x - df/dx|") ax2.legend(bbox_to_anchor=(1.05, 1.0), loc='upper left', borderaxespad = 0) plt.tight_layout() plt.pause(0.1) print("Press ENTER to exit>>", end = '') input()
if __name__ == '__main__': main(x0, h, nh)