Skip to content

Commit 18b630d

Browse files
committed
Merge pull request JuliaAttic#153 from JuliaGraphs/opt/connected-components
Opt/connected components
2 parents 6b51378 + 6c28c9c commit 18b630d

File tree

5 files changed

+134
-44
lines changed

5 files changed

+134
-44
lines changed

bench/connectivity.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function loadmat(matname)
2929
return g
3030
end
3131

32-
names = ["Newman/football" , "Newman/cond-mat-2003", "SNAP/amazon0302"]#, "SNAP/roadNet-CA"]
32+
names = ["Newman/football" , "Newman/cond-mat-2003", "SNAP/amazon0302","SNAP/roadNet-CA"]
3333
sucesses = []
3434
failures = []
3535
for matname in names
@@ -40,7 +40,11 @@ for matname in names
4040
label = zeros(Int, nv(g))
4141
@time label = LightGraphs.connected_components!(label, g)
4242
@time components = LightGraphs.connected_components!(visitor, g)
43+
fill!(visitor.tree, 0)
44+
@time LightGraphs.bfs_tree!(visitor, g, 1)
4345
@show length(components)
46+
#=
4447
@time components_slow = LightGraphs.connected_components(g)
4548
@assert length(components) == length(components_slow)
49+
=#
4650
end

src/connectivity.jl

