From 8b50de5c65eda8a596749e0c53884c4fa104a450 Mon Sep 17 00:00:00 2001
From: Guido Trotter <ultrotter@google.com>
Date: Mon, 3 Dec 2012 09:25:32 +0100
Subject: [PATCH] Fix Dsatur and add Dcolor

Our Dsatur implementation was incorrect: while the paper defined the
degree of saturation of a vertex as the number of different colors it is
adjacent to, we were using the number of colors, without considering
uniqueness. This effectively implemented a different algorithm, which is
very similar to the previous one, and while it performs slightly worse
on average it still beats Dsatur on some cases.

So we refactor the implementation to effectively support both algorithms
without code duplication, and then we export both the old algorithms as
"Dcolor" and the new one as "Dsatur". Since these are all fast
algorithms in hroller we will still be able to pick the best result.

Note that the new Dsatur implementation uses an IntSet to calculate the
uniqueness. Results with nub + length on a list were significantly
slower.

Signed-off-by: Guido Trotter <ultrotter@google.com>
Reviewed-by: Iustin Pop <iustin@google.com>
---
 htest/Test/Ganeti/HTools/Graph.hs | 10 +++++
 htools/Ganeti/HTools/Graph.hs     | 61 ++++++++++++++++++++++++-------
 2 files changed, 58 insertions(+), 13 deletions(-)

diff --git a/htest/Test/Ganeti/HTools/Graph.hs b/htest/Test/Ganeti/HTools/Graph.hs
index 4ded4dbfb..5a2248aa2 100644
--- a/htest/Test/Ganeti/HTools/Graph.hs
+++ b/htest/Test/Ganeti/HTools/Graph.hs
@@ -133,6 +133,10 @@ prop_colorLFClique = prop_colorClique colorLF
 prop_colorDsaturClique :: TestableClique -> Property
 prop_colorDsaturClique = prop_colorClique colorDsatur
 
+-- | Specific check for the Dcolor algorithm.
+prop_colorDcolorClique :: TestableClique -> Property
+prop_colorDcolorClique = prop_colorClique colorDcolor
+
 -- Check that all nodes are colored.
 prop_colorAllNodes :: (Graph.Graph -> ColorVertMap)
                    -> TestableGraph
@@ -149,6 +153,10 @@ prop_colorLFAllNodes = prop_colorAllNodes colorLF
 prop_colorDsaturAllNodes :: TestableGraph -> Property
 prop_colorDsaturAllNodes = prop_colorAllNodes colorDsatur
 
+-- | Specific check for the Dcolor algorithm.
+prop_colorDcolorAllNodes :: TestableGraph -> Property
+prop_colorDcolorAllNodes = prop_colorAllNodes colorDcolor
+
 -- | List of tests for the Graph module.
 testSuite "HTools/Graph"
             [ 'case_emptyVertColorMapNull
@@ -157,8 +165,10 @@ testSuite "HTools/Graph"
             , 'prop_verticesByDegreeDescDesc
             , 'prop_colorLFClique
             , 'prop_colorDsaturClique
+            , 'prop_colorDcolorClique
             , 'prop_colorLFAllNodes
             , 'prop_colorDsaturAllNodes
+            , 'prop_colorDcolorAllNodes
             , 'prop_isColorableTestableGraph
             , 'prop_isColorableTestableClique
             ]
diff --git a/htools/Ganeti/HTools/Graph.hs b/htools/Ganeti/HTools/Graph.hs
index bdb8a334c..8f2801f49 100644
--- a/htools/Ganeti/HTools/Graph.hs
+++ b/htools/Ganeti/HTools/Graph.hs
@@ -58,6 +58,7 @@ module Ganeti.HTools.Graph
   , colorInOrder
   , colorLF
   , colorDsatur
+  , colorDcolor
   , isColorable
     -- * Color map transformations
   , colorVertMap
@@ -74,6 +75,7 @@ import Data.Ord
 import Data.List
 
 import qualified Data.IntMap as IntMap
+import qualified Data.IntSet as IntSet
 import qualified Data.Graph as Graph
 import qualified Data.Array as Array
 
@@ -133,12 +135,17 @@ isColorable g = isUndirected g && not (hasLoop g)
 
 -- | Get the colors of a list of vertices.
 -- Any uncolored vertices are ignored.
