Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/graph_path.py
4036 views
1
r"""
2
Paths in Directed Acyclic Graphs
3
"""
4
#*****************************************************************************
5
# Copyright (C) 2007 Mike Hansen <[email protected]>,
6
#
7
# Distributed under the terms of the GNU General Public License (GPL)
8
#
9
# This code is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
# General Public License for more details.
13
#
14
# The full text of the GPL is available at:
15
#
16
# http://www.gnu.org/licenses/
17
#*****************************************************************************
18
from combinat import CombinatorialClass
19
import sage.graphs.digraph as digraph
20
21
22
def GraphPaths(g, source=None, target=None):
23
"""
24
Returns the combinatorial class of paths in the directed acyclic
25
graph g.
26
27
EXAMPLES::
28
29
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
30
31
If source and target are not given, then the returned class
32
contains all paths (including trivial paths containing only one
33
vertex).
34
35
::
36
37
sage: p = GraphPaths(G); p
38
Paths in Multi-digraph on 5 vertices
39
sage: p.cardinality()
40
37
41
sage: p.random_element()
42
[1, 2, 3, 4, 5]
43
44
If the source is specified, then the returned class contains all of
45
the paths starting at the vertex source (including the trivial
46
path).
47
48
::
49
50
sage: p = GraphPaths(G, source=3); p
51
Paths in Multi-digraph on 5 vertices starting at 3
52
sage: p.list()
53
[[3], [3, 4], [3, 4, 5], [3, 4, 5]]
54
55
If the target is specified, then the returned class contains all of
56
the paths ending at the vertex target (including the trivial
57
path).
58
59
::
60
61
sage: p = GraphPaths(G, target=3); p
62
Paths in Multi-digraph on 5 vertices ending at 3
63
sage: p.cardinality()
64
5
65
sage: p.list()
66
[[3], [1, 3], [2, 3], [1, 2, 3], [1, 2, 3]]
67
68
If both the target and source are specified, then the returned
69
class contains all of the paths from source to target.
70
71
::
72
73
sage: p = GraphPaths(G, source=1, target=3); p
74
Paths in Multi-digraph on 5 vertices starting at 1 and ending at 3
75
sage: p.cardinality()
76
3
77
sage: p.list()
78
[[1, 2, 3], [1, 2, 3], [1, 3]]
79
80
Note that G must be a directed acyclic graph.
81
82
::
83
84
sage: G = DiGraph({1:[2,2,3,5], 2:[3,4], 3:[4], 4:[2,5,7], 5:[6]}, multiedges=True)
85
sage: GraphPaths(G)
86
Traceback (most recent call last):
87
...
88
TypeError: g must be a directed acyclic graph
89
"""
90
if not isinstance(g, digraph.DiGraph):
91
raise TypeError, "g must be a DiGraph"
92
elif not g.is_directed_acyclic():
93
raise TypeError, "g must be a directed acyclic graph"
94
95
if source is None and target is None:
96
return GraphPaths_all(g)
97
elif source is not None and target is None:
98
if source not in g:
99
raise ValueError, "source must be in g"
100
return GraphPaths_s(g, source)
101
elif source is None and target is not None:
102
if target not in g:
103
raise ValueError, "target must be in g"
104
return GraphPaths_t(g, target)
105
else:
106
if source not in g:
107
raise ValueError, "source must be in g"
108
if target not in g:
109
raise ValueError, "target must be in g"
110
return GraphPaths_st(g, source, target)
111
112
class GraphPaths_common:
113
def outgoing_edges(self, v):
114
"""
115
Returns a list of v's outgoing edges.
116
117
EXAMPLES::
118
119
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
120
sage: p = GraphPaths(G)
121
sage: p.outgoing_edges(2)
122
[(2, 3, None), (2, 4, None)]
123
"""
124
return [i for i in self.graph.outgoing_edge_iterator(v)]
125
126
def incoming_edges(self, v):
127
"""
128
Returns a list of v's incoming edges.
129
130
EXAMPLES::
131
132
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
133
sage: p = GraphPaths(G)
134
sage: p.incoming_edges(2)
135
[(1, 2, None), (1, 2, None)]
136
"""
137
return [i for i in self.graph.incoming_edge_iterator(v)]
138
139
def outgoing_paths(self, v):
140
"""
141
Returns a list of the paths that start at v.
142
143
EXAMPLES::
144
145
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
146
sage: gp = GraphPaths(G)
147
sage: gp.outgoing_paths(3)
148
[[3], [3, 4], [3, 4, 5], [3, 4, 5]]
149
sage: gp.outgoing_paths(2)
150
[[2],
151
[2, 3],
152
[2, 3, 4],
153
[2, 3, 4, 5],
154
[2, 3, 4, 5],
155
[2, 4],
156
[2, 4, 5],
157
[2, 4, 5]]
158
"""
159
source_paths = [ [v] ]
160
for e in self.outgoing_edges(v):
161
target = e[1]
162
target_paths = self.outgoing_paths(target)
163
target_paths = [ [v]+path for path in target_paths]
164
165
source_paths += target_paths
166
167
return source_paths
168
169
170
def incoming_paths(self, v):
171
"""
172
Returns a list of paths that end at v.
173
174
EXAMPLES::
175
176
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
177
sage: gp = GraphPaths(G)
178
sage: gp.incoming_paths(2)
179
[[2], [1, 2], [1, 2]]
180
"""
181
target_paths = [ [v] ]
182
for e in self.incoming_edges(v):
183
source = e[0]
184
source_paths = self.incoming_paths(source)
185
source_paths = [ path + [v] for path in source_paths ]
186
target_paths += source_paths
187
return target_paths
188
189
def paths_from_source_to_target(self, source, target):
190
"""
191
Returns a list of paths from source to target.
192
193
EXAMPLES::
194
195
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
196
sage: gp = GraphPaths(G)
197
sage: gp.paths_from_source_to_target(2,4)
198
[[2, 3, 4], [2, 4]]
199
"""
200
source_paths = self.outgoing_paths(source)
201
paths = []
202
for path in source_paths:
203
if path[-1] == target:
204
paths.append(path)
205
return paths
206
207
def paths(self):
208
"""
209
Returns a list of all the paths of self.
210
211
EXAMPLES::
212
213
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
214
sage: gp = GraphPaths(G)
215
sage: len(gp.paths())
216
37
217
"""
218
paths = []
219
for source in self.graph.vertices():
220
paths += self.outgoing_paths(source)
221
return paths
222
223
class GraphPaths_all(CombinatorialClass, GraphPaths_common):
224
"""
225
EXAMPLES::
226
227
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
228
sage: p = GraphPaths(G)
229
sage: p.cardinality()
230
37
231
"""
232
def __init__(self, g):
233
"""
234
TESTS::
235
236
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
237
sage: p = GraphPaths(G)
238
sage: p == loads(dumps(p))
239
True
240
"""
241
self.graph = g
242
243
def __repr__(self):
244
"""
245
TESTS::
246
247
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
248
sage: p = GraphPaths(G)
249
sage: repr(p)
250
'Paths in Multi-digraph on 5 vertices'
251
"""
252
return "Paths in %s"%repr(self.graph)
253
254
def list(self):
255
"""
256
Returns a list of the paths of self.
257
258
EXAMPLES::
259
260
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
261
sage: len(GraphPaths(G).list())
262
37
263
"""
264
return self.paths()
265
266
class GraphPaths_t(CombinatorialClass, GraphPaths_common):
267
def __init__(self, g, target):
268
"""
269
TESTS::
270
271
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
272
sage: p = GraphPaths(G, target=4)
273
sage: p == loads(dumps(p))
274
True
275
"""
276
self.graph = g
277
self.target = target
278
279
def __repr__(self):
280
"""
281
TESTS::
282
283
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
284
sage: p = GraphPaths(G, target=4)
285
sage: repr(p)
286
'Paths in Multi-digraph on 5 vertices ending at 4'
287
"""
288
return "Paths in %s ending at %s"%(repr(self.graph), self.target)
289
290
def list(self):
291
"""
292
EXAMPLES::
293
294
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
295
sage: p = GraphPaths(G, target=4)
296
sage: p.list()
297
[[4],
298
[2, 4],
299
[1, 2, 4],
300
[1, 2, 4],
301
[3, 4],
302
[1, 3, 4],
303
[2, 3, 4],
304
[1, 2, 3, 4],
305
[1, 2, 3, 4]]
306
"""
307
return self.incoming_paths(self.target)
308
309
class GraphPaths_s(CombinatorialClass, GraphPaths_common):
310
def __init__(self, g, source):
311
"""
312
TESTS::
313
314
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
315
sage: p = GraphPaths(G, 4)
316
sage: p == loads(dumps(p))
317
True
318
"""
319
self.graph = g
320
self.source = source
321
322
def __repr__(self):
323
"""
324
TESTS::
325
326
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
327
sage: p = GraphPaths(G, 4)
328
sage: repr(p)
329
'Paths in Multi-digraph on 5 vertices starting at 4'
330
"""
331
return "Paths in %s starting at %s"%(repr(self.graph), self.source)
332
333
def list(self):
334
"""
335
EXAMPLES::
336
337
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
338
sage: p = GraphPaths(G, 4)
339
sage: p.list()
340
[[4], [4, 5], [4, 5]]
341
"""
342
return self.outgoing_paths(self.source)
343
344
class GraphPaths_st(CombinatorialClass, GraphPaths_common):
345
"""
346
EXAMPLES::
347
348
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
349
sage: GraphPaths(G,1,2).cardinality()
350
2
351
sage: GraphPaths(G,1,3).cardinality()
352
3
353
sage: GraphPaths(G,1,4).cardinality()
354
5
355
sage: GraphPaths(G,1,5).cardinality()
356
10
357
sage: GraphPaths(G,2,3).cardinality()
358
1
359
sage: GraphPaths(G,2,4).cardinality()
360
2
361
sage: GraphPaths(G,2,5).cardinality()
362
4
363
sage: GraphPaths(G,3,4).cardinality()
364
1
365
sage: GraphPaths(G,3,5).cardinality()
366
2
367
sage: GraphPaths(G,4,5).cardinality()
368
2
369
"""
370
def __init__(self, g, source, target):
371
"""
372
TESTS::
373
374
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
375
sage: p = GraphPaths(G,1,2)
376
sage: p == loads(dumps(p))
377
True
378
"""
379
self.graph = g
380
self.source = source
381
self.target = target
382
383
def __repr__(self):
384
"""
385
TESTS::
386
387
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
388
sage: p = GraphPaths(G,1,2)
389
sage: repr(p)
390
'Paths in Multi-digraph on 5 vertices starting at 1 and ending at 2'
391
"""
392
return "Paths in %s starting at %s and ending at %s"%(repr(self.graph), self.source, self.target)
393
394
def list(self):
395
"""
396
EXAMPLES::
397
398
sage: G = DiGraph({1:[2,2,3], 2:[3,4], 3:[4], 4:[5,5]}, multiedges=True)
399
sage: p = GraphPaths(G,1,2)
400
sage: p.list()
401
[[1, 2], [1, 2]]
402
"""
403
return self.paths_from_source_to_target(self.source, self.target)
404
405