Ctrl-G × NWA — characterization matrix

One method, one protocol, zero per-task tuning: where does NWA-constrained decoding with an HMM guide work, and why.

NWA = nested-word automaton: a finite automaton augmented with a call/return stack, so it recognizes unbounded balanced nesting (parentheses, JSON, code blocks) that no DFA can. Ctrl-G = constrained decoding where a distilled HMM approximates the LM to score, at each step, the probability mass of constraint-satisfying futures. This project composes the two: hard NWA masks plus HMM future-mass guidance. Benchmarks: countdown (arithmetic target game, equation answer), nested_arith (rewrite a nested expression step by step), planetarium (NL → PDDL problem files), racket (MultiPL-E Racket function completion, unit-test pass), bbeh_dyck (BBEH Dyck bracket-closing), NESTFUL (nested tool-call sequences in JSON).

status: COMPLETE — 56 measured cells (2 models × 6 benchmarks × 5 conditions, 4 n/a) + 24-cell λ ablation updated 2026-08-04 08:28 UTC code 497f812 exploratory-era archive

TL;DR — the complete result

Every number below is final: 2 models × 6 benchmarks × 5 conditions, plus a 24-cell λ ablation. One method, one protocol, no per-task tuning. Nothing is mid-run.

Method & math

Decoding rule (all constrained conditions)

scoret(a) = log pLM(a | x<t) + λ · log βguide(q(x<t a)) ,  a ∈ Valid(x<t) p̃(a) = softmax over walker-valid tokens; invalid tokens = −∞ where q(·) is the NWA configuration reached after emitting a and β(q) is the guide's total probability mass over accepting completions from q. λ = 1.0 in the main matrix below (the pure formulation); the λ-ablation section re-runs E at λ ∈ {0.1, 0.5}. Tokens with β = 0 (no valid completion) are hard-banned at any λ — which is why the mask is a separate run, not a λ→0 sweep point. Implementation note: the runtime scans tokens in LM-score order and keeps the top keep_k=32 walker-valid candidates — argmax-identical to the full valid set under the greedy (beam-1) decoding used everywhere in this matrix, but a real truncation for sampling-based uses.

Conditions — what each isolates

idconstraintguideisolates
Anonenone the raw LLM
BNWA walker (viability projection) nonevalue of constraint support alone
Cwalker, depth capped at 2 (= DFA / original-Ctrl-G class); the guide's future-mass DP is shared with E (full NWA) — only the hard mask is depth-restricted learned HMM, λ=1NWA-vs-DFA constraint-class axis (mask side only)
DNWA walker uniform (unlearned) HMM, λ=1, fp64 does guide learning matter
ENWA walker learned HMM, λ=1the full method as formulated

Results — Llama-3.1-8B-Instruct (complete)

Accuracy (exact task metric), n = full benchmark. Suite cells recomputed from per-example predictions at page build; NESTFUL from the scorer's final log block. Bold = row best. Llama decodes answers directly (no thinking mode).

taskA zeroshotB maskC dfa+guide D uniformE full B−AE−BE−D
countdown0.1330.1910.1720.1880.172+0.059-0.020-0.016
nested_arith0.1950.2010.0050.0000.005+0.005-0.195+0.005
planetarium0.0120.0160.0000.0000.016+0.004+0.000+0.016
racket0.1990.1990.0000.0000.025+0.000-0.174+0.025
bbeh_dyck0.0450.000n/a0.0500.000-0.045+0.000-0.050
nestfulacc-combined0.1830.260n/a0.0170.143+0.077-0.117+0.126

bold = row best among completed cells · — = not started · … k/n = running, mean suppressed below 25% coverage · n/a = condition undefined for the task · Δ columns computed on raw counts before rounding (may differ from displayed cells by ±0.001) · † = transductively distilled guide (see caveats)

Results — Qwen3.5-9B (complete)

Same protocol, thinking ON (free, never constrained; max_think 4096–8192 per task). Every guide is distilled from this model's own constrained samples. Bold = row best.

taskA zeroshotB maskC dfa+guide D uniformE full B−AE−BE−D
countdown0.8050.8320.8320.6410.832+0.027+0.000+0.191
nested_arith0.3960.3830.0000.0050.000-0.013-0.383-0.005
planetarium0.3320.3320.0000.1050.324+0.000-0.008+0.219
racket0.2860.3600.0000.0000.000+0.075-0.360+0.000
bbeh_dyck0.1550.295n/a0.0550.315+0.140+0.020+0.260
nestfulacc-combined0.3780.387n/a0.0610.354+0.009-0.033+0.293

