Routing Python Examples#

The following example showcases how to use the CuOptServiceSelfHostClient to solve a simple routing problem.

The OpenAPI specification for the server is available in open-api spec. The example data is structured as per the OpenAPI specification for the server, please refer OptimizeRoutingData under “POST /cuopt/request” under schema section.

Generic Example#

basic_routing_example.py

 1# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 2# SPDX-License-Identifier: Apache-2.0
 3"""
 4Basic Routing Server Example
 5
 6This example demonstrates how to use the cuOpt Self-Hosted Service Client
 7to solve a simple routing problem via the cuOpt server.
 8
 9Requirements:
10    - cuOpt server running (default: localhost:5000)
11    - cuopt_sh_client package installed
12
13Problem:
14    - 2 locations (0 and 1)
15    - 2 tasks at locations 0 and 1
16    - 2 vehicles starting and ending at location [0, 0]
17
18The data is structured according to the OpenAPI specification:
19    - cost_matrix_data: Cost matrix indexed by string keys
20    - task_data: Task locations
21    - fleet_data: Vehicle starting locations
22
23Expected Response:
24    {
25        "response": {
26            "solver_response": {
27                "status": 0,
28                "num_vehicles": 1,
29                "solution_cost": 2.0,
30                "vehicle_data": {
31                    "0": {
32                        "task_id": ["Depot", "0", "1", "Depot"],
33                        "route": [0, 0, 1, 0]
34                    }
35                }
36            }
37        }
38    }
39"""
40
41from cuopt_sh_client import CuOptServiceSelfHostClient
42import json
43import time
44
45
46def repoll(cuopt_service_client, solution, repoll_tries):
47    """
48    Repoll the server for solution if it's still processing.
49
50    If solver is still busy solving, the job will be assigned a request id
51    and response is sent back in the format {"reqId": <REQUEST-ID>}.
52    Solver needs to be re-polled for response using this <REQUEST-ID>.
53    """
54    if "reqId" in solution and "response" not in solution:
55        req_id = solution["reqId"]
56        for i in range(repoll_tries):
57            solution = cuopt_service_client.repoll(
58                req_id, response_type="dict"
59            )
60            if "reqId" in solution and "response" in solution:
61                break
62
63            # Sleep for a second before requesting
64            time.sleep(1)
65
66    return solution
67
68
69def main():
70    """Run the basic routing example with cuOpt server."""
71    # Example data for routing problem
72    # The data is structured as per the OpenAPI specification for the server
73    data = {
74        "cost_matrix_data": {"data": {"0": [[0, 1], [1, 0]]}},
75        "task_data": {"task_locations": [0, 1]},
76        "fleet_data": {"vehicle_locations": [[0, 0], [0, 0]]},
77    }
78
79    # If cuOpt is not running on localhost:5000, edit ip and port parameters
80    cuopt_service_client = CuOptServiceSelfHostClient(
81        ip="localhost", port=5000, polling_timeout=25, timeout_exception=False
82    )
83
84    # Submit the routing problem to the server
85    solution = cuopt_service_client.get_optimized_routes(data)
86
87    # Number of repoll requests to be carried out for a successful response
88    repoll_tries = 500
89
90    # Repoll if needed
91    solution = repoll(cuopt_service_client, solution, repoll_tries)
92
93    # Display the solution
94    print(json.dumps(solution, indent=4))
95
96
97if __name__ == "__main__":
98    main()

The response would be as follows:

 1{
 2"response": {
 3    "solver_response": {
 4        "status": 0,
 5        "num_vehicles": 1,
 6        "solution_cost": 2.0,
 7        "objective_values": {
 8            "cost": 2.0
 9        },
10        "vehicle_data": {
11            "0": {
12                "task_id": [
13                    "Depot",
14                    "0",
15                    "1",
16                    "Depot"
17                ],
18                "arrival_stamp": [
19                    0.0,
20                    0.0,
21                    0.0,
22                    0.0
23                ],
24                "type": [
25                    "Depot",
26                    "Delivery",
27                    "Delivery",
28                    "Depot"
29                ],
30                "route": [
31                    0,
32                    0,
33                    1,
34                    0
35                ]
36            }
37        },
38        "initial_solutions": [],
39        "dropped_tasks": {
40            "task_id": [],
41            "task_index": []
42        }
43    },
44    "total_solve_time": 0.1120915412902832
45},
46"reqId": "ebd378a3-c02a-47f3-b0a1-adec81be7cdd"
47}

Initial Solution#

Previously run solutions or uploaded solutions can be used as the initial solution for new requests using previously run reqIds as follows:

