diff --git a/Christofides/Christofides.py~ b/Christofides/Christofides.py~ deleted file mode 100644 index 0bf94bb..0000000 --- a/Christofides/Christofides.py~ +++ /dev/null @@ -1,179 +0,0 @@ -from scipy.sparse import csr_matrix -from scipy.sparse.csgraph import minimum_spanning_tree -import numpy as np -from munkres import Munkres -import networkx as nx -import copy -import itertools -from operator import itemgetter -import graph - -def _csr_gen_triples(A): - """Converts a SciPy sparse matrix in **Compressed Sparse Row** format to - an iterable of weighted edge triples. - - """ - graph = nx.Graph() - nrows = A.shape[0] - data, indices, indptr = A.data, A.indices, A.indptr - for i in range(nrows): - for j in range(indptr[i], indptr[i+1]): - graph.add_edge(i,indices[j], weight = data[j]) - return graph.edges(data = 'weight') - -def _odd_vertices_of_MST(M, number_of_nodes): - """Returns the vertices having Odd degree in the Minimum Spanning Tree(MST). - - """ - odd_vertices = [0 for i in range(number_of_nodes)] - for u,v,d in M: - odd_vertices[u] = odd_vertices[u] + 1 - odd_vertices[v] = odd_vertices[v] + 1 - odd_vertices = [vertex for vertex, degree in enumerate(odd_vertices) if degree % 2 == 1] - return odd_vertices - -def min_Munkres(M, bipartite_graphs): - """Implements the Hungarian problem or the Assignment problem to - find Minimum Cost Perfect Matching(MCPM). - - """ - m = Munkres() - minimum = np.inf - for index,bipartite_graph in enumerate(bipartite_graphs[0]): - Munkres_indexes = m.compute(bipartite_graph) - cost = Munkres_cost(Munkres_indexes, bipartite_graph) - if cost < minimum: - minimum = cost - min_index = index - min_Munkres_indexes = Munkres_indexes - Munkres_indexes = [[] for i in range(len(min_Munkres_indexes))] - for index,vertex_set in enumerate(min_Munkres_indexes): - Munkres_indexes[index].append(bipartite_graphs[1][min_index][0][vertex_set[0]]) - Munkres_indexes[index].append(bipartite_graphs[1][min_index][1][vertex_set[1]]) - return Munkres_indexes - -def Munkres_cost(indexes, bipartite_graph): - """Returns cost of the edges in Munkres_indexes - - """ - cost = 0 - for index in indexes: - cost = cost + bipartite_graph[index[0]][index[1]] - return cost - -def bipartite_Graph(M, bipartite_set, odd_vertices): - """ - """ - bipartite_graphs = [] - vertex_sets = [] - for vertex_set1 in bipartite_set: - vertex_set1 = list(sorted(vertex_set1)) - vertex_set2 = [] - for vertex in odd_vertices: - if vertex not in vertex_set1: - vertex_set2.append(vertex) - matrix = [[np.inf for j in range(len(vertex_set2))] for i in range(len(vertex_set1))] - for i in range(len(vertex_set1)): - for j in range(len(vertex_set2)): - if vertex_set1[i] < vertex_set2[j]: - matrix[i][j] = M[vertex_set1[i]][vertex_set2[j]] - else: - matrix[i][j] = M[vertex_set2[j]][vertex_set1[i]] - bipartite_graphs.append(matrix) - vertex_sets.append([vertex_set1,vertex_set2]) - return [bipartite_graphs, vertex_sets] - -def create_Multigraph(M, MST, indexes, odd_vertices): - """Creates a MultiGraph consisting of vertices of both - MST and MCPM. - - """ - multigraph = nx.MultiGraph() - for u,v,d in MST: - multigraph.add_edge(u,v,weight=d) - for pair in indexes: - multigraph.add_edge(pair[0],pair[1],weight=M[pair[0]][pair[1]]) - return multigraph - -def Euler_Tour(multigraph): - """ Uses Fleury's algorithm to find the Euler Tour of the MultiGraph. - - """ - tour = [] - temp_graph = nx.MultiGraph() - graph_nodes = nx.nodes(multigraph) - current_node = graph_nodes[0] - tour.append(current_node) - while nx.number_of_edges(multigraph) > 0: - for edge in multigraph.edges(current_node): - temp_graph = copy.deepcopy(multigraph) - temp_graph.remove_edge(edge[0], edge[1], key=None) - if nx.is_connected(temp_graph): - tour.append(edge[1]) - current_node = edge[1] - multigraph.remove_edge(edge[0], edge[1], key=None) - break - else: - tour.append(edge[1]) - current_node = edge[1] - multigraph.remove_edge(edge[0], edge[1], key=None) - multigraph.remove_nodes_from(nx.isolates(multigraph)) - return tour - -def shortcut_Euler_Tour(tour): - """Find's the shortcut of the Euler Tour to obtain the Approximation. - - """ - Tour = [] - for vertex in tour: - if vertex not in Tour: - Tour.append(vertex) - Tour.append(tour[0]) - return Tour - -def cost(christofides_tour, M): - """Returns Cost of Tour. - - """ - Travel_Cost = 0 - christofides_tour = christofides_tour[0:len(christofides_tour)-1] - previous_vertex = christofides_tour[len(christofides_tour)-1] - for current_vertex in christofides_tour: - if previous_vertex > current_vertex: - Travel_Cost = Travel_Cost + M[current_vertex][previous_vertex] - else: - Travel_Cost = Travel_Cost + M[previous_vertex][current_vertex] - previous_vertex = current_vertex - return Travel_Cost - -def compute(M): - """Returns an Approximation for TSP using Christofide's algorithm by - directing several functions. - - """ - MST = _csr_gen_triples(minimum_spanning_tree(csr_matrix(M))) - odd_vertices = _odd_vertices_of_MST(MST, csr_matrix(M).shape[0]) - bipartite_set = [set(i) for i in itertools.combinations(set(odd_vertices), len(odd_vertices)/2)] - bipartite_graphs = bipartite_Graph(M, bipartite_set, odd_vertices) - indexes = min_Munkres(M, bipartite_graphs) - multigraph = create_Multigraph(M, MST, indexes, odd_vertices) - multiGraph = copy.deepcopy(multigraph) - euler_tour = Euler_Tour(multigraph) - Christofides_Solution = shortcut_Euler_Tour(euler_tour) - Travel_Cost = cost(Christofides_Solution, M) - return { - 'Christofides_Solution': Christofides_Solution, - 'Travel_Cost': Travel_Cost, - 'MST': MST, - 'Odd_Vertices': odd_vertices, - 'Indexes': indexes, - 'Multigraph': multiGraph.edges(data='weight'), - 'Euler_Tour': euler_tour - } - -if __name__ == "__main__": - distance_matrix = graph.distance_matrix - Approximation = compute(distance_matrix) - print '\n1.5 Approximation of TSP (Christofide\'s algorithm):', Approximation['Christofides_Solution'] - print 'Travel Cost:', Approximation['Travel_Cost'] - print '' diff --git a/Christofides/christofides.py b/Christofides/christofides.py index 9be63ba..80a8d8e 100644 --- a/Christofides/christofides.py +++ b/Christofides/christofides.py @@ -7,8 +7,6 @@ import networkx as nx import copy import itertools -from operator import itemgetter -import graph import time def _csr_gen_triples(A): @@ -21,7 +19,7 @@ def _csr_gen_triples(A): data, indices, indptr = A.data, A.indices, A.indptr for i in range(nrows): for j in range(indptr[i], indptr[i+1]): - graph.add_edge(i,indices[j], weight = data[j]) + graph.add_edge(i,indices[j], weight = data[j]) return graph.edges(data = 'weight') def _odd_vertices_of_MST(M, number_of_nodes): @@ -97,17 +95,16 @@ def create_Multigraph(M, MST, indexes, odd_vertices): for pair in indexes: multigraph.add_edge(pair[0],pair[1],weight=M[pair[0]][pair[1]]) return multigraph - + def Euler_Tour(multigraph): """ Uses Fleury's algorithm to find the Euler Tour of the MultiGraph. """ tour = [] temp_graph = nx.MultiGraph() - graph_nodes = nx.nodes(multigraph) - current_node = graph_nodes[0] + current_node = [n for n in nx.nodes(multigraph)][0] tour.append(current_node) - while nx.number_of_edges(multigraph) > 0: + while nx.number_of_edges(multigraph) > 0: for edge in multigraph.edges(current_node): temp_graph = copy.deepcopy(multigraph) temp_graph.remove_edge(edge[0], edge[1], key=None) @@ -117,10 +114,12 @@ def Euler_Tour(multigraph): multigraph.remove_edge(edge[0], edge[1], key=None) break else: + edge = [e for e in multigraph.edges(current_node)][-1] tour.append(edge[1]) current_node = edge[1] multigraph.remove_edge(edge[0], edge[1], key=None) - multigraph.remove_nodes_from(nx.isolates(multigraph)) + isolates = [n for n in nx.isolates(multigraph)] + multigraph.remove_nodes_from(isolates) return tour def shortcut_Euler_Tour(tour): @@ -156,7 +155,7 @@ def compute(M): """ MST = _csr_gen_triples(minimum_spanning_tree(csr_matrix(M))) odd_vertices = _odd_vertices_of_MST(MST, csr_matrix(M).shape[0]) - bipartite_set = [set(i) for i in itertools.combinations(set(odd_vertices), len(odd_vertices)/2)] + bipartite_set = [set(i) for i in itertools.combinations(set(odd_vertices), int(len(odd_vertices)/2))] bipartite_graphs = bipartite_Graph(M, bipartite_set, odd_vertices) indexes = min_Munkres(M, bipartite_graphs) multigraph = create_Multigraph(M, MST, indexes, odd_vertices) @@ -165,25 +164,26 @@ def compute(M): Christofides_Solution = shortcut_Euler_Tour(euler_tour) Travel_Cost = cost(Christofides_Solution, M) return { - 'Christofides_Solution': Christofides_Solution, - 'Travel_Cost': Travel_Cost, - 'MST': MST, - 'Odd_Vertices': odd_vertices, - 'Indexes': indexes, - 'Multigraph': multiGraph.edges(data='weight'), + 'Christofides_Solution': Christofides_Solution, + 'Travel_Cost': Travel_Cost, + 'MST': MST, + 'Odd_Vertices': odd_vertices, + 'Indexes': indexes, + 'Multigraph': multiGraph.edges(data='weight'), 'Euler_Tour': euler_tour } if __name__ == "__main__": - print 'Testing...' + print('Testing...') start = time.time() - distance_matrix = graph.distance_matrix + from graph import distance_matrix Approximation = compute(distance_matrix) end = time.time()-start - print 'Computation Successful...' - print 'Distance Matrix:\n' - print graph.distance_matrix - print '\n1.5 Approximation of TSP (Christofide\'s algorithm):\n', Approximation['Christofides_Solution'] - print 'Travel Cost:', Approximation['Travel_Cost'] - print 'Computation Time:', end - print '' + print('Computation Successful...') + print('Distance Matrix:\n') + print(distance_matrix) + print('\n1.5 Approximation of TSP (Christofide\'s algorithm):') + print(Approximation['Christofides_Solution']) + print('Travel Cost:'+str(Approximation['Travel_Cost'])) + print('Computation Time:'+str(end)) + print('') diff --git a/Christofides/christofides.py~ b/Christofides/christofides.py~ deleted file mode 100644 index 5265bca..0000000 --- a/Christofides/christofides.py~ +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/env python - -from scipy.sparse import csr_matrix -from scipy.sparse.csgraph import minimum_spanning_tree -import numpy as np -from munkres import Munkres -import networkx as nx -import copy -import itertools -from operator import itemgetter -import graph - -def _csr_gen_triples(A): - """Converts a SciPy sparse matrix in **Compressed Sparse Row** format to - an iterable of weighted edge triples. - - """ - graph = nx.Graph() - nrows = A.shape[0] - data, indices, indptr = A.data, A.indices, A.indptr - for i in range(nrows): - for j in range(indptr[i], indptr[i+1]): - graph.add_edge(i,indices[j], weight = data[j]) - return graph.edges(data = 'weight') - -def _odd_vertices_of_MST(M, number_of_nodes): - """Returns the vertices having Odd degree in the Minimum Spanning Tree(MST). - - """ - odd_vertices = [0 for i in range(number_of_nodes)] - for u,v,d in M: - odd_vertices[u] = odd_vertices[u] + 1 - odd_vertices[v] = odd_vertices[v] + 1 - odd_vertices = [vertex for vertex, degree in enumerate(odd_vertices) if degree % 2 == 1] - return odd_vertices - -def min_Munkres(M, bipartite_graphs): - """Implements the Hungarian problem or the Assignment problem to - find Minimum Cost Perfect Matching(MCPM). - - """ - m = Munkres() - minimum = np.inf - for index,bipartite_graph in enumerate(bipartite_graphs[0]): - Munkres_indexes = m.compute(bipartite_graph) - cost = Munkres_cost(Munkres_indexes, bipartite_graph) - if cost < minimum: - minimum = cost - min_index = index - min_Munkres_indexes = Munkres_indexes - Munkres_indexes = [[] for i in range(len(min_Munkres_indexes))] - for index,vertex_set in enumerate(min_Munkres_indexes): - Munkres_indexes[index].append(bipartite_graphs[1][min_index][0][vertex_set[0]]) - Munkres_indexes[index].append(bipartite_graphs[1][min_index][1][vertex_set[1]]) - return Munkres_indexes - -def Munkres_cost(indexes, bipartite_graph): - """Returns cost of the edges in Munkres_indexes - - """ - cost = 0 - for index in indexes: - cost = cost + bipartite_graph[index[0]][index[1]] - return cost - -def bipartite_Graph(M, bipartite_set, odd_vertices): - """ - """ - bipartite_graphs = [] - vertex_sets = [] - for vertex_set1 in bipartite_set: - vertex_set1 = list(sorted(vertex_set1)) - vertex_set2 = [] - for vertex in odd_vertices: - if vertex not in vertex_set1: - vertex_set2.append(vertex) - matrix = [[np.inf for j in range(len(vertex_set2))] for i in range(len(vertex_set1))] - for i in range(len(vertex_set1)): - for j in range(len(vertex_set2)): - if vertex_set1[i] < vertex_set2[j]: - matrix[i][j] = M[vertex_set1[i]][vertex_set2[j]] - else: - matrix[i][j] = M[vertex_set2[j]][vertex_set1[i]] - bipartite_graphs.append(matrix) - vertex_sets.append([vertex_set1,vertex_set2]) - return [bipartite_graphs, vertex_sets] - -def create_Multigraph(M, MST, indexes, odd_vertices): - """Creates a MultiGraph consisting of vertices of both - MST and MCPM. - - """ - multigraph = nx.MultiGraph() - for u,v,d in MST: - multigraph.add_edge(u,v,weight=d) - for pair in indexes: - multigraph.add_edge(pair[0],pair[1],weight=M[pair[0]][pair[1]]) - return multigraph - -def Euler_Tour(multigraph): - """ Uses Fleury's algorithm to find the Euler Tour of the MultiGraph. - - """ - tour = [] - temp_graph = nx.MultiGraph() - graph_nodes = nx.nodes(multigraph) - current_node = graph_nodes[0] - tour.append(current_node) - while nx.number_of_edges(multigraph) > 0: - for edge in multigraph.edges(current_node): - temp_graph = copy.deepcopy(multigraph) - temp_graph.remove_edge(edge[0], edge[1], key=None) - if nx.is_connected(temp_graph): - tour.append(edge[1]) - current_node = edge[1] - multigraph.remove_edge(edge[0], edge[1], key=None) - break - else: - tour.append(edge[1]) - current_node = edge[1] - multigraph.remove_edge(edge[0], edge[1], key=None) - multigraph.remove_nodes_from(nx.isolates(multigraph)) - return tour - -def shortcut_Euler_Tour(tour): - """Find's the shortcut of the Euler Tour to obtain the Approximation. - - """ - Tour = [] - for vertex in tour: - if vertex not in Tour: - Tour.append(vertex) - Tour.append(tour[0]) - return Tour - -def cost(christofides_tour, M): - """Returns Cost of Tour. - - """ - Travel_Cost = 0 - christofides_tour = christofides_tour[0:len(christofides_tour)-1] - previous_vertex = christofides_tour[len(christofides_tour)-1] - for current_vertex in christofides_tour: - if previous_vertex > current_vertex: - Travel_Cost = Travel_Cost + M[current_vertex][previous_vertex] - else: - Travel_Cost = Travel_Cost + M[previous_vertex][current_vertex] - previous_vertex = current_vertex - return Travel_Cost - -def compute(M): - """Returns an Approximation for TSP using Christofide's algorithm by - directing several functions. - - """ - MST = _csr_gen_triples(minimum_spanning_tree(csr_matrix(M))) - odd_vertices = _odd_vertices_of_MST(MST, csr_matrix(M).shape[0]) - bipartite_set = [set(i) for i in itertools.combinations(set(odd_vertices), len(odd_vertices)/2)] - bipartite_graphs = bipartite_Graph(M, bipartite_set, odd_vertices) - indexes = min_Munkres(M, bipartite_graphs) - multigraph = create_Multigraph(M, MST, indexes, odd_vertices) - multiGraph = copy.deepcopy(multigraph) - euler_tour = Euler_Tour(multigraph) - Christofides_Solution = shortcut_Euler_Tour(euler_tour) - Travel_Cost = cost(Christofides_Solution, M) - return { - 'Christofides_Solution': Christofides_Solution, - 'Travel_Cost': Travel_Cost, - 'MST': MST, - 'Odd_Vertices': odd_vertices, - 'Indexes': indexes, - 'Multigraph': multiGraph.edges(data='weight'), - 'Euler_Tour': euler_tour - } - -if __name__ == "__main__": - distance_matrix = graph.distance_matrix - Approximation = compute(distance_matrix) - print '\n1.5 Approximation of TSP (Christofide\'s algorithm):', Approximation['Christofides_Solution'] - print 'Travel Cost:', Approximation['Travel_Cost'] - print '' diff --git a/Christofides/test/__init__.py b/Christofides/test/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/README.md b/README.md index c010072..a99e6df 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,9 @@ -======================== Christofides Algorithm ======================== -This package(Christofides) provides a way to implement Christofides algorithm -for solving Travelling Saleman Problem(TSP) to obtain an approximate solution -on an undirected graph(Distance Matrix) provided as an upper Triangular matrix. -The Distance from a node on to itself is assumed 0. +This package provides a way to implement the Christofides algorithm +for solving Travelling Saleman Problem (TSP) to obtain an approximate solution +on an undirected graph. Usage ====== diff --git a/docs/Christofides.txt~ b/docs/Christofides.txt~ deleted file mode 100644 index 966edbe..0000000 --- a/docs/Christofides.txt~ +++ /dev/null @@ -1,22 +0,0 @@ -======================== - Christofides Algorithm -======================== - -The Christofides algorithm is an algorithm for finding approximate solutions to the travelling salesman problem, on instances where the distances form a metric space (they are symmetric and obey the triangle inequality). It is an approximation algorithm that guarantees that its solutions will be within a factor of 3/2 of the optimal solution length, and is named after Nicos Christofides, who published it in 1976. As of 2015, this is the best approximation ratio that has been proven for the traveling salesman problem on general metric spaces, although better approximations are known for some special cases. - -Algorithm -========= - -Let G = (V,w) be an instance of the travelling salesman problem. That is, G is a complete graph on the set V of vertices, and the function w assigns a nonnegative real weight to every edge of G. According to the triangle inequality, for every three vertices u, v, and x, it should be the case that -w(uv) + w(vx) ≥ w(ux). - -Then the algorithm can be described in pseudocode as follows. - -1) Create a minimum spanning tree T of G. -2) Let O be the set of vertices with odd degree in T. By the handshaking lemma, O has an even number of vertices. -3) Find a minimum-weight perfect matching M in the induced subgraph given by the vertices from O. -4) Combine the edges of M and T to form a connected multigraph H in which each vertex has even degree. -5) Form an Eulerian circuit in H. -6) Make the circuit found in previous step into a Hamiltonian circuit by skipping repeated vertices (shortcutting). - - diff --git a/setup.py b/setup.py index a667ec5..5fe62e4 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ url='http://pypi.python.org/pypi/Christofides/', license='LICENSE.txt', description='Christofides Algorithm for TSP.', - long_description=open('README.txt').read(), + long_description=open('README.md').read(), install_requires=[ "scipy >= 0.13.3", "networkx >= 1.11rc1",