Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/graphs/digraph.py
4036 views
1
r"""
2
Directed graphs
3
4
This module implements functions and operations involving directed
5
graphs. Here is what they can do
6
7
**Graph basic operations:**
8
9
.. csv-table::
10
:class: contentstable
11
:widths: 30, 70
12
:delim: |
13
14
:meth:`~DiGraph.layout_acyclic_dummy` | Computes a (dummy) ranked layout so that all edges point upward.
15
:meth:`~DiGraph.layout_acyclic` | Computes a ranked layout so that all edges point upward.
16
:meth:`~DiGraph.reverse` | Returns a copy of digraph with edges reversed in direction.
17
:meth:`~DiGraph.out_degree_sequence` | Return the outdegree sequence
18
:meth:`~DiGraph.out_degree_iterator` | Same as degree_iterator, but for out degree.
19
:meth:`~DiGraph.out_degree` | Same as degree, but for out degree.
20
:meth:`~DiGraph.in_degree_sequence` | Return the indegree sequence of this digraph.
21
:meth:`~DiGraph.in_degree_iterator` | Same as degree_iterator, but for in degree.
22
:meth:`~DiGraph.in_degree` | Same as degree, but for in-degree.
23
:meth:`~DiGraph.neighbors_out` | Returns the list of the out-neighbors of a given vertex.
24
:meth:`~DiGraph.neighbor_out_iterator` | Returns an iterator over the out-neighbors of a given vertex.
25
:meth:`~DiGraph.neighbors_in` | Returns the list of the in-neighbors of a given vertex.
26
:meth:`~DiGraph.neighbor_in_iterator` | Returns an iterator over the in-neighbors of vertex.
27
:meth:`~DiGraph.outgoing_edges` | Returns a list of edges departing from vertices.
28
:meth:`~DiGraph.outgoing_edge_iterator` | Return an iterator over all departing edges from vertices
29
:meth:`~DiGraph.incoming_edges` | Returns a list of edges arriving at vertices.
30
:meth:`~DiGraph.incoming_edge_iterator` | Return an iterator over all arriving edges from vertices
31
:meth:`~DiGraph.to_undirected` | Returns an undirected version of the graph.
32
:meth:`~DiGraph.to_directed` | Since the graph is already directed, simply returns a copy of itself.
33
:meth:`~DiGraph.is_directed` | Since digraph is directed, returns True.
34
:meth:`~DiGraph.dig6_string` | Returns the dig6 representation of the digraph as an ASCII string.
35
36
37
**Paths and cycles:**
38
39
.. csv-table::
40
:class: contentstable
41
:widths: 30, 70
42
:delim: |
43
44
:meth:`~DiGraph.all_paths_iterator` | Returns an iterator over the paths of self. The paths are
45
:meth:`~DiGraph.all_simple_paths` | Returns a list of all the simple paths of self starting
46
:meth:`~DiGraph.all_cycles_iterator` | Returns an iterator over all the cycles of self starting
47
:meth:`~DiGraph.all_simple_cycles` | Returns a list of all simple cycles of self.
48
49
50
**Connectivity:**
51
52
.. csv-table::
53
:class: contentstable
54
:widths: 30, 70
55
:delim: |
56
57
:meth:`~DiGraph.is_strongly_connected` | Returns whether the current ``DiGraph`` is strongly connected.
58
:meth:`~DiGraph.strongly_connected_components_digraph` | Returns the digraph of the strongly connected components
59
:meth:`~DiGraph.strongly_connected_components_subgraphs` | Returns the strongly connected components as a list of subgraphs.
60
:meth:`~DiGraph.strongly_connected_component_containing_vertex` | Returns the strongly connected component containing a given vertex
61
:meth:`~DiGraph.strongly_connected_components` | Returns the list of strongly connected components.
62
63
64
**Acyclicity:**
65
66
.. csv-table::
67
:class: contentstable
68
:widths: 30, 70
69
:delim: |
70
71
:meth:`~DiGraph.is_directed_acyclic` | Returns whether the digraph is acyclic or not.
72
:meth:`~DiGraph.is_transitive` | Returns whether the digraph is transitive or not.
73
:meth:`~DiGraph.level_sets` | Returns the level set decomposition of the digraph.
74
:meth:`~DiGraph.topological_sort_generator` | Returns a list of all topological sorts of the digraph if it is acyclic
75
:meth:`~DiGraph.topological_sort` | Returns a topological sort of the digraph if it is acyclic
76
77
78
**Hard stuff:**
79
80
.. csv-table::
81
:class: contentstable
82
:widths: 30, 70
83
:delim: |
84
85
:meth:`~DiGraph.feedback_edge_set` | Computes the minimum feedback edge (arc) set of a digraph
86
:meth:`~DiGraph.feedback_vertex_set` | Computes the minimum feedback vertex set of a digraph.
87
88
89
Methods
90
-------
91
"""
92
93
from sage.rings.integer import Integer
94
from sage.misc.misc import deprecated_function_alias
95
import sage.graphs.generic_graph_pyx as generic_graph_pyx
96
from sage.graphs.generic_graph import GenericGraph
97
from sage.graphs.dot2tex_utils import have_dot2tex
98
99
class DiGraph(GenericGraph):
100
"""
101
Directed graph.
102
103
A digraph or directed graph is a set of vertices connected by oriented
104
edges. For more information, see the
105
`Wikipedia article on digraphs
106
<http://en.wikipedia.org/wiki/Digraph_%28mathematics%29>`_.
107
108
One can very easily create a directed graph in Sage by typing::
109
110
sage: g = DiGraph()
111
112
By typing the name of the digraph, one can get some basic information
113
about it::
114
115
sage: g
116
Digraph on 0 vertices
117
118
This digraph is not very interesting as it is by default the empty
119
graph. But Sage contains several pre-defined digraph classes that can
120
be listed this way:
121
122
* Within a Sage sessions, type ``digraphs.``
123
(Do not press "Enter", and do not forget the final period "." )
124
* Hit "tab".
125
126
You will see a list of methods which will construct named digraphs. For
127
example::
128
129
sage: g = digraphs.ButterflyGraph(3)
130
sage: g.plot()
131
132
You can also use the collection of pre-defined graphs, then create a
133
digraph from them. ::
134
135
sage: g = DiGraph(graphs.PetersenGraph())
136
sage: g.plot()
137
138
Calling ``Digraph`` on a graph returns the original graph in which every
139
edge is replaced by two different edges going toward opposite directions.
140
141
In order to obtain more information about these digraph constructors,
142
access the documentation by typing ``digraphs.RandomDirectedGNP?``.
143
144
Once you have defined the digraph you want, you can begin to work on it
145
by using the almost 200 functions on graphs and digraphs in the Sage
146
library! If your digraph is named ``g``, you can list these functions as
147
previously this way
148
149
* Within a Sage session, type ``g.``
150
(Do not press "Enter", and do not forget the final period "." )
151
* Hit "tab".
152
153
As usual, you can get some information about what these functions do by
154
typing (e.g. if you want to know about the ``diameter()`` method)
155
``g.diameter?``.
156
157
If you have defined a digraph ``g`` having several connected components
158
( i.e. ``g.is_connected()`` returns False ), you can print each one of its
159
connected components with only two lines::
160
161
sage: for component in g.connected_components():
162
... g.subgraph(component).plot()
163
164
The same methods works for strongly connected components ::
165
sage: for component in g.strongly_connected_components():
166
... g.subgraph(component).plot()
167
168
169
INPUT:
170
171
- ``data`` - can be any of the following (see the ``format`` keyword):
172
173
#. A dictionary of dictionaries
174
175
#. A dictionary of lists
176
177
#. A numpy matrix or ndarray
178
179
#. A Sage adjacency matrix or incidence matrix
180
181
#. A pygraphviz graph
182
183
#. A SciPy sparse matrix
184
185
#. A NetworkX digraph
186
187
- ``pos`` - a positioning dictionary: for example, the
188
spring layout from NetworkX for the 5-cycle is::
189
190
{0: [-0.91679746, 0.88169588],
191
1: [ 0.47294849, 1.125 ],
192
2: [ 1.125 ,-0.12867615],
193
3: [ 0.12743933,-1.125 ],
194
4: [-1.125 ,-0.50118505]}
195
196
- ``name`` - (must be an explicitly named parameter,
197
i.e., name="complete") gives the graph a name
198
199
- ``loops`` - boolean, whether to allow loops (ignored
200
if data is an instance of the DiGraph class)
201
202
- ``multiedges`` - boolean, whether to allow multiple
203
edges (ignored if data is an instance of the DiGraph class)
204
205
- ``weighted`` - whether digraph thinks of itself as
206
weighted or not. See self.weighted()
207
208
- ``format`` - if None, DiGraph tries to guess- can be
209
several values, including:
210
211
- ``'adjacency_matrix'`` - a square Sage matrix M,
212
with M[i,j] equal to the number of edges {i,j}
213
214
- ``'incidence_matrix'`` - a Sage matrix, with one
215
column C for each edge, where if C represents {i, j}, C[i] is -1
216
and C[j] is 1
217
218
- ``'weighted_adjacency_matrix'`` - a square Sage
219
matrix M, with M[i,j] equal to the weight of the single edge {i,j}.
220
Given this format, weighted is ignored (assumed True).
221
222
- ``NX`` - data must be a NetworkX DiGraph.
223
224
.. NOTE::
225
226
As Sage's default edge labels is ``None`` while NetworkX uses
227
``{}``, the ``{}`` labels of a NetworkX digraph are automatically
228
set to ``None`` when it is converted to a Sage graph. This
229
behaviour can be overruled by setting the keyword
230
``convert_empty_dict_labels_to_None`` to ``False`` (it is
231
``True`` by default).
232
233
- ``boundary`` - a list of boundary vertices, if none,
234
digraph is considered as a 'digraph without boundary'
235
236
- ``implementation`` - what to use as a backend for
237
the graph. Currently, the options are either 'networkx' or
238
'c_graph'
239
240
- ``sparse`` - only for implementation == 'c_graph'.
241
Whether to use sparse or dense graphs as backend. Note that
242
currently dense graphs do not have edge labels, nor can they be
243
multigraphs
244
245
- ``vertex_labels`` - only for implementation == 'c_graph'.
246
Whether to allow any object as a vertex (slower), or
247
only the integers 0, ..., n-1, where n is the number of vertices.
248
249
- ``convert_empty_dict_labels_to_None`` - this arguments sets
250
the default edge labels used by NetworkX (empty dictionaries)
251
to be replaced by None, the default Sage edge label. It is
252
set to ``True`` iff a NetworkX graph is on the input.
253
254
EXAMPLES:
255
256
#. A dictionary of dictionaries::
257
258
sage: g = DiGraph({0:{1:'x',2:'z',3:'a'}, 2:{5:'out'}}); g
259
Digraph on 5 vertices
260
261
The labels ('x', 'z', 'a', 'out') are labels for edges. For
262
example, 'out' is the label for the edge from 2 to 5. Labels can be
263
used as weights, if all the labels share some common parent.
264
265
#. A dictionary of lists (or iterables)::
266
267
sage: g = DiGraph({0:[1,2,3], 2:[4]}); g
268
Digraph on 5 vertices
269
sage: g = DiGraph({0:(1,2,3), 2:(4,)}); g
270
Digraph on 5 vertices
271
272
#. A list of vertices and a function describing adjacencies. Note
273
that the list of vertices and the function must be enclosed in a
274
list (i.e., [list of vertices, function]).
275
276
We construct a graph on the integers 1 through 12 such that there
277
is a directed edge from i to j if and only if i divides j.
278
279
::
280
281
sage: g=DiGraph([[1..12],lambda i,j: i!=j and i.divides(j)])
282
sage: g.vertices()
283
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
284
sage: g.adjacency_matrix()
285
[0 1 1 1 1 1 1 1 1 1 1 1]
286
[0 0 0 1 0 1 0 1 0 1 0 1]
287
[0 0 0 0 0 1 0 0 1 0 0 1]
288
[0 0 0 0 0 0 0 1 0 0 0 1]
289
[0 0 0 0 0 0 0 0 0 1 0 0]
290
[0 0 0 0 0 0 0 0 0 0 0 1]
291
[0 0 0 0 0 0 0 0 0 0 0 0]
292
[0 0 0 0 0 0 0 0 0 0 0 0]
293
[0 0 0 0 0 0 0 0 0 0 0 0]
294
[0 0 0 0 0 0 0 0 0 0 0 0]
295
[0 0 0 0 0 0 0 0 0 0 0 0]
296
[0 0 0 0 0 0 0 0 0 0 0 0]
297
298
#. A numpy matrix or ndarray::
299
300
sage: import numpy
301
sage: A = numpy.array([[0,1,0],[1,0,0],[1,1,0]])
302
sage: DiGraph(A)
303
Digraph on 3 vertices
304
305
#. A Sage matrix: Note: If format is not specified, then Sage
306
assumes a square matrix is an adjacency matrix, and a nonsquare
307
matrix is an incidence matrix.
308
309
- an adjacency matrix::
310
311
sage: M = Matrix([[0, 1, 1, 1, 0],[0, 0, 0, 0, 0],[0, 0, 0, 0, 1],[0, 0, 0, 0, 0],[0, 0, 0, 0, 0]]); M
312
[0 1 1 1 0]
313
[0 0 0 0 0]
314
[0 0 0 0 1]
315
[0 0 0 0 0]
316
[0 0 0 0 0]
317
sage: DiGraph(M)
318
Digraph on 5 vertices
319
320
- an incidence matrix::
321
322
sage: M = Matrix(6, [-1,0,0,0,1, 1,-1,0,0,0, 0,1,-1,0,0, 0,0,1,-1,0, 0,0,0,1,-1, 0,0,0,0,0]); M
323
[-1 0 0 0 1]
324
[ 1 -1 0 0 0]
325
[ 0 1 -1 0 0]
326
[ 0 0 1 -1 0]
327
[ 0 0 0 1 -1]
328
[ 0 0 0 0 0]
329
sage: DiGraph(M)
330
Digraph on 6 vertices
331
332
#. A c_graph implemented DiGraph can be constructed from a
333
networkx implemented DiGraph::
334
335
sage: D = DiGraph({0:[1],1:[2],2:[0]}, implementation="networkx")
336
sage: E = DiGraph(D,implementation="c_graph")
337
sage: D == E
338
True
339
340
#. A dig6 string: Sage automatically recognizes whether a string is
341
in dig6 format, which is a directed version of graph6::
342
343
sage: D = DiGraph('IRAaDCIIOWEOKcPWAo')
344
sage: D
345
Digraph on 10 vertices
346
347
sage: D = DiGraph('IRAaDCIIOEOKcPWAo')
348
Traceback (most recent call last):
349
...
350
RuntimeError: The string (IRAaDCIIOEOKcPWAo) seems corrupt: for n = 10, the string is too short.
351
352
sage: D = DiGraph("IRAaDCI'OWEOKcPWAo")
353
Traceback (most recent call last):
354
...
355
RuntimeError: The string seems corrupt: valid characters are
356
?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
357
358
#. A NetworkX XDiGraph::
359
360
sage: import networkx
361
sage: g = networkx.MultiDiGraph({0:[1,2,3], 2:[4]})
362
sage: DiGraph(g)
363
Digraph on 5 vertices
364
365
366
#. A NetworkX digraph::
367
368
sage: import networkx
369
sage: g = networkx.DiGraph({0:[1,2,3], 2:[4]})
370
sage: DiGraph(g)
371
Digraph on 5 vertices
372
373
Note that in all cases, we copy the NetworkX structure.
374
375
::
376
377
sage: import networkx
378
sage: g = networkx.DiGraph({0:[1,2,3], 2:[4]})
379
sage: G = DiGraph(g, implementation='networkx')
380
sage: H = DiGraph(g, implementation='networkx')
381
sage: G._backend._nxg is H._backend._nxg
382
False
383
384
TESTS::
385
386
sage: DiGraph({0:[1,2,3], 2:[4]}).edges()
387
[(0, 1, None), (0, 2, None), (0, 3, None), (2, 4, None)]
388
sage: DiGraph({0:(1,2,3), 2:(4,)}).edges()
389
[(0, 1, None), (0, 2, None), (0, 3, None), (2, 4, None)]
390
sage: DiGraph({0:Set([1,2,3]), 2:Set([4])}).edges()
391
[(0, 1, None), (0, 2, None), (0, 3, None), (2, 4, None)]
392
393
"""
394
_directed = True
395
396
def __init__(self, data=None, pos=None, loops=None, format=None,
397
boundary=[], weighted=None, implementation='c_graph',
398
sparse=True, vertex_labels=True, name=None,
399
multiedges=None, convert_empty_dict_labels_to_None=None):
400
"""
401
TESTS::
402
403
sage: D = DiGraph()
404
sage: loads(dumps(D)) == D
405
True
406
407
sage: a = matrix(2,2,[1,2,0,1])
408
sage: DiGraph(a,sparse=True).adjacency_matrix() == a
409
True
410
411
sage: a = matrix(2,2,[3,2,0,1])
412
sage: DiGraph(a,sparse=True).adjacency_matrix() == a
413
True
414
415
The positions are copied when the DiGraph is built from
416
another DiGraph or from a Graph ::
417
418
sage: g = DiGraph(graphs.PetersenGraph())
419
sage: h = DiGraph(g)
420
sage: g.get_pos() == h.get_pos()
421
True
422
sage: g.get_pos() == graphs.PetersenGraph().get_pos()
423
True
424
425
Invalid sequence of edges given as an input (they do not all
426
have the same length)::
427
428
sage: g = DiGraph([(1,2),(2,3),(2,3,4)])
429
Traceback (most recent call last):
430
...
431
ValueError: Edges input must all follow the same format.
432
433
434
Two different labels given for the same edge in a graph
435
without multiple edges::
436
437
sage: g = Graph([(1,2,3),(1,2,4)], multiedges = False)
438
Traceback (most recent call last):
439
...
440
ValueError: Two different labels given for the same edge in a graph without multiple edges.
441
442
Detection of multiple edges::
443
444
sage: DiGraph([(1, 2, 0), (1,2,1)])
445
Multi-digraph on 2 vertices
446
sage: DiGraph([(1, 2, 0)])
447
Digraph on 2 vertices
448
449
An empty list or dictionary defines a simple graph (trac #10441 and #12910)::
450
451
sage: DiGraph([])
452
Digraph on 0 vertices
453
sage: DiGraph({})
454
Digraph on 0 vertices
455
sage: # not "Multi-digraph on 0 vertices"
456
"""
457
msg = ''
458
GenericGraph.__init__(self)
459
from sage.structure.element import is_Matrix
460
from sage.misc.misc import uniq
461
if format is None and isinstance(data, str):
462
format = 'dig6'
463
if data[:8] == ">>dig6<<":
464
data = data[8:]
465
if format is None and is_Matrix(data):
466
if data.is_square():
467
format = 'adjacency_matrix'
468
else:
469
format = 'incidence_matrix'
470
msg += "Non-symmetric or non-square matrix assumed to be an incidence matrix: "
471
if format is None and isinstance(data, DiGraph):
472
format = 'DiGraph'
473
from sage.graphs.all import Graph
474
if format is None and isinstance(data, Graph):
475
data = data.to_directed()
476
format = 'DiGraph'
477
if format is None and isinstance(data,list) and \
478
len(data)>=2 and callable(data[1]):
479
format = 'rule'
480
if format is None and isinstance(data,dict):
481
keys = data.keys()
482
if len(keys) == 0: format = 'dict_of_dicts'
483
else:
484
if isinstance(data[keys[0]], dict):
485
format = 'dict_of_dicts'
486
else:
487
format = 'dict_of_lists'
488
if format is None and hasattr(data, 'adj'):
489
import networkx
490
if isinstance(data, (networkx.Graph, networkx.MultiGraph)):
491
data = data.to_directed()
492
format = 'NX'
493
elif isinstance(data, (networkx.DiGraph, networkx.MultiDiGraph)):
494
format = 'NX'
495
if format is None and isinstance(data, (int, Integer)):
496
format = 'int'
497
if format is None and data is None:
498
format = 'int'
499
data = 0
500
501
# Input is a list of edges
502
if format is None and isinstance(data, list):
503
504
# If we are given a list (we assume it is a list of
505
# edges), we convert the problem to a dict_of_dicts or a
506
# dict_of_lists
507
508
edges = data
509
510
# Along the process, we are assuming all edges have the
511
# same length. If it is not true, a ValueError will occur
512
try:
513
514
# The list is empty
515
if not data:
516
data = {}
517
format = 'dict_of_dicts'
518
519
# The edges are not labelled
520
elif len(data[0]) == 2:
521
data = {}
522
for u,v in edges:
523
if not u in data:
524
data[u] = []
525
526
data[u].append(v)
527
528
format = 'dict_of_lists'
529
530
# The edges are labelled
531
elif len(data[0]) == 3:
532
data = {}
533
for u,v,l in edges:
534
535
if not u in data:
536
data[u] = {}
537
538
# Now the key exists, and is a dictionary ...
539
540
# If we notice for the first time that there
541
# are multiple edges, we update the whole
542
# dictionary so that data[u][v] is a list
543
544
if (multiedges is None and
545
(v in data[u])):
546
multiedges = True
547
for uu, dd in data.iteritems():
548
for vv, ddd in dd.iteritems():
549
dd[vv] = [ddd]
550
551
# If multiedges is set to False while the same
552
# edge is present in the list with different
553
# values of its label
554
elif (multiedges is False and
555
(v in data[u] and data[u][v] != l)):
556
raise ValueError("MULTIEDGE")
557
558
# Now we are behaving as if multiedges == None
559
# means multiedges = False. If something bad
560
# happens later, the whole dictionary will be
561
# updated anyway
562
563
if multiedges is True:
564
if v not in data[u]:
565
data[u][v] = []
566
567
data[u][v].append(l)
568
569
else:
570
data[u][v] = l
571
572
format = 'dict_of_dicts'
573
574
except ValueError as ve:
575
if str(ve) == "MULTIEDGE":
576
raise ValueError("Two different labels given for the same edge in a graph without multiple edges.")
577
else:
578
raise ValueError("Edges input must all follow the same format.")
579
580
if format is None:
581
import networkx
582
data = networkx.MultiDiGraph(data)
583
format = 'NX'
584
585
# At this point, format has been set.
586
587
# adjust for empty dicts instead of None in NetworkX default edge labels
588
if convert_empty_dict_labels_to_None is None:
589
convert_empty_dict_labels_to_None = (format == 'NX')
590
591
verts = None
592
593
if format == 'dig6':
594
if loops is None: loops = False
595
if weighted is None: weighted = False
596
if multiedges is None: multiedges = False
597
if not isinstance(data, str):
598
raise ValueError('If input format is dig6, then data must be a string.')
599
n = data.find('\n')
600
if n == -1:
601
n = len(data)
602
ss = data[:n]
603
n, s = generic_graph_pyx.N_inverse(ss)
604
m = generic_graph_pyx.D_inverse(s, n)
605
expected = n**2
606
if len(m) > expected:
607
raise RuntimeError("The string (%s) seems corrupt: for n = %d, the string is too long."%(ss,n))
608
elif len(m) < expected:
609
raise RuntimeError("The string (%s) seems corrupt: for n = %d, the string is too short."%(ss,n))
610
num_verts = n
611
elif format in ['adjacency_matrix', 'incidence_matrix']:
612
assert is_Matrix(data)
613
if format == 'weighted_adjacency_matrix':
614
if weighted is False:
615
raise ValueError("Format was weighted_adjacency_matrix but weighted was False.")
616
if weighted is None: weighted = True
617
if multiedges is None: multiedges = False
618
format = 'adjacency_matrix'
619
if format == 'adjacency_matrix':
620
entries = uniq(data.list())
621
for e in entries:
622
try:
623
e = int(e)
624
assert e >= 0
625
except:
626
if weighted is False:
627
raise ValueError("Non-weighted digraph's"+
628
" adjacency matrix must have only nonnegative"+
629
" integer entries")
630
weighted = True
631
if multiedges is None: multiedges = False
632
break
633
if multiedges is None: multiedges = (sorted(entries) != [0,1])
634
if weighted is None: weighted = False
635
for i in xrange(data.nrows()):
636
if data[i,i] != 0:
637
if loops is None: loops = True
638
elif not loops:
639
raise ValueError("Non-looped digraph's adjacency"+
640
" matrix must have zeroes on the diagonal.")
641
break
642
num_verts = data.nrows()
643
elif format == 'incidence_matrix':
644
try:
645
positions = []
646
for c in data.columns():
647
NZ = c.nonzero_positions()
648
if len(NZ) != 2:
649
msg += "There must be two nonzero entries (-1 & 1) per column."
650
assert False
651
L = uniq(c.list())
652
L.sort()
653
if L != [-1,0,1]:
654
msg += "Each column represents an edge: -1 goes to 1."
655
assert False
656
if c[NZ[0]] == -1:
657
positions.append(tuple(NZ))
658
else:
659
positions.append((NZ[1],NZ[0]))
660
if loops is None: loops = False
661
if weighted is None: weighted = False
662
if multiedges is None:
663
total = len(positions)
664
multiedges = ( len(uniq(positions)) < total )
665
except AssertionError:
666
raise ValueError(msg)
667
num_verts = data.nrows()
668
elif format == 'DiGraph':
669
if loops is None: loops = data.allows_loops()
670
elif not loops and data.has_loops():
671
raise ValueError("No loops but input digraph has loops.")
672
if multiedges is None: multiedges = data.allows_multiple_edges()
673
elif not multiedges:
674
e = data.edges(labels=False)
675
if len(e) != len(uniq(e)):
676
raise ValueError("No multiple edges but input digraph"+
677
" has multiple edges.")
678
if weighted is None: weighted = data.weighted()
679
num_verts = data.num_verts()
680
verts = data.vertex_iterator()
681
if data.get_pos() is not None:
682
pos = data.get_pos().copy()
683
elif format == 'rule':
684
f = data[1]
685
if loops is None: loops = any(f(v,v) for v in data[0])
686
if multiedges is None: multiedges = False
687
if weighted is None: weighted = False
688
num_verts = len(data[0])
689
verts = data[0]
690
elif format == 'dict_of_dicts':
691
if not all(isinstance(data[u], dict) for u in data):
692
raise ValueError("Input dict must be a consistent format.")
693
verts = set(data.keys())
694
if loops is None or loops is False:
695
for u in data:
696
if u in data[u]:
697
if loops is None: loops = True
698
elif loops is False:
699
raise ValueError("No loops but dict has loops.")
700
break
701
if loops is None: loops = False
702
if weighted is None: weighted = False
703
for u in data:
704
for v in data[u]:
705
if v not in verts: verts.add(v)
706
if multiedges is not False and not isinstance(data[u][v], list):
707
if multiedges is None: multiedges = False
708
if multiedges:
709
raise ValueError("Dict of dicts for multidigraph must be in the format {v : {u : list}}")
710
if multiedges is None and len(data) > 0:
711
multiedges = True
712
num_verts = len(verts)
713
elif format == 'dict_of_lists':
714
# convert to a dict of lists if not already one
715
if not all(isinstance(data[u], list) for u in data):
716
data = dict([u, list(data[u])] for u in data)
717
verts = set(data.keys())
718
if loops is None or loops is False:
719
for u in data:
720
if u in data[u]:
721
if loops is None: loops = True
722
elif loops is False:
723
raise ValueError("No loops but dict has loops.")
724
break
725
if loops is None: loops = False
726
if weighted is None: weighted = False
727
for u in data:
728
verts = verts.union([v for v in data[u] if v not in verts])
729
if len(uniq(data[u])) != len(data[u]):
730
if multiedges is False:
731
raise ValueError("Non-multidigraph input dict has multiple edges (%s,%s)"%(u, choice([v for v in data[u] if data[u].count(v) > 1])))
732
if multiedges is None: multiedges = True
733
if multiedges is None: multiedges = False
734
num_verts = len(verts)
735
elif format == 'NX':
736
if weighted is None:
737
if isinstance(data, networkx.DiGraph):
738
weighted = False
739
if multiedges is None:
740
multiedges = False
741
if loops is None:
742
loops = False
743
else:
744
weighted = True
745
if multiedges is None:
746
multiedges = data.multiedges
747
if loops is None:
748
loops = data.selfloops
749
num_verts = data.order()
750
verts = data.nodes()
751
data = data.adj
752
format = 'dict_of_dicts'
753
elif format == 'int':
754
if weighted is None: weighted = False
755
if multiedges is None: multiedges = False
756
if loops is None: loops = False
757
num_verts = data
758
759
# weighted, multiedges, loops, verts and num_verts should now be set
760
761
if implementation == 'networkx':
762
import networkx
763
from sage.graphs.base.graph_backends import NetworkXGraphBackend
764
if format == 'DiGraph':
765
self._backend = NetworkXGraphBackend(data.networkx_graph())
766
self._weighted = weighted
767
self.allow_loops(loops)
768
self.allow_multiple_edges(multiedges)
769
else:
770
self._backend = NetworkXGraphBackend(networkx.MultiDiGraph())
771
self._weighted = weighted
772
self.allow_loops(loops)
773
self.allow_multiple_edges(multiedges)
774
if verts is not None:
775
self.add_vertices(verts)
776
else:
777
self.add_vertices(range(num_verts))
778
elif implementation == 'c_graph':
779
if multiedges or weighted:
780
if not sparse:
781
raise RuntimeError("Multiedge and weighted c_graphs must be sparse.")
782
from sage.graphs.base.sparse_graph import SparseGraphBackend
783
from sage.graphs.base.dense_graph import DenseGraphBackend
784
CGB = SparseGraphBackend if sparse else DenseGraphBackend
785
if format == 'DiGraph':
786
self._backend = CGB(0, directed=True)
787
self.add_vertices(verts)
788
self._weighted = weighted
789
self.allow_loops(loops, check=False)
790
self.allow_multiple_edges(multiedges, check=False)
791
for u,v,l in data.edge_iterator():
792
self._backend.add_edge(u,v,l,True)
793
else:
794
self._backend = CGB(0, directed=True)
795
if verts is not None:
796
self._backend = CGB(0, directed=True)
797
self.add_vertices(verts)
798
else:
799
self._backend = CGB(num_verts, directed=True)
800
self._weighted = weighted
801
self.allow_loops(loops, check=False)
802
self.allow_multiple_edges(multiedges, check=False)
803
self._backend.directed = True
804
else:
805
raise NotImplementedError("Supported implementations: networkx, c_graph.")
806
807
if format == 'dig6':
808
k = 0
809
for i in xrange(n):
810
for j in xrange(n):
811
if m[k] == '1':
812
self.add_edge(i, j)
813
k += 1
814
elif format == 'adjacency_matrix':
815
e = []
816
if weighted:
817
for i,j in data.nonzero_positions():
818
e.append((i,j,data[i][j]))
819
elif multiedges:
820
for i,j in data.nonzero_positions():
821
e += [(i,j)]*int(data[i][j])
822
else:
823
for i,j in data.nonzero_positions():
824
e.append((i,j))
825
self.add_edges(e)
826
elif format == 'incidence_matrix':
827
self.add_edges(positions)
828
elif format == 'DiGraph':
829
self.name(data.name())
830
elif format == 'rule':
831
verts = list(verts)
832
for u in xrange(num_verts):
833
for v in xrange(num_verts):
834
uu,vv = verts[u], verts[v]
835
if f(uu,vv):
836
self.add_edge(uu,vv)
837
elif format == 'dict_of_dicts':
838
if convert_empty_dict_labels_to_None:
839
for u in data:
840
for v in data[u]:
841
if multiedges:
842
self.add_edges([(u,v,l) for l in data[u][v]])
843
else:
844
self.add_edge((u,v,data[u][v] if data[u][v] != {} else None))
845
else:
846
for u in data:
847
for v in data[u]:
848
if multiedges:
849
self.add_edges([(u,v,l) for l in data[u][v]])
850
else:
851
self.add_edge((u,v,data[u][v]))
852
elif format == 'dict_of_lists':
853
for u in data:
854
for v in data[u]:
855
self.add_edge(u,v)
856
else:
857
assert format == 'int'
858
self._pos = pos
859
self._boundary = boundary
860
if format != 'DiGraph' or name is not None:
861
self.name(name)
862
863
### Formats
864
865
def dig6_string(self):
866
"""
867
Returns the dig6 representation of the digraph as an ASCII string.
868
Valid for single (no multiple edges) digraphs on 0 to 262143
869
vertices.
870
871
EXAMPLES::
872
873
sage: D = DiGraph()
874
sage: D.dig6_string()
875
'?'
876
sage: D.add_edge(0,1)
877
sage: D.dig6_string()
878
'AO'
879
"""
880
n = self.order()
881
if n > 262143:
882
raise ValueError('dig6 format supports graphs on 0 to 262143 vertices only.')
883
elif self.has_multiple_edges():
884
raise ValueError('dig6 format does not support multiple edges.')
885
else:
886
return generic_graph_pyx.N(n) + generic_graph_pyx.R(self._bit_vector())
887
888
### Attributes
889
890
def is_directed(self):
891
"""
892
Since digraph is directed, returns True.
893
894
EXAMPLES::
895
896
sage: DiGraph().is_directed()
897
True
898
"""
899
return True
900
901
### Properties
902
903
def is_directed_acyclic(self, certificate = False):
904
"""
905
Returns whether the digraph is acyclic or not.
906
907
A directed graph is acyclic if for any vertex `v`, there is no directed
908
path that starts and ends at `v`. Every directed acyclic graph (DAG)
909
corresponds to a partial ordering of its vertices, however multiple dags
910
may lead to the same partial ordering.
911
912
INPUT:
913
914
- ``certificate`` -- whether to return a certificate (``False`` by
915
default).
916
917
OUTPUT:
918
919
* When ``certificate=False``, returns a boolean value.
920
921
* When ``certificate=True`` :
922
923
* If the graph is acyclic, returns a pair ``(True, ordering)``
924
where ``ordering`` is a list of the vertices such that ``u``
925
appears before ``v`` in ``ordering`` if ``u, v`` is an edge.
926
927
* Else, returns a pair ``(False, cycle)`` where ``cycle`` is a
928
list of vertices representing a circuit in the graph.
929
930
EXAMPLES:
931
932
At first, the following graph is acyclic::
933
934
sage: D = DiGraph({ 0:[1,2,3], 4:[2,5], 1:[8], 2:[7], 3:[7], 5:[6,7], 7:[8], 6:[9], 8:[10], 9:[10] })
935
sage: D.plot(layout='circular').show()
936
sage: D.is_directed_acyclic()
937
True
938
939
Adding an edge from `9` to `7` does not change it::
940
941
sage: D.add_edge(9,7)
942
sage: D.is_directed_acyclic()
943
True
944
945
We can obtain as a proof an ordering of the vertices such that `u`
946
appears before `v` if `uv` is an edge of the graph::
947
948
sage: D.is_directed_acyclic(certificate = True)
949
(True, [4, 5, 6, 9, 0, 1, 2, 3, 7, 8, 10])
950
951
Adding an edge from 7 to 4, though, makes a difference::
952
953
sage: D.add_edge(7,4)
954
sage: D.is_directed_acyclic()
955
False
956
957
Indeed, it creates a circuit `7, 4, 5`::
958
959
sage: D.is_directed_acyclic(certificate = True)
960
(False, [7, 4, 5])
961
962
Checking acyclic graphs are indeed acyclic ::
963
964
sage: def random_acyclic(n, p):
965
... g = graphs.RandomGNP(n, p)
966
... h = DiGraph()
967
... h.add_edges([ ((u,v) if u<v else (v,u)) for u,v,_ in g.edges() ])
968
... return h
969
...
970
sage: all( random_acyclic(100, .2).is_directed_acyclic() # long time
971
... for i in range(50)) # long time
972
True
973
"""
974
return self._backend.is_directed_acyclic(certificate = certificate)
975
976
def is_transitive(self, certificate = False):
977
r"""
978
Tests whether the given digraph is transitive.
979
980
A digraph is transitive if for any pair of vertices `u,v\in G` linked by a
981
`uv`-path the edge `uv` belongs to `G`.
982
983
INPUT:
984
985
- ``certificate`` -- whether to return a certificate for negative answers.
986
987
- If ``certificate = False`` (default), this method returns ``True`` or
988
``False`` according to the graph.
989
990
- If ``certificate = True``, this method either returns ``True`` answers
991
or yield a pair of vertices `uv` such that there exists a `uv`-path in
992
`G` but `uv\not\in G`.
993
994
EXAMPLE::
995
996
sage: digraphs.Circuit(4).is_transitive()
997
False
998
sage: digraphs.Circuit(4).is_transitive(certificate = True)
999
(0, 2)
1000
sage: digraphs.RandomDirectedGNP(30,.2).is_transitive()
1001
False
1002
sage: digraphs.DeBruijn(5,2).is_transitive()
1003
False
1004
sage: digraphs.DeBruijn(5,2).is_transitive(certificate = True)
1005
('00', '10')
1006
sage: digraphs.RandomDirectedGNP(20,.2).transitive_closure().is_transitive()
1007
True
1008
"""
1009
from sage.graphs.comparability import is_transitive
1010
return is_transitive(self, certificate = certificate)
1011
1012
def to_directed(self):
1013
"""
1014
Since the graph is already directed, simply returns a copy of
1015
itself.
1016
1017
EXAMPLES::
1018
1019
sage: DiGraph({0:[1,2,3],4:[5,1]}).to_directed()
1020
Digraph on 6 vertices
1021
"""
1022
from copy import copy
1023
return copy(self)
1024
1025
def to_undirected(self, implementation='c_graph', sparse=None):
1026
"""
1027
Returns an undirected version of the graph. Every directed edge
1028
becomes an edge.
1029
1030
EXAMPLES::
1031
1032
sage: D = DiGraph({0:[1,2],1:[0]})
1033
sage: G = D.to_undirected()
1034
sage: D.edges(labels=False)
1035
[(0, 1), (0, 2), (1, 0)]
1036
sage: G.edges(labels=False)
1037
[(0, 1), (0, 2)]
1038
"""
1039
if sparse is None:
1040
from sage.graphs.base.dense_graph import DenseGraphBackend
1041
sparse = (not isinstance(self._backend, DenseGraphBackend))
1042
from sage.graphs.all import Graph
1043
G = Graph(name=self.name(), pos=self._pos, boundary=self._boundary,
1044
multiedges=self.allows_multiple_edges(), loops=self.allows_loops(),
1045
implementation=implementation, sparse=sparse)
1046
G.name(self.name())
1047
G.add_vertices(self.vertex_iterator())
1048
G.add_edges(self.edge_iterator())
1049
if hasattr(self, '_embedding'):
1050
from copy import copy
1051
G._embedding = copy(self._embedding)
1052
G._weighted = self._weighted
1053
return G
1054
1055
### Edge Handlers
1056
1057
def incoming_edge_iterator(self, vertices=None, labels=True):
1058
"""
1059
Return an iterator over all arriving edges from vertices, or over
1060
all edges if vertices is None.
1061
1062
EXAMPLES::
1063
1064
sage: D = DiGraph( { 0: [1,2,3], 1: [0,2], 2: [3], 3: [4], 4: [0,5], 5: [1] } )
1065
sage: for a in D.incoming_edge_iterator([0]):
1066
... print a
1067
(1, 0, None)
1068
(4, 0, None)
1069
"""
1070
if vertices is None:
1071
vertices = self
1072
elif vertices in self:
1073
vertices = [vertices]
1074
else:
1075
vertices = [v for v in vertices if v in self]
1076
return self._backend.iterator_in_edges(vertices, labels)
1077
1078
def incoming_edges(self, vertices=None, labels=True):
1079
"""
1080
Returns a list of edges arriving at vertices.
1081
1082
INPUT:
1083
1084
1085
- ``labels`` - if False, each edge is a tuple (u,v) of
1086
vertices.
1087
1088
1089
EXAMPLES::
1090
1091
sage: D = DiGraph( { 0: [1,2,3], 1: [0,2], 2: [3], 3: [4], 4: [0,5], 5: [1] } )
1092
sage: D.incoming_edges([0])
1093
[(1, 0, None), (4, 0, None)]
1094
"""
1095
return list(self.incoming_edge_iterator(vertices, labels=labels))
1096
1097
def outgoing_edge_iterator(self, vertices=None, labels=True):
1098
"""
1099
Return an iterator over all departing edges from vertices, or over
1100
all edges if vertices is None.
1101
1102
EXAMPLES::
1103
1104
sage: D = DiGraph( { 0: [1,2,3], 1: [0,2], 2: [3], 3: [4], 4: [0,5], 5: [1] } )
1105
sage: for a in D.outgoing_edge_iterator([0]):
1106
... print a
1107
(0, 1, None)
1108
(0, 2, None)
1109
(0, 3, None)
1110
"""
1111
if vertices is None:
1112
vertices = self
1113
elif vertices in self:
1114
vertices = [vertices]
1115
else:
1116
vertices = [v for v in vertices if v in self]
1117
return self._backend.iterator_out_edges(vertices, labels)
1118
1119
def outgoing_edges(self, vertices=None, labels=True):
1120
"""
1121
Returns a list of edges departing from vertices.
1122
1123
INPUT:
1124
1125
1126
- ``labels`` - if False, each edge is a tuple (u,v) of
1127
vertices.
1128
1129
1130
EXAMPLES::
1131
1132
sage: D = DiGraph( { 0: [1,2,3], 1: [0,2], 2: [3], 3: [4], 4: [0,5], 5: [1] } )
1133
sage: D.outgoing_edges([0])
1134
[(0, 1, None), (0, 2, None), (0, 3, None)]
1135
"""
1136
return list(self.outgoing_edge_iterator(vertices, labels=labels))
1137
1138
def neighbor_in_iterator(self, vertex):
1139
"""
1140
Returns an iterator over the in-neighbors of vertex.
1141
1142
An vertex `u` is an in-neighbor of a vertex `v` if `uv` in an edge.
1143
1144
EXAMPLES::
1145
1146
sage: D = DiGraph( { 0: [1,2,3], 1: [0,2], 2: [3], 3: [4], 4: [0,5], 5: [1] } )
1147
sage: for a in D.neighbor_in_iterator(0):
1148
... print a
1149
1
1150
4
1151
"""
1152
return iter(set(self._backend.iterator_in_nbrs(vertex)))
1153
1154
predecessor_iterator = deprecated_function_alias(neighbor_in_iterator, 'Sage Version 4.3')
1155
1156
def neighbors_in(self, vertex):
1157
"""
1158
Returns the list of the in-neighbors of a given vertex.
1159
1160
An vertex `u` is an in-neighbor of a vertex `v` if `uv` in an edge.
1161
1162
EXAMPLES::
1163
1164
sage: D = DiGraph( { 0: [1,2,3], 1: [0,2], 2: [3], 3: [4], 4: [0,5], 5: [1] } )
1165
sage: D.neighbors_in(0)
1166
[1, 4]
1167
"""
1168
return list(self.neighbor_in_iterator(vertex))
1169
1170
predecessors = deprecated_function_alias(neighbors_in, 'Sage Version 4.3')
1171
1172
def neighbor_out_iterator(self, vertex):
1173
"""
1174
Returns an iterator over the out-neighbors of a given vertex.
1175
1176
An vertex `u` is an out-neighbor of a vertex `v` if `vu` in an edge.
1177
1178
EXAMPLES::
1179
1180
sage: D = DiGraph( { 0: [1,2,3], 1: [0,2], 2: [3], 3: [4], 4: [0,5], 5: [1] } )
1181
sage: for a in D.neighbor_out_iterator(0):
1182
... print a
1183
1
1184
2
1185
3
1186
"""
1187
return iter(set(self._backend.iterator_out_nbrs(vertex)))
1188
1189
successor_iterator = deprecated_function_alias(neighbor_out_iterator, 'Sage Version 4.3')
1190
1191
def neighbors_out(self, vertex):
1192
"""
1193
Returns the list of the out-neighbors of a given vertex.
1194
1195
An vertex `u` is an out-neighbor of a vertex `v` if `vu` in an edge.
1196
1197
EXAMPLES::
1198
1199
sage: D = DiGraph( { 0: [1,2,3], 1: [0,2], 2: [3], 3: [4], 4: [0,5], 5: [1] } )
1200
sage: D.neighbors_out(0)
1201
[1, 2, 3]
1202
"""
1203
return list(self.neighbor_out_iterator(vertex))
1204
1205
successors = deprecated_function_alias(neighbors_out, 'Sage Version 4.3')
1206
1207
### Degree functions
1208
1209
def in_degree(self, vertices=None, labels=False):
1210
"""
1211
Same as degree, but for in degree.
1212
1213
EXAMPLES::
1214
1215
sage: D = DiGraph( { 0: [1,2,3], 1: [0,2], 2: [3], 3: [4], 4: [0,5], 5: [1] } )
1216
sage: D.in_degree(vertices = [0,1,2], labels=True)
1217
{0: 2, 1: 2, 2: 2}
1218
sage: D.in_degree()
1219
[2, 2, 2, 2, 1, 1]
1220
sage: G = graphs.PetersenGraph().to_directed()
1221
sage: G.in_degree(0)
1222
3
1223
"""
1224
if vertices in self:
1225
for d in self.in_degree_iterator(vertices):
1226
return d # (weird, but works: only happens once!)
1227
elif labels:
1228
di = {}
1229
for v, d in self.in_degree_iterator(vertices, labels=labels):
1230
di[v] = d
1231
return di
1232
else:
1233
return list(self.in_degree_iterator(vertices, labels=labels))
1234
1235
def in_degree_iterator(self, vertices=None, labels=False):
1236
"""
1237
Same as degree_iterator, but for in degree.
1238
1239
EXAMPLES::
1240
1241
sage: D = graphs.Grid2dGraph(2,4).to_directed()
1242
sage: for i in D.in_degree_iterator():
1243
... print i
1244
3
1245
3
1246
2
1247
3
1248
2
1249
2
1250
2
1251
3
1252
sage: for i in D.in_degree_iterator(labels=True):
1253
... print i
1254
((0, 1), 3)
1255
((1, 2), 3)
1256
((0, 0), 2)
1257
((0, 2), 3)
1258
((1, 3), 2)
1259
((1, 0), 2)
1260
((0, 3), 2)
1261
((1, 1), 3)
1262
"""
1263
if vertices is None:
1264
vertices = self.vertex_iterator()
1265
elif vertices in self:
1266
d = 0
1267
for e in self.incoming_edge_iterator(vertices):
1268
d += 1
1269
yield d
1270
return
1271
if labels:
1272
for v in vertices:
1273
d = 0
1274
for e in self.incoming_edge_iterator(v):
1275
d += 1
1276
yield (v, d)
1277
else:
1278
for v in vertices:
1279
d = 0
1280
for e in self.incoming_edge_iterator(v):
1281
d += 1
1282
yield d
1283
1284
def in_degree_sequence(self):
1285
r"""
1286
Return the indegree sequence.
1287
1288
EXAMPLES:
1289
1290
The indegree sequences of two digraphs::
1291
1292
sage: g = DiGraph({1: [2, 5, 6], 2: [3, 6], 3: [4, 6], 4: [6], 5: [4, 6]})
1293
sage: g.in_degree_sequence()
1294
[5, 2, 1, 1, 1, 0]
1295
1296
::
1297
1298
sage: V = [2, 3, 5, 7, 8, 9, 10, 11]
1299
sage: E = [[], [8, 10], [11], [8, 11], [9], [], [], [2, 9, 10]]
1300
sage: g = DiGraph(dict(zip(V, E)))
1301
sage: g.in_degree_sequence()
1302
[2, 2, 2, 2, 1, 0, 0, 0]
1303
"""
1304
return sorted(self.in_degree_iterator(), reverse=True)
1305
1306
def out_degree(self, vertices=None, labels=False):
1307
"""
1308
Same as degree, but for out degree.
1309
1310
EXAMPLES::
1311
1312
sage: D = DiGraph( { 0: [1,2,3], 1: [0,2], 2: [3], 3: [4], 4: [0,5], 5: [1] } )
1313
sage: D.out_degree(vertices = [0,1,2], labels=True)
1314
{0: 3, 1: 2, 2: 1}
1315
sage: D.out_degree()
1316
[3, 2, 1, 1, 2, 1]
1317
sage: D.out_degree(2)
1318
1
1319
"""
1320
if vertices in self:
1321
1322
try:
1323
return self._backend.out_degree(vertices)
1324
1325
except AttributeError:
1326
for d in self.out_degree_iterator(vertices):
1327
return d # (weird, but works: only happens once!)
1328
1329
elif labels:
1330
di = {}
1331
for v, d in self.out_degree_iterator(vertices, labels=labels):
1332
di[v] = d
1333
return di
1334
else:
1335
return list(self.out_degree_iterator(vertices, labels=labels))
1336
1337
def out_degree_iterator(self, vertices=None, labels=False):
1338
"""
1339
Same as degree_iterator, but for out degree.
1340
1341
EXAMPLES::
1342
1343
sage: D = graphs.Grid2dGraph(2,4).to_directed()
1344
sage: for i in D.out_degree_iterator():
1345
... print i
1346
3
1347
3
1348
2
1349
3
1350
2
1351
2
1352
2
1353
3
1354
sage: for i in D.out_degree_iterator(labels=True):
1355
... print i
1356
((0, 1), 3)
1357
((1, 2), 3)
1358
((0, 0), 2)
1359
((0, 2), 3)
1360
((1, 3), 2)
1361
((1, 0), 2)
1362
((0, 3), 2)
1363
((1, 1), 3)
1364
"""
1365
if vertices is None:
1366
vertices = self.vertex_iterator()
1367
elif vertices in self:
1368
d = 0
1369
for e in self.outgoing_edge_iterator(vertices):
1370
d += 1
1371
yield d
1372
return
1373
if labels:
1374
for v in vertices:
1375
d = 0
1376
for e in self.outgoing_edge_iterator(v):
1377
d += 1
1378
yield (v, d)
1379
else:
1380
for v in vertices:
1381
d = 0
1382
for e in self.outgoing_edge_iterator(v):
1383
d += 1
1384
yield d
1385
1386
def out_degree_sequence(self):
1387
r"""
1388
Return the outdegree sequence of this digraph.
1389
1390
EXAMPLES:
1391
1392
The outdegree sequences of two digraphs::
1393
1394
sage: g = DiGraph({1: [2, 5, 6], 2: [3, 6], 3: [4, 6], 4: [6], 5: [4, 6]})
1395
sage: g.out_degree_sequence()
1396
[3, 2, 2, 2, 1, 0]
1397
1398
::
1399
1400
sage: V = [2, 3, 5, 7, 8, 9, 10, 11]
1401
sage: E = [[], [8, 10], [11], [8, 11], [9], [], [], [2, 9, 10]]
1402
sage: g = DiGraph(dict(zip(V, E)))
1403
sage: g.out_degree_sequence()
1404
[3, 2, 2, 1, 1, 0, 0, 0]
1405
"""
1406
return sorted(self.out_degree_iterator(), reverse=True)
1407
1408
1409
def feedback_edge_set(self, constraint_generation= True, value_only=False, solver=None, verbose=0):
1410
r"""
1411
Computes the minimum feedback edge set of a digraph (also called
1412
feedback arc set).
1413
1414
The minimum feedback edge set of a digraph is a set of edges that
1415
intersect all the circuits of the digraph. Equivalently, a minimum
1416
feedback arc set of a DiGraph is a set `S` of arcs such that the digraph
1417
`G-S` is acyclic. For more information, see the `Wikipedia article on
1418
feedback arc sets <http://en.wikipedia.org/wiki/Feedback_arc_set>`_.
1419
1420
INPUT:
1421
1422
- ``value_only`` -- boolean (default: ``False``)
1423
1424
- When set to ``True``, only the minimum cardinal of a minimum edge
1425
set is returned.
1426
1427
- When set to ``False``, the ``Set`` of edges of a minimal edge set is
1428
returned.
1429
1430
- ``constraint_generation`` (boolean) -- whether to use constraint
1431
generation when solving the Mixed Integer Linear Program (default:
1432
``True``).
1433
1434
- ``solver`` -- (default: ``None``) Specify a Linear Program (LP)
1435
solver to be used. If set to ``None``, the default one is used. For
1436
more information on LP solvers and which default solver is used, see
1437
the method
1438
:meth:`solve <sage.numerical.mip.MixedIntegerLinearProgram.solve>`
1439
of the class
1440
:class:`MixedIntegerLinearProgram <sage.numerical.mip.MixedIntegerLinearProgram>`.
1441
1442
- ``verbose`` -- integer (default: ``0``). Sets the level of
1443
verbosity. Set to 0 by default, which means quiet.
1444
1445
ALGORITHM:
1446
1447
This problem is solved using Linear Programming, in two different
1448
ways. The first one is to solve the following formulation:
1449
1450
.. MATH::
1451
1452
\mbox{Minimize : }&\sum_{(u,v)\in G} b_{(u,v)}\\
1453
\mbox{Such that : }&\\
1454
&\forall (u,v)\in G, d_u-d_v+ n \cdot b_{(u,v)}\geq 0\\
1455
&\forall u\in G, 0\leq d_u\leq |G|\\
1456
1457
An explanation:
1458
1459
An acyclic digraph can be seen as a poset, and every poset has a linear
1460
extension. This means that in any acyclic digraph the vertices can be
1461
ordered with a total order `<` in such a way that if `(u,v)\in G`, then
1462
`u<v`.
1463
1464
Thus, this linear program is built in order to assign to each vertex `v`
1465
a number `d_v\in [0,\dots,n-1]` such that if there exists an edge
1466
`(u,v)\in G` such that `d_v<d_u`, then the edge `(u,v)` is removed.
1467
1468
The number of edges removed is then minimized, which is the objective.
1469
1470
(Constraint Generation)
1471
1472
If the parameter ``constraint_generation`` is enabled, a more efficient
1473
formulation is used :
1474
1475
.. MATH::
1476
1477
\mbox{Minimize : }&\sum_{(u,v)\in G} b_{(u,v)}\\
1478
\mbox{Such that : }&\\
1479
&\forall C\text{ circuits }\subseteq G, \sum_{uv\in C}b_{(u,v)}\geq 1\\
1480
1481
As the number of circuits contained in a graph is exponential, this LP
1482
is solved through constraint generation. This means that the solver is
1483
sequentially asked to solved the problem, knowing only a portion of the
1484
circuits contained in `G`, each time adding to the list of its
1485
constraints the circuit which its last answer had left intact.
1486
1487
EXAMPLES:
1488
1489
If the digraph is created from a graph, and hence is symmetric (if `uv`
1490
is an edge, then `vu` is an edge too), then obviously the cardinality of
1491
its feedback arc set is the number of edges in the first graph::
1492
1493
sage: cycle=graphs.CycleGraph(5)
1494
sage: dcycle=DiGraph(cycle)
1495
sage: cycle.size()
1496
5
1497
sage: dcycle.feedback_edge_set(value_only=True)
1498
5
1499
1500
And in this situation, for any edge `uv` of the first graph, `uv` of
1501
`vu` is in the returned feedback arc set::
1502
1503
sage: g = graphs.RandomGNP(5,.3)
1504
sage: dg = DiGraph(g)
1505
sage: feedback = dg.feedback_edge_set()
1506
sage: (u,v,l) = g.edge_iterator().next()
1507
sage: (u,v) in feedback or (v,u) in feedback
1508
True
1509
1510
TESTS:
1511
1512
Comparing with/without constraint generation. Also double-checks ticket :trac:`12833`::
1513
1514
sage: for i in range(20):
1515
... g = digraphs.RandomDirectedGNP(10,.3)
1516
... x = g.feedback_edge_set(value_only = True)
1517
... y = g.feedback_edge_set(value_only = True,
1518
... constraint_generation = False)
1519
... if x != y:
1520
... print "Oh my, oh my !"
1521
... break
1522
"""
1523
# It would be a pity to start a LP if the digraph is already acyclic
1524
if self.is_directed_acyclic():
1525
return 0 if value_only else []
1526
1527
from sage.numerical.mip import MixedIntegerLinearProgram, Sum
1528
1529
########################################
1530
# Constraint Generation Implementation #
1531
########################################
1532
if constraint_generation:
1533
1534
p = MixedIntegerLinearProgram(constraint_generation = True,
1535
maximization = False)
1536
1537
# An variable for each edge
1538
b = p.new_variable(binary = True, dim = 2)
1539
1540
# Variables are binary, and their coefficient in the objective is 1
1541
1542
p.set_objective( Sum( b[u][v]
1543
for u,v in self.edges(labels = False)))
1544
1545
p.solve(log = verbose)
1546
1547
# For as long as we do not break because the digraph is
1548
# acyclic....
1549
while True:
1550
1551
# Building the graph without the edges removed by the LP
1552
h = DiGraph()
1553
for u,v in self.edges(labels = False):
1554
if p.get_values(b[u][v]) < .5:
1555
h.add_edge(u,v)
1556
1557
# Is the digraph acyclic ?
1558
isok, certificate = h.is_directed_acyclic(certificate = True)
1559
1560
# If so, we are done !
1561
if isok:
1562
break
1563
1564
if verbose:
1565
print "Adding a constraint on circuit : ",certificate
1566
1567
# There is a circuit left. Let's add the corresponding
1568
# constraint !
1569
1570
p.add_constraint(
1571
Sum( b[u][v] for u,v in
1572
zip(certificate, certificate[1:] + [certificate[0]])),
1573
min = 1)
1574
1575
obj = p.solve(log = verbose)
1576
1577
if value_only:
1578
return Integer(round(obj))
1579
1580
else:
1581
1582
# listing the edges contained in the MFAS
1583
return [(u,v) for u,v in self.edges(labels = False)
1584
if p.get_values(b[u][v]) > .5]
1585
1586
######################################
1587
# Ordering-based MILP Implementation #
1588
######################################
1589
else:
1590
p=MixedIntegerLinearProgram(maximization=False, solver=solver)
1591
1592
b=p.new_variable(binary = True)
1593
d=p.new_variable(integer = True)
1594
1595
n=self.order()
1596
1597
for (u,v) in self.edges(labels=None):
1598
p.add_constraint(d[u]-d[v]+n*(b[(u,v)]),min=1)
1599
1600
for v in self:
1601
p.add_constraint(d[v] <= n)
1602
1603
p.set_objective(Sum([b[(u,v)] for (u,v) in self.edges(labels=None)]))
1604
1605
if value_only:
1606
return Integer(round(p.solve(objective_only=True, log=verbose)))
1607
else:
1608
p.solve(log=verbose)
1609
1610
b_sol=p.get_values(b)
1611
1612
return [(u,v) for (u,v) in self.edges(labels=None) if b_sol[(u,v)]==1]
1613
1614
def feedback_vertex_set(self, value_only=False, solver=None, verbose=0, constraint_generation = True):
1615
r"""
1616
Computes the minimum feedback vertex set of a digraph.
1617
1618
The minimum feedback vertex set of a digraph is a set of vertices
1619
that intersect all the circuits of the digraph.
1620
Equivalently, a minimum feedback vertex set of a DiGraph is a set
1621
`S` of vertices such that the digraph `G-S` is acyclic. For more
1622
information, see the
1623
`Wikipedia article on feedback vertex sets
1624
<http://en.wikipedia.org/wiki/Feedback_vertex_set>`_.
1625
1626
INPUT:
1627
1628
- ``value_only`` -- boolean (default: ``False``)
1629
1630
- When set to ``True``, only the minimum cardinal of a minimum vertex
1631
set is returned.
1632
1633
- When set to ``False``, the ``Set`` of vertices of a minimal feedback
1634
vertex set is returned.
1635
1636
- ``solver`` -- (default: ``None``) Specify a Linear Program (LP)
1637
solver to be used. If set to ``None``, the default one is used. For
1638
more information on LP solvers and which default solver is used,
1639
see the method
1640
:meth:`solve <sage.numerical.mip.MixedIntegerLinearProgram.solve>`
1641
of the class
1642
:class:`MixedIntegerLinearProgram <sage.numerical.mip.MixedIntegerLinearProgram>`.
1643
1644
- ``verbose`` -- integer (default: ``0``). Sets the level of
1645
verbosity. Set to 0 by default, which means quiet.
1646
1647
- ``constraint_generation`` (boolean) -- whether to use constraint
1648
generation when solving the Mixed Integer Linear Program (default:
1649
``True``).
1650
1651
ALGORITHM:
1652
1653
This problem is solved using Linear Programming, which certainly is not
1654
the best way and will have to be replaced by a better algorithm. The
1655
program to be solved is the following:
1656
1657
.. MATH::
1658
1659
\mbox{Minimize : }&\sum_{v\in G} b_v\\
1660
\mbox{Such that : }&\\
1661
&\forall (u,v)\in G, d_u-d_v+nb_u+nb_v\geq 0\\
1662
&\forall u\in G, 0\leq d_u\leq |G|\\
1663
1664
A brief explanation:
1665
1666
An acyclic digraph can be seen as a poset, and every poset has a linear
1667
extension. This means that in any acyclic digraph the vertices can be
1668
ordered with a total order `<` in such a way that if `(u,v)\in G`, then
1669
`u<v`. Thus, this linear program is built in order to assign to each
1670
vertex `v` a number `d_v\in [0,\dots,n-1]` such that if there exists an
1671
edge `(u,v)\in G` then either `d_v<d_u` or one of `u` or `v` is removed.
1672
The number of vertices removed is then minimized, which is the
1673
objective.
1674
1675
(Constraint Generation)
1676
1677
If the parameter ``constraint_generation`` is enabled, a more efficient
1678
formulation is used :
1679
1680
.. MATH::
1681
1682
\mbox{Minimize : }&\sum_{v\in G} b_{v}\\
1683
\mbox{Such that : }&\\
1684
&\forall C\text{ circuits }\subseteq G, \sum_{v\in C}b_{v}\geq 1\\
1685
1686
As the number of circuits contained in a graph is exponential, this LP
1687
is solved through constraint generation. This means that the solver is
1688
sequentially asked to solved the problem, knowing only a portion of the
1689
circuits contained in `G`, each time adding to the list of its
1690
constraints the circuit which its last answer had left intact.
1691
1692
EXAMPLES:
1693
1694
In a digraph built from a graph, any edge is replaced by arcs going in
1695
the two opposite directions, thus creating a cycle of length two.
1696
Hence, to remove all the cycles from the graph, each edge must see one
1697
of its neighbors removed : a feedback vertex set is in this situation a
1698
vertex cover::
1699
1700
sage: cycle=graphs.CycleGraph(5)
1701
sage: dcycle=DiGraph(cycle)
1702
sage: cycle.vertex_cover(value_only=True)
1703
3
1704
sage: feedback = dcycle.feedback_vertex_set()
1705
sage: len(feedback)
1706
3
1707
sage: (u,v,l) = cycle.edge_iterator().next()
1708
sage: u in feedback or v in feedback
1709
True
1710
1711
For a circuit, the minimum feedback arc set is clearly `1`::
1712
1713
sage: circuit = digraphs.Circuit(5)
1714
sage: circuit.feedback_vertex_set(value_only=True) == 1
1715
True
1716
1717
TESTS:
1718
1719
Comparing with/without constraint generation::
1720
1721
sage: g = digraphs.RandomDirectedGNP(10,.3)
1722
sage: x = g.feedback_vertex_set(value_only = True)
1723
sage: y = g.feedback_vertex_set(value_only = True,
1724
... constraint_generation = False)
1725
sage: x == y
1726
True
1727
"""
1728
1729
# It would be a pity to start a LP if the digraph is already acyclic
1730
if self.is_directed_acyclic():
1731
if value_only:
1732
return 0
1733
return []
1734
1735
from sage.numerical.mip import MixedIntegerLinearProgram, Sum
1736
1737
########################################
1738
# Constraint Generation Implementation #
1739
########################################
1740
if constraint_generation:
1741
1742
p = MixedIntegerLinearProgram(constraint_generation = True,
1743
maximization = False)
1744
1745
# An variable for each vertex
1746
b = p.new_variable(binary = True)
1747
1748
# Variables are binary, and their coefficient in the objective is 1
1749
1750
p.set_objective( Sum( b[v] for v in self))
1751
1752
p.solve(log = verbose)
1753
1754
# For as long as we do not break because the digraph is
1755
# acyclic....
1756
while (1):
1757
1758
# Building the graph without the edges removed by the LP
1759
h = self.subgraph(vertices =
1760
[v for v in self if p.get_values(b[v]) < .5])
1761
1762
# Is the digraph acyclic ?
1763
isok, certificate = h.is_directed_acyclic(certificate = True)
1764
1765
# If so, we are done !
1766
if isok:
1767
break
1768
1769
if verbose:
1770
print "Adding a constraint on circuit : ",certificate
1771
1772
# There is a circuit left. Let's add the corresponding
1773
# constraint !
1774
1775
p.add_constraint( Sum( b[v] for v in certificate), min = 1)
1776
1777
obj = p.solve(log = verbose)
1778
1779
if value_only:
1780
return obj
1781
1782
else:
1783
1784
# listing the edges contained in the MFAS
1785
return [v for v in self if p.get_values(b[v]) > .5]
1786
1787
1788
else:
1789
1790
######################################
1791
# Ordering-based MILP Implementation #
1792
######################################
1793
1794
p = MixedIntegerLinearProgram(maximization=False, solver=solver)
1795
1796
b = p.new_variable(binary = True)
1797
d = p.new_variable(integer = True)
1798
n = self.order()
1799
1800
# The removed vertices cover all the back arcs ( third condition )
1801
for (u,v) in self.edges(labels=None):
1802
p.add_constraint(d[u]-d[v]+n*(b[u]+b[v]),min=1)
1803
1804
for u in self:
1805
p.add_constraint(d[u],max=n)
1806
1807
p.set_objective(Sum([b[v] for v in self]))
1808
1809
if value_only:
1810
return Integer(round(p.solve(objective_only=True, log=verbose)))
1811
else:
1812
p.solve(log=verbose)
1813
b_sol=p.get_values(b)
1814
1815
return [v for v in self if b_sol[v]==1]
1816
1817
1818
### Construction
1819
1820
def reverse(self):
1821
"""
1822
Returns a copy of digraph with edges reversed in direction.
1823
1824
EXAMPLES::
1825
1826
sage: D = DiGraph({ 0: [1,2,3], 1: [0,2], 2: [3], 3: [4], 4: [0,5], 5: [1] })
1827
sage: D.reverse()
1828
Reverse of (): Digraph on 6 vertices
1829
"""
1830
H = DiGraph(multiedges=self.allows_multiple_edges(), loops=self.allows_loops())
1831
H.add_vertices(self)
1832
H.add_edges( [ (v,u,d) for (u,v,d) in self.edge_iterator() ] )
1833
name = self.name()
1834
if name is None:
1835
name = ''
1836
H.name("Reverse of (%s)"%name)
1837
return H
1838
1839
### Paths and cycles iterators
1840
1841
def _all_paths_iterator(self, vertex, ending_vertices=None,
1842
simple=False, max_length=None, trivial=False):
1843
r"""
1844
Returns an iterator over the paths of self starting with the
1845
given vertex.
1846
1847
INPUT:
1848
1849
- ``vertex`` - the starting vertex of the paths.
1850
- ``ending_vertices`` - iterable (default: None) on the allowed
1851
ending vertices of the paths. If None, then all vertices are
1852
allowed.
1853
- ``simple`` - boolean (default: False). If set to True, then
1854
only simple paths are considered. Simple paths are paths in
1855
which no two arcs share a head or share a tail, i.e. every
1856
vertex in the path is entered at most once and exited at most
1857
once.
1858
- ``max_length`` - non negative integer (default: None). The
1859
maximum length of the enumerated paths. If set to None, then
1860
all lengths are allowed.
1861
- ``trivial`` - boolean (default: False). If set to True, then
1862
the empty paths are also enumerated.
1863
1864
OUTPUT:
1865
1866
iterator
1867
1868
EXAMPLES::
1869
1870
sage: g = DiGraph({'a' : ['a', 'b'], 'b' : ['c'], 'c' : ['d'], 'd' : ['c']}, loops=True)
1871
sage: pi = g._all_paths_iterator('a')
1872
sage: for _ in range(5): print pi.next()
1873
['a', 'a']
1874
['a', 'b']
1875
['a', 'a', 'a']
1876
['a', 'a', 'b']
1877
['a', 'b', 'c']
1878
1879
::
1880
1881
sage: pi = g._all_paths_iterator('b')
1882
sage: for _ in range(5): print pi.next()
1883
['b', 'c']
1884
['b', 'c', 'd']
1885
['b', 'c', 'd', 'c']
1886
['b', 'c', 'd', 'c', 'd']
1887
['b', 'c', 'd', 'c', 'd', 'c']
1888
1889
One may wish to enumerate simple paths, which are paths in which
1890
no two arcs share a head or share a tail, i.e. every vertex in
1891
the path is entered at most once and exited at most once. The
1892
result is always finite but may take a long time to compute::
1893
1894
sage: pi = g._all_paths_iterator('a', simple=True)
1895
sage: list(pi)
1896
[['a', 'a'], ['a', 'b'], ['a', 'b', 'c'], ['a', 'b', 'c', 'd']]
1897
sage: pi = g._all_paths_iterator('d', simple=True)
1898
sage: list(pi)
1899
[['d', 'c'], ['d', 'c', 'd']]
1900
1901
It is possible to specify the allowed ending vertices::
1902
1903
sage: pi = g._all_paths_iterator('a', ending_vertices=['c'])
1904
sage: for _ in range(5): print pi.next()
1905
['a', 'b', 'c']
1906
['a', 'a', 'b', 'c']
1907
['a', 'a', 'a', 'b', 'c']
1908
['a', 'b', 'c', 'd', 'c']
1909
['a', 'a', 'a', 'a', 'b', 'c']
1910
sage: pi = g._all_paths_iterator('a', ending_vertices=['a', 'b'])
1911
sage: for _ in range(5): print pi.next()
1912
['a', 'a']
1913
['a', 'b']
1914
['a', 'a', 'a']
1915
['a', 'a', 'b']
1916
['a', 'a', 'a', 'a']
1917
1918
One can bound the length of the paths::
1919
1920
sage: pi = g._all_paths_iterator('d', max_length=3)
1921
sage: list(pi)
1922
[['d', 'c'], ['d', 'c', 'd'], ['d', 'c', 'd', 'c']]
1923
1924
Or include the trivial empty path::
1925
1926
sage: pi = g._all_paths_iterator('a', max_length=3, trivial=True)
1927
sage: list(pi)
1928
[['a'], ['a', 'a'], ['a', 'b'], ['a', 'a', 'a'], ['a', 'a', 'b'],
1929
['a', 'b', 'c'], ['a', 'a', 'a', 'a'], ['a', 'a', 'a', 'b'],
1930
['a', 'a', 'b', 'c'], ['a', 'b', 'c', 'd']]
1931
"""
1932
if ending_vertices is None:
1933
ending_vertices = self
1934
if max_length is None:
1935
from sage.rings.infinity import Infinity
1936
max_length = Infinity
1937
if max_length < 1:
1938
return
1939
1940
# Start with the empty path; we will try all extensions of it
1941
queue = []
1942
path = [vertex]
1943
1944
if trivial and vertex in ending_vertices:
1945
yield path
1946
while True:
1947
# Build next generation of paths, one arc longer; max_length refers
1948
# to edges and not vertices, hence <= and not <
1949
if len(path) <= max_length:
1950
1951
# We try all possible extensions
1952
if simple:
1953
# We only keep simple extensions. An extension is simple
1954
# iff the new vertex being entered has not previously
1955
# occurred in the path, or has occurred but only been
1956
# exited (i.e. is the first vertex in the path). In this
1957
# latter case we must not exit the new vertex again, so we
1958
# do not consider it for further extension, but just yield
1959
# it immediately. See trac #12385.
1960
for neighbor in self.neighbor_out_iterator(path[-1]):
1961
if neighbor not in path:
1962
queue.append(path + [neighbor])
1963
elif ( neighbor == path[0] and
1964
neighbor in ending_vertices ):
1965
yield path + [neighbor]
1966
1967
else:
1968
# Non-simple paths requested: we add all of them
1969
for neighbor in self.neighbor_out_iterator(path[-1]):
1970
queue.append(path + [neighbor])
1971
1972
if not queue:
1973
break
1974
path = queue.pop(0) # get the next path
1975
1976
if path[-1] in ending_vertices:
1977
yield path # yield good path
1978
1979
1980
def all_paths_iterator(self, starting_vertices=None, ending_vertices=None,
1981
simple=False, max_length=None, trivial=False):
1982
r"""
1983
Returns an iterator over the paths of self. The paths are
1984
enumerated in increasing length order.
1985
1986
INPUT:
1987
1988
- ``starting_vertices`` - iterable (default: None) on the
1989
vertices from which the paths must start. If None, then all
1990
vertices of the graph can be starting points.
1991
- ``ending_vertices`` - iterable (default: None) on
1992
the allowed ending vertices of the paths. If None,
1993
then all vertices are allowed.
1994
- ``simple`` - boolean (default: False). If set to True,
1995
then only simple paths are considered. These are paths in
1996
which no two arcs share a head or share a tail, i.e. every
1997
vertex in the path is entered at most once and exited at most
1998
once.
1999
- ``max_length`` - non negative integer (default: None).
2000
The maximum length of the enumerated paths. If set to None,
2001
then all lengths are allowed.
2002
- ``trivial`` - boolean (default: False). If set to True,
2003
then the empty paths are also enumerated.
2004
2005
OUTPUT:
2006
2007
iterator
2008
2009
AUTHOR:
2010
2011
Alexandre Blondin Masse
2012
2013
EXAMPLES::
2014
2015
sage: g = DiGraph({'a' : ['a', 'b'], 'b' : ['c'], 'c' : ['d'], 'd' : ['c']}, loops=True)
2016
sage: pi = g.all_paths_iterator()
2017
sage: for _ in range(7): print pi.next()
2018
['a', 'a']
2019
['a', 'b']
2020
['b', 'c']
2021
['c', 'd']
2022
['d', 'c']
2023
['a', 'a', 'a']
2024
['a', 'a', 'b']
2025
2026
It is possible to precise the allowed starting and/or ending vertices::
2027
2028
sage: pi = g.all_paths_iterator(starting_vertices=['a'])
2029
sage: for _ in range(5): print pi.next()
2030
['a', 'a']
2031
['a', 'b']
2032
['a', 'a', 'a']
2033
['a', 'a', 'b']
2034
['a', 'b', 'c']
2035
sage: pi = g.all_paths_iterator(starting_vertices=['a'], ending_vertices=['b'])
2036
sage: for _ in range(5): print pi.next()
2037
['a', 'b']
2038
['a', 'a', 'b']
2039
['a', 'a', 'a', 'b']
2040
['a', 'a', 'a', 'a', 'b']
2041
['a', 'a', 'a', 'a', 'a', 'b']
2042
2043
One may prefer to enumerate only simple paths (see
2044
:meth:`all_simple_paths`)::
2045
2046
sage: pi = g.all_paths_iterator(simple=True)
2047
sage: list(pi)
2048
[['a', 'a'], ['a', 'b'], ['b', 'c'], ['c', 'd'], ['d', 'c'],
2049
['a', 'b', 'c'], ['b', 'c', 'd'], ['c', 'd', 'c'],
2050
['d', 'c', 'd'], ['a', 'b', 'c', 'd']]
2051
2052
Or simply bound the length of the enumerated paths::
2053
2054
sage: pi = g.all_paths_iterator(starting_vertices=['a'], ending_vertices=['b', 'c'], max_length=6)
2055
sage: list(pi)
2056
[['a', 'b'], ['a', 'a', 'b'], ['a', 'b', 'c'],
2057
['a', 'a', 'a', 'b'], ['a', 'a', 'b', 'c'],
2058
['a', 'a', 'a', 'a', 'b'], ['a', 'a', 'a', 'b', 'c'],
2059
['a', 'b', 'c', 'd', 'c'], ['a', 'a', 'a', 'a', 'a', 'b'],
2060
['a', 'a', 'a', 'a', 'b', 'c'], ['a', 'a', 'b', 'c', 'd', 'c'],
2061
['a', 'a', 'a', 'a', 'a', 'a', 'b'],
2062
['a', 'a', 'a', 'a', 'a', 'b', 'c'],
2063
['a', 'a', 'a', 'b', 'c', 'd', 'c'],
2064
['a', 'b', 'c', 'd', 'c', 'd', 'c']]
2065
2066
By default, empty paths are not enumerated, but it may be
2067
parametrized::
2068
2069
sage: pi = g.all_paths_iterator(simple=True, trivial=True)
2070
sage: list(pi)
2071
[['a'], ['b'], ['c'], ['d'], ['a', 'a'], ['a', 'b'], ['b', 'c'],
2072
['c', 'd'], ['d', 'c'], ['a', 'b', 'c'], ['b', 'c', 'd'],
2073
['c', 'd', 'c'], ['d', 'c', 'd'], ['a', 'b', 'c', 'd']]
2074
sage: pi = g.all_paths_iterator(simple=True, trivial=False)
2075
sage: list(pi)
2076
[['a', 'a'], ['a', 'b'], ['b', 'c'], ['c', 'd'], ['d', 'c'],
2077
['a', 'b', 'c'], ['b', 'c', 'd'], ['c', 'd', 'c'],
2078
['d', 'c', 'd'], ['a', 'b', 'c', 'd']]
2079
"""
2080
if starting_vertices is None:
2081
starting_vertices = self
2082
# We create one paths iterator per vertex
2083
# This is necessary if we want to iterate over paths
2084
# with increasing length
2085
vertex_iterators = dict([(v, self._all_paths_iterator(v, ending_vertices=ending_vertices, simple=simple, max_length=max_length, trivial=trivial)) for v in starting_vertices])
2086
paths = []
2087
for vi in vertex_iterators.values():
2088
try:
2089
path = vi.next()
2090
paths.append((len(path), path))
2091
except(StopIteration):
2092
pass
2093
# Since we always extract a shortest path, using a heap
2094
# can speed up the algorithm
2095
from heapq import heapify, heappop, heappush
2096
heapify(paths)
2097
while paths:
2098
# We choose the shortest available path
2099
_, shortest_path = heappop(paths)
2100
yield shortest_path
2101
# We update the path iterator to its next available path if it exists
2102
try:
2103
path = vertex_iterators[shortest_path[0]].next()
2104
heappush(paths, (len(path), path))
2105
except(StopIteration):
2106
pass
2107
2108
def all_simple_paths(self, starting_vertices=None, ending_vertices=None,
2109
max_length=None, trivial=False):
2110
r"""
2111
Returns a list of all the simple paths of self starting
2112
with one of the given vertices. Simple paths are paths in which
2113
no two arcs share a head or share a tail, i.e. every vertex in
2114
the path is entered at most once and exited at most once.
2115
2116
INPUT:
2117
2118
- ``starting_vertices`` - list (default: None) of vertices
2119
from which the paths must start. If None, then all
2120
vertices of the graph can be starting points.
2121
- ``ending_vertices`` - iterable (default: None) on
2122
the allowed ending vertices of the paths. If None,
2123
then all vertices are allowed.
2124
- ``max_length`` - non negative integer (default: None).
2125
The maximum length of the enumerated paths. If set to None,
2126
then all lengths are allowed.
2127
- ``trivial`` - boolean (default: False). If set to True,
2128
then the empty paths are also enumerated.
2129
2130
OUTPUT:
2131
2132
list
2133
2134
.. NOTE::
2135
2136
Although the number of simple paths of a finite graph
2137
is always finite, computing all its paths may take a very
2138
long time.
2139
2140
EXAMPLES::
2141
2142
sage: g = DiGraph({'a' : ['a', 'b'], 'b' : ['c'], 'c' : ['d'], 'd' : ['c']}, loops=True)
2143
sage: g.all_simple_paths()
2144
[['a', 'a'], ['a', 'b'], ['b', 'c'], ['c', 'd'], ['d', 'c'],
2145
['a', 'b', 'c'], ['b', 'c', 'd'], ['c', 'd', 'c'],
2146
['d', 'c', 'd'], ['a', 'b', 'c', 'd']]
2147
2148
One may compute all paths having specific starting and/or
2149
ending vertices::
2150
2151
sage: g.all_simple_paths(starting_vertices=['a'])
2152
[['a', 'a'], ['a', 'b'], ['a', 'b', 'c'], ['a', 'b', 'c', 'd']]
2153
sage: g.all_simple_paths(starting_vertices=['a'], ending_vertices=['c'])
2154
[['a', 'b', 'c']]
2155
sage: g.all_simple_paths(starting_vertices=['a'], ending_vertices=['b', 'c'])
2156
[['a', 'b'], ['a', 'b', 'c']]
2157
2158
It is also possible to bound the length of the paths::
2159
2160
sage: g.all_simple_paths(max_length=2)
2161
[['a', 'a'], ['a', 'b'], ['b', 'c'], ['c', 'd'], ['d', 'c'],
2162
['a', 'b', 'c'], ['b', 'c', 'd'], ['c', 'd', 'c'],
2163
['d', 'c', 'd']]
2164
2165
By default, empty paths are not enumerated, but this can
2166
be parametrized::
2167
2168
sage: g.all_simple_paths(starting_vertices=['a'], trivial=True)
2169
[['a'], ['a', 'a'], ['a', 'b'], ['a', 'b', 'c'],
2170
['a', 'b', 'c', 'd']]
2171
sage: g.all_simple_paths(starting_vertices=['a'], trivial=False)
2172
[['a', 'a'], ['a', 'b'], ['a', 'b', 'c'], ['a', 'b', 'c', 'd']]
2173
"""
2174
return list(self.all_paths_iterator(starting_vertices=starting_vertices, ending_vertices=ending_vertices, simple=True, max_length=max_length, trivial=trivial))
2175
2176
def _all_cycles_iterator_vertex(self, vertex, starting_vertices=None, simple=False,
2177
rooted=False, max_length=None, trivial=False,
2178
remove_acyclic_edges=True):
2179
r"""
2180
Returns an iterator over the cycles of self starting with the
2181
given vertex.
2182
2183
INPUT:
2184
2185
- ``vertex`` - the starting vertex of the cycle.
2186
- ``starting_vertices`` - iterable (default: None) on
2187
vertices from which the cycles must start. If None,
2188
then all vertices of the graph can be starting points.
2189
This argument is necessary if ``rooted`` is set to True.
2190
- ``simple`` - boolean (default: False). If set to True,
2191
then only simple cycles are considered. A cycle is simple
2192
if the only vertex occuring twice in it is the starting
2193
and ending one.
2194
- ``rooted`` - boolean (default: False). If set to False,
2195
then cycles differing only by their starting vertex are
2196
considered the same (e.g. ``['a', 'b', 'c', 'a']`` and
2197
``['b', 'c', 'a', 'b']``). Otherwise, all cycles are enumerated.
2198
- ``max_length`` - non negative integer (default: None).
2199
The maximum length of the enumerated cycles. If set to None,
2200
then all lengths are allowed.
2201
- ``trivial`` - boolean (default: False). If set to True,
2202
then the empty cycles are also enumerated.
2203
- ``remove_acyclic_edges`` - boolean (default: True) which
2204
precises if the acyclic edges must be removed from the graph.
2205
Used to avoid recomputing it for each vertex.
2206
2207
OUTPUT:
2208
2209
iterator
2210
2211
EXAMPLES::
2212
2213
sage: g = DiGraph({'a' : ['a', 'b'], 'b' : ['c'], 'c' : ['d'], 'd' : ['c']}, loops=True)
2214
sage: it = g._all_cycles_iterator_vertex('a', simple=False, max_length=None)
2215
sage: for i in range(5): print it.next()
2216
['a', 'a']
2217
['a', 'a', 'a']
2218
['a', 'a', 'a', 'a']
2219
['a', 'a', 'a', 'a', 'a']
2220
['a', 'a', 'a', 'a', 'a', 'a']
2221
sage: it = g._all_cycles_iterator_vertex('c', simple=False, max_length=None)
2222
sage: for i in range(5): print it.next()
2223
['c', 'd', 'c']
2224
['c', 'd', 'c', 'd', 'c']
2225
['c', 'd', 'c', 'd', 'c', 'd', 'c']
2226
['c', 'd', 'c', 'd', 'c', 'd', 'c', 'd', 'c']
2227
['c', 'd', 'c', 'd', 'c', 'd', 'c', 'd', 'c', 'd', 'c']
2228
2229
sage: it = g._all_cycles_iterator_vertex('d', simple=False, max_length=None)
2230
sage: for i in range(5): print it.next()
2231
['d', 'c', 'd']
2232
['d', 'c', 'd', 'c', 'd']
2233
['d', 'c', 'd', 'c', 'd', 'c', 'd']
2234
['d', 'c', 'd', 'c', 'd', 'c', 'd', 'c', 'd']
2235
['d', 'c', 'd', 'c', 'd', 'c', 'd', 'c', 'd', 'c', 'd']
2236
2237
It is possible to set a maximum length so that the number of cycles is
2238
finite::
2239
2240
sage: it = g._all_cycles_iterator_vertex('d', simple=False, max_length=6)
2241
sage: list(it)
2242
[['d', 'c', 'd'], ['d', 'c', 'd', 'c', 'd'], ['d', 'c', 'd', 'c', 'd', 'c', 'd']]
2243
2244
When ``simple`` is set to True, the number of cycles is finite since no vertex
2245
but the first one can occur more than once::
2246
2247
sage: it = g._all_cycles_iterator_vertex('d', simple=True, max_length=None)
2248
sage: list(it)
2249
[['d', 'c', 'd']]
2250
2251
By default, the empty cycle is not enumerated::
2252
2253
sage: it = g._all_cycles_iterator_vertex('d', simple=True, trivial=True)
2254
sage: list(it)
2255
[['d'], ['d', 'c', 'd']]
2256
"""
2257
if starting_vertices is None:
2258
starting_vertices = [vertex]
2259
# First enumerate the empty cycle
2260
if trivial:
2261
yield [vertex]
2262
# First we remove vertices and edges that are not part of any cycle
2263
if remove_acyclic_edges:
2264
sccs = self.strongly_connected_components()
2265
d = {}
2266
for id, component in enumerate(sccs):
2267
for v in component:
2268
d[v] = id
2269
h = self.copy()
2270
h.delete_edges([(u,v) for (u,v) in h.edge_iterator(labels=False) if d[u] != d[v]])
2271
else:
2272
h = self
2273
queue = [[vertex]]
2274
if max_length is None:
2275
from sage.rings.infinity import Infinity
2276
max_length = Infinity
2277
while queue:
2278
path = queue.pop(0)
2279
# Checks if a cycle has been found
2280
if len(path) > 1 and path[0] == path[-1]:
2281
yield path
2282
# Makes sure that the current cycle is not too long
2283
# Also if a cycle has been encountered and only simple cycles are allowed,
2284
# Then it discards the current path
2285
if len(path) <= max_length and (not simple or path.count(path[-1]) == 1):
2286
for neighbor in h.neighbor_out_iterator(path[-1]):
2287
# If cycles are not rooted, makes sure to keep only the minimum
2288
# cycle according to the lexicographic order
2289
if rooted or neighbor not in starting_vertices or path[0] <= neighbor:
2290
queue.append(path + [neighbor])
2291
2292
def all_cycles_iterator(self, starting_vertices=None, simple=False,
2293
rooted=False, max_length=None, trivial=False):
2294
r"""
2295
Returns an iterator over all the cycles of self starting
2296
with one of the given vertices. The cycles are enumerated
2297
in increasing length order.
2298
2299
INPUT:
2300
2301
- ``starting_vertices`` - iterable (default: None) on vertices
2302
from which the cycles must start. If None, then all
2303
vertices of the graph can be starting points.
2304
- ``simple`` - boolean (default: False). If set to True,
2305
then only simple cycles are considered. A cycle is simple
2306
if the only vertex occuring twice in it is the starting
2307
and ending one.
2308
- ``rooted`` - boolean (default: False). If set to False,
2309
then cycles differing only by their starting vertex are
2310
considered the same (e.g. ``['a', 'b', 'c', 'a']`` and
2311
``['b', 'c', 'a', 'b']``). Otherwise, all cycles are enumerated.
2312
- ``max_length`` - non negative integer (default: None).
2313
The maximum length of the enumerated cycles. If set to None,
2314
then all lengths are allowed.
2315
- ``trivial`` - boolean (default: False). If set to True,
2316
then the empty cycles are also enumerated.
2317
2318
OUTPUT:
2319
2320
iterator
2321
2322
.. NOTE::
2323
2324
See also :meth:`all_simple_cycles`.
2325
2326
AUTHOR:
2327
2328
Alexandre Blondin Masse
2329
2330
EXAMPLES::
2331
2332
sage: g = DiGraph({'a' : ['a', 'b'], 'b' : ['c'], 'c' : ['d'], 'd' : ['c']}, loops=True)
2333
sage: it = g.all_cycles_iterator()
2334
sage: for _ in range(7): print it.next()
2335
['a', 'a']
2336
['a', 'a', 'a']
2337
['c', 'd', 'c']
2338
['a', 'a', 'a', 'a']
2339
['a', 'a', 'a', 'a', 'a']
2340
['c', 'd', 'c', 'd', 'c']
2341
['a', 'a', 'a', 'a', 'a', 'a']
2342
2343
There are no cycles in the empty graph and in acyclic graphs::
2344
2345
sage: g = DiGraph()
2346
sage: it = g.all_cycles_iterator()
2347
sage: list(it)
2348
[]
2349
sage: g = DiGraph({0:[1]})
2350
sage: it = g.all_cycles_iterator()
2351
sage: list(it)
2352
[]
2353
2354
It is possible to restrict the starting vertices of the cycles::
2355
2356
sage: g = DiGraph({'a' : ['a', 'b'], 'b' : ['c'], 'c' : ['d'], 'd' : ['c']}, loops=True)
2357
sage: it = g.all_cycles_iterator(starting_vertices=['b', 'c'])
2358
sage: for _ in range(3): print it.next()
2359
['c', 'd', 'c']
2360
['c', 'd', 'c', 'd', 'c']
2361
['c', 'd', 'c', 'd', 'c', 'd', 'c']
2362
2363
Also, one can bound the length of the cycles::
2364
2365
sage: it = g.all_cycles_iterator(max_length=3)
2366
sage: list(it)
2367
[['a', 'a'], ['a', 'a', 'a'], ['c', 'd', 'c'],
2368
['a', 'a', 'a', 'a']]
2369
2370
By default, cycles differing only by their starting point are not all
2371
enumerated, but this may be parametrized::
2372
2373
sage: it = g.all_cycles_iterator(max_length=3, rooted=False)
2374
sage: list(it)
2375
[['a', 'a'], ['a', 'a', 'a'], ['c', 'd', 'c'],
2376
['a', 'a', 'a', 'a']]
2377
sage: it = g.all_cycles_iterator(max_length=3, rooted=True)
2378
sage: list(it)
2379
[['a', 'a'], ['a', 'a', 'a'], ['c', 'd', 'c'], ['d', 'c', 'd'],
2380
['a', 'a', 'a', 'a']]
2381
2382
One may prefer to enumerate simple cycles, i.e. cycles such that the only
2383
vertex occuring twice in it is the starting and ending one (see also
2384
:meth:`all_simple_cycles`)::
2385
2386
sage: it = g.all_cycles_iterator(simple=True)
2387
sage: list(it)
2388
[['a', 'a'], ['c', 'd', 'c']]
2389
sage: g = digraphs.Circuit(4)
2390
sage: list(g.all_cycles_iterator(simple=True))
2391
[[0, 1, 2, 3, 0]]
2392
"""
2393
if starting_vertices is None:
2394
starting_vertices = self
2395
# Since a cycle is always included in a given strongly connected
2396
# component, we may remove edges from the graph
2397
sccs = self.strongly_connected_components()
2398
d = {}
2399
for id, component in enumerate(sccs):
2400
for v in component:
2401
d[v] = id
2402
h = self.copy()
2403
h.delete_edges([ (u,v) for (u,v) in h.edge_iterator(labels=False)
2404
if d[u] != d[v] ])
2405
# We create one cycles iterator per vertex. This is necessary if we
2406
# want to iterate over cycles with increasing length.
2407
vertex_iterators = dict([(v, h._all_cycles_iterator_vertex( v
2408
, starting_vertices=starting_vertices
2409
, simple=simple
2410
, rooted=rooted
2411
, max_length=max_length
2412
, trivial=trivial
2413
, remove_acyclic_edges=False
2414
)) for v in starting_vertices])
2415
cycles = []
2416
for vi in vertex_iterators.values():
2417
try:
2418
cycle = vi.next()
2419
cycles.append((len(cycle), cycle))
2420
except(StopIteration):
2421
pass
2422
# Since we always extract a shortest path, using a heap
2423
# can speed up the algorithm
2424
from heapq import heapify, heappop, heappush
2425
heapify(cycles)
2426
while cycles:
2427
# We choose the shortest available cycle
2428
_, shortest_cycle = heappop(cycles)
2429
yield shortest_cycle
2430
# We update the cycle iterator to its next available cycle if it
2431
# exists
2432
try:
2433
cycle = vertex_iterators[shortest_cycle[0]].next()
2434
heappush(cycles, (len(cycle), cycle))
2435
except(StopIteration):
2436
pass
2437
2438
def all_simple_cycles(self, starting_vertices=None, rooted=False,
2439
max_length=None, trivial=False):
2440
r"""
2441
Returns a list of all simple cycles of self.
2442
2443
INPUT:
2444
2445
- ``starting_vertices`` - iterable (default: None) on vertices
2446
from which the cycles must start. If None, then all
2447
vertices of the graph can be starting points.
2448
- ``rooted`` - boolean (default: False). If set to False,
2449
then equivalent cycles are merged into one single cycle
2450
(the one starting with minimum vertex).
2451
Two cycles are called equivalent if they differ only from
2452
their starting vertex (e.g. ``['a', 'b', 'c', 'a']`` and
2453
``['b', 'c', 'a', 'b']``). Otherwise, all cycles are enumerated.
2454
- ``max_length`` - non negative integer (default: None).
2455
The maximum length of the enumerated cycles. If set to None,
2456
then all lengths are allowed.
2457
- ``trivial`` - boolean (default: False). If set to True,
2458
then the empty cycles are also enumerated.
2459
2460
OUTPUT:
2461
2462
list
2463
2464
.. NOTE::
2465
2466
Although the number of simple cycles of a finite graph is
2467
always finite, computing all its cycles may take a very long
2468
time.
2469
2470
EXAMPLES::
2471
2472
sage: g = DiGraph({'a' : ['a', 'b'], 'b' : ['c'], 'c' : ['d'], 'd' : ['c']}, loops=True)
2473
sage: g.all_simple_cycles()
2474
[['a', 'a'], ['c', 'd', 'c']]
2475
2476
The directed version of the Petersen graph::
2477
2478
sage: g = graphs.PetersenGraph().to_directed()
2479
sage: g.all_simple_cycles(max_length=4)
2480
[[0, 1, 0], [0, 4, 0], [0, 5, 0], [1, 2, 1], [1, 6, 1], [2, 3, 2],
2481
[2, 7, 2], [3, 8, 3], [3, 4, 3], [4, 9, 4], [5, 8, 5], [5, 7, 5],
2482
[6, 8, 6], [6, 9, 6], [7, 9, 7]]
2483
sage: g.all_simple_cycles(max_length=6)
2484
[[0, 1, 0], [0, 4, 0], [0, 5, 0], [1, 2, 1], [1, 6, 1], [2, 3, 2],
2485
[2, 7, 2], [3, 8, 3], [3, 4, 3], [4, 9, 4], [5, 8, 5], [5, 7, 5],
2486
[6, 8, 6], [6, 9, 6], [7, 9, 7], [0, 1, 2, 3, 4, 0],
2487
[0, 1, 2, 7, 5, 0], [0, 1, 6, 8, 5, 0], [0, 1, 6, 9, 4, 0],
2488
[0, 4, 9, 6, 1, 0], [0, 4, 9, 7, 5, 0], [0, 4, 3, 8, 5, 0],
2489
[0, 4, 3, 2, 1, 0], [0, 5, 8, 3, 4, 0], [0, 5, 8, 6, 1, 0],
2490
[0, 5, 7, 9, 4, 0], [0, 5, 7, 2, 1, 0], [1, 2, 3, 8, 6, 1],
2491
[1, 2, 7, 9, 6, 1], [1, 6, 8, 3, 2, 1], [1, 6, 9, 7, 2, 1],
2492
[2, 3, 8, 5, 7, 2], [2, 3, 4, 9, 7, 2], [2, 7, 9, 4, 3, 2],
2493
[2, 7, 5, 8, 3, 2], [3, 8, 6, 9, 4, 3], [3, 4, 9, 6, 8, 3],
2494
[5, 8, 6, 9, 7, 5], [5, 7, 9, 6, 8, 5], [0, 1, 2, 3, 8, 5, 0],
2495
[0, 1, 2, 7, 9, 4, 0], [0, 1, 6, 8, 3, 4, 0],
2496
[0, 1, 6, 9, 7, 5, 0], [0, 4, 9, 6, 8, 5, 0],
2497
[0, 4, 9, 7, 2, 1, 0], [0, 4, 3, 8, 6, 1, 0],
2498
[0, 4, 3, 2, 7, 5, 0], [0, 5, 8, 3, 2, 1, 0],
2499
[0, 5, 8, 6, 9, 4, 0], [0, 5, 7, 9, 6, 1, 0],
2500
[0, 5, 7, 2, 3, 4, 0], [1, 2, 3, 4, 9, 6, 1],
2501
[1, 2, 7, 5, 8, 6, 1], [1, 6, 8, 5, 7, 2, 1],
2502
[1, 6, 9, 4, 3, 2, 1], [2, 3, 8, 6, 9, 7, 2],
2503
[2, 7, 9, 6, 8, 3, 2], [3, 8, 5, 7, 9, 4, 3],
2504
[3, 4, 9, 7, 5, 8, 3]]
2505
2506
The complete graph (without loops) on `4` vertices::
2507
2508
sage: g = graphs.CompleteGraph(4).to_directed()
2509
sage: g.all_simple_cycles()
2510
[[0, 1, 0], [0, 2, 0], [0, 3, 0], [1, 2, 1], [1, 3, 1], [2, 3, 2],
2511
[0, 1, 2, 0], [0, 1, 3, 0], [0, 2, 1, 0], [0, 2, 3, 0],
2512
[0, 3, 1, 0], [0, 3, 2, 0], [1, 2, 3, 1], [1, 3, 2, 1],
2513
[0, 1, 2, 3, 0], [0, 1, 3, 2, 0], [0, 2, 1, 3, 0],
2514
[0, 2, 3, 1, 0], [0, 3, 1, 2, 0], [0, 3, 2, 1, 0]]
2515
2516
If the graph contains a large number of cycles, one can bound
2517
the length of the cycles, or simply restrict the possible
2518
starting vertices of the cycles::
2519
2520
sage: g = graphs.CompleteGraph(20).to_directed()
2521
sage: g.all_simple_cycles(max_length=2)
2522
[[0, 1, 0], [0, 2, 0], [0, 3, 0], [0, 4, 0], [0, 5, 0], [0, 6, 0],
2523
[0, 7, 0], [0, 8, 0], [0, 9, 0], [0, 10, 0], [0, 11, 0],
2524
[0, 12, 0], [0, 13, 0], [0, 14, 0], [0, 15, 0], [0, 16, 0],
2525
[0, 17, 0], [0, 18, 0], [0, 19, 0], [1, 2, 1], [1, 3, 1],
2526
[1, 4, 1], [1, 5, 1], [1, 6, 1], [1, 7, 1], [1, 8, 1], [1, 9, 1],
2527
[1, 10, 1], [1, 11, 1], [1, 12, 1], [1, 13, 1], [1, 14, 1],
2528
[1, 15, 1], [1, 16, 1], [1, 17, 1], [1, 18, 1], [1, 19, 1],
2529
[2, 3, 2], [2, 4, 2], [2, 5, 2], [2, 6, 2], [2, 7, 2], [2, 8, 2],
2530
[2, 9, 2], [2, 10, 2], [2, 11, 2], [2, 12, 2], [2, 13, 2],
2531
[2, 14, 2], [2, 15, 2], [2, 16, 2], [2, 17, 2], [2, 18, 2],
2532
[2, 19, 2], [3, 4, 3], [3, 5, 3], [3, 6, 3], [3, 7, 3], [3, 8, 3],
2533
[3, 9, 3], [3, 10, 3], [3, 11, 3], [3, 12, 3], [3, 13, 3],
2534
[3, 14, 3], [3, 15, 3], [3, 16, 3], [3, 17, 3], [3, 18, 3],
2535
[3, 19, 3], [4, 5, 4], [4, 6, 4], [4, 7, 4], [4, 8, 4], [4, 9, 4],
2536
[4, 10, 4], [4, 11, 4], [4, 12, 4], [4, 13, 4], [4, 14, 4],
2537
[4, 15, 4], [4, 16, 4], [4, 17, 4], [4, 18, 4], [4, 19, 4],
2538
[5, 6, 5], [5, 7, 5], [5, 8, 5], [5, 9, 5], [5, 10, 5],
2539
[5, 11, 5], [5, 12, 5], [5, 13, 5], [5, 14, 5], [5, 15, 5],
2540
[5, 16, 5], [5, 17, 5], [5, 18, 5], [5, 19, 5], [6, 7, 6],
2541
[6, 8, 6], [6, 9, 6], [6, 10, 6], [6, 11, 6], [6, 12, 6],
2542
[6, 13, 6], [6, 14, 6], [6, 15, 6], [6, 16, 6], [6, 17, 6],
2543
[6, 18, 6], [6, 19, 6], [7, 8, 7], [7, 9, 7], [7, 10, 7],
2544
[7, 11, 7], [7, 12, 7], [7, 13, 7], [7, 14, 7], [7, 15, 7],
2545
[7, 16, 7], [7, 17, 7], [7, 18, 7], [7, 19, 7], [8, 9, 8],
2546
[8, 10, 8], [8, 11, 8], [8, 12, 8], [8, 13, 8], [8, 14, 8],
2547
[8, 15, 8], [8, 16, 8], [8, 17, 8], [8, 18, 8], [8, 19, 8],
2548
[9, 10, 9], [9, 11, 9], [9, 12, 9], [9, 13, 9], [9, 14, 9],
2549
[9, 15, 9], [9, 16, 9], [9, 17, 9], [9, 18, 9], [9, 19, 9],
2550
[10, 11, 10], [10, 12, 10], [10, 13, 10], [10, 14, 10],
2551
[10, 15, 10], [10, 16, 10], [10, 17, 10], [10, 18, 10],
2552
[10, 19, 10], [11, 12, 11], [11, 13, 11], [11, 14, 11],
2553
[11, 15, 11], [11, 16, 11], [11, 17, 11], [11, 18, 11],
2554
[11, 19, 11], [12, 13, 12], [12, 14, 12], [12, 15, 12],
2555
[12, 16, 12], [12, 17, 12], [12, 18, 12], [12, 19, 12],
2556
[13, 14, 13], [13, 15, 13], [13, 16, 13], [13, 17, 13],
2557
[13, 18, 13], [13, 19, 13], [14, 15, 14], [14, 16, 14],
2558
[14, 17, 14], [14, 18, 14], [14, 19, 14], [15, 16, 15],
2559
[15, 17, 15], [15, 18, 15], [15, 19, 15], [16, 17, 16],
2560
[16, 18, 16], [16, 19, 16], [17, 18, 17], [17, 19, 17],
2561
[18, 19, 18]]
2562
sage: g = graphs.CompleteGraph(20).to_directed()
2563
sage: g.all_simple_cycles(max_length=2, starting_vertices=[0])
2564
[[0, 1, 0], [0, 2, 0], [0, 3, 0], [0, 4, 0], [0, 5, 0], [0, 6, 0],
2565
[0, 7, 0], [0, 8, 0], [0, 9, 0], [0, 10, 0], [0, 11, 0],
2566
[0, 12, 0], [0, 13, 0], [0, 14, 0], [0, 15, 0], [0, 16, 0],
2567
[0, 17, 0], [0, 18, 0], [0, 19, 0]]
2568
2569
One may prefer to distinguish equivalent cycles having distinct
2570
starting vertices (compare the following examples)::
2571
2572
sage: g = graphs.CompleteGraph(4).to_directed()
2573
sage: g.all_simple_cycles(max_length=2, rooted=False)
2574
[[0, 1, 0], [0, 2, 0], [0, 3, 0], [1, 2, 1], [1, 3, 1], [2, 3, 2]]
2575
sage: g.all_simple_cycles(max_length=2, rooted=True)
2576
[[0, 1, 0], [0, 2, 0], [0, 3, 0], [1, 0, 1], [1, 2, 1], [1, 3, 1],
2577
[2, 0, 2], [2, 1, 2], [2, 3, 2], [3, 0, 3], [3, 1, 3], [3, 2, 3]]
2578
"""
2579
return list(self.all_cycles_iterator(starting_vertices=starting_vertices, simple=True, rooted=rooted, max_length=max_length, trivial=trivial))
2580
2581
### Directed Acyclic Graphs (DAGs)
2582
2583
def topological_sort(self, implementation = "default"):
2584
"""
2585
Returns a topological sort of the digraph if it is acyclic, and raises a
2586
TypeError if the digraph contains a directed cycle.
2587
2588
A topological sort is an ordering of the vertices of the digraph such
2589
that each vertex comes before all of its successors. That is, if `u`
2590
comes before `v` in the sort, then there may be a directed path from `u`
2591
to `v`, but there will be no directed path from `v` to `u`.
2592
2593
INPUT:
2594
2595
- ``implementation`` -- Use the default Cython implementation
2596
(``implementation = default``) or the NetworkX library
2597
(``implementation = "NetworkX"``)
2598
2599
.. SEEALSO::
2600
2601
- :meth:`is_directed_acyclic` -- Tests whether a directed graph is
2602
acyclic (can also join a certificate -- a topological sort or a
2603
circuit in the graph1).
2604
2605
EXAMPLES::
2606
2607
sage: D = DiGraph({ 0:[1,2,3], 4:[2,5], 1:[8], 2:[7], 3:[7], 5:[6,7], 7:[8], 6:[9], 8:[10], 9:[10] })
2608
sage: D.plot(layout='circular').show()
2609
sage: D.topological_sort()
2610
[4, 5, 6, 9, 0, 1, 2, 3, 7, 8, 10]
2611
2612
::
2613
2614
sage: D.add_edge(9,7)
2615
sage: D.topological_sort()
2616
[4, 5, 6, 9, 0, 1, 2, 3, 7, 8, 10]
2617
2618
Using the NetworkX implementation ::
2619
2620
sage: D.topological_sort(implementation = "NetworkX")
2621
[4, 5, 6, 9, 0, 1, 2, 3, 7, 8, 10]
2622
2623
::
2624
2625
sage: D.add_edge(7,4)
2626
sage: D.topological_sort()
2627
Traceback (most recent call last):
2628
...
2629
TypeError: Digraph is not acyclic-- there is no topological sort.
2630
2631
.. note::
2632
2633
There is a recursive version of this in NetworkX, but it has
2634
problems::
2635
2636
sage: import networkx
2637
sage: D = DiGraph({ 0:[1,2,3], 4:[2,5], 1:[8], 2:[7], 3:[7], 5:[6,7], 7:[8], 6:[9], 8:[10], 9:[10] })
2638
sage: N = D.networkx_graph()
2639
sage: networkx.topological_sort(N)
2640
[4, 5, 6, 9, 0, 1, 2, 3, 7, 8, 10]
2641
sage: networkx.topological_sort_recursive(N) is None
2642
True
2643
2644
TESTS:
2645
2646
A wrong value for the ``implementation`` keyword::
2647
2648
sage: D.topological_sort(implementation = "cloud-reading")
2649
Traceback (most recent call last):
2650
...
2651
ValueError: implementation must be set to one of "default" or "NetworkX"
2652
"""
2653
2654
if implementation == "default":
2655
b, ordering = self._backend.is_directed_acyclic(certificate = True)
2656
if b:
2657
return ordering
2658
else:
2659
raise TypeError('Digraph is not acyclic-- there is no topological sort.')
2660
2661
elif implementation == "NetworkX":
2662
import networkx
2663
S = networkx.topological_sort(self.networkx_graph(copy=False))
2664
if S is None:
2665
raise TypeError('Digraph is not acyclic-- there is no topological sort.')
2666
else:
2667
return S
2668
2669
else:
2670
raise ValueError("implementation must be set to one of \"default\" or \"NetworkX\"")
2671
2672
def topological_sort_generator(self):
2673
"""
2674
Returns a list of all topological sorts of the digraph if it is
2675
acyclic, and raises a TypeError if the digraph contains a directed
2676
cycle.
2677
2678
A topological sort is an ordering of the vertices of the digraph
2679
such that each vertex comes before all of its successors. That is,
2680
if u comes before v in the sort, then there may be a directed path
2681
from u to v, but there will be no directed path from v to u. See
2682
also Graph.topological_sort().
2683
2684
AUTHORS:
2685
2686
- Mike Hansen - original implementation
2687
2688
- Robert L. Miller: wrapping, documentation
2689
2690
REFERENCE:
2691
2692
- [1] Pruesse, Gara and Ruskey, Frank. Generating Linear
2693
Extensions Fast. SIAM J. Comput., Vol. 23 (1994), no. 2, pp.
2694
373-386.
2695
2696
EXAMPLES::
2697
2698
sage: D = DiGraph({ 0:[1,2], 1:[3], 2:[3,4] })
2699
sage: D.plot(layout='circular').show()
2700
sage: D.topological_sort_generator()
2701
[[0, 1, 2, 3, 4], [0, 1, 2, 4, 3], [0, 2, 1, 3, 4], [0, 2, 1, 4, 3], [0, 2, 4, 1, 3]]
2702
2703
::
2704
2705
sage: for sort in D.topological_sort_generator():
2706
... for edge in D.edge_iterator():
2707
... u,v,l = edge
2708
... if sort.index(u) > sort.index(v):
2709
... print "This should never happen."
2710
"""
2711
from sage.graphs.linearextensions import LinearExtensions
2712
try:
2713
return LinearExtensions(self).list()
2714
except TypeError:
2715
raise TypeError('Digraph is not acyclic-- there is no topological sort (or there was an error in sage/graphs/linearextensions.py).')
2716
2717
### Visualization
2718
2719
def layout_acyclic(self, **options):
2720
"""
2721
Computes a ranked layout so that all edges point upward.
2722
2723
To this end, the heights of the vertices are set according to the level
2724
set decomposition of the graph (see :meth:`.level_sets`).
2725
2726
This is achieved by calling ``graphviz`` and ``dot2tex`` if available
2727
(see :meth:`.layout_graphviz`), and using a random horizontal
2728
placement of the vertices otherwise (see :meth:`.layout_acyclic_dummy`).
2729
2730
Non acyclic graphs are partially supported by ``graphviz``, which then
2731
chooses some edges to point down.
2732
2733
EXAMPLES::
2734
2735
sage: H = DiGraph({0:[1,2],1:[3],2:[3],3:[],5:[1,6],6:[2,3]})
2736
2737
The actual layout computed will depend on whether dot2tex and
2738
graphviz are installed, so we don't test it.
2739
2740
sage: H.layout_acyclic()
2741
{0: [..., ...], 1: [..., ...], 2: [..., ...], 3: [..., ...], 5: [..., ...], 6: [..., ...]}
2742
2743
"""
2744
if have_dot2tex():
2745
return self.layout_graphviz(**options)
2746
else:
2747
return self.layout_acyclic_dummy(**options)
2748
2749
def layout_acyclic_dummy(self, heights = None, **options):
2750
"""
2751
Computes a (dummy) ranked layout of an acyclic graph so that
2752
all edges point upward. To this end, the heights of the
2753
vertices are set according to the level set decomposition of
2754
the graph (see :meth:`.level_sets`).
2755
2756
EXAMPLES::
2757
2758
sage: H = DiGraph({0:[1,2],1:[3],2:[3],3:[],5:[1,6],6:[2,3]})
2759
sage: H.layout_acyclic_dummy()
2760
{0: [1.00..., 0], 1: [1.00..., 1], 2: [1.51..., 2], 3: [1.50..., 3], 5: [2.01..., 0], 6: [2.00..., 1]}
2761
2762
sage: H = DiGraph({0:[1,2],1:[3],2:[3],3:[1],5:[1,6],6:[2,3]})
2763
sage: H.layout_acyclic_dummy()
2764
Traceback (most recent call last):
2765
...
2766
AssertionError: `self` should be an acyclic graph
2767
"""
2768
if heights is None:
2769
assert self.is_directed_acyclic(), "`self` should be an acyclic graph"
2770
levels = self.level_sets()
2771
levels = [sorted(z) for z in levels]
2772
heights = dict([[i, levels[i]] for i in range(len(levels))])
2773
return self.layout_ranked(heights = heights, **options)
2774
2775
def level_sets(self):
2776
"""
2777
Returns the level set decomposition of the digraph.
2778
2779
OUTPUT:
2780
2781
- a list of non empty lists of vertices of this graph
2782
2783
The level set decomposition of the digraph is a list `l` such that the
2784
level `l[i]` contains all the vertices having all their predecessors in
2785
the levels `l[j]` for `j<i`, and at least one in level `l[i-1]` (unless
2786
`i=0`).
2787
2788
The level decomposition contains exactly the vertices not occuring in
2789
any cycle of the graph. In particular, the graph is acyclic if and only
2790
if the decomposition forms a set partition of its vertices, and we
2791
recover the usual level set decomposition of the corresponding poset.
2792
2793
EXAMPLES::
2794
2795
sage: H = DiGraph({0:[1,2],1:[3],2:[3],3:[],5:[1,6],6:[2,3]})
2796
sage: H.level_sets()
2797
[[0, 5], [1, 6], [2], [3]]
2798
2799
sage: H = DiGraph({0:[1,2],1:[3],2:[3],3:[1],5:[1,6],6:[2,3]})
2800
sage: H.level_sets()
2801
[[0, 5], [6], [2]]
2802
2803
This routine is mostly used for Hasse diagrams of posets::
2804
2805
sage: from sage.combinat.posets.hasse_diagram import HasseDiagram
2806
sage: H = HasseDiagram({0:[1,2],1:[3],2:[3],3:[]})
2807
sage: [len(x) for x in H.level_sets()]
2808
[1, 2, 1]
2809
2810
::
2811
2812
sage: from sage.combinat.posets.hasse_diagram import HasseDiagram
2813
sage: H = HasseDiagram({0:[1,2], 1:[3], 2:[4], 3:[4]})
2814
sage: [len(x) for x in H.level_sets()]
2815
[1, 2, 1, 1]
2816
2817
Complexity: `O(n+m)` in time and `O(n)` in memory (besides the
2818
storage of the graph itself), where `n` and `m` are
2819
respectively the number of vertices and edges (assuming that
2820
appending to a list is constant time, which it is not quite).
2821
"""
2822
in_degrees = self.in_degree(labels=True)
2823
level = [x for x in in_degrees if in_degrees[x]==0]
2824
Levels = []
2825
while len(level) != 0:
2826
Levels.append(level)
2827
new_level = []
2828
for x in level:
2829
for y in self.neighbors_out(x):
2830
in_degrees[y] -= 1
2831
if in_degrees[y] == 0:
2832
new_level.append(y)
2833
level = new_level
2834
return Levels
2835
2836
def strongly_connected_components(self):
2837
"""
2838
Returns the list of strongly connected components.
2839
2840
EXAMPLES::
2841
2842
sage: D = DiGraph( { 0 : [1, 3], 1 : [2], 2 : [3], 4 : [5, 6], 5 : [6] } )
2843
sage: D.connected_components()
2844
[[0, 1, 2, 3], [4, 5, 6]]
2845
sage: D = DiGraph( { 0 : [1, 3], 1 : [2], 2 : [3], 4 : [5, 6], 5 : [6] } )
2846
sage: D.strongly_connected_components()
2847
[[0], [1], [2], [3], [4], [5], [6]]
2848
sage: D.add_edge([2,0])
2849
sage: D.strongly_connected_components()
2850
[[0, 1, 2], [3], [4], [5], [6]]
2851
2852
TESTS:
2853
2854
Checking against NetworkX, and another of Sage's implementations::
2855
2856
sage: from sage.graphs.base.static_sparse_graph import strongly_connected_components
2857
sage: import networkx
2858
sage: for i in range(100): # long
2859
... g = digraphs.RandomDirectedGNP(100,.05) # long
2860
... h = g.networkx_graph() # long
2861
... scc1 = g.strongly_connected_components() # long
2862
... scc2 = networkx.strongly_connected_components(h) # long
2863
... scc3 = strongly_connected_components(g) # long
2864
... s1 = Set(map(Set,scc1)) # long
2865
... s2 = Set(map(Set,scc2)) # long
2866
... s3 = Set(map(Set,scc3)) # long
2867
... if s1 != s2: # long
2868
... print "Ooch !" # long
2869
... if s1 != s3: # long
2870
... print "Oooooch !" # long
2871
2872
"""
2873
2874
try:
2875
vertices = set(self.vertices())
2876
scc = []
2877
while vertices:
2878
tmp = self.strongly_connected_component_containing_vertex(vertices.__iter__().next())
2879
vertices.difference_update(set(tmp))
2880
scc.append(tmp)
2881
return scc
2882
2883
except AttributeError:
2884
import networkx
2885
return networkx.strongly_connected_components(self.networkx_graph(copy=False))
2886
2887
2888
def strongly_connected_component_containing_vertex(self, v):
2889
"""
2890
Returns the strongly connected component containing a given vertex
2891
2892
INPUT:
2893
2894
- ``v`` -- a vertex
2895
2896
EXAMPLE:
2897
2898
In the symmetric digraph of a graph, the strongly connected components are the connected
2899
components::
2900
2901
sage: g = graphs.PetersenGraph()
2902
sage: d = DiGraph(g)
2903
sage: d.strongly_connected_component_containing_vertex(0)
2904
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2905
"""
2906
2907
if self.order()==1:
2908
return [v]
2909
2910
try:
2911
return self._backend.strongly_connected_component_containing_vertex(v)
2912
2913
except AttributeError:
2914
raise AttributeError("This function is only defined for C graphs.")
2915
2916
def strongly_connected_components_subgraphs(self):
2917
r"""
2918
Returns the strongly connected components as a list of subgraphs.
2919
2920
EXAMPLE:
2921
2922
In the symmetric digraph of a graph, the strongly connected components are the connected
2923
components::
2924
2925
sage: g = graphs.PetersenGraph()
2926
sage: d = DiGraph(g)
2927
sage: d.strongly_connected_components_subgraphs()
2928
[Subgraph of (Petersen graph): Digraph on 10 vertices]
2929
2930
"""
2931
return map(self.subgraph, self.strongly_connected_components())
2932
2933
2934
def strongly_connected_components_digraph(self, keep_labels = False):
2935
r"""
2936
Returns the digraph of the strongly connected components
2937
2938
INPUT:
2939
2940
- ``keep_labels`` -- boolean (default: False)
2941
2942
The digraph of the strongly connected components of a graph `G` has
2943
a vertex per strongly connected component included in `G`. There
2944
is an edge from a component `C_1` to a component `C_2` if there is
2945
an edge from one to the other in `G`.
2946
2947
EXAMPLE:
2948
2949
Such a digraph is always acyclic ::
2950
2951
sage: g = digraphs.RandomDirectedGNP(15,.1)
2952
sage: scc_digraph = g.strongly_connected_components_digraph()
2953
sage: scc_digraph.is_directed_acyclic()
2954
True
2955
2956
The vertices of the digraph of strongly connected components are
2957
exactly the strongly connected components::
2958
2959
sage: g = digraphs.ButterflyGraph(2)
2960
sage: scc_digraph = g.strongly_connected_components_digraph()
2961
sage: g.is_directed_acyclic()
2962
True
2963
sage: all([ Set(scc) in scc_digraph.vertices() for scc in g.strongly_connected_components()])
2964
True
2965
2966
The following digraph has three strongly connected components,
2967
and the digraph of those is a chain::
2968
2969
sage: g = DiGraph({0:{1:"01", 2: "02", 3: 03}, 1: {2: "12"}, 2:{1: "21", 3: "23"}})
2970
sage: scc_digraph = g.strongly_connected_components_digraph()
2971
sage: scc_digraph.vertices()
2972
[{0}, {3}, {1, 2}]
2973
sage: scc_digraph.edges()
2974
[({0}, {3}, None), ({0}, {1, 2}, None), ({1, 2}, {3}, None)]
2975
2976
By default, the labels are discarded, and the result has no
2977
loops nor multiple edges. If ``keep_labels`` is ``True``, then
2978
the labels are kept, and the result is a multi digraph,
2979
possibly with multiple edges and loops. However, edges in the
2980
result with same source, target, and label are not duplicated
2981
(see the edges from 0 to the strongly connected component
2982
`\{1,2\}` below)::
2983
2984
sage: g = DiGraph({0:{1:"0-12", 2: "0-12", 3: "0-3"}, 1: {2: "1-2", 3: "1-3"}, 2:{1: "2-1", 3: "2-3"}})
2985
sage: scc_digraph = g.strongly_connected_components_digraph(keep_labels = True)
2986
sage: scc_digraph.vertices()
2987
[{0}, {3}, {1, 2}]
2988
sage: scc_digraph.edges()
2989
[({0}, {3}, '0-3'), ({0}, {1, 2}, '0-12'),
2990
({1, 2}, {3}, '1-3'), ({1, 2}, {3}, '2-3'),
2991
({1, 2}, {1, 2}, '1-2'), ({1, 2}, {1, 2}, '2-1')]
2992
2993
"""
2994
2995
from sage.sets.set import Set
2996
2997
scc = self.strongly_connected_components()
2998
scc_set = map(Set,scc)
2999
3000
d = {}
3001
for i,c in enumerate(scc):
3002
for v in c:
3003
d[v] = i
3004
3005
if keep_labels:
3006
g = DiGraph(multiedges=True, loops=True)
3007
g.add_vertices(range(len(scc)))
3008
3009
g.add_edges( set((d[u], d[v], label) for (u,v,label) in self.edges() ) )
3010
g.relabel(scc_set, inplace = True)
3011
3012
else:
3013
g = DiGraph(multiedges=False, loops=False)
3014
g.add_vertices(range(len(scc)))
3015
3016
for u,v in self.edges(labels=False):
3017
g.add_edge(d[u], d[v])
3018
3019
g.relabel(scc_set, inplace = True)
3020
3021
return g
3022
3023
def is_strongly_connected(self):
3024
r"""
3025
Returns whether the current ``DiGraph`` is strongly connected.
3026
3027
EXAMPLE:
3028
3029
The circuit is obviously strongly connected ::
3030
3031
sage: g = digraphs.Circuit(5)
3032
sage: g.is_strongly_connected()
3033
True
3034
3035
But a transitive triangle is not::
3036
3037
sage: g = DiGraph({ 0 : [1,2], 1 : [2]})
3038
sage: g.is_strongly_connected()
3039
False
3040
"""
3041
if self.order()==1:
3042
return True
3043
3044
try:
3045
return self._backend.is_strongly_connected()
3046
3047
except AttributeError:
3048
return len(self.strongly_connected_components()) == 1
3049
3050
3051
3052
3053