Skip to content
5 changes: 4 additions & 1 deletion docs/cuopt/source/convex-features.rst
Original file line number Diff line number Diff line change
Expand Up @@ -325,4 +325,7 @@ By default, PDLP operates in the native precision of the problem type (FP64 for
Multi-GPU Mode
--------------

Users can use multiple GPUs to solve a problem by specifying the ``num_gpus`` parameter. The feature is restricted to LP problems that uses concurrent mode and supports up to 2 GPUs at the moment. Using this mode will run PDLP and barrier in parallel on different GPUs to avoid sharing single GPU resources.
Users can use multiple GPUs to solve a problem by specifying the ``num_gpus`` parameter, in one of two ways:

- **Concurrent mode**: restricted to LP problems that use concurrent mode and supports up to 2 GPUs. Using this mode will run PDLP and barrier in parallel on different GPUs to avoid sharing single GPU resources.
- **Distributed PDLP**: restricted to LP problems solved with the ``PDLP`` method. Setting ``num_gpus`` to ``-1`` or to a value greater than 1, together with ``use_distributed_pdlp`` set to true, shards a single PDLP solve across multiple GPUs. ``-1`` selects all GPUs visible to the process, which may resolve to a single GPU on a single-GPU host; multi-GPU sharding only happens when more than one GPU is actually selected. Use ``distributed_pdlp_partitioner`` to control how the problem is split across GPUs. See :ref:`distributed-pdlp-partitioner-constants` for the available strategies.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we calling this distributed PDLP? I've heard it referred to as multi-GPU PDLP. We should make sure we use consistent namin.

@ramakrishnap-nv ramakrishnap-nv Sep 22, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with the API and parameter names in the C++ API, but I am open to make changes and update the API.

29 changes: 28 additions & 1 deletion docs/cuopt/source/convex-settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,34 @@ cuOpt will stop at the first limit (iteration or time) reached.
Number of GPUs
^^^^^^^^^^^^^^

``CUOPT_NUM_GPUS`` controls the number of GPUs to use for the solve. This setting is only relevant for LP problems that uses concurrent mode and supports up to 2 GPUs at the moment. Using this mode will run PDLP and barrier in parallel on different GPUs to avoid sharing single GPU resources.
``CUOPT_NUM_GPUS`` controls the number of GPUs to use for the solve.

For LP problems solved with ``Concurrent`` method, this setting supports up to 2 GPUs. Using this mode will run
PDLP and barrier in parallel on different GPUs to avoid sharing single GPU resources.

For LP problems solved with ``PDLP`` method, setting ``CUOPT_NUM_GPUS`` to ``-1`` or to a value greater than 1,
together with ``CUOPT_USE_DISTRIBUTED_PDLP`` set to true, distributes the PDLP solve across multiple GPUs. A
value of ``-1`` uses all GPUs visible to the process, which may resolve to a single GPU on a single-GPU host;
multi-GPU sharding only happens when more than one GPU is actually selected.

Distributed PDLP
^^^^^^^^^^^^^^^^

``CUOPT_USE_DISTRIBUTED_PDLP`` controls whether PDLP should be distributed across multiple GPUs. It requires
``CUOPT_METHOD`` to be ``PDLP`` and ``CUOPT_NUM_GPUS`` to be ``-1`` or greater than 1 (as above, ``-1`` may
resolve to a single visible GPU, in which case the solve still runs but is not actually sharded).

``CUOPT_DISTRIBUTED_PDLP_PARTITIONER`` controls how the problem is partitioned across the GPUs used by distributed
PDLP:

* ``0``: Auto (default) - picks ``RoundRobin`` on a single GPU and ``KaMinPar`` otherwise
* ``1``: KaMinPar - a multi-threaded graph partitioner that generally produces better balanced shards at the cost of
extra partitioning time
* ``2``: RoundRobin - assigns rows/columns across GPUs in round-robin fashion, without building a partitioning graph

.. note:: The default value is ``false`` for ``CUOPT_USE_DISTRIBUTED_PDLP`` and ``0`` (Auto) for
``CUOPT_DISTRIBUTED_PDLP_PARTITIONER``. C API users should use the constants defined in
:ref:`distributed-pdlp-partitioner-constants`.


Infeasibility Detection
Expand Down
13 changes: 13 additions & 0 deletions docs/cuopt/source/cuopt-c/convex/convex-c-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ These constants are used as parameter names in the :c:func:`cuOptSetParameter`,
.. doxygendefine:: CUOPT_SOLUTION_FILE
.. doxygendefine:: CUOPT_NUM_CPU_THREADS
.. doxygendefine:: CUOPT_NUM_GPUS
.. doxygendefine:: CUOPT_USE_DISTRIBUTED_PDLP
.. doxygendefine:: CUOPT_DISTRIBUTED_PDLP_PARTITIONER
.. doxygendefine:: CUOPT_USER_PROBLEM_FILE
.. doxygendefine:: CUOPT_PDLP_PRECISION

Expand All @@ -230,6 +232,17 @@ These constants are used to configure `CUOPT_PDLP_SOLVER_MODE` via :c:func:`cuOp
.. doxygendefine:: CUOPT_PDLP_SOLVER_MODE_METHODICAL1
.. doxygendefine:: CUOPT_PDLP_SOLVER_MODE_FAST1

.. _distributed-pdlp-partitioner-constants:

Distributed PDLP Partitioner Constants
---------------------------------------

These constants are used to configure `CUOPT_DISTRIBUTED_PDLP_PARTITIONER` via :c:func:`cuOptSetIntegerParameter`.

.. doxygendefine:: CUOPT_DISTRIBUTED_PDLP_PARTITIONER_AUTO
.. doxygendefine:: CUOPT_DISTRIBUTED_PDLP_PARTITIONER_KAMINPAR
.. doxygendefine:: CUOPT_DISTRIBUTED_PDLP_PARTITIONER_ROUND_ROBIN

.. _pdlp-precision-constants:

PDLP Precision Constants
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # noqa
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# cython: profile=False
Expand Down
13 changes: 13 additions & 0 deletions python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,21 @@
CUOPT_ABSOLUTE_DUAL_TOLERANCE,
CUOPT_ABSOLUTE_GAP_TOLERANCE,
CUOPT_ABSOLUTE_PRIMAL_TOLERANCE,
CUOPT_DISTRIBUTED_PDLP_PARTITIONER,
CUOPT_DUAL_INFEASIBLE_TOLERANCE,
CUOPT_INFEASIBILITY_DETECTION,
CUOPT_ITERATION_LIMIT,
CUOPT_METHOD,
CUOPT_MIP_HEURISTICS_ONLY,
CUOPT_NUM_GPUS,
CUOPT_PDLP_SOLVER_MODE,
CUOPT_PRIMAL_INFEASIBLE_TOLERANCE,
CUOPT_RELATIVE_DUAL_TOLERANCE,
CUOPT_RELATIVE_GAP_TOLERANCE,
CUOPT_RELATIVE_PRIMAL_TOLERANCE,
CUOPT_SOLUTION_FILE,
CUOPT_TIME_LIMIT,
CUOPT_USE_DISTRIBUTED_PDLP,
CUOPT_USER_PROBLEM_FILE,
CUOPT_PRESOLVE,
)
Expand Down Expand Up @@ -374,6 +377,16 @@ def test_solver_settings_basic():
PDLPSolverMode.Methodical1
)

# Distributed (multi-GPU) PDLP settings
settings.set_parameter(CUOPT_NUM_GPUS, -1)
settings.set_parameter(CUOPT_USE_DISTRIBUTED_PDLP, True)
# 0=Auto, 1=KaMinPar, 2=RoundRobin
settings.set_parameter(CUOPT_DISTRIBUTED_PDLP_PARTITIONER, 2)

assert settings.get_parameter(CUOPT_NUM_GPUS) == -1
assert settings.get_parameter(CUOPT_USE_DISTRIBUTED_PDLP) is True
assert settings.get_parameter(CUOPT_DISTRIBUTED_PDLP_PARTITIONER) == 2


def test_solver_settings(tmp_path):
"""Push every registered parameter to the C++ layer via set_c_solver_settings."""
Expand Down
2 changes: 1 addition & 1 deletion python/cuopt_self_hosted/cuopt_sh_client/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-FileCopyrightText: Copyright (c) 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

from cuopt_sh_client._version import __git_commit__, __version__
Expand Down
19 changes: 19 additions & 0 deletions python/cuopt_server/cuopt_server/tests/test_lp_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,25 @@ def test_create_solver_limits():
assert int(solver_settings.get_parameter("iteration_limit")) == 100


def test_create_solver_distributed_pdlp():
data = get_lp_json()
data["solver_config"]["method"] = 1 # PDLP
data["solver_config"]["num_gpus"] = -1
data["solver_config"]["use_distributed_pdlp"] = True
data["solver_config"]["distributed_pdlp_partitioner"] = 2 # RoundRobin

warnings, solver_settings = conversion.create_solver(
LPData.parse_obj(data), None
)

assert warnings == []
assert int(solver_settings.get_parameter("num_gpus")) == -1
assert bool(solver_settings.get_parameter("use_distributed_pdlp")) is True
assert (
int(solver_settings.get_parameter("distributed_pdlp_partitioner")) == 2
)


def test_create_solver_limits_clamped_by_environment(monkeypatch):
monkeypatch.setenv("CUOPT_LP_TIME_LIMIT_SEC", "2")
monkeypatch.setenv("CUOPT_LP_ITERATION_LIMIT", "10")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,27 @@ class SolverConfig(BaseModel):
)
num_gpus: Optional[int] = Field(
default=None,
description="Set the number of GPUs to use for LP solve.",
description="Set the number of GPUs to use for LP solve. For "
"distributed (multi-GPU) PDLP, use -1 to use all visible GPUs, "
"or a value greater than 1 to use that many GPUs.",
)
use_distributed_pdlp: Optional[bool] = Field(
default=None,
description="Set True to distribute the PDLP solve of an LP "
"problem across multiple GPUs. Requires method to be PDLP and "
"num_gpus to be -1 or greater than 1.",
)
distributed_pdlp_partitioner: Optional[int] = Field(
Comment thread
coderabbitai[bot] marked this conversation as resolved.
default=None,
description="Partitioner used to split the problem across GPUs "
"when use_distributed_pdlp is set:"
"<br>"
"- Auto: 0, pick automatically (RoundRobin on 1 GPU, "
"KaMinPar otherwise)"
"<br>"
"- KaMinPar: 1, multi-threaded KaMinPar graph partitioner"
"<br>"
"- RoundRobin: 2, round-robin assignment, no graph",
)
augmented: Optional[int] = Field(
default=-1,
Expand Down
Loading