Skip to content

fix: sum() over a dimension crashes when a sibling dimension is empty - #907

Open
FBumann wants to merge 4 commits into
masterfrom
fix/sum-empty-sibling-dim
Open

fix: sum() over a dimension crashes when a sibling dimension is empty#907
FBumann wants to merge 4 commits into
masterfrom
fix/sum-empty-sibling-dim

Conversation

@FBumann

@FBumann FBumann commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Closes #906.

Note

The following content was generated by AI.

Dataset.stack reshapes with an inferred -1, which numpy cannot resolve once the array has zero elements — e.g. x.sum('line') when x also carries an empty cycle dimension. An empty expression has no terms, so it is now stacked onto a _term dimension of size 0, consistent with x.sum('cycle') and LinearExpression.from_constant(...) over the kept coordinates.

Three commits, best reviewed one by one:

  1. strict-xfail regression test
  2. pure refactor: extract _stack_into_term_dim from BaseExpression._sum (no behaviour change)
  3. the edge case inside _stack_into_term_dim, un-xfail, release note

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Aug 27, 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.06872 (1.09)   │      13.33 (1.0) 
 (v1)         0.0631 (1.0)   │   269.30 (20.20) 

benchmarks/drivers/test_build.py::test_build[basic-n=250]
                  time (s)         peak (MiB) 
 name                 mean   │           mean 
──────────────────────────────────────────────
 (legacy)   0.07548 (1.11)   │   11.09 (1.00) 
 (v1)        0.06828 (1.0)   │    11.09 (1.0) 

benchmarks/drivers/test_build.py::test_build[cumsum-severity=0]
                  time (s)        peak (KiB) 
 name                 mean   │          mean 
─────────────────────────────────────────────
 (legacy)   0.02944 (1.11)   │   15.17 (1.0) 
 (v1)        0.02652 (1.0)   │   15.17 (1.0) 

benchmarks/drivers/test_build.py::test_build[cumsum-severity=100]
                  time (s)        peak (MiB) 
 name                 mean   │          mean 
