# JuMP + Ipopt を用いた最適化 - JuMP(Julia for Mathematical Optimization): 数理最適化モデルを記述するためのモデリング言語.簡単だし,ソルバごとにAPIを覚えなくて良い. - Ipopt(Interior Point OPTimizer): 大規模最適化問題を解くためのオープンソースライブラリ. EPL(Ecliple Public License)で利用できる.主双対内点法を使っているのが特徴. ## JupyterLab と Julia のインストール - Mac 1. homebrew をインストール https://brew.sh/ を参考にすればOK 2. JupyterLab と Julia をインストール homebrew が入っている状態でターミナルから以下を実行: ```shell brew install --cask jupyterlab brew install --cask julia ``` 3. Julia から JupyterLab を使えるようにする ターミナルから Julia を起動し, `]`をタイプしてパッケージモードにした後, ``` activate . add IJulia build IJulia ``` を実行. [ここ](https://qiita.com/shyu_manabe/items/3978c1ef5d96d4e9dcef)とか[ ここ](https://julia.quantecon.org/getting_started_julia/getting_started.html#installing-packages)が参考になる. `julia 1.10`からは上のコマンドを使わないとうまく動かないようだ. - Windows Julia と Jupyterlab をそれぞれインストール. たとえば[ここ](https://zenn.dev/ohno/books/356315a0e6437c/viewer/c7687c)とかを参考にする ## JuMPとIpOptのインストール JupyterLab のセルに以下を記述しておく. ```julia import Pkg Pkg.add("JuMP") Pkg.add("Ipopt") ``` # 最適化をやってみる ## 最初のサンプル ```julia= using JuMP, Ipopt # モジュールを使用 model = Model(Ipopt.Optimizer) # モデルを定義 @variable(model, x >= 0) # 変数xを追加 @variable(model, 0 <= y <= 3) # 変数 yを追加 @objective(model, Min, 12x + 20y) # 目的関数を設定 @constraint(model, c1, 6x + 8y >= 100) # 1つめの制約条件を追加 @constraint(model, c2, 7x + 12y >= 120) # 2つめの制約条件を追加 print(model) # 構築されたモデルを数式表示 optimize!(model) # 最適化 ``` これだけで最適化される. 下記を実行すれば計算結果を出力させられる. ```julia= display( termination_status(model) ) display( primal_status(model) ) display( dual_status(model) ) display( objective_value(model) ) display( value(x) ) display( value(y) ) display( shadow_price(c1) ) display( shadow_price(c2) ) ``` ## 利用者均衡配分 $K$本の平行リンクで構成されるネットワークを考え,起点から終点への総交通需要を$Q$で表す.リンク交通量を$\boldsymbol{x} = (x_{k}: k =1, \cdots, K)$で表し,各リンクの所要時間が線形式: $$ \boldsymbol{t}(\boldsymbol{x}) = \boldsymbol{D} \boldsymbol{x} + \boldsymbol{e} $$ で表されるとする.ここで,$\boldsymbol{D}$は正定値対称な$K\times{}K$行列とし,$\boldsymbol{e}$は$K$次元列ベクトルである.このとき,利用者均衡配分問題は,以下の最適化問題に帰着する: \begin{align} \min_{\boldsymbol{x}} &f(\boldsymbol{x}) = \frac{1}{2}\boldsymbol{x}^{\top} \boldsymbol{D} \boldsymbol{x} + \boldsymbol{e}^{\top}\boldsymbol{x}\\ \text{Sub to.} \quad & \boldsymbol{1}^{\top} \boldsymbol{x}= Q\\ & \boldsymbol{x} \geq \boldsymbol{0} \end{align} ```julia= using Random # Dとeをランダムに生成するために Random と LinearAlgebraを使う using LinearAlgebra K = Int(5e4) # 経路数 Q = 1 # 総交通量 D = Diagonal([rand(1:0.1:10) for k in 1:K]) # リンク所要時間係数 e = Vector([rand(1:0.1:10) for k in 1:K]) # リンク所要時間定数 f(x) = 0.5x'*D*x + x'*e # 目的関数 ∇f(x) = D*x+e # 目的関数の勾配 ∇²f(x) = D # 目的関数の Hessian model = Model(Ipopt.Optimizer) # モデルを定義 set_attribute(model, "print_level", 3) # 出力レベルを設定 @variable(model, x[1:K] .>= 0) # K次元の非負未知変数を定義 @constraint(model, c1, sum(x) == Q) # 総交通量保存則 @operator(model, op_f, K, f, ∇f, ∇²f) # 目的関数fに勾配∇fと Hessian ∇2f を明示的に指定した演算 op_f を定義 @objective(model, Min, op_f(x)) # 目的関数に op_f を指定 optimize!(model) # 最適化 _x = value.(x) # 計算結果 _rho = -shadow_price(c1) _obj = objective_value(model) sum(_x.*(∇f(_x).-_rho)) ```