@@ -553,6 +553,36 @@ heuristic's layer count also tightens the search horizon
553553(` max_layers = min(user_max, hint_n_layers + 1) ` ), which shrinks
554554each ` layer_var ` 's domain.
555555
556+ ** The warm-start runs with eager freeing disabled (` eager_free=False ` ).**
557+ The default heuristic frees a node's columns * within* its consumer's
558+ layer and reuses them the same layer (within-layer reuse). The
559+ layer-granular CP-SAT model — where a node occupies the residual
560+ stream for the half-open layer interval ` [birth, cancel) ` — cannot
561+ express that density, so the * eager* schedule is an ** infeasible**
562+ hint: CP-SAT discards it, cold-searches, and times out into the
563+ silent heuristic fallback (the post-J DOOM symptom at d=4096/d_head=32:
564+ ` optimize=2 ` returned 85 via fallback). The no-eager schedule is one
565+ or two layers deeper (87 vs 85 at d=4096) but model-representable, so
566+ CP-SAT gets a * real feasible incumbent* to verify and improve — it
567+ walks 87 → 86 → … → 81–82 within the ` optimize=2 ` budget (the exact
568+ floor varies run-to-run with CP-SAT's parallel workers). The heuristic
569+ ** fallback** (used only when CP-SAT finds nothing) keeps the default
570+ eager behavior, so a timeout never regresses below the eager depth.
571+
572+ ** Rollback-cancel correction.** ` LayerScheduler ` speculatively
573+ allocates a node, then rolls the allocation back with ` free(node) `
574+ when the op can't be committed under the dirty-cancel / head budget.
575+ That rollback is not a real death — the node is re-allocated (reborn)
576+ in a later layer, and may then die by ` reassign ` (free-add, never
577+ ` free ` ). Recording the rollback layer as the cancel produced hints
578+ with ` cancel < birth ` (e.g. freed at layer 6, actually born at 7),
579+ which is hard-infeasible in the model — exactly the 3 records that kept
580+ the otherwise-feasible no-eager hint from being accepted as a complete
581+ incumbent. ` _TrackingResidualStreamMap ` therefore clears a node's
582+ stale cancel whenever it is re-allocated or reborn via ` reassign ` , so
583+ the emitted ` hint_cancel ` is internally consistent (` cancel ≥ birth+1 ` ,
584+ or omitted when the node dies by reassign).
585+
556586### Cancel-domain restriction
557587
558588The cancel decision space is the dominant LB-search cost when the
@@ -610,18 +640,25 @@ feasible but possibly non-optimal. `forward_compile` accepts it —
610640### Geometry sensitivity
611641
612642The win-size from CP-SAT versus heuristic depends on residual-stream
613- slack. Measured on the headless DOOM graph (~ 4.4K nodes); each row
614- records the `` optimize `` level (and corresponding budget) used:
615-
616- | geometry | -O / budget | heuristic | CP-SAT | Δ | first incumbent | OPTIMAL at |
617- | ---------------------| -------------| ----------:| -------:| -----:| ----------------:| -----------:|
618- | d=2048, d_h=8192 | -O 1 (60s) | 61 | (none) | n/a | not in 60s | not in 60s |
619- | d=3072, d_h=8192 | -O 2 (180s) | 58 | 46 | -21% | ~ 27s | ~ 80s |
620- | d=4096, d_h=4096 | -O 1 (60s) | 59 | 46 | -22% | ~ 17s | ~ 31s |
643+ slack. The first three rows were measured on an earlier headless DOOM
644+ graph (~ 4.4K nodes); the last row is the post-J graph (~ 13K nodes,
645+ the flat pass roughly doubled depth). Each row records the
646+ `` optimize `` level (and corresponding budget) used:
647+
648+ | geometry | -O / budget | heuristic | CP-SAT | Δ | first incumbent | OPTIMAL at |
649+ | --------------------------------| -------------| ----------:| -------:| -----:| ----------------:| -----------:|
650+ | d=2048, d_h=8192 | -O 1 (60s) | 61 | (none) | n/a | not in 60s | not in 60s |
651+ | d=3072, d_h=8192 | -O 2 (180s) | 58 | 46 | -21% | ~ 27s | ~ 80s |
652+ | d=4096, d_h=4096 | -O 1 (60s) | 59 | 46 | -22% | ~ 17s | ~ 31s |
653+ | post-J d=4096, d_h=4096, dh=32 | -O 2 (180s) | 85 | 81–82 | -4% | ~ 70–80s | never (LB 50) |
621654
622655At d=2048 the residual cumulative is the binding constraint and
623656CP-SAT struggles to close the LB gap within budget. At d=3072+
624- CP-SAT converges optimally inside the budget. The heuristic-
657+ CP-SAT converges optimally inside the budget. On the much larger
658+ post-J graph CP-SAT no longer proves optimality (the lower bound
659+ sticks at 50) but still beats the heuristic — and only once the
660+ warm-start feeds a * feasible* (no-eager) hint; with the eager hint it
661+ finds no incumbent and falls back. The heuristic-
625662fallback behavior (when CP-SAT can't find an incumbent) is the
626663right answer for d=2048 — users always get a schedule, just not
627664the CP-SAT one.
@@ -643,7 +680,23 @@ the CP-SAT one.
643680 solution. CP-SAT's first feasible reproduces the heuristic
644681 warm-start; stopping there returned no improvement over
645682 ` optimize=0 ` while paying the model-build cost. Removed.
646- - ** ` repair_hint=True ` ** would have let CP-SAT actively complete
647- the partial hint into a feasible solution, but it conflicts
648- with ` AddDecisionStrategy ` (CP-SAT crashes with
649- "fixed_search != nullptr"). Not pursued.
683+ - ** ` repair_hint=True ` ** would have let CP-SAT actively complete the
684+ partial hint into a feasible solution, but it is ** unusable in this
685+ OR-Tools version** (v9.15): it aborts the process with
686+ ` Check failed: heuristics.fixed_search != nullptr ` — * with and
687+ without* ` AddDecisionStrategy ` , and even with
688+ ` search_branching=FIXED_SEARCH ` (where the crash just moves from
689+ setup into a parallel worker mid-search). (An earlier note here
690+ claimed it merely "conflicts with ` AddDecisionStrategy ` "; that was
691+ wrong — removing the strategy does not avoid the crash.) And it
692+ would not have helped regardless: measured on the post-J DOOM graph,
693+ the ~ 70 s to first incumbent is ** genuine model-size search**
694+ (≈23 s presolve + ≈45 s LB/feasibility search on a ~ 80 k-variable
695+ model with three coupled ` AddCumulative ` constraints), not a blocked
696+ seed. The raw hint (with the 3 infeasible ` cancel < birth ` records)
697+ and the clean hint reach their first incumbent at the * same* time —
698+ the soft ` AddHint ` silently drops the infeasible values either way —
699+ so the cancel artifacts were a correctness curiosity, not the
700+ performance lever. The lever is feeding a * feasible* hint at all
701+ (the no-eager schedule, see * Warm-start hints* ) plus an ` optimize≥2 `
702+ budget; CP-SAT then improves 87 → 81.
0 commit comments