def print_solution( data: dict, routing: RoutingModel, manager: RoutingIndexManager, assignment: Assignment, ) -> None: """ Print the solution. """ capacity_dimension = routing.GetDimensionOrDie('Capacity') time_dimension = routing.GetDimensionOrDie('Time') total_time = 0 for vehicle_id in range(data['num_vehicles']): index = routing.Start(vehicle_id) node_props = [] while not routing.IsEnd(index): props = node_properties(manager, assignment, capacity_dimension, time_dimension, index) node_props.append(props) index = assignment.Value(routing.NextVar(index)) props = node_properties(manager, assignment, capacity_dimension, time_dimension, index) node_props.append(props) route_time = assignment.Value(time_dimension.CumulVar(index)) route = "\n -> ".join(['[Node %2s: Load(%s) Time(%2s, %s)]' % prop for prop in node_props]) plan_output = (f'Route for vehicle {vehicle_id}:\n {route}\n' f'Load of the route: {props[1]}\nTime of the route: {route_time} min\n') print(plan_output) total_time += route_time print(f'Total time of all routes: {total_time} min')
def main(): numLocations = 8 depotIndex = 0 numVehicles = 3 vehicleCapacity = 3 times = [[0, 957.7, 748.5, 570.8, 706.7, 574.2, 893.2, 187], [975.1, 0, 436.6, 894.6, 293, 446.4, 760.2, 901.2], [814.6, 399.6, 0, 458, 352.5, 231.5, 876.8, 675.8], [526.8, 841.3, 441.7, 0, 785.8, 598.5, 1266.5, 457.8], [733.3, 328.4, 351.6, 748.7, 0, 231.8, 625, 659.4], [713.7, 360.9, 174.3, 577.1, 270.3, 0, 845.2, 639.8], [917.6, 919.7, 1000.8, 1357.9, 733.1, 931, 0, 996.4], [246.7, 901.6, 626.4, 383.8, 673.9, 540.7, 986.4, 0]] def timeCallback(s, t): return times[s][t] #demands = [0, 0, 1, 1, 3, 0, 0, 1] demands = [0, 1, 1, 1, 1, 1, 1, 1] def demandsCallback(s, _): return demands[s] pickupDeliveries = [ (6, 4), (5, 4) ] #[ ( 6, 4 ), ( 5, 4 ), ( 5, 3 ), ( 1, 2 ), ( 5, 2 ), ( 6, 4 ), ( 5, 7 ) ]; model = RoutingModel(numLocations, numVehicles, depotIndex) model.SetArcCostEvaluatorOfAllVehicles(timeCallback) model.AddDimension(timeCallback, 28800, 28800, True, 'Time') timeDimension = model.GetDimensionOrDie('Time') for i in range(numLocations): timeDimension.CumulVar(i).SetRange(0, 28800) model.AddDimension(demandsCallback, 0, vehicleCapacity, True, 'Capacity') capacityDimension = model.GetDimensionOrDie('Capacity') solver = model.solver() for pickup, delivery in pickupDeliveries: pickupIndex = model.NodeToIndex(pickup) deliveryIndex = model.NodeToIndex(delivery) solver.AddConstraint( model.VehicleVar(pickupIndex) == model.VehicleVar(deliveryIndex)) solver.AddConstraint( timeDimension.CumulVar(pickupIndex) <= timeDimension.CumulVar( deliveryIndex)) model.AddPickupAndDelivery(pickup, delivery) assignment = model.Solve() if not assignment: sys.exit('Error: No Assignment') printAssignment(assignment, model)
def print_solution( data: dict, routing: RoutingModel, manager: RoutingIndexManager, assignment: Assignment, ) -> None: capacity_dimension = routing.GetDimensionOrDie('Capacity') time_dimension = None total_cost = 0 if 'time_windows' in data.keys(): time_dimension = routing.GetDimensionOrDie('Time') total_time = 0 for vehicle_id in range(data['num_vehicles']): index = routing.Start(vehicle_id) node_props = [] while not routing.IsEnd(index): props = node_properties(data, manager, assignment, capacity_dimension, time_dimension, index) node_props.append(props) index = assignment.Value(routing.NextVar(index)) props = node_properties(data, manager, assignment, capacity_dimension, time_dimension, index) node_props.append(props) # arc weight cost = 0 node_indexes = [prop[0] for prop in node_props] for i in range(len(node_indexes)-1): idx_from = node_indexes[i] idx_to = node_indexes[i+1] cost += data['dist_mat'][idx_from][idx_to] total_cost += cost if 'time_windows' in data.keys(): route_time = assignment.Value(time_dimension.CumulVar(index)) route = "\n -> ".join(['[Node %2s(%s)TW(%2s, %2s): Vehicle Load(%2s) and Arrived(%2s)]' % prop for prop in node_props]) plan_output = (f'Route for vehicle {vehicle_id}:\n {route}\n' f'Cost: {cost}\nLoad: {props[-1]}\nTime: {route_time} min\n') print(plan_output) total_time += route_time else: route = "\n -> ".join(['[Node %2s(%s): Vehicle Load(%2s)]' % prop for prop in node_props]) plan_output = (f'Route for vehicle {vehicle_id}:\n {route}\n' f'Cost: {cost}\nLoad: {props[-1]}\n') print(plan_output) print(f'Total cost of all routes: {total_cost}\n') return total_cost if 'time_windows' in data.keys(): print('*** format ***: \n[Node node_index(demand)TW(Time Window min, max): Vehicle Load(accumulated load) and Arrived(time)]') print(f'Total time of all routes: {total_time} min')
def print_solution(data: dict, routing: pywrapcp.RoutingModel, assignment: pywrapcp.Assignment): """ Print routes on console. """ capacity_dimension = routing.GetDimensionOrDie('Capacity') time_dimension = routing.GetDimensionOrDie('Time') total_time = 0 d = {} r = [] for vehicle_id in range(data['num_vehicles']): index = routing.Start(vehicle_id) node_props = [] while not routing.IsEnd(index): props = node_properties(routing, assignment, capacity_dimension, time_dimension, index) node_props.append(props) index = assignment.Value(routing.NextVar(index)) props = node_properties(routing, assignment, capacity_dimension, time_dimension, index) node_props.append(props) route_time = assignment.Value(time_dimension.CumulVar(index)) route = "\n -> ".join(['[Node %2s: Load(%s) Time(%2s, %s)]' % prop \ for prop in node_props]) plan_output = f'Route for vehicle {vehicle_id}:\n {route}\n' + \ f'Load of the route: {props[1]}\nTime of the route: {route_time} min\n' # print(plan_output) #convert to dict ddd = [] for node in node_props: dd = {"node": node[0], "load": node[1], "time": [node[2], node[3]]} ddd.append(dd) r.append({ "vehicle_id": vehicle_id, "route": ddd, "total_time": route_time, "load": len(ddd) - 2 }) total_time += route_time d['solver'] = r d['total_time'] = total_time # print(f'Total time of all routes: {total_time} min') return d
def print_solution(data: VehicleRoutingProblemInstance, manager: RoutingIndexManager, routing: RoutingModel, solution: Assignment): """Prints assignment on console.""" time_dimension = routing.GetDimensionOrDie('Duration') total_time = 0 for vehicle_id in range(data.num_plans_to_create): start_time = None index = routing.Start(vehicle_id) plan_output = 'Route for vehicle {}:\n'.format(vehicle_id) while not routing.IsEnd(index): time_var = time_dimension.CumulVar(index) if start_time is None: start_time = solution.Min(time_var) plan_output += '{0} Time({1},{2}) -> '.format( manager.IndexToNode(index), solution.Min(time_var), solution.Max(time_var)) index = solution.Value(routing.NextVar(index)) time_var = time_dimension.CumulVar(index) plan_output += '{0} Time({1},{2})\n'.format( manager.IndexToNode(index), solution.Min(time_var), solution.Max(time_var)) plan_output += f'Start of the route: {start_time}\n' plan_output += 'Time of the route: {} sec\n'.format( solution.Min(time_var) - start_time) print(plan_output) total_time += solution.Min(time_var) - start_time print('Total time of all routes: {} sec'.format(total_time))
def _set_pickup_delivery_constraints(self, data: VehicleRoutingProblemInstance, routing: RoutingModel, manager: RoutingIndexManager): time_dimension = routing.GetDimensionOrDie( self.DURATION_DIMENSION_NAME) # ========= PICKUP/DELIVERY CONSTRAIN ========= # Define Transportation Requests. solver: Solver = routing.solver() for pickup_node, delivery_node in data.deliveries_not_started: pickup_index = manager.NodeToIndex(pickup_node) delivery_index = manager.NodeToIndex(delivery_node) routing.AddPickupAndDelivery(pickup_index, delivery_index) solver.Add( routing.VehicleVar(pickup_index) == routing.VehicleVar( delivery_index)) solver.Add( time_dimension.CumulVar(pickup_index) <= time_dimension.CumulVar(delivery_index)) # Define constraint of deliveries in progress - only vehicle that picked the order is able to finish it for courier_idx, node in data.deliveries_in_progress: index = manager.NodeToIndex(node) routing.SetAllowedVehiclesForIndex([courier_idx], index)
def _set_capacity_dimension_constraints( self, data: VehicleRoutingProblemInstance, routing: RoutingModel, manager: RoutingIndexManager): if data.courier_capacities is None: return def demand_callback(index): node = manager.IndexToNode(index) return data.node_demands[node] capacity_callback = routing.RegisterUnaryTransitCallback( demand_callback) routing.AddDimensionWithVehicleCapacity( capacity_callback, 0, # waiting time data.courier_capacities, # maximum distance per vehicle False, # Force start cumul to zero. self.CAPACITY_DIMENSION_NAME) capacity_dimension = routing.GetDimensionOrDie( self.CAPACITY_DIMENSION_NAME) for courier_idx, start_utilization in enumerate( data.start_utilizations): node_idx = routing.Start(courier_idx) self._set_constraint_on_var( node_idx, TimeWindowConstraint(node=node_idx, is_hard=True, from_time=start_utilization, to_time=start_utilization), capacity_dimension)
def apply(self, manager: pywrapcp.RoutingModel, routing: pywrapcp.RoutingModel, data_model: DataModel): 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_model.distance_matrix[from_node][to_node] transit_callback_index = routing.RegisterTransitCallback(distance_callback) routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index) dimension_name = 'Distance' routing.AddDimension( transit_callback_index, 0, # no slack 3000, # vehicle maximum travel distance True, # start cumul to zero dimension_name) distance_dimension = routing.GetDimensionOrDie(dimension_name) distance_dimension.SetGlobalSpanCostCoefficient(100) for i in range(data_model.number_of_engineers): routing.AddVariableMinimizedByFinalizer( distance_dimension.CumulVar(routing.Start(i))) routing.AddVariableMinimizedByFinalizer( distance_dimension.CumulVar(routing.End(i))) return routing
def _set_balance_constraints_on_distance_driven( self, data: VehicleRoutingProblemInstance, routing: RoutingModel, deliveries: List[Delivery]): # ========= BALANCE DISTRIBUTION CONSTRAIN ========= # https://activimetrics.com/blog/ortools/counting_dimension/ # A “fair” distribution of loads average_load = int( 2 * len(deliveries) // data.num_plans_to_create) # each delivery has 2 locations count_dimension = routing.GetDimensionOrDie( self.DISTANCE_DIMENSION_NAME) for veh in range(0, data.num_plans_to_create): index_end = routing.End(veh) # https://github.com/google/or-tools/blob/v8.0/ortools/constraint_solver/routing.h#L2460 # 5 min penalty for every order over average load count_dimension.SetCumulVarSoftLowerBound(index_end, average_load, 5 * 60)
def _extract_solution(self, assignment: Assignment, vrp_instance: VehicleRoutingProblemInstance, manager: RoutingIndexManager, routing: RoutingModel) \ -> VehicleRoutingProblemSolution: routes = [] etas = [] etds = [] time_dimension = routing.GetDimensionOrDie( self.DURATION_DIMENSION_NAME) for vehicle_id in range(vrp_instance.num_plans_to_create): index = routing.Start(vehicle_id) route = [] route_etas = [] route_etds = [] while not routing.IsEnd(index): node = manager.IndexToNode(index) if node in vrp_instance.pickup_nodes: this_event_time = vrp_instance.pickup_service_time elif node in vrp_instance.drop_nodes: this_event_time = vrp_instance.drop_service_time else: this_event_time = 0 time_var = time_dimension.CumulVar(index) eta = assignment.Min(time_var) etd = assignment.Max(time_var) + this_event_time route.append(node) route_etas.append(eta) route_etds.append(etd) index = assignment.Value(routing.NextVar(index)) routes.append(route) etas.append(route_etas) etds.append(route_etds) ret = VehicleRoutingProblemSolution(plans=routes, etas=etas, etds=etds) return ret
def add_time_window_constraints(routing: pywrapcp.RoutingModel, data: dict, time_callback): """ Add time window constraints. """ time = 'Time' horizon = 120 routing.AddDimension( evaluator=time_callback, slack_max=horizon, # allow waiting time capacity=horizon, # maximum time per vehicle # Don't force start cumul to zero. This doesn't have any effect in this example, # since the depot has a start window of (0, 0). fix_start_cumul_to_zero=False, name=time, ) time_dimension = routing.GetDimensionOrDie(time) for loc_node, (open_time, close_time) in enumerate(data['time_windows']): index = routing.NodeToIndex(loc_node) time_dimension.CumulVar(index).SetRange(open_time, close_time)
def _set_balance_constraints_on_number_of_packages( self, data: VehicleRoutingProblemInstance, routing: RoutingModel): # ========= BALANCE DISTRIBUTION CONSTRAIN ========= routing.AddConstantDimension( 1, # increment by one every time 100000, # max value forces equivalent # of jobs True, # set count to zero self.COUNT_DIMENSION_NAME) average_load = int( 2 * len(data.drop_nodes) // data.num_plans_to_create) # each delivery has 2 locations count_dimension = routing.GetDimensionOrDie(self.COUNT_DIMENSION_NAME) for veh in range(0, data.num_plans_to_create): index_end = routing.End(veh) # https://github.com/google/or-tools/blob/v8.0/ortools/constraint_solver/routing.h#L2460 # 5 min penalty for every order over average load count_dimension.SetCumulVarSoftUpperBound(index_end, average_load + 6, 5 * 60)
def add_time_window_constraints( routing: RoutingModel, manager: RoutingIndexManager, data: dict, time_callback_index: int, ) -> None: max_ = 120 routing.AddDimension( time_callback_index, slack_max= max_, # An upper bound for slack (the wait times at the locations) capacity= max_, # An upper bound for the total time over each vehicle's route # Don't force start cumul to zero. This doesn't have any effect in this example, # since the depot has a start window of (0, 0). fix_start_cumul_to_zero=False, name='Time', ) time_dimension = routing.GetDimensionOrDie('Time') for loc_idx, (open_time, close_time) in enumerate(data['time_windows']): index = manager.NodeToIndex(loc_idx) time_dimension.CumulVar(index).SetRange(open_time, close_time)
def _set_time_dimension_constraints( self, routing: RoutingModel, manager: RoutingIndexManager, dimension_name: str, start_time_windows: List[TimeWindowConstraint], node_time_windows: List[TimeWindowConstraint], num_plans_to_create: int): # ========= TIME WINDOW ========= time_dimension = routing.GetDimensionOrDie(dimension_name) for constraint in start_time_windows: index = routing.Start(constraint.node) self._set_constraint_on_var(index, constraint, time_dimension) for constraint in node_time_windows: index = manager.NodeToIndex(constraint.node) self._set_constraint_on_var(index, constraint, time_dimension) # Instantiate route start and end times to produce feasible times. for i in range(num_plans_to_create): routing.AddVariableMinimizedByFinalizer( time_dimension.CumulVar(routing.Start(i))) routing.AddVariableMinimizedByFinalizer( time_dimension.CumulVar(routing.End(i)))
def add_time_window_constraints( routing: RoutingModel, manager: RoutingIndexManager, data: dict, time_callback_index: int, ) -> None: """ Add time window constraints. """ horizon = 120 routing.AddDimension( time_callback_index, slack_max=horizon, # allow waiting time capacity=horizon, # maximum time per vehicle # Don't force start cumul to zero. This doesn't have any effect in this example, # since the depot has a start window of (0, 0). fix_start_cumul_to_zero=False, name='Time', ) time_dimension = routing.GetDimensionOrDie('Time') for loc_idx, (open_time, close_time) in enumerate(data['time_windows']): index = manager.NodeToIndex(loc_idx) time_dimension.CumulVar(index).SetRange(open_time, close_time)