mini-2.0.0.0: src/Mini/Data/Graph.hs
-- | A structure representing unique vertices and their interrelations
module Mini.Data.Graph (
-- * Type
Graph,
graph,
-- * Algorithms
bfs,
distance,
path,
reachable,
sort,
-- * Construction
fromList,
singleton,
-- * Modification
add,
connect,
disconnect,
filter,
remove,
transpose,
-- * Partition
partition,
split,
-- * Query
edges,
incoming,
indegree,
indegrees,
member,
outdegree,
outdegrees,
outgoing,
sinkMax,
sinkMin,
sinks,
sourceMax,
sourceMin,
sources,
vertices,
) where
import Data.Foldable (
fold,
)
import Data.List (
unfoldr,
)
import Mini.Data.Map (
Map,
)
import qualified Mini.Data.Map as Map (
delete,
filter,
foldlWith,
foldrWith,
insertWith,
lookup,
member,
partition,
singleton,
split,
unionWith,
)
import Mini.Data.Recursion (
bool,
maybe,
uncurry,
)
import Mini.Data.Set (
Set,
)
import qualified Mini.Data.Set as Set (
delete,
difference,
filter,
member,
singleton,
)
import Mini.Hash.Class (
Hashable,
toBytes,
)
import Prelude (
Bool,
Eq,
Foldable,
Int,
Maybe (
Just,
Nothing
),
Monoid,
Ord,
Semigroup,
Show,
any,
compare,
concatMap,
const,
flip,
foldMap,
foldr,
length,
mempty,
null,
show,
($),
(+),
(.),
(<$>),
(<>),
(==),
)
-- Type
-- | A graph with directed edges between vertices of type /a/
data Graph a
= Graph
(Map a (Set a))
(Map a (Set a))
instance (Eq a) => Eq (Graph a) where
(Graph _ oes1) == (Graph _ oes2) = oes1 == oes2
instance (Ord a) => Ord (Graph a) where
compare (Graph _ oes1) (Graph _ oes2) = compare oes1 oes2
instance (Show a) => Show (Graph a) where
show = show . edges
instance Foldable Graph where
foldr f b = foldr f b . vertices
instance (Ord a) => Semigroup (Graph a) where
(Graph ies1 oes1) <> (Graph ies2 oes2) =
Graph
(Map.unionWith (const (<>)) ies1 ies2)
(Map.unionWith (const (<>)) oes1 oes2)
instance (Ord a) => Monoid (Graph a) where
mempty = Graph mempty mempty
instance (Hashable a) => Hashable (Graph a) where
toBytes = concatMap toBytes
-- | Primitive recursion on graphs (internally represented by adjacency maps)
graph
:: (Map a (Set a) -> Map a (Set a) -> b)
-- ^ Function applied to the adjacency maps of the graph:
-- incoming edges, outgoing edges
-> Graph a
-- ^ The graph
-> b
graph f (Graph ies oes) = f ies oes
-- Algorithms
-- | Breadth-first search for the hierarchy in a graph from a starting vertex
bfs :: (Ord a) => Graph a -> a -> [Set a]
bfs (Graph _ oes) s =
foldMap
( \vs ->
Set.singleton s
: unfoldr
( \((us, es), ds) ->
bool
( Just
( us
,
( foldr
( \u b@(us', es') ->
maybe
b
( \vs' ->
( (us' <> vs') `Set.difference` ds
, Map.delete u es'
)
)
$ Map.lookup u es
)
(mempty, es)
us
, ds <> us
)
)
)
Nothing
$ null us
)
((vs, oes), Set.singleton s)
)
$ Map.lookup s oes
-- | Get the shortest distance in a graph between a vertex and another
distance :: (Ord a) => Graph a -> a -> a -> Maybe Int
distance g s t =
foldr
(\a b -> bool ((+ 1) <$> b) (Just 0) $ t `Set.member` a)
Nothing
$ bfs g s
-- | Check whether there is a path in a graph from a vertex to another
path :: (Ord a) => Graph a -> a -> a -> Bool
path g s t = any (t `Set.member`) $ bfs g s
-- | Get the reachable vertices in a graph from a starting vertex
reachable :: (Ord a) => Graph a -> a -> Set a
reachable g = fold . bfs g
-- | Topologically sort a graph (assumes acyclicity)
sort :: (Ord a) => Graph a -> [a]
sort = unfoldr $ \g -> (\u -> (u, remove u g)) <$> sourceMin g
-- Construction
-- | Make a graph from a list of edges
fromList :: (Ord a) => [(a, a)] -> Graph a
fromList = foldr (uncurry connect) mempty
-- | Make a graph with an isolated vertex
singleton :: (Ord a) => a -> Graph a
singleton u = Graph (Map.singleton u mempty) (Map.singleton u mempty)
-- Modification
-- | Add an isolated vertex to a graph unless already present
add :: (Ord a) => a -> Graph a -> Graph a
add u (Graph ies oes) =
Graph
(Map.insertWith (const (<>)) u mempty ies)
(Map.insertWith (const (<>)) u mempty oes)
-- | Add edges from a vertex to another vertex in a graph
connect :: (Ord a) => a -> a -> Graph a -> Graph a
connect u v (Graph ies oes) =
Graph
( Map.insertWith (const (<>)) v (Set.singleton u) $
Map.insertWith (const (<>)) u mempty ies
)
( Map.insertWith (const (<>)) u (Set.singleton v) $
Map.insertWith (const (<>)) v mempty oes
)
-- | Remove an edge from a vertex to another vertex in a graph
disconnect :: (Ord a) => a -> a -> Graph a -> Graph a
disconnect u v (Graph ies oes) =
Graph
(Map.insertWith (const $ flip Set.difference) v (Set.singleton u) ies)
(Map.insertWith (const $ flip Set.difference) u (Set.singleton v) oes)
-- | Keep the vertices that satisfy a predicate in a graph
filter :: (Ord a) => (a -> Bool) -> Graph a -> Graph a
filter p (Graph ies oes) =
Graph
(Set.filter p <$> Map.filter (const . p) ies)
(Set.filter p <$> Map.filter (const . p) oes)
-- | Remove a vertex and its edges from a graph
remove :: (Ord a) => a -> Graph a -> Graph a
remove u (Graph ies oes) =
Graph
(Set.delete u <$> Map.delete u ies)
(Set.delete u <$> Map.delete u oes)
-- | Reverse the edges of a graph
transpose :: Graph a -> Graph a
transpose (Graph ies oes) = Graph oes ies
-- Partition
-- | Partition a graph with a predicate into @(true, false)@ subgraphs
partition :: (Ord a) => (a -> Bool) -> Graph a -> (Graph a, Graph a)
partition p (Graph ies oes) =
let (ies1, ies2) = Map.partition (const . p) ies
(oes1, oes2) = Map.partition (const . p) oes
in (Graph ies1 oes1, Graph ies2 oes2)
-- | Split a graph by a vertex into @(lt, eq, gt)@ subgraphs
split :: (Ord a) => a -> Graph a -> (Graph a, Maybe a, Graph a)
split v (Graph ies oes) =
let (lti, _, gti) = Map.split v ies
(lto, _, gto) = Map.split v oes
in (Graph lti lto, bool Nothing (Just v) $ v `Map.member` oes, Graph gti gto)
-- Query
-- | Get the edges of a graph
edges :: Graph a -> [(a, a)]
edges (Graph _ oes) =
Map.foldrWith
(\u -> flip $ foldr (\v -> (:) (u, v)))
[]
oes
-- | Get the incoming edges of each vertex in a graph
incoming :: Graph a -> Map a (Set a)
incoming (Graph ies _) = ies
-- | Get the number of incoming edges to a vertex in a graph
indegree :: (Ord a) => a -> Graph a -> Maybe Int
indegree v (Graph ies _) = length <$> Map.lookup v ies
-- | Get the number of incoming edges of each vertex in a graph
indegrees :: Graph a -> Map a Int
indegrees (Graph ies _) = length <$> ies
-- | Check whether a vertex is in a graph
member :: (Ord a) => a -> Graph a -> Bool
member u (Graph _ oes) = u `Map.member` oes
-- | Get the number of outgoing edges from a vertex in a graph
outdegree :: (Ord a) => a -> Graph a -> Maybe Int
outdegree u (Graph _ oes) = length <$> Map.lookup u oes
-- | Get the number of outgoing edges of each vertex in a graph
outdegrees :: Graph a -> Map a Int
outdegrees (Graph _ oes) = length <$> oes
-- | Get the outgoing edges of each vertex in a graph
outgoing :: Graph a -> Map a (Set a)
outgoing (Graph _ oes) = oes
-- | Get the maximum vertex with no outgoing edges from a graph
sinkMax :: Graph a -> Maybe a
sinkMax (Graph _ oes) =
Map.foldlWith
( \b k ->
bool
b
(Just k)
. null
)
Nothing
oes
-- | Get the minimum vertex with no outgoing edges from a graph
sinkMin :: Graph a -> Maybe a
sinkMin (Graph _ oes) =
Map.foldrWith
( \k a b ->
bool
b
(Just k)
$ null a
)
Nothing
oes
-- | Get the vertices with no outgoing edges from a graph
sinks :: Graph a -> [a]
sinks (Graph _ oes) =
Map.foldrWith
( \k a b ->
bool
b
(k : b)
$ null a
)
[]
oes
-- | Get the maximum vertex with no incoming edges from a graph
sourceMax :: Graph a -> Maybe a
sourceMax (Graph ies _) =
Map.foldlWith
( \b k ->
bool
b
(Just k)
. null
)
Nothing
ies
-- | Get the minimum vertex with no incoming edges from a graph
sourceMin :: Graph a -> Maybe a
sourceMin (Graph ies _) =
Map.foldrWith
( \k a b ->
bool
b
(Just k)
$ null a
)
Nothing
ies
-- | Get the vertices with no incoming edges from a graph
sources :: Graph a -> [a]
sources (Graph ies _) =
Map.foldrWith
( \k a b ->
bool
b
(k : b)
$ null a
)
[]
ies
-- | Get the vertices of a graph
vertices :: Graph a -> [a]
vertices (Graph _ oes) = Map.foldrWith (\k _ -> (:) k) [] oes