This commit is contained in:
Claudio Maggioni 2020-12-21 15:46:50 +01:00
parent 412c87ef2a
commit bcce2ac0d5
20 changed files with 173 additions and 533 deletions

2
.gitignore vendored
View file

@ -109,7 +109,7 @@ profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# For a library or package, you might want to ignore these files since the aco is
# intended to run in multiple environments; otherwise, check them in:
# .python-version

Binary file not shown.

78
aco/TSP_solver.py Normal file
View file

@ -0,0 +1,78 @@
from time import time as t
import numpy as np
import matplotlib.pyplot as plt
from aco.ant_colony import ant_colony_opt
class TSPSolver:
def __init__(self, problem_instance):
self.duration = np.inf
self.found_length = np.inf
self.solved = False
self.problem_instance = problem_instance
self.solution = None
def compute_solution(self, verbose=True, return_value=True):
self.solved = False
if verbose:
print(f"### solving with 'C++ ant colony optimization' ####")
start_time = t()
self.solution = ant_colony_opt(self.problem_instance)
if not self.check_if_solution_is_valid():
print(f"Error the solution of 'C++ ant colony optimization'"
f" for problem {self.problem_instance.name} is not valid")
if return_value:
return False
end_time = t()
self.duration = np.around(end_time - start_time, 3)
self.solved = True
self.evaluate_solution()
self._gap()
if return_value:
return self.solution
def plot_solution(self):
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.problem_instance.name} solved with 'C++ ant colony optimization'"
f" 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):
rights_values = np.sum(
[self.check_validation(i, self.solution[:-1]) for i in
np.arange(self.problem_instance.nPoints)])
return rights_values == self.problem_instance.nPoints
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.problem_instance.dist_matrix[from_node, node]
from_node = node
self.found_length = total_length
if return_value:
return total_length
def pass_and_check_if_solution_is_valid(self, solution):
rights_values = np.sum(
[self.check_validation(i, solution[:-1]) for i in
np.arange(self.problem_instance.nPoints)])
return rights_values == self.problem_instance.nPoints
def _gap(self):
self.evaluate_solution(return_value=False)
self.gap = np.round(
((self.found_length - self.problem_instance.best_sol) /
self.problem_instance.best_sol) * 100, 2)

View file

@ -1,6 +1,6 @@
import os
dir = "/Users/maggicl/Git/AI2020BsC/c_prob/"
dir = "c_prob/"
def ant_colony_opt(instance):
fname = dir + instance.name + ".txt"
@ -12,7 +12,7 @@ def ant_colony_opt(instance):
# single core params
params = {
"eil76": (0.9, 8, 0.4, 1000, 500, 200, 30), # 543
"d198": (0.9, 8, 0.4, 1000, 500, 60, 30), # 16042
"d198": (0.9, 8, 0.4, 1000, 250, 125, 5), # 15871
"ch130": (0.9, 8, 0.4, 1000, 1750, 25, 30), # 6212
"kroA100": (0.9, 8, 0.4, 1000, 750, 100, 30), # 21378
"lin318": (0.9, 8, 0.4, 1000, 500, 32, 30), # 43171
@ -20,22 +20,17 @@ def ant_colony_opt(instance):
"pr439": (0.9, 8, 0.4, 1000, 450, 24, 2), # 11734
"rat783": (0.9, 8, 0.4, 1000, 450, 24, 2), # 9232
"u1060": (0.9, 8, 0.4, 1000, 350, 6, 3), # 238025
"fl1577": (0.9, 8, 0.4, 1000, 50, 9, 3), # 24145
#"fl1577": (0.9, 8, 0.4, 1000, 50, 9, 3), # 24145
"fl1577": (0.9, 8, 0.4, 1000, 50, 10, 5), # 24145
}
# params_multicore = {
# "d198": (0.9, 8, 0.4, 1000, 750, 200, 30), # 15970
# "ch130": (0.9, 8, 0.4, 1000, 1750, 200, 30), # 6173
# }
alpha, beta, evap, weight, ants, loops, optruns = params[instance.name]
# Call C++ program
cmd = dir + "aco " + str(instance.nPoints) + " " + str(loops) + " " + str(ants) + \
" " + str(alpha) + " " + str(beta) + " " + str(evap) + " " + str(weight) + " " + str(optruns) + \
" < " + fname + " &2>/dev/null"
" " + str(alpha) + " " + str(beta) + " " + str(evap) + " " + str(weight) + \
" " + str(optruns) + " < " + fname + " &2>/dev/null"
print(cmd)
solution = eval(os.popen(cmd).read())
solution.append(solution[0])
print(solution)
print(len(solution))
return solution

