Why does encoding a hard scheduling problem for a mature satisfiability solver usually beat writing your own backtracking search?
answer
- who writes the search
- you keep the modelling
- the conflict is learned once
- jump back, not step back
- clause count of a pairwise rule
basics
~20 sA mature solver contributes decades of search engineering you will not reproduce: constraint propagation, conflict analysis that learns a clause and jumps back, activity-driven branching and restarts. Your remaining job is the encoding, and the encoding then becomes the thing to get right.
solid answer
~40 sHanding the problem to a mature satisfiability solver changes what you are responsible for. A hand-written backtracker typically has branching and chronological backtracking and nothing else. A conflict-driven solver adds **unit propagation** over a cheap watched-literal scheme, **conflict analysis** that derives a learned clause explaining the contradiction and **backjumps** past the irrelevant decisions, **activity-based branching** that focuses on variables involved in recent conflicts, and **restarts** that keep every learned clause. The learned clause is implied by the original formula, so it prunes elsewhere in the tree without changing the answer set. What you keep is the **encoding**: what a variable means, how at-most-one and counting constraints are expressed, whether symmetry is broken. Encoding quality dominates runtime, and a bad encoding is now your bug, not the solver's.
go deeper
Recall that mature solvers exist for satisfiability and constraint problems, and that expressing your problem for one is usually better than writing your own exhaustive search from scratch.
Explain the machinery you inherit: propagation, a clause learned from each conflict, backjumping and restarts, and why the learned clause prunes without changing which assignments satisfy the formula.
Demonstrate ownership of the model: pick encodings for at-most-one and counting rules, break symmetry, iterate for optimisation, and diagnose an unsatisfiable answer by minimising the constraint set.
Decide when to adopt one at all. Weigh the modelling skill it concentrates in few people, erratic runtimes against a nightly window, and whether the objective is crisp enough to be worth encoding exactly.
## What you are handing over The decision is not *search versus no search* — both approaches search. It is **who writes the search**. A hand-rolled backtracker over a scheduling problem usually amounts to: pick an unassigned decision, try each option, recurse, and on contradiction undo the last choice. That is where most in-house implementations stop, because everything past it is hard to build and harder to keep correct. ## What a conflict-driven solver adds - **Unit propagation.** Whenever a constraint has one remaining way to be satisfied, the solver fixes it immediately and cascades. Implemented with a watched-literal scheme, this costs almost nothing per assignment and does most of the real work. - **Conflict analysis and clause learning.** On reaching a contradiction the solver analyses *why*, derives a new clause that rules out the responsible combination, and adds it. The learned clause is logically implied by the original formula, so the solution set is unchanged — but the same contradiction is never re-derived, anywhere else in the tree. - **Backjumping.** Rather than undoing the most recent decision, the solver jumps back to the decision level the conflict actually depended on, discarding irrelevant work between. - **Activity-based branching.** Variables that appear in recent conflicts are branched on first, so the search concentrates where the instance is genuinely hard. - **Restarts that keep what was learned.** The assignment trail is abandoned, the learned clauses are not. This re-samples the search order while retaining the pruning bought so far. A plain backtracking search re-derives the same contradiction once per subtree that contains it. Learning it once is the single biggest difference between the two approaches. | | Hand-written backtracker | Mature conflict-driven solver | |---|---|---| | Contradiction | undo last choice, retry | learn a clause, jump back | | Same conflict elsewhere | re-derived every time | ruled out once, globally | | Branching order | fixed by your code | driven by recent conflicts | | Your responsibility | the whole search | the encoding | ## The encoding becomes the work Handing search over does not hand over modelling. You now choose what each variable *means* and how each rule is expressed, and those choices swing runtime by orders of magnitude. - **At-most-one constraints.** The direct encoding forbids every pair, which over `k` candidates is `k(k-1)/2` clauses — for `k = 100` that is 4,950 clauses for a single rule, and the quadratic growth soon dominates the input. Compact alternatives introduce auxiliary variables and encode the same rule in a number of clauses that grows far more slowly. - **Counting and cardinality rules.** Naive expansions explode; structured encodings stay manageable and often propagate better, which matters more than raw clause count. - **Symmetry.** If two interchangeable resources make two assignments that differ only by a relabelling, the search may explore both. Adding constraints that fix an arbitrary order among symmetric objects removes that duplicated work. - **Propagation-friendliness.** Two encodings with the same solution set can differ sharply in how early propagation detects a dead branch. Prefer the one whose forced consequences fire soon. ## What you give up 1. **A decision procedure is not an optimiser.** A satisfiability answer is *satisfiable with this assignment* or *unsatisfiable*. To minimise a cost you solve, add a constraint requiring a strictly better cost, and solve again — each round either improves the incumbent or proves the previous one optimal. 2. **Runtimes are erratic.** Nearly identical instances can differ enormously in solve time, so nightly capacity planning must assume the tail, not the median. 3. **Structure is flattened.** Rich domain structure becomes undifferentiated variables and clauses, and a solver cannot exploit what your encoding threw away. 4. **Debugging moves.** An unsatisfiable answer is now ambiguous between *the requirement really is impossible* and *my encoding says something I did not mean*. Keeping a minimal reproducing subset of constraints, and testing the model against known-feasible plans, is how you tell those apart. ## Which solver, though The question of which solver matters and is often glossed. A **satisfiability solver** takes a formula over Boolean variables in conjunctive normal form; everything else you express by encoding. A **constraint solver** keeps variables with richer finite domains and supports global constraints — for example *all these assignments must differ* — each with a specialised propagator that reasons about the whole group at once, which a flattened Boolean encoding cannot do as sharply. Where a problem is naturally about distinctness, capacities and intervals, the richer model often wins; where it is naturally Boolean and conflict-heavy, clause learning often wins. Naming that choice, rather than saying *use a solver*, is what a senior answer sounds like.
- A learned clause is added to the formula during solving. Why does that not change the set of solutions?Because the clause is derived by analysing a conflict among constraints already present, so it is logically implied by the original formula. Every assignment that satisfied the formula before still satisfies it. What changes is pruning: the combination that caused the conflict can no longer be re-explored anywhere else in the tree.
- A satisfiability solver answers yes or no. How do you get a minimum-cost schedule out of it?Iteratively. Solve for feasibility, record the cost of the assignment returned, add a constraint demanding a strictly lower cost, and solve again. Each round either produces a better plan or returns unsatisfiable, which proves the previous plan optimal. Stopping early leaves a feasible plan whose optimality is unproven.
- The solver reports unsatisfiable on a schedule the operations team says they run by hand. What now?Treat it as an encoding bug until proven otherwise. Feed the hand-built schedule into the model as fixed assignments and see which constraint rejects it; then shrink the constraint set to a minimal subset that still reports unsatisfiable. One of those constraints says something stricter than the business rule it was meant to express.
saying these in an interview costs you the question
- Believing a solver avoids exponential behaviour entirely
- Treating the encoding as clerical work with no effect on runtime
- Expecting a satisfiability answer to include a minimum-cost solution
- Claiming an unsatisfiable answer proves the business requirement is impossible
- Assuming two encodings with identical solution sets solve equally fast
- Planning nightly capacity from a median solve time on erratic runtimes