second pass of refactor

This commit is contained in:
Dario Mantegazza 2020-09-25 13:20:28 +02:00
parent 0eabc7fb79
commit dfd74836ed
12 changed files with 332 additions and 333 deletions

21
.gitignore vendored Normal file
View file

@ -0,0 +1,21 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/../../../../:\code\AI2020BsC\.idea/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/
/AI2020BsC.iml
/misc.xml
/modules.xml
/other.xml
/inspectionProfiles/profiles_settings.xml
/vcs.xml
/.idea/AI2020BsC.iml
/.idea/misc.xml
/.idea/modules.xml
/.idea/other.xml
/.idea/inspectionProfiles/profiles_settings.xml
/.idea/vcs.xml
/.idea/workspace.xml

View file

@ -1,26 +1,24 @@
from numpy.core._multiarray_umath import ndarray
import os
from time import time as t
import numpy as np
import matplotlib.pyplot as plt
if 'AI' in os.getcwd():
from src import *
else:
from AI2019.src import *
from src.local_search import TwoOpt_loop2opt, TwoDotFiveOpt_loop2dot5opt
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_initialier.random_method,
"nearest_neighbors": nearest_neighbor.nn,
"best_nn": nearest_neighbor.best_nn,
"multi_fragment": multi_fragment.mf
available_initializers = {"random": random_method,
"nearest_neighbors": nearest_neighbor,
"best_nn": best_nearest_neighbor,
"multi_fragment": multi_fragment_mf
}
available_improvements = {"2-opt": TwoOpt.loop2opt,
"2.5-opt": TwoDotFiveOpt.loop2dot5opt,
available_improvements = {"2-opt": TwoOpt_loop2opt,
"2.5-opt": TwoDotFiveOpt_loop2dot5opt,
"simulated_annealing": Simulated_Annealing.sa}
# ,

View file

@ -4,7 +4,7 @@ if 'AI' in os.getcwd():
from src.utils import *
from src.constructive_algorithms import *
from src.local_search import *
from src.meta_heuristics import *
from src.iterated_local_search import *
from src.TSP_solver import *
from src.io_tsp import *

View file

@ -1,22 +1,15 @@
import os
import numpy as np
if 'AI' in os.getcwd():
from src.utils import *
else:
from AI2019.src.utils import *
from src.utils import compute_length
class random_initialier:
@staticmethod
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]]])
class nearest_neighbor:
@staticmethod
def nn(instance_, starting_node=0):
def nearest_neighbor(instance_, starting_node=0):
dist_matrix = np.copy(instance_.dist_matrix)
n = int(instance_.nPoints)
node = starting_node
@ -30,29 +23,29 @@ class nearest_neighbor:
tour.append(starting_node)
return np.array(tour)
@staticmethod
def best_nn(instance_):
def best_nearest_neighbor(instance_):
solutions, lens = [], []
for start in range(instance_.nPoints):
new_solution = nearest_neighbor.nn(instance_, starting_node=start)
new_solution = nearest_neighbor(instance_, starting_node=start)
solutions.append(new_solution)
lens.append(compute_length(new_solution, instance_.dist_matrix))
if lens is []:
return None # Todo understand this return
else:
solution = solutions[np.argmin(lens)]
return solution
class multi_fragment:
@staticmethod
def check_if_available(n1, n2, sol):
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
@staticmethod
def check_if_not_close(edge_to_append, sol):
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:
@ -85,8 +78,8 @@ class multi_fragment:
iterazione += 1
return return_value
@staticmethod
def create_solution(start_sol, sol, n):
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
@ -108,8 +101,8 @@ class multi_fragment:
sol_list.append(n1)
return sol_list
@staticmethod
def mf(instance):
def multi_fragment_mf(instance):
mat = np.copy(instance.dist_matrix)
mat = np.triu(mat)
mat[mat == 0] = 100000
@ -119,9 +112,9 @@ class multi_fragment:
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,
if multi_fragment_check_if_available(node1, node2,
solution):
if multi_fragment.check_if_not_close(possible_edge, solution):
if multi_fragment_check_if_not_close(possible_edge, solution):
# print("entrato", inside)
solution[str(node1)].append(node2)
solution[str(node2)].append(node1)
@ -134,6 +127,5 @@ class multi_fragment:
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)
solution = multi_fragment_create_solution(start_list, solution, instance.nPoints)
return solution

