packages feed

alms-0.6.8: src/Alt/Graph.hs

-- | A wrapper around the fgs graph library.
module Alt.Graph (
  Gr, ShowGraph(..),
  trcnr, untransitive, nmLab, labelNode, labScc,
  pathScc, erdffWith, xpdffWith, xpdfWith,
  labComponents, labNodeEdges,
  module Data.Graph.Inductive.Basic,
  module Data.Graph.Inductive.Graph,
  module Data.Graph.Inductive.Query.DFS,
  module Data.Graph.Inductive.Query.TransClos,
  module NM,
) where


-- Mine:
import Util
import Alt.NodeMap as NM

import Prelude ()
import qualified Data.List as List
import qualified Data.Tree as Tree

-- From fgs:
import Data.Graph.Inductive.PatriciaTree (Gr)
import Data.Graph.Inductive.Basic
import Data.Graph.Inductive.Graph
import Data.Graph.Inductive.Query.DFS
import Data.Graph.Inductive.Query.TransClos

-- | Transitive, non-reflexive closure
trcnr ∷ DynGraph gr ⇒ gr a b → gr a ()
trcnr g = insEdges newEdges (insNodes lns empty) where
  lns      = labNodes g
  newEdges = [ (n, n', ())
             | (n, _) ← lns
             , n'     ← reachable n g
             , n /= n' ]

-- | Compute the transitive reduction of a transitive, acyclic graph.
untransitive ∷ DynGraph gr ⇒ gr a b → gr a b
untransitive g = foldl' eachEdge g (edges g) where
  eachEdge g' (n1, n2) = foldl' eachSuc g' (suc g' n1) where
    eachSuc g'' n' =
      if n' /= n1 && n' /= n2 && n2 `elem` suc g'' n'
        then delEdge (n1, n2) g''
        else g''

-- | Look up the node index of a node label
nmLab ∷ Ord a ⇒ NM.NodeMap a → a → Node
nmLab = fst <$$> NM.mkNode_

labelNode ∷ Graph gr ⇒ gr a b → Node → LNode a
labelNode g n = case lab g n of
  Just ln → (n, ln)
  Nothing → error "labelNode: node not found"

labScc ∷ Graph gr ⇒ gr a b → [[LNode a]]
labScc g = map preorder (Alt.Graph.rdffWith labNode' (topsort g) g)

pathScc ∷ Graph gr ⇒ gr a b → [Either (LNode a) [(LNode a, b)]]
pathScc g = map (addCycle . preorder) (erdffWith labNode' (topsort g) g)
  where
  addCycle [((n, a), Nothing)] =
    case lookup n (lpre g n) of
      Just b  → Right [((n, a), b)]
      Nothing → Left (n, a)
  addCycle (((n, a), Nothing):rest) =
    case catMaybes [ lookup n' (lsuc g n) | ((n', _), _) ← rest ] of
      b:_  → Right (((n, a), b) : map (second fromJust) rest)
      []   → error "pathScc: bug!"
  addCycle _ = error "pathScc: bug!"

erdffWith ∷ Graph gr ⇒
            CFun a b c → [Node] → gr a b → [Tree.Tree (c, Maybe b)]
erdffWith = xpdffWith (map (second Just) . lpre') <$.> map (\n → (n, Nothing))

rdffWith ∷ Graph gr ⇒ CFun a b c → [Node] → gr a b → [Tree.Tree c]
rdffWith = xdffWith pre'

_g ∷ Gr Int String
_g = mkGraph ns es where
  ns = (id &&& id) <$> [0 .. 20]
  es = map addLab $
         [ (0,5), (1,5), (2,5), (3,5), (4,5),
           (0,6), (1,6), (2,6), (3,6), (4,6),
           (5,7), (6,7), (5,8), (6,8),
           (7,9), (8,9), (7,10), (8,10),
           (9,0), (9,1), (9,2), (9,3), (9,4),
           (10,0), (10,1), (10,2), (10,3), (10,4)
         ]
  addLab (i, j) = (i, j, show i ++ "->" ++ show j)

-- | A generalized, path-sensitive depth-first forest.  Along with
--   each starting node, it takes a value to associate with that node,
--   and the next-finding function produces new values to go with
--   each node as well.
xpdffWith ∷ Graph gr ⇒
           CFun a b [(Node, d)] → CFun a b c →
           [(Node, d)] → gr a b → [Tree.Tree (c, d)]
xpdffWith = fst <$$$$> xpdfWith

xpdfWith ∷ Graph gr ⇒
           CFun a b [(Node, d)] → CFun a b c →
           [(Node, d)] → gr a b → ([Tree.Tree (c, d)], gr a b)
xpdfWith _ _ []     g             = ([],g)
xpdfWith _ _ _      g | isEmpty g = ([],g)
xpdfWith d f ((v,e):vs) g =
  case match v g of
    (Nothing, g1) → xpdfWith d f vs g1
    (Just c, g1)  → (Tree.Node (f c, e) ts:ts', g3)
      where (ts, g2)  = xpdfWith d f (d c) g1
            (ts', g3) = xpdfWith d f vs g2

-- | Partition a graph into components of /labeled/ nodes
labComponents ∷ Graph gr ⇒ gr a b → [[LNode a]]
labComponents = componentsWith labNode'
  where
  udffWith ∷ Graph gr ⇒ CFun a b c → [Node] → gr a b → [Tree.Tree c]
  udffWith = xdffWith neighbors'
  --
  udffWith' ∷ Graph gr ⇒ CFun a b c → gr a b → [Tree.Tree c]
  udffWith' f g = udffWith f (nodes g) g
  --
  componentsWith ∷ Graph gr ⇒ CFun a b c → gr a b → [[c]]
  componentsWith = preorder <$$$> udffWith'

-- | Get the edges of a graph as pairs of node labels
labNodeEdges ∷ Graph gr ⇒ gr n e → [(n, n)]
labNodeEdges g =
  [ (α, β)
  | (n1, n2) ← edges g
  , let Just α = lab g n1
  , let Just β = lab g n2
  ]

-- | For showing graphs
newtype ShowGraph gr v = ShowGraph { unShowGraph ∷ gr v () }

instance (Graph gr, Show v) ⇒ Show (ShowGraph gr v) where
  showsPrec _ (ShowGraph gr) =
    showChar '{' .
    foldr (.) id
      (List.intersperse (showString ", ")
         [ shows n1 . showString "<" . shows n2
         | (n1, n2) ← labNodeEdges gr ])
    . showChar '}'