Skip to content

refac(sparse): rename CSRPayload to CSRExpression - #943

Merged
FabianHofmann merged 2 commits into
masterfrom
refac/rename-payload-etc
Sep 7, 2026
Merged

refac(sparse): rename CSRPayload to CSRExpression#943
FabianHofmann merged 2 commits into
masterfrom
refac/rename-payload-etc

Conversation

@FabianHofmann

Copy link
Copy Markdown
Collaborator

Closes #930.

Note

The following content was generated by AI.

Changes proposed in this Pull Request

CSRPayload is renamed to CSRExpression. "Payload" only said that data is
carried, not what it is; the class holds an affine expression A @ x + c in CSR
form, so the new name states that, and it mirrors the existing CSRConstraint
in constraints.py. The two-layer story is now explicit in the names: a
CSRExpression gets a sign and an rhs stapled on to become a CSRConstraint.

The rename carries through to the members that referred to the old name:

  • LinearExpression._payloadLinearExpression._csr
  • BaseExpression._from_payloadBaseExpression._from_csr
  • CSRConstraint.from_payloadCSRConstraint.from_csr

Local names and docstrings were adjusted where the mechanical rename would have
read badly. In constraints.py the parameter formerly called payload is now
expr, because csr.csr[active] is hard to read while expr.csr[active] is
not. Prose that said "the payload grid" now says "the expression grid".

Three unrelated uses of the word "payload" were deliberately left alone: the JWT
and HTTP payloads in linopy/remote/oetc.py, and the cuOpt warm-start note in
doc/gpu-acceleration.rst with its matching string in test_optimization.py.
Those mean transmitted data, not this class.

The branch also adds an nterm property to CSRExpression, so
LinearExpression.nterm can be answered from the CSR backing without
materializing the dense rectangle.

No public API changes: CSRPayload was never exported from linopy/__init__.py,
so no release note is included.

Verification
  • ruff check . and ruff format clean.
  • mypy linopy reports only the 28 pre-existing xpress attribute errors in
    solvers.py, which this branch does not touch.
  • pytest test/test_sparse_groupby.py: 76 passed, 74 skipped.
  • The remaining suite failures on this machine are all in xpress-backed and
    remote-OETC tests, and reproduce identically without this change (a local
    environment problem, not a regression).

Checklist

  • AI-generated content is marked (see AGENTS.md).
  • Code changes are sufficiently documented; i.e. new functions contain docstrings and further explanations may be given in doc.
  • Unit tests for new features were added (if applicable).
  • A note for the release notes doc/release_notes.rst of the upcoming release is included.
  • I consent to the release of this PR's code under the MIT license.

"Payload" said data is carried, not what it is. The class holds an
affine expression A @ x + c in CSR form, so name it CSRExpression,
mirroring CSRConstraint. LinearExpression._payload becomes _csr, and
_from_payload / CSRConstraint.from_payload become _from_csr / from_csr.
@codspeed-hq

codspeed-hq Bot commented Sep 7, 2026

Copy link
Copy Markdown

Merging this PR will regress 1 benchmark

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 1 improved benchmark
❌ 1 regressed benchmark
✅ 179 untouched benchmarks
⏩ 181 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
test_to_lp[storage-n=250] 30.8 MB 37.5 MB -17.8%
test_to_lp[masked-n=100] 2.7 MB 2.2 MB +26.84%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing refac/rename-payload-etc (9c9550e) with master (dc861fe)

Open in CodSpeed

Footnotes

  1. 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.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

Build cost — v1 vs legacy

v1 build peak & time relative to legacy, on this commit — not a comparison against master (that is CodSpeed).

peak — v1 / legacy time — v1 / legacy
peak v1/legacy time v1/legacy
Full table (time + peak, mean)
benchmarks/drivers/test_build.py::test_build[basic-n=10]
                  time (s)         peak (KiB) 
 name                 mean   │           mean 
──────────────────────────────────────────────
 (legacy)   0.06052 (1.10)   │   15.03 (1.00) 
 (v1)         0.0551 (1.0)   │    15.00 (1.0) 

benchmarks/drivers/test_build.py::test_build[basic-n=250]
                  time (s)         peak (MiB) 
 name                 mean   │           mean 
