diff --git a/docs/cuopt/source/cuopt-grpc/advanced.rst b/docs/cuopt/source/cuopt-grpc/advanced.rst index 6e6e958686..fd3d79b68e 100644 --- a/docs/cuopt/source/cuopt-grpc/advanced.rst +++ b/docs/cuopt/source/cuopt-grpc/advanced.rst @@ -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 @@ -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 ===== @@ -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 ` 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 `). 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. diff --git a/docs/cuopt/source/cuopt-grpc/api.rst b/docs/cuopt/source/cuopt-grpc/api.rst index 918108723c..75d498fd98 100644 --- a/docs/cuopt/source/cuopt-grpc/api.rst +++ b/docs/cuopt/source/cuopt-grpc/api.rst @@ -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. @@ -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`` @@ -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. * **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 diff --git a/docs/cuopt/source/cuopt-grpc/examples.rst b/docs/cuopt/source/cuopt-grpc/examples.rst index 35bb1e535e..6d02cc8cbc 100644 --- a/docs/cuopt/source/cuopt-grpc/examples.rst +++ b/docs/cuopt/source/cuopt-grpc/examples.rst @@ -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 ` + below, or the HTTP/JSON :doc:`REST self-hosted server <../cuopt-server/index>` + and :doc:`Examples <../cuopt-server/examples/index>`. Where to Find Examples ====================== @@ -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: + +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 ` + Custom gRPC Client ------------------ diff --git a/docs/cuopt/source/cuopt-grpc/examples/remote_routing_demo.py b/docs/cuopt/source/cuopt-grpc/examples/remote_routing_demo.py new file mode 100644 index 0000000000..68da94e6a0 --- /dev/null +++ b/docs/cuopt/source/cuopt-grpc/examples/remote_routing_demo.py @@ -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"]) diff --git a/docs/cuopt/source/cuopt-grpc/index.rst b/docs/cuopt/source/cuopt-grpc/index.rst index c0ecb83010..f3747c1515 100644 --- a/docs/cuopt/source/cuopt-grpc/index.rst +++ b/docs/cuopt/source/cuopt-grpc/index.rst @@ -18,8 +18,10 @@ 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 ` - (``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 ` (``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 @@ -27,10 +29,14 @@ 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 ` only -- there is no + ``CUOPT_REMOTE_HOST``/``CUOPT_REMOTE_PORT`` remote-execution path for + routing yet (tracked in `#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 @@ -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 @@ -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 diff --git a/docs/cuopt/source/cuopt-grpc/quick-start.rst b/docs/cuopt/source/cuopt-grpc/quick-start.rst index 96caccd40c..ba0dad824a 100644 --- a/docs/cuopt/source/cuopt-grpc/quick-start.rst +++ b/docs/cuopt/source/cuopt-grpc/quick-start.rst @@ -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 ` 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 ========================== diff --git a/docs/cuopt/source/cuopt-grpc/routing.rst b/docs/cuopt/source/cuopt-grpc/routing.rst new file mode 100644 index 0000000000..2827301058 --- /dev/null +++ b/docs/cuopt/source/cuopt-grpc/routing.rst @@ -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 `: **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 +` 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. + +``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 ` + +.. 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 `). 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 + `_. +* **Settings surface** — only ``time_limit`` is forwarded today. Tracked in + `#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 + `_. +* **No log or incumbent streaming** — unlike the LP/MIP client, there is no + ``start_log_stream``/``start_incumbent_stream`` equivalent yet. Tracked in + `#1630 `_. +* **Input validation** — malformed problems may fail late or with a generic + error rather than an early, descriptive one. Tracked in `#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