Scipyのminimizeで関数の最小値を探すときに探索範囲を制限する方法

minimize関数の使い方の記事2本目です。

前回の記事で基本的な使い方を紹介しましたが、それは探索範囲に特に制限を設けず、各変数について実数値全体から探索するものでした。

しかし、現実の問題では特定の範囲に絞って探索したいってこともよくあります。一番よくあるのは特定の変数は正の値に絞るってケースでしょうか。また、各値がそれぞれの範囲内に絞られる、長方形や直方体系の制限だけでなく、円の内側のような指定も可能です。順番に見ていきましょう。

まずは、bounds 引数を使った境界制約です。
これは、単純に各変数の探索範囲にそれぞれ下限上限を指定できます。下限だけ指定したいとか上限だけ指定したい、といった場合はnp.inf で無限大を利用してください。

次のように、bounds引数に各変数の下限上限の配列を渡します。境界を指定しなければ最小値がない関数(ただの2変数の和)で実験してみましょう。

import numpy as np
from scipy.optimize import minimize


# 目的関数の定義
def sample_function(x):
    return x[0]+x[1]


# 初期値
x0 = [0, 0]

# 最適化の実行
result = minimize(sample_function, x0, method='L-BFGS-B', bounds=[[-2, 5], [3, np.inf]])

# 結果の表示
print("最適化の結果:", result)
print("最小値をとる点 x:", result.x)
print("最小値 f(x):", result.fun)
"""
最適化の結果:   message: CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL
  success: True
   status: 0
      fun: 1.0
        x: [-2.000e+00  3.000e+00]
      nit: 2
      jac: [ 1.000e+00  1.000e+00]
     nfev: 9
     njev: 3
 hess_inv: <2x2 LbfgsInvHessProduct with dtype=float64>
最小値をとる点 x: [-2.  3.]
最小値 f(x): 1.0
"""

想定通り、それぞれの変数が最小値だったときに和も最小値でしたね。

methodは例として’L-BFGS-B’を使いましたが、boundsを指定できるmethodは数種類に限られます。もし何かしらエラーが発生したらドキュメントを確認して対応しているか確かめましょう。これは次の方法でも同じです。

次は、不等式を使って領域を絞り込む方法です。
これは制約を課す関数を定義し、その関数が「正の値」をとる範囲で探索します。

例えば、中心が(5, 3) 半径が2 の円の内側だけ探索するようにしてみましょう。
constraints引数に渡す方法はちょっとクセがあります。

# 目的関数の定義
def sample_function(x):
    return x[0]+x[1]


# 不等式制約
def constraint(x):
    return 4 - (x[0]-5)**2 - (x[1]-3)**2

inequality_constraint = {'type': 'ineq', 'fun': constraint}

# 初期値
x0 = [5, 3]

# 最適化の実行
result = minimize(sample_function, x0, method='SLSQP', constraints=inequality_constraint)

# 結果の表示
print("最適化の結果:", result)
print("最小値をとる点 x:", result.x)
print("最小値 f(x):", result.fun)
"""
最適化の結果:  message: Optimization terminated successfully
 success: True
  status: 0
     fun: 5.171572875250672
       x: [ 3.586e+00  1.586e+00]
     nit: 6
     jac: [ 1.000e+00  1.000e+00]
    nfev: 18
    njev: 6
最小値をとる点 x: [3.58578644 1.58578644]
最小値 f(x): 5.171572875250672
"""

method では今までと違って、 SLSQP を使いました。というのも、COBYLA, COBYQA, SLSQP  でしかサポートされてないのです。この辺りも気をつけて使う必要がありますね。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です