Skip to content

Commit 2d69959

Browse files
Replace pandas-based LP file writing with polars implementation (#496)
* Replace pandas-based LP file writing with polars implementation - Remove pandas-based LP writing functions and replace with polars versions - Rename polars functions to remove '_polars' suffix for consistent API - Create separate get_printers_scalar() for non-LP functions (highspy, gurobi, mosek) - Update get_printers() to handle polars dataframes for LP writing - Consolidate "lp" and "lp-polars" io_api options to use same implementation - Remove unused imports and cleanup handle_batch function * fix: restore NaN validation in polars-based variable bounds checking The polars migration broke NaN validation because check_has_nulls_polars only checked for null values, not NaN values. In polars, these are distinct concepts. This fix enhances the validation to detect both null and NaN values in numeric columns while avoiding type errors on non-numeric columns. Fixes failing tests in test_inconsistency_checks.py that expected ValueError to be raised when variables have NaN bounds.
1 parent 12b0da3 commit 2d69959

File tree

3 files changed

+63
-428
lines changed

3 files changed

+63
-428
lines changed

doc/release_notes.rst

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
Release Notes
22
=============
33

4-
.. Upcoming Version
5-
.. ----------------
6-
.. * Improved constraint equality check in `linopy.testing.assert_conequal` to less strict optionally
7-
.. * Minor bugfix for multiplying variables with numpy type constants
4+
Upcoming Version
5+
----------------
6+
7+
* Replace pandas-based LP file writing with polars implementation for significantly improved performance on large models
8+
* Consolidate "lp" and "lp-polars" io_api options - both now use the optimized polars backend
9+
* Reduced memory usage and faster file I/O operations when exporting models to LP format
10+
* Improved constraint equality check in `linopy.testing.assert_conequal` to less strict optionally
11+
* Minor bugfix for multiplying variables with numpy type constants
812

913
Version 0.5.6
1014
--------------

linopy/common.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -363,22 +363,35 @@ def to_polars(ds: Dataset, **kwargs: Any) -> pl.DataFrame:
363363

364364
def check_has_nulls_polars(df: pl.DataFrame, name: str = "") -> None:
365365
"""
366-
Checks if the given DataFrame contains any null values and raises a ValueError if it does.
366+
Checks if the given DataFrame contains any null or NaN values and raises a ValueError if it does.
367367
368368
Args:
369369
----
370-
df (pl.DataFrame): The DataFrame to check for null values.
370+
df (pl.DataFrame): The DataFrame to check for null or NaN values.
371371
name (str): The name of the data container being checked.
372372
373373
Raises:
374374
------
375-
ValueError: If the DataFrame contains null values,
376-
a ValueError is raised with a message indicating the name of the constraint and the fields containing null values.
375+
ValueError: If the DataFrame contains null or NaN values,
376+
a ValueError is raised with a message indicating the name of the constraint and the fields containing null/NaN values.
377377
"""
378+
# Check for null values in all columns
378379
has_nulls = df.select(pl.col("*").is_null().any())
379380
null_columns = [col for col in has_nulls.columns if has_nulls[col][0]]
380-
if null_columns:
381-
raise ValueError(f"{name} contains nan's in field(s) {null_columns}")
381+
382+
# Check for NaN values only in numeric columns (avoid enum/categorical columns)
383+
numeric_cols = [
384+
col for col, dtype in zip(df.columns, df.dtypes) if dtype.is_numeric()
385+
]
386+
387+
nan_columns = []
388+
if numeric_cols:
389+
has_nans = df.select(pl.col(numeric_cols).is_nan().any())
390+
nan_columns = [col for col in has_nans.columns if has_nans[col][0]]
391+
392+
invalid_columns = list(set(null_columns + nan_columns))
393+
if invalid_columns:
394+
raise ValueError(f"{name} contains nan's in field(s) {invalid_columns}")
382395

383396

384397
def filter_nulls_polars(df: pl.DataFrame) -> pl.DataFrame:

0 commit comments

Comments
 (0)