Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions docs/cuopt/source/cuopt-grpc/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,14 @@ to that client. A **custom** gRPC client must configure the channel itself
Python Async gRPC Client (``cuopt.grpc``)
-----------------------------------------

``Client(host, port, tls=...)`` takes the server address in code. It does
**not** read ``CUOPT_REMOTE_HOST`` or ``CUOPT_REMOTE_PORT``.
``Client(host, port, tls=...)`` (LP/MIP/QP) and ``RoutingClient(host, port,
tls=...)`` (VRP, :doc:`routing`) take the server address in code. Neither
reads ``CUOPT_REMOTE_HOST`` or ``CUOPT_REMOTE_PORT``.

When ``tls`` is omitted (``None``), the client honors the same ``CUOPT_TLS_*``
variables as remote execution. Pass ``tls=False`` for plain TCP, or
``tls=TlsConfig(...)`` for explicit PEM paths (see :doc:`python-async-client`).
When ``tls`` is omitted (``None``), both clients honor the same
``CUOPT_TLS_*`` variables as remote execution. Pass ``tls=False`` for plain
TCP, or ``tls=TlsConfig(...)`` for explicit PEM paths (see
:doc:`python-async-client`).

.. list-table::
:header-rows: 1
Expand Down Expand Up @@ -163,8 +165,11 @@ variables as remote execution. Pass ``tls=False`` for plain TCP, or
- ``0``
- Non-zero: extra gRPC client logging

