@@ -22,21 +22,21 @@ function postprocess!(
2222
2323 if star_or_tree_set isa StarSet
2424 # star_or_tree_set is a StarSet
25- postprocess_star_coloring! (g, color_used, color, star_or_tree_set)
25+ postprocess_star_coloring! (g, color_used, color, star_or_tree_set, offsets )
2626 else
2727 # star_or_tree_set is a TreeSet
28- postprocess_acyclic_coloring! (color_used, color, star_or_tree_set)
28+ postprocess_acyclic_coloring! (color_used, color, star_or_tree_set, offsets )
2929 end
3030 else
3131 row_color_used = zeros (Bool, nb_colors)
3232 column_color_used = color_used
3333
3434 if star_or_tree_set isa StarSet
3535 # star_or_tree_set is a StarSet
36- postprocess_star_bicoloring! (g, row_color_used, column_color_used, color, star_or_tree_set, postprocessing_minimizes)
36+ postprocess_star_bicoloring! (g, row_color_used, column_color_used, color, star_or_tree_set, offsets, postprocessing_minimizes)
3737 else
3838 # star_or_tree_set is a TreeSet
39- postprocess_acyclic_bicoloring! (row_color_used, column_color_used, color, star_or_tree_set, postprocessing_minimizes)
39+ postprocess_acyclic_bicoloring! (row_color_used, column_color_used, color, star_or_tree_set, offsets, postprocessing_minimizes)
4040 end
4141
4242 # Identify colors that are used in either the row or column partition
@@ -78,6 +78,7 @@ function postprocess_star_coloring!(
7878 color_used:: Vector{Bool} ,
7979 color:: AbstractVector{<:Integer} ,
8080 star_set:: StarSet ,
81+ occurrences:: AbstractVector{<:Integer} ,
8182)
8283 S = pattern (g)
8384 edge_to_index = edge_indices (g)
@@ -98,7 +99,8 @@ function postprocess_star_coloring!(
9899
99100 # Process the trivial stars (if any)
100101 if nb_trivial_stars > 0
101- nb_unknown_hubs = nb_trivial_stars
102+ occurrences .= 0
103+ all_trivial_stars_treated = true
102104
103105 rvS = rowvals (S)
104106 for j in axes (S, 2 )
@@ -113,14 +115,17 @@ function postprocess_star_coloring!(
113115 if color_used[color[h]]
114116 # The current hub of this trivial star is already a hub in a non-trivial star
115117 hub[s] = h
116- nb_unknown_hubs -= 1
117118 else
118119 spoke = h == j ? i : j
119120 if color_used[color[spoke]]
120121 # The current spoke of this trivial star is also a hub in a non-trivial star
121122 # Switch the hub and the spoke to avoid adding one more used color
122123 hub[s] = spoke
123- nb_unknown_hubs -= 1
124+ else
125+ all_trivial_stars_treated = false
126+ # Increment the occurrence count of vertices i and j within the remaining set of trivial stars
127+ occurrences[i] += 1
128+ occurrences[j] += 1
124129 end
125130 end
126131 end
@@ -129,7 +134,7 @@ function postprocess_star_coloring!(
129134 end
130135
131136 # Only trivial stars, where both vertices can be promoted as hubs, remain.
132- if nb_unknown_hubs > 0
137+ if ! all_trivial_stars_treated
133138 rvS = rowvals (S)
134139 for j in axes (S, 2 )
135140 for k in nzrange (S, j)
@@ -140,17 +145,24 @@ function postprocess_star_coloring!(
140145 h = hub[s]
141146 # The hub of this trivial star is still unknown
142147 if h < 0
143- h = abs (h)
144148 # We need to decide who is the hub
145149 if ! color_used[color[i]] && ! color_used[color[j]]
146- # We don't do anything special
147- hub[s] = h
148- color_used[color[h]] = true
150+ # We use the vertex with the highest occurrence as the hub
151+ # This is a heuristic to maximize the number of vertices with a neutral color
152+ # and may indirectly reduce the number of colors needed
153+ if occurrences[j] > occurrences[i]
154+ hub[s] = j
155+ color_used[color[j]] = true
156+ else
157+ hub[s] = i
158+ color_used[color[i]] = true
159+ end
149160 else
150- # Ensure that the hub vertex has a used color for decompression
151- spoke = h == j ? i : j
152- if color_used[color[spoke]] && ! color_used[color[h]]
153- hub[s] = spoke
161+ # Previously processed trivial stars determined the hub vertex for this star
162+ if color_used[color[i]]
163+ hub[s] = i
164+ else
165+ hub[s] = j
154166 end
155167 end
156168 end
@@ -168,6 +180,7 @@ function postprocess_star_bicoloring!(
168180 column_color_used:: Vector{Bool} ,
169181 color:: AbstractVector{<:Integer} ,
170182 star_set:: StarSet ,
183+ occurrences:: AbstractVector{<:Integer} ,
171184 postprocessing_minimizes:: Symbol = :all_colors ,
172185)
173186 S = pattern (g)
@@ -196,7 +209,8 @@ function postprocess_star_bicoloring!(
196209
197210 # Process the trivial stars (if any)
198211 if nb_trivial_stars > 0
199- nb_unknown_hubs = nb_trivial_stars
212+ occurrences .= 0
213+ all_trivial_stars_treated = true
200214
201215 rvS = rowvals (S)
202216 for j in axes (S, 2 )
@@ -211,14 +225,17 @@ function postprocess_star_bicoloring!(
211225 if (h ≤ n ? column_color_used[color[h]] : row_color_used[color[h]])
212226 # The current hub of this trivial star is already a hub in a non-trivial star
213227 hub[s] = h
214- nb_unknown_hubs -= 1
215228 else
216229 spoke = h == j ? i : j
217230 if (spoke ≤ n ? column_color_used[color[spoke]] : row_color_used[color[spoke]])
218231 # The current spoke of this trivial star is also a hub in a non-trivial star
219232 # Switch the hub and the spoke to avoid adding one more used color
220233 hub[s] = spoke
221- nb_unknown_hubs -= 1
234+ else
235+ all_trivial_stars_treated = false
236+ # Increment the occurrence count of vertices i and j within the remaining set of trivial stars
237+ occurrences[i] += 1
238+ occurrences[j] += 1
222239 end
223240 end
224241 end
@@ -231,7 +248,7 @@ function postprocess_star_bicoloring!(
231248 # we can achieve optimal post-processing by choosing as hubs the vertices from the opposite partition.
232249 # This is optimal because we never increase the number of colors in the target partition during this phase,
233250 # and all preceding steps of the post-processing are deterministic.
234- if nb_unknown_hubs > 0
251+ if ! all_trivial_stars_treated
235252 rvS = rowvals (S)
236253 for j in axes (S, 2 )
237254 for k in nzrange (S, j)
@@ -253,29 +270,25 @@ function postprocess_star_bicoloring!(
253270 hub[s] = i
254271 row_color_used[color[i]] = true
255272 elseif postprocessing_minimizes == :all_colors
256- # We don't do anything special
257- h = abs (h)
258- hub[s] = h
259- if h ≤ n
260- column_color_used[color[h]] = true
273+ # We use the vertex with the highest occurrence as the hub
274+ # This is a heuristic to maximize the number of vertices with a neutral color
275+ # and may indirectly reduce the number of colors needed
276+ if occurrences[j] > occurrences[i]
277+ hub[s] = j
278+ column_color_used[color[j]] = true
261279 else
262- row_color_used[color[h]] = true
280+ hub[s] = i
281+ row_color_used[color[i]] = true
263282 end
264283 else
265284 error (" The value postprocessing_minimizes = :$postprocessing_minimizes is not supported." )
266285 end
267286 else
268- # Ensure that the hub vertex has a used color for decompression
269- h = abs (h)
270- spoke = h == j ? i : j
271- if h ≤ n
272- if row_color_used[color[spoke]] && ! column_color_used[color[h]]
273- hub[s] = spoke
274- end
287+ # Previously processed trivial stars determined the hub vertex for this star
288+ if row_color_used[color[i]]
289+ hub[s] = i
275290 else
276- if column_color_used[color[spoke]] && ! row_color_used[color[h]]
277- hub[s] = spoke
278- end
291+ hub[s] = j
279292 end
280293 end
281294 end
@@ -291,6 +304,7 @@ function postprocess_acyclic_coloring!(
291304 color_used:: Vector{Bool} ,
292305 color:: AbstractVector{<:Integer} ,
293306 tree_set:: TreeSet ,
307+ occurrences:: AbstractVector{<:Integer} ,
294308)
295309 # only the colors of non-leaf vertices are used
296310 (; reverse_bfs_orders, is_star, tree_edge_indices, nt) = tree_set
@@ -324,7 +338,8 @@ function postprocess_acyclic_coloring!(
324338
325339 # Process the trivial trees (if any)
326340 if nb_trivial_trees > 0
327- nb_unknown_roots = nb_trivial_trees
341+ occurrences .= 0
342+ all_trivial_trees_treated = true
328343
329344 for k in 1 : nt
330345 # Position of the first edge in the tree
@@ -338,20 +353,23 @@ function postprocess_acyclic_coloring!(
338353 (i, j) = reverse_bfs_orders[first]
339354 if color_used[color[j]]
340355 # The current root of this trivial tree is already an internal node in a non-trivial tree
341- nb_unknown_roots -= 1
342356 else
343357 if color_used[color[i]]
344358 # The current leaf of this trivial tree is also an internal node in a non-trivial tree
345359 # Switch the root and the leaf to avoid adding one more used color
346360 reverse_bfs_orders[first] = (j, i)
347- nb_unknown_roots -= 1
361+ else
362+ all_trivial_trees_treated = false
363+ # Increment the occurrence count of vertices i and j within the remaining set of trivial trees
364+ occurrences[i] += 1
365+ occurrences[j] += 1
348366 end
349367 end
350368 end
351369 end
352370
353371 # Only trivial trees, where both vertices can be promoted as roots, remain.
354- if nb_unknown_roots > 0
372+ if ! all_trivial_trees_treated
355373 for k in 1 : nt
356374 # Position of the first edge in the tree
357375 first = tree_edge_indices[k]
@@ -363,9 +381,17 @@ function postprocess_acyclic_coloring!(
363381 if ne_tree == 1
364382 (i, j) = reverse_bfs_orders[first]
365383 if ! color_used[color[i]] && ! color_used[color[j]]
366- # We don't do anything special
367- color_used[color[j]] = true
384+ # We use the vertex with the highest occurrence as the root
385+ # This is a heuristic to maximize the number of vertices with a neutral color
386+ # and may indirectly reduce the number of colors needed
387+ if occurrences[j] > occurrences[i]
388+ color_used[color[j]] = true
389+ else
390+ reverse_bfs_orders[first] = (j, i)
391+ color_used[color[i]] = true
392+ end
368393 else
394+ # Previously processed trivial trees determined the root vertex for this tree
369395 # Ensure that the root vertex has a used color for decompression
370396 if color_used[color[i]] && ! color_used[color[j]]
371397 reverse_bfs_orders[first] = (j, i)
@@ -383,6 +409,7 @@ function postprocess_acyclic_bicoloring!(
383409 column_color_used:: Vector{Bool} ,
384410 color:: AbstractVector{<:Integer} ,
385411 tree_set:: TreeSet ,
412+ occurrences:: AbstractVector{<:Integer} ,
386413 postprocessing_minimizes:: Symbol = :all_colors ,
387414)
388415 # only the colors of non-leaf vertices are used
@@ -426,7 +453,8 @@ function postprocess_acyclic_bicoloring!(
426453
427454 # Process the trivial trees (if any)
428455 if nb_trivial_trees > 0
429- nb_unknown_roots = nb_trivial_trees
456+ occurrences .= 0
457+ all_trivial_trees_treated = true
430458
431459 for k in 1 : nt
432460 # Position of the first edge in the tree
@@ -440,13 +468,16 @@ function postprocess_acyclic_bicoloring!(
440468 (i, j) = reverse_bfs_orders[first]
441469 if (i < j ? row_color_used[color[j]] : column_color_used[color[j]])
442470 # The current root of this trivial tree is already an internal node in a non-trivial tree
443- nb_unknown_roots -= 1
444471 else
445472 if (i < j ? column_color_used[color[i]] : row_color_used[color[i]])
446473 # The current leaf of this trivial tree is also an internal node in a non-trivial tree
447474 # Switch the root and the leaf to avoid adding one more used color
448475 reverse_bfs_orders[first] = (j, i)
449- nb_unknown_roots -= 1
476+ else
477+ all_trivial_trees_treated = false
478+ # Increment the occurrence count of vertices i and j within the remaining set of trivial trees
479+ occurrences[i] += 1
480+ occurrences[j] += 1
450481 end
451482 end
452483 end
@@ -457,7 +488,7 @@ function postprocess_acyclic_bicoloring!(
457488 # we can achieve optimal post-processing by choosing as roots the vertices from the opposite partition.
458489 # This is optimal because we never increase the number of colors in the target partition during this phase,
459490 # and all preceding steps of the post-processing are deterministic.
460- if nb_unknown_roots > 0
491+ if ! all_trivial_trees_treated
461492 for k in 1 : nt
462493 # Position of the first edge in the tree
463494 first = tree_edge_indices[k]
@@ -473,21 +504,39 @@ function postprocess_acyclic_bicoloring!(
473504 # v belongs to a column partition in the context of bicoloring
474505 v = min (i,j)
475506 column_color_used[color[v]] = true
507+ if v == i
508+ reverse_bfs_orders[first] = (j, i)
509+ end
476510 elseif postprocessing_minimizes == :column_colors
477511 # v belongs to a row partition in the context of bicoloring
478512 v = max (i,j)
479513 row_color_used[color[v]] = true
514+ if v == i
515+ reverse_bfs_orders[first] = (j, i)
516+ end
480517 elseif postprocessing_minimizes == :all_colors
481- # We don't do anything special
482- if i < j
483- row_color_used[color[j]] = true
518+ # We use the vertex with the highest occurrence as the root
519+ # This is a heuristic to maximize the number of vertices with a neutral color
520+ # and may indirectly reduce the number of colors needed
521+ if occurrences[j] > occurrences[i]
522+ if i < j
523+ row_color_used[color[j]] = true
524+ else
525+ column_color_used[color[j]] = true
526+ end
484527 else
485- column_color_used[color[j]] = true
528+ reverse_bfs_orders[first] = (j, i)
529+ if i < j
530+ column_color_used[color[i]] = true
531+ else
532+ row_color_used[color[i]] = true
533+ end
486534 end
487535 else
488536 error (" The value postprocessing_minimizes = :$postprocessing_minimizes is not supported." )
489537 end
490538 else
539+ # Previously processed trivial trees determined the root vertex for this tree
491540 # Ensure that the root vertex has a used color for decompression
492541 if (i < j ? column_color_used[color[i]] && ! row_color_used[color[j]] : row_color_used[color[i]] && ! column_color_used[color[j]])
493542 reverse_bfs_orders[first] = (j, i)
0 commit comments