Skip to content

fix(solvers): probe the Xpress licence, run solving tests on licensed solvers - #995

Open
FabianHofmann wants to merge 3 commits into
masterfrom
fix-xpress-availability
Open

FabianHofmann wants to merge 3 commits into
masterfrom
fix-xpress-availability

Conversation

@FabianHofmann

Copy link
Copy Markdown
Collaborator

Closes #994.

Note

The following content was generated by AI.

Changes proposed in this Pull Request

Xpress was the only solver without a licence probe, so licensed_solvers listed it whenever import xpress worked. With a licence that does not cover the installed release (e.g. a licence for <= 9.4 with xpress 9.9.1 installed), every solver-parametrized test failed with xpress.InterfaceError.

  • Xpress._license_probe calls xpress.init(). A rejected licence now shows up as ok=False in check_solver_licenses("xpress"), and Xpress drops out of licensed_solvers. available_solvers still only checks the import and acquires no licence.
  • Tests that solve, or build a solver model (to_xpress, to_gurobipy), now loop over licensed_solvers instead of available_solvers (18 files). Tests that only check installation, or raise before any solve, keep available_solvers.
  • New test test_xpress_license_probe_failure_marks_unlicensed, mocking only xpress.init, like the existing probe tests.

This also makes the -k "not xpress" workaround unnecessary; it deselected every test with "expression" in its name.

Verification (48828a2)
# XPAUTH_PATH pointing at a licence for <= 9.4, xpress 9.9.1 installed
uv run pytest -q --ignore=benchmarks --ignore=test/remote
8195 passed, 1403 skipped
# env -u XPAUTH_PATH (licence shipped with the wheel)
8934 passed, 1388 skipped
uv run ruff check . && uv run ruff format --check .
All checks passed!

uv run mypy linopy reports 28 pre-existing errors in linopy/solvers.py when xpress is installed (old-API fallback names, see #533); unchanged by this PR.

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.

@FabianHofmann FabianHofmann added bug Something isn't working solver interface labels Sep 28, 2026
@github-actions

github-actions Bot commented Sep 28, 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.08739 (1.08)   │   15.03 (1.00) 
 (v1)        0.08088 (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.09362 (1.07)   │   12.04 (1.00) 
 (v1)        0.08789 (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.03808 (1.07)   │   15.20 (1.0) 
 (v1)        0.03567 (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.05423 (1.10)   │   44.93 (1.0) 
 (v1)        0.04944 (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.04227 (1.09)   │   11.51 (1.0) 
 (v1)        0.03864 (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.09959 (1.06)   │   24.34 (1.06) 
 (v1)        0.09357 (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.1121 (1.07)   │   16.12 (1.00) 
 (v1)        0.1047 (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.02381 (1.06)   │   752.18 (1.10) 
 (v1)         0.0225 (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.02329 (1.07)   │   3.12 (1.33) 
 (v1)        0.02181 (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.06329 (1.20)   │   126.16 (1.44) 
 (v1)        0.05253 (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.06331 (1.22)   │   126.16 (1.44) 
 (v1)        0.05171 (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.06342 (1.21)   │   126.16 (1.44) 
 (v1)        0.05244 (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.05471 (1.00)   │    715.12 (1.0) 
 (v1)        0.05464 (1.0)   │   787.73 (1.10) 

benchmarks/drivers/test_build.py::test_build[masked-n=10]
                 time (s)        peak (KiB) 
 name                mean   │          mean 
────────────────────────────────────────────
 (legacy)   0.0532 (1.07)   │   4.54 (1.27) 
 (v1)       0.04949 (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.3734 (1.04)   │   704.12 (1.09) 
 (v1)         0.358 (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.3909 (1.05)   │   18.34 (1.0) 
 (v1)        0.3733 (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.3931 (1.06)   │   9.54 (1.0) 
 (v1)        0.3703 (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.07464 (1.06)   │   3.77 (1.12) 
 (v1)        0.07018 (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.07508 (1.07)   │   216.59 (1.10) 
 (v1)        0.07001 (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.03803 (1.07)   │   938.49 (1.0) 
 (v1)        0.03547 (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.039 (1.06)   │   9.66 (1.0) 
 (v1)       0.03669 (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.03875 (1.06)   │   5.32 (1.0) 
 (v1)        0.03638 (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.01958 (1.02)   │   1.28 (1.0) 
 (v1)         0.0192 (1.0)   │   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.01941 (1.01)   │   1.28 (1.0) 
 (v1)        0.01919 (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.01921 (1.01)   │   1.28 (1.0) 
 (v1)        0.01902 (1.0)   │   1.28 (1.0) 

benchmarks/drivers/test_build.py::test_build[piecewise-n=1000]
                 time (s)          peak (KiB) 
 name                mean   │            mean 
──────────────────────────────────────────────
 (legacy)   0.1876 (1.04)   │   946.85 (1.06) 
 (v1)        0.1805 (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.1819 (1.04)   │   12.01 (1.00) 
 (v1)        0.1745 (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.04695 (1.03)   │   147.70 (1.06) 
 (v1)        0.04563 (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.04658 (1.04)   │   2.60 (1.09) 
 (v1)        0.04491 (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.03874 (1.08)   │   696.75 (1.03) 
 (v1)        0.03596 (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.08475 (1.06)   │   137.97 (1.0) 
 (v1)        0.07987 (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.05831 (1.0)   │   69.22 (1.0) 
 (v1)       0.06038 (1.04)   │   69.22 (1.0) 

benchmarks/drivers/test_build.py::test_build[sos-n=1000]
                 time (s)          peak (KiB) 
 name                mean   │            mean 
──────────────────────────────────────────────
 (legacy)    0.045 (1.07)   │   402.33 (1.00) 
 (v1)       0.04196 (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.04443 (1.09)   │   3.19 (1.19) 
 (v1)        0.04082 (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.04861 (1.06)   │   29.00 (1.54) 
 (v1)        0.04575 (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.05864 (1.10)   │   37.95 (1.43) 
 (v1)        0.05346 (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.09239 (1.0)   │    410.93 (1.0) 
 (v1)       0.09649 (1.04)   │   427.84 (1.04) 

benchmarks/drivers/test_build.py::test_build[storage-n=250]
                 time (s)         peak (MiB) 
 name                mean   │           mean 
─────────────────────────────────────────────
 (legacy)   0.09831 (1.0)   │     9.94 (1.0) 
 (v1)       0.1033 (1.05)   │   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 28, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 181 untouched benchmarks
⏩ 181 skipped benchmarks1


Comparing fix-xpress-availability (fbc4a1d) with master (1b2ea76)

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

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working solver interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Xpress missing _license_probe: licensed_solvers includes xpress with an out-of-date licence, breaking local tests

1 participant