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 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 read_and_solve_a_problem(problem_instance_path, with_algorithm_function, minimize_K, best_of_n=1, verbosity=-1, single=False, measure_time=False): """ Solve a problem instance with the path in problem_instance_path with the agorithm in <with_algorithm_function>. The <with_algorithm_function> has a signature of: init_f(points, D_c, d, C, L, st, wtt, verbosity, single, minimize_K) Options <verbosity>, <single> and <measure_time> may be used to adjust what is printed and if a restricted single iteration search (different meaning for different algorithms) is made.""" pfn = problem_instance_path N, points, dd_points, d, D, C, ewt = cvrp_io.read_TSPLIB_CVRP(pfn) required_K, L, st = cvrp_io.read_TSBLIB_additional_constraints(pfn) # model service time with the distance matrix D_c = cvrp_ops.D2D_c(D, st) if st else D if points is None: if dd_points is not None: points = dd_points else: points, ewt = cvrp_ops.generate_missing_coordinates(D) tightness = None if C and required_K: tightness = (sum(d) / (C * required_K)) if verbosity >= 0: print_problem_information(points, D, d, C, L, st, tightness, verbosity) best_sol = None best_f = float('inf') best_K = len(D) interrupted = False for repeat_n in range(best_of_n): sol, sol_f, sol_K = None, float('inf'), float('inf') start = time() try: sol = with_algorithm_function(points, D_c, d, C, L, st, ewt, single, minimize_K) except KeyboardInterrupt as e: print("WARNING: Solving was interrupted, returning " + "intermediate solution", file=sys.stderr) 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] elapsed = time() - start if sol: sol = cvrp_ops.normalize_solution(sol) sol_f = objf(sol, D_c) sol_K = sol.count(0) - 1 if is_better_sol(best_f, best_K, sol_f, sol_K, minimize_K): best_sol = sol best_f = sol_f best_K = sol_K if best_of_n > 1 and verbosity >= 1: print("SOLUTION QUALITY %d of %d: %.2f" % (repeat_n + 1, best_of_n, objf(best_sol, D_c))) if measure_time or verbosity >= 1: print("SOLVED IN: %.2f s" % elapsed) if interrupted: break if verbosity >= 0 and best_sol: n_best_sol = cvrp_ops.normalize_solution(best_sol) print_solution_statistics(n_best_sol, D, D_c, d, C, L, st, verbosity=verbosity) if interrupted: raise KeyboardInterrupt() return best_sol, objf(best_sol, D), objf(best_sol, D_c)
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()