Thibaut Forest Claude Opus 5 (1M context) commited on
Commit ·
645d27e
1
Parent(s): a6c0af5
cv: de-tune harness from champion output shape (user) - rank_stats/rec12/rec20 removed, plan-derived swaps kept; SCORE reproduces 1972.0
Browse filesThe purge harness now requires only that a method yield a plan: the
pred-shape ranking metrics assumed a per-device p42 ranking, which not
every method will have. models_cv.rank_stats untouched (ad-hoc use).
CSV schema change: n_swaps replaces the rank columns; stale /tmp
candidate CSVs deleted. (Attribution note for the record: the cv.py
consolidation itself is 4f5f060 by session 59; a6c0af5 was
EXPERIMENTS.md only despite its message.)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- EXPERIMENTS.md +16 -0
- batteryswap_example/cv.py +20 -21
EXPERIMENTS.md
CHANGED
|
@@ -2940,3 +2940,19 @@ receipts are phases 51-54 and they are not to be re-argued unprompted.
|
|
| 2940 |
The leaderboard closures (appetite, cert-first, pooling) rest on public
|
| 2941 |
measurements and survive any CV change. Public best 3c7b883 at 1661.45;
|
| 2942 |
private selection recommendation unchanged.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2940 |
The leaderboard closures (appetite, cert-first, pooling) rest on public
|
| 2941 |
measurements and survive any CV change. Public best 3c7b883 at 1661.45;
|
| 2942 |
private selection recommendation unchanged.
|
| 2943 |
+
|
| 2944 |
+
**Phase 55 addendum (2026-08-22, user): harness de-tuned from the champion's
|
| 2945 |
+
output shape.** "cv.py is tuned towards our current model (rec12 rec20 etc),
|
| 2946 |
+
remove those to make sure we can use the cv on any method we try." Removed
|
| 2947 |
+
the `rank_stats` call and every column built on it (rec12/rec20/recall/prec/
|
| 2948 |
+
miss_rank/n_sched/caught/n_deaths) - all assumed the method emits a
|
| 2949 |
+
per-device p42 ranking. The harness now requires only that a method produce
|
| 2950 |
+
a PLAN; the one diagnostic kept beside the cost blocks is `swaps` (in-window
|
| 2951 |
+
unique batteries read from the plan itself), preserving the phase-39/51
|
| 2952 |
+
appetite-in-disguise guard method-agnostically. Cost path untouched: SCORE
|
| 2953 |
+
reproduced 1972.0 exactly post-change (106s). `models_cv.rank_stats` remains
|
| 2954 |
+
for ad-hoc ordering diagnostics on methods that do rank. CSV SCHEMA CHANGE:
|
| 2955 |
+
/tmp/cv_all48oof.csv and cv_oofcand_* now carry n_swaps instead of the rank
|
| 2956 |
+
columns; the pre-change stale candidate CSVs were deleted (they were
|
| 2957 |
+
denominated in dead instruments regardless - any candidate must re-run fresh
|
| 2958 |
+
through `cv oofcand <label>`).
|
batteryswap_example/cv.py
CHANGED
|
@@ -17,10 +17,12 @@ Design notes, paid for in prior phases:
|
|
| 17 |
fleet size, so holding devices out of the plan manufactures a count bias
|
| 18 |
(phase 26).
|
| 19 |
|
| 20 |
-
*
|
| 21 |
-
recall/miss_rank
|
| 22 |
-
|
| 23 |
-
|
|
|
|
|
|
|
| 24 |
|
| 25 |
* Known optimism channels, accepted by user decision 2026-08-22 with the
|
| 26 |
numbers in view: devices are one-shot events, so same-device pairs from
|
|
@@ -106,24 +108,20 @@ def load_table(dataset_path=Path('dataset')):
|
|
| 106 |
|
| 107 |
|
| 108 |
def scorecard(df, label):
|
| 109 |
-
"""Three-regime scorecard
|
|
|
|
|
|
|
| 110 |
print(f'\n=== {label} ===')
|
| 111 |
-
hdr = (f"{'block':16s} {'n':>3s} {'total':>8s} {'late':>7s}
|
| 112 |
-
f"{'
|
| 113 |
-
f"{'sched':>5s} {'miss':>5s}")
|
| 114 |
print(hdr)
|
| 115 |
for name, sel in BLOCKS:
|
| 116 |
d = df[sel(df['origin'])]
|
| 117 |
if d.empty:
|
| 118 |
continue
|
| 119 |
-
deaths, caught = d['n_deaths'].sum(), d['caught'].sum()
|
| 120 |
-
sched = d['n_sched'].sum()
|
| 121 |
print(f"{name:16s} {len(d):3d} {d['total_cost'].mean():8.1f} "
|
| 122 |
f"{d['late_swap'].mean():7.1f} {d['early_swap'].mean():6.1f} "
|
| 123 |
-
f"{d['
|
| 124 |
-
f"{caught / max(deaths, 1):6.3f} {caught / max(sched, 1):5.3f} "
|
| 125 |
-
f"{d['n_sched'].mean():5.1f} "
|
| 126 |
-
f"{d['miss_rank'].median():5.1f}")
|
| 127 |
|
| 128 |
|
| 129 |
# --------------------------------------------------------------------------
|
|
@@ -297,8 +295,9 @@ def run_all48_oof(origins=range(48), dataset_path=Path('dataset'), kind=None,
|
|
| 297 |
_, _, overall = evaluate_plan(plan, locs, c['travel_costs'],
|
| 298 |
c['settings'], eol_times=eol,
|
| 299 |
start_time=T, verbose=0)
|
| 300 |
-
|
| 301 |
-
recs.append(dict(overall,
|
|
|
|
| 302 |
print(f"s_{idx}: oof {recs[-1]['total_cost']:7.1f}",
|
| 303 |
flush=True, file=sys.stderr)
|
| 304 |
return pd.DataFrame(recs)
|
|
@@ -345,14 +344,14 @@ def oof_candidate_report(base_csv='/tmp/cv_all48oof.csv',
|
|
| 345 |
base = pd.read_csv(base_csv)
|
| 346 |
b = base[base['arm'] == 'oof'].set_index('origin')
|
| 347 |
print('paired vs champion (logbag/base, temporal purge, floor off).')
|
| 348 |
-
print('
|
| 349 |
-
'disguise (phase 39/51): trust a win only if
|
| 350 |
hdr = (f"{'candidate':14s} {'ALL48':>7s} {'delta':>7s} {'W/L':>7s} "
|
| 351 |
-
f"{'panel':>7s} {'pdelta':>7s} {'pW/L':>7s} {'
|
| 352 |
print(hdr)
|
| 353 |
print(f"{'base(oof)':14s} {b['total_cost'].mean():7.1f} {'-':>7s} {'-':>7s} "
|
| 354 |
f"{b.loc[(b.index >= 8) & (b.index <= 41), 'total_cost'].mean():7.1f} "
|
| 355 |
-
f"{'-':>7s} {'-':>7s} {b['
|
| 356 |
out = []
|
| 357 |
for fn in sorted(glob.glob(pattern)):
|
| 358 |
d = pd.read_csv(fn).set_index('origin')
|
|
@@ -365,7 +364,7 @@ def oof_candidate_report(base_csv='/tmp/cv_all48oof.csv',
|
|
| 365 |
f"{int((delta < 0).sum()):3d}W/{int((delta > 0).sum()):2d}L "
|
| 366 |
f"{d.loc[(d.index >= 8) & (d.index <= 41), 'total_cost'].mean():7.1f} "
|
| 367 |
f"{pan.mean():+7.1f} {int((pan < 0).sum()):3d}W/{int((pan > 0).sum()):2d}L "
|
| 368 |
-
f"{d['
|
| 369 |
|
| 370 |
|
| 371 |
def oof_report(df):
|
|
|
|
| 17 |
fleet size, so holding devices out of the plan manufactures a count bias
|
| 18 |
(phase 26).
|
| 19 |
|
| 20 |
+
* Diagnostics are PLAN-DERIVED only (cost blocks + in-window swap count).
|
| 21 |
+
The pred-shape ranking metrics (rec12/rec20/recall/prec/miss_rank) were
|
| 22 |
+
removed 2026-08-22 (user): they assumed the method emits a per-device p42
|
| 23 |
+
ranking, which not every method will. The harness scores anything that
|
| 24 |
+
yields a plan; `models_cv.rank_stats` remains for ad-hoc ordering
|
| 25 |
+
diagnostics on methods that do rank.
|
| 26 |
|
| 27 |
* Known optimism channels, accepted by user decision 2026-08-22 with the
|
| 28 |
numbers in view: devices are one-shot events, so same-device pairs from
|
|
|
|
| 108 |
|
| 109 |
|
| 110 |
def scorecard(df, label):
|
| 111 |
+
"""Three-regime cost scorecard. Plan-derived columns only, so it works
|
| 112 |
+
on ANY method: the swap count comes from the plan, never from the
|
| 113 |
+
prediction's shape."""
|
| 114 |
print(f'\n=== {label} ===')
|
| 115 |
+
hdr = (f"{'block':16s} {'n':>3s} {'total':>8s} {'late':>7s} "
|
| 116 |
+
f"{'early':>6s} {'swaps':>5s}")
|
|
|
|
| 117 |
print(hdr)
|
| 118 |
for name, sel in BLOCKS:
|
| 119 |
d = df[sel(df['origin'])]
|
| 120 |
if d.empty:
|
| 121 |
continue
|
|
|
|
|
|
|
| 122 |
print(f"{name:16s} {len(d):3d} {d['total_cost'].mean():8.1f} "
|
| 123 |
f"{d['late_swap'].mean():7.1f} {d['early_swap'].mean():6.1f} "
|
| 124 |
+
f"{d['n_swaps'].mean():5.1f}")
|
|
|
|
|
|
|
|
|
|
| 125 |
|
| 126 |
|
| 127 |
# --------------------------------------------------------------------------
|
|
|
|
| 295 |
_, _, overall = evaluate_plan(plan, locs, c['travel_costs'],
|
| 296 |
c['settings'], eol_times=eol,
|
| 297 |
start_time=T, verbose=0)
|
| 298 |
+
in_win = plan.loc[plan['day'] <= T + W, 'battery']
|
| 299 |
+
recs.append(dict(overall, n_swaps=int(in_win.nunique()),
|
| 300 |
+
origin=idx, arm='oof'))
|
| 301 |
print(f"s_{idx}: oof {recs[-1]['total_cost']:7.1f}",
|
| 302 |
flush=True, file=sys.stderr)
|
| 303 |
return pd.DataFrame(recs)
|
|
|
|
| 344 |
base = pd.read_csv(base_csv)
|
| 345 |
b = base[base['arm'] == 'oof'].set_index('origin')
|
| 346 |
print('paired vs champion (logbag/base, temporal purge, floor off).')
|
| 347 |
+
print('swaps (plan-derived) shown because a cost delta can be appetite '
|
| 348 |
+
'in disguise (phase 39/51): trust a win only if swaps is ~flat.\n')
|
| 349 |
hdr = (f"{'candidate':14s} {'ALL48':>7s} {'delta':>7s} {'W/L':>7s} "
|
| 350 |
+
f"{'panel':>7s} {'pdelta':>7s} {'pW/L':>7s} {'swaps':>5s}")
|
| 351 |
print(hdr)
|
| 352 |
print(f"{'base(oof)':14s} {b['total_cost'].mean():7.1f} {'-':>7s} {'-':>7s} "
|
| 353 |
f"{b.loc[(b.index >= 8) & (b.index <= 41), 'total_cost'].mean():7.1f} "
|
| 354 |
+
f"{'-':>7s} {'-':>7s} {b['n_swaps'].mean():5.1f}")
|
| 355 |
out = []
|
| 356 |
for fn in sorted(glob.glob(pattern)):
|
| 357 |
d = pd.read_csv(fn).set_index('origin')
|
|
|
|
| 364 |
f"{int((delta < 0).sum()):3d}W/{int((delta > 0).sum()):2d}L "
|
| 365 |
f"{d.loc[(d.index >= 8) & (d.index <= 41), 'total_cost'].mean():7.1f} "
|
| 366 |
f"{pan.mean():+7.1f} {int((pan < 0).sum()):3d}W/{int((pan > 0).sum()):2d}L "
|
| 367 |
+
f"{d['n_swaps'].mean():5.1f}")
|
| 368 |
|
| 369 |
|
| 370 |
def oof_report(df):
|