Example¶
Running cuOpt Server for CVRPTW¶
[1]:
import requests
import pandas as pd
Set-up the IP, Port where the server is running
In this example, the server was running on localhost (ip: 0.0.0.0) port 5000. Also shown here is a utility function for displaying the optimized result.
[2]:
ip = "0.0.0.0"
port = "5000"
url = "http://" + ip + ":" + port + "/cuopt/"
data_params = {"return_data_state": False}
def show_results(res):
print("\n====================== Response ===========================\n")
print("Solver status: ", res["status"])
if res["status"] == 0:
print("Cost : ", res["solution_cost"])
print("Vehicle count: ", res["num_vehicles"])
for veh_id in res["vehicle_data"].keys():
print("\nVehicle ID: ", veh_id)
print("----------")
print("Tasks assigned: ", res["vehicle_data"][veh_id]["tasks"])
data = res["vehicle_data"][veh_id]
routes_and_types = {key:data[key] for key in ["routes", "type"]}
print("Route: \n", pd.DataFrame(routes_and_types))
else:
print("Error: ", res["error"])
print("\n======================= End ===============================\n")
Set waypoint graph
Need to provide offsets, edges, and weights; weights can’t be negative values.
[3]:
way_point_graph = {
"offsets": [0, 3, 5, 9, 11, 13, 15, 17, 18, 19, 20, 21], # noqa
"edges": [1, 2, 9, 0, 7, 0, 3, 4, 10, 2, 4, 2, 5, 6, 9, 5, 8, 1, 6, 0, 5], # noqa
"weights": [1, 1, 2, 1, 2, 1, 1, 1, 3, 2, 3, 2, 1, 2, 1, 3, 4, 2, 3, 1, 1] # noqa
}
matrix_response = requests.post(
url + "set_waypoint_graph", params=data_params, json=way_point_graph
)
print(f"\nWAYPOINT GRAPH ENDPOINT RESPONSE: {matrix_response.json()}\n")
WAYPOINT GRAPH ENDPOINT RESPONSE: {'response': {'valid_graph': [True, 'Valid Waypoint Graph']}}
Set Fleet data
Provide vehicle start and end locations along with vehicle features and capacity.
[4]:
fleet_data = {
"vehicle_locations": [[0, 0], [1, 1], [0, 1], [1, 0], [0, 0]],
"capacities": [[10, 12, 15, 8, 10]],
"vehicle_time_windows": [[0, 80], [1, 40], [3, 30], [5, 80], [20, 100]],
}
fleet_response = requests.post(
url + "set_fleet_data", params=data_params, json=fleet_data
)
print(f"FLEET ENDPOINT RESPONSE: {fleet_response.json()}\n")
FLEET ENDPOINT RESPONSE: {'response': {'valid_fleet_data': [True, 'Valid Fleet Data']}}
Set Task data
Task locations need to be prepended with 0 to account for depot behavior
index 0 for demand states the depot has no demand
index 0 for task_time_windows sets the time windows for the depot
index 0 for service time states the depot has no service time
[5]:
task_data = {
"task_locations": [0, 1, 3, 4, 6, 8],
"demand": [[0, 3, 4, 4, 3, 2]],
"task_time_windows": [
[0, 1000],
[3, 20],
[5, 30],
[1, 20],
[4, 40],
[0, 30],
],
"service_times": [0, 3, 1, 8, 4, 0],
}
task_response = requests.post(
url + "set_task_data", params=data_params, json=task_data
)
print(f"TASK ENDPOINT RESPONSE: {task_response.json()}\n")
TASK ENDPOINT RESPONSE: {'response': {'valid_task_data': [True, 'Valid Task Data']}}
Set solver config
larger problems might require more time and/or more climbers
[6]:
solver_config = {"time_limit": 0.01, "number_of_climbers": 128}
solver_config_response = requests.post(
url + "set_solver_config", params=data_params, json=solver_config
)
print(f"SOLVER CONFIG ENDPOINT RESPONSE: {solver_config_response.json()}\n")
SOLVER CONFIG ENDPOINT RESPONSE: {'response': {'valid_solver_config': [True, 'Valid SolverSettings Config']}}
Solve the problem
By default the solver will use all data provided and any constraints implied by that data. For example, if capacity data was provided within the vehicle data, it is assumed that the user desires to set a capacity constraint on the vehicles. If, however, a user would like to ignore particular aspects of the provided data at solve time that can be achieved by setting some of the available values to False.
[7]:
solve_parameters = {
# Uncomment to disable/ignore constraints.
# "ignore_capacities": True,
# "ignore_vehicle_time_windows": True,
# "ignore_vehicle_break_time_windows": True,
# "ignore_task_time_windows": True,
# "ignore_pickup_and_delivery": True,
"return_status": False,
"return_data_state": False,
}
solver_response = requests.get(
url + "get_optimized_routes", params=solve_parameters
)
print(f"SOLVER RESPONSE: {solver_response.json()}\n")
show_results(solver_response.json()["response"]["solver_response"])
SOLVER RESPONSE: {'response': {'solver_response': {'status': 0, 'num_vehicles': 2, 'solution_cost': 23.0, 'vehicle_data': {'0': {'tasks': [4, 8, 6], 'routes': [0, 2, 4, 5, 6, 8, 6, 5, 9, 0], 'type': ['Start', 'w', 'Task', 'w', 'w', 'Task', 'Task', 'w', 'w', 'End']}, '3': {'tasks': [1, 0, 3], 'routes': [1, 0, 2, 3, 2, 0], 'type': ['Task', 'Task', 'w', 'Task', 'w', 'End']}}}}}
====================== Response ===========================
Solver status: 0
Cost : 23.0
Vehicle count: 2
Vehicle ID: 0
----------
Tasks assigned: [4, 8, 6]
Route:
routes type
0 0 Start
1 2 w
2 4 Task
3 5 w
4 6 w
5 8 Task
6 6 Task
7 5 w
8 9 w
9 0 End
Vehicle ID: 3
----------
Tasks assigned: [1, 0, 3]
Route:
routes type
0 1 Task
1 0 Task
2 2 w
3 3 Task
4 2 w
5 0 End
======================= End ===============================
Now let’s update some data and solve again
[8]:
new_task_data = {
"task_locations": [0, 9, 2, 10, 1, 3],
"demand": [[0, 4, 4, 3, 4, 2]],
}
update_task_response = requests.put(
url + "update_task_data", params=data_params, json=new_task_data
)
print(f"TASK UPDATE ENDPOINT RESPONSE: {update_task_response.json()}\n")
TASK UPDATE ENDPOINT RESPONSE: {'response': {'valid_task_data': [True, 'Valid Task Data']}}
Now with the updated data, let’s solve again: Notice the new route output
[9]:
# Now with the updated data, let's solve again: Notice the new route output
solver_response = requests.get(
url + "get_optimized_routes", params=solve_parameters
)
print(f"SOLVER RESPONSE ON UPDATED DATA: {solver_response.json()}\n")
show_results(solver_response.json()["response"]["solver_response"])
SOLVER RESPONSE ON UPDATED DATA: {'response': {'solver_response': {'status': 0, 'num_vehicles': 2, 'solution_cost': 13.0, 'vehicle_data': {'0': {'tasks': [10, 9], 'routes': [0, 2, 10, 5, 9, 0], 'type': ['Start', 'w', 'Task', 'w', 'Task', 'End']}, '2': {'tasks': [0, 3, 2, 1], 'routes': [0, 2, 3, 2, 0, 1], 'type': ['Task', 'w', 'Task', 'Task', 'w', 'Task']}}}}}
====================== Response ===========================
Solver status: 0
Cost : 13.0
Vehicle count: 2
Vehicle ID: 0
----------
Tasks assigned: [10, 9]
Route:
routes type
0 0 Start
1 2 w
2 10 Task
3 5 w
4 9 Task
5 0 End
Vehicle ID: 2
----------
Tasks assigned: [0, 3, 2, 1]
Route:
routes type
0 0 Task
1 2 w
2 3 Task
3 2 Task
4 0 w
5 1 Task
======================= End ===============================