──────────────────────────────────────────────
 (legacy)   0.06527 (1.10)   │   12.04 (1.00) 
 (v1)        0.05908 (1.0)   │    12.04 (1.0) 

benchmarks/drivers/test_build.py::test_build[cumsum-severity=0]
                  time (s)        peak (KiB) 
 name                 mean   │          mean 
─────────────────────────────────────────────
 (legacy)   0.02583 (1.07)   │   15.20 (1.0) 
 (v1)        0.02409 (1.0)   │   15.20 (1.0) 

benchmarks/drivers/test_build.py::test_build[cumsum-severity=100]
                  time (s)        peak (MiB) 
 name                 mean   │          mean 
─────────────────────────────────────────────
 (legacy)   0.03833 (1.10)   │   44.93 (1.0) 
 (v1)        0.03477 (1.0)   │   44.93 (1.0) 

benchmarks/drivers/test_build.py::test_build[cumsum-severity=50]
                  time (s)        peak (MiB) 
 name                 mean   │          mean 
─────────────────────────────────────────────
 (legacy)   0.02826 (1.07)   │   11.51 (1.0) 
 (v1)        0.02649 (1.0)   │   11.51 (1.0) 

benchmarks/drivers/test_build.py::test_build[expression_arithmetic-n=10]
                  time (s)         peak (KiB) 
 name                 mean   │           mean 
──────────────────────────────────────────────
 (legacy)   0.06643 (1.05)   │   24.34 (1.06) 
 (v1)        0.06345 (1.0)   │    23.04 (1.0) 

benchmarks/drivers/test_build.py::test_build[expression_arithmetic-n=250]
                  time (s)         peak (MiB) 
 name                 mean   │           mean 
──────────────────────────────────────────────
 (legacy)   0.07341 (1.05)   │   16.12 (1.00) 
 (v1)        0.06993 (1.0)   │    16.12 (1.0) 

benchmarks/drivers/test_build.py::test_build[knapsack-n=10000]
                  time (s)          peak (KiB) 
 name                 mean   │            mean 
───────────────────────────────────────────────
 (legacy)   0.01622 (1.08)   │   752.18 (1.10) 
 (v1)        0.01497 (1.0)   │    685.15 (1.0) 

benchmarks/drivers/test_build.py::test_build[knapsack-n=100]
                  time (s)        peak (KiB) 
 name                 mean   │          mean 
─────────────────────────────────────────────
 (legacy)   0.01565 (1.06)   │   3.12 (1.33) 
 (v1)         0.0147 (1.0)   │    2.34 (1.0) 

benchmarks/drivers/test_build.py::test_build[kvl_cycles-severity=0]
                  time (s)          peak (MiB) 
 name                 mean   │            mean 
───────────────────────────────────────────────
 (legacy)   0.04753 (1.27)   │   126.16 (1.44) 
 (v1)        0.03733 (1.0)   │     87.71 (1.0) 

benchmarks/drivers/test_build.py::test_build[kvl_cycles-severity=100]
                 time (s)          peak (MiB) 
 name                mean   │            mean 
──────────────────────────────────────────────
 (legacy)   0.0453 (1.21)   │   126.16 (1.44) 
 (v1)       0.03754 (1.0)   │     87.71 (1.0) 

benchmarks/drivers/test_build.py::test_build[kvl_cycles-severity=50]
                  time (s)          peak (MiB) 
 name                 mean   │            mean 
───────────────────────────────────────────────
 (legacy)   0.04597 (1.25)   │   126.16 (1.44) 
 (v1)        0.03684 (1.0)   │     87.71 (1.0) 

benchmarks/drivers/test_build.py::test_build[masked-n=100]
                  time (s)          peak (KiB) 
 name                 mean   │            mean 
───────────────────────────────────────────────
 (legacy)    0.03605 (1.0)   │    715.12 (1.0) 
 (v1)       0.03675 (1.02)   │   787.73 (1.10) 

benchmarks/drivers/test_build.py::test_build[masked-n=10]
                  time (s)        peak (KiB) 
 name                 mean   │          mean 
─────────────────────────────────────────────
 (legacy)   0.03508 (1.08)   │   4.54 (1.27) 
 (v1)         0.0326 (1.0)   │    3.57 (1.0) 