-listColors :: VertColorMap -> [Graph.Vertex] -> [Color]
-listColors cMap = mapMaybe (`IntMap.lookup` cMap)
+verticesColors :: VertColorMap -> [Graph.Vertex] -> [Color]
+verticesColors cMap = mapMaybe (`IntMap.lookup` cMap)
+
+-- | Get the colors of a list of vertices.
+-- Any uncolored vertices are ignored.
+verticesColorSet :: VertColorMap -> [Graph.Vertex] -> IntSet.IntSet
+verticesColorSet cMap = IntSet.fromList . mapMaybe (`IntMap.lookup` cMap)
 
 -- | Get the colors of the neighbors of a vertex.
 neighColors :: Graph.Graph -> VertColorMap -> Graph.Vertex -> [Color]
-neighColors g cMap v = listColors cMap $ neighbors g v
+neighColors g cMap v = verticesColors cMap $ neighbors g v
 
 -- | Color one node.
 colorNode :: Graph.Graph -> VertColorMap -> Graph.Vertex -> Color
@@ -164,28 +171,56 @@ vertexSaturation :: Graph.Graph
                  -> VertColorMap
                  -> Graph.Vertex
                  -> (Graph.Vertex, (Satur, Int))
-vertexSaturation g cMap v = (v, (length (listColors cMap neigh), length neigh))
+vertexSaturation g cMap v =
+  (v, (IntSet.size (verticesColorSet cMap neigh), length neigh))
     where neigh = neighbors g v
 
--- | Auxiliary recursive function to calculate dsatur.
+-- | (vertex, (colordegree, degree)) for a vertex.
+vertexColorDegree :: Graph.Graph
+                  -> VertColorMap
+                  -> Graph.Vertex
+                  -> (Graph.Vertex, (Int, Int))
+vertexColorDegree g cMap v =
+  (v, (length (verticesColors cMap neigh), length neigh))
+    where neigh = neighbors g v
+
+-- | Color all nodes in a dynamic order.
 -- We have a list of vertices still uncolored, and at each round we
--- choose&delete the maximum saturation vertex among the remaining ones.
--- To do so we need explicit recursion.
-colorDsatur' :: Graph.Graph -> VertColorMap -> [Graph.Vertex] -> VertColorMap
-colorDsatur' _ cMap [] = cMap
-colorDsatur' g cMap l = colorDsatur' g newmap newlist
+-- choose&delete one vertex among the remaining ones. A helper function
+-- is used to induce an order so that the next vertex can be chosen.
+colorDynamicOrder :: Ord a
+                  =>  (Graph.Graph
+                      -> VertColorMap
+                      -> Graph.Vertex
+                      -> (Graph.Vertex, a)) -- ^ Helper to induce the choice
+                  -> Graph.Graph -- ^ Target graph
+                  -> VertColorMap -- ^ Accumulating vertex color map
+                  -> [Graph.Vertex] -- ^ List of remaining vertices
+                  -> VertColorMap -- ^ Output vertex color map
+colorDynamicOrder _ _ cMap [] = cMap
+colorDynamicOrder ordind g cMap l = colorDynamicOrder ordind g newmap newlist
     where newmap = colorNodeInMap g choosen cMap
-          choosen = fst . maximumBy (comparing snd) $ satlist
-          satlist = map (vertexSaturation g cMap) l
+          choosen = fst . maximumBy (comparing snd) $ ordlist
+          ordlist = map (ordind g cMap) l
           newlist = delete choosen l
 
+-- | Color greedily all nodes, highest number of colored neighbors, then
+-- highest degree. This is slower than "colorLF" as we must dynamically
+-- recalculate which node to color next among all remaining ones but
+-- produces better results.
+colorDcolor :: Graph.Graph -> ColorVertMap
+colorDcolor g =
+  colorVertMap . colorDynamicOrder vertexColorDegree g emptyVertColorMap $ vert
+    where vert = Graph.vertices g
+
 -- | Color greedily all nodes, highest saturation, then highest degree.
 -- This is slower than "colorLF" as we must dynamically recalculate
 -- which node to color next among all remaining ones but produces better
 -- results.
 colorDsatur :: Graph.Graph -> ColorVertMap
 colorDsatur g =
-  colorVertMap . colorDsatur' g emptyVertColorMap $ Graph.vertices g
+  colorVertMap . colorDynamicOrder vertexSaturation g emptyVertColorMap $ vert
+    where vert = Graph.vertices g
 
 -- | ColorVertMap from VertColorMap.
 colorVertMap :: VertColorMap -> ColorVertMap
-- 
GitLab