packages feed

Graphalyze 0.8.0.0 → 0.9.0.0

raw patch · 10 files changed

+485/−221 lines, 10 filesdep ~graphvizdep ~pandocPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: graphviz, pandoc

API changes (from Hackage documentation)

- Data.Graph.Analysis.Algorithms.Clustering: collapseGraphBy' :: (DynGraph gr) => [gr a b -> [(NGroup, a)]] -> gr a b -> gr a b
- Data.Graph.Analysis.Reporting: type DocGraph = (FilePath, DocInline, DotGraph Node)
+ Data.Graph.Analysis: collapseAndUpdate :: (Ord n) => [AGr n e -> [(NGroup, n)]] -> GraphData n e -> GraphData n e
+ Data.Graph.Analysis: collapseAndUpdate' :: (Ord n) => [AGr n e -> [(NGroup, n)]] -> GraphData n e -> (GraphData n e, Map n n)
+ Data.Graph.Analysis: inaccessibleNodes :: GraphData n e -> Set Node
+ Data.Graph.Analysis: levelGraphFromRoot :: (Ord n) => GraphData n e -> GraphData (GenCluster n) e
+ Data.Graph.Analysis.Algorithms.Clustering: collapseAndReplace :: (DynGraph gr) => [gr a b -> [(NGroup, a)]] -> gr a b -> gr a b
+ Data.Graph.Analysis.Algorithms.Clustering: collapseAndReplace' :: (DynGraph gr) => [gr a b -> [(NGroup, a)]] -> gr a b -> (gr a b, [(NGroup, a)])
+ Data.Graph.Analysis.Algorithms.Directed: accessibleFrom :: (Graph g) => g a b -> [Node] -> [Node]
+ Data.Graph.Analysis.Algorithms.Directed: accessibleFrom' :: (Graph g) => g a b -> Set Node -> Set Node
+ Data.Graph.Analysis.Algorithms.Directed: accessibleOnlyFrom :: (Graph g) => g a b -> [Node] -> [Node]
+ Data.Graph.Analysis.Algorithms.Directed: accessibleOnlyFrom' :: (Graph g) => g a b -> Set Node -> Set Node
+ Data.Graph.Analysis.Algorithms.Directed: leafMinPaths' :: (Graph g) => g a b -> [NGroup]
+ Data.Graph.Analysis.Algorithms.Directed: levelGraphFrom :: (Ord a, DynGraph g) => NGroup -> g a b -> g (GenCluster a) b
+ Data.Graph.Analysis.Algorithms.Directed: minLevel :: Int
+ Data.Graph.Analysis.Reporting: DG :: FilePath -> DocInline -> DotGraph Node -> DocGraph
+ Data.Graph.Analysis.Reporting: VParams :: FilePath -> FilePath -> VisProperties -> Maybe VisProperties -> Bool -> VisParams
+ Data.Graph.Analysis.Reporting: VProps :: GraphSize -> GraphvizOutput -> VisProperties
+ Data.Graph.Analysis.Reporting: data DocGraph
+ Data.Graph.Analysis.Reporting: data VisParams
+ Data.Graph.Analysis.Reporting: data VisProperties
+ Data.Graph.Analysis.Reporting: defaultImage :: VisParams -> VisProperties
+ Data.Graph.Analysis.Reporting: description :: DocGraph -> DocInline
+ Data.Graph.Analysis.Reporting: dotGraph :: DocGraph -> DotGraph Node
+ Data.Graph.Analysis.Reporting: format :: VisProperties -> GraphvizOutput
+ Data.Graph.Analysis.Reporting: graphDir :: VisParams -> FilePath
+ Data.Graph.Analysis.Reporting: imageFile :: DocGraph -> FilePath
+ Data.Graph.Analysis.Reporting: instance Eq DocElement
+ Data.Graph.Analysis.Reporting: instance Eq DocGraph
+ Data.Graph.Analysis.Reporting: instance Eq DocInline
+ Data.Graph.Analysis.Reporting: instance Eq Document
+ Data.Graph.Analysis.Reporting: instance Eq GraphSize
+ Data.Graph.Analysis.Reporting: instance Eq Location
+ Data.Graph.Analysis.Reporting: instance Eq VisParams
+ Data.Graph.Analysis.Reporting: instance Eq VisProperties
+ Data.Graph.Analysis.Reporting: instance Ord DocElement
+ Data.Graph.Analysis.Reporting: instance Ord DocGraph
+ Data.Graph.Analysis.Reporting: instance Ord DocInline
+ Data.Graph.Analysis.Reporting: instance Ord Document
+ Data.Graph.Analysis.Reporting: instance Ord GraphSize
+ Data.Graph.Analysis.Reporting: instance Ord Location
+ Data.Graph.Analysis.Reporting: instance Ord VisParams
+ Data.Graph.Analysis.Reporting: instance Ord VisProperties
+ Data.Graph.Analysis.Reporting: instance Read DocElement
+ Data.Graph.Analysis.Reporting: instance Read DocGraph
+ Data.Graph.Analysis.Reporting: instance Read DocInline
+ Data.Graph.Analysis.Reporting: instance Read Document
+ Data.Graph.Analysis.Reporting: instance Read GraphSize
+ Data.Graph.Analysis.Reporting: instance Read Location
+ Data.Graph.Analysis.Reporting: instance Read VisParams
+ Data.Graph.Analysis.Reporting: instance Read VisProperties
+ Data.Graph.Analysis.Reporting: instance Show DocElement
+ Data.Graph.Analysis.Reporting: instance Show DocGraph
+ Data.Graph.Analysis.Reporting: instance Show DocInline
+ Data.Graph.Analysis.Reporting: instance Show Document
+ Data.Graph.Analysis.Reporting: instance Show GraphSize
+ Data.Graph.Analysis.Reporting: instance Show VisParams
+ Data.Graph.Analysis.Reporting: instance Show VisProperties
+ Data.Graph.Analysis.Reporting: largeImage :: VisParams -> Maybe VisProperties
+ Data.Graph.Analysis.Reporting: rootDir :: VisParams -> FilePath
+ Data.Graph.Analysis.Reporting: saveDot :: VisParams -> Bool
+ Data.Graph.Analysis.Reporting: size :: VisProperties -> GraphSize
+ Data.Graph.Analysis.Reporting.Pandoc: alsoSaveDot :: PandocDocument -> PandocDocument
+ Data.Graph.Analysis.Reporting.Pandoc: instance Eq PandocProcess
+ Data.Graph.Analysis.Reporting.Pandoc: instance Ord PandocProcess
+ Data.Graph.Analysis.Reporting.Pandoc: instance Read PandocProcess
+ Data.Graph.Analysis.Reporting.Pandoc: instance Show PandocProcess
+ Data.Graph.Analysis.Types: Dbl :: Double -> GraphID
+ Data.Graph.Analysis.Types: HTML :: URL -> GraphID
+ Data.Graph.Analysis.Types: Int :: Int -> GraphID
+ Data.Graph.Analysis.Types: Str :: String -> GraphID
+ Data.Graph.Analysis.Types: addRoots :: GraphData n e -> NGroup -> GraphData n e
+ Data.Graph.Analysis.Types: addRootsBy :: (LNode n -> Bool) -> GraphData n e -> GraphData n e
+ Data.Graph.Analysis.Types: data GraphID :: *
+ Data.Graph.Analysis.Utils: addLabels' :: (Ord a, Graph g) => g a b -> Set Node -> Set (LNode a)
+ Data.Graph.Analysis.Utils: getLabels :: (Graph g) => g a b -> [Node] -> [a]
+ Data.Graph.Analysis.Utils: getLabels' :: (Ord a, Graph g) => g a b -> Set Node -> Set a
- Data.Graph.Analysis: classifyRoots :: (Ord n) => GraphData n e -> ([LNode n], [LNode n], [LNode n])
+ Data.Graph.Analysis: classifyRoots :: GraphData n e -> (Set Node, Set Node, Set Node)
- Data.Graph.Analysis.Algorithms.Directed: levelGraph :: (Ord a) => (DynGraph g) => g a b -> g (GenCluster a) b
+ Data.Graph.Analysis.Algorithms.Directed: levelGraph :: (Ord a, DynGraph g) => g a b -> g (GenCluster a) b
- Data.Graph.Analysis.Reporting: addLegend :: FilePath -> FilePath -> Document -> IO Document
+ Data.Graph.Analysis.Reporting: addLegend :: FilePath -> FilePath -> VisProperties -> Document -> IO Document
- Data.Graph.Analysis.Reporting: createGraph :: FilePath -> FilePath -> GraphSize -> Maybe GraphSize -> DocGraph -> IO (Maybe DocElement)
+ Data.Graph.Analysis.Reporting: createGraph :: VisParams -> DocGraph -> IO DocElement
- Data.Graph.Analysis.Utils: compactSame :: (Ord b) => (DynGraph gr) => gr a b -> gr a (Int, b)
+ Data.Graph.Analysis.Utils: compactSame :: (Ord b, DynGraph gr) => gr a b -> gr a (Int, b)

