├── README.md ├── solvers ├── dwave_tsp.py ├── bruteforce.py ├── nearest.py └── tsp_genetico.py ├── main.py └── tsp_utilities.py /README.md: -------------------------------------------------------------------------------- 1 | # tsp_mayhem -------------------------------------------------------------------------------- /solvers/dwave_tsp.py: -------------------------------------------------------------------------------- 1 | 2 | import dwave_networkx as dnx 3 | import dimod 4 | 5 | class Dwave_tsp: 6 | 7 | def calculate(self, G, cost_matrix, starting_node): 8 | 9 | route = [] 10 | for i in list(G): 11 | route.append(i) 12 | 13 | return route 14 | 15 | sapi_token = '' 16 | dwave_url = 'https://cloud.dwavesys.com/sapi' 17 | 18 | #nx.draw_networkx(G, node_color=colors, node_size=400, alpha=.8, ax=default_axes, pos=pos) 19 | # Test with https://docs.ocean.dwavesys.com/en/latest/docs_dnx/reference/algorithms/tsp.html 20 | route = dnx.traveling_salesperson(G, dimod.ExactSolver(), start=starting_node) 21 | 22 | return route 23 | -------------------------------------------------------------------------------- /solvers/bruteforce.py: -------------------------------------------------------------------------------- 1 | from itertools import permutations 2 | 3 | class Bruteforce: 4 | 5 | def calculate(self, G, cost_matrix, starting_node): 6 | 7 | n = len(list(G)) 8 | 9 | a = list(permutations(range(1,n))) 10 | last_best_distance = 1e10 11 | for i in a: 12 | distance = 0 13 | pre_j = 0 14 | for j in i: 15 | distance = distance + cost_matrix[j,pre_j] 16 | pre_j = j 17 | distance = distance + cost_matrix[pre_j,0] 18 | order = (0,) + i 19 | if distance < last_best_distance: 20 | best_order = order 21 | last_best_distance = distance 22 | print('order = ' + str(order) + ' Distance = ' + str(distance)) 23 | return list(best_order) 24 | -------------------------------------------------------------------------------- /solvers/nearest.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | class Nearest: 4 | 5 | def calculate(self, G, cost_matrix, starting_node): 6 | 7 | n = len(list(G)) 8 | cost_matrix = self.check_matrix(cost_matrix) 9 | 10 | solution = [starting_node] 11 | 12 | while len(solution) != n: 13 | dist = np.inf 14 | for indx in range(n): 15 | if cost_matrix[solution[-1]][indx] < dist and indx not in solution: 16 | nearest = indx 17 | dist = cost_matrix[solution[-1]][indx] 18 | solution.append(nearest) 19 | 20 | return solution 21 | 22 | def check_matrix(self, matrix): 23 | for i in range(len(matrix)): 24 | for j in range(len(matrix)): 25 | if matrix[i][j] == 0: 26 | matrix[i][j] = np.inf 27 | return matrix 28 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import time 2 | import importlib 3 | 4 | from tsp_utilities import * 5 | 6 | ############################################## 7 | ## ADD HERE YOUR NEW SOLVERS CLASSES ######### 8 | ############################################## 9 | active_solvers = ["Bruteforce", 10 | #"Dwave_tsp", 11 | "TSP_genetico"] 12 | ############################################## 13 | ############################################## 14 | 15 | def main(): 16 | 17 | starting_node = 0 18 | nodes = 5 19 | 20 | G = get_graph(nodes) 21 | cost_matrix = get_cost_matrix(G, nodes) 22 | 23 | for solver_ in active_solvers: 24 | ClassName = getattr(importlib.import_module("solvers."+solver_.lower()), solver_) 25 | instance = ClassName() 26 | route = instance.calculate(G, cost_matrix, starting_node) 27 | print("Route for %s:" % solver_) 28 | print(route) 29 | print("Cost: %s" % calculate_cost(cost_matrix, route)) 30 | draw_tsp_solution(G, route) 31 | 32 | 33 | if __name__ == '__main__': 34 | main() 35 | 36 | -------------------------------------------------------------------------------- /tsp_utilities.py: -------------------------------------------------------------------------------- 1 | 2 | import networkx as nx 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | import matplotlib.axes as axes 6 | 7 | def get_graph(nodes): 8 | 9 | G = nx.Graph() 10 | G.add_nodes_from(np.arange(0, nodes, 1)) 11 | # TODO Add edges randomly 12 | elist = {(0,1,1.0), 13 | (0,2,1.0), 14 | (0,3,1.0), 15 | (1,2,1.0), 16 | (2,3,1.0), 17 | (3,4,2.0), 18 | (0,4,1.0), 19 | (3,4,1.5), 20 | (2,4,1.0)} 21 | 22 | # tuple is (i,j,weight) where (i,j) is the edge 23 | G.add_weighted_edges_from(elist) 24 | #G.add_weighted_edges_from({(0, 1, .1), (0, 2, .5), (0, 3, .1), (1, 2, .1), (1, 3, .5), (2, 3, .1)}) 25 | 26 | return G 27 | 28 | def get_cost_matrix(G, nodes): 29 | w = np.zeros([nodes,nodes]) 30 | for i in range(nodes): 31 | for j in range(nodes): 32 | temp = G.get_edge_data(i,j,default=0) 33 | if temp != 0: 34 | w[i,j] = temp['weight'] 35 | return w 36 | 37 | def calculate_cost(cost_matrix, solution): 38 | cost = 0 39 | for i in range(len(solution)): 40 | a = i % len(solution) 41 | b = (i + 1) % len(solution) 42 | cost += cost_matrix[solution[a]][solution[b]] 43 | 44 | return cost 45 | 46 | def draw_tsp_solution(G, order): 47 | 48 | colors = ['r' for node in G.nodes()] 49 | pos = nx.spring_layout(G) 50 | default_axes = plt.axes(frameon=True) 51 | 52 | G2 = G.copy() 53 | G2.remove_edges_from(list(G.edges)) 54 | n = len(order) 55 | for i in range(n-1): 56 | j = (i + 1) % n 57 | G2.add_edge(order[i], order[j]) 58 | 59 | 60 | nx.draw_networkx(G2, node_color=colors, node_size=600, alpha=.8, ax=default_axes, pos=pos) 61 | plt.show() 62 | -------------------------------------------------------------------------------- /solvers/tsp_genetico.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from numpy.random import permutation 3 | 4 | class TSP_genetico: 5 | 6 | def calculate(self, G, cost_matrix, starting_node): 7 | 8 | n = len(list(G)) 9 | 10 | cost_matrix = self.check_matrix(cost_matrix) 11 | 12 | prob_mutation = 0.2 13 | epochs = 10 14 | samples = 100 15 | n_top = samples // 2 16 | 17 | print(cost_matrix) 18 | for _ in range(epochs): 19 | 20 | population = self.generate_population(n, samples) 21 | 22 | 23 | select_top = self.select_population(population, n_top, cost_matrix) 24 | 25 | new_population = self.cross_population(select_top, samples) 26 | new_population = self.mutation(new_population, prob_mutation) 27 | 28 | cost_population = [self.cost_travel(person, cost_matrix) for person in new_population] 29 | return new_population[np.argmin(cost_population)] 30 | 31 | def cost_travel(self, travel, cost_matrix): 32 | 33 | n = len(travel) 34 | cost = 0 35 | previous = travel[0] 36 | for i in range(1,n): 37 | cost += cost_matrix[previous][travel[i]] 38 | previous = travel[i] 39 | 40 | cost += cost_matrix[travel[n - 1]][travel[0]] 41 | return cost 42 | 43 | def generate_population(self, n, samples): 44 | return [list(permutation(list(range(n)))) for _ in range(samples)] 45 | 46 | def select_population(self, population, n_top, cost_matrix): 47 | 48 | cost_population = [self.cost_travel(person, cost_matrix) for person in population] 49 | select_population = [] 50 | while len(select_population) != n_top: 51 | select_population.append(population[np.argmin(cost_population)]) 52 | cost_population[np.argmin(cost_population)] = np.inf 53 | 54 | return select_population 55 | 56 | def cross_population(self, parents, samples): 57 | new_population = [] 58 | for _ in range(samples): 59 | index = list(np.random.choice(list(range(len(parents))), size=2, replace=False)) 60 | two_parents = [parents[index[0]], parents[index[1]]] 61 | new_population.append(self.get_child(two_parents)) 62 | return new_population 63 | 64 | def get_child(self, parents): 65 | n = len(parents[0]) 66 | child = parents[0][:n // 2] 67 | for i in range(n): 68 | if not parents[1][i] in child: 69 | child.append(parents[1][i]) 70 | return child 71 | 72 | def check_matrix(self, matrix): 73 | for i in range(len(matrix)): 74 | for j in range(len(matrix)): 75 | if matrix[i][j] == 0: 76 | matrix[i][j] = np.inf 77 | return matrix 78 | 79 | def mutation(self, population, prob): 80 | for person in population: 81 | if np.random.rand() < prob: 82 | person[:len(person) // 2] = permutation(person[:len(person) // 2]) 83 | return population --------------------------------------------------------------------------------