@@ -606,10 +606,10 @@ end
606606
607607function postprocess! (
608608 color:: AbstractVector{<:Integer} ,
609- star_or_tree_set :: Union{ StarSet,TreeSet} ,
609+ star_set :: StarSet ,
610610 g:: AdjacencyGraph ,
611611 offsets:: AbstractVector{<:Integer} ;
612- neutralized_first :: Symbol = :rows ,
612+ postprocessing_minimizes :: Symbol = :all_colors ,
613613)
614614 S = pattern (g)
615615 edge_to_index = edge_indices (g)
@@ -626,134 +626,177 @@ function postprocess!(
626626 end
627627 end
628628
629- if star_or_tree_set isa StarSet
630- # only the colors of the hubs are used
631- (; star, hub) = star_or_tree_set
632- nb_trivial_stars = 0
633-
634- # Iterate through all non-trivial stars
635- for s in eachindex (hub)
636- h = hub[s]
637- if h > 0
638- color_used[color[h]] = true
639- else
640- nb_trivial_stars += 1
641- end
629+ # only the colors of the hubs are used
630+ (; star, hub) = star_set
631+ nb_trivial_stars = 0
632+
633+ # Iterate through all non-trivial stars
634+ for s in eachindex (hub)
635+ h = hub[s]
636+ if h > 0
637+ color_used[color[h]] = true
638+ else
639+ nb_trivial_stars += 1
642640 end
641+ end
643642
644- # Process the trivial stars (if any)
645- nb_unknown_hubs = nb_trivial_stars
646- if nb_trivial_stars > 0
647- rvS = rowvals (S)
648- for j in axes (S, 2 )
649- for k in nzrange (S, j)
650- i = rvS[k]
651- if i > j
652- index_ij = edge_to_index[k]
653- s = star[index_ij]
654- h = hub[s]
655- if h < 0
656- h = abs (h)
657- spoke = h == j ? i : j
658- if color_used[color[h]]
659- # The current hub of this trivial star is already a hub in a non-trivial star
660- hub[s] = h
643+ # Process the trivial stars (if any)
644+ nb_unknown_hubs = nb_trivial_stars
645+ if nb_trivial_stars > 0
646+ rvS = rowvals (S)
647+ for j in axes (S, 2 )
648+ for k in nzrange (S, j)
649+ i = rvS[k]
650+ if i > j
651+ index_ij = edge_to_index[k]
652+ s = star[index_ij]
653+ h = hub[s]
654+ if h < 0
655+ h = abs (h)
656+ spoke = h == j ? i : j
657+ if color_used[color[h]]
658+ # The current hub of this trivial star is already a hub in a non-trivial star
659+ hub[s] = h
660+ nb_unknown_hubs -= 1
661+ else
662+ if color_used[color[spoke]]
663+ # The current spoke of this trivial star is also a hub in a non-trivial star
664+ # Switch the hub and the spoke to avoid adding one more used color
665+ hub[s] = spoke
661666 nb_unknown_hubs -= 1
662- else
663- if color_used[color[spoke]]
664- # The current spoke of this trivial star is also a hub in a non-trivial star
665- # Switch the hub and the spoke to avoid adding one more used color
666- hub[s] = spoke
667- nb_unknown_hubs -= 1
668- end
669667 end
670668 end
671669 end
672670 end
673671 end
674672 end
675- # Only trivial stars where both vertices can be promoted as hub are remaining.
676- # In the context of bicoloring, if we want to minimize the number of row colors OR the number of column colors,
677- # we can have the optimal post-processing by taking as hub the vertices in the other partition.
678- # It is optimal because we will never increase the number of colors in the partition specified by `neutralized_first`
679- # in this phase and everything else in the post-processing is deterministic.
680- if nb_unknown_hubs > 0
681- rvS = rowvals (S)
682- for j in axes (S, 2 )
683- for k in nzrange (S, j)
684- i = rvS[k]
685- if i > j
686- index_ij = edge_to_index[k]
687- s = star[index_ij]
688- h = hub[s]
689- # The hub of this trivial star is still unknown
690- if h < 0
691- if neutralized_first == :rows
692- # j represents a column in the context of bicoloring
693- hub[s] = j
694- color_used[color[j]] = true
695- else # neutralized_first == :cols
696- # i represents a row in the context of bicoloring
697- hub[s] = i
698- color_used[color[i]] = true
699- end
673+ end
674+ # Only trivial stars, where both vertices can be promoted as hubs, remain.
675+ # In the context of bicoloring, if we aim to minimize either the number of row colors or the number of column colors,
676+ # we can achieve optimal post-processing by choosing as hubs the vertices from the opposite partition.
677+ # This is optimal because we never increase the number of colors in the target partition during this phase,
678+ # and all preceding steps of the post-processing are deterministic.
679+ if nb_unknown_hubs > 0
680+ rvS = rowvals (S)
681+ for j in axes (S, 2 )
682+ for k in nzrange (S, j)
683+ i = rvS[k]
684+ if i > j
685+ index_ij = edge_to_index[k]
686+ s = star[index_ij]
687+ h = hub[s]
688+ # The hub of this trivial star is still unknown
689+ if h < 0
690+ if postprocessing_minimizes == :row_colors
691+ # j bzlongs to a column partition in the context of bicoloring
692+ hub[s] = j
693+ color_used[color[j]] = true
694+ elseif postprocessing_minimizes == :column_colors
695+ # i belongs to a row partition in the context of bicoloring
696+ hub[s] = i
697+ color_used[color[i]] = true
698+ else postprocessing_minimizes == :all_colors
699+ l = abs (h)
700+ hub[s] = l
701+ color_used[color[l]] = true
700702 end
701703 end
702704 end
703705 end
704706 end
705- else
706- # only the colors of non-leaf vertices are used
707- (; reverse_bfs_orders, is_star, tree_edge_indices, nt) = star_or_tree_set
708- nb_trivial_trees = 0
707+ end
708+
709+ # if at least one of the colors is useless, modify the color assignments of vertices
710+ if any (! , color_used)
711+ num_colors_useless = 0
712+
713+ # determine what are the useless colors and compute the offsets
714+ for ci in 1 : nb_colors
715+ if color_used[ci]
716+ offsets[ci] = num_colors_useless
717+ else
718+ num_colors_useless += 1
719+ end
720+ end
721+
722+ # assign the neutral color to every vertex with a useless color and remap the colors
723+ for i in eachindex (color)
724+ ci = color[i]
725+ if ! color_used[ci]
726+ # assign the neutral color
727+ color[i] = 0
728+ else
729+ # remap the color to not have any gap
730+ color[i] -= offsets[ci]
731+ end
732+ end
733+ end
734+ return color
735+ end
736+
737+ function postprocess! (
738+ color:: AbstractVector{<:Integer} ,
739+ tree_set:: TreeSet ,
740+ g:: AdjacencyGraph ,
741+ offsets:: AbstractVector{<:Integer} ;
742+ postprocessing_minimizes:: Symbol = :all_colors ,
743+ )
744+ S = pattern (g)
745+ edge_to_index = edge_indices (g)
746+ # flag which colors are actually used during decompression
747+ nb_colors = maximum (color)
748+ color_used = zeros (Bool, nb_colors)
749+
750+ # only the colors of non-leaf vertices are used
751+ (; reverse_bfs_orders, is_star, tree_edge_indices, nt) = tree_set
752+ nb_trivial_trees = 0
753+
754+ # Iterate through all non-trivial trees
755+ for k in 1 : nt
756+ # Position of the first edge in the tree
757+ first = tree_edge_indices[k]
758+
759+ # Total number of edges in the tree
760+ ne_tree = tree_edge_indices[k + 1 ] - first
761+
762+ # Check if we have more than one edge in the tree (non-trivial tree)
763+ if ne_tree > 1
764+ # Determine if the tree is a star
765+ if is_star[k]
766+ # It is a non-trivial star and only the color of the hub is needed
767+ (_, hub) = reverse_bfs_orders[first]
768+ color_used[color[hub]] = true
769+ else
770+ # It is not a star and both colors are needed during the decompression
771+ (i, j) = reverse_bfs_orders[first]
772+ color_used[color[i]] = true
773+ color_used[color[j]] = true
774+ end
775+ else
776+ nb_trivial_trees += 1
777+ end
778+ end
709779
710- # Iterate through all non-trivial trees
780+ # Process the trivial trees (if any)
781+ if nb_trivial_trees > 0
711782 for k in 1 : nt
712783 # Position of the first edge in the tree
713784 first = tree_edge_indices[k]
714785
715786 # Total number of edges in the tree
716787 ne_tree = tree_edge_indices[k + 1 ] - first
717788
718- # Check if we have more than one edge in the tree (non-trivial tree)
719- if ne_tree > 1
720- # Determine if the tree is a star
721- if is_star[k ]
722- # It is a non-trivial star and only the color of the hub is needed
723- (_, hub) = reverse_bfs_orders[first]
724- color_used[color[hub]] = true
789+ # Check if we have exactly one edge in the tree
790+ if ne_tree == 1
791+ (i, j) = reverse_bfs_orders[first]
792+ if color_used[color[i] ]
793+ # Make i the root to avoid possibly adding one more used color
794+ # Switch it with the (only) leaf
795+ reverse_bfs_orders[first] = (j, i)
725796 else
726- # It is not a star and both colors are needed during the decompression
727- (i, j) = reverse_bfs_orders[first]
728- color_used[color[i]] = true
797+ # Keep j as the root
729798 color_used[color[j]] = true
730799 end
731- else
732- nb_trivial_trees += 1
733- end
734- end
735-
736- # Process the trivial trees (if any)
737- if nb_trivial_trees > 0
738- for k in 1 : nt
739- # Position of the first edge in the tree
740- first = tree_edge_indices[k]
741-
742- # Total number of edges in the tree
743- ne_tree = tree_edge_indices[k + 1 ] - first
744-
745- # Check if we have exactly one edge in the tree
746- if ne_tree == 1
747- (i, j) = reverse_bfs_orders[first]
748- if color_used[color[i]]
749- # Make i the root to avoid possibly adding one more used color
750- # Switch it with the (only) leaf
751- reverse_bfs_orders[first] = (j, i)
752- else
753- # Keep j as the root
754- color_used[color[j]] = true
755- end
756- end
757800 end
758801 end
759802 end
0 commit comments