packages feed

haggle 0.2 → 0.3

raw patch · 9 files changed

+128/−12 lines, 9 filesdep −monad-primitivedep ~fgldep ~vectorPVP ok

version bump matches the API change (PVP)

Dependencies removed: monad-primitive

Dependency ranges changed: fgl, vector

API changes (from Hackage documentation)

- Data.Graph.Haggle: type family VertexLabel g
- Data.Graph.Haggle.Classes: type family VertexLabel g;
+ Data.Graph.Haggle: replaceLabeledVertex :: (InductiveGraph g, Graph g, HasEdgeLabel g, HasVertexLabel g) => g -> Vertex -> VertexLabel g -> g
+ Data.Graph.Haggle: type EdgeLabel g
+ Data.Graph.Haggle: type ImmutableGraph g
+ Data.Graph.Haggle: type MEdgeLabel g
+ Data.Graph.Haggle: type MVertexLabel g
+ Data.Graph.Haggle: type MutableGraph g :: (Type -> Type) -> Type
+ Data.Graph.Haggle: type VertexLabel g
+ Data.Graph.Haggle.Classes: replaceLabeledVertex :: InductiveGraph g => g -> Vertex -> VertexLabel g -> g
+ Data.Graph.Haggle.Classes: type EdgeLabel g;
+ Data.Graph.Haggle.Classes: type ImmutableGraph g;
+ Data.Graph.Haggle.Classes: type MEdgeLabel g;
+ Data.Graph.Haggle.Classes: type MVertexLabel g;
+ Data.Graph.Haggle.Classes: type MutableGraph g :: (Type -> Type) -> Type;
+ Data.Graph.Haggle.Classes: type VertexLabel g;
+ Data.Graph.Haggle.PatriciaTree: instance GHC.Base.Functor (Data.Graph.Haggle.PatriciaTree.Ctx nl)
+ Data.Graph.Haggle.PatriciaTree: instance GHC.Base.Functor (Data.Graph.Haggle.PatriciaTree.PatriciaTree nl)

Files