bold = row best among completed cells · — = not started · … k/n = running, mean suppressed below 25% coverage · n/a = condition undefined for the task · Δ columns computed on raw counts before rounding (may differ from displayed cells by ±0.001) · † = transductively distilled guide (see caveats)

Why the uniform guide (D) scores ~0 on long-answer tasks

This is measured formulation behavior, not a bug — condition D exists to expose exactly this. With uniform emissions, every completion of length ℓ has probability V−ℓ (V ≈ 128k–152k vocab), so:

βu(q) = Σw ∈ L(q) V−|w| ≈ Nmin · V−ℓmin(q) — dominated by the shortest accepting completion. Consequences at λ=1:

The data shows all three signatures (figures below are Llama unless noted; Qwen is given where it differs). countdown-Llama (answers ≈ 14 chars): D = 0.188 ≈ B = 0.191 — harmless below the horizon, the clearest evidence the D implementation is correct. But countdown-Qwen D = 0.641 vs B = 0.832: even short answers lose 19pp once the base model is capable enough that the tilt competes with real preferences. nested_arith: outputs collapse to minimal valid shapes ('1. \n7 \n2 \n7…') → 0. racket / planetarium (Llama): long outputs that are almost entirely comments (penalty-free) with no scored structure → 0; planetarium-Qwen reaches 0.105, not 0. NESTFUL-Llama: minimal valid call-JSON (median 76 chars, vs 277 for the mask) — 200/200 schema-valid, 0.017 correct. E−D therefore reads: what does learning the guide buy, given identical machinery.

NESTFUL in depth (validity is a first-class result)

accuracy_combined = the benchmark's combined intent+slot+sequencing metric. "schema-valid" = outputs strictly accepted by the call-schema parser as emitted.

modelcondacc_combinedf1_intent f1_slotschema-valid
llamaA0.1830.7040.4320/200
llamaB0.2600.9320.564196/200
llamaD0.0170.4430.050200/200
llamaE0.1430.5640.280200/200
qwenA0.3780.8920.698111/200
qwenB0.3870.8950.701199/200
qwenD0.0610.2950.168152/200
qwenE0.3540.8760.648200/200

The accuracy–guarantee trade at λ=1: B leads accuracy (0.260) with 4/200 schema failures; E gives up ~12pp accuracy for zero schema failures; zeroshot A emits nothing strictly schema-valid (its calls are recovered only by lenient extraction). The earlier exploratory λ-sweep (archived) showed the gap closing as λ → 0.1–0.2 with validity retained — measuring that on the v2 prefix-conditioned guides is the λ-ablation section below, which found λ=0.1 recovers 0.252 (Llama) and 0.386 (Qwen).

λ ablation — the guidance-strength frontier

The matrix above fixes λ=1 (the pure formulation). Here condition E is re-run at λ ∈ {0.1, 0.5} with everything else identical — same guides, same budgets, greedy, thinking unconstrained. The λ=0 column is condition B shown as the λ→0 limit of the guide term — it is a separate mask-only run, not a sweep point (the β=0 hard ban applies at every λ, so a literal λ=0 guided run is still not the mask). 24 guided cells, all complete at full n.

Qwen3.5-9B

taskA zeroshotλ=0 (mask)λ=0.1 λ=0.5λ=1
countdown0.8050.8320.8320.8360.832
nested_arith0.3960.3830.1300.2010.000
planetarium0.3320.3320.3280.3360.324
racket0.2860.3600.0000.0000.000
bbeh_dyck0.1550.2950.3150.3150.315
nestfulacc + valid0.3780.387199/2000.386200/2000.384200/2000.354200/200

bold = best guided-or-mask cell in the row (suppressed when that best is 0.000) · λ=0 column = condition B, a separate mask-only run shown as the λ→0 limit; λ=1 = condition E from the main matrix · NESTFUL cells show accuracy with schema-validity beneath (jobs 1924871-1924874) · † = transductively distilled guide (see caveats)

Llama-3.1-8B-Instruct

taskA zeroshotλ=0 (mask)λ=0.1 λ=0.5λ=1
countdown0.1330.1910.1760.1800.172
nested_arith0.1950.2010.1090.1040.005
planetarium0.0120.0160.0160.0270.016
racket0.1990.1990.1300.0750.025
bbeh_dyck0.0450.0000.0000.0000.000
nestfulacc + valid0.1830.260196/2000.252195/2000.222198/2000.143200/200

bold = best guided-or-mask cell in the row (suppressed when that best is 0.000) · λ=0 column = condition B, a separate mask-only run shown as the λ→0 limit; λ=1 = condition E from the main matrix · NESTFUL cells show accuracy with schema-validity beneath (jobs 1924871-1924874) · † = transductively distilled guide (see caveats)

