@@ -648,10 +648,10 @@ end
648648
649649function postprocess! (
650650 color:: AbstractVector{<:Integer} ,
651- star_or_tree_set :: Union{ StarSet,TreeSet} ,
651+ star_set :: StarSet ,
652652 g:: AdjacencyGraph ,
653653 offsets:: AbstractVector{<:Integer} ;
654- neutralized_first :: Symbol = :rows ,
654+ postprocessing_minimizes :: Symbol = :all_colors ,
655655)
656656 S = pattern (g)
657657 edge_to_index = edge_indices (g)
@@ -668,134 +668,177 @@ function postprocess!(
668668 end
669669 end
670670
671- if star_or_tree_set isa StarSet
672- # only the colors of the hubs are used
673- (; star, hub) = star_or_tree_set
674- nb_trivial_stars = 0
671+ # only the colors of the hubs are used
672+ (; star, hub) = star_set
673+ nb_trivial_stars = 0
675674
676- # Iterate through all non-trivial stars
677- for s in eachindex (hub)
678- h = hub[s]
679- if h > 0
680- color_used[color[h]] = true
681- else
682- nb_trivial_stars += 1
683- end
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
684682 end
683+ end
685684
686- # Process the trivial stars (if any)
687- nb_unknown_hubs = nb_trivial_stars
688- if nb_trivial_stars > 0
689- rvS = rowvals (S)
690- for j in axes (S, 2 )
691- for k in nzrange (S, j)
692- i = rvS[k]
693- if i > j
694- index_ij = edge_to_index[k]
695- s = star[index_ij]
696- h = hub[s]
697- if h < 0
698- h = abs (h)
699- spoke = h == j ? i : j
700- if color_used[color[h]]
701- # The current hub of this trivial star is already a hub in a non-trivial star
702- hub[s] = h
685+ # Process the trivial stars (if any)
686+ nb_unknown_hubs = nb_trivial_stars
687+ if nb_trivial_stars > 0
688+ rvS = rowvals (S)
689+ for j in axes (S, 2 )
690+ for k in nzrange (S, j)
691+ i = rvS[k]
692+ if i > j
693+ index_ij = edge_to_index[k]
694+ s = star[index_ij]
695+ h = hub[s]
696+ if h < 0
697+ h = abs (h)
698+ spoke = h == j ? i : j
699+ if color_used[color[h]]
700+ # The current hub of this trivial star is already a hub in a non-trivial star
701+ hub[s] = h
702+ nb_unknown_hubs -= 1
703+ else
704+ if color_used[color[spoke]]
705+ # The current spoke of this trivial star is also a hub in a non-trivial star
706+ # Switch the hub and the spoke to avoid adding one more used color
707+ hub[s] = spoke
703708 nb_unknown_hubs -= 1
704- else
705- if color_used[color[spoke]]
706- # The current spoke of this trivial star is also a hub in a non-trivial star
707- # Switch the hub and the spoke to avoid adding one more used color
708- hub[s] = spoke
709- nb_unknown_hubs -= 1
710- end
711709 end
712710 end
713711 end
714712 end
715713 end
716714 end
717- # Only trivial stars where both vertices can be promoted as hub are remaining.
718- # In the context of bicoloring, if we want to minimize the number of row colors OR the number of column colors,
719- # we can have the optimal post-processing by taking as hub the vertices in the other partition.
720- # It is optimal because we will never increase the number of colors in the partition specified by `neutralized_first`
721- # in this phase and everything else in the post-processing is deterministic.
722- if nb_unknown_hubs > 0
723- rvS = rowvals (S)
724- for j in axes (S, 2 )
725- for k in nzrange (S, j)
726- i = rvS[k]
727- if i > j
728- index_ij = edge_to_index[k]
729- s = star[index_ij]
730- h = hub[s]
731- # The hub of this trivial star is still unknown
732- if h < 0
733- if neutralized_first == :rows
734- # j represents a column in the context of bicoloring
735- hub[s] = j
736- color_used[color[j]] = true
737- else # neutralized_first == :cols
738- # i represents a row in the context of bicoloring
739- hub[s] = i
740- color_used[color[i]] = true
741- end
715+ end
716+ # Only trivial stars, where both vertices can be promoted as hubs, remain.
717+ # In the context of bicoloring, if we aim to minimize either the number of row colors or the number of column colors,
718+ # we can achieve optimal post-processing by choosing as hubs the vertices from the opposite partition.
719+ # This is optimal because we never increase the number of colors in the target partition during this phase,
720+ # and all preceding steps of the post-processing are deterministic.
721+ if nb_unknown_hubs > 0
722+ rvS = rowvals (S)
723+ for j in axes (S, 2 )
724+ for k in nzrange (S, j)
725+ i = rvS[k]
726+ if i > j
727+ index_ij = edge_to_index[k]
728+ s = star[index_ij]
729+ h = hub[s]
730+ # The hub of this trivial star is still unknown
731+ if h < 0
732+ if postprocessing_minimizes == :row_colors
733+ # j bzlongs to a column partition in the context of bicoloring
734+ hub[s] = j
735+ color_used[color[j]] = true
736+ elseif postprocessing_minimizes == :column_colors
737+ # i belongs to a row partition in the context of bicoloring
738+ hub[s] = i
739+ color_used[color[i]] = true
740+ else postprocessing_minimizes == :all_colors
741+ l = abs (h)
742+ hub[s] = l
743+ color_used[color[l]] = true
742744 end
743745 end
744746 end
745747 end
746748 end
747- else
748- # only the colors of non-leaf vertices are used
749- (; reverse_bfs_orders, is_star, tree_edge_indices, nt) = star_or_tree_set
750- nb_trivial_trees = 0
749+ end
750+
751+ # if at least one of the colors is useless, modify the color assignments of vertices
752+ if any (! , color_used)
753+ num_colors_useless = 0
751754
752- # Iterate through all non-trivial trees
755+ # determine what are the useless colors and compute the offsets
756+ for ci in 1 : nb_colors
757+ if color_used[ci]
758+ offsets[ci] = num_colors_useless
759+ else
760+ num_colors_useless += 1
761+ end
762+ end
763+
764+ # assign the neutral color to every vertex with a useless color and remap the colors
765+ for i in eachindex (color)
766+ ci = color[i]
767+ if ! color_used[ci]
768+ # assign the neutral color
769+ color[i] = 0
770+ else
771+ # remap the color to not have any gap
772+ color[i] -= offsets[ci]
773+ end
774+ end
775+ end
776+ return color
777+ end
778+
779+ function postprocess! (
780+ color:: AbstractVector{<:Integer} ,
781+ tree_set:: TreeSet ,
782+ g:: AdjacencyGraph ,
783+ offsets:: AbstractVector{<:Integer} ;
784+ postprocessing_minimizes:: Symbol = :all_colors ,
785+ )
786+ S = pattern (g)
787+ edge_to_index = edge_indices (g)
788+ # flag which colors are actually used during decompression
789+ nb_colors = maximum (color)
790+ color_used = zeros (Bool, nb_colors)
791+
792+ # only the colors of non-leaf vertices are used
793+ (; reverse_bfs_orders, is_star, tree_edge_indices, nt) = tree_set
794+ nb_trivial_trees = 0
795+
796+ # Iterate through all non-trivial trees
797+ for k in 1 : nt
798+ # Position of the first edge in the tree
799+ first = tree_edge_indices[k]
800+
801+ # Total number of edges in the tree
802+ ne_tree = tree_edge_indices[k + 1 ] - first
803+
804+ # Check if we have more than one edge in the tree (non-trivial tree)
805+ if ne_tree > 1
806+ # Determine if the tree is a star
807+ if is_star[k]
808+ # It is a non-trivial star and only the color of the hub is needed
809+ (_, hub) = reverse_bfs_orders[first]
810+ color_used[color[hub]] = true
811+ else
812+ # It is not a star and both colors are needed during the decompression
813+ (i, j) = reverse_bfs_orders[first]
814+ color_used[color[i]] = true
815+ color_used[color[j]] = true
816+ end
817+ else
818+ nb_trivial_trees += 1
819+ end
820+ end
821+
822+ # Process the trivial trees (if any)
823+ if nb_trivial_trees > 0
753824 for k in 1 : nt
754825 # Position of the first edge in the tree
755826 first = tree_edge_indices[k]
756827
757828 # Total number of edges in the tree
758829 ne_tree = tree_edge_indices[k + 1 ] - first
759830
760- # Check if we have more than one edge in the tree (non-trivial tree)
761- if ne_tree > 1
762- # Determine if the tree is a star
763- if is_star[k ]
764- # It is a non-trivial star and only the color of the hub is needed
765- (_, hub) = reverse_bfs_orders[first]
766- color_used[color[hub]] = true
831+ # Check if we have exactly one edge in the tree
832+ if ne_tree == 1
833+ (i, j) = reverse_bfs_orders[first]
834+ if color_used[color[i] ]
835+ # Make i the root to avoid possibly adding one more used color
836+ # Switch it with the (only) leaf
837+ reverse_bfs_orders[first] = (j, i)
767838 else
768- # It is not a star and both colors are needed during the decompression
769- (i, j) = reverse_bfs_orders[first]
770- color_used[color[i]] = true
839+ # Keep j as the root
771840 color_used[color[j]] = true
772841 end
773- else
774- nb_trivial_trees += 1
775- end
776- end
777-
778- # Process the trivial trees (if any)
779- if nb_trivial_trees > 0
780- for k in 1 : nt
781- # Position of the first edge in the tree
782- first = tree_edge_indices[k]
783-
784- # Total number of edges in the tree
785- ne_tree = tree_edge_indices[k + 1 ] - first
786-
787- # Check if we have exactly one edge in the tree
788- if ne_tree == 1
789- (i, j) = reverse_bfs_orders[first]
790- if color_used[color[i]]
791- # Make i the root to avoid possibly adding one more used color
792- # Switch it with the (only) leaf
793- reverse_bfs_orders[first] = (j, i)
794- else
795- # Keep j as the root
796- color_used[color[j]] = true
797- end
798- end
799842 end
800843 end
801844 end
0 commit comments