Skip to content

Commit df97fe0

Browse files
fix: xarray new rolling namespace (#432)
* fix: xarray new namespace fix: assign copt as solver only if env can be solved * fix: ruff complaining about unused variables * fix: remove doc test from hard imported xarray doc * fix type annotations * fix type annotations * follow up * fix: version comparison for highspy * follow up
1 parent 639f126 commit df97fe0

File tree

4 files changed

+80
-12
lines changed

4 files changed

+80
-12
lines changed

linopy/common.py

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import pandas as pd
2020
import polars as pl
2121
from numpy import arange, signedinteger
22-
from pandas.util._decorators import doc
2322
from xarray import DataArray, Dataset, apply_ufunc, broadcast
2423
from xarray import align as xr_align
2524
from xarray.core import dtypes, indexing
@@ -990,20 +989,73 @@ def check_common_keys_values(list_of_dicts: list[dict[str, Any]]) -> bool:
990989
return all(len({d[k] for d in list_of_dicts if k in d}) == 1 for k in common_keys)
991990

992991

993-
@doc(xr_align)
994992
def align(
995993
*objects: LinearExpression | Variable | T_Alignable,
996994
join: JoinOptions = "inner",
997995
copy: bool = True,
998-
indexes=None,
996+
indexes: Any = None,
999997
exclude: str | Iterable[Hashable] = frozenset(),
1000-
fill_value=dtypes.NA,
998+
fill_value: Any = dtypes.NA,
1001999
) -> tuple[LinearExpression | Variable | T_Alignable, ...]:
1000+
"""
1001+
Given any number of Variables, Expressions, Dataset and/or DataArray objects,
1002+
returns new objects with aligned indexes and dimension sizes.
1003+
1004+
Array from the aligned objects are suitable as input to mathematical
1005+
operators, because along each dimension they have the same index and size.
1006+
1007+
Missing values (if ``join != 'inner'``) are filled with ``fill_value``.
1008+
The default fill value is NaN.
1009+
1010+
This functions essentially wraps the xarray function
1011+
:py:func:`xarray.align`.
1012+
1013+
Parameters
1014+
----------
1015+
*objects : Variable, LinearExpression, Dataset or DataArray
1016+
Objects to align.
1017+
join : {"outer", "inner", "left", "right", "exact", "override"}, optional
1018+
Method for joining the indexes of the passed objects along each
1019+
dimension:
1020+
1021+
- "outer": use the union of object indexes
1022+
- "inner": use the intersection of object indexes
1023+
- "left": use indexes from the first object with each dimension
1024+
- "right": use indexes from the last object with each dimension
1025+
- "exact": instead of aligning, raise `ValueError` when indexes to be
1026+
aligned are not equal
1027+
- "override": if indexes are of same size, rewrite indexes to be
1028+
those of the first object with that dimension. Indexes for the same
1029+
dimension must have the same size in all objects.
1030+
1031+
copy : bool, default: True
1032+
If ``copy=True``, data in the return values is always copied. If
1033+
``copy=False`` and reindexing is unnecessary, or can be performed with
1034+
only slice operations, then the output may share memory with the input.
1035+
In either case, new xarray objects are always returned.
1036+
indexes : dict-like, optional
1037+
Any indexes explicitly provided with the `indexes` argument should be
1038+
used in preference to the aligned indexes.
1039+
exclude : str, iterable of hashable or None, optional
1040+
Dimensions that must be excluded from alignment
1041+
fill_value : scalar or dict-like, optional
1042+
Value to use for newly missing values. If a dict-like, maps
1043+
variable names to fill values. Use a data array's name to
1044+
refer to its values.
1045+
1046+
Returns
1047+
-------
1048+
aligned : tuple of DataArray or Dataset
1049+
Tuple of objects with the same type as `*objects` with aligned
1050+
coordinates.
1051+
1052+
1053+
"""
10021054
from linopy.expressions import LinearExpression
10031055
from linopy.variables import Variable
10041056

1005-
finisher = []
1006-
das = []
1057+
finisher: list[partial[Any] | Callable[[Any], Any]] = []
1058+
das: list[Any] = []
10071059
for obj in objects:
10081060
if isinstance(obj, LinearExpression):
10091061
finisher.append(partial(obj.__class__, model=obj.model))

linopy/expressions.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,23 @@
2424
import scipy
2525
import xarray as xr
2626
import xarray.core.groupby
27-
import xarray.core.rolling
2827
from numpy import array, nan, ndarray
2928
from pandas.core.frame import DataFrame
3029
from pandas.core.series import Series
3130
from scipy.sparse import csc_matrix
3231
from xarray import Coordinates, DataArray, Dataset, IndexVariable
3332
from xarray.core.coordinates import DataArrayCoordinates, DatasetCoordinates
3433
from xarray.core.indexes import Indexes
35-
from xarray.core.rolling import DatasetRolling
3634
from xarray.core.utils import Frozen
3735

36+
try:
37+
# resolve breaking change in xarray 2025.03.0
38+
import xarray.computation.rolling
39+
from xarray.computation.rolling import DatasetRolling
40+
except ImportError:
41+
import xarray.core.rolling
42+
from xarray.core.rolling import DatasetRolling # type: ignore
43+
3844
from linopy import constraints, variables
3945
from linopy.common import (
4046
EmptyDeprecationWrapper,

linopy/solvers.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import numpy as np
2222
import pandas as pd
23+
from packaging.version import parse as parse_version
2324

2425
from linopy.constants import (
2526
Result,
@@ -62,7 +63,8 @@
6263
available_solvers.append("highs")
6364
from importlib.metadata import version
6465

65-
if version("highspy") < "1.7.1":
66+
if parse_version(version("highspy")) < parse_version("1.7.1"):
67+
# Fallback if parse_version is not available or version string is invalid
6668
_new_highspy_mps_layout = False
6769
else:
6870
_new_highspy_mps_layout = True
@@ -111,7 +113,11 @@
111113
with contextlib.suppress(ModuleNotFoundError):
112114
import coptpy
113115

114-
available_solvers.append("copt")
116+
try:
117+
coptpy.Envr()
118+
available_solvers.append("copt")
119+
except coptpy.CoptError:
120+
pass
115121

116122
quadratic_solvers = [s for s in QUADRATIC_SOLVERS if s in available_solvers]
117123
logger = logging.getLogger(__name__)

test/test_common.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from xarray import DataArray
1414
from xarray.testing.assertions import assert_equal
1515

16-
from linopy import Model, Variable
16+
from linopy import LinearExpression, Variable
1717
from linopy.common import (
1818
align,
1919
as_dataarray,
@@ -651,7 +651,7 @@ def test_get_dims_with_index_levels() -> None:
651651
assert get_dims_with_index_levels(ds5) == []
652652

653653

654-
def test_align(m: Model, x: Variable, u: Variable) -> None:
654+
def test_align(x: Variable, u: Variable) -> None: # noqa: F811
655655
alpha = xr.DataArray([1, 2], [[1, 2]])
656656
beta = xr.DataArray(
657657
[1, 2, 3],
@@ -667,18 +667,21 @@ def test_align(m: Model, x: Variable, u: Variable) -> None:
667667

668668
# inner join
669669
x_obs, alpha_obs = align(x, alpha)
670+
assert isinstance(x_obs, Variable)
670671
assert x_obs.shape == alpha_obs.shape == (1,)
671672
assert_varequal(x_obs, x.loc[[1]])
672673

673674
# left-join
674675
x_obs, alpha_obs = align(x, alpha, join="left")
675676
assert x_obs.shape == alpha_obs.shape == (2,)
677+
assert isinstance(x_obs, Variable)
676678
assert_varequal(x_obs, x)
677679
assert_equal(alpha_obs, DataArray([np.nan, 1], [[0, 1]]))
678680

679681
# multiindex
680682
beta_obs, u_obs = align(beta, u)
681683
assert u_obs.shape == beta_obs.shape == (2,)
684+
assert isinstance(u_obs, Variable)
682685
assert_varequal(u_obs, u.loc[[(1, "b"), (2, "b")]])
683686
assert_equal(beta_obs, beta.loc[[(1, "b"), (2, "b")]])
684687

@@ -687,4 +690,5 @@ def test_align(m: Model, x: Variable, u: Variable) -> None:
687690
x_obs, expr_obs, alpha_obs = align(x, expr, alpha)
688691
assert x_obs.shape == alpha_obs.shape == (1,)
689692
assert expr_obs.shape == (1, 1) # _term dim
693+
assert isinstance(expr_obs, LinearExpression)
690694
assert_linequal(expr_obs, expr.loc[[1]])

0 commit comments

Comments
 (0)