Skip to content

[GP-02] Warm-start (exploratory): persistent rolling-window linopy model for sequential resolution #273

Description

@aoustry

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-rolling-window.md on branch claude/gemspy-warmstart-possibilities-5qo718.

Depends on / follows on from #272 — this is a bigger, riskier phase-2 exploration on top of that plan's inter-block warm-start (§6), not a replacement for it. Not worth pursuing unless the gates below (in particular, profiling) come back favorable; #272 should ship first regardless.

Affected Component

Optimization problem construction (simulation/) — specifically _OptimizationProblemBuilder/build_problem, the shared core every resolution mode and most of the test suite depends on.

Type of Change

Major: this is exactly the kind of change the GP-02 template's own example calls out ("e.g. rolling horizon") — a structural rearchitecture of problem construction, not an additive plumbing change. Still intended to be opt-in and backward-compatible in its outward behavior, but the internal blast radius is large.

Description

Explores replacing #272's file-based inter-block warm-start (rebuild a fresh linopy.Model per block, transfer only the basis) with keeping one persistent linopy.Model per scenario in _run_sequential, rolling its data forward block-by-block in place instead of rebuilding — using linopy's 0.8.0/0.9.0 native stateful-Solver / in-place-update API. This is tier (c) ("no rebuild at all") applied to inter-block resolution specifically, exploiting the fact that GEMS parameters/variables are already indexed on an explicit time dimension as part of the domain model, so block-to-block the shape of the problem is already identical for a fixed block_length — only the data differs.

Explicitly scoped to _run_sequential only, same as #272 — not just for now. A persistent, stateful rolling model is structurally the opposite of what _run_parallel needs (independent, dispatchable blocks for a future concurrent implementation), so it isn't a candidate even in principle. _run_benders/decomposed problems are out of scope too.

The central open technical question — does linopy's in-place update cover constraint and objective coefficients, not just variable bounds (GEMS constraint/objective expressions can reference time-varying parameters directly, e.g. an efficiency or price time series multiplying a variable) — has been verified yes, directly against the installed linopy 0.9.0 source (not just release notes): linopy.persistent.diff.ModelDiff carries coefficient-level deltas for both constraints and the objective, dispatched to real in-place highspy.Highs calls (changeCoeff, changeColsCost, etc.) for HiGHS — one of exactly four backends (HiGHS, Gurobi, Xpress, Mosek) with supports_persistent_update = True. The remaining risk narrows to RebuildReason.SPARSITY (a coefficient going from exactly-zero to nonzero between blocks forces a fallback rebuild), with a concrete guard identified: disable linopy's default sanitize_zeros=True on the persistent/rolling code path.

Two architectures were considered; see the plan for why "build the full horizon once, activate one block at a time" was rejected (it reintroduces the exact scalability problem block-decomposition exists to solve) in favor of the genuinely-rolling, block-sized persistent model.

Gated behind, in order:

  1. [GP-02] Warm-start: reuse solver basis for heuristic solves and sequential inter-block resolution #272's linopy 0.9.x compatibility spike succeeding for the heuristic case — a smaller, lower-risk proving ground for the same underlying API.
  2. A dedicated spike confirming the coefficient-update behavior holds on a real GEMS model (not just the mechanism existing in the abstract), via Solver._last_rebuild_reason introspection, with sanitize_zeros disabled.
  3. Profiling showing per-block rebuild cost (not solve cost) is actually a meaningful fraction of total _run_sequential run time on a realistic study — if rebuild is already cheap relative to solving, this design buys little over [GP-02] Warm-start: reuse solver basis for heuristic solves and sequential inter-block resolution #272's file-based approach.

If any of the three comes back unfavorable, #272's file-based design remains the right one and this issue should be closed as not-planned rather than pursued further.

Results Impact

Same intent as #272: no change to the optimal objective value, only to how it's reached. Same degenerate-tie caveat applies (possibly more acutely — a rolling model may go through many consecutive in-place updates within one scenario without ever re-deriving from scratch, which is a separate, additional numerical-drift concern beyond the degenerate-tie issue; the plan recommends a periodic forced full-rebuild reset boundary, at minimum at every scenario boundary, as a safety valve).

Validation Strategy

Process Checklist

Step 1 — Issue Creation

  • Issue created and linked to process GP-02

Step 2 — Triage

  • Process confirmed applicable
  • Assigned to responsible contributor
  • Priority and milestone set (if applicable)

Step 3 — Impact Analysis ⚠️ extended results analysis required

  • Affected modules identified
  • Results impact explicitly stated: change intended / no change intended
  • If results expected to change: before/after difference described and justified
  • Breaking vs backward-compatible change determined

Step 4 — Implementation

Step 5 — Testing & Validation

  • Rolled-vs-rebuilt objective equivalence confirmed
  • Shape-invariance assertion in place
  • Fallback-path introspection covered by tests

Step 6 — CI Validation

  • Type checking passes (mypy)
  • Formatting passes (black, isort)
  • All tests pass in CI (pytest)

Step 7 — Review & Merge

  • PR reviewed; documentation changes included

Step 8 — Versioning

  • pyproject.toml version bumped

Step 9 — Supporting Files

  • AGENTS.md reviewed for impact and updated if needed

Step 10 — Release

  • If a release is needed: follow the release process in the Developer Guidelines

Metadata

Metadata

Assignees

No one assigned

    Labels

    GP-02Internal GemsPy bug fixes, features, or improvements (not driven by a GEMS Language release)

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions