Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/graphs/graph_bundle.py
4036 views
1
r"""
2
Graph Bundles
3
4
This module implements graph bundles.
5
6
WARNING::
7
8
This module has known bugs. See the ``plot`` method, for example.
9
10
AUTHORS:
11
-- Robert L. Miller (2008-01-20): initial version
12
13
TESTS:
14
15
sage: B = graphs.CompleteBipartiteGraph(7,9)
16
sage: loads(dumps(B)) == B
17
True
18
19
"""
20
21
#*****************************************************************************
22
# Copyright (C) 2008 Robert L. Miller <[email protected]>
23
#
24
# Distributed under the terms of the GNU General Public License (GPL)
25
# http://www.gnu.org/licenses/
26
#*****************************************************************************
27
28
from graph import Graph
29
30
class GraphBundle(Graph):
31
32
def __init__(self, *args, **kwds):
33
r"""
34
Graph Bundle.
35
36
Note that an instance of the GraphBundle class is also a Graph instance-
37
this is the total space of the bundle. The base graph is self.base.
38
39
INPUT:
40
1. Empty: creates a zero vertex trivial graph bundle.
41
42
sage: B = GraphBundle()
43
sage: type(B)
44
<class 'sage.graphs.graph_bundle.GraphBundle'>
45
sage: type(B.base)
46
<class 'sage.graphs.graph.Graph'>
47
sage: B.order()
48
0
49
50
2. From a graph and a partition: the partition determines the fibers,
51
and we take the quotient map to the base.
52
53
sage: P = graphs.PetersenGraph()
54
sage: partition = [range(5), range(5,10)]
55
sage: B = GraphBundle(P, partition)
56
sage: B.base
57
Graph on 2 vertices
58
sage: B.base.size()
59
1
60
sage: B.fiber[0]
61
[0, 1, 2, 3, 4]
62
sage: B.projection[0]
63
0
64
sage: B.projection[5]
65
1
66
67
"""
68
if len(args) == 0:
69
Graph.__init__(self, sparse=True)
70
self.base = Graph()
71
self.fiber = {}
72
self.projection = {}
73
return
74
if isinstance(args[0], Graph):
75
G = args[0]
76
args = args[1:]
77
if isinstance(args[0], (list, tuple)):
78
if len(args[0])>0 and isinstance(args[0][0], (list, tuple)):
79
# Assume that the second argument is a partition of the vertices
80
from copy import copy
81
self.fiber = {}
82
self.projection = {}
83
partition = args[0]
84
args = args[1:]
85
Graph.__init__(self, G, sparse=True)
86
self.base = Graph(sparse=True)
87
base_size = len(partition)
88
self.base.add_vertices(xrange(base_size))
89
edge_list = []
90
for j in xrange(base_size):
91
par_j = partition[j]
92
self.fiber[j] = copy(par_j)
93
for i in xrange(j):
94
par_i = partition[i]
95
if len(par_i) < len(par_j):
96
if len( set(self.vertex_boundary(par_i)) & set(par_j) ) > 0:
97
edge_list.append((i, j))
98
else:
99
if len( set(self.vertex_boundary(par_j)) & set(par_i) ) > 0:
100
edge_list.append((i, j))
101
for v in par_j:
102
self.projection[v] = j
103
self.base.add_edges(edge_list)
104
105
def edge_lift(self, left_vertex, right_vertex):
106
r"""
107
Returns the edge lift of an edge in the base. This is by definition a
108
bipartite graph, so the order the vertices are input determines which
109
partition is on the 'left.'
110
111
EXAMPLE:
112
113
sage: P = graphs.PetersenGraph()
114
sage: partition = [range(5), range(5,10)]
115
sage: B = GraphBundle(P, partition)
116
sage: B.edge_lift(0,1)
117
Bipartite petersen graph: graph on 10 vertices
118
119
"""
120
from bipartite_graph import BipartiteGraph
121
return BipartiteGraph(self, [self.fiber[left_vertex], self.fiber[right_vertex]], check=False)
122
123
def _repr_(self):
124
r"""
125
Returns a short string representation of self.
126
127
EXAMPLE:
128
sage: P = graphs.PetersenGraph()
129
sage: partition = [range(5), range(5,10)]
130
sage: B = GraphBundle(P, partition)
131
sage: B
132
Graph bundle:
133
Total space: petersen graph: graph on 10 vertices
134
Base space: graph on 2 vertices
135
136
"""
137
s_total = Graph._repr_(self).lower()
138
s_base = Graph._repr_(self.base).lower()
139
s = "Graph bundle:\n"
140
s += " Total space: " + s_total + "\n"
141
s += " Base space: " + s_base
142
return s
143
144
def fibers(self):
145
r"""
146
Returns a list of the fibers of the bundle, as a partition of the total
147
space.
148
149
EXAMPLE:
150
sage: P = graphs.PetersenGraph()
151
sage: partition = [range(5), range(5,10)]
152
sage: B = GraphBundle(P, partition)
153
sage: B.fibers()
154
[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]
155
156
"""
157
return self.fiber.values()
158
159
def plot(self, *args, **kwds):
160
r"""
161
Overrides Graph's plot function, to illustrate the bundle nature.
162
163
EXAMPLE::
164
165
sage: P = graphs.PetersenGraph()
166
sage: partition = [range(5), range(5,10)]
167
sage: B = GraphBundle(P, partition)
168
sage: #B.plot()
169
170
Test disabled due to bug in GraphBundle.__init__(). See trac #8329.
171
172
"""
173
if 'pos' not in kwds.keys():
174
kwds['pos'] = None
175
if kwds['pos'] is None:
176
import sage.graphs.generic_graph_pyx as generic_graph_pyx
177
if 'iterations' not in kwds.keys():
178
kwds['iterations'] = 50
179
iters = kwds['iterations']
180
total_pos = generic_graph_pyx.spring_layout_fast(self, iterations=iters)
181
base_pos = generic_graph_pyx.spring_layout_fast(self.base, iterations=iters)
182
for v in base_pos.iterkeys():
183
for v_tilde in self.fiber[v]:
184
total_pos[v_tilde][0] = base_pos[v][0]
185
tot_x = [p[0] for p in total_pos.values()]
186
tot_y = [p[1] for p in total_pos.values()]
187
bas_x = [p[0] for p in base_pos.values()]
188
bas_y = [p[1] for p in base_pos.values()]
189
tot_x_min = min(tot_x)
190
tot_x_max = max(tot_x)
191
tot_y_min = min(tot_y)
192
tot_y_max = max(tot_y)
193
bas_x_min = min(bas_x)
194
bas_x_max = max(bas_x)
195
bas_y_min = min(bas_y)
196
bas_y_max = max(bas_y)
197
if tot_x_max == tot_x_min and tot_y_max == tot_y_min:
198
tot_y_max += 1
199
tot_y_min -= 1
200
elif tot_y_max == tot_y_min:
201
delta = (tot_x_max - tot_x_min)/2.0
202
tot_y_max += delta
203
tot_y_min -= delta
204
if bas_x_max == bas_x_min and bas_y_max == bas_y_min:
205
bas_y_max += 1
206
bas_y_min -= 1
207
elif bas_y_max == bas_y_min:
208
delta = (bas_x_max - bas_x_min)/2.0
209
bas_y_max += delta
210
bas_y_min -= delta
211
y_diff = (bas_y_max - tot_y_min) + 2*(bas_y_max - bas_y_min)
212
pos = {}
213
for v in self:
214
pos[('t',v)] = [total_pos[v][0], total_pos[v][1] + y_diff]
215
for v in self.base:
216
pos[('b',v)] = base_pos[v]
217
from copy import copy
218
G = copy(self)
219
rd = {}
220
for v in G:
221
rd[v] = ('t',v)
222
G.relabel(rd)
223
B = copy(self.base)
224
rd = {}
225
for v in B:
226
rd[v] = ('b',v)
227
B.relabel(rd)
228
E = G.disjoint_union(B)
229
kwds['pos'] = pos
230
from sage.plot.all import arrow
231
G = Graph.plot(E, *args, **kwds)
232
G += arrow( ((tot_x_max + tot_x_min)/2.0, tot_y_min + y_diff),
233
((tot_x_max + tot_x_min)/2.0, bas_y_max), axes=False )
234
G.axes(False)
235
return G
236
else:
237
return G.plot(self, *args, **kwds)
238
239
240