This module implements bipartite graphs.
AUTHORS:
TESTS:
sage: B = graphs.CompleteBipartiteGraph(7, 9)
sage: loads(dumps(B)) == B
True
sage: B = BipartiteGraph(graphs.CycleGraph(4))
sage: B == B.copy()
True
sage: type(B.copy())
<class 'sage.graphs.bipartite_graph.BipartiteGraph'>
Bases: sage.graphs.graph.Graph
Bipartite graph.
INPUT:
A reduced adjacency matrix contains only the non-redundant portion of the full adjacency matrix for the bipartite graph. Specifically, for zero matrices of the appropriate size, for the reduced adjacency matrix H, the full adjacency matrix is [[0, H'], [H, 0]].
The alist file format is described at http://www.inference.phy.cam.ac.uk/mackay/codes/alist.html
EXAMPLES:
No inputs or None for the input creates an empty graph:
sage: B = BipartiteGraph()
sage: type(B)
<class 'sage.graphs.bipartite_graph.BipartiteGraph'>
sage: B.order()
0
sage: B == BipartiteGraph(None)
True
From a graph: without any more information, finds a bipartition:
sage: B = BipartiteGraph(graphs.CycleGraph(4))
sage: B = BipartiteGraph(graphs.CycleGraph(5))
...
TypeError: Input graph is not bipartite!
sage: G = Graph({0:[5,6], 1:[4,5], 2:[4,6], 3:[4,5,6]})
sage: B = BipartiteGraph(G)
sage: B == G
True
sage: B.left
set([0, 1, 2, 3])
sage: B.right
set([4, 5, 6])
sage: B = BipartiteGraph({0:[5,6], 1:[4,5], 2:[4,6], 3:[4,5,6]})
sage: B == G
True
sage: B.left
set([0, 1, 2, 3])
sage: B.right
set([4, 5, 6])
From a graph and a partition. Note that if the input graph is not bipartite, then Sage will raise an error. However, if one specifies check=False, the offending edges are simply deleted (along with those vertices not appearing in either list). We also lump creating one bipartite graph from another into this category:
sage: P = graphs.PetersenGraph()
sage: partition = [range(5), range(5,10)]
sage: B = BipartiteGraph(P, partition)
...
TypeError: Input graph is not bipartite with respect to the given partition!
sage: B = BipartiteGraph(P, partition, check=False)
sage: B.left
set([0, 1, 2, 3, 4])
sage: B.show()
sage: G = Graph({0:[5,6], 1:[4,5], 2:[4,6], 3:[4,5,6]}) sage: B = BipartiteGraph(G) sage: B2 = BipartiteGraph(B) sage: B == B2 True sage: B3 = BipartiteGraph(G, [range(4), range(4,7)]) sage: B3 Bipartite graph on 7 vertices sage: B3 == B2 Truesage: G = Graph({0:[], 1:[], 2:[]}) sage: part = (range(2), [2]) sage: B = BipartiteGraph(G, part) sage: B2 = BipartiteGraph(B) sage: B == B2 True
From a reduced adjacency matrix:
sage: M = Matrix([(1,1,1,0,0,0,0), (1,0,0,1,1,0,0),
... (0,1,0,1,0,1,0), (1,1,0,1,0,0,1)])
sage: M
[1 1 1 0 0 0 0]
[1 0 0 1 1 0 0]
[0 1 0 1 0 1 0]
[1 1 0 1 0 0 1]
sage: H = BipartiteGraph(M); H
Bipartite graph on 11 vertices
sage: H.edges()
[(0, 7, None),
(0, 8, None),
(0, 10, None),
(1, 7, None),
(1, 9, None),
(1, 10, None),
(2, 7, None),
(3, 8, None),
(3, 9, None),
(3, 10, None),
(4, 8, None),
(5, 9, None),
(6, 10, None)]
sage: M = Matrix([(1, 1, 2, 0, 0), (0, 2, 1, 1, 1), (0, 1, 2, 1, 1)]) sage: B = BipartiteGraph(M, multiedges=True, sparse=True) sage: B.edges() [(0, 5, None), (1, 5, None), (1, 6, None), (1, 6, None), (1, 7, None), (2, 5, None), (2, 5, None), (2, 6, None), (2, 7, None), (2, 7, None), (3, 6, None), (3, 7, None), (4, 6, None), (4, 7, None)]sage: F.<a> = GF(4) sage: MS = MatrixSpace(F, 2, 3) sage: M = MS.matrix([[0, 1, a+1], [a, 1, 1]]) sage: B = BipartiteGraph(M, weighted=True, sparse=True) sage: B.edges() [(0, 4, a), (1, 3, 1), (1, 4, 1), (2, 3, a + 1), (2, 4, 1)] sage: B.weighted() True
From an alist file:
sage: file_name = SAGE_TMP + 'deleteme.alist.txt'
sage: fi = open(file_name, 'w')
sage: fi.write("7 4 \n 3 4 \n 3 3 1 3 1 1 1 \n 3 3 3 4 \n\
1 2 4 \n 1 3 4 \n 1 0 0 \n 2 3 4 \n\
2 0 0 \n 3 0 0 \n 4 0 0 \n\
1 2 3 0 \n 1 4 5 0 \n 2 4 6 0 \n 1 2 4 7 \n")
sage: fi.close();
sage: B = BipartiteGraph(file_name)
sage: B == H
True
From a NetworkX bipartite graph:
sage: import networkx
sage: G = graphs.OctahedralGraph()
sage: N = networkx.make_clique_bipartite(G.networkx_graph())
sage: B = BipartiteGraph(N)
Adds an edge from u and v.
INPUT:
The following forms are all accepted:
See Graph.add_edge for more detail. This method simply checks that the edge endpoints are in different partitions.
TEST:
sage: bg = BipartiteGraph()
sage: bg.add_vertices([0,1,2], left=[True,False,True])
sage: bg.add_edges([(0,1), (2,1)])
sage: bg.add_edge(0,2)
...
RuntimeError: Edge vertices must lie in different partitions.
Creates an isolated vertex. If the vertex already exists, then nothing is done.
INPUT:
Obviously, left and right are mutually exclusive.
As it is implemented now, if a graph has a large number of vertices with numeric labels, then G.add_vertex() could potentially be slow, if name is None.
EXAMPLES:
sage: G = BipartiteGraph()
sage: G.add_vertex(left=True)
sage: G.add_vertex(right=True)
sage: G.vertices()
[0, 1]
sage: G.left
set([0])
sage: G.right
set([1])
TESTS:
Exactly one of left and right must be true:
sage: G = BipartiteGraph()
sage: G.add_vertex()
...
RuntimeError: Partition must be specified (e.g. left=True).
sage: G.add_vertex(left=True, right=True)
...
RuntimeError: Only one partition may be specified.
Adding the same vertex must specify the same partition:
sage: bg = BipartiteGraph()
sage: bg.add_vertex(0, right=True)
sage: bg.add_vertex(0, right=True)
sage: bg.vertices()
[0]
sage: bg.add_vertex(0, left=True)
...
RuntimeError: Cannot add duplicate vertex to other partition.
Add vertices to the bipartite graph from an iterable container of vertices. Vertices that already exist in the graph will not be added again.
INPUTS:
Only one of left and right keywords should be provided. See the examples below.
EXAMPLES:
sage: bg = BipartiteGraph()
sage: bg.add_vertices([0,1,2], left=True)
sage: bg.add_vertices([3,4,5], left=[True, False, True])
sage: bg.add_vertices([6,7,8], right=[True, False, True])
sage: bg.add_vertices([9,10,11], right=True)
sage: bg.left
set([0, 1, 2, 3, 5, 7])
sage: bg.right
set([4, 6, 8, 9, 10, 11])
TEST:
sage: bg = BipartiteGraph()
sage: bg.add_vertices([0,1,2], left=True)
sage: bg.add_vertices([0,1,2], left=[True,True,True])
sage: bg.add_vertices([0,1,2], right=[False,False,False])
sage: bg.add_vertices([0,1,2], right=[False,False,False])
sage: bg.add_vertices([0,1,2])
...
RuntimeError: Partition must be specified (e.g. left=True).
sage: bg.add_vertices([0,1,2], left=True, right=True)
...
RuntimeError: Only one partition may be specified.
sage: bg.add_vertices([0,1,2], right=True)
...
RuntimeError: Cannot add duplicate vertex to other partition.
sage: (bg.left, bg.right)
(set([0, 1, 2]), set([]))
Returns the underlying bipartition of the bipartite graph.
EXAMPLE:
sage: B = BipartiteGraph(graphs.CycleGraph(4))
sage: B.bipartition()
(set([0, 2]), set([1, 3]))
Deletes vertex, removing all incident edges. Deleting a non-existent vertex will raise an exception.
INPUT:
EXAMPLES:
sage: B = BipartiteGraph(graphs.CycleGraph(4))
sage: B
Bipartite cycle graph: graph on 4 vertices
sage: B.delete_vertex(0)
sage: B
Bipartite cycle graph: graph on 3 vertices
sage: B.left
set([2])
sage: B.edges()
[(1, 2, {}), (2, 3, {})]
sage: B.delete_vertex(3)
sage: B.right
set([1])
sage: B.edges()
[(1, 2, {})]
sage: B.delete_vertex(0)
...
RuntimeError: Vertex (0) not in the graph.
sage: g = Graph({'a':['b'], 'c':['b']})
sage: bg = BipartiteGraph(g) # finds bipartition
sage: bg.vertices()
['a', 'b', 'c']
sage: bg.delete_vertex('a')
sage: bg.edges()
[('b', 'c', None)]
sage: bg.vertices()
['b', 'c']
sage: bg2 = BipartiteGraph(g)
sage: bg2.delete_vertex(0, in_order=True)
sage: bg2 == bg
True
Remove vertices from the bipartite graph taken from an iterable sequence of vertices. Deleting a non-existent vertex will raise an exception.
INPUT:
EXAMPLES:
sage: B = BipartiteGraph(graphs.CycleGraph(4))
sage: B
Bipartite cycle graph: graph on 4 vertices
sage: B.delete_vertices([0,3])
sage: B
Bipartite cycle graph: graph on 2 vertices
sage: B.left
set([2])
sage: B.right
set([1])
sage: B.edges()
[(1, 2, {})]
sage: B.delete_vertices([0])
...
RuntimeError: Vertex (0) not in the graph.
Loads into the current object the bipartite graph specified in the given file name. This file should follow David MacKay’s alist format, see http://www.inference.phy.cam.ac.uk/mackay/codes/data.html for examples and definition of the format.
EXAMPLE:
sage: file_name = SAGE_TMP + 'deleteme.alist.txt'
sage: fi = open(file_name, 'w')
sage: fi.write("7 4 \n 3 4 \n 3 3 1 3 1 1 1 \n 3 3 3 4 \n\
1 2 4 \n 1 3 4 \n 1 0 0 \n 2 3 4 \n\
2 0 0 \n 3 0 0 \n 4 0 0 \n\
1 2 3 0 \n 1 4 5 0 \n 2 4 6 0 \n 1 2 4 7 \n")
sage: fi.close();
sage: B = BipartiteGraph()
sage: B.load_afile(file_name)
Bipartite graph on 11 vertices
sage: B.edges()
[(0, 7, None),
(0, 8, None),
(0, 10, None),
(1, 7, None),
(1, 9, None),
(1, 10, None),
(2, 7, None),
(3, 8, None),
(3, 9, None),
(3, 10, None),
(4, 8, None),
(5, 9, None),
(6, 10, None)]
sage: B2 = BipartiteGraph(file_name)
sage: B2 == B
True
Overrides Graph’s plot function, to illustrate the bipartite nature.
EXAMPLE:
sage: B = BipartiteGraph(graphs.CycleGraph(20))
sage: B.plot()
Projects self onto left vertices. Edges are 2-paths in the original.
EXAMPLE:
sage: B = BipartiteGraph(graphs.CycleGraph(20))
sage: G = B.project_left()
sage: G.order(), G.size()
(10, 10)
Projects self onto right vertices. Edges are 2-paths in the original.
EXAMPLE:
sage: B = BipartiteGraph(graphs.CycleGraph(20))
sage: G = B.project_right()
sage: G.order(), G.size()
(10, 10)
Return the reduced adjacency matrix for the given graph.
A reduced adjacency matrix contains only the non-redundant portion of the full adjacency matrix for the bipartite graph. Specifically, for zero matrices of the appropriate size, for the reduced adjacency matrix H, the full adjacency matrix is [[0, H'], [H, 0]].
This method supports the named argument ‘sparse’ which defaults to True. When enabled, the returned matrix will be sparse.
EXAMPLES:
Bipartite graphs that are not weighted will return a matrix over ZZ:
sage: M = Matrix([(1,1,1,0,0,0,0), (1,0,0,1,1,0,0),
... (0,1,0,1,0,1,0), (1,1,0,1,0,0,1)])
sage: B = BipartiteGraph(M)
sage: N = B.reduced_adjacency_matrix()
sage: N
[1 1 1 0 0 0 0]
[1 0 0 1 1 0 0]
[0 1 0 1 0 1 0]
[1 1 0 1 0 0 1]
sage: N == M
True
sage: N[0,0].parent()
Integer Ring
Multi-edge graphs also return a matrix over ZZ:
sage: M = Matrix([(1,1,2,0,0), (0,2,1,1,1), (0,1,2,1,1)])
sage: B = BipartiteGraph(M, multiedges=True, sparse=True)
sage: N = B.reduced_adjacency_matrix()
sage: N == M
True
sage: N[0,0].parent()
Integer Ring
Weighted graphs will return a matrix over the ring given by their (first) weights:
sage: F.<a> = GF(4)
sage: MS = MatrixSpace(F, 2, 3)
sage: M = MS.matrix([[0, 1, a+1], [a, 1, 1]])
sage: B = BipartiteGraph(M, weighted=True, sparse=True)
sage: N = B.reduced_adjacency_matrix(sparse=False)
sage: N == M
True
sage: N[0,0].parent()
Finite Field in a of size 2^2
TESTS:
sage: B = BipartiteGraph()
sage: B.reduced_adjacency_matrix()
[]
sage: M = Matrix([[0,0], [0,0]])
sage: BipartiteGraph(M).reduced_adjacency_matrix() == M
True
sage: M = Matrix([[10,2/3], [0,0]])
sage: B = BipartiteGraph(M, weighted=True, sparse=True)
sage: M == B.reduced_adjacency_matrix()
True
Save the graph to file in alist format.
Saves this graph to file in David MacKay’s alist format, see http://www.inference.phy.cam.ac.uk/mackay/codes/data.html for examples and definition of the format.
EXAMPLE:
sage: M = Matrix([(1,1,1,0,0,0,0), (1,0,0,1,1,0,0),
... (0,1,0,1,0,1,0), (1,1,0,1,0,0,1)])
sage: M
[1 1 1 0 0 0 0]
[1 0 0 1 1 0 0]
[0 1 0 1 0 1 0]
[1 1 0 1 0 0 1]
sage: b = BipartiteGraph(M)
sage: file_name = SAGE_TMP + 'deleteme.alist.txt'
sage: b.save_afile(file_name)
sage: b2 = BipartiteGraph(file_name)
sage: b == b2
True
TESTS:
sage: file_name = SAGE_TMP + 'deleteme.alist.txt'
sage: for order in range(3, 13, 3):
... num_chks = int(order / 3)
... num_vars = order - num_chks
... partition = (range(num_vars), range(num_vars, num_vars+num_chks))
... for idx in range(100):
... g = graphs.RandomGNP(order, 0.5)
... try:
... b = BipartiteGraph(g, partition, check=False)
... b.save_afile(file_name)
... b2 = BipartiteGraph(file_name)
... if b != b2:
... print "Load/save failed for code with edges:"
... print b.edges()
... except:
... print "Exception encountered for graph of order "+ str(order)
... print "with edges: "
... g.edges()
... raise
Return an undirected Graph (without bipartite constraint) of the given object.
EXAMPLES:
sage: BipartiteGraph(graphs.CycleGraph(6)).to_undirected()
Cycle graph: Graph on 6 vertices