View file

@ -1,11 +1,8 @@
import numpy as np
from matplotlib import pyplot as plt
from src.utils import distance_euc
from aco.utils import distance_euc
class ProblemInstance:
def __init__(self, name_tsp):
self.exist_opt = False
self.optimal_tour = None
@ -32,7 +29,9 @@ class ProblemInstance:
self.points[i, 2] = line_i[2]
self.create_dist_matrix()
if self.file_name in ["./problems/eil76.tsp", "./problems/kroA100.tsp", "../problems/eil76.tsp",
if self.file_name in ["./problems/eil76.tsp",
"./problems/kroA100.tsp",
"../problems/eil76.tsp",
"../problems/kroA100.tsp"]:
self.exist_opt = True
file_object = open(self.file_name.replace(".tsp", ".opt.tour"))

8
aco/utils.py Normal file
View file

@ -0,0 +1,8 @@
import numpy as np
def distance_euc(point_i, point_j):
rounding = 0
x_i, y_i = point_i[0], point_i[1]
x_j, y_j = point_j[0], point_j[1]
distance = np.sqrt((x_i - x_j) ** 2 + (y_i - y_j) ** 2)
return round(distance, rounding)

View file

@ -1,6 +0,0 @@
ants = 10
alpha = 0.1
rho = 0.1
q0 = 0.95 = 1 - (15/nCities)
beta = b1 = 1

35
execute.md Normal file
View file

@ -0,0 +1,35 @@
# Instructions for execution
## Requirements
In order to reproduce the results reported in the Excel table,
one must have the following requirements:
- A Python 3 interpreter and all Python dependencies required for the `AI2020BsC` repository installed, namely
`time`, `matplotlib` and `numpy`;
- A working C++11 or greater compiler accessible from the `c++` command (Modern versions of clang and g++ are both
acceptable compilers).
## Execution
`cd` in the `code` directory and execute `python3 ./run.py` from the terminal.
The script will automatically compile the C++ companion program and start the TSP computation for each problem.
Results will be saved in `<problemname>.sol` files where `<problemname>` is respectively the name
of the problem solved.
Execution time for each problem will be computed using the same criteria used for the original `AI2020BsC` repository.
Times and lengths found will be saved in a file named `results.csv` which will be located in the same directory as
`run.py`.
## `.sol` file contents
Solution files are made up of just one line, containing a valid Python expression representing an array.
The array in question contains the order in which cities must be visited, representing each city by its number.
The array starts and ends with the starting city of the tour.
Here is the Backus Naur grammar for `.sol` files:
```
<.sol file> ::= <city array> '\n'
<city array> ::= '[' <start city> ", " <cities> <start city> ']'
<cities> ::= <city> ", " <cities> | <city> ", "
<start city> ::= unsigned integer
<city> ::= unsigned integer
```

3
readme.md Normal file
View file

@ -0,0 +1,3 @@
# 2020 AI Cup submission - Claudio Maggioni
For instructions on how to compile and run my submission in order to reproduce
result please read file `../execute.md`.

77
run.py
View file

@ -1,38 +1,16 @@
import glob
import pandas as pd
from src.io_tsp import ProblemInstance
from src.TSP_solver import TSPSolver, available_improvers, available_solvers
import numpy as np
def use_solver_to_compute_solution(solver, improve, index, results, name, verbose, show_plots):
#solver.bind(improve)
# solver.bind("2-opt")
# solver.bind("2.5-opt")
solver.compute_solution(return_value=False, verbose=verbose)
# solver.pop()
# solver.pop()
if verbose:
print(f"the total length for the solution found is {solver.found_length}",
f"while the optimal length is {solver.problem_instance.best_sol}",
f"the gap is {solver.gap}%",
f"the solution is found in {solver.duration} seconds", sep="\n")
index.append((name, solver.name_method))
results.append([solver.found_length, solver.problem_instance.best_sol, solver.gap, solver.duration])
if show_plots:
solver.plot_solution()
from aco.io_tsp import ProblemInstance
from aco.TSP_solver import TSPSolver
import os
def run(show_plots=False, verbose=False):
os.system("rm -f " + " ".join(glob.glob("sol/*") + glob.glob("c_prob/*")))
os.system("c++ -O2 -lpthread --std=c++11 -o c_prob/aco aco.cc opt.cc")
problems = glob.glob('./problems/*.tsp')
# problems = ["./problems/fl1577.tsp"]
problems = ["./problems/fl1577.tsp"]
solvers_names = available_solvers.keys()
improvers_names = available_improvers.keys()
results = []
index = []
for problem_path in problems:
@ -42,35 +20,30 @@ def run(show_plots=False, verbose=False):
if show_plots:
prob_instance.plot_data()
solver = TSPSolver(prob_instance)
solution = solver.compute_solution(verbose=verbose)
if verbose:
print(f"the total length for the solution found is {solver.found_length}",
f"while the optimal length is {solver.problem_instance.best_sol}",
f"the gap is {solver.gap}%",
f"the solution is found in {solver.duration} seconds", sep="\n")
index.append((problem_path, "'C++ ant colony optimization'"))
results.append([solver.found_length,
solver.problem_instance.best_sol,
solver.gap,
solver.duration])
for solver_name in solvers_names:
solver = TSPSolver(solver_name, prob_instance)
use_solver_to_compute_solution(solver, None, index, results, problem_path, verbose, show_plots)
# for improve in improvers_names:
# solver = TSPSolver(solver_name, prob_instance)
# use_solver_to_compute_solution(solver, improve, index, results, problem_path, verbose, show_plots)
# for improve2 in [j for j in improvers_names if j not in [improve]]:
# use_solver_to_compute_solution(solver, improve2, index, results, problem_path, verbose, show_plots)
#
# for improve3 in [j for j in improvers_names if j not in [improve, improve2]]:
# use_solver_to_compute_solution(solver, improve3, index, results, problem_path, verbose,
# show_plots)
# solver.pop()
#
# solver.pop()
with open("sol/" + prob_instance.name + ".sol", "w") as f:
print(solution, file=f)
if prob_instance.exist_opt and show_plots:
solver = TSPSolver("optimal", prob_instance)
solver.solved = True
solver.solution = np.concatenate([prob_instance.optimal_tour, [prob_instance.optimal_tour[0]]])
if show_plots:
solver.plot_solution()
#index = pd.MultiIndex.from_tuples(index, names=['problem', 'method'])
#return None
return pd.DataFrame(results, index=index, columns=["tour length", "optimal solution", "gap", "time to solve"])
return pd.DataFrame(results, index=index,
columns=["tour length", "optimal solution",
"gap", "time to solve"])
if __name__ == '__main__':
df = run(show_plots=True, verbose=True)
df = run(show_plots=False, verbose=True)
df.to_csv("./results.csv")

1
sol/fl1577.sol Normal file

File diff suppressed because one or more lines are too long

View file

@ -1,126 +0,0 @@
from time import time as t
import numpy as np
import matplotlib.pyplot as plt
from src.two_opt import loop2opt
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
from src.ant_colony import ant_colony_opt
# available_solvers = {"random": random_method,
# "nearest_neighbors": nearest_neighbor,
# "best_nn": best_nearest_neighbor,
# "multi_fragment": multi_fragment_mf
# }
available_improvers = {"2-opt": loop2opt,
"2.5-opt": loop2dot5opt,
"simulated_annealing": sa}
# available_solvers = {}
# for i in range(1, 10):
# for j in range(1, 10):
# available_solvers["aco_" + str(i) + "_" + str(j)] = ant_colony_opt(i/10, j)
available_solvers = {"aco": ant_colony_opt}
class TSPSolver:
def __init__(self, algorithm_name, problem_instance, passed_avail_solvers=None, passed_avail_improvers=None):
# assert algorithm_name in available_solvers, f"the {algorithm_name} initializer is not available currently."
if passed_avail_improvers is None:
passed_avail_improvers = available_improvers
if passed_avail_solvers is None:
passed_avail_solvers = available_solvers
self.available_improvers = passed_avail_improvers
self.available_solvers = passed_avail_solvers
self.duration = np.inf
self.found_length = np.inf
self.algorithm_name = algorithm_name
self.algorithms = [algorithm_name]
self.name_method = "initialized with " + algorithm_name
self.solved = False
self.problem_instance = problem_instance
self.solution = None
def bind(self, local_or_meta):
assert local_or_meta in self.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.algorithms.pop()
self.name_method = self.name_method[::-1][self.name_method[::-1].find("improved"[::-1]) + len("improved") + 2:][
::-1]
def compute_solution(self, verbose=True, return_value=True):
self.solved = False
if verbose:
print(f"### solving with {self.algorithms} ####")
start_time = t()
self.solution = self.available_solvers[self.algorithms[0]](self.problem_instance)
if not self.check_if_solution_is_valid():
print(f"Error the solution of {self.algorithm_name} for problem {self.problem_instance.name} is not valid")
if return_value:
return False
for i in range(1, len(self.algorithms)):
improver = self.algorithms[i]
self.solution = self.available_improvers[improver](self.solution, self.problem_instance)
if not self.check_if_solution_is_valid():
print(
f"Error the solution of {self.algorithm_name} with {improver} for problem {self.problem_instance.name} is not valid")
if return_value:
return False
end_time = t()
self.duration = np.around(end_time - start_time, 3)
self.solved = True
self.evaluate_solution()
self._gap()
if return_value:
return self.solution
def plot_solution(self):
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.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):
rights_values = np.sum(
[self.check_validation(i, self.solution[:-1]) for i in np.arange(self.problem_instance.nPoints)])
# rights_values = np.sum(
# [1 if np.sum(self.solution[:-1] == i) == 1 else 0 for i in np.arange(self.problem_instance.nPoints)])
return rights_values == self.problem_instance.nPoints
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.problem_instance.dist_matrix[from_node, node]
from_node = node
self.found_length = total_length
if return_value:
return total_length
def pass_and_check_if_solution_is_valid(self, solution):
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
def _gap(self):
self.evaluate_solution(return_value=False)
self.gap = np.round(
((self.found_length - self.problem_instance.best_sol) / self.problem_instance.best_sol) * 100, 2)

