import argparse
import numpy as np
import sympy as sym
from sympy.parsing.sympy_parser import parse_expr
import sys
"""
python sympy_with_test.py "1/(exp(x)+1)" --mode diff --norder 8 --xmin -2 --xmax 2 --nx 5
python sympy_with_test.py "exp(x)" --mode taylor --norder 8 --xmin -2 --xmax 2 --nx 5
python sympy_with_test.py "x**2 + cos(x)" --mode int --export
"""
import argparse
import numpy as np
import sympy as sym
from sympy.parsing.sympy_parser import parse_expr
import sys
[ドキュメント]
def get_calculation_functions(formula_str, mode, x0, norder):
# ライブラリ名なしの関数対応テーブル
func_map = {
'sin': np.sin, 'cos': np.cos, 'tan': np.tan,
'exp': np.exp, 'log': np.log, 'sqrt': np.sqrt,
'abs': np.abs, 'sinh': np.sinh, 'cosh': np.cosh
}
x = sym.symbols('x')
try:
expr = parse_expr(formula_str)
if mode == "diff":
target_expr = sym.diff(expr, x)
label = "Differentiation (1st)"
elif mode == "int":
target_expr = sym.integrate(expr, x)
label = "Integration (Indefinite)"
elif mode == "taylor":
target_expr = sym.series(expr, x, x0, norder).removeO()
label = f"Taylor Expansion (n={norder}, x0={x0})"
# 数値計算用関数の生成
f_num = sym.lambdify(x, expr, modules=[func_map, "numpy"])
target_num = sym.lambdify(x, target_expr, modules=[func_map, "numpy"])
return expr, target_expr, f_num, target_num, label
except Exception as e:
print(f"解析エラー: {e}")
sys.exit(1)
[ドキュメント]
def main():
parser = argparse.ArgumentParser()
parser.add_argument("formula", type=str)
parser.add_argument("--mode", choices=["diff", "int", "taylor"], default="diff")
parser.add_argument("--xmin", type=float, default=-1.0)
parser.add_argument("--xmax", type=float, default=1.0)
parser.add_argument("--nx", type=int, default=11)
parser.add_argument("--norder", type=int, default=5)
parser.add_argument("--x0", type=float, default=0.0)
parser.add_argument("-s", "--step", type=float, default=1e-5)
parser.add_argument("--export", action="store_true")
args = parser.parse_args()
expr, t_expr, f_func, t_func, label = get_calculation_functions(
args.formula, args.mode, args.x0, args.norder
)
h = args.step
x_vals = np.linspace(args.xmin, args.xmax, args.nx)
print(f"\n{'='*100}")
print(f" Mode: {label}")
print(f" Result Formula: {t_expr}")
print(f"{'='*100}")
# モードに応じたヘッダー
if args.mode == "diff":
col_name = "Numeric Diff"
elif args.mode == "int":
col_name = "Numeric Integ*" # 簡易的な確認
else:
col_name = "Original f(x)"
print(f"{'x':>8} | {'Original f(x)':>18} | {col_name:>18} | {'SymPy Result':>18} | {'Rel.Err':>10}")
print("-" * 100)
for xv in x_vals:
try:
v_orig = f_func(xv)
v_sympy = t_func(xv)
# 比較用の数値計算(Transformed Value)
if args.mode == "diff":
# 中央差分
v_trans = (f_func(xv + h) - f_func(xv - h)) / (2 * h)
elif args.mode == "int":
# 不定積分の比較は難しいが、ここでは x0 からの定積分(台形則)で近似値を出す例
# (簡易的に微分して元の関数に戻るか見る方が正確ですが、要望に合わせ数値比較)
v_trans = float('nan') # 積分は評価が特殊なため一旦nan
elif args.mode == "taylor":
v_trans = v_orig # テイラー展開は元の関数値と比較
# 誤差計算 (SymPyの結果 vs 数値的アプローチ)
err_ref = v_trans if not np.isnan(v_trans) else v_orig
rel_err = abs(v_sympy - err_ref) / abs(err_ref) if err_ref != 0 else 0
print(f"{xv:8.3f} | {v_orig:18.8f} | {v_trans:18.8f} | {v_sympy:18.8f} | {rel_err:10.2e}")
except:
print(f"{xv:8.3f} | 計算エラー")
if args.export:
# 前述のコード出力関数をここに呼ぶ
pass
if __name__ == "__main__":
main()