Skip to content

Conversation

@FBumann
Copy link
Contributor

@FBumann FBumann commented Feb 1, 2026

Closes # (if applicable).

Changes proposed in this Pull Request

Checklist

  • Code changes are sufficiently documented; i.e. new functions contain docstrings and further explanations may be given in doc.
  • Unit tests for new features were added (if applicable).
  • A note for the release notes doc/release_notes.rst of the upcoming release is included.
  • I consent to the release of this PR's code under the MIT license.

RobbieKiwi and others added 4 commits February 1, 2026 14:01
  Two files changed:

  linopy/expressions.py:
  - Added LazyLinearExpression class (inherits LinearExpression) that stores a list of un-merged Dataset _parts instead of a single concatenated Dataset
  - Key overrides: data (lazy materialization), const, flat (iterates parts directly), __add__/__sub__/__neg__ (propagate laziness), all with fallback to parent when not lazy
  - Modified merge() to return LazyLinearExpression when dim == TERM_DIM and cls is a LinearExpression subclass
  - Protected lazy expressions from premature materialization in merge's data extraction and override detection

  linopy/objective.py:
  - Changed is_linear/is_quadratic from type(x) is LinearExpression identity checks to isinstance checks, so LazyLinearExpression is correctly identified as linear

  Performance (different-coordinate variables, 5 × 200×200×50):
  ┌───────────────────────┬─────────┬─────────┬───────────────┐
  │        Metric         │ Before  │  After  │    Change     │
  ├───────────────────────┼─────────┼─────────┼───────────────┤
  │ Expression build time │ 0.31s   │ 0.09s   │ 3.4x faster   │
  ├───────────────────────┼─────────┼─────────┼───────────────┤
  │ flat export time      │ 0.57s   │ 0.17s   │ 3.4x faster   │
  ├───────────────────────┼─────────┼─────────┼───────────────┤
  │ Peak RSS at flat      │ 1337 MB │ 1186 MB │ -151 MB (11%) │
  └───────────────────────┴─────────┴─────────┴───────────────┘
  Same-coordinate variables see no regression — materialization occurs at to_constraint time with the same override join as before. Phase 2 (lazy constraints) would extend
  savings to that path.
  1. _compact() — Groups parts by their coordinate signature and merges same-coord groups using join="override" (no padding). Keeps part count low after many chained additions.
  2. to_polars() — Lazy override that converts each part to a polars DataFrame independently and concatenates, same pattern as flat.
  3. rename() — Per-part dispatch that only renames dims/vars present in each part, avoiding errors for parts that lack the target dimension.
  4. diff() — Per-part dispatch that applies diff only to parts containing the target dimension.
  5. sel() and shift() — Fall back to materialized path since their semantics (indexing, fill values) need a consistent coordinate space.

  Still deferred (Phase 2+):
  - Lazy constraint propagation (to_constraint currently materializes)
  - Lazy _sum() for dimension reduction
  - These would require changes to the Constraint class and solver IO paths
@FBumann
Copy link
Contributor Author

FBumann commented Feb 1, 2026

For now continued in FBumann#12

@FBumann FBumann closed this Feb 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants