※記事内に商品プロモーションを含むことがあります。
はじめに
Pythonの最適化モデリングツールであるPyomoで、最適化モデルに定義された変数や制約の情報を一括して取得する方法をまとめました。Pyomoの変数や制約がベクトル化された状態でも対応できます。
検証環境は以下の通りです。GLPKという線形ソルバを使用します。
|
バージョン |
Python |
3.9.7 |
Pyomo |
6.4.1 |
GLPK |
4.65 |
PyomoとGLPKのインストール方法は以下の記事を参照ください。
Pyomoで線形計画問題を解く – Helve Tech Blog
リンク
対象とするPyomoモデル
以下のPyomoモデルを考えます。整数線形計画問題です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import pyomo.environ as pyo
def pyomo_const(pyo_model, i):
return pyo_model.x1[i] + 3*pyo_model.x1[i+1] <= 8
model = pyo.ConcreteModel()
model.x0 = pyo.Var(domain=pyo.Integers, bounds=(0, 10))
model.x1 = pyo.Var(range(3), domain=pyo.Integers, bounds=(0, 5))
model.const0 = pyo.Constraint(expr = 2*model.x0 + model.x1[0] <= 7)
model.const1 = pyo.Constraint(range(2), rule=pyomo_const)
model.obj = pyo.Objective(expr=2*model.x0 + sum(model.x1[i] for i in range(3)), sense=pyo.maximize)
opt = pyo.SolverFactory('glpk')
res = opt.solve(model, tee=False)
|
変数x0
はスカラー、x1
はベクトルです。また、制約const0
はスカラー、const1
はベクトルです。
変数の一覧を取得する
変数を一括で取得するには、ConcreteModel
オブジェクトのcomponent_data_objects
メソッドを使います。
1
2
|
component_data_objects(ctype=None, active=None, sort=False,
descend_into=True, descent_order=None,)
|
引数ctype
は、絞り込みたい型(変数の場合はVar
, 制約の場合はConstraint
)を与えられることで、フィルタの役割を果たします。component_data_objects
メソッドの戻り値はジェネレータとなります。そのため、for文などを使って個々の要素(変数)を取得します。
変数の一覧を取得する例を以下に示します。
1
2
|
for v in model.component_data_objects(ctype=pyo.Var):
print(f"{v.name=}, {v.lb=}, {v.value=}, {v.ub=}")
|
実行結果
1
2
3
4
|
v.name='x0', v.lb=0, v.value=3.0, v.ub=10
v.name='x1[0]', v.lb=0, v.value=1.0, v.ub=5
v.name='x1[1]', v.lb=0, v.value=2.0, v.ub=5
v.name='x1[2]', v.lb=0, v.value=2.0, v.ub=5
|
取得結果v
の属性の意味は以下の通りです。
name
: 変数名
lb
: 下限
value
: 最適化結果(最適化実行前の場合、初期値またはNone
)
ub
: 上限
さらに、取得結果v
の型を確認してみます。以下の結果から、スカラー変数はScalarVar
, ベクトル変数は_GeneralVarData
というクラスであることが分かります。
1
2
|
for v in model.component_data_objects(ctype=pyo.Var):
print(type(v))
|
実行結果
1
2
3
4
|
<class 'pyomo.core.base.var.ScalarVar'>
<class 'pyomo.core.base.var._GeneralVarData'>
<class 'pyomo.core.base.var._GeneralVarData'>
<class 'pyomo.core.base.var._GeneralVarData'>
|
また、変数の一覧をPandasのDataFrameに格納する例を以下に示します。
1
2
3
4
5
6
7
8
|
import pandas as pd
df = pd.DataFrame(columns=["lb", "value", "ub"])
for v in model.component_data_objects(ctype=pyo.Var):
df.loc[v.name] = [v.lb, v.value, v.ub]
print(df)
|
実行結果
1
2
3
4
5
|
lb value ub
x0 0.0 3.0 10.0
x1[0] 0.0 1.0 5.0
x1[1] 0.0 2.0 5.0
x1[2] 0.0 2.0 5.0
|
制約の一覧を取得する
制約を一括で取得するには、ConcreteModel
オブジェクトのcomponent_objects
メソッドを使います。
1
2
|
component_objects(ctype=None, active=None, sort=False,
descend_into=True, descent_order=None,)
|
引数ctype
は、絞り込みたい型(変数の場合はVar
, 制約の場合はConstraint
)を与えられることで、フィルタの役割を果たします。component_objects
メソッドの戻り値はジェネレータとなります。そのため、for文などを使って個々の要素(制約)を取得します。
制約の一覧を取得する例を以下に示します(2022/7/17 制約の値を取得する方法を追記しました)。
1
2
3
4
5
|
from pyomo.core.expr.numvalue import value
for c in model.component_objects(ctype=pyo.Constraint):
for v in c._data.values():
print(f"{v.name=}, {v.lb=}, {value(v.body)=}, {v.ub=}")
|
実行結果
1
2
3
|
v.name='const0', v.lb=None, value(v.body)=7.0, v.ub=7
v.name='const1[0]', v.lb=None, value(v.body)=7.0, v.ub=8
v.name='const1[1]', v.lb=None, value(v.body)=8.0, v.ub=8
|
取得結果v
の属性の意味は以下の通りです。
name
: 制約名
lb
: 下限
body
: 制約の値(body
だけでは制約式が返されるため、value
関数で実際の値を計算します)
ub
: 上限
さらに、取得結果c
の型を確認してみます。以下の結果から、スカラーの制約はScalarConstraint
, ベクトルの制約はIndexedConstraint
というクラスであることが分かります。
1
2
|
for c in model.component_objects(ctype=pyo.Constraint):
print(type(c))
|
実行結果
1
2
|
<class 'pyomo.core.base.constraint.ScalarConstraint'>
<class 'pyomo.core.base.constraint.IndexedConstraint'>
|
また、制約の結果をコンソールに表示するだけであれば、display
メソッドも使えます。
1
2
|
for c in model.component_objects(ctype=pyo.Constraint):
c.display()
|
実行結果
1
2
3
4
5
6
7
|
const0 : Size=1
Key : Lower : Body : Upper
None : None : 7.0 : 7.0
const1 : Size=2
Key : Lower : Body : Upper
0 : None : 7.0 : 8.0
1 : None : 8.0 : 8.0
|
参考
Pyomoの公式リファレンス
Working with Pyomo Models — Pyomo 6.4.1 documentation.htm