Skip to content

Add legacy/v1 arithmetic convention with deprecation transition#607

Merged
FBumann merged 11 commits intoharmonize-linopy-operations-mixedfrom
harmonize-linopy-operations-mixed+transition
Mar 10, 2026
Merged

Add legacy/v1 arithmetic convention with deprecation transition#607
FBumann merged 11 commits intoharmonize-linopy-operations-mixedfrom
harmonize-linopy-operations-mixed+transition

Conversation

@FBumann
Copy link
Collaborator

@FBumann FBumann commented Mar 10, 2026

Summary

Add a legacy/v1 arithmetic convention transition layer that makes all coordinate alignment behavior convention-aware, enabling a gradual migration to strict coordinate matching.

Core change: linopy.options["arithmetic_convention"]

Two modes:

  • "legacy" (default) — reproduces current behavior exactly (override join for matching sizes, left/outer otherwise, fillna(0) on NaN). Emits LinopyDeprecationWarning on every legacy codepath.
  • "v1" — strict exact-join semantics: mismatched coordinates raise ValueError with helpful messages suggesting explicit join= parameters. NaN values propagate (no implicit fillna).
# One-line opt-in
linopy.options["arithmetic_convention"] = "v1"

What changed in source

  • config.py: New LinopyDeprecationWarning class, LEGACY_DEPRECATION_MESSAGE, validation for arithmetic_convention setting
  • expressions.py: All arithmetic paths (_align_constant, _add_constant, _apply_constant_op, to_constraint, merge) branch on convention. __add__/__sub__ on LinearExpression and QuadraticExpression pre-validate coordinates under v1 before calling merge.
  • common.py: align() reads convention, maps legacy→inner, v1→exact. Refactored to use finisher pattern for cleaner reconstruction.
  • variables.py: Restored scalar fast path in __mul__, explicit TypeError in __div__ for non-linear division.

What changed in tests

  • v1 test files (test_linear_expression.py, test_constraints.py, test_algebraic_properties.py, test_common.py, test_typing.py): autouse fixture sets v1 convention; tests expect ValueError on coordinate mismatch and test explicit join= escape hatches
  • Legacy test files (test_*_legacy.py): validate old behavior is fully preserved under "legacy"
  • conftest.py: shared fixtures (m, x, y, z, v, u), lazy import linopy to avoid CI doctest collection conflict

Rollout plan

  1. This PR: Default "legacy" — nothing breaks. Deprecation warnings on legacy codepaths.
  2. Downstream: Users opt in with linopy.options["arithmetic_convention"] = "v1"
  3. linopy v1: Flip default to "v1", keep "legacy" available
  4. Later: Remove "legacy" mode entirely

Test plan

  • All tests pass (pytest test/)
  • mypy clean (mypy .)
  • Legacy tests validate backward compatibility
  • v1 tests validate strict coordinate matching
  • CI doctest collection works (lazy import in conftest.py)

🤖 Generated with Claude Code

- Add `options["arithmetic_join"]` setting (default: "legacy") to control
  coordinate alignment in arithmetic operations, merge, and constraints
- Legacy mode reproduces old behavior: override when shapes match, outer
  otherwise for merge; reindex_like for constants; inner for align()
- All legacy codepaths emit FutureWarning guiding users to opt in to "exact"
- Move shared test fixtures (m, x, y, z, v, u) to conftest.py
- Exact-behavior tests use autouse fixture to set arithmetic_join="exact"
- Legacy test files (test_*_legacy.py) validate old behavior is preserved
- All 2736 tests pass

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@FBumann FBumann marked this pull request as draft March 10, 2026 07:24
@FBumann FBumann changed the base branch from master to harmonize-linopy-operations-mixed March 10, 2026 07:24
FBumann and others added 2 commits March 10, 2026 08:30
- Restrict options["arithmetic_join"] to {"legacy", "v1"} instead of
  exposing all xarray join values (explicit join= parameter still accepts any)
- "v1" maps to "exact" join internally
- Add LinopyDeprecationWarning class (subclass of FutureWarning) with
  centralized message including how to silence
- Export LinopyDeprecationWarning from linopy.__init__

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rename setting from 'arithmetic_join' to 'arithmetic_convention'
- Update deprecation message: "will be removed in linopy v1"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
FBumann and others added 7 commits March 10, 2026 09:28
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…y-operations-mixed+transition

# Conflicts:
#	linopy/expressions.py
#	test/conftest.py
#	test/test_constraints.py
#	test/test_linear_expression.py
…n function

- Resolve merge conflicts keeping transition layer logic
- Restore NaN fillna(0) in _add_constant and _apply_constant_op
- Restore simple finisher-based align() function (fixes MultiIndex)
- Use check_common_keys_values in merge legacy path
- Update legacy test files to match origin/harmonize-linopy-operations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove dead check_common_keys_values function from common.py
- Remove redundant default_join parameter from _align_constant, use
  options["arithmetic_convention"] directly
- Gate fillna(0) calls in _add_constant and _apply_constant_op behind
  legacy convention check so NaN values propagate correctly under v1
- Fix legacy to_constraint path to compute constraint RHS directly
  instead of routing through sub() which re-applies fillna
- Restore Variable.__mul__ scalar fast path via to_linexpr(other)
- Restore Variable.__div__ explicit TypeError for non-linear division
- Update v1 tests to expect ValueError on mismatched coords and test
  explicit join= escape hatches

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolve merge conflicts keeping convention-aware (legacy/v1) code paths.
Under v1: exact join enforcement in operators, NaN propagation (no fillna),
merge uses outer join for helper dims with pre-validation in __add__.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add return type annotations (Generator) to all v1_convention fixtures
- Add importmode = "importlib" to pytest config to fix import mismatch
  when linopy is installed from wheel and source dir is also present
- Use tuple literal in loop to fix arg-type error

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@FBumann FBumann marked this pull request as ready for review March 10, 2026 15:32
Top-level `import linopy` in conftest.py caused pytest to import the
package from site-packages before collecting doctests from the source
directory, triggering import file mismatch errors on all platforms.

Move the import inside fixture functions where it's actually needed.
Also revert the unnecessary test.yml and importmode changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@FBumann FBumann changed the title Harmonize arithmetic conventions with legacy transition Add legacy/v1 arithmetic convention with deprecation transition Mar 10, 2026
@FBumann FBumann merged commit 0b4605a into harmonize-linopy-operations-mixed Mar 10, 2026
16 checks passed
@FBumann FBumann deleted the harmonize-linopy-operations-mixed+transition branch March 10, 2026 16:02
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.

1 participant