Skip to content

Commit 126f4bb

Browse files
committed
Revisit post-processing
1 parent b53dbf9 commit 126f4bb

File tree

7 files changed

+308
-176
lines changed

7 files changed

+308
-176
lines changed

src/SparseMatrixColorings.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ include("graph.jl")
4747
include("forest.jl")
4848
include("order.jl")
4949
include("coloring.jl")
50+
include("postprocessing.jl")
5051
include("result.jl")
5152
include("matrices.jl")
5253
include("interface.jl")

src/coloring.jl

Lines changed: 8 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ The optional `forced_colors` keyword argument is used to enforce predefined vert
109109
function star_coloring(
110110
g::AdjacencyGraph{T},
111111
vertices_in_order::AbstractVector{<:Integer},
112-
postprocessing::Bool;
112+
postprocessing::Bool,
113+
postprocessing_minimizes::Symbol;
113114
forced_colors::Union{AbstractVector{<:Integer},Nothing}=nothing,
114115
) where {T<:Integer}
115116
# Initialize data structures
@@ -168,7 +169,7 @@ function star_coloring(
168169
if postprocessing
169170
# Reuse the vector forbidden_colors to compute offsets during post-processing
170171
offsets = forbidden_colors
171-
postprocess!(color, star_set, g, offsets)
172+
postprocess!(color, star_set, g, offsets, postprocessing_minimizes)
172173
end
173174
return color, star_set
174175
end
@@ -273,7 +274,10 @@ If `postprocessing=true`, some colors might be replaced with `0` (the "neutral"
273274
> [_New Acyclic and Star Coloring Algorithms with Application to Computing Hessians_](https://epubs.siam.org/doi/abs/10.1137/050639879), Gebremedhin et al. (2007), Algorithm 3.1
274275
"""
275276
function acyclic_coloring(
276-
g::AdjacencyGraph{T}, vertices_in_order::AbstractVector{<:Integer}, postprocessing::Bool
277+
g::AdjacencyGraph{T},
278+
vertices_in_order::AbstractVector{<:Integer},
279+
postprocessing::Bool,
280+
postprocessing_minimizes::Symbol,
277281
) where {T<:Integer}
278282
# Initialize data structures
279283
nv = nb_vertices(g)
@@ -345,7 +349,7 @@ function acyclic_coloring(
345349
if postprocessing
346350
# Reuse the vector forbidden_colors to compute offsets during post-processing
347351
offsets = forbidden_colors
348-
postprocess!(color, tree_set, g, offsets)
352+
postprocess!(color, tree_set, g, offsets, postprocessing_minimizes)
349353
end
350354
return color, tree_set
351355
end
@@ -643,150 +647,3 @@ function TreeSet(
643647

644648
return TreeSet(reverse_bfs_orders, is_star, tree_edge_indices, nt)
645649
end
646-
647-
## Postprocessing, mirrors decompression code
648-
649-
function postprocess!(
650-
color::AbstractVector{<:Integer},
651-
star_or_tree_set::Union{StarSet,TreeSet},
652-
g::AdjacencyGraph,
653-
offsets::AbstractVector{<:Integer},
654-
)
655-
S = pattern(g)
656-
edge_to_index = edge_indices(g)
657-
# flag which colors are actually used during decompression
658-
nb_colors = maximum(color)
659-
color_used = zeros(Bool, nb_colors)
660-
661-
# nonzero diagonal coefficients force the use of their respective color (there can be no neutral colors if the diagonal is fully nonzero)
662-
if has_diagonal(g)
663-
for i in axes(S, 1)
664-
if !iszero(S[i, i])
665-
color_used[color[i]] = true
666-
end
667-
end
668-
end
669-
670-
if star_or_tree_set isa StarSet
671-
# only the colors of the hubs are used
672-
(; star, hub) = star_or_tree_set
673-
nb_trivial_stars = 0
674-
675-
# Iterate through all non-trivial stars
676-
for s in eachindex(hub)
677-
h = hub[s]
678-
if h > 0
679-
color_used[color[h]] = true
680-
else
681-
nb_trivial_stars += 1
682-
end
683-
end
684-
685-
# Process the trivial stars (if any)
686-
if nb_trivial_stars > 0
687-
rvS = rowvals(S)
688-
for j in axes(S, 2)
689-
for k in nzrange(S, j)
690-
i = rvS[k]
691-
if i > j
692-
index_ij = edge_to_index[k]
693-
s = star[index_ij]
694-
h = hub[s]
695-
if h < 0
696-
h = abs(h)
697-
spoke = h == j ? i : j
698-
if color_used[color[spoke]]
699-
# Switch the hub and the spoke to possibly avoid adding one more used color
700-
hub[s] = spoke
701-
else
702-
# Keep the current hub
703-
color_used[color[h]] = true
704-
end
705-
end
706-
end
707-
end
708-
end
709-
end
710-
else
711-
# only the colors of non-leaf vertices are used
712-
(; reverse_bfs_orders, is_star, tree_edge_indices, nt) = star_or_tree_set
713-
nb_trivial_trees = 0
714-
715-
# Iterate through all non-trivial trees
716-
for k in 1:nt
717-
# Position of the first edge in the tree
718-
first = tree_edge_indices[k]
719-
720-
# Total number of edges in the tree
721-
ne_tree = tree_edge_indices[k + 1] - first
722-
723-
# Check if we have more than one edge in the tree (non-trivial tree)
724-
if ne_tree > 1
725-
# Determine if the tree is a star
726-
if is_star[k]
727-
# It is a non-trivial star and only the color of the hub is needed
728-
(_, hub) = reverse_bfs_orders[first]
729-
color_used[color[hub]] = true
730-
else
731-
# It is not a star and both colors are needed during the decompression
732-
(i, j) = reverse_bfs_orders[first]
733-
color_used[color[i]] = true
734-
color_used[color[j]] = true
735-
end
736-
else
737-
nb_trivial_trees += 1
738-
end
739-
end
740-
741-
# Process the trivial trees (if any)
742-
if nb_trivial_trees > 0
743-
for k in 1:nt
744-
# Position of the first edge in the tree
745-
first = tree_edge_indices[k]
746-
747-
# Total number of edges in the tree
748-
ne_tree = tree_edge_indices[k + 1] - first
749-
750-
# Check if we have exactly one edge in the tree
751-
if ne_tree == 1
752-
(i, j) = reverse_bfs_orders[first]
753-
if color_used[color[i]]
754-
# Make i the root to avoid possibly adding one more used color
755-
# Switch it with the (only) leaf
756-
reverse_bfs_orders[first] = (j, i)
757-
else
758-
# Keep j as the root
759-
color_used[color[j]] = true
760-
end
761-
end
762-
end
763-
end
764-
end
765-
766-
# if at least one of the colors is useless, modify the color assignments of vertices
767-
if any(!, color_used)
768-
num_colors_useless = 0
769-
770-
# determine what are the useless colors and compute the offsets
771-
for ci in 1:nb_colors
772-
if color_used[ci]
773-
offsets[ci] = num_colors_useless
774-
else
775-
num_colors_useless += 1
776-
end
777-
end
778-
779-
# assign the neutral color to every vertex with a useless color and remap the colors
780-
for i in eachindex(color)
781-
ci = color[i]
782-
if !color_used[ci]
783-
# assign the neutral color
784-
color[i] = 0
785-
else
786-
# remap the color to not have any gap
787-
color[i] -= offsets[ci]
788-
end
789-
end
790-
end
791-
return color
792-
end

src/interface.jl

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,12 @@ It is passed as an argument to the main function [`coloring`](@ref).
6969
7070
# Constructors
7171
72-
GreedyColoringAlgorithm{decompression}(order=NaturalOrder(); postprocessing=false)
73-
GreedyColoringAlgorithm(order=NaturalOrder(); postprocessing=false, decompression=:direct)
72+
GreedyColoringAlgorithm{decompression}(order=NaturalOrder(); postprocessing=false, postprocessing_minimizes=:all_colors)
73+
GreedyColoringAlgorithm(order=NaturalOrder(); postprocessing=false, postprocessing_minimizes=:all_colors, decompression=:direct)
7474
7575
- `order::Union{AbstractOrder,Tuple}`: the order in which the columns or rows are colored, which can impact the number of colors. Can also be a tuple of different orders to try out, from which the best order (the one with the lowest total number of colors) will be used.
7676
- `postprocessing::Bool`: whether or not the coloring will be refined by assigning the neutral color `0` to some vertices.
77+
- `postprocessing_minimizes::Symbol`: either `:all_colors`, `:row_colors` or `:column_colors`. The options `:row_colors` and `:column_colors` are only available for bicoloring. Otherwise, the setting defaults to `:all_colors`.
7778
- `decompression::Symbol`: either `:direct` or `:substitution`. Usually `:substitution` leads to fewer colors, at the cost of a more expensive coloring (and decompression). When `:substitution` is not applicable, it falls back on `:direct` decompression.
7879
7980
!!! warning
@@ -98,27 +99,30 @@ struct GreedyColoringAlgorithm{decompression,N,O<:NTuple{N,AbstractOrder}} <:
9899
ADTypes.AbstractColoringAlgorithm
99100
orders::O
100101
postprocessing::Bool
102+
postprocessing_minimizes::Symbol
101103

102104
function GreedyColoringAlgorithm{decompression}(
103105
order_or_orders::Union{AbstractOrder,Tuple}=NaturalOrder();
104106
postprocessing::Bool=false,
107+
postprocessing_minimizes::Symbol=:all_colors,
105108
) where {decompression}
106109
check_valid_algorithm(decompression)
107110
if order_or_orders isa AbstractOrder
108111
orders = (order_or_orders,)
109112
else
110113
orders = order_or_orders
111114
end
112-
return new{decompression,length(orders),typeof(orders)}(orders, postprocessing)
115+
return new{decompression,length(orders),typeof(orders)}(orders, postprocessing, postprocessing_minimizes)
113116
end
114117
end
115118

116119
function GreedyColoringAlgorithm(
117120
order_or_orders::Union{AbstractOrder,Tuple}=NaturalOrder();
118121
postprocessing::Bool=false,
119122
decompression::Symbol=:direct,
123+
postprocessing_minimizes::Symbol=:all_colors,
120124
)
121-
return GreedyColoringAlgorithm{decompression}(order_or_orders; postprocessing)
125+
return GreedyColoringAlgorithm{decompression}(order_or_orders; postprocessing, postprocessing_minimizes)
122126
end
123127

124128
## Coloring
@@ -282,7 +286,7 @@ function _coloring(
282286
ag = AdjacencyGraph(A; has_diagonal=true)
283287
color_and_star_set_by_order = map(algo.orders) do order
284288
vertices_in_order = vertices(ag, order)
285-
return star_coloring(ag, vertices_in_order, algo.postprocessing; forced_colors)
289+
return star_coloring(ag, vertices_in_order, algo.postprocessing, :all_colors; forced_colors)
286290
end
287291
color, star_set = argmin(maximum first, color_and_star_set_by_order)
288292
if speed_setting isa WithResult
@@ -303,7 +307,7 @@ function _coloring(
303307
ag = AdjacencyGraph(A; has_diagonal=true)
304308
color_and_tree_set_by_order = map(algo.orders) do order
305309
vertices_in_order = vertices(ag, order)
306-
return acyclic_coloring(ag, vertices_in_order, algo.postprocessing)
310+
return acyclic_coloring(ag, vertices_in_order, algo.postprocessing, :all_colors)
307311
end
308312
color, tree_set = argmin(maximum first, color_and_tree_set_by_order)
309313
if speed_setting isa WithResult
@@ -327,7 +331,7 @@ function _coloring(
327331
outputs_by_order = map(algo.orders) do order
328332
vertices_in_order = vertices(ag, order)
329333
_color, _star_set = star_coloring(
330-
ag, vertices_in_order, algo.postprocessing; forced_colors
334+
ag, vertices_in_order, algo.postprocessing, algo.postprocessing_minimizes; forced_colors
331335
)
332336
(_row_color, _column_color, _symmetric_to_row, _symmetric_to_column) = remap_colors(
333337
eltype(ag), _color, maximum(_color), size(A)...
@@ -373,7 +377,7 @@ function _coloring(
373377
ag = AdjacencyGraph(A_and_Aᵀ, edge_to_index; has_diagonal=false)
374378
outputs_by_order = map(algo.orders) do order
375379
vertices_in_order = vertices(ag, order)
376-
_color, _tree_set = acyclic_coloring(ag, vertices_in_order, algo.postprocessing)
380+
_color, _tree_set = acyclic_coloring(ag, vertices_in_order, algo.postprocessing, algo.postprocessing_minimizes)
377381
(_row_color, _column_color, _symmetric_to_row, _symmetric_to_column) = remap_colors(
378382
eltype(ag), _color, maximum(_color), size(A)...
379383
)

0 commit comments

Comments
 (0)