Ejemplo n.º 1
0
    def _test_ls_vs_vrhp_w_random_sol(self,
                                      vrph_heur,
                                      ls_heur,
                                      times=10,
                                      routes=1):
        for i in range(times):
            initial_sol = list(range(1, len(self.D)))
            shuffle(initial_sol)
            initial_sol = [0] + initial_sol + [0]
            routes_to_create = routes - 1
            while routes_to_create > 0:
                possible_0_positions = len(initial_sol) - initial_sol.count(
                    0) - 2
                insert_0_counter = randint(0, possible_0_positions - 1)
                for j in range(0, len(initial_sol) - 1):
                    if initial_sol[j] != 0 and initial_sol[j + 1] != 0:
                        # valid position to insert

                        if insert_0_counter == 0:
                            initial_sol.insert(j + 1, 0)
                            break
                        else:
                            insert_0_counter -= 1
                routes_to_create -= 1
            #initial_sol = [0, 7, 3, 0, 4, 1, 5, 2, 6, 0]
            #print("initial_solution", initial_sol)
            #route = [max(0,int(n)-1) for n in "0-2-5-7-4-6-8-3-0".split("-")]

            with NamedTemporaryFile(delete=False, suffix='.vrp') as tmpfile:
                tsplib_file_path = tmpfile.name
            write_TSPLIB_file(tsplib_file_path,
                              self.D,
                              float_to_int_precision=1000)

            with NamedTemporaryFile(delete=False, suffix='.opt') as tmpfile:
                opt_file_path = tmpfile.name
            write_OPT_file(opt_file_path, self.D, initial_sol)

            vrph_sol = _do_vrph_ls(tsplib_file_path, opt_file_path,
                                   [vrph_heur])

            ls_sol = do_local_search([ls_heur], initial_sol, self.D, self.d,
                                     self.C, LSOPT.BEST_ACCEPT)

            # make sure the routes are in right order

            vrph_sol = normalize_solution(vrph_sol)
            ls_sol = normalize_solution(ls_sol)

            print(
                "LS on", initial_sol, "vrph_sol = %s (%.2f)" %
                (str(vrph_sol), objf(vrph_sol, self.D)),
                "ls_sol = %s (%.2f)" % (str(ls_sol), objf(ls_sol, self.D)))
            self.assertEqual(vrph_sol, ls_sol)

            if not DEBUG_VRPH_CALL:
                os.remove(tsplib_file_path)
                os.remove(opt_file_path)
Ejemplo n.º 2
0
def solve_tsp_lkh(D, selected_idxs,
                  float_accuracy = LKH_EXACT_DISTANCES_PRECISION_DECIMALS,
                  num_runs = None):
    """
    A wrapper for LKH (Helsgaun 2006, 2009) TSP solver. Prepares the necessary
    problem and parameter files, calls the LKH executable, and interprets the
    results. 
    
    Helsgaun, K. (2006), "An Effective Implementation of K-opt Moves for the 
      Lin-Kernighan TSP Heuristic." DATALOGISKE SKRIFTER, No. 109, 2006. 
      Roskilde University.
    Helsgaun, K. (2009), "General k-opt submoves for the Lin-Kernighan TSP 
      heuristic." Mathematical Programming Computation, 1(2), 119-163.
      
    NOTE: Only symmetric problems are supported. If real valued D is given
    the accuracy of the optimization can be adjusted using the float_accuracy
    argument (the default accuracy is set in config.py"""
    
    if len(selected_idxs)<=3:
        p=None
        sol = list(sorted(selected_idxs))
        sol.append(sol[0])
        sol_f = 0
        for n in sol:
            if p is not None:
                sol_f+=int(D[p,n])
            p=n
        return sol, sol_f

    # 1. Create a TSPLIB file
    are_float_distances = issubclass(D.dtype.type, np.float)
    if not are_float_distances:
        float_accuracy = None
    with NamedTemporaryFile( delete=False, suffix='.tsp') as tmpfile:
        temp_problem_file_path = tmpfile.name
    write_TSPLIB_file(temp_problem_file_path, D,
                      selected_idxs=selected_idxs, 
                      float_to_int_precision=float_accuracy)
    
    # 2. Create a parameter file for lkh.exe
    with NamedTemporaryFile( delete=False, suffix='.par') as tmpfile:
        temp_parameter_file_path = tmpfile.name
    with NamedTemporaryFile( delete=False, suffix='.tspsol') as tmpfile:
        temp_output_file_path = tmpfile.name
    with open(temp_parameter_file_path, 'w') as problem_file:
        problem_file.write("PROBLEM_FILE = %s\n" % temp_problem_file_path)
        problem_file.write("OUTPUT_TOUR_FILE = %s\n" % temp_output_file_path)
        problem_file.write("TRACE_LEVEL = 0\n")
        #problem_file.write("SEED = 42\n")
        if num_runs:
            problem_file.write("RUNS = %d\n"%num_runs)
            
    
    # 3. Call lkh
    command = [LKH_EXE_PATH, temp_parameter_file_path]
    p = Popen(command, stdout=PIPE, stdin=PIPE)
    stdout_data = p.communicate(input=' ')[0]
    
    if __debug__:    
        log(DEBUG-3, stdout_data)
    
    #TODO: CHECK FOR LKH FAILURE?
    #if "Press any key to continue" in stdout_data[-40:]:
    #   lkh_successfull = True
    
    # 4. Process output
    sol = []
    obj_f = 0
    tail = []
    with open(temp_output_file_path, 'r') as output_file:
        skip = True
        depot_found = False
        for l in output_file.readlines():
            l = l.strip()
            if l == "EOF" or l == "-1":
                skip = True
            elif "Length = " in l:
                obj_f = float(l.split()[-1])
            elif not skip:
                vrp_nid = selected_idxs[int(l)-1]
                if vrp_nid==0:
                    tail.append(0)
                    depot_found = True
                if depot_found:                    
                    sol.append(vrp_nid)
                else:
                    tail.append(vrp_nid)
            if l == "TOUR_SECTION":
                skip = False
        sol+=tail
        if not depot_found:
            sol+=[sol[0]]
        
    remove_file(temp_problem_file_path) 
    remove_file(temp_parameter_file_path) 
    remove_file(temp_output_file_path) 
    
    if are_float_distances:
        obj_f = obj_f/float_accuracy;
    
    return sol, obj_f
Ejemplo n.º 3
0
def solve_tsp_acotsp(D,
                     selected_idxs,
                     float_accuracy=ACOTSP_EXACT_DISTANCES_PRECISION_DECIMALS,
                     use_ants=False):
    """
    A wrapper for ACOTSP (Stuzle 2002, Dorigo & Stuzle 2004) solver. This
    wrapper writes a temporary TSBLIB file, calls modified ACOTSP executable,
    and interprets the results. 
    
    The version of the ACOTSP solver used is modified from the v.1.03 of the
    original solver that can disable ants and only do the deterministic local 
    search. It is available at:
        
    https://github.com/juherask/ACOTSP
    
    The executable compiled from the modified code includes additional command
    line arguments for  disabling the ants by issuing command line arguments
    "-m 1 -r 1 -s 1 -n -j 0". The first arguments set only one trial for the
    deterministic version and the "-n" or "--noants" argument turns off all ant
    systems (pheromone trails etc.). The "-j 0" or "--lsrnd 0" turns off
    randomization of the local search search order. These are set if the
    function is called with the use_ants = False (default). Other modification
    includes allowing the printing of the solution to stdout when the --quiet
    flag is given. 
    
    With use_ants = False this is a very fast TSP solver as it builds solutions
    using nearest neighbors construction and then does 2-opt followed by 3-opt
    improvement steps. Using ant systems (i.e. use_ants = True) on the other
    hand is slow as it employs the stochastic metaheuristic in addition to same
    ops as without ants.
    
    Stutzle, T. (2002). ACOTSP: A software package of various ant colony
    optimization algorithms applied to the symmetric TSP.
    URL http://www.aco-metaheuristic.org/aco-code
    
    Dorigo, M. and Stuzle, T. (2004). Ant Colony Optimization, MIT Press,
    Cambridge, MA, USA.
    
    NOTE: Only symmetric problems are supported. Also, ACOTSP does not support 
    float distances, thus if a real valued D is given it is converted using the
    float_accuracy (which is a mutiplier, e.g. 1000.0, the default accuracy is
    set in config.py)"""

    if len(selected_idxs) <= 3:
        p = None
        sol = list(sorted(selected_idxs))
        sol.append(sol[0])
        sol_f = sum(D[sol[i - 1]][sol[i]] for i in range(1, len(sol)))
        return sol, sol_f

    # 1. Create a TSPLIB file
    are_float_distances = issubclass(D.dtype.type, np.float)
    if not are_float_distances:
        float_accuracy = None
    with NamedTemporaryFile(delete=False, suffix='.tsp') as tmpfile:
        temp_problem_file_path = tmpfile.name
    write_TSPLIB_file(temp_problem_file_path,
                      D,
                      selected_idxs=selected_idxs,
                      float_to_int_precision=float_accuracy)

    # 2. Call acotsp
    if use_ants:
        # with defaults
        command = [ACOTSP_EXE_PATH, "--quiet", "-i", temp_problem_file_path]
    else:
        # use only local 2-opt+3-opt local search
        nul_f = open(devnull, 'w')
        command = [
            ACOTSP_EXE_PATH,
            "-r",
            "1",  # one try is enough without ants (determnistic)
            "-m",
            "1",  # one (deteministic) ant is still needed 
            #  to hold the nearest neighbour + 3opt solution
            "-s",
            "1",  # build only 1 route (tour)
            "-n",  # disable all ant systemes (NEW)
            "-j",
            "0",  # turn of random local search order (NEW)
            "-l",
            "3",  # do 3-opt
            "--quiet",  # in 1.03 disables writing the files
            "-i",
            temp_problem_file_path
        ]
    p = Popen(command, stdout=PIPE, stdin=PIPE, stderr=nul_f)
    stdout_data = p.communicate(input=' ')[0]

    #if __debug__:
    #print(stdout_data)

    # 3. Process output
    sol = None
    sol_f = None

    best_re = re.compile("try [0-9]+, Best ([0-9]+(\.?[0-9]*)?),")
    best_obj = None
    for l in stdout_data.split('\n'):
        if "best solution so far is" in l[:25]:
            tour_indices = [
                int(n)
                for n in l.replace("best solution so far is", "").split()
            ]
            sol = list(selected_idxs[i] for i in tour_indices)
            first_pos = sol.index(selected_idxs[0])
            sol = sol[first_pos:] + sol[:first_pos] + [selected_idxs[0]]
            sol_f = sum(D[sol[i - 1]][sol[i]] for i in range(1, len(sol)))
            #print("REMOVEME:", sol_f, best_obj)

        bo = best_re.search(l)
        if bo:
            best_obj = float(bo.group(1))
            if are_float_distances:
                best_obj = best_obj / float_accuracy

    remove_file(temp_problem_file_path)
    return sol, sol_f