HEAD: T3 warmup gate - gated hazard order + alpha 1.4 boost - CV 1418.3
Browse filesWhile the training fleet's death ledger at the plan origin is below
WARMUP_RATE (0.0759/device = 35/461), Ft2RUL switches ORDER to a
censored-pool discrete-time hazard HGB and boosts appetite x1.4 via a
consume-once plan_tsroute.alpha_boost; past the gate the model is
byte-identical to the previous head (WARMUP_RATE=0 disables exactly,
verified 0.0 max cell diff vs 1541.3).
bgkf: 1418.3, -123.0 +/- 43.1 (2.9sigma), 14W/5L, Wilcoxon .0017.
COUNT-COUPLED (swaps 2.54 vs 2.27) - shipped on the user's CV-first
call for a public trigger. Scale harness: gate narrows as fleets grow
(8/4/0 active scenarios at 1x/2x/4x) - bounded downside. Rehearsal:
gate fired on exactly s_0-14, s_15+ byte-identical, 0 warnings.
Includes T3 lane reports (gm label-statistic axis closed: the exact
label is a lagging smoother; cold-start gating; scale validation) and
phases 65-66.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- EXPERIMENTS.md +55 -0
- agent_reports_bgkf/t3_coldstart.md +121 -0
- agent_reports_bgkf/t3_gm.md +92 -0
- agent_reports_bgkf/t3_scale_counts.md +116 -0
- batteryswap_example/models_cv.py +7 -1
- batteryswap_example/submit.py +103 -0
|
@@ -3391,3 +3391,58 @@ cv.py run_bgkf split_days/timing_weight hooks, ft2a10/head track the
|
|
| 3391 |
deployed dials. Verified: cand ft2a10 = 1541.3, cell-exact (0.0) vs the
|
| 3392 |
lane's lam2sp3 measurement; paired -4.11 +/- 1.41 (2.9sig) 25W/4L
|
| 3393 |
p8.8e-6, swaps flat 2.27. New champion CV 1541.3.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3391 |
deployed dials. Verified: cand ft2a10 = 1541.3, cell-exact (0.0) vs the
|
| 3392 |
lane's lam2sp3 measurement; paired -4.11 +/- 1.41 (2.9sig) 25W/4L
|
| 3393 |
p8.8e-6, swaps flat 2.27. New champion CV 1541.3.
|
| 3394 |
+
|
| 3395 |
+
## Phase 65 - CV frontier 1398.0 (user CV-first directive): warmup-gated alpha 1.4 x gated hazard order, both at deaths_seen<35
|
| 3396 |
+
|
| 3397 |
+
User directive: CV is the reported/optimized metric; count-coupled
|
| 3398 |
+
candidates are KEPT (not auto-shipped) as champions-in-waiting. Frontier
|
| 3399 |
+
config st_hz100t35_g16: at origins where the planned fleet's recorded
|
| 3400 |
+
deaths <= T number fewer than 35 (~s_0-15 on bgkf), ORDER = discrete-time
|
| 3401 |
+
hazard HGB (censored-pool trained, c14/c17 machinery) replacing the 3-way
|
| 3402 |
+
blend, and planner alpha = 1.4; everywhere else byte-identical to the
|
| 3403 |
+
1541.3 head. CV 1398.0, -143.3 +/- 37.4 (3.8sig), 16W/0L, Wilcoxon <.001,
|
| 3404 |
+
swaps 2.53/cell (vs 2.27; count-coupled by design).
|
| 3405 |
+
|
| 3406 |
+
Decomposition of the -143.3: gated alpha 1.4 width-16 alone -125.4
|
| 3407 |
+
(4.0sig, clean bowl: width 6/8/10/12/14/16/20/24/32/48 ->
|
| 3408 |
+
1501/1483/1462/1436/1429/1416/1414/1438/1475/1502; level bowl 1.3/1.4/
|
| 3409 |
+
1.45/1.5/1.6 -> 1476/1436-1403/1416/1427-1449/1490 with 1.4 optimal at
|
| 3410 |
+
every width); hazard order adds -13 at thr20 and -18 at thr35 (16W/0L
|
| 3411 |
+
final). EC break-even expansion (teammate mechanism through our pricing)
|
| 3412 |
+
REFUTED: margin 1.0/1.2/1.5 monotone worse (+17/+29/+46), so the count
|
| 3413 |
+
posture belongs to gated alpha, not EC top-ups. Teammate public analysis
|
| 3414 |
+
(1486.57 vs our 1575.31): +191 swaps -> late -353, early +316, ops -53;
|
| 3415 |
+
pure operating-point difference, no luck involved either way.
|
| 3416 |
+
|
| 3417 |
+
Caveats attached to 1398.0: gate widths selected on the shared 48
|
| 3418 |
+
scenarios (multiplicity); deployment rule re-expressed as deaths_seen<35
|
| 3419 |
+
(plan-time observable, FLOOR_GATE family); the composite harness carries
|
| 3420 |
+
exact-rank-tie noise vs a deployed-class implementation (16/240 cells,
|
| 3421 |
+
documented in t3_coldstart.md) - a real ship needs the gate inside the
|
| 3422 |
+
deployed class with non-gated origins byte-identical. Pending: t3_gm
|
| 3423 |
+
label-statistic lane; synthetic-scale validation of the count-coupled
|
| 3424 |
+
family (user-commissioned; key question = does the gate close early on
|
| 3425 |
+
large fleets, bounding the downside). Drivers /tmp/c19, /tmp/c20
|
| 3426 |
+
(volatile); arm CSVs /tmp/cv_bgkfcand_c19_*, c20_*.
|
| 3427 |
+
|
| 3428 |
+
## Phase 66 - SHIPPED the warmup gate (user call): head CV 1418.3
|
| 3429 |
+
|
| 3430 |
+
Deployed form of the phase-65 frontier inside Ft2RUL (user approved the
|
| 3431 |
+
ship for a public trigger): rate-form gate WARMUP_RATE=0.0759 deaths per
|
| 3432 |
+
training-fleet device (= 35/461; a fixed count fires wider on the CV's
|
| 3433 |
+
4/5-size fold ledgers than on deployment's full ledger - measured 1429.7
|
| 3434 |
+
fixed vs 1418.3 rate), TRAIN-ledger observable seen = deaths recorded <=
|
| 3435 |
+
plan origin; while gated: ORDER = censored-pool discrete-time hazard HGB
|
| 3436 |
+
(_DiscHaz, person-period buckets, depth3/lr.06/250it), appetite x1.4 via
|
| 3437 |
+
a consume-once plan_tsroute.alpha_boost. eol column derived from
|
| 3438 |
+
origin+days_to_eol when absent (the cached parquet ships no eol; exact,
|
| 3439 |
+
0 diff on 8206 dying rows - this bug made the first rehearsal silently
|
| 3440 |
+
inert). VERIFIED: cand ft2a10 = 1418.3, -123.0 +/- 43.1 (2.9sig, 14W/5L,
|
| 3441 |
+
Wilcoxon .0017, 19 changed origins, swaps 2.54); WARMUP_RATE=0 control
|
| 3442 |
+
bit-exact vs the 1541.3 head (0.0 max cell diff); rehearsal: gate fired
|
| 3443 |
+
on exactly s_0-14 (+2..+5 swaps each), s_15+ byte-identical, 0 warnings,
|
| 3444 |
+
41s. Delta vs the phase-65 composite 1398.0 (~+20) = the composite's
|
| 3445 |
+
harness-only advantages (full-table hazard pool, literal-16 gate, tie
|
| 3446 |
+
noise); the deployed number is the honest one. COUNT-COUPLED - public
|
| 3447 |
+
trigger arbitrates; previous head remains 60d65da (1541.3/public
|
| 3448 |
+
lineage 1575.31) via WARMUP_RATE=0.
|
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# T3 COLD-START lane: order adapted to training-information richness
|
| 2 |
+
|
| 3 |
+
Agent: T3 cold-start (c17). Date 2026-08-23 (deadline day). Work dir /tmp/c17/
|
| 4 |
+
(driver run.py, analysis analyze.py, gate diagnostic gate_diag.py). Arm CSVs at
|
| 5 |
+
/tmp/cv_bgkfcand_c17_*.csv. Reference /tmp/cv_bgkfcand_ft2a10.csv = deployed
|
| 6 |
+
head 60d65da at 1541.3 (asserted). All arms: values/counts from primary
|
| 7 |
+
logbag(vm7)@alpha1.0 untouched (order-only via run_bgkf blend hook at w 1.0,
|
| 8 |
+
count-flat by construction; swaps 2.275 vs ref 2.267 on every arm),
|
| 9 |
+
split_days=3, timing_weight=2.0.
|
| 10 |
+
|
| 11 |
+
## The gate (plan-time observable, no forward reads)
|
| 12 |
+
|
| 13 |
+
deaths_seen(origin) = number of unique TRAIN-fleet devices with recorded
|
| 14 |
+
eol <= the plan origin date. Computed at fit time from the fold-train pool
|
| 15 |
+
(dev_eol ledger, exactly walk_forward's / submit.py FLOOR_GATE's family),
|
| 16 |
+
evaluated per predict() on the origin timestamp the rows carry. A death is
|
| 17 |
+
observable from the moment it happens; the train fleet's labels are fully
|
| 18 |
+
known when a foreign fleet is served -- deployment-legal both ways.
|
| 19 |
+
Gate g = 1[deaths_seen < 20] (FLOOR_GATE parity). Firing pattern (fold
|
| 20 |
+
crossings of 20 at origins [11,12,9,12,9]): all of warmup s_0-7 plus a
|
| 21 |
+
2-4-origin bleed into early panel (s_8-11). deaths_seen at s_0 is 1-3,
|
| 22 |
+
s_7 is 12-15. Hazard source = c14's DiscHazHGB (discrete-time hazard HGB,
|
| 23 |
+
person-period buckets, censored pool via mc.censored_pairs at max origin
|
| 24 |
+
filtered to train devices -- byte-identical to run_bgkf's own hazard branch).
|
| 25 |
+
|
| 26 |
+
## Baseline calibration (matters for reading the table)
|
| 27 |
+
|
| 28 |
+
cs_champ (my composite replication of the deployed order, champion weights,
|
| 29 |
+
no gate) scores +18.5 +/- 15.0 vs the deployed ref with only 16/240 cells
|
| 30 |
+
differing, dominated by 4 large sign-mixed jumps (+546, +280, +277, -248):
|
| 31 |
+
the documented exact-rank-tie flips (submit.py's own comment: one flipped tie
|
| 32 |
+
moved a cell by 539; the blend hook's extra rank layer resolves ties
|
| 33 |
+
differently than Ft2RUL's internal sort). So:
|
| 34 |
+
- "vs ref" carries ~15-cost-unit tie-flip noise unrelated to the mechanism;
|
| 35 |
+
- "vs cs_champ" is the clean paired mechanism read (same code path, plans
|
| 36 |
+
bitwise identical at every non-gated origin -- deltas exactly 0 there);
|
| 37 |
+
- "ship-proj" = delta vs ref restricted to gate-active origins (s_0-11),
|
| 38 |
+
exact 0 elsewhere: what a deployed-class implementation would score, since
|
| 39 |
+
the gate leaves non-gated origins byte-identical to the deployed head.
|
| 40 |
+
|
| 41 |
+
## Results (48 paired all-building scenario totals)
|
| 42 |
+
|
| 43 |
+
vs deployed ref 1541.3 | per-block = per-origin mean within block
|
| 44 |
+
|
| 45 |
+
| arm | SCORE | vs ref | vs cs_champ (clean) | ship-proj | W/L(ref) | wilc(ref) | swaps | warmup | panel | nearend |
|
| 46 |
+
|------------|--------|---------------|-------------------------|----------------|----------|-----------|-------|--------|-------|---------|
|
| 47 |
+
| cs_champ | 1559.8 | +18.5+/-15.0 | -- | -- | 6W/8L | p0.391 | 2.275 | +34.6 | +15.5 | +13.6 |
|
| 48 |
+
| cs_hz10 | 1556.5 | +15.2+/-15.9 | -3.3+/-4.0 (0.8s) | ~0 | 7W/10L | p0.548 | 2.275 | +38.8 | +10.0 | +13.6 |
|
| 49 |
+
| cs_hz20 | 1547.0 | +5.7+/-18.3 | -12.8+/-9.5 (1.3s) | -7.0+/-11.6 | 9W/10L | p1.000 | 2.275 | -18.5 | +10.0 | +13.6 |
|
| 50 |
+
| cs_hz35 | 1549.9 | +8.6+/-18.2 | -9.8+/-12.5 (0.8s) | -4.1+/-11.5 | 9W/10L | p0.738 | 2.275 | -27.0 | +16.1 | +13.6 |
|
| 51 |
+
| cs_hz60 | 1520.0 | -21.3+/-23.1 | -39.7+/-20.1 (2.0s) | -34.0+/-17.9 (1.9s) | 14W/10L | p0.331 | 2.275 | -149.0 | +2.6 | +13.6 |
|
| 52 |
+
| cs_hz100 | 1514.7 | -26.6+/-28.0 | -45.1+/-25.5 (1.8s) | -39.3+/-23.9 (1.6s) | 14W/11L | p0.474 | 2.275 | -177.9 | +1.9 | +13.6 |
|
| 53 |
+
| cs_hz20t30 | 1540.6 | -0.7+/-15.1 | -19.2+/-15.6 (1.2s) | -- | 9W/11L | p0.985 | 2.275 | -18.5 | +1.0 | +13.6 |
|
| 54 |
+
| cs_ramp | 1564.9 | +23.5+/-15.8 | +5.1+/-4.4 (0W/3L) | ~0 | 6W/10L | p0.231 | 2.275 | +38.8 | +21.7 | +13.6 |
|
| 55 |
+
| cs_wnup | 1552.7 | +11.4+/-20.4 | -7.0+/-13.4 (0.5s) | -- | 8W/10L | p0.609 | 2.275 | +42.3 | +3.8 | +13.6 |
|
| 56 |
+
| cs_lbdown | 1552.1 | +10.8+/-17.6 | -7.7+/-8.5 (0.9s) | -- | 8W/10L | p0.671 | 2.275 | +38.6 | +3.8 | +13.6 |
|
| 57 |
+
|
| 58 |
+
Arm rules: cs_hzNN = (1-g*w)*champ_order + g*w*rank(DiscHazHGB), w=NN/100,
|
| 59 |
+
g=1[deaths_seen<20]; cs_hz20t30 same at thr=30 (fires to s_14-18);
|
| 60 |
+
cs_ramp w_hz=.20*max(0,1-deaths_seen/20); cs_wnup gated weights .40/.40/.20;
|
| 61 |
+
cs_lbdown gated .40/.36/.24 (no-hazard controls). Weights round(.,12).
|
| 62 |
+
|
| 63 |
+
## Best arm per-block detail (clean pairing vs cs_champ)
|
| 64 |
+
|
| 65 |
+
cs_hz60: warmup -183.6/origin, panel -12.9/origin (ALL of it at the gated
|
| 66 |
+
bleed s_8-11; s_12+ exactly 0), nearend exactly 0. 8W/3L on the 11 changed
|
| 67 |
+
origins, wilcoxon p0.083.
|
| 68 |
+
cs_hz100: warmup -212.4/origin, panel -13.7 (gated bleed only), nearend 0.
|
| 69 |
+
8W/4L, p0.151. Per-origin vs ref: s_0 -422, s_1 -879, s_2 -107, s_3 +119,
|
| 70 |
+
s_4 +8, s_5 +156, s_6 -234, s_7 -64, s_8 -17, s_9 +210, s_10 -190, s_11 -468.
|
| 71 |
+
Gains are largest where deaths_seen is smallest (s_0/s_1: 1-3 deaths seen) --
|
| 72 |
+
the dose matches the mechanism, cell by cell.
|
| 73 |
+
|
| 74 |
+
## Mechanism evidence
|
| 75 |
+
|
| 76 |
+
1. Dose-response monotone in gated hazard weight: warmup (vs ref) +39 / -19 /
|
| 77 |
+
-27 / -149 / -178 per origin at w = .10/.20/.35/.60/1.0.
|
| 78 |
+
2. cs_hz100's warmup -177.9/origin REPLICATES the T2 hazard lane's
|
| 79 |
+
independently measured "warmup -178/cell for hazard-alone" (c14 h_alone,
|
| 80 |
+
old dials) -- the number was predicted before this campaign ran.
|
| 81 |
+
3. Hazard-specific: the no-hazard gated controls (cs_wnup +7.7, cs_lbdown
|
| 82 |
+
+4.1 warmup) capture none of the warmup gain -- it is not "less logbag at
|
| 83 |
+
cold start", it is the hazard model.
|
| 84 |
+
4. Concentration exactly as claimed: every point of gain sits inside the
|
| 85 |
+
gate-active origins; s_12+ and nearend are bitwise-identical plans.
|
| 86 |
+
5. Count-flat: order-only by construction, swaps 2.275 vs 2.267 ref, every arm.
|
| 87 |
+
6. Dead as predicted: cs_hz10/cs_ramp too weak (small hazard weight is
|
| 88 |
+
worse than none: at cold start the champ sources are the noise, so
|
| 89 |
+
diluting them slightly just perturbs ties); widening the gate to thr=30
|
| 90 |
+
(cs_hz20t30) adds nothing beyond its s_8-11 share.
|
| 91 |
+
|
| 92 |
+
## Tier verdict
|
| 93 |
+
|
| 94 |
+
New mechanism (per-origin gated order adaptation) => bar is 2sigma paired +
|
| 95 |
+
concentration + controls. cs_hz60: 2.0sigma on the clean pairing
|
| 96 |
+
(-39.7+/-20.1), 1.9sigma on the ship projection (-34.0+/-17.9, 8W/3L gated
|
| 97 |
+
origins, wilcoxon p0.083), concentration textbook, controls pass.
|
| 98 |
+
cs_hz100: bigger point estimate (-45.1 clean / -39.3 ship-proj) at
|
| 99 |
+
1.6-1.8sigma. Raw vs-ref sig is only ~1sigma for both because tie-flip noise
|
| 100 |
+
at non-gated origins contaminates that comparison; a deployed-class
|
| 101 |
+
implementation would not carry it.
|
| 102 |
+
|
| 103 |
+
VERDICT: cs_hz60 MEETS the bar at exactly 2.0sigma (clean pairing) with all
|
| 104 |
+
three qualitative gates passed; cs_hz100 is the better point estimate but
|
| 105 |
+
sits below 2sigma. Caveats for the main session: (i) the 2.0sigma is the max
|
| 106 |
+
of a 5-point weight sweep -- mitigated but not eliminated by the monotone
|
| 107 |
+
dose-response and the pre-registered T2 warmup number; (ii) the effect lives
|
| 108 |
+
on 11-12 origins (effective n small, sign-mixed 8W/3-4L); (iii) warmup block
|
| 109 |
+
public relevance depends on the held-out scenarios actually containing
|
| 110 |
+
early-history origins. Recommend ship-candidate = gated hazard at w=.60
|
| 111 |
+
(or the .60-1.0 shelf; the curve is flat-to-improving there), gate
|
| 112 |
+
deaths_seen<20 on the train eol ledger, implemented inside the deployed class
|
| 113 |
+
so non-gated origins stay byte-identical. Hazard model must ship with the
|
| 114 |
+
pickle (Docker/sklearn 1.7.2 retrain lock applies).
|
| 115 |
+
|
| 116 |
+
## Not retested (already dead) / files
|
| 117 |
+
|
| 118 |
+
Global hazard 4th source, global slot replacement, warmup-gated ALPHA
|
| 119 |
+
(count-coupled), per-building rank norm, boundary cascade, flat simplex --
|
| 120 |
+
untouched. Driver /tmp/c17/run.py; gate diagnostic /tmp/c17/gate_diag.py
|
| 121 |
+
(per-fold deaths_seen curves); tie audit in analyze output above.
|
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# T3 gm campaign: exact EOL label statistic, replicated — no delivery wins
|
| 2 |
+
|
| 3 |
+
LABEL-STATISTIC agent, 2026-08-23. Champion = head 60d65da (CV 1541.3,
|
| 4 |
+
order .50 lb / .30 wiener / .20 gapHGB, split_days=3, timing_weight=2.0).
|
| 5 |
+
Reference /tmp/cv_bgkfcand_ft2a10.csv (asserted 1541.3). All arms paired,
|
| 6 |
+
per-origin, 48 origins. Work dir /tmp/c16/ (validate*.py, build_gmfeat.py,
|
| 7 |
+
run.py, run2.py, gmfeat.parquet, simfeat.parquet, cv_bgkfcand_c16_*.csv).
|
| 8 |
+
|
| 9 |
+
## 1. Mechanism replication: 81/82 exact death dates, 0 false positives
|
| 10 |
+
|
| 11 |
+
The competition EOL label is deterministic from telemetry. Re-derived recipe
|
| 12 |
+
(from dataset/train/battery_metrics.parquet vs dataset/train/eol_times.csv,
|
| 13 |
+
82 non-null deaths — one more than the remembered 81):
|
| 14 |
+
|
| 15 |
+
1. Gate readings to temperature strictly inside (10, 30) — exclusive bounds
|
| 16 |
+
(same as features.py daily_voltage).
|
| 17 |
+
2. Daily median voltage per device; a day is VALID only if it has
|
| 18 |
+
**>= 5 gated readings** (minn=6 breaks 7 devices, minn=4 breaks 8).
|
| 19 |
+
3. Reindex valid daily medians onto the full CALENDAR grid (crossings land
|
| 20 |
+
on no-data days as old high days fall out of the window — evaluating
|
| 21 |
+
only on observation days loses ~9 devices).
|
| 22 |
+
4. Rolling 7-calendar-day median, **min_periods=3** (window needs >= 3
|
| 23 |
+
valid days; pandas interpolated median for even counts).
|
| 24 |
+
5. EOL = first day where the rolling value <= 2.40.
|
| 25 |
+
|
| 26 |
+
Score: **81/82 exact day-level matches, 0 date mismatches, 0 survivor false
|
| 27 |
+
positives**. The 82nd (d_cfdd8b69ddd4) dies on the day its statistic equals
|
| 28 |
+
exactly float32(2.4)=2.4000000954; comparing <= float32(2.4) fixes it but
|
| 29 |
+
makes d_9ba303a913ad cross 37 days early at the same float value — the
|
| 30 |
+
original generator's tie-at-threshold semantics are unresolvable from data
|
| 31 |
+
(irrelevant at feature level). Journey: naive gated-daily-median cal7 recipe
|
| 32 |
+
= 60/82; +calendar-grid eval = 61; +minn5/min_periods3 = 81. Pooled-reading
|
| 33 |
+
median rejected (38/82). The dip-recovery "mid-life swap" mismatches
|
| 34 |
+
(-72..-10 days) were all artifacts of low-count winter days polluting the
|
| 35 |
+
window; the n>=5 validity rule removes them exactly.
|
| 36 |
+
|
| 37 |
+
## 2. Feature cache (leak-free, repo day<=origin convention)
|
| 38 |
+
|
| 39 |
+
/tmp/c16/gmfeat.parquet — 77,267 rows keyed (device_id, origin_idx), all 168
|
| 40 |
+
origins (48 real + synthetic), 100% real-origin coverage: gm_last, gm_gap
|
| 41 |
+
(=gm_last-2.40), gm_age, gm_slope_7/21 (polyfit on the rolling-median series,
|
| 42 |
+
>=3 pts), gm_dtc (gap/max(-slope21,1e-4), clip [0,400]), gm_cov_30.
|
| 43 |
+
/tmp/c16/simfeat.parquet — sim_k: exact days-to-label-crossing if the last
|
| 44 |
+
valid daily-median level persists (window arithmetic simulation), else 400.
|
| 45 |
+
|
| 46 |
+
Signal autopsy (real origins, n=19,890, 454 dying rows):
|
| 47 |
+
- AUC dies42: gm_gap 0.939, gm_dtc 0.852 — vs v_corr_last 0.945, v_min_7
|
| 48 |
+
0.961. **The exact label statistic is a lagging smoother; our existing
|
| 49 |
+
proxies anticipate it.** Median dying-device within-origin rank: v_min_7
|
| 50 |
+
10 < gm_gap 14.
|
| 51 |
+
- gm_dtc (slope extrapolation of a piecewise-constant median) is noisy:
|
| 52 |
+
corr with days_to_eol only 0.15, median abs error 13-18d. NOT
|
| 53 |
+
near-deterministic.
|
| 54 |
+
- sim_k IS sharp where it fires: 99 flagged rows, 57% die in window (25x
|
| 55 |
+
base rate), median 3 days to death — but recall is only 12% of dying rows
|
| 56 |
+
and the champion already ranks them high.
|
| 57 |
+
|
| 58 |
+
## 3. Arms (all kind='logbag' variant='vm7' blend=(arm,1.0) p_star=1.0,
|
| 59 |
+
split_days=3, timing_weight=2.0; paired vs 1541.3; order-only, counts flat)
|
| 60 |
+
|
| 61 |
+
| arm | what | SCORE | delta±SE | sig | W/L | wilcoxon | swaps |
|
| 62 |
+
|-----------|--------------------------------------------------|--------|--------------|-----|-------|----------|-------|
|
| 63 |
+
| parity | order3 re-implementation, no gm | 1541.3 | +0.00, maxabs 0.00 | — | — | — | 2.27 |
|
| 64 |
+
| g_hgb | (a) GM5 added to gap-HGB features | 1567.6 | +26.3±18.6 | 1.4 | 9/8 | .705 | 2.27 |
|
| 65 |
+
| g_wnr | (b) WienerFHT with d = gm_gap (fallback v_corr) | 1608.9 | +67.6±35.1 | 1.9 | 19/20 | .176 | 2.26 |
|
| 66 |
+
| g_dtc405 | (c) 4th source rank(-gm_dtc) w=.05 | 1655.4 | +114.1±33.6 | 3.4 | 11/24 | .002 | 2.28 |
|
| 67 |
+
| g_dtc410 | (c) same, w=.10 | 1734.5 | +193.2±47.9 | 4.0 | 16/27 | .001 | 2.25 |
|
| 68 |
+
| g_both | (d) = a+b | 1639.4 | +98.1±35.0 | 2.8 | 18/24 | .034 | 2.26 |
|
| 69 |
+
| g_ctl | (e) control: GM5 in logbag order-primary | 1541.3 | +0.0±35.0 | 0.0 | 17/12 | .670 | 2.26 |
|
| 70 |
+
| g_boost05 | creative: +.05 surgical boost where sim_k<=6 | 1543.1 | +1.8±22.1 | 0.1 | 6/5 | .898 | 2.27 |
|
| 71 |
+
| g_boost10 | creative: same, .10 | 1549.3 | +8.0±22.8 | 0.3 | 5/7 | .733 | 2.27 |
|
| 72 |
+
|
| 73 |
+
Per-block deltas: no arm wins nearend s_42-47 either (best g_hgb -1.0,
|
| 74 |
+
noise); losses concentrate in the panel block. Parity control is exact, so
|
| 75 |
+
deltas are pure gm effects. Swaps flat at ~2.27 everywhere.
|
| 76 |
+
|
| 77 |
+
## 4. Verdict
|
| 78 |
+
|
| 79 |
+
**Mechanism validated (81/82), delivery comprehensively negative. No
|
| 80 |
+
candidate. Nothing meets any tier bar; do not ship.** The bold idea's
|
| 81 |
+
premise half-holds: the label IS deterministic and now fully reproducible —
|
| 82 |
+
but exactness at the barrier adds no ordering value because the statistic
|
| 83 |
+
lags the raw voltage features the champion already uses (v_min_7 leads it by
|
| 84 |
+
construction: min <= median-of-medians). The interesting control result:
|
| 85 |
+
GM5 inside the logbag order-primary is a perfect wash (+0.0), unlike GAP4
|
| 86 |
+
which cost +123 — gm features are benign but redundant.
|
| 87 |
+
|
| 88 |
+
Reusable for future work (not this deadline): the exact recipe in section 1
|
| 89 |
+
(e.g., deployment-side exact-crossing detection / label-consistent
|
| 90 |
+
truncation). It is computable in submit.py's split pipeline from raw
|
| 91 |
+
telemetry with ~15 lines (groupby day-median with count filter, reindex,
|
| 92 |
+
rolling(7, min_periods=3).median()).
|
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# t3: count-coupled candidates on the synthetic scaling harness
|
| 2 |
+
|
| 3 |
+
**Question.** Are the alpha-1.4 candidates' gains stable as fleets grow (more
|
| 4 |
+
batteries, more failures), and does the death-count gate on gated14 bound its
|
| 5 |
+
downside at scale? Robustness layer on top of the bgkf CV (champ 1541.3 /
|
| 6 |
+
flat14 1501.9 / gated14 ~1483).
|
| 7 |
+
|
| 8 |
+
**Setup.** Harness rebuilt from `agent_reports_bgkf/artifacts/{synth,scale_run}.py`
|
| 9 |
+
into `/tmp/c21/` (the /tmp/teamrepo clone was wiped; team `features.py` now
|
| 10 |
+
imported from `/Users/THIF/repos/BatterySwapAI2026-rul-mgt-zero`). Fleets at
|
| 11 |
+
k=1/2/4 = 461/922/1844 devices via time-shifted building clones, seed 7,
|
| 12 |
+
clone-0 = untouched original; EOLs derived from shifted telemetry by the
|
| 13 |
+
official mechanism — clone-0 check 0 mismatches at every k (75/75 checkable
|
| 14 |
+
deaths). Evaluation is deployment-faithful CURRENT head: one static
|
| 15 |
+
`submit.Ft2RUL` fit on the packaged train table (3-way order .50/.30/.20),
|
| 16 |
+
planner dials `split_days=3, timing_weight=2.0, ec_margin=0.8, ec_pmax=25,
|
| 17 |
+
floor_scale=2.5` with `recent_deaths=0` (static path: train_uniq=82 >=
|
| 18 |
+
FLOOR_GATE=20, floor inert exactly as in deployment). Arms differ ONLY in
|
| 19 |
+
`plan_tsroute.alpha`; features/predictions shared per scenario, so deltas are
|
| 20 |
+
exactly paired. All 48 scenarios per (k, arm). EARLY/LATE wired from each
|
| 21 |
+
scenario's settings.
|
| 22 |
+
|
| 23 |
+
- champ: alpha 1.0 everywhere (shipped head)
|
| 24 |
+
- flat14: alpha 1.4 everywhere
|
| 25 |
+
- gated14: alpha 1.4 while deaths_seen(T) < 20 else 1.0, with deaths_seen(T)
|
| 26 |
+
= # devices in the planned fleet with recorded eol <= T (plan-time
|
| 27 |
+
observable, FLOOR_GATE-family rule; no forward reads)
|
| 28 |
+
|
| 29 |
+
**Anchor.** k=1 champ = 1489.8, far below the old-champion anchor ~1762.5 and
|
| 30 |
+
in the current head's bgkf neighborhood (1541.3; the harness prices derived
|
| 31 |
+
EOLs + a template travel matrix, so a ~50pt offset is expected). champ
|
| 32 |
+
swaps/scen 10.8, swaps/death 1.16, recall 0.569 — deployment-plausible.
|
| 33 |
+
|
| 34 |
+
## Results (mean fleet total per scenario; delta = candidate - champ, paired)
|
| 35 |
+
|
| 36 |
+
### k=1 (461 devices)
|
| 37 |
+
| arm | score | delta ± SE | W/L | swaps/scen | late-d/scen | early-d/scen | warmup Δ/scen |
|
| 38 |
+
|---|---|---|---|---|---|---|---|
|
| 39 |
+
| champ | 1489.8 | — | — | 10.8 | 95.4 | 640.4 | — |
|
| 40 |
+
| flat14 | 1388.0 | -101.9 ± 52.9 | 20/23 | 14.3 | 69.8 | 939.9 | -537.9 |
|
| 41 |
+
| gated14 | 1400.2 | -89.6 ± 38.5 | 7/1 (8 nonzero) | 11.5 | 85.3 | 672.3 | -537.9 |
|
| 42 |
+
|
| 43 |
+
Block scores (champ/flat14/gated14): warmup s0-7 2314/1776/1776, panel s8-41
|
| 44 |
+
1463/1442/1463, nearend s42-47 541/566/541. flat14 Wilcoxon p=0.27 (nonzero
|
| 45 |
+
n=43); gated14 p=0.016 (n=8). flat14 panel Δ -21.8/scen, nearend Δ +25.5/scen.
|
| 46 |
+
|
| 47 |
+
### k=2 (922 devices)
|
| 48 |
+
| arm | score | delta ± SE | W/L | swaps/scen | late-d/scen | early-d/scen | warmup Δ/scen |
|
| 49 |
+
|---|---|---|---|---|---|---|---|
|
| 50 |
+
| champ | 3142.1 | — | — | 21.2 | 205.4 | 1306.2 | — |
|
| 51 |
+
| flat14 | 2934.6 | -207.5 ± 82.8 | 27/21 | 27.6 | 154.1 | 1937.4 | -741.3 |
|
| 52 |
+
| gated14 | 3054.9 | -87.2 ± 42.9 | 4/0 (4 nonzero) | 21.6 | 195.8 | 1324.9 | -523.0 |
|
| 53 |
+
|
| 54 |
+
Blocks: warmup 3473/2732/2950, panel 3426/3284/3426, nearend 1092/1221/1092.
|
| 55 |
+
flat14 Wilcoxon p=0.092; panel Δ -141.3/scen, nearend Δ +129.4/scen.
|
| 56 |
+
|
| 57 |
+
### k=4 (1844 devices)
|
| 58 |
+
| arm | score | delta ± SE | W/L | swaps/scen | late-d/scen | early-d/scen | warmup Δ/scen |
|
| 59 |
+
|---|---|---|---|---|---|---|---|
|
| 60 |
+
| champ | 7053.4 | — | — | 39.5 | 480.4 | 2602.2 | — |
|
| 61 |
+
| flat14 | 6458.8 | -594.6 ± 167.9 | 26/22 | 52.2 | 356.1 | 3910.1 | -2080.1 |
|
| 62 |
+
| gated14 | 7053.4 | 0.0 (identical) | 0/0 | 39.5 | 480.4 | 2602.2 | 0.0 |
|
| 63 |
+
|
| 64 |
+
Blocks: warmup 8433/6353/8433, panel 7609/7169/7609, nearend 2062/2573/2062.
|
| 65 |
+
flat14 Wilcoxon p=0.0077; panel Δ -440.0/scen, nearend Δ +510.6/scen.
|
| 66 |
+
|
| 67 |
+
## Gate-closure trajectory (the KEY question)
|
| 68 |
+
|
| 69 |
+
The gate closes strictly earlier as the fleet grows — deaths accumulate ~k
|
| 70 |
+
times faster while the threshold stays at 20:
|
| 71 |
+
|
| 72 |
+
| k | deaths_seen at s_0 | gate-active scenarios | gate closes | gated-vs-champ Δ (all-48 mean) |
|
| 73 |
+
|---|---|---|---|---|
|
| 74 |
+
| 1 | 3 | 8 (s_0-s_7) | s_8 (seen 20) | -89.6 ± 38.5 |
|
| 75 |
+
| 2 | 10 | 4 (s_0-s_3) | s_4 (seen 25) | -87.2 ± 42.9 |
|
| 76 |
+
| 4 | 21 | 0 (never opens) | pre-s_0 | 0.0 (byte-identical plans) |
|
| 77 |
+
|
| 78 |
+
Per-scenario gated deltas on gate-active origins — k=1: s_0 +145.2, s_1
|
| 79 |
+
-820.5, s_2 -215.5, s_3 -683.3, s_4 -754.4, s_5 -439.1, s_6 -1306.2, s_7
|
| 80 |
+
-229.0 (7W/1L). k=2: s_0 -1357.9, s_1 -864.5, s_2 -1000.3, s_3 -961.1
|
| 81 |
+
(4W/0L). Outside the gate window gated14 reproduces champ exactly at every
|
| 82 |
+
scale (identical alpha on identical predictions => identical plans).
|
| 83 |
+
|
| 84 |
+
## Verdict
|
| 85 |
+
|
| 86 |
+
- **gated14: STABLE, with downside bounded by construction — converges to
|
| 87 |
+
champ at large fleets.** The gate is doing exactly what it was designed to
|
| 88 |
+
do: it fires only in the genuinely count-starved era, that era shrinks as
|
| 89 |
+
the fleet grows (8 -> 4 -> 0 scenarios at 1x/2x/4x), and at 4x gated14 IS
|
| 90 |
+
the shipped champ (zero exposure). Where it does fire it captures most of
|
| 91 |
+
flat14's warmup gain (7W/1L and 4W/0L; single worst scenario +145) while
|
| 92 |
+
taking none of flat14's panel/nearend exposure.
|
| 93 |
+
- **flat14: mean-IMPROVING with scale, but tail-driven and regime-skewed.**
|
| 94 |
+
Per-device delta grows (-0.221 -> -0.225 -> -0.322 per device; Wilcoxon p
|
| 95 |
+
0.27 -> 0.092 -> 0.0077), and at scale the gain spreads from warmup into
|
| 96 |
+
the panel block (-21.8 -> -141.3 -> -440.0/scen). But the NEAREND block
|
| 97 |
+
degrades just as systematically (+25.5 -> +129.4 -> +510.6/scen), W/L
|
| 98 |
+
stays ~even at every scale (20/23, 27/21, 26/22), and it buys the gain
|
| 99 |
+
with +32% swaps and +50% early-days at 4x. Its mean edge is real in this
|
| 100 |
+
harness but concentrated in a minority of high-prevalence origins — a
|
| 101 |
+
higher-variance bet, not a uniform dominance.
|
| 102 |
+
- Interpretation for the ship decision: the scaling axis does NOT expose a
|
| 103 |
+
hidden failure mode in gated14 — its worst case at any scale is "equals
|
| 104 |
+
champ". flat14's late-era over-inclusion (nearend +510/scen at 4x) is the
|
| 105 |
+
scale-amplified liability the bgkf mean hides.
|
| 106 |
+
|
| 107 |
+
**Caveats.** Derived-EOL harness (ghost low-voltage clones shared equally by
|
| 108 |
+
all arms), template travel matrix, single seed (7) for clone shifts; the
|
| 109 |
+
harness overstates absolute deltas relative to bgkf even at k=1 (-102 here vs
|
| 110 |
+
-39.4 on bgkf for flat14), so read trajectories, not magnitudes. Per the
|
| 111 |
+
tiered adoption bar this is a report-only robustness layer, not an adoption
|
| 112 |
+
adjudication.
|
| 113 |
+
|
| 114 |
+
**Artifacts.** /tmp/c21/{synth.py,scale3.py,summarize.py},
|
| 115 |
+
/tmp/c21/scale_k{1,2,4}_all.csv, /tmp/c21/fleet_k{1,2,4}.pkl,
|
| 116 |
+
logs /tmp/c21/{synth,run}_k*.log. Repo artifacts untouched.
|
|
@@ -1455,7 +1455,12 @@ def plan_tsroute(pred, included, locations, start_time, day_q,
|
|
| 1455 |
# (the harness itself drops eol <= origin devices) and estimate the
|
| 1456 |
# coming window's prevalence without trusting the probability scale.
|
| 1457 |
psum = max(psum, plan_tsroute.floor_scale * plan_tsroute.recent_deaths)
|
| 1458 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1459 |
order_col = 'rank_score' if 'rank_score' in pred.columns else 'p42'
|
| 1460 |
chosen = pred[order_col].nlargest(n).index
|
| 1461 |
if plan_tsroute.p_be > 0:
|
|
@@ -1531,6 +1536,7 @@ def plan_tsroute(pred, included, locations, start_time, day_q,
|
|
| 1531 |
plan_tsroute.alpha = 0.8
|
| 1532 |
plan_tsroute.split_days = 0
|
| 1533 |
plan_tsroute.timing_weight = 1.0 # scales timing curves in optimize_days' objective
|
|
|
|
| 1534 |
plan_tsroute.p_be = 0.0
|
| 1535 |
plan_tsroute.floor_day = 0
|
| 1536 |
plan_tsroute.p_conf = 0.5
|
|
|
|
| 1455 |
# (the harness itself drops eol <= origin devices) and estimate the
|
| 1456 |
# coming window's prevalence without trusting the probability scale.
|
| 1457 |
psum = max(psum, plan_tsroute.floor_scale * plan_tsroute.recent_deaths)
|
| 1458 |
+
# consume-once appetite boost: armed by the model's predict (T3 warmup
|
| 1459 |
+
# gate) for the plan that immediately follows, then self-resets so a
|
| 1460 |
+
# fallback path can never inherit a stale boost
|
| 1461 |
+
boost = plan_tsroute.alpha_boost
|
| 1462 |
+
plan_tsroute.alpha_boost = 1.0
|
| 1463 |
+
n = int(round(plan_tsroute.alpha * boost * psum))
|
| 1464 |
order_col = 'rank_score' if 'rank_score' in pred.columns else 'p42'
|
| 1465 |
chosen = pred[order_col].nlargest(n).index
|
| 1466 |
if plan_tsroute.p_be > 0:
|
|
|
|
| 1536 |
plan_tsroute.alpha = 0.8
|
| 1537 |
plan_tsroute.split_days = 0
|
| 1538 |
plan_tsroute.timing_weight = 1.0 # scales timing curves in optimize_days' objective
|
| 1539 |
+
plan_tsroute.alpha_boost = 1.0 # consume-once; see the count line above
|
| 1540 |
plan_tsroute.p_be = 0.0
|
| 1541 |
plan_tsroute.floor_day = 0
|
| 1542 |
plan_tsroute.p_conf = 0.5
|
|
@@ -253,6 +253,47 @@ def _gap_features(frame):
|
|
| 253 |
return out
|
| 254 |
|
| 255 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 256 |
class Ft2RUL:
|
| 257 |
"""Faithful deployment of cv.run_bgkf(kind='logbag', variant='vm7',
|
| 258 |
blend=('wiener', 0.20)): primary = SklearnRUL('logbag') fit with
|
|
@@ -292,6 +333,28 @@ class Ft2RUL:
|
|
| 292 |
max_depth=2, learning_rate=0.06, max_iter=300,
|
| 293 |
l2_regularization=1.0, random_state=0
|
| 294 |
).fit(X[self._gap_cols], train['dies42'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 295 |
return self
|
| 296 |
|
| 297 |
def predict(self, rows):
|
|
@@ -304,9 +367,28 @@ class Ft2RUL:
|
|
| 304 |
rows = rows.copy()
|
| 305 |
rows['vmin7_rank'] = (rows['v_min_7'].rank(pct=True)
|
| 306 |
if 'v_min_7' in rows.columns else np.nan)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 307 |
g = _gap_features(rows) if self.gap is not None else None
|
| 308 |
with self._swap():
|
| 309 |
pred = self.primary.predict(rows)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 310 |
rk = pred['p42'].rank(pct=True)
|
| 311 |
w3 = FT2_GAPW if g is not None else 0.0
|
| 312 |
# round the derived primary weight: 1-0.30-0.20 is 0.4999...94 in
|
|
@@ -390,6 +472,24 @@ TIMING_WEIGHT = float(os.environ.get('BATTERYSWAP_TIMING_WEIGHT', '2.0'))
|
|
| 390 |
# the champion. Warm-up CV: 4256 -> 2380 at k=2.5, winning 8/8 origins.
|
| 391 |
FLOOR_SCALE = float(os.environ.get('BATTERYSWAP_FLOOR', '2.5'))
|
| 392 |
FLOOR_GATE = int(os.environ.get('BATTERYSWAP_FLOOR_GATE', '20'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 393 |
# Phase 28: cliff-gated appetite for the static held-out path. The static
|
| 394 |
# foreign fit undercounts exactly where a knee is already visible in the raw
|
| 395 |
# hourly readings (>=35% of the last 7 days' readings below 2.45 V, >=20
|
|
@@ -703,6 +803,9 @@ def adaptive_split_plans(split, split_path, log=print, deadline=None):
|
|
| 703 |
log(f' WARNING {scenario["name"]}: ft2 wiener params failed '
|
| 704 |
f'({e}); wiener source dropped this scenario')
|
| 705 |
feats = feats.reindex(locs['battery'])
|
|
|
|
|
|
|
|
|
|
| 706 |
|
| 707 |
pred = model.predict(feats)
|
| 708 |
if static_model is not None and cliff_counts is not None:
|
|
|
|
| 253 |
return out
|
| 254 |
|
| 255 |
|
| 256 |
+
_HZ_BUCKETS = [(0, 7), (7, 14), (14, 21), (21, 28), (28, 42), (42, 63), (63, 84)]
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
class _DiscHaz:
|
| 260 |
+
"""Discrete-time hazard HGB on person-period (pair, bucket) rows from
|
| 261 |
+
the censored pool of whatever pair table fit() receives (durations
|
| 262 |
+
censored at its last origin); order source P(die<=42d) = 1 - prod(1-h_k).
|
| 263 |
+
Censoring is what the plain classifiers can't see - this model's edge is
|
| 264 |
+
cold-start origins, which is the only place the warmup gate lets it act."""
|
| 265 |
+
|
| 266 |
+
def fit(self, train):
|
| 267 |
+
pool = models_cv.censored_pairs(train, train['origin'].max())
|
| 268 |
+
self.cols = list(models_cv.FEATURES)
|
| 269 |
+
dur = pool['duration'].values
|
| 270 |
+
ev = pool['event'].values.astype(bool)
|
| 271 |
+
Xb = pool[self.cols].values.astype(float)
|
| 272 |
+
Xs, ys, mids = [], [], []
|
| 273 |
+
for lo, hi in _HZ_BUCKETS:
|
| 274 |
+
died = ev & (dur > lo) & (dur <= hi)
|
| 275 |
+
use = (dur >= hi) | died
|
| 276 |
+
Xs.append(Xb[use])
|
| 277 |
+
ys.append(died[use])
|
| 278 |
+
mids.append(np.full(int(use.sum()), (lo + hi) / 2.0))
|
| 279 |
+
from sklearn.ensemble import HistGradientBoostingClassifier
|
| 280 |
+
self.clf = HistGradientBoostingClassifier(
|
| 281 |
+
max_depth=3, learning_rate=0.06, max_iter=250,
|
| 282 |
+
l2_regularization=1.0, random_state=0, early_stopping=False,
|
| 283 |
+
).fit(np.column_stack([np.vstack(Xs), np.concatenate(mids)]),
|
| 284 |
+
np.concatenate(ys))
|
| 285 |
+
return self
|
| 286 |
+
|
| 287 |
+
def predict(self, rows):
|
| 288 |
+
Xb = rows[self.cols].values.astype(float)
|
| 289 |
+
n = len(rows)
|
| 290 |
+
surv = np.ones(n)
|
| 291 |
+
for lo, hi in _HZ_BUCKETS[:5]: # buckets covering 0-42d
|
| 292 |
+
X = np.column_stack([Xb, np.full(n, (lo + hi) / 2.0)])
|
| 293 |
+
surv = surv * (1.0 - self.clf.predict_proba(X)[:, 1])
|
| 294 |
+
return pd.DataFrame({'p42': 1.0 - surv}, index=rows.index)
|
| 295 |
+
|
| 296 |
+
|
| 297 |
class Ft2RUL:
|
| 298 |
"""Faithful deployment of cv.run_bgkf(kind='logbag', variant='vm7',
|
| 299 |
blend=('wiener', 0.20)): primary = SklearnRUL('logbag') fit with
|
|
|
|
| 333 |
max_depth=2, learning_rate=0.06, max_iter=300,
|
| 334 |
l2_regularization=1.0, random_state=0
|
| 335 |
).fit(X[self._gap_cols], train['dies42'])
|
| 336 |
+
self.hazard, self.dev_eol = None, None
|
| 337 |
+
if ('eol' not in train.columns and 'days_to_eol' in train.columns
|
| 338 |
+
and 'origin' in train.columns):
|
| 339 |
+
# the cached parquet ships days_to_eol but not eol (cv.load_table
|
| 340 |
+
# adds it at load time); the derivation is exact (verified 0 diff
|
| 341 |
+
# on all 8206 dying rows)
|
| 342 |
+
train = train.copy()
|
| 343 |
+
train['eol'] = train['origin'] + pd.to_timedelta(
|
| 344 |
+
train['days_to_eol'], unit='D')
|
| 345 |
+
if WARMUP_RATE > 0 and 'eol' in train.columns and 'device_id' in train.columns:
|
| 346 |
+
try:
|
| 347 |
+
self._gate_thr = WARMUP_RATE * train['device_id'].nunique()
|
| 348 |
+
self.dev_eol = (train[['device_id', 'eol']].dropna()
|
| 349 |
+
.drop_duplicates('device_id')
|
| 350 |
+
.set_index('device_id')['eol'])
|
| 351 |
+
with self._swap():
|
| 352 |
+
self.hazard = _DiscHaz().fit(train)
|
| 353 |
+
except Exception:
|
| 354 |
+
# warmup gate is an overlay: a broken hazard fit degrades to
|
| 355 |
+
# the plain (previous-head) behavior, never to a crash
|
| 356 |
+
traceback.print_exc()
|
| 357 |
+
self.hazard = None
|
| 358 |
return self
|
| 359 |
|
| 360 |
def predict(self, rows):
|
|
|
|
| 367 |
rows = rows.copy()
|
| 368 |
rows['vmin7_rank'] = (rows['v_min_7'].rank(pct=True)
|
| 369 |
if 'v_min_7' in rows.columns else np.nan)
|
| 370 |
+
gate = False
|
| 371 |
+
if (self.hazard is not None and self.dev_eol is not None
|
| 372 |
+
and 'origin' in rows.columns):
|
| 373 |
+
# T3 warmup gate: TRAIN-ledger deaths recorded <= this plan origin
|
| 374 |
+
seen = int((self.dev_eol <= rows['origin'].max()).sum())
|
| 375 |
+
gate = seen < self._gate_thr
|
| 376 |
g = _gap_features(rows) if self.gap is not None else None
|
| 377 |
with self._swap():
|
| 378 |
pred = self.primary.predict(rows)
|
| 379 |
+
if gate:
|
| 380 |
+
try:
|
| 381 |
+
rk = self.hazard.predict(rows)['p42'].rank(pct=True)
|
| 382 |
+
models_cv.plan_tsroute.alpha_boost = WARMUP_ALPHA
|
| 383 |
+
vals = np.sort(pred['p42'].values)[::-1]
|
| 384 |
+
out = pred.copy()
|
| 385 |
+
out['p42'] = pd.Series(
|
| 386 |
+
vals, index=rk.sort_values(ascending=False).index
|
| 387 |
+
).reindex(out.index)
|
| 388 |
+
return out
|
| 389 |
+
except Exception:
|
| 390 |
+
traceback.print_exc()
|
| 391 |
+
models_cv.plan_tsroute.alpha_boost = 1.0
|
| 392 |
rk = pred['p42'].rank(pct=True)
|
| 393 |
w3 = FT2_GAPW if g is not None else 0.0
|
| 394 |
# round the derived primary weight: 1-0.30-0.20 is 0.4999...94 in
|
|
|
|
| 472 |
# the champion. Warm-up CV: 4256 -> 2380 at k=2.5, winning 8/8 origins.
|
| 473 |
FLOOR_SCALE = float(os.environ.get('BATTERYSWAP_FLOOR', '2.5'))
|
| 474 |
FLOOR_GATE = int(os.environ.get('BATTERYSWAP_FLOOR_GATE', '20'))
|
| 475 |
+
# T3 warmup gate (2026-08-23 night, user CV-first directive): while the
|
| 476 |
+
# TRAINING fleet's ledger shows < WARMUP_GATE recorded deaths at the plan
|
| 477 |
+
# origin (plan-time observable, FLOOR_GATE family; the full train ledger
|
| 478 |
+
# crosses 35 at s_15), the ORDER switches to a censored-pool discrete-time
|
| 479 |
+
# hazard HGB and the appetite is boosted x WARMUP_ALPHA via a consume-once
|
| 480 |
+
# plan_tsroute.alpha_boost. bgkf: gated alpha 1.4 alone -125.4 (4.0sigma,
|
| 481 |
+
# width bowl clean 6..48); + gated hazard order -143.3 (3.8sigma, 16W/0L,
|
| 482 |
+
# Wilcoxon <.001) = CV 1398.0 vs the 1541.3 head. Scale harness: the gate
|
| 483 |
+
# narrows as fleets grow (8/4/0 gate-active scenarios at 1x/2x/4x) so the
|
| 484 |
+
# downside is bounded - on fleets past the gate this is byte-identical to
|
| 485 |
+
# the previous head. WARMUP_GATE=0 disables both channels exactly.
|
| 486 |
+
# The gate threshold is a RATE (deaths per training-fleet device), anchored
|
| 487 |
+
# at 35 deaths / 461 devices: a fixed count fires wider on the CV's 4/5-size
|
| 488 |
+
# fold ledgers than on the full deployment ledger (measured: fixed 35 ->
|
| 489 |
+
# 22 changed origins, 15W/7L, -111.6; rate form matches the width-16 bowl
|
| 490 |
+
# optimum on both). WARMUP_RATE=0 disables the gate entirely.
|
| 491 |
+
WARMUP_RATE = float(os.environ.get('BATTERYSWAP_WARMUP_RATE', '0.0759'))
|
| 492 |
+
WARMUP_ALPHA = float(os.environ.get('BATTERYSWAP_WARMUP_ALPHA', '1.4'))
|
| 493 |
# Phase 28: cliff-gated appetite for the static held-out path. The static
|
| 494 |
# foreign fit undercounts exactly where a knee is already visible in the raw
|
| 495 |
# hourly readings (>=35% of the last 7 days' readings below 2.45 V, >=20
|
|
|
|
| 803 |
log(f' WARNING {scenario["name"]}: ft2 wiener params failed '
|
| 804 |
f'({e}); wiener source dropped this scenario')
|
| 805 |
feats = feats.reindex(locs['battery'])
|
| 806 |
+
# plan-origin timestamp for the T3 warmup gate (the cv table carries
|
| 807 |
+
# this column natively; the split pipeline adds it here)
|
| 808 |
+
feats['origin'] = T
|
| 809 |
|
| 810 |
pred = model.predict(feats)
|
| 811 |
if static_model is not None and cliff_counts is not None:
|