我已经使用 or-tools 几个月了,但最近遇到了以下问题:
模型:
- 2辆车,每辆车都有一个起始位置
s0, s1
和一个结束位置t0, t1
。 - 2个参观地点
x0, x1
。
时间窗口:
[[5400, 5820], [9000, 9420], [5520, 39719], [6420, 43319], [5521, 39720], [6421, 43320]]
持续时间矩阵:
[
x0: [0, horizon, horizon, horizon, 5400, 5400],
x1: [horizon, 0, horizon, horizon, 1800, 1800],
s0: [0, 0, horizon, horizon, 0, horizon],
s1: [0, 0, horizon, horizon, horizon, 0],
t0: [horizon, horizon, horizon, horizon, horizon, horizon],
t1: [horizon, horizon, horizon, horizon, horizon, horizon]
]
哪里horizon = 86760
- 这只是一个很大的价值,可以忽略这个潜在的任务。
None
当我将它传递给求解器时,我得到了。不知道为什么求解器不返回简单可行的解决s_i -> x_i -> t_i
方案i=0, 1
评论:
我正在使用
PARALLEL_CHEAPEST_INSERTION
它,因为它为我的目的提供了更好的解决方案,PATH_CHEAPEST_ARC
但梯子返回了正确的解决方案。经过一番折腾,似乎问题不在于开始位置的关闭时间窗口,而在于结束位置的持续时间。即,如果前两行是:
[ x0: [0, horizon, horizon, horizon, a, a], x1: [horizon, 0, horizon, horizon, b, b], ...
对于任何
b >= a
求解器都会找到正确的分配。如果我将车辆限制在正确的位置,即
index = manager.NodeToIndex(0) routing.VehicleVar(index).SetValues([0]) index = manager.NodeToIndex(1) routing.VehicleVar(index).SetValues([1])
我得到了正确的分配(这意味着原始模型确实可行)。
代码示例:
from ortools.constraint_solver import pywrapcp, routing_enums_pb2
def add_durations(manager, routing, data):
def duration_callback(from_index, to_index):
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return data["durations_matrix"][from_node][to_node]
transit_callback_index = routing.RegisterTransitCallback(duration_callback)
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
routing.AddDimension(
transit_callback_index,
data["horizon"], # allow waiting time
data["horizon"], # maximum time per vehicle
False, # Don't force start cumul to zero.
"Duration"
)
duration_dimension = routing.GetDimensionOrDie('Duration')
# Add time window constraints for each location except start/end locations
for location_idx in range(data["num_locations"]):
index = manager.NodeToIndex(location_idx)
duration_dimension.CumulVar(index).SetRange(data["time_windows"][location_idx][0], data["time_windows"][location_idx][1])
routing.AddToAssignment(duration_dimension.SlackVar(index))
# Add time window constraints for each vehicle start and end locations
for vehicle_id in range(data["num_vehicles"]):
st_index = routing.Start(vehicle_id)
end_index = routing.End(vehicle_id)
duration_dimension.CumulVar(st_index).SetRange(data["time_windows"][data["num_locations"] + vehicle_id][0],
data["time_windows"][data["num_locations"] + vehicle_id][1])
duration_dimension.CumulVar(end_index).SetRange(data["time_windows"][data["num_locations"] + data["num_vehicles"] + vehicle_id][0],
data["time_windows"][data["num_locations"] + data["num_vehicles"] + vehicle_id][1])
def print_solution(data, manager, routing, assignment):
"""Prints assignment on console."""
time_dimension = routing.GetDimensionOrDie('Duration')
total_time = 0
for vehicle_id in range(data['num_vehicles']):
index = routing.Start(vehicle_id)
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
while not routing.IsEnd(index):
time_var = time_dimension.CumulVar(index)
plan_output += '{0} Time({1},{2}) -> '.format(
manager.IndexToNode(index), assignment.Min(time_var),
assignment.Max(time_var))
index = assignment.Value(routing.NextVar(index))
time_var = time_dimension.CumulVar(index)
plan_output += '{0} Time({1},{2})\n'.format(manager.IndexToNode(index),
assignment.Min(time_var),
assignment.Max(time_var))
plan_output += 'Time of the route: {}min\n'.format(
assignment.Min(time_var))
print(plan_output)
total_time += assignment.Min(time_var)
print('Total time of all routes: {}min'.format(total_time))
def run_vrp(a, b):
"""Solve the VRP with time windows."""
data = {}
data["num_vehicles"] = 2
data["num_locations"] = 2
data["time_windows"] = [[5400, 5820], [9000, 9420], [5520, 39719], [6420, 43319], [5521, 39720], [6421, 43320]]
horizon = 86760
data["horizon"] = horizon
data["durations_matrix"] = [
[0, horizon, horizon, horizon, a, a],
[horizon, 0, horizon, horizon, b, b],
[0, 0, horizon, horizon, 0, horizon],
[0, 0, horizon, horizon, horizon, 0],
[horizon, horizon, horizon, horizon, horizon, horizon],
[horizon, horizon, horizon, horizon, horizon, horizon]
]
num_all_locations = len(data["durations_matrix"])
start_locations_indices = [2, 3]
end_locations_indices = [4, 5]
manager = pywrapcp.RoutingIndexManager(
data["num_locations"] + 2 * data["num_vehicles"], data["num_vehicles"], start_locations_indices, end_locations_indices
)
routing = pywrapcp.RoutingModel(manager)
add_durations(manager, routing, data)
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = routing_enums_pb2.FirstSolutionStrategy.PARALLEL_CHEAPEST_INSERTION
assignment = routing.SolveWithParameters(search_parameters)
if assignment:
print_solution(data, manager, routing, assignment)
else:
print("Assignment is None")
return assignment
if __name__ == '__main__':
# doesn't work
a = 5000
b = 4000
assert run_vrp(a, b) is None
# works, because b > a
a = 3000
assert run_vrp(a, b)