SabaPivot's picture
download
raw
8.29 kB
"""Figures for the reproduction poster / logbook."""
import sys, os, json
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
OUT = os.path.join(ROOT, "outputs")
FIG = os.path.join(ROOT, "figs")
os.makedirs(FIG, exist_ok=True)
ACC = "#0F766E" # teal accent
ACC2 = "#B45309" # amber
ACC3 = "#334155" # slate
plt.rcParams.update(
{
"font.size": 13,
"axes.grid": True,
"grid.alpha": 0.25,
"axes.spines.top": False,
"axes.spines.right": False,
"figure.dpi": 150,
"savefig.bbox": "tight",
}
)
def load(name):
with open(os.path.join(OUT, name)) as f:
return json.load(f)
c2 = load("claim2_thm56.json")
c3 = load("claim3_thm512.json")
c4 = load("claim4_thm61_62.json")
c4b = load("claim4b_blocks.json")
c5 = load("claim5_covering.json")
c6 = load("claim6_prop51.json")
blk = load("block_sweep.json")
# ============================================================ FIG 1 : minimax rates
fig, ax = plt.subplots(1, 2, figsize=(11.5, 4.3))
h = c3["hard_family_minimax_per_coordinate"]
N = np.array([r["N"] for r in h])
ax[0].loglog(
N,
[r["erm_worstcase_per_coord_EXACT"] for r in h],
"o-",
color=ACC,
label="ERM worst case (exact) slope %.2f" % c3["hard_N_exponent_ERM"][0],
)
ax[0].loglog(
N,
[r["sga_worstcase_per_coord_MC"] for r in h],
"s-",
color=ACC2,
label="SGA + averaging slope %.2f" % c3["hard_N_exponent_SGA"][0],
)
ax[0].loglog(
N,
[r["bayes_lower_bound_per_coord"] for r in h],
"^--",
color=ACC3,
label="exact Bayes minimax LB slope %.2f" % c3["hard_N_exponent_LB"][0],
)
ax[0].set_xlabel("training sample size $N$")
ax[0].set_ylabel("excess risk per coupling constraint")
ax[0].set_title("Minimax regime: rate in $N$ (predicted $-1/2$)", fontsize=13)
ax[0].legend(fontsize=9.5, frameon=False)
sr = c3["hard_family_s_scaling"]
S = np.array([r["s"] for r in sr])
ax[1].loglog(
S,
[r["erm_risk"] for r in sr],
"o-",
color=ACC,
label="ERM slope %.2f" % c3["hard_s_exponent_ERM"][0],
)
ax[1].loglog(
S,
[r["sga_risk"] for r in sr],
"s-",
color=ACC2,
label="SGA + averaging slope %.2f" % c3["hard_s_exponent_SGA"][0],
)
ax[1].loglog(
S,
[r["minimax_lb"] for r in sr],
"^--",
color=ACC3,
label="exact minimax LB slope 1.00",
)
ref = np.array([r["minimax_lb"] for r in sr])[0] * (S / S[0]) ** 1.5
ax[1].loglog(S, ref, ":", color="#94A3B8", label="Thm 5.5 shape $s^{1.5}$")
ax[1].set_xlabel("number of coupling constraints $s$")
ax[1].set_ylabel("excess risk, $N=256$")
ax[1].set_title("Minimax regime: rate in $s$ (LB predicts $+1$)", fontsize=13)
ax[1].legend(fontsize=9.5, frameon=False)
fig.tight_layout()
fig.savefig(os.path.join(FIG, "fig1_minimax_rates.png"))
plt.close(fig)
# ============================================================ FIG 2 : block MILP family
fig, ax = plt.subplots(1, 2, figsize=(11.5, 4.3))
cells = blk["cells"]
S_LIST = blk["S_LIST"]
cmap = plt.cm.viridis(np.linspace(0.1, 0.85, len(S_LIST)))
for k, s in enumerate(S_LIST):
sub = [c for c in cells if c["s"] == s]
ax[0].loglog(
[c["N"] for c in sub],
[c["erm_excess_mean"] for c in sub],
"o-",
color=cmap[k],
label="$s=%d$ slope %.2f"
% (s, blk["fits"]["erm_N_exponent_per_s"][str(s)][0]),
)
ax[0].set_xlabel("training sample size $N$")
ax[0].set_ylabel("ERM excess risk $\\mathcal{E}(\\hat\\pi)$")
ax[0].set_title("Block-decomposable MILP family: exact ERM risk", fontsize=13)
ax[0].legend(fontsize=9, frameon=False)
Nsel = blk["N_LIST"][-1]
sub = [c for c in cells if c["N"] == Nsel]
S = np.array([c["s"] for c in sub])
erm = np.array([c["erm_excess_mean"] for c in sub])
ax[1].loglog(
S,
erm,
"o-",
color=ACC,
label="ERM measured, slope %.2f"
% blk["fits"]["erm_s_exponent_per_N"][str(Nsel)][0],
)
ax[1].loglog(
S,
[c["thm55_constant_bound"] for c in sub],
"--",
color=ACC3,
label="Thm 5.5 constant $12\\sqrt{\\pi}B\\pi_{\\max}s^{1.5}/\\sqrt{N}$",
)
ax[1].loglog(
S,
[c["thm512_constant_bound"] for c in sub],
":",
color=ACC2,
label="Thm 5.12 constant $2B\\pi_{\\max}s/\\sqrt{N}$",
)
ax[1].set_xlabel("number of coupling constraints $s$")
ax[1].set_ylabel("excess risk, $N=%d$" % Nsel)
ax[1].set_title("Measured risk vs the theorems' explicit constants", fontsize=13)
ax[1].legend(fontsize=9, frameon=False)
fig.tight_layout()
fig.savefig(os.path.join(FIG, "fig2_block_family.png"))
plt.close(fig)
# ============================================================ FIG 3 : warm start
fig, ax = plt.subplots(1, 2, figsize=(11.5, 4.3))
rows = c4b["rows"]
S_LIST = sorted({r["s"] for r in rows})
cmap = plt.cm.plasma(np.linspace(0.1, 0.8, len(S_LIST)))
for k, s in enumerate(S_LIST):
sub = [r for r in rows if r["s"] == s]
ax[0].loglog(
[r["N"] for r in sub],
[r["exact_excess_risk"] for r in sub],
"o-",
color=cmap[k],
label="$s=%d$ slope %.2f" % (s, c4b["N_exponents"][str(s)][0]),
)
ax[0].set_xlabel("training sample size $N$")
ax[0].set_ylabel("warm-start excess risk")
ax[0].set_title(
"Thm 6.1: $\\mathcal{E}(\\hat\\phi)=O(s/N)$ on block MILPs", fontsize=13
)
ax[0].legend(fontsize=9, frameon=False)
lb = c4["C_bayes_per_coordinate"]
Ns = sorted(int(k) for k in lb.keys())
per = [lb[str(n)]["bayes_per_coordinate"] for n in Ns]
s0 = 16
ax[1].loglog(
Ns,
[s0 * p for p in per],
"^--",
color=ACC3,
label="exact minimax LB (Thm 6.2) slope %.2f" % c4["C_N_exponent"][0],
)
ax[1].loglog(
Ns,
[s0 * 1.0 / (4 * n) for n in Ns],
"--",
color=ACC2,
label="Popoviciu UB $s\\pi_{\\max}^2/4N$ (Thm 6.1)",
)
sub16 = [r for r in rows if r["s"] == 16]
ax[1].loglog(
[r["N"] for r in sub16],
[r["exact_excess_risk"] for r in sub16],
"o-",
color=ACC,
label="measured ERM risk, block MILPs",
)
ax[1].set_xlabel("training sample size $N$")
ax[1].set_ylabel("excess risk, $s=16$")
ax[1].set_title("$\\Theta(s/N)$ sandwich: upper and lower bounds", fontsize=13)
ax[1].legend(fontsize=9, frameon=False)
fig.tight_layout()
fig.savefig(os.path.join(FIG, "fig3_warmstart.png"))
plt.close(fig)
# ============================================================ FIG 4 : Prop 5.1 + covering
fig, ax = plt.subplots(1, 2, figsize=(11.5, 4.3))
aud = c6["f_boundary_audit"]
rho = [a["claimed_B_over_true_B"] for a in aud]
ax[0].plot(
rho,
[a["max_ratio"] for a in aud],
"o-",
color=ACC,
label="max $\\|g\\|_2/(2B'\\sqrt{s})$",
)
ax[0].plot(
rho,
[a["violation_frac"] for a in aud],
"s-",
color=ACC2,
label="fraction of violations",
)
ax[0].axhline(1.0, color=ACC3, ls="--", lw=1)
ax[0].invert_xaxis()
ax[0].set_xlabel("claimed $B'$ / true Assumption-4.1 $B$")
ax[0].set_ylabel("ratio to the bound")
ax[0].set_title(
"Prop 5.1(ii) boundary audit: the bound fails\nexactly when Assumption 4.1 is broken",
fontsize=12,
)
ax[0].legend(fontsize=9.5, frameon=False)
ax[0].grid(alpha=0.25)
nets = c5["explicit_nets"]
packs = c5["packing_lower_estimates"]
xs = np.arange(len(nets))
ax[1].bar(
xs - 0.22,
[n["lemma53_bound"] for n in nets],
width=0.22,
color=ACC3,
label="Lemma 5.3 bound",
)
ax[1].bar(
xs,
[n["log_net_size"] for n in nets],
width=0.22,
color=ACC,
label="explicit $\\delta$-net built (verified)",
)
pk = {(p["s"], p["delta"]): p["log_packing_lower_est"] for p in packs}
ax[1].bar(
xs + 0.22,
[pk[(n["s"], n["delta"])] for n in nets],
width=0.22,
color=ACC2,
label="greedy $2\\delta$-packing (lower est.)",
)
ax[1].set_xticks(xs)
ax[1].set_xticklabels(
["$s$=%d\n$\\delta$=%g" % (n["s"], n["delta"]) for n in nets], fontsize=8
)
ax[1].set_ylabel("$\\log N(\\delta,\\mathcal{U},\\|\\cdot\\|_{2,N})$")
ax[1].set_title("Lemma 5.3 holds, with 8-14 nats of slack", fontsize=12)
ax[1].legend(fontsize=9, frameon=False)
ax[1].grid(alpha=0.25, axis="y")
fig.tight_layout()
fig.savefig(os.path.join(FIG, "fig4_prop51_covering.png"))
plt.close(fig)
print("figures written to", FIG, os.listdir(FIG))

Xet Storage Details

Size:
8.29 kB
·
Xet hash:
6f1519efaa69c97cc39d998cad21d55aca29258af16657563fe9971ae298eb68

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