View file

@ -1,129 +0,0 @@
import numpy as np
from src.utils import compute_length
def random_method(instance_):
n = int(instance_.nPoints)
solution = np.random.choice(np.arange(n), size=n, replace=False)
return np.concatenate([solution, [solution[0]]])
def nearest_neighbor(instance_, starting_node=0):
dist_matrix = np.copy(instance_.dist_matrix)
n = int(instance_.nPoints)
node = starting_node
tour = [node]
for _ in range(n - 1):
for new_node in np.argsort(dist_matrix[node]):
if new_node not in tour:
tour.append(new_node)
node = new_node
break
tour.append(starting_node)
return np.array(tour)
def best_nearest_neighbor(instance_):
solutions, lengths = [], []
for start in range(instance_.nPoints):
new_solution = nearest_neighbor(instance_, starting_node=start)
solutions.append(new_solution)
lengths.append(compute_length(new_solution, instance_.dist_matrix))
if lengths is []:
return None
else:
solution = solutions[np.argmin(lengths)]
return solution
def multi_fragment_check_if_available(n1, n2, sol):
if len(sol[str(n1)]) < 2 and len(sol[str(n2)]) < 2:
return True
else:
return False
def multi_fragment_check_if_not_close(edge_to_append, sol):
n1, n2 = edge_to_append
from_city = n2
if len(sol[str(from_city)]) == 0:
return True
partial_tour = [from_city]
end = False
iteration = 0
while not end:
if len(sol[str(from_city)]) == 1:
if from_city == n1:
return_value = False
end = True
elif iteration > 1:
# print(f"iterazione {iteration}, elementi dentro partial {len(partial_tour)}",
# f"from city {from_city}")
return_value = True
end = True
else:
from_city = sol[str(from_city)][0]
partial_tour.append(from_city)
iteration += 1
else:
# print(from_city, partial_tour, sol[str(from_city)])
for node_connected in sol[str(from_city)]:
# print(node_connected)
if node_connected not in partial_tour:
from_city = node_connected
partial_tour.append(node_connected)
# print(node_connected, sol[str(from_city)])
iteration += 1
return return_value
def multi_fragment_create_solution(start_sol, sol, n):
assert len(start_sol) == 2, "too many cities with just one link"
end = False
n1, n2 = start_sol
from_city = n2
sol_list = [n1, n2]
iteration = 0
while not end:
for node_connected in sol[str(from_city)]:
iteration += 1
if node_connected not in sol_list:
from_city = node_connected
sol_list.append(node_connected)
# print(f"prossimo {node_connected}",
# f"possibili {sol[str(from_city)]}",
# f"ultim tour {sol_list[-5:]}")
if iteration > 300:
if len(sol_list) == n:
end = True
sol_list.append(n1)
return sol_list
def multi_fragment_mf(instance):
mat = np.copy(instance.dist_matrix)
mat = np.triu(mat)
mat[mat == 0] = 100000
solution = {str(i): [] for i in range(instance.nPoints)}
start_list = [i for i in range(instance.nPoints)]
inside = 0
for el in np.argsort(mat.flatten()):
node1, node2 = el // instance.nPoints, el % instance.nPoints
possible_edge = [node1, node2]
if multi_fragment_check_if_available(node1, node2,
solution):
if multi_fragment_check_if_not_close(possible_edge, solution):
# print("entrato", inside)
solution[str(node1)].append(node2)
solution[str(node2)].append(node1)
if len(solution[str(node1)]) == 2:
start_list.remove(node1)
if len(solution[str(node2)]) == 2:
start_list.remove(node2)
inside += 1
# print(node1, node2, inside)
if inside == instance.nPoints - 1:
# print(f"ricostruire la solutione da {start_list}",
# f"vicini di questi due nodi {[solution[str(i)] for i in start_list]}")
solution = multi_fragment_create_solution(start_list, solution, instance.nPoints)
return solution

