如何在Google OR-Tools中为路线指定终点位置?

问题描述

我有一个有效的“车辆路径问题”解决方案,该解决方案是使用Google的“或”工具在Python中实现的。我有一个包含16个位置的时间矩阵,每个位置的时间窗口以及丢弃每个位置的惩罚。所有值均以为单位。我特意只用一辆车解决这个问题(本质上解决了旅行商问题)。我允许车辆在需要的任何时间等待。我已将某些地点的掉落罚款设定为极高的水平,因为我不希望他们被丢弃。

时间矩阵中表示的每个位置都会有一个时间窗口,该窗口指示自一天开始以来的时间(28800相当于上午8:00,64800相当于下午6:00,依此类推。 )我将上限最大值设置为64800,因为我希望在6:00 pm之前完成车辆。

我已将矩阵中的第一个位置指定为开始位置。
现在,我希望矩阵中的第二个位置是结束位置。

为此,我引用了OR-Tools documentation,但没有成功。我还发现a similar question发表在Stack Overflow上,但没有发现任何特别有用的回复。

下面是我的源代码-它成功运行,但确实使用矩阵中的第二个位置作为它创建的解决方案的结束位置。

来源

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

Matrix = [
  [0,550,743,790,606,810,684,1500,101,1001,722,533,574,1126,713],[0,[530,530,609,953,620,676,1544,598,875,443,489,737,1114,914],[622,622,475,962,570,453,144,1695,585,1073,641,738,745,966,922],[760,760,1014,1147,1018,1208,1088,1029,780,1375,1180,976,1293,450],[629,629,621,716,989,715,706,1654,516,1219,787,734,728,1247,650],[867,867,720,567,1109,653,392,1330,830,1318,886,983,990,731,1092],[671,671,524,213,1011,619,395,1724,634,1122,690,788,795,971],[1372,1372,1549,1758,1030,1593,1358,1731,1392,1987,1714,1588,1221,1443,1101],[132,132,615,764,688,545,704,1398,547,472,612],[930,930,960,1198,1209,1265,1138,2028,1031,526,866,1078,1703,1272],[759,759,549,786,1130,797,853,727,1721,718,556,579,914,1291,1091],[437,437,541,836,887,902,776,1597,538,877,576,1340,811],[621,880,1013,486,926,1079,1196,640,1005,1046,837,1259,441],[1608,1608,1517,1547,1562,1480,1610,1484,2115,1683,1794,1556,1436],[515,515,769,373,773,969,842,534,935,364,1140,0]
]

Windows = [[28800,28800],[64800,64800],[43200,43200],[50400,50400],[21600,79200],79200]]

Durations = [0,1800,3600,7200,1800]

Penalties = [100000,100000,576460752303423487,100000]

# 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 transit_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] + Durations[from_node]

transit_callback_index = routing.RegisterTransitCallback(transit_callback)

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

# Add Time Windows constraint.
routing.AddDimension(
    transit_callback_index,64800,# An upper bound for slack (the wait times at the locations).
    64800,# 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')

# Allow to drop nodes.
for node in range(1,len(Matrix)):
  routing.AddDisjunction([manager.NodeToIndex(node)],Penalties[node])

# 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)

result = {
  'Dropped': None,'Schedule': None
}

# Display dropped nodes.
dropped = []
for node in range(routing.Size()):
  if routing.IsStart(node) or routing.IsEnd(node):
    continue
  if solution.Value(routing.NextVar(node)) == node:
    dropped.append(manager.IndexToNode(node))
result['Dropped'] = dropped

# Return the solution the problem.
schedule = []
time = 0
index = routing.Start(0)
while not routing.IsEnd(index):
  time = time_dimension.CumulVar(index)
  schedule.append([manager.IndexToNode(index),solution.Min(time),solution.Max(time)])
  index = solution.Value(routing.NextVar(index))
time = time_dimension.CumulVar(index)
schedule.append([manager.IndexToNode(index),solution.Max(time)])
result['Schedule'] = schedule
print(result)

输出

{'Dropped': [4,5],'Schedule': [[0,28800,[9,28901,29249],[13,31173,31521],[15,33414,33762],[8,36292,36640],[14,39535,39883],[2,43200,[6,45676,46195],[7,47868,48387],[3,50400,[11,54641,57541],[10,56997,59897],[12,59663,62563],[1,64800]]}

我的理解是,主要的更改需要使用RoutingIndexManager进行。
就像Google OR-Tools文档似乎表明的那样,我尝试了以下更改:

manager = pywrapcp.RoutingIndexManager(len(Matrix),0)

manager = pywrapcp.RoutingIndexManager(len(Matrix),[0],[1])

但这会导致错误:

WARNING: Logging before InitGoogleLogging() is written to STDERR
F0820 15:13:16.748222 62401984 routing.cc:1433] Check failed: kUnassigned != indices[i] (-1 vs. -1) 
*** Check failure stack trace: ***

我使用OR工具时是否存在任何明显的错误?我很高兴回答任何问题。任何帮助将不胜感激!谢谢!

解决方法

我在OR-Tools Google Groups forums中收到了有关此问题的答案。

首先,正确更改管理器构造函数代码。

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

第二,您不能在分离中指定起点或终点:

    # Allow to drop nodes.
[-] for node in range(1,len(Matrix)):
[+] for node in range(2,len(Matrix)):
      routing.AddDisjunction([manager.NodeToIndex(node)],Penalties[node])

第三,您不能在开始或结束节点上使用NodeToIndex(node_index),因为它并不总是双射的。

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

最后,请确保在结束位置上设置一个时间窗口:

    index = routing.Start(0)
    time_dimension.CumulVar(index).SetRange(Windows[0][0],Windows[0][1])
[+] index = routing.End(0)
[+] time_dimension.CumulVar(index).SetRange(Windows[1][0],Windows[1][1])

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...