モデルのファイル入出力¶
LP ファイルもしくは QPLIB ファイルから Amplify SDK の Model を作成できます。逆に、Amplify SDK の Model を保存してこれらのファイルを出力する機能も提供されます。
これらは、他の数理最適化ソルバーとの連携や、Model の保存や再利用に用いると便利です。
LP ファイルフォーマット¶
Amplify SDK が扱える LP ファイルのフォーマットは、Gurobi LP Format に従います。
LP ファイル形式では、以下のモデルのファイル入出力に対応します。
バイナリ変数・整数変数・実数変数を含む問題、およびこれらの変数を組み合わせた問題
2 次以下の目的関数と制約条件で構成された問題
LP フォーマットで表現可能な目的関数・制約を持つ問題
制限事項¶
LP ファイルの入出力において以下の制限があります。
LP ファイル読み込みにおいて以下の形式およびセクションには対応しません
Multi-Objective Case
Indicator Constraints
Lazy Constraints Section
User Cuts Section
SOS Section
PWLObj Section
General Constraint Section
Scenario Section
LP ファイル読み込みにおいて semi-continuous 変数には対応しません
Amplify SDK で設定した変数名や制約条件名が以下の条件に該当する場合、エラーとなり LP ファイルに出力されません。
半角スペース、および改行やタブなどの制御文字を含む
記号
+, -, *, ^, :, /, \, [, ]を含む記号
<, >, =, ,, (, )または数字から始まる (1xなど)以下で示す一部の予約語から始まる
s.t., st., st以下で示す一部の予約語と一致する
free, min, minimum, minimize, max, maximum, maximize, bin, binary, binaries, gen, general, generals, semi, semis, bounds, end256 文字以上
Amplify SDK で設定した制約条件の重みは LP ファイルに保存されません
目的関数が
Matrixのモデルを保存する場合、Polyと同じ形式で保存されます。注釈
なお、LP ファイルからモデルを読み込むと目的関数は常に
Polyになります。
QPLIB ファイルフォーマット¶
Amplify SDK が扱える QPLIB ファイルのフォーマットは、QPLIB: a library of quadratic programming instances とその Supplementary material 1 に従います。
QPLIB ファイル形式では、以下のモデルのファイル入出力に対応します。
バイナリ変数・整数変数・実数変数を含む問題、およびこれらの変数を組み合わせた問題
2 次以下の目的関数と制約条件で構成された問題
QPLIB フォーマットで表現可能な目的関数・制約をもつ問題
制限事項¶
QPLIB ファイルの入出力において以下の制限があります。
Amplify SDK で設定した変数名や制約条件名が以下の条件に該当する場合、エラーとなり QPLIB ファイルに出力されません。
半角スペース、および改行やタブなどの制御文字を含む
記号
!, %, #から始まる
Amplify SDK で設定した制約条件の重みは QPLIB ファイルに保存されません
QPLIB フォーマットは二値変数を境界 0 と 1 の整数変数として書きます。境界が 0 と 1 の整数変数は、モデルを保存して読み込み直すと二値変数になります。変数の取りうる値の集合は変わりません
QPLIB ファイルの読み込みにおいて、変数の初期値に関する記述は無視されます
目的関数が
Matrixのモデルを保存する場合、Polyと同じ形式で保存されます。注釈
なお、QPLIB ファイルからモデルを読み込むと目的関数は常に
Polyになります。
LP/QPLIB ファイルへの出力¶
Amplify SDK で作成したモデルをファイルに出力するには、save_lp() または save_qplib() の引数に Model とファイルパスを与えます。
from amplify import VariableGenerator, Model, one_hot, save_lp
gen = VariableGenerator()
q = gen.array("Binary", 4)
f = 2 * q[0] * q[1] + q[2] * q[3] + q[0] + q[1] + q[2] + q[3] - 1
c = one_hot(q[:3]) # q_0 + q_1 + q_2 == 1
model = Model(f, c)
save_lp(model, "model.lp")
このとき、model.lp がカレントディレクトリに保存されます。
model.lp
Minimize
1 q_0 + 1 q_1 + 1 q_2 + 1 q_3 + [ 4 q_0 * q_1 + 2 q_2 * q_3 ] / 2 - 1
Subject To
1 q_0 + 1 q_1 + 1 q_2 = 1
Bounds
q_0 free
q_1 free
q_2 free
q_3 free
Binaries
q_0 q_1 q_2 q_3
Generals
End
同様にして、QPLIB ファイルに保存する場合は以下のようにします。
from amplify import save_qplib
save_qplib(model, "model.qplib")
このとき、model.qplib がカレントディレクトリに保存されます。
model.qplib
! -------------------
! problem information
! -------------------
# problem name
Amplify
# problem type
QBL
# problem sense
minimize
# number of variables
4
# number of constraints
1
! ------------------
! objective function
! ------------------
# quadratic terms
2
2 1 4
4 3 2
# linear terms
0
4
1 1
2 1
3 1
4 1
# constant
-1
! -----------
! constraints
! -----------
# linear terms
3
1 1 1
1 2 1
1 3 1
# infinity value
1.0e+20
# lower bounds
-2.0e+20
1
1 1
# upper bounds
2.0e+20
1
1 1
! ---------------
! starting points
! ---------------
# starting point for variables
0
0
# starting point for Lagrange multipliers
0
0
# starting point for dual variables
0
0
! -----------------------------
! variable and constraint names
! -----------------------------
# variable names
4
1 q_0
2 q_1
3 q_2
4 q_3
# constraint names
0
注釈
LP ファイルと QPLIB ファイルは、変数を名前で識別します。
そのため、Amplify SDK での定式化において、モデルに含まれる変数の名前が重複しないように注意してください。
LP/QPLIB ファイルからの読み込み¶
load_lp() または load_qplib() の引数に LP/QPLIB ファイルのパスを与えると、(Model, VariableGenerator) のタプルが得られます。要素はそれぞれ、ファイルに記述された定式化モデルと、変数の発行に使われた変数ジェネレータを表します。
たとえば、上の save_lp() の例で保存した model.lp は、以下のように読み込まれます。
from amplify import load_lp
model, gen = load_lp("model.lp")
>>> print(model)
minimize:
2 q_0 q_1 + q_2 q_3 + q_0 + q_1 + q_2 + q_3 - 1
subject to:
q_0 + q_1 + q_2 == 1 (weight: 1)
ファイルに定義されている決定変数は以下のように確認できます。
>>> model.variables
[Variable({name: q_0, id: 0, type: Binary}), Variable({name: q_1, id: 1, type: Binary}),
Variable({name: q_2, id: 2, type: Binary}), Variable({name: q_3, id: 3, type: Binary})]
読み込まれたモデルは通常通り solve() に渡すことで求解が実行されます。
from amplify import solve, AmplifyAEClient
client = AmplifyAEClient()
client.token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
result = solve(model, client)
変数名と得られた解の値の対応関係は次のようにして辞書で取得できます。
>>> {k.name: v for k,v in result.best.values.items()}
{'q_0': 0.0, 'q_1': 1.0, 'q_2': 0.0, 'q_3': 0.0}
読み込み後のモデルの変更¶
ファイルから読み込まれたモデルに対して、モデルに含まれる変数から多項式を作成し、モデルを変更し目的関数の改変や制約条件の追加が可能です。
次のようにして、モデルに含まれる変数を Poly のリストに変換します。
from amplify import Poly
# モデルの読み込み
model, gen = load_lp("model.lp")
# モデルに含まれる変数を Poly のリストに変換
vars = [Poly(v) for v in model.variables]
# モデルの目的関数の変更と制約条件の追加
model += vars[1] * vars[2]
model += one_hot(sum(vars[1:]))
モデル読み込み時に得られた変数ジェネレータを使って新しい変数を生成し、新たな項も追加できます。
ここで新しく発行する変数には、既存のモデルに含まれる変数の名前とは重複しないようしてください。
# 変数ジェネレータで新しく整数変数 "n" を生成
add_vars = gen.array("Integer", 2, bounds=(1, 4), name="n")
# モデルの目的関数を変更
model += 2 * add_vars[0] - add_vars[1]
モデルの変更を確認できます。
>>> print(model)
minimize:
2 q_0 q_1 + q_1 q_2 + q_2 q_3 + q_0 + q_1 + q_2 + q_3 + 2 n_0 - n_1 - 1
subject to:
q_0 + q_1 + q_2 == 1 (weight: 1),
q_1 + q_2 + q_3 == 1 (weight: 1)
制約条件の重みを設定する¶
LP ファイルおよび QPLIB ファイルには、制約条件の重みを記述できません。 そのため、必要であれば、モデルをロードした後に制約の重みを設定します。 (参考:「制約条件の構築/制約条件の重みを設定する」)
先ほど変更したモデルを一度ファイルに出力し、再度読み込んで例として使用します。
# モデルの出力
save_lp(model, "model.lp")
# モデルの再読み込み
model, gen = load_lp("model.lp")
# モデルに含まれる制約条件リストを取り出す
constraints = model.constraints
>>> print(constraints)
[q_0 + q_1 + q_2 == 1 (weight: 1),
q_1 + q_2 + q_3 == 1 (weight: 1)]
>>> constraints *= 2
>>> print(constraints)
[q_0 + q_1 + q_2 == 1 (weight: 2),
q_1 + q_2 + q_3 == 1 (weight: 2)]
>>> constraints[0].weight = 5
>>> print(constraints)
[q_0 + q_1 + q_2 == 1 (weight: 5),
q_1 + q_2 + q_3 == 1 (weight: 2)]
ペナルティ関数の生成アルゴリズムの指定¶
load_lp() と load_qplib() は、第二引数に PenaltyFormulation を与えることができます。
これは、不等式制約のペナルティを生成する際に使用するアルゴリズムであり、デフォルトは Default です。(参考:「制約条件とペナルティ関数/不等式制約」)
from amplify import VariableGenerator, Model, save_lp, load_lp, less_equal
gen = VariableGenerator()
q = gen.array("Binary", 3)
c = less_equal(q[0] + q[1] + q[2], 2)
model = Model(c)
save_lp(model, "model.lp")
>>> model, gen = load_lp("model.lp", "Default")
>>> print(model.constraints[0].penalty)
2 q_0 q_1 + 2 q_0 q_2 - 2 q_0 n_0 + 2 q_1 q_2 - 2 q_1 n_0 - 2 q_2 n_0 + n_0^2 + q_0 + q_1 + q_2
>>> model, gen = load_lp("model.lp", "LinearRelaxation")
>>> print(model.constraints[0].penalty)
0.5 q_0 + 0.5 q_1 + 0.5 q_2