File size: 3,388 Bytes
e80503b
 
9340d8d
e80503b
 
 
 
 
 
9340d8d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e80503b
 
 
 
 
 
 
 
 
 
 
 
9340d8d
e112496
 
 
 
 
9340d8d
 
 
 
 
 
 
e80503b
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
"""Submission entry point (run by the competition platform): builds submission.csv for all splits."""
print('import start', flush=True)
import os, sys, time, traceback
from pathlib import Path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from batteryswap_public.utils import make_submissions
from bsw.solution import SolutionPlanner


def defer_all_split(split, dataset_path):
    """CATASTROPHE FALLBACK: a guaranteed-valid plan for one split that swaps nothing.

    Every battery still alive at each scenario start (same dead-device removal formula as
    batteryswap_public.utils.iterate_scenarios) is scheduled on the day AFTER the planning
    window end (start + planning_window_days + 1), which check_plan_valid accepts and
    evaluate_plan never executes (it keeps only rows with day <= start + planning_window_days).
    """
    import pandas as pd
    from batteryswap_public.utils import load_dataset
    from batteryswap_public.evaluate import check_plan_valid
    locations, _timeseries, eol_times, scenarios = load_dataset(Path(dataset_path) / split)
    plans = []
    for s in scenarios:
        T = pd.Timestamp(s['start_time'])
        window_days = float(getattr(s['settings'], 'planning_window_days', 42))
        is_eol = ((~pd.isna(eol_times)) & (eol_times <= T))
        locs = locations[~locations['battery'].isin(eol_times[is_eol].index)]
        far = T.normalize() + pd.Timedelta(days=int(window_days) + 1)
        plan = pd.DataFrame({'day': [far] * len(locs), 'battery': list(locs['battery'])})
        check_plan_valid(plan, locs, start_time=T)
        plan['split'] = split
        plan['scenario'] = s['name']
        plans.append(plan)
    return pd.concat(plans, ignore_index=True)


def main():
    t0 = time.time()
    here = Path(os.path.dirname(os.path.abspath(__file__)))
    artifact_dir = here / 'artifacts'
    dataset_path = Path(os.environ.get('BATTERYSWAP_DATASET_PATH', '/tmp/data'))
    splits = os.environ.get('BATTERYSWAP_SPLITS', 'public,private').split(',')
    out_path = Path(os.environ.get('BATTERYSWAP_SUBMISSION_PATH', 'submission.csv'))
    # One planner instance PER SPLIT (the incremental daily cache assumes forward cuts over one
    # device set); shared t0 keeps a single soft time budget across all splits.
    import pandas as pd
    frames = []
    for split in splits:
        try:
            # Phase-peer candidate: hard-enable static C5.  This tree composes
            # it with fixed causal temporal and same-building contradiction
            # certificates; a certified conflict abstains to exact C5 base.
            planner = SolutionPlanner(
                str(artifact_dir), verbose=1, reentry_devicecal_c5=True)
            frames.append(make_submissions((lambda p: (lambda: p))(planner), dataset_path=dataset_path,
                                           splits=[split], out_path=None))
        except Exception as e:
            # CATASTROPHE WRAPPER: never return an invalid/absent submission for a split.
            print(f'split {split}: FATAL {e!r}; emitting defer-all fallback plan', flush=True)
            traceback.print_exc()
            frames.append(defer_all_split(split, dataset_path))
    pd.concat(frames).to_csv(out_path, index=False)
    assert os.path.exists(out_path)
    print(f'done in {time.time()-t0:.0f}s', flush=True)


if __name__ == '__main__':
    main()