PySCIPOpt Adapterで特殊制約をそのまま解く#
OMMXでは、条件付きの制約や変数の組合せに対する制約を「特殊制約」として表現できます。各制約の意味はOMMXが定義します。対応するAdapterを使えば、通常の制約へ書き換えず、そのままソルバーへ渡せます。
このチュートリアルでは、次の2種類を含む小さなモデルを作り、PySCIPOpt Adapterで解きます。
Indicator制約: 指定したBinary変数が1のときだけ有効になる制約
SOS1制約: 指定した変数のうち、高々1つだけが非ゼロになる制約
実行環境#
Python SDKとAdapterのインストールで用意したv3の環境を使います。このページではPySCIPOpt Adapterを使います。
特殊制約を持つInstanceを作る#
次のモデルには4つのBinary変数があります。enabled = 1 のときだけ option_a + option_b <= 1 を課し、さらに option_b と option_c のうち高々1つだけを選べるようにします。
from ommx import Instance, Sos1Constraint
source = Instance.maximize()
enabled = source.new_binary("enabled")
option_a = source.new_binary("option_a")
option_b = source.new_binary("option_b")
option_c = source.new_binary("option_c")
source.objective = (
10 * enabled
+ 6 * option_a
+ 5 * option_b
+ 4 * option_c
)
# enabled = 1 のときだけ option_a + option_b <= 1
source.add_indicator_constraint(
(option_a + option_b <= 1).with_indicator(enabled)
)
# option_b と option_c のうち、高々1つだけが非ゼロ
source.add_sos1_constraint(
Sos1Constraint(
variables=[option_b, option_c],
name="exclusive_options",
)
)
assert len(source.indicator_constraints) == 1
assert len(source.sos1_constraints) == 1
source
Instance.maximize() で空の最大化問題を作り、new_binary() と add_*_constraint() でモデルを順に組み立てています。
PySCIPOpt Adapterで解く#
PySCIPOpt Adapterは、線形なIndicator制約とSOS1制約を直接受け取れます。前のナップサック問題と同じく、作ったInstanceをsolve()へ渡します。
from ommx_pyscipopt_adapter import OMMXPySCIPOptAdapter
solution = OMMXPySCIPOptAdapter.solve(source)
assert solution.feasible
assert abs(solution.objective - 20.0) < 1e-8
solution.decision_variables_df()
この解では enabled = 1、option_a = 1、option_b = 0、option_c = 1 となり、Indicator制約とSOS1制約をどちらも満たします。呼び出し前に通常制約へ書き換える必要はありませんでした。
一方、Indicator制約やSOS1制約を直接扱わないソルバーもあります。次のAdapter向けにInstanceを準備するでは、同じモデルをHiGHSで解き、OMMXが通常制約への変換を担う例を見ます。各特殊制約の意味と個別APIの参照先は特殊制約型にまとめています。