First-UIP conflict analysis
In a conflict-driven clause-learning SAT solver, conflict analysis turns each conflict into a new clause that records why the conflict happened[1]. When unit propagation falsifies a clause, every literal of that clause is false because of earlier decisions and propagations, which are recorded in an implication graph: each propagated literal points back to the literals that forced it through its reason clause. The analysis resolves the conflicting clause with the reasons of its literals, working backwards along the trail of assignments, until the clause contains exactly one literal assigned at the current decision level. That literal is the first unique implication point, first UIP. The derived clause is added to the formula as a learned clause, and the solver backtracks to the second-highest decision level among its literals, where the learned clause becomes unit and forces the UIP's negation. First-UIP learning was identified as the most effective scheme by Zhang, Madigan, Moskewicz and Malik in 2001[2], and is used by essentially all CDCL solvers.
The implication graph
During search, each assigned literal carries a decision level, the number of decisions made when it was assigned, and a reason: nothing for a decision, or the clause that became unit and forced it. The implication graph has a node for each assigned literal and an edge from each false literal of a reason clause to the literal it forced; a conflict adds a node with edges from the literals of the falsified clause[1][7].
Consider the clauses
with false at level 1, and false at level 3, and the decision at level 6. Propagation sets through true, and becomes false.
Deriving the clause
A unique implication point is a node of the current level that lies on every path from the level's decision to the conflict. The decision is always one; the first UIP is the one closest to the conflict. Any cut of the graph that separates the conflict from the decisions gives a learned clause, the disjunction of the negations of the literals with an edge crossing the cut. Every assignment that makes those literals true leads by propagation to the same conflict, so the clause is implied by the formula. Choosing the cut just after the first UIP gives the first-UIP clause[2].
Solvers compute it by resolution rather than by graph search. Starting from the conflicting clause, they repeatedly pick the literal of the current level assigned last, resolve with its reason clause, and stop when one literal of the current level remains. Each step is a resolution step, so the learned clause is derivable from the formula by a resolution proof[3].
Conflict analysis on an explicit trail, printing each resolution step.
(* Conflict analysis on an explicit trail. Each entry is a literal madetrue, its decision level, and the clause that implied it (None for adecision). *)type entry = { lit : int; level : int; reason : int list option }let show_clause c = "(" ^ String.concat " ∨ " (List.map (fun l -> (if l < 0 then "¬x" else "x") ^ string_of_int (abs l)) c) ^ ")"(* Pad to a width in characters, not bytes. *)let pad n s =let chars = ref 0 inString.iter (fun ch -> if Char.code ch land 0xC0 <> 0x80 then incr chars) s;s ^ String.make (max 0 (n - !chars)) ' '(* Resolve on variable v: the clauses contain v with opposite signs. *)let resolve a b v =List.sort_uniq compare (List.filter (fun l -> abs l <> v) (a @ b))(* Walk the trail backwards, resolving the conflicting clause with thereason of each current-level literal it contains, and stop as soon asthe clause has exactly one literal of the current level (a UIP), orcontinue to the decision if stop_at_first is false. *)let analyze ?(stop_at_first = true) trail conflict =let level_of v = (List.find (fun e -> abs e.lit = v) trail).level inlet current = (List.hd (List.rev trail)).level inlet at_current c = List.filter (fun l -> level_of (abs l) = current) c inlet rec go c = function| [] -> c| e :: earlier ->let v = abs e.lit inlet done_ = List.length (at_current c) = 1 && stop_at_first inif done_ || not (List.mem (-e.lit) c) then (if done_ then c else go c earlier)else match e.reason with| None -> c (* reached the decision *)| Some r ->let c' = resolve c r v inPrintf.printf " resolve on x%-2d with %s -> %s\n" v (pad 18 (show_clause r)) (show_clause c');go c' earlieringo conflict (List.rev trail)
The example above, stopping at the first UIP and continuing to the decision.
open Analysis(* The clauses of the example, with x9, x10, x11 set false at lowerlevels and x1 decided true at level 6. *)let w1 = [ -1; 2 ] and w2 = [ -1; 3; 9 ] and w3 = [ -2; -3; 4 ]and w4 = [ -4; 5; 10 ] and w5 = [ -4; 6; 11 ] and w6 = [ -5; -6 ]let trail = [{ lit = -9; level = 1; reason = None };{ lit = -10; level = 3; reason = None };{ lit = -11; level = 3; reason = None };{ lit = 1; level = 6; reason = None };{ lit = 2; level = 6; reason = Some w1 };{ lit = 3; level = 6; reason = Some w2 };{ lit = 4; level = 6; reason = Some w3 };{ lit = 5; level = 6; reason = Some w4 };{ lit = 6; level = 6; reason = Some w5 } ]
Running it.
conflict in (¬x5 ∨ ¬x6)first UIP:resolve on x6 with (¬x4 ∨ x6 ∨ x11) -> (¬x5 ∨ ¬x4 ∨ x11)resolve on x5 with (¬x4 ∨ x5 ∨ x10) -> (¬x4 ∨ x10 ∨ x11)learned (¬x4 ∨ x10 ∨ x11): jump back to level 3, where it forces ¬x4resolving back to the decision:resolve on x6 with (¬x4 ∨ x6 ∨ x11) -> (¬x5 ∨ ¬x4 ∨ x11)resolve on x5 with (¬x4 ∨ x5 ∨ x10) -> (¬x4 ∨ x10 ∨ x11)resolve on x4 with (¬x2 ∨ ¬x3 ∨ x4) -> (¬x3 ∨ ¬x2 ∨ x10 ∨ x11)resolve on x3 with (¬x1 ∨ x3 ∨ x9) -> (¬x2 ∨ ¬x1 ∨ x9 ∨ x10 ∨ x11)resolve on x2 with (¬x1 ∨ x2) -> (¬x1 ∨ x9 ∨ x10 ∨ x11)learned (¬x1 ∨ x9 ∨ x10 ∨ x11)
Backjumping
The learned clause contains exactly one literal of the current level, , and its other literals were assigned at earlier levels. The solver undoes assignments back to the highest of those levels, here level 3, rather than just to level 5. At level 3, and are still false, so the learned clause is unit and forces immediately. This is non-chronological backjumping: decisions at levels 4 and 5, which had nothing to do with the conflict, are discarded, and the implication is placed as early in the search as its justification allows[1]. Because the clause is asserting, with a single literal at the conflict level, the solver never needs to flip a decision explicitly as DPLL does.
Why the first UIP
Any UIP gives an asserting clause, and so do cuts containing several literals of the current level if the solver is prepared to handle them. Zhang and co-authors compared a range of schemes in 2001, including learning a clause of decisions only and learning a clause for every UIP, and found the first UIP best on their benchmarks: its clause is usually short, it is closest to the conflict and so most specific, and it backjumps at least as far as the others[2].
First-UIP learning against resolving all the way to the decision at the conflict level, in the same solver.
(* First-UIP learning against resolving back to the decision, the lastUIP, in the same solver, with the average learned clause length. *)let run cfg f =let total = ref 0 and n = ref 0 inlet setup (s : Cdcl.t) = s.on_learn <- (fun c -> total := !total + Array.length c; incr n) inlet r, st = Cdcl.solve ~cfg ~setup f in(r, st.Cdcl.conflicts, float !total /. float (max 1 !n))
Conflicts and average learned clause length.
instance first UIP: conflicts, length last UIP: conflicts, lengthrandom 3-SAT, 80 vars 169 6.0 236 7.0random 3-SAT, 100 vars 442 7.6 590 8.1random 3-SAT, 120 vars 1432 9.4 1576 10.4pigeonhole, 7 in 6 758 13.2 889 14.6pigeonhole, 8 in 7 4164 19.9 6129 21.6
On these formulas the first-UIP clauses are shorter and the solver needs 10 to 30% fewer conflicts. The difference is larger on structured industrial formulas, where implication chains are long.
Refinements
Minimization
A literal of the learned clause is redundant if its negation is implied by the negations of the other literals through reason clauses. Removing such literals, by checking reasons recursively, shortens learned clauses by roughly 30% on typical industrial problems[4].
Clause quality
Learned clauses accumulate, and solvers periodically delete many of them. Audemard and Simon measured the quality of a learned clause by its literal block distance (LBD), the number of different decision levels among its literals, and found that clauses of low LBD, especially those with LBD 2, called glue clauses, are the most useful to keep[6].
History
Explaining a failure by the assumptions it depended on, and backtracking to the relevant one, goes back to dependency-directed backtracking in Stallman and Sussman's circuit analysis program of 1977[9]. Marques-Silva and Sakallah brought the idea to SAT with GRASP, first presented in 1996, which introduced the implication graph, unique implication points, learned clauses and non-chronological backtracking[1]. Bayardo and Schrag's rel_sat of 1997 combined learning with look-back techniques from constraint satisfaction[8]. Zhang, Madigan, Moskewicz and Malik's comparison of learning schemes in 2001 established the first UIP[2], MiniSat's implementation became the reference[5], and clause minimization and LBD followed in 2009[4][6]. Beame, Kautz and Sabharwal related the learned clauses to resolution proofs[3].
see also
- CDCLConflict-driven clause learning (CDCL) is the algorithm of modern SAT solvers. It extends DPLL backtracking search with three ideas: when propagation falsifies a clause, conflict analysis derives a new clause that explains the conflict and adds it to the formula; the solver then backjumps non-chronologically to the decision level at which the new clause forces a literal; and branching follows the variables involved in recent conflicts. With watched literals, restarts and clause deletion, CDCL solvers decide industrial formulas with millions of variables, and with restarts they are as strong as general resolution, exponentially stronger than DPLL.
- ResolutionResolution is the inference rule that derives, from two clauses containing a variable with opposite signs, the clause of all their other literals: from C ∨ x and D ∨ ¬x, infer C ∨ D. It is sound and refutation complete for propositional logic, so a set of clauses is unsatisfiable exactly when the empty clause can be derived. Resolution is the proof system behind SAT solvers, whose runs on unsatisfiable formulas are resolution refutations, and behind first-order theorem provers and logic programming. Some formulas, such as the pigeonhole formulas, have only exponentially long resolution refutations.
- VSIDSVSIDS, Variable State Independent Decaying Sum, is the branching heuristic of conflict-driven SAT solvers. Each variable has an activity score, which is increased whenever the variable takes part in a conflict and decays geometrically over time; when the solver must make a decision, it branches on the unassigned variable with the highest activity. The heuristic is cheap, because it depends on conflicts rather than on the current formula, and it focuses the search on the variables involved in recent conflicts. It was introduced by the Chaff solver in 2001 and, in the exponential form used by MiniSat, remains the default in most solvers.
- Unit propagationUnit propagation is the inference rule at the core of SAT solvers: when every literal of a clause is false under the current partial assignment except one unassigned literal, that literal must be true. Applying the rule until nothing changes either extends the assignment or finds a clause with every literal false, a conflict. It decides satisfiability of Horn formulas on its own, it takes most of the running time of a modern CDCL solver, and it is implemented with two watched literals per clause so that only clauses that may have become unit are ever inspected.
- Craig interpolationCraig's interpolation theorem states that if a formula A implies a formula B, there is a formula I, the interpolant, built only from the symbols A and B have in common, such that A implies I and I implies B. In verification it is used in the equivalent refutation form: when A ∧ B is unsatisfiable, an interpolant is implied by A, inconsistent with B, and mentions only shared variables. For propositional logic, an interpolant can be computed from a resolution refutation of A ∧ B in linear time, which McMillan used in 2003 to build an unbounded model checker from a SAT solver.
further reading
- [1]J. P. Marques-Silva, K. A. Sakallah, “GRASP: a search algorithm for propositional satisfiability”, IEEE Transactions on Computers 48 (1999).
- [2]L. Zhang, C. F. Madigan, M. W. Moskewicz, S. Malik, “Efficient conflict driven learning in a Boolean satisfiability solver”, ICCAD (2001).
- [3]P. Beame, H. Kautz, A. Sabharwal, “Towards understanding and harnessing the potential of clause learning”, Journal of Artificial Intelligence Research 22 (2004).
- [4]N. Sörensson, A. Biere, “Minimizing learned clauses”, SAT, Lecture Notes in Computer Science 5584 (2009).
- [5]N. Eén, N. Sörensson, “An extensible SAT-solver”, SAT, Lecture Notes in Computer Science 2919 (2004).
- [6]G. Audemard, L. Simon, “Predicting learnt clauses quality in modern SAT solvers”, IJCAI (2009).
- [7]J. Marques-Silva, I. Lynce, S. Malik, “Conflict-driven clause learning SAT solvers”, in A. Biere et al. (eds.), Handbook of Satisfiability, IOS Press (2nd ed., 2021).
- [8]R. J. Bayardo, R. C. Schrag, “Using CSP look-back techniques to solve real-world SAT instances”, AAAI (1997).
- [9]R. M. Stallman, G. J. Sussman, “Forward reasoning and dependency-directed backtracking in a system for computer-aided circuit analysis”, Artificial Intelligence 9 (1977).
last updated