``CUOPT_CHUNK_SIZE`` and ``CUOPT_MAX_MESSAGE_BYTES`` also apply to this client
when set (same defaults as the integrated remote client).
``CUOPT_MAX_MESSAGE_BYTES`` also applies to both clients when set (same
default as the integrated remote client) -- it raises the gRPC channel's
max message size regardless of chunking support. ``CUOPT_CHUNK_SIZE`` only
has an effect for ``Client``; ``RoutingClient`` has no chunking to size (see
:doc:`routing`'s Limitations).

Usage
=====
Expand Down Expand Up @@ -364,7 +369,7 @@ See :doc:`python-async-client` for the full job API.
Limitations and Scope
=====================

* **Problem types** — **LP**, **MIP**, and **QP** are supported on the gRPC remote path. **Routing** (VRP, TSP, PDP) is **not** supported yet; use the :doc:`REST self-hosted server <../cuopt-server/index>` for remote routing until a future release adds routing over ``CuOptRemoteService``.
* **Problem types** — **LP**, **MIP**, and **QP** support both remote execution and gRPC clients. **Routing** (VRP, TSP, PDP) supports the explicit :doc:`VRP gRPC client <routing>` only; there is no ``CUOPT_REMOTE_HOST``/``CUOPT_REMOTE_PORT`` remote-execution path for routing yet, and the client has no log/incumbent streaming (see :ref:`Limitations and Roadmap <cuopt-grpc-routing-limitations>`). The :doc:`REST self-hosted server <../cuopt-server/index>` is also available for remote routing.
* **Message size** — Large problems use chunking; very large models can still hit gRPC max message / timeout limits. Tune ``CUOPT_CHUNK_SIZE``, ``CUOPT_MAX_MESSAGE_BYTES``, server ``--max-message-mb``, and solver ``time_limit`` as needed.
* **``CUOPT_GRPC_ARGS``** — Parsed on whitespace only; arguments containing spaces are awkward unless you invoke ``cuopt_grpc_server`` directly.
* **CRL / OCSP** — Not handled by the integrated gRPC TLS stack; use a private CA rotation strategy or a TLS-terminating proxy if you need revocation workflows.
Expand Down
27 changes: 18 additions & 9 deletions docs/cuopt/source/cuopt-grpc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ The **CuOptRemoteService** gRPC API is defined in Protocol Buffers under the ``c

* ``cpp/src/grpc/cuopt_remote_service.proto`` — service and job/chunk/log RPCs
* ``cpp/src/grpc/cuopt_remote.proto`` — LP/MIP problem, settings, and result messages
* ``cpp/src/grpc/routing/cuopt_routing.proto`` — VRP request messages
* ``cpp/src/grpc/routing/cuopt_routing_solution.proto`` — VRP result messages

Most users do **not** call these RPCs directly:

* **Remote execution** — Python, C (``cuOptSolve``), and ``cuopt_cli`` forward
solves when ``CUOPT_REMOTE_HOST`` and ``CUOPT_REMOTE_PORT`` are set
(:doc:`quick-start`, :doc:`advanced`).
* **Python async gRPC client** — ``cuopt.grpc.linear_programming.Client``
(:doc:`python-async-client`).
LP/MIP/QP solves when ``CUOPT_REMOTE_HOST`` and ``CUOPT_REMOTE_PORT`` are
set (:doc:`quick-start`, :doc:`advanced`).
* **Python async gRPC client** — ``cuopt.grpc.linear_programming.Client`` for
LP/MIP/QP (:doc:`python-async-client`), ``cuopt.grpc.routing.RoutingClient``
for VRP (:doc:`routing`).

**Custom** clients call ``CuOptRemoteService`` over gRPC using these definitions.
This page summarizes the service for custom integrators and debugging.
Expand All @@ -35,7 +38,8 @@ Asynchronous Jobs
* - RPC
- Purpose
* - ``SubmitJob``
- Submit an LP or MIP job in one message (within gRPC message size limits).
- Submit an LP, MIP, or VRP job in one message (within gRPC message size
limits; VRP is unary-only today, see :doc:`routing`).
* - ``CheckStatus``
- Poll job status by ``job_id``.
* - ``GetResult``
Expand Down Expand Up @@ -97,10 +101,15 @@ Streaming and Callbacks
Messages and Constraints
========================

* **Problem types** — Wire categories are LP/QP or MIP. QP is submitted as
``lp_request`` (``SolveLPRequest``) with quadratic fields on
``OptimizationProblem``. **Routing** over this gRPC service is **not**
available yet (planned; use REST for remote routing today).
* **Problem types** — Wire categories are LP/QP, MIP, or VRP. QP is submitted
as ``lp_request`` (``SolveLPRequest``) with quadratic fields on
``OptimizationProblem``. **VRP** rides the same ``SubmitJob``/``GetResult``
RPCs as LP/MIP, as a ``vrp_request`` payload typed by
``cpp/src/grpc/routing/cuopt_routing.proto`` (problem) and
``cuopt_routing_solution.proto`` (result) -- not a separate service. There
is no ``CUOPT_REMOTE_HOST``/``CUOPT_REMOTE_PORT`` remote-execution path for
routing yet; use ``cuopt.grpc.routing.RoutingClient`` (:doc:`routing`) or
REST for remote routing today.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
* **Solver settings** — Carried as ``PDLPSolverSettings`` or ``MIPSolverSettings`` inside the request or chunked header, aligned with the NVIDIA cuOpt solver options documentation.
* **Errors** — Transport failures use gRPC status codes. Some outcomes use
``Status::OK`` with response fields: ``CheckStatus`` reports unknown jobs as
Expand Down
16 changes: 15 additions & 1 deletion docs/cuopt/source/cuopt-grpc/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ Add TLS or tuning variables from :doc:`advanced` if your deployment uses them.

.. note::

Routing solve over gRPC is not supported. For solving routing problems remotely today, use the HTTP/JSON :doc:`REST self-hosted server <../cuopt-server/index>` and :doc:`Examples <../cuopt-server/examples/index>`.
Routing does not read ``CUOPT_REMOTE_HOST``/``CUOPT_REMOTE_PORT`` --
use the explicit :ref:`VRP gRPC client <cuopt-grpc-examples-routing>`
below, or the HTTP/JSON :doc:`REST self-hosted server <../cuopt-server/index>`
and :doc:`Examples <../cuopt-server/examples/index>`.

Where to Find Examples
======================
Expand Down Expand Up @@ -70,6 +73,17 @@ without ``CUOPT_REMOTE_*``, use ``cuopt.grpc.linear_programming.Client``:
* :doc:`python-async-client-examples` — log streaming and incumbent streaming
* :doc:`python-async-client-api` — API reference

Routing (VRP)
-------------

.. _cuopt-grpc-examples-routing:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

could we have a sample response here? I think we add it for other example files

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.

Added — routing.rst's Connect and Solve section now has a sample-output block right after the demo's literalinclude, matching the convention from cuopt-python/routing/routing-examples.rst's TSP batch example. Labeled as illustrative rather than a captured run, since there's no GPU in this environment to actually execute it against, and VRP status/objective/route order aren't guaranteed stable across runs.


For VRP, TSP, and PDP problems, use ``cuopt.grpc.routing.RoutingClient`` --
the same submit / wait / result / delete lifecycle, with no
``CUOPT_REMOTE_*`` equivalent yet:

* :doc:`routing` — overview, API reference, and :download:`remote_routing_demo.py <examples/remote_routing_demo.py>`

Custom gRPC Client
------------------

Expand Down
43 changes: 43 additions & 0 deletions docs/cuopt/source/cuopt-grpc/examples/remote_routing_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

"""Minimal VRP demo for the NVIDIA cuOpt VRP gRPC client.

Unlike LP/MIP, routing has no ``CUOPT_REMOTE_HOST``/``CUOPT_REMOTE_PORT``
transparent path yet -- build a :class:`cuopt.routing.DataModel` and solve it
with :class:`cuopt.grpc.routing.RoutingClient`, an explicit client (host and
port passed directly).

Start the server first::

cuopt_grpc_server --port 5001 --workers 1

Then::

python remote_routing_demo.py
"""

import numpy as np
from cuopt import routing
from cuopt.grpc.routing import RoutingClient

dm = routing.DataModel(5, 2)
cost_matrix = np.array(
[
[0, 1, 2, 2, 1],
[1, 0, 1, 2, 2],
[2, 1, 0, 1, 2],
[2, 2, 1, 0, 1],
[1, 2, 2, 1, 0],
],
dtype=np.float32,
)
dm.add_cost_matrix(cost_matrix)

client = RoutingClient("localhost", 5001) # tls=None uses CUOPT_TLS_* if set
solution = client.solve(dm, {"time_limit": 5.0})

print("Status: ", solution["status_message"])
print("Vehicles: ", solution["vehicle_count"])
print("Objective: ", solution["total_objective_value"])
print("Route: ", solution["route"])
27 changes: 18 additions & 9 deletions docs/cuopt/source/cuopt-grpc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,25 @@ NVIDIA cuOpt can run LP, MIP, and QP solves on a remote GPU host through
**gRPC clients** (explicit client)
Your program opens a gRPC connection and manages jobs itself. Use the
:doc:`Python async gRPC client <python-async-client>`
(``cuopt.grpc.linear_programming.Client``) for job management, or speak
``CuOptRemoteService`` directly from a custom client (:doc:`api`).
(``cuopt.grpc.linear_programming.Client``) for LP/MIP/QP job management, the
:doc:`VRP gRPC client <routing>` (``cuopt.grpc.routing.RoutingClient``) for
routing, or speak ``CuOptRemoteService`` directly from a custom client
(:doc:`api`).

In this section, **remote execution** always means the zero-code-change path
above. When talking about programs that construct a client and call gRPC
themselves, we say **gRPC client**.

.. note::

**Problem types:** LP, MIP, and QP are supported today. **Routing** (VRP,
TSP, PDP, and related APIs) over gRPC is **not** available yet; support is
planned for an **upcoming** release. For remote routing today, use the
HTTP/JSON :doc:`REST self-hosted server <../cuopt-server/index>`.
**Problem types:** LP, MIP, and QP support both remote execution and
gRPC clients. **Routing** (VRP, TSP, PDP) supports the explicit
:doc:`VRP gRPC client <routing>` only -- there is no
``CUOPT_REMOTE_HOST``/``CUOPT_REMOTE_PORT`` remote-execution path for
routing yet (tracked in `#1633
<https://github.com/NVIDIA/cuopt/issues/1633>`_). The HTTP/JSON
:doc:`REST self-hosted server <../cuopt-server/index>` is also available
for remote routing.

This is **not** the HTTP/JSON :doc:`REST self-hosted server <../cuopt-server/index>`
(FastAPI). REST is for arbitrary HTTP clients; gRPC serves remote execution
Expand All @@ -43,13 +49,15 @@ When to Choose Which Path
scripts and APIs as a local solve.
* **Python async gRPC client** — explicit job control: submit now, wait or
poll later, cancel, stream solver logs, stream MIP incumbents.
* **VRP gRPC client** — explicit job control for routing (submit / wait /
result / delete); no remote execution or streaming yet.
* **Custom ``CuOptRemoteService`` client** — non-Python (or fully custom)
integrations that speak the protos directly. See :doc:`api`.

Start with :doc:`quick-start` (install, server, and a minimal LP). Use
:doc:`python-async-client` for the Python gRPC client; :doc:`advanced` for
TLS, Docker, environment variables, and troubleshooting; :doc:`examples` for
additional patterns.
:doc:`python-async-client` for the LP/MIP/QP gRPC client, :doc:`routing` for
the VRP gRPC client; :doc:`advanced` for TLS, Docker, environment variables,
and troubleshooting; :doc:`examples` for additional patterns.

.. toctree::
:maxdepth: 2
Expand All @@ -60,6 +68,7 @@ additional patterns.
python-async-client.rst
python-async-client-examples.rst
python-async-client-api.rst
routing.rst
advanced.rst
examples.rst
api.rst
Expand Down
10 changes: 6 additions & 4 deletions docs/cuopt/source/cuopt-grpc/quick-start.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ directly (see :doc:`api`).

.. note::

**Problem types:** **LP**, **MIP**, and **QP** are supported today.
**Routing** (VRP, TSP, PDP) over gRPC is **not** available; for remote
routing, use the HTTP/JSON :doc:`REST self-hosted server <../cuopt-server/index>`.
This guide is **not** the REST server.
**Problem types:** **LP**, **MIP**, and **QP** support remote execution
(this guide) and gRPC clients. **Routing** (VRP, TSP, PDP) supports the
explicit :doc:`VRP gRPC client <routing>` only -- it does not read
``CUOPT_REMOTE_HOST``/``CUOPT_REMOTE_PORT`` the way this guide's LP does.
The HTTP/JSON :doc:`REST self-hosted server <../cuopt-server/index>` is
also available for remote routing. This guide is **not** the REST server.

How Remote Execution Works
==========================
Expand Down
148 changes: 148 additions & 0 deletions docs/cuopt/source/cuopt-grpc/routing.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
..
SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: Apache-2.0

=============================
VRP gRPC Client (Routing)
=============================

``cuopt.grpc.routing.RoutingClient`` is an explicit gRPC client for solving
**VRP** (vehicle routing, including TSP and PDP) problems on
``cuopt_grpc_server``. It uses the same job lifecycle as the LP/MIP
:doc:`Python async gRPC client <python-async-client>`: **submit** → **wait**
→ **result** → **delete**, plus a **solve** convenience method that does all
four.

There is no ``CUOPT_REMOTE_HOST``/``CUOPT_REMOTE_PORT`` transparent path for
routing yet (unlike LP/MIP/QP) -- always construct ``RoutingClient`` with an
explicit host and port. See :ref:`Limitations and Roadmap
<cuopt-grpc-routing-limitations>` below.

Prerequisites
=============

A running ``cuopt_grpc_server`` on a GPU host (see :doc:`quick-start`):

.. code-block:: bash

cuopt_grpc_server --port 5001 --workers 1

Connect and Solve
==================

``RoutingClient(host, port, *, tls=None)`` takes the same arguments as the
LP/MIP client's ``Client(host, port, tls=...)``; see :doc:`python-async-client`
for the ``tls`` options.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

``RoutingClient.submit()`` accepts a :class:`cuopt.routing.DataModel` built
the same way as for a local :func:`cuopt.routing.Solve`. ``solve()`` submits,
waits, and deletes the job's server-side state when done (pass
``delete=False`` to keep it around for a later ``result()`` call).

