🤖 This issue was written by Claude (Claude Code), based on hands-on findings while migrating PyPSA to the v1 arithmetic semantics. Reproductions were verified against the PR #717 branch.
Summary
Under legacy semantics, the documented resolution for absent slots — .fillna(value) — still emits LinopySemanticsWarning. As a result there is no single expression that is both v1-correct and legacy-warning-free for the shift()/where() absent-slot pattern, which forces downstream code to branch on linopy.options["semantics"].
The warning text itself tells users to resolve absence with .fillna(value):
if you meant absent at this slot, mark it on the variable instead (mask=, .where(cond), .reindex(...), .shift(...)) … resolve with .fillna(value)
but applying exactly that resolution does not clear the warning under legacy.
Reproduction
linopy 0.8.0.post1.dev123+g5a131a628 (branch feat/arithmetic-convention), Python 3.12:
import warnings, pandas as pd
import linopy as ln
from linopy.config import LinopySemanticsWarning
m = ln.Model()
names = pd.Index(["a", "b", "c"], name="name")
sns = pd.Index([0, 1, 2], name="snapshot")
x = m.add_variables(coords=[sns, names], name="x")
for sem in ["legacy", "v1"]:
ln.options["semantics"] = sem
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
x - x.shift(snapshot=1).fillna(0) # the documented absent-slot resolution
warned = any(issubclass(a.category, LinopySemanticsWarning) for a in w)
print(sem, "->", "WARN" if warned else "ok")
Both branches produce the intended numeric result (0 at the first snapshot); only legacy warns.
Why it matters
Downstream projects with strict warning filters (e.g. PyPSA runs pytest with error::FutureWarning, and LinopySemanticsWarning subclasses FutureWarning) turn this into a hard error. Because the prescribed fix doesn't silence it, the only way to be green on both semantics is to gate the expression on the active semantics — exactly the kind of version-branching the migration is meant to avoid. This came up repeatedly in PyPSA's unit-commitment (status - status.shift(1)) and storage SOC (previous_e / previous_soc) constraints.
Suggestion
Calling the documented resolution (.fillna(value) on an object that has absent slots) should clear the legacy LinopySemanticsWarning for that operand — i.e. once absence has been explicitly resolved, the subsequent arithmetic is unambiguous under both semantics and should not warn. That would make the shift/where + fillna pattern a single cross-compatible form.
Related: the return-type half of this (Variable.fillna(scalar) differing across releases) is filed separately.
Summary
Under legacy semantics, the documented resolution for absent slots —
.fillna(value)— still emitsLinopySemanticsWarning. As a result there is no single expression that is both v1-correct and legacy-warning-free for theshift()/where()absent-slot pattern, which forces downstream code to branch onlinopy.options["semantics"].The warning text itself tells users to resolve absence with
.fillna(value):but applying exactly that resolution does not clear the warning under legacy.
Reproduction
linopy 0.8.0.post1.dev123+g5a131a628(branchfeat/arithmetic-convention), Python 3.12:Both branches produce the intended numeric result (
0at the first snapshot); only legacy warns.Why it matters
Downstream projects with strict warning filters (e.g. PyPSA runs pytest with
error::FutureWarning, andLinopySemanticsWarningsubclassesFutureWarning) turn this into a hard error. Because the prescribed fix doesn't silence it, the only way to be green on both semantics is to gate the expression on the active semantics — exactly the kind of version-branching the migration is meant to avoid. This came up repeatedly in PyPSA's unit-commitment (status - status.shift(1)) and storage SOC (previous_e/previous_soc) constraints.Suggestion
Calling the documented resolution (
.fillna(value)on an object that has absent slots) should clear the legacyLinopySemanticsWarningfor that operand — i.e. once absence has been explicitly resolved, the subsequent arithmetic is unambiguous under both semantics and should not warn. That would make the shift/where + fillna pattern a single cross-compatible form.Related: the return-type half of this (
Variable.fillna(scalar)differing across releases) is filed separately.