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 _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 _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)))