packages feed

algebraic-graphs 0.3 → 0.4

raw patch · 28 files changed

+1675/−300 lines, 28 filesdep +bifunctorsdep ~QuickChecksetup-changedPVP ok

version bump matches the API change (PVP)

Dependencies added: bifunctors

Dependency ranges changed: QuickCheck

API changes (from Hackage documentation)

- Algebra.Graph.Relation.InternalDerived: SymmetricRelation :: Relation a -> SymmetricRelation a
- Algebra.Graph.Relation.InternalDerived: [fromSymmetric] :: SymmetricRelation a -> Relation a
- Algebra.Graph.Relation.InternalDerived: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.Relation.InternalDerived.SymmetricRelation a)
- Algebra.Graph.Relation.InternalDerived: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Relation.InternalDerived.SymmetricRelation a)
- Algebra.Graph.Relation.InternalDerived: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Relation.InternalDerived.SymmetricRelation a)
- Algebra.Graph.Relation.InternalDerived: instance GHC.Classes.Ord a => Algebra.Graph.Class.Graph (Algebra.Graph.Relation.InternalDerived.SymmetricRelation a)
- Algebra.Graph.Relation.InternalDerived: instance GHC.Classes.Ord a => Algebra.Graph.Class.Undirected (Algebra.Graph.Relation.InternalDerived.SymmetricRelation a)
- Algebra.Graph.Relation.InternalDerived: instance GHC.Classes.Ord a => GHC.Classes.Eq (Algebra.Graph.Relation.InternalDerived.SymmetricRelation a)
- Algebra.Graph.Relation.InternalDerived: newtype SymmetricRelation a
- Algebra.Graph.Relation.Symmetric: data SymmetricRelation a
- Algebra.Graph.Relation.Symmetric: fromRelation :: Relation a -> SymmetricRelation a
- Algebra.Graph.Relation.Symmetric: toRelation :: Ord a => SymmetricRelation a -> Relation a
+ Algebra.Graph: sparsifyKL :: Int -> Graph Int -> Graph
+ Algebra.Graph.AdjacencyIntMap.Internal: instance GHC.Generics.Generic Algebra.Graph.AdjacencyIntMap.Internal.AdjacencyIntMap
+ Algebra.Graph.AdjacencyMap.Internal: instance GHC.Generics.Generic (Algebra.Graph.AdjacencyMap.Internal.AdjacencyMap a)
+ Algebra.Graph.Class: instance GHC.Classes.Ord a => Algebra.Graph.Class.Graph (Algebra.Graph.Relation.Symmetric.Internal.Relation a)
+ Algebra.Graph.Class: instance GHC.Classes.Ord a => Algebra.Graph.Class.Undirected (Algebra.Graph.Relation.Symmetric.Internal.Relation a)
+ Algebra.Graph.Label: instance (GHC.Base.Monoid a, GHC.Classes.Ord a) => Algebra.Graph.Label.Dioid (Algebra.Graph.Label.Minimum a)
+ Algebra.Graph.Label: instance (GHC.Base.Monoid a, GHC.Classes.Ord a) => Algebra.Graph.Label.Semiring (Algebra.Graph.Label.Minimum a)
+ Algebra.Graph.Label: instance (GHC.Base.Monoid a, GHC.Classes.Ord a) => GHC.Base.Monoid (Algebra.Graph.Label.Minimum a)
+ Algebra.Graph.Label: instance GHC.Classes.Ord a => GHC.Base.Semigroup (Algebra.Graph.Label.Minimum a)
+ Algebra.Graph.Label: instance GHC.Show.Show a => GHC.Show.Show (Algebra.Graph.Label.PowerSet a)
+ Algebra.Graph.Labelled: instance Data.Bifunctor.Bifunctor Algebra.Graph.Labelled.Graph
+ Algebra.Graph.Labelled.AdjacencyMap.Internal: instance GHC.Generics.Generic (Algebra.Graph.Labelled.AdjacencyMap.Internal.AdjacencyMap e a)
+ Algebra.Graph.NonEmpty: sparsifyKL :: Int -> Graph Int -> Graph
+ Algebra.Graph.NonEmpty.AdjacencyMap.Internal: instance GHC.Generics.Generic (Algebra.Graph.NonEmpty.AdjacencyMap.Internal.AdjacencyMap a)
+ Algebra.Graph.Relation.Symmetric: adjacencyList :: Eq a => Relation a -> [(a, [a])]
+ Algebra.Graph.Relation.Symmetric: biclique :: Ord a => [a] -> [a] -> Relation a
+ Algebra.Graph.Relation.Symmetric: circuit :: Ord a => [a] -> Relation a
+ Algebra.Graph.Relation.Symmetric: clique :: Ord a => [a] -> Relation a
+ Algebra.Graph.Relation.Symmetric: connect :: Ord a => Relation a -> Relation a -> Relation a
+ Algebra.Graph.Relation.Symmetric: connects :: Ord a => [Relation a] -> Relation a
+ Algebra.Graph.Relation.Symmetric: data Relation a
+ Algebra.Graph.Relation.Symmetric: edge :: Ord a => a -> a -> Relation a
+ Algebra.Graph.Relation.Symmetric: edgeCount :: Ord a => Relation a -> Int
+ Algebra.Graph.Relation.Symmetric: edgeList :: Ord a => Relation a -> [(a, a)]
+ Algebra.Graph.Relation.Symmetric: edgeSet :: Ord a => Relation a -> Set (a, a)
+ Algebra.Graph.Relation.Symmetric: edges :: Ord a => [(a, a)] -> Relation a
+ Algebra.Graph.Relation.Symmetric: empty :: Relation a
+ Algebra.Graph.Relation.Symmetric: forest :: Ord a => Forest a -> Relation a
+ Algebra.Graph.Relation.Symmetric: fromSymmetric :: Relation a -> Relation a
+ Algebra.Graph.Relation.Symmetric: gmap :: Ord b => (a -> b) -> Relation a -> Relation b
+ Algebra.Graph.Relation.Symmetric: hasEdge :: Ord a => a -> a -> Relation a -> Bool
+ Algebra.Graph.Relation.Symmetric: hasVertex :: Ord a => a -> Relation a -> Bool
+ Algebra.Graph.Relation.Symmetric: induce :: (a -> Bool) -> Relation a -> Relation a
+ Algebra.Graph.Relation.Symmetric: isEmpty :: Relation a -> Bool
+ Algebra.Graph.Relation.Symmetric: isSubgraphOf :: Ord a => Relation a -> Relation a -> Bool
+ Algebra.Graph.Relation.Symmetric: mergeVertices :: Ord a => (a -> Bool) -> a -> Relation a -> Relation a
+ Algebra.Graph.Relation.Symmetric: overlay :: Ord a => Relation a -> Relation a -> Relation a
+ Algebra.Graph.Relation.Symmetric: overlays :: Ord a => [Relation a] -> Relation a
+ Algebra.Graph.Relation.Symmetric: path :: Ord a => [a] -> Relation a
+ Algebra.Graph.Relation.Symmetric: removeEdge :: Ord a => a -> a -> Relation a -> Relation a
+ Algebra.Graph.Relation.Symmetric: removeVertex :: Ord a => a -> Relation a -> Relation a
+ Algebra.Graph.Relation.Symmetric: replaceVertex :: Ord a => a -> a -> Relation a -> Relation a
+ Algebra.Graph.Relation.Symmetric: star :: Ord a => a -> [a] -> Relation a
+ Algebra.Graph.Relation.Symmetric: stars :: Ord a => [(a, [a])] -> Relation a
+ Algebra.Graph.Relation.Symmetric: toSymmetric :: Ord a => Relation a -> Relation a
+ Algebra.Graph.Relation.Symmetric: tree :: Ord a => Tree a -> Relation a
+ Algebra.Graph.Relation.Symmetric: vertex :: a -> Relation a
+ Algebra.Graph.Relation.Symmetric: vertexCount :: Relation a -> Int
+ Algebra.Graph.Relation.Symmetric: vertexList :: Relation a -> [a]
+ Algebra.Graph.Relation.Symmetric: vertexSet :: Relation a -> Set a
+ Algebra.Graph.Relation.Symmetric: vertices :: Ord a => [a] -> Relation a
+ Algebra.Graph.Relation.Symmetric.Internal: SR :: Relation a -> Relation a
+ Algebra.Graph.Relation.Symmetric.Internal: connect :: Ord a => Relation a -> Relation a -> Relation a
+ Algebra.Graph.Relation.Symmetric.Internal: consistent :: Ord a => Relation a -> Bool
+ Algebra.Graph.Relation.Symmetric.Internal: edgeSet :: Ord a => Relation a -> Set (a, a)
+ Algebra.Graph.Relation.Symmetric.Internal: empty :: Relation a
+ Algebra.Graph.Relation.Symmetric.Internal: fromSymmetric :: Relation a -> Relation a
+ Algebra.Graph.Relation.Symmetric.Internal: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.Relation.Symmetric.Internal.Relation a)
+ Algebra.Graph.Relation.Symmetric.Internal: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Relation.Symmetric.Internal.Relation a)
+ Algebra.Graph.Relation.Symmetric.Internal: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Relation.Symmetric.Internal.Relation a)
+ Algebra.Graph.Relation.Symmetric.Internal: instance GHC.Classes.Ord a => GHC.Classes.Eq (Algebra.Graph.Relation.Symmetric.Internal.Relation a)
+ Algebra.Graph.Relation.Symmetric.Internal: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Relation.Symmetric.Internal.Relation a)
+ Algebra.Graph.Relation.Symmetric.Internal: newtype Relation a
+ Algebra.Graph.Relation.Symmetric.Internal: overlay :: Ord a => Relation a -> Relation a -> Relation a
+ Algebra.Graph.Relation.Symmetric.Internal: vertex :: a -> Relation a
+ Algebra.Graph.ToGraph: instance GHC.Classes.Ord a => Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.Relation.Symmetric.Internal.Relation a)
- Algebra.Graph.Relation.Symmetric: neighbours :: Ord a => a -> SymmetricRelation a -> Set a
+ Algebra.Graph.Relation.Symmetric: neighbours :: Ord a => a -> Relation a -> Set a

Files

