Skip to content

Commit ee8bb7a

Browse files
amontoisongdalle
andauthored
Improve the computation of TreeSet (#228)
* Improve the computation of TreeSet * Update a comment --------- Co-authored-by: Guillaume Dalle <22795598+gdalle@users.noreply.github.com>
1 parent ba7da77 commit ee8bb7a

3 files changed

Lines changed: 90 additions & 40 deletions

File tree

src/coloring.jl

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,9 @@ function acyclic_coloring(
291291
end
292292
end
293293

294-
tree_set = TreeSet(g, forest)
294+
buffer = forbidden_colors
295+
reverse_bfs_orders = first_visit_to_tree
296+
tree_set = TreeSet(g, forest, buffer, reverse_bfs_orders)
295297
if postprocessing
296298
# Reuse the vector forbidden_colors to compute offsets during post-processing
297299
offsets = forbidden_colors
@@ -372,19 +374,29 @@ Encode a set of 2-colored trees resulting from the [`acyclic_coloring`](@ref) al
372374
$TYPEDFIELDS
373375
"""
374376
struct TreeSet{T}
375-
reverse_bfs_orders::Vector{Vector{Tuple{T,T}}}
377+
reverse_bfs_orders::Vector{Tuple{T,T}}
376378
is_star::Vector{Bool}
379+
num_edges_per_tree::Vector{T}
377380
end
378381

379-
function TreeSet(g::AdjacencyGraph{T}, forest::Forest{T}) where {T}
382+
function TreeSet(
383+
g::AdjacencyGraph{T},
384+
forest::Forest{T},
385+
buffer::AbstractVector{T},
386+
reverse_bfs_orders::Vector{Tuple{T,T}},
387+
) where {T}
380388
S = pattern(g)
381389
edge_to_index = edge_indices(g)
382390
nv = nb_vertices(g)
383391
nt = forest.num_trees
384392

385-
# dictionary that maps a tree's root to the index of the tree
386-
roots = Dict{T,T}()
387-
sizehint!(roots, nt)
393+
# root_to_tree is a vector that maps a tree's root to the index of the tree
394+
# We can recycle forest.ranks because we don't need it anymore to merge trees
395+
root_to_tree = forest.ranks
396+
fill!(root_to_tree, zero(T))
397+
398+
# Contains the number of edges per tree
399+
num_edges_per_tree = zeros(T, nt)
388400

389401
# vector of dictionaries where each dictionary stores the neighbors of each vertex in a tree
390402
trees = [Dict{T,Vector{T}}() for i in 1:nt]
@@ -401,13 +413,14 @@ function TreeSet(g::AdjacencyGraph{T}, forest::Forest{T}) where {T}
401413
root = find_root!(forest, index_ij)
402414

403415
# Update roots
404-
if !haskey(roots, root)
416+
if iszero(root_to_tree[root])
405417
nr += 1
406-
roots[root] = nr
418+
root_to_tree[root] = nr
407419
end
408420

409421
# index of the tree T that contains this edge
410-
index_tree = roots[root]
422+
index_tree = root_to_tree[root]
423+
num_edges_per_tree[index_tree] += 1
411424

412425
# Update the neighbors of i in the tree T
413426
if !haskey(trees[index_tree], i)
@@ -427,10 +440,7 @@ function TreeSet(g::AdjacencyGraph{T}, forest::Forest{T}) where {T}
427440
end
428441

429442
# degrees is a vector of integers that stores the degree of each vertex in a tree
430-
degrees = Vector{T}(undef, nv)
431-
432-
# reverse breadth first (BFS) traversal order for each tree in the forest
433-
reverse_bfs_orders = [Tuple{T,T}[] for i in 1:nt]
443+
degrees = buffer
434444

435445
# nvmax is the number of vertices of the biggest tree in the forest
436446
nvmax = 0
@@ -446,6 +456,10 @@ function TreeSet(g::AdjacencyGraph{T}, forest::Forest{T}) where {T}
446456
# meaning that one vertex is directly connected to all other vertices in the tree
447457
is_star = Vector{Bool}(undef, nt)
448458

459+
# Number of edges treated
460+
num_edges_treated = zero(T)
461+
462+
# reverse_bfs_orders contains the reverse breadth first (BFS) traversal order for each tree in the forest
449463
for k in 1:nt
450464
tree = trees[k]
451465

@@ -483,7 +497,8 @@ function TreeSet(g::AdjacencyGraph{T}, forest::Forest{T}) where {T}
483497
# Check if neighbor is the parent of the leaf or if it was a child before the tree was pruned
484498
if degrees[neighbor] != 0
485499
# (leaf, neighbor) represents the next edge to visit during decompression
486-
push!(reverse_bfs_orders[k], (leaf, neighbor))
500+
num_edges_treated += 1
501+
reverse_bfs_orders[num_edges_treated] = (leaf, neighbor)
487502

488503
if bool_star
489504
# Initialize the potential hub of the star with the first parent of a leaf
@@ -514,7 +529,7 @@ function TreeSet(g::AdjacencyGraph{T}, forest::Forest{T}) where {T}
514529
is_star[k] = bool_star
515530
end
516531

517-
return TreeSet(reverse_bfs_orders, is_star)
532+
return TreeSet(reverse_bfs_orders, is_star, num_edges_per_tree)
518533
end
519534

520535
## Postprocessing, mirrors decompression code
@@ -582,46 +597,52 @@ function postprocess!(
582597
end
583598
else
584599
# only the colors of non-leaf vertices are used
585-
(; reverse_bfs_orders, is_star) = star_or_tree_set
600+
(; reverse_bfs_orders, is_star, num_edges_per_tree) = star_or_tree_set
586601
nb_trivial_trees = 0
587602

603+
# Index of the first edge in reverse_bfs_orders for the current tree
604+
first = 1
605+
588606
# Iterate through all non-trivial trees
589-
for k in eachindex(reverse_bfs_orders)
590-
reverse_bfs_order = reverse_bfs_orders[k]
607+
for k in eachindex(num_edges_per_tree)
608+
ne_tree = num_edges_per_tree[k]
591609
# Check if we have more than one edge in the tree (non-trivial tree)
592-
if length(reverse_bfs_order) > 1
610+
if ne_tree > 1
593611
# Determine if the tree is a star
594612
if is_star[k]
595613
# It is a non-trivial star and only the color of the hub is needed
596-
(_, hub) = reverse_bfs_order[1]
614+
(_, hub) = reverse_bfs_orders[first]
597615
color_used[color[hub]] = true
598616
else
599617
# It is not a star and both colors are needed during the decompression
600-
(i, j) = reverse_bfs_order[1]
618+
(i, j) = reverse_bfs_orders[first]
601619
color_used[color[i]] = true
602620
color_used[color[j]] = true
603621
end
604622
else
605623
nb_trivial_trees += 1
606624
end
625+
first += ne_tree
607626
end
608627

609628
# Process the trivial trees (if any)
610629
if nb_trivial_trees > 0
611-
for k in eachindex(reverse_bfs_orders)
612-
reverse_bfs_order = reverse_bfs_orders[k]
630+
first = 1
631+
for k in eachindex(num_edges_per_tree)
632+
ne_tree = num_edges_per_tree[k]
613633
# Check if we have exactly one edge in the tree
614-
if length(reverse_bfs_order) == 1
615-
(i, j) = reverse_bfs_order[1]
634+
if ne_tree == 1
635+
(i, j) = reverse_bfs_orders[first]
616636
if color_used[color[i]]
617637
# Make i the root to avoid possibly adding one more used color
618638
# Switch it with the (only) leaf
619-
reverse_bfs_order[1] = (j, i)
639+
reverse_bfs_orders[first] = (j, i)
620640
else
621641
# Keep j as the root
622642
color_used[color[j]] = true
623643
end
624644
end
645+
first += ne_tree
625646
end
626647
end
627648
end

src/decompression.jl

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ end
517517
function decompress!(
518518
A::AbstractMatrix, B::AbstractMatrix, result::TreeSetColoringResult, uplo::Symbol=:F
519519
)
520-
(; ag, color, reverse_bfs_orders, buffer) = result
520+
(; ag, color, reverse_bfs_orders, num_edges_per_tree, buffer) = result
521521
(; S) = ag
522522
uplo == :F && check_same_pattern(A, S)
523523
R = eltype(A)
@@ -531,24 +531,32 @@ function decompress!(
531531

532532
# Recover the diagonal coefficients of A
533533
if has_diagonal(ag)
534-
for i in axes(A, 1)
534+
for i in axes(S, 1)
535535
if !iszero(S[i, i])
536536
A[i, i] = B[i, color[i]]
537537
end
538538
end
539539
end
540540

541+
# Index of the first edge in reverse_bfs_orders for the current tree
542+
first = 1
543+
541544
# Recover the off-diagonal coefficients of A
542-
for k in eachindex(reverse_bfs_orders)
545+
for k in eachindex(num_edges_per_tree)
546+
ne_tree = num_edges_per_tree[k]
547+
last = first + ne_tree - 1
548+
543549
# Reset the buffer to zero for all vertices in a tree (except the root)
544-
for (vertex, _) in reverse_bfs_orders[k]
550+
for pos in first:last
551+
(vertex, _) = reverse_bfs_orders[pos]
545552
buffer_right_type[vertex] = zero(R)
546553
end
547554
# Reset the buffer to zero for the root vertex
548-
(_, root) = reverse_bfs_orders[k][end]
555+
(_, root) = reverse_bfs_orders[last]
549556
buffer_right_type[root] = zero(R)
550557

551-
for (i, j) in reverse_bfs_orders[k]
558+
for pos in first:last
559+
(i, j) = reverse_bfs_orders[pos]
552560
val = B[i, color[j]] - buffer_right_type[i]
553561
buffer_right_type[j] = buffer_right_type[j] + val
554562

@@ -559,6 +567,7 @@ function decompress!(
559567
A[j, i] = val
560568
end
561569
end
570+
first += ne_tree
562571
end
563572
return A
564573
end
@@ -573,6 +582,7 @@ function decompress!(
573582
ag,
574583
color,
575584
reverse_bfs_orders,
585+
num_edges_per_tree,
576586
diagonal_indices,
577587
diagonal_nzind,
578588
lower_triangle_offsets,
@@ -612,20 +622,28 @@ function decompress!(
612622
end
613623
end
614624

625+
# Index of the first edge in reverse_bfs_orders for the current tree
626+
first = 1
627+
615628
# Index of offsets in lower_triangle_offsets and upper_triangle_offsets
616629
counter = 0
617630

618631
# Recover the off-diagonal coefficients of A
619-
for k in eachindex(reverse_bfs_orders)
632+
for k in eachindex(num_edges_per_tree)
633+
ne_tree = num_edges_per_tree[k]
634+
last = first + ne_tree - 1
635+
620636
# Reset the buffer to zero for all vertices in a tree (except the root)
621-
for (vertex, _) in reverse_bfs_orders[k]
637+
for pos in first:last
638+
(vertex, _) = reverse_bfs_orders[pos]
622639
buffer_right_type[vertex] = zero(R)
623640
end
624641
# Reset the buffer to zero for the root vertex
625-
(_, root) = reverse_bfs_orders[k][end]
642+
(_, root) = reverse_bfs_orders[last]
626643
buffer_right_type[root] = zero(R)
627644

628-
for (i, j) in reverse_bfs_orders[k]
645+
for pos in first:last
646+
(i, j) = reverse_bfs_orders[pos]
629647
counter += 1
630648
val = B[i, color[j]] - buffer_right_type[i]
631649
buffer_right_type[j] = buffer_right_type[j] + val
@@ -665,6 +683,7 @@ function decompress!(
665683
end
666684
#! format: on
667685
end
686+
first += ne_tree
668687
end
669688
return A
670689
end

src/result.jl

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,8 @@ struct TreeSetColoringResult{
314314
ag::G
315315
color::Vector{T}
316316
group::GT
317-
reverse_bfs_orders::Vector{Vector{Tuple{T,T}}}
317+
reverse_bfs_orders::Vector{Tuple{T,T}}
318+
num_edges_per_tree::Vector{T}
318319
diagonal_indices::Vector{T}
319320
diagonal_nzind::Vector{T}
320321
lower_triangle_offsets::Vector{T}
@@ -329,7 +330,7 @@ function TreeSetColoringResult(
329330
tree_set::TreeSet{<:Integer},
330331
decompression_eltype::Type{R},
331332
) where {T<:Integer,R}
332-
(; reverse_bfs_orders) = tree_set
333+
(; reverse_bfs_orders, num_edges_per_tree) = tree_set
333334
(; S) = ag
334335
nvertices = length(color)
335336
group = group_by_color(T, color)
@@ -358,11 +359,18 @@ function TreeSetColoringResult(
358359
lower_triangle_offsets = Vector{T}(undef, nedges)
359360
upper_triangle_offsets = Vector{T}(undef, nedges)
360361

362+
# Index of the first edge in reverse_bfs_orders for the current tree
363+
first = 1
364+
361365
# Index in lower_triangle_offsets and upper_triangle_offsets
362366
index_offsets = 0
363367

364-
for k in eachindex(reverse_bfs_orders)
365-
for (leaf, neighbor) in reverse_bfs_orders[k]
368+
for k in eachindex(num_edges_per_tree)
369+
ne_tree = num_edges_per_tree[k]
370+
last = first + ne_tree - 1
371+
372+
for pos in first:last
373+
(leaf, neighbor) = reverse_bfs_orders[pos]
366374
# Update lower_triangle_offsets and upper_triangle_offsets
367375
i = leaf
368376
j = neighbor
@@ -393,6 +401,7 @@ function TreeSetColoringResult(
393401
end
394402
#! format: on
395403
end
404+
first += ne_tree
396405
end
397406

398407
# buffer holds the sum of edge values for subtrees in a tree.
@@ -405,6 +414,7 @@ function TreeSetColoringResult(
405414
color,
406415
group,
407416
reverse_bfs_orders,
417+
num_edges_per_tree,
408418
diagonal_indices,
409419
diagonal_nzind,
410420
lower_triangle_offsets,

0 commit comments

Comments
 (0)