Skip to content

Commit d29e1f2

Browse files
Use GenericGraph for testing biconnectivity algorithms (#270)
* Add ignore_missing_comparison=True for JET tests * Use GenericGraph for testing biconnectivity algorithms
1 parent c554ed8 commit d29e1f2

File tree

9 files changed

+81
-17
lines changed

9 files changed

+81
-17
lines changed

src/biconnectivity/articulation.jl

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,25 @@ function articulation end
3535
cnt::T = one(T)
3636
first_time = true
3737

38+
# TODO the algorithm currently relies on the assumption that
39+
# outneighbors(g, v) is indexable. This assumption might not be true
40+
# in general, so in case that outneighbors does not produce a vector
41+
# we collect these vertices. This might lead to a large number of
42+
# allocations, so we should find a way to handle that case differently,
43+
# or require inneighbors, outneighbors and neighbors to always
44+
# return indexable collections.
45+
3846
while !isempty(s) || first_time
3947
first_time = false
4048
if wi < 1
4149
pre[v] = cnt
4250
cnt += 1
4351
low[v] = pre[v]
44-
v_neighbors = outneighbors(g, v)
52+
v_neighbors = collect_if_not_vector(outneighbors(g, v))
4553
wi = 1
4654
else
4755
wi, u, v = pop!(s)
48-
v_neighbors = outneighbors(g, v)
56+
v_neighbors = collect_if_not_vector(outneighbors(g, v))
4957
w = v_neighbors[wi]
5058
low[v] = min(low[v], low[w])
5159
if low[w] >= pre[v] && u != v

src/biconnectivity/biconnect.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ mutable struct Biconnections{E<:AbstractEdge}
1111
id::Int
1212
end
1313

14+
# TODO it might be more reasonable to return the components a s collections of vertices
15+
# instead of edges.
1416
@traitfn function Biconnections(g::::(!IsDirected))
1517
n = nv(g)
1618
E = Edge{eltype(g)}

src/biconnectivity/bridge.jl

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,26 @@ function bridges end
4040
cnt::T = one(T) # keeps record of the time
4141
first_time = true
4242

43+
# TODO the algorithm currently relies on the assumption that
44+
# outneighbors(g, v) is indexable. This assumption might not be true
45+
# in general, so in case that outneighbors does not produce a vector
46+
# we collect these vertices. This might lead to a large number of
47+
# allocations, so we should find a way to handle that case differently,
48+
# or require inneighbors, outneighbors and neighbors to always
49+
# return indexable collections.
50+
4351
# start of DFS
4452
while !isempty(s) || first_time
4553
first_time = false
4654
if wi < 1 # initialisation for vertex v
4755
pre[v] = cnt
4856
cnt += 1
4957
low[v] = pre[v]
50-
v_neighbors = outneighbors(g, v)
58+
v_neighbors = collect_if_not_vector(outneighbors(g, v))
5159
wi = 1
5260
else
5361
wi, u, v = pop!(s) # the stack states, explained later
54-
v_neighbors = outneighbors(g, v)
62+
v_neighbors = collect_if_not_vector(outneighbors(g, v))
5563
w = v_neighbors[wi]
5664
low[v] = min(low[v], low[w]) # condition check for (v, w) being a tree-edge
5765
if low[w] > pre[v]

src/utils.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,6 @@ function deepcopy_adjlist(adjlist::Vector{Vector{T}}) where {T}
297297

298298
return result
299299
end
300+
301+
collect_if_not_vector(xs::AbstractVector) = xs
302+
collect_if_not_vector(xs) = collect(xs)

test/biconnectivity/articulation.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,22 @@
1818
add_edge!(gint, 7, 10)
1919
add_edge!(gint, 7, 12)
2020

21-
for g in testgraphs(gint)
21+
for g in test_generic_graphs(gint)
2222
art = @inferred(articulation(g))
2323
ans = [1, 7, 8, 12]
2424
@test art == ans
2525
end
2626
for level in 1:6
2727
btree = Graphs.binary_tree(level)
28-
for tree in [btree, Graph{UInt8}(btree), Graph{Int16}(btree)]
28+
for tree in test_generic_graphs(btree; eltypes=[Int, UInt8, Int16])
2929
artpts = @inferred(articulation(tree))
3030
@test artpts == collect(1:(2^(level - 1) - 1))
3131
end
3232
end
3333

3434
hint = blockdiag(wheel_graph(5), wheel_graph(5))
3535
add_edge!(hint, 5, 6)
36-
for h in (hint, Graph{UInt8}(hint), Graph{Int16}(hint))
36+
for h in test_generic_graphs(hint, eltypes=[Int, UInt8, Int16])
3737
@test @inferred(articulation(h)) == [5, 6]
3838
end
3939
end

test/biconnectivity/biconnect.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
[Edge(11, 12)],
2424
]
2525

