Skip to content

Commit

Permalink
Use generic graphs for testing (JuliaGraphs#278)
Browse files Browse the repository at this point in the history
Co-authored-by: Guillaume Dalle <22795598+gdalle@users.noreply.github.com>
  • Loading branch information
simonschoelly and gdalle authored Sep 14, 2023
1 parent d25e5d7 commit 3fea924
Show file tree
Hide file tree
Showing 11 changed files with 127 additions and 81 deletions.
16 changes: 13 additions & 3 deletions src/Test/Test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module Test

using Graphs

export GenericEdge, GenericGraph, GenericDiGraph
export GenericEdge, GenericGraph, GenericDiGraph, generic_graph

"""
GenericEdge <: Graphs.AbstractEdge
Expand Down Expand Up @@ -37,7 +37,7 @@ struct GenericGraph{T} <: Graphs.AbstractGraph{T}
end

function GenericGraph(elist::Vector{Graphs.SimpleGraphEdge{T}}) where {T<:Integer}
GenericGraph{T}(SimpleGraph(elist))
return GenericGraph{T}(SimpleGraph(elist))
end

"""
Expand All @@ -50,8 +50,18 @@ struct GenericDiGraph{T} <: Graphs.AbstractGraph{T}
g::SimpleDiGraph{T}
end

"""
generic_graph(g::Union{SimpleGraph, SimpleDiGraph})
Return either a GenericGraph or GenericDiGraph that wraps a copy of g.
"""
function generic_graph(g::Union{SimpleGraph,SimpleDiGraph})
g = copy(g)
return is_directed(g) ? GenericDiGraph(g) : GenericGraph(g)
end

function GenericDiGraph(elist::Vector{Graphs.SimpleDiGraphEdge{T}}) where {T<:Integer}
GenericDiGraph{T}(SimpleDiGraph(elist))
return GenericDiGraph{T}(SimpleDiGraph(elist))
end

Graphs.is_directed(::Type{<:GenericGraph}) = false
Expand Down
4 changes: 4 additions & 0 deletions src/degeneracy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,10 @@ Int64[]
```
"""
function k_corona(g::AbstractGraph, k; corenum=core_number(g))

# TODO k_corona does not correctly work for all AbstractGraph as
# it relies on induced_subgraph, which does not work on any AbstractGraph

kcore = k_core(g, k)
kcoreg = g[kcore]
kcoredeg = degree(kcoreg)
Expand Down
22 changes: 8 additions & 14 deletions src/operators.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# TODO most of the operators here do not work with any AbstractGraph yet
# as they require cloning and modifying graphs.

"""
complement(g)
Expand Down Expand Up @@ -402,25 +405,16 @@ end
# """Provides multiplication of a graph `g` by a vector `v` such that spectral
# graph functions in [GraphMatrices.jl](https://github.com/jpfairbanks/GraphMatrices.jl) can utilize Graphs natively.
# """
function *(g::Graph, v::Vector{T}) where {T<:Real}
length(v) == nv(g) || throw(ArgumentError("Vector size must equal number of vertices"))
y = zeros(T, nv(g))
for e in edges(g)
i = src(e)
j = dst(e)
y[i] += v[j]
y[j] += v[i]
end
return y
end

function *(g::DiGraph, v::Vector{T}) where {T<:Real}
function *(g::AbstractGraph, v::Vector{T}) where {T<:Real}
length(v) == nv(g) || throw(ArgumentError("Vector size must equal number of vertices"))
y = zeros(T, nv(g))
for e in edges(g)
i = src(e)
j = dst(e)
y[i] += v[j]
if !is_directed(g)
y[j] += v[i]
end
end
return y
end
Expand Down Expand Up @@ -481,7 +475,7 @@ julia> size(g, 3)
1
```
"""
size(g::Graph, dim::Int) = (dim == 1 || dim == 2) ? nv(g) : 1
size(g::AbstractGraph, dim::Int) = (dim == 1 || dim == 2) ? nv(g) : 1

"""
sum(g)
Expand Down
6 changes: 5 additions & 1 deletion src/traversals/bfs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ function _bfs_parents(g::AbstractGraph{T}, source, neighborfn::Function) where {
end
while !isempty(cur_level)
@inbounds for v in cur_level
@inbounds @simd for i in neighborfn(g, v)
# TODO we previously used @simd on the loop below, but this would fail
# if the result of neighorfn(g, v) would not implement firstindex
# If @simd really has a performance advantage, then maybe we make
# two different cases here.
@inbounds for i in neighborfn(g, v)
if !visited[i]
push!(next_level, i)
parents[i] = v
Expand Down
70 changes: 44 additions & 26 deletions test/connectivity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
add_edge!(gx, 8, 9)
add_edge!(gx, 10, 9)

for g in testgraphs(gx)
for g in test_generic_graphs(gx)
@test @inferred(!is_connected(g))
cc = @inferred(connected_components(g))
label = zeros(eltype(g), nv(g))
Expand All @@ -20,18 +20,18 @@
@test cclab[8] == [8, 9, 10]
@test length(cc) >= 3 && sort(cc[3]) == [8, 9, 10]
end
for g in testgraphs(g6)
for g in test_generic_graphs(g6)
@test @inferred(is_connected(g))
end

g10 = SimpleDiGraph(4)
add_edge!(g10, 1, 3)
add_edge!(g10, 2, 4)
for g in testdigraphs(g10)
for g in test_generic_graphs(g10)
@test @inferred(is_bipartite(g))
end
add_edge!(g10, 1, 4)
for g in testdigraphs(g10)
for g in test_generic_graphs(g10)
@test @inferred(is_bipartite(g))
end

Expand All @@ -45,7 +45,7 @@
end
if !has_edge(g, i, j)
add_edge!(g, i, j)
@test @inferred(is_bipartite(g))
@test @inferred(is_bipartite(GenericDiGraph(g)))
end
end
end
Expand All @@ -66,7 +66,7 @@
add_edge!(h, 7, 6)
add_edge!(h, 8, 4)
add_edge!(h, 8, 7)
for g in testdigraphs(h)
for g in test_generic_graphs(h)
@test @inferred(is_weakly_connected(g))
scc = @inferred(strongly_connected_components(g))
scc_k = @inferred(strongly_connected_components_kosaraju(g))
Expand All @@ -77,15 +77,27 @@
@test length(wcc) == 1 && length(wcc[1]) == nv(g)
end

function scc_ok(graph)
function scc_ok(graph::GenericDiGraph)
# Check that all SCC really are strongly connected

# TODO it might be better if we did not have to unwrap the GenericDiGraph
# (and we somehow might prevent this in the future) but currently the methods
# used in this utility test function do not work with GenericDiGraph yet.
graph = graph.g

scc = @inferred(strongly_connected_components(graph))
scc_as_subgraphs = map(i -> graph[i], scc)
return all(is_strongly_connected, scc_as_subgraphs)
end

function scc_k_ok(graph)
function scc_k_ok(graph::GenericDiGraph)
# Check that all SCC really are strongly connected

# TODO it might be better if we did not have to unwrap the GenericDiGraph
# (and we somehow might prevent this in the future) but currently the methods
# used in this utility test function do not work with GenericDiGraph yet.
graph = graph.g

scc_k = @inferred(strongly_connected_components_kosaraju(graph))
scc_k_as_subgraphs = map(i -> graph[i], scc_k)
return all(is_strongly_connected, scc_k_as_subgraphs)
Expand All @@ -97,7 +109,7 @@
add_edge!(h, 4, 2)
add_edge!(h, 2, 3)
add_edge!(h, 1, 3)
for g in testdigraphs(h)
for g in test_generic_graphs(h)
@test scc_ok(g)
@test scc_k_ok(g)
end
Expand All @@ -107,14 +119,14 @@
add_edge!(h2, 2, 4)
add_edge!(h2, 4, 3)
add_edge!(h2, 1, 3)
for g in testdigraphs(h2)
for g in test_generic_graphs(h2)
@test scc_ok(g)
@test scc_k_ok(g)
end

# Test case for empty graph
h = SimpleDiGraph(0)
for g in testdigraphs(h)
for g in test_generic_graphs(h)
scc = @inferred(strongly_connected_components(g))
scc_k = @inferred(strongly_connected_components_kosaraju(g))
@test length(scc) == 0
Expand All @@ -123,7 +135,7 @@

# Test case for graph with one vertex
h = SimpleDiGraph(1)
for g in testdigraphs(h)
for g in test_generic_graphs(h)
scc = @inferred(strongly_connected_components(g))
scc_k = @inferred(strongly_connected_components_kosaraju(g))
@test length(scc) == 1 && scc[1] == [1]
Expand All @@ -139,7 +151,7 @@
add_edge!(h, 2, 3)
add_edge!(h, 2, 1)

for g in testdigraphs(h)
for g in test_generic_graphs(h)
scc = @inferred(strongly_connected_components(g))
scc_k = @inferred(strongly_connected_components_kosaraju(g))
@test length(scc) == 2
Expand All @@ -159,7 +171,7 @@
add_edge!(h, 3, 5)
add_edge!(h, 5, 6)
add_edge!(h, 6, 4)
for g in testdigraphs(h)
for g in test_generic_graphs(h)
scc = @inferred(strongly_connected_components(g))
scc_k = @inferred(strongly_connected_components_kosaraju(g))
@test length(scc) == 1 && sort(scc[1]) == [1:6;]
Expand All @@ -171,7 +183,7 @@
add_edge!(h, 2, 3)
add_edge!(h, 3, 1)
add_edge!(h, 4, 1)
for g in testdigraphs(h)
for g in test_generic_graphs(h)
scc = @inferred(strongly_connected_components(g))
scc_k = @inferred(strongly_connected_components_kosaraju(g))
@test length(scc) == 2 && sort(scc[1]) == [1:3;] && sort(scc[2]) == [4]
Expand Down Expand Up @@ -200,7 +212,7 @@
add_edge!(h, 11, 12)
add_edge!(h, 12, 10)

for g in testdigraphs(h)
for g in test_generic_graphs(h)
scc = @inferred(strongly_connected_components(g))
scc_k = @inferred(strongly_connected_components_kosaraju(g))
@test length(scc) == 4
Expand All @@ -224,44 +236,50 @@
fig1[[3, 4, 9, 10, 11, 13, 18, 19, 22, 24]] = [
0.5, 0.4, 0.1, 1.0, 1.0, 0.2, 0.3, 0.2, 1.0, 0.3
]
fig1 = SimpleDiGraph(fig1)
fig1 = GenericDiGraph(SimpleDiGraph(fig1))
scc_fig1 = Vector[[2, 5], [1, 3, 4]]

# figure 2 example
fig2 = spzeros(5, 5)
fig2[[3, 10, 11, 13, 14, 17, 18, 19, 22]] .= 1
fig2 = SimpleDiGraph(fig2)
fig2 = GenericDiGraph(SimpleDiGraph(fig2))

# figure 3 example
fig3 = spzeros(8, 8)
fig3[[
1, 7, 9, 13, 14, 15, 18, 20, 23, 27, 28, 31, 33, 34, 37, 45, 46, 49, 57, 63, 64
]] .= 1
fig3 = SimpleDiGraph(fig3)
fig3 = GenericDiGraph(SimpleDiGraph(fig3))
scc_fig3 = Vector[[3, 4], [2, 5, 6], [8], [1, 7]]
fig3_cond = SimpleDiGraph(4)
add_edge!(fig3_cond, 4, 3)
add_edge!(fig3_cond, 2, 1)
add_edge!(fig3_cond, 4, 1)
add_edge!(fig3_cond, 4, 2)
fig3_cond

# construct a n-number edge ring graph (period = n)
n = 10
n_ring = cycle_digraph(n)
n_ring_shortcut = copy(n_ring)
add_edge!(n_ring_shortcut, 1, 4)
n_ring = GenericDiGraph(n_ring)
n_ring_shortcut = GenericDiGraph(n_ring_shortcut)

# figure 8 example
fig8 = spzeros(6, 6)
fig8[[2, 10, 13, 21, 24, 27, 35]] .= 1
fig8 = SimpleDiGraph(fig8)
fig8 = GenericDiGraph(SimpleDiGraph(fig8))

@test Set(@inferred(strongly_connected_components(fig1))) == Set(scc_fig1)
@test Set(@inferred(strongly_connected_components(fig3))) == Set(scc_fig3)

@test @inferred(period(n_ring)) == n
@test @inferred(period(n_ring_shortcut)) == 2

# TODO condensation currently returns a SimpleDiGraph, even if the input graph
# is a GenericDiGraph, so we compare with a SimpleDiGraph in this test,
# but one should think, if the condensation should not also be a GenericDiGraph
@test @inferred(condensation(fig3)) == fig3_cond

@test @inferred(attracting_components(fig1)) == Vector[[2, 5]]
Expand All @@ -270,7 +288,7 @@
g10dists = ones(10, 10)
g10dists[1, 2] = 10.0
g10 = star_graph(10)
for g in testgraphs(g10)
for g in test_generic_graphs(g10)
@test @inferred(neighborhood_dists(g, 1, 0)) == [(1, 0)]
@test length(@inferred(neighborhood(g, 1, 1))) == 10
@test length(@inferred(neighborhood(g, 1, 1, g10dists))) == 9
Expand All @@ -280,8 +298,8 @@
@test length(@inferred(neighborhood(g, 2, -1))) == 0
end
g10 = star_digraph(10)
for g in testdigraphs(g10)
@test @inferred(neighborhood_dists(g10, 1, 0, dir=:out)) == [(1, 0)]
for g in test_generic_graphs(g10)
@test @inferred(neighborhood_dists(g, 1, 0, dir=:out)) == [(1, 0)]
@test length(@inferred(neighborhood(g, 1, 1, dir=:out))) == 10
@test length(@inferred(neighborhood(g, 1, 1, g10dists, dir=:out))) == 9
@test length(@inferred(neighborhood(g, 2, 1, dir=:out))) == 1
Expand All @@ -301,7 +319,7 @@
##@test !@inferred(isgraphical([2]))

# Test simple digraphicality
sdg = SimpleDiGraph(10, 90)
sdg = GenericDiGraph(SimpleDiGraph(10, 90))
@test @inferred(isdigraphical(indegree(sdg), outdegree(sdg)))
@test !@inferred(isdigraphical([1, 1, 1], [1, 1, 0]))
@test @inferred(isdigraphical(Integer[], Integer[]))
Expand All @@ -314,14 +332,14 @@

# 1116
gc = cycle_graph(4)
for g in testgraphs(gc)
for g in test_generic_graphs(gc)
z = @inferred(neighborhood(g, 3, 3))
@test (z == [3, 2, 4, 1] || z == [3, 4, 2, 1])
end

gd = SimpleDiGraph([0 1 1 0; 0 0 0 1; 0 0 0 1; 0 0 0 0])
add_edge!(gd, 1, 4)
for g in testdigraphs(gd)
for g in test_generic_graphs(gd)
z = @inferred(neighborhood_dists(g, 1, 4))
@test (4, 1) z
@test (4, 2) z
Expand Down
Loading

0 comments on commit 3fea924

Please sign in to comment.