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_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 from_cvrp_solution(cls, data_model: DataModel, manager: pywrapcp.RoutingIndexManager, routing: pywrapcp.RoutingModel, assignment) -> 'Optional[List[VehicleRoute]]': if not assignment: return None res = [] # type: List[VehicleRoute] for vehicle_id in range(data_model.vehicle_number): index = routing.Start(vehicle_id) route_indices = [] # type: List[int] route_distance = 0 # float route_load = 0 # float while not routing.IsEnd(index): node_index = manager.IndexToNode(index) route_indices.append(node_index) route_load += data_model.demands[node_index] previous_index = index index = assignment.Value(routing.NextVar(index)) route_distance += routing.GetArcCostForVehicle( previous_index, index, vehicle_id) route_indices.append(0) # returns to depot res.append( cls(data_model, vehicle_id, route_indices, route_distance, route_load)) current_app.logger.debug('CVRP Solution:\n{res}') return res
def draw_route_graph( data: dict, routing: RoutingModel, manager: RoutingIndexManager, assignment: Assignment, filename: str = 'route.png', prog='sfdp', ) -> None: """ Draw a route graph based on the solution of the problem. """ weights = data['weights'] demands = data['demands'] time_windows = data['time_windows'] graph = pgv.AGraph(directed=True) def _node(index: int) -> str: if index == 0: return f'{index}\nDepot' return f'{index}\nDemand: {demands[index]}\nRange: {time_windows[index]}' for vehicle_id in range(data['num_vehicles']): index = routing.Start(vehicle_id) while not routing.IsEnd(index): node_index = manager.IndexToNode(index) next_index = assignment.Value(routing.NextVar(index)) next_node_index = manager.IndexToNode(next_index) weight = weights[node_index][next_node_index] graph.add_edge(_node(node_index), _node(next_node_index), weight=weight, label=weight) index = next_index graph.draw(filename, prog=prog) print(f'The route graph has been saved to {filename}.')
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 _set_arc_distance(self, data: VehicleRoutingProblemInstance, manager: RoutingIndexManager, routing: RoutingModel): # ========= DISTANCE CONSTRAIN ========= cost_callback_indices = [] # model "cost between nodes per vehicle" for vehicle_idx in range(data.num_plans_to_create): def vehicle_cost_callback(from_index, to_index): from_node = manager.IndexToNode(from_index) to_node = manager.IndexToNode(to_index) distance = data.car_distance_matrix[from_node][ to_node] # in meters return distance cost_callback_index = routing.RegisterTransitCallback( vehicle_cost_callback) cost_callback_indices.append(cost_callback_index) routing.SetArcCostEvaluatorOfVehicle(cost_callback_index, vehicle_idx) routing.AddDimensionWithVehicleTransits( cost_callback_indices, 0, # waiting time MAX_TIMESTAMP_VALUE, # maximum distance per vehicle True, # Force start cumul to zero. self.DISTANCE_DIMENSION_NAME)
def solve(self, indices_to_visit: List[int] = None) -> Dict[str, Any]: """Finds the optimal order of facilities to minimize distance. Parameters ---------- indices_to_visit : List[int] The list of indices corresponding to the desired facilities to visit Returns ------- Dict[str, Any] Soltution dictionary with keys: - 'objective', set to objective value (minified distance) - 'order', instructions for the order of facilities to visit """ if indices_to_visit is None: indices_to_visit = list(range(len(self.matrix))) # make sure home location is in the listed, and that the list is sorted if self.home_index not in indices_to_visit: indices_to_visit.append(self.home_index) indices_to_visit.sort() data = self._create_data_model(indices_to_visit) # create routing index manager manager = RoutingIndexManager(len(data['distance_matrix']), data['num_vehicles'], data['home']) # create routing model routing = RoutingModel(manager) def distance_callback(from_index, to_index): # returns distance between two nodes from_node = manager.IndexToNode(from_index) to_node = manager.IndexToNode(to_index) dist = data['distance_matrix'][from_node][to_node] return dist transit_callback_index = routing.RegisterTransitCallback( distance_callback) # define cost of each arc routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index) # set first solution heuristic search_params = pywrapcp.DefaultRoutingSearchParameters() search_params.first_solution_strategy = ( routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # solve problem assignment = routing.SolveWithParameters(search_params) return self._extract_solution(manager, routing, assignment, indices_to_visit)
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 _solve(self, routing: RoutingModel, search_parameters: RoutingSearchParameters, initial_routes) -> Assignment: if initial_routes is None: solution = routing.SolveWithParameters(search_parameters) else: routing.CloseModelWithParameters(search_parameters) initial_solution = routing.ReadAssignmentFromRoutes( initial_routes, True) solution = routing.SolveFromAssignmentWithParameters( initial_solution, search_parameters) return solution
def add_capacity_constraints(routing: pywrapcp.RoutingModel, data: dict, demand_callback): """ Add capacity constraints. """ demand_callback_index = routing.RegisterUnaryTransitCallback(demand_callback) routing.AddDimensionWithVehicleCapacity( demand_callback_index, slack_max=0, # null slack # vehicle maximum capacities vehicle_capacities=data['vehicle_capacities'], fix_start_cumul_to_zero=True, # start cumul to zero name='Capacity', )
def apply(self, solver: RoutingModel): """Vehicle's occupation is restricted based on demand""" callback_index = solver.RegisterUnaryTransitCallback(self._callback) solver.AddDimensionWithVehicleCapacity( evaluator_index=callback_index, slack_max=0, vehicle_capacities=[ vehicle.capacity for vehicle in self.problem.vehicles.values() ], fix_start_cumul_to_zero=True, name='capacity_constraint' )
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 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 apply(self, manager: pywrapcp.RoutingIndexManager, routing: pywrapcp.RoutingModel, data_model: DataModel): nodes = data_model.nodes engineers = data_model.engineers_to_plan for index in range(0, data_model.number_of_nodes): if index < data_model.number_of_engineers: routing.SetAllowedVehiclesForIndex([index], index) else: allowed_vehicles = [ ind for ind, node in enumerate(engineers) if self.can_visit(data_model, nodes[index], engineers[ind]) ] routing.SetAllowedVehiclesForIndex(allowed_vehicles, index) return routing
def _set_objective_function(problem: Problem, manager: RoutingIndexManager, solver: RoutingModel): """Method to set the objective function of the Optimization Model""" def _time_callback(from_index: int, to_index: int): """Callback to obtain the complete time between Stops""" origin = manager.IndexToNode(from_index) destination = manager.IndexToNode(to_index) travelling_time = problem.estimations[(origin, destination)] service_time = problem.stops[destination].service_time return travelling_time + service_time callback_index = solver.RegisterTransitCallback(_time_callback) solver.SetArcCostEvaluatorOfAllVehicles(callback_index) logging.info('Set the objective function to the OptimizationModel.')
def test_apply_constraints_capacity_constraint(self): """Asserts constraints are read correctly by the solver""" model_builder = OptimizationModelBuilder( constraints=[CapacityConstraint()]) problem = self.problem manager = RoutingIndexManager( len(problem.stops), # Number of locations len(problem.vehicles), # Number of vehicles problem.starts, # Start list of Vehicles problem.ends # End list of Vehicles ) solver = RoutingModel(manager) model_builder._apply_constraints(problem, manager, solver) self.assertTrue(solver, msg='Constraints added incorrectly.') self.assertTrue(solver.HasDimension('capacity_constraint'), msg='Capacity constraint not added.')
def apply(self, manager: pywrapcp.RoutingModel, routing: pywrapcp.RoutingModel, data_model: DataModel): 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_model.demands[from_node] demand_callback_index = routing.RegisterUnaryTransitCallback( demand_callback) routing.AddDimensionWithVehicleCapacity( demand_callback_index, 0, # null capacity slack data_model.vehicle_capacities, True, # start cumul to zero 'Capacity') 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 main() -> None: """ Entry point of the program. """ # Parse command line arguments args = parse_args() # Instantiate the data problem data = load_data_model(args.path) # Create the Routing Index Manager and Routing Model manager = RoutingIndexManager(data["num_locations"], data["num_vehicles"], data["depot"]) routing = RoutingModel(manager) # Define weights of edges weight_callback_index = routing.RegisterTransitCallback( create_weight_callback(manager, data)) routing.SetArcCostEvaluatorOfAllVehicles(weight_callback_index) # Add capacity constraints demand_callback = create_demand_callback(manager, data) demand_callback_index = routing.RegisterUnaryTransitCallback( demand_callback) add_capacity_constraints(routing, manager, data, demand_callback_index) # Add time window constraints time_callback_index = routing.RegisterTransitCallback( create_time_callback(manager, data)) add_time_window_constraints(routing, manager, data, time_callback_index) # Set first solution heuristic (cheapest addition) search_params = DefaultRoutingSearchParameters() search_params.first_solution_strategy = FirstSolutionStrategy.PATH_CHEAPEST_ARC if args.gls: search_params.local_search_metaheuristic = ( LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH) # NOTE: Since Guided Local Search could take a very long time, we set a # reasonable time limit search_params.time_limit.seconds = 30 if args.verbose: search_params.log_search = True # Solve the problem assignment = routing.SolveWithParameters(search_params) if not assignment: print("No solution found.") return # Print the solution print_solution(data, routing, manager, assignment) # Export network and route graphs if args.export_network_graph: draw_network_graph(args.export_network_graph, data) if args.export_route_graph: draw_route_graph(args.export_route_graph, data, routing, manager, assignment)
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 _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: 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_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 add_capacity_constraints(routing: pywrapcp.RoutingModel, data: dict, demand_callback): """ Add capacity constraints. """ routing.AddDimensionWithVehicleCapacity( evaluator=demand_callback, slack_max=0, # null slack # vehicle maximum capacities vehicle_capacities=data['vehicle_capacities'], fix_start_cumul_to_zero=True, # start cumul to zero name='Capacity', )
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_arc_durations(self, data: VehicleRoutingProblemInstance, manager: RoutingIndexManager, routing: RoutingModel, dimension_name: str): # ========= DURATION CONSTRAIN ========= cost_callback_indices = [] # model "cost between nodes per vehicle" for vehicle_idx in range(data.num_plans_to_create): def vehicle_cost_callback(from_index, to_index): from_node = manager.IndexToNode(from_index) to_node = manager.IndexToNode(to_index) time = data.car_duration_matrix[from_node][ to_node] # in seconds is_pickup = from_node in data.pickup_nodes is_drop = from_node in data.drop_nodes waiting_time = 0 if is_pickup: waiting_time = ConfigProvider.get_config( ).pickup_waiting_time elif is_drop: waiting_time = ConfigProvider.get_config( ).drop_waiting_time return time + waiting_time cost_callback_index = routing.RegisterTransitCallback( vehicle_cost_callback) cost_callback_indices.append(cost_callback_index) routing.AddDimensionWithVehicleTransits( cost_callback_indices, MAX_TIMESTAMP_VALUE, # waiting time MAX_TIMESTAMP_VALUE, # maximum time per vehicle # since we are using timestamps as measuring unit we should not overflow False, # Don't force start cumul to zero. dimension_name)
def _extract_solution(self, manager: RoutingIndexManager, routing: RoutingModel, assignment: Assignment, indices_to_visit: List[int]) -> Dict[str, Any]: """Transform results to a usable format Parameters ---------- manager : RoutingIndexManager OR-tools' object to manage conversion between NodeIndex and variable index routing : RoutingModel OR-tools' object for route solving assignment : Assignment OR-tools' object for mapping from variable to domains indices_to_visit : List[int] The list of indices corresponding to the desired facilities to visit Returns ------- Dict[str, Any] With keys: - 'objective', set to objective value (minified distance) - 'order', instructions for the order of facilities to visit """ sln = {"objective": assignment.ObjectiveValue()} stop_indices = [] index = routing.Start(0) while not routing.IsEnd(index): relative_index = manager.IndexToNode(index) stop_indices.append(indices_to_visit[relative_index]) previous_index = index index = assignment.Value(routing.NextVar(index)) relative_index = manager.IndexToNode(index) stop_indices.append(indices_to_visit[relative_index]) sln["order"] = stop_indices return sln
def test_set_objective_function(self): """Asserts the objective function is added to the solver""" model_builder = OptimizationModelBuilder( constraints=[CapacityConstraint()]) problem = self.problem manager = RoutingIndexManager( len(problem.stops), # Number of locations len(problem.vehicles), # Number of vehicles problem.starts, # Start list of Vehicles problem.ends # End list of Vehicles ) solver = RoutingModel(manager) model_builder._set_objective_function(problem, manager, solver) self.assertTrue(solver, msg='Objective function set incorrectly.')
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)
def node_properties( routing: pywrapcp.RoutingModel, assignment: pywrapcp.Assignment, capacity_dimension: pywrapcp.RoutingDimension, time_dimension: pywrapcp.RoutingDimension, index: int, ) -> tuple: """ Get a node's properties on the index. """ node_index = routing.IndexToNode(index) load = assignment.Value(capacity_dimension.CumulVar(index)) time_var = time_dimension.CumulVar(index) time_min, time_max = assignment.Min(time_var), assignment.Max(time_var) return (node_index, load, time_min, time_max)