Created
May 17, 2018 00:37
-
-
Save matheusmessora/c49991d4c8ed8eb2023a8c6d0785b791 to your computer and use it in GitHub Desktop.
Hello World
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Vehicle Routing Problem""" | |
from __future__ import print_function | |
from six.moves import xrange | |
from ortools.constraint_solver import pywrapcp | |
from ortools.constraint_solver import routing_enums_pb2 | |
from math import sin, cos, sqrt, atan2, radians | |
########################### | |
# Problem Data Definition # | |
########################### | |
class CityBlock(): | |
"""City block definition""" | |
@property | |
def width(self): | |
"""Gets Block size West to East""" | |
return 228/2 | |
@property | |
def height(self): | |
"""Gets Block size North to South""" | |
return 80 | |
class DataProblem(): | |
"""Stores the data for the problem""" | |
def __init__(self): | |
"""Initializes the data for the problem""" | |
self._num_vehicles = 3 | |
# Locations in block unit | |
# locations = \ | |
# [(4, 4), # depot | |
# (2, 0), (8, 0), # row 0 | |
# (0, 1), (1, 1), | |
# (5, 2), (7, 2), | |
# (3, 3), (6, 3), | |
# (5, 5), (8, 5), | |
# (1, 6), (2, 6), | |
# (3, 7), (6, 7), | |
# (0, 8), (7, 8)] | |
locations = \ | |
[(-23.603777, -46.694043), # depot 0 | |
(-23.603762, -46.691936), #starbucks 1 | |
(-23.6016602,-46.6936612), #restaurante Caires 2 | |
(-23.606197, -46.686895), #sociedade hipica 3 | |
(-23.599033, -46.687971), #fogo de chao 4 | |
(-23.606645, -46.692680), #ferrovia 5 | |
(-23.607203, -46.694695) # kawa sushi 6 | |
] | |
# locations in meters using the city block dimension | |
city_block = CityBlock() | |
self._locations = [( | |
loc[0], | |
loc[1]) for loc in locations] | |
self._depot = 0 | |
@property | |
def num_vehicles(self): | |
"""Gets number of vehicles""" | |
return self._num_vehicles | |
@property | |
def locations(self): | |
"""Gets locations""" | |
return self._locations | |
@property | |
def num_locations(self): | |
"""Gets number of locations""" | |
return len(self.locations) | |
@property | |
def depot(self): | |
"""Gets depot location index""" | |
return self._depot | |
####################### | |
# Problem Constraints # | |
####################### | |
def manhattan_distance(position_1, position_2): | |
"""Computes the Manhattan distance between two points""" | |
# = r(Xb - Xa)2 + (Yb - Ya)2 | |
# approximate radius of earth in km | |
R = 6373.0 | |
lat1 = radians(position_1[0]) | |
lon1 = radians(position_1[1]) | |
lat2 = radians(position_2[0]) | |
lon2 = radians(position_2[1]) | |
dlon = lon2 - lon1 | |
dlat = lat2 - lat1 | |
a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2 | |
c = 2 * atan2(sqrt(a), sqrt(1 - a)) | |
distance = R * c * 1000 | |
# distance = math.sqrt((position_2[0] - position_1[0])**2 + (position_2[1] - position_1[1])**2) | |
print(distance, position_1, position_2) | |
return distance | |
#return (abs(position_1[0] - position_2[0]) + | |
# abs(position_1[1] - position_2[1])) | |
class CreateDistanceEvaluator(object): # pylint: disable=too-few-public-methods | |
"""Creates callback to return distance between points.""" | |
def __init__(self, data): | |
"""Initializes the distance matrix.""" | |
self._distances = {} | |
# precompute distance between location to have distance callback in O(1) | |
for from_node in xrange(data.num_locations): | |
self._distances[from_node] = {} | |
for to_node in xrange(data.num_locations): | |
if from_node == to_node: | |
self._distances[from_node][to_node] = 0 | |
else: | |
self._distances[from_node][to_node] = ( | |
manhattan_distance( | |
data.locations[from_node], | |
data.locations[to_node])) | |
def distance_evaluator(self, from_node, to_node): | |
"""Returns the manhattan distance between the two nodes""" | |
return self._distances[from_node][to_node] | |
def add_distance_dimension(routing, distance_evaluator): | |
"""Add Global Span constraint""" | |
distance = "Distance" | |
maximum_distance = 15000 | |
routing.AddDimension( | |
distance_evaluator, | |
0, # null slack | |
maximum_distance, # maximum distance per vehicle | |
True, # start cumul to zero | |
distance) | |
distance_dimension = routing.GetDimensionOrDie(distance) | |
# Try to minimize the max distance among vehicles. | |
# /!\ It doesn't mean the standard deviation is minimized | |
distance_dimension.SetGlobalSpanCostCoefficient(100) | |
########### | |
# Printer # | |
########### | |
class ConsolePrinter(): | |
"""Print solution to console""" | |
def __init__(self, data, routing, assignment): | |
"""Initializes the printer""" | |
self._data = data | |
self._routing = routing | |
self._assignment = assignment | |
@property | |
def data(self): | |
"""Gets problem data""" | |
return self._data | |
@property | |
def routing(self): | |
"""Gets routing model""" | |
return self._routing | |
@property | |
def assignment(self): | |
"""Gets routing model""" | |
return self._assignment | |
def print(self): | |
"""Prints assignment on console""" | |
# Inspect solution. | |
print("=====") | |
total_dist = 0 | |
for vehicle_id in xrange(self.data.num_vehicles): | |
index = self.routing.Start(vehicle_id) | |
plan_output = 'Route for vehicle {0}:\n'.format(vehicle_id) | |
route_dist = 0 | |
while not self.routing.IsEnd(index): | |
node_index = self.routing.IndexToNode(index) | |
next_node_index = self.routing.IndexToNode( | |
self.assignment.Value(self.routing.NextVar(index))) | |
route_dist += manhattan_distance( | |
self.data.locations[node_index], | |
self.data.locations[next_node_index]) | |
plan_output += ' {node_index} -> '.format( | |
node_index=node_index) | |
index = self.assignment.Value(self.routing.NextVar(index)) | |
node_index = self.routing.IndexToNode(index) | |
total_dist += route_dist | |
plan_output += ' {node_index}\n'.format( | |
node_index=node_index) | |
plan_output += 'Distance of the route {0}: {dist}\n'.format( | |
vehicle_id, | |
dist=route_dist) | |
print(plan_output) | |
print('Total Distance of all routes: {dist}'.format(dist=total_dist)) | |
######## | |
# Main # | |
######## | |
def main(): | |
"""Entry point of the program""" | |
# Instantiate the data problem. | |
data = DataProblem() | |
# Create Routing Model | |
routing = pywrapcp.RoutingModel(data.num_locations, data.num_vehicles, data.depot) | |
# Define weight of each edge | |
distance_evaluator = CreateDistanceEvaluator(data).distance_evaluator | |
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator) | |
add_distance_dimension(routing, distance_evaluator) | |
# Setting first solution heuristic (cheapest addition). | |
search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters() | |
search_parameters.first_solution_strategy = ( | |
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) | |
# Solve the problem. | |
assignment = routing.SolveWithParameters(search_parameters) | |
printer = ConsolePrinter(data, routing, assignment) | |
printer.print() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment