Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d51cda8
fix(variables): broadcast and order pandas/DataArray bounds in coords
FBumann May 23, 2026
a246006
docs(variables): frame add_variables coords as source of truth
FBumann May 23, 2026
aa0c80d
docs: frame bounds fix as extending 0.7.0's coords-as-truth fix
FBumann May 23, 2026
4ddc3c2
docs: reword as "extend and finalize", emphasize hardening
FBumann May 23, 2026
5557a9f
docs: rephrase as "0.7.0 made ... this release closes the two remaini…
FBumann May 23, 2026
cdc987b
docs: spell out dims/order/values in coords-as-truth bullet
FBumann May 23, 2026
001d071
test(variables): cover pandas MultiIndex bounds and dim reindex
FBumann May 23, 2026
bca89e7
refactor: move as_dataarray_in_coords to common.py
FBumann May 23, 2026
b28f3df
refactor(common): simplify _named_pandas_to_dataarray + cover edge br…
FBumann May 24, 2026
9b4d7cc
fix(common): only accept string axis names in _named_pandas_to_dataarray
FBumann May 24, 2026
7705156
fix(common): align positional inputs to coords, with clear shape errors
FBumann May 24, 2026
26f3e73
fix(sos): use var.indexes[d] for reformulated bounds; widen _coords_t…
FBumann May 24, 2026
095b510
fix(common): tighten _coords_to_dict to raise on non-pd.Index entries
FBumann May 24, 2026
68c4e09
fix(common): proper MultiIndex support in coords helpers (#729)
FabianHofmann May 27, 2026
a3d6f59
fix: apply coords-as-truth rule to mask in add_variables/add_constrai…
FBumann May 27, 2026
48de61b
refactor: unify as_dataarray; split broadcasting from coords validati…
FBumann May 27, 2026
f766318
test(repr): set .name on MultiIndex coord
FBumann May 27, 2026
280f77c
fix(types): widen _coords_to_dict to Hashable; sort with key=str
FBumann May 27, 2026
570fd6f
refactor(common): clarify coords-entry rules and tighten error labels…
FBumann May 27, 2026
e9a354c
docs(release_notes): restore lost bullets, surface coords breaking ch…
FBumann May 27, 2026
cdfff36
docs(release_notes): condense coords-as-truth entries
FBumann May 27, 2026
a23a24c
Merge branch 'master' into fix/bounds-coords-broadcast
FabianHofmann May 28, 2026
a3e4c6c
fix(common): preserve MultiIndex levels when broadcasting a missing dim
FabianHofmann May 28, 2026
208b45d
feat(common): project pandas inputs onto stacked-MultiIndex coords dims
FabianHofmann Jun 1, 2026
423225c
feat(common): warn on implicit MultiIndex-level projection in arithmetic
FabianHofmann Jun 1, 2026
faecce7
use lax dataarray in matmul
FabianHofmann Jun 1, 2026
6b262f1
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 1, 2026
ff9644a
refactor(common): split DataArray conversion into a 3-rung strictness…
FBumann Jun 2, 2026
a868798
docs(release_notes): surface the MI-projection deprecation and DataAr…
FBumann Jun 2, 2026
9fe3ad2
fix(common): reject unnamed-MultiIndex inputs in strict validation
FabianHofmann Jun 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions doc/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,29 @@ Most users should keep calling ``model.solve(...)``. If you want more control, y
**Deprecations**

* ``Solver.solve_problem``, ``Solver.solve_problem_from_model``, and ``Solver.solve_problem_from_file`` still work but emit a ``DeprecationWarning``. Use ``Solver.from_name(...).solve()`` (or simply ``model.solve(...)``) instead. They will be removed in a future release.
* **Implicit MultiIndex-level projection is deprecated.** Passing an input indexed by a *level* of a stacked-``MultiIndex`` dimension (e.g. per-``period`` bounds onto a ``(period, timestep)`` ``snapshot`` index) emits an ``EvolvingAPIWarning`` — in arithmetic and in ``add_variables`` / ``add_constraints`` — and will raise under the upcoming v1 convention. Project the input onto the dimension explicitly (select with the dimension's level values) to keep current behavior. Affects PyPSA multi-investment models. See Bug Fixes below for details.

**Bug Fixes**

* ``add_variables`` / ``add_constraints``: extends 0.7.0's coords-as-truth rule to ``lower``, ``upper`` and ``mask`` for every bound type and dim order. Pandas ``Series`` / ``DataFrame`` bounds or masks missing a dimension are broadcast to ``coords`` instead of being silently dropped (`#709 <https://github.com/PyPSA/linopy/issues/709>`__); the variable's dimension order always follows ``coords`` (`#706 <https://github.com/PyPSA/linopy/issues/706>`__); bare-tuple coord entries (``coords=[(0, 1, 2)]``) now behave like lists. Mismatched values or extra dims raise ``ValueError`` with a labelled message; sparse-coord masks (formerly a v0.6.3 ``FutureWarning``, #580) raise ``ValueError``, and masks with dims not in the data raise ``ValueError`` instead of ``AssertionError``.
* Pandas inputs whose index names *levels* of a stacked-``MultiIndex`` ``coords`` dimension are now projected onto that dimension: a level subset broadcasts across the others, the full set aligns element-wise. This fixes PyPSA multi-investment arithmetic (e.g. an expression over a ``(period, timestep)`` ``snapshot`` MultiIndex times a ``period``-indexed weighting). In ``add_variables`` / ``add_constraints`` the input must provide a value for every level combination of the MultiIndex or a ``ValueError`` is raised (the error lists the missing combinations). **Implicit level projections are deprecated**: they emit an ``EvolvingAPIWarning`` everywhere — in arithmetic *and* in ``add_variables`` / ``add_constraints`` — and will raise under the upcoming v1 convention. Project the input onto the dimension explicitly (select with the dimension's level values) to keep current behavior. Aligning the full level set with full coverage stays silent. Strict validation also rejects a ``MultiIndex`` input with *unnamed* levels whose combinations don't match ``coords`` (previously a silent bypass, as such inputs can't be projected by level name).
* ``add_piecewise_formulation`` now produces a reproducible dimension order in the broadcast breakpoint array. The previous set-based expansion gave a hash-randomized order that varied between processes.
* SOS constraints on masked variables no longer cause solver-specific failures (Gurobi ``IndexError``, Xpress ``?404 Invalid column number``, LP parse errors, silent set corruption). ``Model.solve()`` and ``Model.to_file()`` now raise a clear ``NotImplementedError`` referring users to `#688 <https://github.com/PyPSA/linopy/issues/688>`__; pass ``reformulate_sos=True`` as a workaround.
* ``Model.solve(..., reformulate_sos=True)`` now actually reformulates SOS constraints even when the solver supports them natively. Previously it was silently ignored with a warning.
* Fix Mosek interface to inspect both the basic and IPM solutions and pick the one with the better status, so that an optimal crossover solution is not discarded when IPM terminates with a (near-)Farkas certificate.

**Breaking Changes**

* ``add_variables`` / ``add_constraints``: the v0.6.3 ``mask`` deprecations (#580) are now hard ``ValueError``\ s; an unnamed ``pd.MultiIndex`` in sequence-form ``coords`` raises ``TypeError`` unless paired with ``dims=[i]``. See Bug Fixes above.
* Sequence-form ``coords`` entries can no longer be ``xarray.DataArray`` objects — they raise ``TypeError``. Pass the underlying index instead: ``variable.indexes[dim]`` (a ``pd.Index``).
* ``available_solvers`` now lists all *installed* solvers, even ones without a working license. If you used it to decide "can I actually solve with X?", switch to ``linopy.licensed_solvers`` or ``SolverClass.license_status()``.
* ``Model.solver_model`` and ``Model.solver_name`` are now read-only properties that delegate to ``model.solver``. You can't reassign them (only ``= None`` is allowed, which closes the solver), and ``solver_name`` is ``None`` before the first solve.
* ``result.solution.primal`` and ``result.solution.dual`` are now ``numpy`` arrays indexed by linopy's integer labels (with ``NaN`` for slots without a value), instead of pandas Series keyed by variable/constraint name. If you accessed them by name, use ``model.variables[name].solution`` (or ``model.constraints[name].dual``) instead.
* Drop Python 3.10 support. Minimum supported version is now Python 3.11.

**Internal**

* ``linopy.common`` provides two DataArray conversion helpers: ``as_dataarray`` (convert only) and ``broadcast_to_coords`` (convert and broadcast against ``coords``). The latter takes ``strict`` (default ``True``): any mismatch with ``coords`` raises, naming ``label`` in the error; ``strict=False`` passes mismatches through for downstream xarray alignment.
* Each ``Solver`` subclass now overrides at most three hooks: ``_build_direct`` (build the native model), ``_run_direct`` (run it), and ``_run_file`` (run the solver on an LP/MPS file). File-only solvers (CBC, GLPK, CPLEX, SCIP, Knitro, COPT, MindOpt) only override ``_run_file``.
* New ``ConstraintLabelIndex`` cached on ``Model.constraints`` (mirrors the existing ``Variables.label_index``); ``ConstraintBase`` gains ``active_labels()`` and a ``range`` property; ``CSRConstraint`` exposes ``coords``.
* ``linopy.common`` gains ``values_to_lookup_array``; the legacy pandas-based helpers ``series_to_lookup_array`` and ``lookup_vals`` are removed.
Expand Down
Loading
Loading