Skip to content

fix(v1): drop solve-time coord consistency guard rejecting disjoint labels - #989

Merged
FabianHofmann merged 1 commit into
masterfrom
fix/986-coord-consistency
Sep 25, 2026
Merged

FabianHofmann merged 1 commit into
masterfrom
fix/986-coord-consistency

Conversation

@FabianHofmann

Copy link
Copy Markdown
Collaborator

Closes #986.

Note

The following content was generated by AI.

Changes proposed in this Pull Request

Under v1 semantics, Model.solve() ran Model._check_coord_consistency() (added with Model.assign_coords in #955). It raised whenever two containers share a dimension name but their labels do not nest. PyPSA uses one name dimension for all components with disjoint labels, so every multi-component PyPSA model failed at solve time.

The guard is removed:

  • v1 already enforces join="exact" where containers are actually combined, at arithmetic time.
  • The public container-level assign_coords returns detached copies, and solver matrices pair terms by variable label, not by coordinate value. So only the private _assign_coords could produce the state the guard targeted.
  • Model.assign_coords still validates its own inputs (full-index length, subset mapping, duplicate and ambiguous full-index containers).

Also removed: the guard's tests, the constants.py comment that pointed to it, and the release-note sentence. Since #955 is unreleased, no bug-fix note is needed. A regression test in test/test_optimization.py solves a model with disjoint labels on a shared dimension under both semantics. It fails on master under v1.

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. (The unreleased feat(model): Model.assign_coords — reassign coordinates on an existing model #955 note was amended.)
  • I consent to the release of this PR's code under the MIT license.

@github-actions

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.04606 (1.09)   │   15.03 (1.00) 
 (v1)        0.04226 (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.04994 (1.11)   │   12.04 (1.00) 
 (v1)        0.04482 (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.01882 (1.0)   │   15.20 (1.0) 
 (v1)       0.01929 (1.02)   │   15.20 (1.0) 

benchmarks/drivers/test_build.py::test_build[cumsum-severity=100]
                  time (s)        peak (MiB) 
 name                 mean   │          mean 
─────────────────────────────────────────────
 (legacy)   0.03062 (1.05)   │   44.93 (1.0) 
 (v1)        0.02914 (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.02136 (1.07)   │   11.51 (1.0) 
 (v1)         0.0199 (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.04852 (1.03)   │   24.34 (1.06) 
 (v1)        0.04716 (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.05415 (1.01)   │   16.12 (1.00) 
 (v1)        0.05385 (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.01146 (1.00)   │   752.18 (1.10) 
 (v1)        0.01143 (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.01142 (1.02)   │   3.12 (1.33) 
 (v1)        0.01123 (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.0372 (1.29)   │   126.16 (1.44) 
 (v1)       0.02886 (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.03686 (1.33)   │   126.16 (1.44) 
 (v1)        0.02774 (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.03629 (1.30)   │   126.16 (1.44) 
 (v1)        0.02783 (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.02616 (1.0)   │    715.12 (1.0) 
 (v1)       0.02671 (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.02565 (1.05)   │   4.54 (1.27) 
 (v1)        0.02442 (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.1897 (1.06)   │   704.12 (1.09) 
 (v1)         0.179 (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.1924 (1.03)   │   18.34 (1.0) 
 (v1)        0.1871 (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.1908 (1.00)   │   9.54 (1.0) 
 (v1)        0.1906 (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.03646 (1.02)   │   3.77 (1.12) 
 (v1)        0.03578 (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.03707 (1.0)   │   216.59 (1.10) 
 (v1)       0.03788 (1.02)   │    196.23 (1.0) 

benchmarks/drivers/test_build.py::test_build[nodal_balance-severity=0]
                  time (s)         peak (KiB) 
 name                 mean   │           mean 
──────────────────────────────────────────────
 (legacy)   0.01888 (1.07)   │   938.49 (1.0) 
 (v1)        0.01768 (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.01938 (1.01)   │   9.66 (1.0) 
 (v1)        0.01919 (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.01929 (1.01)   │   5.32 (1.0) 
 (v1)        0.01913 (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.009565 (1.0)   │   1.28 (1.0) 
 (v1)       0.009894 (1.03)   │   1.28 (1.0) 

benchmarks/drivers/test_build.py::test_build[nodal_balance_sparse-severity=100]
                  time (s)       peak (MiB) 
 name                 mean   │         mean 
────────────────────────────────────────────
 (legacy)   0.01015 (1.06)   │   1.28 (1.0) 
 (v1)        0.00956 (1.0)   │   1.28 (1.0) 

benchmarks/drivers/test_build.py::test_build[nodal_balance_sparse-severity=50]
                  time (s)       peak (MiB) 
 name                 mean   │         mean 
────────────────────────────────────────────
 (legacy)   0.009704 (1.0)   │   1.28 (1.0) 
 (v1)       0.01032 (1.06)   │   1.28 (1.0) 

benchmarks/drivers/test_build.py::test_build[piecewise-n=1000]
                  time (s)          peak (KiB) 
 name                 mean   │            mean 
───────────────────────────────────────────────
 (legacy)   0.09512 (1.08)   │   946.85 (1.06) 
 (v1)        0.08795 (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.09594 (1.06)   │   12.01 (1.00) 
 (v1)        0.09017 (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.02325 (1.0)   │   147.70 (1.06) 
 (v1)       0.02465 (1.06)   │    139.87 (1.0) 

benchmarks/drivers/test_build.py::test_build[qp-n=10]
                  time (s)        peak (KiB) 
 name                 mean   │          mean 
─────────────────────────────────────────────
 (legacy)   0.02323 (1.01)   │   2.60 (1.09) 
 (v1)        0.02297 (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.02028 (1.12)   │   696.75 (1.03) 
 (v1)        0.01807 (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.05287 (1.10)   │   137.97 (1.0) 
 (v1)        0.04787 (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.0342 (1.08)   │   69.22 (1.0) 
 (v1)       0.03178 (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.02264 (1.09)   │   402.33 (1.00) 
 (v1)        0.02086 (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.022 (1.03)   │   3.19 (1.19) 
 (v1)       0.02135 (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.02525 (1.03)   │   29.00 (1.54) 
 (v1)        0.02446 (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.0324 (1.14)   │   37.95 (1.43) 
 (v1)       0.02845 (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.04599 (1.0)   │    410.93 (1.0) 
 (v1)       0.05203 (1.13)   │   427.84 (1.04) 

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

@codspeed

codspeed Bot commented Sep 25, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 181 untouched benchmarks
⏩ 181 skipped benchmarks1


Comparing fix/986-coord-consistency (b85807e) with master (26e000d)

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

@FabianHofmann
FabianHofmann merged commit 5e53a10 into master Sep 25, 2026
24 checks passed
@FabianHofmann
FabianHofmann deleted the fix/986-coord-consistency branch September 25, 2026 18:26
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.

v1: solve-time coordinate check rejects models with independent label sets on a shared dimension (breaks PyPSA)

1 participant