What the frontier shows

D1 — does look-ahead buy anything? (measured; answer: no)

The matrix and the λ section both measure D2 — what grading the viable set buys. The other half of the decomposition is D1: what reachability look-ahead buys over plain one-step legality. Two arms, both on the same objects as the matrix.

Arm 1 (walker, repair disabled) turned out to be a null instrument: 0 differing answers in 2,063 matched examples. The per-task walkers are viability-complete by construction — their step function refuses any character that would strand the generation — so the decoder never dead-ends and the fallback is unreachable. It measures nothing about look-ahead and is reported here only so the zero is not mistaken for evidence.

Arm 2 (the real one): one-step nwa.step legality vs 1[β>0] from the offline DP, both on the same token NWA.

lanen(a2) local(b2) viability D1differingwalker mask
countdown / llama2560.01170.0117+0.000000.1914
countdown / qwen2020.11880.1188+0.000000.8366
nested_arith / llama3840.11720.1172+0.000010.2005
nested_arith / qwen2890.17300.1730+0.000050.5526
planetarium / llama2560.01560.0156+0.000000.0156
planetarium / qwen2560.32810.3281+0.000020.3320
racket / llama1610.12420.1242+0.000000.1988
racket / qwen1610.00000.0000+0.000000.3602

1,965 matched examples · 8 differing answers (0.41%) · D1 = +0.0000 on every lane · countdown/qwen and nested_arith/qwen are partial (202, 289) because their 12 h wall hit — matched-pair comparison still settles them, and they were not resubmitted to reconfirm a structural property on a shared cluster

Why — and it was predicted before it was measured

Every structural NWA in this project is a bracket/balance hull, and on such a hull every legal prefix extends to an accepting string by appending closers. So 1[β>0] and 1[legal now] are the same function, and the offline DP decides nothing the one-step relation had not already decided. The project's own handoff argued exactly this a priori — "nesting depth and look-ahead depth are orthogonal; bracket viability is shallow look-ahead yet needs NWA state" — and this measures it on four languages and two models. The NWA earns its keep through state (unbounded nesting a DFA cannot track), not through look-ahead; β's remaining justification is grading, i.e. D2.

Do not read the last column as D1. The walker mask beats both NWA arms by a lot on some lanes (countdown/qwen 0.837 vs 0.119). That is a language difference, not reachability: the structural NWA encodes well-formedness, while the per-example walker also encodes the instance constraint (countdown: use these numbers, once). "The NWA" names two different languages in this codebase — the structural NWA that β is computed over, and the per-example walker used as the runtime mask.

Why does guidance help on CommonGen but not here? (the diagnosis)

The matrix, the λ ablation and the D1 study all return nulls for the guide. Three further experiments — the ladder from the project's own handoff — were run to establish why, and whether the nulls are about the method or about the benchmarks. They answer it.

Experiment A — the CommonGen positive control

