python(scipy)で手っ取り早く制約付き最適化しようとしたら意外と手間取ったのでメモ。
これだけやれば動く↓
# -*- Mode: Python; coding: utf-8; -*-
from scipy.optimize import minimize, BFGS, LinearConstraint
import numpy as np
# 目的関数: ここでは2変数関数にしてある
def func(x):
return x[0]**2 - x[1]**2 + x[0]*x[1] - x[0] + x[1]
# 初期解: (0,0)
x0 = np.array([0,0], dtype=float)
# 制約: 各変数と両者の和が[0,1]という線形制約
A = np.array([[1,1],[1,0],[0,1]])
lb = np.array([0,0,0])
ub = np.array([1,1,1])
const = LinearConstraint(A, lb, ub)
# 最適化
res = minimize(func, # 目的関数,
x0, # 初期解,
method="trust-constr", # 制約付き信頼領域法
jac="2-point", # 勾配関数
hess=BFGS(), # ヘシアンの推定方法
constraints=const, # 制約
options={"maxiter": 50, # 最大反復数
"verbose":2}) # 最適化の過程を出力
print(res["x"])
内容
ひとつひとつは大して難しくない
インポート
scipy1.1以上が前提。
"from scipy.optimize import minimize" ではなく
"import scipy.optimize.minimize"
と書くと通らないので注意(scipy.optimizeがパッケージで、minimizeは関数なので)。
制約条件
LinearConstraint(A, lb, ub)
として lb <= Ax <= ub なる線形制約をscipy.optimize.LinearConstraintで記述する。
scipy.optimize.NonlinearConstraintを使うと非線形制約も書ける。
勾配推定
勾配関数を渡さない場合、関数値から自動で推定してくれる。
ただし推定の方法を明示的に渡す必要があるので、"2-point"などで指定する
ヘシアン推定
勾配関数同様に"2-point"自動推定してくれるが、勾配を自動推定している場合はこのキーワードは渡せない。(おそらく精度面の問題?)
代わりに"HessianUpdateStrategy"なるものを与えてやる必要がある。
scipy.optimize.BFGS にできあいのものがあるのでとりあえずこれを使えばよい。