Exercises — Module 17 (ZDDs and Exact Cover with Colors)
Self-contained problems on this module's material — the zero-suppressed decision diagram and its canonical form, the family algebra (, , , ), counting structures in graphs, and Algorithm 7.2.2.1C (dancing links with colors). You can work every one without the books: each states the problem in full, gives a hint to peek at when stuck, and a worked answer sketch to check against after you try. Computational answers here are reproduced by the code you write in the lab (or a few lines at a REPL).
Ratings follow Knuth's scale (00–50; M = needs mathematics, ▶ = especially
instructive). Exercise IDs are the module's content tags (match by content in
your printing of §7.1.4 / §7.2.2.1).
Tracking
| # | Topic | Rating | Status |
|---|---|---|---|
| 1 | BDD vs. ZDD of : which nodes each elides | 18 | ⬜ |
| 2 | ▶ Canonicity for : all 16 families over | 25 | ⬜ |
| 3 | Member -path bijection, carefully | 20 | ⬜ |
| 4 | ▶ The semiring laws, and a join blowup family | 25 | ⬜ |
| 5 | restrict_with / restrict_without, proved via the split | 22 | ⬜ |
| 6 | ▶ Simpath-style frontier ZDD for – paths | 35 | ⬜ |
| 7 | Order-4 Latin squares: pin the count | 22 | ⬜ |
| 8 | ▶ LIFO exactness of purify/unpurify, by induction | 25 | ⬜ |
| 9 | Why branching on a secondary item is wrong | 20 | ⬜ |
| 10 | ▶ Emit the XCC solution family as a ZDD | 40 | ⬜ |
Problems
1. BDD vs. ZDD of : which nodes each elides (rating 18 · §7.1.4-(z-vs-b))
Problem. Take the family over the universe , with variable order . Draw both the reduced ordered BDD (Module 13's rule: elide any node with LO HI) and the ZDD (Module 17's rule: never create a node with HI , keep LO HI). For each diagram say which nodes are elided and why, and count the nodes.
Hint. As a function, is true only at the three points , , — and at each of those points . Ask, for each structure: does variable matter, and does the structure's deletion rule mean " is don't-care" or " is absent"?
Answer sketch. The ZDD splits on : members without are , members with (with removed) are . Recursing gives the chain
three branch nodes plus the two sinks — . Variables never appear: they are absent from every member, and under zero-suppression a skipped variable defaults to "absent," so they cost literally nothing. The BDD cannot elide them — flipping from to turns a member into a non-member, so genuinely matters and must be branched on to force it to . Building the reduced BDD of the same function over all six variables gives 10 nodes (it elides only genuine LO HI nodes, none of which are the tests). The moral of §1: for a sparse family the ZDD pays for support, not universe; the BDD pays for the universe.
2. ▶ Canonicity for : all 16 families over (rating 25 · §7.1.4-(canon-z))
Problem. Instantiate §1's canonical-form induction at . There are exactly families of subsets of ; write each one's unique reduced zero-suppressed diagram, and confirm the diagrams are pairwise distinct.
Hint. The universe has subsets (), so a family is any subset of that -element powerset: of them. Build each with the split rule , where = members without and = members with , removed.
Answer sketch. families, each denoted by exactly one diagram (write ; are the sinks):
| family | diagram |
|---|---|
The two sinks handle the base case ( vs. , distinct); every other family has a root -node whose LO denotes and HI denotes , unique by induction, unique by the hash table. Distinct families give distinct splits, hence distinct diagrams — exactly the theorem. Note keeps a LO HI node : " is optional," the very information a BDD would throw away.
3. Member -path bijection, carefully (rating 20 · §7.1.4-(count-z))
Problem. Prove §1's lemma: the member sets of the family are in
bijection with the directed paths from node to the sink, where a path
is read by recording, at each node it passes through, whether it took the HI
edge (branch variable is in the set) or the LO edge (variable not in the
set). Include the argument that distinct members give distinct paths, and
conclude that count_sets is , , .
Hint. Induct on the diagram structure using the defining semantics
. The walk that
contains_set performs is the forward map; reading off the HI-choices along a
path is the inverse.
Answer sketch. Forward. Given , run contains_set: at node
, take HI iff . By the semantics this reaches (removing
when present keeps a member of the child family), tracing one path. Back.
Given a path to , let be the set of branch variables at which it took
HI; by the same semantics, walking that path certifies . Injective.
If , they differ at some smallest variable ; because variables are
tested in increasing order and each is tested at most once on any path, the two
walks agree until the node testing , where one takes HI and the other LO —
so the paths diverge and are distinct. The two maps invert each other, giving a
bijection. Since the paths through a node split disjointly into those through
LO and those through HI, the number of members satisfies with , ; memoized it
is additions — no factor, because a skipped variable
is absent (one possibility), not free (two).
4. ▶ The semiring laws, and a join blowup family (rating M25 · §7.1.4-(join-alg))
Problem. (a) Show that families of sets, with union as addition and join as multiplication, form a commutative semiring: is commutative, associative, with identity ; is commutative, associative, with identity ; distributes over ; and annihilates (). (b) Show when and have disjoint supports. (c) Exhibit over a shared support whose join has vastly more members than the node counts would suggest.
Hint. For (a) work from the definition directly — distributivity over is immediate from it. For (b) the disjoint supports let you recover from by restricting to 's support, so the map is injective. For (c) recall §3's power-set construction .
Answer sketch. (a) Commutativity/associativity of are set facts;
. For : gives commutativity;
associativity; is the identity; (no
to choose) annihilates. Distributivity: . So it is a
commutative semiring. (b) If supports are disjoint, determines
(intersect with 's support) and , so is a
bijection onto : . (c) Take ,
the power set of . Its ZDD is a chain of nodes with LO
HI (so with sinks), yet . And (the
power set is closed under union and contains ), so a join of two
-node diagrams denotes members — verified with count_sets.
The lesson of §1's "attractive nuisance": member count and node count are
uncoupled; the ZDD is small exactly when the family is structured, not when it
is small.
5. restrict_with / restrict_without, proved via the split (rating 22 · §7.1.4-(univ))
Problem. Design two one-pass memoized recursions:
restrict_with(f, v) returning the subfamily of members that contain , and
restrict_without(f, v) returning those that do not. Give the recursion in
terms of the top-variable split and prove correctness. (Both return families
over the original universe — restrict_with keeps in each surviving member.)
Hint. Compare 's top variable with the target . Three cases: , , . Under zero-suppression, "'s top is already past " tells you something definite about whether any member contains .
Answer sketch. Write at its top variable , so (members without ) and (members with
, removed). For restrict_without(f, v): if , recurse into
both children — ; if ,
every member through HI contains , so drop it — return ; if (or
), is below the top, so no member contains it and all
survive — return . For restrict_with(f, v): if , recurse
; if , keep only the
HI side but reattach — return ; if or a
sink, no member contains — return . Correctness is the split
semantics case by case: at the HI branch is exactly "members containing
." Both are under memoization. (Sanity: restrict_with(f,v) and
restrict_without(f,v) are disjoint and union to .)
6. ▶ Simpath-style frontier ZDD for – paths (rating 35 · §7.1.4-(frontier))
Problem. The §3 algebra (power set, then filter conflicts) is quadratic in constraints. Implement instead a frontier-based construction: sweep the edges of a grid graph one at a time, maintaining a frontier of partial-state equivalence classes (each vertex on the boundary tracked by its current degree and connected-component mate), and build the ZDD of all simple – paths directly in one pass. Validate the member count against a brute-force path-enumeration on small grids.
Hint. Process edges in a fixed order. The frontier is the set of vertices that have an already-processed incident edge and an unprocessed one; for each you need its path-degree (, , or ) and, via a union-find "mate" array, which frontier endpoint it is connected to. When an edge decision would create a degree- vertex, a cycle, or strand /, route that branch to ; when a vertex leaves the frontier in a bad state, prune. Merge equivalent frontier states to share nodes.
Answer sketch. This is Knuth's Algorithm S (Simpath) specialized to –
paths. Correctness rests on two facts: (i) the frontier state is a sufficient
statistic — two partial edge selections with the same frontier degrees and
mate-structure extend to exactly the same completions, so merging them loses
nothing; (ii) each edge contributes one ZDD level (variable = that edge), and
the canonical-form theorem guarantees that whatever order you build in, the
finished diagram is the same one the §3 algebra would produce — canonicity is
route-independent. Validation: for the grid (, opposite
corners) enumerate all edge subsets by brute force, keep those that form a
simple – path, and check the total equals count_sets of the frontier
ZDD; repeat on . Because the frontier width, not the constraint
count, bounds the work, this is the technique that scales to the -path
grids of §6 — the same finished diagram, a very different route. (This is an
open-ended mini-project; grade yourself by the brute-force agreement on small
grids and by never materializing the power set.)
7. Order-4 Latin squares: pin the count (rating 22 · §7.2.2.1-(latin4))
Problem. Stage 4 encodes an Latin square as exact cover with primary items cell, row-sym, col-sym — items — and options "symbol into cell ." It pins order 3 to squares. Extend the encoding to order 4 and confirm your XCC solver counts exactly 576 Latin squares.
Hint. Nothing about the encoding changes except :
primary items and options (one per ), each covering exactly
one cell item, one row-sym item, one col-sym item. count_solutions should
return .
Answer sketch. With there are primary items and options; the solver reports 576 completed squares — the known value of (the number of Latin squares of order 4), verified by an independent backtracking counter. The point Knuth makes in §4 is that a Latin square is an exact cover: the cell items force one symbol per cell, and the row-sym / col-sym items force each symbol once per line. That completing a general partial Latin square is NP-complete, yet MRV + dancing links handles order 4 (and far beyond) without breaking a sweat, is testimony to how well the choose-minimum-size heuristic exploits structure. (Cross-check ladder: , , , .)
8. ▶ LIFO exactness of purify/unpurify, by induction (rating 25 · §7.2.2.1-(undo))
Problem. Prove fact 3 of §4: in Algorithm 7.2.2.1C, unhide restores
exactly the nodes that the matching hide unlinked, even though hide/unhide
skip purified () nodes under a single color >= 0 guard.
State the invariant relating a node's mark to the stack of active purify
operations, and induct on the recursion tree.
Hint. The only way hide and unhide can disagree is if some node's color
flips between and its true color in between them. Which operation is
the only one that writes those marks, and how is its lifetime nested relative to
a hide/unhide pair by the C5/C6 stack discipline?
Answer sketch. Invariant. At any point in the search, a secondary node
carries iff there is an active (not yet undone) purify of
its item on the recursion stack that marked it; the per-node state machine is
exactly , never deeper, because §4-fact-2 shows no item is
purified twice (a second purify would require committing a still-linked node of
that item, but all such are either already — commit is a no-op on — or
belong to different-color options unreachable after fact 1). Induction on the
recursion tree. commit/uncommit in C5/C6 are strictly LIFO: the purify
that a node's mark belongs to either strictly encloses the hide/unhide pair
in question or is strictly enclosed by it — nesting, never interleaving, by the
stack. So between a hide at depth and its unhide on backtrack, no
enclosing/enclosed purify boundary is crossed, hence no relevant mark flips.
Therefore unhide's color >= 0 guard evaluates identically to hide's, and
relinks precisely the set hide unlinked — Module 09's dancing argument (a
removed node still points at its neighbors) strengthened by a one-bit invariant.
Base case: a leaf does no hide/unhide. Stage 4 tests this the honest way — solve
twice, demand byte-identical output.
9. Why branching on a secondary item is wrong (rating 20 · §7.2.2.1-(mrv))
Problem. Algorithm 7.2.2.1C's step C3 chooses an active primary item to branch on; the root ring threads primary headers only. Show that branching on a secondary item can be outright wrong — producing a missing or spurious solution — not merely slower.
Hint. Recall the covering contract: primary items must be covered exactly once, secondary items at most once. Branching on an item means "try each option that covers it, and one of them must be chosen." Apply that "must" to a secondary item that a valid solution simply leaves uncovered.
Answer sketch. Branching on item commits the search to picking some
option covering — it is a " is covered by exactly one of these" case
split. That is correct for a primary item, whose contract is "exactly once." But
a secondary item's contract is "at most once," so a perfectly valid solution
may leave it uncovered. Concrete instance: one primary item and one
secondary item , with a single option (covering only , not ).
The unique exact cover is : is covered once, zero times —
legal. But if C3 branched on , it would demand an option covering ; there
is none, so the search would report no solution — wrong. (Dually, forcing a
secondary item to be covered can manufacture solutions that violate other
"at most once" items.) Restricting branching to primary items — and, via colors
and purify, letting secondary items constrain reversibly rather than force —
is what keeps the semantics correct; MRV then only chooses which primary item,
never whether to cover an optional one.
10. ▶ Emit the XCC solution family as a ZDD (rating 40 · §7.2.2.1-(zdd-xcc))
Problem. Build §4's bridge between the two halves of the module. Modify the
XCC search so that instead of visiting solutions one at a time it memoizes on
the set of still-active items and emits the entire family of solutions (each
solution = its set of chosen option indices) as a ZDD. Validate count_sets of
that diagram against count_solutions on the module's instances (order-3 Latin
squares, the word-pair grid).
Hint. The subproblem faced at any search node is fully determined by which items are still active — two different partial choices that leave the same items uncovered have identical solution sub-families. Cache on that key; a cache hit becomes a shared ZDD node, exactly as hash-consing shares family nodes in stages 1–2. The ZDD variables are option indices.
Answer sketch. Give the search a return value: the ZDD of all completions of
the current subproblem, over option-index variables. At C2 (root ring empty)
return (the one completion is "add nothing more"). At a branch on primary
item , for each option in 's list form , and these across
; that is the family "choose , then any completion." Memoize the whole call
on the canonical key = the set of active items (a bitmask or sorted item list),
so identical subproblems return the same Ref — turning the search DAG into a
shared ZDD. By §1's canonicity, the result is the unique diagram of the solution
family, and count_sets on it equals the ordinary count_solutions — validate:
for order-3 Latin squares, for the word-pair grid, matching stage 4's
counts. This is Dancing with ZDDs: where the plain solver visits solutions one
by one, the memoized variant hands back all of them as one diagram, ready for
count_sets, uniform sampling, or optimization in — the sense in which
dancing links and ZDDs are two faces of one idea. (Open-ended mini-project;
grade by exact agreement of count_sets with count_solutions on every module
instance.)
Your solutions
Use this space to log your own work — a restated problem, your approach, and how it compared with the sketch above.
Exercise N (rating RR)
Approach. Answer / proof. Compared with the sketch: what you did differently, what you missed.