Пример #1
0
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}.')
Пример #2
0
    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))
Пример #3
0
    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)
Пример #4
0
    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
Пример #5
0
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)
Пример #6
0
    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)
Пример #7
0
    def test_build_manager(self):
        """Asserts that a manager is built correctly from the Problem"""

        manager = RoutingIndexManager(
            len(self.problem.stops),  # Number of locations
            len(self.problem.vehicles),  # Number of vehicles
            self.problem.starts,  # Start list of Vehicles
            self.problem.ends  # End list of Vehicles
        )
        self.assertTrue(manager, msg='Opt. Manager could not be built.')
        self.assertEqual(manager.GetNumberOfVehicles(),
                         len(self.vehicles),
                         msg='Number of vehicles in manager is incorrect.')
        self.assertEqual(manager.GetNumberOfIndices(),
                         len(self.vehicles) * 2 + len(self.stops) -
                         len(self.problem.depots),
                         msg='Number of indices in manager is incorrect.')
        solver = RoutingModel(manager)
        self.assertTrue(solver, msg='Solver could not be instantiated.')
Пример #8
0
    def _parse_initial_routes(self, data: VehicleRoutingProblemInstance,
                              manager: RoutingIndexManager):
        if not data.previous_plans:
            return None

        ret = []

        for route in data.previous_plans:
            ret.append([manager.NodeToIndex(node) for node in route])

        return ret
Пример #9
0
    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
Пример #10
0
    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.')
Пример #11
0
def node_properties(
    manager: RoutingIndexManager,
    assignment: Assignment,
    capacity_dimension: RoutingDimension,
    time_dimension: RoutingDimension,
    index: int,
) -> tuple:
    """
    Get a node's properties corresponding to the index.
    """

    node_index = manager.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)
Пример #12
0
def node_properties(
        routing: pywrapcp.RoutingModel,
        manager: pywrapcp.RoutingIndexManager,
        assignment: pywrapcp.Assignment,
        capacity_dimension: pywrapcp.RoutingDimension,
        time_dimension: pywrapcp.RoutingDimension,
        index: int,
) -> tuple:
    """
    Get a node's properties on the index.
    """
    node_index = manager.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)
Пример #13
0
    def build(self, problem: Problem) -> OptimizationModel:
        """Method to build an Opt. Model from the 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)
        search_parameters = self._build_search_parameters(problem)
        self._apply_constraints(problem, manager, solver)
        self._set_objective_function(problem, manager, solver)

        return OptimizationModel(manager=manager,
                                 solver=solver,
                                 search_parameters=search_parameters)
Пример #14
0
    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.')
Пример #15
0
    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
Пример #16
0
def node_properties(
	data: dict,
	manager: RoutingIndexManager,
	assignment: Assignment,
	capacity_dimension: RoutingDimension,
	time_dimension: RoutingDimension,
	index: int,
) -> tuple:
	"""
	Get a node's properties corresponding to the index.
	"""
	node_index = manager.IndexToNode(index)
	demand = data['demands'][node_index]
	load = assignment.Value(capacity_dimension.CumulVar(index))
	if 'time_windows' in data.keys():
		time_var = time_dimension.CumulVar(index)
		time = assignment.Value(time_dimension.CumulVar(index))
		time_min, time_max = assignment.Min(time_var), assignment.Max(time_var)
		return (node_index, demand, time_min, time_max, load, time)
	return (node_index, demand, load)
Пример #17
0
def add_time_window_constraints(routing: pywrapcp.RoutingModel, manager: pywrapcp.RoutingIndexManager, data: dict,
                                time_callback, transit_callback_index):
    """
    Add time window constraints.
    """
    time = 'Time'
    horizon = 2400

    routing.AddDimension(
        transit_callback_index,
        slack_max=60,  # 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,
    )
    print("hello 5")
    time_dimension = routing.GetDimensionOrDie(time)
    for loc_node, (open_time, close_time) in enumerate(data['time_windows']):
        index = manager.NodeToIndex(loc_node)
        time_dimension.CumulVar(index).SetRange(open_time, close_time)
Пример #18
0
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)
Пример #19
0
    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)))
Пример #20
0
def add_time_window_constraints(
    routing: RoutingModel,
    manager: RoutingIndexManager,
    data: dict,
    time_callback_index: int,
) -> None:
    """
    Add time window constraints.
    """

    horizon = data["max_time"]
    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)
Пример #21
0
    return parser.parse_args()


if __name__ == '__main__':
    # Parse command line arguments
    args = parse_args()
    t1 = time()
    # Instantiate the data problem
    # data = load_data(args.path)
    if args.path is not None:
        hoge = OrtoolsJson(args.path)
        data = hoge.load_json()
    else:
        raise FileNotFoundError(f'json file: {args.path}')
    # Create Routing Index Manager
    manager = RoutingIndexManager(data['num_locations'], data['num_vehicles'],
                                  data['starts'], data['ends'])
    # Create Routing Model
    routing = RoutingModel(manager)
    """
	Define weight of each edge
	https://developers.google.com/optimization/routing/vrp
	"""
    distance_callback = create_distance_callback(manager, data)
    transit_callback_index = routing.RegisterTransitCallback(distance_callback)
    routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
    """
	Add capacity constraints
	https://developers.google.com/optimization/routing/cvrp
	"""
    demand_callback = create_demand_callback(manager, data)
    demand_callback_index = routing.RegisterUnaryTransitCallback(
def main():
    """
    Entry point of the program.
    """

    # Parse command line arguments
    args = parse_args()
    #Crafted data :
    coordinates = [[0.848307, 0.32357132], [0.53917086, 0.862489],
                   [0.28499496, 0.7470896], [0.66891015, 0.07388902],
                   [0.6514969, 0.4077109], [0.782356, 0.28167558],
                   [0.2601446, 0.9196638], [0.3402847, 0.62080956],
                   [0.3920853, 0.8653455], [0.79114413, 0.09456837],
                   [0.11343169, 0.73570406]]
    Distance_matrix = list()
    for j in range(len(coordinates)):
        l = list()
        for i in range(len(coordinates)):
            l.append(distance(coordinates[j], coordinates[i]))
        Distance_matrix.append(l)
    data = dict()
    data['weights'] = Distance_matrix
    data['service_times'] = [0, 1, 0.5, 1, 1, 0.5, 2, 2, 2, 0.5, 2]
    data['demands'] = [
        0, 0.25, 0.2, 0.1, 0.2, 0.35, 0.15, 0.3, 0.15, 0.15, 0.3
    ]
    data['time_windows'] = [[0, 9], [0, 5], [5, 9], [0, 9], [0, 9], [0, 9],
                            [0, 5], [5, 9], [5, 9], [0, 5], [0, 9]]
    data['vehicle_capacities'] = [1, 1, 1]
    data['depot'] = 0
    data['num_locations'] = 11
    data['num_vehicles'] = 3

    # 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 weight of each edge
    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()
    # pylint: disable=no-member
    search_params.first_solution_strategy = FirstSolutionStrategy.PATH_CHEAPEST_ARC
    if args.gls:
        # pylint: disable=no-member
        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(Distance_matrix, data, routing, manager, assignment)

    # Draw network and route graphs
    """if args.graph:
Пример #23
0
 def _set_omit_node_penalty(nodes: List[int], routing: RoutingModel,
                            manager: RoutingIndexManager):
     # Allow to drop nodes.
     penalty = 1000000
     for node in nodes:
         routing.AddDisjunction([manager.NodeToIndex(node)], penalty)