Describe the feature you'd like to see
JIT-compile the sparse matrix assembly in matrices.py using Numba to reduce model build time by 3-4x.
Profiling in #271 (PyOptInterface benchmarks) showed that matrix construction — converting labeled xarray variables/constraints into sparse COO matrices for solver handoff — accounts for 55% of total solve time (8s out of 14.5s). The solver itself is often faster than the formulation layer.
For production workloads (120+ buses, 8760h, scenario sweeps), the model building step is the dominant bottleneck. This is especially painful for iterative workflows like MGA, rolling horizon, and sensitivity analysis where the model is rebuilt many times.
Motivation
| Model Size |
Current Build Time |
Estimated with Numba |
Improvement |
| 50 buses, 8760h |
~5s |
~1.5s |
3x |
| 120 buses, 8760h |
~15s |
~4s |
3-4x |
| 500 buses (PyPSA-Eur scale), 8760h |
~60s+ |
~15s |
4x |
Estimates are based on typical Numba JIT speedups for sparse matrix assembly in similar scientific computing workloads (scipy sparse construction, finite element assembly).
Why this matters more now
Proposed implementation
Target: MatrixAccessor in matrices.py
The core hot path is the loop that constructs COO triplets (row, col, data) from labeled xarray DataArrays. This is pure numerical work operating on integer indices and float coefficients — ideal for Numba.
Steps
- Profile
matrices.py to isolate the specific functions where time is spent (COO triplet construction, index alignment, coefficient extraction)
- Extract the inner numerical loops into standalone functions with pure NumPy array signatures (no xarray/pandas inside the hot path)
- Apply
@numba.njit(cache=True) to the extracted functions — this compiles them to machine code with zero Python interpreter overhead
- Parallel variant using
@numba.njit(parallel=True) with numba.prange for the outer loop over constraints (each constraint's matrix contribution is independent)
- Fallback path for when Numba is not installed — keep current pure-Python implementation as default, use Numba when available
- Benchmark on standard test cases (PyPSA-Eur 37-bus, 128-bus, 256-bus electricity models)
Example pattern
import numba
@numba.njit(cache=True)
def _build_coo_triplets(var_labels, coeff_data, row_offsets, col_offsets):
"""Assemble COO sparse matrix entries from constraint coefficients.
Pure numerical loop — no Python objects, no xarray, no pandas.
"""
n_entries = len(var_labels)
rows = np.empty(n_entries, dtype=np.int64)
cols = np.empty(n_entries, dtype=np.int64)
data = np.empty(n_entries, dtype=np.float64)
for i in range(n_entries):
rows[i] = row_offsets[var_labels[i]]
cols[i] = col_offsets[var_labels[i]]
data[i] = coeff_data[i]
return rows, cols, data
Dependency consideration
Numba is already standard in the scientific Python ecosystem (depends only on NumPy + LLVM). It could be an optional dependency:
try:
from linopy._numba_matrices import _build_coo_triplets
except ImportError:
from linopy._matrices_fallback import _build_coo_triplets
Related issues
Describe the feature you'd like to see
JIT-compile the sparse matrix assembly in
matrices.pyusing Numba to reduce model build time by 3-4x.Profiling in #271 (PyOptInterface benchmarks) showed that matrix construction — converting labeled xarray variables/constraints into sparse COO matrices for solver handoff — accounts for 55% of total solve time (8s out of 14.5s). The solver itself is often faster than the formulation layer.
For production workloads (120+ buses, 8760h, scenario sweeps), the model building step is the dominant bottleneck. This is especially painful for iterative workflows like MGA, rolling horizon, and sensitivity analysis where the model is rebuilt many times.
Motivation
Estimates are based on typical Numba JIT speedups for sparse matrix assembly in similar scientific computing workloads (scipy sparse construction, finite element assembly).
Why this matters more now
Proposed implementation
Target:
MatrixAccessorinmatrices.pyThe core hot path is the loop that constructs COO triplets
(row, col, data)from labeled xarray DataArrays. This is pure numerical work operating on integer indices and float coefficients — ideal for Numba.Steps
matrices.pyto isolate the specific functions where time is spent (COO triplet construction, index alignment, coefficient extraction)@numba.njit(cache=True)to the extracted functions — this compiles them to machine code with zero Python interpreter overhead@numba.njit(parallel=True)withnumba.prangefor the outer loop over constraints (each constraint's matrix contribution is independent)Example pattern
Dependency consideration
Numba is already standard in the scientific Python ecosystem (depends only on NumPy + LLVM). It could be an optional dependency:
Related issues