OR-Tools CVRPTW CP Solver fail: Can't set lower max vehicle distance than the largest time window

39 Views Asked by At

My problem is the following:

I have 24 vehicles and more than a hundred locations. The largest time window in my code is (1410, 1470) in minutes. The idea is that the zero point is 7 pm because there are locations with time window during the night starting from 7 pm and the following day is covered with different time windows. So the model works from Day1 7 pm to Day2 12 pm. BUT one vehicle can only travel 9 hours during this period. I tried to set max vehicle time to 60 * 9 = 540 but I get CP solver fail. Is there a way to implement this?

from ortools.constraint_solver import pywrapcp
from ortools.constraint_solver import routing_enums_pb2
import pandas as pd
import warnings
import os
os.chdir("M:\PTibor\Github\CVRPTW")
import data_process
warnings.filterwarnings("ignore")

#CONSTANTS AND PROCESSING
DATE = "2023-08-01"
RELOAD_STATIONS = 20
VEHICLE_MAX_DISTANCE_METERS = 22200000000
VEHICLE_MAX_TIME_MINUTES = 540
RUN_TIME_SECONDS = 60*5


data = data_process.process_data(DATE, RELOAD_STATIONS, VEHICLE_MAX_DISTANCE_METERS, VEHICLE_MAX_TIME_MINUTES)


def distance_callback(from_index, to_index):
    from_node = manager.IndexToNode(from_index)
    to_node = manager.IndexToNode(to_index)
    return data["distance_matrix"][from_node][to_node]

def time_callback(from_index, to_index):
    from_node = manager.IndexToNode(from_index)
    to_node = manager.IndexToNode(to_index)
    service_time = 0
    if from_node != data['depot']:
        service_time = 30
    return data["time_matrix"][from_node][to_node] + service_time

def demand_callback(from_index):
    from_node = manager.IndexToNode(from_index)
    return data["demands"][from_node]

def print_solution(data, manager, routing, solution):

    time_dimension = routing.GetDimensionOrDie('Time')
    total_time = 0
    
    res2=[]
    
    dropped = []
    dropped_reload = []
    for order in range(data['reload']+1, routing.nodes()):
        index = manager.NodeToIndex(order)
        if solution.Value(routing.NextVar(index)) == index:
            dropped.append(order)
    print(f'dropped orders: {dropped}')
    for reload in range(1, data['reload']+1):
        index = manager.NodeToIndex(reload)
        if solution.Value(routing.NextVar(index)) == index:
            dropped_reload.append(reload)
    print(f'dropped reload stations: {dropped_reload}')
    
    for vehicle_id in range(data['num_vehicles']):
        vehicle=data['vehicles'][vehicle_id]
        index = routing.Start(vehicle_id)
        plan_output = 'Route for vehicle {}:\n\n'.format(vehicle_id)
        route_load = 0
        distance = 0
        total_distance = 0
        time=0
        total_time=0
        previous_node_index=0
        while not routing.IsEnd(index):
            node_index = manager.IndexToNode(index)
            time_var = time_dimension.CumulVar(index)
            if node_index>data['reload']:
                route_load += max(data['demands'][node_index],0)
            else:
                route_load = 0
            
            if node_index==0:
                distance = 0
                time=0
            else:
                #distance = routing.GetArcCostForVehicle(previous_index, index, vehicle_id)
                distance = data['distance_matrix'][previous_node_index][node_index]
                time = data['time_matrix'][previous_node_index][node_index]
                
            total_distance+=distance
            total_time+=time
            
           
            plan_output += 'Place {0:>2} Arrive at {2:>2}min Depart at {3:>2}min (Load {1:>2})\n'.format(manager.IndexToNode(index), route_load, solution.Min(time_var), solution.Max(time_var))
            

            res2.append([vehicle_id, vehicle, manager.IndexToNode(index), data['shops'][manager.IndexToNode(index)], data['locations'][manager.IndexToNode(index)][0], data['locations'][manager.IndexToNode(index)][1],
                         data['demands'][node_index], route_load, distance, total_distance, time, total_time, solution.Min(time_var), solution.Max(time_var)])
            
            index = solution.Value(routing.NextVar(index))
            previous_node_index=node_index
    
            
        time_var = time_dimension.CumulVar(index)
        #total_time += solution.Min(time_var)
        node_index = manager.IndexToNode(index)
        
        distance = data['distance_matrix'][previous_node_index][node_index]
        total_distance+=distance
        time=data['time_matrix'][previous_node_index][node_index]
        total_time+=time
        
        res2.append([vehicle_id, vehicle, manager.IndexToNode(index), data['shops'][manager.IndexToNode(index)], data['locations'][manager.IndexToNode(index)][0], data['locations'][manager.IndexToNode(index)][1],
                     data['demands'][node_index], route_load, distance, total_distance, time, total_time, solution.Min(time_var), solution.Max(time_var)])
        
    return res2



