Adaptive lifting: the triangle walk¶
For quadratic spin problems (MAX-CUT, Ising, QUBO written in \(x_i=\pm1\)) the order-2 relaxation is large, but most of
its strength over order 1 comes from a few triples of variables. moment_sos.walk grows the relaxation one batch of
triples at a time, so you can stop between order 1 and order 2 with a certified bound at every round.
A triple \(\{i,j,k\}\) stands for the three pair monomials \(x_ix_j,\ x_ix_k,\ x_jx_k\). Adding it makes the relaxation contain the four triangle inequalities of the moment matrix \(Y_{ij}=\mathbb E[x_ix_j]\),
A round picks \(K\) triples among those violated by the current solution and re-solves. The bound after every round is a valid, certified bound (it is the bound of a relaxation of the same problem), and bounds never increase.
import moment_sos as po
m = 7
s = po.variables(m)
problem = po.Problem(sum((1 - s[i]*s[(i + 1) % m]) / 2 for i in range(m)), boolean=True) # 7-cycle: max cut 6
for rule in ("nms", "topk"):
w = po.walk(problem, rounds=3, rule=rule, mode="basis")
print(rule, [round(b, 4) for b in w.bounds], "monomials added:", w.sizes[-1], w.stopped)
assert all(b2 <= b1 + 1e-6 for b1, b2 in zip(w.bounds, w.bounds[1:]))
assert abs(w.bounds[0] - 6.6534) < 1e-3 and abs(w.bounds[-1] - 6.0) < 1e-3 # order 1 -> order 2 = the optimum
Selection rules¶
|
which triples |
|---|---|
|
the \(K\) most violated (the standard cutting-plane rule) |
|
the most violated, no two sharing a pair (“non-maximum suppression”) |
|
\(K\) random triples (baseline) |
nms adds diversity: several triples that share a pair tend to cut off the same part of the moment matrix, so
topk can spend a whole batch on one defect. The rules and the walk come from the ai4opt study of learned lifting paths,
in which nms was consistently better than topk and both beat the learned policies at a matched budget.
Two ways to impose the triples¶
|
what is solved |
cost |
|---|---|---|
|
order-1 relaxation plus the four triangle inequalities of every selected triple, as scalar localizing blocks |
cheap blocks, but many of them |
|
order-1 moment block plus the pair monomials of the selected triples (partial order-2 lifting) |
the block grows by 3 rows per triple |
"cuts" drops triples whose inequalities become inactive after a round (drop_inactive=True). The two modes give different
relaxations: the example below on a 7-cycle shows that "basis" reaches the optimum in fewer rounds.
import moment_sos as po
m = 7
s = po.variables(m)
problem = po.Problem(sum((1 - s[i]*s[(i + 1) % m]) / 2 for i in range(m)), boolean=True)
cuts = po.walk(problem, rounds=1, rule="nms", mode="cuts")
basis = po.walk(problem, rounds=1, rule="nms", mode="basis")
print("one round:", round(cuts.bounds[1], 4), "(cuts)", round(basis.bounds[1], 4), "(basis)")
assert basis.bounds[1] <= cuts.bounds[1] + 1e-6
Options¶
w = po.walk(problem, rounds=10, per_round=None, rule="nms", mode="cuts", max_candidates=4000, time_limit=600)
per_roundis \(K\), default the number of variables \(n\).max_candidatesis the size of the pool of most violated triples the rules choose from.Extra keyword arguments go to
solve_alm(the primal moment matrix needed to find violations is read from its multipliers).WalkResulthasbounds(index 0 is order 1),times,sizes(active triples or pair monomials),triples,result(the lastResult, forround()),stopped("rounds","converged"or"time").
You can also use the pieces directly: po.triangle_violations(Y) returns the most violated triples of any matrix Y,
po.select_triples(Y, K, rule) applies a rule, and po.solve_alm(..., extra_basis=rows) adds arbitrary monomials (exponent
rows) to the moment block of a relaxation.
Limits¶
Only problems whose objective has degree at most 2 and whose variables are all spins. Other problems raise a clear error.
Violation-based rules stop when nothing is violated. On the complete graph \(K_5\) the order-1 optimum satisfies every triangle inequality, so the walk stops at the order-1 bound even though order 2 is stronger: its extra strength does not come from triangle inequalities.
It is not fast. Every round re-solves the relaxation with
solve_alm; for \(n=20\) one round with \(K=20\) triples takes tens to a few hundred seconds here, and the per-round cost grows with the number of active triples. The ai4opt study reports that a first-order ADMM for the same polyhedral relaxation is faster than every cutting-plane rule (their implementation; not reproduced here).The
"cuts"relaxation is a polyhedral strengthening of order 1 and is weaker than order 2 (its extra strength is only the triangle inequalities).
Benchmark (one graph, small)¶
G(20, 0.3), seed 1 (58 edges), \(K=20\) triples per round, 3 rounds, solve_alm. Order 1: 45.058; order 2: 43.0000 (= the maximum cut 43). Raw data: research/experiments/results/walk_gnp20.json; one machine, shared, single instance.
mode / rule |
round 1 |
round 2 |
round 3 |
total time |
size after round 3 |
|---|---|---|---|---|---|
cuts / nms |
43.803 |
43.462 |
43.211 |
785 s |
60 triples |
cuts / topk |
44.271 |
43.680 |
43.283 |
1660 s |
60 triples |
cuts / random |
44.594 |
44.402 |
44.223 |
377 s |
60 triples |
basis / nms |
43.598 |
43.005 |
43.000 |
2975 s |
149 pair monomials |
basis / topk |
44.263 |
43.431 |
43.000 |
190 s |
94 pair monomials |
What this does and does not show: nms has the tighter bound after every round in both modes, by a large margin in the first
round, and both beat random; in basis mode both reach the order-2 value after 3 rounds, but topk gets there with fewer
monomials (94 vs 149) and much less time (190 s vs 2975 s) on this instance. It is a single graph; no conclusion about
which rule is better overall should be drawn from it.