Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
Reviewer's suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
Michele Borassi committed Aug 14, 2015
1 parent 8df530a commit 0f855c6
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 142 deletions.
142 changes: 48 additions & 94 deletions src/sage/graphs/base/static_sparse_graph.pyx
Expand Up @@ -471,15 +471,8 @@ cdef int _tarjan_strongly_connected_components(short_digraph g, int *scc):
cdef int *dfs_stack = <int *> mem.malloc((n_edges(g) + 1) * sizeof(int))
cdef int dfs_stack_end
cdef int *scc_stack = <int *> mem.malloc(n * sizeof(int)) # Used to keep track of which nodes are in the "current" SCC
cdef bitset_t in_scc_stack
cdef short *in_scc_stack = <short *> mem.calloc(n, sizeof(short))
cdef uint32_t *p_tmp
# Otherwise, bitset_init creates problems
if n == 0:
return 0

bitset_init(in_scc_stack, n)
bitset_set_first_n(in_scc_stack, 0)

cdef short *visited = <short *> mem.calloc(n, sizeof(short))
# The variable visited[v] is 0 if the vertex has never been visited, 1 if
# it is an ancestor of the current vertex, 2 otherwise.
Expand All @@ -505,7 +498,7 @@ cdef int _tarjan_strongly_connected_components(short_digraph g, int *scc):
# We add v to the stack of vertices in the current SCC
scc_stack[scc_stack_end] = v
scc_stack_end = scc_stack_end + 1
bitset_add(in_scc_stack, v)
in_scc_stack[v] = 1

# We iterate over all neighbors of v
p_tmp = g.neighbors[v]
Expand All @@ -517,7 +510,7 @@ cdef int _tarjan_strongly_connected_components(short_digraph g, int *scc):
pred[w] = v
dfs_stack[dfs_stack_end] = w
dfs_stack_end += 1
elif bitset_in(in_scc_stack, w):
elif in_scc_stack[w]:
# We update the lowlink of v (later, we will "pass"
# this updated value to all ancestors of v.
lowlink[v] = min(lowlink[v], lowlink[w])
Expand All @@ -539,12 +532,11 @@ cdef int _tarjan_strongly_connected_components(short_digraph g, int *scc):
while w != v:
scc_stack_end -= 1
w = scc_stack[scc_stack_end]
bitset_remove(in_scc_stack, w)
in_scc_stack[w] = 0
scc[w] = currentscc
currentscc += 1
visited[v] = 2

bitset_free(in_scc_stack)
return currentscc


