fix: ignore masked variable labels when matching constraints - #886
Open
YassineAbdelouadoud wants to merge 1 commit into
Open
fix: ignore masked variable labels when matching constraints#886YassineAbdelouadoud wants to merge 1 commit into
YassineAbdelouadoud wants to merge 1 commit into
Conversation
Merging this PR will improve performance by 32.18%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ⚡ | Memory | test_to_lp[storage-n=10] |
2.9 MB | 1.7 MB | +70.76% |
| ⚡ | Memory | test_to_lp[storage-n=250] |
35.8 MB | 29.7 MB | +20.8% |
| ⚡ | Memory | test_to_lp[expression_arithmetic-n=250] |
45.8 MB | 40.9 MB | +11.94% |
Tip
Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.
Comparing YassineAbdelouadoud:fix-remove-variables-masked (f884458) with master (09e11ad)
Footnotes
-
175 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
`Model.remove_variables` deleted every constraint whose `has_variable` matched the removed variable. For a masked variable that was every constraint with a padded term slot: `-1` marks a masked entry on the variable side and an empty term slot on the constraint side, and the two were compared without filtering the sentinel. Filter the -1 entries out of the variable labels before matching, via a new `common.assigned_labels` helper, in both `has_variable` implementations and in the objective cleanup of `remove_variables`. Only the dense `Constraint` path was actually affected -- a CSR matrix stores no empty slots -- but the same guard now holds for both. Fixes PyPSA#883 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
YassineAbdelouadoud
force-pushed
the
fix-remove-variables-masked
branch
from
August 11, 2026 13:51
7d2dc5e to
f884458
Compare
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.
I encountered this issue while updating to the latest linopy version and running the POMMES framework
Fixes #883
Note
The following content was generated by AI.
The bug
Model.remove_variablesremoves every constraint for whichconstraint.has_variable(variable)is true.has_variablecompared theconstraint's
varsagainst the variable's labels without filtering the-1sentinel, which means two different things on the two sides:
-1marks a masked entry;vars,-1marks an empty (padded) term slot.So any masked variable matched any constraint that has a padded term slot, even
when the two are entirely unrelated, and the constraint was silently deleted.
Models built with
mask=can therefore lose constraints and solve to a wrongoptimum or unbounded.
The fix
Filter the
-1entries out of the variable labels before matching, through anew
common.assigned_labelshelper, applied at the three places that match avariable's labels against another object's labels:
Constraint.has_variable(dense path) — the one that was actually broken;CSRConstraint.has_variable— a CSR matrix stores no empty slots, so thispath was already correct; the guard is added for symmetry;
remove_variables, which had the samelatent mismatch (there it only dropped already-empty terms).
Test
test_remove_masked_variable_keeps_unrelated_constraintsintest/test_model.py,parametrized over
freezeto cover both constraint representations. It builds aconstraint over a masked variable
b(hence with padded term slots) plus anunmasked
c, and a second constraint that genuinely uses the maskeda, thenremoves
a: the first constraint must survive, the second must still go.On master the
freeze=Falsecase fails atassert not without_a.has_variable(a);the
freeze=Truecase passes before and after.Verification
test/remotewas skipped locally because theoetcextra(
google-cloud-storage) is not installed in this environment; it is unrelatedto this change.
Reproduction on master, before the fix: