second pass of refactor
This commit is contained in:
parent
0eabc7fb79
commit
dfd74836ed
12 changed files with 332 additions and 333 deletions
21
.gitignore
vendored
Normal file
21
.gitignore
vendored
Normal 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
|
|
@ -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}
|
||||||
|
|
||||||
# ,
|
# ,
|
||||||
|
|
|
@ -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 *
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,15 @@
|
||||||
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
|
|
||||||
def random_method(instance_):
|
|
||||||
n = int(instance_.nPoints)
|
n = int(instance_.nPoints)
|
||||||
solution = np.random.choice(np.arange(n), size=n, replace=False)
|
solution = np.random.choice(np.arange(n), size=n, replace=False)
|
||||||
return np.concatenate([solution, [solution[0]]])
|
return np.concatenate([solution, [solution[0]]])
|
||||||
|
|
||||||
|
|
||||||
class nearest_neighbor:
|
def nearest_neighbor(instance_, starting_node=0):
|
||||||
@staticmethod
|
|
||||||
def nn(instance_, starting_node=0):
|
|
||||||
dist_matrix = np.copy(instance_.dist_matrix)
|
dist_matrix = np.copy(instance_.dist_matrix)
|
||||||
n = int(instance_.nPoints)
|
n = int(instance_.nPoints)
|
||||||
node = starting_node
|
node = starting_node
|
||||||
|
@ -30,29 +23,29 @@ class nearest_neighbor:
|
||||||
tour.append(starting_node)
|
tour.append(starting_node)
|
||||||
return np.array(tour)
|
return np.array(tour)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def best_nn(instance_):
|
def best_nearest_neighbor(instance_):
|
||||||
solutions, lens = [], []
|
solutions, lens = [], []
|
||||||
for start in range(instance_.nPoints):
|
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)
|
solutions.append(new_solution)
|
||||||
lens.append(compute_length(new_solution, instance_.dist_matrix))
|
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):
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def check_if_available(n1, n2, sol):
|
|
||||||
if len(sol[str(n1)]) < 2 and len(sol[str(n2)]) < 2:
|
if len(sol[str(n1)]) < 2 and len(sol[str(n2)]) < 2:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
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
|
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:
|
||||||
|
@ -85,8 +78,8 @@ class multi_fragment:
|
||||||
iterazione += 1
|
iterazione += 1
|
||||||
return return_value
|
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"
|
assert len(start_sol) == 2, "too many cities with just one link"
|
||||||
end = False
|
end = False
|
||||||
n1, n2 = start_sol
|
n1, n2 = start_sol
|
||||||
|
@ -108,8 +101,8 @@ class multi_fragment:
|
||||||
sol_list.append(n1)
|
sol_list.append(n1)
|
||||||
return sol_list
|
return sol_list
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def mf(instance):
|
def multi_fragment_mf(instance):
|
||||||
mat = np.copy(instance.dist_matrix)
|
mat = np.copy(instance.dist_matrix)
|
||||||
mat = np.triu(mat)
|
mat = np.triu(mat)
|
||||||
mat[mat == 0] = 100000
|
mat[mat == 0] = 100000
|
||||||
|
@ -119,9 +112,9 @@ class multi_fragment:
|
||||||
for el in np.argsort(mat.flatten()):
|
for el in np.argsort(mat.flatten()):
|
||||||
node1, node2 = el // instance.nPoints, el % instance.nPoints
|
node1, node2 = el // instance.nPoints, el % instance.nPoints
|
||||||
possible_edge = [node1, node2]
|
possible_edge = [node1, node2]
|
||||||
if multi_fragment.check_if_available(node1, node2,
|
if multi_fragment_check_if_available(node1, node2,
|
||||||
solution):
|
solution):
|
||||||
if multi_fragment.check_if_not_close(possible_edge, solution):
|
if multi_fragment_check_if_not_close(possible_edge, solution):
|
||||||
# print("entrato", inside)
|
# print("entrato", inside)
|
||||||
solution[str(node1)].append(node2)
|
solution[str(node1)].append(node2)
|
||||||
solution[str(node2)].append(node1)
|
solution[str(node2)].append(node1)
|
||||||
|
@ -134,6 +127,5 @@ class multi_fragment:
|
||||||
if inside == instance.nPoints - 1:
|
if inside == instance.nPoints - 1:
|
||||||
# print(f"ricostruire la solutione da {start_list}",
|
# print(f"ricostruire la solutione da {start_list}",
|
||||||
# f"vicini di questi due nodi {[solution[str(i)] for i in 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
|
return solution
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
4
src/iterated_local_search.py
Normal file
4
src/iterated_local_search.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
class Iterated_Local_Search:
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
pass
|
|
@ -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()
|
|
|
@ -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
|
|
52
src/simulated_annealing.py
Normal file
52
src/simulated_annealing.py
Normal 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
79
src/two_dot_five_opt.py
Normal 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
49
src/two_opt.py
Normal 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()
|
|
@ -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]
|
||||||
|
|
Reference in a new issue