我试图解决一个VRP问题,允许通过惩罚和多个仓库丢弃节点。
守则适用于罚款及车辆在同一车场起止的情况:
data['num_vehicles'] = 2
data['start'] = [0, 4]
data['end'] = [0, 4]
代码可以很好地处理在不同车库开始和结束的车辆,并将for循环注释到AddDisjunction以进行惩罚(不允许丢弃节点):
data['num_vehicles'] = 2
data['start'] = [0, 4]
data['end'] = [0, 9]
.........................
#for node in range(1, len(data['penalties'])):
# routing.AddDisjunction([manager.NodeToIndex(node)], data['penalties'][node])
但是,当车辆在不同的车库开始和结束,并试图增加惩罚,以允许丢弃节点,python与错误崩溃(调试,我可以看到,在添加不同终端仓库的惩罚失败):
F00-1:-1:-1.000244 24944 routing.cc:1622]检查失败: kUnassigned !=索引i
我找不到关于这个错误的任何参考资料。我查看了第1622行的routing.cc源代码,但是我看不到与错误有任何关系。我需要帮助来解决这个问题。
下面是源代码:
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
def create_data_model():
"""Stores the data for the problem."""
data = {}
data['distance_matrix'] = [
[0, 2253, 2479, 2792, 4707, 6128, 1567, 5643, 1234, 3345, 1827],
[1731, 0, 2193, 2507, 3624, 5040, 3467, 2921, 5791, 1546, 2345],
[1867, 2112, 0, 676, 4406, 5824, 988, 4567, 2134, 4453, 2123],
[2339, 2585, 893, 0, 4879, 6302, 1543, 1298, 6890, 1456, 5623],
[4464, 3766, 5935, 4957, 0, 1749, 987, 3212, 3451, 5212, 3321],
[6568, 5862, 8023, 7055, 2148, 0, 4567, 2124, 4321, 3212, 1234],
[731, 2193, 2507, 7624, 4040, 4467, 0, 2621, 3791, 1567, 1345],
[1731, 3193, 1507, 3624, 6040, 2467, 4921, 0, 5791, 6723, 1345],
[2731, 3193, 2507, 6204, 5040, 1467, 2210, 6791, 0, 2567, 6421],
[3345, 1543, 4421, 1531, 5213, 3215, 1512, 6213, 2431, 0, 5673],
[1832, 2421, 2144, 5232, 3214, 1234, 1432, 1231, 6321, 5461, 0],
]
data['node_time'] = [0, 7200, 3600, 5400, 0, 5400, 7200, 3600, 7200, 0, 300]
data['num_vehicles'] = 2
data['start'] = [0, 4]
data['end'] = [0, 9]
# Penalizaciones por no visitar nodos (drop nodes) en caso de que no tenga solución:
# MAXINT 0x7FFFFFFFFFFFFFF: Hace obligatoria la visita al nodo, no se puede eliminar de la solución
# 1000000: Se puede no visitar el nodo con penalización. La penalización debe ser grande, mayor que 10x veces el mayor tiempo de visita, para evitar que salga mas rentable dejar caer el nodo que pagar la penalización
# 0: En los nodos "depósito" de vehículos, no son visitas intermedias sino inicio y fin de la ruta.
data['penalties'] = [0, 1000000, 1000000, 1000000, 0, 0x7FFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFF, 1000000, 1000000, 0, 1000000]
return data
def print_solution(data, manager, routing, solution):
"""Prints solution on console."""
print(f'Objective: {solution.ObjectiveValue()}')
# Display dropped nodes.
dropped_nodes = 'Nodos sin visitar:'
for node in range(routing.Size()):
if routing.IsStart(node) or routing.IsEnd(node):
continue
if solution.Value(routing.NextVar(node)) == node:
dropped_nodes += ' {}'.format(manager.IndexToNode(node))
print(dropped_nodes + '\n')
max_route_distance = 0
for vehicle_id in range(data['num_vehicles']):
index = routing.Start(vehicle_id)
plan_output = 'Ruta para vehículo {}:\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 += 'Tiempo de la ruta: {}sg\n'.format(route_distance)
print(plan_output)
max_route_distance = max(route_distance, max_route_distance)
print('Maximo tiempo de las rutas: {}sg'.format(max_route_distance))
def main():
"""Entry point of the program."""
# Instantiate the data problem.
data = create_data_model()
# Create the routing index manager.
manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),
data['num_vehicles'], data['start'], data['end'])
# 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)
tiempo_desplazamiento = data['distance_matrix'][from_node][to_node]
tiempo_ejecucion = data['node_time'][to_node]
return tiempo_desplazamiento + tiempo_ejecucion
transit_callback_index = routing.RegisterTransitCallback(distance_callback)
# Define cost of each arc.
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
# Add Distance constraint.
routing.AddDimension(
transit_callback_index,
0, # no slack
27000, # vehicle maximum travel distance (7.5 hours, in seconds)
True, # start cumul to zero
'Time')
distance_dimension = routing.GetDimensionOrDie('Time')
distance_dimension.SetGlobalSpanCostCoefficient(100)
# Allow to drop nodes.
for node in range(1, len(data['penalties'])):
routing.AddDisjunction([manager.NodeToIndex(node)], data['penalties'][node])
# 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.time_limit.seconds = 30
# Solve the problem.
solution = routing.SolveWithParameters(search_parameters)
# Print solution on console.
if solution:
print_solution(data, manager, routing, solution)
else:
print('No solution found!')
if __name__ == '__main__':
main()
我在Ortools Google上发布了这个问题,还有更多的研究: https://groups.google.com/g/or-tools-discuss/c/s3PfgLVZpj0
正如在那篇文章中所解释的,代码似乎在将开始和结束节点排除在分离之外,但是我想要更多的信息来理解它是如何工作的。
发布于 2022-02-15 10:14:46
在routing.h中有一个关于AddDisjunction的评论
/// Adds a disjunction constraint on the indices: exactly 'max_cardinality' of
/// the indices are active. Start and end indices of any vehicle cannot be
/// part of a disjunction.
在for循环中,要将节点添加到分离,不包括开始节点和结束节点似乎是有效的:
# Allow to drop nodes.
for node in range(0, len(data['distance_matrix'])):
if(node in data['start'] or node in data['end']):
continue
else:
routing.AddDisjunction([manager.NodeToIndex(node)], data['penalties'][node])
发布于 2022-02-07 10:59:34
对于定制的开始和结束,您应该使用Routing.Start(vehicle_index)
和Routing.End(vehicle_index)
来获取这些节点的索引。
https://stackoverflow.com/questions/71017337
复制相似问题