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