具有距离约束的 OR-tools CVRP 不起作用

OR-tools CVRP with a distance constraint not working

我正在使用 OR 工具代码来优化具有距离和容量限制的公交路线。这是我得到的输出:

Route for vehicle 0:
 Stop 0 Students(2) ->  Stop 3 Students(37) ->  Stop 5 Students(37)
Duration of the route: 48m
Number of students of the route: 37

Route for vehicle 1:
 Stop 6 Students(4) ->  Stop 4 Students(38) ->  Stop 9 Students(41) ->  Stop 19 Students(45) ->  Stop 5 Students(45)
Duration of the route: 55m
Number of students of the route: 45

Route for vehicle 2:
 Stop 7 Students(4) ->  Stop 1 Students(39) ->  Stop 8 Students(40) ->  Stop 18 Students(43) ->  Stop 5 Students(43)
Duration of the route: 56m
Number of students of the route: 43

Route for vehicle 3:
 Stop 11 Students(1) ->  Stop 10 Students(2) ->  Stop 20 Students(12) ->  Stop 23 Students(43) ->  Stop 5 Students(43)
Duration of the route: 54m
Number of students of the route: 43

Route for vehicle 4:
 Stop 12 Students(1) ->  Stop 2 Students(36) ->  Stop 22 Students(39) ->  Stop 21 Students(45) ->  Stop 5 Students(45)
Duration of the route: 58m
Number of students of the route: 45

Route for vehicle 5:
 Stop 13 Students(1) ->  Stop 14 Students(4) ->  Stop 5 Students(4)
Duration of the route: 60m
Number of students of the route: 4

Route for vehicle 6:
 Stop 16 Students(3) ->  Stop 15 Students(6) ->  Stop 17 Students(12) ->  Stop 5 Students(12)
Duration of the route: 57m
Number of students of the route: 12

Route for vehicle 7:
 Stop 24 Students(3) ->  Stop 5 Students(3)
Duration of the route: 0m
Number of students of the route: 3

Route for vehicle 8:
 Stop 25 Students(3) ->  Stop 5 Students(3)
Duration of the route: 0m
Number of students of the route: 3

Total duration of all routes: 388m
Total number of students of all routes: 235

车辆 0-6 的持续时间似乎是正确的,但车辆 7 和 8 的持续时间为 0。我多次检查矩阵,看来我的 matrix/data 不是问题所在。我认为 def print_solution 下的 while not 部分可能有问题,但我不确定如何更改它。因为只有两站,所以我认为不是 运行 while not 部分将每条路线的持续时间相加。

#@CVRP ()
"""Capacited Vehicles Routing Problem (CVRP)."""
!pip install ortools
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
from google.colab import drive
import pandas as pd
import numpy as np
import csv

reader = csv.reader(open("Duration.csv", "r"), delimiter=",")
x = list(reader)
result = np.array(x).astype("float")

def create_data_model():
    """Stores the data for the problem."""
    data = {}
    data['distance_matrix'] = result
    # stops: huayuan, aocheng1, aocheng2, aocheng3, aocheng4, school, yangguang, aocheng back, olympics village, diamond hill
    # wuyiyangguang, hopeland, haotian, !!shanhaitian!!, tianjiaoyuan, olympics tower, international building, tangchen, !!astor hotel!!,
    # jinlanque, hongzonglv, lanwan, !!wangzuo!!, !!haiyi back!!, haiyi, liusuyuan, !!muxuyuan!!, swan lake, bandao, b!!andao back!!, mingquan, kameier, !!fulijinmenhu!!
    #data['demands'] = [2, 35, 35, 35, 34, 0, 4, 4, 1, 3, 1, 1, 1, 0, 1, 3, 3, 3, 0, 6, 3, 4, 0, 0, 10, 6, 0, 3, 31, 0, 3, 3, 0]
    
    #UPDATED stop names:huayuan, aocheng1, aocheng2, aocheng3, aocheng4, school, yangguang, aocheng back, olympics village, diamond hill
    # wuyiyangguang, hopeland, haotian, tianjiaoyuan, olympics tower, international building, tangchen,
    # jinlanque, hongzonglv, lanwan, haiyi, liusuyuan, swan lake, bandao, mingquan, kameier
    data['demands'] = [2, 35, 35, 35, 34, 0, 4, 4, 1, 3, 1, 1, 1, 1, 3, 3, 3, 6, 3, 4, 10, 6, 3, 31, 3, 3]
    data['vehicle_capacities'] = [45, 45, 45, 45, 45, 45, 49, 15, 15] 
    data['num_vehicles'] = 9 
    data['starts'] = [0, 6, 7, 11, 12, 13, 16, 24, 25]
    data['ends'] = [5, 5, 5, 5, 5, 5, 5, 5, 5]
    return data

