Download src/sudoku_remask.py from GOVINDFROM/parallel-consistent-masked-diffusion: direct link, hf CLI and curl.
- Browser
- Download file 7.56 kB
-
https://huggingface.co/GOVINDFROM/parallel-consistent-masked-diffusion/resolve/main/src/sudoku_remask.py
- Command line
-
hf download hf://GOVINDFROM/parallel-consistent-masked-diffusion/src/sudoku_remask.py
-
curl -L -o sudoku_remask.py https://huggingface.co/GOVINDFROM/parallel-consistent-masked-diffusion/resolve/main/src/sudoku_remask.py
7.56 kB
| """Does exact verifier feedback fix the absorbing-commit failure? | |
| Diagnosis from the trained models: 94.9% of cells are correct but only 87.9% of | |
| boards. Failed boards therefore carry roughly three wrong cells out of | |
| fifty-seven - they are near misses, not collapses. Masked diffusion decoding is | |
| absorbing, so those three cells can never be revised and each one destroys a | |
| whole board. | |
| Sudoku constraints are exactly checkable, so the repair signal is not a | |
| heuristic: we know precisely which cells conflict. Remask exactly those (widened | |
| to their row, column and box, since re-deciding one cell in an unchanged context | |
| reproduces the same value), re-decode, repeat. | |
| Every forward pass is counted, including the repair rounds, so the comparison | |
| against fixed-K budgets is honest rather than flattering: a method that needs | |
| sixty passes to repair is reported at sixty. | |
| The control that matters is RANDOM remasking of the same number of cells. If | |
| random repair helps as much as verifier-guided repair, then the gain is just | |
| extra compute and the verifier is doing nothing. | |
| """ | |
| import argparse, glob, json, os, sys | |
| import numpy as np | |
| import torch | |
| sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) | |
| from model import Transformer | |
| import diffusion as dfn | |
| from sudoku import (CELLS, SudokuTokenizer, expand_to_units, violation_mask) | |
| p = argparse.ArgumentParser() | |
| p.add_argument("--hard", default="data/sudoku_hard") | |
| p.add_argument("--runs", default="runs") | |
| p.add_argument("--pattern", default="sudh-*") | |
| p.add_argument("--eval_bs", type=int, default=250) | |
| p.add_argument("--out", default="figures/sudoku_remask.json") | |
| a = p.parse_args() | |
| dev = "cuda" if torch.cuda.is_available() else "cpu" | |
| tok = SudokuTokenizer() | |
| P = np.load(os.path.join(a.hard, "test_puz.npy")) | |
| S = np.load(os.path.join(a.hard, "test_sol.npy")) | |
| clues = (P > 0).sum(1) | |
| print(f"benchmark: {len(P)} puzzles, clues {clues.min()}-{clues.max()} " | |
| f"(mean {clues.mean():.1f})", flush=True) | |
| Pt, St = torch.from_numpy(P), torch.from_numpy(S - 1) | |
| blank = Pt == 0 | |
| def load(d): | |
| cfg = json.load(open(os.path.join(d, "result.json")))["args"] | |
| m = Transformer(len(tok), cfg["d"], cfg["layers"], cfg["heads"], cfg["pe"], | |
| causal=False, max_len=CELLS + 8).to(dev) | |
| m.load_state_dict(torch.load(os.path.join(d, "model.pt"), map_location=dev)) | |
| m.eval() | |
| return m, cfg | |
| def random_remask_like(bad_counts): | |
| """Control: remask the same NUMBER of cells, chosen at random.""" | |
| def fn(x): | |
| B, L = x.shape | |
| out = torch.zeros_like(x, dtype=torch.bool) | |
| for b in range(B): | |
| k = int(bad_counts[b]) | |
| if k: | |
| idx = torch.randperm(L, device=x.device)[:k] | |
| out[b, idx] = True | |
| return out | |
| return fn | |
| def evaluate(m, fn): | |
| preds, nfes = [], [] | |
| for i in range(0, len(Pt), a.eval_bs): | |
| gold = St[i:i + a.eval_bs].to(dev) | |
| bl = blank[i:i + a.eval_bs].to(dev) | |
| x = torch.where(bl, torch.full_like(gold, tok.mask), gold) | |
| y, nfe = fn(x, bl) | |
| preds.append(y.cpu().numpy() + 1) | |
| nfes.append(nfe) | |
| pred = np.concatenate(preds) | |
| board = float((pred == S).all(axis=1).mean()) | |
| cell = float((pred[P == 0] == S[P == 0]).mean()) | |
| return board, cell, float(np.mean(nfes)), pred | |
| res = {} | |
| dirs = sorted(glob.glob(os.path.join(a.runs, a.pattern))) | |
| dirs = [d for d in dirs if os.path.exists(os.path.join(d, "model.pt"))] | |
| print(f"evaluating {len(dirs)} models\n", flush=True) | |
| for d in dirs: | |
| m, cfg = load(d) | |
| name = os.path.basename(d) | |
| print(f"--- {name}", flush=True) | |
| print(f" {'decoder':<34}{'NFE':>8}{'BOARD':>10}{'cell':>9}", flush=True) | |
| row = {} | |
| for K in (16, 61): | |
| b, c, n, _ = evaluate(m, lambda x, bl, K=K: dfn.fixed_k_decode( | |
| m, x, tok, dev, K, fillable=bl)) | |
| row[f"fixedK_{K}"] = {"board": b, "cell": c, "nfe": n} | |
| print(f" {'fixed-K K=' + str(K):<34}{n:>8.1f}{b*100:>9.2f}%{c*100:>8.2f}%", | |
| flush=True) | |
| b, c, n, _ = evaluate(m, lambda x, bl: dfn.entropy_budget_decode( | |
| m, x, tok, dev, 0.01, max_steps=CELLS + 2, fillable=bl)) | |
| row["entbudget"] = {"board": b, "cell": c, "nfe": n} | |
| print(f" {'entropy-budget B=0.01':<34}{n:>8.1f}{b*100:>9.2f}%{c*100:>8.2f}%", | |
| flush=True) | |
| # Repair must start from the STRONGEST available decode, not a weak one. | |
| # An earlier version used base_steps=16, which decodes to ~83% before repair, | |
| # so the repaired result never beat plain 61-pass decoding at ~88% - the | |
| # method was handicapped by its starting point, not by the idea. | |
| for base in (32, 61): | |
| for rounds in (4, 8): | |
| b, c, n, _ = evaluate( | |
| m, lambda x, bl, r=rounds, bs=base: dfn.verifier_remask_decode( | |
| m, x, tok, dev, violation_mask, base_steps=bs, rounds=r, | |
| max_revisits=3, fillable=bl, expand_peers=expand_to_units)) | |
| row[f"verifier_b{base}_r{rounds}"] = {"board": b, "cell": c, "nfe": n} | |
| print(f" {'VERIFIER base=' + str(base) + ' rounds=' + str(rounds):<34}" | |
| f"{n:>8.1f}{b*100:>9.2f}%{c*100:>8.2f}%", flush=True) | |
| # narrow repair: remask ONLY the conflicting cells, not their whole units | |
| b, c, n, _ = evaluate(m, lambda x, bl: dfn.verifier_remask_decode( | |
| m, x, tok, dev, violation_mask, base_steps=61, rounds=8, | |
| max_revisits=3, fillable=bl, expand_peers=None)) | |
| row["verifier_narrow"] = {"board": b, "cell": c, "nfe": n} | |
| print(f" {'VERIFIER base=61 narrow (no units)':<34}{n:>8.1f}{b*100:>9.2f}%" | |
| f"{c*100:>8.2f}%", flush=True) | |
| # control: same budget, but remask at random instead of where it is wrong | |
| b, c, n, _ = evaluate(m, lambda x, bl: dfn.verifier_remask_decode( | |
| m, x, tok, dev, | |
| lambda z: (torch.rand_like(z, dtype=torch.float) < 0.12), | |
| base_steps=61, rounds=8, max_revisits=3, fillable=bl, | |
| expand_peers=None)) | |
| row["random_remask_control"] = {"board": b, "cell": c, "nfe": n} | |
| print(f" {'CONTROL: random remask, 8 rounds':<34}{n:>8.1f}{b*100:>9.2f}%" | |
| f"{c*100:>8.2f}%", flush=True) | |
| res[name] = row | |
| del m | |
| torch.cuda.empty_cache() | |
| print(flush=True) | |
| if res: | |
| print("=" * 76) | |
| print("SUMMARY (mean over models)") | |
| print("=" * 76) | |
| keys = list(next(iter(res.values())).keys()) | |
| print(f"{'decoder':<34}{'NFE':>8}{'BOARD':>10}") | |
| base = None | |
| for k in keys: | |
| bs = [r[k]["board"] for r in res.values()] | |
| ns = [r[k]["nfe"] for r in res.values()] | |
| if k == "fixedK_61": | |
| base = float(np.mean(bs)) | |
| print(f"{k:<34}{np.mean(ns):>8.1f}{np.mean(bs)*100:>9.2f}%") | |
| if base is not None: | |
| best_v = max((np.mean([r[k]["board"] for r in res.values()]) | |
| for k in keys if k.startswith("verifier_")), default=0) | |
| ctrl = np.mean([r["random_remask_control"]["board"] for r in res.values()]) | |
| print(f"\n best verifier-guided : {best_v*100:.2f}%") | |
| print(f" fixed-K at 61 passes : {base*100:.2f}% " | |
| f"(verifier gains {(best_v-base)*100:+.2f} pts)") | |
| print(f" random-remask control: {ctrl*100:.2f}% " | |
| f"({(ctrl-base)*100:+.2f} pts)") | |
| print("\n If the control gains as much, the verifier is doing nothing and") | |
| print(" the improvement is only the extra compute.") | |
| os.makedirs(os.path.dirname(a.out) or ".", exist_ok=True) | |
| json.dump(res, open(a.out, "w"), indent=2) | |
| print(f"\nwrote {a.out}") | |