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.Bipartite.AdjacencyMap

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 AdjacencyMap data type for undirected bipartite graphs and associated functions. See Algebra.Graph.Bipartite.AdjacencyMap.Algorithm for basic bipartite graph algorithms.

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

import qualified Algebra.Graph.Bipartite.AdjacencyMap as Bipartite
  • 2 types
  • 52 values

Data structure

3 declarations
datadata AdjacencyMap a b
#

The Bipartite.AdjacencyMap data type represents an undirected bipartite graph. The two type parameters determine the types of vertices of each part. If the types coincide, the vertices of the left part are still treated as disjoint from the vertices of the right part. See examples for more details.

We define a Num instance as a convenient notation for working with bipartite graphs:

0                     == rightVertex 0
swap 1                == leftVertex 1
swap 1 + 2            == vertices [1] [2]
swap 1 * 2            == edge 1 2
swap 1 + 2 * swap 3   == overlay (leftVertex 1) (edge 3 2)
swap 1 * (2 + swap 3) == connect (leftVertex 1) (vertices [3] [2])

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                 == "empty"
show 1                     == "rightVertex 1"
show (swap 2)              == "leftVertex 2"
show (1 + 2)               == "vertices [] [1,2]"
show (swap (1 + 2))        == "vertices [1,2] []"
show (swap 1 * 2)          == "edge 1 2"
show (swap 1 * 2 * swap 3) == "edges [(1,2),(3,2)]"
show (swap 1 * 2 + swap 3) == "overlay (leftVertex 3) (edge 1 2)"

The Eq instance satisfies all axioms of undirected bipartite algebraic graphs:

  • overlay is commutative and associative:

          x + y == y + x
    x + (y + z) == (x + y) + z
  • connect is commutative, associative 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
  • connect has the same effect as overlay on vertices of the same part:

     leftVertex x * leftVertex y  ==  leftVertex x + leftVertex y
    rightVertex x * rightVertex y == rightVertex x + rightVertex y

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 of the graph, respectively. In addition, l and r will denote the number of vertices in the left and right parts of the graph, respectively.

Instances8Eq, Num, Ord, Show, Generic, Semigroup, …

Basic graph construction primitives

12 declarations
valueoverlay
  1. :: (Ord a, Ord b)
  2. => AdjacencyMap a b
  3. -> AdjacencyMap a b
  4. -> AdjacencyMap a b
#

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
valueconnect
  1. :: (Ord a, Ord b)
  2. => AdjacencyMap a b
  3. -> AdjacencyMap a b
  4. -> AdjacencyMap a b
#

Connect two graphs, filtering out the edges between vertices of the same part. This is a commutative and 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 in the arguments: O(m1 + m2 + l1 * r2 + l2 * r1).

connect (leftVertex x)     (leftVertex y)     == vertices [x,y] []
connect (leftVertex x)     (rightVertex y)    == edge x y
connect (rightVertex x)    (leftVertex y)     == edge y x
connect (rightVertex x)    (rightVertex y)    == vertices [] [x,y]
connect (vertices xs1 ys1) (vertices xs2 ys2) == overlay (biclique xs1 ys2) (biclique xs2 ys1)
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)                     >= leftVertexCount x * rightVertexCount y
edgeCount   (connect x y)                     <= leftVertexCount x * rightVertexCount y + rightVertexCount x * leftVertexCount y + edgeCount x + edgeCount y

Conversion functions

4 declarations
valuetoBipartite
  1. :: (Ord a, Ord b)
  2. => AdjacencyMap (Either a b)
  3. -> AdjacencyMap a b
#

Construct a bipartite AdjacencyMap from an Algebra.Graph.AdjacencyMap, adding any missing edges to make the graph undirected and filtering out the edges within the same parts. Complexity: O(m * log(n)).

toBipartite empty                      == empty
toBipartite (vertex (Left x))          == leftVertex x
toBipartite (vertex (Right x))         == rightVertex x
toBipartite (edge (Left x) (Left y))   == vertices [x,y] []
toBipartite (edge (Left x) (Right y))  == edge x y
toBipartite (edge (Right x) (Left y))  == edge y x
toBipartite (edge (Right x) (Right y)) == vertices [] [x,y]
toBipartite . clique                   == uncurry biclique . partitionEithers
toBipartite . fromBipartite            == id
valuetoBipartiteWith
  1. :: (Ord a, Ord b, Ord c)
  2. => a -> Either b c
  3. -> AdjacencyMap a
  4. -> AdjacencyMap b c
#

Construct a bipartite AdjacencyMap from an Algebra.Graph.AdjacencyMap, where the two parts are identified by a separate function, adding any missing edges to make the graph undirected and filtering out the edges within the same parts. Complexity: O(m * log(n)).

toBipartiteWith f empty == empty
toBipartiteWith Left x  == vertices (vertexList x) []
toBipartiteWith Right x == vertices [] (vertexList x)
toBipartiteWith f       == toBipartite . gmap f
toBipartiteWith id      == toBipartite

Graph properties

19 declarations
valueedgeList :: AdjacencyMap a b -> [(a, b)]
#

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 . edges    == nub . sort
valueleftAdjacencyList :: AdjacencyMap a b -> [(a, [b])]
#

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

leftAdjacencyList empty            == []
leftAdjacencyList (vertices [] xs) == []
leftAdjacencyList (vertices xs []) == [(x, []) | x <- nub (sort xs)]
leftAdjacencyList (edge x y)       == [(x, [y])]
leftAdjacencyList (star x ys)      == [(x, nub (sort ys))]
valuerightAdjacencyList :: AdjacencyMap a b -> [(b, [a])]
#

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

