From 9873d0f6274e5843594187e6c60bb2045cdc48a0 Mon Sep 17 00:00:00 2001 From: CarloLucibello Date: Tue, 12 Apr 2016 09:59:45 +0200 Subject: [PATCH 01/17] small improvement to connected_components --- src/connectivity.jl | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/connectivity.jl b/src/connectivity.jl index 8e2e7a653..382c6867d 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -2,7 +2,10 @@ # licensing details. -"""connected_components! produces a label array of components +""" + connected_components!(label::Vector{Int}, g::SimpleGraph) + +Fills `label` with the `id` of the connected component to which it belongs. Arguments: label: a place to store the output @@ -51,25 +54,25 @@ function components_dict(labels::Vector{Int}) return d end -"""components(labels) converts an array of labels to a Vector{Vector{Int}} of components +""" + components(labels::Vector{Int}) + +Converts an array of labels to a Vector{Vector{Int}} of components Arguments: c = labels[i] => vertex i belongs to component c. Output: vs = c[i] => vertices in vs belong to component i. - a = d[i] => if label[v[j]]==i then j in c[a] end + a = d[i] => if labels[v]==i then v in c[a] end """ function components(labels::Vector{Int}) d = Dict{Int, Int}() c = Vector{Vector{Int}}() i = 1 for (v,l) in enumerate(labels) - index = get(d, l, i) - d[l] = index + index = get!(d, l, i) if length(c) >= index - vec = c[index] - push!(vec, v) - c[index] = vec + push!(c[index], v) else push!(c, [v]) i += 1 @@ -78,19 +81,26 @@ function components(labels::Vector{Int}) return c, d end -"""Returns the [connected components](https://en.wikipedia.org/wiki/Connectivity_(graph_theory)) -of an undirected graph `g` as a vector of components, each represented by a -vector of vectors of vertices belonging to the component. """ -function connected_components(g) + connected_components(g) + +Returns the [connected components](https://en.wikipedia.org/wiki/Connectivity_(graph_theory)) +of `g` as a vector of components, each represented by a +vector of vertices belonging to the component. +""" +function connected_components(g::SimpleGraph) label = zeros(Int, nv(g)) connected_components!(label, g) c, d = components(label) return c end -"""Returns `true` if `g` is connected. -For DiGraphs, this is equivalent to a test of weak connectivity.""" +""" + is_connected(g) + +Returns `true` if `g` is connected. +For DiGraphs, this is equivalent to a test of weak connectivity. +""" is_connected(g::Graph) = length(connected_components(g)) == 1 is_connected(g::DiGraph) = is_weakly_connected(g) From 042c7b5e3d77066f30be8b3a146bd5fdfe38c98e Mon Sep 17 00:00:00 2001 From: CarloLucibello Date: Sun, 10 Apr 2016 16:42:12 +0200 Subject: [PATCH 02/17] add neighborhood --- doc/build.jl | 4 +++- doc/pathing.md | 7 +++++++ src/LightGraphs.jl | 4 ++-- src/connectivity.jl | 26 ++++++++++++++++++++++++++ test/connectivity.jl | 19 +++++++++++++++++++ 5 files changed, 57 insertions(+), 3 deletions(-) diff --git a/doc/build.jl b/doc/build.jl index d89ce8974..8e08e10d4 100644 --- a/doc/build.jl +++ b/doc/build.jl @@ -354,7 +354,9 @@ Any graph traversal will traverse an edge only if it is present in the graph. W ## Connectivity / Bipartiteness `Graph connectivity` functions are defined on both undirected and directed graphs: -{{is_connected, is_strongly_connected, is_weakly_connected, connected_components, strongly_connected_components, weakly_connected_components, has_self_loop, attracting_components, is_bipartite, condensation, period}} +{{is_connected, is_strongly_connected, is_weakly_connected, connected_components, + strongly_connected_components, weakly_connected_components, has_self_loop, + attracting_components, is_bipartite, condensation, period, neighborhood}} ## Cycle Detection In graph theory, a cycle is defined to be a path that starts from some vertex diff --git a/doc/pathing.md b/doc/pathing.md index b9b556618..8c35ce6f8 100644 --- a/doc/pathing.md +++ b/doc/pathing.md @@ -131,6 +131,13 @@ period(g::LightGraphs.DiGraph) ``` Computes the (common) period for all nodes in a strongly connected graph. +### neighborhood +```julia +neighborhood(g, v::Int, d::Int; dir=:out) +``` + +Returns a vector of the vertices in `g` at distance less or equal to `d` from `v`. If `g` is a `DiGraph` the `dir` optional argument specifies the edge direction with respect to `v` (i.e. `:in` or `:out`) to be considered. + ## Cycle Detection In graph theory, a cycle is defined to be a path that starts from some vertex `v` and ends up at `v`. diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index 47a04724d..3eda359ef 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -62,7 +62,7 @@ randomwalk, saw, non_backtracking_randomwalk, # connectivity connected_components, strongly_connected_components, weakly_connected_components, is_connected, is_strongly_connected, is_weakly_connected, period, -condensation, attracting_components, +condensation, attracting_components, neighborhood, # cliques maximal_cliques, @@ -100,7 +100,7 @@ maximum_weight_maximal_matching, MatchingResult, # randgraphs erdos_renyi, watts_strogatz, random_regular_graph, random_regular_digraph, random_configuration_model, StochasticBlockModel, make_edgestream, nearbipartiteSBM, blockcounts, blockfractions, -stochastic_block_model, +stochastic_block_model, #community modularity, community_detection_nback, core_periphery_deg, diff --git a/src/connectivity.jl b/src/connectivity.jl index 382c6867d..1a6eab4ab 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -237,3 +237,29 @@ function attracting_components(g::DiGraph) end return scc[attracting] end + +""" + neighborhood(g, v::Int, d::Int; dir=:out) + +Returns a vector of the vertices in `g` at distance less or equal to `d` +from `v`. If `g` is a `DiGraph` the `dir` optional argument specifies the edge direction +with respect to `v` (i.e. `:in` or `:out`) to be considered. +""" +function neighborhood(g::SimpleGraph, v::Int, d::Int; dir=:out) + neig = Set{Int}(v) + ∂neig = copy(neig) + fneig = dir == :out ? out_neighbors : in_neighbors + for l=1:d + newneigs = Set{Int}() + for i in ∂neig + for j in fneig(g, i) + if j ∉ neig + push!(newneigs, j) + push!(neig, j) + end + end + end + ∂neig = newneigs + end + return collect(neig) +end diff --git a/test/connectivity.jl b/test/connectivity.jl index d6a374be8..326f75b41 100644 --- a/test/connectivity.jl +++ b/test/connectivity.jl @@ -132,3 +132,22 @@ fig8 = DiGraph(fig8) @test attracting_components(fig1) == Vector[[2,5]] @test attracting_components(fig3) == Vector[[3,4],[8]] + +g10 = StarGraph(10) +@test neighborhood(g10, 1 , 0) == [1] +@test length(neighborhood(g10, 1, 1)) == 10 +@test length(neighborhood(g10, 2, 1)) == 2 +@test length(neighborhood(g10, 1, 2)) == 10 +@test length(neighborhood(g10, 2, 2)) == 10 + +g10 = StarDiGraph(10) +@test neighborhood(g10, 1 , 0, dir=:out) == [1] +@test length(neighborhood(g10, 1, 1, dir=:out)) == 10 +@test length(neighborhood(g10, 2, 1, dir=:out)) == 1 +@test length(neighborhood(g10, 1, 2, dir=:out)) == 10 +@test length(neighborhood(g10, 2, 2, dir=:out)) == 1 +@test neighborhood(g10, 1 , 0, dir=:in) == [1] +@test length(neighborhood(g10, 1, 1, dir=:in)) == 1 +@test length(neighborhood(g10, 2, 1, dir=:in)) == 2 +@test length(neighborhood(g10, 1, 2, dir=:in)) == 1 +@test length(neighborhood(g10, 2, 2, dir=:in)) == 2 From 4a09f3b7b36da9e152643c34e265d842d015f32e Mon Sep 17 00:00:00 2001 From: CarloLucibello Date: Mon, 11 Apr 2016 09:16:33 +0200 Subject: [PATCH 03/17] traverse_graph -> traverse_graph! --- src/LightGraphs.jl | 2 +- src/connectivity.jl | 4 ++-- src/traversals/bfs.jl | 12 ++++++------ src/traversals/dfs.jl | 8 ++++---- src/traversals/graphvisit.jl | 4 ++-- src/traversals/maxadjvisit.jl | 6 +++--- test/traversals/graphvisit.jl | 6 +++--- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index 3eda359ef..805ed8839 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -48,7 +48,7 @@ crosspath, # graph visit SimpleGraphVisitor, TrivialGraphVisitor, LogGraphVisitor, discover_vertex!, open_vertex!, close_vertex!, -examine_neighbor!, visited_vertices, traverse_graph, traverse_graph_withlog, +examine_neighbor!, visited_vertices, traverse_graph!, traverse_graph_withlog, # bfs BreadthFirst, gdistances, gdistances!, bfs_tree, is_bipartite, bipartite_map, diff --git a/src/connectivity.jl b/src/connectivity.jl index 1a6eab4ab..076e82eb7 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -31,7 +31,7 @@ function connected_components!(label::Vector{Int}, g::SimpleGraph) if label[v] == 0 visitor.labels[v] = v visitor.seed = v - traverse_graph(g, BreadthFirst(), v, visitor; colormap=colormap, que=que) + traverse_graph!(g, BreadthFirst(), v, visitor; colormap=colormap, que=que) end end return label @@ -160,7 +160,7 @@ function strongly_connected_components(g::DiGraph) for v in vertices(g) if cmap[v] == 0 # 0 means not visited yet visitor = TarjanVisitor(nvg) - traverse_graph(g, DepthFirst(), v, visitor, vertexcolormap=cmap) + traverse_graph!(g, DepthFirst(), v, visitor, vertexcolormap=cmap) for component in visitor.components push!(components, component) end diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index d1c109500..3c172cf38 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -42,7 +42,7 @@ function breadth_first_visit_impl!( nothing end -function traverse_graph( +function traverse_graph!( graph::SimpleGraph, alg::BreadthFirst, s::Int, @@ -57,7 +57,7 @@ function traverse_graph( breadth_first_visit_impl!(graph, que, colormap, visitor) end -function traverse_graph( +function traverse_graph!( graph::SimpleGraph, alg::BreadthFirst, sources::AbstractVector{Int}, @@ -103,7 +103,7 @@ of source vertices `ss`. function gdistances!{DMap}(graph::SimpleGraph, s::Int, dists::DMap) visitor = GDistanceVisitor(graph, dists) dists[s] = 0 - traverse_graph(graph, BreadthFirst(), s, visitor) + traverse_graph!(graph, BreadthFirst(), s, visitor) return dists end @@ -112,7 +112,7 @@ function gdistances!{DMap}(graph::SimpleGraph, sources::AbstractVector{Int}, dis for s in sources dists[s] = 0 end - traverse_graph(graph, BreadthFirst(), sources, visitor) + traverse_graph!(graph, BreadthFirst(), sources, visitor) return dists end @@ -193,7 +193,7 @@ function bfs_tree!(visitor::TreeBFSVisitorVector, nvg = nv(g) length(visitor.tree) >= nvg || error("visitor.tree too small for graph") visitor.tree[s] = s - traverse_graph(g, BreadthFirst(), s, visitor; colormap=colormap, que=que) + traverse_graph!(g, BreadthFirst(), s, visitor; colormap=colormap, que=que) end """Provides a breadth-first traversal of the graph `g` starting with source vertex `s`, @@ -259,7 +259,7 @@ end function _bipartite_visitor(g::SimpleGraph, s::Int) nvg = nv(g) visitor = BipartiteVisitor(nvg) - traverse_graph(g, BreadthFirst(), s, visitor) + traverse_graph!(g, BreadthFirst(), s, visitor) return visitor end diff --git a/src/traversals/dfs.jl b/src/traversals/dfs.jl index 24984acb7..7d674f181 100644 --- a/src/traversals/dfs.jl +++ b/src/traversals/dfs.jl @@ -64,7 +64,7 @@ function _mkedgecolormap(g::SimpleGraph, n::Integer=0) return d end -function traverse_graph( +function traverse_graph!( graph::SimpleGraph, alg::DepthFirst, s::Int, @@ -120,7 +120,7 @@ function is_cyclic(graph::SimpleGraph) for s in vertices(graph) if cmap[s] == 0 - traverse_graph(graph, DepthFirst(), s, visitor, vertexcolormap=cmap) + traverse_graph!(graph, DepthFirst(), s, visitor, vertexcolormap=cmap) end visitor.found_cycle && return true end @@ -155,7 +155,7 @@ function topological_sort_by_dfs(graph::SimpleGraph) for s in vertices(graph) if cmap[s] == 0 - traverse_graph(graph, DepthFirst(), s, visitor, vertexcolormap=cmap) + traverse_graph!(graph, DepthFirst(), s, visitor, vertexcolormap=cmap) end end @@ -183,7 +183,7 @@ and returns a directed acyclic graph of vertices in the order they were discover function dfs_tree(g::SimpleGraph, s::Int) nvg = nv(g) visitor = TreeDFSVisitor(nvg) - traverse_graph(g, DepthFirst(), s, visitor) + traverse_graph!(g, DepthFirst(), s, visitor) # visitor = traverse_dfs(g, s, TreeDFSVisitor(nvg)) h = DiGraph(nvg) for (v, u) in enumerate(visitor.predecessor) diff --git a/src/traversals/graphvisit.jl b/src/traversals/graphvisit.jl index fe14d71fe..36bc16e1e 100644 --- a/src/traversals/graphvisit.jl +++ b/src/traversals/graphvisit.jl @@ -58,7 +58,7 @@ function visited_vertices( sources) visitor = VertexListVisitor(nv(graph)) - traverse_graph(graph, alg, sources, visitor) + traverse_graph!(graph, alg, sources, visitor) visitor.vertices::Vector{Int} end @@ -96,5 +96,5 @@ function traverse_graph_withlog( io::IO = STDOUT ) visitor = LogGraphVisitor(io) - traverse_graph(g, alg, sources, visitor) + traverse_graph!(g, alg, sources, visitor) end diff --git a/src/traversals/maxadjvisit.jl b/src/traversals/maxadjvisit.jl index 23c31e16e..755cb4d14 100644 --- a/src/traversals/maxadjvisit.jl +++ b/src/traversals/maxadjvisit.jl @@ -37,7 +37,7 @@ function maximum_adjacency_visit_impl!{T}( end -function traverse_graph( +function traverse_graph!( graph::SimpleGraph, T::DataType, alg::MaximumAdjacency, @@ -184,7 +184,7 @@ function mincut{T}( ) visitor = MinCutVisitor(graph, distmx) colormap = zeros(Int, nv(graph)) - traverse_graph(graph, T, MaximumAdjacency(), 1, visitor, colormap) + traverse_graph!(graph, T, MaximumAdjacency(), 1, visitor, colormap) return(visitor.parities + 1, visitor.bestweight) end @@ -203,7 +203,7 @@ function maximum_adjacency_visit{T}( io::IO ) visitor = MASVisitor(io, Vector{Int}(), distmx, log) - traverse_graph(graph, T, MaximumAdjacency(), 1, visitor, zeros(Int, nv(graph))) + traverse_graph!(graph, T, MaximumAdjacency(), 1, visitor, zeros(Int, nv(graph))) return visitor.vertices end diff --git a/test/traversals/graphvisit.jl b/test/traversals/graphvisit.jl index c96ddc9f5..6e85e278f 100644 --- a/test/traversals/graphvisit.jl +++ b/test/traversals/graphvisit.jl @@ -14,11 +14,11 @@ function trivialgraphvisit( sources ) visitor = TrivialGraphVisitor() - traverse_graph(g, alg, sources, visitor) + traverse_graph!(g, alg, sources, visitor) end @test trivialgraphvisit(g, BreadthFirst(), 1) == nothing # this just exercises some graph visitors -@test traverse_graph(g, BreadthFirst(), 1, TrivialGraphVisitor()) == nothing -@test traverse_graph(g, BreadthFirst(), 1, LogGraphVisitor(IOBuffer())) == nothing +@test traverse_graph!(g, BreadthFirst(), 1, TrivialGraphVisitor()) == nothing +@test traverse_graph!(g, BreadthFirst(), 1, LogGraphVisitor(IOBuffer())) == nothing From d522ed121f66047dcd8c28fd99d55e3c1885f88a Mon Sep 17 00:00:00 2001 From: CarloLucibello Date: Mon, 11 Apr 2016 10:23:35 +0200 Subject: [PATCH 04/17] relax colormap type in traversal --- src/traversals/bfs.jl | 14 ++++++-------- src/traversals/dfs.jl | 4 ++-- test/traversals/bfs.jl | 36 +++++++++++++++++++++++++++--------- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index 3c172cf38..2e1c1d20c 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -13,21 +13,19 @@ type BreadthFirst <: SimpleGraphVisitAlgorithm end function breadth_first_visit_impl!( - graph::SimpleGraph, # the graph - queue::Vector{Int}, # an (initialized) queue that stores the active vertices - colormap::Vector{Int}, # an (initialized) color-map to indicate status of vertices - visitor::SimpleGraphVisitor) # the visitor + graph::SimpleGraph, # the graph + queue::Vector{Int}, # an (initialized) queue that stores the active vertices + colormap, # an (initialized) color-map to indicate status of vertices (0=unseen, 1=seen, 2=closed) + visitor::SimpleGraphVisitor) # the visitor while !isempty(queue) u = shift!(queue) open_vertex!(visitor, u) for v in out_neighbors(graph, u) - v_color::Int = colormap[v] + v_color = get(colormap, v, 0) # TODO: Incorporate edge colors to BFS - if !(examine_neighbor!(visitor, u, v, v_color, -1)) - return - end + examine_neighbor!(visitor, u, v, v_color, -1) || return if v_color == 0 colormap[v] = 1 diff --git a/src/traversals/dfs.jl b/src/traversals/dfs.jl index 7d674f181..bb489ac02 100644 --- a/src/traversals/dfs.jl +++ b/src/traversals/dfs.jl @@ -16,7 +16,7 @@ end function depth_first_visit_impl!( graph::SimpleGraph, # the graph stack, # an (initialized) stack of vertex - vertexcolormap::Vector{Int}, # an (initialized) color-map to indicate status of vertices + vertexcolormap, # an (initialized) color-map to indicate status of vertices edgecolormap::Dict{Edge,Int}, # an (initialized) color-map to indicate status of edges visitor::SimpleGraphVisitor) # the visitor @@ -27,7 +27,7 @@ function depth_first_visit_impl!( while !done(udsts, tstate) && !found_new_vertex v, tstate = next(udsts, tstate) - v_color = vertexcolormap[v] + v_color = get(vertexcolormap, v, 0) v_edge = Edge(u,v) e_color = haskey(edgecolormap, v_edge)? edgecolormap[v_edge] : edgecolormap[reverse(v_edge)] diff --git a/test/traversals/bfs.jl b/test/traversals/bfs.jl index a978057d5..48d5462cc 100644 --- a/test/traversals/bfs.jl +++ b/test/traversals/bfs.jl @@ -18,15 +18,8 @@ add_edge!(g,3,4) import LightGraphs: TreeBFSVisitorVector, bfs_tree!, TreeBFSVisitor, tree -g = smallgraph(:house) -n = nv(g) -visitor = TreeBFSVisitorVector(n) -@test length(visitor.tree) == n -parents = visitor.tree -bfs_tree!(visitor, g, 1) -maxdepth = n -function istree(parents::Vector{Int}) +function istree(parents::Vector{Int}, maxdepth) flag = true for i in 1:n s = i @@ -42,7 +35,32 @@ function istree(parents::Vector{Int}) return flag end -@test istree(parents) == true +g = smallgraph(:house) +n = nv(g) +visitor = TreeBFSVisitorVector(n) +@test length(visitor.tree) == n +parents = visitor.tree +bfs_tree!(visitor, g, 1) + +@test istree(parents, n) == true +tvis = TreeBFSVisitor(visitor) +@test nv(tvis.tree) == nv(g) +@test typeof(tvis.tree) <: DiGraph +t = tree(parents) +@test typeof(t) <: DiGraph +@test typeof(tvis.tree) <: DiGraph +@test t == tvis.tree +@test ne(t) < nv(t) + +# test Dict{Int,Int}() colormap +g = smallgraph(:house) +n = nv(g) +visitor = TreeBFSVisitorVector(n) +@test length(visitor.tree) == n +parents = visitor.tree +bfs_tree!(visitor, g, 1, colormap = Dict{Int,Int}()) + +@test istree(parents, n) == true tvis = TreeBFSVisitor(visitor) @test nv(tvis.tree) == nv(g) @test typeof(tvis.tree) <: DiGraph From cec121fbc6da98ff815a105ef081b0f82fcafdfb Mon Sep 17 00:00:00 2001 From: CarloLucibello Date: Mon, 11 Apr 2016 21:44:09 +0200 Subject: [PATCH 05/17] -add edgecolormap to BFS - relax type of edgecolormap --- src/connectivity.jl | 6 ++--- src/traversals/bfs.jl | 53 +++++++++++++++++------------------------- src/traversals/dfs.jl | 22 ++++-------------- test/traversals/bfs.jl | 2 +- 4 files changed, 30 insertions(+), 53 deletions(-) diff --git a/src/connectivity.jl b/src/connectivity.jl index 076e82eb7..18f97e8dd 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -25,13 +25,13 @@ function connected_components!(label::Vector{Int}, g::SimpleGraph) nvg = nv(g) visitor = LightGraphs.ComponentVisitorVector(label, 0) colormap = zeros(Int,nvg) - que = Vector{Int}() - sizehint!(que, nvg) + queue = Vector{Int}() + sizehint!(queue, nvg) for v in 1:nvg if label[v] == 0 visitor.labels[v] = v visitor.seed = v - traverse_graph!(g, BreadthFirst(), v, visitor; colormap=colormap, que=que) + traverse_graph!(g, BreadthFirst(), v, visitor; vertexcolormap=colormap, queue=queue) end end return label diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index 2e1c1d20c..c006a0191 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -15,7 +15,8 @@ end function breadth_first_visit_impl!( graph::SimpleGraph, # the graph queue::Vector{Int}, # an (initialized) queue that stores the active vertices - colormap, # an (initialized) color-map to indicate status of vertices (0=unseen, 1=seen, 2=closed) + vertexcolormap, # an (initialized) color-map to indicate status of vertices (0=unseen, 1=seen, 2=closed) + edgecolormap, # an (initialized) color-map to indicate status of edges visitor::SimpleGraphVisitor) # the visitor while !isempty(queue) @@ -23,53 +24,41 @@ function breadth_first_visit_impl!( open_vertex!(visitor, u) for v in out_neighbors(graph, u) - v_color = get(colormap, v, 0) - # TODO: Incorporate edge colors to BFS - examine_neighbor!(visitor, u, v, v_color, -1) || return + v_color = get(vertexcolormap, v, 0) + v_edge = Edge(u,v) + e_color = get(edgecolormap, v_edge, 0) + examine_neighbor!(visitor, u, v, v_color, e_color) || return + + edgecolormap[v_edge] = 1 if v_color == 0 - colormap[v] = 1 + vertexcolormap[v] = 1 discover_vertex!(visitor, v) || return push!(queue, v) end end - colormap[u] = 2 close_vertex!(visitor, u) + vertexcolormap[u] = 2 end - nothing -end - -function traverse_graph!( - graph::SimpleGraph, - alg::BreadthFirst, - s::Int, - visitor::SimpleGraphVisitor; - colormap = zeros(Int, nv(graph)), - que = Vector{Int}()) - - colormap[s] = 1 - discover_vertex!(visitor, s) || return - push!(que, s) - - breadth_first_visit_impl!(graph, que, colormap, visitor) end function traverse_graph!( graph::SimpleGraph, alg::BreadthFirst, - sources::AbstractVector{Int}, + source, visitor::SimpleGraphVisitor; - colormap = zeros(Int, nv(graph)), - que = Vector{Int}()) + vertexcolormap = zeros(Int, nv(graph)), + edgecolormap = Dict{Edge, Int}(), + queue = Vector{Int}()) - for s in sources - colormap[s] = 1 + for s in source + vertexcolormap[s] = 1 discover_vertex!(visitor, s) || return - push!(que, s) + push!(queue, s) end - breadth_first_visit_impl!(graph, que, colormap, visitor) + breadth_first_visit_impl!(graph, queue, vertexcolormap, edgecolormap, visitor) end @@ -179,8 +168,8 @@ end function bfs_tree!(visitor::TreeBFSVisitorVector, g::SimpleGraph, s::Int; - colormap=zeros(Int, nv(g)), - que=Vector{Int}()) + vertexcolormap = zeros(Int, nv(g)), + queue = Vector{Int}()) # this version of bfs_tree! allows one to reuse the memory necessary to compute the tree # the output is stored in the visitor.tree array whose entries are the vertex id of the # parent of the index. This function checks if the scratch space is too small for the graph. @@ -191,7 +180,7 @@ function bfs_tree!(visitor::TreeBFSVisitorVector, nvg = nv(g) length(visitor.tree) >= nvg || error("visitor.tree too small for graph") visitor.tree[s] = s - traverse_graph!(g, BreadthFirst(), s, visitor; colormap=colormap, que=que) + traverse_graph!(g, BreadthFirst(), s, visitor; vertexcolormap=vertexcolormap, queue=queue) end """Provides a breadth-first traversal of the graph `g` starting with source vertex `s`, diff --git a/src/traversals/dfs.jl b/src/traversals/dfs.jl index bb489ac02..93f45af4e 100644 --- a/src/traversals/dfs.jl +++ b/src/traversals/dfs.jl @@ -17,7 +17,7 @@ function depth_first_visit_impl!( graph::SimpleGraph, # the graph stack, # an (initialized) stack of vertex vertexcolormap, # an (initialized) color-map to indicate status of vertices - edgecolormap::Dict{Edge,Int}, # an (initialized) color-map to indicate status of edges + edgecolormap, # an (initialized) color-map to indicate status of edges visitor::SimpleGraphVisitor) # the visitor @@ -29,13 +29,10 @@ function depth_first_visit_impl!( v, tstate = next(udsts, tstate) v_color = get(vertexcolormap, v, 0) v_edge = Edge(u,v) - e_color = haskey(edgecolormap, v_edge)? - edgecolormap[v_edge] : edgecolormap[reverse(v_edge)] - examine_neighbor!(visitor, u, v, v_color, e_color) + e_color = get(edgecolormap, v_edge, 0) + examine_neighbor!(visitor, u, v, v_color, e_color) #no return here - if e_color == 0 - edgecolormap[v_edge] = 1 - end + edgecolormap[v_edge] = 1 if v_color == 0 found_new_vertex = true @@ -56,21 +53,13 @@ function depth_first_visit_impl!( end end -function _mkedgecolormap(g::SimpleGraph, n::Integer=0) - d = Dict{Edge, Int}() - for e in edges(g) - d[e] = n - end - return d -end - function traverse_graph!( graph::SimpleGraph, alg::DepthFirst, s::Int, visitor::SimpleGraphVisitor; vertexcolormap = zeros(Int, nv(graph)), - edgecolormap = _mkedgecolormap(graph)) + edgecolormap = Dict{Edge, Int}()) vertexcolormap[s] = 1 discover_vertex!(visitor, s) || return @@ -82,7 +71,6 @@ function traverse_graph!( depth_first_visit_impl!(graph, stack, vertexcolormap, edgecolormap, visitor) end - ################################################# # # Useful applications diff --git a/test/traversals/bfs.jl b/test/traversals/bfs.jl index 48d5462cc..74c13dd34 100644 --- a/test/traversals/bfs.jl +++ b/test/traversals/bfs.jl @@ -58,7 +58,7 @@ n = nv(g) visitor = TreeBFSVisitorVector(n) @test length(visitor.tree) == n parents = visitor.tree -bfs_tree!(visitor, g, 1, colormap = Dict{Int,Int}()) +bfs_tree!(visitor, g, 1, vertexcolormap = Dict{Int,Int}()) @test istree(parents, n) == true tvis = TreeBFSVisitor(visitor) From d55b1369e030ae8e1d07a061737cfc0b82cfdba0 Mon Sep 17 00:00:00 2001 From: CarloLucibello Date: Tue, 12 Apr 2016 09:19:34 +0200 Subject: [PATCH 06/17] add DummyEdgeMap and make it default --- src/LightGraphs.jl | 4 ++-- src/traversals/bfs.jl | 2 +- src/traversals/dfs.jl | 2 +- src/traversals/graphvisit.jl | 9 +++++++++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index 805ed8839..3e8d5c2cd 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -23,9 +23,9 @@ catch end import Base: write, ==, <, *, isless, issubset, complement, union, intersect, - reverse, reverse!, blkdiag, getindex, show, print, copy, in, + reverse, reverse!, blkdiag, getindex, setindex!, show, print, copy, in, sum, size, sparse, eltype, length, ndims, issym, transpose, - ctranspose, join, start, next, done, eltype + ctranspose, join, start, next, done, eltype, get # core diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index c006a0191..9baa06faf 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -49,7 +49,7 @@ function traverse_graph!( source, visitor::SimpleGraphVisitor; vertexcolormap = zeros(Int, nv(graph)), - edgecolormap = Dict{Edge, Int}(), + edgecolormap = DummyEdgeMap(), queue = Vector{Int}()) for s in source diff --git a/src/traversals/dfs.jl b/src/traversals/dfs.jl index 93f45af4e..0226b22ee 100644 --- a/src/traversals/dfs.jl +++ b/src/traversals/dfs.jl @@ -59,7 +59,7 @@ function traverse_graph!( s::Int, visitor::SimpleGraphVisitor; vertexcolormap = zeros(Int, nv(graph)), - edgecolormap = Dict{Edge, Int}()) + edgecolormap = DummyEdgeMap()) vertexcolormap[s] = 1 discover_vertex!(visitor, s) || return diff --git a/src/traversals/graphvisit.jl b/src/traversals/graphvisit.jl index 36bc16e1e..9d9efe24e 100644 --- a/src/traversals/graphvisit.jl +++ b/src/traversals/graphvisit.jl @@ -28,6 +28,15 @@ end # This is the common base for BreadthFirst and DepthFirst abstract SimpleGraphVisitAlgorithm +type DummyEdgeMap +end + +getindex(d::DummyEdgeMap, e::Edge) = 0 +setindex!(d::DummyEdgeMap, x, e::Edge) = x +get(d::DummyEdgeMap, e::Edge, x) = x + + + ########################################################### # From 7d5e2d97927fa7389297fe0fc11619c8a5c865fe Mon Sep 17 00:00:00 2001 From: CarloLucibello Date: Tue, 12 Apr 2016 10:14:23 +0200 Subject: [PATCH 07/17] make Dict default vertexcolormap --- src/traversals/bfs.jl | 4 ++-- src/traversals/dfs.jl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index 9baa06faf..341f8ae5a 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -48,7 +48,7 @@ function traverse_graph!( alg::BreadthFirst, source, visitor::SimpleGraphVisitor; - vertexcolormap = zeros(Int, nv(graph)), + vertexcolormap = Dict{Int, Int}(), edgecolormap = DummyEdgeMap(), queue = Vector{Int}()) @@ -224,7 +224,7 @@ BipartiteVisitor(n::Int) = BipartiteVisitor(zeros(UInt8,n), true) function examine_neighbor!(visitor::BipartiteVisitor, u::Int, v::Int, vcolor::Int, ecolor::Int) if vcolor == 0 - visitor.bipartitemap[v] = (visitor.bipartitemap[u] == 1)? 2:1 + visitor.bipartitemap[v] = (visitor.bipartitemap[u] == 1) ? 2 : 1 else if visitor.bipartitemap[v] == visitor.bipartitemap[u] visitor.is_bipartite = false diff --git a/src/traversals/dfs.jl b/src/traversals/dfs.jl index 0226b22ee..410357466 100644 --- a/src/traversals/dfs.jl +++ b/src/traversals/dfs.jl @@ -58,7 +58,7 @@ function traverse_graph!( alg::DepthFirst, s::Int, visitor::SimpleGraphVisitor; - vertexcolormap = zeros(Int, nv(graph)), + vertexcolormap = Dict{Int, Int}(), edgecolormap = DummyEdgeMap()) vertexcolormap[s] = 1 From 13a96763533d1896e10ebc3b1f5cd8714c7a5234 Mon Sep 17 00:00:00 2001 From: CarloLucibello Date: Tue, 12 Apr 2016 10:30:47 +0200 Subject: [PATCH 08/17] remove TreeBFSVisitor : end deprecation --- src/traversals/bfs.jl | 16 ---------------- test/traversals/bfs.jl | 13 +------------ 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index 341f8ae5a..16eab8c37 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -115,14 +115,6 @@ end # Constructing BFS trees # ########################################### -# this type has been deprecated in favor of TreeBFSVisitorVector and the tree function. -"""TreeBFSVisitor is a type for representing a BFS traversal of the graph as a DiGraph""" -type TreeBFSVisitor <:SimpleGraphVisitor - tree::DiGraph -end - -@deprecate TreeBFSVisitor(x) TreeBFSVisitorVector(x) - """TreeBFSVisitorVector is a type for representing a BFS traversal of the graph as a parents array. This type allows for a more performant implementation. """ @@ -134,14 +126,6 @@ function TreeBFSVisitorVector(n::Int) return TreeBFSVisitorVector(zeros(Int, n)) end -"""TreeBFSVisitor converts a parents array into a DiGraph""" -function TreeBFSVisitor(tvv::TreeBFSVisitorVector) - n = length(tvv.tree) - parents = tvv.tree - g = tree(parents) - return TreeBFSVisitor(g) -end - """tree converts a parents array into a DiGraph""" function tree(parents::AbstractVector) n = length(parents) diff --git a/test/traversals/bfs.jl b/test/traversals/bfs.jl index 74c13dd34..e6cce0e6b 100644 --- a/test/traversals/bfs.jl +++ b/test/traversals/bfs.jl @@ -17,7 +17,7 @@ add_edge!(g,3,4) @test is_bipartite(g) -import LightGraphs: TreeBFSVisitorVector, bfs_tree!, TreeBFSVisitor, tree +import LightGraphs: TreeBFSVisitorVector, bfs_tree!, tree function istree(parents::Vector{Int}, maxdepth) flag = true @@ -43,13 +43,8 @@ parents = visitor.tree bfs_tree!(visitor, g, 1) @test istree(parents, n) == true -tvis = TreeBFSVisitor(visitor) -@test nv(tvis.tree) == nv(g) -@test typeof(tvis.tree) <: DiGraph t = tree(parents) @test typeof(t) <: DiGraph -@test typeof(tvis.tree) <: DiGraph -@test t == tvis.tree @test ne(t) < nv(t) # test Dict{Int,Int}() colormap @@ -61,16 +56,10 @@ parents = visitor.tree bfs_tree!(visitor, g, 1, vertexcolormap = Dict{Int,Int}()) @test istree(parents, n) == true -tvis = TreeBFSVisitor(visitor) -@test nv(tvis.tree) == nv(g) -@test typeof(tvis.tree) <: DiGraph t = tree(parents) @test typeof(t) <: DiGraph -@test typeof(tvis.tree) <: DiGraph -@test t == tvis.tree @test ne(t) < nv(t) - g10 = CompleteGraph(10) @test bipartite_map(g10) == Vector{Int}() From 33b7a8e3b0f0a792ddf05644a5a55224096c9f35 Mon Sep 17 00:00:00 2001 From: CarloLucibello Date: Tue, 12 Apr 2016 11:44:19 +0200 Subject: [PATCH 09/17] make vertexcolormap indicate distances from root in BFS --- src/connectivity.jl | 4 ++-- src/traversals/bfs.jl | 30 +++++++++++++----------------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/connectivity.jl b/src/connectivity.jl index 18f97e8dd..d571b6ff3 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -24,7 +24,7 @@ function connected_components!(label::Vector{Int}, g::SimpleGraph) # passed to components(a) nvg = nv(g) visitor = LightGraphs.ComponentVisitorVector(label, 0) - colormap = zeros(Int,nvg) + colormap = fill(-1,nvg) queue = Vector{Int}() sizehint!(queue, nvg) for v in 1:nvg @@ -133,7 +133,7 @@ function discover_vertex!(vis::TarjanVisitor, v) end function examine_neighbor!(vis::TarjanVisitor, v, w, w_color::Int, e_color::Int) - if w_color > 0 # 1 means added seen, but not explored; 2 means closed + if w_color >= 0 # >=0 means added seen while vis.index[w] > 0 && vis.index[w] < vis.lowlink[end] pop!(vis.lowlink) end diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index 16eab8c37..d5b3553ab 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -15,31 +15,27 @@ end function breadth_first_visit_impl!( graph::SimpleGraph, # the graph queue::Vector{Int}, # an (initialized) queue that stores the active vertices - vertexcolormap, # an (initialized) color-map to indicate status of vertices (0=unseen, 1=seen, 2=closed) + vertexcolormap, # an (initialized) color-map to indicate status of vertices (-1=unseen, otherwise distance from root) edgecolormap, # an (initialized) color-map to indicate status of edges visitor::SimpleGraphVisitor) # the visitor while !isempty(queue) u = shift!(queue) open_vertex!(visitor, u) - + u_color = vertexcolormap[u] for v in out_neighbors(graph, u) - v_color = get(vertexcolormap, v, 0) + v_color = get(vertexcolormap, v, -1) v_edge = Edge(u,v) - e_color = get(edgecolormap, v_edge, 0) + e_color = get(edgecolormap, v_edge, -1) examine_neighbor!(visitor, u, v, v_color, e_color) || return - edgecolormap[v_edge] = 1 - - if v_color == 0 - vertexcolormap[v] = 1 + if v_color < 0 + vertexcolormap[v] = u_color + 1 discover_vertex!(visitor, v) || return push!(queue, v) end end - close_vertex!(visitor, u) - vertexcolormap[u] = 2 end end @@ -53,7 +49,7 @@ function traverse_graph!( queue = Vector{Int}()) for s in source - vertexcolormap[s] = 1 + vertexcolormap[s] = 0 discover_vertex!(visitor, s) || return push!(queue, s) end @@ -76,7 +72,7 @@ immutable GDistanceVisitor <: SimpleGraphVisitor end function examine_neighbor!(visitor::GDistanceVisitor, u, v, vcolor::Int, ecolor::Int) - if vcolor == 0 + if vcolor < 0 g = visitor.graph dists = visitor.dists dists[v] = dists[u] + 1 @@ -123,7 +119,7 @@ type TreeBFSVisitorVector <: SimpleGraphVisitor end function TreeBFSVisitorVector(n::Int) - return TreeBFSVisitorVector(zeros(Int, n)) + return TreeBFSVisitorVector(fill(-1, n)) end """tree converts a parents array into a DiGraph""" @@ -143,7 +139,7 @@ tree(parents::TreeBFSVisitorVector) = tree(parents.tree) function examine_neighbor!(visitor::TreeBFSVisitorVector, u::Int, v::Int, vcolor::Int, ecolor::Int) # println("discovering $u -> $v, vcolor = $vcolor, ecolor = $ecolor") - if u != v && vcolor == 0 + if u != v && vcolor < 0 visitor.tree[v] = u end return true @@ -152,7 +148,7 @@ end function bfs_tree!(visitor::TreeBFSVisitorVector, g::SimpleGraph, s::Int; - vertexcolormap = zeros(Int, nv(g)), + vertexcolormap = Dict{Int,Int}(), queue = Vector{Int}()) # this version of bfs_tree! allows one to reuse the memory necessary to compute the tree # the output is stored in the visitor.tree array whose entries are the vertex id of the @@ -190,7 +186,7 @@ end function examine_neighbor!(visitor::ComponentVisitorVector, u::Int, v::Int, vcolor::Int, ecolor::Int) # println("discovering $u -> $v, vcolor = $vcolor, ecolor = $ecolor") - if u != v && vcolor == 0 + if u != v && vcolor < 0 visitor.labels[v] = visitor.seed end return true @@ -207,7 +203,7 @@ end BipartiteVisitor(n::Int) = BipartiteVisitor(zeros(UInt8,n), true) function examine_neighbor!(visitor::BipartiteVisitor, u::Int, v::Int, vcolor::Int, ecolor::Int) - if vcolor == 0 + if vcolor < 0 visitor.bipartitemap[v] = (visitor.bipartitemap[u] == 1) ? 2 : 1 else if visitor.bipartitemap[v] == visitor.bipartitemap[u] From c571fdfdf9bee2a01c15dc989cdebfe750a9211f Mon Sep 17 00:00:00 2001 From: CarloLucibello Date: Tue, 12 Apr 2016 12:00:49 +0200 Subject: [PATCH 10/17] simplify gdistances relying on vertecolormap --- src/traversals/bfs.jl | 50 +++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index d5b3553ab..c6326a996 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -64,48 +64,34 @@ end # ################################################# -# Get the map of the (geodesic) distances from vertices to source by BFS +########################################### +# Get the map of the (geodesic) distances from vertices to source by BFS # +########################################### immutable GDistanceVisitor <: SimpleGraphVisitor - graph::SimpleGraph - dists::Vector{Int} end -function examine_neighbor!(visitor::GDistanceVisitor, u, v, vcolor::Int, ecolor::Int) - if vcolor < 0 - g = visitor.graph - dists = visitor.dists - dists[v] = dists[u] + 1 - end - return true -end +""" + gdistances!(g, source, dists) -> dists -"""Returns the geodesic distances of graph `g` from source vertex `s` or a set -of source vertices `ss`. +Fills `dists` with the geodesic distances of vertices in `g` from vertex/vertices `source`. +`dists` can be either a vector or a dictionary. """ -function gdistances!{DMap}(graph::SimpleGraph, s::Int, dists::DMap) - visitor = GDistanceVisitor(graph, dists) - dists[s] = 0 - traverse_graph!(graph, BreadthFirst(), s, visitor) +function gdistances!(g::SimpleGraph, source, dists) + visitor = GDistanceVisitor() + traverse_graph!(g, BreadthFirst(), source, visitor, vertexcolormap=dists) return dists end -function gdistances!{DMap}(graph::SimpleGraph, sources::AbstractVector{Int}, dists::DMap) - visitor = GDistanceVisitor(graph, dists) - for s in sources - dists[s] = 0 - end - traverse_graph!(graph, BreadthFirst(), sources, visitor) - return dists -end -"""Returns the geodesic distances of graph `g` from source vertex `s` or a set -of source vertices `ss`. """ -function gdistances(graph::SimpleGraph, sources; defaultdist::Int=-1) - dists = fill(defaultdist, nv(graph)) - gdistances!(graph, sources, dists) -end + gdistances(g, source) -> dists + +Returns a vector filled with the geodesic distances of vertices in `g` from vertex/vertices `source`. +For vertices in disconnected components the default distance is -1. +""" +gdistances(g, source) = gdistances!(g, source, fill(-1,nv(g))) + ########################################### # Constructing BFS trees # @@ -138,7 +124,6 @@ end tree(parents::TreeBFSVisitorVector) = tree(parents.tree) function examine_neighbor!(visitor::TreeBFSVisitorVector, u::Int, v::Int, vcolor::Int, ecolor::Int) - # println("discovering $u -> $v, vcolor = $vcolor, ecolor = $ecolor") if u != v && vcolor < 0 visitor.tree[v] = u end @@ -185,7 +170,6 @@ type ComponentVisitorVector <: SimpleGraphVisitor end function examine_neighbor!(visitor::ComponentVisitorVector, u::Int, v::Int, vcolor::Int, ecolor::Int) - # println("discovering $u -> $v, vcolor = $vcolor, ecolor = $ecolor") if u != v && vcolor < 0 visitor.labels[v] = visitor.seed end From 6443079c743bdebd52a7eb019dd7cc3484a20d3c Mon Sep 17 00:00:00 2001 From: CarloLucibello Date: Thu, 14 Apr 2016 00:43:16 +0200 Subject: [PATCH 11/17] - add optional dir=:out/:in for BFS - reimplement neighborhood using BFS --- README.md | 2 +- doc/basicmeasures.md | 7 ++++ doc/build.jl | 3 +- doc/index.md | 2 +- doc/pathing.md | 24 +++++++------ src/connectivity.jl | 38 ++++++++++++--------- src/traversals/bfs.jl | 26 +++++++++----- src/traversals/dfs.jl | 18 ++++++---- src/traversals/graphvisit.jl | 2 +- test/runtests.jl | 66 ++++++++++++++++++------------------ 10 files changed, 110 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index 43e44b0ee..d10557d6e 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ save("mygraph.jgz", g, "mygraph", compress=true) - **distance:** eccentricity, diameter, periphery, radius, center -- **connectivity:** strongly- and weakly-connected components, bipartite checks, condensation, attracting components +- **connectivity:** strongly- and weakly-connected components, bipartite checks, condensation, attracting components, neighborhood - **operators:** complement, reverse, reverse!, union, join, intersect, difference, symmetric difference, blkdiag, induced subgraphs, products (cartesian/scalar) diff --git a/doc/basicmeasures.md b/doc/basicmeasures.md index 0d2496d98..7a3a42586 100644 --- a/doc/basicmeasures.md +++ b/doc/basicmeasures.md @@ -186,3 +186,10 @@ common_neighbors(g::Union{LightGraphs.DiGraph,LightGraphs.Graph}, u::Int64, v::I ``` Returns the neighbors common to vertices `u` and `v` in `g`. +### neighborhood +```julia +neighborhood(g, v::Int, d::Int; dir=:out) +``` + +Returns a vector of the vertices in `g` at distance less or equal to `d` from `v`. If `g` is a `DiGraph` the `dir` optional argument specifies the edge direction with respect to `v` (i.e. `:in` or `:out`) to be considered. + diff --git a/doc/build.jl b/doc/build.jl index 8e08e10d4..4bea20a83 100644 --- a/doc/build.jl +++ b/doc/build.jl @@ -133,7 +133,8 @@ types: ## Neighbors and Degree -{{degree, indegree, outdegree, Δ, δ, Δout, δout, δin, Δin, degree_histogram, density, neighbors, in_neighbors, all_neighbors, common_neighbors}} +{{degree, indegree, outdegree, Δ, δ, Δout, δout, δin, Δin, degree_histogram, density, neighbors, + in_neighbors, all_neighbors, common_neighbors, neighborhood}} """ @file "centrality.md" """ diff --git a/doc/index.md b/doc/index.md index de3b96809..fffde854d 100644 --- a/doc/index.md +++ b/doc/index.md @@ -118,7 +118,7 @@ save("mygraph.jgz", g, "mygraph", compress=true) - **distance:** eccentricity, diameter, periphery, radius, center -- **connectivity:** strongly- and weakly-connected components, bipartite checks, condensation, attracting components +- **connectivity:** strongly- and weakly-connected components, bipartite checks, condensation, attracting components, neighborhood - **operators:** complement, reverse, reverse!, union, join, intersect, difference, symmetric difference, blkdiag, induced subgraphs, products (cartesian/scalar) diff --git a/doc/pathing.md b/doc/pathing.md index 8c35ce6f8..cf10c437c 100644 --- a/doc/pathing.md +++ b/doc/pathing.md @@ -33,8 +33,9 @@ This function is a high level wrapper around bfs_tree!, use that function for mo ### dfs_tree ```julia -dfs_tree(g::Union{LightGraphs.DiGraph,LightGraphs.Graph}, s::Int64) +dfs_tree(g, s::Int) ``` + Provides a depth-first traversal of the graph `g` starting with source vertex `s`, and returns a directed acyclic graph of vertices in the order they were discovered. ### maximum_adjacency_visit @@ -62,9 +63,9 @@ Performs a [self-avoiding walk](https://en.wikipedia.org/wiki/Self-avoiding_walk `Graph connectivity` functions are defined on both undirected and directed graphs: ### is_connected ```julia -is_connected(g::LightGraphs.Graph) -is_connected(g::LightGraphs.DiGraph) +is_connected(g) ``` + Returns `true` if `g` is connected. For DiGraphs, this is equivalent to a test of weak connectivity. ### is_strongly_connected @@ -83,7 +84,8 @@ Returns `true` if the undirected graph of `g` is connected. ```julia connected_components(g) ``` -Returns the [connected components](https://en.wikipedia.org/wiki/Connectivity_(graph_theory)) of an undirected graph `g` as a vector of components, each represented by a vector of vectors of vertices belonging to the component. + +Returns the [connected components](https://en.wikipedia.org/wiki/Connectivity_(graph_theory)) of `g` as a vector of components, each represented by a vector of vertices belonging to the component. ### strongly_connected_components ```julia @@ -143,8 +145,9 @@ In graph theory, a cycle is defined to be a path that starts from some vertex `v` and ends up at `v`. ### is_cyclic ```julia -is_cyclic(graph::Union{LightGraphs.DiGraph,LightGraphs.Graph}) +is_cyclic(g) ``` + Tests whether a graph contains a cycle through depth-first search. It returns `true` when it finds a cycle, otherwise `false`. ## Shortest-Path Algorithms @@ -191,16 +194,17 @@ Note that this algorithm may return a large amount of data (it will allocate on ## Path discovery / enumeration ### gdistances ```julia -gdistances(graph::Union{LightGraphs.DiGraph,LightGraphs.Graph}, sources) +gdistances(g, source) -> dists ``` -Returns the geodesic distances of graph `g` from source vertex `s` or a set of source vertices `ss`. + +Returns a vector filled with the geodesic distances of vertices in `g` from vertex/vertices `source`. For vertices in disconnected components the default distance is -1. ### gdistances! ```julia -gdistances!{DMap}(graph::Union{LightGraphs.DiGraph,LightGraphs.Graph}, s::Int64, dists::DMap) -gdistances!{DMap}(graph::Union{LightGraphs.DiGraph,LightGraphs.Graph}, sources::AbstractArray{Int64,1}, dists::DMap) +gdistances!(g, source, dists) -> dists ``` -Returns the geodesic distances of graph `g` from source vertex `s` or a set of source vertices `ss`. + +Fills `dists` with the geodesic distances of vertices in `g` from vertex/vertices `source`. `dists` can be either a vector or a dictionary. ### enumerate_paths ```julia diff --git a/src/connectivity.jl b/src/connectivity.jl index d571b6ff3..0a68cfe97 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -238,6 +238,22 @@ function attracting_components(g::DiGraph) return scc[attracting] end +type NeighborhoodVisitor <: SimpleGraphVisitor + d::Int + neigs::Vector{Int} +end + +NeighborhoodVisitor(d::Int) = NeighborhoodVisitor(d, Vector{Int}()) + +function examine_neighbor!(visitor::NeighborhoodVisitor, u::Int, v::Int, ucolor::Int, vcolor::Int, ecolor::Int) + ucolor >= visitor.d && return false + if vcolor < 0 + push!(visitor.neigs, v) + end + return true +end + + """ neighborhood(g, v::Int, d::Int; dir=:out) @@ -246,20 +262,10 @@ from `v`. If `g` is a `DiGraph` the `dir` optional argument specifies the edge d with respect to `v` (i.e. `:in` or `:out`) to be considered. """ function neighborhood(g::SimpleGraph, v::Int, d::Int; dir=:out) - neig = Set{Int}(v) - ∂neig = copy(neig) - fneig = dir == :out ? out_neighbors : in_neighbors - for l=1:d - newneigs = Set{Int}() - for i in ∂neig - for j in fneig(g, i) - if j ∉ neig - push!(newneigs, j) - push!(neig, j) - end - end - end - ∂neig = newneigs - end - return collect(neig) + @assert d >= 0 "Distance has to be greater then zero." + visitor = NeighborhoodVisitor(d) + push!(visitor.neigs, v) + traverse_graph!(g, BreadthFirst(), v, visitor, + vertexcolormap=Dict{Int,Int}(), dir=dir) + return visitor.neigs end diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index c6326a996..c39b0a9aa 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -17,17 +17,20 @@ function breadth_first_visit_impl!( queue::Vector{Int}, # an (initialized) queue that stores the active vertices vertexcolormap, # an (initialized) color-map to indicate status of vertices (-1=unseen, otherwise distance from root) edgecolormap, # an (initialized) color-map to indicate status of edges - visitor::SimpleGraphVisitor) # the visitor + visitor::SimpleGraphVisitor, # the visitor + dir::Symbol) # direction [:in,:out] + fneig = dir == :out ? out_neighbors : in_neighbors while !isempty(queue) u = shift!(queue) open_vertex!(visitor, u) u_color = vertexcolormap[u] - for v in out_neighbors(graph, u) + + for v in fneig(graph, u) v_color = get(vertexcolormap, v, -1) v_edge = Edge(u,v) e_color = get(edgecolormap, v_edge, -1) - examine_neighbor!(visitor, u, v, v_color, e_color) || return + examine_neighbor!(visitor, u, v, u_color, v_color, e_color) || return edgecolormap[v_edge] = 1 if v_color < 0 vertexcolormap[v] = u_color + 1 @@ -46,7 +49,8 @@ function traverse_graph!( visitor::SimpleGraphVisitor; vertexcolormap = Dict{Int, Int}(), edgecolormap = DummyEdgeMap(), - queue = Vector{Int}()) + queue = Vector{Int}(), + dir = :out) for s in source vertexcolormap[s] = 0 @@ -54,7 +58,8 @@ function traverse_graph!( push!(queue, s) end - breadth_first_visit_impl!(graph, queue, vertexcolormap, edgecolormap, visitor) + breadth_first_visit_impl!(graph, queue, vertexcolormap, edgecolormap + , visitor, dir) end @@ -123,7 +128,8 @@ end tree(parents::TreeBFSVisitorVector) = tree(parents.tree) -function examine_neighbor!(visitor::TreeBFSVisitorVector, u::Int, v::Int, vcolor::Int, ecolor::Int) +function examine_neighbor!(visitor::TreeBFSVisitorVector, u::Int, v::Int, + ucolor::Int, vcolor::Int, ecolor::Int) if u != v && vcolor < 0 visitor.tree[v] = u end @@ -169,7 +175,8 @@ type ComponentVisitorVector <: SimpleGraphVisitor seed::Int end -function examine_neighbor!(visitor::ComponentVisitorVector, u::Int, v::Int, vcolor::Int, ecolor::Int) +function examine_neighbor!(visitor::ComponentVisitorVector, u::Int, v::Int, + ucolor::Int, vcolor::Int, ecolor::Int) if u != v && vcolor < 0 visitor.labels[v] = visitor.seed end @@ -186,7 +193,8 @@ end BipartiteVisitor(n::Int) = BipartiteVisitor(zeros(UInt8,n), true) -function examine_neighbor!(visitor::BipartiteVisitor, u::Int, v::Int, vcolor::Int, ecolor::Int) +function examine_neighbor!(visitor::BipartiteVisitor, u::Int, v::Int, + ucolor::Int, vcolor::Int, ecolor::Int) if vcolor < 0 visitor.bipartitemap[v] = (visitor.bipartitemap[u] == 1) ? 2 : 1 else @@ -210,7 +218,7 @@ end function _bipartite_visitor(g::SimpleGraph, s::Int) nvg = nv(g) visitor = BipartiteVisitor(nvg) - traverse_graph!(g, BreadthFirst(), s, visitor) + traverse_graph!(g, BreadthFirst(), s, visitor, vertexcolormap=fill(-1,nv(g))) return visitor end diff --git a/src/traversals/dfs.jl b/src/traversals/dfs.jl index 410357466..e76a160be 100644 --- a/src/traversals/dfs.jl +++ b/src/traversals/dfs.jl @@ -99,16 +99,19 @@ end discover_vertex!(vis::DFSCyclicTestVisitor, v) = !vis.found_cycle -"""Tests whether a graph contains a cycle through depth-first search. It +""" + is_cyclic(g) + +Tests whether a graph contains a cycle through depth-first search. It returns `true` when it finds a cycle, otherwise `false`. """ -function is_cyclic(graph::SimpleGraph) - cmap = zeros(Int, nv(graph)) +function is_cyclic(g::SimpleGraph) + cmap = zeros(Int, nv(g)) visitor = DFSCyclicTestVisitor() - for s in vertices(graph) + for s in vertices(g) if cmap[s] == 0 - traverse_graph!(graph, DepthFirst(), s, visitor, vertexcolormap=cmap) + traverse_graph!(g, DepthFirst(), s, visitor, vertexcolormap=cmap) end visitor.found_cycle && return true end @@ -165,7 +168,10 @@ function examine_neighbor!(visitor::TreeDFSVisitor, u::Int, v::Int, vcolor::Int, return true end -"""Provides a depth-first traversal of the graph `g` starting with source vertex `s`, +""" + dfs_tree(g, s::Int) + +Provides a depth-first traversal of the graph `g` starting with source vertex `s`, and returns a directed acyclic graph of vertices in the order they were discovered. """ function dfs_tree(g::SimpleGraph, s::Int) diff --git a/src/traversals/graphvisit.jl b/src/traversals/graphvisit.jl index 9d9efe24e..65f830f74 100644 --- a/src/traversals/graphvisit.jl +++ b/src/traversals/graphvisit.jl @@ -15,7 +15,7 @@ discover_vertex!(vis::SimpleGraphVisitor, v) = true open_vertex!(vis::SimpleGraphVisitor, v) = true # invoked when a neighbor is discovered & examined -examine_neighbor!(vis::SimpleGraphVisitor, u, v, vcolor::Int, ecolor::Int) = true +examine_neighbor!(vis::SimpleGraphVisitor, u, v, ucolor::Int, vcolor::Int, ecolor::Int) = true # invoked when all of v's neighbors have been examined close_vertex!(vis::SimpleGraphVisitor, v) = true diff --git a/test/runtests.jl b/test/runtests.jl index 0cfa4ebd5..42fe6c088 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,4 @@ -# include("../src/LightGraphs.jl") +include("../src/LightGraphs.jl") using LightGraphs using LightGraphs.Datasets using Requires @@ -45,44 +45,44 @@ a1 = Graph(adjmx1) a2 = DiGraph(adjmx2) tests = [ - "core", - "edgeiter", - "operators", - "graphdigraph", - "persistence", - "distance", - "spectral", - "cliques", - "subgraphs", + # "core", + # "edgeiter", + # "operators", + # "graphdigraph", + # "persistence", + # "distance", + # "spectral", + # "cliques", + # "subgraphs", "connectivity", - "randgraphs", - "generators", - "shortestpaths/astar", - "shortestpaths/bellman-ford", - "shortestpaths/dijkstra", - "shortestpaths/floyd-warshall", + # "randgraphs", + # "generators", + # "shortestpaths/astar", + # "shortestpaths/bellman-ford", + # "shortestpaths/dijkstra", + # "shortestpaths/floyd-warshall", "traversals/bfs", "traversals/dfs", "traversals/maxadjvisit", "traversals/graphvisit", "traversals/randomwalks", - "community/core-periphery", - "community/detection", - "community/modularity", - "community/clustering", - "centrality/betweenness", - "centrality/closeness", - "centrality/degree", - "centrality/katz", - "centrality/pagerank", - "flow/edmonds_karp", - "flow/dinic", - "flow/boykov_kolmogorov", - "flow/push_relabel", - "flow/maximum_flow", - "matching/linear-programming", - "datasets/runtests", - "utils" + # "community/core-periphery", + # "community/detection", + # "community/modularity", + # "community/clustering", + # "centrality/betweenness", + # "centrality/closeness", + # "centrality/degree", + # "centrality/katz", + # "centrality/pagerank", + # "flow/edmonds_karp", + # "flow/dinic", + # "flow/boykov_kolmogorov", + # "flow/push_relabel", + # "flow/maximum_flow", + # "matching/linear-programming", + # "datasets/runtests", + # "utils" ] From c4db3ff979eefe361369227680e0b009aedef30d Mon Sep 17 00:00:00 2001 From: CarloLucibello Date: Thu, 14 Apr 2016 07:50:23 +0200 Subject: [PATCH 12/17] add egonet --- doc/build.jl | 2 +- src/LightGraphs.jl | 2 +- src/connectivity.jl | 13 ++++++++- test/connectivity.jl | 4 +++ test/runtests.jl | 66 ++++++++++++++++++++++---------------------- 5 files changed, 51 insertions(+), 36 deletions(-) diff --git a/doc/build.jl b/doc/build.jl index 4bea20a83..1a9c06777 100644 --- a/doc/build.jl +++ b/doc/build.jl @@ -134,7 +134,7 @@ types: ## Neighbors and Degree {{degree, indegree, outdegree, Δ, δ, Δout, δout, δin, Δin, degree_histogram, density, neighbors, - in_neighbors, all_neighbors, common_neighbors, neighborhood}} + in_neighbors, all_neighbors, common_neighbors, neighborhood, egonet}} """ @file "centrality.md" """ diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index 3e8d5c2cd..41109dc53 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -62,7 +62,7 @@ randomwalk, saw, non_backtracking_randomwalk, # connectivity connected_components, strongly_connected_components, weakly_connected_components, is_connected, is_strongly_connected, is_weakly_connected, period, -condensation, attracting_components, neighborhood, +condensation, attracting_components, neighborhood, egonet, # cliques maximal_cliques, diff --git a/src/connectivity.jl b/src/connectivity.jl index 0a68cfe97..76637214f 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -259,7 +259,7 @@ end Returns a vector of the vertices in `g` at distance less or equal to `d` from `v`. If `g` is a `DiGraph` the `dir` optional argument specifies the edge direction -with respect to `v` (i.e. `:in` or `:out`) to be considered. +the edge direction with respect to `v` (i.e. `:in` or `:out`) to be considered. """ function neighborhood(g::SimpleGraph, v::Int, d::Int; dir=:out) @assert d >= 0 "Distance has to be greater then zero." @@ -269,3 +269,14 @@ function neighborhood(g::SimpleGraph, v::Int, d::Int; dir=:out) vertexcolormap=Dict{Int,Int}(), dir=dir) return visitor.neigs end + + +""" + egonet(g, v::Int, d::Int; dir=:out) + +Returns the subgraph of `g` induced by the neighbors of `v` up to distance +`d`. If `g` is a `DiGraph` the `dir` optional argument specifies +the edge direction the edge direction with respect to `v` (i.e. `:in` or `:out`) +to be considered. This is equivalent to `induced_subgraph(g, neighborhood(g, v, d, dir=dir)).` +""" +egonet(g::SimpleGraph, v::Int, d::Int; dir=:out) = induced_subgraph(g, neighborhood(g, v, d, dir=dir)) diff --git a/test/connectivity.jl b/test/connectivity.jl index 326f75b41..a3227b730 100644 --- a/test/connectivity.jl +++ b/test/connectivity.jl @@ -151,3 +151,7 @@ g10 = StarDiGraph(10) @test length(neighborhood(g10, 2, 1, dir=:in)) == 2 @test length(neighborhood(g10, 1, 2, dir=:in)) == 1 @test length(neighborhood(g10, 2, 2, dir=:in)) == 2 + +g10 = StarGraph(10) +@test egonet(g10, 1, 0) == Graph(1,0) +@test egonet(g10, 1, 1) == g10 diff --git a/test/runtests.jl b/test/runtests.jl index 42fe6c088..0cfa4ebd5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,4 @@ -include("../src/LightGraphs.jl") +# include("../src/LightGraphs.jl") using LightGraphs using LightGraphs.Datasets using Requires @@ -45,44 +45,44 @@ a1 = Graph(adjmx1) a2 = DiGraph(adjmx2) tests = [ - # "core", - # "edgeiter", - # "operators", - # "graphdigraph", - # "persistence", - # "distance", - # "spectral", - # "cliques", - # "subgraphs", + "core", + "edgeiter", + "operators", + "graphdigraph", + "persistence", + "distance", + "spectral", + "cliques", + "subgraphs", "connectivity", - # "randgraphs", - # "generators", - # "shortestpaths/astar", - # "shortestpaths/bellman-ford", - # "shortestpaths/dijkstra", - # "shortestpaths/floyd-warshall", + "randgraphs", + "generators", + "shortestpaths/astar", + "shortestpaths/bellman-ford", + "shortestpaths/dijkstra", + "shortestpaths/floyd-warshall", "traversals/bfs", "traversals/dfs", "traversals/maxadjvisit", "traversals/graphvisit", "traversals/randomwalks", - # "community/core-periphery", - # "community/detection", - # "community/modularity", - # "community/clustering", - # "centrality/betweenness", - # "centrality/closeness", - # "centrality/degree", - # "centrality/katz", - # "centrality/pagerank", - # "flow/edmonds_karp", - # "flow/dinic", - # "flow/boykov_kolmogorov", - # "flow/push_relabel", - # "flow/maximum_flow", - # "matching/linear-programming", - # "datasets/runtests", - # "utils" + "community/core-periphery", + "community/detection", + "community/modularity", + "community/clustering", + "centrality/betweenness", + "centrality/closeness", + "centrality/degree", + "centrality/katz", + "centrality/pagerank", + "flow/edmonds_karp", + "flow/dinic", + "flow/boykov_kolmogorov", + "flow/push_relabel", + "flow/maximum_flow", + "matching/linear-programming", + "datasets/runtests", + "utils" ] From bcf3afc8bc83904d29f4ad3e4a3a1feb30886264 Mon Sep 17 00:00:00 2001 From: CarloLucibello Date: Thu, 14 Apr 2016 09:05:57 +0200 Subject: [PATCH 13/17] improve is_bipartite performance --- src/traversals/bfs.jl | 22 ++++++++++++++++------ test/connectivity.jl | 1 - 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index c39b0a9aa..240d8d49b 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -205,20 +205,30 @@ function examine_neighbor!(visitor::BipartiteVisitor, u::Int, v::Int, return visitor.is_bipartite end -"""Will return `true` if graph `g` is -[bipartite](https://en.wikipedia.org/wiki/Bipartite_graph). """ -is_bipartite(g::SimpleGraph, s::Int) = _bipartite_visitor(g, s).is_bipartite + is_bipartite(g) + is_bipartite(g, v) +Will return `true` if graph `g` is [bipartite](https://en.wikipedia.org/wiki/Bipartite_graph). +If a node `v` is specified, only the connected component to which it belongs is considered. +""" function is_bipartite(g::SimpleGraph) cc = filter(x->length(x)>2, connected_components(g)) - return all(x->is_bipartite(g,x[1]), cc) + vmap = Dict{Int,Int}() + return all(x->_is_bipartite(g,x[1], vmap=vmap), cc) end -function _bipartite_visitor(g::SimpleGraph, s::Int) +is_bipartite(g::SimpleGraph, v::Int) = _is_bipartite(g, v) + +_is_bipartite(g::SimpleGraph, v::Int; vmap = Dict{Int,Int}()) = _bipartite_visitor(g, v, vmap=vmap).is_bipartite + +function _bipartite_visitor(g::SimpleGraph, s::Int; vmap=Dict{Int,Int}()) nvg = nv(g) visitor = BipartiteVisitor(nvg) - traverse_graph!(g, BreadthFirst(), s, visitor, vertexcolormap=fill(-1,nv(g))) + for v in keys(vmap) #have to reset vmap, otherway problems with digraphs + vmap[v] = -1 + end + traverse_graph!(g, BreadthFirst(), s, visitor, vertexcolormap=vmap) return visitor end diff --git a/test/connectivity.jl b/test/connectivity.jl index a3227b730..0359f4b44 100644 --- a/test/connectivity.jl +++ b/test/connectivity.jl @@ -40,7 +40,6 @@ for m=1:50 end end - # graph from https://en.wikipedia.org/wiki/Strongly_connected_component h = DiGraph(8) add_edge!(h,1,2); add_edge!(h,2,3); add_edge!(h,2,5); From 4f4b4d3daa9918088773e1074048c2148e253ac9 Mon Sep 17 00:00:00 2001 From: CarloLucibello Date: Thu, 14 Apr 2016 09:06:59 +0200 Subject: [PATCH 14/17] update docs --- doc/basicmeasures.md | 9 ++++++++- doc/pathing.md | 9 +++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/doc/basicmeasures.md b/doc/basicmeasures.md index 7a3a42586..a16825371 100644 --- a/doc/basicmeasures.md +++ b/doc/basicmeasures.md @@ -191,5 +191,12 @@ Returns the neighbors common to vertices `u` and `v` in `g`. neighborhood(g, v::Int, d::Int; dir=:out) ``` -Returns a vector of the vertices in `g` at distance less or equal to `d` from `v`. If `g` is a `DiGraph` the `dir` optional argument specifies the edge direction with respect to `v` (i.e. `:in` or `:out`) to be considered. +Returns a vector of the vertices in `g` at distance less or equal to `d` from `v`. If `g` is a `DiGraph` the `dir` optional argument specifies the edge direction the edge direction with respect to `v` (i.e. `:in` or `:out`) to be considered. + +### egonet +```julia +egonet(g, v::Int, d::Int; dir=:out) +``` + +Returns the subgraph of `g` induced by the neighbors of `v` up to distance `d`. If `g` is a `DiGraph` the `dir` optional argument specifies the edge direction the edge direction with respect to `v` (i.e. `:in` or `:out`) to be considered. This is equivalent to `induced_subgraph(g, neighborhood(g, v, d, dir=dir)).` diff --git a/doc/pathing.md b/doc/pathing.md index cf10c437c..793ecee20 100644 --- a/doc/pathing.md +++ b/doc/pathing.md @@ -113,10 +113,11 @@ Returns a vector of vectors of integers representing lists of attracting compone ### is_bipartite ```julia -is_bipartite(g::Union{LightGraphs.DiGraph,LightGraphs.Graph}) -is_bipartite(g::Union{LightGraphs.DiGraph,LightGraphs.Graph}, s::Int64) +is_bipartite(g) +is_bipartite(g, v) ``` -Will return `true` if graph `g` is [bipartite](https://en.wikipedia.org/wiki/Bipartite_graph). + +Will return `true` if graph `g` is [bipartite](https://en.wikipedia.org/wiki/Bipartite_graph). If a node `v` is specified, only the connected component to which it belongs is considered. ### condensation ```julia @@ -138,7 +139,7 @@ Computes the (common) period for all nodes in a strongly connected graph. neighborhood(g, v::Int, d::Int; dir=:out) ``` -Returns a vector of the vertices in `g` at distance less or equal to `d` from `v`. If `g` is a `DiGraph` the `dir` optional argument specifies the edge direction with respect to `v` (i.e. `:in` or `:out`) to be considered. +Returns a vector of the vertices in `g` at distance less or equal to `d` from `v`. If `g` is a `DiGraph` the `dir` optional argument specifies the edge direction the edge direction with respect to `v` (i.e. `:in` or `:out`) to be considered. ## Cycle Detection In graph theory, a cycle is defined to be a path that starts from some vertex From d2afba99bc263dc635ea36d0c199147d038e12ee Mon Sep 17 00:00:00 2001 From: CarloLucibello Date: Thu, 14 Apr 2016 09:13:07 +0200 Subject: [PATCH 15/17] another improve for is_bipartite --- src/traversals/bfs.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index 240d8d49b..11fca0da2 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -215,7 +215,10 @@ If a node `v` is specified, only the connected component to which it belongs is function is_bipartite(g::SimpleGraph) cc = filter(x->length(x)>2, connected_components(g)) vmap = Dict{Int,Int}() - return all(x->_is_bipartite(g,x[1], vmap=vmap), cc) + for c in cc + _is_bipartite(g,c[1], vmap=vmap) || return false + end + return true end is_bipartite(g::SimpleGraph, v::Int) = _is_bipartite(g, v) From cc42c7dfeb43021e9f4cfb55468f72ce2c6fcec6 Mon Sep 17 00:00:00 2001 From: CarloLucibello Date: Mon, 18 Apr 2016 09:44:58 +0200 Subject: [PATCH 16/17] add AbstractEdgeMap and AbstractVertexMap and use them in graph_traversal --- src/traversals/bfs.jl | 8 ++++---- src/traversals/dfs.jl | 4 ++-- src/traversals/graphvisit.jl | 11 ++++++----- test/runtests.jl | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index 11fca0da2..243d50759 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -15,8 +15,8 @@ end function breadth_first_visit_impl!( graph::SimpleGraph, # the graph queue::Vector{Int}, # an (initialized) queue that stores the active vertices - vertexcolormap, # an (initialized) color-map to indicate status of vertices (-1=unseen, otherwise distance from root) - edgecolormap, # an (initialized) color-map to indicate status of edges + vertexcolormap::AbstractVertexMap, # an (initialized) color-map to indicate status of vertices (-1=unseen, otherwise distance from root) + edgecolormap::AbstractEdgeMap, # an (initialized) color-map to indicate status of edges visitor::SimpleGraphVisitor, # the visitor dir::Symbol) # direction [:in,:out] @@ -47,8 +47,8 @@ function traverse_graph!( alg::BreadthFirst, source, visitor::SimpleGraphVisitor; - vertexcolormap = Dict{Int, Int}(), - edgecolormap = DummyEdgeMap(), + vertexcolormap::AbstractVertexMap = Dict{Int, Int}(), + edgecolormap::AbstractEdgeMap = DummyEdgeMap(), queue = Vector{Int}(), dir = :out) diff --git a/src/traversals/dfs.jl b/src/traversals/dfs.jl index e76a160be..61539f4ed 100644 --- a/src/traversals/dfs.jl +++ b/src/traversals/dfs.jl @@ -16,8 +16,8 @@ end function depth_first_visit_impl!( graph::SimpleGraph, # the graph stack, # an (initialized) stack of vertex - vertexcolormap, # an (initialized) color-map to indicate status of vertices - edgecolormap, # an (initialized) color-map to indicate status of edges + vertexcolormap::AbstractVertexMap, # an (initialized) color-map to indicate status of vertices + edgecolormap::AbstractEdgeMap, # an (initialized) color-map to indicate status of edges visitor::SimpleGraphVisitor) # the visitor diff --git a/src/traversals/graphvisit.jl b/src/traversals/graphvisit.jl index 65f830f74..44e2ae87b 100644 --- a/src/traversals/graphvisit.jl +++ b/src/traversals/graphvisit.jl @@ -28,14 +28,15 @@ end # This is the common base for BreadthFirst and DepthFirst abstract SimpleGraphVisitAlgorithm -type DummyEdgeMap +typealias AbstractEdgeMap{T} Associative{Edge,T} +typealias AbstractVertexMap{T} Union{AbstractVector{T},Associative{Int, T}} + +type DummyEdgeMap <: AbstractEdgeMap{Int} end getindex(d::DummyEdgeMap, e::Edge) = 0 -setindex!(d::DummyEdgeMap, x, e::Edge) = x -get(d::DummyEdgeMap, e::Edge, x) = x - - +setindex!(d::DummyEdgeMap, x::Int, e::Edge) = x +get(d::DummyEdgeMap, e::Edge, x::Int) = x ########################################################### diff --git a/test/runtests.jl b/test/runtests.jl index 0cfa4ebd5..c029e9c88 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,4 @@ -# include("../src/LightGraphs.jl") +include("../src/LightGraphs.jl") using LightGraphs using LightGraphs.Datasets using Requires From e8d7351c87463c74c19604cc564da6a816e811c3 Mon Sep 17 00:00:00 2001 From: CarloLucibello Date: Tue, 19 Apr 2016 11:16:25 +0200 Subject: [PATCH 17/17] uniform color convention for BFS and DFS --- src/connectivity.jl | 8 ++++---- src/traversals/bfs.jl | 37 ++++++++++++++++++++++++++----------- src/traversals/dfs.jl | 21 ++++++++++++++++----- test/runtests.jl | 2 +- 4 files changed, 47 insertions(+), 21 deletions(-) diff --git a/src/connectivity.jl b/src/connectivity.jl index 76637214f..843dafc54 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -24,7 +24,7 @@ function connected_components!(label::Vector{Int}, g::SimpleGraph) # passed to components(a) nvg = nv(g) visitor = LightGraphs.ComponentVisitorVector(label, 0) - colormap = fill(-1,nvg) + colormap = fill(0, nvg) queue = Vector{Int}() sizehint!(queue, nvg) for v in 1:nvg @@ -133,7 +133,7 @@ function discover_vertex!(vis::TarjanVisitor, v) end function examine_neighbor!(vis::TarjanVisitor, v, w, w_color::Int, e_color::Int) - if w_color >= 0 # >=0 means added seen + if w_color != 0 # != 0 means seen while vis.index[w] > 0 && vis.index[w] < vis.lowlink[end] pop!(vis.lowlink) end @@ -246,8 +246,8 @@ end NeighborhoodVisitor(d::Int) = NeighborhoodVisitor(d, Vector{Int}()) function examine_neighbor!(visitor::NeighborhoodVisitor, u::Int, v::Int, ucolor::Int, vcolor::Int, ecolor::Int) - ucolor >= visitor.d && return false - if vcolor < 0 + -ucolor > visitor.d && return false # color is negative for not-closed vertices + if vcolor == 0 push!(visitor.neigs, v) end return true diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index 243d50759..52e0f5ea3 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -8,6 +8,17 @@ # Breadth-first visit # ################################################# +""" +**Conventions in Breadth First Search and Depth First Search** +VertexColorMap : +- color == 0 => unseen +- color < 0 => examined but not closed +- color > 0 => examined and closed + +EdgeColorMap : +- color == 0 => unseen +- color == 1 => examined +""" type BreadthFirst <: SimpleGraphVisitAlgorithm end @@ -27,18 +38,19 @@ function breadth_first_visit_impl!( u_color = vertexcolormap[u] for v in fneig(graph, u) - v_color = get(vertexcolormap, v, -1) + v_color = get(vertexcolormap, v, 0) v_edge = Edge(u,v) - e_color = get(edgecolormap, v_edge, -1) + e_color = get(edgecolormap, v_edge, 0) examine_neighbor!(visitor, u, v, u_color, v_color, e_color) || return edgecolormap[v_edge] = 1 - if v_color < 0 - vertexcolormap[v] = u_color + 1 + if v_color == 0 + vertexcolormap[v] = u_color - 1 discover_vertex!(visitor, v) || return push!(queue, v) end end close_vertex!(visitor, u) + vertexcolormap[u] *= -1 end end @@ -53,7 +65,7 @@ function traverse_graph!( dir = :out) for s in source - vertexcolormap[s] = 0 + vertexcolormap[s] = -1 discover_vertex!(visitor, s) || return push!(queue, s) end @@ -85,6 +97,9 @@ Fills `dists` with the geodesic distances of vertices in `g` from vertex/vertic function gdistances!(g::SimpleGraph, source, dists) visitor = GDistanceVisitor() traverse_graph!(g, BreadthFirst(), source, visitor, vertexcolormap=dists) + for i in eachindex(dists) + dists[i] -= 1 + end return dists end @@ -95,7 +110,7 @@ end Returns a vector filled with the geodesic distances of vertices in `g` from vertex/vertices `source`. For vertices in disconnected components the default distance is -1. """ -gdistances(g, source) = gdistances!(g, source, fill(-1,nv(g))) +gdistances(g::SimpleGraph, source) = gdistances!(g, source, fill(0,nv(g))) ########################################### @@ -110,7 +125,7 @@ type TreeBFSVisitorVector <: SimpleGraphVisitor end function TreeBFSVisitorVector(n::Int) - return TreeBFSVisitorVector(fill(-1, n)) + return TreeBFSVisitorVector(fill(0, n)) end """tree converts a parents array into a DiGraph""" @@ -130,7 +145,7 @@ tree(parents::TreeBFSVisitorVector) = tree(parents.tree) function examine_neighbor!(visitor::TreeBFSVisitorVector, u::Int, v::Int, ucolor::Int, vcolor::Int, ecolor::Int) - if u != v && vcolor < 0 + if u != v && vcolor == 0 visitor.tree[v] = u end return true @@ -177,7 +192,7 @@ end function examine_neighbor!(visitor::ComponentVisitorVector, u::Int, v::Int, ucolor::Int, vcolor::Int, ecolor::Int) - if u != v && vcolor < 0 + if u != v && vcolor == 0 visitor.labels[v] = visitor.seed end return true @@ -195,7 +210,7 @@ BipartiteVisitor(n::Int) = BipartiteVisitor(zeros(UInt8,n), true) function examine_neighbor!(visitor::BipartiteVisitor, u::Int, v::Int, ucolor::Int, vcolor::Int, ecolor::Int) - if vcolor < 0 + if vcolor == 0 visitor.bipartitemap[v] = (visitor.bipartitemap[u] == 1) ? 2 : 1 else if visitor.bipartitemap[v] == visitor.bipartitemap[u] @@ -229,7 +244,7 @@ function _bipartite_visitor(g::SimpleGraph, s::Int; vmap=Dict{Int,Int}()) nvg = nv(g) visitor = BipartiteVisitor(nvg) for v in keys(vmap) #have to reset vmap, otherway problems with digraphs - vmap[v] = -1 + vmap[v] = 0 end traverse_graph!(g, BreadthFirst(), s, visitor, vertexcolormap=vmap) return visitor diff --git a/src/traversals/dfs.jl b/src/traversals/dfs.jl index 61539f4ed..fb006898f 100644 --- a/src/traversals/dfs.jl +++ b/src/traversals/dfs.jl @@ -9,6 +9,17 @@ # Depth-first visit # ################################################# +""" +**Conventions in Breadth First Search and Depth First Search** +VertexColorMap : +- color == 0 => unseen +- color < 0 => examined but not closed +- color > 0 => examined and closed + +EdgeColorMap : +- color == 0 => unseen +- color == 1 => examined +""" type DepthFirst <: SimpleGraphVisitAlgorithm end @@ -36,7 +47,7 @@ function depth_first_visit_impl!( if v_color == 0 found_new_vertex = true - vertexcolormap[v] = 1 + vertexcolormap[v] = vertexcolormap[u] - 1 #negative numbers discover_vertex!(visitor, v) || return push!(stack, (u, udsts, tstate)) @@ -48,7 +59,7 @@ function depth_first_visit_impl!( if !found_new_vertex close_vertex!(visitor, u) - vertexcolormap[u] = 2 + vertexcolormap[u] *= -1 end end end @@ -61,7 +72,7 @@ function traverse_graph!( vertexcolormap = Dict{Int, Int}(), edgecolormap = DummyEdgeMap()) - vertexcolormap[s] = 1 + vertexcolormap[s] = -1 discover_vertex!(visitor, s) || return sdsts = fadj(graph, s) @@ -92,7 +103,7 @@ function examine_neighbor!( vcolor::Int, ecolor::Int) - if vcolor == 1 && ecolor == 0 + if vcolor < 0 && ecolor == 0 vis.found_cycle = true end end @@ -132,7 +143,7 @@ end function examine_neighbor!(visitor::TopologicalSortVisitor, u::Int, v::Int, vcolor::Int, ecolor::Int) - (vcolor == 1 && ecolor == 0) && error("The input graph contains at least one loop.") + (vcolor < 0 && ecolor == 0) && error("The input graph contains at least one loop.") end function close_vertex!(visitor::TopologicalSortVisitor, v::Int) diff --git a/test/runtests.jl b/test/runtests.jl index c029e9c88..0cfa4ebd5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,4 @@ -include("../src/LightGraphs.jl") +# include("../src/LightGraphs.jl") using LightGraphs using LightGraphs.Datasets using Requires