Skip to content

Commit

Permalink
created iterators src folder, added kruskal
Browse files Browse the repository at this point in the history
  • Loading branch information
kylebeggs committed Mar 15, 2023
1 parent 9e941c8 commit 0ff24ef
Show file tree
Hide file tree
Showing 9 changed files with 364 additions and 4 deletions.
21 changes: 21 additions & 0 deletions docs/src/algorithms/iterators.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 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",
"iterators/kruskal.jl",
]
```
1 change: 0 additions & 1 deletion docs/src/algorithms/traversals.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,5 @@ Pages = [
"traversals/greedy_color.jl",
"traversals/maxadjvisit.jl",
"traversals/randomwalks.jl",
"traversals/iterators.jl",
]
```
4 changes: 2 additions & 2 deletions src/Graphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ export
is_cyclic, topological_sort_by_dfs, dfs_tree, dfs_parents,

# iterators
VertexIterator, DFSIterator, BFSIterator, iterate,
DFSIterator, BFSIterator, KruskalIterator,

# random
randomwalk,
Expand Down Expand Up @@ -489,7 +489,7 @@ include("traversals/dfs.jl")
include("traversals/maxadjvisit.jl")
include("traversals/randomwalks.jl")
include("traversals/diffusion.jl")
include("traversals/iterators.jl")
include("iterators/iterators.jl")
include("connectivity.jl")
include("distance.jl")
include("editdist.jl")
Expand Down
104 changes: 104 additions & 0 deletions src/iterators/bfs.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
"""
BFSIterator
`BFSIterator` is a subtype of [`VertexIterator`](@ref) 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. If no source is provided, it defaults to the first vertex.
# 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
julia> for node in BFSIterator(g,[1,3])
display(node)
end
1
2
3
4
5
3
1
4
5
2
```
"""
struct BFSIterator{S} <: VertexIterator
graph::AbstractGraph
source::S
end

BFSIterator(g::AbstractGraph) = BFSIterator(g, one(eltype(g)))


"""
Base.iterate(t::BFSIterator)
First iteration to visit each vertex in a graph using breadth-first search.
"""
function Base.iterate(t::BFSIterator)
visited = falses(nv(t.graph))
if t.source isa Number
visited[t.source] = true
return (t.source, SingleSourceIteratorState(visited, [t.source]))
else
init_source = first(t.source)
visited[init_source] = true
return (init_source, MultiSourceIteratorState(visited, [init_source], 1))
end
end


"""
Base.iterate(t::BFSIterator, state::SingleSourceIteratorState)
Iterator to visit each vertex in a graph using breadth-first search.
"""
function Base.iterate(t::BFSIterator, state::SingleSourceIteratorState)
while !isempty(state.queue)
for node in outneighbors(t.graph, state.queue[1])
if !state.visited[node]
push!(state.queue, node)
state.visited[node] = true
return (node, state)
end
end
popfirst!(state.queue)
end
end


"""
Base.iterate(t::BFSIterator, state::SingleSourceIteratorState)
Iterator to visit each vertex in a graph using breadth-first search.
"""
function Base.iterate(t::BFSIterator, state::MultiSourceIteratorState)
while !isempty(state.queue)
for node in outneighbors(t.graph, state.queue[1])
if !state.visited[node]
push!(state.queue, node)
state.visited[node] = true
return (node, state)
end
end
popfirst!(state.queue)
end
# reset state and begin traversal at next source
state.source_id += 1
state.source_id > length(t.source) && return nothing
init_source =t.source[state.source_id]
state.visited .= false
state.visited[init_source] = true
state.queue = [init_source]
return (init_source, state)
end
104 changes: 104 additions & 0 deletions src/iterators/dfs.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
"""
DFSIterator
`DFSIterator` is a subtype of [`VertexIterator`](@ref) 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. If no source is provided, it defaults to the first vertex.
# 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
julia> for node in DFSIterator(g,[1,5])
display(node)
end
1
2
4
3
5
5
3
1
2
4
```
"""
struct DFSIterator{S} <: VertexIterator
graph::AbstractGraph
source::S
end

DFSIterator(g::AbstractGraph) = DFSIterator(g, one(eltype(g)))


"""
Base.iterate(t::DFSIterator)
First iteration to visit each vertex in a graph using depth-first search.
"""
function Base.iterate(t::DFSIterator)
visited = falses(nv(t.graph))
if t.source isa Number
visited[t.source] = true
return (t.source, SingleSourceIteratorState(visited, [t.source]))
else
init_source = first(t.source)
visited[init_source] = true
return (init_source, MultiSourceIteratorState(visited, [init_source], 1))
end
end


"""
Base.iterate(t::DFSIterator, state::SingleSourceIteratorState)
Iterator to visit each vertex in a graph using depth-first search.
"""
function Base.iterate(t::DFSIterator, state::SingleSourceIteratorState)
while !isempty(state.queue)
for node in outneighbors(t.graph, state.queue[end])
if !state.visited[node]
push!(state.queue, node)
state.visited[node] = true
return (node, state)
end
end
pop!(state.queue)
end
end


