Part IV · trunk C · constraint programming and SAT · addendum
When a dead end has nothing to do with the last choice, go back to the choice that caused it. Why a search that skips subtrees this way still misses no solution, and why it always stops.
needs 19~1½ h slidesno lab
19b · overview
Why chronological search thrashes; frames, closed values, reasons; Prosser's jump rule, step by step.
Reasons are nogoods; one invariant; why an empty conflict set means "no solution"; learned nogoods.
A number that drops on every step, even when a jump sends the digits below it up.
The heuristic's contract, and why both proofs survive it; orders against CBJ, measured; a jump-target bug that tests in the input order cannot show; restarts; where the argument stops; optimization.
Words with a dotted underline are glossary terms: click one for its definition. Solid-underlined names are pieces of the running example, or of the other small instances: hover to see them. G lists every term in the unit, N shows the notes. Optional extra: interactive figures, linked from the slides they support by a small ▸ chip; skip them freely.
section 1
Go back to the choice that caused the failure.
19b · backjumping
The running example: $a, b, c, d \in \{1, 2\}$, constraints $a \ne c$, $a \ne d$, $c \ne d$. $b$ is in no constraint. Unsatisfiable: $a, c, d$ form a triangle, which needs three colours. Static order $a, b, c, d$.
Chronological backtracking undoes the most recent choice first. When the $c, d$ part fails under $a = 1, b = 1$, it tries $b = 2$ and fails in exactly the same way: $b$ played no part. That is thrashing, the same failure rediscovered under an irrelevant choice.
Conflict-directed backjumping (CBJ) sees that the failure below $b$ only blames $a$, and jumps straight back to $a$.
Chronological search under $a = 1$. Red: rejected, with the constraint that rejected it. No rejection involves $b$, so trying $b = 2$ (shaded) replays the identical failure. CBJ jumps from the exhausted $c$ frame straight back to $a$ (step 8) and never enters it. Under $a = 2$ the same happens again, mirrored.
19b · backjumping
A frame is one level of the stack: the variable chosen for that depth, its candidate values in order with a cursor at the current one, and a conflict set.
A value is closed at a frame when search is finished with it while the assignments above stay put: a constraint rejected it, a learned nogood matched it, or everything below it failed and a backjump landed here.
Each closed value has a reason: the earlier variables that caused it to be closed.
The conflict set of a frame starts empty when the frame is opened, and is the union of the reasons of its closed values.
| closed because | reason |
|---|---|
| rejected by a constraint | the constraint's other variables (assigned, earlier) |
| a deeper frame was exhausted and jumped here | that frame's conflict set, minus this frame's variable |
| a stored nogood matched | the nogood's other variables |
Stack $a = 1$, $b = 1$, $c = 2$, and a frame for $d$ with both values closed: $d = 1$ by $a \ne d$, reason $\{a\}$; $d = 2$ by $c \ne d$, reason $\{c\}$. Conflict set of $d$: $\{a, c\}$.
Frame $c$: $c = 1$ closed by $a \ne c$, reason $\{a\}$; cursor on $c = 2$. Frame $b$: nothing closed, conflict set empty. $b$ appears in no reason.
19b · backjumping
When every value of a frame is closed (the frame is exhausted), let $E$ be its conflict set.
Chronological backtracking is the same loop with the jump always going to the frame just above, and stopping only when the top frame is exhausted. It differs from step 8: "$c$ exhausted, back to $b$", and then it repeats the $c, d$ failure under $b = 2$, for $a = 1$ and again for $a = 2$.
| step | what happens | reason, or $E$ |
|---|---|---|
| 1–2 | descend $a = 1$, $b = 1$ | |
| 3 | reject $c = 1$ by $a \ne c$ | $\{a\}$ |
| 4 | descend $c = 2$ | |
| 5 | reject $d = 1$ by $a \ne d$ | $\{a\}$ |
| 6 | reject $d = 2$ by $c \ne d$ | $\{c\}$ |
| 7 | $d$ exhausted: jump to $c$ | $E = \{a, c\}$ |
| 8 | $c$ exhausted: jump to $a$, skipping $b$ | $E = \{a\}$ |
| 9–13 | $a = 2$, $b = 1$, $c = 1$; $d = 1$, $d = 2$ rejected | $\{c\}$, $\{a\}$ |
| 14 | $d$ exhausted: jump to $c$ | $E = \{a, c\}$ |
| 15 | reject $c = 2$ by $a \ne c$ | $\{a\}$ |
| 16 | $c$ exhausted: jump to $a$ | $E = \{a\}$ |
| 17 | $a$ exhausted: unsatisfiable | $E = \emptyset$ |
All 17 steps: hover the CBJ run; compare the chronological run.
section 2
Skipping a subtree is safe when the reason says it must fail.
19b · completeness
A reason $R$ for closing value $v$ of frame variable $x$, under the current assignment $A$: no solution agrees with $A$ on $R$ and has $x = v$.
Equivalently, $\{y = A(y) : y \in R\} \cup \{x = v\}$ is a nogood in unit 19's sense. That is what makes it safe to store.
Every closed value of every frame has a valid reason that names only variables of frames above it.
If every value of $x$ is closed, with reasons $R_v$, then no solution agrees with $A$ on $E = \bigcup_v R_v$.
Why: a solution gives $x$ some value $v$ of its domain, and agrees with $A$ on $R_v \subseteq E$: exactly what the reason for $v$ rules out.
So $E$ minus the jump target's variable is a valid reason for closing the target's value. Discarding the frames below the target breaks nothing: reasons mention only variables above their own frame, and those keep their values.
full proof in the notes (N)
$d = 1$ has reason $\{a\}$, $d = 2$ has $\{c\}$, so no solution has $a = 1$ and $c = 2$. Closing $c = 2$ with reason $\{a\}$ says exactly that.
19b · completeness
Take $E = \emptyset$ in the combining lemma: no solution agrees with $A$ on nothing, that is, there is no solution at all.
A conflict set is also empty when its frame is opened, and then it claims nothing. The conclusion needs both: every value closed, and $E$ empty.
At the top frame $E$ is necessarily empty, since there are no variables above it. It can also become empty deeper, and search stops there.
CDCL's learned clause is the same kind of object: a nogood written as a clause. Both go back past decisions it does not mention: CBJ to the deepest variable of $E$, closing its value; CDCL further up, to the second-highest level, asserting the flipped UIP. The empty clause is $E = \emptyset$.
Step 17: $a$'s two values were closed at steps 8 and 16, each with reason $\{a\} \setminus \{a\} = \emptyset$. Every value closed, $E = \emptyset$: unsatisfiable.
Fine print. "Every value" means every value of the domain. A value propagation removed under the current assignment is closed only if its reason goes into the conflict set; if not, $E = \emptyset$ can be reported on a satisfiable problem (notes).
19b · completeness
The invariant and the measure of section 3 do not care where a closing came from, so a store changes neither soundness nor the termination of a single run.
Step 7 closes $c = 2$ with reason $\{a\}$ under $a = 1$: the nogood $\{a = 1, c = 2\}$.
Step 8 closes $a = 1$ with reason $\emptyset$: the nogood $\{a = 1\}$, so $a = 1$ is in no solution.
The decision nogood at step 7 is $\{a = 1, b = 1, c = 2\}$: every choice above. It does not fire under $b = 2$. The reason form $\{a = 1, c = 2\}$ leaves $b$ out, so it does: the difference between the two is exactly the thrashing.
section 3
One number that goes down on every step.
19b · termination
$n$ variables, $D_{\max}$ the largest domain, radix $B = D_{\max} + 2$. For depth $d < n$, $r_d$ counts the values of its frame not yet closed, the current one included, or is $\top = B - 1$ if depth $d$ has no frame.
$$\mu = \sum_{d<n} r_d \, B^{\,n-1-d}$$The termination measure: the counts read as one number in base $B$, shallowest digit first.
| after step | digits, depth 0 1 2 3 | $\mu$ |
|---|---|---|
| start | 2 3 3 3 | 191 |
| 6 | 2 2 1 0 | 164 |
| 7, jump to $c$ | 2 2 0 3 | 163 |
| 8, jump to $a$ | 1 3 3 3 | 127 |
| 9, descend | 1 2 3 3 | 111 |
| 16, jump to $a$ | 0 3 3 3 | 63 |
Each step lowers the shallowest digit it changes, and changes only digits below it.
Lowering digit $j$ by one outweighs anything below it.
Why $+2$: $\top$ must exceed every open frame's $r_d \le D_{\max}$, so that opening a frame is a decrease.
full proof in the notes (N)
In the table, step 8: depths 1 and 2 go up (2 → 3, 0 → 3), but depth 0 drops from 2 to 1, which outweighs them: $163 \to 127$. Digits are per depth, not per variable.
section 4
Any variable, as long as it is a new one.
19b · variable ordering
In dynamic variable ordering the heuristic is consulted each time a frame opens. It may read anything: the assignments so far, the conflict history, unit 19's weights, randomness. Its one obligation: return an unassigned variable whenever one exists.
Returning a variable already on the stack: the stack's variables stop being distinct, the depth is no longer bounded by $n$, and $\mu$ is undefined.
The measure counts values per depth. On a descend, the new frame's digit drops from $\top$ to at most $D_{\max}$, whichever variable it holds. On a jump, the digits that change sit below the one that decreases.
The invariant names variables, not depths: "above" means above on the stack as it is now.
full proof in the notes (N)
Plain CBJ never shrinks a domain, so "smallest domain first" is a static order. A dynamic first-fail counts the live domain: the values still consistent with the assignments so far, computed only for choosing; the frame still opens on the full domain. dom/wdeg also reads the history, bumping the constraint that rejected a value: on the Mycielski graph (next slide) CBJ with dom/wdeg puts 10 different variables at depth 5 in one run, with dom 3. Both stop with the right answer.
19b · variable ordering
All four instances are unsatisfiable. Every run checks $\mu$ at every step, and its answer against brute force.
| steps | example | Mycielski | pigeons | random | ||||
|---|---|---|---|---|---|---|---|---|
| chron. | CBJ | chron. | CBJ | chron. | CBJ | chron. | CBJ | |
| input | 33 | 17 | 1888 | 1175 | 325 | 325 | 468 | 174 |
| max-card. | 15 | 15 | 1312 | 1050 | 325 | 325 | 164 | 91 |
| dom | 15 | 15 | 568 | 568 | 325 | 325 | 72 | 64 |
| dom/deg | 15 | 15 | 652 | 652 | 325 | 325 | 68 | 68 |
| dom/wdeg | 15 | 15 | 700 | 659 | 325 | 325 | 68 | 68 |
Under the input order CBJ saves 16, 713 and 294 steps on the example, the Mycielski graph and the random CSP; under dom, 0, 0 and 8. Ordering and backjumping attack the same waste: a good order leaves CBJ less to save. On the pigeons there is nothing to skip under any order.
19b · variable ordering
By stack position: the deepest frame on the stack whose variable is in $E$. This is the rule the proofs use.
By index: the variable of $E$ that comes last in the input order. A CBJ written to search in the input order may do this, since there index order is depth order.
It can jump to a frame above the deepest frame in $E$. Then it merges $E$ minus the target's variable into the target's conflict set: a set naming a variable below the target, whose frame the jump discards. The invariant is broken.
Stack $v_5, v_3, v_0, v_2, v_4, v_1$. Step 10: $v_1$ is exhausted with $E = \{v_0, v_3\}$.
| index rule, 3 000 random CSPs | input order | random order |
|---|---|---|
| the jump looks for a discarded variable: crash | 0 | 319 |
| such variables dropped: a satisfiable instance called unsatisfiable | 0 | 31 |
A static order is not enough: under max-cardinality, static but not the input order, 159 crashes and 12 wrong answers.
19b · variable ordering
Keep an array depth[$x$], set when $x$ is assigned and cleared when it is unassigned. The jump target is the frame at the largest depth[$x$] over $x \in E$.
After each step, assert: every variable in a frame's conflict set is on the stack above that frame. On trial 291 the index rule trips it at step 10: $v_3$'s set, at depth 1, is $\{v_0\}$, and above it is only $v_5$.
When the search follows the input order the two rules coincide, so tests that always search in index order cannot see the bug. Test with other orders, dynamic ones included, and assert the invariant.
| 3 000 random CSPs | input order | random order |
|---|---|---|
| index rule: the check fires | 0 | 417 |
| index rule, dropping: wrong answers | 0 | 31 |
| stack rule: wrong answers | 0 | 0 |
| stack rule: the check fires | 0 | 0 |
With the check on, the index rule is stopped in all 31 wrong-answer runs before it answers. It also fires on runs that crash, and on runs that break the invariant and still, by luck, answer correctly.
19b · variable ordering
Each run starts from a fresh stack; a cutoff counts steps; the instance is fixed, so $n$ and $B$ never change. Dom/wdeg's weights, and a store of nogoods written out as values, may carry over.
A restart sends $\mu$ back up. But a run that is not cut off stops within $B^n$ steps, whatever the order. The Luby sequence (unit 19) is unbounded, so some run gets a cutoff of at least $B^n$, and that run finishes. Every answer is sound, so the loop is complete.
full proof in the notes (N)
A triangle $a, c, d$, plus $a \ne b$ and $b \ne e$, all in $\{1, 2\}$; CBJ with dom/wdeg, weights kept, Luby cutoffs times 4. Each run restarts at $\mu = 767$. From run 4 the weights make the order open $c$ after $a$, not $b$; run 7 has room for its 15 steps.
19b · scope
If the cutoffs stay below $B^n$, no run need finish, and the last slide's argument says nothing. Termination then needs the learned nogoods: if every run adds one the store did not have and none is forgotten, there can be only finitely many runs.
Reordering the assigned variables is a different algorithm. It moves the culprit variable without erasing the assignments in between, so the per-depth counters no longer decrease. Its termination argument rests on the recorded nogoods instead.
For any search that keeps a stack of frames, closes values only with valid reasons that point upwards, jumps by Prosser's rule, opens frames on unassigned variables in any order, and restarts, if at all, with unbounded cutoffs: sound in both answers, terminating, hence complete.
19b · optimization
To minimise $f$, keep the best solution found so far, the incumbent, and its cost $U$. Treat $f < U$ as a constraint that tightens each time a solution is found.
A reason found under $f < U$ says: no solution agrees with $A$ on $R$, has $x = v$ and costs less than $U$. $U$ only goes down, so that stays true under every later bound. The invariant holds, and the combining lemma with it.
Recording a solution closes a value like any rejection, so the measure of section 3 still drops on every step.
Reasons from the bound, and nogoods learned from them, are true relative to the bound, not of the problem. They are safe to keep while $U$ only decreases; not if the objective or the question changes.
19b · optimization
$a, b, c \in \{1, 2\}$, constraint $a \ne c$, minimise $f = a + 2c$. As in the running example, $b$ is in nothing. Static order $a, b, c$.
Optimum: $a = 2$, $c = 1$, cost $4$ (the only other option, $a = 1$, $c = 2$, costs $5$).
CBJ stops after 11 steps, chronological branch and bound after 21: it tries $b = 2$ under both values of $a$, and fails on $c$ in the same way each time.
Step 5: the closing of $a = 1$ carries reason $\emptyset$ under $U = 5$. It is still true at $U = 4$: there is no solution with $a = 1$ and cost below 4 either.
The tree chronological branch and bound visits. Green: a solution, which sets $U$ and closes its value with reason $\{a\}$. Red: rejected, by $a \ne c$ or by the bound. Once $c$ is exhausted (steps 5 and 10), CBJ jumps straight back to $a$ and never enters the shaded subtrees.
19b · scope
Explain each dead end by a nogood that names only the choices that caused it; go back past the choices it does not name; an empty nogood proves there is no solution. Unit 20's learned clause is such a nogood, written as a clause.
CBJ jumps only when a frame is exhausted, and what it carries up is that frame's conflict set: the union of the reasons of its closed values.
CDCL analyses every conflict at once, resolving back through the implication graph to the first UIP: one literal of the current level, the rest from earlier levels.
CBJ goes to the deepest variable of $E$ and closes its value; the next value is a new choice.
CDCL goes to the second-deepest level of the clause, where the clause is unit and forces the flipped UIP: an implication, not a decision.
This deck's store is checked when a value is tried: a hit closes that value. CDCL's clauses join unit propagation, so they fix values before anything is tried, and serve as reasons in later conflict analysis.
Around that, CDCL adds propagation at every node, VSIDS, frequent restarts that keep what was learned, and LBD deletion (unit 20). Unit 21 brings the whole engine back to CP.
19b · checkpoint
Prosser, "Hybrid algorithms for the constraint satisfaction problem", Computational Intelligence 9(3), 1993.
Ginsberg, "Dynamic backtracking", JAIR 1, 1993.
Dechter, Constraint Processing, 2003, the look-back chapter.
Chen & van Beek, "Conflict-directed backjumping revisited", JAIR 14, 2001.
20 SAT and CDCL: the same ideas with clauses, where the reasons come from unit propagation and the nogoods are kept.