initial_solution_example.py

 1# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 2# SPDX-License-Identifier: Apache-2.0
 3"""
 4Initial Solution Routing Example
 5
 6This example demonstrates how to use previous solutions as initial solutions
 7for new routing requests. This can help warm-start the solver and potentially
 8find better solutions faster.
 9
10Features demonstrated:
11    - Using previous reqId as initial solution
12    - Uploading a saved solution for reuse
13    - Providing inline initial solution in data
14    - Deleting saved solutions to free memory
15
16Requirements:
17    - cuOpt server running (default: localhost:5000)
18    - cuopt_sh_client package installed
19
20Note:
21    Initial solutions may not always be accepted. For very small/simple problems,
22    the solver might find the optimal solution immediately without needing the
23    initial solution.
24"""
25
26from cuopt_sh_client import CuOptServiceSelfHostClient
27import json
28
29
30def main():
31    """Run the initial solution routing example."""
32    data = {
33        "cost_matrix_data": {"data": {"0": [[0, 1], [1, 0]]}},
34        "task_data": {"task_locations": [0, 1]},
35        "fleet_data": {"vehicle_locations": [[0, 0], [0, 0]]},
36    }
37
38    # If cuOpt is not running on localhost:5000, edit ip and port parameters
39    cuopt_service_client = CuOptServiceSelfHostClient(
40        ip="localhost", port=5000, timeout_exception=False
41    )
42
43    print("=== Getting Initial Solution ===")
44    # Get initial solution
45    # Set delete_solution to false so it can be used in next request
46    initial_solution = cuopt_service_client.get_optimized_routes(
47        data, delete_solution=False
48    )
49
50    print(f"Initial solution reqId: {initial_solution['reqId']}")
51
52    print("\n=== Uploading Solution for Reuse ===")
53    # Upload a solution returned/saved from previous request as initial solution
54    initial_solution_3 = cuopt_service_client.upload_solution(initial_solution)
55
56    print(f"Uploaded solution reqId: {initial_solution_3['reqId']}")
57
58    print("\n=== Solving with Multiple Initial Solutions ===")
59    # Use previous solution saved in server as initial solution to this request.
60    # That solution is referenced with previous request id.
61    solution = cuopt_service_client.get_optimized_routes(
62        data,
63        initial_ids=[initial_solution["reqId"], initial_solution_3["reqId"]],
64    )
65
66    print(json.dumps(solution, indent=4))
67
68    # Delete saved solution if not required to save space
69    print("\n=== Cleaning Up Saved Solutions ===")
70    cuopt_service_client.delete(initial_solution["reqId"])
71    cuopt_service_client.delete(initial_solution_3["reqId"])
72    print("Saved solutions deleted")
73
74    print("\n=== Using Inline Initial Solution ===")
75    # Another option is to add a solution that was generated
76    # to data model option as follows
77    initial_solution_2 = [
78        {
79            "0": {
80                "task_id": ["Depot", "0", "1", "Depot"],
81                "type": ["Depot", "Delivery", "Delivery", "Depot"],
82            }
83        }
84    ]
85
86    data["initial_solution"] = initial_solution_2
87    solution = cuopt_service_client.get_optimized_routes(data)
88
89    print(json.dumps(solution, indent=4))
90
91    print("\n=== Note ===")
92    print("The initial solution in the response may show 'not accepted',")
93    print("because the problem is too small and the optimal solution is")
94    print("found even before cuOpt could use an initial solution.")
95
96
97if __name__ == "__main__":
98    main()

The initial solution in the response is not accepted, because the problem is too small, and the optimal solution is found even before cuOpt could use an initial solution.

The response would be as follows:

 1{
 2"response": {
 3    "solver_response": {
 4        "status": 0,
 5        "num_vehicles": 1,
 6        "solution_cost": 2.0,
 7        "objective_values": {
 8            "cost": 2.0
 9        },
10        "vehicle_data": {
11            "0": {
12                "task_id": [
13                    "Depot",
14                    "0",
15                    "1",
16                    "Depot"
17                ],
18                "arrival_stamp": [
19                    0.0,
20                    0.0,
21                    0.0,
22                    0.0
23                ],
24                "type": [
25                    "Depot",
26                    "Delivery",
27                    "Delivery",
28                    "Depot"
29                ],
30                "route": [
31                    0,
32                    0,
33                    1,
34                    0
35                ]
36            }
37        },
38        "initial_solutions": [
39            "not accepted",
40        ],
41        "dropped_tasks": {
42            "task_id": [],
43            "task_index": []
44        }
45    },
46    "total_solve_time": 0.06160402297973633
47},
48"reqId": "ebd378a3-c02a-47f3-b0a1-adec81be7cdd"
49}

