feat(piecewise): active_fill for partial active gates (alt. to #797, #796)#798
Merged
Conversation
…#796) `add_piecewise_formulation(active=...)` assumed the gate covered the whole indexed dimension. A gate defined over only a subset of coordinate labels (or with masked entries) silently forced the uncovered entries to zero — breaking mixed committable / non-committable formulations (PyPSA#1755). - Add `linopy.active_gate(active, coords, fill_value=1)`: pads a partial gate to full coverage, treating missing/masked entries as always-active (1) or always-off (0). Lives in its own module `linopy/_active_gate.py` as a temporary legacy stopgap; under v1 the bare `active.reindex(coords).fillna(fill_value)` idiom suffices and the helper is expected to be deprecated. - `add_piecewise_formulation` now rejects an under-defined `active` (strict subset or masked) with an actionable error instead of mis-solving. A lower-dimensional gate still broadcasts and is accepted. - Docs (api, piecewise guide, release notes) and tests in a dedicated `test/test_piecewise_active_gate.py` (parametrized over both partial shapes and incremental/sos2/disjunctive paths). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ction Apply review suggestion (#797): replace the hand-rolled vars/term-dim reduction with the public `has_terms` property in `active_gate` and the coverage validation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…meter Per PR #797 review: instead of a standalone `active_gate` helper, gate a partial `active` via an `active_fill` parameter on `add_piecewise_formulation`. The function derives its own coordinate, so the caller supplies nothing extra; `active_fill=None` (default) keeps the function strict (partial `active` raises), and `0`/`1` opt into always-off / always-on. - Drop `linopy/_active_gate.py` and the `active_gate` export. - Add `active_fill: int | None` to `add_piecewise_formulation`; fold the guard + padding into `_resolve_active`. - `active_fill` is transitional (removed once v1 makes `active.reindex(coords).fillna(value)` sufficient) — documented as such. - Tests renamed to test_piecewise_active_fill.py; docs updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Merging this PR will not alter performance
Comparing Footnotes
|
…ls or masked Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
@FabianHofmann Id argue this is more ergonomic than #797. And handling the removal of this isn't harder... |
FabianHofmann
approved these changes
Jun 30, 2026
FabianHofmann
left a comment
Collaborator
There was a problem hiding this comment.
let's pull that in. I spent some time with trying to find a way without the active_fill. but in the end this is fail fast and explicit. just as we want.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
(placeholder — Felix to rewrite)
Note
The following content was generated by AI.
Alternative to #797. Closes #796. Same goal (partial
activegate), but exposes the fix as anactive_fillparameter onadd_piecewise_formulationinstead of a standaloneactive_gatehelper. The first two commits are #797's helper approach; the third refactors it into the parameter, so the evolution is visible in history while the net diff is just the parameter design.What
add_piecewise_formulation(..., active=status, active_fill=1):active_fill=None(default) → a partialactive(subset of the indexed dim, or masked) raises — the function stays strict.active_fill=0|1→ opt in to gating the missing entries as always-off / always-on.The function derives its own coordinate, so the caller supplies nothing extra (no
coords, no helper, no import).Why a parameter instead of the
active_gatehelper (#797)coordsargument; the function already knows its coordinate.active_fill=Nonerejects a partial gate, so accidental partial coverage surfaces as an error rather than a silent always-on/off.active_fillis documented as slated for removal once v1 arithmetic semantics makeactive.reindex(coords).fillna(value)correct on its own. It's a parameter whose implementation is legacy-specific, not throwaway public surface elsewhere.Changes (net)
linopy/piecewise.py—active_fillparameter;_resolve_activefolds the coverage guard (raise whenNone) and the padding (where(has_terms, fill)when set) into one place.test/test_piecewise_active_fill.py— parametrized over both gap shapes (strict-subset, masked) × resolution/validation/solver (incremental, sos2, disjunctive); coversactive_fill=0/1,None-raises,active_fillwithoutactive, and lower-dimensional broadcast.Tests
test_piecewise_active_fill.py+test_piecewise_constraints.py: 248 passed. ruff / blackdoc / mypy clean.🤖 Generated with Claude Code