Skip to content

Commit 9d5d09f

Browse files
committed
Remove dictionary in acyclic coloring
1 parent b02a2d4 commit 9d5d09f

3 files changed

Lines changed: 59 additions & 103 deletions

File tree

src/coloring.jl

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ function acyclic_coloring(g::AdjacencyGraph, order::AbstractOrder; postprocessin
332332
iszero(color[x]) && continue
333333
if forbidden_colors[color[x]] != v
334334
_prevent_cycle!(
335-
v, w, x, color, first_visit_to_tree, forbidden_colors, forest
335+
v, w, x, color, g, first_visit_to_tree, forbidden_colors, forest
336336
)
337337
end
338338
end
@@ -345,20 +345,20 @@ function acyclic_coloring(g::AdjacencyGraph, order::AbstractOrder; postprocessin
345345
end
346346
for w in neighbors(g, v) # grow two-colored stars around the vertex v
347347
iszero(color[w]) && continue
348-
_grow_star!(v, w, color, first_neighbor, forest)
348+
_grow_star!(v, w, color, g, first_neighbor, forest)
349349
end
350350
for w in neighbors(g, v)
351351
iszero(color[w]) && continue
352352
for x in neighbors(g, w)
353353
(x == v || iszero(color[x])) && continue
354354
if color[x] == color[v]
355-
_merge_trees!(v, w, x, forest) # merge trees T₁ ∋ vw and T₂ ∋ wx if T₁ != T₂
355+
_merge_trees!(v, w, x, g, forest) # merge trees T₁ ∋ vw and T₂ ∋ wx if T₁ != T₂
356356
end
357357
end
358358
end
359359
end
360360

361-
tree_set = TreeSet(forest, nb_vertices(g))
361+
tree_set = TreeSet(g, forest, nb_vertices(g))
362362
if postprocessing
363363
# Reuse the vector forbidden_colors to compute offsets during post-processing
364364
postprocess!(color, tree_set, g, forbidden_colors)
@@ -373,12 +373,14 @@ function _prevent_cycle!(
373373
x::Integer,
374374
color::AbstractVector{<:Integer},
375375
# modified
376+
g::AdjacencyGraph,
376377
first_visit_to_tree::AbstractVector{<:Tuple},
377378
forbidden_colors::AbstractVector{<:Integer},
378379
forest::Forest{<:Integer},
379380
)
380381
wx = _sort(w, x)
381-
id = find_root!(forest, wx) # The edge wx belongs to the 2-colored tree T, represented by an edge with an integer ID
382+
index_wx = g.M[wx...]
383+
id = find_root!(forest, index_wx) # The edge wx belongs to the 2-colored tree T, represented by an edge with an integer ID
382384
(p, q) = first_visit_to_tree[id]
383385
if p != v # T is being visited from vertex v for the first time
384386
vw = _sort(v, w)
@@ -395,19 +397,22 @@ function _grow_star!(
395397
w::Integer,
396398
color::AbstractVector{<:Integer},
397399
# modified
400+
g::AdjacencyGraph,
398401
first_neighbor::AbstractVector{<:Tuple},
399402
forest::Forest{<:Integer},
400403
)
401404
vw = _sort(v, w)
402-
push!(forest, vw) # Create a new tree T_{vw} consisting only of edge vw
405+
# Create a new tree T_{vw} consisting only of edge vw
403406
(p, q) = first_neighbor[color[w]]
404407
if p != v # a neighbor of v with color[w] encountered for the first time
405408
first_neighbor[color[w]] = (v, w)
406409
else # merge T_{vw} with a two-colored star being grown around v
407410
vw = _sort(v, w)
408411
pq = _sort(p, q)
409-
root1 = find_root!(forest, vw)
410-
root2 = find_root!(forest, pq)
412+
index_vw = g.M[vw...]
413+
index_pq = g.M[pq...]
414+
root1 = find_root!(forest, index_vw)
415+
root2 = find_root!(forest, index_pq)
411416
root_union!(forest, root1, root2)
412417
end
413418
return nothing
@@ -419,12 +424,15 @@ function _merge_trees!(
419424
w::Integer,
420425
x::Integer,
421426
# modified
427+
g::AdjacencyGraph,
422428
forest::Forest{<:Integer},
423429
)
424430
vw = _sort(v, w)
425431
wx = _sort(w, x)
426-
root1 = find_root!(forest, vw)
427-
root2 = find_root!(forest, wx)
432+
index_vw = g.M[vw...]
433+
index_wx = g.M[wx...]
434+
root1 = find_root!(forest, index_vw)
435+
root2 = find_root!(forest, index_wx)
428436
if root1 != root2
429437
root_union!(forest, root1, root2)
430438
end
@@ -445,10 +453,8 @@ struct TreeSet
445453
is_star::Vector{Bool}
446454
end
447455

448-
function TreeSet(forest::Forest{Int}, nvertices::Int)
449-
# Forest is a structure defined in forest.jl
450-
# - forest.intmap: a dictionary that maps an edge (i, j) to an integer k
451-
# - forest.num_trees: the number of trees in the forest
456+
function TreeSet(g::AdjacencyGraph{Int}, forest::Forest{Int}, nvertices::Int)
457+
# he number of trees in the forest
452458
nt = forest.num_trees
453459

454460
# dictionary that maps a tree's root to the index of the tree
@@ -460,31 +466,33 @@ function TreeSet(forest::Forest{Int}, nvertices::Int)
460466

461467
# counter of the number of roots found
462468
k = 0
463-
for edge in keys(forest.intmap)
464-
i, j = edge
465-
root = find_root!(forest, edge)
466-
467-
# Update roots
468-
if !haskey(roots, root)
469-
k += 1
470-
roots[root] = k
471-
end
469+
for j in axes(g.M, 2)
470+
for edge_index in nzrange(g.M, j)
471+
i = g.M.rowval[edge_index]
472+
root = find_root!(forest, edge_index)
473+
474+
# Update roots
475+
if !haskey(roots, root)
476+
k += 1
477+
roots[root] = k
478+
end
472479

473-
# index of the tree T that contains this edge
474-
index_tree = roots[root]
480+
# index of the tree T that contains this edge
481+
index_tree = roots[root]
475482

476-
# Update the neighbors of i in the tree T
477-
if !haskey(trees[index_tree], i)
478-
trees[index_tree][i] = [j]
479-
else
480-
push!(trees[index_tree][i], j)
481-
end
483+
# Update the neighbors of i in the tree T
484+
if !haskey(trees[index_tree], i)
485+
trees[index_tree][i] = [j]
486+
else
487+
push!(trees[index_tree][i], j)
488+
end
482489

483-
# Update the neighbors of j in the tree T
484-
if !haskey(trees[index_tree], j)
485-
trees[index_tree][j] = [i]
486-
else
487-
push!(trees[index_tree][j], i)
490+
# Update the neighbors of j in the tree T
491+
if !haskey(trees[index_tree], j)
492+
trees[index_tree][j] = [i]
493+
else
494+
push!(trees[index_tree][j], i)
495+
end
488496
end
489497
end
490498

src/forest.jl

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,19 @@ Structure that provides fast union-find operations for constructing a forest dur
1010
$TYPEDFIELDS
1111
"""
1212
mutable struct Forest{T<:Integer}
13-
"current number of edges in the forest"
14-
num_edges::T
1513
"current number of distinct trees in the forest"
1614
num_trees::T
17-
"dictionary mapping each edge represented as a tuple of vertices to its unique integer index"
18-
intmap::Dict{Tuple{T,T},T}
1915
"vector storing the index of a parent in the tree for each edge, used in union-find operations"
2016
parents::Vector{T}
2117
"vector approximating the depth of each tree to optimize path compression"
2218
ranks::Vector{T}
2319
end
2420

2521
function Forest{T}(n::Integer) where {T<:Integer}
26-
num_edges = zero(T)
27-
num_trees = zero(T)
28-
intmap = Dict{Tuple{T,T},T}()
29-
sizehint!(intmap, n)
22+
num_trees = T(n)
3023
parents = collect(Base.OneTo(T(n)))
3124
ranks = zeros(T, T(n))
32-
return Forest{T}(num_edges, num_trees, intmap, parents, ranks)
33-
end
34-
35-
function Base.push!(forest::Forest{T}, edge::Tuple{T,T}) where {T<:Integer}
36-
forest.num_edges += 1
37-
forest.intmap[edge] = forest.num_edges
38-
forest.num_trees += one(T)
39-
return forest
25+
return Forest{T}(num_trees, parents, ranks)
4026
end
4127

4228
function _find_root!(parents::Vector{T}, index_edge::T) where {T<:Integer}
@@ -47,8 +33,8 @@ function _find_root!(parents::Vector{T}, index_edge::T) where {T<:Integer}
4733
return p
4834
end
4935

50-
function find_root!(forest::Forest{T}, edge::Tuple{T,T}) where {T<:Integer}
51-
return _find_root!(forest.parents, forest.intmap[edge])
36+
function find_root!(forest::Forest{T}, index_edge::T) where {T<:Integer}
37+
return _find_root!(forest.parents, index_edge)
5238
end
5339

5440
function root_union!(forest::Forest{T}, index_edge1::T, index_edge2::T) where {T<:Integer}

test/forest.jl

Lines changed: 11 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,74 +3,36 @@ using Test
33

44
@testset "Constructor Forest" begin
55
forest = Forest{Int}(5)
6-
7-
@test forest.num_edges == 0
8-
@test forest.num_trees == 0
9-
@test length(forest.intmap) == 0
6+
@test forest.num_trees == 5
107
@test length(forest.parents) == 5
118
@test all(forest.parents .== 1:5)
129
@test all(forest.ranks .== 0)
1310
end
1411

15-
@testset "Push edge" begin
12+
@testset "Root union -- find root" begin
1613
forest = Forest{Int}(5)
14+
@test forest.num_trees == 5
1715

18-
push!(forest, (1, 2))
19-
@test forest.num_edges == 1
20-
@test forest.num_trees == 1
21-
@test haskey(forest.intmap, (1, 2))
22-
@test forest.intmap[(1, 2)] == 1
23-
@test forest.num_trees == 1
24-
25-
push!(forest, (3, 4))
26-
@test forest.num_edges == 2
27-
@test forest.num_trees == 2
28-
@test haskey(forest.intmap, (3, 4))
29-
@test forest.intmap[(3, 4)] == 2
30-
@test forest.num_trees == 2
31-
end
32-
33-
@testset "Find root" begin
34-
forest = Forest{Int}(5)
35-
push!(forest, (1, 2))
36-
push!(forest, (3, 4))
37-
38-
@test find_root!(forest, (1, 2)) == 1
39-
@test find_root!(forest, (3, 4)) == 2
40-
end
41-
42-
@testset "Root union" begin
43-
forest = Forest{Int}(5)
44-
push!(forest, (1, 2))
45-
push!(forest, (4, 5))
46-
push!(forest, (2, 4))
47-
@test forest.num_trees == 3
48-
49-
root1 = find_root!(forest, (1, 2))
50-
root3 = find_root!(forest, (2, 4))
16+
root1 = find_root!(forest, 1)
17+
root3 = find_root!(forest, 3)
5118
@test root1 != root3
5219

5320
root_union!(forest, root1, root3)
54-
@test find_root!(forest, (2, 4)) == 1
21+
@test find_root!(forest, 3) == 1
5522
@test forest.parents[1] == 1
5623
@test forest.parents[3] == 1
5724
@test forest.ranks[1] == 1
5825
@test forest.ranks[3] == 0
59-
@test forest.num_trees == 2
26+
@test forest.num_trees == 4
6027

61-
root1 = find_root!(forest, (1, 2))
62-
root2 = find_root!(forest, (4, 5))
28+
root1 = find_root!(forest, 1)
29+
root2 = find_root!(forest, 2)
6330
@test root1 != root2
6431
root_union!(forest, root1, root2)
65-
@test find_root!(forest, (4, 5)) == 1
32+
@test find_root!(forest, 2) == 1
6633
@test forest.parents[1] == 1
6734
@test forest.parents[2] == 1
6835
@test forest.ranks[1] == 1
6936
@test forest.ranks[2] == 0
70-
@test forest.num_trees == 1
71-
72-
push!(forest, (1, 4))
73-
@test forest.num_trees == 2
74-
@test forest.intmap[(1, 4)] == 4
75-
@test forest.parents[4] == 4
37+
@test forest.num_trees == 3
7638
end

0 commit comments

Comments
 (0)