Repository navigation
feat(csr): keep CSR backing through metadata, constant arithmetic, sum/groupby and selection - #973
Conversation
Selection runs on the grid's row numbers with xarray semantics, rows are gathered by CSRLinearExpression.taken; Grid.aux carries scalar coords. Cross-grid merges with aux coords stay sparse.
…l on joins - warn_outside_linopy: shared frame-skipping warning, dynamic on <3.12 - each densification emits exactly one notice carrying its reason - aggregated keeps a float constant on empty grids - joins with a constant fill only values; aux coords of new labels are NaN
…ed constraint once Keyword-only flags on _combined_with_constant; sparse_and_dense test helper and one parametrized absent-cells test.
…roupby obj argument and constant-join aux fix
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 7.86%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | test_to_lp[sparse_network-n=10] |
718.3 KB | 1,343.2 KB | -46.52% |
| ⚡ | test_to_lp[qp-n=1000] |
2.6 MB | 2 MB | +29.81% |
| ⚡ | test_to_lp[nodal_balance-severity=50] |
3.7 MB | 3.3 MB | +12.69% |
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-expression-kernel (799d514) with master (82e7993)
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. ↩
…draticExpression in test op table
Closes #962, closes #964, closes #965, closes #966. Part of #972 (PR 1 of 3). Partially addresses #969 and #970.
Note
The following content was generated by AI.
Changes proposed in this Pull Request
A CSR-backed
LinearExpression(v1 only, fromgroupby(...).sum(sparse=True)oroptions["sparse_groupby"]) now keeps its sparse backing through the common model-building operations instead of silently expanding to the dense_termrectangle. Sparse results equal the dense ones cell by cell (values, coordinates incl. auxiliary coordinates, absent cells); only the term layout may differ.Observability (#962, #969 parts 1+2)
repr,dims,shape,sizes,coords,indexes,const,isnull()are served from the CSR store. The repr prints only the displayed rows and showsLinearExpression (sparse).LinearExpression.is_sparse.options["warn_on_densify"](default off): onePerformanceWarningper densification, naming the reason and pointing at user code. A newwarn_outside_linopyhelper is shared withwarn_legacy.Operations that keep the backing
*,/,+,-with DataArray / Series / ndarray, aligned through the same_broadcast_and_alignas the dense path (Keep CSR backing for elementwise ops with non-scalar constants #965).sum(dim)and chainedgroupby().sum()via direct row aggregation (Sparse sum(dim=...) and chained groupby on CSR-backed expressions #964).where,sel,isel,loc,[]via one selection path on grid row numbers, so semantics and errors match xarray (Keep CSR backing through where/mask/sel/isel/loc #966).add_constraints(..., mask=, freeze=True)with an expression lhs stays sparse (mask part of Widen the CSRConstraint fast path in Model.add_constraints #970).mergeof differing grids with auxiliary coordinates.Operations that still densify (operands adding new dims, MultiIndex, callables, expression
otherinwhere, …) do so explicitly and report it underwarn_on_densify.Zero policy. All operations keep explicit zeros, so sparse and dense give the same terms up to order; only
@/dotprunes them (unchanged from #961). Documented in thelinopy/csr.pymodule docstring.Bug fixes (dense and sparse)
mergewithjoin="outer"/"left", and arithmetic with a constant on an outer join, raised xarray'sMergeError(or, on the sparse path, wrote0) when only one operand carried an auxiliary coordinate. A scalar reindex/concat fill was also applied to auxiliary coordinates; the fill is now restricted to the data values. Affects legacy semantics too.Objective.selwrappedLinearExpression.seland failed on a quadratic objective.Internal
Grid.aux(scalar coordinates included).LinearExpressionGroupbystores its source asobj(wasdata), so it can group a CSR-backed expression without densifying.Not in this PR (see #972): aux coords on
CSRConstraint(#941),CSRConstraintAPI errors (#963), expression rhs andmask=on a pre-builtCSRConstraint(#970), sparse objective /flat/to_polars(#967, #968), per-callsparse=(#969 part 3), docs (#971).Testing
test/test_csr.pyextended with sparse-vs-dense parity tests for every operation above, asparse_and_densehelper, and a "backing kept" assertion; fallbacks assert exactly one densify notice with its reason.test/test_legacy_violations.py, over join and operand order.test/remoteand Xpress, which fails locally on a licence limit only): 7002 passed, 1018 skipped.test_csr.py+test_legacy_violations.pyon Python 3.11: 848 passed.ededa153,bb22c8a2,63ebfa97.Checklist
AGENTS.md).doc.doc/release_notes.rstof the upcoming release is included.