From cedcb51c3dcc5615e2be7bd65ef8d1579c2e9990 Mon Sep 17 00:00:00 2001 From: Laurent Perron Date: Wed, 29 Jan 2025 13:25:44 +0100 Subject: [PATCH] Update routing Python samples --- .../constraint_solver/samples/cp_is_fun_cp.py | 1 + .../constraint_solver/samples/cvrp_reload.py | 247 +++++----- .../constraint_solver/samples/cvrptw_break.py | 3 + .../constraint_solver/samples/nqueens_cp.py | 1 + .../samples/simple_cp_program.py | 1 + .../samples/simple_routing_program.py | 1 + ortools/constraint_solver/samples/tsp.py | 1 + .../samples/tsp_circuit_board.py | 1 + .../constraint_solver/samples/tsp_cities.py | 3 +- .../samples/tsp_distance_matrix.py | 1 + ortools/constraint_solver/samples/vrp.py | 11 +- .../constraint_solver/samples/vrp_breaks.py | 3 + .../samples/vrp_breaks_from_start.py | 81 ++-- .../constraint_solver/samples/vrp_capacity.py | 3 + .../samples/vrp_drop_nodes.py | 3 + .../samples/vrp_global_span.py | 4 + .../samples/vrp_initial_routes.py | 4 + .../samples/vrp_items_to_deliver.py | 448 ++++++++++++++---- .../constraint_solver/samples/vrp_node_max.py | 5 + .../samples/vrp_nodes_indices.py | 54 ++- .../samples/vrp_pickup_delivery.py | 3 + .../samples/vrp_pickup_delivery_fifo.py | 3 + .../samples/vrp_pickup_delivery_lifo.py | 3 + .../samples/vrp_resources.py | 3 + .../samples/vrp_solution_callback.py | 5 + .../samples/vrp_starts_ends.py | 3 + .../samples/vrp_time_windows.py | 3 + .../samples/vrp_time_windows_per_vehicles.py | 71 +-- .../constraint_solver/samples/vrp_tokens.py | 3 + .../samples/vrp_with_time_limit.py | 3 + .../samples/vrptw_store_solution_data.py | 7 + 31 files changed, 684 insertions(+), 299 deletions(-) diff --git a/ortools/constraint_solver/samples/cp_is_fun_cp.py b/ortools/constraint_solver/samples/cp_is_fun_cp.py index fbb230be13..fbc820aee2 100755 --- a/ortools/constraint_solver/samples/cp_is_fun_cp.py +++ b/ortools/constraint_solver/samples/cp_is_fun_cp.py @@ -22,6 +22,7 @@ This problem has 72 different solutions in base 10. """ # [START import] from ortools.constraint_solver import pywrapcp + # [END import] diff --git a/ortools/constraint_solver/samples/cvrp_reload.py b/ortools/constraint_solver/samples/cvrp_reload.py index 4cbbb5eb7f..a86316b39c 100755 --- a/ortools/constraint_solver/samples/cvrp_reload.py +++ b/ortools/constraint_solver/samples/cvrp_reload.py @@ -78,52 +78,69 @@ def create_data_model(): (3, 7), (6, 7), (0, 8), - (7, 8) + (7, 8), ] # Compute locations in meters using the block dimension defined as follow # Manhattan average block: 750ft x 264ft -> 228m x 80m # here we use: 114m x 80m city block # src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance' - data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations] - data['num_locations'] = len(data['locations']) - data['demands'] = \ - [0, # depot - -_capacity, # unload depot_first - -_capacity, # unload depot_second - -_capacity, # unload depot_third - -_capacity, # unload depot_fourth - -_capacity, # unload depot_fifth - 3, 3, # 1, 2 - 3, 4, # 3, 4 - 3, 4, # 5, 6 - 8, 8, # 7, 8 - 3, 3, # 9,10 - 3, 3, # 11,12 - 4, 4, # 13, 14 - 8, 8] # 15, 16 - data['time_per_demand_unit'] = 5 # 5 minutes/unit - data['time_windows'] = \ - [(0, 0), # depot - (0, 1000), # unload depot_first - (0, 1000), # unload depot_second - (0, 1000), # unload depot_third - (0, 1000), # unload depot_fourth - (0, 1000), # unload depot_fifth - (75, 850), (75, 850), # 1, 2 - (60, 700), (45, 550), # 3, 4 - (0, 800), (50, 600), # 5, 6 - (0, 1000), (10, 200), # 7, 8 - (0, 1000), (75, 850), # 9, 10 - (85, 950), (5, 150), # 11, 12 - (15, 250), (10, 200), # 13, 14 - (45, 550), (30, 400)] # 15, 16 - data['num_vehicles'] = 3 - data['vehicle_capacity'] = _capacity - data['vehicle_max_distance'] = 10_000 - data['vehicle_max_time'] = 1_500 - data[ - 'vehicle_speed'] = 5 * 60 / 3.6 # Travel speed: 5km/h to convert in m/min - data['depot'] = 0 + data["locations"] = [(l[0] * 114, l[1] * 80) for l in _locations] + data["num_locations"] = len(data["locations"]) + data["demands"] = [ + 0, # depot + -_capacity, # unload depot_first + -_capacity, # unload depot_second + -_capacity, # unload depot_third + -_capacity, # unload depot_fourth + -_capacity, # unload depot_fifth + 3, + 3, # 1, 2 + 3, + 4, # 3, 4 + 3, + 4, # 5, 6 + 8, + 8, # 7, 8 + 3, + 3, # 9,10 + 3, + 3, # 11,12 + 4, + 4, # 13, 14 + 8, + 8, + ] # 15, 16 + data["time_per_demand_unit"] = 5 # 5 minutes/unit + data["time_windows"] = [ + (0, 0), # depot + (0, 1000), # unload depot_first + (0, 1000), # unload depot_second + (0, 1000), # unload depot_third + (0, 1000), # unload depot_fourth + (0, 1000), # unload depot_fifth + (75, 850), + (75, 850), # 1, 2 + (60, 700), + (45, 550), # 3, 4 + (0, 800), + (50, 600), # 5, 6 + (0, 1000), + (10, 200), # 7, 8 + (0, 1000), + (75, 850), # 9, 10 + (85, 950), + (5, 150), # 11, 12 + (15, 250), + (10, 200), # 13, 14 + (45, 550), + (30, 400), + ] # 15, 16 + data["num_vehicles"] = 3 + data["vehicle_capacity"] = _capacity + data["vehicle_max_distance"] = 10_000 + data["vehicle_max_time"] = 1_500 + data["vehicle_speed"] = 5 * 60 / 3.6 # Travel speed: 5km/h to convert in m/min + data["depot"] = 0 return data @@ -132,30 +149,29 @@ def create_data_model(): ####################### def manhattan_distance(position_1, position_2): """Computes the Manhattan distance between two points""" - return (abs(position_1[0] - position_2[0]) + - abs(position_1[1] - position_2[1])) + return abs(position_1[0] - position_2[0]) + abs(position_1[1] - position_2[1]) def create_distance_evaluator(data): """Creates callback to return distance between points.""" _distances = {} # precompute distance between location to have distance callback in O(1) - for from_node in range(data['num_locations']): + for from_node in range(data["num_locations"]): _distances[from_node] = {} - for to_node in range(data['num_locations']): + for to_node in range(data["num_locations"]): if from_node == to_node: _distances[from_node][to_node] = 0 # Forbid start/end/reload node to be consecutive. elif from_node in range(6) and to_node in range(6): - _distances[from_node][to_node] = data['vehicle_max_distance'] + _distances[from_node][to_node] = data["vehicle_max_distance"] else: - _distances[from_node][to_node] = (manhattan_distance( - data['locations'][from_node], data['locations'][to_node])) + _distances[from_node][to_node] = manhattan_distance( + data["locations"][from_node], data["locations"][to_node] + ) def distance_evaluator(manager, from_node, to_node): """Returns the manhattan distance between the two nodes""" - return _distances[manager.IndexToNode(from_node)][manager.IndexToNode( - to_node)] + return _distances[manager.IndexToNode(from_node)][manager.IndexToNode(to_node)] return distance_evaluator @@ -163,13 +179,14 @@ def create_distance_evaluator(data): def add_distance_dimension(routing, manager, data, distance_evaluator_index): """Add Global Span constraint""" del manager - distance = 'Distance' + distance = "Distance" routing.AddDimension( distance_evaluator_index, 0, # null slack - data['vehicle_max_distance'], # maximum distance per vehicle + data["vehicle_max_distance"], # maximum distance per vehicle True, # start cumul to zero - distance) + distance, + ) distance_dimension = routing.GetDimensionOrDie(distance) # Try to minimize the max distance among vehicles. # /!\ It doesn't mean the standard deviation is minimized @@ -178,7 +195,7 @@ def add_distance_dimension(routing, manager, data, distance_evaluator_index): def create_demand_evaluator(data): """Creates callback to get demands at each location.""" - _demands = data['demands'] + _demands = data["demands"] def demand_evaluator(manager, from_node): """Returns the demand of the current node""" @@ -189,14 +206,15 @@ def create_demand_evaluator(data): def add_capacity_constraints(routing, manager, data, demand_evaluator_index): """Adds capacity constraint""" - vehicle_capacity = data['vehicle_capacity'] - capacity = 'Capacity' + vehicle_capacity = data["vehicle_capacity"] + capacity = "Capacity" routing.AddDimension( demand_evaluator_index, vehicle_capacity, vehicle_capacity, True, # start cumul to zero - capacity) + capacity, + ) # Add Slack for reseting to zero unload depot nodes. # e.g. vehicle with load 10/15 arrives at node 1 (depot unload) @@ -208,7 +226,7 @@ def add_capacity_constraints(routing, manager, data, demand_evaluator_index): routing.AddDisjunction([node_index], 0) # Allow to drop regular node with a cost. - for node in range(6, len(data['demands'])): + for node in range(6, len(data["demands"])): node_index = manager.NodeToIndex(node) capacity_dimension.SlackVar(node_index).SetValue(0) routing.AddDisjunction([node_index], 100_000) @@ -219,52 +237,56 @@ def create_time_evaluator(data): def service_time(data, node): """Gets the service time for the specified location.""" - return abs(data['demands'][node]) * data['time_per_demand_unit'] + return abs(data["demands"][node]) * data["time_per_demand_unit"] def travel_time(data, from_node, to_node): """Gets the travel times between two locations.""" if from_node == to_node: travel_time = 0 else: - travel_time = manhattan_distance( - data['locations'][from_node], - data['locations'][to_node]) / data['vehicle_speed'] + travel_time = ( + manhattan_distance( + data["locations"][from_node], data["locations"][to_node] + ) + / data["vehicle_speed"] + ) return travel_time _total_time = {} # precompute total time to have time callback in O(1) - for from_node in range(data['num_locations']): + for from_node in range(data["num_locations"]): _total_time[from_node] = {} - for to_node in range(data['num_locations']): + for to_node in range(data["num_locations"]): if from_node == to_node: _total_time[from_node][to_node] = 0 else: _total_time[from_node][to_node] = int( - service_time(data, from_node) + - travel_time(data, from_node, to_node)) + service_time(data, from_node) + + travel_time(data, from_node, to_node) + ) def time_evaluator(manager, from_node, to_node): """Returns the total time between the two nodes""" - return _total_time[manager.IndexToNode(from_node)][manager.IndexToNode( - to_node)] + return _total_time[manager.IndexToNode(from_node)][manager.IndexToNode(to_node)] return time_evaluator def add_time_window_constraints(routing, manager, data, time_evaluator): """Add Time windows constraint""" - time = 'Time' - max_time = data['vehicle_max_time'] + time = "Time" + max_time = data["vehicle_max_time"] routing.AddDimension( time_evaluator, max_time, # allow waiting time max_time, # maximum time per vehicle False, # don't force start cumul to zero since we are giving TW to start nodes - time) + time, + ) time_dimension = routing.GetDimensionOrDie(time) # Add time window constraints for each location except depot # and 'copy' the slack var in the solution object (aka Assignment) to print it - for location_idx, time_window in enumerate(data['time_windows']): + for location_idx, time_window in enumerate(data["time_windows"]): if location_idx == 0: continue index = manager.NodeToIndex(location_idx) @@ -272,70 +294,77 @@ def add_time_window_constraints(routing, manager, data, time_evaluator): routing.AddToAssignment(time_dimension.SlackVar(index)) # Add time window constraints for each vehicle start node # and 'copy' the slack var in the solution object (aka Assignment) to print it - for vehicle_id in range(data['num_vehicles']): + for vehicle_id in range(data["num_vehicles"]): index = routing.Start(vehicle_id) - time_dimension.CumulVar(index).SetRange(data['time_windows'][0][0], - data['time_windows'][0][1]) + time_dimension.CumulVar(index).SetRange( + data["time_windows"][0][0], data["time_windows"][0][1] + ) routing.AddToAssignment(time_dimension.SlackVar(index)) # Warning: Slack var is not defined for vehicle's end node - #routing.AddToAssignment(time_dimension.SlackVar(self.routing.End(vehicle_id))) + # routing.AddToAssignment(time_dimension.SlackVar(self.routing.End(vehicle_id))) ########### # Printer # ########### -def print_solution(data, manager, routing, assignment): # pylint:disable=too-many-locals +def print_solution( + data, manager, routing, assignment +): # pylint:disable=too-many-locals """Prints assignment on console""" - print(f'Objective: {assignment.ObjectiveValue()}') + print(f"Objective: {assignment.ObjectiveValue()}") total_distance = 0 total_load = 0 total_time = 0 - capacity_dimension = routing.GetDimensionOrDie('Capacity') - time_dimension = routing.GetDimensionOrDie('Time') + capacity_dimension = routing.GetDimensionOrDie("Capacity") + time_dimension = routing.GetDimensionOrDie("Time") + distance_dimension = routing.GetDimensionOrDie("Distance") dropped = [] for order in range(6, routing.nodes()): index = manager.NodeToIndex(order) if assignment.Value(routing.NextVar(index)) == index: dropped.append(order) - print(f'dropped orders: {dropped}') + print(f"dropped orders: {dropped}") + dropped = [] for reload in range(1, 6): index = manager.NodeToIndex(reload) if assignment.Value(routing.NextVar(index)) == index: dropped.append(reload) - print(f'dropped reload stations: {dropped}') + print(f"dropped reload stations: {dropped}") - for vehicle_id in range(data['num_vehicles']): + for vehicle_id in range(data["num_vehicles"]): + if not routing.IsVehicleUsed(assignment, vehicle_id): + continue index = routing.Start(vehicle_id) - plan_output = f'Route for vehicle {vehicle_id}:\n' + plan_output = f"Route for vehicle {vehicle_id}:\n" distance = 0 while not routing.IsEnd(index): load_var = capacity_dimension.CumulVar(index) time_var = time_dimension.CumulVar(index) plan_output += ( - f' {manager.IndexToNode(index)} ' - f'Load({assignment.Min(load_var)}) ' - f'Time({assignment.Min(time_var)},{assignment.Max(time_var)}) ->' + f" {manager.IndexToNode(index)} " + f"Load({assignment.Min(load_var)}) " + f"Time({assignment.Min(time_var)},{assignment.Max(time_var)}) ->" ) previous_index = index index = assignment.Value(routing.NextVar(index)) - distance += routing.GetArcCostForVehicle(previous_index, index, - vehicle_id) + distance += distance_dimension.GetTransitValue(previous_index, index, vehicle_id) load_var = capacity_dimension.CumulVar(index) time_var = time_dimension.CumulVar(index) plan_output += ( - f' {manager.IndexToNode(index)} ' - f'Load({assignment.Min(load_var)}) ' - f'Time({assignment.Min(time_var)},{assignment.Max(time_var)})\n') - plan_output += f'Distance of the route: {distance}m\n' - plan_output += f'Load of the route: {assignment.Min(load_var)}\n' - plan_output += f'Time of the route: {assignment.Min(time_var)}min\n' + f" {manager.IndexToNode(index)} " + f"Load({assignment.Min(load_var)}) " + f"Time({assignment.Min(time_var)},{assignment.Max(time_var)})\n" + ) + plan_output += f"Distance of the route: {distance}m\n" + plan_output += f"Load of the route: {assignment.Min(load_var)}\n" + plan_output += f"Time of the route: {assignment.Min(time_var)}min\n" print(plan_output) total_distance += distance total_load += assignment.Min(load_var) total_time += assignment.Min(time_var) - print(f'Total Distance of all routes: {total_distance}m') - print(f'Total Load of all routes: {total_load}') - print(f'Total Time of all routes: {total_time}min') + print(f"Total Distance of all routes: {total_distance}m") + print(f"Total Load of all routes: {total_load}") + print(f"Total Time of all routes: {total_time}min") ######## @@ -347,15 +376,17 @@ def main(): data = create_data_model() # Create the routing index manager - manager = pywrapcp.RoutingIndexManager(data['num_locations'], - data['num_vehicles'], data['depot']) + manager = pywrapcp.RoutingIndexManager( + data["num_locations"], data["num_vehicles"], data["depot"] + ) # Create Routing Model routing = pywrapcp.RoutingModel(manager) # Define weight of each edge distance_evaluator_index = routing.RegisterTransitCallback( - partial(create_distance_evaluator(data), manager)) + partial(create_distance_evaluator(data), manager) + ) routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index) # Add Distance constraint to minimize the longuest route @@ -363,20 +394,24 @@ def main(): # Add Capacity constraint demand_evaluator_index = routing.RegisterUnaryTransitCallback( - partial(create_demand_evaluator(data), manager)) + partial(create_demand_evaluator(data), manager) + ) add_capacity_constraints(routing, manager, data, demand_evaluator_index) # Add Time Window constraint time_evaluator_index = routing.RegisterTransitCallback( - partial(create_time_evaluator(data), manager)) + partial(create_time_evaluator(data), manager) + ) add_time_window_constraints(routing, manager, data, time_evaluator_index) # Setting first solution heuristic (cheapest addition). search_parameters = pywrapcp.DefaultRoutingSearchParameters() search_parameters.first_solution_strategy = ( - routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member + routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC + ) # pylint: disable=no-member search_parameters.local_search_metaheuristic = ( - routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH) + routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH + ) search_parameters.time_limit.FromSeconds(3) # Solve the problem. @@ -387,5 +422,5 @@ def main(): print("No solution found !") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ortools/constraint_solver/samples/cvrptw_break.py b/ortools/constraint_solver/samples/cvrptw_break.py index e755306a3f..c3922567f9 100755 --- a/ortools/constraint_solver/samples/cvrptw_break.py +++ b/ortools/constraint_solver/samples/cvrptw_break.py @@ -27,6 +27,7 @@ import functools from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -240,6 +241,8 @@ def print_solution( capacity_dimension = routing.GetDimensionOrDie("Capacity") time_dimension = routing.GetDimensionOrDie("Time") for vehicle_id in range(data["num_vehicles"]): + if not routing.IsVehicleUsed(assignment, vehicle_id): + continue index = routing.Start(vehicle_id) plan_output = f"Route for vehicle {vehicle_id}:\n" distance = 0 diff --git a/ortools/constraint_solver/samples/nqueens_cp.py b/ortools/constraint_solver/samples/nqueens_cp.py index eed0d217b2..21aa7e5761 100755 --- a/ortools/constraint_solver/samples/nqueens_cp.py +++ b/ortools/constraint_solver/samples/nqueens_cp.py @@ -17,6 +17,7 @@ # [START import] import sys from ortools.constraint_solver import pywrapcp + # [END import] diff --git a/ortools/constraint_solver/samples/simple_cp_program.py b/ortools/constraint_solver/samples/simple_cp_program.py index 7c62799558..7fdb0af5d8 100755 --- a/ortools/constraint_solver/samples/simple_cp_program.py +++ b/ortools/constraint_solver/samples/simple_cp_program.py @@ -17,6 +17,7 @@ # [START import] from ortools.constraint_solver import pywrapcp + # [END import] diff --git a/ortools/constraint_solver/samples/simple_routing_program.py b/ortools/constraint_solver/samples/simple_routing_program.py index a28047ae3c..04c7abc988 100755 --- a/ortools/constraint_solver/samples/simple_routing_program.py +++ b/ortools/constraint_solver/samples/simple_routing_program.py @@ -18,6 +18,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] diff --git a/ortools/constraint_solver/samples/tsp.py b/ortools/constraint_solver/samples/tsp.py index 859a814617..6e100b8c75 100755 --- a/ortools/constraint_solver/samples/tsp.py +++ b/ortools/constraint_solver/samples/tsp.py @@ -22,6 +22,7 @@ http://en.wikipedia.org/wiki/Travelling_salesperson_problem. # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] diff --git a/ortools/constraint_solver/samples/tsp_circuit_board.py b/ortools/constraint_solver/samples/tsp_circuit_board.py index 388bc95d89..76f62601ee 100755 --- a/ortools/constraint_solver/samples/tsp_circuit_board.py +++ b/ortools/constraint_solver/samples/tsp_circuit_board.py @@ -19,6 +19,7 @@ import math from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] diff --git a/ortools/constraint_solver/samples/tsp_cities.py b/ortools/constraint_solver/samples/tsp_cities.py index dcba215b0b..c17007fdb9 100755 --- a/ortools/constraint_solver/samples/tsp_cities.py +++ b/ortools/constraint_solver/samples/tsp_cities.py @@ -18,6 +18,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -59,8 +60,8 @@ def print_solution(manager, routing, solution): index = solution.Value(routing.NextVar(index)) route_distance += routing.GetArcCostForVehicle(previous_index, index, 0) plan_output += f" {manager.IndexToNode(index)}\n" - print(plan_output) plan_output += f"Route distance: {route_distance}miles\n" + print(plan_output) # [END solution_printer] diff --git a/ortools/constraint_solver/samples/tsp_distance_matrix.py b/ortools/constraint_solver/samples/tsp_distance_matrix.py index 7c4624b9d0..ff3c5ef284 100755 --- a/ortools/constraint_solver/samples/tsp_distance_matrix.py +++ b/ortools/constraint_solver/samples/tsp_distance_matrix.py @@ -18,6 +18,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] diff --git a/ortools/constraint_solver/samples/vrp.py b/ortools/constraint_solver/samples/vrp.py index 6192a30f49..011a422004 100755 --- a/ortools/constraint_solver/samples/vrp.py +++ b/ortools/constraint_solver/samples/vrp.py @@ -26,6 +26,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -65,16 +66,18 @@ def print_solution(data, manager, routing, solution): """Prints solution on console.""" print(f"Objective: {solution.ObjectiveValue()}") total_distance = 0 - for vehicle_id in range(data["num_vehicles"]): - index = routing.Start(vehicle_id) - plan_output = f"Route for vehicle {vehicle_id}:\n" + for vehicle_index in range(manager.GetNumberOfVehicles()): + if not routing.IsVehicleUsed(solution, vehicle_index): + continue + index = routing.Start(vehicle_index) + plan_output = f"Route for vehicle {vehicle_index}:\n" route_distance = 0 while not routing.IsEnd(index): plan_output += f" {manager.IndexToNode(index)} ->" previous_index = index index = solution.Value(routing.NextVar(index)) route_distance += routing.GetArcCostForVehicle( - previous_index, index, vehicle_id + previous_index, index, vehicle_index ) plan_output += f" {manager.IndexToNode(index)}\n" plan_output += f"Distance of the route: {route_distance}m\n" diff --git a/ortools/constraint_solver/samples/vrp_breaks.py b/ortools/constraint_solver/samples/vrp_breaks.py index 7f97e1c2fb..2313920363 100755 --- a/ortools/constraint_solver/samples/vrp_breaks.py +++ b/ortools/constraint_solver/samples/vrp_breaks.py @@ -26,6 +26,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -82,6 +83,8 @@ def print_solution(manager, routing, solution): time_dimension = routing.GetDimensionOrDie("Time") total_time = 0 for vehicle_id in range(manager.GetNumberOfVehicles()): + if not routing.IsVehicleUsed(solution, vehicle_id): + continue index = routing.Start(vehicle_id) plan_output = f"Route for vehicle {vehicle_id}:\n" while not routing.IsEnd(index): diff --git a/ortools/constraint_solver/samples/vrp_breaks_from_start.py b/ortools/constraint_solver/samples/vrp_breaks_from_start.py index 77d9e16082..eb1be8b1dd 100755 --- a/ortools/constraint_solver/samples/vrp_breaks_from_start.py +++ b/ortools/constraint_solver/samples/vrp_breaks_from_start.py @@ -26,6 +26,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -33,9 +34,9 @@ from ortools.constraint_solver import pywrapcp def create_data_model(): """Stores the data for the problem.""" data = {} - data['num_vehicles'] = 4 - data['depot'] = 0 - data['time_matrix'] = [ + data["num_vehicles"] = 4 + data["depot"] = 0 + data["time_matrix"] = [ [0, 27, 38, 34, 29, 13, 25, 9, 15, 9, 26, 25, 19, 17, 23, 38, 33], [27, 0, 34, 15, 9, 25, 36, 17, 34, 37, 54, 29, 24, 33, 50, 43, 60], [38, 34, 0, 49, 43, 25, 13, 40, 23, 37, 20, 63, 58, 56, 39, 77, 37], @@ -55,9 +56,9 @@ def create_data_model(): [33, 60, 37, 67, 62, 35, 24, 42, 25, 23, 17, 42, 36, 26, 9, 39, 0], ] # 15 min of service time - data['service_time'] = [15] * len(data['time_matrix']) - data['service_time'][data['depot']] = 0 - assert len(data['time_matrix']) == len(data['service_time']) + data["service_time"] = [15] * len(data["time_matrix"]) + data["service_time"][data["depot"]] = 0 + assert len(data["time_matrix"]) == len(data["service_time"]) return data # [END data_model] @@ -65,38 +66,42 @@ def create_data_model(): # [START solution_printer] def print_solution(manager, routing, solution): """Prints solution on console.""" - print(f'Objective: {solution.ObjectiveValue()}') + print(f"Objective: {solution.ObjectiveValue()}") - print('Breaks:') + print("Breaks:") intervals = solution.IntervalVarContainer() for i in range(intervals.Size()): brk = intervals.Element(i) if brk.PerformedValue() == 1: - print(f'{brk.Var().Name()}: ' + - f'Start({brk.StartValue()}) Duration({brk.DurationValue()})') + print( + f"{brk.Var().Name()}: " + + f"Start({brk.StartValue()}) Duration({brk.DurationValue()})" + ) else: - print(f'{brk.Var().Name()}: Unperformed') + print(f"{brk.Var().Name()}: Unperformed") - time_dimension = routing.GetDimensionOrDie('Time') + time_dimension = routing.GetDimensionOrDie("Time") total_time = 0 for vehicle_id in range(manager.GetNumberOfVehicles()): + if not routing.IsVehicleUsed(solution, vehicle_id): + continue index = routing.Start(vehicle_id) - plan_output = f'Route for vehicle {vehicle_id}:\n' + plan_output = f"Route for vehicle {vehicle_id}:\n" while not routing.IsEnd(index): time_var = time_dimension.CumulVar(index) if routing.IsStart(index): start_time = solution.Value(time_var) - plan_output += f'{manager.IndexToNode(index)} ' - plan_output += f'Time({solution.Value(time_var)}) -> ' + plan_output += f"{manager.IndexToNode(index)} " + plan_output += f"Time({solution.Value(time_var)}) -> " index = solution.Value(routing.NextVar(index)) time_var = time_dimension.CumulVar(index) - plan_output += f'{manager.IndexToNode(index)} ' - plan_output += f'Time({solution.Value(time_var)})' + plan_output += f"{manager.IndexToNode(index)} " + plan_output += f"Time({solution.Value(time_var)})" print(plan_output) route_time = solution.Value(time_var) - start_time - print(f'Time of the route: {route_time}min\n') + print(f"Time of the route: {route_time}min\n") total_time += route_time - print(f'Total time of all routes: {total_time}min') + print(f"Total time of all routes: {total_time}min") # [END solution_printer] @@ -109,8 +114,9 @@ def main(): # Create the routing index manager. # [START index_manager] - manager = pywrapcp.RoutingIndexManager(len(data['time_matrix']), - data['num_vehicles'], data['depot']) + manager = pywrapcp.RoutingIndexManager( + len(data["time_matrix"]), data["num_vehicles"], data["depot"] + ) # [END index_manager] # Create Routing Model. @@ -118,7 +124,6 @@ def main(): routing = pywrapcp.RoutingModel(manager) # [END routing_model] - # Create and register a transit callback. # [START transit_callback] def time_callback(from_index, to_index): @@ -126,7 +131,7 @@ def main(): # Convert from routing variable Index to time matrix NodeIndex. from_node = manager.IndexToNode(from_index) to_node = manager.IndexToNode(to_index) - return data['time_matrix'][from_node][to_node] + return data["time_matrix"][from_node][to_node] transit_callback_index = routing.RegisterTransitCallback(time_callback) # [END transit_callback] @@ -137,13 +142,14 @@ def main(): # [END arc_cost] # Add Time Windows constraint. - time = 'Time' + time = "Time" routing.AddDimension( transit_callback_index, 10, # need optional waiting time to place break 180, # maximum time per vehicle False, # Don't force start cumul to zero. - time) + time, + ) time_dimension = routing.GetDimensionOrDie(time) time_dimension.SetGlobalSpanCostCoefficient(10) @@ -158,29 +164,32 @@ def main(): node_visit_transit = [0] * routing.Size() for index in range(routing.Size()): node = manager.IndexToNode(index) - node_visit_transit[index] = data['service_time'][node] + node_visit_transit[index] = data["service_time"][node] # Add a break lasting 5 minutes, start between 25 and 45 minutes after route start for v in range(manager.GetNumberOfVehicles()): start_var = time_dimension.CumulVar(routing.Start(v)) - break_start = routing.solver().Sum( - [routing.solver().IntVar(25, 45), start_var]) + break_start = routing.solver().Sum([routing.solver().IntVar(25, 45), start_var]) break_intervals = [ - routing.solver().FixedDurationIntervalVar(break_start, 5, - f'Break for vehicle {v}') + routing.solver().FixedDurationIntervalVar( + break_start, 5, f"Break for vehicle {v}" + ) ] - time_dimension.SetBreakIntervalsOfVehicle(break_intervals, v, - node_visit_transit) + time_dimension.SetBreakIntervalsOfVehicle( + break_intervals, v, node_visit_transit + ) # [END break_constraint] # Setting first solution heuristic. # [START parameters] search_parameters = pywrapcp.DefaultRoutingSearchParameters() search_parameters.first_solution_strategy = ( - routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) + routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC + ) search_parameters.local_search_metaheuristic = ( - routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH) + routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH + ) # search_parameters.log_search = True search_parameters.time_limit.FromSeconds(2) # [END parameters] @@ -195,10 +204,10 @@ def main(): if solution: print_solution(manager, routing, solution) else: - print('No solution found !') + print("No solution found !") # [END print_solution] -if __name__ == '__main__': +if __name__ == "__main__": main() # [END program] diff --git a/ortools/constraint_solver/samples/vrp_capacity.py b/ortools/constraint_solver/samples/vrp_capacity.py index 4c516e0317..f720a7f5b7 100755 --- a/ortools/constraint_solver/samples/vrp_capacity.py +++ b/ortools/constraint_solver/samples/vrp_capacity.py @@ -18,6 +18,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -63,6 +64,8 @@ def print_solution(data, manager, routing, solution): total_distance = 0 total_load = 0 for vehicle_id in range(data["num_vehicles"]): + if not routing.IsVehicleUsed(solution, vehicle_id): + continue index = routing.Start(vehicle_id) plan_output = f"Route for vehicle {vehicle_id}:\n" route_distance = 0 diff --git a/ortools/constraint_solver/samples/vrp_drop_nodes.py b/ortools/constraint_solver/samples/vrp_drop_nodes.py index d513dbc0fa..cb62ed8e17 100755 --- a/ortools/constraint_solver/samples/vrp_drop_nodes.py +++ b/ortools/constraint_solver/samples/vrp_drop_nodes.py @@ -18,6 +18,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -72,6 +73,8 @@ def print_solution(data, manager, routing, assignment): total_distance = 0 total_load = 0 for vehicle_id in range(data["num_vehicles"]): + if not routing.IsVehicleUsed(assignment, vehicle_id): + continue index = routing.Start(vehicle_id) plan_output = f"Route for vehicle {vehicle_id}:\n" route_distance = 0 diff --git a/ortools/constraint_solver/samples/vrp_global_span.py b/ortools/constraint_solver/samples/vrp_global_span.py index 4182eea0ef..3d88b0dbee 100755 --- a/ortools/constraint_solver/samples/vrp_global_span.py +++ b/ortools/constraint_solver/samples/vrp_global_span.py @@ -26,6 +26,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -66,6 +67,8 @@ def print_solution(data, manager, routing, solution): print(f"Objective: {solution.ObjectiveValue()}") max_route_distance = 0 for vehicle_id in range(data["num_vehicles"]): + if not routing.IsVehicleUsed(solution, vehicle_id): + continue index = routing.Start(vehicle_id) plan_output = f"Route for vehicle {vehicle_id}:\n" route_distance = 0 @@ -82,6 +85,7 @@ def print_solution(data, manager, routing, solution): max_route_distance = max(route_distance, max_route_distance) print(f"Maximum of the route distances: {max_route_distance}m") + # [END solution_printer] diff --git a/ortools/constraint_solver/samples/vrp_initial_routes.py b/ortools/constraint_solver/samples/vrp_initial_routes.py index 842e82b5a3..eecccb054c 100755 --- a/ortools/constraint_solver/samples/vrp_initial_routes.py +++ b/ortools/constraint_solver/samples/vrp_initial_routes.py @@ -18,6 +18,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -68,6 +69,8 @@ def print_solution(data, manager, routing, solution): print(f"Objective: {solution.ObjectiveValue()}") max_route_distance = 0 for vehicle_id in range(data["num_vehicles"]): + if not routing.IsVehicleUsed(solution, vehicle_id): + continue index = routing.Start(vehicle_id) plan_output = f"Route for vehicle {vehicle_id}:\n" route_distance = 0 @@ -84,6 +87,7 @@ def print_solution(data, manager, routing, solution): max_route_distance = max(route_distance, max_route_distance) print(f"Maximum of the route distances: {max_route_distance}m") + # [END solution_printer] diff --git a/ortools/constraint_solver/samples/vrp_items_to_deliver.py b/ortools/constraint_solver/samples/vrp_items_to_deliver.py index fade08d880..7225f0113f 100755 --- a/ortools/constraint_solver/samples/vrp_items_to_deliver.py +++ b/ortools/constraint_solver/samples/vrp_items_to_deliver.py @@ -24,17 +24,17 @@ from ortools.constraint_solver import pywrapcp def create_data_model(): """Stores the data for the problem.""" data = {} - data['num_vehicles'] = 2 + data["num_vehicles"] = 2 # [START starts_ends] - data['starts'] = [0] * data['num_vehicles'] - data['ends'] = [1] * data['num_vehicles'] - assert len(data['starts']) == data['num_vehicles'] - assert len(data['ends']) == data['num_vehicles'] + data["starts"] = [0] * data["num_vehicles"] + data["ends"] = [1] * data["num_vehicles"] + assert len(data["starts"]) == data["num_vehicles"] + assert len(data["ends"]) == data["num_vehicles"] # [END starts_ends] # [START demands_capacities] # Need 11 X and 13 Y - data['providers_x'] = [ + data["providers_x"] = [ 0, # start -11, # end 2, # X supply 1 @@ -53,7 +53,7 @@ def create_data_model(): 0, # Y supply 5 0, # Y supply 6 ] - data['providers_y'] = [ + data["providers_y"] = [ 0, # start -13, # ends 0, # X supply 1 @@ -72,83 +72,338 @@ def create_data_model(): 3, # Y supply 5 5, # Y supply 6 ] - data['vehicle_capacities_x'] = [15] * data['num_vehicles'] - data['vehicle_capacities_y'] = [15] * data['num_vehicles'] - assert len(data['vehicle_capacities_x']) == data['num_vehicles'] - assert len(data['vehicle_capacities_y']) == data['num_vehicles'] + data["vehicle_capacities_x"] = [15] * data["num_vehicles"] + data["vehicle_capacities_y"] = [15] * data["num_vehicles"] + assert len(data["vehicle_capacities_x"]) == data["num_vehicles"] + assert len(data["vehicle_capacities_y"]) == data["num_vehicles"] # [END demands_capacities] - data['distance_matrix'] = [ + data["distance_matrix"] = [ [ - 0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, - 468, 776, 662 + 0, + 548, + 776, + 696, + 582, + 274, + 502, + 194, + 308, + 194, + 536, + 502, + 388, + 354, + 468, + 776, + 662, ], [ - 548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, - 1016, 868, 1210 + 548, + 0, + 684, + 308, + 194, + 502, + 730, + 354, + 696, + 742, + 1084, + 594, + 480, + 674, + 1016, + 868, + 1210, ], [ - 776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, - 1130, 788, 1552, 754 + 776, + 684, + 0, + 992, + 878, + 502, + 274, + 810, + 468, + 742, + 400, + 1278, + 1164, + 1130, + 788, + 1552, + 754, ], [ - 696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, - 1164, 560, 1358 + 696, + 308, + 992, + 0, + 114, + 650, + 878, + 502, + 844, + 890, + 1232, + 514, + 628, + 822, + 1164, + 560, + 1358, ], [ - 582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, - 1050, 674, 1244 + 582, + 194, + 878, + 114, + 0, + 536, + 764, + 388, + 730, + 776, + 1118, + 400, + 514, + 708, + 1050, + 674, + 1244, ], [ - 274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, - 514, 1050, 708 + 274, + 502, + 502, + 650, + 536, + 0, + 228, + 308, + 194, + 240, + 582, + 776, + 662, + 628, + 514, + 1050, + 708, ], [ - 502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, - 514, 1278, 480 + 502, + 730, + 274, + 878, + 764, + 228, + 0, + 536, + 194, + 468, + 354, + 1004, + 890, + 856, + 514, + 1278, + 480, ], [ - 194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, - 662, 742, 856 + 194, + 354, + 810, + 502, + 388, + 308, + 536, + 0, + 342, + 388, + 730, + 468, + 354, + 320, + 662, + 742, + 856, ], [ - 308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, - 320, 1084, 514 + 308, + 696, + 468, + 844, + 730, + 194, + 194, + 342, + 0, + 274, + 388, + 810, + 696, + 662, + 320, + 1084, + 514, ], [ - 194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, - 274, 810, 468 + 194, + 742, + 742, + 890, + 776, + 240, + 468, + 388, + 274, + 0, + 342, + 536, + 422, + 388, + 274, + 810, + 468, ], [ - 536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, - 730, 388, 1152, 354 + 536, + 1084, + 400, + 1232, + 1118, + 582, + 354, + 730, + 388, + 342, + 0, + 878, + 764, + 730, + 388, + 1152, + 354, ], [ - 502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, - 308, 650, 274, 844 + 502, + 594, + 1278, + 514, + 400, + 776, + 1004, + 468, + 810, + 536, + 878, + 0, + 114, + 308, + 650, + 274, + 844, ], [ - 388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, - 536, 388, 730 + 388, + 480, + 1164, + 628, + 514, + 662, + 890, + 354, + 696, + 422, + 764, + 114, + 0, + 194, + 536, + 388, + 730, ], [ - 354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, - 342, 422, 536 + 354, + 674, + 1130, + 822, + 708, + 628, + 856, + 320, + 662, + 388, + 730, + 308, + 194, + 0, + 342, + 422, + 536, ], [ - 468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, - 342, 0, 764, 194 + 468, + 1016, + 788, + 1164, + 1050, + 514, + 514, + 662, + 320, + 274, + 388, + 650, + 536, + 342, + 0, + 764, + 194, ], [ - 776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, - 388, 422, 764, 0, 798 + 776, + 868, + 1552, + 560, + 674, + 1050, + 1278, + 742, + 1084, + 810, + 1152, + 274, + 388, + 422, + 764, + 0, + 798, ], [ - 662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, - 536, 194, 798, 0 + 662, + 1210, + 754, + 1358, + 1244, + 708, + 480, + 856, + 514, + 468, + 354, + 844, + 730, + 536, + 194, + 798, + 0, ], ] - assert len(data['providers_x']) == len(data['distance_matrix']) - assert len(data['providers_y']) == len(data['distance_matrix']) + assert len(data["providers_x"]) == len(data["distance_matrix"]) + assert len(data["providers_y"]) == len(data["distance_matrix"]) return data # [END data_model] @@ -156,46 +411,48 @@ def create_data_model(): # [START solution_printer] def print_solution(data, manager, routing, assignment): """Prints assignment on console.""" - print(f'Objective: {assignment.ObjectiveValue()}') + print(f"Objective: {assignment.ObjectiveValue()}") # Display dropped nodes. - dropped_nodes = 'Dropped nodes:' + dropped_nodes = "Dropped nodes:" for node in range(routing.Size()): if routing.IsStart(node) or routing.IsEnd(node): continue if assignment.Value(routing.NextVar(node)) == node: - dropped_nodes += f' {manager.IndexToNode(node)}' + dropped_nodes += f" {manager.IndexToNode(node)}" print(dropped_nodes) # Display routes total_distance = 0 total_load_x = 0 total_load_y = 0 for vehicle_id in range(manager.GetNumberOfVehicles()): + if not routing.IsVehicleUsed(assignment, vehicle_id): + continue index = routing.Start(vehicle_id) - plan_output = f'Route for vehicle {vehicle_id}:\n' + plan_output = f"Route for vehicle {vehicle_id}:\n" route_distance = 0 route_load_x = 0 route_load_y = 0 while not routing.IsEnd(index): node_index = manager.IndexToNode(index) - route_load_x += data['providers_x'][node_index] - route_load_y += data['providers_y'][node_index] - plan_output += f' {node_index} Load(X:{route_load_x}, Y:{route_load_y}) -> ' + route_load_x += data["providers_x"][node_index] + route_load_y += data["providers_y"][node_index] + plan_output += f" {node_index} Load(X:{route_load_x}, Y:{route_load_y}) -> " previous_index = index previous_node_index = node_index index = assignment.Value(routing.NextVar(index)) node_index = manager.IndexToNode(index) - #route_distance += routing.GetArcCostForVehicle(previous_index, index, vehicle_id) - route_distance += data['distance_matrix'][previous_node_index][node_index] + # route_distance += routing.GetArcCostForVehicle(previous_index, index, vehicle_id) + route_distance += data["distance_matrix"][previous_node_index][node_index] node_index = manager.IndexToNode(index) - plan_output += f' {node_index} Load({route_load_x}, {route_load_y})\n' - plan_output += f'Distance of the route: {route_distance}m\n' - plan_output += f'Load of the route: X:{route_load_x}, Y:{route_load_y}\n' + plan_output += f" {node_index} Load({route_load_x}, {route_load_y})\n" + plan_output += f"Distance of the route: {route_distance}m\n" + plan_output += f"Load of the route: X:{route_load_x}, Y:{route_load_y}\n" print(plan_output) total_distance += route_distance total_load_x += route_load_x total_load_y += route_load_y - print(f'Total Distance of all routes: {total_distance}m') - print(f'Total load of all routes: X:{total_load_x}, Y:{total_load_y}') + print(f"Total Distance of all routes: {total_distance}m") + print(f"Total load of all routes: X:{total_load_x}, Y:{total_load_y}") # [END solution_printer] @@ -208,9 +465,9 @@ def main(): # Create the routing index manager. # [START index_manager] - manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']), - data['num_vehicles'], data['starts'], - data['ends']) + manager = pywrapcp.RoutingIndexManager( + len(data["distance_matrix"]), data["num_vehicles"], data["starts"], data["ends"] + ) # [END index_manager] # Create Routing Model. @@ -226,7 +483,7 @@ def main(): # Convert from routing variable Index to distance matrix NodeIndex. from_node = manager.IndexToNode(from_index) to_node = manager.IndexToNode(to_index) - return data['distance_matrix'][from_node][to_node] + return data["distance_matrix"][from_node][to_node] transit_callback_index = routing.RegisterTransitCallback(distance_callback) # [END transit_callback] @@ -238,13 +495,14 @@ def main(): # Add Distance constraint. # [START distance_constraint] - dimension_name = 'Distance' + dimension_name = "Distance" routing.AddDimension( transit_callback_index, 0, # no slack 2000, # vehicle maximum travel distance True, # start cumul to zero - dimension_name) + dimension_name, + ) distance_dimension = routing.GetDimensionOrDie(dimension_name) # Minimize the longest road distance_dimension.SetGlobalSpanCostCoefficient(100) @@ -257,65 +515,69 @@ def main(): """Returns the demand of the node.""" # Convert from routing variable Index to demands NodeIndex. from_node = manager.IndexToNode(from_index) - return data['providers_x'][from_node] + return data["providers_x"][from_node] - demand_callback_x_index = routing.RegisterUnaryTransitCallback( - demand_callback_x) + demand_callback_x_index = routing.RegisterUnaryTransitCallback(demand_callback_x) routing.AddDimensionWithVehicleCapacity( demand_callback_x_index, 0, # null capacity slack - data['vehicle_capacities_x'], # vehicle maximum capacities + data["vehicle_capacities_x"], # vehicle maximum capacities True, # start cumul to zero - 'Load_x') + "Load_x", + ) def demand_callback_y(from_index): """Returns the demand of the node.""" # Convert from routing variable Index to demands NodeIndex. from_node = manager.IndexToNode(from_index) - return data['providers_y'][from_node] + return data["providers_y"][from_node] - demand_callback_y_index = routing.RegisterUnaryTransitCallback( - demand_callback_y) + demand_callback_y_index = routing.RegisterUnaryTransitCallback(demand_callback_y) routing.AddDimensionWithVehicleCapacity( demand_callback_y_index, 0, # null capacity slack - data['vehicle_capacities_y'], # vehicle maximum capacities + data["vehicle_capacities_y"], # vehicle maximum capacities True, # start cumul to zero - 'Load_y') + "Load_y", + ) # [END capacity_constraint] # Add constraint at end solver = routing.solver() - load_x_dim = routing.GetDimensionOrDie('Load_x') - load_y_dim = routing.GetDimensionOrDie('Load_y') + load_x_dim = routing.GetDimensionOrDie("Load_x") + load_y_dim = routing.GetDimensionOrDie("Load_y") ends = [] for v in range(manager.GetNumberOfVehicles()): ends.append(routing.End(v)) - node_end = data['ends'][0] + node_end = data["ends"][0] solver.Add( - solver.Sum([load_x_dim.CumulVar(l) - for l in ends]) >= -data['providers_x'][node_end]) + solver.Sum([load_x_dim.CumulVar(l) for l in ends]) + >= -data["providers_x"][node_end] + ) solver.Add( - solver.Sum([load_y_dim.CumulVar(l) - for l in ends]) >= -data['providers_y'][node_end]) - #solver.Add(load_y_dim.CumulVar(end) >= -data['providers_y'][node_end]) + solver.Sum([load_y_dim.CumulVar(l) for l in ends]) + >= -data["providers_y"][node_end] + ) + # solver.Add(load_y_dim.CumulVar(end) >= -data['providers_y'][node_end]) # Allow to freely drop any nodes. penalty = 0 - for node in range(0, len(data['distance_matrix'])): - if node not in data['starts'] and node not in data['ends']: + for node in range(0, len(data["distance_matrix"])): + if node not in data["starts"] and node not in data["ends"]: routing.AddDisjunction([manager.NodeToIndex(node)], penalty) # Setting first solution heuristic. # [START parameters] search_parameters = pywrapcp.DefaultRoutingSearchParameters() search_parameters.first_solution_strategy = ( - routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) + routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC + ) search_parameters.local_search_metaheuristic = ( - routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH) + routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH + ) # Sets a time limit; default is 100 milliseconds. - #search_parameters.log_search = True + # search_parameters.log_search = True search_parameters.time_limit.FromSeconds(1) # [END parameters] @@ -329,10 +591,10 @@ def main(): if solution: print_solution(data, manager, routing, solution) else: - print('no solution found !') + print("no solution found !") # [END print_solution] -if __name__ == '__main__': +if __name__ == "__main__": main() # [END program] diff --git a/ortools/constraint_solver/samples/vrp_node_max.py b/ortools/constraint_solver/samples/vrp_node_max.py index 9d4441444e..5c1092382b 100755 --- a/ortools/constraint_solver/samples/vrp_node_max.py +++ b/ortools/constraint_solver/samples/vrp_node_max.py @@ -22,6 +22,7 @@ road multiply by a constant factor (4200) # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -74,6 +75,7 @@ def create_data_model(): data["depot"] = 0 return data + # [END data_model] @@ -86,6 +88,8 @@ def print_solution(data, manager, routing, solution): dim_two = routing.GetDimensionOrDie("Two") for vehicle_id in range(data["num_vehicles"]): + if not routing.IsVehicleUsed(solution, vehicle_id): + continue index = routing.Start(vehicle_id) plan_output = f"Route for vehicle {vehicle_id}:\n" route_distance = 0 @@ -117,6 +121,7 @@ def print_solution(data, manager, routing, solution): max_route_distance = max(route_distance, max_route_distance) print(f"Maximum of the route distances: {max_route_distance}m") + # [END solution_printer] diff --git a/ortools/constraint_solver/samples/vrp_nodes_indices.py b/ortools/constraint_solver/samples/vrp_nodes_indices.py index 1567bb39b5..6d6fc605a4 100755 --- a/ortools/constraint_solver/samples/vrp_nodes_indices.py +++ b/ortools/constraint_solver/samples/vrp_nodes_indices.py @@ -51,60 +51,64 @@ def main(): manager = pywrapcp.RoutingIndexManager(locations, vehicles, starts, ends) routing = pywrapcp.RoutingModel(manager) - print('Starts/Ends:') - header = '| |' - separator = '|---|' - v_starts = '| start |' - v_ends = '| end |' + print("Starts/Ends:") + header = "| |" + separator = "|---|" + v_starts = "| start |" + v_ends = "| end |" for v in range(manager.GetNumberOfVehicles()): - header += f' vehicle {v} |' - separator += '---|' - v_starts += f' {starts[v]} |' - v_ends += f' {ends[v]} |' + header += f" vehicle {v} |" + separator += "---|" + v_starts += f" {starts[v]} |" + v_ends += f" {ends[v]} |" print(header) print(separator) print(v_starts) print(v_ends) - print('\nNodes:') + print("\nNodes:") print( - '| locations | manager.GetNumberOfNodes | manager.GetNumberOfIndices | routing.nodes | routing.Size |' + "| locations | manager.GetNumberOfNodes | manager.GetNumberOfIndices |" + " routing.nodes | routing.Size |" ) - print('|---|---|---|---|---|') + print("|---|---|---|---|---|") print( - f'| {locations} | {manager.GetNumberOfNodes()} | {manager.GetNumberOfIndices()} | {routing.nodes()} | {routing.Size()} |' + f"| {locations} | {manager.GetNumberOfNodes()} |" + f" {manager.GetNumberOfIndices()} | {routing.nodes()} |" + f" {routing.Size()} |" ) - print('\nLocations:') - print('| node | index | routing.IsStart | routing.IsEnd |') - print('|---|---|---|---|') + print("\nLocations:") + print("| node | index | routing.IsStart | routing.IsEnd |") + print("|---|---|---|---|") for node in range(manager.GetNumberOfNodes()): if node in starts or node in ends: continue index = manager.NodeToIndex(node) print( - f'| {node} | {index} | {routing.IsStart(index)} | {routing.IsEnd(index)} |' + f"| {node} | {index} | {routing.IsStart(index)} |" + f" {routing.IsEnd(index)} |" ) - print('\nStart/End:') - print( - '| vehicle | Start/end | node | index | routing.IsStart | routing.IsEnd |' - ) - print('|---|---|---|---|---|---|') + print("\nStart/End:") + print("| vehicle | Start/end | node | index | routing.IsStart | routing.IsEnd |") + print("|---|---|---|---|---|---|") for v in range(manager.GetNumberOfVehicles()): start_index = routing.Start(v) start_node = manager.IndexToNode(start_index) print( - f'| {v} | start | {start_node} | {start_index} | {routing.IsStart(start_index)} | {routing.IsEnd(start_index)} |' + f"| {v} | start | {start_node} | {start_index} |" + f" {routing.IsStart(start_index)} | {routing.IsEnd(start_index)} |" ) for v in range(manager.GetNumberOfVehicles()): end_index = routing.End(v) end_node = manager.IndexToNode(end_index) print( - f'| {v} | end | {end_node} | {end_index} | {routing.IsStart(end_index)} | {routing.IsEnd(end_index)} |' + f"| {v} | end | {end_node} | {end_index} |" + f" {routing.IsStart(end_index)} | {routing.IsEnd(end_index)} |" ) -if __name__ == '__main__': +if __name__ == "__main__": main() # [END program] diff --git a/ortools/constraint_solver/samples/vrp_pickup_delivery.py b/ortools/constraint_solver/samples/vrp_pickup_delivery.py index 945d8927eb..e5b7912d36 100755 --- a/ortools/constraint_solver/samples/vrp_pickup_delivery.py +++ b/ortools/constraint_solver/samples/vrp_pickup_delivery.py @@ -18,6 +18,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -70,6 +71,8 @@ def print_solution(data, manager, routing, solution): print(f"Objective: {solution.ObjectiveValue()}") total_distance = 0 for vehicle_id in range(data["num_vehicles"]): + if not routing.IsVehicleUsed(solution, vehicle_id): + continue index = routing.Start(vehicle_id) plan_output = f"Route for vehicle {vehicle_id}:\n" route_distance = 0 diff --git a/ortools/constraint_solver/samples/vrp_pickup_delivery_fifo.py b/ortools/constraint_solver/samples/vrp_pickup_delivery_fifo.py index 4aaa91f530..83641a21e9 100755 --- a/ortools/constraint_solver/samples/vrp_pickup_delivery_fifo.py +++ b/ortools/constraint_solver/samples/vrp_pickup_delivery_fifo.py @@ -18,6 +18,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -70,6 +71,8 @@ def print_solution(data, manager, routing, assignment): print(f"Objective: {assignment.ObjectiveValue()}") total_distance = 0 for vehicle_id in range(data["num_vehicles"]): + if not routing.IsVehicleUsed(assignment, vehicle_id): + continue index = routing.Start(vehicle_id) plan_output = f"Route for vehicle {vehicle_id}:\n" route_distance = 0 diff --git a/ortools/constraint_solver/samples/vrp_pickup_delivery_lifo.py b/ortools/constraint_solver/samples/vrp_pickup_delivery_lifo.py index 072228f7a0..3f9c144cf4 100755 --- a/ortools/constraint_solver/samples/vrp_pickup_delivery_lifo.py +++ b/ortools/constraint_solver/samples/vrp_pickup_delivery_lifo.py @@ -18,6 +18,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -70,6 +71,8 @@ def print_solution(data, manager, routing, assignment): print(f"Objective: {assignment.ObjectiveValue()}") total_distance = 0 for vehicle_id in range(data["num_vehicles"]): + if not routing.IsVehicleUsed(assignment, vehicle_id): + continue index = routing.Start(vehicle_id) plan_output = f"Route for vehicle {vehicle_id}:\n" route_distance = 0 diff --git a/ortools/constraint_solver/samples/vrp_resources.py b/ortools/constraint_solver/samples/vrp_resources.py index 6fff3dd170..ba46a5ffcc 100755 --- a/ortools/constraint_solver/samples/vrp_resources.py +++ b/ortools/constraint_solver/samples/vrp_resources.py @@ -18,6 +18,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -81,6 +82,8 @@ def print_solution(data, manager, routing, solution): time_dimension = routing.GetDimensionOrDie("Time") total_time = 0 for vehicle_id in range(data["num_vehicles"]): + if not routing.IsVehicleUsed(solution, vehicle_id): + continue index = routing.Start(vehicle_id) plan_output = f"Route for vehicle {vehicle_id}:\n" while not routing.IsEnd(index): diff --git a/ortools/constraint_solver/samples/vrp_solution_callback.py b/ortools/constraint_solver/samples/vrp_solution_callback.py index 881d5fd61f..7d1093e068 100755 --- a/ortools/constraint_solver/samples/vrp_solution_callback.py +++ b/ortools/constraint_solver/samples/vrp_solution_callback.py @@ -28,6 +28,7 @@ import weakref from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -72,6 +73,8 @@ def print_solution( total_distance = 0 for vehicle_id in range(routing_manager.GetNumberOfVehicles()): index = routing_model.Start(vehicle_id) + if routing_model.IsEnd(routing_model.NextVar(index).Value()): + continue plan_output = f"Route for vehicle {vehicle_id}:\n" route_distance = 0 while not routing_model.IsEnd(index): @@ -87,6 +90,7 @@ def print_solution( total_distance += route_distance print(f"Total Distance of all routes: {total_distance}m") + # [END solution_callback_printer] @@ -116,6 +120,7 @@ class SolutionCallback: if self._counter > self._counter_limit: self._routing_model_ref().solver().FinishCurrentSearch() + # [END solution_callback] diff --git a/ortools/constraint_solver/samples/vrp_starts_ends.py b/ortools/constraint_solver/samples/vrp_starts_ends.py index 4181891b04..5342a78a26 100755 --- a/ortools/constraint_solver/samples/vrp_starts_ends.py +++ b/ortools/constraint_solver/samples/vrp_starts_ends.py @@ -18,6 +18,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -61,6 +62,8 @@ def print_solution(data, manager, routing, solution): print(f"Objective: {solution.ObjectiveValue()}") max_route_distance = 0 for vehicle_id in range(data["num_vehicles"]): + if not routing.IsVehicleUsed(solution, vehicle_id): + continue index = routing.Start(vehicle_id) plan_output = f"Route for vehicle {vehicle_id}:\n" route_distance = 0 diff --git a/ortools/constraint_solver/samples/vrp_time_windows.py b/ortools/constraint_solver/samples/vrp_time_windows.py index aee4db8a40..80fac449b2 100755 --- a/ortools/constraint_solver/samples/vrp_time_windows.py +++ b/ortools/constraint_solver/samples/vrp_time_windows.py @@ -18,6 +18,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -76,6 +77,8 @@ def print_solution(data, manager, routing, solution): time_dimension = routing.GetDimensionOrDie("Time") total_time = 0 for vehicle_id in range(data["num_vehicles"]): + if not routing.IsVehicleUsed(solution, vehicle_id): + continue index = routing.Start(vehicle_id) plan_output = f"Route for vehicle {vehicle_id}:\n" while not routing.IsEnd(index): diff --git a/ortools/constraint_solver/samples/vrp_time_windows_per_vehicles.py b/ortools/constraint_solver/samples/vrp_time_windows_per_vehicles.py index b27301a773..ebf9bb8115 100755 --- a/ortools/constraint_solver/samples/vrp_time_windows_per_vehicles.py +++ b/ortools/constraint_solver/samples/vrp_time_windows_per_vehicles.py @@ -32,6 +32,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -39,7 +40,7 @@ from ortools.constraint_solver import pywrapcp def create_data_model(): """Stores the data for the problem.""" data = {} - data['time_matrix'] = [ + data["time_matrix"] = [ [0, 6, 9, 8, 7, 3, 6, 2, 3, 2, 6, 6, 4, 4, 5, 9, 7], [6, 0, 8, 3, 2, 6, 8, 4, 8, 8, 13, 7, 5, 8, 12, 10, 14], [9, 8, 0, 11, 10, 6, 3, 9, 5, 8, 4, 15, 14, 13, 9, 18, 9], @@ -58,8 +59,8 @@ def create_data_model(): [9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9], [7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0], ] - data['num_vehicles'] = 4 - data['depot'] = 0 + data["num_vehicles"] = 4 + data["depot"] = 0 return data # [END data_model] @@ -67,9 +68,9 @@ def create_data_model(): # [START solution_printer] def print_solution(manager, routing, assignment): """Prints solution on console.""" - print(f'Objective: {assignment.ObjectiveValue()}') + print(f"Objective: {assignment.ObjectiveValue()}") # Display dropped nodes. - dropped_nodes = 'Dropped nodes:' + dropped_nodes = "Dropped nodes:" for index in range(routing.Size()): if routing.IsStart(index) or routing.IsEnd(index): continue @@ -79,15 +80,15 @@ def print_solution(manager, routing, assignment): original = node while original > 16: original = original - 16 - dropped_nodes += f' {node}({original})' + dropped_nodes += f" {node}({original})" else: - dropped_nodes += f' {node}' + dropped_nodes += f" {node}" print(dropped_nodes) # Display routes - time_dimension = routing.GetDimensionOrDie('Time') + time_dimension = routing.GetDimensionOrDie("Time") total_time = 0 for vehicle_id in range(manager.GetNumberOfVehicles()): - plan_output = f'Route for vehicle {vehicle_id}:\n' + plan_output = f"Route for vehicle {vehicle_id}:\n" index = routing.Start(vehicle_id) start_time = 0 while not routing.IsEnd(index): @@ -97,22 +98,22 @@ def print_solution(manager, routing, assignment): original = node while original > 16: original = original - 16 - plan_output += f'{node}({original})' + plan_output += f"{node}({original})" else: - plan_output += f'{node}' - plan_output += f' Time:{assignment.Value(time_var)} -> ' + plan_output += f"{node}" + plan_output += f" Time:{assignment.Value(time_var)} -> " if start_time == 0: start_time = assignment.Value(time_var) index = assignment.Value(routing.NextVar(index)) time_var = time_dimension.CumulVar(index) node = manager.IndexToNode(index) - plan_output += f'{node} Time:{assignment.Value(time_var)}\n' + plan_output += f"{node} Time:{assignment.Value(time_var)}\n" end_time = assignment.Value(time_var) duration = end_time - start_time - plan_output += f'Duration of the route:{duration}min\n' + plan_output += f"Duration of the route:{duration}min\n" print(plan_output) total_time += duration - print(f'Total duration of all routes: {total_time}min') + print(f"Total duration of all routes: {total_time}min") # [END solution_printer] @@ -126,9 +127,8 @@ def main(): # Create the routing index manager. # [START index_manager] manager = pywrapcp.RoutingIndexManager( - 1 + 16 * 4, # number of locations - data['num_vehicles'], - data['depot']) + 1 + 16 * 4, data["num_vehicles"], data["depot"] # number of locations + ) # [END index_manager] # Create Routing Model. @@ -152,9 +152,9 @@ def main(): to_node = to_node - 16 # add service of 25min for each location (except depot) service_time = 0 - if from_node != data['depot']: + if from_node != data["depot"]: service_time = 25 - return data['time_matrix'][from_node][to_node] + service_time + return data["time_matrix"][from_node][to_node] + service_time transit_callback_index = routing.RegisterTransitCallback(time_callback) # [END transit_callback] @@ -166,17 +166,18 @@ def main(): # Add Time Windows constraint. # [START time_windows_constraint] - time = 'Time' + time = "Time" routing.AddDimension( transit_callback_index, 0, # allow waiting time (0 min) 1020, # maximum time per vehicle (9 hours) False, # Don't force start cumul to zero. - time) + time, + ) time_dimension = routing.GetDimensionOrDie(time) # Add time window constraints for each location except depot. for location_idx in range(17): - if location_idx == data['depot']: + if location_idx == data["depot"]: continue # Vehicle 0 location TW: [9am, 11am] index_0 = manager.NodeToIndex(location_idx) @@ -203,34 +204,36 @@ def main(): routing.AddDisjunction([index_0, index_1, index_2, index_3], penalty, 1) # Add time window constraints for each vehicle start node. - depot_idx = data['depot'] - for vehicle_id in range(data['num_vehicles']): + depot_idx = data["depot"] + for vehicle_id in range(data["num_vehicles"]): index = routing.Start(vehicle_id) time_dimension.CumulVar(index).SetRange(480, 1020) # (8am, 5pm) # Add time window constraints for each vehicle end node. - depot_idx = data['depot'] - for vehicle_id in range(data['num_vehicles']): + depot_idx = data["depot"] + for vehicle_id in range(data["num_vehicles"]): index = routing.End(vehicle_id) time_dimension.CumulVar(index).SetRange(480, 1020) # (8am, 5pm) # [END time_windows_constraint] # Instantiate route start and end times to produce feasible times. # [START depot_start_end_times] - for i in range(data['num_vehicles']): + for i in range(data["num_vehicles"]): routing.AddVariableMinimizedByFinalizer( - time_dimension.CumulVar(routing.Start(i))) - routing.AddVariableMinimizedByFinalizer( - time_dimension.CumulVar(routing.End(i))) + time_dimension.CumulVar(routing.Start(i)) + ) + routing.AddVariableMinimizedByFinalizer(time_dimension.CumulVar(routing.End(i))) # [END depot_start_end_times] # Setting first solution heuristic. # [START parameters] search_parameters = pywrapcp.DefaultRoutingSearchParameters() search_parameters.first_solution_strategy = ( - routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) + routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC + ) search_parameters.local_search_metaheuristic = ( - routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH) + routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH + ) search_parameters.time_limit.FromSeconds(1) # [END parameters] @@ -248,6 +251,6 @@ def main(): # [END print_solution] -if __name__ == '__main__': +if __name__ == "__main__": main() # [END program] diff --git a/ortools/constraint_solver/samples/vrp_tokens.py b/ortools/constraint_solver/samples/vrp_tokens.py index 88a2297d9d..e5ede08333 100755 --- a/ortools/constraint_solver/samples/vrp_tokens.py +++ b/ortools/constraint_solver/samples/vrp_tokens.py @@ -17,6 +17,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -59,6 +60,8 @@ def print_solution(manager, routing, solution): total_distance = 0 total_token = 0 for vehicle_id in range(manager.GetNumberOfVehicles()): + if not routing.IsVehicleUsed(solution, vehicle_id): + continue plan_output = f"Route for vehicle {vehicle_id}:\n" index = routing.Start(vehicle_id) total_token += solution.Value(token_dimension.CumulVar(index)) diff --git a/ortools/constraint_solver/samples/vrp_with_time_limit.py b/ortools/constraint_solver/samples/vrp_with_time_limit.py index b3a7104b8a..fb286ed275 100755 --- a/ortools/constraint_solver/samples/vrp_with_time_limit.py +++ b/ortools/constraint_solver/samples/vrp_with_time_limit.py @@ -18,6 +18,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -27,6 +28,8 @@ def print_solution(manager, routing, solution): print(f"Objective: {solution.ObjectiveValue()}") max_route_distance = 0 for vehicle_id in range(manager.GetNumberOfVehicles()): + if not routing.IsVehicleUsed(solution, vehicle_id): + continue index = routing.Start(vehicle_id) plan_output = f"Route for vehicle {vehicle_id}:\n" route_distance = 0 diff --git a/ortools/constraint_solver/samples/vrptw_store_solution_data.py b/ortools/constraint_solver/samples/vrptw_store_solution_data.py index 611afb61e9..bab40b558f 100755 --- a/ortools/constraint_solver/samples/vrptw_store_solution_data.py +++ b/ortools/constraint_solver/samples/vrptw_store_solution_data.py @@ -18,6 +18,7 @@ # [START import] from ortools.constraint_solver import routing_enums_pb2 from ortools.constraint_solver import pywrapcp + # [END import] @@ -68,6 +69,7 @@ def create_data_model(): data["depot"] = 0 return data + # [END data_model] @@ -77,6 +79,8 @@ def print_solution(routes, cumul_data): total_time = 0 route_str = "" for i, route in enumerate(routes): + if len(route) <= 2: + continue route_str += "Route " + str(i) + ":\n" start_time = cumul_data[i][0][0] end_time = cumul_data[i][0][1] @@ -106,6 +110,7 @@ def print_solution(routes, cumul_data): route_str += f"Total time: {total_time}min" print(route_str) + # [END solution_printer] @@ -124,6 +129,7 @@ def get_routes(solution, routing, manager): routes.append(route) return routes + # [END get_routes] @@ -148,6 +154,7 @@ def get_cumul_data(solution, routing, dimension): cumul_data.append(route_data) return cumul_data + # [END get_cumulative_data]