Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/graphs/graph_generators.py
8815 views
1
# -*- coding: utf-8 -*-
2
r"""
3
Common Graphs (Graph Generators)
4
5
All graphs in Sage can be built through the ``graphs`` object. In order to
6
build a complete graph on 15 elements, one can do::
7
8
sage: g = graphs.CompleteGraph(15)
9
10
To get a path with 4 vertices, and the house graph::
11
12
sage: p = graphs.PathGraph(4)
13
sage: h = graphs.HouseGraph()
14
15
More interestingly, one can get the list of all graphs that Sage knows how to
16
build by typing ``graphs.`` in Sage and then hitting tab.
17
"""
18
19
# This method appends a list of methods to the doc as a 3xN table.
20
21
# Here's the point :
22
#
23
# we just have to insert the method's name in this file to add it to
24
# the tab, and in exchange the doc contains a table of width 3 with
25
# all methods listed, so that the reading order is Column1, then
26
# Column2, then Column3. Doing this by hand is hell with Sphinx when
27
# you need to insert a new method inside of the list !
28
29
def __append_to_doc(methods):
30
global __doc__
31
__doc__ += ("\n.. csv-table::\n"
32
" :class: contentstable\n"
33
" :widths: 33, 33, 33\n"
34
" :delim: |\n\n")
35
36
h = (len(methods)+2)//3
37
# Reorders the list of methods for horizontal reading, the only one Sphinx understands
38
reordered_methods = [0]*3*h
39
for i, m in enumerate(methods):
40
reordered_methods[3*(i%h)+(i//h)] = m
41
methods = reordered_methods
42
43
# Adding the list to the __doc__ string
44
wrap_name = lambda x : ":meth:`"+str(x)+" <GraphGenerators."+str(x)+">`" if x else ""
45
while methods:
46
a = methods.pop(0)
47
b = methods.pop(0)
48
c = methods.pop(0)
49
__doc__ += " "+wrap_name(a)+" | "+wrap_name(b)+" | "+wrap_name(c)+"\n"
50
51
__doc__ += """
52
**Basic structures**
53
"""
54
55
__append_to_doc(
56
["BullGraph",
57
"ButterflyGraph",
58
"CircularLadderGraph",
59
"ClawGraph",
60
"CycleGraph",
61
"CompleteBipartiteGraph",
62
"CompleteGraph",
63
"CompleteMultipartiteGraph",
64
"DiamondGraph",
65
"EmptyGraph",
66
"Grid2dGraph",
67
"GridGraph",
68
"HouseGraph",
69
"HouseXGraph",
70
"LadderGraph",
71
"LollipopGraph",
72
"PathGraph",
73
"StarGraph",
74
"ToroidalGrid2dGraph",
75
"Toroidal6RegularGrid2dGraph"]
76
)
77
78
__doc__ += """
79
**Small Graphs**
80
81
A small graph is just a single graph and has no parameter influencing
82
the number of edges or vertices.
83
"""
84
85
__append_to_doc(
86
["Balaban10Cage",
87
"Balaban11Cage",
88
"BidiakisCube",
89
"BiggsSmithGraph",
90
"BlanusaFirstSnarkGraph",
91
"BlanusaSecondSnarkGraph",
92
"BrinkmannGraph",
93
"BrouwerHaemersGraph",
94
"BuckyBall",
95
"CameronGraph",
96
"ChvatalGraph",
97
"ClebschGraph",
98
"CoxeterGraph",
99
"DesarguesGraph",
100
"DoubleStarSnark",
101
"DurerGraph",
102
"DyckGraph",
103
"EllinghamHorton54Graph",
104
"EllinghamHorton78Graph",
105
"ErreraGraph",
106
"FlowerSnark",
107
"FolkmanGraph",
108
"FosterGraph",
109
"FranklinGraph",
110
"FruchtGraph",
111
"GoldnerHararyGraph",
112
"GrayGraph",
113
"GrotzschGraph",
114
"HallJankoGraph",
115
"HarriesGraph",
116
"HarriesWongGraph",
117
"HeawoodGraph",
118
"HerschelGraph",
119
"HigmanSimsGraph",
120
"HoffmanGraph",
121
"HoffmanSingletonGraph",
122
"HoltGraph",
123
"HortonGraph",
124
"KittellGraph",
125
"KrackhardtKiteGraph",
126
"LjubljanaGraph",
127
"M22Graph",
128
"MarkstroemGraph",
129
"McGeeGraph",
130
"McLaughlinGraph",
131
"MeredithGraph",
132
"MoebiusKantorGraph",
133
"MoserSpindle",
134
"NauruGraph",
135
"PappusGraph",
136
"PoussinGraph",
137
"PetersenGraph",
138
"RobertsonGraph",
139
"SchlaefliGraph",
140
"ShrikhandeGraph",
141
"SimsGewirtzGraph",
142
"SousselierGraph",
143
"SylvesterGraph",
144
"SzekeresSnarkGraph",
145
"ThomsenGraph",
146
"TietzeGraph",
147
"Tutte12Cage",
148
"TutteCoxeterGraph",
149
"TutteGraph",
150
"WagnerGraph",
151
"WatkinsSnarkGraph",
152
"WellsGraph",
153
"WienerArayaGraph"])
154
155
__doc__ += """
156
*Platonic solids* (ordered ascending by number of vertices)
157
"""
158
159
__append_to_doc(
160
["TetrahedralGraph",
161
"OctahedralGraph",
162
"HexahedralGraph",
163
"IcosahedralGraph",
164
"DodecahedralGraph"])
165
166
__doc__ += """
167
**Families of graphs**
168
169
A family of graph is an infinite set of graphs which can be indexed by fixed
170
number of parameters, e.g. two integer parameters. (A method whose name starts
171
with a small letter does not return a single graph object but a graph iterator
172
or a list of graphs or ...)
173
"""
174
175
__append_to_doc(
176
["BalancedTree",
177
"BarbellGraph",
178
"BubbleSortGraph",
179
"CirculantGraph",
180
"cospectral_graphs",
181
"CubeGraph",
182
"DorogovtsevGoltsevMendesGraph",
183
"FibonacciTree",
184
"FoldedCubeGraph",
185
"FriendshipGraph",
186
"fullerenes",
187
"fusenes",
188
"FuzzyBallGraph",
189
"GeneralizedPetersenGraph",
190
"HanoiTowerGraph",
191
"HararyGraph",
192
"HyperStarGraph",
193
"JohnsonGraph",
194
"KneserGraph",
195
"LCFGraph",
196
"line_graph_forbidden_subgraphs",
197
"MycielskiGraph",
198
"MycielskiStep",
199
"NKStarGraph",
200
"NStarGraph",
201
"OddGraph",
202
"PaleyGraph",
203
"RingedTree",
204
"SymplecticGraph",
205
"trees",
206
"WheelGraph"])
207
208
__doc__ += """
209
*Chessboard Graphs*
210
"""
211
212
__append_to_doc(
213
["BishopGraph",
214
"KingGraph",
215
"KnightGraph",
216
"QueenGraph",
217
"RookGraph"])
218
219
__doc__ += """
220
**Intersection graphs**
221
222
These graphs are generated by geometric representations. The objects of
223
the representation correspond to the graph vertices and the intersections
224
of objects yield the graph edges.
225
"""
226
227
__append_to_doc(
228
["IntervalGraph",
229
"PermutationGraph",
230
"ToleranceGraph"])
231
232
__doc__ += """
233
**Random graphs**
234
"""
235
236
__append_to_doc(
237
["RandomBarabasiAlbert",
238
"RandomBipartite",
239
"RandomBoundedToleranceGraph",
240
"RandomGNM",
241
"RandomGNP",
242
"RandomHolmeKim",
243
"RandomIntervalGraph",
244
"RandomLobster",
245
"RandomNewmanWattsStrogatz",
246
"RandomRegular",
247
"RandomShell",
248
"RandomToleranceGraph",
249
"RandomTree",
250
"RandomTreePowerlaw"])
251
252
__doc__ += """
253
**Graphs with a given degree sequence**
254
"""
255
256
__append_to_doc(
257
["DegreeSequence",
258
"DegreeSequenceBipartite",
259
"DegreeSequenceConfigurationModel",
260
"DegreeSequenceExpected",
261
"DegreeSequenceTree"])
262
263
__doc__ += """
264
**Miscellaneous**
265
"""
266
267
__append_to_doc(
268
["WorldMap"]
269
)
270
271
__doc__ += """
272
273
AUTHORS:
274
275
- Robert Miller (2006-11-05): initial version, empty, random, petersen
276
277
- Emily Kirkman (2006-11-12): basic structures, node positioning for
278
all constructors
279
280
- Emily Kirkman (2006-11-19): docstrings, examples
281
282
- William Stein (2006-12-05): Editing.
283
284
- Robert Miller (2007-01-16): Cube generation and plotting
285
286
- Emily Kirkman (2007-01-16): more basic structures, docstrings
287
288
- Emily Kirkman (2007-02-14): added more named graphs
289
290
- Robert Miller (2007-06-08-11): Platonic solids, random graphs,
291
graphs with a given degree sequence, random directed graphs
292
293
- Robert Miller (2007-10-24): Isomorph free exhaustive generation
294
295
- Nathann Cohen (2009-08-12): WorldMap
296
297
- Michael Yurko (2009-9-01): added hyperstar, (n,k)-star, n-star, and
298
bubblesort graphs
299
300
- Anders Jonsson (2009-10-15): added generalized Petersen graphs
301
302
- Harald Schilly and Yann Laigle-Chapuy (2010-03-24): added Fibonacci Tree
303
304
- Jason Grout (2010-06-04): cospectral_graphs
305
306
- Edward Scheinerman (2010-08-11): RandomTree
307
308
- Ed Scheinerman (2010-08-21): added Grotzsch graph and Mycielski graphs
309
310
- Minh Van Nguyen (2010-11-26): added more named graphs
311
312
- Keshav Kini (2011-02-16): added Shrikhande and Dyck graphs
313
314
- David Coudert (2012-02-10): new RandomGNP generator
315
316
- David Coudert (2012-08-02): added chessboard graphs: Queen, King,
317
Knight, Bishop, and Rook graphs
318
319
- Nico Van Cleemput (2013-05-26): added fullerenes
320
321
- Nico Van Cleemput (2013-07-01): added benzenoids
322
323
- Birk Eisermann (2013-07-29): new section 'intersection graphs',
324
added (random, bounded) tolerance graphs
325
326
327
Functions and methods
328
---------------------
329
"""
330
331
###########################################################################
332
333
# Copyright (C) 2006 Robert L. Miller <[email protected]>
334
# and Emily A. Kirkman
335
# Copyright (C) 2009 Michael C. Yurko <[email protected]>
336
#
337
# Distributed under the terms of the GNU General Public License (GPL)
338
# http://www.gnu.org/licenses/
339
###########################################################################
340
341
# import from Python standard library
342
343
# import from Sage library
344
import graph
345
346
class GraphGenerators():
347
r"""
348
A class consisting of constructors for several common graphs, as well as
349
orderly generation of isomorphism class representatives. See the
350
:mod:`module's help <sage.graphs.graph_generators>` for a list of supported
351
constructors.
352
353
A list of all graphs and graph structures (other than isomorphism class
354
representatives) in this database is available via tab completion. Type
355
"graphs." and then hit the tab key to see which graphs are available.
356
357
The docstrings include educational information about each named
358
graph with the hopes that this class can be used as a reference.
359
360
For all the constructors in this class (except the octahedral,
361
dodecahedral, random and empty graphs), the position dictionary is
362
filled to override the spring-layout algorithm.
363
364
365
ORDERLY GENERATION::
366
367
graphs(vertices, property=lambda x: True, augment='edges', size=None)
368
369
This syntax accesses the generator of isomorphism class
370
representatives. Iterates over distinct, exhaustive
371
representatives.
372
373
Also: see the use of the optional nauty package for generating graphs
374
at the :meth:`nauty_geng` method.
375
376
INPUT:
377
378
- ``vertices`` -- natural number.
379
380
- ``property`` -- (default: ``lambda x: True``) any property to be
381
tested on graphs before generation, but note that in general the
382
graphs produced are not the same as those produced by using the
383
property function to filter a list of graphs produced by using
384
the ``lambda x: True`` default. The generation process assumes
385
the property has certain characteristics set by the ``augment``
386
argument, and only in the case of inherited properties such that
387
all subgraphs of the relevant kind (for ``augment='edges'`` or
388
``augment='vertices'``) of a graph with the property also
389
possess the property will there be no missing graphs. (The
390
``property`` argument is ignored if ``degree_sequence`` is
391
specified.)
392
393
- ``augment`` -- (default: ``'edges'``) possible values:
394
395
- ``'edges'`` -- augments a fixed number of vertices by
396
adding one edge. In this case, all graphs on exactly ``n=vertices`` are
397
generated. If for any graph G satisfying the property, every
398
subgraph, obtained from G by deleting one edge but not the vertices
399
incident to that edge, satisfies the property, then this will
400
generate all graphs with that property. If this does not hold, then
401
all the graphs generated will satisfy the property, but there will
402
be some missing.
403
404
- ``'vertices'`` -- augments by adding a vertex and
405
edges incident to that vertex. In this case, all graphs up to
406
``n=vertices`` are generated. If for any graph G satisfying the
407
property, every subgraph, obtained from G by deleting one vertex
408
and only edges incident to that vertex, satisfies the property,
409
then this will generate all graphs with that property. If this does
410
not hold, then all the graphs generated will satisfy the property,
411
but there will be some missing.
412
413
- ``size`` -- (default: ``None``) the size of the graph to be generated.
414
415
- ``degree_sequence`` -- (default: ``None``) a sequence of non-negative integers,
416
or ``None``. If specified, the generated graphs will have these
417
integers for degrees. In this case, property and size are both
418
ignored.
419
420
- ``loops`` -- (default: ``False``) whether to allow loops in the graph
421
or not.
422
423
- ``implementation`` -- (default: ``'c_graph'``) which underlying
424
implementation to use (see ``Graph?``).
425
426
- ``sparse`` -- (default: ``True``) ignored if implementation is not
427
``'c_graph'``.
428
429
- ``copy`` (boolean) -- If set to ``True`` (default)
430
this method makes copies of the graphs before returning
431
them. If set to ``False`` the method returns the graph it
432
is working on. The second alternative is faster, but modifying
433
any of the graph instances returned by the method may break
434
the function's behaviour, as it is using these graphs to
435
compute the next ones : only use ``copy_graph = False`` when
436
you stick to *reading* the graphs returned.
437
438
EXAMPLES:
439
440
Print graphs on 3 or less vertices::
441
442
sage: for G in graphs(3, augment='vertices'):
443
... print G
444
Graph on 0 vertices
445
Graph on 1 vertex
446
Graph on 2 vertices
447
Graph on 3 vertices
448
Graph on 3 vertices
449
Graph on 3 vertices
450
Graph on 2 vertices
451
Graph on 3 vertices
452
453
Note that we can also get graphs with underlying Cython implementation::
454
455
sage: for G in graphs(3, augment='vertices', implementation='c_graph'):
456
... print G
457
Graph on 0 vertices
458
Graph on 1 vertex
459
Graph on 2 vertices
460
Graph on 3 vertices
461
Graph on 3 vertices
462
Graph on 3 vertices
463
Graph on 2 vertices
464
Graph on 3 vertices
465
466
Print graphs on 3 vertices.
467
468
::
469
470
sage: for G in graphs(3):
471
... print G
472
Graph on 3 vertices
473
Graph on 3 vertices
474
Graph on 3 vertices
475
Graph on 3 vertices
476
477
Generate all graphs with 5 vertices and 4 edges.
478
479
::
480
481
sage: L = graphs(5, size=4)
482
sage: len(list(L))
483
6
484
485
Generate all graphs with 5 vertices and up to 4 edges.
486
487
::
488
489
sage: L = list(graphs(5, lambda G: G.size() <= 4))
490
sage: len(L)
491
14
492
sage: graphs_list.show_graphs(L) # long time
493
494
Generate all graphs with up to 5 vertices and up to 4 edges.
495
496
::
497
498
sage: L = list(graphs(5, lambda G: G.size() <= 4, augment='vertices'))
499
sage: len(L)
500
31
501
sage: graphs_list.show_graphs(L) # long time
502
503
Generate all graphs with degree at most 2, up to 6 vertices.
504
505
::
506
507
sage: property = lambda G: ( max([G.degree(v) for v in G] + [0]) <= 2 )
508
sage: L = list(graphs(6, property, augment='vertices'))
509
sage: len(L)
510
45
511
512
Generate all bipartite graphs on up to 7 vertices: (see
513
http://oeis.org/classic/A033995)
514
515
::
516
517
sage: L = list( graphs(7, lambda G: G.is_bipartite(), augment='vertices') )
518
sage: [len([g for g in L if g.order() == i]) for i in [1..7]]
519
[1, 2, 3, 7, 13, 35, 88]
520
521
Generate all bipartite graphs on exactly 7 vertices::
522
523
sage: L = list( graphs(7, lambda G: G.is_bipartite()) )
524
sage: len(L)
525
88
526
527
Generate all bipartite graphs on exactly 8 vertices::
528
529
sage: L = list( graphs(8, lambda G: G.is_bipartite()) ) # long time
530
sage: len(L) # long time
531
303
532
533
Remember that the property argument does not behave as a filter,
534
except for appropriately inheritable properties::
535
536
sage: property = lambda G: G.is_vertex_transitive()
537
sage: len(list(graphs(4, property)))
538
1
539
sage: len(filter(property, graphs(4)))
540
4
541
sage: property = lambda G: G.is_bipartite()
542
sage: len(list(graphs(4, property)))
543
7
544
sage: len(filter(property, graphs(4)))
545
7
546
547
Generate graphs on the fly: (see http://oeis.org/classic/A000088)
548
549
::
550
551
sage: for i in range(0, 7):
552
... print len(list(graphs(i)))
553
1
554
1
555
2
556
4
557
11
558
34
559
156
560
561
Generate all simple graphs, allowing loops: (see
562
http://oeis.org/classic/A000666)
563
564
::
565
566
sage: L = list(graphs(5,augment='vertices',loops=True)) # long time
567
sage: for i in [0..5]: print i, len([g for g in L if g.order() == i]) # long time
568
0 1
569
1 2
570
2 6
571
3 20
572
4 90
573
5 544
574
575
Generate all graphs with a specified degree sequence (see
576
http://oeis.org/classic/A002851)::
577
578
sage: for i in [4,6,8]: # long time (4s on sage.math, 2012)
579
... print i, len([g for g in graphs(i, degree_sequence=[3]*i) if g.is_connected()])
580
4 1
581
6 2
582
8 5
583
sage: for i in [4,6,8]: # long time (7s on sage.math, 2012)
584
... print i, len([g for g in graphs(i, augment='vertices', degree_sequence=[3]*i) if g.is_connected()])
585
4 1
586
6 2
587
8 5
588
589
::
590
591
sage: print 10, len([g for g in graphs(10,degree_sequence=[3]*10) if g.is_connected()]) # not tested
592
10 19
593
594
Make sure that the graphs are really independent and the generator
595
survives repeated vertex removal (trac 8458)::
596
597
sage: for G in graphs(3):
598
... G.delete_vertex(0)
599
... print(G.order())
600
2
601
2
602
2
603
2
604
605
REFERENCE:
606
607
- Brendan D. McKay, Isomorph-Free Exhaustive generation. *Journal
608
of Algorithms*, Volume 26, Issue 2, February 1998, pages 306-324.
609
"""
610
611
###########################################################################
612
# Graph Iterators
613
###########################################################################
614
615
def __call__(self, vertices=None, property=lambda x: True, augment='edges',
616
size=None, deg_seq=None, degree_sequence=None, loops=False, implementation='c_graph',
617
sparse=True, copy = True):
618
"""
619
Accesses the generator of isomorphism class representatives.
620
Iterates over distinct, exhaustive representatives. See the docstring
621
of this class for full documentation.
622
623
EXAMPLES:
624
625
Print graphs on 3 or less vertices::
626
627
sage: for G in graphs(3, augment='vertices'):
628
... print G
629
Graph on 0 vertices
630
Graph on 1 vertex
631
Graph on 2 vertices
632
Graph on 3 vertices
633
Graph on 3 vertices
634
Graph on 3 vertices
635
Graph on 2 vertices
636
Graph on 3 vertices
637
638
::
639
640
sage: for g in graphs():
641
... if g.num_verts() > 3: break
642
... print g
643
Graph on 0 vertices
644
Graph on 1 vertex
645
Graph on 2 vertices
646
Graph on 2 vertices
647
Graph on 3 vertices
648
Graph on 3 vertices
649
Graph on 3 vertices
650
Graph on 3 vertices
651
652
For more examples, see the class level documentation, or type::
653
654
sage: graphs? # not tested
655
656
REFERENCE:
657
658
- Brendan D. McKay, Isomorph-Free Exhaustive generation.
659
Journal of Algorithms Volume 26, Issue 2, February 1998,
660
pages 306-324.
661
"""
662
from sage.graphs.all import Graph
663
from sage.misc.superseded import deprecation
664
from copy import copy as copyfun
665
666
if deg_seq is not None:
667
deprecation(11927, "The argument name deg_seq is deprecated. It will be "
668
"removed in a future release of Sage. So, please use "
669
"degree_sequence instead.")
670
if degree_sequence is None:
671
degree_sequence=deg_seq
672
if degree_sequence is not None:
673
if vertices is None:
674
raise NotImplementedError
675
if len(degree_sequence) != vertices or sum(degree_sequence)%2 or sum(degree_sequence) > vertices*(vertices-1):
676
raise ValueError("Invalid degree sequence.")
677
degree_sequence = sorted(degree_sequence)
678
if augment == 'edges':
679
property = lambda x: all([degree_sequence[i] >= d for i,d in enumerate(sorted(x.degree()))])
680
extra_property = lambda x: degree_sequence == sorted(x.degree())
681
else:
682
property = lambda x: all([degree_sequence[i] >= d for i,d in enumerate(sorted(x.degree() + [0]*(vertices-x.num_verts()) ))])
683
extra_property = lambda x: x.num_verts() == vertices and degree_sequence == sorted(x.degree())
684
elif size is not None:
685
extra_property = lambda x: x.size() == size
686
else:
687
extra_property = lambda x: True
688
689
if augment == 'vertices':
690
if vertices is None:
691
raise NotImplementedError
692
g = Graph(loops=loops, implementation=implementation, sparse=sparse)
693
for gg in canaug_traverse_vert(g, [], vertices, property, loops=loops, implementation=implementation, sparse=sparse):
694
if extra_property(gg):
695
yield copyfun(gg) if copy else gg
696
elif augment == 'edges':
697
if vertices is None:
698
from sage.rings.all import Integer
699
vertices = Integer(0)
700
while True:
701
for g in self(vertices, loops=loops, implementation=implementation, sparse=sparse):
702
yield copyfun(g) if copy else g
703
vertices += 1
704
g = Graph(vertices, loops=loops, implementation=implementation, sparse=sparse)
705
gens = []
706
for i in range(vertices-1):
707
gen = range(i)
708
gen.append(i+1); gen.append(i)
709
gen += range(i+2, vertices)
710
gens.append(gen)
711
for gg in canaug_traverse_edge(g, gens, property, loops=loops, implementation=implementation, sparse=sparse):
712
if extra_property(gg):
713
yield copyfun(gg) if copy else gg
714
else:
715
raise NotImplementedError
716
717
718
def nauty_geng(self, options="", debug=False):
719
r"""
720
Returns a generator which creates graphs from nauty's geng program.
721
722
.. note::
723
724
Due to license restrictions, the nauty package is distributed
725
as a Sage optional package. At a system command line, execute
726
``sage -i nauty`` to see the nauty license and install the
727
package.
728
729
INPUT:
730
731
- ``options`` - a string passed to geng as if it was run at
732
a system command line. At a minimum, you *must* pass the
733
number of vertices you desire. Sage expects the graphs to be
734
in nauty's "graph6" format, do not set an option to change
735
this default or results will be unpredictable.
736
737
- ``debug`` - default: ``False`` - if ``True`` the first line of
738
geng's output to standard error is captured and the first call
739
to the generator's ``next()`` function will return this line
740
as a string. A line leading with ">A" indicates a successful
741
initiation of the program with some information on the arguments,
742
while a line beginning with ">E" indicates an error with the input.
743
744
The possible options, obtained as output of ``geng --help``::
745
746
n : the number of vertices
747
mine:maxe : a range for the number of edges
748
#:0 means '# or more' except in the case 0:0
749
res/mod : only generate subset res out of subsets 0..mod-1
750
751
-c : only write connected graphs
752
-C : only write biconnected graphs
753
-t : only generate triangle-free graphs
754
-f : only generate 4-cycle-free graphs
755
-b : only generate bipartite graphs
756
(-t, -f and -b can be used in any combination)
757
-m : save memory at the expense of time (only makes a
758
difference in the absence of -b, -t, -f and n <= 28).
759
-d# : a lower bound for the minimum degree
760
-D# : a upper bound for the maximum degree
761
-v : display counts by number of edges
762
-l : canonically label output graphs
763
764
-q : suppress auxiliary output (except from -v)
765
766
Options which cause geng to use an output format different
767
than the graph6 format are not listed above (-u, -g, -s, -y, -h)
768
as they will confuse the creation of a Sage graph. The res/mod
769
option can be useful when using the output in a routine run
770
several times in parallel.
771
772
OUTPUT:
773
774
A generator which will produce the graphs as Sage graphs.
775
These will be simple graphs: no loops, no multiple edges, no
776
directed edges.
777
778
.. SEEALSO::
779
780
:meth:`Graph.is_strongly_regular` -- tests whether a graph is
781
strongly regular and/or returns its parameters.
782
783
EXAMPLES:
784
785
The generator can be used to construct graphs for testing,
786
one at a time (usually inside a loop). Or it can be used to
787
create an entire list all at once if there is sufficient memory
788
to contain it. ::
789
790
sage: gen = graphs.nauty_geng("2") # optional nauty
791
sage: gen.next() # optional nauty
792
Graph on 2 vertices
793
sage: gen.next() # optional nauty
794
Graph on 2 vertices
795
sage: gen.next() # optional nauty
796
Traceback (most recent call last):
797
...
798
StopIteration: Exhausted list of graphs from nauty geng
799
800
A list of all graphs on 7 vertices. This agrees with
801
Sloane's OEIS sequence A000088. ::
802
803
sage: gen = graphs.nauty_geng("7") # optional nauty
804
sage: len(list(gen)) # optional nauty
805
1044
806
807
A list of just the connected graphs on 7 vertices. This agrees with
808
Sloane's OEIS sequence A001349. ::
809
810
sage: gen = graphs.nauty_geng("7 -c") # optional nauty
811
sage: len(list(gen)) # optional nauty
812
853
813
814
The ``debug`` switch can be used to examine geng's reaction
815
to the input in the ``options`` string. We illustrate success.
816
(A failure will be a string beginning with ">E".) Passing the
817
"-q" switch to geng will supress the indicator of a
818
successful initiation. ::
819
820
sage: gen = graphs.nauty_geng("4", debug=True) # optional nauty
821
sage: print gen.next() # optional nauty
822
>A nauty-geng -d0D3 n=4 e=0-6
823
"""
824
import subprocess
825
from sage.misc.package import is_package_installed
826
if not is_package_installed("nauty"):
827
raise TypeError("the optional nauty package is not installed")
828
sp = subprocess.Popen("nauty-geng {0}".format(options), shell=True,
829
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
830
stderr=subprocess.PIPE, close_fds=True)
831
if debug:
832
yield sp.stderr.readline()
833
gen = sp.stdout
834
while True:
835
try:
836
s = gen.next()
837
except StopIteration:
838
raise StopIteration("Exhausted list of graphs from nauty geng")
839
G = graph.Graph(s[:-1], format='graph6')
840
yield G
841
842
843
def cospectral_graphs(self, vertices, matrix_function=lambda g: g.adjacency_matrix(), graphs=None):
844
r"""
845
Find all sets of graphs on ``vertices`` vertices (with
846
possible restrictions) which are cospectral with respect to a
847
constructed matrix.
848
849
INPUT:
850
851
- ``vertices`` - The number of vertices in the graphs to be tested
852
853
- ``matrix_function`` - A function taking a graph and giving back
854
a matrix. This defaults to the adjacency matrix. The spectra
855
examined are the spectra of these matrices.
856
857
- ``graphs`` - One of three things:
858
859
- ``None`` (default) - test all graphs having ``vertices``
860
vertices
861
862
- a function taking a graph and returning ``True`` or ``False``
863
- test only the graphs on ``vertices`` vertices for which
864
the function returns ``True``
865
866
- a list of graphs (or other iterable object) - these graphs
867
are tested for cospectral sets. In this case,
868
``vertices`` is ignored.
869
870
OUTPUT:
871
872
A list of lists of graphs. Each sublist will be a list of
873
cospectral graphs (lists of cadinality 1 being omitted).
874
875
876
.. SEEALSO::
877
878
:meth:`Graph.is_strongly_regular` -- tests whether a graph is
879
strongly regular and/or returns its parameters.
880
881
EXAMPLES::
882
883
sage: g=graphs.cospectral_graphs(5)
884
sage: sorted(sorted(g.graph6_string() for g in glist) for glist in g)
885
[['Dr?', 'Ds_']]
886
sage: g[0][1].am().charpoly()==g[0][1].am().charpoly()
887
True
888
889
There are two sets of cospectral graphs on six vertices with no isolated vertices::
890
891
sage: g=graphs.cospectral_graphs(6, graphs=lambda x: min(x.degree())>0)
892
sage: sorted(sorted(g.graph6_string() for g in glist) for glist in g)
893
[['Ep__', 'Er?G'], ['ExGg', 'ExoG']]
894
sage: g[0][1].am().charpoly()==g[0][1].am().charpoly()
895
True
896
sage: g[1][1].am().charpoly()==g[1][1].am().charpoly()
897
True
898
899
There is one pair of cospectral trees on eight vertices::
900
901
sage: g=graphs.cospectral_graphs(6, graphs=graphs.trees(8))
902
sage: sorted(sorted(g.graph6_string() for g in glist) for glist in g)
903
[['GiPC?C', 'GiQCC?']]
904
sage: g[0][1].am().charpoly()==g[0][1].am().charpoly()
905
True
906
907
There are two sets of cospectral graphs (with respect to the
908
Laplacian matrix) on six vertices::
909
910
sage: g=graphs.cospectral_graphs(6, matrix_function=lambda g: g.laplacian_matrix())
911
sage: sorted(sorted(g.graph6_string() for g in glist) for glist in g)
912
[['Edq_', 'ErcG'], ['Exoo', 'EzcG']]
913
sage: g[0][1].laplacian_matrix().charpoly()==g[0][1].laplacian_matrix().charpoly()
914
True
915
sage: g[1][1].laplacian_matrix().charpoly()==g[1][1].laplacian_matrix().charpoly()
916
True
917
918
To find cospectral graphs with respect to the normalized
919
Laplacian, assuming the graphs do not have an isolated vertex, it
920
is enough to check the spectrum of the matrix `D^{-1}A`, where `D`
921
is the diagonal matrix of vertex degrees, and A is the adjacency
922
matrix. We find two such cospectral graphs (for the normalized
923
Laplacian) on five vertices::
924
925
sage: def DinverseA(g):
926
... A=g.adjacency_matrix().change_ring(QQ)
927
... for i in range(g.order()):
928
... A.rescale_row(i, 1/len(A.nonzero_positions_in_row(i)))
929
... return A
930
sage: g=graphs.cospectral_graphs(5, matrix_function=DinverseA, graphs=lambda g: min(g.degree())>0)
931
sage: sorted(sorted(g.graph6_string() for g in glist) for glist in g)
932
[['Dlg', 'Ds_']]
933
sage: g[0][1].laplacian_matrix(normalized=True).charpoly()==g[0][1].laplacian_matrix(normalized=True).charpoly()
934
True
935
"""
936
from sage.graphs.all import graphs as graph_gen
937
if graphs is None:
938
graph_list=graph_gen(vertices)
939
elif callable(graphs):
940
graph_list=iter(g for g in graph_gen(vertices) if graphs(g))
941
else:
942
graph_list=iter(graphs)
943
944
from collections import defaultdict
945
charpolys=defaultdict(list)
946
for g in graph_list:
947
cp=matrix_function(g).charpoly()
948
charpolys[cp].append(g)
949
950
cospectral_graphs=[]
951
for cp,g_list in charpolys.items():
952
if len(g_list)>1:
953
cospectral_graphs.append(g_list)
954
955
return cospectral_graphs
956
957
def fullerenes(self, order, ipr=False):
958
r"""
959
Returns a generator which creates fullerene graphs using
960
the buckygen generator (see [buckygen]_).
961
962
INPUT:
963
964
- ``order`` - a positive even integer smaller than or equal to 254.
965
This specifies the number of vertices in the generated fullerenes.
966
967
- ``ipr`` - default: ``False`` - if ``True`` only fullerenes that
968
satisfy the Isolated Pentagon Rule are generated. This means that
969
no pentagonal faces share an edge.
970
971
OUTPUT:
972
973
A generator which will produce the fullerene graphs as Sage graphs
974
with an embedding set. These will be simple graphs: no loops, no
975
multiple edges, no directed edges.
976
977
.. SEEALSO::
978
979
- :meth:`~sage.graphs.generic_graph.GenericGraph.set_embedding`,
980
:meth:`~sage.graphs.generic_graph.GenericGraph.get_embedding` --
981
get/set methods for embeddings.
982
983
EXAMPLES:
984
985
There are 1812 isomers of `\textrm{C}_{60}`, i.e., 1812 fullerene graphs
986
on 60 vertices: ::
987
988
sage: gen = graphs.fullerenes(60) # optional buckygen
989
sage: len(list(gen)) # optional buckygen
990
1812
991
992
However, there is only one IPR fullerene graph on 60 vertices: the famous
993
Buckminster Fullerene: ::
994
995
sage: gen = graphs.fullerenes(60, ipr=True) # optional buckygen
996
sage: gen.next() # optional buckygen
997
Graph on 60 vertices
998
sage: gen.next() # optional buckygen
999
Traceback (most recent call last):
1000
...
1001
StopIteration
1002
1003
The unique fullerene graph on 20 vertices is isomorphic to the dodecahedron
1004
graph. ::
1005
1006
sage: gen = graphs.fullerenes(20) # optional buckygen
1007
sage: g = gen.next() # optional buckygen
1008
sage: g.is_isomorphic(graphs.DodecahedralGraph()) # optional buckygen
1009
True
1010
sage: g.get_embedding() # optional buckygen
1011
{1: [2, 3, 4],
1012
2: [1, 5, 6],
1013
3: [1, 7, 8],
1014
4: [1, 9, 10],
1015
5: [2, 10, 11],
1016
6: [2, 12, 7],
1017
7: [3, 6, 13],
1018
8: [3, 14, 9],
1019
9: [4, 8, 15],
1020
10: [4, 16, 5],
1021
11: [5, 17, 12],
1022
12: [6, 11, 18],
1023
13: [7, 18, 14],
1024
14: [8, 13, 19],
1025
15: [9, 19, 16],
1026
16: [10, 15, 17],
1027
17: [11, 16, 20],
1028
18: [12, 20, 13],
1029
19: [14, 20, 15],
1030
20: [17, 19, 18]}
1031
sage: g.plot3d(layout='spring') # optional buckygen
1032
1033
REFERENCE:
1034
1035
.. [buckygen] G. Brinkmann, J. Goedgebeur and B.D. McKay, Generation of Fullerenes,
1036
Journal of Chemical Information and Modeling, 52(11):2910-2918, 2012.
1037
"""
1038
from sage.misc.package import is_package_installed
1039
if not is_package_installed("buckygen"):
1040
raise TypeError("the optional buckygen package is not installed")
1041
1042
# number of vertices should be positive
1043
if order < 0:
1044
raise ValueError("Number of vertices should be positive.")
1045
1046
# buckygen can only output fullerenes on up to 254 vertices
1047
if order > 254:
1048
raise ValueError("Number of vertices should be at most 254.")
1049
1050
# fullerenes only exist for an even number of vertices, larger than 20
1051
# and different from 22
1052
if order % 2 == 1 or order < 20 or order == 22:
1053
return
1054
1055
command = 'buckygen -'+('I' if ipr else '')+'d {0}d'.format(order)
1056
1057
import subprocess
1058
sp = subprocess.Popen(command, shell=True,
1059
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
1060
stderr=subprocess.PIPE, close_fds=True)
1061
out = sp.stdout
1062
1063
#start of code to read planar code
1064
1065
header = out.read(15)
1066
assert header == '>>planar_code<<', 'Not a valid planar code header'
1067
1068
#read graph per graph
1069
while True:
1070
c = out.read(1)
1071
if len(c)==0:
1072
return
1073
1074
# Each graph is stored in the following way :
1075
#
1076
# The first character is the number of vertices, followed by
1077
# n11,...,n1k,null character,n21,...,n2k',null character, ...
1078
#
1079
# where the n1* are all neighbors of n1 and all n2* are the
1080
# neighbors of n2, ...
1081
#
1082
# Besides, these neighbors are enumerated in clockwise order.
1083
order = ord(c)
1084
1085
zeroCount = 0
1086
1087
g = [[] for i in range(order)]
1088
1089
while zeroCount < order:
1090
c = out.read(1)
1091
if ord(c)==0:
1092
zeroCount += 1
1093
else:
1094
g[zeroCount].append(ord(c))
1095
1096
#construct graph based on g
1097
g = {i+1:di for i,di in enumerate(g)}
1098
G = graph.Graph(g)
1099
G.set_embedding(g)
1100
yield(G)
1101
1102
def fusenes(self, hexagon_count, benzenoids=False):
1103
r"""
1104
Returns a generator which creates fusenes and benzenoids using
1105
the benzene generator (see [benzene]_). Fusenes are planar
1106
polycyclic hydrocarbons with all bounded faces hexagons. Benzenoids
1107
are fusenes that are subgraphs of the hexagonal lattice.
1108
1109
INPUT:
1110
1111
- ``hexagon_count`` - a positive integer smaller than or equal to 30.
1112
This specifies the number of hexagons in the generated benzenoids.
1113
1114
- ``benzenoids`` - default: ``False`` - if ``True`` only benzenoids are
1115
generated.
1116
1117
OUTPUT:
1118
1119
A generator which will produce the fusenes as Sage graphs
1120
with an embedding set. These will be simple graphs: no loops, no
1121
multiple edges, no directed edges.
1122
1123
.. SEEALSO::
1124
1125
- :meth:`~sage.graphs.generic_graph.GenericGraph.set_embedding`,
1126
:meth:`~sage.graphs.generic_graph.GenericGraph.get_embedding` --
1127
get/set methods for embeddings.
1128
1129
EXAMPLES:
1130
1131
There is a unique fusene with 2 hexagons: ::
1132
1133
sage: gen = graphs.fusenes(2) # optional benzene
1134
sage: len(list(gen)) # optional benzene
1135
1
1136
1137
This fusene is naphtalene (`\textrm{C}_{10}\textrm{H}_{8}`).
1138
In the fusene graph the H-atoms are not stored, so this is
1139
a graph on just 10 vertices: ::
1140
1141
sage: gen = graphs.fusenes(2) # optional benzene
1142
sage: gen.next() # optional benzene
1143
Graph on 10 vertices
1144
sage: gen.next() # optional benzene
1145
Traceback (most recent call last):
1146
...
1147
StopIteration
1148
1149
There are 6505 benzenoids with 9 hexagons: ::
1150
1151
sage: gen = graphs.fusenes(9, benzenoids=True) # optional benzene
1152
sage: len(list(gen)) # optional benzene
1153
6505
1154
1155
REFERENCE:
1156
1157
.. [benzene] G. Brinkmann, G. Caporossi and P. Hansen, A Constructive Enumeration of Fusenes and Benzenoids,
1158
Journal of Algorithms, 45:155-166, 2002.
1159
"""
1160
from sage.misc.package import is_package_installed
1161
if not is_package_installed("benzene"):
1162
raise TypeError("the optional benzene package is not installed")
1163
1164
# number of hexagons should be positive
1165
if hexagon_count < 0:
1166
raise ValueError("Number of hexagons should be positive.")
1167
1168
# benzene is only built for fusenes with up to 30 hexagons
1169
if hexagon_count > 30:
1170
raise ValueError("Number of hexagons should be at most 30.")
1171
1172
# there are no fusenes with 0 hexagons
1173
if hexagon_count == 0:
1174
return
1175
1176
# there is only one unique fusene with 1 hexagon (and benzene doesn't generate it)
1177
if hexagon_count == 1:
1178
g = {1:[6, 2], 2:[1, 3], 3:[2, 4], 4:[3, 5], 5:[4, 6], 6:[5, 1]}
1179
G = graph.Graph(g)
1180
G.set_embedding(g)
1181
yield(G)
1182
return
1183
1184
command = 'benzene '+('b' if benzenoids else '')+' {0} p'.format(hexagon_count)
1185
1186
import subprocess
1187
sp = subprocess.Popen(command, shell=True,
1188
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
1189
stderr=subprocess.PIPE, close_fds=True)
1190
out = sp.stdout
1191
1192
#start of code to read planar code
1193
1194
header = out.read(15)
1195
assert header == '>>planar_code<<', 'Not a valid planar code header'
1196
1197
#read graph per graph
1198
while True:
1199
c = out.read(1)
1200
if len(c)==0:
1201
return
1202
1203
# Each graph is stored in the following way :
1204
#
1205
# The first character is the number of vertices, followed by
1206
# n11,...,n1k,null character,n21,...,n2k',null character, ...
1207
#
1208
# where the n1* are all neighbors of n1 and all n2* are the
1209
# neighbors of n2, ...
1210
#
1211
# Besides, these neighbors are enumerated in clockwise order.
1212
order = ord(c)
1213
1214
zeroCount = 0
1215
1216
g = [[] for i in range(order)]
1217
1218
while zeroCount < order:
1219
c = out.read(1)
1220
if ord(c)==0:
1221
zeroCount += 1
1222
else:
1223
g[zeroCount].append(ord(c))
1224
1225
#construct graph based on g
1226
g = {i+1:di for i,di in enumerate(g)}
1227
G = graph.Graph(g)
1228
G.set_embedding(g)
1229
yield(G)
1230
1231
###########################################################################
1232
# Basic Graphs
1233
###########################################################################
1234
import sage.graphs.generators.basic
1235
BullGraph = staticmethod(sage.graphs.generators.basic.BullGraph)
1236
ButterflyGraph = staticmethod(sage.graphs.generators.basic.ButterflyGraph)
1237
CircularLadderGraph = staticmethod(sage.graphs.generators.basic.CircularLadderGraph)
1238
ClawGraph = staticmethod(sage.graphs.generators.basic.ClawGraph)
1239
CycleGraph = staticmethod(sage.graphs.generators.basic.CycleGraph)
1240
CompleteGraph = staticmethod(sage.graphs.generators.basic.CompleteGraph)
1241
CompleteBipartiteGraph = staticmethod(sage.graphs.generators.basic.CompleteBipartiteGraph)
1242
CompleteMultipartiteGraph= staticmethod(sage.graphs.generators.basic.CompleteMultipartiteGraph)
1243
DiamondGraph = staticmethod(sage.graphs.generators.basic.DiamondGraph)
1244
EmptyGraph = staticmethod(sage.graphs.generators.basic.EmptyGraph)
1245
Grid2dGraph = staticmethod(sage.graphs.generators.basic.Grid2dGraph)
1246
GridGraph = staticmethod(sage.graphs.generators.basic.GridGraph)
1247
HouseGraph = staticmethod(sage.graphs.generators.basic.HouseGraph)
1248
HouseXGraph = staticmethod(sage.graphs.generators.basic.HouseXGraph)
1249
LadderGraph = staticmethod(sage.graphs.generators.basic.LadderGraph)
1250
LollipopGraph = staticmethod(sage.graphs.generators.basic.LollipopGraph)
1251
PathGraph = staticmethod(sage.graphs.generators.basic.PathGraph)
1252
StarGraph = staticmethod(sage.graphs.generators.basic.StarGraph)
1253
Toroidal6RegularGrid2dGraph = staticmethod(sage.graphs.generators.basic.Toroidal6RegularGrid2dGraph)
1254
ToroidalGrid2dGraph = staticmethod(sage.graphs.generators.basic.ToroidalGrid2dGraph)
1255
1256
###########################################################################
1257
# Small Graphs
1258
###########################################################################
1259
import sage.graphs.generators.smallgraphs
1260
Balaban10Cage = staticmethod(sage.graphs.generators.smallgraphs.Balaban10Cage)
1261
Balaban11Cage = staticmethod(sage.graphs.generators.smallgraphs.Balaban11Cage)
1262
BidiakisCube = staticmethod(sage.graphs.generators.smallgraphs.BidiakisCube)
1263
BiggsSmithGraph = staticmethod(sage.graphs.generators.smallgraphs.BiggsSmithGraph)
1264
BlanusaFirstSnarkGraph = staticmethod(sage.graphs.generators.smallgraphs.BlanusaFirstSnarkGraph)
1265
BlanusaSecondSnarkGraph = staticmethod(sage.graphs.generators.smallgraphs.BlanusaSecondSnarkGraph)
1266
BrinkmannGraph = staticmethod(sage.graphs.generators.smallgraphs.BrinkmannGraph)
1267
BrouwerHaemersGraph = staticmethod(sage.graphs.generators.smallgraphs.BrouwerHaemersGraph)
1268
BuckyBall = staticmethod(sage.graphs.generators.smallgraphs.BuckyBall)
1269
CameronGraph = staticmethod(sage.graphs.generators.smallgraphs.CameronGraph)
1270
ChvatalGraph = staticmethod(sage.graphs.generators.smallgraphs.ChvatalGraph)
1271
ClebschGraph = staticmethod(sage.graphs.generators.smallgraphs.ClebschGraph)
1272
CoxeterGraph = staticmethod(sage.graphs.generators.smallgraphs.CoxeterGraph)
1273
DesarguesGraph = staticmethod(sage.graphs.generators.smallgraphs.DesarguesGraph)
1274
DoubleStarSnark = staticmethod(sage.graphs.generators.smallgraphs.DoubleStarSnark)
1275
DurerGraph = staticmethod(sage.graphs.generators.smallgraphs.DurerGraph)
1276
DyckGraph = staticmethod(sage.graphs.generators.smallgraphs.DyckGraph)
1277
EllinghamHorton54Graph = staticmethod(sage.graphs.generators.smallgraphs.EllinghamHorton54Graph)
1278
EllinghamHorton78Graph = staticmethod(sage.graphs.generators.smallgraphs.EllinghamHorton78Graph)
1279
ErreraGraph = staticmethod(sage.graphs.generators.smallgraphs.ErreraGraph)
1280
FlowerSnark = staticmethod(sage.graphs.generators.smallgraphs.FlowerSnark)
1281
FolkmanGraph = staticmethod(sage.graphs.generators.smallgraphs.FolkmanGraph)
1282
FosterGraph = staticmethod(sage.graphs.generators.smallgraphs.FosterGraph)
1283
FranklinGraph = staticmethod(sage.graphs.generators.smallgraphs.FranklinGraph)
1284
FruchtGraph = staticmethod(sage.graphs.generators.smallgraphs.FruchtGraph)
1285
GoldnerHararyGraph = staticmethod(sage.graphs.generators.smallgraphs.GoldnerHararyGraph)
1286
GrayGraph = staticmethod(sage.graphs.generators.smallgraphs.GrayGraph)
1287
GrotzschGraph = staticmethod(sage.graphs.generators.smallgraphs.GrotzschGraph)
1288
HallJankoGraph = staticmethod(sage.graphs.generators.smallgraphs.HallJankoGraph)
1289
WellsGraph = staticmethod(sage.graphs.generators.smallgraphs.WellsGraph)
1290
HarriesGraph = staticmethod(sage.graphs.generators.smallgraphs.HarriesGraph)
1291
HarriesWongGraph = staticmethod(sage.graphs.generators.smallgraphs.HarriesWongGraph)
1292
HeawoodGraph = staticmethod(sage.graphs.generators.smallgraphs.HeawoodGraph)
1293
HerschelGraph = staticmethod(sage.graphs.generators.smallgraphs.HerschelGraph)
1294
HigmanSimsGraph = staticmethod(sage.graphs.generators.smallgraphs.HigmanSimsGraph)
1295
HoffmanGraph = staticmethod(sage.graphs.generators.smallgraphs.HoffmanGraph)
1296
HoffmanSingletonGraph = staticmethod(sage.graphs.generators.smallgraphs.HoffmanSingletonGraph)
1297
HoltGraph = staticmethod(sage.graphs.generators.smallgraphs.HoltGraph)
1298
HortonGraph = staticmethod(sage.graphs.generators.smallgraphs.HortonGraph)
1299
KittellGraph = staticmethod(sage.graphs.generators.smallgraphs.KittellGraph)
1300
KrackhardtKiteGraph = staticmethod(sage.graphs.generators.smallgraphs.KrackhardtKiteGraph)
1301
LjubljanaGraph = staticmethod(sage.graphs.generators.smallgraphs.LjubljanaGraph)
1302
M22Graph = staticmethod(sage.graphs.generators.smallgraphs.M22Graph)
1303
MarkstroemGraph = staticmethod(sage.graphs.generators.smallgraphs.MarkstroemGraph)
1304
McGeeGraph = staticmethod(sage.graphs.generators.smallgraphs.McGeeGraph)
1305
McLaughlinGraph = staticmethod(sage.graphs.generators.smallgraphs.McLaughlinGraph)
1306
MeredithGraph = staticmethod(sage.graphs.generators.smallgraphs.MeredithGraph)
1307
MoebiusKantorGraph = staticmethod(sage.graphs.generators.smallgraphs.MoebiusKantorGraph)
1308
MoserSpindle = staticmethod(sage.graphs.generators.smallgraphs.MoserSpindle)
1309
NauruGraph = staticmethod(sage.graphs.generators.smallgraphs.NauruGraph)
1310
PappusGraph = staticmethod(sage.graphs.generators.smallgraphs.PappusGraph)
1311
PoussinGraph = staticmethod(sage.graphs.generators.smallgraphs.PoussinGraph)
1312
PetersenGraph = staticmethod(sage.graphs.generators.smallgraphs.PetersenGraph)
1313
RobertsonGraph = staticmethod(sage.graphs.generators.smallgraphs.RobertsonGraph)
1314
SchlaefliGraph = staticmethod(sage.graphs.generators.smallgraphs.SchlaefliGraph)
1315
ShrikhandeGraph = staticmethod(sage.graphs.generators.smallgraphs.ShrikhandeGraph)
1316
SimsGewirtzGraph = staticmethod(sage.graphs.generators.smallgraphs.SimsGewirtzGraph)
1317
SousselierGraph = staticmethod(sage.graphs.generators.smallgraphs.SousselierGraph)
1318
SylvesterGraph = staticmethod(sage.graphs.generators.smallgraphs.SylvesterGraph)
1319
SzekeresSnarkGraph = staticmethod(sage.graphs.generators.smallgraphs.SzekeresSnarkGraph)
1320
ThomsenGraph = staticmethod(sage.graphs.generators.smallgraphs.ThomsenGraph)
1321
TietzeGraph = staticmethod(sage.graphs.generators.smallgraphs.TietzeGraph)
1322
Tutte12Cage = staticmethod(sage.graphs.generators.smallgraphs.Tutte12Cage)
1323
TutteCoxeterGraph = staticmethod(sage.graphs.generators.smallgraphs.TutteCoxeterGraph)
1324
TutteGraph = staticmethod(sage.graphs.generators.smallgraphs.TutteGraph)
1325
WagnerGraph = staticmethod(sage.graphs.generators.smallgraphs.WagnerGraph)
1326
WatkinsSnarkGraph = staticmethod(sage.graphs.generators.smallgraphs.WatkinsSnarkGraph)
1327
WienerArayaGraph = staticmethod(sage.graphs.generators.smallgraphs.WienerArayaGraph)
1328
1329
###########################################################################
1330
# Platonic Solids
1331
###########################################################################
1332
import sage.graphs.generators.platonic_solids
1333
DodecahedralGraph = staticmethod(sage.graphs.generators.platonic_solids.DodecahedralGraph)
1334
HexahedralGraph = staticmethod(sage.graphs.generators.platonic_solids.HexahedralGraph)
1335
IcosahedralGraph = staticmethod(sage.graphs.generators.platonic_solids.IcosahedralGraph)
1336
OctahedralGraph = staticmethod(sage.graphs.generators.platonic_solids.OctahedralGraph)
1337
TetrahedralGraph = staticmethod(sage.graphs.generators.platonic_solids.TetrahedralGraph)
1338
1339
###########################################################################
1340
# Families
1341
###########################################################################
1342
import sage.graphs.generators.families
1343
BalancedTree = staticmethod(sage.graphs.generators.families.BalancedTree)
1344
BarbellGraph = staticmethod(sage.graphs.generators.families.BarbellGraph)
1345
BubbleSortGraph = staticmethod(sage.graphs.generators.families.BubbleSortGraph)
1346
CirculantGraph = staticmethod(sage.graphs.generators.families.CirculantGraph)
1347
CubeGraph = staticmethod(sage.graphs.generators.families.CubeGraph)
1348
DorogovtsevGoltsevMendesGraph = staticmethod(sage.graphs.generators.families.DorogovtsevGoltsevMendesGraph)
1349
FibonacciTree = staticmethod(sage.graphs.generators.families.FibonacciTree)
1350
FoldedCubeGraph = staticmethod(sage.graphs.generators.families.FoldedCubeGraph)
1351
FriendshipGraph = staticmethod(sage.graphs.generators.families.FriendshipGraph)
1352
FuzzyBallGraph = staticmethod(sage.graphs.generators.families.FuzzyBallGraph)
1353
GeneralizedPetersenGraph = staticmethod(sage.graphs.generators.families.GeneralizedPetersenGraph)
1354
HanoiTowerGraph = staticmethod(sage.graphs.generators.families.HanoiTowerGraph)
1355
HararyGraph = staticmethod(sage.graphs.generators.families.HararyGraph)
1356
HyperStarGraph = staticmethod(sage.graphs.generators.families.HyperStarGraph)
1357
JohnsonGraph = staticmethod(sage.graphs.generators.families.JohnsonGraph)
1358
KneserGraph = staticmethod(sage.graphs.generators.families.KneserGraph)
1359
LCFGraph = staticmethod(sage.graphs.generators.families.LCFGraph)
1360
line_graph_forbidden_subgraphs = staticmethod(sage.graphs.generators.families.line_graph_forbidden_subgraphs)
1361
MycielskiGraph = staticmethod(sage.graphs.generators.families.MycielskiGraph)
1362
MycielskiStep = staticmethod(sage.graphs.generators.families.MycielskiStep)
1363
NKStarGraph = staticmethod(sage.graphs.generators.families.NKStarGraph)
1364
NStarGraph = staticmethod(sage.graphs.generators.families.NStarGraph)
1365
OddGraph = staticmethod(sage.graphs.generators.families.OddGraph)
1366
PaleyGraph = staticmethod(sage.graphs.generators.families.PaleyGraph)
1367
RingedTree = staticmethod(sage.graphs.generators.families.RingedTree)
1368
SymplecticGraph = staticmethod(sage.graphs.generators.families.SymplecticGraph)
1369
trees = staticmethod(sage.graphs.generators.families.trees)
1370
WheelGraph = staticmethod(sage.graphs.generators.families.WheelGraph)
1371
1372
###########################################################################
1373
# Chessboard Graphs
1374
###########################################################################
1375
import sage.graphs.generators.chessboard
1376
ChessboardGraphGenerator = staticmethod(sage.graphs.generators.chessboard.ChessboardGraphGenerator)
1377
BishopGraph = staticmethod(sage.graphs.generators.chessboard.BishopGraph)
1378
KingGraph = staticmethod(sage.graphs.generators.chessboard.KingGraph)
1379
KnightGraph = staticmethod(sage.graphs.generators.chessboard.KnightGraph)
1380
QueenGraph = staticmethod(sage.graphs.generators.chessboard.QueenGraph)
1381
RookGraph = staticmethod(sage.graphs.generators.chessboard.RookGraph)
1382
1383
###########################################################################
1384
# Intersection graphs
1385
###########################################################################
1386
import sage.graphs.generators.intersection
1387
IntervalGraph = staticmethod(sage.graphs.generators.intersection.IntervalGraph)
1388
PermutationGraph = staticmethod(sage.graphs.generators.intersection.PermutationGraph)
1389
ToleranceGraph = staticmethod(sage.graphs.generators.intersection.ToleranceGraph)
1390
1391
###########################################################################
1392
# Random Graphs
1393
###########################################################################
1394
import sage.graphs.generators.random
1395
RandomBarabasiAlbert = staticmethod(sage.graphs.generators.random.RandomBarabasiAlbert)
1396
RandomBipartite = staticmethod(sage.graphs.generators.random.RandomBipartite)
1397
RandomBoundedToleranceGraph = staticmethod(sage.graphs.generators.random.RandomBoundedToleranceGraph)
1398
RandomGNM = staticmethod(sage.graphs.generators.random.RandomGNM)
1399
RandomGNP = staticmethod(sage.graphs.generators.random.RandomGNP)
1400
RandomHolmeKim = staticmethod(sage.graphs.generators.random.RandomHolmeKim)
1401
RandomInterval = staticmethod(sage.graphs.generators.random.RandomInterval) # deprecated
1402
RandomIntervalGraph = staticmethod(sage.graphs.generators.random.RandomIntervalGraph)
1403
RandomLobster = staticmethod(sage.graphs.generators.random.RandomLobster)
1404
RandomNewmanWattsStrogatz = staticmethod(sage.graphs.generators.random.RandomNewmanWattsStrogatz)
1405
RandomRegular = staticmethod(sage.graphs.generators.random.RandomRegular)
1406
RandomShell = staticmethod(sage.graphs.generators.random.RandomShell)
1407
RandomToleranceGraph = staticmethod(sage.graphs.generators.random.RandomToleranceGraph)
1408
RandomTreePowerlaw = staticmethod(sage.graphs.generators.random.RandomTreePowerlaw)
1409
RandomTree = staticmethod(sage.graphs.generators.random.RandomTree)
1410
1411
###########################################################################
1412
# World Map
1413
###########################################################################
1414
import sage.graphs.generators.world_map
1415
WorldMap = staticmethod(sage.graphs.generators.world_map.WorldMap)
1416
1417
###########################################################################
1418
# Degree Sequence
1419
###########################################################################
1420
import sage.graphs.generators.degree_sequence
1421
DegreeSequence = staticmethod(sage.graphs.generators.degree_sequence.DegreeSequence)
1422
DegreeSequenceBipartite = staticmethod(sage.graphs.generators.degree_sequence.DegreeSequenceBipartite)
1423
DegreeSequenceConfigurationModel = staticmethod(sage.graphs.generators.degree_sequence.DegreeSequenceConfigurationModel)
1424
DegreeSequenceTree = staticmethod(sage.graphs.generators.degree_sequence.DegreeSequenceTree)
1425
DegreeSequenceExpected = staticmethod(sage.graphs.generators.degree_sequence.DegreeSequenceExpected)
1426
1427
def canaug_traverse_vert(g, aut_gens, max_verts, property, dig=False, loops=False, implementation='c_graph', sparse=True):
1428
"""
1429
Main function for exhaustive generation. Recursive traversal of a
1430
canonically generated tree of isomorph free (di)graphs satisfying a
1431
given property.
1432
1433
INPUT:
1434
1435
1436
- ``g`` - current position on the tree.
1437
1438
- ``aut_gens`` - list of generators of Aut(g), in
1439
list notation.
1440
1441
- ``max_verts`` - when to retreat.
1442
1443
- ``property`` - check before traversing below g.
1444
1445
- ``degree_sequence`` - specify a degree sequence to try to
1446
obtain.
1447
1448
1449
EXAMPLES::
1450
1451
sage: from sage.graphs.graph_generators import canaug_traverse_vert
1452
sage: list(canaug_traverse_vert(Graph(), [], 3, lambda x: True))
1453
[Graph on 0 vertices, ... Graph on 3 vertices]
1454
1455
The best way to access this function is through the graphs()
1456
iterator:
1457
1458
Print graphs on 3 or less vertices.
1459
1460
::
1461
1462
sage: for G in graphs(3, augment='vertices'):
1463
... print G
1464
...
1465
Graph on 0 vertices
1466
Graph on 1 vertex
1467
Graph on 2 vertices
1468
Graph on 3 vertices
1469
Graph on 3 vertices
1470
Graph on 3 vertices
1471
Graph on 2 vertices
1472
Graph on 3 vertices
1473
1474
Print digraphs on 2 or less vertices.
1475
1476
::
1477
1478
sage: for D in digraphs(2, augment='vertices'):
1479
... print D
1480
...
1481
Digraph on 0 vertices
1482
Digraph on 1 vertex
1483
Digraph on 2 vertices
1484
Digraph on 2 vertices
1485
Digraph on 2 vertices
1486
"""
1487
from sage.groups.perm_gps.partn_ref.refinement_graphs import search_tree
1488
1489
if not property(g):
1490
return
1491
yield g
1492
1493
n = g.order()
1494
if n < max_verts:
1495
1496
# build a list representing C(g) - the vertex to be added
1497
# is at the end, so only specify which edges...
1498
# in the case of graphs, there are n possibilities,
1499
# and in the case of digraphs, there are 2*n.
1500
if dig:
1501
possibilities = 2*n
1502
else:
1503
possibilities = n
1504
num_roots = 2**possibilities
1505
children = [-1]*num_roots
1506
1507
# union-find C(g) under Aut(g)
1508
for gen in aut_gens:
1509
for i in xrange(len(children)):
1510
k = 0
1511
for j in xrange(possibilities):
1512
if (1 << j)&i:
1513
if dig and j >= n:
1514
k += (1 << (gen[j-n]+n))
1515
else:
1516
k += (1 << gen[j])
1517
while children[k] != -1:
1518
k = children[k]
1519
while children[i] != -1:
1520
i = children[i]
1521
if i != k:
1522
# union i & k
1523
smaller, larger = sorted([i,k])
1524
children[larger] = smaller
1525
num_roots -= 1
1526
1527
# find representatives of orbits of C(g)
1528
roots = []
1529
found_roots = 0
1530
i = 0
1531
while found_roots < num_roots:
1532
if children[i] == -1:
1533
found_roots += 1
1534
roots.append(i)
1535
i += 1
1536
for i in roots:
1537
# construct a z for each number in roots...
1538
z = g.copy(implementation=implementation, sparse=sparse)
1539
z.add_vertex(n)
1540
edges = []
1541
if dig:
1542
index = 0
1543
while index < possibilities/2:
1544
if (1 << index)&i:
1545
edges.append((index,n))
1546
index += 1
1547
while index < possibilities:
1548
if (1 << index)&i:
1549
edges.append((n,index-n))
1550
index += 1
1551
else:
1552
index = 0
1553
while (1 << index) <= i:
1554
if (1 << index)&i:
1555
edges.append((index,n))
1556
index += 1
1557
z.add_edges(edges)
1558
z_s = []
1559
if property(z):
1560
z_s.append(z)
1561
if loops:
1562
z = z.copy(implementation=implementation, sparse=sparse)
1563
z.add_edge((n,n))
1564
if property(z):
1565
z_s.append(z)
1566
for z in z_s:
1567
z_aut_gens, _, canonical_relabeling = search_tree(z, [z.vertices()], certify=True, dig=(dig or loops))
1568
cut_vert = 0
1569
while canonical_relabeling[cut_vert] != n:
1570
cut_vert += 1
1571
sub_verts = [v for v in z if v != cut_vert]
1572
m_z = z.subgraph(sub_verts)
1573
1574
if m_z == g:
1575
for a in canaug_traverse_vert(z, z_aut_gens, max_verts, property, dig=dig, loops=loops, implementation=implementation, sparse=sparse):
1576
yield a
1577
else:
1578
for possibility in check_aut(z_aut_gens, cut_vert, n):
1579
if m_z.relabel(possibility, inplace=False) == g:
1580
for a in canaug_traverse_vert(z, z_aut_gens, max_verts, property, dig=dig, loops=loops, implementation=implementation, sparse=sparse):
1581
yield a
1582
break
1583
1584
def check_aut(aut_gens, cut_vert, n):
1585
"""
1586
Helper function for exhaustive generation.
1587
1588
At the start, check_aut is given a set of generators for the
1589
automorphism group, aut_gens. We already know we are looking for
1590
an element of the auto- morphism group that sends cut_vert to n,
1591
and check_aut generates these for the canaug_traverse function.
1592
1593
EXAMPLE: Note that the last two entries indicate that none of the
1594
automorphism group has yet been searched - we are starting at the
1595
identity [0, 1, 2, 3] and so far that is all we have seen. We
1596
return automorphisms mapping 2 to 3.
1597
1598
::
1599
1600
sage: from sage.graphs.graph_generators import check_aut
1601
sage: list( check_aut( [ [0, 3, 2, 1], [1, 0, 3, 2], [2, 1, 0, 3] ], 2, 3))
1602
[[1, 0, 3, 2], [1, 2, 3, 0]]
1603
"""
1604
from copy import copy
1605
perm = range(n+1)
1606
seen_perms = [perm]
1607
unchecked_perms = [perm]
1608
while len(unchecked_perms) != 0:
1609
perm = unchecked_perms.pop(0)
1610
for gen in aut_gens:
1611
new_perm = copy(perm)
1612
for i in xrange(len(perm)):
1613
new_perm[i] = gen[perm[i]]
1614
if new_perm not in seen_perms:
1615
seen_perms.append(new_perm)
1616
unchecked_perms.append(new_perm)
1617
if new_perm[cut_vert] == n:
1618
yield new_perm
1619
1620
def canaug_traverse_edge(g, aut_gens, property, dig=False, loops=False, implementation='c_graph', sparse=True):
1621
"""
1622
Main function for exhaustive generation. Recursive traversal of a
1623
canonically generated tree of isomorph free graphs satisfying a
1624
given property.
1625
1626
INPUT:
1627
1628
1629
- ``g`` - current position on the tree.
1630
1631
- ``aut_gens`` - list of generators of Aut(g), in
1632
list notation.
1633
1634
- ``property`` - check before traversing below g.
1635
1636
1637
EXAMPLES::
1638
1639
sage: from sage.graphs.graph_generators import canaug_traverse_edge
1640
sage: G = Graph(3)
1641
sage: list(canaug_traverse_edge(G, [], lambda x: True))
1642
[Graph on 3 vertices, ... Graph on 3 vertices]
1643
1644
The best way to access this function is through the graphs()
1645
iterator:
1646
1647
Print graphs on 3 or less vertices.
1648
1649
::
1650
1651
sage: for G in graphs(3):
1652
... print G
1653
...
1654
Graph on 3 vertices
1655
Graph on 3 vertices
1656
Graph on 3 vertices
1657
Graph on 3 vertices
1658
1659
Print digraphs on 3 or less vertices.
1660
1661
::
1662
1663
sage: for G in digraphs(3):
1664
... print G
1665
...
1666
Digraph on 3 vertices
1667
Digraph on 3 vertices
1668
...
1669
Digraph on 3 vertices
1670
Digraph on 3 vertices
1671
"""
1672
from sage.groups.perm_gps.partn_ref.refinement_graphs import search_tree
1673
1674
if not property(g):
1675
return
1676
yield g
1677
n = g.order()
1678
if dig:
1679
max_size = n*(n-1)
1680
else:
1681
max_size = (n*(n-1))>>1 # >> 1 is just / 2 (this is n choose 2)
1682
if loops: max_size += n
1683
if g.size() < max_size:
1684
# build a list representing C(g) - the edge to be added
1685
# is one of max_size choices
1686
if dig:
1687
children = [[(j,i) for i in xrange(n)] for j in xrange(n)]
1688
else:
1689
children = [[(j,i) for i in xrange(j)] for j in xrange(n)]
1690
# union-find C(g) under Aut(g)
1691
orbits = range(n)
1692
for gen in aut_gens:
1693
for iii in xrange(n):
1694
if orbits[gen[iii]] != orbits[iii]:
1695
temp = orbits[gen[iii]]
1696
for jjj in xrange(n):
1697
if orbits[jjj] == temp:
1698
orbits[jjj] = orbits[iii]
1699
if dig:
1700
jjj_range = range(iii) + range(iii+1, n)
1701
else:
1702
jjj_range = xrange(iii) # iii > jjj
1703
for jjj in jjj_range:
1704
i, j = iii, jjj
1705
if dig:
1706
x, y = gen[i], gen[j]
1707
else:
1708
y, x = sorted([gen[i], gen[j]])
1709
if children[i][j] != children[x][y]:
1710
x_val, y_val = x, y
1711
i_val, j_val = i, j
1712
if dig:
1713
while (x_val, y_val) != children[x_val][y_val]:
1714
x_val, y_val = children[x_val][y_val]
1715
while (i_val, j_val) != children[i_val][j_val]:
1716
i_val, j_val = children[i_val][j_val]
1717
else:
1718
while (x_val, y_val) != children[x_val][y_val]:
1719
y_val, x_val = sorted(children[x_val][y_val])
1720
while (i_val, j_val) != children[i_val][j_val]:
1721
j_val, i_val = sorted(children[i_val][j_val])
1722
while (x, y) != (x_val, y_val):
1723
xx, yy = x, y
1724
x, y = children[x][y]
1725
children[xx][yy] = (x_val, y_val)
1726
while (i, j) != (i_val, j_val):
1727
ii, jj = i, j
1728
i, j = children[i][j]
1729
children[ii][jj] = (i_val, j_val)
1730
if x < i:
1731
children[i][j] = (x, y)
1732
elif x > i:
1733
children[x][y] = (i, j)
1734
elif y < j:
1735
children[i][j] = (x, y)
1736
elif y > j:
1737
children[x][y] = (i, j)
1738
else:
1739
continue
1740
# find representatives of orbits of C(g)
1741
roots = []
1742
for i in range(n):
1743
if dig:
1744
j_range = range(i) + range(i+1, n)
1745
else:
1746
j_range = range(i)
1747
for j in j_range:
1748
if children[i][j] == (i, j):
1749
roots.append((i,j))
1750
if loops:
1751
seen = []
1752
for i in xrange(n):
1753
if orbits[i] not in seen:
1754
roots.append((i,i))
1755
seen.append(orbits[i])
1756
for i, j in roots:
1757
if g.has_edge(i, j):
1758
continue
1759
# construct a z for each edge in roots...
1760
z = g.copy(implementation=implementation, sparse=sparse)
1761
z.add_edge(i, j)
1762
if not property(z):
1763
continue
1764
z_aut_gens, _, canonical_relabeling = search_tree(z, [z.vertices()], certify=True, dig=(dig or loops))
1765
relabel_inverse = [0]*n
1766
for ii in xrange(n):
1767
relabel_inverse[canonical_relabeling[ii]] = ii
1768
z_can = z.relabel(canonical_relabeling, inplace=False)
1769
cut_edge_can = z_can.edges(labels=False, sort=True)[-1]
1770
cut_edge = [relabel_inverse[cut_edge_can[0]], relabel_inverse[cut_edge_can[1]]]
1771
if dig:
1772
cut_edge = tuple(cut_edge)
1773
else:
1774
cut_edge = tuple(sorted(cut_edge))
1775
1776
from copy import copy
1777
m_z = copy(z)
1778
m_z.delete_edge(cut_edge)
1779
if m_z == g:
1780
for a in canaug_traverse_edge(z, z_aut_gens, property, dig=dig, loops=loops, implementation=implementation, sparse=sparse):
1781
yield a
1782
else:
1783
for possibility in check_aut_edge(z_aut_gens, cut_edge, i, j, n, dig=dig):
1784
if m_z.relabel(possibility, inplace=False) == g:
1785
for a in canaug_traverse_edge(z, z_aut_gens, property, dig=dig, loops=loops, implementation=implementation, sparse=sparse):
1786
yield a
1787
break
1788
1789
def check_aut_edge(aut_gens, cut_edge, i, j, n, dig=False):
1790
"""
1791
Helper function for exhaustive generation.
1792
1793
At the start, check_aut_edge is given a set of generators for the
1794
automorphism group, aut_gens. We already know we are looking for
1795
an element of the auto- morphism group that sends cut_edge to {i,
1796
j}, and check_aut generates these for the canaug_traverse
1797
function.
1798
1799
EXAMPLE: Note that the last two entries indicate that none of the
1800
automorphism group has yet been searched - we are starting at the
1801
identity [0, 1, 2, 3] and so far that is all we have seen. We
1802
return automorphisms mapping 2 to 3.
1803
1804
::
1805
1806
sage: from sage.graphs.graph_generators import check_aut
1807
sage: list( check_aut( [ [0, 3, 2, 1], [1, 0, 3, 2], [2, 1, 0, 3] ], 2, 3))
1808
[[1, 0, 3, 2], [1, 2, 3, 0]]
1809
"""
1810
from copy import copy
1811
perm = range(n)
1812
seen_perms = [perm]
1813
unchecked_perms = [perm]
1814
while len(unchecked_perms) != 0:
1815
perm = unchecked_perms.pop(0)
1816
for gen in aut_gens:
1817
new_perm = copy(perm)
1818
for ii in xrange(n):
1819
new_perm[ii] = gen[perm[ii]]
1820
if new_perm not in seen_perms:
1821
seen_perms.append(new_perm)
1822
unchecked_perms.append(new_perm)
1823
if new_perm[cut_edge[0]] == i and new_perm[cut_edge[1]] == j:
1824
yield new_perm
1825
if not dig and new_perm[cut_edge[0]] == j and new_perm[cut_edge[1]] == i:
1826
yield new_perm
1827
1828
1829
# Easy access to the graph generators from the command line:
1830
graphs = GraphGenerators()
1831
1832