def _improve_with_3opt_star(self, problem, solution, strategy): if len(problem) == 5: N, pts, d, D, C = problem L = None else: N, pts, d, D, C, L = problem sol = list(solution) if not PRINT_ONLY_FINAL_RESULT: print("\nin", sol) total_t = 0 while True: start_t = time() out_sol = do_3optstar_move(sol, D, d, C, L, strategy) elapsed_t = time() - start_t total_t += elapsed_t if not PRINT_ONLY_FINAL_RESULT: print("elapsed %.2f s" % elapsed_t) print("out (%s)\n" % _strategy_to_str(strategy)) if out_sol[1] == None: print("no more improvements found") if PRINT_ONLY_FINAL_RESULT: print("final (%s)" % _strategy_to_str(strategy), sol, calculate_objective(sol, D)) print("total elapsed %.2f s" % total_t) break if not PRINT_ONLY_FINAL_RESULT: print(out_sol, calculate_objective(out_sol[0], D)) sol = out_sol[0] self.assertTrue(all(check_solution_feasibility(sol, D, d, C, L)), "must be feasible")
def _solve_instance(self, algo, pfn, round_D_func=None, require_K=False, predefined_k=None, suppress_constraint_check=False): N, points, dd_points, d, D, C, _ = cvrp_io.read_TSPLIB_CVRP(pfn) K, L, service_time = cvrp_io.read_TSBLIB_additional_constraints(pfn) if round_D_func: D = round_D_func(D) if predefined_k is not None: K = predefined_k if require_K and (K is None): raise IOError( "It is required that the VEHICLE field is set in %s" % pfn) if service_time: half_st = service_time / 2.0 if int(half_st) == half_st: half_st = int(half_st) service_time = int(service_time) # The service time can be modeled modifying the distance # matrix in a way that any visit to a depot node costs # service_time units. D_c = np.copy(D) D_c[1:, 1:] += service_time D_c[0, :] += half_st D_c[:, 0] += half_st np.fill_diagonal(D_c, 0.0) else: D_c = D if points is None and dd_points is not None: points = dd_points startt = time() if require_K: sol = algo(points, D_c, d, C, L, service_time, K) else: sol = algo(points, D_c, d, C, L, service_time) endt = time() elapsedt = endt - startt if __debug__: print_solution_statistics(sol, D, D_c, d, C, L, service_time) cover_ok, capa_ok, rlen_ok = check_solution_feasibility( sol, D, d, C, L, True) if not suppress_constraint_check: self.assertTrue(cover_ok, "Must be a valid solution") self.assertTrue(capa_ok, "Must not violate the C constraint") self.assertTrue(rlen_ok, "Must not violate the L constraint") return sol, objf(sol, D), objf(sol, D_c), elapsedt
def _compare_improved_from_solution(testcase, sol, D,d,C,L, ls_ops, naive_ops, operator_strategy=LSOPT.BEST_ACCEPT, extra_msg=""): """ Improves the solution `sol` for the problem `D`,`d`,`C`,`L` using the local_search module operators `ls_ops` and naive implementations `naive_ops`. """ if __debug__: print("THE PROBLEM:") print("D,d,C,L =","np.%s"%repr(D),",",d,",",C,",",L) rls_ops = list(reversed(ls_ops)) # note: if given multiple operators, the best single move out of among # all moves is chosen at each step due to ITEROPT.BEST_ACCEPT. ls_sol_fwdop = do_local_search(ls_ops, sol, D, d, C, L=L, operator_strategy=operator_strategy, iteration_strategy=operator_strategy) ls_sol_rwdop = do_local_search(rls_ops, sol, D, d, C, L=L, operator_strategy=operator_strategy, iteration_strategy=operator_strategy) bf_sol = do_naive_local_search(naive_ops, sol, D, d, C, L=L, operator_strategy=operator_strategy) ls_sol_fwdop = normalize_solution(ls_sol_fwdop) ls_sol_rwdop = normalize_solution(ls_sol_rwdop) bf_sol = normalize_solution(bf_sol) if __debug__: print("\nFINAL SOLUTIONS:") print("+".join( op.__name__ for op in ls_ops),"alt 1 :", ls_sol_fwdop, "(%.2f)"%objf(ls_sol_fwdop, D)) print("+".join( op.__name__ for op in ls_ops),"alt 2 :", ls_sol_rwdop, "(%.2f)"%objf(ls_sol_rwdop, D)) print("+".join( op.__name__ for op in naive_ops),":", bf_sol, "(%.2f)"%objf(bf_sol, D)) testcase.assertTrue(all(check_solution_feasibility(ls_sol_fwdop, D, d, C, L))) testcase.assertTrue(all(check_solution_feasibility(ls_sol_rwdop, D, d, C, L))) testcase.assertTrue(all(check_solution_feasibility(bf_sol, D, d, C, L))) testcase.assertTrue(ls_sol_fwdop==bf_sol or ls_sol_rwdop==bf_sol, extra_msg)
def _solve(self, name, desc, algof, C=None, L=None, st=None): d = self.d if C is None: d = None if st is not None: D_c = D2D_c(self.D, st) else: D_c = self.D for minimize_K in [False, True]: print( name, "(min_K=%s, C=%s, L=%s)" % (str(minimize_K), "%.1f" % C if C else "None", "%.1f" % L if L else "None")) try: sol = algof(self.pts, D_c, d, C=C, L=L, st=st, wtt="EXACT_2D", single=False, minimize_K=minimize_K) #print("done") except NotImplementedError: continue sol_f = objf(sol, D_c) print("SOLUTION %s (%.2f)" % (sol, objf(sol, D_c))) cover_ok, capa_ok, rlen_ok = check_solution_feasibility( sol, D_c, d, C, L) self.assertTrue(cover_ok, str(sol) + " is not a valid solution") self.assertTrue(capa_ok, str(sol) + " violates C constraint") self.assertTrue(rlen_ok, str(sol) + " violates L constraint") if L: self.assertGreaterEqual( sol_f, self.optf_L, "Cannot be better than the optimal solution") self.assertLessEqual( sol_f, self.worstf_L, "Must be better than the worst possible solution") else: self.assertGreaterEqual( sol_f, self.optf_C, "Cannot be better than the optimal solution") self.assertLessEqual( sol_f, self.worstf_C, "Must be better than the worst possible solution")
def print_solution_statistics(sol, D, D_cost, d, C, L=None, service_time=None, verbosity=-1): print("\nSOLUTION:", sol) cover_ok, capa_ok, rlen_ok = cvrp_ops.check_solution_feasibility( sol, D_cost, d, C, L, True) if verbosity > 1: print("ALL SERVED:", cover_ok) if C: print("IS C FEASIBLE:", capa_ok) if L: print("IS L FEASIBLE:", rlen_ok) else: print("FEASIBLE:", cover_ok and capa_ok and rlen_ok) print("SOLUTION K:", sol.count(0) - 1) sol_f = None if D is None else objf(sol, D) sol_c = None if D_cost is None else objf(sol, D_cost) if (verbosity > 0 and sol_f != sol_c) or (not sol_c): print("SOLUTION COST:", sol_c, "\n") if sol_c: print("SOLUTION LENGTH:", sol_f) if verbosity > 1: routes = sol2routes(sol) print("ROUTES:") print("No.\tCost\tLength\tLoad\tRoute") for i, route in enumerate(routes): print(i + 1, "%.2f" % objf(route, D_cost), "%.2f" % objf(route, D), sum((d[n] for n in route)) if C else "-", route, sep='\t') print("Total", "%.2f" % objf(sol, D_cost), "%.2f" % objf(sol, D), sep='\t')
def test_verify_reference_solutions_FosterRyan1976_instances(self): for problem_idx, problem_name in enumerate(self.problem_names): ref_k, ref_f = self.targets[1][problem_idx] if problem_name == r"04-CW64_n30a_k8c.vrp": problem_name = r"04-CW64_n31_k9c.vrp" ref_f = 1377 pfn = path.join(BENCHMARKS_BASEPATH, self.problem_path, problem_name) N, points, dd_points, d, D, C, _ = cvrp_io.read_TSPLIB_CVRP(pfn) K, L, service_time = cvrp_io.read_TSBLIB_additional_constraints( pfn) if service_time: D_c = D2D_c(D, service_time) else: D_c = D ref_sol = self.target_solutions[problem_idx] ref_sol_f = int(objf(ref_sol, D_c)) ref_sol_k = ref_sol.count(0) - 1 cover_ok, capa_ok, rlen_ok = check_solution_feasibility( ref_sol, D, d, C, L, True) self.assertTrue(cover_ok, "Must be a valid solution") self.assertTrue(capa_ok, "Must not violate the C constraint") self.assertTrue(rlen_ok, "Must not violate the L constraint") self.assertEqual( ref_k, ref_sol_k, msg= ("The appendix solution route count differs from the one given " + "in Table 2 for %s (%d vs %d)" % (problem_name, ref_sol_k, ref_k))) self.assertAlmostEqual( ref_f, ref_sol_f, msg=("The appendix solution result differs from the one given " + "in Table 2 for %s : %d (ours) vs %d (theirs)" % (problem_name, ref_sol_f, ref_f)))
def test_regression_small_static_case_with_C_and_L_constraints(self): pts = [ (0, 0), #0 (1, 1), #1 (1, 2), #2 (1, 3), #3 (0, 4), #4 (-2, 3), #5 (-2, 2), #6 (-2, 1) ] #7 D = squareform(pdist(pts, "euclidean")) d = [0.0] + [1.0] * (len(D) - 1) C = 4.0 L = 14.0 st = 2.0 D_c = D2D_c(D, st) #import cvrp_io #cvrp_io.write_TSPLIB_file("tiny_7_pt_problem.vrp", D, d, C, L) sol = gillet_miller_init(pts, D_c, d, C, L) self.assertTrue(check_solution_feasibility(sol, D_c, d, C, L), "Should produce feasible solution")
def main(overridden_args=None): ## 1. parse arguments parser = ArgumentParser( description= "Solve some .vrp problems with the algorithms built into VeRyPy.") parser.add_argument('-l', dest='list_algorithms', help="List the available heuristics and quit", action="store_true") parser.add_argument( '-v', dest='verbosity', help= "Set the verbosity level (to completely disable debug output, run this script with 'python -O')", type=int, default=-1) parser.add_argument( '-a', dest='active_algorithms', help= "Algorithm to apply (argument can be set multiple times to enable multiple algorithms, or one can use 'all' or 'classical')", action='append') parser.add_argument( '-b', dest='objective', choices=['c', 'cost', 'K', 'vehicles'], help="Primary optimization oBjective (default is cost)", default="cost") parser.add_argument( '-m', dest='minimal_output', help= "Overrides the output options and prints only one line CSV report per solved instance", action="store_true") parser.add_argument( '-t', dest='print_elapsed_time', help="Print elapsed wall time for each solution attempt", action="store_true") parser.add_argument( '-c', dest='show_solution_cost', help="Display solution cost instead of solution length", action="store_true") parser.add_argument('-D', dest='dist_weight_format', choices=['ROUND', 'EXACT', 'TRUNCATE'], help="Force distance matrix rounding") parser.add_argument( '-1', dest='use_single_iteration', help="Force the algorithms to use only single iteration.", action="store_true") parser.add_argument( '--iinfo', dest='print_instance_info', help="Print the instance info in the collected results", action="store_true") parser.add_argument( '--routes', dest='print_route_stat', help="Print per route statistics of the final solution", action="store_true") parser.add_argument('--vrph', dest='print_vrph_sol', help="Print the final solution in the VRPH format", action="store_true") parser.add_argument( '--forbid', dest='forbid_algorithms', help= "Forbid applying algorithms (argument can set multiple times to forbid multiple algorithms)", action='append') parser.add_argument( '--simulate', dest='simulate', help= "Do not really invoke algorithms, can be used e.g. to test scripts", action="store_true") #TODO: consider adding more LS opts e.g. 2optstart, 3optstart parser.add_argument( '--post-optimize', dest='local_search_operators', choices=['2opt', '3opt'], help= "Do post-optimization with local search operator(s) (can set multiple)", action='append') parser.add_argument( "problem_file", help= "a path of a .vrp problem file, a directory containing .vrp files, or a text file of paths to .vrp files", action='append') if overridden_args: app_args = parser.parse_args(overridden_args) elif "-l" in sys.argv: print("Select at least one algorithm (with -a) from the list:", file=sys.stderr) print(_build_algorithm_help()) sys.exit(1) elif len(sys.argv) == 1: print("Give at least one .vrp file and use -h to get help.", file=sys.stderr) sys.exit(1) else: app_args = parser.parse_args() # some further argument validation if not app_args.active_algorithms or app_args.list_algorithms: print("Select at least one algorithm (with -a) from the list:", file=sys.stderr) print(_build_algorithm_help()) exit() if len(app_args.problem_file) == 0: print("Provide at least one .vrp file to solve", file=sys.stderr) exit() # get .vrp file list files_to_solve = shared_cli.get_a_problem_file_list(app_args.problem_file) # get algorithms algos = get_algorithms(app_args.active_algorithms) if app_args.forbid_algorithms: forbidden_algos = [ algo_name_aliases[algo_name] for algo_name in app_args.forbid_algorithms if (algo_name in app_args.forbid_algorithms) ] algos = [a for a in algos if (a[0] not in forbidden_algos)] # get primary objective minimize_K = False if app_args.objective == 'K' or app_args.objective == 'vehicles': minimize_K = True run_single_iteration = False if app_args.use_single_iteration: run_single_iteration = True # get post-optimization local search move operators ls_ops = [] ls_algo_names = [] if app_args.local_search_operators: ls_algo_names = app_args.local_search_operators for ls_op_name in ls_algo_names: if ls_op_name == "2opt": ls_ops.append(do_2opt_move) if ls_op_name == "3opt": ls_ops.append(do_3opt_move) # verbosity if app_args.verbosity >= 0: shared_cli.set_logger_level(app_args.verbosity) # minimal header if app_args.minimal_output: print("algo;problem;is_feasible;f;K;t") ## 2. solve results = defaultdict(lambda: defaultdict(float)) instance_data = dict() interrupted = False for pfn in files_to_solve: bn = path.basename(pfn).replace(".vrp", "").replace(".tsp", "").replace(".pickle", "") try: N, points, dd_points, d, D, C, ewt, K, L, st = pickle.load( open(pfn, "rb")) except: N, points, dd_points, d, D, C, ewt = cvrp_io.read_TSPLIB_CVRP(pfn) K, L, st = cvrp_io.read_TSBLIB_additional_constraints(pfn) # We do not have point coodrinates, but we have D! if points is None: if dd_points is not None: points = dd_points else: points, ewt = cvrp_ops.generate_missing_coordinates(D) if app_args.dist_weight_format == "TRUNCATE": D = np.floor(D) ewt = "FLOOR_2D" if app_args.dist_weight_format == "ROUND": D = np.int(D) ewt = "EUC_2D" # Bake service time to D (if needed) D_c = cvrp_ops.D2D_c(D, st) if st else D for algo_abbreviation, algo_name, _, algo_f in algos: if not app_args.minimal_output: print("Solving %s with %s" % (bn, algo_name)) start_t = time() sol = None try: if not app_args.simulate: sol = algo_f(points, D_c, d, C, L, st, ewt, run_single_iteration, minimize_K) except (KeyboardInterrupt, Exception) as e: if type(e) is KeyboardInterrupt: interrupted = True # if interrupted on initial sol gen, return the best of those if len(e.args) > 0 and type(e.args[0]) is list: sol = e.args[0] if not app_args.minimal_output: print("WARNING: Interrupted solving %s with %s" % (bn, algo_abbreviation), file=sys.stderr) else: if not app_args.minimal_output: print("ERROR: Failed to solve %s with %s because %s" % (bn, algo_abbreviation, str(e)), file=sys.stderr) sol = None if sol: sol = cvrp_ops.normalize_solution(sol) if app_args.show_solution_cost: sol_q = cvrp_ops.calculate_objective(sol, D_c) else: sol_q = cvrp_ops.calculate_objective(sol, D) sol_K = sol.count(0) - 1 if app_args.local_search_operators: if not app_args.minimal_output: print("Postoptimize with %s ..." % ", ".join(app_args.local_search_operators), end="") sol = do_local_search(ls_ops, sol, D, d, C, L) sol = cvrp_ops.normalize_solution(sol) if app_args.show_solution_cost: ls_sol_q = cvrp_ops.calculate_objective(sol, D_c) else: ls_sol_q = cvrp_ops.calculate_objective(sol, D) if ls_sol_q < sol_q: if not app_args.minimal_output: print(" improved by %.2f%%." % (1 - ls_sol_q / sol_q)) sol_q = ls_sol_q sol_K = sol.count(0) - 1 else: if not app_args.minimal_output: print(" did not find improving moves.") else: sol_q = float('inf') elapsed_t = time() - start_t if app_args.minimal_output: print("%s;%s" % (algo_abbreviation, bn), end="") timecap_symbol = "*" if interrupted else "" if sol: feasible = all( cvrp_ops.check_solution_feasibility( sol, D_c, d, C, L, st)) print(";%s;%.2f;%d;%.2f%s" % (str(feasible), sol_q, sol_K, elapsed_t, timecap_symbol)) else: print(";False;inf;inf;%.2f%s" % (elapsed_t, timecap_symbol)) elif sol: # Minimal output is not enabled, print like crazy :) if app_args.print_elapsed_time: print("Algorithm produced a solution in %.2f s\n" % (elapsed_t)) else: #just a newline print() tightness = None if C and sol_K: tightness = (sum(d) / (C * sol_K)) if not bn in instance_data or sol_K < instance_data[bn][1]: #"N K C tightness L st" instance_data[bn] = (N, sol_K, C, "%.3f" % tightness, L, st) shared_cli.print_problem_information( points, D_c, d, C, L, st, tightness, verbosity=app_args.verbosity) solution_print_verbosity = 3 if app_args.print_route_stat else 1 shared_cli.print_solution_statistics(sol, D, D_c, d, C, L, st, solution_print_verbosity) if app_args.print_vrph_sol: print("SOLUTION IN VRPH FORMAT:") print(" ".join( str(n) for n in cvrp_io.as_VRPH_solution(sol))) print("\n") short_algo_name = algo_name results[bn][short_algo_name] = sol_q if interrupted: break # algo loop if interrupted: break # problem file loop ## Print collected results sys.stdout.flush() sys.stderr.flush() if not app_args.minimal_output and (len(results) > 1 or len(algos) > 1): print("\n") print_title = True ls_label = "+".join(ls_algo_names) for problem, algo_results in sorted(results.items()): algo_names = [ "%s+%s"%(algo_name,ls_label) if ls_algo_names else (algo_name)\ for algo_name in sorted(algo_results.keys())] if print_title: instance_fields = "instance\t" if PRINT_INSTANCE_DATA: #"N K C tightness L st" instance_fields += "N\tK*\tC\ttightness\tL\tst\t" print(instance_fields + "\t".join(algo_names)) print_title = False print(problem, end="") if PRINT_INSTANCE_DATA: print("\t", end="") print("\t".join(str(e) for e in instance_data[problem]), end="") for _, result in sorted(algo_results.items()): print("\t", result, end="") print() sys.stdout.flush() sys.stderr.flush()