View file

@ -1,4 +0,0 @@
class IteratedLocalSearch:
def __call__(self):
pass

View file

@ -1,52 +0,0 @@
import numpy as np
from src.utils import compute_length
def sa(solution, instance, constant_temperature=0.95, iterations_for_each_temp=100):
# initial setup
temperature = instance.best_sol / np.sqrt(instance.nPoints)
current_sol = np.array(solution)
current_len = compute_length(solution, instance.dist_matrix)
best_sol = np.array(solution)
best_len = current_len
# main loop
while temperature > 0.001:
for it in range(iterations_for_each_temp):
next_sol, delta_E = random_sol_from_neigh(current_sol, instance)
if delta_E < 0:
current_sol = next_sol
current_len += delta_E
if current_len < best_len:
best_sol = current_sol
best_len = current_len
else:
r = np.random.uniform(0, 1)
if r < np.exp(- delta_E / temperature):
current_sol = next_sol
current_len += delta_E
temperature *= constant_temperature
return best_sol.tolist()
def random_sol_from_neigh(solution, instance):
i, j = np.random.choice(np.arange(1, len(solution) - 1), 2, replace=False)
i, j = np.sort([i, j])
return sa_swap2opt(solution, i, j), gain(i, j, solution, instance.dist_matrix)
def sa_swap2opt(tsp_sequence, i, j):
new_tsp_sequence = np.copy(tsp_sequence)
new_tsp_sequence[i:j + 1] = np.flip(tsp_sequence[i:j + 1], axis=0) # flip or swap ?
return new_tsp_sequence
def gain(i, j, tsp_sequence, matrix_dist):
old_link_len = (matrix_dist[tsp_sequence[i], tsp_sequence[i - 1]] + matrix_dist[
tsp_sequence[j], tsp_sequence[j + 1]])
changed_links_len = (matrix_dist[tsp_sequence[j], tsp_sequence[i - 1]] + matrix_dist[
tsp_sequence[i], tsp_sequence[j + 1]])
return - old_link_len + changed_links_len

