Update routing Python samples

This commit is contained in:
Laurent Perron
2025-01-29 13:25:44 +01:00
parent 0ce4b910ab
commit cedcb51c3d
31 changed files with 684 additions and 299 deletions

View File

@@ -22,6 +22,7 @@ This problem has 72 different solutions in base 10.
"""
# [START import]
from ortools.constraint_solver import pywrapcp
# [END import]

View File

@@ -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()

View File

@@ -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

View File

@@ -17,6 +17,7 @@
# [START import]
import sys
from ortools.constraint_solver import pywrapcp
# [END import]

View File

@@ -17,6 +17,7 @@
# [START import]
from ortools.constraint_solver import pywrapcp
# [END import]

View File

@@ -18,6 +18,7 @@
# [START import]
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
# [END import]

View File

@@ -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]

View File

@@ -19,6 +19,7 @@
import math
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
# [END import]

View File

@@ -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]

View File

@@ -18,6 +18,7 @@
# [START import]
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
# [END import]

View File

@@ -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"

View File

@@ -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):

View File

@@ -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]

View File

@@ -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

View File

@@ -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

View File

@@ -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]

View File

@@ -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]

View File

@@ -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]

View File

@@ -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]

View File

@@ -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]

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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):

View File

@@ -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]

View File

@@ -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

View File

@@ -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):

View File

@@ -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]

View File

@@ -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))

View File

@@ -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

View File

@@ -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]