Update routing Python samples
This commit is contained in:
@@ -22,6 +22,7 @@ This problem has 72 different solutions in base 10.
|
||||
"""
|
||||
# [START import]
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# [START import]
|
||||
import sys
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
# [START import]
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
# [START import]
|
||||
from ortools.constraint_solver import routing_enums_pb2
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import math
|
||||
from ortools.constraint_solver import routing_enums_pb2
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
# [START import]
|
||||
from ortools.constraint_solver import routing_enums_pb2
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user