Example Model for BatterySwapAI 2026

This repository shows how to create a solution for the BatterySwapAI 2026 challenge.

It contains working example code that can be submitted as-is, along with tools to check the solution before submitting.

Setup

Prerequisites

You need to have the following installed

  • git
  • Python 3.10+
  • Docker

Setting up virtual environment

On Linux / Mac OS / Windows Subsystem for Linux (WSL)

python -m venv venv
source venv/bin/activate

Install dependencies

pip install -r requirements.txt -r requirements.dev.txt

Developing

Develop in virtual environment

Run the training

python batteryswap_example/train.py

Making submissions

Submitting trained models

Trained models or other output used by submission processing (script.py), must be committed in the git repository. An example is batteryswap_example/planners/best.pickle.

Test submissions in Docker - recommended

Using Docker allows to have exactly the same software versions as the submissions system.

This helps to ensure there are no errors when running in the submission environment.

NOTE: this requires around 20 GB+ of disk space.

Build Docker image

docker build -t batteryswapai-2026-example .

Make submissions and run evaluation (mount your local data folder to /tmp/data)

docker run --name batteryswapai -v ./BatterySwapAI-2026-Public:/tmp/data batteryswapai-2026-example bash -c "/app/env/bin/python3 script.py && /app/env/bin/python3 -m batteryswap_public.metric"

Copy submission.csv out of container

docker cp batteryswapai:/app/submission.csv ./submission.csv

Create new submission

NOTE: remember to commit and push your changes to the HuggingFace model repository.

Use New submission in the competition application to submit your current code for evaluation.

Reproducing the submitted model

The committed batteryswap_example/planners/best.pickle is produced by:

BATTERYSWAP_DATASET_PATH=BatterySwapAI-2026-Public \
    venv/bin/python3 batteryswap_example/train.py

Exact configuration used (all hardcoded defaults in batteryswap_example/train.py, so no environment variables are required at submission time):

  • RUL model: final_model = 'survival' (lifelines Weibull AFT). Chosen by lowest evaluate_plan total_cost summed over all scenarios, not by MAE β€” MAE only scores observed (non-censored) devices and is blind to the survival model's censoring-aware conservatism on long-lived devices.

  • Right-censoring bound: censor_horizon='true' (per-device). 82% of devices (379/461) have a blank EOL, i.e. are right-censored, and an AFT likelihood contributes S(C) for each of them β€” so C must be the time we actually know the device survived to. The original code used a constant 120 days for every censored device, although the data shows they were still alive at the dataset cutoff (~334 days after s_0's start). Measured (sweep_censoring.py, day-caps trimmed to 32,inf):

    censor_horizon total early_swap late_swap
    'true' 106,733 41,437 43,220
    365.0 112,393 33,596 59,660
    240.0 112,551 32,046 60,930
    120.0 (old) 112,641 21,724 70,780

    The 240/365 arms are controls: 'true' beats equal-magnitude constants by ~5,700, so the win comes from the per-device variation, not merely from a larger bound. Mechanism: a correct bound spreads predicted RUL out, so the planner can prioritise genuinely-dying devices instead of treating the whole fleet as due at once β€” late_swap falls 70,780 β†’ 43,220. early_swap rises, but that is a good trade at the evaluator's 20:1 late:early ratio.

  • Scheduling quantile: OrderedPlanner.SCHEDULE_QUANTILE = 'p10' (PLANNER_SCHEDULE_QUANTILE default). Re-validated AFTER the censoring fix (sweep_quantiles.py, trained with censor_horizon='true', day-caps 32,inf):

    q total early_swap late_swap
    p10 106,733 41,437 43,220
    p12 112,046 32,065 56,970
    p08 115,759 61,014 32,540
    p15 114,331 22,290 73,400
    p05 139,788 102,092 13,980
    p50 151,106 463 137,790

    p10 is a clear, stable minimum (next-best p12 is +5,312). Note the empirical optimum sits ABOVE the newsvendor anchor q* = early/(early+late) = 0.5/10.5 β‰ˆ p05: perfect-calibration theory says p05, but the day-cap/batching congestion penalises over-early swaps more than the raw 20:1 ratio implies, so p05 actually scores 139,788 β€” trusting theory over measurement would cost ~33k. NOTE: this default is what the grader uses, because the scheduling quantile is a class attribute read at import and the grader sets no env var. final_quantiles must include the chosen quantile or the planner silently falls back to p50.

  • Planner day-work caps: PLANNER_DAY_CAPS default 24,32,48,inf (self-tuned per scenario by evaluate_plan against the predicted EOL).

  • Random seed: seed=0 (default in train_rul_model).

  • Training data: split='train', limit_scenarios=None (ALL 48 scenarios feed RUL training; every scenario is the same physical fleet at a +7-day-shifted start, so this exposes each device at many life-stages rather than duplicating rows). Measured-best: survival + p10 + all-scenarios sums to 112,641 total_cost across all scenarios, ~16% better than the old limit_scenarios=1 (134,576). The gain is monotonic across 1,2,4,8,16,all (see sweep_training_data.py), consistent with the RUL model being data-starved at limit_scenarios=1 (only ~82 observed failures). NOTE: this local harness evaluates on the same fleet it trains on, so treat the absolute number as optimistic β€” the hidden competition devices are the real judge β€” but the monotonic trend makes the direction trustworthy.

Files required for a complete, reproducible submission

Runtime (copied into the competition Docker image via Dockerfile):

  • script.py β€” submission entrypoint (loads best.pickle, calls make_submissions).
  • Dockerfile β€” build recipe (COPY batteryswap_example/, COPY script.py).
  • requirements.txt β€” fixed by the competition (already includes lifelines, scikit-learn, etc.).
  • batteryswap_example/train.py β€” defines OrderedPlanner + RUL models (must be importable for unpickling best.pickle).
  • batteryswap_example/planners/best.pickle β€” the trained planner artifact.

Repository / reproducibility:

  • README.md β€” this file (documents the exact reproduce command).
  • LICENSE β€” MIT (edit the copyright holder).

Not part of the grader run and kept out of the submission repo via .gitignore (the Dockerfile only copies batteryswap_example/ and script.py; these are local-only tuning/investigation tools that informed the hardcoded defaults in train.py, plus generated outputs and the organizer-provided dataset): compare_runs.py, inspect_data.py, investigate_*.py, sweep_*.py (sweep_quantiles.py, sweep_training_data.py, sweep_censoring.py), results/, submission.csv (generated), BatterySwapAI-2026-Public/ (dataset). requirements.dev.txt is kept (referenced by the setup instructions above).

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support