packages feed

intricacy-0.4.1: GraphColouring.hs

-- This file is part of Intricacy
-- Copyright (C) 2013 Martin Bays <mbays@sdf.org>
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of version 3 of the GNU General Public License as
-- published by the Free Software Foundation, or any later version.
--
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see http://www.gnu.org/licenses/.

module GraphColouring (fiveColour) where

import qualified Data.Map as Map
import Data.Map (Map)
import qualified Data.Set as Set
import Data.Set (Set)
import qualified Data.Vector as Vector
import Data.List
import Data.Maybe

type Colouring a = Map a Int
type Graph a = (Set a, Set (Set a))
type PlanarGraph a = Map a [a]

fiveColour :: Ord a => PlanarGraph a -> Colouring a -> Colouring a
-- ^algorithm based on that presented in
-- http://people.math.gatech.edu/~thomas/PAP/fcstoc.pdf
-- Key point: a planar graph can't have all vertices of degree >= 6
-- (Proof: suppose it does, so |E| >= 3|V|; WLOG the graph is triangulated,
-- so then |F| <= 2/3 |E|. So \xi = |V|-|E|+|F| <= (1/3 - 1 + 2/3)|E| = 0.
-- But a planar graph has Euler characteristic 1.)
-- Aims to minimise changes from given (partial) colouring lastCol.
fiveColour g lastCol =
    if Map.keysSet lastCol == Map.keysSet g && isColouring g lastCol
	then lastCol
	else fiveColour' lastCol g

isColouring :: Ord a => PlanarGraph a -> Colouring a -> Bool
isColouring g mapping = and
    [ Map.lookup s mapping /= Map.lookup e mapping
    | s <- Map.keys g
    , e <- g Map.! s ]

fiveColour' :: Ord a => Colouring a -> PlanarGraph a -> Colouring a
fiveColour' pref g | g == Map.empty = Map.empty
fiveColour' pref g =
    let adjsOf v = (nub $ g Map.! v) \\ [v]
	v0 = head $ filter ((<=5) . length . adjsOf) $ Map.keys g
	adjs = adjsOf v0
	addTo c =
	    let vc = head $ possCols pref v0 \\ map (c Map.!) adjs
	    in Map.insert v0 vc c
    in if length adjs < 5
       then addTo $ fiveColour' pref $ deleteNode v0 g
       else let (v',v'') = if adjs!!2 `elem` (g Map.! (adjs!!0))
		    then (adjs!!1,adjs!!3)
		    else (adjs!!0,adjs!!2)
	    in addTo $ demerge v' v'' $ fiveColour' pref $ merge v0 v' v'' g

possCols :: Ord a => Colouring a -> a -> [Int]
possCols pref v = maybe [0..4] (\lvc -> lvc:([0..4] \\ [lvc])) $ Map.lookup v pref

demerge :: Ord a => a -> a -> Colouring a -> Colouring a
demerge v v' c = Map.insert v' (c Map.! v) c

merge :: Ord a => a -> a -> a -> PlanarGraph a -> PlanarGraph a
merge v v' v'' g =
    deleteNode v $ contractNodes v' v''
	$ Map.adjust (concatAdjsOver v $ g Map.! v'') v' g

concatAdjsOver :: Ord a => a -> [a] -> [a] -> [a]
concatAdjsOver v adjs adjs' =
    let (s,_:e) = splitAt (fromJust $ elemIndex v adjs) adjs
    in s ++ adjs' ++ e

deleteNode :: Ord a => a -> PlanarGraph a -> PlanarGraph a
deleteNode v =
    fmap (filter (/= v)) . Map.delete v

contractNodes :: Ord a => a -> a -> PlanarGraph a -> PlanarGraph a
contractNodes v v' =
    fmap (map (\v'' -> if v'' == v' then v else v'')) . Map.delete v'