algebraic-graphs 0.2 → 0.3
raw patch · 47 files changed
+7626/−2450 lines, 47 filesdep +inspection-testingdep ~QuickCheckPVP ok
version bump matches the API change (PVP)
Dependencies added: inspection-testing
Dependency ranges changed: QuickCheck
API changes (from Hackage documentation)
- Algebra.Graph: adjacencyIntMap :: Graph Int -> IntMap IntSet
- Algebra.Graph: adjacencyMap :: Ord a => Graph a -> Map a (Set a)
- Algebra.Graph: instance Data.Foldable.Foldable Algebra.Graph.Graph
- Algebra.Graph: instance Data.Traversable.Traversable Algebra.Graph.Graph
- Algebra.Graph: vertexIntSet :: Graph Int -> IntSet
- Algebra.Graph.AdjacencyIntMap: dfs :: [Int] -> AdjacencyIntMap -> [Int]
- Algebra.Graph.AdjacencyIntMap: dfsForest :: AdjacencyIntMap -> Forest Int
- Algebra.Graph.AdjacencyIntMap: dfsForestFrom :: [Int] -> AdjacencyIntMap -> Forest Int
- Algebra.Graph.AdjacencyIntMap: isAcyclic :: AdjacencyIntMap -> Bool
- Algebra.Graph.AdjacencyIntMap: isDfsForestOf :: Forest Int -> AdjacencyIntMap -> Bool
- Algebra.Graph.AdjacencyIntMap: isTopSortOf :: [Int] -> AdjacencyIntMap -> Bool
- Algebra.Graph.AdjacencyIntMap: reachable :: Int -> AdjacencyIntMap -> [Int]
- Algebra.Graph.AdjacencyIntMap: topSort :: AdjacencyIntMap -> Maybe [Int]
- Algebra.Graph.AdjacencyIntMap.Internal: connect :: AdjacencyIntMap -> AdjacencyIntMap -> AdjacencyIntMap
- Algebra.Graph.AdjacencyIntMap.Internal: empty :: AdjacencyIntMap
- Algebra.Graph.AdjacencyIntMap.Internal: fromAdjacencyIntSets :: [(Int, IntSet)] -> AdjacencyIntMap
- Algebra.Graph.AdjacencyIntMap.Internal: overlay :: AdjacencyIntMap -> AdjacencyIntMap -> AdjacencyIntMap
- Algebra.Graph.AdjacencyIntMap.Internal: vertex :: Int -> AdjacencyIntMap
- Algebra.Graph.AdjacencyMap: dfs :: Ord a => [a] -> AdjacencyMap a -> [a]
- Algebra.Graph.AdjacencyMap: dfsForest :: Ord a => AdjacencyMap a -> Forest a
- Algebra.Graph.AdjacencyMap: dfsForestFrom :: Ord a => [a] -> AdjacencyMap a -> Forest a
- Algebra.Graph.AdjacencyMap: isAcyclic :: Ord a => AdjacencyMap a -> Bool
- Algebra.Graph.AdjacencyMap: isDfsForestOf :: Ord a => Forest a -> AdjacencyMap a -> Bool
- Algebra.Graph.AdjacencyMap: isTopSortOf :: Ord a => [a] -> AdjacencyMap a -> Bool
- Algebra.Graph.AdjacencyMap: reachable :: Ord a => a -> AdjacencyMap a -> [a]
- Algebra.Graph.AdjacencyMap: scc :: Ord a => AdjacencyMap a -> AdjacencyMap (Set a)
- Algebra.Graph.AdjacencyMap: topSort :: Ord a => AdjacencyMap a -> Maybe [a]
- Algebra.Graph.AdjacencyMap: vertexIntSet :: AdjacencyMap Int -> IntSet
- Algebra.Graph.AdjacencyMap.Internal: connect :: Ord a => AdjacencyMap a -> AdjacencyMap a -> AdjacencyMap a
- Algebra.Graph.AdjacencyMap.Internal: empty :: AdjacencyMap a
- Algebra.Graph.AdjacencyMap.Internal: fromAdjacencySets :: Ord a => [(a, Set a)] -> AdjacencyMap a
- Algebra.Graph.AdjacencyMap.Internal: overlay :: Ord a => AdjacencyMap a -> AdjacencyMap a -> AdjacencyMap a
- Algebra.Graph.AdjacencyMap.Internal: vertex :: a -> AdjacencyMap a
- Algebra.Graph.Class: instance Algebra.Graph.Class.Graph g => Algebra.Graph.Class.Graph (GHC.Base.Maybe g)
- Algebra.Graph.Class: instance Algebra.Graph.Class.Preorder g => Algebra.Graph.Class.Preorder (GHC.Base.Maybe g)
- Algebra.Graph.Class: instance Algebra.Graph.Class.Reflexive g => Algebra.Graph.Class.Reflexive (GHC.Base.Maybe g)
- Algebra.Graph.Class: instance Algebra.Graph.Class.Transitive g => Algebra.Graph.Class.Transitive (GHC.Base.Maybe g)
- Algebra.Graph.Class: instance Algebra.Graph.Class.Undirected g => Algebra.Graph.Class.Undirected (GHC.Base.Maybe g)
- Algebra.Graph.Class: starTranspose :: Graph g => Vertex g -> [Vertex g] -> g
- Algebra.Graph.Fold: instance Data.Foldable.Foldable Algebra.Graph.Fold.Fold
- Algebra.Graph.Fold: instance Data.Traversable.Traversable Algebra.Graph.Fold.Fold
- Algebra.Graph.Fold: vertexIntSet :: Fold Int -> IntSet
- Algebra.Graph.HigherKinded.Class: box :: Graph g => g a -> g b -> g (a, b)
- Algebra.Graph.HigherKinded.Class: hasVertex :: (Eq a, Graph g) => a -> g a -> Bool
- Algebra.Graph.HigherKinded.Class: isEmpty :: Graph g => g a -> Bool
- Algebra.Graph.HigherKinded.Class: starTranspose :: Graph g => a -> [a] -> g a
- Algebra.Graph.HigherKinded.Class: vertexCount :: (Ord a, Graph g) => g a -> Int
- Algebra.Graph.HigherKinded.Class: vertexIntSet :: Graph g => g Int -> IntSet
- Algebra.Graph.HigherKinded.Class: vertexList :: (Ord a, Graph g) => g a -> [a]
- Algebra.Graph.HigherKinded.Class: vertexSet :: (Ord a, Graph g) => g a -> Set a
- Algebra.Graph.Label: (/\) :: Dioid a => a -> a -> a
- Algebra.Graph.Label: (\/) :: Semilattice a => a -> a -> a
- Algebra.Graph.Label: Finite :: a -> Distance a
- Algebra.Graph.Label: Infinite :: Distance a
- Algebra.Graph.Label: class Semilattice a
- Algebra.Graph.Label: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.Label.Distance a)
- Algebra.Graph.Label: instance Algebra.Graph.Label.Dioid GHC.Types.Bool
- Algebra.Graph.Label: instance Algebra.Graph.Label.Semilattice GHC.Types.Bool
- Algebra.Graph.Label: instance GHC.Classes.Ord a => Algebra.Graph.Label.Semilattice (Algebra.Graph.Label.Distance a)
- Algebra.Graph.Label: instance GHC.Classes.Ord a => Algebra.Graph.Label.Semilattice (Data.Set.Internal.Set a)
- Algebra.Graph.Labelled: connectBy :: e -> Graph e a -> Graph e a -> Graph e a
- Algebra.Graph.Labelled: instance Algebra.Graph.Label.Dioid e => Algebra.Graph.Class.Graph (Algebra.Graph.Labelled.Graph e a)
- Algebra.Graph.Labelled: instance Data.Foldable.Foldable (Algebra.Graph.Labelled.Graph e)
- Algebra.Graph.Labelled: instance Data.Traversable.Traversable (Algebra.Graph.Labelled.Graph e)
- Algebra.Graph.NonEmpty: data NonEmptyGraph a
- Algebra.Graph.NonEmpty: instance Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.NonEmpty.NonEmptyGraph a)
- Algebra.Graph.NonEmpty: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.NonEmpty.NonEmptyGraph a)
- Algebra.Graph.NonEmpty: instance Data.Foldable.Foldable Algebra.Graph.NonEmpty.NonEmptyGraph
- Algebra.Graph.NonEmpty: instance Data.Traversable.Traversable Algebra.Graph.NonEmpty.NonEmptyGraph
- Algebra.Graph.NonEmpty: instance GHC.Base.Applicative Algebra.Graph.NonEmpty.NonEmptyGraph
- Algebra.Graph.NonEmpty: instance GHC.Base.Functor Algebra.Graph.NonEmpty.NonEmptyGraph
- Algebra.Graph.NonEmpty: instance GHC.Base.Monad Algebra.Graph.NonEmpty.NonEmptyGraph
- Algebra.Graph.NonEmpty: instance GHC.Classes.Ord a => GHC.Classes.Eq (Algebra.Graph.NonEmpty.NonEmptyGraph a)
- Algebra.Graph.NonEmpty: instance GHC.Num.Num a => GHC.Num.Num (Algebra.Graph.NonEmpty.NonEmptyGraph a)
- Algebra.Graph.NonEmpty: instance GHC.Show.Show a => GHC.Show.Show (Algebra.Graph.NonEmpty.NonEmptyGraph a)
- Algebra.Graph.NonEmpty: toNonEmptyGraph :: Graph a -> Maybe (NonEmptyGraph a)
- Algebra.Graph.NonEmpty: vertexIntSet :: NonEmptyGraph Int -> IntSet
- Algebra.Graph.Relation: preorderClosure :: Ord a => Relation a -> Relation a
- Algebra.Graph.Relation: vertexIntSet :: Relation Int -> IntSet
+ Algebra.Graph: compose :: Ord a => Graph a -> Graph a -> Graph a
+ Algebra.Graph: instance GHC.Classes.Eq a => GHC.Classes.Eq (Algebra.Graph.Context a)
+ Algebra.Graph: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Graph a)
+ Algebra.Graph: instance GHC.Show.Show a => GHC.Show.Show (Algebra.Graph.Context a)
+ Algebra.Graph.AdjacencyIntMap: closure :: AdjacencyIntMap -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: compose :: AdjacencyIntMap -> AdjacencyIntMap -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: fromAdjacencyIntSets :: [(Int, IntSet)] -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: reflexiveClosure :: AdjacencyIntMap -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: symmetricClosure :: AdjacencyIntMap -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: transitiveClosure :: AdjacencyIntMap -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap.Algorithm: dfs :: [Int] -> AdjacencyIntMap -> [Int]
+ Algebra.Graph.AdjacencyIntMap.Algorithm: dfsForest :: AdjacencyIntMap -> Forest Int
+ Algebra.Graph.AdjacencyIntMap.Algorithm: dfsForestFrom :: [Int] -> AdjacencyIntMap -> Forest Int
+ Algebra.Graph.AdjacencyIntMap.Algorithm: isAcyclic :: AdjacencyIntMap -> Bool
+ Algebra.Graph.AdjacencyIntMap.Algorithm: isDfsForestOf :: Forest Int -> AdjacencyIntMap -> Bool
+ Algebra.Graph.AdjacencyIntMap.Algorithm: isTopSortOf :: [Int] -> AdjacencyIntMap -> Bool
+ Algebra.Graph.AdjacencyIntMap.Algorithm: reachable :: Int -> AdjacencyIntMap -> [Int]
+ Algebra.Graph.AdjacencyIntMap.Algorithm: topSort :: AdjacencyIntMap -> Maybe [Int]
+ Algebra.Graph.AdjacencyIntMap.Internal: instance GHC.Classes.Ord Algebra.Graph.AdjacencyIntMap.Internal.AdjacencyIntMap
+ Algebra.Graph.AdjacencyMap: closure :: Ord a => AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.AdjacencyMap: compose :: Ord a => AdjacencyMap a -> AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.AdjacencyMap: fromAdjacencySets :: Ord a => [(a, Set a)] -> AdjacencyMap a
+ Algebra.Graph.AdjacencyMap: reflexiveClosure :: Ord a => AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.AdjacencyMap: symmetricClosure :: Ord a => AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.AdjacencyMap: transitiveClosure :: Ord a => AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.AdjacencyMap.Algorithm: dfs :: Ord a => [a] -> AdjacencyMap a -> [a]
+ Algebra.Graph.AdjacencyMap.Algorithm: dfsForest :: Ord a => AdjacencyMap a -> Forest a
+ Algebra.Graph.AdjacencyMap.Algorithm: dfsForestFrom :: Ord a => [a] -> AdjacencyMap a -> Forest a
+ Algebra.Graph.AdjacencyMap.Algorithm: isAcyclic :: Ord a => AdjacencyMap a -> Bool
+ Algebra.Graph.AdjacencyMap.Algorithm: isDfsForestOf :: Ord a => Forest a -> AdjacencyMap a -> Bool
+ Algebra.Graph.AdjacencyMap.Algorithm: isTopSortOf :: Ord a => [a] -> AdjacencyMap a -> Bool
+ Algebra.Graph.AdjacencyMap.Algorithm: reachable :: Ord a => a -> AdjacencyMap a -> [a]
+ Algebra.Graph.AdjacencyMap.Algorithm: scc :: Ord a => AdjacencyMap a -> AdjacencyMap (AdjacencyMap a)
+ Algebra.Graph.AdjacencyMap.Algorithm: topSort :: Ord a => AdjacencyMap a -> Maybe [a]
+ Algebra.Graph.AdjacencyMap.Internal: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.AdjacencyMap.Internal.AdjacencyMap a)
+ Algebra.Graph.AdjacencyMap.Internal: internalEdgeList :: Map a (Set a) -> [(a, a)]
+ Algebra.Graph.AdjacencyMap.Internal: referredToVertexSet :: Ord a => Map a (Set a) -> Set a
+ Algebra.Graph.Class: -- | The type of graph vertices.
+ Algebra.Graph.Class: instance (Algebra.Graph.Label.Dioid e, GHC.Classes.Eq e, GHC.Classes.Ord a) => Algebra.Graph.Class.Graph (Algebra.Graph.Labelled.AdjacencyMap.Internal.AdjacencyMap e a)
+ Algebra.Graph.Class: instance Algebra.Graph.Class.Graph g => Algebra.Graph.Class.Graph (GHC.Maybe.Maybe g)
+ Algebra.Graph.Class: instance Algebra.Graph.Class.Preorder g => Algebra.Graph.Class.Preorder (GHC.Maybe.Maybe g)
+ Algebra.Graph.Class: instance Algebra.Graph.Class.Reflexive g => Algebra.Graph.Class.Reflexive (GHC.Maybe.Maybe g)
+ Algebra.Graph.Class: instance Algebra.Graph.Class.Transitive g => Algebra.Graph.Class.Transitive (GHC.Maybe.Maybe g)
+ Algebra.Graph.Class: instance Algebra.Graph.Class.Undirected g => Algebra.Graph.Class.Undirected (GHC.Maybe.Maybe g)
+ Algebra.Graph.Class: instance Algebra.Graph.Label.Dioid e => Algebra.Graph.Class.Graph (Algebra.Graph.Labelled.Graph e a)
+ Algebra.Graph.Export: isEmpty :: Doc s -> Bool
+ Algebra.Graph.Fold: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Fold.Fold a)
+ Algebra.Graph.HigherKinded.Class: stars :: Graph g => [(a, [a])] -> g a
+ Algebra.Graph.Internal: maybeF :: (a -> b -> a) -> a -> Maybe b -> Maybe a
+ Algebra.Graph.Internal: setProduct :: Set a -> Set b -> Set (a, b)
+ Algebra.Graph.Internal: setProductWith :: Ord c => (a -> b -> c) -> Set a -> Set b -> Set c
+ Algebra.Graph.Label: (<+>) :: Semigroup a => a -> a -> a
+ Algebra.Graph.Label: (<.>) :: Semiring a => a -> a -> a
+ Algebra.Graph.Label: Optimum :: o -> a -> Optimum o a
+ Algebra.Graph.Label: PowerSet :: Set a -> PowerSet a
+ Algebra.Graph.Label: [getArgument] :: Optimum o a -> a
+ Algebra.Graph.Label: [getOptimum] :: Optimum o a -> o
+ Algebra.Graph.Label: [getPowerSet] :: PowerSet a -> Set a
+ Algebra.Graph.Label: capacity :: NonNegative a -> Capacity a
+ Algebra.Graph.Label: class (Monoid a, Semigroup a) => Semiring a
+ Algebra.Graph.Label: class Semiring a => StarSemiring a
+ Algebra.Graph.Label: count :: NonNegative a -> Count a
+ Algebra.Graph.Label: data Capacity a
+ Algebra.Graph.Label: data Count a
+ Algebra.Graph.Label: data Label a
+ Algebra.Graph.Label: data Minimum a
+ Algebra.Graph.Label: data NonNegative a
+ Algebra.Graph.Label: data Optimum o a
+ Algebra.Graph.Label: distance :: NonNegative a -> Distance a
+ Algebra.Graph.Label: finite :: (Num a, Ord a) => a -> Maybe (NonNegative a)
+ Algebra.Graph.Label: finiteWord :: Word -> NonNegative Word
+ Algebra.Graph.Label: getCapacity :: Capacity a -> NonNegative a
+ Algebra.Graph.Label: getCount :: Count a -> NonNegative a
+ Algebra.Graph.Label: getDistance :: Distance a -> NonNegative a
+ Algebra.Graph.Label: getFinite :: NonNegative a -> Maybe a
+ Algebra.Graph.Label: getMinimum :: Minimum a -> Maybe a
+ Algebra.Graph.Label: infinite :: NonNegative a
+ Algebra.Graph.Label: infixr 6 <+>
+ Algebra.Graph.Label: infixr 7 <.>
+ Algebra.Graph.Label: instance (GHC.Base.Monoid a, GHC.Classes.Ord a) => Algebra.Graph.Label.Dioid (Algebra.Graph.Label.PowerSet a)
+ Algebra.Graph.Label: instance (GHC.Base.Monoid a, GHC.Classes.Ord a) => Algebra.Graph.Label.Semiring (Algebra.Graph.Label.PowerSet a)
+ Algebra.Graph.Label: instance (GHC.Base.Monoid a, GHC.Classes.Ord a) => Algebra.Graph.Label.StarSemiring (Algebra.Graph.Label.PowerSet a)
+ Algebra.Graph.Label: instance (GHC.Classes.Eq o, Algebra.Graph.Label.Dioid a, Algebra.Graph.Label.Dioid o) => Algebra.Graph.Label.Dioid (Algebra.Graph.Label.Optimum o a)
+ Algebra.Graph.Label: instance (GHC.Classes.Eq o, Algebra.Graph.Label.Semiring a, Algebra.Graph.Label.Semiring o) => Algebra.Graph.Label.Semiring (Algebra.Graph.Label.Optimum o a)
+ Algebra.Graph.Label: instance (GHC.Classes.Eq o, Algebra.Graph.Label.StarSemiring a, Algebra.Graph.Label.StarSemiring o) => Algebra.Graph.Label.StarSemiring (Algebra.Graph.Label.Optimum o a)
+ Algebra.Graph.Label: instance (GHC.Classes.Eq o, GHC.Base.Monoid a, GHC.Base.Monoid o) => GHC.Base.Monoid (Algebra.Graph.Label.Optimum o a)
+ Algebra.Graph.Label: instance (GHC.Classes.Eq o, GHC.Base.Monoid a, GHC.Base.Monoid o) => GHC.Base.Semigroup (Algebra.Graph.Label.Optimum o a)
+ Algebra.Graph.Label: instance (GHC.Classes.Eq o, GHC.Classes.Eq a) => GHC.Classes.Eq (Algebra.Graph.Label.Optimum o a)
+ Algebra.Graph.Label: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Base.Monoid (Algebra.Graph.Label.Capacity a)
+ Algebra.Graph.Label: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Base.Monoid (Algebra.Graph.Label.Distance a)
+ Algebra.Graph.Label: instance (GHC.Classes.Ord o, GHC.Classes.Ord a) => GHC.Classes.Ord (Algebra.Graph.Label.Optimum o a)
+ Algebra.Graph.Label: instance (GHC.Num.Num a, GHC.Classes.Ord a) => Algebra.Graph.Label.Dioid (Algebra.Graph.Label.Capacity a)
+ Algebra.Graph.Label: instance (GHC.Num.Num a, GHC.Classes.Ord a) => Algebra.Graph.Label.Semiring (Algebra.Graph.Label.Capacity a)
+ Algebra.Graph.Label: instance (GHC.Num.Num a, GHC.Classes.Ord a) => Algebra.Graph.Label.Semiring (Algebra.Graph.Label.Count a)
+ Algebra.Graph.Label: instance (GHC.Num.Num a, GHC.Classes.Ord a) => Algebra.Graph.Label.Semiring (Algebra.Graph.Label.Distance a)
+ Algebra.Graph.Label: instance (GHC.Num.Num a, GHC.Classes.Ord a) => Algebra.Graph.Label.StarSemiring (Algebra.Graph.Label.Capacity a)
+ Algebra.Graph.Label: instance (GHC.Num.Num a, GHC.Classes.Ord a) => Algebra.Graph.Label.StarSemiring (Algebra.Graph.Label.Count a)
+ Algebra.Graph.Label: instance (GHC.Num.Num a, GHC.Classes.Ord a) => Algebra.Graph.Label.StarSemiring (Algebra.Graph.Label.Distance a)
+ Algebra.Graph.Label: instance (GHC.Num.Num a, GHC.Classes.Ord a) => GHC.Base.Monoid (Algebra.Graph.Label.Count a)
+ Algebra.Graph.Label: instance (GHC.Num.Num a, GHC.Classes.Ord a) => GHC.Base.Semigroup (Algebra.Graph.Label.Count a)
+ Algebra.Graph.Label: instance (GHC.Num.Num a, GHC.Classes.Ord a) => GHC.Num.Num (Algebra.Graph.Label.Capacity a)
+ Algebra.Graph.Label: instance (GHC.Num.Num a, GHC.Classes.Ord a) => GHC.Num.Num (Algebra.Graph.Label.Count a)
+ Algebra.Graph.Label: instance (GHC.Num.Num a, GHC.Classes.Ord a) => GHC.Num.Num (Algebra.Graph.Label.Distance a)
+ Algebra.Graph.Label: instance (GHC.Num.Num a, GHC.Classes.Ord a) => GHC.Num.Num (Algebra.Graph.Label.NonNegative a)
+ Algebra.Graph.Label: instance (GHC.Num.Num a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Label.Minimum a)
+ Algebra.Graph.Label: instance (GHC.Num.Num a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Label.NonNegative a)
+ Algebra.Graph.Label: instance (GHC.Show.Show o, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Label.Optimum o a)
+ Algebra.Graph.Label: instance Algebra.Graph.Label.Dioid Data.Semigroup.Internal.Any
+ Algebra.Graph.Label: instance Algebra.Graph.Label.Semiring (Algebra.Graph.Label.Label a)
+ Algebra.Graph.Label: instance Algebra.Graph.Label.Semiring Data.Semigroup.Internal.Any
+ Algebra.Graph.Label: instance Algebra.Graph.Label.StarSemiring (Algebra.Graph.Label.Label a)
+ Algebra.Graph.Label: instance Algebra.Graph.Label.StarSemiring Data.Semigroup.Internal.Any
+ Algebra.Graph.Label: instance GHC.Base.Applicative Algebra.Graph.Label.Extended
+ Algebra.Graph.Label: instance GHC.Base.Applicative Algebra.Graph.Label.Minimum
+ Algebra.Graph.Label: instance GHC.Base.Applicative Algebra.Graph.Label.NonNegative
+ Algebra.Graph.Label: instance GHC.Base.Functor Algebra.Graph.Label.Extended
+ Algebra.Graph.Label: instance GHC.Base.Functor Algebra.Graph.Label.Label
+ Algebra.Graph.Label: instance GHC.Base.Functor Algebra.Graph.Label.Minimum
+ Algebra.Graph.Label: instance GHC.Base.Functor Algebra.Graph.Label.NonNegative
+ Algebra.Graph.Label: instance GHC.Base.Monad Algebra.Graph.Label.Extended
+ Algebra.Graph.Label: instance GHC.Base.Monad Algebra.Graph.Label.Minimum
+ Algebra.Graph.Label: instance GHC.Base.Monad Algebra.Graph.Label.NonNegative
+ Algebra.Graph.Label: instance GHC.Base.Monoid (Algebra.Graph.Label.Label a)
+ Algebra.Graph.Label: instance GHC.Base.Semigroup (Algebra.Graph.Label.Label a)
+ Algebra.Graph.Label: instance GHC.Classes.Eq a => GHC.Classes.Eq (Algebra.Graph.Label.Capacity a)
+ Algebra.Graph.Label: instance GHC.Classes.Eq a => GHC.Classes.Eq (Algebra.Graph.Label.Count a)
+ Algebra.Graph.Label: instance GHC.Classes.Eq a => GHC.Classes.Eq (Algebra.Graph.Label.Extended a)
+ Algebra.Graph.Label: instance GHC.Classes.Eq a => GHC.Classes.Eq (Algebra.Graph.Label.Minimum a)
+ Algebra.Graph.Label: instance GHC.Classes.Eq a => GHC.Classes.Eq (Algebra.Graph.Label.NonNegative a)
+ Algebra.Graph.Label: instance GHC.Classes.Eq a => GHC.Classes.Eq (Algebra.Graph.Label.PowerSet a)
+ Algebra.Graph.Label: instance GHC.Classes.Ord a => GHC.Base.Monoid (Algebra.Graph.Label.PowerSet a)
+ Algebra.Graph.Label: instance GHC.Classes.Ord a => GHC.Base.Semigroup (Algebra.Graph.Label.Capacity a)
+ Algebra.Graph.Label: instance GHC.Classes.Ord a => GHC.Base.Semigroup (Algebra.Graph.Label.Distance a)
+ Algebra.Graph.Label: instance GHC.Classes.Ord a => GHC.Base.Semigroup (Algebra.Graph.Label.PowerSet a)
+ Algebra.Graph.Label: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Label.Capacity a)
+ Algebra.Graph.Label: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Label.Count a)
+ Algebra.Graph.Label: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Label.Extended a)
+ Algebra.Graph.Label: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Label.Minimum a)
+ Algebra.Graph.Label: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Label.NonNegative a)
+ Algebra.Graph.Label: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Label.PowerSet a)
+ Algebra.Graph.Label: instance GHC.Exts.IsList (Algebra.Graph.Label.Label a)
+ Algebra.Graph.Label: instance GHC.Exts.IsList a => GHC.Exts.IsList (Algebra.Graph.Label.Minimum a)
+ Algebra.Graph.Label: instance GHC.Num.Num a => GHC.Enum.Bounded (Algebra.Graph.Label.Capacity a)
+ Algebra.Graph.Label: instance GHC.Num.Num a => GHC.Enum.Bounded (Algebra.Graph.Label.Count a)
+ Algebra.Graph.Label: instance GHC.Num.Num a => GHC.Enum.Bounded (Algebra.Graph.Label.Distance a)
+ Algebra.Graph.Label: instance GHC.Num.Num a => GHC.Enum.Bounded (Algebra.Graph.Label.NonNegative a)
+ Algebra.Graph.Label: instance GHC.Num.Num a => GHC.Num.Num (Algebra.Graph.Label.Extended a)
+ Algebra.Graph.Label: instance GHC.Show.Show a => GHC.Show.Show (Algebra.Graph.Label.Capacity a)
+ Algebra.Graph.Label: instance GHC.Show.Show a => GHC.Show.Show (Algebra.Graph.Label.Count a)
+ Algebra.Graph.Label: instance GHC.Show.Show a => GHC.Show.Show (Algebra.Graph.Label.Extended a)
+ Algebra.Graph.Label: instance GHC.Show.Show a => GHC.Show.Show (Algebra.Graph.Label.Label a)
+ Algebra.Graph.Label: isZero :: Label a -> Bool
+ Algebra.Graph.Label: newtype PowerSet a
+ Algebra.Graph.Label: noMinimum :: Minimum a
+ Algebra.Graph.Label: star :: StarSemiring a => a -> a
+ Algebra.Graph.Label: type AllShortestPaths e a = Optimum (Distance e) (PowerSet (Path a))
+ Algebra.Graph.Label: type CountShortestPaths e a = Optimum (Distance e) (Count Integer)
+ Algebra.Graph.Label: type Path a = [(a, a)]
+ Algebra.Graph.Label: type RegularExpression a = Label a
+ Algebra.Graph.Label: type ShortestPath e a = Optimum (Distance e) (Minimum (Path a))
+ Algebra.Graph.Label: type WidestPath e a = Optimum (Capacity e) (Minimum (Path a))
+ Algebra.Graph.Label: unsafeFinite :: a -> NonNegative a
+ Algebra.Graph.Labelled: Context :: [(e, a)] -> [(e, a)] -> Context e a
+ Algebra.Graph.Labelled: [inputs] :: Context e a -> [(e, a)]
+ Algebra.Graph.Labelled: [outputs] :: Context e a -> [(e, a)]
+ Algebra.Graph.Labelled: closure :: (Eq e, Ord a, StarSemiring e) => Graph e a -> Graph e a
+ Algebra.Graph.Labelled: context :: (Eq e, Monoid e) => (a -> Bool) -> Graph e a -> Maybe (Context e a)
+ Algebra.Graph.Labelled: data Context e a
+ Algebra.Graph.Labelled: edgeList :: (Eq e, Monoid e, Ord a) => Graph e a -> [(e, a, a)]
+ Algebra.Graph.Labelled: edgeSet :: (Eq e, Monoid e, Ord a) => Graph e a -> Set (e, a, a)
+ Algebra.Graph.Labelled: edges :: Monoid e => [(e, a, a)] -> Graph e a
+ Algebra.Graph.Labelled: emap :: (e -> f) -> Graph e a -> Graph f a
+ Algebra.Graph.Labelled: foldg :: b -> (a -> b) -> (e -> b -> b -> b) -> Graph e a -> b
+ Algebra.Graph.Labelled: hasEdge :: (Eq e, Monoid e, Ord a) => a -> a -> Graph e a -> Bool
+ Algebra.Graph.Labelled: hasVertex :: Eq a => a -> Graph e a -> Bool
+ Algebra.Graph.Labelled: induce :: (a -> Bool) -> Graph e a -> Graph e a
+ Algebra.Graph.Labelled: instance (GHC.Classes.Eq e, GHC.Base.Monoid e, GHC.Classes.Ord a) => GHC.Classes.Eq (Algebra.Graph.Labelled.Graph e a)
+ Algebra.Graph.Labelled: instance (GHC.Classes.Eq e, GHC.Base.Monoid e, GHC.Classes.Ord a, GHC.Classes.Ord e) => GHC.Classes.Ord (Algebra.Graph.Labelled.Graph e a)
+ Algebra.Graph.Labelled: instance (GHC.Classes.Eq e, GHC.Classes.Eq a) => GHC.Classes.Eq (Algebra.Graph.Labelled.Context e a)
+ Algebra.Graph.Labelled: instance (GHC.Classes.Ord a, GHC.Num.Num a, Algebra.Graph.Label.Dioid e) => GHC.Num.Num (Algebra.Graph.Labelled.Graph e a)
+ Algebra.Graph.Labelled: instance (GHC.Show.Show e, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.Labelled.Context e a)
+ Algebra.Graph.Labelled: isEmpty :: Graph e a -> Bool
+ Algebra.Graph.Labelled: isSubgraphOf :: (Eq e, Monoid e, Ord a) => Graph e a -> Graph e a -> Bool
+ Algebra.Graph.Labelled: overlays :: Monoid e => [Graph e a] -> Graph e a
+ Algebra.Graph.Labelled: reflexiveClosure :: (Ord a, Semiring e) => Graph e a -> Graph e a
+ Algebra.Graph.Labelled: removeEdge :: (Eq a, Eq e, Monoid e) => a -> a -> Graph e a -> Graph e a
+ Algebra.Graph.Labelled: removeVertex :: Eq a => a -> Graph e a -> Graph e a
+ Algebra.Graph.Labelled: replaceEdge :: (Eq e, Monoid e, Ord a) => e -> a -> a -> Graph e a -> Graph e a
+ Algebra.Graph.Labelled: replaceVertex :: Eq a => a -> a -> Graph e a -> Graph e a
+ Algebra.Graph.Labelled: size :: Graph e a -> Int
+ Algebra.Graph.Labelled: symmetricClosure :: Monoid e => Graph e a -> Graph e a
+ Algebra.Graph.Labelled: transitiveClosure :: (Eq e, Ord a, StarSemiring e) => Graph e a -> Graph e a
+ Algebra.Graph.Labelled: transpose :: Graph e a -> Graph e a
+ Algebra.Graph.Labelled: type Automaton a s = Graph (RegularExpression a) s
+ Algebra.Graph.Labelled: type Network e a = Graph (Distance e) a
+ Algebra.Graph.Labelled: vertexList :: Ord a => Graph e a -> [a]
+ Algebra.Graph.Labelled: vertexSet :: Ord a => Graph e a -> Set a
+ Algebra.Graph.Labelled: vertices :: Monoid e => [a] -> Graph e a
+ Algebra.Graph.Labelled.AdjacencyMap: (-<) :: a -> e -> (a, e)
+ Algebra.Graph.Labelled.AdjacencyMap: (>-) :: (Eq e, Monoid e, Ord a) => (a, e) -> a -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: adjacencyMap :: AdjacencyMap e a -> Map a (Map a e)
+ Algebra.Graph.Labelled.AdjacencyMap: closure :: (Eq e, Ord a, StarSemiring e) => AdjacencyMap e a -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: connect :: (Eq e, Monoid e, Ord a) => e -> AdjacencyMap e a -> AdjacencyMap e a -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: data AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: edge :: (Eq e, Monoid e, Ord a) => e -> a -> a -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: edgeCount :: AdjacencyMap e a -> Int
+ Algebra.Graph.Labelled.AdjacencyMap: edgeLabel :: (Monoid e, Ord a) => a -> a -> AdjacencyMap e a -> e
+ Algebra.Graph.Labelled.AdjacencyMap: edgeList :: AdjacencyMap e a -> [(e, a, a)]
+ Algebra.Graph.Labelled.AdjacencyMap: edgeSet :: (Eq a, Eq e) => AdjacencyMap e a -> Set (e, a, a)
+ Algebra.Graph.Labelled.AdjacencyMap: edges :: (Eq e, Monoid e, Ord a) => [(e, a, a)] -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: emap :: (Eq f, Monoid f) => (e -> f) -> AdjacencyMap e a -> AdjacencyMap f a
+ Algebra.Graph.Labelled.AdjacencyMap: empty :: AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: fromAdjacencyMaps :: (Eq e, Monoid e, Ord a) => [(a, Map a e)] -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: gmap :: (Eq e, Monoid e, Ord a, Ord b) => (a -> b) -> AdjacencyMap e a -> AdjacencyMap e b
+ Algebra.Graph.Labelled.AdjacencyMap: hasEdge :: Ord a => a -> a -> AdjacencyMap e a -> Bool
+ Algebra.Graph.Labelled.AdjacencyMap: hasVertex :: Ord a => a -> AdjacencyMap e a -> Bool
+ Algebra.Graph.Labelled.AdjacencyMap: induce :: (a -> Bool) -> AdjacencyMap e a -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: infixl 5 >-
+ Algebra.Graph.Labelled.AdjacencyMap: isEmpty :: AdjacencyMap e a -> Bool
+ Algebra.Graph.Labelled.AdjacencyMap: isSubgraphOf :: (Eq e, Monoid e, Ord a) => AdjacencyMap e a -> AdjacencyMap e a -> Bool
+ Algebra.Graph.Labelled.AdjacencyMap: overlay :: (Eq e, Monoid e, Ord a) => AdjacencyMap e a -> AdjacencyMap e a -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: overlays :: (Eq e, Monoid e, Ord a) => [AdjacencyMap e a] -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: postSet :: Ord a => a -> AdjacencyMap e a -> Set a
+ Algebra.Graph.Labelled.AdjacencyMap: preSet :: Ord a => a -> AdjacencyMap e a -> Set a
+ Algebra.Graph.Labelled.AdjacencyMap: reflexiveClosure :: (Ord a, Semiring e) => AdjacencyMap e a -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: removeEdge :: Ord a => a -> a -> AdjacencyMap e a -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: removeVertex :: Ord a => a -> AdjacencyMap e a -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: replaceEdge :: (Eq e, Monoid e, Ord a) => e -> a -> a -> AdjacencyMap e a -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: replaceVertex :: (Eq e, Monoid e, Ord a) => a -> a -> AdjacencyMap e a -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: skeleton :: AdjacencyMap e a -> AdjacencyMap a
+ Algebra.Graph.Labelled.AdjacencyMap: symmetricClosure :: (Eq e, Monoid e, Ord a) => AdjacencyMap e a -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: transitiveClosure :: (Eq e, Ord a, StarSemiring e) => AdjacencyMap e a -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: transpose :: (Monoid e, Ord a) => AdjacencyMap e a -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: vertex :: a -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap: vertexCount :: AdjacencyMap e a -> Int
+ Algebra.Graph.Labelled.AdjacencyMap: vertexList :: AdjacencyMap e a -> [a]
+ Algebra.Graph.Labelled.AdjacencyMap: vertexSet :: AdjacencyMap e a -> Set a
+ Algebra.Graph.Labelled.AdjacencyMap: vertices :: Ord a => [a] -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap.Internal: AM :: Map a (Map a e) -> AdjacencyMap e a
+ Algebra.Graph.Labelled.AdjacencyMap.Internal: [adjacencyMap] :: AdjacencyMap e a -> Map a (Map a e)
+ Algebra.Graph.Labelled.AdjacencyMap.Internal: consistent :: (Ord a, Eq e, Monoid e) => AdjacencyMap e a -> Bool
+ Algebra.Graph.Labelled.AdjacencyMap.Internal: instance (Control.DeepSeq.NFData a, Control.DeepSeq.NFData e) => Control.DeepSeq.NFData (Algebra.Graph.Labelled.AdjacencyMap.Internal.AdjacencyMap e a)
+ Algebra.Graph.Labelled.AdjacencyMap.Internal: instance (GHC.Classes.Eq a, GHC.Classes.Eq e) => GHC.Classes.Eq (Algebra.Graph.Labelled.AdjacencyMap.Internal.AdjacencyMap e a)
+ Algebra.Graph.Labelled.AdjacencyMap.Internal: instance (GHC.Classes.Eq e, Algebra.Graph.Label.Dioid e, GHC.Num.Num a, GHC.Classes.Ord a) => GHC.Num.Num (Algebra.Graph.Labelled.AdjacencyMap.Internal.AdjacencyMap e a)
+ Algebra.Graph.Labelled.AdjacencyMap.Internal: instance (GHC.Classes.Ord a, GHC.Show.Show a, GHC.Classes.Ord e, GHC.Show.Show e) => GHC.Show.Show (Algebra.Graph.Labelled.AdjacencyMap.Internal.AdjacencyMap e a)
+ Algebra.Graph.Labelled.AdjacencyMap.Internal: instance (GHC.Classes.Ord e, GHC.Base.Monoid e, GHC.Classes.Ord a) => GHC.Classes.Ord (Algebra.Graph.Labelled.AdjacencyMap.Internal.AdjacencyMap e a)
+ Algebra.Graph.Labelled.AdjacencyMap.Internal: newtype AdjacencyMap e a
+ Algebra.Graph.Labelled.Example.Automaton: Cancel :: Alphabet
+ Algebra.Graph.Labelled.Example.Automaton: Choice :: State
+ Algebra.Graph.Labelled.Example.Automaton: Coffee :: Alphabet
+ Algebra.Graph.Labelled.Example.Automaton: Complete :: State
+ Algebra.Graph.Labelled.Example.Automaton: Pay :: Alphabet
+ Algebra.Graph.Labelled.Example.Automaton: Payment :: State
+ Algebra.Graph.Labelled.Example.Automaton: Tea :: Alphabet
+ Algebra.Graph.Labelled.Example.Automaton: coffeeTeaAutomaton :: Automaton Alphabet State
+ Algebra.Graph.Labelled.Example.Automaton: data Alphabet
+ Algebra.Graph.Labelled.Example.Automaton: data State
+ Algebra.Graph.Labelled.Example.Automaton: instance GHC.Classes.Eq Algebra.Graph.Labelled.Example.Automaton.Alphabet
+ Algebra.Graph.Labelled.Example.Automaton: instance GHC.Classes.Eq Algebra.Graph.Labelled.Example.Automaton.State
+ Algebra.Graph.Labelled.Example.Automaton: instance GHC.Classes.Ord Algebra.Graph.Labelled.Example.Automaton.Alphabet
+ Algebra.Graph.Labelled.Example.Automaton: instance GHC.Classes.Ord Algebra.Graph.Labelled.Example.Automaton.State
+ Algebra.Graph.Labelled.Example.Automaton: instance GHC.Enum.Bounded Algebra.Graph.Labelled.Example.Automaton.Alphabet
+ Algebra.Graph.Labelled.Example.Automaton: instance GHC.Enum.Bounded Algebra.Graph.Labelled.Example.Automaton.State
+ Algebra.Graph.Labelled.Example.Automaton: instance GHC.Enum.Enum Algebra.Graph.Labelled.Example.Automaton.Alphabet
+ Algebra.Graph.Labelled.Example.Automaton: instance GHC.Enum.Enum Algebra.Graph.Labelled.Example.Automaton.State
+ Algebra.Graph.Labelled.Example.Automaton: instance GHC.Show.Show Algebra.Graph.Labelled.Example.Automaton.Alphabet
+ Algebra.Graph.Labelled.Example.Automaton: instance GHC.Show.Show Algebra.Graph.Labelled.Example.Automaton.State
+ Algebra.Graph.Labelled.Example.Automaton: reachability :: Map State [State]
+ Algebra.Graph.Labelled.Example.Network: Aberdeen :: City
+ Algebra.Graph.Labelled.Example.Network: Edinburgh :: City
+ Algebra.Graph.Labelled.Example.Network: Glasgow :: City
+ Algebra.Graph.Labelled.Example.Network: London :: City
+ Algebra.Graph.Labelled.Example.Network: Newcastle :: City
+ Algebra.Graph.Labelled.Example.Network: data City
+ Algebra.Graph.Labelled.Example.Network: eastCoast :: Network JourneyTime City
+ Algebra.Graph.Labelled.Example.Network: instance GHC.Classes.Eq Algebra.Graph.Labelled.Example.Network.City
+ Algebra.Graph.Labelled.Example.Network: instance GHC.Classes.Ord Algebra.Graph.Labelled.Example.Network.City
+ Algebra.Graph.Labelled.Example.Network: instance GHC.Enum.Bounded Algebra.Graph.Labelled.Example.Network.City
+ Algebra.Graph.Labelled.Example.Network: instance GHC.Enum.Enum Algebra.Graph.Labelled.Example.Network.City
+ Algebra.Graph.Labelled.Example.Network: instance GHC.Show.Show Algebra.Graph.Labelled.Example.Network.City
+ Algebra.Graph.Labelled.Example.Network: network :: Network JourneyTime City
+ Algebra.Graph.Labelled.Example.Network: scotRail :: Network JourneyTime City
+ Algebra.Graph.Labelled.Example.Network: type JourneyTime = Int
+ Algebra.Graph.NonEmpty: data Graph a
+ Algebra.Graph.NonEmpty: instance Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.NonEmpty.Graph a)
+ Algebra.Graph.NonEmpty: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.NonEmpty.Graph a)
+ Algebra.Graph.NonEmpty: instance GHC.Base.Applicative Algebra.Graph.NonEmpty.Graph
+ Algebra.Graph.NonEmpty: instance GHC.Base.Functor Algebra.Graph.NonEmpty.Graph
+ Algebra.Graph.NonEmpty: instance GHC.Base.Monad Algebra.Graph.NonEmpty.Graph
+ Algebra.Graph.NonEmpty: instance GHC.Classes.Ord a => GHC.Classes.Eq (Algebra.Graph.NonEmpty.Graph a)
+ Algebra.Graph.NonEmpty: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.NonEmpty.Graph a)
+ Algebra.Graph.NonEmpty: instance GHC.Num.Num a => GHC.Num.Num (Algebra.Graph.NonEmpty.Graph a)
+ Algebra.Graph.NonEmpty: instance GHC.Show.Show a => GHC.Show.Show (Algebra.Graph.NonEmpty.Graph a)
+ Algebra.Graph.NonEmpty: toNonEmpty :: Graph a -> Maybe (Graph a)
+ Algebra.Graph.NonEmpty.AdjacencyMap: biclique1 :: Ord a => NonEmpty a -> NonEmpty a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: circuit1 :: Ord a => NonEmpty a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: clique1 :: Ord a => NonEmpty a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: closure :: Ord a => AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: connect :: Ord a => AdjacencyMap a -> AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: connects1 :: Ord a => NonEmpty (AdjacencyMap a) -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: data AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: edge :: Ord a => a -> a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: edgeCount :: AdjacencyMap a -> Int
+ Algebra.Graph.NonEmpty.AdjacencyMap: edgeList :: AdjacencyMap a -> [(a, a)]
+ Algebra.Graph.NonEmpty.AdjacencyMap: edgeSet :: Ord a => AdjacencyMap a -> Set (a, a)
+ Algebra.Graph.NonEmpty.AdjacencyMap: edges1 :: Ord a => NonEmpty (a, a) -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: gmap :: (Ord a, Ord b) => (a -> b) -> AdjacencyMap a -> AdjacencyMap b
+ Algebra.Graph.NonEmpty.AdjacencyMap: hasEdge :: Ord a => a -> a -> AdjacencyMap a -> Bool
+ Algebra.Graph.NonEmpty.AdjacencyMap: hasVertex :: Ord a => a -> AdjacencyMap a -> Bool
+ Algebra.Graph.NonEmpty.AdjacencyMap: induce1 :: (a -> Bool) -> AdjacencyMap a -> Maybe (AdjacencyMap a)
+ Algebra.Graph.NonEmpty.AdjacencyMap: isSubgraphOf :: Ord a => AdjacencyMap a -> AdjacencyMap a -> Bool
+ Algebra.Graph.NonEmpty.AdjacencyMap: mergeVertices :: Ord a => (a -> Bool) -> a -> AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: overlay :: Ord a => AdjacencyMap a -> AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: overlays1 :: Ord a => NonEmpty (AdjacencyMap a) -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: path1 :: Ord a => NonEmpty a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: postSet :: Ord a => a -> AdjacencyMap a -> Set a
+ Algebra.Graph.NonEmpty.AdjacencyMap: preSet :: Ord a => a -> AdjacencyMap a -> Set a
+ Algebra.Graph.NonEmpty.AdjacencyMap: reflexiveClosure :: Ord a => AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: removeEdge :: Ord a => a -> a -> AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: removeVertex1 :: Ord a => a -> AdjacencyMap a -> Maybe (AdjacencyMap a)
+ Algebra.Graph.NonEmpty.AdjacencyMap: replaceVertex :: Ord a => a -> a -> AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: star :: Ord a => a -> [a] -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: stars1 :: Ord a => NonEmpty (a, [a]) -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: symmetricClosure :: Ord a => AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: toNonEmpty :: AdjacencyMap a -> Maybe (AdjacencyMap a)
+ Algebra.Graph.NonEmpty.AdjacencyMap: transitiveClosure :: Ord a => AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: transpose :: Ord a => AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: tree :: Ord a => Tree a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: vertex :: a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap: vertexCount :: AdjacencyMap a -> Int
+ Algebra.Graph.NonEmpty.AdjacencyMap: vertexList1 :: AdjacencyMap a -> NonEmpty a
+ Algebra.Graph.NonEmpty.AdjacencyMap: vertexSet :: AdjacencyMap a -> Set a
+ Algebra.Graph.NonEmpty.AdjacencyMap: vertices1 :: Ord a => NonEmpty a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap.Internal: NAM :: AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap.Internal: [am] :: AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.NonEmpty.AdjacencyMap.Internal: consistent :: Ord a => AdjacencyMap a -> Bool
+ Algebra.Graph.NonEmpty.AdjacencyMap.Internal: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.NonEmpty.AdjacencyMap.Internal.AdjacencyMap a)
+ Algebra.Graph.NonEmpty.AdjacencyMap.Internal: instance (GHC.Classes.Ord a, GHC.Show.Show a) => GHC.Show.Show (Algebra.Graph.NonEmpty.AdjacencyMap.Internal.AdjacencyMap a)
+ Algebra.Graph.NonEmpty.AdjacencyMap.Internal: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.NonEmpty.AdjacencyMap.Internal.AdjacencyMap a)
+ Algebra.Graph.NonEmpty.AdjacencyMap.Internal: instance GHC.Classes.Eq a => GHC.Classes.Eq (Algebra.Graph.NonEmpty.AdjacencyMap.Internal.AdjacencyMap a)
+ Algebra.Graph.NonEmpty.AdjacencyMap.Internal: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.NonEmpty.AdjacencyMap.Internal.AdjacencyMap a)
+ Algebra.Graph.NonEmpty.AdjacencyMap.Internal: newtype AdjacencyMap a
+ Algebra.Graph.Relation: closure :: Ord a => Relation a -> Relation a
+ Algebra.Graph.Relation.Internal: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Relation.Internal.Relation a)
+ Algebra.Graph.ToGraph: -- | The type of vertices of the resulting graph.
+ Algebra.Graph.ToGraph: instance (GHC.Classes.Eq e, GHC.Base.Monoid e, GHC.Classes.Ord a) => Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.Labelled.AdjacencyMap.Internal.AdjacencyMap e a)
+ Algebra.Graph.ToGraph: instance (GHC.Classes.Eq e, GHC.Base.Monoid e, GHC.Classes.Ord a) => Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.Labelled.Graph e a)
+ Algebra.Graph.ToGraph: instance GHC.Classes.Ord a => Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.NonEmpty.AdjacencyMap.Internal.AdjacencyMap a)
- Algebra.Graph: Connect :: (Graph a) -> (Graph a) -> Graph a
+ Algebra.Graph: Connect :: Graph a -> Graph a -> Graph a
- Algebra.Graph: Overlay :: (Graph a) -> (Graph a) -> Graph a
+ Algebra.Graph: Overlay :: Graph a -> Graph a -> Graph a
- Algebra.Graph.AdjacencyMap: edgeSet :: Ord a => AdjacencyMap a -> Set (a, a)
+ Algebra.Graph.AdjacencyMap: edgeSet :: Eq a => AdjacencyMap a -> Set (a, a)
- Algebra.Graph.Export: (<+>) :: (Eq s, IsString s, Monoid s) => Doc s -> Doc s -> Doc s
+ Algebra.Graph.Export: (<+>) :: IsString s => Doc s -> Doc s -> Doc s
- Algebra.Graph.Export.Dot: Style :: s -> s -> [Attribute s] -> [Attribute s] -> [Attribute s] -> a -> s -> a -> [Attribute s] -> a -> a -> [Attribute s] -> Style a s
+ Algebra.Graph.Export.Dot: Style :: s -> [s] -> [Attribute s] -> [Attribute s] -> [Attribute s] -> (a -> s) -> (a -> [Attribute s]) -> (a -> a -> [Attribute s]) -> Style a s
- Algebra.Graph.Export.Dot: [preamble] :: Style a s -> s
+ Algebra.Graph.Export.Dot: [preamble] :: Style a s -> [s]
- Algebra.Graph.Export.Dot: export :: (IsString s, Monoid s, Eq s, Ord a, ToGraph g, ToVertex g ~ a) => Style a s -> g -> s
+ Algebra.Graph.Export.Dot: export :: (IsString s, Monoid s, Ord a, ToGraph g, ToVertex g ~ a) => Style a s -> g -> s
- Algebra.Graph.Export.Dot: exportAsIs :: (IsString s, Monoid s, Ord s, ToGraph g, ToVertex g ~ s) => g -> s
+ Algebra.Graph.Export.Dot: exportAsIs :: (IsString s, Monoid s, Ord (ToVertex g), ToGraph g, ToVertex g ~ s) => g -> s
- Algebra.Graph.Export.Dot: exportViaShow :: (IsString s, Monoid s, Eq s, ToGraph g, Ord (ToVertex g), Show (ToVertex g)) => g -> s
+ Algebra.Graph.Export.Dot: exportViaShow :: (IsString s, Monoid s, Ord (ToVertex g), Show (ToVertex g), ToGraph g) => g -> s
- Algebra.Graph.HigherKinded.Class: class (Traversable g, MonadPlus g) => Graph g
+ Algebra.Graph.HigherKinded.Class: class (MonadPlus g) => Graph g
- Algebra.Graph.Internal: List :: (Endo [a]) -> List a
+ Algebra.Graph.Internal: List :: Endo [a] -> List a
- Algebra.Graph.Label: class Semilattice a => Dioid a
+ Algebra.Graph.Label: class Semiring a => Dioid a
- Algebra.Graph.Label: one :: Dioid a => a
+ Algebra.Graph.Label: one :: Semiring a => a
- Algebra.Graph.Label: zero :: Semilattice a => a
+ Algebra.Graph.Label: zero :: Monoid a => a
- Algebra.Graph.Labelled: (-<) :: Graph e a -> e -> (Graph e a, e)
+ Algebra.Graph.Labelled: (-<) :: a -> e -> (a, e)
- Algebra.Graph.Labelled: (>-) :: (Graph e a, e) -> Graph e a -> Graph e a
+ Algebra.Graph.Labelled: (>-) :: (a, e) -> a -> Graph e a
- Algebra.Graph.Labelled: Connect :: e -> (Graph e a) -> (Graph e a) -> Graph e a
+ Algebra.Graph.Labelled: Connect :: e -> Graph e a -> Graph e a -> Graph e a
- Algebra.Graph.Labelled: connect :: Dioid e => Graph e a -> Graph e a -> Graph e a
+ Algebra.Graph.Labelled: connect :: e -> Graph e a -> Graph e a -> Graph e a
- Algebra.Graph.Labelled: edge :: Dioid e => a -> a -> Graph e a
+ Algebra.Graph.Labelled: edge :: e -> a -> a -> Graph e a
- Algebra.Graph.Labelled: edgeLabel :: (Eq a, Semilattice e) => a -> a -> Graph e a -> e
+ Algebra.Graph.Labelled: edgeLabel :: (Eq a, Monoid e) => a -> a -> Graph e a -> e
- Algebra.Graph.Labelled: overlay :: Semilattice e => Graph e a -> Graph e a -> Graph e a
+ Algebra.Graph.Labelled: overlay :: Monoid e => Graph e a -> Graph e a -> Graph e a
- Algebra.Graph.Labelled: type UnlabelledGraph a = Graph Bool a
+ Algebra.Graph.Labelled: type UnlabelledGraph a = Graph Any a
- Algebra.Graph.NonEmpty: (===) :: Eq a => NonEmptyGraph a -> NonEmptyGraph a -> Bool
+ Algebra.Graph.NonEmpty: (===) :: Eq a => Graph a -> Graph a -> Bool
- Algebra.Graph.NonEmpty: Connect :: (NonEmptyGraph a) -> (NonEmptyGraph a) -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: Connect :: Graph a -> Graph a -> Graph a
- Algebra.Graph.NonEmpty: Overlay :: (NonEmptyGraph a) -> (NonEmptyGraph a) -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: Overlay :: Graph a -> Graph a -> Graph a
- Algebra.Graph.NonEmpty: Vertex :: a -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: Vertex :: a -> Graph a
- Algebra.Graph.NonEmpty: biclique1 :: NonEmpty a -> NonEmpty a -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: biclique1 :: NonEmpty a -> NonEmpty a -> Graph a
- Algebra.Graph.NonEmpty: box :: NonEmptyGraph a -> NonEmptyGraph b -> NonEmptyGraph (a, b)
+ Algebra.Graph.NonEmpty: box :: Graph a -> Graph b -> Graph (a, b)
- Algebra.Graph.NonEmpty: circuit1 :: NonEmpty a -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: circuit1 :: NonEmpty a -> Graph a
- Algebra.Graph.NonEmpty: clique1 :: NonEmpty a -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: clique1 :: NonEmpty a -> Graph a
- Algebra.Graph.NonEmpty: connect :: NonEmptyGraph a -> NonEmptyGraph a -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: connect :: Graph a -> Graph a -> Graph a
- Algebra.Graph.NonEmpty: connects1 :: NonEmpty (NonEmptyGraph a) -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: connects1 :: NonEmpty (Graph a) -> Graph a
- Algebra.Graph.NonEmpty: edge :: a -> a -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: edge :: a -> a -> Graph a
- Algebra.Graph.NonEmpty: edgeCount :: Ord a => NonEmptyGraph a -> Int
+ Algebra.Graph.NonEmpty: edgeCount :: Ord a => Graph a -> Int
- Algebra.Graph.NonEmpty: edgeList :: Ord a => NonEmptyGraph a -> [(a, a)]
+ Algebra.Graph.NonEmpty: edgeList :: Ord a => Graph a -> [(a, a)]
- Algebra.Graph.NonEmpty: edgeSet :: Ord a => NonEmptyGraph a -> Set (a, a)
+ Algebra.Graph.NonEmpty: edgeSet :: Ord a => Graph a -> Set (a, a)
- Algebra.Graph.NonEmpty: edges1 :: NonEmpty (a, a) -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: edges1 :: NonEmpty (a, a) -> Graph a
- Algebra.Graph.NonEmpty: foldg1 :: (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> NonEmptyGraph a -> b
+ Algebra.Graph.NonEmpty: foldg1 :: (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Graph a -> b
- Algebra.Graph.NonEmpty: hasEdge :: Eq a => a -> a -> NonEmptyGraph a -> Bool
+ Algebra.Graph.NonEmpty: hasEdge :: Eq a => a -> a -> Graph a -> Bool
- Algebra.Graph.NonEmpty: hasVertex :: Eq a => a -> NonEmptyGraph a -> Bool
+ Algebra.Graph.NonEmpty: hasVertex :: Eq a => a -> Graph a -> Bool
- Algebra.Graph.NonEmpty: induce1 :: (a -> Bool) -> NonEmptyGraph a -> Maybe (NonEmptyGraph a)
+ Algebra.Graph.NonEmpty: induce1 :: (a -> Bool) -> Graph a -> Maybe (Graph a)
- Algebra.Graph.NonEmpty: isSubgraphOf :: Ord a => NonEmptyGraph a -> NonEmptyGraph a -> Bool
+ Algebra.Graph.NonEmpty: isSubgraphOf :: Ord a => Graph a -> Graph a -> Bool
- Algebra.Graph.NonEmpty: mergeVertices :: (a -> Bool) -> a -> NonEmptyGraph a -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: mergeVertices :: (a -> Bool) -> a -> Graph a -> Graph a
- Algebra.Graph.NonEmpty: mesh1 :: NonEmpty a -> NonEmpty b -> NonEmptyGraph (a, b)
+ Algebra.Graph.NonEmpty: mesh1 :: NonEmpty a -> NonEmpty b -> Graph (a, b)
- Algebra.Graph.NonEmpty: overlay :: NonEmptyGraph a -> NonEmptyGraph a -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: overlay :: Graph a -> Graph a -> Graph a
- Algebra.Graph.NonEmpty: overlay1 :: Graph a -> NonEmptyGraph a -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: overlay1 :: Graph a -> Graph a -> Graph a
- Algebra.Graph.NonEmpty: overlays1 :: NonEmpty (NonEmptyGraph a) -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: overlays1 :: NonEmpty (Graph a) -> Graph a
- Algebra.Graph.NonEmpty: path1 :: NonEmpty a -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: path1 :: NonEmpty a -> Graph a
- Algebra.Graph.NonEmpty: removeEdge :: Eq a => a -> a -> NonEmptyGraph a -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: removeEdge :: Eq a => a -> a -> Graph a -> Graph a
- Algebra.Graph.NonEmpty: removeVertex1 :: Eq a => a -> NonEmptyGraph a -> Maybe (NonEmptyGraph a)
+ Algebra.Graph.NonEmpty: removeVertex1 :: Eq a => a -> Graph a -> Maybe (Graph a)
- Algebra.Graph.NonEmpty: replaceVertex :: Eq a => a -> a -> NonEmptyGraph a -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: replaceVertex :: Eq a => a -> a -> Graph a -> Graph a
- Algebra.Graph.NonEmpty: simplify :: Ord a => NonEmptyGraph a -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: simplify :: Ord a => Graph a -> Graph a
- Algebra.Graph.NonEmpty: size :: NonEmptyGraph a -> Int
+ Algebra.Graph.NonEmpty: size :: Graph a -> Int
- Algebra.Graph.NonEmpty: sparsify :: NonEmptyGraph a -> NonEmptyGraph (Either Int a)
+ Algebra.Graph.NonEmpty: sparsify :: Graph a -> Graph (Either Int a)
- Algebra.Graph.NonEmpty: splitVertex1 :: Eq a => a -> NonEmpty a -> NonEmptyGraph a -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: splitVertex1 :: Eq a => a -> NonEmpty a -> Graph a -> Graph a
- Algebra.Graph.NonEmpty: star :: a -> [a] -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: star :: a -> [a] -> Graph a
- Algebra.Graph.NonEmpty: stars1 :: NonEmpty (a, [a]) -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: stars1 :: NonEmpty (a, [a]) -> Graph a
- Algebra.Graph.NonEmpty: torus1 :: NonEmpty a -> NonEmpty b -> NonEmptyGraph (a, b)
+ Algebra.Graph.NonEmpty: torus1 :: NonEmpty a -> NonEmpty b -> Graph (a, b)
- Algebra.Graph.NonEmpty: transpose :: NonEmptyGraph a -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: transpose :: Graph a -> Graph a
- Algebra.Graph.NonEmpty: tree :: Tree a -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: tree :: Tree a -> Graph a
- Algebra.Graph.NonEmpty: vertex :: a -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: vertex :: a -> Graph a
- Algebra.Graph.NonEmpty: vertexCount :: Ord a => NonEmptyGraph a -> Int
+ Algebra.Graph.NonEmpty: vertexCount :: Ord a => Graph a -> Int
- Algebra.Graph.NonEmpty: vertexList1 :: Ord a => NonEmptyGraph a -> NonEmpty a
+ Algebra.Graph.NonEmpty: vertexList1 :: Ord a => Graph a -> NonEmpty a
- Algebra.Graph.NonEmpty: vertexSet :: Ord a => NonEmptyGraph a -> Set a
+ Algebra.Graph.NonEmpty: vertexSet :: Ord a => Graph a -> Set a
- Algebra.Graph.NonEmpty: vertices1 :: NonEmpty a -> NonEmptyGraph a
+ Algebra.Graph.NonEmpty: vertices1 :: NonEmpty a -> Graph a
- Data.Graph.Typed: GraphKL :: Graph -> Vertex -> a -> a -> Maybe Vertex -> GraphKL a
+ Data.Graph.Typed: GraphKL :: Graph -> (Vertex -> a) -> (a -> Maybe Vertex) -> GraphKL a
Files
- CHANGES.md +23/−0
- README.md +17/−0
- algebraic-graphs.cabal +47/−18
- src/Algebra/Graph.hs +348/−169
- src/Algebra/Graph/AdjacencyIntMap.hs +183/−179
- src/Algebra/Graph/AdjacencyIntMap/Algorithm.hs +198/−0
- src/Algebra/Graph/AdjacencyIntMap/Internal.hs +73/−101
- src/Algebra/Graph/AdjacencyMap.hs +189/−220
- src/Algebra/Graph/AdjacencyMap/Algorithm.hs +235/−0
- src/Algebra/Graph/AdjacencyMap/Internal.hs +84/−111
- src/Algebra/Graph/Class.hs +26/−23
- src/Algebra/Graph/Export.hs +41/−14
- src/Algebra/Graph/Export/Dot.hs +10/−10
- src/Algebra/Graph/Fold.hs +74/−61
- src/Algebra/Graph/HigherKinded/Class.hs +44/−134
- src/Algebra/Graph/Internal.hs +36/−10
- src/Algebra/Graph/Label.hs +414/−66
- src/Algebra/Graph/Labelled.hs +569/−57
- src/Algebra/Graph/Labelled/AdjacencyMap.hs +612/−0
- src/Algebra/Graph/Labelled/AdjacencyMap/Internal.hs +113/−0
- src/Algebra/Graph/Labelled/Example/Automaton.hs +84/−0
- src/Algebra/Graph/Labelled/Example/Network.hs +64/−0
- src/Algebra/Graph/NonEmpty.hs +274/−232
- src/Algebra/Graph/NonEmpty/AdjacencyMap.hs +568/−0
- src/Algebra/Graph/NonEmpty/AdjacencyMap/Internal.hs +163/−0
- src/Algebra/Graph/Relation.hs +74/−67
- src/Algebra/Graph/Relation/Internal.hs +62/−17
- src/Algebra/Graph/Relation/InternalDerived.hs +3/−3
- src/Algebra/Graph/Relation/Preorder.hs +1/−1
- src/Algebra/Graph/ToGraph.hs +123/−20
- src/Data/Graph/Typed.hs +2/−2
- test/Algebra/Graph/Test.hs +2/−6
- test/Algebra/Graph/Test/API.hs +119/−79
- test/Algebra/Graph/Test/AdjacencyIntMap.hs +1/−0
- test/Algebra/Graph/Test/AdjacencyMap.hs +25/−12
- test/Algebra/Graph/Test/Arbitrary.hs +89/−31
- test/Algebra/Graph/Test/Export.hs +21/−9
- test/Algebra/Graph/Test/Generic.hs +234/−63
- test/Algebra/Graph/Test/Graph.hs +23/−0
- test/Algebra/Graph/Test/Labelled/AdjacencyMap.hs +475/−0
- test/Algebra/Graph/Test/Labelled/Graph.hs +465/−0
- test/Algebra/Graph/Test/NonEmpty/AdjacencyMap.hs +613/−0
- test/Algebra/Graph/Test/NonEmpty/Graph.hs +690/−0
- test/Algebra/Graph/Test/NonEmptyGraph.hs +0/−665
- test/Algebra/Graph/Test/Relation.hs +8/−67
- test/Algebra/Graph/Test/RewriteRules.hs +98/−0
- test/Main.hs +9/−3
CHANGES.md view
@@ -1,5 +1,28 @@ # 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`.
README.md view
@@ -54,6 +54,18 @@ To represent *non-empty graphs*, we can drop the `Empty` constructor -- see module [Algebra.Graph.NonEmpty](http://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph-NonEmpty.html). +To represent *edge-labelled graphs*, we can switch to the following data type, as+explained in my [Haskell eXchange 2018 talk](https://skillsmatter.com/skillscasts/12361-labelled-algebraic-graphs):++```haskell+data Graph e a = Empty+ | Vertex a+ | Connect e (Graph e a) (Graph e a)+```++Here `e` is the type of edge labels. If `e` is a monoid `(<+>, zero)` then graph overlay can be recovered+as `Connect zero`, and `<+>` corresponds to *parallel composition* of edge labels.+ ## How fast is the library? Alga can handle graphs comprising millions of vertices and billions of edges in a matter of seconds, which is fast@@ -69,3 +81,8 @@ * A few different flavours of the algebra: https://blogs.ncl.ac.uk/andreymokhov/graphs-a-la-carte/ * Graphs in disguise or How to plan you holiday using Haskell: https://blogs.ncl.ac.uk/andreymokhov/graphs-in-disguise/ * Old graphs from new types: https://blogs.ncl.ac.uk/andreymokhov/old-graphs-from-new-types/++## Algebraic graphs in other languages++See draft implementations in [Agda](http://github.com/algebraic-graphs/agda)+and [Scala](http://github.com/algebraic-graphs/scala).
algebraic-graphs.cabal view
@@ -1,5 +1,5 @@ name: algebraic-graphs-version: 0.2+version: 0.3 synopsis: A library for algebraic graph construction and transformation license: MIT license-file: LICENSE@@ -25,24 +25,34 @@ . The top-level module <http://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph.html Algebra.Graph>- defines the core data type+ defines the main data type for /algebraic graphs/ <http://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph.html#t:Graph Graph>,- which is a deep embedding of four graph construction primitives /empty/,- /vertex/, /overlay/ and /connect/. To represent non-empty graphs, see+ as well as associated algorithms. For type-safe representation and+ manipulation of /non-empty algebraic graphs/, see <http://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph-NonEmpty.html Algebra.Graph.NonEmpty>.- More conventional graph representations can be found in+ Furthermore, /algebraic graphs with edge labels/ are implemented in+ <http://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph-Labelled.html Algebra.Graph.Labelled>.+ .+ The library also provides conventional graph data structures, such as <http://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph-AdjacencyMap.html Algebra.Graph.AdjacencyMap>- and- <http://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph-Relation.html Algebra.Graph.Relation>.+ along with its various flavours: adjacency maps specialised to graphs with+ vertices of type 'Int'+ (<http://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph-AdjacencyIntMap.html Algebra.Graph.AdjacencyIntMap>),+ non-empty adjacency maps+ (<http://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph-NonEmpty-AdjacencyMap.html Algebra.Graph.NonEmpty.AdjacencyMap>),+ and adjacency maps with edge labels+ (<http://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph-Labelled-AdjacencyMap.html Algebra.Graph.Labelled.AdjacencyMap>).+ A large part of the API of algebraic graphs and adjacency maps is available+ through the 'Foldable'-like type class+ <http://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph-ToGraph.html Algebra.Graph.ToGraph>. . The type classes defined in <http://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph-Class.html Algebra.Graph.Class> and <http://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph-HigherKinded-Class.html Algebra.Graph.HigherKinded.Class>- can be used for polymorphic graph construction and manipulation. Also see+ can be used for polymorphic construction and manipulation of graphs. Also see <http://hackage.haskell.org/package/algebraic-graphs/docs/Algebra-Graph-Fold.html Algebra.Graph.Fold>- that defines the Boehm-Berarducci encoding of algebraic graphs and provides additional- flexibility for polymorphic graph manipulation.+ that defines the Boehm-Berarducci encoding of algebraic graphs. . This is an experimental library and the API is expected to remain unstable until version 1.0.0. Please consider contributing to the on-going@@ -59,19 +69,27 @@ library hs-source-dirs: src exposed-modules: Algebra.Graph,+ Algebra.Graph.AdjacencyIntMap,+ Algebra.Graph.AdjacencyIntMap.Algorithm,+ Algebra.Graph.AdjacencyIntMap.Internal, Algebra.Graph.AdjacencyMap,+ Algebra.Graph.AdjacencyMap.Algorithm, Algebra.Graph.AdjacencyMap.Internal, Algebra.Graph.Class, Algebra.Graph.Export, Algebra.Graph.Export.Dot, Algebra.Graph.Fold, Algebra.Graph.HigherKinded.Class,- Algebra.Graph.AdjacencyIntMap,- Algebra.Graph.AdjacencyIntMap.Internal, Algebra.Graph.Internal, Algebra.Graph.Label, Algebra.Graph.Labelled,+ Algebra.Graph.Labelled.AdjacencyMap,+ Algebra.Graph.Labelled.AdjacencyMap.Internal,+ Algebra.Graph.Labelled.Example.Automaton,+ Algebra.Graph.Labelled.Example.Network, Algebra.Graph.NonEmpty,+ Algebra.Graph.NonEmpty.AdjacencyMap,+ Algebra.Graph.NonEmpty.AdjacencyMap.Internal, Algebra.Graph.Relation, Algebra.Graph.Relation.Internal, Algebra.Graph.Relation.InternalDerived,@@ -91,18 +109,18 @@ build-depends: semigroups >= 0.18.3 && < 0.18.4 default-language: Haskell2010 default-extensions: FlexibleContexts+ FlexibleInstances GeneralizedNewtypeDeriving ScopedTypeVariables TupleSections TypeFamilies other-extensions: CPP- DeriveFoldable DeriveFunctor- DeriveTraversable OverloadedStrings RecordWildCards GHC-options: -Wall -fno-warn-name-shadowing+ -fspec-constr if impl(ghc >= 8.0) GHC-options: -Wcompat -Wincomplete-record-updates@@ -115,17 +133,22 @@ main-is: Main.hs other-modules: Algebra.Graph.Test, Algebra.Graph.Test.API,+ Algebra.Graph.Test.AdjacencyIntMap, Algebra.Graph.Test.AdjacencyMap, Algebra.Graph.Test.Arbitrary, Algebra.Graph.Test.Export, Algebra.Graph.Test.Fold, Algebra.Graph.Test.Generic, Algebra.Graph.Test.Graph,- Algebra.Graph.Test.AdjacencyIntMap, Algebra.Graph.Test.Internal,- Algebra.Graph.Test.NonEmptyGraph,+ Algebra.Graph.Test.Labelled.AdjacencyMap,+ Algebra.Graph.Test.Labelled.Graph,+ Algebra.Graph.Test.NonEmpty.AdjacencyMap,+ Algebra.Graph.Test.NonEmpty.Graph, Algebra.Graph.Test.Relation, Data.Graph.Test.Typed+ if impl(ghc >= 8.0.2)+ other-modules: Algebra.Graph.Test.RewriteRules build-depends: algebraic-graphs, array >= 0.4 && < 0.6, base >= 4.7 && < 5,@@ -133,21 +156,27 @@ base-orphans >= 0.5.4 && < 0.9, containers >= 0.5.5.1 && < 0.8, extra >= 1.5 && < 2,- QuickCheck >= 2.9 && < 2.12+ QuickCheck >= 2.9 && < 2.13 if !impl(ghc >= 8.0) build-depends: semigroups >= 0.18.3 && < 0.18.4+ if impl(ghc >= 8.0.2)+ build-depends: inspection-testing >= 0.4 && < 0.5+ default-language: Haskell2010 GHC-options: -Wall -fno-warn-name-shadowing+ -fspec-constr if impl(ghc >= 8.0) GHC-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints default-extensions: FlexibleContexts+ FlexibleInstances GeneralizedNewtypeDeriving- TypeFamilies ScopedTypeVariables+ TupleSections+ TypeFamilies other-extensions: ConstrainedClassMethods ConstraintKinds RankNTypes
src/Algebra/Graph.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}+{-# LANGUAGE RankNTypes #-} ----------------------------------------------------------------------------- -- | -- Module : Algebra.Graph@@ -33,8 +33,7 @@ -- * Graph properties isEmpty, size, hasVertex, hasEdge, vertexCount, edgeCount, vertexList,- edgeList, vertexSet, vertexIntSet, edgeSet, adjacencyList, adjacencyMap,- adjacencyIntMap,+ edgeList, vertexSet, edgeSet, adjacencyList, -- * Standard families of graphs path, circuit, clique, biclique, star, stars, tree, forest, mesh, torus,@@ -45,14 +44,14 @@ transpose, induce, simplify, sparsify, -- * Graph composition- box,+ compose, box, -- * Context Context (..), context- ) where+ ) where import Prelude ()-import Prelude.Compat+import Prelude.Compat hiding ((<>)) import Control.Applicative (Alternative) import Control.DeepSeq (NFData (..))@@ -60,15 +59,11 @@ import Control.Monad.State (runState, get, put) import Data.Foldable (toList) import Data.Maybe (fromMaybe)+import Data.Monoid ((<>)) import Data.Tree import Algebra.Graph.Internal -import Data.IntMap (IntMap)-import Data.IntSet (IntSet)-import Data.Map (Map)-import Data.Set (Set)- import qualified Algebra.Graph.AdjacencyMap as AM import qualified Algebra.Graph.AdjacencyIntMap as AIM import qualified Control.Applicative as Ap@@ -86,6 +81,14 @@ > 1 + 2 * 3 == Overlay (Vertex 1) (Connect (Vertex 2) (Vertex 3)) > 1 * (2 + 3) == Connect (Vertex 1) (Overlay (Vertex 2) (Vertex 3)) +__Note:__ the 'Num' instance does not satisfy several "customary laws" of 'Num',+which dictate that 'fromInteger' @0@ and 'fromInteger' @1@ should act as+additive and multiplicative identities, and 'negate' as additive inverse.+Nevertheless, overloading 'fromInteger', '+' and '*' is very convenient when+working with algebraic graphs; we hope that in future Haskell's Prelude will+provide a more fine-grained class hierarchy for algebraic structures, which we+would be able to utilise without violating any laws.+ The 'Eq' instance is currently implemented using the 'AM.AdjacencyMap' as the /canonical graph representation/ and satisfies all axioms of algebraic graphs: @@ -132,37 +135,96 @@ m == 'edgeCount' g s == 'size' g@ -Note that 'size' is slightly different from the 'length' method of the-'Foldable' type class, as the latter does not count 'empty' leaves of the-expression:--@'length' 'empty' == 0-'size' 'empty' == 1-'length' ('vertex' x) == 1-'size' ('vertex' x) == 1-'length' ('empty' + 'empty') == 0-'size' ('empty' + 'empty') == 2@+Note that 'size' counts all leaves of the expression: -The 'size' of any graph is positive, and the difference @('size' g - 'length' g)@-corresponds to the number of occurrences of 'empty' in an expression @g@.+@'vertexCount' 'empty' == 0+'size' 'empty' == 1+'vertexCount' ('vertex' x) == 1+'size' ('vertex' x) == 1+'vertexCount' ('empty' + 'empty') == 0+'size' ('empty' + 'empty') == 2@ Converting a 'Graph' to the corresponding 'AM.AdjacencyMap' takes /O(s + m * log(m))/-time and /O(s + m)/ memory. This is also the complexity of the graph equality test,-because it is currently implemented by converting graph expressions to canonical-representations based on adjacency maps.+time and /O(s + m)/ memory. This is also the complexity of the graph equality+test, because it is currently implemented by converting graph expressions to+canonical representations based on adjacency maps.++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 < 'edge' 1 2+'vertex' 1 < 'edge' 1 1+'edge' 1 1 < 'edge' 1 2+'edge' 1 2 < 'edge' 1 1 + 'edge' 2 2+'edge' 1 2 < 'edge' 1 3@++Note that the resulting order refines the 'isSubgraphOf' relation and is+compatible with 'overlay' and 'connect' operations:++@'isSubgraphOf' x y ==> x <= y@++@'empty' <= x+x <= x + y+x + y <= x * y@ -} data Graph a = Empty | Vertex a | Overlay (Graph a) (Graph a) | Connect (Graph a) (Graph a)- deriving (Foldable, Functor, Show, Traversable)+ deriving (Show) +{- Note [Functions for rewrite rules]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++This module contains several functions whose only purpose is to guide GHC+rewrite rules. The names of all such functions are suffixed with "R" so that it+is easier to distinguish them from others.++Why do we need them?++These functions are annotated with carefully chosen GHC pragmas that control+inlining, which would be impossible or unreliable if we used standard functions+instead. For example, the function 'eqR' has the following annotations:++ NOINLINE [1] eqR+ RULES "eqIntR" eqR = eqIntR++This tells GHC to rewrite 'eqR' to faster 'eqIntR' if possible (if the types+match), and -- importantly -- not to inline 'eqR' too early, before the rewrite+rule had a chance to fire.++We could have written the following rule instead:++ RULES "eqIntR" (==) = eqIntR++But that would have to rely on appropriate inlining behaviour of (==) which is+not under our control. We therefore choose the safe and more explicit path of+creating our own intermediate functions for guiding rewrite rules when needed.+-}++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 #-}+ instance NFData a => NFData (Graph a) where rnf Empty = () rnf (Vertex x ) = rnf x rnf (Overlay x y) = rnf x `seq` rnf y rnf (Connect x y) = rnf x `seq` rnf y +-- | __Note:__ this does not satisfy the usual ring laws; see 'Graph' for more+-- details. instance Num a => Num (Graph a) where fromInteger = Vertex . fromInteger (+) = Overlay@@ -172,19 +234,33 @@ negate = id instance Ord a => Eq (Graph a) where- (==) = equals+ (==) = eqR +instance Ord a => Ord (Graph a) where+ compare = ordR+ -- TODO: Find a more efficient equality check.--- | Compare two graphs by converting them to their adjacency maps.-{-# NOINLINE [1] equals #-}-{-# RULES "equalsInt" equals = equalsInt #-}-equals :: Ord a => Graph a -> Graph a -> Bool-equals x y = adjacencyMap x == adjacencyMap y+-- Check if two graphs are equal by converting them to their adjacency maps.+eqR :: Ord a => Graph a -> Graph a -> Bool+eqR x y = toAdjacencyMap x == toAdjacencyMap y+{-# NOINLINE [1] eqR #-}+{-# RULES "eqR/Int" eqR = eqIntR #-} --- | Like @equals@ but specialised for graphs with vertices of type 'Int'.-equalsInt :: Graph Int -> Graph Int -> Bool-equalsInt x y = adjacencyIntMap x == adjacencyIntMap y+-- Like 'eqR' but specialised for graphs with vertices of type 'Int'.+eqIntR :: Graph Int -> Graph Int -> Bool+eqIntR x y = toAdjacencyIntMap x == toAdjacencyIntMap y +-- TODO: Find a more efficient comparison.+-- Compare two graphs by converting them to their adjacency maps.+ordR :: Ord a => Graph a -> Graph a -> Ordering+ordR x y = compare (toAdjacencyMap x) (toAdjacencyMap y)+{-# NOINLINE [1] ordR #-}+{-# RULES "ordR/Int" ordR = ordIntR #-}++-- Like 'ordR' but specialised for graphs with vertices of type 'Int'.+ordIntR :: Graph Int -> Graph Int -> Ordering+ordIntR x y = compare (toAdjacencyIntMap x) (toAdjacencyIntMap y)+ instance Applicative Graph where pure = Vertex (<*>) = ap@@ -299,7 +375,7 @@ -- @ vertices :: [a] -> Graph a vertices = overlays . map vertex-{-# NOINLINE [1] vertices #-}+{-# INLINE vertices #-} -- | Construct the graph from a list of edges. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the@@ -325,8 +401,8 @@ -- 'isEmpty' . overlays == 'all' 'isEmpty' -- @ overlays :: [Graph a] -> Graph a-overlays = concatg overlay-{-# INLINE [2] overlays #-}+overlays = fromMaybe empty . foldr1Safe overlay+{-# INLINE [1] overlays #-} -- | Connect a given list of graphs. -- Complexity: /O(L)/ time and memory, and /O(S)/ size, where /L/ is the length@@ -340,12 +416,8 @@ -- 'isEmpty' . connects == 'all' 'isEmpty' -- @ connects :: [Graph a] -> Graph a-connects = concatg connect-{-# INLINE [2] connects #-}---- | Auxiliary function, similar to 'mconcat'.-concatg :: (Graph a -> Graph a -> Graph a) -> [Graph a] -> Graph a-concatg combine = fromMaybe empty . foldr1Safe combine+connects = fromMaybe empty . foldr1Safe connect+{-# INLINE [1] connects #-} -- | Generalised 'Graph' folding: recursively collapse a 'Graph' by applying -- the provided functions to the leaves and internal nodes of the expression.@@ -355,11 +427,10 @@ -- -- @ -- foldg 'empty' 'vertex' 'overlay' 'connect' == id--- foldg 'empty' 'vertex' 'overlay' (flip 'connect') == 'transpose'--- foldg [] return (++) (++) == 'Data.Foldable.toList'--- foldg 0 (const 1) (+) (+) == 'Data.Foldable.length'--- foldg 1 (const 1) (+) (+) == 'size'--- foldg True (const False) (&&) (&&) == 'isEmpty'+-- foldg 'empty' 'vertex' 'overlay' ('flip' 'connect') == 'transpose'+-- foldg 1 ('const' 1) (+) (+) == 'size'+-- foldg True ('const' False) (&&) (&&) == 'isEmpty'+-- foldg False (== x) (||) (||) == 'hasVertex' x -- @ foldg :: b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Graph a -> b foldg e v o c = go@@ -368,22 +439,40 @@ go (Vertex x ) = v x go (Overlay x y) = o (go x) (go y) go (Connect x y) = c (go x) (go y)+{-# INLINE [0] foldg #-} +{-# RULES+"foldg/Empty" forall e v o c.+ foldg e v o c Empty = e+"foldg/Vertex" forall e v o c x.+ foldg e v o c (Vertex x) = v x+"foldg/Overlay" forall e v o c x y.+ foldg e v o c (Overlay x y) = o (foldg e v o c x) (foldg e v o c y)+"foldg/Connect" forall e v o c x y.+ foldg e v o c (Connect x y) = c (foldg e v o c x) (foldg e v o c y)++"foldg/overlays" forall e v o c xs.+ foldg e v o c (overlays xs) = fromMaybe e (foldr (maybeF o . foldg e v o c) Nothing xs)+"foldg/connects" forall e v o c xs.+ foldg e v o c (connects xs) = fromMaybe e (foldr (maybeF c . foldg e v o c) Nothing xs)+ #-}+ -- | The 'isSubgraphOf' function takes two graphs and returns 'True' if the -- first graph is a /subgraph/ of the second. -- Complexity: /O(s + m * log(m))/ time. Note that the number of edges /m/ of a -- graph can be quadratic with respect to the expression size /s/. -- -- @--- 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 '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 x y ==> x <= y -- @-{-# SPECIALISE isSubgraphOf :: Graph Int -> Graph Int -> Bool #-} isSubgraphOf :: Ord a => Graph a -> Graph a -> Bool isSubgraphOf x y = overlay x y == y+{-# SPECIALISE isSubgraphOf :: Graph Int -> Graph Int -> Bool #-} -- | Structural equality on graph expressions. -- Complexity: /O(s)/ time.@@ -395,13 +484,13 @@ -- 1 + 2 === 2 + 1 == False -- x + y === x * y == False -- @-{-# SPECIALISE (===) :: Graph Int -> Graph Int -> Bool #-} (===) :: Eq a => Graph a -> Graph a -> Bool Empty === Empty = True (Vertex x1 ) === (Vertex x2 ) = x1 == x2 (Overlay x1 y1) === (Overlay x2 y2) = x1 === x2 && y1 === y2 (Connect x1 y1) === (Connect x2 y2) = x1 === x2 && y1 === y2 _ === _ = False+{-# SPECIALISE (===) :: Graph Int -> Graph Int -> Bool #-} infix 4 === @@ -433,18 +522,18 @@ size :: Graph a -> Int size = foldg 1 (const 1) (+) (+) --- | Check if a graph contains a given vertex. A convenient alias for `elem`.+-- | Check if a graph contains a given vertex. -- Complexity: /O(s)/ time. -- -- @ -- hasVertex x 'empty' == False -- hasVertex x ('vertex' x) == True -- hasVertex 1 ('vertex' 2) == False--- hasVertex x . 'removeVertex' x == const False+-- hasVertex x . 'removeVertex' x == 'const' False -- @-{-# SPECIALISE hasVertex :: Int -> Graph Int -> Bool #-} hasVertex :: Eq a => a -> Graph a -> Bool hasVertex x = foldg False (==x) (||) (||)+{-# SPECIALISE hasVertex :: Int -> Graph Int -> Bool #-} -- | Check if a graph contains a given edge. -- Complexity: /O(s)/ time.@@ -453,10 +542,9 @@ -- hasEdge x y 'empty' == False -- hasEdge x y ('vertex' z) == False -- hasEdge x y ('edge' x y) == True--- hasEdge x y . 'removeEdge' x y == const False+-- hasEdge x y . 'removeEdge' x y == 'const' False -- hasEdge x y == 'elem' (x,y) . 'edgeList' -- @-{-# SPECIALISE hasEdge :: Int -> Int -> Graph Int -> Bool #-} hasEdge :: Eq a => a -> a -> Graph a -> Bool hasEdge s t g = hit g == Edge where@@ -470,23 +558,25 @@ Miss -> hit y Tail -> if hasVertex t y then Edge else Tail Edge -> Edge+{-# SPECIALISE hasEdge :: Int -> Int -> Graph Int -> Bool #-} -- | The number of vertices in a graph. -- Complexity: /O(s * log(n))/ time. -- -- @--- vertexCount 'empty' == 0--- vertexCount ('vertex' x) == 1--- vertexCount == 'length' . 'vertexList'+-- vertexCount 'empty' == 0+-- vertexCount ('vertex' x) == 1+-- vertexCount == 'length' . 'vertexList'+-- vertexCount x \< vertexCount y ==> x \< y -- @-{-# INLINE [1] vertexCount #-}-{-# RULES "vertexCount/Int" vertexCount = vertexIntCount #-} vertexCount :: Ord a => Graph a -> Int vertexCount = Set.size . vertexSet+{-# INLINE [1] vertexCount #-}+{-# RULES "vertexCount/Int" vertexCount = vertexIntCountR #-} --- | Like 'vertexCount' but specialised for graphs with vertices of type 'Int'.-vertexIntCount :: Graph Int -> Int-vertexIntCount = IntSet.size . vertexIntSet+-- Like 'vertexCount' but specialised for graphs with vertices of type 'Int'.+vertexIntCountR :: Graph Int -> Int+vertexIntCountR = IntSet.size . vertexIntSetR -- | The number of edges in a graph. -- Complexity: /O(s + m * log(m))/ time. Note that the number of edges /m/ of a@@ -498,14 +588,14 @@ -- edgeCount ('edge' x y) == 1 -- edgeCount == 'length' . 'edgeList' -- @-{-# INLINE [1] edgeCount #-}-{-# RULES "edgeCount/Int" edgeCount = edgeCountInt #-} edgeCount :: Ord a => Graph a -> Int edgeCount = AM.edgeCount . toAdjacencyMap+{-# INLINE [1] edgeCount #-}+{-# RULES "edgeCount/Int" edgeCount = edgeCountIntR #-} --- | Like 'edgeCount' but specialised for graphs with vertices of type 'Int'.-edgeCountInt :: Graph Int -> Int-edgeCountInt = AIM.edgeCount . toAdjacencyIntMap+-- Like 'edgeCount' but specialised for graphs with vertices of type 'Int'.+edgeCountIntR :: Graph Int -> Int+edgeCountIntR = AIM.edgeCount . toAdjacencyIntMap -- | The sorted list of vertices of a given graph. -- Complexity: /O(s * log(n))/ time and /O(n)/ memory.@@ -515,14 +605,14 @@ -- vertexList ('vertex' x) == [x] -- vertexList . 'vertices' == 'Data.List.nub' . 'Data.List.sort' -- @-{-# INLINE [1] vertexList #-}-{-# RULES "vertexList/Int" vertexList = vertexIntList #-} vertexList :: Ord a => Graph a -> [a] vertexList = Set.toAscList . vertexSet+{-# INLINE [1] vertexList #-}+{-# RULES "vertexList/Int" vertexList = vertexIntListR #-} --- | Like 'vertexList' but specialised for graphs with vertices of type 'Int'.-vertexIntList :: Graph Int -> [Int]-vertexIntList = IntSet.toList . vertexIntSet+-- Like 'vertexList' but specialised for graphs with vertices of type 'Int'.+vertexIntListR :: Graph Int -> [Int]+vertexIntListR = IntSet.toList . vertexIntSetR -- | The sorted list of edges of a graph. -- Complexity: /O(s + m * log(m))/ time and /O(m)/ memory. Note that the number of@@ -534,16 +624,16 @@ -- edgeList ('edge' x y) == [(x,y)] -- edgeList ('star' 2 [3,1]) == [(2,1), (2,3)] -- edgeList . 'edges' == 'Data.List.nub' . 'Data.List.sort'--- edgeList . 'transpose' == 'Data.List.sort' . map 'Data.Tuple.swap' . edgeList+-- edgeList . 'transpose' == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . edgeList -- @-{-# INLINE [1] edgeList #-}-{-# RULES "edgeList/Int" edgeList = edgeIntList #-} edgeList :: Ord a => Graph a -> [(a, a)] edgeList = AM.edgeList . toAdjacencyMap+{-# INLINE [1] edgeList #-}+{-# RULES "edgeList/Int" edgeList = edgeIntListR #-} --- | Like 'edgeList' but specialised for graphs with vertices of type 'Int'.-edgeIntList :: Graph Int -> [(Int, Int)]-edgeIntList = AIM.edgeList . toAdjacencyIntMap+-- Like 'edgeList' but specialised for graphs with vertices of type 'Int'.+edgeIntListR :: Graph Int -> [(Int, Int)]+edgeIntListR = AIM.edgeList . toAdjacencyIntMap -- | The set of vertices of a given graph. -- Complexity: /O(s * log(n))/ time and /O(n)/ memory.@@ -552,23 +642,13 @@ -- vertexSet 'empty' == Set.'Set.empty' -- vertexSet . 'vertex' == Set.'Set.singleton' -- vertexSet . 'vertices' == Set.'Set.fromList'--- vertexSet . 'clique' == Set.'Set.fromList' -- @ vertexSet :: Ord a => Graph a -> Set.Set a vertexSet = foldg Set.empty Set.singleton Set.union Set.union --- | The set of vertices of a given graph. Like 'vertexSet' but specialised for--- graphs with vertices of type 'Int'.--- Complexity: /O(s * log(n))/ time and /O(n)/ memory.------ @--- vertexIntSet 'empty' == IntSet.'IntSet.empty'--- vertexIntSet . 'vertex' == IntSet.'IntSet.singleton'--- vertexIntSet . 'vertices' == IntSet.'IntSet.fromList'--- vertexIntSet . 'clique' == IntSet.'IntSet.fromList'--- @-vertexIntSet :: Graph Int -> IntSet.IntSet-vertexIntSet = foldg IntSet.empty IntSet.singleton IntSet.union IntSet.union+-- Like 'vertexSet' but specialised for graphs with vertices of type 'Int'.+vertexIntSetR :: Graph Int -> IntSet.IntSet+vertexIntSetR = foldg IntSet.empty IntSet.singleton IntSet.union IntSet.union -- | The set of edges of a given graph. -- Complexity: /O(s * log(m))/ time and /O(m)/ memory.@@ -582,11 +662,11 @@ edgeSet :: Ord a => Graph a -> Set.Set (a, a) edgeSet = AM.edgeSet . toAdjacencyMap {-# INLINE [1] edgeSet #-}-{-# RULES "edgeSet/Int" edgeSet = edgeIntSet #-}+{-# RULES "edgeSet/Int" edgeSet = edgeIntSetR #-} --- | Like 'edgeSet' but specialised for graphs with vertices of type 'Int'.-edgeIntSet :: Graph Int -> Set.Set (Int,Int)-edgeIntSet = AIM.edgeSet . toAdjacencyIntMap+-- Like 'edgeSet' but specialised for graphs with vertices of type 'Int'.+edgeIntSetR :: Graph Int -> Set.Set (Int,Int)+edgeIntSetR = AIM.edgeSet . toAdjacencyIntMap -- | The sorted /adjacency list/ of a graph. -- Complexity: /O(n + m)/ time and /O(m)/ memory.@@ -598,29 +678,18 @@ -- adjacencyList ('star' 2 [3,1]) == [(1, []), (2, [1,3]), (3, [])] -- 'stars' . adjacencyList == id -- @-{-# SPECIALISE adjacencyList :: Graph Int -> [(Int, [Int])] #-} adjacencyList :: Ord a => Graph a -> [(a, [a])] adjacencyList = AM.adjacencyList . toAdjacencyMap---- | The /adjacency map/ of a graph: each vertex is associated with a set of its--- direct successors.--- Complexity: /O(s + m * log(m))/ time. Note that the number of edges /m/ of a--- graph can be quadratic with respect to the expression size /s/.-adjacencyMap :: Ord a => Graph a -> Map a (Set a)-adjacencyMap = AM.adjacencyMap . toAdjacencyMap+{-# SPECIALISE adjacencyList :: Graph Int -> [(Int, [Int])] #-} -- TODO: This is a very inefficient implementation. Find a way to construct an -- adjacency map directly, without building intermediate representations for all -- subgraphs.--- | Convert a graph to 'AM.AdjacencyMap'.+-- Convert a graph to 'AM.AdjacencyMap'. toAdjacencyMap :: Ord a => Graph a -> AM.AdjacencyMap a toAdjacencyMap = foldg AM.empty AM.vertex AM.overlay AM.connect --- | Like 'adjacencyMap' but specialised for graphs with vertices of type 'Int'.-adjacencyIntMap :: Graph Int -> IntMap IntSet-adjacencyIntMap = AIM.adjacencyIntMap . toAdjacencyIntMap---- | Like @toAdjacencyMap@ but specialised for graphs with vertices of type 'Int'.+-- Like @toAdjacencyMap@ but specialised for graphs with vertices of type 'Int'. toAdjacencyIntMap :: Graph Int -> AIM.AdjacencyIntMap toAdjacencyIntMap = foldg AIM.empty AIM.vertex AIM.overlay AIM.connect @@ -667,7 +736,7 @@ -- @ clique :: [a] -> Graph a clique = connects . map vertex-{-# NOINLINE [1] clique #-}+{-# INLINE [1] clique #-} -- | The /biclique/ on two lists of vertices. -- Complexity: /O(L1 + L2)/ time, memory and size, where /L1/ and /L2/ are the@@ -710,7 +779,7 @@ -- stars [(x, [])] == 'vertex' x -- stars [(x, [y])] == 'edge' x y -- stars [(x, ys)] == 'star' x ys--- stars == 'overlays' . map (uncurry 'star')+-- stars == 'overlays' . 'map' ('uncurry' 'star') -- stars . 'adjacencyList' == id -- 'overlay' (stars xs) (stars ys) == stars (xs ++ ys) -- @@@ -741,7 +810,7 @@ -- 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 == 'overlays' . 'map' 'tree' -- @ forest :: Tree.Forest a -> Graph a forest = overlays . map tree@@ -825,9 +894,9 @@ -- removeVertex 1 ('edge' 1 2) == 'vertex' 2 -- removeVertex x . removeVertex x == removeVertex x -- @-{-# SPECIALISE removeVertex :: Int -> Graph Int -> Graph Int #-} removeVertex :: Eq a => a -> Graph a -> Graph a removeVertex v = induce (/= v)+{-# SPECIALISE removeVertex :: Int -> Graph Int -> Graph Int #-} -- | Remove an edge from a given graph. -- Complexity: /O(s)/ time, memory and size.@@ -840,19 +909,18 @@ -- removeEdge 1 2 (1 * 1 * 2 * 2) == 1 * 1 + 2 * 2 -- 'size' (removeEdge x y z) <= 3 * 'size' z -- @-{-# SPECIALISE removeEdge :: Int -> Int -> Graph Int -> Graph Int #-} removeEdge :: Eq a => a -> a -> Graph a -> Graph a removeEdge s t = filterContext s (/=s) (/=t)-+{-# SPECIALISE removeEdge :: Int -> Int -> Graph Int -> Graph Int #-} -- TODO: Export--- | Filter vertices in a subgraph context.-{-# SPECIALISE filterContext :: Int -> (Int -> Bool) -> (Int -> Bool) -> Graph Int -> Graph Int #-}+-- Filter vertices in a subgraph context. filterContext :: Eq a => a -> (a -> Bool) -> (a -> Bool) -> Graph a -> Graph a filterContext s i o g = maybe g go $ context (==s) g where go (Context is os) = induce (/=s) g `overlay` transpose (star s (filter i is))- `overlay` star s (filter o os)+ `overlay` star s (filter o os)+{-# SPECIALISE filterContext :: Int -> (Int -> Bool) -> (Int -> Bool) -> Graph Int -> Graph Int #-} -- | The function @'replaceVertex' x y@ replaces vertex @x@ with vertex @y@ in a -- given 'Graph'. If @y@ already exists, @x@ and @y@ will be merged.@@ -863,20 +931,19 @@ -- replaceVertex x y ('vertex' x) == 'vertex' y -- replaceVertex x y == 'mergeVertices' (== x) y -- @-{-# SPECIALISE replaceVertex :: Int -> Int -> Graph Int -> Graph Int #-} replaceVertex :: Eq a => a -> a -> Graph a -> Graph a replaceVertex u v = fmap $ \w -> if w == u then v else w-+{-# SPECIALISE replaceVertex :: Int -> Int -> Graph Int -> Graph Int #-} -- | Merge vertices satisfying a given predicate into a given vertex. -- Complexity: /O(s)/ time, memory and size, assuming that the predicate takes -- /O(1)/ to be evaluated. -- -- @--- mergeVertices (const False) x == id+-- 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 'even' 1 (0 * 2) == 1 * 1+-- mergeVertices 'odd' 1 (3 + 4 * 5) == 4 * 1 -- @ mergeVertices :: (a -> Bool) -> a -> Graph a -> Graph a mergeVertices p v = fmap $ \w -> if p w then v else w@@ -892,9 +959,9 @@ -- splitVertex x [y] == 'replaceVertex' x y -- splitVertex 1 [0,1] $ 1 * (2 + 3) == (0 + 1) * (2 + 3) -- @-{-# SPECIALISE splitVertex :: Int -> [Int] -> Graph Int -> Graph Int #-} splitVertex :: Eq a => a -> [a] -> Graph a -> Graph a splitVertex v us g = g >>= \w -> if w == v then vertices us else vertex w+{-# SPECIALISE splitVertex :: Int -> [Int] -> Graph Int -> Graph Int #-} -- | Transpose a given graph. -- Complexity: /O(s)/ time, memory and size.@@ -905,24 +972,11 @@ -- transpose ('edge' x y) == 'edge' y x -- transpose . transpose == id -- transpose ('box' x y) == 'box' (transpose x) (transpose y)--- 'edgeList' . transpose == 'Data.List.sort' . map 'Data.Tuple.swap' . 'edgeList'+-- 'edgeList' . transpose == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . 'edgeList' -- @ transpose :: Graph a -> Graph a transpose = foldg Empty Vertex Overlay (flip Connect)-{-# NOINLINE [1] transpose #-}--{-# RULES-"transpose/Empty" transpose Empty = Empty-"transpose/Vertex" forall x. transpose (Vertex x) = Vertex x-"transpose/Overlay" forall g1 g2. transpose (Overlay g1 g2) = Overlay (transpose g1) (transpose g2)-"transpose/Connect" forall g1 g2. transpose (Connect g1 g2) = Connect (transpose g2) (transpose g1)--"transpose/overlays" forall xs. transpose (overlays xs) = overlays (map transpose xs)-"transpose/connects" forall xs. transpose (connects xs) = connects (reverse (map transpose xs))--"transpose/vertices" forall xs. transpose (vertices xs) = vertices xs-"transpose/clique" forall xs. transpose (clique xs) = clique (reverse xs)- #-}+{-# INLINE transpose #-} -- | Construct the /induced subgraph/ of a given graph by removing the -- vertices that do not satisfy a given predicate.@@ -930,8 +984,8 @@ -- /O(1)/ to be evaluated. -- -- @--- induce (const True ) x == x--- induce (const False) x == 'empty'+-- 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@@ -942,6 +996,7 @@ k _ x Empty = x -- Constant folding to get rid of Empty leaves k _ Empty y = y k f x y = f x y+{-# INLINE [1] induce #-} -- | Simplify a graph expression. Semantically, this is the identity function, -- but it simplifies a given expression according to the laws of the algebra.@@ -959,11 +1014,10 @@ -- simplify (1 + 2 + 1) '===' 1 + 2 -- simplify (1 * 1 * 1) '===' 1 * 1 -- @-{-# SPECIALISE simplify :: Graph Int -> Graph Int #-} simplify :: Ord a => Graph a -> Graph a simplify = foldg Empty Vertex (simple Overlay) (simple Connect)+{-# SPECIALISE simplify :: Graph Int -> Graph Int #-} -{-# SPECIALISE simple :: (Int -> Int -> Int) -> Int -> Int -> Int #-} simple :: Eq g => (g -> g -> g) -> g -> g -> g simple op x y | x == z = x@@ -971,7 +1025,44 @@ | otherwise = z where z = op x y+{-# SPECIALISE simple :: (Int -> Int -> Int) -> Int -> Int -> Int #-} +-- | Left-to-right /relational composition/ of graphs: vertices @x@ and @z@ are+-- connected in the resulting graph if there is a vertex @y@, such that @x@ is+-- connected to @y@ in the first graph, and @y@ is connected to @z@ in the+-- second graph. There are no isolated vertices in the result. This operation is+-- associative, has 'empty' and single-'vertex' graphs as /annihilating zeroes/,+-- and distributes over 'overlay'.+-- Complexity: /O(n * m * log(n))/ time, /O(n + m)/ memory, and /O(m1 + m2)/+-- size, where /n/ and /m/ stand for the number of vertices and edges in the+-- resulting graph, while /m1/ and /m2/ are the number of edges in the original+-- graphs. Note that the number of edges in the resulting graph may be+-- quadratic, i.e. /m = O(m1 * m2)/, but the algebraic representation requires+-- only /O(m1 + m2)/ operations to list them.+--+-- @+-- compose 'empty' x == 'empty'+-- compose x 'empty' == 'empty'+-- compose ('vertex' x) y == 'empty'+-- compose x ('vertex' y) == 'empty'+-- compose x (compose y z) == compose (compose x y) z+-- compose x ('overlay' y z) == 'overlay' (compose x y) (compose x z)+-- compose ('overlay' x y) z == 'overlay' (compose x z) (compose y z)+-- compose ('edge' x y) ('edge' y z) == 'edge' x z+-- compose ('path' [1..5]) ('path' [1..5]) == 'edges' [(1,3), (2,4), (3,5)]+-- compose ('circuit' [1..5]) ('circuit' [1..5]) == 'circuit' [1,3,5,2,4]+-- 'size' (compose x y) <= 'edgeCount' x + 'edgeCount' y + 1+-- @+compose :: Ord a => Graph a -> Graph a -> Graph a+compose x y = overlays+ [ biclique xs ys+ | v <- Set.toList (AM.vertexSet mx `Set.union` AM.vertexSet my)+ , let xs = Set.toList (AM.postSet v mx), not (null xs)+ , let ys = Set.toList (AM.postSet v my), not (null ys) ]+ where+ mx = toAdjacencyMap (transpose x)+ my = toAdjacencyMap y+ -- | Compute the /Cartesian product/ of graphs. -- Complexity: /O(s1 * s2)/ time, memory and size, where /s1/ and /s2/ are the -- sizes of the given graphs.@@ -1000,24 +1091,10 @@ box :: Graph a -> Graph b -> Graph (a, b) box x y = overlays $ xs ++ ys where- xs = map (\b -> fmap (,b) x) $ toList y- ys = map (\a -> fmap (a,) y) $ toList x---- | 'Focus' on a specified subgraph.-focus :: (a -> Bool) -> Graph a -> Focus a-focus f = foldg emptyFocus (vertexFocus f) overlayFoci connectFoci---- | The context of a subgraph comprises the input and output vertices outside--- the subgraph that are connected to the vertices inside the subgraph.-data Context a = Context { inputs :: [a], outputs :: [a] }---- | Extract the context from a graph 'Focus'. Returns @Nothing@ if the focus--- could not be obtained.-context :: (a -> Bool) -> Graph a -> Maybe (Context a)-context p g | ok f = Just $ Context (toList $ is f) (toList $ os f)- | otherwise = Nothing- where- f = focus p g+ 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 (<>) (<>) -- | /Sparsify/ a graph by adding intermediate 'Left' @Int@ vertices between the -- original vertices (wrapping the latter in 'Right') such that the resulting@@ -1045,3 +1122,105 @@ m <- get put (m + 1) overlay <$> s `x` m <*> m `y` t++{- Note [The rules of foldg]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~++The rules for foldg work very similarly to GHC's mapFB rules; see a note below+this line: http://hackage.haskell.org/package/base/docs/src/GHC.Base.html#mapFB.++* Up to (but not including) phase 1, we use the "buildR/f" rule to rewrite all+ saturated applications of f into its buildR/foldg form, hoping for fusion to+ happen (through the "foldg/buildR" rule).++ In phases 1 and 0, we switch off these rules, inline buildR, and switch on the+ "graph/f" rule, which rewrites "foldg/f" back into plain functions if needed.++ It's important that these two rules aren't both active at once (along with+ build's unfolding) else we'd get an infinite loop in the rules. Hence the+ activation control below.++* composeR and matchR are here to remember the original function after applying+ 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.+-}++type Foldg a = forall b. b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> b++buildR :: Foldg a -> Graph a+buildR g = g Empty Vertex Overlay Connect+{-# INLINE [1] buildR #-}++composeR :: (b -> c) -> (a -> b) -> a -> c+composeR = (.)+{-# INLINE [0] composeR #-}++matchR :: b -> (a -> b) -> (a -> Bool) -> a -> b+matchR e v p = \x -> if p x then v x else e+{-# INLINE [0] matchR #-}++-- 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/induce" [~1] forall p g.+ induce p g = buildR (\e v o c -> foldg e (matchR e v p) o c g)++"buildR/foldg(fc)" [~1] forall (f :: forall b. (b -> b -> b) -> (b -> b -> b)) g.+ foldg Empty Vertex Overlay (f Connect) g = buildR (\e v o c -> foldg e v o (f c) g)++"buildR/foldg(fo)" [~1] forall (f :: forall b. (b -> b -> b) -> (b -> b -> b)) g.+ foldg Empty Vertex (f Overlay) Connect g = buildR (\e v o c -> foldg e v (f o) c g)++"buildR/foldg(fo)(hc)" [~1] forall (f :: forall b. (b -> b -> b) -> (b -> b -> b)) (h :: forall b. (b -> b -> b) -> (b -> b -> b)) g.+ foldg Empty Vertex (f Overlay) (h Connect) g = buildR (\e v o c -> foldg e v (f o) (h c) g)+ #-}++-- Rewrite rules for fusion.+{-# RULES+-- Fuse a foldg followed by a buildR+"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+-- their buildR form.+"fmapR/fmapR" forall c f g.+ composeR (composeR c f) g = composeR c (f.g)+ #-}++-- Eliminate remaining rewrite-only functions.+{-# RULES+"graph/induce" [1] forall f.+ foldg Empty (matchR Empty Vertex f) Overlay Connect = induce f+ #-}++-- 'Focus' on a specified subgraph.+focus :: (a -> Bool) -> Graph a -> Focus a+focus f = foldg emptyFocus (vertexFocus f) overlayFoci connectFoci++-- | The 'Context' of a subgraph comprises its 'inputs' and 'outputs', i.e. all+-- the vertices that are connected to the subgraph's vertices. Note that inputs+-- and outputs can belong to the subgraph itself. In general, there are no+-- guarantees on the order of vertices in 'inputs' and 'outputs'; furthermore,+-- there may be repetitions.+data Context a = Context { inputs :: [a], outputs :: [a] }+ deriving (Eq, Show)++-- | Extract the 'Context' of a subgraph specified by a given predicate. Returns+-- @Nothing@ if the specified subgraph is empty.+--+-- @+-- context ('const' False) x == Nothing+-- context (== 1) ('edge' 1 2) == Just ('Context' [ ] [2 ])+-- context (== 2) ('edge' 1 2) == Just ('Context' [1 ] [ ])+-- context ('const' True ) ('edge' 1 2) == Just ('Context' [1 ] [2 ])+-- context (== 4) (3 * 1 * 4 * 1 * 5) == Just ('Context' [3,1] [1,5])+-- @+context :: (a -> Bool) -> Graph a -> Maybe (Context a)+context p g | ok f = Just $ Context (toList $ is f) (toList $ os f)+ | otherwise = Nothing+ where+ f = focus p g
src/Algebra/Graph/AdjacencyIntMap.hs view
@@ -10,11 +10,11 @@ -- in Haskell. See <https://github.com/snowleopard/alga-paper this paper> for the -- motivation behind the library, the underlying theory, and implementation details. ----- This module defines the 'AdjacencyIntMap' data type, as well as associated--- operations and algorithms. 'AdjacencyIntMap' is an instance of the 'C.Graph'--- type class, which can be used for polymorphic graph construction--- and manipulation. See "Algebra.Graph.AdjacencyMap" for graphs with--- non-@Int@ vertices.+-- This module defines the 'AdjacencyIntMap' data type and associated functions.+-- See "Algebra.Graph.AdjacencyIntMap.Algorithm" for implementations of basic+-- graph algorithms. 'AdjacencyIntMap' is an instance of the 'C.Graph' type+-- class, which can be used for polymorphic graph construction and manipulation.+-- See "Algebra.Graph.AdjacencyMap" for graphs with non-@Int@ vertices. ----------------------------------------------------------------------------- module Algebra.Graph.AdjacencyIntMap ( -- * Data structure@@ -31,34 +31,55 @@ adjacencyList, vertexIntSet, edgeSet, preIntSet, postIntSet, -- * Standard families of graphs- path, circuit, clique, biclique, star, stars, tree, forest,+ path, circuit, clique, biclique, star, stars, fromAdjacencyIntSets, tree,+ forest, -- * Graph transformation removeVertex, removeEdge, replaceVertex, mergeVertices, transpose, gmap, induce, - -- * Algorithms- dfsForest, dfsForestFrom, dfs, reachable, topSort, isAcyclic,-- -- * Correctness properties- isDfsForestOf, isTopSortOf- ) where+ -- * Relational operations+ compose, closure, reflexiveClosure, symmetricClosure, transitiveClosure+ ) where -import Control.Monad import Data.Foldable (foldMap) import Data.IntSet (IntSet)-import Data.Maybe import Data.Monoid import Data.Set (Set) import Data.Tree import Algebra.Graph.AdjacencyIntMap.Internal -import qualified Data.Graph.Typed as Typed import qualified Data.IntMap.Strict as IntMap import qualified Data.IntSet as IntSet import qualified Data.Set as Set +-- | Construct the /empty graph/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'isEmpty' empty == True+-- 'hasVertex' x empty == False+-- 'vertexCount' empty == 0+-- 'edgeCount' empty == 0+-- @+empty :: AdjacencyIntMap+empty = AM IntMap.empty+{-# NOINLINE [1] empty #-}++-- | Construct the graph comprising /a single isolated vertex/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'isEmpty' (vertex x) == False+-- 'hasVertex' x (vertex x) == True+-- 'vertexCount' (vertex x) == 1+-- 'edgeCount' (vertex x) == 0+-- @+vertex :: Int -> AdjacencyIntMap+vertex x = AM $ IntMap.singleton x IntSet.empty+{-# NOINLINE [1] vertex #-}+ -- | Construct the graph comprising /a single edge/. -- Complexity: /O(1)/ time, memory. --@@ -73,6 +94,47 @@ edge x y | x == y = AM $ IntMap.singleton x (IntSet.singleton y) | otherwise = AM $ IntMap.fromList [(x, IntSet.singleton y), (y, IntSet.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.+--+-- @+-- 'isEmpty' (overlay x y) == 'isEmpty' x && 'isEmpty' y+-- 'hasVertex' z (overlay x y) == 'hasVertex' z x || 'hasVertex' z y+-- 'vertexCount' (overlay x y) >= 'vertexCount' x+-- 'vertexCount' (overlay x y) <= 'vertexCount' x + 'vertexCount' y+-- 'edgeCount' (overlay x y) >= 'edgeCount' x+-- 'edgeCount' (overlay x y) <= 'edgeCount' x + 'edgeCount' y+-- 'vertexCount' (overlay 1 2) == 2+-- 'edgeCount' (overlay 1 2) == 0+-- @+overlay :: AdjacencyIntMap -> AdjacencyIntMap -> AdjacencyIntMap+overlay x y = AM $ IntMap.unionWith IntSet.union (adjacencyIntMap x) (adjacencyIntMap y)+{-# NOINLINE [1] overlay #-}++-- | /Connect/ two graphs. This is an 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)/.+--+-- @+-- 'isEmpty' (connect x y) == 'isEmpty' x && 'isEmpty' y+-- 'hasVertex' z (connect x y) == 'hasVertex' z x || 'hasVertex' z y+-- 'vertexCount' (connect x y) >= 'vertexCount' x+-- 'vertexCount' (connect x y) <= 'vertexCount' x + 'vertexCount' y+-- 'edgeCount' (connect x y) >= 'edgeCount' x+-- 'edgeCount' (connect x y) >= 'edgeCount' y+-- 'edgeCount' (connect x y) >= 'vertexCount' x * 'vertexCount' y+-- 'edgeCount' (connect x y) <= 'vertexCount' x * 'vertexCount' y + 'edgeCount' x + 'edgeCount' y+-- 'vertexCount' (connect 1 2) == 2+-- 'edgeCount' (connect 1 2) == 1+-- @+connect :: AdjacencyIntMap -> AdjacencyIntMap -> AdjacencyIntMap+connect x y = AM $ IntMap.unionsWith IntSet.union [ adjacencyIntMap x, adjacencyIntMap y,+ IntMap.fromSet (const . IntMap.keysSet $ adjacencyIntMap y) (IntMap.keysSet $ adjacencyIntMap x) ]+{-# NOINLINE [1] connect #-}+ -- | 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.@@ -133,11 +195,12 @@ -- 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 '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 x y ==> x <= y -- @ isSubgraphOf :: AdjacencyIntMap -> AdjacencyIntMap -> Bool isSubgraphOf x y = IntMap.isSubmapOfBy IntSet.isSubsetOf (adjacencyIntMap x) (adjacencyIntMap y)@@ -162,7 +225,7 @@ -- hasVertex x 'empty' == False -- hasVertex x ('vertex' x) == True -- hasVertex 1 ('vertex' 2) == False--- hasVertex x . 'removeVertex' x == const False+-- hasVertex x . 'removeVertex' x == 'const' False -- @ hasVertex :: Int -> AdjacencyIntMap -> Bool hasVertex x = IntMap.member x . adjacencyIntMap@@ -174,7 +237,7 @@ -- hasEdge x y 'empty' == False -- hasEdge x y ('vertex' z) == False -- hasEdge x y ('edge' x y) == True--- hasEdge x y . 'removeEdge' x y == const False+-- hasEdge x y . 'removeEdge' x y == 'const' False -- hasEdge x y == 'elem' (x,y) . 'edgeList' -- @ hasEdge :: Int -> Int -> AdjacencyIntMap -> Bool@@ -186,9 +249,10 @@ -- Complexity: /O(1)/ time. -- -- @--- vertexCount 'empty' == 0--- vertexCount ('vertex' x) == 1--- vertexCount == 'length' . 'vertexList'+-- vertexCount 'empty' == 0+-- vertexCount ('vertex' x) == 1+-- vertexCount == 'length' . 'vertexList'+-- vertexCount x \< vertexCount y ==> x \< y -- @ vertexCount :: AdjacencyIntMap -> Int vertexCount = IntMap.size . adjacencyIntMap@@ -225,7 +289,7 @@ -- edgeList ('edge' x y) == [(x,y)] -- edgeList ('star' 2 [3,1]) == [(2,1), (2,3)] -- edgeList . 'edges' == 'Data.List.nub' . 'Data.List.sort'--- edgeList . 'transpose' == 'Data.List.sort' . map 'Data.Tuple.swap' . edgeList+-- edgeList . 'transpose' == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . edgeList -- @ edgeList :: AdjacencyIntMap -> [(Int, Int)] edgeList (AM m) = [ (x, y) | (x, ys) <- IntMap.toAscList m, y <- IntSet.toAscList ys ]@@ -381,13 +445,29 @@ -- stars [(x, [])] == 'vertex' x -- stars [(x, [y])] == 'edge' x y -- stars [(x, ys)] == 'star' x ys--- stars == 'overlays' . map (uncurry 'star')+-- stars == 'overlays' . 'map' ('uncurry' 'star') -- stars . 'adjacencyList' == id -- 'overlay' (stars xs) (stars ys) == stars (xs ++ ys) -- @ stars :: [(Int, [Int])] -> AdjacencyIntMap stars = fromAdjacencyIntSets . map (fmap IntSet.fromList) +-- | Construct a graph from a list of adjacency sets; a variation of 'stars'.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- fromAdjacencyIntSets [] == 'empty'+-- fromAdjacencyIntSets [(x, IntSet.'IntSet.empty')] == 'vertex' x+-- fromAdjacencyIntSets [(x, IntSet.'IntSet.singleton' y)] == 'edge' x y+-- fromAdjacencyIntSets . 'map' ('fmap' IntSet.'IntSet.fromList') == 'stars'+-- 'overlay' (fromAdjacencyIntSets xs) (fromAdjacencyIntSets ys) == fromAdjacencyIntSets (xs ++ ys)+-- @+fromAdjacencyIntSets :: [(Int, IntSet)] -> AdjacencyIntMap+fromAdjacencyIntSets ss = AM $ IntMap.unionWith IntSet.union vs es+ where+ vs = IntMap.fromSet (const IntSet.empty) . IntSet.unions $ map snd ss+ es = IntMap.fromListWith IntSet.union ss+ -- | The /tree graph/ constructed from a given 'Tree' data structure. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory. --@@ -409,7 +489,7 @@ -- 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 == 'overlays' . 'map' 'tree' -- @ forest :: Forest Int -> AdjacencyIntMap forest = overlays . map tree@@ -457,10 +537,10 @@ -- /O(1)/ to be evaluated. -- -- @--- mergeVertices (const False) x == id+-- 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 'even' 1 (0 * 2) == 1 * 1+-- mergeVertices 'odd' 1 (3 + 4 * 5) == 4 * 1 -- @ mergeVertices :: (Int -> Bool) -> Int -> AdjacencyIntMap -> AdjacencyIntMap mergeVertices p v = gmap $ \u -> if p u then v else u@@ -473,7 +553,7 @@ -- transpose ('vertex' x) == 'vertex' x -- transpose ('edge' x y) == 'edge' y x -- transpose . transpose == id--- 'edgeList' . transpose == 'Data.List.sort' . map 'Data.Tuple.swap' . 'edgeList'+-- 'edgeList' . transpose == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . 'edgeList' -- @ transpose :: AdjacencyIntMap -> AdjacencyIntMap transpose (AM m) = AM $ IntMap.foldrWithKey combine vs m@@ -516,8 +596,8 @@ -- be evaluated. -- -- @--- induce (const True ) x == x--- induce (const False) x == 'empty'+-- 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@@ -525,168 +605,92 @@ induce :: (Int -> Bool) -> AdjacencyIntMap -> AdjacencyIntMap induce p = AM . IntMap.map (IntSet.filter p) . IntMap.filterWithKey (\k _ -> p k) . adjacencyIntMap --- | Compute the /depth-first search/ forest of a graph that corresponds to--- searching from each of the graph vertices in the 'Ord' @a@ order.------ @--- dfsForest 'empty' == []--- 'forest' (dfsForest $ 'edge' 1 1) == 'vertex' 1--- 'forest' (dfsForest $ 'edge' 1 2) == 'edge' 1 2--- 'forest' (dfsForest $ 'edge' 2 1) == 'vertices' [1,2]--- 'isSubgraphOf' ('forest' $ dfsForest x) x == True--- 'isDfsForestOf' (dfsForest x) x == True--- dfsForest . 'forest' . dfsForest == dfsForest--- dfsForest ('vertices' vs) == map (\\v -> Node v []) ('Data.List.nub' $ 'Data.List.sort' vs)--- 'dfsForestFrom' ('vertexList' x) x == dfsForest x--- dfsForest $ 3 * (1 + 4) * (1 + 5) == [ Node { rootLabel = 1--- , subForest = [ Node { rootLabel = 5--- , subForest = [] }]}--- , Node { rootLabel = 3--- , subForest = [ Node { rootLabel = 4--- , subForest = [] }]}]--- @-dfsForest :: AdjacencyIntMap -> Forest Int-dfsForest = Typed.dfsForest . Typed.fromAdjacencyIntMap---- | Compute the /depth-first search/ forest of a graph, searching from each of--- the given vertices in order. Note that the resulting forest does not--- necessarily span the whole graph, as some vertices may be unreachable.------ @--- dfsForestFrom vs 'empty' == []--- 'forest' (dfsForestFrom [1] $ 'edge' 1 1) == 'vertex' 1--- 'forest' (dfsForestFrom [1] $ 'edge' 1 2) == 'edge' 1 2--- 'forest' (dfsForestFrom [2] $ 'edge' 1 2) == 'vertex' 2--- 'forest' (dfsForestFrom [3] $ 'edge' 1 2) == 'empty'--- 'forest' (dfsForestFrom [2,1] $ 'edge' 1 2) == 'vertices' [1,2]--- 'isSubgraphOf' ('forest' $ dfsForestFrom vs x) x == True--- 'isDfsForestOf' (dfsForestFrom ('vertexList' x) x) x == True--- dfsForestFrom ('vertexList' x) x == 'dfsForest' x--- dfsForestFrom vs ('vertices' vs) == map (\\v -> Node v []) ('Data.List.nub' vs)--- dfsForestFrom [] x == []--- dfsForestFrom [1,4] $ 3 * (1 + 4) * (1 + 5) == [ Node { rootLabel = 1--- , subForest = [ Node { rootLabel = 5--- , subForest = [] }--- , Node { rootLabel = 4--- , subForest = [] }]--- @-dfsForestFrom :: [Int] -> AdjacencyIntMap -> Forest Int-dfsForestFrom vs = Typed.dfsForestFrom vs . Typed.fromAdjacencyIntMap---- | Compute the list of vertices visited by the /depth-first search/ in a graph,--- when searching from each of the given vertices in order.------ @--- dfs vs $ 'empty' == []--- dfs [1] $ 'edge' 1 1 == [1]--- dfs [1] $ 'edge' 1 2 == [1,2]--- dfs [2] $ 'edge' 1 2 == [2]--- dfs [3] $ 'edge' 1 2 == []--- dfs [1,2] $ 'edge' 1 2 == [1,2]--- dfs [2,1] $ 'edge' 1 2 == [2,1]--- dfs [] $ x == []--- dfs [1,4] $ 3 * (1 + 4) * (1 + 5) == [1,5,4]--- 'isSubgraphOf' ('vertices' $ dfs vs x) x == True--- @-dfs :: [Int] -> AdjacencyIntMap -> [Int]-dfs vs = concatMap flatten . dfsForestFrom vs---- | Compute the list of vertices that are /reachable/ from a given source--- vertex in a graph. The vertices in the resulting list appear in the--- /depth-first order/.+-- | Left-to-right /relational composition/ of graphs: vertices @x@ and @z@ are+-- connected in the resulting graph if there is a vertex @y@, such that @x@ is+-- connected to @y@ in the first graph, and @y@ is connected to @z@ in the+-- second graph. There are no isolated vertices in the result. This operation is+-- associative, has 'empty' and single-'vertex' graphs as /annihilating zeroes/,+-- and distributes over 'overlay'.+-- Complexity: /O(n * m * log(n))/ time and /O(n + m)/ memory. -- -- @--- reachable x $ 'empty' == []--- reachable 1 $ 'vertex' 1 == [1]--- reachable 1 $ 'vertex' 2 == []--- reachable 1 $ 'edge' 1 1 == [1]--- reachable 1 $ 'edge' 1 2 == [1,2]--- reachable 4 $ 'path' [1..8] == [4..8]--- reachable 4 $ 'circuit' [1..8] == [4..8] ++ [1..3]--- reachable 8 $ 'clique' [8,7..1] == [8] ++ [1..7]--- 'isSubgraphOf' ('vertices' $ reachable x y) y == True+-- compose 'empty' x == 'empty'+-- compose x 'empty' == 'empty'+-- compose ('vertex' x) y == 'empty'+-- compose x ('vertex' y) == 'empty'+-- compose x (compose y z) == compose (compose x y) z+-- compose x ('overlay' y z) == 'overlay' (compose x y) (compose x z)+-- compose ('overlay' x y) z == 'overlay' (compose x z) (compose y z)+-- compose ('edge' x y) ('edge' y z) == 'edge' x z+-- compose ('path' [1..5]) ('path' [1..5]) == 'edges' [(1,3), (2,4), (3,5)]+-- compose ('circuit' [1..5]) ('circuit' [1..5]) == 'circuit' [1,3,5,2,4] -- @-reachable :: Int -> AdjacencyIntMap -> [Int]-reachable x = dfs [x]+compose :: AdjacencyIntMap -> AdjacencyIntMap -> AdjacencyIntMap+compose x y = fromAdjacencyIntSets+ [ (t, ys) | v <- IntSet.toList vs, let ys = postIntSet v y+ , not (IntSet.null ys), t <- IntSet.toList (postIntSet v tx) ]+ where+ tx = transpose x+ vs = vertexIntSet x `IntSet.union` vertexIntSet y --- | Compute the /topological sort/ of a graph or return @Nothing@ if the graph--- is cyclic.+-- | Compute the /reflexive and transitive closure/ of a graph.+-- Complexity: /O(n * m * log(n)^2)/ time. -- -- @--- topSort (1 * 2 + 3 * 1) == Just [3,1,2]--- topSort (1 * 2 + 2 * 1) == Nothing--- fmap (flip 'isTopSortOf' x) (topSort x) /= Just False--- 'isJust' . topSort == 'isAcyclic'+-- closure 'empty' == 'empty'+-- closure ('vertex' x) == 'edge' x x+-- closure ('edge' x x) == 'edge' x x+-- closure ('edge' x y) == 'edges' [(x,x), (x,y), (y,y)]+-- closure ('path' $ 'Data.List.nub' xs) == 'reflexiveClosure' ('clique' $ 'Data.List.nub' xs)+-- closure == 'reflexiveClosure' . 'transitiveClosure'+-- closure == 'transitiveClosure' . 'reflexiveClosure'+-- closure . closure == closure+-- 'postIntSet' x (closure y) == IntSet.'IntSet.fromList' ('Algebra.Graph.ToGraph.reachable' x y) -- @-topSort :: AdjacencyIntMap -> Maybe [Int]-topSort m = if isTopSortOf result m then Just result else Nothing- where- result = Typed.topSort (Typed.fromAdjacencyIntMap m)+closure :: AdjacencyIntMap -> AdjacencyIntMap+closure = reflexiveClosure . transitiveClosure --- | Check if a given graph is /acyclic/.+-- | Compute the /reflexive closure/ of a graph by adding a self-loop to every+-- vertex.+-- Complexity: /O(n * log(n))/ time. -- -- @--- isAcyclic (1 * 2 + 3 * 1) == True--- isAcyclic (1 * 2 + 2 * 1) == False--- isAcyclic . 'circuit' == 'null'--- isAcyclic == 'isJust' . 'topSort'+-- reflexiveClosure 'empty' == 'empty'+-- reflexiveClosure ('vertex' x) == 'edge' x x+-- reflexiveClosure ('edge' x x) == 'edge' x x+-- reflexiveClosure ('edge' x y) == 'edges' [(x,x), (x,y), (y,y)]+-- reflexiveClosure . reflexiveClosure == reflexiveClosure -- @-isAcyclic :: AdjacencyIntMap -> Bool-isAcyclic = isJust . topSort+reflexiveClosure :: AdjacencyIntMap -> AdjacencyIntMap+reflexiveClosure (AM m) = AM $ IntMap.mapWithKey (\k -> IntSet.insert k) m --- | Check if a given forest is a correct /depth-first search/ forest of a graph.--- The implementation is based on the paper "Depth-First Search and Strong--- Connectivity in Coq" by François Pottier.+-- | Compute the /symmetric closure/ of a graph by overlaying it with its own+-- transpose.+-- Complexity: /O((n + m) * log(n))/ time. -- -- @--- isDfsForestOf [] 'empty' == True--- isDfsForestOf [] ('vertex' 1) == False--- isDfsForestOf [Node 1 []] ('vertex' 1) == True--- isDfsForestOf [Node 1 []] ('vertex' 2) == False--- isDfsForestOf [Node 1 [], Node 1 []] ('vertex' 1) == False--- isDfsForestOf [Node 1 []] ('edge' 1 1) == True--- isDfsForestOf [Node 1 []] ('edge' 1 2) == False--- isDfsForestOf [Node 1 [], Node 2 []] ('edge' 1 2) == False--- isDfsForestOf [Node 2 [], Node 1 []] ('edge' 1 2) == True--- isDfsForestOf [Node 1 [Node 2 []]] ('edge' 1 2) == True--- isDfsForestOf [Node 1 [], Node 2 []] ('vertices' [1,2]) == True--- isDfsForestOf [Node 2 [], Node 1 []] ('vertices' [1,2]) == True--- isDfsForestOf [Node 1 [Node 2 []]] ('vertices' [1,2]) == False--- isDfsForestOf [Node 1 [Node 2 [Node 3 []]]] ('path' [1,2,3]) == True--- isDfsForestOf [Node 1 [Node 3 [Node 2 []]]] ('path' [1,2,3]) == False--- isDfsForestOf [Node 3 [], Node 1 [Node 2 []]] ('path' [1,2,3]) == True--- isDfsForestOf [Node 2 [Node 3 []], Node 1 []] ('path' [1,2,3]) == True--- isDfsForestOf [Node 1 [], Node 2 [Node 3 []]] ('path' [1,2,3]) == False+-- symmetricClosure 'empty' == 'empty'+-- symmetricClosure ('vertex' x) == 'vertex' x+-- symmetricClosure ('edge' x y) == 'edges' [(x,y), (y,x)]+-- symmetricClosure x == 'overlay' x ('transpose' x)+-- symmetricClosure . symmetricClosure == symmetricClosure -- @-isDfsForestOf :: Forest Int -> AdjacencyIntMap -> Bool-isDfsForestOf f am = case go IntSet.empty f of- Just seen -> seen == vertexIntSet am- Nothing -> False- where- go seen [] = Just seen- go seen (t:ts) = do- let root = rootLabel t- guard $ root `IntSet.notMember` seen- guard $ and [ hasEdge root (rootLabel subTree) am | subTree <- subForest t ]- newSeen <- go (IntSet.insert root seen) (subForest t)- guard $ postIntSet root am `IntSet.isSubsetOf` newSeen- go newSeen ts+symmetricClosure :: AdjacencyIntMap -> AdjacencyIntMap+symmetricClosure m = overlay m (transpose m) --- | Check if a given list of vertices is a correct /topological sort/ of a graph.+-- | Compute the /transitive closure/ of a graph.+-- Complexity: /O(n * m * log(n)^2)/ time. -- -- @--- isTopSortOf [3,1,2] (1 * 2 + 3 * 1) == True--- isTopSortOf [1,2,3] (1 * 2 + 3 * 1) == False--- isTopSortOf [] (1 * 2 + 3 * 1) == False--- isTopSortOf [] 'empty' == True--- isTopSortOf [x] ('vertex' x) == True--- isTopSortOf [x] ('edge' x x) == False+-- transitiveClosure 'empty' == 'empty'+-- transitiveClosure ('vertex' x) == 'vertex' x+-- transitiveClosure ('edge' x y) == 'edge' x y+-- transitiveClosure ('path' $ 'Data.List.nub' xs) == 'clique' ('Data.List.nub' xs)+-- transitiveClosure . transitiveClosure == transitiveClosure -- @-isTopSortOf :: [Int] -> AdjacencyIntMap -> Bool-isTopSortOf xs m = go IntSet.empty xs+transitiveClosure :: AdjacencyIntMap -> AdjacencyIntMap+transitiveClosure old+ | old == new = old+ | otherwise = transitiveClosure new where- go seen [] = seen == IntMap.keysSet (adjacencyIntMap m)- go seen (v:vs) = postIntSet v m `IntSet.intersection` newSeen == IntSet.empty- && go newSeen vs- where- newSeen = IntSet.insert v seen+ new = overlay old (old `compose` old)
+ src/Algebra/Graph/AdjacencyIntMap/Algorithm.hs view
@@ -0,0 +1,198 @@+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.AdjacencyIntMap.Algorithm+-- Copyright : (c) Andrey Mokhov 2016-2018+-- License : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability : unstable+--+-- __Alga__ is a library for algebraic construction and manipulation of graphs+-- in Haskell. See <https://github.com/snowleopard/alga-paper this paper> for the+-- motivation behind the library, the underlying theory, and implementation details.+--+-- This module provides basic graph algorithms, such as /depth-first search/,+-- implemented for the "Algebra.Graph.AdjacencyIntMap" data type.+-----------------------------------------------------------------------------+module Algebra.Graph.AdjacencyIntMap.Algorithm (+ -- * Algorithms+ dfsForest, dfsForestFrom, dfs, reachable, topSort, isAcyclic,++ -- * Correctness properties+ isDfsForestOf, isTopSortOf+ ) where++import Control.Monad+import Data.Maybe+import Data.Tree++import Algebra.Graph.AdjacencyIntMap++import qualified Data.Graph.Typed as Typed+import qualified Data.IntMap.Strict as IntMap+import qualified Data.IntSet as IntSet++-- | Compute the /depth-first search/ forest of a graph that corresponds to+-- searching from each of the graph vertices in the 'Ord' @a@ order.+--+-- @+-- dfsForest 'empty' == []+-- 'forest' (dfsForest $ 'edge' 1 1) == 'vertex' 1+-- 'forest' (dfsForest $ 'edge' 1 2) == 'edge' 1 2+-- 'forest' (dfsForest $ 'edge' 2 1) == 'vertices' [1,2]+-- 'isSubgraphOf' ('forest' $ dfsForest x) x == True+-- 'isDfsForestOf' (dfsForest x) x == True+-- dfsForest . 'forest' . dfsForest == dfsForest+-- dfsForest ('vertices' vs) == 'map' (\\v -> Node v []) ('Data.List.nub' $ 'Data.List.sort' vs)+-- 'dfsForestFrom' ('vertexList' x) x == dfsForest x+-- dfsForest $ 3 * (1 + 4) * (1 + 5) == [ Node { rootLabel = 1+-- , subForest = [ Node { rootLabel = 5+-- , subForest = [] }]}+-- , Node { rootLabel = 3+-- , subForest = [ Node { rootLabel = 4+-- , subForest = [] }]}]+-- @+dfsForest :: AdjacencyIntMap -> Forest Int+dfsForest = Typed.dfsForest . Typed.fromAdjacencyIntMap++-- | Compute the /depth-first search/ forest of a graph, searching from each of+-- the given vertices in order. Note that the resulting forest does not+-- necessarily span the whole graph, as some vertices may be unreachable.+--+-- @+-- dfsForestFrom vs 'empty' == []+-- 'forest' (dfsForestFrom [1] $ 'edge' 1 1) == 'vertex' 1+-- 'forest' (dfsForestFrom [1] $ 'edge' 1 2) == 'edge' 1 2+-- 'forest' (dfsForestFrom [2] $ 'edge' 1 2) == 'vertex' 2+-- 'forest' (dfsForestFrom [3] $ 'edge' 1 2) == 'empty'+-- 'forest' (dfsForestFrom [2,1] $ 'edge' 1 2) == 'vertices' [1,2]+-- 'isSubgraphOf' ('forest' $ dfsForestFrom vs x) x == True+-- 'isDfsForestOf' (dfsForestFrom ('vertexList' x) x) x == True+-- dfsForestFrom ('vertexList' x) x == 'dfsForest' x+-- dfsForestFrom vs ('vertices' vs) == 'map' (\\v -> Node v []) ('Data.List.nub' vs)+-- dfsForestFrom [] x == []+-- dfsForestFrom [1,4] $ 3 * (1 + 4) * (1 + 5) == [ Node { rootLabel = 1+-- , subForest = [ Node { rootLabel = 5+-- , subForest = [] }+-- , Node { rootLabel = 4+-- , subForest = [] }]+-- @+dfsForestFrom :: [Int] -> AdjacencyIntMap -> Forest Int+dfsForestFrom vs = Typed.dfsForestFrom vs . Typed.fromAdjacencyIntMap++-- | Compute the list of vertices visited by the /depth-first search/ in a graph,+-- when searching from each of the given vertices in order.+--+-- @+-- dfs vs $ 'empty' == []+-- dfs [1] $ 'edge' 1 1 == [1]+-- dfs [1] $ 'edge' 1 2 == [1,2]+-- dfs [2] $ 'edge' 1 2 == [2]+-- dfs [3] $ 'edge' 1 2 == []+-- dfs [1,2] $ 'edge' 1 2 == [1,2]+-- dfs [2,1] $ 'edge' 1 2 == [2,1]+-- dfs [] $ x == []+-- dfs [1,4] $ 3 * (1 + 4) * (1 + 5) == [1,5,4]+-- 'isSubgraphOf' ('vertices' $ dfs vs x) x == True+-- @+dfs :: [Int] -> AdjacencyIntMap -> [Int]+dfs vs = concatMap flatten . dfsForestFrom vs++-- | Compute the list of vertices that are /reachable/ from a given source+-- vertex in a graph. The vertices in the resulting list appear in the+-- /depth-first order/.+--+-- @+-- reachable x $ 'empty' == []+-- reachable 1 $ 'vertex' 1 == [1]+-- reachable 1 $ 'vertex' 2 == []+-- reachable 1 $ 'edge' 1 1 == [1]+-- reachable 1 $ 'edge' 1 2 == [1,2]+-- reachable 4 $ 'path' [1..8] == [4..8]+-- reachable 4 $ 'circuit' [1..8] == [4..8] ++ [1..3]+-- reachable 8 $ 'clique' [8,7..1] == [8] ++ [1..7]+-- 'isSubgraphOf' ('vertices' $ reachable x y) y == True+-- @+reachable :: Int -> AdjacencyIntMap -> [Int]+reachable x = dfs [x]++-- | Compute the /topological sort/ of a graph or return @Nothing@ if the graph+-- is cyclic.+--+-- @+-- topSort (1 * 2 + 3 * 1) == Just [3,1,2]+-- topSort (1 * 2 + 2 * 1) == Nothing+-- fmap ('flip' 'isTopSortOf' x) (topSort x) /= Just False+-- 'isJust' . topSort == 'isAcyclic'+-- @+topSort :: AdjacencyIntMap -> Maybe [Int]+topSort m = if isTopSortOf result m then Just result else Nothing+ where+ result = Typed.topSort (Typed.fromAdjacencyIntMap m)++-- | Check if a given graph is /acyclic/.+--+-- @+-- isAcyclic (1 * 2 + 3 * 1) == True+-- isAcyclic (1 * 2 + 2 * 1) == False+-- isAcyclic . 'circuit' == 'null'+-- isAcyclic == 'isJust' . 'topSort'+-- @+isAcyclic :: AdjacencyIntMap -> Bool+isAcyclic = isJust . topSort++-- | Check if a given forest is a correct /depth-first search/ forest of a graph.+-- The implementation is based on the paper "Depth-First Search and Strong+-- Connectivity in Coq" by François Pottier.+--+-- @+-- isDfsForestOf [] 'empty' == True+-- isDfsForestOf [] ('vertex' 1) == False+-- isDfsForestOf [Node 1 []] ('vertex' 1) == True+-- isDfsForestOf [Node 1 []] ('vertex' 2) == False+-- isDfsForestOf [Node 1 [], Node 1 []] ('vertex' 1) == False+-- isDfsForestOf [Node 1 []] ('edge' 1 1) == True+-- isDfsForestOf [Node 1 []] ('edge' 1 2) == False+-- isDfsForestOf [Node 1 [], Node 2 []] ('edge' 1 2) == False+-- isDfsForestOf [Node 2 [], Node 1 []] ('edge' 1 2) == True+-- isDfsForestOf [Node 1 [Node 2 []]] ('edge' 1 2) == True+-- isDfsForestOf [Node 1 [], Node 2 []] ('vertices' [1,2]) == True+-- isDfsForestOf [Node 2 [], Node 1 []] ('vertices' [1,2]) == True+-- isDfsForestOf [Node 1 [Node 2 []]] ('vertices' [1,2]) == False+-- isDfsForestOf [Node 1 [Node 2 [Node 3 []]]] ('path' [1,2,3]) == True+-- isDfsForestOf [Node 1 [Node 3 [Node 2 []]]] ('path' [1,2,3]) == False+-- isDfsForestOf [Node 3 [], Node 1 [Node 2 []]] ('path' [1,2,3]) == True+-- isDfsForestOf [Node 2 [Node 3 []], Node 1 []] ('path' [1,2,3]) == True+-- isDfsForestOf [Node 1 [], Node 2 [Node 3 []]] ('path' [1,2,3]) == False+-- @+isDfsForestOf :: Forest Int -> AdjacencyIntMap -> Bool+isDfsForestOf f am = case go IntSet.empty f of+ Just seen -> seen == vertexIntSet am+ Nothing -> False+ where+ go seen [] = Just seen+ go seen (t:ts) = do+ let root = rootLabel t+ guard $ root `IntSet.notMember` seen+ guard $ and [ hasEdge root (rootLabel subTree) am | subTree <- subForest t ]+ newSeen <- go (IntSet.insert root seen) (subForest t)+ guard $ postIntSet root am `IntSet.isSubsetOf` newSeen+ go newSeen ts++-- | Check if a given list of vertices is a correct /topological sort/ of a graph.+--+-- @+-- isTopSortOf [3,1,2] (1 * 2 + 3 * 1) == True+-- isTopSortOf [1,2,3] (1 * 2 + 3 * 1) == False+-- isTopSortOf [] (1 * 2 + 3 * 1) == False+-- isTopSortOf [] 'empty' == True+-- isTopSortOf [x] ('vertex' x) == True+-- isTopSortOf [x] ('edge' x x) == False+-- @+isTopSortOf :: [Int] -> AdjacencyIntMap -> Bool+isTopSortOf xs m = go IntSet.empty xs+ where+ go seen [] = seen == IntMap.keysSet (adjacencyIntMap m)+ go seen (v:vs) = postIntSet v m `IntSet.intersection` newSeen == IntSet.empty+ && go newSeen vs+ where+ newSeen = IntSet.insert v seen
src/Algebra/Graph/AdjacencyIntMap/Internal.hs view
@@ -12,10 +12,14 @@ ----------------------------------------------------------------------------- module Algebra.Graph.AdjacencyIntMap.Internal ( -- * Adjacency map implementation- AdjacencyIntMap (..), empty, vertex, overlay, connect, fromAdjacencyIntSets,- consistent+ AdjacencyIntMap (..), consistent ) where +import Prelude ()+import Prelude.Compat hiding (null)++import Data.Foldable (foldMap)+import Data.Monoid (getSum, Sum (..)) import Data.IntMap.Strict (IntMap, keysSet, fromSet) import Data.IntSet (IntSet) import Data.List@@ -35,6 +39,14 @@ > 1 + 2 * 3 == overlay (vertex 1) (connect (vertex 2) (vertex 3)) > 1 * (2 + 3) == connect (vertex 1) (overlay (vertex 2) (vertex 3)) +__Note:__ the 'Num' instance does not satisfy several "customary laws" of 'Num',+which dictate that 'fromInteger' @0@ and 'fromInteger' @1@ should act as+additive and multiplicative identities, and 'negate' as additive inverse.+Nevertheless, overloading 'fromInteger', '+' and '*' is very convenient when+working with algebraic graphs; we hope that in future Haskell's Prelude will+provide a more fine-grained class hierarchy for algebraic structures, which we+would be able to utilise without violating any laws.+ The 'Show' instance is defined using basic graph construction primitives: @show (empty :: AdjacencyIntMap Int) == "empty"@@ -84,10 +96,36 @@ When specifying the time and memory complexity of graph algorithms, /n/ and /m/ will denote the number of vertices and edges in the graph, respectively.++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:++@'Algebra.Graph.AdjacencyIntMap.vertex' 1 < 'Algebra.Graph.AdjacencyIntMap.vertex' 2+'Algebra.Graph.AdjacencyIntMap.vertex' 3 < 'Algebra.Graph.AdjacencyIntMap.edge' 1 2+'Algebra.Graph.AdjacencyIntMap.vertex' 1 < 'Algebra.Graph.AdjacencyIntMap.edge' 1 1+'Algebra.Graph.AdjacencyIntMap.edge' 1 1 < 'Algebra.Graph.AdjacencyIntMap.edge' 1 2+'Algebra.Graph.AdjacencyIntMap.edge' 1 2 < 'Algebra.Graph.AdjacencyIntMap.edge' 1 1 + 'Algebra.Graph.AdjacencyIntMap.edge' 2 2+'Algebra.Graph.AdjacencyIntMap.edge' 1 2 < 'Algebra.Graph.AdjacencyIntMap.edge' 1 3@++Note that the resulting order refines the 'Algebra.Graph.AdjacencyIntMap.isSubgraphOf'+relation and is compatible with 'Algebra.Graph.AdjacencyIntMap.overlay' and+'Algebra.Graph.AdjacencyIntMap.connect' operations:++@'Algebra.Graph.AdjacencyIntMap.isSubgraphOf' x y ==> x <= y@++@'Algebra.Graph.AdjacencyIntMap.empty' <= x+x <= x + y+x + y <= x * y@ -} newtype AdjacencyIntMap = AM {- -- | The /adjacency map/ of the graph: each vertex is associated with a set- -- of its direct successors. Complexity: /O(1)/ time and memory.+ -- | The /adjacency map/ of a graph: each vertex is associated with a set of+ -- its direct successors. Complexity: /O(1)/ time and memory. -- -- @ -- adjacencyIntMap 'empty' == IntMap.'IntMap.empty'@@ -98,113 +136,47 @@ adjacencyIntMap :: IntMap IntSet } deriving Eq instance Show AdjacencyIntMap where- show (AM m)- | null vs = "empty"- | null es = vshow vs- | vs == used = eshow es- | otherwise = "overlay (" ++ vshow (vs \\ used) ++ ") (" ++ eshow es ++ ")"+ showsPrec p (AM m)+ | null vs = showString "empty"+ | null es = showParen (p > 10) $ vshow vs+ | vs == used = showParen (p > 10) $ eshow es+ | otherwise = showParen (p > 10) $+ showString "overlay (" . vshow (vs \\ used) .+ showString ") (" . eshow es . showString ")" where vs = IntSet.toAscList (keysSet m) es = internalEdgeList m- vshow [x] = "vertex " ++ show x- vshow xs = "vertices " ++ show xs- eshow [(x, y)] = "edge " ++ show x ++ " " ++ show y- eshow xs = "edges " ++ show xs+ vshow [x] = showString "vertex " . showsPrec 11 x+ vshow xs = showString "vertices " . showsPrec 11 xs+ eshow [(x, y)] = showString "edge " . showsPrec 11 x .+ showString " " . showsPrec 11 y+ eshow xs = showString "edges " . showsPrec 11 xs used = IntSet.toAscList (referredToVertexSet m) --- | Construct the /empty graph/.--- Complexity: /O(1)/ time and memory.------ @--- 'Algebra.Graph.AdjacencyIntMap.isEmpty' empty == True--- 'Algebra.Graph.AdjacencyIntMap.hasVertex' x empty == False--- 'Algebra.Graph.AdjacencyIntMap.vertexCount' empty == 0--- 'Algebra.Graph.AdjacencyIntMap.edgeCount' empty == 0--- @-empty :: AdjacencyIntMap-empty = AM IntMap.empty-{-# NOINLINE [1] empty #-}---- | Construct the graph comprising /a single isolated vertex/.--- Complexity: /O(1)/ time and memory.------ @--- 'Algebra.Graph.AdjacencyIntMap.isEmpty' (vertex x) == False--- 'Algebra.Graph.AdjacencyIntMap.hasVertex' x (vertex x) == True--- 'Algebra.Graph.AdjacencyIntMap.vertexCount' (vertex x) == 1--- 'Algebra.Graph.AdjacencyIntMap.edgeCount' (vertex x) == 0--- @-vertex :: Int -> AdjacencyIntMap-vertex x = AM $ IntMap.singleton x IntSet.empty-{-# NOINLINE [1] vertex #-}---- | /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.AdjacencyIntMap.isEmpty' (overlay x y) == 'Algebra.Graph.AdjacencyIntMap.isEmpty' x && 'Algebra.Graph.AdjacencyIntMap.isEmpty' y--- 'Algebra.Graph.AdjacencyIntMap.hasVertex' z (overlay x y) == 'Algebra.Graph.AdjacencyIntMap.hasVertex' z x || 'Algebra.Graph.AdjacencyIntMap.hasVertex' z y--- 'Algebra.Graph.AdjacencyIntMap.vertexCount' (overlay x y) >= 'Algebra.Graph.AdjacencyIntMap.vertexCount' x--- 'Algebra.Graph.AdjacencyIntMap.vertexCount' (overlay x y) <= 'Algebra.Graph.AdjacencyIntMap.vertexCount' x + 'Algebra.Graph.AdjacencyIntMap.vertexCount' y--- 'Algebra.Graph.AdjacencyIntMap.edgeCount' (overlay x y) >= 'Algebra.Graph.AdjacencyIntMap.edgeCount' x--- 'Algebra.Graph.AdjacencyIntMap.edgeCount' (overlay x y) <= 'Algebra.Graph.AdjacencyIntMap.edgeCount' x + 'Algebra.Graph.AdjacencyIntMap.edgeCount' y--- 'Algebra.Graph.AdjacencyIntMap.vertexCount' (overlay 1 2) == 2--- 'Algebra.Graph.AdjacencyIntMap.edgeCount' (overlay 1 2) == 0--- @-overlay :: AdjacencyIntMap -> AdjacencyIntMap -> AdjacencyIntMap-overlay x y = AM $ IntMap.unionWith IntSet.union (adjacencyIntMap x) (adjacencyIntMap y)-{-# NOINLINE [1] overlay #-}---- | /Connect/ two graphs. This is an 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)/.------ @--- 'Algebra.Graph.AdjacencyIntMap.isEmpty' (connect x y) == 'Algebra.Graph.AdjacencyIntMap.isEmpty' x && 'Algebra.Graph.AdjacencyIntMap.isEmpty' y--- 'Algebra.Graph.AdjacencyIntMap.hasVertex' z (connect x y) == 'Algebra.Graph.AdjacencyIntMap.hasVertex' z x || 'Algebra.Graph.AdjacencyIntMap.hasVertex' z y--- 'Algebra.Graph.AdjacencyIntMap.vertexCount' (connect x y) >= 'Algebra.Graph.AdjacencyIntMap.vertexCount' x--- 'Algebra.Graph.AdjacencyIntMap.vertexCount' (connect x y) <= 'Algebra.Graph.AdjacencyIntMap.vertexCount' x + 'Algebra.Graph.AdjacencyIntMap.vertexCount' y--- 'Algebra.Graph.AdjacencyIntMap.edgeCount' (connect x y) >= 'Algebra.Graph.AdjacencyIntMap.edgeCount' x--- 'Algebra.Graph.AdjacencyIntMap.edgeCount' (connect x y) >= 'Algebra.Graph.AdjacencyIntMap.edgeCount' y--- 'Algebra.Graph.AdjacencyIntMap.edgeCount' (connect x y) >= 'Algebra.Graph.AdjacencyIntMap.vertexCount' x * 'Algebra.Graph.AdjacencyIntMap.vertexCount' y--- 'Algebra.Graph.AdjacencyIntMap.edgeCount' (connect x y) <= 'Algebra.Graph.AdjacencyIntMap.vertexCount' x * 'Algebra.Graph.AdjacencyIntMap.vertexCount' y + 'Algebra.Graph.AdjacencyIntMap.edgeCount' x + 'Algebra.Graph.AdjacencyIntMap.edgeCount' y--- 'Algebra.Graph.AdjacencyIntMap.vertexCount' (connect 1 2) == 2--- 'Algebra.Graph.AdjacencyIntMap.edgeCount' (connect 1 2) == 1--- @-connect :: AdjacencyIntMap -> AdjacencyIntMap -> AdjacencyIntMap-connect x y = AM $ IntMap.unionsWith IntSet.union [ adjacencyIntMap x, adjacencyIntMap y,- fromSet (const . keysSet $ adjacencyIntMap y) (keysSet $ adjacencyIntMap x) ]-{-# NOINLINE [1] connect #-}+instance Ord AdjacencyIntMap where+ compare (AM x) (AM y) = mconcat+ [ compare (vNum x) (vNum y)+ , compare (vSet x) (vSet y)+ , compare (eNum x) (eNum y)+ , compare x y ]+ where+ vNum = IntMap.size+ vSet = IntMap.keysSet+ eNum = getSum . foldMap (Sum . IntSet.size) +-- | __Note:__ this does not satisfy the usual ring laws; see 'AdjacencyIntMap'+-- for more details. instance Num AdjacencyIntMap where- fromInteger = vertex . fromInteger- (+) = overlay- (*) = connect- signum = const empty- abs = id- negate = id+ fromInteger x = AM $ IntMap.singleton (fromInteger x) IntSet.empty+ x + y = AM $ IntMap.unionWith IntSet.union (adjacencyIntMap x) (adjacencyIntMap y)+ x * y = AM $ IntMap.unionsWith IntSet.union [ adjacencyIntMap x, adjacencyIntMap y,+ fromSet (const . keysSet $ adjacencyIntMap y) (keysSet $ adjacencyIntMap x) ]+ signum = const (AM IntMap.empty)+ abs = id+ negate = id instance NFData AdjacencyIntMap where rnf (AM a) = rnf a---- | Construct a graph from a list of adjacency sets.--- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.------ @--- fromAdjacencyIntSets [] == 'Algebra.Graph.AdjacencyIntMap.empty'--- fromAdjacencyIntSets [(x, IntSet.'IntSet.empty')] == 'Algebra.Graph.AdjacencyIntMap.vertex' x--- fromAdjacencyIntSets [(x, IntSet.'IntSet.singleton' y)] == 'Algebra.Graph.AdjacencyIntMap.edge' x y--- fromAdjacencyIntSets . map (fmap IntSet.'IntSet.fromList') . 'Algebra.Graph.AdjacencyIntMap.adjacencyList' == id--- 'Algebra.Graph.AdjacencyIntMap.overlay' (fromAdjacencyIntSets xs) (fromAdjacencyIntSets ys) == fromAdjacencyIntSets (xs ++ ys)--- @-fromAdjacencyIntSets :: [(Int, IntSet)] -> AdjacencyIntMap-fromAdjacencyIntSets ss = AM $ IntMap.unionWith IntSet.union vs es- where- vs = IntMap.fromSet (const IntSet.empty) . IntSet.unions $ map snd ss- es = IntMap.fromListWith IntSet.union ss -- | Check if the internal graph representation is consistent, i.e. that all -- edges refer to existing vertices. It should be impossible to create an
src/Algebra/Graph/AdjacencyMap.hs view
@@ -10,9 +10,10 @@ -- in Haskell. See <https://github.com/snowleopard/alga-paper this paper> for the -- motivation behind the library, the underlying theory, and implementation details. ----- This module defines the 'AdjacencyMap' data type, as well as associated--- operations and algorithms. 'AdjacencyMap' is an instance of the 'C.Graph' type--- class, which can be used for polymorphic graph construction and manipulation.+-- This module defines the 'AdjacencyMap' data type and associated functions.+-- See "Algebra.Graph.AdjacencyMap.Algorithm" for implementations of basic graph+-- algorithms. 'AdjacencyMap' is an instance of the 'C.Graph' type class, which+-- can be used for polymorphic graph construction and manipulation. -- "Algebra.Graph.AdjacencyIntMap" defines adjacency maps specialised to graphs -- with @Int@ vertices. -----------------------------------------------------------------------------@@ -28,37 +29,56 @@ -- * Graph properties isEmpty, hasVertex, hasEdge, vertexCount, edgeCount, vertexList, edgeList,- adjacencyList, vertexSet, vertexIntSet, edgeSet, preSet, postSet,+ adjacencyList, vertexSet, edgeSet, preSet, postSet, -- * Standard families of graphs- path, circuit, clique, biclique, star, stars, tree, forest,+ path, circuit, clique, biclique, star, stars, fromAdjacencySets, tree,+ forest, -- * Graph transformation removeVertex, removeEdge, replaceVertex, mergeVertices, transpose, gmap, induce, - -- * Algorithms- dfsForest, dfsForestFrom, dfs, reachable, topSort, isAcyclic, scc,-- -- * Correctness properties- isDfsForestOf, isTopSortOf- ) where+ -- * Relational operations+ compose, closure, reflexiveClosure, symmetricClosure, transitiveClosure+ ) where -import Control.Monad-import Data.Foldable (foldMap, toList)-import Data.Maybe+import Data.Foldable (foldMap) import Data.Monoid import Data.Set (Set) import Data.Tree import Algebra.Graph.AdjacencyMap.Internal -import qualified Data.Graph.Typed as Typed-import qualified Data.Graph as KL-import qualified Data.Map.Strict as Map-import qualified Data.Set as Set-import qualified Data.IntSet as IntSet+import qualified Data.Map.Strict as Map+import qualified Data.Set as Set +-- | Construct the /empty graph/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'isEmpty' empty == True+-- 'hasVertex' x empty == False+-- 'vertexCount' empty == 0+-- 'edgeCount' empty == 0+-- @+empty :: AdjacencyMap a+empty = AM Map.empty+{-# NOINLINE [1] empty #-}++-- | Construct the graph comprising /a single isolated vertex/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'isEmpty' (vertex x) == False+-- 'hasVertex' x (vertex x) == True+-- 'vertexCount' (vertex x) == 1+-- 'edgeCount' (vertex x) == 0+-- @+vertex :: a -> AdjacencyMap a+vertex x = AM $ Map.singleton x Set.empty+{-# NOINLINE [1] vertex #-}+ -- | Construct the graph comprising /a single edge/. -- Complexity: /O(1)/ time, memory. --@@ -73,6 +93,47 @@ edge x y | x == y = AM $ Map.singleton x (Set.singleton y) | otherwise = AM $ Map.fromList [(x, Set.singleton y), (y, 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.+--+-- @+-- 'isEmpty' (overlay x y) == 'isEmpty' x && 'isEmpty' y+-- 'hasVertex' z (overlay x y) == 'hasVertex' z x || 'hasVertex' z y+-- 'vertexCount' (overlay x y) >= 'vertexCount' x+-- 'vertexCount' (overlay x y) <= 'vertexCount' x + 'vertexCount' y+-- 'edgeCount' (overlay x y) >= 'edgeCount' x+-- 'edgeCount' (overlay x y) <= 'edgeCount' x + 'edgeCount' y+-- 'vertexCount' (overlay 1 2) == 2+-- 'edgeCount' (overlay 1 2) == 0+-- @+overlay :: Ord a => AdjacencyMap a -> AdjacencyMap a -> AdjacencyMap a+overlay x y = AM $ Map.unionWith Set.union (adjacencyMap x) (adjacencyMap y)+{-# NOINLINE [1] overlay #-}++-- | /Connect/ two graphs. This is an 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)/.+--+-- @+-- 'isEmpty' (connect x y) == 'isEmpty' x && 'isEmpty' y+-- 'hasVertex' z (connect x y) == 'hasVertex' z x || 'hasVertex' z y+-- 'vertexCount' (connect x y) >= 'vertexCount' x+-- 'vertexCount' (connect x y) <= 'vertexCount' x + 'vertexCount' y+-- 'edgeCount' (connect x y) >= 'edgeCount' x+-- 'edgeCount' (connect x y) >= 'edgeCount' y+-- 'edgeCount' (connect x y) >= 'vertexCount' x * 'vertexCount' y+-- 'edgeCount' (connect x y) <= 'vertexCount' x * 'vertexCount' y + 'edgeCount' x + 'edgeCount' y+-- 'vertexCount' (connect 1 2) == 2+-- 'edgeCount' (connect 1 2) == 1+-- @+connect :: Ord a => AdjacencyMap a -> AdjacencyMap a -> AdjacencyMap a+connect x y = AM $ Map.unionsWith Set.union $ adjacencyMap x : adjacencyMap y :+ [ Map.fromSet (const . Map.keysSet $ adjacencyMap y) (Map.keysSet $ adjacencyMap x) ]+{-# NOINLINE [1] connect #-}+ -- | 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.@@ -133,11 +194,12 @@ -- 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 '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 x y ==> x <= y -- @ isSubgraphOf :: Ord a => AdjacencyMap a -> AdjacencyMap a -> Bool isSubgraphOf x y = Map.isSubmapOfBy Set.isSubsetOf (adjacencyMap x) (adjacencyMap y)@@ -162,7 +224,7 @@ -- hasVertex x 'empty' == False -- hasVertex x ('vertex' x) == True -- hasVertex 1 ('vertex' 2) == False--- hasVertex x . 'removeVertex' x == const False+-- hasVertex x . 'removeVertex' x == 'const' False -- @ hasVertex :: Ord a => a -> AdjacencyMap a -> Bool hasVertex x = Map.member x . adjacencyMap@@ -174,7 +236,7 @@ -- hasEdge x y 'empty' == False -- hasEdge x y ('vertex' z) == False -- hasEdge x y ('edge' x y) == True--- hasEdge x y . 'removeEdge' x y == const False+-- hasEdge x y . 'removeEdge' x y == 'const' False -- hasEdge x y == 'elem' (x,y) . 'edgeList' -- @ hasEdge :: Ord a => a -> a -> AdjacencyMap a -> Bool@@ -186,9 +248,10 @@ -- Complexity: /O(1)/ time. -- -- @--- vertexCount 'empty' == 0--- vertexCount ('vertex' x) == 1--- vertexCount == 'length' . 'vertexList'+-- vertexCount 'empty' == 0+-- vertexCount ('vertex' x) == 1+-- vertexCount == 'length' . 'vertexList'+-- vertexCount x \< vertexCount y ==> x \< y -- @ vertexCount :: AdjacencyMap a -> Int vertexCount = Map.size . adjacencyMap@@ -225,7 +288,7 @@ -- edgeList ('edge' x y) == [(x,y)] -- edgeList ('star' 2 [3,1]) == [(2,1), (2,3)] -- edgeList . 'edges' == 'Data.List.nub' . 'Data.List.sort'--- edgeList . 'transpose' == 'Data.List.sort' . map 'Data.Tuple.swap' . edgeList+-- edgeList . 'transpose' == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . edgeList -- @ edgeList :: AdjacencyMap a -> [(a, a)] edgeList (AM m) = [ (x, y) | (x, ys) <- Map.toAscList m, y <- Set.toAscList ys ]@@ -237,24 +300,10 @@ -- vertexSet 'empty' == Set.'Set.empty' -- vertexSet . 'vertex' == Set.'Set.singleton' -- vertexSet . 'vertices' == Set.'Set.fromList'--- vertexSet . 'clique' == Set.'Set.fromList' -- @ vertexSet :: AdjacencyMap a -> Set a vertexSet = Map.keysSet . adjacencyMap --- | The set of vertices of a given graph. Like 'vertexSet' but specialised for--- graphs with vertices of type 'Int'.--- Complexity: /O(n)/ time and memory.------ @--- vertexIntSet 'empty' == IntSet.'IntSet.empty'--- vertexIntSet . 'vertex' == IntSet.'IntSet.singleton'--- vertexIntSet . 'vertices' == IntSet.'IntSet.fromList'--- vertexIntSet . 'clique' == IntSet.'IntSet.fromList'--- @-vertexIntSet :: AdjacencyMap Int -> IntSet.IntSet-vertexIntSet = IntSet.fromAscList . Set.toAscList . vertexSet- -- | The set of edges of a given graph. -- Complexity: /O((n + m) * log(m))/ time and /O(m)/ memory. --@@ -264,7 +313,7 @@ -- edgeSet ('edge' x y) == Set.'Set.singleton' (x,y) -- edgeSet . 'edges' == Set.'Set.fromList' -- @-edgeSet :: Ord a => AdjacencyMap a -> Set (a, a)+edgeSet :: Eq a => AdjacencyMap a -> Set (a, a) edgeSet = Set.fromAscList . edgeList -- | The sorted /adjacency list/ of a graph.@@ -341,7 +390,7 @@ -- 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 (xs '++' ys) == 'connect' (clique xs) (clique ys) -- clique . 'reverse' == 'transpose' . clique -- @ clique :: Ord a => [a] -> AdjacencyMap a@@ -393,13 +442,29 @@ -- stars [(x, [])] == 'vertex' x -- stars [(x, [y])] == 'edge' x y -- stars [(x, ys)] == 'star' x ys--- stars == 'overlays' . map (uncurry 'star')+-- stars == 'overlays' . 'map' ('uncurry' 'star') -- stars . 'adjacencyList' == id--- 'overlay' (stars xs) (stars ys) == stars (xs ++ ys)+-- 'overlay' (stars xs) (stars ys) == stars (xs '++' ys) -- @ stars :: Ord a => [(a, [a])] -> AdjacencyMap a stars = fromAdjacencySets . map (fmap Set.fromList) +-- | Construct a graph from a list of adjacency sets; a variation of 'stars'.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- fromAdjacencySets [] == 'empty'+-- fromAdjacencySets [(x, Set.'Set.empty')] == 'vertex' x+-- fromAdjacencySets [(x, Set.'Set.singleton' y)] == 'edge' x y+-- fromAdjacencySets . 'map' ('fmap' Set.'Set.fromList') == 'stars'+-- 'overlay' (fromAdjacencySets xs) (fromAdjacencySets ys) == fromAdjacencySets (xs '++' ys)+-- @+fromAdjacencySets :: Ord a => [(a, Set a)] -> AdjacencyMap a+fromAdjacencySets ss = AM $ Map.unionWith Set.union vs es+ where+ vs = Map.fromSet (const Set.empty) . Set.unions $ map snd ss+ es = Map.fromListWith Set.union ss+ -- | The /tree graph/ constructed from a given 'Tree' data structure. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory. --@@ -421,7 +486,7 @@ -- 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 == 'overlays' . 'map' 'tree' -- @ forest :: Ord a => Forest a -> AdjacencyMap a forest = overlays . map tree@@ -469,10 +534,10 @@ -- /O(1)/ to be evaluated. -- -- @--- mergeVertices (const False) x == id+-- 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 'even' 1 (0 * 2) == 1 * 1+-- mergeVertices 'odd' 1 (3 + 4 * 5) == 4 * 1 -- @ mergeVertices :: Ord a => (a -> Bool) -> a -> AdjacencyMap a -> AdjacencyMap a mergeVertices p v = gmap $ \u -> if p u then v else u@@ -485,7 +550,7 @@ -- transpose ('vertex' x) == 'vertex' x -- transpose ('edge' x y) == 'edge' y x -- transpose . transpose == id--- 'edgeList' . transpose == 'Data.List.sort' . map 'Data.Tuple.swap' . 'edgeList'+-- 'edgeList' . transpose == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . 'edgeList' -- @ transpose :: Ord a => AdjacencyMap a -> AdjacencyMap a transpose (AM m) = AM $ Map.foldrWithKey combine vs m@@ -516,7 +581,7 @@ -- 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 'id' == 'id' -- gmap f . gmap g == gmap (f . g) -- @ gmap :: (Ord a, Ord b) => (a -> b) -> AdjacencyMap a -> AdjacencyMap b@@ -528,8 +593,8 @@ -- be evaluated. -- -- @--- induce (const True ) x == x--- induce (const False) x == 'empty'+-- 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@@ -537,188 +602,92 @@ induce :: (a -> Bool) -> AdjacencyMap a -> AdjacencyMap a induce p = AM . Map.map (Set.filter p) . Map.filterWithKey (\k _ -> p k) . adjacencyMap --- | Compute the /depth-first search/ forest of a graph that corresponds to--- searching from each of the graph vertices in the 'Ord' @a@ order.------ @--- dfsForest 'empty' == []--- 'forest' (dfsForest $ 'edge' 1 1) == 'vertex' 1--- 'forest' (dfsForest $ 'edge' 1 2) == 'edge' 1 2--- 'forest' (dfsForest $ 'edge' 2 1) == 'vertices' [1,2]--- 'isSubgraphOf' ('forest' $ dfsForest x) x == True--- 'isDfsForestOf' (dfsForest x) x == True--- dfsForest . 'forest' . dfsForest == dfsForest--- dfsForest ('vertices' vs) == map (\\v -> Node v []) ('Data.List.nub' $ 'Data.List.sort' vs)--- 'dfsForestFrom' ('vertexList' x) x == dfsForest x--- dfsForest $ 3 * (1 + 4) * (1 + 5) == [ Node { rootLabel = 1--- , subForest = [ Node { rootLabel = 5--- , subForest = [] }]}--- , Node { rootLabel = 3--- , subForest = [ Node { rootLabel = 4--- , subForest = [] }]}]--- @-dfsForest :: Ord a => AdjacencyMap a -> Forest a-dfsForest g = dfsForestFrom (vertexList g) g---- | Compute the /depth-first search/ forest of a graph, searching from each of--- the given vertices in order. Note that the resulting forest does not--- necessarily span the whole graph, as some vertices may be unreachable.------ @--- dfsForestFrom vs 'empty' == []--- 'forest' (dfsForestFrom [1] $ 'edge' 1 1) == 'vertex' 1--- 'forest' (dfsForestFrom [1] $ 'edge' 1 2) == 'edge' 1 2--- 'forest' (dfsForestFrom [2] $ 'edge' 1 2) == 'vertex' 2--- 'forest' (dfsForestFrom [3] $ 'edge' 1 2) == 'empty'--- 'forest' (dfsForestFrom [2,1] $ 'edge' 1 2) == 'vertices' [1,2]--- 'isSubgraphOf' ('forest' $ dfsForestFrom vs x) x == True--- 'isDfsForestOf' (dfsForestFrom ('vertexList' x) x) x == True--- dfsForestFrom ('vertexList' x) x == 'dfsForest' x--- dfsForestFrom vs ('vertices' vs) == map (\\v -> Node v []) ('Data.List.nub' vs)--- dfsForestFrom [] x == []--- dfsForestFrom [1,4] $ 3 * (1 + 4) * (1 + 5) == [ Node { rootLabel = 1--- , subForest = [ Node { rootLabel = 5--- , subForest = [] }--- , Node { rootLabel = 4--- , subForest = [] }]--- @-dfsForestFrom :: Ord a => [a] -> AdjacencyMap a -> Forest a-dfsForestFrom vs = Typed.dfsForestFrom vs . Typed.fromAdjacencyMap---- | Compute the list of vertices visited by the /depth-first search/ in a--- graph, when searching from each of the given vertices in order.------ @--- dfs vs $ 'empty' == []--- dfs [1] $ 'edge' 1 1 == [1]--- dfs [1] $ 'edge' 1 2 == [1,2]--- dfs [2] $ 'edge' 1 2 == [2]--- dfs [3] $ 'edge' 1 2 == []--- dfs [1,2] $ 'edge' 1 2 == [1,2]--- dfs [2,1] $ 'edge' 1 2 == [2,1]--- dfs [] $ x == []--- dfs [1,4] $ 3 * (1 + 4) * (1 + 5) == [1,5,4]--- 'isSubgraphOf' ('vertices' $ dfs vs x) x == True--- @-dfs :: Ord a => [a] -> AdjacencyMap a -> [a]-dfs vs = concatMap flatten . dfsForestFrom vs---- | Compute the list of vertices that are /reachable/ from a given source--- vertex in a graph. The vertices in the resulting list appear in the--- /depth-first order/.------ @--- reachable x $ 'empty' == []--- reachable 1 $ 'vertex' 1 == [1]--- reachable 1 $ 'vertex' 2 == []--- reachable 1 $ 'edge' 1 1 == [1]--- reachable 1 $ 'edge' 1 2 == [1,2]--- reachable 4 $ 'path' [1..8] == [4..8]--- reachable 4 $ 'circuit' [1..8] == [4..8] ++ [1..3]--- reachable 8 $ 'clique' [8,7..1] == [8] ++ [1..7]--- 'isSubgraphOf' ('vertices' $ reachable x y) y == True--- @-reachable :: Ord a => a -> AdjacencyMap a -> [a]-reachable x = dfs [x]---- | Compute the /topological sort/ of a graph or return @Nothing@ if the graph--- is cyclic.+-- | Left-to-right /relational composition/ of graphs: vertices @x@ and @z@ are+-- connected in the resulting graph if there is a vertex @y@, such that @x@ is+-- connected to @y@ in the first graph, and @y@ is connected to @z@ in the+-- second graph. There are no isolated vertices in the result. This operation is+-- associative, has 'empty' and single-'vertex' graphs as /annihilating zeroes/,+-- and distributes over 'overlay'.+-- Complexity: /O(n * m * log(n))/ time and /O(n + m)/ memory. -- -- @--- topSort (1 * 2 + 3 * 1) == Just [3,1,2]--- topSort (1 * 2 + 2 * 1) == Nothing--- fmap (flip 'isTopSortOf' x) (topSort x) /= Just False--- 'isJust' . topSort == 'isAcyclic'+-- compose 'empty' x == 'empty'+-- compose x 'empty' == 'empty'+-- compose ('vertex' x) y == 'empty'+-- compose x ('vertex' y) == 'empty'+-- compose x (compose y z) == compose (compose x y) z+-- compose x ('overlay' y z) == 'overlay' (compose x y) (compose x z)+-- compose ('overlay' x y) z == 'overlay' (compose x z) (compose y z)+-- compose ('edge' x y) ('edge' y z) == 'edge' x z+-- compose ('path' [1..5]) ('path' [1..5]) == 'edges' [(1,3), (2,4), (3,5)]+-- compose ('circuit' [1..5]) ('circuit' [1..5]) == 'circuit' [1,3,5,2,4] -- @-topSort :: Ord a => AdjacencyMap a -> Maybe [a]-topSort m = if isTopSortOf result m then Just result else Nothing+compose :: Ord a => AdjacencyMap a -> AdjacencyMap a -> AdjacencyMap a+compose x y = fromAdjacencySets+ [ (t, ys) | v <- Set.toList vs, let ys = postSet v y, not (Set.null ys)+ , t <- Set.toList (postSet v tx) ] where- result = Typed.topSort (Typed.fromAdjacencyMap m)+ tx = transpose x+ vs = vertexSet x `Set.union` vertexSet y --- | Check if a given graph is /acyclic/.+-- | Compute the /reflexive and transitive closure/ of a graph.+-- Complexity: /O(n * m * log(n)^2)/ time. -- -- @--- isAcyclic (1 * 2 + 3 * 1) == True--- isAcyclic (1 * 2 + 2 * 1) == False--- isAcyclic . 'circuit' == 'null'--- isAcyclic == 'isJust' . 'topSort'+-- closure 'empty' == 'empty'+-- closure ('vertex' x) == 'edge' x x+-- closure ('edge' x x) == 'edge' x x+-- closure ('edge' x y) == 'edges' [(x,x), (x,y), (y,y)]+-- closure ('path' $ 'Data.List.nub' xs) == 'reflexiveClosure' ('clique' $ 'Data.List.nub' xs)+-- closure == 'reflexiveClosure' . 'transitiveClosure'+-- closure == 'transitiveClosure' . 'reflexiveClosure'+-- closure . closure == closure+-- 'postSet' x (closure y) == Set.'Set.fromList' ('Algebra.Graph.ToGraph.reachable' x y) -- @-isAcyclic :: Ord a => AdjacencyMap a -> Bool-isAcyclic = isJust . topSort+closure :: Ord a => AdjacencyMap a -> AdjacencyMap a+closure = reflexiveClosure . transitiveClosure --- | Compute the /condensation/ of a graph, where each vertex corresponds to a--- /strongly-connected component/ of the original graph.+-- | Compute the /reflexive closure/ of a graph by adding a self-loop to every+-- vertex.+-- Complexity: /O(n * log(n))/ time. -- -- @--- scc 'empty' == 'empty'--- scc ('vertex' x) == 'vertex' (Set.'Set.singleton' x)--- scc ('edge' x y) == 'edge' (Set.'Set.singleton' x) (Set.'Set.singleton' y)--- scc ('circuit' (1:xs)) == 'edge' (Set.'Set.fromList' (1:xs)) (Set.'Set.fromList' (1:xs))--- scc (3 * 1 * 4 * 1 * 5) == 'edges' [ (Set.'Set.fromList' [1,4], Set.'Set.fromList' [1,4])--- , (Set.'Set.fromList' [1,4], Set.'Set.fromList' [5] )--- , (Set.'Set.fromList' [3] , Set.'Set.fromList' [1,4])--- , (Set.'Set.fromList' [3] , Set.'Set.fromList' [5] )]+-- reflexiveClosure 'empty' == 'empty'+-- reflexiveClosure ('vertex' x) == 'edge' x x+-- reflexiveClosure ('edge' x x) == 'edge' x x+-- reflexiveClosure ('edge' x y) == 'edges' [(x,x), (x,y), (y,y)]+-- reflexiveClosure . reflexiveClosure == reflexiveClosure -- @-scc :: Ord a => AdjacencyMap a -> AdjacencyMap (Set a)-scc m = gmap (\v -> Map.findWithDefault Set.empty v components) m- where- (Typed.GraphKL g r _) = Typed.fromAdjacencyMap m- components = Map.fromList $ concatMap (expand . fmap r . toList) (KL.scc g)- expand xs = let s = Set.fromList xs in map (\x -> (x, s)) xs+reflexiveClosure :: Ord a => AdjacencyMap a -> AdjacencyMap a+reflexiveClosure (AM m) = AM $ Map.mapWithKey (\k -> Set.insert k) m --- | Check if a given forest is a correct /depth-first search/ forest of a graph.--- The implementation is based on the paper "Depth-First Search and Strong--- Connectivity in Coq" by François Pottier.+-- | Compute the /symmetric closure/ of a graph by overlaying it with its own+-- transpose.+-- Complexity: /O((n + m) * log(n))/ time. -- -- @--- isDfsForestOf [] 'empty' == True--- isDfsForestOf [] ('vertex' 1) == False--- isDfsForestOf [Node 1 []] ('vertex' 1) == True--- isDfsForestOf [Node 1 []] ('vertex' 2) == False--- isDfsForestOf [Node 1 [], Node 1 []] ('vertex' 1) == False--- isDfsForestOf [Node 1 []] ('edge' 1 1) == True--- isDfsForestOf [Node 1 []] ('edge' 1 2) == False--- isDfsForestOf [Node 1 [], Node 2 []] ('edge' 1 2) == False--- isDfsForestOf [Node 2 [], Node 1 []] ('edge' 1 2) == True--- isDfsForestOf [Node 1 [Node 2 []]] ('edge' 1 2) == True--- isDfsForestOf [Node 1 [], Node 2 []] ('vertices' [1,2]) == True--- isDfsForestOf [Node 2 [], Node 1 []] ('vertices' [1,2]) == True--- isDfsForestOf [Node 1 [Node 2 []]] ('vertices' [1,2]) == False--- isDfsForestOf [Node 1 [Node 2 [Node 3 []]]] ('path' [1,2,3]) == True--- isDfsForestOf [Node 1 [Node 3 [Node 2 []]]] ('path' [1,2,3]) == False--- isDfsForestOf [Node 3 [], Node 1 [Node 2 []]] ('path' [1,2,3]) == True--- isDfsForestOf [Node 2 [Node 3 []], Node 1 []] ('path' [1,2,3]) == True--- isDfsForestOf [Node 1 [], Node 2 [Node 3 []]] ('path' [1,2,3]) == False+-- symmetricClosure 'empty' == 'empty'+-- symmetricClosure ('vertex' x) == 'vertex' x+-- symmetricClosure ('edge' x y) == 'edges' [(x,y), (y,x)]+-- symmetricClosure x == 'overlay' x ('transpose' x)+-- symmetricClosure . symmetricClosure == symmetricClosure -- @-isDfsForestOf :: Ord a => Forest a -> AdjacencyMap a -> Bool-isDfsForestOf f am = case go Set.empty f of- Just seen -> seen == vertexSet am- Nothing -> False- where- go seen [] = Just seen- go seen (t:ts) = do- let root = rootLabel t- guard $ root `Set.notMember` seen- guard $ and [ hasEdge root (rootLabel subTree) am | subTree <- subForest t ]- newSeen <- go (Set.insert root seen) (subForest t)- guard $ postSet root am `Set.isSubsetOf` newSeen- go newSeen ts+symmetricClosure :: Ord a => AdjacencyMap a -> AdjacencyMap a+symmetricClosure m = overlay m (transpose m) --- | Check if a given list of vertices is a correct /topological sort/ of a graph.+-- | Compute the /transitive closure/ of a graph.+-- Complexity: /O(n * m * log(n)^2)/ time. -- -- @--- isTopSortOf [3,1,2] (1 * 2 + 3 * 1) == True--- isTopSortOf [1,2,3] (1 * 2 + 3 * 1) == False--- isTopSortOf [] (1 * 2 + 3 * 1) == False--- isTopSortOf [] 'empty' == True--- isTopSortOf [x] ('vertex' x) == True--- isTopSortOf [x] ('edge' x x) == False+-- transitiveClosure 'empty' == 'empty'+-- transitiveClosure ('vertex' x) == 'vertex' x+-- transitiveClosure ('edge' x y) == 'edge' x y+-- transitiveClosure ('path' $ 'Data.List.nub' xs) == 'clique' ('Data.List.nub' xs)+-- transitiveClosure . transitiveClosure == transitiveClosure -- @-isTopSortOf :: Ord a => [a] -> AdjacencyMap a -> Bool-isTopSortOf xs m = go Set.empty xs+transitiveClosure :: Ord a => AdjacencyMap a -> AdjacencyMap a+transitiveClosure old+ | old == new = old+ | otherwise = transitiveClosure new where- go seen [] = seen == Map.keysSet (adjacencyMap m)- go seen (v:vs) = postSet v m `Set.intersection` newSeen == Set.empty- && go newSeen vs- where- newSeen = Set.insert v seen+ new = overlay old (old `compose` old)
+ src/Algebra/Graph/AdjacencyMap/Algorithm.hs view
@@ -0,0 +1,235 @@+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.AdjacencyMap.Algorithm+-- Copyright : (c) Andrey Mokhov 2016-2018+-- License : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability : unstable+--+-- __Alga__ is a library for algebraic construction and manipulation of graphs+-- in Haskell. See <https://github.com/snowleopard/alga-paper this paper> for the+-- motivation behind the library, the underlying theory, and implementation details.+--+-- This module provides basic graph algorithms, such as /depth-first search/,+-- implemented for the "Algebra.Graph.AdjacencyMap" data type.+-----------------------------------------------------------------------------+module Algebra.Graph.AdjacencyMap.Algorithm (+ -- * Algorithms+ dfsForest, dfsForestFrom, dfs, reachable, topSort, isAcyclic, scc,++ -- * Correctness properties+ isDfsForestOf, isTopSortOf+ ) where++import Control.Monad+import Data.Foldable (toList)+import Data.Maybe+import Data.Tree++import Algebra.Graph.AdjacencyMap++import qualified Algebra.Graph.AdjacencyMap.Internal as AM+import qualified Algebra.Graph.NonEmpty.AdjacencyMap as NonEmpty+import qualified Data.Graph as KL+import qualified Data.Graph.Typed as Typed+import qualified Data.Map.Strict as Map+import qualified Data.Set as Set++-- | Compute the /depth-first search/ forest of a graph that corresponds to+-- searching from each of the graph vertices in the 'Ord' @a@ order.+--+-- @+-- dfsForest 'empty' == []+-- 'forest' (dfsForest $ 'edge' 1 1) == 'vertex' 1+-- 'forest' (dfsForest $ 'edge' 1 2) == 'edge' 1 2+-- 'forest' (dfsForest $ 'edge' 2 1) == 'vertices' [1,2]+-- 'isSubgraphOf' ('forest' $ dfsForest x) x == True+-- 'isDfsForestOf' (dfsForest x) x == True+-- dfsForest . 'forest' . dfsForest == dfsForest+-- dfsForest ('vertices' vs) == 'map' (\\v -> Node v []) ('Data.List.nub' $ 'Data.List.sort' vs)+-- 'dfsForestFrom' ('vertexList' x) x == dfsForest x+-- dfsForest $ 3 * (1 + 4) * (1 + 5) == [ Node { rootLabel = 1+-- , subForest = [ Node { rootLabel = 5+-- , subForest = [] }]}+-- , Node { rootLabel = 3+-- , subForest = [ Node { rootLabel = 4+-- , subForest = [] }]}]+-- @+dfsForest :: Ord a => AdjacencyMap a -> Forest a+dfsForest g = dfsForestFrom (vertexList g) g++-- | Compute the /depth-first search/ forest of a graph, searching from each of+-- the given vertices in order. Note that the resulting forest does not+-- necessarily span the whole graph, as some vertices may be unreachable.+--+-- @+-- dfsForestFrom vs 'empty' == []+-- 'forest' (dfsForestFrom [1] $ 'edge' 1 1) == 'vertex' 1+-- 'forest' (dfsForestFrom [1] $ 'edge' 1 2) == 'edge' 1 2+-- 'forest' (dfsForestFrom [2] $ 'edge' 1 2) == 'vertex' 2+-- 'forest' (dfsForestFrom [3] $ 'edge' 1 2) == 'empty'+-- 'forest' (dfsForestFrom [2,1] $ 'edge' 1 2) == 'vertices' [1,2]+-- 'isSubgraphOf' ('forest' $ dfsForestFrom vs x) x == True+-- 'isDfsForestOf' (dfsForestFrom ('vertexList' x) x) x == True+-- dfsForestFrom ('vertexList' x) x == 'dfsForest' x+-- dfsForestFrom vs ('vertices' vs) == 'map' (\\v -> Node v []) ('Data.List.nub' vs)+-- dfsForestFrom [] x == []+-- dfsForestFrom [1,4] $ 3 * (1 + 4) * (1 + 5) == [ Node { rootLabel = 1+-- , subForest = [ Node { rootLabel = 5+-- , subForest = [] }+-- , Node { rootLabel = 4+-- , subForest = [] }]+-- @+dfsForestFrom :: Ord a => [a] -> AdjacencyMap a -> Forest a+dfsForestFrom vs = Typed.dfsForestFrom vs . Typed.fromAdjacencyMap++-- | Compute the list of vertices visited by the /depth-first search/ in a+-- graph, when searching from each of the given vertices in order.+--+-- @+-- dfs vs $ 'empty' == []+-- dfs [1] $ 'edge' 1 1 == [1]+-- dfs [1] $ 'edge' 1 2 == [1,2]+-- dfs [2] $ 'edge' 1 2 == [2]+-- dfs [3] $ 'edge' 1 2 == []+-- dfs [1,2] $ 'edge' 1 2 == [1,2]+-- dfs [2,1] $ 'edge' 1 2 == [2,1]+-- dfs [] $ x == []+-- dfs [1,4] $ 3 * (1 + 4) * (1 + 5) == [1,5,4]+-- 'isSubgraphOf' ('vertices' $ dfs vs x) x == True+-- @+dfs :: Ord a => [a] -> AdjacencyMap a -> [a]+dfs vs = concatMap flatten . dfsForestFrom vs++-- | Compute the list of vertices that are /reachable/ from a given source+-- vertex in a graph. The vertices in the resulting list appear in the+-- /depth-first order/.+--+-- @+-- reachable x $ 'empty' == []+-- reachable 1 $ 'vertex' 1 == [1]+-- reachable 1 $ 'vertex' 2 == []+-- reachable 1 $ 'edge' 1 1 == [1]+-- reachable 1 $ 'edge' 1 2 == [1,2]+-- reachable 4 $ 'path' [1..8] == [4..8]+-- reachable 4 $ 'circuit' [1..8] == [4..8] ++ [1..3]+-- reachable 8 $ 'clique' [8,7..1] == [8] ++ [1..7]+-- 'isSubgraphOf' ('vertices' $ reachable x y) y == True+-- @+reachable :: Ord a => a -> AdjacencyMap a -> [a]+reachable x = dfs [x]++-- | Compute the /topological sort/ of a graph or return @Nothing@ if the graph+-- is cyclic.+--+-- @+-- topSort (1 * 2 + 3 * 1) == Just [3,1,2]+-- topSort (1 * 2 + 2 * 1) == Nothing+-- fmap ('flip' 'isTopSortOf' x) (topSort x) /= Just False+-- 'isJust' . topSort == 'isAcyclic'+-- @+topSort :: Ord a => AdjacencyMap a -> Maybe [a]+topSort m = if isTopSortOf result m then Just result else Nothing+ where+ result = Typed.topSort (Typed.fromAdjacencyMap m)++-- | Check if a given graph is /acyclic/.+--+-- @+-- isAcyclic (1 * 2 + 3 * 1) == True+-- isAcyclic (1 * 2 + 2 * 1) == False+-- isAcyclic . 'circuit' == 'null'+-- isAcyclic == 'isJust' . 'topSort'+-- @+isAcyclic :: Ord a => AdjacencyMap a -> Bool+isAcyclic = isJust . topSort++-- TODO: Benchmark and optimise.+-- | Compute the /condensation/ of a graph, where each vertex corresponds to a+-- /strongly-connected component/ of the original graph. Note that component+-- graphs are non-empty, and are therefore of type+-- "Algebra.Graph.NonEmpty.AdjacencyMap".+--+-- @+-- scc 'empty' == 'empty'+-- scc ('vertex' x) == 'vertex' (NonEmpty.'NonEmpty.vertex' x)+-- scc ('edge' 1 1) == 'vertex' (NonEmpty.'NonEmpty.edge' 1 1)+-- scc ('edge' 1 2) == 'edge' (NonEmpty.'NonEmpty.vertex' 1) (NonEmpty.'NonEmpty.vertex' 2)+-- scc ('circuit' (1:xs)) == 'vertex' (NonEmpty.'NonEmpty.circuit1' (1 'Data.List.NonEmpty.:|' xs))+-- scc (3 * 1 * 4 * 1 * 5) == 'edges' [ (NonEmpty.'NonEmpty.vertex' 3 , NonEmpty.'NonEmpty.vertex' 5 )+-- , (NonEmpty.'NonEmpty.vertex' 3 , NonEmpty.'NonEmpty.clique1' [1,4,1])+-- , (NonEmpty.'NonEmpty.clique1' [1,4,1], NonEmpty.'NonEmpty.vertex' 5 ) ]+-- 'isAcyclic' . scc == 'const' True+-- 'isAcyclic' x == (scc x == 'gmap' NonEmpty.'NonEmpty.vertex' x)+-- @+scc :: Ord a => AdjacencyMap a -> AdjacencyMap (NonEmpty.AdjacencyMap a)+scc m = gmap (component Map.!) $ removeSelfLoops $ gmap (leader Map.!) m+ where+ Typed.GraphKL g decode _ = Typed.fromAdjacencyMap m+ sccs = map toList (KL.scc g)+ leader = Map.fromList [ (decode y, x) | x:xs <- sccs, y <- x:xs ]+ component = Map.fromList [ (x, expand (x:xs)) | x:xs <- sccs ]+ expand xs = fromJust $ NonEmpty.toNonEmpty $ induce (`Set.member` s) m+ where+ s = Set.fromList (map decode xs)++-- Remove all self loops from a graph.+removeSelfLoops :: Ord a => AdjacencyMap a -> AdjacencyMap a+removeSelfLoops (AM.AM m) = AM.AM (Map.mapWithKey Set.delete m)++-- | Check if a given forest is a correct /depth-first search/ forest of a graph.+-- The implementation is based on the paper "Depth-First Search and Strong+-- Connectivity in Coq" by François Pottier.+--+-- @+-- isDfsForestOf [] 'empty' == True+-- isDfsForestOf [] ('vertex' 1) == False+-- isDfsForestOf [Node 1 []] ('vertex' 1) == True+-- isDfsForestOf [Node 1 []] ('vertex' 2) == False+-- isDfsForestOf [Node 1 [], Node 1 []] ('vertex' 1) == False+-- isDfsForestOf [Node 1 []] ('edge' 1 1) == True+-- isDfsForestOf [Node 1 []] ('edge' 1 2) == False+-- isDfsForestOf [Node 1 [], Node 2 []] ('edge' 1 2) == False+-- isDfsForestOf [Node 2 [], Node 1 []] ('edge' 1 2) == True+-- isDfsForestOf [Node 1 [Node 2 []]] ('edge' 1 2) == True+-- isDfsForestOf [Node 1 [], Node 2 []] ('vertices' [1,2]) == True+-- isDfsForestOf [Node 2 [], Node 1 []] ('vertices' [1,2]) == True+-- isDfsForestOf [Node 1 [Node 2 []]] ('vertices' [1,2]) == False+-- isDfsForestOf [Node 1 [Node 2 [Node 3 []]]] ('path' [1,2,3]) == True+-- isDfsForestOf [Node 1 [Node 3 [Node 2 []]]] ('path' [1,2,3]) == False+-- isDfsForestOf [Node 3 [], Node 1 [Node 2 []]] ('path' [1,2,3]) == True+-- isDfsForestOf [Node 2 [Node 3 []], Node 1 []] ('path' [1,2,3]) == True+-- isDfsForestOf [Node 1 [], Node 2 [Node 3 []]] ('path' [1,2,3]) == False+-- @+isDfsForestOf :: Ord a => Forest a -> AdjacencyMap a -> Bool+isDfsForestOf f am = case go Set.empty f of+ Just seen -> seen == vertexSet am+ Nothing -> False+ where+ go seen [] = Just seen+ go seen (t:ts) = do+ let root = rootLabel t+ guard $ root `Set.notMember` seen+ guard $ and [ hasEdge root (rootLabel subTree) am | subTree <- subForest t ]+ newSeen <- go (Set.insert root seen) (subForest t)+ guard $ postSet root am `Set.isSubsetOf` newSeen+ go newSeen ts++-- | Check if a given list of vertices is a correct /topological sort/ of a graph.+--+-- @+-- isTopSortOf [3,1,2] (1 * 2 + 3 * 1) == True+-- isTopSortOf [1,2,3] (1 * 2 + 3 * 1) == False+-- isTopSortOf [] (1 * 2 + 3 * 1) == False+-- isTopSortOf [] 'empty' == True+-- isTopSortOf [x] ('vertex' x) == True+-- isTopSortOf [x] ('edge' x x) == False+-- @+isTopSortOf :: Ord a => [a] -> AdjacencyMap a -> Bool+isTopSortOf xs m = go Set.empty xs+ where+ go seen [] = seen == Map.keysSet (adjacencyMap m)+ go seen (v:vs) = postSet v m `Set.intersection` newSeen == Set.empty+ && go newSeen vs+ where+ newSeen = Set.insert v seen
src/Algebra/Graph/AdjacencyMap/Internal.hs view
@@ -12,16 +12,19 @@ ----------------------------------------------------------------------------- module Algebra.Graph.AdjacencyMap.Internal ( -- * Adjacency map implementation- AdjacencyMap (..), empty, vertex, overlay, connect, fromAdjacencySets,- consistent+ AdjacencyMap (..), consistent, internalEdgeList, referredToVertexSet ) where +import Prelude ()+import Prelude.Compat hiding (null)++import Control.DeepSeq+import Data.Foldable (foldMap) import Data.List import Data.Map.Strict (Map, keysSet, fromSet)+import Data.Monoid import Data.Set (Set) -import Control.DeepSeq (NFData (..))- import qualified Data.Map.Strict as Map import qualified Data.Set as Set @@ -35,6 +38,14 @@ > 1 + 2 * 3 == overlay (vertex 1) (connect (vertex 2) (vertex 3)) > 1 * (2 + 3) == connect (vertex 1) (overlay (vertex 2) (vertex 3)) +__Note:__ the 'Num' instance does not satisfy several "customary laws" of 'Num',+which dictate that 'fromInteger' @0@ and 'fromInteger' @1@ should act as+additive and multiplicative identities, and 'negate' as additive inverse.+Nevertheless, overloading 'fromInteger', '+' and '*' is very convenient when+working with algebraic graphs; we hope that in future Haskell's Prelude will+provide a more fine-grained class hierarchy for algebraic structures, which we+would be able to utilise without violating any laws.+ The 'Show' instance is defined using basic graph construction primitives: @show (empty :: AdjacencyMap Int) == "empty"@@ -84,128 +95,88 @@ When specifying the time and memory complexity of graph algorithms, /n/ and /m/ will denote the number of vertices and edges in the graph, respectively.++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:++@'Algebra.Graph.AdjacencyMap.vertex' 1 < 'Algebra.Graph.AdjacencyMap.vertex' 2+'Algebra.Graph.AdjacencyMap.vertex' 3 < 'Algebra.Graph.AdjacencyMap.edge' 1 2+'Algebra.Graph.AdjacencyMap.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@++Note that the resulting order refines the 'Algebra.Graph.AdjacencyMap.isSubgraphOf'+relation and is compatible with 'Algebra.Graph.AdjacencyMap.overlay' and+'Algebra.Graph.AdjacencyMap.connect' operations:++@'Algebra.Graph.AdjacencyMap.isSubgraphOf' x y ==> x <= y@++@'Algebra.Graph.AdjacencyMap.empty' <= x+x <= x + y+x + y <= x * y@ -} newtype AdjacencyMap a = AM {- -- | The /adjacency map/ of the graph: each vertex is associated with a set- -- of its direct successors. Complexity: /O(1)/ time and memory.+ -- | The /adjacency map/ of a graph: each vertex is associated with a set of+ -- its direct successors. Complexity: /O(1)/ time and memory. -- -- @- -- adjacencyMap 'empty' == Map.'Map.empty'- -- adjacencyMap ('vertex' x) == Map.'Map.singleton' x Set.'Set.empty'+ -- adjacencyMap 'Algebra.Graph.AdjacencyMap.empty' == Map.'Map.empty'+ -- adjacencyMap ('Algebra.Graph.AdjacencyMap.vertex' x) == Map.'Map.singleton' x Set.'Set.empty' -- 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 +instance Ord a => Ord (AdjacencyMap a) where+ compare (AM x) (AM y) = mconcat+ [ compare (vNum x) (vNum y)+ , compare (vSet x) (vSet y)+ , compare (eNum x) (eNum y)+ , compare x y ]+ where+ vNum = Map.size+ vSet = Map.keysSet+ eNum = getSum . foldMap (Sum . Set.size)+ instance (Ord a, Show a) => Show (AdjacencyMap a) where- show (AM m)- | null vs = "empty"- | null es = vshow vs- | vs == used = eshow es- | otherwise = "overlay (" ++ vshow (vs \\ used) ++ ") (" ++ eshow es ++ ")"+ showsPrec p (AM m)+ | null vs = showString "empty"+ | null es = showParen (p > 10) $ vshow vs+ | vs == used = showParen (p > 10) $ eshow es+ | otherwise = showParen (p > 10) $+ showString "overlay (" . vshow (vs \\ used) .+ showString ") (" . eshow es . showString ")" where vs = Set.toAscList (keysSet m) es = internalEdgeList m- vshow [x] = "vertex " ++ show x- vshow xs = "vertices " ++ show xs- eshow [(x, y)] = "edge " ++ show x ++ " " ++ show y- eshow xs = "edges " ++ show xs+ vshow [x] = showString "vertex " . showsPrec 11 x+ vshow xs = showString "vertices " . showsPrec 11 xs+ eshow [(x, y)] = showString "edge " . showsPrec 11 x .+ showString " " . showsPrec 11 y+ eshow xs = showString "edges " . showsPrec 11 xs used = Set.toAscList (referredToVertexSet m) --- | Construct the /empty graph/.--- Complexity: /O(1)/ time and memory.------ @--- 'Algebra.Graph.AdjacencyMap.isEmpty' empty == True--- 'Algebra.Graph.AdjacencyMap.hasVertex' x empty == False--- 'Algebra.Graph.AdjacencyMap.vertexCount' empty == 0--- 'Algebra.Graph.AdjacencyMap.edgeCount' empty == 0--- @-empty :: AdjacencyMap a-empty = AM Map.empty-{-# NOINLINE [1] empty #-}---- | Construct the graph comprising /a single isolated vertex/.--- Complexity: /O(1)/ time and memory.------ @--- 'Algebra.Graph.AdjacencyMap.isEmpty' (vertex x) == False--- 'Algebra.Graph.AdjacencyMap.hasVertex' x (vertex x) == True--- 'Algebra.Graph.AdjacencyMap.vertexCount' (vertex x) == 1--- 'Algebra.Graph.AdjacencyMap.edgeCount' (vertex x) == 0--- @-vertex :: a -> AdjacencyMap a-vertex x = AM $ Map.singleton x Set.empty-{-# NOINLINE [1] vertex #-}---- | /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.AdjacencyMap.isEmpty' (overlay x y) == 'Algebra.Graph.AdjacencyMap.isEmpty' x && 'Algebra.Graph.AdjacencyMap.isEmpty' y--- 'Algebra.Graph.AdjacencyMap.hasVertex' z (overlay x y) == 'Algebra.Graph.AdjacencyMap.hasVertex' z x || 'Algebra.Graph.AdjacencyMap.hasVertex' z y--- 'Algebra.Graph.AdjacencyMap.vertexCount' (overlay x y) >= 'Algebra.Graph.AdjacencyMap.vertexCount' x--- 'Algebra.Graph.AdjacencyMap.vertexCount' (overlay x y) <= 'Algebra.Graph.AdjacencyMap.vertexCount' x + 'Algebra.Graph.AdjacencyMap.vertexCount' y--- 'Algebra.Graph.AdjacencyMap.edgeCount' (overlay x y) >= 'Algebra.Graph.AdjacencyMap.edgeCount' x--- 'Algebra.Graph.AdjacencyMap.edgeCount' (overlay x y) <= 'Algebra.Graph.AdjacencyMap.edgeCount' x + 'Algebra.Graph.AdjacencyMap.edgeCount' y--- 'Algebra.Graph.AdjacencyMap.vertexCount' (overlay 1 2) == 2--- 'Algebra.Graph.AdjacencyMap.edgeCount' (overlay 1 2) == 0--- @-overlay :: Ord a => AdjacencyMap a -> AdjacencyMap a -> AdjacencyMap a-overlay x y = AM $ Map.unionWith Set.union (adjacencyMap x) (adjacencyMap y)-{-# NOINLINE [1] overlay #-}---- | /Connect/ two graphs. This is an 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)/.------ @--- 'isEmpty' (connect x y) == 'isEmpty' x && 'Algebra.Graph.AdjacencyMap.isEmpty' y--- 'hasVertex' z (connect x y) == 'hasVertex' z x || 'Algebra.Graph.AdjacencyMap.hasVertex' z y--- 'vertexCount' (connect x y) >= 'vertexCount' x--- 'vertexCount' (connect x y) <= 'vertexCount' x + 'Algebra.Graph.AdjacencyMap.vertexCount' y--- 'edgeCount' (connect x y) >= 'edgeCount' x--- 'edgeCount' (connect x y) >= 'edgeCount' y--- 'edgeCount' (connect x y) >= 'vertexCount' x * 'Algebra.Graph.AdjacencyMap.vertexCount' y--- 'edgeCount' (connect x y) <= 'vertexCount' x * 'Algebra.Graph.AdjacencyMap.vertexCount' y + 'Algebra.Graph.AdjacencyMap.edgeCount' x + 'Algebra.Graph.AdjacencyMap.edgeCount' y--- 'vertexCount' (connect 1 2) == 2--- 'edgeCount' (connect 1 2) == 1--- @-connect :: Ord a => AdjacencyMap a -> AdjacencyMap a -> AdjacencyMap a-connect x y = AM $ Map.unionsWith Set.union [ adjacencyMap x, adjacencyMap y,- fromSet (const . keysSet $ adjacencyMap y) (keysSet $ adjacencyMap x) ]-{-# NOINLINE [1] connect #-}-+-- | __Note:__ this does not satisfy the usual ring laws; see 'AdjacencyMap'+-- for more details. instance (Ord a, Num a) => Num (AdjacencyMap a) where- fromInteger = vertex . fromInteger- (+) = overlay- (*) = connect- signum = const empty- abs = id- negate = id+ fromInteger x = AM $ Map.singleton (fromInteger x) Set.empty+ x + y = AM $ Map.unionWith Set.union (adjacencyMap x) (adjacencyMap y)+ x * y = AM $ Map.unionsWith Set.union [ adjacencyMap x, adjacencyMap y,+ fromSet (const . keysSet $ adjacencyMap y) (keysSet $ adjacencyMap x) ]+ signum = const (AM Map.empty)+ abs = id+ negate = id instance NFData a => NFData (AdjacencyMap a) where rnf (AM a) = rnf a --- | Construct a graph from a list of adjacency sets.--- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.------ @--- fromAdjacencySets [] == 'Algebra.Graph.AdjacencyMap.empty'--- fromAdjacencySets [(x, Set.'Set.empty')] == 'Algebra.Graph.AdjacencyMap.vertex' x--- fromAdjacencySets [(x, Set.'Set.singleton' y)] == 'Algebra.Graph.AdjacencyMap.edge' x y--- fromAdjacencySets . map (fmap Set.'Set.fromList') . 'Algebra.Graph.AdjacencyMap.adjacencyList' == id--- 'Algebra.Graph.AdjacencyMap.overlay' (fromAdjacencySets xs) (fromAdjacencySets ys) == fromAdjacencySets (xs ++ ys)--- @-fromAdjacencySets :: Ord a => [(a, Set a)] -> AdjacencyMap a-fromAdjacencySets ss = AM $ Map.unionWith Set.union vs es- where- vs = Map.fromSet (const Set.empty) . Set.unions $ map snd ss- es = Map.fromListWith Set.union ss- -- | Check if the internal graph representation is consistent, i.e. that all -- edges refer to existing vertices. It should be impossible to create an -- inconsistent adjacency map, and we use this function in testing.@@ -223,10 +194,12 @@ consistent :: Ord a => AdjacencyMap a -> Bool consistent (AM m) = referredToVertexSet m `Set.isSubsetOf` keysSet m --- The set of vertices that are referred to by the edges-referredToVertexSet :: Ord a => Map a (Set a) -> Set a-referredToVertexSet = Set.fromList . uncurry (++) . unzip . internalEdgeList---- The list of edges in adjacency map+-- | The list of edges of an adjacency map.+-- /Note: this function is for internal use only/. internalEdgeList :: Map a (Set a) -> [(a, a)] internalEdgeList m = [ (x, y) | (x, ys) <- Map.toAscList m, y <- Set.toAscList ys ]++-- | The set of vertices that are referred to by the edges of an adjacency map.+-- /Note: this function is for internal use only/.+referredToVertexSet :: Ord a => Map a (Set a) -> Set a+referredToVertexSet = Set.fromList . uncurry (++) . unzip . internalEdgeList
src/Algebra/Graph/Class.hs view
@@ -44,20 +44,24 @@ isSubgraphOf, -- * Standard families of graphs- path, circuit, clique, biclique, star, starTranspose, tree, forest- ) where+ path, circuit, clique, biclique, star, tree, forest+ ) where import Prelude () import Prelude.Compat import Data.Tree -import qualified Algebra.Graph as G-import qualified Algebra.Graph.AdjacencyMap as AM-import qualified Algebra.Graph.Fold as F-import qualified Algebra.Graph.AdjacencyIntMap as AIM-import qualified Algebra.Graph.Relation as R+import Algebra.Graph.Label (Dioid, one) +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+import qualified Algebra.Graph.Fold as F+import qualified Algebra.Graph.AdjacencyIntMap as AIM+import qualified Algebra.Graph.Relation as R+ {-| The core type class for constructing algebraic graphs, characterised by the following minimal set of axioms. In equations we use @+@ and @*@ as convenient@@ -145,6 +149,20 @@ overlay = AIM.overlay connect = AIM.connect +instance Dioid e => Graph (LG.Graph e a) where+ type Vertex (LG.Graph e a) = a+ empty = LG.empty+ vertex = LG.vertex+ overlay = LG.overlay+ connect = LG.connect one++instance (Dioid e, Eq e, Ord a) => Graph (LAM.AdjacencyMap e a) where+ type Vertex (LAM.AdjacencyMap e a) = a+ empty = LAM.empty+ vertex = LAM.vertex+ overlay = LAM.overlay+ connect = LAM.connect one+ instance Ord a => Graph (R.Relation a) where type Vertex (R.Relation a) = a empty = R.empty@@ -405,21 +423,6 @@ star x [] = vertex x star x ys = connect (vertex x) (vertices ys) --- | The /star transpose/ formed by a list of leaves connected to a centre vertex.--- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the--- given list.------ @--- starTranspose x [] == 'vertex' x--- starTranspose x [y] == 'edge' y x--- starTranspose x [y,z] == 'edges' [(y,x), (z,x)]--- starTranspose x ys == 'connect' ('vertices' ys) ('vertex' x)--- starTranspose x ys == transpose ('star' x ys)--- @-starTranspose :: Graph g => Vertex g -> [Vertex g] -> g-starTranspose x [] = vertex x-starTranspose x ys = connect (vertices ys) (vertex x)- -- | The /tree graph/ constructed from a given 'Tree' data structure. -- Complexity: /O(T)/ time, memory and size, where /T/ is the size of the -- given tree (i.e. the number of vertices in the tree).@@ -443,7 +446,7 @@ -- 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 == 'overlays' . 'map' 'tree' -- @ forest :: Graph g => Forest (Vertex g) -> g forest = overlays . map tree
src/Algebra/Graph/Export.hs view
@@ -16,7 +16,7 @@ ----------------------------------------------------------------------------- module Algebra.Graph.Export ( -- * Constructing and exporting documents- Doc, literal, render,+ Doc, isEmpty, literal, render, -- * Common combinators for text documents (<+>), brackets, doubleQuotes, indent, unlines,@@ -39,26 +39,56 @@ -- | An abstract document data type with /O(1)/ time concatenation (the current -- implementation uses difference lists). Here @s@ is the type of abstract -- symbols or strings (text or binary). 'Doc' @s@ is a 'Monoid', therefore--- 'mempty' corresponds to the empty document and two documents can be+-- 'mempty' corresponds to the /empty document/ and two documents can be -- concatenated with 'mappend' (or operator 'Data.Monoid.<>'). Documents -- comprising a single symbol or string can be constructed using the function--- 'literal'. Alternatively, you can construct documents as string literals, e.g.--- simply as @"alga"@, by using the @OverloadedStrings@ GHC extension. To extract--- the document contents use the function 'render'. See some examples below.+-- 'literal'. Alternatively, you can construct documents as string literals,+-- e.g. simply as @"alga"@, by using the @OverloadedStrings@ GHC extension. To+-- extract the document contents use the function 'render'.+--+-- Note that the document comprising a single empty string is considered to be+-- different from the empty document. This design choice is motivated by the+-- desire to support string types @s@ that have no 'Eq' instance, such as+-- "Data.ByteString.Builder", for which there is no way to check whether a+-- string is empty or not. As a consequence, the 'Eq' and 'Ord' instances are+-- defined as follows:+--+-- @+-- 'mempty' /= 'literal' ""+-- 'mempty' < 'literal' ""+-- @ newtype Doc s = Doc (List s) deriving (Monoid, Semigroup) instance (Monoid s, Show s) => Show (Doc s) where show = show . render instance (Monoid s, Eq s) => Eq (Doc s) where- x == y = render x == render y+ x == y | isEmpty x = isEmpty y+ | isEmpty y = False+ | otherwise = render x == render y +-- | The empty document is smallest. instance (Monoid s, Ord s) => Ord (Doc s) where- compare x y = compare (render x) (render y)+ compare x y | isEmpty x = if isEmpty y then EQ else LT+ | isEmpty y = GT+ | otherwise = compare (render x) (render y) instance IsString s => IsString (Doc s) where fromString = literal . fromString +-- | Check if a document is empty. The result is the same as when comparing the+-- given document to 'mempty', but this function does not require the 'Eq' @s@+-- constraint. Note that the document comprising a single empty string is+-- considered to be different from the empty document.+--+-- @+-- isEmpty 'mempty' == True+-- isEmpty ('literal' \"\") == False+-- isEmpty x == (x == 'mempty')+-- @+isEmpty :: Doc s -> Bool+isEmpty (Doc xs) = null xs+ -- | Construct a document comprising a single symbol or string. If @s@ is an -- instance of class 'IsString', then documents of type 'Doc' @s@ can be -- constructed directly from string literals (see the second example below).@@ -66,9 +96,7 @@ -- @ -- literal "Hello, " 'Data.Monoid.<>' literal "World!" == literal "Hello, World!" -- literal "I am just a string literal" == "I am just a string literal"--- literal 'mempty' == 'mempty' -- 'render' . literal == 'id'--- literal . 'render' == 'id' -- @ literal :: s -> Doc s literal = Doc . pure@@ -80,7 +108,6 @@ -- render ('literal' "al" 'Data.Monoid.<>' 'literal' "ga") == "alga" -- render 'mempty' == 'mempty' -- render . 'literal' == 'id'--- 'literal' . render == 'id' -- @ render :: Monoid s => Doc s -> s render (Doc x) = fold x@@ -94,10 +121,10 @@ -- x \<+\> (y \<+\> z) == (x \<+\> y) \<+\> z -- "name" \<+\> "surname" == "name surname" -- @-(<+>) :: (Eq s, IsString s, Monoid s) => Doc s -> Doc s -> Doc s-x <+> y | x == mempty = y- | y == mempty = x- | otherwise = x <> " " <> y+(<+>) :: IsString s => Doc s -> Doc s -> Doc s+x <+> y | isEmpty x = y+ | isEmpty y = x+ | otherwise = x <> " " <> y infixl 7 <+>
src/Algebra/Graph/Export/Dot.hs view
@@ -43,8 +43,8 @@ data Style a s = Style { graphName :: s -- ^ Name of the graph.- , preamble :: s- -- ^ Preamble is added at the beginning of the DOT file body.+ , preamble :: [s]+ -- ^ Preamble (a list of lines) is added at the beginning of the DOT file body. , graphAttributes :: [Attribute s] -- ^ Graph style, e.g. @["bgcolor" := "azure"]@. , defaultVertexAttributes :: [Attribute s]@@ -62,7 +62,7 @@ -- | Default style for exporting graphs. All style settings are empty except for -- 'vertexName', which is provided as the only argument. defaultStyle :: Monoid s => (a -> s) -> Style a s-defaultStyle v = Style mempty mempty [] [] [] v (\_ -> []) (\_ _ -> [])+defaultStyle v = Style mempty [] [] [] [] v (\_ -> []) (\_ _ -> []) -- | Default style for exporting graphs whose vertices are 'Show'-able. All -- style settings are empty except for 'vertexName', which is computed from@@ -82,7 +82,7 @@ -- style :: 'Style' Int String -- style = 'Style' -- { 'graphName' = \"Example\"--- , 'preamble' = " // This is an example\\n"+-- , 'preamble' = [" // This is an example", ""] -- , 'graphAttributes' = ["label" := \"Example\", "labelloc" := "top"] -- , 'defaultVertexAttributes' = ["shape" := "circle"] -- , 'defaultEdgeAttributes' = 'mempty'@@ -109,14 +109,14 @@ -- "v4" -> "v5" -- } -- @-export :: (IsString s, Monoid s, Eq s, Ord a, ToGraph g, ToVertex g ~ a) => Style a s -> g -> s+export :: (IsString s, Monoid s, Ord a, ToGraph g, ToVertex g ~ a) => Style a s -> g -> s export Style {..} g = render $ header <> body <> "}\n" where header = "digraph" <+> literal graphName <> "\n{\n"- <> if preamble == mempty then mempty else literal preamble <> "\n"- with x as = if null as then mempty else line (x <+> attributes as)+ with x as = if null as then mempty else line (x <+> attributes as) line s = indent 2 s <> "\n"- body = ("graph" `with` graphAttributes)+ body = unlines (map literal preamble)+ <> ("graph" `with` graphAttributes) <> ("node" `with` defaultVertexAttributes) <> ("edge" `with` defaultEdgeAttributes) <> E.export vDoc eDoc g@@ -150,7 +150,7 @@ -- "c" -> "a" -- } -- @-exportAsIs :: (IsString s, Monoid s, Ord s, ToGraph g, ToVertex g ~ s) => g -> s+exportAsIs :: (IsString s, Monoid s, Ord (ToVertex g), ToGraph g, ToVertex g ~ s) => g -> s exportAsIs = export (defaultStyle id) -- | Export a graph using the 'defaultStyleViaShow'.@@ -170,5 +170,5 @@ -- "2" -> "4" -- } -- @-exportViaShow :: (IsString s, Monoid s, Eq s, ToGraph g, Ord (ToVertex g), Show (ToVertex g)) => g -> s+exportViaShow :: (IsString s, Monoid s, Ord (ToVertex g), Show (ToVertex g), ToGraph g) => g -> s exportViaShow = export defaultStyleViaShow
src/Algebra/Graph/Fold.hs view
@@ -33,19 +33,19 @@ -- * Graph properties isEmpty, size, hasVertex, hasEdge, vertexCount, edgeCount, vertexList,- edgeList, vertexSet, vertexIntSet, edgeSet, adjacencyList,+ edgeList, vertexSet, edgeSet, adjacencyList, -- * Standard families of graphs path, circuit, clique, biclique, star, stars, -- * Graph transformation removeVertex, removeEdge, transpose, induce, simplify,- ) where+ ) where import Prelude () import Prelude.Compat -import Control.Applicative (Alternative, liftA2)+import Control.Applicative (Alternative) import Control.Monad.Compat (MonadPlus (..), ap) import Data.Function @@ -57,7 +57,6 @@ import qualified Algebra.Graph.AdjacencyMap as AM import qualified Algebra.Graph.ToGraph as T import qualified Control.Applicative as Ap-import qualified Data.IntSet as IntSet import qualified Data.Set as Set {-| The 'Fold' data type is the Boehm-Berarducci encoding of the core graph@@ -70,6 +69,14 @@ > 1 + 2 * 3 == overlay (vertex 1) (connect (vertex 2) (vertex 3)) > 1 * (2 + 3) == connect (vertex 1) (overlay (vertex 2) (vertex 3)) +__Note:__ the 'Num' instance does not satisfy several "customary laws" of 'Num',+which dictate that 'fromInteger' @0@ and 'fromInteger' @1@ should act as+additive and multiplicative identities, and 'negate' as additive inverse.+Nevertheless, overloading 'fromInteger', '+' and '*' is very convenient when+working with algebraic graphs; we hope that in future Haskell's Prelude will+provide a more fine-grained class hierarchy for algebraic structures, which we+would be able to utilise without violating any laws.+ The 'Show' instance is defined using basic graph construction primitives: @show (empty :: Fold Int) == "empty"@@ -125,36 +132,61 @@ m == 'edgeCount' g s == 'size' g@ -Note that 'size' is slightly different from the 'length' method of the-'Foldable' type class, as the latter does not count 'empty' leaves of the-expression:--@'length' 'empty' == 0-'size' 'empty' == 1-'length' ('vertex' x) == 1-'size' ('vertex' x) == 1-'length' ('empty' + 'empty') == 0-'size' ('empty' + 'empty') == 2@+Note that 'size' counts all leaves of the expression: -The 'size' of any graph is positive, and the difference @('size' g - 'length' g)@-corresponds to the number of occurrences of 'empty' in an expression @g@.+@'vertexCount' 'empty' == 0+'size' 'empty' == 1+'vertexCount' ('vertex' x) == 1+'size' ('vertex' x) == 1+'vertexCount' ('empty' + 'empty') == 0+'size' ('empty' + 'empty') == 2@ Converting a 'Fold' to the corresponding 'AM.AdjacencyMap' takes /O(s + m * log(m))/ time and /O(s + m)/ memory. This is also the complexity of the graph equality test, because it is currently implemented by converting graph expressions to canonical representations based on adjacency maps.++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 < 'edge' 1 2+'vertex' 1 < 'edge' 1 1+'edge' 1 1 < 'edge' 1 2+'edge' 1 2 < 'edge' 1 1 + 'edge' 2 2+'edge' 1 2 < 'edge' 1 3@++Note that the resulting order refines the 'isSubgraphOf' relation and is+compatible with 'overlay' and 'connect' operations:++@'isSubgraphOf' x y ==> x <= y@++@'empty' <= x+x <= x + y+x + y <= x * y@ -} newtype Fold a = Fold { runFold :: forall b. b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> b } instance (Ord a, Show a) => Show (Fold a) where- show = show . foldg AM.empty AM.vertex AM.overlay AM.connect+ showsPrec p = showsPrec p . foldg AM.empty AM.vertex AM.overlay AM.connect instance Ord a => Eq (Fold a) where- x == y = T.adjacencyMap x == T.adjacencyMap y+ x == y = T.toAdjacencyMap x == T.toAdjacencyMap y +instance Ord a => Ord (Fold a) where+ compare x y = compare (T.toAdjacencyMap x) (T.toAdjacencyMap y)+ instance NFData a => NFData (Fold a) where rnf = foldg () rnf seq seq +-- | __Note:__ this does not satisfy the usual ring laws; see 'Fold' for more+-- details. instance Num a => Num (Fold a) where fromInteger = vertex . fromInteger (+) = overlay@@ -182,12 +214,6 @@ return = vertex g >>=f = foldg empty f overlay connect g -instance Foldable Fold where- foldMap f = foldg mempty f mappend mappend--instance Traversable Fold where- traverse f = foldg (pure empty) (fmap vertex . f) (liftA2 overlay) (liftA2 connect)- instance ToGraph (Fold a) where type ToVertex (Fold a) = a foldg = foldg@@ -340,11 +366,10 @@ -- -- @ -- foldg 'empty' 'vertex' 'overlay' 'connect' == id--- foldg 'empty' 'vertex' 'overlay' (flip 'connect') == 'transpose'--- foldg [] return (++) (++) == 'Data.Foldable.toList'--- foldg 0 (const 1) (+) (+) == 'Data.Foldable.length'--- foldg 1 (const 1) (+) (+) == 'size'--- foldg True (const False) (&&) (&&) == 'isEmpty'+-- foldg 'empty' 'vertex' 'overlay' ('flip' 'connect') == 'transpose'+-- foldg 1 ('const' 1) (+) (+) == 'size'+-- foldg True ('const' False) (&&) (&&) == 'isEmpty'+-- foldg False (== x) (||) (||) == 'hasVertex' x -- @ foldg :: b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Fold a -> b foldg e v o c g = runFold g e v o c@@ -355,11 +380,12 @@ -- graph can be quadratic with respect to the expression size /s/. -- -- @--- 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 '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 x y ==> x <= y -- @ isSubgraphOf :: Ord a => Fold a -> Fold a -> Bool isSubgraphOf x y = overlay x y == y@@ -392,14 +418,14 @@ size :: Fold a -> Int size = T.size --- | Check if a graph contains a given vertex. A convenient alias for `elem`.+-- | Check if a graph contains a given vertex. -- Complexity: /O(s)/ time. -- -- @ -- hasVertex x 'empty' == False -- hasVertex x ('vertex' x) == True -- hasVertex 1 ('vertex' 2) == False--- hasVertex x . 'removeVertex' x == const False+-- hasVertex x . 'removeVertex' x == 'const' False -- @ hasVertex :: Eq a => a -> Fold a -> Bool hasVertex = T.hasVertex@@ -411,7 +437,7 @@ -- hasEdge x y 'empty' == False -- hasEdge x y ('vertex' z) == False -- hasEdge x y ('edge' x y) == True--- hasEdge x y . 'removeEdge' x y == const False+-- hasEdge x y . 'removeEdge' x y == 'const' False -- hasEdge x y == 'elem' (x,y) . 'edgeList' -- @ hasEdge :: Eq a => a -> a -> Fold a -> Bool@@ -421,9 +447,10 @@ -- Complexity: /O(s * log(n))/ time. -- -- @--- vertexCount 'empty' == 0--- vertexCount ('vertex' x) == 1--- vertexCount == 'length' . 'vertexList'+-- vertexCount 'empty' == 0+-- vertexCount ('vertex' x) == 1+-- vertexCount == 'length' . 'vertexList'+-- vertexCount x \< vertexCount y ==> x \< y -- @ vertexCount :: Ord a => Fold a -> Int vertexCount = T.vertexCount@@ -462,7 +489,7 @@ -- edgeList ('edge' x y) == [(x,y)] -- edgeList ('star' 2 [3,1]) == [(2,1), (2,3)] -- edgeList . 'edges' == 'Data.List.nub' . 'Data.List.sort'--- edgeList . 'transpose' == 'Data.List.sort' . map 'Data.Tuple.swap' . edgeList+-- edgeList . 'transpose' == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . edgeList -- @ edgeList :: Ord a => Fold a -> [(a, a)] edgeList = T.edgeList@@ -474,24 +501,10 @@ -- vertexSet 'empty' == Set.'Set.empty' -- vertexSet . 'vertex' == Set.'Set.singleton' -- vertexSet . 'vertices' == Set.'Set.fromList'--- vertexSet . 'clique' == Set.'Set.fromList' -- @ vertexSet :: Ord a => Fold a -> Set.Set a vertexSet = T.vertexSet --- | The set of vertices of a given graph. Like 'vertexSet' but specialised for--- graphs with vertices of type 'Int'.--- Complexity: /O(s * log(n))/ time and /O(n)/ memory.------ @--- vertexIntSet 'empty' == IntSet.'IntSet.empty'--- vertexIntSet . 'vertex' == IntSet.'IntSet.singleton'--- vertexIntSet . 'vertices' == IntSet.'IntSet.fromList'--- vertexIntSet . 'clique' == IntSet.'IntSet.fromList'--- @-vertexIntSet :: Fold Int -> IntSet.IntSet-vertexIntSet = T.vertexIntSet- -- | The set of edges of a given graph. -- Complexity: /O(s * log(m))/ time and /O(m)/ memory. --@@ -604,7 +617,7 @@ -- stars [(x, [])] == 'vertex' x -- stars [(x, [y])] == 'edge' x y -- stars [(x, ys)] == 'star' x ys--- stars == 'overlays' . map (uncurry 'star')+-- stars == 'overlays' . 'map' ('uncurry' 'star') -- stars . 'adjacencyList' == id -- 'overlay' (stars xs) (stars ys) == stars (xs ++ ys) -- @@@ -640,12 +653,12 @@ removeEdge s t = filterContext s (/=s) (/=t) -- TODO: Export--- | Filter vertices in a subgraph context.+-- Filter vertices in a subgraph context. filterContext :: Eq a => a -> (a -> Bool) -> (a -> Bool) -> Fold a -> Fold a filterContext s i o g = maybe g go $ G.context (==s) (toGraph g) where go (G.Context is os) = induce (/=s) g `overlay` transpose (star s (filter i is))- `overlay` star s (filter o os)+ `overlay` star s (filter o os) -- | Transpose a given graph. -- Complexity: /O(s)/ time, memory and size.@@ -656,7 +669,7 @@ -- transpose ('edge' x y) == 'edge' y x -- transpose . transpose == id -- transpose ('box' x y) == 'box' (transpose x) (transpose y)--- 'edgeList' . transpose == 'Data.List.sort' . map 'Data.Tuple.swap' . 'edgeList'+-- 'edgeList' . transpose == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . 'edgeList' -- @ transpose :: Fold a -> Fold a transpose = foldg empty vertex overlay (flip connect)@@ -681,8 +694,8 @@ -- /O(1)/ to be evaluated. -- -- @--- induce (const True ) x == x--- induce (const False) x == 'empty'+-- 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
src/Algebra/Graph/HigherKinded/Class.hs view
@@ -43,31 +43,25 @@ isSubgraphOf, -- * Graph properties- isEmpty, hasVertex, hasEdge, vertexCount, vertexList, vertexSet, vertexIntSet,+ hasEdge, -- * Standard families of graphs- path, circuit, clique, biclique, star, starTranspose, tree, forest, mesh,- torus, deBruijn,+ path, circuit, clique, biclique, star, stars, tree, forest, mesh, torus,+ deBruijn, -- * Graph transformation- removeVertex, replaceVertex, mergeVertices, splitVertex, induce,-- -- * Graph composition- box- ) where+ removeVertex, replaceVertex, mergeVertices, splitVertex, induce+ ) where import Prelude () import Prelude.Compat import Control.Applicative (Alternative(empty, (<|>)))-import Control.Monad.Compat (MonadPlus, msum, mfilter)-import Data.Foldable (toList)+import Control.Monad.Compat (MonadPlus, mfilter) import Data.Tree import qualified Algebra.Graph as G import qualified Algebra.Graph.Fold as F-import qualified Data.IntSet as IntSet-import qualified Data.Set as Set {-| The core type class for constructing algebraic graphs is defined by introducing@@ -128,7 +122,7 @@ edges in the graph, and /s/ will denote the /size/ of the corresponding 'Graph' expression. -}-class (Traversable g,+class ( #if !MIN_VERSION_base(4,8,0) Alternative g, #endif@@ -282,30 +276,6 @@ isSubgraphOf :: (Graph g, Eq (g a)) => g a -> g a -> Bool isSubgraphOf x y = overlay x y == y --- | Check if a graph is empty. A convenient alias for 'null'.--- Complexity: /O(s)/ time.------ @--- isEmpty 'empty' == True--- isEmpty ('overlay' 'empty' 'empty') == True--- isEmpty ('vertex' x) == False--- isEmpty ('removeVertex' x $ 'vertex' x) == True--- @-isEmpty :: Graph g => g a -> Bool-isEmpty = null---- | Check if a graph contains a given vertex. A convenient alias for `elem`.--- Complexity: /O(s)/ time.------ @--- hasVertex x 'empty' == False--- hasVertex x ('vertex' x) == True--- hasVertex 1 ('vertex' 2) == False--- hasVertex x . 'removeVertex' x == const False--- @-hasVertex :: (Eq a, Graph g) => a -> g a -> Bool-hasVertex = elem- -- | Check if a graph contains a given edge. -- Complexity: /O(s)/ time. --@@ -316,54 +286,7 @@ -- hasEdge x y == 'elem' (x,y) . 'edgeList' -- @ hasEdge :: (Eq (g a), Graph g, Ord a) => a -> a -> g a -> Bool-hasEdge u v = (edge u v `isSubgraphOf`) . induce (`elem` [u, v])---- | The number of vertices in a graph.--- Complexity: /O(s * log(n))/ time.------ @--- vertexCount 'empty' == 0--- vertexCount ('vertex' x) == 1--- vertexCount == 'length' . 'vertexList'--- @-vertexCount :: (Ord a, Graph g) => g a -> Int-vertexCount = length . vertexList---- | The sorted list of vertices of a given graph.--- Complexity: /O(s * log(n))/ time and /O(n)/ memory.------ @--- vertexList 'empty' == []--- vertexList ('vertex' x) == [x]--- vertexList . 'vertices' == 'Data.List.nub' . 'Data.List.sort'--- @-vertexList :: (Ord a, Graph g) => g a -> [a]-vertexList = Set.toAscList . vertexSet---- | The set of vertices of a given graph.--- Complexity: /O(s * log(n))/ time and /O(n)/ memory.------ @--- vertexSet 'empty' == Set.'Set.empty'--- vertexSet . 'vertex' == Set.'Set.singleton'--- vertexSet . 'vertices' == Set.'Set.fromList'--- vertexSet . 'clique' == Set.'Set.fromList'--- @-vertexSet :: (Ord a, Graph g) => g a -> Set.Set a-vertexSet = foldr Set.insert Set.empty---- | The set of vertices of a given graph. Like 'vertexSet' but specialised for--- graphs with vertices of type 'Int'.--- Complexity: /O(s * log(n))/ time and /O(n)/ memory.------ @--- vertexIntSet 'empty' == IntSet.'IntSet.empty'--- vertexIntSet . 'vertex' == IntSet.'IntSet.singleton'--- vertexIntSet . 'vertices' == IntSet.'IntSet.fromList'--- vertexIntSet . 'clique' == IntSet.'IntSet.fromList'--- @-vertexIntSet :: Graph g => g Int -> IntSet.IntSet-vertexIntSet = foldr IntSet.insert IntSet.empty+hasEdge u v = (edge u v `isSubgraphOf`) . induce (\x -> x == u || x == v) -- | The /path/ on a list of vertices. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the@@ -436,20 +359,22 @@ star x [] = vertex x star x ys = connect (vertex x) (vertices ys) --- | The /star transpose/ formed by a list of leaves connected to a centre vertex.--- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the--- given list.+-- | The /stars/ formed by overlaying a list of 'star's. An inverse of+-- 'adjacencyList'.+-- Complexity: /O(L)/ time, memory and size, where /L/ is the total size of the+-- input. -- -- @--- starTranspose x [] == 'vertex' x--- starTranspose x [y] == 'edge' y x--- starTranspose x [y,z] == 'edges' [(y,x), (z,x)]--- starTranspose x ys == 'connect' ('vertices' ys) ('vertex' x)--- starTranspose x ys == transpose ('star' x ys)+-- 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) -- @-starTranspose :: Graph g => a -> [a] -> g a-starTranspose x [] = vertex x-starTranspose x ys = connect (vertices ys) (vertex x)+stars :: Graph g => [(a, [a])] -> g a+stars = overlays . map (uncurry star) -- | The /tree graph/ constructed from a given 'Tree' data structure. -- Complexity: /O(T)/ time, memory and size, where /T/ is the size of the@@ -474,7 +399,7 @@ -- 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 == 'overlays' . 'map' 'tree' -- @ forest :: Graph g => Forest a -> g a forest = overlays . map tree@@ -492,7 +417,17 @@ -- , ((2,\'a\'),(3,\'a\')), ((2,\'b\'),(3,\'b\')), ((3,\'a\'),(3,\'b\')) ] -- @ mesh :: Graph g => [a] -> [b] -> g (a, b)-mesh xs ys = path xs `box` path ys+mesh [] _ = empty+mesh _ [] = empty+mesh [x] [y] = vertex (x, y)+mesh xs ys = stars $ [ ((a1, b1), [(a1, b2), (a2, b1)]) | (a1, a2) <- ipxs, (b1, b2) <- ipys ]+ ++ [ ((lx,y1), [(lx,y2)]) | (y1,y2) <- ipys]+ ++ [ ((x1,ly), [(x2,ly)]) | (x1,x2) <- ipxs]+ where+ lx = last xs+ ly = last ys+ ipxs = init (pairs xs)+ ipys = init (pairs ys) -- | Construct a /torus graph/ from two lists of vertices. -- Complexity: /O(L1 * L2)/ time, memory and size, where /L1/ and /L2/ are the@@ -507,8 +442,13 @@ -- , ((2,\'a\'),(1,\'a\')), ((2,\'a\'),(2,\'b\')), ((2,\'b\'),(1,\'b\')), ((2,\'b\'),(2,\'a\')) ] -- @ torus :: Graph g => [a] -> [b] -> g (a, b)-torus xs ys = circuit xs `box` circuit ys+torus xs ys = stars [ ((a1, b1), [(a1, b2), (a2, b1)]) | (a1, a2) <- pairs xs, (b1, b2) <- pairs ys ] +-- | Auxiliary function for 'mesh' and 'torus'+pairs :: [a] -> [(a, a)]+pairs [] = []+pairs as@(x:xs) = zip as (xs ++ [x])+ -- | Construct a /De Bruijn graph/ of a given non-negative dimension using symbols -- from a given alphabet. -- Complexity: /O(A^(D + 1))/ time, memory and size, where /A/ is the size of the@@ -539,8 +479,8 @@ -- /O(1)/ to be evaluated. -- -- @--- induce (const True ) x == x--- induce (const False) x == 'empty'+-- 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@@ -578,10 +518,10 @@ -- /O(1)/ to be evaluated. -- -- @--- mergeVertices (const False) x == id+-- 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 'even' 1 (0 * 2) == 1 * 1+-- mergeVertices 'odd' 1 (3 + 4 * 5) == 4 * 1 -- @ mergeVertices :: Graph g => (a -> Bool) -> a -> g a -> g a mergeVertices p v = fmap $ \w -> if p w then v else w@@ -599,33 +539,3 @@ -- @ splitVertex :: (Eq a, Graph g) => a -> [a] -> g a -> g a splitVertex v us g = g >>= \w -> if w == v then vertices us else vertex w---- | Compute the /Cartesian product/ of graphs.--- Complexity: /O(s1 * s2)/ time, memory and size, where /s1/ and /s2/ are the--- sizes of the given graphs.------ @--- box ('path' [0,1]) ('path' "ab") == 'edges' [ ((0,\'a\'), (0,\'b\'))--- , ((0,\'a\'), (1,\'a\'))--- , ((0,\'b\'), (1,\'b\'))--- , ((1,\'a\'), (1,\'b\')) ]--- @--- Up to an isomorphism between the resulting vertex types, this operation--- is /commutative/, /associative/, /distributes/ over 'overlay', has singleton--- graphs as /identities/ and 'empty' as the /annihilating zero/. Below @~~@--- stands for the equality up to an isomorphism, e.g. @(x, ()) ~~ x@.------ @--- box x y ~~ box y x--- box x (box y z) ~~ box (box x y) z--- box x ('overlay' y z) == 'overlay' (box x y) (box x z)--- box x ('vertex' ()) ~~ x--- box x 'empty' ~~ 'empty'--- 'vertexCount' (box x y) == 'vertexCount' x * 'vertexCount' y--- 'edgeCount' (box x y) <= 'vertexCount' x * 'edgeCount' y + 'edgeCount' x * 'vertexCount' y--- @-box :: Graph g => g a -> g b -> g (a, b)-box x y = msum $ xs ++ ys- where- xs = map (\b -> fmap (,b) x) $ toList y- ys = map (\a -> fmap (a,) y) $ toList x
src/Algebra/Graph/Internal.hs view
@@ -16,13 +16,15 @@ -- is unstable and unsafe, and is exposed only for documentation. ----------------------------------------------------------------------------- module Algebra.Graph.Internal (- -- * General data structures+ -- * Data structures List (..), - -- * Data structures for graph traversal+ -- * Graph traversal Focus (..), emptyFocus, vertexFocus, overlayFoci, connectFoci, Hit (..),+ foldr1Safe, maybeF, - foldr1Safe+ -- * Utilities+ setProduct, setProductWith ) where import Prelude ()@@ -30,7 +32,9 @@ import Data.Foldable import Data.Semigroup+import Data.Set (Set) +import qualified Data.Set as Set import qualified GHC.Exts as Exts -- | An abstract list data type with /O(1)/ time concatenation (the current@@ -110,11 +114,33 @@ -- its 'Tail', i.e. the source vertex, the whole 'Edge', or 'Miss' it entirely. data Hit = Miss | Tail | Edge deriving (Eq, Ord) --- | A safe version of 'foldr1'+-- | A safe version of 'foldr1'. foldr1Safe :: (a -> a -> a) -> [a] -> Maybe a-foldr1Safe f = foldr mf Nothing- where- mf x m = Just (case m of- Nothing -> x- Just y -> f x y)-{-# INLINE foldr1Safe #-}+foldr1Safe f = foldr (maybeF f) Nothing+{-# INLINE [0] foldr1Safe #-}++-- | Tragetting 'map' directly+{-# RULES+"foldr1Safe/build"+ forall k f lst.+ foldr1Safe k (map f lst) = foldr (maybeF k . f) Nothing lst+ #-}++-- | Auxiliary function that try to apply a function to a base case and a 'Maybe'+-- value and return 'Just' the result or 'Just' the base case.+maybeF :: (a -> b -> a) -> a -> Maybe b -> Maybe a+maybeF f x = Just . maybe x (f x)+{-# INLINE maybeF #-}++-- | Compute the Cartesian product of two sets.+setProduct :: Set a -> Set b -> Set (a, b)+#if MIN_VERSION_containers(0,5,11)+setProduct = Set.cartesianProduct+#else+setProduct x y = Set.fromDistinctAscList [ (a, b) | a <- Set.toAscList x, b <- Set.toAscList y ]+#endif++-- | Compute the Cartesian product of two sets, applying a function to each+-- resulting pair.+setProductWith :: Ord c => (a -> b -> c) -> Set a -> Set b -> Set c+setProductWith f x y = Set.fromList [ f a b | a <- Set.toAscList x, b <- Set.toAscList y ]
src/Algebra/Graph/Label.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DeriveFunctor, OverloadedLists #-} ----------------------------------------------------------------------------- -- | -- Module : Algebra.Graph.Label@@ -15,112 +16,459 @@ -- ----------------------------------------------------------------------------- module Algebra.Graph.Label (- -- * Type classes for edge labels- Semilattice (..), Dioid (..),+ -- * Semirings and dioids+ Semiring (..), zero, (<+>), StarSemiring (..), Dioid, -- * Data types for edge labels- Distance (..)- ) where+ NonNegative, finite, finiteWord, unsafeFinite, infinite, getFinite,+ Distance, distance, getDistance, Capacity, capacity, getCapacity,+ Count, count, getCount, PowerSet (..), Minimum, getMinimum, noMinimum,+ Path, Label, isZero, RegularExpression, + -- * Combining edge labels+ Optimum (..), ShortestPath, AllShortestPaths, CountShortestPaths, WidestPath+ ) where+ import Prelude () import Prelude.Compat++import Control.Applicative+import Control.Monad+import Data.Maybe+import Data.Monoid (Any (..), Monoid (..), Sum (..))+import Data.Semigroup (Min (..), Max (..), Semigroup (..)) import Data.Set (Set)+import GHC.Exts (IsList (..)) +import Algebra.Graph.Internal+ import qualified Data.Set as Set -{-| A /bounded join semilattice/, satisfying the following laws:+{-| A /semiring/ extends a commutative 'Monoid' with operation '<.>' that acts+similarly to multiplication over the underlying (additive) monoid and has 'one'+as the identity. This module also provides two convenient aliases: 'zero' for+'mempty', and '<+>' for '<>', which makes the interface more uniform. - * Commutativity:+Instances of this type class must satisfy the following semiring laws: - > x \/ y == y \/ x+ * Associativity of '<+>' and '<.>': - * Associativity:+ > x <+> (y <+> z) == (x <+> y) <+> z+ > x <.> (y <.> z) == (x <.> y) <.> z - > x \/ (y \/ z) == (x \/ y) \/ z+ * Identities of '<+>' and '<.>': - * Identity:+ > zero <+> x == x == x <+> zero+ > one <.> x == x == x <.> one - > x \/ zero == x+ * Commutativity of '<+>': - * Idempotence:+ > x <+> y == y <+> x - > x \/ x == x+ * Annihilating 'zero':++ > x <.> zero == zero+ > zero <.> x == zero++ * Distributivity:++ > x <.> (y <+> z) == x <.> y <+> x <.> z+ > (x <+> y) <.> z == x <.> z <+> y <.> z -}-class Semilattice a where- zero :: a- (\/) :: a -> a -> a+class (Monoid a, Semigroup a) => Semiring a where+ one :: a+ (<.>) :: a -> a -> a -{-| A /dioid/ is an /idempotent semiring/, i.e. it satisfies the following laws:+{-| A /star semiring/ is a 'Semiring' with an additional unary operator 'star'+satisfying the following two laws: - * Associativity:+ > star a = one <+> a <.> star a+ > star a = one <+> star a <.> a+-}+class Semiring a => StarSemiring a where+ star :: a -> a - > x /\ (y /\ z) == (x /\ y) /\ z+{-| A /dioid/ is an /idempotent semiring/, i.e. it satisfies the following+/idempotence/ law in addition to the 'Semiring' laws: - * Identity:+ > x <+> x == x+-}+class Semiring a => Dioid a - > x /\ one == x- > one /\ x == x+-- | An alias for 'mempty'.+zero :: Monoid a => a+zero = mempty - * Annihilating zero:+-- | An alias for '<>'.+(<+>) :: Semigroup a => a -> a -> a+(<+>) = (<>) - > x /\ zero == zero- > zero /\ x == zero+infixr 6 <+>+infixr 7 <.> - * Distributivity:+instance Semiring Any where+ one = Any True+ Any x <.> Any y = Any (x && y) - > x /\ (y \/ z) == x /\ y \/ x /\ z- > (x \/ y) /\ z == x /\ z \/ y /\ z--}-class Semilattice a => Dioid a where- one :: a- (/\) :: a -> a -> a+instance StarSemiring Any where+ star _ = Any True -infixl 6 \/-infixl 7 /\+instance Dioid Any -instance Semilattice Bool where- zero = False- (\/) = (||)+-- | A non-negative value that can be 'finite' or 'infinite'. Note: the current+-- implementation of the 'Num' instance raises an error on negative literals+-- and on the 'negate' method.+newtype NonNegative a = NonNegative (Extended a)+ deriving (Applicative, Eq, Functor, Ord, Monad) -instance Dioid Bool where- one = True- (/\) = (&&)+instance (Num a, Show a) => Show (NonNegative a) where+ show (NonNegative Infinite ) = "infinite"+ show (NonNegative (Finite x)) = show x --- | A /distance/ is a non-negative value that can be 'Finite' or 'Infinite'.-data Distance a = Finite a | Infinite deriving (Eq, Ord, Show)+instance Num a => Bounded (NonNegative a) where+ minBound = unsafeFinite 0+ maxBound = infinite -instance (Ord a, Num a) => Num (Distance a) where+instance (Num a, Ord a) => Num (NonNegative a) where+ fromInteger x | f < 0 = error "NonNegative values cannot be negative"+ | otherwise = unsafeFinite f+ where+ f = fromInteger x++ (+) = liftA2 (+)+ (*) = liftA2 (*)++ negate _ = error "NonNegative values cannot be negated"++ signum (NonNegative Infinite) = 1+ signum x = signum <$> x++ abs = id++-- | A finite non-negative value or @Nothing@ if the argument is negative.+finite :: (Num a, Ord a) => a -> Maybe (NonNegative a)+finite x | x < 0 = Nothing+ | otherwise = Just (unsafeFinite x)++-- | A finite 'Word'.+finiteWord :: Word -> NonNegative Word+finiteWord = unsafeFinite++-- | A non-negative finite value, created /unsafely/: the argument is not+-- checked for being non-negative, so @unsafeFinite (-1)@ compiles just fine.+unsafeFinite :: a -> NonNegative a+unsafeFinite = NonNegative . Finite++-- | The (non-negative) infinite value.+infinite :: NonNegative a+infinite = NonNegative Infinite++-- | Get a finite value or @Nothing@ if the value is infinite.+getFinite :: NonNegative a -> Maybe a+getFinite (NonNegative x) = fromExtended x++-- | A /capacity/ is a non-negative value that can be 'finite' or 'infinite'.+-- Capacities form a 'Dioid' as follows:+--+-- @+-- 'zero' = 0+-- 'one' = 'capacity' 'infinite'+-- ('<+>') = 'max'+-- ('<.>') = 'min'+-- @+newtype Capacity a = Capacity (Max (NonNegative a))+ deriving (Bounded, Eq, Monoid, Num, Ord, Semigroup)++instance Show a => Show (Capacity a) where+ show (Capacity (Max (NonNegative (Finite x)))) = show x+ show _ = "capacity infinite"++instance (Num a, Ord a) => Semiring (Capacity a) where+ one = capacity infinite+ (<.>) = min++instance (Num a, Ord a) => StarSemiring (Capacity a) where+ star _ = one++instance (Num a, Ord a) => Dioid (Capacity a)++-- | A non-negative capacity.+capacity :: NonNegative a -> Capacity a+capacity = Capacity . Max++-- | Get the value of a capacity.+getCapacity :: Capacity a -> NonNegative a+getCapacity (Capacity (Max x)) = x++-- | A /count/ is a non-negative value that can be 'finite' or 'infinite'.+-- Counts form a 'Semiring' as follows:+--+-- @+-- 'zero' = 0+-- 'one' = 1+-- ('<+>') = ('+')+-- ('<.>') = ('*')+-- @+newtype Count a = Count (Sum (NonNegative a))+ deriving (Bounded, Eq, Monoid, Num, Ord, Semigroup)++instance Show a => Show (Count a) where+ show (Count (Sum (NonNegative (Finite x)))) = show x+ show _ = "count infinite"++instance (Num a, Ord a) => Semiring (Count a) where+ one = 1+ (<.>) = (*)++instance (Num a, Ord a) => StarSemiring (Count a) where+ star x | x == zero = one+ | otherwise = count infinite++-- | A non-negative count.+count :: NonNegative a -> Count a+count = Count . Sum++-- | Get the value of a count.+getCount :: Count a -> NonNegative a+getCount (Count (Sum x)) = x++-- | A /distance/ is a non-negative value that can be 'finite' or 'infinite'.+-- Distances form a 'Dioid' as follows:+--+-- @+-- 'zero' = 'distance' 'infinite'+-- 'one' = 0+-- ('<+>') = 'min'+-- ('<.>') = ('+')+-- @+newtype Distance a = Distance (Min (NonNegative a))+ deriving (Bounded, Eq, Monoid, Num, Ord, Semigroup)++instance Show a => Show (Distance a) where+ show (Distance (Min (NonNegative (Finite x)))) = show x+ show _ = "distance infinite"++instance (Num a, Ord a) => Semiring (Distance a) where+ one = 0+ (<.>) = (+)++instance (Num a, Ord a) => StarSemiring (Distance a) where+ star _ = one++instance (Num a, Ord a) => Dioid (Distance a)++-- | A non-negative distance.+distance :: NonNegative a -> Distance a+distance = Distance . Min++-- | Get the value of a distance.+getDistance :: Distance a -> NonNegative a+getDistance (Distance (Min x)) = x++-- This data type extends the underlying type @a@ with a new 'Infinite' value.+data Extended a = Finite a | Infinite+ deriving (Eq, Functor, Ord, Show)++instance Applicative Extended where+ pure = Finite+ (<*>) = ap++instance Monad Extended where+ return = pure++ Infinite >>= _ = Infinite+ Finite x >>= f = f x++-- Extract the finite value or @Nothing@ if the value is 'Infinite'.+fromExtended :: Extended a -> Maybe a+fromExtended (Finite a) = Just a+fromExtended Infinite = Nothing++instance Num a => Num (Extended a) where fromInteger = Finite . fromInteger - Infinite + _ = Infinite- _ + Infinite = Infinite- Finite x + Finite y = Finite (x + y)+ (+) = liftA2 (+)+ (*) = liftA2 (*) - Infinite * _ = Infinite- _ * Infinite = Infinite- Finite x * Finite y = Finite (x * y)+ negate = fmap negate+ signum = fmap signum+ abs = fmap abs - negate _ = error "Negative distances not allowed"+-- | If @a@ is a monoid, 'Minimum' @a@ forms the following 'Dioid':+--+-- @+-- 'zero' = 'pure' 'mempty'+-- 'one' = 'noMinimum'+-- ('<+>') = 'liftA2' 'min'+-- ('<.>') = 'liftA2' 'mappend'+-- @+--+-- To create a singleton value of type 'Minimum' @a@ use the 'pure' function.+-- For example:+--+-- @+-- getMinimum ('pure' "Hello, " '<+>' 'pure' "World!") == Just "Hello, "+-- getMinimum ('pure' "Hello, " '<.>' 'pure' "World!") == Just "Hello, World!"+-- @+newtype Minimum a = Minimum (Extended a)+ deriving (Applicative, Eq, Functor, Ord, Monad) - signum (Finite 0) = 0- signum _ = 1+-- | Extract the minimum or @Nothing@ if it does not exist.+getMinimum :: Minimum a -> Maybe a+getMinimum (Minimum x) = fromExtended x - abs = id+-- | The value corresponding to the lack of minimum, e.g. the minimum of the+-- empty set.+noMinimum :: Minimum a+noMinimum = Minimum Infinite -instance Ord a => Semilattice (Distance a) where- zero = Infinite+instance (Num a, Show a) => Show (Minimum a) where+ show (Minimum Infinite ) = "one"+ show (Minimum (Finite x)) = show x - Infinite \/ x = x- x \/ Infinite = x- Finite x \/ Finite y = Finite (min x y)+instance IsList a => IsList (Minimum a) where+ type Item (Minimum a) = Item a+ fromList = Minimum . Finite . fromList+ toList (Minimum x) = toList $ fromMaybe errorMessage (fromExtended x)+ where+ errorMessage = error "Minimum.toList applied to noMinimum value." -instance (Num a, Ord a) => Dioid (Distance a) where- one = Finite 0+-- | The /power set/ over the underlying set of elements @a@. If @a@ is a+-- monoid, then the power set forms a 'Dioid' as follows:+--+-- @+-- 'zero' = PowerSet Set.'Set.empty'+-- 'one' = PowerSet $ Set.'Set.singleton' 'mempty'+-- x '<+>' y = PowerSet $ Set.'Set.union' (getPowerSet x) (getPowerSet y)+-- x '<.>' y = PowerSet $ 'setProductWith' 'mappend' (getPowerSet x) (getPowerSet y)+-- @+newtype PowerSet a = PowerSet { getPowerSet :: Set a }+ deriving (Eq, Monoid, Ord, Semigroup) - Infinite /\ _ = Infinite- _ /\ Infinite = Infinite- Finite x /\ Finite y = Finite (x + y)+instance (Monoid a, Ord a) => Semiring (PowerSet a) where+ one = PowerSet (Set.singleton mempty)+ PowerSet x <.> PowerSet y = PowerSet (setProductWith mappend x y) -instance Ord a => Semilattice (Set a) where- zero = Set.empty- (\/) = Set.union+instance (Monoid a, Ord a) => StarSemiring (PowerSet a) where+ star _ = one++instance (Monoid a, Ord a) => Dioid (PowerSet a) where++-- | The type of /free labels/ over the underlying set of symbols @a@. This data+-- type is an instance of classes 'StarSemiring' and 'Dioid'.+data Label a = Zero+ | One+ | Symbol a+ | Label a :+: Label a+ | Label a :*: Label a+ | Star (Label a)+ deriving Functor++infixl 6 :+:+infixl 7 :*:++instance IsList (Label a) where+ type Item (Label a) = a+ fromList = foldr ((<>) . Symbol) Zero+ toList = error "Label.toList cannot be given a reasonable definition"++instance Show a => Show (Label a) where+ showsPrec p label = case label of+ Zero -> shows (0 :: Int)+ One -> shows (1 :: Int)+ Symbol x -> shows x+ x :+: y -> showParen (p >= 6) $ showsPrec 6 x . (" | " ++) . showsPrec 6 y+ x :*: y -> showParen (p >= 7) $ showsPrec 7 x . (" ; " ++) . showsPrec 7 y+ Star x -> showParen (p >= 8) $ showsPrec 8 x . ("*" ++)++instance Semigroup (Label a) where+ Zero <> x = x+ x <> Zero = x+ One <> One = One+ One <> Star x = Star x+ Star x <> One = Star x+ x <> y = x :+: y++instance Monoid (Label a) where+ mempty = Zero+ mappend = (<>)++instance Semiring (Label a) where+ one = One++ One <.> x = x+ x <.> One = x+ Zero <.> _ = Zero+ _ <.> Zero = Zero+ x <.> y = x :*: y++instance StarSemiring (Label a) where+ star Zero = One+ star One = One+ star (Star x) = star x+ star x = Star x++-- | Check if a 'Label' is 'zero'.+isZero :: Label a -> Bool+isZero Zero = True+isZero _ = False++-- | A type synonym for /regular expressions/, built on top of /free labels/.+type RegularExpression a = Label a++-- | An /optimum semiring/ obtained by combining a semiring @o@ that defines an+-- /optimisation criterion/, and a semiring @a@ that describes the /arguments/+-- of an optimisation problem. For example, by choosing @o = 'Distance' Int@ and+-- and @a = 'Minimum' ('Path' String)@, we obtain the /shortest path semiring/+-- for computing the shortest path in an @Int@-labelled graph with @String@+-- vertices.+--+-- We assume that the semiring @o@ is /selective/ i.e. for all @x@ and @y@:+--+-- > x <+> y == x || x <+> y == y+--+-- In words, the operation '<+>' always simply selects one of its arguments. For+-- example, the 'Capacity' and 'Distance' semirings are selective, whereas the+-- the 'Count' semiring is not.+data Optimum o a = Optimum { getOptimum :: o, getArgument :: a }+ deriving (Eq, Ord, Show)++-- This is similar to geodetic semirings.+-- See http://vlado.fmf.uni-lj.si/vlado/papers/SemiRingSNA.pdf+instance (Eq o, Monoid a, Monoid o) => Semigroup (Optimum o a) where+ Optimum o1 a1 <> Optimum o2 a2+ | o1 == o2 = Optimum o1 (mappend a1 a2)+ | otherwise = Optimum o a+ where+ o = mappend o1 o2+ a = if o == o1 then a1 else a2++instance (Eq o, Monoid a, Monoid o) => Monoid (Optimum o a) where+ mempty = Optimum mempty mempty+ mappend = (<>)++instance (Eq o, Semiring a, Semiring o) => Semiring (Optimum o a) where+ one = Optimum one one+ Optimum o1 a1 <.> Optimum o2 a2 = Optimum (o1 <.> o2) (a1 <.> a2)++instance (Eq o, StarSemiring a, StarSemiring o) => StarSemiring (Optimum o a) where+ star (Optimum o a) = Optimum (star o) (star a)++instance (Eq o, Dioid a, Dioid o) => Dioid (Optimum o a) where++-- | A /path/ is a list of edges.+type Path a = [(a, a)]++-- | The 'Optimum' semiring specialised to /finding the lexicographically+-- smallest shortest path/.+type ShortestPath e a = Optimum (Distance e) (Minimum (Path a))++-- | The 'Optimum' semiring specialised to /finding all shortest paths/.+type AllShortestPaths e a = Optimum (Distance e) (PowerSet (Path a))++-- | The 'Optimum' semiring specialised to /counting all shortest paths/.+type CountShortestPaths e a = Optimum (Distance e) (Count Integer)++-- | The 'Optimum' semiring specialised to /finding the lexicographically+-- smallest widest path/.+type WidestPath e a = Optimum (Capacity e) (Minimum (Path a))
src/Algebra/Graph/Labelled.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}+{-# LANGUAGE DeriveFunctor, FlexibleInstances #-} ----------------------------------------------------------------------------- -- | -- Module : Algebra.Graph.Labelled@@ -16,107 +16,619 @@ ----------------------------------------------------------------------------- module Algebra.Graph.Labelled ( -- * Algebraic data type for edge-labeleld graphs- Graph (..), UnlabelledGraph, empty, vertex, edge, overlay, connect,- connectBy, (-<), (>-),+ Graph (..), empty, vertex, edge, (-<), (>-), overlay, connect, vertices,+ edges, overlays, - -- * Operations- edgeLabel- ) where+ -- * Graph folding+ foldg, + -- * Relations on graphs+ isSubgraphOf,++ -- * Graph properties+ isEmpty, size, hasVertex, hasEdge, edgeLabel, vertexList, edgeList,+ vertexSet, edgeSet,++ -- * Graph transformation+ removeVertex, removeEdge, replaceVertex, replaceEdge, transpose, emap,+ induce,++ -- * Relational operations+ closure, reflexiveClosure, symmetricClosure, transitiveClosure,++ -- * Types of edge-labelled graphs+ UnlabelledGraph, Automaton, Network,++ -- * Context+ Context (..), context+ ) where+ import Prelude () import Prelude.Compat +import Data.Monoid (Any (..))+import Data.Semigroup ((<>))++import Algebra.Graph.Internal (List (..)) import Algebra.Graph.Label-import qualified Algebra.Graph.Class as C +import qualified Algebra.Graph.Labelled.AdjacencyMap as AM+import qualified Data.Set as Set+import qualified Data.Map as Map+import qualified GHC.Exts as Exts+ -- | Edge-labelled graphs, where the type variable @e@ stands for edge labels.--- For example, @Graph Bool a@ is isomorphic to unlabelled graphs defined in+-- For example, 'Graph' @Bool@ @a@ is isomorphic to unlabelled graphs defined in -- the top-level module "Algebra.Graph.Graph", where @False@ and @True@ denote -- the lack of and the existence of an unlabelled edge, respectively. data Graph e a = Empty | Vertex a | Connect e (Graph e a) (Graph e a)- deriving (Foldable, Functor, Show, Traversable)+ deriving (Functor, Show) --- | A type synonym for unlabelled graphs.-type UnlabelledGraph a = Graph Bool a+instance (Eq e, Monoid e, Ord a) => Eq (Graph e a) where+ x == y = toAdjacencyMap x == toAdjacencyMap y +instance (Eq e, Monoid e, Ord a, Ord e) => Ord (Graph e a) where+ compare x y = compare (toAdjacencyMap x) (toAdjacencyMap y)++-- | __Note:__ this does not satisfy the usual ring laws; see 'Graph'+-- for more details.+instance (Ord a, Num a, Dioid e) => Num (Graph e a) where+ fromInteger = vertex . fromInteger+ (+) = overlay+ (*) = connect one+ signum = const empty+ abs = id+ negate = id++-- TODO: This is a very inefficient implementation. Find a way to construct an+-- adjacency map directly, without building intermediate representations for all+-- subgraphs.+-- Extract the adjacency map of a graph.+toAdjacencyMap :: (Eq e, Monoid e, Ord a) => Graph e a -> AM.AdjacencyMap e a+toAdjacencyMap = foldg AM.empty AM.vertex AM.connect++-- Convert the adjacency map to a graph.+fromAdjacencyMap :: Monoid e => AM.AdjacencyMap e a -> Graph e a+fromAdjacencyMap = overlays . map go . Map.toList . AM.adjacencyMap+ where+ go (u, m) = overlay (vertex u) (edges [ (e, u, v) | (v, e) <- Map.toList m])++-- | Generalised 'Graph' folding: recursively collapse a 'Graph' by applying+-- the provided functions to the leaves and internal nodes of the expression.+-- The order of arguments is: empty, vertex and connect.+-- Complexity: /O(s)/ applications of given functions. As an example, the+-- complexity of 'size' is /O(s)/, since all functions have cost /O(1)/.+--+-- @+-- foldg 'empty' 'vertex' 'connect' == 'id'+-- foldg 'empty' 'vertex' ('fmap' 'flip' 'connect') == 'transpose'+-- foldg 1 ('const' 1) ('const' (+)) == 'size'+-- foldg True ('const' False) ('const' (&&)) == 'isEmpty'+-- foldg False (== x) ('const' (||)) == 'hasVertex' x+-- foldg Set.'Set.empty' Set.'Set.singleton' ('const' Set.'Set.union') == 'vertexSet'+-- @+foldg :: b -> (a -> b) -> (e -> b -> b -> b) -> Graph e a -> b+foldg e v c = go+ where+ go Empty = e+ go (Vertex x ) = v x+ go (Connect e x y) = c e (go x) (go y)++-- | The 'isSubgraphOf' function takes two graphs and returns 'True' if the+-- first graph is a /subgraph/ of the second.+-- Complexity: /O(s + m * log(m))/ time. Note that the number of edges /m/ of a+-- graph can be quadratic with respect to the expression size /s/.+--+-- @+-- isSubgraphOf 'empty' x == True+-- isSubgraphOf ('vertex' x) 'empty' == False+-- isSubgraphOf x ('overlay' x y) == True+-- isSubgraphOf ('overlay' x y) ('connect' x y) == True+-- isSubgraphOf x y ==> x <= y+-- @+isSubgraphOf :: (Eq e, Monoid e, Ord a) => Graph e a -> Graph e a -> Bool+isSubgraphOf x y = overlay x y == y+ -- | Construct the /empty graph/. An alias for the constructor 'Empty'. -- Complexity: /O(1)/ time, memory and size.+--+-- @+-- 'isEmpty' empty == True+-- 'hasVertex' x empty == False+-- 'Algebra.Graph.ToGraph.vertexCount' empty == 0+-- 'Algebra.Graph.ToGraph.edgeCount' empty == 0+-- @ empty :: Graph e a empty = Empty -- | Construct the graph comprising /a single isolated vertex/. An alias for the -- constructor 'Vertex'. -- Complexity: /O(1)/ time, memory and size.+--+-- @+-- 'isEmpty' (vertex x) == False+-- 'hasVertex' x (vertex x) == True+-- 'Algebra.Graph.ToGraph.vertexCount' (vertex x) == 1+-- 'Algebra.Graph.ToGraph.edgeCount' (vertex x) == 0+-- @ vertex :: a -> Graph e a vertex = Vertex --- | Construct the graph comprising /a single edge/ with the label 'one'.+-- | Construct the graph comprising /a single labelled edge/. -- Complexity: /O(1)/ time, memory and size.-edge :: Dioid e => a -> a -> Graph e a-edge = C.edge+--+-- @+-- edge e x y == 'connect' e ('vertex' x) ('vertex' y)+-- edge 'zero' x y == 'vertices' [x,y]+-- 'hasEdge' x y (edge e x y) == (e /= 'zero')+-- 'edgeLabel' x y (edge e x y) == e+-- 'Algebra.Graph.ToGraph.edgeCount' (edge e x y) == if e == 'zero' then 0 else 1+-- 'Algebra.Graph.ToGraph.vertexCount' (edge e 1 1) == 1+-- 'Algebra.Graph.ToGraph.vertexCount' (edge e 1 2) == 2+-- @+edge :: e -> a -> a -> Graph e a+edge e x y = connect e (vertex x) (vertex y) --- | /Overlay/ two graphs. An alias for 'Connect' 'zero'. This is a commutative,--- associative and idempotent operation with the identity 'empty'.--- Complexity: /O(1)/ time and memory, /O(s1 + s2)/ size.-overlay :: Semilattice e => Graph e a -> Graph e a -> Graph e a-overlay = Connect zero+-- | The left-hand part of a convenient ternary-ish operator @x-\<e\>-y@ for+-- creating labelled edges.+--+-- @+-- x -\<e\>- y == 'edge' e x y+-- @+(-<) :: a -> e -> (a, e)+g -< e = (g, e) --- | /Connect/ two graphs. An alias for 'Connect' 'one'. This is an associative--- operation with the identity 'empty', which distributes over 'overlay' and--- obeys the decomposition axiom. See the full list of laws in "Algebra.Graph".--- Complexity: /O(1)/ time and memory, /O(s1 + s2)/ size. 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 :: Dioid e => Graph e a -> Graph e a -> Graph e a-connect = Connect one+-- | The right-hand part of a convenient ternary-ish operator @x-\<e\>-y@ for+-- creating labelled edges.+--+-- @+-- x -\<e\>- y == 'edge' e x y+-- @+(>-) :: (a, e) -> a -> Graph e a+(x, e) >- y = edge e x y +infixl 5 -<+infixl 5 >-++-- | /Overlay/ two graphs. An alias for 'Connect' 'zero'.+-- Complexity: /O(1)/ time and memory, /O(s1 + s2)/ size.+--+-- @+-- 'isEmpty' (overlay x y) == 'isEmpty' x && 'isEmpty' y+-- 'hasVertex' z (overlay x y) == 'hasVertex' z x || 'hasVertex' z y+-- 'Algebra.Graph.ToGraph.vertexCount' (overlay x y) >= 'Algebra.Graph.ToGraph.vertexCount' x+-- 'Algebra.Graph.ToGraph.vertexCount' (overlay x y) <= 'Algebra.Graph.ToGraph.vertexCount' x + 'Algebra.Graph.ToGraph.vertexCount' y+-- 'Algebra.Graph.ToGraph.edgeCount' (overlay x y) >= 'Algebra.Graph.ToGraph.edgeCount' x+-- 'Algebra.Graph.ToGraph.edgeCount' (overlay x y) <= 'Algebra.Graph.ToGraph.edgeCount' x + 'Algebra.Graph.ToGraph.edgeCount' y+-- 'Algebra.Graph.ToGraph.vertexCount' (overlay 1 2) == 2+-- 'Algebra.Graph.ToGraph.edgeCount' (overlay 1 2) == 0+-- @+--+-- Note: 'overlay' composes edges in parallel using the operator '<+>' with+-- 'zero' acting as the identity:+--+-- @+-- 'edgeLabel' x y $ overlay ('edge' e x y) ('edge' 'zero' x y) == e+-- 'edgeLabel' x y $ overlay ('edge' e x y) ('edge' f x y) == e '<+>' f+-- @+--+-- Furthermore, when applied to transitive graphs, 'overlay' composes edges in+-- sequence using the operator '<.>' with 'one' acting as the identity:+--+-- @+-- 'edgeLabel' x z $ 'transitiveClosure' (overlay ('edge' e x y) ('edge' 'one' y z)) == e+-- 'edgeLabel' x z $ 'transitiveClosure' (overlay ('edge' e x y) ('edge' f y z)) == e '<.>' f+-- @+overlay :: Monoid e => Graph e a -> Graph e a -> Graph e a+overlay = connect zero+ -- | /Connect/ two graphs with edges labelled by a given label. An alias for -- 'Connect'. -- Complexity: /O(1)/ time and memory, /O(s1 + s2)/ size. 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)/.-connectBy :: e -> Graph e a -> Graph e a -> Graph e a-connectBy = Connect+--+-- @+-- 'isEmpty' (connect e x y) == 'isEmpty' x && 'isEmpty' y+-- 'hasVertex' z (connect e x y) == 'hasVertex' z x || 'hasVertex' z y+-- 'Algebra.Graph.ToGraph.vertexCount' (connect e x y) >= 'Algebra.Graph.ToGraph.vertexCount' x+-- 'Algebra.Graph.ToGraph.vertexCount' (connect e x y) <= 'Algebra.Graph.ToGraph.vertexCount' x + 'Algebra.Graph.ToGraph.vertexCount' y+-- 'Algebra.Graph.ToGraph.edgeCount' (connect e x y) <= 'Algebra.Graph.ToGraph.vertexCount' x * 'Algebra.Graph.ToGraph.vertexCount' y + 'Algebra.Graph.ToGraph.edgeCount' x + 'Algebra.Graph.ToGraph.edgeCount' y+-- 'Algebra.Graph.ToGraph.vertexCount' (connect e 1 2) == 2+-- 'Algebra.Graph.ToGraph.edgeCount' (connect e 1 2) == if e == 'zero' then 0 else 1+-- @+connect :: e -> Graph e a -> Graph e a -> Graph e a+connect = Connect --- | The left-hand part of a convenient ternary-ish operator @x -\<e\>- y@ for--- connecting graphs with labelled edges. For example:+-- | Construct the graph comprising a given list of isolated vertices.+-- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the+-- given list. -- -- @--- x = 'vertex' "x"--- y = 'vertex' "y"--- z = x -\<2\>- y+-- vertices [] == 'empty'+-- vertices [x] == 'vertex' x+-- 'hasVertex' x . vertices == 'elem' x+-- 'Algebra.Graph.ToGraph.vertexCount' . vertices == 'length' . 'Data.List.nub'+-- 'Algebra.Graph.ToGraph.vertexSet' . vertices == Set.'Set.fromList' -- @-(-<) :: Graph e a -> e -> (Graph e a, e)-g -< e = (g, e)+vertices :: Monoid e => [a] -> Graph e a+vertices = overlays . map vertex --- | The right-hand part of a convenient ternary-ish operator @x -\<e\>- y@ for--- connecting graphs with labelled edges. For example:+-- | Construct the graph from a list of labelled edges.+-- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the+-- given list. -- -- @--- x = 'vertex' "x"--- y = 'vertex' "y"--- z = x -\<2\>- y+-- edges [] == 'empty'+-- edges [(e,x,y)] == 'edge' e x y+-- edges == 'overlays' . 'map' (\\(e, x, y) -> 'edge' e x y) -- @-(>-) :: (Graph e a, e) -> Graph e a -> Graph e a-(g, e) >- h = Connect e g h+edges :: Monoid e => [(e, a, a)] -> Graph e a+edges = overlays . map (\(e, x, y) -> edge e x y) -infixl 5 -<-infixl 5 >-+-- | Overlay a given list of graphs.+-- Complexity: /O(L)/ time and memory, and /O(S)/ size, where /L/ is the length+-- of the given list, and /S/ is the sum of sizes of the graphs in the list.+--+-- @+-- overlays [] == 'empty'+-- overlays [x] == x+-- overlays [x,y] == 'overlay' x y+-- overlays == 'foldr' 'overlay' 'empty'+-- 'isEmpty' . overlays == 'all' 'isEmpty'+-- @+overlays :: Monoid e => [Graph e a] -> Graph e a+overlays = foldr overlay empty -instance Dioid e => C.Graph (Graph e a) where- type Vertex (Graph e a) = a- empty = Empty- vertex = Vertex- overlay = overlay- connect = connect+-- | Check if a graph is empty. A convenient alias for 'null'.+-- Complexity: /O(s)/ time.+--+-- @+-- isEmpty 'empty' == True+-- isEmpty ('overlay' 'empty' 'empty') == True+-- isEmpty ('vertex' x) == False+-- isEmpty ('removeVertex' x $ 'vertex' x) == True+-- isEmpty ('removeEdge' x y $ 'edge' e x y) == False+-- @+isEmpty :: Graph e a -> Bool+isEmpty = foldg True (const False) (const (&&)) +-- | The /size/ of a graph, i.e. the number of leaves of the expression+-- including 'empty' leaves.+-- Complexity: /O(s)/ time.+--+-- @+-- size 'empty' == 1+-- size ('vertex' x) == 1+-- size ('overlay' x y) == size x + size y+-- size ('connect' x y) == size x + size y+-- size x >= 1+-- size x >= 'Algebra.Graph.ToGraph.vertexCount' x+-- @+size :: Graph e a -> Int+size = foldg 1 (const 1) (const (+))++-- | Check if a graph contains a given vertex.+-- Complexity: /O(s)/ time.+--+-- @+-- hasVertex x 'empty' == False+-- hasVertex x ('vertex' x) == True+-- hasVertex 1 ('vertex' 2) == False+-- hasVertex x . 'removeVertex' x == 'const' False+-- @+hasVertex :: Eq a => a -> Graph e a -> Bool+hasVertex x = foldg False (==x) (const (||))++-- | Check if a graph contains a given edge.+-- Complexity: /O(s)/ time.+--+-- @+-- hasEdge x y 'empty' == False+-- hasEdge x y ('vertex' z) == False+-- hasEdge x y ('edge' e x y) == (e /= 'zero')+-- hasEdge x y . 'removeEdge' x y == 'const' False+-- hasEdge x y == 'not' . 'null' . 'filter' (\\(_,ex,ey) -> ex == x && ey == y) . 'edgeList'+-- @+hasEdge :: (Eq e, Monoid e, Ord a) => a -> a -> Graph e a -> Bool+hasEdge x y = (/= zero) . edgeLabel x y+ -- | Extract the label of a specified edge from a graph.-edgeLabel :: (Eq a, Semilattice e) => a -> a -> Graph e a -> e-edgeLabel _ _ Empty = zero-edgeLabel _ _ (Vertex _) = zero-edgeLabel x y (Connect e g h) = edgeLabel x y g \/ edgeLabel x y h \/ new+edgeLabel :: (Eq a, Monoid e) => a -> a -> Graph e a -> e+edgeLabel s t g = let (res, _, _) = foldg e v c g in res where- new | x `elem` g && y `elem` h = e- | otherwise = zero+ e = (zero , False , False )+ v x = (zero , x == s , x == t )+ c l (l1, s1, t1) (l2, s2, t2) | s1 && t2 = (mconcat [l1, l, l2], s1 || s2, t1 || t2)+ | otherwise = (mconcat [l1, l2], s1 || s2, t1 || t2)++-- | The sorted list of vertices of a given graph.+-- Complexity: /O(s * log(n))/ time and /O(n)/ memory.+--+-- @+-- vertexList 'empty' == []+-- vertexList ('vertex' x) == [x]+-- vertexList . 'vertices' == 'Data.List.nub' . 'Data.List.sort'+-- @+vertexList :: Ord a => Graph e a -> [a]+vertexList = Set.toAscList . vertexSet++-- | The list of edges of a graph, sorted lexicographically with respect to+-- pairs of connected vertices (i.e. edge-labels are ignored when sorting).+-- Complexity: /O(n + m)/ time and /O(m)/ memory.+--+-- @+-- edgeList 'empty' == []+-- edgeList ('vertex' x) == []+-- edgeList ('edge' e x y) == if e == 'zero' then [] else [(e,x,y)]+-- @+edgeList :: (Eq e, Monoid e, Ord a) => Graph e a -> [(e, a, a)]+edgeList = AM.edgeList . toAdjacencyMap++-- | The set of vertices of a given graph.+-- Complexity: /O(s * log(n))/ time and /O(n)/ memory.+--+-- @+-- vertexSet 'empty' == Set.'Set.empty'+-- vertexSet . 'vertex' == Set.'Set.singleton'+-- vertexSet . 'vertices' == Set.'Set.fromList'+-- @+vertexSet :: Ord a => Graph e a -> Set.Set a+vertexSet = foldg Set.empty Set.singleton (const Set.union)++-- | The set of edges of a given graph.+-- Complexity: /O(n + m)/ time and /O(m)/ memory.+--+-- @+-- edgeSet 'empty' == Set.'Set.empty'+-- edgeSet ('vertex' x) == Set.'Set.empty'+-- edgeSet ('edge' e x y) == if e == 'zero' then Set.'Set.empty' else Set.'Set.singleton' (e,x,y)+-- @+edgeSet :: (Eq e, Monoid e, Ord a) => Graph e a -> Set.Set (e, a, a)+edgeSet = Set.fromAscList . edgeList++-- | Remove a vertex from a given graph.+-- Complexity: /O(s)/ time, memory and size.+--+-- @+-- removeVertex x ('vertex' x) == 'empty'+-- removeVertex 1 ('vertex' 2) == 'vertex' 2+-- removeVertex x ('edge' e x x) == 'empty'+-- removeVertex 1 ('edge' e 1 2) == 'vertex' 2+-- removeVertex x . removeVertex x == removeVertex x+-- @+removeVertex :: Eq a => a -> Graph e a -> Graph e a+removeVertex x = induce (/= x)++-- | Remove an edge from a given graph.+-- Complexity: /O(s)/ time, memory and size.+--+-- @+-- removeEdge x y ('edge' e x y) == 'vertices' [x,y]+-- removeEdge x y . removeEdge x y == removeEdge x y+-- 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 :: (Eq a, Eq e, Monoid e) => a -> a -> Graph e a -> Graph e a+removeEdge s t = filterContext s (/=s) (/=t)++-- | The function @'replaceVertex' x y@ replaces vertex @x@ with vertex @y@ in a+-- given 'Graph'. If @y@ already exists, @x@ and @y@ will be merged.+-- Complexity: /O(s)/ time, memory and size.+--+-- @+-- replaceVertex x x == id+-- replaceVertex x y ('vertex' x) == 'vertex' y+-- replaceVertex x y == 'fmap' (\\v -> if v == x then y else v)+-- @+replaceVertex :: Eq a => a -> a -> Graph e a -> Graph e a+replaceVertex u v = fmap $ \w -> if w == u then v else w++-- | Replace an edge from a given graph. If it doesn't exist, it will be created.+-- Complexity: /O(log(n))/ time.+--+-- @+-- replaceEdge e x y z == 'overlay' (removeEdge x y z) ('edge' e x y)+-- replaceEdge e x y ('edge' f x y) == 'edge' e x y+-- 'edgeLabel' x y (replaceEdge e x y z) == e+-- @+replaceEdge :: (Eq e, Monoid e, Ord a) => e -> a -> a -> Graph e a -> Graph e a+replaceEdge e x y = overlay (edge e x y) . removeEdge x y++-- | Transpose a given graph.+-- Complexity: /O(s)/ time, memory and size.+--+-- @+-- transpose 'empty' == 'empty'+-- transpose ('vertex' x) == 'vertex' x+-- transpose ('edge' e x y) == 'edge' e y x+-- transpose . transpose == id+-- @+transpose :: Graph e a -> Graph e a+transpose = foldg empty vertex (fmap flip connect)++-- | Transform a graph by applying a function to each of its edge labels.+-- Complexity: /O(s)/ time, memory and size.+--+-- The function @h@ is required to be a /homomorphism/ on the underlying type of+-- labels @e@. At the very least it must preserve 'zero' and '<+>':+--+-- @+-- h 'zero' == 'zero'+-- h x '<+>' h y == h (x '<+>' y)+-- @+--+-- If @e@ is also a semiring, then @h@ must also preserve the multiplicative+-- structure:+--+-- @+-- h 'one' == 'one'+-- h x '<.>' h y == h (x '<.>' y)+-- @+--+-- If the above requirements hold, then the implementation provides the+-- following guarantees.+--+-- @+-- emap h 'empty' == 'empty'+-- emap h ('vertex' x) == 'vertex' x+-- emap h ('edge' e x y) == 'edge' (h e) x y+-- emap h ('overlay' x y) == 'overlay' (emap h x) (emap h y)+-- emap h ('connect' e x y) == 'connect' (h e) (emap h x) (emap h y)+-- emap 'id' == 'id'+-- emap g . emap h == emap (g . h)+-- @+emap :: (e -> f) -> Graph e a -> Graph f a+emap f = foldg Empty Vertex (Connect . f)++-- | Construct the /induced subgraph/ of a given graph by removing the+-- vertices that do not satisfy a given predicate.+-- Complexity: /O(s)/ time, memory and size, 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) -> Graph e a -> Graph e a+induce p = foldg Empty (\x -> if p x then Vertex x else Empty) c+ where+ c _ x Empty = x -- Constant folding to get rid of Empty leaves+ c _ Empty y = y+ c e x y = Connect e x y++-- | Compute the /reflexive and transitive closure/ of a graph over the+-- underlying star semiring using the Warshall-Floyd-Kleene algorithm.+--+-- @+-- closure 'empty' == 'empty'+-- closure ('vertex' x) == 'edge' 'one' x x+-- closure ('edge' e x x) == 'edge' 'one' x x+-- closure ('edge' e x y) == 'edges' [('one',x,x), (e,x,y), ('one',y,y)]+-- closure == 'reflexiveClosure' . 'transitiveClosure'+-- closure == 'transitiveClosure' . 'reflexiveClosure'+-- closure . closure == closure+-- 'Algebra.Graph.ToGraph.postSet' x (closure y) == Set.'Set.fromList' ('Algebra.Graph.ToGraph.reachable' x y)+-- @+closure :: (Eq e, Ord a, StarSemiring e) => Graph e a -> Graph e a+closure = fromAdjacencyMap . AM.closure . toAdjacencyMap++-- | Compute the /reflexive closure/ of a graph over the underlying semiring by+-- adding a self-loop of weight 'one' to every vertex.+-- Complexity: /O(n * log(n))/ time.+--+-- @+-- reflexiveClosure 'empty' == 'empty'+-- reflexiveClosure ('vertex' x) == 'edge' 'one' x x+-- reflexiveClosure ('edge' e x x) == 'edge' 'one' x x+-- reflexiveClosure ('edge' e x y) == 'edges' [('one',x,x), (e,x,y), ('one',y,y)]+-- reflexiveClosure . reflexiveClosure == reflexiveClosure+-- @+reflexiveClosure :: (Ord a, Semiring e) => Graph e a -> Graph e a+reflexiveClosure x = overlay x $ edges [ (one, v, v) | v <- vertexList x ]++-- | Compute the /symmetric closure/ of a graph by overlaying it with its own+-- transpose.+-- Complexity: /O((n + m) * log(n))/ time.+--+-- @+-- symmetricClosure 'empty' == 'empty'+-- symmetricClosure ('vertex' x) == 'vertex' x+-- symmetricClosure ('edge' e x y) == 'edges' [(e,x,y), (e,y,x)]+-- symmetricClosure x == 'overlay' x ('transpose' x)+-- symmetricClosure . symmetricClosure == symmetricClosure+-- @+symmetricClosure :: Monoid e => Graph e a -> Graph e a+symmetricClosure m = overlay m (transpose m)++-- | Compute the /transitive closure/ of a graph over the underlying star+-- semiring using a modified version of the Warshall-Floyd-Kleene algorithm,+-- which omits the reflexivity step.+--+-- @+-- transitiveClosure 'empty' == 'empty'+-- transitiveClosure ('vertex' x) == 'vertex' x+-- transitiveClosure ('edge' e x y) == 'edge' e x y+-- transitiveClosure . transitiveClosure == transitiveClosure+-- @+transitiveClosure :: (Eq e, Ord a, StarSemiring e) => Graph e a -> Graph e a+transitiveClosure = fromAdjacencyMap . AM.transitiveClosure . toAdjacencyMap++-- | A type synonym for /unlabelled graphs/.+type UnlabelledGraph a = Graph Any a++-- | A type synonym for /automata/ or /labelled transition systems/.+type Automaton a s = Graph (RegularExpression a) s++-- | A /network/ is a graph whose edges are labelled with distances.+type Network e a = Graph (Distance e) a++-- Filter vertices in a subgraph context.+filterContext :: (Eq a, Eq e, Monoid e) => a -> (a -> Bool) -> (a -> Bool) -> Graph e a -> Graph e a+filterContext s i o g = maybe g go $ context (==s) g+ where+ go (Context is os) = overlays [ vertex s+ , induce (/=s) g+ , edges [ (e, v, s) | (e, v) <- is, i v ]+ , edges [ (e, s, v) | (e, v) <- os, o v ] ]++-- The /focus/ of a graph expression is a flattened represenentation of the+-- subgraph under focus, its context, as well as the list of all encountered+-- vertices. See 'removeEdge' for a use-case example.+data Focus e a = Focus+ { ok :: Bool -- ^ True if focus on the specified subgraph is obtained.+ , is :: List (e, a) -- ^ Inputs into the focused subgraph.+ , os :: List (e, a) -- ^ Outputs out of the focused subgraph.+ , vs :: List a } -- ^ All vertices (leaves) of the graph expression.++-- Focus on the 'empty' graph.+emptyFocus :: Focus e a+emptyFocus = Focus False mempty mempty mempty++-- | Focus on the graph with a single vertex, given a predicate indicating+-- whether the vertex is of interest.+vertexFocus :: (a -> Bool) -> a -> Focus e a+vertexFocus f x = Focus (f x) mempty mempty (pure x)++-- | Connect two foci.+connectFoci :: (Eq e, Monoid e) => e -> Focus e a -> Focus e a -> Focus e a+connectFoci e x y+ | e == mempty = Focus (ok x || ok y) (is x <> is y) (os x <> os y) (vs x <> vs y)+ | otherwise = Focus (ok x || ok y) (xs <> is y) (os x <> ys ) (vs x <> vs y)+ where+ xs = if ok y then fmap (e,) (vs x) else is x+ ys = if ok x then fmap (e,) (vs y) else os y++-- | 'Focus' on a specified subgraph.+focus :: (Eq e, Monoid e) => (a -> Bool) -> Graph e a -> Focus e a+focus f = foldg emptyFocus (vertexFocus f) connectFoci++-- | The 'Context' of a subgraph comprises its 'inputs' and 'outputs', i.e. all+-- the vertices that are connected to the subgraph's vertices (along with the+-- corresponding edge labels). Note that inputs and outputs can belong to the+-- subgraph itself. In general, there are no guarantees on the order of vertices+-- in 'inputs' and 'outputs'; furthermore, there may be repetitions.+data Context e a = Context { inputs :: [(e, a)], outputs :: [(e, a)] }+ deriving (Eq, Show)++-- | Extract the 'Context' of a subgraph specified by a given predicate. Returns+-- @Nothing@ if the specified subgraph is empty.+--+-- @+-- context ('const' False) x == Nothing+-- context (== 1) ('edge' e 1 2) == if e == 'zero' then Just ('Context' [] []) else Just ('Context' [ ] [(e,2)])+-- context (== 2) ('edge' e 1 2) == if e == 'zero' then Just ('Context' [] []) else Just ('Context' [(e,1)] [ ])+-- context ('const' True ) ('edge' e 1 2) == if e == 'zero' then Just ('Context' [] []) else Just ('Context' [(e,1)] [(e,2)])+-- context (== 4) (3 * 1 * 4 * 1 * 5) == Just ('Context' [('one',3), ('one',1)] [('one',1), ('one',5)])+-- @+context :: (Eq e, Monoid e) => (a -> Bool) -> Graph e a -> Maybe (Context e a)+context p g | ok f = Just $ Context (Exts.toList $ is f) (Exts.toList $ os f)+ | otherwise = Nothing+ where+ f = focus p g
+ src/Algebra/Graph/Labelled/AdjacencyMap.hs view
@@ -0,0 +1,612 @@+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.Labelled.AdjacencyMap+-- Copyright : (c) Andrey Mokhov 2016-2018+-- License : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability : experimental+--+-- __Alga__ is a library for algebraic construction and manipulation of graphs+-- in Haskell. See <https://github.com/snowleopard/alga-paper this paper> for the+-- motivation behind the library, the underlying theory, and implementation details.+--+-- This module defines the 'AdjacencyMap' data type for edge-labelled graphs, as+-- well as associated operations and algorithms. 'AdjacencyMap' is an instance+-- of the 'C.Graph' type class, which can be used for polymorphic graph+-- construction and manipulation.+-----------------------------------------------------------------------------+module Algebra.Graph.Labelled.AdjacencyMap (+ -- * Data structure+ AdjacencyMap, adjacencyMap,++ -- * Basic graph construction primitives+ empty, vertex, edge, (-<), (>-), overlay, connect, vertices, edges,+ overlays, fromAdjacencyMaps,++ -- * Relations on graphs+ isSubgraphOf,++ -- * Graph properties+ isEmpty, hasVertex, hasEdge, edgeLabel, vertexCount, edgeCount, vertexList,+ edgeList, vertexSet, edgeSet, preSet, postSet, skeleton,++ -- * Graph transformation+ removeVertex, removeEdge, replaceVertex, replaceEdge, transpose, gmap,+ emap, induce,++ -- * Relational operations+ closure, reflexiveClosure, symmetricClosure, transitiveClosure+ ) where++import Prelude ()+import Prelude.Compat++import Data.Foldable (foldMap)+import Data.Maybe+import Data.Map (Map)+import Data.Monoid (Monoid, Sum (..))+import Data.Set (Set)++import Algebra.Graph.Label+import Algebra.Graph.Labelled.AdjacencyMap.Internal++import qualified Algebra.Graph.AdjacencyMap as AM+import qualified Algebra.Graph.AdjacencyMap.Internal as AMI+import qualified Data.Map.Strict as Map+import qualified Data.Set as Set++-- | Construct the /empty graph/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'isEmpty' empty == True+-- 'hasVertex' x empty == False+-- 'vertexCount' empty == 0+-- 'edgeCount' empty == 0+-- @+empty :: AdjacencyMap e a+empty = AM Map.empty++-- | Construct the graph comprising /a single isolated vertex/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'isEmpty' (vertex x) == False+-- 'hasVertex' x (vertex x) == True+-- 'vertexCount' (vertex x) == 1+-- 'edgeCount' (vertex x) == 0+-- @+vertex :: a -> AdjacencyMap e a+vertex x = AM $ Map.singleton x Map.empty++-- | Construct the graph comprising /a single edge/.+-- Complexity: /O(1)/ time, memory.+--+-- @+-- edge e x y == 'connect' e ('vertex' x) ('vertex' y)+-- edge 'zero' x y == 'vertices' [x,y]+-- 'hasEdge' x y (edge e x y) == (e /= 'zero')+-- 'edgeLabel' x y (edge e x y) == e+-- 'edgeCount' (edge e x y) == if e == 'zero' then 0 else 1+-- 'vertexCount' (edge e 1 1) == 1+-- 'vertexCount' (edge e 1 2) == 2+-- @+edge :: (Eq e, Monoid e, Ord a) => e -> a -> a -> AdjacencyMap e a+edge e x y | e == zero = vertices [x, y]+ | x == y = AM $ Map.singleton x (Map.singleton x e)+ | otherwise = AM $ Map.fromList [(x, Map.singleton y e), (y, Map.empty)]++-- | The left-hand part of a convenient ternary-ish operator @x-\<e\>-y@ for+-- creating labelled edges.+--+-- @+-- x -\<e\>- y == 'edge' e x y+-- @+(-<) :: a -> e -> (a, e)+g -< e = (g, e)++-- | The right-hand part of a convenient ternary-ish operator @x-\<e\>-y@ for+-- creating labelled edges.+--+-- @+-- x -\<e\>- y == 'edge' e x y+-- @+(>-) :: (Eq e, Monoid e, Ord a) => (a, e) -> a -> AdjacencyMap e a+(x, e) >- y = edge e x y++infixl 5 -<+infixl 5 >-++-- | /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.+--+-- @+-- 'isEmpty' (overlay x y) == 'isEmpty' x && 'isEmpty' y+-- 'hasVertex' z (overlay x y) == 'hasVertex' z x || 'hasVertex' z y+-- 'vertexCount' (overlay x y) >= 'vertexCount' x+-- 'vertexCount' (overlay x y) <= 'vertexCount' x + 'vertexCount' y+-- 'edgeCount' (overlay x y) >= 'edgeCount' x+-- 'edgeCount' (overlay x y) <= 'edgeCount' x + 'edgeCount' y+-- 'vertexCount' (overlay 1 2) == 2+-- 'edgeCount' (overlay 1 2) == 0+-- @+--+-- Note: 'overlay' composes edges in parallel using the operator '<+>' with+-- 'zero' acting as the identity:+--+-- @+-- 'edgeLabel' x y $ overlay ('edge' e x y) ('edge' 'zero' x y) == e+-- 'edgeLabel' x y $ overlay ('edge' e x y) ('edge' f x y) == e '<+>' f+-- @+--+-- Furthermore, when applied to transitive graphs, 'overlay' composes edges in+-- sequence using the operator '<.>' with 'one' acting as the identity:+--+-- @+-- 'edgeLabel' x z $ 'transitiveClosure' (overlay ('edge' e x y) ('edge' 'one' y z)) == e+-- 'edgeLabel' x z $ 'transitiveClosure' (overlay ('edge' e x y) ('edge' f y z)) == e '<.>' f+-- @+overlay :: (Eq e, Monoid e, Ord a) => AdjacencyMap e a -> AdjacencyMap e a -> AdjacencyMap e a+overlay (AM x) (AM y) = AM $ Map.unionWith nonZeroUnion x y++-- Union maps, removing zero elements from the result.+nonZeroUnion :: (Eq e, Monoid e, Ord a) => Map a e -> Map a e -> Map a e+nonZeroUnion x y = Map.filter (/= zero) $ Map.unionWith mappend x y++-- Drop all edges with zero labels.+trimZeroes :: (Eq e, Monoid e) => Map a (Map a e) -> Map a (Map a e)+trimZeroes = Map.map (Map.filter (/= zero))++-- | /Connect/ two graphs with edges labelled by a given label. When applied to+-- the same labels, this is an 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)/.+--+-- @+-- 'isEmpty' (connect e x y) == 'isEmpty' x && 'isEmpty' y+-- 'hasVertex' z (connect e x y) == 'hasVertex' z x || 'hasVertex' z y+-- 'vertexCount' (connect e x y) >= 'vertexCount' x+-- 'vertexCount' (connect e x y) <= 'vertexCount' x + 'vertexCount' y+-- 'edgeCount' (connect e x y) <= 'vertexCount' x * 'vertexCount' y + 'edgeCount' x + 'edgeCount' y+-- 'vertexCount' (connect e 1 2) == 2+-- 'edgeCount' (connect e 1 2) == if e == 'zero' then 0 else 1+-- @+connect :: (Eq e, Monoid e, Ord a) => e -> AdjacencyMap e a -> AdjacencyMap e a -> AdjacencyMap e a+connect e (AM x) (AM y)+ | e == mempty = overlay (AM x) (AM y)+ | otherwise = AM $ Map.unionsWith nonZeroUnion $ x : y :+ [ Map.fromSet (const targets) (Map.keysSet x) ]+ where+ targets = Map.fromSet (const e) (Map.keysSet y)++-- | 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] -> AdjacencyMap e a+vertices = AM . Map.fromList . map (, Map.empty)++-- | Construct the graph from a list of edges.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- edges [] == 'empty'+-- edges [(e,x,y)] == 'edge' e x y+-- edges == 'overlays' . 'map' (\\(e, x, y) -> 'edge' e x y)+-- @+edges :: (Eq e, Monoid e, Ord a) => [(e, a, a)] -> AdjacencyMap e a+edges es = fromAdjacencyMaps [ (x, Map.singleton y e) | (e, x, y) <- 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 :: (Eq e, Monoid e, Ord a) => [AdjacencyMap e a] -> AdjacencyMap e a+overlays = AM . Map.unionsWith nonZeroUnion . map adjacencyMap++-- | Construct a graph from a list of adjacency sets.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- fromAdjacencyMaps [] == 'empty'+-- fromAdjacencyMaps [(x, Map.'Map.empty')] == 'vertex' x+-- fromAdjacencyMaps [(x, Map.'Map.singleton' y e)] == if e == 'zero' then 'vertices' [x,y] else 'edge' e x y+-- 'overlay' (fromAdjacencyMaps xs) (fromAdjacencyMaps ys) == fromAdjacencyMaps (xs '++' ys)+-- @+fromAdjacencyMaps :: (Eq e, Monoid e, Ord a) => [(a, Map a e)] -> AdjacencyMap e a+fromAdjacencyMaps xs = AM $ trimZeroes $ Map.unionWith mappend vs es+ where+ vs = Map.fromSet (const Map.empty) . Set.unions $ map (Map.keysSet . snd) xs+ es = Map.fromListWith (Map.unionWith mappend) xs++-- | The 'isSubgraphOf' function takes two graphs and returns 'True' if the+-- first graph is a /subgraph/ of the second.+-- Complexity: /O(s + m * log(m))/ time. Note that the number of edges /m/ of a+-- graph can be quadratic with respect to the expression size /s/.+--+-- @+-- isSubgraphOf 'empty' x == True+-- isSubgraphOf ('vertex' x) 'empty' == False+-- isSubgraphOf x y ==> x <= y+-- @+isSubgraphOf :: (Eq e, Monoid e, Ord a) => AdjacencyMap e a -> AdjacencyMap e a -> Bool+isSubgraphOf (AM x) (AM y) = Map.isSubmapOfBy (Map.isSubmapOfBy le) x y+ where+ le x y = mappend x y == y++-- | Check if a graph 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' e x y) == False+-- @+isEmpty :: AdjacencyMap e a -> Bool+isEmpty = Map.null . adjacencyMap++-- | 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 -> AdjacencyMap e a -> Bool+hasVertex x = Map.member x . adjacencyMap++-- | 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' e x y) == (e /= 'zero')+-- hasEdge x y . 'removeEdge' x y == 'const' False+-- hasEdge x y == 'not' . 'null' . 'filter' (\\(_,ex,ey) -> ex == x && ey == y) . 'edgeList'+-- @+hasEdge :: Ord a => a -> a -> AdjacencyMap e a -> Bool+hasEdge x y (AM m) = fromMaybe False (Map.member y <$> Map.lookup x m)++-- | Extract the label of a specified edge in a graph.+-- Complexity: /O(log(n))/ time.+--+-- @+-- edgeLabel x y 'empty' == 'zero'+-- edgeLabel x y ('vertex' z) == 'zero'+-- edgeLabel x y ('edge' e x y) == e+-- edgeLabel s t ('overlay' x y) == edgeLabel s t x <+> edgeLabel s t y+-- @+edgeLabel :: (Monoid e, Ord a) => a -> a -> AdjacencyMap e a -> e+edgeLabel x y (AM m) = fromMaybe zero (Map.lookup x m >>= Map.lookup y)++-- | 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 :: AdjacencyMap e a -> Int+vertexCount = Map.size . adjacencyMap++-- | The number of (non-'zero') edges in a graph.+-- Complexity: /O(n)/ time.+--+-- @+-- edgeCount 'empty' == 0+-- edgeCount ('vertex' x) == 0+-- edgeCount ('edge' e x y) == if e == 'zero' then 0 else 1+-- edgeCount == 'length' . 'edgeList'+-- @+edgeCount :: AdjacencyMap e a -> Int+edgeCount = getSum . foldMap (Sum . Map.size) . adjacencyMap++-- | 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 :: AdjacencyMap e a -> [a]+vertexList = Map.keys . adjacencyMap++-- | The list of edges of a graph, sorted lexicographically with respect to+-- pairs of connected vertices (i.e. edge-labels are ignored when sorting).+-- Complexity: /O(n + m)/ time and /O(m)/ memory.+--+-- @+-- edgeList 'empty' == []+-- edgeList ('vertex' x) == []+-- edgeList ('edge' e x y) == if e == 'zero' then [] else [(e,x,y)]+-- @+edgeList :: AdjacencyMap e a -> [(e, a, a)]+edgeList (AM m) =+ [ (e, x, y) | (x, ys) <- Map.toAscList m, (y, e) <- Map.toAscList ys ]++-- | The set of vertices of a given graph.+-- Complexity: /O(n)/ time and memory.+--+-- @+-- vertexSet 'empty' == Set.'Set.empty'+-- vertexSet . 'vertex' == Set.'Set.singleton'+-- vertexSet . 'vertices' == Set.'Set.fromList'+-- @+vertexSet :: AdjacencyMap e a -> Set a+vertexSet = Map.keysSet . adjacencyMap++-- | The set of edges of a given graph.+-- Complexity: /O(n + m)/ time and /O(m)/ memory.+--+-- @+-- edgeSet 'empty' == Set.'Set.empty'+-- edgeSet ('vertex' x) == Set.'Set.empty'+-- edgeSet ('edge' e x y) == if e == 'zero' then Set.'Set.empty' else Set.'Set.singleton' (e,x,y)+-- @+edgeSet :: (Eq a, Eq e) => AdjacencyMap e a -> Set (e, a, a)+edgeSet = Set.fromAscList . edgeList++-- | The /preset/ of an element @x@ is the set of its /direct predecessors/.+-- Complexity: /O(n * log(n))/ time and /O(n)/ memory.+--+-- @+-- preSet x 'empty' == Set.'Set.empty'+-- preSet x ('vertex' x) == Set.'Set.empty'+-- preSet 1 ('edge' e 1 2) == Set.'Set.empty'+-- preSet y ('edge' e x y) == if e == 'zero' then Set.'Set.empty' else Set.'Set.fromList' [x]+-- @+preSet :: Ord a => a -> AdjacencyMap e a -> Set a+preSet x (AM m) = Set.fromAscList+ [ a | (a, es) <- Map.toAscList m, Map.member x es ]++-- | The /postset/ of a vertex is the set of its /direct successors/.+-- Complexity: /O(log(n))/ time and /O(1)/ memory.+--+-- @+-- postSet x 'empty' == Set.'Set.empty'+-- postSet x ('vertex' x) == Set.'Set.empty'+-- postSet x ('edge' e x y) == if e == 'zero' then Set.'Set.empty' else Set.'Set.fromList' [y]+-- postSet 2 ('edge' e 1 2) == Set.'Set.empty'+-- @+postSet :: Ord a => a -> AdjacencyMap e a -> Set a+postSet x = Map.keysSet . Map.findWithDefault Map.empty x . adjacencyMap++-- | Convert a graph to the corresponding unlabelled 'AM.AdjacencyMap' by+-- forgetting labels on all non-'zero' edges.+--+-- @+-- 'hasEdge' x y == 'AM.hasEdge' x y . skeleton+-- @+skeleton :: AdjacencyMap e a -> AM.AdjacencyMap a+skeleton (AM m) = AMI.AM (Map.map Map.keysSet m)++-- | Remove a vertex from a given graph.+-- Complexity: /O(n*log(n))/ time.+--+-- @+-- removeVertex x ('vertex' x) == 'empty'+-- removeVertex 1 ('vertex' 2) == 'vertex' 2+-- removeVertex x ('edge' e x x) == 'empty'+-- removeVertex 1 ('edge' e 1 2) == 'vertex' 2+-- removeVertex x . removeVertex x == removeVertex x+-- @+removeVertex :: Ord a => a -> AdjacencyMap e a -> AdjacencyMap e a+removeVertex x = AM . Map.map (Map.delete x) . Map.delete x . adjacencyMap++-- | Remove an edge from a given graph.+-- Complexity: /O(log(n))/ time.+--+-- @+-- removeEdge x y ('edge' e x y) == 'vertices' [x,y]+-- removeEdge x y . removeEdge x y == removeEdge x y+-- 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 -> AdjacencyMap e a -> AdjacencyMap e a+removeEdge x y = AM . Map.adjust (Map.delete y) x . adjacencyMap++-- | The function @'replaceVertex' x y@ replaces vertex @x@ with vertex @y@ in a+-- given 'AdjacencyMap'. 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 == 'gmap' (\\v -> if v == x then y else v)+-- @+replaceVertex :: (Eq e, Monoid e, Ord a) => a -> a -> AdjacencyMap e a -> AdjacencyMap e a+replaceVertex u v = gmap $ \w -> if w == u then v else w++-- | Replace an edge from a given graph. If it doesn't exist, it will be created.+-- Complexity: /O(log(n))/ time.+--+-- @+-- replaceEdge e x y z == 'overlay' (removeEdge x y z) ('edge' e x y)+-- replaceEdge e x y ('edge' f x y) == 'edge' e x y+-- 'edgeLabel' x y (replaceEdge e x y z) == e+-- @+replaceEdge :: (Eq e, Monoid e, Ord a) => e -> a -> a -> AdjacencyMap e a -> AdjacencyMap e a+replaceEdge e x y+ | e == zero = AM . addY . Map.alter (Just . maybe Map.empty (Map.delete y)) x . adjacencyMap+ | otherwise = AM . addY . Map.alter replace x . adjacencyMap+ where+ addY = Map.alter (Just . fromMaybe Map.empty) y+ replace (Just m) = Just $ Map.insert y e m+ replace Nothing = Just $ Map.singleton y e++-- | Transpose a given graph.+-- Complexity: /O(m * log(n))/ time, /O(n + m)/ memory.+--+-- @+-- transpose 'empty' == 'empty'+-- transpose ('vertex' x) == 'vertex' x+-- transpose ('edge' e x y) == 'edge' e y x+-- transpose . transpose == id+-- @+transpose :: (Monoid e, Ord a) => AdjacencyMap e a -> AdjacencyMap e a+transpose (AM m) = AM $ Map.foldrWithKey combine vs m+ where+ -- No need to use @nonZeroUnion@ here, since we do not add any new edges+ combine v es = Map.unionWith (Map.unionWith mappend) $+ Map.fromAscList [ (u, Map.singleton v e) | (u, e) <- Map.toAscList es ]+ vs = Map.fromSet (const Map.empty) (Map.keysSet m)++-- | 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+-- 'AdjacencyMap'.+-- Complexity: /O((n + m) * log(n))/ time.+--+-- @+-- gmap f 'empty' == 'empty'+-- gmap f ('vertex' x) == 'vertex' (f x)+-- gmap f ('edge' e x y) == 'edge' e (f x) (f y)+-- gmap 'id' == 'id'+-- gmap f . gmap g == gmap (f . g)+-- @+gmap :: (Eq e, Monoid e, Ord a, Ord b) => (a -> b) -> AdjacencyMap e a -> AdjacencyMap e b+gmap f = AM . trimZeroes . Map.map (Map.mapKeysWith mappend f) .+ Map.mapKeysWith (Map.unionWith mappend) f . adjacencyMap++-- | Transform a graph by applying a function @h@ to each of its edge labels.+-- Complexity: /O((n + m) * log(n))/ time.+--+-- The function @h@ is required to be a /homomorphism/ on the underlying type of+-- labels @e@. At the very least it must preserve 'zero' and '<+>':+--+-- @+-- h 'zero' == 'zero'+-- h x '<+>' h y == h (x '<+>' y)+-- @+--+-- If @e@ is also a semiring, then @h@ must also preserve the multiplicative+-- structure:+--+-- @+-- h 'one' == 'one'+-- h x '<.>' h y == h (x '<.>' y)+-- @+--+-- If the above requirements hold, then the implementation provides the+-- following guarantees.+--+-- @+-- emap h 'empty' == 'empty'+-- emap h ('vertex' x) == 'vertex' x+-- emap h ('edge' e x y) == 'edge' (h e) x y+-- emap h ('overlay' x y) == 'overlay' (emap h x) (emap h y)+-- emap h ('connect' e x y) == 'connect' (h e) (emap h x) (emap h y)+-- emap 'id' == 'id'+-- emap g . emap h == emap (g . h)+-- @+emap :: (Eq f, Monoid f) => (e -> f) -> AdjacencyMap e a -> AdjacencyMap f a+emap h = AM . trimZeroes . Map.map (Map.map h) . adjacencyMap++-- | 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) -> AdjacencyMap e a -> AdjacencyMap e a+induce p = AM . Map.map (Map.filterWithKey (\k _ -> p k)) .+ Map.filterWithKey (\k _ -> p k) . adjacencyMap++-- | Compute the /reflexive and transitive closure/ of a graph over the+-- underlying star semiring using the Warshall-Floyd-Kleene algorithm.+--+-- @+-- closure 'empty' == 'empty'+-- closure ('vertex' x) == 'edge' 'one' x x+-- closure ('edge' e x x) == 'edge' 'one' x x+-- closure ('edge' e x y) == 'edges' [('one',x,x), (e,x,y), ('one',y,y)]+-- closure == 'reflexiveClosure' . 'transitiveClosure'+-- closure == 'transitiveClosure' . 'reflexiveClosure'+-- closure . closure == closure+-- 'postSet' x (closure y) == Set.'Set.fromList' ('Algebra.Graph.ToGraph.reachable' x y)+-- @+closure :: (Eq e, Ord a, StarSemiring e) => AdjacencyMap e a -> AdjacencyMap e a+closure = goWarshallFloydKleene . reflexiveClosure++-- | Compute the /reflexive closure/ of a graph over the underlying semiring by+-- adding a self-loop of weight 'one' to every vertex.+-- Complexity: /O(n * log(n))/ time.+--+-- @+-- reflexiveClosure 'empty' == 'empty'+-- reflexiveClosure ('vertex' x) == 'edge' 'one' x x+-- reflexiveClosure ('edge' e x x) == 'edge' 'one' x x+-- reflexiveClosure ('edge' e x y) == 'edges' [('one',x,x), (e,x,y), ('one',y,y)]+-- reflexiveClosure . reflexiveClosure == reflexiveClosure+-- @+reflexiveClosure :: (Ord a, Semiring e) => AdjacencyMap e a -> AdjacencyMap e a+reflexiveClosure (AM m) = AM $ Map.mapWithKey (\k -> Map.insertWith (<+>) k one) m++-- | Compute the /symmetric closure/ of a graph by overlaying it with its own+-- transpose.+-- Complexity: /O((n + m) * log(n))/ time.+--+-- @+-- symmetricClosure 'empty' == 'empty'+-- symmetricClosure ('vertex' x) == 'vertex' x+-- symmetricClosure ('edge' e x y) == 'edges' [(e,x,y), (e,y,x)]+-- symmetricClosure x == 'overlay' x ('transpose' x)+-- symmetricClosure . symmetricClosure == symmetricClosure+-- @+symmetricClosure :: (Eq e, Monoid e, Ord a) => AdjacencyMap e a -> AdjacencyMap e a+symmetricClosure m = overlay m (transpose m)++-- | Compute the /transitive closure/ of a graph over the underlying star+-- semiring using a modified version of the Warshall-Floyd-Kleene algorithm,+-- which omits the reflexivity step.+--+-- @+-- transitiveClosure 'empty' == 'empty'+-- transitiveClosure ('vertex' x) == 'vertex' x+-- transitiveClosure ('edge' e x y) == 'edge' e x y+-- transitiveClosure . transitiveClosure == transitiveClosure+-- @+transitiveClosure :: (Eq e, Ord a, StarSemiring e) => AdjacencyMap e a -> AdjacencyMap e a+transitiveClosure = goWarshallFloydKleene++-- The iterative part of the Warshall-Floyd-Kleene algorithm+goWarshallFloydKleene :: (Eq e, Ord a, StarSemiring e) => AdjacencyMap e a -> AdjacencyMap e a+goWarshallFloydKleene (AM m) = AM $ foldr update m vs+ where+ vs = Set.toAscList (Map.keysSet m)+ update k cur = Map.fromAscList [ (i, go i (get i k <.> starkk)) | i <- vs ]+ where+ get i j = edgeLabel i j (AM cur)+ starkk = star (get k k)+ go i ik = Map.fromAscList+ [ (j, e) | j <- vs, let e = get i j <+> ik <.> get k j, e /= zero ]
+ src/Algebra/Graph/Labelled/AdjacencyMap/Internal.hs view
@@ -0,0 +1,113 @@+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.Labelled.AdjdacencyMap.Internal+-- Copyright : (c) Andrey Mokhov 2016-2018+-- License : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability : unstable+--+-- This module exposes the implementation of edge-labelled adjacency maps. The+-- API is unstable and unsafe, and is exposed only for documentation. You should+-- use the non-internal module "Algebra.Graph.Labelled.AdjdacencyMap" instead.+-----------------------------------------------------------------------------+module Algebra.Graph.Labelled.AdjacencyMap.Internal (+ -- * Labelled adjacency map implementation+ AdjacencyMap (..), consistent+ ) where++import Prelude ()+import Prelude.Compat++import Control.DeepSeq+import Data.Map.Strict (Map)+import Data.Monoid (Monoid, getSum, Sum (..))+import Data.Set (Set, (\\))++import qualified Data.Map.Strict as Map+import qualified Data.Set as Set++import Algebra.Graph.Label++-- | Edge-labelled graphs, where the type variable @e@ stands for edge labels.+-- For example, 'AdjacencyMap' @Bool@ @a@ is isomorphic to unlabelled graphs+-- defined in the top-level module "Algebra.Graph.AdjacencyMap", where @False@+-- and @True@ denote the lack of and the existence of an unlabelled edge,+-- respectively.+newtype AdjacencyMap e a = AM {+ -- | 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)++instance (Ord a, Show a, Ord e, Show e) => Show (AdjacencyMap e a) where+ showsPrec p (AM m)+ | Set.null vs = showString "empty"+ | null es = showParen (p > 10) $ vshow vs+ | vs == used = showParen (p > 10) $ eshow es+ | otherwise = showParen (p > 10) $+ showString "overlay (" . vshow (vs \\ used) .+ showString ") (" . eshow es . showString ")"+ where+ vs = Map.keysSet m+ es = internalEdgeList m+ used = referredToVertexSet m+ vshow vs = case Set.toAscList vs of+ [x] -> showString "vertex " . showsPrec 11 x+ xs -> showString "vertices " . showsPrec 11 xs+ eshow es = case es of+ [(e, x, y)] -> showString "edge " . showsPrec 11 e .+ showString " " . showsPrec 11 x .+ showString " " . showsPrec 11 y+ xs -> showString "edges " . showsPrec 11 xs++instance (Ord e, Monoid e, Ord a) => Ord (AdjacencyMap e a) where+ compare (AM x) (AM y) = mconcat+ [ compare (vNum x) (vNum y)+ , compare (vSet x) (vSet y)+ , compare (eNum x) (eNum y)+ , compare (eSet x) (eSet y)+ , cmp ]+ where+ vNum = Map.size+ vSet = Map.keysSet+ eNum = getSum . foldMap (Sum . Map.size)+ eSet m = [ (x, y) | (x, ys) <- Map.toAscList m, (y, _) <- Map.toAscList ys ]+ cmp | x == y = EQ+ | overlays [x, y] == y = LT+ | otherwise = compare x y++-- Overlay a list of adjacency maps.+overlays :: (Eq e, Monoid e, Ord a) => [Map a (Map a e)] -> Map a (Map a e)+overlays = Map.unionsWith (\x -> Map.filter (/= zero) . Map.unionWith mappend x)++-- | __Note:__ this does not satisfy the usual ring laws; see 'AdjacencyMap'+-- for more details.+instance (Eq e, Dioid e, Num a, Ord a) => Num (AdjacencyMap e a) where+ fromInteger x = AM $ Map.singleton (fromInteger x) Map.empty+ AM x + AM y = AM $ overlays [x, y]+ AM x * AM y = AM $ overlays $ x : y :+ [ Map.fromSet (const targets) (Map.keysSet x) ]+ where+ targets = Map.fromSet (const one) (Map.keysSet y)+ signum = const (AM Map.empty)+ abs = id+ negate = id++-- | Check if the internal graph representation is consistent, i.e. that all+-- edges refer to existing vertices, and there are no 'zero'-labelled edges. It+-- should be impossible to create an inconsistent adjacency map, and we use this+-- function in testing.+-- /Note: this function is for internal use only/.+consistent :: (Ord a, Eq e, Monoid e) => AdjacencyMap e a -> Bool+consistent (AM m) = referredToVertexSet m `Set.isSubsetOf` Map.keysSet m+ && and [ e /= zero | (_, es) <- Map.toAscList m, (_, e) <- Map.toAscList es ]++-- The set of vertices that are referred to by the edges in an adjacency map+referredToVertexSet :: Ord a => Map a (Map a e) -> Set a+referredToVertexSet m = Set.fromList $ concat+ [ [x, y] | (x, ys) <- Map.toAscList m, (y, _) <- Map.toAscList ys ]++-- The list of edges in an adjacency map+internalEdgeList :: Map a (Map a e) -> [(e, a, a)]+internalEdgeList m =+ [ (e, x, y) | (x, ys) <- Map.toAscList m, (y, e) <- Map.toAscList ys ]
+ src/Algebra/Graph/Labelled/Example/Automaton.hs view
@@ -0,0 +1,84 @@+{-# LANGUAGE CPP, OverloadedLists, TypeFamilies #-}+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.Labelled.Example.Automaton+-- Copyright : (c) Andrey Mokhov 2016-2018+-- License : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability : experimental+--+-- __Alga__ is a library for algebraic construction and manipulation of graphs+-- in Haskell. See <https://github.com/snowleopard/alga-paper this paper> for the+-- motivation behind the library, the underlying theory, and implementation details.+--+-- This module contains a simple example of using edge-labelled graphs defined+-- in the module "Algebra.Graph.Labelled" for working with finite automata.+-----------------------------------------------------------------------------+module Algebra.Graph.Labelled.Example.Automaton where++import Data.Map (Map)+import Data.Monoid (Any (..))++import Algebra.Graph.Label+import Algebra.Graph.Labelled+import Algebra.Graph.ToGraph++import qualified Data.Map as Map++#if !MIN_VERSION_base(4,8,0)+import Data.Set (Set)+import qualified Data.Set as Set+import GHC.Exts hiding (Any)++instance Ord a => IsList (Set a) where+ type Item (Set a) = a+ fromList = Set.fromList+ toList = Set.toList+#endif++-- | The alphabet of actions for ordering coffee or tea.+data Alphabet = Coffee -- ^ Order coffee+ | Tea -- ^ Order tea+ | Cancel -- ^ Cancel payment or order+ | Pay -- ^ Pay for the order+ deriving (Bounded, Enum, Eq, Ord, Show)++-- | The state of the order.+data State = Choice -- ^ Choosing what to order+ | Payment -- ^ Making the payment+ | Complete -- ^ The order is complete+ deriving (Bounded, Enum, Eq, Ord, Show)++-- TODO: Add an illustration.+-- | An example automaton for ordering coffee or tea.+--+-- @+-- order = 'overlays' [ 'Choice' '-<'['Coffee', 'Tea']'>-' 'Payment'+-- , 'Choice' '-<'['Cancel' ]'>-' 'Complete'+-- , 'Payment' '-<'['Cancel' ]'>-' 'Choice'+-- , 'Payment' '-<'['Pay' ]'>-' 'Complete' ]+-- @+coffeeTeaAutomaton :: Automaton Alphabet State+coffeeTeaAutomaton = overlays [ Choice -<[Coffee, Tea]>- Payment+ , Payment -<[Pay ]>- Complete+ , Choice -<[Cancel ]>- Complete+ , Payment -<[Cancel ]>- Choice ]++-- | The map of 'State' reachability.+--+-- @+-- reachability = Map.'Map.fromList' $ map (\s -> (s, 'reachable' s 'order')) ['Choice' ..]+-- @+--+-- Or, when evaluated:+--+-- @+-- reachability = Map.'Map.fromList' [ ('Choice' , ['Choice' , 'Payment', 'Complete'])+-- , ('Payment' , ['Payment' , 'Choice' , 'Complete'])+-- , ('Complete', ['Complete' ]) ]+-- @+reachability :: Map State [State]+reachability = Map.fromList $ map (\s -> (s, reachable s skeleton)) [Choice ..]+ where+ skeleton :: Graph Any State+ skeleton = emap (Any . not . isZero) coffeeTeaAutomaton
+ src/Algebra/Graph/Labelled/Example/Network.hs view
@@ -0,0 +1,64 @@+{-# LANGUAGE TypeFamilies #-}+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.Labelled.Example.Network+-- Copyright : (c) Andrey Mokhov 2016-2018+-- License : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability : experimental+--+-- __Alga__ is a library for algebraic construction and manipulation of graphs+-- in Haskell. See <https://github.com/snowleopard/alga-paper this paper> for the+-- motivation behind the library, the underlying theory, and implementation details.+--+-- This module contains a simple example of using edge-labelled graphs defined+-- in the module "Algebra.Graph.Labelled" for working with networks, i.e. graphs+-- whose edges are labelled with distances.+-----------------------------------------------------------------------------+module Algebra.Graph.Labelled.Example.Network where++import Algebra.Graph.Labelled++-- | Our example networks have /cities/ as vertices.+data City = Aberdeen+ | Edinburgh+ | Glasgow+ | London+ | Newcastle+ deriving (Bounded, Enum, Eq, Ord, Show)++-- | For simplicity we measure /journey times/ in integer number of minutes.+type JourneyTime = Int++-- | A part of the EastCoast train network between 'Aberdeen' and 'London'.+--+-- @+-- eastCoast = 'overlays' [ 'Aberdeen' '-<'150'>-' 'Edinburgh'+-- , 'Edinburgh' '-<' 90'>-' 'Newcastle'+-- , 'Newcastle' '-<'170'>-' 'London' ]+-- @+eastCoast :: Network JourneyTime City+eastCoast = overlays [ Aberdeen -<150>- Edinburgh+ , Edinburgh -< 90>- Newcastle+ , Newcastle -<170>- London ]++-- | A part of the ScotRail train network between 'Aberdeen' and 'Glasgow'.+--+-- @+-- scotRail = 'overlays' [ 'Aberdeen' '-<'140'>-' 'Edinburgh'+-- , 'Edinburgh' '-<' 50'>-' 'Glasgow'+-- , 'Edinburgh' '-<' 70'>-' 'Glasgow' ]+-- @+scotRail :: Network JourneyTime City+scotRail = overlays [ Aberdeen -<140>- Edinburgh+ , Edinburgh -< 50>- Glasgow+ , Edinburgh -< 70>- Glasgow ]++-- TODO: Add an illustration.+-- | An example train network.+--+-- @+-- network = 'overlay' 'scotRail' 'eastCoast'+-- @+network :: Network JourneyTime City+network = overlay scotRail eastCoast
src/Algebra/Graph/NonEmpty.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, DeriveFunctor, DeriveFoldable, DeriveTraversable #-}+{-# LANGUAGE CPP, DeriveFunctor #-} ----------------------------------------------------------------------------- -- | -- Module : Algebra.Graph.NonEmpty@@ -11,15 +11,22 @@ -- in Haskell. See <https://github.com/snowleopard/alga-paper this paper> for the -- motivation behind the library, the underlying theory, and implementation details. ----- This module defines the data type 'NonEmptyGraph' for graphs that are known--- to be non-empty at compile time. The naming convention generally follows that--- of "Data.List.NonEmpty": we use suffix @1@ to indicate the functions whose--- interface must be changed compared to "Algebra.Graph", e.g. 'vertices1'.+-- This module defines the data type 'Graph' for algebraic graphs that are known+-- to be non-empty at compile time. To avoid name clashes with "Algebra.Graph",+-- this module can be imported qualified: --+-- @+-- import qualified Algebra.Graph.NonEmpty as NonEmpty+-- @+--+-- The naming convention generally follows that of "Data.List.NonEmpty": we use+-- suffix @1@ to indicate the functions whose interface must be changed compared+-- to "Algebra.Graph", e.g. 'vertices1'.+-- ----------------------------------------------------------------------------- module Algebra.Graph.NonEmpty (- -- * Algebraic data type for non-empty graphs- NonEmptyGraph (..), toNonEmptyGraph,+ -- * Non-empty algebraic graphs+ Graph (..), toNonEmpty, -- * Basic graph construction primitives vertex, edge, overlay, overlay1, connect, vertices1, edges1, overlays1,@@ -33,7 +40,7 @@ -- * Graph properties size, hasVertex, hasEdge, vertexCount, edgeCount, vertexList1, edgeList,- vertexSet, vertexIntSet, edgeSet,+ vertexSet, edgeSet, -- * Standard families of graphs path1, circuit1, clique1, biclique1, star, stars1, tree, mesh1, torus1,@@ -44,7 +51,7 @@ -- * Graph composition box- ) where+ ) where import Prelude () import Prelude.Compat@@ -53,40 +60,43 @@ import Data.Semigroup #endif -import Control.DeepSeq (NFData (..))+import Control.DeepSeq import Control.Monad.Compat-import Control.Monad.State (runState, get, put)+import Control.Monad.State import Data.List.NonEmpty (NonEmpty (..)) import Algebra.Graph.Internal -import qualified Algebra.Graph as G-import qualified Algebra.Graph.AdjacencyIntMap as AIM-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 Data.IntSet as IntSet+import qualified Data.List.NonEmpty as NonEmpty+import qualified Data.Set as Set+import qualified Data.Tree as Tree -{-| The 'NonEmptyGraph' data type is a deep embedding of the core graph-construction primitives 'vertex', 'overlay' and 'connect'. As one can guess from-the name, the empty graph cannot be represented using this data type. See module-"Algebra.Graph" for a graph data type that allows for the construction of the-empty graph.+{-| Non-empty algebraic graphs, which are constructed using three primitives:+'vertex', 'overlay' and 'connect'. See module "Algebra.Graph" for algebraic+graphs that can be empty. We define a 'Num' instance as a convenient notation for working with graphs: - > 0 == Vertex 0- > 1 + 2 == Overlay (Vertex 1) (Vertex 2)- > 1 * 2 == Connect (Vertex 1) (Vertex 2)- > 1 + 2 * 3 == Overlay (Vertex 1) (Connect (Vertex 2) (Vertex 3))- > 1 * (2 + 3) == Connect (Vertex 1) (Overlay (Vertex 2) (Vertex 3))+ > 0 == vertex 0+ > 1 + 2 == overlay (vertex 1) (vertex 2)+ > 1 * 2 == connect (vertex 1) (vertex 2)+ > 1 + 2 * 3 == overlay (vertex 1) (connect (vertex 2) (vertex 3))+ > 1 * (2 + 3) == connect (vertex 1) (overlay (vertex 2) (vertex 3)) -Note that the 'signum' method of the 'Num' type class cannot be implemented.+__Note:__ the 'signum' method of the type class 'Num' cannot be implemented and+will throw an error. Furthermore, the 'Num' instance does not satisfy several+"customary laws" of 'Num', which dictate that 'fromInteger' @0@ and+'fromInteger' @1@ should act as additive and multiplicative identities, and+'negate' as additive inverse. Nevertheless, overloading 'fromInteger', '+' and+'*' is very convenient when working with algebraic graphs; we hope that in+future Haskell's Prelude will provide a more fine-grained class hierarchy for+algebraic structures, which we would be able to utilise without violating any+laws. -The 'Eq' instance is currently implemented using the 'AM.AdjacencyMap' as the-/canonical graph representation/ and satisfies the following laws of algebraic-graphs:+The 'Eq' instance satisfies the following laws of non-empty algebraic graphs. * 'overlay' is commutative, associative and idempotent: @@ -114,78 +124,115 @@ When specifying the time and memory complexity of graph algorithms, /n/ will denote the number of vertices in the graph, /m/ will denote the number of-edges in the graph, and /s/ will denote the /size/ of the corresponding-'NonEmptyGraph' expression, defined as the number of vertex leaves. For example,-if @g@ is a 'NonEmptyGraph' then /n/, /m/ and /s/ can be computed as follows:+edges in the graph, and /s/ will denote the /size/ of the corresponding 'Graph'+expression, defined as the number of vertex leaves (note that /n/ <= /s/). If+@g@ is a 'Graph', the corresponding /n/, /m/ and /s/ can be computed as follows: @n == 'vertexCount' g m == 'edgeCount' g s == 'size' g@ -The 'size' of any graph is positive and coincides with the result of 'length'-method of the 'Foldable' type class. We define 'size' only for the consistency-with the API of other graph representations, such as "Algebra.Graph".+Converting a 'Graph' to the corresponding+'Algebra.Graph.NonEmpty.AdjacencyMap.AdjacencyMap' takes /O(s + m * log(m))/ time and /O(s + m)/ memory. This is also the+complexity of the graph equality test, because it is currently implemented by+converting graph expressions to canonical representations based on adjacency+maps. -Converting a 'NonEmptyGraph' to the corresponding 'AM.AdjacencyMap' takes-/O(s + m * log(m))/ time and /O(s + m)/ memory. This is also the complexity of-the graph equality test, because it is currently implemented by converting graph-expressions to canonical representations based on adjacency maps.+The total order 'Ord' 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 < 'edge' 1 2+'vertex' 1 < 'edge' 1 1+'edge' 1 1 < 'edge' 1 2+'edge' 1 2 < 'edge' 1 1 + 'edge' 2 2+'edge' 1 2 < 'edge' 1 3@++Note that the resulting order refines the 'isSubgraphOf' relation and is+compatible with 'overlay' and 'connect' operations:++@'isSubgraphOf' x y ==> x <= y@++@x <= x + y+x + y <= x * y@ -}-data NonEmptyGraph a = Vertex a- | Overlay (NonEmptyGraph a) (NonEmptyGraph a)- | Connect (NonEmptyGraph a) (NonEmptyGraph a)- deriving (Foldable, Functor, Show, Traversable)+data Graph a = Vertex a+ | Overlay (Graph a) (Graph a)+ | Connect (Graph a) (Graph a)+ deriving (Functor, Show) -instance NFData a => NFData (NonEmptyGraph a) where+instance NFData a => NFData (Graph a) where rnf (Vertex x ) = rnf x rnf (Overlay x y) = rnf x `seq` rnf y rnf (Connect x y) = rnf x `seq` rnf y -instance T.ToGraph (NonEmptyGraph a) where- type ToVertex (NonEmptyGraph a) = a+instance T.ToGraph (Graph a) where+ type ToVertex (Graph a) = a foldg _ = foldg1 hasEdge = hasEdge -instance Num a => Num (NonEmptyGraph a) where+-- | __Note:__ this does not satisfy the usual ring laws; see 'Graph' for more+-- details.+instance Num a => Num (Graph a) where fromInteger = Vertex . fromInteger (+) = Overlay (*) = Connect- signum = error "NonEmptyGraph.signum cannot be implemented."+ signum = error "NonEmpty.Graph.signum cannot be implemented." abs = id negate = id -instance Ord a => Eq (NonEmptyGraph a) where- (==) = equals+instance Ord a => Eq (Graph a) where+ (==) = eq +instance Ord a => Ord (Graph a) where+ compare = ord+ -- TODO: Find a more efficient equality check.--- | Compare two graphs by converting them to their adjacency maps.-{-# NOINLINE [1] equals #-}-{-# RULES "equalsInt" equals = equalsInt #-}-equals :: Ord a => NonEmptyGraph a -> NonEmptyGraph a -> Bool-equals x y = T.adjacencyMap x == T.adjacencyMap y+-- | Check if two graphs are equal by converting them to their adjacency maps.+eq :: Ord a => Graph a -> Graph a -> Bool+eq x y = T.toAdjacencyMap x == T.toAdjacencyMap y+{-# NOINLINE [1] eq #-}+{-# RULES "eqInt" eq = eqInt #-} --- | Like @equals@ but specialised for graphs with vertices of type 'Int'.-equalsInt :: NonEmptyGraph Int -> NonEmptyGraph Int -> Bool-equalsInt x y = T.adjacencyIntMap x == T.adjacencyIntMap y+-- Like @eq@ but specialised for graphs with vertices of type 'Int'.+eqInt :: Graph Int -> Graph Int -> Bool+eqInt x y = T.toAdjacencyIntMap x == T.toAdjacencyIntMap y -instance Applicative NonEmptyGraph where+-- TODO: Find a more efficient comparison.+-- Compare two graphs by converting them to their adjacency maps.+ord :: Ord a => Graph a -> Graph a -> Ordering+ord x y = compare (T.toAdjacencyMap x) (T.toAdjacencyMap y)+{-# NOINLINE [1] ord #-}+{-# RULES "ordInt" ord = ordInt #-}++-- Like @ord@ but specialised for graphs with vertices of type 'Int'.+ordInt :: Graph Int -> Graph Int -> Ordering+ordInt x y = compare (T.toAdjacencyIntMap x) (T.toAdjacencyIntMap y)++instance Applicative Graph where pure = Vertex (<*>) = ap -instance Monad NonEmptyGraph where+instance Monad Graph where return = pure g >>= f = foldg1 f Overlay Connect g --- | Convert a 'G.Graph' into 'NonEmptyGraph'. Returns 'Nothing' if the argument--- is 'G.empty'.+-- | Convert an algebraic graph (from "Algebra.Graph") into a non-empty+-- algebraic graph. Returns 'Nothing' if the argument is 'G.empty'. -- Complexity: /O(s)/ time, memory and size. -- -- @--- toNonEmptyGraph 'G.empty' == Nothing--- toNonEmptyGraph ('C.toGraph' x) == Just (x :: NonEmptyGraph a)+-- toNonEmpty 'G.empty' == Nothing+-- toNonEmpty ('T.toGraph' x) == Just (x :: 'Graph' a) -- @-toNonEmptyGraph :: G.Graph a -> Maybe (NonEmptyGraph a)-toNonEmptyGraph = G.foldg Nothing (Just . Vertex) (go Overlay) (go Connect)+toNonEmpty :: G.Graph a -> Maybe (Graph a)+toNonEmpty = G.foldg Nothing (Just . Vertex) (go Overlay) (go Connect) where go _ Nothing y = y go _ x Nothing = x@@ -201,7 +248,7 @@ -- 'edgeCount' (vertex x) == 0 -- 'size' (vertex x) == 1 -- @-vertex :: a -> NonEmptyGraph a+vertex :: a -> Graph a vertex = Vertex {-# INLINE vertex #-} @@ -215,7 +262,7 @@ -- 'vertexCount' (edge 1 1) == 1 -- 'vertexCount' (edge 1 2) == 2 -- @-edge :: a -> a -> NonEmptyGraph a+edge :: a -> a -> Graph a edge u v = connect (vertex u) (vertex v) -- | /Overlay/ two graphs. An alias for the constructor 'Overlay'. This is a@@ -232,21 +279,21 @@ -- 'vertexCount' (overlay 1 2) == 2 -- 'edgeCount' (overlay 1 2) == 0 -- @-overlay :: NonEmptyGraph a -> NonEmptyGraph a -> NonEmptyGraph a+overlay :: Graph a -> Graph a -> Graph a overlay = Overlay {-# INLINE overlay #-} --- | Overlay a possibly empty graph with a non-empty graph. If the first--- argument is 'G.empty', the function returns the second argument; otherwise--- it is semantically the same as 'overlay'.+-- | Overlay a possibly empty graph (from "Algebra.Graph") with a non-empty+-- graph. If the first argument is 'G.empty', the function returns the second+-- argument; otherwise it is semantically the same as 'overlay'. -- Complexity: /O(s1)/ time and memory, and /O(s1 + s2)/ size. -- -- @ -- overlay1 'G.empty' x == x--- x /= 'G.empty' ==> overlay1 x y == overlay (fromJust $ toNonEmptyGraph x) y+-- x /= 'G.empty' ==> overlay1 x y == overlay (fromJust $ toNonEmpty x) y -- @-overlay1 :: G.Graph a -> NonEmptyGraph a -> NonEmptyGraph a-overlay1 = maybe id overlay . toNonEmptyGraph+overlay1 :: G.Graph a -> Graph a -> Graph a+overlay1 = maybe id overlay . toNonEmpty -- | /Connect/ two graphs. An alias for the constructor 'Connect'. This is an -- associative operation, which distributes over 'overlay' and obeys the@@ -267,7 +314,7 @@ -- 'vertexCount' (connect 1 2) == 2 -- 'edgeCount' (connect 1 2) == 1 -- @-connect :: NonEmptyGraph a -> NonEmptyGraph a -> NonEmptyGraph a+connect :: Graph a -> Graph a -> Graph a connect = Connect {-# INLINE connect #-} @@ -276,12 +323,12 @@ -- given list. -- -- @--- vertices1 (x ':|' []) == 'vertex' x+-- vertices1 [x] == 'vertex' x -- 'hasVertex' x . vertices1 == 'elem' x -- 'vertexCount' . vertices1 == 'length' . 'Data.List.NonEmpty.nub' -- 'vertexSet' . vertices1 == Set.'Set.fromList' . 'Data.List.NonEmpty.toList' -- @-vertices1 :: NonEmpty a -> NonEmptyGraph a+vertices1 :: NonEmpty a -> Graph a vertices1 = overlays1 . fmap vertex {-# NOINLINE [1] vertices1 #-} @@ -290,10 +337,10 @@ -- given list. -- -- @--- edges1 ((x,y) ':|' []) == 'edge' x y--- 'edgeCount' . edges1 == 'Data.List.NonEmpty.length' . 'Data.List.NonEmpty.nub'+-- edges1 [(x,y)] == 'edge' x y+-- 'edgeCount' . edges1 == 'Data.List.NonEmpty.length' . 'Data.List.NonEmpty.nub' -- @-edges1 :: NonEmpty (a, a) -> NonEmptyGraph a+edges1 :: NonEmpty (a, a) -> Graph a edges1 = overlays1 . fmap (uncurry edge) -- | Overlay a given list of graphs.@@ -301,10 +348,10 @@ -- of the given list, and /S/ is the sum of sizes of the graphs in the list. -- -- @--- overlays1 (x ':|' [] ) == x--- overlays1 (x ':|' [y]) == 'overlay' x y+-- overlays1 [x] == x+-- overlays1 [x,y] == 'overlay' x y -- @-overlays1 :: NonEmpty (NonEmptyGraph a) -> NonEmptyGraph a+overlays1 :: NonEmpty (Graph a) -> Graph a overlays1 = concatg1 overlay {-# INLINE [2] overlays1 #-} @@ -313,28 +360,30 @@ -- of the given list, and /S/ is the sum of sizes of the graphs in the list. -- -- @--- connects1 (x ':|' [] ) == x--- connects1 (x ':|' [y]) == 'connect' x y+-- connects1 [x] == x+-- connects1 [x,y] == 'connect' x y -- @-connects1 :: NonEmpty (NonEmptyGraph a) -> NonEmptyGraph a+connects1 :: NonEmpty (Graph a) -> Graph a connects1 = concatg1 connect {-# INLINE [2] connects1 #-} --- | Auxiliary function, similar to 'sconcat'.-concatg1 :: (NonEmptyGraph a -> NonEmptyGraph a -> NonEmptyGraph a) -> NonEmpty (NonEmptyGraph a) -> NonEmptyGraph a+-- Auxiliary function, similar to 'sconcat'.+concatg1 :: (Graph a -> Graph a -> Graph a) -> NonEmpty (Graph a) -> Graph a concatg1 combine (x :| xs) = maybe x (combine x) $ foldr1Safe combine xs --- | Generalised graph folding: recursively collapse a 'NonEmptyGraph' by+-- | Generalised graph folding: recursively collapse a 'Graph' by -- applying the provided functions to the leaves and internal nodes of the -- expression. The order of arguments is: vertex, overlay and connect. -- Complexity: /O(s)/ applications of given functions. As an example, the -- complexity of 'size' is /O(s)/, since all functions have cost /O(1)/. -- -- @--- foldg1 (const 1) (+) (+) == 'size'--- foldg1 (==x) (||) (||) == 'hasVertex' x+-- foldg1 'vertex' 'overlay' 'connect' == id+-- foldg1 'vertex' 'overlay' ('flip' 'connect') == 'transpose'+-- foldg1 ('const' 1) (+) (+) == 'size'+-- foldg1 (== x) (||) (||) == 'hasVertex' x -- @-foldg1 :: (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> NonEmptyGraph a -> b+foldg1 :: (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Graph a -> b foldg1 v o c = go where go (Vertex x ) = v x@@ -347,13 +396,14 @@ -- graph can be quadratic with respect to the expression size /s/. -- -- @--- isSubgraphOf x ('overlay' x y) == True--- isSubgraphOf ('overlay' x y) ('connect' x y) == True--- isSubgraphOf ('path1' xs) ('circuit1' xs) == True+-- isSubgraphOf x ('overlay' x y) == True+-- isSubgraphOf ('overlay' x y) ('connect' x y) == True+-- isSubgraphOf ('path1' xs) ('circuit1' xs) == True+-- isSubgraphOf x y ==> x <= y -- @-{-# SPECIALISE isSubgraphOf :: NonEmptyGraph Int -> NonEmptyGraph Int -> Bool #-}-isSubgraphOf :: Ord a => NonEmptyGraph a -> NonEmptyGraph a -> Bool+isSubgraphOf :: Ord a => Graph a -> Graph a -> Bool isSubgraphOf x y = overlay x y == y+{-# SPECIALISE isSubgraphOf :: Graph Int -> Graph Int -> Bool #-} -- | Structural equality on graph expressions. -- Complexity: /O(s)/ time.@@ -364,12 +414,12 @@ -- 1 + 2 === 2 + 1 == False -- x + y === x * y == False -- @-{-# SPECIALISE (===) :: NonEmptyGraph Int -> NonEmptyGraph Int -> Bool #-}-(===) :: Eq a => NonEmptyGraph a -> NonEmptyGraph a -> Bool+(===) :: Eq a => Graph a -> Graph a -> Bool (Vertex x1 ) === (Vertex x2 ) = x1 == x2 (Overlay x1 y1) === (Overlay x2 y2) = x1 === x2 && y1 === y2 (Connect x1 y1) === (Connect x2 y2) = x1 === x2 && y1 === y2 _ === _ = False+{-# SPECIALISE (===) :: Graph Int -> Graph Int -> Bool #-} infix 4 === @@ -383,19 +433,19 @@ -- size x >= 1 -- size x >= 'vertexCount' x -- @-size :: NonEmptyGraph a -> Int+size :: Graph a -> Int size = foldg1 (const 1) (+) (+) --- | Check if a graph contains a given vertex. A convenient alias for `elem`.+-- | Check if a graph contains a given vertex. -- Complexity: /O(s)/ time. -- -- @ -- hasVertex x ('vertex' x) == True -- hasVertex 1 ('vertex' 2) == False -- @-{-# SPECIALISE hasVertex :: Int -> NonEmptyGraph Int -> Bool #-}-hasVertex :: Eq a => a -> NonEmptyGraph a -> Bool+hasVertex :: Eq a => a -> Graph a -> Bool hasVertex v = foldg1 (==v) (||) (||)+{-# SPECIALISE hasVertex :: Int -> Graph Int -> Bool #-} -- TODO: Reduce code duplication with 'Algebra.Graph.hasEdge'. -- | Check if a graph contains a given edge.@@ -404,11 +454,10 @@ -- @ -- hasEdge x y ('vertex' z) == False -- hasEdge x y ('edge' x y) == True--- hasEdge x y . 'removeEdge' x y == const False+-- hasEdge x y . 'removeEdge' x y == 'const' False -- hasEdge x y == 'elem' (x,y) . 'edgeList' -- @-{-# SPECIALISE hasEdge :: Int -> Int -> NonEmptyGraph Int -> Bool #-}-hasEdge :: Eq a => a -> a -> NonEmptyGraph a -> Bool+hasEdge :: Eq a => a -> a -> Graph a -> Bool hasEdge s t g = hit g == Edge where hit (Vertex x ) = if x == s then Tail else Miss@@ -420,22 +469,23 @@ Miss -> hit y Tail -> if hasVertex t y then Edge else Tail Edge -> Edge+{-# SPECIALISE hasEdge :: Int -> Int -> Graph Int -> Bool #-} -- | The number of vertices in a graph. -- Complexity: /O(s * log(n))/ time. -- -- @--- vertexCount ('vertex' x) == 1--- vertexCount x >= 1--- vertexCount == 'length' . 'vertexList1'+-- vertexCount ('vertex' x) == 1+-- vertexCount == 'length' . 'vertexList'+-- vertexCount x \< vertexCount y ==> x \< y -- @+vertexCount :: Ord a => Graph a -> Int+vertexCount = T.vertexCount {-# RULES "vertexCount/Int" vertexCount = vertexIntCount #-} {-# INLINE [1] vertexCount #-}-vertexCount :: Ord a => NonEmptyGraph a -> Int-vertexCount = T.vertexCount --- | Like 'vertexCount' but specialised for NonEmptyGraph with vertices of type 'Int'.-vertexIntCount :: NonEmptyGraph Int -> Int+-- Like 'vertexCount' but specialised for Graph with vertices of type 'Int'.+vertexIntCount :: Graph Int -> Int vertexIntCount = IntSet.size . vertexIntSet -- | The number of edges in a graph.@@ -447,29 +497,29 @@ -- edgeCount ('edge' x y) == 1 -- edgeCount == 'length' . 'edgeList' -- @+edgeCount :: Ord a => Graph a -> Int+edgeCount = T.edgeCount {-# INLINE [1] edgeCount #-} {-# RULES "edgeCount/Int" edgeCount = edgeCountInt #-}-edgeCount :: Ord a => NonEmptyGraph a -> Int-edgeCount = T.edgeCount --- | Like 'edgeCount' but specialised for graphs with vertices of type 'Int'.-edgeCountInt :: NonEmptyGraph Int -> Int-edgeCountInt = AIM.edgeCount . T.toAdjacencyIntMap+-- Like 'edgeCount' but specialised for graphs with vertices of type 'Int'.+edgeCountInt :: Graph Int -> Int+edgeCountInt = T.edgeCount . T.toAdjacencyIntMap -- | The sorted list of vertices of a given graph. -- Complexity: /O(s * log(n))/ time and /O(n)/ memory. -- -- @--- vertexList1 ('vertex' x) == x ':|' []+-- vertexList1 ('vertex' x) == [x] -- vertexList1 . 'vertices1' == 'Data.List.NonEmpty.nub' . 'Data.List.NonEmpty.sort' -- @+vertexList1 :: Ord a => Graph a -> NonEmpty a+vertexList1 = NonEmpty.fromList . Set.toAscList . vertexSet {-# RULES "vertexList1/Int" vertexList1 = vertexIntList1 #-} {-# INLINE [1] vertexList1 #-}-vertexList1 :: Ord a => NonEmptyGraph a -> NonEmpty a-vertexList1 = NonEmpty.fromList . Set.toAscList . vertexSet --- | Like 'vertexList1' but specialised for NonEmptyGraph with vertices of type 'Int'.-vertexIntList1 :: NonEmptyGraph Int -> NonEmpty Int+-- | Like 'vertexList1' but specialised for Graph with vertices of type 'Int'.+vertexIntList1 :: Graph Int -> NonEmpty Int vertexIntList1 = NonEmpty.fromList . IntSet.toAscList . vertexIntSet -- | The sorted list of edges of a graph.@@ -481,16 +531,16 @@ -- edgeList ('edge' x y) == [(x,y)] -- edgeList ('star' 2 [3,1]) == [(2,1), (2,3)] -- edgeList . 'edges1' == 'Data.List.nub' . 'Data.List.sort' . 'Data.List.NonEmpty.toList'--- edgeList . 'transpose' == 'Data.List.sort' . map 'Data.Tuple.swap' . edgeList+-- edgeList . 'transpose' == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . edgeList -- @+edgeList :: Ord a => Graph a -> [(a, a)]+edgeList = T.edgeList {-# RULES "edgeList/Int" edgeList = edgeIntList #-} {-# INLINE [1] edgeList #-}-edgeList :: Ord a => NonEmptyGraph a -> [(a, a)]-edgeList = T.edgeList --- | Like 'edgeList' but specialised for NonEmptyGraph with vertices of type 'Int'.-edgeIntList :: NonEmptyGraph Int -> [(Int,Int)]-edgeIntList = AIM.edgeList . T.toAdjacencyIntMap+-- Like 'edgeList' but specialised for Graph with vertices of type 'Int'.+edgeIntList :: Graph Int -> [(Int, Int)]+edgeIntList = T.edgeList . T.toAdjacencyIntMap -- | The set of vertices of a given graph. -- Complexity: /O(s * log(n))/ time and /O(n)/ memory.@@ -500,19 +550,11 @@ -- vertexSet . 'vertices1' == Set.'Set.fromList' . 'Data.List.NonEmpty.toList' -- vertexSet . 'clique1' == Set.'Set.fromList' . 'Data.List.NonEmpty.toList' -- @-vertexSet :: Ord a => NonEmptyGraph a -> Set.Set a+vertexSet :: Ord a => Graph a -> Set.Set a vertexSet = T.vertexSet --- | The set of vertices of a given graph. Like 'vertexSet' but specialised for--- graphs with vertices of type 'Int'.--- Complexity: /O(s * log(n))/ time and /O(n)/ memory.------ @--- vertexIntSet . 'vertex' == IntSet.'IntSet.singleton'--- vertexIntSet . 'vertices1' == IntSet.'IntSet.fromList' . 'Data.List.NonEmpty.toList'--- vertexIntSet . 'clique1' == IntSet.'IntSet.fromList' . 'Data.List.NonEmpty.toList'--- @-vertexIntSet :: NonEmptyGraph Int -> IntSet.IntSet+-- Like 'vertexSet' but specialised for graphs with vertices of type 'Int'.+vertexIntSet :: Graph Int -> IntSet.IntSet vertexIntSet = T.vertexIntSet -- | The set of edges of a given graph.@@ -523,7 +565,7 @@ -- edgeSet ('edge' x y) == Set.'Set.singleton' (x,y) -- edgeSet . 'edges1' == Set.'Set.fromList' . 'Data.List.NonEmpty.toList' -- @-edgeSet :: Ord a => NonEmptyGraph a -> Set.Set (a, a)+edgeSet :: Ord a => Graph a -> Set.Set (a, a) edgeSet = T.edgeSet -- | The /path/ on a list of vertices.@@ -531,11 +573,11 @@ -- given list. -- -- @--- path1 (x ':|' [] ) == 'vertex' x--- path1 (x ':|' [y]) == 'edge' x y--- path1 . 'Data.List.NonEmpty.reverse' == 'transpose' . path1+-- path1 [x] == 'vertex' x+-- path1 [x,y] == 'edge' x y+-- path1 . 'Data.List.NonEmpty.reverse' == 'transpose' . path1 -- @-path1 :: NonEmpty a -> NonEmptyGraph a+path1 :: NonEmpty a -> Graph a path1 (x :| [] ) = vertex x path1 (x :| (y:ys)) = edges1 ((x, y) :| zip (y:ys) ys) @@ -544,11 +586,11 @@ -- given list. -- -- @--- circuit1 (x ':|' [] ) == 'edge' x x--- circuit1 (x ':|' [y]) == 'edges1' ((x,y) ':|' [(y,x)])--- circuit1 . 'Data.List.NonEmpty.reverse' == 'transpose' . circuit1+-- circuit1 [x] == 'edge' x x+-- circuit1 [x,y] == 'edges1' [(x,y), (y,x)]+-- circuit1 . 'Data.List.NonEmpty.reverse' == 'transpose' . circuit1 -- @-circuit1 :: NonEmpty a -> NonEmptyGraph a+circuit1 :: NonEmpty a -> Graph a circuit1 (x :| xs) = path1 (x :| xs ++ [x]) -- | The /clique/ on a list of vertices.@@ -556,13 +598,13 @@ -- given list. -- -- @--- clique1 (x ':|' [] ) == 'vertex' x--- clique1 (x ':|' [y] ) == 'edge' x y--- clique1 (x ':|' [y,z]) == 'edges1' ((x,y) ':|' [(x,z), (y,z)])--- clique1 (xs '<>' ys) == 'connect' (clique1 xs) (clique1 ys)--- clique1 . 'Data.List.NonEmpty.reverse' == 'transpose' . clique1+-- clique1 [x] == 'vertex' x+-- clique1 [x,y] == 'edge' x y+-- clique1 [x,y,z] == 'edges1' [(x,y), (x,z), (y,z)]+-- clique1 (xs '<>' ys) == 'connect' (clique1 xs) (clique1 ys)+-- clique1 . 'Data.List.NonEmpty.reverse' == 'transpose' . clique1 -- @-clique1 :: NonEmpty a -> NonEmptyGraph a+clique1 :: NonEmpty a -> Graph a clique1 = connects1 . fmap vertex {-# NOINLINE [1] clique1 #-} @@ -571,10 +613,10 @@ -- lengths of the given lists. -- -- @--- biclique1 (x1 ':|' [x2]) (y1 ':|' [y2]) == 'edges1' ((x1,y1) ':|' [(x1,y2), (x2,y1), (x2,y2)])--- biclique1 xs ys == 'connect' ('vertices1' xs) ('vertices1' ys)+-- biclique1 [x1,x2] [y1,y2] == 'edges1' [(x1,y1), (x1,y2), (x2,y1), (x2,y2)]+-- biclique1 xs ys == 'connect' ('vertices1' xs) ('vertices1' ys) -- @-biclique1 :: NonEmpty a -> NonEmpty a -> NonEmptyGraph a+biclique1 :: NonEmpty a -> NonEmpty a -> Graph a biclique1 xs ys = connect (vertices1 xs) (vertices1 ys) -- | The /star/ formed by a centre vertex connected to a list of leaves.@@ -584,9 +626,9 @@ -- @ -- star x [] == 'vertex' x -- star x [y] == 'edge' x y--- star x [y,z] == 'edges1' ((x,y) ':|' [(x,z)])+-- star x [y,z] == 'edges1' [(x,y), (x,z)] -- @-star :: a -> [a] -> NonEmptyGraph a+star :: a -> [a] -> Graph a star x [] = vertex x star x (y:ys) = connect (vertex x) (vertices1 $ y :| ys) {-# INLINE star #-}@@ -596,13 +638,13 @@ -- input. -- -- @--- stars1 ((x, []) ':|' []) == 'vertex' x--- stars1 ((x, [y]) ':|' []) == 'edge' x y--- stars1 ((x, ys) ':|' []) == 'star' x ys--- stars1 == 'overlays1' . fmap (uncurry 'star')--- 'overlay' (stars1 xs) (stars1 ys) == stars1 (xs <> ys)+-- stars1 [(x, [] )] == 'vertex' x+-- stars1 [(x, [y])] == 'edge' x y+-- stars1 [(x, ys )] == 'star' x ys+-- stars1 == 'overlays1' . 'fmap' ('uncurry' 'star')+-- 'overlay' (stars1 xs) (stars1 ys) == stars1 (xs '<>' ys) -- @-stars1 :: NonEmpty (a, [a]) -> NonEmptyGraph a+stars1 :: NonEmpty (a, [a]) -> Graph a stars1 = overlays1 . fmap (uncurry star) {-# INLINE stars1 #-} @@ -612,11 +654,11 @@ -- -- @ -- tree (Node x []) == 'vertex' x--- tree (Node x [Node y [Node z []]]) == 'path1' (x ':|' [y,z])+-- tree (Node x [Node y [Node z []]]) == 'path1' [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 []]]) == 'edges1' ((1,2) ':|' [(1,3), (3,4), (3,5)])+-- tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == 'edges1' [(1,2), (1,3), (3,4), (3,5)] -- @-tree :: Tree.Tree a -> NonEmptyGraph a+tree :: Tree.Tree a -> Graph a tree (Tree.Node x f) = overlays1 $ star x (map Tree.rootLabel f) :| map tree f -- | Construct a /mesh graph/ from two lists of vertices.@@ -624,14 +666,14 @@ -- lengths of the given lists. -- -- @--- mesh1 (x ':|' []) (y ':|' []) == 'vertex' (x, y)--- mesh1 xs ys == 'box' ('path1' xs) ('path1' ys)--- mesh1 (1 ':|' [2,3]) (\'a\' ':|' "b") == 'edges1' ('Data.List.NonEmpty.fromList' [ ((1,\'a\'),(1,\'b\')), ((1,\'a\'),(2,\'a\'))--- , ((1,\'b\'),(2,\'b\')), ((2,\'a\'),(2,\'b\'))--- , ((2,\'a\'),(3,\'a\')), ((2,\'b\'),(3,\'b\'))--- , ((3,\'a\'),(3,\'b\')) ])+-- mesh1 [x] [y] == 'vertex' (x, y)+-- mesh1 xs ys == 'box' ('path1' xs) ('path1' ys)+-- mesh1 [1,2,3] [\'a\', \'b\'] == 'edges1' [ ((1,\'a\'),(1,\'b\')), ((1,\'a\'),(2,\'a\'))+-- , ((1,\'b\'),(2,\'b\')), ((2,\'a\'),(2,\'b\'))+-- , ((2,\'a\'),(3,\'a\')), ((2,\'b\'),(3,\'b\'))+-- , ((3,\'a\'),(3,\'b\')) ] -- @-mesh1 :: NonEmpty a -> NonEmpty b -> NonEmptyGraph (a, b)+mesh1 :: NonEmpty a -> NonEmpty b -> Graph (a, b) mesh1 xx@(x:|xs) yy@(y:|ys) = case NonEmpty.nonEmpty ipxs of Nothing ->@@ -659,21 +701,22 @@ -- lengths of the given lists. -- -- @--- torus1 (x ':|' []) (y ':|' []) == 'edge' (x,y) (x,y)--- torus1 xs ys == 'box' ('circuit1' xs) ('circuit1' ys)--- torus1 (1 ':|' [2]) (\'a\' ':|' "b") == 'edges1' ('Data.List.NonEmpty.fromList' [ ((1,\'a\'),(1,\'b\')), ((1,\'a\'),(2,\'a\'))--- , ((1,\'b\'),(1,\'a\')), ((1,\'b\'),(2,\'b\'))--- , ((2,\'a\'),(1,\'a\')), ((2,\'a\'),(2,\'b\'))--- , ((2,\'b\'),(1,\'b\')), ((2,\'b\'),(2,\'a\')) ])+-- torus1 [x] [y] == 'edge' (x,y) (x,y)+-- torus1 xs ys == 'box' ('circuit1' xs) ('circuit1' ys)+-- torus1 [1,2] [\'a\', \'b\'] == 'edges1' [ ((1,\'a\'),(1,\'b\')), ((1,\'a\'),(2,\'a\'))+-- , ((1,\'b\'),(1,\'a\')), ((1,\'b\'),(2,\'b\'))+-- , ((2,\'a\'),(1,\'a\')), ((2,\'a\'),(2,\'b\'))+-- , ((2,\'b\'),(1,\'b\')), ((2,\'b\'),(2,\'a\')) ] -- @-torus1 :: NonEmpty a -> NonEmpty b -> NonEmptyGraph (a, b)-torus1 xs ys = stars1 $ fmap (\((a1,a2),(b1,b2)) -> ((a1, b1), [(a1, b2), (a2, b1)])) $ liftM2 (,) (pairs1 xs) (pairs1 ys)+torus1 :: NonEmpty a -> NonEmpty b -> Graph (a, b)+torus1 xs ys = stars1 $ fmap (\((a1,a2),(b1,b2)) -> ((a1, b1), [(a1, b2), (a2, b1)]))+ $ liftM2 (,) (pairs1 xs) (pairs1 ys) --- | Auxiliary function for 'mesh1' and 'torus1'+-- Auxiliary function for 'mesh1' and 'torus1' pairs1 :: NonEmpty a -> NonEmpty (a, a) pairs1 as@(x:|xs) = NonEmpty.zip as $ maybe (x :| []) (`appendNonEmpty` [x]) $ NonEmpty.nonEmpty xs --- | Append a list to a non-empty one+-- Append a list to a non-empty one appendNonEmpty :: NonEmpty a -> [a] -> NonEmpty a appendNonEmpty (w:|ws) zs = w :| (ws++zs) @@ -688,34 +731,34 @@ -- removeVertex1 1 ('edge' 1 2) == Just ('vertex' 2) -- removeVertex1 x '>=>' removeVertex1 x == removeVertex1 x -- @-{-# SPECIALISE removeVertex1 :: Int -> NonEmptyGraph Int -> Maybe (NonEmptyGraph Int) #-}-removeVertex1 :: Eq a => a -> NonEmptyGraph a -> Maybe (NonEmptyGraph a)+removeVertex1 :: Eq a => a -> Graph a -> Maybe (Graph a) removeVertex1 x = induce1 (/= x)+{-# SPECIALISE removeVertex1 :: Int -> Graph Int -> Maybe (Graph Int) #-} -- | Remove an edge from a given graph. -- Complexity: /O(s)/ time, memory and size. -- -- @--- removeEdge x y ('edge' x y) == 'vertices1' (x ':|' [y])+-- removeEdge x y ('edge' x y) == 'vertices1' [x,y] -- removeEdge x y . removeEdge x y == removeEdge x y -- removeEdge 1 1 (1 * 1 * 2 * 2) == 1 * 2 * 2 -- removeEdge 1 2 (1 * 1 * 2 * 2) == 1 * 1 + 2 * 2 -- 'size' (removeEdge x y z) <= 3 * 'size' z -- @-{-# SPECIALISE removeEdge :: Int -> Int -> NonEmptyGraph Int -> NonEmptyGraph Int #-}-removeEdge :: Eq a => a -> a -> NonEmptyGraph a -> NonEmptyGraph a+removeEdge :: Eq a => a -> a -> Graph a -> Graph a removeEdge s t = filterContext s (/=s) (/=t)+{-# SPECIALISE removeEdge :: Int -> Int -> Graph Int -> Graph Int #-} -- TODO: Export-{-# SPECIALISE filterContext :: Int -> (Int -> Bool) -> (Int -> Bool) -> NonEmptyGraph Int -> NonEmptyGraph Int #-}-filterContext :: Eq a => a -> (a -> Bool) -> (a -> Bool) -> NonEmptyGraph a -> NonEmptyGraph a+filterContext :: Eq a => a -> (a -> Bool) -> (a -> Bool) -> Graph a -> Graph a filterContext s i o g = maybe g go $ G.context (==s) (T.toGraph g) where go (G.Context is os) = G.induce (/=s) (T.toGraph g) `overlay1` transpose (star s (filter i is)) `overlay` star s (filter o os)+{-# SPECIALISE filterContext :: Int -> (Int -> Bool) -> (Int -> Bool) -> Graph Int -> Graph Int #-} --- | The function @'replaceVertex' x y@ replaces vertex @x@ with vertex @y@ in a--- given 'NonEmptyGraph'. If @y@ already exists, @x@ and @y@ will be merged.+-- | The function 'replaceVertex' @x y@ replaces vertex @x@ with vertex @y@ in a+-- given 'Graph'. If @y@ already exists, @x@ and @y@ will be merged. -- Complexity: /O(s)/ time, memory and size. -- -- @@@ -723,21 +766,21 @@ -- replaceVertex x y ('vertex' x) == 'vertex' y -- replaceVertex x y == 'mergeVertices' (== x) y -- @-{-# SPECIALISE replaceVertex :: Int -> Int -> NonEmptyGraph Int -> NonEmptyGraph Int #-}-replaceVertex :: Eq a => a -> a -> NonEmptyGraph a -> NonEmptyGraph a+replaceVertex :: Eq a => a -> a -> Graph a -> Graph a replaceVertex u v = fmap $ \w -> if w == u then v else w+{-# SPECIALISE replaceVertex :: Int -> Int -> Graph Int -> Graph Int #-} -- | Merge vertices satisfying a given predicate into a given vertex. -- Complexity: /O(s)/ time, memory and size, assuming that the predicate takes -- /O(1)/ to be evaluated. -- -- @--- mergeVertices (const False) x == id+-- 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 'even' 1 (0 * 2) == 1 * 1+-- mergeVertices 'odd' 1 (3 + 4 * 5) == 4 * 1 -- @-mergeVertices :: (a -> Bool) -> a -> NonEmptyGraph a -> NonEmptyGraph a+mergeVertices :: (a -> Bool) -> a -> Graph a -> Graph a mergeVertices p v = fmap $ \w -> if p w then v else w -- | Split a vertex into a list of vertices with the same connectivity.@@ -746,13 +789,13 @@ -- given list. -- -- @--- splitVertex1 x (x ':|' [] ) == id--- splitVertex1 x (y ':|' [] ) == 'replaceVertex' x y--- splitVertex1 1 (0 ':|' [1]) $ 1 * (2 + 3) == (0 + 1) * (2 + 3)+-- splitVertex1 x [x] == id+-- splitVertex1 x [y] == 'replaceVertex' x y+-- splitVertex1 1 [0,1] $ 1 * (2 + 3) == (0 + 1) * (2 + 3) -- @-{-# SPECIALISE splitVertex1 :: Int -> NonEmpty Int -> NonEmptyGraph Int -> NonEmptyGraph Int #-}-splitVertex1 :: Eq a => a -> NonEmpty a -> NonEmptyGraph a -> NonEmptyGraph a+splitVertex1 :: Eq a => a -> NonEmpty a -> Graph a -> Graph a splitVertex1 v us g = g >>= \w -> if w == v then vertices1 us else vertex w+{-# SPECIALISE splitVertex1 :: Int -> NonEmpty Int -> Graph Int -> Graph Int #-} -- | Transpose a given graph. -- Complexity: /O(s)/ time, memory and size.@@ -762,9 +805,9 @@ -- transpose ('edge' x y) == 'edge' y x -- transpose . transpose == id -- transpose ('box' x y) == 'box' (transpose x) (transpose y)--- 'edgeList' . transpose == 'Data.List.sort' . map 'Data.Tuple.swap' . 'edgeList'+-- 'edgeList' . transpose == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . 'edgeList' -- @-transpose :: NonEmptyGraph a -> NonEmptyGraph a+transpose :: Graph a -> Graph a transpose = foldg1 vertex overlay (flip connect) {-# NOINLINE [1] transpose #-} @@ -787,12 +830,12 @@ -- /O(1)/ to be evaluated. -- -- @--- induce1 (const True ) x == Just x--- induce1 (const False) x == Nothing+-- induce1 ('const' True ) x == Just x+-- induce1 ('const' False) x == Nothing -- induce1 (/= x) == 'removeVertex1' x -- induce1 p '>=>' induce1 q == induce1 (\\x -> p x && q x) -- @-induce1 :: (a -> Bool) -> NonEmptyGraph a -> Maybe (NonEmptyGraph a)+induce1 :: (a -> Bool) -> Graph a -> Maybe (Graph a) induce1 p = foldg1 (\x -> if p x then Just (Vertex x) else Nothing) (k Overlay)@@ -810,18 +853,17 @@ -- that the size of the result does not exceed the size of the given expression. -- -- @--- simplify == id--- 'size' (simplify x) <= 'size' x+-- simplify == id+-- 'size' (simplify x) <= 'size' x -- simplify 1 '===' 1 -- simplify (1 + 1) '===' 1 -- simplify (1 + 2 + 1) '===' 1 + 2 -- simplify (1 * 1 * 1) '===' 1 * 1 -- @-{-# SPECIALISE simplify :: NonEmptyGraph Int -> NonEmptyGraph Int #-}-simplify :: Ord a => NonEmptyGraph a -> NonEmptyGraph a+simplify :: Ord a => Graph a -> Graph a simplify = foldg1 Vertex (simple Overlay) (simple Connect)+{-# SPECIALISE simplify :: Graph Int -> Graph Int #-} -{-# SPECIALISE simple :: (NonEmptyGraph Int -> NonEmptyGraph Int -> NonEmptyGraph Int) -> NonEmptyGraph Int -> NonEmptyGraph Int -> NonEmptyGraph Int #-} simple :: Eq g => (g -> g -> g) -> g -> g -> g simple op x y | x == z = x@@ -829,16 +871,17 @@ | otherwise = z where z = op x y+{-# SPECIALISE simple :: (Graph Int -> Graph Int -> Graph Int) -> Graph Int -> Graph Int -> Graph Int #-} -- | Compute the /Cartesian product/ of graphs. -- Complexity: /O(s1 * s2)/ time, memory and size, where /s1/ and /s2/ are the -- sizes of the given graphs. -- -- @--- box ('path1' $ 'Data.List.NonEmpty.fromList' [0,1]) ('path1' $ 'Data.List.NonEmpty.fromList' "ab") == 'edges1' ('Data.List.NonEmpty.fromList' [ ((0,\'a\'), (0,\'b\'))--- , ((0,\'a\'), (1,\'a\'))--- , ((0,\'b\'), (1,\'b\'))--- , ((1,\'a\'), (1,\'b\')) ])+-- box ('path1' [0,1]) ('path1' [\'a\',\'b\']) == 'edges1' [ ((0,\'a\'), (0,\'b\'))+-- , ((0,\'a\'), (1,\'a\'))+-- , ((0,\'b\'), (1,\'b\'))+-- , ((1,\'a\'), (1,\'b\')) ] -- @ -- Up to an isomorphism between the resulting vertex types, this operation -- is /commutative/, /associative/, /distributes/ over 'overlay', and has@@ -854,12 +897,15 @@ -- 'vertexCount' (box x y) == 'vertexCount' x * 'vertexCount' y -- 'edgeCount' (box x y) <= 'vertexCount' x * 'edgeCount' y + 'edgeCount' x * 'vertexCount' y -- @-box :: NonEmptyGraph a -> NonEmptyGraph b -> NonEmptyGraph (a, b)+box :: Graph a -> Graph b -> Graph (a, b) box x y = overlays1 xs `overlay` overlays1 ys where- xs = fmap (\b -> fmap (,b) x) $ toNonEmpty y- ys = fmap (\a -> fmap (a,) y) $ toNonEmpty x+ xs = fmap (\b -> fmap (,b) x) $ toNonEmptyList y+ ys = fmap (\a -> fmap (a,) y) $ toNonEmptyList x +toNonEmptyList :: Graph a -> NonEmpty a+toNonEmptyList = foldg1 (:| []) (<>) (<>)+ -- | /Sparsify/ a graph by adding intermediate 'Left' @Int@ vertices between the -- original vertices (wrapping the latter in 'Right') such that the resulting -- graph is /sparse/, i.e. contains only O(s) edges, but preserves the@@ -875,7 +921,7 @@ -- 'edgeCount' (sparsify x) <= 3 * 'size' x -- 'size' (sparsify x) <= 3 * 'size' x -- @-sparsify :: NonEmptyGraph a -> NonEmptyGraph (Either Int a)+sparsify :: Graph a -> Graph (Either Int a) sparsify graph = res where (res, end) = runState (foldg1 v o c graph 0 end) 1@@ -885,7 +931,3 @@ m <- get put (m + 1) overlay <$> s `x` m <*> m `y` t---- Shall we export this? I suggest to wait for Foldable1 type class instead.-toNonEmpty :: NonEmptyGraph a -> NonEmpty a-toNonEmpty = foldg1 (:| []) (<>) (<>)
+ src/Algebra/Graph/NonEmpty/AdjacencyMap.hs view
@@ -0,0 +1,568 @@+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.NonEmpty.AdjacencyMap+-- Copyright : (c) Andrey Mokhov 2016-2018+-- License : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability : experimental+--+-- __Alga__ is a library for algebraic construction and manipulation of graphs+-- in Haskell. See <https://github.com/snowleopard/alga-paper this paper> for the+-- motivation behind the library, the underlying theory, and implementation details.+--+-- This module defines the data type 'AdjacencyMap' for graphs that are known+-- to be non-empty at compile time. To avoid name clashes with+-- "Algebra.Graph.AdjacencyMap", this module can be imported qualified:+--+-- @+-- import qualified Algebra.Graph.NonEmpty.AdjacencyMap as NonEmpty+-- @+--+-- The naming convention generally follows that of "Data.List.NonEmpty": we use+-- suffix @1@ to indicate the functions whose interface must be changed compared+-- to "Algebra.Graph.AdjacencyMap", e.g. 'vertices1'.+-----------------------------------------------------------------------------+module Algebra.Graph.NonEmpty.AdjacencyMap (+ -- * Data structure+ AdjacencyMap, toNonEmpty,++ -- * Basic graph construction primitives+ vertex, edge, overlay, connect, vertices1, edges1, overlays1, connects1,++ -- * Relations on graphs+ isSubgraphOf,++ -- * Graph properties+ hasVertex, hasEdge, vertexCount, edgeCount, vertexList1, edgeList,+ vertexSet, edgeSet, preSet, postSet,++ -- * Standard families of graphs+ path1, circuit1, clique1, biclique1, star, stars1, tree,++ -- * Graph transformation+ removeVertex1, removeEdge, replaceVertex, mergeVertices, transpose, gmap,+ induce1,++ -- * Graph closure+ closure, reflexiveClosure, symmetricClosure, transitiveClosure+ ) where++import Prelude hiding (reverse)+import Data.List.NonEmpty (NonEmpty (..), nonEmpty, toList, reverse)+import Data.Maybe+import Data.Set (Set)+import Data.Tree++import Algebra.Graph.NonEmpty.AdjacencyMap.Internal++import qualified Algebra.Graph.AdjacencyMap as AM+import qualified Data.Set as Set++-- Lift a function to non-empty adjacency maps+via :: (AM.AdjacencyMap a -> AM.AdjacencyMap b)+ -> AdjacencyMap a -> AdjacencyMap b+via f = NAM . f . am++-- Lift a two-argument function to non-empty adjacency maps+via2 :: (AM.AdjacencyMap a -> AM.AdjacencyMap b -> AM.AdjacencyMap c)+ -> AdjacencyMap a -> AdjacencyMap b -> AdjacencyMap c+via2 f (NAM x) (NAM y) = NAM (f x y)++-- Lift a list function to non-empty adjacency maps+viaL :: ( [AM.AdjacencyMap a] -> AM.AdjacencyMap b)+ -> NonEmpty ( AdjacencyMap a) -> AdjacencyMap b+viaL f = NAM . f . fmap am . toList++-- Unsafe creation of a NonEmpty list.+unsafeNonEmpty :: [a] -> NonEmpty a+unsafeNonEmpty = fromMaybe (error msg) . nonEmpty+ where+ msg = "Algebra.Graph.AdjacencyMap.unsafeNonEmpty: Graph is empty"++-- | Convert a possibly empty 'AM.AdjacencyMap' into NonEmpty.'AdjacencyMap'.+-- Returns 'Nothing' if the argument is 'AM.empty'.+-- Complexity: /O(1)/ time, memory and size.+--+-- @+-- toNonEmpty 'AM.empty' == Nothing+-- toNonEmpty ('Algebra.Graph.ToGraph.toAdjacencyMap' x) == Just (x :: 'AdjacencyMap' a)+-- @+toNonEmpty :: AM.AdjacencyMap a -> Maybe (AdjacencyMap a)+toNonEmpty x | AM.isEmpty x = Nothing+ | otherwise = Just (NAM x)++-- | Construct the graph comprising /a single isolated vertex/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'AdjacencyMap.hasVertex' x (vertex x) == True+-- 'AdjacencyMap.vertexCount' (vertex x) == 1+-- 'AdjacencyMap.edgeCount' (vertex x) == 0+-- @+vertex :: a -> AdjacencyMap a+vertex = NAM . AM.vertex+{-# NOINLINE [1] vertex #-}++-- | /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.+--+-- @+-- 'hasVertex' z (overlay x y) == 'hasVertex' z x || 'hasVertex' z y+-- 'vertexCount' (overlay x y) >= 'vertexCount' x+-- 'vertexCount' (overlay x y) <= 'vertexCount' x + 'vertexCount' y+-- 'edgeCount' (overlay x y) >= 'edgeCount' x+-- 'edgeCount' (overlay x y) <= 'edgeCount' x + 'edgeCount' y+-- 'vertexCount' (overlay 1 2) == 2+-- 'edgeCount' (overlay 1 2) == 0+-- @+overlay :: Ord a => AdjacencyMap a -> AdjacencyMap a -> AdjacencyMap a+overlay = via2 AM.overlay+{-# NOINLINE [1] overlay #-}++-- | /Connect/ two graphs. This is an 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)/.+--+-- @+-- 'hasVertex' z (connect x y) == 'hasVertex' z x || 'hasVertex' z y+-- 'vertexCount' (connect x y) >= 'vertexCount' x+-- 'vertexCount' (connect x y) <= 'vertexCount' x + 'vertexCount' y+-- 'edgeCount' (connect x y) >= 'edgeCount' x+-- 'edgeCount' (connect x y) >= 'edgeCount' y+-- 'edgeCount' (connect x y) >= 'vertexCount' x * 'vertexCount' y+-- 'edgeCount' (connect x y) <= 'vertexCount' x * 'vertexCount' y + 'edgeCount' x + 'edgeCount' y+-- 'vertexCount' (connect 1 2) == 2+-- 'edgeCount' (connect 1 2) == 1+-- @+connect :: Ord a => AdjacencyMap a -> AdjacencyMap a -> AdjacencyMap a+connect = via2 AM.connect+{-# NOINLINE [1] connect #-}++-- | Construct the graph comprising /a single edge/.+-- Complexity: /O(1)/ time, memory.+--+-- @+-- edge x y == 'connect' ('vertex' x) ('vertex' y)+-- '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 -> AdjacencyMap a+edge x y = NAM (AM.edge x y)++-- | 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.+--+-- @+-- vertices1 [x] == 'vertex' x+-- 'hasVertex' x . vertices1 == 'elem' x+-- 'vertexCount' . vertices1 == 'length' . 'Data.List.NonEmpty.nub'+-- 'vertexSet' . vertices1 == Set.'Set.fromList' . 'Data.List.NonEmpty.toList'+-- @+vertices1 :: Ord a => NonEmpty a -> AdjacencyMap a+vertices1 = NAM . AM.vertices . toList+{-# NOINLINE [1] vertices1 #-}++-- | Construct the graph from a list of edges.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- edges1 [(x,y)] == 'edge' x y+-- 'edgeCount' . edges1 == 'Data.List.NonEmpty.length' . 'Data.List.NonEmpty.nub'+-- @+edges1 :: Ord a => NonEmpty (a, a) -> AdjacencyMap a+edges1 = NAM . AM.edges . toList++-- | Overlay a given list of graphs.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- overlays1 [x] == x+-- overlays1 [x,y] == 'overlay' x y+-- @+overlays1 :: Ord a => NonEmpty (AdjacencyMap a) -> AdjacencyMap a+overlays1 = viaL AM.overlays+{-# NOINLINE overlays1 #-}++-- | Connect a given list of graphs.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- connects1 [x] == x+-- connects1 [x,y] == 'connect' x y+-- @+connects1 :: Ord a => NonEmpty (AdjacencyMap a) -> AdjacencyMap a+connects1 = viaL AM.connects+{-# NOINLINE connects1 #-}++-- | 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 x ('overlay' x y) == True+-- isSubgraphOf ('overlay' x y) ('connect' x y) == True+-- isSubgraphOf ('path1' xs) ('circuit1' xs) == True+-- isSubgraphOf x y ==> x <= y+-- @+isSubgraphOf :: Ord a => AdjacencyMap a -> AdjacencyMap a -> Bool+isSubgraphOf (NAM x) (NAM y) = AM.isSubgraphOf x y++-- | Check if a graph contains a given vertex.+-- Complexity: /O(log(n))/ time.+--+-- @+-- hasVertex x ('vertex' x) == True+-- hasVertex 1 ('vertex' 2) == False+-- @+hasVertex :: Ord a => a -> AdjacencyMap a -> Bool+hasVertex x = AM.hasVertex x . am++-- | Check if a graph contains a given edge.+-- Complexity: /O(log(n))/ time.+--+-- @+-- hasEdge x y ('vertex' z) == False+-- hasEdge x y ('edge' x y) == True+-- hasEdge x y . 'removeEdge' x y == 'const' False+-- hasEdge x y == 'elem' (x,y) . 'edgeList'+-- @+hasEdge :: Ord a => a -> a -> AdjacencyMap a -> Bool+hasEdge x y = AM.hasEdge x y . am++-- | The number of vertices in a graph.+-- Complexity: /O(1)/ time.+--+-- @+-- vertexCount ('vertex' x) == 1+-- vertexCount == 'length' . 'vertexList'+-- vertexCount x \< vertexCount y ==> x \< y+-- @+vertexCount :: AdjacencyMap a -> Int+vertexCount = AM.vertexCount . am++-- | The number of edges in a graph.+-- Complexity: /O(n)/ time.+--+-- @+-- edgeCount ('vertex' x) == 0+-- edgeCount ('edge' x y) == 1+-- edgeCount == 'length' . 'edgeList'+-- @+edgeCount :: AdjacencyMap a -> Int+edgeCount = AM.edgeCount . am++-- | The sorted list of vertices of a given graph.+-- Complexity: /O(n)/ time and memory.+--+-- @+-- vertexList1 ('vertex' x) == [x]+-- vertexList1 . 'vertices1' == 'Data.List.NonEmpty.nub' . 'Data.List.NonEmpty.sort'+-- @+vertexList1 :: AdjacencyMap a -> NonEmpty a+vertexList1 = unsafeNonEmpty . AM.vertexList . am++-- | The sorted list of edges of a graph.+-- Complexity: /O(n + m)/ time and /O(m)/ memory.+--+-- @+-- edgeList ('vertex' x) == []+-- edgeList ('edge' x y) == [(x,y)]+-- edgeList ('star' 2 [3,1]) == [(2,1), (2,3)]+-- edgeList . 'edges' == 'Data.List.NonEmpty.nub' . 'Data.List.sort'+-- edgeList . 'transpose' == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . edgeList+-- @+edgeList :: AdjacencyMap a -> [(a, a)]+edgeList = AM.edgeList . am++-- | The set of vertices of a given graph.+-- Complexity: /O(n)/ time and memory.+--+-- @+-- vertexSet . 'vertex' == Set.'Set.singleton'+-- vertexSet . 'vertices1' == Set.'Set.fromList' . 'Data.List.NonEmpty.toList'+-- vertexSet . 'clique1' == Set.'Set.fromList' . 'Data.List.NonEmpty.toList'+-- @+vertexSet :: AdjacencyMap a -> Set a+vertexSet = AM.vertexSet . am++-- | The set of edges of a given graph.+-- Complexity: /O((n + m) * log(m))/ time and /O(m)/ memory.+--+-- @+-- edgeSet ('vertex' x) == Set.'Set.empty'+-- edgeSet ('edge' x y) == Set.'Set.singleton' (x,y)+-- edgeSet . 'edges' == Set.'Set.fromList'+-- @+edgeSet :: Ord a => AdjacencyMap a -> Set (a, a)+edgeSet = Set.fromAscList . edgeList++-- | The /preset/ of an element @x@ is the set of its /direct predecessors/.+-- Complexity: /O(n * log(n))/ time and /O(n)/ memory.+--+-- @+-- preSet x ('vertex' x) == Set.'Set.empty'+-- preSet 1 ('edge' 1 2) == Set.'Set.empty'+-- preSet y ('edge' x y) == Set.'Set.fromList' [x]+-- @+preSet :: Ord a => a -> AdjacencyMap a -> Set.Set a+preSet x = AM.preSet x . am++-- | The /postset/ of a vertex is the set of its /direct successors/.+-- Complexity: /O(log(n))/ time and /O(1)/ memory.+--+-- @+-- postSet x ('vertex' x) == Set.'Set.empty'+-- postSet x ('edge' x y) == Set.'Set.fromList' [y]+-- postSet 2 ('edge' 1 2) == Set.'Set.empty'+-- @+postSet :: Ord a => a -> AdjacencyMap a -> Set a+postSet x = AM.postSet x . am++-- | The /path/ on a list of vertices.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- path1 [x] == 'vertex' x+-- path1 [x,y] == 'edge' x y+-- path1 . 'Data.List.NonEmpty.reverse' == 'transpose' . path1+-- @+path1 :: Ord a => NonEmpty a -> AdjacencyMap a+path1 = NAM . AM.path . toList++-- | The /circuit/ on a list of vertices.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- circuit1 [x] == 'edge' x x+-- circuit1 [x,y] == 'edges1' [(x,y), (y,x)]+-- circuit1 . 'Data.List.NonEmpty.reverse' == 'transpose' . circuit1+-- @+circuit1 :: Ord a => NonEmpty a -> AdjacencyMap a+circuit1 = NAM . AM.circuit . toList++-- | The /clique/ on a list of vertices.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- clique1 [x] == 'vertex' x+-- clique1 [x,y] == 'edge' x y+-- clique1 [x,y,z] == 'edges1' [(x,y), (x,z), (y,z)]+-- clique1 (xs '<>' ys) == 'connect' (clique1 xs) (clique1 ys)+-- clique1 . 'Data.List.NonEmpty.reverse' == 'transpose' . clique1+-- @+clique1 :: Ord a => NonEmpty a -> AdjacencyMap a+clique1 = NAM . AM.clique . toList+{-# NOINLINE [1] clique1 #-}++-- | The /biclique/ on two lists of vertices.+-- Complexity: /O(n * log(n) + m)/ time and /O(n + m)/ memory.+--+-- @+-- biclique1 [x1,x2] [y1,y2] == 'edges1' [(x1,y1), (x1,y2), (x2,y1), (x2,y2)]+-- biclique1 xs ys == 'connect' ('vertices1' xs) ('vertices1' ys)+-- @+biclique1 :: Ord a => NonEmpty a -> NonEmpty a -> AdjacencyMap a+biclique1 xs ys = NAM $ AM.biclique (toList xs) (toList ys)++-- 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] == 'edges1' [(x,y), (x,z)]+-- @+star :: Ord a => a -> [a] -> AdjacencyMap a+star x = NAM . AM.star x+{-# INLINE star #-}++-- | 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.+--+-- @+-- stars1 [(x, [] )] == 'vertex' x+-- stars1 [(x, [y])] == 'edge' x y+-- stars1 [(x, ys )] == 'star' x ys+-- stars1 == 'overlays1' . 'fmap' ('uncurry' 'star')+-- 'overlay' (stars1 xs) (stars1 ys) == stars1 (xs '<>' ys)+-- @+stars1 :: Ord a => NonEmpty (a, [a]) -> AdjacencyMap a+stars1 = NAM . AM.stars . toList++-- | The /tree graph/ constructed from a given '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 []]]) == 'path1' [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 []]]) == 'edges1' [(1,2), (1,3), (3,4), (3,5)]+-- @+tree :: Ord a => Tree a -> AdjacencyMap a+tree = NAM . AM.tree++-- | Remove a vertex from a given graph.+-- Complexity: /O(n*log(n))/ time.+--+-- @+-- removeVertex1 x ('vertex' x) == Nothing+-- removeVertex1 1 ('vertex' 2) == Just ('vertex' 2)+-- removeVertex1 x ('edge' x x) == Nothing+-- removeVertex1 1 ('edge' 1 2) == Just ('vertex' 2)+-- removeVertex1 x 'Control.Monad.>=>' removeVertex1 x == removeVertex1 x+-- @+removeVertex1 :: Ord a => a -> AdjacencyMap a -> Maybe (AdjacencyMap a)+removeVertex1 x = toNonEmpty . AM.removeVertex x . am++-- | Remove an edge from a given graph.+-- Complexity: /O(log(n))/ time.+--+-- @+-- removeEdge x y ('edge' x y) == 'vertices1' [x,y]+-- removeEdge x y . removeEdge x y == removeEdge x y+-- 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 -> AdjacencyMap a -> AdjacencyMap a+removeEdge x y = via (AM.removeEdge x y)++-- | The function @'replaceVertex' x y@ replaces vertex @x@ with vertex @y@ in a+-- given 'AdjacencyMap'. 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 -> AdjacencyMap a -> AdjacencyMap a+replaceVertex u v = via (AM.replaceVertex u v)++-- | 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 -> AdjacencyMap a -> AdjacencyMap a+mergeVertices p v = via (AM.mergeVertices p v)++-- | Transpose a given graph.+-- Complexity: /O(m * log(n))/ time, /O(n + m)/ memory.+--+-- @+-- transpose ('vertex' x) == 'vertex' x+-- transpose ('edge' x y) == 'edge' y x+-- transpose . transpose == id+-- 'edgeList' . transpose == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . 'edgeList'+-- @+transpose :: Ord a => AdjacencyMap a -> AdjacencyMap a+transpose = via AM.transpose+{-# NOINLINE [1] transpose #-}++{-# RULES+"transpose/vertex" forall x. transpose (vertex x) = vertex x+"transpose/overlay" forall g1 g2. transpose (overlay g1 g2) = overlay (transpose g1) (transpose g2)+"transpose/connect" forall g1 g2. transpose (connect g1 g2) = connect (transpose g2) (transpose g1)++"transpose/overlays1" forall xs. transpose (overlays1 xs) = overlays1 (fmap transpose xs)+"transpose/connects1" forall xs. transpose (connects1 xs) = connects1 (reverse (fmap transpose xs))++"transpose/vertices1" forall xs. transpose (vertices1 xs) = vertices1 xs+"transpose/clique1" forall xs. transpose (clique1 xs) = clique1 (reverse xs)+ #-}++-- | 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+-- 'AdjacencyMap'.+-- Complexity: /O((n + m) * log(n))/ time.+--+-- @+-- 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 a, Ord b) => (a -> b) -> AdjacencyMap a -> AdjacencyMap b+gmap f = via (AM.gmap f)++-- | 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.+--+-- @+-- induce1 ('const' True ) x == Just x+-- induce1 ('const' False) x == Nothing+-- induce1 (/= x) == 'removeVertex1' x+-- induce1 p 'Control.Monad.>=>' induce1 q == induce1 (\\x -> p x && q x)+-- @+induce1 :: (a -> Bool) -> AdjacencyMap a -> Maybe (AdjacencyMap a)+induce1 p = toNonEmpty . AM.induce p . am++-- | Compute the /reflexive and transitive closure/ of a graph.+-- Complexity: /O(n * m * log(n)^2)/ time.+--+-- @+-- closure ('vertex' x) == 'edge' x x+-- closure ('edge' x x) == 'edge' x x+-- closure ('edge' x y) == 'edges1' [(x,x), (x,y), (y,y)]+-- closure ('path1' $ 'Data.List.NonEmpty.nub' xs) == 'reflexiveClosure' ('clique1' $ 'Data.List.NonEmpty.nub' xs)+-- closure == 'reflexiveClosure' . 'transitiveClosure'+-- closure == 'transitiveClosure' . 'reflexiveClosure'+-- closure . closure == closure+-- 'postSet' x (closure y) == Set.'Set.fromList' ('Algebra.Graph.ToGraph.reachable' x y)+-- @+closure :: Ord a => AdjacencyMap a -> AdjacencyMap a+closure = via (AM.closure)++-- | Compute the /reflexive closure/ of a graph by adding a self-loop to every+-- vertex.+-- Complexity: /O(n * log(n))/ time.+--+-- @+-- reflexiveClosure ('vertex' x) == 'edge' x x+-- reflexiveClosure ('edge' x x) == 'edge' x x+-- reflexiveClosure ('edge' x y) == 'edges1' [(x,x), (x,y), (y,y)]+-- reflexiveClosure . reflexiveClosure == reflexiveClosure+-- @+reflexiveClosure :: Ord a => AdjacencyMap a -> AdjacencyMap a+reflexiveClosure = via AM.reflexiveClosure++-- | Compute the /symmetric closure/ of a graph by overlaying it with its own+-- transpose.+-- Complexity: /O((n + m) * log(n))/ time.+--+-- @+-- symmetricClosure ('vertex' x) == 'vertex' x+-- symmetricClosure ('edge' x y) == 'edges1' [(x,y), (y,x)]+-- symmetricClosure x == 'overlay' x ('transpose' x)+-- symmetricClosure . symmetricClosure == symmetricClosure+-- @+symmetricClosure :: Ord a => AdjacencyMap a -> AdjacencyMap a+symmetricClosure = via AM.symmetricClosure++-- | Compute the /transitive closure/ of a graph.+-- Complexity: /O(n * m * log(n)^2)/ time.+--+-- @+-- transitiveClosure ('vertex' x) == 'vertex' x+-- transitiveClosure ('edge' x y) == 'edge' x y+-- transitiveClosure ('path1' $ 'Data.List.NonEmpty.nub' xs) == 'clique1' ('Data.List.NonEmpty.nub' xs)+-- transitiveClosure . transitiveClosure == transitiveClosure+-- @+transitiveClosure :: Ord a => AdjacencyMap a -> AdjacencyMap a+transitiveClosure = via AM.transitiveClosure
+ src/Algebra/Graph/NonEmpty/AdjacencyMap/Internal.hs view
@@ -0,0 +1,163 @@+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.NonEmpty.AdjacencyMap.Internal+-- Copyright : (c) Andrey Mokhov 2016-2018+-- License : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability : experimental+--+-- This module exposes the implementation of non-empty adjacency maps. The API+-- is unstable and unsafe, and is exposed only for documentation. You should use+-- the non-internal module "Algebra.Graph.NonEmpty.AdjacencyMap" instead.+-----------------------------------------------------------------------------+module Algebra.Graph.NonEmpty.AdjacencyMap.Internal (+ -- * Adjacency map implementation+ AdjacencyMap (..), consistent+ ) where++import Control.DeepSeq+import Data.List++import qualified Algebra.Graph.AdjacencyMap as AM+import qualified Algebra.Graph.AdjacencyMap.Internal as AM+import qualified Data.Map.Strict as Map+import qualified Data.Set as Set++{-| The 'AdjacencyMap' data type represents a graph by a map of vertices to+their adjacency sets. We define a 'Num' instance as a convenient notation for+working with graphs:++ > 0 == vertex 0+ > 1 + 2 == overlay (vertex 1) (vertex 2)+ > 1 * 2 == connect (vertex 1) (vertex 2)+ > 1 + 2 * 3 == overlay (vertex 1) (connect (vertex 2) (vertex 3))+ > 1 * (2 + 3) == connect (vertex 1) (overlay (vertex 2) (vertex 3))++__Note:__ the 'signum' method of the type class 'Num' cannot be implemented and+will throw an error. Furthermore, the 'Num' instance does not satisfy several+"customary laws" of 'Num', which dictate that 'fromInteger' @0@ and+'fromInteger' @1@ should act as additive and multiplicative identities, and+'negate' as additive inverse. Nevertheless, overloading 'fromInteger', '+' and+'*' is very convenient when working with algebraic graphs; we hope that in+future Haskell's Prelude will provide a more fine-grained class hierarchy for+algebraic structures, which we would be able to utilise without violating any+laws.++The 'Show' instance is defined using basic graph construction primitives:++@show (1 :: AdjacencyMap Int) == "vertex 1"+show (1 + 2 :: AdjacencyMap Int) == "vertices1 [1,2]"+show (1 * 2 :: AdjacencyMap Int) == "edge 1 2"+show (1 * 2 * 3 :: AdjacencyMap Int) == "edges1 [(1,2),(1,3),(2,3)]"+show (1 * 2 + 3 :: AdjacencyMap Int) == "overlay (vertex 3) (edge 1 2)"@++The 'Eq' instance satisfies the following laws of algebraic graphs:++ * 'Algebra.Graph.NonEmpty.AdjacencyMap.overlay' is commutative, associative and idempotent:++ > x + y == y + x+ > x + (y + z) == (x + y) + z+ > x + x == x++ * 'Algebra.Graph.NonEmpty.AdjacencyMap.connect' is associative:++ > x * (y * z) == (x * y) * z++ * 'Algebra.Graph.NonEmpty.AdjacencyMap.connect' distributes over 'Algebra.Graph.NonEmpty.AdjacencyMap.overlay':++ > x * (y + z) == x * y + x * z+ > (x + y) * z == x * z + y * z++ * 'Algebra.Graph.NonEmpty.AdjacencyMap.connect' can be decomposed:++ > x * y * z == x * y + x * z + y * z++ * 'Algebra.Graph.NonEmpty.AdjacencyMap.connect' satisfies absorption and saturation:++ > x * y + x + y == x * y+ > x * x * x == x * x++When specifying the time and memory complexity of graph algorithms, /n/ and /m/+will denote the number of vertices and edges in the graph, respectively.++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:++@'Algebra.Graph.NonEmpty.AdjacencyMap.vertex' 1 < 'Algebra.Graph.NonEmpty.AdjacencyMap.vertex' 2+'Algebra.Graph.NonEmpty.AdjacencyMap.vertex' 3 < 'Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 2+'Algebra.Graph.NonEmpty.AdjacencyMap.vertex' 1 < 'Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 1+'Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 1 < 'Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 2+'Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 2 < 'Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 1 + 'Algebra.Graph.NonEmpty.AdjacencyMap.edge' 2 2+'Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 2 < 'Algebra.Graph.NonEmpty.AdjacencyMap.edge' 1 3@++Note that the resulting order refines the+'Algebra.Graph.NonEmpty.AdjacencyMap.isSubgraphOf' relation and is compatible+with 'Algebra.Graph.NonEmpty.AdjacencyMap.overlay' and+'Algebra.Graph.NonEmpty.AdjacencyMap.connect' operations:++@'Algebra.Graph.NonEmpty.AdjacencyMap.isSubgraphOf' x y ==> x <= y@++@x <= x + y+x + y <= x * y@+-}+newtype AdjacencyMap a = NAM {+ -- | The /adjacency map/ of a graph: each vertex is associated with a set of+ -- its direct successors. Complexity: /O(1)/ time and memory.+ --+ -- @+ -- adjacencyMap ('vertex' x) == Map.'Map.singleton' x Set.'Set.empty'+ -- 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)++-- | __Note:__ this does not satisfy the usual ring laws; see 'AdjacencyMap' for+-- more details.+instance (Ord a, Num a) => Num (AdjacencyMap a) where+ fromInteger = NAM . AM.vertex . fromInteger+ NAM x + NAM y = NAM (AM.overlay x y)+ NAM x * NAM y = NAM (AM.connect x y)+ signum = error "NonEmpty.AdjacencyMap.signum cannot be implemented."+ abs = id+ negate = id++instance (Ord a, Show a) => Show (AdjacencyMap a) where+ showsPrec p (NAM (AM.AM m))+ | null vs = error "NonEmpty.AdjacencyMap.Show: Graph is empty"+ | null es = showParen (p > 10) $ vshow vs+ | vs == used = showParen (p > 10) $ eshow es+ | otherwise = showParen (p > 10) $+ showString "overlay (" . vshow (vs \\ used) .+ showString ") (" . eshow es . showString ")"+ where+ vs = Set.toAscList (Map.keysSet m)+ es = AM.internalEdgeList m+ vshow [x] = showString "vertex " . showsPrec 11 x+ vshow xs = showString "vertices1 " . showsPrec 11 xs+ eshow [(x, y)] = showString "edge " . showsPrec 11 x .+ showString " " . showsPrec 11 y+ eshow xs = showString "edges1 " . showsPrec 11 xs+ used = Set.toAscList (AM.referredToVertexSet m)++-- | Check if the internal graph representation is consistent, i.e. that all+-- edges refer to existing vertices, and the graph is non-empty. It should be+-- impossible to create an inconsistent adjacency map, and we use this function+-- in testing.+-- /Note: this function is for internal use only/.+--+-- @+-- consistent ('vertex' x) == True+-- consistent ('overlay' x y) == True+-- consistent ('connect' x y) == True+-- consistent ('Algebra.Graph.NonEmpty.AdjacencyMap.edge' x y) == True+-- consistent ('Algebra.Graph.NonEmpty.AdjacencyMap.edges' xs) == True+-- consistent ('Algebra.Graph.NonEmpty.AdjacencyMap.stars' xs) == True+-- @+consistent :: Ord a => AdjacencyMap a -> Bool+consistent (NAM x) = AM.consistent x && not (AM.isEmpty x)
src/Algebra/Graph/Relation.hs view
@@ -26,7 +26,7 @@ -- * Graph properties isEmpty, hasVertex, hasEdge, vertexCount, edgeCount, vertexList, edgeList,- adjacencyList, vertexSet, vertexIntSet, edgeSet, preSet, postSet,+ adjacencyList, vertexSet, edgeSet, preSet, postSet, -- * Standard families of graphs path, circuit, clique, biclique, star, stars, tree, forest,@@ -34,8 +34,8 @@ -- * Graph transformation removeVertex, removeEdge, replaceVertex, mergeVertices, transpose, gmap, induce, - -- * Operations on binary relations- compose, reflexiveClosure, symmetricClosure, transitiveClosure, preorderClosure+ -- * Relational operations+ compose, closure, reflexiveClosure, symmetricClosure, transitiveClosure ) where import Prelude ()@@ -46,7 +46,6 @@ import Algebra.Graph.Relation.Internal -import qualified Data.IntSet as IntSet import qualified Data.Set as Set import qualified Data.Tree as Tree @@ -119,14 +118,16 @@ -- 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 '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 x y ==> x <= y -- @ isSubgraphOf :: Ord a => Relation a -> Relation a -> Bool-isSubgraphOf x y = domain x `Set.isSubsetOf` domain y && relation x `Set.isSubsetOf` relation y+isSubgraphOf x y = domain x `Set.isSubsetOf` domain y+ && relation x `Set.isSubsetOf` relation y -- | Check if a relation is empty. -- Complexity: /O(1)/ time.@@ -148,7 +149,7 @@ -- hasVertex x 'empty' == False -- hasVertex x ('vertex' x) == True -- hasVertex 1 ('vertex' 2) == False--- hasVertex x . 'removeVertex' x == const False+-- hasVertex x . 'removeVertex' x == 'const' False -- @ hasVertex :: Ord a => a -> Relation a -> Bool hasVertex x = Set.member x . domain@@ -160,7 +161,7 @@ -- hasEdge x y 'empty' == False -- hasEdge x y ('vertex' z) == False -- hasEdge x y ('edge' x y) == True--- hasEdge x y . 'removeEdge' x y == const False+-- hasEdge x y . 'removeEdge' x y == 'const' False -- hasEdge x y == 'elem' (x,y) . 'edgeList' -- @ hasEdge :: Ord a => a -> a -> Relation a -> Bool@@ -170,9 +171,10 @@ -- Complexity: /O(1)/ time. -- -- @--- vertexCount 'empty' == 0--- vertexCount ('vertex' x) == 1--- vertexCount == 'length' . 'vertexList'+-- vertexCount 'empty' == 0+-- vertexCount ('vertex' x) == 1+-- vertexCount == 'length' . 'vertexList'+-- vertexCount x \< vertexCount y ==> x \< y -- @ vertexCount :: Relation a -> Int vertexCount = Set.size . domain@@ -209,7 +211,7 @@ -- edgeList ('edge' x y) == [(x,y)] -- edgeList ('star' 2 [3,1]) == [(2,1), (2,3)] -- edgeList . 'edges' == 'Data.List.nub' . 'Data.List.sort'--- edgeList . 'transpose' == 'Data.List.sort' . map 'Data.Tuple.swap' . edgeList+-- edgeList . 'transpose' == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . edgeList -- @ edgeList :: Relation a -> [(a, a)] edgeList = Set.toAscList . relation@@ -221,24 +223,10 @@ -- vertexSet 'empty' == Set.'Set.empty' -- vertexSet . 'vertex' == Set.'Set.singleton' -- vertexSet . 'vertices' == Set.'Set.fromList'--- vertexSet . 'clique' == Set.'Set.fromList' -- @ vertexSet :: Relation a -> Set.Set a vertexSet = domain --- | The set of vertices of a given graph. Like 'vertexSet' but specialised for--- graphs with vertices of type 'Int'.--- Complexity: /O(n)/ time.------ @--- vertexIntSet 'empty' == IntSet.'IntSet.empty'--- vertexIntSet . 'vertex' == IntSet.'IntSet.singleton'--- vertexIntSet . 'vertices' == IntSet.'IntSet.fromList'--- vertexIntSet . 'clique' == IntSet.'IntSet.fromList'--- @-vertexIntSet :: Relation Int -> IntSet.IntSet-vertexIntSet = IntSet.fromAscList . vertexList- -- | The set of edges of a given graph. -- Complexity: /O(1)/ time. --@@ -382,7 +370,7 @@ -- stars [(x, [])] == 'vertex' x -- stars [(x, [y])] == 'edge' x y -- stars [(x, ys)] == 'star' x ys--- stars == 'overlays' . map (uncurry 'star')+-- stars == 'overlays' . 'map' ('uncurry' 'star') -- stars . 'adjacencyList' == id -- 'overlay' (stars xs) (stars ys) == stars (xs ++ ys) -- @@@ -413,7 +401,7 @@ -- 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 == 'overlays' . 'map' 'tree' -- @ forest :: Ord a => Tree.Forest a -> Relation a forest = overlays. map tree@@ -463,10 +451,10 @@ -- /O(1)/ to be evaluated. -- -- @--- mergeVertices (const False) x == id+-- 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 '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@@ -479,7 +467,7 @@ -- transpose ('vertex' x) == 'vertex' x -- transpose ('edge' x y) == 'edge' y x -- transpose . transpose == id--- 'edgeList' . transpose == 'Data.List.sort' . map 'Data.Tuple.swap' . 'edgeList'+-- 'edgeList' . transpose == 'Data.List.sort' . 'map' 'Data.Tuple.swap' . 'edgeList' -- @ transpose :: Ord a => Relation a -> Relation a transpose (Relation d r) = Relation d (Set.map swap r)@@ -505,8 +493,8 @@ -- be evaluated. -- -- @--- induce (const True ) x == x--- induce (const False) x == 'empty'+-- 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@@ -516,55 +504,85 @@ where pp (x, y) = p x && p y --- | /Compose/ two relations: @R = 'compose' Q P@. Two elements @x@ and @y@ are--- related in the resulting relation, i.e. @xRy@, if there exists an element @z@,--- such that @xPz@ and @zQy@. This is an associative operation which has 'empty'--- as the /annihilating zero/.+-- | Left-to-right /relational composition/ of graphs: vertices @x@ and @z@ are+-- connected in the resulting graph if there is a vertex @y@, such that @x@ is+-- connected to @y@ in the first graph, and @y@ is connected to @z@ in the+-- second graph. There are no isolated vertices in the result. This operation is+-- associative, has 'empty' and single-'vertex' graphs as /annihilating zeroes/,+-- and distributes over 'overlay'. -- Complexity: /O(n * m * log(m))/ time and /O(n + m)/ memory. -- -- @ -- compose 'empty' x == 'empty' -- compose x 'empty' == 'empty'+-- compose ('vertex' x) y == 'empty'+-- compose x ('vertex' y) == 'empty' -- compose x (compose y z) == compose (compose x y) z--- compose ('edge' y z) ('edge' x y) == 'edge' x z--- compose ('path' [1..5]) ('path' [1..5]) == 'edges' [(1,3),(2,4),(3,5)]+-- compose x ('overlay' y z) == 'overlay' (compose x y) (compose x z)+-- compose ('overlay' x y) z == 'overlay' (compose x z) (compose y z)+-- compose ('edge' x y) ('edge' y z) == 'edge' x z+-- compose ('path' [1..5]) ('path' [1..5]) == 'edges' [(1,3), (2,4), (3,5)] -- compose ('circuit' [1..5]) ('circuit' [1..5]) == 'circuit' [1,3,5,2,4] -- @ compose :: Ord a => Relation a -> Relation a -> Relation a compose x y = Relation (referredToVertexSet r) r where d = domain x `Set.union` domain y- r = Set.unions [ preSet z y `setProduct` postSet z x | z <- Set.toAscList d ]+ r = Set.unions [ preSet v x `setProduct` postSet v y | v <- Set.toAscList d ] --- | Compute the /reflexive closure/ of a 'Relation'.+-- | Compute the /reflexive and transitive closure/ of a graph.+-- Complexity: /O(n * m * log(n) * log(m))/ time.+--+-- @+-- closure 'empty' == 'empty'+-- closure ('vertex' x) == 'edge' x x+-- closure ('edge' x x) == 'edge' x x+-- closure ('edge' x y) == 'edges' [(x,x), (x,y), (y,y)]+-- closure ('path' $ 'Data.List.nub' xs) == 'reflexiveClosure' ('clique' $ 'Data.List.nub' xs)+-- closure == 'reflexiveClosure' . 'transitiveClosure'+-- closure == 'transitiveClosure' . 'reflexiveClosure'+-- closure . closure == closure+-- 'postSet' x (closure y) == Set.'Set.fromList' ('Algebra.Graph.ToGraph.reachable' x y)+-- @+closure :: Ord a => Relation a -> Relation a+closure = reflexiveClosure . transitiveClosure++-- | Compute the /reflexive closure/ of a graph. -- Complexity: /O(n * log(m))/ time. -- -- @--- reflexiveClosure 'empty' == 'empty'--- reflexiveClosure ('vertex' x) == 'edge' x x+-- reflexiveClosure 'empty' == 'empty'+-- reflexiveClosure ('vertex' x) == 'edge' x x+-- reflexiveClosure ('edge' x x) == 'edge' x x+-- reflexiveClosure ('edge' x y) == 'edges' [(x,x), (x,y), (y,y)]+-- reflexiveClosure . reflexiveClosure == reflexiveClosure -- @ reflexiveClosure :: Ord a => Relation a -> Relation a reflexiveClosure (Relation d r) = Relation d $ r `Set.union` Set.fromDistinctAscList [ (a, a) | a <- Set.toAscList d ] --- | Compute the /symmetric closure/ of a 'Relation'.+-- | Compute the /symmetric closure/ of a graph. -- Complexity: /O(m * log(m))/ time. -- -- @--- symmetricClosure 'empty' == 'empty'--- symmetricClosure ('vertex' x) == 'vertex' x--- symmetricClosure ('edge' x y) == 'edges' [(x, y), (y, x)]+-- symmetricClosure 'empty' == 'empty'+-- symmetricClosure ('vertex' x) == 'vertex' x+-- symmetricClosure ('edge' x y) == 'edges' [(x,y), (y,x)]+-- symmetricClosure x == 'overlay' x ('transpose' x)+-- symmetricClosure . symmetricClosure == symmetricClosure -- @ symmetricClosure :: Ord a => Relation a -> Relation a symmetricClosure (Relation d r) = Relation d $ r `Set.union` Set.map swap r --- | Compute the /transitive closure/ of a 'Relation'.+-- | Compute the /transitive closure/ of a graph. -- Complexity: /O(n * m * log(n) * log(m))/ time. -- -- @--- transitiveClosure 'empty' == 'empty'--- transitiveClosure ('vertex' x) == 'vertex' x--- transitiveClosure ('path' $ 'Data.List.nub' xs) == 'clique' ('Data.List.nub' xs)+-- transitiveClosure 'empty' == 'empty'+-- transitiveClosure ('vertex' x) == 'vertex' x+-- transitiveClosure ('edge' x y) == 'edge' x y+-- transitiveClosure ('path' $ 'Data.List.nub' xs) == 'clique' ('Data.List.nub' xs)+-- transitiveClosure . transitiveClosure == transitiveClosure -- @ transitiveClosure :: Ord a => Relation a -> Relation a transitiveClosure old@@ -572,14 +590,3 @@ | otherwise = transitiveClosure new where new = overlay old (old `compose` old)---- | Compute the /preorder closure/ of a 'Relation'.--- Complexity: /O(n * m * log(m))/ time.------ @--- preorderClosure 'empty' == 'empty'--- preorderClosure ('vertex' x) == 'edge' x x--- preorderClosure ('path' $ 'Data.List.nub' xs) == 'reflexiveClosure' ('clique' $ 'Data.List.nub' xs)--- @-preorderClosure :: Ord a => Relation a -> Relation a-preorderClosure = reflexiveClosure . transitiveClosure
src/Algebra/Graph/Relation/Internal.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module : Algebra.Graph.Relation.Internal@@ -14,13 +15,15 @@ -- * Binary relation implementation Relation (..), empty, vertex, overlay, connect, setProduct, consistent, referredToVertexSet- ) where+ ) where +import Control.DeepSeq (NFData, rnf)+import Data.Monoid (mconcat) import Data.Set (Set, union) -import qualified Data.Set as Set+import Algebra.Graph.Internal -import Control.DeepSeq (NFData, rnf)+import qualified Data.Set as Set {-| The 'Relation' data type represents a graph as a /binary relation/. We define a 'Num' instance as a convenient notation for working with graphs:@@ -31,6 +34,14 @@ > 1 + 2 * 3 == overlay (vertex 1) (connect (vertex 2) (vertex 3)) > 1 * (2 + 3) == connect (vertex 1) (overlay (vertex 2) (vertex 3)) +__Note:__ the 'Num' instance does not satisfy several "customary laws" of 'Num',+which dictate that 'fromInteger' @0@ and 'fromInteger' @1@ should act as+additive and multiplicative identities, and 'negate' as additive inverse.+Nevertheless, overloading 'fromInteger', '+' and '*' is very convenient when+working with algebraic graphs; we hope that in future Haskell's Prelude will+provide a more fine-grained class hierarchy for algebraic structures, which we+would be able to utilise without violating any laws.+ The 'Show' instance is defined using basic graph construction primitives: @show (empty :: Relation Int) == "empty"@@ -80,6 +91,31 @@ When specifying the time and memory complexity of graph algorithms, /n/ and /m/ will denote the number of vertices and edges in the graph, respectively.++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.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@++Note that the resulting order refines the 'isSubgraphOf' relation and is+compatible with 'overlay' and 'connect' operations:++@'Algebra.Graph.AdjacencyMap.isSubgraphOf' x y ==> x <= y@++@'empty' <= x+x <= x + y+x + y <= x * y@ -} data Relation a = Relation { -- | The /domain/ of the relation.@@ -90,19 +126,30 @@ } deriving Eq instance (Ord a, Show a) => Show (Relation a) where- show (Relation d r)- | Set.null d = "empty"- | Set.null r = vshow (Set.toAscList d)- | d == used = eshow (Set.toAscList r)- | otherwise = "overlay (" ++ vshow (Set.toAscList $ Set.difference d used)- ++ ") (" ++ eshow (Set.toAscList r) ++ ")"+ showsPrec p (Relation d r)+ | Set.null d = showString "empty"+ | Set.null r = showParen (p > 10) $ vshow (Set.toAscList d)+ | d == used = showParen (p > 10) $ eshow (Set.toAscList r)+ | otherwise = showParen (p > 10) $+ showString "overlay (" .+ vshow (Set.toAscList $ Set.difference d used) .+ showString ") (" . eshow (Set.toAscList r) .+ showString ")" where- vshow [x] = "vertex " ++ show x- vshow xs = "vertices " ++ show xs- eshow [(x, y)] = "edge " ++ show x ++ " " ++ show y- eshow xs = "edges " ++ show xs+ vshow [x] = showString "vertex " . showsPrec 11 x+ vshow xs = showString "vertices " . showsPrec 11 xs+ eshow [(x, y)] = showString "edge " . showsPrec 11 x .+ showString " " . showsPrec 11 y+ eshow xs = showString "edges " . showsPrec 11 xs used = referredToVertexSet r +instance Ord a => Ord (Relation a) where+ compare x y = mconcat+ [ compare (Set.size $ domain x) (Set.size $ domain y)+ , compare ( domain x) ( domain y)+ , compare (Set.size $ relation x) (Set.size $ relation y)+ , compare ( relation x) ( relation y) ]+ -- | Construct the /empty graph/. -- Complexity: /O(1)/ time and memory. --@@ -169,10 +216,8 @@ instance NFData a => NFData (Relation a) where rnf (Relation d r) = rnf d `seq` rnf r `seq` () --- | Compute the Cartesian product of two sets. /Note: this function is for internal use only/.-setProduct :: Set a -> Set b -> Set (a, b)-setProduct x y = Set.fromDistinctAscList [ (a, b) | a <- Set.toAscList x, b <- Set.toAscList y ]-+-- | __Note:__ this does not satisfy the usual ring laws; see 'Relation' for+-- more details. instance (Ord a, Num a) => Num (Relation a) where fromInteger = vertex . fromInteger (+) = overlay
src/Algebra/Graph/Relation/InternalDerived.hs view
@@ -23,7 +23,7 @@ import Algebra.Graph.Class import Algebra.Graph.Relation (Relation, reflexiveClosure, symmetricClosure,- transitiveClosure, preorderClosure)+ transitiveClosure, closure) {-| The 'ReflexiveRelation' data type represents a /reflexive binary relation/ over a set of elements. Reflexive relations satisfy all laws of the@@ -146,10 +146,10 @@ deriving (Num, NFData) instance (Ord a, Show a) => Show (PreorderRelation a) where- show = show . preorderClosure . fromPreorder+ show = show . closure . fromPreorder instance Ord a => Eq (PreorderRelation a) where- x == y = preorderClosure (fromPreorder x) == preorderClosure (fromPreorder y)+ x == y = closure (fromPreorder x) == closure (fromPreorder y) -- TODO: To be derived automatically using GeneralizedNewtypeDeriving in GHC 8.2 instance Ord a => Graph (PreorderRelation a) where
src/Algebra/Graph/Relation/Preorder.hs view
@@ -25,4 +25,4 @@ -- | Extract the underlying relation. -- Complexity: /O(n * m * log(m))/ time. toRelation :: Ord a => PreorderRelation a -> Relation a-toRelation = preorderClosure . fromPreorder+toRelation = closure . fromPreorder
src/Algebra/Graph/ToGraph.hs view
@@ -14,36 +14,66 @@ -- This module defines the type class 'ToGraph' for capturing data types that -- can be converted to algebraic graphs. To make an instance of this class you -- need to define just a single method ('toGraph' or 'foldg'), which gives you--- access to many other useful methods for free. This type class is similar to--- the standard "Data.Foldable" defined for lists.+-- access to many other useful methods for free (although note that the default+-- implementations may be suboptimal performance-wise). --+-- This type class is similar to the standard type class 'Data.Foldable.Foldable'+-- defined for lists. Furthermore, one can define 'Foldable' methods 'foldMap'+-- and 'Data.Foldable.toList' using @ToGraph@.'foldg':+--+-- @+-- 'foldMap' f = 'foldg' 'mempty' f ('<>') ('<>')+-- 'Data.Foldable.toList' = 'foldg' [] 'pure' ('++') ('++')+-- @+--+-- However, the resulting 'Foldable' instance is problematic. For example,+-- folding equivalent algebraic graphs @1@ and @1@ + @1@ leads to different+-- results:+--+-- @+-- 'Data.Foldable.toList' (1 ) == [1]+-- 'Data.Foldable.toList' (1 + 1) == [1, 1]+-- @+--+-- To avoid such cases, we do not provide 'Foldable' instances for algebraic+-- graph datatypes. Furthermore, we require that the four arguments passed to+-- 'foldg' satisfy the laws of the algebra of graphs. The above definitions+-- of 'foldMap' and 'Data.Foldable.toList' violate this requirement, for example+-- @[1] ++ [1] /= [1]@, and are therefore disallowed. ----------------------------------------------------------------------------- module Algebra.Graph.ToGraph (ToGraph (..)) where import Prelude () import Prelude.Compat- import Data.IntMap (IntMap) import Data.IntSet (IntSet) import Data.Map (Map) import Data.Set (Set) import Data.Tree -import qualified Algebra.Graph as G-import qualified Algebra.Graph.AdjacencyMap as AM-import qualified Algebra.Graph.AdjacencyMap.Internal as AM-import qualified Algebra.Graph.AdjacencyIntMap as AIM-import qualified Algebra.Graph.AdjacencyIntMap.Internal as AIM-import qualified Algebra.Graph.Relation as R-import qualified Data.IntMap as IntMap-import qualified Data.IntSet as IntSet-import qualified Data.Map as Map-import qualified Data.Set as Set+import qualified Algebra.Graph as G+import qualified Algebra.Graph.AdjacencyMap as AM+import qualified Algebra.Graph.AdjacencyMap.Algorithm as AM+import qualified Algebra.Graph.AdjacencyMap.Internal as AM+import qualified Algebra.Graph.Labelled as LG+import qualified Algebra.Graph.Labelled.AdjacencyMap as LAM+import qualified Algebra.Graph.NonEmpty.AdjacencyMap as NAM+import qualified Algebra.Graph.NonEmpty.AdjacencyMap.Internal as NAM+import qualified Algebra.Graph.AdjacencyIntMap as AIM+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 Data.IntMap as IntMap+import qualified Data.IntSet as IntSet+import qualified Data.Map as Map+import qualified Data.Set as Set -- | The 'ToGraph' type class captures data types that can be converted to--- algebraic graphs.+-- algebraic graphs. Instances of this type class should satisfy the laws+-- specified by the default method definitions. class ToGraph t where {-# MINIMAL toGraph | foldg #-}+ -- | The type of vertices of the resulting graph. type ToVertex t -- | Convert a value to the corresponding algebraic graph, see "Algebra.Graph".@@ -68,7 +98,7 @@ -- | Check if a graph is empty. -- -- @- -- isEmpty == 'foldg' True (const False) (&&) (&&)+ -- isEmpty == 'foldg' True ('const' False) (&&) (&&) -- @ isEmpty :: t -> Bool isEmpty = foldg True (const False) (&&) (&&)@@ -76,8 +106,12 @@ -- | The /size/ of a graph, i.e. the number of leaves of the expression -- including 'empty' leaves. --+ -- __Note:__ The default implementation of this function violates the+ -- requirement that the four arguments of 'foldg' should satisfy the laws+ -- of algebraic graphs, since @1 + 1 /= 1@. Use this function with care.+ -- -- @- -- size == 'foldg' 1 (const 1) (+) (+)+ -- size == 'foldg' 1 ('const' 1) (+) (+) -- @ size :: t -> Int size = foldg 1 (const 1) (+) (+)@@ -304,7 +338,7 @@ -- result. -- -- @- -- toAdjacencyMapTranspose == 'foldg' 'AM.empty' 'AM.vertex' 'AM.overlay' (flip 'AM.connect')+ -- toAdjacencyMapTranspose == 'foldg' 'AM.empty' 'AM.vertex' 'AM.overlay' ('flip' 'AM.connect') -- @ toAdjacencyMapTranspose :: Ord (ToVertex t) => t -> AM.AdjacencyMap (ToVertex t) toAdjacencyMapTranspose = foldg AM.empty AM.vertex AM.overlay (flip AM.connect)@@ -321,7 +355,7 @@ -- the result. -- -- @- -- toAdjacencyIntMapTranspose == 'foldg' 'AIM.empty' 'AIM.vertex' 'AIM.overlay' (flip 'AIM.connect')+ -- toAdjacencyIntMapTranspose == 'foldg' 'AIM.empty' 'AIM.vertex' 'AIM.overlay' ('flip' 'AIM.connect') -- @ toAdjacencyIntMapTranspose :: ToVertex t ~ Int => t -> AIM.AdjacencyIntMap toAdjacencyIntMapTranspose = foldg AIM.empty AIM.vertex AIM.overlay (flip AIM.connect)@@ -350,6 +384,7 @@ foldg = G.foldg hasEdge = G.hasEdge +-- | See "Algebra.Graph.AdjacencyMap". instance Ord a => ToGraph (AM.AdjacencyMap a) where type ToVertex (AM.AdjacencyMap a) = a toGraph = G.stars@@ -363,7 +398,7 @@ edgeCount = AM.edgeCount vertexList = AM.vertexList vertexSet = AM.vertexSet- vertexIntSet = AM.vertexIntSet+ vertexIntSet = IntSet.fromAscList . AM.vertexList edgeList = AM.edgeList edgeSet = AM.edgeSet adjacencyList = AM.adjacencyList@@ -424,6 +459,74 @@ isDfsForestOf = AIM.isDfsForestOf isTopSortOf = AIM.isTopSortOf +-- | See "Algebra.Graph.Labelled".+instance (Eq e, Monoid e, Ord a) => ToGraph (LG.Graph e a) where+ type ToVertex (LG.Graph e a) = a+ foldg e v o c = LG.foldg e v (\e -> if e == mempty then o else c)+ vertexList = LG.vertexList+ vertexSet = LG.vertexSet+ toAdjacencyMap = LAM.skeleton+ . LG.foldg LAM.empty LAM.vertex LAM.connect+ toAdjacencyMapTranspose = LAM.skeleton+ . LG.foldg LAM.empty LAM.vertex (fmap flip LAM.connect)+ toAdjacencyIntMap = toAdjacencyIntMap . toAdjacencyMap+ toAdjacencyIntMapTranspose = toAdjacencyIntMapTranspose . toAdjacencyMapTranspose++-- | See "Algebra.Graph.Labelled.AdjacencyMap".+instance (Eq e, Monoid e, Ord a) => ToGraph (LAM.AdjacencyMap e a) where+ type ToVertex (LAM.AdjacencyMap e a) = a+ toGraph = toGraph . LAM.skeleton+ foldg e v o c = foldg e v o c . LAM.skeleton+ isEmpty = LAM.isEmpty+ hasVertex = LAM.hasVertex+ hasEdge = LAM.hasEdge+ vertexCount = LAM.vertexCount+ edgeCount = LAM.edgeCount+ vertexList = LAM.vertexList+ vertexSet = LAM.vertexSet+ vertexIntSet = IntSet.fromAscList . LAM.vertexList+ edgeList = edgeList . LAM.skeleton+ edgeSet = edgeSet . LAM.skeleton+ adjacencyList = adjacencyList . LAM.skeleton+ preSet = LAM.preSet+ postSet = LAM.postSet+ toAdjacencyMap = LAM.skeleton+ toAdjacencyIntMap = toAdjacencyIntMap . LAM.skeleton+ toAdjacencyMapTranspose = toAdjacencyMapTranspose . LAM.skeleton+ toAdjacencyIntMapTranspose = toAdjacencyIntMapTranspose . LAM.skeleton++-- | See "Algebra.Graph.NonEmpty.AdjacencyMap".+instance Ord a => ToGraph (NAM.AdjacencyMap a) where+ type ToVertex (NAM.AdjacencyMap a) = a+ toGraph = toGraph . NAM.am+ isEmpty _ = False+ hasVertex = NAM.hasVertex+ hasEdge = NAM.hasEdge+ vertexCount = NAM.vertexCount+ edgeCount = NAM.edgeCount+ vertexList = vertexList . NAM.am+ vertexSet = NAM.vertexSet+ vertexIntSet = vertexIntSet . NAM.am+ edgeList = NAM.edgeList+ edgeSet = NAM.edgeSet+ adjacencyList = adjacencyList . NAM.am+ preSet = NAM.preSet+ postSet = NAM.postSet+ adjacencyMap = adjacencyMap . NAM.am+ adjacencyIntMap = adjacencyIntMap . NAM.am+ dfsForest = dfsForest . NAM.am+ dfsForestFrom xs = dfsForestFrom xs . NAM.am+ dfs xs = dfs xs . NAM.am+ reachable x = reachable x . NAM.am+ topSort = topSort . NAM.am+ isAcyclic = isAcyclic . NAM.am+ toAdjacencyMap = NAM.am+ toAdjacencyIntMap = toAdjacencyIntMap . NAM.am+ toAdjacencyMapTranspose = NAM.am . NAM.transpose+ toAdjacencyIntMapTranspose = toAdjacencyIntMap . NAM.transpose+ isDfsForestOf f = isDfsForestOf f . NAM.am+ isTopSortOf x = isTopSortOf x . NAM.am+ -- TODO: Get rid of "Relation.Internal" and move this instance to "Relation". instance Ord a => ToGraph (R.Relation a) where type ToVertex (R.Relation a) = a@@ -436,7 +539,7 @@ edgeCount = R.edgeCount vertexList = R.vertexList vertexSet = R.vertexSet- vertexIntSet = R.vertexIntSet+ vertexIntSet = IntSet.fromAscList . R.vertexList edgeList = R.edgeList edgeSet = R.edgeSet adjacencyList = R.adjacencyList
src/Data/Graph/Typed.hs view
@@ -96,7 +96,7 @@ -- 'Algebra.Graph.AdjacencyMap.forest' (dfsForest % 'Algebra.Graph.AdjacencyMap.edge' 2 1) == 'AM.vertices' [1, 2] -- 'AM.isSubgraphOf' ('Algebra.Graph.AdjacencyMap.forest' $ dfsForest % x) x == True -- dfsForest % 'Algebra.Graph.AdjacencyMap.forest' (dfsForest % x) == dfsForest % x--- dfsForest % 'AM.vertices' vs == map (\\v -> Node v []) ('Data.List.nub' $ 'Data.List.sort' vs)+-- dfsForest % 'AM.vertices' vs == 'map' (\\v -> Node v []) ('Data.List.nub' $ 'Data.List.sort' vs) -- 'Algebra.Graph.AdjacencyMap.dfsForestFrom' ('Algebra.Graph.AdjacencyMap.vertexList' x) % x == dfsForest % x -- dfsForest % (3 * (1 + 4) * (1 + 5)) == [ Node { rootLabel = 1 -- , subForest = [ Node { rootLabel = 5@@ -120,7 +120,7 @@ -- 'Algebra.Graph.AdjacencyMap.forest' (dfsForestFrom [2, 1] % 'Algebra.Graph.AdjacencyMap.edge' 1 2) == 'Algebra.Graph.AdjacencyMap.vertices' [1, 2] -- 'Algebra.Graph.AdjacencyMap.isSubgraphOf' ('Algebra.Graph.AdjacencyMap.forest' $ dfsForestFrom vs % x) x == True -- dfsForestFrom ('Algebra.Graph.AdjacencyMap.vertexList' x) % x == 'dfsForest' % x--- dfsForestFrom vs % 'Algebra.Graph.AdjacencyMap.vertices' vs == map (\\v -> Node v []) ('Data.List.nub' vs)+-- dfsForestFrom vs % 'Algebra.Graph.AdjacencyMap.vertices' vs == 'map' (\\v -> Node v []) ('Data.List.nub' vs) -- dfsForestFrom [] % x == [] -- dfsForestFrom [1, 4] % (3 * (1 + 4) * (1 + 5)) == [ Node { rootLabel = 1 -- , subForest = [ Node { rootLabel = 5
test/Algebra/Graph/Test.hs view
@@ -11,7 +11,7 @@ import Data.List (sort) import Data.List.Extra (nubOrd)-import Prelude hiding ((+), (*), (<=))+import Prelude hiding ((+), (*)) import System.Exit (exitFailure) import Test.QuickCheck hiding ((===)) import Test.QuickCheck.Function@@ -36,14 +36,10 @@ (*) :: Graph g => g -> g -> g (*) = connect -(<=) :: (Eq g, Graph g) => g -> g -> Bool-(<=) = isSubgraphOf- (//) :: Testable prop => prop -> String -> Property p // s = label s $ counterexample ("Failed when checking '" ++ s ++ "'") p infixl 1 //-infixl 4 <= infixl 6 + infixl 7 * @@ -60,7 +56,7 @@ , (x + y) * z == x * z + y * z // "Right distributivity" , x * y * z == x * y + x * z + y * z // "Decomposition" ] -theorems :: (Eq g, Graph g) => GraphTestsuite g+theorems :: (Ord g, Graph g) => GraphTestsuite g theorems x y z = conjoin [ x + empty == x // "Overlay identity" , x + x == x // "Overlay idempotence"
test/Algebra/Graph/Test/API.hs view
@@ -14,20 +14,21 @@ GraphAPI (..) ) where +import Data.Monoid (Any) import Data.Tree import Algebra.Graph.Class (Graph (..)) -import qualified Algebra.Graph as Graph-import qualified Algebra.Graph.AdjacencyMap as AdjacencyMap-import qualified Algebra.Graph.AdjacencyMap.Internal as AdjacencyMap-import qualified Algebra.Graph.Fold as Fold-import qualified Algebra.Graph.HigherKinded.Class as HClass-import qualified Algebra.Graph.AdjacencyIntMap as AdjacencyIntMap-import qualified Algebra.Graph.AdjacencyIntMap.Internal as AdjacencyIntMap-import qualified Algebra.Graph.Relation as Relation-import qualified Data.Set as Set-import qualified Data.IntSet as IntSet+import qualified Algebra.Graph as Graph+import qualified Algebra.Graph.AdjacencyMap as AM+import qualified Algebra.Graph.Labelled as LG+import qualified Algebra.Graph.Labelled.AdjacencyMap as LAM+import qualified Algebra.Graph.Fold as Fold+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 class Graph g => GraphAPI g where edge :: Vertex g -> Vertex g -> g@@ -86,6 +87,16 @@ gmap = notImplemented induce :: (Vertex g -> Bool) -> g -> g induce = notImplemented+ compose :: g -> g -> g+ compose = notImplemented+ closure :: g -> g+ closure = notImplemented+ reflexiveClosure :: g -> g+ reflexiveClosure = notImplemented+ symmetricClosure :: g -> g+ symmetricClosure = notImplemented+ transitiveClosure :: g -> g+ transitiveClosure = notImplemented bind :: Vertex g ~ Int => g -> (Int -> g) -> g bind = notImplemented simplify :: g -> g@@ -96,29 +107,34 @@ notImplemented :: a notImplemented = error "Not implemented" -instance Ord a => GraphAPI (AdjacencyMap.AdjacencyMap a) where- edge = AdjacencyMap.edge- vertices = AdjacencyMap.vertices- edges = AdjacencyMap.edges- overlays = AdjacencyMap.overlays- connects = AdjacencyMap.connects- fromAdjacencySets = AdjacencyMap.fromAdjacencySets- isSubgraphOf = AdjacencyMap.isSubgraphOf- path = AdjacencyMap.path- circuit = AdjacencyMap.circuit- clique = AdjacencyMap.clique- biclique = AdjacencyMap.biclique- star = AdjacencyMap.star- stars = AdjacencyMap.stars- tree = AdjacencyMap.tree- forest = AdjacencyMap.forest- removeVertex = AdjacencyMap.removeVertex- removeEdge = AdjacencyMap.removeEdge- replaceVertex = AdjacencyMap.replaceVertex- mergeVertices = AdjacencyMap.mergeVertices- transpose = AdjacencyMap.transpose- gmap = AdjacencyMap.gmap- induce = AdjacencyMap.induce+instance Ord a => GraphAPI (AM.AdjacencyMap a) where+ edge = AM.edge+ vertices = AM.vertices+ edges = AM.edges+ overlays = AM.overlays+ connects = AM.connects+ fromAdjacencySets = AM.fromAdjacencySets+ isSubgraphOf = AM.isSubgraphOf+ path = AM.path+ circuit = AM.circuit+ clique = AM.clique+ biclique = AM.biclique+ star = AM.star+ stars = AM.stars+ tree = AM.tree+ forest = AM.forest+ removeVertex = AM.removeVertex+ removeEdge = AM.removeEdge+ replaceVertex = AM.replaceVertex+ mergeVertices = AM.mergeVertices+ transpose = AM.transpose+ gmap = AM.gmap+ induce = AM.induce+ compose = AM.compose+ closure = AM.closure+ reflexiveClosure = AM.reflexiveClosure+ symmetricClosure = AM.symmetricClosure+ transitiveClosure = AM.transitiveClosure instance Ord a => GraphAPI (Fold.Fold a) where edge = Fold.edge@@ -148,7 +164,6 @@ induce = Fold.induce bind = (>>=) simplify = Fold.simplify- box = HClass.box instance Ord a => GraphAPI (Graph.Graph a) where edge = Graph.edge@@ -177,53 +192,78 @@ transpose = Graph.transpose gmap = fmap induce = Graph.induce+ compose = Graph.compose bind = (>>=) simplify = Graph.simplify box = Graph.box -instance GraphAPI AdjacencyIntMap.AdjacencyIntMap where- edge = AdjacencyIntMap.edge- vertices = AdjacencyIntMap.vertices- edges = AdjacencyIntMap.edges- overlays = AdjacencyIntMap.overlays- connects = AdjacencyIntMap.connects- fromAdjacencyIntSets = AdjacencyIntMap.fromAdjacencyIntSets- isSubgraphOf = AdjacencyIntMap.isSubgraphOf- path = AdjacencyIntMap.path- circuit = AdjacencyIntMap.circuit- clique = AdjacencyIntMap.clique- biclique = AdjacencyIntMap.biclique- star = AdjacencyIntMap.star- stars = AdjacencyIntMap.stars- tree = AdjacencyIntMap.tree- forest = AdjacencyIntMap.forest- removeVertex = AdjacencyIntMap.removeVertex- removeEdge = AdjacencyIntMap.removeEdge- replaceVertex = AdjacencyIntMap.replaceVertex- mergeVertices = AdjacencyIntMap.mergeVertices- transpose = AdjacencyIntMap.transpose- gmap = AdjacencyIntMap.gmap- induce = AdjacencyIntMap.induce+instance GraphAPI AIM.AdjacencyIntMap where+ edge = AIM.edge+ vertices = AIM.vertices+ edges = AIM.edges+ overlays = AIM.overlays+ connects = AIM.connects+ fromAdjacencyIntSets = AIM.fromAdjacencyIntSets+ isSubgraphOf = AIM.isSubgraphOf+ path = AIM.path+ circuit = AIM.circuit+ clique = AIM.clique+ biclique = AIM.biclique+ star = AIM.star+ stars = AIM.stars+ tree = AIM.tree+ forest = AIM.forest+ removeVertex = AIM.removeVertex+ removeEdge = AIM.removeEdge+ replaceVertex = AIM.replaceVertex+ mergeVertices = AIM.mergeVertices+ transpose = AIM.transpose+ gmap = AIM.gmap+ induce = AIM.induce+ compose = AIM.compose+ closure = AIM.closure+ reflexiveClosure = AIM.reflexiveClosure+ symmetricClosure = AIM.symmetricClosure+ transitiveClosure = AIM.transitiveClosure -instance Ord a => GraphAPI (Relation.Relation a) where- edge = Relation.edge- vertices = Relation.vertices- edges = Relation.edges- overlays = Relation.overlays- connects = Relation.connects- isSubgraphOf = Relation.isSubgraphOf- path = Relation.path- circuit = Relation.circuit- clique = Relation.clique- biclique = Relation.biclique- star = Relation.star- stars = Relation.stars- tree = Relation.tree- forest = Relation.forest- removeVertex = Relation.removeVertex- removeEdge = Relation.removeEdge- replaceVertex = Relation.replaceVertex- mergeVertices = Relation.mergeVertices- transpose = Relation.transpose- gmap = Relation.gmap- induce = Relation.induce+instance Ord a => GraphAPI (R.Relation a) where+ edge = R.edge+ vertices = R.vertices+ edges = R.edges+ overlays = R.overlays+ connects = R.connects+ isSubgraphOf = R.isSubgraphOf+ path = R.path+ circuit = R.circuit+ clique = R.clique+ biclique = R.biclique+ star = R.star+ stars = R.stars+ tree = R.tree+ forest = R.forest+ removeVertex = R.removeVertex+ removeEdge = R.removeEdge+ replaceVertex = R.replaceVertex+ mergeVertices = R.mergeVertices+ transpose = R.transpose+ gmap = R.gmap+ induce = R.induce+ compose = R.compose+ closure = R.closure+ reflexiveClosure = R.reflexiveClosure+ symmetricClosure = R.symmetricClosure+ transitiveClosure = R.transitiveClosure++instance Ord a => GraphAPI (LG.Graph Any a) where+ vertices = LG.vertices+ overlays = LG.overlays+ isSubgraphOf = LG.isSubgraphOf+ removeVertex = LG.removeVertex+ induce = LG.induce++instance Ord a => GraphAPI (LAM.AdjacencyMap Any a) where+ vertices = LAM.vertices+ overlays = LAM.overlays+ isSubgraphOf = LAM.isSubgraphOf+ removeVertex = LAM.removeVertex+ induce = LAM.induce
test/Algebra/Graph/Test/AdjacencyIntMap.hs view
@@ -36,6 +36,7 @@ testToGraph t testGraphFamilies t testTransformations t+ testRelational t testDfsForest t testDfsForestFrom t testDfs t
test/Algebra/Graph/Test/AdjacencyMap.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedLists #-} ----------------------------------------------------------------------------- -- | -- Module : Algebra.Graph.Test.AdjacencyMap@@ -13,12 +14,15 @@ testAdjacencyMap ) where +import Data.List.NonEmpty+ import Algebra.Graph.AdjacencyMap+import Algebra.Graph.AdjacencyMap.Algorithm import Algebra.Graph.AdjacencyMap.Internal import Algebra.Graph.Test import Algebra.Graph.Test.Generic -import qualified Data.Set as Set+import qualified Algebra.Graph.NonEmpty.AdjacencyMap as NonEmpty t :: Testsuite t = testsuite "AdjacencyMap." empty@@ -40,6 +44,7 @@ testToGraph t testGraphFamilies t testTransformations t+ testRelational t testDfsForest t testDfsForestFrom t testDfs t@@ -51,19 +56,27 @@ putStrLn "\n============ AdjacencyMap.scc ============" test "scc empty == empty" $- scc(empty :: AI) == empty+ scc (empty :: AI) == empty - test "scc (vertex x) == vertex (Set.singleton x)" $ \(x :: Int) ->- scc (vertex x) == vertex (Set.singleton x)+ test "scc (vertex x) == vertex (NonEmpty.vertex x)" $ \(x :: Int) ->+ scc (vertex x) == vertex (NonEmpty.vertex x) - test "scc (edge x y) == edge (Set.singleton x) (Set.singleton y)" $ \(x :: Int) y ->- scc (edge x y) == edge (Set.singleton x) (Set.singleton y)+ test "scc (edge 1 1) == vertex (NonEmpty.edge 1 1)" $+ scc (edge 1 1 :: AI) == vertex (NonEmpty.edge 1 1) - test "scc (circuit (1:xs)) == edge (Set.fromList (1:xs)) (Set.fromList (1:xs))" $ \(xs :: [Int]) ->- scc (circuit (1:xs)) == edge (Set.fromList (1:xs)) (Set.fromList (1:xs))+ test "scc (edge 1 2) == edge (NonEmpty.vertex 1) (NonEmpty.vertex 2)" $+ scc (edge 1 2 :: AI) == edge (NonEmpty.vertex 1) (NonEmpty.vertex 2) + test "scc (circuit (1:xs)) == vertex (NonEmpty.circuit1 (1 :| xs))" $ \(xs :: [Int]) ->+ scc (circuit (1:xs)) == vertex (NonEmpty.circuit1 (1 :| xs))+ test "scc (3 * 1 * 4 * 1 * 5) == <correct result>" $- scc (3 * 1 * 4 * 1 * 5) == edges [ (Set.fromList [1,4], Set.fromList [1,4])- , (Set.fromList [1,4], Set.fromList [5] )- , (Set.fromList [3] , Set.fromList [1,4])- , (Set.fromList [3] , Set.fromList [5 :: Int])]+ scc (3 * 1 * 4 * 1 * 5) == edges [ (NonEmpty.vertex 3 , NonEmpty.vertex 5 )+ , (NonEmpty.vertex 3 , NonEmpty.clique1 [1,4,1])+ , (NonEmpty.clique1 [1,4,1], NonEmpty.vertex (5 :: Int)) ]++ test "isAcyclic . scc == const True" $ \(x :: AI) ->+ (isAcyclic . scc) x == (const True) x++ test "isAcyclic x == (scc x == gmap NonEmpty.vertex x)" $ \(x :: AI) ->+ isAcyclic x == (scc x == gmap NonEmpty.vertex x)
test/Algebra/Graph/Test/Arbitrary.hs view
@@ -18,24 +18,29 @@ import Prelude.Compat import Control.Monad+import Data.List.NonEmpty (NonEmpty (..)) import Data.Tree import Test.QuickCheck import Algebra.Graph import Algebra.Graph.AdjacencyMap.Internal+import Algebra.Graph.AdjacencyIntMap.Internal import Algebra.Graph.Export import Algebra.Graph.Fold (Fold)-import Algebra.Graph.AdjacencyIntMap.Internal+import Algebra.Graph.Label import Algebra.Graph.Relation.Internal import Algebra.Graph.Relation.InternalDerived -import qualified Algebra.Graph.AdjacencyMap as AdjacencyMap-import qualified Algebra.Graph.Class as C-import qualified Algebra.Graph.AdjacencyIntMap as AdjacencyIntMap-import qualified Algebra.Graph.NonEmpty as NE-import qualified Algebra.Graph.Relation as Relation+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.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 --- | Generate an arbitrary 'Graph' value of a specified size.+-- | Generate an arbitrary 'C.Graph' value of a specified size. arbitraryGraph :: (C.Graph g, Arbitrary (C.Vertex g)) => Gen g arbitraryGraph = sized expr where@@ -56,40 +61,34 @@ shrink (Connect x y) = [Empty, x, y, Overlay x y] ++ [Connect x' y' | (x', y') <- shrink (x, y) ] --- | Generate an arbitrary 'NonEmptyGraph' value of a specified size.-arbitraryNonEmptyGraph :: Arbitrary a => Gen (NE.NonEmptyGraph a)+-- TODO: Implement a custom shrink method.+instance Arbitrary a => Arbitrary (Fold a) where+ arbitrary = arbitraryGraph++-- | Generate an arbitrary 'NonEmpty.Graph' value of a specified size.+arbitraryNonEmptyGraph :: Arbitrary a => Gen (NonEmpty.Graph a) arbitraryNonEmptyGraph = sized expr where- expr 0 = NE.vertex <$> arbitrary -- can't generate non-empty graph of size 0- expr 1 = NE.vertex <$> arbitrary+ expr 0 = NonEmpty.vertex <$> arbitrary -- can't generate non-empty graph of size 0+ expr 1 = NonEmpty.vertex <$> arbitrary expr n = do left <- choose (1, n)- oneof [ NE.overlay <$> expr left <*> expr (n - left)- , NE.connect <$> expr left <*> expr (n - left) ]+ oneof [ NonEmpty.overlay <$> expr left <*> expr (n - left)+ , NonEmpty.connect <$> expr left <*> expr (n - left) ] -instance Arbitrary a => Arbitrary (NE.NonEmptyGraph a) where+instance Arbitrary a => Arbitrary (NonEmpty.Graph a) where arbitrary = arbitraryNonEmptyGraph - shrink (NE.Vertex _) = []- shrink (NE.Overlay x y) = [x, y]- ++ [NE.Overlay x' y' | (x', y') <- shrink (x, y) ]- shrink (NE.Connect x y) = [x, y, NE.Overlay x y]- ++ [NE.Connect x' y' | (x', y') <- shrink (x, y) ]+ shrink (NonEmpty.Vertex _) = []+ shrink (NonEmpty.Overlay x y) = [x, y]+ ++ [NonEmpty.Overlay x' y' | (x', y') <- shrink (x, y) ]+ shrink (NonEmpty.Connect x y) = [x, y, NonEmpty.Overlay x y]+ ++ [NonEmpty.Connect x' y' | (x', y') <- shrink (x, y) ] -- | Generate an arbitrary 'Relation'. arbitraryRelation :: (Arbitrary a, Ord a) => Gen (Relation a) arbitraryRelation = Relation.stars <$> arbitrary --- | Generate an arbitrary 'AdjacencyMap'. It is guaranteed that the--- resulting adjacency map is 'consistent'.-arbitraryAdjacencyMap :: (Arbitrary a, Ord a) => Gen (AdjacencyMap a)-arbitraryAdjacencyMap = AdjacencyMap.stars <$> arbitrary---- | 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 a, Ord a) => Arbitrary (Relation a) where arbitrary = arbitraryRelation@@ -106,15 +105,70 @@ instance (Arbitrary a, Ord a) => Arbitrary (PreorderRelation a) where arbitrary = PreorderRelation <$> arbitraryRelation +-- | Generate an arbitrary 'AdjacencyMap'. It is guaranteed that the+-- resulting adjacency map is 'consistent'.+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 +-- | 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)+arbitraryNonEmptyAdjacencyMap = NAM.stars1 <$> nonEmpty+ where+ nonEmpty = do+ xs <- arbitrary+ case xs of+ [] -> do+ x <- arbitrary+ 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++-- | 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 -instance Arbitrary a => Arbitrary (Fold a) where- arbitrary = arbitraryGraph+-- | 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++-- | Generate an arbitrary labelled 'LAM.Graph' value of a specified size.+arbitraryLabelledGraph :: (Arbitrary a, Arbitrary e) => Gen (LG.Graph e a)+arbitraryLabelledGraph = sized expr+ where+ expr 0 = return LG.empty+ expr 1 = LG.vertex <$> arbitrary+ expr n = do+ label <- arbitrary+ left <- choose (0, n)+ LG.connect label <$> expr left <*> expr (n - left)++instance (Arbitrary a, Arbitrary e, Monoid e) => Arbitrary (LG.Graph e a) where+ arbitrary = arbitraryLabelledGraph++ shrink LG.Empty = []+ shrink (LG.Vertex _) = [LG.Empty]+ 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@@ -128,5 +182,9 @@ children <- replicateM subTrees (go subSize) return $ Node root children +-- TODO: Implement a custom shrink method. instance Arbitrary s => Arbitrary (Doc s) where 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/Export.hs view
@@ -31,6 +31,24 @@ testExport :: IO () testExport = do+ putStrLn "\n============ Export.Eq ============"+ test "mempty /= literal \"\"" $+ mempty /= (literal "" :: Doc String)++ putStrLn "\n============ Export.Ord ============"+ test "mempty < literal \"\"" $+ mempty < (literal "" :: Doc String)++ putStrLn "\n============ Export.isEmpty ============"+ test "isEmpty mempty == True" $+ isEmpty mempty == True++ test "isEmpty (literal \"\") == False" $+ isEmpty (literal "" :: Doc String) == False++ test "isEmpty x == (x == mempty)" $ \(x :: Doc String) ->+ isEmpty x == (x == mempty)+ putStrLn "\n============ Export.literal ============" test "literal \"Hello, \" <> literal \"World!\" == literal \"Hello, World!\"" $ literal "Hello, " <> literal "World!" == literal ("Hello, World!" :: String)@@ -38,15 +56,9 @@ test "literal \"I am just a string literal\" == \"I am just a string literal\"" $ literal "I am just a string literal" == ("I am just a string literal" :: Doc String) - test "literal mempty == mempty" $- literal mempty == (mempty :: Doc String)- test "render . literal == id" $ \(x :: String) -> (render . literal) x == x - test "literal . render == id" $ \(xs :: [String]) -> let x = mconcat (map literal xs) in- (literal . render) x == x- putStrLn "\n============ Export.render ============" test "render (literal \"al\" <> literal \"ga\") == \"alga\"" $ render (literal "al" <> literal "ga") == ("alga" :: String)@@ -113,7 +125,7 @@ putStrLn "\n============ Export.Dot.export ============" let style = ED.Style { ED.graphName = "Example"- , ED.preamble = " // This is an example\n"+ , ED.preamble = [" // This is an example", ""] , ED.graphAttributes = ["label" := "Example", "labelloc" := "top"] , ED.defaultVertexAttributes = ["shape" := "circle"] , ED.defaultEdgeAttributes = mempty@@ -142,7 +154,7 @@ putStrLn "\n============ Export.Dot.exportAsIs ============" test "exportAsIs (circuit [\"a\", \"b\", \"c\"] :: Graph String)" $ (ED.exportAsIs (circuit ["a", "b", "c"] :: Graph String) :: String) ==- unlines [ "digraph"+ unlines [ "digraph " , "{" , " \"a\"" , " \"b\""@@ -155,7 +167,7 @@ putStrLn "\n============ Export.Dot.exportViaShow ============" test "exportViaShow (1 + 2 * (3 + 4) :: Graph Int)" $ (ED.exportViaShow (1 + 2 * (3 + 4) :: Graph Int) :: String) ==- unlines [ "digraph"+ unlines [ "digraph " , "{" , " \"1\"" , " \"2\""
test/Algebra/Graph/Test/Generic.hs view
@@ -9,15 +9,7 @@ -- -- Generic graph API testing. ------------------------------------------------------------------------------module Algebra.Graph.Test.Generic (- -- * Generic tests- Testsuite, testsuite, testShow, testFromAdjacencySets,- testFromAdjacencyIntSets, testBasicPrimitives, testIsSubgraphOf, testSize,- testToGraph, testAdjacencyList, testPreSet, testPreIntSet, testPostSet,- testPostIntSet, testGraphFamilies, testTransformations, testSplitVertex,- testBind, testSimplify, testDfsForest, testDfsForestFrom, testDfs,- testReachable, testTopSort, testIsAcyclic, testIsDfsForestOf, testIsTopSortOf- ) where+module Algebra.Graph.Test.Generic where import Prelude () import Prelude.Compat@@ -36,22 +28,27 @@ import Algebra.Graph.Test import Algebra.Graph.Test.API -import qualified Algebra.Graph as G-import qualified Algebra.Graph.AdjacencyMap as AM-import qualified Algebra.Graph.AdjacencyIntMap as AIM-import qualified Data.Set as Set-import qualified Data.IntSet as IntSet+import qualified Algebra.Graph as G+import qualified Algebra.Graph.AdjacencyMap as AM+import qualified Algebra.Graph.AdjacencyMap.Algorithm as AM+import qualified Algebra.Graph.AdjacencyIntMap as AIM+import qualified Data.Set as Set+import qualified Data.IntSet as IntSet data Testsuite where- Testsuite :: (Arbitrary g, Eq g, GraphAPI g, Num g, Show g, ToGraph g, ToVertex g ~ Int, Vertex g ~ Int)+ Testsuite :: (Arbitrary g, GraphAPI g, Num g, Ord g, Show g, ToGraph g, ToVertex g ~ Int, Vertex g ~ Int) => String -> (forall r. (g -> r) -> g -> r) -> Testsuite -testsuite :: (Arbitrary g, Eq g, GraphAPI g, Num g, Show g, ToGraph g, ToVertex g ~ Int, Vertex g ~ Int)+testsuite :: (Arbitrary g, GraphAPI g, Num g, Ord g, Show g, ToGraph g, ToVertex g ~ Int, Vertex g ~ Int) => String -> g -> Testsuite testsuite prefix g = Testsuite prefix (\f x -> f (x `asTypeOf` g)) +size10 :: Testable prop => prop -> Property+size10 = mapSize (min 10)+ testBasicPrimitives :: Testsuite -> IO ()-testBasicPrimitives = mconcat [ testEmpty+testBasicPrimitives = mconcat [ testOrd+ , testEmpty , testVertex , testEdge , testOverlay@@ -80,6 +77,13 @@ , testPostSet , testPostIntSet ] +testRelational :: Testsuite -> IO ()+testRelational = mconcat [ testCompose+ , testClosure+ , testReflexiveClosure+ , testSymmetricClosure+ , testTransitiveClosure ]+ testGraphFamilies :: Testsuite -> IO () testGraphFamilies = mconcat [ testPath , testCircuit@@ -120,6 +124,50 @@ test "show (1 * 2 + 3) == \"overlay (vertex 3) (edge 1 2)\"" $ show % (1 * 2 + 3) == "overlay (vertex 3) (edge 1 2)" + putStrLn ""+ test "show (vertex (-1) ) == \"vertex (-1)\"" $+ show % (vertex (-1) ) == "vertex (-1)"++ 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 (-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 (-1) * vertex (-2) + vertex (-3)) == \"overlay (vertex (-3)) (edge (-1) (-2))\"" $+ show % (vertex (-1) * vertex (-2) + vertex (-3)) == "overlay (vertex (-3)) (edge (-1) (-2))"+++testOrd :: Testsuite -> IO ()+testOrd (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 1 2 < edge 1 3" $+ edge 1 2 < id % edge 1 3++ 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 ============"@@ -270,10 +318,10 @@ test "overlays [x,y] == overlay x y" $ \x y -> overlays [x,y] == id % overlay x y - test "overlays == foldr overlay empty" $ mapSize (min 10) $ \xs ->+ test "overlays == foldr overlay empty" $ size10 $ \xs -> overlays xs == id % foldr overlay empty xs - test "isEmpty . overlays == all isEmpty" $ mapSize (min 10) $ \xs ->+ test "isEmpty . overlays == all isEmpty" $ size10 $ \xs -> isEmpty % overlays xs == all isEmpty xs testConnects :: Testsuite -> IO ()@@ -288,10 +336,10 @@ test "connects [x,y] == connect x y" $ \x y -> connects [x,y] == id % connect x y - test "connects == foldr connect empty" $ mapSize (min 10) $ \xs ->+ test "connects == foldr connect empty" $ size10 $ \xs -> connects xs == id % foldr connect empty xs - test "isEmpty . connects == all isEmpty" $ mapSize (min 10) $ \xs ->+ test "isEmpty . connects == all isEmpty" $ size10 $ \xs -> isEmpty % connects xs == all isEmpty xs testStars :: Testsuite -> IO ()@@ -321,57 +369,61 @@ testFromAdjacencySets :: Testsuite -> IO () testFromAdjacencySets (Testsuite prefix (%)) = do putStrLn $ "\n============ " ++ prefix ++ "fromAdjacencySets ============"- test "fromAdjacencySets [] == empty" $- fromAdjacencySets [] == id % empty+ test "fromAdjacencySets [] == empty" $+ fromAdjacencySets [] == id % empty - test "fromAdjacencySets [(x, Set.empty)] == vertex x" $ \x ->- fromAdjacencySets [(x, Set.empty)] == id % vertex x+ test "fromAdjacencySets [(x, Set.empty)] == vertex x" $ \x ->+ fromAdjacencySets [(x, Set.empty)] == id % vertex x - test "fromAdjacencySets [(x, Set.singleton y)] == edge x y" $ \x y ->- fromAdjacencySets [(x, Set.singleton y)] == id % edge x y+ test "fromAdjacencySets [(x, Set.singleton y)] == edge x y" $ \x y ->+ fromAdjacencySets [(x, Set.singleton y)] == id % edge x y - test "fromAdjacencySets . map (fmap Set.fromList) . adjacencyList == id" $ \x ->- (fromAdjacencySets . map (fmap Set.fromList) . adjacencyList) % x == x+ test "fromAdjacencySets . map (fmap Set.fromList) == stars" $ \x ->+ (fromAdjacencySets . map (fmap Set.fromList)) x == id % stars x - test "overlay (fromAdjacencySets xs) (fromAdjacencySets ys) == fromAdjacencySets (xs ++ ys)" $ \xs ys ->- overlay (fromAdjacencySets xs) % fromAdjacencySets ys == fromAdjacencySets (xs ++ ys)+ test "overlay (fromAdjacencySets xs) (fromAdjacencySets ys) == fromAdjacencySets (xs ++ ys)" $ \xs ys ->+ overlay (fromAdjacencySets xs) % fromAdjacencySets ys == fromAdjacencySets (xs ++ ys) testFromAdjacencyIntSets :: Testsuite -> IO () testFromAdjacencyIntSets (Testsuite prefix (%)) = do putStrLn $ "\n============ " ++ prefix ++ "fromAdjacencyIntSets ============"- test "fromAdjacencyIntSets [] == empty" $- fromAdjacencyIntSets [] == id % empty+ test "fromAdjacencyIntSets [] == empty" $+ fromAdjacencyIntSets [] == id % empty - test "fromAdjacencyIntSets [(x, IntSet.empty)] == vertex x" $ \x ->- fromAdjacencyIntSets [(x, IntSet.empty)] == id % vertex x+ test "fromAdjacencyIntSets [(x, IntSet.empty)] == vertex x" $ \x ->+ fromAdjacencyIntSets [(x, IntSet.empty)] == id % vertex x - test "fromAdjacencyIntSets [(x, IntSet.singleton y)] == edge x y" $ \x y ->- fromAdjacencyIntSets [(x, IntSet.singleton y)] == id % edge x y+ test "fromAdjacencyIntSets [(x, IntSet.singleton y)] == edge x y" $ \x y ->+ fromAdjacencyIntSets [(x, IntSet.singleton y)] == id % edge x y - test "fromAdjacencyIntSets . map (fmap IntSet.fromList) . adjacencyList == id" $ \x ->- (fromAdjacencyIntSets . map (fmap IntSet.fromList) . adjacencyList) % x == x+ test "fromAdjacencyIntSets . map (fmap IntSet.fromList) == stars" $ \x ->+ (fromAdjacencyIntSets . map (fmap IntSet.fromList)) x == id % stars x - test "overlay (fromAdjacencyIntSets xs) (fromAdjacencyIntSets ys) == fromAdjacencyIntSets (xs ++ ys)" $ \xs ys ->- overlay (fromAdjacencyIntSets xs) % fromAdjacencyIntSets ys == fromAdjacencyIntSets (xs ++ ys)+ test "overlay (fromAdjacencyIntSets xs) (fromAdjacencyIntSets ys) == fromAdjacencyIntSets (xs ++ ys)" $ \xs ys ->+ overlay (fromAdjacencyIntSets xs) % fromAdjacencyIntSets ys == fromAdjacencyIntSets (xs ++ ys) testIsSubgraphOf :: Testsuite -> IO () testIsSubgraphOf (Testsuite prefix (%)) = do putStrLn $ "\n============ " ++ prefix ++ "isSubgraphOf ============"- test "isSubgraphOf empty x == True" $ \x ->- isSubgraphOf empty % x == True+ test "isSubgraphOf empty x == True" $ \x ->+ isSubgraphOf empty % x == True - test "isSubgraphOf (vertex x) empty == False" $ \x ->- isSubgraphOf (vertex x) % empty == False+ test "isSubgraphOf (vertex x) empty == False" $ \x ->+ isSubgraphOf (vertex x) % empty == False - test "isSubgraphOf x (overlay x y) == True" $ \x y ->- isSubgraphOf x % overlay x y == True+ test "isSubgraphOf x (overlay x y) == True" $ \x y ->+ isSubgraphOf x % overlay x y == True - test "isSubgraphOf (overlay x y) (connect x y) == True" $ \x y ->- isSubgraphOf (overlay x y) % connect x y == True+ test "isSubgraphOf (overlay x y) (connect x y) == True" $ \x y ->+ isSubgraphOf (overlay x y) % connect x y == True - test "isSubgraphOf (path xs) (circuit xs) == True" $ \xs ->- isSubgraphOf (path xs) % circuit xs == True+ test "isSubgraphOf (path xs) (circuit xs) == True" $ \xs ->+ isSubgraphOf (path xs) % circuit xs == True + test "isSubgraphOf x y ==> x <= y" $ \x z ->+ let y = x + z -- Make sure we hit the precondition+ in isSubgraphOf x % y ==> x <= y+ testToGraphDefault :: Testsuite -> IO () testToGraphDefault (Testsuite prefix (%)) = do putStrLn $ "\n============ " ++ prefix ++ "toGraph et al. ============"@@ -571,15 +623,20 @@ testVertexCount :: Testsuite -> IO () testVertexCount (Testsuite prefix (%)) = do putStrLn $ "\n============ " ++ prefix ++ "vertexCount ============"- test "vertexCount empty == 0" $- vertexCount % empty == 0+ test "vertexCount empty == 0" $+ vertexCount % empty == 0 - test "vertexCount (vertex x) == 1" $ \x ->- vertexCount % vertex x == 1+ test "vertexCount (vertex x) == 1" $ \x ->+ vertexCount % (vertex x) == 1 - test "vertexCount == length . vertexList" $ \x ->- vertexCount % x == (length . vertexList) x+ test "vertexCount == length . vertexList" $ \x ->+ vertexCount % x == (length . vertexList) x + test "vertexCount x < vertexCount y ==> x < y" $ \x y ->+ if vertexCount x < vertexCount % y+ then property (x < y)+ else (vertexCount x > vertexCount y ==> x > y)+ testEdgeCount :: Testsuite -> IO () testEdgeCount (Testsuite prefix (%)) = do putStrLn $ "\n============ " ++ prefix ++ "edgeCount ============"@@ -652,9 +709,6 @@ test "vertexSet . vertices == Set.fromList" $ \xs -> vertexSet % vertices xs == Set.fromList xs - test "vertexSet . clique == Set.fromList" $ \xs ->- vertexSet % clique xs == Set.fromList xs- testVertexIntSet :: Testsuite -> IO () testVertexIntSet (Testsuite prefix (%)) = do putStrLn $ "\n============ " ++ prefix ++ "vertexIntSet ============"@@ -930,7 +984,7 @@ test "transpose (edge x y) == edge y x" $ \x y -> transpose % edge x y == edge y x - test "transpose . transpose == id" $ mapSize (min 10) $ \x ->+ test "transpose . transpose == id" $ size10 $ \x -> (transpose . transpose) % x == x test "edgeList . transpose == sort . map swap . edgeList" $ \x ->@@ -972,6 +1026,123 @@ test "isSubgraphOf (induce p x) x == True" $ \(apply -> p) x -> isSubgraphOf (induce p x) % x == True +testCompose :: Testsuite -> IO ()+testCompose (Testsuite prefix (%)) = do+ putStrLn $ "\n============ " ++ prefix ++ "compose ============"+ test "compose empty x == empty" $ \x ->+ compose empty % x == empty++ test "compose x empty == empty" $ \x ->+ compose x % empty == empty++ test "compose (vertex x) y == empty" $ \x y ->+ compose (vertex x) % y == empty++ test "compose x (vertex y) == empty" $ \x y ->+ compose x % (vertex y) == empty++ test "compose x (compose y z) == compose (compose x y) z" $ size10 $ \x y z ->+ compose x % (compose y z) == compose (compose x y) z++ test "compose x (overlay y z) == overlay (compose x y) (compose x z)" $ size10 $ \x y z ->+ compose x % (overlay y z) == overlay (compose x y) (compose x z)++ test "compose (overlay x y) z == overlay (compose x z) (compose y z)" $ size10 $ \x y z ->+ compose (overlay x y) % z == overlay (compose x z) (compose y z)++ test "compose (edge x y) (edge y z) == edge x z" $ \x y z ->+ compose (edge x y) % (edge y z) == edge x z++ test "compose (path [1..5]) (path [1..5]) == edges [(1,3),(2,4),(3,5)]" $+ compose (path [1..5])%(path [1..5]) == edges [(1,3),(2,4),(3,5)]++ test "compose (circuit [1..5]) (circuit [1..5]) == circuit [1,3,5,2,4]" $+ compose (circuit [1..5])%(circuit [1..5]) == circuit [1,3,5,2,4]++testClosure :: Testsuite -> IO ()+testClosure (Testsuite prefix (%)) = do+ putStrLn $ "\n============ " ++ prefix ++ "closure ============"+ test "closure empty == empty" $+ closure % empty == empty++ test "closure (vertex x) == edge x x" $ \x ->+ closure % (vertex x) == edge x x++ test "closure (edge x x) == edge x x" $ \x ->+ closure % (edge x x) == edge x x++ test "closure (edge x y) == edges [(x,x), (x,y), (y,y)]" $ \x y ->+ closure % (edge x y) == edges [(x,x), (x,y), (y,y)]++ test "closure (path $ nub xs) == reflexiveClosure (clique $ nub xs)" $ \xs ->+ closure % (path $ nubOrd xs) == reflexiveClosure (clique $ nubOrd xs)++ test "closure == reflexiveClosure . transitiveClosure" $ size10 $ \x ->+ closure % x == (reflexiveClosure . transitiveClosure) x++ test "closure == transitiveClosure . reflexiveClosure" $ size10 $ \x ->+ closure % x == (transitiveClosure . reflexiveClosure) x++ test "closure . closure == closure" $ size10 $ \x ->+ (closure . closure) % x == closure x++ test "postSet x (closure y) == Set.fromList (reachable x y)" $ size10 $ \x y ->+ postSet x % (closure y) == Set.fromList (reachable x y)++testReflexiveClosure :: Testsuite -> IO ()+testReflexiveClosure (Testsuite prefix (%)) = do+ putStrLn $ "\n============ " ++ prefix ++ "reflexiveClosure ============"+ test "reflexiveClosure empty == empty" $+ reflexiveClosure % empty == empty++ test "reflexiveClosure (vertex x) == edge x x" $ \x ->+ reflexiveClosure % vertex x == edge x x++ test "reflexiveClosure (edge x x) == edge x x" $ \x ->+ reflexiveClosure % edge x x == edge x x++ test "reflexiveClosure (edge x y) == edges [(x,x), (x,y), (y,y)]" $ \x y ->+ reflexiveClosure % edge x y == edges [(x,x), (x,y), (y,y)]++ test "reflexiveClosure . reflexiveClosure == reflexiveClosure" $ \x ->+ (reflexiveClosure . reflexiveClosure) x == reflexiveClosure % x++testSymmetricClosure :: Testsuite -> IO ()+testSymmetricClosure (Testsuite prefix (%)) = do+ putStrLn $ "\n============ " ++ prefix ++ "symmetricClosure ============"+ test "symmetricClosure empty == empty" $+ symmetricClosure % empty == empty++ test "symmetricClosure (vertex x) == vertex x" $ \x ->+ symmetricClosure % vertex x == vertex x++ test "symmetricClosure (edge x y) == edges [(x,y), (y,x)]" $ \x y ->+ symmetricClosure % edge x y == edges [(x,y), (y,x)]++ test "symmetricClosure x == overlay x (transpose x)" $ \x ->+ symmetricClosure % x == overlay x (transpose x)++ test "symmetricClosure . symmetricClosure == symmetricClosure" $ \x ->+ (symmetricClosure . symmetricClosure) x == symmetricClosure % x++testTransitiveClosure :: Testsuite -> IO ()+testTransitiveClosure (Testsuite prefix (%)) = do+ putStrLn $ "\n============ " ++ prefix ++ "transitiveClosure ============"+ test "transitiveClosure empty == empty" $+ transitiveClosure % empty == empty++ test "transitiveClosure (vertex x) == vertex x" $ \x ->+ transitiveClosure % (vertex x) == vertex x++ test "transitiveClosure (edge x y) == edge x y" $ \x y ->+ transitiveClosure % (edge x y) == edge x y++ test "transitiveClosure (path $ nub xs) == clique (nub $ xs)" $ \xs ->+ transitiveClosure % (path $ nubOrd xs) == clique (nubOrd xs)++ test "transitiveClosure . transitiveClosure == transitiveClosure" $ size10 $ \x ->+ (transitiveClosure . transitiveClosure) x == transitiveClosure % x+ testSplitVertex :: Testsuite -> IO () testSplitVertex (Testsuite prefix (%)) = do putStrLn $ "\n============ " ++ prefix ++ "splitVertex ============"@@ -999,7 +1170,7 @@ test "bind (edge x y) f == connect (f x) (f y)" $ \(apply -> f) x y -> bind (edge x y) f == connect (f x) % f y - test "bind (vertices xs) f == overlays (map f xs)" $ mapSize (min 10) $ \xs (apply -> f) ->+ test "bind (vertices xs) f == overlays (map f xs)" $ size10 $ \xs (apply -> f) -> bind (vertices xs) f == id % overlays (map f xs) test "bind x (const empty) == empty" $ \x ->@@ -1008,7 +1179,7 @@ test "bind x vertex == x" $ \x -> bind x vertex == id % x - test "bind (bind x f) g == bind x (\\y -> bind (f y) g)" $ mapSize (min 10) $ \x (apply -> f) (apply -> g) ->+ test "bind (bind x f) g == bind x (\\y -> bind (f y) g)" $ size10 $ \x (apply -> f) (apply -> g) -> bind (bind x f) g == bind (id % x) (\y -> bind (f y) g) testSimplify :: Testsuite -> IO ()
test/Algebra/Graph/Test/Graph.hs view
@@ -39,6 +39,13 @@ testGraphFamilies t testTransformations t + ----------------------------------------------------------------+ -- Generic relational composition tests, plus an additional one+ testCompose t+ test "size (compose x y) <= edgeCount x + edgeCount y + 1" $ \(x :: G) y ->+ size (compose x y) <= edgeCount x + edgeCount y + 1+ ----------------------------------------------------------------+ putStrLn "\n============ Graph.(===) ============" test " x === x == True" $ \(x :: G) -> (x === x) == True@@ -165,3 +172,19 @@ test "size (sparsify x) <= 3 * size x" $ \(x :: G) -> size (sparsify x) <= 3 * size x++ putStrLn "\n============ Labelled.Graph.context ============"+ test "context (const False) x == Nothing" $ \x ->+ context (const False) (x :: G) == Nothing++ test "context (== 1) (edge 1 2) == Just (Context [ ] [2 ])" $+ context (== 1) (edge 1 2 :: G) == Just (Context [ ] [2 ])++ test "context (== 2) (edge 1 2) == Just (Context [1 ] [ ])" $+ context (== 2) (edge 1 2 :: G) == Just (Context [1 ] [ ])++ test "context (const True ) (edge 1 2) == Just (Context [1 ] [2 ])" $+ context (const True ) (edge 1 2 :: G) == Just (Context [1 ] [2 ])++ test "context (== 4) (3 * 1 * 4 * 1 * 5) == Just (Context [3,1] [1,5])" $+ context (== 4) (3 * 1 * 4 * 1 * 5 :: G) == Just (Context [3,1] [1,5])
+ test/Algebra/Graph/Test/Labelled/AdjacencyMap.hs view
@@ -0,0 +1,475 @@+{-# LANGUAGE ViewPatterns #-}+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.Test.Labelled.AdjacencyMap+-- Copyright : (c) Andrey Mokhov 2016-2018+-- License : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability : experimental+--+-- Testsuite for "Algebra.Graph.Labelled.AdjacencyMap".+-----------------------------------------------------------------------------+module Algebra.Graph.Test.Labelled.AdjacencyMap (+ -- * Testsuite+ testLabelledAdjacencyMap+ ) where++import Data.Monoid++import Algebra.Graph.Label+import Algebra.Graph.Labelled.AdjacencyMap+import Algebra.Graph.Labelled.AdjacencyMap.Internal+import Algebra.Graph.Test+import Algebra.Graph.Test.Generic+import Algebra.Graph.ToGraph (reachable)++import qualified Algebra.Graph.AdjacencyMap as AM+import qualified Data.Map as Map+import qualified Data.Set as Set++t :: Testsuite+t = testsuite "Labelled.AdjacencyMap." (empty :: LAI)++type S = Sum Int+type D = Distance Int++type LAI = AdjacencyMap Any Int+type LAS = AdjacencyMap S Int+type LAD = AdjacencyMap D Int++testLabelledAdjacencyMap :: IO ()+testLabelledAdjacencyMap = do+ putStrLn "\n============ Labelled.AdjacencyMap.Internal.consistent ============"+ test "arbitraryLabelledAdjacencyMap" $ \x -> consistent (x :: LAS)+ test "empty" $ consistent (empty :: LAS)+ test "vertex" $ \x -> consistent (vertex x :: LAS)+ test "edge" $ \e x y -> consistent (edge e x y :: LAS)+ test "overlay" $ \x y -> consistent (overlay x y :: LAS)+ test "connect" $ size10 $ \e x y -> consistent (connect e x y :: LAS)+ test "vertices" $ \xs -> consistent (vertices xs :: LAS)+ test "edges" $ \es -> consistent (edges es :: LAS)+ test "overlays" $ size10 $ \xs -> consistent (overlays xs :: LAS)+ test "fromAdjacencyMaps" $ \xs -> consistent (fromAdjacencyMaps xs :: LAS)+ test "removeVertex" $ \x y -> consistent (removeVertex x y :: LAS)+ test "removeEdge" $ \x y z -> consistent (removeEdge x y z :: LAS)+ test "replaceVertex" $ \x y z -> consistent (replaceVertex x y z :: LAS)+ test "replaceEdge" $ \e x y z -> consistent (replaceEdge e x y z :: LAS)+ test "transpose" $ \x -> consistent (transpose x :: LAS)+ test "gmap" $ \(apply -> f) x -> consistent (gmap f (x :: LAS) :: LAS)+ test "emap" $ \(apply -> f) x -> consistent (emap (fmap f::S->S) x:: LAS)+ test "induce" $ \(apply -> p) x -> consistent (induce p x :: LAS)++ test "closure" $ size10 $ \x -> consistent (closure x :: LAD)+ test "reflexiveClosure" $ size10 $ \x -> consistent (reflexiveClosure x :: LAD)+ test "symmetricClosure" $ size10 $ \x -> consistent (symmetricClosure x :: LAD)+ test "transitiveClosure" $ size10 $ \x -> consistent (transitiveClosure x :: LAD)++ testEmpty t+ testVertex t++ putStrLn "\n============ Labelled.AdjacencyMap.edge ============"+ test "edge e x y == connect e (vertex x) (vertex y)" $ \(e :: S) (x :: Int) y ->+ edge e x y == connect e (vertex x) (vertex y)++ test "edge zero x y == vertices [x,y]" $ \(x :: Int) y ->+ edge (zero :: S) x y == vertices [x,y]++ test "hasEdge x y (edge e x y) == (e /= mempty)" $ \(e :: S) (x :: Int) y ->+ hasEdge x y (edge e x y) == (e /= mempty)++ test "edgeLabel x y (edge e x y) == e" $ \(e :: S) (x :: Int) y ->+ edgeLabel x y (edge e x y) == e++ test "edgeCount (edge e x y) == if e == mempty then 0 else 1" $ \(e :: S) (x :: Int) y ->+ edgeCount (edge e x y) == if e == mempty then 0 else 1++ test "vertexCount (edge e 1 1) == 1" $ \(e :: S) ->+ vertexCount (edge e 1 (1 :: Int)) == 1++ test "vertexCount (edge e 1 2) == 2" $ \(e :: S) ->+ vertexCount (edge e 1 (2 :: Int)) == 2++ test "x -<e>- y == edge e x y" $ \(e :: S) (x :: Int) y ->+ x -<e>- y == edge e x y++ testOverlay t++ putStrLn ""+ test "edgeLabel x y $ overlay (edge e x y) (edge zero x y) == e" $ \(e :: S) (x :: Int) y ->+ edgeLabel x y (overlay (edge e x y) (edge zero x y)) == e++ test "edgeLabel x y $ overlay (edge e x y) (edge f x y) == e <+> f" $ \(e :: S) f (x :: Int) y ->+ edgeLabel x y (overlay (edge e x y) (edge f x y)) == e <+> f++ putStrLn ""+ test "edgeLabel 1 3 $ transitiveClosure (overlay (edge e 1 2) (edge one 2 3)) == e" $ \(e :: D) ->+ edgeLabel 1 3 (transitiveClosure (overlay (edge e 1 2) (edge one 2 (3 :: Int)))) == e++ test "edgeLabel 1 3 $ transitiveClosure (overlay (edge e 1 2) (edge f 2 3)) == e <.> f" $ \(e :: D) f ->+ edgeLabel 1 3 (transitiveClosure (overlay (edge e 1 2) (edge f 2 (3 :: Int))))== e <.> f++ putStrLn "\n============ Labelled.AdjacencyMap.connect ============"+ test "isEmpty (connect e x y) == isEmpty x && isEmpty y" $ size10 $ \(e :: S) (x :: LAS) y ->+ isEmpty (connect e x y) ==(isEmpty x && isEmpty y)++ test "hasVertex z (connect e x y) == hasVertex z x || hasVertex z y" $ size10 $ \(e :: S) (x :: LAS) y z ->+ hasVertex z (connect e x y) ==(hasVertex z x || hasVertex z y)++ test "vertexCount (connect e x y) >= vertexCount x" $ size10 $ \(e :: S) (x :: LAS) y ->+ vertexCount (connect e x y) >= vertexCount x++ test "vertexCount (connect e x y) <= vertexCount x + vertexCount y" $ size10 $ \(e :: S) (x :: LAS) y ->+ vertexCount (connect e x y) <= vertexCount x + vertexCount y++ test "edgeCount (connect e x y) <= vertexCount x * vertexCount y + edgeCount x + edgeCount y" $ size10 $ \(e :: S) (x :: LAS) y ->+ edgeCount (connect e x y) <= vertexCount x * vertexCount y + edgeCount x + edgeCount y++ test "vertexCount (connect e 1 2) == 2" $ \(e :: Any) ->+ vertexCount (connect e 1 (2 :: LAI)) == 2++ test "edgeCount (connect e 1 2) == if e == zero then 0 else 1" $ \(e :: Any) ->+ edgeCount (connect e 1 (2 :: LAI)) == if e == zero then 0 else 1++ testVertices t++ putStrLn "\n============ Labelled.AdjacencyMap.edges ============"+ test "edges [] == empty" $+ edges [] == (empty :: LAS)++ test "edges [(e,x,y)] == edge e x y" $ \(e :: S) (x :: Int) y ->+ edges [(e,x,y)] == edge e x y++ test "edges == overlays . map (\\(e, x, y) -> edge e x y)" $ \(es :: [(S, Int, Int)]) ->+ edges es ==(overlays . map (\(e, x, y) -> edge e x y)) es++ testOverlays t++ putStrLn "\n============ Labelled.AdjacencyMap.fromAdjacencyMaps ============"+ test "fromAdjacencyMaps [] == empty" $+ fromAdjacencyMaps [] == (empty :: LAS)++ test "fromAdjacencyMaps [(x, Map.empty)] == vertex x" $ \(x :: Int) ->+ fromAdjacencyMaps [(x, Map.empty)] == (vertex x :: LAS)++ test "fromAdjacencyMaps [(x, Map.singleton y e)] == if e == zero then vertices [x,y] else edge e x y" $ \(e :: S) (x :: Int) y ->+ fromAdjacencyMaps [(x, Map.singleton y e)] == if e == zero then vertices [x,y] else edge e x y++ test "overlay (fromAdjacencyMaps xs) (fromAdjacencyMaps ys) == fromAdjacencyMaps (xs ++ ys)" $ \xs ys ->+ overlay (fromAdjacencyMaps xs) (fromAdjacencyMaps ys) == (fromAdjacencyMaps (xs ++ ys) :: LAS)++ putStrLn "\n============ Labelled.AdjacencyMap.isSubgraphOf ============"+ test "isSubgraphOf empty x == True" $ \(x :: LAS) ->+ isSubgraphOf empty x == True++ test "isSubgraphOf (vertex x) empty == False" $ \(x :: Int) ->+ isSubgraphOf (vertex x)(empty :: LAS)== False++ test "isSubgraphOf x y ==> x <= y" $ \(x :: LAD) z ->+ let y = x + z -- Make sure we hit the precondition+ in isSubgraphOf x y ==> x <= y++ putStrLn "\n============ Labelled.AdjacencyMap.isEmpty ============"+ test "isEmpty empty == True" $+ isEmpty empty == True++ test "isEmpty (overlay empty empty) == True" $+ isEmpty (overlay empty empty :: LAS) == True++ test "isEmpty (vertex x) == False" $ \(x :: Int) ->+ isEmpty (vertex x) == False++ test "isEmpty (removeVertex x $ vertex x) == True" $ \(x :: Int) ->+ isEmpty (removeVertex x $ vertex x) == True++ test "isEmpty (removeEdge x y $ edge e x y) == False" $ \(e :: S) (x :: Int) y ->+ isEmpty (removeEdge x y $ edge e x y) == False++ testHasVertex t++ putStrLn "\n============ Labelled.AdjacencyMap.hasEdge ============"+ test "hasEdge x y empty == False" $ \(x :: Int) y ->+ hasEdge x y empty == False++ test "hasEdge x y (vertex z) == False" $ \(x :: Int) y z ->+ hasEdge x y (vertex z) == False++ test "hasEdge x y (edge e x y) == (e /= zero)" $ \(e :: S) (x :: Int) y ->+ hasEdge x y (edge e x y) == (e /= zero)++ test "hasEdge x y . removeEdge x y == const False" $ \x y (z :: LAS) ->+ (hasEdge x y . removeEdge x y) z == const False z++ test "hasEdge x y == not . null . filter (\\(_,ex,ey) -> ex == x && ey == y) . edgeList" $ \x y (z :: LAS) -> do+ (_, u, v) <- elements ((zero, x, y) : edgeList z)+ return $ hasEdge u v z == (not . null . filter (\(_,ex,ey) -> ex == u && ey == v) . edgeList) z++ putStrLn "\n============ Labelled.AdjacencyMap.edgeLabel ============"+ test "edgeLabel x y empty == zero" $ \(x :: Int) y ->+ edgeLabel x y empty == (zero :: S)++ test "edgeLabel x y (vertex z) == zero" $ \(x :: Int) y z ->+ edgeLabel x y (vertex z) == (zero :: S)++ test "edgeLabel x y (edge e x y) == e" $ \(e :: S) (x :: Int) y ->+ edgeLabel x y (edge e x y) == e++ test "edgeLabel s t (overlay x y) == edgeLabel s t x + edgeLabel s t y" $ \(x :: LAS) y -> do+ z <- arbitrary+ s <- elements ([z] ++ vertexList x ++ vertexList y)+ t <- elements ([z] ++ vertexList x ++ vertexList y)+ return $ edgeLabel s t (overlay x y) == edgeLabel s t x + edgeLabel s t y++ testVertexCount t++ putStrLn "\n============ Labelled.AdjacencyMap.edgeCount ============"+ test "edgeCount empty == 0" $+ edgeCount empty == 0++ test "edgeCount (vertex x) == 0" $ \(x :: Int) ->+ edgeCount (vertex x) == 0++ test "edgeCount (edge e x y) == if e == zero then 0 else 1" $ \(e :: S) (x :: Int) y ->+ edgeCount (edge e x y) == if e == zero then 0 else 1++ test "edgeCount == length . edgeList" $ \(x :: LAS) ->+ edgeCount x == (length . edgeList) x++ testVertexList t++ putStrLn "\n============ Labelled.AdjacencyMap.edgeList ============"+ test "edgeList empty == []" $+ edgeList (empty :: LAS) == []++ test "edgeList (vertex x) == []" $ \(x :: Int) ->+ edgeList (vertex x :: LAS) == []++ test "edgeList (edge e x y) == if e == zero then [] else [(e,x,y)]" $ \(e :: S) (x :: Int) y ->+ edgeList (edge e x y) == if e == zero then [] else [(e,x,y)]++ testVertexSet t++ putStrLn "\n============ Labelled.AdjacencyMap.edgeSet ============"+ test "edgeSet empty == Set.empty" $+ edgeSet (empty :: LAS) == Set.empty++ test "edgeSet (vertex x) == Set.empty" $ \(x :: Int) ->+ edgeSet (vertex x :: LAS) == Set.empty++ test "edgeSet (edge e x y) == if e == zero then Set.empty else Set.singleton (e,x,y)" $ \(e :: S) (x :: Int) y ->+ edgeSet (edge e x y) == if e == zero then Set.empty else Set.singleton (e,x,y)++ putStrLn "\n============ Labelled.AdjacencyMap.preSet ============"+ test "preSet x empty == Set.empty" $ \x ->+ preSet x (empty :: LAS) == Set.empty++ test "preSet x (vertex x) == Set.empty" $ \x ->+ preSet x (vertex x :: LAS) == Set.empty++ test "preSet 1 (edge e 1 2) == Set.empty" $ \e ->+ preSet 1 (edge e 1 2 :: LAS) == Set.empty++ test "preSet y (edge e x y) == if e == zero then Set.empty else Set.fromList [x]" $ \(e :: S) (x :: Int) y ->+ preSet y (edge e x y) == if e == zero then Set.empty else Set.fromList [x]++ putStrLn "\n============ Labelled.AdjacencyMap.postSet ============"+ test "postSet x empty == Set.empty" $ \x ->+ postSet x (empty :: LAS) == Set.empty++ test "postSet x (vertex x) == Set.empty" $ \x ->+ postSet x (vertex x :: LAS) == Set.empty++ test "postSet x (edge e x y) == if e == zero then Set.empty else Set.fromList [y]" $ \(e :: S) (x :: Int) y ->+ postSet x (edge e x y) == if e == zero then Set.empty else Set.fromList [y]++ test "postSet 2 (edge e 1 2) == Set.empty" $ \e ->+ postSet 2 (edge e 1 2 :: LAS) == Set.empty++ putStrLn "\n============ Labelled.AdjacencyMap.skeleton ============"+ test "hasEdge x y == hasEdge x y . skeleton" $ \x y (z :: LAS) ->+ hasEdge x y z == (AM.hasEdge x y . skeleton) z++ putStrLn "\n============ Labelled.AdjacencyMap.removeVertex ============"+ test "removeVertex x (vertex x) == empty" $ \x ->+ removeVertex x (vertex x) == (empty :: LAS)++ test "removeVertex 1 (vertex 2) == vertex 2" $+ removeVertex 1 (vertex 2) == (vertex 2 :: LAS)++ test "removeVertex x (edge e x x) == empty" $ \(e :: S) (x :: Int) ->+ removeVertex x (edge e x x) == empty++ test "removeVertex 1 (edge e 1 2) == vertex 2" $ \(e :: S) ->+ removeVertex 1 (edge e 1 2) == vertex (2 :: Int)++ test "removeVertex x . removeVertex x == removeVertex x" $ \x (y :: LAS) ->+ (removeVertex x . removeVertex x) y == removeVertex x y++ putStrLn "\n============ Labelled.AdjacencyMap.removeEdge ============"+ test "removeEdge x y (edge e x y) == vertices [x,y]" $ \(e :: S) (x :: Int) y ->+ removeEdge x y (edge e x y) == vertices [x,y]++ test "removeEdge x y . removeEdge x y == removeEdge x y" $ \x y (z :: LAS) ->+ (removeEdge x y . removeEdge x y) z == removeEdge x y z++ test "removeEdge x y . removeVertex x == removeVertex x" $ \x y (z :: LAS) ->+ (removeEdge x y . removeVertex x) z == removeVertex x z++ test "removeEdge 1 1 (1 * 1 * 2 * 2) == 1 * 2 * 2" $+ removeEdge 1 1 (1 * 1 * 2 * 2) == (1 * 2 * 2 :: LAD)++ test "removeEdge 1 2 (1 * 1 * 2 * 2) == 1 * 1 + 2 * 2" $+ removeEdge 1 2 (1 * 1 * 2 * 2) == (1 * 1 + 2 * 2 :: LAD)++ putStrLn "\n============ Labelled.AdjacencyMap.replaceVertex ============"+ test "replaceVertex x x == id" $ \x y ->+ replaceVertex x x y == (y :: LAS)++ test "replaceVertex x y (vertex x) == vertex y" $ \x y ->+ replaceVertex x y (vertex x) == (vertex y :: LAS)++ test "replaceVertex x y == gmap (\\v -> if v == x then y else v)" $ \x y (z :: LAS) ->+ replaceVertex x y z == gmap (\v -> if v == x then y else v) z++ putStrLn "\n============ Labelled.AdjacencyMap.replaceEdge ============"+ test "replaceEdge e x y z == overlay (removeEdge x y z) (edge e x y)" $ \(e :: S) (x :: Int) y z ->+ replaceEdge e x y z == overlay (removeEdge x y z) (edge e x y)++ test "replaceEdge e x y (edge f x y) == edge e x y" $ \(e :: S) f (x :: Int) y ->+ replaceEdge e x y (edge f x y) == edge e x y++ test "edgeLabel x y (replaceEdge e x y z) == e" $ \(e :: S) (x :: Int) y z ->+ edgeLabel x y (replaceEdge e x y z) == e++ putStrLn "\n============ Labelled.AdjacencyMap.transpose ============"+ test "transpose empty == empty" $+ transpose empty == (empty :: LAS)++ test "transpose (vertex x) == vertex x" $ \x ->+ transpose (vertex x) == (vertex x :: LAS)++ test "transpose (edge e x y) == edge e y x" $ \e x y ->+ transpose (edge e x y) == (edge e y x :: LAS)++ test "transpose . transpose == id" $ size10 $ \x ->+ (transpose . transpose) x == (x :: LAS)++ putStrLn "\n============ Labelled.AdjacencyMap.gmap ============"+ test "gmap f empty == empty" $ \(apply -> f) ->+ gmap f (empty :: LAS) == (empty :: LAS)++ test "gmap f (vertex x) == vertex (f x)" $ \(apply -> f) x ->+ gmap f (vertex x :: LAS) == (vertex (f x) :: LAS)++ test "gmap f (edge e x y) == edge e (f x) (f y)" $ \(apply -> f) e x y ->+ gmap f (edge e x y :: LAS) == (edge e (f x) (f y) :: LAS)++ test "gmap id == id" $ \x ->+ gmap id x == (x :: LAS)++ test "gmap f . gmap g == gmap (f . g)" $ \(apply -> f) (apply -> g) x ->+ ((gmap f :: LAS -> LAS) . gmap g) (x :: LAS) == gmap (f . g) x++ -- TODO: We only test homomorphisms @h@ on @Sum Int@, which all happen to be+ -- just linear transformations: @h = (k*)@ for some @k :: Int@. These tests+ -- are therefore rather weak and do not cover the ruch space of possible+ -- monoid homomorphisms. How can we improve this?+ putStrLn "\n============ Labelled.AdjacencyMap.emap ============"+ test "emap h empty == empty" $ \(k :: S) ->+ let h = (k*)+ in emap h empty == (empty :: LAS)++ test "emap h (vertex x) == vertex x" $ \(k :: S) x ->+ let h = (k*)+ in emap h (vertex x) == (vertex x :: LAS)++ test "emap h (edge e x y) == edge (h e) x y" $ \(k :: S) e x y ->+ let h = (k*)+ in emap h (edge e x y) == (edge (h e) x y :: LAS)++ test "emap h (overlay x y) == overlay (emap h x) (emap h y)" $ \(k :: S) x y ->+ let h = (k*)+ in emap h (overlay x y) == (overlay (emap h x) (emap h y) :: LAS)++ test "emap h (connect e x y) == connect (h e) (emap h x) (emap h y)" $ \(k :: S) (e :: S) x y ->+ let h = (k*)+ in emap h (connect e x y) == (connect (h e) (emap h x) (emap h y) :: LAS)++ test "emap id == id" $ \x ->+ emap id x == (id x :: LAS)++ test "emap g . emap h == emap (g . h)" $ \(k :: S) (l :: S) x ->+ let h = (k*)+ g = (l*)+ in (emap g . emap h) x == (emap (g . h) x :: LAS)++ testInduce t++ putStrLn "\n============ Labelled.AdjacencyMap.closure ============"+ test "closure empty == empty" $+ closure empty == (empty :: LAD)++ test "closure (vertex x) == edge one x x" $ \x ->+ closure (vertex x) == (edge one x x :: LAD)++ test "closure (edge e x x) == edge one x x" $ \e x ->+ closure (edge e x x) == (edge one x x :: LAD)++ test "closure (edge e x y) == edges [(one,x,x), (e,x,y), (one,y,y)]" $ \e x y ->+ closure (edge e x y) == (edges [(one,x,x), (e,x,y), (one,y,y)] :: LAD)++ test "closure == reflexiveClosure . transitiveClosure" $ size10 $ \x ->+ closure (x :: LAD) == (reflexiveClosure . transitiveClosure) x++ test "closure == transitiveClosure . reflexiveClosure" $ size10 $ \x ->+ closure (x :: LAD) == (transitiveClosure . reflexiveClosure) x++ test "closure . closure == closure" $ size10 $ \x ->+ (closure . closure) x == closure (x :: LAD)++ test "postSet x (closure y) == Set.fromList (reachable x y)" $ size10 $ \(x :: Int) (y :: LAD) ->+ postSet x (closure y) == Set.fromList (reachable x y)++ putStrLn "\n============ Labelled.AdjacencyMap.reflexiveClosure ============"+ test "reflexiveClosure empty == empty" $+ reflexiveClosure empty == (empty :: LAD)++ test "reflexiveClosure (vertex x) == edge one x x" $ \x ->+ reflexiveClosure (vertex x) == (edge one x x :: LAD)++ test "reflexiveClosure (edge e x x) == edge one x x" $ \e x ->+ reflexiveClosure (edge e x x) == (edge one x x :: LAD)++ test "reflexiveClosure (edge e x y) == edges [(one,x,x), (e,x,y), (one,y,y)]" $ \e x y ->+ reflexiveClosure (edge e x y) == (edges [(one,x,x), (e,x,y), (one,y,y)] :: LAD)++ test "reflexiveClosure . reflexiveClosure == reflexiveClosure" $ size10 $ \x ->+ (reflexiveClosure . reflexiveClosure) x == reflexiveClosure (x :: LAD)++ putStrLn "\n============ Labelled.AdjacencyMap.symmetricClosure ============"+ test "symmetricClosure empty == empty" $+ symmetricClosure empty == (empty :: LAD)++ test "symmetricClosure (vertex x) == vertex x" $ \x ->+ symmetricClosure (vertex x) == (vertex x :: LAD)++ test "symmetricClosure (edge e x y) == edges [(e,x,y), (e,y,x)]" $ \e x y ->+ symmetricClosure (edge e x y) == (edges [(e,x,y), (e,y,x)] :: LAD)++ test "symmetricClosure x == overlay x (transpose x)" $ \x ->+ symmetricClosure x == (overlay x (transpose x) :: LAD)++ test "symmetricClosure . symmetricClosure == symmetricClosure" $ size10 $ \x ->+ (symmetricClosure . symmetricClosure) x == symmetricClosure (x :: LAD)++ putStrLn "\n============ Labelled.AdjacencyMap.transitiveClosure ============"+ test "transitiveClosure empty == empty" $+ transitiveClosure empty == (empty :: LAD)++ test "transitiveClosure (vertex x) == vertex x" $ \x ->+ transitiveClosure (vertex x) == (vertex x :: LAD)++ test "transitiveClosure (edge e x y) == edge e x y" $ \e x y ->+ transitiveClosure (edge e x y) == (edge e x y :: LAD)++ test "transitiveClosure . transitiveClosure == transitiveClosure" $ size10 $ \x ->+ (transitiveClosure . transitiveClosure) x == transitiveClosure (x :: LAD)
+ test/Algebra/Graph/Test/Labelled/Graph.hs view
@@ -0,0 +1,465 @@+{-# LANGUAGE ViewPatterns #-}+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.Test.Labelled.Graph+-- Copyright : (c) Andrey Mokhov 2016-2018+-- License : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability : experimental+--+-- Testsuite for "Algebra.Graph.Labelled.Graph".+-----------------------------------------------------------------------------+module Algebra.Graph.Test.Labelled.Graph (+ -- * Testsuite+ testLabelledGraph+ ) where++import Data.Monoid++import Algebra.Graph.Label+import Algebra.Graph.Labelled+import Algebra.Graph.Test+import Algebra.Graph.Test.Generic++import qualified Algebra.Graph.ToGraph as T+import qualified Data.Set as Set++t :: Testsuite+t = testsuite "Labelled.Graph." (empty :: LAI)++type S = Sum Int+type D = Distance Int++type LAI = Graph Any Int+type LAS = Graph S Int+type LAD = Graph D Int++testLabelledGraph :: IO ()+testLabelledGraph = do+ putStrLn "\n============ Labelled.Graph.foldg ============"+ test "foldg empty vertex connect == id" $ \(x :: LAS) ->+ foldg empty vertex connect x == id x++ test "foldg empty vertex (fmap flip connect) == transpose" $ \(x :: LAS) ->+ foldg empty vertex (fmap flip connect) x == transpose x++ test "foldg 1 (const 1) (const (+)) == size" $ \(x :: LAS) ->+ foldg 1 (const 1) (const (+)) x == size x++ test "foldg True (const False) (const (&&)) == isEmpty" $ \(x :: LAS) ->+ foldg True (const False) (const (&&)) x == isEmpty x++ test "foldg False (== x) (const (||)) == hasVertex x" $ \x (y :: LAS) ->+ foldg False (== x) (const (||)) y == hasVertex x y++ test "foldg Set.empty Set.singleton (const Set.union) == vertexSet" $ \(x :: LAS) ->+ foldg Set.empty Set.singleton (const Set.union) x == vertexSet x++ testEmpty t+ testVertex t++ putStrLn "\n============ Labelled.Graph.edge ============"+ test "edge e x y == connect e (vertex x) (vertex y)" $ \(e :: S) (x :: Int) y ->+ edge e x y == connect e (vertex x) (vertex y)++ test "edge zero x y == vertices [x,y]" $ \(x :: Int) y ->+ edge (zero :: S) x y == vertices [x,y]++ test "hasEdge x y (edge e x y) == (e /= mempty)" $ \(e :: S) (x :: Int) y ->+ hasEdge x y (edge e x y) == (e /= mempty)++ test "edgeLabel x y (edge e x y) == e" $ \(e :: S) (x :: Int) y ->+ edgeLabel x y (edge e x y) == e++ test "edgeCount (edge e x y) == if e == mempty then 0 else 1" $ \(e :: S) (x :: Int) y ->+ T.edgeCount (edge e x y) == if e == mempty then 0 else 1++ test "vertexCount (edge e 1 1) == 1" $ \(e :: S) ->+ T.vertexCount (edge e 1 (1 :: Int)) == 1++ test "vertexCount (edge e 1 2) == 2" $ \(e :: S) ->+ T.vertexCount (edge e 1 (2 :: Int)) == 2++ test "x -<e>- y == edge e x y" $ \(e :: S) (x :: Int) y ->+ x -<e>- y == edge e x y++ testOverlay t++ putStrLn ""+ test "edgeLabel x y $ overlay (edge e x y) (edge zero x y) == e" $ \(e :: S) (x :: Int) y ->+ edgeLabel x y (overlay (edge e x y) (edge zero x y)) == e++ test "edgeLabel x y $ overlay (edge e x y) (edge f x y) == e <+> f" $ \(e :: S) f (x :: Int) y ->+ edgeLabel x y (overlay (edge e x y) (edge f x y)) == e <+> f++ putStrLn ""+ test "edgeLabel 1 3 $ transitiveClosure (overlay (edge e 1 2) (edge one 2 3)) == e" $ \(e :: D) ->+ edgeLabel 1 3 (transitiveClosure (overlay (edge e 1 2) (edge one 2 (3 :: Int)))) == e++ test "edgeLabel 1 3 $ transitiveClosure (overlay (edge e 1 2) (edge f 2 3)) == e <.> f" $ \(e :: D) f ->+ edgeLabel 1 3 (transitiveClosure (overlay (edge e 1 2) (edge f 2 (3 :: Int))))== e <.> f++ putStrLn "\n============ Labelled.Graph.connect ============"+ test "isEmpty (connect e x y) == isEmpty x && isEmpty y" $ size10 $ \(e :: S) (x :: LAS) y ->+ isEmpty (connect e x y) ==(isEmpty x && isEmpty y)++ test "hasVertex z (connect e x y) == hasVertex z x || hasVertex z y" $ size10 $ \(e :: S) (x :: LAS) y z ->+ hasVertex z (connect e x y) ==(hasVertex z x || hasVertex z y)++ test "vertexCount (connect e x y) >= vertexCount x" $ size10 $ \(e :: S) (x :: LAS) y ->+ T.vertexCount (connect e x y) >= T.vertexCount x++ test "vertexCount (connect e x y) <= vertexCount x + vertexCount y" $ size10 $ \(e :: S) (x :: LAS) y ->+ T.vertexCount (connect e x y) <= T.vertexCount x + T.vertexCount y++ test "edgeCount (connect e x y) <= vertexCount x * vertexCount y + edgeCount x + edgeCount y" $ size10 $ \(e :: S) (x :: LAS) y ->+ T.edgeCount (connect e x y) <= T.vertexCount x * T.vertexCount y + T.edgeCount x + T.edgeCount y++ test "vertexCount (connect e 1 2) == 2" $ \(e :: Any) ->+ T.vertexCount (connect e 1 (2 :: LAI)) == 2++ test "edgeCount (connect e 1 2) == if e == zero then 0 else 1" $ \(e :: Any) ->+ T.edgeCount (connect e 1 (2 :: LAI)) == if e == zero then 0 else 1++ testVertices t++ putStrLn "\n============ Labelled.Graph.edges ============"+ test "edges [] == empty" $+ edges [] == (empty :: LAS)++ test "edges [(e,x,y)] == edge e x y" $ \(e :: S) (x :: Int) y ->+ edges [(e,x,y)] == edge e x y++ test "edges == overlays . map (\\(e, x, y) -> edge e x y)" $ \(es :: [(S, Int, Int)]) ->+ edges es ==(overlays . map (\(e, x, y) -> edge e x y)) es++ testOverlays t++ putStrLn "\n============ Labelled.Graph.isSubgraphOf ============"+ test "isSubgraphOf empty x == True" $ \(x :: LAS) ->+ isSubgraphOf empty x == True++ test "isSubgraphOf (vertex x) empty == False" $ \(x :: Int) ->+ isSubgraphOf (vertex x)(empty :: LAS)== False++ test "isSubgraphOf x y ==> x <= y" $ \(x :: LAD) z ->+ let y = x + z -- Make sure we hit the precondition+ in isSubgraphOf x y ==> x <= y++ putStrLn "\n============ Labelled.Graph.isEmpty ============"+ test "isEmpty empty == True" $+ isEmpty empty == True++ test "isEmpty (overlay empty empty) == True" $+ isEmpty (overlay empty empty :: LAS) == True++ test "isEmpty (vertex x) == False" $ \(x :: Int) ->+ isEmpty (vertex x) == False++ test "isEmpty (removeVertex x $ vertex x) == True" $ \(x :: Int) ->+ isEmpty (removeVertex x $ vertex x) == True++ test "isEmpty (removeEdge x y $ edge e x y) == False" $ \(e :: S) (x :: Int) y ->+ isEmpty (removeEdge x y $ edge e x y) == False++ testHasVertex t++ putStrLn "\n============ Labelled.Graph.hasEdge ============"+ test "hasEdge x y empty == False" $ \(x :: Int) y ->+ hasEdge x y (empty :: LAS) == False++ test "hasEdge x y (vertex z) == False" $ \(x :: Int) y z ->+ hasEdge x y (vertex z :: LAS) == False++ test "hasEdge x y (edge e x y) == (e /= zero)" $ \(e :: S) (x :: Int) y ->+ hasEdge x y (edge e x y) == (e /= zero)++ test "hasEdge x y . removeEdge x y == const False" $ \x y (z :: LAS) ->+ (hasEdge x y . removeEdge x y) z == const False z++ test "hasEdge x y == not . null . filter (\\(_,ex,ey) -> ex == x && ey == y) . edgeList" $ \x y (z :: LAS) -> do+ (_, u, v) <- elements ((zero, x, y) : edgeList z)+ return $ hasEdge u v z == (not . null . filter (\(_,ex,ey) -> ex == u && ey == v) . edgeList) z++ putStrLn "\n============ Labelled.Graph.edgeLabel ============"+ test "edgeLabel x y empty == zero" $ \(x :: Int) y ->+ edgeLabel x y empty == (zero :: S)++ test "edgeLabel x y (vertex z) == zero" $ \(x :: Int) y z ->+ edgeLabel x y (vertex z) == (zero :: S)++ test "edgeLabel x y (edge e x y) == e" $ \(e :: S) (x :: Int) y ->+ edgeLabel x y (edge e x y) == e++ test "edgeLabel s t (overlay x y) == edgeLabel s t x + edgeLabel s t y" $ \(x :: LAS) y -> do+ z <- arbitrary+ s <- elements ([z] ++ T.vertexList x ++ T.vertexList y)+ t <- elements ([z] ++ T.vertexList x ++ T.vertexList y)+ return $ edgeLabel s t (overlay x y) == edgeLabel s t x + edgeLabel s t y++ testVertexCount t++ putStrLn "\n============ Labelled.Graph.edgeCount ============"+ test "edgeCount empty == 0" $+ T.edgeCount (empty :: LAS) == 0++ test "edgeCount (vertex x) == 0" $ \(x :: Int) ->+ T.edgeCount (vertex x :: LAS) == 0++ test "edgeCount (edge e x y) == if e == zero then 0 else 1" $ \(e :: S) (x :: Int) y ->+ T.edgeCount (edge e x y) == if e == zero then 0 else 1++ test "edgeCount == length . edgeList" $ \(x :: LAS) ->+ T.edgeCount x == (length . edgeList) x++ testVertexList t++ putStrLn "\n============ Labelled.Graph.edgeList ============"+ test "edgeList empty == []" $+ edgeList (empty :: LAS) == []++ test "edgeList (vertex x) == []" $ \(x :: Int) ->+ edgeList (vertex x :: LAS) == []++ test "edgeList (edge e x y) == if e == zero then [] else [(e,x,y)]" $ \(e :: S) (x :: Int) y ->+ edgeList (edge e x y) == if e == zero then [] else [(e,x,y)]++ testVertexSet t++ putStrLn "\n============ Labelled.Graph.edgeSet ============"+ test "edgeSet empty == Set.empty" $+ edgeSet (empty :: LAS) == Set.empty++ test "edgeSet (vertex x) == Set.empty" $ \(x :: Int) ->+ edgeSet (vertex x :: LAS) == Set.empty++ test "edgeSet (edge e x y) == if e == zero then Set.empty else Set.singleton (e,x,y)" $ \(e :: S) (x :: Int) y ->+ edgeSet (edge e x y) == if e == zero then Set.empty else Set.singleton (e,x,y)++ putStrLn "\n============ Labelled.Graph.preSet ============"+ test "preSet x empty == Set.empty" $ \x ->+ T.preSet x (empty :: LAS) == Set.empty++ test "preSet x (vertex x) == Set.empty" $ \x ->+ T.preSet x (vertex x :: LAS) == Set.empty++ test "preSet 1 (edge e 1 2) == Set.empty" $ \e ->+ T.preSet 1 (edge e 1 2 :: LAS) == Set.empty++ test "preSet y (edge e x y) == if e == zero then Set.empty else Set.fromList [x]" $ \(e :: S) (x :: Int) y ->+ T.preSet y (edge e x y) == if e == zero then Set.empty else Set.fromList [x]++ putStrLn "\n============ Labelled.Graph.postSet ============"+ test "postSet x empty == Set.empty" $ \x ->+ T.postSet x (empty :: LAS) == Set.empty++ test "postSet x (vertex x) == Set.empty" $ \x ->+ T.postSet x (vertex x :: LAS) == Set.empty++ test "postSet x (edge e x y) == if e == zero then Set.empty else Set.fromList [y]" $ \(e :: S) (x :: Int) y ->+ T.postSet x (edge e x y) == if e == zero then Set.empty else Set.fromList [y]++ test "postSet 2 (edge e 1 2) == Set.empty" $ \e ->+ T.postSet 2 (edge e 1 2 :: LAS) == Set.empty++ putStrLn "\n============ Labelled.Graph.removeVertex ============"+ test "removeVertex x (vertex x) == empty" $ \x ->+ removeVertex x (vertex x) == (empty :: LAS)++ test "removeVertex 1 (vertex 2) == vertex 2" $+ removeVertex 1 (vertex 2) == (vertex 2 :: LAS)++ test "removeVertex x (edge e x x) == empty" $ \(e :: S) (x :: Int) ->+ removeVertex x (edge e x x) == empty++ test "removeVertex 1 (edge e 1 2) == vertex 2" $ \(e :: S) ->+ removeVertex 1 (edge e 1 2) == vertex (2 :: Int)++ test "removeVertex x . removeVertex x == removeVertex x" $ \x (y :: LAS) ->+ (removeVertex x . removeVertex x) y == removeVertex x y++ putStrLn "\n============ Labelled.Graph.removeEdge ============"+ test "removeEdge x y (edge e x y) == vertices [x,y]" $ \(e :: S) (x :: Int) y ->+ removeEdge x y (edge e x y) == vertices [x,y]++ test "removeEdge x y . removeEdge x y == removeEdge x y" $ \x y (z :: LAS) ->+ (removeEdge x y . removeEdge x y) z == removeEdge x y z++ test "removeEdge x y . removeVertex x == removeVertex x" $ \x y (z :: LAS) ->+ (removeEdge x y . removeVertex x) z == removeVertex x z++ test "removeEdge 1 1 (1 * 1 * 2 * 2) == 1 * 2 * 2" $+ removeEdge 1 1 (1 * 1 * 2 * 2) == (1 * 2 * 2 :: LAD)++ test "removeEdge 1 2 (1 * 1 * 2 * 2) == 1 * 1 + 2 * 2" $+ removeEdge 1 2 (1 * 1 * 2 * 2) == (1 * 1 + 2 * 2 :: LAD)++ putStrLn "\n============ Labelled.Graph.replaceVertex ============"+ test "replaceVertex x x == id" $ \x y ->+ replaceVertex x x y == (y :: LAS)++ test "replaceVertex x y (vertex x) == vertex y" $ \x y ->+ replaceVertex x y (vertex x) == (vertex y :: LAS)++ test "replaceVertex x y == fmap (\\v -> if v == x then y else v)" $ \x y (z :: LAS) ->+ replaceVertex x y z == fmap (\v -> if v == x then y else v) z++ putStrLn "\n============ Labelled.Graph.replaceEdge ============"+ test "replaceEdge e x y z == overlay (removeEdge x y z) (edge e x y)" $ \(e :: S) (x :: Int) y z ->+ replaceEdge e x y z == overlay (removeEdge x y z) (edge e x y)++ test "replaceEdge e x y (edge f x y) == edge e x y" $ \(e :: S) f (x :: Int) y ->+ replaceEdge e x y (edge f x y) == edge e x y++ test "edgeLabel x y (replaceEdge e x y z) == e" $ \(e :: S) (x :: Int) y z ->+ edgeLabel x y (replaceEdge e x y z) == e++ putStrLn "\n============ Labelled.Graph.transpose ============"+ test "transpose empty == empty" $+ transpose empty == (empty :: LAS)++ test "transpose (vertex x) == vertex x" $ \x ->+ transpose (vertex x) == (vertex x :: LAS)++ test "transpose (edge e x y) == edge e y x" $ \e x y ->+ transpose (edge e x y) == (edge e y x :: LAS)++ test "transpose . transpose == id" $ size10 $ \x ->+ (transpose . transpose) x == (x :: LAS)++ putStrLn "\n============ Labelled.Graph.fmap ============"+ test "fmap f empty == empty" $ \(apply -> f) ->+ fmap f (empty :: LAS) == (empty :: LAS)++ test "fmap f (vertex x) == vertex (f x)" $ \(apply -> f) x ->+ fmap f (vertex x :: LAS) == (vertex (f x) :: LAS)++ test "fmap f (edge e x y) == edge e (f x) (f y)" $ \(apply -> f) e x y ->+ fmap f (edge e x y :: LAS) == (edge e (f x) (f y) :: LAS)++ test "fmap id == id" $ \x ->+ fmap id x == (x :: LAS)++ test "fmap f . fmap g == fmap (f . g)" $ \(apply -> f) (apply -> g) x ->+ ((fmap f :: LAS -> LAS) . fmap g) (x :: LAS) == fmap (f . g) x++ -- TODO: We only test homomorphisms @h@ on @Sum Int@, which all happen to be+ -- just linear transformations: @h = (k*)@ for some @k :: Int@. These tests+ -- are therefore rather weak and do not cover the ruch space of possible+ -- monoid homomorphisms. How can we improve this?+ putStrLn "\n============ Labelled.Graph.emap ============"+ test "emap h empty == empty" $ \(k :: S) ->+ let h = (k*)+ in emap h empty == (empty :: LAS)++ test "emap h (vertex x) == vertex x" $ \(k :: S) x ->+ let h = (k*)+ in emap h (vertex x) == (vertex x :: LAS)++ test "emap h (edge e x y) == edge (h e) x y" $ \(k :: S) e x y ->+ let h = (k*)+ in emap h (edge e x y) == (edge (h e) x y :: LAS)++ test "emap h (overlay x y) == overlay (emap h x) (emap h y)" $ \(k :: S) x y ->+ let h = (k*)+ in emap h (overlay x y) == (overlay (emap h x) (emap h y) :: LAS)++ test "emap h (connect e x y) == connect (h e) (emap h x) (emap h y)" $ \(k :: S) (e :: S) x y ->+ let h = (k*)+ in emap h (connect e x y) == (connect (h e) (emap h x) (emap h y) :: LAS)++ test "emap id == id" $ \x ->+ emap id x == (id x :: LAS)++ test "emap g . emap h == emap (g . h)" $ \(k :: S) (l :: S) x ->+ let h = (k*)+ g = (l*)+ in (emap g . emap h) x == (emap (g . h) x :: LAS)++ testInduce t++ putStrLn "\n============ Labelled.Graph.closure ============"+ test "closure empty == empty" $+ closure empty == (empty :: LAD)++ test "closure (vertex x) == edge one x x" $ \x ->+ closure (vertex x) == (edge one x x :: LAD)++ test "closure (edge e x x) == edge one x x" $ \e x ->+ closure (edge e x x) == (edge one x x :: LAD)++ test "closure (edge e x y) == edges [(one,x,x), (e,x,y), (one,y,y)]" $ \e x y ->+ closure (edge e x y) == (edges [(one,x,x), (e,x,y), (one,y,y)] :: LAD)++ test "closure == reflexiveClosure . transitiveClosure" $ size10 $ \x ->+ closure (x :: LAD) == (reflexiveClosure . transitiveClosure) x++ test "closure == transitiveClosure . reflexiveClosure" $ size10 $ \x ->+ closure (x :: LAD) == (transitiveClosure . reflexiveClosure) x++ test "closure . closure == closure" $ size10 $ \x ->+ (closure . closure) x == closure (x :: LAD)++ test "postSet x (closure y) == Set.fromList (reachable x y)" $ size10 $ \(x :: Int) (y :: LAD) ->+ T.postSet x (closure y) == Set.fromList (T.reachable x y)++ putStrLn "\n============ Labelled.Graph.reflexiveClosure ============"+ test "reflexiveClosure empty == empty" $+ reflexiveClosure empty == (empty :: LAD)++ test "reflexiveClosure (vertex x) == edge one x x" $ \x ->+ reflexiveClosure (vertex x) == (edge one x x :: LAD)++ test "reflexiveClosure (edge e x x) == edge one x x" $ \e x ->+ reflexiveClosure (edge e x x) == (edge one x x :: LAD)++ test "reflexiveClosure (edge e x y) == edges [(one,x,x), (e,x,y), (one,y,y)]" $ \e x y ->+ reflexiveClosure (edge e x y) == (edges [(one,x,x), (e,x,y), (one,y,y)] :: LAD)++ test "reflexiveClosure . reflexiveClosure == reflexiveClosure" $ size10 $ \x ->+ (reflexiveClosure . reflexiveClosure) x == reflexiveClosure (x :: LAD)++ putStrLn "\n============ Labelled.Graph.symmetricClosure ============"+ test "symmetricClosure empty == empty" $+ symmetricClosure empty == (empty :: LAD)++ test "symmetricClosure (vertex x) == vertex x" $ \x ->+ symmetricClosure (vertex x) == (vertex x :: LAD)++ test "symmetricClosure (edge e x y) == edges [(e,x,y), (e,y,x)]" $ \e x y ->+ symmetricClosure (edge e x y) == (edges [(e,x,y), (e,y,x)] :: LAD)++ test "symmetricClosure x == overlay x (transpose x)" $ \x ->+ symmetricClosure x == (overlay x (transpose x) :: LAD)++ test "symmetricClosure . symmetricClosure == symmetricClosure" $ size10 $ \x ->+ (symmetricClosure . symmetricClosure) x == symmetricClosure (x :: LAD)++ putStrLn "\n============ Labelled.Graph.transitiveClosure ============"+ test "transitiveClosure empty == empty" $+ transitiveClosure empty == (empty :: LAD)++ test "transitiveClosure (vertex x) == vertex x" $ \x ->+ transitiveClosure (vertex x) == (vertex x :: LAD)++ test "transitiveClosure (edge e x y) == edge e x y" $ \e x y ->+ transitiveClosure (edge e x y) == (edge e x y :: LAD)++ test "transitiveClosure . transitiveClosure == transitiveClosure" $ size10 $ \x ->+ (transitiveClosure . transitiveClosure) x == transitiveClosure (x :: LAD)++ putStrLn "\n============ Labelled.Graph.context ============"+ test "context (const False) x == Nothing" $ \x ->+ context (const False) (x :: LAS) == Nothing++ test "context (== 1) (edge e 1 2) == if e == zero then Just (Context [] []) else Just (Context [] [(e,2)])" $ \e ->+ context (== 1) (edge e 1 2 :: LAS) == if e == zero then Just (Context [] []) else Just (Context [] [(e,2)])++ test "context (== 2) (edge e 1 2) == if e == zero then Just (Context [] []) else Just (Context [(e,1)] [] )" $ \e ->+ context (== 2) (edge e 1 2 :: LAS) == if e == zero then Just (Context [] []) else Just (Context [(e,1)] [] )++ test "context (const True ) (edge e 1 2) == if e == zero then Just (Context [] []) else Just (Context [(e,1)] [(e,2)])" $ \e ->+ context (const True ) (edge e 1 2 :: LAS) == if e == zero then Just (Context [] []) else Just (Context [(e,1)] [(e,2)])++ test "context (== 4) (3 * 1 * 4 * 1 * 5) == Just (Context [(one,3), (one,1)] [(one,1), (one,5)])" $+ context (== 4) (3 * 1 * 4 * 1 * 5 :: LAD) == Just (Context [(one,3), (one,1)] [(one,1), (one,5)])
+ test/Algebra/Graph/Test/NonEmpty/AdjacencyMap.hs view
@@ -0,0 +1,613 @@+{-# LANGUAGE CPP, OverloadedLists, ViewPatterns #-}+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.Test.NonEmpty.AdjacencyMap+-- Copyright : (c) Andrey Mokhov 2016-2018+-- License : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability : experimental+--+-- Testsuite for "Algebra.Graph.NonEmpty.AdjacencyMap".+-----------------------------------------------------------------------------+module Algebra.Graph.Test.NonEmpty.AdjacencyMap (+ -- * Testsuite+ testNonEmptyAdjacencyMap+ ) where++import Prelude ()+import Prelude.Compat++#if !MIN_VERSION_base(4,11,0)+import Data.Semigroup+#endif++import Control.Monad+import Data.Tree+import Data.Tuple++import Algebra.Graph.NonEmpty.AdjacencyMap+import Algebra.Graph.Test hiding (axioms, theorems)+import Algebra.Graph.ToGraph (toAdjacencyMap, reachable)++import qualified Algebra.Graph.AdjacencyMap as AM+import qualified Algebra.Graph.NonEmpty.AdjacencyMap as NonEmpty+import qualified Data.List.NonEmpty as NonEmpty+import qualified Data.Set as Set++sizeLimit :: Testable prop => prop -> Property+sizeLimit = mapSize (min 10)++type G = NonEmpty.AdjacencyMap Int++axioms :: G -> G -> G -> Property+axioms x y z = conjoin+ [ x + y == y + x // "Overlay commutativity"+ , x + (y + z) == (x + y) + z // "Overlay associativity"+ , x * (y * z) == (x * y) * z // "Connect associativity"+ , x * (y + z) == x * y + x * z // "Left distributivity"+ , (x + y) * z == x * z + y * z // "Right distributivity"+ , x * y * z == x * y + x * z + y * z // "Decomposition" ]++theorems :: G -> G -> Property+theorems x y = conjoin+ [ x + x == x // "Overlay idempotence"+ , x + y + x * y == x * y // "Absorption"+ , x * x == x * x * x // "Connect saturation"+ , x <= x + y // "Overlay order"+ , x + y <= x * y // "Overlay-connect order" ]++testNonEmptyAdjacencyMap :: IO ()+testNonEmptyAdjacencyMap = do+ putStrLn "\n============ NonEmpty.AdjacencyMap ============"+ test "Axioms of non-empty graphs" axioms+ test "Theorems of non-empty graphs" theorems++ putStrLn $ "\n============ Ord (NonEmpty.AdjacencyMap a) ============"+ test "vertex 1 < vertex 2" $+ vertex 1 < vertex (2 :: Int)++ test "vertex 3 < edge 1 2" $+ vertex 3 < edge 1 (2 :: Int)++ test "vertex 1 < edge 1 1" $+ vertex 1 < edge 1 (1 :: Int)++ test "edge 1 1 < edge 1 2" $+ edge 1 1 < edge 1 (2 :: Int)++ test "edge 1 2 < edge 1 1 + edge 2 2" $+ edge 1 2 < edge 1 1 + edge 2 (2 :: Int)++ test "edge 1 2 < edge 1 3" $+ edge 1 2 < edge 1 (3 :: Int)++ test "x <= x + y" $ \(x :: G) y ->+ x <= x + y++ test "x + y <= x * y" $ \(x :: G) y ->+ x + y <= x * y++ putStrLn $ "\n============ Show (NonEmpty.AdjacencyMap a) ============"+ test "show (1 :: AdjacencyMap Int) == \"vertex 1\"" $+ show (1 :: AdjacencyMap Int) == "vertex 1"++ test "show (1 + 2 :: AdjacencyMap Int) == \"vertices1 [1,2]\"" $+ show (1 + 2 :: AdjacencyMap Int) == "vertices1 [1,2]"++ test "show (1 * 2 :: AdjacencyMap Int) == \"edge 1 2\"" $+ show (1 * 2 :: AdjacencyMap Int) == "edge 1 2"++ test "show (1 * 2 * 3 :: AdjacencyMap Int) == \"edges1 [(1,2),(1,3),(2,3)]\"" $+ show (1 * 2 * 3 :: AdjacencyMap Int) == "edges1 [(1,2),(1,3),(2,3)]"++ test "show (1 * 2 + 3 :: AdjacencyMap Int) == \"overlay (vertex 3) (edge 1 2)\"" $+ show (1 * 2 + 3 :: AdjacencyMap Int) == "overlay (vertex 3) (edge 1 2)"++ test "show (vertex (-1) :: AdjacencyMap Int) == \"vertex (-1)\"" $+ show (vertex (-1) :: AdjacencyMap Int) == "vertex (-1)"++ test "show (vertex (-1) + vertex (-2) :: AdjacencyMap Int) == \"vertices1 [-2,-1]\"" $+ show (vertex (-1) + vertex (-2) :: AdjacencyMap Int) == "vertices1 [-2,-1]"++ test "show (vertex (-1) * vertex (-2) :: AdjacencyMap Int) == \"edge (-1) (-2)\"" $+ show (vertex (-1) * vertex (-2) :: AdjacencyMap Int) == "edge (-1) (-2)"++ test "show (vertex (-1) * vertex (-2) * vertex (-3) :: AdjacencyMap Int) == \"edges1 [(-2,-3),(-1,-3),(-1,-2)]\"" $+ show (vertex (-1) * vertex (-2) * vertex (-3) :: AdjacencyMap Int) == "edges1 [(-2,-3),(-1,-3),(-1,-2)]"++ test "show (vertex (-1) * vertex (-2) + vertex (-3) :: AdjacencyMap Int) == \"overlay (vertex (-3)) (edge (-1) (-2))\"" $+ show (vertex (-1) * vertex (-2) + vertex (-3) :: AdjacencyMap Int) == "overlay (vertex (-3)) (edge (-1) (-2))"++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.toNonEmpty ============"+ test "toNonEmpty empty == Nothing" $+ toNonEmpty (AM.empty :: AM.AdjacencyMap Int) == Nothing++ test "toNonEmpty (toAdjacencyMap x) == Just (x :: NonEmpty.AdjacencyMap a)" $ \x ->+ toNonEmpty (toAdjacencyMap x) == Just (x :: G)++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.vertex ============"+ test "hasVertex x (vertex x) == True" $ \(x :: Int) ->+ hasVertex x (vertex x) == True++ test "vertexCount (vertex x) == 1" $ \(x :: Int) ->+ vertexCount (vertex x) == 1++ test "edgeCount (vertex x) == 0" $ \(x :: Int) ->+ edgeCount (vertex x) == 0++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.edge ============"+ test "edge x y == connect (vertex x) (vertex y)" $ \(x :: Int) y ->+ edge x y == connect (vertex x) (vertex y)++ test "hasEdge x y (edge x y) == True" $ \(x :: Int) y ->+ hasEdge x y (edge x y) == True++ test "edgeCount (edge x y) == 1" $ \(x :: Int) y ->+ edgeCount (edge x y) == 1++ test "vertexCount (edge 1 1) == 1" $+ vertexCount (edge 1 1 :: G) == 1++ test "vertexCount (edge 1 2) == 2" $+ vertexCount (edge 1 2 :: G) == 2++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.overlay ============"+ test "hasVertex z (overlay x y) == hasVertex z x || hasVertex z y" $ \(x :: G) y z ->+ hasVertex z (overlay x y) == hasVertex z x || hasVertex z y++ test "vertexCount (overlay x y) >= vertexCount x" $ \(x :: G) y ->+ vertexCount (overlay x y) >= vertexCount x++ test "vertexCount (overlay x y) <= vertexCount x + vertexCount y" $ \(x :: G) y ->+ vertexCount (overlay x y) <= vertexCount x + vertexCount y++ test "edgeCount (overlay x y) >= edgeCount x" $ \(x :: G) y ->+ edgeCount (overlay x y) >= edgeCount x++ test "edgeCount (overlay x y) <= edgeCount x + edgeCount y" $ \(x :: G) y ->+ edgeCount (overlay x y) <= edgeCount x + edgeCount y++ test "vertexCount (overlay 1 2) == 2" $+ vertexCount (overlay 1 2 :: G) == 2++ test "edgeCount (overlay 1 2) == 0" $+ edgeCount (overlay 1 2 :: G) == 0++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.connect ============"+ test "hasVertex z (connect x y) == hasVertex z x || hasVertex z y" $ \(x :: G) y z ->+ hasVertex z (connect x y) == hasVertex z x || hasVertex z y++ test "vertexCount (connect x y) >= vertexCount x" $ \(x :: G) y ->+ vertexCount (connect x y) >= vertexCount x++ test "vertexCount (connect x y) <= vertexCount x + vertexCount y" $ \(x :: G) y ->+ vertexCount (connect x y) <= vertexCount x + vertexCount y++ test "edgeCount (connect x y) >= edgeCount x" $ \(x :: G) y ->+ edgeCount (connect x y) >= edgeCount x++ test "edgeCount (connect x y) >= edgeCount y" $ \(x :: G) y ->+ edgeCount (connect x y) >= edgeCount y++ test "edgeCount (connect x y) >= vertexCount x * vertexCount y" $ \(x :: G) y ->+ edgeCount (connect x y) >= vertexCount x * vertexCount y++ test "edgeCount (connect x y) <= vertexCount x * vertexCount y + edgeCount x + edgeCount y" $ \(x :: G) y ->+ edgeCount (connect x y) <= vertexCount x * vertexCount y + edgeCount x + edgeCount y++ test "vertexCount (connect 1 2) == 2" $+ vertexCount (connect 1 2 :: G) == 2++ test "edgeCount (connect 1 2) == 1" $+ edgeCount (connect 1 2 :: G) == 1++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.vertices1 ============"+ test "vertices1 [x] == vertex x" $ \(x :: Int) ->+ vertices1 [x] == vertex x++ test "hasVertex x . vertices1 == elem x" $ \(x :: Int) (xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (hasVertex x . vertices1) xs == elem x (NonEmpty.toList xs)++ test "vertexCount . vertices1 == length . nub" $ \(xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (vertexCount . vertices1) xs == (NonEmpty.length . NonEmpty.nub) xs++ test "vertexSet . vertices1 == Set.fromList . toList" $ \(xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (vertexSet . vertices1) xs == (Set.fromList . NonEmpty.toList) xs++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.edges1 ============"+ test "edges1 [(x,y)] == edge x y" $ \(x :: Int) y ->+ edges1 [(x,y)] == edge x y++ test "edgeCount . edges1 == length . nub" $ \(xs' :: NonEmptyList (Int, Int)) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (edgeCount . edges1) xs == (NonEmpty.length . NonEmpty.nub) xs++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.overlays1 ============"+ test "overlays1 [x] == x" $ \(x :: G) ->+ overlays1 [x] == x++ test "overlays1 [x,y] == overlay x y" $ \(x :: G) y ->+ overlays1 [x,y] == overlay x y++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.connects1 ============"+ test "connects1 [x] == x" $ \(x :: G) ->+ connects1 [x] == x++ test "connects1 [x,y] == connect x y" $ \(x :: G) y ->+ connects1 [x,y] == connect x y++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.isSubgraphOf ============"+ test "isSubgraphOf x (overlay x y) == True" $ \(x :: G) y ->+ isSubgraphOf x (overlay x y) == True++ test "isSubgraphOf (overlay x y) (connect x y) == True" $ \(x :: G) y ->+ isSubgraphOf (overlay x y) (connect x y) == True++ test "isSubgraphOf (path1 xs) (circuit1 xs) == True" $ \(xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in isSubgraphOf (path1 xs) (circuit1 xs) == True++ test "isSubgraphOf x y ==> x <= y" $ \(x :: G) z ->+ let y = x + z -- Make sure we hit the precondition+ in isSubgraphOf x y ==> x <= y++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.hasVertex ============"+ test "hasVertex x (vertex x) == True" $ \(x :: Int) ->+ hasVertex x (vertex x) == True++ test "hasVertex 1 (vertex 2) == False" $+ hasVertex 1 (vertex 2 :: G) == False++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.hasEdge ============"+ test "hasEdge x y (vertex z) == False" $ \(x :: Int) y z ->+ hasEdge x y (vertex z) == False++ test "hasEdge x y (edge x y) == True" $ \(x :: Int) y ->+ hasEdge x y (edge x y) == True++ test "hasEdge x y . removeEdge x y == const False" $ \(x :: Int) y z ->+ (hasEdge x y . removeEdge x y) z == False++ test "hasEdge x y == elem (x,y) . edgeList" $ \(x :: Int) y z -> do+ (u, v) <- elements ((x, y) : edgeList z)+ return $ hasEdge u v z == elem (u, v) (edgeList z)++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.vertexCount ============"+ test "vertexCount (vertex x) == 1" $ \(x :: Int) ->+ vertexCount (vertex x) == 1++ test "vertexCount x >= 1" $ \(x :: G) ->+ vertexCount x >= 1++ test "vertexCount == length . vertexList1" $ \(x :: G) ->+ vertexCount x == (NonEmpty.length . vertexList1) x++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.edgeCount ============"+ test "edgeCount (vertex x) == 0" $ \(x :: Int) ->+ edgeCount (vertex x) == 0++ test "edgeCount (edge x y) == 1" $ \(x :: Int) y ->+ edgeCount (edge x y) == 1++ test "edgeCount == length . edgeList" $ \(x :: G) ->+ edgeCount x == (length . edgeList) x++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.vertexList1 ============"+ test "vertexList1 (vertex x) == [x]" $ \(x :: Int) ->+ vertexList1 (vertex x) == [x]++ test "vertexList1 . vertices1 == nub . sort" $ \(xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (vertexList1 . vertices1) xs == (NonEmpty.nub . NonEmpty.sort) xs++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.edgeList ============"+ test "edgeList (vertex x) == []" $ \(x :: Int) ->+ edgeList (vertex x) == []++ test "edgeList (edge x y) == [(x,y)]" $ \(x :: Int) y ->+ edgeList (edge x y) == [(x,y)]++ test "edgeList (star 2 [3,1]) == [(2,1), (2,3)]" $+ edgeList (star 2 [3,1]) == [(2,1), (2,3 :: Int)]++ test "edgeList . edges1 == nub . sort . toList" $ \(xs' :: NonEmptyList (Int, Int)) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (edgeList . edges1) xs == (nubOrd . sort . NonEmpty.toList) xs++ test "edgeList . transpose == sort . map swap . edgeList" $ \(x :: G) ->+ (edgeList . transpose) x == (sort . map swap . edgeList) x++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.vertexSet ============"+ test "vertexSet . vertex == Set.singleton" $ \(x :: Int) ->+ (vertexSet . vertex) x == Set.singleton x++ test "vertexSet . vertices1 == Set.fromList . toList" $ \(xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (vertexSet . vertices1) xs == (Set.fromList . NonEmpty.toList) xs++ test "vertexSet . clique1 == Set.fromList . toList" $ \(xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (vertexSet . clique1) xs == (Set.fromList . NonEmpty.toList) xs++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.edgeSet ============"+ test "edgeSet (vertex x) == Set.empty" $ \(x :: Int) ->+ edgeSet (vertex x) == Set.empty++ test "edgeSet (edge x y) == Set.singleton (x,y)" $ \(x :: Int) y ->+ edgeSet (edge x y) == Set.singleton (x,y)++ test "edgeSet . edges1 == Set.fromList . toList" $ \(xs' :: NonEmptyList (Int, Int)) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (edgeSet . edges1) xs == (Set.fromList . NonEmpty.toList) xs++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.preSet ============"+ test "preSet x (vertex x) == Set.empty" $ \(x :: G) ->+ preSet x (vertex x) == Set.empty++ test "preSet 1 (edge 1 2) == Set.empty" $+ preSet 1 (edge 1 2 :: G) == Set.empty++ test "preSet y (edge x y) == Set.fromList [x]" $ \(x :: G) y ->+ preSet y (edge x y) == Set.fromList [x]++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.postSet ============"+ test "postSet x (vertex x) == Set.empty" $ \(x :: G) ->+ postSet x (vertex x) == Set.empty++ test "postSet x (edge x y) == Set.fromList [y]" $ \(x :: G) y ->+ postSet x (edge x y) == Set.fromList [y]++ test "postSet 2 (edge 1 2) == Set.empty" $+ postSet 2 (edge 1 2 :: G) == Set.empty++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.path1 ============"+ test "path1 [x] == vertex x" $ \(x :: Int) ->+ path1 [x] == vertex x++ test "path1 [x,y] == edge x y" $ \(x :: Int) y ->+ path1 [x,y] == edge x y++ test "path1 . reverse == transpose . path1" $ \(xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (path1 . NonEmpty.reverse) xs == (transpose . path1) xs++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.circuit1 ============"+ test "circuit1 [x] == edge x x" $ \(x :: Int) ->+ circuit1 [x] == edge x x++ test "circuit1 [x,y] == edges1 [(x,y), (y,x)]" $ \(x :: Int) y ->+ circuit1 [x,y] == edges1 [(x,y), (y,x)]++ test "circuit1 . reverse == transpose . circuit1" $ \(xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (circuit1 . NonEmpty.reverse) xs == (transpose . circuit1) xs++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.clique1 ============"+ test "clique1 [x] == vertex x" $ \(x :: Int) ->+ clique1 [x] == vertex x++ test "clique1 [x,y] == edge x y" $ \(x :: Int) y ->+ clique1 [x,y] == edge x y++ test "clique1 [x,y,z] == edges1 [(x,y), (x,z), (y,z)]" $ \(x :: Int) y z ->+ clique1 [x,y,z] == edges1 [(x,y), (x,z), (y,z)]++ test "clique1 (xs <> ys) == connect (clique1 xs) (clique1 ys)" $ \(xs' :: NonEmptyList Int) ys' ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ ys = NonEmpty.fromList (getNonEmpty ys')+ in clique1 (xs <> ys) == connect (clique1 xs) (clique1 ys)++ test "clique1 . reverse == transpose . clique1" $ \(xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (clique1 . NonEmpty.reverse) xs == (transpose . clique1) xs++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.biclique1 ============"+ test "biclique1 [x1,x2] [y1,y2] == edges1 [(x1,y1), (x1,y2), (x2,y1), (x2,y2)]" $ \(x1 :: Int) x2 y1 y2 ->+ biclique1 [x1,x2] [y1,y2] == edges1 [(x1,y1), (x1,y2), (x2,y1), (x2,y2)]++ test "biclique1 xs ys == connect (vertices1 xs) (vertices1 ys)" $ \(xs' :: NonEmptyList Int) ys' ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ ys = NonEmpty.fromList (getNonEmpty ys')+ in biclique1 xs ys == connect (vertices1 xs) (vertices1 ys)++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.star ============"+ test "star x [] == vertex x" $ \(x :: Int) ->+ star x [] == vertex x++ test "star x [y] == edge x y" $ \(x :: Int) y ->+ star x [y] == edge x y++ test "star x [y,z] == edges1 [(x,y), (x,z)]" $ \(x :: Int) y z ->+ star x [y,z] == edges1 [(x,y), (x,z)]++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.stars1 ============"+ test "stars1 [(x, [] )] == vertex x" $ \(x :: Int) ->+ stars1 [(x, [] )] == vertex x++ test "stars1 [(x, [y])] == edge x y" $ \(x :: Int) y ->+ stars1 [(x, [y])] == edge x y++ test "stars1 [(x, ys )] == star x ys" $ \(x :: Int) ys ->+ stars1 [(x, ys )] == star x ys++ test "stars1 == overlays1 . fmap (uncurry star)" $ \(xs' :: NonEmptyList (Int, [Int])) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in stars1 xs == overlays1 (fmap (uncurry star) xs)++ test "overlay (stars1 xs) (stars1 ys) == stars1 (xs <> ys)" $ \(xs' :: NonEmptyList (Int, [Int])) (ys' :: NonEmptyList (Int, [Int])) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ ys = NonEmpty.fromList (getNonEmpty ys')+ in overlay (stars1 xs) (stars1 ys) == stars1 (xs <> ys)++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.tree ============"+ test "tree (Node x []) == vertex x" $ \(x :: Int) ->+ tree (Node x []) == vertex x++ test "tree (Node x [Node y [Node z []]]) == path1 [x,y,z]" $ \(x :: Int) y z ->+ tree (Node x [Node y [Node z []]]) == path1 [x,y,z]++ test "tree (Node x [Node y [], Node z []]) == star x [y,z]" $ \(x :: Int) y z ->+ tree (Node x [Node y [], Node z []]) == star x [y,z]++ test "tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == edges1 [(1,2), (1,3), (3,4), (3,5)]" $+ tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == edges1 [(1,2), (1,3), (3,4), (3,5::Int)]++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.removeVertex1 ============"+ test "removeVertex1 x (vertex x) == Nothing" $ \(x :: Int) ->+ removeVertex1 x (vertex x) == Nothing++ test "removeVertex1 1 (vertex 2) == Just (vertex 2)" $+ removeVertex1 1 (vertex 2) == Just (vertex 2 :: G)++ test "removeVertex1 x (edge x x) == Nothing" $ \(x :: Int) ->+ removeVertex1 x (edge x x) == Nothing++ test "removeVertex1 1 (edge 1 2) == Just (vertex 2)" $+ removeVertex1 1 (edge 1 2) == Just (vertex 2 :: G)++ test "removeVertex1 x >=> removeVertex1 x == removeVertex1 x" $ \(x :: Int) y ->+ (removeVertex1 x >=> removeVertex1 x) y == removeVertex1 x y++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.removeEdge ============"+ test "removeEdge x y (edge x y) == vertices1 [x,y]" $ \(x :: Int) y ->+ removeEdge x y (edge x y) == vertices1 [x,y]++ test "removeEdge x y . removeEdge x y == removeEdge x y" $ \(x :: Int) y z ->+ (removeEdge x y . removeEdge x y) z == removeEdge x y z++ test "removeEdge 1 1 (1 * 1 * 2 * 2) == 1 * 2 * 2" $+ removeEdge 1 1 (1 * 1 * 2 * 2) == 1 * 2 * (2 :: G)++ test "removeEdge 1 2 (1 * 1 * 2 * 2) == 1 * 1 + 2 * 2" $+ removeEdge 1 2 (1 * 1 * 2 * 2) == 1 * 1 + 2 * (2 :: G)++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.replaceVertex ============"+ test "replaceVertex x x == id" $ \(x :: Int) y ->+ replaceVertex x x y == y++ test "replaceVertex x y (vertex x) == vertex y" $ \(x :: Int) y ->+ replaceVertex x y (vertex x) == vertex y++ test "replaceVertex x y == mergeVertices (== x) y" $ \(x :: Int) y z ->+ replaceVertex x y z == mergeVertices (== x) y z++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.mergeVertices ============"+ test "mergeVertices (const False) x == id" $ \(x :: Int) y ->+ mergeVertices (const False) x y == y++ test "mergeVertices (== x) y == replaceVertex x y" $ \(x :: Int) y z ->+ mergeVertices (== x) y z == replaceVertex x y z++ test "mergeVertices even 1 (0 * 2) == 1 * 1" $+ mergeVertices even 1 (0 * 2) == (1 * 1 :: G)++ test "mergeVertices odd 1 (3 + 4 * 5) == 4 * 1" $+ mergeVertices odd 1 (3 + 4 * 5) == (4 * 1 :: G)++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.transpose ============"+ test "transpose (vertex x) == vertex x" $ \(x :: Int) ->+ transpose (vertex x) == vertex x++ test "transpose (edge x y) == edge y x" $ \(x :: Int) y ->+ transpose (edge x y) == edge y x++ test "transpose . transpose == id" $ \(x :: G) ->+ (transpose . transpose) x == x++ test "edgeList . transpose == sort . map swap . edgeList" $ \(x :: G) ->+ (edgeList . transpose) x == (sort . map swap . edgeList) x++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.gmap ============"+ test "gmap f (vertex x) == vertex (f x)" $ \(apply -> f) (x :: Int) ->+ gmap f (vertex x) == vertex (f x :: Int)++ test "gmap f (edge x y) == edge (f x) (f y)" $ \(apply -> f) (x :: Int) y ->+ gmap f (edge x y) == edge (f x) (f y :: Int)++ test "gmap id == id" $ \(x :: G) ->+ gmap id x == x++ test "gmap f . gmap g == gmap (f . g)" $ \(apply -> f) (apply -> g) (x :: G) ->+ (gmap f . gmap g) x == (gmap (f . (g :: Int -> Int)) x :: G)++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.induce1 ============"+ test "induce1 (const True ) x == Just x" $ \(x :: G) ->+ induce1 (const True ) x == Just x++ test "induce1 (const False) x == Nothing" $ \(x :: G) ->+ induce1 (const False) x == Nothing++ test "induce1 (/= x) == removeVertex1 x" $ \(x :: Int) y ->+ induce1 (/= x) y == removeVertex1 x y++ test "induce1 p >=> induce1 q == induce1 (\\x -> p x && q x)" $ \(apply -> p) (apply -> q) (y :: G) ->+ (induce1 p >=> induce1 q) y == induce1 (\x -> p x && q x) y++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.closure ============"+ test "closure (vertex x) == edge x x" $ \(x :: Int) ->+ closure (vertex x) == edge x x++ test "closure (edge x x) == edge x x" $ \(x :: Int) ->+ closure (edge x x) == edge x x++ test "closure (edge x y) == edges1 [(x,x), (x,y), (y,y)]" $ \(x :: Int) y ->+ closure (edge x y) == edges1 [(x,x), (x,y), (y,y)]++ test "closure (path1 $ nub xs) == reflexiveClosure (clique1 $ nub xs)" $ \(xs :: NonEmptyList Int) ->+ let ys = NonEmpty.fromList (nubOrd $ getNonEmpty xs)+ in closure (path1 $ ys) == reflexiveClosure (clique1 $ ys)++ test "closure == reflexiveClosure . transitiveClosure" $ sizeLimit $ \(x :: G) ->+ closure x == (reflexiveClosure . transitiveClosure) x++ test "closure == transitiveClosure . reflexiveClosure" $ sizeLimit $ \(x :: G) ->+ closure x == (transitiveClosure . reflexiveClosure) x++ test "closure . closure == closure" $ sizeLimit $ \(x :: G) ->+ (closure . closure) x == closure x++ test "postSet x (closure y) == Set.fromList (reachable x y)" $ sizeLimit $ \x (y :: G) ->+ postSet x (closure y) == Set.fromList (reachable x y)++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.reflexiveClosure ============"+ test "reflexiveClosure (vertex x) == edge x x" $ \(x :: Int) ->+ reflexiveClosure (vertex x) == edge x x++ test "reflexiveClosure (edge x x) == edge x x" $ \(x :: Int) ->+ reflexiveClosure (edge x x) == edge x x++ test "reflexiveClosure (edge x y) == edges1 [(x,x), (x,y), (y,y)]" $ \(x :: Int) y ->+ reflexiveClosure (edge x y) == edges1 [(x,x), (x,y), (y,y)]++ test "reflexiveClosure . reflexiveClosure == reflexiveClosure" $ \(x :: G) ->+ (reflexiveClosure . reflexiveClosure) x == reflexiveClosure x++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.symmetricClosure ============"+ test "symmetricClosure (vertex x) == vertex x" $ \(x :: Int) ->+ symmetricClosure (vertex x) == vertex x++ test "symmetricClosure (edge x y) == edges1 [(x,y), (y,x)]" $ \(x :: G) y ->+ symmetricClosure (edge x y) == edges1 [(x,y), (y,x)]++ test "symmetricClosure x == overlay x (transpose x)" $ \(x :: G) ->+ symmetricClosure x == overlay x (transpose x)++ test "symmetricClosure . symmetricClosure == symmetricClosure" $ \(x :: G) ->+ (symmetricClosure . symmetricClosure) x == symmetricClosure x++ putStrLn $ "\n============ NonEmpty.AdjacencyMap.transitiveClosure ============"+ test "transitiveClosure (vertex x) == vertex x" $ \(x :: Int) ->+ transitiveClosure (vertex x) == vertex x++ test "transitiveClosure (edge x y) == edge x y" $ \(x :: G) y ->+ transitiveClosure (edge x y) == edge x y++ test "transitiveClosure (path1 $ nub xs) == clique1 (nub $ xs)" $ \(xs :: NonEmptyList Int) ->+ let ys = NonEmpty.fromList (nubOrd $ getNonEmpty xs)+ in transitiveClosure (path1 ys) == clique1 ys++ test "transitiveClosure . transitiveClosure == transitiveClosure" $ sizeLimit $ \(x :: G) ->+ (transitiveClosure . transitiveClosure) x == transitiveClosure x
+ test/Algebra/Graph/Test/NonEmpty/Graph.hs view
@@ -0,0 +1,690 @@+{-# LANGUAGE CPP, OverloadedLists, ViewPatterns #-}+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.Test.NonEmpty.Graph+-- Copyright : (c) Andrey Mokhov 2016-2018+-- License : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability : experimental+--+-- Testsuite for "Algebra.Graph.NonEmpty".+-----------------------------------------------------------------------------+module Algebra.Graph.Test.NonEmpty.Graph (+ -- * Testsuite+ testNonEmptyGraph+ ) where++import Prelude ()+import Prelude.Compat++#if !MIN_VERSION_base(4,11,0)+import Data.Semigroup+#endif++import Control.Monad+import Data.Either+import Data.Maybe+import Data.Tree+import Data.Tuple++import Algebra.Graph.NonEmpty hiding (Graph)+import Algebra.Graph.Test hiding (axioms, theorems)+import Algebra.Graph.ToGraph (reachable, toGraph)++import qualified Algebra.Graph as G+import qualified Algebra.Graph.NonEmpty as NonEmpty+import qualified Data.List.NonEmpty as NonEmpty+import qualified Data.Set as Set++type G = NonEmpty.Graph Int++axioms :: G -> G -> G -> Property+axioms x y z = conjoin+ [ x + y == y + x // "Overlay commutativity"+ , x + (y + z) == (x + y) + z // "Overlay associativity"+ , x * (y * z) == (x * y) * z // "Connect associativity"+ , x * (y + z) == x * y + x * z // "Left distributivity"+ , (x + y) * z == x * z + y * z // "Right distributivity"+ , x * y * z == x * y + x * z + y * z // "Decomposition" ]++theorems :: G -> G -> Property+theorems x y = conjoin+ [ x + x == x // "Overlay idempotence"+ , x + y + x * y == x * y // "Absorption"+ , x * x == x * x * x // "Connect saturation"+ , x <= x + y // "Overlay order"+ , x + y <= x * y // "Overlay-connect order" ]++testNonEmptyGraph :: IO ()+testNonEmptyGraph = do+ putStrLn "\n============ NonEmpty.Graph.============"+ test "Axioms of non-empty graphs" axioms+ test "Theorems of non-empty graphs" theorems++ putStrLn $ "\n============ Ord (NonEmpty.Graph a) ============"+ test "vertex 1 < vertex 2" $+ vertex 1 < vertex (2 :: Int)++ test "vertex 3 < edge 1 2" $+ vertex 3 < edge 1 (2 :: Int)++ test "vertex 1 < edge 1 1" $+ vertex 1 < edge 1 (1 :: Int)++ test "edge 1 1 < edge 1 2" $+ edge 1 1 < edge 1 (2 :: Int)++ test "edge 1 2 < edge 1 1 + edge 2 2" $+ edge 1 2 < edge 1 1 + edge 2 (2 :: Int)++ test "edge 1 2 < edge 1 3" $+ edge 1 2 < edge 1 (3 :: Int)++ test "x <= x + y" $ \(x :: G) y ->+ x <= x + y++ test "x + y <= x * y" $ \(x :: G) y ->+ x + y <= x * y++ putStrLn $ "\n============ Functor (NonEmpty.Graph a) ============"+ test "fmap f (vertex x) == vertex (f x)" $ \(apply -> f) (x :: Int) ->+ fmap f (vertex x) == vertex (f x :: Int)++ test "fmap f (edge x y) == edge (f x) (f y)" $ \(apply -> f) (x :: Int) y ->+ fmap f (edge x y) == edge (f x) (f y :: Int)++ test "fmap id == id" $ \(x :: G) ->+ fmap id x == x++ test "fmap f . fmap g == fmap (f . g)" $ \(apply -> f) (apply -> g) (x :: G) ->+ (fmap f . fmap g) x == (fmap (f . (g :: Int -> Int)) x :: G)++ putStrLn $ "\n============ Monad (NonEmpty.Graph a) ============"+ test "(vertex x >>= f) == f x" $ \(apply -> f) (x :: Int) ->+ (vertex x >>= f) == (f x :: G)++ test "(edge x y >>= f) == connect (f x) (f y)" $ \(apply -> f) (x :: Int) y ->+ (edge x y >>= f) == connect (f x) (f y :: G)++ test "(vertices1 xs >>= f) == overlays1 (fmap f xs)" $ mapSize (min 10) $ \(xs' :: NonEmptyList Int) (apply -> f) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (vertices1 xs >>= f) == (overlays1 (fmap f xs) :: G)++ test "(x >>= vertex) == x" $ \(x :: G) ->+ (x >>= vertex) == x++ test "((x >>= f) >>= g) == (x >>= (\\y -> (f y) >>= g))" $ mapSize (min 10) $ \(x :: G) (apply -> f) (apply -> g) ->+ ((x >>= f) >>= g) == (x >>= (\(y :: Int) -> (f y) >>= (g :: Int -> G)))++ putStrLn $ "\n============ NonEmpty.Graph.toNonEmpty ============"+ test "toNonEmpty empty == Nothing" $+ toNonEmpty (G.empty :: G.Graph Int) == Nothing++ test "toNonEmpty (toGraph x) == Just (x :: NonEmpty.Graph a)" $ \x ->+ toNonEmpty (toGraph x) == Just (x :: G)++ putStrLn $ "\n============ NonEmpty.Graph.vertex ============"+ test "hasVertex x (vertex x) == True" $ \(x :: Int) ->+ hasVertex x (vertex x) == True++ test "vertexCount (vertex x) == 1" $ \(x :: Int) ->+ vertexCount (vertex x) == 1++ test "edgeCount (vertex x) == 0" $ \(x :: Int) ->+ edgeCount (vertex x) == 0++ test "size (vertex x) == 1" $ \(x :: Int) ->+ size (vertex x) == 1++ putStrLn $ "\n============ NonEmpty.Graph.edge ============"+ test "edge x y == connect (vertex x) (vertex y)" $ \(x :: Int) y ->+ edge x y == connect (vertex x) (vertex y)++ test "hasEdge x y (edge x y) == True" $ \(x :: Int) y ->+ hasEdge x y (edge x y) == True++ test "edgeCount (edge x y) == 1" $ \(x :: Int) y ->+ edgeCount (edge x y) == 1++ test "vertexCount (edge 1 1) == 1" $+ vertexCount (edge 1 1 :: G) == 1++ test "vertexCount (edge 1 2) == 2" $+ vertexCount (edge 1 2 :: G) == 2++ putStrLn $ "\n============ NonEmpty.Graph.overlay ============"+ test "hasVertex z (overlay x y) == hasVertex z x || hasVertex z y" $ \(x :: G) y z ->+ hasVertex z (overlay x y) == hasVertex z x || hasVertex z y++ test "vertexCount (overlay x y) >= vertexCount x" $ \(x :: G) y ->+ vertexCount (overlay x y) >= vertexCount x++ test "vertexCount (overlay x y) <= vertexCount x + vertexCount y" $ \(x :: G) y ->+ vertexCount (overlay x y) <= vertexCount x + vertexCount y++ test "edgeCount (overlay x y) >= edgeCount x" $ \(x :: G) y ->+ edgeCount (overlay x y) >= edgeCount x++ test "edgeCount (overlay x y) <= edgeCount x + edgeCount y" $ \(x :: G) y ->+ edgeCount (overlay x y) <= edgeCount x + edgeCount y++ test "size (overlay x y) == size x + size y" $ \(x :: G) y ->+ size (overlay x y) == size x + size y++ test "vertexCount (overlay 1 2) == 2" $+ vertexCount (overlay 1 2 :: G) == 2++ test "edgeCount (overlay 1 2) == 0" $+ edgeCount (overlay 1 2 :: G) == 0++ putStrLn $ "\n============ NonEmpty.Graph.overlay1 ============"+ test " overlay1 empty x == x" $ \(x :: G) ->+ overlay1 G.empty x == x++ test "x /= empty ==> overlay1 x y == overlay (fromJust $ toNonEmpty x) y" $ \(x :: G.Graph Int) (y :: G) ->+ x /= G.empty ==> overlay1 x y == overlay (fromJust $ toNonEmpty x) y+++ putStrLn $ "\n============ NonEmpty.Graph.connect ============"+ test "hasVertex z (connect x y) == hasVertex z x || hasVertex z y" $ \(x :: G) y z ->+ hasVertex z (connect x y) == hasVertex z x || hasVertex z y++ test "vertexCount (connect x y) >= vertexCount x" $ \(x :: G) y ->+ vertexCount (connect x y) >= vertexCount x++ test "vertexCount (connect x y) <= vertexCount x + vertexCount y" $ \(x :: G) y ->+ vertexCount (connect x y) <= vertexCount x + vertexCount y++ test "edgeCount (connect x y) >= edgeCount x" $ \(x :: G) y ->+ edgeCount (connect x y) >= edgeCount x++ test "edgeCount (connect x y) >= edgeCount y" $ \(x :: G) y ->+ edgeCount (connect x y) >= edgeCount y++ test "edgeCount (connect x y) >= vertexCount x * vertexCount y" $ \(x :: G) y ->+ edgeCount (connect x y) >= vertexCount x * vertexCount y++ test "edgeCount (connect x y) <= vertexCount x * vertexCount y + edgeCount x + edgeCount y" $ \(x :: G) y ->+ edgeCount (connect x y) <= vertexCount x * vertexCount y + edgeCount x + edgeCount y++ test "size (connect x y) == size x + size y" $ \(x :: G) y ->+ size (connect x y) == size x + size y++ test "vertexCount (connect 1 2) == 2" $+ vertexCount (connect 1 2 :: G) == 2++ test "edgeCount (connect 1 2) == 1" $+ edgeCount (connect 1 2 :: G) == 1++ putStrLn $ "\n============ NonEmpty.Graph.vertices1 ============"+ test "vertices1 [x] == vertex x" $ \(x :: Int) ->+ vertices1 [x] == vertex x++ test "hasVertex x . vertices1 == elem x" $ \(x :: Int) (xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (hasVertex x . vertices1) xs == elem x (NonEmpty.toList xs)++ test "vertexCount . vertices1 == length . nub" $ \(xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (vertexCount . vertices1) xs == (NonEmpty.length . NonEmpty.nub) xs++ test "vertexSet . vertices1 == Set.fromList . toList" $ \(xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (vertexSet . vertices1) xs == (Set.fromList . NonEmpty.toList) xs++ putStrLn $ "\n============ NonEmpty.Graph.edges1 ============"+ test "edges1 [(x,y)] == edge x y" $ \(x :: Int) y ->+ edges1 [(x,y)] == edge x y++ test "edgeCount . edges1 == length . nub" $ \(xs' :: NonEmptyList (Int, Int)) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (edgeCount . edges1) xs == (NonEmpty.length . NonEmpty.nub) xs++ putStrLn $ "\n============ NonEmpty.Graph.overlays1 ============"+ test "overlays1 [x] == x" $ \(x :: G) ->+ overlays1 [x] == x++ test "overlays1 [x,y] == overlay x y" $ \(x :: G) y ->+ overlays1 [x,y] == overlay x y++ putStrLn $ "\n============ NonEmpty.Graph.connects1 ============"+ test "connects1 [x] == x" $ \(x :: G) ->+ connects1 [x] == x++ test "connects1 [x,y] == connect x y" $ \(x :: G) y ->+ connects1 [x,y] == connect x y++ putStrLn $ "\n============ NonEmpty.Graph.foldg1 ============"+ test "foldg1 vertex overlay connect == id" $ \(x :: G) ->+ foldg1 vertex overlay connect x == id x++ test "foldg1 vertex overlay (flip connect) == transpose" $ \(x :: G) ->+ foldg1 vertex overlay (flip connect) x == transpose x++ test "foldg1 (const 1) (+) (+) == size" $ \(x :: G) ->+ foldg1 (const 1) (+) (+) x == size x++ test "foldg1 (== x) (||) (||) == hasVertex x" $ \(x :: Int) y ->+ foldg1 (== x) (||) (||) y == hasVertex x y++ putStrLn $ "\n============ NonEmpty.Graph.isSubgraphOf ============"+ test "isSubgraphOf x (overlay x y) == True" $ \(x :: G) y ->+ isSubgraphOf x (overlay x y) == True++ test "isSubgraphOf (overlay x y) (connect x y) == True" $ \(x :: G) y ->+ isSubgraphOf (overlay x y) (connect x y) == True++ test "isSubgraphOf (path1 xs) (circuit1 xs) == True" $ \(xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in isSubgraphOf (path1 xs) (circuit1 xs) == True++ test "isSubgraphOf x y ==> x <= y" $ \(x :: G) z ->+ let y = x + z -- Make sure we hit the precondition+ in isSubgraphOf x y ==> x <= y++ putStrLn "\n============ NonEmpty.Graph.(===) ============"+ test " x === x == True" $ \(x :: G) ->+ (x === x) == True++ test "x + y === x + y == True" $ \(x :: G) y ->+ (x + y === x + y) == True++ test "1 + 2 === 2 + 1 == False" $+ (1 + 2 === 2 + (1 :: G)) == False++ test "x + y === x * y == False" $ \(x :: G) y ->+ (x + y === x * y) == False++ putStrLn $ "\n============ NonEmpty.Graph.size ============"+ test "size (vertex x) == 1" $ \(x :: Int) ->+ size (vertex x) == 1++ test "size (overlay x y) == size x + size y" $ \(x :: G) y ->+ size (overlay x y) == size x + size y++ test "size (connect x y) == size x + size y" $ \(x :: G) y ->+ size (connect x y) == size x + size y++ test "size x >= 1" $ \(x :: G) ->+ size x >= 1++ test "size x >= vertexCount x" $ \(x :: G) ->+ size x >= vertexCount x++ putStrLn $ "\n============ NonEmpty.Graph.hasVertex ============"+ test "hasVertex x (vertex x) == True" $ \(x :: Int) ->+ hasVertex x (vertex x) == True++ test "hasVertex 1 (vertex 2) == False" $+ hasVertex 1 (vertex 2 :: G) == False++ putStrLn $ "\n============ NonEmpty.Graph.hasEdge ============"+ test "hasEdge x y (vertex z) == False" $ \(x :: Int) y z ->+ hasEdge x y (vertex z) == False++ test "hasEdge x y (edge x y) == True" $ \(x :: Int) y ->+ hasEdge x y (edge x y) == True++ test "hasEdge x y . removeEdge x y == const False" $ \(x :: Int) y z ->+ (hasEdge x y . removeEdge x y) z == False++ test "hasEdge x y == elem (x,y) . edgeList" $ \(x :: Int) y z -> do+ (u, v) <- elements ((x, y) : edgeList z)+ return $ hasEdge u v z == elem (u, v) (edgeList z)++ putStrLn $ "\n============ NonEmpty.Graph.vertexCount ============"+ test "vertexCount (vertex x) == 1" $ \(x :: Int) ->+ vertexCount (vertex x) == 1++ test "vertexCount x >= 1" $ \(x :: G) ->+ vertexCount x >= 1++ test "vertexCount == length . vertexList1" $ \(x :: G) ->+ vertexCount x == (NonEmpty.length . vertexList1) x++ putStrLn $ "\n============ NonEmpty.Graph.edgeCount ============"+ test "edgeCount (vertex x) == 0" $ \(x :: Int) ->+ edgeCount (vertex x) == 0++ test "edgeCount (edge x y) == 1" $ \(x :: Int) y ->+ edgeCount (edge x y) == 1++ test "edgeCount == length . edgeList" $ \(x :: G) ->+ edgeCount x == (length . edgeList) x++ putStrLn $ "\n============ NonEmpty.Graph.vertexList1 ============"+ test "vertexList1 (vertex x) == [x]" $ \(x :: Int) ->+ vertexList1 (vertex x) == [x]++ test "vertexList1 . vertices1 == nub . sort" $ \(xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (vertexList1 . vertices1) xs == (NonEmpty.nub . NonEmpty.sort) xs++ putStrLn $ "\n============ NonEmpty.Graph.edgeList ============"+ test "edgeList (vertex x) == []" $ \(x :: Int) ->+ edgeList (vertex x) == []++ test "edgeList (edge x y) == [(x,y)]" $ \(x :: Int) y ->+ edgeList (edge x y) == [(x,y)]++ test "edgeList (star 2 [3,1]) == [(2,1), (2,3)]" $+ edgeList (star 2 [3,1]) == [(2,1), (2,3 :: Int)]++ test "edgeList . edges1 == nub . sort . toList" $ \(xs' :: NonEmptyList (Int, Int)) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (edgeList . edges1) xs == (nubOrd . sort . NonEmpty.toList) xs++ test "edgeList . transpose == sort . map swap . edgeList" $ \(x :: G) ->+ (edgeList . transpose) x == (sort . map swap . edgeList) x++ putStrLn $ "\n============ NonEmpty.Graph.vertexSet ============"+ test "vertexSet . vertex == Set.singleton" $ \(x :: Int) ->+ (vertexSet . vertex) x == Set.singleton x++ test "vertexSet . vertices1 == Set.fromList . toList" $ \(xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (vertexSet . vertices1) xs == (Set.fromList . NonEmpty.toList) xs++ test "vertexSet . clique1 == Set.fromList . toList" $ \(xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (vertexSet . clique1) xs == (Set.fromList . NonEmpty.toList) xs++ putStrLn $ "\n============ NonEmpty.Graph.edgeSet ============"+ test "edgeSet (vertex x) == Set.empty" $ \(x :: Int) ->+ edgeSet (vertex x) == Set.empty++ test "edgeSet (edge x y) == Set.singleton (x,y)" $ \(x :: Int) y ->+ edgeSet (edge x y) == Set.singleton (x,y)++ test "edgeSet . edges1 == Set.fromList . toList" $ \(xs' :: NonEmptyList (Int, Int)) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (edgeSet . edges1) xs == (Set.fromList . NonEmpty.toList) xs++ putStrLn $ "\n============ NonEmpty.Graph.path1 ============"+ test "path1 [x] == vertex x" $ \(x :: Int) ->+ path1 [x] == vertex x++ test "path1 [x,y] == edge x y" $ \(x :: Int) y ->+ path1 [x,y] == edge x y++ test "path1 . reverse == transpose . path1" $ \(xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (path1 . NonEmpty.reverse) xs == (transpose . path1) xs++ putStrLn $ "\n============ NonEmpty.Graph.circuit1 ============"+ test "circuit1 [x] == edge x x" $ \(x :: Int) ->+ circuit1 [x] == edge x x++ test "circuit1 [x,y] == edges1 [(x,y), (y,x)]" $ \(x :: Int) y ->+ circuit1 [x,y] == edges1 [(x,y), (y,x)]++ test "circuit1 . reverse == transpose . circuit1" $ \(xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (circuit1 . NonEmpty.reverse) xs == (transpose . circuit1) xs++ putStrLn $ "\n============ NonEmpty.Graph.clique1 ============"+ test "clique1 [x] == vertex x" $ \(x :: Int) ->+ clique1 [x] == vertex x++ test "clique1 [x,y] == edge x y" $ \(x :: Int) y ->+ clique1 [x,y] == edge x y++ test "clique1 [x,y,z] == edges1 [(x,y), (x,z), (y,z)]" $ \(x :: Int) y z ->+ clique1 [x,y,z] == edges1 [(x,y), (x,z), (y,z)]++ test "clique1 (xs <> ys) == connect (clique1 xs) (clique1 ys)" $ \(xs' :: NonEmptyList Int) ys' ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ ys = NonEmpty.fromList (getNonEmpty ys')+ in clique1 (xs <> ys) == connect (clique1 xs) (clique1 ys)++ test "clique1 . reverse == transpose . clique1" $ \(xs' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in (clique1 . NonEmpty.reverse) xs == (transpose . clique1) xs++ putStrLn $ "\n============ NonEmpty.Graph.biclique1 ============"+ test "biclique1 [x1,x2] [y1,y2] == edges1 [(x1,y1), (x1,y2), (x2,y1), (x2,y2)]" $ \(x1 :: Int) x2 y1 y2 ->+ biclique1 [x1,x2] [y1,y2] == edges1 [(x1,y1), (x1,y2), (x2,y1), (x2,y2)]++ test "biclique1 xs ys == connect (vertices1 xs) (vertices1 ys)" $ \(xs' :: NonEmptyList Int) ys' ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ ys = NonEmpty.fromList (getNonEmpty ys')+ in biclique1 xs ys == connect (vertices1 xs) (vertices1 ys)++ putStrLn $ "\n============ NonEmpty.Graph.star ============"+ test "star x [] == vertex x" $ \(x :: Int) ->+ star x [] == vertex x++ test "star x [y] == edge x y" $ \(x :: Int) y ->+ star x [y] == edge x y++ test "star x [y,z] == edges1 [(x,y), (x,z)]" $ \(x :: Int) y z ->+ star x [y,z] == edges1 [(x,y), (x,z)]++ putStrLn $ "\n============ NonEmpty.Graph.stars1 ============"+ test "stars1 [(x, [] )] == vertex x" $ \(x :: Int) ->+ stars1 [(x, [] )] == vertex x++ test "stars1 [(x, [y])] == edge x y" $ \(x :: Int) y ->+ stars1 [(x, [y])] == edge x y++ test "stars1 [(x, ys )] == star x ys" $ \(x :: Int) ys ->+ stars1 [(x, ys )] == star x ys++ test "stars1 == overlays1 . fmap (uncurry star)" $ \(xs' :: NonEmptyList (Int, [Int])) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ in stars1 xs == overlays1 (fmap (uncurry star) xs)++ test "overlay (stars1 xs) (stars1 ys) == stars1 (xs <> ys)" $ \(xs' :: NonEmptyList (Int, [Int])) (ys' :: NonEmptyList (Int, [Int])) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ ys = NonEmpty.fromList (getNonEmpty ys')+ in overlay (stars1 xs) (stars1 ys) == stars1 (xs <> ys)++ putStrLn $ "\n============ NonEmpty.Graph.tree ============"+ test "tree (Node x []) == vertex x" $ \(x :: Int) ->+ tree (Node x []) == vertex x++ test "tree (Node x [Node y [Node z []]]) == path1 [x,y,z]" $ \(x :: Int) y z ->+ tree (Node x [Node y [Node z []]]) == path1 [x,y,z]++ test "tree (Node x [Node y [], Node z []]) == star x [y,z]" $ \(x :: Int) y z ->+ tree (Node x [Node y [], Node z []]) == star x [y,z]++ test "tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == edges1 [(1,2), (1,3), (3,4), (3,5)]" $+ tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == edges1 [(1,2), (1,3), (3,4), (3,5::Int)]++ putStrLn $ "\n============ NonEmpty.Graph.mesh1 ============"+ test "mesh1 [x] [y] == vertex (x, y)" $ \(x :: Int) (y :: Int) ->+ mesh1 [x] [y] == vertex (x, y)++ test "mesh1 xs ys == box (path1 xs) (path1 ys)" $ \(xs' :: NonEmptyList Int) (ys' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ ys = NonEmpty.fromList (getNonEmpty ys')+ in mesh1 xs ys == box (path1 xs) (path1 ys)++ test "mesh1 [1,2,3] ['a', 'b'] == <correct result>" $+ mesh1 [1,2,3] ['a', 'b'] == edges1 [ ((1,'a'),(1,'b')), ((1,'a'),(2,'a'))+ , ((1,'b'),(2,'b')), ((2,'a'),(2,'b'))+ , ((2,'a'),(3,'a')), ((2,'b'),(3,'b'))+ , ((3,'a'),(3 :: Int,'b')) ]++ test "size (mesh xs ys) == max 1 (3 * length xs * length ys - length xs - length ys -1)" $ \(xs' :: NonEmptyList Int) (ys' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ ys = NonEmpty.fromList (getNonEmpty ys')+ in size (mesh1 xs ys) == max 1 (3 * length xs * length ys - length xs - length ys -1)++ putStrLn $ "\n============ NonEmpty.Graph.torus1 ============"+ test "torus1 [x] [y] == edge (x,y) (x,y)" $ \(x :: Int) (y :: Int) ->+ torus1 [x] [y] == edge (x,y) (x,y)++ test "torus1 xs ys == box (circuit1 xs) (circuit1 ys)" $ \(xs' :: NonEmptyList Int) (ys' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ ys = NonEmpty.fromList (getNonEmpty ys')+ in torus1 xs ys == box (circuit1 xs) (circuit1 ys)++ test "torus1 [1,2] ['a', 'b'] == <correct result>" $+ torus1 [1,2] ['a', 'b'] == edges1 [ ((1,'a'),(1,'b')), ((1,'a'),(2,'a'))+ , ((1,'b'),(1,'a')), ((1,'b'),(2,'b'))+ , ((2,'a'),(1,'a')), ((2,'a'),(2,'b'))+ , ((2,'b'),(1,'b')), ((2,'b'),(2 :: Int,'a')) ]++ test "size (torus1 xs ys) == max 1 (3 * length xs * length ys)" $ \(xs' :: NonEmptyList Int) (ys' :: NonEmptyList Int) ->+ let xs = NonEmpty.fromList (getNonEmpty xs')+ ys = NonEmpty.fromList (getNonEmpty ys')+ in size (torus1 xs ys) == max 1 (3 * length xs * length ys)++ putStrLn $ "\n============ NonEmpty.Graph.removeVertex1 ============"+ test "removeVertex1 x (vertex x) == Nothing" $ \(x :: Int) ->+ removeVertex1 x (vertex x) == Nothing++ test "removeVertex1 1 (vertex 2) == Just (vertex 2)" $+ removeVertex1 1 (vertex 2) == Just (vertex 2 :: G)++ test "removeVertex1 x (edge x x) == Nothing" $ \(x :: Int) ->+ removeVertex1 x (edge x x) == Nothing++ test "removeVertex1 1 (edge 1 2) == Just (vertex 2)" $+ removeVertex1 1 (edge 1 2) == Just (vertex 2 :: G)++ test "removeVertex1 x >=> removeVertex1 x == removeVertex1 x" $ \(x :: Int) y ->+ (removeVertex1 x >=> removeVertex1 x) y == removeVertex1 x y++ putStrLn $ "\n============ NonEmpty.Graph.removeEdge ============"+ test "removeEdge x y (edge x y) == vertices1 [x,y]" $ \(x :: Int) y ->+ removeEdge x y (edge x y) == vertices1 [x,y]++ test "removeEdge x y . removeEdge x y == removeEdge x y" $ \(x :: Int) y z ->+ (removeEdge x y . removeEdge x y) z == removeEdge x y z++ test "removeEdge 1 1 (1 * 1 * 2 * 2) == 1 * 2 * 2" $+ removeEdge 1 1 (1 * 1 * 2 * 2) == 1 * 2 * (2 :: G)++ test "removeEdge 1 2 (1 * 1 * 2 * 2) == 1 * 1 + 2 * 2" $+ removeEdge 1 2 (1 * 1 * 2 * 2) == 1 * 1 + 2 * (2 :: G)++ test "size (removeEdge x y z) <= 3 * size z" $ \(x :: Int) y z ->+ size (removeEdge x y z) <= 3 * size z++ putStrLn $ "\n============ NonEmpty.Graph.replaceVertex ============"+ test "replaceVertex x x == id" $ \(x :: Int) y ->+ replaceVertex x x y == y++ test "replaceVertex x y (vertex x) == vertex y" $ \(x :: Int) y ->+ replaceVertex x y (vertex x) == vertex y++ test "replaceVertex x y == mergeVertices (== x) y" $ \(x :: Int) y z ->+ replaceVertex x y z == mergeVertices (== x) y z++ putStrLn $ "\n============ NonEmpty.Graph.mergeVertices ============"+ test "mergeVertices (const False) x == id" $ \(x :: Int) y ->+ mergeVertices (const False) x y == y++ test "mergeVertices (== x) y == replaceVertex x y" $ \(x :: Int) y z ->+ mergeVertices (== x) y z == replaceVertex x y z++ test "mergeVertices even 1 (0 * 2) == 1 * 1" $+ mergeVertices even 1 (0 * 2) == (1 * 1 :: G)++ test "mergeVertices odd 1 (3 + 4 * 5) == 4 * 1" $+ mergeVertices odd 1 (3 + 4 * 5) == (4 * 1 :: G)++ putStrLn $ "\n============ NonEmpty.Graph.splitVertex1 ============"+ test "splitVertex1 x [x] == id" $ \x (y :: G) ->+ splitVertex1 x [x] y == y++ test "splitVertex1 x [y] == replaceVertex x y" $ \x y (z :: G) ->+ splitVertex1 x [y] z == replaceVertex x y z++ test "splitVertex1 1 [0,1] $ 1 * (2 + 3) == (0 + 1) * (2 + 3)" $+ splitVertex1 1 [0,1] (1 * (2 + 3)) == (0 + 1) * (2 + 3 :: G)++ putStrLn $ "\n============ NonEmpty.Graph.transpose ============"+ test "transpose (vertex x) == vertex x" $ \(x :: Int) ->+ transpose (vertex x) == vertex x++ test "transpose (edge x y) == edge y x" $ \(x :: Int) y ->+ transpose (edge x y) == edge y x++ test "transpose . transpose == id" $ \(x :: G) ->+ (transpose . transpose) x == x++ test "transpose (box x y) == box (transpose x) (transpose y)" $ mapSize (min 10) $ \(x :: G) (y :: G) ->+ transpose (box x y) == box (transpose x) (transpose y)++ test "edgeList . transpose == sort . map swap . edgeList" $ \(x :: G) ->+ (edgeList . transpose) x == (sort . map swap . edgeList) x++ putStrLn $ "\n============ NonEmpty.Graph.induce1 ============"+ test "induce1 (const True ) x == Just x" $ \(x :: G) ->+ induce1 (const True ) x == Just x++ test "induce1 (const False) x == Nothing" $ \(x :: G) ->+ induce1 (const False) x == Nothing++ test "induce1 (/= x) == removeVertex1 x" $ \(x :: Int) y ->+ induce1 (/= x) y == removeVertex1 x y++ test "induce1 p >=> induce1 q == induce1 (\\x -> p x && q x)" $ \(apply -> p) (apply -> q) (y :: G) ->+ (induce1 p >=> induce1 q) y == induce1 (\x -> p x && q x) y++ putStrLn $ "\n============ NonEmpty.Graph.simplify ============"+ test "simplify == id" $ \(x :: G) ->+ simplify x == x++ test "size (simplify x) <= size x" $ \(x :: G) ->+ size (simplify x) <= size x++ test "simplify 1 === 1" $+ simplify 1 === (1 :: G)++ test "simplify (1 + 1) === 1" $+ simplify (1 + 1) === (1 :: G)++ test "simplify (1 + 2 + 1) === 1 + 2" $+ simplify (1 + 2 + 1) === (1 + 2 :: G)++ test "simplify (1 * 1 * 1) === 1 * 1" $+ simplify (1 * 1 * 1) === (1 * 1 :: G)++ putStrLn "\n============ NonEmpty.Graph.sparsify ============"+ test "sort . reachable x == sort . rights . reachable (Right x) . sparsify" $ \x (y :: G) ->+ (sort . reachable x) y == (sort . rights . reachable (Right x) . sparsify) y++ test "vertexCount (sparsify x) <= vertexCount x + size x + 1" $ \(x :: G) ->+ vertexCount (sparsify x) <= vertexCount x + size x + 1++ test "edgeCount (sparsify x) <= 3 * size x" $ \(x :: G) ->+ edgeCount (sparsify x) <= 3 * size x++ test "size (sparsify x) <= 3 * size x" $ \(x :: G) ->+ size (sparsify x) <= 3 * size x++ putStrLn "\n============ NonEmpty.Graph.box ============"+ test "box (path1 [0,1]) (path1 ['a','b']) == <correct result>" $ mapSize (min 10) $+ box (path1 [0,1]) (path1 ['a','b']) == edges1 [ ((0,'a'), (0,'b'))+ , ((0,'a'), (1,'a'))+ , ((0,'b'), (1,'b'))+ , ((1,'a'), (1::Int,'b')) ]++ let unit = fmap $ \(a, ()) -> a+ comm = fmap $ \(a, b) -> (b, a)+ test "box x y ~~ box y x" $ mapSize (min 10) $ \(x :: G) (y :: G) ->+ comm (box x y) == box y x++ test "box x (overlay y z) == overlay (box x y) (box x z)" $ mapSize (min 10) $ \(x :: G) (y :: G) z ->+ box x (overlay y z) == overlay (box x y) (box x z)++ test "box x (vertex ()) ~~ x" $ mapSize (min 10) $ \(x :: G) ->+ unit(box x (vertex ())) == x++ let assoc = fmap $ \(a, (b, c)) -> ((a, b), c)+ test "box x (box y z) ~~ box (box x y) z" $ mapSize (min 5) $ \(x :: G) (y :: G) (z :: G) ->+ assoc (box x (box y z)) == box (box x y) z++ test "transpose (box x y) == box (transpose x) (transpose y)" $ mapSize (min 10) $ \(x :: G) (y :: G) ->+ transpose (box x y) == box (transpose x) (transpose y)++ test "vertexCount (box x y) == vertexCount x * vertexCount y" $ mapSize (min 10) $ \(x :: G) (y :: G) ->+ vertexCount (box x y) == vertexCount x * vertexCount y++ test "edgeCount (box x y) <= vertexCount x * edgeCount y + edgeCount x * vertexCount y" $ mapSize (min 10) $ \(x :: G) (y :: G) ->+ edgeCount (box x y) <= vertexCount x * edgeCount y + edgeCount x * vertexCount y
− test/Algebra/Graph/Test/NonEmptyGraph.hs
@@ -1,665 +0,0 @@-{-# LANGUAGE CPP, ViewPatterns #-}--------------------------------------------------------------------------------- |--- Module : Algebra.Graph.Test.NonEmptyGraph--- Copyright : (c) Andrey Mokhov 2016-2018--- License : MIT (see the file LICENSE)--- Maintainer : andrey.mokhov@gmail.com--- Stability : experimental------ Testsuite for "Algebra.Graph.NonEmpty".-------------------------------------------------------------------------------module Algebra.Graph.Test.NonEmptyGraph (- -- * Testsuite- testGraphNonEmpty- ) where--import Prelude ()-import Prelude.Compat--#if !MIN_VERSION_base(4,11,0)-import Data.Semigroup-#endif--import Control.Monad-import Data.Either-import Data.List.NonEmpty (NonEmpty (..))-import Data.Maybe-import Data.Tree-import Data.Tuple--import Algebra.Graph.NonEmpty-import Algebra.Graph.Test hiding (axioms, theorems)-import Algebra.Graph.ToGraph (reachable, toGraph)--import qualified Algebra.Graph as G-import qualified Data.List.NonEmpty as NonEmpty-import qualified Data.Set as Set-import qualified Data.IntSet as IntSet--type G = NonEmptyGraph Int--axioms :: G -> G -> G -> Property-axioms x y z = conjoin- [ x + y == y + x // "Overlay commutativity"- , x + (y + z) == (x + y) + z // "Overlay associativity"- , x * (y * z) == (x * y) * z // "Connect associativity"- , x * (y + z) == x * y + x * z // "Left distributivity"- , (x + y) * z == x * z + y * z // "Right distributivity"- , x * y * z == x * y + x * z + y * z // "Decomposition" ]--theorems :: G -> G -> Property-theorems x y = conjoin- [ x + x == x // "Overlay idempotence"- , x + y + x * y == x * y // "Absorption"- , x * x == x * x * x // "Connect saturation"- , x <= x + y // "Overlay order"- , x + y <= x * y // "Overlay-connect order" ]- where- (<=) = isSubgraphOf- infixl 4 <=--testGraphNonEmpty :: IO ()-testGraphNonEmpty = do- putStrLn "\n============ Graph.NonEmpty ============"- test "Axioms of non-empty graphs" axioms- test "Theorems of non-empty graphs" theorems-- putStrLn $ "\n============ Functor (NonEmptyGraph a) ============"- test "fmap f (vertex x) == vertex (f x)" $ \(apply -> f) (x :: Int) ->- fmap f (vertex x) == vertex (f x :: Int)-- test "fmap f (edge x y) == edge (f x) (f y)" $ \(apply -> f) (x :: Int) y ->- fmap f (edge x y) == edge (f x) (f y :: Int)-- test "fmap id == id" $ \(x :: G) ->- fmap id x == x-- test "fmap f . fmap g == fmap (f . g)" $ \(apply -> f) (apply -> g) (x :: G) ->- (fmap f . fmap g) x == (fmap (f . (g :: Int -> Int)) x :: G)-- putStrLn $ "\n============ Monad (NonEmptyGraph a) ============"- test "(vertex x >>= f) == f x" $ \(apply -> f) (x :: Int) ->- (vertex x >>= f) == (f x :: G)-- test "(edge x y >>= f) == connect (f x) (f y)" $ \(apply -> f) (x :: Int) y ->- (edge x y >>= f) == connect (f x) (f y :: G)-- test "(vertices1 xs >>= f) == overlays1 (fmap f xs)" $ mapSize (min 10) $ \(xs' :: NonEmptyList Int) (apply -> f) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- in (vertices1 xs >>= f) == (overlays1 (fmap f xs) :: G)-- test "(x >>= vertex) == x" $ \(x :: G) ->- (x >>= vertex) == x-- test "((x >>= f) >>= g) == (x >>= (\\y -> (f y) >>= g))" $ mapSize (min 10) $ \(x :: G) (apply -> f) (apply -> g) ->- ((x >>= f) >>= g) == (x >>= (\(y :: Int) -> (f y) >>= (g :: Int -> G)))-- putStrLn $ "\n============ Graph.NonEmpty.toNonEmptyGraph ============"- test "toNonEmptyGraph empty == Nothing" $- toNonEmptyGraph (G.empty :: G.Graph Int) == Nothing-- test "toNonEmptyGraph (toGraph x) == Just (x :: NonEmptyGraph a)" $ \x ->- toNonEmptyGraph (toGraph x) == Just (x :: NonEmptyGraph Int)-- putStrLn $ "\n============ Graph.NonEmpty.vertex ============"- test "hasVertex x (vertex x) == True" $ \(x :: Int) ->- hasVertex x (vertex x) == True-- test "vertexCount (vertex x) == 1" $ \(x :: Int) ->- vertexCount (vertex x) == 1-- test "edgeCount (vertex x) == 0" $ \(x :: Int) ->- edgeCount (vertex x) == 0-- test "size (vertex x) == 1" $ \(x :: Int) ->- size (vertex x) == 1-- putStrLn $ "\n============ Graph.NonEmpty.edge ============"- test "edge x y == connect (vertex x) (vertex y)" $ \(x :: Int) y ->- edge x y == connect (vertex x) (vertex y)-- test "hasEdge x y (edge x y) == True" $ \(x :: Int) y ->- hasEdge x y (edge x y) == True-- test "edgeCount (edge x y) == 1" $ \(x :: Int) y ->- edgeCount (edge x y) == 1-- test "vertexCount (edge 1 1) == 1" $- vertexCount (edge 1 1 :: G) == 1-- test "vertexCount (edge 1 2) == 2" $- vertexCount (edge 1 2 :: G) == 2-- putStrLn $ "\n============ Graph.NonEmpty.overlay ============"- test "hasVertex z (overlay x y) == hasVertex z x || hasVertex z y" $ \(x :: G) y z ->- hasVertex z (overlay x y) == hasVertex z x || hasVertex z y-- test "vertexCount (overlay x y) >= vertexCount x" $ \(x :: G) y ->- vertexCount (overlay x y) >= vertexCount x-- test "vertexCount (overlay x y) <= vertexCount x + vertexCount y" $ \(x :: G) y ->- vertexCount (overlay x y) <= vertexCount x + vertexCount y-- test "edgeCount (overlay x y) >= edgeCount x" $ \(x :: G) y ->- edgeCount (overlay x y) >= edgeCount x-- test "edgeCount (overlay x y) <= edgeCount x + edgeCount y" $ \(x :: G) y ->- edgeCount (overlay x y) <= edgeCount x + edgeCount y-- test "size (overlay x y) == size x + size y" $ \(x :: G) y ->- size (overlay x y) == size x + size y-- test "vertexCount (overlay 1 2) == 2" $- vertexCount (overlay 1 2 :: G) == 2-- test "edgeCount (overlay 1 2) == 0" $- edgeCount (overlay 1 2 :: G) == 0-- putStrLn $ "\n============ Graph.NonEmpty.overlay1 ============"- test " overlay1 empty x == x" $ \(x :: G) ->- overlay1 G.empty x == x-- test "x /= empty ==> overlay1 x y == overlay (fromJust $ toNonEmptyGraph x) y" $ \(x :: G.Graph Int) (y :: G) ->- x /= G.empty ==> overlay1 x y == overlay (fromJust $ toNonEmptyGraph x) y--- putStrLn $ "\n============ Graph.NonEmpty.connect ============"- test "hasVertex z (connect x y) == hasVertex z x || hasVertex z y" $ \(x :: G) y z ->- hasVertex z (connect x y) == hasVertex z x || hasVertex z y-- test "vertexCount (connect x y) >= vertexCount x" $ \(x :: G) y ->- vertexCount (connect x y) >= vertexCount x-- test "vertexCount (connect x y) <= vertexCount x + vertexCount y" $ \(x :: G) y ->- vertexCount (connect x y) <= vertexCount x + vertexCount y-- test "edgeCount (connect x y) >= edgeCount x" $ \(x :: G) y ->- edgeCount (connect x y) >= edgeCount x-- test "edgeCount (connect x y) >= edgeCount y" $ \(x :: G) y ->- edgeCount (connect x y) >= edgeCount y-- test "edgeCount (connect x y) >= vertexCount x * vertexCount y" $ \(x :: G) y ->- edgeCount (connect x y) >= vertexCount x * vertexCount y-- test "edgeCount (connect x y) <= vertexCount x * vertexCount y + edgeCount x + edgeCount y" $ \(x :: G) y ->- edgeCount (connect x y) <= vertexCount x * vertexCount y + edgeCount x + edgeCount y-- test "size (connect x y) == size x + size y" $ \(x :: G) y ->- size (connect x y) == size x + size y-- test "vertexCount (connect 1 2) == 2" $- vertexCount (connect 1 2 :: G) == 2-- test "edgeCount (connect 1 2) == 1" $- edgeCount (connect 1 2 :: G) == 1-- putStrLn $ "\n============ Graph.NonEmpty.vertices1 ============"- test "vertices1 (x :| []) == vertex x" $ \(x :: Int) ->- vertices1 (x :| []) == vertex x-- test "hasVertex x . vertices1 == elem x" $ \(x :: Int) (xs' :: NonEmptyList Int) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- in (hasVertex x . vertices1) xs == elem x (NonEmpty.toList xs)-- test "vertexCount . vertices1 == length . nub" $ \(xs' :: NonEmptyList Int) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- in (vertexCount . vertices1) xs == (NonEmpty.length . NonEmpty.nub) xs-- test "vertexSet . vertices1 == Set.fromList . toList" $ \(xs' :: NonEmptyList Int) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- in (vertexSet . vertices1) xs == (Set.fromList . NonEmpty.toList) xs-- putStrLn $ "\n============ Graph.NonEmpty.edges1 ============"- test "edges1 ((x,y) :| []) == edge x y" $ \(x :: Int) y ->- edges1 ((x,y) :| []) == edge x y-- test "edgeCount . edges1 == length . nub" $ \(xs' :: NonEmptyList (Int, Int)) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- in (edgeCount . edges1) xs == (NonEmpty.length . NonEmpty.nub) xs-- putStrLn $ "\n============ Graph.NonEmpty.overlays1 ============"- test "overlays1 (x :| [] ) == x" $ \(x :: G) ->- overlays1 (x :| [] ) == x-- test "overlays1 (x :| [y]) == overlay x y" $ \(x :: G) y ->- overlays1 (x :| [y]) == overlay x y-- putStrLn $ "\n============ Graph.NonEmpty.connects1 ============"- test "connects1 (x :| [] ) == x" $ \(x :: G) ->- connects1 (x :| [] ) == x-- test "connects1 (x :| [y]) == connect x y" $ \(x :: G) y ->- connects1 (x :| [y]) == connect x y-- putStrLn $ "\n============ Graph.NonEmpty.foldg1 ============"- test "foldg1 (const 1) (+) (+) == size" $ \(x :: G) ->- foldg1 (const 1) (+) (+) x == size x-- test "foldg1 (==x) (||) (||) == hasVertex x" $ \(x :: Int) y ->- foldg1 (==x) (||) (||) y == hasVertex x y-- putStrLn $ "\n============ Graph.NonEmpty.isSubgraphOf ============"- test "isSubgraphOf x (overlay x y) == True" $ \(x :: G) y ->- isSubgraphOf x (overlay x y) == True-- test "isSubgraphOf (overlay x y) (connect x y) == True" $ \(x :: G) y ->- isSubgraphOf (overlay x y) (connect x y) == True-- test "isSubgraphOf (path1 xs) (circuit1 xs) == True" $ \(xs' :: NonEmptyList Int) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- in isSubgraphOf (path1 xs) (circuit1 xs) == True-- putStrLn "\n============ Graph.NonEmpty.(===) ============"- test " x === x == True" $ \(x :: G) ->- (x === x) == True-- test "x + y === x + y == True" $ \(x :: G) y ->- (x + y === x + y) == True-- test "1 + 2 === 2 + 1 == False" $- (1 + 2 === 2 + (1 :: G)) == False-- test "x + y === x * y == False" $ \(x :: G) y ->- (x + y === x * y) == False-- putStrLn $ "\n============ Graph.NonEmpty.size ============"- test "size (vertex x) == 1" $ \(x :: Int) ->- size (vertex x) == 1-- test "size (overlay x y) == size x + size y" $ \(x :: G) y ->- size (overlay x y) == size x + size y-- test "size (connect x y) == size x + size y" $ \(x :: G) y ->- size (connect x y) == size x + size y-- test "size x >= 1" $ \(x :: G) ->- size x >= 1-- test "size x >= vertexCount x" $ \(x :: G) ->- size x >= vertexCount x-- putStrLn $ "\n============ Graph.NonEmpty.hasVertex ============"- test "hasVertex x (vertex x) == True" $ \(x :: Int) ->- hasVertex x (vertex x) == True-- test "hasVertex 1 (vertex 2) == False" $- hasVertex 1 (vertex 2 :: G) == False-- putStrLn $ "\n============ Graph.NonEmpty.hasEdge ============"- test "hasEdge x y (vertex z) == False" $ \(x :: Int) y z ->- hasEdge x y (vertex z) == False-- test "hasEdge x y (edge x y) == True" $ \(x :: Int) y ->- hasEdge x y (edge x y) == True-- test "hasEdge x y . removeEdge x y == const False" $ \(x :: Int) y z ->- (hasEdge x y . removeEdge x y) z == False-- test "hasEdge x y == elem (x,y) . edgeList" $ \(x :: Int) y z -> do- (u, v) <- elements ((x, y) : edgeList z)- return $ hasEdge u v z == elem (u, v) (edgeList z)-- putStrLn $ "\n============ Graph.NonEmpty.vertexCount ============"- test "vertexCount (vertex x) == 1" $ \(x :: Int) ->- vertexCount (vertex x) == 1-- test "vertexCount x >= 1" $ \(x :: G) ->- vertexCount x >= 1-- test "vertexCount == length . vertexList1" $ \(x :: G) ->- vertexCount x == (NonEmpty.length . vertexList1) x-- putStrLn $ "\n============ Graph.NonEmpty.edgeCount ============"- test "edgeCount (vertex x) == 0" $ \(x :: Int) ->- edgeCount (vertex x) == 0-- test "edgeCount (edge x y) == 1" $ \(x :: Int) y ->- edgeCount (edge x y) == 1-- test "edgeCount == length . edgeList" $ \(x :: G) ->- edgeCount x == (length . edgeList) x-- putStrLn $ "\n============ Graph.NonEmpty.vertexList1 ============"- test "vertexList1 (vertex x) == x :| []" $ \(x :: Int) ->- vertexList1 (vertex x) == x :| []-- test "vertexList1 . vertices1 == nub . sort" $ \(xs' :: NonEmptyList Int) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- in (vertexList1 . vertices1) xs == (NonEmpty.nub . NonEmpty.sort) xs-- putStrLn $ "\n============ Graph.NonEmpty.edgeList ============"- test "edgeList (vertex x) == []" $ \(x :: Int) ->- edgeList (vertex x) == []-- test "edgeList (edge x y) == [(x,y)]" $ \(x :: Int) y ->- edgeList (edge x y) == [(x,y)]-- test "edgeList (star 2 [3,1]) == [(2,1), (2,3)]" $- edgeList (star 2 [3,1]) == [(2,1), (2,3 :: Int)]-- test "edgeList . edges1 == nub . sort . toList" $ \(xs' :: NonEmptyList (Int, Int)) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- in (edgeList . edges1) xs == (nubOrd . sort . NonEmpty.toList) xs-- test "edgeList . transpose == sort . map swap . edgeList" $ \(x :: G) ->- (edgeList . transpose) x == (sort . map swap . edgeList) x-- putStrLn $ "\n============ Graph.NonEmpty.vertexSet ============"- test "vertexSet . vertex == Set.singleton" $ \(x :: Int) ->- (vertexSet . vertex) x == Set.singleton x-- test "vertexSet . vertices1 == Set.fromList . toList" $ \(xs' :: NonEmptyList Int) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- in (vertexSet . vertices1) xs == (Set.fromList . NonEmpty.toList) xs-- test "vertexSet . clique1 == Set.fromList . toList" $ \(xs' :: NonEmptyList Int) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- in (vertexSet . clique1) xs == (Set.fromList . NonEmpty.toList) xs-- putStrLn $ "\n============ Graph.NonEmpty.vertexIntSet ============"- test "vertexIntSet . vertex == IntSet.singleton" $ \(x :: Int) ->- (vertexIntSet . vertex) x == IntSet.singleton x-- test "vertexIntSet . vertices1 == IntSet.fromList . toList" $ \(xs' :: NonEmptyList Int) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- in (vertexIntSet . vertices1) xs == (IntSet.fromList . NonEmpty.toList) xs-- test "vertexIntSet . clique1 == IntSet.fromList . toList" $ \(xs' :: NonEmptyList Int) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- in (vertexIntSet . clique1) xs == (IntSet.fromList . NonEmpty.toList) xs-- putStrLn $ "\n============ Graph.NonEmpty.edgeSet ============"- test "edgeSet (vertex x) == Set.empty" $ \(x :: Int) ->- edgeSet (vertex x) == Set.empty-- test "edgeSet (edge x y) == Set.singleton (x,y)" $ \(x :: Int) y ->- edgeSet (edge x y) == Set.singleton (x,y)-- test "edgeSet . edges1 == Set.fromList . toList" $ \(xs' :: NonEmptyList (Int, Int)) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- in (edgeSet . edges1) xs == (Set.fromList . NonEmpty.toList) xs-- putStrLn $ "\n============ Graph.NonEmpty.path1 ============"- test "path1 (x :| [] ) == vertex x" $ \(x :: Int) ->- path1 (x :| [] ) == vertex x-- test "path1 (x :| [y]) == edge x y" $ \(x :: Int) y ->- path1 (x :| [y]) == edge x y-- test "path1 . reverse == transpose . path1" $ \(xs' :: NonEmptyList Int) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- in (path1 . NonEmpty.reverse) xs == (transpose . path1) xs-- putStrLn $ "\n============ Graph.NonEmpty.circuit1 ============"- test "circuit1 (x :| [] ) == edge x x" $ \(x :: Int) ->- circuit1 (x :| [] ) == edge x x-- test "circuit1 (x :| [y]) == edges1 ((x,y) :| [(y,x)])" $ \(x :: Int) y ->- circuit1 (x :| [y]) == edges1 ((x,y) :| [(y,x)])-- test "circuit1 . reverse == transpose . circuit1" $ \(xs' :: NonEmptyList Int) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- in (circuit1 . NonEmpty.reverse) xs == (transpose . circuit1) xs-- putStrLn $ "\n============ Graph.NonEmpty.clique1 ============"- test "clique1 (x :| [] ) == vertex x" $ \(x :: Int) ->- clique1 (x :| [] ) == vertex x-- test "clique1 (x :| [y] ) == edge x y" $ \(x :: Int) y ->- clique1 (x :| [y] ) == edge x y-- test "clique1 (x :| [y,z]) == edges1 ((x,y) :| [(x,z), (y,z)])" $ \(x :: Int) y z ->- clique1 (x :| [y,z]) == edges1 ((x,y) :| [(x,z), (y,z)])-- test "clique1 (xs <> ys) == connect (clique1 xs) (clique1 ys)" $ \(xs' :: NonEmptyList Int) ys' ->- let xs = NonEmpty.fromList (getNonEmpty xs')- ys = NonEmpty.fromList (getNonEmpty ys')- in clique1 (xs <> ys) == connect (clique1 xs) (clique1 ys)-- test "clique1 . reverse == transpose . clique1" $ \(xs' :: NonEmptyList Int) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- in (clique1 . NonEmpty.reverse) xs == (transpose . clique1) xs-- putStrLn $ "\n============ Graph.NonEmpty.biclique1 ============"- test "biclique1 (x1 :| [x2]) (y1 :| [y2]) == edges1 ((x1,y1) :| [(x1,y2), (x2,y1), (x2,y2)])" $ \(x1 :: Int) x2 y1 y2 ->- biclique1 (x1 :| [x2]) (y1 :| [y2]) == edges1 ((x1,y1) :| [(x1,y2), (x2,y1), (x2,y2)])-- test "biclique1 xs ys == connect (vertices1 xs) (vertices1 ys)" $ \(xs' :: NonEmptyList Int) ys' ->- let xs = NonEmpty.fromList (getNonEmpty xs')- ys = NonEmpty.fromList (getNonEmpty ys')- in biclique1 xs ys == connect (vertices1 xs) (vertices1 ys)-- putStrLn $ "\n============ Graph.NonEmpty.star ============"- test "star x [] == vertex x" $ \(x :: Int) ->- star x [] == vertex x-- test "star x [y] == edge x y" $ \(x :: Int) y ->- star x [y] == edge x y-- test "star x [y,z] == edges1 ((x,y) :| [(x,z)])" $ \(x :: Int) y z ->- star x [y,z] == edges1 ((x,y) :| [(x,z)])-- putStrLn $ "\n============ Graph.NonEmpty.stars1 ============"- test "stars1 ((x, []) :| []) == vertex x" $ \(x :: Int) ->- stars1 ((x, []) :| []) == vertex x-- test "stars1 ((x, [y]) :| []) == edge x y" $ \(x :: Int) y ->- stars1 ((x, [y]) :| []) == edge x y-- test "stars1 ((x, ys) :| []) == star x ys" $ \(x :: Int) ys ->- stars1 ((x, ys) :| []) == star x ys-- test "stars1 == overlays1 . fmap (uncurry star)" $ \(xs' :: NonEmptyList (Int, [Int])) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- in stars1 xs == overlays1 (fmap (uncurry star) xs)-- test "overlay (stars1 xs) (stars1 ys) == stars1 (xs <> ys)" $ \(xs' :: NonEmptyList (Int, [Int])) (ys' :: NonEmptyList (Int, [Int])) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- ys = NonEmpty.fromList (getNonEmpty ys')- in overlay (stars1 xs) (stars1 ys) == stars1 (xs <> ys)-- putStrLn $ "\n============ Graph.NonEmpty.tree ============"- test "tree (Node x []) == vertex x" $ \(x :: Int) ->- tree (Node x []) == vertex x-- test "tree (Node x [Node y [Node z []]]) == path1 (x :| [y,z])" $ \(x :: Int) y z ->- tree (Node x [Node y [Node z []]]) == path1 (x :| [y,z])-- test "tree (Node x [Node y [], Node z []]) == star x [y,z]" $ \(x :: Int) y z ->- tree (Node x [Node y [], Node z []]) == star x [y,z]-- test "tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == edges1 ((1,2) :| [(1,3), (3,4), (3,5)])" $- tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == edges1 ((1,2) :| [(1,3), (3,4), (3,5 :: Int)])-- putStrLn $ "\n============ Graph.NonEmpty.mesh1 ============"- test "mesh1 (x :| []) (y :| []) == vertex (x, y)" $ \(x :: Int) (y :: Int) ->- mesh1 (x :| []) (y :| []) == vertex (x, y)-- test "mesh1 xs ys == box (path1 xs) (path1 ys)" $ \(xs' :: NonEmptyList Int) (ys' :: NonEmptyList Int) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- ys = NonEmpty.fromList (getNonEmpty ys')- in mesh1 xs ys == box (path1 xs) (path1 ys)-- test "mesh1 (1 :| [2,3]) ('a' :| \"b\") == <correct result>" $- mesh1 (1 :| [2,3]) ('a' :| "b") == edges1 (NonEmpty.fromList [ ((1,'a'),(1,'b')), ((1,'a'),(2,'a'))- , ((1,'b'),(2,'b')), ((2,'a'),(2,'b'))- , ((2,'a'),(3,'a')), ((2,'b'),(3,'b'))- , ((3,'a'),(3 :: Int,'b')) ])-- test "size (mesh xs ys) == max 1 (3 * length xs * length ys - length xs - length ys -1)" $ \(xs' :: NonEmptyList Int) (ys' :: NonEmptyList Int) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- ys = NonEmpty.fromList (getNonEmpty ys')- in size (mesh1 xs ys) == max 1 (3 * length xs * length ys - length xs - length ys -1)-- putStrLn $ "\n============ Graph.NonEmpty.torus1 ============"- test "torus1 (x :| []) (y :| []) == edge (x,y) (x,y)" $ \(x :: Int) (y :: Int) ->- torus1 (x :| []) (y :| []) == edge (x,y) (x,y)-- test "torus1 xs ys == box (circuit1 xs) (circuit1 ys)" $ \(xs' :: NonEmptyList Int) (ys' :: NonEmptyList Int) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- ys = NonEmpty.fromList (getNonEmpty ys')- in torus1 xs ys == box (circuit1 xs) (circuit1 ys)-- test "torus1 (1 :| [2]) ('a' :| \"b\") == <correct result>" $- torus1 (1 :| [2]) ('a' :| "b") == edges1 (NonEmpty.fromList [ ((1,'a'),(1,'b')), ((1,'a'),(2,'a'))- , ((1,'b'),(1,'a')), ((1,'b'),(2,'b'))- , ((2,'a'),(1,'a')), ((2,'a'),(2,'b'))- , ((2,'b'),(1,'b')), ((2,'b'),(2 :: Int,'a')) ])-- test "size (torus1 xs ys) == max 1 (3 * length xs * length ys)" $ \(xs' :: NonEmptyList Int) (ys' :: NonEmptyList Int) ->- let xs = NonEmpty.fromList (getNonEmpty xs')- ys = NonEmpty.fromList (getNonEmpty ys')- in size (torus1 xs ys) == max 1 (3 * length xs * length ys)-- putStrLn $ "\n============ Graph.NonEmpty.removeVertex1 ============"- test "removeVertex1 x (vertex x) == Nothing" $ \(x :: Int) ->- removeVertex1 x (vertex x) == Nothing-- test "removeVertex1 1 (vertex 2) == Just (vertex 2)" $- removeVertex1 1 (vertex 2) == Just (vertex 2 :: G)-- test "removeVertex1 x (edge x x) == Nothing" $ \(x :: Int) ->- removeVertex1 x (edge x x) == Nothing-- test "removeVertex1 1 (edge 1 2) == Just (vertex 2)" $- removeVertex1 1 (edge 1 2) == Just (vertex 2 :: G)-- test "removeVertex1 x >=> removeVertex1 x == removeVertex1 x" $ \(x :: Int) y ->- (removeVertex1 x >=> removeVertex1 x) y == removeVertex1 x y-- putStrLn $ "\n============ Graph.NonEmpty.removeEdge ============"- test "removeEdge x y (edge x y) == vertices1 (x :| [y])" $ \(x :: Int) y ->- removeEdge x y (edge x y) == vertices1 (x :| [y])-- test "removeEdge x y . removeEdge x y == removeEdge x y" $ \(x :: Int) y z ->- (removeEdge x y . removeEdge x y) z == removeEdge x y z-- test "removeEdge 1 1 (1 * 1 * 2 * 2) == 1 * 2 * 2" $- removeEdge 1 1 (1 * 1 * 2 * 2) == 1 * 2 * (2 :: NonEmptyGraph Int)-- test "removeEdge 1 2 (1 * 1 * 2 * 2) == 1 * 1 + 2 * 2" $- removeEdge 1 2 (1 * 1 * 2 * 2) == 1 * 1 + 2 * (2 :: NonEmptyGraph Int)-- test "size (removeEdge x y z) <= 3 * size z" $ \(x :: Int) y z ->- size (removeEdge x y z) <= 3 * size z-- putStrLn $ "\n============ Graph.NonEmpty.replaceVertex ============"- test "replaceVertex x x == id" $ \(x :: Int) y ->- replaceVertex x x y == y-- test "replaceVertex x y (vertex x) == vertex y" $ \(x :: Int) y ->- replaceVertex x y (vertex x) == vertex y-- test "replaceVertex x y == mergeVertices (== x) y" $ \(x :: Int) y z ->- replaceVertex x y z == mergeVertices (== x) y z-- putStrLn $ "\n============ Graph.NonEmpty.mergeVertices ============"- test "mergeVertices (const False) x == id" $ \(x :: Int) y ->- mergeVertices (const False) x y == y-- test "mergeVertices (== x) y == replaceVertex x y" $ \(x :: Int) y z ->- mergeVertices (== x) y z == replaceVertex x y z-- test "mergeVertices even 1 (0 * 2) == 1 * 1" $- mergeVertices even 1 (0 * 2) == (1 * 1 :: G)-- test "mergeVertices odd 1 (3 + 4 * 5) == 4 * 1" $- mergeVertices odd 1 (3 + 4 * 5) == (4 * 1 :: G)-- putStrLn $ "\n============ Graph.NonEmpty.splitVertex1 ============"- test "splitVertex1 x (x :| [] ) == id" $ \x (y :: G) ->- splitVertex1 x (x :| [] ) y == y-- test "splitVertex1 x (y :| [] ) == replaceVertex x y" $ \x y (z :: G) ->- splitVertex1 x (y :| [] ) z == replaceVertex x y z-- test "splitVertex1 1 (0 :| [1]) $ 1 * (2 + 3) == (0 + 1) * (2 + 3)" $- splitVertex1 1 (0 :| [1]) (1 * (2 + 3)) == (0 + 1) * (2 + 3 :: G)-- putStrLn $ "\n============ Graph.NonEmpty.transpose ============"- test "transpose (vertex x) == vertex x" $ \(x :: Int) ->- transpose (vertex x) == vertex x-- test "transpose (edge x y) == edge y x" $ \(x :: Int) y ->- transpose (edge x y) == edge y x-- test "transpose . transpose == id" $ \(x :: G) ->- (transpose . transpose) x == x-- test "transpose (box x y) == box (transpose x) (transpose y)" $ mapSize (min 10) $ \(x :: G) (y :: G) ->- transpose (box x y) == box (transpose x) (transpose y)-- test "edgeList . transpose == sort . map swap . edgeList" $ \(x :: G) ->- (edgeList . transpose) x == (sort . map swap . edgeList) x-- putStrLn $ "\n============ Graph.NonEmpty.induce1 ============"- test "induce1 (const True ) x == Just x" $ \(x :: G) ->- induce1 (const True ) x == Just x-- test "induce1 (const False) x == Nothing" $ \(x :: G) ->- induce1 (const False) x == Nothing-- test "induce1 (/= x) == removeVertex1 x" $ \(x :: Int) y ->- induce1 (/= x) y == removeVertex1 x y-- test "induce1 p >=> induce1 q == induce1 (\\x -> p x && q x)" $ \(apply -> p) (apply -> q) (y :: G) ->- (induce1 p >=> induce1 q) y == induce1 (\x -> p x && q x) y-- putStrLn $ "\n============ Graph.NonEmpty.simplify ============"- test "simplify == id" $ \(x :: G) ->- simplify x == x-- test "size (simplify x) <= size x" $ \(x :: G) ->- size (simplify x) <= size x-- test "simplify 1 === 1" $- simplify 1 === (1 :: G)-- test "simplify (1 + 1) === 1" $- simplify (1 + 1) === (1 :: G)-- test "simplify (1 + 2 + 1) === 1 + 2" $- simplify (1 + 2 + 1) === (1 + 2 :: G)-- test "simplify (1 * 1 * 1) === 1 * 1" $- simplify (1 * 1 * 1) === (1 * 1 :: G)-- putStrLn "\n============ Graph.NonEmpty.box ============"- let unit = fmap $ \(a, ()) -> a- comm = fmap $ \(a, b) -> (b, a)- test "box x y ~~ box y x" $ mapSize (min 10) $ \(x :: G) (y :: G) ->- comm (box x y) == box y x-- test "box x (overlay y z) == overlay (box x y) (box x z)" $ mapSize (min 10) $ \(x :: G) (y :: G) z ->- box x (overlay y z) == overlay (box x y) (box x z)-- test "box x (vertex ()) ~~ x" $ mapSize (min 10) $ \(x :: G) ->- unit(box x (vertex ())) == x-- let assoc = fmap $ \(a, (b, c)) -> ((a, b), c)- test "box x (box y z) ~~ box (box x y) z" $ mapSize (min 5) $ \(x :: G) (y :: G) (z :: G) ->- assoc (box x (box y z)) == box (box x y) z-- test "transpose (box x y) == box (transpose x) (transpose y)" $ mapSize (min 10) $ \(x :: G) (y :: G) ->- transpose (box x y) == box (transpose x) (transpose y)-- test "vertexCount (box x y) == vertexCount x * vertexCount y" $ mapSize (min 10) $ \(x :: G) (y :: G) ->- vertexCount (box x y) == vertexCount x * vertexCount y-- test "edgeCount (box x y) <= vertexCount x * edgeCount y + edgeCount x * vertexCount y" $ mapSize (min 10) $ \(x :: G) (y :: G) ->- edgeCount (box x y) <= vertexCount x * edgeCount y + edgeCount x * vertexCount y-- putStrLn "\n============ Graph.NonEmpty.sparsify ============"- test "sort . reachable x == sort . rights . reachable (Right x) . sparsify" $ \x (y :: G) ->- (sort . reachable x) y == (sort . rights . reachable (Right x) . sparsify) y-- test "vertexCount (sparsify x) <= vertexCount x + size x + 1" $ \(x :: G) ->- vertexCount (sparsify x) <= vertexCount x + size x + 1-- test "edgeCount (sparsify x) <= 3 * size x" $ \(x :: G) ->- edgeCount (sparsify x) <= 3 * size x-- test "size (sparsify x) <= 3 * size x" $ \(x :: G) ->- size (sparsify x) <= 3 * size x
test/Algebra/Graph/Test/Relation.hs view
@@ -30,13 +30,10 @@ type RI = Relation Int -sizeLimit :: Testable prop => prop -> Property-sizeLimit = mapSize (min 10)- testRelation :: IO () testRelation = do putStrLn "\n============ Relation ============"- test "Axioms of graphs" $ sizeLimit (axioms :: GraphTestsuite RI)+ test "Axioms of graphs" $ size10 (axioms :: GraphTestsuite RI) test "Consistency of arbitraryRelation" $ \(m :: RI) -> consistent m@@ -47,70 +44,14 @@ testToGraph t testGraphFamilies t testTransformations t-- putStrLn "\n============ Relation.compose ============"- test "compose empty x == empty" $ \(x :: RI) ->- compose empty x == empty-- test "compose x empty == empty" $ \(x :: RI) ->- compose x empty == empty-- test "compose x (compose y z) == compose (compose x y) z" $ sizeLimit $ \(x :: RI) y z ->- compose x (compose y z) == compose (compose x y) z-- test "compose (edge y z) (edge x y) == edge x z" $ \(x :: Int) y z ->- compose (edge y z) (edge x y) == edge x z-- test "compose (path [1..5]) (path [1..5]) == edges [(1,3),(2,4),(3,5)]" $- compose (path [1..5]) (path [1..5]) == edges [(1,3),(2,4),(3,5::Int)]-- test "compose (circuit [1..5]) (circuit [1..5]) == circuit [1,3,5,2,4]" $- compose (circuit [1..5]) (circuit [1..5]) == circuit [1,3,5,2,4::Int]-- putStrLn "\n============ Relation.reflexiveClosure ============"- test "reflexiveClosure empty == empty" $- reflexiveClosure empty ==(empty :: RI)-- test "reflexiveClosure (vertex x) == edge x x" $ \(x :: Int) ->- reflexiveClosure (vertex x) == edge x x-- putStrLn "\n============ Relation.symmetricClosure ============"-- test "symmetricClosure empty == empty" $- symmetricClosure empty ==(empty :: RI)-- test "symmetricClosure (vertex x) == vertex x" $ \(x :: Int) ->- symmetricClosure (vertex x) == vertex x-- test "symmetricClosure (edge x y) == edges [(x, y), (y, x)]" $ \(x :: Int) y ->- symmetricClosure (edge x y) == edges [(x, y), (y, x)]-- putStrLn "\n============ Relation.transitiveClosure ============"- test "transitiveClosure empty == empty" $- transitiveClosure empty ==(empty :: RI)-- test "transitiveClosure (vertex x) == vertex x" $ \(x :: Int) ->- transitiveClosure (vertex x) == vertex x-- test "transitiveClosure (path $ nub xs) == clique (nub $ xs)" $ \(xs :: [Int]) ->- transitiveClosure (path $ nubOrd xs) == clique (nubOrd xs)-- putStrLn "\n============ Relation.preorderClosure ============"- test "preorderClosure empty == empty" $- preorderClosure empty ==(empty :: RI)-- test "preorderClosure (vertex x) == edge x x" $ \(x :: Int) ->- preorderClosure (vertex x) == edge x x-- test "preorderClosure (path $ nub xs) == reflexiveClosure (clique $ nub xs)" $ \(xs :: [Int]) ->- preorderClosure (path $ nubOrd xs) == reflexiveClosure (clique $ nubOrd xs)+ testRelational t putStrLn "\n============ ReflexiveRelation ============"- test "Axioms of reflexive graphs" $ sizeLimit+ test "Axioms of reflexive graphs" $ size10 (reflexiveAxioms :: GraphTestsuite (ReflexiveRelation Int)) putStrLn "\n============ SymmetricRelation ============"- test "Axioms of undirected graphs" $ sizeLimit+ test "Axioms of undirected graphs" $ size10 (undirectedAxioms :: GraphTestsuite (SymmetricRelation Int)) putStrLn "\n============ SymmetricRelation.neighbours ============"@@ -127,15 +68,15 @@ neighbours y (C.edge x y) == Set.fromList [x] putStrLn "\n============ TransitiveRelation ============"- test "Axioms of transitive graphs" $ sizeLimit+ test "Axioms of transitive graphs" $ size10 (transitiveAxioms :: GraphTestsuite (TransitiveRelation Int)) - test "path xs == (clique xs :: TransitiveRelation Int)" $ sizeLimit $ \xs ->+ test "path xs == (clique xs :: TransitiveRelation Int)" $ size10 $ \xs -> C.path xs == (C.clique xs :: TransitiveRelation Int) putStrLn "\n============ PreorderRelation ============"- test "Axioms of preorder graphs" $ sizeLimit+ test "Axioms of preorder graphs" $ size10 (preorderAxioms :: GraphTestsuite (PreorderRelation Int)) - test "path xs == (clique xs :: PreorderRelation Int)" $ sizeLimit $ \xs ->+ test "path xs == (clique xs :: PreorderRelation Int)" $ size10 $ \xs -> C.path xs == (C.clique xs :: PreorderRelation Int)
+ test/Algebra/Graph/Test/RewriteRules.hs view
@@ -0,0 +1,98 @@+{-# LANGUAGE TemplateHaskell #-}+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.Test.RewriteRules+-- Copyright : (c) Andrey Mokhov 2016-2018+-- License : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability : experimental+--+-- Testsuite for "Algebra.Graph" rewrite rules.+-----------------------------------------------------------------------------+module Algebra.Graph.Test.RewriteRules where++import Data.Maybe (fromMaybe)++import Algebra.Graph hiding ((===))+import Algebra.Graph.Internal++import Test.Inspection++-- Naming convention: we use the suffix "R" to indicate the desired outcome of+-- rewrite rules, and suffices "1", "2", etc. to indicate initial expressions.++-- Testsuite for 'overlays' and 'connects'.+vertices1, verticesR :: [a] -> Graph a+vertices1 = overlays . map vertex+verticesR = fromMaybe Empty . foldr (maybeF Overlay . Vertex) Nothing++inspect $ 'vertices1 === 'verticesR++clique1, cliqueR :: [a] -> Graph a+clique1 = connects . map vertex+cliqueR = fromMaybe Empty . foldr (maybeF Connect . Vertex) Nothing++inspect $ 'clique1 === 'cliqueR++-- Testsuite for 'transpose'.+empty1, emptyR :: Graph a+empty1 = transpose Empty+emptyR = Empty++inspect $ 'empty1 === 'emptyR++vertex1, vertexR :: a -> Graph a+vertex1 = transpose . vertex+vertexR = Vertex++inspect $ 'vertex1 === 'vertexR++overlay1, overlayR :: Graph a -> Graph a -> Graph a+overlay1 x y = transpose (Overlay x y)+overlayR x y = Overlay (transpose x) (transpose y)++inspect $ 'overlay1 === 'overlayR++connect1, connectR :: Graph a -> Graph a -> Graph a+connect1 x y = transpose (Connect x y)+connectR x y = Connect (transpose y) (transpose x)++inspect $ 'connect1 === 'connectR++overlays1, overlaysR :: [Graph a] -> Graph a+overlays1 = transpose . overlays+overlaysR = overlays . map transpose++inspect $ 'overlays1 === 'overlaysR++connects1, connectsR :: [Graph a] -> Graph a+connects1 = transpose . connects+connectsR = fromMaybe Empty . foldr (maybeF (flip Connect) . transpose) Nothing++inspect $ 'connects1 === 'connectsR++vertices2 :: [a] -> Graph a+vertices2 = transpose . overlays . map vertex++inspect $ 'vertices2 === 'vertices1++-- Note that we currently have these three tests:+-- * vertices2 === vertices1+-- * vertices1 === verticesR+-- * vertices2 =/= verticesR+-- This non-transitivity is awkward, and feels like a bug in the inspection+-- testing library. See https://github.com/nomeata/inspection-testing/issues/23.+inspect $ 'vertices2 =/= 'verticesR++cliqueT1, cliqueTR :: [a] -> Graph a+cliqueT1 = transpose . connects . map vertex+cliqueTR = fromMaybe Empty . foldr (maybeF (flip Connect) . Vertex) Nothing++inspect $ 'cliqueT1 === 'cliqueTR++starT1, starTR :: a -> [a] -> Graph a+starT1 x = transpose . star x+starTR a [] = vertex a+starTR a xs = connect (vertices xs) (vertex a)++inspect $ 'starT1 === 'starTR
test/Main.hs view
@@ -1,10 +1,13 @@+import Algebra.Graph.Test.AdjacencyIntMap import Algebra.Graph.Test.AdjacencyMap+import Algebra.Graph.Test.NonEmpty.AdjacencyMap import Algebra.Graph.Test.Export import Algebra.Graph.Test.Fold import Algebra.Graph.Test.Graph-import Algebra.Graph.Test.AdjacencyIntMap+import Algebra.Graph.Test.NonEmpty.Graph import Algebra.Graph.Test.Internal-import Algebra.Graph.Test.NonEmptyGraph+import Algebra.Graph.Test.Labelled.AdjacencyMap+import Algebra.Graph.Test.Labelled.Graph import Algebra.Graph.Test.Relation import Data.Graph.Test.Typed @@ -15,7 +18,10 @@ testExport testFold testGraph- testGraphNonEmpty testInternal+ testLabelledAdjacencyMap+ testLabelledGraph+ testNonEmptyAdjacencyMap+ testNonEmptyGraph testRelation testTyped