Summary
The core DP backward induction in _run_dynamic_programming (core/bess/dp_battery_algorithm.py) is a nested pure-Python loop over the discretized state/action space — roughly 240 SOE levels (0.1 kWh steps) × ~150 power levels (0.2 kW steps) — evaluated once per period, with no numpy vectorization across states or actions.
Measured cost
Direct benchmark of optimize_battery_schedule (synthetic multi-window price scenario, .venv interpreter):
- 96 periods (1-day, 15-min resolution — production's actual horizon per
battery_system_manager.py): ~11.5s per optimizer call
- 192 periods (2-day, 15-min resolution): ~22.8s per optimizer call
Why this matters now
#234 identifies a real correctness bug in this same function (dead cost-basis threading in the backward pass — the anti-cycling profitability floor evaluates every discharge against a frozen initial_cost_basis instead of the true path-dependent value). The minimal correct fix requires running backward induction twice per optimizer call (compute a candidate policy, forward-replay it to get the true per-period entering cost basis, then re-run backward induction with that as input) — a value-function correction, not something a cheap post-hoc patch can achieve.
That fix would roughly double the runtimes above (~23s / ~45s per hourly re-optimization) on top of an already-unvectorized loop. We've decided not to ship the #234 fix until this hot loop is vectorized, so the doubling is negligible instead of user-visible.
Suggested direction (not verified as sufficient — needs its own investigation)
- Vectorize the per-period inner loops (state × action search, reward computation, state transition) with numpy so each period's DP step is array-based instead of doubly-nested Python loops.
- Verify numerical parity against existing pinned test fixtures before and after — every existing regression test must produce identical (or floating-point-tolerance-identical) actions and economics.
- Re-benchmark at both 96- and 192-period horizons after vectorizing.
Related
Summary
The core DP backward induction in
_run_dynamic_programming(core/bess/dp_battery_algorithm.py) is a nested pure-Python loop over the discretized state/action space — roughly 240 SOE levels (0.1 kWh steps) × ~150 power levels (0.2 kW steps) — evaluated once per period, with no numpy vectorization across states or actions.Measured cost
Direct benchmark of
optimize_battery_schedule(synthetic multi-window price scenario,.venvinterpreter):battery_system_manager.py): ~11.5s per optimizer callWhy this matters now
#234 identifies a real correctness bug in this same function (dead cost-basis threading in the backward pass — the anti-cycling profitability floor evaluates every discharge against a frozen
initial_cost_basisinstead of the true path-dependent value). The minimal correct fix requires running backward induction twice per optimizer call (compute a candidate policy, forward-replay it to get the true per-period entering cost basis, then re-run backward induction with that as input) — a value-function correction, not something a cheap post-hoc patch can achieve.That fix would roughly double the runtimes above (~23s / ~45s per hourly re-optimization) on top of an already-unvectorized loop. We've decided not to ship the #234 fix until this hot loop is vectorized, so the doubling is negligible instead of user-visible.
Suggested direction (not verified as sufficient — needs its own investigation)
Related