From 742bd043817c8f538c889589fb9a2b8b36735b28 Mon Sep 17 00:00:00 2001
From: Guido Trotter <ultrotter@google.com>
Date: Fri, 30 Nov 2012 18:15:29 +0100
Subject: [PATCH] Add Dsatur implementation

Implement the Dsatur algorithm for Graph coloring. This also abstracts
the neighColors function into two subfunctions that this algorithm can
reuse.

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     | 57 +++++++++++++++++++++++++++++--
 2 files changed, 64 insertions(+), 3 deletions(-)

diff --git a/htest/Test/Ganeti/HTools/Graph.hs b/htest/Test/Ganeti/HTools/Graph.hs
index 03b2fda5d..b9c11118c 100644
--- a/htest/Test/Ganeti/HTools/Graph.hs
+++ b/htest/Test/Ganeti/HTools/Graph.hs
@@ -100,6 +100,10 @@ prop_colorClique alg (TestableClique g) = numvertices ==? numcolors
 prop_colorLFClique :: TestableClique -> Property
 prop_colorLFClique = prop_colorClique colorLF
 
+-- | Specific check for the Dsatur algorithm.
+prop_colorDsaturClique :: TestableClique -> Property
+prop_colorDsaturClique = prop_colorClique colorDsatur
+
 -- Check that all nodes are colored.
 prop_colorAllNodes :: (Graph.Graph -> ColorVertMap)
                    -> TestableGraph
@@ -112,10 +116,16 @@ prop_colorAllNodes alg (TestableGraph g) = numvertices ==? numcolored
 prop_colorLFAllNodes :: TestableGraph -> Property
 prop_colorLFAllNodes = prop_colorAllNodes colorLF
 
+-- | Specific check for the Dsatur algorithm.
+prop_colorDsaturAllNodes :: TestableGraph -> Property
+prop_colorDsaturAllNodes = prop_colorAllNodes colorDsatur
+
 -- | List of tests for the Graph module.
 testSuite "HTools/Graph"
             [ 'case_emptyVertColorMapNull
             , 'case_emptyVertColorMapEmpty
             , 'prop_colorLFClique
+            , 'prop_colorDsaturClique
             , 'prop_colorLFAllNodes
+            , 'prop_colorDsaturAllNodes
             ]
diff --git a/htools/Ganeti/HTools/Graph.hs b/htools/Ganeti/HTools/Graph.hs
index 8d3e37230..3555921d5 100644
--- a/htools/Ganeti/HTools/Graph.hs
+++ b/htools/Ganeti/HTools/Graph.hs
@@ -13,6 +13,17 @@ of a graph and its application to timetabling problems", The Computer Journal
 10 (1): 85-86, doi:10.1093/comjnl/10.1.85
 <http://comjnl.oxfordjournals.org/content/10/1/85>
 
+DSatur is described in:
+Brelaz, D. (1979), "New methods to color the vertices of a graph",
+Communications of the ACM 22 (4): 251-256, doi:10.1145/359094.359101
+<http://dx.doi.org/10.1145%2F359094.359101>
+
+Also interesting:
+Klotz, W. (2002). Graph coloring algorithms. Mathematics Report, Technical
+University Clausthal, 1-9.
+<http://www.math.tu-clausthal.de/Arbeitsgruppen/Diskrete-Optimierung
+/publications/2002/gca.pdf>
+
 -}
 
 {-
@@ -46,6 +57,7 @@ module Ganeti.HTools.Graph
     -- * Coloring
   , colorInOrder
   , colorLF
+  , colorDsatur
     -- * Color map transformations
   , colorVertMap
     -- * Vertex sorting
@@ -66,13 +78,16 @@ import qualified Data.Array as Array
 -- | Node colors.
 type Color = Int
 
+-- | Saturation: number of colored neighbors.
+type Satur = Int
+
 -- | Vertex to Color association.
 type VertColorMap = IntMap.IntMap Color
 
 -- | Color to Vertex association.
 type ColorVertMap = IntMap.IntMap [Int]
 
--- * Sorting of vertices
+-- * Vertices characteristics
 
 -- | (vertex, degree) tuples on a graph.
 verticesDegree :: Graph.Graph -> [(Graph.Vertex, Int)]
@@ -87,16 +102,24 @@ verticesByDegreeDesc g =
 verticesByDegreeAsc :: Graph.Graph -> [Graph.Vertex]
 verticesByDegreeAsc g = map fst . sortBy (comparing snd) $ verticesDegree g
 
+-- | Get the neighbors of a vertex.
+neighbors :: Graph.Graph -> Graph.Vertex -> [Graph.Vertex]
+neighbors g v = g Array.! v
+
 -- * Coloring
 
 -- | Empty color map.
 emptyVertColorMap :: VertColorMap
 emptyVertColorMap = IntMap.empty
 
+-- | Get the colors of a list of vertices.
+-- Any uncolored vertices are ignored.
+listColors :: VertColorMap -> [Graph.Vertex] -> [Color]
+listColors cMap = mapMaybe (`IntMap.lookup` cMap)
+
 -- | Get the colors of the neighbors of a vertex.
 neighColors :: Graph.Graph -> VertColorMap -> Graph.Vertex -> [Color]
-neighColors g cMap v = mapMaybe (`IntMap.lookup` cMap) neighbors
-    where neighbors = g Array.! v
+neighColors g cMap v = listColors cMap $ neighbors g v
 
 -- | Color one node.
 colorNode :: Graph.Graph -> VertColorMap -> Graph.Vertex -> Color
@@ -117,6 +140,34 @@ colorInOrder g = foldr (colorNodeInMap g) emptyVertColorMap
 colorLF :: Graph.Graph -> ColorVertMap
 colorLF g = colorVertMap . colorInOrder g $ verticesByDegreeAsc g
 
+-- | (vertex, (saturation, degree)) for a vertex.
+vertexSaturation :: Graph.Graph
+                 -> VertColorMap
+                 -> Graph.Vertex
+                 -> (Graph.Vertex, (Satur, Int))
+vertexSaturation g cMap v = (v, (length (listColors cMap neigh), length neigh))
+    where neigh = neighbors g v
+
+-- | Auxiliary recursive function to calculate dsatur.
+-- 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
+    where newmap = colorNodeInMap g choosen cMap
+          choosen = fst . maximumBy (comparing snd) $ satlist
+          satlist = map (vertexSaturation g cMap) l
+          newlist = delete choosen l
+
+-- | 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 from VertColorMap.
 colorVertMap :: VertColorMap -> ColorVertMap
 colorVertMap = IntMap.foldWithKey
-- 
GitLab