amkkk's picture
download
raw
7.23 kB
"""Generate all plots for the reproduction logbook."""
import json
import sys
from pathlib import Path
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
OUT = Path(__file__).resolve().parent.parent / "outputs"
def load(name):
return json.loads((OUT / name).read_text())
def fig_m5_asymptotic():
"""Claim 2: E[tau] vs log(1/alpha) for m=5, with the 1/D asymptote."""
d = load("alpha_sweep_m5.json")
th = d["theory"]
L = np.array([r["log_inv_alpha"] for r in d["alpha_results"]])
E = np.array([r["mean_tau"] for r in d["alpha_results"]])
Estd = np.array([r["std_tau"] for r in d["alpha_results"]])
rate = th["asymptotic_rate"]
fig, ax = plt.subplots(figsize=(7, 5))
ax.errorbar(L, E, yerr=Estd, fmt="o-", color="C0", capsize=3,
label=r"measured $\mathbb{E}[\tau_\alpha]$")
ax.plot(L, L / th["D_M_inf"], "k--", lw=1.5,
label=rf"asymptotic LB $\log(1/\alpha)/D_M^\inf$ (slope $1/D={rate:.3f}$)")
ax.plot(L, L / th["naive_BPI_denom"], "r:", lw=1.5,
label=rf"naive BPI $\log(1/\alpha)/\|f\|_\infty$ (slope ${1/th['naive_BPI_denom']:.3f}$)")
ax.set_xscale("log"); ax.set_yscale("log")
ax.set_xlabel(r"$\log(1/\alpha)$"); ax.set_ylabel(r"expected stopping time $\mathbb{E}[\tau_\alpha]$")
ax.set_title(f"Claim 2 (m=5): $\\mathbb{{E}}[\\tau_\\alpha]$ tracks $\\log(1/\\alpha)/D_M^\\inf$ as $\\alpha\\to 0$\n"
f"$D_M^\\inf={th['D_M_inf']:.3f}$, $1/D={rate:.3f}$, naive $1/\\|f\\|_\\infty={1/th['naive_BPI_denom']:.3f}$")
ax.grid(True, which="both", alpha=0.3); ax.legend(loc="upper left", fontsize=9)
fig.tight_layout(); fig.savefig(OUT / "fig_claim2_m5.png", dpi=130)
plt.close(fig)
print("saved fig_claim2_m5.png")
def fig_ratio_convergence():
"""Claim 1+2: ratio E[tau]/log(1/alpha) -> 1/D as alpha->0 (m=5),
and stays far above 1/D for m=64 (overhead-dominated, non-asymptotic)."""
d5 = load("alpha_sweep_m5.json")
d64 = load("alpha_sweep_m64.json")
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13, 5))
for d, ax, name in [(d5, ax1, "m=5"), (d64, ax2, "m=64")]:
th = d["theory"]
L = np.array([r["log_inv_alpha"] for r in d["alpha_results"]])
R = np.array([r["mean_tau"] / r["log_inv_alpha"] for r in d["alpha_results"]])
ax.semilogx(L, R, "o-", color="C0", label=r"measured $\mathbb{E}[\tau]/\log(1/\alpha)$")
ax.axhline(th["asymptotic_rate"], color="k", ls="--",
label=rf"asymptotic $1/D_M^\inf={th['asymptotic_rate']:.3f}$")
ax.axhline(1.0 / th["naive_BPI_denom"], color="r", ls=":",
label=rf"naive BPI $1/\|f\|_\infty={1/th['naive_BPI_denom']:.3f}$")
ax.set_xlabel(r"$\log(1/\alpha)$")
ax.set_ylabel(r"$\mathbb{E}[\tau_\alpha]/\log(1/\alpha)$")
ax.set_title(f"{name}: $D_M^\\inf={th['D_M_inf']:.3f}$, "
f"$C_Q={th['C_Q']:.1f}$, $\\pi^*={th['pi_min']:.2e}$")
ax.grid(True, which="both", alpha=0.3); ax.legend(fontsize=9)
fig.suptitle("Claim 1+2: ratio $\\mathbb{E}[\\tau]/\\log(1/\\alpha)$ "
"$\\to 1/D_M^\\inf$ as $\\alpha\\to 0$ (left); overhead-dominated "
"for large m (right)", y=1.02)
fig.tight_layout(); fig.savefig(OUT / "fig_ratio_convergence.png", dpi=130, bbox_inches="tight")
plt.close(fig)
print("saved fig_ratio_convergence.png")
def fig_m64_table3():
"""Claim 1 (non-asymptotic): m=64 E[tau] nearly flat across alpha (overhead
dominates), reproducing paper Table 3's pattern. Compare to asymptotic LB."""
d = load("alpha_sweep_m64.json")
th = d["theory"]
L = np.array([r["log_inv_alpha"] for r in d["alpha_results"]])
E = np.array([r["mean_tau"] for r in d["alpha_results"]])
Estd = np.array([r["std_tau"] for r in d["alpha_results"]])
fig, ax = plt.subplots(figsize=(7, 5))
ax.errorbar(L, E, yerr=Estd, fmt="o-", color="C0", capsize=3,
label=r"measured $\mathbb{E}[\tau_\alpha]$ (m=64)")
ax.plot(L, L / th["D_M_inf"], "k--", lw=1.5,
label=rf"asymptotic LB $\log(1/\alpha)/D_M^\inf$")
ax.set_xscale("log")
ax.set_xlabel(r"$\log(1/\alpha)$"); ax.set_ylabel(r"$\mathbb{E}[\tau_\alpha]$")
ax.set_title("Claim 1 (m=64): $\\mathbb{E}[\\tau_\\alpha]$ is nearly flat in $\\alpha$\n"
"(overhead $(m-1)\\psi_t$ dominates $\\log(1/\\alpha)$; "
"matches paper Table 3 pattern)")
ax.grid(True, which="both", alpha=0.3); ax.legend(fontsize=9)
fig.tight_layout(); fig.savefig(OUT / "fig_claim1_m64.png", dpi=130)
plt.close(fig)
print("saved fig_claim1_m64.png")
def fig_claim3_tracking():
"""Claim 3 (part C): E[tau] tracks 1/D_M^inf across theta_Q."""
d = load("claim3_ablation.json")
rows = d["C"]["rows"]
D = np.array([r["D_M_inf"] for r in rows])
E = np.array([r["mean_tau"] for r in rows])
Estd = np.array([r["std_tau"] for r in rows])
invD = 1.0 / D
alpha = d["C"]["alpha"]
fig, ax = plt.subplots(figsize=(7, 5))
ax.errorbar(invD, E, yerr=Estd, fmt="o-", color="C0", capsize=3,
label=r"measured $\mathbb{E}[\tau_\alpha]$")
ax.plot(invD, invD * np.log(1.0 / alpha), "k--", lw=1.5,
label=rf"asymptotic LB $\log(1/\alpha)/D_M^\inf$ ($\alpha={alpha}$)")
ax.set_xlabel(r"$1/D_M^\inf(Q,\mathcal{P})$"); ax.set_ylabel(r"$\mathbb{E}[\tau_\alpha]$")
ax.set_title("Claim 3: $\\mathbb{E}[\\tau]$ tracks $1/D_M^\\inf$ across $\\theta_Q$\n"
"(both $\\pi_Q$ and the transition rows vary; bound stays predictive)")
ax.grid(True, alpha=0.3); ax.legend(fontsize=9)
fig.tight_layout(); fig.savefig(OUT / "fig_claim3_tracking.png", dpi=130)
plt.close(fig)
print("saved fig_claim3_tracking.png")
def fig_mcmc_trajectory():
"""MCMC (Figure 1): L_t trajectory under Q_bad (slope D_M^inf) vs Q_good (flat)."""
d = load("mcmc_figure1.json")
tb = d["traj_bad_run0"]; tg = d["traj_good_run0"]
D_bad = d["D_M_inf_Q_bad"]
fig, ax = plt.subplots(figsize=(8, 5))
t = np.array(tb["t"]); L = np.array(tb["L"]); beta = np.array(tb["beta"])
ax.plot(t, L, "C3-", label=r"$L_t$ under $Q_{bad}$ (alternative)")
ax.plot(t, beta, "k--", label=r"boundary $\beta_t$")
ax.plot(t, D_bad * t, "C3:", lw=1.5,
label=rf"theoretical slope $D_M^\inf={D_bad:.4f}$")
tg_t = np.array(tg["t"]); tg_L = np.array(tg["L"]); tg_beta = np.array(tg["beta"])
ax.plot(tg_t, tg_L, "C2-", label=r"$L_t$ under $Q_{good}$ (null)")
ax.plot(tg_t, tg_beta, "k--", alpha=0.4)
ax.set_xlabel("time $t$"); ax.set_ylabel("statistic $L_t$")
ax.set_title("MCMC misspecification (paper Figure 1): $L_t$ grows with slope "
"$D_M^\\inf$ under alt,\nstays below boundary under null (0/20 false rejections)")
ax.grid(True, alpha=0.3); ax.legend(fontsize=9)
fig.tight_layout(); fig.savefig(OUT / "fig_mcmc_figure1.png", dpi=130)
plt.close(fig)
print("saved fig_mcmc_figure1.png")
def main():
fig_m5_asymptotic()
fig_ratio_convergence()
fig_m64_table3()
fig_claim3_tracking()
fig_mcmc_trajectory()
if __name__ == "__main__":
main()

Xet Storage Details

Size:
7.23 kB
·
Xet hash:
68d982da6ef0ea99668e1d863a80bb5ba41d767b85f43eac20520b699d9d71e6

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.