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

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 the AdjacencyIntMap data type and associated functions. See Algebra.Graph.AdjacencyIntMap.Algorithm for implementations of basic graph algorithms. AdjacencyIntMap is an instance of the C.Graph type class, which can be used for polymorphic graph construction and manipulation. See Algebra.Graph.AdjacencyMap for graphs with non-Int vertices.

  • 1 type
  • 46 values

Data structure

3 declarations
newtypenewtype AdjacencyIntMap
#

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))

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

The Show instance is defined using basic graph construction primitives:

show (empty     :: AdjacencyIntMap Int) == "empty"
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:

  • overlay is commutative and associative:

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

      x * empty == x
      empty * x == 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 and m will denote the number of vertices and edges in the graph, respectively.

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

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

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

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

  • Compare the sets of edges.

Here are a few examples:

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

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

isSubgraphOf x y ==> x <= y
empty <= x
x     <= x + y
x + y <= x * y
Instances13Eq, Num, Ord, Show, Generic, Semigroup, …

Basic graph construction primitives

9 declarations

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

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

Relations on graphs

1 declaration

Graph properties

12 declarations
valueadjacencyList :: AdjacencyIntMap -> [(Int, [Int])]
#

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, [])]
adjacencyList (star 2 [3,1]) == [(1, []), (2, [1,3]), (3, [])]
stars . adjacencyList        == id

Standard families of graphs

9 declarations
valueclique :: [Int] -> AdjacencyIntMap
#

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
valuetree :: Tree Int -> AdjacencyIntMap
#

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)]

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

Graph transformation

7 declarations

Merge vertices satisfying a given predicate into a given vertex. Complexity: O((n + m) * log(n)) time, 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

Construct the induced subgraph of a given graph by removing the vertices that do not satisfy a given predicate. Complexity: O(n + m) time, 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

Relational operations

5 declarations

Left-to-right relational composition of graphs: vertices x and z are connected in the resulting graph if there is a vertex y, such that x is connected to y in the first graph, and y is connected to z in the second graph. There are no isolated vertices in the result. This operation is associative, has empty and single-vertex graphs as annihilating zeroes, and distributes over overlay. Complexity: O(n * m * log(n)) time and O(n + m) memory.

compose empty            x                == empty
compose x                empty            == empty
compose (vertex x)       y                == empty
compose x                (vertex y)       == empty
compose x                (compose y z)    == compose (compose x y) z
compose x                (overlay y z)    == overlay (compose x y) (compose x z)
compose (overlay x y)    z                == overlay (compose x z) (compose y z)
compose (edge x y)       (edge y z)       == edge x z
compose (path    [1..5]) (path    [1..5]) == edges [(1,3), (2,4), (3,5)]
compose (circuit [1..5]) (circuit [1..5]) == circuit [1,3,5,2,4]

Compute the reflexive and transitive closure of a graph. Complexity: O(n * m * log(n)^2) time.

closure empty            == empty
closure (vertex x)       == edge x x
closure (edge x x)       == edge x x
closure (edge x y)       == edges [(x,x), (x,y), (y,y)]
closure (path $ nub xs) == reflexiveClosure (clique $ nub xs)
closure                  == reflexiveClosure . transitiveClosure
closure                  == transitiveClosure . reflexiveClosure
closure . closure        == closure
postIntSet x (closure y) == IntSet.fromList (reachable y x)

Miscellaneous

1 declaration

Check that 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.

consistent empty         == True
consistent (vertex x)    == True
consistent (overlay x y) == True
consistent (connect x y) == True
consistent (edge x y)    == True
consistent (edges xs)    == True
consistent (stars xs)    == True