From e625a9fd9bba809ce615a359b72c53cbdb73df94 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Sun, 31 May 2020 11:49:52 +0530 Subject: [PATCH] Kahn's algorithm for topological sort (#286) --- pydatastructs/graphs/__init__.py | 3 +- pydatastructs/graphs/algorithms.py | 75 ++++++++++++++++++- pydatastructs/graphs/tests/test_algorithms.py | 26 ++++++- 3 files changed, 100 insertions(+), 4 deletions(-) diff --git a/pydatastructs/graphs/__init__.py b/pydatastructs/graphs/__init__.py index 9f33371f1..d2ab66651 100644 --- a/pydatastructs/graphs/__init__.py +++ b/pydatastructs/graphs/__init__.py @@ -14,7 +14,8 @@ minimum_spanning_tree_parallel, strongly_connected_components, depth_first_search, - shortest_paths + shortest_paths, + topological_sort ) __all__.extend(algorithms.__all__) diff --git a/pydatastructs/graphs/algorithms.py b/pydatastructs/graphs/algorithms.py index 4dd35e1b9..b06d2666a 100644 --- a/pydatastructs/graphs/algorithms.py +++ b/pydatastructs/graphs/algorithms.py @@ -1,5 +1,5 @@ """ -Contains all the algorithms associated with graph +Contains algorithms associated with graph data structure. """ from collections import deque @@ -18,7 +18,8 @@ 'minimum_spanning_tree_parallel', 'strongly_connected_components', 'depth_first_search', - 'shortest_paths' + 'shortest_paths', + 'topological_sort' ] Stack = Queue = deque @@ -725,3 +726,73 @@ def _bellman_ford_adjacency_list(graph: Graph, source: str, target: str) -> tupl return (distances, predecessor) _bellman_ford_adjacency_matrix = _bellman_ford_adjacency_list + +def topological_sort(graph: Graph, algorithm: str) -> list: + """ + Performs topological sort on the given graph using given algorithm. + + Parameters + ========== + + graph: Graph + The graph under consideration. + algorithm: str + The algorithm to be used. + Currently, following are supported, + 'kahn' -> Kahn's algorithm as given in [1]. + + Returns + ======= + + list + The list of topologically sorted vertices. + + Examples + ======== + + >>> from pydatastructs import Graph, AdjacencyListGraphNode, topological_sort + >>> v_1 = AdjacencyListGraphNode('v_1') + >>> v_2 = AdjacencyListGraphNode('v_2') + >>> graph = Graph(v_1, v_2) + >>> graph.add_edge('v_1', 'v_2') + >>> topological_sort(graph, 'kahn') + ['v_1', 'v_2'] + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm + """ + import pydatastructs.graphs.algorithms as algorithms + func = "_" + algorithm + "_" + graph._impl + if not hasattr(algorithms, func): + raise NotImplementedError( + "Currently %s algorithm isn't implemented for " + "performing topological sort on %s graphs."%(algorithm, graph._impl)) + return getattr(algorithms, func)(graph) + +def _kahn_adjacency_list(graph: Graph) -> list: + S = set(graph.vertices) + in_degree = dict() + for u in graph.vertices: + for v in graph.neighbors(u): + if v.name not in in_degree: + in_degree[v.name] = 0 + in_degree[v.name] += 1 + if v.name in S: + S.remove(v.name) + + L = [] + while S: + n = S.pop() + L.append(n) + for m in graph.neighbors(n): + graph.remove_edge(n, m.name) + in_degree[m.name] -= 1 + if in_degree[m.name] == 0: + S.add(m.name) + in_degree.pop(m.name) + + if in_degree: + raise ValueError("Graph is not acyclic.") + return L diff --git a/pydatastructs/graphs/tests/test_algorithms.py b/pydatastructs/graphs/tests/test_algorithms.py index 3ecd0c60e..fdb7e9965 100644 --- a/pydatastructs/graphs/tests/test_algorithms.py +++ b/pydatastructs/graphs/tests/test_algorithms.py @@ -1,7 +1,7 @@ from pydatastructs import (breadth_first_search, Graph, breadth_first_search_parallel, minimum_spanning_tree, minimum_spanning_tree_parallel, strongly_connected_components, -depth_first_search, shortest_paths) +depth_first_search, shortest_paths, topological_sort) from pydatastructs.utils.raises_util import raises def test_breadth_first_search(): @@ -289,3 +289,27 @@ def _test_shortest_paths(ds, algorithm): _test_shortest_paths("List", 'bellman_ford') _test_shortest_paths("Matrix", 'bellman_ford') + +def test_topological_sort(): + + def _test_topological_sort(ds, algorithm): + import pydatastructs.utils.misc_util as utils + GraphNode = getattr(utils, "Adjacency" + ds + "GraphNode") + vertices = [GraphNode('2'), GraphNode('3'), GraphNode('5'), + GraphNode('7'), GraphNode('8'), GraphNode('10'), + GraphNode('11'), GraphNode('9')] + + graph = Graph(*vertices) + graph.add_edge('5', '11') + graph.add_edge('7', '11') + graph.add_edge('7', '8') + graph.add_edge('3', '8') + graph.add_edge('3', '10') + graph.add_edge('11', '2') + graph.add_edge('11', '9') + graph.add_edge('11', '10') + graph.add_edge('8', '9') + l = topological_sort(graph, algorithm) + assert all([(l1 in l[0:3]) for l1 in ('3', '5', '7')] + + [(l2 in l[3:5]) for l2 in ('8', '11')] + + [(l3 in l[5:]) for l3 in ('10', '9', '2')])