View file

@ -1,80 +0,0 @@
import numpy as np
from src.utils import compute_length
from src.two_opt import swap2opt, gain
def step2dot5opt(solution, matrix_dist, distance):
seq_length = len(solution) - 2
tsp_sequence = np.array(solution)
uncrosses = 0
for i in range(1, seq_length - 1):
for j in range(i + 1, seq_length):
# 2opt swap
two_opt_tsp_sequence = swap2opt(tsp_sequence, i, j)
two_opt_len = distance + gain(i, j, tsp_sequence, matrix_dist)
# node shift 1
first_shift_tsp_sequence = shift1(tsp_sequence, i, j)
first_shift_len = distance + shift_gain1(i, j, tsp_sequence, matrix_dist)
# node shift 2
second_shift_tsp_sequence = shift2(tsp_sequence, i, j)
second_shift_len = distance + shift_gain2(i, j, tsp_sequence, matrix_dist)
best_len, best_method = min([two_opt_len, first_shift_len, second_shift_len]), np.argmin(
[two_opt_len, first_shift_len, second_shift_len])
sequences = [two_opt_tsp_sequence, first_shift_tsp_sequence, second_shift_tsp_sequence]
if best_len < distance:
uncrosses += 1
tsp_sequence = sequences[best_method]
distance = best_len
print(i, j, best_len)
#print(distance, best_method, [twoOpt_len, first_shift_len, second_shift_len])
return tsp_sequence, distance, uncrosses
def shift1(tsp_sequence, i, j):
new_tsp_sequence = np.concatenate(
[tsp_sequence[:i], tsp_sequence[i + 1: j + 1], [tsp_sequence[i]], tsp_sequence[j + 1:]])
return new_tsp_sequence
def shift_gain1(i, j, tsp_sequence, matrix_dist):
old_link_len = (matrix_dist[tsp_sequence[i], tsp_sequence[i - 1]] +
matrix_dist[tsp_sequence[i], tsp_sequence[i + 1]] +
matrix_dist[tsp_sequence[j], tsp_sequence[j + 1]])
changed_links_len = (matrix_dist[tsp_sequence[i - 1], tsp_sequence[i + 1]] +
matrix_dist[tsp_sequence[i], tsp_sequence[j]]
+ matrix_dist[tsp_sequence[i], tsp_sequence[j + 1]])
return - old_link_len + changed_links_len
def shift2(tsp_sequence, i, j):
new_tsp_sequence = np.concatenate(
[tsp_sequence[:i], [tsp_sequence[j]], tsp_sequence[i: j], tsp_sequence[j + 1:]])
return new_tsp_sequence
def shift_gain2(i, j, tsp_sequence, matrix_dist):
old_link_len = (matrix_dist[tsp_sequence[i], tsp_sequence[i - 1]] + matrix_dist[
tsp_sequence[j], tsp_sequence[j - 1]] + matrix_dist[tsp_sequence[j], tsp_sequence[j + 1]])
changed_links_len = (
matrix_dist[tsp_sequence[j], tsp_sequence[i - 1]] + matrix_dist[tsp_sequence[i], tsp_sequence[j]] +
matrix_dist[tsp_sequence[j - 1], tsp_sequence[j + 1]])
return - old_link_len + changed_links_len
def loop2dot5opt(solution, instance, max_num_of_changes=2500):
matrix_dist = instance.dist_matrix
actual_len = compute_length(solution, matrix_dist)
new_tsp_sequence = np.copy(np.array(solution))
uncross = 0
while uncross < max_num_of_changes:
new_tsp_sequence, new_len, uncr_ = step2dot5opt(new_tsp_sequence, matrix_dist, actual_len)
uncross += uncr_
# print(new_len, uncross)
if new_len < actual_len:
actual_len = new_len
else:
return new_tsp_sequence.tolist()
return new_tsp_sequence.tolist()

