gRPC Routing Client#

cuopt.grpc.routing.RoutingClient is the gRPC routing client, a Python client for VRP (vehicle routing, including TSP and PDP) jobs on cuopt_grpc_server. It uses a job lifecycle: submit → wait / status → result → delete, plus a solve convenience method that does all four.

Asynchronous here means the job-based API (submit, then wait or poll). It is separate from Python asyncio.

There is no CUOPT_REMOTE_HOST/CUOPT_REMOTE_PORT transparent path for routing at this time (unlike LP/MIP/QP) – always construct RoutingClient with an explicit host and port. See Limitations and Roadmap below.

Prerequisites#

A running cuopt_grpc_server on a GPU host (see Quick Start):

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 gRPC Linear Programming Client for the tls options.

RoutingClient.submit() accepts a cuopt.routing.DataModel built the same way as for a local 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).

remote_routing_demo.py

 1# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 2# SPDX-License-Identifier: Apache-2.0
 3
 4"""Minimal VRP demo for the NVIDIA cuOpt VRP gRPC client.
 5
 6Unlike LP/MIP, routing has no ``CUOPT_REMOTE_HOST``/``CUOPT_REMOTE_PORT``
 7transparent path yet -- build a :class:`cuopt.routing.DataModel` and solve it
 8with :class:`cuopt.grpc.routing.RoutingClient`, an explicit client (host and
 9port passed directly).
10
11Start the server first::
12
13    cuopt_grpc_server --port 5001 --workers 1
14
15Then::
16
17    python remote_routing_demo.py
18"""
19
20import numpy as np
21from cuopt import routing
22from cuopt.grpc.routing import RoutingClient
23
24dm = routing.DataModel(5, 2)
25cost_matrix = np.array(
26    [
27        [0, 1, 2, 2, 1],
28        [1, 0, 1, 2, 2],
29        [2, 1, 0, 1, 2],
30        [2, 2, 1, 0, 1],
31        [1, 2, 2, 1, 0],
32    ],
33    dtype=np.float32,
34)
35dm.add_cost_matrix(cost_matrix)
36
37client = RoutingClient("localhost", 5001)  # tls=None uses CUOPT_TLS_* if set
38solution = client.solve(dm, {"time_limit": 5.0})
39
40print("Status:    ", solution["status_message"])
41print("Vehicles:  ", solution["vehicle_count"])
42print("Objective: ", solution["total_objective_value"])
43print("Route:     ", solution["route"])

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

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

settings accepts a dict or a cuopt.routing.SolverSettings. The API reference describes which settings are forwarded and what result() and solve() return.

Limitations and Roadmap#

Note

For routing, gRPC currently supports problems of up to about 5500 locations when a travel time matrix is included, or about 8000 locations when it is not. For a larger problem, use a local cuopt.routing.Solve() or the legacy HTTP server.

  • 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 — time_limit, verbose_mode (or verbose), and error_logging are forwarded. dump_best_results is not, because the solver would write that file on the server. dump_config_file is not forwarded; the local solve writes that YAML in the calling process. Tracked in #1632.

  • No chunking — A problem or solution larger than the gRPC max message size when serialized (256 MB by default) cannot be sent or received. Tracked in #1629.

Next Steps#