63 lines
2.4 KiB
Python
Raw Normal View History

Round-4 TSI report review: apply findings, editorial pass, code + figure fixes Applied the reconstructed round-4 review to the TSI parameter-selection report set (reports/tsi) and executed the follow-ups. Report (reports/tsi): - Applied the must+should findings across README + parts 1-4: cross-part numeric corrections, figure-caption fixes, spec reconciliation, and cross-file companions (hops-degradation and notch/reward numbers, tip-agreement ordering, density-window timing, VRF -> ZK Proof-of-Leadership, w_u window/reward gloss). - Editorial pass for timeless voice (no "now adopted / merged / coin" narration) and a gentle spec-safety framing (recommendations are thresholds; the protocol's MAX_UNCLES=4 sits safely above them). - Added the fork-rate-vs-scale table (6.10), defined "grinding gain", promoted the clock-skew study to its own paragraph, added the correlated-latency caveat, and moved fig27/fig28 beside their discussion. - Documented the Blend cascade in 2: hops propagate over the shared gossip graph (not direct links), the final broadcast comes from the last relay, relays are blind forwarders. Simulator (tools/simulators/tsi/tsi-sim-pernode): - Docstring/dead-code fixes: theory.block_count_ceiling (legacy framing), measure, reorg (catch-up reading), metrics (removed two dead helpers), config (fixed_point 10^-6; clock_skew_max/lottery_chunks documented inert), stake_vs_delay. - Generator correctness + regenerated figures: figures_pernode.CONFIG_COLS now exhaustive (f no longer pooled); rho_boundary_analysis SEM across replicates + hollow floored markers + de-hardcoded ell_mean (measured from the run's graph); appendix_fluct per-N sigma + ~18x title (figB2); bootstrap_dynamics driving estimate so fig1 epoch-0 matches genesis. - pytest: 186 passed; report links 528/0 dangling. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-31 13:13:03 +02:00
"""Per-epoch per-node divergence rows."""
2026-07-30 18:57:10 +02:00
from __future__ import annotations
from typing import Any
import numpy as np
from .config import SimConfig
from .epoch import EpochResult
# Config fields recorded on every row for grouping/plotting.
_CONFIG_FIELDS = (
"n_nodes", "stake_dist", "pareto_shape", "latency", "topology", "degree",
"link_latency_mean", "link_latency_dist", "blend_hops", "blend_delay_max",
"init_dest", "init_spread", "uncle_window", "max_uncles", "uncle_strategy",
"f", "beta", "k", "genesis_d_factor", "epochs", "fixed_point", "legacy_block_count",
"replicate",
"adversary_frac", "adversary_strategy", "adversary_period", "adversary_withhold_epochs",
)
def divergence_row(
config: SimConfig, epoch: int, d_in: np.ndarray, er: EpochResult, d_true: float
) -> dict[str, Any]:
"""One row per (config, epoch): per-node D_est spread + chain agreement."""
ratio = np.asarray(er.d_next, dtype=float) / d_true # (N,)
row: dict[str, Any] = {field: getattr(config, field) for field in _CONFIG_FIELDS}
row.update(
epoch=epoch,
mean_ratio=float(ratio.mean()),
median_ratio=float(np.median(ratio)),
std_ratio=float(ratio.std()),
min_ratio=float(ratio.min()),
max_ratio=float(ratio.max()),
range_ratio=float(ratio.max() - ratio.min()), # the headline divergence measure
iqr_ratio=float(np.percentile(ratio, 75) - np.percentile(ratio, 25)),
p10_ratio=float(np.percentile(ratio, 10)),
p90_ratio=float(np.percentile(ratio, 90)),
mean_ratio_in=float((np.asarray(d_in, dtype=float) / d_true).mean()),
range_ratio_in=float(np.ptp(np.asarray(d_in, dtype=float) / d_true)),
mean_m=float(np.mean(er.m)),
mean_q=float(np.nanmean(er.q)),
mean_q_eff=float(np.nanmean(er.q_eff)),
std_q=float(np.nanstd(er.q)),
agreement_window=er.agreement_window,
agreement_tip=er.agreement_tip,
mean_orphan_rate=er.mean_orphan_rate,
n_active_window=er.n_active_window,
n_blocks=er.n_blocks,
adv_blocks=er.adv_blocks,
honest_blocks=er.honest_blocks,
adv_block_share=(
er.adv_blocks / (er.adv_blocks + er.honest_blocks)
if (er.adv_blocks + er.honest_blocks) > 0 else 0.0
),
fork_rate=er.fork_rate,
max_reorg_depth=er.max_reorg_depth,
mean_reorg_depth=er.mean_reorg_depth,
p_ref=er.p_ref,
)
return row