class TestSpanningTrees(unittest.TestCase): def setUp(self): self.graph = Graph() self.graph.add_edges([('a', 'b'), ('a', 's'), ('b', 'c'), ('c', 's'), ('d', 'e'), ('d', 's'), ('e', 'd'), ('e', 's')]) def test_breadth_first_search(self): vertices = list(breadth_first_search(self.graph, 's')) self.assertEqual(vertices, ['s', 'a', 'c', 'e', 'd', 'b']) vertices = list(breadth_first_search(self.graph, 's', 1)) self.assertEqual(vertices, ['s', 'a', 'c', 'e', 'd']) def test_breadth_first_search_tree(self): pred = breadth_first_search_tree(self.graph, 's') ans = {'a': 's', 'b': 'a', 'c': 's', 'd': 's', 'e': 's', 's': None} self.assertEqual(pred, ans) def test_depth_first_search(self): vertices = list(depth_first_search(self.graph, 's')) self.assertEqual(vertices, ['s', 'd', 'e', 'c', 'b', 'a']) vertices = list(depth_first_search(self.graph, 's', 1)) self.assertEqual(vertices, ['s', 'd', 'e', 'c', 'a']) def test_depth_first_search_tree(self): pred = depth_first_search_tree(self.graph, 's') ans = {'a': 'b', 'b': 'c', 'c': 's', 'd': 's', 'e': 'd', 's': None} self.assertEqual(pred, ans)
class TestSpanningTrees(unittest.TestCase): def setUp(self): self.graph = Graph() self.graph.add_edges([ ('a', 'b'), ('a', 's'), ('b', 'c'), ('c', 's'), ('d', 'e'), ('d', 's'), ('e', 'd'), ('e', 's') ]) def test_breadth_first_search(self): vertices = list(breadth_first_search(self.graph, 's')) self.assertEqual(vertices, ['s', 'a', 'c', 'e', 'd', 'b']) vertices = list(breadth_first_search(self.graph, 's', 1)) self.assertEqual(vertices, ['s', 'a', 'c', 'e', 'd']) def test_breadth_first_search_tree(self): pred = breadth_first_search_tree(self.graph, 's') ans = {'a': 's', 'b': 'a', 'c': 's', 'd': 's', 'e': 's', 's': None} self.assertEqual(pred, ans) def test_depth_first_search(self): vertices = list(depth_first_search(self.graph, 's')) self.assertEqual(vertices, ['s', 'd', 'e', 'c', 'b', 'a']) vertices = list(depth_first_search(self.graph, 's', 1)) self.assertEqual(vertices, ['s', 'd', 'e', 'c', 'a']) def test_depth_first_search_tree(self): pred = depth_first_search_tree(self.graph, 's') ans = {'a': 'b', 'b': 'c', 'c': 's', 'd': 's', 'e': 'd', 's': None} self.assertEqual(pred, ans)
class TestSpanningTrees(unittest.TestCase): def setUp(self): # Graph in Section 3.2.2 of Dasgupta self.graph = Graph() self.graph.add_vertices(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l']) self.graph.add_edges([('a', 'b'), ('a', 'e'), ('e', 'i'), ('e', 'j'), ('i', 'j'), ('c', 'd'), ('c', 'g'), ('c', 'h'), ('d', 'h'), ('g', 'h'), ('g', 'k'), ('h', 'k'), ('h', 'l')]) self.graph_components = [ ['a', 'b', 'e', 'i', 'j'], ['c', 'h', 'd', 'g', 'k', 'l'], ['f'] ] self.graph_component = ['a', 'b', 'e', 'i', 'j'] def test_connected_component(self): component = connected_component(self.graph, 'a') self.assertEqual(component, self.graph_component) def test_connected_components(self): components = connected_components(self.graph) self.assertEqual(components, self.graph_components)
def setUp(self): graph = Graph() graph.add_edges([('a', 'b', 2), ('a', 'c', 1), ('b', 'c', 1), ('b', 'd', 2), ('b', 'e', 3), ('c', 'e', 4), ('d', 'e', 2)]) self.graph = graph self.distance = {'a': 0, 'b': 2, 'c': 1, 'd': 4, 'e': 5} self.previous = {'a': None, 'b': 'a', 'c': 'a', 'd': 'b', 'e': 'c'}
def setUp(self): # Example in Section 5.1.5 in Dasgupta graph = Graph() graph.add_edges([('a', 'b', 5), ('a', 'c', 6), ('a', 'd', 4), ('b', 'c', 1), ('b', 'd', 2), ('c', 'd', 2), ('c', 'e', 5), ('c', 'f', 3), ('d', 'f', 4), ('e', 'f', 4)]) self.dasgupta_graph = graph self.dasgupta_weight = 14 self.dasgupta_edges = [('a', 'd'), ('b', 'c'), ('b', 'd'), ('c', 'f'), ('e', 'f')] self.dasgupta_dict = { 'a': None, 'b': 'd', 'c': 'b', 'd': 'a', 'e': 'f', 'f': 'c' } graph = Graph() graph.add_edges([(1, 2, 3), (1, 3, 4), (1, 4, 5), (2, 3, 4), (3, 4, 5)]) self.gabow_graph = graph self.gabow_trees = [(12, [(1, 2), (1, 3), (1, 4)]), (12, [(1, 2), (1, 4), (2, 3)]), (12, [(1, 2), (1, 3), (3, 4)]), (12, [(1, 2), (2, 3), (3, 4)]), (13, [(1, 3), (1, 4), (2, 3)]), (13, [(1, 3), (2, 3), (3, 4)]), (13, [(1, 2), (1, 4), (3, 4)]), (14, [(1, 4), (2, 3), (3, 4)])] self.petersen_graph = petersen_graph()
def setUp(self): # Graph in Section 3.2.2 of Dasgupta self.graph = Graph() self.graph.add_vertices( ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l']) self.graph.add_edges([('a', 'b'), ('a', 'e'), ('e', 'i'), ('e', 'j'), ('i', 'j'), ('c', 'd'), ('c', 'g'), ('c', 'h'), ('d', 'h'), ('g', 'h'), ('g', 'k'), ('h', 'k'), ('h', 'l')]) self.graph_components = [['a', 'b', 'e', 'i', 'j'], ['c', 'h', 'd', 'g', 'k', 'l'], ['f']] self.graph_component = ['a', 'b', 'e', 'i', 'j']
def setUp(self): self.petersen_graph = petersen_graph() self.random_graph10_5 = random_graph(10, 0.5) self.random_graph100_3 = random_graph(100, 0.3) self.petersen_cliques = [ [0, 1], [0, 4], [0, 5], [1, 2], [1, 6], [2, 3], [2, 7], [3, 4], [3, 8], [4, 9], [5, 7], [5, 8], [6, 8], [6, 9], [7, 9], ] self.random_graph_edges = [ (0, 2), (0, 3), (0, 5), (0, 7), (0, 9), (1, 2), (1, 3), (1, 5), (1, 6), (1, 7), (1, 9), (2, 3), (2, 5), (2, 6), (2, 7), (2, 8), (3, 5), (3, 7), (4, 6), (4, 7), (5, 6), (5, 7), (5, 9), (6, 7), (6, 8), (6, 9), (7, 9), ] self.random_graph = Graph() self.random_graph.add_edges(self.random_graph_edges) self.random_graph_clique = [0, 2, 3, 5, 7] self.coprime_graph10 = coprime_pairs_graph(10) self.coprime_graph10_weight = 30 self.coprime_graph10_clique = [1, 5, 7, 8, 9] self.coprime_graph100 = coprime_pairs_graph(100) self.coprime_graph100_weight = 1356 self.coprime_graph100_clique = [ 1, 17, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 81, 83, 88, 89, 91, 95, 97, ]
def setUp(self): # Example in Section 5.1.5 in Dasgupta graph = Graph() graph.add_edges([('u1', 'u2', 1), ('u1', 'v1', 2), ('u1', 'v2', 2), ('u2', 'v3', 4), ('u2', 'v4', 3), ('u2', 'v5', 5), ('u3', 'v1', 2), ('u3', 'v3', 8), ('u4', 'v2', 8), ('u4', 'v5', 8), ('v1', 'v2', 8), ('v1', 'v3', 9), ('v2', 'v5', 5), ('v3', 'v4', 8)]) self.graph1 = graph self.terminals1 = ['v1', 'v2', 'v3', 'v4', 'v5'] self.weight1 = 17 self.edges1 = [('u1', 'u2'), ('u1', 'v1'), ('u1', 'v2'), ('u2', 'v3'), ('u2', 'v4'), ('u2', 'v5')] graph = Graph() graph.add_edges([(0, 1, 1), (0, 4, 2), (1, 2, 3), (1, 4, 1), (1, 5, 2), (2, 3, 2), (2, 6, 6), (3, 6, 3), (3, 7, 1), (4, 5, 3), (4, 8, 3), (4, 9, 4), (5, 6, 1), (5, 9, 4), (5, 10, 3), (6, 7, 4), (6, 10, 2), (7, 10, 1), (7, 11, 2), (8, 9, 2), (8, 12, 1), (9, 10, 5), (9, 13, 6), (10, 11, 3), (10, 13, 1), (10, 14, 1), (10, 15, 4), (11, 15, 1), (12, 13, 3), (13, 14, 4), (14, 15, 6)]) self.graph2 = graph self.terminals2 = [0, 3, 6, 9, 12, 15] self.weight2 = 18 self.edges2 = [(0, 1), (1, 5), (3, 6), (3, 7), (5, 6), (5, 9), (7, 11), (8, 9), (8, 12), (11, 15)] graph = Graph() edges = [ (0, 10, 0.18711581985575576), (1, 9, 0.8355759080627565), (1, 16, 0.7333895522013495), (2, 8, 0.2179642234669944), (2, 13, 0.8912036215549415), (2, 19, 0.6431373258117827), (2, 24, 0.04038712999015037), (3, 6, 0.12324233297257259), (4, 6, 0.8716054380512974), (4, 14, 0.8394000635595052), (5, 13, 0.5808432084428543), (5, 15, 0.3817889531355405), (6, 28, 0.7104386125476283), (7, 17, 0.8196386513589949), (7, 22, 0.5415599110939858), (7, 26, 0.48022044306517697), (10, 13, 0.2289107839123704), (10, 27, 0.6078612772657713), (10, 28, 0.18320583236837396), (11, 14, 0.21670116683074658), (11, 23, 0.814622874490316), (11, 24, 0.2741012085352842), (12, 14, 0.020838710732781984), (12, 26, 0.32305190820208796), (13, 16, 0.7798216089004076), (14, 24, 0.42823907465971456), (14, 29, 0.8005872492974788), (15, 24, 0.5714901649169496), (16, 23, 0.3254825323952686), (17, 19, 0.4371620863350263), (17, 20, 0.7305916551727157), (17, 26, 0.20057807143450757), (18, 25, 0.99192719132646), (19, 25, 0.4630494211413302), (20, 29, 0.02443067673210797), (21, 22, 0.19217126655249206), (21, 29, 0.5188924055605231), (22, 25, 0.7632549163886935), (24, 28, 0.8954891075360596) ] graph.add_edges(edges) self.graph3 = graph self.terminals3 = range(5) self.weight3 = 4.708933602364698 self.edges3 = [(0, 10), (1, 16), (2, 13), (3, 6), (4, 6), (6, 28), (10, 13), (10, 28), (13, 16)]
def setUp(self): # Example in Section 5.1.5 in Dasgupta graph = Graph() graph.add_edges([('a', 'b', 5), ('a', 'c', 6), ('a', 'd', 4), ('b', 'c', 1), ('b', 'd', 2), ('c', 'd', 2), ('c', 'e', 5), ('c', 'f', 3), ('d', 'f', 4), ('e', 'f', 4)]) self.dasgupta_graph = graph self.dasgupta_weight = 14 self.dasgupta_edges = [('a', 'd'), ('b', 'c'), ('b', 'd'), ('c', 'f'), ('e', 'f')] self.dasgupta_dict = {'a': None, 'b': 'd', 'c': 'b', 'd': 'a', 'e': 'f', 'f': 'c'} graph = Graph() graph.add_edges([(1, 2, 3), (1, 3, 4), (1, 4, 5), (2, 3, 4), (3, 4, 5)]) self.gabow_graph = graph self.gabow_trees = [ (12, [(1, 2), (1, 3), (1, 4)]), (12, [(1, 2), (1, 4), (2, 3)]), (12, [(1, 2), (1, 3), (3, 4)]), (12, [(1, 2), (2, 3), (3, 4)]), (13, [(1, 3), (1, 4), (2, 3)]), (13, [(1, 3), (2, 3), (3, 4)]), (13, [(1, 2), (1, 4), (3, 4)]), (14, [(1, 4), (2, 3), (3, 4)]) ] self.petersen_graph = petersen_graph()
class TestSpanningTrees(unittest.TestCase): def setUp(self): # Graph in Section 3.2.2 of Dasgupta self.graph = Graph() self.graph.add_vertices( ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l']) self.graph.add_edges([('a', 'b'), ('a', 'e'), ('e', 'i'), ('e', 'j'), ('i', 'j'), ('c', 'd'), ('c', 'g'), ('c', 'h'), ('d', 'h'), ('g', 'h'), ('g', 'k'), ('h', 'k'), ('h', 'l')]) self.graph_components = [['a', 'b', 'e', 'i', 'j'], ['c', 'h', 'd', 'g', 'k', 'l'], ['f']] self.graph_component = ['a', 'b', 'e', 'i', 'j'] def test_connected_component(self): component = connected_component(self.graph, 'a') self.assertEqual(component, self.graph_component) def test_connected_components(self): components = connected_components(self.graph) self.assertEqual(components, self.graph_components)
def _delete_nonterminal_leaves(edges, terminals): r"""Prunes all non-terminal leaves from the tree. Given the edges of a tree and a set of terminal vertices, this method removes all non-terminal leaves and returns the new tree edges. Parameters ---------- edges : list A list of the edges of the tree. terminals : set A set of the terminal vertices. Returns ------- tree_edges : list A list of the edges of the pruned tree. Notes ----- This method deletes all non-terminal leaves from the tree. This process is repeated until all leaves are terminals. There is room to improve this algorithm. """ # Form a graph from the edge list. tree = Graph() for (u, v) in edges: tree.add_edge(u, v) # Now delete all leaves that are not terminals while True: leaves = tree.leaves() changed = False for leaf in leaves: if leaf not in terminals: tree.delete_vertex(leaf) changed = True if not changed: break return tree.edges()
def setUp(self): # Graph in Section 3.2.2 of Dasgupta self.graph = Graph() self.graph.add_vertices(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l']) self.graph.add_edges([('a', 'b'), ('a', 'e'), ('e', 'i'), ('e', 'j'), ('i', 'j'), ('c', 'd'), ('c', 'g'), ('c', 'h'), ('d', 'h'), ('g', 'h'), ('g', 'k'), ('h', 'k'), ('h', 'l')]) self.graph_components = [ ['a', 'b', 'e', 'i', 'j'], ['c', 'h', 'd', 'g', 'k', 'l'], ['f'] ] self.graph_component = ['a', 'b', 'e', 'i', 'j']
def setUp(self): graph = Graph() graph.add_edges([('a', 'b', 2), ('a', 'c', 1), ('b', 'c', 1), ('b', 'd', 2), ('b', 'e', 3), ('c', 'e', 4), ('d', 'e', 2)]) self.graph = graph self.distance = {'a': 0, 'b': 2, 'c': 1, 'd': 4, 'e': 5} self.previous = {'a': None, 'b': 'a', 'c': 'a', 'd': 'b', 'e': 'c'} graph = Graph() edges = [('a', 'd', 4), ('a', 'e', 1), ('a', 'h', 10), ('b', 'c', 2), ('b', 'f', 1), ('c', 'f', 3), ('d', 'h', 1), ('e', 'f', 3), ('e', 'h', 5), ('f', 'g', 7), ('f', 'i', 1), ('g', 'j', 1), ('h', 'i', 9), ('i', 'j', 2)] graph.add_edges(edges) self.graph2 = graph self.distance2 = {'a': 4, 'c': 11, 'b': 9, 'e': 5, 'd': 0, 'g': 12, 'f': 8, 'i': 9, 'h': 1, 'j': 11} self.previous2 = {'a': 'd', 'c': 'f', 'b': 'f', 'e': 'a', 'd': None, 'g': 'j', 'f': 'e', 'i': 'f', 'h': 'd', 'j': 'i'}
def setUp(self): self.graph = Graph() self.graph.add_edges([('a', 'b'), ('a', 's'), ('b', 'c'), ('c', 's'), ('d', 'e'), ('d', 's'), ('e', 'd'), ('e', 's')])
def distance_network_heuristic(graph, terminals): r"""Returns an approximate minimal Steiner tree connecting `terminals` in `graph`. Given a connected, undirected graph `graph` with positive edge weights and a subset of the vertices `terminals`, this method finds a subgraph with near-minimal total edge cost among all connected subgraphs that contain these vertices. Parameters ---------- graph : rosemary.graphs.graphs.Graph terminals : list or set or tuple A collection of vertices of `graph`. Returns ------- (weight, edges) : tuple The number `weight` is the weight of the Steiner tree. The list `edges` is a list of the edges in the Steiner tree. Notes ----- The Steiner problem in graphs asks to find the minimal tree connecting the terminal vertices. This problem is NP-complete and has proven to be extremely difficult even for small problem instances. Given this, it is of practical importance to have heuristics for finding low-cost Steiner trees. This method uses the heuristic algorithm given in [1], which produces a tree spanning the terminals with total cost <= 2*(1 - 1/t)*MST, where t is the number of terminal vertices and MST is the weight of a minimal Steiner tree. We also apply the improvement procedure given in [3]. The implementation given runs in time O(t*|E|*log(|V|)), where |E| is the number of edges of `graph` and |V| is the number of vertices. References ---------- .. [1] L. Kou, G. Markowsky, L. Berman, "A Fast Algorithm for Steiner Trees", Acta Informatica, Volume 15, Issue 2, June 1981, 141-145. .. [2] P. Winter, "Steiner Problem in Networks: A Survey", Networks, Volume 17, Issue 2, Summer 1987, 129-167. .. [3] S. Voss, "Steiner's Problem in Graphs: Heuristic Methods", Discrete Applied Mathematics 40, 1992, 45-72. .. [4] H.J. Proemel, A. Steger, "The Steiner Tree Problem - A Tour through Graphs, Algorithms, and Complexity", Vieweg, 2002. Examples -------- >>> G = Graph() >>> G.add_edges([('u1', 'u2', 1), ('u1', 'v1', 2), ('u1', 'v2', 2), ('u2', 'v3', 4), ('u2', 'v4', 3), ('u2', 'v5', 5), ('u3', 'v1', 2), ('u3', 'v3', 8), ('u4', 'v2', 8), ('u4', 'v5', 8), ('v1', 'v2', 8), ('v1', 'v3', 9), ('v2', 'v5', 5), ('v3', 'v4', 8)]) >>> distance_network_heuristic(G, ['v1', 'v2', 'v3', 'v4', 'v5']) (17, [('u1', 'u2'), ('u1', 'v1'), ('u1', 'v2'), ('u2', 'v3'), ('u2', 'v4'), ('v2', 'v5')]) """ # Create the distance network induced by the terminal set. distance_network = Graph() # shortest_prev[u] holds the predecessor dict for the shortest path # tree rooted at u. shortest_prev = {} shortest_dist = {} for u in terminals: u_dist, u_prev = dijkstra(graph, u) shortest_dist[u] = u_dist shortest_prev[u] = u_prev # For each pair (u, v) of terminal vertices, add an edge with weight # equal to the length of the shortest u, v path. distance_network.add_edges([(u, v, shortest_dist[u][v]) for (u, v) in itertools.combinations(terminals, 2)]) # Determine the minimum spanning tree of the distance network. _, mst_edges = prim(distance_network, edge_list=True) subnetwork = Graph() # Construct a subnetwork of the graph by replacing each edge in the # minimum spanning tree by the corresponding minimum cost path. for (u, v) in mst_edges: a, b = shortest_prev[u][v], v while a is not None: subnetwork.add_edge(a, b, graph[a][b]) a, b = shortest_prev[u][a], a # Determine the minimum spanning tree of the subnetwork. _, subnetwork_mst_edges = prim(subnetwork, edge_list=True) tree_weight, tree_edges = _improve(graph, subnetwork_mst_edges, terminals) return (tree_weight, tree_edges)
def setUp(self): # Example in Section 5.1.5 in Dasgupta graph = Graph() graph.add_edges([('u1', 'u2', 1), ('u1', 'v1', 2), ('u1', 'v2', 2), ('u2', 'v3', 4), ('u2', 'v4', 3), ('u2', 'v5', 5), ('u3', 'v1', 2), ('u3', 'v3', 8), ('u4', 'v2', 8), ('u4', 'v5', 8), ('v1', 'v2', 8), ('v1', 'v3', 9), ('v2', 'v5', 5), ('v3', 'v4', 8)]) self.graph1 = graph self.terminals1 = ['v1', 'v2', 'v3', 'v4', 'v5'] self.weight1 = 17 self.edges1 = [('u1', 'u2'), ('u1', 'v1'), ('u1', 'v2'), ('u2', 'v3'), ('u2', 'v4'), ('u2', 'v5')] graph = Graph() graph.add_edges([(0, 1, 1), (0, 4, 2), (1, 2, 3), (1, 4, 1), (1, 5, 2), (2, 3, 2), (2, 6, 6), (3, 6, 3), (3, 7, 1), (4, 5, 3), (4, 8, 3), (4, 9, 4), (5, 6, 1), (5, 9, 4), (5, 10, 3), (6, 7, 4), (6, 10, 2), (7, 10, 1), (7, 11, 2), (8, 9, 2), (8, 12, 1), (9, 10, 5), (9, 13, 6), (10, 11, 3), (10, 13, 1), (10, 14, 1), (10, 15, 4), (11, 15, 1), (12, 13, 3), (13, 14, 4), (14, 15, 6)]) self.graph2 = graph self.terminals2 = [0, 3, 6, 9, 12, 15] self.weight2 = 18 self.edges2 = [(0, 1), (1, 5), (3, 6), (3, 7), (5, 6), (5, 9), (7, 11), (8, 9), (8, 12), (11, 15)] graph = Graph() edges = [(0, 10, 0.18711581985575576), (1, 9, 0.8355759080627565), (1, 16, 0.7333895522013495), (2, 8, 0.2179642234669944), (2, 13, 0.8912036215549415), (2, 19, 0.6431373258117827), (2, 24, 0.04038712999015037), (3, 6, 0.12324233297257259), (4, 6, 0.8716054380512974), (4, 14, 0.8394000635595052), (5, 13, 0.5808432084428543), (5, 15, 0.3817889531355405), (6, 28, 0.7104386125476283), (7, 17, 0.8196386513589949), (7, 22, 0.5415599110939858), (7, 26, 0.48022044306517697), (10, 13, 0.2289107839123704), (10, 27, 0.6078612772657713), (10, 28, 0.18320583236837396), (11, 14, 0.21670116683074658), (11, 23, 0.814622874490316), (11, 24, 0.2741012085352842), (12, 14, 0.020838710732781984), (12, 26, 0.32305190820208796), (13, 16, 0.7798216089004076), (14, 24, 0.42823907465971456), (14, 29, 0.8005872492974788), (15, 24, 0.5714901649169496), (16, 23, 0.3254825323952686), (17, 19, 0.4371620863350263), (17, 20, 0.7305916551727157), (17, 26, 0.20057807143450757), (18, 25, 0.99192719132646), (19, 25, 0.4630494211413302), (20, 29, 0.02443067673210797), (21, 22, 0.19217126655249206), (21, 29, 0.5188924055605231), (22, 25, 0.7632549163886935), (24, 28, 0.8954891075360596)] graph.add_edges(edges) self.graph3 = graph self.terminals3 = range(5) self.weight3 = 4.708933602364698 self.edges3 = [(0, 10), (1, 16), (2, 13), (3, 6), (4, 6), (6, 28), (10, 13), (10, 28), (13, 16)]
def gabow(graph, k=None): def find_exchange(father, included, excluded): X = NamedUnionFind(vertices) find = X.find union = X.union (min_weight, e, f) = (inf, None, None) for (x, y) in included: # Make it so that y = father[x] if y != father[x]: x, y = y, x y = find(y) union(x, y, y) for edge in excluded: mark(edge) for (x, y) in edges: if (x, y) in marked or (y, x) in marked: unmark((x, y)) unmark((y, x)) elif father[x] != y and father[y] != x: a = find(x) ancestors = set() while a not in ancestors: ancestors.add(a) a = find(father[a]) a = find(y) while a not in ancestors: a = find(father[a]) for u in [x, y]: v = find(u) while v != a: fv = father[v] exchange_weight = weight[x, y] - weight[v, fv] if exchange_weight < min_weight: min_weight = exchange_weight e = (v, fv) f = (x, y) w = find(fv) union(v, w, w) v = w return (min_weight, e, f) inf = float('inf') if k is None: k = inf weight = {(u, v): graph[u][v] for u in graph for v in graph[u]} marked = set() mark = marked.add unmark = marked.discard edges = sorted(graph.edge_set(), key=lambda (u, v): weight[(u, v)]) vertices = graph.vertices() # arbitrary root vertex root = vertices[0] tree_weight, father = prim(graph, root, edge_list=False) father[root] = root (exchange_weight, e, f) = find_exchange(father, [], []) heap = [(tree_weight + exchange_weight, e, f, father, [], [])] tree_edges = sorted([(min(x, y), max(x, y)) for (x, y) in father.items() if x != y]) yield tree_weight, tree_edges j = 1 while j < k: (tree_weight, e, f, father, included, excluded) = heappop(heap) if tree_weight == inf: return new_graph = Graph() new_graph.add_edges(father.items()) new_graph.delete_edge(e) new_graph.add_edge(f) new_father = breadth_first_search_tree(new_graph, root) new_father[root] = root tree_edges = sorted([(min(x, y), max(x, y)) for (x, y) in new_father.items() if x != y]) yield tree_weight, tree_edges new_tree_weight = tree_weight - weight[f] + weight[e] included_i = included + [e] excluded_j = excluded + [e] (exchange_weight, e, f) = find_exchange(father, included_i, excluded) heappush(heap, (new_tree_weight + exchange_weight, e, f, father, included_i, excluded)) (exchange_weight, e, f) = find_exchange(new_father, included, excluded_j) heappush(heap, (tree_weight + exchange_weight, e, f, new_father, included, excluded_j)) j += 1
def setUp(self): self.graph = Graph() self.graph.add_edges([ ('a', 'b'), ('a', 's'), ('b', 'c'), ('c', 's'), ('d', 'e'), ('d', 's'), ('e', 'd'), ('e', 's') ])
def setUp(self): self.petersen_graph = petersen_graph() self.petersen_colors = 3 self.coprime_graph30 = coprime_pairs_graph(30) self.coprime_graph30_colors = 11 graph = Graph() graph.add_edges([(0, 2), (0, 4), (0, 5), (0, 8), (1, 3), (1, 4), (2, 6), (2, 8), (3, 4), (3, 7), (4, 6), (4, 7), (4, 9), (5, 8)]) self.small_random = graph self.small_colors = 3 self.small_classes = [[4, 8], [0, 3, 6, 9], [1, 2, 5, 7]] graph = Graph() graph.add_edges([(0, 4), (0, 6), (1, 2), (1, 4), (1, 8), (1, 9), (2, 3), (2, 4), (3, 5), (3, 7), (4, 6), (4, 8), (4, 9), (5, 7), (5, 9), (6, 8), (7, 8)]) self.small_random2 = graph self.small_colors2 = 4 self.small_classes2 = [[4, 7], [0, 2, 8, 9], [1, 5, 6], [3]] graph = Graph() graph.add_edges([(0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 7), (0, 8), (0, 9), (0, 10), (0, 11), (0, 12), (0, 13), (0, 14), (0, 15), (0, 16), (0, 17), (0, 18), (0, 19), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8), (1, 9), (1, 10), (1, 12), (1, 14), (1, 15), (1, 17), (1, 18), (1, 19), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (2, 9), (2, 11), (2, 12), (2, 13), (2, 14), (2, 15), (2, 17), (2, 18), (2, 19), (3, 4), (3, 5), (3, 6), (3, 7), (3, 8), (3, 9), (3, 10), (3, 11), (3, 12), (3, 14), (3, 15), (3, 16), (3, 17), (3, 18), (4, 5), (4, 6), (4, 7), (4, 9), (4, 10), (4, 11), (4, 13), (4, 14), (4, 15), (4, 16), (4, 17), (4, 18), (4, 19), (5, 6), (5, 7), (5, 9), (5, 10), (5, 13), (5, 14), (5, 15), (5, 17), (5, 19), (6, 8), (6, 9), (6, 10), (6, 11), (6, 12), (6, 13), (6, 14), (6, 15), (6, 16), (6, 17), (6, 19), (7, 8), (7, 9), (7, 12), (7, 15), (7, 16), (7, 17), (7, 19), (8, 9), (8, 10), (8, 11), (8, 12), (8, 13), (8, 14), (8, 16), (8, 17), (8, 18), (8, 19), (9, 10), (9, 11), (9, 14), (9, 17), (9, 18), (9, 19), (10, 11), (10, 12), (10, 13), (10, 15), (10, 16), (10, 17), (10, 18), (10, 19), (11, 12), (11, 13), (11, 14), (11, 15), (11, 16), (11, 17), (11, 18), (11, 19), (12, 13), (12, 14), (12, 15), (12, 16), (12, 17), (12, 18), (12, 19), (13, 14), (13, 15), (13, 16), (13, 17), (13, 18), (13, 19), (14, 16), (14, 17), (14, 18), (15, 17), (15, 19), (16, 18), (16, 19), (17, 18), (18, 19)]) self.big_random = graph self.big_colors = 10 self.big_classes = [[0, 6], [17, 19], [4, 8], [5, 11], [7, 10, 14], [2, 3], [15, 18], [9, 12], [1, 16], [13]]
class TestCliques(unittest.TestCase): def setUp(self): self.petersen_graph = petersen_graph() self.random_graph10_5 = random_graph(10, 0.5) self.random_graph100_3 = random_graph(100, 0.3) self.petersen_cliques = [ [0, 1], [0, 4], [0, 5], [1, 2], [1, 6], [2, 3], [2, 7], [3, 4], [3, 8], [4, 9], [5, 7], [5, 8], [6, 8], [6, 9], [7, 9], ] self.random_graph_edges = [ (0, 2), (0, 3), (0, 5), (0, 7), (0, 9), (1, 2), (1, 3), (1, 5), (1, 6), (1, 7), (1, 9), (2, 3), (2, 5), (2, 6), (2, 7), (2, 8), (3, 5), (3, 7), (4, 6), (4, 7), (5, 6), (5, 7), (5, 9), (6, 7), (6, 8), (6, 9), (7, 9), ] self.random_graph = Graph() self.random_graph.add_edges(self.random_graph_edges) self.random_graph_clique = [0, 2, 3, 5, 7] self.coprime_graph10 = coprime_pairs_graph(10) self.coprime_graph10_weight = 30 self.coprime_graph10_clique = [1, 5, 7, 8, 9] self.coprime_graph100 = coprime_pairs_graph(100) self.coprime_graph100_weight = 1356 self.coprime_graph100_clique = [ 1, 17, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 81, 83, 88, 89, 91, 95, 97, ] def test_maximal_cliques(self): print "Testing Bron-Kerbosh algorithm" cliques = [sorted(e) for e in bron_kerbosch(self.petersen_graph)] self.assertEqual(sorted(cliques), self.petersen_cliques) print "Testing binary Bron-Kerbosh algorithm" cliques = [sorted(e) for e in bron_kerbosch_binary(self.petersen_graph)] self.assertEqual(sorted(cliques), self.petersen_cliques) print "Testing Tomita algorithm" cliques = [sorted(e) for e in tomita(self.petersen_graph)] self.assertEqual(sorted(cliques), self.petersen_cliques) print "Testing generic maximal cliques algorithm" for algorithm in ['tomita', 'bron_kerbosch', 'bron_kerbosch_binary']: cliques = [sorted(e) for e in maximal_cliques(self.petersen_graph, algorithm=algorithm)] self.assertEqual(sorted(cliques), self.petersen_cliques) print "Testing with random graphs" cliques1 = list(bron_kerbosch(self.random_graph10_5)) cliques2 = list(bron_kerbosch_binary(self.random_graph10_5)) cliques3 = list(tomita(self.random_graph10_5)) self.assertEqual(len(cliques1), len(cliques2)) self.assertEqual(len(cliques2), len(cliques3)) cliques1 = list(bron_kerbosch(self.random_graph100_3)) cliques2 = list(bron_kerbosch_binary(self.random_graph100_3)) cliques3 = list(tomita(self.random_graph100_3)) self.assertEqual(len(cliques1), len(cliques2)) self.assertEqual(len(cliques2), len(cliques3)) def test_maximum_clique(self): print "Testing Ostergard algorithm" size, clique = ostergard(self.random_graph) self.assertEqual(size, 5) self.assertEqual(sorted(clique), self.random_graph_clique) print "Testing Pardalos algorithm" size, clique = pardalos(self.random_graph) self.assertEqual(size, 5) self.assertEqual(sorted(clique), self.random_graph_clique) print "Testing generic maximum-clique algorithm" for algorithm in ['pardalos', 'ostergard']: size, clique = maximum_clique(self.random_graph, algorithm=algorithm) self.assertEqual(size, 5) self.assertEqual(sorted(clique), self.random_graph_clique) def test_maximum_weight_clique(self): print "Testing Maximum-weight clique algorithm" size, clique = maximum_weight_clique(self.coprime_graph10) self.assertEqual(size, self.coprime_graph10_weight) self.assertEqual(sorted(clique), self.coprime_graph10_clique) size, clique = maximum_weight_clique(self.coprime_graph100) self.assertEqual(size, self.coprime_graph100_weight) self.assertEqual(sorted(clique), self.coprime_graph100_clique)
def setUp(self): self.petersen_graph = petersen_graph() self.random_graph10_5 = random_graph(10, 0.5) self.random_graph100_3 = random_graph(100, 0.3) self.petersen_cliques = [ [0, 1], [0, 4], [0, 5], [1, 2], [1, 6], [2, 3], [2, 7], [3, 4], [3, 8], [4, 9], [5, 7], [5, 8], [6, 8], [6, 9], [7, 9], ] self.random_graph_edges = [ (0, 2), (0, 3), (0, 5), (0, 7), (0, 9), (1, 2), (1, 3), (1, 5), (1, 6), (1, 7), (1, 9), (2, 3), (2, 5), (2, 6), (2, 7), (2, 8), (3, 5), (3, 7), (4, 6), (4, 7), (5, 6), (5, 7), (5, 9), (6, 7), (6, 8), (6, 9), (7, 9), ] self.random_graph = Graph() self.random_graph.add_edges(self.random_graph_edges) self.random_graph_clique = [0, 2, 3, 5, 7] self.random_graph_clique2 = [1, 5, 6, 7, 9] self.coprime_graph10 = coprime_pairs_graph(10) self.coprime_graph10_weight = 30 self.coprime_graph10_clique = [1, 5, 7, 8, 9] self.coprime_graph100 = coprime_pairs_graph(100) self.coprime_graph100_weight = 1356 self.coprime_graph100_clique = [ 1, 17, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 81, 83, 88, 89, 91, 95, 97, ]
class TestCliques(unittest.TestCase): def setUp(self): self.petersen_graph = petersen_graph() self.random_graph10_5 = random_graph(10, 0.5) self.random_graph100_3 = random_graph(100, 0.3) self.petersen_cliques = [ [0, 1], [0, 4], [0, 5], [1, 2], [1, 6], [2, 3], [2, 7], [3, 4], [3, 8], [4, 9], [5, 7], [5, 8], [6, 8], [6, 9], [7, 9], ] self.random_graph_edges = [ (0, 2), (0, 3), (0, 5), (0, 7), (0, 9), (1, 2), (1, 3), (1, 5), (1, 6), (1, 7), (1, 9), (2, 3), (2, 5), (2, 6), (2, 7), (2, 8), (3, 5), (3, 7), (4, 6), (4, 7), (5, 6), (5, 7), (5, 9), (6, 7), (6, 8), (6, 9), (7, 9), ] self.random_graph = Graph() self.random_graph.add_edges(self.random_graph_edges) self.random_graph_clique = [0, 2, 3, 5, 7] self.random_graph_clique2 = [1, 5, 6, 7, 9] self.coprime_graph10 = coprime_pairs_graph(10) self.coprime_graph10_weight = 30 self.coprime_graph10_clique = [1, 5, 7, 8, 9] self.coprime_graph100 = coprime_pairs_graph(100) self.coprime_graph100_weight = 1356 self.coprime_graph100_clique = [ 1, 17, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 81, 83, 88, 89, 91, 95, 97, ] def test_maximal_cliques(self): print "Testing Bron-Kerbosh algorithm" cliques = [sorted(e) for e in bron_kerbosch(self.petersen_graph)] self.assertEqual(sorted(cliques), self.petersen_cliques) print "Testing binary Bron-Kerbosh algorithm" cliques = [ sorted(e) for e in bron_kerbosch_binary(self.petersen_graph) ] self.assertEqual(sorted(cliques), self.petersen_cliques) print "Testing Tomita algorithm" cliques = [sorted(e) for e in tomita(self.petersen_graph)] self.assertEqual(sorted(cliques), self.petersen_cliques) print "Testing generic maximal cliques algorithm" for algorithm in ['tomita', 'bron_kerbosch', 'bron_kerbosch_binary']: cliques = [ sorted(e) for e in maximal_cliques(self.petersen_graph, algorithm=algorithm) ] self.assertEqual(sorted(cliques), self.petersen_cliques) print "Testing with random graphs" cliques1 = list(bron_kerbosch(self.random_graph10_5)) cliques2 = list(bron_kerbosch_binary(self.random_graph10_5)) cliques3 = list(tomita(self.random_graph10_5)) self.assertEqual(len(cliques1), len(cliques2)) self.assertEqual(len(cliques2), len(cliques3)) cliques1 = list(bron_kerbosch(self.random_graph100_3)) cliques2 = list(bron_kerbosch_binary(self.random_graph100_3)) cliques3 = list(tomita(self.random_graph100_3)) self.assertEqual(len(cliques1), len(cliques2)) self.assertEqual(len(cliques2), len(cliques3)) def test_maximum_clique(self): print "Testing Ostergard algorithm" size, clique = ostergard(self.random_graph) self.assertEqual(size, 5) self.assertEqual(sorted(clique), self.random_graph_clique2) print "Testing Pardalos algorithm" size, clique = pardalos(self.random_graph) self.assertEqual(size, 5) self.assertEqual(sorted(clique), self.random_graph_clique) print "Testing generic maximum-clique algorithm" for algorithm in ['pardalos', 'ostergard']: size, clique = maximum_clique(self.random_graph, algorithm=algorithm) self.assertEqual(size, 5) def test_maximum_weight_clique(self): print "Testing Maximum-weight clique algorithm" size, clique = maximum_weight_clique(self.coprime_graph10) self.assertEqual(size, self.coprime_graph10_weight) self.assertEqual(sorted(clique), self.coprime_graph10_clique) size, clique = maximum_weight_clique(self.coprime_graph100) self.assertEqual(size, self.coprime_graph100_weight) self.assertEqual(sorted(clique), self.coprime_graph100_clique)