benchmarks/drivers/test_build.py::test_build[merge_balance-severity=0]
                 time (s)          peak (KiB) 
 name                mean   │            mean 
──────────────────────────────────────────────
 (legacy)   0.2507 (1.04)   │   704.12 (1.09) 
 (v1)        0.2416 (1.0)   │    643.85 (1.0) 

benchmarks/drivers/test_build.py::test_build[merge_balance-severity=100]
                 time (s)        peak (MiB) 
 name                mean   │          mean 
────────────────────────────────────────────
 (legacy)   0.2645 (1.04)   │   18.34 (1.0) 
 (v1)         0.255 (1.0)   │   18.34 (1.0) 

benchmarks/drivers/test_build.py::test_build[merge_balance-severity=50]
                 time (s)       peak (MiB) 
 name                mean   │         mean 
───────────────────────────────────────────
 (legacy)   0.2594 (1.03)   │   9.54 (1.0) 
 (v1)        0.2519 (1.0)   │   9.54 (1.0) 

benchmarks/drivers/test_build.py::test_build[milp-n=10]
                  time (s)        peak (KiB) 
 name                 mean   │          mean 
─────────────────────────────────────────────
 (legacy)   0.04995 (1.07)   │   3.77 (1.12) 
 (v1)        0.04671 (1.0)   │    3.37 (1.0) 

benchmarks/drivers/test_build.py::test_build[milp-n=50]
                  time (s)          peak (KiB) 
 name                 mean   │            mean 
───────────────────────────────────────────────
 (legacy)   0.05014 (1.06)   │   216.59 (1.10) 
 (v1)        0.04714 (1.0)   │    196.23 (1.0) 

benchmarks/drivers/test_build.py::test_build[nodal_balance-severity=0]
                  time (s)         peak (KiB) 
 name                 mean   │           mean 
──────────────────────────────────────────────
 (legacy)   0.02564 (1.07)   │   938.49 (1.0) 
 (v1)        0.02402 (1.0)   │   938.49 (1.0) 

benchmarks/drivers/test_build.py::test_build[nodal_balance-severity=100]
                  time (s)       peak (MiB) 
 name                 mean   │         mean 
────────────────────────────────────────────
 (legacy)   0.02658 (1.05)   │   9.66 (1.0) 
 (v1)        0.02521 (1.0)   │   9.66 (1.0) 

benchmarks/drivers/test_build.py::test_build[nodal_balance-severity=50]
                  time (s)       peak (MiB) 
 name                 mean   │         mean 
────────────────────────────────────────────
 (legacy)   0.02629 (1.07)   │   5.32 (1.0) 
 (v1)        0.02454 (1.0)   │   5.32 (1.0) 

benchmarks/drivers/test_build.py::test_build[nodal_balance_sparse-severity=0]
                 time (s)       peak (MiB) 
 name                mean   │         mean 
───────────────────────────────────────────
 (legacy)   0.01556 (1.0)   │   1.47 (1.0) 
 (v1)       0.0159 (1.02)   │   1.47 (1.0) 

benchmarks/drivers/test_build.py::test_build[nodal_balance_sparse-severity=100]
                  time (s)       peak (MiB) 
 name                 mean   │         mean 
────────────────────────────────────────────
 (legacy)    0.01561 (1.0)   │   1.47 (1.0) 
 (v1)       0.01606 (1.03)   │   1.47 (1.0) 

benchmarks/drivers/test_build.py::test_build[nodal_balance_sparse-severity=50]
                  time (s)       peak (MiB) 
 name                 mean   │         mean 
────────────────────────────────────────────
 (legacy)    0.01557 (1.0)   │   1.47 (1.0) 
 (v1)       0.01625 (1.04)   │   1.47 (1.0) 

benchmarks/drivers/test_build.py::test_build[piecewise-n=1000]
                 time (s)          peak (KiB) 
 name                mean   │            mean 
──────────────────────────────────────────────
 (legacy)   0.1246 (1.02)   │   946.85 (1.06) 
 (v1)        0.1222 (1.0)   │    891.54 (1.0) 

benchmarks/drivers/test_build.py::test_build[piecewise-n=10]
                 time (s)         peak (KiB) 
 name                mean   │           mean 
