linopy
Advanced tools
+2
-5
@@ -16,3 +16,3 @@ # Configuration file for the Sphinx documentation builder. | ||
| # sys.path.insert(0, os.path.abspath('.')) | ||
| import pkg_resources # part of setuptools | ||
| import linopy | ||
@@ -26,8 +26,5 @@ # -- Project information ----------------------------------------------------- | ||
| # The full version, including alpha/beta/rc tags | ||
| version = pkg_resources.get_distribution("linopy").version | ||
| version = linopy.__version__ | ||
| release = "master" if "dev" in version else version | ||
| # For some reason is this needed, otherwise autosummary does fail on RTD but not locally | ||
| import linopy # noqa | ||
| # -- General configuration --------------------------------------------------- | ||
@@ -34,0 +31,0 @@ |
@@ -7,2 +7,10 @@ Release Notes | ||
| Version 0.6.3 | ||
| -------------- | ||
| **Fix Regression** | ||
| * Reinsert broadcasting logic of mask object to be fully compatible with performance improvements in version 0.6.2 using `np.where` instead of `xr.where`. | ||
| Version 0.6.2 | ||
@@ -9,0 +17,0 @@ -------------- |
| Metadata-Version: 2.4 | ||
| Name: linopy | ||
| Version: 0.6.2 | ||
| Version: 0.6.3 | ||
| Summary: Linear optimization with N-D labeled arrays in Python | ||
@@ -30,3 +30,3 @@ Author-email: Fabian Hofmann <fabianmarikhofmann@gmail.com> | ||
| Requires-Dist: dask>=0.18.0 | ||
| Requires-Dist: polars>=1.31 | ||
| Requires-Dist: polars>=1.31.1 | ||
| Requires-Dist: tqdm | ||
@@ -33,0 +33,0 @@ Requires-Dist: deprecation |
@@ -7,3 +7,3 @@ scipy | ||
| dask>=0.18.0 | ||
| polars>=1.31 | ||
| polars>=1.31.1 | ||
| tqdm | ||
@@ -10,0 +10,0 @@ deprecation |
+26
-0
@@ -289,2 +289,28 @@ #!/usr/bin/env python3 | ||
| def broadcast_mask(mask: DataArray, labels: DataArray) -> DataArray: | ||
| """ | ||
| Broadcast a boolean mask to match the shape of labels. | ||
| Ensures that mask dimensions are a subset of labels dimensions, broadcasts | ||
| the mask accordingly, and fills any NaN values (from missing coordinates) | ||
| with False while emitting a FutureWarning. | ||
| """ | ||
| assert set(mask.dims).issubset(labels.dims), ( | ||
| "Dimensions of mask not a subset of resulting labels dimensions." | ||
| ) | ||
| mask = mask.broadcast_like(labels) | ||
| if mask.isnull().any(): | ||
| warn( | ||
| "Mask contains coordinates not covered by the data dimensions. " | ||
| "Missing values will be filled with False (masked out). " | ||
| "In a future version, this will raise an error. " | ||
| "Use mask.reindex() or `linopy.align()` to explicitly handle missing " | ||
| "coordinates.", | ||
| FutureWarning, | ||
| stacklevel=3, | ||
| ) | ||
| mask = mask.fillna(False).astype(bool) | ||
| return mask | ||
| # TODO: rename to to_pandas_dataframe | ||
@@ -291,0 +317,0 @@ def to_dataframe( |
+5
-13
@@ -32,2 +32,3 @@ """ | ||
| best_int, | ||
| broadcast_mask, | ||
| maybe_replace_signs, | ||
@@ -555,2 +556,3 @@ replace_by_map, | ||
| mask = as_dataarray(mask, coords=data.coords, dims=data.dims).astype(bool) | ||
| mask = broadcast_mask(mask, data.labels) | ||
@@ -576,3 +578,2 @@ # Auto-mask based on NaN in bounds (use numpy for speed) | ||
| if mask is not None: | ||
| # Use numpy where for speed (38x faster than xarray where) | ||
| data.labels.values = np.where(mask.values, data.labels.values, -1) | ||
@@ -751,10 +752,4 @@ | ||
| if mask is not None: | ||
| mask = as_dataarray(mask).astype(bool) | ||
| # TODO: simplify | ||
| assert set(mask.dims).issubset(data.dims), ( | ||
| "Dimensions of mask not a subset of resulting labels dimensions." | ||
| ) | ||
| # Broadcast mask to match data shape for correct numpy where behavior | ||
| if mask.shape != data.labels.shape: | ||
| mask, _ = xr.broadcast(mask, data.labels) | ||
| mask = as_dataarray(mask, coords=data.coords, dims=data.dims).astype(bool) | ||
| mask = broadcast_mask(mask, data.labels) | ||
@@ -770,7 +765,5 @@ # Auto-mask based on null expressions or NaN RHS (use numpy for speed) | ||
| coords, dims, rhs_notnull = original_rhs_mask | ||
| # Broadcast RHS mask to match data shape if needed | ||
| if rhs_notnull.shape != auto_mask_values.shape: | ||
| rhs_da = DataArray(rhs_notnull, coords=coords, dims=dims) | ||
| rhs_da, _ = xr.broadcast(rhs_da, data.labels) | ||
| rhs_notnull = rhs_da.values | ||
| rhs_notnull = rhs_da.broadcast_like(data.labels).values | ||
| auto_mask_values = auto_mask_values & rhs_notnull | ||
@@ -793,3 +786,2 @@ auto_mask_arr = DataArray( | ||
| if mask is not None: | ||
| # Use numpy where for speed (38x faster than xarray where) | ||
| data.labels.values = np.where(mask.values, data.labels.values, -1) | ||
@@ -796,0 +788,0 @@ |
@@ -31,5 +31,5 @@ # file generated by setuptools-scm | ||
| __version__ = version = '0.6.2' | ||
| __version_tuple__ = version_tuple = (0, 6, 2) | ||
| __version__ = version = '0.6.3' | ||
| __version_tuple__ = version_tuple = (0, 6, 3) | ||
| __commit_id__ = commit_id = 'gc9f83bbd3' | ||
| __commit_id__ = commit_id = 'g16d6f3264' |
+2
-2
| Metadata-Version: 2.4 | ||
| Name: linopy | ||
| Version: 0.6.2 | ||
| Version: 0.6.3 | ||
| Summary: Linear optimization with N-D labeled arrays in Python | ||
@@ -30,3 +30,3 @@ Author-email: Fabian Hofmann <fabianmarikhofmann@gmail.com> | ||
| Requires-Dist: dask>=0.18.0 | ||
| Requires-Dist: polars>=1.31 | ||
| Requires-Dist: polars>=1.31.1 | ||
| Requires-Dist: tqdm | ||
@@ -33,0 +33,0 @@ Requires-Dist: deprecation |
+1
-1
@@ -36,3 +36,3 @@ [build-system] | ||
| "dask>=0.18.0", | ||
| "polars>=1.31", | ||
| "polars>=1.31.1", | ||
| "tqdm", | ||
@@ -39,0 +39,0 @@ "deprecation", |
@@ -165,2 +165,37 @@ #!/usr/bin/env python3 | ||
| def test_masked_constraints_broadcast() -> None: | ||
| m: Model = Model() | ||
| lower = xr.DataArray(np.zeros((10, 10)), coords=[range(10), range(10)]) | ||
| upper = xr.DataArray(np.ones((10, 10)), coords=[range(10), range(10)]) | ||
| x = m.add_variables(lower, upper) | ||
| y = m.add_variables() | ||
| mask = pd.Series([True] * 5 + [False] * 5) | ||
| m.add_constraints(1 * x + 10 * y, EQUAL, 0, name="bc1", mask=mask) | ||
| assert (m.constraints.labels.bc1[0:5, :] != -1).all() | ||
| assert (m.constraints.labels.bc1[5:10, :] == -1).all() | ||
| mask2 = xr.DataArray([True] * 5 + [False] * 5, dims=["dim_1"]) | ||
| m.add_constraints(1 * x + 10 * y, EQUAL, 0, name="bc2", mask=mask2) | ||
| assert (m.constraints.labels.bc2[:, 0:5] != -1).all() | ||
| assert (m.constraints.labels.bc2[:, 5:10] == -1).all() | ||
| mask3 = xr.DataArray( | ||
| [True, True, False, False, False], | ||
| dims=["dim_0"], | ||
| coords={"dim_0": range(5)}, | ||
| ) | ||
| with pytest.warns(FutureWarning, match="Missing values will be filled"): | ||
| m.add_constraints(1 * x + 10 * y, EQUAL, 0, name="bc3", mask=mask3) | ||
| assert (m.constraints.labels.bc3[0:2, :] != -1).all() | ||
| assert (m.constraints.labels.bc3[2:5, :] == -1).all() | ||
| assert (m.constraints.labels.bc3[5:10, :] == -1).all() | ||
| # Mask with extra dimension not in data should raise | ||
| mask4 = xr.DataArray([True, False], dims=["extra_dim"]) | ||
| with pytest.raises(AssertionError, match="not a subset"): | ||
| m.add_constraints(1 * x + 10 * y, EQUAL, 0, name="bc4", mask=mask4) | ||
| def test_non_aligned_constraints() -> None: | ||
@@ -167,0 +202,0 @@ m: Model = Model() |
@@ -110,2 +110,35 @@ #!/usr/bin/env python3 | ||
| def test_variables_mask_broadcast() -> None: | ||
| m = Model() | ||
| lower = xr.DataArray(np.zeros((10, 10)), coords=[range(10), range(10)]) | ||
| upper = xr.DataArray(np.ones((10, 10)), coords=[range(10), range(10)]) | ||
| mask = pd.Series([True] * 5 + [False] * 5) | ||
| x = m.add_variables(lower, upper, name="x", mask=mask) | ||
| assert (x.labels[0:5, :] != -1).all() | ||
| assert (x.labels[5:10, :] == -1).all() | ||
| mask2 = xr.DataArray([True] * 5 + [False] * 5, dims=["dim_1"]) | ||
| y = m.add_variables(lower, upper, name="y", mask=mask2) | ||
| assert (y.labels[:, 0:5] != -1).all() | ||
| assert (y.labels[:, 5:10] == -1).all() | ||
| mask3 = xr.DataArray( | ||
| [True, True, False, False, False], | ||
| dims=["dim_0"], | ||
| coords={"dim_0": range(5)}, | ||
| ) | ||
| with pytest.warns(FutureWarning, match="Missing values will be filled"): | ||
| z = m.add_variables(lower, upper, name="z", mask=mask3) | ||
| assert (z.labels[0:2, :] != -1).all() | ||
| assert (z.labels[2:5, :] == -1).all() | ||
| assert (z.labels[5:10, :] == -1).all() | ||
| # Mask with extra dimension not in data should raise | ||
| mask4 = xr.DataArray([True, False], dims=["extra_dim"]) | ||
| with pytest.raises(AssertionError, match="not a subset"): | ||
| m.add_variables(lower, upper, name="w", mask=mask4) | ||
| def test_variables_get_name_by_label(m: Model) -> None: | ||
@@ -112,0 +145,0 @@ assert m.variables.get_name_by_label(4) == "x" |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
2673334
0.13%21291
0.32%