微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在谷歌或工具中用两种车辆类型解决时间受限的 CVRP

如何解决在谷歌或工具中用两种车辆类型解决时间受限的 CVRP

我正在建模受时间约束的 CVRP。问题是在车辆(交付)容量和总花费时间(每辆车)的限制下,最小化总旅行时间(不包括包裹投递时间)。丢包时间是指在每个节点额外花费的时间,花费的总时间等于行程时间加上这个额外的时间。我有以下模型适用于单个车辆类型的情况。我想在那里引入两辆车类型的概念,这意味着我有一组V1类型的车辆和另一组V2类型的车辆。车辆类型的唯一区别是每次旅行的成本。让 x 表示 V1 的每时间单位旅行成本,y 表示 V2 的每时间单位旅行成本。我如何设计模型以使其包含此附加方面?

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

def create_data_model(n_vehicles):
    """Stores the data for the problem."""
    data = {}
    data['time_matrix'] = TT #travel time
    data['num_vehicles'] = n_vehicles
    data['depot'] = 0
    data['demands'] = demands
    data['vehicle_capacities'] = vehicle_capacities
    data['service_time'] = service_time
    return data

def print_solution(data,manager,routing,solution):
    """Prints solution on console."""
    print(f'Objective: {solution.ObjectiveValue()}')
    max_route_time = 0; tour = {i:[] for i in range(data['num_vehicles'])}
    for vehicle_id in range(data['num_vehicles']):
        index = routing.Start(vehicle_id)
        plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
        route_time = 0
        while not routing.IsEnd(index):
            plan_output += ' {} -> '.format(manager.IndexToNode(index))
            tour[vehicle_id].append(manager.IndexToNode(index))
            prevIoUs_index = index
            index = solution.Value(routing.Nextvar(index))
            if prevIoUs_index != 0 and prevIoUs_index <= len(data['service_time'])-1:
                service_time = data['service_time'][prevIoUs_index]
            else:
                service_time = 0
            route_time += (routing.GetArcCostForVehicle(
                prevIoUs_index,index,vehicle_id) + service_time)
        plan_output += '{}\n'.format(manager.IndexToNode(index))
        tour[vehicle_id].append(manager.IndexToNode(index))
        plan_output += 'Travel time of the route: {} sec\n'.format(route_time)
        print(plan_output)
        max_route_time = max(route_time,max_route_time)
    print('Maximum of the route time: {} sec'.format(max_route_time))
    return(tour)

def main(n_vehicles):
    number_of_veh = [n_vehicles][0]
    solution_found = False
    while solution_found == False:
        """Entry point of the program."""
        # Instantiate the data problem.
        data = create_data_model(number_of_veh)

        # Create the routing index manager.
        manager = pywrapcp.RoutingIndexManager(len(data['time_matrix']),data['num_vehicles'],data['depot'])
        # Create Routing Model.
        routing = pywrapcp.RoutingModel(manager)

        # Create and register a transit callback.
        def time_callback(from_index,to_index):
            """Returns the 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 data['time_matrix'][from_node][to_node]

        transit_callback_index = routing.RegisterTransitCallback(time_callback)

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

        # Create and register a transit callback.
        def time_callback2(from_index,to_index):
            """Returns the 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)
            if from_node != 0:
                return data['time_matrix'][from_node][to_node] + data['service_time'][from_node]
            else:
                return data['time_matrix'][from_node][to_node]

        transit_callback_index2 = routing.RegisterTransitCallback(time_callback2)

        # Add Time constraint.
        dimension_name = 'Time'
        routing.AddDimension(
            transit_callback_index2,# no slack
            Operational_hours*3600,# vehicle maximum travel time
            True,# start cumul to zero
            dimension_name)
        time_dimension = routing.GetDimensionorDie(dimension_name)

        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)
        routing.AddDimensionWithVehicleCapacity(
            demand_callback_index,# null capacity slack
            data['vehicle_capacities'],# vehicle maximum capacities
            True,# start cumul to zero
            'Capacity')

        # 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.FromSeconds(VRP_time_limit)

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

        # Print solution on console.
        if solution:
            tour = print_solution(data,solution)
            solution_found = True
        else:
            print('No solution found! Increasing the vehicle numbers by one and resolving.\n')
            solution_found = False
            number_of_veh += 1
    
    return(tour,number_of_veh)

编辑

根据@Mizux 的回答,我写了以下内容,结果出现了以下错误

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

def create_data_model(n_vehicles):
    """Stores the data for the problem."""
    data = {}
    TT = np.array(df.values)
    data['time_matrix'] = TT
    data['num_vehicles'] = n_vehicles
    data['depot'] = 0
    data['demands'] = demands
    if len(vehicle_capacities) < n_vehicles:
        data['vehicle_capacities'] = [vehicle_capacities[0]]*n_vehicles
    else:
        data['vehicle_capacities'] = vehicle_capacities
    data['service_time'] = service_time
    return data

