简体   繁体   中英

How do I interpret the Vehicle Routing Problem solution returned from Google OR Tools?

I have a working Vehicle Routing Problem solution implemented using Google's OR Tools python library. I have a time matrix of 9 locations, and time windows for each location. All values are in units of seconds .

(For example, the first time window is from 28800 to 28800. 28800 seconds is equivalent to 8:00am. I want this location, the depot , to be visited at exactly 8:00am)

I am intentionally solving this with just one vehicle (essentially solving a Traveling Salesperson Problem). I believe that I have added my dimension correctly, but I could certainly have made a mistake with that - my intention is to allow the vehicle to wait at any location for as long as it would like, as along as it allows it to solve the vehicle routing problem. I set the upper bound maximum value as 86400 because there are 86400 seconds in a day, and I figure that would be a sufficiently high number given this data.

Source

from ortools.constraint_solver import pywrapcp
from ortools.constraint_solver import routing_enums_pb2

Matrix = [
  [0,557,763,1156,813,618,822,700,112],       # Depot
  [523,0,598,1107,934,607,658,535,589],       # 1 - Location
  [631,480,0,968,960,570,451,135,582],        # 2 - Location
  [1343,1247,1367,0,1270,1289,809,1193,1253], # 3 - Location
  [746,1000,1135,1283,0,1003,1186,1071,776],  # 4 - Location
  [685,627,810,1227,990,0,712,709,550],       # 5 - Location
  [869,718,558,732,1105,650,0,384,821],       # 6 - Location
  [679,528,202,878,1008,618,412,0,630],       # 7 - Location
  [149,626,762,1124,696,532,821,698,0]        # 8 - Location
]

Windows = [
  [ 28800, 28800 ], # Depot
  [ 43200, 43200 ], # 1 - Location
  [ 50400, 50400 ], # 2 - Location
  [ 21600, 79200 ], # 3 - Location
  [ 21600, 79200 ], # 4 - Location
  [ 21600, 79200 ], # 5 - Location
  [ 21600, 79200 ], # 6 - Location
  [ 21600, 79200 ], # 7 - Location
  [ 21600, 79200 ]  # 8 - Location
]

# Create the routing index manager.
manager = pywrapcp.RoutingIndexManager(len(Matrix), 1, 0)

# Create Routing Model.
routing = pywrapcp.RoutingModel(manager)

# Create and register a transit callback.
def time_callback(from_index, to_index):
  # Returns the travel time between the two nodes.
  # Convert from routing variable Index to time matrix NodeIndex.
  from_node = manager.IndexToNode(from_index)
  to_node = manager.IndexToNode(to_index)
  return Matrix[from_node][to_node]

transit_callback_index = routing.RegisterTransitCallback(time_callback)

# Define cost of each arc.
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)

# Add Time Windows constraint.
routing.AddDimension(
    transit_callback_index,
    86400,  # An upper bound for slack (the wait times at the locations).
    86400,  # An upper bound for the total time over each vehicle's route.
    False,  # Determine whether the cumulative variable is set to zero at the start of the vehicle's route.
    'Time')
time_dimension = routing.GetDimensionOrDie('Time')

# Add time window constraints for each location except depot.
for location_idx, time_window in enumerate(Windows):
  if location_idx == 0:
    continue
  index = manager.NodeToIndex(location_idx)
  time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1])

# Add time window constraints for each vehicle start node.
index = routing.Start(0)
time_dimension.CumulVar(index).SetRange(Windows[0][0],Windows[0][1])

# Instantiate route start and end times to produce feasible times.
routing.AddVariableMinimizedByFinalizer(time_dimension.CumulVar(routing.Start(0)))
routing.AddVariableMinimizedByFinalizer(time_dimension.CumulVar(routing.End(0)))

# Setting first solution heuristic. 
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)

# Setting local search metaheuristics:
search_parameters.local_search_metaheuristic = (routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)
search_parameters.time_limit.seconds = 5
search_parameters.log_search = False

# Solve the problem.
solution = routing.SolveWithParameters(search_parameters)

# Return the solution.
time = 0
index = routing.Start(0)
print("Locations:")
while not routing.IsEnd(index):
  time = time_dimension.CumulVar(index)
  print("{0} ({1}, {2})".format(manager.IndexToNode(index),solution.Min(time),solution.Max(time)))
  index = solution.Value(routing.NextVar(index))
print("{0} ({1}, {2})".format(manager.IndexToNode(index),solution.Min(time),solution.Max(time)))

Output

Locations:
0 (28800, 28800)
8 (28912, 42041)
5 (29444, 42573)
1 (43200, 43200)
2 (50400, 50400)
7 (50535, 50535)
6 (50947, 50947)
3 (51679, 51679)
4 (52949, 52949)
0 (52949, 52949)

My question is in regards to the output that the solution has calculated for me. I am confused about the time windows for the second and third locations in the solution. I expected all of the time windows to look like the rest of the result. What do the solution.Min() and solution.Max() values mean in the scope of this problem when I'm processing my solution? Are there any blatant errors in my usage of OR Tools?

What I understand of this tuples is that you have

(Min_time, Max_time)

Where Min_time is the minimum time that you should arrive to satisfy the Time Window. For the Max_time is exactly the same logic.

The program outputs a range when you can arrive to the node satisfying the constraints.

Locations:
0 (28800, 28800) // must arrive and leave no later than 28800
8 (28912, 42041) // must arrive at or after 28912 and leave no later than 42041
5 (29444, 42573) // must arrive at or after 29444and leave no later than 42573
1 (43200, 43200) // must arrive and leave no later than 43200
2 (50400, 50400) // must arrive and leave no later than 50400

See the comments I have added. When the arrival time is a range as, say node 8 or 5, it basically means the arrival time needs to fall in that time range. The solution remains feasible so long that condition is met.

You can verify it as follows:

Depot [28800, 28800] -> Travel (0, 8) 112-> Loc 8 [21600, 79200] -> Travel (8, 5) 532 -> Loc 5 [21600, 79200] -> Travel (5, 1) 685 -> Loc 1 [43200, 43200]

Departing at the depot at time 28800 with a travel time of 112 will have you arrive at loc 8 at time 28912 (the min value in your solution), departing immediately with a travel time of 532 will have you reach loc 5 at time 29444.

Now, loc 1 has a single time slot available, which is 43200 . So if the vehicle were to leave at time 29444 with a travel time of 627 it would reach loc 1 at time 30071 , which is not a valid arrival time. But if the vehicle were to depart at 43200-627= 42573 it would arrive on time. So this means the vehicle needs to be idle (slack) for a little while before it can go. As both loc 8 and loc 5 have a range, the solution is stating that there is some slack available at those locations. So what the min and max values are really telling you is the solution is feasible so long the arrival and departure are within those ranges.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM