refac(csr): dedicated csr module, shared Grid, aligned dense/sparse names - #944
Conversation
…ames - rename CSRExpression -> CSRLinearExpression; sparse_expression.py -> csr.py - make csr.py a pure data module; move _try_csr_merge next to merge - move _csr slot from BaseExpression to LinearExpression - add shared Grid value object backing both CSR types - unify conversions (from_dense/to_dense, added) and share csr_to_term_arrays/csr_nterm - fix: preserve coefficient dtype when reconstructing frozen constraints
Build cost — v1 vs legacyv1 build peak & time relative to legacy, on this commit — not a comparison against master (that is CodSpeed).
Full table (time + peak, mean)📊 Interactive plots + CSV: download the semantics-report-v1-vs-legacy artifact from this run. Report-only · not a gate · refreshed on every push · obsolete once legacy is dropped. |
Merging this PR will degrade performance by 16.87%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | test_to_lp[qp-n=1000] |
2 MB | 2.6 MB | -22.88% |
| ❌ | test_to_lp[nodal_balance-severity=50] |
3.3 MB | 3.7 MB | -10.39% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing refac/csr-module-and-grid (d76b6c9) with master (c25f779)
Footnotes
-
181 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
Move grid alignment onto Grid (is_unique, reordered, with_indexes, combined) so no module pokes Grid.indexes. CSRLinearExpression.to_dense now returns a LinearExpression, mirroring CSRConstraint.to_dense. Fix transposed-grid merge to build a real Grid via reordered (was passing a dict to reindexed); add a regression test for the default-join path.
Moving _csr onto LinearExpression left merge()/.add() results (typed BaseExpression / the Linear|Quadratic union) without _csr under mypy. Pass cls=LinearExpression on the merge calls and narrow the .add case with isinstance, so `mypy .` passes. Test-only, no runtime change.
We needed a better structure for all the CSR data containers. This improve the module's scopes and reduced duplication of code.
Note
The description below was generated by AI.
Changes proposed in this Pull Request
Structural refactor of the CSR (compressed sparse row) code. No change to public solver behaviour; the sparse groupby/freeze features work as before.
Module and naming
LinearExpressionfromlinopy/sparse_expression.pytolinopy/csr.py.CSRExpressiontoCSRLinearExpression(it encodesA @ x + c; there is no quadratic analog).from_dense/to_dense(wasfrom_expression/materializeon the expression,from_mutableon the constraint).Constraint.freeze()andCSRConstraint.mutable()are unchanged.addedto match the participle convention (reindexed,renamed,filled,scaled).Boundaries
linopy/csr.pycarries no top-level import ofLinearExpression,ConstraintorModel. The single exception to the leaf boundary isCSRLinearExpression.to_dense, which wraps its expanded dataset in aLinearExpression(its dense equivalent, mirroringCSRConstraint.to_dense -> Constraint) via a function-local import._try_csr_mergenext tomergeinexpressions.py._csrslot and_from_csroffBaseExpressionontoLinearExpression, soQuadraticExpressionno longer carries an unused slot.Shared code
Gridvalue object (dims, indexes, shape, size, strides, indexer, rename, equality) shared byCSRLinearExpressionandCSRConstraint, replacing the two divergent grid representations (grid_dims+indexesdict vslist[pd.Index]).csr_to_term_arrays(CSR to padded term arrays) andcsr_nterm(widest row), used by bothto_denseand the constraint reconstruction.Fix
csr_to_term_arraysnow preserves the coefficient dtype. Reconstructing a frozen constraint previously widenedfloat32coefficients tofloat64. Added a regression test.Checklist
AGENTS.md).doc.doc/release_notes.rstof the upcoming release is included.