def print_solution(data,max_route_time)
    print('Maximum of the route time: {} sec'.format(max_route_time))
    return(tour)

def main(n_vehicles,cost1,cost2):
    number_of_veh = [n_vehicles][0]
    solution_found = False
    while solution_found == False:
        Num_of_Class6 = int(n_vehicles*Percent_of_Class6)
        Num_of_Hybrid = n_vehicles - Num_of_Class6
        V = list(range(n_vehicles))
        V2 = list(set(np.random.choice(V,size=Num_of_Class6,replace=False)))
        V1 = list(set(V)-set(V2))
        """Entry point of the program."""
        # Instantiate the data problem.
        data = create_data_model(number_of_veh)

        # Create the routing index manager.
        manager = pywrapcp.RoutingIndexManager(len(data['time_matrix']),data['depot'])
        # Create Routing Model.
        routing = pywrapcp.RoutingModel(manager)
        '''Major Diff Starts Here'''
        # Create and register a transit callback.
        def time_callback(from_index,to_index,cost):
            """Returns the 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 data['time_matrix'][from_node][to_node]*cost

        range_extender_callback = partial(time_callback,cost=cost1)
        class6_callback = partial(time_callback,cost=cost2)

        transit_callback_index_V1 = routing.RegisterTransitCallback(range_extender_callback)
        transit_callback_index_V2 = routing.RegisterTransitCallback(class6_callback)
        '''Major Diff Ends Here'''
        # Define cost of each arc.
        for v in V1:
            routing.SetArcCostEvaluatorOfVehicle(transit_callback_index_V1,v)
        for v in V2:
            routing.SetArcCostEvaluatorOfVehicle(transit_callback_index_V2,v)

        # Create and register a transit callback.
        def time_callback2(from_index,solution)
            solution_found = True
        else:
            print('No solution found! Increasing the vehicle numbers by one and resolving.\n')
            solution_found = False
            number_of_veh += 1

    return(tour,number_of_veh,V1,V2)

main(n_vehicles,cost2)

输出为:

Beginning the Googe OR-tools to solve the problem.
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-15-0447402a4e3d> in <module>
    166     return(tour,V2)
    167 
--> 168 final_tour,V2 = main(n_vehicles,cost2)

<ipython-input-15-0447402a4e3d> in main(n_vehicles,cost2)
    104             routing.SetArcCostEvaluatorOfVehicle(transit_callback_index_V1,v)
    105         for v in V2:
--> 106             routing.SetArcCostEvaluatorOfVehicle(transit_callback_index_V2,v)
    107 
    108         # Create and register a transit callback.

~/.local/lib/python3.7/site-packages/ortools/constraint_solver/pywrapcp.py in SetArcCostEvaluatorOfVehicle(self,evaluator_index,vehicle)
   5224     def SetArcCostEvaluatorOfVehicle(self,evaluator_index: "int",vehicle: "int") -> "void":
   5225         r""" Sets the cost function for a given vehicle route."""
-> 5226         return _pywrapcp.RoutingModel_SetArcCostEvaluatorOfVehicle(self,vehicle)
   5227 
   5228     def SetFixedcostOfAllVehicles(self,cost: "int64_t") -> "void":

TypeError: in method 'RoutingModel_SetArcCostEvaluatorOfVehicle',argument 3 of type 'int'

解决方法

为了解决这个问题,我跟着Mizux的回答。我有以下 MWE 供将来考虑。我希望它对某人有所帮助!

from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
import numpy as np
from functools import partial

n_vehicles = 4 #Number of vehicles
max_vehicle_tt = 3600 #Maximum travel time for each vehicle (excludes service times)

data = {}
data['time_matrix'] = np.array([[   0,1187,1200,1110,1134,892,1526,903,1482,1544],[1232,13,90,67,426,537,419,493,555],[1218,57,82,73,412,523,405,479,541],[1177,23,370,481,364,438,500],[1187,80,380,491,374,448,509],[ 870,390,403,314,337,729,17,686,747],[1539,557,543,485,495,733,726,53,68],[ 882,384,397,307,331,723,679,741],[1496,514,500,442,451,689,683,122],[1584,602,588,530,539,777,68,771,122,0]])
data['num_vehicles'] = n_vehicles
data['depot'] = 0
data['demands'] = [0,4,3,1,12,24,20]
data['vehicle_capacities'] = [30]*data['num_vehicles']
data['service_time'] = [0,18,27,25,11,92,6,239,143]

def print_solution(data,manager,routing,solution):
    """Prints solution on console."""
    print(f'Objective: {solution.ObjectiveValue()}')
    max_route_time = 0; tour = {i:[] for i in range(data['num_vehicles'])}
    for vehicle_id in range(data['num_vehicles']):
        index = routing.Start(vehicle_id)
        plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
        route_time = 0
        while not routing.IsEnd(index):
            plan_output += ' {} -> '.format(manager.IndexToNode(index))
            tour[vehicle_id].append(manager.IndexToNode(index))
            previous_index = index
            index = solution.Value(routing.NextVar(index))
            if previous_index != 0 and previous_index <= len(data['service_time'])-1:
                service_time = data['service_time'][previous_index]
            else:
                service_time = 0
            route_time += (routing.GetArcCostForVehicle(
                previous_index,index,vehicle_id) + service_time)
        plan_output += '{}\n'.format(manager.IndexToNode(index))
        tour[vehicle_id].append(manager.IndexToNode(index))
        plan_output += 'Travel time of the route: {} sec\n'.format(route_time)
        print(plan_output)
        max_route_time = max(route_time,max_route_time)
    print('Maximum of the route time: {} sec'.format(max_route_time))
    return(tour)

def main(n_vehicles,cost1,cost2):
    np.random.seed(0)
    Num_of_Class6 = int(n_vehicles*0.6)
    Num_of_Hybrid = n_vehicles - Num_of_Class6
    V = list(range(n_vehicles))
    V2 = list(set(np.random.choice(V,size=Num_of_Class6,replace=False)))
    V1 = list(set(V)-set(V2))
    print('V1:%s'%V1); print('V2:%s'%V2)
    
    # Create the routing index manager.
    manager = pywrapcp.RoutingIndexManager(len(data['time_matrix']),data['num_vehicles'],data['depot'])
    # Create Routing Model.
    routing = pywrapcp.RoutingModel(manager)
    
    # Create and register a transit callback.
    def time_callback(from_index,to_index,cost):
        """Returns the 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 data['time_matrix'][from_node][to_node]*cost

    range_extender_callback = partial(time_callback,cost=cost1)
    class6_callback = partial(time_callback,cost=cost2)

    transit_callback_index_V1 = routing.RegisterTransitCallback(range_extender_callback)
    transit_callback_index_V2 = routing.RegisterTransitCallback(class6_callback)

    # Define cost of each arc.
    for v in V1:
        routing.SetArcCostEvaluatorOfVehicle(transit_callback_index_V1,int(v))
    for v in V2:
        routing.SetArcCostEvaluatorOfVehicle(transit_callback_index_V2,int(v))

    # Create and register a transit callback to limit the total travel+service time
    def time_callback2(from_index,to_index):
        """Returns the 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)
        if from_node != 0:
            return data['time_matrix'][from_node][to_node] + data['service_time'][from_node]
        else:
            return data['time_matrix'][from_node][to_node]

    transit_callback_index2 = routing.RegisterTransitCallback(time_callback2)

    # Add Time constraint.
    dimension_name = 'Time'
    routing.AddDimensionWithVehicleCapacity(
        transit_callback_index2,# no slack
        [max_vehicle_tt]*data['num_vehicles'],# vehicle maximum travel time
        True,# start cumul to zero
        dimension_name)
    time_dimension = routing.GetDimensionOrDie(dimension_name)

    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)
    routing.AddDimensionWithVehicleCapacity(
        demand_callback_index,# null capacity slack
        data['vehicle_capacities'],# vehicle maximum capacities
        True,# start cumul to zero
        'Capacity')

    # 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.FromSeconds(1)

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

    # Print solution on console.
    if solution:
        tour = print_solution(data,solution)
        return(tour,V1,V2)
    else:
        print('No solution found!\n')


tour,V2 = main(n_vehicles,0.5,0.7)

奖励:使用以下功能检查关键解决方案指标。

pairs = {}; serv_time = {}; tt ={}; cont_to_obj = {}
for i,j in tour.items():
    if len(j) > 2:
        serv_time[i] = sum([data['service_time'][k] for k in j])
        print('Service time for vehicle %s: %s.'%(i,serv_time[i]))
        num_deliveries = sum([data['demands'][k] for k in j])
        print('Number of deliveries for vehicle %s: %s.'%(i,num_deliveries))
        pairs[i] = list(zip(j,j[1:]))
        tt[i] = sum([data['time_matrix'][k] for k in pairs[i]])
        print('Travel time for vehicle %s: %s.'%(i,tt))
        print('Total time for vehicle %s: %s'%(i,serv_time[i]+tt[i]))
        if i in V1:
            cont_to_obj[i] = sum([int(data['time_matrix'][k]*0.002244) for k in pairs[i]])
        else:
            cont_to_obj[i] = sum([int(data['time_matrix'][k]*0.0080517) for k in pairs[i]])
        print('Contribution to the obj. fun. for vehicle %s: %s\n'%(i,cont_to_obj[i]))
,

只需注册两个公交回调(即每个车辆类型一个)

然后使用 AddDimension() 的重载来传递已注册的公交回调索引数组。

例如Mizux/vrp_multiple_transit.py

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。