ソフトな忘却力(93) SciPy.optimize で関数の根を求めるお勉強

Joseph Halfmoon

「サイエンティフィックPythonのための」IDE、Spyder上にてScientific Python Lecturesの実習中。前回は「補間」。今回はSciPyが ”root finding”と呼んでいる「関数の根」デス。代表的なアルゴリズムはニュートン法ね。遥か半世紀くらい前に学校でやった遠い記憶。大丈夫か?

※「 ソフトな忘却力」投稿順 Index はこちら

Scientific Python Lectures様のコースは例題だけでなく、エクササイズなども充実、それを全部順番に解いていったら必ずや立派な人になれるだろ~と思います。でも老い先短い年寄には量が多過ぎて多分死ぬまでに終わりません。適当な練習でお茶を濁してます。今回は、「5.5 Optimization and fit: scipy.optimize」から「5.5.1 Root Finding」です。

実習する前に、お言葉「根」と「解」について気になったので調べてみました。かの結城浩先生(「数学ガール」で有名なセンセ)、が以下で書かれてました。

「根」と「解」は同じですか

ううむ、勉強になるなあ。

SciPyのAPI解説ページ

以下に Optimization and root finding (scipy.optimize) へのURLを貼りました。

https://docs.scipy.org/doc/scipy/reference/optimize.html

この中に Root finding という一節があり、その中でも今回練習してみるのは Scalar functions のところです。Scalarあれば多次元空間での求解もあり、さらには「リニア・プログラミング」もここに含まれているみたい。奥が深いな Root finding。

でもま、今回は「フツーのスカラーな関数」相手です。root_scalarという関数で求めることができるみたい。何もオプション指定などしないと、みんな大好き「ニュートン法」で求めてくれるみたいっす。ただし、オプション指定すれば各種アルゴリズムが使えるようです。

ニュートン法の求根サンプル

以下はSpyder上で作成の「ちょい意地悪な」サンプルプログラムです。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Jun 16 2025

@author: jhalfmoon
Scipy.optimize.root_scalar sample
"""
import sys
import scipy as sp

def f0(x):
    return (x-1.0)**2

def fp(x):
    return (x-1.0)**2 + sys.float_info.epsilon

def fp2(x):
    return (x-1.0)**2 + 0.0001

def fn(x):
    return (x-1.0)**2 - sys.float_info.epsilon

def main():
    xinit = 1.1
    newton0 = sp.optimize.root_scalar(f0, x0=xinit, method='newton')
    print("---newton0","---"*10, "\n", newton0)
    newtonp = sp.optimize.root_scalar(fp, x0=xinit, method='newton')
    print("---newtonp","---"*10, "\n", newtonp)
    newtonn = sp.optimize.root_scalar(fn, x0=xinit, method='newton')
    print("---newtonn","---"*10, "\n", newtonn)
    newtonp2 = sp.optimize.root_scalar(fp2, x0=xinit, method='newton')
    print("---newtonp2","---"*10, "\n", newtonp2)
   
if __name__ == '__main__':
    main()

ニュートン法の場合、オプショナルで対象となる関数の微分を与えられると収束が速くなるみたい(確かめてないけど。)でも今回は、比較のために「開始ポイント」を全て同じ値と指定しただけで、あとはニュートン法のデフォルトのまま計算していただいております。

ニュートン法のアルゴリズム的に「ある精度」内に落ち着いたならば根が求まったと判断して打ち切るのであろうと想像。そこで下記のような4関数を用意して実行してみました。

    1. f0、x=1.0で重根を持つ。負の値はとらないのよ。
    2. fp、x=1.0で限りなくx軸に近付くが、実は実根は存在しない。
    3. fn、x=1.0の上下かなり近くに2つの実根がある。
    4. fp2、x=1.0でx軸に近付くけれど、実根が存在しないのは多分アカラサマ。
実行結果

上記のプログラムの実行結果が以下に。

newtonEC重根を持つ newton0と、2根を持つnewtonnの根(そのうちの一つ)が見つかっているのは良いとして、本当は根が存在しない筈のピンクのnewtonpもサクッと根が見つかったことになっているみたいデス。convergedだと。まあ、ニュートン法のデフォルトのトレランス値がいくつか確認してないけど、newtonp関数みたいなことをすると0にタッチしていると誤解するみたい。まあ数値解だし、「限りなく近い」数字が見つかったってことで良いのか。。。

一方、アカラサマに根が存在しない筈のnewtonp2は、coverged: Flase、flag: convergence errorと判定され、「めでたく」根は存在しないことになってます。良かった。

ソフトな忘却力(92) SciPy.interpolate で補間のお勉強 へ戻る

ソフトな忘却力(94) SciPy.optimize で線形計画法のお勉強 へ進む