diff --git a/code/check_consistency.py b/code/check_consistency.py index 384ab21..64fc92c 100644 --- a/code/check_consistency.py +++ b/code/check_consistency.py @@ -233,13 +233,41 @@ if HAVE_TEX: # so the quoted floor must track the data and not one lucky draw. kl = rows("sec_keylen.csv") floor = min(float(r["eve_ser"]) for r in kl) -chk("eavesdropper floor over key length", abs(floor - 0.9984) < 5e-4, +chk("eavesdropper floor over key length", 0.9983 < floor < 0.9984, "%.6f" % floor) if HAVE_TEX: - chk("quoted eavesdropper floor in tex", "$0.9984$" in tex, + chk("quoted eavesdropper floor in tex", "$0.9983$" in tex, "searched tex", needs_tex=True) +# --- quantities that used to be quoted with no artifact --------------- +import json as _json +rs = _json.load(open(base / "data" / "real_sec_stats.json")) +chk("token collision probability", abs(rs["token_collision"] - 0.0065) < 5e-5, + "%.6f" % rs["token_collision"]) +vm = {r["check"]: r for r in rows("verify_math.csv")} +chk("V5 matched-over-blind ratio stored", + "V5 matched bias over blind RMS" in vm, + ", ".join(sorted(vm))[:60]) +chk("V8 cross-period remainder", + abs(float(vm["V8 cross-period remainder"]["empirical"])) < 5e-4, + vm["V8 cross-period remainder"]["empirical"]) + +# --- information-theoretic leakage, which no assertion covered --------- +it = {float(r["snr_db"]): r for r in rows("infotheory.csv")}[10.0] +chk("fixed-key leakage 1.34 bits", + abs(float(it["mi_eve_fixed_bits"]) - 1.34) < 5e-3, it["mi_eve_fixed_bits"]) +chk("distinguishing advantage 0.27", + abs(float(it["tv_fixed"]) - 0.27) < 5e-3, it["tv_fixed"]) +chk("refreshed leakage 0.055 bits", + abs(float(it["mi_eve_refresh_bits"]) - 0.055) < 5e-4, + it["mi_eve_refresh_bits"]) +chk("secrecy rate 14.87 of 14.93", + abs(float(it["secrecy_rate_refresh_bits"]) - 14.87) < 5e-3 + and abs(float(it["mi_legit_bits"]) - 14.93) < 5e-3, + "%s of %s" % (it["secrecy_rate_refresh_bits"], it["mi_legit_bits"])) + + # --- tables against their generator ----------------------------------- # Every printed table cell must be the one make_tables.py derives from # data/, so a rerun that moves a number cannot leave the manuscript behind. @@ -273,3 +301,6 @@ chk("abstract has no abbreviations", print() print("ALL CONSISTENT" if ok else "INCONSISTENCIES FOUND") +# a checker that always exits zero cannot gate anything +import sys as _sys +_sys.exit(0 if ok else 1) diff --git a/code/exp_real_sec.py b/code/exp_real_sec.py index 357a7f0..1591530 100644 --- a/code/exp_real_sec.py +++ b/code/exp_real_sec.py @@ -189,6 +189,10 @@ def main(): "repeats": REPEATS, "decisions_per_point": N * U * REPEATS, "distinct_tokens": int(torch.unique(ids_all).numel()), + # the chance that two independently drawn tokens coincide, which + # is the floor the insider TER is measured against + "token_collision": float(((torch.bincount(ids_all.reshape(-1)).double() + / ids_all.numel()) ** 2).sum()), "max_token_id": int(ids_all.max()), "headlines_scored": sum(len(b) for b in bounds), "headline_runs": REC_RUNS, diff --git a/code/verify_math.py b/code/verify_math.py index 585b363..eb6264c 100644 --- a/code/verify_math.py +++ b/code/verify_math.py @@ -185,6 +185,10 @@ def v5_matched_jammer_concentrates(): print(f"[{'PASS' if ok else 'FAIL'}] V5 matched bias / blind RMS: " f"matched={bm:.3f} blind_rms={brms:.4f} ratio={ratio:.1f} " f"(claim sqrt(d)={np.sqrt(D):.1f})") + ROWS.append(("V5 matched bias over blind RMS", + "%.1f" % np.sqrt(D), "%.1f" % ratio, + "%.2f" % abs(ratio - np.sqrt(D)), "1.0", + "PASS" if ok else "FAIL")) return ok @@ -260,16 +264,19 @@ def v8_cross_period_terms(): import math import torch from exp_full import main_model - torch.manual_seed(7) m = main_model() Bn = m.unit_codebook().detach().cpu() pat = m.masks().detach().cpu()[0] L, P, d = m.L, m.P, m.d + # a generator of its own, seeded after the model is built: seeding the + # global one first leaves the draw dependent on how main_model consumed + # it, which moved this number between runs + g = torch.Generator().manual_seed(7) rel = [] - for _ in range(300): - w = torch.randn(d) + for _ in range(20000): + w = torch.randn(d, generator=g) w /= w.norm() - i = torch.randint(m.vu, (P,)) + i = torch.randint(m.vu, (P,), generator=g) e = (Bn[i] / math.sqrt(P)).reshape(-1) a = (w * e).reshape(P, L) * pat[None, :] diag = float((a ** 2).sum()) diff --git a/data/real_sec_stats.json b/data/real_sec_stats.json index f362aff..673e7d0 100644 --- a/data/real_sec_stats.json +++ b/data/real_sec_stats.json @@ -5,6 +5,7 @@ "repeats": 8, "decisions_per_point": 799936, "distinct_tokens": 10486, + "token_collision": 0.006535221793661566, "max_token_id": 29599, "headlines_scored": 1948, "headline_runs": 4, diff --git a/data/verify_math.csv b/data/verify_math.csv index 8c715b5..a7267e8 100644 --- a/data/verify_math.csv +++ b/data/verify_math.csv @@ -8,6 +8,7 @@ V2b eve SER @ 80dB,0.99609375,0.9896666666666667,0.0064270833333333055,0.015,PAS V3b random mask E|corr|,0.09973557010035818,0.10187042771408686,0.002134857613728683,0.009973557010035819,PASS V4a blind jammer projection mean,0.0,0.00026396107284673695,0.00026396107284673695,0.003,PASS V4b blind jammer projection variance,0.01558576233568703,0.015476787953278994,0.00010897438240803532,0.0007792881167843516,PASS +V5 matched bias over blind RMS,8.0,8.0,0.04,1.0,PASS V6 coded-OMA outage @ 10 dB,0.0406,0.040575,0,0,REFERENCE V7 symbolic identities,exact,exact,0,0,PASS -V8 cross-period remainder,0.0,-0.001285,0.001285,0.01,PASS +V8 cross-period remainder,0.0,0.000337,0.000337,0.01,PASS