Skip to content

feat: add BaseExpression.has_terms property#743

Merged
FBumann merged 2 commits into
masterfrom
feat/expression-has-terms
Jun 3, 2026
Merged

feat: add BaseExpression.has_terms property#743
FBumann merged 2 commits into
masterfrom
feat/expression-has-terms

Conversation

@FBumann
Copy link
Copy Markdown
Collaborator

@FBumann FBumann commented Jun 3, 2026

Closes #741.

Adds a public BaseExpression.has_terms property: a boolean array, true at slots with at least one live term (vars != -1), regardless of the constant.

Motivation

PyPSA's nodal balance constraint currently reaches into linopy internals to find constraint rows with no variables (pypsa/optimization/constraints.py#L1166):

empty_nodal_balance = (lhs.vars == -1).all("_term")

With this PR it becomes:

mask = lhs.has_terms

Relation to isnull()

isnull() tests absence (no terms and no constant); has_terms ignores the constant. The two diverge at slots whose constant was revived by fillna() — present, but still without terms. Covered by test_linear_expression_has_terms_ignores_const.

Changes

  • has_terms property on BaseExpression (inherited by LinearExpression and QuadraticExpression), with doctest
  • Tests: masking via where(), divergence from isnull() after fillna(), the outer-merge + reindex pattern, constant-only expressions, and quadratic expressions (linear terms with one factor == -1 must count as live)
  • API reference and release-notes entries

🤖 Generated with Claude Code

Boolean array, true at slots with at least one live term (vars != -1),
regardless of the constant. Gives downstream code a public way to find
empty constraint rows for masking, without reaching into the internal
vars / _term representation.

Closes #741

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@FBumann
Copy link
Copy Markdown
Collaborator Author

FBumann commented Jun 3, 2026

@lkstrp This is mainly to get pypsa out of reaching into linopy internals, which will is a precaution for #717 (although this should not cause problems).

@FBumann FBumann requested a review from lkstrp June 3, 2026 07:53
Copy link
Copy Markdown
Collaborator

@FabianHofmann FabianHofmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this makes very much sense

Comment thread test/test_linear_expression.py Outdated
Per review: TestHasTerms with basic/masking, const-divergence,
merge+reindex, constant-only, and quadratic cases as methods.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@FBumann FBumann merged commit f4af3f1 into master Jun 3, 2026
21 of 22 checks passed
@FBumann FBumann deleted the feat/expression-has-terms branch June 3, 2026 09:16
FBumann added a commit that referenced this pull request Jun 3, 2026
Brings in the coords-as-truth stack (#732), the alignment.py module split
(#742), has_terms (#743), and the MatrixAccessor caching (#716).

Conflict resolutions (consistent rule: keep this branch's v1/legacy
dispatch structure, use master's conversion calls inside it):

- expressions.py: _add_constant, _apply_constant_op_{v1,legacy}, and
  to_constraint use broadcast_to_coords(..., strict=False) instead of
  as_dataarray; SUPPORTED_CONSTANT_TYPES -> CONSTANT_TYPES.
- variables.py: to_linexpr converts via broadcast_to_coords(strict=False),
  then applies the v1/legacy absence handling.
- __init__.py: align from linopy.alignment + LinopySemanticsWarning export.

Test adaptations for post-#732 APIs and semantics:

- test_legacy_violations: name the MultiIndex coords entry (required
  since #732).
- test_linear_expression: the masked-addend tails of test_nterm and
  test_variable_names pin legacy absence behavior; split into
  @pytest.mark.legacy / @pytest.mark.v1 pairs (section 6 divergence).

Full suite under both semantics: 6446 passed, 514 skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

Add public per-slot emptiness check on expressions (has_terms) — PyPSA reaches into .vars/_term internals

2 participants