"""
Base.iterate(t::DFSIterator, state::MultiSourceIteratorState)
Iterator to visit each vertex in a graph using depth-first search.
"""
function Base.iterate(t::DFSIterator, state::MultiSourceIteratorState)
while !isempty(state.queue)
for node in outneighbors(t.graph, state.queue[end])
if !state.visited[node]
push!(state.queue, node)
state.visited[node] = true
return (node, state)
end
end
pop!(state.queue)
end
# reset state and begin traversal at next source
state.source_id += 1
state.source_id > length(t.source) && return nothing
init_source =t.source[state.source_id]
state.visited .= false
state.visited[init_source] = true
state.queue = [init_source]
return (init_source, state)
end
65 changes: 65 additions & 0 deletions src/iterators/iterators.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""
abstract type Iterator
`Iterator` is an abstract type which specifies a particular algorithm to use when iterating through a graph.
"""
abstract type Iterator end


"""
abstract type VertexIterator <: Iterator
`VertexIterator` is an abstract type to iterate through graph vertices.
"""
abstract type VertexIterator <: Iterator end


"""
abstract type EdgeIterator <: Iterator
`EdgeIterator` is an abstract type to iterate through graph edges.
"""
abstract type EdgeIterator <: Iterator end


"""
abstract type AbstractIteratorState
`IteratorState` is an abstract type to hold the current state of iteration which is need for the Base.iterate() function.
"""
abstract type AbstractIteratorState end


"""
mutable struct SingleSourceIteratorState
`SingleSourceIteratorState` is a struct to hold the current state of iteration which is need for the Base.iterate() function. It is a basic implementation used for depth-first or breadth-first iterators when a single source is supplied.
"""
mutable struct SingleSourceIteratorState <: AbstractIteratorState
visited::BitArray
queue::Vector{Int}
end


"""
mutable struct MultiSourceIteratorState
`MultiSourceIteratorState` is a struct to hold the current state of iteration which is need for Julia's Base.iterate() function. It is a basic implementation used for depth-first or breadth-first iterators when mutltiple sources are supplied.
"""
mutable struct MultiSourceIteratorState <: AbstractIteratorState
visited::BitArray
queue::Vector{Int}
source_id::Int
end


include("bfs.jl")
include("dfs.jl")
include("kruskal.jl")







67 changes: 67 additions & 0 deletions src/iterators/kruskal.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# The kruskal algorithms are largely copied from Graphs/src/spanningtrees/kruskal.jl

struct KruskalIterator <: EdgeIterator
graph::AbstractGraph
connected_vs::IntDisjointSets
distmx::AbstractMatrix
edge_list

function KruskalIterator(graph, distmx=weights(g); minimize=true)
is_directed(graph) && throw(ArgumentError("$graph is a directed graph."))
weights = Vector{eltype(distmx)}()
sizehint!(weights, ne(graph))
edge_list = collect(edges(graph))
for e in edge_list
push!(weights, distmx[src(e), dst(e)])
end
e = edge_list[sortperm(weights; rev=!minimize)]
new(graph, IntDisjointSets(nv(graph)), distmx, e)
end
end


"""
mutable struct KruskalIteratorState
`KruskalIteratorState` is a struct to hold the current state of iteration which is need for the Base.iterate() function.
"""
mutable struct KruskalIteratorState <: AbstractIteratorState
edge_id::Int
mst_len::Int
end


function Base.iterate(t::KruskalIterator, state::KruskalIteratorState=KruskalIteratorState(1,1))
while state.mst_len <= (nv(t.graph)-1)
i = state.edge_id
if !in_same_set(t.connected_vs, src(t.edge_list[i]), dst(t.edge_list[i]))
union!(t.connected_vs, src(t.edge_list[i]), dst(t.edge_list[i]))
state.mst_len += 1
return (t.edge_list[i], state)
end
state.edge_id += 1
end
end


function traverse_kruskal_mst(t::EdgeIterator, state::SingleSourceIteratorState)
connected_vs = IntDisjointSets(nv(g))

mst = Vector{edgetype(g)}()
sizehint!(mst, nv(g) - 1)

weights = Vector{T}()
sizehint!(weights, ne(g))
edge_list = collect(edges(g))
for e in edge_list
push!(weights, distmx[src(e), dst(e)])
end

for e in edge_list[sortperm(weights; rev=!minimize)]
if !in_same_set(connected_vs, src(e), dst(e))
union!(connected_vs, src(e), dst(e))
push!(mst, e)
(length(mst) >= nv(g) - 1) && break
end
end
end
File renamed without changes.
Loading

0 comments on commit 0ff24ef

Please sign in to comment.