Skip to content

Commit e698fb2

Browse files
Add support for solver cuPDLPx (#516)
* Implement interface to cupdlpx solver and required IO * Update tests * Add to docs & add package dependency * Add comment for release notes * Correction: add support for equality constraints (same l & u bounds) * Parameterize solution tolerance in tests to allow different standard for CPU and GPU solver precisions * Improve cuPDLPx solver integration - Add warning when explicit_coordinate_names is set (unsupported) - Add warning when log_fn is provided (unsupported) - Fix typo in comment (cuDPLPx -> cuPDLPx) - Use pytest.skip() for clearer MIP test reporting - Guard feasible_mip_solvers.remove() when cupdlpx unavailable * Add cuPDLPx to package registry & update tests to use pattern for new solver features * Add dynamic checking of package version when setting xpress solver features * make xpress import save; use GPU/CPU_SOL_TOL * update docs * update gpu doc and remove cupdlx from solver deps * fix: handle None constraint matrix in to_cupdlpx * doc: add experimental warning to GPU acceleration docs --------- Co-authored-by: Fabian Hofmann <fab.hof@gmx.de>
1 parent dc20cc4 commit e698fb2

15 files changed

+771
-62
lines changed

CLAUDE.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
66

77
### Running Tests
88
```bash
9-
# Run all tests
9+
# Run all tests (excluding GPU tests by default)
1010
pytest
1111

1212
# Run tests with coverage
@@ -17,8 +17,16 @@ pytest test/test_model.py
1717

1818
# Run a specific test function
1919
pytest test/test_model.py::test_model_creation
20+
21+
# Run GPU tests (requires GPU hardware and cuPDLPx installation)
22+
pytest --run-gpu
23+
24+
# Run only GPU tests
25+
pytest -m gpu --run-gpu
2026
```
2127

28+
**GPU Testing**: Tests that require GPU hardware (e.g., cuPDLPx solver) are automatically skipped by default since CI machines typically don't have GPUs. To run GPU tests locally, use the `--run-gpu` flag. The tests are automatically marked with `@pytest.mark.gpu` based on solver capabilities.
29+
2230
### Linting and Type Checking
2331
```bash
2432
# Run linter (ruff)

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ Fri 0 4
149149
* [Cplex](https://www.ibm.com/de-de/analytics/cplex-optimizer)
150150
* [MOSEK](https://www.mosek.com/)
151151
* [COPT](https://www.shanshu.ai/copt)
152+
* [cuPDLPx](https://github.com/MIT-Lu-Lab/cuPDLPx)
152153

153154
Note that these do have to be installed by the user separately.
154155

doc/contributing.rst

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ contributing code.
1010
You are invited to submit pull requests / issues to our
1111
`Github repository <https://github.com/pypsa/linopy>`_.
1212

13+
Development Setup
14+
=================
15+
1316
For linting, formatting and checking your code contributions
1417
against our guidelines (e.g. we use `Black <https://github.com/psf/black>`_ as code style
1518
and use `pre-commit <https://pre-commit.com/index.html>`_:
@@ -19,6 +22,52 @@ and use `pre-commit <https://pre-commit.com/index.html>`_:
1922
* To automatically activate ``pre-commit`` on every ``git commit``: Run ``pre-commit install``
2023
* To manually run it: ``pre-commit run --all``
2124

25+
Running Tests
26+
=============
27+
28+
Testing is essential for maintaining code quality. We use pytest as our testing framework.
29+
30+
Basic Testing
31+
-------------
32+
33+
To run the test suite:
34+
35+
.. code-block:: bash
36+
37+
# Install development dependencies
38+
pip install -e .[dev,solvers]
39+
40+
# Run all tests
41+
pytest
42+
43+
# Run tests with coverage
44+
pytest --cov=./ --cov-report=xml linopy --doctest-modules test
45+
46+
# Run a specific test file
47+
pytest test/test_model.py
48+
49+
# Run a specific test function
50+
pytest test/test_model.py::test_model_creation
51+
52+
GPU Testing
53+
-----------
54+
55+
Tests for GPU-accelerated solvers (e.g., cuPDLPx) are automatically skipped by default since CI machines and most development environments don't have GPU hardware. This ensures tests pass in all environments.
56+
57+
To run GPU tests locally (requires GPU hardware and CUDA):
58+
59+
.. code-block:: bash
60+
61+
# Run all tests including GPU tests
62+
pytest --run-gpu
63+
64+
# Run only GPU tests
65+
pytest -m gpu --run-gpu
66+
67+
GPU tests are automatically detected based on solver capabilities - no manual marking is required. When you add a new GPU solver to linopy, tests using that solver will automatically be marked as GPU tests.
68+
69+
See the :doc:`gpu-acceleration` guide for more information about GPU solver setup and usage.
70+
2271
Contributing examples
2372
=====================
2473

doc/gpu-acceleration.rst

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
========================
2+
GPU-Accelerated Solving
3+
========================
4+
5+
.. warning::
6+
7+
This feature is **experimental** and not tested in CI due to the lack of GPU-enabled machines. Use with caution and please report any issues.
8+
9+
Linopy supports GPU-accelerated optimization solvers that can significantly speed up solving large-scale linear programming problems by leveraging the parallel processing capabilities of modern GPUs.
10+
11+
Supported GPU Solvers
12+
=====================
13+
14+
cuPDLPx
15+
-------
16+
17+
`cuPDLPx <https://github.com/MIT-Lu-Lab/cuPDLPx>`_ is an open-source, GPU-accelerated first-order solver developed by MIT. It implements a Primal-Dual hybrid gradient (PDHG) method optimized for GPUs.
18+
19+
To install it, you have to have the `CUDA Toolkit <https://developer.nvidia.com/cuda/toolkit>`_ installed requiring NVIDIA GPUs on your computer. Then, install with
20+
21+
.. code-block:: bash
22+
23+
# Install CUDA Toolkit first (if not already installed)
24+
# Follow instructions at: https://developer.nvidia.com/cuda-downloads
25+
26+
# Install cuPDLPx
27+
pip install cupdlpx>=0.1.2
28+
29+
**Features:**
30+
31+
- GPU-accelerated solving for large-scale linear programs
32+
- Open source (Apache 2.0 license)
33+
- Direct API integration with linopy
34+
- Designed for problems with millions of variables and constraints
35+
36+
**Limitations:**
37+
38+
- Currently supports only Linear Programming (LP)
39+
- Does not support Mixed-Integer Programming (MIP) or Quadratic Programming (QP)
40+
- Lower numerical precision compared to CPU solvers (typical tolerance: ~2.5e-4 vs 1e-5)
41+
- File I/O not currently supported through cuPDLPx API
42+
43+
For a complete list of cuPDLPx parameters, see the `cuPDLPx documentation <https://github.com/MIT-Lu-Lab/cuPDLPx/tree/main/python#parameters>`_.
44+
45+
Xpress with GPU Acceleration
46+
-----------------------------
47+
48+
`FICO Xpress <https://www.fico.com/en/fico-xpress-trial-and-licensing-options>`_ version 9.8 and later includes GPU acceleration support for certain operations.
49+
50+
**Features:**
51+
52+
- Commercial solver with GPU support
53+
- Supports LP, MIP, and QP
54+
- Full-precision solving
55+
56+
Prerequisites
57+
=============
58+
59+
Hardware Requirements
60+
---------------------
61+
62+
GPU solvers require:
63+
64+
- NVIDIA GPU with CUDA support (compute capability 6.0 or higher recommended)
65+
- Sufficient GPU memory for your problem size (varies by problem)
66+
- PCIe 3.0 or higher for optimal data transfer
67+
68+
Software Requirements
69+
---------------------
70+
71+
1. **CUDA Toolkit**: Most GPU solvers require CUDA 11.0 or later
72+
2. **Compatible GPU drivers**: Match your CUDA version
73+
74+
Verifying Installation
75+
======================
76+
77+
To verify that the GPU solvers are properly installed and detected:
78+
79+
.. code-block:: python
80+
81+
import linopy
82+
from linopy.solver_capabilities import (
83+
SolverFeature,
84+
get_available_solvers_with_feature,
85+
)
86+
87+
# Check available solvers
88+
print("All available solvers:", linopy.available_solvers)
89+
90+
# Check GPU-accelerated solvers
91+
gpu_solvers = get_available_solvers_with_feature(
92+
SolverFeature.GPU_ACCELERATION, linopy.available_solvers
93+
)
94+
print("GPU solvers:", gpu_solvers)
95+
96+
97+
By default, GPU tests are skipped in the test suite to support CI environments without GPUs. To run GPU tests locally:
98+
99+
.. code-block:: bash
100+
101+
# Run all tests including GPU tests
102+
pytest --run-gpu
103+
104+
# Run only GPU tests
105+
pytest -m gpu --run-gpu
106+
107+
# Run specific GPU test
108+
pytest test/test_optimization.py -k cupdlpx --run-gpu
109+
110+
111+
References
112+
==========
113+
114+
- `cuPDLPx Repository <https://github.com/MIT-Lu-Lab/cuPDLPx>`_
115+
- `cuPDLPx Python Documentation <https://github.com/MIT-Lu-Lab/cuPDLPx/tree/main/python>`_
116+
- `CUDA Installation Guide <https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html>`_
117+
- `NVIDIA GPU Computing Resources <https://developer.nvidia.com/gpu-computing>`_

doc/index.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ flexible data-handling features:
3737
- Use **lazy operations** for large linear programs with
3838
`dask <https://dask.org/>`__
3939
- Choose from **different commercial and non-commercial solvers**
40-
- Fast **import and export** a linear model using xarray’s netcdf IO
40+
- Fast **import and export** a linear model using xarray's netcdf IO
41+
- Support for **GPU-accelerated solving** for large-scale problems
4142
- Support of various solvers
4243
- `Cbc <https://projects.coin-or.org/Cbc>`__
4344
- `GLPK <https://www.gnu.org/software/glpk/>`__
@@ -48,6 +49,7 @@ flexible data-handling features:
4849
- `Cplex <https://www.ibm.com/de-de/analytics/cplex-optimizer>`__
4950
- `MOSEK <https://www.mosek.com/>`__
5051
- `COPT <https://www.shanshu.ai/copt>`__
52+
- `cuPDLPx <https://github.com/MIT-Lu-Lab/cuPDLPx>`__ (GPU-accelerated)
5153

5254

5355

@@ -116,6 +118,7 @@ This package is published under MIT license.
116118
infeasible-model
117119
solve-on-remote
118120
solve-on-oetc
121+
gpu-acceleration
119122
migrating-from-pyomo
120123
gurobi-double-logging
121124

doc/prerequisites.rst

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,14 @@ Install a solver
3030

3131
Linopy won't work without a solver. Currently, the following solvers are supported:
3232

33+
CPU-based solvers
34+
~~~~~~~~~~~~~~~~~
35+
3336
- `Cbc <https://projects.coin-or.org/Cbc>`__ - open source, free, fast
3437
- `GLPK <https://www.gnu.org/software/glpk/>`__ - open source, free, not very fast
3538
- `HiGHS <https://www.maths.ed.ac.uk/hall/HiGHS/>`__ - open source, free, fast
3639
- `Gurobi <https://www.gurobi.com/>`__ - closed source, commercial, very fast
37-
- `Xpress <https://www.fico.com/en/fico-xpress-trial-and-licensing-options>`__ - closed source, commercial, very fast
40+
- `Xpress <https://www.fico.com/en/fico-xpress-trial-and-licensing-options>`__ - closed source, commercial, very fast (GPU acceleration available in v9.8+)
3841
- `Cplex <https://www.ibm.com/de-de/analytics/cplex-optimizer>`__ - closed source, commercial, very fast
3942
- `MOSEK <https://www.mosek.com/>`__
4043
- `MindOpt <https://solver.damo.alibaba.com/doc/en/html/index.html>`__ -
@@ -54,6 +57,20 @@ We recommend to install the HiGHS solver if possible, which is free and open sou
5457
pip install highspy
5558
5659
60+
GPU-accelerated solvers
61+
~~~~~~~~~~~~~~~~~~~~~~~
62+
63+
For large-scale optimization problems, GPU-accelerated solvers can provide significant performance improvements:
64+
65+
- `cuPDLPx <https://github.com/MIT-Lu-Lab/cuPDLPx>`__ - open source, GPU-accelerated first-order solver
66+
67+
**Note:** GPU solvers require compatible NVIDIA GPU hardware and CUDA installation. See the :doc:`gpu-acceleration` guide for detailed setup instructions.
68+
69+
.. code:: bash
70+
71+
pip install cupdlpx
72+
73+
5774
For most of the other solvers, please click on the links to get further installation information.
5875

5976

doc/release_notes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Release Notes
1010
* Fix compatibility for xpress versions below 9.6 (regression)
1111
* Performance: Up to 50x faster ``repr()`` for variables/constraints via O(log n) label lookup and direct numpy indexing
1212
* Performance: Up to 46x faster ``ncons`` property by replacing ``.flat.labels.unique()`` with direct counting
13+
* Add support for GPU-accelerated solver [cuPDLPx](https://github.com/MIT-Lu-Lab/cuPDLPx)
1314

1415
Version 0.5.8
1516
--------------

linopy/io.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import logging
99
import shutil
1010
import time
11+
import warnings
1112
from collections.abc import Callable
1213
from io import BufferedWriter
1314
from pathlib import Path
@@ -28,6 +29,7 @@
2829
from linopy.objective import Objective
2930

3031
if TYPE_CHECKING:
32+
from cupdlpx import Model as cupdlpxModel
3133
from highspy.highs import Highs
3234

3335
from linopy.model import Model
@@ -852,6 +854,72 @@ def to_highspy(m: Model, explicit_coordinate_names: bool = False) -> Highs:
852854
return h
853855

854856

857+
def to_cupdlpx(m: Model, explicit_coordinate_names: bool = False) -> cupdlpxModel:
858+
"""
859+
Export the model to cupdlpx.
860+
861+
This function does not write the model to intermediate files but directly
862+
passes it to cupdlpx.
863+
864+
cuPDLPx does not support named variables and constraints, so the
865+
`explicit_coordinate_names` parameter is ignored.
866+
867+
Parameters
868+
----------
869+
m : linopy.Model
870+
explicit_coordinate_names : bool, optional
871+
Ignored. cuPDLPx does not support named variables/constraints.
872+
873+
Returns
874+
-------
875+
model : cupdlpx.Model
876+
"""
877+
import cupdlpx
878+
879+
if explicit_coordinate_names:
880+
warnings.warn(
881+
"cuPDLPx does not support named variables/constraints. "
882+
"The explicit_coordinate_names parameter is ignored.",
883+
UserWarning,
884+
stacklevel=2,
885+
)
886+
887+
# build model using canonical form matrices and vectors
888+
# see https://github.com/MIT-Lu-Lab/cuPDLPx/tree/main/python#modeling
889+
M = m.matrices
890+
if M.A is None:
891+
msg = "Model has no constraints, cannot export to cuPDLPx."
892+
raise ValueError(msg)
893+
A = M.A.tocsr() # cuPDLPx only supports CSR sparse matrix format
894+
# linopy stores constraints as Ax ?= b and keeps track of inequality
895+
# sense in M.sense. Convert to separate lower and upper bound vectors.
896+
l = np.where(
897+
np.logical_or(np.equal(M.sense, ">"), np.equal(M.sense, "=")),
898+
M.b,
899+
-np.inf,
900+
)
901+
u = np.where(
902+
np.logical_or(np.equal(M.sense, "<"), np.equal(M.sense, "=")),
903+
M.b,
904+
np.inf,
905+
)
906+
907+
cu_model = cupdlpx.Model(
908+
objective_vector=M.c,
909+
constraint_matrix=A,
910+
constraint_lower_bound=l,
911+
constraint_upper_bound=u,
912+
variable_lower_bound=M.lb,
913+
variable_upper_bound=M.ub,
914+
)
915+
916+
# change objective sense
917+
if m.objective.sense == "max":
918+
cu_model.ModelSense = cupdlpx.PDLP.MAXIMIZE
919+
920+
return cu_model
921+
922+
855923
def to_block_files(m: Model, fn: Path) -> None:
856924
"""
857925
Write out the linopy model to a block structured output.

linopy/model.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
)
5151
from linopy.io import (
5252
to_block_files,
53+
to_cupdlpx,
5354
to_file,
5455
to_gurobipy,
5556
to_highspy,
@@ -1661,4 +1662,6 @@ def reset_solution(self) -> None:
16611662

16621663
to_highspy = to_highspy
16631664

1665+
to_cupdlpx = to_cupdlpx
1666+
16641667
to_block_files = to_block_files

0 commit comments

Comments
 (0)