Lines changed: 90 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,36 @@
55
of an undirected graph `g` as a vector of components, each represented by a
66
vector of vectors of vertices belonging to the component.
77
"""
8-
function connected_components(g::Graph)
9-
nvg = nv(g)
10-
found = zeros(Bool, nvg)
11-
components = @compat Vector{Vector{Int}}()
12-
for v in 1:nvg
13-
if !found[v]
14-
bfstree = bfs_tree(g, v)
15-
found_vertices = @compat Vector{Int}()
16-
for e in edges(bfstree)
17-
push!(found_vertices, src(e))
18-
push!(found_vertices, dst(e))
19-
end
20-
found_vertices = unique(found_vertices)
21-
found[found_vertices] = true
22-
if length(found_vertices) > 0
23-
push!(components, found_vertices)
24-
end
25-
end
26-
end
27-
return components
28-
end
8+
#= function connected_components(g::Graph) =#
9+
#= nvg = nv(g) =#
10+
#= found = zeros(Bool, nvg) =#
11+
#= components = @compat Vector{Vector{Int}}() =#
12+
#= for v in 1:nvg =#
13+
#= if !found[v] =#
14+
#= bfstree = bfs_tree(g, v) =#
15+
#= found_vertices = @compat Vector{Int}() =#
16+
#= for e in edges(bfstree) =#
17+
#= push!(found_vertices, src(e)) =#
18+
#= push!(found_vertices, dst(e)) =#
19+
#= end =#
20+
#= found_vertices = unique(found_vertices) =#
21+
#= found[found_vertices] = true =#
22+
#= if length(found_vertices) > 0 =#
23+
#= push!(components, found_vertices) =#
24+
#= end =#
25+
#= end =#
26+
#= end =#
27+
#= return components =#
28+
#= end =#
2929

3030
function connected_components!(visitor::TreeBFSVisitorVector, g::Graph)
3131
nvg = nv(g)
3232
found = zeros(Bool, nvg)
3333
components = @compat Vector{Vector{Int}}()
3434
for v in 1:nvg
3535
if !found[v]
36-
visitor.tree[:] = 0
36+
fill!(visitor.tree, 0)
37+
visitor.tree[v] = v
3738
parents = bfs_tree!(visitor, g, v)
3839
found_vertices = @compat Vector{Int}()
3940
for i in 1:nvg
@@ -50,23 +51,82 @@ function connected_components!(visitor::TreeBFSVisitorVector, g::Graph)
5051
return components
5152
end
5253

54+
"""connected_components! produces a label array of components
55+
56+
Arguments:
57+
label: a place to store the output
58+
g: the graph
59+
Output:
60+
c = labels[i] => vertex i belongs to component c.
61+
c is the smallest vertex id in the component.
62+
"""
5363
function connected_components!(label::Vector{Int}, g::Graph)
5464
nvg = nv(g)
55-
visitor = LightGraphs.TreeBFSVisitorVector(zeros(Int, nv(g)))
65+
visitor = LightGraphs.ComponentVisitorVector(label, 0)
66+
colormap = zeros(Int,nvg)
67+
que = @compat Vector{Int}()
68+
sizehint!(que, nvg)
5669
for v in 1:nvg
5770
if label[v] == 0
58-
visitor.tree[:] = 0
59-
parents = bfs_tree!(visitor, g, v)
60-
for i in 1:nvg
61-
if parents[i] > 0
62-
label[i] = v
63-
end
64-
end
71+
visitor.labels[v] = v
72+
visitor.seed = v
73+
traverse_graph(g, BreadthFirst(), v, visitor; colormap=colormap, que=que)
6574
end
6675
end
6776
return label
6877
end
6978

79+
"""components_dict(labels) converts an array of labels to a Dict{Int,Vector{Int}} of components
80+
81+
Arguments:
82+
c = labels[i] => vertex i belongs to component c.
83+
Output:
84+
vs = d[c] => vertices in vs belong to component c.
85+
"""
86+
function components_dict(labels::Vector{Int})
87+
d = Dict{Int,Vector{Int}}()
88+
for (v,l) in enumerate(labels)
89+
vec = get(d, l, @compat Vector{Int}())
90+
push!(vec, v)
91+
d[l] = vec
92+
end
93+
return d
94+
end
95+
96+
"""components(labels) converts an array of labels to a Vector{Vector{Int} of components
97+
98+
Arguments:
99+
c = labels[i] => vertex i belongs to component c.
100+
Output:
101+
vs = c[i] => vertices in vs belong to component i.
102+
a = d[i] => if label[v[j]]==i then j in c[a] end
103+
"""
104+
function components(labels::Vector{Int})
105+
d = Dict{Int, Int}()
106+
c = Vector{Vector{Int}}()
107+
i = 1
108+
for (v,l) in enumerate(labels)
109+
index = get(d, l, i)
110+
d[l] = index
111+
if length(c) >= index
112+
vec = c[index]
113+
push!(vec, v)
114+
c[index] = vec
115+
else
116+
push!(c, [v])
117+
i += 1
118+
end
119+
end
120+
return c, d
121+
end
122+
123+
function connected_components(g)
124+
label = zeros(Int, nv(g))
125+
connected_components!(label, g)
126+
c, d = components(label)
127+
return c
128+
end
129+
70130
"""Returns `true` if `g` is connected.
71131
For DiGraphs, this is equivalent to a test of weak connectivity."""
72132
is_connected(g::Graph) = length(connected_components(g)) == 1

src/traversals/bfs.jl

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,13 @@ function breadth_first_visit_impl!(
4242
nothing
4343
end
4444

45-
4645
function traverse_graph(
4746
graph::SimpleGraph,
4847
alg::BreadthFirst,
4948
s::Int,
5049
visitor::SimpleGraphVisitor;
51-
colormap = zeros(Int, nv(graph)))
52-
53-
que = @compat Vector{Int}()
50+
colormap = zeros(Int, nv(graph)),
51+
que = @compat Vector{Int}())
5452

5553
colormap[s] = 1
5654
discover_vertex!(visitor, s) || return
@@ -59,15 +57,13 @@ function traverse_graph(
5957
breadth_first_visit_impl!(graph, que, colormap, visitor)
6058
end
6159

62-
6360
function traverse_graph(
6461
graph::SimpleGraph,
6562
alg::BreadthFirst,
6663
sources::AbstractVector{Int},
6764
visitor::SimpleGraphVisitor;
68-
colormap = zeros(Int, nv(graph)))
69-
70-
que = @compat Vector{Int}()
65+
colormap = zeros(Int, nv(graph)),
66+
que = @compat Vector{Int}())
7167

7268
for s in sources
7369
colormap[s] = 1
@@ -136,13 +132,25 @@ type TreeBFSVisitorVector <: SimpleGraphVisitor
136132
tree::Vector{Int}
137133
end
138134

135+
type ComponentVisitorVector <: SimpleGraphVisitor
136+
labels::Vector{Int}
137+
seed::Int
138+
end
139139
function examine_neighbor!(visitor::TreeBFSVisitorVector, u::Int, v::Int, vcolor::Int, ecolor::Int)
140140
# println("discovering $u -> $v, vcolor = $vcolor, ecolor = $ecolor")
141141
if u != v && vcolor == 0
142142
visitor.tree[v] = u
143143
end
144144
return true
145145
end
146+
147+
function examine_neighbor!(visitor::ComponentVisitorVector, u::Int, v::Int, vcolor::Int, ecolor::Int)
148+
# println("discovering $u -> $v, vcolor = $vcolor, ecolor = $ecolor")
149+
if u != v && vcolor == 0
150+
visitor.labels[v] = visitor.seed
151+
end
152+
return true
153+
end
146154
# Return the DAG representing the traversal of a graph.
147155

148156
TreeBFSVisitor(n::Int) = TreeBFSVisitor(DiGraph(n))
@@ -172,8 +180,12 @@ function bfs_tree(visitor::TreeBFSVisitorVector, g::SimpleGraph, s::Int)
172180
return bfs_tree!(visitor, g, s)
173181
end
174182

175-
function bfs_tree!(visitor::TreeBFSVisitorVector, g::SimpleGraph, s::Int)
176-
traverse_graph(g, BreadthFirst(), s, visitor)
183+
function bfs_tree!(visitor::TreeBFSVisitorVector,
184+
g::SimpleGraph,
185+
s::Int;
186+
colormap=zeros(Int, nv(g)),
187+
que=Vector{Int}())
188+
traverse_graph(g, BreadthFirst(), s, visitor; colormap=colormap, que=que)
177189
return visitor.tree
178190
end
179191

test/connectivity.jl

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,20 @@ add_edge!(g,10,9)
1010
@test is_connected(HouseGraph())
1111

1212
cc = connected_components(g)
13-
14-
15-
@test length(cc) == 3 && sort(cc[3]) == [8,9,10]
13+
visitor = LightGraphs.TreeBFSVisitorVector(zeros(Int, nv(g)))
14+
label = zeros(Int, nv(g))
15+
label = LightGraphs.connected_components!(label, g)
16+
ccfast = LightGraphs.connected_components!(visitor, g)
17+
@test label[1:10] == [1,1,1,1,5,5,5,8,8,8]
18+
@test ccfast[1:3] == map(sort, cc)[1:3]
19+
import LightGraphs: components, components_dict
20+
cclab = components_dict(label)
21+
@test cclab[1] == [1,2,3,4]
22+
@test cclab[5] == [5,6,7]
23+
@test cclab[8] == [8,9,10]
24+
25+
26+
@test length(cc) >= 3 && sort(cc[3]) == [8,9,10]
1627

1728

1829
# graph from https://en.wikipedia.org/wiki/Strongly_connected_component

test/traversals/bfs.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
z = bfs_tree(g5, 1)
2+
visitor = LightGraphs.TreeBFSVisitorVector(zeros(Int,nv(g5)))
3+
t = LightGraphs.bfs_tree(visitor, g5, 1)
24
@test nv(z) == 4 && ne(z) == 3 && !has_edge(z, 2, 3)
5+
@test t == [1,1,1,3]
36

47
g = HouseGraph()
58
@test gdistances(g, 2) == [1, 0, 2, 1, 2]

0 commit comments

Comments
 (0)