Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Create some new options for BFSIterator #389

Merged
merged 8 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions src/iterators/bfs.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"""
BFSIterator
BFSIterator(graph, source; depth_limit=nothing, neighbors_type=outneighbors)

`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.
A source node(s) must be supplied as an `Int` or an array-like type that can be
indexed if supplying multiple sources. It is also possible to specify a `depth_limit`
which will stop the search after it is reached and a `neighbors_type` which specifies
Tortar marked this conversation as resolved.
Show resolved Hide resolved
what kind of neighbors of a node should be considered when exploring the graph.

# Examples
```julia-repl
Expand All @@ -20,14 +22,18 @@ julia> for node in BFSIterator(g,3)
2
```
"""
struct BFSIterator{S,G<:AbstractGraph}
struct BFSIterator{S,G<:AbstractGraph,F<:Function}
Tortar marked this conversation as resolved.
Show resolved Hide resolved
graph::G
source::S
function BFSIterator(graph::G, source::S) where {S,G}
depth_limit::Int
neighbors_type::F
function BFSIterator(
graph::G, source::S; depth_limit=typemax(Int64), neighbors_type::F=outneighbors
) where {S,G,F}
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)
return new{S,G,F}(graph, source, depth_limit, neighbors_type)
end
end

Expand All @@ -46,6 +52,7 @@ mutable struct BFSVertexIteratorState
next_level::Vector{Int}
node_idx::Int
n_visited::Int
n_level::Int
end

Base.IteratorSize(::BFSIterator) = Base.SizeUnknown()
Expand All @@ -59,7 +66,7 @@ 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
state = BFSVertexIteratorState(visited, [t.source], Int[], 0, 0)
state = BFSVertexIteratorState(visited, [t.source], Int[], 0, 0, 0)
return Base.iterate(t, state)
end

Expand All @@ -68,7 +75,7 @@ function Base.iterate(t::BFSIterator{<:AbstractArray})
curr_level = unique(s for s in t.source)
sort!(curr_level)
visited[curr_level] .= true
state = BFSVertexIteratorState(visited, curr_level, Int[], 0, 0)
state = BFSVertexIteratorState(visited, curr_level, Int[], 0, 0, 0)
return Base.iterate(t, state)
end

Expand All @@ -80,10 +87,13 @@ Iterator to visit vertices in a graph using breadth-first search.
function Base.iterate(t::BFSIterator, state::BFSVertexIteratorState)
# we fill nodes in this level
if state.node_idx == length(state.curr_level)
state.n_level == t.depth_limit && return nothing
state.n_level += 1
state.n_visited += length(state.curr_level)
state.n_visited == nv(t.graph) && return nothing
neighbors_type = t.neighbors_type
@inbounds for node in state.curr_level
for adj_node in outneighbors(t.graph, node)
for adj_node in neighbors_type(t.graph, node)
if !state.visited[adj_node]
push!(state.next_level, adj_node)
state.visited[adj_node] = true
Expand Down
8 changes: 8 additions & 0 deletions test/iterators/bfs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,12 @@
@test sort(nodes_visited[1:3]) == sort(levels[1])
@test sort(nodes_visited[4:8]) == sort(levels[2])
@test sort(nodes_visited[9:end]) == sort(levels[3])

nodes_visited = collect(BFSIterator(g2, [8, 1, 6]; depth_limit=1))
Tortar marked this conversation as resolved.
Show resolved Hide resolved
@test sort(nodes_visited[1:3]) == sort(levels[1])
@test sort(nodes_visited[4:end]) == sort(levels[2])

g = path_digraph(7)
nodes_visited = collect(BFSIterator(g, 7; neighbors_type=inneighbors))
@test nodes_visited == collect(7:-1:1)
end
Loading