Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/graphs/digraph_generators.py
4036 views
1
r"""
2
Common Digraphs
3
4
Generators for common digraphs.
5
6
AUTHORS:
7
8
- Robert L. Miller (2006)
9
- Emily A. Kirkman (2006)
10
- Michael C. Yurko (2009)
11
- David Coudert (2012)
12
"""
13
14
################################################################################
15
# Copyright (C) 2006 Robert L. Miller <[email protected]>
16
# and Emily A. Kirkman
17
# Copyright (C) 2009 Michael C. Yurko <[email protected]>
18
#
19
# Distributed under the terms of the GNU General Public License (GPL)
20
# http://www.gnu.org/licenses/
21
################################################################################
22
23
from math import sin, cos, pi
24
from sage.misc.randstate import current_randstate
25
from sage.graphs.digraph import DiGraph
26
27
28
class DiGraphGenerators():
29
r"""
30
A class consisting of constructors for several common digraphs,
31
including orderly generation of isomorphism class representatives.
32
33
A list of all graphs and graph structures in this database is
34
available via tab completion. Type "digraphs." and then hit tab to
35
see which graphs are available.
36
37
The docstrings include educational information about each named
38
digraph with the hopes that this class can be used as a reference.
39
40
The constructors currently in this class include::
41
42
Random Directed Graphs:
43
- RandomDirectedGN
44
- RandomDirectedGNC
45
- RandomDirectedGNP
46
- RandomDirectedGNM
47
- RandomDirectedGNR
48
49
Families of Graphs:
50
- DeBruijn
51
- GeneralizedDeBruijn
52
- Kautz
53
- ImaseItoh
54
55
56
57
ORDERLY GENERATION: digraphs(vertices, property=lambda x: True,
58
augment='edges', size=None)
59
60
Accesses the generator of isomorphism class representatives.
61
Iterates over distinct, exhaustive representatives.
62
63
INPUT:
64
65
66
- ``vertices`` - natural number
67
68
- ``property`` - any property to be tested on digraphs
69
before generation.
70
71
- ``augment`` - choices:
72
73
- ``'vertices'`` - augments by adding a vertex, and
74
edges incident to that vertex. In this case, all digraphs on up to
75
n=vertices are generated. If for any digraph G satisfying the
76
property, every subgraph, obtained from G by deleting one vertex
77
and only edges incident to that vertex, satisfies the property,
78
then this will generate all digraphs with that property. If this
79
does not hold, then all the digraphs generated will satisfy the
80
property, but there will be some missing.
81
82
- ``'edges'`` - augments a fixed number of vertices by
83
adding one edge In this case, all digraphs on exactly n=vertices
84
are generated. If for any graph G satisfying the property, every
85
subgraph, obtained from G by deleting one edge but not the vertices
86
incident to that edge, satisfies the property, then this will
87
generate all digraphs with that property. If this does not hold,
88
then all the digraphs generated will satisfy the property, but
89
there will be some missing.
90
91
- ``implementation`` - which underlying implementation to use (see DiGraph?)
92
93
- ``sparse`` - ignored if implementation is not ``c_graph``
94
95
EXAMPLES: Print digraphs on 2 or less vertices.
96
97
::
98
99
sage: for D in digraphs(2, augment='vertices'):
100
... print D
101
...
102
Digraph on 0 vertices
103
Digraph on 1 vertex
104
Digraph on 2 vertices
105
Digraph on 2 vertices
106
Digraph on 2 vertices
107
108
Note that we can also get digraphs with underlying Cython implementation::
109
110
sage: for D in digraphs(2, augment='vertices', implementation='c_graph'):
111
... print D
112
...
113
Digraph on 0 vertices
114
Digraph on 1 vertex
115
Digraph on 2 vertices
116
Digraph on 2 vertices
117
Digraph on 2 vertices
118
119
Print digraphs on 3 vertices.
120
121
::
122
123
sage: for D in digraphs(3):
124
... print D
125
Digraph on 3 vertices
126
Digraph on 3 vertices
127
...
128
Digraph on 3 vertices
129
Digraph on 3 vertices
130
131
Generate all digraphs with 4 vertices and 3 edges.
132
133
::
134
135
sage: L = digraphs(4, size=3)
136
sage: len(list(L))
137
13
138
139
Generate all digraphs with 4 vertices and up to 3 edges.
140
141
::
142
143
sage: L = list(digraphs(4, lambda G: G.size() <= 3))
144
sage: len(L)
145
20
146
sage: graphs_list.show_graphs(L) # long time
147
148
Generate all digraphs with degree at most 2, up to 5 vertices.
149
150
::
151
152
sage: property = lambda G: ( max([G.degree(v) for v in G] + [0]) <= 2 )
153
sage: L = list(digraphs(5, property, augment='vertices'))
154
sage: len(L)
155
75
156
157
Generate digraphs on the fly: (see http://oeis.org/classic/A000273)
158
159
::
160
161
sage: for i in range(0, 5):
162
... print len(list(digraphs(i)))
163
1
164
1
165
3
166
16
167
218
168
169
REFERENCE:
170
171
- Brendan D. McKay, Isomorph-Free Exhaustive generation. Journal
172
of Algorithms Volume 26, Issue 2, February 1998, pages 306-324.
173
"""
174
175
def ButterflyGraph(self, n, vertices='strings'):
176
"""
177
Returns a n-dimensional butterfly graph. The vertices consist of
178
pairs (v,i), where v is an n-dimensional tuple (vector) with binary
179
entries (or a string representation of such) and i is an integer in
180
[0..n]. A directed edge goes from (v,i) to (w,i+1) if v and w are
181
identical except for possibly v[i] != w[i].
182
183
A butterfly graph has `(2^n)(n+1)` vertices and
184
`n2^{n+1}` edges.
185
186
INPUT:
187
188
189
- ``vertices`` - 'strings' (default) or 'vectors',
190
specifying whether the vertices are zero-one strings or actually
191
tuples over GF(2).
192
193
194
EXAMPLES::
195
196
sage: digraphs.ButterflyGraph(2).edges(labels=False)
197
[(('00', 0), ('00', 1)),
198
(('00', 0), ('10', 1)),
199
(('00', 1), ('00', 2)),
200
(('00', 1), ('01', 2)),
201
(('01', 0), ('01', 1)),
202
(('01', 0), ('11', 1)),
203
(('01', 1), ('00', 2)),
204
(('01', 1), ('01', 2)),
205
(('10', 0), ('00', 1)),
206
(('10', 0), ('10', 1)),
207
(('10', 1), ('10', 2)),
208
(('10', 1), ('11', 2)),
209
(('11', 0), ('01', 1)),
210
(('11', 0), ('11', 1)),
211
(('11', 1), ('10', 2)),
212
(('11', 1), ('11', 2))]
213
sage: digraphs.ButterflyGraph(2,vertices='vectors').edges(labels=False)
214
[(((0, 0), 0), ((0, 0), 1)),
215
(((0, 0), 0), ((1, 0), 1)),
216
(((0, 0), 1), ((0, 0), 2)),
217
(((0, 0), 1), ((0, 1), 2)),
218
(((0, 1), 0), ((0, 1), 1)),
219
(((0, 1), 0), ((1, 1), 1)),
220
(((0, 1), 1), ((0, 0), 2)),
221
(((0, 1), 1), ((0, 1), 2)),
222
(((1, 0), 0), ((0, 0), 1)),
223
(((1, 0), 0), ((1, 0), 1)),
224
(((1, 0), 1), ((1, 0), 2)),
225
(((1, 0), 1), ((1, 1), 2)),
226
(((1, 1), 0), ((0, 1), 1)),
227
(((1, 1), 0), ((1, 1), 1)),
228
(((1, 1), 1), ((1, 0), 2)),
229
(((1, 1), 1), ((1, 1), 2))]
230
"""
231
# We could switch to Sage integers to handle arbitrary n.
232
if vertices=='strings':
233
if n>=31:
234
raise NotImplementedError, "vertices='strings' is only valid for n<=30."
235
from sage.graphs.generic_graph_pyx import binary
236
butterfly = {}
237
for v in xrange(2**n):
238
for i in range(n):
239
w = v
240
w ^= (1 << i) # push 1 to the left by i and xor with w
241
bv = binary(v)
242
bw = binary(w)
243
# pad and reverse the strings
244
padded_bv = ('0'*(n-len(bv))+bv)[::-1]
245
padded_bw = ('0'*(n-len(bw))+bw)[::-1]
246
butterfly[(padded_bv,i)]=[(padded_bv,i+1), (padded_bw,i+1)]
247
elif vertices=='vectors':
248
from sage.modules.free_module import VectorSpace
249
from sage.rings.finite_rings.constructor import FiniteField
250
from copy import copy
251
butterfly = {}
252
for v in VectorSpace(FiniteField(2),n):
253
for i in xrange(n):
254
w=copy(v)
255
w[i] += 1 # Flip the ith bit
256
# We must call tuple since vectors are mutable. To obtain
257
# a vector from the tuple t, just call vector(t).
258
butterfly[(tuple(v),i)]=[(tuple(v),i+1), (tuple(w),i+1)]
259
else:
260
raise NotImplementedError, "vertices must be 'strings' or 'vectors'."
261
return DiGraph(butterfly)
262
263
def Circuit(self,n):
264
r"""
265
Returns the circuit on `n` vertices
266
267
The circuit is an oriented ``CycleGraph``
268
269
EXAMPLE:
270
271
A circuit is the smallest strongly connected digraph::
272
273
sage: circuit = digraphs.Circuit(15)
274
sage: len(circuit.strongly_connected_components()) == 1
275
True
276
"""
277
if n<0:
278
raise ValueError("The number of vertices must be a positive integer.")
279
280
g = DiGraph()
281
g.name("Circuit on "+str(n)+" vertices")
282
283
if n==0:
284
return g
285
elif n == 1:
286
g.allow_loops(True)
287
g.add_edge(0,0)
288
return g
289
else:
290
g.add_edges([(i,i+1) for i in xrange(n-1)])
291
g.add_edge(n-1,0)
292
return g
293
294
def DeBruijn(self, k, n, vertices = 'strings'):
295
r"""
296
Returns the De Bruijn diraph with parameters `k,n`.
297
298
The De Bruijn digraph with parameters `k,n` is built upon a set of
299
vertices equal to the set of words of length `n` from a dictionary of
300
`k` letters.
301
302
In this digraph, there is an arc `w_1w_2` if `w_2` can be obtained from
303
`w_1` by removing the leftmost letter and adding a new letter at its
304
right end. For more information, see the
305
:wikipedia:`Wikipedia article on De Bruijn graph <De_Bruijn_graph>`.
306
307
INPUT:
308
309
- ``k`` -- Two possibilities for this parameter :
310
- An integer equal to the cardinality of the alphabet to use, that
311
is the degree of the digraph to be produced.
312
- An iterable object to be used as the set of letters. The degree
313
of the resulting digraph is the cardinality of the set of
314
letters.
315
316
- ``n`` -- An integer equal to the length of words in the De Bruijn
317
digraph when ``vertices == 'strings'``, and also to the diameter of
318
the digraph.
319
320
- ``vertices`` -- 'strings' (default) or 'integers', specifying whether
321
the vertices are words build upon an alphabet or integers.
322
323
EXAMPLES::
324
325
sage: db=digraphs.DeBruijn(2,2); db
326
De Bruijn digraph (k=2, n=2): Looped digraph on 4 vertices
327
sage: db.order()
328
4
329
sage: db.size()
330
8
331
332
TESTS::
333
334
sage: digraphs.DeBruijn(5,0)
335
De Bruijn digraph (k=5, n=0): Looped multi-digraph on 1 vertex
336
sage: digraphs.DeBruijn(0,0)
337
De Bruijn digraph (k=0, n=0): Looped multi-digraph on 0 vertices
338
"""
339
from sage.combinat.words.words import Words
340
from sage.rings.integer import Integer
341
342
W = Words(range(k) if isinstance(k, Integer) else k, n)
343
A = Words(range(k) if isinstance(k, Integer) else k, 1)
344
g = DiGraph(loops=True)
345
346
if vertices == 'strings':
347
if n == 0 :
348
g.allow_multiple_edges(True)
349
v = W[0]
350
for a in A:
351
g.add_edge(v.string_rep(), v.string_rep(), a.string_rep())
352
else:
353
for w in W:
354
ww = w[1:]
355
for a in A:
356
g.add_edge(w.string_rep(), (ww*a).string_rep(), a.string_rep())
357
else:
358
d = W.size_of_alphabet()
359
g = digraphs.GeneralizedDeBruijn(d**n, d)
360
361
g.name( "De Bruijn digraph (k=%s, n=%s)"%(k,n) )
362
return g
363
364
def GeneralizedDeBruijn(self, n, d):
365
r"""
366
Returns the generalized de Bruijn digraph of order `n` and degree `d`.
367
368
The generalized de Bruijn digraph has been defined in [RPK80]_
369
[RPK83]_. It has vertex set `V=\{0, 1,..., n-1\}` and there is an arc
370
from vertex `u \in V` to all vertices `v \in V` such that
371
`v \equiv (u*d + a) \mod{n}` with `0 \leq a < d`.
372
373
When `n = d^{D}`, the generalized de Bruijn digraph is isomorphic to the
374
de Bruijn digraph of degree `d` and diameter `D`.
375
376
INPUTS:
377
378
- ``n`` -- is the number of vertices of the digraph
379
380
- ``d`` -- is the degree of the digraph
381
382
EXAMPLE::
383
384
sage: GB = digraphs.GeneralizedDeBruijn(8, 2)
385
sage: GB.is_isomorphic(digraphs.DeBruijn(2, 3), certify = True)
386
(True, {0: '000', 1: '001', 2: '010', 3: '011', 4: '100', 5: '101', 6: '110', 7: '111'})
387
388
TESTS:
389
390
An exception is raised when the degree is less than one::
391
392
sage: G = digraphs.GeneralizedDeBruijn(2, 0)
393
Traceback (most recent call last):
394
...
395
ValueError: The generalized de Bruijn digraph is defined for degree at least one.
396
397
An exception is raised when the order of the graph is less than one::
398
399
sage: G = digraphs.GeneralizedDeBruijn(0, 2)
400
Traceback (most recent call last):
401
...
402
ValueError: The generalized de Bruijn digraph is defined for at least one vertex.
403
404
405
REFERENCES:
406
407
.. [RPK80] S. M. Reddy, D. K. Pradhan, and J. Kuhl. Directed graphs with
408
minimal diameter and maximal connectivity, School Eng., Oakland Univ.,
409
Rochester MI, Tech. Rep., July 1980.
410
411
.. [RPK83] S. Reddy, P. Raghavan, and J. Kuhl. A Class of Graphs for
412
Processor Interconnection. *IEEE International Conference on Parallel
413
Processing*, pages 154-157, Los Alamitos, Ca., USA, August 1983.
414
"""
415
if n < 1:
416
raise ValueError("The generalized de Bruijn digraph is defined for at least one vertex.")
417
if d < 1:
418
raise ValueError("The generalized de Bruijn digraph is defined for degree at least one.")
419
420
GB = DiGraph(loops = True)
421
GB.allow_multiple_edges(True)
422
for u in xrange(n):
423
for a in xrange(u*d, u*d+d):
424
GB.add_edge(u, a%n)
425
426
GB.name( "Generalized de Bruijn digraph (n=%s, d=%s)"%(n,d) )
427
return GB
428
429
430
def ImaseItoh(self, n, d):
431
r"""
432
Returns the digraph of Imase and Itoh of order `n` and degree `d`.
433
434
The digraph of Imase and Itoh has been defined in [II83]_. It has vertex
435
set `V=\{0, 1,..., n-1\}` and there is an arc from vertex `u \in V` to
436
all vertices `v \in V` such that `v \equiv (-u*d-a-1) \mod{n}` with
437
`0 \leq a < d`.
438
439
When `n = d^{D}`, the digraph of Imase and Itoh is isomorphic to the de
440
Bruijn digraph of degree `d` and diameter `D`. When `n = d^{D-1}(d+1)`,
441
the digraph of Imase and Itoh is isomorphic to the Kautz digraph
442
[Kautz68]_ of degree `d` and diameter `D`.
443
444
INPUTS:
445
446
- ``n`` -- is the number of vertices of the digraph
447
448
- ``d`` -- is the degree of the digraph
449
450
EXAMPLES::
451
452
sage: II = digraphs.ImaseItoh(8, 2)
453
sage: II.is_isomorphic(digraphs.DeBruijn(2, 3), certify = True)
454
(True, {0: '010', 1: '011', 2: '000', 3: '001', 4: '110', 5: '111', 6: '100', 7: '101'})
455
456
sage: II = digraphs.ImaseItoh(12, 2)
457
sage: II.is_isomorphic(digraphs.Kautz(2, 3), certify = True)
458
(True, {0: '010', 1: '012', 2: '021', 3: '020', 4: '202', 5: '201', 6: '210', 7: '212', 8: '121', 9: '120', 10: '102', 11: '101'})
459
460
461
TESTS:
462
463
An exception is raised when the degree is less than one::
464
465
sage: G = digraphs.ImaseItoh(2, 0)
466
Traceback (most recent call last):
467
...
468
ValueError: The digraph of Imase and Itoh is defined for degree at least one.
469
470
An exception is raised when the order of the graph is less than two::
471
472
sage: G = digraphs.ImaseItoh(1, 2)
473
Traceback (most recent call last):
474
...
475
ValueError: The digraph of Imase and Itoh is defined for at least two vertices.
476
477
478
REFERENCE:
479
480
.. [II83] M. Imase and M. Itoh. A design for directed graphs with
481
minimum diameter, *IEEE Trans. Comput.*, vol. C-32, pp. 782-784, 1983.
482
"""
483
if n < 2:
484
raise ValueError("The digraph of Imase and Itoh is defined for at least two vertices.")
485
if d < 1:
486
raise ValueError("The digraph of Imase and Itoh is defined for degree at least one.")
487
488
II = DiGraph(loops = True)
489
II.allow_multiple_edges(True)
490
for u in xrange(n):
491
for a in xrange(-u*d-d, -u*d):
492
II.add_edge(u, a % n)
493
494
II.name( "Imase and Itoh digraph (n=%s, d=%s)"%(n,d) )
495
return II
496
497
498
def Kautz(self, k, D, vertices = 'strings'):
499
r"""
500
Returns the Kautz digraph of degree `d` and diameter `D`.
501
502
The Kautz digraph has been defined in [Kautz68]_. The Kautz digraph of
503
degree `d` and diameter `D` has `d^{D-1}(d+1)` vertices. This digraph is
504
build upon a set of vertices equal to the set of words of length `D`
505
from an alphabet of `d+1` letters such that consecutive letters are
506
differents. There is an arc from vertex `u` to vertex `v` if `v` can be
507
obtained from `u` by removing the leftmost letter and adding a new
508
letter, distinct from the rightmost letter of `u`, at the right end.
509
510
The Kautz digraph of degree `d` and diameter `D` is isomorphic to the
511
digraph of Imase and Itoh [II83]_ of degree `d` and order
512
`d^{D-1}(d+1)`.
513
514
See also the
515
:wikipedia:`Wikipedia article on Kautz Graphs <Kautz_graph>`.
516
517
INPUTS:
518
519
- ``k`` -- Two possibilities for this parameter :
520
- An integer equal to the degree of the digraph to be produced, that
521
is the cardinality minus one of the alphabet to use.
522
- An iterable object to be used as the set of letters. The degree of
523
the resulting digraph is the cardinality of the set of letters
524
minus one.
525
526
- ``D`` -- An integer equal to the diameter of the digraph, and also to
527
the length of a vertex label when ``vertices == 'strings'``.
528
529
- ``vertices`` -- 'strings' (default) or 'integers', specifying whether
530
the vertices are words build upon an alphabet or integers.
531
532
533
EXAMPLES::
534
535
sage: K = digraphs.Kautz(2, 3)
536
sage: K.is_isomorphic(digraphs.ImaseItoh(12, 2), certify = True)
537
(True, {'201': 5, '120': 9, '202': 4, '212': 7, '210': 6, '010': 0, '121': 8, '012': 1, '021': 2, '020': 3, '102': 10, '101': 11})
538
539
sage: K = digraphs.Kautz([1,'a','B'], 2)
540
sage: K.edges()
541
[('1B', 'B1', '1'), ('1B', 'Ba', 'a'), ('1a', 'a1', '1'), ('1a', 'aB', 'B'), ('B1', '1B', 'B'), ('B1', '1a', 'a'), ('Ba', 'a1', '1'), ('Ba', 'aB', 'B'), ('a1', '1B', 'B'), ('a1', '1a', 'a'), ('aB', 'B1', '1'), ('aB', 'Ba', 'a')]
542
543
sage: K = digraphs.Kautz([1,'aA','BB'], 2)
544
sage: K.edges()
545
[('1,BB', 'BB,1', '1'), ('1,BB', 'BB,aA', 'aA'), ('1,aA', 'aA,1', '1'), ('1,aA', 'aA,BB', 'BB'), ('BB,1', '1,BB', 'BB'), ('BB,1', '1,aA', 'aA'), ('BB,aA', 'aA,1', '1'), ('BB,aA', 'aA,BB', 'BB'), ('aA,1', '1,BB', 'BB'), ('aA,1', '1,aA', 'aA'), ('aA,BB', 'BB,1', '1'), ('aA,BB', 'BB,aA', 'aA')]
546
547
548
TESTS:
549
550
An exception is raised when the degree is less than one::
551
552
sage: G = digraphs.Kautz(0, 2)
553
Traceback (most recent call last):
554
...
555
ValueError: Kautz digraphs are defined for degree at least one.
556
557
sage: G = digraphs.Kautz(['a'], 2)
558
Traceback (most recent call last):
559
...
560
ValueError: Kautz digraphs are defined for degree at least one.
561
562
An exception is raised when the diameter of the graph is less than one::
563
564
sage: G = digraphs.Kautz(2, 0)
565
Traceback (most recent call last):
566
...
567
ValueError: Kautz digraphs are defined for diameter at least one.
568
569
570
REFERENCE:
571
572
.. [Kautz68] W. H. Kautz. Bounds on directed (d, k) graphs. Theory of
573
cellular logic networks and machines, AFCRL-68-0668, SRI Project 7258,
574
Final Rep., pp. 20-28, 1968.
575
"""
576
if D < 1:
577
raise ValueError("Kautz digraphs are defined for diameter at least one.")
578
579
from sage.combinat.words.words import Words
580
from sage.rings.integer import Integer
581
582
my_alphabet = Words([str(i) for i in range(k+1)] if isinstance(k, Integer) else k, 1)
583
if my_alphabet.size_of_alphabet() < 2:
584
raise ValueError("Kautz digraphs are defined for degree at least one.")
585
586
if vertices == 'strings':
587
588
# We start building the set of vertices
589
V = [i for i in my_alphabet]
590
for i in range(D-1):
591
VV = []
592
for w in V:
593
VV += [w*a for a in my_alphabet if not w.has_suffix(a) ]
594
V = VV
595
596
# We now build the set of arcs
597
G = DiGraph()
598
for u in V:
599
for a in my_alphabet:
600
if not u.has_suffix(a):
601
G.add_edge(u.string_rep(), (u[1:]*a).string_rep(), a.string_rep())
602
603
else:
604
d = my_alphabet.size_of_alphabet()-1
605
G = digraphs.ImaseItoh( (d+1)*(d**(D-1)), d)
606
607
G.name( "Kautz digraph (k=%s, D=%s)"%(k,D) )
608
return G
609
610
611
def RandomDirectedGN(self, n, kernel=lambda x:x, seed=None):
612
"""
613
Returns a random GN (growing network) digraph with n vertices.
614
615
The digraph is constructed by adding vertices with a link to one
616
previously added vertex. The vertex to link to is chosen with a
617
preferential attachment model, i.e. probability is proportional to
618
degree. The default attachment kernel is a linear function of
619
degree. The digraph is always a tree, so in particular it is a
620
directed acyclic graph.
621
622
INPUT:
623
624
625
- ``n`` - number of vertices.
626
627
- ``kernel`` - the attachment kernel
628
629
- ``seed`` - for the random number generator
630
631
632
EXAMPLE::
633
634
sage: D = digraphs.RandomDirectedGN(25)
635
sage: D.edges(labels=False)
636
[(1, 0), (2, 0), (3, 1), (4, 0), (5, 0), (6, 1), (7, 0), (8, 3), (9, 0), (10, 8), (11, 3), (12, 9), (13, 8), (14, 0), (15, 11), (16, 11), (17, 5), (18, 11), (19, 6), (20, 5), (21, 14), (22, 5), (23, 18), (24, 11)]
637
sage: D.show() # long time
638
639
REFERENCE:
640
641
- [1] Krapivsky, P.L. and Redner, S. Organization of Growing
642
Random Networks, Phys. Rev. E vol. 63 (2001), p. 066123.
643
"""
644
if seed is None:
645
seed = current_randstate().long_seed()
646
import networkx
647
return DiGraph(networkx.gn_graph(n, kernel, seed=seed))
648
649
def RandomDirectedGNC(self, n, seed=None):
650
"""
651
Returns a random GNC (growing network with copying) digraph with n
652
vertices.
653
654
The digraph is constructed by adding vertices with a link to one
655
previously added vertex. The vertex to link to is chosen with a
656
preferential attachment model, i.e. probability is proportional to
657
degree. The new vertex is also linked to all of the previously
658
added vertex's successors.
659
660
INPUT:
661
662
663
- ``n`` - number of vertices.
664
665
- ``seed`` - for the random number generator
666
667
668
EXAMPLE::
669
670
sage: D = digraphs.RandomDirectedGNC(25)
671
sage: D.edges(labels=False)
672
[(1, 0), (2, 0), (2, 1), (3, 0), (4, 0), (4, 1), (5, 0), (5, 1), (5, 2), (6, 0), (6, 1), (7, 0), (7, 1), (7, 4), (8, 0), (9, 0), (9, 8), (10, 0), (10, 1), (10, 2), (10, 5), (11, 0), (11, 8), (11, 9), (12, 0), (12, 8), (12, 9), (13, 0), (13, 1), (14, 0), (14, 8), (14, 9), (14, 12), (15, 0), (15, 8), (15, 9), (15, 12), (16, 0), (16, 1), (16, 4), (16, 7), (17, 0), (17, 8), (17, 9), (17, 12), (18, 0), (18, 8), (19, 0), (19, 1), (19, 4), (19, 7), (20, 0), (20, 1), (20, 4), (20, 7), (20, 16), (21, 0), (21, 8), (22, 0), (22, 1), (22, 4), (22, 7), (22, 19), (23, 0), (23, 8), (23, 9), (23, 12), (23, 14), (24, 0), (24, 8), (24, 9), (24, 12), (24, 15)]
673
sage: D.show() # long time
674
675
REFERENCE:
676
677
- [1] Krapivsky, P.L. and Redner, S. Network Growth by
678
Copying, Phys. Rev. E vol. 71 (2005), p. 036118.
679
"""
680
if seed is None:
681
seed = current_randstate().long_seed()
682
import networkx
683
return DiGraph(networkx.gnc_graph(n, seed=seed))
684
685
def RandomDirectedGNP(self, n, p, loops = False, seed = None):
686
r"""
687
Returns a random digraph on `n` nodes. Each edge is inserted
688
independently with probability `p`.
689
690
INPUTS:
691
692
- ``n`` -- number of nodes of the digraph
693
694
- ``p`` -- probability of an edge
695
696
- ``loops`` -- is a boolean set to True if the random digraph may have
697
loops, and False (default) otherwise.
698
699
- ``seed`` -- integer seed for random number generator (default=None).
700
701
REFERENCES:
702
703
.. [1] P. Erdos and A. Renyi, On Random Graphs, Publ. Math. 6, 290
704
(1959).
705
706
.. [2] E. N. Gilbert, Random Graphs, Ann. Math. Stat., 30, 1141 (1959).
707
708
709
PLOTTING: When plotting, this graph will use the default spring-layout
710
algorithm, unless a position dictionary is specified.
711
712
EXAMPLE::
713
714
sage: set_random_seed(0)
715
sage: D = digraphs.RandomDirectedGNP(10, .2)
716
sage: D.num_verts()
717
10
718
sage: D.edges(labels=False)
719
[(1, 0), (1, 2), (3, 6), (3, 7), (4, 5), (4, 7), (4, 8), (5, 2), (6, 0), (7, 2), (8, 1), (8, 9), (9, 4)]
720
"""
721
from sage.graphs.graph_generators_pyx import RandomGNP
722
if n < 0:
723
raise ValueError("The number of nodes must be positive or null.")
724
if 0.0 > p or 1.0 < p:
725
raise ValueError("The probability p must be in [0..1].")
726
727
if seed is None:
728
seed = current_randstate().long_seed()
729
730
return RandomGNP(n, p, directed = True, loops = loops)
731
732
def RandomDirectedGNM(self, n, m, loops = False):
733
r"""
734
Returns a random labelled digraph on `n` nodes and `m` arcs.
735
736
INPUT:
737
738
- ``n`` (integer) -- number of vertices.
739
740
- ``m`` (integer) -- number of edges.
741
742
- ``loops`` (boolean) -- whether to allow loops (set to ``False`` by
743
default).
744
745
PLOTTING: When plotting, this graph will use the default spring-layout
746
algorithm, unless a position dictionary is specified.
747
748
EXAMPLE::
749
750
sage: D = digraphs.RandomDirectedGNM(10, 5)
751
sage: D.num_verts()
752
10
753
sage: D.edges(labels=False)
754
[(0, 3), (1, 5), (5, 1), (7, 0), (8, 5)]
755
756
With loops::
757
758
sage: D = digraphs.RandomDirectedGNM(10, 100, loops = True)
759
sage: D.num_verts()
760
10
761
sage: D.loops()
762
[(0, 0, None), (1, 1, None), (2, 2, None), (3, 3, None), (4, 4, None), (5, 5, None), (6, 6, None), (7, 7, None), (8, 8, None), (9, 9, None)]
763
764
TESTS::
765
766
sage: digraphs.RandomDirectedGNM(10,-3)
767
Traceback (most recent call last):
768
...
769
ValueError: The number of edges must satisfy 0<= m <= n(n-1) when no loops are allowed, and 0<= m <= n^2 otherwise.
770
771
sage: digraphs.RandomDirectedGNM(10,100)
772
Traceback (most recent call last):
773
...
774
ValueError: The number of edges must satisfy 0<= m <= n(n-1) when no loops are allowed, and 0<= m <= n^2 otherwise.
775
"""
776
n, m = int(n), int(m)
777
778
# The random graph is built by drawing randomly and uniformly two
779
# integers u,v, and adding the corresponding edge if it does not exist,
780
# as many times as necessary.
781
782
# When the graph is dense, we actually compute its complement. This will
783
# prevent us from drawing the same pair u,v too many times.
784
785
from sage.misc.prandom import _pyrand
786
rand = _pyrand()
787
D = DiGraph(n, loops = loops)
788
789
# Ensuring the parameters n,m make sense.
790
#
791
# If the graph is dense, we actually want to build its complement. We
792
# update m accordingly.
793
794
good_input = True
795
is_dense = False
796
797
if m < 0:
798
good_input = False
799
800
if loops:
801
if m > n*n:
802
good_input = False
803
elif m > n*n/2:
804
is_dense = True
805
m = n*n - m
806
807
else:
808
if m > n*(n-1):
809
good_input = False
810
elif m > n*(n-1)/2:
811
is_dense = True
812
m = n*(n-1) - m
813
814
if not good_input:
815
raise ValueError("The number of edges must satisfy 0<= m <= n(n-1) when no loops are allowed, and 0<= m <= n^2 otherwise.")
816
817
# When the given number of edges defines a density larger than 1/2, it
818
# should be faster to compute the complement of the graph (less edges to
819
# generate), then to return its complement. This being said, the
820
# .complement() method for sparse graphs is very slow at the moment.
821
822
# Similarly, it is faster to test whether a pair belongs to a dictionary
823
# than to test the adjacency of two vertices in a graph. For these
824
# reasons, the following code mainly works on dictionaries.
825
826
adj = dict( (i, dict()) for i in range(n) )
827
828
# We fill the dictionary structure, but add the corresponding edge in
829
# the graph only if is_dense is False. If it is true, we will add the
830
# edges in a second phase.
831
832
833
while m > 0:
834
835
# It is better to obtain random numbers this way than by calling the
836
# randint or randrange method. This, because they are very expensive
837
# when trying to compute MANY random integers, and because the
838
# following lines is precisely what they do anyway, after checking
839
# their parameters are correct.
840
841
u=int(rand.random()*n)
842
v=int(rand.random()*n)
843
844
if (u != v or loops) and (not v in adj[u]):
845
adj[u][v] = 1
846
m -= 1
847
if not is_dense:
848
D.add_edge(u,v)
849
850
# If is_dense is True, it means the graph has not been built. We fill D
851
# with the complement of the edges stored in the adj dictionary
852
853
if is_dense:
854
for u in range(n):
855
for v in range(n):
856
if ((u != v) or loops) and (not (v in adj[u])):
857
D.add_edge(u,v)
858
859
return D
860
861
def RandomDirectedGNR(self, n, p, seed=None):
862
"""
863
Returns a random GNR (growing network with redirection) digraph
864
with n vertices and redirection probability p.
865
866
The digraph is constructed by adding vertices with a link to one
867
previously added vertex. The vertex to link to is chosen uniformly.
868
With probability p, the arc is instead redirected to the successor
869
vertex. The digraph is always a tree.
870
871
INPUT:
872
873
874
- ``n`` - number of vertices.
875
876
- ``p`` - redirection probability
877
878
- ``seed`` - for the random number generator.
879
880
881
EXAMPLE::
882
883
sage: D = digraphs.RandomDirectedGNR(25, .2)
884
sage: D.edges(labels=False)
885
[(1, 0), (2, 0), (2, 1), (3, 0), (4, 0), (4, 1), (5, 0), (5, 1), (5, 2), (6, 0), (6, 1), (7, 0), (7, 1), (7, 4), (8, 0), (9, 0), (9, 8), (10, 0), (10, 1), (10, 2), (10, 5), (11, 0), (11, 8), (11, 9), (12, 0), (12, 8), (12, 9), (13, 0), (13, 1), (14, 0), (14, 8), (14, 9), (14, 12), (15, 0), (15, 8), (15, 9), (15, 12), (16, 0), (16, 1), (16, 4), (16, 7), (17, 0), (17, 8), (17, 9), (17, 12), (18, 0), (18, 8), (19, 0), (19, 1), (19, 4), (19, 7), (20, 0), (20, 1), (20, 4), (20, 7), (20, 16), (21, 0), (21, 8), (22, 0), (22, 1), (22, 4), (22, 7), (22, 19), (23, 0), (23, 8), (23, 9), (23, 12), (23, 14), (24, 0), (24, 8), (24, 9), (24, 12), (24, 15)]
886
sage: D.show() # long time
887
888
REFERENCE:
889
890
- [1] Krapivsky, P.L. and Redner, S. Organization of Growing
891
Random Networks, Phys. Rev. E vol. 63 (2001), p. 066123.
892
"""
893
if seed is None:
894
seed = current_randstate().long_seed()
895
import networkx
896
return DiGraph(networkx.gnc_graph(n, seed=seed))
897
898
################################################################################
899
# DiGraph Iterators
900
################################################################################
901
902
def __call__(self, vertices, property=lambda x: True, augment='edges', size=None, implementation='c_graph', sparse=True):
903
"""
904
Accesses the generator of isomorphism class representatives.
905
Iterates over distinct, exhaustive representatives.
906
907
INPUT:
908
909
910
- ``vertices`` - natural number
911
912
- ``property`` - any property to be tested on digraphs
913
before generation.
914
915
- ``augment`` - choices:
916
917
- ``'vertices'`` - augments by adding a vertex, and
918
edges incident to that vertex. In this case, all digraphs on up to
919
n=vertices are generated. If for any digraph G satisfying the
920
property, every subgraph, obtained from G by deleting one vertex
921
and only edges incident to that vertex, satisfies the property,
922
then this will generate all digraphs with that property. If this
923
does not hold, then all the digraphs generated will satisfy the
924
property, but there will be some missing.
925
926
- ``'edges'`` - augments a fixed number of vertices by
927
adding one edge In this case, all digraphs on exactly n=vertices
928
are generated. If for any graph G satisfying the property, every
929
subgraph, obtained from G by deleting one edge but not the vertices
930
incident to that edge, satisfies the property, then this will
931
generate all digraphs with that property. If this does not hold,
932
then all the digraphs generated will satisfy the property, but
933
there will be some missing.
934
935
- ``implementation`` - which underlying implementation to use (see DiGraph?)
936
937
- ``sparse`` - ignored if implementation is not ``c_graph``
938
939
EXAMPLES: Print digraphs on 2 or less vertices.
940
941
::
942
943
sage: for D in digraphs(2, augment='vertices'):
944
... print D
945
...
946
Digraph on 0 vertices
947
Digraph on 1 vertex
948
Digraph on 2 vertices
949
Digraph on 2 vertices
950
Digraph on 2 vertices
951
952
Print digraphs on 3 vertices.
953
954
::
955
956
sage: for D in digraphs(3):
957
... print D
958
Digraph on 3 vertices
959
Digraph on 3 vertices
960
...
961
Digraph on 3 vertices
962
Digraph on 3 vertices
963
964
For more examples, see the class level documentation, or type
965
966
::
967
968
sage: digraphs? # not tested
969
970
REFERENCE:
971
972
- Brendan D. McKay, Isomorph-Free Exhaustive generation.
973
Journal of Algorithms Volume 26, Issue 2, February 1998,
974
pages 306-324.
975
"""
976
if size is not None:
977
extra_property = lambda x: x.size() == size
978
else:
979
extra_property = lambda x: True
980
if augment == 'vertices':
981
from sage.graphs.graph_generators import canaug_traverse_vert
982
g = DiGraph(implementation=implementation, sparse=sparse)
983
for gg in canaug_traverse_vert(g, [], vertices, property, dig=True, implementation=implementation, sparse=sparse):
984
if extra_property(gg):
985
yield gg
986
elif augment == 'edges':
987
from sage.graphs.graph_generators import canaug_traverse_edge
988
g = DiGraph(vertices, implementation=implementation, sparse=sparse)
989
gens = []
990
for i in range(vertices-1):
991
gen = range(i)
992
gen.append(i+1); gen.append(i)
993
gen += range(i+2, vertices)
994
gens.append(gen)
995
for gg in canaug_traverse_edge(g, gens, property, dig=True, implementation=implementation, sparse=sparse):
996
if extra_property(gg):
997
yield gg
998
else:
999
raise NotImplementedError()
1000
1001
1002
# Easy access to the graph generators from the command line:
1003
digraphs = DiGraphGenerators()
1004
1005
1006
1007
1008
1009