The data argument to get_optimized_routes may be a dictionary of the format shown in Get Routes Open-API spec. It may also be the path of a file containing such a dictionary as JSON or written using the Python msgpack module. A JSON file may optionally be compressed with zlib. More details on the responses can be found under the responses schema in “get /cuopt/request” and “get /cuopt/solution” API spec.

To enable HTTPS:

  • In the case of the server using public certificates, simply enable https.

     1from cuopt_sh_client import CuOptServiceSelfHostClient
     2
     3data = {"cost_matrix_data": {"data": {"0": [[0,1],[1,0]]}},
     4        "task_data": {"task_locations": [0,1]},
     5        "fleet_data": {"vehicle_locations": [[0,0],[0,0]]}}
     6
     7# If cuOpt is not running on localhost:5000, edit ip and port parameters
     8cuopt_service_client = CuOptServiceSelfHostClient(
     9    ip="localhost",
    10    port=5000,
    11    use_https=True
    12)
    
  • In the case of a self-signed certificate, provide the complete path to the certificate.

     1from cuopt_sh_client import CuOptServiceSelfHostClient
     2
     3data = {"cost_matrix_data": {"data": {"0": [[0,1],[1,0]]}},
     4        "task_data": {"task_locations": [0,1]},
     5        "fleet_data": {"vehicle_locations": [[0,0],[0,0]]}}
     6
     7# If cuOpt is not running on localhost:5000, edit ip and port parameters
     8cuopt_service_client = CuOptServiceSelfHostClient(
     9    ip="localhost",
    10    port=5000,
    11    use_https=True,
    12    self_signed_cert=/complete/path/to/certificate
    13)
    

    You can generate a self-signed certificate easily as follows:

    openssl genrsa -out ca.key 2048
    openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=Acme Root CA" -out ca.crt
    
    openssl req -newkey rsa:2048 -nodes -keyout server.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=*.example.com" -out server.csr
    openssl x509 -req -extfile <(printf "subjectAltName=DNS:example.com,DNS:www.example.com") -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
    

    server.crt and server.key are meant for server, ca.crt is meant for client.

More examples are available in the Examples Notebooks Repository.

Aborting a Running Job in Thin Client#

Please refer to the Aborting a Running Job in Thin Client for more details.

Routing CLI Examples#

Create a data.json file containing this sample data:

Routing Example#

echo '{"cost_matrix_data": {"data": {"0": [[0, 1], [1, 0]]}},
 "task_data": {"task_locations": [0, 1]},
 "fleet_data": {"vehicle_locations": [[0, 0], [0, 0]]}}' > data.json

Invoke the CLI.

# client's default ip address for cuOpt is localhost:5000 if ip/port are not specified
export ip="localhost"
export port=5000
cuopt_sh data.json -i $ip -p $port

Initial Solution in CLI#

To use a previous solution as an initial solution for a new request ID, you are required to save the previous solution, which can be accomplished use option -k. Use the previous reqId in the next request as follows:

# Please update ip and port if the server is running on a different IP address or port
export ip="localhost"
export port=5000
reqId=$(cuopt_sh data.json -i $ip -p $port -k | sed "s/'/\"/g" | jq -r '.reqId')

cuopt_sh data.json -i $ip -p $port -id $reqId

# delete previous saved solutions using follwing command
cuopt_sh -i $ip -p $port -d $reqId

Uploading a Solution#

Users can also upload a solution which might have been saved for later runs.

# Please update ip and port if the server is running on a different IP address or port
export ip="localhost"
export port=5000

# Save solution to a file
cuopt_sh data.json -i $ip -p $port | sed "s/'/\"/g" > solution.json

# Upload the solution and get request-id generated for that
reqId=$(cuopt_sh solution.json -us -i $ip -p $port | sed "s/'/\"/g" | jq -r '.reqId')

# Use this request id for initial solution
cuopt_sh data.json -i $ip -p $port -id $reqId

# delete previous saved solutions using follwing command
cuopt_sh -i $ip -p $port -ds $reqId

Aborting a Running Job In CLI#

Please refer to the Aborting a Running Job In CLI for more in MILP Example.

Note

Please use solver settings while using .mps files.

To enable HTTPS#

  • In the case of the server using public certificates, simply enable https.

    cuopt_sh data.json -s -i $ip -p $port
    
  • In the case of a self-signed certificate, provide the complete path to the certificate.

    cuopt_sh data.json -s -c /complete/path/to/certificate -i $ip -p $port