Process GP-02 — Triggered by internal initiative to fix, improve, or extend GemsPy. Not driven by a new GEMS Language version.
Deviation from standard workflow — Step 3: extended impact analysis on results is required. You must explicitly state whether solver results are expected to change, and if so, document the reasoning.
Full design doc (current, updated in place as decisions are made): docs/design/warm-start-plan.md on branch claude/gemspy-warmstart-possibilities-5qo718.
Related: #273 (ambitious follow-on — persistent rolling-window model for _run_sequential, gated on this issue's linopy spike and on profiling). This issue should ship first regardless of whether #273 is pursued.
Affected Component
Optimization problem construction (simulation/) — plus session orchestration (gems_runner/session/session.py) and config schema (gems_craft/optim_config/parsing.py).
Type of Change
Minor: backward-compatible new feature, opt-in via two new config flags (warm-start-heuristic, warm-start-interblock), both defaulting to false. No existing study, config, or test changes behavior by default.
Description
GemsPy never reuses an LP/MIP basis between solves today. Two places re-solve a structurally near-identical problem and can benefit from solver warm-start, the same way Antares Simulator's useOptim1BasisInOptim2 and useOptim1BasisInNextWeek flags do:
- Two-stage heuristic solve —
SimulationSession._run_block solves the same OptimizationProblem twice (once, then again after apply_thermal_heuristics tightens a few variable bounds). Living inside _run_block means this benefits frontal, sequential, and parallel resolution modes uniformly, with no per-mode branching.
- Inter-block chaining —
_run_sequential builds and solves a fresh linopy.Model per time block; block-to-block the constraint structure is identical, only the data differs. Scoped to _run_sequential only — _run_parallel is deliberately excluded: its blocks are meant to stay independent so a future genuinely-concurrent implementation (process/thread pool) doesn't inherit an artificial ordering dependency that would need undoing later.
Mechanism: linopy.Model.solve() already exposes basis_fn/warmstart_fn (confirmed working for HiGHS via io_api="direct"), so OptimizationProblem.solve() needs no changes — only new orchestration in session.py (a _BasisChain helper with a verified O(1) space bound, RAM-backed temp files via /dev/shm with a portable fallback, and a defensive wrapper for solvers that don't support warmstarting).
Before implementing as file-based: linopy 0.8.0/0.9.0 shipped native persistent-Solver support with in-place updates (verified against the installed source, not just release notes) that looks purpose-built for the heuristic-solve case specifically, since _run_block already reuses one linopy.Model for both solves. GemsPy is pinned to 0.6.6, but the dependency floor ("linopy>=0.6") already permits the upgrade. Recommend a short compatibility spike (bump to 0.9.x, verify heuristic_runner.py's direct variable-bound mutation and optimization.py's _MergedGroupVariable still behave correctly) before building the file-based design — if it pans out, §5 of the plan gets meaningfully simpler. This spike also gates #273.
Open questions carried into implementation (see plan §4 and prior-art review):
- Solver support: generic-with-fallback (try any solver, catch
NotImplementedError) vs. an explicit whitelist matching Antares's own conservative design (only SIRIUS/XPRESS there) — not resolved in the plan, flagged for the implementation PR.
- The heuristic-solve benefit is conditional: it behaves as a clean LP-to-LP warmstart only when every component uses
RELAXED/HEURISTIC integer strategy. Since IntegerStrategyId defaults to EXACT, mixed-strategy studies make both solves MIPs, and warmstart only re-warms the root LP relaxation — weaker, but still worth having.
Adjacent, near-free win scoped into the same implementation: simulation_table.py already declares a basis_status output column that's currently a dead placeholder (None) — the basis-extraction machinery this feature needs can populate it, matching Antares's own documented modeler output format.
Results Impact
No change to the optimal objective value is intended — warm-start only affects how fast a solve converges, not what problem is being solved. However: per-timestep dispatch values are not guaranteed to be bit-identical between warm-start-on and warm-start-off runs on degenerate LPs (multiple exactly-tied optimal bases — routine in energy dispatch: symmetric generators, zero-marginal-cost curtailment ties). Antares hit this in production twice (a real shipped bugfix to clear the basis at MC-year boundaries, and a test-infrastructure fix to exclude basis status from result comparisons) — this is why the plan's validation strategy leads with objective-value equality rather than per-timestep equality.
Validation Strategy
- Objective-value equality (tight relative tolerance) between warm-start-on/off runs, for both flags independently and combined — the primary correctness check.
- Per-timestep equality as a secondary check, gated to a test study verified free of dispatch degeneracy (documented explicitly, so future test-data changes don't reintroduce flakiness).
- Space-boundedness test: assert at most one basis file exists per chain at any point during a multi-block
_run_sequential run.
- Plumbing unit tests for
_BasisChain and the solver-fallback wrapper.
- Dedicated regression test on a mixed-integer-strategy study (weaker-benefit case) confirming warm-start doesn't change the objective there either.
- Full detail in plan §7.
Process Checklist
Step 1 — Issue Creation
Step 2 — Triage
Step 3 — Impact Analysis ⚠️ extended results analysis required
Step 4 — Implementation
Step 5 — Testing & Validation
Step 6 — CI Validation
Step 7 — Review & Merge
Step 8 — Versioning
Step 9 — Supporting Files
Step 10 — Release
Process GP-02 — Triggered by internal initiative to fix, improve, or extend GemsPy. Not driven by a new GEMS Language version.
Full design doc (current, updated in place as decisions are made):
docs/design/warm-start-plan.mdon branchclaude/gemspy-warmstart-possibilities-5qo718.Related: #273 (ambitious follow-on — persistent rolling-window model for
_run_sequential, gated on this issue's linopy spike and on profiling). This issue should ship first regardless of whether #273 is pursued.Affected Component
Optimization problem construction (
simulation/) — plus session orchestration (gems_runner/session/session.py) and config schema (gems_craft/optim_config/parsing.py).Type of Change
Minor: backward-compatible new feature, opt-in via two new config flags (
warm-start-heuristic,warm-start-interblock), both defaulting tofalse. No existing study, config, or test changes behavior by default.Description
GemsPy never reuses an LP/MIP basis between solves today. Two places re-solve a structurally near-identical problem and can benefit from solver warm-start, the same way Antares Simulator's
useOptim1BasisInOptim2anduseOptim1BasisInNextWeekflags do:SimulationSession._run_blocksolves the sameOptimizationProblemtwice (once, then again afterapply_thermal_heuristicstightens a few variable bounds). Living inside_run_blockmeans this benefits frontal, sequential, and parallel resolution modes uniformly, with no per-mode branching._run_sequentialbuilds and solves a freshlinopy.Modelper time block; block-to-block the constraint structure is identical, only the data differs. Scoped to_run_sequentialonly —_run_parallelis deliberately excluded: its blocks are meant to stay independent so a future genuinely-concurrent implementation (process/thread pool) doesn't inherit an artificial ordering dependency that would need undoing later.Mechanism:
linopy.Model.solve()already exposesbasis_fn/warmstart_fn(confirmed working for HiGHS viaio_api="direct"), soOptimizationProblem.solve()needs no changes — only new orchestration insession.py(a_BasisChainhelper with a verified O(1) space bound, RAM-backed temp files via/dev/shmwith a portable fallback, and a defensive wrapper for solvers that don't support warmstarting).Before implementing as file-based: linopy
0.8.0/0.9.0shipped native persistent-Solver support with in-place updates (verified against the installed source, not just release notes) that looks purpose-built for the heuristic-solve case specifically, since_run_blockalready reuses onelinopy.Modelfor both solves. GemsPy is pinned to0.6.6, but the dependency floor ("linopy>=0.6") already permits the upgrade. Recommend a short compatibility spike (bump to0.9.x, verifyheuristic_runner.py's direct variable-bound mutation andoptimization.py's_MergedGroupVariablestill behave correctly) before building the file-based design — if it pans out, §5 of the plan gets meaningfully simpler. This spike also gates #273.Open questions carried into implementation (see plan §4 and prior-art review):
NotImplementedError) vs. an explicit whitelist matching Antares's own conservative design (only SIRIUS/XPRESS there) — not resolved in the plan, flagged for the implementation PR.RELAXED/HEURISTICinteger strategy. SinceIntegerStrategyIddefaults toEXACT, mixed-strategy studies make both solves MIPs, and warmstart only re-warms the root LP relaxation — weaker, but still worth having.Adjacent, near-free win scoped into the same implementation:
simulation_table.pyalready declares abasis_statusoutput column that's currently a dead placeholder (None) — the basis-extraction machinery this feature needs can populate it, matching Antares's own documented modeler output format.Results Impact
No change to the optimal objective value is intended — warm-start only affects how fast a solve converges, not what problem is being solved. However: per-timestep dispatch values are not guaranteed to be bit-identical between warm-start-on and warm-start-off runs on degenerate LPs (multiple exactly-tied optimal bases — routine in energy dispatch: symmetric generators, zero-marginal-cost curtailment ties). Antares hit this in production twice (a real shipped bugfix to clear the basis at MC-year boundaries, and a test-infrastructure fix to exclude basis status from result comparisons) — this is why the plan's validation strategy leads with objective-value equality rather than per-timestep equality.
Validation Strategy
_run_sequentialrun._BasisChainand the solver-fallback wrapper.Process Checklist
Step 1 — Issue Creation
Step 2 — Triage
Step 3 — Impact Analysis⚠️ extended results analysis required
Step 4 — Implementation
false)Step 5 — Testing & Validation
_BasisChain, fallback wrapper)Step 6 — CI Validation
mypy)black,isort)pytest)Step 7 — Review & Merge
Step 8 — Versioning
pyproject.tomlversion bumpedStep 9 — Supporting Files
AGENTS.mdreviewed for impact and updated if neededStep 10 — Release