Пример #1
0
    def city_swap(self, route_id1, route_id2, pick_random=False):
        """city swap in two route"""
        if route_id1 == route_id2:
            print("[ERROR] Route IDs must be different for city_swap: {} {}".
                  format(route_id1, route_id2))

        route1, route2 = self.schedule.get_multiple([route_id1, route_id2])

        old_distance = route1.total_distance + route2.total_distance
        min_dist = None
        neighbor = None
        feasible_neighbors = []
        delta_distance = None

        # go through each store in route 1 except HQ
        for idx1, store1 in enumerate(route1.path[1:len(route1.path) - 1]):

            new_path1 = route1.path[:]

            # go through each store in route 2 except HQ
            for idx2, store2 in enumerate(route2.path[1:len(route2.path) - 1]):

                new_path2 = route2.path[:]

                # swap city between two routes
                new_path1[idx1 + 1], new_path2[idx2 + 1] = store2, store1

                # check if new routes are feasible
                # add 0.01 as minimum distance gained to prevent numerical problem (float problem)
                new_route1 = Route()
                if new_route1.traverse(new_path1, self.maps):
                    new_route2 = Route()
                    if new_route2.traverse(new_path2, self.maps):
                        new_distance = new_route1.total_distance + new_route2.total_distance
                        if pick_random:
                            feasible_neighbors.append(
                                (old_distance - new_distance, (new_route1,
                                                               new_route2)))
                        # if pick_random is False,
                        # consider the best out of the feasible alternatives in terms of distance.
                        elif (min_dist is None
                              or (new_route1.total_distance +
                                  new_route2.total_distance < min_dist)):
                            min_dist = new_distance
                            neighbor = (new_route1, new_route2)

        if pick_random and len(feasible_neighbors) > 0:
            delta_distance, neighbor = feasible_neighbors[random.randint(
                0,
                len(feasible_neighbors) - 1)]
        elif not pick_random and min_dist is not None:
            delta_distance = old_distance - min_dist

        return (delta_distance, neighbor)
Пример #2
0
    def single_route_exchange(self, route_id, pick_random=False):
        """2-edge exchange within a route"""

        route = self.schedule.get(route_id)

        # no possible exchange can be made
        # if none or only 1 store is visited
        if len(route.path) < 4:
            return (None, None)

        old_distance = route.total_distance
        neighbor = None
        feasible_neighbors = []
        delta_distance = None

        # go through the stores in the path
        for i in range(1, len(route.path)):

            # edges cannot be adjacent to each other
            for j in range(i + 2, len(route.path)):

                # reverse the path in the middle
                candidate = route.path[0:i] + route.path[
                    i:j][::-1] + route.path[j:len(route.path)]

                # check if the route is equal to the original route,
                # a reversed order path is onsidered equal.
                if not route.is_equal(candidate):
                    new_route = Route()

                    # check if new route is feasible
                    # add 0.01 as minimum distance gained to prevent numerical problem (float problem)
                    if new_route.traverse(candidate, self.maps):
                        if pick_random:
                            feasible_neighbors.append(
                                (new_route.total_distance, (new_route, )))
                        elif abs(old_distance -
                                 new_route.total_distance) > 0.01 and (
                                     neighbor is None
                                     or new_route.total_distance <
                                     neighbor[0].total_distance):
                            # put inside iterable for consistency
                            neighbor = (new_route, )

        if pick_random and len(feasible_neighbors) > 0:
            delta_distance, neighbor = feasible_neighbors[random.randint(
                0,
                len(feasible_neighbors) - 1)]
        elif not pick_random and neighbor is not None:
            delta_distance = old_distance - neighbor[0].total_distance

        return (delta_distance, neighbor)
    def optimize_all(self, schedule):
        # call optimize() for each route
        for uid in schedule.get_all_route_ids():
            curr_route = schedule.get(uid)
            solution = self.optimize(curr_route)

            # rotate the route
            new_path = self.make_cycle(solution.tour)
            new_route = Route()

            # make sure the new route is feasible
            if new_route.traverse(new_path, self.maps):
                schedule.replace(uid, new_route)

            # if it is not feasible, use unoptimised route
            else:
                print("[INFO] route is no longer valid after ACO: {}".format(new_route))
Пример #4
0
    def two_route_exchange(self, route_id1, route_id2, pick_random=False):
        """2-edge exchange between 2 routes"""

        if route_id1 == route_id2:
            print(
                "[ERROR] Route IDs must be different for two_route_exchange: {} {}"
                .format(route_id1, route_id2))
        min_dist = None
        neighbor = None
        feasible_neighbors = []
        delta_distance = None
        route1, route2 = self.schedule.get_multiple((route_id1, route_id2))
        old_distance = route1.total_distance + route2.total_distance

        # go through every possible split of each route
        for split1 in route1.splits:
            for split2 in route2.splits:

                # for every 2-edge exchange, there are 2 alternatives
                # each alternative consists of possibly 2 routes.
                # e.g. we want to exchange:
                #   route1 = [ABC][DEF], route2 = [GHI][JKL]
                candidates = (
                    # alternative 1: [ABC][JKL], [GHI][DEF]
                    (split1[0] + split2[1], split2[0] + split1[1]),

                    # alternative 2: [ABC][IHG], [LKJ][DEF]
                    (split1[0] + split2[0][::-1], split1[1][::-1] + split2[1]))

                # check feasibility and find minimum
                for candidate in candidates:
                    new_route1 = Route()

                    # check if the first candidate route is equal with either route 1 or 2
                    is_exist1 = route1.is_equal(
                        candidate[0]) or route2.is_equal(candidate[0])

                    # check if the first candidate route is feasible
                    if not is_exist1 and new_route1.traverse(
                            candidate[0], self.maps):
                        new_route2 = Route()

                        # check if the second candidate route is equal with either route 1 or 2
                        is_exist2 = route1.is_equal(
                            candidate[1]) or route2.is_equal(candidate[1])

                        # check if the second route is feasible
                        if not is_exist2 and new_route2.traverse(
                                candidate[1], self.maps):
                            new_distance = new_route1.total_distance + new_route2.total_distance
                            if pick_random:
                                feasible_neighbors.append(
                                    (new_distance - old_distance,
                                     (new_route1, new_route2)))

                            # if pick_random is False,
                            # consider the best out of the feasible alternatives
                            # in terms of distance.
                            elif (min_dist is None
                                  or (new_route1.total_distance +
                                      new_route2.total_distance < min_dist)):
                                min_dist = new_distance
                                neighbor = (new_route1, new_route2)

                        # if is_valid2 and (min_dist is None or (
                        #         new_route1.total_distance + new_route2.total_distance < min_dist)):
                        #     min_dist = new_route1.total_distance + new_route2.total_distance
                        #     min_neighbor = (new_route1, new_route2)

        if pick_random and len(feasible_neighbors) > 0:
            delta_distance, neighbor = feasible_neighbors[random.randint(
                0,
                len(feasible_neighbors) - 1)]
        elif not pick_random and min_dist is not None:
            delta_distance = old_distance - min_dist

        return (delta_distance, neighbor)