def print_solution(data, manager, routing, solution):
    """Prints solution on console."""
    total_distance = 0
    total_load = 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
        route_load = 0
        while not routing.IsEnd(index):
            node_index = manager.IndexToNode(index)
            route_load += data['demands'][node_index]
            plan_output += ' Stop {0} Students({1}) -> '.format(node_index, route_load)
            previous_index = index
            index = solution.Value(routing.NextVar(index))
            route_distance += routing.GetArcCostForVehicle(
                previous_index, index, vehicle_id)
        plan_output += ' Stop {0} Students({1})\n'.format(manager.IndexToNode(index),
                                                 route_load)
        plan_output += 'Duration of the route: {}m\n'.format(route_distance)
        plan_output += 'Number of students of the route: {}\n'.format(route_load)
        print(plan_output)
        total_distance += route_distance
        total_load += route_load
    print('Total duration of all routes: {}m'.format(total_distance))
    print('Total number of students of all routes: {}'.format(total_load))


def main():
    """Solve the CVRP problem."""
    # Instantiate the data problem.
    data = create_data_model()
    # Create the routing index manager.
    manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),
                                           data['num_vehicles'], data['starts'],
                                           data['ends'])
    # 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)
        return data['distance_matrix'][from_node][to_node]
    
    transit_callback_index = routing.RegisterTransitCallback(distance_callback)
  
    #Define cost of each arc.
    routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
    

    # Add Capacity constraint.
    def demand_callback(from_index):
        """Returns the demand of the node."""
        # Convert from routing variable Index to demands NodeIndex.
        from_node = manager.IndexToNode(from_index)
        return data['demands'][from_node]

    demand_callback_index = routing.RegisterUnaryTransitCallback(
        demand_callback)
    
    dimension_name = 'Capacity'
    routing.AddDimensionWithVehicleCapacity(
        demand_callback_index,
        0,  # null capacity slack
        data['vehicle_capacities'],  # vehicle maximum capacities
        True,  # start cumul to zero
        dimension_name)
    # Add Distance constraint.
    dimension_name = 'Distance'
    routing.AddDimension(
        transit_callback_index,
        0,  # no slack
        100,  # vehicle maximum travel distance
        True,  # start cumul to zero
        dimension_name)
    distance_dimension = routing.GetDimensionOrDie(dimension_name)
    distance_dimension.SetGlobalSpanCostCoefficient(100)

    # 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.log_search = True
    search_parameters.time_limit.FromSeconds(5)

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

    # Print solution.
    if solution:
        print_solution(data, manager, routing, solution)
    #else:
        #print('no solution')

if __name__ == '__main__':
    main()
    data = create_data_model()
    #print(len(data['distance_matrix']))
    #print(len(data['distance_matrix'][0]))

如果有人能帮助我,我将不胜感激!

默认情况下,空路由 Start -> Endarc 成本 为零。
因此,当使用 routing.GetArcCostForVehicle(previous_index, index, vehicle_id).

时,您的最后两条车辆路线为空,将 return 0

您可以使用 RoutingModel::ConsiderEmptyRouteCostsForVehicle(bool consider_costs, int vehicle) 启用费用。

恕我直言,这样的事情应该可行:

for v in range(manager.GetNumberOfVehicles()):
  routing.ConsiderEmptyRouteCostsForVehicle(true, v)

参考:https://github.com/google/or-tools/blob/fa84bc05e72641dddfbb98164d81b8bc9bef6ea5/ortools/constraint_solver/routing.h#L950-L953