algebraic-graphs 0.1.1.1 → 0.2
raw patch · 37 files changed
+3959/−2573 lines, 37 filesdep +mtldep −criteriondep ~base-compatdep ~base-orphansdep ~extraPVP ok
version bump matches the API change (PVP)
Dependencies added: mtl
Dependencies removed: criterion
Dependency ranges changed: base-compat, base-orphans, extra
API changes (from Hackage documentation)
- Algebra.Graph: instance Algebra.Graph.Class.Graph (Algebra.Graph.Graph a)
- Algebra.Graph: instance Algebra.Graph.Class.ToGraph (Algebra.Graph.Graph a)
- Algebra.Graph: instance Algebra.Graph.HigherKinded.Class.Graph Algebra.Graph.Graph
- Algebra.Graph: instance Algebra.Graph.HigherKinded.Class.ToGraph Algebra.Graph.Graph
- Algebra.Graph: starTranspose :: a -> [a] -> Graph a
- Algebra.Graph.AdjacencyMap: fromAdjacencyList :: Ord a => [(a, [a])] -> AdjacencyMap a
- Algebra.Graph.AdjacencyMap: isTopSort :: Ord a => [a] -> AdjacencyMap a -> Bool
- Algebra.Graph.AdjacencyMap: starTranspose :: Ord a => a -> [a] -> AdjacencyMap a
- Algebra.Graph.AdjacencyMap.Internal: GraphKL :: Graph -> (Vertex -> a) -> (a -> Maybe Vertex) -> GraphKL a
- Algebra.Graph.AdjacencyMap.Internal: [fromVertexKL] :: GraphKL a -> Vertex -> a
- Algebra.Graph.AdjacencyMap.Internal: [graphKL] :: AdjacencyMap a -> GraphKL a
- Algebra.Graph.AdjacencyMap.Internal: [toGraphKL] :: GraphKL a -> Graph
- Algebra.Graph.AdjacencyMap.Internal: [toVertexKL] :: GraphKL a -> a -> Maybe Vertex
- Algebra.Graph.AdjacencyMap.Internal: data AdjacencyMap a
- Algebra.Graph.AdjacencyMap.Internal: data GraphKL a
- Algebra.Graph.AdjacencyMap.Internal: instance Algebra.Graph.Class.ToGraph (Algebra.Graph.AdjacencyMap.Internal.AdjacencyMap a)
- Algebra.Graph.AdjacencyMap.Internal: instance GHC.Classes.Ord a => Algebra.Graph.Class.Graph (Algebra.Graph.AdjacencyMap.Internal.AdjacencyMap a)
- Algebra.Graph.AdjacencyMap.Internal: mkAM :: Ord a => Map a (Set a) -> AdjacencyMap a
- Algebra.Graph.AdjacencyMap.Internal: mkGraphKL :: Ord a => Map a (Set a) -> GraphKL a
- Algebra.Graph.Class: class ToGraph t where {
- Algebra.Graph.Class: foldg :: ToGraph t => r -> (ToVertex t -> r) -> (r -> r -> r) -> (r -> r -> r) -> t -> r
- Algebra.Graph.Class: instance Algebra.Graph.Class.Graph (Algebra.Graph.Class.G a)
- Algebra.Graph.Class: toGraph :: (ToGraph t, Graph g, Vertex g ~ ToVertex t) => t -> g
- Algebra.Graph.Export: instance Data.Semigroup.Semigroup (Algebra.Graph.Export.Doc s)
- Algebra.Graph.Fold: bind :: Graph g => Fold a -> (a -> g) -> g
- Algebra.Graph.Fold: box :: (Graph g, Vertex g ~ (a, b)) => Fold a -> Fold b -> g
- Algebra.Graph.Fold: deBruijn :: (Graph g, Vertex g ~ [a]) => Int -> [a] -> g
- Algebra.Graph.Fold: forest :: Graph g => Forest (Vertex g) -> g
- Algebra.Graph.Fold: gmap :: Graph g => (a -> Vertex g) -> Fold a -> g
- Algebra.Graph.Fold: instance Algebra.Graph.Class.Graph (Algebra.Graph.Fold.Fold a)
- Algebra.Graph.Fold: instance Algebra.Graph.Class.ToGraph (Algebra.Graph.Fold.Fold a)
- Algebra.Graph.Fold: instance Algebra.Graph.HigherKinded.Class.Graph Algebra.Graph.Fold.Fold
- Algebra.Graph.Fold: instance Algebra.Graph.HigherKinded.Class.ToGraph Algebra.Graph.Fold.Fold
- Algebra.Graph.Fold: mergeVertices :: Graph g => (Vertex g -> Bool) -> Vertex g -> Fold (Vertex g) -> g
- Algebra.Graph.Fold: mesh :: (Graph g, Vertex g ~ (a, b)) => [a] -> [b] -> g
- Algebra.Graph.Fold: replaceVertex :: (Eq (Vertex g), Graph g) => Vertex g -> Vertex g -> Fold (Vertex g) -> g
- Algebra.Graph.Fold: splitVertex :: (Eq (Vertex g), Graph g) => Vertex g -> [Vertex g] -> Fold (Vertex g) -> g
- Algebra.Graph.Fold: starTranspose :: Graph g => Vertex g -> [Vertex g] -> g
- Algebra.Graph.Fold: torus :: (Graph g, Vertex g ~ (a, b)) => [a] -> [b] -> g
- Algebra.Graph.Fold: tree :: Graph g => Tree (Vertex g) -> g
- Algebra.Graph.HigherKinded.Class: class ToGraph t
- Algebra.Graph.HigherKinded.Class: toGraph :: (ToGraph t, Graph g) => t a -> g a
- Algebra.Graph.IntAdjacencyMap: adjacencyList :: IntAdjacencyMap -> [(Int, [Int])]
- Algebra.Graph.IntAdjacencyMap: adjacencyMap :: IntAdjacencyMap -> (IntMap IntSet)
- Algebra.Graph.IntAdjacencyMap: biclique :: [Int] -> [Int] -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: circuit :: [Int] -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: clique :: [Int] -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: connect :: IntAdjacencyMap -> IntAdjacencyMap -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: connects :: [IntAdjacencyMap] -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: data IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: dfs :: [Int] -> IntAdjacencyMap -> [Int]
- Algebra.Graph.IntAdjacencyMap: dfsForest :: IntAdjacencyMap -> Forest Int
- Algebra.Graph.IntAdjacencyMap: dfsForestFrom :: [Int] -> IntAdjacencyMap -> Forest Int
- Algebra.Graph.IntAdjacencyMap: edge :: Int -> Int -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: edgeCount :: IntAdjacencyMap -> Int
- Algebra.Graph.IntAdjacencyMap: edgeList :: IntAdjacencyMap -> [(Int, Int)]
- Algebra.Graph.IntAdjacencyMap: edgeSet :: IntAdjacencyMap -> Set (Int, Int)
- Algebra.Graph.IntAdjacencyMap: edges :: [(Int, Int)] -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: empty :: IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: forest :: Forest Int -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: fromAdjacencyList :: [(Int, [Int])] -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: gmap :: (Int -> Int) -> IntAdjacencyMap -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: hasEdge :: Int -> Int -> IntAdjacencyMap -> Bool
- Algebra.Graph.IntAdjacencyMap: hasVertex :: Int -> IntAdjacencyMap -> Bool
- Algebra.Graph.IntAdjacencyMap: induce :: (Int -> Bool) -> IntAdjacencyMap -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: isEmpty :: IntAdjacencyMap -> Bool
- Algebra.Graph.IntAdjacencyMap: isSubgraphOf :: IntAdjacencyMap -> IntAdjacencyMap -> Bool
- Algebra.Graph.IntAdjacencyMap: isTopSort :: [Int] -> IntAdjacencyMap -> Bool
- Algebra.Graph.IntAdjacencyMap: mergeVertices :: (Int -> Bool) -> Int -> IntAdjacencyMap -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: overlay :: IntAdjacencyMap -> IntAdjacencyMap -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: overlays :: [IntAdjacencyMap] -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: path :: [Int] -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: postIntSet :: Int -> IntAdjacencyMap -> IntSet
- Algebra.Graph.IntAdjacencyMap: removeEdge :: Int -> Int -> IntAdjacencyMap -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: removeVertex :: Int -> IntAdjacencyMap -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: replaceVertex :: Int -> Int -> IntAdjacencyMap -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: star :: Int -> [Int] -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: starTranspose :: Int -> [Int] -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: topSort :: IntAdjacencyMap -> Maybe [Int]
- Algebra.Graph.IntAdjacencyMap: transpose :: IntAdjacencyMap -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: tree :: Tree Int -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: vertex :: Int -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap: vertexCount :: IntAdjacencyMap -> Int
- Algebra.Graph.IntAdjacencyMap: vertexIntSet :: IntAdjacencyMap -> IntSet
- Algebra.Graph.IntAdjacencyMap: vertexList :: IntAdjacencyMap -> [Int]
- Algebra.Graph.IntAdjacencyMap: vertices :: [Int] -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap.Internal: AM :: !(IntMap IntSet) -> GraphKL -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap.Internal: GraphKL :: Graph -> (Vertex -> Int) -> (Int -> Maybe Vertex) -> GraphKL
- Algebra.Graph.IntAdjacencyMap.Internal: [adjacencyMap] :: IntAdjacencyMap -> !(IntMap IntSet)
- Algebra.Graph.IntAdjacencyMap.Internal: [fromVertexKL] :: GraphKL -> Vertex -> Int
- Algebra.Graph.IntAdjacencyMap.Internal: [graphKL] :: IntAdjacencyMap -> GraphKL
- Algebra.Graph.IntAdjacencyMap.Internal: [toGraphKL] :: GraphKL -> Graph
- Algebra.Graph.IntAdjacencyMap.Internal: [toVertexKL] :: GraphKL -> Int -> Maybe Vertex
- Algebra.Graph.IntAdjacencyMap.Internal: consistent :: IntAdjacencyMap -> Bool
- Algebra.Graph.IntAdjacencyMap.Internal: data GraphKL
- Algebra.Graph.IntAdjacencyMap.Internal: data IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap.Internal: instance Algebra.Graph.Class.Graph Algebra.Graph.IntAdjacencyMap.Internal.IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap.Internal: instance Algebra.Graph.Class.ToGraph Algebra.Graph.IntAdjacencyMap.Internal.IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap.Internal: instance GHC.Classes.Eq Algebra.Graph.IntAdjacencyMap.Internal.IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap.Internal: instance GHC.Num.Num Algebra.Graph.IntAdjacencyMap.Internal.IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap.Internal: instance GHC.Show.Show Algebra.Graph.IntAdjacencyMap.Internal.IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap.Internal: mkAM :: IntMap IntSet -> IntAdjacencyMap
- Algebra.Graph.IntAdjacencyMap.Internal: mkGraphKL :: IntMap IntSet -> GraphKL
- Algebra.Graph.Internal: Context :: [a] -> [a] -> Context a
- Algebra.Graph.Internal: [inputs] :: Context a -> [a]
- Algebra.Graph.Internal: [outputs] :: Context a -> [a]
- Algebra.Graph.Internal: context :: ToGraph g => (ToVertex g -> Bool) -> g -> Maybe (Context (ToVertex g))
- Algebra.Graph.Internal: data Context a
- Algebra.Graph.Internal: focus :: ToGraph g => (ToVertex g -> Bool) -> g -> Focus (ToVertex g)
- Algebra.Graph.Internal: instance Data.Semigroup.Semigroup (Algebra.Graph.Internal.List a)
- Algebra.Graph.NonEmpty: instance Algebra.Graph.Class.ToGraph (Algebra.Graph.NonEmpty.NonEmptyGraph a)
- Algebra.Graph.NonEmpty: instance Algebra.Graph.HigherKinded.Class.ToGraph Algebra.Graph.NonEmpty.NonEmptyGraph
- Algebra.Graph.NonEmpty: starTranspose :: a -> [a] -> NonEmptyGraph a
- Algebra.Graph.Relation: fromAdjacencyList :: Ord a => [(a, [a])] -> Relation a
- Algebra.Graph.Relation: starTranspose :: Ord a => a -> [a] -> Relation a
- Algebra.Graph.Relation.Internal: instance Algebra.Graph.Class.ToGraph (Algebra.Graph.Relation.Internal.Relation a)
- Algebra.Graph.Relation.Internal: instance GHC.Classes.Ord a => Algebra.Graph.Class.Graph (Algebra.Graph.Relation.Internal.Relation a)
- Algebra.Graph.Relation.InternalDerived: instance (GHC.Num.Num a, GHC.Classes.Ord a) => GHC.Num.Num (Algebra.Graph.Relation.InternalDerived.PreorderRelation a)
- Algebra.Graph.Relation.InternalDerived: instance (GHC.Num.Num a, GHC.Classes.Ord a) => GHC.Num.Num (Algebra.Graph.Relation.InternalDerived.ReflexiveRelation a)
- Algebra.Graph.Relation.InternalDerived: instance (GHC.Num.Num a, GHC.Classes.Ord a) => GHC.Num.Num (Algebra.Graph.Relation.InternalDerived.SymmetricRelation a)
- Algebra.Graph.Relation.InternalDerived: instance (GHC.Num.Num a, GHC.Classes.Ord a) => GHC.Num.Num (Algebra.Graph.Relation.InternalDerived.TransitiveRelation a)
+ Algebra.Graph: Context :: [a] -> [a] -> Context a
+ Algebra.Graph: [inputs] :: Context a -> [a]
+ Algebra.Graph: [outputs] :: Context a -> [a]
+ Algebra.Graph: adjacencyIntMap :: Graph Int -> IntMap IntSet
+ Algebra.Graph: adjacencyList :: Ord a => Graph a -> [(a, [a])]
+ Algebra.Graph: adjacencyMap :: Ord a => Graph a -> Map a (Set a)
+ Algebra.Graph: context :: (a -> Bool) -> Graph a -> Maybe (Context a)
+ Algebra.Graph: data Context a
+ Algebra.Graph: sparsify :: Graph a -> Graph (Either Int a)
+ Algebra.Graph: stars :: [(a, [a])] -> Graph a
+ Algebra.Graph.AdjacencyIntMap: adjacencyIntMap :: AdjacencyIntMap -> IntMap IntSet
+ Algebra.Graph.AdjacencyIntMap: adjacencyList :: AdjacencyIntMap -> [(Int, [Int])]
+ Algebra.Graph.AdjacencyIntMap: biclique :: [Int] -> [Int] -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: circuit :: [Int] -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: clique :: [Int] -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: connect :: AdjacencyIntMap -> AdjacencyIntMap -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: connects :: [AdjacencyIntMap] -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: data AdjacencyIntMap
+ 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: edge :: Int -> Int -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: edgeCount :: AdjacencyIntMap -> Int
+ Algebra.Graph.AdjacencyIntMap: edgeList :: AdjacencyIntMap -> [(Int, Int)]
+ Algebra.Graph.AdjacencyIntMap: edgeSet :: AdjacencyIntMap -> Set (Int, Int)
+ Algebra.Graph.AdjacencyIntMap: edges :: [(Int, Int)] -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: empty :: AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: forest :: Forest Int -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: gmap :: (Int -> Int) -> AdjacencyIntMap -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: hasEdge :: Int -> Int -> AdjacencyIntMap -> Bool
+ Algebra.Graph.AdjacencyIntMap: hasVertex :: Int -> AdjacencyIntMap -> Bool
+ Algebra.Graph.AdjacencyIntMap: induce :: (Int -> Bool) -> AdjacencyIntMap -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: isAcyclic :: AdjacencyIntMap -> Bool
+ Algebra.Graph.AdjacencyIntMap: isDfsForestOf :: Forest Int -> AdjacencyIntMap -> Bool
+ Algebra.Graph.AdjacencyIntMap: isEmpty :: AdjacencyIntMap -> Bool
+ Algebra.Graph.AdjacencyIntMap: isSubgraphOf :: AdjacencyIntMap -> AdjacencyIntMap -> Bool
+ Algebra.Graph.AdjacencyIntMap: isTopSortOf :: [Int] -> AdjacencyIntMap -> Bool
+ Algebra.Graph.AdjacencyIntMap: mergeVertices :: (Int -> Bool) -> Int -> AdjacencyIntMap -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: overlay :: AdjacencyIntMap -> AdjacencyIntMap -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: overlays :: [AdjacencyIntMap] -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: path :: [Int] -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: postIntSet :: Int -> AdjacencyIntMap -> IntSet
+ Algebra.Graph.AdjacencyIntMap: preIntSet :: Int -> AdjacencyIntMap -> IntSet
+ Algebra.Graph.AdjacencyIntMap: reachable :: Int -> AdjacencyIntMap -> [Int]
+ Algebra.Graph.AdjacencyIntMap: removeEdge :: Int -> Int -> AdjacencyIntMap -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: removeVertex :: Int -> AdjacencyIntMap -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: replaceVertex :: Int -> Int -> AdjacencyIntMap -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: star :: Int -> [Int] -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: stars :: [(Int, [Int])] -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: topSort :: AdjacencyIntMap -> Maybe [Int]
+ Algebra.Graph.AdjacencyIntMap: transpose :: AdjacencyIntMap -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: tree :: Tree Int -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: vertex :: Int -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap: vertexCount :: AdjacencyIntMap -> Int
+ Algebra.Graph.AdjacencyIntMap: vertexIntSet :: AdjacencyIntMap -> IntSet
+ Algebra.Graph.AdjacencyIntMap: vertexList :: AdjacencyIntMap -> [Int]
+ Algebra.Graph.AdjacencyIntMap: vertices :: [Int] -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap.Internal: AM :: IntMap IntSet -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap.Internal: [adjacencyIntMap] :: AdjacencyIntMap -> IntMap IntSet
+ Algebra.Graph.AdjacencyIntMap.Internal: connect :: AdjacencyIntMap -> AdjacencyIntMap -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap.Internal: consistent :: AdjacencyIntMap -> Bool
+ Algebra.Graph.AdjacencyIntMap.Internal: empty :: AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap.Internal: fromAdjacencyIntSets :: [(Int, IntSet)] -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap.Internal: instance Control.DeepSeq.NFData Algebra.Graph.AdjacencyIntMap.Internal.AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap.Internal: instance GHC.Classes.Eq Algebra.Graph.AdjacencyIntMap.Internal.AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap.Internal: instance GHC.Num.Num Algebra.Graph.AdjacencyIntMap.Internal.AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap.Internal: instance GHC.Show.Show Algebra.Graph.AdjacencyIntMap.Internal.AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap.Internal: newtype AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap.Internal: overlay :: AdjacencyIntMap -> AdjacencyIntMap -> AdjacencyIntMap
+ Algebra.Graph.AdjacencyIntMap.Internal: vertex :: Int -> AdjacencyIntMap
+ 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: preSet :: Ord a => a -> AdjacencyMap a -> Set a
+ Algebra.Graph.AdjacencyMap: reachable :: Ord a => a -> AdjacencyMap a -> [a]
+ Algebra.Graph.AdjacencyMap: stars :: Ord a => [(a, [a])] -> AdjacencyMap 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: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.AdjacencyMap.Internal.AdjacencyMap a)
+ Algebra.Graph.AdjacencyMap.Internal: newtype 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 (Algebra.Graph.Fold.Fold a)
+ Algebra.Graph.Class: instance Algebra.Graph.Class.Graph (Algebra.Graph.Graph a)
+ Algebra.Graph.Class: instance Algebra.Graph.Class.Graph Algebra.Graph.AdjacencyIntMap.Internal.AdjacencyIntMap
+ Algebra.Graph.Class: instance GHC.Classes.Ord a => Algebra.Graph.Class.Graph (Algebra.Graph.AdjacencyMap.Internal.AdjacencyMap a)
+ Algebra.Graph.Class: instance GHC.Classes.Ord a => Algebra.Graph.Class.Graph (Algebra.Graph.Relation.Internal.Relation a)
+ Algebra.Graph.Export: instance GHC.Base.Semigroup (Algebra.Graph.Export.Doc s)
+ Algebra.Graph.Fold: adjacencyList :: Ord a => Fold a -> [(a, [a])]
+ Algebra.Graph.Fold: instance Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.Fold.Fold a)
+ Algebra.Graph.Fold: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Fold.Fold a)
+ Algebra.Graph.Fold: stars :: [(a, [a])] -> Fold a
+ Algebra.Graph.HigherKinded.Class: instance Algebra.Graph.HigherKinded.Class.Graph Algebra.Graph.Fold.Fold
+ Algebra.Graph.HigherKinded.Class: instance Algebra.Graph.HigherKinded.Class.Graph Algebra.Graph.Graph
+ Algebra.Graph.Internal: Edge :: Hit
+ Algebra.Graph.Internal: Focus :: Bool -> List a -> List a -> List a -> Focus a
+ Algebra.Graph.Internal: Miss :: Hit
+ Algebra.Graph.Internal: Tail :: Hit
+ Algebra.Graph.Internal: [is] :: Focus a -> List a
+ Algebra.Graph.Internal: [ok] :: Focus a -> Bool
+ Algebra.Graph.Internal: [os] :: Focus a -> List a
+ Algebra.Graph.Internal: [vs] :: Focus a -> List a
+ Algebra.Graph.Internal: connectFoci :: Focus a -> Focus a -> Focus a
+ Algebra.Graph.Internal: data Hit
+ Algebra.Graph.Internal: emptyFocus :: Focus a
+ Algebra.Graph.Internal: foldr1Safe :: (a -> a -> a) -> [a] -> Maybe a
+ Algebra.Graph.Internal: instance GHC.Base.Semigroup (Algebra.Graph.Internal.List a)
+ Algebra.Graph.Internal: instance GHC.Classes.Eq Algebra.Graph.Internal.Hit
+ Algebra.Graph.Internal: instance GHC.Classes.Ord Algebra.Graph.Internal.Hit
+ Algebra.Graph.Internal: overlayFoci :: Focus a -> Focus a -> Focus a
+ Algebra.Graph.Internal: vertexFocus :: (a -> Bool) -> a -> Focus 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 => Dioid a
+ Algebra.Graph.Label: class Semilattice a
+ Algebra.Graph.Label: data Distance 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 (GHC.Num.Num a, GHC.Classes.Ord a) => Algebra.Graph.Label.Dioid (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.Eq a => GHC.Classes.Eq (Algebra.Graph.Label.Distance a)
+ 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.Label: instance GHC.Classes.Ord a => GHC.Classes.Ord (Algebra.Graph.Label.Distance a)
+ Algebra.Graph.Label: instance GHC.Show.Show a => GHC.Show.Show (Algebra.Graph.Label.Distance a)
+ Algebra.Graph.Label: one :: Dioid a => a
+ Algebra.Graph.Label: zero :: Semilattice a => a
+ Algebra.Graph.Labelled: (-<) :: Graph e a -> e -> (Graph e a, e)
+ Algebra.Graph.Labelled: (>-) :: (Graph e a, e) -> Graph e a -> Graph e a
+ Algebra.Graph.Labelled: Connect :: e -> (Graph e a) -> (Graph e a) -> Graph e a
+ Algebra.Graph.Labelled: Empty :: Graph e a
+ Algebra.Graph.Labelled: Vertex :: a -> Graph e a
+ Algebra.Graph.Labelled: connect :: Dioid e => Graph e a -> Graph e a -> Graph e a
+ Algebra.Graph.Labelled: connectBy :: e -> Graph e a -> Graph e a -> Graph e a
+ Algebra.Graph.Labelled: data Graph e a
+ Algebra.Graph.Labelled: edge :: Dioid e => a -> a -> Graph e a
+ Algebra.Graph.Labelled: edgeLabel :: (Eq a, Semilattice e) => a -> a -> Graph e a -> e
+ Algebra.Graph.Labelled: empty :: Graph e a
+ Algebra.Graph.Labelled: infixl 5 >-
+ Algebra.Graph.Labelled: instance (GHC.Show.Show a, GHC.Show.Show e) => GHC.Show.Show (Algebra.Graph.Labelled.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.Labelled: instance GHC.Base.Functor (Algebra.Graph.Labelled.Graph e)
+ Algebra.Graph.Labelled: overlay :: Semilattice e => Graph e a -> Graph e a -> Graph e a
+ Algebra.Graph.Labelled: type UnlabelledGraph a = Graph Bool a
+ Algebra.Graph.Labelled: vertex :: a -> Graph e a
+ Algebra.Graph.NonEmpty: instance Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.NonEmpty.NonEmptyGraph a)
+ Algebra.Graph.NonEmpty: sparsify :: NonEmptyGraph a -> NonEmptyGraph (Either Int a)
+ Algebra.Graph.NonEmpty: stars1 :: NonEmpty (a, [a]) -> NonEmptyGraph a
+ Algebra.Graph.Relation: adjacencyList :: Eq a => Relation a -> [(a, [a])]
+ Algebra.Graph.Relation: stars :: Ord a => [(a, [a])] -> Relation a
+ Algebra.Graph.Relation.Internal: connect :: Ord a => Relation a -> Relation a -> Relation a
+ Algebra.Graph.Relation.Internal: empty :: Relation a
+ Algebra.Graph.Relation.Internal: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Relation.Internal.Relation a)
+ Algebra.Graph.Relation.Internal: overlay :: Ord a => Relation a -> Relation a -> Relation a
+ Algebra.Graph.Relation.Internal: vertex :: a -> Relation a
+ Algebra.Graph.Relation.InternalDerived: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.Relation.InternalDerived.PreorderRelation a)
+ Algebra.Graph.Relation.InternalDerived: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.Relation.InternalDerived.ReflexiveRelation a)
+ Algebra.Graph.Relation.InternalDerived: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.Relation.InternalDerived.SymmetricRelation a)
+ Algebra.Graph.Relation.InternalDerived: instance (GHC.Classes.Ord a, GHC.Num.Num a) => GHC.Num.Num (Algebra.Graph.Relation.InternalDerived.TransitiveRelation a)
+ Algebra.Graph.Relation.InternalDerived: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Relation.InternalDerived.PreorderRelation a)
+ Algebra.Graph.Relation.InternalDerived: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Relation.InternalDerived.ReflexiveRelation a)
+ Algebra.Graph.Relation.InternalDerived: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Relation.InternalDerived.SymmetricRelation a)
+ Algebra.Graph.Relation.InternalDerived: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Algebra.Graph.Relation.InternalDerived.TransitiveRelation a)
+ Algebra.Graph.ToGraph: adjacencyIntMap :: (ToGraph t, ToVertex t ~ Int) => t -> IntMap IntSet
+ Algebra.Graph.ToGraph: adjacencyIntMapTranspose :: (ToGraph t, ToVertex t ~ Int) => t -> IntMap IntSet
+ Algebra.Graph.ToGraph: adjacencyList :: (ToGraph t, Ord (ToVertex t)) => t -> [(ToVertex t, [ToVertex t])]
+ Algebra.Graph.ToGraph: adjacencyMap :: (ToGraph t, Ord (ToVertex t)) => t -> Map (ToVertex t) (Set (ToVertex t))
+ Algebra.Graph.ToGraph: adjacencyMapTranspose :: (ToGraph t, Ord (ToVertex t)) => t -> Map (ToVertex t) (Set (ToVertex t))
+ Algebra.Graph.ToGraph: class ToGraph t where {
+ Algebra.Graph.ToGraph: dfs :: (ToGraph t, Ord (ToVertex t)) => [ToVertex t] -> t -> [ToVertex t]
+ Algebra.Graph.ToGraph: dfsForest :: (ToGraph t, Ord (ToVertex t)) => t -> Forest (ToVertex t)
+ Algebra.Graph.ToGraph: dfsForestFrom :: (ToGraph t, Ord (ToVertex t)) => [ToVertex t] -> t -> Forest (ToVertex t)
+ Algebra.Graph.ToGraph: edgeCount :: (ToGraph t, Ord (ToVertex t)) => t -> Int
+ Algebra.Graph.ToGraph: edgeList :: (ToGraph t, Ord (ToVertex t)) => t -> [(ToVertex t, ToVertex t)]
+ Algebra.Graph.ToGraph: edgeSet :: (ToGraph t, Ord (ToVertex t)) => t -> Set (ToVertex t, ToVertex t)
+ Algebra.Graph.ToGraph: foldg :: ToGraph t => r -> (ToVertex t -> r) -> (r -> r -> r) -> (r -> r -> r) -> t -> r
+ Algebra.Graph.ToGraph: hasEdge :: (ToGraph t, Eq (ToVertex t)) => ToVertex t -> ToVertex t -> t -> Bool
+ Algebra.Graph.ToGraph: hasVertex :: (ToGraph t, Eq (ToVertex t)) => ToVertex t -> t -> Bool
+ Algebra.Graph.ToGraph: instance Algebra.Graph.ToGraph.ToGraph Algebra.Graph.AdjacencyIntMap.Internal.AdjacencyIntMap
+ Algebra.Graph.ToGraph: instance GHC.Classes.Ord a => Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.AdjacencyMap.Internal.AdjacencyMap a)
+ Algebra.Graph.ToGraph: instance GHC.Classes.Ord a => Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.Graph a)
+ Algebra.Graph.ToGraph: instance GHC.Classes.Ord a => Algebra.Graph.ToGraph.ToGraph (Algebra.Graph.Relation.Internal.Relation a)
+ Algebra.Graph.ToGraph: isAcyclic :: (ToGraph t, Ord (ToVertex t)) => t -> Bool
+ Algebra.Graph.ToGraph: isDfsForestOf :: (ToGraph t, Ord (ToVertex t)) => Forest (ToVertex t) -> t -> Bool
+ Algebra.Graph.ToGraph: isEmpty :: ToGraph t => t -> Bool
+ Algebra.Graph.ToGraph: isTopSortOf :: (ToGraph t, Ord (ToVertex t)) => [ToVertex t] -> t -> Bool
+ Algebra.Graph.ToGraph: postIntSet :: (ToGraph t, ToVertex t ~ Int) => Int -> t -> IntSet
+ Algebra.Graph.ToGraph: postSet :: (ToGraph t, Ord (ToVertex t)) => ToVertex t -> t -> Set (ToVertex t)
+ Algebra.Graph.ToGraph: preIntSet :: (ToGraph t, ToVertex t ~ Int) => Int -> t -> IntSet
+ Algebra.Graph.ToGraph: preSet :: (ToGraph t, Ord (ToVertex t)) => ToVertex t -> t -> Set (ToVertex t)
+ Algebra.Graph.ToGraph: reachable :: (ToGraph t, Ord (ToVertex t)) => ToVertex t -> t -> [ToVertex t]
+ Algebra.Graph.ToGraph: size :: ToGraph t => t -> Int
+ Algebra.Graph.ToGraph: toAdjacencyIntMap :: (ToGraph t, ToVertex t ~ Int) => t -> AdjacencyIntMap
+ Algebra.Graph.ToGraph: toAdjacencyIntMapTranspose :: (ToGraph t, ToVertex t ~ Int) => t -> AdjacencyIntMap
+ Algebra.Graph.ToGraph: toAdjacencyMap :: (ToGraph t, Ord (ToVertex t)) => t -> AdjacencyMap (ToVertex t)
+ Algebra.Graph.ToGraph: toAdjacencyMapTranspose :: (ToGraph t, Ord (ToVertex t)) => t -> AdjacencyMap (ToVertex t)
+ Algebra.Graph.ToGraph: toGraph :: ToGraph t => t -> Graph (ToVertex t)
+ Algebra.Graph.ToGraph: topSort :: (ToGraph t, Ord (ToVertex t)) => t -> Maybe [ToVertex t]
+ Algebra.Graph.ToGraph: type family ToVertex t;
+ Algebra.Graph.ToGraph: vertexCount :: (ToGraph t, Ord (ToVertex t)) => t -> Int
+ Algebra.Graph.ToGraph: vertexIntSet :: (ToGraph t, ToVertex t ~ Int) => t -> IntSet
+ Algebra.Graph.ToGraph: vertexList :: (ToGraph t, Ord (ToVertex t)) => t -> [ToVertex t]
+ Algebra.Graph.ToGraph: vertexSet :: (ToGraph t, Ord (ToVertex t)) => t -> Set (ToVertex t)
+ Algebra.Graph.ToGraph: }
+ Data.Graph.Typed: GraphKL :: Graph -> Vertex -> a -> a -> Maybe Vertex -> GraphKL a
+ Data.Graph.Typed: [fromVertexKL] :: GraphKL a -> Vertex -> a
+ Data.Graph.Typed: [toGraphKL] :: GraphKL a -> Graph
+ Data.Graph.Typed: [toVertexKL] :: GraphKL a -> a -> Maybe Vertex
+ Data.Graph.Typed: data GraphKL a
+ Data.Graph.Typed: dfs :: [a] -> GraphKL a -> [a]
+ Data.Graph.Typed: dfsForest :: GraphKL a -> Forest a
+ Data.Graph.Typed: dfsForestFrom :: [a] -> GraphKL a -> Forest a
+ Data.Graph.Typed: fromAdjacencyIntMap :: AdjacencyIntMap -> GraphKL Int
+ Data.Graph.Typed: fromAdjacencyMap :: Ord a => AdjacencyMap a -> GraphKL a
+ Data.Graph.Typed: topSort :: GraphKL a -> [a]
- Algebra.Graph: hasEdge :: Ord a => a -> a -> Graph a -> Bool
+ Algebra.Graph: hasEdge :: Eq a => a -> a -> Graph a -> Bool
- Algebra.Graph.AdjacencyMap: adjacencyMap :: AdjacencyMap a -> (Map a (Set a))
+ Algebra.Graph.AdjacencyMap: adjacencyMap :: AdjacencyMap a -> Map a (Set a)
- Algebra.Graph.AdjacencyMap: dfs :: [a] -> AdjacencyMap a -> [a]
+ Algebra.Graph.AdjacencyMap: dfs :: Ord a => [a] -> AdjacencyMap a -> [a]
- Algebra.Graph.AdjacencyMap: dfsForest :: AdjacencyMap a -> Forest a
+ Algebra.Graph.AdjacencyMap: dfsForest :: Ord a => AdjacencyMap a -> Forest a
- Algebra.Graph.AdjacencyMap: dfsForestFrom :: [a] -> AdjacencyMap a -> Forest a
+ Algebra.Graph.AdjacencyMap: dfsForestFrom :: Ord a => [a] -> AdjacencyMap a -> Forest a
- Algebra.Graph.AdjacencyMap: empty :: Ord a => AdjacencyMap a
+ Algebra.Graph.AdjacencyMap: empty :: AdjacencyMap a
- Algebra.Graph.AdjacencyMap: induce :: Ord a => (a -> Bool) -> AdjacencyMap a -> AdjacencyMap a
+ Algebra.Graph.AdjacencyMap: induce :: (a -> Bool) -> AdjacencyMap a -> AdjacencyMap a
- Algebra.Graph.AdjacencyMap: vertex :: Ord a => a -> AdjacencyMap a
+ Algebra.Graph.AdjacencyMap: vertex :: a -> AdjacencyMap a
- Algebra.Graph.AdjacencyMap.Internal: AM :: !(Map a (Set a)) -> GraphKL a -> AdjacencyMap a
+ Algebra.Graph.AdjacencyMap.Internal: AM :: Map a (Set a) -> AdjacencyMap a
- Algebra.Graph.AdjacencyMap.Internal: [adjacencyMap] :: AdjacencyMap a -> !(Map a (Set a))
+ Algebra.Graph.AdjacencyMap.Internal: [adjacencyMap] :: AdjacencyMap a -> Map a (Set a)
- Algebra.Graph.Class: type family ToVertex t;
+ Algebra.Graph.Class: type family Vertex g;
- 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.Fold: biclique :: Graph g => [Vertex g] -> [Vertex g] -> g
+ Algebra.Graph.Fold: biclique :: [a] -> [a] -> Fold a
- Algebra.Graph.Fold: circuit :: Graph g => [Vertex g] -> g
+ Algebra.Graph.Fold: circuit :: [a] -> Fold a
- Algebra.Graph.Fold: clique :: Graph g => [Vertex g] -> g
+ Algebra.Graph.Fold: clique :: [a] -> Fold a
- Algebra.Graph.Fold: connect :: Graph g => g -> g -> g
+ Algebra.Graph.Fold: connect :: Fold a -> Fold a -> Fold a
- Algebra.Graph.Fold: connects :: Graph g => [g] -> g
+ Algebra.Graph.Fold: connects :: [Fold a] -> Fold a
- Algebra.Graph.Fold: edge :: Graph g => Vertex g -> Vertex g -> g
+ Algebra.Graph.Fold: edge :: a -> a -> Fold a
- Algebra.Graph.Fold: edges :: Graph g => [(Vertex g, Vertex g)] -> g
+ Algebra.Graph.Fold: edges :: [(a, a)] -> Fold a
- Algebra.Graph.Fold: empty :: Graph g => g
+ Algebra.Graph.Fold: empty :: Fold a
- Algebra.Graph.Fold: hasEdge :: Ord a => a -> a -> Fold a -> Bool
+ Algebra.Graph.Fold: hasEdge :: Eq a => a -> a -> Fold a -> Bool
- Algebra.Graph.Fold: induce :: Graph g => (Vertex g -> Bool) -> Fold (Vertex g) -> g
+ Algebra.Graph.Fold: induce :: (a -> Bool) -> Fold a -> Fold a
- Algebra.Graph.Fold: isSubgraphOf :: (Graph g, Eq g) => g -> g -> Bool
+ Algebra.Graph.Fold: isSubgraphOf :: Ord a => Fold a -> Fold a -> Bool
- Algebra.Graph.Fold: overlay :: Graph g => g -> g -> g
+ Algebra.Graph.Fold: overlay :: Fold a -> Fold a -> Fold a
- Algebra.Graph.Fold: overlays :: Graph g => [g] -> g
+ Algebra.Graph.Fold: overlays :: [Fold a] -> Fold a
- Algebra.Graph.Fold: path :: Graph g => [Vertex g] -> g
+ Algebra.Graph.Fold: path :: [a] -> Fold a
- Algebra.Graph.Fold: removeEdge :: (Eq (Vertex g), Graph g) => Vertex g -> Vertex g -> Fold (Vertex g) -> g
+ Algebra.Graph.Fold: removeEdge :: Eq a => a -> a -> Fold a -> Fold a
- Algebra.Graph.Fold: removeVertex :: (Eq (Vertex g), Graph g) => Vertex g -> Fold (Vertex g) -> g
+ Algebra.Graph.Fold: removeVertex :: Eq a => a -> Fold a -> Fold a
- Algebra.Graph.Fold: simplify :: (Eq g, Graph g) => Fold (Vertex g) -> g
+ Algebra.Graph.Fold: simplify :: Ord a => Fold a -> Fold a
- Algebra.Graph.Fold: star :: Graph g => Vertex g -> [Vertex g] -> g
+ Algebra.Graph.Fold: star :: a -> [a] -> Fold a
- Algebra.Graph.Fold: transpose :: Graph g => Fold (Vertex g) -> g
+ Algebra.Graph.Fold: transpose :: Fold a -> Fold a
- Algebra.Graph.Fold: vertex :: Graph g => Vertex g -> g
+ Algebra.Graph.Fold: vertex :: a -> Fold a
- Algebra.Graph.Fold: vertices :: Graph g => [Vertex g] -> g
+ Algebra.Graph.Fold: vertices :: [a] -> Fold a
- Algebra.Graph.HigherKinded.Class: empty :: Alternative f => forall a. () => f a
+ Algebra.Graph.HigherKinded.Class: empty :: Alternative f => f a
- Algebra.Graph.NonEmpty: hasEdge :: Ord a => a -> a -> NonEmptyGraph a -> Bool
+ Algebra.Graph.NonEmpty: hasEdge :: Eq a => a -> a -> NonEmptyGraph a -> Bool
- Algebra.Graph.Relation: empty :: Ord a => Relation a
+ Algebra.Graph.Relation: empty :: Relation a
- Algebra.Graph.Relation: vertex :: Ord a => a -> Relation a
+ Algebra.Graph.Relation: vertex :: a -> Relation a
Files
- CHANGES.md +24/−1
- README.md +6/−3
- algebraic-graphs.cabal +22/−36
- bench/Bench.hs +0/−200
- src/Algebra/Graph.hs +283/−83
- src/Algebra/Graph/AdjacencyIntMap.hs +692/−0
- src/Algebra/Graph/AdjacencyIntMap/Internal.hs +232/−0
- src/Algebra/Graph/AdjacencyMap.hs +253/−192
- src/Algebra/Graph/AdjacencyMap/Internal.hs +106/−70
- src/Algebra/Graph/Class.hs +43/−47
- src/Algebra/Graph/Export.hs +6/−6
- src/Algebra/Graph/Export/Dot.hs +2/−2
- src/Algebra/Graph/Fold.hs +197/−221
- src/Algebra/Graph/HigherKinded/Class.hs +15/−21
- src/Algebra/Graph/IntAdjacencyMap.hs +0/−646
- src/Algebra/Graph/IntAdjacencyMap/Internal.hs +0/−196
- src/Algebra/Graph/Internal.hs +22/−25
- src/Algebra/Graph/Label.hs +126/−0
- src/Algebra/Graph/Labelled.hs +122/−0
- src/Algebra/Graph/NonEmpty.hs +186/−50
- src/Algebra/Graph/Relation.hs +63/−106
- src/Algebra/Graph/Relation/Internal.hs +76/−22
- src/Algebra/Graph/Relation/InternalDerived.hs +7/−4
- src/Algebra/Graph/ToGraph.hs +452/−0
- src/Data/Graph/Typed.hs +160/−0
- test/Algebra/Graph/Test/API.hs +130/−246
- test/Algebra/Graph/Test/AdjacencyIntMap.hs +46/−0
- test/Algebra/Graph/Test/AdjacencyMap.hs +6/−18
- test/Algebra/Graph/Test/Arbitrary.hs +10/−10
- test/Algebra/Graph/Test/Fold.hs +5/−98
- test/Algebra/Graph/Test/Generic.hs +404/−153
- test/Algebra/Graph/Test/Graph.hs +43/−25
- test/Algebra/Graph/Test/IntAdjacencyMap.hs +0/−60
- test/Algebra/Graph/Test/NonEmptyGraph.hs +47/−17
- test/Algebra/Graph/Test/Relation.hs +6/−13
- test/Data/Graph/Test/Typed.hs +163/−0
- test/Main.hs +4/−2
CHANGES.md view
@@ -1,8 +1,31 @@ # Change log +## 0.2 + +* #117: Add `sparsify`. +* #115: Add `isDfsForestOf`. +* #114: Add a basic implementation of edge-labelled graphs. +* #107: Drop `starTranspose`. +* #106: Extend `ToGraph` with algorithms based on adjacency maps. +* #106: Add `isAcyclic` and `reachable`. +* #106: Rename `isTopSort` to `isTopSortOf`. +* #102: Switch the master branch to GHC 8.4.3. Add a CI instance for GHC 8.6.1. +* #101: Drop `-O2` from the `ghc-options` section of the Cabal file. +* #100: Rename `fromAdjacencyList` to `stars`. +* #79: Improve the API consistency: rename `IntAdjacencyMap` to `AdjacencyIntMap`, + and then rename the function that extracts its adjacency map to + `adjacencyIntMap` to avoid the clash with `AdjacencyMap.adjacencyMap`, + which has incompatible type. +* #82, #92: Add performance regression suite. +* #76: Remove benchmarks. +* #74: Drop dependency of `Algebra.Graph` on graph type classes. +* #62: Move King-Launchbury graphs into `Data.Graph.Typed`. +* #67, #68, #69, #77, #81, #93, #94, #97, #103, #110: Various performance improvements. +* #66, #72, #96, #98: Add missing `NFData` instances. + ## 0.1.1.1 -* #59: Allow base-compat-0.10. +* #59: Allow `base-compat-0.10`. ## 0.1.1
README.md view
@@ -3,8 +3,11 @@ [](https://hackage.haskell.org/package/algebraic-graphs) [](https://travis-ci.org/snowleopard/alga) [](https://ci.appveyor.com/project/snowleopard/alga) **Alga** is a library for algebraic construction and manipulation of graphs in Haskell. See-[this paper](https://github.com/snowleopard/alga-paper) for the motivation behind the library, the underlying-theory and implementation details.+[this Haskell Symposium paper](https://github.com/snowleopard/alga-paper) and the+corresponding [talk](https://www.youtube.com/watch?v=EdQGLewU-8k) for the motivation+behind the library, the underlying theory and implementation details. There is also a+[Haskell eXchange talk](https://skillsmatter.com/skillscasts/10635-algebraic-graphs), +and a [tutorial](https://nobrakal.github.io/alga-tutorial) by Alexandre Moine. ## Main idea @@ -57,7 +60,7 @@ enough for many applications. We believe there is a lot of potential for improving the performance of the library, and this is one of our top priorities. If you come across a performance issue when using the library, please let us know. -Some preliminary benchmarks can be found in [doc/benchmarks](https://github.com/snowleopard/alga/blob/master/doc/benchmarks.md).+Some preliminary benchmarks can be found [here](https://github.com/haskell-perf/graphs). ## Blog posts
algebraic-graphs.cabal view
@@ -1,10 +1,11 @@ name: algebraic-graphs-version: 0.1.1.1+version: 0.2 synopsis: A library for algebraic graph construction and transformation license: MIT license-file: LICENSE author: Andrey Mokhov <andrey.mokhov@gmail.com>, github: @snowleopard-maintainer: Andrey Mokhov <andrey.mokhov@gmail.com>, github: @snowleopard+maintainer: Andrey Mokhov <andrey.mokhov@gmail.com>, github: @snowleopard,+ Alexandre Moine <alexandre@moine.me>, github: @nobrakal copyright: Andrey Mokhov, 2016-2018 homepage: https://github.com/snowleopard/alga category: Algebra, Algorithms, Data Structures, Graphs@@ -14,7 +15,8 @@ GHC==7.10.3, GHC==8.0.2, GHC==8.2.2,- GHC==8.4.1+ GHC==8.4.3,+ GHC==8.6.1 stability: experimental description: <https://github.com/snowleopard/alga Alga> is a library for algebraic construction and@@ -64,9 +66,11 @@ Algebra.Graph.Export.Dot, Algebra.Graph.Fold, Algebra.Graph.HigherKinded.Class,- Algebra.Graph.IntAdjacencyMap,- Algebra.Graph.IntAdjacencyMap.Internal,+ Algebra.Graph.AdjacencyIntMap,+ Algebra.Graph.AdjacencyIntMap.Internal, Algebra.Graph.Internal,+ Algebra.Graph.Label,+ Algebra.Graph.Labelled, Algebra.Graph.NonEmpty, Algebra.Graph.Relation, Algebra.Graph.Relation.Internal,@@ -74,12 +78,15 @@ Algebra.Graph.Relation.Preorder, Algebra.Graph.Relation.Reflexive, Algebra.Graph.Relation.Symmetric,- Algebra.Graph.Relation.Transitive+ Algebra.Graph.Relation.Transitive,+ Algebra.Graph.ToGraph,+ Data.Graph.Typed build-depends: array >= 0.4 && < 0.6, base >= 4.7 && < 5, base-compat >= 0.9.1 && < 0.11, containers >= 0.5.5.1 && < 0.8,- deepseq >= 1.3.0.1 && < 1.5+ deepseq >= 1.3.0.1 && < 1.5,+ mtl >= 2.1 && < 2.3 if !impl(ghc >= 8.0) build-depends: semigroups >= 0.18.3 && < 0.18.4 default-language: Haskell2010@@ -114,22 +121,23 @@ Algebra.Graph.Test.Fold, Algebra.Graph.Test.Generic, Algebra.Graph.Test.Graph,- Algebra.Graph.Test.IntAdjacencyMap,+ Algebra.Graph.Test.AdjacencyIntMap, Algebra.Graph.Test.Internal, Algebra.Graph.Test.NonEmptyGraph,- Algebra.Graph.Test.Relation+ Algebra.Graph.Test.Relation,+ Data.Graph.Test.Typed build-depends: algebraic-graphs,+ array >= 0.4 && < 0.6, base >= 4.7 && < 5,- base-compat >= 0.9.1 && < 0.10,- base-orphans >= 0.5.4 && < 0.8,+ base-compat >= 0.9.1 && < 0.11,+ base-orphans >= 0.5.4 && < 0.9, containers >= 0.5.5.1 && < 0.8,- extra >= 1.5,+ extra >= 1.5 && < 2, QuickCheck >= 2.9 && < 2.12 if !impl(ghc >= 8.0) build-depends: semigroups >= 0.18.3 && < 0.18.4 default-language: Haskell2010- GHC-options: -O2- -Wall+ GHC-options: -Wall -fno-warn-name-shadowing if impl(ghc >= 8.0) GHC-options: -Wcompat@@ -144,25 +152,3 @@ ConstraintKinds RankNTypes ViewPatterns--benchmark benchmark-alga- hs-source-dirs: bench- type: exitcode-stdio-1.0- main-is: Bench.hs- build-depends: algebraic-graphs,- base >= 4.7 && < 5,- base-compat >= 0.9.1 && < 0.10,- containers >= 0.5.5.1 && < 0.8,- criterion >= 1.1- default-language: Haskell2010- GHC-options: -O2- -Wall- -fno-warn-name-shadowing- if impl(ghc >= 8.0)- GHC-options: -Wcompat- -Wincomplete-record-updates- -Wincomplete-uni-patterns- -Wredundant-constraints- default-extensions: FlexibleContexts- TypeFamilies- ScopedTypeVariables
− bench/Bench.hs
@@ -1,200 +0,0 @@-import Prelude ()-import Prelude.Compat--import Criterion.Main-import Data.Char-import Data.Foldable (toList)--import Algebra.Graph.Class-import Algebra.Graph.AdjacencyMap (AdjacencyMap, adjacencyMap)-import Algebra.Graph.Fold (Fold, box, deBruijn, gmap, vertexIntSet, vertexSet)-import Algebra.Graph.IntAdjacencyMap (IntAdjacencyMap)-import Algebra.Graph.Relation (Relation, relation)--import qualified Algebra.Graph.IntAdjacencyMap as Int-import qualified Data.IntSet as IntSet-import qualified Data.Set as Set--v :: Ord a => Fold a -> Int-v = Set.size . vertexSet--l :: Fold a -> Int-l = length . toList--e :: AdjacencyMap a -> Int-e = foldr (\s t -> Set.size s + t) 0 . adjacencyMap--r :: Relation a -> Int-r = Set.size . relation--vInt :: Fold Int -> Int-vInt = IntSet.size . vertexIntSet--eInt :: IntAdjacencyMap -> Int-eInt = foldr (\s t -> IntSet.size s + t) 0 . Int.adjacencyMap--vDeBruijn :: Int -> Int-vDeBruijn n = v $ deBruijn n "0123456789"--lDeBruijn :: Int -> Int-lDeBruijn n = l $ deBruijn n "0123456789"--eDeBruijn :: Int -> Int-eDeBruijn n = e $ deBruijn n "0123456789"--rDeBruijn :: Int -> Int-rDeBruijn n = r $ deBruijn n "0123456789"--vIntDeBruijn :: Int -> Int-vIntDeBruijn n = v $ gmap fastRead $ deBruijn n "0123456789"--eIntDeBruin :: Int -> Int-eIntDeBruin n = e $ gmap fastRead $ deBruijn n "0123456789"---- fastRead is ~3000x faster than read-fastRead :: String -> Int-fastRead = foldr (\c t -> t + ord c - ord '0') 0--fastReadInts :: Int -> Int-fastReadInts n = foldr (+) 0 $ map fastRead $ ints ++ ints- where- ints = mapM (const "0123456789") [1..n]--vMesh :: Int -> Int-vMesh n = v $ gmap (\(x, y) -> x * n + y) $ path [1..n] `box` path [1..n]--lMesh :: Int -> Int-lMesh n = l $ gmap (\(x, y) -> x * n + y) $ path [1..n] `box` path [1..n]--eMesh :: Int -> Int-eMesh n = e $ gmap (\(x, y) -> x * n + y) $ path [1..n] `box` path [1..n]--rMesh :: Int -> Int-rMesh n = r $ gmap (\(x, y) -> x * n + y) $ path [1..n] `box` path [1..n]--vIntMesh :: Int -> Int-vIntMesh n = vInt $ gmap (\(x, y) -> x * n + y) $ path [1..n] `box` path [1..n]--eIntMesh :: Int -> Int-eIntMesh n = eInt $ gmap (\(x, y) -> x * n + y) $ path [1..n] `box` path [1..n]--vIntClique :: Int -> Int-vIntClique n = vInt $ clique [1..n]--eIntClique :: Int -> Int-eIntClique n = eInt $ clique [1..n]--lClique :: Int -> Int-lClique n = l $ clique [1..n]--rClique :: Int -> Int-rClique n = r $ clique [1..n]--main :: IO ()-main = defaultMain- [ bgroup "vDeBruijn"- [ bench "10^1" $ whnf vDeBruijn 1- , bench "10^2" $ whnf vDeBruijn 2- , bench "10^3" $ whnf vDeBruijn 3- , bench "10^4" $ whnf vDeBruijn 4- , bench "10^5" $ whnf vDeBruijn 5- , bench "10^6" $ whnf vDeBruijn 6 ]- , bgroup "lDeBruijn"- [ bench "10^1" $ whnf lDeBruijn 1- , bench "10^2" $ whnf lDeBruijn 2- , bench "10^3" $ whnf lDeBruijn 3- , bench "10^4" $ whnf lDeBruijn 4- , bench "10^5" $ whnf lDeBruijn 5- , bench "10^6" $ whnf lDeBruijn 6 ]- , bgroup "eDeBruijn"- [ bench "10^1" $ whnf eDeBruijn 1- , bench "10^2" $ whnf eDeBruijn 2- , bench "10^3" $ whnf eDeBruijn 3- , bench "10^4" $ whnf eDeBruijn 4- , bench "10^5" $ whnf eDeBruijn 5- , bench "10^6" $ whnf eDeBruijn 6 ]- , bgroup "rDeBruijn"- [ bench "10^1" $ whnf rDeBruijn 1- , bench "10^2" $ whnf rDeBruijn 2- , bench "10^3" $ whnf rDeBruijn 3- , bench "10^4" $ whnf rDeBruijn 4- , bench "10^5" $ whnf rDeBruijn 5- , bench "10^6" $ whnf rDeBruijn 6 ]- , bgroup "vIntDeBruijn"- [ bench "10^1" $ whnf vIntDeBruijn 1- , bench "10^2" $ whnf vIntDeBruijn 2- , bench "10^3" $ whnf vIntDeBruijn 3- , bench "10^4" $ whnf vIntDeBruijn 4- , bench "10^5" $ whnf vIntDeBruijn 5- , bench "10^6" $ whnf vIntDeBruijn 6 ]- , bgroup "eIntDeBruin"- [ bench "10^1" $ whnf eIntDeBruin 1- , bench "10^2" $ whnf eIntDeBruin 2- , bench "10^3" $ whnf eIntDeBruin 3- , bench "10^4" $ whnf eIntDeBruin 4- , bench "10^5" $ whnf eIntDeBruin 5- , bench "10^6" $ whnf eIntDeBruin 6 ]- , bgroup "fastReadInts"- [ bench "10^1" $ whnf fastReadInts 1- , bench "10^2" $ whnf fastReadInts 2- , bench "10^3" $ whnf fastReadInts 3- , bench "10^4" $ whnf fastReadInts 4- , bench "10^5" $ whnf fastReadInts 5- , bench "10^6" $ whnf fastReadInts 6 ]- , bgroup "vMesh"- [ bench "1x1" $ whnf vMesh 1- , bench "10x10" $ whnf vMesh 10- , bench "100x100" $ whnf vMesh 100- , bench "1000x1000" $ whnf vMesh 1000 ]- , bgroup "lMesh"- [ bench "1x1" $ whnf lMesh 1- , bench "10x10" $ whnf lMesh 10- , bench "100x100" $ whnf lMesh 100- , bench "1000x1000" $ whnf lMesh 1000 ]- , bgroup "eMesh"- [ bench "1x1" $ whnf eMesh 1- , bench "10x10" $ whnf eMesh 10- , bench "100x100" $ whnf eMesh 100- , bench "1000x1000" $ whnf eMesh 1000 ]- , bgroup "rMesh"- [ bench "1x1" $ whnf rMesh 1- , bench "10x10" $ whnf rMesh 10- , bench "100x100" $ whnf rMesh 100- , bench "1000x1000" $ whnf rMesh 1000 ]- , bgroup "vIntMesh"- [ bench "1x1" $ whnf vIntMesh 1- , bench "10x10" $ whnf vIntMesh 10- , bench "100x100" $ whnf vIntMesh 100- , bench "1000x1000" $ whnf vIntMesh 1000 ]- , bgroup "eIntMesh"- [ bench "1x1" $ whnf eIntMesh 1- , bench "10x10" $ whnf eIntMesh 10- , bench "100x100" $ whnf eIntMesh 100- , bench "1000x1000" $ whnf eIntMesh 1000 ]- , bgroup "rClique"- [ bench "1" $ nf rClique 1- , bench "10" $ nf rClique 10- , bench "100" $ nf rClique 100- , bench "1000" $ nf rClique 1000- , bench "10000" $ nf rClique 10000 ]- , bgroup "vIntClique"- [ bench "1" $ nf vIntClique 1- , bench "10" $ nf vIntClique 10- , bench "100" $ nf vIntClique 100- , bench "1000" $ nf vIntClique 1000- , bench "10000" $ nf vIntClique 10000- , bench "44722" $ nf vIntClique 44722 ]- , bgroup "lClique"- [ bench "1" $ nf lClique 1- , bench "10" $ nf lClique 10- , bench "100" $ nf lClique 100- , bench "1000" $ nf lClique 1000- , bench "10000" $ nf lClique 10000- , bench "44722" $ nf lClique 44722 ]- , bgroup "eIntClique"- [ bench "1" $ nf eIntClique 1- , bench "10" $ nf eIntClique 10- , bench "100" $ nf eIntClique 100- , bench "1000" $ nf eIntClique 1000- , bench "10000" $ nf eIntClique 10000- , bench "44722" $ nf eIntClique 44722 ] ]
src/Algebra/Graph.hs view
@@ -33,37 +33,49 @@ -- * Graph properties isEmpty, size, hasVertex, hasEdge, vertexCount, edgeCount, vertexList,- edgeList, vertexSet, vertexIntSet, edgeSet,+ edgeList, vertexSet, vertexIntSet, edgeSet, adjacencyList, adjacencyMap,+ adjacencyIntMap, -- * 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, removeEdge, replaceVertex, mergeVertices, splitVertex,- transpose, induce, simplify,+ transpose, induce, simplify, sparsify, -- * Graph composition- box+ box,++ -- * Context+ Context (..), context ) where import Prelude () import Prelude.Compat -import Control.Applicative (Alternative, (<|>))+import Control.Applicative (Alternative) import Control.DeepSeq (NFData (..)) import Control.Monad.Compat+import Control.Monad.State (runState, get, put)+import Data.Foldable (toList)+import Data.Maybe (fromMaybe)+import Data.Tree import Algebra.Graph.Internal -import qualified Algebra.Graph.AdjacencyMap as AM-import qualified Algebra.Graph.Class as C-import qualified Algebra.Graph.HigherKinded.Class as H-import qualified Algebra.Graph.Relation as R-import qualified Data.IntSet as IntSet-import qualified Data.Set as Set-import qualified Data.Tree as Tree+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+import qualified Data.IntSet as IntSet+import qualified Data.Set as Set+import qualified Data.Tree as Tree+ {-| The 'Graph' data type is a deep embedding of the core graph construction primitives 'empty', 'vertex', 'overlay' and 'connect'. We define a 'Num' instance as a convenient notation for working with graphs:@@ -151,28 +163,6 @@ rnf (Overlay x y) = rnf x `seq` rnf y rnf (Connect x y) = rnf x `seq` rnf y -instance C.Graph (Graph a) where- type Vertex (Graph a) = a- empty = empty- vertex = vertex- overlay = overlay- connect = connect--instance C.ToGraph (Graph a) where- type ToVertex (Graph a) = a- foldg e v o c = go- where- go Empty = e- go (Vertex x ) = v x- go (Overlay x y) = o (go x) (go y)- go (Connect x y) = c (go x) (go y)--instance H.ToGraph Graph where- toGraph = foldg H.empty H.vertex H.overlay H.connect--instance H.Graph Graph where- connect = connect- instance Num a => Num (Graph a) where fromInteger = Vertex . fromInteger (+) = Overlay@@ -182,8 +172,19 @@ negate = id instance Ord a => Eq (Graph a) where- x == y = C.toGraph x == (C.toGraph y :: AM.AdjacencyMap a)+ (==) = equals +-- 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++-- | Like @equals@ but specialised for graphs with vertices of type 'Int'.+equalsInt :: Graph Int -> Graph Int -> Bool+equalsInt x y = adjacencyIntMap x == adjacencyIntMap y+ instance Applicative Graph where pure = Vertex (<*>) = ap@@ -212,6 +213,7 @@ -- @ empty :: Graph a empty = Empty+{-# INLINE empty #-} -- | Construct the graph comprising /a single isolated vertex/. An alias for the -- constructor 'Vertex'.@@ -226,6 +228,7 @@ -- @ vertex :: a -> Graph a vertex = Vertex+{-# INLINE vertex #-} -- | Construct the graph comprising /a single edge/. -- Complexity: /O(1)/ time, memory and size.@@ -238,7 +241,7 @@ -- 'vertexCount' (edge 1 2) == 2 -- @ edge :: a -> a -> Graph a-edge = H.edge+edge x y = connect (vertex x) (vertex y) -- | /Overlay/ two graphs. An alias for the constructor 'Overlay'. This is a -- commutative, associative and idempotent operation with the identity 'empty'.@@ -257,6 +260,7 @@ -- @ overlay :: Graph a -> Graph a -> Graph a overlay = Overlay+{-# INLINE overlay #-} -- | /Connect/ two graphs. An alias for the constructor 'Connect'. This is an -- associative operation with the identity 'empty', which distributes over@@ -280,6 +284,7 @@ -- @ connect :: Graph a -> Graph a -> Graph a connect = Connect+{-# INLINE connect #-} -- | Construct the graph comprising a given list of isolated vertices. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the@@ -293,7 +298,8 @@ -- 'vertexSet' . vertices == Set.'Set.fromList' -- @ vertices :: [a] -> Graph a-vertices = H.vertices+vertices = overlays . map vertex+{-# NOINLINE [1] vertices #-} -- | Construct the graph from a list of edges. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the@@ -305,7 +311,7 @@ -- 'edgeCount' . edges == 'length' . 'Data.List.nub' -- @ edges :: [(a, a)] -> Graph a-edges = H.edges+edges = overlays . map (uncurry edge) -- | Overlay a given list of graphs. -- Complexity: /O(L)/ time and memory, and /O(S)/ size, where /L/ is the length@@ -319,7 +325,8 @@ -- 'isEmpty' . overlays == 'all' 'isEmpty' -- @ overlays :: [Graph a] -> Graph a-overlays = H.overlays+overlays = concatg overlay+{-# INLINE [2] overlays #-} -- | Connect a given list of graphs. -- Complexity: /O(L)/ time and memory, and /O(S)/ size, where /L/ is the length@@ -333,8 +340,13 @@ -- 'isEmpty' . connects == 'all' 'isEmpty' -- @ connects :: [Graph a] -> Graph a-connects = H.connects+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+ -- | 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, overlay and connect.@@ -350,7 +362,12 @@ -- foldg True (const False) (&&) (&&) == 'isEmpty' -- @ foldg :: b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Graph a -> b-foldg = C.foldg+foldg e v o c = go+ where+ go Empty = e+ go (Vertex x ) = v x+ go (Overlay x y) = o (go x) (go y)+ go (Connect x y) = c (go x) (go y) -- | The 'isSubgraphOf' function takes two graphs and returns 'True' if the -- first graph is a /subgraph/ of the second.@@ -364,8 +381,9 @@ -- isSubgraphOf ('overlay' x y) ('connect' x y) == True -- isSubgraphOf ('path' xs) ('circuit' xs) == True -- @+{-# SPECIALISE isSubgraphOf :: Graph Int -> Graph Int -> Bool #-} isSubgraphOf :: Ord a => Graph a -> Graph a -> Bool-isSubgraphOf = H.isSubgraphOf+isSubgraphOf x y = overlay x y == y -- | Structural equality on graph expressions. -- Complexity: /O(s)/ time.@@ -377,6 +395,7 @@ -- 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@@ -397,7 +416,7 @@ -- isEmpty ('removeEdge' x y $ 'edge' x y) == False -- @ isEmpty :: Graph a -> Bool-isEmpty = H.isEmpty+isEmpty = foldg True (const False) (&&) (&&) -- | The /size/ of a graph, i.e. the number of leaves of the expression -- including 'empty' leaves.@@ -423,8 +442,9 @@ -- hasVertex 1 ('vertex' 2) == False -- hasVertex x . 'removeVertex' x == const False -- @+{-# SPECIALISE hasVertex :: Int -> Graph Int -> Bool #-} hasVertex :: Eq a => a -> Graph a -> Bool-hasVertex = H.hasVertex+hasVertex x = foldg False (==x) (||) (||) -- | Check if a graph contains a given edge. -- Complexity: /O(s)/ time.@@ -436,8 +456,20 @@ -- hasEdge x y . 'removeEdge' x y == const False -- hasEdge x y == 'elem' (x,y) . 'edgeList' -- @-hasEdge :: Ord a => a -> a -> Graph a -> Bool-hasEdge = H.hasEdge+{-# SPECIALISE hasEdge :: Int -> Int -> Graph Int -> Bool #-}+hasEdge :: Eq a => a -> a -> Graph a -> Bool+hasEdge s t g = hit g == Edge+ where+ hit Empty = Miss+ hit (Vertex x ) = if x == s then Tail else Miss+ hit (Overlay x y) = case hit x of+ Miss -> hit y+ Tail -> max Tail (hit y)+ Edge -> Edge+ hit (Connect x y) = case hit x of+ Miss -> hit y+ Tail -> if hasVertex t y then Edge else Tail+ Edge -> Edge -- | The number of vertices in a graph. -- Complexity: /O(s * log(n))/ time.@@ -447,9 +479,15 @@ -- vertexCount ('vertex' x) == 1 -- vertexCount == 'length' . 'vertexList' -- @+{-# INLINE [1] vertexCount #-}+{-# RULES "vertexCount/Int" vertexCount = vertexIntCount #-} vertexCount :: Ord a => Graph a -> Int-vertexCount = length . vertexList+vertexCount = Set.size . vertexSet +-- | Like 'vertexCount' but specialised for graphs with vertices of type 'Int'.+vertexIntCount :: Graph Int -> Int+vertexIntCount = IntSet.size . vertexIntSet+ -- | The number of edges in a graph. -- 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/.@@ -460,9 +498,15 @@ -- edgeCount ('edge' x y) == 1 -- edgeCount == 'length' . 'edgeList' -- @+{-# INLINE [1] edgeCount #-}+{-# RULES "edgeCount/Int" edgeCount = edgeCountInt #-} edgeCount :: Ord a => Graph a -> Int-edgeCount = length . edgeList+edgeCount = AM.edgeCount . toAdjacencyMap +-- | Like 'edgeCount' but specialised for graphs with vertices of type 'Int'.+edgeCountInt :: Graph Int -> Int+edgeCountInt = AIM.edgeCount . toAdjacencyIntMap+ -- | The sorted list of vertices of a given graph. -- Complexity: /O(s * log(n))/ time and /O(n)/ memory. --@@ -471,9 +515,15 @@ -- 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 +-- | Like 'vertexList' but specialised for graphs with vertices of type 'Int'.+vertexIntList :: Graph Int -> [Int]+vertexIntList = IntSet.toList . vertexIntSet+ -- | The sorted list of edges of a graph. -- Complexity: /O(s + m * log(m))/ time and /O(m)/ memory. Note that the number of -- edges /m/ of a graph can be quadratic with respect to the expression size /s/.@@ -486,9 +536,15 @@ -- edgeList . 'edges' == 'Data.List.nub' . 'Data.List.sort' -- 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 . C.toGraph+edgeList = AM.edgeList . toAdjacencyMap +-- | Like 'edgeList' but specialised for graphs with vertices of type 'Int'.+edgeIntList :: Graph Int -> [(Int, Int)]+edgeIntList = AIM.edgeList . toAdjacencyIntMap+ -- | The set of vertices of a given graph. -- Complexity: /O(s * log(n))/ time and /O(n)/ memory. --@@ -499,7 +555,7 @@ -- vertexSet . 'clique' == Set.'Set.fromList' -- @ vertexSet :: Ord a => Graph a -> Set.Set a-vertexSet = H.vertexSet+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'.@@ -512,7 +568,7 @@ -- vertexIntSet . 'clique' == IntSet.'IntSet.fromList' -- @ vertexIntSet :: Graph Int -> IntSet.IntSet-vertexIntSet = H.vertexIntSet+vertexIntSet = 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.@@ -524,8 +580,50 @@ -- edgeSet . 'edges' == Set.'Set.fromList' -- @ edgeSet :: Ord a => Graph a -> Set.Set (a, a)-edgeSet = R.edgeSet . C.toGraph+edgeSet = AM.edgeSet . toAdjacencyMap+{-# INLINE [1] edgeSet #-}+{-# RULES "edgeSet/Int" edgeSet = edgeIntSet #-} +-- | Like 'edgeSet' but specialised for graphs with vertices of type 'Int'.+edgeIntSet :: Graph Int -> Set.Set (Int,Int)+edgeIntSet = AIM.edgeSet . toAdjacencyIntMap++-- | The sorted /adjacency list/ of a graph.+-- Complexity: /O(n + m)/ time and /O(m)/ memory.+--+-- @+-- adjacencyList 'empty' == []+-- adjacencyList ('vertex' x) == [(x, [])]+-- adjacencyList ('edge' 1 2) == [(1, [2]), (2, [])]+-- 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++-- 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'.+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'.+toAdjacencyIntMap :: Graph Int -> AIM.AdjacencyIntMap+toAdjacencyIntMap = foldg AIM.empty AIM.vertex AIM.overlay AIM.connect+ -- | The /path/ on a list of vertices. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the -- given list.@@ -537,7 +635,9 @@ -- path . 'reverse' == 'transpose' . path -- @ path :: [a] -> Graph a-path = H.path+path xs = case xs of [] -> empty+ [x] -> vertex x+ (_:ys) -> edges (zip xs ys) -- | The /circuit/ on a list of vertices. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the@@ -550,7 +650,8 @@ -- circuit . 'reverse' == 'transpose' . circuit -- @ circuit :: [a] -> Graph a-circuit = H.circuit+circuit [] = empty+circuit (x:xs) = path $ [x] ++ xs ++ [x] -- | The /clique/ on a list of vertices. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the@@ -565,7 +666,8 @@ -- clique . 'reverse' == 'transpose' . clique -- @ clique :: [a] -> Graph a-clique = H.clique+clique = connects . map vertex+{-# NOINLINE [1] clique #-} -- | The /biclique/ on two lists of vertices. -- Complexity: /O(L1 + L2)/ time, memory and size, where /L1/ and /L2/ are the@@ -579,7 +681,9 @@ -- biclique xs ys == 'connect' ('vertices' xs) ('vertices' ys) -- @ biclique :: [a] -> [a] -> Graph a-biclique = H.biclique+biclique xs [] = vertices xs+biclique [] ys = vertices ys+biclique xs ys = connect (vertices xs) (vertices ys) -- | The /star/ formed by a centre vertex connected to a list of leaves. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the@@ -592,21 +696,27 @@ -- star x ys == 'connect' ('vertex' x) ('vertices' ys) -- @ star :: a -> [a] -> Graph a-star = H.star+star x [] = vertex x+star x ys = connect (vertex x) (vertices ys)+{-# INLINE star #-} --- | 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 :: a -> [a] -> Graph a-starTranspose = H.starTranspose+stars :: [(a, [a])] -> Graph a+stars = overlays . map (uncurry star)+{-# INLINE stars #-} -- | The /tree graph/ constructed from a given 'Tree.Tree' data structure. -- Complexity: /O(T)/ time, memory and size, where /T/ is the size of the@@ -619,7 +729,9 @@ -- tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == 'edges' [(1,2), (1,3), (3,4), (3,5)] -- @ tree :: Tree.Tree a -> Graph a-tree = H.tree+tree (Node x []) = vertex x+tree (Node x f ) = star x (map rootLabel f)+ `overlay` forest (filter (not . null . subForest) f) -- | The /forest graph/ constructed from a given 'Tree.Forest' data structure. -- Complexity: /O(F)/ time, memory and size, where /F/ is the size of the@@ -632,7 +744,7 @@ -- forest == 'overlays' . map 'tree' -- @ forest :: Tree.Forest a -> Graph a-forest = H.forest+forest = overlays . map tree -- | Construct a /mesh graph/ from two lists of vertices. -- Complexity: /O(L1 * L2)/ time, memory and size, where /L1/ and /L2/ are the@@ -647,7 +759,17 @@ -- , ((2,\'a\'),(3,\'a\')), ((2,\'b\'),(3,\'b\')), ((3,\'a\'),(3,\'b\')) ] -- @ mesh :: [a] -> [b] -> Graph (a, b)-mesh = H.mesh+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@@ -656,14 +778,19 @@ -- @ -- torus xs [] == 'empty' -- torus [] ys == 'empty'--- torus [x] [y] == 'edge' (x, y) (x, y)+-- torus [x] [y] == 'edge' (x,y) (x,y) -- torus xs ys == 'box' ('circuit' xs) ('circuit' ys) -- torus [1,2] "ab" == 'edges' [ ((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\')) ] -- @ torus :: [a] -> [b] -> Graph (a, b)-torus = H.torus+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@@ -681,7 +808,12 @@ -- n > 0 ==> 'edgeCount' (deBruijn n xs) == ('length' $ 'Data.List.nub' xs)^(n + 1) -- @ deBruijn :: Int -> [a] -> Graph [a]-deBruijn = H.deBruijn+deBruijn 0 _ = edge [] []+deBruijn len alphabet = skeleton >>= expand+ where+ overlaps = mapM (const alphabet) [2..len]+ skeleton = edges [ (Left s, Right s) | s <- overlaps ]+ expand v = vertices [ either ([a] ++) (++ [a]) v | a <- alphabet ] -- | Remove a vertex from a given graph. -- Complexity: /O(s)/ time, memory and size.@@ -693,31 +825,34 @@ -- 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 = H.removeVertex+removeVertex v = induce (/= v) -- | Remove an edge from a given graph. -- Complexity: /O(s)/ time, memory and size. -- -- @--- removeEdge x y ('edge' x y) == 'vertices' [x, y]+-- removeEdge x y ('edge' 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 -- '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) + -- TODO: Export -- | Filter vertices in a subgraph context.+{-# SPECIALISE filterContext :: Int -> (Int -> Bool) -> (Int -> Bool) -> Graph Int -> Graph Int #-} 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) = overlays [ induce (/=s) g- , starTranspose s (filter i is)- , star s (filter o os) ]+ go (Context is os) = induce (/=s) g `overlay` transpose (star s (filter i is))+ `overlay` star s (filter o os) -- | 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.@@ -728,9 +863,11 @@ -- 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 = H.replaceVertex+replaceVertex u v = fmap $ \w -> if w == u then v else w + -- | 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.@@ -742,7 +879,7 @@ -- mergeVertices odd 1 (3 + 4 * 5) == 4 * 1 -- @ mergeVertices :: (a -> Bool) -> a -> Graph a -> Graph a-mergeVertices = H.mergeVertices+mergeVertices p v = fmap $ \w -> if p w then v else w -- | Split a vertex into a list of vertices with the same connectivity. -- Complexity: /O(s + k * L)/ time, memory and size, where /k/ is the number of@@ -755,8 +892,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 = H.splitVertex+splitVertex v us g = g >>= \w -> if w == v then vertices us else vertex w -- | Transpose a given graph. -- Complexity: /O(s)/ time, memory and size.@@ -771,7 +909,21 @@ -- @ 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)+ #-}+ -- | 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@@ -807,9 +959,11 @@ -- 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 simple :: (Int -> Int -> Int) -> Int -> Int -> Int #-} simple :: Eq g => (g -> g -> g) -> g -> g -> g simple op x y | x == z = x@@ -844,4 +998,50 @@ -- 'edgeCount' (box x y) <= 'vertexCount' x * 'edgeCount' y + 'edgeCount' x * 'vertexCount' y -- @ box :: Graph a -> Graph b -> Graph (a, b)-box = H.box+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++-- | /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+-- reachability relation between the original vertices. Sparsification is useful+-- when working with dense graphs, as it can reduce the number of edges from+-- O(n^2) down to O(n) by replacing cliques, bicliques and similar densely+-- connected structures by sparse subgraphs built out of intermediate vertices.+-- Complexity: O(s) time, memory and size.+--+-- @+-- 'Data.List.sort' . 'Algebra.Graph.ToGraph.reachable' x == 'Data.List.sort' . 'Data.Either.rights' . 'Algebra.Graph.ToGraph.reachable' ('Data.Either.Right' x) . sparsify+-- 'vertexCount' (sparsify x) <= 'vertexCount' x + 'size' x + 1+-- 'edgeCount' (sparsify x) <= 3 * 'size' x+-- 'size' (sparsify x) <= 3 * 'size' x+-- @+sparsify :: Graph a -> Graph (Either Int a)+sparsify graph = res+ where+ (res, end) = runState (foldg e v o c graph 0 end) 1+ e s t = return $ path [Left s, Left t]+ v x s t = return $ clique [Left s, Right x, Left t]+ o x y s t = overlay <$> s `x` t <*> s `y` t+ c x y s t = do+ m <- get+ put (m + 1)+ overlay <$> s `x` m <*> m `y` t
+ src/Algebra/Graph/AdjacencyIntMap.hs view
@@ -0,0 +1,692 @@+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.AdjacencyIntMap+-- 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 '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.+-----------------------------------------------------------------------------+module Algebra.Graph.AdjacencyIntMap (+ -- * Data structure+ AdjacencyIntMap, adjacencyIntMap,++ -- * Basic graph construction primitives+ empty, vertex, edge, overlay, connect, vertices, edges, overlays, connects,++ -- * Relations on graphs+ isSubgraphOf,++ -- * Graph properties+ isEmpty, hasVertex, hasEdge, vertexCount, edgeCount, vertexList, edgeList,+ adjacencyList, vertexIntSet, edgeSet, preIntSet, postIntSet,++ -- * Standard families of graphs+ path, circuit, clique, biclique, star, stars, tree, forest,++ -- * Graph transformation+ removeVertex, removeEdge, replaceVertex, mergeVertices, transpose, gmap,+ induce,++ -- * Algorithms+ dfsForest, dfsForestFrom, dfs, reachable, topSort, isAcyclic,++ -- * Correctness properties+ isDfsForestOf, isTopSortOf+ ) 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 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 :: Int -> Int -> AdjacencyIntMap+edge x y | x == y = AM $ IntMap.singleton x (IntSet.singleton y)+ | otherwise = AM $ IntMap.fromList [(x, IntSet.singleton y), (y, IntSet.empty)]++-- | 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'+-- 'vertexIntSet' . vertices == IntSet.'IntSet.fromList'+-- @+vertices :: [Int] -> AdjacencyIntMap+vertices = AM . IntMap.fromList . map (\x -> (x, IntSet.empty))+{-# NOINLINE [1] vertices #-}++-- | Construct the graph from a list of edges.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- edges [] == 'empty'+-- edges [(x,y)] == 'edge' x y+-- 'edgeCount' . edges == 'length' . 'Data.List.nub'+-- 'edgeList' . edges == 'Data.List.nub' . 'Data.List.sort'+-- @+edges :: [(Int, Int)] -> AdjacencyIntMap+edges = fromAdjacencyIntSets . map (fmap IntSet.singleton)++-- | 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 :: [AdjacencyIntMap] -> AdjacencyIntMap+overlays = AM . IntMap.unionsWith IntSet.union . map adjacencyIntMap+{-# NOINLINE [1] overlays #-}++-- | Connect a given list of graphs.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- connects [] == 'empty'+-- connects [x] == x+-- connects [x,y] == 'connect' x y+-- connects == 'foldr' 'connect' 'empty'+-- 'isEmpty' . connects == 'all' 'isEmpty'+-- @+connects :: [AdjacencyIntMap] -> AdjacencyIntMap+connects = foldr connect empty+{-# NOINLINE [1] connects #-}++-- | The 'isSubgraphOf' function takes two graphs and returns 'True' if the+-- first graph is a /subgraph/ of the second.+-- Complexity: /O((n + m) * log(n))/ time.+--+-- @+-- isSubgraphOf 'empty' x == True+-- isSubgraphOf ('vertex' x) 'empty' == False+-- isSubgraphOf x ('overlay' x y) == True+-- isSubgraphOf ('overlay' x y) ('connect' x y) == True+-- isSubgraphOf ('path' xs) ('circuit' xs) == True+-- @+isSubgraphOf :: AdjacencyIntMap -> AdjacencyIntMap -> Bool+isSubgraphOf x y = IntMap.isSubmapOfBy IntSet.isSubsetOf (adjacencyIntMap x) (adjacencyIntMap 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' x y) == False+-- @+isEmpty :: AdjacencyIntMap -> Bool+isEmpty = IntMap.null . adjacencyIntMap++-- | 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 :: Int -> AdjacencyIntMap -> Bool+hasVertex x = IntMap.member x . adjacencyIntMap++-- | Check if a graph contains a given edge.+-- Complexity: /O(log(n))/ time.+--+-- @+-- hasEdge x y 'empty' == False+-- hasEdge x y ('vertex' z) == False+-- hasEdge x y ('edge' x y) == True+-- hasEdge x y . 'removeEdge' x y == const False+-- hasEdge x y == 'elem' (x,y) . 'edgeList'+-- @+hasEdge :: Int -> Int -> AdjacencyIntMap -> Bool+hasEdge u v a = case IntMap.lookup u (adjacencyIntMap a) of+ Nothing -> False+ Just vs -> IntSet.member v vs++-- | The number of vertices in a graph.+-- Complexity: /O(1)/ time.+--+-- @+-- vertexCount 'empty' == 0+-- vertexCount ('vertex' x) == 1+-- vertexCount == 'length' . 'vertexList'+-- @+vertexCount :: AdjacencyIntMap -> Int+vertexCount = IntMap.size . adjacencyIntMap++-- | The number of edges in a graph.+-- Complexity: /O(n)/ time.+--+-- @+-- edgeCount 'empty' == 0+-- edgeCount ('vertex' x) == 0+-- edgeCount ('edge' x y) == 1+-- edgeCount == 'length' . 'edgeList'+-- @+edgeCount :: AdjacencyIntMap -> Int+edgeCount = getSum . foldMap (Sum . IntSet.size) . adjacencyIntMap++-- | 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 :: AdjacencyIntMap -> [Int]+vertexList = IntMap.keys . adjacencyIntMap++-- | The sorted list of edges of a graph.+-- Complexity: /O(n + m)/ time and /O(m)/ memory.+--+-- @+-- edgeList 'empty' == []+-- edgeList ('vertex' x) == []+-- 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 :: AdjacencyIntMap -> [(Int, Int)]+edgeList (AM m) = [ (x, y) | (x, ys) <- IntMap.toAscList m, y <- IntSet.toAscList ys ]++-- | The set of vertices of a given graph.+-- 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 :: AdjacencyIntMap -> IntSet+vertexIntSet = IntMap.keysSet . adjacencyIntMap++-- | The set of edges of a given graph.+-- Complexity: /O((n + m) * log(m))/ time and /O(m)/ memory.+--+-- @+-- edgeSet 'empty' == Set.'Set.empty'+-- edgeSet ('vertex' x) == Set.'Set.empty'+-- edgeSet ('edge' x y) == Set.'Set.singleton' (x,y)+-- edgeSet . 'edges' == Set.'Set.fromList'+-- @+edgeSet :: AdjacencyIntMap -> Set (Int, Int)+edgeSet = Set.fromAscList . edgeList++-- | The sorted /adjacency list/ of a graph.+-- Complexity: /O(n + m)/ time and /O(m)/ memory.+--+-- @+-- adjacencyList 'empty' == []+-- adjacencyList ('vertex' x) == [(x, [])]+-- adjacencyList ('edge' 1 2) == [(1, [2]), (2, [])]+-- adjacencyList ('star' 2 [3,1]) == [(1, []), (2, [1,3]), (3, [])]+-- 'stars' . adjacencyList == id+-- @+adjacencyList :: AdjacencyIntMap -> [(Int, [Int])]+adjacencyList = map (fmap IntSet.toAscList) . IntMap.toAscList . adjacencyIntMap++-- | The /preset/ (here @preIntSet@) of an element @x@ is the set of its+-- /direct predecessors/.+-- Complexity: /O(n * log(n))/ time and /O(n)/ memory.+--+-- @+-- preIntSet x 'empty' == Set.'Set.empty'+-- preIntSet x ('vertex' x) == Set.'Set.empty'+-- preIntSet 1 ('edge' 1 2) == Set.'Set.empty'+-- preIntSet y ('edge' x y) == Set.'Set.fromList' [x]+-- @+preIntSet :: Int -> AdjacencyIntMap -> IntSet.IntSet+preIntSet x = IntSet.fromAscList . map fst . filter p . IntMap.toAscList . adjacencyIntMap+ where+ p (_, set) = x `IntSet.member` set++-- | The /postset/ (here @postIntSet@) of a vertex is the set of its+-- /direct successors/.+--+-- @+-- postIntSet x 'empty' == IntSet.'IntSet.empty'+-- postIntSet x ('vertex' x) == IntSet.'IntSet.empty'+-- postIntSet x ('edge' x y) == IntSet.'IntSet.fromList' [y]+-- postIntSet 2 ('edge' 1 2) == IntSet.'IntSet.empty'+-- @+postIntSet :: Int -> AdjacencyIntMap -> IntSet+postIntSet x = IntMap.findWithDefault IntSet.empty x . adjacencyIntMap++-- | The /path/ on a list of vertices.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- path [] == 'empty'+-- path [x] == 'vertex' x+-- path [x,y] == 'edge' x y+-- path . 'reverse' == 'transpose' . path+-- @+path :: [Int] -> AdjacencyIntMap+path xs = case xs of [] -> empty+ [x] -> vertex x+ (_:ys) -> edges (zip xs ys)++-- | The /circuit/ on a list of vertices.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- circuit [] == 'empty'+-- circuit [x] == 'edge' x x+-- circuit [x,y] == 'edges' [(x,y), (y,x)]+-- circuit . 'reverse' == 'transpose' . circuit+-- @+circuit :: [Int] -> AdjacencyIntMap+circuit [] = empty+circuit (x:xs) = path $ [x] ++ xs ++ [x]++-- | The /clique/ on a list of vertices.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- clique [] == 'empty'+-- clique [x] == 'vertex' x+-- clique [x,y] == 'edge' x y+-- clique [x,y,z] == 'edges' [(x,y), (x,z), (y,z)]+-- clique (xs ++ ys) == 'connect' (clique xs) (clique ys)+-- clique . 'reverse' == 'transpose' . clique+-- @+clique :: [Int] -> AdjacencyIntMap+clique = fromAdjacencyIntSets . fst . go+ where+ go [] = ([], IntSet.empty)+ go (x:xs) = let (res, set) = go xs in ((x, set) : res, IntSet.insert x set)+{-# NOINLINE [1] clique #-}++-- | The /biclique/ on two lists of vertices.+-- Complexity: /O(n * log(n) + m)/ time and /O(n + m)/ memory.+--+-- @+-- biclique [] [] == 'empty'+-- biclique [x] [] == 'vertex' x+-- biclique [] [y] == 'vertex' y+-- biclique [x1,x2] [y1,y2] == 'edges' [(x1,y1), (x1,y2), (x2,y1), (x2,y2)]+-- biclique xs ys == 'connect' ('vertices' xs) ('vertices' ys)+-- @+biclique :: [Int] -> [Int] -> AdjacencyIntMap+biclique xs ys = AM $ IntMap.fromSet adjacent (x `IntSet.union` y)+ where+ x = IntSet.fromList xs+ y = IntSet.fromList ys+ adjacent v = if v `IntSet.member` x then y else IntSet.empty++-- TODO: Optimise.+-- | The /star/ formed by a centre vertex connected to a list of leaves.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- star x [] == 'vertex' x+-- star x [y] == 'edge' x y+-- star x [y,z] == 'edges' [(x,y), (x,z)]+-- star x ys == 'connect' ('vertex' x) ('vertices' ys)+-- @+star :: Int -> [Int] -> AdjacencyIntMap+star x [] = vertex x+star x ys = connect (vertex x) (vertices ys)+{-# 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.+--+-- @+-- stars [] == 'empty'+-- stars [(x, [])] == 'vertex' x+-- stars [(x, [y])] == 'edge' x y+-- stars [(x, ys)] == 'star' x ys+-- stars == 'overlays' . map (uncurry 'star')+-- stars . 'adjacencyList' == id+-- 'overlay' (stars xs) (stars ys) == stars (xs ++ ys)+-- @+stars :: [(Int, [Int])] -> AdjacencyIntMap+stars = fromAdjacencyIntSets . map (fmap IntSet.fromList)++-- | 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 []]]) == 'path' [x,y,z]+-- tree (Node x [Node y [], Node z []]) == 'star' x [y,z]+-- tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == 'edges' [(1,2), (1,3), (3,4), (3,5)]+-- @+tree :: Tree Int -> AdjacencyIntMap+tree (Node x []) = vertex x+tree (Node x f ) = star x (map rootLabel f)+ `overlay` forest (filter (not . null . subForest) f)++-- | The /forest graph/ constructed from a given 'Forest' data structure.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- forest [] == 'empty'+-- forest [x] == 'tree' x+-- forest [Node 1 [Node 2 [], Node 3 []], Node 4 [Node 5 []]] == 'edges' [(1,2), (1,3), (4,5)]+-- forest == 'overlays' . map 'tree'+-- @+forest :: Forest Int -> AdjacencyIntMap+forest = overlays . map tree++-- | 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' x x) == 'empty'+-- removeVertex 1 ('edge' 1 2) == 'vertex' 2+-- removeVertex x . removeVertex x == removeVertex x+-- @+removeVertex :: Int -> AdjacencyIntMap -> AdjacencyIntMap+removeVertex x = AM . IntMap.map (IntSet.delete x) . IntMap.delete x . adjacencyIntMap++-- | Remove an edge from a given graph.+-- Complexity: /O(log(n))/ time.+--+-- @+-- removeEdge x y ('edge' 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 :: Int -> Int -> AdjacencyIntMap -> AdjacencyIntMap+removeEdge x y = AM . IntMap.adjust (IntSet.delete y) x . adjacencyIntMap++-- | The function @'replaceVertex' x y@ replaces vertex @x@ with vertex @y@ in a+-- given 'AdjacencyIntMap'. 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 :: Int -> Int -> AdjacencyIntMap -> AdjacencyIntMap+replaceVertex u v = gmap $ \w -> if w == u then v else w++-- | Merge vertices satisfying a given predicate into a given vertex.+-- Complexity: /O((n + m) * log(n))/ time, assuming that the predicate takes+-- /O(1)/ to be evaluated.+--+-- @+-- mergeVertices (const False) x == id+-- mergeVertices (== x) y == 'replaceVertex' x y+-- mergeVertices even 1 (0 * 2) == 1 * 1+-- mergeVertices odd 1 (3 + 4 * 5) == 4 * 1+-- @+mergeVertices :: (Int -> Bool) -> Int -> AdjacencyIntMap -> AdjacencyIntMap+mergeVertices p v = gmap $ \u -> if p u then v else u++-- | Transpose a given graph.+-- Complexity: /O(m * log(n))/ time, /O(n + m)/ memory.+--+-- @+-- transpose 'empty' == 'empty'+-- 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 :: AdjacencyIntMap -> AdjacencyIntMap+transpose (AM m) = AM $ IntMap.foldrWithKey combine vs m+ where+ combine v es = IntMap.unionWith IntSet.union (IntMap.fromSet (const $ IntSet.singleton v) es)+ vs = IntMap.fromSet (const IntSet.empty) (IntMap.keysSet m)+{-# 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)+ #-}++-- | 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+-- 'AdjacencyIntMap'.+-- Complexity: /O((n + m) * log(n))/ time.+--+-- @+-- gmap f 'empty' == 'empty'+-- gmap f ('vertex' x) == 'vertex' (f x)+-- gmap f ('edge' x y) == 'edge' (f x) (f y)+-- gmap id == id+-- gmap f . gmap g == gmap (f . g)+-- @+gmap :: (Int -> Int) -> AdjacencyIntMap -> AdjacencyIntMap+gmap f = AM . IntMap.map (IntSet.map f) . IntMap.mapKeysWith IntSet.union f . adjacencyIntMap++-- | 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 :: (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/.+--+-- @+-- 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
@@ -0,0 +1,232 @@+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.AdjacencyIntMap.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 adjacency maps. The API is unstable+-- and unsafe, and is exposed only for documentation. You should use the+-- non-internal module "Algebra.Graph.AdjacencyIntMap" instead.+-----------------------------------------------------------------------------+module Algebra.Graph.AdjacencyIntMap.Internal (+ -- * Adjacency map implementation+ AdjacencyIntMap (..), empty, vertex, overlay, connect, fromAdjacencyIntSets,+ consistent+ ) where++import Data.IntMap.Strict (IntMap, keysSet, fromSet)+import Data.IntSet (IntSet)+import Data.List++import Control.DeepSeq (NFData (..))++import qualified Data.IntMap.Strict as IntMap+import qualified Data.IntSet as IntSet++{-| The 'AdjacencyIntMap' 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))++The 'Show' instance is defined using basic graph construction primitives:++@show (empty :: AdjacencyIntMap Int) == "empty"+show (1 :: AdjacencyIntMap Int) == "vertex 1"+show (1 + 2 :: AdjacencyIntMap Int) == "vertices [1,2]"+show (1 * 2 :: AdjacencyIntMap Int) == "edge 1 2"+show (1 * 2 * 3 :: AdjacencyIntMap Int) == "edges [(1,2),(1,3),(2,3)]"+show (1 * 2 + 3 :: AdjacencyIntMap Int) == "overlay (vertex 3) (edge 1 2)"@++The 'Eq' instance satisfies all axioms of algebraic graphs:++ * 'Algebra.Graph.AdjacencyIntMap.overlay' is commutative and associative:++ > x + y == y + x+ > x + (y + z) == (x + y) + z++ * 'Algebra.Graph.AdjacencyIntMap.connect' is associative and has+ 'Algebra.Graph.AdjacencyIntMap.empty' as the identity:++ > x * empty == x+ > empty * x == x+ > x * (y * z) == (x * y) * z++ * 'Algebra.Graph.AdjacencyIntMap.connect' distributes over+ 'Algebra.Graph.AdjacencyIntMap.overlay':++ > x * (y + z) == x * y + x * z+ > (x + y) * z == x * z + y * z++ * 'Algebra.Graph.AdjacencyIntMap.connect' can be decomposed:++ > x * y * z == x * y + x * z + y * z++The following useful theorems can be proved from the above set of axioms.++ * 'Algebra.Graph.AdjacencyIntMap.overlay' has+ 'Algebra.Graph.AdjacencyIntMap.empty' as the identity and is idempotent:++ > x + empty == x+ > empty + x == x+ > x + x == x++ * Absorption and saturation of 'Algebra.Graph.AdjacencyIntMap.connect':++ > 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.+-}+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.+ --+ -- @+ -- adjacencyIntMap 'empty' == IntMap.'IntMap.empty'+ -- adjacencyIntMap ('vertex' x) == IntMap.'IntMap.singleton' x IntSet.'IntSet.empty'+ -- adjacencyIntMap ('Algebra.Graph.AdjacencyIntMap.edge' 1 1) == IntMap.'IntMap.singleton' 1 (IntSet.'IntSet.singleton' 1)+ -- adjacencyIntMap ('Algebra.Graph.AdjacencyIntMap.edge' 1 2) == IntMap.'IntMap.fromList' [(1,IntSet.'IntSet.singleton' 2), (2,IntSet.'IntSet.empty')]+ -- @+ adjacencyIntMap :: IntMap IntSet } deriving Eq++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 ++ ")"+ 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+ 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 Num AdjacencyIntMap where+ fromInteger = vertex . fromInteger+ (+) = overlay+ (*) = connect+ signum = const 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+-- inconsistent adjacency map, and we use this function in testing.+-- /Note: this function is for internal use only/.+--+-- @+-- consistent 'Algebra.Graph.AdjacencyIntMap.empty' == True+-- consistent ('Algebra.Graph.AdjacencyIntMap.vertex' x) == True+-- consistent ('Algebra.Graph.AdjacencyIntMap.overlay' x y) == True+-- consistent ('Algebra.Graph.AdjacencyIntMap.connect' x y) == True+-- consistent ('Algebra.Graph.AdjacencyIntMap.edge' x y) == True+-- consistent ('Algebra.Graph.AdjacencyIntMap.edges' xs) == True+-- consistent ('Algebra.Graph.AdjacencyIntMap.stars' xs) == True+-- @+consistent :: AdjacencyIntMap -> Bool+consistent (AM m) = referredToVertexSet m `IntSet.isSubsetOf` keysSet m++-- The set of vertices that are referred to by the edges+referredToVertexSet :: IntMap IntSet -> IntSet+referredToVertexSet = IntSet.fromList . uncurry (++) . unzip . internalEdgeList++-- The list of edges in adjacency map+internalEdgeList :: IntMap IntSet -> [(Int, Int)]+internalEdgeList m = [ (x, y) | (x, ys) <- IntMap.toAscList m, y <- IntSet.toAscList ys ]
src/Algebra/Graph/AdjacencyMap.hs view
@@ -13,7 +13,7 @@ -- 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.--- "Algebra.Graph.IntAdjacencyMap" defines adjacency maps specialised to graphs+-- "Algebra.Graph.AdjacencyIntMap" defines adjacency maps specialised to graphs -- with @Int@ vertices. ----------------------------------------------------------------------------- module Algebra.Graph.AdjacencyMap (@@ -22,60 +22,42 @@ -- * Basic graph construction primitives empty, vertex, edge, overlay, connect, vertices, edges, overlays, connects,- fromAdjacencyList, -- * Relations on graphs isSubgraphOf, -- * Graph properties isEmpty, hasVertex, hasEdge, vertexCount, edgeCount, vertexList, edgeList,- adjacencyList, vertexSet, edgeSet, postSet,+ adjacencyList, vertexSet, vertexIntSet, edgeSet, preSet, postSet, -- * Standard families of graphs- path, circuit, clique, biclique, star, starTranspose, tree, forest,+ path, circuit, clique, biclique, star, stars, tree, forest, -- * Graph transformation- removeVertex, removeEdge, replaceVertex, mergeVertices, transpose, gmap, induce,+ removeVertex, removeEdge, replaceVertex, mergeVertices, transpose, gmap,+ induce, -- * Algorithms- dfsForest, dfsForestFrom, dfs, topSort, isTopSort, scc+ dfsForest, dfsForestFrom, dfs, reachable, topSort, isAcyclic, scc,++ -- * Correctness properties+ isDfsForestOf, isTopSortOf ) where -import Data.Foldable (toList)+import Control.Monad+import Data.Foldable (foldMap, toList) import Data.Maybe+import Data.Monoid import Data.Set (Set) import Data.Tree import Algebra.Graph.AdjacencyMap.Internal -import qualified Algebra.Graph.Class as C-import qualified Data.Graph as KL-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 :: Ord a => AdjacencyMap a-empty = C.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 :: Ord a => a -> AdjacencyMap a-vertex = C.vertex+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 -- | Construct the graph comprising /a single edge/. -- Complexity: /O(1)/ time, memory.@@ -88,45 +70,8 @@ -- 'vertexCount' (edge 1 2) == 2 -- @ edge :: Ord a => a -> a -> AdjacencyMap a-edge = C.edge---- | /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 = C.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 = C.connect+edge x y | x == y = AM $ Map.singleton x (Set.singleton y)+ | otherwise = AM $ Map.fromList [(x, Set.singleton y), (y, Set.empty)] -- | 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@@ -140,19 +85,20 @@ -- 'vertexSet' . vertices == Set.'Set.fromList' -- @ vertices :: Ord a => [a] -> AdjacencyMap a-vertices = mkAM . Map.fromList . map (\x -> (x, Set.empty))+vertices = AM . Map.fromList . map (\x -> (x, Set.empty))+{-# NOINLINE [1] vertices #-} -- | Construct the graph from a list of edges. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory. -- -- @ -- edges [] == 'empty'--- edges [(x, y)] == 'edge' x y+-- edges [(x,y)] == 'edge' x y -- 'edgeCount' . edges == 'length' . 'Data.List.nub' -- 'edgeList' . edges == 'Data.List.nub' . 'Data.List.sort' -- @ edges :: Ord a => [(a, a)] -> AdjacencyMap a-edges = fromAdjacencyList . map (fmap return)+edges = fromAdjacencySets . map (fmap Set.singleton) -- | Overlay a given list of graphs. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -165,7 +111,8 @@ -- 'isEmpty' . overlays == 'all' 'isEmpty' -- @ overlays :: Ord a => [AdjacencyMap a] -> AdjacencyMap a-overlays = C.overlays+overlays = AM . Map.unionsWith Set.union . map adjacencyMap+{-# NOINLINE overlays #-} -- | Connect a given list of graphs. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -178,24 +125,8 @@ -- 'isEmpty' . connects == 'all' 'isEmpty' -- @ connects :: Ord a => [AdjacencyMap a] -> AdjacencyMap a-connects = C.connects---- | Construct a graph from an adjacency list.--- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.------ @--- fromAdjacencyList [] == 'empty'--- fromAdjacencyList [(x, [])] == 'vertex' x--- fromAdjacencyList [(x, [y])] == 'edge' x y--- fromAdjacencyList . 'adjacencyList' == id--- 'overlay' (fromAdjacencyList xs) (fromAdjacencyList ys) == fromAdjacencyList (xs ++ ys)--- @-fromAdjacencyList :: Ord a => [(a, [a])] -> AdjacencyMap a-fromAdjacencyList as = mkAM $ Map.unionWith Set.union vs es- where- ss = map (fmap Set.fromList) as- vs = Map.fromSet (const Set.empty) . Set.unions $ map snd ss- es = Map.fromListWith Set.union ss+connects = foldr connect empty+{-# NOINLINE connects #-} -- | The 'isSubgraphOf' function takes two graphs and returns 'True' if the -- first graph is a /subgraph/ of the second.@@ -272,7 +203,7 @@ -- edgeCount == 'length' . 'edgeList' -- @ edgeCount :: AdjacencyMap a -> Int-edgeCount = Map.foldr (\es r -> (Set.size es + r)) 0 . adjacencyMap+edgeCount = getSum . foldMap (Sum . Set.size) . adjacencyMap -- | The sorted list of vertices of a given graph. -- Complexity: /O(n)/ time and memory.@@ -297,20 +228,7 @@ -- 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 ]---- | The sorted /adjacency list/ of a graph.--- Complexity: /O(n + m)/ time and /O(m)/ memory.------ @--- adjacencyList 'empty' == []--- adjacencyList ('vertex' x) == [(x, [])]--- adjacencyList ('edge' 1 2) == [(1, [2]), (2, [])]--- adjacencyList ('star' 2 [3,1]) == [(1, []), (2, [1,3]), (3, [])]--- 'fromAdjacencyList' . adjacencyList == id--- @-adjacencyList :: AdjacencyMap a -> [(a, [a])]-adjacencyList = map (fmap Set.toAscList) . Map.toAscList . adjacencyMap+edgeList (AM m) = [ (x, y) | (x, ys) <- Map.toAscList m, y <- Set.toAscList ys ] -- | The set of vertices of a given graph. -- Complexity: /O(n)/ time and memory.@@ -324,6 +242,19 @@ 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. --@@ -334,11 +265,39 @@ -- edgeSet . 'edges' == Set.'Set.fromList' -- @ edgeSet :: Ord a => AdjacencyMap a -> Set (a, a)-edgeSet = Map.foldrWithKey (\v es -> Set.union (Set.mapMonotonic (v,) es)) Set.empty . adjacencyMap+edgeSet = Set.fromAscList . edgeList --- | The /postset/ (here 'postSet') of a vertex is the set of its /direct successors/.+-- | The sorted /adjacency list/ of a graph.+-- Complexity: /O(n + m)/ time and /O(m)/ memory. -- -- @+-- adjacencyList 'empty' == []+-- adjacencyList ('vertex' x) == [(x, [])]+-- adjacencyList ('edge' 1 2) == [(1, [2]), (2, [])]+-- adjacencyList ('star' 2 [3,1]) == [(1, []), (2, [1,3]), (3, [])]+-- 'stars' . adjacencyList == id+-- @+adjacencyList :: AdjacencyMap a -> [(a, [a])]+adjacencyList = map (fmap Set.toAscList) . Map.toAscList . adjacencyMap++-- | 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' 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 = Set.fromAscList . map fst . filter p . Map.toAscList . adjacencyMap+ where+ p (_, set) = x `Set.member` set++-- | 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' x y) == Set.'Set.fromList' [y]@@ -357,7 +316,9 @@ -- path . 'reverse' == 'transpose' . path -- @ path :: Ord a => [a] -> AdjacencyMap a-path = C.path+path xs = case xs of [] -> empty+ [x] -> vertex x+ (_:ys) -> edges (zip xs ys) -- | The /circuit/ on a list of vertices. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -369,7 +330,8 @@ -- circuit . 'reverse' == 'transpose' . circuit -- @ circuit :: Ord a => [a] -> AdjacencyMap a-circuit = C.circuit+circuit [] = empty+circuit (x:xs) = path $ [x] ++ xs ++ [x] -- | The /clique/ on a list of vertices. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -383,7 +345,11 @@ -- clique . 'reverse' == 'transpose' . clique -- @ clique :: Ord a => [a] -> AdjacencyMap a-clique = C.clique+clique = fromAdjacencySets . fst . go+ where+ go [] = ([], Set.empty)+ go (x:xs) = let (res, set) = go xs in ((x, set) : res, Set.insert x set)+{-# NOINLINE [1] clique #-} -- | The /biclique/ on two lists of vertices. -- Complexity: /O(n * log(n) + m)/ time and /O(n + m)/ memory.@@ -396,14 +362,13 @@ -- biclique xs ys == 'connect' ('vertices' xs) ('vertices' ys) -- @ biclique :: Ord a => [a] -> [a] -> AdjacencyMap a-biclique xs ys = mkAM $ Map.fromSet adjacent (x `Set.union` y)+biclique xs ys = AM $ Map.fromSet adjacent (x `Set.union` y) where x = Set.fromList xs y = Set.fromList ys- adjacent v- | v `Set.member` x = y- | otherwise = Set.empty+ adjacent v = if v `Set.member` x then y else Set.empty +-- 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. --@@ -414,21 +379,26 @@ -- star x ys == 'connect' ('vertex' x) ('vertices' ys) -- @ star :: Ord a => a -> [a] -> AdjacencyMap a-star = C.star+star x [] = vertex x+star x ys = connect (vertex x) (vertices ys)+{-# INLINE star #-} --- | 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 * log(n))/ 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 :: Ord a => a -> [a] -> AdjacencyMap a-starTranspose = C.starTranspose+stars :: Ord a => [(a, [a])] -> AdjacencyMap a+stars = fromAdjacencySets . map (fmap Set.fromList) -- | The /tree graph/ constructed from a given 'Tree' data structure. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -440,7 +410,9 @@ -- tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == 'edges' [(1,2), (1,3), (3,4), (3,5)] -- @ tree :: Ord a => Tree a -> AdjacencyMap a-tree = C.tree+tree (Node x []) = vertex x+tree (Node x f ) = star x (map rootLabel f)+ `overlay` forest (filter (not . null . subForest) f) -- | The /forest graph/ constructed from a given 'Forest' data structure. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -452,7 +424,7 @@ -- forest == 'overlays' . map 'tree' -- @ forest :: Ord a => Forest a -> AdjacencyMap a-forest = C.forest+forest = overlays . map tree -- | Remove a vertex from a given graph. -- Complexity: /O(n*log(n))/ time.@@ -465,20 +437,20 @@ -- removeVertex x . removeVertex x == removeVertex x -- @ removeVertex :: Ord a => a -> AdjacencyMap a -> AdjacencyMap a-removeVertex x = mkAM . Map.map (Set.delete x) . Map.delete x . adjacencyMap+removeVertex x = AM . Map.map (Set.delete x) . Map.delete x . adjacencyMap -- | Remove an edge from a given graph. -- Complexity: /O(log(n))/ time. -- -- @--- removeEdge x y ('edge' x y) == 'vertices' [x, y]+-- removeEdge x y ('edge' 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 a -> AdjacencyMap a-removeEdge x y = mkAM . Map.adjust (Set.delete y) x . adjacencyMap+removeEdge x y = AM . Map.adjust (Set.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.@@ -516,11 +488,25 @@ -- 'edgeList' . transpose == 'Data.List.sort' . map 'Data.Tuple.swap' . 'edgeList' -- @ transpose :: Ord a => AdjacencyMap a -> AdjacencyMap a-transpose (AM m _) = mkAM $ Map.foldrWithKey combine vs m+transpose (AM m) = AM $ Map.foldrWithKey combine vs m where combine v es = Map.unionWith Set.union (Map.fromSet (const $ Set.singleton v) es) vs = Map.fromSet (const Set.empty) (Map.keysSet m)+{-# 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)+ #-}+ -- | 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'.@@ -534,7 +520,7 @@ -- gmap f . gmap g == gmap (f . g) -- @ gmap :: (Ord a, Ord b) => (a -> b) -> AdjacencyMap a -> AdjacencyMap b-gmap f = mkAM . Map.map (Set.map f) . Map.mapKeysWith Set.union f . adjacencyMap+gmap f = AM . Map.map (Set.map f) . Map.mapKeysWith Set.union f . adjacencyMap -- | Construct the /induced subgraph/ of a given graph by removing the -- vertices that do not satisfy a given predicate.@@ -548,16 +534,19 @@ -- induce p . induce q == induce (\\x -> p x && q x) -- 'isSubgraphOf' (induce p x) x == True -- @-induce :: Ord a => (a -> Bool) -> AdjacencyMap a -> AdjacencyMap a-induce p = mkAM . Map.map (Set.filter p) . Map.filterWithKey (\k _ -> p k) . adjacencyMap+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.+-- | 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]+-- '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@@ -568,79 +557,94 @@ -- , subForest = [ Node { rootLabel = 4 -- , subForest = [] }]}] -- @-dfsForest :: AdjacencyMap a -> Forest a-dfsForest (AM _ (GraphKL g r _)) = fmap (fmap r) (KL.dff g)+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. -- -- @--- '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--- 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 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 :: [a] -> AdjacencyMap a -> Forest a-dfsForestFrom vs (AM _ (GraphKL g r t)) = fmap (fmap r) (KL.dfs g (mapMaybe t vs))+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.+-- | 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 [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]+-- 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 :: [a] -> AdjacencyMap a -> [a]+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 'isTopSort' x) (topSort x) /= Just False+-- 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@(AM _ (GraphKL g r _)) =- if isTopSort result m then Just result else Nothing+topSort m = if isTopSortOf result m then Just result else Nothing where- result = map r (KL.topSort g)+ result = Typed.topSort (Typed.fromAdjacencyMap m) --- | Check if a given list of vertices is a valid /topological sort/ of a graph.+-- | Check if a given graph is /acyclic/. -- -- @--- isTopSort [3, 1, 2] (1 * 2 + 3 * 1) == True--- isTopSort [1, 2, 3] (1 * 2 + 3 * 1) == False--- isTopSort [] (1 * 2 + 3 * 1) == False--- isTopSort [] 'empty' == True--- isTopSort [x] ('vertex' x) == True--- isTopSort [x] ('edge' x x) == False+-- isAcyclic (1 * 2 + 3 * 1) == True+-- isAcyclic (1 * 2 + 2 * 1) == False+-- isAcyclic . 'circuit' == 'null'+-- isAcyclic == 'isJust' . 'topSort' -- @-isTopSort :: Ord a => [a] -> AdjacencyMap a -> Bool-isTopSort xs m = go Set.empty xs- where- go seen [] = seen == Map.keysSet (adjacencyMap m)- go seen (v:vs) = let newSeen = seen `seq` Set.insert v seen- in postSet v m `Set.intersection` newSeen == Set.empty && go newSeen vs+isAcyclic :: Ord a => AdjacencyMap a -> Bool+isAcyclic = isJust . topSort -- | Compute the /condensation/ of a graph, where each vertex corresponds to a -- /strongly-connected component/ of the original graph.@@ -656,8 +660,65 @@ -- , (Set.'Set.fromList' [3] , Set.'Set.fromList' [5] )] -- @ scc :: Ord a => AdjacencyMap a -> AdjacencyMap (Set a)-scc m@(AM _ (GraphKL g r _)) =- gmap (\v -> Map.findWithDefault Set.empty v components) m+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++-- | 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,19 +12,16 @@ ----------------------------------------------------------------------------- module Algebra.Graph.AdjacencyMap.Internal ( -- * Adjacency map implementation- AdjacencyMap (..), mkAM, consistent,-- -- * Interoperability with King-Launchbury graphs- GraphKL (..), mkGraphKL+ AdjacencyMap (..), empty, vertex, overlay, connect, fromAdjacencySets,+ consistent ) where import Data.List import Data.Map.Strict (Map, keysSet, fromSet) import Data.Set (Set) -import Algebra.Graph.Class+import Control.DeepSeq (NFData (..)) -import qualified Data.Graph as KL import qualified Data.Map.Strict as Map import qualified Data.Set as Set @@ -88,25 +85,20 @@ 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. -}-data AdjacencyMap a = AM {+newtype AdjacencyMap a = AM { -- | The /adjacency map/ of the graph: each vertex is associated with a set- -- of its direct successors.- adjacencyMap :: !(Map a (Set a)),- -- | Cached King-Launchbury representation.- -- /Note: this field is for internal use only/.- graphKL :: GraphKL a }---- | Construct an 'AdjacencyMap' from a map of successor sets and (lazily)--- compute the corresponding King-Launchbury representation.--- /Note: this function is for internal use only/.-mkAM :: Ord a => Map a (Set a) -> AdjacencyMap a-mkAM m = AM m (mkGraphKL m)--instance Eq a => Eq (AdjacencyMap a) where- x == y = adjacencyMap x == adjacencyMap y+ -- 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.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, Show a) => Show (AdjacencyMap a) where- show (AM m _)+ show (AM m) | null vs = "empty" | null es = vshow vs | vs == used = eshow es@@ -120,14 +112,73 @@ eshow xs = "edges " ++ show xs used = Set.toAscList (referredToVertexSet m) -instance Ord a => Graph (AdjacencyMap a) where- type Vertex (AdjacencyMap a) = a- empty = mkAM Map.empty- vertex x = mkAM $ Map.singleton x Set.empty- overlay x y = mkAM $ Map.unionWith Set.union (adjacencyMap x) (adjacencyMap y)- connect x y = mkAM $ Map.unionsWith Set.union [ adjacencyMap x, adjacencyMap y,- fromSet (const . keysSet $ adjacencyMap y) (keysSet $ adjacencyMap x) ]+-- | 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 #-}+ instance (Ord a, Num a) => Num (AdjacencyMap a) where fromInteger = vertex . fromInteger (+) = overlay@@ -136,27 +187,41 @@ abs = id negate = id -instance ToGraph (AdjacencyMap a) where- type ToVertex (AdjacencyMap a) = a- toGraph = overlays . map (uncurry star . fmap Set.toList) . Map.toList . adjacencyMap+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. -- /Note: this function is for internal use only/. -- -- @--- consistent 'Algebra.Graph.AdjacencyMap.empty' == True--- consistent ('Algebra.Graph.AdjacencyMap.vertex' x) == True--- consistent ('Algebra.Graph.AdjacencyMap.overlay' x y) == True--- consistent ('Algebra.Graph.AdjacencyMap.connect' x y) == True--- consistent ('Algebra.Graph.AdjacencyMap.edge' x y) == True--- consistent ('Algebra.Graph.AdjacencyMap.edges' xs) == True--- consistent ('Algebra.Graph.AdjacencyMap.graph' xs ys) == True--- consistent ('Algebra.Graph.AdjacencyMap.fromAdjacencyList' xs) == True+-- consistent 'Algebra.Graph.AdjacencyMap.empty' == True+-- consistent ('Algebra.Graph.AdjacencyMap.vertex' x) == True+-- consistent ('Algebra.Graph.AdjacencyMap.overlay' x y) == True+-- consistent ('Algebra.Graph.AdjacencyMap.connect' x y) == True+-- consistent ('Algebra.Graph.AdjacencyMap.edge' x y) == True+-- consistent ('Algebra.Graph.AdjacencyMap.edges' xs) == True+-- consistent ('Algebra.Graph.AdjacencyMap.stars' xs) == True -- @ consistent :: Ord a => AdjacencyMap a -> Bool-consistent (AM m _) = referredToVertexSet m `Set.isSubsetOf` keysSet m+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@@ -165,32 +230,3 @@ -- The list of edges in adjacency map internalEdgeList :: Map a (Set a) -> [(a, a)] internalEdgeList m = [ (x, y) | (x, ys) <- Map.toAscList m, y <- Set.toAscList ys ]---- | 'GraphKL' encapsulates King-Launchbury graphs, which are implemented in--- the "Data.Graph" module of the @containers@ library.--- /Note: this data structure is for internal use only/.------ If @mkGraphKL (adjacencyMap g) == h@ then the following holds:------ @--- map ('fromVertexKL' h) ('Data.Graph.vertices' $ 'toGraphKL' h) == 'Algebra.Graph.AdjacencyMap.vertexList' g--- map (\\(x, y) -> ('fromVertexKL' h x, 'fromVertexKL' h y)) ('Data.Graph.edges' $ 'toGraphKL' h) == 'Algebra.Graph.AdjacencyMap.edgeList' g--- @-data GraphKL a = GraphKL {- -- | Array-based graph representation (King and Launchbury, 1995).- toGraphKL :: KL.Graph,- -- | A mapping of "Data.Graph.Vertex" to vertices of type @a@.- fromVertexKL :: KL.Vertex -> a,- -- | A mapping from vertices of type @a@ to "Data.Graph.Vertex".- -- Returns 'Nothing' if the argument is not in the graph.- toVertexKL :: a -> Maybe KL.Vertex }---- | Build 'GraphKL' from a map of successor sets.--- /Note: this function is for internal use only/.-mkGraphKL :: Ord a => Map a (Set a) -> GraphKL a-mkGraphKL m = GraphKL- { toGraphKL = g- , fromVertexKL = \u -> case r u of (_, v, _) -> v- , toVertexKL = t }- where- (g, r, t) = KL.graphFromEdges [ ((), v, Set.toAscList us) | (v, us) <- Map.toAscList m ]
src/Algebra/Graph/Class.hs view
@@ -44,10 +44,7 @@ isSubgraphOf, -- * Standard families of graphs- path, circuit, clique, biclique, star, starTranspose, tree, forest,-- -- * Conversion between graph data types- ToGraph (..)+ path, circuit, clique, biclique, star, starTranspose, tree, forest ) where import Prelude ()@@ -55,6 +52,12 @@ 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+ {-| The core type class for constructing algebraic graphs, characterised by the following minimal set of axioms. In equations we use @+@ and @*@ as convenient@@ -114,6 +117,41 @@ -- | Connect two graphs. connect :: g -> g -> g +instance Graph (G.Graph a) where+ type Vertex (G.Graph a) = a+ empty = G.empty+ vertex = G.vertex+ overlay = G.overlay+ connect = G.connect++instance Ord a => Graph (AM.AdjacencyMap a) where+ type Vertex (AM.AdjacencyMap a) = a+ empty = AM.empty+ vertex = AM.vertex+ overlay = AM.overlay+ connect = AM.connect++instance Graph (F.Fold a) where+ type Vertex (F.Fold a) = a+ empty = F.empty+ vertex = F.vertex+ overlay = F.overlay+ connect = F.connect++instance Graph AIM.AdjacencyIntMap where+ type Vertex AIM.AdjacencyIntMap = Int+ empty = AIM.empty+ vertex = AIM.vertex+ overlay = AIM.overlay+ connect = AIM.connect++instance Ord a => Graph (R.Relation a) where+ type Vertex (R.Relation a) = a+ empty = R.empty+ vertex = R.vertex+ overlay = R.overlay+ connect = R.connect+ {-| The class of /undirected graphs/ that satisfy the following additional axiom. @@ -395,7 +433,7 @@ tree :: Graph g => Tree (Vertex g) -> g tree (Node x []) = vertex x tree (Node x f ) = star x (map rootLabel f)- `overlay` forest (filter (not . null . subForest) f)+ `overlay` forest (filter (not . null . subForest) f) -- | The /forest graph/ constructed from a given 'Forest' data structure. -- Complexity: /O(F)/ time, memory and size, where /F/ is the size of the@@ -409,45 +447,3 @@ -- @ forest :: Graph g => Forest (Vertex g) -> g forest = overlays . map tree---- | The 'ToGraph' type class captures data types that can be converted to--- polymorphic graph expressions. The conversion method 'toGraph' semantically--- acts as the identity on graph data structures, but allows to convert graphs--- between different data representations.------ @--- toGraph (g :: 'Algebra.Graph.Graph' a ) :: 'Algebra.Graph.Graph' a == g--- 'show' (toGraph (1 * 2 :: 'Algebra.Graph.Graph' Int) :: 'Algebra.Graph.Relation' Int) == "edge 1 2"--- @------ The second method 'foldg' is used for generalised graph folding. It recursively--- collapses a given data type by applying the provided graph construction--- primitives. The order of arguments is: empty, vertex, overlay and connect,--- and it is assumed that the functions satisfy the axioms of the algebra.--- The following law establishes the relation between 'toGraph' and 'foldg':------ @--- toGraph == foldg 'empty' 'vertex' 'overlay' 'connect'--- @-class ToGraph t where- type ToVertex t- toGraph :: (Graph g, Vertex g ~ ToVertex t) => t -> g- toGraph = foldg empty vertex overlay connect- foldg :: r -> (ToVertex t -> r) -> (r -> r -> r) -> (r -> r -> r) -> t -> r- foldg e v o c = go . toGraph- where- go E = e- go (V x ) = v x- go (O x y) = o (go x) (go y)- go (C x y) = c (go x) (go y)---- TODO: Get rid of code duplication. Note: we do not use the data type Graph--- here due to import cycle.-data G a = E | V a | O (G a) (G a) | C (G a) (G a)--instance Graph (G a) where- type Vertex (G a) = a- empty = E- vertex = V- overlay = O- connect = C
src/Algebra/Graph/Export.hs view
@@ -32,8 +32,8 @@ import Data.Semigroup import Data.String hiding (unlines) -import Algebra.Graph.AdjacencyMap-import Algebra.Graph.Class (ToGraph (..))+import Algebra.Graph.ToGraph (ToGraph, ToVertex, toAdjacencyMap)+import Algebra.Graph.AdjacencyMap (vertexList, edgeList) import Algebra.Graph.Internal -- | An abstract document data type with /O(1)/ time concatenation (the current@@ -159,8 +159,8 @@ -- 2 -> 4 -- @ export :: (Ord a, ToGraph g, ToVertex g ~ a) => (a -> Doc s) -> (a -> a -> Doc s) -> g -> Doc s-export vs es g = vDoc <> eDoc+export v e g = vDoc <> eDoc where- vDoc = mconcat $ map vs (vertexList adjMap)- eDoc = mconcat $ map (uncurry es) (edgeList adjMap)- adjMap = toGraph g+ vDoc = mconcat $ map v (vertexList adjMap)+ eDoc = mconcat $ map (uncurry e) (edgeList adjMap)+ adjMap = toAdjacencyMap g
src/Algebra/Graph/Export/Dot.hs view
@@ -26,7 +26,7 @@ import Data.String hiding (unlines) import Prelude hiding (unlines) -import Algebra.Graph.Class (ToGraph (..))+import Algebra.Graph.ToGraph (ToGraph (..)) import Algebra.Graph.Export hiding (export) import qualified Algebra.Graph.Export as E @@ -124,7 +124,7 @@ vDoc x = line $ label x <+> attributes (vertexAttributes x) eDoc x y = line $ label x <> " -> " <> label y <+> attributes (edgeAttributes x y) --- A list of attributes formatted as a DOT document.+-- | A list of attributes formatted as a DOT document. -- Example: @attributes ["label" := "A label", "shape" := "box"]@ -- corresponds to document: @ [label="A label" shape="box"]@. attributes :: IsString s => [Attribute s] -> Doc s
src/Algebra/Graph/Fold.hs view
@@ -29,40 +29,37 @@ foldg, -- * Relations on graphs- C.isSubgraphOf,+ isSubgraphOf, -- * Graph properties isEmpty, size, hasVertex, hasEdge, vertexCount, edgeCount, vertexList,- edgeList, vertexSet, vertexIntSet, edgeSet,+ edgeList, vertexSet, vertexIntSet, edgeSet, adjacencyList, -- * Standard families of graphs- C.path, C.circuit, C.clique, C.biclique, C.star, C.starTranspose, C.tree,- C.forest, mesh, torus, deBruijn,+ path, circuit, clique, biclique, star, stars, -- * Graph transformation- removeVertex, removeEdge, replaceVertex, mergeVertices, splitVertex,- transpose, gmap, bind, induce, simplify,-- -- * Graph composition- box+ removeVertex, removeEdge, transpose, induce, simplify, ) where import Prelude () import Prelude.Compat -import Control.Applicative hiding (empty)+import Control.Applicative (Alternative, liftA2) import Control.Monad.Compat (MonadPlus (..), ap)-import Data.Foldable+import Data.Function -import Algebra.Graph.Internal+import Control.DeepSeq (NFData (..)) -import qualified Algebra.Graph.AdjacencyMap as AM-import qualified Algebra.Graph.Class as C-import qualified Algebra.Graph.HigherKinded.Class as H-import qualified Algebra.Graph.Relation as R-import qualified Data.IntSet as IntSet-import qualified Data.Set as Set+import Algebra.Graph.ToGraph (ToGraph, ToVertex, toGraph) +import qualified Algebra.Graph as G+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 construction primitives 'empty', 'vertex', 'overlay' and 'connect'. We define a 'Num' instance as a convenient notation for working with graphs:@@ -150,17 +147,13 @@ 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 f = show (C.toGraph f :: AM.AdjacencyMap a)+ show = show . foldg AM.empty AM.vertex AM.overlay AM.connect instance Ord a => Eq (Fold a) where- x == y = C.toGraph x == (C.toGraph y :: AM.AdjacencyMap a)+ x == y = T.adjacencyMap x == T.adjacencyMap y -instance C.Graph (Fold a) where- type Vertex (Fold a) = a- empty = Fold $ \e _ _ _ -> e- vertex x = Fold $ \_ v _ _ -> v x- overlay x y = Fold $ \e v o c -> runFold x e v o c `o` runFold y e v o c- connect x y = Fold $ \e v o c -> runFold x e v o c `c` runFold y e v o c+instance NFData a => NFData (Fold a) where+ rnf = foldg () rnf seq seq instance Num a => Num (Fold a) where fromInteger = vertex . fromInteger@@ -171,7 +164,7 @@ negate = id instance Functor Fold where- fmap = gmap+ fmap f = foldg empty (vertex . f) overlay connect instance Applicative Fold where pure = vertex@@ -187,10 +180,7 @@ instance Monad Fold where return = vertex- (>>=) = bind--instance H.Graph Fold where- connect = connect+ g >>=f = foldg empty f overlay connect g instance Foldable Fold where foldMap f = foldg mempty f mappend mappend@@ -198,12 +188,9 @@ instance Traversable Fold where traverse f = foldg (pure empty) (fmap vertex . f) (liftA2 overlay) (liftA2 connect) -instance C.ToGraph (Fold a) where+instance ToGraph (Fold a) where type ToVertex (Fold a) = a- foldg e v o c g = runFold g e v o c--instance H.ToGraph Fold where- toGraph = foldg H.empty H.vertex H.overlay H.connect+ foldg = foldg -- | Construct the /empty graph/. -- Complexity: /O(1)/ time, memory and size.@@ -215,8 +202,9 @@ -- 'edgeCount' empty == 0 -- 'size' empty == 1 -- @-empty :: C.Graph g => g-empty = C.empty+empty :: Fold a+empty = Fold $ \e _ _ _ -> e+{-# NOINLINE [1] empty #-} -- | Construct the graph comprising /a single isolated vertex/. -- Complexity: /O(1)/ time, memory and size.@@ -228,8 +216,9 @@ -- 'edgeCount' (vertex x) == 0 -- 'size' (vertex x) == 1 -- @-vertex :: C.Graph g => C.Vertex g -> g-vertex = C.vertex+vertex :: a -> Fold a+vertex x = Fold $ \_ v _ _ -> v x+{-# NOINLINE [1] vertex #-} -- | Construct the graph comprising /a single edge/. -- Complexity: /O(1)/ time, memory and size.@@ -241,8 +230,8 @@ -- 'vertexCount' (edge 1 1) == 1 -- 'vertexCount' (edge 1 2) == 2 -- @-edge :: C.Graph g => C.Vertex g -> C.Vertex g -> g-edge = C.edge+edge :: a -> a -> Fold a+edge x y = Fold $ \_ v _ c -> v x `c` v y -- | /Overlay/ two graphs. This is a commutative, associative and idempotent -- operation with the identity 'empty'.@@ -259,8 +248,9 @@ -- 'vertexCount' (overlay 1 2) == 2 -- 'edgeCount' (overlay 1 2) == 0 -- @-overlay :: C.Graph g => g -> g -> g-overlay = C.overlay+overlay :: Fold a -> Fold a -> Fold a+overlay x y = Fold $ \e v o c -> runFold x e v o c `o` runFold y e v o c+{-# NOINLINE [1] overlay #-} -- | /Connect/ two graphs. This is an associative operation with the identity -- 'empty', which distributes over 'overlay' and obeys the decomposition axiom.@@ -281,8 +271,9 @@ -- 'vertexCount' (connect 1 2) == 2 -- 'edgeCount' (connect 1 2) == 1 -- @-connect :: C.Graph g => g -> g -> g-connect = C.connect+connect :: Fold a -> Fold a -> Fold a+connect x y = Fold $ \e v o c -> runFold x e v o c `c` runFold y e v o c+{-# NOINLINE [1] connect #-} -- | Construct the graph comprising a given list of isolated vertices. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the@@ -295,8 +286,9 @@ -- 'vertexCount' . vertices == 'length' . 'Data.List.nub' -- 'vertexSet' . vertices == Set.'Set.fromList' -- @-vertices :: C.Graph g => [C.Vertex g] -> g-vertices = C.vertices+vertices :: [a] -> Fold a+vertices = overlays . map vertex+{-# NOINLINE [1] vertices #-} -- | Construct the graph from a list of edges. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the@@ -307,8 +299,8 @@ -- edges [(x,y)] == 'edge' x y -- 'edgeCount' . edges == 'length' . 'Data.List.nub' -- @-edges :: C.Graph g => [(C.Vertex g, C.Vertex g)] -> g-edges = C.edges+edges :: [(a, a)] -> Fold a+edges es = Fold $ \e v o c -> foldr (flip o . uncurry (c `on` v)) e es -- | Overlay a given list of graphs. -- Complexity: /O(L)/ time and memory, and /O(S)/ size, where /L/ is the length@@ -321,8 +313,9 @@ -- overlays == 'foldr' 'overlay' 'empty' -- 'isEmpty' . overlays == 'all' 'isEmpty' -- @-overlays :: C.Graph g => [g] -> g-overlays = C.overlays+overlays :: [Fold a] -> Fold a+overlays = foldr overlay empty+{-# INLINE [2] overlays #-} -- | Connect a given list of graphs. -- Complexity: /O(L)/ time and memory, and /O(S)/ size, where /L/ is the length@@ -335,10 +328,11 @@ -- connects == 'foldr' 'connect' 'empty' -- 'isEmpty' . connects == 'all' 'isEmpty' -- @-connects :: C.Graph g => [g] -> g-connects = C.connects+connects :: [Fold a] -> Fold a+connects = foldr connect empty+{-# INLINE [2] connects #-} --- | Generalised graph folding: recursively collapse a 'Fold' by applying+-- | 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, overlay and connect. -- Complexity: /O(s)/ applications of given functions. As an example, the@@ -353,8 +347,23 @@ -- foldg True (const False) (&&) (&&) == 'isEmpty' -- @ foldg :: b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Fold a -> b-foldg = C.foldg+foldg e v o c g = runFold g e v o c +-- | 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 :: Ord a => Fold a -> Fold a -> Bool+isSubgraphOf x y = overlay x y == y+ -- | Check if a graph is empty. A convenient alias for 'null'. -- Complexity: /O(s)/ time. --@@ -366,7 +375,7 @@ -- isEmpty ('removeEdge' x y $ 'edge' x y) == False -- @ isEmpty :: Fold a -> Bool-isEmpty = H.isEmpty+isEmpty = T.isEmpty -- | The /size/ of a graph, i.e. the number of leaves of the expression -- including 'empty' leaves.@@ -381,7 +390,7 @@ -- size x >= 'vertexCount' x -- @ size :: Fold a -> Int-size = foldg 1 (const 1) (+) (+)+size = T.size -- | Check if a graph contains a given vertex. A convenient alias for `elem`. -- Complexity: /O(s)/ time.@@ -393,7 +402,7 @@ -- hasVertex x . 'removeVertex' x == const False -- @ hasVertex :: Eq a => a -> Fold a -> Bool-hasVertex = H.hasVertex+hasVertex = T.hasVertex -- | Check if a graph contains a given edge. -- Complexity: /O(s)/ time.@@ -405,8 +414,8 @@ -- hasEdge x y . 'removeEdge' x y == const False -- hasEdge x y == 'elem' (x,y) . 'edgeList' -- @-hasEdge :: Ord a => a -> a -> Fold a -> Bool-hasEdge = H.hasEdge+hasEdge :: Eq a => a -> a -> Fold a -> Bool+hasEdge = T.hasEdge -- | The number of vertices in a graph. -- Complexity: /O(s * log(n))/ time.@@ -417,7 +426,7 @@ -- vertexCount == 'length' . 'vertexList' -- @ vertexCount :: Ord a => Fold a -> Int-vertexCount = length . vertexList+vertexCount = T.vertexCount -- | The number of edges in a graph. -- Complexity: /O(s + m * log(m))/ time. Note that the number of edges /m/ of a@@ -430,7 +439,7 @@ -- edgeCount == 'length' . 'edgeList' -- @ edgeCount :: Ord a => Fold a -> Int-edgeCount = length . edgeList+edgeCount = T.edgeCount -- | The sorted list of vertices of a given graph. -- Complexity: /O(s * log(n))/ time and /O(n)/ memory.@@ -441,7 +450,7 @@ -- vertexList . 'vertices' == 'Data.List.nub' . 'Data.List.sort' -- @ vertexList :: Ord a => Fold a -> [a]-vertexList = Set.toAscList . vertexSet+vertexList = T.vertexList -- | The sorted list of edges of a graph. -- Complexity: /O(s + m * log(m))/ time and /O(m)/ memory. Note that the number of@@ -456,7 +465,7 @@ -- edgeList . 'transpose' == 'Data.List.sort' . map 'Data.Tuple.swap' . edgeList -- @ edgeList :: Ord a => Fold a -> [(a, a)]-edgeList = AM.edgeList . C.toGraph+edgeList = T.edgeList -- | The set of vertices of a given graph. -- Complexity: /O(s * log(n))/ time and /O(n)/ memory.@@ -468,7 +477,7 @@ -- vertexSet . 'clique' == Set.'Set.fromList' -- @ vertexSet :: Ord a => Fold a -> Set.Set a-vertexSet = H.vertexSet+vertexSet = T.vertexSet -- | The set of vertices of a given graph. Like 'vertexSet' but specialised for -- graphs with vertices of type 'Int'.@@ -481,7 +490,7 @@ -- vertexIntSet . 'clique' == IntSet.'IntSet.fromList' -- @ vertexIntSet :: Fold Int -> IntSet.IntSet-vertexIntSet = H.vertexIntSet+vertexIntSet = T.vertexIntSet -- | The set of edges of a given graph. -- Complexity: /O(s * log(m))/ time and /O(m)/ memory.@@ -493,62 +502,116 @@ -- edgeSet . 'edges' == Set.'Set.fromList' -- @ edgeSet :: Ord a => Fold a -> Set.Set (a, a)-edgeSet = R.edgeSet . C.toGraph+edgeSet = T.edgeSet --- | Construct a /mesh graph/ from two lists of vertices.--- Complexity: /O(L1 * L2)/ time, memory and size, where /L1/ and /L2/ are the--- lengths of the given lists.+-- | The sorted /adjacency list/ of a graph.+-- 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/. -- -- @--- mesh xs [] == 'empty'--- mesh [] ys == 'empty'--- mesh [x] [y] == 'vertex' (x, y)--- mesh xs ys == 'box' ('path' xs) ('path' ys)--- mesh [1..3] "ab" == 'edges' [ ((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\')) ]+-- adjacencyList 'empty' == []+-- adjacencyList ('vertex' x) == [(x, [])]+-- adjacencyList ('edge' 1 2) == [(1, [2]), (2, [])]+-- adjacencyList ('star' 2 [3,1]) == [(1, []), (2, [1,3]), (3, [])]+-- 'stars' . adjacencyList == id -- @-mesh :: (C.Graph g, C.Vertex g ~ (a, b)) => [a] -> [b] -> g-mesh xs ys = C.path xs `box` C.path ys+adjacencyList :: Ord a => Fold a -> [(a, [a])]+adjacencyList = T.adjacencyList --- | Construct a /torus graph/ from two lists of vertices.--- Complexity: /O(L1 * L2)/ time, memory and size, where /L1/ and /L2/ are the+-- | The /path/ on a list of vertices.+-- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the+-- given list.+--+-- @+-- path [] == 'empty'+-- path [x] == 'vertex' x+-- path [x,y] == 'edge' x y+-- path . 'reverse' == 'transpose' . path+-- @+path :: [a] -> Fold a+path xs = case xs of [] -> empty+ [x] -> vertex x+ (_:ys) -> edges (zip xs ys)++-- | The /circuit/ on a list of vertices.+-- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the+-- given list.+--+-- @+-- circuit [] == 'empty'+-- circuit [x] == 'edge' x x+-- circuit [x,y] == 'edges' [(x,y), (y,x)]+-- circuit . 'reverse' == 'transpose' . circuit+-- @+circuit :: [a] -> Fold a+circuit [] = empty+circuit (x:xs) = path $ [x] ++ xs ++ [x]++-- | The /clique/ on a list of vertices.+-- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the+-- given list.+--+-- @+-- clique [] == 'empty'+-- clique [x] == 'vertex' x+-- clique [x,y] == 'edge' x y+-- clique [x,y,z] == 'edges' [(x,y), (x,z), (y,z)]+-- clique (xs ++ ys) == 'connect' (clique xs) (clique ys)+-- clique . 'reverse' == 'transpose' . clique+-- @+clique :: [a] -> Fold a+clique = connects . map vertex+{-# NOINLINE [1] clique #-}++-- | The /biclique/ on two lists of vertices.+-- Complexity: /O(L1 + L2)/ time, memory and size, where /L1/ and /L2/ are the -- lengths of the given lists. -- -- @--- torus xs [] == 'empty'--- torus [] ys == 'empty'--- torus [x] [y] == 'edge' (x, y) (x, y)--- torus xs ys == 'box' ('circuit' xs) ('circuit' ys)--- torus [1,2] "ab" == 'edges' [ ((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\')) ]+-- biclique [] [] == 'empty'+-- biclique [x] [] == 'vertex' x+-- biclique [] [y] == 'vertex' y+-- biclique [x1,x2] [y1,y2] == 'edges' [(x1,y1), (x1,y2), (x2,y1), (x2,y2)]+-- biclique xs ys == 'connect' ('vertices' xs) ('vertices' ys) -- @-torus :: (C.Graph g, C.Vertex g ~ (a, b)) => [a] -> [b] -> g-torus xs ys = C.circuit xs `box` C.circuit ys+biclique :: [a] -> [a] -> Fold a+biclique xs [] = vertices xs+biclique [] ys = vertices ys+biclique xs ys = connect (vertices xs) (vertices ys) --- | 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--- alphabet and /D/ is the dimension of the graph.+-- | The /star/ formed by a centre vertex connected to a list of leaves.+-- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the+-- given list. -- -- @--- deBruijn 0 xs == 'edge' [] []--- n > 0 ==> deBruijn n [] == 'empty'--- deBruijn 1 [0,1] == 'edges' [ ([0],[0]), ([0],[1]), ([1],[0]), ([1],[1]) ]--- deBruijn 2 "0" == 'edge' "00" "00"--- deBruijn 2 "01" == 'edges' [ ("00","00"), ("00","01"), ("01","10"), ("01","11")--- , ("10","00"), ("10","01"), ("11","10"), ("11","11") ]--- 'transpose' (deBruijn n xs) == 'gmap' 'reverse' $ deBruijn n xs--- 'vertexCount' (deBruijn n xs) == ('length' $ 'Data.List.nub' xs)^n--- n > 0 ==> 'edgeCount' (deBruijn n xs) == ('length' $ 'Data.List.nub' xs)^(n + 1)+-- star x [] == 'vertex' x+-- star x [y] == 'edge' x y+-- star x [y,z] == 'edges' [(x,y), (x,z)]+-- star x ys == 'connect' ('vertex' x) ('vertices' ys) -- @-deBruijn :: (C.Graph g, C.Vertex g ~ [a]) => Int -> [a] -> g-deBruijn 0 _ = edge [] []-deBruijn len alphabet = bind skeleton expand- where- overlaps = mapM (const alphabet) [2..len]- skeleton = C.edges [ (Left s, Right s) | s <- overlaps ]- expand v = C.vertices [ either ([a] ++) (++ [a]) v | a <- alphabet ]+star :: a -> [a] -> Fold a+star x [] = vertex x+star x ys = connect (vertex x) (vertices ys)+{-# INLINE star #-} +-- | 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.+--+-- @+-- stars [] == 'empty'+-- stars [(x, [])] == 'vertex' x+-- stars [(x, [y])] == 'edge' x y+-- stars [(x, ys)] == 'star' x ys+-- stars == 'overlays' . map (uncurry 'star')+-- stars . 'adjacencyList' == id+-- 'overlay' (stars xs) (stars ys) == stars (xs ++ ys)+-- @+stars :: [(a, [a])] -> Fold a+stars = overlays . map (uncurry star)+{-# INLINE stars #-}+ -- | Remove a vertex from a given graph. -- Complexity: /O(s)/ time, memory and size. --@@ -559,71 +622,30 @@ -- removeVertex 1 ('edge' 1 2) == 'vertex' 2 -- removeVertex x . removeVertex x == removeVertex x -- @-removeVertex :: (Eq (C.Vertex g), C.Graph g) => C.Vertex g -> Fold (C.Vertex g) -> g+removeVertex :: Eq a => a -> Fold a -> Fold a removeVertex v = induce (/= v) -- | Remove an edge from a given graph. -- Complexity: /O(s)/ time, memory and size. -- -- @--- removeEdge x y ('edge' x y) == 'vertices' [x, y]+-- removeEdge x y ('edge' 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 -- 'size' (removeEdge x y z) <= 3 * 'size' z -- @-removeEdge :: (Eq (C.Vertex g), C.Graph g) => C.Vertex g -> C.Vertex g -> Fold (C.Vertex g) -> g+removeEdge :: Eq a => a -> a -> Fold a -> Fold a removeEdge s t = filterContext s (/=s) (/=t) -- TODO: Export -- | Filter vertices in a subgraph context.-filterContext :: (Eq (C.Vertex g), C.Graph g) => C.Vertex g -> (C.Vertex g -> Bool)- -> (C.Vertex g -> Bool) -> Fold (C.Vertex g) -> g-filterContext s i o g = maybe (C.toGraph g) go $ context (==s) g+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 (Context is os) = overlays [ induce (/=s) g- , C.starTranspose s (filter i is)- , C.star s (filter o os) ]---- | The function @'replaceVertex' x y@ replaces vertex @x@ with vertex @y@ in a--- given graph expression. 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 == 'mergeVertices' (== x) y--- @-replaceVertex :: (Eq (C.Vertex g), C.Graph g) => C.Vertex g -> C.Vertex g -> Fold (C.Vertex g) -> g-replaceVertex u v = gmap $ \w -> if w == u then v else w---- | 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 (== x) y == 'replaceVertex' x y--- mergeVertices even 1 (0 * 2) == 1 * 1--- mergeVertices odd 1 (3 + 4 * 5) == 4 * 1--- @-mergeVertices :: C.Graph g => (C.Vertex g -> Bool) -> C.Vertex g -> Fold (C.Vertex g) -> g-mergeVertices p v = gmap $ \u -> if p u then v else u---- | Split a vertex into a list of vertices with the same connectivity.--- Complexity: /O(s + k * L)/ time, memory and size, where /k/ is the number of--- occurrences of the vertex in the expression and /L/ is the length of the--- given list.------ @--- splitVertex x [] == 'removeVertex' x--- splitVertex x [x] == id--- splitVertex x [y] == 'replaceVertex' x y--- splitVertex 1 [0,1] $ 1 * (2 + 3) == (0 + 1) * (2 + 3)--- @-splitVertex :: (Eq (C.Vertex g), C.Graph g) => C.Vertex g -> [C.Vertex g] -> Fold (C.Vertex g) -> g-splitVertex v vs g = bind g $ \u -> if u == v then C.vertices vs else C.vertex u+ go (G.Context is os) = induce (/=s) g `overlay` transpose (star s (filter i is))+ `overlay` star s (filter o os) -- | Transpose a given graph. -- Complexity: /O(s)/ time, memory and size.@@ -636,38 +658,23 @@ -- transpose ('box' x y) == 'box' (transpose x) (transpose y) -- 'edgeList' . transpose == 'Data.List.sort' . map 'Data.Tuple.swap' . 'edgeList' -- @-transpose :: C.Graph g => Fold (C.Vertex g) -> g-transpose = foldg C.empty C.vertex C.overlay (flip C.connect)+transpose :: Fold a -> Fold a+transpose = foldg empty vertex overlay (flip connect)+{-# NOINLINE [1] transpose #-} --- | Transform a given graph by applying a function to each of its vertices.--- This is similar to 'fmap' but can be used with non-fully-parametric graphs.------ @--- gmap f 'empty' == 'empty'--- gmap f ('vertex' x) == 'vertex' (f x)--- gmap f ('edge' x y) == 'edge' (f x) (f y)--- gmap id == id--- gmap f . gmap g == gmap (f . g)--- @-gmap :: C.Graph g => (a -> C.Vertex g) -> Fold a -> g-gmap f = foldg C.empty (C.vertex . f) C.overlay C.connect+{-# 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) --- | Transform a given graph by substituting each of its vertices with a subgraph.--- This is similar to Monad's bind '>>=' but can be used with non-fully-parametric--- graphs.------ @--- bind 'empty' f == 'empty'--- bind ('vertex' x) f == f x--- bind ('edge' x y) f == 'connect' (f x) (f y)--- bind ('vertices' xs) f == 'overlays' ('map' f xs)--- bind x (const 'empty') == 'empty'--- bind x 'vertex' == x--- bind (bind x f) g == bind x (\\y -> bind (f y) g)--- @-bind :: C.Graph g => Fold a -> (a -> g) -> g-bind g f = foldg C.empty f C.overlay C.connect g+"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)+ #-}+ -- | 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@@ -680,8 +687,8 @@ -- induce p . induce q == induce (\\x -> p x && q x) -- 'isSubgraphOf' (induce p x) x == True -- @-induce :: C.Graph g => (C.Vertex g -> Bool) -> Fold (C.Vertex g) -> g-induce p = C.toGraph . foldg empty (\x -> if p x then vertex x else empty) (k overlay) (k connect)+induce :: (a -> Bool) -> Fold a -> Fold a+induce p = foldg empty (\x -> if p x then vertex x else empty) (k overlay) (k connect) where k f x y | isEmpty x = y -- Constant folding to get rid of Empty leaves | isEmpty y = x@@ -704,8 +711,8 @@ -- simplify (1 + 2 + 1) ~> 1 + 2 -- simplify (1 * 1 * 1) ~> 1 * 1 -- @-simplify :: (Eq g, C.Graph g) => Fold (C.Vertex g) -> g-simplify = foldg C.empty C.vertex (simple C.overlay) (simple C.connect)+simplify :: Ord a => Fold a -> Fold a+simplify = foldg empty vertex (simple overlay) (simple connect) simple :: Eq g => (g -> g -> g) -> g -> g -> g simple op x y@@ -714,34 +721,3 @@ | otherwise = z where z = op x 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.------ @--- 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'--- 'transpose' (box x y) == box ('transpose' x) ('transpose' y)--- 'vertexCount' (box x y) == 'vertexCount' x * 'vertexCount' y--- 'edgeCount' (box x y) <= 'vertexCount' x * 'edgeCount' y + 'edgeCount' x * 'vertexCount' y--- @-box :: (C.Graph g, C.Vertex g ~ (a, b)) => Fold a -> Fold b -> g-box x y = C.overlays $ xs ++ ys- where- xs = map (\b -> gmap (,b) x) $ toList y- ys = map (\a -> gmap (a,) y) $ toList x
src/Algebra/Graph/HigherKinded/Class.hs view
@@ -53,11 +53,7 @@ removeVertex, replaceVertex, mergeVertices, splitVertex, induce, -- * Graph composition- box,-- -- * Conversion between graph data types- ToGraph (..)-+ box ) where import Prelude ()@@ -68,8 +64,10 @@ import Data.Foldable (toList) import Data.Tree -import qualified Data.IntSet as IntSet-import qualified Data.Set as Set+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@@ -138,6 +136,12 @@ -- | Connect two graphs. connect :: g a -> g a -> g a +instance Graph G.Graph where+ connect = G.connect++instance Graph F.Fold where+ connect = F.connect+ -- | Construct the graph comprising a single isolated vertex. An alias for 'pure'. vertex :: Graph g => a -> g a vertex = pure@@ -212,7 +216,9 @@ -- 'vertexSet' . vertices == Set.'Set.fromList' -- @ vertices :: Graph g => [a] -> g a-vertices = overlays . map vertex+vertices [] = empty+vertices [x] = vertex x+vertices (x:xs) = vertex x `overlay` vertices xs -- | Construct the graph from a list of edges. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the@@ -495,7 +501,7 @@ -- @ -- torus xs [] == 'empty' -- torus [] ys == 'empty'--- torus [x] [y] == 'edge' (x, y) (x, y)+-- torus [x] [y] == 'edge' (x,y) (x,y) -- torus xs ys == 'box' ('circuit' xs) ('circuit' ys) -- torus [1,2] "ab" == 'edges' [ ((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\')) ]@@ -623,15 +629,3 @@ where xs = map (\b -> fmap (,b) x) $ toList y ys = map (\a -> fmap (a,) y) $ toList x---- | The 'ToGraph' type class captures data types that can be converted to--- polymorphic graph expressions. The conversion method 'toGraph' semantically--- acts as the identity on graph data structures, but allows to convert graphs--- between different data representations.------ @--- toGraph (g :: 'Algebra.Graph.Graph' a ) :: 'Algebra.Graph.Graph' a == g--- 'show' (toGraph (1 * 2 :: 'Algebra.Graph.Graph' Int) :: 'Algebra.Graph.Fold' Int) == "edge 1 2"--- @-class ToGraph t where- toGraph :: Graph g => t a -> g a
− src/Algebra/Graph/IntAdjacencyMap.hs
@@ -1,646 +0,0 @@--------------------------------------------------------------------------------- |--- Module : Algebra.Graph.IntAdjacencyMap--- 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 'IntAdjacencyMap' data type, as well as associated--- operations and algorithms. 'IntAdjacencyMap' 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.IntAdjacencyMap (- -- * Data structure- IntAdjacencyMap, adjacencyMap,-- -- * Basic graph construction primitives- empty, vertex, edge, overlay, connect, vertices, edges, overlays, connects,- fromAdjacencyList,-- -- * Relations on graphs- isSubgraphOf,-- -- * Graph properties- isEmpty, hasVertex, hasEdge, vertexCount, edgeCount, vertexList, edgeList,- adjacencyList, vertexIntSet, edgeSet, postIntSet,-- -- * Standard families of graphs- path, circuit, clique, biclique, star, starTranspose, tree, forest,-- -- * Graph transformation- removeVertex, removeEdge, replaceVertex, mergeVertices, transpose, gmap, induce,-- -- * Algorithms- dfsForest, dfsForestFrom, dfs, topSort, isTopSort- ) where--import Data.IntSet (IntSet)-import Data.Maybe-import Data.Set (Set)-import Data.Tree--import Algebra.Graph.IntAdjacencyMap.Internal--import qualified Algebra.Graph.Class as C-import qualified Data.Graph as KL-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 :: IntAdjacencyMap-empty = C.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 -> IntAdjacencyMap-vertex = C.vertex---- | 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 :: Int -> Int -> IntAdjacencyMap-edge = C.edge---- | /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 :: IntAdjacencyMap -> IntAdjacencyMap -> IntAdjacencyMap-overlay = C.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 :: IntAdjacencyMap -> IntAdjacencyMap -> IntAdjacencyMap-connect = C.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.------ @--- vertices [] == 'empty'--- vertices [x] == 'vertex' x--- 'hasVertex' x . vertices == 'elem' x--- 'vertexCount' . vertices == 'length' . 'Data.List.nub'--- 'vertexIntSet' . vertices == IntSet.'IntSet.fromList'--- @-vertices :: [Int] -> IntAdjacencyMap-vertices = mkAM . IntMap.fromList . map (\x -> (x, IntSet.empty))---- | Construct the graph from a list of edges.--- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.------ @--- edges [] == 'empty'--- edges [(x, y)] == 'edge' x y--- 'edgeCount' . edges == 'length' . 'Data.List.nub'--- 'edgeList' . edges == 'Data.List.nub' . 'Data.List.sort'--- @-edges :: [(Int, Int)] -> IntAdjacencyMap-edges = fromAdjacencyList . map (fmap return)---- | 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 :: [IntAdjacencyMap] -> IntAdjacencyMap-overlays = C.overlays---- | Connect a given list of graphs.--- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.------ @--- connects [] == 'empty'--- connects [x] == x--- connects [x,y] == 'connect' x y--- connects == 'foldr' 'connect' 'empty'--- 'isEmpty' . connects == 'all' 'isEmpty'--- @-connects :: [IntAdjacencyMap] -> IntAdjacencyMap-connects = C.connects---- | Construct a graph from an adjacency list.--- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.------ @--- fromAdjacencyList [] == 'empty'--- fromAdjacencyList [(x, [])] == 'vertex' x--- fromAdjacencyList [(x, [y])] == 'edge' x y--- fromAdjacencyList . 'adjacencyList' == id--- 'overlay' (fromAdjacencyList xs) (fromAdjacencyList ys) == fromAdjacencyList (xs ++ ys)--- @-fromAdjacencyList :: [(Int, [Int])] -> IntAdjacencyMap-fromAdjacencyList as = mkAM $ IntMap.unionWith IntSet.union vs es- where- ss = map (fmap IntSet.fromList) as- vs = IntMap.fromSet (const IntSet.empty) . IntSet.unions $ map snd ss- es = IntMap.fromListWith IntSet.union ss---- | The 'isSubgraphOf' function takes two graphs and returns 'True' if the--- first graph is a /subgraph/ of the second.--- Complexity: /O((n + m) * log(n))/ time.------ @--- isSubgraphOf 'empty' x == True--- isSubgraphOf ('vertex' x) 'empty' == False--- isSubgraphOf x ('overlay' x y) == True--- isSubgraphOf ('overlay' x y) ('connect' x y) == True--- isSubgraphOf ('path' xs) ('circuit' xs) == True--- @-isSubgraphOf :: IntAdjacencyMap -> IntAdjacencyMap -> Bool-isSubgraphOf x y = IntMap.isSubmapOfBy IntSet.isSubsetOf (adjacencyMap x) (adjacencyMap 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' x y) == False--- @-isEmpty :: IntAdjacencyMap -> Bool-isEmpty = IntMap.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 :: Int -> IntAdjacencyMap -> Bool-hasVertex x = IntMap.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' x y) == True--- hasEdge x y . 'removeEdge' x y == const False--- hasEdge x y == 'elem' (x,y) . 'edgeList'--- @-hasEdge :: Int -> Int -> IntAdjacencyMap -> Bool-hasEdge u v a = case IntMap.lookup u (adjacencyMap a) of- Nothing -> False- Just vs -> IntSet.member v vs---- | The number of vertices in a graph.--- Complexity: /O(1)/ time.------ @--- vertexCount 'empty' == 0--- vertexCount ('vertex' x) == 1--- vertexCount == 'length' . 'vertexList'--- @-vertexCount :: IntAdjacencyMap -> Int-vertexCount = IntMap.size . adjacencyMap---- | The number of edges in a graph.--- Complexity: /O(n)/ time.------ @--- edgeCount 'empty' == 0--- edgeCount ('vertex' x) == 0--- edgeCount ('edge' x y) == 1--- edgeCount == 'length' . 'edgeList'--- @-edgeCount :: IntAdjacencyMap -> Int-edgeCount = IntMap.foldr (\es r -> (IntSet.size es + r)) 0 . 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 :: IntAdjacencyMap -> [Int]-vertexList = IntMap.keys . adjacencyMap---- | The sorted list of edges of a graph.--- Complexity: /O(n + m)/ time and /O(m)/ memory.------ @--- edgeList 'empty' == []--- edgeList ('vertex' x) == []--- 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 :: IntAdjacencyMap -> [(Int, Int)]-edgeList (AM m _) = [ (x, y) | (x, ys) <- IntMap.toAscList m, y <- IntSet.toAscList ys ]---- | The sorted /adjacency list/ of a graph.--- Complexity: /O(n + m)/ time and /O(m)/ memory.------ @--- adjacencyList 'empty' == []--- adjacencyList ('vertex' x) == [(x, [])]--- adjacencyList ('edge' 1 2) == [(1, [2]), (2, [])]--- adjacencyList ('star' 2 [3,1]) == [(1, []), (2, [1,3]), (3, [])]--- 'fromAdjacencyList' . adjacencyList == id--- @-adjacencyList :: IntAdjacencyMap -> [(Int, [Int])]-adjacencyList = map (fmap IntSet.toAscList) . IntMap.toAscList . adjacencyMap---- | The set of vertices of a given graph.--- 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 :: IntAdjacencyMap -> IntSet-vertexIntSet = IntMap.keysSet . adjacencyMap---- | The set of edges of a given graph.--- Complexity: /O((n + m) * log(m))/ time and /O(m)/ memory.------ @--- edgeSet 'empty' == Set.'Set.empty'--- edgeSet ('vertex' x) == Set.'Set.empty'--- edgeSet ('edge' x y) == Set.'Set.singleton' (x,y)--- edgeSet . 'edges' == Set.'Set.fromList'--- @-edgeSet :: IntAdjacencyMap -> Set (Int, Int)-edgeSet = IntMap.foldrWithKey combine Set.empty . adjacencyMap- where- combine u es = Set.union (Set.fromAscList [ (u, v) | v <- IntSet.toAscList es ])---- | The /postset/ (here 'postIntSet') of a vertex is the set of its /direct successors/.------ @--- postIntSet x 'empty' == IntSet.'IntSet.empty'--- postIntSet x ('vertex' x) == IntSet.'IntSet.empty'--- postIntSet x ('edge' x y) == IntSet.'IntSet.fromList' [y]--- postIntSet 2 ('edge' 1 2) == IntSet.'IntSet.empty'--- @-postIntSet :: Int -> IntAdjacencyMap -> IntSet-postIntSet x = IntMap.findWithDefault IntSet.empty x . adjacencyMap---- | The /path/ on a list of vertices.--- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.------ @--- path [] == 'empty'--- path [x] == 'vertex' x--- path [x,y] == 'edge' x y--- path . 'reverse' == 'transpose' . path--- @-path :: [Int] -> IntAdjacencyMap-path = C.path---- | The /circuit/ on a list of vertices.--- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.------ @--- circuit [] == 'empty'--- circuit [x] == 'edge' x x--- circuit [x,y] == 'edges' [(x,y), (y,x)]--- circuit . 'reverse' == 'transpose' . circuit--- @-circuit :: [Int] -> IntAdjacencyMap-circuit = C.circuit---- | The /clique/ on a list of vertices.--- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.------ @--- clique [] == 'empty'--- clique [x] == 'vertex' x--- clique [x,y] == 'edge' x y--- clique [x,y,z] == 'edges' [(x,y), (x,z), (y,z)]--- clique (xs ++ ys) == 'connect' (clique xs) (clique ys)--- clique . 'reverse' == 'transpose' . clique--- @-clique :: [Int] -> IntAdjacencyMap-clique = C.clique---- | The /biclique/ on two lists of vertices.--- Complexity: /O(n * log(n) + m)/ time and /O(n + m)/ memory.------ @--- biclique [] [] == 'empty'--- biclique [x] [] == 'vertex' x--- biclique [] [y] == 'vertex' y--- biclique [x1,x2] [y1,y2] == 'edges' [(x1,y1), (x1,y2), (x2,y1), (x2,y2)]--- biclique xs ys == 'connect' ('vertices' xs) ('vertices' ys)--- @-biclique :: [Int] -> [Int] -> IntAdjacencyMap-biclique xs ys = mkAM $ IntMap.fromSet adjacent (x `IntSet.union` y)- where- x = IntSet.fromList xs- y = IntSet.fromList ys- adjacent v- | v `IntSet.member` x = y- | otherwise = IntSet.empty---- | The /star/ formed by a centre vertex connected to a list of leaves.--- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.------ @--- star x [] == 'vertex' x--- star x [y] == 'edge' x y--- star x [y,z] == 'edges' [(x,y), (x,z)]--- star x ys == 'connect' ('vertex' x) ('vertices' ys)--- @-star :: Int -> [Int] -> IntAdjacencyMap-star = C.star---- | 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 :: Int -> [Int] -> IntAdjacencyMap-starTranspose = C.starTranspose---- | 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 []]]) == 'path' [x,y,z]--- tree (Node x [Node y [], Node z []]) == 'star' x [y,z]--- tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == 'edges' [(1,2), (1,3), (3,4), (3,5)]--- @-tree :: Tree Int -> IntAdjacencyMap-tree = C.tree---- | The /forest graph/ constructed from a given 'Forest' data structure.--- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.------ @--- forest [] == 'empty'--- forest [x] == 'tree' x--- forest [Node 1 [Node 2 [], Node 3 []], Node 4 [Node 5 []]] == 'edges' [(1,2), (1,3), (4,5)]--- forest == 'overlays' . map 'tree'--- @-forest :: Forest Int -> IntAdjacencyMap-forest = C.forest---- | 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' x x) == 'empty'--- removeVertex 1 ('edge' 1 2) == 'vertex' 2--- removeVertex x . removeVertex x == removeVertex x--- @-removeVertex :: Int -> IntAdjacencyMap -> IntAdjacencyMap-removeVertex x = mkAM . IntMap.map (IntSet.delete x) . IntMap.delete x . adjacencyMap---- | Remove an edge from a given graph.--- Complexity: /O(log(n))/ time.------ @--- removeEdge x y ('edge' 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 :: Int -> Int -> IntAdjacencyMap -> IntAdjacencyMap-removeEdge x y = mkAM . IntMap.adjust (IntSet.delete y) x . adjacencyMap---- | The function @'replaceVertex' x y@ replaces vertex @x@ with vertex @y@ in a--- given 'IntAdjacencyMap'. 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 :: Int -> Int -> IntAdjacencyMap -> IntAdjacencyMap-replaceVertex u v = gmap $ \w -> if w == u then v else w---- | Merge vertices satisfying a given predicate into a given vertex.--- Complexity: /O((n + m) * log(n))/ time, assuming that the predicate takes--- /O(1)/ to be evaluated.------ @--- mergeVertices (const False) x == id--- mergeVertices (== x) y == 'replaceVertex' x y--- mergeVertices even 1 (0 * 2) == 1 * 1--- mergeVertices odd 1 (3 + 4 * 5) == 4 * 1--- @-mergeVertices :: (Int -> Bool) -> Int -> IntAdjacencyMap -> IntAdjacencyMap-mergeVertices p v = gmap $ \u -> if p u then v else u---- | Transpose a given graph.--- Complexity: /O(m * log(n))/ time, /O(n + m)/ memory.------ @--- transpose 'empty' == 'empty'--- 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 :: IntAdjacencyMap -> IntAdjacencyMap-transpose (AM m _) = mkAM $ IntMap.foldrWithKey combine vs m- where- combine v es = IntMap.unionWith IntSet.union (IntMap.fromSet (const $ IntSet.singleton v) es)- vs = IntMap.fromSet (const IntSet.empty) (IntMap.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--- 'IntAdjacencyMap'.--- Complexity: /O((n + m) * log(n))/ time.------ @--- gmap f 'empty' == 'empty'--- gmap f ('vertex' x) == 'vertex' (f x)--- gmap f ('edge' x y) == 'edge' (f x) (f y)--- gmap id == id--- gmap f . gmap g == gmap (f . g)--- @-gmap :: (Int -> Int) -> IntAdjacencyMap -> IntAdjacencyMap-gmap f = mkAM . IntMap.map (IntSet.map f) . IntMap.mapKeysWith IntSet.union f . 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 :: (Int -> Bool) -> IntAdjacencyMap -> IntAdjacencyMap-induce p = mkAM . IntMap.map (IntSet.filter p) . IntMap.filterWithKey (\k _ -> p k) . adjacencyMap---- | Compute the /depth-first search/ forest of a graph.------ @--- '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--- 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 :: IntAdjacencyMap -> Forest Int-dfsForest (AM _ (GraphKL g r _)) = fmap (fmap r) (KL.dff 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.------ @--- '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--- 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] -> IntAdjacencyMap -> Forest Int-dfsForestFrom vs (AM _ (GraphKL g r t)) = fmap (fmap r) (KL.dfs g (mapMaybe t vs))---- | 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 [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] -> IntAdjacencyMap -> [Int]-dfs vs = concatMap flatten . dfsForestFrom vs---- | 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 'isTopSort' x) (topSort x) /= Just False--- @-topSort :: IntAdjacencyMap -> Maybe [Int]-topSort m@(AM _ (GraphKL g r _)) =- if isTopSort result m then Just result else Nothing- where- result = map r (KL.topSort g)---- | Check if a given list of vertices is a valid /topological sort/ of a graph.------ @--- isTopSort [3, 1, 2] (1 * 2 + 3 * 1) == True--- isTopSort [1, 2, 3] (1 * 2 + 3 * 1) == False--- isTopSort [] (1 * 2 + 3 * 1) == False--- isTopSort [] 'empty' == True--- isTopSort [x] ('vertex' x) == True--- isTopSort [x] ('edge' x x) == False--- @-isTopSort :: [Int] -> IntAdjacencyMap -> Bool-isTopSort xs m = go IntSet.empty xs- where- go seen [] = seen == IntMap.keysSet (adjacencyMap m)- go seen (v:vs) = let newSeen = seen `seq` IntSet.insert v seen- in postIntSet v m `IntSet.intersection` newSeen == IntSet.empty && go newSeen vs
− src/Algebra/Graph/IntAdjacencyMap/Internal.hs
@@ -1,196 +0,0 @@--------------------------------------------------------------------------------- |--- Module : Algebra.Graph.IntAdjacencyMap.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 adjacency maps. The API is unstable--- and unsafe, and is exposed only for documentation. You should use the--- non-internal module "Algebra.Graph.IntAdjacencyMap" instead.-------------------------------------------------------------------------------module Algebra.Graph.IntAdjacencyMap.Internal (- -- * Adjacency map implementation- IntAdjacencyMap (..), mkAM, consistent,-- -- * Interoperability with King-Launchbury graphs- GraphKL (..), mkGraphKL- ) where--import Data.IntMap.Strict (IntMap, keysSet, fromSet)-import Data.IntSet (IntSet)-import Data.List--import Algebra.Graph.Class--import qualified Data.Graph as KL-import qualified Data.IntMap.Strict as IntMap-import qualified Data.IntSet as IntSet--{-| The 'IntAdjacencyMap' 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))--The 'Show' instance is defined using basic graph construction primitives:--@show (empty :: IntAdjacencyMap Int) == "empty"-show (1 :: IntAdjacencyMap Int) == "vertex 1"-show (1 + 2 :: IntAdjacencyMap Int) == "vertices [1,2]"-show (1 * 2 :: IntAdjacencyMap Int) == "edge 1 2"-show (1 * 2 * 3 :: IntAdjacencyMap Int) == "edges [(1,2),(1,3),(2,3)]"-show (1 * 2 + 3 :: IntAdjacencyMap Int) == "overlay (vertex 3) (edge 1 2)"@--The 'Eq' instance satisfies all axioms of algebraic graphs:-- * 'Algebra.Graph.IntAdjacencyMap.overlay' is commutative and associative:-- > x + y == y + x- > x + (y + z) == (x + y) + z-- * 'Algebra.Graph.IntAdjacencyMap.connect' is associative and has- 'Algebra.Graph.IntAdjacencyMap.empty' as the identity:-- > x * empty == x- > empty * x == x- > x * (y * z) == (x * y) * z-- * 'Algebra.Graph.IntAdjacencyMap.connect' distributes over- 'Algebra.Graph.IntAdjacencyMap.overlay':-- > x * (y + z) == x * y + x * z- > (x + y) * z == x * z + y * z-- * 'Algebra.Graph.IntAdjacencyMap.connect' can be decomposed:-- > x * y * z == x * y + x * z + y * z--The following useful theorems can be proved from the above set of axioms.-- * 'Algebra.Graph.IntAdjacencyMap.overlay' has- 'Algebra.Graph.IntAdjacencyMap.empty' as the identity and is idempotent:-- > x + empty == x- > empty + x == x- > x + x == x-- * Absorption and saturation of 'Algebra.Graph.IntAdjacencyMap.connect':-- > 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.--}-data IntAdjacencyMap = AM {- -- | The /adjacency map/ of the graph: each vertex is associated with a set- -- of its direct successors.- adjacencyMap :: !(IntMap IntSet),- -- | Cached King-Launchbury representation.- -- /Note: this field is for internal use only/.- graphKL :: GraphKL }---- | Construct an 'AdjacencyMap' from a map of successor sets and (lazily)--- compute the corresponding King-Launchbury representation.--- /Note: this function is for internal use only/.-mkAM :: IntMap IntSet -> IntAdjacencyMap-mkAM m = AM m (mkGraphKL m)--instance Eq IntAdjacencyMap where- x == y = adjacencyMap x == adjacencyMap y--instance Show IntAdjacencyMap where- show (AM m _)- | null vs = "empty"- | null es = vshow vs- | vs == used = eshow es- | otherwise = "overlay (" ++ vshow (vs \\ used) ++ ") (" ++ eshow es ++ ")"- 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- used = IntSet.toAscList (referredToVertexSet m)--instance Graph IntAdjacencyMap where- type Vertex IntAdjacencyMap = Int- empty = mkAM IntMap.empty- vertex x = mkAM $ IntMap.singleton x IntSet.empty- overlay x y = mkAM $ IntMap.unionWith IntSet.union (adjacencyMap x) (adjacencyMap y)- connect x y = mkAM $ IntMap.unionsWith IntSet.union [ adjacencyMap x, adjacencyMap y,- fromSet (const . keysSet $ adjacencyMap y) (keysSet $ adjacencyMap x) ]--instance Num IntAdjacencyMap where- fromInteger = vertex . fromInteger- (+) = overlay- (*) = connect- signum = const empty- abs = id- negate = id--instance ToGraph IntAdjacencyMap where- type ToVertex IntAdjacencyMap = Int- toGraph = overlays . map (uncurry star . fmap IntSet.toList) . IntMap.toList . adjacencyMap---- | 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.--- /Note: this function is for internal use only/.------ @--- consistent 'Algebra.Graph.IntAdjacencyMap.empty' == True--- consistent ('Algebra.Graph.IntAdjacencyMap.vertex' x) == True--- consistent ('Algebra.Graph.IntAdjacencyMap.overlay' x y) == True--- consistent ('Algebra.Graph.IntAdjacencyMap.connect' x y) == True--- consistent ('Algebra.Graph.IntAdjacencyMap.edge' x y) == True--- consistent ('Algebra.Graph.IntAdjacencyMap.edges' xs) == True--- consistent ('Algebra.Graph.IntAdjacencyMap.graph' xs ys) == True--- consistent ('Algebra.Graph.IntAdjacencyMap.fromAdjacencyList' xs) == True--- @-consistent :: IntAdjacencyMap -> Bool-consistent (AM m _) = referredToVertexSet m `IntSet.isSubsetOf` keysSet m---- The set of vertices that are referred to by the edges-referredToVertexSet :: IntMap IntSet -> IntSet-referredToVertexSet = IntSet.fromList . uncurry (++) . unzip . internalEdgeList---- The list of edges in adjacency map-internalEdgeList :: IntMap IntSet -> [(Int, Int)]-internalEdgeList m = [ (x, y) | (x, ys) <- IntMap.toAscList m, y <- IntSet.toAscList ys ]---- | 'GraphKL' encapsulates King-Launchbury graphs, which are implemented in--- the "Data.Graph" module of the @containers@ library.--- /Note: this data structure is for internal use only/.------ If @mkGraphKL (adjacencyMap g) == h@ then the following holds:------ @--- map ('fromVertexKL' h) ('Data.Graph.vertices' $ 'toGraphKL' h) == 'Algebra.Graph.AdjacencyMap.vertexList' g--- map (\\(x, y) -> ('fromVertexKL' h x, 'fromVertexKL' h y)) ('Data.Graph.edges' $ 'toGraphKL' h) == 'Algebra.Graph.AdjacencyMap.edgeList' g--- @-data GraphKL = GraphKL {- -- | Array-based graph representation (King and Launchbury, 1995).- toGraphKL :: KL.Graph,- -- | A mapping of "Data.Graph.Vertex" to vertices of type @Int@.- fromVertexKL :: KL.Vertex -> Int,- -- | A mapping from vertices of type @Int@ to "Data.Graph.Vertex".- -- Returns 'Nothing' if the argument is not in the graph.- toVertexKL :: Int -> Maybe KL.Vertex }---- | Build 'GraphKL' from a map of successor sets.--- /Note: this function is for internal use only/.-mkGraphKL :: IntMap IntSet -> GraphKL-mkGraphKL m = GraphKL- { toGraphKL = g- , fromVertexKL = \u -> case r u of (_, v, _) -> v- , toVertexKL = t }- where- (g, r, t) = KL.graphFromEdges [ ((), v, IntSet.toAscList us) | (v, us) <- IntMap.toAscList m ]
src/Algebra/Graph/Internal.hs view
@@ -20,7 +20,9 @@ List (..), -- * Data structures for graph traversal- Focus, focus, Context (..), context+ Focus (..), emptyFocus, vertexFocus, overlayFoci, connectFoci, Hit (..),++ foldr1Safe ) where import Prelude ()@@ -29,8 +31,6 @@ import Data.Foldable import Data.Semigroup -import Algebra.Graph.Class (ToGraph(..))- import qualified GHC.Exts as Exts -- | An abstract list data type with /O(1)/ time concatenation (the current@@ -77,6 +77,15 @@ return = pure x >>= f = Exts.fromList (toList x >>= toList . f) +-- | 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 'Algebra.Graph.removeEdge' for a use-case example.+data Focus a = Focus+ { ok :: Bool -- ^ True if focus on the specified subgraph is obtained.+ , is :: List a -- ^ Inputs into the focused subgraph.+ , os :: List a -- ^ Outputs out of the focused subgraph.+ , vs :: List a } -- ^ All vertices (leaves) of the graph expression.+ -- | Focus on the empty graph. emptyFocus :: Focus a emptyFocus = Focus False mempty mempty mempty@@ -97,27 +106,15 @@ xs = if ok y then vs x else is x ys = if ok x then vs y else os y --- | 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] }+-- | An auxiliary data type for 'hasEdge': when searching for an edge, we can hit+-- its 'Tail', i.e. the source vertex, the whole 'Edge', or 'Miss' it entirely.+data Hit = Miss | Tail | Edge deriving (Eq, Ord) --- | Extract the context from a graph 'Focus'. Returns @Nothing@ if the focus--- could not be obtained.-context :: ToGraph g => (ToVertex g -> Bool) -> g -> Maybe (Context (ToVertex g))-context p g | ok f = Just $ Context (toList $ is f) (toList $ os f)- | otherwise = Nothing+-- | A safe version of 'foldr1'+foldr1Safe :: (a -> a -> a) -> [a] -> Maybe a+foldr1Safe f = foldr mf Nothing where- f = focus p g---- | 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 'Algebra.Graph.removeEdge' for a use-case example.-data Focus a = Focus- { ok :: Bool -- ^ True if focus on the specified subgraph is obtained.- , is :: List a -- ^ Inputs into the focused subgraph.- , os :: List a -- ^ Outputs out of the focused subgraph.- , vs :: List a } -- ^ All vertices (leaves) of the graph expression.---- | 'Focus' on a specified subgraph.-focus :: ToGraph g => (ToVertex g -> Bool) -> g -> Focus (ToVertex g)-focus f = foldg emptyFocus (vertexFocus f) overlayFoci connectFoci+ mf x m = Just (case m of+ Nothing -> x+ Just y -> f x y)+{-# INLINE foldr1Safe #-}
+ src/Algebra/Graph/Label.hs view
@@ -0,0 +1,126 @@+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.Label+-- 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 provides basic data types and type classes for representing edge+-- labels in edge-labelled graphs, e.g. see "Algebra.Graph.Labelled".+--+-----------------------------------------------------------------------------+module Algebra.Graph.Label (+ -- * Type classes for edge labels+ Semilattice (..), Dioid (..),++ -- * Data types for edge labels+ Distance (..)+ ) where++import Prelude ()+import Prelude.Compat+import Data.Set (Set)++import qualified Data.Set as Set++{-| A /bounded join semilattice/, satisfying the following laws:++ * Commutativity:++ > x \/ y == y \/ x++ * Associativity:++ > x \/ (y \/ z) == (x \/ y) \/ z++ * Identity:++ > x \/ zero == x++ * Idempotence:++ > x \/ x == x+-}+class Semilattice a where+ zero :: a+ (\/) :: a -> a -> a++{-| A /dioid/ is an /idempotent semiring/, i.e. it satisfies the following laws:++ * Associativity:++ > x /\ (y /\ z) == (x /\ y) /\ z++ * Identity:++ > x /\ one == x+ > one /\ 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 => Dioid a where+ one :: a+ (/\) :: a -> a -> a++infixl 6 \/+infixl 7 /\++instance Semilattice Bool where+ zero = False+ (\/) = (||)++instance Dioid Bool where+ one = True+ (/\) = (&&)++-- | A /distance/ is a non-negative value that can be 'Finite' or 'Infinite'.+data Distance a = Finite a | Infinite deriving (Eq, Ord, Show)++instance (Ord a, Num a) => Num (Distance a) where+ fromInteger = Finite . fromInteger++ Infinite + _ = Infinite+ _ + Infinite = Infinite+ Finite x + Finite y = Finite (x + y)++ Infinite * _ = Infinite+ _ * Infinite = Infinite+ Finite x * Finite y = Finite (x * y)++ negate _ = error "Negative distances not allowed"++ signum (Finite 0) = 0+ signum _ = 1++ abs = id++instance Ord a => Semilattice (Distance a) where+ zero = Infinite++ Infinite \/ x = x+ x \/ Infinite = x+ Finite x \/ Finite y = Finite (min x y)++instance (Num a, Ord a) => Dioid (Distance a) where+ one = Finite 0++ Infinite /\ _ = Infinite+ _ /\ Infinite = Infinite+ Finite x /\ Finite y = Finite (x + y)++instance Ord a => Semilattice (Set a) where+ zero = Set.empty+ (\/) = Set.union
+ src/Algebra/Graph/Labelled.hs view
@@ -0,0 +1,122 @@+{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.Labelled+-- 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 provides a minimal and experimental implementation of algebraic+-- graphs with edge labels. The API will be expanded in the next release.+-----------------------------------------------------------------------------+module Algebra.Graph.Labelled (+ -- * Algebraic data type for edge-labeleld graphs+ Graph (..), UnlabelledGraph, empty, vertex, edge, overlay, connect,+ connectBy, (-<), (>-),++ -- * Operations+ edgeLabel+ ) where++import Prelude ()+import Prelude.Compat++import Algebra.Graph.Label+import qualified Algebra.Graph.Class as C++-- | Edge-labelled graphs, where the type variable @e@ stands for edge labels.+-- 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)++-- | A type synonym for unlabelled graphs.+type UnlabelledGraph a = Graph Bool a++-- | Construct the /empty graph/. An alias for the constructor 'Empty'.+-- Complexity: /O(1)/ time, memory and size.+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.+vertex :: a -> Graph e a+vertex = Vertex++-- | Construct the graph comprising /a single edge/ with the label 'one'.+-- Complexity: /O(1)/ time, memory and size.+edge :: Dioid e => a -> a -> Graph e a+edge = C.edge++-- | /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++-- | /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++-- | /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++-- | The left-hand part of a convenient ternary-ish operator @x -\<e\>- y@ for+-- connecting graphs with labelled edges. For example:+--+-- @+-- x = 'vertex' "x"+-- y = 'vertex' "y"+-- z = x -\<2\>- y+-- @+(-<) :: Graph e a -> e -> (Graph e a, e)+g -< e = (g, e)++-- | The right-hand part of a convenient ternary-ish operator @x -\<e\>- y@ for+-- connecting graphs with labelled edges. For example:+--+-- @+-- x = 'vertex' "x"+-- y = 'vertex' "y"+-- z = x -\<2\>- y+-- @+(>-) :: (Graph e a, e) -> Graph e a -> Graph e a+(g, e) >- h = Connect e g h++infixl 5 -<+infixl 5 >-++instance Dioid e => C.Graph (Graph e a) where+ type Vertex (Graph e a) = a+ empty = Empty+ vertex = Vertex+ overlay = overlay+ connect = connect++-- | 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+ where+ new | x `elem` g && y `elem` h = e+ | otherwise = zero
src/Algebra/Graph/NonEmpty.hs view
@@ -36,11 +36,11 @@ vertexSet, vertexIntSet, edgeSet, -- * Standard families of graphs- path1, circuit1, clique1, biclique1, star, starTranspose, tree, mesh1, torus1,+ path1, circuit1, clique1, biclique1, star, stars1, tree, mesh1, torus1, -- * Graph transformation removeVertex1, removeEdge, replaceVertex, mergeVertices, splitVertex1,- transpose, induce1, simplify,+ transpose, induce1, simplify, sparsify, -- * Graph composition box@@ -55,20 +55,18 @@ import Control.DeepSeq (NFData (..)) import Control.Monad.Compat+import Control.Monad.State (runState, get, put) import Data.List.NonEmpty (NonEmpty (..)) import Algebra.Graph.Internal -import qualified Algebra.Graph as G-import qualified Algebra.Graph.AdjacencyMap as AM-import qualified Algebra.Graph.Class as C-import qualified Algebra.Graph.HigherKinded.Class as H-import qualified Algebra.Graph.IntAdjacencyMap as IAM-import qualified Algebra.Graph.Relation as R-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.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 {-| The 'NonEmptyGraph' data type is a deep embedding of the core graph construction primitives 'vertex', 'overlay' and 'connect'. As one can guess from@@ -143,12 +141,10 @@ rnf (Overlay x y) = rnf x `seq` rnf y rnf (Connect x y) = rnf x `seq` rnf y -instance C.ToGraph (NonEmptyGraph a) where+instance T.ToGraph (NonEmptyGraph a) where type ToVertex (NonEmptyGraph a) = a foldg _ = foldg1--instance H.ToGraph NonEmptyGraph where- toGraph = foldg1 H.vertex H.overlay H.connect+ hasEdge = hasEdge instance Num a => Num (NonEmptyGraph a) where fromInteger = Vertex . fromInteger@@ -159,8 +155,19 @@ negate = id instance Ord a => Eq (NonEmptyGraph a) where- x == y = C.toGraph x == (C.toGraph y :: AM.AdjacencyMap a)+ (==) = equals +-- 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++-- | 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+ instance Applicative NonEmptyGraph where pure = Vertex (<*>) = ap@@ -196,6 +203,7 @@ -- @ vertex :: a -> NonEmptyGraph a vertex = Vertex+{-# INLINE vertex #-} -- | Construct the graph comprising /a single edge/. -- Complexity: /O(1)/ time, memory and size.@@ -226,6 +234,7 @@ -- @ overlay :: NonEmptyGraph a -> NonEmptyGraph a -> NonEmptyGraph 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@@ -260,6 +269,7 @@ -- @ connect :: NonEmptyGraph a -> NonEmptyGraph a -> NonEmptyGraph a connect = Connect+{-# INLINE connect #-} -- | Construct the graph comprising a given list of isolated vertices. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the@@ -273,6 +283,7 @@ -- @ vertices1 :: NonEmpty a -> NonEmptyGraph a vertices1 = overlays1 . fmap vertex+{-# NOINLINE [1] vertices1 #-} -- | Construct the graph from a list of edges. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the@@ -283,7 +294,7 @@ -- 'edgeCount' . edges1 == 'Data.List.NonEmpty.length' . 'Data.List.NonEmpty.nub' -- @ edges1 :: NonEmpty (a, a) -> NonEmptyGraph a-edges1 = overlays1 . fmap (uncurry edge)+edges1 = overlays1 . fmap (uncurry edge) -- | Overlay a given list of graphs. -- Complexity: /O(L)/ time and memory, and /O(S)/ size, where /L/ is the length@@ -294,8 +305,8 @@ -- overlays1 (x ':|' [y]) == 'overlay' x y -- @ overlays1 :: NonEmpty (NonEmptyGraph a) -> NonEmptyGraph a-overlays1 (x :| xs) = case xs of [] -> x- (y:ys) -> overlay x (overlays1 $ y :| ys)+overlays1 = concatg1 overlay+{-# INLINE [2] overlays1 #-} -- | Connect a given list of graphs. -- Complexity: /O(L)/ time and memory, and /O(S)/ size, where /L/ is the length@@ -306,9 +317,13 @@ -- connects1 (x ':|' [y]) == 'connect' x y -- @ connects1 :: NonEmpty (NonEmptyGraph a) -> NonEmptyGraph a-connects1 (x :| xs) = case xs of [] -> x- (y:ys) -> connect x (connects1 $ y :| ys)+connects1 = concatg1 connect+{-# INLINE [2] connects1 #-} +-- | Auxiliary function, similar to 'sconcat'.+concatg1 :: (NonEmptyGraph a -> NonEmptyGraph a -> NonEmptyGraph a) -> NonEmpty (NonEmptyGraph a) -> NonEmptyGraph a+concatg1 combine (x :| xs) = maybe x (combine x) $ foldr1Safe combine xs+ -- | Generalised graph folding: recursively collapse a 'NonEmptyGraph' by -- applying the provided functions to the leaves and internal nodes of the -- expression. The order of arguments is: vertex, overlay and connect.@@ -336,6 +351,7 @@ -- isSubgraphOf ('overlay' x y) ('connect' x y) == True -- isSubgraphOf ('path1' xs) ('circuit1' xs) == True -- @+{-# SPECIALISE isSubgraphOf :: NonEmptyGraph Int -> NonEmptyGraph Int -> Bool #-} isSubgraphOf :: Ord a => NonEmptyGraph a -> NonEmptyGraph a -> Bool isSubgraphOf x y = overlay x y == y @@ -348,6 +364,7 @@ -- 1 + 2 === 2 + 1 == False -- x + y === x * y == False -- @+{-# SPECIALISE (===) :: NonEmptyGraph Int -> NonEmptyGraph Int -> Bool #-} (===) :: Eq a => NonEmptyGraph a -> NonEmptyGraph a -> Bool (Vertex x1 ) === (Vertex x2 ) = x1 == x2 (Overlay x1 y1) === (Overlay x2 y2) = x1 === x2 && y1 === y2@@ -376,9 +393,11 @@ -- hasVertex x ('vertex' x) == True -- hasVertex 1 ('vertex' 2) == False -- @+{-# SPECIALISE hasVertex :: Int -> NonEmptyGraph Int -> Bool #-} hasVertex :: Eq a => a -> NonEmptyGraph a -> Bool hasVertex v = foldg1 (==v) (||) (||) +-- TODO: Reduce code duplication with 'Algebra.Graph.hasEdge'. -- | Check if a graph contains a given edge. -- Complexity: /O(s)/ time. --@@ -388,8 +407,19 @@ -- hasEdge x y . 'removeEdge' x y == const False -- hasEdge x y == 'elem' (x,y) . 'edgeList' -- @-hasEdge :: Ord a => a -> a -> NonEmptyGraph a -> Bool-hasEdge u v = G.hasEdge u v . H.toGraph+{-# SPECIALISE hasEdge :: Int -> Int -> NonEmptyGraph Int -> Bool #-}+hasEdge :: Eq a => a -> a -> NonEmptyGraph a -> Bool+hasEdge s t g = hit g == Edge+ where+ hit (Vertex x ) = if x == s then Tail else Miss+ hit (Overlay x y) = case hit x of+ Miss -> hit y+ Tail -> max Tail (hit y)+ Edge -> Edge+ hit (Connect x y) = case hit x of+ Miss -> hit y+ Tail -> if hasVertex t y then Edge else Tail+ Edge -> Edge -- | The number of vertices in a graph. -- Complexity: /O(s * log(n))/ time.@@ -399,9 +429,15 @@ -- vertexCount x >= 1 -- vertexCount == 'length' . 'vertexList1' -- @+{-# RULES "vertexCount/Int" vertexCount = vertexIntCount #-}+{-# INLINE [1] vertexCount #-} vertexCount :: Ord a => NonEmptyGraph a -> Int-vertexCount = length . vertexList1+vertexCount = T.vertexCount +-- | Like 'vertexCount' but specialised for NonEmptyGraph with vertices of type 'Int'.+vertexIntCount :: NonEmptyGraph Int -> Int+vertexIntCount = IntSet.size . vertexIntSet+ -- | The number of edges in a graph. -- 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/.@@ -411,9 +447,15 @@ -- edgeCount ('edge' x y) == 1 -- edgeCount == 'length' . 'edgeList' -- @+{-# INLINE [1] edgeCount #-}+{-# RULES "edgeCount/Int" edgeCount = edgeCountInt #-} edgeCount :: Ord a => NonEmptyGraph a -> Int-edgeCount = AM.edgeCount . C.toGraph+edgeCount = T.edgeCount +-- | Like 'edgeCount' but specialised for graphs with vertices of type 'Int'.+edgeCountInt :: NonEmptyGraph Int -> Int+edgeCountInt = AIM.edgeCount . T.toAdjacencyIntMap+ -- | The sorted list of vertices of a given graph. -- Complexity: /O(s * log(n))/ time and /O(n)/ memory. --@@ -421,9 +463,15 @@ -- vertexList1 ('vertex' x) == x ':|' [] -- vertexList1 . 'vertices1' == 'Data.List.NonEmpty.nub' . 'Data.List.NonEmpty.sort' -- @+{-# RULES "vertexList1/Int" vertexList1 = vertexIntList1 #-}+{-# INLINE [1] vertexList1 #-} vertexList1 :: Ord a => NonEmptyGraph a -> NonEmpty a-vertexList1 = NonEmpty.fromList . G.vertexList . H.toGraph+vertexList1 = NonEmpty.fromList . Set.toAscList . vertexSet +-- | Like 'vertexList1' but specialised for NonEmptyGraph with vertices of type 'Int'.+vertexIntList1 :: NonEmptyGraph Int -> NonEmpty Int+vertexIntList1 = NonEmpty.fromList . IntSet.toAscList . vertexIntSet+ -- | The sorted list of edges of a graph. -- Complexity: /O(s + m * log(m))/ time and /O(m)/ memory. Note that the number of -- edges /m/ of a graph can be quadratic with respect to the expression size /s/.@@ -435,9 +483,15 @@ -- edgeList . 'edges1' == 'Data.List.nub' . 'Data.List.sort' . 'Data.List.NonEmpty.toList' -- edgeList . 'transpose' == 'Data.List.sort' . map 'Data.Tuple.swap' . edgeList -- @+{-# RULES "edgeList/Int" edgeList = edgeIntList #-}+{-# INLINE [1] edgeList #-} edgeList :: Ord a => NonEmptyGraph a -> [(a, a)]-edgeList = AM.edgeList . C.toGraph+edgeList = T.edgeList +-- | Like 'edgeList' but specialised for NonEmptyGraph with vertices of type 'Int'.+edgeIntList :: NonEmptyGraph Int -> [(Int,Int)]+edgeIntList = AIM.edgeList . T.toAdjacencyIntMap+ -- | The set of vertices of a given graph. -- Complexity: /O(s * log(n))/ time and /O(n)/ memory. --@@ -447,7 +501,7 @@ -- vertexSet . 'clique1' == Set.'Set.fromList' . 'Data.List.NonEmpty.toList' -- @ vertexSet :: Ord a => NonEmptyGraph a -> Set.Set a-vertexSet = AM.vertexSet . C.toGraph+vertexSet = T.vertexSet -- | The set of vertices of a given graph. Like 'vertexSet' but specialised for -- graphs with vertices of type 'Int'.@@ -459,7 +513,7 @@ -- vertexIntSet . 'clique1' == IntSet.'IntSet.fromList' . 'Data.List.NonEmpty.toList' -- @ vertexIntSet :: NonEmptyGraph Int -> IntSet.IntSet-vertexIntSet = IAM.vertexIntSet . C.toGraph+vertexIntSet = T.vertexIntSet -- | The set of edges of a given graph. -- Complexity: /O(s * log(m))/ time and /O(m)/ memory.@@ -470,7 +524,7 @@ -- edgeSet . 'edges1' == Set.'Set.fromList' . 'Data.List.NonEmpty.toList' -- @ edgeSet :: Ord a => NonEmptyGraph a -> Set.Set (a, a)-edgeSet = R.edgeSet . C.toGraph+edgeSet = T.edgeSet -- | The /path/ on a list of vertices. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the@@ -510,6 +564,7 @@ -- @ clique1 :: NonEmpty a -> NonEmptyGraph a clique1 = connects1 . fmap vertex+{-# NOINLINE [1] clique1 #-} -- | The /biclique/ on two lists of vertices. -- Complexity: /O(L1 + L2)/ time, memory and size, where /L1/ and /L2/ are the@@ -534,20 +589,22 @@ star :: a -> [a] -> NonEmptyGraph a star x [] = vertex x star x (y:ys) = connect (vertex x) (vertices1 $ y :| ys)+{-# INLINE star #-} --- | 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 non-empty list of 'star's.+-- 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] == 'edges1' ((y,x) ':|' [(z,x)])--- starTranspose x ys == 'transpose' ('star' x 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) -- @-starTranspose :: a -> [a] -> NonEmptyGraph a-starTranspose x [] = vertex x-starTranspose x (y:ys) = connect (vertices1 $ y :| ys) (vertex x)+stars1 :: NonEmpty (a, [a]) -> NonEmptyGraph a+stars1 = overlays1 . fmap (uncurry star)+{-# INLINE stars1 #-} -- | The /tree graph/ constructed from a given 'Tree.Tree' data structure. -- Complexity: /O(T)/ time, memory and size, where /T/ is the size of the@@ -575,14 +632,34 @@ -- , ((3,\'a\'),(3,\'b\')) ]) -- @ mesh1 :: NonEmpty a -> NonEmpty b -> NonEmptyGraph (a, b)-mesh1 xs ys = path1 xs `box` path1 ys+mesh1 xx@(x:|xs) yy@(y:|ys) =+ case NonEmpty.nonEmpty ipxs of+ Nothing ->+ case NonEmpty.nonEmpty ipys of+ Nothing -> vertex (x,y)+ Just ipys' ->+ stars1 $ fmap (\(y1,y2) -> ((x,y1), [(x,y2)]) ) ipys'+ Just ipxs' ->+ case NonEmpty.nonEmpty ipys of+ Nothing ->+ stars1 $ fmap (\(x1,x2) -> ((x1,y), [(x2,y)]) ) ipxs'+ Just ipys' ->+ stars1 $+ appendNonEmpty (fmap (\((a1,a2),(b1,b2)) -> ((a1, b1), [(a1, b2), (a2, b1)])) $ liftM2 (,) ipxs' ipys') $+ [ ((lx,y1), [(lx,y2)]) | (y1,y2) <- ipys]+ ++ [ ((x1,ly), [(x2,ly)]) | (x1,x2) <- ipxs]+ where+ lx = last xs+ ly = last ys+ ipxs = NonEmpty.init (pairs1 xx)+ ipys = NonEmpty.init (pairs1 yy) -- | Construct a /torus graph/ from two lists of vertices. -- Complexity: /O(L1 * L2)/ time, memory and size, where /L1/ and /L2/ are the -- lengths of the given lists. -- -- @--- torus1 (x ':|' []) (y ':|' []) == 'edge' (x, y) (x, y)+-- 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\'))@@ -590,8 +667,16 @@ -- , ((2,\'b\'),(1,\'b\')), ((2,\'b\'),(2,\'a\')) ]) -- @ torus1 :: NonEmpty a -> NonEmpty b -> NonEmptyGraph (a, b)-torus1 xs ys = circuit1 xs `box` circuit1 ys+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'+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+appendNonEmpty :: NonEmpty a -> [a] -> NonEmpty a+appendNonEmpty (w:|ws) zs = w :| (ws++zs)+ -- | Remove a vertex from a given graph. Returns @Nothing@ if the resulting -- graph is empty. -- Complexity: /O(s)/ time, memory and size.@@ -603,6 +688,7 @@ -- 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 x = induce1 (/= x) @@ -616,17 +702,17 @@ -- 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 s t = filterContext s (/=s) (/=t) -- TODO: Export--- TODO: Here if @context (==s) g == Just ctx@ then we know for sure that--- @induce1 (/=s) g == Just subgraph@. Can we exploit this?+{-# SPECIALISE filterContext :: Int -> (Int -> Bool) -> (Int -> Bool) -> NonEmptyGraph Int -> NonEmptyGraph Int #-} filterContext :: Eq a => a -> (a -> Bool) -> (a -> Bool) -> NonEmptyGraph a -> NonEmptyGraph a-filterContext s i o g = maybe g go $ context (==s) g+filterContext s i o g = maybe g go $ G.context (==s) (T.toGraph g) where- go (Context is os) = G.induce (/=s) (C.toGraph g) `overlay1`- starTranspose s (filter i is) `overlay` star s (filter o os)+ go (G.Context is os) = G.induce (/=s) (T.toGraph g) `overlay1`+ transpose (star s (filter i is)) `overlay` star s (filter o os) -- | 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.@@ -637,6 +723,7 @@ -- 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 u v = fmap $ \w -> if w == u then v else w @@ -663,6 +750,7 @@ -- 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 v us g = g >>= \w -> if w == v then vertices1 us else vertex w @@ -678,7 +766,20 @@ -- @ transpose :: NonEmptyGraph a -> NonEmptyGraph a transpose = foldg1 vertex overlay (flip connect)+{-# 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 (NonEmpty.reverse (fmap transpose xs))++"transpose/vertices1" forall xs. transpose (vertices1 xs) = vertices1 xs+"transpose/clique1" forall xs. transpose (clique1 xs) = clique1 (NonEmpty.reverse xs)+ #-}+ -- | Construct the /induced subgraph/ of a given graph by removing the -- vertices that do not satisfy a given predicate. Returns @Nothing@ if the -- resulting graph is empty.@@ -692,7 +793,14 @@ -- induce1 p '>=>' induce1 q == induce1 (\\x -> p x && q x) -- @ induce1 :: (a -> Bool) -> NonEmptyGraph a -> Maybe (NonEmptyGraph a)-induce1 p = toNonEmptyGraph . G.induce p . C.toGraph+induce1 p = foldg1+ (\x -> if p x then Just (Vertex x) else Nothing)+ (k Overlay)+ (k Connect)+ where+ k _ Nothing a = a+ k _ a Nothing = a+ k f (Just a) (Just b) = Just $ f a b -- | Simplify a graph expression. Semantically, this is the identity function, -- but it simplifies a given expression according to the laws of the algebra.@@ -709,9 +817,11 @@ -- 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 = foldg1 Vertex (simple Overlay) (simple Connect) +{-# 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@@ -749,6 +859,32 @@ where xs = fmap (\b -> fmap (,b) x) $ toNonEmpty y ys = fmap (\a -> fmap (a,) y) $ toNonEmpty x++-- | /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+-- reachability relation between the original vertices. Sparsification is useful+-- when working with dense graphs, as it can reduce the number of edges from+-- O(n^2) down to O(n) by replacing cliques, bicliques and similar densely+-- connected structures by sparse subgraphs built out of intermediate vertices.+-- Complexity: O(s) time, memory and size.+--+-- @+-- 'Data.List.sort' . 'Algebra.Graph.ToGraph.reachable' x == 'Data.List.sort' . 'Data.Either.rights' . 'Algebra.Graph.ToGraph.reachable' ('Data.Either.Right' x) . sparsify+-- 'vertexCount' (sparsify x) <= 'vertexCount' x + 'size' x + 1+-- 'edgeCount' (sparsify x) <= 3 * 'size' x+-- 'size' (sparsify x) <= 3 * 'size' x+-- @+sparsify :: NonEmptyGraph a -> NonEmptyGraph (Either Int a)+sparsify graph = res+ where+ (res, end) = runState (foldg1 v o c graph 0 end) 1+ v x s t = return $ clique1 (Left s :| [Right x, Left t])+ o x y s t = overlay <$> s `x` t <*> s `y` t+ c x y s t = do+ 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
src/Algebra/Graph/Relation.hs view
@@ -20,17 +20,16 @@ -- * Basic graph construction primitives empty, vertex, edge, overlay, connect, vertices, edges, overlays, connects,- fromAdjacencyList, -- * Relations on graphs isSubgraphOf, -- * Graph properties isEmpty, hasVertex, hasEdge, vertexCount, edgeCount, vertexList, edgeList,- vertexSet, vertexIntSet, edgeSet, preSet, postSet,+ adjacencyList, vertexSet, vertexIntSet, edgeSet, preSet, postSet, -- * Standard families of graphs- path, circuit, clique, biclique, star, starTranspose, tree, forest,+ path, circuit, clique, biclique, star, stars, tree, forest, -- * Graph transformation removeVertex, removeEdge, replaceVertex, mergeVertices, transpose, gmap, induce,@@ -42,38 +41,14 @@ import Prelude () import Prelude.Compat +import Data.Tree import Data.Tuple import Algebra.Graph.Relation.Internal -import qualified Algebra.Graph.Class as C-import qualified Data.IntSet as IntSet-import qualified Data.Set as Set-import qualified Data.Tree as Tree---- | Construct the /empty graph/.--- Complexity: /O(1)/ time and memory.------ @--- 'isEmpty' empty == True--- 'hasVertex' x empty == False--- 'vertexCount' empty == 0--- 'edgeCount' empty == 0--- @-empty :: Ord a => Relation a-empty = C.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 :: Ord a => a -> Relation a-vertex = C.vertex+import qualified Data.IntSet as IntSet+import qualified Data.Set as Set+import qualified Data.Tree as Tree -- | Construct the graph comprising /a single edge/. -- Complexity: /O(1)/ time, memory and size.@@ -86,45 +61,7 @@ -- 'vertexCount' (edge 1 2) == 2 -- @ edge :: Ord a => a -> a -> Relation a-edge = C.edge---- | /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 => Relation a -> Relation a -> Relation a-overlay = C.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 => Relation a -> Relation a -> Relation a-connect = C.connect+edge x y = Relation (Set.fromList [x, y]) (Set.singleton (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@@ -162,7 +99,7 @@ -- 'isEmpty' . overlays == 'all' 'isEmpty' -- @ overlays :: Ord a => [Relation a] -> Relation a-overlays = C.overlays+overlays xs = Relation (Set.unions $ map domain xs) (Set.unions $ map relation xs) -- | Connect a given list of graphs. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -175,22 +112,7 @@ -- 'isEmpty' . connects == 'all' 'isEmpty' -- @ connects :: Ord a => [Relation a] -> Relation a-connects = C.connects---- | Construct a graph from an adjacency list.--- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.------ @--- fromAdjacencyList [] == 'empty'--- fromAdjacencyList [(x, [])] == 'vertex' x--- fromAdjacencyList [(x, [y])] == 'edge' x y--- 'overlay' (fromAdjacencyList xs) (fromAdjacencyList ys) == fromAdjacencyList (xs ++ ys)--- @-fromAdjacencyList :: Ord a => [(a, [a])] -> Relation a-fromAdjacencyList as = Relation (Set.fromList vs) (Set.fromList es)- where- vs = concatMap (uncurry (:)) as- es = [ (x, y) | (x, ys) <- as, y <- ys ]+connects = foldr connect empty -- | The 'isSubgraphOf' function takes two graphs and returns 'True' if the -- first graph is a /subgraph/ of the second.@@ -329,7 +251,24 @@ edgeSet :: Relation a -> Set.Set (a, a) edgeSet = relation --- | The /preset/ (here 'preSet') of an element @x@ is the set of elements that are related to+-- | The sorted /adjacency list/ of a graph.+-- Complexity: /O(n + m)/ time and /O(m)/ memory.+--+-- @+-- adjacencyList 'empty' == []+-- adjacencyList ('vertex' x) == [(x, [])]+-- adjacencyList ('edge' 1 2) == [(1, [2]), (2, [])]+-- adjacencyList ('star' 2 [3,1]) == [(1, []), (2, [1,3]), (3, [])]+-- 'stars' . adjacencyList == id+-- @+adjacencyList :: Eq a => Relation a -> [(a, [a])]+adjacencyList r = go (Set.toAscList $ domain r) (Set.toAscList $ relation r)+ where+ go [] _ = []+ go vs [] = map ((,[])) vs+ go (x:vs) es = let (ys, zs) = span ((==x) . fst) es in (x, map snd ys) : go vs zs++-- | The /preset/ of an element @x@ is the set of elements that are related to -- it on the /left/, i.e. @preSet x == { a | aRx }@. In the context of directed -- graphs, this corresponds to the set of /direct predecessors/ of vertex @x@. -- Complexity: /O(n + m)/ time and /O(n)/ memory.@@ -343,7 +282,7 @@ preSet :: Ord a => a -> Relation a -> Set.Set a preSet x = Set.mapMonotonic fst . Set.filter ((== x) . snd) . relation --- | The /postset/ (here 'postSet') of an element @x@ is the set of elements that are related to+-- | The /postset/ of an element @x@ is the set of elements that are related to -- it on the /right/, i.e. @postSet x == { a | xRa }@. In the context of directed -- graphs, this corresponds to the set of /direct successors/ of vertex @x@. -- Complexity: /O(n + m)/ time and /O(n)/ memory.@@ -367,7 +306,9 @@ -- path . 'reverse' == 'transpose' . path -- @ path :: Ord a => [a] -> Relation a-path = C.path+path xs = case xs of [] -> empty+ [x] -> vertex x+ (_:ys) -> edges (zip xs ys) -- | The /circuit/ on a list of vertices. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -379,7 +320,8 @@ -- circuit . 'reverse' == 'transpose' . circuit -- @ circuit :: Ord a => [a] -> Relation a-circuit = C.circuit+circuit [] = empty+circuit (x:xs) = path $ [x] ++ xs ++ [x] -- | The /clique/ on a list of vertices. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -393,7 +335,12 @@ -- clique . 'reverse' == 'transpose' . clique -- @ clique :: Ord a => [a] -> Relation a-clique = C.clique+clique xs = Relation (Set.fromList xs) (fst $ go xs)+ where+ go [] = (Set.empty, Set.empty)+ go (x:xs) = (Set.union res (Set.map (x,) set), Set.insert x set)+ where+ (res, set) = go xs -- | The /biclique/ on two lists of vertices. -- Complexity: /O(n * log(n) + m)/ time and /O(n + m)/ memory.@@ -411,6 +358,7 @@ x = Set.fromList xs y = Set.fromList 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. --@@ -421,21 +369,28 @@ -- star x ys == 'connect' ('vertex' x) ('vertices' ys) -- @ star :: Ord a => a -> [a] -> Relation a-star = C.star+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 * log(n))/ 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 :: Ord a => a -> [a] -> Relation a-starTranspose = C.starTranspose+stars :: Ord a => [(a, [a])] -> Relation a+stars as = Relation (Set.fromList vs) (Set.fromList es)+ where+ vs = concatMap (uncurry (:)) as+ es = [ (x, y) | (x, ys) <- as, y <- ys ] -- | The /tree graph/ constructed from a given 'Tree.Tree' data structure. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -447,7 +402,9 @@ -- tree (Node 1 [Node 2 [], Node 3 [Node 4 [], Node 5 []]]) == 'edges' [(1,2), (1,3), (3,4), (3,5)] -- @ tree :: Ord a => Tree.Tree a -> Relation a-tree = C.tree+tree (Node x []) = vertex x+tree (Node x f ) = star x (map rootLabel f)+ `overlay` forest (filter (not . null . subForest) f) -- | The /forest graph/ constructed from a given 'Tree.Forest' data structure. -- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.@@ -459,7 +416,7 @@ -- forest == 'overlays' . map 'tree' -- @ forest :: Ord a => Tree.Forest a -> Relation a-forest = C.forest+forest = overlays. map tree -- | Remove a vertex from a given graph. -- Complexity: /O(n + m)/ time.@@ -480,7 +437,7 @@ -- Complexity: /O(log(m))/ time. -- -- @--- removeEdge x y ('AdjacencyMap.edge' x y) == 'vertices' [x, y]+-- removeEdge x y ('AdjacencyMap.edge' 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
src/Algebra/Graph/Relation/Internal.hs view
@@ -12,15 +12,16 @@ ----------------------------------------------------------------------------- module Algebra.Graph.Relation.Internal ( -- * Binary relation implementation- Relation (..), consistent, setProduct, referredToVertexSet+ Relation (..), empty, vertex, overlay, connect, setProduct, consistent,+ referredToVertexSet ) where import Data.Set (Set, union) -import Algebra.Graph.Class- import qualified Data.Set as Set +import Control.DeepSeq (NFData, rnf)+ {-| The 'Relation' data type represents a graph as a /binary relation/. We define a 'Num' instance as a convenient notation for working with graphs: @@ -102,14 +103,72 @@ eshow xs = "edges " ++ show xs used = referredToVertexSet r -instance Ord a => Graph (Relation a) where- type Vertex (Relation a) = a- empty = Relation Set.empty Set.empty- vertex x = Relation (Set.singleton x) Set.empty- overlay x y = Relation (domain x `union` domain y) (relation x `union` relation y)- connect x y = Relation (domain x `union` domain y) (relation x `union` relation y- `union` (domain x `setProduct` domain y))+-- | Construct the /empty graph/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'Algebra.Graph.Relation.isEmpty' empty == True+-- 'Algebra.Graph.Relation.hasVertex' x empty == False+-- 'Algebra.Graph.Relation.vertexCount' empty == 0+-- 'Algebra.Graph.Relation.edgeCount' empty == 0+-- @+empty :: Relation a+empty = Relation Set.empty Set.empty +-- | Construct the graph comprising /a single isolated vertex/.+-- Complexity: /O(1)/ time and memory.+--+-- @+-- 'Algebra.Graph.Relation.isEmpty' (vertex x) == False+-- 'Algebra.Graph.Relation.hasVertex' x (vertex x) == True+-- 'Algebra.Graph.Relation.vertexCount' (vertex x) == 1+-- 'Algebra.Graph.Relation.edgeCount' (vertex x) == 0+-- @+vertex :: a -> Relation a+vertex x = Relation (Set.singleton x) Set.empty++-- | /Overlay/ two graphs. This is a commutative, associative and idempotent+-- operation with the identity 'empty'.+-- Complexity: /O((n + m) * log(n))/ time and /O(n + m)/ memory.+--+-- @+-- 'Algebra.Graph.Relation.isEmpty' (overlay x y) == 'Algebra.Graph.Relation.isEmpty' x && 'iAlgebra.Graph.Relation.sEmpty' y+-- 'Algebra.Graph.Relation.hasVertex' z (overlay x y) == 'Algebra.Graph.Relation.hasVertex' z x || 'Algebra.Graph.Relation.hasVertex' z y+-- 'Algebra.Graph.Relation.vertexCount' (overlay x y) >= 'Algebra.Graph.Relation.vertexCount' x+-- 'Algebra.Graph.Relation.vertexCount' (overlay x y) <= 'Algebra.Graph.Relation.vertexCount' x + 'Algebra.Graph.Relation.vertexCount' y+-- 'Algebra.Graph.Relation.edgeCount' (overlay x y) >= 'Algebra.Graph.Relation.edgeCount' x+-- 'Algebra.Graph.Relation.edgeCount' (overlay x y) <= 'Algebra.Graph.Relation.edgeCount' x + 'Algebra.Graph.Relation.edgeCount' y+-- 'Algebra.Graph.Relation.vertexCount' (overlay 1 2) == 2+-- 'Algebra.Graph.Relation.edgeCount' (overlay 1 2) == 0+-- @+overlay :: Ord a => Relation a -> Relation a -> Relation a+overlay x y = Relation (domain x `union` domain y) (relation x `union` relation y)++-- | /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.Relation.isEmpty' (connect x y) == 'Algebra.Graph.Relation.isEmpty' x && 'Algebra.Graph.Relation.isEmpty' y+-- 'Algebra.Graph.Relation.hasVertex' z (connect x y) == 'Algebra.Graph.Relation.hasVertex' z x || 'Algebra.Graph.Relation.hasVertex' z y+-- 'Algebra.Graph.Relation.vertexCount' (connect x y) >= 'Algebra.Graph.Relation.vertexCount' x+-- 'Algebra.Graph.Relation.vertexCount' (connect x y) <= 'Algebra.Graph.Relation.vertexCount' x + 'Algebra.Graph.Relation.vertexCount' y+-- 'Algebra.Graph.Relation.edgeCount' (connect x y) >= 'Algebra.Graph.Relation.edgeCount' x+-- 'Algebra.Graph.Relation.edgeCount' (connect x y) >= 'Algebra.Graph.Relation.edgeCount' y+-- 'Algebra.Graph.Relation.edgeCount' (connect x y) >= 'Algebra.Graph.Relation.vertexCount' x * 'Algebra.Graph.Relation.vertexCount' y+-- 'Algebra.Graph.Relation.edgeCount' (connect x y) <= 'Algebra.Graph.Relation.vertexCount' x * 'Algebra.Graph.Relation.vertexCount' y + 'Algebra.Graph.Relation.edgeCount' x + 'Algebra.Graph.Relation.edgeCount' y+-- 'Algebra.Graph.Relation.vertexCount' (connect 1 2) == 2+-- 'Algebra.Graph.Relation.edgeCount' (connect 1 2) == 1+-- @+connect :: Ord a => Relation a -> Relation a -> Relation a+connect x y = Relation (domain x `union` domain y)+ (relation x `union` relation y `union` (domain x `setProduct` domain y))++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 ]@@ -122,10 +181,6 @@ abs = id negate = id -instance ToGraph (Relation a) where- type ToVertex (Relation a) = a- toGraph (Relation d r) = vertices (Set.toList d) `overlay` edges (Set.toList r)- -- | Check if the internal representation of a relation is consistent, i.e. if all -- pairs of elements in the 'relation' refer to existing elements in the 'domain'. -- It should be impossible to create an inconsistent 'Relation', and we use this@@ -133,14 +188,13 @@ -- /Note: this function is for internal use only/. -- -- @--- consistent 'Algebra.Graph.Relation.empty' == True--- consistent ('Algebra.Graph.Relation.vertex' x) == True--- consistent ('Algebra.Graph.Relation.overlay' x y) == True--- consistent ('Algebra.Graph.Relation.connect' x y) == True--- consistent ('Algebra.Graph.Relation.edge' x y) == True--- consistent ('Algebra.Graph.Relation.edges' xs) == True--- consistent ('Algebra.Graph.Relation.graph' xs ys) == True--- consistent ('Algebra.Graph.Relation.fromAdjacencyList' xs) == True+-- consistent 'Algebra.Graph.Relation.empty' == True+-- consistent ('Algebra.Graph.Relation.vertex' x) == True+-- consistent ('Algebra.Graph.Relation.overlay' x y) == True+-- consistent ('Algebra.Graph.Relation.connect' x y) == True+-- consistent ('Algebra.Graph.Relation.edge' x y) == True+-- consistent ('Algebra.Graph.Relation.edges' xs) == True+-- consistent ('Algebra.Graph.Relation.stars' xs) == True -- @ consistent :: Ord a => Relation a -> Bool consistent (Relation d r) = referredToVertexSet r `Set.isSubsetOf` d
src/Algebra/Graph/Relation/InternalDerived.hs view
@@ -18,6 +18,9 @@ PreorderRelation (..) ) where ++import Control.DeepSeq (NFData (..))+ import Algebra.Graph.Class import Algebra.Graph.Relation (Relation, reflexiveClosure, symmetricClosure, transitiveClosure, preorderClosure)@@ -34,7 +37,7 @@ show (1 * 2 :: ReflexiveRelation Int) == "edges [(1,1),(1,2),(2,2)]"@ -} newtype ReflexiveRelation a = ReflexiveRelation { fromReflexive :: Relation a }- deriving Num+ deriving (Num, NFData) instance Ord a => Eq (ReflexiveRelation a) where x == y = reflexiveClosure (fromReflexive x) == reflexiveClosure (fromReflexive y)@@ -65,7 +68,7 @@ show (1 * 2 :: SymmetricRelation Int) == "edges [(1,2),(2,1)]"@ -} newtype SymmetricRelation a = SymmetricRelation { fromSymmetric :: Relation a }- deriving Num+ deriving (Num, NFData) instance Ord a => Eq (SymmetricRelation a) where x == y = symmetricClosure (fromSymmetric x) == symmetricClosure (fromSymmetric y)@@ -100,7 +103,7 @@ show (1 * 2 + 2 * 3 :: TransitiveRelation Int) == "edges [(1,2),(1,3),(2,3)]"@ -} newtype TransitiveRelation a = TransitiveRelation { fromTransitive :: Relation a }- deriving Num+ deriving (Num, NFData) instance Ord a => Eq (TransitiveRelation a) where x == y = transitiveClosure (fromTransitive x) == transitiveClosure (fromTransitive y)@@ -140,7 +143,7 @@ show (1 * 2 + 2 * 3 :: PreorderRelation Int) == "edges [(1,1),(1,2),(1,3),(2,2),(2,3),(3,3)]"@ -} newtype PreorderRelation a = PreorderRelation { fromPreorder :: Relation a }- deriving Num+ deriving (Num, NFData) instance (Ord a, Show a) => Show (PreorderRelation a) where show = show . preorderClosure . fromPreorder
+ src/Algebra/Graph/ToGraph.hs view
@@ -0,0 +1,452 @@+{-# LANGUAGE ConstrainedClassMethods #-}+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.ToGraph+-- 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 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.+--+-----------------------------------------------------------------------------+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++-- | The 'ToGraph' type class captures data types that can be converted to+-- algebraic graphs.+class ToGraph t where+ {-# MINIMAL toGraph | foldg #-}+ type ToVertex t++ -- | Convert a value to the corresponding algebraic graph, see "Algebra.Graph".+ --+ -- @+ -- toGraph == 'foldg' 'G.Empty' 'G.Vertex' 'G.Overlay' 'G.Connect'+ -- @+ toGraph :: t -> G.Graph (ToVertex t)+ toGraph = foldg G.Empty G.Vertex G.Overlay G.Connect++ -- | The method 'foldg' is used for generalised graph folding. It collapses+ -- a given value by applying the provided graph construction primitives. The+ -- order of arguments is: empty, vertex, overlay and connect, and it is+ -- assumed that the arguments satisfy the axioms of the graph algebra.+ --+ -- @+ -- foldg == Algebra.Graph.'G.foldg' . 'toGraph'+ -- @+ foldg :: r -> (ToVertex t -> r) -> (r -> r -> r) -> (r -> r -> r) -> t -> r+ foldg e v o c = G.foldg e v o c . toGraph++ -- | Check if a graph is empty.+ --+ -- @+ -- isEmpty == 'foldg' True (const False) (&&) (&&)+ -- @+ isEmpty :: t -> Bool+ isEmpty = foldg True (const False) (&&) (&&)++ -- | The /size/ of a graph, i.e. the number of leaves of the expression+ -- including 'empty' leaves.+ --+ -- @+ -- size == 'foldg' 1 (const 1) (+) (+)+ -- @+ size :: t -> Int+ size = foldg 1 (const 1) (+) (+)++ -- | Check if a graph contains a given vertex.+ --+ -- @+ -- hasVertex x == 'foldg' False (==x) (||) (||)+ -- @+ hasVertex :: Eq (ToVertex t) => ToVertex t -> t -> Bool+ hasVertex x = foldg False (==x) (||) (||)++ -- | Check if a graph contains a given edge.+ --+ -- @+ -- hasEdge x y == Algebra.Graph.'G.hasEdge' x y . 'toGraph'+ -- @+ hasEdge :: Eq (ToVertex t) => ToVertex t -> ToVertex t -> t -> Bool+ hasEdge x y = G.hasEdge x y . toGraph++ -- | The number of vertices in a graph.+ --+ -- @+ -- vertexCount == Set.'Set.size' . 'vertexSet'+ -- @+ vertexCount :: Ord (ToVertex t) => t -> Int+ vertexCount = Set.size . vertexSet++ -- | The number of edges in a graph.+ --+ -- @+ -- edgeCount == Set.'Set.size' . 'edgeSet'+ -- @+ edgeCount :: Ord (ToVertex t) => t -> Int+ edgeCount = AM.edgeCount . toAdjacencyMap++ -- | The sorted list of vertices of a given graph.+ --+ -- @+ -- vertexList == Set.'Set.toAscList' . 'vertexSet'+ -- @+ vertexList :: Ord (ToVertex t) => t -> [ToVertex t]+ vertexList = Set.toAscList . vertexSet++ -- | The sorted list of edges of a graph.+ --+ -- @+ -- edgeList == Set.'Set.toAscList' . 'edgeSet'+ -- @+ edgeList :: Ord (ToVertex t) => t -> [(ToVertex t, ToVertex t)]+ edgeList = AM.edgeList . toAdjacencyMap++ -- | The set of vertices of a graph.+ --+ -- @+ -- vertexSet == 'foldg' Set.'Set.empty' Set.'Set.singleton' Set.'Set.union' Set.'Set.union'+ -- @+ vertexSet :: Ord (ToVertex t) => t -> Set (ToVertex t)+ vertexSet = foldg Set.empty Set.singleton Set.union Set.union++ -- | The set of vertices of a graph. Like 'vertexSet' but specialised for+ -- graphs with vertices of type 'Int'.+ --+ -- @+ -- vertexIntSet == 'foldg' IntSet.'IntSet.empty' IntSet.'IntSet.singleton' IntSet.'IntSet.union' IntSet.'IntSet.union'+ -- @+ vertexIntSet :: ToVertex t ~ Int => t -> IntSet+ vertexIntSet = foldg IntSet.empty IntSet.singleton IntSet.union IntSet.union++ -- | The set of edges of a graph.+ --+ -- @+ -- edgeSet == Algebra.Graph.AdjacencyMap.'AM.edgeSet' . 'toAdjacencyMap'+ -- @+ edgeSet :: Ord (ToVertex t) => t -> Set (ToVertex t, ToVertex t)+ edgeSet = AM.edgeSet . toAdjacencyMap++ -- | The /preset/ of a vertex is the set of its /direct predecessors/.+ --+ -- @+ -- preSet x == Algebra.Graph.AdjacencyMap.'AM.preSet' x . 'toAdjacencyMap'+ -- @+ preSet :: Ord (ToVertex t) => ToVertex t -> t -> Set (ToVertex t)+ preSet x = AM.postSet x . toAdjacencyMapTranspose++ -- | The /preset/ (here @preIntSet@) of a vertex is the set of its+ -- /direct predecessors/. Like 'preSet' but specialised for graphs with+ -- vertices of type 'Int'.+ --+ -- @+ -- preIntSet x == Algebra.Graph.AdjacencyIntMap.'AIM.preIntSet' x . 'toAdjacencyIntMap'+ -- @+ preIntSet :: ToVertex t ~ Int => Int -> t -> IntSet+ preIntSet x = AIM.postIntSet x . toAdjacencyIntMapTranspose++ -- | The /postset/ of a vertex is the set of its /direct successors/.+ --+ -- @+ -- postSet x == Algebra.Graph.AdjacencyMap.'AM.postSet' x . 'toAdjacencyMap'+ -- @+ postSet :: Ord (ToVertex t) => ToVertex t -> t -> Set (ToVertex t)+ postSet x = AM.postSet x . toAdjacencyMap++ -- | The /postset/ (here @postIntSet@) of a vertex is the set of its+ -- /direct successors/. Like 'postSet' but specialised for graphs with+ -- vertices of type 'Int'.+ --+ -- @+ -- postIntSet x == Algebra.Graph.AdjacencyIntMap.'AIM.postIntSet' x . 'toAdjacencyIntMap'+ -- @+ postIntSet :: ToVertex t ~ Int => Int -> t -> IntSet+ postIntSet x = AIM.postIntSet x . toAdjacencyIntMap++ -- | The sorted /adjacency list/ of a graph.+ --+ -- @+ -- adjacencyList == Algebra.Graph.AdjacencyMap.'AM.adjacencyList' . 'toAdjacencyMap'+ -- @+ adjacencyList :: Ord (ToVertex t) => t -> [(ToVertex t, [ToVertex t])]+ adjacencyList = AM.adjacencyList . toAdjacencyMap++ -- | The /adjacency map/ of a graph: each vertex is associated with a set+ -- of its /direct successors/.+ --+ -- @+ -- adjacencyMap == Algebra.Graph.AdjacencyMap.'Algebra.Graph.AdjacencyMap.adjacencyMap' . 'toAdjacencyMap'+ -- @+ adjacencyMap :: Ord (ToVertex t) => t -> Map (ToVertex t) (Set (ToVertex t))+ adjacencyMap = AM.adjacencyMap . toAdjacencyMap++ -- | The /adjacency map/ of a graph: each vertex is associated with a set+ -- of its /direct successors/. Like 'adjacencyMap' but specialised for+ -- graphs with vertices of type 'Int'.+ --+ -- @+ -- adjacencyIntMap == Algebra.Graph.AdjacencyIntMap.'Algebra.Graph.AdjacencyIntMap.adjacencyIntMap' . 'toAdjacencyIntMap'+ -- @+ adjacencyIntMap :: ToVertex t ~ Int => t -> IntMap IntSet+ adjacencyIntMap = AIM.adjacencyIntMap . toAdjacencyIntMap++ -- | The transposed /adjacency map/ of a graph: each vertex is associated+ -- with a set of its /direct predecessors/.+ --+ -- @+ -- adjacencyMapTranspose == Algebra.Graph.AdjacencyMap.'Algebra.Graph.AdjacencyMap.adjacencyMap' . 'toAdjacencyMapTranspose'+ -- @+ adjacencyMapTranspose :: Ord (ToVertex t) => t -> Map (ToVertex t) (Set (ToVertex t))+ adjacencyMapTranspose = AM.adjacencyMap . toAdjacencyMapTranspose++ -- | The transposed /adjacency map/ of a graph: each vertex is associated+ -- with a set of its /direct predecessors/. Like 'adjacencyMapTranspose' but+ -- specialised for graphs with vertices of type 'Int'.+ --+ -- @+ -- adjacencyIntMapTranspose == Algebra.Graph.AdjacencyIntMap.'Algebra.Graph.AdjacencyIntMap.adjacencyIntMap' . 'toAdjacencyIntMapTranspose'+ -- @+ adjacencyIntMapTranspose :: ToVertex t ~ Int => t -> IntMap IntSet+ adjacencyIntMapTranspose = AIM.adjacencyIntMap . toAdjacencyIntMapTranspose++ -- | 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 == Algebra.Graph.AdjacencyMap.'AM.dfsForest' . toAdjacencyMap+ -- @+ dfsForest :: Ord (ToVertex t) => t -> Forest (ToVertex t)+ dfsForest = AM.dfsForest . toAdjacencyMap++ -- | 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 == Algebra.Graph.AdjacencyMap.'AM.dfsForestFrom' vs . toAdjacencyMap+ -- @+ dfsForestFrom :: Ord (ToVertex t) => [ToVertex t] -> t -> Forest (ToVertex t)+ dfsForestFrom vs = AM.dfsForestFrom vs . toAdjacencyMap++ -- | 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 == Algebra.Graph.AdjacencyMap.'AM.dfs' vs . toAdjacencyMap+ -- @+ dfs :: Ord (ToVertex t) => [ToVertex t] -> t -> [ToVertex t]+ dfs vs = AM.dfs vs . toAdjacencyMap++ -- | 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 == Algebra.Graph.AdjacencyMap.'AM.reachable' x . toAdjacencyMap+ -- @+ reachable :: Ord (ToVertex t) => ToVertex t -> t -> [ToVertex t]+ reachable x = AM.reachable x . toAdjacencyMap++ -- | Compute the /topological sort/ of a graph or return @Nothing@ if the+ -- graph is cyclic.+ --+ -- @+ -- topSort == Algebra.Graph.AdjacencyMap.'AM.topSort' . toAdjacencyMap+ -- @+ topSort :: Ord (ToVertex t) => t -> Maybe [ToVertex t]+ topSort = AM.topSort . toAdjacencyMap++ -- | Check if a given graph is /acyclic/.+ --+ -- @+ -- isAcyclic == Algebra.Graph.AdjacencyMap.'AM.isAcyclic' . toAdjacencyMap+ -- @+ isAcyclic :: Ord (ToVertex t) => t -> Bool+ isAcyclic = AM.isAcyclic . toAdjacencyMap++ -- | Convert a value to the corresponding 'AM.AdjacencyMap'.+ --+ -- @+ -- toAdjacencyMap == 'foldg' 'AM.empty' 'AM.vertex' 'AM.overlay' 'AM.connect'+ -- @+ toAdjacencyMap :: Ord (ToVertex t) => t -> AM.AdjacencyMap (ToVertex t)+ toAdjacencyMap = foldg AM.empty AM.vertex AM.overlay AM.connect++ -- | Convert a value to the corresponding 'AM.AdjacencyMap' and transpose the+ -- result.+ --+ -- @+ -- 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)++ -- | Convert a value to the corresponding 'AIM.AdjacencyIntMap'.+ --+ -- @+ -- toAdjacencyIntMap == 'foldg' 'AIM.empty' 'AIM.vertex' 'AIM.overlay' 'AIM.connect'+ -- @+ toAdjacencyIntMap :: ToVertex t ~ Int => t -> AIM.AdjacencyIntMap+ toAdjacencyIntMap = foldg AIM.empty AIM.vertex AIM.overlay AIM.connect++ -- | Convert a value to the corresponding 'AIM.AdjacencyIntMap' and transpose+ -- the result.+ --+ -- @+ -- 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)++ -- | Check if a given forest is a valid /depth-first search/ forest of a+ -- graph.+ --+ -- @+ -- isDfsForestOf f == Algebra.Graph.AdjacencyMap.'AM.isDfsForestOf' f . toAdjacencyMap+ -- @+ isDfsForestOf :: Ord (ToVertex t) => Forest (ToVertex t) -> t -> Bool+ isDfsForestOf f = AM.isDfsForestOf f . toAdjacencyMap++ -- | Check if a given list of vertices is a valid /topological sort/ of a+ -- graph.+ --+ -- @+ -- isTopSortOf vs == Algebra.Graph.AdjacencyMap.'AM.isTopSortOf' vs . toAdjacencyMap+ -- @+ isTopSortOf :: Ord (ToVertex t) => [ToVertex t] -> t -> Bool+ isTopSortOf vs = AM.isTopSortOf vs . toAdjacencyMap++instance Ord a => ToGraph (G.Graph a) where+ type ToVertex (G.Graph a) = a+ toGraph = id+ foldg = G.foldg+ hasEdge = G.hasEdge++instance Ord a => ToGraph (AM.AdjacencyMap a) where+ type ToVertex (AM.AdjacencyMap a) = a+ toGraph = G.stars+ . map (fmap Set.toList)+ . Map.toList+ . AM.adjacencyMap+ isEmpty = AM.isEmpty+ hasVertex = AM.hasVertex+ hasEdge = AM.hasEdge+ vertexCount = AM.vertexCount+ edgeCount = AM.edgeCount+ vertexList = AM.vertexList+ vertexSet = AM.vertexSet+ vertexIntSet = AM.vertexIntSet+ edgeList = AM.edgeList+ edgeSet = AM.edgeSet+ adjacencyList = AM.adjacencyList+ preSet = AM.preSet+ postSet = AM.postSet+ adjacencyMap = AM.adjacencyMap+ adjacencyIntMap = IntMap.fromAscList+ . map (fmap $ IntSet.fromAscList . Set.toAscList)+ . Map.toAscList+ . AM.adjacencyMap+ dfsForest = AM.dfsForest+ dfsForestFrom = AM.dfsForestFrom+ dfs = AM.dfs+ reachable = AM.reachable+ topSort = AM.topSort+ isAcyclic = AM.isAcyclic+ toAdjacencyMap = id+ toAdjacencyIntMap = AIM.AM . adjacencyIntMap+ toAdjacencyMapTranspose = AM.transpose . toAdjacencyMap+ toAdjacencyIntMapTranspose = AIM.transpose . toAdjacencyIntMap+ isDfsForestOf = AM.isDfsForestOf+ isTopSortOf = AM.isTopSortOf++instance ToGraph AIM.AdjacencyIntMap where+ type ToVertex AIM.AdjacencyIntMap = Int+ toGraph = G.stars+ . map (fmap IntSet.toList)+ . IntMap.toList+ . AIM.adjacencyIntMap+ isEmpty = AIM.isEmpty+ hasVertex = AIM.hasVertex+ hasEdge = AIM.hasEdge+ vertexCount = AIM.vertexCount+ edgeCount = AIM.edgeCount+ vertexList = AIM.vertexList+ vertexSet = Set.fromAscList . IntSet.toAscList . AIM.vertexIntSet+ vertexIntSet = AIM.vertexIntSet+ edgeList = AIM.edgeList+ edgeSet = AIM.edgeSet+ adjacencyList = AIM.adjacencyList+ preIntSet = AIM.preIntSet+ postIntSet = AIM.postIntSet+ adjacencyMap = Map.fromAscList+ . map (fmap $ Set.fromAscList . IntSet.toAscList)+ . IntMap.toAscList+ . AIM.adjacencyIntMap+ dfsForest = AIM.dfsForest+ dfsForestFrom = AIM.dfsForestFrom+ dfs = AIM.dfs+ reachable = AIM.reachable+ topSort = AIM.topSort+ isAcyclic = AIM.isAcyclic+ adjacencyIntMap = AIM.adjacencyIntMap+ toAdjacencyMap = AM.AM . adjacencyMap+ toAdjacencyIntMap = id+ toAdjacencyMapTranspose = AM.transpose . toAdjacencyMap+ toAdjacencyIntMapTranspose = AIM.transpose . toAdjacencyIntMap+ isDfsForestOf = AIM.isDfsForestOf+ isTopSortOf = AIM.isTopSortOf++-- 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+ toGraph r = G.vertices (Set.toList $ R.domain r) `G.overlay`+ G.edges (Set.toList $ R.relation r)+ isEmpty = R.isEmpty+ hasVertex = R.hasVertex+ hasEdge = R.hasEdge+ vertexCount = R.vertexCount+ edgeCount = R.edgeCount+ vertexList = R.vertexList+ vertexSet = R.vertexSet+ vertexIntSet = R.vertexIntSet+ edgeList = R.edgeList+ edgeSet = R.edgeSet+ adjacencyList = R.adjacencyList+ adjacencyMap = Map.fromAscList+ . map (fmap Set.fromAscList)+ . R.adjacencyList+ adjacencyIntMap = IntMap.fromAscList+ . map (fmap IntSet.fromAscList)+ . R.adjacencyList+ toAdjacencyMap = AM.AM . adjacencyMap+ toAdjacencyIntMap = AIM.AM . adjacencyIntMap+ toAdjacencyMapTranspose = AM.transpose . toAdjacencyMap+ toAdjacencyIntMapTranspose = AIM.transpose . toAdjacencyIntMap
+ src/Data/Graph/Typed.hs view
@@ -0,0 +1,160 @@+-----------------------------------------------------------------------------+-- |+-- Module : Data.Graph.Typed+-- Copyright : (c) Anton Lorenzen, Andrey Mokhov 2016-2018+-- License : MIT (see the file LICENSE)+-- Maintainer : anfelor@posteo.de, 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 primitives for interoperability between this library and+-- the "Data.Graph" module of the containers library. It is for internal use only+-- and may be removed without notice at any point.+-----------------------------------------------------------------------------+module Data.Graph.Typed (+ -- * Data type and construction+ GraphKL(..), fromAdjacencyMap, fromAdjacencyIntMap,++ -- * Basic algorithms+ dfsForest, dfsForestFrom, dfs, topSort+ ) where++import Algebra.Graph.AdjacencyMap.Internal as AM+import Algebra.Graph.AdjacencyIntMap.Internal as AIM++import Data.Tree+import Data.Maybe++import qualified Data.Graph as KL+import qualified Data.Map.Strict as Map+import qualified Data.IntMap.Strict as IntMap+import qualified Data.Set as Set+import qualified Data.IntSet as IntSet++-- | 'GraphKL' encapsulates King-Launchbury graphs, which are implemented in+-- the "Data.Graph" module of the @containers@ library.+data GraphKL a = GraphKL {+ -- | Array-based graph representation (King and Launchbury, 1995).+ toGraphKL :: KL.Graph,+ -- | A mapping of "Data.Graph.Vertex" to vertices of type @a@.+ -- This is partial and may fail if the vertex is out of bounds.+ fromVertexKL :: KL.Vertex -> a,+ -- | A mapping from vertices of type @a@ to "Data.Graph.Vertex".+ -- Returns 'Nothing' if the argument is not in the graph.+ toVertexKL :: a -> Maybe KL.Vertex }++-- | Build 'GraphKL' from an 'AdjacencyMap'.+-- If @fromAdjacencyMap g == h@ then the following holds:+--+-- @+-- map ('fromVertexKL' h) ('Data.Graph.vertices' $ 'toGraphKL' h) == 'Algebra.Graph.AdjacencyMap.vertexList' g+-- map (\\(x, y) -> ('fromVertexKL' h x, 'fromVertexKL' h y)) ('Data.Graph.edges' $ 'toGraphKL' h) == 'Algebra.Graph.AdjacencyMap.edgeList' g+-- 'toGraphKL' (fromAdjacencyMap (1 * 2 + 3 * 1)) == 'array' (0,2) [(0,[1]), (1,[]), (2,[0])]+-- 'toGraphKL' (fromAdjacencyMap (1 * 2 + 2 * 1)) == 'array' (0,1) [(0,[1]), (1,[0])]+-- @+fromAdjacencyMap :: Ord a => AdjacencyMap a -> GraphKL a+fromAdjacencyMap (AM.AM m) = GraphKL+ { toGraphKL = g+ , fromVertexKL = \u -> case r u of (_, v, _) -> v+ , toVertexKL = t }+ where+ (g, r, t) = KL.graphFromEdges [ ((), v, Set.toAscList us) | (v, us) <- Map.toAscList m ]++-- | Build 'GraphKL' from an 'AdjacencyIntMap'.+-- If @fromAdjacencyIntMap g == h@ then the following holds:+--+-- @+-- map ('fromVertexKL' h) ('Data.Graph.vertices' $ 'toGraphKL' h) == 'Data.IntSet.toAscList' ('Algebra.Graph.AdjacencyIntMap.vertexIntSet' g)+-- map (\\(x, y) -> ('fromVertexKL' h x, 'fromVertexKL' h y)) ('Data.Graph.edges' $ 'toGraphKL' h) == 'Algebra.Graph.AdjacencyIntMap.edgeList' g+-- 'toGraphKL' (fromAdjacencyIntMap (1 * 2 + 3 * 1)) == 'array' (0,2) [(0,[1]), (1,[]), (2,[0])]+-- 'toGraphKL' (fromAdjacencyIntMap (1 * 2 + 2 * 1)) == 'array' (0,1) [(0,[1]), (1,[0])]+-- @+fromAdjacencyIntMap :: AdjacencyIntMap -> GraphKL Int+fromAdjacencyIntMap (AIM.AM m) = GraphKL+ { toGraphKL = g+ , fromVertexKL = \u -> case r u of (_, v, _) -> v+ , toVertexKL = t }+ where+ (g, r, t) = KL.graphFromEdges [ ((), v, IntSet.toAscList us) | (v, us) <- IntMap.toAscList m ]++-- | Compute the /depth-first search/ forest of a graph.+--+-- In the following we will use the helper function:+--+-- @+-- (%) :: (GraphKL Int -> a) -> AM.AdjacencyMap Int -> a+-- a % g = a $ fromAdjacencyMap g+-- @+-- for greater clarity. (One could use an AdjacencyIntMap just as well)+--+-- @+-- 'Algebra.Graph.AdjacencyMap.forest' (dfsForest % 'Algebra.Graph.AdjacencyMap.edge' 1 1) == 'AM.vertex' 1+-- 'Algebra.Graph.AdjacencyMap.forest' (dfsForest % 'Algebra.Graph.AdjacencyMap.edge' 1 2) == 'Algebra.Graph.AdjacencyMap.edge' 1 2+-- '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)+-- '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+-- , subForest = [] }]}+-- , Node { rootLabel = 3+-- , subForest = [ Node { rootLabel = 4+-- , subForest = [] }]}]+-- @+dfsForest :: GraphKL a -> Forest a+dfsForest (GraphKL g r _) = fmap (fmap r) (KL.dff 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.+--+-- @+-- 'Algebra.Graph.AdjacencyMap.forest' (dfsForestFrom [1] % 'Algebra.Graph.AdjacencyMap.edge' 1 1) == 'AM.vertex' 1+-- 'Algebra.Graph.AdjacencyMap.forest' (dfsForestFrom [1] % 'Algebra.Graph.AdjacencyMap.edge' 1 2) == 'Algebra.Graph.AdjacencyMap.edge' 1 2+-- 'Algebra.Graph.AdjacencyMap.forest' (dfsForestFrom [2] % 'Algebra.Graph.AdjacencyMap.edge' 1 2) == 'AM.vertex' 2+-- 'Algebra.Graph.AdjacencyMap.forest' (dfsForestFrom [3] % 'Algebra.Graph.AdjacencyMap.edge' 1 2) == 'AM.empty'+-- '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 [] % x == []+-- dfsForestFrom [1, 4] % (3 * (1 + 4) * (1 + 5)) == [ Node { rootLabel = 1+-- , subForest = [ Node { rootLabel = 5+-- , subForest = [] }+-- , Node { rootLabel = 4+-- , subForest = [] }]+-- @+dfsForestFrom :: [a] -> GraphKL a -> Forest a+dfsForestFrom vs (GraphKL g r t) = fmap (fmap r) (KL.dfs g (mapMaybe t vs))++-- | 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 [1] % 'Algebra.Graph.AdjacencyMap.edge' 1 1 == [1]+-- dfs [1] % 'Algebra.Graph.AdjacencyMap.edge' 1 2 == [1,2]+-- dfs [2] % 'Algebra.Graph.AdjacencyMap.edge' 1 2 == [2]+-- dfs [3] % 'Algebra.Graph.AdjacencyMap.edge' 1 2 == []+-- dfs [1,2] % 'Algebra.Graph.AdjacencyMap.edge' 1 2 == [1,2]+-- dfs [2,1] % 'Algebra.Graph.AdjacencyMap.edge' 1 2 == [2,1]+-- dfs [] % x == []+-- dfs [1,4] % (3 * (1 + 4) * (1 + 5)) == [1, 5, 4]+-- 'Algebra.Graph.AdjacencyMap.isSubgraphOf' ('Algebra.Graph.AdjacencyMap.vertices' $ dfs vs x) x == True+-- @+dfs :: [a] -> GraphKL a -> [a]+dfs vs = concatMap flatten . dfsForestFrom vs++-- | Compute the /topological sort/ of a graph.+-- Unlike the (Int)AdjacencyMap algorithm this returns+-- a result even if the graph is cyclic.+--+-- @+-- topSort % (1 * 2 + 3 * 1) == [3,1,2]+-- topSort % (1 * 2 + 2 * 1) == [1,2]+-- @+topSort :: GraphKL a -> [a]+topSort (GraphKL g r _) = map r (KL.topSort g)
test/Algebra/Graph/Test/API.hs view
@@ -14,126 +14,84 @@ GraphAPI (..) ) where -import Data.IntSet (IntSet)-import Data.Set (Set) import Data.Tree -import Algebra.Graph.Class hiding (toGraph)+import Algebra.Graph.Class (Graph (..)) -import qualified Algebra.Graph.AdjacencyMap as AdjacencyMap-import qualified Algebra.Graph.Class as Class-import qualified Algebra.Graph.Fold as Fold-import qualified Algebra.Graph as Graph-import qualified Algebra.Graph.IntAdjacencyMap as IntAdjacencyMap-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 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 class Graph g => GraphAPI g where- edge :: Vertex g -> Vertex g -> g- edge = notImplemented- vertices :: [Vertex g] -> g- vertices = notImplemented- edges :: [(Vertex g, Vertex g)] -> g- edges = notImplemented- overlays :: [g] -> g- overlays = notImplemented- connects :: [g] -> g- connects = notImplemented- fromAdjacencyList :: [(Vertex g, [Vertex g])] -> g- fromAdjacencyList = notImplemented- toGraph :: (Graph h, Vertex g ~ Vertex h) => g -> h- toGraph = notImplemented- foldg :: r -> (Vertex g -> r) -> (r -> r -> r) -> (r -> r -> r) -> g -> r- foldg = notImplemented- isSubgraphOf :: g -> g -> Bool- isSubgraphOf = notImplemented- (===) :: g -> g -> Bool- (===) = notImplemented- isEmpty :: g -> Bool- isEmpty = notImplemented- size :: g -> Int- size = notImplemented- hasVertex :: Vertex g -> g -> Bool- hasVertex = notImplemented- hasEdge :: Vertex g -> Vertex g -> g -> Bool- hasEdge = notImplemented- vertexCount :: g -> Int- vertexCount = notImplemented- edgeCount :: g -> Int- edgeCount = notImplemented- vertexList :: g -> [Vertex g]- vertexList = notImplemented- edgeList :: g -> [(Vertex g, Vertex g)]- edgeList = notImplemented- adjacencyList :: g -> [(Vertex g, [Vertex g])]- adjacencyList = notImplemented- vertexSet :: g -> Set (Vertex g)- vertexSet = notImplemented- vertexIntSet :: Vertex g ~ Int => g -> IntSet- vertexIntSet = notImplemented- edgeSet :: g -> Set (Vertex g, Vertex g)- edgeSet = notImplemented- preSet :: Vertex g -> g -> Set (Vertex g)- preSet = notImplemented- postSet :: Vertex g -> g -> Set (Vertex g)- postSet = notImplemented- postIntSet :: Vertex g ~ Int => Int -> g -> IntSet- postIntSet = notImplemented- path :: [Vertex g] -> g- path = notImplemented- circuit :: [Vertex g] -> g- circuit = notImplemented- clique :: [Vertex g] -> g- clique = notImplemented- biclique :: [Vertex g] -> [Vertex g] -> g- biclique = notImplemented- star :: Vertex g -> [Vertex g] -> g- star = notImplemented- starTranspose :: Vertex g -> [Vertex g] -> g- starTranspose = notImplemented- tree :: Tree (Vertex g) -> g- tree = notImplemented- forest :: Forest (Vertex g) -> g- forest = notImplemented- mesh :: Vertex g ~ (a, b) => [a] -> [b] -> g- mesh = notImplemented- torus :: Vertex g ~ (a, b) => [a] -> [b] -> g- torus = notImplemented- deBruijn :: Vertex g ~ [a] => Int -> [a] -> g- deBruijn = notImplemented- removeVertex :: Vertex g -> g -> g- removeVertex = notImplemented- removeEdge :: Vertex g -> Vertex g -> g -> g- removeEdge = notImplemented- replaceVertex :: Vertex g -> Vertex g -> g -> g- replaceVertex = notImplemented- mergeVertices :: (Vertex g -> Bool) -> Vertex g -> g -> g- mergeVertices = notImplemented- splitVertex :: Vertex g -> [Vertex g] -> g -> g- splitVertex = notImplemented- transpose :: g -> g- transpose = notImplemented- gmap :: Vertex g ~ Int => (Int -> Int) -> g -> g- gmap = notImplemented- induce :: (Vertex g -> Bool) -> g -> g- induce = notImplemented- bind :: Vertex g ~ Int => g -> (Int -> g) -> g- bind = notImplemented- simplify :: g -> g- simplify = notImplemented- box :: forall a b f. (Vertex (f a) ~ a, Vertex (f b) ~ b, Vertex (f (a, b)) ~ (a, b), g ~ f (a, b)) => f a -> f b -> f (a, b)- box = notImplemented- dfsForest :: g -> Forest (Vertex g)- dfsForest = notImplemented- dfsForestFrom :: [Vertex g] -> g -> Forest (Vertex g)- dfsForestFrom = notImplemented- dfs :: [Vertex g] -> g -> [Vertex g]- dfs = notImplemented- topSort :: g -> Maybe [Vertex g]- topSort = notImplemented- isTopSort :: [Vertex g] -> g -> Bool- isTopSort = notImplemented+ edge :: Vertex g -> Vertex g -> g+ edge = notImplemented+ vertices :: [Vertex g] -> g+ vertices = notImplemented+ edges :: [(Vertex g, Vertex g)] -> g+ edges = notImplemented+ overlays :: [g] -> g+ overlays = notImplemented+ connects :: [g] -> g+ connects = notImplemented+ fromAdjacencySets :: [(Vertex g, Set.Set (Vertex g))] -> g+ fromAdjacencySets = notImplemented+ fromAdjacencyIntSets :: [(Int, IntSet.IntSet)] -> g+ fromAdjacencyIntSets = notImplemented+ isSubgraphOf :: g -> g -> Bool+ isSubgraphOf = notImplemented+ (===) :: g -> g -> Bool+ (===) = notImplemented+ path :: [Vertex g] -> g+ path = notImplemented+ circuit :: [Vertex g] -> g+ circuit = notImplemented+ clique :: [Vertex g] -> g+ clique = notImplemented+ biclique :: [Vertex g] -> [Vertex g] -> g+ biclique = notImplemented+ star :: Vertex g -> [Vertex g] -> g+ star = notImplemented+ stars :: [(Vertex g, [Vertex g])] -> g+ stars = notImplemented+ tree :: Tree (Vertex g) -> g+ tree = notImplemented+ forest :: Forest (Vertex g) -> g+ forest = notImplemented+ mesh :: Vertex g ~ (a, b) => [a] -> [b] -> g+ mesh = notImplemented+ torus :: Vertex g ~ (a, b) => [a] -> [b] -> g+ torus = notImplemented+ deBruijn :: Vertex g ~ [a] => Int -> [a] -> g+ deBruijn = notImplemented+ removeVertex :: Vertex g -> g -> g+ removeVertex = notImplemented+ removeEdge :: Vertex g -> Vertex g -> g -> g+ removeEdge = notImplemented+ replaceVertex :: Vertex g -> Vertex g -> g -> g+ replaceVertex = notImplemented+ mergeVertices :: (Vertex g -> Bool) -> Vertex g -> g -> g+ mergeVertices = notImplemented+ splitVertex :: Vertex g -> [Vertex g] -> g -> g+ splitVertex = notImplemented+ transpose :: g -> g+ transpose = notImplemented+ gmap :: Vertex g ~ Int => (Int -> Int) -> g -> g+ gmap = notImplemented+ induce :: (Vertex g -> Bool) -> g -> g+ induce = notImplemented+ bind :: Vertex g ~ Int => g -> (Int -> g) -> g+ bind = notImplemented+ simplify :: g -> g+ simplify = notImplemented+ box :: forall a b f. (Vertex (f a) ~ a, Vertex (f b) ~ b, Vertex (f (a, b)) ~ (a, b), g ~ f (a, b)) => f a -> f b -> f (a, b)+ box = notImplemented notImplemented :: a notImplemented = error "Not implemented"@@ -144,26 +102,14 @@ edges = AdjacencyMap.edges overlays = AdjacencyMap.overlays connects = AdjacencyMap.connects- fromAdjacencyList = AdjacencyMap.fromAdjacencyList+ fromAdjacencySets = AdjacencyMap.fromAdjacencySets isSubgraphOf = AdjacencyMap.isSubgraphOf- isEmpty = AdjacencyMap.isEmpty- hasVertex = AdjacencyMap.hasVertex- hasEdge = AdjacencyMap.hasEdge- vertexCount = AdjacencyMap.vertexCount- edgeCount = AdjacencyMap.edgeCount- vertexList = AdjacencyMap.vertexList- edgeList = AdjacencyMap.edgeList- adjacencyList = AdjacencyMap.adjacencyList- vertexSet = AdjacencyMap.vertexSet- vertexIntSet = IntSet.fromAscList . Set.toAscList . AdjacencyMap.vertexSet- edgeSet = AdjacencyMap.edgeSet- postSet = AdjacencyMap.postSet path = AdjacencyMap.path circuit = AdjacencyMap.circuit clique = AdjacencyMap.clique biclique = AdjacencyMap.biclique star = AdjacencyMap.star- starTranspose = AdjacencyMap.starTranspose+ stars = AdjacencyMap.stars tree = AdjacencyMap.tree forest = AdjacencyMap.forest removeVertex = AdjacencyMap.removeVertex@@ -173,11 +119,6 @@ transpose = AdjacencyMap.transpose gmap = AdjacencyMap.gmap induce = AdjacencyMap.induce- dfsForest = AdjacencyMap.dfsForest- dfsForestFrom = AdjacencyMap.dfsForestFrom- dfs = AdjacencyMap.dfs- topSort = AdjacencyMap.topSort- isTopSort = AdjacencyMap.isTopSort instance Ord a => GraphAPI (Fold.Fold a) where edge = Fold.edge@@ -185,42 +126,29 @@ edges = Fold.edges overlays = Fold.overlays connects = Fold.connects- toGraph = Class.toGraph- foldg = Fold.foldg isSubgraphOf = Fold.isSubgraphOf- isEmpty = Fold.isEmpty- size = Fold.size- hasVertex = Fold.hasVertex- hasEdge = Fold.hasEdge- vertexCount = Fold.vertexCount- edgeCount = Fold.edgeCount- vertexList = Fold.vertexList- edgeList = Fold.edgeList- vertexSet = Fold.vertexSet- vertexIntSet = Fold.vertexIntSet- edgeSet = Fold.edgeSet path = Fold.path circuit = Fold.circuit clique = Fold.clique biclique = Fold.biclique star = Fold.star- starTranspose = Fold.starTranspose- tree = Fold.tree- forest = Fold.forest- mesh = Fold.mesh- torus = Fold.torus- deBruijn = Fold.deBruijn+ stars = Fold.stars+ tree = HClass.tree+ forest = HClass.forest+ mesh = HClass.mesh+ torus = HClass.torus+ deBruijn = HClass.deBruijn removeVertex = Fold.removeVertex removeEdge = Fold.removeEdge- replaceVertex = Fold.replaceVertex- mergeVertices = Fold.mergeVertices- splitVertex = Fold.splitVertex+ replaceVertex = HClass.replaceVertex+ mergeVertices = HClass.mergeVertices+ splitVertex = HClass.splitVertex transpose = Fold.transpose gmap = fmap induce = Fold.induce bind = (>>=) simplify = Fold.simplify- box = Fold.box+ box = HClass.box instance Ord a => GraphAPI (Graph.Graph a) where edge = Graph.edge@@ -228,27 +156,14 @@ edges = Graph.edges overlays = Graph.overlays connects = Graph.connects- toGraph = Class.toGraph- foldg = Graph.foldg isSubgraphOf = Graph.isSubgraphOf (===) = (Graph.===)- isEmpty = Graph.isEmpty- size = Graph.size- hasVertex = Graph.hasVertex- hasEdge = Graph.hasEdge- vertexCount = Graph.vertexCount- edgeCount = Graph.edgeCount- vertexList = Graph.vertexList- edgeList = Graph.edgeList- vertexSet = Graph.vertexSet- vertexIntSet = Graph.vertexIntSet- edgeSet = Graph.edgeSet path = Graph.path circuit = Graph.circuit clique = Graph.clique biclique = Graph.biclique star = Graph.star- starTranspose = Graph.starTranspose+ stars = Graph.stars tree = Graph.tree forest = Graph.forest mesh = Graph.mesh@@ -266,80 +181,49 @@ simplify = Graph.simplify box = Graph.box -instance GraphAPI IntAdjacencyMap.IntAdjacencyMap where- edge = IntAdjacencyMap.edge- vertices = IntAdjacencyMap.vertices- edges = IntAdjacencyMap.edges- overlays = IntAdjacencyMap.overlays- connects = IntAdjacencyMap.connects- fromAdjacencyList = IntAdjacencyMap.fromAdjacencyList- isSubgraphOf = IntAdjacencyMap.isSubgraphOf- isEmpty = IntAdjacencyMap.isEmpty- hasVertex = IntAdjacencyMap.hasVertex- hasEdge = IntAdjacencyMap.hasEdge- vertexCount = IntAdjacencyMap.vertexCount- edgeCount = IntAdjacencyMap.edgeCount- vertexList = IntAdjacencyMap.vertexList- edgeList = IntAdjacencyMap.edgeList- postIntSet = IntAdjacencyMap.postIntSet- adjacencyList = IntAdjacencyMap.adjacencyList- vertexSet = Set.fromAscList . IntSet.toAscList . IntAdjacencyMap.vertexIntSet- vertexIntSet = IntAdjacencyMap.vertexIntSet- edgeSet = IntAdjacencyMap.edgeSet- path = IntAdjacencyMap.path- circuit = IntAdjacencyMap.circuit- clique = IntAdjacencyMap.clique- biclique = IntAdjacencyMap.biclique- star = IntAdjacencyMap.star- starTranspose = IntAdjacencyMap.starTranspose- tree = IntAdjacencyMap.tree- forest = IntAdjacencyMap.forest- removeVertex = IntAdjacencyMap.removeVertex- removeEdge = IntAdjacencyMap.removeEdge- replaceVertex = IntAdjacencyMap.replaceVertex- mergeVertices = IntAdjacencyMap.mergeVertices- transpose = IntAdjacencyMap.transpose- gmap = IntAdjacencyMap.gmap- induce = IntAdjacencyMap.induce- dfsForest = IntAdjacencyMap.dfsForest- dfsForestFrom = IntAdjacencyMap.dfsForestFrom- dfs = IntAdjacencyMap.dfs- topSort = IntAdjacencyMap.topSort- isTopSort = IntAdjacencyMap.isTopSort+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 Ord a => GraphAPI (Relation.Relation a) where- edge = Relation.edge- vertices = Relation.vertices- edges = Relation.edges- overlays = Relation.overlays- connects = Relation.connects- fromAdjacencyList = Relation.fromAdjacencyList- isSubgraphOf = Relation.isSubgraphOf- isEmpty = Relation.isEmpty- hasVertex = Relation.hasVertex- hasEdge = Relation.hasEdge- vertexCount = Relation.vertexCount- edgeCount = Relation.edgeCount- vertexList = Relation.vertexList- edgeList = Relation.edgeList- preSet = Relation.preSet- postSet = Relation.postSet- adjacencyList = AdjacencyMap.adjacencyList . Class.toGraph- vertexSet = Relation.vertexSet- vertexIntSet = IntSet.fromAscList . Set.toAscList . Relation.vertexSet- edgeSet = Relation.edgeSet- path = Relation.path- circuit = Relation.circuit- clique = Relation.clique- biclique = Relation.biclique- star = Relation.star- starTranspose = Relation.starTranspose- 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+ 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
+ test/Algebra/Graph/Test/AdjacencyIntMap.hs view
@@ -0,0 +1,46 @@+-----------------------------------------------------------------------------+-- |+-- Module : Algebra.Graph.Test.AdjacencyIntMap+-- Copyright : (c) Andrey Mokhov 2016-2018+-- License : MIT (see the file LICENSE)+-- Maintainer : andrey.mokhov@gmail.com+-- Stability : experimental+--+-- Testsuite for "Algebra.Graph.AdjacencyIntMap".+-----------------------------------------------------------------------------+module Algebra.Graph.Test.AdjacencyIntMap (+ -- * Testsuite+ testAdjacencyIntMap+ ) where++import Algebra.Graph.AdjacencyIntMap+import Algebra.Graph.AdjacencyIntMap.Internal+import Algebra.Graph.Test+import Algebra.Graph.Test.Generic++t :: Testsuite+t = testsuite "AdjacencyIntMap." empty++testAdjacencyIntMap :: IO ()+testAdjacencyIntMap = do+ putStrLn "\n============ AdjacencyIntMap ============"+ test "Axioms of graphs" (axioms :: GraphTestsuite AdjacencyIntMap)++ test "Consistency of arbitraryAdjacencyMap" $ \m ->+ consistent m++ testShow t+ testBasicPrimitives t+ testFromAdjacencyIntSets t+ testIsSubgraphOf t+ testToGraph t+ testGraphFamilies t+ testTransformations t+ testDfsForest t+ testDfsForestFrom t+ testDfs t+ testReachable t+ testTopSort t+ testIsAcyclic t+ testIsDfsForestOf t+ testIsTopSortOf t
test/Algebra/Graph/Test/AdjacencyMap.hs view
@@ -18,7 +18,6 @@ import Algebra.Graph.Test import Algebra.Graph.Test.Generic -import qualified Data.Graph as KL import qualified Data.Set as Set t :: Testsuite@@ -34,23 +33,21 @@ test "Consistency of arbitraryAdjacencyMap" $ \(m :: AI) -> consistent m - test "Consistency of fromAdjacencyList" $ \xs ->- consistent (fromAdjacencyList xs :: AI)- testShow t testBasicPrimitives t- testFromAdjacencyList t+ testFromAdjacencySets t testIsSubgraphOf t- testProperties t- testAdjacencyList t- testPostSet t+ testToGraph t testGraphFamilies t testTransformations t testDfsForest t testDfsForestFrom t testDfs t+ testReachable t testTopSort t- testIsTopSort t+ testIsAcyclic t+ testIsDfsForestOf t+ testIsTopSortOf t putStrLn "\n============ AdjacencyMap.scc ============" test "scc empty == empty" $@@ -70,12 +67,3 @@ , (Set.fromList [1,4], Set.fromList [5] ) , (Set.fromList [3] , Set.fromList [1,4]) , (Set.fromList [3] , Set.fromList [5 :: Int])]-- putStrLn "\n============ AdjacencyMap.Internal.GraphKL ============"- test "map (fromVertexKL h) (vertices $ toGraphKL h) == vertexList g"- $ \(g :: AI) -> let h = mkGraphKL (adjacencyMap g) in- map (fromVertexKL h) (KL.vertices $ toGraphKL h) == vertexList g-- test "map (\\(x, y) -> (fromVertexKL h x, fromVertexKL h y)) (edges $ toGraphKL h) == edgeList g"- $ \(g :: AI) -> let h = mkGraphKL (adjacencyMap g) in- map ( \(x, y) -> (fromVertexKL h x, fromVertexKL h y)) (KL.edges $ toGraphKL h) == edgeList g
test/Algebra/Graph/Test/Arbitrary.hs view
@@ -11,7 +11,7 @@ ----------------------------------------------------------------------------- module Algebra.Graph.Test.Arbitrary ( -- * Generators of arbitrary graph instances- arbitraryGraph, arbitraryRelation, arbitraryAdjacencyMap, arbitraryIntAdjacencyMap+ arbitraryGraph, arbitraryRelation, arbitraryAdjacencyMap, arbitraryAdjacencyIntMap ) where import Prelude ()@@ -25,13 +25,13 @@ import Algebra.Graph.AdjacencyMap.Internal import Algebra.Graph.Export import Algebra.Graph.Fold (Fold)-import Algebra.Graph.IntAdjacencyMap.Internal+import Algebra.Graph.AdjacencyIntMap.Internal 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.IntAdjacencyMap as IntAdjacencyMap+import qualified Algebra.Graph.AdjacencyIntMap as AdjacencyIntMap import qualified Algebra.Graph.NonEmpty as NE import qualified Algebra.Graph.Relation as Relation @@ -78,17 +78,17 @@ -- | Generate an arbitrary 'Relation'. arbitraryRelation :: (Arbitrary a, Ord a) => Gen (Relation a)-arbitraryRelation = Relation.fromAdjacencyList <$> arbitrary+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.fromAdjacencyList <$> arbitrary+arbitraryAdjacencyMap = AdjacencyMap.stars <$> arbitrary --- | Generate an arbitrary 'IntAdjacencyMap'. It is guaranteed that the+-- | Generate an arbitrary 'AdjacencyIntMap'. It is guaranteed that the -- resulting adjacency map is 'consistent'.-arbitraryIntAdjacencyMap :: Gen IntAdjacencyMap-arbitraryIntAdjacencyMap = IntAdjacencyMap.fromAdjacencyList <$> arbitrary+arbitraryAdjacencyIntMap :: Gen AdjacencyIntMap+arbitraryAdjacencyIntMap = AdjacencyIntMap.stars <$> arbitrary -- TODO: Implement a custom shrink method. instance (Arbitrary a, Ord a) => Arbitrary (Relation a) where@@ -109,8 +109,8 @@ instance (Arbitrary a, Ord a) => Arbitrary (AdjacencyMap a) where arbitrary = arbitraryAdjacencyMap -instance Arbitrary IntAdjacencyMap where- arbitrary = arbitraryIntAdjacencyMap+instance Arbitrary AdjacencyIntMap where+ arbitrary = arbitraryAdjacencyIntMap instance Arbitrary a => Arbitrary (Fold a) where arbitrary = arbitraryGraph
test/Algebra/Graph/Test/Fold.hs view
@@ -21,10 +21,7 @@ t :: Testsuite t = testsuite "Fold." (empty :: Fold Int) -h :: HTestsuite-h = hTestsuite "Fold." (empty :: Fold Int)--type F = Fold Int+type F = Fold Int testFold :: IO () testFold = do@@ -33,101 +30,11 @@ testShow t testBasicPrimitives t- testToGraph h testIsSubgraphOf t+ testToGraph t testSize t- testProperties t testGraphFamilies t testTransformations t-- putStrLn "\n============ Fold.mesh ============"- test "mesh xs [] == empty" $ \xs ->- mesh xs [] == (empty :: Fold (Int, Int))-- test "mesh [] ys == empty" $ \ys ->- mesh [] ys == (empty :: Fold (Int, Int))-- test "mesh [x] [y] == vertex (x, y)" $ \(x :: Int) (y :: Int) ->- mesh [x] [y] == (vertex (x, y) :: Fold (Int, Int))-- test "mesh xs ys == box (path xs) (path ys)" $ \(xs :: [Int]) (ys :: [Int]) ->- mesh xs ys == (box (path xs) (path ys) :: Fold (Int, Int))-- test "mesh [1..3] \"ab\" == <correct result>" $- (mesh [1..3] "ab" :: Fold (Int, Char)) == edges [ ((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')) ]-- putStrLn "\n============ Fold.torus ============"- test "torus xs [] == empty" $ \xs ->- torus xs [] == (empty :: Fold (Int, Int))-- test "torus [] ys == empty" $ \ys ->- torus [] ys == (empty :: Fold (Int, Int))-- test "torus [x] [y] == edge (x, y) (x, y)" $ \(x :: Int) (y :: Int) ->- torus [x] [y] == (edge (x, y) (x, y) :: Fold (Int, Int))-- test "torus xs ys == box (circuit xs) (circuit ys)" $ \(xs :: [Int]) (ys :: [Int]) ->- torus xs ys == (box (circuit xs) (circuit ys) :: Fold (Int, Int))-- test "torus [1,2] \"ab\" == <correct result>" $- (torus [1,2] "ab" :: Fold (Int, Char)) == edges [ ((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')) ]-- putStrLn "\n============ Fold.deBruijn ============"- test " deBruijn 0 xs == edge [] []" $ \(xs :: [Int]) ->- deBruijn 0 xs ==(edge [] [] :: Fold [Int])-- test "n > 0 ==> deBruijn n [] == empty" $ \n ->- n > 0 ==> deBruijn n [] == (empty :: Fold [Int])-- test " deBruijn 1 [0,1] == edges [ ([0],[0]), ([0],[1]), ([1],[0]), ([1],[1]) ]" $- deBruijn 1 [0,1] ==(edges [ ([0],[0]), ([0],[1]), ([1],[0]), ([1],[1]) ] :: Fold [Int])-- test " deBruijn 2 \"0\" == edge \"00\" \"00\"" $- deBruijn 2 "0" ==(edge "00" "00" :: Fold String)-- test " deBruijn 2 \"01\" == <correct result>" $- deBruijn 2 "01" ==(edges [ ("00","00"), ("00","01"), ("01","10"), ("01","11")- , ("10","00"), ("10","01"), ("11","10"), ("11","11") ] :: Fold String)-- test " transpose (deBruijn n xs) == gmap reverse $ deBruijn n xs" $ mapSize (min 5) $ \(NonNegative n) (xs :: [Int]) ->- transpose (deBruijn n xs) == ((gmap reverse $ deBruijn n xs) :: Fold [Int])-- test " vertexCount (deBruijn n xs) == (length $ nub xs)^n" $ mapSize (min 5) $ \(NonNegative n) (xs :: [Int]) ->- vertexCount (deBruijn n xs) == (length $ nubOrd xs)^n-- test "n > 0 ==> edgeCount (deBruijn n xs) == (length $ nub xs)^(n + 1)" $ mapSize (min 5) $ \(NonNegative n) (xs :: [Int]) ->- n > 0 ==> edgeCount (deBruijn n xs) == (length $ nubOrd xs)^(n + 1)-- testSplitVertex t- testBind t- testSimplify t-- putStrLn "\n============ Fold.box ============"- let unit = fmap $ \(a, ()) -> a- comm = fmap $ \(a, b) -> (b, a)- test "box x y ~~ box y x" $ mapSize (min 10) $ \(x :: F) (y :: F) ->- comm (box x y) == (box y x :: Fold (Int, Int))-- test "box x (overlay y z) == overlay (box x y) (box x z)" $ mapSize (min 10) $ \(x :: F) (y :: F) z ->- box x (overlay y z) == (overlay (box x y) (box x z) :: Fold (Int, Int))-- test "box x (vertex ()) ~~ x" $ mapSize (min 10) $ \(x :: F) ->- unit(box x (vertex ())) == x-- test "box x empty ~~ empty" $ mapSize (min 10) $ \(x :: F) ->- unit(box x empty) == empty-- let assoc = fmap $ \(a, (b, c)) -> ((a, b), c)- test "box x (box y z) ~~ box (box x y) z" $ mapSize (min 10) $ \(x :: F) (y :: F) (z :: F) ->- assoc (box x (box y z)) == (box (box x y) z :: Fold ((Int, Int), Int))-- test "transpose (box x y) == box (transpose x) (transpose y)" $ mapSize (min 10) $ \(x :: F) (y :: F) ->- transpose (box x y) == (box (transpose x) (transpose y) :: Fold (Int, Int))-- test "vertexCount (box x y) == vertexCount x * vertexCount y" $ mapSize (min 10) $ \(x :: F) (y :: F) ->- 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 :: F) (y :: F) ->- edgeCount (box x y) <= vertexCount x * edgeCount y + edgeCount x * vertexCount y+ testSplitVertex t+ testBind t+ testSimplify t
test/Algebra/Graph/Test/Generic.hs view
@@ -11,11 +11,12 @@ ----------------------------------------------------------------------------- module Algebra.Graph.Test.Generic ( -- * Generic tests- Testsuite, testsuite, HTestsuite, hTestsuite, testShow, testFromAdjacencyList,- testBasicPrimitives, testToGraph, testIsSubgraphOf, testSize, testProperties,- testAdjacencyList, testPreSet, testPostSet, testPostIntSet, testGraphFamilies,- testTransformations, testDfsForest, testDfsForestFrom, testDfs, testTopSort,- testIsTopSort, testSplitVertex, testBind, testSimplify+ 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 import Prelude ()@@ -24,36 +25,31 @@ import Control.Monad (when) import Data.Orphans () -import Data.Foldable (toList) import Data.List (nub)+import Data.Maybe import Data.Tree import Data.Tuple +import Algebra.Graph (Graph (..)) import Algebra.Graph.Class (Graph (..))+import Algebra.Graph.ToGraph (ToGraph (..)) import Algebra.Graph.Test import Algebra.Graph.Test.API-import Algebra.Graph.Relation (Relation) -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.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, Vertex g ~ Int)+ Testsuite :: (Arbitrary g, Eq g, GraphAPI g, Num 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, Vertex g ~ Int)+testsuite :: (Arbitrary g, Eq g, GraphAPI g, Num 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)) -data HTestsuite where- HTestsuite :: (Arbitrary g, Eq g, GraphAPI g, Num g, Show g, Vertex g ~ Int,- g ~ f Int, Foldable f)- => String -> (forall r. (g -> r) -> g -> r) -> HTestsuite--hTestsuite :: (Arbitrary g, Eq g, GraphAPI g, Num g, Show g, Vertex g ~ Int,- g ~ f Int, Foldable f) => String -> g -> HTestsuite-hTestsuite prefix g = HTestsuite prefix (\f x -> f (x `asTypeOf` g))- testBasicPrimitives :: Testsuite -> IO () testBasicPrimitives = mconcat [ testEmpty , testVertex@@ -65,17 +61,24 @@ , testOverlays , testConnects ] -testProperties :: Testsuite -> IO ()-testProperties = mconcat [ testIsEmpty- , testHasVertex- , testHasEdge- , testVertexCount- , testEdgeCount- , testVertexList- , testEdgeList- , testVertexSet- , testVertexIntSet- , testEdgeSet ]+testToGraph :: Testsuite -> IO ()+testToGraph = mconcat [ testToGraphDefault+ , testFoldg+ , testIsEmpty+ , testHasVertex+ , testHasEdge+ , testVertexCount+ , testEdgeCount+ , testVertexList+ , testVertexSet+ , testVertexIntSet+ , testEdgeList+ , testEdgeSet+ , testAdjacencyList+ , testPreSet+ , testPreIntSet+ , testPostSet+ , testPostIntSet ] testGraphFamilies :: Testsuite -> IO () testGraphFamilies = mconcat [ testPath@@ -83,7 +86,7 @@ , testClique , testBiclique , testStar- , testStarTranspose+ , testStars , testTree , testForest ] @@ -291,55 +294,66 @@ test "isEmpty . connects == all isEmpty" $ mapSize (min 10) $ \xs -> isEmpty % connects xs == all isEmpty xs -testFromAdjacencyList :: Testsuite -> IO ()-testFromAdjacencyList (Testsuite prefix (%)) = do- putStrLn $ "\n============ " ++ prefix ++ "fromAdjacencyList ============"- test "fromAdjacencyList [] == empty" $- fromAdjacencyList [] == id % empty+testStars :: Testsuite -> IO ()+testStars (Testsuite prefix (%)) = do+ putStrLn $ "\n============ " ++ prefix ++ "stars ============"+ test "stars [] == empty" $+ stars [] == id % empty - test "fromAdjacencyList [(x, [])] == vertex x" $ \x ->- fromAdjacencyList [(x, [])] == id % vertex x+ test "stars [(x, [])] == vertex x" $ \x ->+ stars [(x, [])] == id % vertex x - test "fromAdjacencyList [(x, [y])] == edge x y" $ \x y ->- fromAdjacencyList [(x, [y])] == id % edge x y+ test "stars [(x, [y])] == edge x y" $ \x y ->+ stars [(x, [y])] == id % edge x y - test "fromAdjacencyList . adjacencyList == id" $ \x ->- (fromAdjacencyList . adjacencyList) % x == x+ test "stars [(x, ys)] == star x ys" $ \x ys ->+ stars [(x, ys)] == id % star x ys - test "overlay (fromAdjacencyList xs) (fromAdjacencyList ys) == fromAdjacencyList (xs ++ ys)" $ \xs ys ->- overlay (fromAdjacencyList xs) % fromAdjacencyList ys == fromAdjacencyList (xs ++ ys)+ test "stars == overlays . map (uncurry star)" $ \xs ->+ stars xs == id % overlays (map (uncurry star) xs) -testToGraph :: HTestsuite -> IO ()-testToGraph (HTestsuite prefix (%)) = do- putStrLn $ "\n============ " ++ prefix ++ "toGraph ============"- test " toGraph (g :: Graph a ) :: Graph a == g" $ \g ->- toGraph % g == g+ test "stars . adjacencyList == id" $ \x ->+ (stars . adjacencyList) x == id % x - test "show (toGraph (1 * 2 :: Graph Int) :: Relation Int) == \"edge 1 2\"" $- show (toGraph % (1 * 2) :: Relation Int) == "edge 1 2"+ test "overlay (stars xs) (stars ys) == stars (xs ++ ys)" $ \xs ys ->+ overlay (stars xs) % stars ys == stars (xs ++ ys) - test "\ntoGraph == foldg empty vertex overlay connect" $ \x ->- toGraph % x == id % foldg empty vertex overlay connect x+testFromAdjacencySets :: Testsuite -> IO ()+testFromAdjacencySets (Testsuite prefix (%)) = do+ putStrLn $ "\n============ " ++ prefix ++ "fromAdjacencySets ============"+ test "fromAdjacencySets [] == empty" $+ fromAdjacencySets [] == id % empty - putStrLn $ "\n============ " ++ prefix ++ "foldg ============"- test "foldg empty vertex overlay connect == id" $ \x ->- foldg empty vertex overlay connect x == id % x+ test "fromAdjacencySets [(x, Set.empty)] == vertex x" $ \x ->+ fromAdjacencySets [(x, Set.empty)] == id % vertex x - test "foldg empty vertex overlay (flip connect) == transpose" $ \x ->- foldg empty vertex overlay (flip connect)x== transpose % x+ test "fromAdjacencySets [(x, Set.singleton y)] == edge x y" $ \x y ->+ fromAdjacencySets [(x, Set.singleton y)] == id % edge x y - test "foldg [] return (++) (++) == toList" $ \x ->- foldg [] return (++) (++) x == toList % x+ test "fromAdjacencySets . map (fmap Set.fromList) . adjacencyList == id" $ \x ->+ (fromAdjacencySets . map (fmap Set.fromList) . adjacencyList) % x == x - test "foldg 0 (const 1) (+) (+) == length" $ \x ->- foldg 0 (const 1) (+) (+) x == length % x+ test "overlay (fromAdjacencySets xs) (fromAdjacencySets ys) == fromAdjacencySets (xs ++ ys)" $ \xs ys ->+ overlay (fromAdjacencySets xs) % fromAdjacencySets ys == fromAdjacencySets (xs ++ ys) - test "foldg 1 (const 1) (+) (+) == size" $ \x ->- foldg 1 (const 1) (+) (+) x == size % x+testFromAdjacencyIntSets :: Testsuite -> IO ()+testFromAdjacencyIntSets (Testsuite prefix (%)) = do+ putStrLn $ "\n============ " ++ prefix ++ "fromAdjacencyIntSets ============"+ test "fromAdjacencyIntSets [] == empty" $+ fromAdjacencyIntSets [] == id % empty - test "foldg True (const False) (&&) (&&) == isEmpty" $ \x ->- foldg True (const False) (&&) (&&) x == isEmpty % 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 . map (fmap IntSet.fromList) . adjacencyList == id" $ \x ->+ (fromAdjacencyIntSets . map (fmap IntSet.fromList) . adjacencyList) % x == x++ 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 ============"@@ -358,6 +372,129 @@ test "isSubgraphOf (path xs) (circuit xs) == True" $ \xs -> isSubgraphOf (path xs) % circuit xs == True +testToGraphDefault :: Testsuite -> IO ()+testToGraphDefault (Testsuite prefix (%)) = do+ putStrLn $ "\n============ " ++ prefix ++ "toGraph et al. ============"+ test "toGraph == foldg Empty Vertex Overlay Connect" $ \x ->+ toGraph % x == foldg Empty Vertex Overlay Connect x++ test "foldg == Algebra.Graph.foldg . toGraph" $ \e (apply -> v) (applyFun2 -> o) (applyFun2 -> c) x ->+ foldg e v o c x == (G.foldg (e :: Int) v o c . toGraph) % x++ test "isEmpty == foldg True (const False) (&&) (&&)" $ \x ->+ isEmpty x == foldg True (const False) (&&) (&&) % x++ test "size == foldg 1 (const 1) (+) (+)" $ \x ->+ size x == foldg 1 (const 1) (+) (+) % x++ test "hasVertex x == foldg False (==x) (||) (||)" $ \x y ->+ hasVertex x y == foldg False (==x) (||) (||) % y++ test "hasEdge x y == Algebra.Graph.hasEdge x y . toGraph" $ \x y z ->+ hasEdge x y z == (G.hasEdge x y . toGraph) % z++ test "vertexCount == Set.size . vertexSet" $ \x ->+ vertexCount x == (Set.size . vertexSet) % x++ test "edgeCount == Set.size . edgeSet" $ \x ->+ edgeCount x == (Set.size . edgeSet) % x++ test "vertexList == Set.toAscList . vertexSet" $ \x ->+ vertexList x == (Set.toAscList . vertexSet) % x++ test "edgeList == Set.toAscList . edgeSet" $ \x ->+ edgeList x == (Set.toAscList . edgeSet) % x++ test "vertexSet == foldg Set.empty Set.singleton Set.union Set.union" $ \x ->+ vertexSet x == foldg Set.empty Set.singleton Set.union Set.union % x++ test "vertexIntSet == foldg IntSet.empty IntSet.singleton IntSet.union IntSet.union" $ \x ->+ vertexIntSet x == foldg IntSet.empty IntSet.singleton IntSet.union IntSet.union % x++ test "edgeSet == Algebra.Graph.AdjacencyMap.edgeSet . foldg empty vertex overlay connect" $ \x ->+ edgeSet x == (AM.edgeSet . foldg empty vertex overlay connect) % x++ test "preSet x == Algebra.Graph.AdjacencyMap.preSet x . toAdjacencyMap" $ \x y ->+ preSet x y == (AM.preSet x . toAdjacencyMap) % y++ test "preIntSet x == Algebra.Graph.AdjacencyIntMap.preIntSet x . toAdjacencyIntMap" $ \x y ->+ preIntSet x y == (AIM.preIntSet x . toAdjacencyIntMap) % y++ test "postSet x == Algebra.Graph.AdjacencyMap.postSet x . toAdjacencyMap" $ \x y ->+ postSet x y == (AM.postSet x . toAdjacencyMap) % y++ test "postIntSet x == Algebra.Graph.AdjacencyIntMap.postIntSet x . toAdjacencyIntMap" $ \x y ->+ postIntSet x y == (AIM.postIntSet x . toAdjacencyIntMap) % y++ test "adjacencyList == Algebra.Graph.AdjacencyMap.adjacencyList . toAdjacencyMap" $ \x ->+ adjacencyList x == (AM.adjacencyList . toAdjacencyMap) % x++ test "adjacencyMap == Algebra.Graph.AdjacencyMap.adjacencyMap . toAdjacencyMap" $ \x ->+ adjacencyMap x == (AM.adjacencyMap . toAdjacencyMap) % x++ test "adjacencyIntMap == Algebra.Graph.AdjacencyIntMap.adjacencyIntMap . toAdjacencyIntMap" $ \x ->+ adjacencyIntMap x == (AIM.adjacencyIntMap . toAdjacencyIntMap) % x++ test "adjacencyMapTranspose == Algebra.Graph.AdjacencyMap.adjacencyMap . toAdjacencyMapTranspose" $ \x ->+ adjacencyMapTranspose x == (AM.adjacencyMap . toAdjacencyMapTranspose) % x++ test "adjacencyIntMapTranspose == Algebra.Graph.AdjacencyIntMap.adjacencyIntMap . toAdjacencyIntMapTranspose" $ \x ->+ adjacencyIntMapTranspose x == (AIM.adjacencyIntMap . toAdjacencyIntMapTranspose) % x++ test "dfsForest == Algebra.Graph.AdjacencyMap.dfsForest . toAdjacencyMap" $ \x ->+ dfsForest x == (AM.dfsForest . toAdjacencyMap) % x++ test "dfsForestFrom vs == Algebra.Graph.AdjacencyMap.dfsForestFrom vs . toAdjacencyMap" $ \vs x ->+ dfsForestFrom vs x == (AM.dfsForestFrom vs . toAdjacencyMap) % x++ test "dfs vs == Algebra.Graph.AdjacencyMap.dfs vs . toAdjacencyMap" $ \vs x ->+ dfs vs x == (AM.dfs vs . toAdjacencyMap) % x++ test "reachable x == Algebra.Graph.AdjacencyMap.reachable x . toAdjacencyMap" $ \x y ->+ reachable x y == (AM.reachable x . toAdjacencyMap) % y++ test "topSort == Algebra.Graph.AdjacencyMap.topSort . toAdjacencyMap" $ \x ->+ topSort x == (AM.topSort . toAdjacencyMap) % x++ test "isAcyclic == Algebra.Graph.AdjacencyMap.isAcyclic . toAdjacencyMap" $ \x ->+ isAcyclic x == (AM.isAcyclic . toAdjacencyMap) % x++ test "isTopSortOf vs == Algebra.Graph.AdjacencyMap.isTopSortOf vs . toAdjacencyMap" $ \vs x ->+ isTopSortOf vs x == (AM.isTopSortOf vs . toAdjacencyMap) % x++ test "toAdjacencyMap == foldg empty vertex overlay connect" $ \x ->+ toAdjacencyMap x == foldg AM.empty AM.vertex AM.overlay AM.connect % x++ test "toAdjacencyMapTranspose == foldg empty vertex overlay (flip connect)" $ \x ->+ toAdjacencyMapTranspose x == foldg AM.empty AM.vertex AM.overlay (flip AM.connect) % x++ test "toAdjacencyIntMap == foldg empty vertex overlay connect" $ \x ->+ toAdjacencyIntMap x == foldg AIM.empty AIM.vertex AIM.overlay AIM.connect % x++ test "toAdjacencyIntMapTranspose == foldg empty vertex overlay (flip connect)" $ \x ->+ toAdjacencyIntMapTranspose x == foldg AIM.empty AIM.vertex AIM.overlay (flip AIM.connect) % x++ test "isDfsForestOf f == Algebra.Graph.AdjacencyMap.isDfsForestOf f . toAdjacencyMap" $ \f x ->+ isDfsForestOf f x == (AM.isDfsForestOf f . toAdjacencyMap) % x++ test "isTopSortOf vs == Algebra.Graph.AdjacencyMap.isTopSortOf vs . toAdjacencyMap" $ \vs x ->+ isTopSortOf vs x == (AM.isTopSortOf vs . toAdjacencyMap) % x++testFoldg :: Testsuite -> IO ()+testFoldg (Testsuite prefix (%)) = do+ putStrLn $ "\n============ " ++ prefix ++ "foldg ============"+ test "foldg empty vertex overlay connect == id" $ \x ->+ foldg empty vertex overlay connect % x == id x++ test "foldg empty vertex overlay (flip connect) == transpose" $ \x ->+ foldg empty vertex overlay (flip connect) % x == transpose x++ test "foldg 1 (const 1) (+) (+) == size" $ \x ->+ foldg 1 (const 1) (+) (+) % x == size x++ test "foldg True (const False) (&&) (&&) == isEmpty" $ \x ->+ foldg True (const False) (&&) (&&) % x == isEmpty x+ testIsEmpty :: Testsuite -> IO () testIsEmpty (Testsuite prefix (%)) = do putStrLn $ "\n============ " ++ prefix ++ "isEmpty ============"@@ -578,6 +715,21 @@ test "postSet 2 (edge 1 2) == Set.empty" $ postSet 2 % edge 1 2 == Set.empty +testPreIntSet :: Testsuite -> IO ()+testPreIntSet (Testsuite prefix (%)) = do+ putStrLn $ "\n============ " ++ prefix ++ "preIntSet ============"+ test "preIntSet x empty == IntSet.empty" $ \x ->+ preIntSet x % empty == IntSet.empty++ test "preIntSet x (vertex x) == IntSet.empty" $ \x ->+ preIntSet x % vertex x == IntSet.empty++ test "preIntSet 1 (edge 1 2) == IntSet.empty" $+ preIntSet 1 % edge 1 2 == IntSet.empty++ test "preIntSet y (edge x y) == IntSet.fromList [x]" $ \x y ->+ preIntSet y % edge x y == IntSet.fromList [x]+ testPostIntSet :: Testsuite -> IO () testPostIntSet (Testsuite prefix (%)) = do putStrLn $ "\n============ " ++ prefix ++ "postIntSet ============"@@ -587,12 +739,12 @@ test "postIntSet x (vertex x) == IntSet.empty" $ \x -> postIntSet x % vertex x == IntSet.empty - test "postIntSet x (edge x y) == IntSet.fromList [y]" $ \x y ->- postIntSet x % edge x y == IntSet.fromList [y]- test "postIntSet 2 (edge 1 2) == IntSet.empty" $ postIntSet 2 % edge 1 2 == IntSet.empty + test "postIntSet x (edge x y) == IntSet.fromList [y]" $ \x y ->+ postIntSet x % edge x y == IntSet.fromList [y]+ testPath :: Testsuite -> IO () testPath (Testsuite prefix (%)) = do putStrLn $ "\n============ " ++ prefix ++ "path ============"@@ -668,24 +820,6 @@ test "star x ys == connect (vertex x) (vertices ys)" $ \x ys -> star x ys == connect (vertex x) % (vertices ys) -testStarTranspose :: Testsuite -> IO ()-testStarTranspose (Testsuite prefix (%)) = do- putStrLn $ "\n============ " ++ prefix ++ "starTranspose ============"- test "starTranspose x [] == vertex x" $ \x ->- starTranspose x [] == id % vertex x-- test "starTranspose x [y] == edge y x" $ \x y ->- starTranspose x [y] == id % edge y x-- test "starTranspose x [y,z] == edges [(y,x), (z,x)]" $ \x y z ->- starTranspose x [y,z] == id % edges [(y,x), (z,x)]-- test "starTranspose x ys == connect (vertices ys) (vertex x)" $ \x ys ->- starTranspose x ys == connect (vertices ys) % (vertex x)-- test "starTranspose x ys == transpose (star x ys)" $ \x ys ->- starTranspose x ys == transpose % (star x ys)- testTree :: Testsuite -> IO () testTree (Testsuite prefix (%)) = do putStrLn $ "\n============ " ++ prefix ++ "tree ============"@@ -737,8 +871,8 @@ testRemoveEdge :: Testsuite -> IO () testRemoveEdge (Testsuite prefix (%)) = do putStrLn $ "\n============ " ++ prefix ++ "removeEdge ============"- test "removeEdge x y (edge x y) == vertices [x, y]" $ \x y ->- removeEdge x y % edge x y == vertices [x, y]+ test "removeEdge x y (edge x y) == vertices [x,y]" $ \x y ->+ removeEdge x y % edge x y == vertices [x,y] test "removeEdge x y . removeEdge x y == removeEdge x y" $ \x y z -> (removeEdge x y . removeEdge x y) z == removeEdge x y % z@@ -890,18 +1024,24 @@ testDfsForest :: Testsuite -> IO () testDfsForest (Testsuite prefix (%)) = do putStrLn $ "\n============ " ++ prefix ++ "dfsForest ============"+ test "dfsForest empty == []" $+ dfsForest % empty == []+ test "forest (dfsForest $ edge 1 1) == vertex 1" $ forest (dfsForest % edge 1 1) == id % vertex 1 test "forest (dfsForest $ edge 1 2) == edge 1 2" $ forest (dfsForest % edge 1 2) == id % edge 1 2 - test "forest (dfsForest $ edge 2 1) == vertices [1, 2]" $- forest (dfsForest % edge 2 1) == id % vertices [1, 2]+ test "forest (dfsForest $ edge 2 1) == vertices [1,2]" $+ forest (dfsForest % edge 2 1) == id % vertices [1,2] test "isSubgraphOf (forest $ dfsForest x) x == True" $ \x -> isSubgraphOf (forest $ dfsForest x) % x == True + test "isDfsForestOf (dfsForest x) x == True" $ \x ->+ isDfsForestOf (dfsForest x) % x == True+ test "dfsForest . forest . dfsForest == dfsForest" $ \x -> dfsForest % forest (dfsForest x) == dfsForest % x @@ -919,99 +1059,210 @@ testDfsForestFrom :: Testsuite -> IO () testDfsForestFrom (Testsuite prefix (%)) = do putStrLn $ "\n============ " ++ prefix ++ "dfsForestFrom ============"- test "forest (dfsForestFrom [1] $ edge 1 1) == vertex 1" $- forest (dfsForestFrom [1] % edge 1 1) == id % vertex 1+ test "dfsForestFrom vs empty == []" $ \vs ->+ dfsForestFrom vs % empty == [] - test "forest (dfsForestFrom [1] $ edge 1 2) == edge 1 2" $- forest (dfsForestFrom [1] % edge 1 2) == id % edge 1 2+ test "forest (dfsForestFrom [1] $ edge 1 1) == vertex 1" $+ forest (dfsForestFrom [1] % edge 1 1) == id % vertex 1 - test "forest (dfsForestFrom [2] $ edge 1 2) == vertex 2" $- forest (dfsForestFrom [2] % edge 1 2) == id % vertex 2+ test "forest (dfsForestFrom [1] $ edge 1 2) == edge 1 2" $+ forest (dfsForestFrom [1] % edge 1 2) == id % edge 1 2 - test "forest (dfsForestFrom [3] $ edge 1 2) == empty" $- forest (dfsForestFrom [3] % edge 1 2) == id % empty+ test "forest (dfsForestFrom [2] $ edge 1 2) == vertex 2" $+ forest (dfsForestFrom [2] % edge 1 2) == id % vertex 2 - test "forest (dfsForestFrom [2, 1] $ edge 1 2) == vertices [1, 2]" $- forest (dfsForestFrom [2, 1] % edge 1 2) == id % vertices [1, 2]+ test "forest (dfsForestFrom [3] $ edge 1 2) == empty" $+ forest (dfsForestFrom [3] % edge 1 2) == id % empty - test "isSubgraphOf (forest $ dfsForestFrom vs x) x == True" $ \vs x ->- isSubgraphOf (forest $ dfsForestFrom vs x) % x == True+ test "forest (dfsForestFrom [2,1] $ edge 1 2) == vertices [1,2]" $+ forest (dfsForestFrom [2,1] % edge 1 2) == id % vertices [1,2] - test "dfsForestFrom (vertexList x) x == dfsForest x" $ \x ->- dfsForestFrom (vertexList x) % x == dfsForest % x+ test "isSubgraphOf (forest $ dfsForestFrom vs x) x == True" $ \vs x ->+ isSubgraphOf (forest $ dfsForestFrom vs x) % x == True - test "dfsForestFrom vs (vertices vs) == map (\\v -> Node v []) (nub vs)" $ \vs ->- dfsForestFrom vs % vertices vs == map (\v -> Node v []) (nub vs)+ test "isDfsForestOf (dfsForestFrom (vertexList x) x) x == True" $ \x ->+ isDfsForestOf (dfsForestFrom (vertexList x) x) % x == True - test "dfsForestFrom [] x == []" $ \x ->- dfsForestFrom [] % x == []+ test "dfsForestFrom (vertexList x) x == dfsForest x" $ \x ->+ dfsForestFrom (vertexList x) % x == dfsForest % x - test "dfsForestFrom [1, 4] $ 3 * (1 + 4) * (1 + 5) == <correct result>" $- dfsForestFrom [1, 4] % (3 * (1 + 4) * (1 + 5)) == [ Node { rootLabel = 1- , subForest = [ Node { rootLabel = 5- , subForest = [] }]}- , Node { rootLabel = 4- , subForest = [] }]+ test "dfsForestFrom vs (vertices vs) == map (\\v -> Node v []) (nub vs)" $ \vs ->+ dfsForestFrom vs % vertices vs == map (\v -> Node v []) (nub vs) + test "dfsForestFrom [] x == []" $ \x ->+ dfsForestFrom [] % x == []++ test "dfsForestFrom [1,4] $ 3 * (1 + 4) * (1 + 5) == <correct result>" $+ dfsForestFrom [1,4] % (3 * (1 + 4) * (1 + 5)) == [ Node { rootLabel = 1+ , subForest = [ Node { rootLabel = 5+ , subForest = [] }]}+ , Node { rootLabel = 4+ , subForest = [] }]+ testDfs :: Testsuite -> IO () testDfs (Testsuite prefix (%)) = do putStrLn $ "\n============ " ++ prefix ++ "dfs ============"- test "dfs [1] $ edge 1 1 == [1]" $- dfs [1] % edge 1 1 == [1]+ test "dfs vs $ empty == []" $ \vs ->+ dfs vs % empty == [] - test "dfs [1] $ edge 1 2 == [1, 2]" $- dfs [1] % edge 1 2 == [1, 2]+ test "dfs [1] $ edge 1 1 == [1]" $+ dfs [1] % edge 1 1 == [1] - test "dfs [2] $ edge 1 2 == [2]" $- dfs [2] % edge 1 2 == [2]+ test "dfs [1] $ edge 1 2 == [1,2]" $+ dfs [1] % edge 1 2 == [1,2] - test "dfs [3] $ edge 1 2 == []" $- dfs [3] % edge 1 2 == []+ test "dfs [2] $ edge 1 2 == [2]" $+ dfs [2] % edge 1 2 == [2] - test "dfs [1, 2] $ edge 1 2 == [1, 2]" $- dfs [1, 2] % edge 1 2 == [1, 2]+ test "dfs [3] $ edge 1 2 == []" $+ dfs [3] % edge 1 2 == [] - test "dfs [2, 1] $ edge 1 2 == [2, 1]" $- dfs [2, 1] % edge 1 2 == [2, 1]+ test "dfs [1,2] $ edge 1 2 == [1,2]" $+ dfs [1,2] % edge 1 2 == [1,2] - test "dfs [] $ x == []" $ \x ->- dfs [] % x == []+ test "dfs [2,1] $ edge 1 2 == [2,1]" $+ dfs [2,1] % edge 1 2 == [2,1] - test "dfs [1, 4] $ 3 * (1 + 4) * (1 + 5) == [1, 5, 4]" $- dfs [1, 4] % (3 * (1 + 4) * (1 + 5)) == [1, 5, 4]+ test "dfs [] $ x == []" $ \x ->+ dfs [] % x == [] + test "dfs [1,4] $ 3 * (1 + 4) * (1 + 5) == [1,5,4]" $+ dfs [1,4] % (3 * (1 + 4) * (1 + 5)) == [1,5,4]+ test "isSubgraphOf (vertices $ dfs vs x) x == True" $ \vs x -> isSubgraphOf (vertices $ dfs vs x) % x == True +testReachable :: Testsuite -> IO ()+testReachable (Testsuite prefix (%)) = do+ putStrLn $ "\n============ " ++ prefix ++ "dfs ============"+ test "reachable x $ empty == []" $ \x ->+ reachable x % empty == []++ test "reachable 1 $ vertex 1 == [1]" $+ reachable 1 % vertex 1 == [1]++ test "reachable 1 $ vertex 2 == []" $+ reachable 1 % vertex 2 == []++ test "reachable 1 $ edge 1 1 == [1]" $+ reachable 1 % edge 1 1 == [1]++ test "reachable 1 $ edge 1 2 == [1,2]" $+ reachable 1 % edge 1 2 == [1,2]++ test "reachable 4 $ path [1..8] == [4..8]" $+ reachable 4 % path [1..8] == [4..8]++ test "reachable 4 $ circuit [1..8] == [4..8] ++ [1..3]" $+ reachable 4 % circuit [1..8] == [4..8] ++ [1..3]++ test "reachable 8 $ clique [8,7..1] == [8] ++ [1..7]" $+ reachable 8 % clique [8,7..1] == [8] ++ [1..7]++ test "isSubgraphOf (vertices $ reachable x y) y == True" $ \x y ->+ isSubgraphOf (vertices $ reachable x y) % y == True+ testTopSort :: Testsuite -> IO () testTopSort (Testsuite prefix (%)) = do putStrLn $ "\n============ " ++ prefix ++ "topSort ============"- test "topSort (1 * 2 + 3 * 1) == Just [3,1,2]" $- topSort % (1 * 2 + 3 * 1) == Just [3,1,2]+ test "topSort (1 * 2 + 3 * 1) == Just [3,1,2]" $+ topSort % (1 * 2 + 3 * 1) == Just [3,1,2] - test "topSort (1 * 2 + 2 * 1) == Nothing" $- topSort % (1 * 2 + 2 * 1) == Nothing+ test "topSort (1 * 2 + 2 * 1) == Nothing" $+ topSort % (1 * 2 + 2 * 1) == Nothing - test "fmap (flip isTopSort x) (topSort x) /= Just False" $ \x ->- fmap (flip isTopSort x) (topSort % x) /= Just False+ test "fmap (flip isTopSortOf x) (topSort x) /= Just False" $ \x ->+ fmap (flip isTopSortOf x) (topSort % x) /= Just False -testIsTopSort :: Testsuite -> IO ()-testIsTopSort (Testsuite prefix (%)) = do- putStrLn $ "\n============ " ++ prefix ++ "isTopSort ============"- test "isTopSort [3, 1, 2] (1 * 2 + 3 * 1) == True" $- isTopSort [3, 1, 2] % (1 * 2 + 3 * 1) == True+testIsAcyclic :: Testsuite -> IO ()+testIsAcyclic (Testsuite prefix (%)) = do+ putStrLn $ "\n============ " ++ prefix ++ "testIsAcyclic ============"+ test "isAcyclic (1 * 2 + 3 * 1) == True" $+ isAcyclic % (1 * 2 + 3 * 1) == True - test "isTopSort [1, 2, 3] (1 * 2 + 3 * 1) == False" $- isTopSort [1, 2, 3] % (1 * 2 + 3 * 1) == False+ test "isAcyclic (1 * 2 + 2 * 1) == False" $+ isAcyclic % (1 * 2 + 2 * 1) == False - test "isTopSort [] (1 * 2 + 3 * 1) == False" $- isTopSort [] % (1 * 2 + 3 * 1) == False+ test "isAcyclic . circuit == null" $ \xs ->+ isAcyclic % circuit xs == null xs - test "isTopSort [] empty == True" $- isTopSort [] % empty == True+ test "isAcyclic == isJust . topSort" $ \x ->+ isAcyclic % x == isJust (topSort x) - test "isTopSort [x] (vertex x) == True" $ \x ->- isTopSort [x] % vertex x == True+testIsDfsForestOf :: Testsuite -> IO ()+testIsDfsForestOf (Testsuite prefix (%)) = do+ putStrLn $ "\n============ " ++ prefix ++ "isDfsForestOf ============"+ test "isDfsForestOf [] empty == True" $+ isDfsForestOf [] % empty == True - test "isTopSort [x] (edge x x) == False" $ \x ->- isTopSort [x] % edge x x == False+ test "isDfsForestOf [] (vertex 1) == False" $+ isDfsForestOf [] % (vertex 1) == False++ test "isDfsForestOf [Node 1 []] (vertex 1) == True" $+ isDfsForestOf [Node 1 []] % (vertex 1) == True++ test "isDfsForestOf [Node 1 []] (vertex 2) == False" $+ isDfsForestOf [Node 1 []] % (vertex 2) == False++ test "isDfsForestOf [Node 1 [], Node 1 []] (vertex 1) == False" $+ isDfsForestOf [Node 1 [], Node 1 []] % (vertex 1) == False++ test "isDfsForestOf [Node 1 []] (edge 1 1) == True" $+ isDfsForestOf [Node 1 []] % (edge 1 1) == True++ test "isDfsForestOf [Node 1 []] (edge 1 2) == False" $+ isDfsForestOf [Node 1 []] % (edge 1 2) == False++ test "isDfsForestOf [Node 1 [], Node 2 []] (edge 1 2) == False" $+ isDfsForestOf [Node 1 [], Node 2 []] % (edge 1 2) == False++ test "isDfsForestOf [Node 2 [], Node 1 []] (edge 1 2) == True" $+ isDfsForestOf [Node 2 [], Node 1 []] % (edge 1 2) == True++ test "isDfsForestOf [Node 1 [Node 2 []]] (edge 1 2) == True" $+ isDfsForestOf [Node 1 [Node 2 []]] % (edge 1 2) == True++ test "isDfsForestOf [Node 1 [], Node 2 []] (vertices [1,2]) == True" $+ isDfsForestOf [Node 1 [], Node 2 []] % (vertices [1,2]) == True++ test "isDfsForestOf [Node 2 [], Node 1 []] (vertices [1,2]) == True" $+ isDfsForestOf [Node 2 [], Node 1 []] % (vertices [1,2]) == True++ test "isDfsForestOf [Node 1 [Node 2 []]] (vertices [1,2]) == False" $+ isDfsForestOf [Node 1 [Node 2 []]] % (vertices [1,2]) == False++ test "isDfsForestOf [Node 1 [Node 2 [Node 3 []]]] (path [1,2,3]) == True" $+ isDfsForestOf [Node 1 [Node 2 [Node 3 []]]] % (path [1,2,3]) == True++ test "isDfsForestOf [Node 1 [Node 3 [Node 2 []]]] (path [1,2,3]) == False" $+ isDfsForestOf [Node 1 [Node 3 [Node 2 []]]] % (path [1,2,3]) == False++ test "isDfsForestOf [Node 3 [], Node 1 [Node 2 []]] (path [1,2,3]) == True" $+ isDfsForestOf [Node 3 [], Node 1 [Node 2 []]] % (path [1,2,3]) == True++ test "isDfsForestOf [Node 2 [Node 3 []], Node 1 []] (path [1,2,3]) == True" $+ isDfsForestOf [Node 2 [Node 3 []], Node 1 []] % (path [1,2,3]) == True++ test "isDfsForestOf [Node 1 [], Node 2 [Node 3 []]] (path [1,2,3]) == False" $+ isDfsForestOf [Node 1 [], Node 2 [Node 3 []]] % (path [1,2,3]) == False++testIsTopSortOf :: Testsuite -> IO ()+testIsTopSortOf (Testsuite prefix (%)) = do+ putStrLn $ "\n============ " ++ prefix ++ "isTopSortOf ============"+ test "isTopSortOf [3,1,2] (1 * 2 + 3 * 1) == True" $+ isTopSortOf [3,1,2] % (1 * 2 + 3 * 1) == True++ test "isTopSortOf [1,2,3] (1 * 2 + 3 * 1) == False" $+ isTopSortOf [1,2,3] % (1 * 2 + 3 * 1) == False++ test "isTopSortOf [] (1 * 2 + 3 * 1) == False" $+ isTopSortOf [] % (1 * 2 + 3 * 1) == False++ test "isTopSortOf [] empty == True" $+ isTopSortOf [] % empty == True++ test "isTopSortOf [x] (vertex x) == True" $ \x ->+ isTopSortOf [x] % vertex x == True++ test "isTopSortOf [x] (edge x x) == False" $ \x ->+ isTopSortOf [x] % edge x x == False
test/Algebra/Graph/Test/Graph.hs view
@@ -14,16 +14,16 @@ testGraph ) where +import Data.Either+ import Algebra.Graph import Algebra.Graph.Test import Algebra.Graph.Test.Generic+import Algebra.Graph.ToGraph (reachable) t :: Testsuite t = testsuite "Graph." empty -h :: HTestsuite-h = hTestsuite "Graph." empty- type G = Graph Int testGraph :: IO ()@@ -33,10 +33,9 @@ test "Theorems of graphs" (theorems :: GraphTestsuite G) testBasicPrimitives t- testToGraph h testIsSubgraphOf t+ testToGraph t testSize t- testProperties t testGraphFamilies t testTransformations t @@ -57,39 +56,45 @@ (x + y === x * y) == False putStrLn "\n============ Graph.mesh ============"- test "mesh xs [] == empty" $ \xs ->- mesh xs [] == (empty :: Graph (Int, Int))+ test "mesh xs [] == empty" $ \xs ->+ mesh xs [] == (empty :: Graph (Int, Int)) - test "mesh [] ys == empty" $ \ys ->- mesh [] ys == (empty :: Graph (Int, Int))+ test "mesh [] ys == empty" $ \ys ->+ mesh [] ys == (empty :: Graph (Int, Int)) - test "mesh [x] [y] == vertex (x, y)" $ \(x :: Int) (y :: Int) ->- mesh [x] [y] == vertex (x, y)+ test "mesh [x] [y] == vertex (x, y)" $ \(x :: Int) (y :: Int) ->+ mesh [x] [y] == vertex (x, y) - test "mesh xs ys == box (path xs) (path ys)" $ \(xs :: [Int]) (ys :: [Int]) ->- mesh xs ys == box (path xs) (path ys)+ test "mesh xs ys == box (path xs) (path ys)" $ \(xs :: [Int]) (ys :: [Int]) ->+ mesh xs ys == box (path xs) (path ys) - test "mesh [1..3] \"ab\" == <correct result>" $- mesh [1..3] "ab" == edges [ ((1,'a'),(1,'b')), ((1,'a'),(2,'a')), ((1,'b'),(2,'b')), ((2,'a'),(2,'b'))+ test "mesh [1..3] \"ab\" == <correct result>" $+ mesh [1..3] "ab" == edges [ ((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 :: [Int]) (ys :: [Int]) ->+ size (mesh xs ys) == max 1 (3 * length xs * length ys - length xs - length ys -1) putStrLn "\n============ Graph.torus ============"- test "torus xs [] == empty" $ \xs ->- torus xs [] == (empty :: Graph (Int, Int))+ test "torus xs [] == empty" $ \xs ->+ torus xs [] == (empty :: Graph (Int, Int)) - test "torus [] ys == empty" $ \ys ->- torus [] ys == (empty :: Graph (Int, Int))+ test "torus [] ys == empty" $ \ys ->+ torus [] ys == (empty :: Graph (Int, Int)) - test "torus [x] [y] == edge (x, y) (x, y)" $ \(x :: Int) (y :: Int) ->- torus [x] [y] == edge (x, y) (x, y)+ test "torus [x] [y] == edge (x,y) (x,y)" $ \(x :: Int) (y :: Int) ->+ torus [x] [y] == edge (x,y) (x,y) - test "torus xs ys == box (circuit xs) (circuit ys)" $ \(xs :: [Int]) (ys :: [Int]) ->- torus xs ys == box (circuit xs) (circuit ys)+ test "torus xs ys == box (circuit xs) (circuit ys)" $ \(xs :: [Int]) (ys :: [Int]) ->+ torus xs ys == box (circuit xs) (circuit ys) - test "torus [1,2] \"ab\" == <correct result>" $- torus [1,2] "ab" == edges [ ((1,'a'),(1,'b')), ((1,'a'),(2,'a')), ((1,'b'),(1,'a')), ((1,'b'),(2,'b'))+ test "torus [1,2] \"ab\" == <correct result>" $+ torus [1,2] "ab" == edges [ ((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 (torus xs ys) == max 1 (3 * length xs * length ys)" $ \(xs :: [Int]) (ys :: [Int]) ->+ size (torus xs ys) == max 1 (3 * length xs * length ys)++ putStrLn "\n============ Graph.deBruijn ============" test " deBruijn 0 xs == edge [] []" $ \(xs :: [Int]) -> deBruijn 0 xs ==(edge [] [] :: Graph [Int])@@ -147,3 +152,16 @@ 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.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/IntAdjacencyMap.hs
@@ -1,60 +0,0 @@--------------------------------------------------------------------------------- |--- Module : Algebra.Graph.Test.IntAdjacencyMap--- Copyright : (c) Andrey Mokhov 2016-2018--- License : MIT (see the file LICENSE)--- Maintainer : andrey.mokhov@gmail.com--- Stability : experimental------ Testsuite for "Algebra.Graph.IntAdjacencyMap".-------------------------------------------------------------------------------module Algebra.Graph.Test.IntAdjacencyMap (- -- * Testsuite- testIntAdjacencyMap- ) where--import Algebra.Graph.IntAdjacencyMap-import Algebra.Graph.IntAdjacencyMap.Internal-import Algebra.Graph.Test-import Algebra.Graph.Test.Generic--import qualified Data.Graph as KL-import qualified Data.IntSet as IntSet--t :: Testsuite-t = testsuite "IntAdjacencyMap." empty--testIntAdjacencyMap :: IO ()-testIntAdjacencyMap = do- putStrLn "\n============ IntAdjacencyMap ============"- test "Axioms of graphs" (axioms :: GraphTestsuite IntAdjacencyMap)-- test "Consistency of arbitraryAdjacencyMap" $ \m ->- consistent m-- test "Consistency of fromAdjacencyList" $ \xs ->- consistent (fromAdjacencyList xs)-- testShow t- testBasicPrimitives t- testFromAdjacencyList t- testIsSubgraphOf t- testProperties t- testAdjacencyList t- testPostIntSet t- testGraphFamilies t- testTransformations t- testDfsForest t- testDfsForestFrom t- testDfs t- testTopSort t- testIsTopSort t-- putStrLn "\n============ IntAdjacencyMap.Internal.GraphKL ============"- test "map (fromVertexKL h) (vertices $ toGraphKL h) == IntSet.toAscList (vertexIntSet g)"- $ \g -> let h = mkGraphKL (adjacencyMap g) in- map (fromVertexKL h) (KL.vertices $ toGraphKL h) == IntSet.toAscList (vertexIntSet g)-- test "map (\\(x, y) -> (fromVertexKL h x, fromVertexKL h y)) (edges $ toGraphKL h) == edgeList g"- $ \g -> let h = mkGraphKL (adjacencyMap g) in- map (\(x, y) -> (fromVertexKL h x, fromVertexKL h y)) (KL.edges $ toGraphKL h) == edgeList g
test/Algebra/Graph/Test/NonEmptyGraph.hs view
@@ -22,6 +22,7 @@ #endif import Control.Monad+import Data.Either import Data.List.NonEmpty (NonEmpty (..)) import Data.Maybe import Data.Tree@@ -29,12 +30,12 @@ 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 Algebra.Graph.Class as C-import qualified Data.List.NonEmpty as NonEmpty-import qualified Data.Set as Set-import qualified Data.IntSet as IntSet+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 @@ -99,7 +100,7 @@ toNonEmptyGraph (G.empty :: G.Graph Int) == Nothing test "toNonEmptyGraph (toGraph x) == Just (x :: NonEmptyGraph a)" $ \x ->- toNonEmptyGraph (C.toGraph x) == Just (x :: NonEmptyGraph Int)+ toNonEmptyGraph (toGraph x) == Just (x :: NonEmptyGraph Int) putStrLn $ "\n============ Graph.NonEmpty.vertex ============" test "hasVertex x (vertex x) == True" $ \(x :: Int) ->@@ -440,19 +441,25 @@ 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.starTranspose ============"- test "starTranspose x [] == vertex x" $ \(x :: Int) ->- starTranspose x [] == vertex x+ putStrLn $ "\n============ Graph.NonEmpty.stars1 ============"+ test "stars1 ((x, []) :| []) == vertex x" $ \(x :: Int) ->+ stars1 ((x, []) :| []) == vertex x - test "starTranspose x [y] == edge y x" $ \(x :: Int) y ->- starTranspose x [y] == edge y x+ test "stars1 ((x, [y]) :| []) == edge x y" $ \(x :: Int) y ->+ stars1 ((x, [y]) :| []) == edge x y - test "starTranspose x [y,z] == edges1 ((y,x) :| [(z,x)])" $ \(x :: Int) y z ->- starTranspose x [y,z] == edges1 ((y,x) :| [(z,x)])+ test "stars1 ((x, ys) :| []) == star x ys" $ \(x :: Int) ys ->+ stars1 ((x, ys) :| []) == star x ys - test "starTranspose x ys == transpose (star x ys)" $ \(x :: Int) ys ->- starTranspose x ys == transpose (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@@ -481,9 +488,14 @@ , ((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 (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')@@ -496,6 +508,11 @@ , ((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@@ -633,3 +650,16 @@ 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
@@ -41,19 +41,12 @@ test "Consistency of arbitraryRelation" $ \(m :: RI) -> consistent m - test "Consistency of fromAdjacencyList" $ \xs ->- consistent (fromAdjacencyList xs :: RI)-- testShow t- testBasicPrimitives t- testFromAdjacencyList t- testIsSubgraphOf t- testProperties t- testAdjacencyList t- testPreSet t- testPostSet t- testGraphFamilies t- testTransformations t+ testShow t+ testBasicPrimitives t+ testIsSubgraphOf t+ testToGraph t+ testGraphFamilies t+ testTransformations t putStrLn "\n============ Relation.compose ============" test "compose empty x == empty" $ \(x :: RI) ->
+ test/Data/Graph/Test/Typed.hs view
@@ -0,0 +1,163 @@+-----------------------------------------------------------------------------+-- |+-- Module : Data.Graph.Test.Typed+-- Copyright : (c) Andrey Mokhov 2016-2018+-- License : MIT (see the file LICENSE)+-- Maintainer : anfelor@posteo.de, andrey.mokhov@gmail.com+-- Stability : experimental+--+-- Testsuite for "Data.Graph.Typed".+-----------------------------------------------------------------------------+module Data.Graph.Test.Typed (+ -- * Testsuite+ testTyped+ ) where++import qualified Algebra.Graph.AdjacencyMap as AM+import qualified Algebra.Graph.AdjacencyIntMap as AIM+import Algebra.Graph.Test+import Data.Array (array)+import Data.Graph.Typed+import Data.Tree+import Data.List++import qualified Data.Graph as KL+import qualified Data.IntSet as IntSet++type AI = AM.AdjacencyMap Int++(%) :: (GraphKL Int -> a) -> AM.AdjacencyMap Int -> a+a % g = a $ fromAdjacencyMap g++testTyped :: IO ()+testTyped = do+ putStrLn "\n============ Typed ============"++ putStrLn "\n============ Typed.fromAdjacencyMap ============"++ test "toGraphKL (fromAdjacencyMap (1 * 2 + 3 * 1)) == array (0,2) [(0,[1]), (1,[]), (2,[0])]" $+ toGraphKL (fromAdjacencyMap (1 * 2 + 3 * 1 :: AI)) == array (0,2) [(0,[1]), (1,[]), (2,[0])]++ test "toGraphKL (fromAdjacencyMap (1 * 2 + 2 * 1)) == array (0,1) [(0,[1]), (1,[0])]" $+ toGraphKL (fromAdjacencyMap (1 * 2 + 2 * 1 :: AI)) == array (0,1) [(0,[1]), (1,[0])]++ test "map (fromVertexKL h) (vertices $ toGraphKL h) == vertexList g"+ $ \(g :: AI) -> let h = fromAdjacencyMap g in+ map (fromVertexKL h) (KL.vertices $ toGraphKL h) == AM.vertexList g++ test "map (\\(x, y) -> (fromVertexKL h x, fromVertexKL h y)) (edges $ toGraphKL h) == edgeList g"+ $ \(g :: AI) -> let h = fromAdjacencyMap g in+ map (\(x, y) -> (fromVertexKL h x, fromVertexKL h y)) (KL.edges $ toGraphKL h) == AM.edgeList g++ putStrLn "\n============ Typed.fromAdjacencyIntMap ============"++ test "toGraphKL (fromAdjacencyIntMap (1 * 2 + 3 * 1)) == array (0,2) [(0,[1]), (1,[]), (2,[0])]" $+ toGraphKL (fromAdjacencyIntMap (1 * 2 + 3 * 1)) == array (0,2) [(0,[1]), (1,[]), (2,[0])]++ test "toGraphKL (fromAdjacencyIntMap (1 * 2 + 2 * 1)) == array (0,1) [(0,[1]), (1,[0])]" $+ toGraphKL (fromAdjacencyIntMap (1 * 2 + 2 * 1)) == array (0,1) [(0,[1]), (1,[0])]++ test "map (fromVertexKL h) (vertices $ toGraphKL h) == IntSet.toAscList (vertexIntSet g)"+ $ \g -> let h = fromAdjacencyIntMap g in+ map (fromVertexKL h) (KL.vertices $ toGraphKL h) == IntSet.toAscList (AIM.vertexIntSet g)++ test "map (\\(x, y) -> (fromVertexKL h x, fromVertexKL h y)) (edges $ toGraphKL h) == edgeList g"+ $ \g -> let h = fromAdjacencyIntMap g in+ map (\(x, y) -> (fromVertexKL h x, fromVertexKL h y)) (KL.edges $ toGraphKL h) == AIM.edgeList g++ putStrLn $ "\n============ Typed.dfsForest ============"+ test "forest (dfsForest % edge 1 1) == vertex 1" $+ AM.forest (dfsForest % AM.edge 1 1) == AM.vertex 1++ test "forest (dfsForest % edge 1 2) == edge 1 2" $+ AM.forest (dfsForest % AM.edge 1 2) == AM.edge 1 2++ test "forest (dfsForest % edge 2 1) == vertices [1, 2]" $+ AM.forest (dfsForest % AM.edge 2 1) == AM.vertices [1, 2]++ test "isSubgraphOf (forest $ dfsForest % x) x == True" $ \x ->+ AM.isSubgraphOf (AM.forest $ dfsForest % x) x == True++ test "dfsForest % forest (dfsForest % x) == dfsForest % x" $ \x ->+ dfsForest % AM.forest (dfsForest % x) == dfsForest % x++ test "dfsForest % vertices vs == map (\\v -> Node v []) (nub $ sort vs)" $ \vs ->+ dfsForest % AM.vertices vs == map (\v -> Node v []) (nub $ sort vs)++ test "dfsForest % (3 * (1 + 4) * (1 + 5)) == <correct result>" $+ dfsForest % (3 * (1 + 4) * (1 + 5)) == [ Node { rootLabel = 1+ , subForest = [ Node { rootLabel = 5+ , subForest = [] }]}+ , Node { rootLabel = 3+ , subForest = [ Node { rootLabel = 4+ , subForest = [] }]}]++ putStrLn $ "\n============ Typed.dfsForestFrom ============"+ test "forest (dfsForestFrom [1] % edge 1 1) == vertex 1" $+ AM.forest (dfsForestFrom [1] % AM.edge 1 1) == AM.vertex 1++ test "forest (dfsForestFrom [1] % edge 1 2) == edge 1 2" $+ AM.forest (dfsForestFrom [1] % AM.edge 1 2) == AM.edge 1 2++ test "forest (dfsForestFrom [2] % edge 1 2) == vertex 2" $+ AM.forest (dfsForestFrom [2] % AM.edge 1 2) == AM.vertex 2++ test "forest (dfsForestFrom [3] % edge 1 2) == empty" $+ AM.forest (dfsForestFrom [3] % AM.edge 1 2) == AM.empty++ test "forest (dfsForestFrom [2, 1] % edge 1 2) == vertices [1, 2]" $+ AM.forest (dfsForestFrom [2, 1] % AM.edge 1 2) == AM.vertices [1, 2]++ test "isSubgraphOf (forest $ dfsForestFrom vs % x) x == True" $ \vs x ->+ AM.isSubgraphOf (AM.forest (dfsForestFrom vs % x)) x == True++ test "dfsForestFrom (vertexList x) % x == dfsForest % x" $ \x ->+ dfsForestFrom (AM.vertexList x) % x == dfsForest % x++ test "dfsForestFrom vs % (AM.vertices vs) == map (\\v -> Node v []) (nub vs)" $ \vs ->+ dfsForestFrom vs % AM.vertices vs == map (\v -> Node v []) (nub vs)++ test "dfsForestFrom [] % x == []" $ \x ->+ dfsForestFrom [] % x == []++ test "dfsForestFrom [1, 4] % 3 * (1 + 4) * (1 + 5) == <correct result>" $+ dfsForestFrom [1, 4] % (3 * (1 + 4) * (1 + 5)) == [ Node { rootLabel = 1+ , subForest = [ Node { rootLabel = 5+ , subForest = [] }]}+ , Node { rootLabel = 4+ , subForest = [] }]++ putStrLn $ "\n============ Typed.dfs ============"+ test "dfs [1] % edge 1 1 == [1]" $+ dfs [1] % AM.edge 1 1 == [1]++ test "dfs [1] % edge 1 2 == [1,2]" $+ dfs [1] % AM.edge 1 2 == [1,2]++ test "dfs [2] % edge 1 2 == [2]" $+ dfs [2] % AM.edge 1 2 == [2]++ test "dfs [3] % edge 1 2 == []" $+ dfs [3] % AM.edge 1 2 == []++ test "dfs [1, 2] % edge 1 2 == [1, 2]" $+ dfs [1, 2] % AM.edge 1 2 == [1, 2]++ test "dfs [2, 1] % edge 1 2 == [2, 1]" $+ dfs [2, 1] % AM.edge 1 2 == [2, 1]++ test "dfs [] % x == []" $ \x ->+ dfs [] % x == []++ test "dfs [1, 4] % 3 * (1 + 4) * (1 + 5) == [1, 5, 4]" $+ dfs [1, 4] % (3 * (1 + 4) * (1 + 5)) == [1, 5, 4]++ test "isSubgraphOf (vertices $ dfs vs % x) x == True" $ \vs x ->+ AM.isSubgraphOf (AM.vertices $ dfs vs % x) x == True++ putStrLn "\n============ Typed.topSort ============"+ test "topSort % (1 * 2 + 3 * 1) == [3,1,2]" $+ topSort % (1 * 2 + 3 * 1) == ([3,1,2] :: [Int])++ test "topSort % (1 * 2 + 2 * 1) == [1,2]" $+ topSort % (1 * 2 + 2 * 1) == ([1,2] :: [Int])
test/Main.hs view
@@ -2,18 +2,20 @@ import Algebra.Graph.Test.Export import Algebra.Graph.Test.Fold import Algebra.Graph.Test.Graph-import Algebra.Graph.Test.IntAdjacencyMap+import Algebra.Graph.Test.AdjacencyIntMap import Algebra.Graph.Test.Internal import Algebra.Graph.Test.NonEmptyGraph import Algebra.Graph.Test.Relation+import Data.Graph.Test.Typed main :: IO () main = do+ testAdjacencyIntMap testAdjacencyMap testExport testFold testGraph testGraphNonEmpty- testIntAdjacencyMap testInternal testRelation+ testTyped