Files

Data/Graph/Analysis.hs view
@@ -29,7 +29,11 @@       -- $analfuncts       lengthAnalysis,       classifyRoots,-      interiorChains+      inaccessibleNodes,+      interiorChains,+      collapseAndUpdate,+      collapseAndUpdate',+      levelGraphFromRoot     ) where  import Data.Graph.Analysis.Internal@@ -41,9 +45,13 @@  import Data.Graph.Inductive.Graph -import Data.Maybe(mapMaybe)+import Data.List(find)+import Data.Maybe(mapMaybe, maybe) import qualified Data.Map as M+import Data.Map(Map) import qualified Data.Set as S+import Data.Set(Set)+import Control.Arrow(first)  import Data.Version(showVersion) import qualified Paths_Graphalyze as Paths(version)@@ -122,26 +130,38 @@  {- |    Compare the actual roots in the graph with those that are expected-   (i.e. those in 'wantedRoots').  Returns (in order):+   (i.e. those in 'wantedRootNodes').  Returns (in order): -   * Those roots that are expected (i.e. elements of 'wantedRoots'+   * Those roots that are expected (i.e. elements of 'wantedRootNodes'      that are roots).     * Those roots that are expected but not present (i.e. elements of-     'wantedRoots' that /aren't/ roots.+     'wantedRootNodes' that /aren't/ roots.     * Unexpected roots (i.e. those roots that aren't present in-     'wantedRoots').+     'wantedRootNodes').  -}-classifyRoots    :: (Ord n) => GraphData n e -> ([LNode n], [LNode n], [LNode n])+classifyRoots    :: GraphData n e -> (Set Node, Set Node, Set Node) classifyRoots gd = (areWanted, notRoots, notWanted)     where-      wntd = S.fromList $ wantedRoots gd-      rts = S.fromList $ applyAlg rootsOf gd-      areWanted = S.toList $ S.intersection wntd rts-      notRoots  = S.toList $ S.difference wntd rts-      notWanted = S.toList $ S.difference rts wntd+      wntd = S.fromList $ wantedRootNodes gd+      rts = S.fromList $ applyAlg rootsOf' gd+      areWanted = S.intersection wntd rts+      notRoots  = S.difference wntd rts+      notWanted = S.difference rts wntd +-- | Find the nodes that are not reachable from the expected roots+--   (i.e. those in 'wantedRootNodes').+inaccessibleNodes    :: GraphData n e -> Set Node+inaccessibleNodes gd = allNs `S.difference` reachableNs+    where+      -- We can't use accessibleOnlyFrom' on notWanted from+      -- classifyRoots, as there might be nodes that are roots but not+      -- detectable (e.g. a loop).+      allNs = S.fromList $ applyAlg nodes gd+      rs = S.fromList $ wantedRootNodes gd+      reachableNs = applyAlg accessibleFrom' gd rs+ -- | Only return those chains (see 'chainsIn') where the non-initial --   nodes are /not/ expected roots. interiorChains    :: (Eq n, Eq e) => GraphData n e -> [LNGroup n]@@ -151,3 +171,37 @@       rts = wantedRoots gd       interiorRoot = any (`elem` rts) . tail +-- | As with 'collapseAndReplace', but also update the+--   'wantedRootNodes' to contain the possibly compressed nodes.+--   Since the datums they refer to may no longer exist (as they are+--   compressed), 'unusedRelationships' is set to @[]@.+collapseAndUpdate    :: (Ord n) => [AGr n e -> [(NGroup, n)]]+                        -> GraphData n e -> GraphData n e+collapseAndUpdate fs = fst . collapseAndUpdate' fs++-- | As with 'collapseAndUpdate', but also includes a lookup 'Map'+--   from the old label to the new.+collapseAndUpdate'       :: (Ord n) => [AGr n e -> [(NGroup, n)]]+                            -> GraphData n e -> (GraphData n e, Map n n)+collapseAndUpdate' fs gd = (gd', repLookup)+  where+    gr = graph gd+    (gr', reps) = collapseAndReplace' fs gr+    lns' = mkNodeMap $ labNodes gr'+    reps' = map (first S.fromList) reps+    rs = S.fromList $ wantedRootNodes gd+    replace r = maybe r ((M.!) lns' . snd)+                $ find (S.member r . fst) reps'+    gd' = gd { graph = gr'+             , wantedRootNodes = S.toList $ S.map replace rs+             , unusedRelationships = []+             }+    nlLookup = M.fromList $ labNodes gr+    getLs = mapMaybe (flip M.lookup nlLookup)+    repLookup = M.fromList . spreadOut $ map (first getLs) reps++-- | As with 'levelGraph', but use the expected roots rather than the+--   actual roots.+levelGraphFromRoot    :: (Ord n) => GraphData n e+                         -> GraphData (GenCluster n) e+levelGraphFromRoot gd = updateGraph (levelGraphFrom (wantedRootNodes gd)) gd
Data/Graph/Analysis/Algorithms/Clustering.hs view
@@ -7,6 +7,9 @@     Clustering and grouping algorithms that are graph-invariant and require    no user intervention.++   For a clustering algorithm that works only on directed graphs, see+   @levelGraph@ in "Data.Graph.Analysis.Algorithms.Directed".  -} module Data.Graph.Analysis.Algorithms.Clustering     ( -- * Clustering Algorithms@@ -21,7 +24,8 @@       CNodes,       collapseGraph,       collapseGraphBy,-      collapseGraphBy',+      collapseAndReplace,+      collapseAndReplace',       trivialCollapse,     ) where @@ -34,9 +38,10 @@  import Data.List(foldl', tails, delete, intersect) import Data.Function(on)+import Data.Maybe(fromJust) import qualified Data.Set.BKTree as BK import Data.Set.BKTree(BKTree, Metric(..))-import Control.Arrow(first, second)+import Control.Arrow(first, second, (***)) import System.Random(RandomGen, randomR)  -- -----------------------------------------------------------------------------@@ -276,6 +281,10 @@     It may be possible to extend this to a clustering algorithm by    collapsing low density regions into high density regions.++   If providing custom collapsing functions, you should ensure that+   for each function, it is not possible to have a recursive situation+   where a collapsed node keeps getting collapsed to itself.  -}  -- | A collapsed node contains a list of nodes that it represents.@@ -292,24 +301,32 @@ -- | Use the given functions to determine which nodes to collapse. collapseGraphBy    :: (DynGraph gr) => [gr (CNodes a) b -> [NGroup]]                       -> gr a b -> gr (CNodes a) b-collapseGraphBy fs = collapseGr fs'+collapseGraphBy fs = fst . collapseGr fs'     where       fs' = map (map (flip (,) Nothing) .) fs  -- | Use the given functions to determine which nodes to collapse, --   with a new label to represent the collapsed nodes.-collapseGraphBy'    :: (DynGraph gr) => [gr a b -> [(NGroup, a)]]-                       -> gr a b -> gr a b-collapseGraphBy' fs = unCollapse . collapseGr fs'+collapseAndReplace    :: (DynGraph gr) => [gr a b -> [(NGroup, a)]]+                         -> gr a b -> gr a b+collapseAndReplace fs = fst . collapseAndReplace' fs++-- | As with 'collapseAndReplace', but also return the+--   @('NGroup', a)@'s calculated with the functions provided.+collapseAndReplace'    :: (DynGraph gr) => [gr a b -> [(NGroup, a)]]+                          -> gr a b -> (gr a b, [(NGroup, a)])+collapseAndReplace' fs = (unCollapse *** strip) . collapseGr fs'     where-      -- convert gr a b -> [(NGroup a)] to+      -- convert gr a b -> [(NGroup, a)] to       -- gr (CNodes a) b -> [(NGroup, Maybe a)]-      fs' = map ((.nmap head) . (map (second Just) .)) fs+      fs' = map ((. nmap head) . (map (second Just) .)) fs+      -- Strip the Maybes+      strip = map (second fromJust)  -- | Collapse the graph. collapseGr      :: (DynGraph gr) => [gr (CNodes a) b -> [(NGroup, Maybe a)]]-                   -> gr a b -> gr (CNodes a) b-collapseGr fs g = foldl' collapseAllBy (makeCollapsible g) fs+                   -> gr a b -> (gr (CNodes a) b, [(NGroup, Maybe a)])+collapseGr fs g = foldl' collapseAllBy (makeCollapsible g, []) fs  -- | Return @'True'@ if the collapsed graph is either a singleton node --   or else isomorphic to the original graph (i.e. not collapsed at all).@@ -347,16 +364,21 @@       c' = (p,n1,l1++l2,s)  -- | Collapse the list of nodes down to one node.-collapseAll               :: (DynGraph gr) => gr (CNodes a) b-                             -> (NGroup, Maybe a)+collapseAll               :: (DynGraph gr) => (NGroup, Maybe a)                              -> gr (CNodes a) b-collapseAll g ([],_)      = g -- These two cases-collapseAll g ([_],_)     = g -- shouldn't occur.-collapseAll g ((n:ns),ma) = adj $ foldl' collapser g ns+                             -> gr (CNodes a) b+collapseAll ([],_)      g = g -- This case shouldn't occur+collapseAll ([n],ma)    g = maybeAdjustLabel n ma g+collapseAll ((n:ns),ma) g = foldl' collapser g' ns     where-      adj = maybe id (adjustLabel n) ma-      collapser g' = collapse g' n+      g' = maybeAdjustLabel n ma g+      collapser = flip collapse n +maybeAdjustLabel   :: (DynGraph gr) => Node -> Maybe a -> gr (CNodes a) b+                      -> gr (CNodes a) b+maybeAdjustLabel n = maybe id (adjustLabel n)++-- | Replace the label of the provided node with @[a]@. adjustLabel       :: (DynGraph gr) => Node -> a                      -> gr (CNodes a) b -> gr (CNodes a) b adjustLabel n a g = c & g'@@ -365,12 +387,13 @@       c = (p,n,[a],s)  -- | Collapse all results of the given function.-collapseAllBy     :: (DynGraph gr) => gr (CNodes a) b+collapseAllBy     :: (DynGraph gr) => (gr (CNodes a) b, [(NGroup, Maybe a)])                      -> (gr (CNodes a) b -> [(NGroup, Maybe a)])-                     -> gr (CNodes a) b-collapseAllBy g f = case (filter (not . single . fst) $ f g) of+                     -> (gr (CNodes a) b, [(NGroup, Maybe a)])+collapseAllBy g f = case (filter (not . null . fst) $ f (fst g)) of                       []     -> g                              -- We re-evaluate the function in case                              -- the original results used nodes that                              -- have been collapsed down.-                      (nsr:_) -> collapseAllBy (collapseAll g nsr) f+                      (nsr:_) -> second (nsr :)+                                 $ collapseAllBy (first (collapseAll nsr) g) f
Data/Graph/Analysis/Algorithms/Directed.hs view
@@ -25,8 +25,16 @@       coreOf,       -- * Clustering       levelGraph,+      levelGraphFrom,+      minLevel,+      -- * Node accessibility+      accessibleFrom,+      accessibleFrom',+      accessibleOnlyFrom,+      accessibleOnlyFrom',       -- * Other-      leafMinPaths+      leafMinPaths,+      leafMinPaths'     ) where  import Data.Graph.Analysis.Types@@ -36,10 +44,13 @@ import Data.Graph.Inductive.Query.BFS(esp)  import Data.List(minimumBy, unfoldr)+import Data.Maybe(fromMaybe) import Data.Function(on) import qualified Data.Map as M+import Data.Map(Map) import qualified Data.Set as S import Data.Set(Set)+import Control.Monad(ap)  -- ----------------------------------------------------------------------------- {- $ends@@ -161,29 +172,49 @@  {- |    Cluster the nodes in the graph based upon how far away they are-   from a root node.  Root nodes are in the cluster labelled "0",-   nodes in level "n" are at least /n/ edges away from a root node.+   from a root node.  Root nodes are in the cluster labelled 'minLevel',+   nodes in level \"n\" (with @n > minLevel@) are at least /n/ edges away+   from a root node. -}-levelGraph   :: (Ord a) => (DynGraph g) => g a b -> g (GenCluster a) b-levelGraph g = gmap addLbl g+levelGraph   :: (Ord a, DynGraph g) => g a b -> g (GenCluster a) b+levelGraph g = levelGraphFrom (rootsOf' g) g++-- | As with 'levelGraph' but provide a custom grouping of 'Node's to+--   consider as the \"roots\".+levelGraphFrom      :: (Ord a, DynGraph g) => NGroup -> g a b+                       -> g (GenCluster a) b+levelGraphFrom rs g = gmap addLbl g     where-      lvls = zip [0..] . map S.toList $ graphLevels g+      lvls = zip [minLevel..] . map S.toList $ graphLevels rs g       lvMap = M.fromList               $ concatMap (\(l,ns) -> map (flip (,) l) ns) lvls-      mkLbl n l = GC { clust = lvMap M.! n+      mkLbl n l = GC { clust = getLevel n                      , nLbl  = l                      }        addLbl (p,n,l,s) = (p, n, mkLbl n l, s) +      -- Have to consider unaccessible nodes.+      getLevel n = fromMaybe (pred minLevel) $ n `M.lookup` lvMap++-- | The level of the nodes in the 'NGroup' provided to+--   'levelGraphFrom' (or the root nodes for 'levelGraph').  A level+--   less than this indicates that the node is not accessible.+minLevel :: Int+minLevel = 0+ type NSet = Set Node  -- | Obtain the levels in the graph.-graphLevels   :: (DynGraph g) => g a b -> [NSet]-graphLevels g = unfoldr getNextLevel-                 (S.fromList $ rootsOf' g, g)+graphLevels :: (Graph g) => NGroup -> g a b -> [NSet]+graphLevels = flip graphLevels' . S.fromList -getNextLevel :: (DynGraph g) => (NSet, g a b)+graphLevels'   :: (Graph g) => g a b -> NSet -> [NSet]+graphLevels' g = unfoldr getNextLevel . flip (,) g++-- | The @(NSet, g a b)@ parameters are the current nodes to be+--   starting with in the current graph.+getNextLevel :: (Graph g) => (NSet, g a b)                 -> Maybe (NSet, (NSet, g a b)) getNextLevel (ns,g)     | S.null ns = Nothing@@ -191,8 +222,8 @@     where       g' = delNodes (S.toList ns) g       ns' = flip S.difference ns-            . S.unions . S.toList-            $ S.map getSuc ns+            . S.unions . map getSuc+            $ S.toList ns       getSuc = S.fromList . suc g  -- -----------------------------------------------------------------------------@@ -208,6 +239,14 @@       rs = rootsOf' g       ls = leavesOf' g +{- |+   The shortest paths to each of the leaves in the graph (excluding+   singletons).  This can be used to obtain an indication of the+   overall height/depth of the graph.+ -}+leafMinPaths' :: (Graph g) => g a b -> [NGroup]+leafMinPaths' = map (map node) . leafMinPaths+ -- | Given the list of roots in this graph, find the shortest path to --   this leaf node. lfMinPth        :: (Graph g) => g a b -> [Node] -> Node -> LNGroup a@@ -216,3 +255,39 @@                   . minimumBy (compare `on` fst)                   . addLengths                   $ map (\ r -> esp r l g) rs++-- -----------------------------------------------------------------------------++-- | Find all 'Node's that can be reached from the provided 'Node's.+accessibleFrom   :: (Graph g) => g a b -> [Node] -> [Node]+accessibleFrom g = S.toList . accessibleFrom' g . S.fromList++-- | Find all 'Node's that can be reached from the provided nodes+--   using 'Set's rather than lists.+accessibleFrom'   :: (Graph g) => g a b -> Set Node -> Set Node+accessibleFrom' g = S.unions . graphLevels' g++-- | Find those 'Node's that are reachable only from the provided+--   'Node's.+accessibleOnlyFrom   :: (Graph g) => g a b -> [Node] -> [Node]+accessibleOnlyFrom g = S.toList . accessibleOnlyFrom' g . S.fromList++-- | Find those 'Node's that are reachable only from the provided+--   'Node's, using 'Set's rather than lists.+accessibleOnlyFrom'   :: (Graph g) => g a b -> Set Node -> Set Node+accessibleOnlyFrom' g = M.keysSet+                        . fixPoint keepOnlyInternal+                        . setKeys (pre g)+                        . accessibleFrom' g++-- | Pseudo-inverse of 'M.keysSet'.+setKeys   :: (Ord a) => (a -> b) -> Set a -> Map a b+setKeys f = M.fromDistinctAscList . map (ap (,) f) . S.toAscList++-- | Removing nodes which have predecessors outside of this Map.+keepOnlyInternal :: Map Node NGroup -> Map Node NGroup+keepOnlyInternal = M.filter =<< onlyInternalPred++-- | Are these predecessor nodes all found within this Map?+onlyInternalPred :: Map Node NGroup -> NGroup -> Bool+onlyInternalPred = all . flip M.member
Data/Graph/Analysis/Internal.hs view
@@ -17,6 +17,8 @@ import Data.Either(partitionEithers) import qualified Data.Map as M import Data.Map(Map)+import qualified Data.Set as S+import Data.Set(Set) import Data.Maybe(fromJust) import Control.Arrow((***)) import Control.Monad(ap)@@ -39,11 +41,61 @@ applyBoth :: (a -> b) -> (a,a) -> (b,b) applyBoth f = f *** f +-- | Create a lookup 'Map' to determine which 'Node' has a specific label. mkNodeMap :: (Ord a) => [LNode a] -> Map a Node mkNodeMap = M.fromList . map swap +spreadOut :: [([a], b)] -> [(a,b)]+spreadOut = concatMap spread+  where+    spread (as, b) = map (flip (,) b) as+ -- -----------------------------------------------------------------------------+-- Items re-exported in Utils (needed by Types, so defined here to+-- avoid cycles). +-- | The node number of an 'LNode'.+node :: LNode a -> Node+node = fst++-- | The label of an 'LNode'.+label :: LNode a -> a+label = snd++-- | Find all the labelled nodes in the graph that match the given predicate.+filterNodes     :: (Graph g) => (g a b -> LNode a -> Bool) -> g a b -> [LNode a]+filterNodes p g = filter (p g) (labNodes g)++-- | Find all the nodes in the graph that match the given predicate.+filterNodes'     :: (Graph g) => (g a b -> Node -> Bool) -> g a b -> [Node]+filterNodes' p g = filter (p g) (nodes g)++-- | Obtain the labels for a list of 'Node's.+--   It is assumed that each 'Node' is indeed present in the given graph.+addLabels    :: (Graph g) => g a b -> [Node] -> [LNode a]+addLabels gr = map (ap (,) (fromJust . lab gr))++-- | Obtain the labels for a 'Set' of 'Node's.+--   It is assumed that each 'Node' is indeed present in the given graph.+addLabels'    :: (Ord a, Graph g) => g a b -> Set Node -> Set (LNode a)+addLabels' gr = S.map (ap (,) (fromJust . lab gr))++-- | Obtain the labels for a list of 'Node's.+--   It is assumed that each 'Node' is indeed present in the given graph.+getLabels   :: (Graph g) => g a b -> [Node] -> [a]+getLabels gr = map label . addLabels gr++-- | Obtain the labels for a list of 'Node's.+--   It is assumed that each 'Node' is indeed present in the given graph.+getLabels'   :: (Ord a, Graph g) => g a b -> Set Node -> Set a+getLabels' gr = S.fromList -- List fusion might make this more+                           -- efficient than multiple S.map's with the+                           -- resulting internal re-organisation.+                . getLabels gr+                . S.toList++-- -----------------------------------------------------------------------------+ -- | A relationship between two nodes with a label. type Rel n e = (n, n, e) @@ -79,11 +131,3 @@       dupSwap e@(x,y,l) | x == y    = [e]                         | otherwise = [e, (y,x,l)]       graphEdges = dupSwap' gEdges---- This is needed by Types, so it's defined here and then exported by--- Utils to avoid cyclic problems.---- | Obtain the labels for a list of 'Node's.---   It is assumed that each 'Node' is indeed present in the given graph.-addLabels    :: (Graph g) => g a b -> [Node] -> [LNode a]-addLabels gr = map (ap (,) (fromJust . lab gr))
Data/Graph/Analysis/Reporting.hs view
@@ -16,7 +16,9 @@       DocElement(..),       DocInline(..),       GraphSize(..),-      DocGraph,+      DocGraph(..),+      VisParams(..),+      VisProperties(..),       -- * Helper functions       -- $utilities       addLegend,@@ -30,12 +32,12 @@ import Data.Graph.Inductive(Node) import Data.GraphViz -import Data.Maybe(isJust, fromJust, catMaybes) import Data.Time(getZonedTime, zonedTimeToLocalTime, formatTime) import Control.Exception.Extensible(SomeException(..), tryJust) import System.Directory(createDirectoryIfMissing)-import System.FilePath((</>), (<.>))+import System.FilePath((</>), makeRelative) import System.Locale(defaultTimeLocale)+import Control.Monad(liftM, when)  -- ----------------------------------------------------------------------------- @@ -52,19 +54,20 @@    instance of 'DocumentGenerator'.  -} data Document = Doc { -- | Document location-                      rootDirectory  :: FilePath,-                      fileFront      :: String,+                      rootDirectory  :: FilePath+                    , fileFront      :: String                       -- | The sub-directory of 'rootDirectory',                       --   where graphs are to be created.-                      graphDirectory :: FilePath,+                    , graphDirectory :: FilePath                       -- | Pre-matter-                      title          :: DocInline,-                      author         :: String,-                      date           :: String,+                    , title          :: DocInline+                    , author         :: String+                    , date           :: String                       -- | Main-matter-                      legend         :: [(DocGraph, DocInline)],-                      content        :: [DocElement]+                    , legend         :: [(DocGraph, DocInline)]+                    , content        :: [DocElement]                     }+                deriving (Eq, Ord, Show, Read)  -- | Represents the class of document generators. class DocumentGenerator dg where@@ -76,11 +79,9 @@     docExtension   :: dg -> String  -- | Representation of a location, either on the internet or locally.-data Location = Web String | File FilePath--instance Show Location where-    show (Web url) = url-    show (File fp) = fp+data Location = Web String+              | File FilePath+              deriving (Eq, Ord, Show, Read)  -- | Elements of a document. data DocElement = Section DocInline [DocElement]@@ -89,6 +90,7 @@                 | Itemized [DocElement]                 | Definitions [(DocInline, DocInline)]                 | GraphImage DocGraph+                deriving (Eq, Ord, Show, Read)  -- | Inline elements of a document. data DocInline = Text String@@ -98,12 +100,47 @@                | Emphasis DocInline                | DocLink DocInline Location                | DocImage DocInline Location+               deriving (Eq, Ord, Show, Read)  -- | Specify the 'DotGraph' to turn into an image, its filename (sans --   extension) and its caption.  The 'DotGraph' should not have a --   'Size' set.-type DocGraph = (FilePath, DocInline, DotGraph Node)+data DocGraph = DG {  -- | What name to provide the image file+                      --   (without an extension).+                     imageFile   :: FilePath+                   , description :: DocInline+                   , dotGraph    :: DotGraph Node+                   }+              deriving (Eq, Ord, Show, Read) +-- | Defines the parameters used for creating visualisations of+--   graphs.+data VisParams = VParams { -- | Root directory of the document.+                           rootDir      :: FilePath+                           -- | Image sub-directory.+                         , graphDir     :: FilePath+                           -- | The default visualisation.+                         , defaultImage :: VisProperties+                           -- | If @'Just' vp'@, then a larger+                           --   visualisation is linked to from the+                           --   default one.+                         , largeImage   :: Maybe VisProperties+                           -- | Should the Dot source code be saved as well?+                         , saveDot      :: Bool+                         }+               deriving (Eq, Ord, Show, Read)++-- | A specification on how to visualise a 'DocGraph'.+data VisProperties = VProps { size   :: GraphSize+                            , format :: GraphvizOutput+                            }+                   deriving (Eq, Ord, Show, Read)++-- | Specify the size the 'DotGraph' should be at.+data GraphSize = GivenSize Point  -- ^ Specify the size to use.+               | DefaultSize      -- ^ Let GraphViz choose an appropriate size.+               deriving (Eq, Ord, Show, Read)+ -- -----------------------------------------------------------------------------  {- $utilities@@ -111,29 +148,30 @@  -}  -- | Create the legend section and add it to the document proper.-addLegend       :: FilePath -> FilePath -> Document -> IO Document-addLegend fp gfp d = do mLg <- legendToElement fp gfp $ legend d-                        let es = content d-                            es' = maybe es (flip (:) es) mLg-                        return $ d { legend  = []-                                   , content = es'-                                   }+addLegend             :: FilePath -> FilePath -> VisProperties+                         -> Document -> IO Document+addLegend fp gfp vp d = do mLg <- legendToElement fp gfp vp $ legend d+                           let es = content d+                               es' = maybe es (flip (:) es) mLg+                           return $ d { legend  = []+                                      , content = es'+                                      } -legendToElement           :: FilePath -> FilePath -> [(DocGraph, DocInline)]+legendToElement           :: FilePath -> FilePath -> VisProperties+                             -> [(DocGraph, DocInline)]                              -> IO (Maybe DocElement)-legendToElement _  _   [] = return Nothing-legendToElement fp gfp ls = do mDefs <- mapM (uncurry (legToDef fp gfp)) ls-                               let defs = catMaybes mDefs-                                   df   = Definitions defs-                                   sec  = Section (Text "Legend") [df]-                               return $ Just sec--legToDef               :: FilePath -> FilePath -> DocGraph -> DocInline-                          -> IO (Maybe (DocInline, DocInline))-legToDef fp gfp dg def = fmap (fmap (flip (,) def)) img-    where-      img = graphImage fp gfp DefaultSize Png "png" DocImage dg+legendToElement _  _   _  [] = return Nothing+legendToElement fp gfp vp ls = do defs <- mapM (uncurry (legToDef fp gfp vp)) ls+                                  let df   = Definitions defs+                                  return $ Just $ Section (Text "Legend") [df] +legToDef                  :: FilePath -> FilePath -> VisProperties+                             -> DocGraph -> DocInline+                             -> IO (DocInline, DocInline)+legToDef fp gfp vp dg def = liftM (flip (,) def)+                            $ graphImage' fp gfp vp' dg+  where+    vp' = vp { size = DefaultSize }  -- | Return today's date as a string, e.g. \"Monday 1 January, 2000\". --   This arbitrary format is chosen as there doesn't seem to be a way@@ -158,62 +196,73 @@       isRight (Right _) = True       isRight _         = False --- | Attempts to creates a png file (with the given filename in the---   given directory) from the graph using the given attributes.---   If the second set of attributes is not 'Nothing', then the first---   image links to the second.  The whole result is wrapped in a---   'Paragraph'.-createGraph :: FilePath -> FilePath -> GraphSize -> Maybe GraphSize-            -> DocGraph -> IO (Maybe DocElement)-createGraph fp gfp s ms (fn,inl,ag)-    = do eImg <- gI s Png "png" DocImage fn inl Nothing-         if isJust eImg-            then case ms of-                   Nothing    -> rt eImg-                   (Just s') -> rt =<< gI s' Svg "svg" DocLink fn' (toImg eImg) eImg-            else return Nothing-    where-      fn' = fn ++ "-large"-      i2e i = Just (i,Paragraph [i])-      rt = return . fmap snd-      -- This is safe because of the isJust above.-      toImg = fst . fromJust-      gI a o e ln nm lb fl = do mImg <- graphImage fp gfp a o e ln (nm,lb,ag)-                                case mImg of-                                  Nothing    -> return fl-                                  (Just img) -> return $ i2e img+-- | Attempts to create image files (with the given filename in the+--   given directory) from the graph.  If the second 'VisProperties'+--   not 'Nothing', then the first image links to the second.  The+--   whole result is wrapped in a 'Paragraph'.  'unDotPath' is applied+--   to the filename in the 'DocGraph'.+createGraph :: VisParams -- ^ Visualisation parameters.+               -> DocGraph+               -> IO DocElement+createGraph params dg+  = do when (saveDot params) (graphImage rDir gDir vpD dgD >> return ())+       dl  <- graphImage' rDir gDir vp dg'+       dl' <- maybe return tryImg mvp dl+       return $ Paragraph [dl']+  where+    rDir = rootDir params+    gDir = graphDir params+    vp = defaultImage params+    vpD = VProps { size   = DefaultSize+                 , format = Canon+                 }+    mvp = largeImage params+    dg' = dg { imageFile = unDotPath $ imageFile dg }+    dgL = checkLargeFilename vp mvp dg'+    dgD = checkFilename vp vpD "dot" dg'+    tryImg vp' di = liftM (either (const di) (DocLink di))+                   $ graphImage rDir gDir vp' dgL --- | Create the inline image/link from the given DocGraph.-graphImage :: FilePath -> FilePath -> GraphSize-           -> GraphvizOutput -> FilePath-           -> (DocInline -> Location -> DocInline)-           -> DocGraph -> IO (Maybe DocInline)-graphImage fp gfp s output ext link (fn,inl,dg)-    = do created <- runGraphviz dg' output filename'-         if created-            then return (Just img)-            else return Nothing-    where-      dg' = setSize s dg-      fn' = unDotPath fn-      filename = gfp </> fn' <.> ext-      filename' = fp </> filename-      loc = File filename-      img = link inl loc+-- | If both output formats are the same, then the larger image needs+--   a different filename.+checkLargeFilename :: VisProperties -> Maybe VisProperties+                      -> DocGraph -> DocGraph+checkLargeFilename _   Nothing    dg = dg+checkLargeFilename vp1 (Just vp2) dg = checkFilename vp1 vp2 "large" dg --- | Specify the size the 'DotGraph' should be at.-data GraphSize = GivenSize Point  -- ^ Specify the size to use.-               | DefaultSize      -- ^ Let GraphViz choose an appropriate size.+checkFilename :: VisProperties -> VisProperties -> String+                 -> DocGraph -> DocGraph+checkFilename vp1 vp2 s dg+  | format vp1 == format vp2 = dg { imageFile = imageFile dg ++ '-' : s }+  | otherwise                = dg +graphImage :: FilePath -> FilePath -> VisProperties -> DocGraph+              -> IO (Either DocInline Location)+graphImage rDir gDir vp dg = liftM (either' Text (File . fixPath))+                             $ addExtension (runGraphviz dot)+                                            (format vp)+                                            filename+  where+    dot = setSize vp $ dotGraph dg+    filename = rDir </> gDir </> imageFile dg+    fixPath = makeRelative rDir++graphImage' :: FilePath -> FilePath -> VisProperties -> DocGraph+               -> IO DocInline+graphImage' rDir gDir vp dg = liftM (either id f)+                              $ graphImage rDir gDir vp dg+  where+    f = DocImage (description dg)+ -- | Add a 'GlobalAttribute' to the 'DotGraph' specifying the given size.-setSize                 :: GraphSize -> DotGraph a -> DotGraph a-setSize DefaultSize   g = g-setSize (GivenSize p) g = g { graphStatements = stmts' }-    where-      stmts = graphStatements g-      stmts' = stmts { attrStmts = a : attrStmts stmts }-      a = GraphAttrs [s]-      s = Size p+setSize      :: VisProperties -> DotGraph a -> DotGraph a+setSize vp g = case size vp of+                 DefaultSize   -> g+                 (GivenSize s) -> g { graphStatements = setS s}+  where+    setS s = stmts { attrStmts = sizeA s : attrStmts stmts }+    stmts = graphStatements g+    sizeA s = GraphAttrs [Size s]  -- | Using a 6:4 ratio, create the given 'Point' representing --   width,height from the width.@@ -227,3 +276,8 @@     where       replace '.' = '-'       replace c   = c++-- | Map either element of an 'Either'.+either'                 :: (a -> c) -> (b -> d) -> Either a b -> Either c d+either' fl _  (Left a)  = Left  $ fl a+either' _  fr (Right b) = Right $ fr b
Data/Graph/Analysis/Reporting/Pandoc.hs view
@@ -21,13 +21,15 @@       pandocHtml,       pandocLaTeX,       pandocRtf,-      pandocMarkdown+      pandocMarkdown,+      alsoSaveDot     ) where  -- TODO : the ability to create multiple files.  import Data.Graph.Analysis.Reporting +import Data.GraphViz.Commands(GraphvizOutput(Png, Svg)) import Text.Pandoc  import Data.List(intersperse)@@ -44,30 +46,32 @@  -}  pandocHtml :: PandocDocument-pandocHtml = pd { writer       = writeHtmlString-                , extension    = "html"-                , header       = "" -- Header will be included-                , extGraphSize = Just DefaultSize+pandocHtml = pd { writer        = writeHtmlString+                , extension     = "html"+                , templateName  = "html"+                , extGraphProps = Just VProps { size   = DefaultSize+                                              , format = Svg+                                              }                 }  pandocLaTeX :: PandocDocument-pandocLaTeX = pd { writer    = writeLaTeX-                 , extension = "tex"-                 , header    = defaultLaTeXHeader+pandocLaTeX = pd { writer     = writeLaTeX+                 , extension  = "tex"+                 , templateName = "latex"                  -- 4.5" should be less than \textwidth in LaTeX.-                 , graphSize = createSize 4.5+                 , graphProps = defaultProps { size = createSize 4.5 }                  }  pandocRtf :: PandocDocument pandocRtf = pd { writer    = writeRTF                , extension = "rtf"-               , header    = defaultRTFHeader+               , templateName = "rtf"                }  pandocMarkdown :: PandocDocument pandocMarkdown = pd { writer = writeMarkdown                     , extension = "text"-                    , header = ""+                    , templateName = "markdown"                     }  -- -----------------------------------------------------------------------------@@ -79,32 +83,41 @@ -- | Definition of a Pandoc Document.  Size measurements are in inches, --   and a 6:4 ratio is used for width:length. data PandocDocument = PD { -- | The Pandoc document style-                           writer       :: WriterOptions -> Pandoc -> String,+                           writer        :: WriterOptions -> Pandoc -> String                            -- | The file extension used-                           extension    :: FilePath,-                           -- | The Pandoc header to use-                           header       :: String,+                         , extension     :: FilePath+                           -- | Which template to get.+                         , templateName  :: String                            -- | Size of graphs to be produced.-                           graphSize    :: GraphSize,+                         , graphProps    :: VisProperties                            -- | Optional size of external linked graphs.-                           extGraphSize :: Maybe GraphSize+                         , extGraphProps :: Maybe VisProperties+                           -- | Should the Dot source code be saved as well?+                         , keepDot       :: Bool                          }  -- | Some default sizes.  Note that all other fields of 'PandocDocument' --   still need to be defined. pd :: PandocDocument-pd = PD { writer       = undefined,-          extension    = undefined,-          header       = undefined,-          graphSize    = defaultSize,-          extGraphSize = Nothing+pd = PD { writer        = undefined+        , extension     = undefined+        , templateName  = undefined+        , graphProps    = defaultProps+        , extGraphProps = Nothing+        , keepDot       = False         } +-- | Also save the generated Dot code to file when creating visualisations.+alsoSaveDot   :: PandocDocument -> PandocDocument+alsoSaveDot p = p { keepDot = True }+ defaultWidth :: Double defaultWidth = 10 -defaultSize :: GraphSize-defaultSize = createSize defaultWidth+defaultProps :: VisProperties+defaultProps = VProps { size   = createSize defaultWidth+                      , format = Png+                      }  instance DocumentGenerator PandocDocument where     createDocument = createPandoc@@ -119,34 +132,30 @@  -- | Used when traversing the document structure. data PandocProcess = PP { secLevel :: Int-                        , filedir  :: FilePath-                        , graphdir :: FilePath-                        , grSize   :: GraphSize-                        , eGSize   :: Maybe GraphSize+                        , visParams :: VisParams                         }+                   deriving (Eq, Ord, Show, Read)  -- | Start with a level 1 heading. defaultProcess :: PandocProcess-defaultProcess = PP { secLevel = 1-                    , graphdir = undefined-                    , filedir  = undefined-                    , grSize   = undefined-                    , eGSize   = undefined+defaultProcess = PP { secLevel  = 1+                    , visParams = undefined                     }  -- | Create the document. createPandoc     :: PandocDocument -> Document -> IO (Maybe FilePath)-createPandoc p d = do created <- tryCreateDirectory dir+createPandoc p d = do Right template <- getDefaultTemplate (templateName p)+                      created <- tryCreateDirectory dir                       -- If the first one fails, so will this one.                       tryCreateDirectory $ dir </> gdir                       if not created                          then failDoc-                         else do d' <- addLegend dir gdir d+                         else do d' <- addLegend dir gdir (graphProps p) d                                  elems <- multiElems pp $ content d'                                  case elems of                                    Just es -> do let es' = htmlAuthDt : es                                                      pnd = Pandoc meta es'-                                                     doc = convert pnd+                                                     doc = convert template pnd                                                  wr <- tryWrite doc                                                  case wr of                                                    (Right _) -> success@@ -160,13 +169,14 @@       meta = makeMeta (title d) auth dt       -- Html output doesn't show date and auth anywhere by default.       htmlAuthDt = htmlInfo auth dt-      pp = defaultProcess { filedir = dir-                          , graphdir = gdir-                          , grSize = graphSize p-                          , eGSize = extGraphSize p-                          }-      opts = writerOptions { writerHeader = (header p) }-      convert = writer p opts+      pp = defaultProcess { visParams = vp }+      vp = VParams { rootDir      = dir+                   , graphDir     = gdir+                   , defaultImage = graphProps p+                   , largeImage   = extGraphProps p+                   , saveDot      = keepDot p+                   }+      convert t = writer p (writerOptions {writerTemplate = t})       file = dir </> fileFront d <.> extension p       tryWrite :: String -> IO (Either SomeException ())       tryWrite = try . writeFile file@@ -180,8 +190,8 @@  -}  -- | The meta information-makeMeta     :: DocInline -> String -> String -> Meta-makeMeta t a = Meta (inlines t) [a]+makeMeta         :: DocInline -> String -> String -> Meta+makeMeta tle a t = Meta (inlines tle) [[Str a]] [Str t]  -- | Html output doesn't show the author and date; use this to print it. htmlInfo         :: String -> String -> Block@@ -236,16 +246,11 @@                                     return (fmap (return . BulletList) elems')  elements _ (Definitions defs)  = return . Just . return . DefinitionList-                                 $ map (inlines *** (return . Plain . inlines))+                                 $ map (inlines *** ((:[]) . (:[])+                                                     . Plain . inlines))                                        defs -elements p (GraphImage dg)     = do el <- createGraph (filedir p)-                                                      (graphdir p)-                                                      (grSize p)-                                                      (eGSize p) dg-                                    case el of-                                      Nothing  -> return Nothing-                                      Just img -> elements p img+elements p (GraphImage dg)     = elements p =<< createGraph (visParams p) dg  -- | Concatenate the result of multiple calls to 'elements'. multiElems         :: PandocProcess -> [DocElement] -> IO (Maybe [Block])
Data/Graph/Analysis/Types.hs view
@@ -22,6 +22,8 @@       LNGroup,       -- * Functions on @GraphData@.       wantedRoots,+      addRoots,+      addRootsBy,       applyAlg,       applyDirAlg,       mergeUnused,@@ -74,6 +76,22 @@       gns = S.fromList $ nodes g       rs = S.fromList $ wantedRootNodes gd       rs' = S.toList $ gns `S.intersection` rs++-- | Add extra expected root nodes.  No checks are made that these+--   are valid 'Node' values.+addRoots      :: GraphData n e -> NGroup -> GraphData n e+addRoots gd ns = gd { wantedRootNodes = S.toList rs' }+    where+      ns' = S.fromList ns+      rs = S.fromList $ wantedRootNodes gd+      rs' = rs `S.union` ns'++-- | Use a filtering function to find extra root nodes to add.+addRootsBy      :: (LNode n -> Bool) -> GraphData n e -> GraphData n e+addRootsBy p gd = addRoots gd rs'+    where+      p' _ = p+      rs' = map node $ applyAlg (filterNodes p') gd  -- | Apply an algorithm to the data to be analysed. applyAlg   :: (AGr n e -> a) -> GraphData n e -> a
Data/Graph/Analysis/Utils.hs view
@@ -11,14 +11,17 @@     ( -- * Graph functions       -- ** Data extraction       -- $extracting-      node,-      label,+      node,         -- Re-exported from Internal+      label,        -- Re-exported from Internal       labels,       edge,       eLabel,-      addLabels, -- Re-exported from Internal-      filterNodes,-      filterNodes',+      addLabels,    -- Re-exported from Internal+      addLabels',   -- Re-exported from Internal+      getLabels,    -- Re-exported from Internal+      getLabels',   -- Re-exported from Internal+      filterNodes,  -- Re-exported from Internal+      filterNodes', -- Re-exported from Internal       pathValues,       -- ** Graph manipulation       undir,@@ -82,14 +85,6 @@  -- $extracting Extracting data from graphs. --- | The node number of an 'LNode'.-node :: LNode a -> Node-node = fst---- | The label of an 'LNode'.-label :: LNode a -> a-label = snd- -- | The labels of all nodes in a tree. labels :: (Graph g) => g a b -> [a] labels = map label . labNodes@@ -102,14 +97,6 @@ eLabel         :: LEdge b -> b eLabel (_,_,b) = b --- | Find all the labelled nodes in the graph that match the given predicate.-filterNodes     :: (Graph g) => (g a b -> LNode a -> Bool) -> g a b -> [LNode a]-filterNodes p g = filter (p g) (labNodes g)---- | Find all the nodes in the graph that match the given predicate.-filterNodes'     :: (Graph g) => (g a b -> Node -> Bool) -> g a b -> [Node]-filterNodes' p g = filter (p g) (nodes g)- -- | Extract the actual 'LNode's from an 'LPath'. pathValues          :: LPath a -> [LNode a] pathValues (LP lns) = lns@@ -171,7 +158,7 @@ compact' = emap length . compact  -- | Compact the graph by adjoining identical duplicate edges.-compactSame :: (Ord b) => (DynGraph gr) => gr a b -> gr a (Int,b)+compactSame :: (Ord b, DynGraph gr) => gr a b -> gr a (Int,b) compactSame = gmap cmpct     where       cEs = map toAdj . group . sort
Graphalyze.cabal view
@@ -1,5 +1,5 @@ Name:                Graphalyze-Version:             0.8.0.0+Version:             0.9.0.0 Synopsis:            Graph-Theoretic Analysis library. Description:         A library to use graph theory to analyse the relationships                         inherent in discrete data.@@ -31,8 +31,8 @@                          time,                          bktrees >= 0.2,                          fgl >= 5.4.2.2,-                         graphviz >= 2999.6.0.0 && < 2999.7.0.0,-                         pandoc+                         graphviz >= 2999.8.0.0 && < 2999.9.0.0,+                         pandoc >= 1.4 && < 1.5          Exposed-Modules:     Data.Graph.Analysis                              Data.Graph.Analysis.Types
TODO view
@@ -12,3 +12,7 @@  * Write a README +* For Reporting, have a Document -> IO Document function that+  generates all the visualisations and replaces all GraphImage+  DocElement values with DocImage, etc. (and then the Document ->+  Pandoc conversion can be pure).