Expand Down Expand Up @@ -582,33 +574,63 @@ def tarjan_strongly_connected_components(G):
sage: from sage.graphs.base.static_sparse_graph import tarjan_strongly_connected_components
sage: tarjan_strongly_connected_components(digraphs.Path(3))
[3, {0: 2, 1: 1, 2: 0}]
[[2], [1], [0]]
sage: D = DiGraph( { 0 : [1, 3], 1 : [2], 2 : [3], 4 : [5, 6], 5 : [6] } )
sage: D.connected_components()
[[0, 1, 2, 3], [4, 5, 6]]
sage: D = DiGraph( { 0 : [1, 3], 1 : [2], 2 : [3], 4 : [5, 6], 5 : [6] } )
sage: D.strongly_connected_components()
[[3], [2], [1], [0], [6], [5], [4]]
sage: D.add_edge([2,0])
sage: D.strongly_connected_components()
[[3], [0, 1, 2], [6], [5], [4]]
TESTS::
TESTS:
Checking that the result is correct::
sage: from sage.graphs.base.static_sparse_graph import tarjan_strongly_connected_components
sage: import random
sage: for i in range(100):
sage: for i in range(100): # long
....: n = random.randint(2,20)
....: m = random.randint(1, n*(n-1))
....: g = digraphs.RandomDirectedGNM(n,m)
....: nscc, sccs = tarjan_strongly_connected_components(g)
....: for v in range(n):
....: scc_check = g.strongly_connected_component_containing_vertex(v)
....: for w in range(n):
....: assert((sccs[w]==sccs[scc_check[0]]) == (w in scc_check))
....: sccs = tarjan_strongly_connected_components(g)
....: for scc in sccs:
....: scc_check = g.strongly_connected_component_containing_vertex(scc[0])
....: assert(sorted(scc) == sorted(scc_check))
Checking against NetworkX::
sage: import networkx
sage: for i in range(100): # long
... g = digraphs.RandomDirectedGNP(100,.05)
... h = g.networkx_graph()
... scc1 = g.strongly_connected_components()
... scc2 = networkx.strongly_connected_components(h)
... s1 = Set(map(Set,scc1))
... s2 = Set(map(Set,scc2))
... if s1 != s2:
... print "Ooch !"
"""
from sage.graphs.digraph import DiGraph

if not isinstance(G, DiGraph):
raise ValueError("G must be a DiGraph.")

sig_on()
cdef MemoryAllocator mem = MemoryAllocator()
cdef short_digraph g
init_short_digraph(g, G)
cdef int * scc = <int*> mem.malloc(g.n * sizeof(int))
sig_on()
cdef int nscc = _tarjan_strongly_connected_components(g, scc)

d = {i:list() for i in range(nscc)}

for i,v in enumerate(G.vertices()):
d[scc[i]].append(v)
sig_off()
return [nscc, {v:scc[i] for i,v in enumerate(G.vertices())}]
return [d[i] for i in range(nscc)]

cdef void _strongly_connected_components_digraph(short_digraph g, int nscc, int *scc, short_digraph output):
r"""
Expand All @@ -630,12 +652,10 @@ cdef void _strongly_connected_components_digraph(short_digraph g, int nscc, int
cdef vector[vector[int]] scc_list = vector[vector[int]](nscc, vector[int]())
cdef vector[vector[int]] sons = vector[vector[int]](nscc + 1, vector[int]())
cdef vector[int].iterator iter
cdef bitset_t neighbors
cdef short *neighbors = <short *> mem.calloc(nscc, sizeof(short))
cdef long m = 0
cdef uint32_t degv
cdef uint32_t *p_tmp
bitset_init(neighbors, nscc)
bitset_set_first_n(neighbors, 0)

for v in range(nscc):
scc_list[v] = vector[int]()
Expand All @@ -651,12 +671,12 @@ cdef void _strongly_connected_components_digraph(short_digraph g, int nscc, int
while p_tmp<g.neighbors[scc_list[v][i]+1]:
w = <int> scc[p_tmp[0]]
p_tmp += 1
if bitset_not_in(neighbors, w) and not w == v:
bitset_add(neighbors, w)
if neighbors[w] == 0 and not w == v:
neighbors[w] = 1
sons[v].push_back(w)
m += 1
for w in range(sons[v].size()):
bitset_remove(neighbors, sons[v][w])
neighbors[sons[v][w]] = 0

output.n = nscc
output.m = m
Expand All @@ -674,8 +694,6 @@ cdef void _strongly_connected_components_digraph(short_digraph g, int nscc, int
for i in range(sons[v].size()):
output.neighbors[v][i] = sons[v][i]

bitset_free(neighbors)

def strongly_connected_components_digraph(G):
r"""
Returns the digraph of the strongly connected components (SCCs).
Expand Down Expand Up @@ -752,70 +770,6 @@ cdef void free_short_digraph(short_digraph g):
if g.edge_labels != NULL:
cpython.Py_XDECREF(g.edge_labels)

def strongly_connected_components(G):
r"""
Returns the strongly connected components of the given DiGraph.
INPUT:
- ``G`` -- a DiGraph.
.. NOTE::
This method has been written as an attempt to solve the slowness
reported in :trac:`12235`. It is not the one used by
:meth:`sage.graphs.digraph.DiGraph.strongly_connected_components` as
saving some time on the computation of the strongly connected components
is not worth copying the whole graph, but it is a nice way to test this
module's functions. It is also tested in the doctest or
:meth:`sage.graphs.digraph.DiGraph.strongly_connected_components`.
EXAMPLE::
sage: from sage.graphs.base.static_sparse_graph import strongly_connected_components
sage: g = digraphs.ButterflyGraph(2)
sage: strongly_connected_components(g)
[[('00', 0)], [('00', 1)], [('00', 2)], [('01', 0)], [('01', 1)], [('01', 2)],
[('10', 0)], [('10', 1)], [('10', 2)], [('11', 0)], [('11', 1)], [('11', 2)]]
"""

if G.order() == 0:
return [[]]

# To compute the connected component containing a given vertex v, we take
# the intersection of the set of vertices that can be reached from v in G
# and the set of vertices that can be reached from v in G reversed.
#
# That's all that happens here.

cdef list answer = []
cdef list vertices = G.vertices()
cdef short_digraph g, gr

init_short_digraph(g, G)
init_reverse(gr, g)

cdef bitset_t seen
bitset_init(seen, g.n)
bitset_set_first_n(seen, 0)

cdef bitset_t scc
bitset_init(scc, g.n)
bitset_set_first_n(scc, 0)

cdef int v
while bitset_len(seen) < g.n:
v = bitset_first_in_complement(seen)
strongly_connected_component_containing_vertex(g, gr, v, scc)
answer.append([vertices[i] for i in bitset_list(scc)])
bitset_union(seen, seen, scc)

bitset_free(seen)
bitset_free(scc)
free_short_digraph(g)
free_short_digraph(gr)
return answer

def triangles_count(G):
r"""
Return the number of triangles containing `v`, for every `v`.
Expand Down
51 changes: 3 additions & 48 deletions src/sage/graphs/digraph.py
Expand Up @@ -3073,54 +3073,6 @@ def level_sets(self):
level = new_level
return Levels

def strongly_connected_components(self):
"""
Returns the list of strongly connected components.
EXAMPLES::
sage: D = DiGraph( { 0 : [1, 3], 1 : [2], 2 : [3], 4 : [5, 6], 5 : [6] } )
sage: D.connected_components()
[[0, 1, 2, 3], [4, 5, 6]]
sage: D = DiGraph( { 0 : [1, 3], 1 : [2], 2 : [3], 4 : [5, 6], 5 : [6] } )
sage: D.strongly_connected_components()
[[3], [2], [1], [0], [6], [5], [4]]
sage: D.add_edge([2,0])
sage: D.strongly_connected_components()
[[3], [0, 1, 2], [6], [5], [4]]
TESTS:
Checking against NetworkX, and another of Sage's implementations::
sage: from sage.graphs.base.static_sparse_graph import strongly_connected_components
sage: import networkx
sage: for i in range(100): # long
... g = digraphs.RandomDirectedGNP(100,.05) # long
... h = g.networkx_graph() # long
... scc1 = g.strongly_connected_components() # long
... scc2 = networkx.strongly_connected_components(h) # long
... scc3 = strongly_connected_components(g) # long
... s1 = Set(map(Set,scc1)) # long
... s2 = Set(map(Set,scc2)) # long
... s3 = Set(map(Set,scc3)) # long
... if s1 != s2: # long
... print "Ooch !" # long
... if s1 != s3: # long
... print "Oooooch !" # long
"""
from sage.graphs.base.static_sparse_graph import tarjan_strongly_connected_components
import numpy as np
nscc, scc = tarjan_strongly_connected_components(self)
filler = np.frompyfunc(lambda x: list(), 1, 1)
output = np.empty((nscc), dtype=np.object)
filler(output, output)

for v in self.vertices():
output[scc[v]].append(v)
return output.tolist()

def strongly_connected_component_containing_vertex(self, v):
"""
Returns the strongly connected component containing a given vertex
Expand Down Expand Up @@ -3578,3 +3530,6 @@ def flow_polytope(self, edges=None, ends=None):

import sage.graphs.comparability
DiGraph.is_transitive = types.MethodType(sage.graphs.comparability.is_transitive, None, DiGraph)

from sage.graphs.base.static_sparse_graph import tarjan_strongly_connected_components
DiGraph.strongly_connected_components = types.MethodType(tarjan_strongly_connected_components, None, DiGraph)

0 comments on commit 0f855c6

Please sign in to comment.