From ec6ad9e379c7e9d8f35fcfceeb6a0a2d8fbeea21 Mon Sep 17 00:00:00 2001 From: Mayk Thewessen Date: Fri, 13 Mar 2026 22:23:58 +0100 Subject: [PATCH 1/2] perf: use numpy array lookup for solution unpacking Convert the primal/dual pandas Series to a dense numpy lookup array before the per-variable/per-constraint unpacking loop. This replaces pandas indexing (sol[idx].values) with direct numpy array indexing (sol_arr[idx]), avoiding pandas overhead per variable type. The loop over variable/constraint types still exists (needed to set each variable's .solution xr.DataArray), but the inner indexing operation is now pure numpy instead of pandas Series.__getitem__. Co-Authored-By: Claude Opus 4.6 --- linopy/model.py | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/linopy/model.py b/linopy/model.py index 54334411..cd9a2215 100644 --- a/linopy/model.py +++ b/linopy/model.py @@ -1572,26 +1572,42 @@ def solve( sol = set_int_index(sol) sol.loc[-1] = nan + # Convert to numpy array for fast label-based lookup. + # Labels are integers; build a dense lookup array so each + # variable can index directly without pandas overhead. + sol_max_idx = max(sol.index.max(), 0) + sol_arr = np.full(sol_max_idx + 1, nan) + sol_arr[sol.index[sol.index >= 0]] = sol.values[sol.index >= 0] + for name, var in self.variables.items(): idx = np.ravel(var.labels) - try: - vals = sol[idx].values.reshape(var.labels.shape) - except KeyError: - vals = sol.reindex(idx).values.reshape(var.labels.shape) - var.solution = xr.DataArray(vals, var.coords) + # Use numpy indexing: labels ≥ 0 map to sol_arr, -1 maps to NaN + safe_idx = np.clip(idx, 0, sol_max_idx) + vals = sol_arr[safe_idx] + vals[idx < 0] = nan + var.solution = xr.DataArray( + vals.reshape(var.labels.shape), var.coords + ) if not result.solution.dual.empty: dual = result.solution.dual.copy() dual = set_int_index(dual) dual.loc[-1] = nan + dual_max_idx = max(dual.index.max(), 0) + dual_arr = np.full(dual_max_idx + 1, nan) + dual_arr[dual.index[dual.index >= 0]] = dual.values[ + dual.index >= 0 + ] + for name, con in self.constraints.items(): idx = np.ravel(con.labels) - try: - vals = dual[idx].values.reshape(con.labels.shape) - except KeyError: - vals = dual.reindex(idx).values.reshape(con.labels.shape) - con.dual = xr.DataArray(vals, con.labels.coords) + safe_idx = np.clip(idx, 0, dual_max_idx) + vals = dual_arr[safe_idx] + vals[idx < 0] = nan + con.dual = xr.DataArray( + vals.reshape(con.labels.shape), con.labels.coords + ) return result.status.status.value, result.status.termination_condition.value finally: From 4e0234726249fa003858f09120a4bbeaefce8b5a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 21:24:24 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- linopy/model.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/linopy/model.py b/linopy/model.py index cd9a2215..85a7e9a3 100644 --- a/linopy/model.py +++ b/linopy/model.py @@ -1585,9 +1585,7 @@ def solve( safe_idx = np.clip(idx, 0, sol_max_idx) vals = sol_arr[safe_idx] vals[idx < 0] = nan - var.solution = xr.DataArray( - vals.reshape(var.labels.shape), var.coords - ) + var.solution = xr.DataArray(vals.reshape(var.labels.shape), var.coords) if not result.solution.dual.empty: dual = result.solution.dual.copy() @@ -1596,9 +1594,7 @@ def solve( dual_max_idx = max(dual.index.max(), 0) dual_arr = np.full(dual_max_idx + 1, nan) - dual_arr[dual.index[dual.index >= 0]] = dual.values[ - dual.index >= 0 - ] + dual_arr[dual.index[dual.index >= 0]] = dual.values[dual.index >= 0] for name, con in self.constraints.items(): idx = np.ravel(con.labels)