You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

linopy

Package Overview
Dependencies
Maintainers
2
Versions
64
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

linopy - pypi Package Compare versions

Comparing version
0.6.2
to
0.6.3
+2
-5
doc/conf.py

@@ -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

@@ -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(

@@ -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'
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

@@ -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"