:download:`remote_routing_demo.py <examples/remote_routing_demo.py>`

.. literalinclude:: examples/remote_routing_demo.py
:language: python
:linenos:

Illustrative output (exact status text, vehicle count, objective, and route
order depend on the solver run and are not guaranteed to match):

.. code-block:: text

Status: Success
Vehicles: 1
Objective: 5.0
Route: [0 1 2 3 4 0]

Job Lifecycle
=============

* ``submit(data_model, settings=None)`` — serializes the problem and settings, returns a ``job_id``.
* ``wait(job_id, timeout=0)`` — blocks until the job reaches a terminal state; returns the status.
* ``result(job_id)`` — returns the solution dict, or ``None`` if the job has not finished.
* ``delete(job_id)`` — releases the job's server-side result.
* ``solve(data_model, settings=None, *, timeout=0, delete=True)`` — submit + wait + result, deleting the job afterward unless ``delete=False``.

A failed or non-completed job raises ``RoutingSolveError`` from ``submit``,
``wait``, or ``solve``.

Settings
========

``settings`` accepts a ``dict`` or a :class:`cuopt.routing.SolverSettings`.
Today only ``time_limit`` is forwarded to the remote solve; other
``SolverSettings`` options (``verbose``, ``error_logging``,
``dump_best_results_path``/``interval``) are not yet mapped over gRPC (see
:ref:`Limitations and Roadmap <cuopt-grpc-routing-limitations>`). A ``dict``
key other than ``time_limit`` is silently ignored rather than raising an
error.

