HORIZON HASKELLDocslts/ghc-9.14.xd82dee82026-10-12Search names, modules, packages, or :: a typeCtrl K

GHC 9.14.1 · lts/ghc-9.14.x · d82dee8 · 2026-10-12

Modulealgebraic-graphs-0.8Haskell2010

Algebra.Graph.Undirected

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 defines an undirected version of algebraic graphs. Undirected graphs satisfy all laws of the Undirected type class, including the commutativity of connect.

To avoid name clashes with Algebra.Graph, this module can be imported qualified:

import qualified Algebra.Graph.Undirected as Undirected
  • 1 type
  • 41 values

Algebraic data type for graphs

3 declarations
newtypenewtype Graph a
#

The Graph data type provides the four algebraic graph construction primitives empty, vertex, overlay and connect, as well as various derived functions. The only difference compared to the Graph data type defined in Algebra.Graph is that the connect operation is commutative. We define a Num instance as a convenient notation for working with undirected graphs:

0           == vertex 0
1 + 2       == overlay (vertex 1) (vertex 2)
1 * 2       == connect (vertex 1) (vertex 2)
1 + 2 * 3   == overlay (vertex 1) (connect (vertex 2) (vertex 3))
1 * (2 + 3) == connect (vertex 1) (overlay (vertex 2) (vertex 3))

Note: the Num instance does not satisfy several "customary laws" of Num, which dictate that fromInteger 0 and fromInteger 1 should act as additive and multiplicative identities, and negate as additive inverse. Nevertheless, overloading fromInteger, + and * is very convenient when working with algebraic graphs; we hope that in future Haskell's Prelude will provide a more fine-grained class hierarchy for algebraic structures, which we would be able to utilise without violating any laws.

The Eq instance is currently implemented using the Relation as the canonical graph representation and satisfies all axioms of algebraic graphs:

  • overlay is commutative and associative:

          x + y == y + x
    x + (y + z) == (x + y) + z
  • connect is associative, commutative and has empty as the identity:

      x * empty == x
      empty * x == x
          x * y == y * x
    x * (y * z) == (x * y) * z
  • connect distributes over overlay:

    x * (y + z) == x * y + x * z
    (x + y) * z == x * z + y * z
  • 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.

  • overlay has empty as the identity and is idempotent:

      x + empty == x
      empty + x == x
          x + x == x
  • Absorption and saturation of connect:

    x * y + x + y == x * y
        x * x * x == x * x

When specifying the time and memory complexity of graph algorithms, n will denote the number of vertices in the graph, m will denote the number of edges in the graph, and s will denote the size of the corresponding Graph expression. For example, if g is a Graph then n, m and s can be computed as follows:

n == vertexCount g
m == edgeCount g
s == size g

Note that size counts all leaves of the expression:

vertexCount empty           == 0
size        empty           == 1
vertexCount (vertex x)      == 1
size        (vertex x)      == 1
vertexCount (empty + empty) == 0
size        (empty + empty) == 2

Converting an undirected Graph to the corresponding Relation takes O(s + m * log(m)) time and O(s + m) memory. This is also the complexity of the graph equality test, because it is currently implemented by converting graph expressions to canonical representations based on adjacency maps.

The total order on graphs is defined using size-lexicographic comparison:

  • Compare the number of vertices. In case of a tie, continue.

  • Compare the sets of vertices. In case of a tie, continue.

  • Compare the number of edges. In case of a tie, continue.

  • Compare the sets of edges.

Here are a few examples:

vertex 1 < vertex 2
vertex 3 < edge 1 2
vertex 1 < edge 1 1
edge 1 1 < edge 1 2
edge 1 2 < edge 1 1 + edge 2 2
edge 1 2 < edge 1 3
edge 1 2 == edge 2 1

Note that the resulting order refines the isSubgraphOf relation and is compatible with overlay and connect operations:

isSubgraphOf x y ==> x <= y
empty <= x
x     <= x + y
x + y <= x * y
Instances18Monad, Functor, Applicative, Alternative, MonadPlus, Eq, …

Basic graph construction primitives

9 declarations
valueoverlay :: Graph a -> Graph a -> Graph a
#

Overlay two graphs. This is a commutative, associative and idempotent operation with the identity empty. Complexity: O(1) time and memory, O(s1 + s2) size.

isEmpty     (overlay x y) == isEmpty   x   && isEmpty   y
hasVertex z (overlay x y) == hasVertex z x || hasVertex z y
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
size        (overlay x y) == size x        + size y
vertexCount (overlay 1 2) == 2
edgeCount   (overlay 1 2) == 0
valueconnect :: Graph a -> Graph a -> Graph a
#

