You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Tracking issue for the CSR follow-ups after #961 (umbrella #756 is closed). Each child names a place where a CSR-backed expression silently densifies, or a gap in the frozen-constraint API. This issue fixes the order in which to address them.
Key observations
LinearExpression.data (linopy/expressions.py:2358) is where a CSR-backed expression densifies on read. Other paths drop the backing without passing through it: @ returns dense when the input was dense and sparse_groupby is off (expressions.py:2609), CSRConstraint.to_dense()/mutable(), the rebuild at model.py:1345, and the return None fallbacks in _sparse_matmul, _try_csr_merge, _aligned and csr_rhs, which lose the reason.
Zero policy on the sparse path: every operation keeps explicit zeros (merge, scaling, sum, groupby, selection), so sparse and dense give the same terms up to order; only @/dot prunes them. Cell activeness is carried by const alone. Export culls zeros regardless; Zero culling forces model rebuilding #925 is an export/persistent-diff question and does not block this series.
Plan
Four pull requests, grouped by shared design rather than one per issue:
Serve shape/sizes/coords/dims/isnull from the CSR store. .is_sparse, repr marker. Opt-in warn_on_densify through one _densify_notice(reason) helper, called at data, at each return None fallback and in CSRConstraint.to_dense. data stays a one-way conversion (no cached dense copy next to _csr, the setters would make it stale). Move aux coords into Grid.
sum(dim) by merging rows directly (CSRLinearExpression.aggregated), keeping explicit zeros. Chained groupby from a CSR source; fix the gate at expressions.py:613 to use coord_dims instead of self.data.dims.
Aux coords on CSRConstraint (trivial once in Grid), incl. netcdf round trip (io.py:1260). Explanatory errors for loc/update/from_rule naming .mutable(). Expression rhs moved to lhs.
flat/to_polars from the CSR store, modelled on CSRConstraint.to_polars. Sparse objective across objective.py, matrices.py, io.py, persistent/diff.py; decide where the objective name lives (CSRLinearExpression has no attrs).
One read-only model key instead of the three opt-ins (semantics="v1", sparse=/sparse_groupby, freeze_constraints), as proposed in #976 (under discussion). Replaces the earlier plan of per-call sparse= on @/dot/merge; removes the coupling of @ to options["sparse_groupby"].
Tests extend the existing helpers in test/test_csr.py (assert_frozen_equal, assert_terms_equal, assert_cells_equal, assert_contracted_equal) with a "backing kept" assertion instead of adding a new fixture.
Note
The following content was generated by AI.
Describe the feature you'd like to see
Tracking issue for the CSR follow-ups after #961 (umbrella #756 is closed). Each child names a place where a CSR-backed expression silently densifies, or a gap in the frozen-constraint API. This issue fixes the order in which to address them.
Key observations
LinearExpression.data(linopy/expressions.py:2358) is where a CSR-backed expression densifies on read. Other paths drop the backing without passing through it:@returns dense when the input was dense andsparse_groupbyis off (expressions.py:2609),CSRConstraint.to_dense()/mutable(), the rebuild atmodel.py:1345, and thereturn Nonefallbacks in_sparse_matmul,_try_csr_merge,_alignedandcsr_rhs, which lose the reason.Gridholds only per-dim indexes. Aux coords live next to it inCSRLinearExpression.coordsand are dropped byCSRConstraint.from_csr(constraints.py:1401), which is CSRConstraint drops auxiliary coordinates when a sparse expression becomes a constraint #941.sum,groupby, selection), so sparse and dense give the same terms up to order; only@/dotprunes them. Cell activeness is carried byconstalone. Export culls zeros regardless; Zero culling forces model rebuilding #925 is an export/persistent-diff question and does not block this series.Plan
Four pull requests, grouped by shared design rather than one per issue:
Grid, and all operations that keep the backing. Merged.soften/penalty=on frozen constraints, so the model key in PR 4 needs no special case for softening. Merged.shape/sizes/coords/dims/isnullfrom the CSR store..is_sparse, repr marker. Opt-inwarn_on_densifythrough one_densify_notice(reason)helper, called atdata, at eachreturn Nonefallback and inCSRConstraint.to_dense.datastays a one-way conversion (no cached dense copy next to_csr, the setters would make it stale). Move aux coords intoGrid.scaled_by/shiftedfor non-scalar constants, aligned through_matmul_operand_to_matrixsum(dim)by merging rows directly (CSRLinearExpression.aggregated), keeping explicit zeros. Chainedgroupbyfrom a CSR source; fix the gate atexpressions.py:613to usecoord_dimsinstead ofself.data.dims.mask=part of #970selthroughreindexed,iselas row gather. Keep_try_csr_mergesparse when aux coords differ across grids.CSRConstraint(trivial once inGrid), incl. netcdf round trip (io.py:1260). Explanatory errors forloc/update/from_rulenaming.mutable(). Expression rhs moved to lhs.flat/to_polarsfrom the CSR store, modelled onCSRConstraint.to_polars. Sparse objective acrossobjective.py,matrices.py,io.py,persistent/diff.py; decide where the objective name lives (CSRLinearExpressionhas noattrs).CSRConstraintnatively (one slack term per active row), drop thepenalty+freezeerrorsemantics="v1",sparse=/sparse_groupby,freeze_constraints), as proposed in #976 (under discussion). Replaces the earlier plan of per-callsparse=on@/dot/merge; removes the coupling of@tooptions["sparse_groupby"].Ordering notes:
constraints.pyso fix: support adding/removing variables after adding frozen constraint #806 can land before PR 2.test/test_csr.py(assert_frozen_equal,assert_terms_equal,assert_cells_equal,assert_contracted_equal) with a "backing kept" assertion instead of adding a new fixture.signinto_constraintand non-scalarfillnastill densify. groupby-sum pads every group to the largest group size #745 is likely covered by sparsegroupby; re-check and close or narrow.Children