带有距离约束的 OR 工具 CVRP 不起作用

问题描述

我正在使用 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。我多次检查矩阵,似乎我的矩阵/数据没有问题。我认为 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,34,4,1,3,6,10,31,0]
    
    #UPDATED stop names:huayuan,kameier
    data['demands'] = [2,3]
    data['vehicle_capacities'] = [45,45,49,15,15] 
    data['num_vehicles'] = 9 
    data['starts'] = [0,7,11,12,13,16,24,25]
    data['ends'] = [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,# 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,# 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,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 -> End弧成本为零。
因此,您的最后两条车辆路线为空,在使用 routing.GetArcCostForVehicle(previous_index,index,vehicle_id) 时将返回 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