67 lines
3.3 KiB
Python
Raw Normal View History

Make the model carry-over metric-specific instead of wholesale §9 claimed that studies predating the countable model "carry over unchanged" because no difference is resolvable at ρ < 1. That is true of accuracy and false of the reference rate, and asserting it wholesale is what let the §8.4 capstone sit at p_ref = 1.000 for weeks after the redesign made it 0.944. Measured per metric off the committed paired band (δ_max 1–5, U ≥ 1, common random numbers, so the uncle rule is the only difference): D̂/D 0.9994 vs 1.0000 +0.00055 t = 4.9 carries over fork rate 0.2256 vs 0.2255 -0.00010 t = -2.0 carries over orphan rate 0.2256 vs 0.2255 -0.00009 t = -2.2 carries over tip agreement 0.9943 vs 0.9950 +0.00070 t = 0.9 carries over p_ref 0.9260 vs 0.9998 +0.07381 t = 155.0 RE-MEASURE p_ref fails in kind, not in degree: the unrestricted rule makes every in-window orphan referenceable, so its ≈1.0 restates the model rather than measuring the design. Any pre-countable p_ref is wrong by ~7 points. Also corrects §6.7(1), which still asserted p_ref ≈ 1 for genuine latency orphans — §6.8 had been corrected to the measured 0.944 but the earlier claim was left standing — and drops "no difference is resolvable" from §9, which contradicted §3.2a's pooled t = 5.7. Adds scripts/model_carryover.py so the verdict is reproducible rather than asserted, and a §9 study-table row. deep_ref_share is unaffected (0 by construction under the countable rule); no other reference-structure metric is quoted from a pre-countable run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 11:47:51 +02:00
"""Which metrics carry over from the unrestricted model to the countable one? (§9)
Most of the report's studies predate the countable default. They reproduce exactly under `--old`,
so the question is not whether they are valid runs it is whether their *numbers* still describe
the countable design. §9 used to answer that wholesale ("no difference is resolvable in the design
regime, so those findings carry over"), and that is how the §8.4 capstone went stale: its `p_ref`
was quoted at 1.000 for weeks after the redesign made the true value 0.944.
The wholesale answer is wrong because the carry-over is metric-specific. Accuracy barely moves
between the models at `rho < 1` that is the §3.2a finding. But `p_ref` is not a small difference,
it is an artefact: under the unrestricted rule EVERY in-window orphan is referenceable, so
`p_ref ~ 1` is a restatement of the model rather than a measurement of the design.
This prints the per-metric verdict off the committed paired sweep, where both arms share the stake
draw, the peering graph and every lottery outcome, so the uncle rule is the only difference and the
comparison is a paired one. Any metric that fails here must be re-measured rather than carried over.
Run: python scripts/model_carryover.py
"""
from __future__ import annotations
from pathlib import Path
import pandas as pd
HERE = Path(__file__).resolve().parent.parent
RUNS = HERE / "runs"
# The design band: the operating regime the recommendation lives in. U = 0 is excluded because
# with no uncle slots the two models are identical by construction and would dilute the contrast.
METRICS = ["mean_ratio", "fork_rate", "mean_orphan_rate", "agreement_tip", "p_ref"]
CELL = ["blend_delay_max", "max_uncles", "replicate"]
def _load(label: str) -> pd.DataFrame:
"""Equilibrium tail of the newest paired run with this label."""
src = sorted(RUNS.glob(f"*_{label}/results.parquet"))[-1]
df = pd.read_parquet(src)
return df[(df.epoch >= df.epochs.iloc[0] // 2) & (df.max_uncles > 0)]
def main() -> None:
countable, unrestricted = _load("fine-paired-countable"), _load("fine-paired-old")
print("=== countable vs unrestricted ceiling, PAIRED, design band (delta_max 1-5, U >= 1) ===")
print(f"{'metric':>18} {'countable':>11} {'ceiling':>11} {'paired diff':>20} {'t':>7} verdict")
for m in METRICS:
if m not in countable.columns:
continue
a = countable.groupby(CELL)[m].mean()
b = unrestricted.groupby(CELL)[m].mean()
i = a.index.intersection(b.index)
d = b[i] - a[i]
# Paired t over cells. A metric "carries over" if the gap is negligible against the
# +-0.9% per-epoch noise floor of Appendix B, NOT if it merely fails to resolve --- with
# 40 paired replicates even a 0.06% gap resolves, and that one is still immaterial.
t = d.mean() / d.sem() if d.std(ddof=1) > 0 else float("nan")
verdict = "carries over" if abs(d.mean()) < 0.009 else "RE-MEASURE"
print(f"{m:>18} {a[i].mean():11.4f} {b[i].mean():11.4f} "
f"{d.mean():+12.5f}+-{d.sem():.5f} {t:7.1f} {verdict}")
print("\np_ref is the one that fails: the unrestricted model makes every in-window orphan\n"
"referenceable, so its ~1.0 is a property of the model, not of the design.")
if __name__ == "__main__":
main()