dotnet: Remove reference to dotnet release command
- Currently not implemented... Add abseil patch - Add patches/absl-config.cmake Makefile: Add abseil-cpp on unix - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake Makefile: Add abseil-cpp on windows - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake CMake: Add abseil-cpp - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake port to absl: C++ Part - Fix warning with the use of ABSL_MUST_USE_RESULT > The macro must appear as the very first part of a function declaration or definition: ... Note: past advice was to place the macro after the argument list. src: dependencies/sources/abseil-cpp-master/absl/base/attributes.h:418 - Rename enum after windows clash - Remove non compact table constraints - Change index type from int64 to int in routing library - Fix file_nonport compilation on windows - Fix another naming conflict with windows (NO_ERROR is a macro) - Cleanup hash containers; work on sat internals - Add optional_boolean sub-proto Sync cpp examples with internal code - reenable issue173 after reducing number of loops port to absl: Python Part - Add back cp_model.INT32_MIN|MAX for examples Update Python examples - Add random_tsp.py - Run words_square example - Run magic_square in python tests port to absl: Java Part - Fix compilation of the new routing parameters in java - Protect some code from SWIG parsing Update Java Examples port to absl: .Net Part Update .Net examples work on sat internals; Add C++ CP-SAT CpModelBuilder API; update sample code and recipes to use the new API; sync with internal code Remove VS 2015 in Appveyor-CI - abseil-cpp does not support VS 2015... improve tables upgrade C++ sat examples to use the new API; work on sat internals update license dates rewrite jobshop_ft06_distance.py to use the CP-SAT solver rename last example revert last commit more work on SAT internals fix
This commit is contained in:
@@ -24,7 +24,10 @@
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from functools import partial
|
||||
from six.moves import xrange
|
||||
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
from ortools.constraint_solver import routing_enums_pb2
|
||||
|
||||
@@ -33,153 +36,160 @@ from ortools.constraint_solver import routing_enums_pb2
|
||||
# Problem Data Definition #
|
||||
###########################
|
||||
def create_data_model():
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(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)]
|
||||
# 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
|
||||
1, 1, # 1, 2
|
||||
2, 4, # 3, 4
|
||||
2, 4, # 5, 6
|
||||
8, 8, # 7, 8
|
||||
1, 2, # 9,10
|
||||
1, 2, # 11,12
|
||||
4, 4, # 13, 14
|
||||
8, 8] # 15, 16
|
||||
data["num_vehicles"] = 4
|
||||
data["vehicle_capacity"] = 15
|
||||
data["depot"] = 0
|
||||
return data
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(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)]
|
||||
# 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
|
||||
1, 1, # 1, 2
|
||||
2, 4, # 3, 4
|
||||
2, 4, # 5, 6
|
||||
8, 8, # 7, 8
|
||||
1, 2, # 9,10
|
||||
1, 2, # 11,12
|
||||
4, 4, # 13, 14
|
||||
8, 8] # 15, 16
|
||||
data['num_vehicles'] = 4
|
||||
data['vehicle_capacity'] = 15
|
||||
data['depot'] = 0
|
||||
return data
|
||||
|
||||
|
||||
#######################
|
||||
# Problem Constraints #
|
||||
#######################
|
||||
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]))
|
||||
"""Computes the Manhattan distance between two points"""
|
||||
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 xrange(data["num_locations"]):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data["num_locations"]):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (manhattan_distance(
|
||||
data["locations"][from_node], data["locations"][to_node]))
|
||||
"""Creates callback to return distance between points."""
|
||||
_distances = {}
|
||||
# precompute distance between location to have distance callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (
|
||||
manhattan_distance(data['locations'][from_node],
|
||||
data['locations'][to_node]))
|
||||
|
||||
def distance_evaluator(from_node, to_node):
|
||||
"""Returns the manhattan distance between the two nodes"""
|
||||
return _distances[from_node][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 distance_evaluator
|
||||
return distance_evaluator
|
||||
|
||||
|
||||
def create_demand_evaluator(data):
|
||||
"""Creates callback to get demands at each location."""
|
||||
_demands = data["demands"]
|
||||
"""Creates callback to get demands at each location."""
|
||||
_demands = data['demands']
|
||||
|
||||
def demand_evaluator(from_node, to_node):
|
||||
"""Returns the demand of the current node"""
|
||||
del to_node
|
||||
return _demands[from_node]
|
||||
def demand_evaluator(manager, node):
|
||||
"""Returns the demand of the current node"""
|
||||
return _demands[manager.IndexToNode(node)]
|
||||
|
||||
return demand_evaluator
|
||||
return demand_evaluator
|
||||
|
||||
|
||||
def add_capacity_constraints(routing, data, demand_evaluator):
|
||||
"""Adds capacity constraint"""
|
||||
capacity = 'Capacity'
|
||||
routing.AddDimension(
|
||||
demand_evaluator,
|
||||
0, # null capacity slack
|
||||
data["vehicle_capacity"],
|
||||
True, # start cumul to zero
|
||||
capacity)
|
||||
def add_capacity_constraints(routing, data, demand_evaluator_index):
|
||||
"""Adds capacity constraint"""
|
||||
capacity = 'Capacity'
|
||||
routing.AddDimension(
|
||||
demand_evaluator_index,
|
||||
0, # null capacity slack
|
||||
data['vehicle_capacity'],
|
||||
True, # start cumul to zero
|
||||
capacity)
|
||||
|
||||
|
||||
###########
|
||||
# Printer #
|
||||
###########
|
||||
def print_solution(data, routing, assignment):
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
total_load = 0
|
||||
capacity_dimension = routing.GetDimensionOrDie('Capacity')
|
||||
for vehicle_id in xrange(data["num_vehicles"]):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
plan_output += ' {} Load({}) -> '.format(
|
||||
routing.IndexToNode(index), assignment.Value(load_var))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index,
|
||||
vehicle_id)
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
plan_output += ' {0} Load({1})\n'.format(
|
||||
routing.IndexToNode(index), assignment.Value(load_var))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
plan_output += 'Load of the route: {}\n'.format(
|
||||
assignment.Value(load_var))
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
total_load += assignment.Value(load_var)
|
||||
print('Total Distance of all routes: {}m'.format(total_distance))
|
||||
print('Total Load of all routes: {}'.format(total_load))
|
||||
def print_solution(data, routing, manager, assignment): # pylint:disable=too-many-locals
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
total_load = 0
|
||||
capacity_dimension = routing.GetDimensionOrDie('Capacity')
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
plan_output += ' {} Load({}) -> '.format(
|
||||
manager.IndexToNode(index), assignment.Value(load_var))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index,
|
||||
vehicle_id)
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
plan_output += ' {0} Load({1})\n'.format(
|
||||
manager.IndexToNode(index), assignment.Value(load_var))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
plan_output += 'Load of the route: {}\n'.format(assignment.Value(load_var))
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
total_load += assignment.Value(load_var)
|
||||
print('Total Distance of all routes: {}m'.format(total_distance))
|
||||
print('Total Load of all routes: {}'.format(total_load))
|
||||
|
||||
|
||||
########
|
||||
# Main #
|
||||
########
|
||||
def main():
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(data["num_locations"],
|
||||
data["num_vehicles"], data["depot"])
|
||||
# Define weight of each edge
|
||||
distance_evaluator = create_distance_evaluator(data)
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator)
|
||||
# Add Capacity constraint
|
||||
demand_evaluator = create_demand_evaluator(data)
|
||||
add_capacity_constraints(routing, data, demand_evaluator)
|
||||
# Create the routing index manager
|
||||
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
|
||||
data['num_vehicles'], data['depot'])
|
||||
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters()
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, routing, assignment)
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
|
||||
# Define weight of each edge
|
||||
distance_evaluator = routing.RegisterTransitCallback(
|
||||
partial(create_distance_evaluator(data), manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator)
|
||||
|
||||
# Add Capacity constraint
|
||||
demand_evaluator_index = routing.RegisterUnaryTransitCallback(
|
||||
partial(create_demand_evaluator(data), manager))
|
||||
add_capacity_constraints(routing, data, demand_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
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, routing, manager, assignment)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -24,7 +24,10 @@
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from functools import partial
|
||||
from six.moves import xrange
|
||||
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
from ortools.constraint_solver import routing_enums_pb2
|
||||
|
||||
@@ -33,304 +36,315 @@ from ortools.constraint_solver import routing_enums_pb2
|
||||
# Problem Data Definition #
|
||||
###########################
|
||||
def create_data_model():
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
_capacity = 15
|
||||
# Locations in block unit
|
||||
_locations = [
|
||||
(4, 4), # depot
|
||||
(4, 4), # unload depot_prime
|
||||
(4, 4), # unload depot_second
|
||||
(4, 4), # unload depot_fourth
|
||||
(4, 4), # unload depot_fourth
|
||||
(4, 4), # unload depot_fifth
|
||||
(2, 0),
|
||||
(8, 0), # locations to visit
|
||||
(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)
|
||||
]
|
||||
# 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,
|
||||
-_capacity,
|
||||
-_capacity,
|
||||
-_capacity,
|
||||
-_capacity,
|
||||
1, 1, # 1, 2
|
||||
2, 4, # 3, 4
|
||||
2, 4, # 5, 6
|
||||
8, 8, # 7, 8
|
||||
1, 2, # 9,10
|
||||
1, 2, # 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),
|
||||
(0, 1000),
|
||||
(0, 1000),
|
||||
(0, 1000),
|
||||
(0, 1000),
|
||||
(75, 8500), (75, 8500), # 1, 2
|
||||
(60, 7000), (45, 5500), # 3, 4
|
||||
(0, 8000), (50, 6000), # 5, 6
|
||||
(0, 1000), (10, 2000), # 7, 8
|
||||
(0, 1000), (75, 8500), # 9, 10
|
||||
(85, 9500), (5, 1500), # 11, 12
|
||||
(15, 2500), (10, 2000), # 13, 14
|
||||
(45, 5500), (30, 4000)] # 15, 16
|
||||
data["num_vehicles"] = 3
|
||||
data["vehicle_capacity"] = _capacity
|
||||
data[
|
||||
"vehicle_speed"] = 5 * 60 / 3.6 # Travel speed: 5km/h to convert in m/min
|
||||
data["depot"] = 0
|
||||
return data
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
_capacity = 15
|
||||
# Locations in block unit
|
||||
_locations = [
|
||||
(4, 4), # depot
|
||||
(4, 4), # unload depot_prime
|
||||
(4, 4), # unload depot_second
|
||||
(4, 4), # unload depot_fourth
|
||||
(4, 4), # unload depot_fourth
|
||||
(4, 4), # unload depot_fifth
|
||||
(2, 0),
|
||||
(8, 0), # locations to visit
|
||||
(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)
|
||||
]
|
||||
# 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,
|
||||
-_capacity,
|
||||
-_capacity,
|
||||
-_capacity,
|
||||
-_capacity,
|
||||
1, 1, # 1, 2
|
||||
2, 4, # 3, 4
|
||||
2, 4, # 5, 6
|
||||
8, 8, # 7, 8
|
||||
1, 2, # 9,10
|
||||
1, 2, # 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),
|
||||
(0, 1000),
|
||||
(0, 1000),
|
||||
(0, 1000),
|
||||
(0, 1000),
|
||||
(75, 8500), (75, 8500), # 1, 2
|
||||
(60, 7000), (45, 5500), # 3, 4
|
||||
(0, 8000), (50, 6000), # 5, 6
|
||||
(0, 1000), (10, 2000), # 7, 8
|
||||
(0, 1000), (75, 8500), # 9, 10
|
||||
(85, 9500), (5, 1500), # 11, 12
|
||||
(15, 2500), (10, 2000), # 13, 14
|
||||
(45, 5500), (30, 4000)] # 15, 16
|
||||
data['num_vehicles'] = 3
|
||||
data['vehicle_capacity'] = _capacity
|
||||
data[
|
||||
'vehicle_speed'] = 5 * 60 / 3.6 # Travel speed: 5km/h to convert in m/min
|
||||
data['depot'] = 0
|
||||
return data
|
||||
|
||||
|
||||
#######################
|
||||
# Problem Constraints #
|
||||
#######################
|
||||
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]))
|
||||
"""Computes the Manhattan distance between two points"""
|
||||
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 xrange(data["num_locations"]):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data["num_locations"]):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (manhattan_distance(
|
||||
data["locations"][from_node], data["locations"][to_node]))
|
||||
"""Creates callback to return distance between points."""
|
||||
_distances = {}
|
||||
# precompute distance between location to have distance callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (
|
||||
manhattan_distance(data['locations'][from_node],
|
||||
data['locations'][to_node]))
|
||||
|
||||
def distance_evaluator(from_node, to_node):
|
||||
"""Returns the manhattan distance between the two nodes"""
|
||||
return _distances[from_node][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 distance_evaluator
|
||||
return distance_evaluator
|
||||
|
||||
|
||||
def add_distance_dimension(routing, distance_evaluator):
|
||||
"""Add Global Span constraint"""
|
||||
distance = 'Distance'
|
||||
routing.AddDimension(
|
||||
distance_evaluator,
|
||||
0, # null slack
|
||||
10000, # 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)
|
||||
def add_distance_dimension(routing, distance_evaluator_index):
|
||||
"""Add Global Span constraint"""
|
||||
distance = 'Distance'
|
||||
routing.AddDimension(
|
||||
distance_evaluator_index,
|
||||
0, # null slack
|
||||
10000, # 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)
|
||||
|
||||
|
||||
def create_demand_evaluator(data):
|
||||
"""Creates callback to get demands at each location."""
|
||||
_demands = data["demands"]
|
||||
"""Creates callback to get demands at each location."""
|
||||
_demands = data['demands']
|
||||
|
||||
def demand_evaluator(from_node, to_node):
|
||||
"""Returns the demand of the current node"""
|
||||
del to_node
|
||||
return _demands[from_node]
|
||||
def demand_evaluator(manager, from_node):
|
||||
"""Returns the demand of the current node"""
|
||||
return _demands[manager.IndexToNode(from_node)]
|
||||
|
||||
return demand_evaluator
|
||||
return demand_evaluator
|
||||
|
||||
|
||||
def add_capacity_constraints(routing, data, demand_evaluator):
|
||||
"""Adds capacity constraint"""
|
||||
vehicle_capacity = data["vehicle_capacity"]
|
||||
capacity = 'Capacity'
|
||||
routing.AddDimension(
|
||||
demand_evaluator,
|
||||
0, # Null slack
|
||||
vehicle_capacity,
|
||||
True, # start cumul to zero
|
||||
capacity)
|
||||
def add_capacity_constraints(routing, manager, data, demand_evaluator_index):
|
||||
"""Adds capacity constraint"""
|
||||
vehicle_capacity = data['vehicle_capacity']
|
||||
capacity = 'Capacity'
|
||||
routing.AddDimension(
|
||||
demand_evaluator_index,
|
||||
0, # Null slack
|
||||
vehicle_capacity,
|
||||
True, # start cumul to zero
|
||||
capacity)
|
||||
|
||||
# Add Slack for reseting to zero unload depot nodes.
|
||||
# e.g. vehicle with load 10/15 arrives at node 1 (depot unload)
|
||||
# so we have CumulVar = 10(current load) + -15(unload) + 5(slack) = 0.
|
||||
capacity_dimension = routing.GetDimensionOrDie(capacity)
|
||||
for node_index in [1, 2, 3, 4, 5]:
|
||||
index = routing.NodeToIndex(node_index)
|
||||
capacity_dimension.SlackVar(index).SetRange(0, vehicle_capacity)
|
||||
routing.AddDisjunction([node_index], 0)
|
||||
# Add Slack for reseting to zero unload depot nodes.
|
||||
# e.g. vehicle with load 10/15 arrives at node 1 (depot unload)
|
||||
# so we have CumulVar = 10(current load) + -15(unload) + 5(slack) = 0.
|
||||
capacity_dimension = routing.GetDimensionOrDie(capacity)
|
||||
for node_index in [1, 2, 3, 4, 5]:
|
||||
index = manager.NodeToIndex(node_index)
|
||||
capacity_dimension.SlackVar(index).SetRange(0, vehicle_capacity)
|
||||
routing.AddDisjunction([node_index], 0)
|
||||
|
||||
|
||||
def create_time_evaluator(data):
|
||||
"""Creates callback to get total times between locations."""
|
||||
"""Creates callback to get total times between locations."""
|
||||
|
||||
def service_time(data, node):
|
||||
"""Gets the service time for the specified location."""
|
||||
return abs(data["demands"][node]) * data["time_per_demand_unit"]
|
||||
def service_time(data, node):
|
||||
"""Gets the service time for the specified location."""
|
||||
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"]
|
||||
return travel_time
|
||||
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']
|
||||
return travel_time
|
||||
|
||||
_total_time = {}
|
||||
# precompute total time to have time callback in O(1)
|
||||
for from_node in xrange(data["num_locations"]):
|
||||
_total_time[from_node] = {}
|
||||
for to_node in xrange(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))
|
||||
_total_time = {}
|
||||
# precompute total time to have time callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_total_time[from_node] = {}
|
||||
for to_node in xrange(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))
|
||||
|
||||
def time_evaluator(from_node, to_node):
|
||||
"""Returns the total time between the two nodes"""
|
||||
return _total_time[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 time_evaluator
|
||||
return time_evaluator
|
||||
|
||||
|
||||
def add_time_window_constraints(routing, data, time_evaluator):
|
||||
"""Add Time windows constraint"""
|
||||
time = 'Time'
|
||||
horizon = 1500
|
||||
routing.AddDimension(
|
||||
time_evaluator,
|
||||
horizon, # allow waiting time
|
||||
horizon, # maximum time per vehicle
|
||||
False, # don't force start cumul to zero since we are giving TW to start nodes
|
||||
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"]):
|
||||
if location_idx == 0:
|
||||
continue
|
||||
index = routing.NodeToIndex(location_idx)
|
||||
time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1])
|
||||
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 xrange(data["num_vehicles"]):
|
||||
index = routing.Start(vehicle_id)
|
||||
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)))
|
||||
def add_time_window_constraints(routing, manager, data, time_evaluator):
|
||||
"""Add Time windows constraint"""
|
||||
time = 'Time'
|
||||
horizon = 1500
|
||||
routing.AddDimension(
|
||||
time_evaluator,
|
||||
horizon, # allow waiting time
|
||||
horizon, # maximum time per vehicle
|
||||
False, # don't force start cumul to zero since we are giving TW to start nodes
|
||||
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']):
|
||||
if location_idx == 0:
|
||||
continue
|
||||
index = manager.NodeToIndex(location_idx)
|
||||
time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1])
|
||||
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 xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
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)))
|
||||
|
||||
|
||||
###########
|
||||
# Printer #
|
||||
###########
|
||||
def print_solution(data, routing, assignment):
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
total_load = 0
|
||||
total_time = 0
|
||||
capacity_dimension = routing.GetDimensionOrDie('Capacity')
|
||||
time_dimension = routing.GetDimensionOrDie('Time')
|
||||
dropped = []
|
||||
for order in xrange(0, routing.nodes()):
|
||||
index = routing.NodeToIndex(order)
|
||||
if assignment.Value(routing.NextVar(index)) == index:
|
||||
dropped.append(order)
|
||||
print('dropped orders: {}'.format(dropped))
|
||||
def print_solution(data, manager, routing, assignment): # pylint:disable=too-many-locals
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
total_load = 0
|
||||
total_time = 0
|
||||
capacity_dimension = routing.GetDimensionOrDie('Capacity')
|
||||
time_dimension = routing.GetDimensionOrDie('Time')
|
||||
dropped = []
|
||||
for order in xrange(0, routing.nodes()):
|
||||
index = manager.NodeToIndex(order)
|
||||
if assignment.Value(routing.NextVar(index)) == index:
|
||||
dropped.append(order)
|
||||
print('dropped orders: {}'.format(dropped))
|
||||
|
||||
for vehicle_id in xrange(data["num_vehicles"]):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
time_var = time_dimension.CumulVar(index)
|
||||
plan_output += ' {0} Load({1}) Time({2},{3}) ->'.format(
|
||||
routing.IndexToNode(index), assignment.Value(load_var),
|
||||
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)
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
time_var = time_dimension.CumulVar(index)
|
||||
plan_output += ' {0} Load({1}) Time({2},{3})\n'.format(
|
||||
routing.IndexToNode(index), assignment.Value(load_var),
|
||||
assignment.Min(time_var), assignment.Max(time_var))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
plan_output += 'Load of the route: {}\n'.format(
|
||||
assignment.Value(load_var))
|
||||
plan_output += 'Time of the route: {}min\n'.format(
|
||||
assignment.Value(time_var))
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
total_load += assignment.Value(load_var)
|
||||
total_time += assignment.Value(time_var)
|
||||
print('Total Distance of all routes: {}m'.format(total_distance))
|
||||
print('Total Load of all routes: {}'.format(total_load))
|
||||
print('Total Time of all routes: {}min'.format(total_time))
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
time_var = time_dimension.CumulVar(index)
|
||||
plan_output += ' {0} Load({1}) Time({2},{3}) ->'.format(
|
||||
manager.IndexToNode(index), assignment.Value(load_var),
|
||||
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)
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
time_var = time_dimension.CumulVar(index)
|
||||
plan_output += ' {0} Load({1}) Time({2},{3})\n'.format(
|
||||
manager.IndexToNode(index), assignment.Value(load_var),
|
||||
assignment.Min(time_var), assignment.Max(time_var))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
plan_output += 'Load of the route: {}\n'.format(assignment.Value(load_var))
|
||||
plan_output += 'Time of the route: {}min\n'.format(
|
||||
assignment.Value(time_var))
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
total_load += assignment.Value(load_var)
|
||||
total_time += assignment.Value(time_var)
|
||||
print('Total Distance of all routes: {}m'.format(total_distance))
|
||||
print('Total Load of all routes: {}'.format(total_load))
|
||||
print('Total Time of all routes: {}min'.format(total_time))
|
||||
|
||||
|
||||
########
|
||||
# Main #
|
||||
########
|
||||
def main():
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(data["num_locations"],
|
||||
data["num_vehicles"], data["depot"])
|
||||
# Define weight of each edge
|
||||
distance_evaluator = create_distance_evaluator(data)
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator)
|
||||
# Add Distance constraint to minimize the longuest route
|
||||
add_distance_dimension(routing, distance_evaluator)
|
||||
# Add Capacity constraint
|
||||
demand_evaluator = create_demand_evaluator(data)
|
||||
add_capacity_constraints(routing, data, demand_evaluator)
|
||||
# Add Time Window constraint
|
||||
time_evaluator = create_time_evaluator(data)
|
||||
add_time_window_constraints(routing, data, time_evaluator)
|
||||
# Create the routing index manager
|
||||
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
|
||||
data['num_vehicles'], data['depot'])
|
||||
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters()
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, routing, assignment)
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
|
||||
# Define weight of each edge
|
||||
distance_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_distance_evaluator(data), manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index)
|
||||
|
||||
# Add Distance constraint to minimize the longuest route
|
||||
add_distance_dimension(routing, distance_evaluator_index)
|
||||
|
||||
# Add Capacity constraint
|
||||
demand_evaluator_index = routing.RegisterUnaryTransitCallback(
|
||||
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))
|
||||
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
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, manager, routing, assignment)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -24,7 +24,10 @@
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from functools import partial
|
||||
from six.moves import xrange
|
||||
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
from ortools.constraint_solver import routing_enums_pb2
|
||||
|
||||
@@ -33,248 +36,256 @@ from ortools.constraint_solver import routing_enums_pb2
|
||||
# Problem Data Definition #
|
||||
###########################
|
||||
def create_data_model():
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(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)]
|
||||
# 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["time_windows"] = \
|
||||
[(0, 0),
|
||||
(75, 85), (75, 85), # 1, 2
|
||||
(60, 70), (45, 55), # 3, 4
|
||||
(0, 8), (50, 60), # 5, 6
|
||||
(0, 10), (10, 20), # 7, 8
|
||||
(0, 10), (75, 85), # 9, 10
|
||||
(85, 95), (5, 15), # 11, 12
|
||||
(15, 25), (10, 20), # 13, 14
|
||||
(45, 55), (30, 40)] # 15, 16
|
||||
data["demands"] = \
|
||||
[0, # depot
|
||||
1, 1, # 1, 2
|
||||
2, 4, # 3, 4
|
||||
2, 4, # 5, 6
|
||||
8, 8, # 7, 8
|
||||
1, 2, # 9,10
|
||||
1, 2, # 11,12
|
||||
4, 4, # 13, 14
|
||||
8, 8] # 15, 16
|
||||
data["time_per_demand_unit"] = 5 # 5 minutes/unit
|
||||
data["num_vehicles"] = 4
|
||||
data["vehicle_capacity"] = 15
|
||||
data[
|
||||
"vehicle_speed"] = 5 * 60 / 3.6 # Travel speed: 5km/h to convert in m/min
|
||||
data["depot"] = 0
|
||||
return data
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(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)]
|
||||
# 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['time_windows'] = \
|
||||
[(0, 0),
|
||||
(75, 85), (75, 85), # 1, 2
|
||||
(60, 70), (45, 55), # 3, 4
|
||||
(0, 8), (50, 60), # 5, 6
|
||||
(0, 10), (10, 20), # 7, 8
|
||||
(0, 10), (75, 85), # 9, 10
|
||||
(85, 95), (5, 15), # 11, 12
|
||||
(15, 25), (10, 20), # 13, 14
|
||||
(45, 55), (30, 40)] # 15, 16
|
||||
data['demands'] = \
|
||||
[0, # depot
|
||||
1, 1, # 1, 2
|
||||
2, 4, # 3, 4
|
||||
2, 4, # 5, 6
|
||||
8, 8, # 7, 8
|
||||
1, 2, # 9,10
|
||||
1, 2, # 11,12
|
||||
4, 4, # 13, 14
|
||||
8, 8] # 15, 16
|
||||
data['time_per_demand_unit'] = 5 # 5 minutes/unit
|
||||
data['num_vehicles'] = 4
|
||||
data['vehicle_capacity'] = 15
|
||||
data['vehicle_speed'] = 83 # Travel speed: 5km/h converted in m/min
|
||||
data['depot'] = 0
|
||||
return data
|
||||
|
||||
|
||||
#######################
|
||||
# Problem Constraints #
|
||||
#######################
|
||||
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]))
|
||||
"""Computes the Manhattan distance between two points"""
|
||||
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 xrange(data["num_locations"]):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data["num_locations"]):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (manhattan_distance(
|
||||
data["locations"][from_node], data["locations"][to_node]))
|
||||
"""Creates callback to return distance between points."""
|
||||
_distances = {}
|
||||
# precompute distance between location to have distance callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (
|
||||
manhattan_distance(data['locations'][from_node],
|
||||
data['locations'][to_node]))
|
||||
|
||||
def distance_evaluator(from_node, to_node):
|
||||
"""Returns the manhattan distance between the two nodes"""
|
||||
return _distances[from_node][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 distance_evaluator
|
||||
return distance_evaluator
|
||||
|
||||
|
||||
def create_demand_evaluator(data):
|
||||
"""Creates callback to get demands at each location."""
|
||||
_demands = data["demands"]
|
||||
"""Creates callback to get demands at each location."""
|
||||
_demands = data['demands']
|
||||
|
||||
def demand_evaluator(from_node, to_node):
|
||||
"""Returns the demand of the current node"""
|
||||
del to_node
|
||||
return _demands[from_node]
|
||||
def demand_evaluator(manager, node):
|
||||
"""Returns the demand of the current node"""
|
||||
return _demands[manager.IndexToNode(node)]
|
||||
|
||||
return demand_evaluator
|
||||
return demand_evaluator
|
||||
|
||||
|
||||
def add_capacity_constraints(routing, data, demand_evaluator):
|
||||
"""Adds capacity constraint"""
|
||||
capacity = 'Capacity'
|
||||
routing.AddDimension(
|
||||
demand_evaluator,
|
||||
0, # null capacity slack
|
||||
data["vehicle_capacity"],
|
||||
True, # start cumul to zero
|
||||
capacity)
|
||||
def add_capacity_constraints(routing, data, demand_evaluator_index):
|
||||
"""Adds capacity constraint"""
|
||||
capacity = 'Capacity'
|
||||
routing.AddDimension(
|
||||
demand_evaluator_index,
|
||||
0, # null capacity slack
|
||||
data['vehicle_capacity'],
|
||||
True, # start cumul to zero
|
||||
capacity)
|
||||
|
||||
|
||||
def create_time_evaluator(data):
|
||||
"""Creates callback to get total times between locations."""
|
||||
"""Creates callback to get total times between locations."""
|
||||
|
||||
def service_time(data, node):
|
||||
"""Gets the service time for the specified location."""
|
||||
return data["demands"][node] * data["time_per_demand_unit"]
|
||||
def service_time(data, node):
|
||||
"""Gets the service time for the specified location."""
|
||||
return 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"]
|
||||
return travel_time
|
||||
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']
|
||||
return travel_time
|
||||
|
||||
_total_time = {}
|
||||
# precompute total time to have time callback in O(1)
|
||||
for from_node in xrange(data["num_locations"]):
|
||||
_total_time[from_node] = {}
|
||||
for to_node in xrange(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))
|
||||
_total_time = {}
|
||||
# precompute total time to have time callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_total_time[from_node] = {}
|
||||
for to_node in xrange(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))
|
||||
|
||||
def time_evaluator(from_node, to_node):
|
||||
"""Returns the total time between the two nodes"""
|
||||
return _total_time[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 time_evaluator
|
||||
return time_evaluator
|
||||
|
||||
|
||||
def add_time_window_constraints(routing, data, time_evaluator):
|
||||
"""Add Global Span constraint"""
|
||||
time = 'Time'
|
||||
horizon = 120
|
||||
routing.AddDimension(
|
||||
time_evaluator,
|
||||
horizon, # allow waiting time
|
||||
horizon, # maximum time per vehicle
|
||||
False, # don't force start cumul to zero since we are giving TW to start nodes
|
||||
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"]):
|
||||
if location_idx == 0:
|
||||
continue
|
||||
index = routing.NodeToIndex(location_idx)
|
||||
time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1])
|
||||
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 xrange(data["num_vehicles"]):
|
||||
index = routing.Start(vehicle_id)
|
||||
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)))
|
||||
def add_time_window_constraints(routing, manager, data, time_evaluator_index):
|
||||
"""Add Global Span constraint"""
|
||||
time = 'Time'
|
||||
horizon = 120
|
||||
routing.AddDimension(
|
||||
time_evaluator_index,
|
||||
horizon, # allow waiting time
|
||||
horizon, # maximum time per vehicle
|
||||
False, # don't force start cumul to zero since we are giving TW to start nodes
|
||||
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']):
|
||||
if location_idx == 0:
|
||||
continue
|
||||
index = manager.NodeToIndex(location_idx)
|
||||
time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1])
|
||||
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 xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
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)))
|
||||
|
||||
|
||||
###########
|
||||
# Printer #
|
||||
###########
|
||||
def print_solution(data, routing, assignment): # pylint:disable=too-many-locals
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
total_load = 0
|
||||
total_time = 0
|
||||
capacity_dimension = routing.GetDimensionOrDie('Capacity')
|
||||
time_dimension = routing.GetDimensionOrDie('Time')
|
||||
for vehicle_id in xrange(data["num_vehicles"]):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
time_var = time_dimension.CumulVar(index)
|
||||
slack_var = time_dimension.SlackVar(index)
|
||||
plan_output += ' {0} Load({1}) Time({2},{3}) Slack({4},{5}) ->'.format(
|
||||
routing.IndexToNode(index), assignment.Value(load_var),
|
||||
assignment.Min(time_var), assignment.Max(time_var),
|
||||
assignment.Min(slack_var), assignment.Max(slack_var))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index,
|
||||
vehicle_id)
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
time_var = time_dimension.CumulVar(index)
|
||||
slack_var = time_dimension.SlackVar(index)
|
||||
plan_output += ' {0} Load({1}) Time({2},{3})\n'.format(
|
||||
routing.IndexToNode(index), assignment.Value(load_var),
|
||||
assignment.Min(time_var), assignment.Max(time_var))
|
||||
plan_output += 'Distance of the route: {0}m\n'.format(distance)
|
||||
plan_output += 'Load of the route: {}\n'.format(
|
||||
assignment.Value(load_var))
|
||||
plan_output += 'Time of the route: {}\n'.format(
|
||||
assignment.Value(time_var))
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
total_load += assignment.Value(load_var)
|
||||
total_time += assignment.Value(time_var)
|
||||
print('Total Distance of all routes: {0}m'.format(total_distance))
|
||||
print('Total Load of all routes: {}'.format(total_load))
|
||||
print('Total Time of all routes: {0}min'.format(total_time))
|
||||
def print_solution(data, manager, routing, assignment): # pylint:disable=too-many-locals
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
total_load = 0
|
||||
total_time = 0
|
||||
capacity_dimension = routing.GetDimensionOrDie('Capacity')
|
||||
time_dimension = routing.GetDimensionOrDie('Time')
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
time_var = time_dimension.CumulVar(index)
|
||||
slack_var = time_dimension.SlackVar(index)
|
||||
plan_output += ' {0} Load({1}) Time({2},{3}) Slack({4},{5}) ->'.format(
|
||||
manager.IndexToNode(index), assignment.Value(load_var),
|
||||
assignment.Min(time_var), assignment.Max(time_var),
|
||||
assignment.Min(slack_var), assignment.Max(slack_var))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index,
|
||||
vehicle_id)
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
time_var = time_dimension.CumulVar(index)
|
||||
slack_var = time_dimension.SlackVar(index)
|
||||
plan_output += ' {0} Load({1}) Time({2},{3})\n'.format(
|
||||
manager.IndexToNode(index), assignment.Value(load_var),
|
||||
assignment.Min(time_var), assignment.Max(time_var))
|
||||
plan_output += 'Distance of the route: {0}m\n'.format(distance)
|
||||
plan_output += 'Load of the route: {}\n'.format(assignment.Value(load_var))
|
||||
plan_output += 'Time of the route: {}\n'.format(assignment.Value(time_var))
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
total_load += assignment.Value(load_var)
|
||||
total_time += assignment.Value(time_var)
|
||||
print('Total Distance of all routes: {0}m'.format(total_distance))
|
||||
print('Total Load of all routes: {}'.format(total_load))
|
||||
print('Total Time of all routes: {0}min'.format(total_time))
|
||||
|
||||
|
||||
########
|
||||
# Main #
|
||||
########
|
||||
def main():
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(data["num_locations"],
|
||||
data["num_vehicles"], data["depot"])
|
||||
# Define weight of each edge
|
||||
distance_evaluator = create_distance_evaluator(data)
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator)
|
||||
# Add Capacity constraint
|
||||
demand_evaluator = create_demand_evaluator(data)
|
||||
add_capacity_constraints(routing, data, demand_evaluator)
|
||||
# Add Time Window constraint
|
||||
time_evaluator = create_time_evaluator(data)
|
||||
add_time_window_constraints(routing, data, time_evaluator)
|
||||
# Create the routing index manager
|
||||
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
|
||||
data['num_vehicles'], data['depot'])
|
||||
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters()
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, routing, assignment)
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
|
||||
# Define weight of each edge
|
||||
distance_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_distance_evaluator(data), manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index)
|
||||
|
||||
# Add Capacity constraint
|
||||
demand_evaluator_index = routing.RegisterUnaryTransitCallback(
|
||||
partial(create_demand_evaluator(data), manager))
|
||||
add_capacity_constraints(routing, data, demand_evaluator_index)
|
||||
|
||||
# Add Time Window constraint
|
||||
time_evaluator_index = routing.RegisterTransitCallback(
|
||||
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
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, manager, routing, assignment)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
# Copyright 2010-2018 Google LLC
|
||||
# Copyright 2010-2017 Google
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
|
||||
145
examples/python/random_tsp.py
Executable file
145
examples/python/random_tsp.py
Executable file
@@ -0,0 +1,145 @@
|
||||
# Copyright 2010-2018 Google LLC
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""Traveling Salesman Sample.
|
||||
|
||||
This is a sample using the routing library python wrapper to solve a
|
||||
Traveling Salesman Problem.
|
||||
The description of the problem can be found here:
|
||||
http://en.wikipedia.org/wiki/Travelling_salesman_problem.
|
||||
The optimization engine uses local search to improve solutions, first
|
||||
solutions being generated using a cheapest addition heuristic.
|
||||
Optionally one can randomly forbid a set of random connections between nodes
|
||||
(forbidden arcs).
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from functools import partial
|
||||
import random
|
||||
|
||||
from ortools.constraint_solver import routing_enums_pb2
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument(
|
||||
'--tsp_size',
|
||||
default=10,
|
||||
type=int,
|
||||
help='Size of Traveling Salesman Problem instance.')
|
||||
parser.add_argument(
|
||||
'--tsp_use_random_matrix',
|
||||
default=True,
|
||||
type=bool,
|
||||
help='Use random cost matrix.')
|
||||
parser.add_argument(
|
||||
'--tsp_random_forbidden_connections',
|
||||
default=0,
|
||||
type=int,
|
||||
help='Number of random forbidden connections.')
|
||||
parser.add_argument(
|
||||
'--tsp_random_seed', default=0, type=int, help='Random seed.')
|
||||
|
||||
# Cost/distance functions.
|
||||
|
||||
|
||||
def Distance(manager, i, j):
|
||||
"""Sample function."""
|
||||
# Put your distance code here.
|
||||
node_i = manager.IndexToNode(i)
|
||||
node_j = manager.IndexToNode(j)
|
||||
return node_i + node_j
|
||||
|
||||
|
||||
class RandomMatrix(object):
|
||||
"""Random matrix."""
|
||||
|
||||
def __init__(self, size, seed):
|
||||
"""Initialize random matrix."""
|
||||
|
||||
rand = random.Random()
|
||||
rand.seed(seed)
|
||||
distance_max = 100
|
||||
self.matrix = {}
|
||||
for from_node in range(size):
|
||||
self.matrix[from_node] = {}
|
||||
for to_node in range(size):
|
||||
if from_node == to_node:
|
||||
self.matrix[from_node][to_node] = 0
|
||||
else:
|
||||
self.matrix[from_node][to_node] = rand.randrange(distance_max)
|
||||
|
||||
def Distance(self, manager, from_index, to_index):
|
||||
return self.matrix[manager.IndexToNode(from_index)][manager.IndexToNode(
|
||||
to_index)]
|
||||
|
||||
|
||||
def main(args):
|
||||
# Create routing model
|
||||
if args.tsp_size > 0:
|
||||
# TSP of size args.tsp_size
|
||||
# Second argument = 1 to build a single tour (it's a TSP).
|
||||
# Nodes are indexed from 0 to args_tsp_size - 1, by default the start of
|
||||
# the route is node 0.
|
||||
manager = pywrapcp.RoutingIndexManager(args.tsp_size, 1, 0)
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
|
||||
|
||||
# Setting the cost function.
|
||||
# Put a callback to the distance accessor here. The callback takes two
|
||||
# arguments (the from and to node indices) and returns the distance between
|
||||
# these indices.
|
||||
cost = 0
|
||||
if args.tsp_use_random_matrix:
|
||||
matrix = RandomMatrix(args.tsp_size, args.tsp_random_seed)
|
||||
cost = routing.RegisterTransitCallback(partial(matrix.Distance, manager))
|
||||
else:
|
||||
cost = routing.RegisterTransitCallback(partial(Distance, manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(cost)
|
||||
# Forbid node connections (randomly).
|
||||
rand = random.Random()
|
||||
rand.seed(args.tsp_random_seed)
|
||||
forbidden_connections = 0
|
||||
while forbidden_connections < args.tsp_random_forbidden_connections:
|
||||
from_node = rand.randrange(args.tsp_size - 1)
|
||||
to_node = rand.randrange(args.tsp_size - 1) + 1
|
||||
if routing.NextVar(from_node).Contains(to_node):
|
||||
print('Forbidding connection ' + str(from_node) + ' -> ' + str(to_node))
|
||||
routing.NextVar(from_node).RemoveValue(to_node)
|
||||
forbidden_connections += 1
|
||||
|
||||
# Solve, returns a solution if any.
|
||||
assignment = routing.Solve()
|
||||
if assignment:
|
||||
# Solution cost.
|
||||
print(assignment.ObjectiveValue())
|
||||
# Inspect solution.
|
||||
# Only one route here; otherwise iterate from 0 to routing.vehicles() - 1
|
||||
route_number = 0
|
||||
node = routing.Start(route_number)
|
||||
route = ''
|
||||
while not routing.IsEnd(node):
|
||||
route += str(node) + ' -> '
|
||||
node = assignment.Value(routing.NextVar(node))
|
||||
route += '0'
|
||||
print(route)
|
||||
else:
|
||||
print('No solution found.')
|
||||
else:
|
||||
print('Specify an instance greater than 0.')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(parser.parse_args())
|
||||
@@ -507,6 +507,13 @@ def collect_valid_slabs(capacities, colors, widths, loss_array, all_colors):
|
||||
print('Collect Valid Slabs...DONE')
|
||||
return collector.all_solutions()
|
||||
|
||||
num_orders = len(orders)
|
||||
num_capacities = len(capacities)
|
||||
all_slabs = range(num_slabs)
|
||||
all_colors = range(num_colors)
|
||||
all_orders = range(len(orders))
|
||||
print('Solving steel mill with %i orders, %i slabs, and %i capacities' %
|
||||
(num_orders, num_slabs, num_capacities - 1))
|
||||
|
||||
def steel_mill_slab_with_valid_slabs(problem, break_symmetries, output_proto):
|
||||
"""Solves the Steel Mill Slab Problem."""
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""CP-SAT model for task allocation problem.
|
||||
see
|
||||
http://yetanothermathprogrammingconsultant.blogspot.com/2018/09/minizinc-cpsat-vs-mip.html
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""Display Transit Time
|
||||
|
||||
Distances are in meters and time in minutes.
|
||||
|
||||
Manhattan average block: 750ft x 264ft -> 228m x 80m
|
||||
|
||||
@@ -24,7 +24,10 @@
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from functools import partial
|
||||
from six.moves import xrange
|
||||
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
from ortools.constraint_solver import routing_enums_pb2
|
||||
|
||||
@@ -33,101 +36,108 @@ from ortools.constraint_solver import routing_enums_pb2
|
||||
# Problem Data Definition #
|
||||
###########################
|
||||
def create_data_model():
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(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)]
|
||||
# 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["num_vehicles"] = 1
|
||||
data["depot"] = 0
|
||||
return data
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(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)]
|
||||
# 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['num_vehicles'] = 1
|
||||
data['depot'] = 0
|
||||
return data
|
||||
|
||||
|
||||
#######################
|
||||
# Problem Constraints #
|
||||
#######################
|
||||
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]))
|
||||
"""Computes the Manhattan distance between two points"""
|
||||
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 xrange(data["num_locations"]):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data["num_locations"]):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (manhattan_distance(
|
||||
data["locations"][from_node], data["locations"][to_node]))
|
||||
"""Creates callback to return distance between points."""
|
||||
_distances = {}
|
||||
# precompute distance between location to have distance callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (
|
||||
manhattan_distance(data['locations'][from_node],
|
||||
data['locations'][to_node]))
|
||||
|
||||
def distance_evaluator(from_node, to_node):
|
||||
"""Returns the manhattan distance between the two nodes"""
|
||||
return _distances[from_node][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 distance_evaluator
|
||||
return distance_evaluator
|
||||
|
||||
|
||||
###########
|
||||
# Printer #
|
||||
###########
|
||||
def print_solution(routing, assignment):
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
index = routing.Start(0)
|
||||
plan_output = 'Route:\n'
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
plan_output += ' {} ->'.format(routing.IndexToNode(index))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index, 0)
|
||||
plan_output += ' {}\n'.format(routing.IndexToNode(index))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
print(plan_output)
|
||||
def print_solution(routing, manager, assignment):
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
index = routing.Start(0)
|
||||
plan_output = 'Route:\n'
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
plan_output += ' {} ->'.format(manager.IndexToNode(index))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index, 0)
|
||||
plan_output += ' {}\n'.format(manager.IndexToNode(index))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
print(plan_output)
|
||||
|
||||
|
||||
########
|
||||
# Main #
|
||||
########
|
||||
def main():
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(data["num_locations"],
|
||||
data["num_vehicles"], data["depot"])
|
||||
# Define weight of each edge
|
||||
distance_evaluator = create_distance_evaluator(data)
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator)
|
||||
# Create the routing index manager
|
||||
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
|
||||
data['num_vehicles'], data['depot'])
|
||||
|
||||
# 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)
|
||||
print_solution(routing, assignment)
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
|
||||
# Define weight of each edge
|
||||
distance_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_distance_evaluator(data), manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_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
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(routing, manager, assignment)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -24,7 +24,10 @@
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from functools import partial
|
||||
from six.moves import xrange
|
||||
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
from ortools.constraint_solver import routing_enums_pb2
|
||||
|
||||
@@ -33,106 +36,113 @@ from ortools.constraint_solver import routing_enums_pb2
|
||||
# Problem Data Definition #
|
||||
###########################
|
||||
def create_data_model():
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(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)]
|
||||
# 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["num_vehicles"] = 4
|
||||
data["depot"] = 0
|
||||
return data
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(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)]
|
||||
# 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['num_vehicles'] = 4
|
||||
data['depot'] = 0
|
||||
return data
|
||||
|
||||
|
||||
#######################
|
||||
# Problem Constraints #
|
||||
#######################
|
||||
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]))
|
||||
"""Computes the Manhattan distance between two points"""
|
||||
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 xrange(data["num_locations"]):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data["num_locations"]):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (manhattan_distance(
|
||||
data["locations"][from_node], data["locations"][to_node]))
|
||||
"""Creates callback to return distance between points."""
|
||||
_distances = {}
|
||||
# precompute distance between location to have distance callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (
|
||||
manhattan_distance(data['locations'][from_node],
|
||||
data['locations'][to_node]))
|
||||
|
||||
def distance_evaluator(from_node, to_node):
|
||||
"""Returns the manhattan distance between the two nodes"""
|
||||
return _distances[from_node][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 distance_evaluator
|
||||
return distance_evaluator
|
||||
|
||||
|
||||
###########
|
||||
# Printer #
|
||||
###########
|
||||
def print_solution(data, routing, assignment):
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
for vehicle_id in xrange(data["num_vehicles"]):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
plan_output += ' {} ->'.format(routing.IndexToNode(index))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index,
|
||||
vehicle_id)
|
||||
plan_output += ' {}\n'.format(routing.IndexToNode(index))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
print('Total Distance of all routes: {}m'.format(total_distance))
|
||||
def print_solution(data, routing, manager, assignment): # pylint:disable=too-many-locals
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
plan_output += ' {} ->'.format(manager.IndexToNode(index))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index,
|
||||
vehicle_id)
|
||||
plan_output += ' {}\n'.format(manager.IndexToNode(index))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
print('Total Distance of all routes: {}m'.format(total_distance))
|
||||
|
||||
|
||||
########
|
||||
# Main #
|
||||
########
|
||||
def main():
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(data["num_locations"],
|
||||
data["num_vehicles"], data["depot"])
|
||||
# Define weight of each edge
|
||||
distance_evaluator = create_distance_evaluator(data)
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator)
|
||||
# Create the routing index manager
|
||||
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
|
||||
data['num_vehicles'], data['depot'])
|
||||
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters()
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, routing, assignment)
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
|
||||
# Define weight of each edge
|
||||
distance_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_distance_evaluator(data), manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_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
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, routing, manager, assignment)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -24,7 +24,10 @@
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from functools import partial
|
||||
from six.moves import xrange
|
||||
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
from ortools.constraint_solver import routing_enums_pb2
|
||||
|
||||
@@ -33,122 +36,129 @@ from ortools.constraint_solver import routing_enums_pb2
|
||||
# Problem Data Definition #
|
||||
###########################
|
||||
def create_data_model():
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(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)]
|
||||
# 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["num_vehicles"] = 4
|
||||
data["depot"] = 0
|
||||
return data
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(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)]
|
||||
# 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['num_vehicles'] = 4
|
||||
data['depot'] = 0
|
||||
return data
|
||||
|
||||
|
||||
#######################
|
||||
# Problem Constraints #
|
||||
#######################
|
||||
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]))
|
||||
"""Computes the Manhattan distance between two points"""
|
||||
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 xrange(data["num_locations"]):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data["num_locations"]):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (manhattan_distance(
|
||||
data["locations"][from_node], data["locations"][to_node]))
|
||||
"""Creates callback to return distance between points."""
|
||||
_distances = {}
|
||||
# precompute distance between location to have distance callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (
|
||||
manhattan_distance(data['locations'][from_node],
|
||||
data['locations'][to_node]))
|
||||
|
||||
def distance_evaluator(from_node, to_node):
|
||||
"""Returns the manhattan distance between the two nodes"""
|
||||
return _distances[from_node][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 distance_evaluator
|
||||
return distance_evaluator
|
||||
|
||||
|
||||
def add_distance_dimension(routing, distance_evaluator):
|
||||
"""Add Global Span constraint"""
|
||||
distance = 'Distance'
|
||||
routing.AddDimension(
|
||||
distance_evaluator,
|
||||
0, # null slack
|
||||
3000, # 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)
|
||||
def add_distance_dimension(routing, distance_evaluator_index):
|
||||
"""Add Global Span constraint"""
|
||||
distance = 'Distance'
|
||||
routing.AddDimension(
|
||||
distance_evaluator_index,
|
||||
0, # null slack
|
||||
3000, # 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 #
|
||||
###########
|
||||
def print_solution(data, routing, assignment):
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
for vehicle_id in xrange(data["num_vehicles"]):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
plan_output += ' {} ->'.format(routing.IndexToNode(index))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index,
|
||||
vehicle_id)
|
||||
plan_output += ' {}\n'.format(routing.IndexToNode(index))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
print('Total Distance of all routes: {}m'.format(total_distance))
|
||||
def print_solution(data, routing, manager, assignment): # pylint:disable=too-many-locals
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
plan_output += ' {} ->'.format(manager.IndexToNode(index))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index,
|
||||
vehicle_id)
|
||||
plan_output += ' {}\n'.format(manager.IndexToNode(index))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
print('Total Distance of all routes: {}m'.format(total_distance))
|
||||
|
||||
|
||||
########
|
||||
# Main #
|
||||
########
|
||||
def main():
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(data["num_locations"],
|
||||
data["num_vehicles"], data["depot"])
|
||||
# Define weight of each edge
|
||||
distance_evaluator = create_distance_evaluator(data)
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator)
|
||||
add_distance_dimension(routing, distance_evaluator)
|
||||
# Create the routing index manager
|
||||
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
|
||||
data['num_vehicles'], data['depot'])
|
||||
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters()
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, routing, assignment)
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
|
||||
# Define weight of each edge
|
||||
distance_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_distance_evaluator(data), manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index)
|
||||
add_distance_dimension(routing, distance_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
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, routing, manager, assignment)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user