"""
Created on Thursday Sep 09 13:45:42 2021
@author: Angelo Antonio Manzatto
"""
##################################################################################################
# Libraries
##################################################################################################
import random
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
##################################################################################################
# Methods
##################################################################################################
def print_solution(data, manager, routing, solution, dimensions):
dropped_vehicles = []
dash = 120 * "-"
star = 120 * "*"
print(star)
print("VEHICLE ITINERARIES - START")
print(star)
"""Prints solution on console."""
for vehicle_id in range(data['num_vehicles']):
# Check if vehicle is used
if not routing.IsVehicleUsed(solution, vehicle_id):
dropped_vehicles.append(vehicle_id)
continue
index = routing.Start(vehicle_id)
print(dash)
print("Route for vehicle: {}"
.format(vehicle_id))
print(dash)
print('{:<4s}{:>15s}{:>15s}{:>15s}{:>15s}{:>15s}{:>15s}{:>15s}'
.format("Location",
"Distance",
"Cumul",
"Route",
"Client",
"ClientA",
"ClientB",
"ClientC"
))
print(dash)
currt_distance = 0
while not routing.IsEnd(index):
cumul_distance = solution.Value(dimensions['distance'].CumulVar(index))
currt_distance = cumul_distance - currt_distance
cumul_clientA = solution.Value(dimensions['ClientA'].CumulVar(solution.Value(routing.NextVar(index))))
cumul_clientB = solution.Value(dimensions['ClientB'].CumulVar(solution.Value(routing.NextVar(index))))
cumul_clientC = solution.Value(dimensions['ClientC'].CumulVar(solution.Value(routing.NextVar(index))))
node = manager.IndexToNode(index)
route = []
for r in data['routes']:
if node in r:
route = r
break
client = ""
for c in data['clients']:
if node in data['clients'][c]:
client = c
details = "{:4d}".format(node)
details += "{:>15.0f}".format(currt_distance)
details += "{:>18.0f}".format(cumul_distance)
details += "{:>15}".format(str(route))
details += "{:>15}".format(client)
details += "{:>15}".format(cumul_clientA)
details += "{:>15}".format(cumul_clientB)
details += "{:>15}".format(cumul_clientC)
print(details)
currt_distance = cumul_distance
index = solution.Value(routing.NextVar(index))
node = manager.IndexToNode(index)
route = []
for r in data['routes']:
if node in r:
route = r
break
client = ""
for c in data['clients']:
if node in data['clients'][c]:
client = c
cumul_distance = solution.Value(dimensions['distance'].CumulVar(index))
currt_distance = cumul_distance - currt_distance
cumul_clientA = solution.Value(dimensions['ClientA'].CumulVar(index))
cumul_clientB = solution.Value(dimensions['ClientB'].CumulVar(index))
cumul_clientC = solution.Value(dimensions['ClientC'].CumulVar(index))
details = "{:4d}".format(node)
details += "{:>15.0f}".format(currt_distance)
details += "{:>18.0f}".format(cumul_distance)
details += "{:>15}".format("")
details += "{:>15}".format(client)
details += "{:>15}".format("")
details += "{:>15}".format("")
details += "{:>15}".format("")
print(details)
print(dash)
##################################################################################################
# Data model
##################################################################################################
data = {}
data['distance_matrix'] = [
[ 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 ],
[ 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 ],
[ 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 ],
[ 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 ],
[ 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 ],
[ 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 ],
[ 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 ],
[ 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 ],
[ 662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0 ],
]
data['num_vehicles'] = 4
data['starts'] = [0,0,0,0]
data['ends'] = [0,0,0,0]
data['routes'] = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12], [13, 14], [15, 16]]
data['is_start'] = [0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0]
data['is_end'] = [0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1]
data['clients'] = {
"A" : [1, 2, 3, 4, 5, 6],
"B" : [7, 8, 9, 10, 11,12],
"C": [13, 14, 15, 16]
}
##################################################################################################
# Optimizar routing model and manager
##################################################################################################
# Create the routing index manager.
manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),
data['num_vehicles'],
data['starts'],
data['ends'])
# Create Routing Model.
routing = pywrapcp.RoutingModel(manager)
##################################################################################################
# Optimizar dimensions
##################################################################################################
dimensions = {}
##################################################################################################
# Distance Dimension
##################################################################################################
def distance_callback(from_index, to_index):
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
distance = data['distance_matrix'][from_node][to_node]
return distance
transit_callback_index = routing.RegisterTransitCallback(distance_callback)
# Define cost of each arc.
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
# Add Distance constraint.
dimension_name = 'Distance'
routing.AddDimension(
transit_callback_index,
0, # no slack
999999, # vehicle maximum travel distance
True, # start cumul to zero
dimension_name)
distance_dimension = routing.GetDimensionOrDie(dimension_name)
distance_dimension.SetGlobalSpanCostCoefficient(100)
dimensions['distance'] = distance_dimension
##################################################################################################
# Constraint
##################################################################################################
# Same vehicle constraint
for route in data['routes']:
for i in range(len(route) - 1):
current_index = manager.NodeToIndex(route[i])
next_index = manager.NodeToIndex(route[i+1])
if len(route) == 2:
routing.AddPickupAndDelivery(current_index, next_index)
# same vehicle condition
routing.solver().Add(
routing.VehicleVar(current_index) == routing.VehicleVar(
next_index)
)
# Pickup before Delivery order
for route in data['routes']:
for i in range(len(route) - 1):
current_index = manager.NodeToIndex(route[i])
next_index = manager.NodeToIndex(route[i+1])
routing.solver().Add(
distance_dimension.CumulVar(current_index) <=
distance_dimension.CumulVar(next_index)
)
# Client constraint - NOT WORKING!!!
# Coment the following code to make it work
for c in data['clients']:
other_dims = [dimensions["Client" + k] for k in data['clients'].keys() if k != c]
for node in data['clients'][c]:
index = manager.NodeToIndex(node)
for other_dim in other_dims:
routing.solver().Add(
other_dim.CumulVar(index) == 0
)
##################################################################################################
# Optimizer parameters
##################################################################################################
# Setting first solution heuristic.
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
# Solve the problem.
solution = routing.SolveWithParameters(search_parameters)
# Search time limit to find a solution
search_parameters.time_limit.seconds = 60
# Print solution on console.
if solution:
print_solution(data, manager, routing, solution, dimensions)
else:
print('No solution found !')