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