manager = pywrapcp.RoutingIndexManager(
    len(data["time_matrix"]), data["num_vehicles"], data["depot"]
)

routing = pywrapcp.RoutingModel(manager)

distance_callback_index = routing.RegisterTransitCallback(distance_callback)
routing.AddDimension(
    distance_callback_index,
    0,  # no slack
    data["vehicle_max_distance"],  # vehicle maximum travel distance
    True,  # start cumul to zero
    "Distance",
)   



demand_callback_index = routing.RegisterUnaryTransitCallback(demand_callback)
routing.AddDimensionWithVehicleCapacity(
demand_callback_index,
1000,  # null capacity slack
data["vehicle_capacities"],  # vehicle maximum capacities
True,  # start cumul to zero
"Capacity",
)   
capacity_dimension = routing.GetDimensionOrDie("Capacity")

for node_index in range(1, RELOAD_STATIONS):
        index = manager.NodeToIndex(node_index)
        routing.AddDisjunction([node_index], 0)
        capacity_dimension.SlackVar(index).SetRange(0, 1000)



for node in range(RELOAD_STATIONS, len(data['demands'])):
        node_index = manager.NodeToIndex(node)
        capacity_dimension.SlackVar(node_index).SetValue(0)
        routing.AddDisjunction([node_index], 100000000000000)


time_callback_index = routing.RegisterTransitCallback(time_callback)
routing.SetArcCostEvaluatorOfAllVehicles(distance_callback_index)

time = "Time"
routing.AddDimension(
    time_callback_index,
    30,  # allow waiting time
    data['vehicle_max_time'],  # maximum time per vehicle
    False,  # Don't force start cumul to zero.
    time,
)

time_dimension = routing.GetDimensionOrDie(time)
time_dimension.SetGlobalSpanCostCoefficient(1000)

for i in range(data["num_vehicles"]):
    routing.AddVariableMinimizedByFinalizer(
        time_dimension.CumulVar(routing.Start(i))
    )
    routing.AddVariableMinimizedByFinalizer(time_dimension.CumulVar(routing.End(i)))




for location_idx, time_window in enumerate(data["time_windows"]):
    if location_idx == data["depot"]:
        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.
depot_idx = data["depot"]
for vehicle_id in range(data["num_vehicles"]):
    index = routing.Start(vehicle_id)
    time_dimension.CumulVar(index).SetRange(
        data["time_windows"][depot_idx][0], data["time_windows"][depot_idx][1]
    )
    
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 = RUN_TIME_SECONDS


solution = routing.SolveWithParameters(search_parameters)
if solution:
    res = print_solution(data, manager, routing, solution)
    res2=pd.DataFrame(res, columns=['Vehicle_id','Vehicle','Place_id','Shop','lat','lon','Load','TotalLoad', 'Distance', 'TotalDistance', 'Time', 'TotalTime','Arrive','Departure'])
else: 
    print("No solution!")
0

There are 0 best solutions below