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
|
||||
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}
|
||||
|
||||
# ,
|
||||
|
|
|
@ -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 *
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
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):
|
||||
total_length = 0
|
||||
starting_node = solution[0]
|
||||
|
|
Reference in a new issue