※記事内に商品プロモーションを含むことがあります。
はじめに
CasADiは自動微分と非線形最適化のためのライブラリである。C++で実装されており、C++, Python, Matlab, Octaveのインターフェースを備えている。
本記事ではPythonを使い、CasADiで非線形最適化問題を解く方法をまとめた。
CasADiにはIPOPTという非線形ソルバが含まれているため、これを用いる。IPOPTは主双対内点法を利用したソルバであり、大規模な非線形問題を高速に解くことができる。ただし、問題は連続である必要があり、大域的最適解を求めるには問題が凸である必要がある。
環境は以下の通り。
ソフトウェア | バージョン |
---|---|
Python | 3.7.4 |
CasADi | 3.5.5 |
ソフトウェアのインストール
pipでCasADiをインストールする。CasADiには最適化ソルバも含まれており、同時にインストールされる。
pip install casadi
対象とする非線形計画問題
以下の制約付き非線形最小化問題を考える。
$$ \begin{array}{ll} \text{minimize} & f(x_1, x_2) = x_1^2 + x_2^2 \\ \text{subject to} & x_1 x_2 \ge 1, \\ & 0 \le x_1 \le 4, 0 \le x_2 \le 4 \end{array} $$
図示すると以下のようになり、$(x_1, x_2)=(1, 1)$で最小値$2$をとる。青色で図示した領域は実行可能領域を示す。
CasADiのソースコード
上記の問題をCasADiを使って記述し、最適化を実行したコードは以下のようになる。
|
|
実行結果は以下のようになり、最適値を得られている。
This is Ipopt version 3.12.3, running with linear solver mumps.
NOTE: Other linear solvers might be more efficient (see Ipopt documentation).
(中略)
EXIT: Optimal Solution Found.
solver : t_proc (avg) t_wall (avg) n_eval
nlp_f | 0 ( 0) 0 ( 0) 19
nlp_g | 0 ( 0) 0 ( 0) 19
nlp_grad_f | 1.00ms ( 76.92us) 997.00us ( 76.69us) 13
nlp_hess_l | 0 ( 0) 0 ( 0) 11
nlp_jac_g | 0 ( 0) 0 ( 0) 13
total | 22.00ms ( 22.00ms) 21.94ms ( 21.94ms) 1
評価関数:1.9999999825046224
x1: 0.9999999956261556
x2: 0.9999999956261556
ソースコードの解説
以下、ソースコードの詳細を述べる。
最適化問題のオブジェクトの作成
まず、Opti
クラスを用いて最適化問題のオブジェクトを作成する。
|
|
変数の定義
次に、opti.variable()
メソッドを用いて変数を定義する。
|
|
variable()
に引数を与えない場合、変数はスカラになる。一方、
x = opti.variable(2)
のように整数を引数にとることで、ベクトルにできる。
ベクトルにした場合、個々の要素にはx[0]
, x[1]
のようにアクセスする。
初期値の指定
変数に初期値を指定する場合、opti.set_initial()
メソッドを用いる。指定しない場合、初期値は0となる。
|
|
目的関数の定義
次に、目的関数を定義し、opti.minimize()
メソッドに引数として与える。
|
|
もしくは、以下のように目的関数を直接与えることも可能である。
|
|
しかし、直接与えた場合、最適化後の目的関数の値を取得するには、以下のようにする必要があるため、目的関数を別途定義したほうが便利である。
sol.stats()['iterations']['obj'][-1]
制約条件の定義
subject_to()
メソッドで制約条件を設定する。
|
|
等式制約は==
で、不等式制約は<=
, >=
, <
, >
でそれぞれ設定する。
また、制約式に上限と下限の両方がある場合、opti.bounded()
メソッドでまとめて定義できる。例えば、$1 \le x_1 x_2 \le 6$という制約は以下のように定義できる。
|
|
変数の範囲の指定
opti.subject_to()
メソッドとopti.bounded()
メソッドを用いて、変数の範囲(下限値と上限値)を指定できる。
|
|
ソルバの指定と最適化
最後に、ソルバを指定して最適化を行う。
|
|
solver
メソッドでソルバを指定する。ここではIPOPTを用いる。次に、solve
メソッドを実行すると、最適化が行われる。sol
には最適化の結果が格納される。
最適化ソルバのオプション設定
最適化ソルバ(ここではIPOPT)にオプションを設定するためには、solver
メソッドに辞書形式で渡す。
ここで、solver
メソッドの2番目の引数はCasADiプラグインのオプション(本記事では説明を省略)、3番目の引数が最適化ソルバのオプションになる。
|
|
この例では、以下のように設定した。
- 最適化計算のログ出力の詳細度合い (
print_level
) を4(0~12の範囲。数字が大きいほど詳細になる) - 最適解の許容誤差 (
tol
) を1E-3
IPOPTで設定可能なオプションについては以下のページを参照。
Ipopt: Ipopt Options
一覧の形式で確認したい場合には以下のページを参照。
https://www.coin-or.org/Bonmin/option_pages/options_list_ipopt.html
最適化結果の評価
最適化後の変数や目的関数の値を取得するには、sol
のvalue
メソッドに変数名を渡す。
|
|
最適化問題の概要を表示するには、sol.disp()
, sol.stats()
などを実行する。
|
|
|
|
参考
CasADiの公式リファレンス
CasADi - Docs