Solution Fields
================

``result()`` and ``solve()`` return a ``dict`` with the same fields as a
local :class:`cuopt.routing.Assignment`, read directly off the wire:

.. list-table::
:header-rows: 1

* - Key
- Description
* - ``status`` / ``status_message``
- Integer and human-readable solve status.
* - ``error_message``
- Set when the solve failed.
* - ``vehicle_count``
- Number of vehicles used.
* - ``total_objective_value`` / ``objective_values``
- Overall cost and the per-objective breakdown.
* - ``route``, ``truck_id``, ``locations``, ``node_types``, ``arrival_stamp``
- Per-stop route arrays, one entry per stop across all vehicles.
* - ``unserviced_nodes``
- Orders that cannot be served.
* - ``accepted``
- Orders accepted, for prize-collection problems.

.. _cuopt-grpc-routing-limitations:

Limitations and Roadmap
=========================

* **No transparent remote execution** — routing does not read
``CUOPT_REMOTE_HOST``/``CUOPT_REMOTE_PORT``; always pass host and port to
``RoutingClient`` explicitly. Tracked in `#1633
<https://github.com/NVIDIA/cuopt/issues/1633>`_.
* **Settings surface** — only ``time_limit`` is forwarded today. Tracked in
`#1632 <https://github.com/NVIDIA/cuopt/issues/1632>`_.
* **No 2 GiB chunking** — VRP is unary-only; a cost/transit matrix or
``RoutingSolution`` that exceeds the gRPC max message size cannot be sent
or retrieved (the LP/MIP client chunks automatically). Tracked in `#1629
<https://github.com/NVIDIA/cuopt/issues/1629>`_.
* **No log or incumbent streaming** — unlike the LP/MIP client, there is no
``start_log_stream``/``start_incumbent_stream`` equivalent yet. Tracked in
`#1630 <https://github.com/NVIDIA/cuopt/issues/1630>`_.
* **Input validation** — malformed problems may fail late or with a generic
error rather than an early, descriptive one. Tracked in `#1631
<https://github.com/NVIDIA/cuopt/issues/1631>`_.

API Reference
=============

Import path: ``cuopt.grpc.routing``.

.. autoclass:: cuopt.grpc.routing.RoutingClient
:members:
:undoc-members:

.. autoexception:: cuopt.grpc.routing.RoutingSolveError
:members:
:show-inheritance:

See Also
========

* :doc:`index` — when to use gRPC vs. the REST self-hosted server
* :doc:`python-async-client` — the LP/MIP/QP equivalent client
* :doc:`api` — how VRP rides ``CuOptRemoteService``'s RPCs
* :doc:`../cuopt-python/routing/index` — the local routing Python API
Loading