26-
for g in testgraphs(gint)
26+
for g in test_generic_graphs(gint)
2727
bcc = @inferred(biconnected_components(g))
2828
@test bcc == a
2929
@test typeof(bcc) === Vector{Vector{Edge{eltype(g)}}}
@@ -50,7 +50,7 @@
5050
[Edge(1, 4), Edge(3, 4), Edge(2, 3), Edge(1, 2)],
5151
]
5252

53-
for g in testgraphs(gint)
53+
for g in test_generic_graphs(gint)
5454
bcc = @inferred(biconnected_components(g))
5555
@test bcc == a
5656
@test typeof(bcc) === Vector{Vector{Edge{eltype(g)}}}
@@ -59,6 +59,6 @@
5959
# Non regression test for #13
6060
g = complete_graph(4)
6161
a = [[Edge(2, 4), Edge(1, 4), Edge(3, 4), Edge(1, 3), Edge(2, 3), Edge(1, 2)]]
62-
bcc = @inferred(biconnected_components(g))
62+
bcc = @inferred(biconnected_components(GenericGraph(g)))
6363
@test bcc == a
6464
end

test/biconnectivity/bridge.jl

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,32 @@
2020
add_edge!(gint, 7, 10)
2121
add_edge!(gint, 7, 12)
2222

23-
for g in testgraphs(gint)
23+
for g in test_generic_graphs(gint)
2424
brd = @inferred(bridges(g))
2525
ans = [Edge(1, 2), Edge(8, 9), Edge(7, 8), Edge(11, 12)]
2626
@test brd == ans
2727
end
2828
for level in 1:6
2929
btree = Graphs.binary_tree(level)
30-
for tree in [btree, Graph{UInt8}(btree), Graph{Int16}(btree)]
30+
for tree in test_generic_graphs(btree; eltypes=[Int, UInt8, Int16])
3131
brd = @inferred(bridges(tree))
32-
ans = collect(edges(tree))
33-
@test Set(brd) == Set(ans)
32+
ans = edges(tree)
33+
34+
# AbstractEdge currently does not implement any comparison operators
35+
# so instead we compare tuples of source and target vertices
36+
@test sort([(src(e), dst(e)) for e in brd]) == sort([(src(e), dst(e)) for e in ans])
3437
end
3538
end
3639

3740
hint = blockdiag(wheel_graph(5), wheel_graph(5))
3841
add_edge!(hint, 5, 6)
39-
for h in (hint, Graph{UInt8}(hint), Graph{Int16}(hint))
40-
@test @inferred(bridges(h)) == [Edge(5, 6)]
42+
for h in test_generic_graphs(hint; eltypes=[Int, UInt8, Int16])
43+
brd = @inferred bridges(h)
44+
@test length(brd) == 1
45+
@test src(brd[begin]) == 5
46+
@test dst(brd[begin]) == 6
4147
end
4248

43-
dir = SimpleDiGraph(10, 10; rng=rng)
49+
dir = GenericDiGraph(SimpleDiGraph(10, 10; rng=rng))
4450
@test_throws MethodError bridges(dir)
4551
end

test/runtests.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,22 @@ function testlargegraphs(g)
6666
end
6767
testlargegraphs(gs...) = vcat((testlargegraphs(g) for g in gs)...)
6868

69+
function test_generic_graphs(g; eltypes=[UInt8, Int16], skip_if_too_large::Bool=false)
70+
SG = is_directed(g) ? SimpleDiGraph : SimpleGraph
71+
GG = is_directed(g) ? GenericDiGraph : GenericGraph
72+
result = GG[]
73+
for T in eltypes
74+
if skip_if_too_large && nv(g) > typemax(T)
75+
continue
76+
end
77+
push!(result, GG(SG{T}(g)))
78+
end
79+
return result
80+
end
81+
82+
test_large_generic_graphs(g; skip_if_too_large::Bool=false) = test_generic_graphs(g; eltypes=[UInt16, Int32], skip_if_too_large=skip_if_too_large)
83+
84+
6985
tests = [
7086
"simplegraphs/runtests",
7187
"linalg/runtests",

test/utils.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,24 @@ end
8282
p = @inferred(Graphs.optimal_contiguous_partition([1, 1, 1, 1], 4))
8383
@test p == [1:1, 2:2, 3:3, 4:4]
8484
end
85+
86+
@testset "collect_if_not_vector" begin
87+
88+
vectors = [["ab", "cd"], 1:2:9, BitVector([0, 1, 0])]
89+
not_vectors = [Set([1, 2]), (x for x in Int8[3, 4]), "xyz"]
90+
91+
@testset "identitcal if vector" for v in vectors
92+
@test Graphs.collect_if_not_vector(v) === v
93+
end
94+
95+
@testset "not identical if not vector" for v in not_vectors
96+
@test Graphs.collect_if_not_vector(v) !== v
97+
end
98+
99+
@testset "collected if not vector" for v in not_vectors
100+
actual = Graphs.collect_if_not_vector(v)
101+
expected = collect(v)
102+
@test typeof(actual) == typeof(expected)
103+
@test actual == expected
104+
end
105+
end

0 commit comments

Comments
 (0)