{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module IGraph
( LGraph(..)
, U(..)
, D(..)
, Graph(..)
, mkGraph
, fromLabeledEdges
, unsafeFreeze
, freeze
, unsafeThaw
, thaw
, neighbors
, pre
, suc
, filterNode
, filterEdge
, nmap
, emap
) where
import Control.Arrow ((***))
import Control.Monad (forM_, liftM)
import Control.Monad.Primitive
import Control.Monad.ST (runST)
import Data.Hashable (Hashable)
import qualified Data.HashMap.Strict as M
import Data.List (nub)
import Data.Maybe
import Data.Binary
import System.IO.Unsafe (unsafePerformIO)
import IGraph.Internal.Attribute
import IGraph.Internal.Constants
import IGraph.Internal.Graph
import IGraph.Internal.Selector
import IGraph.Mutable
type Node = Int
type Edge = (Node, Node)
-- | graph with labeled nodes and edges
data LGraph d v e = LGraph
{ _graph :: IGraphPtr
, _labelToNode :: M.HashMap v [Node]
}
instance ( Binary v, Hashable v, Read v, Show v, Eq v
, Binary e, Read e, Show e, Graph d) => Binary (LGraph d v e) where
put gr = do
put nlabs
put es
put elabs
where
nlabs = map (nodeLab gr) $ nodes gr
es = edges gr
elabs = map (edgeLab gr) es
get = do
nlabs <- get
es <- get
elabs <- get
return $ mkGraph nlabs $ zip es elabs
class MGraph d => Graph d where
isDirected :: LGraph d v e -> Bool
isD :: d -> Bool
nNodes :: LGraph d v e -> Int
nNodes (LGraph g _) = igraphVcount g
{-# INLINE nNodes #-}
nodes :: LGraph d v e -> [Int]
nodes gr = [0 .. nNodes gr - 1]
{-# INLINE nodes #-}
nEdges :: LGraph d v e -> Int
nEdges (LGraph g _) = igraphEcount g
{-# INLINE nEdges #-}
edges :: LGraph d v e -> [Edge]
edges gr@(LGraph g _) = unsafePerformIO $ mapM (igraphEdge g) [0..n-1]
where
n = nEdges gr
{-# INLINE edges #-}
hasEdge :: LGraph d v e -> Edge -> Bool
hasEdge (LGraph g _) (fr, to)
| igraphGetEid g fr to True False < 0 = False
| otherwise = True
{-# INLINE hasEdge #-}
nodeLab :: Read v => LGraph d v e -> Node -> v
nodeLab (LGraph g _) i = read $ igraphCattributeVAS g vertexAttr i
{-# INLINE nodeLab #-}
nodeLabMaybe :: Read v => LGraph d v e -> Node -> Maybe v
nodeLabMaybe gr@(LGraph g _) i =
if igraphCattributeHasAttr g IgraphAttributeVertex vertexAttr
then Just $ nodeLab gr i
else Nothing
{-# INLINE nodeLabMaybe #-}
getNodes :: (Hashable v, Eq v) => LGraph d v e -> v -> [Node]
getNodes gr x = M.lookupDefault [] x $ _labelToNode gr
{-# INLINE getNodes #-}
edgeLab :: Read e => LGraph d v e -> Edge -> e
edgeLab (LGraph g _) (fr,to) = read $ igraphCattributeEAS g edgeAttr $
igraphGetEid g fr to True True
{-# INLINE edgeLab #-}
edgeLabMaybe :: Read e => LGraph d v e -> Edge -> Maybe e
edgeLabMaybe gr@(LGraph g _) i =
if igraphCattributeHasAttr g IgraphAttributeEdge edgeAttr
then Just $ edgeLab gr i
else Nothing
{-# INLINE edgeLabMaybe #-}
edgeLabByEid :: Read e => LGraph d v e -> Int -> e
edgeLabByEid (LGraph g _) i = read $ igraphCattributeEAS g edgeAttr i
{-# INLINE edgeLabByEid #-}
instance Graph U where
isDirected = const False
isD = const False
instance Graph D where
isDirected = const True
isD = const True
mkGraph :: (Graph d, Hashable v, Read v, Eq v, Show v, Show e)
=> [v] -> [(Edge, e)] -> LGraph d v e
mkGraph vattr es = runST $ do
g <- new 0
addLNodes n vattr g
addLEdges (map (\((fr,to),x) -> (fr,to,x)) es) g
unsafeFreeze g
where
n = length vattr
fromLabeledEdges :: (Graph d, Hashable v, Read v, Eq v, Show v, Show e)
=> [((v, v), e)] -> LGraph d v e
fromLabeledEdges es = mkGraph labels es'
where
es' = flip map es $ \((fr, to), x) -> ((f fr, f to), x)
where f x = M.lookupDefault undefined x labelToId
labels = nub $ concat [ [a,b] | ((a,b),_) <- es ]
labelToId = M.fromList $ zip labels [0..]
unsafeFreeze :: (Hashable v, Eq v, Read v, PrimMonad m) => MLGraph (PrimState m) d v e -> m (LGraph d v e)
unsafeFreeze (MLGraph g) = return $ LGraph g labToId
where
labToId = M.fromListWith (++) $ zip labels $ map return [0..nV-1]
nV = igraphVcount g
labels = map (read . igraphCattributeVAS g vertexAttr) [0 .. nV-1]
freeze :: (Hashable v, Eq v, Read v, PrimMonad m) => MLGraph (PrimState m) d v e -> m (LGraph d v e)
freeze (MLGraph g) = do
g' <- unsafePrimToPrim $ igraphCopy g
unsafeFreeze (MLGraph g')
unsafeThaw :: PrimMonad m => LGraph d v e -> m (MLGraph (PrimState m) d v e)
unsafeThaw (LGraph g _) = return $ MLGraph g
thaw :: (PrimMonad m, Graph d) => LGraph d v e -> m (MLGraph (PrimState m) d v e)
thaw (LGraph g _) = unsafePrimToPrim . liftM MLGraph . igraphCopy $ g
-- | Find all neighbors of the given node
neighbors :: LGraph d v e -> Node -> [Node]
neighbors gr i = unsafePerformIO $ do
vs <- igraphVsAdj i IgraphAll
vit <- igraphVitNew (_graph gr) vs
vitToList vit
-- | Find all nodes that have a link from the given node.
suc :: LGraph D v e -> Node -> [Node]
suc gr i = unsafePerformIO $ do
vs <- igraphVsAdj i IgraphOut
vit <- igraphVitNew (_graph gr) vs
vitToList vit
-- | Find all nodes that link to to the given node.
pre :: LGraph D v e -> Node -> [Node]
pre gr i = unsafePerformIO $ do
vs <- igraphVsAdj i IgraphIn
vit <- igraphVitNew (_graph gr) vs
vitToList vit
-- | Keep nodes that satisfy the constraint
filterNode :: (Hashable v, Eq v, Read v, Graph d)
=> (LGraph d v e -> Node -> Bool) -> LGraph d v e -> LGraph d v e
filterNode f gr = runST $ do
let deleted = filter (not . f gr) $ nodes gr
gr' <- thaw gr
delNodes deleted gr'
unsafeFreeze gr'
-- | Keep nodes that satisfy the constraint
filterEdge :: (Hashable v, Eq v, Read v, Graph d)
=> (LGraph d v e -> Edge -> Bool) -> LGraph d v e -> LGraph d v e
filterEdge f gr = runST $ do
let deleted = filter (not . f gr) $ edges gr
gr' <- thaw gr
delEdges deleted gr'
unsafeFreeze gr'
-- | Map a function over the node labels in a graph
nmap :: (Graph d, Read v, Hashable u, Read u, Eq u, Show u)
=> ((Node, v) -> u) -> LGraph d v e -> LGraph d u e
nmap fn gr = unsafePerformIO $ do
(MLGraph g) <- thaw gr
forM_ (nodes gr) $ \i -> do
let label = fn (i, nodeLab gr i)
igraphCattributeVASSet g vertexAttr i (show label)
unsafeFreeze (MLGraph g)
-- | Map a function over the edge labels in a graph
emap :: (Graph d, Read v, Hashable v, Eq v, Read e1, Show e2)
=> ((Edge, e1) -> e2) -> LGraph d v e1 -> LGraph d v e2
emap fn gr = unsafePerformIO $ do
(MLGraph g) <- thaw gr
forM_ (edges gr) $ \(fr, to) -> do
let label = fn ((fr,to), edgeLabByEid gr i)
i = igraphGetEid g fr to True True
igraphCattributeEASSet g edgeAttr i (show label)
unsafeFreeze (MLGraph g)