ChangeLog.md view
@@ -1,3 +1,12 @@+0.3 (2023-08-20)+----------------++- Add `Functor` instances for `PatriciaTree` and `Ctx` (@RyanGlScott)+- Support building with vector-0.13.* (@RyanGlScott)+- Remove an unused dependency on monad-primitive (@RyanGlScott)+- Add `replaceLabeledVertex` to the `InductiveGraph` interface (@kquick)+- Fixed a bug in the dominators calculation (@kquick)+ 0.2 (2022-05-08) ---------------- 
README.md view
@@ -1,5 +1,5 @@ # Overview-![travis-ci status](https://travis-ci.org/travitch/haggle.svg?branch=master)+![Github Actions status](https://github.com/travitch/haggle/actions/workflows/haskell-ci.yml/badge.svg)  Haggle is a graph library for Haskell.  It aims to support large graphs efficiently and compactly.  It differs from
haggle.cabal view
@@ -1,5 +1,5 @@ name: haggle-version: 0.2+version: 0.3 synopsis: A graph library offering mutable, immutable, and inductive graphs description: This library provides mutable (in ST or IO), immutable, and inductive graphs.              There are multiple graphs implementations provided to support different use@@ -16,7 +16,7 @@ category: Data Structures, Graphs build-type: Simple cabal-version: >=1.10-tested-with: GHC ==7.10.3 || ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.2+tested-with: GHC ==7.10.3 || ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.8 || ==9.4.5 || ==0.6.2 extra-source-files: ChangeLog.md                     README.md @@ -43,13 +43,12 @@                  Data.Graph.Haggle.Internal.BitSet   build-depends: base >= 4.5 && < 5,                  ref-tf >= 0.4 && < 0.6,-                 vector >= 0.9 && < 0.13,+                 vector >= 0.9 && < 0.14,                  vector-th-unbox >= 0.2.1.3 && < 0.3,                  primitive >= 0.4 && < 0.9,                  containers >= 0.4,                  hashable >= 1.2 && < 1.5,-                 deepseq >= 1 && < 2,-                 monad-primitive < 0.2+                 deepseq >= 1 && < 2  test-suite GraphTests   type: exitcode-stdio-1.0@@ -60,7 +59,8 @@   build-depends: haggle,                  base >= 4.5,                  containers,-                 fgl,+                 -- fgl 5.8.1.1 is required for its dom fix for oracle testing here+                 fgl >= 5.8.1.1,                  HUnit,                  QuickCheck > 2.4,                  test-framework,
src/Data/Graph/Haggle.hs view
@@ -157,6 +157,7 @@   deleteEdge,   deleteEdgesBetween,   replaceLabeledEdge,+  replaceLabeledVertex,   deleteVertex,   I.Context(..), @@ -379,6 +380,10 @@ replaceLabeledEdge :: (I.InductiveGraph g, I.Graph g, I.HasEdgeLabel g, I.HasVertexLabel g) => g -> I.Vertex -> I.Vertex -> I.EdgeLabel g -> Maybe (I.Edge, g) replaceLabeledEdge = I.replaceLabeledEdge {-# INLINABLE replaceLabeledEdge #-}++replaceLabeledVertex :: (I.InductiveGraph g, I.Graph g, I.HasEdgeLabel g, I.HasVertexLabel g) => g -> I.Vertex -> I.VertexLabel g -> g+replaceLabeledVertex = I.replaceLabeledVertex+{-# INLINABLE replaceLabeledVertex #-}  deleteVertex :: (I.InductiveGraph g, I.Graph g, I.HasEdgeLabel g, I.HasVertexLabel g) => g -> I.Vertex -> g deleteVertex = I.deleteVertex
src/Data/Graph/Haggle/Algorithms/Dominators.hs view
@@ -61,7 +61,7 @@   (res, toNode, fromNode) <- domWork g root   let dom' = getDom toNode res       rest = M.keys (M.filter (-1 ==) fromNode)-      verts = vertices g+      verts = reachable root g   return $ [(toNode ! i, dom' ! i) | i <- [0..V.length dom' - 1]] ++            [(n, verts) | n <- rest] @@ -70,6 +70,7 @@   | null trees = Nothing   | otherwise = return (idom, toNode, fromNode)   where+    vlist = reachable root g     -- Build up a depth-first tree from the root as a first approximation     trees@(~[tree]) = dff g [root]     (s, ntree) = numberTree 0 tree@@ -83,7 +84,7 @@     -- are the rest of the nodes in the graph, mapped to -1 (since they aren't     -- going to be in the result)     treeNodes = M.fromList $ zip (T.flatten tree) (T.flatten ntree)-    otherNodes = M.fromList $ zip (vertices g) (repeat (-1))+    otherNodes = M.fromList $ zip vlist (repeat (-1))     fromNode = M.unionWith const treeNodes otherNodes     -- Translate from internal nodes back to graph nodes (only need the nodes     -- in the depth-first tree)@@ -93,7 +94,7 @@     -- Use a pre-pass over the graph to collect predecessors so that we don't     -- require a Bidirectional graph.  We need a linear pass over the graph     -- here anyway, so we don't lose anything.-    predMap = fmap S.toList $ foldr (toPredecessor g) M.empty (vertices g)+    predMap = fmap S.toList $ foldr (toPredecessor g) M.empty vlist     preds = V.fromList $ [0] : [filter (/= -1) (map (fromNode M.!) (predMap M.! (toNode ! i)))                                | i <- [1..s-1]]     idom = fixEq (refineIDom preds) idom0
src/Data/Graph/Haggle/Classes.hs view
@@ -208,6 +208,10 @@     let g' = deleteEdgesBetween g src dst     in insertLabeledEdge g' src dst lbl +  -- | Used to update the label for a Vertex.  The Vertex itself remains, but the+  -- label associated with the vertex is modified.+  replaceLabeledVertex :: g -> Vertex -> VertexLabel g -> g+   -- | Remove a 'Vertex' from the graph   deleteVertex :: g -> Vertex -> g   deleteVertex g v = fromMaybe g $ do
src/Data/Graph/Haggle/PatriciaTree.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TypeFamilies, BangPatterns #-}+{-# LANGUAGE TypeFamilies, BangPatterns, DeriveFunctor #-} -- | This graph is based on the implementation in fgl (using -- big-endian patricia-tries -- IntMap). --@@ -20,6 +20,7 @@ import qualified Data.Graph.Haggle.Internal.Basic as I  data Ctx nl el = Ctx !(IntMap el) I.Vertex nl !(IntMap el)+  deriving Functor  instance (NFData nl, NFData el) => NFData (Ctx nl el) where   rnf (Ctx p v nl s) =@@ -37,6 +38,7 @@ -- This graph type is most useful for incremental construction in pure -- code.  It also supports node removal from pure code. data PatriciaTree nl el = Gr { graphRepr :: IntMap (Ctx nl el) }+  deriving Functor  instance (NFData nl, NFData el) => NFData (PatriciaTree nl el) where   rnf (Gr im) = im `deepseq` ()@@ -120,6 +122,17 @@         v = I.V vid         g' = IM.insert vid (Ctx mempty v lab mempty) g     in (v, Gr g')+  replaceLabeledVertex (Gr g) (I.V v) vl =+    let updLabel (Ctx ie nv _nl oe) = Ctx ie nv vl oe+    in Gr $ IM.adjust updLabel v g+  insertLabeledEdge gr@(Gr g) v1@(I.V src) (I.V dst) lab | src == dst = do+    guard (IM.member src g)+    guard (not (I.edgeExists gr v1 v1))+    let e = I.E (-1) src src+    Ctx spp sv sl sss <- IM.lookup src g+    let ctx' = Ctx (IM.insert src lab spp) sv sl (IM.insert dst lab sss)+        !g' = IM.insert src ctx' g+    return (e, Gr g')   insertLabeledEdge gr@(Gr g) v1@(I.V src) v2@(I.V dst) lab = do     guard (IM.member src g && IM.member dst g)     guard (not (I.edgeExists gr v1 v2))@@ -132,6 +145,11 @@         !g'' = IM.insert dst dctx' g'     return (e, Gr g'')   deleteEdge g (I.E _ s d) = I.deleteEdgesBetween g (I.V s) (I.V d)+  deleteEdgesBetween gr@(Gr g) (I.V src) (I.V dst) | src == dst = fromMaybe gr $ do+    Ctx spp sv sl sss <- IM.lookup src g+    let ctx' = Ctx (IM.delete src spp) sv sl (IM.delete src sss)+        !g' = IM.insert src ctx' g+    return (Gr g')   deleteEdgesBetween gr@(Gr g) (I.V src) (I.V dst) = fromMaybe gr $ do     Ctx spp sv sl sss <- IM.lookup src g     Ctx dpp dv dl dss <- IM.lookup dst g
src/Data/Graph/Haggle/VertexLabelAdapter.hs view
@@ -229,7 +229,7 @@ -- > import Data.Graph.Haggle.VertexLabelAdapter -- > import Data.Graph.Haggle.SimpleBiDigraph -- >--- > let g = fromEdgeList newMSimpleBiDigraph [(0,1), (1,2), (2,3), (3,0)]+-- > let (g,m) = fromEdgeList newMSimpleBiDigraph [(0,1), (1,2), (2,3), (3,0)] -- -- @g@ has type SimpleBiDigraph. --
tests/GraphTests.hs view
@@ -1,4 +1,6 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE TypeFamilies #-}+ -- | This module tests Haggle by comparing its results to those of FGL. -- This assumes that FGL is reasonably correct. --@@ -6,6 +8,7 @@ -- then constructs equivalent FGL and Haggle graphs.  The quickcheck -- properties for each operation try to ensure that the two implementations -- return the same results.+ module Main ( main ) where  import Test.Framework ( defaultMain, Test )@@ -80,8 +83,13 @@         , testProperty "prop_sameComponents" prop_sameComponents         , testProperty "prop_sameNoComponents" prop_sameNoComponents         , testProperty "prop_immDominatorsSame" prop_immDominatorsSame++        -- prop_dominatorsSame requires fgl 5.8.1.1, which fixes errors in fgl's+        -- dom functionality that is used as the oracle for the tests here.         , testProperty "prop_dominatorsSame" prop_dominatorsSame+         ] <>  testPatricia+        <> testExplicit  prop_sameVertexCount :: GraphPair -> Bool prop_sameVertexCount (GP _ bg tg) =@@ -167,6 +175,70 @@  -- Explicit tests for various functionality +testExplicit :: [Test.Framework.Test]+testExplicit =+  let gr0 = foldl (\g -> snd . HGL.insertLabeledVertex g)+            (HGL.emptyGraph :: HGL.PatriciaTree Int Char)+            [1,2,4]+      vs = fst <$> HGL.labeledVertices gr0++      plusEdge g f t = snd $ fromJust $ HGL.insertLabeledEdge g f t 'a'++      gr1 = plusEdge gr0 (vs !! 0) (vs !! 1)+      -- gr1 has three nodes, two are connected, one is not connected (i.e. two+      -- independent subgraphs)++      gr2 = plusEdge (plusEdge gr0 (vs !! 1) (vs !! 0)) (vs !! 1) (vs !! 1)+++  in hUnitTestToTests $ test+     [ "haggle (patricia) [1-2,4] reachable from 1" ~:+       do HGL.reachable (vs !! 0) gr1 @?= [ (vs !! 0)+                                          , (vs !! 1)+                                          ]+     , "haggle (patricia) [1-2,4] reachable from 2" ~:+       do HGL.reachable (vs !! 1) gr1 @?= [ (vs !! 1)+                                          ]+     , "haggle (patricia) [2-1,2-2] reachable from 1" ~:+       do HGL.reachable (vs !! 0) gr2 @?= [ (vs !! 0)+                                          ]++     , "haggle dominator [1-2,4] from 1" ~:+       do HGL.dominators gr1 (vs !! 0) @?= [ (vs !! 0, [ (vs !! 0) ])+                                           , (vs !! 1, [ (vs !! 1), (vs !! 0) ])+                                           ]++     , "haggle dominator [2-1,2-2,4] from 1" ~:+       do HGL.dominators gr2 (vs !! 0) @?= [ (vs !! 0, [ (vs !! 0) ])+                                           ]++     , "haggle add self-edge" ~:+       do let Just (e,g) = HGL.insertLabeledEdge gr0 (vs!!0) (vs!!0) 's'+          HGL.edges g @?= [e]++     , "haggle delete self-edge" ~:+       do let Just (_,g) = HGL.insertLabeledEdge gr0 (vs!!0) (vs!!0) 's'+          HGL.edges (HGL.deleteEdgesBetween g (vs!!0) (vs!!0)) @?= []+++     -- n.b. fgl's dominator is broken (as haggle's original version also was) in+     -- that its return includes (4, [1,2,4]), which is invalid: 4 is in an+     -- independent subgraph and cannot be dominated by 1.+     -- , "fgl dominator for [1-2,4] from 1" ~:+     --   do let fgr0 = FGL.mkGraph [(1,1), (2,2), (4,4)] [(0, 1, 'f')] :: FGL.Gr Int Char+     --      FGL.dom fgr0 1 @?= [ (1, [ 1 ])+     --                         , (2, [ 1, 2 ])+     --                         ]++     -- n.b. fgl's dominator is also broken in regards to reachability.  For a+     -- dom return for [2-1, 2-2] from 1 also returns (2, [1,2]) which is+     -- invalid, because 2 is not reachable from 1 and so 1 cannot be a dominator+     -- for 2+     -- , "fgl dominator for [2-1,2-2] from 1" ~:+     --   do let fgr2 = FGL.mkGraph [(1,1), (2,2)] [(2,1,'f'), (2,2,'s')] :: FGL.Gr Int Char+     --      FGL.dom fgr2 1 @?= [ (1, [ 1 ]) ]+     ]+ testPatricia :: [Test.Framework.Test] testPatricia =   let gr0 = foldl (\g -> snd . HGL.insertLabeledVertex g)@@ -199,4 +271,11 @@        do let gr2 = Bi.bimap (+2) (succ . succ) gr1           sum (snd <$> HGL.labeledVertices gr2) @?= 27           L.sort (snd <$> HGL.labeledEdges gr2) @?= "cde"++     , "replaceLabeledVertex" ~:+       do let gr2 = HGL.replaceLabeledVertex gr1 (vs !! 2) 11+          -- Vertex label changed?+          sum (snd <$> HGL.labeledVertices gr2) @?= (15 + (11 - 4))+          -- Edges are still in place?+          L.sort (snd <$> HGL.labeledEdges gr2) @?= "abc"      ]