"""Numerical audit. Checks the things that would silently invalidate results. Written because two earlier projects produced confident, wrong numbers: an answer-length leak that handed the model free information, and a learning-rate artifact that made a working method look broken. Both were invisible in the accuracy tables. """ import glob, json, os, sys import numpy as np import torch sys.path.insert(0, os.path.dirname(__file__)) from data import build_addition, build_probe from sigmorphon import load_language import diffusion as dfn from model import Transformer FAIL = [] def check(name, ok, detail=""): print(f" [{'PASS' if ok else 'FAIL'}] {name}" + (f" — {detail}" if detail else ""), flush=True) if not ok: FAIL.append(name) print("\n=== 1. data integrity ===") for d in (4, 8, 12, 16): X, A, tok = build_addition(300, d, 3 * d + 4, seed=5, exact=True) bad = 0 for i in range(len(X)): s = [tok.itos[t] for t in X[i] if t != tok.pad] a = int("".join(s[: s.index("+")])) b = int("".join(s[s.index("+") + 1: s.index("=")])) got = int("".join(reversed(s[s.index("=") + 1:]))) bad += (a + b != got) check(f"{d}-digit addition arithmetic", bad == 0, f"{bad}/300 wrong") # the answer region must not reveal its own length check(f"{d}-digit answer mask is fixed-width", len({int(x) for x in A.sum(1)}) <= 2, f"{len({int(x) for x in A.sum(1)})} distinct widths") print("\n=== 2. the prompt is never masked (no label leakage) ===") X, A, tok = build_addition(200, 8, 28, seed=7, exact=True) Xt, At = torch.from_numpy(X), torch.from_numpy(A) xt = torch.where(At, torch.full_like(Xt, tok.mask), Xt) prompt_ok = ((xt != tok.mask) | At).all() check("only answer positions are masked at decode time", bool(prompt_ok)) eq_visible = all((xt[i] == tok.stoi["="]).any() for i in range(len(xt))) check("the '=' separator stays visible", eq_visible) print("\n=== 3. metric cannot be gamed ===") gold = Xt[:50] am = At[:50] perfect = dfn.cond_decode # placeholder to keep import used from train_add import exact_match check("identical prediction scores 1.0", exact_match(gold, gold, am, tok) == 1.0) shifted = gold.clone() shifted[am] = (shifted[am] + 1) % 10 + 4 check("all-wrong prediction scores 0.0", exact_match(shifted, gold, am, tok) == 0.0) const = gold.clone() const[am] = tok.stoi["0"] acc_const = exact_match(const, gold, am, tok) check("constant-output prediction is not rewarded", acc_const < 0.05, f"{acc_const:.3f}") print("\n=== 4. probe tasks: ground truth valid, degenerate output rejected ===") from train import probe_accuracy from data import CharTokenizer for task in ("copy", "agree", "parity"): Xp, tk = build_probe(200, 64, 4, seed=1, task=task) gt = probe_accuracy(Xp, task, 4, tk) cst = probe_accuracy(np.full_like(Xp, 5), task, 4, tk) check(f"{task}: ground truth valid", gt > 0.99, f"{gt:.2f}") if task != "copy": check(f"{task}: constant output rejected", cst < 0.01, f"{cst:.2f}") print("\n=== 5. inflection: no train/dev overlap ===") try: (Xtr, Atr), (Xdv, Adv), tok2 = load_language( "english", os.environ.get("SIGROOT", "data/sig"), "medium", 48) tr = {tuple(int(v) for v in r) for r in Xtr} dv = {tuple(int(v) for v in r) for r in Xdv} check("english train/dev disjoint", len(tr & dv) == 0, f"{len(tr & dv)} shared") check("dev set is non-trivial", len(Xdv) > 200, f"{len(Xdv)} items") except Exception as e: check("inflection loader", False, f"{type(e).__name__}: {e}") print("\n=== 6. reported numbers reproduce from raw run files ===") runs = os.environ.get("RUNS", "runs") groups = {} for f in glob.glob(f"{runs}/g11-*/result.json"): r = json.load(open(f)) a = r["args"] groups.setdefault((a["digits"], a["mode"]), []).append(r["accuracy_by_passes"]["1"]) bad = [k for k, v in groups.items() if len(v) < 2] check("every reported group has >=2 seeds", not bad, f"thin groups: {bad[:3]}") oor = [] for f in glob.glob(f"{runs}/*/result.json"): r = json.load(open(f)) for k, v in (r.get("accuracy_by_passes") or {}).items(): if not (0.0 <= v <= 1.0): oor.append((os.path.basename(os.path.dirname(f)), k, v)) check("all accuracies within [0,1]", not oor, str(oor[:3])) print("\n=== 7. decoders: adaptive commitment rules behave as specified ===") # These three rules produce the headline text8 numbers, so their contract is # checked directly rather than trusted: every masked position must end up # filled, decoding must always make progress (no stall), and the entropy budget # must actually behave monotonically in the budget - otherwise the # quality/compute curve it traces would be meaningless. try: from text8 import Text8Tokenizer t8 = Text8Tokenizer() torch.manual_seed(0) L, B = 32, 8 dm = Transformer(len(t8), 64, 2, 2, "rope", causal=False, max_len=L + 8) dm.eval() blank = torch.full((B, L), t8.mask, dtype=torch.long) for name, fn in [ ("fixed-K K=1", lambda b: dfn.fixed_k_decode(dm, b, t8, "cpu", 1)), ("fixed-K K=8", lambda b: dfn.fixed_k_decode(dm, b, t8, "cpu", 8)), ("confidence tau=0.99", lambda b: dfn.confidence_threshold_decode(dm, b, t8, "cpu", 0.99)), ("entropy-budget B=0.1", lambda b: dfn.entropy_budget_decode(dm, b, t8, "cpu", 0.1)), ("entropy-budget B=inf", lambda b: dfn.entropy_budget_decode(dm, b, t8, "cpu", 1e9))]: x, nfe = fn(blank) check(f"{name}: no [MASK] survives", not bool((x == t8.mask).any()), f"{int((x == t8.mask).sum())} left, nfe={nfe}") check(f"{name}: emits only real symbols", bool((x < len(t8) - 1).all())) _, n1 = dfn.fixed_k_decode(dm, blank, t8, "cpu", 1) check("fixed-K K=1 uses exactly one forward pass", n1 == 1, f"nfe={n1}") _, nb = dfn.entropy_budget_decode(dm, blank, t8, "cpu", 1e9) check("unbounded entropy budget commits in one pass", nb == 1, f"nfe={nb}") # a tighter budget must never be cheaper than a looser one nfes = [dfn.entropy_budget_decode(dm, blank, t8, "cpu", b)[1] for b in (0.01, 0.5, 2.0, 8.0, 1e9)] check("entropy budget is monotone: tighter budget costs >= passes", all(nfes[i] >= nfes[i + 1] for i in range(len(nfes) - 1)), str(nfes)) # partial contexts must be left alone: only `fillable` positions may change part = blank.clone() part[:, :L // 2] = 3 fill = torch.zeros_like(part, dtype=torch.bool) fill[:, L // 2:] = True y, _ = dfn.entropy_budget_decode(dm, part, t8, "cpu", 0.5, fillable=fill) check("decoding never overwrites the given context", bool((y[:, :L // 2] == 3).all())) except Exception as e: check("decoder contract", False, f"{type(e).__name__}: {e}") print("\n=== 8. text8 splits are disjoint and standard ===") try: from text8 import load_text8, N_TRAIN, N_VALID, N_TEST root = os.environ.get("T8ROOT", "data/text8") tr8, va8, te8, tk8 = load_text8(root, 256) check("standard 90M/5M/5M split", (N_TRAIN, N_VALID, N_TEST) == (90_000_000, 5_000_000, 5_000_000)) check("vocabulary is 27 symbols + [MASK]", len(tk8) == 28, f"{len(tk8)}") check("no window straddles a split boundary", tr8.size + va8.size + te8.size <= 100_000_000, f"{tr8.size + va8.size + te8.size} chars covered") seen = {bytes(r) for r in tr8[::997]} dup = sum(bytes(r) in seen for r in te8[::997]) check("no test window appears in the training sample", dup == 0, f"{dup} shared") except FileNotFoundError: print(" [skip] text8 not downloaded here") except Exception as e: check("text8 loader", False, f"{type(e).__name__}: {e}") print("\n=== 9. graph families: enumeration matches known combinatorics ===") # These families have closed-form counts, so the enumerator can be checked # against mathematics rather than against itself. If enumeration is wrong then # every marginal, every entropy and V* are wrong, and the central proposition # would be "verified" against a fiction. try: from graphs import enumerate_family, exact_stats, sample, is_valid, n_edges # Cayley: labelled spanning trees on n nodes = n^(n-2) for n in (5, 6, 7): got = len(enumerate_family("tree", n)) check(f"spanning trees on {n} nodes = {n}^{{{n}-2}}", got == n ** (n - 2), f"{got} vs {n ** (n - 2)}") # perfect matchings on 2m nodes = (2m-1)!! for n, want in ((4, 3), (6, 15)): got = len(enumerate_family("matching", n)) check(f"perfect matchings on {n} nodes = {want}", got == want, f"{got}") # 2-regular on 6 labelled vertices: one 6-cycle (5!/2=60) + two 3-cycles (10) got = len(enumerate_family("2regular", 6)) check("2-regular graphs on 6 nodes = 70", got == 70, f"{got}") # the proposition itself, on every family bad = [] for n in (6, 7): for fam in ("matching", "2regular", "tree", "trianglefree", "bipartite"): try: st = exact_stats(fam, n) except ValueError: continue V, tc = st["one_pass_ceiling"], st["total_correlation_bits"] if V < 2 ** (-tc) - 1e-9: bad.append((fam, n, V, 2 ** (-tc))) if tc < -1e-9: bad.append((fam, n, "negative TC", tc)) check("V* >= 2^-TC on every family (the proposition)", not bad, str(bad[:2])) # sampled graphs must actually belong to the family they were drawn from for fam in ("matching", "tree", "bipartite"): X = sample(fam, 6, 200, seed=3) check(f"{fam}: sampled graphs are all in the family", bool(is_valid(X, fam, 6).all())) check(f"{fam}: sample width = n_edges", X.shape[1] == n_edges(6)) except Exception as e: check("graph families", False, f"{type(e).__name__}: {e}") print("\n=== 10. molecules: the encoding is lossless ===") # Storing aromatic bond types loses per-ATOM aromaticity, so real molecules # failed to rebuild and generated-sample validity would have been scored against # a target the representation could not reach. Round-trip on REAL data is the # check that catches it. try: from qm9 import MolTokenizer, SEQ_LEN, MAX_ATOMS, load_qm9, validity mt = MolTokenizer() am = mt.allowed_mask() check("node slots forbid bond symbols", not am[:MAX_ATOMS][:, mt.bond_ids].any()) check("edge slots forbid atom symbols", not am[MAX_ATOMS:][:, mt.atom_ids].any()) check("no position may emit [MASK]", not am[:, mt.mask].any()) root = os.environ.get("QM9ROOT", "data/qm9") Xm, mtok, smis = load_qm9(root, limit=1500) v, _ = validity(Xm, mtok) check("round-trip validity on REAL molecules is ~100%", v > 0.999, f"{v*100:.2f}%") check("sequence width = 9 atoms + 36 bonds", Xm.shape[1] == SEQ_LEN, f"{Xm.shape[1]} vs {SEQ_LEN}") except FileNotFoundError: print(" [skip] qm9.csv not downloaded here") except Exception as e: check("molecule encoding", False, f"{type(e).__name__}: {e}") print("\n=== 11. budget conditioning ===") try: from data import build_addition as _ba _, _, tk = _ba(2, 4, 16, seed=0, exact=True) m0 = Transformer(len(tk), 32, 2, 2, "rope", causal=False, max_len=24) m1 = Transformer(len(tk), 32, 2, 2, "rope", causal=False, max_len=24, budget_bins=8) check("budget_bins=0 adds no parameters", m0.n_params() == m1.n_params() - 8 * 32, f"{m0.n_params()} vs {m1.n_params()}") # a K=1 decode only ever sees the fully-masked state, so t must be exactly 1 t1 = dfn._t_for_budget(torch.ones(256)) check("K=1 always draws t=1", bool((t1 == 1.0).all()), f"max dev {float((t1-1).abs().max()):.2e}") t4 = dfn._t_for_budget(torch.full((4096,), 4.0)) vals = sorted({round(float(v), 4) for v in t4}) check("K=4 draws only {0.25,0.5,0.75,1.0}", vals == [0.25, 0.5, 0.75, 1.0], str(vals)) check("t is never zero", bool((dfn._t_for_budget(torch.full((4096,), 64.0)) > 0).all())) b = Transformer.budget_bin(torch.tensor([1., 2., 4., 8., 1024.]), 8, "cpu") check("budget bins are log2 and clamped", b.tolist() == [0, 1, 2, 3, 7], str(b.tolist())) except Exception as e: check("budget conditioning", False, f"{type(e).__name__}: {e}") print(f"\n=== AUDIT: {len(FAIL)} failure(s) ===") for f in FAIL: print(" !", f) sys.exit(1 if FAIL else 0)