View file

@ -17,6 +17,7 @@ class Instance:
def __init__(self, name_tsp):
self.read_instance(name_tsp)
self.exist_opt = None # TODO determine default value
self.optimal_tour = None
def read_instance(self, name_tsp):
# read raw data
@ -66,8 +67,6 @@ class Instance:
plt.annotate(txt, (self.points[i, 1], self.points[i, 2]))
plt.show()
def create_dist_matrix(self):
self.dist_matrix = np.zeros((self.nPoints, self.nPoints))
@ -75,4 +74,3 @@ class Instance:
for j in range(i, self.nPoints):
self.dist_matrix[i, j] = distance_euc(self.points[i][1:3], self.points[j][1:3])
self.dist_matrix += self.dist_matrix.T

View file

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

View file

@ -1,134 +1,3 @@
import os
import numpy as np
if 'AI' in os.getcwd():
from src.utils import *
else:
from AI2019.src.utils import *
class TwoOpt:
@staticmethod
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 = TwoOpt.swap2opt(tsp_sequence, i, j)
new_distance = distance + TwoOpt.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
@staticmethod
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
@staticmethod
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
@staticmethod
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_ = TwoOpt.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()
class TwoDotFiveOpt:
@staticmethod
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
twoOpt_tsp_sequence = TwoOpt.swap2opt(tsp_sequence, i, j)
twoOpt_len = distance + TwoOpt.gain(i, j, tsp_sequence, matrix_dist)
# node shift 1
first_shift_tsp_sequence = TwoDotFiveOpt.shift1(tsp_sequence, i, j)
first_shift_len = distance + TwoDotFiveOpt.shift_gain1(i, j, tsp_sequence, matrix_dist)
# node shift 2
second_shift_tsp_sequence = TwoDotFiveOpt.shift2(tsp_sequence, i, j)
second_shift_len = distance + TwoDotFiveOpt.shift_gain2(i, j, tsp_sequence, matrix_dist)
best_len, best_method = min([twoOpt_len, first_shift_len, second_shift_len]), np.argmin(
[twoOpt_len, first_shift_len, second_shift_len])
sequences = [twoOpt_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(distance, best_method, [twoOpt_len, first_shift_len, second_shift_len])
return tsp_sequence, distance, uncrosses
@staticmethod
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
@staticmethod
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
@staticmethod
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
@staticmethod
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
@staticmethod
def loop2dot5opt(solution, instance, max_num_of_changes=10000):
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_ = TwoDotFiveOpt.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,66 +0,0 @@
import numpy as np
import os
if 'AI' in os.getcwd():
from src.utils import *
else:
from AI2019.src.utils import *
class Simulated_Annealing:
@staticmethod
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 = Simulated_Annealing.random_sol_from_neig(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()
@staticmethod
def random_sol_from_neig(solution, instance):
i, j = np.random.choice(np.arange(1, len(solution) - 1), 2, replace=False)
i, j = np.sort([i, j])
return Simulated_Annealing.swap2opt(solution, i, j), Simulated_Annealing.gain(i, j, solution,
instance.dist_matrix)
@staticmethod
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
@staticmethod
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
class Iterated_Local_Search:
def __call__(self):
pass

View file

@ -0,0 +1,52 @@
import numpy as np
from src 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_neig(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_neig(solution, instance):
i, j = np.random.choice(np.arange(1, len(solution) - 1), 2, replace=False)
i, j = np.sort([i, j])
return swap2opt(solution, i, j), gain(i, j, solution, instance.dist_matrix)
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

79
src/two_dot_five_opt.py Normal file
View file

@ -0,0 +1,79 @@
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
twoOpt_tsp_sequence = swap2opt(tsp_sequence, i, j)
twoOpt_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([twoOpt_len, first_shift_len, second_shift_len]), np.argmin(
[twoOpt_len, first_shift_len, second_shift_len])
sequences = [twoOpt_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(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=10000):
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()

49
src/two_opt.py Normal file
View file

@ -0,0 +1,49 @@
import numpy as np
from src 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,3 +1,6 @@
import numpy as np
def compute_length(solution, dist_matrix):
total_length = 0
starting_node = solution[0]