ommx.v1.Instance#
ommx.v1.Instance は最適化問題自体(数理モデル)を記述するためのデータ構造です。次のコンポーネントから構成されます。
決定変数 (
decision_variables)目的関数(
objective)制約条件(
constraints)最大化・最小化(
sense)
例えば簡単な最適化問題を考えましょう
これに対応する ommx.v1.Instance は次のようになります。
from ommx.v1 import Instance, DecisionVariable
x = DecisionVariable.binary(1, name='x')
y = DecisionVariable.binary(2, name='y')
instance = Instance.from_components(
decision_variables=[x, y],
objective=x + y,
constraints=[x * y == 0],
sense=Instance.MAXIMIZE
)
これらのコンポーネントはそれぞれに対応するプロパティが用意されています。目的関数については前節で説明した ommx.v1.Function の形に変換されます。
instance.objective
Function(x1 + x2)
sense は最大化問題を表す Instance.MAXIMIZE または最小化問題を表す Instance.MINIMIZE が設定されます。
instance.sense == Instance.MAXIMIZE
True
決定変数#
決定変数と制約条件については pandas.DataFrame の形式で取得できます
instance.decision_variables
[DecisionVariable(raw=DecisionVariable(id=1, kind=1, name="x", bound=[0, 1])),
DecisionVariable(raw=DecisionVariable(id=2, kind=1, name="y", bound=[0, 1]))]
まず kind と lower, upper は数理モデルとして必須の情報です。
kindはその決定変数の種類でBinary, Integer, Continuousに加えてSemiInteger, SemiContinuousがあります。lowerとupperはその決定変数の下限と上限です。Binaryの場合は \([0, 1]\) になります。
加えてOMMXは数理最適化を実務上のデータ分析に統合した時に必要になるようなメタデータを統合的に扱う事を目指して設計されているので、決定変数のメタデータを保持することができます。これらは数理モデル自体には影響を与えないので必須の情報ではありませんがデータ分析や可視化の際に有用です。
nameは人間が読める形の決定変数の名前です。OMMXでは決定変数は常にIDで識別されるのこの名前は重複することがあります。後述するsubscriptsと合わせて利用することが想定されています。descriptionはその決定変数についてのより詳細な説明です。多くの数理最適化問題を扱う際、多次元配列として決定変数を扱うことが多いです。例えば \(x_i + y_i \leq 1, \forall i \in [1, N]\) のような添字 \(i\) を持った制約条件を考えるのが普通でしょう。この時
xとyはそれぞれの決定変数の名前なのでnameに保存し、\(i\) に相当する部分をsubscriptsに保存します。subscriptsは整数のリストであり、もし添字が整数で表現できない倍はdict[str, str]型として保存できるparametersというプロパティが用意されています。
なお直接 ommx.v1.DecisionVariable のリストが欲しい場合は decision_variables プロパティを使うことができます
for v in instance.decision_variables:
print(f"{v.id=}, {v.name=}")
v.id=1, v.name='x'
v.id=2, v.name='y'
決定変数のIDから ommx.v1.DecisionVariable を取得するには get_decision_variable_by_id メソッドを使うことができます
x1 = instance.get_decision_variable_by_id(1)
print(f"{x1.id=}, {x1.name=}")
x1.id=1, x1.name='x'
制約条件#
次に制約条件を見てみましょう
instance.constraints_df
| equality | type | used_ids | name | subscripts | description | |
|---|---|---|---|---|---|---|
| id | ||||||
| 0 | =0 | Quadratic | {1, 2} | <NA> | [] | <NA> |
OMMXでは制約条件もIDで管理されます。このIDは決定変数のIDとは独立です。上の例で x * y == 0 のように制約条件を作った場合は自動的に連番が振られるようになっています。手動でIDを設定するには set_id メソッドを使うことができます。
c = (x * y == 0).set_id(100)
print(f"{c.id=}")
c.id=100
制約条件に必須の情報は id と equality です。equality はその制約条件が等式制約 (Constraint.EQUAL_TO_ZERO) か不等式制約 (Constraint.LESS_THAN_OR_EQUAL_TO_ZERO) かを表します。\(f(x) \geq 0\)のタイプの制約条件は \(-f(x) \leq 0\) として扱われることに注意してくください。
制約条件にも決定変数と同様にメタデータを保存することができます。決定変数と同様に name, description, subscripts, parameters が利用できます。これらは add_name, add_description, add_subscripts, add_parameters メソッドで設定できます。
c = (x * y == 0).set_id(100).add_name("prod-zero")
print(f"{c.id=}, {c.name=}")
c.id=100, c.name='prod-zero'
また constraints プロパティを使うことで直接 ommx.v1.Constraint のリストを取得でき、また制約条件のIDから ommx.v1.Constraint を取得するには get_constraint_by_id メソッドを使うことができます
for c in instance.constraints:
print(c)
Constraint(x1*x2 == 0)
記号的な代入#
Instance.substitute は目的関数と有効な制約条件に現れる決定変数を、指定した関数式で置き換えます。これは整数変数を新しいバイナリ変数で表現する binary encoding のような変換で使われます。
この操作は代数的な書き換えです。代入された変数の kind, lower, upper を、置換後の式に対する制約へ自動的には変換しません。例えば x1 が binary で、x1 を x2 + x3 に置き換えても、OMMX は 0 <= x2 + x3 や x2 + x3 <= 1 を追加しません。x1 が integer の場合も、置換後の式が整数値を取るという制約は追加されません。
代入された変数は従属変数として記録されるため、解を評価するときに値を復元できます。その bound や kind は Solution.feasible で検証されますが、置換後の式に対するソルバー制約としては渡されません。削除済み制約は即座には書き換えられず、後で復元するときに従属変数と固定済み変数が置換されます。つまり substitute だけでは、最適化モデルとして等価な変換であることは保証されません。
これは意図した仕様です。制約を緩和する操作のように、モデルを意図的に変える変換もあります。一方で log encoding や独自の binary encoding のような変換は、エンコーディング自体が元の変数の domain を保つように構築されるため正当化できます。
一般の代入でモデルの意味を保存したい場合は、必要な制約を明示的に追加してください。保守的な方法は、元の変数を消去せずに x1 - (x2 + x3) == 0 のような linking equality を変換後モデルの制約に含めることです。substitute で x1 を消去する場合は、置換後の式に必要な bound 制約を別途追加してください。