I am using OR tools to solve VRP without any constraints. Here is the source code:
def create_data_model():
"""Stores the data for the problem."""
data = {}
data['distance_matrix'] = [
[0, 20079, 2613, 8005, 19277, 12468, 13701],
[0, 0, 21285, 16012, 32574, 35394, 28806],
[0, 18233, 0, 5392, 19965, 19650, 13064],
[0, 15013, 5639, 0, 22883, 22570, 15982],
[0, 32991, 19256, 21815, 0, 18414, 9112],
[0, 34348, 16976, 23122, 15678, 0, 14647],
[0, 27652, 13917, 16476, 8043, 14820, 0]
]
data['time_matrix'] = [
[0, 1955, 508, 1331, 1474, 1427, 1292],
[0, 0, 1795, 1608, 2057, 2410, 2036],
[0, 1485, 0, 823, 1370, 1541, 1100],
[0, 1402, 924, 0, 1533, 1637, 1263],
[0, 2308, 1663, 1853, 0, 1766, 1104],
[0, 2231, 1373, 1660, 1441, 0, 1554],
[0, 1998, 1353, 1543, 764, 1550, 0]
]
data['num_vehicles'] = 6
data['depot'] = 0
return data
def print_solution(data, manager, routing, solution):
"""Prints solution on console."""
max_route_distance = 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
while not routing.IsEnd(index):
plan_output += ' {} -> '.format(manager.IndexToNode(index))
previous_index = index
index = solution.Value(routing.NextVar(index))
route_distance += routing.GetArcCostForVehicle(
previous_index, index, vehicle_id)
plan_output += '{}\n'.format(manager.IndexToNode(index))
plan_output += 'Distance of the route: {}m\n'.format(route_distance)
print(plan_output)
max_route_distance = max(route_distance, max_route_distance)
print('Maximum of the route distances: {}m'.format(max_route_distance))
def test(request):
# Instantiate the data problem.
data = create_data_model()
# Create the routing index manager.
manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),
data['num_vehicles'], data['depot'])
# Create Routing Model.
routing = pywrapcp.RoutingModel(manager)
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)
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
dimension_name = 'Distance'
routing.AddDimension(
transit_callback_index,
0, # no slack
1000000000, # vehicle maximum travel distance
True, # start cumul to zero
dimension_name)
distance_dimension = routing.GetDimensionOrDie(dimension_name)
distance_dimension.SetGlobalSpanCostCoefficient(35394)
# Setting first solution heuristic.
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
# Solve the problem.
solution = routing.SolveWithParameters(search_parameters)
# Print solution on console.
if solution:
print_solution(data, manager, routing, solution)
return HttpResponse('')
Everything above was copy-pasted from google's example, except for the following:
The result for the above code is shown below:
Route for vehicle 0:
0 -> 1 -> 0
Distance of the route: 20079m
Route for vehicle 1:
0 -> 5 -> 0
Distance of the route: 12468m
Route for vehicle 2:
0 -> 4 -> 0
Distance of the route: 19277m
Route for vehicle 3:
0 -> 2 -> 3 -> 0
Distance of the route: 8005m
Route for vehicle 4:
0 -> 6 -> 0
Distance of the route: 13701m
Route for vehicle 5:
0 -> 0
Distance of the route: 0m
Maximum of the route distances: 20079m
To verify the above output, I marked the points in the google map. The numbering order is same as the order of the distance matrix.
The depot(starting point) is in Watthana, which can be seen near marker B.
Clearly, from Watthana, the cheapest path should be 2-1-3 in a single trip. But Google OR returns it as two trips (as seen in routes for vehicles 0 and 3). This can also be verified by manually adding the distances.
Dist from home to 2 to 1 to 3 = 2613+5392+15013 = 23018m
Dist of vehicles 0 and 3 = 20079+8005 = 28084m
What am I doing wrong? How can I get google to not separate out the point 1? Also please note that ideally points E,F,D could have also been grouped but they were not.
Thanks in advance!
From the question, I think what you want is to reduce the cumulative distance traveled by all vehicles.
distance_dimension.SetGlobalSpanCostCoefficient(35394)
Conversely, This code makes sure that the distance traveled by each vehicle is minimized by adding a Span Cost to the objective function weighted by 35394.
global_span_cost =
coefficient * (Max(dimension end value) - Min(dimension start value))
In your case this is not a very high priority, hence the solution would be to comment that line or reduce the coefficient to a small value like 1 or 2, to reduce the sole importance of it.
Read more about GSpanCoeff
Now the solution should be
Route for vehicle 0:
0 -> 2 -> 3 -> 1 -> 0
Distance of the route: 23018m
Route for vehicle 1:
0 -> 6 -> 4 -> 0
Distance of the route: 21744m
Route for vehicle 2:
0 -> 5 -> 0
Distance of the route: 12468m
Maximum of the route distances: 23018m
Sum of the route distances: 57230m