feat(csr): constraint and export boundaries of the sparse path (PR 2 of #972) - #974
Conversation
2aa83b1 to
4e7eb3a
Compare
…e; Objective owns its name (#967)
…build, join for constant rhs, 0-d merge
…he detached mutable() copy
…ression, named setter in FROZEN_MUTATIONS
4e7eb3a to
8f27931
Compare
Merging this PR will regress 1 benchmark
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | test_to_lp[nodal_balance-severity=0] |
3.3 MB | 3.7 MB | -11.58% |
| ⚡ | test_to_lp[sparse_network-n=10] |
1,337.8 KB | 718.2 KB | +86.27% |
| ⚡ | test_build[nodal_balance_sparse-severity=0] |
1.5 MB | 1.3 MB | +13.94% |
| ⚡ | test_build[nodal_balance_sparse-severity=100] |
1.5 MB | 1.3 MB | +13.94% |
| ⚡ | test_build[nodal_balance_sparse-severity=50] |
1.5 MB | 1.3 MB | +13.94% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing feat/csr-boundaries (6785da3) with master (38fbd4a)
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. ↩
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. |
… polars round trip
…ys, skip unit scaling
CSRLinearExpression.live_terms is the single sparse term filter, shared by _csr_terms and LinearExpression.linear_terms. QuadraticExpression aligns factors with coeffs by dim name. Objective.linear_terms delegates.
Note
The following content was generated by AI.
PR 2 ("boundaries") of the sparse/CSR plan in #972, stacked on #973 (targets
feat/csr-expression-kernel; retarget tomasteronce #973 merges).Closes #941, closes #963, closes #967, closes #968, closes #970.
What changes
LinearExpression.flat/to_polarson a CSR-backed expression are built from the CSR store (numpy arrays, no dense rectangle, no pyarrow dependency). Rows equal the dense path: absent cells and zero coefficients dropped, same columns and dtypes.add_objective,matrices.c, LP/MPS export, netcdf write,Model.copy, persistent snapshots and direct solver APIs.matrices.cand the persistent snapshot share one reader,Objective.linear_terms(). Quadratic objectives stay dense.CSRConstraintkeeps the fullGridincl. aux coords (scalar too) throughfrom_csr,Constraint.freeze(),to_dense()/mutable()and netcdf IO.loc,update,soften,from_ruleand thecoeffs/vars/signsetters onCSRConstraintraise an explanatoryAttributeErrornaming.mutable()(orConstraint.from_rule+.freeze()), consistent with the existingrhs/lhs/scalingsetters, via one helper.lhs - rhs <= 0), so a CSR-backed lhs stays aCSRConstraint; a constant rhs with an explicitjoin=takes the same route, so sparse matches dense for every join mode.mask=also applies sparsely to aCSRConstraintbuilt beforehand; the mask logic now lives once inCSRConstraint.masked. The dense rebuild inadd_constraints(freeze=False, chunked model,penalty=) now emits one densify notice naming its cause.Decisions
Objective.CSRLinearExpressionstays attrs-free.Objective.nameis the fixed"objective",Objective.attrsreturns{"name": ...}, the setter writesattrs["name"]only on a dense expression, and the netcdf writer adds it.softenrewrites the constraint in place, which a frozen constraint does not allow. This is a missing feature, not a technical limit: a native sparse soften (one slack term per active row) is filed as Native sparse soften for frozen constraints (penalty= with freeze=True) #975. Thesoftenerror on a frozen constraint now points tofreeze=Falseinstead of.mutable(), sincesoftenrefuses the detached copy that.mutable()returns.master. Edits in theCSRConstraintmethods it touches are kept small and local.Also fixed along the way
read_netcdffailed for a whole model when a frozen constraint had a-in a dim, MultiIndex level or aux coord name (the reader splits variable names on-).CSRConstraintcoordinate metadata is now stored under positional names with the real names as attributes. Files written by earlier versions still load; files written now need this version to read the frozen constraints.CSRLinearExpression.from_densecrashed on a 0-d expression, e.g. adding a scalar term to a fully summed sparse expression.Known gaps (not in scope)
x <= sparse_expr) still densifies.Objective.data/vars/coeffs,Model.remove_variables,calculate_block_maps,objectiverange.sparse=and docs (Make the sparse path observable and controllable (.is_sparse, densify warning, per-call sparse=) #969 part 3, Document the sparse expression path (CSRLinearExpression, sparse=True, sparse_groupby) #971) are PR 3.Verification
uv run pytest -q --ignore=test/remote -p no:cacheprovider -k "not xpress": 7061 passed, 1073 skippeduv run --isolated --python 3.11 --extra dev pytest test/test_csr.py -q -p no:cacheprovider: 551 passed, 547 skipped (legacy variants of v1-only tests)ruff check,ruff format,mypy linopy: clean