SPB Git

spb/wp28_uqo

Public
27.2 KB · 291 lines python
1#!/usr/bin/env python32"""Generate paper/tables/*.tex (LaTeX fragments), paper/tables/numbers.tex (macros) and experiments/EXPERIMENT_MANIFEST.csv3from results/processed/summary.csv, results/processed/boundary_summary.csv, results/raw/ablation_*.csv, results/raw/skew_*.csv4and results/raw/benchmarks.jsonl.  Degrades gracefully when a result set is absent (the fragment then says so)."""5import glob, json, pathlib, sys6import numpy as np, pandas as pd7ROOT = pathlib.Path(__file__).resolve().parents[1]8PROC, RAW, TAB = ROOT / "results" / "processed", ROOT / "results" / "raw", ROOT / "paper" / "tables"9TAB.mkdir(parents=True, exist_ok=True)10LAB = {"v1_q0": "(1,0) valid workers only", "v1_q1": "(1,1) valid workers only", "v2_q0": "(2,0) valid workers only", "v2_q1": "(2,1) valid workers only", "v3_q1": "(3,1) valid workers only", "r1_q0": "(1,0) plain average", "r1_q1": "(1,1) average + 1 Newton", "r1_q2": "(1,2) average + 2 Newton", "r2_q0": "(2,0) half-block jackknife",11       "r2_q1": "(2,1) half-block + 1 Newton", "r2_q2": "(2,2) half-block + 2 Newton", "r3_q0": "(3,0) quarter-block", "r3_q1": "(3,1) quarter-block + 1 Newton",12       "r3_q2": "(3,2) quarter-block + 2 Newton", "cs_q0": "analytic Cox--Snell, $q=0$", "cs_q1": "analytic Cox--Snell + 1 Newton", "csl1": "CSL, 1 round", "csl2": "CSL, 2 rounds",13       "aee": "AEE (Lin--Xi)", "stale_r1_q1": "(1,1) stale Hessian", "stale_r2_q1": "(2,1) stale Hessian", "stale_r1_q2": "(1,2) stale Hessian", "central": "centralized MLE"}14MAIN = ["r1_q0", "r1_q1", "r1_q2", "r2_q0", "r2_q1", "r2_q2", "r3_q0", "r3_q1", "v2_q1", "v3_q1", "cs_q1", "csl1", "csl2", "aee", "stale_r2_q1", "central"]151617def fmt(x, d=3):18    if x is None or (isinstance(x, float) and not np.isfinite(x)):19        return "--"20    a = abs(x)21    if a == 0:22        return "0"23    if a < 1e-3 or a >= 1e4:24        return f"{x:.2e}".replace("e-0", "e-").replace("e+0", "e+")25    return f"{x:.{d}f}"262728def table_cell(s, label, p, N, ms, fname, caption, ests=MAIN):29    d = s[(s.label == label) & (s.p == p) & (s.N == N) & (s.m.isin(ms))]30    if d.empty:31        (TAB / fname).write_text(f"% no data for {label} p={p} N={N}\n"); return False32    ms = sorted(d.m.unique())33    if len(ms) > 4:                                      # keep at most four local sizes: the two extremes and two interior ones34        idx = sorted(set([0, len(ms) - 1, len(ms) // 3, (2 * len(ms)) // 3]))35        ms = [ms[i] for i in idx]36    lines = [r"\begin{table}[p]", r"\centering\scriptsize\setlength{\tabcolsep}{2.6pt}", r"\begin{threeparttable}", f"\\caption{{{caption}}}\\label{{tab:{fname[:-4]}}}",37             r"\begin{tabular}{@{}l" + "r" * (3 * len(ms)) + "@{}}", r"\toprule",38             "Estimator & " + " & ".join(f"\\multicolumn{{3}}{{c}}{{$m = {m}$, $K = {int(N//m):,}$}}" for m in ms) + r" \\",39             " & " + " & ".join("gap & median & cov" for _ in ms) + r" \\", r"\midrule"]40    for e in ests:41        row = [LAB.get(e, e)]42        for m in ms:43            dd = d[(d.estimator == e) & (d.m == m)]44            if dd.empty:45                row += ["--", "--", "--"]; continue46            dd = dd.iloc[0]47            row += [fmt(dd["rms_gap"]), fmt(dd["median_gap"]), (f"{dd['cov95']:.3f}" if "cov95" in dd and np.isfinite(dd["cov95"]) else "--")]48        lines.append(" & ".join(row) + r" \\")49    rp = d.drop_duplicates("m").sort_values("m").reps.astype(int).tolist()50    reps = str(rp[0]) if len(set(rp)) == 1 else "/".join(map(str, rp)); cf = d.drop_duplicates("m")[["m", "central_fail", "valid_frac_r2", "valid_frac_r3"]].sort_values("m") if "valid_frac_r3" in d else None51    lines += [r"\bottomrule", r"\end{tabular}", r"\begin{tablenotes}\footnotesize",52              f"\\item {reps} paired replications per column. gap $=$ root mean square of $\\sqrt N\\,\\lVert\\tilde\\theta - \\widehat\\theta_N\\rVert$ over all replications (including those with failed local fits); median $=$ its median; cov $=$ empirical coverage of nominal 95\\% Wald intervals (observed information at the estimate; `--' when not computed). Monte Carlo standard errors of the gaps and RMSEs are in \\texttt{{results/processed/summary.csv}}; coverage standard error $\\approx {np.sqrt(0.95*0.05/min(rp)):.3f}$ or less."]53    if cf is not None:54        lines.append("\\item Fraction of workers with all local fits valid (orders 2 / 3): " + "; ".join(f"$m={int(r.m)}$: {r.valid_frac_r2:.3f} / {r.valid_frac_r3:.3f}" for r in cf.itertuples()) + ". Rows `valid workers only' drop the workers with a failed sub-block fit from the pilot (weights renormalized) and coincide with the corresponding rows when no fit failed.")55    lines += [r"\end{tablenotes}", r"\end{threeparttable}", r"\end{table}"]56    (TAB / fname).write_text("\n".join(lines) + "\n"); return True575859def table_rates(s, fname="mc_rates.tex"):60    """Log-log slopes of rms_gap vs m at fixed N (largest N per model) for the main estimators."""61    rows = []62    for (label, p), d in s.groupby(["label", "p"]):63        N = d.N.max()64        for e in ["r1_q0", "r1_q1", "r1_q2", "r2_q0", "r2_q1", "r3_q0", "r3_q1", "cs_q1"]:65            dd = d[(d.N == N) & (d.estimator == e) & (d.rms_gap > 1e-9)].sort_values("m")66            if "valid_frac_r3" in dd:67                dd = dd[(dd.valid_frac_r3 >= 0.999) & (dd.valid_frac_r2 >= 0.999)]   # failure-free cells only68            if len(dd) >= 3:69                slope = np.polyfit(np.log(dd.m), np.log(dd.rms_gap), 1)[0]70                rows.append((label, p, N, e, slope, len(dd)))71    if not rows:72        (TAB / fname).write_text("% no data\n"); return73    th = {"r1_q0": -1, "r1_q1": -2, "r1_q2": -4, "r2_q0": -2, "r2_q1": -4, "r3_q0": -3, "r3_q1": -6, "cs_q1": -4}74    df = pd.DataFrame(rows, columns=["label", "p", "N", "estimator", "slope", "n"])75    piv = df.pivot_table(index=["label", "p", "N"], columns="estimator", values="slope")76    cols = [c for c in ["r1_q0", "r1_q1", "r1_q2", "r2_q0", "r2_q1", "r3_q0", "r3_q1", "cs_q1"] if c in piv.columns]77    lines = [r"\begin{table}[t]", r"\centering\small", r"\begin{threeparttable}", r"\caption{Empirical log-log slopes of the RMS scaled gap to $\widehat\theta_N$ against $m$ at fixed $N$ (largest $N$ of each design), with the slopes predicted by the leading bias term.}\label{tab:mc_rates}",78             r"\begin{tabular}{@{}llr" + "r" * len(cols) + "@{}}", r"\toprule", "Design & $p$ & $N$ & " + " & ".join(c.replace("_", "\\_") for c in cols) + r" \\",79             "predicted & & & " + " & ".join(str(th[c]) for c in cols) + r" \\", r"\midrule"]80    for idx, r in piv.iterrows():81        lines.append(f"{idx[0].replace('_', ' ')} & {idx[1]} & {int(idx[2]):,} & " + " & ".join(fmt(r[c], 2) for c in cols) + r" \\")82    lines += [r"\bottomrule", r"\end{tabular}", r"\begin{tablenotes}\footnotesize", r"\item Slopes are least-squares fits over the available $m$ grid; they approach the predicted exponent only where the bias term dominates the random remainder and the sample is in the asymptotic regime. The predicted slope for the Cox--Snell + 1 Newton estimator assumes an exact first-order bias formula ($-4$). Source: \texttt{results/processed/summary.csv}.", r"\end{tablenotes}", r"\end{threeparttable}", r"\end{table}"]83    (TAB / fname).write_text("\n".join(lines) + "\n")848586def table_boundary(fname="boundary.tex"):87    f = PROC / "boundary_summary.csv"88    if not f.exists():89        (TAB / fname).write_text("% no boundary data\n"); return90    b = pd.read_csv(f)91    sys.path.insert(0, str(ROOT / "experiments")); from boundary_pred import predicted_mean_gap92    b["pred_finite_m"] = [predicted_mean_gap(int(r.r), int(r.q), int(r.m), int(r.K), int(r.N)) for r in b.itertuples()]93    b.to_csv(PROC / "boundary_summary_with_prediction.csv", index=False)94    lines = [r"\begin{table}[t]", r"\centering\scriptsize", r"\begin{threeparttable}", r"\caption{Boundary study in the exponential model (Theorem~\ref{thm:exponential}(v)): mean of $\sqrt N(\theta^{(q)}_r - \widehat\theta_N)$ when $K \approx cN^{\alpha(r,q)}$, against the limit $-\theta_0(\beta_r c_{\mathrm{eff}}^r)^{2^q}$ ($q\ge1$) or $\beta_r\theta_0 c_{\mathrm{eff}}^r$ ($q=0$), with $c_{\mathrm{eff}} = K/N^{\alpha}$ after rounding $m$ to a multiple of the finest split.}\label{tab:boundary}",95             r"\begin{tabular}{@{}ccrrrrrrrr@{}}", r"\toprule", r"$(r,q)$ & $c$ & $N$ & $K$ & $m$ & reps & mean gap (s.e.) & finite-$m$ prediction & limit at $c_{\mathrm{eff}}$ & gap/limit \\", r"\midrule"]96    for (r, q, c), d in b.groupby(["r", "q", "c"]):97        for row in d.sort_values("N").itertuples():98            ratio = row.mean_gap / row.limit_c_eff if row.limit_c_eff != 0 else np.nan99            lines.append(f"({r},{q}) & {c:g} & $2^{{{int(np.log2(row.N))}}}$ & {row.K:,} & {row.m} & {row.reps} & {row.mean_gap:+.4g} ({row.se:.2g}) & {(f'{row.pred_finite_m:+.4g}' if np.isfinite(row.pred_finite_m) else '--')} & {row.limit_c_eff:+.4g} & {ratio:.2f} \\\\")100    lines += [r"\bottomrule", r"\end{tabular}", r"\begin{tablenotes}\footnotesize", r"\item Exact-Gamma simulation of block sufficient statistics; seed 20261028 plus a cell hash. The ratio converges to one only as $m\to\infty$; with $m \asymp N^{1/(2^{q+1}r)}$ the local sample sizes in the table are small and the finite-$m$ bias $\theta_0\sum_j w_js_j/(m-s_j)$ exceeds its leading term. Source: \texttt{results/processed/boundary\_summary.csv}.", r"\end{tablenotes}", r"\end{threeparttable}", r"\end{table}"]101    (TAB / fname).write_text("\n".join(lines) + "\n")102103104def table_ablation(model, fname):105    files = sorted(glob.glob(str(RAW / f"ablation_{model}_*.csv")))106    if not files:107        (TAB / fname).write_text(f"% no ablation data for {model}\n"); return None108    d = pd.concat([pd.read_csv(f) for f in files], ignore_index=True)109    d = d[np.isfinite(d.gap)]110    g = d.groupby(["N", "m", "variant", "pattern", "r", "q", "hessian", "bits"]).agg(rms=("gap", lambda x: np.sqrt(np.mean(x ** 2))), mean0=("gap0", "mean"), n=("gap", "size"), valid=("valid_frac", "mean")).reset_index()111    g.to_csv(PROC / f"ablation_{model}_summary.csv", index=False)112    # (a) patterns x q at the two largest m of the largest N113    N = g.N.max(); ms = sorted(g[g.N == N].m.unique())114    lines = [r"\begin{table}[p]", r"\centering\scriptsize", r"\begin{threeparttable}", f"\\caption{{Ablation ({model}, $N = {N:,}$): RMS of $\\sqrt N\\,\\lVert\\tilde\\theta - \\widehat\\theta_N\\rVert$ by split pattern, order $r$, Newton rounds $q$ and curvature used (exact / stale averaged local / single machine); quantized exact curvature with $b$ mantissa bits for $q = 1$.}}\\label{{tab:abl_{model}}}",115             r"\begin{tabular}{@{}llr" + "r" * len(ms) + "@{}}", r"\toprule", "Pattern & curvature & $q$ & " + " & ".join(f"$m = {m}$" for m in ms) + r" \\", r"\midrule"]116    for pat in ["1", "1-2", "1-3", "1-2-4", "1-2-3", "1-2-4-8", "1-2-3-4"]:117        for hess in ["exact", "stale", "single"]:118            for q in range(0, 4):119                if hess != "exact" and q == 0:120                    continue121                vals = []122                for m in ms:123                    dd = g[(g.N == N) & (g.m == m) & (g.pattern == pat) & (g.q == q) & (g.hessian == hess) & (g.bits == 64)]124                    vals.append(fmt(float(dd.rms.iloc[0])) if len(dd) else "--")125                if all(v == "--" for v in vals):126                    continue127                lines.append(f"({pat.replace('-', ',')}) & {hess} & {q} & " + " & ".join(vals) + r" \\")128    lines.append(r"\midrule")129    for pat in ["1", "1-2", "1-2-4"]:130        for hess, lab in [("quantizedH", "$b$-bit Hessian"), ("quantizedg", "$b$-bit score"), ("quantizedBoth", "$b$-bit both")]:131            for bits in sorted(g.bits.unique()):132                if bits == 64:133                    continue134                vals = []135                for m in ms:136                    dd = g[(g.N == N) & (g.m == m) & (g.pattern == pat) & (g.q == 1) & (g.hessian == hess) & (g.bits == bits)]137                    vals.append(fmt(float(dd.rms.iloc[0])) if len(dd) else "--")138                if all(v == "--" for v in vals):139                    continue140                lines.append(f"({pat.replace('-', ',')}) & {lab}, $b={bits}$ & 1 & " + " & ".join(vals) + r" \\")141    rc = d.groupby(["N", "m"]).rep.nunique(); reps = f"{int(rc.min())}--{int(rc.max())}" if rc.min() != rc.max() else str(int(rc.max()))142    lines += [r"\bottomrule", r"\end{tabular}", r"\begin{tablenotes}\footnotesize", f"\\item {reps} paired replications per column (exact count per cell in the processed CSV); $K = N/m$. Rows are omitted when the pattern does not divide $m$. Source: the processed ablation summary for the {model} model in \\texttt{{results/processed/}}.", r"\end{tablenotes}", r"\end{threeparttable}", r"\end{table}"]143    (TAB / fname).write_text("\n".join(lines) + "\n"); return g144145146def table_skew(fname="skew.tex"):147    files = sorted(glob.glob(str(RAW / "skew_*.csv")))148    if not files:149        (TAB / fname).write_text("% no skew data\n"); return150    d = pd.concat([pd.read_csv(f) for f in files], ignore_index=True)151    g = d.groupby(["model", "K", "ratio", "agg", "pilot"]).agg(gap0=("gap_q0", lambda x: np.sqrt(np.mean(x ** 2))), gap1=("gap_q1", lambda x: np.sqrt(np.mean(x ** 2))), gap2=("gap_q2", lambda x: np.sqrt(np.mean(x ** 2))), n=("rep", "size"), N=("N", "first")).reset_index()152    g.to_csv(PROC / "skew_summary.csv", index=False)153    lines = [r"\begin{table}[t]", r"\centering\scriptsize", r"\begin{threeparttable}", r"\caption{Unequal block sizes (Proposition~\ref{prop:robust}): RMS of $\sqrt N\,\lVert\tilde\theta - \widehat\theta_N\rVert$ for size-weighted and equal-weight aggregation of the raw and half-block-jackknife local estimates, with $q = 0, 1, 2$ Newton corrections. Sizes grow geometrically with max/min ratio as shown; $N \approx 2^{18}$.}\label{tab:skew}",154             r"\begin{tabular}{@{}llrrllrrr@{}}", r"\toprule", r"Model & $K$ & ratio & reps & weights & pilot & $q=0$ & $q=1$ & $q=2$ \\", r"\midrule"]155    for r in g.sort_values(["model", "K", "ratio", "agg", "pilot"]).itertuples():156        lines.append(f"{r.model} & {r.K} & {r.ratio:g} & {r.n} & {r.agg} & {r.pilot} & {fmt(r.gap0)} & {fmt(r.gap1)} & {fmt(r.gap2)} \\\\")157    lines += [r"\bottomrule", r"\end{tabular}", r"\begin{tablenotes}\footnotesize", r"\item Source: \texttt{results/processed/skew\_summary.csv}.", r"\end{tablenotes}", r"\end{threeparttable}", r"\end{table}"]158    (TAB / fname).write_text("\n".join(lines) + "\n")159160161def table_bench(fname="bench.tex"):162    f = RAW / "benchmarks.jsonl"163    if not f.exists():164        (TAB / fname).write_text("% no benchmark data\n"); return None165    rows = [json.loads(l) for l in open(f) if l.strip()]166    recs = []167    for r in rows:168        st = r["stats"]; A = st["stages"]["A_B_local_fit_and_upload"]; rounds = [v for k, v in st["stages"].items() if k.startswith("D_F")]169        recs.append({"label": r["label"], "K": r["K"], "hosts": r["n_hosts"], "p": r["p"], "m": r["m"], "r": len(r["splits"]), "q": r["q"], "hessian": r["hessian_mode"],170                     "t_total": r["t_total_after_connect"], "t_local": A["wall"], "fit_mean": A["worker_fit_s_mean"], "fit_max": A["worker_fit_s_max"], "lat99_A": A["latency_p99"],171                     "t_rounds": sum(v["wall"] for v in rounds), "round_wall": np.mean([v["wall"] for v in rounds]) if rounds else 0.0, "der_max": max([v["worker_der_s_max"] for v in rounds]) if rounds else 0.0,172                     "lat99_round": max([v["latency_p99"] for v in rounds]) if rounds else 0.0, "up_B": st["bytes_up"], "down_B": st["bytes_down"], "msgs": st["messages_up"] + st["messages_down"], "grad_final": r["newton"][-1]["grad_norm"] if r["newton"] else np.nan, "t_connect": r["t_connect"]})173    d = pd.DataFrame(recs)174    g = d.groupby(["label", "K", "hosts", "p", "r", "q", "hessian"]).agg(reps=("t_total", "size"), t_total=("t_total", "median"), t_local=("t_local", "median"), fit_mean=("fit_mean", "median"), fit_max=("fit_max", "median"),175                                                                     t_rounds=("t_rounds", "median"), round_wall=("round_wall", "median"), der_max=("der_max", "median"), lat99=("lat99_round", "median"), up_B=("up_B", "first"), down_B=("down_B", "first"), msgs=("msgs", "first"), grad=("grad_final", "median"), t_connect=("t_connect", "median")).reset_index()176    g.to_csv(PROC / "benchmarks_summary.csv", index=False)177    lines = [r"\begin{table}[p]", r"\centering\scriptsize\setlength{\tabcolsep}{2.2pt}", r"\begin{threeparttable}", r"\caption{Measured multi-node benchmarks of the protocol (logistic regression, $N = 2^{22}$, medians over repeats): wall-clock of the local phase (slowest worker) and of the synchronized derivative phases, worker compute times, 99th-percentile reply latency, and bytes moved.}\label{tab:bench}",178             r"\begin{tabular}{@{}lrrrrrlrrrrrrrr@{}}", r"\toprule", r"Design & $K$ & hosts & $p$ & $r$ & $q$ & curvature & local (s) & fit max (s) & rounds (s) & per round (s) & der.\ max (s) & lat.\ p99 (s) & up (kB) & down (kB) \\", r"\midrule"]179    for r in g.itertuples():180        lines.append(f"{r.label.replace('_', ' ')} & {r.K} & {r.hosts} & {r.p} & {r.r} & {r.q} & {r.hessian} & {r.t_local:.3f} & {r.fit_max:.3f} & {r.t_rounds:.3f} & {r.round_wall:.3f} & {r.der_max:.3f} & {r.lat99:.3f} & {r.up_B/1e3:.1f} & {r.down_B/1e3:.1f} \\\\")181    lines += [r"\bottomrule", r"\end{tabular}", r"\begin{tablenotes}\footnotesize", r"\item Python 3.9/3.14 + NumPy over TCP (length-prefixed pickle frames) on the MacLustr LAN (1 GbE/10 GbE mixed); coordinator on a Mac Studio M4 Max. `local' is the wall-clock from the first `fit' broadcast to the last upload, i.e.\ the slowest worker; `rounds' sums the synchronized derivative phases. Bytes include frame headers and pickle overhead. Repeats and raw logs: \texttt{results/raw/benchmarks.jsonl}.", r"\end{tablenotes}", r"\end{threeparttable}", r"\end{table}"]182    (TAB / fname).write_text("\n".join(lines) + "\n"); return g183184185def _g(s, label, p, m, est, col="rms_gap"):186    d = s[(s.label == label) & (s.p == p) & (s.m == m) & (s.estimator == est)]187    d = d[d.N == d.N.max()]188    return float(d[col].iloc[0]) if len(d) else float("nan")189190191def _f(x, d=2):192    if not np.isfinite(x):193        return "--"194    if abs(x) < 1e-3 or abs(x) >= 1e4:195        s = f"{x:.{d}e}"; mant, ex = s.split("e"); return f"${mant}\\times10^{{{int(ex)}}}$"196    return f"{x:.{d}g}" if abs(x) < 1 else f"{x:.{d+1}g}"197198199def quoted_numbers(s):200    """Numbers quoted in the prose of the Monte Carlo section; every one is computed here."""201    M = {}202    if s.empty:203        return M204    c = s[s.estimator.isin(["r1_q2", "r2_q1"])].pivot_table(index=["label", "p", "N", "m"], columns="estimator", values="rms_gap")205    M["fracOneTwoBeatsTwoOne"] = f"{100 * (c['r1_q2'] < c['r2_q1']).mean():.0f}"206    c = s[s.estimator.isin(["r2_q0", "r3_q0"])].pivot_table(index=["label", "p", "N", "m"], columns="estimator", values="rms_gap")207    w = (c["r3_q0"] < c["r2_q0"]).groupby(level="m").mean()208    M["fracThreeBeatsTwoSmallM"] = f"{100 * w.get(16, float('nan')):.0f}"; M["fracThreeBeatsTwoLargeM"] = f"{100 * w[w.index >= 256].mean():.0f}"209    c = s[s.estimator.isin(["r2_q1", "cs_q1"])].pivot_table(index=["label", "p", "N", "m"], columns="estimator", values="rms_gap")210    w = (c["cs_q1"] < c["r2_q1"]).groupby(level="label").mean()211    M["fracCSBeatsTwoOneLogit"] = f"{100 * w.get('logit', float('nan')):.0f}"; M["fracCSBeatsTwoOneContam"] = f"{100 * w.get('exp_contam', float('nan')):.0f}"212    M["gapLogitEightTwoOneMTwoFiftySix"] = _f(_g(s, "logit", 8, 256, "r2_q1")); M["gapLogitEightThreeOneMTwoFiftySix"] = _f(_g(s, "logit", 8, 256, "r3_q1"))213    M["gapLogitEightCSMTwoFiftySix"] = _f(_g(s, "logit", 8, 256, "cs_q1")); M["gapLogitEightOneTwoMTwoFiftySix"] = _f(_g(s, "logit", 8, 256, "r1_q2"))214    M["gapLogitEightOneOneMTwoFiftySix"] = _f(_g(s, "logit", 8, 256, "r1_q1")); M["gapLogitEightStaleMTwoFiftySix"] = _f(_g(s, "logit", 8, 256, "stale_r2_q1"))215    M["validLogitEightRthreeMOneTwentyEight"] = f"{100 * _g(s, 'logit', 8, 128, 'r2_q1', 'valid_frac_r3'):.0f}"; M["validLogitEightRthreeMSixtyFour"] = f"{100 * _g(s, 'logit', 8, 64, 'r2_q1', 'valid_frac_r3'):.1f}"216    M["validLogitThreeRthreeMThirtyTwo"] = f"{100 * _g(s, 'logit', 3, 32, 'r2_q1', 'valid_frac_r3'):.0f}"; M["validLogitThreeRtwoMThirtyTwo"] = f"{100 * _g(s, 'logit', 3, 32, 'r2_q1', 'valid_frac_r2'):.0f}"217    M["validPtwentyRtwoMOneTwentyEight"] = f"{100 * _g(s, 'logit_p20', 20, 128, 'r2_q1', 'valid_frac_r2'):.0f}"; M["validPtwentyRthreeMTwoFiftySix"] = f"{100 * _g(s, 'logit_p20', 20, 256, 'r2_q1', 'valid_frac_r3'):.0f}"218    M["covContamFisherMSixtyFour"] = f"{_g(s, 'exp_contam', 1, 64, 'r2_q1', 'cov95'):.2f}"; M["covContamSandwichMSixtyFour"] = f"{_g(s, 'exp_contam', 1, 64, 'central', 'cov95_sandwich'):.2f}"219    M["gapPoisEightTwoOneMOneTwentyEight"] = _f(_g(s, "pois", 8, 128, "r2_q1")); M["gapPoisEightThreeOneMOneTwentyEight"] = _f(_g(s, "pois", 8, 128, "r3_q1"))220    M["gapPoisEightTwoOneMTwoFiftySix"] = _f(_g(s, "pois", 8, 256, "r2_q1")); M["gapPoisEightThreeOneMTwoFiftySix"] = _f(_g(s, "pois", 8, 256, "r3_q1"))221    M["covExpOneOneMThirtyTwo"] = f"{_g(s, 'exp', 1, 32, 'r1_q1', 'cov95'):.2f}"; M["covExpTwoOneMSixteen"] = f"{_g(s, 'exp', 1, 16, 'r2_q1', 'cov95'):.2f}"; M["covExpTwoZeroMThirtyTwo"] = f"{_g(s, 'exp', 1, 32, 'r2_q0', 'cov95'):.2f}"222    M["NexpMax"] = f"{int(s[s.label == 'exp'].N.max()):,}"223    # ablation: smallest number of bits at which the quantized (1,2) one-step gap is within 5% of the exact one, largest m, largest N224    fa = PROC / "ablation_logistic_summary.csv"225    if fa.exists():226        g = pd.read_csv(fa); N = g.N.max(); mm = g[g.N == N].m.max(); base = g[(g.N == N) & (g.m == mm) & (g.pattern == "1-2") & (g.q == 1) & (g.hessian == "exact")].rms.iloc[0]227        for key, hess in (("bitsHalfBlockHessian", "quantizedH"), ("bitsHalfBlockBoth", "quantizedBoth")):228            q = g[(g.N == N) & (g.m == mm) & (g.pattern == "1-2") & (g.q == 1) & (g.hessian == hess)].sort_values("bits")229            ok = q[q.rms <= 1.05 * base]230            M[key] = str(int(ok.bits.iloc[0])) if len(ok) else "--"231        M["ablNlogit"] = f"{int(N):,}"; M["ablMlogit"] = str(int(mm)); M["ablKlogit"] = f"{int(N // mm):,}"232        M["ablAlphaLogit"] = f"{np.log(N // mm) / np.log(N):.2f}"233    fb = PROC / "boundary_summary_with_prediction.csv"234    if fb.exists():235        b = pd.read_csv(fb); b["ratio"] = b.mean_gap / b.limit_c_eff236        top = b[b.N == b.N.max()]237        r11 = top[(top.r == 1) & (top.q == 1)].ratio; r20 = top[(top.r == 2) & (top.q == 0)].ratio238        M["boundaryRatioOneOneMax"] = f"{max(abs(r11 - 1).max(), abs(r20 - 1).max()) * 100:.0f}"239        r21 = b[(b.r == 2) & (b.q == 1) & (b.m >= 8)].ratio; M["boundaryRatioTwoOneMin"] = f"{r21.min():.1f}"; M["boundaryRatioTwoOneMax"] = f"{r21.max():.1f}"240        M["boundaryMaxM"] = str(int(b[(b.r == 2) & (b.q == 1)].m.max()))241    return M242243244def manifest():245    rows = []246    for f in sorted(glob.glob(str(RAW / "*.manifest.json"))):247        m = json.load(open(f)); c = m["cell"]248        rows.append({"run_id": m["run_id"], "git_commit": m["git_commit"], "python_version": m["python"], "numpy": m["numpy"], "dependency_lock_sha256": "", "seed": m["seed"], "N": c["N"], "K": c["K"], "m": c["m"], "p": c["p"],249                     "DGP": json.dumps(c["dgp"]), "method": "estimate_all", "optimizer": "batched Newton, step halving, tol 1e-9 (central 1e-12)", "tolerance": "1e-9", "replicates": m["reps"], "parallel_backend": "xargs -P / nohup", "node_count": 1,250                     "hardware": m["host"] + " " + m["machine"], "OS": "macOS", "wall_seconds": round(m["wall_seconds"], 1), "communicated_bytes": 0, "rounds": "in-memory (no network)", "status": m["status"], "raw_output_sha256": m["raw_output_sha256"]})251    pd.DataFrame(rows).to_csv(ROOT / "experiments" / "EXPERIMENT_MANIFEST.csv", index=False)252    return len(rows)253254255def main():256    s = pd.read_csv(PROC / "summary.csv") if (PROC / "summary.csv").exists() else pd.DataFrame()257    made = {}258    if not s.empty:259        for (label, p), d in s.groupby(["label", "p"]):260            N = d.N.max()261            made[(label, p)] = table_cell(s, label, p, N, sorted(d.m.unique()), f"mc_{label}_p{p}.tex", f"Monte Carlo, {label.replace('_', ' ')} design, $p = {p}$, $N = {N:,}$: scaled gap to $\\widehat\\theta_N$ (root mean square and median) and 95\\% coverage by local sample size $m$.")262        table_rates(s)263    table_boundary(); table_ablation("exponential", "abl_exponential.tex"); table_ablation("logistic", "abl_logistic.tex"); table_skew(); table_bench()264    n = manifest()265    # numbers macros266    macros = {"numMCcells": str(s.groupby(["label", "p", "N", "K"]).ngroups if not s.empty else 0), "numManifest": str(n)}267    if not s.empty:268        macros["numMCrepsExp"] = str(int(s[s.label == "exp"].reps.max())) if (s.label == "exp").any() else "0"269        macros["numMCrepsLogit"] = str(int(s[s.label == "logit"].reps.max())) if (s.label == "logit").any() else "0"270    bf = PROC / "benchmarks_summary.csv"271    macros["benchHosts"] = str(int(pd.read_csv(bf).hosts.max())) if bf.exists() else "0"272    macros.update(quoted_numbers(s))273    if bf.exists():274        g = pd.read_csv(bf); G = lambda lab, col: float(g[g.label == lab][col].iloc[0]) if (g.label == lab).any() else float("nan")275        macros.update({"benchLocalTwoOne": f"{G('hosts16','t_local'):.2f}", "benchRoundTwoOne": f"{1000*G('hosts16','t_rounds'):.0f}", "benchLocalOneTwo": f"{G('cmp_r1q2','t_local'):.2f}",276                       "benchRoundsOneTwo": f"{1000*G('cmp_r1q2','t_rounds'):.0f}", "benchRoundsOneTwoS": f"{G('cmp_r1q2','t_rounds'):.3f}", "benchRoundTwoOneS": f"{G('hosts16','t_rounds'):.3f}", "benchLocalThreeOne": f"{G('cmp_r3q1','t_local'):.2f}", "benchLocalFourZero": f"{G('cmp_r4q0','t_local'):.2f}",277                       "benchLocalPsixtyfourTwoOne": f"{G('cmp_p64_r2q1','t_local'):.1f}", "benchRoundPsixtyfourTwoOne": f"{G('cmp_p64_r2q1','t_rounds'):.2f}", "benchLocalPsixtyfourOneTwo": f"{G('cmp_p64_r1q2','t_local'):.1f}",278                       "benchRoundsPsixtyfourOneTwo": f"{G('cmp_p64_r1q2','t_rounds'):.2f}", "benchDerMaxPsixtyfour": f"{G('cmp_p64_r2q1','der_max'):.2f}", "benchUpPsixtyfourTwoOne": f"{G('cmp_p64_r2q1','up_B')/1e3:.0f}",279                       "benchUpTwoOne": f"{G('hosts16','up_B')/1e3:.1f}", "benchLocalHostsOne": f"{G('hosts1','t_local'):.2f}", "benchLocalHostsSixteen": f"{G('hosts16','t_local'):.2f}", "benchFitMeanHostsSixteen": f"{G('hosts16','fit_mean'):.2f}",280                       "benchRoundHostsOne": f"{1000*G('hosts1','t_rounds'):.0f}", "benchLatencyExcess": f"{1000*(G('hosts16','lat99') - G('hosts16','der_max')):.1f}", "benchLocalKonetwentyeight": f"{G('K128_r2q1','t_local'):.2f}", "benchRoundKonetwentyeight": f"{1000*G('K128_r2q1','t_rounds'):.0f}",281                       "benchConnect": f"{G('hosts16','t_connect'):.1f}", "benchLocalPthirtytwoTwoOne": f"{G('p32_r2q1','t_local'):.2f}", "benchRoundPthirtytwoTwoOne": f"{G('p32_r2q1','t_rounds'):.2f}", "benchLocalPthirtytwoOneTwo": f"{G('p32_r1q2','t_local'):.2f}", "benchRoundsPthirtytwoOneTwo": f"{G('p32_r1q2','t_rounds'):.2f}", "benchLocalPthirtytwoThreeOne": f"{G('p32_r3q1','t_local'):.2f}", "benchLocalKtwofiftysix": f"{G('K256_r2q1','t_local'):.2f}", "benchRoundKtwofiftysix": f"{1000*G('K256_r2q1','t_rounds'):.0f}", "benchLatNinetyNineKtwofiftysix": f"{1000*G('K256_r2q1','lat99'):.0f}", "benchRuns": str(int(g.reps.sum())), "benchDerMaxPeight": f"{1000*G('hosts16','der_max'):.0f}"})282    with open(TAB / "numbers.tex", "w") as f:283        f.write("% Auto-generated by experiments/make_tables.py\n")284        for k, v in macros.items():285            f.write(f"\\newcommand{{\\{k}}}{{{v}}}\n")286    print("tables written:", sorted(p.name for p in TAB.glob("*.tex")), "manifest rows:", n)287288289if __name__ == "__main__":290    main()291
Files · main
Menu

Theme