rightAdjacencyList empty            == []
rightAdjacencyList (vertices [] xs) == [(x, []) | x <- nub (sort xs)]
rightAdjacencyList (vertices xs []) == []
rightAdjacencyList (edge x y)       == [(y, [x])]
rightAdjacencyList (star x ys)      == [(y, [x])  | y <- nub (sort ys)]

Standard families of graphs

9 declarations
datadata List a b
#

A list of values of two alternating types. The first type argument denotes the type of the value at the head.

With the OverloadedLists extension it is possible to use the standard list notation to construct a List where the two types coincide, for example:

[1, 2, 3, 4, 5] :: List Int Int

We make use of this shorthand notation in the examples below.

Constructors

Instances7IsList, Eq, Ord, Show, Generic, Rep, …
valueevenList :: [(a, b)] -> List a b
#

Construct a List of even length from a list of pairs.

evenList []                 == Nil
evenList [(1,2), (3,4)]     == [1, 2, 3, 4] :: List Int Int
evenList [(1,'a'), (2,'b')] == Cons 1 (Cons 'a' (Cons 2 (Cons 'b' Nil)))
valueoddList :: a -> [(b, a)] -> List a b
#

Construct a List of odd length given the first element and a list of pairs.

oddList 1 []                 == Cons 1 Nil
oddList 1 [(2,3), (4,5)]     == [1, 2, 3, 4, 5] :: List Int Int
oddList 1 [('a',2), ('b',3)] == Cons 1 (Cons 'a' (Cons 2 (Cons 'b' (Cons 3 Nil))))
valuecircuit :: (Ord a, Ord b) => [(a, b)] -> AdjacencyMap a b
#

The circuit on a list of pairs of vertices. Complexity: O(L * log(L)) time, where L is the length of the given list.

circuit []                    == empty
circuit [(x,y)]               == edge x y
circuit [(1,2), (3,4), (5,6)] == edges [(1,2), (3,2), (3,4), (5,4), (5,6), (1,6)]
circuit . reverse             == swap . circuit . map Data.Tuple.swap
valuestar :: (Ord a, Ord b) => a -> [b] -> AdjacencyMap a b
#

The star formed by a center vertex connected to a list of leaves. Complexity: O(L * log(L)) time, where L is the length of the given list.

star x []    == leftVertex x
star x [y]   == edge x y
star x [y,z] == edges [(x,y), (x,z)]
star x ys    == connect (leftVertex x) (vertices [] ys)
valuemesh :: (Ord a, Ord b) => [a] -> [b] -> AdjacencyMap (a, b) (a, b)
#

Construct a mesh graph from two lists of vertices. Complexity: O(L1 * L2 * log(L1 * L2)) time, where L1 and L2 are the lengths of the given lists.

mesh xs []           == empty
mesh [] ys           == empty
mesh [x] [y]         == leftVertex (x,y)
mesh [1,1] ['a','b'] == biclique [(1,'a'), (1,'b')] [(1,'a'), (1,'b')]
mesh [1,2] ['a','b'] == biclique [(1,'a'), (2,'b')] [(1,'b'), (2,'a')]

Graph transformation

4 declarations

Graph composition

2 declarations
valuebox
  1. :: (Ord a, Ord b)
  2. => AdjacencyMap a a
  3. -> AdjacencyMap b b
  4. -> AdjacencyMap (a, b) (a, b)
#

Compute the Cartesian product of two graphs. Complexity: O((n + m) * log(n)) time and O(n + m) memory.

box (path [0,1]) (path ['a','b']) == edges [ ((0,'a'), (0,'b'))
                                           , ((0,'a'), (1,'a'))
                                           , ((1,'b'), (0,'b'))
                                           , ((1,'b'), (1,'a')) ]

Up to isomorphism between the resulting vertex types, this operation is commutative, associative, distributes over overlay, has singleton graphs as identities and swapping identities, and empty as the annihilating zero. Below ~~ stands for 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 (leftVertex ())  ~~ x
box x (rightVertex ()) ~~ swap x
box x empty            ~~ empty
vertexCount (box x y)  == vertexCount x * vertexCount y
edgeCount   (box x y)  == vertexCount x * edgeCount y + edgeCount x * vertexCount y
valueboxWith
  1. :: (Ord a, Ord b, Ord c, Ord d, Ord e, Ord f)
  2. => a -> c -> e
  3. -> b -> d -> e
  4. -> a -> d -> f
  5. -> b -> c -> f
  6. -> AdjacencyMap a b
  7. -> AdjacencyMap c d
  8. -> AdjacencyMap e f
#

Compute the generalised Cartesian product of two graphs. The resulting vertices are obtained using the given vertex combinators. Complexity: O((n + m) * log(n)) time and O(n + m) memory.

See box for some examples.

box == boxWith (,) (,) (,) (,)

Miscellaneous

1 declaration
valueconsistent :: (Ord a, Ord b) => AdjacencyMap a b -> Bool
#

Check that the internal graph representation is consistent, i.e. that all edges that are present in the leftAdjacencyMap are also present in the rightAdjacencyMap map. 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 (edge x y)      == True
consistent (edges x)       == True
consistent (toBipartite x) == True
consistent (swap x)        == True
consistent (circuit x)     == True
consistent (biclique x y)  == True