My submission for the NORA BatterySwapAI 2026 challenge. It holds the fitted
models and the inference code that writes the submission file. No training code
is included.
What changed
Solution 46 jointly calibrates candidate ranking, hazard strength, late-risk
pricing, and weekly-aware schedule construction. It retains the fourteen-day
candidate window and exact cost-based local search, while spreading feasible
work more effectively across weeks. The serialized submission path scores
1381.48 under held-out-building validation on all 48 scenarios.
Method
Build features for every device, including the forward temperature block.
Score each device with the thermal survival model.
Mix 23.5 percent of the hazard rank with 76.5 percent of a learned ranker.
Keep the top 17 as swap candidates.
Plan dates and routes under a 16-swap cap, pricing travel, overtime, the
daily and weekly hour caps, and the emergency visit the evaluator forces for
any battery that was due and left unplanned.
Relieve any week that has gone over its hour cap, choosing dates from a
window at least fourteen days wide so another week is always reachable.
Validation
Validation holds out whole buildings, so a battery and its neighbours never
appear in both training and validation. Devices in one building share an install
date and a thermal environment, which makes any weaker split optimistic. Every
battery below is scored by a model that never saw its own building, through the
same code path the submission runs, with features rebuilt from the raw readings
rather than read from a cache.
Version
Total time
Late swap
Early swap
Weekly cap
Daily cap
Solution 30
1430.14
704.17
533.13
33.33
31.25
plus the wider window
1425.69
702.92
532.53
31.25
31.25
Solution 41
1425.09
702.92
532.24
29.17
33.33
Solution 46
1381.48
690.21
512.39
18.75
33.33
Split into the four chronological quarters of the scenario set, against
solution 30: 1303.8 to 1303.6, 2322.4 to 2316.2, 1189.5 unchanged, and 904.9 to
891.1. No quarter is worse, which is the reason for preferring this
configuration over the larger but lopsided one the search preferred.
These are local results on the 48 public training scenarios, measured with the
competition's own simulator. They are not competition scores.
A note on measurement. Configuration choices here were made only on numbers
produced by the planner's own feature builder. An earlier version of this work
was tuned against a cached feature table and picked a setting that turned out to
be worse once measured properly, so cached results are not used for decisions.
Files
text
1script.py submission entry point
2batteryswap_example/features.py feature extraction
3batteryswap_example/thermal.py forward temperature features
4batteryswap_example/survival.py end-of-life model
5batteryswap_example/tsbp.py trajectory matching
6batteryswap_example/costmodel.py schedule cost calculation
7batteryswap_example/planner.py scheduling search
8batteryswap_example/ensemble.py shortlist and serving wrapper
9batteryswap_example/planners/best.pickle fitted model and ranker
Running
The competition image runs script.py, which reads the dataset, plans every
scenario and writes submission.csv. Locally: