GurobiのLazy Constraintで制約を逐次追加する
組合せ最適化問題について、整数計画の定式化をしていると、表現したい条件についての制約が非常に多くなってしまうことがある。
このような場合には、必要な制約のみを逐次追加していく切除平面法が用いられることがある。
本記事では、それをGurobiのLazy Constraintという機能を用いて実装していく。
問題としては、無向グラフと辺の重みについての最小全域木問題に対する、カットセット不等式を用いた定式化を考える。
また、問題例としては、重みをランダムに定めた格子グラフを生成する。
その生成コードは次の通り。
import networkx as nx import random import gurobipy as gp from gurobipy import GRB n, m = 5, 5 lb, ub = 1, 100 random.seed(42) G = nx.grid_2d_graph(n, m) V = n * m nodes = list(G.nodes) d = {} for e in G.edges: d[e] = random.randint(lb, ub)
カットセット不等式を用いた定式化は、次のようになる。
これをGurobiで素直に実装すると次のようになる。
model = gp.Model() x = model.addVars(G.edges, vtype=GRB.BINARY) model.setObjective(x.prod(d)) model.addConstr(gp.quicksum(x) == V - 1) for s in range(1, 1 << V - 1): S = [nodes[i] for i in range(V) if s & (1 << i)] cut_x = [x[(u, v)] for u, v in G.edges if (u in S and v not in S) or (v in S and u not in S)] x_cutsum = gp.quicksum(cut_x) model.addConstr(x_cutsum >= 1) model.optimize()
カットセット不等式は頂点数の指数本になるため、頂点数が20~30程度で限界がくる。
よって、カットセット不等式を逐次追加していくことを考える。
方針は次のようにする。
- ある頂点を定める。
- 分枝限定法で暫定解が得られたときに、を、得られた暫定解によってから到達可能な頂点集合とする。
- ならば、何もしない。そうでなければ、に対応するカットセット不等式を追加する。
この実装は次のようになる。深さ優先探索で、到達可能な頂点集合を求めている。
model.cbLazyで制約を追加するためには、事前にパラメータ「LazyConstraints」を1に設定する必要があることに注意。
model = gp.Model() model.setParam("LazyConstraints", 1) x = model.addVars(G.edges, vtype=GRB.BINARY) model.setObjective(x.prod(d)) model.addConstr(gp.quicksum(x) == V - 1) def callback(model, where): if where == GRB.Callback.MIPSOL: x_cur = model.cbGetSolution(x) S = [(0, 0)] que = [(0, 0)] while len(que) > 0: v = que.pop() for u in G[v]: if u in S: continue if ((u, v) in x.keys() and x_cur[(u, v)] >= 0.5) \ or ((v, u) in x.keys() and x_cur[(v, u)] >= 0.5): S.append(u) que.append(u) if len(S) < V: cut_x = [x[(u, v)] for u, v in G.edges if (u in S and v not in S) or (v in S and u not in S)] x_cutsum = gp.quicksum(cut_x) model.cbLazy(x_cutsum >= 1) model.optimize(callback)
頂点数を64(8×8)に設定したときのGurobiのログの抜粋が以下のようになる。
ログを見ると、255本のカットセット不等式が追加されていることがわかる。
Nodes | Current Node | Objective Bounds | Work Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time 0 0 1703.50000 0 4 - 1703.50000 - - 0s 0 0 1715.50000 0 4 - 1715.50000 - - 0s 0 2 1726.50000 0 8 - 1726.50000 - - 0s * 2910 462 13 1861.0000000 1831.00000 1.61% 2.3 0s * 2923 462 9 1842.0000000 1831.00000 0.60% 2.3 0s * 3068 464 11 1834.0000000 1833.00000 0.05% 2.3 0s * 3541 0 12 1833.0000000 1833.00000 0.00% 2.4 0s Cutting planes: Lazy constraints: 255
カットセット不等式は本となるため、本質的に必要な制約が少ないことがわかる。
また、クラスカル法でもとめた重みと一致していることも確認できるため、最小全域木として必要な制約は追加できていることもわかる。
ただし、これでもMiller-Tucker-Zemlin制約を用いた定式化のような本質的に制約の本数が少ない定式化に比べると劣ってしまうため、定式化が難しい場合の最終手段ととらえるべきだろう。