Fix cvrptw_plot.py

This commit is contained in:
Corentin Le Molgat
2019-03-15 16:19:09 +01:00
parent 814935dab7
commit c4aaa4321d

View File

@@ -160,6 +160,9 @@ class Customers():
# The number of seconds needed to 'unload' 1 unit of goods.
self.service_time_per_dem = 300 # seconds
def set_manager(self, manager):
self.manager = manager
def central_start_node(self, invert=False):
"""
Return a random starting node, with probability weighted by distance
@@ -271,8 +274,11 @@ class Customers():
"""
self.make_distance_mat(**kwargs)
def dist_return(a, b):
return (self.distmat[a][b])
def dist_return(from_index, to_index):
# Convert from routing variable Index to distance matrix NodeIndex.
from_node = self.manager.IndexToNode(from_index)
to_node = self.manager.IndexToNode(to_index)
return (self.distmat[from_node][to_node])
return dist_return
@@ -285,8 +291,11 @@ class Customers():
index and the 'to' node index and returns the distance in km.
"""
def dem_return(a, b):
return (self.customers[a].demand)
def dem_return(from_index, to_index):
# Convert from routing variable Index to distance matrix NodeIndex.
from_node = self.manager.IndexToNode(from_index)
to_node = self.manager.IndexToNode(to_index)
return (self.customers[from_node].demand)
return dem_return
@@ -432,7 +441,7 @@ def discrete_cmap(N, base_cmap=None):
return base.from_list(cmap_name, color_list, N)
def vehicle_output_string(routing, plan):
def vehicle_output_string(manager, routing, plan):
"""
Return a string displaying the output of the routing instance and
assignment (plan).
@@ -464,9 +473,10 @@ def vehicle_output_string(routing, plan):
while True:
load_var = capacity_dimension.CumulVar(order)
time_var = time_dimension.CumulVar(order)
node = manager.IndexToNode(order)
plan_output += \
' {order} Load({load}) Time({tmin}, {tmax}) -> '.format(
order=order,
' {node} Load({load}) Time({tmin}, {tmax}) -> '.format(
node=node,
load=plan.Value(load_var),
tmin=str(timedelta(seconds=plan.Min(time_var))),
tmax=str(timedelta(seconds=plan.Max(time_var))))
@@ -480,7 +490,7 @@ def vehicle_output_string(routing, plan):
return (plan_output, dropped)
def build_vehicle_route(routing, plan, customers, veh_number):
def build_vehicle_route(manager, routing, plan, customers, veh_number):
"""
Build a route for a vehicle by starting at the strat node and
continuing to the end node.
@@ -498,12 +508,12 @@ def build_vehicle_route(routing, plan, customers, veh_number):
if veh_used:
route = []
node = routing.Start(veh_number) # Get the starting node index
route.append(customers.customers[routing.IndexToNode(node)])
route.append(customers.customers[manager.IndexToNode(node)])
while not routing.IsEnd(node):
route.append(customers.customers[routing.IndexToNode(node)])
route.append(customers.customers[manager.IndexToNode(node)])
node = plan.Value(routing.NextVar(node))
route.append(customers.customers[routing.IndexToNode(node)])
route.append(customers.customers[manager.IndexToNode(node)])
return route
else:
return None
@@ -575,18 +585,6 @@ def main():
min_tw=3,
max_tw=6)
# Create callback fns for distances, demands, service and transit-times.
dist_fn = customers.return_dist_callback()
dem_fn = customers.return_dem_callback()
serv_time_fn = customers.make_service_time_call_callback()
transit_time_fn = customers.make_transit_time_callback()
def tot_time_fn(a, b):
"""
The time function we want is both transit time and service time.
"""
return serv_time_fn(a, b) + transit_time_fn(a, b)
# Create a list of inhomgenious vehicle capacities as integer units.
capacity = [50, 75, 100, 125, 150, 175, 200, 250]
@@ -604,8 +602,17 @@ def main():
start_fn = vehicles.return_starting_callback(
customers, sameStartFinish=False)
# Create the routing index manager.
manager = pywrapcp.RoutingIndexManager(
customers.number, # int number
vehicles.number, # int number
vehicles.starts, # List of int start depot
vehicles.ends) # List of int end depot
customers.set_manager(manager)
# Set model parameters
model_parameters = pywrapcp.RoutingModel.DefaultModelParameters()
model_parameters = pywrapcp.DefaultRoutingModelParameters()
# The solver parameters can be accessed from the model parameters. For example :
# model_parameters.solver_parameters.CopyFrom(
@@ -613,30 +620,46 @@ def main():
# model_parameters.solver_parameters.trace_propagation = True
# Make the routing model instance.
routing = pywrapcp.RoutingModel(
customers.number, # int number
vehicles.number, # int number
vehicles.starts, # List of int start depot
vehicles.ends, # List of int end depot
model_parameters)
routing = pywrapcp.RoutingModel(manager, model_parameters)
parameters = routing.DefaultSearchParameters()
parameters = pywrapcp.DefaultRoutingSearchParameters()
# Setting first solution heuristic (cheapest addition).
parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
# Routing: forbids use of TSPOpt neighborhood, (this is the default behaviour)
parameters.local_search_operators.use_tsp_opt = pywrapcp.BOOL_FALSE
# Disabling Large Neighborhood Search, (this is the default behaviour)
parameters.local_search_operators.use_path_lns = False
parameters.local_search_operators.use_inactive_lns = False
# Routing: forbids use of TSPOpt neighborhood,
parameters.local_search_operators.use_tsp_opt = False
parameters.local_search_operators.use_path_lns = pywrapcp.BOOL_FALSE
parameters.local_search_operators.use_inactive_lns = pywrapcp.BOOL_FALSE
parameters.time_limit_ms = 10 * 1000 # 10 seconds
parameters.use_light_propagation = False
# parameters.log_search = True
parameters.time_limit.seconds = 10
parameters.use_full_propagation = True
#parameters.log_search = True
# Create callback fns for distances, demands, service and transit-times.
dist_fn = customers.return_dist_callback()
dist_fn_index = routing.RegisterTransitCallback(dist_fn)
dem_fn = customers.return_dem_callback()
dem_fn_index = routing.RegisterTransitCallback(dem_fn)
# Create and register a transit callback.
serv_time_fn = customers.make_service_time_call_callback()
transit_time_fn = customers.make_transit_time_callback()
def tot_time_fn(from_index, to_index):
"""
The time function we want is both transit time and service time.
"""
# Convert from routing variable Index to distance matrix NodeIndex.
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return serv_time_fn(from_node, to_node) + transit_time_fn(from_node, to_node)
tot_time_fn_index = routing.RegisterTransitCallback(tot_time_fn)
# Set the cost function (distance callback) for each arc, homogenious for
# all vehicles.
routing.SetArcCostEvaluatorOfAllVehicles(dist_fn)
routing.SetArcCostEvaluatorOfAllVehicles(dist_fn_index)
# Set vehicle costs for each vehicle, not homogenious.
for veh in vehicles.vehicles:
@@ -645,14 +668,14 @@ def main():
# Add a dimension for vehicle capacities
null_capacity_slack = 0
routing.AddDimensionWithVehicleCapacity(
dem_fn, # demand callback
dem_fn_index, # demand callback
null_capacity_slack,
capacity, # capacity array
True,
'Capacity')
# Add a dimension for time and a limit on the total time_horizon
routing.AddDimension(
tot_time_fn, # total time function callback
tot_time_fn_index, # total time function callback
customers.time_horizon,
customers.time_horizon,
True,
@@ -661,7 +684,7 @@ def main():
time_dimension = routing.GetDimensionOrDie('Time')
for cust in customers.customers:
if cust.tw_open is not None:
time_dimension.CumulVar(routing.NodeToIndex(cust.index)).SetRange(
time_dimension.CumulVar(manager.NodeToIndex(cust.index)).SetRange(
cust.tw_open.seconds, cust.tw_close.seconds)
"""
To allow the dropping of orders, we add disjunctions to all the customer
@@ -674,7 +697,7 @@ def main():
non_depot.difference_update(vehicles.starts)
non_depot.difference_update(vehicles.ends)
penalty = 400000 # The cost for dropping a node from the plan.
nodes = [routing.AddDisjunction([int(c)], penalty) for c in non_depot]
nodes = [routing.AddDisjunction([manager.NodeToIndex(c)], penalty) for c in non_depot]
# This is how you would implement partial routes if you already knew part
# of a feasible solution for example:
@@ -693,15 +716,15 @@ def main():
# The rest is all optional for saving, printing or plotting the solution.
if assignment:
# save the assignment, (Google Protobuf format)
save_file_base = os.path.realpath(__file__).split('.')[0]
if routing.WriteAssignment(save_file_base + '_assignment.ass'):
print('succesfully wrote assignment to file ' + save_file_base +
'_assignment.ass')
## save the assignment, (Google Protobuf format)
#save_file_base = os.path.realpath(__file__).split('.')[0]
#if routing.WriteAssignment(save_file_base + '_assignment.ass'):
# print('succesfully wrote assignment to file ' + save_file_base +
# '_assignment.ass')
print('The Objective Value is {0}'.format(assignment.ObjectiveValue()))
plan_output, dropped = vehicle_output_string(routing, assignment)
plan_output, dropped = vehicle_output_string(manager, routing, assignment)
print(plan_output)
print('dropped nodes: ' + ', '.join(dropped))
@@ -710,7 +733,7 @@ def main():
vehicle_routes = {}
for veh in range(vehicles.number):
vehicle_routes[veh] = build_vehicle_route(routing, assignment,
vehicle_routes[veh] = build_vehicle_route(manager, routing, assignment,
customers, veh)
# Plotting of the routes in matplotlib.
@@ -721,6 +744,7 @@ def main():
ax.plot(clon, clat, 'k.')
# plot the routes as arrows
plot_vehicle_routes(vehicle_routes, ax, customers, vehicles)
plt.show()
else:
print('No assignment')