Reputation: 13
I am using the OR-tools code to optimize bus routes with distance and capacity constraints. This is the output that I got:
Route for vehicle 0:
Stop 0 Students(2) -> Stop 3 Students(37) -> Stop 5 Students(37)
Duration of the route: 48m
Number of students of the route: 37
Route for vehicle 1:
Stop 6 Students(4) -> Stop 4 Students(38) -> Stop 9 Students(41) -> Stop 19 Students(45) -> Stop 5 Students(45)
Duration of the route: 55m
Number of students of the route: 45
Route for vehicle 2:
Stop 7 Students(4) -> Stop 1 Students(39) -> Stop 8 Students(40) -> Stop 18 Students(43) -> Stop 5 Students(43)
Duration of the route: 56m
Number of students of the route: 43
Route for vehicle 3:
Stop 11 Students(1) -> Stop 10 Students(2) -> Stop 20 Students(12) -> Stop 23 Students(43) -> Stop 5 Students(43)
Duration of the route: 54m
Number of students of the route: 43
Route for vehicle 4:
Stop 12 Students(1) -> Stop 2 Students(36) -> Stop 22 Students(39) -> Stop 21 Students(45) -> Stop 5 Students(45)
Duration of the route: 58m
Number of students of the route: 45
Route for vehicle 5:
Stop 13 Students(1) -> Stop 14 Students(4) -> Stop 5 Students(4)
Duration of the route: 60m
Number of students of the route: 4
Route for vehicle 6:
Stop 16 Students(3) -> Stop 15 Students(6) -> Stop 17 Students(12) -> Stop 5 Students(12)
Duration of the route: 57m
Number of students of the route: 12
Route for vehicle 7:
Stop 24 Students(3) -> Stop 5 Students(3)
Duration of the route: 0m
Number of students of the route: 3
Route for vehicle 8:
Stop 25 Students(3) -> Stop 5 Students(3)
Duration of the route: 0m
Number of students of the route: 3
Total duration of all routes: 388m
Total number of students of all routes: 235
The durations for vehicles 0-6 seem about right, but vehicles 7 and 8 have durations of 0. I checked the matrix multiple times, and it doesn't seem like my matrix/data is the problem. I think there could be a problem with the while not section under def print_solution, but I am not sure how to change it. Because there are only two stops, I think it's not running the while not section that adds up the durations for each route.
#@CVRP ()
"""Capacited Vehicles Routing Problem (CVRP)."""
!pip install ortools
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
from google.colab import drive
import pandas as pd
import numpy as np
import csv
reader = csv.reader(open("Duration.csv", "r"), delimiter=",")
x = list(reader)
result = np.array(x).astype("float")
def create_data_model():
"""Stores the data for the problem."""
data = {}
data['distance_matrix'] = result
# stops: huayuan, aocheng1, aocheng2, aocheng3, aocheng4, school, yangguang, aocheng back, olympics village, diamond hill
# wuyiyangguang, hopeland, haotian, !!shanhaitian!!, tianjiaoyuan, olympics tower, international building, tangchen, !!astor hotel!!,
# jinlanque, hongzonglv, lanwan, !!wangzuo!!, !!haiyi back!!, haiyi, liusuyuan, !!muxuyuan!!, swan lake, bandao, b!!andao back!!, mingquan, kameier, !!fulijinmenhu!!
#data['demands'] = [2, 35, 35, 35, 34, 0, 4, 4, 1, 3, 1, 1, 1, 0, 1, 3, 3, 3, 0, 6, 3, 4, 0, 0, 10, 6, 0, 3, 31, 0, 3, 3, 0]
#UPDATED stop names:huayuan, aocheng1, aocheng2, aocheng3, aocheng4, school, yangguang, aocheng back, olympics village, diamond hill
# wuyiyangguang, hopeland, haotian, tianjiaoyuan, olympics tower, international building, tangchen,
# jinlanque, hongzonglv, lanwan, haiyi, liusuyuan, swan lake, bandao, mingquan, kameier
data['demands'] = [2, 35, 35, 35, 34, 0, 4, 4, 1, 3, 1, 1, 1, 1, 3, 3, 3, 6, 3, 4, 10, 6, 3, 31, 3, 3]
data['vehicle_capacities'] = [45, 45, 45, 45, 45, 45, 49, 15, 15]
data['num_vehicles'] = 9
data['starts'] = [0, 6, 7, 11, 12, 13, 16, 24, 25]
data['ends'] = [5, 5, 5, 5, 5, 5, 5, 5, 5]
return data
def print_solution(data, manager, routing, solution):
"""Prints solution on console."""
total_distance = 0
total_load = 0
for vehicle_id in range(data['num_vehicles']):
index = routing.Start(vehicle_id)
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
route_distance = 0
route_load = 0
while not routing.IsEnd(index):
node_index = manager.IndexToNode(index)
route_load += data['demands'][node_index]
plan_output += ' Stop {0} Students({1}) -> '.format(node_index, route_load)
previous_index = index
index = solution.Value(routing.NextVar(index))
route_distance += routing.GetArcCostForVehicle(
previous_index, index, vehicle_id)
plan_output += ' Stop {0} Students({1})\n'.format(manager.IndexToNode(index),
route_load)
plan_output += 'Duration of the route: {}m\n'.format(route_distance)
plan_output += 'Number of students of the route: {}\n'.format(route_load)
print(plan_output)
total_distance += route_distance
total_load += route_load
print('Total duration of all routes: {}m'.format(total_distance))
print('Total number of students of all routes: {}'.format(total_load))
def main():
"""Solve the CVRP problem."""
# Instantiate the data problem.
data = create_data_model()
# Create the routing index manager.
manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),
data['num_vehicles'], data['starts'],
data['ends'])
# Create Routing Model.
routing = pywrapcp.RoutingModel(manager)
# Create and register a transit callback.
def distance_callback(from_index, to_index):
"""Returns the distance between the two nodes."""
# Convert from routing variable Index to distance matrix NodeIndex.
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return data['distance_matrix'][from_node][to_node]
transit_callback_index = routing.RegisterTransitCallback(distance_callback)
#Define cost of each arc.
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
# Add Capacity constraint.
def demand_callback(from_index):
"""Returns the demand of the node."""
# Convert from routing variable Index to demands NodeIndex.
from_node = manager.IndexToNode(from_index)
return data['demands'][from_node]
demand_callback_index = routing.RegisterUnaryTransitCallback(
demand_callback)
dimension_name = 'Capacity'
routing.AddDimensionWithVehicleCapacity(
demand_callback_index,
0, # null capacity slack
data['vehicle_capacities'], # vehicle maximum capacities
True, # start cumul to zero
dimension_name)
# Add Distance constraint.
dimension_name = 'Distance'
routing.AddDimension(
transit_callback_index,
0, # no slack
100, # vehicle maximum travel distance
True, # start cumul to zero
dimension_name)
distance_dimension = routing.GetDimensionOrDie(dimension_name)
distance_dimension.SetGlobalSpanCostCoefficient(100)
# Setting first solution heuristic.
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
search_parameters.local_search_metaheuristic = (
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)
search_parameters.log_search = True
search_parameters.time_limit.FromSeconds(5)
# Solve the problem.
solution = routing.SolveWithParameters(search_parameters)
# Print solution.
if solution:
print_solution(data, manager, routing, solution)
#else:
#print('no solution')
if __name__ == '__main__':
main()
data = create_data_model()
#print(len(data['distance_matrix']))
#print(len(data['distance_matrix'][0]))
It would be much appreciated if someone can help me!
Upvotes: 1
Views: 671
Reputation: 9281
By default, the arc cost for empty route aka Start -> End
is zero.
So your last two vehicle routes, being empty, will return 0 when using routing.GetArcCostForVehicle(previous_index, index, vehicle_id)
.
You can enable the cost using RoutingModel::ConsiderEmptyRouteCostsForVehicle(bool consider_costs, int vehicle)
.
Something like this should work IMHO:
for v in range(manager.GetNumberOfVehicles()):
routing.ConsiderEmptyRouteCostsForVehicle(true, v)
Upvotes: 1