def test_2opt_one_crossed(self):
        route = [0, 1, 6, 2, 7, 0]

        initial_f = objf(route, self.D)
        sol, delta_f = do_2opt_move(route, self.D, LSOPT.BEST_ACCEPT)
        do_2opt_f = objf(sol, self.D)
        self.assertEqual(
            sol, [0, 1, 2, 6, 7, 0],
            "chose invalid move, initial %f, optimized %f" %
            (initial_f, do_2opt_f))
        self.assertEqual(
            initial_f + delta_f, do_2opt_f,
            "The delta based and recalculated objective functions differ")
Esempio n. 2
0
def inspect_heuristic(routes, D, C, d, L):
    improvement_found = False
    for rd in routes:
        if rd.is_empty():
            continue

        delta = 0.0
        while delta is not None:
            new_route, delta = do_2opt_move(rd.route, D, FAS)
            if delta is not None:
                rd.route = new_route
                rd.cost += delta
                improvement_found = True
    return improvement_found
 def test_2opt_two_crossed(self):
     route = [0, 1, 6, 3, 4, 5, 2, 7, 0]
     required_moves = 2
     for i in range(required_moves):
         #print "op %d:"%i+1
         #print "initial f =",f(route,self.D)
         opt_route, opt_f = do_2opt_move(route, self.D, LSOPT.BEST_ACCEPT)
         #print "2-opt'd f =",f(opt_route,self.D)
         if opt_route is None:
             return route
         else:
             route = opt_route
     self.assertEqual(i + 1, required_moves)
     self.assertEqual(opt_route, [0, 1, 2, 3, 4, 5, 6, 7, 0])
Esempio n. 4
0
def solve_tsp_ropt(D,
                   selected_idxs,
                   do_shuffle=False,
                   do2opt=True,
                   do3opt=True):
    # r-Opt (r \in {2,3} )
    endp = selected_idxs[0]

    if do_shuffle:
        shuffled_idxs = list(selected_idxs[1:])
        shuffle(shuffled_idxs)
        new_route = [endp] + shuffled_idxs + [endp]
    elif selected_idxs[-1] != endp:
        new_route = selected_idxs + [endp]
    else:
        new_route = selected_idxs

    new_route_cost = objf(new_route, D)

    # make first 2-optimal
    if do2opt:
        improved = True
        while improved:
            improved = False
            improved_route, delta = do_2opt_move(new_route, D, 1)
            if improved_route is not None:
                new_route = improved_route
                new_route_cost += delta
                improved = True

    # then 3-optimal (do not waste time on "easy" 2-opt
    #  operations if the route has already been made 2-optimal
    if do3opt:
        improved = True
        while improved:
            improved = False
            improved_route, delta = do_3opt_move(new_route, D, 1)
            if improved_route is not None:
                new_route = improved_route
                new_route_cost += delta
                improved = True

    return new_route, new_route_cost
 def test_2opt_none_crossed(self):
     route = [0, 1, 2, 3, 0]
     sol, delta_f = do_2opt_move(route, self.D)
     self.assertEqual(
         sol, None,
         "Route was already 2-optimal, improvements are not possible")
 def test_empty_route(self):
     self.assertEqual(do_2opt_move([], self.D), (None, None))
     self.assertEqual(do_2opt_move([0, 0], self.D), (None, None))
Esempio n. 7
0
def _try_insert_2opt_and_update(insertion, rd, D, L, minimize_K):

    inserted = rd.route.insert(insertion.customer, insertion.before_node)
    #print("inserting", insertion, "resulting to", list(rd.route))

    ## keep the route 2-optimal
    # unfortunately we cannot do local_search.py do_2opt_move on the reoute,
    #  because it is an dllist. However, as the route is kept 2-optimal, it
    #  is probable that not all search attempts lead to updating the route,
    #  so convert and udpate only as needed.
    route_2opt_improved = list(rd.route)
    total_delta = insertion.cost_delta
    applied_2opt_moves = False
    while True:
        updated_route, delta = do_2opt_move(route_2opt_improved,
                                            D,
                                            strategy=LSOPT.BEST_ACCEPT)
        if delta is not None:
            applied_2opt_moves = True
            total_delta += delta
            route_2opt_improved = updated_route
        else:
            break

    # do not accept insertions/moves that make the solution worse!
    if not minimize_K:
        # Compared to a solution where the customer is served individually
        insertion_cost = total_delta - D[0, insertion.customer] - D[
            insertion.customer, 0]
        if insertion_cost > 0:
            if __debug__:
                log(DEBUG - 2,
                    ("Rejecting insertion of n%d " % insertion.customer) +
                    ("as it is expected make solution worse."))
            rd.route.remove(inserted)  # reverse the change
            return False, None

    if L and rd.cost + total_delta - S_EPS > L:
        # The L constraint cannot be satisfied, even with 2-opt
        rd.route.remove(inserted)  # reverse the change
        return False, None

    rd.cost += total_delta
    rd.used_capacity += insertion.demand_delta

    if applied_2opt_moves:
        # this is very ineffective, but as the dllist nodes and next/prev
        #  are read only, and the llist module does not offer a sequence
        #  reversing functionality (!) we have no choice but scrap the entire
        #  insertion queue and calculate the insertions all over for the
        #  updated route. :(
        #
        #TODO: an improvement would be to use an another doubly-linked-list
        # implementaiton that allows manipulation of the nodes / list
        del rd.potential_insertions[:]

        updated_dllist = dllist(route_2opt_improved)
        new_edges = []
        prev_node = updated_dllist.first
        current_node = prev_node.next
        while prev_node != updated_dllist.last:
            new_edges.append((prev_node, current_node))
            prev_node = current_node
            current_node = prev_node.next

        if __debug__:
            log(
                DEBUG, "Applying a 2-opt moves, which transforms " +
                " %s to %s with an %.2f improvement" %
                (str(list(rd.route)), str(route_2opt_improved),
                 total_delta - insertion.cost_delta))

        rd.route = updated_dllist
        return True, new_edges

    else:
        new_edges = [(insertion.after_node, inserted),
                     (inserted, insertion.before_node)]
        return True, new_edges