CHANGES.md view
@@ -1,78 +1,85 @@-# Change log
-
-## 0.3
-
-* #129: Add a testsuite for rewrite rules based on the `inspection-testing` library.
-* #63, #148: Add relational composition of algebraic graphs.
-* #139, #146: Add relational operations to adjacency maps.
-* #146: Rename `preorderClosure` to `closure`.
-* #146: Switch to left-to-right composition in `Relation.compose`.
-* #143: Allow newer QuickCheck.
-* #140, #142: Fix `Show` instances.
-* #128, #130: Modify the SCC algorithm to return non-empty graph components.
-* #130: Move adjacency map algorithms to separate modules.
-* #130: Export `fromAdjacencySets` and `fromAdjacencyIntSets`.
-* #138: Do not require `Eq` instance on the string type when exporting graphs.
-* #136: Rename `Algebra.Graph.NonEmpty.NonEmptyGraph` to `Algebra.Graph.NonEmpty.Graph`.
-* #136: Add `Algebra.Graph.NonEmpty.AdjacencyMap`.
-* #136: Remove `vertexIntSet` from the API of basic graph data types. Also
-        remove `Algebra.Graph.adjacencyMap` and `Algebra.Graph.adjacencyIntMap`.
-        This functionality is still available from the type class `ToGraph`.
-* #126, #131: Implement custom `Ord` instance.
-* #17, #122, #125, #149: Add labelled algebraic graphs.
-* #121: Drop `Foldable` and `Traversable` instances.
-* #113: Add `Labelled.AdjacencyMap`.
-
-## 0.2
-
-* #117: Add `sparsify`.
-* #115: Add `isDfsForestOf`.
-* #114: Add a basic implementation of edge-labelled graphs.
-* #107: Drop `starTranspose`.
-* #106: Extend `ToGraph` with algorithms based on adjacency maps.
-* #106: Add `isAcyclic` and `reachable`.
-* #106: Rename `isTopSort` to `isTopSortOf`.
-* #102: Switch the master branch to GHC 8.4.3. Add a CI instance for GHC 8.6.1.
-* #101: Drop `-O2` from the `ghc-options` section of the Cabal file.
-* #100: Rename `fromAdjacencyList` to `stars`.
-* #79: Improve the API consistency: rename `IntAdjacencyMap` to `AdjacencyIntMap`,
-       and then rename the function that extracts its adjacency map to
-       `adjacencyIntMap` to avoid the clash with `AdjacencyMap.adjacencyMap`,
-       which has incompatible type.
-* #82, #92: Add performance regression suite.
-* #76: Remove benchmarks.
-* #74: Drop dependency of `Algebra.Graph` on graph type classes.
-* #62: Move King-Launchbury graphs into `Data.Graph.Typed`.
-* #67, #68, #69, #77, #81, #93, #94, #97, #103, #110: Various performance improvements.
-* #66, #72, #96, #98: Add missing `NFData` instances.
-
-## 0.1.1.1
-
-* #59: Allow `base-compat-0.10`.
-
-## 0.1.1
-
-* #58: Update documentation.
-* #57: Allow newer QuickCheck.
-
-## 0.1.0
-
-* Start complying with PVP.
-* #48: Add `starTranspose`.
-* #48: Add `foldg` to `ToGraph`.
-* #15: Optimise `removeEdge`.
-* #39: Factor out difference lists into `Algebra.Graph.Internal`.
-* #31: Add `Algebra.Graph.NonEmpty`.
-* #32: Remove smart constructor `graph`.
-* #27, #55: Support GHC versions 7.8.4, 7.10.3, 8.0.2, 8.2.2, 8.4.1.
-* #25: Add `NFData Graph` instance.
-* General improvements to code, documentation and tests.
-
-## 0.0.5
-
-* Add `dfs`.
-* #19: Move `GraphKL` to an internal module.
-* #18: Add `dfsForestFrom`.
-* #16: Add support for graph export, in particular in DOT format.
-* Make API more consistent, e.g. rename `postset` to `postSet`.
-* Improve documentation and tests.
+# Change log++## 0.4++* #174: Add `Symmetric.Relation`.+* #143: Allow newer QuickCheck.+* #171: Implement sparsification for King-Launchbury graph representation.+* #178: Derive `Generic` for adjacency maps.++## 0.3++* #129: Add a testsuite for rewrite rules based on the `inspection-testing` library.+* #63, #148: Add relational composition of algebraic graphs.+* #139, #146: Add relational operations to adjacency maps.+* #146: Rename `preorderClosure` to `closure`.+* #146: Switch to left-to-right composition in `Relation.compose`.+* #143: Allow newer QuickCheck.+* #140, #142: Fix `Show` instances.+* #128, #130: Modify the SCC algorithm to return non-empty graph components.+* #130: Move adjacency map algorithms to separate modules.+* #130: Export `fromAdjacencySets` and `fromAdjacencyIntSets`.+* #138: Do not require `Eq` instance on the string type when exporting graphs.+* #136: Rename `Algebra.Graph.NonEmpty.NonEmptyGraph` to `Algebra.Graph.NonEmpty.Graph`.+* #136: Add `Algebra.Graph.NonEmpty.AdjacencyMap`.+* #136: Remove `vertexIntSet` from the API of basic graph data types. Also+        remove `Algebra.Graph.adjacencyMap` and `Algebra.Graph.adjacencyIntMap`.+        This functionality is still available from the type class `ToGraph`.+* #126, #131: Implement custom `Ord` instance.+* #17, #122, #125, #149: Add labelled algebraic graphs.+* #121: Drop `Foldable` and `Traversable` instances.+* #113: Add `Labelled.AdjacencyMap`.++## 0.2++* #117: Add `sparsify`.+* #115: Add `isDfsForestOf`.+* #114: Add a basic implementation of edge-labelled graphs.+* #107: Drop `starTranspose`.+* #106: Extend `ToGraph` with algorithms based on adjacency maps.+* #106: Add `isAcyclic` and `reachable`.+* #106: Rename `isTopSort` to `isTopSortOf`.+* #102: Switch the master branch to GHC 8.4.3. Add a CI instance for GHC 8.6.1.+* #101: Drop `-O2` from the `ghc-options` section of the Cabal file.+* #100: Rename `fromAdjacencyList` to `stars`.+* #79: Improve the API consistency: rename `IntAdjacencyMap` to `AdjacencyIntMap`,+       and then rename the function that extracts its adjacency map to+       `adjacencyIntMap` to avoid the clash with `AdjacencyMap.adjacencyMap`,+       which has incompatible type.+* #82, #92: Add performance regression suite.+* #76: Remove benchmarks.+* #74: Drop dependency of `Algebra.Graph` on graph type classes.+* #62: Move King-Launchbury graphs into `Data.Graph.Typed`.+* #67, #68, #69, #77, #81, #93, #94, #97, #103, #110: Various performance improvements.+* #66, #72, #96, #98: Add missing `NFData` instances.++## 0.1.1.1++* #59: Allow `base-compat-0.10`.++## 0.1.1++* #58: Update documentation.+* #57: Allow newer QuickCheck.++## 0.1.0++* Start complying with PVP.+* #48: Add `starTranspose`.+* #48: Add `foldg` to `ToGraph`.+* #15: Optimise `removeEdge`.+* #39: Factor out difference lists into `Algebra.Graph.Internal`.+* #31: Add `Algebra.Graph.NonEmpty`.+* #32: Remove smart constructor `graph`.+* #27, #55: Support GHC versions 7.8.4, 7.10.3, 8.0.2, 8.2.2, 8.4.1.+* #25: Add `NFData Graph` instance.+* General improvements to code, documentation and tests.++## 0.0.5++* Add `dfs`.+* #19: Move `GraphKL` to an internal module.+* #18: Add `dfsForestFrom`.+* #16: Add support for graph export, in particular in DOT format.+* Make API more consistent, e.g. rename `postset` to `postSet`.+* Improve documentation and tests.
Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple
-main = defaultMain
+import Distribution.Simple+main = defaultMain
algebraic-graphs.cabal view
@@ -1,22 +1,22 @@ name:          algebraic-graphs-version:       0.3+version:       0.4 synopsis:      A library for algebraic graph construction and transformation license:       MIT license-file:  LICENSE author:        Andrey Mokhov <andrey.mokhov@gmail.com>, github: @snowleopard maintainer:    Andrey Mokhov <andrey.mokhov@gmail.com>, github: @snowleopard,                Alexandre Moine <alexandre@moine.me>, github: @nobrakal-copyright:     Andrey Mokhov, 2016-2018+copyright:     Andrey Mokhov, 2016-2019 homepage:      https://github.com/snowleopard/alga category:      Algebra, Algorithms, Data Structures, Graphs build-type:    Simple-cabal-version: >=1.18+cabal-version: 1.18 tested-with:   GHC==7.8.4,                GHC==7.10.3,                GHC==8.0.2,                GHC==8.2.2,                GHC==8.4.3,-               GHC==8.6.1+               GHC==8.6.4 stability:     experimental description:     <https://github.com/snowleopard/alga Alga> is a library for algebraic construction and@@ -96,6 +96,7 @@                         Algebra.Graph.Relation.Preorder,                         Algebra.Graph.Relation.Reflexive,                         Algebra.Graph.Relation.Symmetric,+                        Algebra.Graph.Relation.Symmetric.Internal,                         Algebra.Graph.Relation.Transitive,                         Algebra.Graph.ToGraph,                         Data.Graph.Typed@@ -107,6 +108,8 @@                         mtl         >= 2.1     && < 2.3     if !impl(ghc >= 8.0)         build-depends:  semigroups  >= 0.18.3  && < 0.18.4+    if !impl(ghc >= 7.10)+        build-depends:  bifunctors  >= 5       && < 5.6     default-language:   Haskell2010     default-extensions: FlexibleContexts                         FlexibleInstances@@ -146,6 +149,7 @@                         Algebra.Graph.Test.NonEmpty.AdjacencyMap,                         Algebra.Graph.Test.NonEmpty.Graph,                         Algebra.Graph.Test.Relation,+                        Algebra.Graph.Test.Relation.SymmetricRelation,                         Data.Graph.Test.Typed     if impl(ghc >= 8.0.2)         other-modules:  Algebra.Graph.Test.RewriteRules@@ -156,7 +160,7 @@                         base-orphans >= 0.5.4   && < 0.9,                         containers   >= 0.5.5.1 && < 0.8,                         extra        >= 1.5     && < 2,-                        QuickCheck   >= 2.9     && < 2.13+                        QuickCheck   >= 2.9     && < 2.14     if !impl(ghc >= 8.0)         build-depends:  semigroups   >= 0.18.3  && < 0.18.4     if impl(ghc >= 8.0.2)
src/Algebra/Graph.hs view
@@ -41,7 +41,7 @@      -- * Graph transformation     removeVertex, removeEdge, replaceVertex, mergeVertices, splitVertex,-    transpose, induce, simplify, sparsify,+    transpose, induce, simplify, sparsify, sparsifyKL,      -- * Graph composition     compose, box,@@ -51,7 +51,7 @@     ) where  import Prelude ()-import Prelude.Compat hiding ((<>))+import Prelude.Compat  import Control.Applicative (Alternative) import Control.DeepSeq (NFData (..))@@ -59,7 +59,7 @@ import Control.Monad.State (runState, get, put) import Data.Foldable (toList) import Data.Maybe (fromMaybe)-import Data.Monoid ((<>))+import Data.Semigroup ((<>)) import Data.Tree  import Algebra.Graph.Internal@@ -67,9 +67,11 @@ import qualified Algebra.Graph.AdjacencyMap    as AM import qualified Algebra.Graph.AdjacencyIntMap as AIM import qualified Control.Applicative           as Ap+import qualified Data.Graph                    as KL import qualified Data.IntSet                   as IntSet import qualified Data.Set                      as Set import qualified Data.Tree                     as Tree+import qualified GHC.Exts                      as Exts  {-| The 'Graph' data type is a deep embedding of the core graph construction primitives 'empty', 'vertex', 'overlay' and 'connect'. We define a 'Num'@@ -212,10 +214,9 @@ instance Functor Graph where     fmap = fmapR --- This is a usual implementation of 'fmap', but with custom rewrite rules. fmapR :: (a -> b) -> Graph a -> Graph b-fmapR f = foldg empty (vertex . f) overlay connect-{-# INLINE [0] fmapR #-}+fmapR f g = bindR g (vertex . f)+{-# INLINE fmapR #-}  instance NFData a => NFData (Graph a) where     rnf Empty         = ()@@ -263,12 +264,20 @@  instance Applicative Graph where     pure  = Vertex-    (<*>) = ap+    (<*>) = apR +apR :: Graph (a -> b) -> Graph a -> Graph b+apR f x = bindR f (<$> x)+{-# INLINE apR #-}+ instance Monad Graph where-    return  = pure-    g >>= f = foldg Empty f Overlay Connect g+    return = pure+    (>>=)  = bindR +bindR :: Graph a -> (a -> Graph b) -> Graph b+bindR g f = foldg Empty f Overlay Connect g+{-# INLINE [0] bindR #-}+ instance Alternative Graph where     empty = Empty     (<|>) = Overlay@@ -471,9 +480,14 @@ -- isSubgraphOf x y                         ==> x <= y -- @ isSubgraphOf :: Ord a => Graph a -> Graph a -> Bool-isSubgraphOf x y = overlay x y == y-{-# SPECIALISE isSubgraphOf :: Graph Int -> Graph Int -> Bool #-}+isSubgraphOf x y = AM.isSubgraphOf (toAdjacencyMap x) (toAdjacencyMap y)+{-# NOINLINE [1] isSubgraphOf #-}+{-# RULES "isSubgraphOf/Int" isSubgraphOf = isSubgraphOfIntR #-} +-- Like 'isSubgraphOf' but specialised for graphs with vertices of type 'Int'.+isSubgraphOfIntR :: Graph Int -> Graph Int -> Bool+isSubgraphOfIntR x y = AIM.isSubgraphOf (toAdjacencyIntMap x) (toAdjacencyIntMap y)+ -- | Structural equality on graph expressions. -- Complexity: /O(s)/ time. --@@ -1089,12 +1103,10 @@ -- 'edgeCount'   (box x y) <= 'vertexCount' x * 'edgeCount' y + 'edgeCount' x * 'vertexCount' y -- @ box :: Graph a -> Graph b -> Graph (a, b)-box x y = overlays $ xs ++ ys+box x y = overlay (fx <*> y) (fy <*> x)   where-    xs = map (\b -> fmap (,b) x) $ toList $ toListGr y-    ys = map (\a -> fmap (a,) y) $ toList $ toListGr x-    toListGr :: Graph a -> List a-    toListGr = foldg mempty pure (<>) (<>)+    fx = foldg empty (vertex .      (,)) overlay overlay x+    fy = foldg empty (vertex . flip (,)) overlay overlay y  -- | /Sparsify/ a graph by adding intermediate 'Left' @Int@ vertices between the -- original vertices (wrapping the latter in 'Right') such that the resulting@@ -1123,6 +1135,35 @@         put (m + 1)         overlay <$> s `x` m <*> m `y` t +-- | Sparsify a graph whose vertices are integers in the range @[1..n]@, where+-- @n@ is the first argument of the function, producing an array-based graph+-- representation from "Data.Graph" (introduced by King and Launchbury, hence+-- the name of the function). In the resulting graph, vertices @[1..n]@+-- correspond to the original vertices, and all vertices greater than @n@ are+-- introduced by the sparsification procedure.+--+-- Complexity: /O(s)/ time and memory. Note that thanks to sparsification, the+-- resulting graph has a linear number of edges with respect to the size of the+-- original algebraic representation even though the latter can potentially+-- contain a quadratic /O(s^2)/ number of edges.+--+-- @+-- 'Data.List.sort' . 'Algebra.Graph.ToGraph.reachable' k                 == 'Data.List.sort' . 'filter' (<= n) . 'flip' 'Data.Graph.reachable' k . sparsifyKL n+-- 'length' ('Data.Graph.vertices' $ sparsifyKL n x) <= 'vertexCount' x + 'size' x + 1+-- 'length' ('Data.Graph.edges'    $ sparsifyKL n x) <= 3 * 'size' x+-- @+sparsifyKL :: Int -> Graph Int -> KL.Graph+sparsifyKL n graph = KL.buildG (1, next - 1) ((n + 1, n + 2) : Exts.toList (res :: List KL.Edge))+  where+    (res, next) = runState (foldg e v o c graph (n + 1) (n + 2)) (n + 3)+    e     _ _   = return $ Exts.fromList []+    v x   s t   = return $ Exts.fromList [(s,x), (x,t)]+    o x y s t   = (<>) <$> s `x` t <*> s `y` t+    c x y s t   = do+        m <- get+        put (m + 1)+        (\xs ys -> Exts.fromList [(s,m), (m,t)] <> xs <> ys) <$> s `x` m <*> m `y` t+ {- Note [The rules of foldg] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1144,7 +1185,7 @@   a "buildR/f" rule. These functions are higher-order functions and therefore   benefit from inlining in the final phase. -* The "fmapR/fmapR" rule optimises compositions of multiple fmapR's.+* The "bindR/bindR" rule optimises compositions of multiple bindR's. -}  type Foldg a = forall b. b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> b@@ -1163,8 +1204,8 @@  -- These rules transform functions into their buildR equivalents. {-# RULES-"buildR/fmapR" forall f g.-    fmapR f g = buildR (\e v o c -> foldg e (composeR v f) o c g)+"buildR/bindR" forall f g.+    bindR g f = buildR (\e v o c -> foldg e (composeR (foldg e v o c) f) o c g)  "buildR/induce" [~1] forall p g.     induce p g = buildR (\e v o c -> foldg e (matchR e v p) o c g)@@ -1185,10 +1226,14 @@ "foldg/buildR" forall e v o c (g :: Foldg a).     foldg e v o c (buildR g) = g e v o c --- Fuse composeR's. This occurs when two adjacent 'fmapR' were rewritted into+-- Fuse composeR's. This occurs when two adjacent 'bindR' were rewritted into -- their buildR form.-"fmapR/fmapR" forall c f g.+"bindR/bindR" forall c f g.     composeR (composeR c f) g = composeR c (f.g)++-- Rewrite identity (which can appear in the rewriting of bindR) to a much efficient one+"foldg/id"+    foldg Empty Vertex Overlay Connect = id  #-}  -- Eliminate remaining rewrite-only functions.
src/Algebra/Graph/AdjacencyIntMap/Internal.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DeriveGeneric #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.AdjacencyIntMap.Internal@@ -23,6 +24,7 @@ import Data.IntMap.Strict (IntMap, keysSet, fromSet) import Data.IntSet (IntSet) import Data.List+import GHC.Generics  import Control.DeepSeq (NFData (..)) @@ -133,7 +135,7 @@     -- adjacencyIntMap ('Algebra.Graph.AdjacencyIntMap.edge' 1 1) == IntMap.'IntMap.singleton' 1 (IntSet.'IntSet.singleton' 1)     -- adjacencyIntMap ('Algebra.Graph.AdjacencyIntMap.edge' 1 2) == IntMap.'IntMap.fromList' [(1,IntSet.'IntSet.singleton' 2), (2,IntSet.'IntSet.empty')]     -- @-    adjacencyIntMap :: IntMap IntSet } deriving Eq+    adjacencyIntMap :: IntMap IntSet } deriving (Eq, Generic)  instance Show AdjacencyIntMap where     showsPrec p (AM m)
src/Algebra/Graph/AdjacencyMap/Internal.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DeriveGeneric #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.AdjacencyMap.Internal@@ -24,6 +25,7 @@ import Data.Map.Strict (Map, keysSet, fromSet) import Data.Monoid import Data.Set (Set)+import GHC.Generics  import qualified Data.Map.Strict as Map import qualified Data.Set        as Set@@ -132,7 +134,7 @@     -- adjacencyMap ('Algebra.Graph.AdjacencyMap.edge' 1 1) == Map.'Map.singleton' 1 (Set.'Set.singleton' 1)     -- adjacencyMap ('Algebra.Graph.AdjacencyMap.edge' 1 2) == Map.'Map.fromList' [(1,Set.'Set.singleton' 2), (2,Set.'Set.empty')]     -- @-    adjacencyMap :: Map a (Set a) } deriving Eq+    adjacencyMap :: Map a (Set a) } deriving (Eq, Generic)  instance Ord a => Ord (AdjacencyMap a) where     compare (AM x) (AM y) = mconcat
src/Algebra/Graph/Class.hs view
@@ -1,7 +1,7 @@ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Class--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -61,6 +61,7 @@ import qualified Algebra.Graph.Fold                  as F import qualified Algebra.Graph.AdjacencyIntMap       as AIM import qualified Algebra.Graph.Relation              as R+import qualified Algebra.Graph.Relation.Symmetric    as RS  {-| The core type class for constructing algebraic graphs, characterised by the@@ -169,6 +170,15 @@     vertex  = R.vertex     overlay = R.overlay     connect = R.connect++instance Ord a => Graph (RS.Relation a) where+    type Vertex (RS.Relation a) = a+    empty   = RS.empty+    vertex  = RS.vertex+    overlay = RS.overlay+    connect = RS.connect++instance Ord a => Undirected (RS.Relation a)  {-| The class of /undirected graphs/ that satisfy the following additional axiom.
src/Algebra/Graph/Label.hs view
@@ -322,6 +322,18 @@ noMinimum :: Minimum a noMinimum = Minimum Infinite +instance Ord a => Semigroup (Minimum a) where+    (<>) = liftA2 min++instance (Monoid a, Ord a) => Monoid (Minimum a) where+    mempty = pure mempty ++instance (Monoid a, Ord a) => Semiring (Minimum a) where+    one = noMinimum+    (<.>) = liftA2 mappend++instance (Monoid a, Ord a) => Dioid (Minimum a)+ instance (Num a, Show a) => Show (Minimum a) where     show (Minimum Infinite  ) = "one"     show (Minimum (Finite x)) = show x@@ -343,7 +355,7 @@ -- x '<.>' y = PowerSet $ 'setProductWith' 'mappend' (getPowerSet x) (getPowerSet y) -- @ newtype PowerSet a = PowerSet { getPowerSet :: Set a }-    deriving (Eq, Monoid, Ord, Semigroup)+    deriving (Eq, Monoid, Ord, Semigroup, Show)  instance (Monoid a, Ord a) => Semiring (PowerSet a) where     one                       = PowerSet (Set.singleton mempty)
src/Algebra/Graph/Labelled.hs view
@@ -46,6 +46,7 @@ import Prelude () import Prelude.Compat +import Data.Bifunctor import Data.Monoid (Any (..)) import Data.Semigroup ((<>)) @@ -81,6 +82,9 @@     signum      = const empty     abs         = id     negate      = id++instance Bifunctor Graph where+  bimap f g = foldg Empty (Vertex . g) (Connect . f)  -- TODO: This is a very inefficient implementation. Find a way to construct an -- adjacency map directly, without building intermediate representations for all
src/Algebra/Graph/Labelled/AdjacencyMap/Internal.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DeriveGeneric #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Labelled.AdjdacencyMap.Internal@@ -22,6 +23,7 @@ import Data.Map.Strict (Map) import Data.Monoid (Monoid, getSum, Sum (..)) import Data.Set (Set, (\\))+import GHC.Generics  import qualified Data.Map.Strict as Map import qualified Data.Set        as Set@@ -37,7 +39,7 @@     -- | The /adjacency map/ of an edge-labelled graph: each vertex is     -- associated with a map from its direct successors to the corresponding     -- edge labels.-    adjacencyMap :: Map a (Map a e) } deriving (Eq, NFData)+    adjacencyMap :: Map a (Map a e) } deriving (Eq, Generic, NFData)  instance (Ord a, Show a, Ord e, Show e) => Show (AdjacencyMap e a) where     showsPrec p (AM m)
src/Algebra/Graph/NonEmpty.hs view
@@ -47,7 +47,7 @@      -- * Graph transformation     removeVertex1, removeEdge, replaceVertex, mergeVertices, splitVertex1,-    transpose, induce1, simplify, sparsify,+    transpose, induce1, simplify, sparsify, sparsifyKL,      -- * Graph composition     box@@ -56,23 +56,24 @@ import Prelude () import Prelude.Compat -#if !MIN_VERSION_base(4,11,0)-import Data.Semigroup-#endif- import Control.DeepSeq import Control.Monad.Compat import Control.Monad.State import Data.List.NonEmpty (NonEmpty (..))+import Data.Semigroup ((<>))  import Algebra.Graph.Internal -import qualified Algebra.Graph         as G-import qualified Algebra.Graph.ToGraph as T-import qualified Data.IntSet           as IntSet-import qualified Data.List.NonEmpty    as NonEmpty-import qualified Data.Set              as Set-import qualified Data.Tree             as Tree+import qualified Algebra.Graph                 as G+import qualified Algebra.Graph.ToGraph         as T+import qualified Algebra.Graph.AdjacencyMap    as AM+import qualified Algebra.Graph.AdjacencyIntMap as AIM+import qualified Data.Graph                    as KL+import qualified Data.IntSet                   as IntSet+import qualified Data.List.NonEmpty            as NonEmpty+import qualified Data.Set                      as Set+import qualified Data.Tree                     as Tree+import qualified GHC.Exts                      as Exts  {-| Non-empty algebraic graphs, which are constructed using three primitives: 'vertex', 'overlay' and 'connect'. See module "Algebra.Graph" for algebraic@@ -216,8 +217,8 @@ ordInt x y = compare (T.toAdjacencyIntMap x) (T.toAdjacencyIntMap y)  instance Applicative Graph where-    pure  = Vertex-    (<*>) = ap+    pure    = Vertex+    f <*> x = f >>= (<$> x)  instance Monad Graph where     return  = pure@@ -402,9 +403,14 @@ -- isSubgraphOf x y                         ==> x <= y -- @ isSubgraphOf :: Ord a => Graph a -> Graph a -> Bool-isSubgraphOf x y = overlay x y == y-{-# SPECIALISE isSubgraphOf :: Graph Int -> Graph Int -> Bool #-}+isSubgraphOf x y = AM.isSubgraphOf (T.toAdjacencyMap x) (T.toAdjacencyMap y)+{-# NOINLINE [1] isSubgraphOf #-}+{-# RULES "isSubgraphOf/Int" isSubgraphOf = isSubgraphOfIntR #-} +-- Like 'isSubgraphOf' but specialised for graphs with vertices of type 'Int'.+isSubgraphOfIntR :: Graph Int -> Graph Int -> Bool+isSubgraphOfIntR x y = AIM.isSubgraphOf (T.toAdjacencyIntMap x) (T.toAdjacencyIntMap y)+ -- | Structural equality on graph expressions. -- Complexity: /O(s)/ time. --@@ -898,13 +904,10 @@ -- 'edgeCount'   (box x y) <= 'vertexCount' x * 'edgeCount' y + 'edgeCount' x * 'vertexCount' y -- @ box :: Graph a -> Graph b -> Graph (a, b)-box x y = overlays1 xs `overlay` overlays1 ys+box x y = overlay (fx <*> y) (fy <*> x)   where-    xs = fmap (\b -> fmap (,b) x) $ toNonEmptyList y-    ys = fmap (\a -> fmap (a,) y) $ toNonEmptyList x--toNonEmptyList :: Graph a -> NonEmpty a-toNonEmptyList = foldg1 (:| []) (<>) (<>)+    fx = foldg1 (vertex .      (,)) overlay overlay x+    fy = foldg1 (vertex . flip (,)) overlay overlay y  -- | /Sparsify/ a graph by adding intermediate 'Left' @Int@ vertices between the -- original vertices (wrapping the latter in 'Right') such that the resulting@@ -931,3 +934,31 @@         m <- get         put (m + 1)         overlay <$> s `x` m <*> m `y` t++-- | Sparsify a graph whose vertices are integers in the range @[1..n]@, where+-- @n@ is the first argument of the function, producing an array-based graph+-- representation from "Data.Graph" (introduced by King and Launchbury, hence+-- the name of the function). In the resulting graph, vertices @[1..n]@+-- correspond to the original vertices, and all vertices greater than @n@ are+-- introduced by the sparsification procedure.+--+-- Complexity: /O(s)/ time and memory. Note that thanks to sparsification, the+-- resulting graph has a linear number of edges with respect to the size of the+-- original algebraic representation even though the latter can potentially+-- contain a quadratic /O(s^2)/ number of edges.+--+-- @+-- 'Data.List.sort' . 'Algebra.Graph.ToGraph.reachable' k                 == 'Data.List.sort' . 'filter' (<= n) . 'flip' 'Data.Graph.reachable' k . sparsifyKL n+-- 'length' ('Data.Graph.vertices' $ sparsifyKL n x) <= 'vertexCount' x + 'size' x + 1+-- 'length' ('Data.Graph.edges'    $ sparsifyKL n x) <= 3 * 'size' x+-- @+sparsifyKL :: Int -> Graph Int -> KL.Graph+sparsifyKL n graph = KL.buildG (1, next - 1) ((n + 1, n + 2) : Exts.toList (res :: List KL.Edge))+  where+    (res, next) = runState (foldg1 v o c graph (n + 1) (n + 2)) (n + 3)+    v x   s t   = return $ Exts.fromList [(s,x), (x,t)]+    o x y s t   = (<>) <$> s `x` t <*> s `y` t+    c x y s t   = do+        m <- get+        put (m + 1)+        (\xs ys -> Exts.fromList [(s,m), (m,t)] <> xs <> ys) <$> s `x` m <*> m `y` t
src/Algebra/Graph/NonEmpty/AdjacencyMap/Internal.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DeriveGeneric #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.NonEmpty.AdjacencyMap.Internal@@ -17,6 +18,7 @@  import Control.DeepSeq import Data.List+import GHC.Generics  import qualified Algebra.Graph.AdjacencyMap          as AM import qualified Algebra.Graph.AdjacencyMap.Internal as AM@@ -115,7 +117,7 @@     -- adjacencyMap ('Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 1) == Map.'Map.singleton' 1 (Set.'Set.singleton' 1)     -- adjacencyMap ('Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 2) == Map.'Map.fromList' [(1,Set.'Set.singleton' 2), (2,Set.'Set.empty')]     -- @-    am :: AM.AdjacencyMap a } deriving (Eq, NFData, Ord)+    am :: AM.AdjacencyMap a } deriving (Eq, Generic, NFData, Ord)  -- | __Note:__ this does not satisfy the usual ring laws; see 'AdjacencyMap' for -- more details.
src/Algebra/Graph/Relation/Internal.hs view
@@ -1,8 +1,7 @@-{-# LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Relation.Internal--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : unstable@@ -102,26 +101,27 @@ Here are a few examples:  @'vertex' 1 < 'vertex' 2-'vertex' 3 < 'Algebra.Graph.AdjacencyMap.edge' 1 2-'vertex' 1 < 'Algebra.Graph.AdjacencyMap.edge' 1 1-'Algebra.Graph.AdjacencyMap.edge' 1 1 < 'Algebra.Graph.AdjacencyMap.edge' 1 2-'Algebra.Graph.AdjacencyMap.edge' 1 2 < 'Algebra.Graph.AdjacencyMap.edge' 1 1 + 'Algebra.Graph.AdjacencyMap.edge' 2 2-'Algebra.Graph.AdjacencyMap.edge' 1 2 < 'Algebra.Graph.AdjacencyMap.edge' 1 3@+'vertex' 3 < 'Algebra.Graph.Relation.edge' 1 2+'vertex' 1 < 'Algebra.Graph.Relation.edge' 1 1+'Algebra.Graph.Relation.edge' 1 1 < 'Algebra.Graph.Relation.edge' 1 2+'Algebra.Graph.Relation.edge' 1 2 < 'Algebra.Graph.Relation.edge' 1 1 + 'Algebra.Graph.Relation.edge' 2 2+'Algebra.Graph.Relation.edge' 1 2 < 'Algebra.Graph.Relation.edge' 1 3@ -Note that the resulting order refines the 'isSubgraphOf' relation and is-compatible with 'overlay' and 'connect' operations:+Note that the resulting order refines the+'Algebra.Graph.Relation.isSubgraphOf' relation and is compatible with+'overlay' and 'connect' operations: -@'Algebra.Graph.AdjacencyMap.isSubgraphOf' x y ==> x <= y@+@'Algebra.Graph.Relation.isSubgraphOf' x y ==> x <= y@  @'empty' <= x x     <= x + y x + y <= x * y@ -} data Relation a = Relation {-    -- | The /domain/ of the relation.+    -- | The /domain/ of the relation. Complexity: /O(1)/ time and memory.     domain :: Set a,     -- | The set of pairs of elements that are /related/. It is guaranteed that-    -- each element belongs to the domain.+    -- each element belongs to the domain. Complexity: /O(1)/ time and memory.     relation :: Set (a, a)   } deriving Eq 
src/Algebra/Graph/Relation/InternalDerived.hs view
@@ -1,7 +1,7 @@ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Relation.InternalDerived--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : unstable@@ -14,16 +14,13 @@ ----------------------------------------------------------------------------- module Algebra.Graph.Relation.InternalDerived (     -- * Implementation of derived binary relations-    ReflexiveRelation (..), SymmetricRelation (..), TransitiveRelation (..),-    PreorderRelation (..)+    ReflexiveRelation (..), TransitiveRelation (..), PreorderRelation (..)   ) where - import Control.DeepSeq (NFData (..))  import Algebra.Graph.Class-import Algebra.Graph.Relation (Relation, reflexiveClosure, symmetricClosure,-                               transitiveClosure, closure)+import Algebra.Graph.Relation (Relation, reflexiveClosure, transitiveClosure, closure)  {-| The 'ReflexiveRelation' data type represents a /reflexive binary relation/ over a set of elements. Reflexive relations satisfy all laws of the@@ -54,38 +51,6 @@  instance Ord a => Reflexive (ReflexiveRelation a) --- TODO: Optimise the implementation by caching the results of symmetric closure.-{-|  The 'SymmetricRelation' data type represents a /symmetric binary relation/-over a set of elements. Symmetric relations satisfy all laws of the-'Undirected' type class and, in particular, the-commutativity of connect:--@'connect' x y == 'connect' y x@--The 'Show' instance produces symmetrically closed expressions:--@show (1     :: SymmetricRelation Int) == "vertex 1"-show (1 * 2 :: SymmetricRelation Int) == "edges [(1,2),(2,1)]"@--}-newtype SymmetricRelation a = SymmetricRelation { fromSymmetric :: Relation a }-    deriving (Num, NFData)--instance Ord a => Eq (SymmetricRelation a) where-    x == y = symmetricClosure (fromSymmetric x) == symmetricClosure (fromSymmetric y)--instance (Ord a, Show a) => Show (SymmetricRelation a) where-    show = show . symmetricClosure . fromSymmetric---- TODO: To be derived automatically using GeneralizedNewtypeDeriving in GHC 8.2-instance Ord a => Graph (SymmetricRelation a) where-    type Vertex (SymmetricRelation a) = a-    empty       = SymmetricRelation empty-    vertex      = SymmetricRelation . vertex-    overlay x y = SymmetricRelation $ fromSymmetric x `overlay` fromSymmetric y-    connect x y = SymmetricRelation $ fromSymmetric x `connect` fromSymmetric y--instance Ord a => Undirected (SymmetricRelation a)- -- TODO: Optimise the implementation by caching the results of transitive closure. {-| The 'TransitiveRelation' data type represents a /transitive binary relation/ over a set of elements. Transitive relations satisfy all laws of the@@ -162,3 +127,4 @@ instance Ord a => Reflexive  (PreorderRelation a) instance Ord a => Transitive (PreorderRelation a) instance Ord a => Preorder   (PreorderRelation a)+
src/Algebra/Graph/Relation/Symmetric.hs view
@@ -1,37 +1,462 @@ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Relation.Symmetric--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental ----- An abstract implementation of symmetric binary relations. Use--- "Algebra.Graph.Class" for polymorphic construction and manipulation.+-- An abstract implementation of symmetric binary relations. To avoid name+-- clashes with "Algebra.Graph.Relation", this module can be imported qualified:+--+-- @+-- import qualified Algebra.Graph.Relation.Symmetric as Symmetric+-- @+--+-- 'Relation' is an instance of the 'Algebra.Graph.Class.Graph' type+-- class, which can be used for polymorphic graph construction and manipulation. ----------------------------------------------------------------------------- module Algebra.Graph.Relation.Symmetric (     -- * Data structure-    SymmetricRelation, fromRelation, toRelation,+    Relation, toSymmetric, fromSymmetric, +    -- * Basic graph construction primitives+    empty, vertex, edge, overlay, connect, vertices, edges, overlays, connects,++    -- * Relations on graphs+    isSubgraphOf,+     -- * Graph properties-    neighbours+    isEmpty, hasVertex, hasEdge, vertexCount, edgeCount, vertexList, edgeList,+    adjacencyList, vertexSet, edgeSet, neighbours,++    -- * Standard families of graphs+    path, circuit, clique, biclique, star, stars, tree, forest,++    -- * Graph transformation+    removeVertex, removeEdge, replaceVertex, mergeVertices, gmap, induce,   ) where -import Algebra.Graph.Relation-import Algebra.Graph.Relation.InternalDerived+import Algebra.Graph.Relation.Symmetric.Internal+import Data.Set (Set)+import Data.Tree+import Data.Tuple  import qualified Data.Set as Set --- | Construct a symmetric relation from a 'Relation'.--- Complexity: /O(1)/ time.-fromRelation :: Relation a -> SymmetricRelation a-fromRelation = SymmetricRelation+import qualified Algebra.Graph.Relation          as R+import qualified Algebra.Graph.Relation.Internal as RI --- | Extract the underlying relation.+-- | Construct a symmetric relation from a given "Algebra.Graph.Relation". -- Complexity: /O(m*log(m))/ time.-toRelation :: Ord a => SymmetricRelation a -> Relation a-toRelation = symmetricClosure . fromSymmetric+--+-- @+-- toSymmetric ('Algebra.Graph.Relation.edge' 1 2)         == 'edge' 1 2+-- toSymmetric . 'fromSymmetric'    == id+-- 'fromSymmetric'    . toSymmetric == 'Algebra.Graph.Relation.symmetricClosure'+-- 'vertexCount'      . toSymmetric == 'Algebra.Graph.Relation.vertexCount'+-- (*2) . 'edgeCount' . toSymmetric >= 'Algebra.Graph.Relation.edgeCount'+-- @+toSymmetric :: Ord a => R.Relation a -> Relation a+toSymmetric = SR . R.symmetricClosure +-- | Construct the graph comprising /a single edge/.+-- Complexity: /O(1)/ time, memory and size.+--+-- @+-- edge x y               == 'connect' ('vertex' x) ('vertex' y)+-- edge x y               == 'edge' y x+-- edge x y               == 'edges' [(x,y), (y,x)]+-- 'hasEdge' x y (edge x y) == True+-- 'edgeCount'   (edge x y) == 1+-- 'vertexCount' (edge 1 1) == 1+-- 'vertexCount' (edge 1 2) == 2+-- @+edge :: Ord a => a -> a -> Relation a+edge x y = SR $ RI.Relation (Set.fromList [x, y]) (Set.fromList [(x,y), (y,x)])++-- | Construct the graph comprising a given list of isolated vertices.+-- Complexity: /O(L * log(L))/ time and /O(L)/ memory, where /L/ is the length+-- of the given list.+--+-- @+-- vertices []            == 'empty'+-- vertices [x]           == 'vertex' x+-- 'hasVertex' x . vertices == 'elem' x+-- 'vertexCount' . vertices == 'length' . 'Data.List.nub'+-- 'vertexSet'   . vertices == Set.'Set.fromList'+-- @+vertices :: Ord a => [a] -> Relation a+vertices = SR . R.vertices++-- TODO: Optimise by avoiding multiple list traversal.+-- | Construct the graph from a list of edges.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- edges []             == 'empty'+-- edges [(x,y)]        == 'edge' x y+-- edges [(x,y), (y,x)] == 'edge' x y+-- @+edges :: Ord a => [(a, a)] -> Relation a+edges es = SR $ RI.Relation+    (Set.fromList $ uncurry (++) $ unzip es) (Set.fromList (es ++ map swap es))++-- | Overlay a given list of graphs.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- overlays []        == 'empty'+-- overlays [x]       == x+-- overlays [x,y]     == 'overlay' x y+-- overlays           == 'foldr' 'overlay' 'empty'+-- 'isEmpty' . overlays == 'all' 'isEmpty'+-- @+overlays :: Ord a => [Relation a] -> Relation a+overlays = SR . R.overlays . map fromSymmetric++-- | Connect a given list of graphs.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- connects []        == 'empty'+-- connects [x]       == x+-- connects [x,y]     == 'connect' x y+-- connects           == 'foldr' 'connect' 'empty'+-- 'isEmpty' . connects == 'all' 'isEmpty'+-- connects           == connects . 'reverse'+-- @+connects :: Ord a => [Relation a] -> Relation a+connects = foldr connect empty++-- | The 'isSubgraphOf' function takes two graphs and returns 'True' if the+-- first graph is a /subgraph/ of the second.+-- Complexity: /O((n + m) * log(n))/ time.+--+-- @+-- isSubgraphOf 'empty'         x             ==  True+-- isSubgraphOf ('vertex' x)    'empty'         ==  False+-- isSubgraphOf x             ('overlay' x y) ==  True+-- isSubgraphOf ('overlay' x y) ('connect' x y) ==  True+-- isSubgraphOf ('path' xs)     ('circuit' xs)  ==  True+-- isSubgraphOf ('edge' x y)    ('edge' y x)    ==  True+-- isSubgraphOf x y                         ==> x <= y+-- @+isSubgraphOf :: Ord a => Relation a -> Relation a -> Bool+isSubgraphOf x y = R.isSubgraphOf (fromSymmetric x) (fromSymmetric y)++-- | Check if a relation is empty.+-- Complexity: /O(1)/ time.+--+-- @+-- isEmpty 'empty'                       == True+-- isEmpty ('overlay' 'empty' 'empty')       == True+-- isEmpty ('vertex' x)                  == False+-- isEmpty ('removeVertex' x $ 'vertex' x) == True+-- isEmpty ('removeEdge' x y $ 'edge' x y) == False+-- @+isEmpty :: Relation a -> Bool+isEmpty = R.isEmpty . fromSymmetric++-- | Check if a graph contains a given vertex.+-- Complexity: /O(log(n))/ time.+--+-- @+-- hasVertex x 'empty'            == False+-- hasVertex x ('vertex' x)       == True+-- hasVertex 1 ('vertex' 2)       == False+-- hasVertex x . 'removeVertex' x == 'const' False+-- @+hasVertex :: Ord a => a -> Relation a -> Bool+hasVertex x = R.hasVertex x . fromSymmetric++-- | Check if a graph contains a given edge.+-- Complexity: /O(log(n))/ time.+--+-- @+-- hasEdge x y 'empty'            == False+-- hasEdge x y ('vertex' z)       == False+-- hasEdge x y ('edge' x y)       == True+-- hasEdge x y ('edge' y x)       == True+-- hasEdge x y . 'removeEdge' x y == 'const' False+-- hasEdge x y                  == 'elem' (min x y, max x y) . 'edgeList'+-- @+hasEdge :: Ord a => a -> a -> Relation a -> Bool+hasEdge x y = R.hasEdge x y . fromSymmetric++-- | The number of vertices in a graph.+-- Complexity: /O(1)/ time.+--+-- @+-- vertexCount 'empty'             ==  0+-- vertexCount ('vertex' x)        ==  1+-- vertexCount                   ==  'length' . 'vertexList'+-- vertexCount x \< vertexCount y ==> x \< y+-- @+vertexCount :: Relation a -> Int+vertexCount = R.vertexCount . fromSymmetric++-- | The number of edges in a graph.+-- Complexity: /O(1)/ time.+--+-- @+-- edgeCount 'empty'      == 0+-- edgeCount ('vertex' x) == 0+-- edgeCount ('edge' x y) == 1+-- edgeCount            == 'length' . 'edgeList'+-- @+edgeCount :: Ord a => Relation a -> Int+edgeCount = length . edgeList++-- | The sorted list of vertices of a given graph.+-- Complexity: /O(n)/ time and memory.+--+-- @+-- vertexList 'empty'      == []+-- vertexList ('vertex' x) == [x]+-- vertexList . 'vertices' == 'Data.List.nub' . 'Data.List.sort'+-- @+vertexList :: Relation a -> [a]+vertexList = R.vertexList . fromSymmetric++-- | The sorted list of edges of a graph, where edge vertices appear in the+-- non-decreasing order.+-- Complexity: /O(n + m)/ time and /O(m)/ memory.+--+-- Note: If you need the sorted list of edges where an edge appears in both+-- directions, use @'Algebra.Graph.Relation.edgeList' . 'fromSymmetric'@.+--+-- @+-- edgeList 'empty'          == []+-- edgeList ('vertex' x)     == []+-- edgeList ('edge' x y)     == [(min x y, max y x)]+-- edgeList ('star' 2 [3,1]) == [(1,2), (2,3)]+-- @+edgeList :: Ord a => Relation a -> [(a, a)]+edgeList = Set.toAscList . edgeSet++-- | The set of vertices of a given graph.+-- Complexity: /O(1)/ time.+--+-- @+-- vertexSet 'empty'      == Set.'Set.empty'+-- vertexSet . 'vertex'   == Set.'Set.singleton'+-- vertexSet . 'vertices' == Set.'Set.fromList'+-- @+vertexSet :: Relation a -> Set a+vertexSet = R.vertexSet . fromSymmetric++-- | The sorted /adjacency list/ of a graph.+-- Complexity: /O(n + m)/ time and /O(m)/ memory.+--+-- @+-- adjacencyList 'empty'          == []+-- adjacencyList ('vertex' x)     == [(x, [])]+-- adjacencyList ('edge' 1 2)     == [(1, [2]), (2, [1])]+-- adjacencyList ('star' 2 [3,1]) == [(1, [2]), (2, [1,3]), (3, [2])]+-- 'stars' . adjacencyList        == id+-- @+adjacencyList :: Eq a => Relation a -> [(a, [a])]+adjacencyList = R.adjacencyList . fromSymmetric++-- | The /path/ on a list of vertices.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- path []    == 'empty'+-- path [x]   == 'vertex' x+-- path [x,y] == 'edge' x y+-- path       == path . 'reverse'+-- @+path :: Ord a => [a] -> Relation a+path xs = case xs of []     -> empty+                     [x]    -> vertex x+                     (_:ys) -> edges (zip xs ys)++-- | The /circuit/ on a list of vertices.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- circuit []    == 'empty'+-- circuit [x]   == 'edge' x x+-- circuit [x,y] == 'edge' x y+-- circuit       == circuit . 'reverse'+-- @+circuit :: Ord a => [a] -> Relation a+circuit []     = empty+circuit (x:xs) = path $ [x] ++ xs ++ [x]++-- | The /clique/ on a list of vertices.+-- Complexity: /O((n + m) * log(n))/ time + /O(m*log(m)) time from computing the symmetricClosure and /O(n + m)/ memory.+--+-- @+-- clique []         == 'empty'+-- clique [x]        == 'vertex' x+-- clique [x,y]      == 'edge' x y+-- clique [x,y,z]    == 'edges' [(x,y), (x,z), (y,z)]+-- clique (xs ++ ys) == 'connect' (clique xs) (clique ys)+-- clique            == clique . 'reverse'+-- @+clique :: Ord a => [a] -> Relation a+clique = SR . R.symmetricClosure . R.clique++-- | The /biclique/ on two lists of vertices.+-- Complexity: /O(n * log(n) + m)/ time + /O(m*log(m)) time from computing the symmetricClosure and /O(n + m)/ memory.+--+-- @+-- biclique []      []      == 'empty'+-- biclique [x]     []      == 'vertex' x+-- biclique []      [y]     == 'vertex' y+-- biclique [x1,x2] [y1,y2] == 'edges' [(x1,y1), (x1,y2), (x2,x2), (x2,y2)]+-- biclique xs      ys      == 'connect' ('vertices' xs) ('vertices' ys)+-- @+biclique :: Ord a => [a] -> [a] -> Relation a+biclique xs = SR . R.symmetricClosure . R.biclique xs++-- TODO: Optimise.+-- | The /star/ formed by a centre vertex connected to a list of leaves.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- star x []    == 'vertex' x+-- star x [y]   == 'edge' x y+-- star x [y,z] == 'edges' [(x,y), (x,z)]+-- star x ys    == 'connect' ('vertex' x) ('vertices' ys)+-- @+star :: Ord a => a -> [a] -> Relation a+star x [] = vertex x+star x ys = connect (vertex x) (vertices ys)++-- | The /stars/ formed by overlaying a list of 'star's. An inverse of+-- 'adjacencyList'.+-- Complexity: /O(L * log(n))/ time, memory and size, where /L/ is the total+-- size of the input.+--+-- @+-- stars []                      == 'empty'+-- stars [(x, [])]               == 'vertex' x+-- stars [(x, [y])]              == 'edge' x y+-- stars [(x, ys)]               == 'star' x ys+-- stars                         == 'overlays' . 'map' ('uncurry' 'star')+-- stars . 'adjacencyList'         == id+-- 'overlay' (stars xs) (stars ys) == stars (xs ++ ys)+-- @+stars :: Ord a => [(a, [a])] -> Relation a+stars as = SR $ RI.Relation (Set.fromList vs) (Set.fromList es)+  where+    vs = concatMap (uncurry (:)) as+    es = [ (x, y) | (x, ys) <- as, y <- ys ] ++ [ (y, x) | (x, ys) <- as, y <- ys ]++-- | The /tree graph/ constructed from a given 'Tree.Tree' data structure.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- tree (Node x [])                                         == 'vertex' x+-- tree (Node x [Node y [Node z []]])                       == 'path' [x,y,z]+-- tree (Node x [Node y [], Node z []])                     == 'star' x [y,z]+-- tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == 'edges' [(1,2), (1,3), (3,4), (3,5)]+-- @+tree :: Ord a => Tree a -> Relation a+tree (Node x []) = vertex x+tree (Node x f ) = star x (map rootLabel f)+    `overlay` forest (filter (not . null . subForest) f)++-- | The /forest graph/ constructed from a given 'Tree.Forest' data structure.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- forest []                                                  == 'empty'+-- forest [x]                                                 == 'tree' x+-- forest [Node 1 [Node 2 [], Node 3 []], Node 4 [Node 5 []]] == 'edges' [(1,2), (1,3), (4,5)]+-- forest                                                     == 'overlays' . 'map' 'tree'+-- @+forest :: Ord a => Forest a -> Relation a+forest = overlays . map tree++-- | Remove a vertex from a given graph.+-- Complexity: /O(n + m)/ time.+--+-- @+-- removeVertex x ('vertex' x)       == 'empty'+-- removeVertex 1 ('vertex' 2)       == 'vertex' 2+-- removeVertex x ('edge' x x)       == 'empty'+-- removeVertex 1 ('edge' 1 2)       == 'vertex' 2+-- removeVertex x . removeVertex x == removeVertex x+-- @+removeVertex :: Ord a => a -> Relation a -> Relation a+removeVertex x = SR . R.removeVertex x . fromSymmetric++-- | Remove an edge from a given graph.+-- Complexity: /O(log(m))/ time.+--+-- @+-- removeEdge x y ('edge' x y)       == 'vertices' [x,y]+-- removeEdge x y . removeEdge x y == removeEdge x y+-- removeEdge x y                  == removeEdge y x+-- removeEdge x y . 'removeVertex' x == 'removeVertex' x+-- removeEdge 1 1 (1 * 1 * 2 * 2)  == 1 * 2 * 2+-- removeEdge 1 2 (1 * 1 * 2 * 2)  == 1 * 1 + 2 * 2+-- @+removeEdge :: Ord a => a -> a -> Relation a -> Relation a+removeEdge x y r = SR $ RI.Relation d (Set.delete (y, x) $ Set.delete (x, y) rr)+  where+    RI.Relation d rr = fromSymmetric r++-- | The function @'replaceVertex' x y@ replaces vertex @x@ with vertex @y@ in a+-- given 'Relation'. If @y@ already exists, @x@ and @y@ will be merged.+-- Complexity: /O((n + m) * log(n))/ time.+--+-- @+-- replaceVertex x x            == id+-- replaceVertex x y ('vertex' x) == 'vertex' y+-- replaceVertex x y            == 'mergeVertices' (== x) y+-- @+replaceVertex :: Ord a => a -> a -> Relation a -> Relation a+replaceVertex u v = gmap $ \w -> if w == u then v else w++-- | Merge vertices satisfying a given predicate into a given vertex.+-- Complexity: /O((n + m) * log(n))/ time, assuming that the predicate takes+-- /O(1)/ to be evaluated.+--+-- @+-- mergeVertices ('const' False) x    == id+-- mergeVertices (== x) y           == 'replaceVertex' x y+-- mergeVertices 'even' 1 (0 * 2)     == 1 * 1+-- mergeVertices 'odd'  1 (3 + 4 * 5) == 4 * 1+-- @+mergeVertices :: Ord a => (a -> Bool) -> a -> Relation a -> Relation a+mergeVertices p v = gmap $ \u -> if p u then v else u++-- | Transform a graph by applying a function to each of its vertices. This is+-- similar to @Functor@'s 'fmap' but can be used with non-fully-parametric+-- 'Relation'.+-- Complexity: /O((n + m) * log(n))/ time.+--+-- @+-- gmap f 'empty'      == 'empty'+-- gmap f ('vertex' x) == 'vertex' (f x)+-- gmap f ('edge' x y) == 'edge' (f x) (f y)+-- gmap id           == id+-- gmap f . gmap g   == gmap (f . g)+-- @+gmap :: Ord b => (a -> b) -> Relation a -> Relation b+gmap f = SR . R.gmap f . fromSymmetric++-- | Construct the /induced subgraph/ of a given graph by removing the+-- vertices that do not satisfy a given predicate.+-- Complexity: /O(m)/ time, assuming that the predicate takes /O(1)/ to+-- be evaluated.+--+-- @+-- induce ('const' True ) x      == x+-- induce ('const' False) x      == 'empty'+-- induce (/= x)               == 'removeVertex' x+-- induce p . induce q         == induce (\\x -> p x && q x)+-- 'isSubgraphOf' (induce p x) x == True+-- @+induce :: (a -> Bool) -> Relation a -> Relation a+induce p = SR . R.induce p . fromSymmetric+ -- | The set of /neighbours/ of an element @x@ is the set of elements that are -- related to it, i.e. @neighbours x == { a | aRx }@. In the context of undirected -- graphs, this corresponds to the set of /adjacent/ vertices of vertex @x@.@@ -42,5 +467,5 @@ -- neighbours x ('Algebra.Graph.Class.edge' x y) == Set.'Set.fromList' [y] -- neighbours y ('Algebra.Graph.Class.edge' x y) == Set.'Set.fromList' [x] -- @-neighbours :: Ord a => a -> SymmetricRelation a -> Set.Set a-neighbours x = postSet x . toRelation+neighbours :: Ord a => a -> Relation a -> Set a+neighbours x = R.postSet x . fromSymmetric
+ src/Algebra/Graph/Relation/Symmetric/Internal.hs view
@@ -0,0 +1,215 @@+-----------------------------------------------------------------------------+-- |+-- Module     : Algebra.Graph.Relation.Symmetric.Internal+-- Copyright  : (c) Andrey Mokhov 2016-2019+-- License    : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability  : unstable+--+-- This module exposes the implementation of symmetric binary relation data type.+-- The API is unstable and unsafe, and is exposed only for documentation. You+-- should use the non-internal module "Algebra.Graph.Relation.Symmetric" instead.+-----------------------------------------------------------------------------++module Algebra.Graph.Relation.Symmetric.Internal (+    -- * Implementation of symmetric binary relations+    Relation (..), fromSymmetric, empty, vertex, overlay, connect, edgeSet,+    consistent+  ) where++import Algebra.Graph.Internal+import Control.DeepSeq+import Data.Monoid (mconcat)+import Data.Set (Set)++import qualified Data.Set as Set++import qualified Algebra.Graph.Relation.Internal as RI+import qualified Algebra.Graph.Relation          as R++{-| This data type represents a /symmetric binary relation/ over a set of+elements of type @a@. Symmetric relations satisfy all laws of the+'Algebra.Graph.Class.Undirected' type class, including the commutativity of+'connect':++@'connect' x y == 'connect' y x@++The 'Show' instance lists edge vertices in non-decreasing order:++@show (empty     :: Relation Int) == "empty"+show (1         :: Relation Int) == "vertex 1"+show (1 + 2     :: Relation Int) == "vertices [1,2]"+show (1 * 2     :: Relation Int) == "edge 1 2"+show (2 * 1     :: Relation Int) == "edge 1 2"+show (1 * 2 * 1 :: Relation Int) == "edges [(1,1),(1,2)]"+show (3 * 2 * 1 :: Relation Int) == "edges [(1,2),(1,3),(2,3)]"+show (1 * 2 + 3 :: Relation Int) == "overlay (vertex 3) (edge 1 2)"@++The total order on graphs is defined using /size-lexicographic/ comparison:++* Compare the number of vertices. In case of a tie, continue.+* Compare the sets of vertices. In case of a tie, continue.+* Compare the number of edges. In case of a tie, continue.+* Compare the sets of edges.++Here are a few examples:++@'vertex' 1 < 'vertex' 2+'vertex' 3 < 'Algebra.Graph.Relation.Symmetric.edge' 1 2+'vertex' 1 < 'Algebra.Graph.Relation.Symmetric.edge' 1 1+'Algebra.Graph.Relation.Symmetric.edge' 1 1 < 'Algebra.Graph.Relation.Symmetric.edge' 1 2+'Algebra.Graph.Relation.Symmetric.edge' 1 2 < 'Algebra.Graph.Relation.Symmetric.edge' 1 1 + 'Algebra.Graph.Relation.Symmetric.edge' 2 2+'Algebra.Graph.Relation.Symmetric.edge' 2 1 < 'Algebra.Graph.Relation.Symmetric.edge' 1 3@++@'Algebra.Graph.Relation.Symmetric.edge' 1 2 == 'Algebra.Graph.Relation.Symmetric.edge' 2 1@++Note that the resulting order refines the+'Algebra.Graph.Relation.Symmetric.isSubgraphOf' relation and is compatible with+'overlay' and 'connect' operations:++@'Algebra.Graph.Relation.Symmetric.isSubgraphOf' x y ==> x <= y@++@'empty' <= x+x     <= x + y+x + y <= x * y@+-}+newtype Relation a = SR (RI.Relation a) deriving NFData++instance Ord a => Eq (Relation a) where+    x == y = fromSymmetric x == fromSymmetric y++instance (Ord a, Show a) => Show (Relation a) where+    show r@(SR (RI.Relation d _)) = show (RI.Relation d $ edgeSet r)++instance Ord a => Ord (Relation a) where+    compare rx@(SR (RI.Relation vx _)) ry@(SR (RI.Relation vy _)) = mconcat+        [ compare (Set.size vx) (Set.size vy)+        , compare vx            vy+        , compare (Set.size ex) (Set.size ey)+        , compare ex            ey ]+      where+        ex = edgeSet rx+        ey = edgeSet ry++instance (Ord a, Num a) => Num (Relation a) where+    fromInteger = vertex . fromInteger+    (+)         = overlay+    (*)         = connect+    signum      = const empty+    abs         = id+    negate      = id++-- | Extract the underlying symmetric "Algebra.Graph.Relation".+-- Complexity: /O(1)/ time and memory.+--+-- @+-- fromSymmetric ('Algebra.Graph.Relation.Symmetric.edge' 1 2)    == 'Algebra.Graph.Relation.edges' [(1,2), (2,1)]+-- 'Algebra.Graph.Relation.vertexCount' . fromSymmetric == 'Algebra.Graph.Relation.Symmetric.vertexCount'+-- 'Algebra.Graph.Relation.edgeCount'   . fromSymmetric <= (*2) . 'Algebra.Graph.Relation.Symmetric.edgeCount'+-- @+fromSymmetric :: Relation a -> RI.Relation a+fromSymmetric (SR x) = x++-- | Construct the /empty graph/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'Algebra.Graph.Relation.Symmetric.isEmpty'     empty == True+-- 'Algebra.Graph.Relation.Symmetric.hasVertex' x empty == False+-- 'Algebra.Graph.Relation.Symmetric.vertexCount' empty == 0+-- 'Algebra.Graph.Relation.Symmetric.edgeCount'   empty == 0+-- @+empty :: Relation a+empty = SR $ RI.Relation Set.empty Set.empty++-- | Construct the graph comprising /a single isolated vertex/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'Algebra.Graph.Relation.Symmetric.isEmpty'     (vertex x) == False+-- 'Algebra.Graph.Relation.Symmetric.hasVertex' x (vertex x) == True+-- 'Algebra.Graph.Relation.Symmetric.vertexCount' (vertex x) == 1+-- 'Algebra.Graph.Relation.Symmetric.edgeCount'   (vertex x) == 0+-- @+vertex :: a -> Relation a+vertex x = SR $ RI.Relation (Set.singleton x) Set.empty++-- | /Overlay/ two graphs. This is a commutative, associative and idempotent+-- operation with the identity 'empty'.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- 'Algebra.Graph.Relation.Symmetric.isEmpty'     (overlay x y) == 'Algebra.Graph.Relation.Symmetric.isEmpty'   x   && 'Algebra.Graph.Relation.Symmetric.isEmpty'   y+-- 'Algebra.Graph.Relation.Symmetric.hasVertex' z (overlay x y) == 'Algebra.Graph.Relation.Symmetric.hasVertex' z x || 'Algebra.Graph.Relation.Symmetric.hasVertex' z y+-- 'Algebra.Graph.Relation.Symmetric.vertexCount' (overlay x y) >= 'Algebra.Graph.Relation.Symmetric.vertexCount' x+-- 'Algebra.Graph.Relation.Symmetric.vertexCount' (overlay x y) <= 'Algebra.Graph.Relation.Symmetric.vertexCount' x + 'Algebra.Graph.Relation.Symmetric.vertexCount' y+-- 'Algebra.Graph.Relation.Symmetric.edgeCount'   (overlay x y) >= 'Algebra.Graph.Relation.Symmetric.edgeCount' x+-- 'Algebra.Graph.Relation.Symmetric.edgeCount'   (overlay x y) <= 'Algebra.Graph.Relation.Symmetric.edgeCount' x   + 'Algebra.Graph.Relation.Symmetric.edgeCount' y+-- 'Algebra.Graph.Relation.Symmetric.vertexCount' (overlay 1 2) == 2+-- 'Algebra.Graph.Relation.Symmetric.edgeCount'   (overlay 1 2) == 0+-- @+overlay :: Ord a => Relation a -> Relation a -> Relation a+overlay (SR x) (SR y) = SR $ RI.Relation (R.domain   x `Set.union` R.domain   y)+                                         (R.relation x `Set.union` R.relation y)++-- | /Connect/ two graphs. This is a commutative and associative operation with+-- the identity 'empty', which distributes over 'overlay' and obeys the+-- decomposition axiom.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory. Note that the+-- number of edges in the resulting graph is quadratic with respect to the number+-- of vertices of the arguments: /m = O(m1 + m2 + n1 * n2)/.+--+-- @+-- connect x y               == connect y x+-- 'Algebra.Graph.Relation.Symmetric.isEmpty'     (connect x y) == 'Algebra.Graph.Relation.Symmetric.isEmpty'   x   && 'Algebra.Graph.Relation.Symmetric.isEmpty'   y+-- 'Algebra.Graph.Relation.Symmetric.hasVertex' z (connect x y) == 'Algebra.Graph.Relation.Symmetric.hasVertex' z x || 'Algebra.Graph.Relation.Symmetric.hasVertex' z y+-- 'Algebra.Graph.Relation.Symmetric.vertexCount' (connect x y) >= 'Algebra.Graph.Relation.Symmetric.vertexCount' x+-- 'Algebra.Graph.Relation.Symmetric.vertexCount' (connect x y) <= 'Algebra.Graph.Relation.Symmetric.vertexCount' x + 'Algebra.Graph.Relation.Symmetric.vertexCount' y+-- 'Algebra.Graph.Relation.Symmetric.edgeCount'   (connect x y) >= 'Algebra.Graph.Relation.Symmetric.edgeCount' x+-- 'Algebra.Graph.Relation.Symmetric.edgeCount'   (connect x y) >= 'Algebra.Graph.Relation.Symmetric.edgeCount' y+-- 'Algebra.Graph.Relation.Symmetric.edgeCount'   (connect x y) >= 'Algebra.Graph.Relation.Symmetric.vertexCount' x * 'Algebra.Graph.Relation.Symmetric.vertexCount' y \`div\` 2+-- 'Algebra.Graph.Relation.Symmetric.vertexCount' (connect 1 2) == 2+-- 'Algebra.Graph.Relation.Symmetric.edgeCount'   (connect 1 2) == 1+-- @+connect :: Ord a => Relation a -> Relation a -> Relation a+connect (SR x) (SR y) = SR $ RI.Relation (R.domain x `Set.union` R.domain y)+    (Set.unions [R.relation x, R.relation y, R.domain x `setProduct` R.domain y+                                           , R.domain y `setProduct` R.domain x ])++-- | The set of edges of a given graph, where edge vertices appear in the+-- non-decreasing order.+-- Complexity: /O(m)/ time.+--+-- Note: If you need the set of edges where an edge appears in both directions,+-- use @'Algebra.Graph.Relation.relation' . 'fromSymmetric'@. The latter is much+-- faster than this function, and takes only /O(1)/ time and memory.+--+-- @+-- edgeSet 'empty'      == Set.'Set.empty'+-- edgeSet ('vertex' x) == Set.'Set.empty'+-- edgeSet ('Algebra.Graph.Relation.Symmetric.edge' x y) == Set.'Set.singleton' (min x y, max x y)+-- @+edgeSet :: Ord a => Relation a -> Set (a, a)+edgeSet (SR (RI.Relation _ r)) = Set.filter (uncurry (<=)) r++-- | Check if the internal representation of a symmetric relation is consistent,+-- i.e. if (i) all pairs of elements in the 'RI.relation' refer to existing+-- elements in the 'RI.domain', and (ii) all edges have their symmetric+-- counterparts. It should be impossible to create an inconsistent 'Relation',+-- and we use this function in testing.+-- /Note: this function is for internal use only/.+--+-- @+-- consistent 'Algebra.Graph.Relation.Symmetric.empty'         == True+-- consistent ('Algebra.Graph.Relation.Symmetric.vertex' x)    == True+-- consistent ('Algebra.Graph.Relation.Symmetric.overlay' x y) == True+-- consistent ('Algebra.Graph.Relation.Symmetric.connect' x y) == True+-- consistent ('Algebra.Graph.Relation.Symmetric.edge' x y)    == True+-- consistent ('Algebra.Graph.Relation.Symmetric.edges' xs)    == True+-- consistent ('Algebra.Graph.Relation.Symmetric.stars' xs)    == True+-- @+consistent :: Ord a => Relation a -> Bool+consistent (SR r) =+    RI.referredToVertexSet (R.relation r) `Set.isSubsetOf` R.domain r+    &&+    r == R.transpose r
src/Algebra/Graph/ToGraph.hs view
@@ -63,6 +63,7 @@ import qualified Algebra.Graph.AdjacencyIntMap.Algorithm      as AIM import qualified Algebra.Graph.AdjacencyIntMap.Internal       as AIM import qualified Algebra.Graph.Relation                       as R+import qualified Algebra.Graph.Relation.Symmetric             as SR import qualified Data.IntMap                                  as IntMap import qualified Data.IntSet                                  as IntSet import qualified Data.Map                                     as Map@@ -528,6 +529,7 @@     isTopSortOf x              = isTopSortOf x . NAM.am  -- TODO: Get rid of "Relation.Internal" and move this instance to "Relation".+-- | See "Algebra.Graph.Relation". instance Ord a => ToGraph (R.Relation a) where     type ToVertex (R.Relation a) = a     toGraph r                  = G.vertices (Set.toList $ R.domain   r) `G.overlay`@@ -553,3 +555,28 @@     toAdjacencyIntMap          = AIM.AM . adjacencyIntMap     toAdjacencyMapTranspose    = AM.transpose . toAdjacencyMap     toAdjacencyIntMapTranspose = AIM.transpose . toAdjacencyIntMap++-- TODO: This instance is probably wrong because of the way it treats edges.+-- Find out a better way to integrate undirected graphs into 'ToGraph'.+-- | See "Algebra.Graph.Symmetric.Relation". Warning: this instance is likely to+-- be modified or removed in future.+instance Ord a => ToGraph (SR.Relation a) where+    type ToVertex (SR.Relation a) = a+    toGraph                    = toGraph . SR.fromSymmetric+    isEmpty                    = SR.isEmpty+    hasVertex                  = SR.hasVertex+    hasEdge                    = SR.hasEdge+    vertexCount                = SR.vertexCount+    edgeCount                  = SR.edgeCount+    vertexList                 = SR.vertexList+    vertexSet                  = SR.vertexSet+    vertexIntSet               = IntSet.fromAscList . SR.vertexList+    edgeList                   = SR.edgeList+    edgeSet                    = SR.edgeSet+    adjacencyList              = SR.adjacencyList+    adjacencyMap               = adjacencyMap . SR.fromSymmetric+    adjacencyIntMap            = adjacencyIntMap . SR.fromSymmetric+    toAdjacencyMap             = AM.AM . adjacencyMap+    toAdjacencyIntMap          = AIM.AM . adjacencyIntMap+    toAdjacencyMapTranspose    = toAdjacencyMap+    toAdjacencyIntMapTranspose = toAdjacencyIntMap
test/Algebra/Graph/Test/API.hs view
@@ -15,11 +15,13 @@   ) where  import Data.Monoid (Any)+import Data.IntSet (IntSet)+import Data.Set (Set) import Data.Tree  import Algebra.Graph.Class (Graph (..)) -import qualified Algebra.Graph                       as Graph+import qualified Algebra.Graph                       as G import qualified Algebra.Graph.AdjacencyMap          as AM import qualified Algebra.Graph.Labelled              as LG import qualified Algebra.Graph.Labelled.AdjacencyMap as LAM@@ -27,10 +29,16 @@ import qualified Algebra.Graph.HigherKinded.Class    as HClass import qualified Algebra.Graph.AdjacencyIntMap       as AIM import qualified Algebra.Graph.Relation              as R-import qualified Data.Set                            as Set-import qualified Data.IntSet                         as IntSet+import qualified Algebra.Graph.Relation.Symmetric    as SR +import qualified Algebra.Graph.AdjacencyMap.Internal       as AMI+import qualified Algebra.Graph.AdjacencyIntMap.Internal    as AIMI+import qualified Algebra.Graph.Relation.Internal           as RI+import qualified Algebra.Graph.Relation.Symmetric.Internal as SRI+ class Graph g => GraphAPI g where+    consistent           :: g -> Bool+    consistent           = notImplemented     edge                 :: Vertex g -> Vertex g -> g     edge                 = notImplemented     vertices             :: [Vertex g] -> g@@ -41,14 +49,16 @@     overlays             = notImplemented     connects             :: [g] -> g     connects             = notImplemented-    fromAdjacencySets    :: [(Vertex g, Set.Set (Vertex g))] -> g+    fromAdjacencySets    :: [(Vertex g, Set (Vertex g))] -> g     fromAdjacencySets    = notImplemented-    fromAdjacencyIntSets :: [(Int, IntSet.IntSet)] -> g+    fromAdjacencyIntSets :: [(Int, IntSet)] -> g     fromAdjacencyIntSets = notImplemented     isSubgraphOf         :: g -> g -> Bool     isSubgraphOf         = notImplemented     (===)                :: g -> g -> Bool     (===)                = notImplemented+    neighbours           :: Vertex g -> g -> Set (Vertex g)+    neighbours           = notImplemented     path                 :: [Vertex g] -> g     path                 = notImplemented     circuit              :: [Vertex g] -> g@@ -108,6 +118,7 @@ notImplemented = error "Not implemented"  instance Ord a => GraphAPI (AM.AdjacencyMap a) where+    consistent        = AMI.consistent     edge              = AM.edge     vertices          = AM.vertices     edges             = AM.edges@@ -165,39 +176,40 @@     bind          = (>>=)     simplify      = Fold.simplify -instance Ord a => GraphAPI (Graph.Graph a) where-    edge          = Graph.edge-    vertices      = Graph.vertices-    edges         = Graph.edges-    overlays      = Graph.overlays-    connects      = Graph.connects-    isSubgraphOf  = Graph.isSubgraphOf-    (===)         = (Graph.===)-    path          = Graph.path-    circuit       = Graph.circuit-    clique        = Graph.clique-    biclique      = Graph.biclique-    star          = Graph.star-    stars         = Graph.stars-    tree          = Graph.tree-    forest        = Graph.forest-    mesh          = Graph.mesh-    torus         = Graph.torus-    deBruijn      = Graph.deBruijn-    removeVertex  = Graph.removeVertex-    removeEdge    = Graph.removeEdge-    replaceVertex = Graph.replaceVertex-    mergeVertices = Graph.mergeVertices-    splitVertex   = Graph.splitVertex-    transpose     = Graph.transpose+instance Ord a => GraphAPI (G.Graph a) where+    edge          = G.edge+    vertices      = G.vertices+    edges         = G.edges+    overlays      = G.overlays+    connects      = G.connects+    isSubgraphOf  = G.isSubgraphOf+    (===)         = (G.===)+    path          = G.path+    circuit       = G.circuit+    clique        = G.clique+    biclique      = G.biclique+    star          = G.star+    stars         = G.stars+    tree          = G.tree+    forest        = G.forest+    mesh          = G.mesh+    torus         = G.torus+    deBruijn      = G.deBruijn+    removeVertex  = G.removeVertex+    removeEdge    = G.removeEdge+    replaceVertex = G.replaceVertex+    mergeVertices = G.mergeVertices+    splitVertex   = G.splitVertex+    transpose     = G.transpose     gmap          = fmap-    induce        = Graph.induce-    compose       = Graph.compose+    induce        = G.induce+    compose       = G.compose     bind          = (>>=)-    simplify      = Graph.simplify-    box           = Graph.box+    simplify      = G.simplify+    box           = G.box  instance GraphAPI AIM.AdjacencyIntMap where+    consistent           = AIMI.consistent     edge                 = AIM.edge     vertices             = AIM.vertices     edges                = AIM.edges@@ -227,6 +239,7 @@     transitiveClosure    = AIM.transitiveClosure  instance Ord a => GraphAPI (R.Relation a) where+    consistent        = RI.consistent     edge              = R.edge     vertices          = R.vertices     edges             = R.edges@@ -253,6 +266,31 @@     reflexiveClosure  = R.reflexiveClosure     symmetricClosure  = R.symmetricClosure     transitiveClosure = R.transitiveClosure++instance Ord a => GraphAPI (SR.Relation a) where+    consistent        = SRI.consistent+    edge              = SR.edge+    vertices          = SR.vertices+    edges             = SR.edges+    overlays          = SR.overlays+    connects          = SR.connects+    isSubgraphOf      = SR.isSubgraphOf+    neighbours        = SR.neighbours+    path              = SR.path+    circuit           = SR.circuit+    clique            = SR.clique+    biclique          = SR.biclique+    star              = SR.star+    stars             = SR.stars+    tree              = SR.tree+    forest            = SR.forest+    removeVertex      = SR.removeVertex+    removeEdge        = SR.removeEdge+    replaceVertex     = SR.replaceVertex+    mergeVertices     = SR.mergeVertices+    transpose         = id+    gmap              = SR.gmap+    induce            = SR.induce  instance Ord a => GraphAPI (LG.Graph Any a) where     vertices     = LG.vertices
test/Algebra/Graph/Test/AdjacencyIntMap.hs view
@@ -14,7 +14,6 @@   ) where  import Algebra.Graph.AdjacencyIntMap-import Algebra.Graph.AdjacencyIntMap.Internal import Algebra.Graph.Test import Algebra.Graph.Test.Generic @@ -26,9 +25,7 @@     putStrLn "\n============ AdjacencyIntMap ============"     test "Axioms of graphs" (axioms :: GraphTestsuite AdjacencyIntMap) -    test "Consistency of arbitraryAdjacencyMap" $ \m ->-        consistent m-+    testConsistent           t     testShow                 t     testBasicPrimitives      t     testFromAdjacencyIntSets t
test/Algebra/Graph/Test/AdjacencyMap.hs view
@@ -18,7 +18,6 @@  import Algebra.Graph.AdjacencyMap import Algebra.Graph.AdjacencyMap.Algorithm-import Algebra.Graph.AdjacencyMap.Internal import Algebra.Graph.Test import Algebra.Graph.Test.Generic @@ -34,9 +33,7 @@     putStrLn "\n============ AdjacencyMap ============"     test "Axioms of graphs" (axioms :: GraphTestsuite AI) -    test "Consistency of arbitraryAdjacencyMap" $ \(m :: AI) ->-        consistent m-+    testConsistent        t     testShow              t     testBasicPrimitives   t     testFromAdjacencySets t
test/Algebra/Graph/Test/Arbitrary.hs view
@@ -18,7 +18,8 @@ import Prelude.Compat  import Control.Monad-import Data.List.NonEmpty (NonEmpty (..))+import Data.List.NonEmpty (NonEmpty (..), toList)+import Data.Maybe (catMaybes) import Data.Tree import Test.QuickCheck @@ -28,17 +29,19 @@ import Algebra.Graph.Export import Algebra.Graph.Fold (Fold) import Algebra.Graph.Label-import Algebra.Graph.Relation.Internal import Algebra.Graph.Relation.InternalDerived+import Algebra.Graph.Relation.Symmetric.Internal  import qualified Algebra.Graph.AdjacencyIntMap       as AdjacencyIntMap import qualified Algebra.Graph.AdjacencyMap          as AdjacencyMap import qualified Algebra.Graph.NonEmpty.AdjacencyMap as NAM import qualified Algebra.Graph.Class                 as C+import qualified Algebra.Graph.Fold                  as Fold import qualified Algebra.Graph.Labelled              as LG import qualified Algebra.Graph.Labelled.AdjacencyMap as LAM import qualified Algebra.Graph.NonEmpty              as NonEmpty import qualified Algebra.Graph.Relation              as Relation+import qualified Algebra.Graph.Relation.Symmetric    as Symmetric  -- | Generate an arbitrary 'C.Graph' value of a specified size. arbitraryGraph :: (C.Graph g, Arbitrary (C.Vertex g)) => Gen g@@ -61,10 +64,20 @@     shrink (Connect x y) = [Empty, x, y, Overlay x y]                         ++ [Connect x' y' | (x', y') <- shrink (x, y) ] --- TODO: Implement a custom shrink method.-instance Arbitrary a => Arbitrary (Fold a) where+instance (Eq a, Ord a, Arbitrary a) => Arbitrary (Fold a) where     arbitrary = arbitraryGraph +    shrink g = oneLessVertex ++ oneLessEdge+      where+         oneLessVertex =+           let vertices = Fold.vertexList g+           in  [ Fold.removeVertex v g | v <- vertices ]++         oneLessEdge =+           let edges = Fold.edgeList g+           in  [ Fold.removeEdge v w g | (v, w) <- edges ]++ -- | Generate an arbitrary 'NonEmpty.Graph' value of a specified size. arbitraryNonEmptyGraph :: Arbitrary a => Gen (NonEmpty.Graph a) arbitraryNonEmptyGraph = sized expr@@ -86,18 +99,29 @@         ++ [NonEmpty.Connect x' y' | (x', y') <- shrink (x, y) ]  -- | Generate an arbitrary 'Relation'.-arbitraryRelation :: (Arbitrary a, Ord a) => Gen (Relation a)+arbitraryRelation :: (Arbitrary a, Ord a) => Gen (Relation.Relation a) arbitraryRelation = Relation.stars <$> arbitrary  -- TODO: Implement a custom shrink method.-instance (Arbitrary a, Ord a) => Arbitrary (Relation a) where+instance (Arbitrary a, Ord a) => Arbitrary (Relation.Relation a) where     arbitrary = arbitraryRelation +    shrink g = oneLessVertex ++ oneLessEdge+      where+         oneLessVertex =+           let vertices = Relation.vertexList g+           in  [ Relation.removeVertex v g | v <- vertices ]++         oneLessEdge =+           let edges = Relation.edgeList g+           in  [ Relation.removeEdge v w g | (v, w) <- edges ]++ instance (Arbitrary a, Ord a) => Arbitrary (ReflexiveRelation a) where     arbitrary = ReflexiveRelation <$> arbitraryRelation -instance (Arbitrary a, Ord a) => Arbitrary (SymmetricRelation a) where-    arbitrary = SymmetricRelation <$> arbitraryRelation+instance (Arbitrary a, Ord a) => Arbitrary (Symmetric.Relation a) where+    arbitrary = SR . Relation.symmetricClosure <$> arbitraryRelation  instance (Arbitrary a, Ord a) => Arbitrary (TransitiveRelation a) where     arbitrary = TransitiveRelation <$> arbitraryRelation@@ -110,10 +134,19 @@ arbitraryAdjacencyMap :: (Arbitrary a, Ord a) => Gen (AdjacencyMap a) arbitraryAdjacencyMap = AdjacencyMap.stars <$> arbitrary --- TODO: Implement a custom shrink method. instance (Arbitrary a, Ord a) => Arbitrary (AdjacencyMap a) where     arbitrary = arbitraryAdjacencyMap +    shrink g = oneLessVertex ++ oneLessEdge+      where+         oneLessVertex =+           let vertices = AdjacencyMap.vertexList g+           in  [ AdjacencyMap.removeVertex v g | v <- vertices ]++         oneLessEdge =+           let edges = AdjacencyMap.edgeList g+           in  [ AdjacencyMap.removeEdge v w g | (v, w) <- edges ]+ -- | Generate an arbitrary non-empty 'NAM.AdjacencyMap'. It is guaranteed that -- the resulting adjacency map is 'consistent'. arbitraryNonEmptyAdjacencyMap :: (Arbitrary a, Ord a) => Gen (NAM.AdjacencyMap a)@@ -127,28 +160,55 @@                 return ((x, []) :| []) -- There must be at least one vertex             (x:xs) -> return (x :| xs) --- TODO: Implement a custom shrink method. instance (Arbitrary a, Ord a) => Arbitrary (NAM.AdjacencyMap a) where     arbitrary = arbitraryNonEmptyAdjacencyMap +    shrink g = oneLessVertex ++ oneLessEdge+      where+         oneLessVertex =+           let vertices = toList $ NAM.vertexList1 g+           in catMaybes [ NAM.removeVertex1 v g | v <- vertices ]++         oneLessEdge =+           let edges = NAM.edgeList g+           in  [ NAM.removeEdge v w g | (v, w) <- edges ]+ -- | Generate an arbitrary 'AdjacencyIntMap'. It is guaranteed that the -- resulting adjacency map is 'consistent'. arbitraryAdjacencyIntMap :: Gen AdjacencyIntMap arbitraryAdjacencyIntMap = AdjacencyIntMap.stars <$> arbitrary --- TODO: Implement a custom shrink method. instance Arbitrary AdjacencyIntMap where     arbitrary = arbitraryAdjacencyIntMap +    shrink g = oneLessVertex ++ oneLessEdge+      where+         oneLessVertex =+           let vertices = AdjacencyIntMap.vertexList g+           in  [ AdjacencyIntMap.removeVertex v g | v <- vertices ]++         oneLessEdge =+           let edges = AdjacencyIntMap.edgeList g+           in  [ AdjacencyIntMap.removeEdge v w g | (v, w) <- edges ]+ -- | Generate an arbitrary labelled 'LAM.AdjacencyMap'. It is guaranteed -- that the resulting adjacency map is 'consistent'. arbitraryLabelledAdjacencyMap :: (Arbitrary a, Ord a, Eq e, Arbitrary e, Monoid e) => Gen (LAM.AdjacencyMap e a) arbitraryLabelledAdjacencyMap = LAM.fromAdjacencyMaps <$> arbitrary --- TODO: Implement a custom shrink method. instance (Arbitrary a, Ord a, Eq e, Arbitrary e, Monoid e) => Arbitrary (LAM.AdjacencyMap e a) where     arbitrary = arbitraryLabelledAdjacencyMap +    shrink g = oneLessVertex ++ oneLessEdge+      where+         oneLessVertex =+           let vertices = LAM.vertexList g+           in  [ LAM.removeVertex v g | v <- vertices ]++         oneLessEdge =+           let edges = LAM.edgeList g+           in  [ LAM.removeEdge v w g | (_, v, w) <- edges ]+ -- | Generate an arbitrary labelled 'LAM.Graph' value of a specified size. arbitraryLabelledGraph :: (Arbitrary a, Arbitrary e) => Gen (LG.Graph e a) arbitraryLabelledGraph = sized expr@@ -168,7 +228,6 @@     shrink (LG.Connect e x y) = [LG.Empty, x, y, LG.Connect mempty x y]                              ++ [LG.Connect e x' y' | (x', y') <- shrink (x, y) ] --- TODO: Implement a custom shrink method. instance Arbitrary a => Arbitrary (Tree a) where     arbitrary = sized go       where@@ -182,9 +241,11 @@             children <- replicateM subTrees (go subSize)             return $ Node root children +    shrink (Node r fs) = [Node r fs' | fs' <- shrink fs]+ -- TODO: Implement a custom shrink method. instance Arbitrary s => Arbitrary (Doc s) where-    arbitrary = (mconcat . map literal) <$> arbitrary+    arbitrary = mconcat . map literal <$> arbitrary  instance (Arbitrary a, Num a, Ord a) => Arbitrary (Distance a) where     arbitrary = (\x -> if x < 0 then distance infinite else distance (unsafeFinite x)) <$> arbitrary
test/Algebra/Graph/Test/Generic.hs view
@@ -2,7 +2,7 @@ ----------------------------------------------------------------------------- -- | -- Module     : Algebra.Graph.Test.Generic--- Copyright  : (c) Andrey Mokhov 2016-2018+-- Copyright  : (c) Andrey Mokhov 2016-2019 -- License    : MIT (see the file LICENSE) -- Maintainer : andrey.mokhov@gmail.com -- Stability  : experimental@@ -22,9 +22,8 @@ import Data.Tree import Data.Tuple -import Algebra.Graph (Graph (..)) import Algebra.Graph.Class (Graph (..))-import Algebra.Graph.ToGraph (ToGraph (..))+import Algebra.Graph.ToGraph import Algebra.Graph.Test import Algebra.Graph.Test.API @@ -58,6 +57,18 @@                               , testOverlays                               , testConnects ] +testSymmetricBasicPrimitives :: Testsuite -> IO ()+testSymmetricBasicPrimitives = mconcat [ testSymmetricOrd+                                       , testEmpty+                                       , testVertex+                                       , testSymmetricEdge+                                       , testOverlay+                                       , testSymmetricConnect+                                       , testVertices+                                       , testSymmetricEdges+                                       , testOverlays+                                       , testSymmetricConnects ]+ testToGraph :: Testsuite -> IO () testToGraph = mconcat [ testToGraphDefault                       , testFoldg@@ -77,6 +88,21 @@                       , testPostSet                       , testPostIntSet ] +testSymmetricToGraph :: Testsuite -> IO ()+testSymmetricToGraph = mconcat [ testSymmetricToGraphDefault+                               , testIsEmpty+                               , testHasVertex+                               , testSymmetricHasEdge+                               , testVertexCount+                               , testEdgeCount+                               , testVertexList+                               , testVertexSet+                               , testVertexIntSet+                               , testSymmetricEdgeList+                               , testSymmetricEdgeSet+                               , testSymmetricAdjacencyList+                               , testNeighbours ]+ testRelational :: Testsuite -> IO () testRelational = mconcat [ testCompose                          , testClosure@@ -94,6 +120,16 @@                             , testTree                             , testForest ] +testSymmetricGraphFamilies :: Testsuite -> IO ()+testSymmetricGraphFamilies = mconcat [ testSymmetricPath+                                     , testSymmetricCircuit+                                     , testSymmetricClique+                                     , testBiclique+                                     , testStar+                                     , testStars+                                     , testTree+                                     , testForest ]+ testTransformations :: Testsuite -> IO () testTransformations = mconcat [ testRemoveVertex                               , testRemoveEdge@@ -103,6 +139,41 @@                               , testGmap                               , testInduce ] +testSymmetricTransformations :: Testsuite -> IO ()+testSymmetricTransformations = mconcat [ testRemoveVertex+                                       , testSymmetricRemoveEdge+                                       , testReplaceVertex+                                       , testMergeVertices+                                       , testGmap+                                       , testInduce ]++testConsistent :: Testsuite -> IO ()+testConsistent (Testsuite prefix (%)) = do+    putStrLn $ "\n============ " ++ prefix ++ "consistent ============"+    test "Consistency of the Arbitrary instance" $ \x -> consistent % x++    putStrLn ""+    test "consistent empty         == True" $+          consistent % empty       == True++    test "consistent (vertex x)    == True" $ \x ->+          consistent % (vertex x)  == True++    test "consistent (overlay x y) == True" $ \x y ->+          consistent % (overlay x y) == True++    test "consistent (connect x y) == True" $ \x y ->+          consistent % (connect x y) == True++    test "consistent (edge x y)    == True" $ \x y ->+          consistent % (edge x y)  == True++    test "consistent (edges xs)    == True" $ \xs ->+          consistent % (edges xs)  == True++    test "consistent (stars xs)    == True" $ \xs ->+          consistent % (stars xs)  == True+ testShow :: Testsuite -> IO () testShow (Testsuite prefix (%)) = do     putStrLn $ "\n============ " ++ prefix ++ "Show ============"@@ -131,16 +202,28 @@     test "show (vertex (-1) + vertex (-2)              ) == \"vertices [-2,-1]\"" $           show % (vertex (-1) + vertex (-2)              ) == "vertices [-2,-1]" -    test "show (vertex (-1) * vertex (-2)              ) == \"edge (-1) (-2)\"" $-          show % (vertex (-1) * vertex (-2)              ) == "edge (-1) (-2)"+    test "show (vertex (-2) * vertex (-1)              ) == \"edge (-2) (-1)\"" $+          show % (vertex (-2) * vertex (-1)              ) == "edge (-2) (-1)" -    test "show (vertex (-1) * vertex (-2) * vertex (-3)) == \"edges [(-2,-3),(-1,-3),(-1,-2)]\"" $-          show % (vertex (-1) * vertex (-2) * vertex (-3)) == "edges [(-2,-3),(-1,-3),(-1,-2)]"+    test "show (vertex (-3) * vertex (-2) * vertex (-1)) == \"edges [(-3,-2),(-3,-1),(-2,-1)]\"" $+          show % (vertex (-3) * vertex (-2) * vertex (-1)) == "edges [(-3,-2),(-3,-1),(-2,-1)]" -    test "show (vertex (-1) * vertex (-2) + vertex (-3)) == \"overlay (vertex (-3)) (edge (-1) (-2))\"" $-          show % (vertex (-1) * vertex (-2) + vertex (-3)) == "overlay (vertex (-3)) (edge (-1) (-2))"+    test "show (vertex (-3) * vertex (-2) + vertex (-1)) == \"overlay (vertex (-1)) (edge (-3) (-2))\"" $+          show % (vertex (-3) * vertex (-2) + vertex (-1)) == "overlay (vertex (-1)) (edge (-3) (-2))" +testSymmetricShow :: Testsuite -> IO ()+testSymmetricShow t@(Testsuite _ (%)) = do+    testShow t+    putStrLn ""+    test "show (2 * 1    ) == \"edge 1 2\"" $+          show % (2 * 1)   ==  "edge 1 2" +    test "show (1 * 2 * 1) == \"edges [(1,1),(1,2)]\"" $+          show % (1 * 2 * 1) == "edges [(1,1),(1,2)]"++    test "show (3 * 2 * 1) == \"edges [(1,2),(1,3),(2,3)]\"" $+          show % (3 * 2 * 1) == "edges [(1,2),(1,3),(2,3)]"+ testOrd :: Testsuite -> IO () testOrd (Testsuite prefix (%)) = do     putStrLn $ "\n============ " ++ prefix ++ "Ord ============"@@ -168,6 +251,36 @@     test "x + y    <= x * y" $ \x y ->           id % x + y <= x * y +testSymmetricOrd :: Testsuite -> IO ()+testSymmetricOrd (Testsuite prefix (%)) = do+    putStrLn $ "\n============ " ++ prefix ++ "Ord ============"+    test "vertex 1 <  vertex 2" $+          vertex 1 < id % vertex 2++    test "vertex 3 <  edge 1 2" $+          vertex 3 < id % edge 1 2++    test "vertex 1 <  edge 1 1" $+          vertex 1 < id % edge 1 1++    test "edge 1 1 <  edge 1 2" $+          edge 1 1 < id % edge 1 2++    test "edge 1 2 <  edge 1 1 + edge 2 2" $+          edge 1 2 < id % edge 1 1 + edge 2 2++    test "edge 2 1 <  edge 1 3" $+          edge 2 1 < id % edge 1 3++    test "edge 1 2 == edge 2 1" $+          edge 1 2 == id % edge 2 1++    test "x        <= x + y" $ \x y ->+          id % x   <= x + y++    test "x + y    <= x * y" $ \x y ->+          id % x + y <= x * y+ testEmpty :: Testsuite -> IO () testEmpty (Testsuite prefix (%)) = do     putStrLn $ "\n============ " ++ prefix ++ "empty ============"@@ -216,6 +329,30 @@     test "vertexCount (edge 1 2) == 2" $           vertexCount % edge 1 2 == 2 +testSymmetricEdge :: Testsuite -> IO ()+testSymmetricEdge (Testsuite prefix (%)) = do+    putStrLn $ "\n============ " ++ prefix ++ "edge ============"+    test "edge x y               == connect (vertex x) (vertex y)" $ \x y ->+          edge x y               == connect (vertex x) % vertex y++    test "edge x y               == edge y x" $ \x y ->+          edge x y               == id % edge y x++    test "edge x y               == edges [(x,y), (y,x)]" $ \x y ->+          edge x y               == id % edges [(x,y), (y,x)]++    test "hasEdge x y (edge x y) == True" $ \x y ->+          hasEdge x y % edge x y == True++    test "edgeCount   (edge x y) == 1" $ \x y ->+          edgeCount % edge x y   == 1++    test "vertexCount (edge 1 1) == 1" $+          vertexCount % edge 1 1 == 1++    test "vertexCount (edge 1 2) == 2" $+          vertexCount % edge 1 2 == 2+ testOverlay :: Testsuite -> IO () testOverlay (Testsuite prefix (%)) = do     putStrLn $ "\n============ " ++ prefix ++ "overlay ============"@@ -276,6 +413,42 @@     test "edgeCount   (connect 1 2) == 1" $           edgeCount  % connect 1 2  == 1 +testSymmetricConnect :: Testsuite -> IO ()+testSymmetricConnect (Testsuite prefix (%)) = do+    putStrLn $ "\n============ " ++ prefix ++ "connect ============"+    test "connect x y               == connect y x" $ \x y ->+          connect x y               == id % connect y x++    test "isEmpty     (connect x y) == isEmpty   x   && isEmpty   y" $ \x y ->+          isEmpty    % connect x y  == (isEmpty   x   && isEmpty   y)++    test "hasVertex z (connect x y) == hasVertex z x || hasVertex z y" $ \x y z ->+          hasVertex z % connect x y == (hasVertex z x || hasVertex z y)++    test "vertexCount (connect x y) >= vertexCount x" $ \x y ->+          vertexCount % connect x y >= vertexCount x++    test "vertexCount (connect x y) <= vertexCount x + vertexCount y" $ \x y ->+          vertexCount % connect x y <= vertexCount x + vertexCount y++    test "edgeCount   (connect x y) >= edgeCount x" $ \x y ->+          edgeCount  % connect x y  >= edgeCount x++    test "edgeCount   (connect x y) >= edgeCount y" $ \x y ->+          edgeCount  % connect x y  >= edgeCount y++    test "edgeCount   (connect x y) >= vertexCount x * vertexCount y `div` 2" $ \x y ->+          edgeCount  % connect x y  >= vertexCount x * vertexCount y `div` 2++    test "edgeCount   (connect x y) <= vertexCount x * vertexCount y + edgeCount x + edgeCount y" $ \x y ->+          edgeCount  % connect x y  <= vertexCount x * vertexCount y + edgeCount x + edgeCount y++    test "vertexCount (connect 1 2) == 2" $+          vertexCount % connect 1 2 == 2++    test "edgeCount   (connect 1 2) == 1" $+          edgeCount  % connect 1 2  == 1+ testVertices :: Testsuite -> IO () testVertices (Testsuite prefix (%)) = do     putStrLn $ "\n============ " ++ prefix ++ "vertices ============"@@ -306,6 +479,18 @@     test "edgeCount . edges == length . nub" $ \xs ->           edgeCount % edges xs == (length . nubOrd) xs +testSymmetricEdges :: Testsuite -> IO ()+testSymmetricEdges (Testsuite prefix (%)) = do+    putStrLn $ "\n============ " ++ prefix ++ "edges ============"+    test "edges []             == empty" $+          edges []             == id % empty++    test "edges [(x,y)]        == edge x y" $ \x y ->+          edges [(x,y)]        == id % edge x y++    test "edges [(x,y), (y,x)] == edge x y" $ \x y ->+          edges [(x,y), (y,x)] == id % edge x y+ testOverlays :: Testsuite -> IO () testOverlays (Testsuite prefix (%)) = do     putStrLn $ "\n============ " ++ prefix ++ "overlays ============"@@ -342,6 +527,12 @@     test "isEmpty . connects == all isEmpty" $ size10 $ \xs ->           isEmpty % connects xs == all isEmpty xs +testSymmetricConnects :: Testsuite -> IO ()+testSymmetricConnects t@(Testsuite _ (%)) = do+    testConnects t+    test "connects           == connects . reverse" $ size10 $ \xs ->+          connects xs        == id % connects (reverse xs)+ testStars :: Testsuite -> IO () testStars (Testsuite prefix (%)) = do     putStrLn $ "\n============ " ++ prefix ++ "stars ============"@@ -424,11 +615,17 @@         let y = x + z -- Make sure we hit the precondition         in isSubgraphOf x % y                      ==> x <= y +testSymmetricIsSubgraphOf :: Testsuite -> IO ()+testSymmetricIsSubgraphOf t@(Testsuite _ (%)) = do+    testIsSubgraphOf t+    test "isSubgraphOf (edge x y) (edge y x)       ==  True" $ \x y ->+          isSubgraphOf (edge x y) % edge y x       ==  True+ testToGraphDefault :: Testsuite -> IO () testToGraphDefault (Testsuite prefix (%)) = do     putStrLn $ "\n============ " ++ prefix ++ "toGraph et al. ============"     test "toGraph                    == foldg Empty Vertex Overlay Connect" $ \x ->-          toGraph % x                == foldg Empty Vertex Overlay Connect x+          toGraph % x                == foldg G.Empty G.Vertex G.Overlay G.Connect x      test "foldg                      == Algebra.Graph.foldg . toGraph" $ \e (apply -> v) (applyFun2 -> o) (applyFun2 -> c) x ->           foldg e v o c x            == (G.foldg (e :: Int) v o c . toGraph) % x@@ -532,6 +729,100 @@     test "isTopSortOf vs             == Algebra.Graph.AdjacencyMap.isTopSortOf vs . toAdjacencyMap" $ \vs x ->           isTopSortOf vs x           == (AM.isTopSortOf vs . toAdjacencyMap) % x +-- TODO: We currently do not test 'edgeSet'.+testSymmetricToGraphDefault :: Testsuite -> IO ()+testSymmetricToGraphDefault (Testsuite prefix (%)) = do+    putStrLn $ "\n============ " ++ prefix ++ "toGraph et al. ============"+    test "toGraph                    == foldg Empty Vertex Overlay Connect" $ \x ->+          toGraph % x                == foldg G.Empty G.Vertex G.Overlay G.Connect x++    test "foldg                      == Algebra.Graph.foldg . toGraph" $ \e (apply -> v) (applyFun2 -> o) (applyFun2 -> c) x ->+          foldg e v o c x            == (G.foldg (e :: Int) v o c . toGraph) % x++    test "isEmpty                    == foldg True (const False) (&&) (&&)" $ \x ->+          isEmpty x                  == foldg True (const False) (&&) (&&) % x++    test "size                       == foldg 1 (const 1) (+) (+)" $ \x ->+          size x                     == foldg 1 (const 1) (+) (+) % x++    test "hasVertex x                == foldg False (==x) (||) (||)" $ \x y ->+          hasVertex x y              == foldg False (==x) (||) (||) % y++    test "hasEdge x y                == Algebra.Graph.hasEdge x y . toGraph" $ \x y z ->+          hasEdge x y z              == (G.hasEdge x y . toGraph) % z++    test "vertexCount                == Set.size . vertexSet" $ \x ->+          vertexCount x              == (Set.size . vertexSet) % x++    test "edgeCount                  == Set.size . edgeSet" $ \x ->+          edgeCount x                == (Set.size . edgeSet) % x++    test "vertexList                 == Set.toAscList . vertexSet" $ \x ->+          vertexList x               == (Set.toAscList . vertexSet) % x++    test "edgeList                   == Set.toAscList . edgeSet" $ \x ->+          edgeList x                 == (Set.toAscList . edgeSet) % x++    test "vertexSet                  == foldg Set.empty Set.singleton Set.union Set.union" $ \x ->+          vertexSet x                == foldg Set.empty Set.singleton Set.union Set.union % x++    test "vertexIntSet               == foldg IntSet.empty IntSet.singleton IntSet.union IntSet.union" $ \x ->+          vertexIntSet x             == foldg IntSet.empty IntSet.singleton IntSet.union IntSet.union % x++    test "adjacencyList              == Algebra.Graph.AdjacencyMap.adjacencyList . toAdjacencyMap" $ \x ->+          adjacencyList x            == (AM.adjacencyList . toAdjacencyMap) % x++    test "adjacencyMap               == Algebra.Graph.AdjacencyMap.adjacencyMap . toAdjacencyMap" $ \x ->+          adjacencyMap x             == (AM.adjacencyMap . toAdjacencyMap) % x++    test "adjacencyIntMap            == Algebra.Graph.AdjacencyIntMap.adjacencyIntMap . toAdjacencyIntMap" $ \x ->+          adjacencyIntMap x          == (AIM.adjacencyIntMap . toAdjacencyIntMap) % x++    test "adjacencyMapTranspose      == Algebra.Graph.AdjacencyMap.adjacencyMap . toAdjacencyMapTranspose" $ \x ->+          adjacencyMapTranspose x    == (AM.adjacencyMap . toAdjacencyMapTranspose) % x++    test "adjacencyIntMapTranspose   == Algebra.Graph.AdjacencyIntMap.adjacencyIntMap . toAdjacencyIntMapTranspose" $ \x ->+          adjacencyIntMapTranspose x == (AIM.adjacencyIntMap . toAdjacencyIntMapTranspose) % x++    test "dfsForest                  == Algebra.Graph.AdjacencyMap.dfsForest . toAdjacencyMap" $ \x ->+          dfsForest x                == (AM.dfsForest . toAdjacencyMap) % x++    test "dfsForestFrom vs           == Algebra.Graph.AdjacencyMap.dfsForestFrom vs . toAdjacencyMap" $ \vs x ->+          dfsForestFrom vs x         == (AM.dfsForestFrom vs . toAdjacencyMap) % x++    test "dfs vs                     == Algebra.Graph.AdjacencyMap.dfs vs . toAdjacencyMap" $ \vs x ->+          dfs vs x                   == (AM.dfs vs . toAdjacencyMap) % x++    test "reachable x                == Algebra.Graph.AdjacencyMap.reachable x . toAdjacencyMap" $ \x y ->+          reachable x y              == (AM.reachable x . toAdjacencyMap) % y++    test "topSort                    == Algebra.Graph.AdjacencyMap.topSort . toAdjacencyMap" $ \x ->+          topSort x                  == (AM.topSort . toAdjacencyMap) % x++    test "isAcyclic                  == Algebra.Graph.AdjacencyMap.isAcyclic . toAdjacencyMap" $ \x ->+          isAcyclic x                == (AM.isAcyclic . toAdjacencyMap) % x++    test "isTopSortOf vs             == Algebra.Graph.AdjacencyMap.isTopSortOf vs . toAdjacencyMap" $ \vs x ->+          isTopSortOf vs x           == (AM.isTopSortOf vs . toAdjacencyMap) % x++    test "toAdjacencyMap             == foldg empty vertex overlay connect" $ \x ->+          toAdjacencyMap x           == foldg AM.empty AM.vertex AM.overlay AM.connect % x++    test "toAdjacencyMapTranspose    == foldg empty vertex overlay (flip connect)" $ \x ->+          toAdjacencyMapTranspose x  == foldg AM.empty AM.vertex AM.overlay (flip AM.connect) % x++    test "toAdjacencyIntMap          == foldg empty vertex overlay connect" $ \x ->+          toAdjacencyIntMap x        == foldg AIM.empty AIM.vertex AIM.overlay AIM.connect % x++    test "toAdjacencyIntMapTranspose == foldg empty vertex overlay (flip connect)" $ \x ->+          toAdjacencyIntMapTranspose x == foldg AIM.empty AIM.vertex AIM.overlay (flip AIM.connect) % x++    test "isDfsForestOf f            == Algebra.Graph.AdjacencyMap.isDfsForestOf f . toAdjacencyMap" $ \f x ->+          isDfsForestOf f x          == (AM.isDfsForestOf f . toAdjacencyMap) % x++    test "isTopSortOf vs             == Algebra.Graph.AdjacencyMap.isTopSortOf vs . toAdjacencyMap" $ \vs x ->+          isTopSortOf vs x           == (AM.isTopSortOf vs . toAdjacencyMap) % x+ testFoldg :: Testsuite -> IO () testFoldg (Testsuite prefix (%)) = do     putStrLn $ "\n============ " ++ prefix ++ "foldg ============"@@ -620,6 +911,28 @@         (u, v) <- elements ((x, y) : edgeList z)         return $ hasEdge u v z == elem (u, v) (edgeList % z) +testSymmetricHasEdge :: Testsuite -> IO ()+testSymmetricHasEdge (Testsuite prefix (%)) = do+    putStrLn $ "\n============ " ++ prefix ++ "hasEdge ============"+    test "hasEdge x y empty            == False" $ \x y ->+          hasEdge x y % empty          == False++    test "hasEdge x y (vertex z)       == False" $ \x y z ->+          hasEdge x y % vertex z       == False++    test "hasEdge x y (edge x y)       == True" $ \x y ->+          hasEdge x y % edge x y       == True++    test "hasEdge x y (edge y x)       == True" $ \x y ->+          hasEdge x y % edge y x       == True++    test "hasEdge x y . removeEdge x y == const False" $ \x y z ->+         (hasEdge x y . removeEdge x y) z == const False % z++    test "hasEdge x y                  == elem (min x y, max x y) . edgeList" $ \x y z -> do+        (u, v) <- elements ((x, y) : edgeList z)+        return $ hasEdge u v z == elem (min u v, max u v) (edgeList % z)+ testVertexCount :: Testsuite -> IO () testVertexCount (Testsuite prefix (%)) = do     putStrLn $ "\n============ " ++ prefix ++ "vertexCount ============"@@ -682,6 +995,21 @@     test "edgeList . edges        == nub . sort" $ \xs ->           edgeList % edges xs     == (nubOrd . sort) xs +testSymmetricEdgeList :: Testsuite -> IO ()+testSymmetricEdgeList (Testsuite prefix (%)) = do+    putStrLn $ "\n============ " ++ prefix ++ "edgeList ============"+    test "edgeList empty          == []" $+          edgeList % empty        == []++    test "edgeList (vertex x)     == []" $ \x ->+          edgeList % vertex x     == []++    test "edgeList (edge x y)     == [(min x y, max y x)]" $ \x y ->+          edgeList % edge x y     == [(min x y, max y x)]++    test "edgeList (star 2 [3,1]) == [(1,2), (2,3)]" $+          edgeList % star 2 [3,1] == [(1,2), (2,3)]+ testAdjacencyList :: Testsuite -> IO () testAdjacencyList (Testsuite prefix (%)) = do     putStrLn $ "\n============ " ++ prefix ++ "adjacencyList ============"@@ -697,6 +1025,21 @@     test "adjacencyList (star 2 [3,1]) == [(1, []), (2, [1,3]), (3, [])]" $           adjacencyList % star 2 [3,1] == [(1, []), (2, [1,3]), (3, [])] +testSymmetricAdjacencyList :: Testsuite -> IO ()+testSymmetricAdjacencyList (Testsuite prefix (%)) = do+    putStrLn $ "\n============ " ++ prefix ++ "adjacencyList ============"+    test "adjacencyList empty          == []" $+          adjacencyList % empty        == []++    test "adjacencyList (vertex x)     == [(x, [])]" $ \x ->+          adjacencyList % vertex x     == [(x, [])]++    test "adjacencyList (edge 1 2)     == [(1, [2]), (2, [1])]" $+          adjacencyList % edge 1 2     == [(1, [2]), (2, [1])]++    test "adjacencyList (star 2 [3,1]) == [(1, [2]), (2, [1,3]), (3, [2])]" $+          adjacencyList % star 2 [3,1] == [(1, [2]), (2, [1,3]), (3, [2])]+ testVertexSet :: Testsuite -> IO () testVertexSet (Testsuite prefix (%)) = do     putStrLn $ "\n============ " ++ prefix ++ "vertexSet ============"@@ -739,6 +1082,18 @@     test "edgeSet . edges    == Set.fromList" $ \xs ->           edgeSet % edges xs == Set.fromList xs +testSymmetricEdgeSet :: Testsuite -> IO ()+testSymmetricEdgeSet (Testsuite prefix (%)) = do+    putStrLn $ "\n============ " ++ prefix ++ "edgeSet ============"+    test "edgeSet empty      == Set.empty" $+          edgeSet % empty    == Set.empty++    test "edgeSet (vertex x) == Set.empty" $ \x ->+          edgeSet % vertex x == Set.empty++    test "edgeSet ('edge' x y) == Set.'Set.singleton' (min x y, max x y)" $ \x y ->+          edgeSet % edge x y   == Set.singleton (min x y, max x y)+ testPreSet :: Testsuite -> IO () testPreSet (Testsuite prefix (%)) = do     putStrLn $ "\n============ " ++ prefix ++ "preSet ============"@@ -799,6 +1154,21 @@     test "postIntSet x (edge x y) == IntSet.fromList [y]" $ \x y ->           postIntSet x % edge x y == IntSet.fromList [y] +testNeighbours :: Testsuite -> IO ()+testNeighbours (Testsuite prefix (%)) = do+    putStrLn $ "\n============ " ++ prefix ++ "neighbours ============"+    test "neighbours x empty      == Set.empty" $ \x ->+          neighbours x % empty    == Set.empty++    test "neighbours x (vertex x) == Set.empty" $ \x ->+          neighbours x % vertex x == Set.empty++    test "neighbours x (edge x y) == Set.fromList [y]" $ \x y ->+          neighbours x % edge x y == Set.fromList [y]++    test "neighbours y (edge x y) == Set.fromList [x]" $ \x y ->+          neighbours y % edge x y == Set.fromList [x]+ testPath :: Testsuite -> IO () testPath (Testsuite prefix (%)) = do     putStrLn $ "\n============ " ++ prefix ++ "path ============"@@ -811,6 +1181,12 @@     test "path [x,y] == edge x y" $ \x y ->           path [x,y] == id % edge x y +testSymmetricPath :: Testsuite -> IO ()+testSymmetricPath t@(Testsuite _ (%)) = do+    testPath t+    test "path       == path . reverse" $ \xs ->+          path xs    == id % path (reverse xs)+ testCircuit :: Testsuite -> IO () testCircuit (Testsuite prefix (%)) = do     putStrLn $ "\n============ " ++ prefix ++ "circuit ============"@@ -823,6 +1199,12 @@     test "circuit [x,y] == edges [(x,y), (y,x)]" $ \x y ->           circuit [x,y] == id % edges [(x,y), (y,x)] +testSymmetricCircuit :: Testsuite -> IO ()+testSymmetricCircuit t@(Testsuite _ (%)) = do+    testCircuit t+    test "circuit       == circuit . reverse" $ \xs ->+          circuit xs    == id % circuit (reverse xs)+ testClique :: Testsuite -> IO () testClique (Testsuite prefix (%)) = do     putStrLn $ "\n============ " ++ prefix ++ "clique ============"@@ -841,6 +1223,12 @@     test "clique (xs ++ ys) == connect (clique xs) (clique ys)" $ \xs ys ->           clique (xs ++ ys) == connect (clique xs) % clique ys +testSymmetricClique :: Testsuite -> IO ()+testSymmetricClique t@(Testsuite _ (%)) = do+    testClique t+    test "clique            == clique . reverse" $ \xs->+          clique xs         == id % clique (reverse xs)+ testBiclique :: Testsuite -> IO () testBiclique (Testsuite prefix (%)) = do     putStrLn $ "\n============ " ++ prefix ++ "biclique ============"@@ -944,6 +1332,12 @@     when (prefix == "Fold." || prefix == "Graph.") $ do         test "size (removeEdge x y z)         <= 3 * size z" $ \x y z ->               size % (removeEdge x y z)       <= 3 * size z++testSymmetricRemoveEdge :: Testsuite -> IO ()+testSymmetricRemoveEdge t@(Testsuite _ (%)) = do+    testRemoveEdge t+    test "removeEdge x y                  == removeEdge y x" $ \x y z ->+          removeEdge x y z                == removeEdge y x % z  testReplaceVertex :: Testsuite -> IO () testReplaceVertex (Testsuite prefix (%)) = do
test/Algebra/Graph/Test/Graph.hs view
@@ -14,6 +14,9 @@     testGraph   ) where +import Prelude ()+import Prelude.Compat+ import Data.Either  import Algebra.Graph@@ -21,6 +24,8 @@ import Algebra.Graph.Test.Generic import Algebra.Graph.ToGraph (reachable) +import qualified Data.Graph as KL+ t :: Testsuite t = testsuite "Graph." empty @@ -172,6 +177,26 @@      test "size        (sparsify x) <= 3 * size x" $ \(x :: G) ->           size        (sparsify x) <= 3 * size x++    putStrLn "\n============ Graph.sparsifyKL ============"+    test "sort . reachable k                 == sort . filter (<= n) . flip reachable k . sparsifyKL n" $ \(Positive n) -> do+        let pairs = (,) <$> choose (1, n) <*> choose (1, n)+        k  <- choose (1, n)+        es <- listOf pairs+        let x = vertices [1..n] `overlay` edges es+        return $ (sort . reachable k) x == (sort . filter (<= n) . flip KL.reachable k . sparsifyKL n) x++    test "length (vertices $ sparsifyKL n x) <= vertexCount x + size x + 1" $ \(Positive n) -> do+        let pairs = (,) <$> choose (1, n) <*> choose (1, n)+        es <- listOf pairs+        let x = vertices [1..n] `overlay` edges es+        return $ length (KL.vertices $ sparsifyKL n x) <= vertexCount x + size x + 1++    test "length (edges    $ sparsifyKL n x) <= 3 * size x" $ \(Positive n) -> do+        let pairs = (,) <$> choose (1, n) <*> choose (1, n)+        es <- listOf pairs+        let x = vertices [1..n] `overlay` edges es+        return $ length (KL.edges $ sparsifyKL n x) <= 3 * size x      putStrLn "\n============ Labelled.Graph.context ============"     test "context (const False) x                   == Nothing" $ \x ->
test/Algebra/Graph/Test/NonEmpty/Graph.hs view
@@ -33,6 +33,7 @@  import qualified Algebra.Graph          as G import qualified Algebra.Graph.NonEmpty as NonEmpty+import qualified Data.Graph             as KL import qualified Data.List.NonEmpty     as NonEmpty import qualified Data.Set               as Set @@ -657,6 +658,26 @@      test "size        (sparsify x) <= 3 * size x" $ \(x :: G) ->           size        (sparsify x) <= 3 * size x++    putStrLn "\n============ NonEmpty.Graph.sparsify ============"+    test "sort . reachable k                 == sort . filter (<= n) . flip reachable k . sparsifyKL n" $ \(Positive n) -> do+        let pairs = (,) <$> choose (1, n) <*> choose (1, n)+        k  <- choose (1, n)+        es <- listOf pairs+        let x = G.edges es `overlay1` vertices1 [1..n]+        return $ (sort . reachable k) x == (sort . filter (<= n) . flip KL.reachable k . sparsifyKL n) x++    test "length (vertices $ sparsifyKL n x) <= vertexCount x + size x + 1" $ \(Positive n) -> do+        let pairs = (,) <$> choose (1, n) <*> choose (1, n)+        es <- listOf pairs+        let x = G.edges es `overlay1` vertices1 [1..n]+        return $ length (KL.vertices $ sparsifyKL n x) <= vertexCount x + size x + 1++    test "length (edges    $ sparsifyKL n x) <= 3 * size x" $ \(Positive n) -> do+        let pairs = (,) <$> choose (1, n) <*> choose (1, n)+        es <- listOf pairs+        let x = G.edges es `overlay1` vertices1 [1..n]+        return $ length (KL.edges $ sparsifyKL n x) <= 3 * size x      putStrLn "\n============ NonEmpty.Graph.box ============"     test "box (path1 [0,1]) (path1 ['a','b']) == <correct result>" $ mapSize (min 10) $
test/Algebra/Graph/Test/Relation.hs view
@@ -14,16 +14,13 @@   ) where  import Algebra.Graph.Relation-import Algebra.Graph.Relation.Internal import Algebra.Graph.Relation.Preorder import Algebra.Graph.Relation.Reflexive-import Algebra.Graph.Relation.Symmetric import Algebra.Graph.Relation.Transitive import Algebra.Graph.Test import Algebra.Graph.Test.Generic  import qualified Algebra.Graph.Class as C-import qualified Data.Set            as Set  t :: Testsuite t = testsuite "Relation." empty@@ -35,9 +32,7 @@     putStrLn "\n============ Relation ============"     test "Axioms of graphs" $ size10 (axioms :: GraphTestsuite RI) -    test "Consistency of arbitraryRelation" $ \(m :: RI) ->-        consistent m-+    testConsistent      t     testShow            t     testBasicPrimitives t     testIsSubgraphOf    t@@ -49,23 +44,6 @@     putStrLn "\n============ ReflexiveRelation ============"     test "Axioms of reflexive graphs" $ size10         (reflexiveAxioms :: GraphTestsuite (ReflexiveRelation Int))--    putStrLn "\n============ SymmetricRelation ============"-    test "Axioms of undirected graphs" $ size10-        (undirectedAxioms :: GraphTestsuite (SymmetricRelation Int))--    putStrLn "\n============ SymmetricRelation.neighbours ============"-    test "neighbours x empty      == Set.empty" $ \(x :: Int) ->-          neighbours x C.empty    == Set.empty--    test "neighbours x (vertex x) == Set.empty" $ \(x :: Int) ->-          neighbours x (C.vertex x) == Set.empty--    test "neighbours x (edge x y) == Set.fromList [y]" $ \(x :: Int) y ->-          neighbours x (C.edge x y) == Set.fromList [y]--    test "neighbours y (edge x y) == Set.fromList [x]" $ \(x :: Int) y ->-          neighbours y (C.edge x y) == Set.fromList [x]      putStrLn "\n============ TransitiveRelation ============"     test "Axioms of transitive graphs" $ size10
+ test/Algebra/Graph/Test/Relation/SymmetricRelation.hs view
@@ -0,0 +1,68 @@+-----------------------------------------------------------------------------+-- |+-- Module     : Algebra.Graph.Test.Relation+-- Copyright  : (c) Andrey Mokhov 2016-2019+-- License    : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability  : experimental+--+-- Testsuite for "Algebra.Graph.Relation".+-----------------------------------------------------------------------------+module Algebra.Graph.Test.Relation.SymmetricRelation (+    -- * Testsuite+    testSymmetricRelation+  ) where++import Algebra.Graph.Relation.Symmetric+import Algebra.Graph.Test+import Algebra.Graph.Test.Generic++import qualified Algebra.Graph.Relation as R++t :: Testsuite+t = testsuite "Symmetric.Relation." empty++type RI  = R.Relation Int+type SRI = Relation Int++testSymmetricRelation :: IO ()+testSymmetricRelation = do+    putStrLn "\n============ Symmetric.Relation ============"+    test "Axioms of undirected graphs" $+        size10 (undirectedAxioms :: GraphTestsuite SRI)++    testConsistent    t+    testSymmetricShow t++    putStrLn $ "\n============ Symmetric.Relation.toSymmetric ============"+    test "toSymmetric (edge 1 2)         == edge 1 2" $+          toSymmetric (R.edge 1 2)       == edge 1 (2 :: Int)++    test "toSymmetric . fromSymmetric    == id" $ \(x :: SRI) ->+          (toSymmetric . fromSymmetric) x == id x++    test "fromSymmetric    . toSymmetric == symmetricClosure" $ \(x :: RI) ->+          (fromSymmetric . toSymmetric) x == R.symmetricClosure x++    test "vertexCount      . toSymmetric == vertexCount" $ \(x :: RI) ->+          vertexCount (toSymmetric x) == R.vertexCount x++    test "(*2) . edgeCount . toSymmetric >= edgeCount" $ \(x :: RI) ->+          ((*2) . edgeCount . toSymmetric) x >= R.edgeCount x++    putStrLn $ "\n============ Symmetric.Relation.fromSymmetric ============"+    test "fromSymmetric (edge 1 2)    == edges [(1,2), (2,1)]" $+          fromSymmetric (edge 1 2)    == R.edges [(1,2), (2,1 :: Int)]++    test "vertexCount . fromSymmetric == vertexCount" $ \(x :: SRI) ->+          (R.vertexCount . fromSymmetric) x == vertexCount x++    test "edgeCount   . fromSymmetric <= (*2) . edgeCount" $ \(x :: SRI) ->+          (R.edgeCount . fromSymmetric) x <= ((*2) . edgeCount) x++    testSymmetricBasicPrimitives t+    testSymmetricIsSubgraphOf    t+    testSymmetricToGraph         t+    testSymmetricGraphFamilies   t+    testSymmetricTransformations t+
test/Algebra/Graph/Test/RewriteRules.hs view
@@ -96,3 +96,31 @@ starTR a xs = connect (vertices xs) (vertex a)  inspect $ 'starT1 === 'starTR++fmapFmap1, fmapFmapR :: Graph a -> (a -> b) -> (b -> c) -> Graph c+fmapFmap1 g f h = fmap h (fmap f g)+fmapFmapR g f h = fmap (h . f) g++inspect $ 'fmapFmap1 === 'fmapFmapR++bind2, bind2R :: (a -> Graph b) -> (b -> Graph c) -> Graph a -> Graph c+bind2 f g x = x >>= f >>= g+bind2R f g x = x >>= (\x -> f x >>= g)++inspect $ 'bind2 === 'bind2R++-- Ideally, we want this test to pass.+-- Strangely, '<*>' in 'ovApR' does not inline and makes the test fail.+--+-- This is corrected below, where '<*>' was inlined "by hand"+ovAp, ovApR :: Graph (a -> b) -> Graph (a -> b) -> Graph a -> Graph b+ovAp  x y z = overlay x y <*> z+ovApR x y z = overlay (x <*> z) (y <*> z)++inspect $ 'ovAp =/= 'ovApR++ovAp', ovApR' :: Graph (a -> b) -> Graph (a -> b) -> Graph a -> Graph b+ovAp'  x y z = overlay x y <*> z+ovApR' x y z = overlay (x >>= (<$> z)) (y >>= (<$> z))++inspect $ 'ovAp' === 'ovApR'
test/Main.hs view
@@ -9,19 +9,33 @@ import Algebra.Graph.Test.Labelled.AdjacencyMap import Algebra.Graph.Test.Labelled.Graph import Algebra.Graph.Test.Relation+import Algebra.Graph.Test.Relation.SymmetricRelation import Data.Graph.Test.Typed +import Control.Monad+import System.Environment++-- | By default, all testsuites will be executed, which takes a few minutes. If+-- you would like to execute only some specific testsuites, you can specify+-- their names in the command line. For example:+--+-- stack test --test-arguments "Graph SymmetricRelation"+--+-- will test the modules "Algebra.Graph" and "Algebra.Graph.Symmetric.Relation". main :: IO () main = do-    testAdjacencyIntMap-    testAdjacencyMap-    testExport-    testFold-    testGraph-    testInternal-    testLabelledAdjacencyMap-    testLabelledGraph-    testNonEmptyAdjacencyMap-    testNonEmptyGraph-    testRelation-    testTyped+    selected <- getArgs+    let go current = when (null selected || current `elem` selected)+    go "AdjacencyIntMap"      testAdjacencyIntMap+    go "AdjacencyMap"         testAdjacencyMap+    go "Export"               testExport+    go "Fold"                 testFold+    go "Graph"                testGraph+    go "Internal"             testInternal+    go "LabelledAdjacencyMap" testLabelledAdjacencyMap+    go "LabelledGraph"        testLabelledGraph+    go "NonEmptyAdjacencyMap" testNonEmptyAdjacencyMap+    go "NonEmptyGraph"        testNonEmptyGraph+    go "Relation"             testRelation+    go "SymmetricRelation"    testSymmetricRelation+    go "Typed"                testTyped