─────────────────────────────────────────────
 (legacy)   0.04283 (1.11)   │   44.93 (1.0) 
 (v1)        0.03854 (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.03251 (1.09)   │   11.51 (1.0) 
 (v1)        0.02975 (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.07877 (1.07)   │   24.23 (1.10) 
 (v1)        0.07377 (1.0)   │    22.05 (1.0) 

benchmarks/drivers/test_build.py::test_build[expression_arithmetic-n=250]
                  time (s)         peak (MiB) 
 name                 mean   │           mean 
──────────────────────────────────────────────
 (legacy)   0.08844 (1.08)   │   15.64 (1.00) 
 (v1)         0.0822 (1.0)   │    15.64 (1.0) 

benchmarks/drivers/test_build.py::test_build[knapsack-n=10000]
                  time (s)          peak (KiB) 
 name                 mean   │            mean 
───────────────────────────────────────────────
 (legacy)   0.01895 (1.08)   │   752.15 (1.10) 
 (v1)        0.01759 (1.0)   │    685.11 (1.0) 

benchmarks/drivers/test_build.py::test_build[knapsack-n=100]
                  time (s)        peak (KiB) 
 name                 mean   │          mean 
─────────────────────────────────────────────
 (legacy)   0.01862 (1.08)   │   3.09 (1.34) 
 (v1)         0.0173 (1.0)   │    2.30 (1.0) 

benchmarks/drivers/test_build.py::test_build[kvl_cycles-severity=0]
                  time (s)          peak (MiB) 
 name                 mean   │            mean 
───────────────────────────────────────────────
 (legacy)   0.05409 (1.26)   │   126.16 (1.44) 
 (v1)        0.04277 (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.05324 (1.27)   │   126.16 (1.44) 
 (v1)        0.04188 (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.05495 (1.31)   │   126.16 (1.44) 
 (v1)         0.0421 (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.04099 (1.02)   │    714.28 (1.0) 
 (v1)        0.04022 (1.0)   │   785.32 (1.10) 

benchmarks/drivers/test_build.py::test_build[masked-n=10]
                  time (s)        peak (KiB) 
 name                 mean   │          mean 
─────────────────────────────────────────────
 (legacy)   0.04331 (1.21)   │   4.40 (1.34) 
 (v1)        0.03581 (1.0)   │    3.27 (1.0) 

benchmarks/drivers/test_build.py::test_build[merge_balance-severity=0]
                 time (s)          peak (KiB) 
 name                mean   │            mean 
──────────────────────────────────────────────
 (legacy)   0.3108 (1.05)   │   702.97 (1.10) 
 (v1)         0.296 (1.0)   │    639.22 (1.0) 

benchmarks/drivers/test_build.py::test_build[merge_balance-severity=100]
                 time (s)        peak (MiB) 
 name                mean   │          mean 
────────────────────────────────────────────
 (legacy)   0.3275 (1.06)   │   18.34 (1.0) 
 (v1)        0.3097 (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.3235 (1.05)   │   9.54 (1.0) 
 (v1)        0.3093 (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.05851 (1.10)   │   3.64 (1.14) 
 (v1)        0.05313 (1.0)   │    3.20 (1.0) 

benchmarks/drivers/test_build.py::test_build[milp-n=50]
                  time (s)          peak (KiB) 
 name                 mean   │            mean 
───────────────────────────────────────────────
 (legacy)   0.05871 (1.10)   │   216.15 (1.10) 
 (v1)        0.05323 (1.0)   │    195.76 (1.0) 

benchmarks/drivers/test_build.py::test_build[nodal_balance-severity=0]
                  time (s)         peak (KiB) 
 name                 mean   │           mean 
──────────────────────────────────────────────
 (legacy)   0.02887 (1.10)   │   935.30 (1.0) 
 (v1)         0.0262 (1.0)   │   935.30 (1.0) 

benchmarks/drivers/test_build.py::test_build[nodal_balance-severity=100]
                 time (s)       peak (MiB) 
 name                mean   │         mean 
───────────────────────────────────────────
 (legacy)   0.0299 (1.08)   │   9.66 (1.0) 
 (v1)       0.02779 (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.03033 (1.12)   │   5.32 (1.0) 
 (v1)        0.02706 (1.0)   │   5.32 (1.0) 

benchmarks/drivers/test_build.py::test_build[piecewise-n=1000]
                 time (s)          peak (KiB) 
 name                mean   │            mean 
──────────────────────────────────────────────
 (legacy)   0.1427 (1.06)   │   891.72 (1.07) 
 (v1)        0.1343 (1.0)   │    836.28 (1.0) 

benchmarks/drivers/test_build.py::test_build[piecewise-n=10]
                 time (s)         peak (KiB) 
 name                mean   │           mean 
─────────────────────────────────────────────
 (legacy)   0.1407 (1.05)   │   11.02 (1.03) 
 (v1)        0.1339 (1.0)   │    10.67 (1.0) 

benchmarks/drivers/test_build.py::test_build[qp-n=1000]
                  time (s)          peak (KiB) 
 name                 mean   │            mean 
───────────────────────────────────────────────
 (legacy)   0.03997 (1.08)   │   147.66 (1.06) 
 (v1)         0.0369 (1.0)   │    139.84 (1.0) 

benchmarks/drivers/test_build.py::test_build[qp-n=10]
                  time (s)        peak (KiB) 
 name                 mean   │          mean 
─────────────────────────────────────────────
 (legacy)   0.03934 (1.07)   │   2.57 (1.09) 
 (v1)        0.03673 (1.0)   │    2.35 (1.0) 

benchmarks/drivers/test_build.py::test_build[rolling-severity=0]
                  time (s)          peak (KiB) 
 name                 mean   │            mean 
───────────────────────────────────────────────
 (legacy)   0.02989 (1.10)   │   696.72 (1.14) 
 (v1)        0.02723 (1.0)   │    611.13 (1.0) 

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

benchmarks/drivers/test_build.py::test_build[rolling-severity=50]
                  time (s)        peak (MiB) 
 name                 mean   │          mean 
─────────────────────────────────────────────
 (legacy)   0.04776 (1.06)   │   69.22 (1.0) 
 (v1)        0.04489 (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.03388 (1.14)   │   377.39 (1.04) 
 (v1)         0.0297 (1.0)   │    363.14 (1.0) 

benchmarks/drivers/test_build.py::test_build[sos-n=10]
                 time (s)        peak (KiB) 
 name                mean   │          mean 
────────────────────────────────────────────
 (legacy)   0.0334 (1.14)   │   3.10 (1.41) 
 (v1)       0.02942 (1.0)   │    2.20 (1.0) 

benchmarks/drivers/test_build.py::test_build[sparse_network-n=10]
                  time (s)         peak (KiB) 
 name                 mean   │           mean 
──────────────────────────────────────────────
 (legacy)   0.03867 (1.10)   │   28.94 (1.61) 
 (v1)        0.03513 (1.0)   │    17.97 (1.0) 

benchmarks/drivers/test_build.py::test_build[sparse_network-n=250]
                 time (s)         peak (MiB) 
 name                mean   │           mean 
─────────────────────────────────────────────
 (legacy)   0.0498 (1.14)   │   37.95 (1.43) 
 (v1)       0.04383 (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.07682 (1.0)   │    397.39 (1.0) 
 (v1)       0.07753 (1.01)   │   414.56 (1.04) 

benchmarks/drivers/test_build.py::test_build[storage-n=250]
                  time (s)        peak (MiB) 
 name                 mean   │          mean 
─────────────────────────────────────────────
 (legacy)    0.08181 (1.0)   │    9.62 (1.0) 
 (v1)       0.08536 (1.04)   │   9.90 (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.

@codspeed-hq

codspeed-hq Bot commented Aug 27, 2026

Copy link
Copy Markdown

Merging this PR will regress 4 benchmarks

⚠️ 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

⚡ 5 improved benchmarks
❌ 4 regressed benchmarks
✅ 166 untouched benchmarks
⏩ 175 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Memory test_to_lp[milp-n=50] 2 MB 2.6 MB -23.36%
Memory test_to_lp[nodal_balance-severity=50] 2.8 MB 3.3 MB -15.08%
Memory test_to_lp[rolling-severity=0] 2.7 MB 3.1 MB -12.55%
Memory test_to_lp[nodal_balance-severity=0] 3.3 MB 3.7 MB -10.29%
Memory test_to_lp[knapsack-n=10000] 2.8 MB 2.2 MB +26.3%
Memory test_to_lp[cumsum-severity=100] 208.7 MB 172 MB +21.37%
Memory test_to_lp[storage-n=250] 35.4 MB 29.6 MB +19.68%
Memory test_to_lp[rolling-severity=50] 429.7 MB 367.7 MB +16.88%
Memory test_to_lp[storage-n=10] 2.9 MB 2.6 MB +10.79%

Tip

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


Comparing fix/sum-empty-sibling-dim (3f2c50a) with master (09c34dd)2

Open in CodSpeed

Footnotes

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

  2. No successful run was found on master (570ff2e) during the generation of this report, so 09c34dd was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@FBumann FBumann added the bug Something isn't working label Aug 27, 2026
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@FBumann
FBumann force-pushed the fix/sum-empty-sibling-dim branch from f64b8a0 to f90d3d9 Compare August 27, 2026 08:49
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@FBumann
FBumann force-pushed the fix/sum-empty-sibling-dim branch from f90d3d9 to ffc6bb5 Compare August 27, 2026 08:53
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sum() over a dimension crashes when a sibling dimension is empty

2 participants