What it does. Runs Ctrl-G on the benchmark it was built for, using its own released assets (gpt2-large_common-gen + the h=4096 CommonGen HMM) and its own DFA machinery: the constraint is keyword inclusion (every concept must appear in some inflected form). Two arms, identical except for the guide — uniform (h=2, uninformative) vs trained (h=4096). n=200. Metric: constraint satisfaction, plus mean log pLM per generated token (what rank_generated_ids already ranks by, i.e. the pipeline's own notion of quality).

Why it matters. The handoff is blunt about it: "without this anchor, a null result anywhere else is uninterpretable." If our apparatus cannot detect a guide's benefit where one is known to exist, every null we report is worthless.

armconstraint satisfiedmean log pLM
uniform (h=2)97/200  (48.5%)−2.217
trained (h=4096)200/200  (100%)−2.054

a third "viability" arm was attempted and abandoned after two failed implementations — documented in EXPERIMENT_LOG, excluded here rather than reported

Finding. The trained guide dominates on both axes at once: +51.5pp constraint satisfaction and better fluency. D2 is large and easily detected when it exists. The apparatus works.

Experiment B — T1, event calibration

What it does. Asks whether β̂ is actually a good forecaster of the thing it claims to predict. Take an in-language partial generation, read β̂ at its automaton config, then roll the base LM forward unconstrained 8× and record whether those rollouts satisfy the constraint. Score β̂ as a probabilistic forecaster of that realized event — Brier and AUC — for the trained guide and a uniform one. 59 live prefixes, stratified across difficulty.

Why it matters. Perplexity measures the guide's content model. Whether the guide predicts the satisfaction event is a different functional, and it is the one Ctrl-G's decoding rule actually consumes.

guideBrier (lower better)AUC
trained (h=256)0.74310.9402
uniform (h=2)0.74580.9462

base rate 0.746 — predicting the base rate alone would score Brier ≈ 0.19

Findings. (1) β is not a probability. Both guides forecast ≈1e−10 where realized satisfaction is 0.75; Brier ≈ 0.74 against ≈0.19 for base-rate prediction. (2) Learning adds nothing to event prediction — uniform is marginally ahead on AUC. Both rank well (≈0.94) using the same structural signal, shortest-completion length, which the uniform guide has by construction. This is the handoff's H4 hypothesis, confirmed.

Experiment C — T2, the MC-oracle ceiling

What it does. Removes guide quality from the equation entirely. At each binding decision point (where more than one token is viable, so grading could change the outcome), it replaces β̂ with a Monte-Carlo estimate from the base LM itself: roll forward 6× per candidate and score the outcomes, then decode with that. It is the ceiling any future-aware scorer could reach.

condition (matched, n=25 stratified)accuracy
A zeroshot0.400
B mask0.400
E learned guide (λ=1)0.000
T2 correctness-oracle0.360

oracle − mask = −0.040, one differing example in 25; oracle active at 11.0 decision points per generation on average

Finding. The oracle does not beat the mask. And this is the strong form: our oracle scores rollouts by verified task correctness, not merely by constraint satisfaction — it is more powerful than Ctrl-G's β is entitled to be — and it still buys nothing. By the handoff's own decision rule, nested_arith has no D2 headroom. Not a guide-fidelity problem.

caveats: n=25 with one differing example means "no detectable difference", not "the oracle is worse"; and the oracle is capped at 12 binding points before falling back to mask decoding, making it a lower bound on the true ceiling

The takeaway these three give together

Guidance helps exactly when the satisfaction event is uncertain during generation — and the mask removes that uncertainty here. On CommonGen you can still fail to fit the keywords at any point, so the event is live, a guide that forecasts it wins +51.5pp, and our apparatus sees that clearly. On this suite the walker mask guarantees that every viable token leads to an accepting completion, so the satisfaction event is deterministic: the true satisfaction-oracle β is identically 1, there is nothing left to forecast, and T1 confirms the trained guide is indistinguishable from a blank one as a forecaster (AUC .940 vs .946). T2 closes it — even a correctness-leaking oracle cannot beat plain masking.

So the nulls throughout this page are a property of these benchmarks, not a failure of the method. The corollary is a benchmark-design claim: to show a guide's value you need a task where constraint satisfaction stays genuinely in doubt mid-generation. Fully-maskable structured output — JSON schemas, PDDL, s-expressions — is precisely the wrong place to look.

Caveats & scope

Implementation map

stagecode notes
Task adapters (5 suite benchmarks; NESTFUL has its own m14 pipeline) ctrlg/task_adapter.py load / messages / walker / NWA / extract / verify per task
NWA constructions ctrlg/nwa_*.py per-task nested-word automata; documented supersets where exact languages are impractical
Runtime hard mask ctrlg/walker_mask.py, ctrlg/nwa_nestful_mask.py char-level walkers over token strings; force-EOS on dead ends; mask_only = Outlines-style renormalize
Offline DP cache ctrlg/nwa_backward.py build_offline_cache (Tι, Cγ, Rγ, W, β), fp64
Guided processor ctrlg/nwa_processor.py score = log pLM + λ·log β; hmm_prefix_ids conditions the belief on the prompt
Guide distillation scripts/suite_distill.py, scripts/m14_distill_nestful_hmm.py constrained temp-1.0 corpus → LVD init → EM (--with_prefix v2 rows)
Evaluation scripts/suite_eval.py, scripts/m14_constrained_eval.py modes = conditions; per-example checkpointing, resumable
Launcher / protocol scripts/run_matrix.sh, PROTOCOL.md full matrix as Slurm jobs; canonical protocol text

Per-task budgets (identical across conditions within a task)

tasknmax_think (Qwen)max_new guide
countdown25681921024h256-pfx
nested_arith38440961536h256-pfx
planetarium25640962048h256-pfx
racket1614096384h256-pfx (transductive)
bbeh_dyck2008192256 (Llama 2048)h256-pfx (transductive)
nestful2008192 (Qwen only)512h512-pfx
Built 2026-08-04 08:28 UTC from results/ at code 497f812 · suite cells = mean of per-example correct flags recomputed at build time · NESTFUL cells = final log blocks of jobs 1896241/1896242/1896243/1898460 (Llama) and 1898686/1903191/1903192/1907241 (Qwen); NESTFUL λ cells = jobs 1924871/1924873 (Llama) and 1924872/1924874 (Qwen) · pre-protocol exploratory dashboard (frozen)