mirror of
https://github.com/logos-blockchain/research.git
synced 2026-08-07 11:43:20 +00:00
61 lines
2.6 KiB
Python
61 lines
2.6 KiB
Python
|
|
"""fig3 — uncle recovery under the Blend cascade: hops × per-hop delay × U (4-panel grid).
|
|||
|
|
|
|||
|
|
Committed generator for report fig3. Previously fig3 was produced ad hoc (via make_figures on the
|
|||
|
|
blend-hops-delay run, then hand-copied into report-figures/) and had NO reproducible source in the
|
|||
|
|
repo; this script closes that gap. Per uncle cap U it plots mean D̂/D vs the per-hop budget δ_max,
|
|||
|
|
one curve per hop count, at N=1000 from the canonical blend-hops-delay sweep (per-trajectory 50%
|
|||
|
|
burn-in via figures_pernode.equilibrium). Accuracy is bounded by 1 (slot-counting cannot over-count
|
|||
|
|
occupied slots), so the y-axis is capped at the exact-recovery bound — no above-1 headroom.
|
|||
|
|
|
|||
|
|
Run: python scripts/hops_delay_grid.py
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import sys
|
|||
|
|
from pathlib import Path
|
|||
|
|
|
|||
|
|
import pandas as pd
|
|||
|
|
|
|||
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
|||
|
|
|
|||
|
|
from tsi_sim.plotting import style # noqa: E402
|
|||
|
|
from tsi_sim.plotting.figures_pernode import equilibrium # noqa: E402
|
|||
|
|
|
|||
|
|
HERE = Path(__file__).resolve().parent.parent
|
|||
|
|
RUNS = HERE / "runs"
|
|||
|
|
FIGS = HERE / "report-figures"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def main() -> None:
|
|||
|
|
import matplotlib.pyplot as plt
|
|||
|
|
src = sorted(RUNS.glob("*_blend-hops-delay/results.parquet"))[-1]
|
|||
|
|
eq = equilibrium(pd.read_parquet(src))
|
|||
|
|
eq = eq[(eq.n_nodes == 1000) & (eq.topology == "blend") & (eq.stake_dist == "pareto")]
|
|||
|
|
us = sorted(eq.max_uncles.unique())
|
|||
|
|
hops = sorted(eq.blend_hops.unique())
|
|||
|
|
style.apply_style()
|
|||
|
|
fig, axes = plt.subplots(1, len(us), figsize=(3.2 * len(us), 3.6), sharey=True)
|
|||
|
|
for ax, U in zip(axes, us, strict=True):
|
|||
|
|
s = eq[eq.max_uncles == U]
|
|||
|
|
for i, h in enumerate(hops):
|
|||
|
|
g = s[s.blend_hops == h].groupby("blend_delay_max").mean_ratio.agg(["mean", "sem"])
|
|||
|
|
ax.errorbar(g.index, g["mean"], yerr=g["sem"], fmt="-o", ms=4, capsize=2,
|
|||
|
|
color=style.OKABE_ITO[i], label=f"{int(h)} hops")
|
|||
|
|
ax.axhline(1.0, color="0.4", lw=1.0, ls="--", zorder=0)
|
|||
|
|
ax.axhline(0.98, color="0.75", lw=0.8, ls=":", zorder=0)
|
|||
|
|
ax.set_title(f"U = {int(U)}")
|
|||
|
|
ax.set_xlabel(r"per-hop budget $\delta_{max}$ (s)")
|
|||
|
|
axes[0].set_ylabel(r"mean $\hat D / D$")
|
|||
|
|
axes[0].set_ylim(top=1.01) # bounded by 1: cap at the exact-recovery bound
|
|||
|
|
axes[0].legend(fontsize=8, loc="lower left")
|
|||
|
|
fig.suptitle(r"Uncle recovery under Blend cascade: hops × per-hop delay × U "
|
|||
|
|
r"(pareto, N=1000, f=1/30)", y=1.02)
|
|||
|
|
style.save(fig, FIGS / "fig3_hops_delay", provenance="scripts/hops_delay_grid.py")
|
|||
|
|
plt.close(fig)
|
|||
|
|
print("wrote fig3_hops_delay")
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|