code reworking WIP
This commit is contained in:
parent
11f6a75f74
commit
a60ce57e19
3 changed files with 77 additions and 81 deletions
51
run.py
51
run.py
|
@ -1,57 +1,56 @@
|
|||
import pandas as pd
|
||||
from src.io_tsp import Instance
|
||||
from src.TSP_solver import SolverTSP
|
||||
from src.io_tsp import ProblemInstance
|
||||
from src.TSP_solver import SolverTSP, available_improvers, available_solvers
|
||||
import numpy as np
|
||||
|
||||
|
||||
def add(solver, instance, improve, index, results, name, verbose, show_plots):
|
||||
def add(solver, improve, index, results, name, verbose, show_plots):
|
||||
solver.bind(improve)
|
||||
solver(instance, return_value=False, verbose=verbose)
|
||||
solver.compute_solution(return_value=False, verbose=verbose)
|
||||
|
||||
if verbose:
|
||||
print(f"the total length for the solution found is {solver.found_length}",
|
||||
f"while the optimal length is {instance.best_sol}",
|
||||
f"while the optimal length is {solver.problem_instance.best_sol}",
|
||||
f"the gap is {solver.gap}%",
|
||||
f"the solution is found in {solver.time_to_solve} seconds", sep="\n")
|
||||
f"the solution is found in {solver.duration} seconds", sep="\n")
|
||||
|
||||
index.append((name, solver.name_method))
|
||||
results.append([solver.found_length, instance.best_sol, solver.gap, solver.time_to_solve])
|
||||
results.append([solver.found_length, solver.problem_instance.best_sol, solver.gap, solver.duration])
|
||||
|
||||
if show_plots:
|
||||
solver.plot_solution()
|
||||
|
||||
|
||||
def run(show_plots=False, verbose=False):
|
||||
# names = [name_ for name_ in os.listdir("./problems") if "tsp" in name_]
|
||||
names = ["eil76.tsp"]
|
||||
initializers = SolverTSP.available_initializers.keys()
|
||||
improvements = SolverTSP.available_improvements.keys()
|
||||
# problems = glob.glob('./problems/*.tsp')
|
||||
problems = ["./problems/eil76.tsp"]
|
||||
solvers_names = available_solvers.keys()
|
||||
improvers_names = available_improvers.keys()
|
||||
results = []
|
||||
index = []
|
||||
for name in names:
|
||||
filename = f"problems/{name}"
|
||||
instance = Instance(filename)
|
||||
for problem_path in problems:
|
||||
prob_instance = ProblemInstance(problem_path)
|
||||
if verbose:
|
||||
print("\n\n#############################")
|
||||
instance.print_info()
|
||||
prob_instance.print_info()
|
||||
if show_plots:
|
||||
instance.plot_data()
|
||||
prob_instance.plot_data()
|
||||
|
||||
for init in initializers:
|
||||
for improve in improvements:
|
||||
solver = SolverTSP(init)
|
||||
add(solver, instance, improve, index, results, name, verbose, show_plots)
|
||||
for improve2 in [j for j in improvements if j not in [improve]]:
|
||||
add(solver, instance, improve2, index, results, name, verbose, show_plots)
|
||||
for solver_name in solvers_names:
|
||||
for improve in improvers_names:
|
||||
solver = SolverTSP(solver_name, prob_instance)
|
||||
add(solver, improve, index, results, problem_path, verbose, show_plots)
|
||||
for improve2 in [j for j in improvers_names if j not in [improve]]:
|
||||
add(solver, improve2, index, results, problem_path, verbose, show_plots)
|
||||
|
||||
for improve3 in [j for j in improvements if j not in [improve, improve2]]:
|
||||
add(solver, instance, improve3, index, results, name, verbose, show_plots)
|
||||
for improve3 in [j for j in improvers_names if j not in [improve, improve2]]:
|
||||
add(solver, improve3, index, results, problem_path, verbose, show_plots)
|
||||
solver.pop()
|
||||
|
||||
solver.pop()
|
||||
|
||||
if instance.exist_opt and show_plots:
|
||||
solver.solution = np.concatenate([instance.optimal_tour, [instance.optimal_tour[0]]])
|
||||
if prob_instance.exist_opt and show_plots:
|
||||
solver.solution = np.concatenate([prob_instance.optimal_tour, [prob_instance.optimal_tour[0]]])
|
||||
solver.method = "optimal"
|
||||
solver.plot_solution()
|
||||
|
||||
|
|
|
@ -8,99 +8,95 @@ from src.two_dot_five_opt import loop2dot5opt
|
|||
from src.simulated_annealing import sa
|
||||
from src.constructive_algorithms import random_method, nearest_neighbor, best_nearest_neighbor, multi_fragment_mf
|
||||
|
||||
|
||||
class SolverTSP:
|
||||
|
||||
solution: ndarray
|
||||
found_length: float
|
||||
available_initializers = {"random": random_method,
|
||||
available_solvers = {"random": random_method,
|
||||
"nearest_neighbors": nearest_neighbor,
|
||||
"best_nn": best_nearest_neighbor,
|
||||
"multi_fragment": multi_fragment_mf
|
||||
}
|
||||
|
||||
available_improvements = {"2-opt": loop2opt,
|
||||
available_improvers = {"2-opt": loop2opt,
|
||||
"2.5-opt": loop2dot5opt,
|
||||
"simulated_annealing": sa}
|
||||
|
||||
# ,
|
||||
# "simulated_annealing": Simulated_Annealing,
|
||||
# "iterated_local_search": Iterated_Local_Search}
|
||||
|
||||
def __init__(self, initializer):
|
||||
# self.available_methods = {"random": self.random_method, "nearest_neighbors": self.nn,
|
||||
# "best_nn": self.best_nn, "multi_fragment": self.mf}
|
||||
self.initializer = initializer
|
||||
self.methods = [initializer]
|
||||
self.name_method = "initialized with " + initializer
|
||||
class SolverTSP:
|
||||
solution: ndarray
|
||||
found_length: float
|
||||
|
||||
def __init__(self, algorithm_name, problem_instance):
|
||||
assert algorithm_name in available_solvers, f"the {algorithm_name} initializer is not available currently."
|
||||
self.duration = np.inf
|
||||
self.algorithm_name = algorithm_name
|
||||
self.algorithms = [algorithm_name]
|
||||
self.name_method = "initialized with " + algorithm_name
|
||||
self.solved = False
|
||||
assert initializer in self.available_initializers, f"the {initializer} initializer is not available currently."
|
||||
self.problem_instance = problem_instance
|
||||
|
||||
def bind(self, local_or_meta):
|
||||
assert local_or_meta in self.available_improvements, f"the {local_or_meta} method is not available currently."
|
||||
self.methods.append(local_or_meta)
|
||||
assert local_or_meta in available_improvers, f"the {local_or_meta} method is not available currently."
|
||||
self.algorithms.append(local_or_meta)
|
||||
self.name_method += ", improved with " + local_or_meta
|
||||
|
||||
def pop(self):
|
||||
self.methods.pop()
|
||||
self.algorithms.pop()
|
||||
self.name_method = self.name_method[::-1][self.name_method[::-1].find("improved"[::-1]) + len("improved") + 2:][
|
||||
::-1]
|
||||
|
||||
def __call__(self, instance_, verbose=True, return_value=True):
|
||||
self.instance = instance_
|
||||
def compute_solution(self, verbose=True, return_value=True):
|
||||
self.solved = False
|
||||
if verbose:
|
||||
print(f"### solving with {self.methods} ####")
|
||||
start = t()
|
||||
self.solution = self.available_initializers[self.methods[0]](instance_)
|
||||
print(f"### solving with {self.algorithms} ####")
|
||||
start_time = t()
|
||||
self.solution = available_solvers[self.algorithms[0]](self.problem_instance)
|
||||
assert self.check_if_solution_is_valid(self.solution), "Error the solution is not valid"
|
||||
for i in range(1, len(self.methods)):
|
||||
self.solution = self.available_improvements[self.methods[i]](self.solution, self.instance)
|
||||
for i in range(1, len(self.algorithms)):
|
||||
self.solution = available_improvers[self.algorithms[i]](self.solution, self.problem_instance)
|
||||
assert self.check_if_solution_is_valid(self.solution), "Error the solution is not valid"
|
||||
|
||||
end = t()
|
||||
self.time_to_solve = np.around(end - start,3)
|
||||
end_time = t()
|
||||
self.duration = np.around(end_time - start_time, 3)
|
||||
self.solved = True
|
||||
self.evaluate_solution()
|
||||
self._gap()
|
||||
if verbose:
|
||||
print(f"### solution found with {self.gap} % gap in {self.time_to_solve} seconds ####")
|
||||
print(f"the total length for the solution found is {self.found_length}",
|
||||
f"while the optimal length is {self.instance.best_sol}",
|
||||
f"the gap is {self.gap}%",
|
||||
f"the solution is found in {self.time_to_solve} seconds", sep="\n")
|
||||
# if verbose:
|
||||
# print(f"### solution found with {self.gap} % gap in {self.duration} seconds ####",
|
||||
# f"the total length for the solution found is {self.found_length}",
|
||||
# f"while the optimal length is {prob_instance.best_sol}",
|
||||
# f"the gap is {self.gap}%")
|
||||
|
||||
if return_value:
|
||||
return self.solution
|
||||
|
||||
def plot_solution(self):
|
||||
assert self.solved, "You can't plot the solution, you need to solve it first!"
|
||||
assert self.solved, "You can't plot the solution, you need to compute it first!"
|
||||
plt.figure(figsize=(8, 8))
|
||||
self._gap()
|
||||
plt.title(f"{self.instance.name} solved with {self.name_method} solver, gap {self.gap}")
|
||||
ordered_points = self.instance.points[self.solution]
|
||||
plt.title(f"{self.problem_instance.name} solved with {self.name_method} solver, gap {self.gap}")
|
||||
ordered_points = self.problem_instance.points[self.solution]
|
||||
plt.plot(ordered_points[:, 1], ordered_points[:, 2], 'b-')
|
||||
plt.show()
|
||||
|
||||
def check_if_solution_is_valid(self, solution):
|
||||
rights_values = np.sum([self.check_validation(i, solution[:-1]) for i in np.arange(self.instance.nPoints)])
|
||||
if rights_values == self.instance.nPoints:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
# rights_values = np.sum(
|
||||
# [self.check_validation(i, solution[:-1]) for i in np.arange(self.problem_instance.nPoints)])
|
||||
rights_values = np.sum([1 if np.sum(solution[:-1] == i) == 1 else 0 for i in np.arange(self.problem_instance.nPoints)])
|
||||
return rights_values == self.problem_instance.nPoints
|
||||
# return True
|
||||
# else:
|
||||
# return False
|
||||
|
||||
def check_validation(self, node, solution):
|
||||
if np.sum(solution == node) == 1:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
# def check_validation(self, node, solution):
|
||||
# if np.sum(solution == node) == 1:
|
||||
# return 1
|
||||
# else:
|
||||
# return 0
|
||||
|
||||
def evaluate_solution(self, return_value=False):
|
||||
total_length = 0
|
||||
starting_node = self.solution[0]
|
||||
from_node = starting_node
|
||||
for node in self.solution[1:]:
|
||||
total_length += self.instance.dist_matrix[from_node, node]
|
||||
total_length += self.problem_instance.dist_matrix[from_node, node]
|
||||
from_node = node
|
||||
|
||||
self.found_length = total_length
|
||||
|
@ -109,4 +105,5 @@ class SolverTSP:
|
|||
|
||||
def _gap(self):
|
||||
self.evaluate_solution(return_value=False)
|
||||
self.gap = np.round(((self.found_length - self.instance.best_sol) / self.instance.best_sol) * 100, 2)
|
||||
self.gap = np.round(
|
||||
((self.found_length - self.problem_instance.best_sol) / self.problem_instance.best_sol) * 100, 2)
|
||||
|
|
|
@ -6,7 +6,7 @@ from numpy.core._multiarray_umath import ndarray
|
|||
from src.utils import distance_euc
|
||||
|
||||
|
||||
class Instance:
|
||||
class ProblemInstance:
|
||||
nPoints: int
|
||||
best_sol: int
|
||||
name: str
|
||||
|
|
Reference in a new issue