View file

@ -1,48 +0,0 @@
import numpy as np
from src.utils import compute_length
def step2opt(solution, matrix_dist, distance):
seq_length = len(solution) - 1
tsp_sequence = np.array(solution)
uncrosses = 0
for i in range(1, seq_length - 1):
for j in range(i + 1, seq_length):
new_tsp_sequence = swap2opt(tsp_sequence, i, j)
new_distance = distance + gain(i, j, tsp_sequence, matrix_dist)
if new_distance < distance:
uncrosses += 1
tsp_sequence = np.copy(new_tsp_sequence)
distance = new_distance
return tsp_sequence, distance, uncrosses
def swap2opt(tsp_sequence, i, j):
new_tsp_sequence = np.copy(tsp_sequence)
new_tsp_sequence[i:j + 1] = np.flip(tsp_sequence[i:j + 1], axis=0) # flip or swap ?
return new_tsp_sequence
def gain(i, j, tsp_sequence, matrix_dist):
old_link_len = (matrix_dist[tsp_sequence[i], tsp_sequence[i - 1]] + matrix_dist[
tsp_sequence[j], tsp_sequence[j + 1]])
changed_links_len = (matrix_dist[tsp_sequence[j], tsp_sequence[i - 1]] + matrix_dist[
tsp_sequence[i], tsp_sequence[j + 1]])
return - old_link_len + changed_links_len
def loop2opt(solution, instance, max_num_of_uncrosses=10000):
matrix_dist = instance.dist_matrix
new_len = compute_length(solution, matrix_dist)
new_tsp_sequence = np.copy(np.array(solution))
uncross = 0
while uncross < max_num_of_uncrosses:
new_tsp_sequence, new_reward, uncr_ = step2opt(new_tsp_sequence, matrix_dist, new_len)
uncross += uncr_
if new_reward < new_len:
new_len = new_reward
else:
return new_tsp_sequence.tolist()
# return new_tsp_sequence.tolist(), new_len, uncross
return new_tsp_sequence.tolist()

View file

@ -1,19 +0,0 @@
import numpy as np
def compute_length(solution, dist_matrix):
total_length = 0
starting_node = solution[0]
from_node = starting_node
for node in solution[1:]:
total_length += dist_matrix[from_node, node]
from_node = node
return total_length
def distance_euc(point_i, point_j):
rounding = 0
x_i, y_i = point_i[0], point_i[1]
x_j, y_j = point_j[0], point_j[1]
distance = np.sqrt((x_i - x_j) ** 2 + (y_i - y_j) ** 2)
return round(distance, rounding)

12
sysinfo.md Normal file
View file

@ -0,0 +1,12 @@
# System information
Here is a summary of the system characteristics of the machine used to measure the execution time and
execution results for my AI cup submission:
| Characteristic | Value |
| :------------- | :----------: |
| OS Version | MacOS Catalina 10.15.7 |
| Kernel | Mach 19.6.0 |
| Compiler used | Apple clang version 12.0.0 (clang-1200.0.32.21) |
| Compiler optimization level | `-O2` |
| CPU | Intel i7-8750H (12) @ 2.20GHz |