Connect two graphs. This is a commutative and associative operation with the identity empty, which distributes over overlay and obeys the decomposition axiom. Complexity: O(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 x y               == connect y x
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 div 2
size        (connect x y) == size x        + size y
vertexCount (connect 1 2) == 2
edgeCount   (connect 1 2) == 1
valueedges :: [(a, a)] -> Graph a
#

Construct the graph from a list of edges. Complexity: O(L) time, memory and size, where L is the length of the given list.

edges []             == empty
edges [(x,y)]        == edge x y
edges [(x,y), (y,x)] == edge x y
valueoverlays :: [Graph a] -> Graph a
#

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
valueconnects :: [Graph a] -> Graph a
#

Connect 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.

connects []        == empty
connects [x]       == x
connects [x,y]     == connect x y
connects           == foldr connect empty
isEmpty . connects == all isEmpty
connects           == connects . reverse

Graph folding

1 declaration
valuefoldg :: b -> (a -> b) -> (b -> b -> b) -> (b -> b -> b) -> Graph a -> b
#

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 the given functions. As an example, the complexity of size is O(s), since const and + have constant costs.

foldg empty vertex        overlay connect        == id
foldg empty vertex        overlay (flip connect) == id
foldg 1     (const 1)     (+)     (+)            == size
foldg True  (const False) (&&)    (&&)           == isEmpty
foldg False (== x)        (||)    (||)           == hasVertex x

Relations on graphs

2 declarations
valueisSubgraphOf :: Ord a => Graph a -> Graph a -> Bool
#

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 (edge x y)    (edge y x)    ==  True
isSubgraphOf x y                         ==> x <= y

Graph properties

12 declarations
valuesize :: Graph a -> Int
#

The size of a graph, i.e. the number of leaves of the expression including empty leaves. Complexity: O(s) time.

size empty         == 1
size (vertex x)    == 1
size (overlay x y) == size x + size y
size (connect x y) == size x + size y
size x             >= 1
size x             >= vertexCount x
valuehasEdge :: Eq a => a -> a -> Graph a -> Bool
#

Check if a graph contains a given edge. Complexity: O(s) time.

hasEdge x y empty            == False
hasEdge x y (vertex z)       == False
hasEdge x y (edge x y)       == True
hasEdge x y (edge y x)       == True
hasEdge x y . removeEdge x y == const False
hasEdge x y                  == elem (min x y, max x y) . edgeList
valueedgeCount :: Ord a => Graph a -> Int
#

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.

edgeCount empty      == 0
edgeCount (vertex x) == 0
edgeCount (edge x y) == 1
edgeCount            == length . edgeList
valuevertexList :: Ord a => Graph a -> [a]
#

The sorted list of vertices of a given graph. Complexity: O(s * log(n)) time and O(n) memory.

vertexList empty      == []
vertexList (vertex x) == [x]
vertexList . vertices == nub . sort
valueedgeList :: Ord a => Graph a -> [(a, a)]
#

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.

edgeList empty          == []
edgeList (vertex x)     == []
edgeList (edge x y)     == [(min x y, max y x)]
edgeList (star 2 [3,1]) == [(1,2), (2,3)]
valueadjacencyList :: Ord a => Graph a -> [(a, [a])]
#

The sorted adjacency list of a graph. Complexity: O(n + m) time and memory.

adjacencyList empty          == []
adjacencyList (vertex x)     == [(x, [])]
adjacencyList (edge 1 2)     == [(1, [2]), (2, [1])]
adjacencyList (star 2 [3,1]) == [(1, [2]), (2, [1,3]), (3, [2])]
stars . adjacencyList        == id

Standard families of graphs

8 declarations
valuepath :: [a] -> Graph a
#

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 == path
valuecircuit :: [a] -> Graph a
#

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]     == edge (x,y)
circuit . reverse == circuit
valueclique :: [a] -> Graph a
#

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  == clique
valuebiclique :: [a] -> [a] -> Graph a
#

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.

biclique []      []      == empty
biclique [x]     []      == vertex x
biclique []      [y]     == vertex y
biclique [x1,x2] [y1,y2] == edges [(x1,y1), (x1,y2), (x2,x2), (x2,y2)]
biclique xs      ys      == connect (vertices xs) (vertices ys)
valuestar :: a -> [a] -> Graph a
#

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.

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)
valuetree :: Tree a -> Graph a
#

The tree graph constructed from a given Tree data structure. Complexity: O(T) time, memory and size, where T is the size of the given tree (i.e. the number of vertices in the tree).

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)]
valueforest :: Forest a -> Graph a
#

The forest graph constructed from a given Forest data structure. Complexity: O(F) time, memory and size, where F is the size of the given forest (i.e. the number of vertices in the forest).

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

Graph transformation

7 declarations
valueremoveEdge :: Eq a => a -> a -> Graph a -> Graph a
#

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 . removeEdge x y == removeEdge x y
removeEdge x y                  == removeEdge y x
removeEdge x y . removeVertex x == removeVertex x
removeEdge 1 1 (1 * 1 * 2 * 2)  == 1 * 2 * 2
removeEdge 1 2 (1 * 1 * 2 * 2)  == 1 * 1 + 2 * 2
valuemergeVertices :: (a -> Bool) -> a -> Graph a -> Graph a
#

Merge vertices satisfying a given predicate into a given vertex. Complexity: O(s) time, memory and size, assuming that the predicate takes constant time.

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
valueinduce :: (a -> Bool) -> Graph a -> Graph a
#

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
valuecomplement :: Ord a => Graph a -> Graph a
#

The edge complement of a graph. Note that, as can be seen from the examples below, this operation ignores self-loops. Complexity: O(n^2 * log n) time, O(n^2) memory.

complement empty           == empty
complement (vertex x)      == (vertex x)
complement (edge 1 2)      == (vertices [1, 2])
complement (edge 0 0)      == (edge 0 0)
complement (star 1 [2, 3]) == (overlay (vertex 1) (edge 2 3))
complement . complement    == id