mirror of
https://github.com/logos-blockchain/research.git
synced 2026-08-07 19:53:10 +00:00
74 lines
3.1 KiB
Python
74 lines
3.1 KiB
Python
|
|
"""Window sufficiency at scale + the W-as-buffer question (fig25, report §3.4).
|
|||
|
|
|
|||
|
|
From the window-scale sweep (N = 1 000 vs 10 000, W = 50..600, delta in {8, 16, 32} s, U in
|
|||
|
|
{1, 2}, k = 256): the window floor's position is N-invariant, a wider window buys back the
|
|||
|
|
near-boundary (rho ~ 1) undershoot at U = 1, and no window fixes sustained overload (rho > U).
|
|||
|
|
|
|||
|
|
Run: python scripts/window_scale_analysis.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
|
|||
|
|
|
|||
|
|
HERE = Path(__file__).resolve().parent.parent
|
|||
|
|
RUNS = HERE / "runs"
|
|||
|
|
FIGS = HERE / "report-figures"
|
|||
|
|
|
|||
|
|
BAR = 0.98
|
|||
|
|
|
|||
|
|
|
|||
|
|
def main() -> None:
|
|||
|
|
src = sorted(RUNS.glob("*_window-scale/results.parquet"))[-1]
|
|||
|
|
df = pd.read_parquet(src)
|
|||
|
|
# Early stop terminates each config once it converges (~epoch 16), well short of the
|
|||
|
|
# nominal ``epochs`` (40). A fixed ``epoch >= 20`` tail would drop almost every config;
|
|||
|
|
# take the second half of each config's *actually-run* epochs instead (matches the
|
|||
|
|
# burn_frac=0.5 tail used elsewhere).
|
|||
|
|
keys = ["n_nodes", "blend_delay_max", "uncle_window", "max_uncles", "replicate"]
|
|||
|
|
tail_from = df.groupby(keys).epoch.transform("max") * 0.5
|
|||
|
|
t = df[df.epoch >= tail_from]
|
|||
|
|
eq = (t.groupby(["n_nodes", "blend_delay_max", "uncle_window", "max_uncles"],
|
|||
|
|
as_index=False).mean_ratio.mean())
|
|||
|
|
|
|||
|
|
import matplotlib.pyplot as plt
|
|||
|
|
style.apply_style()
|
|||
|
|
fig, axes = plt.subplots(1, 3, figsize=(13.2, 4.0), sharey=True)
|
|||
|
|
for ax, delay in zip(axes, (8.0, 16.0, 32.0), strict=True):
|
|||
|
|
for n, u in ((1000, 1), (10000, 1), (1000, 2), (10000, 2)):
|
|||
|
|
s = eq[(eq.blend_delay_max == delay) & (eq.n_nodes == n)
|
|||
|
|
& (eq.max_uncles == u)].sort_values("uncle_window")
|
|||
|
|
ax.plot(s.uncle_window, s.mean_ratio,
|
|||
|
|
"-o" if u == 1 else "--s", ms=4, lw=1.3,
|
|||
|
|
color=style.OKABE_ITO[0 if n == 1000 else 1],
|
|||
|
|
label=f"N={n:,}, U={u}" if delay == 8.0 else None)
|
|||
|
|
ax.axhline(BAR, color="0.7", lw=0.8, ls="--")
|
|||
|
|
ax.set_xlabel("uncle window W (slots)")
|
|||
|
|
ax.set_title(f"blending budget δ = {delay:g} s")
|
|||
|
|
axes[0].set_ylabel(r"$\hat D / D$")
|
|||
|
|
axes[0].text(60, BAR + 0.006, "0.98 recovery bar", fontsize=7, color="0.5")
|
|||
|
|
axes[0].legend(fontsize=8, loc="lower right")
|
|||
|
|
fig.suptitle("Window sufficiency at scale: a wider W buys back the ρ ≈ 1 boundary (middle) "
|
|||
|
|
"but cannot fix sustained overload (right)", y=1.03)
|
|||
|
|
style.save(fig, FIGS / "fig25_window_scale",
|
|||
|
|
provenance=f"scripts/window_scale_analysis.py ({src.parent.name})")
|
|||
|
|
plt.close(fig)
|
|||
|
|
print("wrote fig25_window_scale")
|
|||
|
|
|
|||
|
|
for delay in (8.0, 16.0, 32.0):
|
|||
|
|
s = eq[(eq.blend_delay_max == delay) & (eq.max_uncles == 1)]
|
|||
|
|
piv = s.pivot(index="n_nodes", columns="uncle_window", values="mean_ratio")
|
|||
|
|
print(f"\nU=1 δ={delay:g}:")
|
|||
|
|
print(piv.round(3).to_string())
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|