Skip to content

Upgrade linopy to >=0.9.0 - #275

Merged
aoustry merged 2 commits into
mainfrom
claude/upgrade-linopy-0-9-0-94ay56
Aug 19, 2026
Merged

Upgrade linopy to >=0.9.0#275
aoustry merged 2 commits into
mainfrom
claude/upgrade-linopy-0-9-0-94ay56

Conversation

@aoustry

@aoustry aoustry commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Process ID

Process: N/A

Description

Upgrades the linopy dependency from >=0.6 to >=0.9.0 and re-locks uv.lock (linopy 0.6.6 → 0.9.0).

Two consequences of the upgrade:

  • Python 3.10 support is dropped. linopy 0.9 requires Python >= 3.11, so requires-python moves to >=3.11 and the 3.10 classifier is removed. CI and publishing already run on 3.11, so no workflow changes are needed.

  • linopy 0.9 refuses to solve a model with no objective (ValueError: No objective has been set on the model). Six tests built pure feasibility problems from models with no objective contribution and started failing. Rather than changing the solver pipeline (optimization.py is deliberately untouched), those tests now declare a useless null objective:

    • the five inline models in test_simulation_table_extra_outputs.py gain objective_contributions={"null_objective": (literal(0) * var(...)).time_sum().expec()};
    • test_short_term_storage_base_with_yaml uses models from the shared lib_unittest.yml fixture (left untouched, since other tests assert exact objective values against it), so the test sets 0 * <first variable> directly on the built linopy model before solving.

    Objective values are unchanged in all six tests. Note this means a user-authored library with no objective contribution at all now fails at solve time under linopy 0.9; the changelog entry documents the null-objective idiom.

Impact Analysis

  • pyproject.toml / uv.lock: linopy floor raised to 0.9.0, Python floor raised to 3.11 (the lock file shrinks ~800 lines because dropping 3.10 collapses the forked numpy/pandas/xarray/scipy resolutions).
  • docs/CHANGELOG.md: entry added under [Unreleased].
  • tests/: null objectives added to the six tests described above.
  • No source modules are modified (expression/, simulation/, study/, optim_config/ untouched).
  • Solver output values are not expected to change: the full test suite passes with identical asserted objective values, and the added objectives are identically zero.

Checklist

  • Unit tests pass (pytest) — 587 passed, 6 skipped, 2 xfailed
  • Type checking passes (mypy)
  • Formatting passes (black, isort)
  • pyproject.toml version bumped if applicable — left at 0.1.3; bump deferred to the maintainers since this changes the supported Python range
  • AGENTS.md reviewed for impact and updated if needed — no Python-version or linopy-version statements to update

linopy 0.9 requires Python >= 3.11, so requires-python moves from >=3.10 to
>=3.11 and the 3.10 classifier is dropped. CI already runs on 3.11.

linopy 0.9 also refuses to solve a model whose objective is empty. A handful
of tests built pure feasibility problems (models with no objective
contribution); they now declare a null objective (0 * variable) so the solve
goes through, with objective values unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NetqdfrCBHwavdcB2EQj4G
@aoustry
aoustry requested a review from tbittar August 14, 2026 15:56
# linopy >= 0.9 refuses to solve a model with no objective: set a null one.
linopy_model = problem.linopy_model
first_var = linopy_model.variables[next(iter(linopy_model.variables))]
linopy_model.add_objective(0 * first_var)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We cannot set a constant 0 objective ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Unfortunately not — linopy 0.9 gives no way to express a constant objective. Verified against linopy 0.9.0:

  • m.add_objective(0) raises ValueError: Invalid type of 'expr' (<class 'int'>). Must be a LinearExpression or QuadraticExpression.
  • A LinearExpression holding only a constant (no variable terms) is empty, and solve() rejects an empty objective with ValueError: No objective has been set on the model. linopy's own error message suggests exactly this workaround: "Use m.add_objective(...) first (e.g. m.add_objective(0 * x) for a pure feasibility problem)."

So 0 * <variable> is the minimal objective linopy accepts: one term with a zero coefficient. The solved objective value is still 0, so the assertion below is unaffected.


Generated by Claude Code

@aoustry
aoustry merged commit 8b18229 into main Aug 19, 2026
2 checks passed
@aoustry
aoustry deleted the claude/upgrade-linopy-0-9-0-94ay56 branch August 19, 2026 08:42
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.

3 participants