Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Kosaraju's algorithm for SCC #233

Merged
merged 3 commits into from
Mar 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pydatastructs/graphs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
breadth_first_search,
breadth_first_search_parallel,
minimum_spanning_tree,
minimum_spanning_tree_parallel
minimum_spanning_tree_parallel,
strongly_connected_components
)

__all__.extend(algorithms.__all__)
105 changes: 104 additions & 1 deletion pydatastructs/graphs/algorithms.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
'breadth_first_search',
'breadth_first_search_parallel',
'minimum_spanning_tree',
'minimum_spanning_tree_parallel'
'minimum_spanning_tree_parallel',
'strongly_connected_components'
]

def breadth_first_search(
Expand Down Expand Up @@ -445,3 +446,105 @@ def minimum_spanning_tree_parallel(graph, algorithm, num_threads):
"isn't implemented for finding minimum spanning trees."
%(algorithm, graph._impl))
return getattr(algorithms, func)(graph, num_threads)

def _visit(graph, vertex, visited, incoming, L):
stack = [vertex]
while stack:
top = stack[-1]
if not visited.get(top, False):
visited[top] = True
for node in graph.neighbors(top):
if incoming.get(node.name, None) is None:
incoming[node.name] = []
incoming[node.name].append(top)
if not visited.get(node.name, False):
stack.append(node.name)
if top is stack[-1]:
L.append(stack.pop())

def _assign(graph, u, incoming, assigned, component):
stack = [u]
while stack:
top = stack[-1]
if not assigned.get(top, False):
assigned[top] = True
component.add(top)
for u in incoming[top]:
if not assigned.get(u, False):
stack.append(u)
if top is stack[-1]:
stack.pop()

def _strongly_connected_components_kosaraju_adjacency_list(graph):
visited, incoming, L = dict(), dict(), []
for u in graph.vertices:
if not visited.get(u, False):
_visit(graph, u, visited, incoming, L)

assigned = dict()
components = []
for i in range(-1, -len(L) - 1, -1):
comp = set()
if not assigned.get(L[i], False):
_assign(graph, L[i], incoming, assigned, comp)
if comp:
components.append(comp)

return components

_strongly_connected_components_kosaraju_adjacency_matrix = \
_strongly_connected_components_kosaraju_adjacency_list

def strongly_connected_components(graph, algorithm):
"""
Computes strongly connected components for the given
graph and algorithm.

Parameters
==========

graph: Graph
The graph whose minimum spanning tree
has to be computed.
algorithm: str
The algorithm which should be used for
computing strongly connected components.
Currently the following algorithms are
supported,
'kosaraju' -> Kosaraju's algorithm as given in
[1].

Returns
=======

components: list
Python list with each element as set of vertices.

Examples
========

>>> from pydatastructs import Graph, AdjacencyListGraphNode
>>> from pydatastructs import strongly_connected_components
>>> v1, v2, v3 = [AdjacencyListGraphNode(i) for i in range(3)]
>>> g = Graph(v1, v2, v3)
>>> g.add_edge(v1.name, v2.name)
>>> g.add_edge(v2.name, v3.name)
>>> g.add_edge(v3.name, v1.name)
>>> scc = strongly_connected_components(g, 'kosaraju')
>>> scc == [{'2', '0', '1'}]
True

References
==========

.. [1] https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm

"""
import pydatastructs.graphs.algorithms as algorithms
func = "_strongly_connected_components_" + algorithm + "_" + graph._impl
if not hasattr(algorithms, func):
raise NotImplementedError(
"Currently %s algoithm for %s implementation of graphs "
"isn't implemented for finding strongly connected components."
%(algorithm, graph._impl))
return getattr(algorithms, func)(graph)
32 changes: 31 additions & 1 deletion pydatastructs/graphs/tests/test_algorithms.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from pydatastructs import (breadth_first_search, Graph,
breadth_first_search_parallel, minimum_spanning_tree,
minimum_spanning_tree_parallel)
minimum_spanning_tree_parallel, strongly_connected_components)


def test_breadth_first_search():
Expand Down Expand Up @@ -155,3 +155,33 @@ def _test_minimum_spanning_tree(func, ds, algorithm, *args):
_test_minimum_spanning_tree(fmstp, "List", "kruskal", 3)
_test_minimum_spanning_tree(fmstp, "Matrix", "kruskal", 3)
_test_minimum_spanning_tree(fmstp, "List", "prim", 3)

def test_strongly_connected_components():

def _test_strongly_connected_components(func, ds, algorithm, *args):
import pydatastructs.utils.misc_util as utils
GraphNode = getattr(utils, "Adjacency" + ds + "GraphNode")
a, b, c, d, e, f, g, h = \
[GraphNode(chr(x)) for x in range(ord('a'), ord('h') + 1)]
graph = Graph(a, b, c, d, e, f, g, h)
graph.add_edge(a.name, b.name)
graph.add_edge(b.name, c.name)
graph.add_edge(b.name, f.name)
graph.add_edge(b.name, e.name)
graph.add_edge(c.name, d.name)
graph.add_edge(c.name, g.name)
graph.add_edge(d.name, h.name)
graph.add_edge(d.name, c.name)
graph.add_edge(e.name, f.name)
graph.add_edge(e.name, a.name)
graph.add_edge(f.name, g.name)
graph.add_edge(g.name, f.name)
graph.add_edge(h.name, d.name)
graph.add_edge(h.name, g.name)
comps = func(graph, algorithm)
expected_comps = [{'e', 'a', 'b'}, {'d', 'c', 'h'}, {'g', 'f'}]
assert comps == expected_comps

scc = strongly_connected_components
_test_strongly_connected_components(scc, "List", "kosaraju")
_test_strongly_connected_components(scc, "Matrix", "kosaraju")