-
Notifications
You must be signed in to change notification settings - Fork 103
added iterators for dfs and bfs #163
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
40 commits
Select commit
Hold shift + click to select a range
d5922fc
added iterators for dfs and bfs
kylebeggs fa40087
fixed exisintg identifier issue
kylebeggs 1d513a5
added iterators for dfs and bfs
kylebeggs a279eca
fixed exisintg identifier issue
kylebeggs d6b120c
add abstract iterator state and fix first iter arg
kylebeggs 9e941c8
iterator refactor
kylebeggs 0ff24ef
created iterators src folder, added kruskal
kylebeggs 1f59073
created iterators src folder, added kruskal
kylebeggs 3a5794a
Merge branch 'JuliaGraphs:master' into master
kylebeggs 1bcc1c1
multi source version for bfs and dfs
Tortar 9bfafbe
fix
Tortar 48a2512
Merge branch 'master' into master
Tortar 72c6840
remove kruskal
Tortar dd63b60
Update Graphs.jl
Tortar c6fdf27
remove kruskal from pages
Tortar 27aa945
format
Tortar b5df963
format 2
Tortar c14111f
format branch
Tortar 790572e
improve perf of bfs
Tortar 4f6bd50
optimize bfs
Tortar 8f4223f
use copy of source nodes
Tortar a56ced8
Update bfs.jl
Tortar 19a4aa6
improve dfs
Tortar e52f03a
Update bfs.jl
Tortar a96d943
use size unknown
Tortar f5f60cc
Update bfs.jl
Tortar 09f1edd
Merge branch 'JuliaGraphs:master' into master
Tortar 80c1d0f
Clean up
gdalle a48b4c0
Fix typo
gdalle 971c790
Fix tests and length
gdalle af494f6
Remove ref
gdalle 74c003b
Fix eltype
gdalle 22df731
Merge branch 'JuliaGraphs:master' into master
Tortar 9022e24
address review comments
Tortar c89176e
some more comments
Tortar 9fd1a3a
Update bfs.jl
Tortar a3b7a9e
Update bfs.jl
Tortar a26a165
Update dfs.jl
Tortar de59874
formatting
Tortar 10e7c6e
Apply suggestions from code review
gdalle File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# Iterators | ||
|
||
_Graphs.jl_ includes various routines for iterating through graphs. | ||
|
||
## Index | ||
|
||
```@index | ||
Pages = ["iterators.md"] | ||
``` | ||
|
||
## Full docs | ||
|
||
```@autodocs | ||
Modules = [Graphs] | ||
Pages = [ | ||
"iterators/iterators.jl", | ||
"iterators/bfs.jl", | ||
"iterators/dfs.jl", | ||
] | ||
Private = false | ||
``` | ||
|
||
The following names are internals, not part of the public API: | ||
|
||
```@autodocs | ||
Modules = [Graphs] | ||
Pages = [ | ||
"iterators/iterators.jl", | ||
"iterators/bfs.jl", | ||
"iterators/dfs.jl", | ||
] | ||
Public = false | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
""" | ||
BFSIterator | ||
|
||
`BFSIterator` is used to iterate through graph vertices using a breadth-first search. | ||
A source node(s) is optionally supplied as an `Int` or an array-like type that can be | ||
indexed if supplying multiple sources. | ||
|
||
# Examples | ||
```julia-repl | ||
julia> g = smallgraph(:house) | ||
{5, 6} undirected simple Int64 graph | ||
|
||
julia> for node in BFSIterator(g,3) | ||
display(node) | ||
end | ||
3 | ||
1 | ||
4 | ||
5 | ||
2 | ||
``` | ||
""" | ||
struct BFSIterator{S,G<:AbstractGraph} | ||
graph::G | ||
source::S | ||
function BFSIterator(graph::G, source::S) where {S,G} | ||
if any(node -> !has_vertex(graph, node), source) | ||
error("Some source nodes for the iterator are not in the graph") | ||
end | ||
return new{S,G}(graph, source) | ||
end | ||
end | ||
|
||
""" | ||
BFSVertexIteratorState | ||
|
||
`BFSVertexIteratorState` is a struct to hold the current state of iteration | ||
in BFS which is needed for the `Base.iterate()` function. A queue is used to | ||
keep track of the vertices which will be visited during BFS. Since the queue | ||
can contains repetitions of already visited nodes, we also keep track of that | ||
in a `BitVector` so that to skip those nodes. | ||
""" | ||
mutable struct BFSVertexIteratorState | ||
visited::BitVector | ||
queue::Vector{Int} | ||
gdalle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
neighbor_idx::Int | ||
n_visited::Int | ||
end | ||
|
||
Base.IteratorSize(::BFSIterator) = Base.SizeUnknown() | ||
Base.eltype(::Type{BFSIterator{S,G}}) where {S,G} = eltype(G) | ||
|
||
""" | ||
Base.iterate(t::BFSIterator) | ||
|
||
First iteration to visit vertices in a graph using breadth-first search. | ||
""" | ||
function Base.iterate(t::BFSIterator{<:Integer}) | ||
visited = falses(nv(t.graph)) | ||
visited[t.source] = true | ||
return (t.source, BFSVertexIteratorState(visited, [t.source], 1, 1)) | ||
end | ||
|
||
function Base.iterate(t::BFSIterator{<:AbstractArray}) | ||
visited = falses(nv(t.graph)) | ||
visited[first(t.source)] = true | ||
state = BFSVertexIteratorState(visited, copy(t.source), 1, 1) | ||
return (first(t.source), state) | ||
end | ||
|
||
""" | ||
Base.iterate(t::BFSIterator, state::VertexIteratorState) | ||
|
||
Iterator to visit vertices in a graph using breadth-first search. | ||
""" | ||
function Base.iterate(t::BFSIterator, state::BFSVertexIteratorState) | ||
gdalle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
graph, visited, queue = t.graph, state.visited, state.queue | ||
while !isempty(queue) | ||
if state.n_visited == nv(graph) | ||
gdalle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return nothing | ||
end | ||
# we visit the first node in the queue | ||
node_start = first(queue) | ||
if !visited[node_start] | ||
visited[node_start] = true | ||
state.n_visited += 1 | ||
return (node_start, state) | ||
end | ||
# which means we arrive here when the first node was visited. | ||
neigh = outneighbors(graph, node_start) | ||
if state.neighbor_idx <= length(neigh) | ||
node = neigh[state.neighbor_idx] | ||
# we update the idx of the neighbor we will visit, | ||
# if it is already visited, we repeat | ||
state.neighbor_idx += 1 | ||
if !visited[node] | ||
push!(queue, node) | ||
state.visited[node] = true | ||
state.n_visited += 1 | ||
return (node, state) | ||
end | ||
else | ||
# when the first node and its neighbors are visited | ||
# we remove the first node of the queue | ||
popfirst!(queue) | ||
state.neighbor_idx = 1 | ||
end | ||
end | ||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
""" | ||
DFSIterator | ||
|
||
`DFSIterator` is used to iterate through graph vertices using a depth-first search. | ||
A source node(s) is optionally supplied as an `Int` or an array-like type that can be | ||
indexed if supplying multiple sources. | ||
|
||
# Examples | ||
```julia-repl | ||
julia> g = smallgraph(:house) | ||
{5, 6} undirected simple Int64 graph | ||
|
||
julia> for node in DFSIterator(g, 3) | ||
display(node) | ||
end | ||
1 | ||
2 | ||
4 | ||
3 | ||
5 | ||
``` | ||
""" | ||
struct DFSIterator{S,G<:AbstractGraph} | ||
graph::G | ||
source::S | ||
function DFSIterator(graph::G, source::S) where {S,G} | ||
if any(node -> !has_vertex(graph, node), source) | ||
error("Some source nodes for the iterator are not in the graph") | ||
end | ||
return new{S,G}(graph, source) | ||
end | ||
end | ||
|
||
""" | ||
DFSVertexIteratorState | ||
|
||
`DFSVertexIteratorState` is a struct to hold the current state of iteration | ||
in DFS which is needed for the `Base.iterate()` function. A queue is used to | ||
keep track of the vertices which will be visited during DFS. Since the queue | ||
can contains repetitions of already visited nodes, we also keep track of that | ||
in a `BitVector` so that to skip those nodes. | ||
""" | ||
mutable struct DFSVertexIteratorState | ||
gdalle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
visited::BitVector | ||
queue::Vector{Int} | ||
gdalle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
end | ||
|
||
Base.IteratorSize(::DFSIterator) = Base.SizeUnknown() | ||
Base.eltype(::Type{DFSIterator{S,G}}) where {S,G} = eltype(G) | ||
|
||
""" | ||
Base.iterate(t::DFSIterator) | ||
|
||
First iteration to visit vertices in a graph using depth-first search. | ||
""" | ||
function Base.iterate(t::DFSIterator{<:Integer}) | ||
visited = falses(nv(t.graph)) | ||
visited[t.source] = true | ||
return (t.source, DFSVertexIteratorState(visited, [t.source])) | ||
end | ||
|
||
function Base.iterate(t::DFSIterator{<:AbstractArray}) | ||
visited = falses(nv(t.graph)) | ||
source_rev = reverse(t.source) | ||
visited[last(source_rev)] = true | ||
state = DFSVertexIteratorState(visited, source_rev) | ||
return (last(source_rev), state) | ||
end | ||
|
||
""" | ||
Base.iterate(t::DFSIterator, state::VertexIteratorState) | ||
|
||
Iterator to visit vertices in a graph using depth-first search. | ||
""" | ||
function Base.iterate(t::DFSIterator, state::DFSVertexIteratorState) | ||
gdalle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
graph, visited, queue = t.graph, state.visited, state.queue | ||
while !isempty(queue) | ||
# we take the last node in the queue | ||
node_start = last(queue) | ||
# we first return it | ||
if !visited[node_start] | ||
visited[node_start] = true | ||
return (node_start, state) | ||
end | ||
# and then we visit a neighbor and push it at the | ||
# end of the queue | ||
for node in outneighbors(graph, node_start) | ||
if !visited[node] | ||
push!(queue, node) | ||
visited[node] = true | ||
return (node, state) | ||
end | ||
end | ||
# we pop the last node in the queue | ||
# when it and all its neighbors were visited | ||
pop!(queue) | ||
end | ||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
@testset "BFSIterator" begin | ||
gdalle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
g = Graph() | ||
error_exc = ErrorException("Some source nodes for the iterator are not in the graph") | ||
@test_throws error_exc BFSIterator(g, 3) | ||
g = path_graph(7) | ||
add_edge!(g, 6, 3) | ||
add_edge!(g, 3, 1) | ||
add_edge!(g, 4, 7) | ||
g2 = deepcopy(g) | ||
add_vertex!(g2) | ||
add_vertex!(g2) | ||
add_edge!(g2, 8, 9) | ||
|
||
for g in testgraphs(g) | ||
nodes_visited = fill(0, nv(g)) | ||
for (i, node) in enumerate(BFSIterator(g, 6)) | ||
nodes_visited[i] = node | ||
end | ||
@test nodes_visited[1] == 6 | ||
@test any(nodes_visited[2] .== [3, 5, 7]) | ||
if nodes_visited[2] == 3 | ||
@test nodes_visited[3:4] == [5, 7] || nodes_visited[3:4] == [7, 5] | ||
elseif nodes_visited[2] == 5 | ||
@test nodes_visited[3:4] == [3, 7] || nodes_visited[3:4] == [7, 3] | ||
else | ||
@test nodes_visited[3:4] == [3, 5] || nodes_visited[3:4] == [5, 3] | ||
end | ||
@test any(nodes_visited[5] .== [1, 2, 4]) | ||
if nodes_visited[5] == 1 | ||
@test nodes_visited[6:7] == [2, 4] || nodes_visited[6:7] == [4, 2] | ||
elseif nodes_visited[5] == 2 | ||
@test nodes_visited[6:7] == [1, 4] || nodes_visited[6:7] == [4, 1] | ||
else | ||
@test nodes_visited[6:7] == [1, 2] || nodes_visited[6:7] == [2, 1] | ||
end | ||
end | ||
nodes_visited = collect(BFSIterator(g2, [1, 6])) | ||
@test nodes_visited == [1, 2, 3, 6, 5, 7, 4] | ||
nodes_visited = collect(BFSIterator(g2, [8, 1, 6])) | ||
@test nodes_visited == [8, 9, 1, 2, 3, 6, 5, 7, 4] | ||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
@testset "DFSIterator" begin | ||
gdalle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
g = Graph() | ||
error_exc = ErrorException("Some source nodes for the iterator are not in the graph") | ||
@test_throws error_exc DFSIterator(g, 3) | ||
g = path_graph(7) | ||
add_edge!(g, 6, 3) | ||
add_edge!(g, 3, 1) | ||
add_edge!(g, 4, 7) | ||
g2 = deepcopy(g) | ||
add_vertex!(g2) | ||
add_vertex!(g2) | ||
add_edge!(g2, 8, 9) | ||
|
||
for g in testgraphs(g) | ||
nodes_visited = fill(0, nv(g)) | ||
for (i, node) in enumerate(DFSIterator(g, 6)) | ||
nodes_visited[i] = node | ||
end | ||
@test nodes_visited[1:2] == [6, 3] | ||
@test any(nodes_visited[3] .== [1, 4]) | ||
if nodes_visited[3] == 1 | ||
@test nodes_visited[4] == 2 | ||
@test nodes_visited[5] == 4 | ||
@test any(nodes_visited[6] .== [5, 7]) | ||
if nodes_visited[6] == 5 | ||
@test nodes_visited[7] == 7 | ||
end | ||
else | ||
@test any(nodes_visited[4] .== [5, 7]) | ||
if nodes_visited[4] == 5 | ||
@test nodes_visited[5] == 7 | ||
end | ||
@test nodes_visited[6] == 1 | ||
@test nodes_visited[7] == 2 | ||
end | ||
end | ||
nodes_visited = collect(DFSIterator(g2, [1, 6])) | ||
@test nodes_visited == [1, 2, 3, 4, 5, 6, 7] | ||
nodes_visited = collect(DFSIterator(g2, [8, 1, 6])) | ||
@test nodes_visited == [8, 9, 1, 2, 3, 4, 5, 6, 7] | ||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.