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

View file

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

View file

@ -1,139 +1,131 @@
import os
import numpy as np import numpy as np
if 'AI' in os.getcwd():
from src.utils import * from src.utils import compute_length
else:
from AI2019.src.utils import *
class random_initialier: def random_method(instance_):
@staticmethod n = int(instance_.nPoints)
def random_method(instance_): solution = np.random.choice(np.arange(n), size=n, replace=False)
n = int(instance_.nPoints) return np.concatenate([solution, [solution[0]]])
solution = np.random.choice(np.arange(n), size=n, replace=False)
return np.concatenate([solution, [solution[0]]])
class nearest_neighbor: def nearest_neighbor(instance_, starting_node=0):
@staticmethod dist_matrix = np.copy(instance_.dist_matrix)
def nn(instance_, starting_node=0): n = int(instance_.nPoints)
dist_matrix = np.copy(instance_.dist_matrix) node = starting_node
n = int(instance_.nPoints) tour = [node]
node = starting_node for _ in range(n - 1):
tour = [node] for new_node in np.argsort(dist_matrix[node]):
for _ in range(n - 1): if new_node not in tour:
for new_node in np.argsort(dist_matrix[node]): tour.append(new_node)
if new_node not in tour: node = new_node
tour.append(new_node) break
node = new_node tour.append(starting_node)
break return np.array(tour)
tour.append(starting_node)
return np.array(tour)
@staticmethod
def best_nn(instance_):
solutions, lens = [], []
for start in range(instance_.nPoints):
new_solution = nearest_neighbor.nn(instance_, starting_node=start)
solutions.append(new_solution)
lens.append(compute_length(new_solution, instance_.dist_matrix))
def best_nearest_neighbor(instance_):
solutions, lens = [], []
for start in range(instance_.nPoints):
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)] solution = solutions[np.argmin(lens)]
return solution return solution
class multi_fragment: 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_available(n1, n2, sol):
if len(sol[str(n1)]) < 2 and len(sol[str(n2)]) < 2:
return True
else:
return False
@staticmethod def multi_fragment_check_if_not_close(edge_to_append, sol):
def check_if_not_close(edge_to_append, sol): n1, n2 = edge_to_append
n1, n2 = edge_to_append from_city = n2
from_city = n2 if len(sol[str(from_city)]) == 0:
if len(sol[str(from_city)]) == 0: return True
return True partial_tour = [from_city]
partial_tour = [from_city] end = False
end = False iterazione = 0
iterazione = 0 while not end:
while not end: if len(sol[str(from_city)]) == 1:
if len(sol[str(from_city)]) == 1: if from_city == n1:
if from_city == n1: return_value = False
return_value = False end = True
end = True elif iterazione > 1:
elif iterazione > 1: # print(f"iterazione {iterazione}, elementi dentro partial {len(partial_tour)}",
# print(f"iterazione {iterazione}, elementi dentro partial {len(partial_tour)}", # f"from city {from_city}")
# f"from city {from_city}") return_value = True
return_value = True end = True
end = True
else:
from_city = sol[str(from_city)][0]
partial_tour.append(from_city)
iterazione += 1
else: else:
# print(from_city, partial_tour, sol[str(from_city)]) from_city = sol[str(from_city)][0]
for node_connected in sol[str(from_city)]: partial_tour.append(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)])
iterazione += 1
return return_value
@staticmethod
def 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]
iterazione = 0
while not end:
for node_connected in sol[str(from_city)]:
iterazione += 1 iterazione += 1
if node_connected not in sol_list: 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 from_city = node_connected
sol_list.append(node_connected) partial_tour.append(node_connected)
# print(f"prossimo {node_connected}", # print(node_connected, sol[str(from_city)])
# f"possibili {sol[str(from_city)]}", iterazione += 1
# f"ultim tour {sol_list[-5:]}") return return_value
if iterazione > 300:
if len(sol_list) == n:
end = True
sol_list.append(n1)
return sol_list
@staticmethod
def 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
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]
iterazione = 0
while not end:
for node_connected in sol[str(from_city)]:
iterazione += 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 iterazione > 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

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