| Copyright | (c) Andrey Mokhov 2016-2020 |
|---|---|
| License | MIT (see the file LICENSE) |
| Maintainer | andrey.mokhov@gmail.com |
| Stability | experimental |
| Safe Haskell | None |
| Language | Haskell2010 |
Algebra.Graph.Bipartite.Undirected.AdjacencyMap
Contents
Description
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. To avoid name clashes with
Algebra.Graph.AdjacencyMap, this module can be imported qualified:
import qualified Algebra.Graph.Bipartite.Undirected.AdjacencyMap as Bipartite
Synopsis
- data AdjacencyMap a b
- leftAdjacencyMap :: AdjacencyMap a b -> Map a (Set b)
- rightAdjacencyMap :: AdjacencyMap a b -> Map b (Set a)
- empty :: AdjacencyMap a b
- leftVertex :: a -> AdjacencyMap a b
- rightVertex :: b -> AdjacencyMap a b
- vertex :: Either a b -> AdjacencyMap a b
- edge :: a -> b -> AdjacencyMap a b
- overlay :: (Ord a, Ord b) => AdjacencyMap a b -> AdjacencyMap a b -> AdjacencyMap a b
- connect :: (Ord a, Ord b) => AdjacencyMap a b -> AdjacencyMap a b -> AdjacencyMap a b
- vertices :: (Ord a, Ord b) => [a] -> [b] -> AdjacencyMap a b
- edges :: (Ord a, Ord b) => [(a, b)] -> AdjacencyMap a b
- overlays :: (Ord a, Ord b) => [AdjacencyMap a b] -> AdjacencyMap a b
- connects :: (Ord a, Ord b) => [AdjacencyMap a b] -> AdjacencyMap a b
- swap :: AdjacencyMap a b -> AdjacencyMap b a
- toBipartite :: (Ord a, Ord b) => AdjacencyMap (Either a b) -> AdjacencyMap a b
- toBipartiteWith :: (Ord a, Ord b, Ord c) => (a -> Either b c) -> AdjacencyMap a -> AdjacencyMap b c
- fromBipartite :: (Ord a, Ord b) => AdjacencyMap a b -> AdjacencyMap (Either a b)
- fromBipartiteWith :: Ord c => (a -> c) -> (b -> c) -> AdjacencyMap a b -> AdjacencyMap c
- isEmpty :: AdjacencyMap a b -> Bool
- hasLeftVertex :: Ord a => a -> AdjacencyMap a b -> Bool
- hasRightVertex :: Ord b => b -> AdjacencyMap a b -> Bool
- hasVertex :: (Ord a, Ord b) => Either a b -> AdjacencyMap a b -> Bool
- hasEdge :: (Ord a, Ord b) => a -> b -> AdjacencyMap a b -> Bool
- leftVertexCount :: AdjacencyMap a b -> Int
- rightVertexCount :: AdjacencyMap a b -> Int
- vertexCount :: AdjacencyMap a b -> Int
- edgeCount :: AdjacencyMap a b -> Int
- leftVertexList :: AdjacencyMap a b -> [a]
- rightVertexList :: AdjacencyMap a b -> [b]
- vertexList :: AdjacencyMap a b -> [Either a b]
- edgeList :: AdjacencyMap a b -> [(a, b)]
- leftVertexSet :: AdjacencyMap a b -> Set a
- rightVertexSet :: AdjacencyMap a b -> Set b
- vertexSet :: (Ord a, Ord b) => AdjacencyMap a b -> Set (Either a b)
- edgeSet :: (Ord a, Ord b) => AdjacencyMap a b -> Set (a, b)
- circuit :: (Ord a, Ord b) => [(a, b)] -> AdjacencyMap a b
- biclique :: (Ord a, Ord b) => [a] -> [b] -> AdjacencyMap a b
- type OddCycle a = [a]
- detectParts :: Ord a => AdjacencyMap a -> Either (OddCycle a) (AdjacencyMap a a)
- consistent :: (Ord a, Ord b) => AdjacencyMap a b -> Bool
Data structure
data AdjacencyMap a b Source #
The AdjacencyMap data type represents an undirected bipartite
graph. The two type parameteters define the types of identifiers of the vertices
of each part.
Note: even if the identifiers and their types for two vertices of different parts are equal, these vertices are considered to be different. See examples for more details.
We define a Num instance as a convenient notation for working with bipartite
graphs:
0 == rightVertex 0swap1 == leftVertex 1swap1 + 2 == vertices [1] [2]swap1 * 2 == edge 1 2swap1 + 2 *swap3 == overlay (leftVertex 1) (edge 3 2)swap1 * (2 +swap3) == 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 (swap2) == "leftVertex 2" show (1 + 2) == "vertices [] [1,2]" show (swap(1 + 2)) == "vertices [1,2] []" show (swap1 * 2) == "edge 1 2" show (swap1 * 2 *swap3) == "edges [(1,2),(3,2)]" show (swap1 * 2 +swap3) == "overlay (leftVertex 3) (edge 1 2)"
The Eq instance satisfies all axioms of algebraic graphs:
overlayis commutative and associative:x + y == y + x x + (y + z) == (x + y) + z
connectis commutative, associative and hasemptyas the identity:x * empty == x empty * x == x x * y == y * x x * (y * z) == (x * y) * zconnectdistributes overoverlay:x * (y + z) == x * y + x * z (x + y) * z == x * z + y * z
connectcan be decomposed:x * y * z == x * y + x * z + y * z
connecthas the same effect asoverlayon vertices of one 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.
overlayhasemptyas the identity and is idempotent:x + empty == x empty + x == x x + x == xAbsorption 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. In addition, l and r will denote the number of vertices in the left and in the right part of graph, respectively.
Instances
leftAdjacencyMap :: AdjacencyMap a b -> Map a (Set b) Source #
The adjacency map of the left part of the graph: each left vertex is associated with a set of its right neighbours. Complexity: O(1) time and memory.
leftAdjacencyMapempty== Map.emptyleftAdjacencyMap (leftVertexx) == Map.singletonx Set.emptyleftAdjacencyMap (rightVertexx) == Map.emptyleftAdjacencyMap (edgex y) == Map.singletonx (Set.singletony)
rightAdjacencyMap :: AdjacencyMap a b -> Map b (Set a) Source #
The adjacency map of the right part of the graph: each right vertex is associated with a set of left neighbours. Complexity: O(1) time and memory.
rightAdjacencyMapempty== Map.emptyrightAdjacencyMap (leftVertexx) == Map.emptyrightAdjacencyMap (rightVertexx) == Map.singletonx Set.emptyrightAdjacencyMap (edgex y) == Map.singletony (Set.singletonx)
Basic graph construction primitives
empty :: AdjacencyMap a b Source #
Construct the empty graph. Complexity: O(1) time and memory.
isEmptyempty == TrueleftAdjacencyMapempty == Map.emptyrightAdjacencyMapempty == Map.emptyhasVertexx empty == False
leftVertex :: a -> AdjacencyMap a b Source #
Construct the bipartite graph comprising a single isolated vertex in the left part. Complexity: O(1) time and memory.
leftAdjacencyMap(leftVertex x) == Map.singletonx Set.emptyrightAdjacencyMap(leftVertex x) == Map.emptyhasLeftVertexx (leftVertex y) == (x == y)hasRightVertexx (leftVertex y) == FalsehasEdgex y (leftVertex z) == False
rightVertex :: b -> AdjacencyMap a b Source #
Construct the bipartite graph comprising a single isolated vertex in the right part. Complexity: O(1) time and memory.
leftAdjacencyMap(rightVertex x) == Map.emptyrightAdjacencyMap(rightVertex x) == Map.singletonx Set.emptyhasLeftVertexx (rightVertex y) == FalsehasRightVertexx (rightVertex y) == (x == y)hasEdgex y (rightVertex z) == False
vertex :: Either a b -> AdjacencyMap a b Source #
Construct the bipartite graph comprising a single isolated vertex. Complexity: O(1) time and memory.
vertex . Left ==leftVertexvertex . Right ==rightVertex
edge :: a -> b -> AdjacencyMap a b Source #
Construct the bipartite graph comprising a single edge. Complexity: O(1) time and memory.
edge x y ==connect(leftVertexx) (rightVertexy)leftAdjacencyMap(edge x y) == Map.singletonx (Set.singletony)rightAdjacencyMap(edge x y) == Map.singletony (Set.singletonx)hasEdgex y (edge x y) == TruehasEdge1 2 (edge 2 1) == False
overlay :: (Ord a, Ord b) => AdjacencyMap a b -> AdjacencyMap a b -> AdjacencyMap a b Source #
Overlay two bipartite 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) ==isEmptyx &&isEmptyyhasVertexz (overlay x y) ==hasVertexz x ||hasVertexz yvertexCount(overlay x y) >=vertexCountxvertexCount(overlay x y) <=vertexCountx +vertexCountyedgeCount(overlay x y) >=edgeCountxedgeCount(overlay x y) <=edgeCountx +edgeCounty
connect :: (Ord a, Ord b) => AdjacencyMap a b -> AdjacencyMap a b -> AdjacencyMap a b Source #
Connect two bipartite graphs, not adding the edges between vertices in
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 (leftVertexx) (leftVertexy) ==vertices[x,y] [] connect (leftVertexx) (rightVertexy) ==edgex y connect (rightVertexx) (leftVertexy) ==edgey x connect (rightVertexx) (rightVertexy) ==vertices[] [x,y] connect (verticesxs1 ys1) (verticesxs2 ys2) ==overlay(bicliquexs1 ys2) (bicliquexs2 ys1)isEmpty(connect x y) ==isEmptyx &&isEmptyyhasVertexz (connect x y) ==hasVertexz x ||hasVertexz yvertexCount(connect x y) >=vertexCountxvertexCount(connect x y) <=vertexCountx +vertexCountyedgeCount(connect x y) >=edgeCountxedgeCount(connect x y) >=leftVertexCountx *rightVertexCountyedgeCount(connect x y) <=leftVertexCountx *rightVertexCounty +rightVertexCountx *leftVertexCounty +edgeCountx +edgeCounty
vertices :: (Ord a, Ord b) => [a] -> [b] -> AdjacencyMap a b Source #
Construct the graph comprising two given lists of isolated vertices for each part. Complexity: O(L * log(L)) time and O(L) memory, where L is the total length of two lists.
vertices [] [] ==emptyvertices [x] [] ==leftVertexx vertices [] [x] ==rightVertexxhasLeftVertexx (vertices xs ys) ==elemx xshasRightVertexy (vertices xs ys) ==elemy ys
overlays :: (Ord a, Ord b) => [AdjacencyMap a b] -> AdjacencyMap a b Source #
connects :: (Ord a, Ord b) => [AdjacencyMap a b] -> AdjacencyMap a b Source #
swap :: AdjacencyMap a b -> AdjacencyMap b a Source #
Conversion functions
toBipartite :: (Ord a, Ord b) => AdjacencyMap (Either a b) -> AdjacencyMap a b Source #
Construct a bipartite AdjacencyMap from an Algebra.Graph.AdjacencyMap
with given part identifiers, adding all needed edges to make the graph
undirected and removing all edges within the same parts.
Complexity: O(m * log(n)).
toBipartiteempty==emptytoBipartite (vertex(Left x)) ==leftVertexx toBipartite (vertex(Right x)) ==rightVertexx toBipartite (edge(Left x) (Left y)) ==vertices[x,y] [] toBipartite (edge(Left x) (Right y)) ==edgex y toBipartite (edge(Right x) (Left y)) ==edgey x toBipartite (edge(Right x) (Right y)) ==vertices[] [x,y] toBipartite (cliquexs) ==uncurrybiclique(partitionEithersxs) toBipartite .fromBipartite==id
toBipartiteWith :: (Ord a, Ord b, Ord c) => (a -> Either b c) -> AdjacencyMap a -> AdjacencyMap b c Source #
Construct a bipartite AdjacencyMap from Algebra.Graph.AdjacencyMap
with part identifiers obtained from a given function, adding all neeeded
edges to make the graph undirected and removing all edges within the same
parts.
Complexity: O(m * log(n)).
toBipartiteWith fempty==emptytoBipartiteWith Left x ==vertices(vertexListx) [] toBipartiteWith Right x ==vertices[] (vertexListx) toBipartiteWith f ==toBipartite.gmapf toBipartiteWith id ==toBipartite
fromBipartite :: (Ord a, Ord b) => AdjacencyMap a b -> AdjacencyMap (Either a b) Source #
Construct an AdjacencyMap from a bipartite AdjacencyMap.
Complexity: O(m * log(n)).
fromBipartiteempty==emptyfromBipartite (leftVertexx) ==vertex(Left x) fromBipartite (edgex y) ==edges[(Left x, Right y), (Right y, Left x)]toBipartite. fromBipartite ==id
fromBipartiteWith :: Ord c => (a -> c) -> (b -> c) -> AdjacencyMap a b -> AdjacencyMap c Source #
Construct an AdjacencyMap from a bipartite AdjacencyMap
given a way to inject vertices from different parts into the resulting vertex
type.
Complexity: O(m * log(n)).
fromBipartiteWith Left Right ==fromBipartitefromBipartiteWith id id (verticesxs ys) ==vertices(xs ++ ys) fromBipartiteWith id id .edges==symmetricClosure.edges
Graph properties
isEmpty :: AdjacencyMap a b -> Bool Source #
hasLeftVertex :: Ord a => a -> AdjacencyMap a b -> Bool Source #
Check if a graph contains a given vertex in the left part. Complexity: O(log(n)) time.
hasLeftVertex xempty== False hasLeftVertex x (leftVertexy) == (x == y) hasLeftVertex x (rightVertexy) == False
hasRightVertex :: Ord b => b -> AdjacencyMap a b -> Bool Source #
Check if a graph contains a given vertex in the right part. Complexity: O(log(n)) time.
hasRightVertex xempty== False hasRightVertex x (leftVertexy) == False hasRightVertex x (rightVertexy) == (x == y)
hasVertex :: (Ord a, Ord b) => Either a b -> AdjacencyMap a b -> Bool Source #
Check if a graph contains a given vertex. Complexity: O(log(n)) time.
hasVertex . Left ==hasLeftVertexhasVertex . Right ==hasRightVertex
leftVertexCount :: AdjacencyMap a b -> Int Source #
The number of vertices in the left part in a graph. Complexity: O(1) time.
leftVertexCountempty== 0 leftVertexCount (leftVertexx) == 1 leftVertexCount (rightVertexx) == 0 leftVertexCount (edgex y) == 1 leftVertexCount .edges==length.nub.mapfst
rightVertexCount :: AdjacencyMap a b -> Int Source #
The number of vertices in the right part in a graph. Complexity: O(1) time.
rightVertexCountempty== 0 rightVertexCount (leftVertexx) == 0 rightVertexCount (rightVertexx) == 1 rightVertexCount (edgex y) == 1 rightVertexCount .edges==length.nub.mapsnd
vertexCount :: AdjacencyMap a b -> Int Source #
The number of vertices in a graph. Complexity: O(1) time.
vertexCountempty== 0 vertexCount (vertexx) == 1 vertexCount (edgex y) == 2 vertexCount x ==leftVertexCountx +rightVertexCountx
edgeCount :: AdjacencyMap a b -> Int Source #
leftVertexList :: AdjacencyMap a b -> [a] Source #
The sorted list of vertices of the left part of a given graph. Complexity: O(l) time and memory.
leftVertexListempty== [] leftVertexList (leftVertexx) == [x] leftVertexList (rightVertexx) == [] leftVertexList .flipvertices[] ==nub.sort
rightVertexList :: AdjacencyMap a b -> [b] Source #
The sorted list of vertices of the right part of a given graph. Complexity: O(r) time and memory.
rightVertexListempty== [] rightVertexList (leftVertexx) == [] rightVertexList (rightVertexx) == [x] rightVertexList .vertices[] ==nub.sort
vertexList :: AdjacencyMap a b -> [Either a b] Source #
edgeList :: AdjacencyMap a b -> [(a, b)] Source #
leftVertexSet :: AdjacencyMap a b -> Set a Source #
The set of vertices of the left part of a given graph. Complexity: O(l) time and memory.
leftVertexSetempty== Set.emptyleftVertexSet .leftVertex== Set.singletonleftVertexSet .rightVertex==constSet.emptyleftVertexSet .flipvertices[] == Set.fromList
rightVertexSet :: AdjacencyMap a b -> Set b Source #
The set of vertices of the right part of a given graph. Complexity: O(r) time and memory.
rightVertexSetempty== Set.emptyrightVertexSet .leftVertex==constSet.emptyrightVertexSet .rightVertex== Set.singletonrightVertexSet .vertices[] == Set.fromList
Standard families of graphs
circuit :: (Ord a, Ord b) => [(a, b)] -> AdjacencyMap a b Source #
The circuit on a list of vertices. Complexity: O(n * log(n)) time and O(n) memory.
circuit [] ==emptycircuit [(x,y)] ==edgex y circuit [(1,2), (3,4)] ==biclique[1,3] [2,4] circuit [(1,2), (3,4), (5,6)] ==edges[(1,2), (3,2), (3,4), (5,4), (5,6), (1,6)] circuit .reverse==swap. circuit .mapData.Tuple.swap
Algorithms
type OddCycle a = [a] Source #
An cycle of odd length. For example, [1, 2, 3] represents the cycle
1 -> 2 -> 3 -> 1.
detectParts :: Ord a => AdjacencyMap a -> Either (OddCycle a) (AdjacencyMap a a) Source #
Test the bipartiteness of given graph. In case of success, return an
AdjacencyMap with the same set of edges and each vertex marked with the
part it belongs to. In case of failure, return any cycle of odd length in the
graph.
The returned partition is lexicographically minimal. That is, consider the string of part identifiers for each vertex in ascending order. Then, considering that the identifier of the left part is less then the identifier of the right part, this string is lexicographically minimal of all such strings for all partitions.
The returned cycle is optimal in the following way: there exists a path that
is either empty or ends in a vertex adjacent to the first vertex in the
cycle, such that all vertices in path ++ cycle are distinct and
path ++ cycle is lexicographically minimal among all such pairs of paths
and cycles.
Note: since AdjacencyMap represents undirected bipartite graphs, all
edges in the input graph are treated as undirected. See the examples and the
correctness property for a clarification.
It is advised to use leftVertexList and rightVertexList to obtain the
partition of the vertices and hasLeftVertex and hasRightVertex to check
whether a vertex belongs to a part.
Complexity: O((n + m) * log(n)) time and O(n + m) memory.
detectPartsempty== RightemptydetectParts (vertexx) == Right (leftVertexx) detectParts (edgex x) == Left [x] detectParts (edge1 2) == Right (edge1 2) detectParts (1 * (2 + 3)) == Right (edges[(1,2), (1,3)]) detectParts (1 * 2 * 3) == Left [1, 2, 3] detectParts ((1 + 3) * (2 + 4) + 6 * 5) == Right (swap(1 + 3) * (2 + 4) +swap5 * 6) detectParts ((1 * 3 * 4) + 2 * (1 + 2)) == Left [2] detectParts (clique[1..10]) == Left [1, 2, 3] detectParts (circuit[1..10]) == Right (circuit[(x, x + 1) | x <- [1,3,5,7,9]]) detectParts (circuit[1..11]) == Left [1..11] detectParts (biclique[] xs) == Right (verticesxs []) detectParts (biclique(mapLeft (x:xs)) (mapRight ys)) == Right (biclique(mapLeft (x:xs)) (mapRight ys))isRight(detectParts (starx ys)) ==notElemx ysisRight(detectParts (fromBipartite(toBipartitex))) == True
The correctness of detectParts can be expressed by the following property:
let undirected =symmetricClosureinput in case detectParts input of Left cycle ->mod(length cycle) 2 == 1 &&isSubgraphOf(circuitcycle) undirected Right result ->gmapfromEither(fromBipartiteresult) == undirected
Miscellaneous
consistent :: (Ord a, Ord b) => AdjacencyMap a b -> Bool Source #
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.
consistentempty== True consistent (vertexx) == True consistent (edgex y) == True consistent (edgesx) == True consistent (toBipartitex) == True consistent (swapx) == True consistent (circuitx) == True consistent (bicliquex y) == True