─────────────────────────────────────────────
 (legacy)   0.1224 (1.02)   │   12.01 (1.00) 
 (v1)        0.1196 (1.0)   │    11.99 (1.0) 

benchmarks/drivers/test_build.py::test_build[qp-n=1000]
                  time (s)          peak (KiB) 
 name                 mean   │            mean 
───────────────────────────────────────────────
 (legacy)   0.03202 (1.05)   │   147.70 (1.06) 
 (v1)        0.03035 (1.0)   │    139.87 (1.0) 

benchmarks/drivers/test_build.py::test_build[qp-n=10]
                  time (s)        peak (KiB) 
 name                 mean   │          mean 
─────────────────────────────────────────────
 (legacy)   0.03157 (1.05)   │   2.60 (1.09) 
 (v1)         0.0301 (1.0)   │    2.38 (1.0) 

benchmarks/drivers/test_build.py::test_build[rolling-severity=0]
                 time (s)          peak (KiB) 
 name                mean   │            mean 
──────────────────────────────────────────────
 (legacy)   0.0263 (1.05)   │   696.75 (1.03) 
 (v1)       0.02508 (1.0)   │    673.70 (1.0) 

benchmarks/drivers/test_build.py::test_build[rolling-severity=100]
                  time (s)         peak (MiB) 
 name                 mean   │           mean 
──────────────────────────────────────────────
 (legacy)    0.05949 (1.0)   │   137.97 (1.0) 
 (v1)       0.06096 (1.02)   │   137.97 (1.0) 

benchmarks/drivers/test_build.py::test_build[rolling-severity=50]
                  time (s)        peak (MiB) 
 name                 mean   │          mean 
─────────────────────────────────────────────
 (legacy)   0.04246 (1.07)   │   69.22 (1.0) 
 (v1)        0.03968 (1.0)   │   69.22 (1.0) 

benchmarks/drivers/test_build.py::test_build[sos-n=1000]
                  time (s)          peak (KiB) 
 name                 mean   │            mean 
───────────────────────────────────────────────
 (legacy)   0.03028 (1.07)   │   402.33 (1.00) 
 (v1)         0.0282 (1.0)   │    402.30 (1.0) 

benchmarks/drivers/test_build.py::test_build[sos-n=10]
                  time (s)        peak (KiB) 
 name                 mean   │          mean 
─────────────────────────────────────────────
 (legacy)   0.02977 (1.06)   │   3.19 (1.19) 
 (v1)        0.02799 (1.0)   │    2.69 (1.0) 

benchmarks/drivers/test_build.py::test_build[sparse_network-n=10]
                  time (s)         peak (KiB) 
 name                 mean   │           mean 
──────────────────────────────────────────────
 (legacy)   0.03288 (1.04)   │   29.00 (1.54) 
 (v1)        0.03173 (1.0)   │    18.84 (1.0) 

benchmarks/drivers/test_build.py::test_build[sparse_network-n=250]
                  time (s)         peak (MiB) 
 name                 mean   │           mean 
──────────────────────────────────────────────
 (legacy)   0.04149 (1.10)   │   37.95 (1.43) 
 (v1)        0.03785 (1.0)   │    26.51 (1.0) 

benchmarks/drivers/test_build.py::test_build[storage-n=10]
                  time (s)          peak (KiB) 
 name                 mean   │            mean 
───────────────────────────────────────────────
 (legacy)    0.06349 (1.0)   │    410.93 (1.0) 
 (v1)       0.06551 (1.03)   │   427.84 (1.04) 

benchmarks/drivers/test_build.py::test_build[storage-n=250]
                  time (s)         peak (MiB) 
 name                 mean   │           mean 
──────────────────────────────────────────────
 (legacy)    0.06709 (1.0)   │     9.94 (1.0) 
 (v1)       0.07163 (1.07)   │   10.22 (1.03) 

📊 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.

@FabianHofmann
FabianHofmann merged commit c25f779 into master Sep 7, 2026
24 of 25 checks passed
@FabianHofmann
FabianHofmann deleted the refac/rename-payload-etc branch September 7, 2026 10:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rename CSRPayload to CSRExpression

1 participant