Copyright | (c) Andrey Mokhov 2016-2022 |
---|---|
License | MIT (see the file LICENSE) |
Maintainer | andrey.mokhov@gmail.com |
Stability | experimental |
Safe Haskell | None |
Language | Haskell2010 |
Alga is a library for algebraic construction and manipulation of graphs in Haskell. See 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.
Synopsis
- data Graph e a
- empty :: Graph e a
- vertex :: a -> Graph e a
- edge :: e -> a -> a -> Graph e a
- (-<) :: a -> e -> (a, e)
- (>-) :: (a, e) -> a -> Graph e a
- overlay :: Monoid e => Graph e a -> Graph e a -> Graph e a
- connect :: e -> Graph e a -> Graph e a -> Graph e a
- vertices :: Monoid e => [a] -> Graph e a
- edges :: Monoid e => [(e, a, a)] -> Graph e a
- overlays :: Monoid e => [ Graph e a] -> Graph e a
- foldg :: b -> (a -> b) -> (e -> b -> b -> b) -> Graph e a -> b
- buildg :: ( forall r. r -> (a -> r) -> (e -> r -> r -> r) -> r) -> Graph e a
- isSubgraphOf :: ( Eq e, Monoid e, Ord a) => Graph e a -> Graph e a -> Bool
- isEmpty :: Graph e a -> Bool
- size :: Graph e a -> Int
- hasVertex :: Eq a => a -> Graph e a -> Bool
- hasEdge :: ( Eq e, Monoid e, Ord a) => a -> a -> Graph e a -> Bool
- edgeLabel :: ( Eq a, Monoid e) => a -> a -> Graph e a -> e
- vertexList :: Ord a => Graph e a -> [a]
- edgeList :: ( Eq e, Monoid e, Ord a) => Graph e a -> [(e, a, a)]
- vertexSet :: Ord a => Graph e a -> Set a
- edgeSet :: ( Eq e, Monoid e, Ord a) => Graph e a -> Set (e, a, a)
- removeVertex :: Eq a => a -> Graph e a -> Graph e a
- removeEdge :: ( Eq a, Eq e, Monoid e) => a -> a -> Graph e a -> Graph e a
- replaceVertex :: Eq a => a -> a -> Graph e a -> Graph e a
- replaceEdge :: ( Eq e, Monoid e, Ord a) => e -> a -> a -> Graph e a -> Graph e a
- transpose :: Graph e a -> Graph e a
- emap :: (e -> f) -> Graph e a -> Graph f a
- induce :: (a -> Bool ) -> Graph e a -> Graph e a
- induceJust :: Graph e ( Maybe a) -> Graph e a
- closure :: ( Eq e, Ord a, StarSemiring e) => Graph e a -> Graph e a
- reflexiveClosure :: ( Ord a, Semiring e) => Graph e a -> Graph e a
- symmetricClosure :: Monoid e => Graph e a -> Graph e a
- transitiveClosure :: ( Eq e, Ord a, StarSemiring e) => Graph e a -> Graph e a
- type UnlabelledGraph a = Graph Any a
- type Automaton a s = Graph ( RegularExpression a) s
- type Network e a = Graph ( Distance e) a
- data Context e a = Context { }
- context :: ( Eq e, Monoid e) => (a -> Bool ) -> Graph e a -> Maybe ( Context e a)
Algebraic data type for edge-labelled graphs
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.
Instances
Construct the
empty graph
. An alias for the constructor
Empty
.
isEmpty
empty == TruehasVertex
x empty == FalsevertexCount
empty == 0edgeCount
empty == 0
vertex :: a -> Graph e a Source #
Construct the graph comprising
a single isolated vertex
. An alias for the
constructor
Vertex
.
isEmpty
(vertex x) == FalsehasVertex
x (vertex y) == (x == y)vertexCount
(vertex x) == 1edgeCount
(vertex x) == 0
edge :: e -> a -> a -> Graph e a Source #
Construct the graph comprising a single labelled edge .
edge e x y ==connect
e (vertex
x) (vertex
y) edgezero
x y ==vertices
[x,y]hasEdge
x y (edge e x y) == (e /=zero
)edgeLabel
x y (edge e x y) == eedgeCount
(edge e x y) == if e ==zero
then 0 else 1vertexCount
(edge e 1 1) == 1vertexCount
(edge e 1 2) == 2
(-<) :: a -> e -> (a, e) infixl 5 Source #
The left-hand part of a convenient ternary-ish operator
x-<e>-y
for
creating labelled edges.
x -<e>- y == edge
e x y
(>-) :: (a, e) -> a -> Graph e a infixl 5 Source #
The right-hand part of a convenient ternary-ish operator
x-<e>-y
for
creating labelled edges.
x -<e>- y == edge
e x y
overlay :: Monoid e => Graph e a -> Graph e a -> Graph e a Source #
Overlay
two graphs. An alias for
Connect
zero
.
Complexity:
O(1)
time and memory,
O(s1 + s2)
size.
isEmpty
(overlay x y) ==isEmpty
x &&isEmpty
yhasVertex
z (overlay x y) ==hasVertex
z x ||hasVertex
z yvertexCount
(overlay x y) >=vertexCount
xvertexCount
(overlay x y) <=vertexCount
x +vertexCount
yedgeCount
(overlay x y) >=edgeCount
xedgeCount
(overlay x y) <=edgeCount
x +edgeCount
yvertexCount
(overlay 1 2) == 2edgeCount
(overlay 1 2) == 0
Note:
overlay
composes edges in parallel using the operator
<+>
with
zero
acting as the identity:
edgeLabel
x y $ overlay (edge
e x y) (edge
zero
x y) == eedgeLabel
x y $ overlay (edge
e x y) (edge
f x y) == e<+>
f
Furthermore, when applied to transitive graphs,
overlay
composes edges in
sequence using the operator
<.>
with
one
acting as the identity:
edgeLabel
x z $transitiveClosure
(overlay (edge
e x y) (edge
one
y z)) == eedgeLabel
x z $transitiveClosure
(overlay (edge
e x y) (edge
f y z)) == e<.>
f
connect :: e -> Graph e a -> Graph e a -> Graph e a Source #
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)
.
isEmpty
(connect e x y) ==isEmpty
x &&isEmpty
yhasVertex
z (connect e x y) ==hasVertex
z x ||hasVertex
z yvertexCount
(connect e x y) >=vertexCount
xvertexCount
(connect e x y) <=vertexCount
x +vertexCount
yedgeCount
(connect e x y) <=vertexCount
x *vertexCount
y +edgeCount
x +edgeCount
yvertexCount
(connect e 1 2) == 2edgeCount
(connect e 1 2) == if e ==zero
then 0 else 1
vertices :: Monoid e => [a] -> Graph e a Source #
Construct the graph comprising a given list of isolated vertices. Complexity: O(L) time, memory and size, where L is the length of the given list.
vertices [] ==empty
vertices [x] ==vertex
x vertices ==overlays
. mapvertex
hasVertex
x . vertices ==elem
xvertexCount
. vertices ==length
.nub
vertexSet
. vertices == Set.fromList
overlays :: Monoid e => [ Graph e a] -> Graph e a Source #
Overlay a given list of graphs. Complexity: O(L) time and memory, and O(S) size, where L is the length of the given list, and S is the sum of sizes of the graphs in the list.
overlays [] ==empty
overlays [x] == x overlays [x,y] ==overlay
x y overlays ==foldr
overlay
empty
isEmpty
. overlays ==all
isEmpty
Graph folding
foldg :: b -> (a -> b) -> (e -> b -> b -> b) -> Graph e a -> b Source #
Generalised
Graph
folding: recursively collapse a
Graph
by applying
the provided functions to the leaves and internal nodes of the expression.
The order of arguments is: empty, vertex and connect.
Complexity:
O(s)
applications of the given functions. As an example, the
complexity of
size
is
O(s)
, since
const
and
+
have constant costs.
foldgempty
vertex
connect
==id
foldgempty
vertex
(fmap
flip
connect
) ==transpose
foldg 1 (const
1) (const
(+)) ==size
foldg True (const
False) (const
(&&)) ==isEmpty
foldg False (== x) (const
(||)) ==hasVertex
x foldg Set.empty
Set.singleton
(const
Set.union
) ==vertexSet
buildg :: ( forall r. r -> (a -> r) -> (e -> r -> r -> r) -> r) -> Graph e a Source #
Build a graph given an interpretation of the three graph construction
primitives
empty
,
vertex
and
connect
, in this order. See examples for
further clarification.
buildg f == fempty
vertex
connect
buildg (\e _ _ -> e) ==empty
buildg (\_ v _ -> v x) ==vertex
x buildg (\e v c -> c l (foldg
e v c x) (foldg
e v c y)) ==connect
l x y buildg (\e v c ->foldr
(czero
) e (map
v xs)) ==vertices
xs buildg (\e v c ->foldg
e v (flip
. c) g) ==transpose
gfoldg
e v c (buildg f) == f e v c
Relations on graphs
isSubgraphOf :: ( Eq e, Monoid e, Ord a) => Graph e a -> Graph e a -> Bool Source #
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
.
isSubgraphOfempty
x == True isSubgraphOf (vertex
x)empty
== False isSubgraphOf x (overlay
x y) == True isSubgraphOf (overlay
x y) (connect
x y) == True isSubgraphOf x y ==> x <= y
Graph properties
isEmpty :: Graph e a -> Bool Source #
Check if a graph is empty. Complexity: O(s) time.
isEmptyempty
== True isEmpty (overlay
empty
empty
) == True isEmpty (vertex
x) == False isEmpty (removeVertex
x $vertex
x) == True isEmpty (removeEdge
x y $edge
e x y) == False
hasVertex :: Eq a => a -> Graph e a -> Bool Source #
Check if a graph contains a given vertex. Complexity: O(s) time.
hasVertex xempty
== False hasVertex x (vertex
y) == (x == y) hasVertex x .removeVertex
x ==const
False
edgeLabel :: ( Eq a, Monoid e) => a -> a -> Graph e a -> e Source #
Extract the label of a specified edge from a graph.
vertexList :: Ord a => Graph e a -> [a] Source #
Graph transformation
removeEdge :: ( Eq a, Eq e, Monoid e) => a -> a -> Graph e a -> Graph e a Source #
Remove an edge from a given graph. Complexity: O(s) time, memory and size.
removeEdge x y (edge
e x y) ==vertices
[x,y] removeEdge x y . removeEdge x y == removeEdge x y removeEdge x y .removeVertex
x ==removeVertex
x removeEdge 1 1 (1 * 1 * 2 * 2) == 1 * 2 * 2 removeEdge 1 2 (1 * 1 * 2 * 2) == 1 * 1 + 2 * 2
replaceVertex :: Eq a => a -> a -> Graph e a -> Graph e a Source #
The function
replaces vertex
replaceVertex
x y
x
with vertex
y
in a
given
Graph
. If
y
already exists,
x
and
y
will be merged.
Complexity:
O(s)
time, memory and size.
replaceVertex x x == id replaceVertex x y (vertex
x) ==vertex
y replaceVertex x y ==fmap
(\v -> if v == x then y else v)
emap :: (e -> f) -> Graph e a -> Graph f a Source #
Transform a graph by applying a function to each of its edge labels. Complexity: O(s) time, memory and size.
The function
h
is required to be a
homomorphism
on the underlying type of
labels
e
. At the very least it must preserve
zero
and
<+>
:
hzero
==zero
h x<+>
h y == h (x<+>
y)
If
e
is also a semiring, then
h
must also preserve the multiplicative
structure:
hone
==one
h x<.>
h y == h (x<.>
y)
If the above requirements hold, then the implementation provides the following guarantees.
emap hempty
==empty
emap h (vertex
x) ==vertex
x emap h (edge
e x y) ==edge
(h e) x y emap h (overlay
x y) ==overlay
(emap h x) (emap h y) emap h (connect
e x y) ==connect
(h e) (emap h x) (emap h y) emapid
==id
emap g . emap h == emap (g . h)
induce :: (a -> Bool ) -> Graph e a -> Graph e a Source #
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 constant time.
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
induceJust :: Graph e ( Maybe a) -> Graph e a Source #
Construct the
induced subgraph
of a given graph by removing the vertices
that are
Nothing
.
Complexity:
O(s)
time, memory and size.
induceJust (vertex
Nothing
) ==empty
induceJust (edge
(Just
x)Nothing
) ==vertex
x induceJust .fmap
Just
==id
induceJust .fmap
(\x -> if p x thenJust
x elseNothing
) ==induce
p
Relational operations
closure :: ( Eq e, Ord a, StarSemiring e) => Graph e a -> Graph e a Source #
Compute the reflexive and transitive closure of a graph over the underlying star semiring using the Warshall-Floyd-Kleene algorithm.
closureempty
==empty
closure (vertex
x) ==edge
one
x x closure (edge
e x x) ==edge
one
x x closure (edge
e x y) ==edges
[(one
,x,x), (e,x,y), (one
,y,y)] closure ==reflexiveClosure
.transitiveClosure
closure ==transitiveClosure
.reflexiveClosure
closure . closure == closurepostSet
x (closure y) == Set.fromList
(reachable
x y)
reflexiveClosure :: ( Ord a, Semiring e) => Graph e a -> Graph e a Source #
Compute the
reflexive closure
of a graph over the underlying semiring by
adding a self-loop of weight
one
to every vertex.
Complexity:
O(n * log(n))
time.
reflexiveClosureempty
==empty
reflexiveClosure (vertex
x) ==edge
one
x x reflexiveClosure (edge
e x x) ==edge
one
x x reflexiveClosure (edge
e x y) ==edges
[(one
,x,x), (e,x,y), (one
,y,y)] reflexiveClosure . reflexiveClosure == reflexiveClosure
symmetricClosure :: Monoid e => Graph e a -> Graph e a Source #
Compute the symmetric closure of a graph by overlaying it with its own transpose. Complexity: O((n + m) * log(n)) time.
symmetricClosureempty
==empty
symmetricClosure (vertex
x) ==vertex
x symmetricClosure (edge
e x y) ==edges
[(e,x,y), (e,y,x)] symmetricClosure x ==overlay
x (transpose
x) symmetricClosure . symmetricClosure == symmetricClosure
transitiveClosure :: ( Eq e, Ord a, StarSemiring e) => Graph e a -> Graph e a Source #
Compute the transitive closure of a graph over the underlying star semiring using a modified version of the Warshall-Floyd-Kleene algorithm, which omits the reflexivity step.
transitiveClosureempty
==empty
transitiveClosure (vertex
x) ==vertex
x transitiveClosure (edge
e x y) ==edge
e x y transitiveClosure . transitiveClosure == transitiveClosure
Types of edge-labelled graphs
type UnlabelledGraph a = Graph Any a Source #
A type synonym for unlabelled graphs .
type Automaton a s = Graph ( RegularExpression a) s Source #
A type synonym for automata or labelled transition systems .
type Network e a = Graph ( Distance e) a Source #
A network is a graph whose edges are labelled with distances.
Context
The
Context
of a subgraph comprises its
inputs
and
outputs
, i.e. all
the vertices that are connected to the subgraph's vertices (along with the
corresponding edge labels). Note that inputs and outputs can belong to the
subgraph itself. In general, there are no guarantees on the order of vertices
in
inputs
and
outputs
; furthermore, there may be repetitions.
context :: ( Eq e, Monoid e) => (a -> Bool ) -> Graph e a -> Maybe ( Context e a) Source #
Extract the
Context
of a subgraph specified by a given predicate. Returns
Nothing
if the specified subgraph is empty.
context (const
False) x == Nothing context (== 1) (edge
e 1 2) == if e ==zero
then Just (Context
[] []) else Just (Context
[ ] [(e,2)]) context (== 2) (edge
e 1 2) == if e ==zero
then Just (Context
[] []) else Just (Context
[(e,1)] [ ]) context (const
True ) (edge
e 1 2) == if e ==zero
then Just (Context
[] []) else Just (Context
[(e,1)] [(e,2)]) context (== 4) (3 * 1 * 4 * 1 * 5) == Just (Context
[(one
,3), (one
,1)] [(one
,1), (one
,5)])