Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/graphs/graph_generators.py
4045 views
1
# -*- coding: utf-8 -*-
2
3
r"""
4
Common graphs
5
6
Usage
7
=====
8
9
To see a list of all graph constructors, type "graphs." and then
10
press the tab key. The documentation for each constructor includes
11
information about each graph, which provides a useful reference.
12
13
14
Plotting
15
========
16
17
All graphs (i.e., networks) have an associated Sage
18
graphics object, which you can display::
19
20
sage: G = graphs.WheelGraph(15)
21
sage: P = G.plot()
22
sage: P.show() # long time
23
24
If you create a graph in Sage using the ``Graph``
25
command, then plot that graph, the positioning of nodes is
26
determined using the spring-layout algorithm. For the special graph
27
constructors, which you get using ``graphs.[tab]``, the
28
positions are preset. For example, consider the Petersen graph with
29
default node positioning vs. the Petersen graph constructed by this
30
database::
31
32
sage: petersen_spring = Graph({0:[1,4,5], 1:[0,2,6], 2:[1,3,7], 3:[2,4,8], 4:[0,3,9], 5:[0,7,8], 6:[1,8,9], 7:[2,5,9], 8:[3,5,6], 9:[4,6,7]})
33
sage: petersen_spring.show() # long time
34
sage: petersen_database = graphs.PetersenGraph()
35
sage: petersen_database.show() # long time
36
37
For all the constructors in this database (except the octahedral,
38
dodecahedral, random and empty graphs), the position dictionary is
39
filled in, instead of using the spring-layout algorithm.
40
41
For further visual examples and explanation, see the docstrings
42
below, particularly for
43
:meth:`CycleGraph <GraphGenerators.CycleGraph>`,
44
:meth:`StarGraph <GraphGenerators.StarGraph>`,
45
:meth:`WheelGraph <GraphGenerators.WheelGraph>`,
46
:meth:`CompleteGraph <GraphGenerators.CompleteGraph>`, and
47
:meth:`CompleteBipartiteGraph <GraphGenerators.CompleteBipartiteGraph>`.
48
49
50
.. _organization:
51
52
Organization
53
============
54
55
The constructors available in this database are
56
organized as follows.
57
58
59
Basic structures
60
----------------
61
62
- :meth:`BarbellGraph <GraphGenerators.BarbellGraph>`
63
- :meth:`BuckyBall <GraphGenerators.BuckyBall>`
64
- :meth:`BullGraph <GraphGenerators.BullGraph>`
65
- :meth:`ButterflyGraph <GraphGenerators.ButterflyGraph>`
66
- :meth:`CircularLadderGraph <GraphGenerators.CircularLadderGraph>`
67
- :meth:`ClawGraph <GraphGenerators.ClawGraph>`
68
- :meth:`CycleGraph <GraphGenerators.CycleGraph>`
69
- :meth:`DiamondGraph <GraphGenerators.DiamondGraph>`
70
- :meth:`EmptyGraph <GraphGenerators.EmptyGraph>`
71
- :meth:`Grid2dGraph <GraphGenerators.Grid2dGraph>`
72
- :meth:`GridGraph <GraphGenerators.GridGraph>`
73
- :meth:`HouseGraph <GraphGenerators.HouseGraph>`
74
- :meth:`HouseXGraph <GraphGenerators.HouseXGraph>`
75
- :meth:`KrackhardtKiteGraph <GraphGenerators.KrackhardtKiteGraph>`
76
- :meth:`LadderGraph <GraphGenerators.LadderGraph>`
77
- :meth:`LCFGraph <GraphGenerators.LCFGraph>`
78
- :meth:`LollipopGraph <GraphGenerators.LollipopGraph>`
79
- :meth:`PathGraph <GraphGenerators.PathGraph>`
80
- :meth:`StarGraph <GraphGenerators.StarGraph>`
81
- :meth:`ToroidalGrid2dGraph <GraphGenerators.ToroidalGrid2dGraph>`
82
- :meth:`WheelGraph <GraphGenerators.WheelGraph>`
83
84
85
Platonic solids
86
---------------
87
88
- :meth:`DodecahedralGraph <GraphGenerators.DodecahedralGraph>`
89
- :meth:`HexahedralGraph <GraphGenerators.HexahedralGraph>`
90
- :meth:`IcosahedralGraph <GraphGenerators.IcosahedralGraph>`
91
- :meth:`OctahedralGraph <GraphGenerators.OctahedralGraph>`
92
- :meth:`TetrahedralGraph <GraphGenerators.TetrahedralGraph>`
93
94
95
Named Graphs
96
------------
97
98
- :meth:`Balaban10Cage <GraphGenerators.Balaban10Cage>`
99
- :meth:`Balaban11Cage <GraphGenerators.Balaban11Cage>`
100
- :meth:`BidiakisCube <GraphGenerators.BidiakisCube>`
101
- :meth:`BiggsSmithGraph <GraphGenerators.BiggsSmithGraph>`
102
- :meth:`BrinkmannGraph <GraphGenerators.BrinkmannGraph>`
103
- :meth:`DoubleStarSnark <GraphGenerators.DoubleStarSnark>`
104
- :meth:`ChvatalGraph <GraphGenerators.ChvatalGraph>`
105
- :meth:`DesarguesGraph <GraphGenerators.DesarguesGraph>`
106
- :meth:`DurerGraph <GraphGenerators.DurerGraph>`
107
- :meth:`DyckGraph <GraphGenerators.DyckGraph>`
108
- :meth:`ErreraGraph <GraphGenerators.ErreraGraph>`
109
- :meth:`FlowerSnark <GraphGenerators.FlowerSnark>`
110
- :meth:`FosterGraph <GraphGenerators.FosterGraph>`
111
- :meth:`FranklinGraph <GraphGenerators.FranklinGraph>`
112
- :meth:`FruchtGraph <GraphGenerators.FruchtGraph>`
113
- :meth:`GoldnerHararyGraph <GraphGenerators.GoldnerHararyGraph>`
114
- :meth:`GrayGraph <GraphGenerators.GrayGraph>`
115
- :meth:`GrotzschGraph <GraphGenerators.GrotzschGraph>`
116
- :meth:`HararyGraph <GraphGenerators.HararyGraph>`
117
- :meth:`HarriesGraph <GraphGenerators.HarriesGraph>`
118
- :meth:`HarriesWongGraph <GraphGenerators.HarriesWongGraph>`
119
- :meth:`HeawoodGraph <GraphGenerators.HeawoodGraph>`
120
- :meth:`HerschelGraph <GraphGenerators.HerschelGraph>`
121
- :meth:`HigmanSimsGraph <GraphGenerators.HigmanSimsGraph>`
122
- :meth:`HoffmanSingletonGraph <GraphGenerators.HoffmanSingletonGraph>`
123
- :meth:`LjubljanaGraph <GraphGenerators.LjubljanaGraph>`
124
- :meth:`McGeeGraph <GraphGenerators.McGeeGraph>`
125
- :meth:`MoebiusKantorGraph <GraphGenerators.MoebiusKantorGraph>`
126
- :meth:`MoserSpindle <GraphGenerators.MoserSpindle>`
127
- :meth:`PappusGraph <GraphGenerators.PappusGraph>`
128
- :meth:`PetersenGraph <GraphGenerators.PetersenGraph>`
129
- :meth:`ShrikhandeGraph <GraphGenerators.ShrikhandeGraph>`
130
- :meth:`ThomsenGraph <GraphGenerators.ThomsenGraph>`
131
- :meth:`Tutte12Cage <GraphGenerators.Tutte12Cage>`
132
- :meth:`TutteCoxeterGraph <GraphGenerators.TutteCoxeterGraph>`
133
- :meth:`WagnerGraph <GraphGenerators.WagnerGraph>`
134
135
136
Families of graphs
137
------------------
138
139
- :meth:`BalancedTree <GraphGenerators.BalancedTree>`
140
- :meth:`BubbleSortGraph <GraphGenerators.BubbleSortGraph>`
141
- :meth:`CirculantGraph <GraphGenerators.CirculantGraph>`
142
- :meth:`CompleteBipartiteGraph <GraphGenerators.CompleteBipartiteGraph>`
143
- :meth:`CompleteGraph <GraphGenerators.CompleteGraph>`
144
- :meth:`CubeGraph <GraphGenerators.CubeGraph>`
145
- :meth:`FibonacciTree <GraphGenerators.FibonacciTree>`
146
- :meth:`FriendshipGraph <GraphGenerators.FriendshipGraph>`
147
- :meth:`FuzzyBallGraph <GraphGenerators.FuzzyBallGraph>`
148
- :meth:`GeneralizedPetersenGraph <GraphGenerators.GeneralizedPetersenGraph>`
149
- :meth:`HanoiTowerGraph <GraphGenerators.HanoiTowerGraph>`
150
151
- :meth:`HyperStarGraph <GraphGenerators.HyperStarGraph>`
152
- :meth:`KneserGraph <GraphGenerators.KneserGraph>`
153
- :meth:`LCFGraph <GraphGenerators.LCFGraph>`
154
- :meth:`MycielskiGraph <GraphGenerators.MycielskiGraph>`
155
- :meth:`MycielskiStep <GraphGenerators.MycielskiStep>`
156
- :meth:`NKStarGraph <GraphGenerators.NKStarGraph>`
157
- :meth:`NStarGraph <GraphGenerators.NStarGraph>`
158
- :meth:`OddGraph <GraphGenerators.OddGraph>`
159
- :meth:`line_graph_forbidden_subgraphs <GraphGenerators.line_graph_forbidden_subgraphs>`
160
- :meth:`PermutationGraph <GraphGenerators.PermutationGraph>`
161
- :meth:`trees <GraphGenerators.trees>`
162
163
164
Pseudofractal graphs
165
--------------------
166
167
- :meth:`DorogovtsevGoltsevMendesGraph <GraphGenerators.DorogovtsevGoltsevMendesGraph>`
168
169
170
Random graphs
171
-------------
172
173
- :meth:`RandomBarabasiAlbert <GraphGenerators.RandomBarabasiAlbert>`
174
- :meth:`RandomBipartite <GraphGenerators.RandomBipartite>`
175
- :meth:`RandomGNM <GraphGenerators.RandomGNM>`
176
- :meth:`RandomGNP <GraphGenerators.RandomGNP>`
177
- :meth:`RandomHolmeKim <GraphGenerators.RandomHolmeKim>`
178
- :meth:`RandomInterval <GraphGenerators.RandomInterval>`
179
- :meth:`RandomLobster <GraphGenerators.RandomLobster>`
180
- :meth:`RandomNewmanWattsStrogatz <GraphGenerators.RandomNewmanWattsStrogatz>`
181
- :meth:`RandomRegular <GraphGenerators.RandomRegular>`
182
- :meth:`RandomShell <GraphGenerators.RandomShell>`
183
- :meth:`RandomTree <GraphGenerators.RandomTree>`
184
- :meth:`RandomTreePowerlaw <GraphGenerators.RandomTreePowerlaw>`
185
186
187
Graphs with a given degree sequence
188
-----------------------------------
189
190
- :meth:`DegreeSequence <GraphGenerators.DegreeSequence>`
191
- :meth:`DegreeSequenceBipartite <GraphGenerators.DegreeSequenceBipartite>`
192
- :meth:`DegreeSequenceConfigurationModel <GraphGenerators.DegreeSequenceConfigurationModel>`
193
- :meth:`DegreeSequenceExpected <GraphGenerators.DegreeSequenceExpected>`
194
- :meth:`DegreeSequenceTree <GraphGenerators.DegreeSequenceTree>`
195
196
197
Miscellaneous
198
-------------
199
200
- :meth:`WorldMap <GraphGenerators.WorldMap>`
201
202
203
AUTHORS:
204
205
- Robert Miller (2006-11-05): initial version, empty, random, petersen
206
207
- Emily Kirkman (2006-11-12): basic structures, node positioning for
208
all constructors
209
210
- Emily Kirkman (2006-11-19): docstrings, examples
211
212
- William Stein (2006-12-05): Editing.
213
214
- Robert Miller (2007-01-16): Cube generation and plotting
215
216
- Emily Kirkman (2007-01-16): more basic structures, docstrings
217
218
- Emily Kirkman (2007-02-14): added more named graphs
219
220
- Robert Miller (2007-06-08-11): Platonic solids, random graphs,
221
graphs with a given degree sequence, random directed graphs
222
223
- Robert Miller (2007-10-24): Isomorph free exhaustive generation
224
225
- Nathann Cohen (2009-08-12): WorldMap
226
227
- Michael Yurko (2009-9-01): added hyperstar, (n,k)-star, n-star, and
228
bubblesort graphs
229
230
- Anders Jonsson (2009-10-15): added generalized Petersen graphs
231
232
- Harald Schilly and Yann Laigle-Chapuy (2010-03-24): added Fibonacci Tree
233
234
- Jason Grout (2010-06-04): cospectral_graphs
235
236
- Edward Scheinerman (2010-08-11): RandomTree
237
238
- Ed Scheinerman (2010-08-21): added Grotzsch graph and Mycielski graphs
239
240
- Minh Van Nguyen (2010-11-26): added more named graphs
241
242
- Keshav Kini (2011-02-16): added Shrikhande and Dyck graphs
243
244
- David Coudert (2012-02-10): new RandomGNP generator
245
"""
246
247
###########################################################################
248
249
# Copyright (C) 2006 Robert L. Miller <[email protected]>
250
# and Emily A. Kirkman
251
# Copyright (C) 2009 Michael C. Yurko <[email protected]>
252
#
253
# Distributed under the terms of the GNU General Public License (GPL)
254
# http://www.gnu.org/licenses/
255
###########################################################################
256
257
# import from Python standard library
258
from math import sin, cos, pi
259
260
# import from Sage library
261
import graph
262
from sage.misc.randstate import current_randstate
263
264
class GraphGenerators():
265
r"""
266
A class consisting of constructors for several common graphs, as
267
well as orderly generation of isomorphism class representatives. See the
268
section :ref:`organization` for a list of supported constructors.
269
270
A list of all graphs and graph structures (other than isomorphism class
271
representatives) in this database is available via tab completion. Type
272
"graphs." and then hit the tab key to see which graphs are available.
273
274
The docstrings include educational information about each named
275
graph with the hopes that this class can be used as a reference.
276
277
For all the constructors in this class (except the octahedral,
278
dodecahedral, random and empty graphs), the position dictionary is
279
filled to override the spring-layout algorithm.
280
281
282
ORDERLY GENERATION::
283
284
graphs(vertices, property=lambda x: True, augment='edges', size=None)
285
286
This syntax accesses the generator of isomorphism class
287
representatives. Iterates over distinct, exhaustive
288
representatives.
289
290
Also: see the use of the optional nauty package for generating graphs
291
at the :meth:`nauty_geng` method.
292
293
INPUT:
294
295
- ``vertices`` -- natural number.
296
297
- ``property`` -- (default: ``lambda x: True``) any property to be
298
tested on graphs before generation, but note that in general the
299
graphs produced are not the same as those produced by using the
300
property function to filter a list of graphs produced by using
301
the ``lambda x: True`` default. The generation process assumes
302
the property has certain characteristics set by the ``augment``
303
argument, and only in the case of inherited properties such that
304
all subgraphs of the relevant kind (for ``augment='edges'`` or
305
``augment='vertices'``) of a graph with the property also
306
possess the property will there be no missing graphs. (The
307
``property`` argument is ignored if ``degree_sequence`` is
308
specified.)
309
310
- ``augment`` -- (default: ``'edges'``) possible values:
311
312
- ``'edges'`` -- augments a fixed number of vertices by
313
adding one edge. In this case, all graphs on exactly ``n=vertices`` are
314
generated. If for any graph G satisfying the property, every
315
subgraph, obtained from G by deleting one edge but not the vertices
316
incident to that edge, satisfies the property, then this will
317
generate all graphs with that property. If this does not hold, then
318
all the graphs generated will satisfy the property, but there will
319
be some missing.
320
321
- ``'vertices'`` -- augments by adding a vertex and
322
edges incident to that vertex. In this case, all graphs up to
323
``n=vertices`` are generated. If for any graph G satisfying the
324
property, every subgraph, obtained from G by deleting one vertex
325
and only edges incident to that vertex, satisfies the property,
326
then this will generate all graphs with that property. If this does
327
not hold, then all the graphs generated will satisfy the property,
328
but there will be some missing.
329
330
- ``size`` -- (default: ``None``) the size of the graph to be generated.
331
332
- ``degree_sequence`` -- (default: ``None``) a sequence of non-negative integers,
333
or ``None``. If specified, the generated graphs will have these
334
integers for degrees. In this case, property and size are both
335
ignored.
336
337
- ``loops`` -- (default: ``False``) whether to allow loops in the graph
338
or not.
339
340
- ``implementation`` -- (default: ``'c_graph'``) which underlying
341
implementation to use (see ``Graph?``).
342
343
- ``sparse`` -- (default: ``True``) ignored if implementation is not
344
``'c_graph'``.
345
346
- ``copy`` (boolean) -- If set to ``True`` (default)
347
this method makes copies of the graphs before returning
348
them. If set to ``False`` the method returns the graph it
349
is working on. The second alternative is faster, but modifying
350
any of the graph instances returned by the method may break
351
the function's behaviour, as it is using these graphs to
352
compute the next ones : only use ``copy_graph = False`` when
353
you stick to *reading* the graphs returned.
354
355
EXAMPLES:
356
357
Print graphs on 3 or less vertices::
358
359
sage: for G in graphs(3, augment='vertices'):
360
... print G
361
Graph on 0 vertices
362
Graph on 1 vertex
363
Graph on 2 vertices
364
Graph on 3 vertices
365
Graph on 3 vertices
366
Graph on 3 vertices
367
Graph on 2 vertices
368
Graph on 3 vertices
369
370
Note that we can also get graphs with underlying Cython implementation::
371
372
sage: for G in graphs(3, augment='vertices', implementation='c_graph'):
373
... print G
374
Graph on 0 vertices
375
Graph on 1 vertex
376
Graph on 2 vertices
377
Graph on 3 vertices
378
Graph on 3 vertices
379
Graph on 3 vertices
380
Graph on 2 vertices
381
Graph on 3 vertices
382
383
Print graphs on 3 vertices.
384
385
::
386
387
sage: for G in graphs(3):
388
... print G
389
Graph on 3 vertices
390
Graph on 3 vertices
391
Graph on 3 vertices
392
Graph on 3 vertices
393
394
Generate all graphs with 5 vertices and 4 edges.
395
396
::
397
398
sage: L = graphs(5, size=4)
399
sage: len(list(L))
400
6
401
402
Generate all graphs with 5 vertices and up to 4 edges.
403
404
::
405
406
sage: L = list(graphs(5, lambda G: G.size() <= 4))
407
sage: len(L)
408
14
409
sage: graphs_list.show_graphs(L) # long time
410
411
Generate all graphs with up to 5 vertices and up to 4 edges.
412
413
::
414
415
sage: L = list(graphs(5, lambda G: G.size() <= 4, augment='vertices'))
416
sage: len(L)
417
31
418
sage: graphs_list.show_graphs(L) # long time
419
420
Generate all graphs with degree at most 2, up to 6 vertices.
421
422
::
423
424
sage: property = lambda G: ( max([G.degree(v) for v in G] + [0]) <= 2 )
425
sage: L = list(graphs(6, property, augment='vertices'))
426
sage: len(L)
427
45
428
429
Generate all bipartite graphs on up to 7 vertices: (see
430
http://oeis.org/classic/A033995)
431
432
::
433
434
sage: L = list( graphs(7, lambda G: G.is_bipartite(), augment='vertices') )
435
sage: [len([g for g in L if g.order() == i]) for i in [1..7]]
436
[1, 2, 3, 7, 13, 35, 88]
437
438
Generate all bipartite graphs on exactly 7 vertices::
439
440
sage: L = list( graphs(7, lambda G: G.is_bipartite()) )
441
sage: len(L)
442
88
443
444
Generate all bipartite graphs on exactly 8 vertices::
445
446
sage: L = list( graphs(8, lambda G: G.is_bipartite()) ) # long time
447
sage: len(L) # long time
448
303
449
450
Remember that the property argument does not behave as a filter,
451
except for appropriately inheritable properties::
452
453
sage: property = lambda G: G.is_vertex_transitive()
454
sage: len(list(graphs(4, property)))
455
1
456
sage: len(filter(property, graphs(4)))
457
4
458
sage: property = lambda G: G.is_bipartite()
459
sage: len(list(graphs(4, property)))
460
7
461
sage: len(filter(property, graphs(4)))
462
7
463
464
Generate graphs on the fly: (see http://oeis.org/classic/A000088)
465
466
::
467
468
sage: for i in range(0, 7):
469
... print len(list(graphs(i)))
470
1
471
1
472
2
473
4
474
11
475
34
476
156
477
478
Generate all simple graphs, allowing loops: (see
479
http://oeis.org/classic/A000666)
480
481
::
482
483
sage: L = list(graphs(5,augment='vertices',loops=True)) # long time
484
sage: for i in [0..5]: print i, len([g for g in L if g.order() == i]) # long time
485
0 1
486
1 2
487
2 6
488
3 20
489
4 90
490
5 544
491
492
Generate all graphs with a specified degree sequence (see
493
http://oeis.org/classic/A002851)::
494
495
sage: for i in [4,6,8]: # long time (4s on sage.math, 2012)
496
... print i, len([g for g in graphs(i, degree_sequence=[3]*i) if g.is_connected()])
497
4 1
498
6 2
499
8 5
500
sage: for i in [4,6,8]: # long time (7s on sage.math, 2012)
501
... print i, len([g for g in graphs(i, augment='vertices', degree_sequence=[3]*i) if g.is_connected()])
502
4 1
503
6 2
504
8 5
505
506
::
507
508
sage: print 10, len([g for g in graphs(10,degree_sequence=[3]*10) if g.is_connected()]) # not tested
509
10 19
510
511
Make sure that the graphs are really independent and the generator
512
survives repeated vertex removal (trac 8458)::
513
514
sage: for G in graphs(3):
515
... G.delete_vertex(0)
516
... print(G.order())
517
2
518
2
519
2
520
2
521
522
REFERENCE:
523
524
- Brendan D. McKay, Isomorph-Free Exhaustive generation. *Journal
525
of Algorithms*, Volume 26, Issue 2, February 1998, pages 306-324.
526
"""
527
528
#######################################################################
529
# Basic Structures
530
#######################################################################
531
532
def BarbellGraph(self, n1, n2):
533
r"""
534
Returns a barbell graph with ``2*n1 + n2`` nodes. The argument ``n1``
535
must be greater than or equal to 2.
536
537
A barbell graph is a basic structure that consists of a path graph
538
of order ``n2`` connecting two complete graphs of order ``n1`` each.
539
540
This constructor depends on `NetworkX <http://networkx.lanl.gov>`_
541
numeric labels. In this case, the ``n1``-th node connects to the
542
path graph from one complete graph and the ``n1 + n2 + 1``-th node
543
connects to the path graph from the other complete graph.
544
545
INPUT:
546
547
- ``n1`` -- integer `\geq 2`. The order of each of the two
548
complete graphs.
549
550
- ``n2`` -- nonnegative integer. The order of the path graph
551
connecting the two complete graphs.
552
553
OUTPUT:
554
555
A barbell graph of order ``2*n1 + n2``. A ``ValueError`` is
556
returned if ``n1 < 2`` or ``n2 < 0``.
557
558
ALGORITHM:
559
560
Uses `NetworkX <http://networkx.lanl.gov>`_.
561
562
PLOTTING:
563
564
Upon construction, the position dictionary is filled to
565
override the spring-layout algorithm. By convention, each barbell
566
graph will be displayed with the two complete graphs in the
567
lower-left and upper-right corners, with the path graph connecting
568
diagonally between the two. Thus the ``n1``-th node will be drawn at a
569
45 degree angle from the horizontal right center of the first
570
complete graph, and the ``n1 + n2 + 1``-th node will be drawn 45
571
degrees below the left horizontal center of the second complete graph.
572
573
EXAMPLES:
574
575
Construct and show a barbell graph ``Bar = 4``, ``Bells = 9``::
576
577
sage: g = graphs.BarbellGraph(9, 4); g
578
Barbell graph: Graph on 22 vertices
579
sage: g.show() # long time
580
581
An ``n1 >= 2``, ``n2 >= 0`` barbell graph has order ``2*n1 + n2``. It
582
has the complete graph on ``n1`` vertices as a subgraph. It also has
583
the path graph on ``n2`` vertices as a subgraph. ::
584
585
sage: n1 = randint(2, 2*10^2)
586
sage: n2 = randint(0, 2*10^2)
587
sage: g = graphs.BarbellGraph(n1, n2)
588
sage: v = 2*n1 + n2
589
sage: g.order() == v
590
True
591
sage: K_n1 = graphs.CompleteGraph(n1)
592
sage: P_n2 = graphs.PathGraph(n2)
593
sage: s_K = g.subgraph_search(K_n1, induced=True)
594
sage: s_P = g.subgraph_search(P_n2, induced=True)
595
sage: K_n1.is_isomorphic(s_K)
596
True
597
sage: P_n2.is_isomorphic(s_P)
598
True
599
600
Create several barbell graphs in a Sage graphics array::
601
602
sage: g = []
603
sage: j = []
604
sage: for i in range(6):
605
... k = graphs.BarbellGraph(i + 2, 4)
606
... g.append(k)
607
...
608
sage: for i in range(2):
609
... n = []
610
... for m in range(3):
611
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
612
... j.append(n)
613
...
614
sage: G = sage.plot.graphics.GraphicsArray(j)
615
sage: G.show() # long time
616
617
TESTS:
618
619
The input ``n1`` must be `\geq 2`::
620
621
sage: graphs.BarbellGraph(1, randint(0, 10^6))
622
Traceback (most recent call last):
623
...
624
ValueError: Invalid graph description, n1 should be >= 2
625
sage: graphs.BarbellGraph(randint(-10^6, 1), randint(0, 10^6))
626
Traceback (most recent call last):
627
...
628
ValueError: Invalid graph description, n1 should be >= 2
629
630
The input ``n2`` must be `\geq 0`::
631
632
sage: graphs.BarbellGraph(randint(2, 10^6), -1)
633
Traceback (most recent call last):
634
...
635
ValueError: Invalid graph description, n2 should be >= 0
636
sage: graphs.BarbellGraph(randint(2, 10^6), randint(-10^6, -1))
637
Traceback (most recent call last):
638
...
639
ValueError: Invalid graph description, n2 should be >= 0
640
sage: graphs.BarbellGraph(randint(-10^6, 1), randint(-10^6, -1))
641
Traceback (most recent call last):
642
...
643
ValueError: Invalid graph description, n1 should be >= 2
644
"""
645
# sanity checks
646
if n1 < 2:
647
raise ValueError("Invalid graph description, n1 should be >= 2")
648
if n2 < 0:
649
raise ValueError("Invalid graph description, n2 should be >= 0")
650
651
pos_dict = {}
652
653
for i in range(n1):
654
x = float(cos((pi / 4) - ((2 * pi) / n1) * i) - (n2 / 2) - 1)
655
y = float(sin((pi / 4) - ((2 * pi) / n1) * i) - (n2 / 2) - 1)
656
j = n1 - 1 - i
657
pos_dict[j] = (x, y)
658
for i in range(n1, n1 + n2):
659
x = float(i - n1 - (n2 / 2) + 1)
660
y = float(i - n1 - (n2 / 2) + 1)
661
pos_dict[i] = (x, y)
662
for i in range(n1 + n2, (2 * n1) + n2):
663
x = float(
664
cos((5 * (pi / 4)) + ((2 * pi) / n1) * (i - n1 - n2))
665
+ (n2 / 2) + 2)
666
y = float(
667
sin((5 * (pi / 4)) + ((2 * pi) / n1) * (i - n1 - n2))
668
+ (n2 / 2) + 2)
669
pos_dict[i] = (x, y)
670
671
import networkx
672
G = networkx.barbell_graph(n1, n2)
673
return graph.Graph(G, pos=pos_dict, name="Barbell graph")
674
675
676
def BuckyBall(self):
677
r"""
678
Create the Bucky Ball graph.
679
680
This graph is a 3-regular 60-vertex planar graph. Its vertices
681
and edges correspond precisely to the carbon atoms and bonds
682
in buckminsterfullerene. When embedded on a sphere, its 12
683
pentagon and 20 hexagon faces are arranged exactly as the
684
sections of a soccer ball.
685
686
EXAMPLES:
687
688
The Bucky Ball is planar. ::
689
690
sage: g = graphs.BuckyBall()
691
sage: g.is_planar()
692
True
693
694
The Bucky Ball can also be created by extracting the 1-skeleton
695
of the Bucky Ball polyhedron, but this is much slower. ::
696
697
sage: g = polytopes.buckyball().vertex_graph()
698
sage: g.remove_loops()
699
sage: h = graphs.BuckyBall()
700
sage: g.is_isomorphic(h)
701
True
702
703
The graph is returned along with an attractive embedding. ::
704
705
sage: g = graphs.BuckyBall()
706
sage: g.plot(vertex_labels=False, vertex_size=10).show() # long time
707
"""
708
edges = [(0, 2), (0, 48), (0, 59), (1, 3), (1, 9), (1, 58),
709
(2, 3), (2, 36), (3, 17), (4, 6), (4, 8), (4, 12),
710
(5, 7), (5, 9), (5, 16), (6, 7), (6, 20), (7, 21),
711
(8, 9), (8, 56), (10, 11), (10, 12), (10, 20), (11, 27),
712
(11, 47), (12, 13), (13, 46), (13, 54), (14, 15), (14, 16),
713
(14, 21), (15, 25), (15, 41), (16, 17), (17, 40), (18, 19),
714
(18, 20), (18, 26), (19, 21), (19, 24), (22, 23), (22, 31),
715
(22, 34), (23, 25), (23, 38), (24, 25), (24, 30), (26, 27),
716
(26, 30), (27, 29), (28, 29), (28, 31), (28, 35), (29, 44),
717
(30, 31), (32, 34), (32, 39), (32, 50), (33, 35), (33, 45),
718
(33, 51), (34, 35), (36, 37), (36, 40), (37, 39), (37, 52),
719
(38, 39), (38, 41), (40, 41), (42, 43), (42, 46), (42, 55),
720
(43, 45), (43, 53), (44, 45), (44, 47), (46, 47), (48, 49),
721
(48, 52), (49, 53), (49, 57), (50, 51), (50, 52), (51, 53),
722
(54, 55), (54, 56), (55, 57), (56, 58), (57, 59), (58, 59)
723
]
724
g = graph.Graph()
725
g.add_edges(edges)
726
g.name("Bucky Ball")
727
728
pos = {
729
0 : (1.00000000000000, 0.000000000000000),
730
1 : (-1.00000000000000, 0.000000000000000),
731
2 : (0.500000000000000, 0.866025403784439),
732
3 : (-0.500000000000000, 0.866025403784439),
733
4 : (-0.252886764483159, -0.146004241548845),
734
5 : (-0.368953972399043, 0.0928336233191176),
735
6 : (-0.217853192651371, -0.0480798425451855),
736
7 : (-0.255589950938772, 0.0495517623332213),
737
8 : (-0.390242139418333, -0.225306404242310),
738
9 : (-0.586398703939125, -0.0441575936410641),
739
10: (-0.113926229169631, -0.101751920396670),
740
11: (-0.0461308635969359, -0.0928422349110366),
741
12: (-0.150564961379772, -0.164626477859040),
742
13: (-0.0848818904865275, -0.246123271631605),
743
14: (-0.170708060452244, 0.196571509298384),
744
15: (-0.0672882312715990, 0.212706320404226),
745
16: (-0.264873262319233, 0.273106701265196),
746
17: (-0.254957754106411, 0.529914971178085),
747
18: (-0.103469165775548, 0.00647061768205703),
748
19: (-0.113590051906687, 0.0655812470455896),
749
20: (-0.145082862532183, -0.0477870484199328),
750
21: (-0.179962687765901, 0.103901506225732),
751
22: (0.0573383021786124, 0.0863716172289798),
752
23: (0.0311566333625530, 0.149538968816603),
753
24: (-0.0573383021786121, 0.0863716172289799),
754
25: (-0.0311566333625527, 0.149538968816603),
755
26: (-0.0517345828877740, 0.00161765442051429),
756
27: (-0.0244663616211774, -0.0456122902452611),
757
28: (0.0517345828877743, 0.00161765442051431),
758
29: (0.0244663616211777, -0.0456122902452611),
759
30: (-0.0272682212665964, 0.0439946358247470),
760
31: (0.0272682212665968, 0.0439946358247470),
761
32: (0.179962687765901, 0.103901506225732),
762
33: (0.145082862532184, -0.0477870484199329),
763
34: (0.113590051906687, 0.0655812470455895),
764
35: (0.103469165775548, 0.00647061768205698),
765
36: (0.254957754106411, 0.529914971178085),
766
37: (0.264873262319233, 0.273106701265196),
767
38: (0.0672882312715993, 0.212706320404226),
768
39: (0.170708060452245, 0.196571509298384),
769
40: (1.59594559789866e-16, 0.450612808484620),
770
41: (2.01227923213310e-16, 0.292008483097691),
771
42: (0.0848818904865278, -0.246123271631605),
772
43: (0.150564961379773, -0.164626477859040),
773
44: (0.0461308635969362, -0.0928422349110366),
774
45: (0.113926229169631, -0.101751920396670),
775
46: (1.66533453693773e-16, -0.207803012451463),
776
47: (1.80411241501588e-16, -0.131162494091179),
777
48: (0.586398703939126, -0.0441575936410641),
778
49: (0.390242139418333, -0.225306404242310),
779
50: (0.255589950938772, 0.0495517623332212),
780
51: (0.217853192651372, -0.0480798425451855),
781
52: (0.368953972399044, 0.0928336233191175),
782
53: (0.252886764483159, -0.146004241548845),
783
54: (-0.104080710079810, -0.365940324584313),
784
55: (0.104080710079811, -0.365940324584313),
785
56: (-0.331440949832714, -0.485757377537020),
786
57: (0.331440949832715, -0.485757377537021),
787
58: (-0.500000000000000, -0.866025403784438),
788
59: (0.500000000000000, -0.866025403784439)
789
}
790
791
g.set_pos(pos)
792
793
return g
794
795
def BullGraph(self):
796
r"""
797
Returns a bull graph with 5 nodes.
798
799
A bull graph is named for its shape. It's a triangle with horns.
800
This constructor depends on `NetworkX <http://networkx.lanl.gov>`_
801
numeric labeling. For more information, see this
802
`Wikipedia article on the bull graph <http://en.wikipedia.org/wiki/Bull_graph>`_.
803
804
PLOTTING:
805
806
Upon construction, the position dictionary is filled to
807
override the spring-layout algorithm. By convention, the bull graph
808
is drawn as a triangle with the first node (0) on the bottom. The
809
second and third nodes (1 and 2) complete the triangle. Node 3 is
810
the horn connected to 1 and node 4 is the horn connected to node
811
2.
812
813
ALGORITHM:
814
815
Uses `NetworkX <http://networkx.lanl.gov>`_.
816
817
EXAMPLES:
818
819
Construct and show a bull graph::
820
821
sage: g = graphs.BullGraph(); g
822
Bull graph: Graph on 5 vertices
823
sage: g.show() # long time
824
825
The bull graph has 5 vertices and 5 edges. Its radius is 2, its
826
diameter 3, and its girth 3. The bull graph is planar with chromatic
827
number 3 and chromatic index also 3. ::
828
829
sage: g.order(); g.size()
830
5
831
5
832
sage: g.radius(); g.diameter(); g.girth()
833
2
834
3
835
3
836
sage: g.chromatic_number()
837
3
838
839
The bull graph has chromatic polynomial `x(x - 2)(x - 1)^3` and
840
Tutte polynomial `x^4 + x^3 + x^2 y`. Its characteristic polynomial
841
is `x(x^2 - x - 3)(x^2 + x - 1)`, which follows from the definition of
842
characteristic polynomials for graphs, i.e. `\det(xI - A)`, where
843
`x` is a variable, `A` the adjacency matrix of the graph, and `I`
844
the identity matrix of the same dimensions as `A`. ::
845
846
sage: chrompoly = g.chromatic_polynomial()
847
sage: bool(expand(x * (x - 2) * (x - 1)^3) == chrompoly)
848
True
849
sage: charpoly = g.characteristic_polynomial()
850
sage: M = g.adjacency_matrix(); M
851
[0 1 1 0 0]
852
[1 0 1 1 0]
853
[1 1 0 0 1]
854
[0 1 0 0 0]
855
[0 0 1 0 0]
856
sage: Id = identity_matrix(ZZ, M.nrows())
857
sage: D = x*Id - M
858
sage: bool(D.determinant() == charpoly)
859
True
860
sage: bool(expand(x * (x^2 - x - 3) * (x^2 + x - 1)) == charpoly)
861
True
862
"""
863
pos_dict = {0:(0,0), 1:(-1,1), 2:(1,1), 3:(-2,2), 4:(2,2)}
864
import networkx
865
G = networkx.bull_graph()
866
return graph.Graph(G, pos=pos_dict, name="Bull graph")
867
868
def ButterflyGraph(self):
869
r"""
870
Returns the butterfly graph.
871
872
Let `C_3` be the cycle graph on 3 vertices. The butterfly or bowtie
873
graph is obtained by joining two copies of `C_3` at a common vertex,
874
resulting in a graph that is isomorphic to the friendship graph `F_2`.
875
For more information, see this
876
`Wikipedia article on the butterfly graph <http://en.wikipedia.org/wiki/Butterfly_graph>`_.
877
878
.. seealso::
879
880
- :meth:`GraphGenerators.FriendshipGraph`
881
882
EXAMPLES:
883
884
The butterfly graph is a planar graph on 5 vertices and having
885
6 edges. ::
886
887
sage: G = graphs.ButterflyGraph(); G
888
Butterfly graph: Graph on 5 vertices
889
sage: G.show() # long time
890
sage: G.is_planar()
891
True
892
sage: G.order()
893
5
894
sage: G.size()
895
6
896
897
It has diameter 2, girth 3, and radius 1. ::
898
899
sage: G.diameter()
900
2
901
sage: G.girth()
902
3
903
sage: G.radius()
904
1
905
906
The butterfly graph is Eulerian, with chromatic number 3. ::
907
908
sage: G.is_eulerian()
909
True
910
sage: G.chromatic_number()
911
3
912
"""
913
edge_dict = {
914
0: [3,4],
915
1: [2,4],
916
2: [4],
917
3: [4]}
918
pos_dict = {
919
0: [-1, 1],
920
1: [1, 1],
921
2: [1, -1],
922
3: [-1, -1],
923
4: [0, 0]}
924
return graph.Graph(edge_dict, pos=pos_dict, name="Butterfly graph")
925
926
def CircularLadderGraph(self, n):
927
"""
928
Returns a circular ladder graph with 2\*n nodes.
929
930
A Circular ladder graph is a ladder graph that is connected at the
931
ends, i.e.: a ladder bent around so that top meets bottom. Thus it
932
can be described as two parallel cycle graphs connected at each
933
corresponding node pair.
934
935
This constructor depends on NetworkX numeric labels.
936
937
PLOTTING: Upon construction, the position dictionary is filled to
938
override the spring-layout algorithm. By convention, the circular
939
ladder graph is displayed as an inner and outer cycle pair, with
940
the first n nodes drawn on the inner circle. The first (0) node is
941
drawn at the top of the inner-circle, moving clockwise after that.
942
The outer circle is drawn with the (n+1)th node at the top, then
943
counterclockwise as well.
944
945
EXAMPLES: Construct and show a circular ladder graph with 26 nodes
946
947
::
948
949
sage: g = graphs.CircularLadderGraph(13)
950
sage: g.show() # long time
951
952
Create several circular ladder graphs in a Sage graphics array
953
954
::
955
956
sage: g = []
957
sage: j = []
958
sage: for i in range(9):
959
... k = graphs.CircularLadderGraph(i+3)
960
... g.append(k)
961
...
962
sage: for i in range(3):
963
... n = []
964
... for m in range(3):
965
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
966
... j.append(n)
967
...
968
sage: G = sage.plot.graphics.GraphicsArray(j)
969
sage: G.show() # long time
970
"""
971
pos_dict = {}
972
for i in range(n):
973
x = float(cos((pi/2) + ((2*pi)/n)*i))
974
y = float(sin((pi/2) + ((2*pi)/n)*i))
975
pos_dict[i] = [x,y]
976
for i in range(n,2*n):
977
x = float(2*(cos((pi/2) + ((2*pi)/n)*(i-n))))
978
y = float(2*(sin((pi/2) + ((2*pi)/n)*(i-n))))
979
pos_dict[i] = (x,y)
980
import networkx
981
G = networkx.circular_ladder_graph(n)
982
return graph.Graph(G, pos=pos_dict, name="Circular Ladder graph")
983
984
def ClawGraph(self):
985
"""
986
Returns a claw graph.
987
988
A claw graph is named for its shape. It is actually a complete
989
bipartite graph with (n1, n2) = (1, 3).
990
991
PLOTTING: See CompleteBipartiteGraph.
992
993
EXAMPLES: Show a Claw graph
994
995
::
996
997
sage: (graphs.ClawGraph()).show() # long time
998
999
Inspect a Claw graph
1000
1001
::
1002
1003
sage: G = graphs.ClawGraph()
1004
sage: G
1005
Claw graph: Graph on 4 vertices
1006
"""
1007
pos_dict = {0:(0,1),1:(-1,0),2:(0,0),3:(1,0)}
1008
import networkx
1009
G = networkx.complete_bipartite_graph(1,3)
1010
return graph.Graph(G, pos=pos_dict, name="Claw graph")
1011
1012
def CycleGraph(self, n):
1013
r"""
1014
Returns a cycle graph with n nodes.
1015
1016
A cycle graph is a basic structure which is also typically called
1017
an n-gon.
1018
1019
This constructor is dependent on vertices numbered 0 through n-1 in
1020
NetworkX ``cycle_graph()``
1021
1022
PLOTTING: Upon construction, the position dictionary is filled to
1023
override the spring-layout algorithm. By convention, each cycle
1024
graph will be displayed with the first (0) node at the top, with
1025
the rest following in a counterclockwise manner.
1026
1027
The cycle graph is a good opportunity to compare efficiency of
1028
filling a position dictionary vs. using the spring-layout algorithm
1029
for plotting. Because the cycle graph is very symmetric, the
1030
resulting plots should be similar (in cases of small n).
1031
1032
Filling the position dictionary in advance adds O(n) to the
1033
constructor.
1034
1035
EXAMPLES: Compare plotting using the predefined layout and
1036
networkx::
1037
1038
sage: import networkx
1039
sage: n = networkx.cycle_graph(23)
1040
sage: spring23 = Graph(n)
1041
sage: posdict23 = graphs.CycleGraph(23)
1042
sage: spring23.show() # long time
1043
sage: posdict23.show() # long time
1044
1045
We next view many cycle graphs as a Sage graphics array. First we
1046
use the ``CycleGraph`` constructor, which fills in the
1047
position dictionary::
1048
1049
sage: g = []
1050
sage: j = []
1051
sage: for i in range(9):
1052
... k = graphs.CycleGraph(i+3)
1053
... g.append(k)
1054
...
1055
sage: for i in range(3):
1056
... n = []
1057
... for m in range(3):
1058
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
1059
... j.append(n)
1060
...
1061
sage: G = sage.plot.graphics.GraphicsArray(j)
1062
sage: G.show() # long time
1063
1064
Compare to plotting with the spring-layout algorithm::
1065
1066
sage: g = []
1067
sage: j = []
1068
sage: for i in range(9):
1069
... spr = networkx.cycle_graph(i+3)
1070
... k = Graph(spr)
1071
... g.append(k)
1072
...
1073
sage: for i in range(3):
1074
... n = []
1075
... for m in range(3):
1076
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
1077
... j.append(n)
1078
...
1079
sage: G = sage.plot.graphics.GraphicsArray(j)
1080
sage: G.show() # long time
1081
"""
1082
pos_dict = {}
1083
for i in range(n):
1084
x = float(cos((pi/2) + ((2*pi)/n)*i))
1085
y = float(sin((pi/2) + ((2*pi)/n)*i))
1086
pos_dict[i] = (x,y)
1087
import networkx
1088
G = networkx.cycle_graph(n)
1089
return graph.Graph(G, pos=pos_dict, name="Cycle graph")
1090
1091
def DiamondGraph(self):
1092
"""
1093
Returns a diamond graph with 4 nodes.
1094
1095
A diamond graph is a square with one pair of diagonal nodes
1096
connected.
1097
1098
This constructor depends on NetworkX numeric labeling.
1099
1100
PLOTTING: Upon construction, the position dictionary is filled to
1101
override the spring-layout algorithm. By convention, the diamond
1102
graph is drawn as a diamond, with the first node on top, second on
1103
the left, third on the right, and fourth on the bottom; with the
1104
second and third node connected.
1105
1106
EXAMPLES: Construct and show a diamond graph
1107
1108
::
1109
1110
sage: g = graphs.DiamondGraph()
1111
sage: g.show() # long time
1112
"""
1113
pos_dict = {0:(0,1),1:(-1,0),2:(1,0),3:(0,-1)}
1114
import networkx
1115
G = networkx.diamond_graph()
1116
return graph.Graph(G, pos=pos_dict, name="Diamond Graph")
1117
1118
def EmptyGraph(self):
1119
"""
1120
Returns an empty graph (0 nodes and 0 edges).
1121
1122
This is useful for constructing graphs by adding edges and vertices
1123
individually or in a loop.
1124
1125
PLOTTING: When plotting, this graph will use the default
1126
spring-layout algorithm, unless a position dictionary is
1127
specified.
1128
1129
EXAMPLES: Add one vertex to an empty graph and then show::
1130
1131
sage: empty1 = graphs.EmptyGraph()
1132
sage: empty1.add_vertex()
1133
0
1134
sage: empty1.show() # long time
1135
1136
Use for loops to build a graph from an empty graph::
1137
1138
sage: empty2 = graphs.EmptyGraph()
1139
sage: for i in range(5):
1140
... empty2.add_vertex() # add 5 nodes, labeled 0-4
1141
...
1142
0
1143
1
1144
2
1145
3
1146
4
1147
sage: for i in range(3):
1148
... empty2.add_edge(i,i+1) # add edges {[0:1],[1:2],[2:3]}
1149
...
1150
sage: for i in range(4)[1:]:
1151
... empty2.add_edge(4,i) # add edges {[1:4],[2:4],[3:4]}
1152
...
1153
sage: empty2.show() # long time
1154
"""
1155
return graph.Graph(sparse=True)
1156
1157
def ToroidalGrid2dGraph(self,n1,n2):
1158
r"""
1159
Returns a toroidal 2-dimensional grid graph with `n_1n_2` nodes
1160
(`n_1` rows and `n_2` columns).
1161
1162
The toroidal 2-dimensional grid with parameters `n_1,n_2` is
1163
the 2-dimensional grid graph with identical parameters
1164
to which are added the edges `((i,0),(i,n_2-1))` and
1165
`((0,i),(n_1-1,i))`.
1166
1167
EXAMPLE:
1168
1169
The toroidal 2-dimensional grid is a regular graph, while
1170
the usual 2-dimensional grid is not ::
1171
1172
sage: tgrid = graphs.ToroidalGrid2dGraph(8,9)
1173
sage: print tgrid
1174
Toroidal 2D Grid Graph with parameters 8,9
1175
sage: grid = graphs.Grid2dGraph(8,9)
1176
sage: grid.is_regular()
1177
False
1178
sage: tgrid.is_regular()
1179
True
1180
"""
1181
1182
g = self.Grid2dGraph(n1,n2)
1183
1184
g.add_edges([((i,0),(i,n2-1)) for i in range(n1)] + [((0,i),(n1-1,i)) for i in range(n2)])
1185
1186
g.name("Toroidal 2D Grid Graph with parameters "+str(n1)+","+str(n2))
1187
1188
return g
1189
1190
def Grid2dGraph(self, n1, n2):
1191
r"""
1192
Returns a `2`-dimensional grid graph with `n_1n_2` nodes (`n_1` rows and
1193
`n_2` columns).
1194
1195
A 2d grid graph resembles a `2` dimensional grid. All inner nodes are
1196
connected to their `4` neighbors. Outer (non-corner) nodes are
1197
connected to their `3` neighbors. Corner nodes are connected to their
1198
2 neighbors.
1199
1200
This constructor depends on NetworkX numeric labels.
1201
1202
PLOTTING: Upon construction, the position dictionary is filled to
1203
override the spring-layout algorithm. By convention, nodes are
1204
labelled in (row, column) pairs with `(0, 0)` in the top left corner.
1205
Edges will always be horizontal and vertical - another advantage of
1206
filling the position dictionary.
1207
1208
EXAMPLES: Construct and show a grid 2d graph Rows = `5`, Columns = `7`
1209
1210
::
1211
1212
sage: g = graphs.Grid2dGraph(5,7)
1213
sage: g.show() # long time
1214
"""
1215
pos_dict = {}
1216
for i in range(n1):
1217
y = -i
1218
for j in range(n2):
1219
x = j
1220
pos_dict[i,j] = (x,y)
1221
import networkx
1222
G = networkx.grid_2d_graph(n1,n2)
1223
return graph.Graph(G, pos=pos_dict, name="2D Grid Graph")
1224
1225
def GridGraph(self, dim_list):
1226
"""
1227
Returns an n-dimensional grid graph.
1228
1229
INPUT:
1230
1231
1232
- ``dim_list`` - a list of integers representing the
1233
number of nodes to extend in each dimension.
1234
1235
1236
PLOTTING: When plotting, this graph will use the default
1237
spring-layout algorithm, unless a position dictionary is
1238
specified.
1239
1240
EXAMPLES::
1241
1242
sage: G = graphs.GridGraph([2,3,4])
1243
sage: G.show() # long time
1244
1245
::
1246
1247
sage: C = graphs.CubeGraph(4)
1248
sage: G = graphs.GridGraph([2,2,2,2])
1249
sage: C.show() # long time
1250
sage: G.show() # long time
1251
"""
1252
import networkx
1253
dim = [int(a) for a in dim_list]
1254
G = networkx.grid_graph(dim)
1255
return graph.Graph(G, name="Grid Graph for %s"%dim)
1256
1257
def HanoiTowerGraph(self, pegs, disks, labels=True, positions=True):
1258
r"""
1259
Returns the graph whose vertices are the states of the
1260
Tower of Hanoi puzzle, with edges representing legal moves between states.
1261
1262
INPUT:
1263
1264
- ``pegs`` - the number of pegs in the puzzle, 2 or greater
1265
- ``disks`` - the number of disks in the puzzle, 1 or greater
1266
- ``labels`` - default: ``True``, if ``True`` the graph contains
1267
more meaningful labels, see explanation below. For large instances,
1268
turn off labels for much faster creation of the graph.
1269
- ``positions`` - default: ``True``, if ``True`` the graph contains
1270
layout information. This creates a planar layout for the case
1271
of three pegs. For large instances, turn off layout information
1272
for much faster creation of the graph.
1273
1274
OUTPUT:
1275
1276
The Tower of Hanoi puzzle has a certain number of identical pegs
1277
and a certain number of disks, each of a different radius.
1278
Initially the disks are all on a single peg, arranged
1279
in order of their radii, with the largest on the bottom.
1280
1281
The goal of the puzzle is to move the disks to any other peg,
1282
arranged in the same order. The one constraint is that the
1283
disks resident on any one peg must always be arranged with larger
1284
radii lower down.
1285
1286
The vertices of this graph represent all the possible states
1287
of this puzzle. Each state of the puzzle is a tuple with length
1288
equal to the number of disks, ordered by largest disk first.
1289
The entry of the tuple is the peg where that disk resides.
1290
Since disks on a given peg must go down in size as we go
1291
up the peg, this totally describes the state of the puzzle.
1292
1293
For example ``(2,0,0)`` means the large disk is on peg 2, the
1294
medium disk is on peg 0, and the small disk is on peg 0
1295
(and we know the small disk must be above the medium disk).
1296
We encode these tuples as integers with a base equal to
1297
the number of pegs, and low-order digits to the right.
1298
1299
Two vertices are adjacent if we can change the puzzle from
1300
one state to the other by moving a single disk. For example,
1301
``(2,0,0)`` is adjacent to ``(2,0,1)`` since we can move
1302
the small disk off peg 0 and onto (the empty) peg 1.
1303
So the solution to a 3-disk puzzle (with at least
1304
two pegs) can be expressed by the shortest path between
1305
``(0,0,0)`` and ``(1,1,1)``. For more on this representation
1306
of the graph, or its properties, see [ARETT-DOREE]_.
1307
1308
For greatest speed we create graphs with integer vertices,
1309
where we encode the tuples as integers with a base equal
1310
to the number of pegs, and low-order digits to the right.
1311
So for example, in a 3-peg puzzle with 5 disks, the
1312
state ``(1,2,0,1,1)`` is encoded as
1313
`1\ast 3^4 + 2\ast 3^3 + 0\ast 3^2 + 1\ast 3^1 + 1\ast 3^0 = 139`.
1314
1315
For smaller graphs, the labels that are the tuples are informative,
1316
but slow down creation of the graph. Likewise computing layout
1317
information also incurs a significant speed penalty. For maximum
1318
speed, turn off labels and layout and decode the
1319
vertices explicitly as needed. The
1320
:meth:`sage.rings.integer.Integer.digits`
1321
with the ``padsto`` option is a quick way to do this, though you
1322
may want to reverse the list that is output.
1323
1324
PLOTTING:
1325
1326
The layout computed when ``positions = True`` will
1327
look especially good for the three-peg case, when the graph is known
1328
to be planar. Except for two small cases on 4 pegs, the graph is
1329
otherwise not planar, and likely there is a better way to layout
1330
the vertices.
1331
1332
EXAMPLES:
1333
1334
A classic puzzle uses 3 pegs. We solve the 5 disk puzzle using
1335
integer labels and report the minimum number of moves required.
1336
Note that `3^5-1` is the state where all 5 disks
1337
are on peg 2. ::
1338
1339
sage: H = graphs.HanoiTowerGraph(3, 5, labels=False, positions=False)
1340
sage: H.distance(0, 3^5-1)
1341
31
1342
1343
A slightly larger instance. ::
1344
1345
sage: H = graphs.HanoiTowerGraph(4, 6, labels=False, positions=False)
1346
sage: H.num_verts()
1347
4096
1348
sage: H.distance(0, 4^6-1)
1349
17
1350
1351
For a small graph, labels and layout information can be useful.
1352
Here we explicitly list a solution as a list of states. ::
1353
1354
sage: H = graphs.HanoiTowerGraph(3, 3, labels=True, positions=True)
1355
sage: H.shortest_path((0,0,0), (1,1,1))
1356
[(0, 0, 0), (0, 0, 1), (0, 2, 1), (0, 2, 2), (1, 2, 2), (1, 2, 0), (1, 1, 0), (1, 1, 1)]
1357
1358
Some facts about this graph with `p` pegs and `d` disks:
1359
1360
- only automorphisms are the "obvious" ones - renumber the pegs.
1361
- chromatic number is less than or equal to `p`
1362
- independence number is `p^{d-1}`
1363
1364
::
1365
1366
sage: H = graphs.HanoiTowerGraph(3,4,labels=False,positions=False)
1367
sage: H.automorphism_group().is_isomorphic(SymmetricGroup(3))
1368
True
1369
sage: H.chromatic_number()
1370
3
1371
sage: len(H.independent_set()) == 3^(4-1)
1372
True
1373
1374
TESTS:
1375
1376
It is an error to have just one peg (or less). ::
1377
1378
sage: graphs.HanoiTowerGraph(1, 5)
1379
Traceback (most recent call last):
1380
...
1381
ValueError: Pegs for Tower of Hanoi graph should be two or greater (not 1)
1382
1383
It is an error to have zero disks (or less). ::
1384
1385
sage: graphs.HanoiTowerGraph(2, 0)
1386
Traceback (most recent call last):
1387
...
1388
ValueError: Disks for Tower of Hanoi graph should be one or greater (not 0)
1389
1390
.. rubric:: Citations
1391
1392
.. [ARETT-DOREE] Arett, Danielle and Doree, Suzanne
1393
"Coloring and counting on the Hanoi graphs"
1394
Mathematics Magazine, Volume 83, Number 3, June 2010, pages 200-9
1395
1396
1397
AUTHOR:
1398
1399
- Rob Beezer, (2009-12-26), with assistance from Su Doree
1400
1401
"""
1402
1403
# sanitize input
1404
from sage.rings.all import Integer
1405
pegs = Integer(pegs)
1406
if pegs < 2:
1407
raise ValueError("Pegs for Tower of Hanoi graph should be two or greater (not %d)" % pegs)
1408
disks = Integer(disks)
1409
if disks < 1:
1410
raise ValueError("Disks for Tower of Hanoi graph should be one or greater (not %d)" % disks)
1411
1412
# Each state of the puzzle is a tuple with length
1413
# equal to the number of disks, ordered by largest disk first
1414
# The entry of the tuple is the peg where that disk resides
1415
# Since disks on a given peg must go down in size as we go
1416
# up the peg, this totally describes the puzzle
1417
# We encode these tuples as integers with a base equal to
1418
# the number of pegs, and low-order digits to the right
1419
1420
# complete graph on number of pegs when just a single disk
1421
edges = [[i,j] for i in range(pegs) for j in range(i+1,pegs)]
1422
1423
nverts = 1
1424
for d in range(2, disks+1):
1425
prevedges = edges # remember subgraph to build from
1426
nverts = pegs*nverts # pegs^(d-1)
1427
edges = []
1428
1429
# Take an edge, change its two states in the same way by adding
1430
# a large disk to the bottom of the same peg in each state
1431
# This is accomplished by adding a multiple of pegs^(d-1)
1432
for p in range(pegs):
1433
largedisk = p*nverts
1434
for anedge in prevedges:
1435
edges.append([anedge[0]+largedisk, anedge[1]+largedisk])
1436
1437
# Two new states may only differ in the large disk
1438
# being the only disk on two different pegs, thus
1439
# otherwise being a common state with one less disk
1440
# We construct all such pairs of new states and add as edges
1441
from sage.combinat.subset import Subsets
1442
for state in range(nverts):
1443
emptypegs = range(pegs)
1444
reduced_state = state
1445
for i in range(d-1):
1446
apeg = reduced_state % pegs
1447
if apeg in emptypegs:
1448
emptypegs.remove(apeg)
1449
reduced_state = reduced_state//pegs
1450
for freea, freeb in Subsets(emptypegs, 2):
1451
edges.append([freea*nverts+state,freeb*nverts+state])
1452
1453
H = graph.Graph({}, loops=False, multiedges=False)
1454
H.add_edges(edges)
1455
1456
1457
# Making labels and/or computing positions can take a long time,
1458
# relative to just constructing the edges on integer vertices.
1459
# We try to minimize coercion overhead, but need Sage
1460
# Integers in order to use digits() for labels.
1461
# Getting the digits with custom code was no faster.
1462
# Layouts are circular (symmetric on the number of pegs)
1463
# radiating outward to the number of disks (radius)
1464
# Algorithm uses some combination of alternate
1465
# clockwise/counterclockwise placements, which
1466
# works well for three pegs (planar layout)
1467
#
1468
from sage.functions.trig import sin, cos, csc
1469
if labels or positions:
1470
mapping = {}
1471
pos = {}
1472
a = Integer(-1)
1473
one = Integer(1)
1474
if positions:
1475
radius_multiplier = 1 + csc(pi/pegs)
1476
sine = []; cosine = []
1477
for i in range(pegs):
1478
angle = 2*i*pi/float(pegs)
1479
sine.append(sin(angle))
1480
cosine.append(cos(angle))
1481
for i in range(pegs**disks):
1482
a += one
1483
state = a.digits(base=pegs, padto=disks)
1484
if labels:
1485
state.reverse()
1486
mapping[i] = tuple(state)
1487
state.reverse()
1488
if positions:
1489
locx = 0.0; locy = 0.0
1490
radius = 1.0
1491
parity = -1.0
1492
for index in range(disks):
1493
p = state[index]
1494
radius *= radius_multiplier
1495
parity *= -1.0
1496
locx_temp = cosine[p]*locx - parity*sine[p]*locy + radius*cosine[p]
1497
locy_temp = parity*sine[p]*locx + cosine[p]*locy - radius*parity*sine[p]
1498
locx = locx_temp
1499
locy = locy_temp
1500
pos[i] = (locx,locy)
1501
# set positions, then relabel (not vice versa)
1502
if positions:
1503
H.set_pos(pos)
1504
if labels:
1505
H.relabel(mapping)
1506
1507
return H
1508
1509
def HararyGraph( self, k, n ):
1510
r"""
1511
Returns the Harary graph on `n` vertices and connectivity `k`, where
1512
`2 \leq k < n`.
1513
1514
A `k`-connected graph `G` on `n` vertices requires the minimum degree
1515
`\delta(G)\geq k`, so the minimum number of edges `G` should have is
1516
`\lceil kn/2\rceil`. Harary graphs achieve this lower bound, that is,
1517
Harary graphs are minimal `k`-connected graphs on `n` vertices.
1518
1519
The construction provided uses the method CirculantGraph. For more
1520
details, see the book D. B. West, Introduction to Graph Theory, 2nd
1521
Edition, Prentice Hall, 2001, p. 150--151; or the `MathWorld article on
1522
Harary graphs <http://mathworld.wolfram.com/HararyGraph.html>`_.
1523
1524
EXAMPLES:
1525
1526
Harary graphs `H_{k,n}`::
1527
1528
sage: h = graphs.HararyGraph(5,9); h
1529
Harary graph 5, 9: Graph on 9 vertices
1530
sage: h.order()
1531
9
1532
sage: h.size()
1533
23
1534
sage: h.vertex_connectivity()
1535
5
1536
1537
TESTS:
1538
1539
Connectivity of some Harary graphs::
1540
1541
sage: n=10
1542
sage: for k in range(2,n):
1543
... g = graphs.HararyGraph(k,n)
1544
... if k != g.vertex_connectivity():
1545
... print "Connectivity of Harary graphs not satisfied."
1546
"""
1547
if k < 2:
1548
raise ValueError("Connectivity parameter k should be at least 2.")
1549
if k >= n:
1550
raise ValueError("Number of vertices n should be greater than k.")
1551
1552
if k%2 == 0:
1553
G = self.CirculantGraph( n, range(1,k/2+1) )
1554
else:
1555
if n%2 == 0:
1556
G = self.CirculantGraph( n, range(1,(k-1)/2+1) )
1557
for i in range(n):
1558
G.add_edge( i, (i+n/2)%n )
1559
else:
1560
G = self.HararyGraph( k-1, n )
1561
for i in range((n-1)/2+1):
1562
G.add_edge( i, (i+(n-1)/2)%n )
1563
G.name('Harary graph {0}, {1}'.format(k,n))
1564
return G
1565
1566
def HarriesGraph(self, embedding=1):
1567
r"""
1568
Returns the Harries Graph.
1569
1570
The Harries graph is a Hamiltonian 3-regular graph on 70
1571
vertices. See the :wikipedia:`Wikipedia page on the Harries
1572
graph <Harries_graph>`.
1573
1574
The default embedding here is to emphasize the graph's 4 orbits.
1575
This graph actually has a funny construction. The following
1576
procedure gives an idea of it, though not all the adjacencies
1577
are being properly defined.
1578
1579
#. Take two disjoint copies of a :meth:`Petersen graph
1580
<PetersenGraph>`. Their vertices will form an orbit of the
1581
final graph.
1582
1583
#. Subdivide all the edges once, to create 15+15=30 new
1584
vertices, which together form another orbit.
1585
1586
#. Create 15 vertices, each of them linked to 2 corresponding
1587
vertices of the previous orbit, one in each of the two
1588
subdivided Petersen graphs. At the end of this step all
1589
vertices from the previous orbit have degree 3, and the only
1590
vertices of degree 2 in the graph are those that were just
1591
created.
1592
1593
#. Create 5 vertices connected only to the ones from the
1594
previous orbit so that the graph becomes 3-regular.
1595
1596
INPUT:
1597
1598
- ``embedding`` -- two embeddings are available, and can be
1599
selected by setting ``embedding`` to 1 or 2.
1600
1601
EXAMPLES::
1602
1603
sage: g = graphs.HarriesGraph()
1604
sage: g.order()
1605
70
1606
sage: g.size()
1607
105
1608
sage: g.girth()
1609
10
1610
sage: g.diameter()
1611
6
1612
sage: g.show(figsize=[10, 10]) # long time
1613
sage: graphs.HarriesGraph(embedding=2).show(figsize=[10, 10]) # long time
1614
1615
TESTS::
1616
1617
sage: graphs.HarriesGraph(embedding=3)
1618
Traceback (most recent call last):
1619
...
1620
ValueError: The value of embedding must be 1 or 2.
1621
1622
"""
1623
g = graphs.LCFGraph(70, [-29, -19, -13, 13, 21, -27, 27, 33, -13, 13,
1624
19, -21, -33, 29], 5)
1625
g.name("Harries Graph")
1626
1627
if embedding == 1:
1628
gpos = g.get_pos()
1629
ppos = graphs.PetersenGraph().get_pos()
1630
1631
# The graph's four orbits
1632
o = [None]*4
1633
o[0] = [0, 2, 6, 8, 14, 16, 20, 22, 28, 30, 34, 36, 42, 44, 48, 50,
1634
56, 58, 62, 64]
1635
o[1] = [1, 3, 5, 7, 9, 13, 15, 17, 19, 21, 23, 27, 29, 31, 33, 35,
1636
37, 41, 43, 45, 47, 49, 51, 55, 57, 59, 61, 63, 65, 69]
1637
o[2] = [60, 10, 12, 4, 24, 26, 18, 38, 40, 32, 52, 54, 46, 66, 68]
1638
o[3] = [11, 25, 39, 53, 67]
1639
1640
# Correspondence between the vertices of one of the two Petersen
1641
# graphs on o[0] and the vertices of a standard Petersen graph
1642
# object
1643
g_to_p = {0: 0, 2: 1, 42: 5, 44: 8, 14: 7, 16: 2, 56: 9, 58: 6,
1644
28: 4, 30: 3}
1645
1646
# Correspondence between the vertices of the other Petersen graph
1647
# on o[0] and the vertices of the first one
1648
g_to_g = {64: 44, 34: 0, 36: 28, 6: 2, 8: 58, 48: 16, 50: 30,
1649
20: 14, 22: 56, 62: 42}
1650
1651
# Position for the vertices from the first copy
1652
for v, i in g_to_p.iteritems():
1653
gpos[v] = ppos[i]
1654
1655
# Position for the vertices in the second copy. Moves the first,
1656
# too.
1657
offset = 3.5
1658
for v, i in g_to_g.iteritems():
1659
x, y = gpos[i]
1660
gpos[v] = (x + offset*0.5, y)
1661
gpos[i] = (x - offset*0.5, y)
1662
1663
# Vertices from o[1]. These are actually the "edges" of the
1664
# copies of Petersen.
1665
for v in o[1]:
1666
p1, p2 = [gpos[x] for x in g.neighbors(v) if x in o[0]]
1667
gpos[v] = ((p1[0] + p2[0])/2, (p1[1] + p2[1])/2)
1668
1669
# 15 vertices from o[2]
1670
for i, v in enumerate(o[2]):
1671
gpos[v] = (-1.75 + i*.25, 2)
1672
1673
# 5 vertices from o[3]
1674
for i, v in enumerate(o[3]):
1675
gpos[v] = (-1 + i*.5, 2.5)
1676
1677
return g
1678
1679
elif embedding == 2:
1680
return g
1681
else:
1682
raise ValueError("The value of embedding must be 1 or 2.")
1683
1684
def HarriesWongGraph(self, embedding=1):
1685
r"""
1686
Returns the Harries-Wong Graph.
1687
1688
See the :wikipedia:`Wikipedia page on the Harries-Wong graph
1689
<Harries-Wong_graph>`.
1690
1691
*About the default embedding:*
1692
1693
The default embedding is an attempt to emphasize the graph's
1694
8 (!!!) different orbits. In order to understand this better,
1695
one can picture the graph as being built in the following way:
1696
1697
#. One first creates a 3-dimensional cube (8 vertices, 12
1698
edges), whose vertices define the first orbit of the
1699
final graph.
1700
1701
#. The edges of this graph are subdivided once, to create 12
1702
new vertices which define a second orbit.
1703
1704
#. The edges of the graph are subdivided once more, to
1705
create 24 new vertices giving a third orbit.
1706
1707
#. 4 vertices are created and made adjacent to the vertices
1708
of the second orbit so that they have degree
1709
3. These 4 vertices also define a new orbit.
1710
1711
#. In order to make the vertices from the third orbit
1712
3-regular (they all miss one edge), one creates a binary
1713
tree on 1 + 3 + 6 + 12 vertices. The leaves of this new
1714
tree are made adjacent to the 12 vertices of the third
1715
orbit, and the graph is now 3-regular. This binary tree
1716
contributes 4 new orbits to the Harries-Wong graph.
1717
1718
INPUT:
1719
1720
- ``embedding`` -- two embeddings are available, and can be
1721
selected by setting ``embedding`` to 1 or 2.
1722
1723
EXAMPLES::
1724
1725
sage: g = graphs.HarriesWongGraph()
1726
sage: g.order()
1727
70
1728
sage: g.size()
1729
105
1730
sage: g.girth()
1731
10
1732
sage: g.diameter()
1733
6
1734
sage: orbits = g.automorphism_group(orbits=True)[-1]
1735
sage: g.show(figsize=[15, 15], partition=orbits) # long time
1736
1737
Alternative embedding::
1738
1739
sage: graphs.HarriesWongGraph(embedding=2).show()
1740
1741
TESTS::
1742
1743
sage: graphs.HarriesWongGraph(embedding=3)
1744
Traceback (most recent call last):
1745
...
1746
ValueError: The value of embedding must be 1 or 2.
1747
"""
1748
1749
L = [9, 25, 31, -17, 17, 33, 9, -29, -15, -9, 9, 25, -25, 29, 17, -9,
1750
9, -27, 35, -9, 9, -17, 21, 27, -29, -9, -25, 13, 19, -9, -33,
1751
-17, 19, -31, 27, 11, -25, 29, -33, 13, -13, 21, -29, -21, 25,
1752
9, -11, -19, 29, 9, -27, -19, -13, -35, -9, 9, 17, 25, -9, 9, 27,
1753
-27, -21, 15, -9, 29, -29, 33, -9, -25]
1754
1755
g = graphs.LCFGraph(70, L, 1)
1756
g.name("Harries-Wong graph")
1757
1758
if embedding == 1:
1759
d = g.get_pos()
1760
1761
# Binary tree (left side)
1762
d[66] = (-9.5, 0)
1763
_line_embedding(g, [37, 65, 67], first=(-8, 2.25),
1764
last=(-8, -2.25))
1765
_line_embedding(g, [36, 38, 64, 24, 68, 30], first=(-7, 3),
1766
last=(-7, -3))
1767
_line_embedding(g, [35, 39, 63, 25, 59, 29, 11, 5, 55, 23, 69, 31],
1768
first=(-6, 3.5), last=(-6, -3.5))
1769
1770
# Cube, corners: [9, 15, 21, 27, 45, 51, 57, 61]
1771
_circle_embedding(g, [61, 9], center=(0, -1.5), shift=.2,
1772
radius=4)
1773
_circle_embedding(g, [27, 15], center=(0, -1.5), shift=.7,
1774
radius=4*.707)
1775
_circle_embedding(g, [51, 21], center=(0, 2.5), shift=.2,
1776
radius=4)
1777
_circle_embedding(g, [45, 57], center=(0, 2.5), shift=.7,
1778
radius=4*.707)
1779
1780
# Cube, subdivision
1781
_line_embedding(g, [21, 22, 43, 44, 45], first=d[21], last=d[45])
1782
_line_embedding(g, [21, 4, 3, 56, 57], first=d[21], last=d[57])
1783
_line_embedding(g, [57, 12, 13, 14, 15], first=d[57], last=d[15])
1784
_line_embedding(g, [15, 6, 7, 8, 9], first=d[15], last=d[9])
1785
_line_embedding(g, [9, 10, 19, 20, 21], first=d[9], last=d[21])
1786
_line_embedding(g, [45, 54, 53, 52, 51], first=d[45], last=d[51])
1787
_line_embedding(g, [51, 50, 49, 58, 57], first=d[51], last=d[57])
1788
_line_embedding(g, [51, 32, 33, 34, 61], first=d[51], last=d[61])
1789
_line_embedding(g, [61, 62, 41, 40, 27], first=d[61], last=d[27])
1790
_line_embedding(g, [9, 0, 1, 26, 27], first=d[9], last=d[27])
1791
_line_embedding(g, [27, 28, 47, 46, 45], first=d[27], last=d[45])
1792
_line_embedding(g, [15, 16, 17, 60, 61], first=d[15], last=d[61])
1793
1794
# Top vertices
1795
_line_embedding(g, [2, 18, 42, 48], first=(-1, 7), last=(3, 7))
1796
1797
return g
1798
1799
elif embedding == 2:
1800
return g
1801
else:
1802
raise ValueError("The value of embedding must be 1 or 2.")
1803
1804
def HouseGraph(self):
1805
"""
1806
Returns a house graph with 5 nodes.
1807
1808
A house graph is named for its shape. It is a triangle (roof) over a
1809
square (walls).
1810
1811
This constructor depends on NetworkX numeric labeling.
1812
1813
PLOTTING: Upon construction, the position dictionary is filled to
1814
override the spring-layout algorithm. By convention, the house
1815
graph is drawn with the first node in the lower-left corner of the
1816
house, the second in the lower-right corner of the house. The third
1817
node is in the upper-left corner connecting the roof to the wall,
1818
and the fourth is in the upper-right corner connecting the roof to
1819
the wall. The fifth node is the top of the roof, connected only to
1820
the third and fourth.
1821
1822
EXAMPLES: Construct and show a house graph
1823
1824
::
1825
1826
sage: g = graphs.HouseGraph()
1827
sage: g.show() # long time
1828
"""
1829
pos_dict = {0:(-1,0),1:(1,0),2:(-1,1),3:(1,1),4:(0,2)}
1830
import networkx
1831
G = networkx.house_graph()
1832
return graph.Graph(G, pos=pos_dict, name="House Graph")
1833
1834
def HouseXGraph(self):
1835
"""
1836
Returns a house X graph with 5 nodes.
1837
1838
A house X graph is a house graph with two additional edges. The
1839
upper-right corner is connected to the lower-left. And the
1840
upper-left corner is connected to the lower-right.
1841
1842
This constructor depends on NetworkX numeric labeling.
1843
1844
PLOTTING: Upon construction, the position dictionary is filled to
1845
override the spring-layout algorithm. By convention, the house X
1846
graph is drawn with the first node in the lower-left corner of the
1847
house, the second in the lower-right corner of the house. The third
1848
node is in the upper-left corner connecting the roof to the wall,
1849
and the fourth is in the upper-right corner connecting the roof to
1850
the wall. The fifth node is the top of the roof, connected only to
1851
the third and fourth.
1852
1853
EXAMPLES: Construct and show a house X graph
1854
1855
::
1856
1857
sage: g = graphs.HouseXGraph()
1858
sage: g.show() # long time
1859
"""
1860
pos_dict = {0:(-1,0),1:(1,0),2:(-1,1),3:(1,1),4:(0,2)}
1861
import networkx
1862
G = networkx.house_x_graph()
1863
return graph.Graph(G, pos=pos_dict, name="House Graph")
1864
1865
def KrackhardtKiteGraph(self):
1866
"""
1867
Returns a Krackhardt kite graph with 10 nodes.
1868
1869
The Krackhardt kite graph was originally developed by David
1870
Krackhardt for the purpose of studying social networks. It is used
1871
to show the distinction between: degree centrality, betweeness
1872
centrality, and closeness centrality. For more information read the
1873
plotting section below in conjunction with the example.
1874
1875
REFERENCES:
1876
1877
- [1] Kreps, V. (2002). "Social Network Analysis". [Online] Available:
1878
http://www.fsu.edu/~spap/water/network/intro.htm [2007,
1879
January 17]
1880
1881
This constructor depends on NetworkX numeric labeling.
1882
1883
PLOTTING: Upon construction, the position dictionary is filled to
1884
override the spring-layout algorithm. By convention, the graph is
1885
drawn left to right, in top to bottom row sequence of [2, 3, 2, 1,
1886
1, 1] nodes on each row. This places the fourth node (3) in the
1887
center of the kite, with the highest degree. But the fourth node
1888
only connects nodes that are otherwise connected, or those in its
1889
clique (i.e.: Degree Centrality). The eighth (7) node is where the
1890
kite meets the tail. It has degree = 3, less than the average, but
1891
is the only connection between the kite and tail (i.e.: Betweenness
1892
Centrality). The sixth and seventh nodes (5 and 6) are drawn in the
1893
third row and have degree = 5. These nodes have the shortest path
1894
to all other nodes in the graph (i.e.: Closeness Centrality).
1895
Please execute the example for visualization.
1896
1897
EXAMPLE: Construct and show a Krackhardt kite graph
1898
1899
::
1900
1901
sage: g = graphs.KrackhardtKiteGraph()
1902
sage: g.show() # long time
1903
"""
1904
pos_dict = {0:(-1,4),1:(1,4),2:(-2,3),3:(0,3),4:(2,3),5:(-1,2),6:(1,2),7:(0,1),8:(0,0),9:(0,-1)}
1905
import networkx
1906
G = networkx.krackhardt_kite_graph()
1907
return graph.Graph(G, pos=pos_dict, name="Krackhardt Kite Graph")
1908
1909
def LadderGraph(self, n):
1910
"""
1911
Returns a ladder graph with 2\*n nodes.
1912
1913
A ladder graph is a basic structure that is typically displayed as
1914
a ladder, i.e.: two parallel path graphs connected at each
1915
corresponding node pair.
1916
1917
This constructor depends on NetworkX numeric labels.
1918
1919
PLOTTING: Upon construction, the position dictionary is filled to
1920
override the spring-layout algorithm. By convention, each ladder
1921
graph will be displayed horizontally, with the first n nodes
1922
displayed left to right on the top horizontal line.
1923
1924
EXAMPLES: Construct and show a ladder graph with 14 nodes
1925
1926
::
1927
1928
sage: g = graphs.LadderGraph(7)
1929
sage: g.show() # long time
1930
1931
Create several ladder graphs in a Sage graphics array
1932
1933
::
1934
1935
sage: g = []
1936
sage: j = []
1937
sage: for i in range(9):
1938
... k = graphs.LadderGraph(i+2)
1939
... g.append(k)
1940
...
1941
sage: for i in range(3):
1942
... n = []
1943
... for m in range(3):
1944
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
1945
... j.append(n)
1946
...
1947
sage: G = sage.plot.graphics.GraphicsArray(j)
1948
sage: G.show() # long time
1949
"""
1950
pos_dict = {}
1951
for i in range(n):
1952
pos_dict[i] = (i,1)
1953
for i in range(n,2*n):
1954
x = i - n
1955
pos_dict[i] = (x,0)
1956
import networkx
1957
G = networkx.ladder_graph(n)
1958
return graph.Graph(G, pos=pos_dict, name="Ladder graph")
1959
1960
def LollipopGraph(self, n1, n2):
1961
"""
1962
Returns a lollipop graph with n1+n2 nodes.
1963
1964
A lollipop graph is a path graph (order n2) connected to a complete
1965
graph (order n1). (A barbell graph minus one of the bells).
1966
1967
This constructor depends on NetworkX numeric labels.
1968
1969
PLOTTING: Upon construction, the position dictionary is filled to
1970
override the spring-layout algorithm. By convention, the complete
1971
graph will be drawn in the lower-left corner with the (n1)th node
1972
at a 45 degree angle above the right horizontal center of the
1973
complete graph, leading directly into the path graph.
1974
1975
EXAMPLES: Construct and show a lollipop graph Candy = 13, Stick =
1976
4
1977
1978
::
1979
1980
sage: g = graphs.LollipopGraph(13,4)
1981
sage: g.show() # long time
1982
1983
Create several lollipop graphs in a Sage graphics array
1984
1985
::
1986
1987
sage: g = []
1988
sage: j = []
1989
sage: for i in range(6):
1990
... k = graphs.LollipopGraph(i+3,4)
1991
... g.append(k)
1992
...
1993
sage: for i in range(2):
1994
... n = []
1995
... for m in range(3):
1996
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
1997
... j.append(n)
1998
...
1999
sage: G = sage.plot.graphics.GraphicsArray(j)
2000
sage: G.show() # long time
2001
"""
2002
pos_dict = {}
2003
2004
for i in range(n1):
2005
x = float(cos((pi/4) - ((2*pi)/n1)*i) - n2/2 - 1)
2006
y = float(sin((pi/4) - ((2*pi)/n1)*i) - n2/2 - 1)
2007
j = n1-1-i
2008
pos_dict[j] = (x,y)
2009
for i in range(n1, n1+n2):
2010
x = float(i - n1 - n2/2 + 1)
2011
y = float(i - n1 - n2/2 + 1)
2012
pos_dict[i] = (x,y)
2013
2014
import networkx
2015
G = networkx.lollipop_graph(n1,n2)
2016
return graph.Graph(G, pos=pos_dict, name="Lollipop Graph")
2017
2018
def PathGraph(self, n, pos=None):
2019
"""
2020
Returns a path graph with n nodes. Pos argument takes a string
2021
which is either 'circle' or 'line', (otherwise the default is
2022
used). See the plotting section below for more detail.
2023
2024
A path graph is a graph where all inner nodes are connected to
2025
their two neighbors and the two end-nodes are connected to their
2026
one inner neighbors. (i.e.: a cycle graph without the first and
2027
last node connected).
2028
2029
This constructor depends on NetworkX numeric labels.
2030
2031
PLOTTING: Upon construction, the position dictionary is filled to
2032
override the spring-layout algorithm. By convention, the graph may
2033
be drawn in one of two ways: The 'line' argument will draw the
2034
graph in a horizontal line (left to right) if there are less than
2035
11 nodes. Otherwise the 'line' argument will append horizontal
2036
lines of length 10 nodes below, alternating left to right and right
2037
to left. The 'circle' argument will cause the graph to be drawn in
2038
a cycle-shape, with the first node at the top and then about the
2039
circle in a clockwise manner. By default (without an appropriate
2040
string argument) the graph will be drawn as a 'circle' if 10 n 41
2041
and as a 'line' for all other n.
2042
2043
EXAMPLES: Show default drawing by size: 'line': n 11
2044
2045
::
2046
2047
sage: p = graphs.PathGraph(10)
2048
sage: p.show() # long time
2049
2050
'circle': 10 n 41
2051
2052
::
2053
2054
sage: q = graphs.PathGraph(25)
2055
sage: q.show() # long time
2056
2057
'line': n 40
2058
2059
::
2060
2061
sage: r = graphs.PathGraph(55)
2062
sage: r.show() # long time
2063
2064
Override the default drawing::
2065
2066
sage: s = graphs.PathGraph(5,'circle')
2067
sage: s.show() # long time
2068
"""
2069
pos_dict = {}
2070
2071
# Choose appropriate drawing pattern
2072
circle = False
2073
if pos == "circle": circle = True
2074
elif pos == "line": circle = False
2075
# Otherwise use default by size of n
2076
elif 10 < n < 41: circle = True
2077
2078
# Draw 'circle'
2079
if circle:
2080
for i in range(n):
2081
x = float(cos((pi/2) + ((2*pi)/n)*i))
2082
y = float(sin((pi/2) + ((2*pi)/n)*i))
2083
pos_dict[i] = (x,y)
2084
# Draw 'line'
2085
else:
2086
counter = 0 # node index
2087
rem = n%10 # remainder to appear on last row
2088
rows = n//10 # number of rows (not counting last row)
2089
lr = True # left to right
2090
2091
for i in range(rows): # note that rows doesn't include last row
2092
y = -i
2093
for j in range(10):
2094
if lr:
2095
x = j
2096
else:
2097
x = 9 - j
2098
pos_dict[counter] = (x,y)
2099
counter += 1
2100
if lr: lr = False
2101
else: lr = True
2102
y = -rows
2103
for j in range(rem): # last row
2104
if lr:
2105
x = j
2106
else:
2107
x = 9 - j
2108
pos_dict[counter] = (x,y)
2109
counter += 1
2110
2111
import networkx
2112
G = networkx.path_graph(n)
2113
return graph.Graph(G, pos=pos_dict, name="Path Graph")
2114
2115
def StarGraph(self, n):
2116
"""
2117
Returns a star graph with n+1 nodes.
2118
2119
A Star graph is a basic structure where one node is connected to
2120
all other nodes.
2121
2122
This constructor is dependent on NetworkX numeric labels.
2123
2124
PLOTTING: Upon construction, the position dictionary is filled to
2125
override the spring-layout algorithm. By convention, each star
2126
graph will be displayed with the first (0) node in the center, the
2127
second node (1) at the top, with the rest following in a
2128
counterclockwise manner. (0) is the node connected to all other
2129
nodes.
2130
2131
The star graph is a good opportunity to compare efficiency of
2132
filling a position dictionary vs. using the spring-layout algorithm
2133
for plotting. As far as display, the spring-layout should push all
2134
other nodes away from the (0) node, and thus look very similar to
2135
this constructor's positioning.
2136
2137
EXAMPLES::
2138
2139
sage: import networkx
2140
2141
Compare the plots::
2142
2143
sage: n = networkx.star_graph(23)
2144
sage: spring23 = Graph(n)
2145
sage: posdict23 = graphs.StarGraph(23)
2146
sage: spring23.show() # long time
2147
sage: posdict23.show() # long time
2148
2149
View many star graphs as a Sage Graphics Array
2150
2151
With this constructor (i.e., the position dictionary filled)
2152
2153
::
2154
2155
sage: g = []
2156
sage: j = []
2157
sage: for i in range(9):
2158
... k = graphs.StarGraph(i+3)
2159
... g.append(k)
2160
...
2161
sage: for i in range(3):
2162
... n = []
2163
... for m in range(3):
2164
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
2165
... j.append(n)
2166
...
2167
sage: G = sage.plot.graphics.GraphicsArray(j)
2168
sage: G.show() # long time
2169
2170
Compared to plotting with the spring-layout algorithm
2171
2172
::
2173
2174
sage: g = []
2175
sage: j = []
2176
sage: for i in range(9):
2177
... spr = networkx.star_graph(i+3)
2178
... k = Graph(spr)
2179
... g.append(k)
2180
...
2181
sage: for i in range(3):
2182
... n = []
2183
... for m in range(3):
2184
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
2185
... j.append(n)
2186
...
2187
sage: G = sage.plot.graphics.GraphicsArray(j)
2188
sage: G.show() # long time
2189
"""
2190
pos_dict = {}
2191
pos_dict[0] = (0,0)
2192
for i in range(1,n+1):
2193
x = float(cos((pi/2) + ((2*pi)/n)*(i-1)))
2194
y = float(sin((pi/2) + ((2*pi)/n)*(i-1)))
2195
pos_dict[i] = (x,y)
2196
import networkx
2197
G = networkx.star_graph(n)
2198
return graph.Graph(G, pos=pos_dict, name="Star graph")
2199
2200
def WheelGraph(self, n):
2201
"""
2202
Returns a Wheel graph with n nodes.
2203
2204
A Wheel graph is a basic structure where one node is connected to
2205
all other nodes and those (outer) nodes are connected cyclically.
2206
2207
This constructor depends on NetworkX numeric labels.
2208
2209
PLOTTING: Upon construction, the position dictionary is filled to
2210
override the spring-layout algorithm. By convention, each wheel
2211
graph will be displayed with the first (0) node in the center, the
2212
second node at the top, and the rest following in a
2213
counterclockwise manner.
2214
2215
With the wheel graph, we see that it doesn't take a very large n at
2216
all for the spring-layout to give a counter-intuitive display. (See
2217
Graphics Array examples below).
2218
2219
EXAMPLES: We view many wheel graphs with a Sage Graphics Array,
2220
first with this constructor (i.e., the position dictionary
2221
filled)::
2222
2223
sage: g = []
2224
sage: j = []
2225
sage: for i in range(9):
2226
... k = graphs.WheelGraph(i+3)
2227
... g.append(k)
2228
...
2229
sage: for i in range(3):
2230
... n = []
2231
... for m in range(3):
2232
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
2233
... j.append(n)
2234
...
2235
sage: G = sage.plot.graphics.GraphicsArray(j)
2236
sage: G.show() # long time
2237
2238
Next, using the spring-layout algorithm::
2239
2240
sage: import networkx
2241
sage: g = []
2242
sage: j = []
2243
sage: for i in range(9):
2244
... spr = networkx.wheel_graph(i+3)
2245
... k = Graph(spr)
2246
... g.append(k)
2247
...
2248
sage: for i in range(3):
2249
... n = []
2250
... for m in range(3):
2251
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
2252
... j.append(n)
2253
...
2254
sage: G = sage.plot.graphics.GraphicsArray(j)
2255
sage: G.show() # long time
2256
2257
Compare the plotting::
2258
2259
sage: n = networkx.wheel_graph(23)
2260
sage: spring23 = Graph(n)
2261
sage: posdict23 = graphs.WheelGraph(23)
2262
sage: spring23.show() # long time
2263
sage: posdict23.show() # long time
2264
"""
2265
pos_dict = {}
2266
pos_dict[0] = (0,0)
2267
for i in range(1,n):
2268
x = float(cos((pi/2) + ((2*pi)/(n-1))*(i-1)))
2269
y = float(sin((pi/2) + ((2*pi)/(n-1))*(i-1)))
2270
pos_dict[i] = (x,y)
2271
import networkx
2272
G = networkx.wheel_graph(n)
2273
return graph.Graph(G, pos=pos_dict, name="Wheel graph")
2274
2275
################################################################################
2276
# Platonic Solids
2277
################################################################################
2278
2279
def TetrahedralGraph(self):
2280
"""
2281
Returns a tetrahedral graph (with 4 nodes).
2282
2283
A tetrahedron is a 4-sided triangular pyramid. The tetrahedral
2284
graph corresponds to the connectivity of the vertices of the
2285
tetrahedron. This graph is equivalent to a wheel graph with 4 nodes
2286
and also a complete graph on four nodes. (See examples below).
2287
2288
PLOTTING: The tetrahedral graph should be viewed in 3 dimensions.
2289
We chose to use the default spring-layout algorithm here, so that
2290
multiple iterations might yield a different point of reference for
2291
the user. We hope to add rotatable, 3-dimensional viewing in the
2292
future. In such a case, a string argument will be added to select
2293
the flat spring-layout over a future implementation.
2294
2295
EXAMPLES: Construct and show a Tetrahedral graph
2296
2297
::
2298
2299
sage: g = graphs.TetrahedralGraph()
2300
sage: g.show() # long time
2301
2302
The following example requires networkx::
2303
2304
sage: import networkx as NX
2305
2306
Compare this Tetrahedral, Wheel(4), Complete(4), and the
2307
Tetrahedral plotted with the spring-layout algorithm below in a
2308
Sage graphics array::
2309
2310
sage: tetra_pos = graphs.TetrahedralGraph()
2311
sage: tetra_spring = Graph(NX.tetrahedral_graph())
2312
sage: wheel = graphs.WheelGraph(4)
2313
sage: complete = graphs.CompleteGraph(4)
2314
sage: g = [tetra_pos, tetra_spring, wheel, complete]
2315
sage: j = []
2316
sage: for i in range(2):
2317
... n = []
2318
... for m in range(2):
2319
... n.append(g[i + m].plot(vertex_size=50, vertex_labels=False))
2320
... j.append(n)
2321
sage: G = sage.plot.graphics.GraphicsArray(j)
2322
sage: G.show() # long time
2323
"""
2324
import networkx
2325
G = networkx.tetrahedral_graph()
2326
return graph.Graph(G, name="Tetrahedron", pos =
2327
{ 0 : (0, 0),
2328
1 : (0, 1),
2329
2 : (cos(3.5*pi/3), sin(3.5*pi/3)),
2330
3 : (cos(5.5*pi/3), sin(5.5*pi/3))}
2331
)
2332
2333
def HexahedralGraph(self):
2334
"""
2335
Returns a hexahedral graph (with 8 nodes).
2336
2337
A regular hexahedron is a 6-sided cube. The hexahedral graph
2338
corresponds to the connectivity of the vertices of the hexahedron.
2339
This graph is equivalent to a 3-cube.
2340
2341
PLOTTING: The hexahedral graph should be viewed in 3 dimensions. We
2342
chose to use the default spring-layout algorithm here, so that
2343
multiple iterations might yield a different point of reference for
2344
the user. We hope to add rotatable, 3-dimensional viewing in the
2345
future. In such a case, a string argument will be added to select
2346
the flat spring-layout over a future implementation.
2347
2348
EXAMPLES: Construct and show a Hexahedral graph
2349
2350
::
2351
2352
sage: g = graphs.HexahedralGraph()
2353
sage: g.show() # long time
2354
2355
Create several hexahedral graphs in a Sage graphics array. They
2356
will be drawn differently due to the use of the spring-layout
2357
algorithm.
2358
2359
::
2360
2361
sage: g = []
2362
sage: j = []
2363
sage: for i in range(9):
2364
... k = graphs.HexahedralGraph()
2365
... g.append(k)
2366
...
2367
sage: for i in range(3):
2368
... n = []
2369
... for m in range(3):
2370
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
2371
... j.append(n)
2372
...
2373
sage: G = sage.plot.graphics.GraphicsArray(j)
2374
sage: G.show() # long time
2375
"""
2376
return graph.Graph({0:[1,3,4], 1:[2,5], 2:[3,6], 3:[7], 4:[5,7],\
2377
5:[6], 6:[7]},
2378
name="Hexahedron",
2379
pos = {
2380
0 : (0,0),
2381
1 : (1,0),
2382
3 : (0,1),
2383
2 : (1,1),
2384
4 : (.5,.5),
2385
5 : (1.5,.5),
2386
7 : (.5,1.5),
2387
6 : (1.5,1.5)
2388
})
2389
2390
def OctahedralGraph(self):
2391
"""
2392
Returns an Octahedral graph (with 6 nodes).
2393
2394
The regular octahedron is an 8-sided polyhedron with triangular
2395
faces. The octahedral graph corresponds to the connectivity of the
2396
vertices of the octahedron. It is the line graph of the tetrahedral
2397
graph. The octahedral is symmetric, so the spring-layout algorithm
2398
will be very effective for display.
2399
2400
PLOTTING: The Octahedral graph should be viewed in 3 dimensions. We
2401
chose to use the default spring-layout algorithm here, so that
2402
multiple iterations might yield a different point of reference for
2403
the user. We hope to add rotatable, 3-dimensional viewing in the
2404
future. In such a case, a string argument will be added to select
2405
the flat spring-layout over a future implementation.
2406
2407
EXAMPLES: Construct and show an Octahedral graph
2408
2409
::
2410
2411
sage: g = graphs.OctahedralGraph()
2412
sage: g.show() # long time
2413
2414
Create several octahedral graphs in a Sage graphics array They will
2415
be drawn differently due to the use of the spring-layout algorithm
2416
2417
::
2418
2419
sage: g = []
2420
sage: j = []
2421
sage: for i in range(9):
2422
... k = graphs.OctahedralGraph()
2423
... g.append(k)
2424
...
2425
sage: for i in range(3):
2426
... n = []
2427
... for m in range(3):
2428
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
2429
... j.append(n)
2430
...
2431
sage: G = sage.plot.graphics.GraphicsArray(j)
2432
sage: G.show() # long time
2433
"""
2434
import networkx
2435
G = networkx.octahedral_graph()
2436
2437
pos = {}
2438
r1 = 5
2439
r2 = 1
2440
for i,v in enumerate([0,1,2]):
2441
i = i + 0.75
2442
pos[v] = (r1*cos(i*2*pi/3),r1*sin(i*2*pi/3))
2443
2444
for i,v in enumerate([4,3,5]):
2445
i = i + .25
2446
pos[v] = (r2*cos(i*2*pi/3),r2*sin(i*2*pi/3))
2447
2448
2449
return graph.Graph(G, name="Octahedron", pos=pos)
2450
2451
def IcosahedralGraph(self):
2452
"""
2453
Returns an Icosahedral graph (with 12 nodes).
2454
2455
The regular icosahedron is a 20-sided triangular polyhedron. The
2456
icosahedral graph corresponds to the connectivity of the vertices
2457
of the icosahedron. It is dual to the dodecahedral graph. The
2458
icosahedron is symmetric, so the spring-layout algorithm will be
2459
very effective for display.
2460
2461
PLOTTING: The Icosahedral graph should be viewed in 3 dimensions.
2462
We chose to use the default spring-layout algorithm here, so that
2463
multiple iterations might yield a different point of reference for
2464
the user. We hope to add rotatable, 3-dimensional viewing in the
2465
future. In such a case, a string argument will be added to select
2466
the flat spring-layout over a future implementation.
2467
2468
EXAMPLES: Construct and show an Octahedral graph
2469
2470
::
2471
2472
sage: g = graphs.IcosahedralGraph()
2473
sage: g.show() # long time
2474
2475
Create several icosahedral graphs in a Sage graphics array. They
2476
will be drawn differently due to the use of the spring-layout
2477
algorithm.
2478
2479
::
2480
2481
sage: g = []
2482
sage: j = []
2483
sage: for i in range(9):
2484
... k = graphs.IcosahedralGraph()
2485
... g.append(k)
2486
...
2487
sage: for i in range(3):
2488
... n = []
2489
... for m in range(3):
2490
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
2491
... j.append(n)
2492
...
2493
sage: G = sage.plot.graphics.GraphicsArray(j)
2494
sage: G.show() # long time
2495
"""
2496
import networkx
2497
G = networkx.icosahedral_graph()
2498
2499
pos = {}
2500
r1 = 5
2501
r2 = 2
2502
for i,v in enumerate([2,8,7,11,4,6]):
2503
i = i + .5
2504
pos[v] = (r1*cos(i*pi/3),r1*sin(i*pi/3))
2505
2506
for i,v in enumerate([1,9,0,10,5,3]):
2507
i = i + .5
2508
pos[v] = (r2*cos(i*pi/3),r2*sin(i*pi/3))
2509
2510
return graph.Graph(G, name="Icosahedron", pos = pos)
2511
2512
def DodecahedralGraph(self):
2513
"""
2514
Returns a Dodecahedral graph (with 20 nodes)
2515
2516
The dodecahedral graph is cubic symmetric, so the spring-layout
2517
algorithm will be very effective for display. It is dual to the
2518
icosahedral graph.
2519
2520
PLOTTING: The Dodecahedral graph should be viewed in 3 dimensions.
2521
We chose to use the default spring-layout algorithm here, so that
2522
multiple iterations might yield a different point of reference for
2523
the user. We hope to add rotatable, 3-dimensional viewing in the
2524
future. In such a case, a string argument will be added to select
2525
the flat spring-layout over a future implementation.
2526
2527
EXAMPLES: Construct and show a Dodecahedral graph
2528
2529
::
2530
2531
sage: g = graphs.DodecahedralGraph()
2532
sage: g.show() # long time
2533
2534
Create several dodecahedral graphs in a Sage graphics array They
2535
will be drawn differently due to the use of the spring-layout
2536
algorithm
2537
2538
::
2539
2540
sage: g = []
2541
sage: j = []
2542
sage: for i in range(9):
2543
... k = graphs.DodecahedralGraph()
2544
... g.append(k)
2545
...
2546
sage: for i in range(3):
2547
... n = []
2548
... for m in range(3):
2549
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
2550
... j.append(n)
2551
...
2552
sage: G = sage.plot.graphics.GraphicsArray(j)
2553
sage: G.show() # long time
2554
"""
2555
import networkx
2556
G = networkx.dodecahedral_graph()
2557
2558
pos = {}
2559
r1 = 7
2560
r2 = 4.7
2561
r3 = 3.8
2562
r4 = 1.5
2563
2564
for i,v in enumerate([19,0,1,2,3]):
2565
i = i + .25
2566
pos[v] = (r1*cos(i*2*pi/5),r1*sin(i*2*pi/5))
2567
2568
for i,v in enumerate([18,10,8,6,4]):
2569
i = i + .25
2570
pos[v] = (r2*cos(i*2*pi/5),r2*sin(i*2*pi/5))
2571
2572
for i,v in enumerate([17,11,9,7,5]):
2573
i = i - .25
2574
pos[v] = (r3*cos(i*2*pi/5),r3*sin(i*2*pi/5))
2575
2576
for i,v in enumerate([12,13,14,15,16]):
2577
i = i + .75
2578
pos[v] = (r4*cos(i*2*pi/5),r4*sin(i*2*pi/5))
2579
2580
return graph.Graph(G, name="Dodecahedron", pos=pos)
2581
2582
#######################################################################
2583
# Named Graphs
2584
#######################################################################
2585
2586
def Balaban10Cage(self, embedding=1):
2587
r"""
2588
Returns the Balaban 10-cage.
2589
2590
The Balaban 10-cage is a 3-regular graph with 70 vertices and
2591
105 edges. See its :wikipedia:`Wikipedia page
2592
<Balaban_10-cage>`.
2593
2594
The default embedding gives a deeper understanding of the
2595
graph's automorphism group. It is divided into 4 layers (each
2596
layer being a set of points at equal distance from the drawing's
2597
center). From outside to inside:
2598
2599
- L1: The outer layer (vertices which are the furthest from the
2600
origin) is actually the disjoint union of two cycles of length
2601
10.
2602
2603
- L2: The second layer is an independent set of 20 vertices.
2604
2605
- L3: The third layer is a matching on 10 vertices.
2606
2607
- L4: The inner layer (vertices which are the closest from the
2608
origin) is also the disjoint union of two cycles of length 10.
2609
2610
This graph is not vertex-transitive, and its vertices are
2611
partitioned into 3 orbits: L2, L3, and the union of L1 of L4
2612
whose elements are equivalent.
2613
2614
INPUT:
2615
2616
- ``embedding`` -- two embeddings are available, and can be
2617
selected by setting ``embedding`` to be either 1 or 2.
2618
2619
EXAMPLES::
2620
2621
sage: g = graphs.Balaban10Cage()
2622
sage: g.girth()
2623
10
2624
sage: g.chromatic_number()
2625
2
2626
sage: g.diameter()
2627
6
2628
sage: g.is_hamiltonian()
2629
True
2630
sage: g.show(figsize=[10,10]) # long time
2631
2632
TESTS::
2633
2634
sage: graphs.Balaban10Cage(embedding='foo')
2635
Traceback (most recent call last):
2636
...
2637
ValueError: The value of embedding must be 1 or 2.
2638
"""
2639
2640
L = [-9, -25, -19, 29, 13, 35, -13, -29, 19, 25, 9, -29, 29, 17, 33,
2641
21, 9,-13, -31, -9, 25, 17, 9, -31, 27, -9, 17, -19, -29, 27,
2642
-17, -9, -29, 33, -25,25, -21, 17, -17, 29, 35, -29, 17, -17,
2643
21, -25, 25, -33, 29, 9, 17, -27, 29, 19, -17, 9, -27, 31, -9,
2644
-17, -25, 9, 31, 13, -9, -21, -33, -17, -29, 29]
2645
2646
g = graphs.LCFGraph(70, L, 1)
2647
g.name("Balaban 10-cage")
2648
2649
if embedding == 2:
2650
return g
2651
elif embedding != 1:
2652
raise ValueError("The value of embedding must be 1 or 2.")
2653
2654
L3 = [5, 24, 35, 46, 29, 40, 51, 34, 45, 56]
2655
_circle_embedding(g, L3, center=(0,0), radius = 4.3)
2656
2657
L2 = [6, 4, 23, 25, 60, 36, 1, 47, 28, 30, 39, 41, 50, 52, 33, 9, 44,
2658
20, 55, 57]
2659
_circle_embedding(g, L2, center=(0,0), radius = 5, shift=-.5)
2660
2661
2662
L1a = [69, 68, 67, 66, 65, 64, 63, 62, 61, 0]
2663
L1b = [19, 18, 17, 16, 15, 14, 13, 12, 11, 10]
2664
_circle_embedding(g, L1a, center=(0,0), radius = 6, shift = 3.25)
2665
_circle_embedding(g, L1b, center=(0,0), radius = 6, shift = -1.25)
2666
2667
L4a = [37, 2, 31, 38, 53, 32, 21, 54, 3, 22]
2668
_circle_embedding(g, L4a, center=(0,0), radius = 3, shift = 1.9)
2669
2670
L4b = [26, 59, 48, 27, 42, 49, 8, 43, 58, 7]
2671
_circle_embedding(g, L4b, center=(0,0), radius = 3, shift = 1.1)
2672
2673
return g
2674
2675
def Balaban11Cage(self, embedding = 1):
2676
r"""
2677
Returns the Balaban 11-cage.
2678
2679
For more information, see this :wikipedia:`Wikipedia article on
2680
the Balaban 11-cage <Balaban_11-cage>`.
2681
2682
INPUT:
2683
2684
- ``embedding`` -- three embeddings are available, and can be
2685
selected by setting ``embedding`` to be 1, 2, or 3.
2686
2687
- The first embedding is the one appearing on page 9 of the
2688
Fifth Annual Graph Drawing Contest report [FAGDC]_. It
2689
separates vertices based on their eccentricity (see
2690
:meth:`eccentricity()
2691
<sage.graphs.generic_graph.GenericGraph.eccentricity>`).
2692
2693
- The second embedding has been produced just for Sage and is
2694
meant to emphasize the automorphism group's 6 orbits.
2695
2696
- The last embedding is the default one produced by the
2697
:meth:`LCFGraph` constructor.
2698
2699
.. NOTE::
2700
2701
The vertex labeling changes according to the value of
2702
``embedding=1``.
2703
2704
EXAMPLES:
2705
2706
Basic properties::
2707
2708
sage: g = graphs.Balaban11Cage()
2709
sage: g.order()
2710
112
2711
sage: g.size()
2712
168
2713
sage: g.girth()
2714
11
2715
sage: g.diameter()
2716
8
2717
sage: g.automorphism_group().cardinality()
2718
64
2719
2720
Our many embeddings::
2721
2722
sage: g1 = graphs.Balaban11Cage(embedding=1)
2723
sage: g2 = graphs.Balaban11Cage(embedding=2)
2724
sage: g3 = graphs.Balaban11Cage(embedding=3)
2725
sage: g1.show(figsize=[10,10]) # long time
2726
sage: g2.show(figsize=[10,10]) # long time
2727
sage: g3.show(figsize=[10,10]) # long time
2728
2729
Proof that the embeddings are the same graph::
2730
2731
sage: g1.is_isomorphic(g2) # g2 and g3 are obviously isomorphic
2732
True
2733
2734
TESTS::
2735
2736
sage: graphs.Balaban11Cage(embedding='xyzzy')
2737
Traceback (most recent call last):
2738
...
2739
ValueError: The value of embedding must be 1, 2, or 3.
2740
2741
REFERENCES:
2742
2743
.. [FAGDC] Fifth Annual Graph Drawing Contest
2744
P. Eaded, J. Marks, P.Mutzel, S. North
2745
http://www.merl.com/papers/docs/TR98-16.pdf
2746
"""
2747
if embedding == 1:
2748
pos_dict = {}
2749
for j in range(8):
2750
for i in range(8):
2751
pos_dict[str(j) + str(i)]= [
2752
0.8 * float(cos(2*((8*j + i)*pi/64 + pi/128))),
2753
0.8 * float(sin(2*((8*j + i)*pi/64 + pi/128)))
2754
]
2755
for i in range(4):
2756
pos_dict['1' + str(j) + str(i)] = [
2757
1.1 * float(cos(2*((4*j + i)*pi/32 + pi/64))),
2758
1.1 * float(sin(2*((4*j + i)*pi/32 + pi/64)))
2759
]
2760
for i in range(2):
2761
pos_dict['1' + str(j) + str(i + 4)] = [
2762
1.4 * float(cos(2*((2*j + i)*pi/16 + pi/32))),
2763
1.4 * float(sin(2*((2*j + i)*pi/16 + pi/32)))
2764
]
2765
2766
edge_dict = {
2767
"00": ["11"], "01": ["10"], "02": ["53"], "03": ["52"],
2768
"11": ["20"], "10": ["21"], "53": ["22"], "52": ["23"],
2769
"20": ["31"], "21": ["30"], "22": ["33"], "23": ["32"],
2770
"31": ["40"], "30": ["41"], "33": ["43"], "32": ["42"],
2771
"40": ["50"], "41": ["51"], "43": ["12"], "42": ["13"],
2772
"50": ["61"], "51": ["60"], "12": ["63"], "13": ["62"],
2773
"61": ["70"], "60": ["71"], "63": ["72"], "62": ["73"],
2774
"70": ["01"], "71": ["00"], "72": ["03"], "73": ["02"],
2775
2776
"04": ["35"], "05": ["34"], "06": ["37"], "07": ["36"],
2777
"35": ["64"], "34": ["65"], "37": ["66"], "36": ["67"],
2778
"64": ["55"], "65": ["54"], "66": ["17"], "67": ["16"],
2779
"55": ["45"], "54": ["44"], "17": ["46"], "16": ["47"],
2780
"45": ["74"], "44": ["75"], "46": ["76"], "47": ["77"],
2781
"74": ["25"], "75": ["24"], "76": ["27"], "77": ["26"],
2782
"25": ["14"], "24": ["15"], "27": ["56"], "26": ["57"],
2783
"14": ["05"], "15": ["04"], "56": ["07"], "57": ["06"],
2784
2785
"100": ["03", "04"], "110": ["10", "12"],
2786
"101": ["01", "06"], "111": ["11", "13"],
2787
"102": ["00", "07"], "112": ["14", "16"],
2788
"103": ["02", "05"], "113": ["15", "17"],
2789
2790
"120": ["22", "24"], "130": ["33", "36"],
2791
"121": ["20", "26"], "131": ["32", "37"],
2792
"122": ["21", "27"], "132": ["31", "34"],
2793
"123": ["23", "25"], "133": ["30", "35"],
2794
2795
"140": ["43", "45"], "150": ["50", "52"],
2796
"141": ["40", "46"], "151": ["51", "53"],
2797
"142": ["41", "47"], "152": ["54", "56"],
2798
"143": ["42", "44"], "153": ["55", "57"],
2799
2800
"160": ["60", "66"], "170": ["73", "76"],
2801
"161": ["63", "65"], "171": ["72", "77"],
2802
"162": ["62", "64"], "172": ["71", "74"],
2803
"163": ["61", "67"], "173": ["70", "75"],
2804
2805
"104": ["100", "102", "105"], "114": ["110", "111", "115"],
2806
"105": ["101", "103", "104"], "115": ["112", "113", "114"],
2807
2808
"124": ["120", "121", "125"], "134": ["130", "131", "135"],
2809
"125": ["122", "123", "124"], "135": ["132", "133", "134"],
2810
2811
"144": ["140", "141", "145"], "154": ["150", "151", "155"],
2812
"145": ["142", "143", "144"], "155": ["152", "153", "154"],
2813
2814
"164": ["160", "161", "165"], "174": ["170", "171", "175"],
2815
"165": ["162", "163", "164"], "175": ["172", "173", "174"]
2816
}
2817
2818
return graph.Graph(edge_dict, pos=pos_dict, name="Balaban 11-cage")
2819
2820
elif embedding == 2 or embedding == 3:
2821
L = [44, 26, -47, -15, 35, -39, 11, -27, 38, -37, 43, 14, 28, 51,
2822
-29, -16, 41, -11, -26, 15, 22, -51, -35, 36, 52, -14, -33,
2823
-26, -46, 52, 26, 16, 43, 33, -15, 17, -53, 23, -42, -35, -28,
2824
30, -22, 45, -44, 16, -38, -16, 50, -55, 20, 28, -17, -43,
2825
47, 34, -26, -41, 11, -36, -23, -16, 41, 17, -51, 26, -33,
2826
47, 17, -11, -20, -30, 21, 29, 36, -43, -52, 10, 39, -28, -17,
2827
-52, 51, 26, 37, -17, 10, -10, -45, -34, 17, -26, 27, -21,
2828
46, 53, -10, 29, -50, 35, 15, -47, -29, -41, 26, 33, 55, -17,
2829
42, -26, -36, 16]
2830
2831
g = graphs.LCFGraph(112, L, 1)
2832
g.name("Balaban 11-cage")
2833
2834
if embedding == 3:
2835
return g
2836
2837
v1 = [34, 2, 54, 43, 66, 20, 89, 100, 72, 76, 6, 58, 16, 78, 74,
2838
70, 36, 94, 27, 25, 10, 8, 45, 60, 14, 64, 80, 82, 109, 107,
2839
49, 98]
2840
v2 = [88, 3, 19, 55, 67, 42, 101, 33, 77, 5, 17, 57, 69, 71, 73,
2841
75, 11, 61, 28, 9, 37, 26, 46, 95, 13, 63, 81, 83, 108, 106,
2842
48, 97]
2843
l1 = [35, 93, 1, 24, 53, 7, 44, 59, 15, 65, 79, 21, 110, 90, 50,
2844
99]
2845
l2 = [87, 4, 18, 56, 68, 41, 102, 32, 12, 62, 29, 84, 38, 105, 47,
2846
96]
2847
2848
d = g.get_pos()
2849
for i,v in enumerate(v1):
2850
d[v] = (-2, 16.5-i)
2851
2852
for i,v in enumerate(l1):
2853
d[v] = (-10, 8-i)
2854
2855
for i,v in enumerate(l2):
2856
d[v] = (10, 8.5-i)
2857
2858
for i,v in enumerate(v2):
2859
d[v] = (2, 16.5-i)
2860
2861
for i,v in enumerate([0, 111, 92, 91, 52, 51, 23, 22]):
2862
d[v] = (-20, 14.5-4*i)
2863
2864
for i,v in enumerate([104, 103, 86, 85, 40, 39, 31, 30]):
2865
d[v] = (20, 14.5-4*i)
2866
2867
return g
2868
2869
else:
2870
raise ValueError("The value of embedding must be 1, 2, or 3.")
2871
2872
def BidiakisCube(self):
2873
r"""
2874
Returns the Bidiakis cube.
2875
2876
For more information, see this
2877
`Wikipedia article on the Bidiakis cube <http://en.wikipedia.org/wiki/Bidiakis_cube>`_.
2878
2879
EXAMPLES:
2880
2881
The Bidiakis cube is a 3-regular graph having 12 vertices and 18
2882
edges. This means that each vertex has a degree of 3. ::
2883
2884
sage: g = graphs.BidiakisCube(); g
2885
Bidiakis cube: Graph on 12 vertices
2886
sage: g.show() # long time
2887
sage: g.order()
2888
12
2889
sage: g.size()
2890
18
2891
sage: g.is_regular(3)
2892
True
2893
2894
It is a Hamiltonian graph with diameter 3 and girth 4::
2895
2896
sage: g.is_hamiltonian()
2897
True
2898
sage: g.diameter()
2899
3
2900
sage: g.girth()
2901
4
2902
2903
It is a planar graph with characteristic polynomial
2904
`(x - 3) (x - 2) (x^4) (x + 1) (x + 2) (x^2 + x - 4)^2` and
2905
chromatic number 3::
2906
2907
sage: g.is_planar()
2908
True
2909
sage: bool(g.characteristic_polynomial() == expand((x - 3) * (x - 2) * (x^4) * (x + 1) * (x + 2) * (x^2 + x - 4)^2))
2910
True
2911
sage: g.chromatic_number()
2912
3
2913
"""
2914
edge_dict = {
2915
0:[1,6,11], 1:[2,5], 2:[3,10], 3:[4,9], 4:[5,8],
2916
5:[6], 6:[7], 7:[8,11], 8:[9], 9:[10], 10:[11]}
2917
pos_dict = {
2918
0: [0, 1],
2919
1: [0.5, 0.866025403784439],
2920
2: [0.866025403784439, 0.500000000000000],
2921
3: [1, 0],
2922
4: [0.866025403784439, -0.5],
2923
5: [0.5, -0.866025403784439],
2924
6: [0, -1],
2925
7: [-0.5, -0.866025403784439],
2926
8: [-0.866025403784439, -0.5],
2927
9: [-1, 0],
2928
10: [-0.866025403784439, 0.5],
2929
11: [-0.5, 0.866025403784439]}
2930
return graph.Graph(edge_dict, pos=pos_dict, name="Bidiakis cube")
2931
2932
def BiggsSmithGraph(self, embedding=1):
2933
r"""
2934
Returns the Biggs-Smith graph.
2935
2936
For more information, see this :wikipedia:`Wikipedia article on
2937
the Biggs-Smith graph <Biggs-Smith_graph>`.
2938
2939
INPUT:
2940
2941
- ``embedding`` -- two embeddings are available, and can be
2942
selected by setting ``embedding`` to be 1 or 2.
2943
2944
EXAMPLES:
2945
2946
Basic properties::
2947
2948
sage: g = graphs.BiggsSmithGraph()
2949
sage: g.order()
2950
102
2951
sage: g.size()
2952
153
2953
sage: g.girth()
2954
9
2955
sage: g.diameter()
2956
7
2957
sage: g.automorphism_group().cardinality()
2958
2448
2959
sage: g.show(figsize=[10, 10]) # long time
2960
2961
The other embedding::
2962
2963
sage: graphs.BiggsSmithGraph(embedding=2).show()
2964
2965
TESTS::
2966
2967
sage: graphs.BiggsSmithGraph(embedding='xyzzy')
2968
Traceback (most recent call last):
2969
...
2970
ValueError: The value of embedding must be 1 or 2.
2971
2972
"""
2973
L = [16, 24, -38, 17, 34, 48, -19, 41, -35, 47, -20, 34, -36,
2974
21, 14, 48, -16, -36, -43, 28, -17, 21, 29, -43, 46, -24,
2975
28, -38, -14, -50, -45, 21, 8, 27, -21, 20, -37, 39, -34,
2976
-44, -8, 38, -21, 25, 15, -34, 18, -28, -41, 36, 8, -29,
2977
-21, -48, -28, -20, -47, 14, -8, -15, -27, 38, 24, -48, -18,
2978
25, 38, 31, -25, 24, -46, -14, 28, 11, 21, 35, -39, 43, 36,
2979
-38, 14, 50, 43, 36, -11, -36, -24, 45, 8, 19, -25, 38, 20,
2980
-24, -14, -21, -8, 44, -31, -38, -28, 37]
2981
2982
g = graphs.LCFGraph(102, L, 1)
2983
g.name("Biggs-Smith graph")
2984
2985
if embedding == 1:
2986
2987
orbs = [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0],
2988
[17, 101, 25, 66, 20, 38, 53, 89, 48, 75, 56, 92, 45, 78,
2989
34, 28, 63],
2990
[18, 36, 26, 65, 19, 37, 54, 90, 47, 76, 55, 91, 46, 77,
2991
35, 27, 64],
2992
[21, 39, 52, 88, 49, 74, 57, 93, 44, 79, 33, 29, 62, 83,
2993
100, 24, 67],
2994
[22, 97, 51, 96, 50, 95, 58, 94, 59, 80, 60, 81, 61, 82,
2995
99, 23, 98],
2996
[30, 86, 84, 72, 70, 68, 42, 40, 31, 87, 85, 73, 71, 69,
2997
43, 41, 32]]
2998
2999
# central orbits
3000
_circle_embedding(g, orbs[1], center=(-.4, 0), radius=.2)
3001
_circle_embedding(g, orbs[3], center=(.4, 0), radius=.2, shift=4)
3002
3003
# lower orbits
3004
_circle_embedding(g, orbs[0], center=(-.9, -.5), radius=.3,
3005
shift=2)
3006
_circle_embedding(g, orbs[2], center=(-.9, .5), radius=.3)
3007
3008
# upper orbits
3009
_circle_embedding(g, orbs[4], center=(.9, -.5), radius=.3, shift=4)
3010
_circle_embedding(g, orbs[5], center=(.9, .5), radius=.3, shift=-2)
3011
3012
elif embedding == 2:
3013
pass
3014
else:
3015
raise ValueError("The value of embedding must be 1 or 2.")
3016
3017
return g
3018
3019
def BrinkmannGraph(self):
3020
r"""
3021
Returns the Brinkmann graph.
3022
3023
For more information, see the
3024
`Wikipedia article on the Brinkmann graph <http://en.wikipedia.org/wiki/Brinkmann_graph>`_.
3025
3026
EXAMPLES:
3027
3028
The Brinkmann graph is a 4-regular graph having 21 vertices and 42
3029
edges. This means that each vertex has degree 4. ::
3030
3031
sage: G = graphs.BrinkmannGraph(); G
3032
Brinkmann graph: Graph on 21 vertices
3033
sage: G.show() # long time
3034
sage: G.order()
3035
21
3036
sage: G.size()
3037
42
3038
sage: G.is_regular(4)
3039
True
3040
3041
It is an Eulerian graph with radius 3, diameter 3, and girth 5. ::
3042
3043
sage: G.is_eulerian()
3044
True
3045
sage: G.radius()
3046
3
3047
sage: G.diameter()
3048
3
3049
sage: G.girth()
3050
5
3051
3052
The Brinkmann graph is also Hamiltonian with chromatic number 4::
3053
3054
sage: G.is_hamiltonian()
3055
True
3056
sage: G.chromatic_number()
3057
4
3058
3059
Its automorphism group is isomorphic to `D_7`::
3060
3061
sage: ag = G.automorphism_group()
3062
sage: ag.is_isomorphic(DihedralGroup(7))
3063
True
3064
"""
3065
edge_dict = {
3066
0: [2,5,7,13],
3067
1: [3,6,7,8],
3068
2: [4,8,9],
3069
3: [5,9,10],
3070
4: [6,10,11],
3071
5: [11,12],
3072
6: [12,13],
3073
7: [15,20],
3074
8: [14,16],
3075
9: [15,17],
3076
10: [16,18],
3077
11: [17,19],
3078
12: [18,20],
3079
13: [14,19],
3080
14: [17,18],
3081
15: [18,19],
3082
16: [19,20],
3083
17: [20]}
3084
pos_dict = {
3085
0: [0, 4],
3086
1: [3.12732592987212, 2.49395920743493],
3087
2: [3.89971164872729, -0.890083735825258],
3088
3: [1.73553495647023, -3.60387547160968],
3089
4: [-1.73553495647023, -3.60387547160968],
3090
5: [-3.89971164872729, -0.890083735825258],
3091
6: [-3.12732592987212, 2.49395920743493],
3092
7: [0.867767478235116, 1.80193773580484],
3093
8: [1.94985582436365, 0.445041867912629],
3094
9: [1.56366296493606, -1.24697960371747],
3095
10: [0, -2],
3096
11: [-1.56366296493606, -1.24697960371747],
3097
12: [-1.94985582436365, 0.445041867912629],
3098
13: [-0.867767478235116, 1.80193773580484],
3099
14: [0.433883739117558, 0.900968867902419],
3100
15: [0.974927912181824, 0.222520933956314],
3101
16: [0.781831482468030, -0.623489801858733],
3102
17: [0, -1],
3103
18: [-0.781831482468030, -0.623489801858733],
3104
19: [-0.974927912181824, 0.222520933956315],
3105
20: [-0.433883739117558, 0.900968867902419]}
3106
return graph.Graph(edge_dict, pos=pos_dict, name="Brinkmann graph")
3107
3108
def DoubleStarSnark(self):
3109
r"""
3110
Returns the double star snark.
3111
3112
The double star snark is a 3-regular graph on 30 vertices. See
3113
the :wikipedia:`Wikipedia page on the double star snark
3114
<Double-star_snark>`.
3115
3116
EXAMPLES::
3117
3118
sage: g = graphs.DoubleStarSnark()
3119
sage: g.order()
3120
30
3121
sage: g.size()
3122
45
3123
sage: g.chromatic_number()
3124
3
3125
sage: g.is_hamiltonian()
3126
False
3127
sage: g.automorphism_group().cardinality()
3128
80
3129
sage: g.show()
3130
"""
3131
3132
d = { 0: [1, 14, 15]
3133
, 1: [0, 2, 11]
3134
, 2: [1, 3, 7]
3135
, 3: [2, 4, 18]
3136
, 4: [3, 5, 14]
3137
, 5: [10, 4, 6]
3138
, 6: [5, 21, 7]
3139
, 7: [8, 2, 6]
3140
, 8: [9, 13, 7]
3141
, 9: [24, 8, 10]
3142
, 10: [9, 11, 5]
3143
, 11: [1, 10, 12]
3144
, 12: [11, 27, 13]
3145
, 13: [8, 12, 14]
3146
, 14: [0, 4, 13]
3147
, 15: [0, 16, 29]
3148
, 16: [15, 20, 23]
3149
, 17: [25, 18, 28]
3150
, 18: [3, 17, 19]
3151
, 19: [18, 26, 23]
3152
, 20: [16, 28, 21]
3153
, 21: [20, 6, 22]
3154
, 22: [26, 21, 29]
3155
, 23: [16, 24, 19]
3156
, 24: [25, 9, 23]
3157
, 25: [24, 17, 29]
3158
, 26: [27, 19, 22]
3159
, 27: [12, 26, 28]
3160
, 28: [17, 27, 20]
3161
, 29: [25, 22, 15]
3162
}
3163
3164
g = graph.Graph(d, pos={}, name="Double star snark")
3165
_circle_embedding(g, range(15), radius=2)
3166
_circle_embedding(g, range(15, 30), radius=1.4)
3167
3168
return g
3169
3170
def ChvatalGraph(self):
3171
r"""
3172
Returns the Chvatal graph.
3173
3174
Chvatal graph is one of the few known graphs to satisfy Grunbaum's
3175
conjecture that for every m, n, there is an m-regular,
3176
m-chromatic graph of girth at least n. For more information, see this
3177
`Wikipedia article on the Chvatal graph <http://en.wikipedia.org/wiki/Chv%C3%A1tal_graph>`_.
3178
3179
EXAMPLES:
3180
3181
The Chvatal graph has 12 vertices and 24 edges. It is a 4-regular,
3182
4-chromatic graph with radius 2, diameter 2, and girth 4. ::
3183
3184
sage: G = graphs.ChvatalGraph(); G
3185
Chvatal graph: Graph on 12 vertices
3186
sage: G.order(); G.size()
3187
12
3188
24
3189
sage: G.degree()
3190
[4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
3191
sage: G.chromatic_number()
3192
4
3193
sage: G.radius(); G.diameter(); G.girth()
3194
2
3195
2
3196
4
3197
"""
3198
import networkx
3199
pos_dict = {}
3200
for i in range(5, 10):
3201
x = float(cos((pi / 2) + ((2 * pi) / 5) * i))
3202
y = float(sin((pi / 2) + ((2 * pi) / 5) * i))
3203
pos_dict[i] = (x, y)
3204
for i in range(5):
3205
x = float(2 * (cos((pi / 2) + ((2 * pi) / 5) * (i - 5))))
3206
y = float(2 * (sin((pi / 2) + ((2 * pi) / 5) * (i - 5))))
3207
pos_dict[i] = (x, y)
3208
pos_dict[10] = (0.5, 0)
3209
pos_dict[11] = (-0.5, 0)
3210
3211
return graph.Graph(networkx.chvatal_graph(), pos=pos_dict, name="Chvatal graph")
3212
3213
def DesarguesGraph(self):
3214
"""
3215
Returns the Desargues graph.
3216
3217
PLOTTING: The layout chosen is the same as on the cover of [1].
3218
3219
EXAMPLE::
3220
3221
sage: D = graphs.DesarguesGraph()
3222
sage: L = graphs.LCFGraph(20,[5,-5,9,-9],5)
3223
sage: D.is_isomorphic(L)
3224
True
3225
sage: D.show() # long time
3226
3227
REFERENCE:
3228
3229
- [1] Harary, F. Graph Theory. Reading, MA: Addison-Wesley,
3230
1994.
3231
"""
3232
G=graphs.GeneralizedPetersenGraph(10,3)
3233
G.name("Desargues Graph")
3234
return G
3235
3236
def DurerGraph(self):
3237
r"""
3238
Returns the Dürer graph.
3239
3240
For more information, see this
3241
`Wikipedia article on the Dürer graph <http://en.wikipedia.org/wiki/D%C3%BCrer_graph>`_.
3242
3243
EXAMPLES:
3244
3245
The Dürer graph is named after Albrecht Dürer. It is a planar graph
3246
with 12 vertices and 18 edges. ::
3247
3248
sage: G = graphs.DurerGraph(); G
3249
Durer graph: Graph on 12 vertices
3250
sage: G.is_planar()
3251
True
3252
sage: G.order()
3253
12
3254
sage: G.size()
3255
18
3256
3257
The Dürer graph has chromatic number 3, diameter 4, and girth 3. ::
3258
3259
sage: G.chromatic_number()
3260
3
3261
sage: G.diameter()
3262
4
3263
sage: G.girth()
3264
3
3265
3266
Its automorphism group is isomorphic to `D_6`. ::
3267
3268
sage: ag = G.automorphism_group()
3269
sage: ag.is_isomorphic(DihedralGroup(6))
3270
True
3271
"""
3272
edge_dict = {
3273
0: [1,5,6],
3274
1: [2,7],
3275
2: [3,8],
3276
3: [4,9],
3277
4: [5,10],
3278
5: [11],
3279
6: [8,10],
3280
7: [9,11],
3281
8: [10],
3282
9: [11]}
3283
pos_dict = {
3284
0: [2, 0],
3285
1: [1, 1.73205080756888],
3286
2: [-1, 1.73205080756888],
3287
3: [-2, 0],
3288
4: [-1, -1.73205080756888],
3289
5: [1, -1.73205080756888],
3290
6: [1, 0],
3291
7: [0.5, 0.866025403784439],
3292
8: [-0.5, 0.866025403784439],
3293
9: [-1, 0],
3294
10: [-0.5, -0.866025403784439],
3295
11: [0.5, -0.866025403784439]}
3296
return graph.Graph(edge_dict, pos=pos_dict, name="Durer graph")
3297
3298
def DyckGraph(self):
3299
"""
3300
Returns the Dyck graph.
3301
3302
For more information, see the `MathWorld article on the Dyck graph
3303
<http://mathworld.wolfram.com/DyckGraph.html>`_ or the `Wikipedia
3304
article on the Dyck graph <http://en.wikipedia.org/wiki/Dyck_graph>`_.
3305
3306
EXAMPLES:
3307
3308
The Dyck graph was defined by Walther von Dyck in 1881. It has `32`
3309
vertices and `48` edges, and is a cubic graph (regular of degree `3`)::
3310
3311
sage: G = graphs.DyckGraph(); G
3312
Dyck graph: Graph on 32 vertices
3313
sage: G.order()
3314
32
3315
sage: G.size()
3316
48
3317
sage: G.is_regular()
3318
True
3319
sage: G.is_regular(3)
3320
True
3321
3322
It is non-planar and Hamiltonian, as well as bipartite (making it a
3323
bicubic graph)::
3324
3325
sage: G.is_planar()
3326
False
3327
sage: G.is_hamiltonian()
3328
True
3329
sage: G.is_bipartite()
3330
True
3331
3332
It has radius `5`, diameter `5`, and girth `6`::
3333
3334
sage: G.radius()
3335
5
3336
sage: G.diameter()
3337
5
3338
sage: G.girth()
3339
6
3340
3341
Its chromatic number is `2` and its automorphism group is of order
3342
`192`::
3343
3344
sage: G.chromatic_number()
3345
2
3346
sage: G.automorphism_group().cardinality()
3347
192
3348
3349
It is a non-integral graph as it has irrational eigenvalues::
3350
3351
sage: G.characteristic_polynomial().factor()
3352
(x - 3) * (x + 3) * (x - 1)^9 * (x + 1)^9 * (x^2 - 5)^6
3353
3354
It is a toroidal graph, and its embedding on a torus is dual to an
3355
embedding of the Shrikhande graph (:meth:`ShrikhandeGraph
3356
<GraphGenerators.ShrikhandeGraph>`).
3357
"""
3358
pos_dict = {}
3359
for i in range(8):
3360
pos_dict[i] = [float(cos((2*i) * pi/8)),
3361
float(sin((2*i) * pi/8))]
3362
pos_dict[8 + i] = [0.75 * pos_dict[i][0],
3363
0.75 * pos_dict[i][1]]
3364
pos_dict[16 + i] = [0.50 * pos_dict[i][0],
3365
0.50 * pos_dict[i][1]]
3366
pos_dict[24 + i] = [0.25 * pos_dict[i][0],
3367
0.25 * pos_dict[i][1]]
3368
3369
edge_dict = {
3370
0O00: [0O07, 0O01, 0O10], 0O10: [0O00, 0O27, 0O21],
3371
0O01: [0O00, 0O02, 0O11], 0O11: [0O01, 0O20, 0O22],
3372
0O02: [0O01, 0O03, 0O12], 0O12: [0O02, 0O21, 0O23],
3373
0O03: [0O02, 0O04, 0O13], 0O13: [0O03, 0O22, 0O24],
3374
0O04: [0O03, 0O05, 0O14], 0O14: [0O04, 0O23, 0O25],
3375
0O05: [0O04, 0O06, 0O15], 0O15: [0O05, 0O24, 0O26],
3376
0O06: [0O05, 0O07, 0O16], 0O16: [0O06, 0O25, 0O27],
3377
0O07: [0O06, 0O00, 0O17], 0O17: [0O07, 0O26, 0O20],
3378
3379
0O20: [0O17, 0O11, 0O30], 0O30: [0O20, 0O35, 0O33],
3380
0O21: [0O10, 0O12, 0O31], 0O31: [0O21, 0O36, 0O34],
3381
0O22: [0O11, 0O13, 0O32], 0O32: [0O22, 0O37, 0O35],
3382
0O23: [0O12, 0O14, 0O33], 0O33: [0O23, 0O30, 0O36],
3383
0O24: [0O13, 0O15, 0O34], 0O34: [0O24, 0O31, 0O37],
3384
0O25: [0O14, 0O16, 0O35], 0O35: [0O25, 0O32, 0O30],
3385
0O26: [0O15, 0O17, 0O36], 0O36: [0O26, 0O33, 0O31],
3386
0O27: [0O16, 0O10, 0O37], 0O37: [0O27, 0O34, 0O32],
3387
}
3388
3389
return graph.Graph(edge_dict, pos=pos_dict, name="Dyck graph")
3390
3391
def ErreraGraph(self):
3392
r"""
3393
Returns the Errera graph.
3394
3395
For more information, see this
3396
`Wikipedia article on the Errera graph <http://en.wikipedia.org/wiki/Errera_graph>`_.
3397
3398
EXAMPLES:
3399
3400
The Errera graph is named after Alfred Errera. It is a planar graph
3401
on 17 vertices and having 45 edges. ::
3402
3403
sage: G = graphs.ErreraGraph(); G
3404
Errera graph: Graph on 17 vertices
3405
sage: G.is_planar()
3406
True
3407
sage: G.order()
3408
17
3409
sage: G.size()
3410
45
3411
3412
The Errera graph is Hamiltonian with radius 3, diameter 4, girth 3,
3413
and chromatic number 4. ::
3414
3415
sage: G.is_hamiltonian()
3416
True
3417
sage: G.radius()
3418
3
3419
sage: G.diameter()
3420
4
3421
sage: G.girth()
3422
3
3423
sage: G.chromatic_number()
3424
4
3425
3426
Each vertex degree is either 5 or 6. That is, if `f` counts the
3427
number of vertices of degree 5 and `s` counts the number of vertices
3428
of degree 6, then `f + s` is equal to the order of the Errera
3429
graph. ::
3430
3431
sage: D = G.degree_sequence()
3432
sage: D.count(5) + D.count(6) == G.order()
3433
True
3434
3435
The automorphism group of the Errera graph is isomorphic to the
3436
dihedral group of order 20. ::
3437
3438
sage: ag = G.automorphism_group()
3439
sage: ag.is_isomorphic(DihedralGroup(10))
3440
True
3441
"""
3442
edge_dict = {
3443
0: [1,7,14,15,16],
3444
1: [2,9,14,15],
3445
2: [3,8,9,10,14],
3446
3: [4,9,10,11],
3447
4: [5,10,11,12],
3448
5: [6,11,12,13],
3449
6: [7,8,12,13,16],
3450
7: [13,15,16],
3451
8: [10,12,14,16],
3452
9: [11,13,15],
3453
10: [12],
3454
11: [13],
3455
13: [15],
3456
14: [16]}
3457
return graph.Graph(edge_dict, name="Errera graph")
3458
3459
def FlowerSnark(self):
3460
"""
3461
Returns a Flower Snark.
3462
3463
A flower snark has 20 vertices. It is part of the class of
3464
biconnected cubic graphs with edge chromatic number = 4, known as
3465
snarks. (i.e.: the Petersen graph). All snarks are not Hamiltonian,
3466
non-planar and have Petersen graph graph minors.
3467
3468
PLOTTING: Upon construction, the position dictionary is filled to
3469
override the spring-layout algorithm. By convention, the nodes are
3470
drawn 0-14 on the outer circle, and 15-19 in an inner pentagon.
3471
3472
REFERENCES:
3473
3474
- [1] Weisstein, E. (1999). "Flower Snark - from Wolfram
3475
MathWorld". [Online] Available:
3476
http://mathworld.wolfram.com/FlowerSnark.html [2007, February 17]
3477
3478
EXAMPLES: Inspect a flower snark::
3479
3480
sage: F = graphs.FlowerSnark()
3481
sage: F
3482
Flower Snark: Graph on 20 vertices
3483
sage: F.graph6_string()
3484
'ShCGHC@?GGg@?@?Gp?K??C?CA?G?_G?Cc'
3485
3486
Now show it::
3487
3488
sage: F.show() # long time
3489
"""
3490
pos_dict = {}
3491
for i in range(15):
3492
x = float(2.5*(cos((pi/2) + ((2*pi)/15)*i)))
3493
y = float(2.5*(sin((pi/2) + ((2*pi)/15)*i)))
3494
pos_dict[i] = (x,y)
3495
for i in range(15,20):
3496
x = float(cos((pi/2) + ((2*pi)/5)*i))
3497
y = float(sin((pi/2) + ((2*pi)/5)*i))
3498
pos_dict[i] = (x,y)
3499
return graph.Graph({0:[1,14,15],1:[2,11],2:[3,7],3:[2,4,16],4:[5,14], \
3500
5:[6,10],6:[5,7,17],8:[7,9,13],9:[10,18],11:[10,12], \
3501
12:[13,19],13:[14],15:[19],16:[15,17],18:[17,19]}, \
3502
pos=pos_dict, name="Flower Snark")
3503
3504
def FosterGraph(self):
3505
"""
3506
Returns the Foster graph.
3507
3508
See the :wikipedia:`Wikipedia page on the Foster Graph
3509
<Foster_graph>`.
3510
3511
EXAMPLE::
3512
3513
sage: g = graphs.FosterGraph()
3514
sage: g.order()
3515
90
3516
sage: g.size()
3517
135
3518
sage: g.diameter()
3519
8
3520
sage: g.girth()
3521
10
3522
sage: g.automorphism_group().cardinality()
3523
4320
3524
sage: g.is_hamiltonian()
3525
True
3526
"""
3527
g= graphs.LCFGraph(90, [17, -9, 37, -37, 9, -17], 15)
3528
g.name("Foster Graph")
3529
return g
3530
3531
3532
def FranklinGraph(self):
3533
r"""
3534
Returns the Franklin graph.
3535
3536
For more information, see this
3537
`Wikipedia article on the Franklin graph <http://en.wikipedia.org/wiki/Franklin_graph>`_.
3538
3539
EXAMPLES:
3540
3541
The Franklin graph is named after Philip Franklin. It is a
3542
3-regular graph on 12 vertices and having 18 edges. ::
3543
3544
sage: G = graphs.FranklinGraph(); G
3545
Franklin graph: Graph on 12 vertices
3546
sage: G.is_regular(3)
3547
True
3548
sage: G.order()
3549
12
3550
sage: G.size()
3551
18
3552
3553
The Franklin graph is a Hamiltonian, bipartite graph with radius 3,
3554
diameter 3, and girth 4. ::
3555
3556
sage: G.is_hamiltonian()
3557
True
3558
sage: G.is_bipartite()
3559
True
3560
sage: G.radius()
3561
3
3562
sage: G.diameter()
3563
3
3564
sage: G.girth()
3565
4
3566
3567
It is a perfect, triangle-free graph having chromatic number 2. ::
3568
3569
sage: G.is_perfect()
3570
True
3571
sage: G.is_triangle_free()
3572
True
3573
sage: G.chromatic_number()
3574
2
3575
"""
3576
edge_dict = {
3577
0: [1,5,6],
3578
1: [2,7],
3579
2: [3,8],
3580
3: [4,9],
3581
4: [5,10],
3582
5: [11],
3583
6: [7,9],
3584
7: [10],
3585
8: [9,11],
3586
10: [11]}
3587
pos_dict = {
3588
0: [2, 0],
3589
1: [1, 1.73205080756888],
3590
2: [-1, 1.73205080756888],
3591
3: [-2, 0],
3592
4: [-1, -1.73205080756888],
3593
5: [1, -1.73205080756888],
3594
6: [1, 0],
3595
7: [0.5, 0.866025403784439],
3596
8: [-0.5, 0.866025403784439],
3597
9: [-1, 0],
3598
10: [-0.5, -0.866025403784439],
3599
11: [0.5, -0.866025403784439]}
3600
return graph.Graph(edge_dict, pos=pos_dict, name="Franklin graph")
3601
3602
def FruchtGraph(self):
3603
"""
3604
Returns a Frucht Graph.
3605
3606
A Frucht graph has 12 nodes and 18 edges. It is the smallest cubic
3607
identity graph. It is planar and it is Hamiltonian.
3608
3609
This constructor is dependent on NetworkX's numeric labeling.
3610
3611
PLOTTING: Upon construction, the position dictionary is filled to
3612
override the spring-layout algorithm. By convention, the first
3613
seven nodes are on the outer circle, with the next four on an inner
3614
circle and the last in the center.
3615
3616
REFERENCES:
3617
3618
- [1] Weisstein, E. (1999). "Frucht Graph - from Wolfram
3619
MathWorld". [Online] Available:
3620
http://mathworld.wolfram.com/FruchtGraph.html [2007, February 17]
3621
3622
EXAMPLES::
3623
3624
sage: FRUCHT = graphs.FruchtGraph()
3625
sage: FRUCHT
3626
Frucht graph: Graph on 12 vertices
3627
sage: FRUCHT.graph6_string()
3628
'KhCKM?_EGK?L'
3629
sage: (graphs.FruchtGraph()).show() # long time
3630
"""
3631
pos_dict = {}
3632
for i in range(7):
3633
x = float(2*(cos((pi/2) + ((2*pi)/7)*i)))
3634
y = float(2*(sin((pi/2) + ((2*pi)/7)*i)))
3635
pos_dict[i] = (x,y)
3636
pos_dict[7] = (0,1)
3637
pos_dict[8] = (-1,0)
3638
pos_dict[9] = (0,-1)
3639
pos_dict[10] = (1,0)
3640
pos_dict[11] = (0,0)
3641
import networkx
3642
G = networkx.frucht_graph()
3643
return graph.Graph(G, pos=pos_dict, name="Frucht graph")
3644
3645
def GoldnerHararyGraph(self):
3646
r"""
3647
Return the Goldner-Harary graph.
3648
3649
For more information, see this
3650
`Wikipedia article on the Goldner-Harary graph <http://en.wikipedia.org/wiki/Goldner%E2%80%93Harary_graph>`_.
3651
3652
EXAMPLES:
3653
3654
The Goldner-Harary graph is named after A. Goldner and Frank Harary.
3655
It is a planar graph having 11 vertices and 27 edges. ::
3656
3657
sage: G = graphs.GoldnerHararyGraph(); G
3658
Goldner-Harary graph: Graph on 11 vertices
3659
sage: G.is_planar()
3660
True
3661
sage: G.order()
3662
11
3663
sage: G.size()
3664
27
3665
3666
The Goldner-Harary graph is chordal with radius 2, diameter 2, and
3667
girth 3. ::
3668
3669
sage: G.is_chordal()
3670
True
3671
sage: G.radius()
3672
2
3673
sage: G.diameter()
3674
2
3675
sage: G.girth()
3676
3
3677
3678
Its chromatic number is 4 and its automorphism group is isomorphic to
3679
the dihedral group `D_6`. ::
3680
3681
sage: G.chromatic_number()
3682
4
3683
sage: ag = G.automorphism_group()
3684
sage: ag.is_isomorphic(DihedralGroup(6))
3685
True
3686
"""
3687
edge_dict = {
3688
0: [1,3,4],
3689
1: [2,3,4,5,6,7,10],
3690
2: [3,7],
3691
3: [7,8,9,10],
3692
4: [3,5,9,10],
3693
5: [10],
3694
6: [7,10],
3695
7: [8,10],
3696
8: [10],
3697
9: [10]}
3698
3699
pos = {
3700
0: (-2, 0),
3701
1: (0, 1.5),
3702
2: (2, 0),
3703
3: (0, -1.5),
3704
4: (-1.5, 0),
3705
5: (-0.5, 0.5),
3706
6: (0.5, 0.5),
3707
7: (1.5, 0),
3708
8: (0.5, -0.5),
3709
9: (-0.5, -0.5),
3710
10: (0, 0)}
3711
3712
return graph.Graph(edge_dict, pos = pos, name="Goldner-Harary graph")
3713
3714
def GrayGraph(self, embedding=1):
3715
r"""
3716
Returns the Gray graph.
3717
3718
See the :wikipedia:`Wikipedia page on the Gray Graph
3719
<Gray_graph>`.
3720
3721
INPUT:
3722
3723
- ``embedding`` -- two embeddings are available, and can be
3724
selected by setting ``embedding`` to 1 or 2.
3725
3726
EXAMPLES::
3727
3728
sage: g = graphs.GrayGraph()
3729
sage: g.order()
3730
54
3731
sage: g.size()
3732
81
3733
sage: g.girth()
3734
8
3735
sage: g.diameter()
3736
6
3737
sage: g.show(figsize=[10, 10]) # long time
3738
sage: graphs.GrayGraph(embedding = 2).show(figsize=[10, 10]) # long time
3739
3740
TESTS::
3741
3742
sage: graphs.GrayGraph(embedding = 3)
3743
Traceback (most recent call last):
3744
...
3745
ValueError: The value of embedding must be 1, 2, or 3.
3746
"""
3747
3748
g = graphs.LCFGraph(54, [-25,7,-7,13,-13,25], 9)
3749
g.name("Gray graph")
3750
3751
if embedding == 1:
3752
o = g.automorphism_group(orbits=True)[-1]
3753
_circle_embedding(g, o[0], center=(0, 0), radius=1)
3754
_circle_embedding(g, o[1], center=(0, 0), radius=.6, shift=-.5)
3755
3756
elif embedding != 2:
3757
raise ValueError("The value of embedding must be 1, 2, or 3.")
3758
3759
return g
3760
3761
def GrotzschGraph(self):
3762
r"""
3763
Returns the Grötzsch graph.
3764
3765
The Grötzsch graph is an example of a triangle-free graph with
3766
chromatic number equal to 4. For more information, see this
3767
`Wikipedia article on Grötzsch graph <http://en.wikipedia.org/wiki/Gr%C3%B6tzsch_graph>`_.
3768
3769
REFERENCE:
3770
3771
- [1] Weisstein, Eric W. "Grotzsch Graph."
3772
From MathWorld--A Wolfram Web Resource.
3773
http://mathworld.wolfram.com/GroetzschGraph.html
3774
3775
EXAMPLES:
3776
3777
The Grötzsch graph is named after Herbert Grötzsch. It is a
3778
Hamiltonian graph with 11 vertices and 20 edges. ::
3779
3780
sage: G = graphs.GrotzschGraph(); G
3781
Grotzsch graph: Graph on 11 vertices
3782
sage: G.is_hamiltonian()
3783
True
3784
sage: G.order()
3785
11
3786
sage: G.size()
3787
20
3788
3789
The Grötzsch graph is triangle-free and having radius 2, diameter 2,
3790
and girth 4. ::
3791
3792
sage: G.is_triangle_free()
3793
True
3794
sage: G.radius()
3795
2
3796
sage: G.diameter()
3797
2
3798
sage: G.girth()
3799
4
3800
3801
Its chromatic number is 4 and its automorphism group is isomorphic
3802
to the dihedral group `D_5`. ::
3803
3804
sage: G.chromatic_number()
3805
4
3806
sage: ag = G.automorphism_group()
3807
sage: ag.is_isomorphic(DihedralGroup(5))
3808
True
3809
"""
3810
g = graph.Graph()
3811
g.add_vertices(range(11))
3812
3813
edges = [];
3814
for u in range(1,6):
3815
edges.append( (0,u) )
3816
3817
edges.append( (10,6) )
3818
3819
for u in range(6,10):
3820
edges.append( (u,u+1) )
3821
edges.append( (u,u-4) )
3822
3823
edges.append( (10,1) )
3824
3825
for u in range(7,11):
3826
edges.append( (u,u-6) )
3827
3828
edges.append((6,5))
3829
3830
g.add_edges(edges)
3831
3832
pos = {}
3833
pos[0] = (0,0)
3834
for u in range(1,6):
3835
theta = (u-1)*2*pi/5
3836
pos[u] = (float(5*sin(theta)),float(5*cos(theta)))
3837
pos[u+5] = (2*pos[u][0], 2*pos[u][1])
3838
3839
g.set_pos(pos)
3840
g.name("Grotzsch graph")
3841
return g
3842
3843
def HeawoodGraph(self):
3844
"""
3845
Returns a Heawood graph.
3846
3847
The Heawood graph is a cage graph that has 14 nodes. It is a cubic
3848
symmetric graph. (See also the Moebius-Kantor graph). It is
3849
nonplanar and Hamiltonian. It has diameter = 3, radius = 3, girth =
3850
6, chromatic number = 2. It is 4-transitive but not 5-transitive.
3851
3852
This constructor is dependent on NetworkX's numeric labeling.
3853
3854
PLOTTING: Upon construction, the position dictionary is filled to
3855
override the spring-layout algorithm. By convention, the nodes are
3856
positioned in a circular layout with the first node appearing at
3857
the top, and then continuing counterclockwise.
3858
3859
REFERENCES:
3860
3861
- [1] Weisstein, E. (1999). "Heawood Graph - from Wolfram
3862
MathWorld". [Online] Available:
3863
http://mathworld.wolfram.com/HeawoodGraph.html [2007, February 17]
3864
3865
EXAMPLES::
3866
3867
sage: H = graphs.HeawoodGraph()
3868
sage: H
3869
Heawood graph: Graph on 14 vertices
3870
sage: H.graph6_string()
3871
'MhEGHC@AI?_PC@_G_'
3872
sage: (graphs.HeawoodGraph()).show() # long time
3873
"""
3874
pos_dict = {}
3875
for i in range(14):
3876
x = float(cos((pi/2) + (pi/7)*i))
3877
y = float(sin((pi/2) + (pi/7)*i))
3878
pos_dict[i] = (x,y)
3879
import networkx
3880
G = networkx.heawood_graph()
3881
return graph.Graph(G, pos=pos_dict, name="Heawood graph")
3882
3883
def HerschelGraph(self):
3884
r"""
3885
Returns the Herschel graph.
3886
3887
For more information, see this
3888
`Wikipedia article on the Herschel graph <http://en.wikipedia.org/wiki/Herschel_graph>`_.
3889
3890
EXAMPLES:
3891
3892
The Herschel graph is named after Alexander Stewart Herschel. It is
3893
a planar, bipartite graph with 11 vertices and 18 edges. ::
3894
3895
sage: G = graphs.HerschelGraph(); G
3896
Herschel graph: Graph on 11 vertices
3897
sage: G.is_planar()
3898
True
3899
sage: G.is_bipartite()
3900
True
3901
sage: G.order()
3902
11
3903
sage: G.size()
3904
18
3905
3906
The Herschel graph is a perfect graph with radius 3, diameter 4, and
3907
girth 4. ::
3908
3909
sage: G.is_perfect()
3910
True
3911
sage: G.radius()
3912
3
3913
sage: G.diameter()
3914
4
3915
sage: G.girth()
3916
4
3917
3918
Its chromatic number is 2 and its automorphism group is
3919
isomorphic to the dihedral group `D_6`. ::
3920
3921
sage: G.chromatic_number()
3922
2
3923
sage: ag = G.automorphism_group()
3924
sage: ag.is_isomorphic(DihedralGroup(6))
3925
True
3926
"""
3927
edge_dict = {
3928
0: [1,3,4],
3929
1: [2,5,6],
3930
2: [3,7],
3931
3: [8,9],
3932
4: [5,9],
3933
5: [10],
3934
6: [7,10],
3935
7: [8],
3936
8: [10],
3937
9: [10]}
3938
pos_dict = {
3939
0: [2, 0],
3940
1: [0, 2],
3941
2: [-2, 0],
3942
3: [0, -2],
3943
4: [1, 0],
3944
5: [0.5, 0.866025403784439],
3945
6: [-0.5, 0.866025403784439],
3946
7: [-1, 0],
3947
8: [-0.5, -0.866025403784439],
3948
9: [0.5, -0.866025403784439],
3949
10: [0, 0]}
3950
return graph.Graph(edge_dict, pos=pos_dict, name="Herschel graph")
3951
3952
def HigmanSimsGraph(self, relabel=True):
3953
r"""
3954
The Higman-Sims graph is a remarkable strongly regular
3955
graph of degree 22 on 100 vertices. For example, it can
3956
be split into two sets of 50 vertices each, so that each
3957
half induces a subgraph isomorphic to the
3958
Hoffman-Singleton graph
3959
(:meth:`~HoffmanSingletonGraph`).
3960
This can be done in 352 ways (see [BROUWER-HS-2009]_).
3961
3962
Its most famous property is that the automorphism
3963
group has an index 2 subgroup which is one of the
3964
26 sporadic groups. [HIGMAN1968]_
3965
3966
The construction used here follows [HAFNER2004]_.
3967
3968
INPUT:
3969
3970
- ``relabel`` - default: ``True``. If ``True`` the
3971
vertices will be labeled with consecutive integers.
3972
If ``False`` the labels are strings that are three
3973
digits long. "xyz" means the vertex is in group
3974
x (zero through three), pentagon or pentagram y
3975
(zero through four), and is vertex z (zero
3976
through four) of that pentagon or pentagram.
3977
See [HAFNER2004]_ for more.
3978
3979
OUTPUT:
3980
3981
The Higman-Sims graph.
3982
3983
EXAMPLES:
3984
3985
A split into the first 50 and last 50 vertices
3986
will induce two copies of the Hoffman-Singleton graph,
3987
and we illustrate another such split, which is obvious
3988
based on the construction used. ::
3989
3990
sage: H = graphs.HigmanSimsGraph()
3991
sage: A = H.subgraph(range(0,50))
3992
sage: B = H.subgraph(range(50,100))
3993
sage: K = graphs.HoffmanSingletonGraph()
3994
sage: K.is_isomorphic(A) and K.is_isomorphic(B)
3995
True
3996
sage: C = H.subgraph(range(25,75))
3997
sage: D = H.subgraph(range(0,25)+range(75,100))
3998
sage: K.is_isomorphic(C) and K.is_isomorphic(D)
3999
True
4000
4001
The automorphism group contains only one nontrivial
4002
proper normal subgroup, which is of index 2 and is
4003
simple. It is known as the Higman-Sims group. ::
4004
4005
sage: H = graphs.HigmanSimsGraph()
4006
sage: G = H.automorphism_group()
4007
sage: g=G.order(); g
4008
88704000
4009
sage: K = G.normal_subgroups()[1]
4010
sage: K.is_simple()
4011
True
4012
sage: g//K.order()
4013
2
4014
4015
REFERENCES:
4016
4017
.. [BROUWER-HS-2009] `Higman-Sims graph
4018
<http://www.win.tue.nl/~aeb/graphs/Higman-Sims.html>`_.
4019
Andries E. Brouwer, accessed 24 October 2009.
4020
.. [HIGMAN1968] A simple group of order 44,352,000,
4021
Math.Z. 105 (1968) 110-113. D.G. Higman & C. Sims.
4022
.. [HAFNER2004] `On the graphs of Hoffman-Singleton and
4023
Higman-Sims
4024
<http://www.combinatorics.org/Volume_11/PDF/v11i1r77.pdf>`_.
4025
The Electronic Journal of Combinatorics 11 (2004), #R77,
4026
Paul R. Hafner, accessed 24 October 2009.
4027
4028
AUTHOR:
4029
4030
- Rob Beezer (2009-10-24)
4031
"""
4032
HS = graph.Graph()
4033
HS.name('Higman-Sims graph')
4034
4035
# Four groups of either five pentagons, or five pentagrams
4036
# 4 x 5 x 5 = 100 vertices
4037
# First digit is "group", second is "penta{gon|gram}", third is "vertex"
4038
vlist = ['%d%d%d'%(g,p,v)
4039
for g in range(4) for p in range(5) for v in range(5)]
4040
for avertex in vlist:
4041
HS.add_vertex(avertex)
4042
4043
# Edges: Within groups 0 and 2, joined as pentagons
4044
# Edges: Within groups 1 and 3, joined as pentagrams
4045
for g in range(4):
4046
shift = 1
4047
if g in [1,3]:
4048
shift += 1
4049
for p in range(5):
4050
for v in range(5):
4051
HS.add_edge(('%d%d%d'%(g,p,v), '%d%d%d'%(g,p,(v+shift)%5)))
4052
4053
# Edges: group 0 to group 1
4054
for x in range(5):
4055
for m in range(5):
4056
for c in range(5):
4057
y = (m*x+c)%5
4058
HS.add_edge(('0%d%d'%(x,y), '1%d%d'%(m,c)))
4059
4060
# Edges: group 1 to group 2
4061
for m in range(5):
4062
for A in range(5):
4063
for B in range(5):
4064
c = (2*(m-A)*(m-A)+B)%5
4065
HS.add_edge(('1%d%d'%(m,c), '2%d%d'%(A,B)))
4066
4067
# Edges: group 2 to group 3
4068
for A in range(5):
4069
for a in range(5):
4070
for b in range(5):
4071
B = (2*A*A+3*a*A-a*a+b)%5
4072
HS.add_edge(('2%d%d'%(A,B), '3%d%d'%(a,b)))
4073
4074
# Edges: group 3 to group 0
4075
for a in range(5):
4076
for b in range(5):
4077
for x in range(5):
4078
y = ((x-a)*(x-a)+b)%5
4079
HS.add_edge(('3%d%d'%(a,b), '0%d%d'%(x,y)))
4080
4081
# Edges: group 0 to group 2
4082
for x in range(5):
4083
for A in range(5):
4084
for B in range(5):
4085
y = (3*x*x+A*x+B+1)%5
4086
HS.add_edge(('0%d%d'%(x,y), '2%d%d'%(A,B)))
4087
y = (3*x*x+A*x+B-1)%5
4088
HS.add_edge(('0%d%d'%(x,y), '2%d%d'%(A,B)))
4089
4090
# Edges: group 1 to group 3
4091
for m in range(5):
4092
for a in range(5):
4093
for b in range(5):
4094
c = (m*(m-a)+b+2)%5
4095
HS.add_edge(('1%d%d'%(m,c), '3%d%d'%(a,b)))
4096
c = (m*(m-a)+b-2)%5
4097
HS.add_edge(('1%d%d'%(m,c), '3%d%d'%(a,b)))
4098
4099
# Rename to integer vertex labels, creating dictionary
4100
# Or not, and create identity mapping
4101
if relabel:
4102
vmap = HS.relabel(return_map=True)
4103
else:
4104
vmap={}
4105
for v in vlist:
4106
vmap[v] = v
4107
# Layout vertices in a circle
4108
# In the order given in vlist
4109
# Using labels from vmap
4110
pos_dict = {}
4111
for i in range(100):
4112
x = float(cos((pi/2) + ((2*pi)/100)*i))
4113
y = float(sin((pi/2) + ((2*pi)/100)*i))
4114
pos_dict[vmap[vlist[i]]] = (x,y)
4115
HS.set_pos(pos_dict)
4116
return HS
4117
4118
def HoffmanSingletonGraph(self):
4119
r"""
4120
Returns the Hoffman-Singleton graph.
4121
4122
The Hoffman-Singleton graph is the Moore graph of degree 7,
4123
diameter 2 and girth 5. The Hoffman-Singleton theorem states that
4124
any Moore graph with girth 5 must have degree 2, 3, 7 or 57. The
4125
first three respectively are the pentagon, the Petersen graph, and
4126
the Hoffman-Singleton graph. The existence of a Moore graph with
4127
girth 5 and degree 57 is still open.
4128
4129
A Moore graph is a graph with diameter `d` and girth
4130
`2d + 1`. This implies that the graph is regular, and
4131
distance regular.
4132
4133
PLOTTING: Upon construction, the position dictionary is filled to
4134
override the spring-layout algorithm. A novel algorithm written by
4135
Tom Boothby gives a random layout which is pleasing to the eye.
4136
4137
REFERENCES:
4138
4139
- [1] Godsil, C. and Royle, G. Algebraic Graph Theory.
4140
Springer, 2001.
4141
4142
EXAMPLES::
4143
4144
sage: HS = graphs.HoffmanSingletonGraph()
4145
sage: Set(HS.degree())
4146
{7}
4147
sage: HS.girth()
4148
5
4149
sage: HS.diameter()
4150
2
4151
sage: HS.num_verts()
4152
50
4153
4154
Note that you get a different layout each time you create the graph.
4155
::
4156
4157
sage: HS.layout()[1]
4158
(-0.844..., 0.535...)
4159
sage: graphs.HoffmanSingletonGraph().layout()[1]
4160
(-0.904..., 0.425...)
4161
4162
"""
4163
H = graph.Graph({ \
4164
'q00':['q01'], 'q01':['q02'], 'q02':['q03'], 'q03':['q04'], 'q04':['q00'], \
4165
'q10':['q11'], 'q11':['q12'], 'q12':['q13'], 'q13':['q14'], 'q14':['q10'], \
4166
'q20':['q21'], 'q21':['q22'], 'q22':['q23'], 'q23':['q24'], 'q24':['q20'], \
4167
'q30':['q31'], 'q31':['q32'], 'q32':['q33'], 'q33':['q34'], 'q34':['q30'], \
4168
'q40':['q41'], 'q41':['q42'], 'q42':['q43'], 'q43':['q44'], 'q44':['q40'], \
4169
'p00':['p02'], 'p02':['p04'], 'p04':['p01'], 'p01':['p03'], 'p03':['p00'], \
4170
'p10':['p12'], 'p12':['p14'], 'p14':['p11'], 'p11':['p13'], 'p13':['p10'], \
4171
'p20':['p22'], 'p22':['p24'], 'p24':['p21'], 'p21':['p23'], 'p23':['p20'], \
4172
'p30':['p32'], 'p32':['p34'], 'p34':['p31'], 'p31':['p33'], 'p33':['p30'], \
4173
'p40':['p42'], 'p42':['p44'], 'p44':['p41'], 'p41':['p43'], 'p43':['p40']})
4174
for j in range(5):
4175
for i in range(5):
4176
for k in range(5):
4177
con = (i+j*k)%5
4178
H.add_edge(('q%d%d'%(k,con),'p%d%d'%(j,i)))
4179
H.name('Hoffman-Singleton graph')
4180
from sage.combinat.combinat import permutations
4181
from sage.misc.prandom import randint
4182
P = permutations([1,2,3,4])
4183
qpp = [0]+P[randint(0,23)]
4184
ppp = [0]+P[randint(0,23)]
4185
qcycle = lambda i,s : ['q%s%s'%(i,(j+s)%5) for j in qpp]
4186
pcycle = lambda i,s : ['p%s%s'%(i,(j+s)%5) for j in ppp]
4187
l = 0
4188
s = 0
4189
D = []
4190
while l < 5:
4191
for q in qcycle(l,s):
4192
D.append(q)
4193
vv = 'p%s'%q[1]
4194
s = int([v[-1] for v in H.neighbors(q) if v[:2] == vv][0])
4195
for p in pcycle(l,s):
4196
D.append(p)
4197
vv = 'q%s'%(int(p[1])+1)
4198
v = [v[-1] for v in H.neighbors(p) if v[:2] == vv]
4199
if len(v):
4200
s = int(v[0])
4201
l+=1
4202
map = H.relabel(return_map=True)
4203
pos_dict = {}
4204
for i in range(50):
4205
x = float(cos((pi/2) + ((2*pi)/50)*i))
4206
y = float(sin((pi/2) + ((2*pi)/50)*i))
4207
pos_dict[map[D[i]]] = (x,y)
4208
H.set_pos(pos_dict)
4209
return H
4210
4211
def LjubljanaGraph(self, embedding=1):
4212
r"""
4213
Returns the Ljubljana Graph.
4214
4215
The Ljubljana graph is a bipartite 3-regular graph on 112
4216
vertices and 168 edges. It is not vertex-transitive as it has
4217
two orbits which are also independent sets of size 56. See the
4218
:wikipedia:`Wikipedia page on the Ljubljana Graph
4219
<Ljubljana_graph>`.
4220
4221
The default embedding is obtained from the Heawood graph.
4222
4223
INPUT:
4224
4225
- ``embedding`` -- two embeddings are available, and can be
4226
selected by setting ``embedding`` to 1 or 2.
4227
4228
EXAMPLES::
4229
4230
sage: g = graphs.LjubljanaGraph()
4231
sage: g.order()
4232
112
4233
sage: g.size()
4234
168
4235
sage: g.girth()
4236
10
4237
sage: g.diameter()
4238
8
4239
sage: g.show(figsize=[10, 10]) # long time
4240
sage: graphs.LjubljanaGraph(embedding=2).show(figsize=[10, 10]) # long time
4241
4242
TESTS::
4243
4244
sage: graphs.LjubljanaGraph(embedding=3)
4245
Traceback (most recent call last):
4246
...
4247
ValueError: The value of embedding must be 1 or 2.
4248
"""
4249
4250
L = [47, -23, -31, 39, 25, -21, -31, -41, 25, 15, 29, -41, -19, 15,
4251
-49, 33, 39, -35, -21, 17, -33, 49, 41, 31, -15, -29, 41, 31,
4252
-15, -25, 21, 31, -51, -25, 23, 9, -17, 51, 35, -29, 21, -51,
4253
-39, 33, -9, -51, 51, -47, -33, 19, 51, -21, 29, 21, -31, -39]
4254
4255
g = graphs.LCFGraph(112, L, 2)
4256
g.name("Ljubljana graph")
4257
4258
if embedding == 1:
4259
return g
4260
4261
elif embedding == 2:
4262
dh = graphs.HeawoodGraph().get_pos()
4263
4264
# Correspondence between the vertices of the Heawood Graph and
4265
# 8-sets of the Ljubljana Graph.
4266
4267
d = {
4268
0: [1, 21, 39, 57, 51, 77, 95, 107],
4269
1: [2, 22, 38, 58, 50, 78, 94, 106],
4270
2: [3, 23, 37, 59, 49, 79, 93, 105],
4271
3: [4, 24, 36, 60, 48, 80, 92, 104],
4272
4: [5, 25, 35, 61, 15, 81, 91, 71],
4273
9: [6, 26, 44, 62, 16, 82, 100, 72],
4274
10: [7, 27, 45, 63, 17, 83, 101, 73],
4275
11: [8, 28, 46, 64, 18, 84, 102, 74],
4276
12: [9, 29, 47, 65, 19, 85, 103, 75],
4277
13: [10, 30, 0, 66, 20, 86, 56, 76],
4278
8: [11, 31, 111, 67, 99, 87, 55, 43],
4279
7: [12, 32, 110, 68, 98, 88, 54, 42],
4280
6: [13, 33, 109, 69, 97, 89, 53, 41],
4281
5: [14, 34, 108, 70, 96, 90, 52, 40]
4282
}
4283
4284
# The vertices of each 8-set are plotted on a circle, and the
4285
# circles are slowly shifted to obtain a symmetric drawing.
4286
4287
for i, (u, vertices) in enumerate(d.iteritems()):
4288
_circle_embedding(g, vertices, center=dh[u], radius=.1,
4289
shift=8.*i/14)
4290
4291
return g
4292
4293
else:
4294
raise ValueError("The value of embedding must be 1 or 2.")
4295
4296
def KneserGraph(self,n,k):
4297
r"""
4298
Returns the Kneser Graph with parameters `n, k`.
4299
4300
The Kneser Graph with parameters `n,k` is the graph
4301
whose vertices are the `k`-subsets of `[0,1,\dots,n-1]`, and such
4302
that two vertices are adjacent if their corresponding sets
4303
are disjoint.
4304
4305
For example, the Petersen Graph can be defined
4306
as the Kneser Graph with parameters `5,2`.
4307
4308
EXAMPLE::
4309
4310
sage: KG=graphs.KneserGraph(5,2)
4311
sage: print KG.vertices()
4312
[{4, 5}, {1, 3}, {2, 5}, {2, 3}, {3, 4}, {3, 5}, {1, 4}, {1, 5}, {1, 2}, {2, 4}]
4313
sage: P=graphs.PetersenGraph()
4314
sage: P.is_isomorphic(KG)
4315
True
4316
4317
TESTS::
4318
4319
sage: KG=graphs.KneserGraph(0,0)
4320
Traceback (most recent call last):
4321
...
4322
ValueError: Parameter n should be a strictly positive integer
4323
sage: KG=graphs.KneserGraph(5,6)
4324
Traceback (most recent call last):
4325
...
4326
ValueError: Parameter k should be a strictly positive integer inferior to n
4327
"""
4328
4329
if not n>0:
4330
raise ValueError, "Parameter n should be a strictly positive integer"
4331
if not (k>0 and k<=n):
4332
raise ValueError, "Parameter k should be a strictly positive integer inferior to n"
4333
4334
g=graph.Graph(name="Kneser graph with parameters "+str(n)+","+str(k))
4335
from sage.combinat.subset import Subsets
4336
4337
if k>n/2:
4338
g.add_vertices(Subsets(n,k).list())
4339
4340
S = Subsets(n,k)
4341
for s in S:
4342
for t in Subsets(S.s.difference(s),k):
4343
g.add_edge(s,t)
4344
4345
return g
4346
4347
def McGeeGraph(self, embedding=2):
4348
r"""
4349
Returns the McGee Graph.
4350
4351
See the :wikipedia:`Wikipedia page on the McGee Graph
4352
<McGee_graph>`.
4353
4354
INPUT:
4355
4356
- ``embedding`` -- two embeddings are available, and can be
4357
selected by setting ``embedding`` to 1 or 2.
4358
4359
EXAMPLES::
4360
4361
sage: g = graphs.McGeeGraph()
4362
sage: g.order()
4363
24
4364
sage: g.size()
4365
36
4366
sage: g.girth()
4367
7
4368
sage: g.diameter()
4369
4
4370
sage: g.show()
4371
sage: graphs.McGeeGraph(embedding=1).show()
4372
4373
TESTS::
4374
4375
sage: graphs.McGeeGraph(embedding=3)
4376
Traceback (most recent call last):
4377
...
4378
ValueError: The value of embedding must be 1 or 2.
4379
"""
4380
4381
L = [47, -23, -31, 39, 25, -21, -31, -41, 25, 15, 29, -41, -19, 15,
4382
-49, 33, 39, -35, -21, 17, -33, 49, 41, 31, -15, -29, 41, 31,
4383
-15, -25, 21, 31, -51, -25, 23, 9, -17, 51, 35, -29, 21, -51,
4384
-39, 33, -9, -51, 51, -47, -33, 19, 51, -21, 29, 21, -31, -39]
4385
4386
g = graphs.LCFGraph(24, [12, 7, -7], 8)
4387
g.name('McGee graph')
4388
4389
if embedding == 1:
4390
return g
4391
4392
elif embedding == 2:
4393
4394
o = [[7, 2, 13, 8, 19, 14, 1, 20],
4395
[5, 4, 11, 10, 17, 16, 23, 22],
4396
[3, 12, 9, 18, 15, 0, 21, 6]]
4397
4398
_circle_embedding(g, o[0], radius=1.5)
4399
_circle_embedding(g, o[1], radius=3, shift=-.5)
4400
_circle_embedding(g, o[2], radius=2.25, shift=.5)
4401
4402
return g
4403
4404
else:
4405
raise ValueError("The value of embedding must be 1 or 2.")
4406
4407
def MoebiusKantorGraph(self):
4408
"""
4409
Returns a Moebius-Kantor Graph.
4410
4411
A Moebius-Kantor graph is a cubic symmetric graph. (See also the
4412
Heawood graph). It has 16 nodes and 24 edges. It is nonplanar and
4413
Hamiltonian. It has diameter = 4, girth = 6, and chromatic number =
4414
2. It is identical to the Generalized Petersen graph, P[8,3].
4415
4416
PLOTTING: See the plotting section for the generalized Petersen graphs.
4417
4418
REFERENCES:
4419
4420
- [1] Weisstein, E. (1999). "Moebius-Kantor Graph - from
4421
Wolfram MathWorld". [Online] Available:
4422
http://mathworld.wolfram.com/Moebius-KantorGraph.html [2007,
4423
February 17]
4424
4425
EXAMPLES::
4426
4427
sage: MK = graphs.MoebiusKantorGraph()
4428
sage: MK
4429
Moebius-Kantor Graph: Graph on 16 vertices
4430
sage: MK.graph6_string()
4431
'OhCGKE?O@?ACAC@I?Q_AS'
4432
sage: (graphs.MoebiusKantorGraph()).show() # long time
4433
"""
4434
G=graphs.GeneralizedPetersenGraph(8,3)
4435
G.name("Moebius-Kantor Graph")
4436
return G
4437
4438
def MoserSpindle(self):
4439
r"""
4440
Returns the Moser spindle.
4441
4442
For more information, see this
4443
`MathWorld article on the Moser spindle <http://mathworld.wolfram.com/MoserSpindle.html>`_.
4444
4445
EXAMPLES:
4446
4447
The Moser spindle is a planar graph having 7 vertices and 11 edges. ::
4448
4449
sage: G = graphs.MoserSpindle(); G
4450
Moser spindle: Graph on 7 vertices
4451
sage: G.is_planar()
4452
True
4453
sage: G.order()
4454
7
4455
sage: G.size()
4456
11
4457
4458
It is a Hamiltonian graph with radius 2, diameter 2, and girth 3. ::
4459
4460
sage: G.is_hamiltonian()
4461
True
4462
sage: G.radius()
4463
2
4464
sage: G.diameter()
4465
2
4466
sage: G.girth()
4467
3
4468
4469
The Moser spindle has chromatic number 4 and its automorphism
4470
group is isomorphic to the dihedral group `D_4`. ::
4471
4472
sage: G.chromatic_number()
4473
4
4474
sage: ag = G.automorphism_group()
4475
sage: ag.is_isomorphic(DihedralGroup(4))
4476
True
4477
"""
4478
edge_dict = {
4479
0: [1,4,5,6],
4480
1: [2,5],
4481
2: [3,5],
4482
3: [4,6],
4483
4: [6]}
4484
pos_dict = {
4485
0: [0, 2],
4486
1: [-1.90211303259031, 0.618033988749895],
4487
2: [-1.17557050458495, -1.61803398874989],
4488
3: [1.17557050458495, -1.61803398874989],
4489
4: [1.90211303259031, 0.618033988749895],
4490
5: [1, 0],
4491
6: [-1, 0]}
4492
return graph.Graph(edge_dict, pos=pos_dict, name="Moser spindle")
4493
4494
def MycielskiGraph(self, k=1, relabel=True):
4495
r"""
4496
Returns the `k`-th Mycielski Graph.
4497
4498
The graph `M_k` is triangle-free and has chromatic number
4499
equal to `k`. These graphs show, constructively, that there
4500
are triangle-free graphs with arbitrarily high chromatic
4501
number.
4502
4503
The Mycielski graphs are built recursively starting with
4504
`M_0`, an empty graph; `M_1`, a single vertex graph; and `M_2`
4505
is the graph `K_2`. `M_{k+1}` is then built from `M_k`
4506
as follows:
4507
4508
If the vertices of `M_k` are `v_1,\ldots,v_n`, then the
4509
vertices of `M_{k+1}` are
4510
`v_1,\ldots,v_n,w_1,\ldots,w_n,z`. Vertices `v_1,\ldots,v_n`
4511
induce a copy of `M_k`. Vertices `w_1,\ldots,w_n` are an
4512
independent set. Vertex `z` is adjacent to all the
4513
`w_i`-vertices. Finally, vertex `w_i` is adjacent to vertex
4514
`v_j` iff `v_i` is adjacent to `v_j`.
4515
4516
INPUT:
4517
4518
- ``k`` Number of steps in the construction process.
4519
4520
- ``relabel`` Relabel the vertices so their names are the integers
4521
``range(n)`` where ``n`` is the number of vertices in the graph.
4522
4523
EXAMPLE:
4524
4525
The Mycielski graph `M_k` is triangle-free and has chromatic
4526
number equal to `k`. ::
4527
4528
sage: g = graphs.MycielskiGraph(5)
4529
sage: g.is_triangle_free()
4530
True
4531
sage: g.chromatic_number()
4532
5
4533
4534
The graphs `M_4` is (isomorphic to) the Grotzsch graph. ::
4535
4536
sage: g = graphs.MycielskiGraph(4)
4537
sage: g.is_isomorphic(graphs.GrotzschGraph())
4538
True
4539
4540
REFERENCES:
4541
4542
- [1] Weisstein, Eric W. "Mycielski Graph."
4543
From MathWorld--A Wolfram Web Resource.
4544
http://mathworld.wolfram.com/MycielskiGraph.html
4545
4546
"""
4547
g = graph.Graph()
4548
g.name("Mycielski Graph " + str(k))
4549
4550
if k<0:
4551
raise ValueError, "parameter k must be a nonnegative integer"
4552
4553
if k == 0:
4554
return g
4555
4556
if k == 1:
4557
g.add_vertex(0)
4558
return g
4559
4560
if k == 2:
4561
g.add_edge(0,1)
4562
return g
4563
4564
g0 = graphs.MycielskiGraph(k-1)
4565
g = graphs.MycielskiStep(g0)
4566
g.name("Mycielski Graph " + str(k))
4567
if relabel: g.relabel()
4568
4569
return g
4570
4571
def MycielskiStep(self, g):
4572
r"""
4573
Perform one iteration of the Mycielski construction.
4574
4575
See the documentation for ``MycielskiGraph`` which uses this
4576
method. We expose it to all users in case they may find it
4577
useful.
4578
4579
EXAMPLE. One iteration of the Mycielski step applied to the
4580
5-cycle yields a graph isomorphic to the Grotzsch graph ::
4581
4582
sage: g = graphs.CycleGraph(5)
4583
sage: h = graphs.MycielskiStep(g)
4584
sage: h.is_isomorphic(graphs.GrotzschGraph())
4585
True
4586
"""
4587
4588
# Make a copy of the input graph g
4589
gg = g.copy()
4590
4591
# rename a vertex v of gg as (1,v)
4592
renamer = dict( [ (v, (1,v)) for v in g.vertices() ] )
4593
gg.relabel(renamer)
4594
4595
# add the w vertices to gg as (2,v)
4596
wlist = [ (2,v) for v in g.vertices() ]
4597
gg.add_vertices(wlist)
4598
4599
# add the z vertex as (0,0)
4600
gg.add_vertex((0,0))
4601
4602
# add the edges from z to w_i
4603
gg.add_edges( [ ( (0,0) , (2,v) ) for v in g.vertices() ] )
4604
4605
# make the v_i w_j edges
4606
for v in g.vertices():
4607
gg.add_edges( [ ((1,v),(2,vv)) for vv in g.neighbors(v) ] )
4608
4609
return gg
4610
4611
def NauruGraph(self, embedding=2):
4612
"""
4613
Returns the Nauru Graph.
4614
4615
See the :wikipedia:`Wikipedia page on the Nauru Graph
4616
<Nauru_graph>`.
4617
4618
INPUT:
4619
4620
- ``embedding`` -- two embeddings are available, and can be
4621
selected by setting ``embedding`` to 1 or 2.
4622
4623
EXAMPLES::
4624
4625
sage: g = graphs.NauruGraph()
4626
sage: g.order()
4627
24
4628
sage: g.size()
4629
36
4630
sage: g.girth()
4631
6
4632
sage: g.diameter()
4633
4
4634
sage: g.show()
4635
sage: graphs.NauruGraph(embedding=1).show()
4636
4637
TESTS::
4638
4639
sage: graphs.NauruGraph(embedding=3)
4640
Traceback (most recent call last):
4641
...
4642
ValueError: The value of embedding must be 1 or 2.
4643
sage: graphs.NauruGraph(embedding=1).is_isomorphic(g)
4644
True
4645
"""
4646
4647
if embedding == 1:
4648
g = graphs.LCFGraph(24, [5, -9, 7, -7, 9, -5], 4)
4649
g.name('Nauru Graph')
4650
return g
4651
elif embedding == 2:
4652
g = graphs.GeneralizedPetersenGraph(12, 5)
4653
g.name("Nauru Graph")
4654
return g
4655
else:
4656
raise ValueError("The value of embedding must be 1 or 2.")
4657
4658
def PappusGraph(self):
4659
"""
4660
Returns the Pappus graph, a graph on 18 vertices.
4661
4662
The Pappus graph is cubic, symmetric, and distance-regular.
4663
4664
EXAMPLES::
4665
4666
sage: G = graphs.PappusGraph()
4667
sage: G.show() # long time
4668
sage: L = graphs.LCFGraph(18, [5,7,-7,7,-7,-5], 3)
4669
sage: L.show() # long time
4670
sage: G.is_isomorphic(L)
4671
True
4672
"""
4673
pos_dict = {}
4674
for i in range(6):
4675
pos_dict[i] = [float(cos(pi/2 + ((2*pi)/6)*i)),\
4676
float(sin(pi/2 + ((2*pi)/6)*i))]
4677
pos_dict[6 + i] = [(2/3.0)*float(cos(pi/2 + ((2*pi)/6)*i)),\
4678
(2/3.0)*float(sin(pi/2 + ((2*pi)/6)*i))]
4679
pos_dict[12 + i] = [(1/3.0)*float(cos(pi/2 + ((2*pi)/6)*i)),\
4680
(1/3.0)*float(sin(pi/2 + ((2*pi)/6)*i))]
4681
return graph.Graph({0:[1,5,6],1:[2,7],2:[3,8],3:[4,9],4:[5,10],\
4682
5:[11],6:[13,17],7:[12,14],8:[13,15],9:[14,16],\
4683
10:[15,17],11:[12,16],12:[15],13:[16],14:[17]},\
4684
pos=pos_dict, name="Pappus Graph")
4685
4686
def PetersenGraph(self):
4687
"""
4688
The Petersen Graph is a named graph that consists of 10 vertices
4689
and 15 edges, usually drawn as a five-point star embedded in a
4690
pentagon.
4691
4692
The Petersen Graph is a common counterexample. For example, it is
4693
not Hamiltonian.
4694
4695
PLOTTING: See the plotting section for the generalized Petersen graphs.
4696
4697
EXAMPLES: We compare below the Petersen graph with the default
4698
spring-layout versus a planned position dictionary of [x,y]
4699
tuples::
4700
4701
sage: petersen_spring = Graph({0:[1,4,5], 1:[0,2,6], 2:[1,3,7], 3:[2,4,8], 4:[0,3,9], 5:[0,7,8], 6:[1,8,9], 7:[2,5,9], 8:[3,5,6], 9:[4,6,7]})
4702
sage: petersen_spring.show() # long time
4703
sage: petersen_database = graphs.PetersenGraph()
4704
sage: petersen_database.show() # long time
4705
"""
4706
P=graphs.GeneralizedPetersenGraph(5,2)
4707
P.name("Petersen graph")
4708
return P
4709
4710
def ShrikhandeGraph(self):
4711
"""
4712
Returns the Shrikhande graph.
4713
4714
For more information, see the `MathWorld article on the Shrikhande graph
4715
<http://mathworld.wolfram.com/ShrikhandeGraph.html>`_ or the `Wikipedia
4716
article on the Shrikhande graph
4717
<http://en.wikipedia.org/wiki/Shrikhande_graph>`_.
4718
4719
EXAMPLES:
4720
4721
The Shrikhande graph was defined by S. S. Shrikhande in 1959. It has
4722
`16` vertices and `48` edges, and is strongly regular of degree `6` with
4723
parameters `(2,2)`::
4724
4725
sage: G = graphs.ShrikhandeGraph(); G
4726
Shrikhande graph: Graph on 16 vertices
4727
sage: G.order()
4728
16
4729
sage: G.size()
4730
48
4731
sage: G.is_regular(6)
4732
True
4733
sage: set([ len([x for x in G.neighbors(i) if x in G.neighbors(j)])
4734
... for i in range(G.order())
4735
... for j in range(i) ])
4736
set([2])
4737
4738
It is non-planar, and both Hamiltonian and Eulerian::
4739
4740
sage: G.is_planar()
4741
False
4742
sage: G.is_hamiltonian()
4743
True
4744
sage: G.is_eulerian()
4745
True
4746
4747
It has radius `2`, diameter `2`, and girth `3`::
4748
4749
sage: G.radius()
4750
2
4751
sage: G.diameter()
4752
2
4753
sage: G.girth()
4754
3
4755
4756
Its chromatic number is `4` and its automorphism group is of order
4757
`192`::
4758
4759
sage: G.chromatic_number()
4760
4
4761
sage: G.automorphism_group().cardinality()
4762
192
4763
4764
It is an integral graph since it has only integral eigenvalues::
4765
4766
sage: G.characteristic_polynomial().factor()
4767
(x - 6) * (x - 2)^6 * (x + 2)^9
4768
4769
It is a toroidal graph, and its embedding on a torus is dual to an
4770
embedding of the Dyck graph (:meth:`DyckGraph <GraphGenerators.DyckGraph>`).
4771
"""
4772
pos_dict = {}
4773
for i in range(8):
4774
pos_dict[i] = [float(cos((2*i) * pi/8)),
4775
float(sin((2*i) * pi/8))]
4776
pos_dict[8 + i] = [0.5 * pos_dict[i][0],
4777
0.5 * pos_dict[i][1]]
4778
edge_dict = {
4779
0O00: [0O06, 0O07, 0O01, 0O02, 0O11, 0O17],
4780
0O01: [0O07, 0O00, 0O02, 0O03, 0O12, 0O10],
4781
0O02: [0O00, 0O01, 0O03, 0O04, 0O13, 0O11],
4782
0O03: [0O01, 0O02, 0O04, 0O05, 0O14, 0O12],
4783
0O04: [0O02, 0O03, 0O05, 0O06, 0O15, 0O13],
4784
0O05: [0O03, 0O04, 0O06, 0O07, 0O16, 0O14],
4785
0O06: [0O04, 0O05, 0O07, 0O00, 0O17, 0O15],
4786
0O07: [0O05, 0O06, 0O00, 0O01, 0O10, 0O16],
4787
4788
0O10: [0O12, 0O13, 0O15, 0O16, 0O07, 0O01],
4789
0O11: [0O13, 0O14, 0O16, 0O17, 0O00, 0O02],
4790
0O12: [0O14, 0O15, 0O17, 0O10, 0O01, 0O03],
4791
0O13: [0O15, 0O16, 0O10, 0O11, 0O02, 0O04],
4792
0O14: [0O16, 0O17, 0O11, 0O12, 0O03, 0O05],
4793
0O15: [0O17, 0O10, 0O12, 0O13, 0O04, 0O06],
4794
0O16: [0O10, 0O11, 0O13, 0O14, 0O05, 0O07],
4795
0O17: [0O11, 0O12, 0O14, 0O15, 0O06, 0O00]
4796
}
4797
4798
return graph.Graph(edge_dict, pos=pos_dict, name="Shrikhande graph")
4799
4800
def ThomsenGraph(self):
4801
"""
4802
Returns the Thomsen Graph.
4803
4804
The Thomsen Graph is actually a complete bipartite graph with (n1,
4805
n2) = (3, 3). It is also called the Utility graph.
4806
4807
PLOTTING: See CompleteBipartiteGraph.
4808
4809
EXAMPLES::
4810
4811
sage: T = graphs.ThomsenGraph()
4812
sage: T
4813
Thomsen graph: Graph on 6 vertices
4814
sage: T.graph6_string()
4815
'EFz_'
4816
sage: (graphs.ThomsenGraph()).show() # long time
4817
"""
4818
pos_dict = {0:(-1,1),1:(0,1),2:(1,1),3:(-1,0),4:(0,0),5:(1,0)}
4819
import networkx
4820
G = networkx.complete_bipartite_graph(3,3)
4821
return graph.Graph(G, pos=pos_dict, name="Thomsen graph")
4822
4823
def Tutte12Cage(self):
4824
r"""
4825
Returns Tutte's 12-Cage.
4826
4827
See the :wikipedia:`Wikipedia page on the Tutte 12-Cage
4828
<Tutte_12-cage>`.
4829
4830
EXAMPLES::
4831
4832
sage: g = graphs.Tutte12Cage()
4833
sage: g.order()
4834
126
4835
sage: g.size()
4836
189
4837
sage: g.girth()
4838
12
4839
sage: g.diameter()
4840
6
4841
sage: g.show()
4842
"""
4843
L = [17, 27, -13, -59, -35, 35, -11, 13, -53, 53, -27, 21, 57, 11,
4844
-21, -57, 59, -17]
4845
4846
g = graphs.LCFGraph(126, L, 7)
4847
g.name("Tutte 12-Cage")
4848
return g
4849
4850
def TutteCoxeterGraph(self, embedding=2):
4851
r"""
4852
Returns the Tutte-Coxeter graph.
4853
4854
See the :wikipedia:`Wikipedia page on the Tutte-Coxeter Graph
4855
<Tutte-Coxeter_graph>`.
4856
4857
INPUT:
4858
4859
- ``embedding`` -- two embeddings are available, and can be
4860
selected by setting ``embedding`` to 1 or 2.
4861
4862
EXAMPLES::
4863
4864
sage: g = graphs.TutteCoxeterGraph()
4865
sage: g.order()
4866
30
4867
sage: g.size()
4868
45
4869
sage: g.girth()
4870
8
4871
sage: g.diameter()
4872
4
4873
sage: g.show()
4874
sage: graphs.TutteCoxeterGraph(embedding=1).show()
4875
4876
TESTS::
4877
4878
sage: graphs.TutteCoxeterGraph(embedding=3)
4879
Traceback (most recent call last):
4880
...
4881
ValueError: The value of embedding must be 1 or 2.
4882
"""
4883
4884
g = graphs.LCFGraph(30, [-13, -9, 7, -7, 9, 13], 5)
4885
g.name("Tutte-Coxeter graph")
4886
4887
if embedding == 1:
4888
d = {
4889
0: [1, 3, 5, 7, 29],
4890
1: [2, 4, 6, 28, 0],
4891
2: [8, 18, 26, 22, 12],
4892
3: [9, 13, 23, 27, 17],
4893
4: [11, 15, 21, 25, 19],
4894
5: [10, 14, 24, 20, 16]
4895
}
4896
4897
_circle_embedding(g, d[0], center=(-1, 1), radius=.25)
4898
_circle_embedding(g, d[1], center=(1, 1), radius=.25)
4899
_circle_embedding(g, d[2], center=(-.8, 0), radius=.25, shift=2.5)
4900
_circle_embedding(g, d[3], center=(1.2, 0), radius=.25)
4901
_circle_embedding(g, d[4], center=(-1, -1), radius=.25, shift=2)
4902
_circle_embedding(g, d[5], center=(1, -1), radius=.25)
4903
4904
return g
4905
4906
elif embedding == 2:
4907
return g
4908
4909
else:
4910
raise ValueError("The value of embedding must be 1 or 2.")
4911
4912
def WagnerGraph(self):
4913
"""
4914
Returns the Wagner Graph.
4915
4916
See the :wikipedia:`Wikipedia page on the Wagner Graph
4917
<Wagner_graph>`.
4918
4919
EXAMPLES::
4920
4921
sage: g = graphs.WagnerGraph()
4922
sage: g.order()
4923
8
4924
sage: g.size()
4925
12
4926
sage: g.girth()
4927
4
4928
sage: g.diameter()
4929
2
4930
sage: g.show()
4931
"""
4932
g = graphs.LCFGraph(8, [4], 8)
4933
g.name("Wagner Graph")
4934
return g
4935
4936
###########################################################################
4937
# Families of Graphs
4938
###########################################################################
4939
4940
def BalancedTree(self, r, h):
4941
r"""
4942
Returns the perfectly balanced tree of height `h \geq 1`,
4943
whose root has degree `r \geq 2`.
4944
4945
The number of vertices of this graph is
4946
`1 + r + r^2 + \cdots + r^h`, that is,
4947
`\frac{r^{h+1} - 1}{r - 1}`. The number of edges is one
4948
less than the number of vertices.
4949
4950
INPUT:
4951
4952
- ``r`` -- positive integer `\geq 2`. The degree of the root node.
4953
4954
- ``h`` -- positive integer `\geq 1`. The height of the balanced tree.
4955
4956
OUTPUT:
4957
4958
The perfectly balanced tree of height `h \geq 1` and whose root has
4959
degree `r \geq 2`. A ``NetworkXError`` is returned if `r < 2` or
4960
`h < 1`.
4961
4962
ALGORITHM:
4963
4964
Uses `NetworkX <http://networkx.lanl.gov>`_.
4965
4966
EXAMPLES:
4967
4968
A balanced tree whose root node has degree `r = 2`, and of height
4969
`h = 1`, has order 3 and size 2::
4970
4971
sage: G = graphs.BalancedTree(2, 1); G
4972
Balanced tree: Graph on 3 vertices
4973
sage: G.order(); G.size()
4974
3
4975
2
4976
sage: r = 2; h = 1
4977
sage: v = 1 + r
4978
sage: v; v - 1
4979
3
4980
2
4981
4982
Plot a balanced tree of height 5, whose root node has degree `r = 3`::
4983
4984
sage: G = graphs.BalancedTree(3, 5)
4985
sage: G.show() # long time
4986
4987
A tree is bipartite. If its vertex set is finite, then it is planar. ::
4988
4989
sage: r = randint(2, 5); h = randint(1, 7)
4990
sage: T = graphs.BalancedTree(r, h)
4991
sage: T.is_bipartite()
4992
True
4993
sage: T.is_planar()
4994
True
4995
sage: v = (r^(h + 1) - 1) / (r - 1)
4996
sage: T.order() == v
4997
True
4998
sage: T.size() == v - 1
4999
True
5000
5001
TESTS:
5002
5003
We only consider balanced trees whose root node has degree `r \geq 2`::
5004
5005
sage: graphs.BalancedTree(1, randint(1, 10^6))
5006
Traceback (most recent call last):
5007
...
5008
NetworkXError: Invalid graph description, r should be >=2
5009
sage: graphs.BalancedTree(randint(-10^6, 1), randint(1, 10^6))
5010
Traceback (most recent call last):
5011
...
5012
NetworkXError: Invalid graph description, r should be >=2
5013
5014
The tree must have height `h \geq 1`::
5015
5016
sage: graphs.BalancedTree(randint(2, 10^6), 0)
5017
Traceback (most recent call last):
5018
...
5019
NetworkXError: Invalid graph description, h should be >=1
5020
sage: graphs.BalancedTree(randint(2, 10^6), randint(-10^6, 0))
5021
Traceback (most recent call last):
5022
...
5023
NetworkXError: Invalid graph description, h should be >=1
5024
sage: graphs.BalancedTree(randint(-10^6, 1), randint(-10^6, 0))
5025
Traceback (most recent call last):
5026
...
5027
NetworkXError: Invalid graph description, r should be >=2
5028
"""
5029
import networkx
5030
return graph.Graph(networkx.balanced_tree(r, h), name="Balanced tree")
5031
5032
def BubbleSortGraph(self, n):
5033
r"""
5034
Returns the bubble sort graph `B(n)`.
5035
5036
The vertices of the bubble sort graph are the set of permutations on
5037
`n` symbols. Two vertices are adjacent if one can be obtained from the
5038
other by swapping the labels in the `i`-th and `(i+1)`-th positions for
5039
`1 \leq i \leq n-1`. In total, `B(n)` has order `n!`. Thus, the order
5040
of `B(n)` increases according to `f(n) = n!`.
5041
5042
INPUT:
5043
5044
- ``n`` -- positive integer. The number of symbols to permute.
5045
5046
OUTPUT:
5047
5048
The bubble sort graph `B(n)` on `n` symbols. If `n < 1`, a
5049
``ValueError`` is returned.
5050
5051
EXAMPLES::
5052
5053
sage: g = graphs.BubbleSortGraph(4); g
5054
Bubble sort: Graph on 24 vertices
5055
sage: g.plot() # long time
5056
5057
The bubble sort graph on `n = 1` symbol is the trivial graph `K_1`::
5058
5059
sage: graphs.BubbleSortGraph(1)
5060
Bubble sort: Graph on 1 vertex
5061
5062
If `n \geq 1`, then the order of `B(n)` is `n!`::
5063
5064
sage: n = randint(1, 8)
5065
sage: g = graphs.BubbleSortGraph(n)
5066
sage: g.order() == factorial(n)
5067
True
5068
5069
TESTS:
5070
5071
Input ``n`` must be positive::
5072
5073
sage: graphs.BubbleSortGraph(0)
5074
Traceback (most recent call last):
5075
...
5076
ValueError: Invalid number of symbols to permute, n should be >= 1
5077
sage: graphs.BubbleSortGraph(randint(-10^6, 0))
5078
Traceback (most recent call last):
5079
...
5080
ValueError: Invalid number of symbols to permute, n should be >= 1
5081
5082
AUTHORS:
5083
5084
- Michael Yurko (2009-09-01)
5085
"""
5086
# sanity checks
5087
if n < 1:
5088
raise ValueError(
5089
"Invalid number of symbols to permute, n should be >= 1")
5090
if n == 1:
5091
return graph.Graph(self.CompleteGraph(n), name="Bubble sort")
5092
from sage.combinat.permutation import Permutations
5093
#create set from which to permute
5094
label_set = [str(i) for i in xrange(1, n + 1)]
5095
d = {}
5096
#iterate through all vertices
5097
for v in Permutations(label_set):
5098
tmp_dict = {}
5099
#add all adjacencies
5100
for i in xrange(n - 1):
5101
#swap entries
5102
v[i], v[i + 1] = v[i + 1], v[i]
5103
#add new vertex
5104
new_vert = ''.join(v)
5105
tmp_dict[new_vert] = None
5106
#swap back
5107
v[i], v[i + 1] = v[i + 1], v[i]
5108
#add adjacency dict
5109
d[''.join(v)] = tmp_dict
5110
return graph.Graph(d, name="Bubble sort")
5111
5112
def CirculantGraph(self, n, adjacency):
5113
r"""
5114
Returns a circulant graph with n nodes.
5115
5116
A circulant graph has the property that the vertex i is connected
5117
with the vertices i+j and i-j for each j in adj.
5118
5119
INPUT:
5120
5121
5122
- ``n`` - number of vertices in the graph
5123
5124
- ``adjacency`` - the list of j values
5125
5126
5127
PLOTTING: Upon construction, the position dictionary is filled to
5128
override the spring-layout algorithm. By convention, each circulant
5129
graph will be displayed with the first (0) node at the top, with
5130
the rest following in a counterclockwise manner.
5131
5132
Filling the position dictionary in advance adds O(n) to the
5133
constructor.
5134
5135
EXAMPLES: Compare plotting using the predefined layout and
5136
networkx::
5137
5138
sage: import networkx
5139
sage: n = networkx.cycle_graph(23)
5140
sage: spring23 = Graph(n)
5141
sage: posdict23 = graphs.CirculantGraph(23,2)
5142
sage: spring23.show() # long time
5143
sage: posdict23.show() # long time
5144
5145
We next view many cycle graphs as a Sage graphics array. First we
5146
use the ``CirculantGraph`` constructor, which fills in
5147
the position dictionary::
5148
5149
sage: g = []
5150
sage: j = []
5151
sage: for i in range(9):
5152
... k = graphs.CirculantGraph(i+3,i)
5153
... g.append(k)
5154
...
5155
sage: for i in range(3):
5156
... n = []
5157
... for m in range(3):
5158
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
5159
... j.append(n)
5160
...
5161
sage: G = sage.plot.graphics.GraphicsArray(j)
5162
sage: G.show() # long time
5163
5164
Compare to plotting with the spring-layout algorithm::
5165
5166
sage: g = []
5167
sage: j = []
5168
sage: for i in range(9):
5169
... spr = networkx.cycle_graph(i+3)
5170
... k = Graph(spr)
5171
... g.append(k)
5172
...
5173
sage: for i in range(3):
5174
... n = []
5175
... for m in range(3):
5176
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
5177
... j.append(n)
5178
...
5179
sage: G = sage.plot.graphics.GraphicsArray(j)
5180
sage: G.show() # long time
5181
5182
Passing a 1 into adjacency should give the cycle.
5183
5184
::
5185
5186
sage: graphs.CirculantGraph(6,1)==graphs.CycleGraph(6)
5187
True
5188
sage: graphs.CirculantGraph(7,[1,3]).edges(labels=false)
5189
[(0, 1),
5190
(0, 3),
5191
(0, 4),
5192
(0, 6),
5193
(1, 2),
5194
(1, 4),
5195
(1, 5),
5196
(2, 3),
5197
(2, 5),
5198
(2, 6),
5199
(3, 4),
5200
(3, 6),
5201
(4, 5),
5202
(5, 6)]
5203
"""
5204
if not isinstance(adjacency,list):
5205
adjacency=[adjacency]
5206
pos_dict = {}
5207
for i in range(n):
5208
x = float(cos((pi/2) + ((2*pi)/n)*i))
5209
y = float(sin((pi/2) + ((2*pi)/n)*i))
5210
pos_dict[i] = (x,y)
5211
G=graph.Graph(n, name="Circulant graph ("+str(adjacency)+")")
5212
G._pos=pos_dict
5213
for v in G:
5214
G.add_edges([(v,(v+j)%n) for j in adjacency])
5215
G.add_edges([(v,(v-j)%n) for j in adjacency])
5216
return G
5217
5218
def CompleteGraph(self, n):
5219
"""
5220
Returns a complete graph on n nodes.
5221
5222
A Complete Graph is a graph in which all nodes are connected to all
5223
other nodes.
5224
5225
This constructor is dependent on vertices numbered 0 through n-1 in
5226
NetworkX complete_graph()
5227
5228
PLOTTING: Upon construction, the position dictionary is filled to
5229
override the spring-layout algorithm. By convention, each complete
5230
graph will be displayed with the first (0) node at the top, with
5231
the rest following in a counterclockwise manner.
5232
5233
In the complete graph, there is a big difference visually in using
5234
the spring-layout algorithm vs. the position dictionary used in
5235
this constructor. The position dictionary flattens the graph,
5236
making it clear which nodes an edge is connected to. But the
5237
complete graph offers a good example of how the spring-layout
5238
works. The edges push outward (everything is connected), causing
5239
the graph to appear as a 3-dimensional pointy ball. (See examples
5240
below).
5241
5242
EXAMPLES: We view many Complete graphs with a Sage Graphics Array,
5243
first with this constructor (i.e., the position dictionary
5244
filled)::
5245
5246
sage: g = []
5247
sage: j = []
5248
sage: for i in range(9):
5249
... k = graphs.CompleteGraph(i+3)
5250
... g.append(k)
5251
...
5252
sage: for i in range(3):
5253
... n = []
5254
... for m in range(3):
5255
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
5256
... j.append(n)
5257
...
5258
sage: G = sage.plot.graphics.GraphicsArray(j)
5259
sage: G.show() # long time
5260
5261
We compare to plotting with the spring-layout algorithm::
5262
5263
sage: import networkx
5264
sage: g = []
5265
sage: j = []
5266
sage: for i in range(9):
5267
... spr = networkx.complete_graph(i+3)
5268
... k = Graph(spr)
5269
... g.append(k)
5270
...
5271
sage: for i in range(3):
5272
... n = []
5273
... for m in range(3):
5274
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
5275
... j.append(n)
5276
...
5277
sage: G = sage.plot.graphics.GraphicsArray(j)
5278
sage: G.show() # long time
5279
5280
Compare the constructors (results will vary)
5281
5282
::
5283
5284
sage: import networkx
5285
sage: t = cputime()
5286
sage: n = networkx.complete_graph(389); spring389 = Graph(n)
5287
sage: cputime(t) # random
5288
0.59203700000000126
5289
sage: t = cputime()
5290
sage: posdict389 = graphs.CompleteGraph(389)
5291
sage: cputime(t) # random
5292
0.6680419999999998
5293
5294
We compare plotting::
5295
5296
sage: import networkx
5297
sage: n = networkx.complete_graph(23)
5298
sage: spring23 = Graph(n)
5299
sage: posdict23 = graphs.CompleteGraph(23)
5300
sage: spring23.show() # long time
5301
sage: posdict23.show() # long time
5302
"""
5303
pos_dict = {}
5304
for i in range(n):
5305
x = float(cos((pi/2) + ((2*pi)/n)*i))
5306
y = float(sin((pi/2) + ((2*pi)/n)*i))
5307
pos_dict[i] = (x,y)
5308
import networkx
5309
G = networkx.complete_graph(n)
5310
return graph.Graph(G, pos=pos_dict, name="Complete graph")
5311
5312
def CompleteBipartiteGraph(self, n1, n2):
5313
"""
5314
Returns a Complete Bipartite Graph sized n1+n2, with each of the
5315
nodes [0,(n1-1)] connected to each of the nodes [n1,(n2-1)] and
5316
vice versa.
5317
5318
A Complete Bipartite Graph is a graph with its vertices partitioned
5319
into two groups, V1 and V2. Each v in V1 is connected to every v in
5320
V2, and vice versa.
5321
5322
PLOTTING: Upon construction, the position dictionary is filled to
5323
override the spring-layout algorithm. By convention, each complete
5324
bipartite graph will be displayed with the first n1 nodes on the
5325
top row (at y=1) from left to right. The remaining n2 nodes appear
5326
at y=0, also from left to right. The shorter row (partition with
5327
fewer nodes) is stretched to the same length as the longer row,
5328
unless the shorter row has 1 node; in which case it is centered.
5329
The x values in the plot are in domain [0,maxn1,n2].
5330
5331
In the Complete Bipartite graph, there is a visual difference in
5332
using the spring-layout algorithm vs. the position dictionary used
5333
in this constructor. The position dictionary flattens the graph and
5334
separates the partitioned nodes, making it clear which nodes an
5335
edge is connected to. The Complete Bipartite graph plotted with the
5336
spring-layout algorithm tends to center the nodes in n1 (see
5337
spring_med in examples below), thus overlapping its nodes and
5338
edges, making it typically hard to decipher.
5339
5340
Filling the position dictionary in advance adds O(n) to the
5341
constructor. Feel free to race the constructors below in the
5342
examples section. The much larger difference is the time added by
5343
the spring-layout algorithm when plotting. (Also shown in the
5344
example below). The spring model is typically described as
5345
`O(n^3)`, as appears to be the case in the NetworkX source
5346
code.
5347
5348
EXAMPLES: Two ways of constructing the complete bipartite graph,
5349
using different layout algorithms::
5350
5351
sage: import networkx
5352
sage: n = networkx.complete_bipartite_graph(389,157); spring_big = Graph(n) # long time
5353
sage: posdict_big = graphs.CompleteBipartiteGraph(389,157) # long time
5354
5355
Compare the plotting::
5356
5357
sage: n = networkx.complete_bipartite_graph(11,17)
5358
sage: spring_med = Graph(n)
5359
sage: posdict_med = graphs.CompleteBipartiteGraph(11,17)
5360
5361
Notice here how the spring-layout tends to center the nodes of n1
5362
5363
::
5364
5365
sage: spring_med.show() # long time
5366
sage: posdict_med.show() # long time
5367
5368
View many complete bipartite graphs with a Sage Graphics Array,
5369
with this constructor (i.e., the position dictionary filled)::
5370
5371
sage: g = []
5372
sage: j = []
5373
sage: for i in range(9):
5374
... k = graphs.CompleteBipartiteGraph(i+1,4)
5375
... g.append(k)
5376
...
5377
sage: for i in range(3):
5378
... n = []
5379
... for m in range(3):
5380
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
5381
... j.append(n)
5382
...
5383
sage: G = sage.plot.graphics.GraphicsArray(j)
5384
sage: G.show() # long time
5385
5386
We compare to plotting with the spring-layout algorithm::
5387
5388
sage: g = []
5389
sage: j = []
5390
sage: for i in range(9):
5391
... spr = networkx.complete_bipartite_graph(i+1,4)
5392
... k = Graph(spr)
5393
... g.append(k)
5394
...
5395
sage: for i in range(3):
5396
... n = []
5397
... for m in range(3):
5398
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
5399
... j.append(n)
5400
...
5401
sage: G = sage.plot.graphics.GraphicsArray(j)
5402
sage: G.show() # long time
5403
5404
Trac ticket #12155::
5405
5406
sage: graphs.CompleteBipartiteGraph(5,6).complement()
5407
complement(Complete bipartite graph): Graph on 11 vertices
5408
"""
5409
pos_dict = {}
5410
c1 = 1 # scaling factor for top row
5411
c2 = 1 # scaling factor for bottom row
5412
c3 = 0 # pad to center if top row has 1 node
5413
c4 = 0 # pad to center if bottom row has 1 node
5414
if n1 > n2:
5415
if n2 == 1:
5416
c4 = (n1-1)/2
5417
else:
5418
c2 = ((n1-1)/(n2-1))
5419
elif n2 > n1:
5420
if n1 == 1:
5421
c3 = (n2-1)/2
5422
else:
5423
c1 = ((n2-1)/(n1-1))
5424
for i in range(n1):
5425
x = c1*i + c3
5426
y = 1
5427
pos_dict[i] = (x,y)
5428
for i in range(n1+n2)[n1:]:
5429
x = c2*(i-n1) + c4
5430
y = 0
5431
pos_dict[i] = (x,y)
5432
import networkx
5433
from sage.graphs.graph import Graph
5434
G = networkx.complete_bipartite_graph(n1,n2)
5435
return Graph(G, pos=pos_dict, name="Complete bipartite graph")
5436
5437
def CompleteMultipartiteGraph(self, l):
5438
r"""
5439
Returns a complete multipartite graph.
5440
5441
INPUT:
5442
5443
- ``l`` -- a list of integers : the respective sizes
5444
of the components.
5445
5446
EXAMPLE:
5447
5448
A complete tripartite graph with sets of sizes
5449
`5, 6, 8`::
5450
5451
sage: g = graphs.CompleteMultipartiteGraph([5, 6, 8]); g
5452
Multipartite Graph with set sizes [5, 6, 8]: Graph on 19 vertices
5453
5454
It clearly has a chromatic number of 3::
5455
5456
sage: g.chromatic_number()
5457
3
5458
"""
5459
5460
from sage.graphs.graph import Graph
5461
g = Graph()
5462
for i in l:
5463
g = g + self.CompleteGraph(i)
5464
5465
g = g.complement()
5466
g.name("Multipartite Graph with set sizes "+str(l))
5467
5468
return g
5469
5470
def CubeGraph(self, n):
5471
r"""
5472
Returns the hypercube in `n` dimensions.
5473
5474
The hypercube in `n` dimension is build upon the binary
5475
strings on `n` bits, two of them being adjacent if
5476
they differ in exactly one bit. Hence, the distance
5477
between two vertices in the hypercube is the Hamming
5478
distance.
5479
5480
EXAMPLES:
5481
5482
The distance between `0100110` and `1011010` is
5483
`5`, as expected ::
5484
5485
sage: g = graphs.CubeGraph(7)
5486
sage: g.distance('0100110','1011010')
5487
5
5488
5489
Plot several `n`-cubes in a Sage Graphics Array ::
5490
5491
sage: g = []
5492
sage: j = []
5493
sage: for i in range(6):
5494
... k = graphs.CubeGraph(i+1)
5495
... g.append(k)
5496
...
5497
sage: for i in range(2):
5498
... n = []
5499
... for m in range(3):
5500
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
5501
... j.append(n)
5502
...
5503
sage: G = sage.plot.graphics.GraphicsArray(j)
5504
sage: G.show(figsize=[6,4]) # long time
5505
5506
Use the plot options to display larger `n`-cubes
5507
5508
::
5509
5510
sage: g = graphs.CubeGraph(9)
5511
sage: g.show(figsize=[12,12],vertex_labels=False, vertex_size=20) # long time
5512
5513
AUTHORS:
5514
5515
- Robert Miller
5516
"""
5517
theta = float(pi/n)
5518
5519
d = {'':[]}
5520
dn={}
5521
p = {'':(float(0),float(0))}
5522
pn={}
5523
5524
# construct recursively the adjacency dict and the positions
5525
for i in xrange(n):
5526
ci = float(cos(i*theta))
5527
si = float(sin(i*theta))
5528
for v,e in d.iteritems():
5529
v0 = v+'0'
5530
v1 = v+'1'
5531
l0 = [v1]
5532
l1 = [v0]
5533
for m in e:
5534
l0.append(m+'0')
5535
l1.append(m+'1')
5536
dn[v0] = l0
5537
dn[v1] = l1
5538
x,y = p[v]
5539
pn[v0] = (x, y)
5540
pn[v1] = (x+ci, y+si)
5541
d,dn = dn,{}
5542
p,pn = pn,{}
5543
5544
# construct the graph
5545
r = graph.Graph(name="%d-Cube"%n)
5546
r.add_vertices(d.keys())
5547
for u,L in d.iteritems():
5548
for v in L:
5549
r.add_edge(u,v)
5550
r.set_pos(p)
5551
5552
return r
5553
5554
def FriendshipGraph(self, n):
5555
r"""
5556
Returns the friendship graph `F_n`.
5557
5558
The friendship graph is also known as the Dutch windmill graph. Let
5559
`C_3` be the cycle graph on 3 vertices. Then `F_n` is constructed by
5560
joining `n \geq 1` copies of `C_3` at a common vertex. If `n = 1`,
5561
then `F_1` is isomorphic to `C_3` (the triangle graph). If `n = 2`,
5562
then `F_2` is the butterfly graph, otherwise known as the bowtie
5563
graph. For more information, see this
5564
`Wikipedia article on the friendship graph <http://en.wikipedia.org/wiki/Friendship_graph>`_.
5565
5566
INPUT:
5567
5568
- ``n`` -- positive integer; the number of copies of `C_3` to use in
5569
constructing `F_n`.
5570
5571
OUTPUT:
5572
5573
- The friendship graph `F_n` obtained from `n` copies of the cycle
5574
graph `C_3`.
5575
5576
.. seealso::
5577
5578
- :meth:`GraphGenerators.ButterflyGraph`
5579
5580
EXAMPLES:
5581
5582
The first few friendship graphs. ::
5583
5584
sage: A = []; B = []
5585
sage: for i in range(9):
5586
... g = graphs.FriendshipGraph(i + 1)
5587
... A.append(g)
5588
sage: for i in range(3):
5589
... n = []
5590
... for j in range(3):
5591
... n.append(A[3*i + j].plot(vertex_size=20, vertex_labels=False))
5592
... B.append(n)
5593
sage: G = sage.plot.graphics.GraphicsArray(B)
5594
sage: G.show() # long time
5595
5596
For `n = 1`, the friendship graph `F_1` is isomorphic to the cycle
5597
graph `C_3`, whose visual representation is a triangle. ::
5598
5599
sage: G = graphs.FriendshipGraph(1); G
5600
Friendship graph: Graph on 3 vertices
5601
sage: G.show() # long time
5602
sage: G.is_isomorphic(graphs.CycleGraph(3))
5603
True
5604
5605
For `n = 2`, the friendship graph `F_2` is isomorphic to the
5606
butterfly graph, otherwise known as the bowtie graph. ::
5607
5608
sage: G = graphs.FriendshipGraph(2); G
5609
Friendship graph: Graph on 5 vertices
5610
sage: G.is_isomorphic(graphs.ButterflyGraph())
5611
True
5612
5613
If `n \geq 1`, then the friendship graph `F_n` has `2n + 1` vertices
5614
and `3n` edges. It has radius 1, diameter 2, girth 3, and
5615
chromatic number 3. Furthermore, `F_n` is planar and Eulerian. ::
5616
5617
sage: n = randint(1, 10^3)
5618
sage: G = graphs.FriendshipGraph(n)
5619
sage: G.order() == 2*n + 1
5620
True
5621
sage: G.size() == 3*n
5622
True
5623
sage: G.radius()
5624
1
5625
sage: G.diameter()
5626
2
5627
sage: G.girth()
5628
3
5629
sage: G.chromatic_number()
5630
3
5631
sage: G.is_planar()
5632
True
5633
sage: G.is_eulerian()
5634
True
5635
5636
TESTS:
5637
5638
The input ``n`` must be a positive integer. ::
5639
5640
sage: graphs.FriendshipGraph(randint(-10^5, 0))
5641
Traceback (most recent call last):
5642
...
5643
ValueError: n must be a positive integer
5644
"""
5645
# sanity checks
5646
if n < 1:
5647
raise ValueError("n must be a positive integer")
5648
# construct the friendship graph
5649
if n == 1:
5650
G = self.CycleGraph(3)
5651
G.name("Friendship graph")
5652
return G
5653
# build the edge and position dictionaries
5654
from sage.functions.trig import cos, sin
5655
from sage.rings.real_mpfr import RR
5656
from sage.symbolic.constants import pi
5657
N = 2*n + 1 # order of F_n
5658
d = (2*pi) / (N - 1) # angle between external nodes
5659
edge_dict = {}
5660
pos_dict = {}
5661
for i in range(N - 2):
5662
if i & 1: # odd numbered node
5663
edge_dict.setdefault(i, [i + 1, N - 1])
5664
else: # even numbered node
5665
edge_dict.setdefault(i, [N - 1])
5666
pos_dict.setdefault(i, [RR(cos(i*d)), RR(sin(i*d))])
5667
edge_dict.setdefault(N - 2, [0, N - 1])
5668
pos_dict.setdefault(N - 2, [RR(cos(d * (N-2))), RR(sin(d * (N-2)))])
5669
pos_dict.setdefault(N - 1, [0, 0])
5670
return graph.Graph(edge_dict, pos=pos_dict, name="Friendship graph")
5671
5672
def FuzzyBallGraph(self, partition, q):
5673
r"""
5674
Construct a Fuzzy Ball graph with the integer partition
5675
``partition`` and ``q`` extra vertices.
5676
5677
Let `q` be an integer and let `m_1,m_2,...,m_k` be a set of positive
5678
integers. Let `n=q+m_1+...+m_k`. The Fuzzy Ball graph with partition
5679
`m_1,m_2,...,m_k` and `q` extra vertices is the graph constructed from the
5680
graph `G=K_n` by attaching, for each `i=1,2,...,k`, a new vertex `a_i` to
5681
`m_i` distinct vertices of `G`.
5682
5683
For given positive integers `k` and `m` and nonnegative
5684
integer `q`, the set of graphs ``FuzzyBallGraph(p, q)`` for
5685
all partitions `p` of `m` with `k` parts are cospectral with
5686
respect to the normalized Laplacian.
5687
5688
EXAMPLES::
5689
5690
sage: graphs.FuzzyBallGraph([3,1],2).adjacency_matrix()
5691
[0 1 1 1 1 1 1 0]
5692
[1 0 1 1 1 1 1 0]
5693
[1 1 0 1 1 1 1 0]
5694
[1 1 1 0 1 1 0 1]
5695
[1 1 1 1 0 1 0 0]
5696
[1 1 1 1 1 0 0 0]
5697
[1 1 1 0 0 0 0 0]
5698
[0 0 0 1 0 0 0 0]
5699
5700
5701
Pick positive integers `m` and `k` and a nonnegative integer `q`.
5702
All the FuzzyBallGraphs constructed from partitions of `m` with
5703
`k` parts should be cospectral with respect to the normalized
5704
Laplacian::
5705
5706
sage: m=4; q=2; k=2
5707
sage: g_list=[graphs.FuzzyBallGraph(p,q) for p in Partitions(m, length=k)]
5708
sage: set([g.laplacian_matrix(normalized=True).charpoly() for g in g_list]) # long time (7s on sage.math, 2011)
5709
set([x^8 - 8*x^7 + 4079/150*x^6 - 68689/1350*x^5 + 610783/10800*x^4 - 120877/3240*x^3 + 1351/100*x^2 - 931/450*x])
5710
"""
5711
if len(partition)<1:
5712
raise ValueError, "partition must be a nonempty list of positive integers"
5713
n=q+sum(partition)
5714
g=graphs.CompleteGraph(n)
5715
curr_vertex=0
5716
for e,p in enumerate(partition):
5717
g.add_edges([(curr_vertex+i, 'a{0}'.format(e+1)) for i in range(p)])
5718
curr_vertex+=p
5719
return g
5720
5721
def FibonacciTree(self, n):
5722
r"""
5723
Returns the graph of the Fibonacci Tree `F_{i}` of order `n`.
5724
`F_{i}` is recursively defined as the a tree with a root vertex
5725
and two attached child trees `F_{i-1}` and `F_{i-2}`, where
5726
`F_{1}` is just one vertex and `F_{0}` is empty.
5727
5728
INPUT:
5729
5730
- ``n`` - the recursion depth of the Fibonacci Tree
5731
5732
EXAMPLES::
5733
5734
sage: g = graphs.FibonacciTree(3)
5735
sage: g.is_tree()
5736
True
5737
5738
::
5739
5740
sage: l1 = [ len(graphs.FibonacciTree(_)) + 1 for _ in range(6) ]
5741
sage: l2 = list(fibonacci_sequence(2,8))
5742
sage: l1 == l2
5743
True
5744
5745
AUTHORS:
5746
5747
- Harald Schilly and Yann Laigle-Chapuy (2010-03-25)
5748
"""
5749
T = graph.Graph(name="Fibonacci-Tree-%d"%n)
5750
if n == 1: T.add_vertex(0)
5751
if n < 2: return T
5752
5753
from sage.combinat.combinat import fibonacci_sequence
5754
F = list(fibonacci_sequence(n + 2))
5755
s = 1.618 ** (n / 1.618 - 1.618)
5756
pos = {}
5757
5758
def fib(level, node, y):
5759
pos[node] = (node, y)
5760
if level < 2: return
5761
level -= 1
5762
y -= s
5763
diff = F[level]
5764
T.add_edge(node, node - diff)
5765
if level == 1: # only one child
5766
pos[node - diff] = (node, y)
5767
return
5768
T.add_edge(node, node + diff)
5769
fib(level, node - diff, y)
5770
fib(level - 1, node + diff, y)
5771
5772
T.add_vertices(xrange(sum(F[:-1])))
5773
fib(n, F[n + 1] - 1, 0)
5774
T.set_pos(pos)
5775
5776
return T
5777
5778
def GeneralizedPetersenGraph(self, n,k):
5779
r"""
5780
Returns a generalized Petersen graph with `2n` nodes. The variables
5781
`n`, `k` are integers such that `n>2` and `0<k\leq\lfloor(n-1)`/`2\rfloor`
5782
5783
For `k=1` the result is a graph isomorphic to the circular ladder graph
5784
with the same `n`. The regular Petersen Graph has `n=5` and `k=2`.
5785
Other named graphs that can be described using this notation include
5786
the Desargues graph and the Moebius-Kantor graph.
5787
5788
INPUT:
5789
5790
- ``n`` - the number of nodes is `2*n`.
5791
5792
- ``k`` - integer `0<k\leq\lfloor(n-1)`/`2\rfloor`. Decides
5793
how inner vertices are connected.
5794
5795
PLOTTING: Upon construction, the position dictionary is filled to
5796
override the spring-layout algorithm. By convention, the generalized
5797
Petersen graphs are displayed as an inner and outer cycle pair, with
5798
the first n nodes drawn on the outer circle. The first (0) node is
5799
drawn at the top of the outer-circle, moving counterclockwise after that.
5800
The inner circle is drawn with the (n)th node at the top, then
5801
counterclockwise as well.
5802
5803
EXAMPLES: For `k=1` the resulting graph will be isomorphic to a circular
5804
ladder graph. ::
5805
5806
sage: g = graphs.GeneralizedPetersenGraph(13,1)
5807
sage: g2 = graphs.CircularLadderGraph(13)
5808
sage: g.is_isomorphic(g2)
5809
True
5810
5811
The Desargues graph::
5812
5813
sage: g = graphs.GeneralizedPetersenGraph(10,3)
5814
sage: g.girth()
5815
6
5816
sage: g.is_bipartite()
5817
True
5818
5819
AUTHORS:
5820
5821
- Anders Jonsson (2009-10-15)
5822
"""
5823
if (n < 3):
5824
raise ValueError("n must be larger than 2")
5825
if (k < 1 or k>((n-1)/2)):
5826
raise ValueError("k must be in 1<= k <=floor((n-1)/2)")
5827
pos_dict = {}
5828
G=graphs.EmptyGraph()
5829
for i in range(n):
5830
x = float(cos((pi/2) + ((2*pi)/n)*i))
5831
y = float(sin((pi/2) + ((2*pi)/n)*i))
5832
pos_dict[i] = (x,y)
5833
for i in range(n, 2*n):
5834
x = float(0.5*cos((pi/2) + ((2*pi)/n)*i))
5835
y = float(0.5*sin((pi/2) + ((2*pi)/n)*i))
5836
pos_dict[i] = (x,y)
5837
for i in range(n):
5838
G.add_edge(i, (i+1) % n)
5839
G.add_edge(i, i+n)
5840
G.add_edge(i+n, n + (i+k) % n)
5841
return graph.Graph(G, pos=pos_dict, name="Generalized Petersen graph (n="+str(n)+",k="+str(k)+")")
5842
5843
def HyperStarGraph(self,n,k):
5844
r"""
5845
Returns the hyper-star graph HS(n,k).
5846
5847
The vertices of the hyper-star graph are the set of binary strings
5848
of length n which contain k 1s. Two vertices, u and v, are adjacent
5849
only if u can be obtained from v by swapping the first bit with a
5850
different symbol in another position.
5851
5852
INPUT:
5853
5854
- ``n``
5855
5856
- ``k``
5857
5858
EXAMPLES::
5859
5860
sage: g = graphs.HyperStarGraph(6,3)
5861
sage: g.plot() # long time
5862
5863
REFERENCES:
5864
5865
- Lee, Hyeong-Ok, Jong-Seok Kim, Eunseuk Oh, and Hyeong-Seok Lim.
5866
"Hyper-Star Graph: A New Interconnection Network Improving the
5867
Network Cost of the Hypercube." In Proceedings of the First EurAsian
5868
Conference on Information and Communication Technology, 858-865.
5869
Springer-Verlag, 2002.
5870
5871
AUTHORS:
5872
5873
- Michael Yurko (2009-09-01)
5874
"""
5875
from sage.combinat.combination import Combinations
5876
# dictionary associating the positions of the 1s to the corresponding
5877
# string: e.g. if n=6 and k=3, comb_to_str([0,1,4])=='110010'
5878
comb_to_str={}
5879
for c in Combinations(n,k):
5880
L = ['0']*n
5881
for i in c:
5882
L[i]='1'
5883
comb_to_str[tuple(c)] = ''.join(L)
5884
5885
g=graph.Graph(name="HS(%d,%d)"%(n,k))
5886
g.add_vertices(comb_to_str.values())
5887
5888
for c in Combinations(range(1,n),k): # 0 is not in c
5889
L = []
5890
u = comb_to_str[tuple(c)]
5891
# switch 0 with the 1s
5892
for i in xrange(len(c)):
5893
v = tuple([0]+c[:i]+c[i+1:])
5894
g.add_edge( u , comb_to_str[v] )
5895
5896
return g
5897
5898
def LCFGraph(self, n, shift_list, repeats):
5899
"""
5900
Returns the cubic graph specified in LCF notation.
5901
5902
LCF (Lederberg-Coxeter-Fruchte) notation is a concise way of
5903
describing cubic Hamiltonian graphs. The way a graph is constructed
5904
is as follows. Since there is a Hamiltonian cycle, we first create
5905
a cycle on n nodes. The variable shift_list = [s_0, s_1, ...,
5906
s_k-1] describes edges to be created by the following scheme: for
5907
each i, connect vertex i to vertex (i + s_i). Then, repeats
5908
specifies the number of times to repeat this process, where on the
5909
jth repeat we connect vertex (i + j\*len(shift_list)) to vertex (
5910
i + j\*len(shift_list) + s_i).
5911
5912
INPUT:
5913
5914
5915
- ``n`` - the number of nodes.
5916
5917
- ``shift_list`` - a list of integer shifts mod n.
5918
5919
- ``repeats`` - the number of times to repeat the
5920
process.
5921
5922
5923
EXAMPLES::
5924
5925
sage: G = graphs.LCFGraph(4, [2,-2], 2)
5926
sage: G.is_isomorphic(graphs.TetrahedralGraph())
5927
True
5928
5929
::
5930
5931
sage: G = graphs.LCFGraph(20, [10,7,4,-4,-7,10,-4,7,-7,4], 2)
5932
sage: G.is_isomorphic(graphs.DodecahedralGraph())
5933
True
5934
5935
::
5936
5937
sage: G = graphs.LCFGraph(14, [5,-5], 7)
5938
sage: G.is_isomorphic(graphs.HeawoodGraph())
5939
True
5940
5941
The largest cubic nonplanar graph of diameter three::
5942
5943
sage: G = graphs.LCFGraph(20, [-10,-7,-5,4,7,-10,-7,-4,5,7,-10,-7,6,-5,7,-10,-7,5,-6,7], 1)
5944
sage: G.degree()
5945
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
5946
sage: G.diameter()
5947
3
5948
sage: G.show() # long time
5949
5950
PLOTTING: LCF Graphs are plotted as an n-cycle with edges in the
5951
middle, as described above.
5952
5953
REFERENCES:
5954
5955
- [1] Frucht, R. "A Canonical Representation of Trivalent
5956
Hamiltonian Graphs." J. Graph Th. 1, 45-60, 1976.
5957
5958
- [2] Grunbaum, B. Convex Polytope es. New York: Wiley,
5959
pp. 362-364, 1967.
5960
5961
- [3] Lederberg, J. 'DENDRAL-64: A System for Computer
5962
Construction, Enumeration and Notation of Organic Molecules
5963
as Tree Structures and Cyclic Graphs. Part II. Topology of
5964
Cyclic Graphs.' Interim Report to the National Aeronautics
5965
and Space Administration. Grant NsG 81-60. December 15,
5966
1965. http://profiles.nlm.nih.gov/BB/A/B/I/U/_/bbabiu.pdf.
5967
"""
5968
import networkx
5969
pos_dict = {}
5970
for i in range(n):
5971
x = float(cos(pi/2 + ((2*pi)/n)*i))
5972
y = float(sin(pi/2 + ((2*pi)/n)*i))
5973
pos_dict[i] = [x,y]
5974
return graph.Graph(networkx.LCF_graph(n, shift_list, repeats),\
5975
pos=pos_dict, name="LCF Graph")
5976
5977
def NKStarGraph(self,n,k):
5978
r"""
5979
Returns the (n,k)-star graph.
5980
5981
The vertices of the (n,k)-star graph are the set of all arrangements of
5982
n symbols into labels of length k. There are two adjacency rules for
5983
the (n,k)-star graph. First, two vertices are adjacent if one can be
5984
obtained from the other by swapping the first symbol with another
5985
symbol. Second, two vertices are adjacent if one can be obtained from
5986
the other by swapping the first symbol with an external symbol (a
5987
symbol not used in the original label).
5988
5989
INPUT:
5990
5991
- ``n``
5992
5993
- ``k``
5994
5995
EXAMPLES::
5996
5997
sage: g = graphs.NKStarGraph(4,2)
5998
sage: g.plot() # long time
5999
6000
REFERENCES:
6001
6002
- Wei-Kuo, Chiang, and Chen Rong-Jaye. "The (n, k)-star graph: A
6003
generalized star graph." Information Processing Letters 56,
6004
no. 5 (December 8, 1995): 259-264.
6005
6006
AUTHORS:
6007
6008
- Michael Yurko (2009-09-01)
6009
"""
6010
from sage.combinat.permutation import Arrangements
6011
#set from which to permute
6012
set = [str(i) for i in xrange(1,n+1)]
6013
#create dict
6014
d = {}
6015
for v in Arrangements(set,k):
6016
tmp_dict = {}
6017
#add edges of dimension i
6018
for i in xrange(1,k):
6019
#swap 0th and ith element
6020
v[0], v[i] = v[i], v[0]
6021
#convert to str and add to list
6022
vert = "".join(v)
6023
tmp_dict[vert] = None
6024
#swap back
6025
v[0], v[i] = v[i], v[0]
6026
#add other edges
6027
tmp_bit = v[0]
6028
for i in set:
6029
#check if external
6030
if not (i in v):
6031
v[0] = i
6032
#add edge
6033
vert = "".join(v)
6034
tmp_dict[vert] = None
6035
v[0] = tmp_bit
6036
d["".join(v)] = tmp_dict
6037
return graph.Graph(d, name="(%d,%d)-star"%(n,k))
6038
6039
def NStarGraph(self,n):
6040
r"""
6041
Returns the n-star graph.
6042
6043
The vertices of the n-star graph are the set of permutations on n
6044
symbols. There is an edge between two vertices if their labels differ
6045
only in the first and one other position.
6046
6047
INPUT:
6048
6049
- ``n``
6050
6051
EXAMPLES::
6052
6053
sage: g = graphs.NStarGraph(4)
6054
sage: g.plot() # long time
6055
6056
REFERENCES:
6057
6058
- S.B. Akers, D. Horel and B. Krishnamurthy, The star graph: An
6059
attractive alternative to the previous n-cube. In: Proc. Internat.
6060
Conf. on Parallel Processing (1987), pp. 393--400.
6061
6062
AUTHORS:
6063
6064
- Michael Yurko (2009-09-01)
6065
"""
6066
from sage.combinat.permutation import Permutations
6067
#set from which to permute
6068
set = [str(i) for i in xrange(1,n+1)]
6069
#create dictionary of lists
6070
#vertices are adjacent if the first element
6071
#is swapped with the ith element
6072
d = {}
6073
for v in Permutations(set):
6074
tmp_dict = {}
6075
for i in xrange(1,n):
6076
if v[0] != v[i]:
6077
#swap 0th and ith element
6078
v[0], v[i] = v[i], v[0]
6079
#convert to str and add to list
6080
vert = "".join(v)
6081
tmp_dict[vert] = None
6082
#swap back
6083
v[0], v[i] = v[i], v[0]
6084
d["".join(v)] = tmp_dict
6085
return graph.Graph(d, name = "%d-star"%n)
6086
6087
def OddGraph(self,n):
6088
r"""
6089
Returns the Odd Graph with parameter `n`.
6090
6091
The Odd Graph with parameter `n` is defined as the
6092
Kneser Graph with parameters `2n-1,n-1`.
6093
Equivalently, the Odd Graph is the graph whose vertices
6094
are the `n-1`-subsets of `[0,1,\dots,2(n-1)]`, and such
6095
that two vertices are adjacent if their corresponding sets
6096
are disjoint.
6097
6098
For example, the Petersen Graph can be defined
6099
as the Odd Graph with parameter `3`.
6100
6101
EXAMPLE::
6102
6103
sage: OG=graphs.OddGraph(3)
6104
sage: print OG.vertices()
6105
[{4, 5}, {1, 3}, {2, 5}, {2, 3}, {3, 4}, {3, 5}, {1, 4}, {1, 5}, {1, 2}, {2, 4}]
6106
sage: P=graphs.PetersenGraph()
6107
sage: P.is_isomorphic(OG)
6108
True
6109
6110
TESTS::
6111
6112
sage: KG=graphs.OddGraph(1)
6113
Traceback (most recent call last):
6114
...
6115
ValueError: Parameter n should be an integer strictly greater than 1
6116
"""
6117
6118
if not n>1:
6119
raise ValueError, "Parameter n should be an integer strictly greater than 1"
6120
g = self.KneserGraph(2*n-1,n-1)
6121
g.name("Odd Graph with parameter %s" % n)
6122
return g
6123
6124
6125
def PermutationGraph(self, second_permutation, first_permutation = None):
6126
r"""
6127
Builds a permutation graph from one (or two) permutations.
6128
6129
General definition
6130
6131
A Permutation Graph can be encoded by a permutation `\sigma`
6132
of `0, ..., n`. It is then built in the following way :
6133
6134
Take two horizontal lines in the euclidean plane, and mark points `0,
6135
..., n` from left to right on the first of them. On the second one,
6136
still from left to right, mark point in the order in which they appear
6137
in `\sigma`. Now, link by a segment the two points marked with 1, then
6138
link together the points marked with 2, and so on. The permutation
6139
graph defined by the permutation is the intersection graph of those
6140
segments : there exists a point in this graph for each element from
6141
`1` to `n`, two vertices `i, j` being adjacent if the segments `i` and
6142
`j` cross each other.
6143
6144
The set of edges of the resulting graph is equal to the set of
6145
inversions of the inverse of the given permutation.
6146
6147
INPUT:
6148
6149
- ``second_permutation`` -- the permutation from which the graph should
6150
be built. It corresponds to the ordering of the elements on the second
6151
line (see previous definition)
6152
6153
- ``first_permutation`` (optional) -- the ordering of the elements on
6154
the *first* line. This is useful when the elements have no natural
6155
ordering, for instance when they are strings, or tuples, or anything
6156
else.
6157
6158
When ``first_permutation == None`` (default), it is set to be equal to
6159
``sorted(second_permutation)``, which just yields the expected
6160
ordering when the elements of the graph are integers.
6161
6162
.. SEEALSO:
6163
6164
- Recognition of Permutation graphs in the :mod:`comparability module
6165
<sage.graphs.comparability>`.
6166
6167
- Drawings of permutation graphs as intersection graphs of segments is
6168
possible through the
6169
:meth:`~sage.combinat.permutation.Permutation_class.show` method of
6170
:class:`~sage.combinat.permutation.Permutation` objects.
6171
6172
The correct argument to use in this case is ``show(representation =
6173
"braid")``.
6174
6175
- :meth:`~sage.combinat.permutation.Permutation_class.inversions`
6176
6177
EXAMPLE::
6178
6179
sage: p = Permutations(5).random_element()
6180
sage: edges = graphs.PermutationGraph(p).edges(labels =False)
6181
sage: set(edges) == set(map(lambda (x,y) : (x+1,y+1),p.inverse().inversions()))
6182
True
6183
6184
TESTS::
6185
6186
sage: graphs.PermutationGraph([1, 2, 3], [4, 5, 6])
6187
Traceback (most recent call last):
6188
...
6189
ValueError: The two permutations do not contain the same set of elements ...
6190
"""
6191
if first_permutation == None:
6192
first_permutation = sorted(second_permutation)
6193
else:
6194
if set(second_permutation) != set(first_permutation):
6195
raise ValueError("The two permutations do not contain the same "+
6196
"set of elements ! It is going to be pretty "+
6197
"hard to define a permutation graph from that !")
6198
6199
vertex_to_index = {}
6200
for i, v in enumerate(first_permutation):
6201
vertex_to_index[v] = i+1
6202
6203
from sage.combinat.permutation import Permutation
6204
p2 = Permutation(map(lambda x:vertex_to_index[x], second_permutation))
6205
p1 = Permutation(map(lambda x:vertex_to_index[x], first_permutation))
6206
p2 = p2 * p1.inverse()
6207
p2 = p2.inverse()
6208
6209
g = graph.Graph(name="Permutation graph for "+str(second_permutation))
6210
g.add_vertices(second_permutation)
6211
6212
for u,v in p2.inversions():
6213
g.add_edge(first_permutation[u], first_permutation[v])
6214
6215
return g
6216
6217
################################################################################
6218
# Pseudofractal Graphs
6219
################################################################################
6220
6221
def DorogovtsevGoltsevMendesGraph(self, n):
6222
"""
6223
Construct the n-th generation of the Dorogovtsev-Goltsev-Mendes
6224
graph.
6225
6226
EXAMPLE::
6227
6228
sage: G = graphs.DorogovtsevGoltsevMendesGraph(8)
6229
sage: G.size()
6230
6561
6231
6232
REFERENCE:
6233
6234
- [1] Dorogovtsev, S. N., Goltsev, A. V., and Mendes, J.
6235
F. F., Pseudofractal scale-free web, Phys. Rev. E 066122
6236
(2002).
6237
"""
6238
import networkx
6239
return graph.Graph(networkx.dorogovtsev_goltsev_mendes_graph(n),\
6240
name="Dorogovtsev-Goltsev-Mendes Graph, %d-th generation"%n)
6241
6242
################################################################################
6243
# Random Graphs
6244
################################################################################
6245
6246
def RandomGNP(self, n, p, seed=None, fast=True, method='Sage'):
6247
r"""
6248
Returns a random graph on `n` nodes. Each edge is inserted independently
6249
with probability `p`.
6250
6251
INPUTS:
6252
6253
- ``n`` -- number of nodes of the digraph
6254
6255
- ``p`` -- probability of an edge
6256
6257
- ``seed`` -- integer seed for random number generator (default=None).
6258
6259
- ``fast`` -- boolean set to True (default) to use the algorithm with
6260
time complexity in `O(n+m)` proposed in [3]_. It is designed for
6261
generating large sparse graphs. It is faster than other methods for
6262
*LARGE* instances (try it to know whether it is useful for you).
6263
6264
- ``method`` -- By default (```method='Sage'``), this function uses the
6265
method implemented in ```sage.graphs.graph_generators_pyx.pyx``. When
6266
``method='networkx'``, this function calls the NetworkX function
6267
``fast_gnp_random_graph``, unless ``fast=False``, then
6268
``gnp_random_graph``. Try them to know which method is the best for
6269
you. The ``fast`` parameter is not taken into account by the 'Sage'
6270
method so far.
6271
6272
REFERENCES:
6273
6274
.. [1] P. Erdos and A. Renyi. On Random Graphs, Publ. Math. 6, 290 (1959).
6275
6276
.. [2] E. N. Gilbert. Random Graphs, Ann. Math. Stat., 30, 1141 (1959).
6277
6278
.. [3] V. Batagelj and U. Brandes. Efficient generation of large
6279
random networks. Phys. Rev. E, 71, 036113, 2005.
6280
6281
PLOTTING: When plotting, this graph will use the default spring-layout
6282
algorithm, unless a position dictionary is specified.
6283
6284
EXAMPLES: We show the edge list of a random graph on 6 nodes with
6285
probability `p = .4`::
6286
6287
sage: set_random_seed(0)
6288
sage: graphs.RandomGNP(6, .4).edges(labels=False)
6289
[(0, 1), (0, 5), (1, 2), (2, 4), (3, 4), (3, 5), (4, 5)]
6290
6291
We plot a random graph on 12 nodes with probability
6292
`p = .71`::
6293
6294
sage: gnp = graphs.RandomGNP(12,.71)
6295
sage: gnp.show() # long time
6296
6297
We view many random graphs using a graphics array::
6298
6299
sage: g = []
6300
sage: j = []
6301
sage: for i in range(9):
6302
... k = graphs.RandomGNP(i+3,.43)
6303
... g.append(k)
6304
...
6305
sage: for i in range(3):
6306
... n = []
6307
... for m in range(3):
6308
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
6309
... j.append(n)
6310
...
6311
sage: G = sage.plot.graphics.GraphicsArray(j)
6312
sage: G.show() # long time
6313
sage: graphs.RandomGNP(4,1)
6314
Complete graph: Graph on 4 vertices
6315
6316
TESTS::
6317
6318
sage: graphs.RandomGNP(50,.2,method=50)
6319
Traceback (most recent call last):
6320
...
6321
ValueError: 'method' must be equal to 'networkx' or to 'Sage'.
6322
sage: set_random_seed(0)
6323
sage: graphs.RandomGNP(50,.2, method="Sage").size()
6324
243
6325
sage: graphs.RandomGNP(50,.2, method="networkx").size()
6326
258
6327
"""
6328
if n < 0:
6329
raise ValueError("The number of nodes must be positive or null.")
6330
if 0.0 > p or 1.0 < p:
6331
raise ValueError("The probability p must be in [0..1].")
6332
6333
if seed is None:
6334
seed = current_randstate().long_seed()
6335
if p == 1:
6336
return graphs.CompleteGraph(n)
6337
6338
if method == 'networkx':
6339
import networkx
6340
if fast:
6341
G = networkx.fast_gnp_random_graph(n, p, seed=seed)
6342
else:
6343
G = networkx.gnp_random_graph(n, p, seed=seed)
6344
return graph.Graph(G)
6345
elif method in ['Sage', 'sage']:
6346
# We use the Sage generator
6347
from sage.graphs.graph_generators_pyx import RandomGNP as sageGNP
6348
return sageGNP(n, p)
6349
else:
6350
raise ValueError("'method' must be equal to 'networkx' or to 'Sage'.")
6351
6352
def RandomBarabasiAlbert(self, n, m, seed=None):
6353
u"""
6354
Return a random graph created using the Barabasi-Albert preferential
6355
attachment model.
6356
6357
A graph with m vertices and no edges is initialized, and a graph of n
6358
vertices is grown by attaching new vertices each with m edges that are
6359
attached to existing vertices, preferentially with high degree.
6360
6361
INPUT:
6362
6363
- ``n`` - number of vertices in the graph
6364
6365
- ``m`` - number of edges to attach from each new node
6366
6367
- ``seed`` - for random number generator
6368
6369
EXAMPLES:
6370
6371
We show the edge list of a random graph on 6 nodes with m = 2.
6372
6373
::
6374
6375
sage: graphs.RandomBarabasiAlbert(6,2).edges(labels=False)
6376
[(0, 2), (0, 3), (0, 4), (1, 2), (2, 3), (2, 4), (2, 5), (3, 5)]
6377
6378
We plot a random graph on 12 nodes with m = 3.
6379
6380
::
6381
6382
sage: ba = graphs.RandomBarabasiAlbert(12,3)
6383
sage: ba.show() # long time
6384
6385
We view many random graphs using a graphics array::
6386
6387
sage: g = []
6388
sage: j = []
6389
sage: for i in range(1,10):
6390
... k = graphs.RandomBarabasiAlbert(i+3, 3)
6391
... g.append(k)
6392
...
6393
sage: for i in range(3):
6394
... n = []
6395
... for m in range(3):
6396
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
6397
... j.append(n)
6398
...
6399
sage: G = sage.plot.graphics.GraphicsArray(j)
6400
sage: G.show() # long time
6401
6402
"""
6403
if seed is None:
6404
seed = current_randstate().long_seed()
6405
import networkx
6406
return graph.Graph(networkx.barabasi_albert_graph(n,m,seed=seed))
6407
6408
def RandomBipartite(self, n1,n2, p):
6409
r"""
6410
Returns a bipartite graph with `n1+n2` vertices
6411
such that any edge from `[n1]` to `[n2]` exists
6412
with probability `p`.
6413
6414
INPUT:
6415
6416
- ``n1,n2`` : Cardinalities of the two sets
6417
- ``p`` : Probability for an edge to exist
6418
6419
6420
EXAMPLE::
6421
6422
sage: g=graphs.RandomBipartite(5,2,0.5)
6423
sage: g.vertices()
6424
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (1, 1)]
6425
6426
TESTS::
6427
6428
sage: g=graphs.RandomBipartite(5,-3,0.5)
6429
Traceback (most recent call last):
6430
...
6431
ValueError: n1 and n2 should be integers strictly greater than 0
6432
sage: g=graphs.RandomBipartite(5,3,1.5)
6433
Traceback (most recent call last):
6434
...
6435
ValueError: Parameter p is a probability, and so should be a real value between 0 and 1
6436
6437
Trac ticket #12155::
6438
6439
sage: graphs.RandomBipartite(5,6,.2).complement()
6440
complement(Random bipartite graph of size 5+6 with edge probability 0.200000000000000): Graph on 11 vertices
6441
"""
6442
if not (p>=0 and p<=1):
6443
raise ValueError, "Parameter p is a probability, and so should be a real value between 0 and 1"
6444
if not (n1>0 and n2>0):
6445
raise ValueError, "n1 and n2 should be integers strictly greater than 0"
6446
6447
from numpy.random import uniform
6448
from sage.graphs.all import Graph
6449
6450
g=Graph(name="Random bipartite graph of size "+str(n1) +"+"+str(n2)+" with edge probability "+str(p))
6451
6452
S1=[(0,i) for i in range(n1)]
6453
S2=[(1,i) for i in range(n2)]
6454
g.add_vertices(S1)
6455
g.add_vertices(S2)
6456
6457
for w in range(n2):
6458
for v in range(n1):
6459
if uniform()<=p :
6460
g.add_edge((0,v),(1,w))
6461
6462
pos = {}
6463
for i in range(n1):
6464
pos[(0,i)] = (0, i/(n1-1.0))
6465
for i in range(n2):
6466
pos[(1,i)] = (1, i/(n2-1.0))
6467
6468
g.set_pos(pos)
6469
6470
return g
6471
6472
def RandomGNM(self, n, m, dense=False, seed=None):
6473
"""
6474
Returns a graph randomly picked out of all graphs on n vertices
6475
with m edges.
6476
6477
INPUT:
6478
6479
6480
- ``n`` - number of vertices.
6481
6482
- ``m`` - number of edges.
6483
6484
- ``dense`` - whether to use NetworkX's
6485
dense_gnm_random_graph or gnm_random_graph
6486
6487
6488
EXAMPLES: We show the edge list of a random graph on 5 nodes with
6489
10 edges.
6490
6491
::
6492
6493
sage: graphs.RandomGNM(5, 10).edges(labels=False)
6494
[(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
6495
6496
We plot a random graph on 12 nodes with m = 12.
6497
6498
::
6499
6500
sage: gnm = graphs.RandomGNM(12, 12)
6501
sage: gnm.show() # long time
6502
6503
We view many random graphs using a graphics array::
6504
6505
sage: g = []
6506
sage: j = []
6507
sage: for i in range(9):
6508
... k = graphs.RandomGNM(i+3, i^2-i)
6509
... g.append(k)
6510
...
6511
sage: for i in range(3):
6512
... n = []
6513
... for m in range(3):
6514
... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))
6515
... j.append(n)
6516
...
6517
sage: G = sage.plot.graphics.GraphicsArray(j)
6518
sage: G.show() # long time
6519
"""
6520
if seed is None:
6521
seed = current_randstate().long_seed()
6522
import networkx
6523
if dense:
6524
return graph.Graph(networkx.dense_gnm_random_graph(n, m, seed=seed))
6525
else:
6526
return graph.Graph(networkx.gnm_random_graph(n, m, seed=seed))
6527
6528
def RandomNewmanWattsStrogatz(self, n, k, p, seed=None):
6529
"""
6530
Returns a Newman-Watts-Strogatz small world random graph on n
6531
vertices.
6532
6533
From the NetworkX documentation: First create a ring over n nodes.
6534
Then each node in the ring is connected with its k nearest
6535
neighbors. Then shortcuts are created by adding new edges as
6536
follows: for each edge u-v in the underlying "n-ring with k nearest
6537
neighbors"; with probability p add a new edge u-w with
6538
randomly-chosen existing node w. In contrast with
6539
watts_strogatz_graph(), no edges are removed.
6540
6541
INPUT:
6542
6543
6544
- ``n`` - number of vertices.
6545
6546
- ``k`` - each vertex is connected to its k nearest
6547
neighbors
6548
6549
- ``p`` - the probability of adding a new edge for
6550
each edge
6551
6552
- ``seed`` - for the random number generator
6553
6554
6555
EXAMPLE: We show the edge list of a random graph on 7 nodes with 2
6556
"nearest neighbors" and probability `p = 0.2`::
6557
6558
sage: graphs.RandomNewmanWattsStrogatz(7, 2, 0.2).edges(labels=False)
6559
[(0, 1), (0, 2), (0, 3), (0, 6), (1, 2), (2, 3), (2, 4), (3, 4), (3, 6), (4, 5), (5, 6)]
6560
6561
::
6562
6563
sage: G = graphs.RandomNewmanWattsStrogatz(12, 2, .3)
6564
sage: G.show() # long time
6565
6566
REFERENCE:
6567
6568
- [1] Newman, M.E.J., Watts, D.J. and Strogatz, S.H. Random
6569
graph models of social networks. Proc. Nat. Acad. Sci. USA
6570
99, 2566-2572.
6571
"""
6572
if seed is None:
6573
seed = current_randstate().long_seed()
6574
import networkx
6575
return graph.Graph(networkx.newman_watts_strogatz_graph(n, k, p, seed=seed))
6576
6577
def RandomHolmeKim(self, n, m, p, seed=None):
6578
"""
6579
Returns a random graph generated by the Holme and Kim algorithm for
6580
graphs with power law degree distribution and approximate average
6581
clustering.
6582
6583
INPUT:
6584
6585
6586
- ``n`` - number of vertices.
6587
6588
- ``m`` - number of random edges to add for each new
6589
node.
6590
6591
- ``p`` - probability of adding a triangle after
6592
adding a random edge.
6593
6594
- ``seed`` - for the random number generator.
6595
6596
6597
From the NetworkX documentation: The average clustering has a hard
6598
time getting above a certain cutoff that depends on m. This cutoff
6599
is often quite low. Note that the transitivity (fraction of
6600
triangles to possible triangles) seems to go down with network
6601
size. It is essentially the Barabasi-Albert growth model with an
6602
extra step that each random edge is followed by a chance of making
6603
an edge to one of its neighbors too (and thus a triangle). This
6604
algorithm improves on B-A in the sense that it enables a higher
6605
average clustering to be attained if desired. It seems possible to
6606
have a disconnected graph with this algorithm since the initial m
6607
nodes may not be all linked to a new node on the first iteration
6608
like the BA model.
6609
6610
EXAMPLE: We show the edge list of a random graph on 8 nodes with 2
6611
random edges per node and a probability `p = 0.5` of
6612
forming triangles.
6613
6614
::
6615
6616
sage: graphs.RandomHolmeKim(8, 2, 0.5).edges(labels=False)
6617
[(0, 2), (0, 4), (1, 2), (1, 3), (2, 3), (3, 4), (3, 5), (3, 6), (3, 7), (4, 5), (4, 6)]
6618
6619
::
6620
6621
sage: G = graphs.RandomHolmeKim(12, 3, .3)
6622
sage: G.show() # long time
6623
6624
REFERENCE:
6625
6626
- [1] Holme, P. and Kim, B.J. Growing scale-free networks with
6627
tunable clustering, Phys. Rev. E (2002). vol 65, no 2,
6628
026107.
6629
"""
6630
if seed is None:
6631
seed = current_randstate().long_seed()
6632
import networkx
6633
return graph.Graph(networkx.powerlaw_cluster_graph(n, m, p, seed=seed))
6634
6635
def RandomInterval(self,n):
6636
"""
6637
Returns a random interval graph.
6638
6639
An interval graph is built from a list `(a_i,b_i)_{1\leq i \leq n}`
6640
of intervals : to each interval of the list is associated one
6641
vertex, two vertices being adjacent if the two corresponding
6642
intervals intersect.
6643
6644
A random interval graph of order `n` is generated by picking
6645
random values for the `(a_i,b_j)`, each of the two coordinates
6646
being generated from the uniform distribution on the interval
6647
`[0,1]`.
6648
6649
This definitions follows [boucheron2001]_.
6650
6651
.. NOTE::
6652
6653
The vertices are named 0, 1, 2, and so on. The intervals
6654
used to create the graph are saved with the graph and can
6655
be recovered using ``get_vertex()`` or ``get_vertices()``.
6656
6657
INPUT:
6658
6659
- ``n`` (integer) -- the number of vertices in the random
6660
graph.
6661
6662
EXAMPLE:
6663
6664
As for any interval graph, the chromatic number is equal to
6665
the clique number ::
6666
6667
sage: g = graphs.RandomInterval(8)
6668
sage: g.clique_number() == g.chromatic_number()
6669
True
6670
6671
REFERENCE:
6672
6673
.. [boucheron2001] Boucheron, S. and FERNANDEZ de la VEGA, W.,
6674
On the Independence Number of Random Interval Graphs,
6675
Combinatorics, Probability and Computing v10, issue 05,
6676
Pages 385--396,
6677
Cambridge Univ Press, 2001
6678
"""
6679
6680
from sage.misc.prandom import random
6681
6682
intervals = [tuple(sorted((random(), random()))) for i in range(n)]
6683
6684
return self.IntervalGraph(intervals)
6685
6686
def IntervalGraph(self,intervals):
6687
r"""
6688
Returns the graph corresponding to the given intervals.
6689
6690
An interval graph is built from a list `(a_i,b_i)_{1\leq i \leq n}`
6691
of intervals : to each interval of the list is associated one
6692
vertex, two vertices being adjacent if the two corresponding
6693
(closed) intervals intersect.
6694
6695
INPUT:
6696
6697
- ``intervals`` -- the list of pairs `(a_i,b_i)`
6698
defining the graph.
6699
6700
.. NOTE::
6701
6702
* The vertices are named 0, 1, 2, and so on. The
6703
intervals used to create the graph are saved with the
6704
graph and can be recovered using ``get_vertex()`` or
6705
``get_vertices()``.
6706
6707
* The intervals `(a_i,b_i)` need not verify `a_i<b_i`.
6708
6709
EXAMPLE:
6710
6711
The following line creates the sequence of intervals
6712
`(i, i+2)` for i in `[0, ..., 8]`::
6713
6714
sage: intervals = [(i,i+2) for i in range(9)]
6715
6716
In the corresponding graph... ::
6717
6718
sage: g = graphs.IntervalGraph(intervals)
6719
sage: g.get_vertex(3)
6720
(3, 5)
6721
sage: neigh = g.neighbors(3)
6722
sage: for v in neigh: print g.get_vertex(v)
6723
(1, 3)
6724
(2, 4)
6725
(4, 6)
6726
(5, 7)
6727
6728
The is_interval() method verifies that this graph is an interval
6729
graph. ::
6730
6731
sage: g.is_interval()
6732
True
6733
6734
The intervals in the list need not be distinct. ::
6735
6736
sage: intervals = [ (1,2), (1,2), (1,2), (2,3), (3,4) ]
6737
sage: g = graphs.IntervalGraph(intervals)
6738
sage: g.clique_maximum()
6739
[0, 1, 2, 3]
6740
sage: g.get_vertices()
6741
{0: (1, 2), 1: (1, 2), 2: (1, 2), 3: (2, 3), 4: (3, 4)}
6742
6743
"""
6744
6745
n = len(intervals)
6746
g = graph.Graph(n)
6747
6748
edges = []
6749
6750
for i in range(n-1):
6751
I = intervals[i]
6752
for j in range(i+1,n):
6753
J = intervals[j]
6754
if max(I) < min(J) or max(J) < min(I): continue
6755
edges.append((i,j))
6756
6757
g.add_edges(edges)
6758
6759
rep = dict( zip(range(n),intervals) )
6760
g.set_vertices(rep)
6761
6762
return g
6763
6764
6765
def RandomLobster(self, n, p, q, seed=None):
6766
"""
6767
Returns a random lobster.
6768
6769
A lobster is a tree that reduces to a caterpillar when pruning all
6770
leaf vertices. A caterpillar is a tree that reduces to a path when
6771
pruning all leaf vertices (q=0).
6772
6773
INPUT:
6774
6775
6776
- ``n`` - expected number of vertices in the backbone
6777
6778
- ``p`` - probability of adding an edge to the
6779
backbone
6780
6781
- ``q`` - probability of adding an edge (claw) to the
6782
arms
6783
6784
- ``seed`` - for the random number generator
6785
6786
6787
EXAMPLE: We show the edge list of a random graph with 3 backbone
6788
nodes and probabilities `p = 0.7` and `q = 0.3`::
6789
6790
sage: graphs.RandomLobster(3, 0.7, 0.3).edges(labels=False)
6791
[(0, 1), (1, 2)]
6792
6793
::
6794
6795
sage: G = graphs.RandomLobster(9, .6, .3)
6796
sage: G.show() # long time
6797
"""
6798
if seed is None:
6799
seed = current_randstate().long_seed()
6800
import networkx
6801
return graph.Graph(networkx.random_lobster(n, p, q, seed=seed))
6802
6803
def RandomTree(self, n):
6804
"""
6805
Returns a random tree on `n` nodes numbered `0` through `n-1`.
6806
6807
By Cayley's theorem, there are `n^{n-2}` trees with vertex
6808
set `\{0,1,...,n-1\}`. This constructor chooses one of these uniformly
6809
at random.
6810
6811
ALGORITHM:
6812
6813
The algoritm works by generating an `(n-2)`-long
6814
random sequence of numbers chosen independently and uniformly
6815
from `\{0,1,\ldots,n-1\}` and then applies an inverse
6816
Prufer transformation.
6817
6818
INPUT:
6819
6820
- ``n`` - number of vertices in the tree
6821
6822
EXAMPLE::
6823
6824
sage: G = graphs.RandomTree(10)
6825
sage: G.is_tree()
6826
True
6827
sage: G.show() # long
6828
6829
TESTS:
6830
6831
Ensuring that we encounter no unexpected surprise ::
6832
6833
sage: all( graphs.RandomTree(10).is_tree()
6834
... for i in range(100) )
6835
True
6836
6837
"""
6838
from sage.misc.prandom import randint
6839
g = graph.Graph()
6840
6841
# create random Prufer code
6842
code = [ randint(0,n-1) for i in xrange(n-2) ]
6843
6844
# We count the number of symbols of each type.
6845
# count[k] is the no. of times k appears in code
6846
#
6847
# (count[k] is set to -1 when the corresponding vertex is not
6848
# available anymore)
6849
count = [ 0 for i in xrange(n) ]
6850
for k in code:
6851
count[k] += 1
6852
6853
g.add_vertices(range(n))
6854
6855
for s in code:
6856
for x in range(n):
6857
if count[x] == 0:
6858
break
6859
6860
count[x] = -1
6861
g.add_edge(x,s)
6862
count[s] -= 1
6863
6864
# Adding as an edge the last two available vertices
6865
last_edge = [ v for v in range(n) if count[v] != -1 ]
6866
g.add_edge(last_edge)
6867
6868
return g
6869
6870
def RandomTreePowerlaw(self, n, gamma=3, tries=100, seed=None):
6871
"""
6872
Returns a tree with a power law degree distribution. Returns False
6873
on failure.
6874
6875
From the NetworkX documentation: A trial power law degree sequence
6876
is chosen and then elements are swapped with new elements from a
6877
power law distribution until the sequence makes a tree (size = order
6878
- 1).
6879
6880
INPUT:
6881
6882
6883
- ``n`` - number of vertices
6884
6885
- ``gamma`` - exponent of power law
6886
6887
- ``tries`` - number of attempts to adjust sequence to
6888
make a tree
6889
6890
- ``seed`` - for the random number generator
6891
6892
6893
EXAMPLE: We show the edge list of a random graph with 10 nodes and
6894
a power law exponent of 2.
6895
6896
::
6897
6898
sage: graphs.RandomTreePowerlaw(10, 2).edges(labels=False)
6899
[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (6, 8), (6, 9)]
6900
6901
::
6902
6903
sage: G = graphs.RandomTreePowerlaw(15, 2)
6904
sage: if G:
6905
... G.show() # random output, long time
6906
"""
6907
if seed is None:
6908
seed = current_randstate().long_seed()
6909
import networkx
6910
try:
6911
return graph.Graph(networkx.random_powerlaw_tree(n, gamma, seed=seed, tries=tries))
6912
except:
6913
return False
6914
6915
def RandomRegular(self, d, n, seed=None):
6916
"""
6917
Returns a random d-regular graph on n vertices, or returns False on
6918
failure.
6919
6920
Since every edge is incident to two vertices, n\*d must be even.
6921
6922
INPUT:
6923
6924
6925
- ``n`` - number of vertices
6926
6927
- ``d`` - degree
6928
6929
- ``seed`` - for the random number generator
6930
6931
6932
EXAMPLE: We show the edge list of a random graph with 8 nodes each
6933
of degree 3.
6934
6935
::
6936
6937
sage: graphs.RandomRegular(3, 8).edges(labels=False)
6938
[(0, 1), (0, 4), (0, 7), (1, 5), (1, 7), (2, 3), (2, 5), (2, 6), (3, 4), (3, 6), (4, 5), (6, 7)]
6939
6940
::
6941
6942
sage: G = graphs.RandomRegular(3, 20)
6943
sage: if G:
6944
... G.show() # random output, long time
6945
6946
REFERENCES:
6947
6948
- [1] Kim, Jeong Han and Vu, Van H. Generating random regular
6949
graphs. Proc. 35th ACM Symp. on Thy. of Comp. 2003, pp
6950
213-222. ACM Press, San Diego, CA, USA.
6951
http://doi.acm.org/10.1145/780542.780576
6952
6953
- [2] Steger, A. and Wormald, N. Generating random regular
6954
graphs quickly. Prob. and Comp. 8 (1999), pp 377-396.
6955
"""
6956
if seed is None:
6957
seed = current_randstate().long_seed()
6958
import networkx
6959
try:
6960
N = networkx.random_regular_graph(d, n, seed=seed)
6961
if N is False: return False
6962
return graph.Graph(N, sparse=True)
6963
except:
6964
return False
6965
6966
def RandomShell(self, constructor, seed=None):
6967
"""
6968
Returns a random shell graph for the constructor given.
6969
6970
INPUT:
6971
6972
6973
- ``constructor`` - a list of 3-tuples (n,m,d), each
6974
representing a shell
6975
6976
- ``n`` - the number of vertices in the shell
6977
6978
- ``m`` - the number of edges in the shell
6979
6980
- ``d`` - the ratio of inter (next) shell edges to
6981
intra shell edges
6982
6983
- ``seed`` - for the random number generator
6984
6985
6986
EXAMPLE::
6987
6988
sage: G = graphs.RandomShell([(10,20,0.8),(20,40,0.8)])
6989
sage: G.edges(labels=False)
6990
[(0, 3), (0, 7), (0, 8), (1, 2), (1, 5), (1, 8), (1, 9), (3, 6), (3, 11), (4, 6), (4, 7), (4, 8), (4, 21), (5, 8), (5, 9), (6, 9), (6, 10), (7, 8), (7, 9), (8, 18), (10, 11), (10, 13), (10, 19), (10, 22), (10, 26), (11, 18), (11, 26), (11, 28), (12, 13), (12, 14), (12, 28), (12, 29), (13, 16), (13, 21), (13, 29), (14, 18), (16, 20), (17, 18), (17, 26), (17, 28), (18, 19), (18, 22), (18, 27), (18, 28), (19, 23), (19, 25), (19, 28), (20, 22), (24, 26), (24, 27), (25, 27), (25, 29)]
6991
sage: G.show() # long time
6992
"""
6993
if seed is None:
6994
seed = current_randstate().long_seed()
6995
import networkx
6996
return graph.Graph(networkx.random_shell_graph(constructor, seed=seed))
6997
6998
def WorldMap(self):
6999
"""
7000
Returns the Graph of all the countries, in which two countries are adjacent
7001
in the graph if they have a common boundary.
7002
7003
This graph has been built from the data available
7004
in The CIA World Factbook [CIA]_ (2009-08-21).
7005
7006
The returned graph ``G`` has a member ``G.gps_coordinates``
7007
equal to a dictionary containing the GPS coordinates
7008
of each country's capital city.
7009
7010
EXAMPLE::
7011
7012
sage: g=graphs.WorldMap()
7013
sage: g.has_edge("France","Italy")
7014
True
7015
sage: g.gps_coordinates["Bolivia"]
7016
[[17, 'S'], [65, 'W']]
7017
sage: sorted(g.connected_component_containing_vertex('Ireland'))
7018
['Ireland', 'United Kingdom']
7019
7020
REFERENCE:
7021
7022
.. [CIA] CIA Factbook 09 https://www.cia.gov/library/publications/the-world-factbook/
7023
"""
7024
from sage.graphs.all import Graph
7025
edges = [
7026
('Afghanistan', 'China', None), ('Afghanistan', 'Iran', None),
7027
('Afghanistan', 'Uzbekistan', None), ('Albania', 'Greece', None),
7028
('Albania', 'Kosovo', None), ('Albania', 'Macedonia', None),
7029
('Albania', 'Montenegro', None), ('Algeria', 'Morocco', None),
7030
('Algeria', 'Tunisia', None), ('Andorra', 'Spain', None),
7031
('Angola', 'Democratic Republic of the Congo', None), ('Angola', 'Namibia', None),
7032
('Angola', 'Zambia', None), ('Argentina', 'Bolivia', None),
7033
('Argentina', 'Brazil', None), ('Argentina', 'Chile', None),
7034
('Argentina', 'Paraguay', None), ('Argentina', 'Uruguay', None),
7035
('Armenia', 'Georgia', None), ('Armenia', 'Iran', None),
7036
('Austria', 'Germany', None), ('Azerbaijan', 'Armenia', None),
7037
('Azerbaijan', 'Georgia', None), ('Azerbaijan', 'Iran', None),
7038
('Azerbaijan', 'Russia', None), ('Azerbaijan', 'Turkey', None),
7039
('Bangladesh', 'Burma', None), ('Belgium', 'Germany', None),
7040
('Belgium', 'Netherlands', None), ('Belize', 'Mexico', None),
7041
('Benin', 'Burkina Faso', None), ('Benin', 'Niger', None),
7042
('Benin', 'Nigeria', None), ('Benin', 'Togo', None),
7043
('Bolivia', 'Brazil', None), ('Bolivia', 'Chile', None),
7044
('Bolivia', 'Paraguay', None), ('Bolivia', 'Peru', None),
7045
('Bosnia and Herzegovina', 'Croatia', None), ('Bosnia and Herzegovina', 'Montenegro', None),
7046
('Bosnia and Herzegovina', 'Serbia', None), ('Brazil', 'Colombia', None),
7047
('Brazil', 'Guyana', None), ('Brazil', 'Suriname', None),
7048
('Brazil', 'Venezuela', None), ('Bulgaria', 'Greece', None),
7049
('Bulgaria', 'Macedonia', None), ('Bulgaria', 'Romania', None),
7050
('Bulgaria', 'Serbia', None), ('Burkina Faso', 'Mali', None),
7051
('Burkina Faso', 'Niger', None), ('Burkina Faso', 'Togo', None),
7052
('Burundi', 'Democratic Republic of the Congo', None), ('Cambodia', 'Laos', None),
7053
('Cambodia', 'Thailand', None), ('Cambodia', 'Vietnam', None),
7054
('Cameroon', 'Central African Republic', None), ('Cameroon', 'Chad', None),
7055
('Cameroon', 'Equatorial Guinea', None), ('Cameroon', 'Nigeria', None),
7056
('Cameroon', 'Republic of the Congo', None), ('Canada', 'United States', None),
7057
('Central African Republic', 'Chad', None), ('Central African Republic', 'Democratic Republic of the Congo', None),
7058
('Central African Republic', 'Sudan', None), ('Chad', 'Niger', None),
7059
('Chad', 'Nigeria', None), ('Chad', 'Sudan', None),
7060
('China', 'Bhutan', None), ('China', 'Burma', None),
7061
('China', 'Hong Kong', None), ('China', 'Kazakhstan', None),
7062
('China', 'Kyrgyzstan', None), ('China', 'Mongolia', None),
7063
('China', 'Nepal', None), ('China', 'North Korea', None),
7064
('China', 'Russia', None), ('China', 'Vietnam', None),
7065
('Colombia', 'Venezuela', None), ('Costa Rica', 'Nicaragua', None),
7066
("Cote d'Ivoire", 'Burkina Faso', None), ("Cote d'Ivoire", 'Guinea', None),
7067
("Cote d'Ivoire", 'Mali', None), ('Cyprus', 'Akrotiri', None),
7068
('Cyprus', 'Dhekelia', None), ('Czech Republic', 'Austria', None),
7069
('Czech Republic', 'Germany', None), ('Czech Republic', 'Poland', None),
7070
('Democratic Republic of the Congo', 'Zambia', None), ('Denmark', 'Germany', None),
7071
('Djibouti', 'Eritrea', None), ('Dominican Republic', 'Haiti', None),
7072
('Ecuador', 'Colombia', None), ('El Salvador', 'Honduras', None),
7073
('Ethiopia', 'Djibouti', None), ('Ethiopia', 'Eritrea', None),
7074
('Ethiopia', 'Kenya', None), ('Ethiopia', 'Somalia', None),
7075
('Ethiopia', 'Sudan', None), ('Finland', 'Russia', None),
7076
('Finland', 'Sweden', None), ('France', 'Andorra', None),
7077
('France', 'Belgium', None), ('France', 'Brazil', None),
7078
('France', 'Germany', None), ('France', 'Italy', None),
7079
('France', 'Luxembourg', None), ('France', 'Spain', None),
7080
('France', 'Suriname', None), ('France', 'Switzerland', None),
7081
('Gabon', 'Cameroon', None), ('Gabon', 'Equatorial Guinea', None),
7082
('Gabon', 'Republic of the Congo', None), ('Gaza Strip', 'Egypt', None),
7083
('Gaza Strip', 'Israel', None), ('Ghana', 'Burkina Faso', None),
7084
('Ghana', "Cote d'Ivoire", None), ('Ghana', 'Togo', None),
7085
('Gibraltar', 'Spain', None), ('Guatemala', 'Belize', None),
7086
('Guatemala', 'El Salvador', None), ('Guatemala', 'Honduras', None),
7087
('Guatemala', 'Mexico', None), ('Guinea', 'Sierra Leone', None),
7088
('Guinea-Bissau', 'Guinea', None), ('Guinea-Bissau', 'Senegal', None),
7089
('Honduras', 'Nicaragua', None), ('Hungary', 'Austria', None),
7090
('Hungary', 'Croatia', None), ('Hungary', 'Serbia', None),
7091
('India', 'Bangladesh', None), ('India', 'Bhutan', None),
7092
('India', 'Burma', None), ('India', 'China', None),
7093
('India', 'Nepal', None), ('Indonesia', 'Papua New Guinea', None),
7094
('Iran', 'Iraq', None), ('Ireland', 'United Kingdom', None),
7095
('Israel', 'Egypt', None), ('Italy', 'Austria', None),
7096
('Jordan', 'Iraq', None), ('Jordan', 'Israel', None),
7097
('Jordan', 'Syria', None), ('Jordan', 'West Bank', None),
7098
('Kazakhstan', 'Kyrgyzstan', None), ('Kenya', 'Somalia', None),
7099
('Kenya', 'Sudan', None), ('Kenya', 'Uganda', None),
7100
('Kosovo', 'Macedonia', None), ('Kosovo', 'Serbia', None),
7101
('Kuwait', 'Iraq', None), ('Laos', 'Burma', None),
7102
('Laos', 'China', None), ('Laos', 'Thailand', None),
7103
('Laos', 'Vietnam', None), ('Latvia', 'Belarus', None),
7104
('Latvia', 'Estonia', None), ('Lebanon', 'Israel', None),
7105
('Lesotho', 'South Africa', None), ('Liberia', "Cote d'Ivoire", None),
7106
('Liberia', 'Guinea', None), ('Liberia', 'Sierra Leone', None),
7107
('Libya', 'Algeria', None), ('Libya', 'Chad', None),
7108
('Libya', 'Egypt', None), ('Libya', 'Niger', None),
7109
('Libya', 'Sudan', None), ('Libya', 'Tunisia', None),
7110
('Liechtenstein', 'Austria', None), ('Liechtenstein', 'Switzerland', None),
7111
('Lithuania', 'Belarus', None), ('Lithuania', 'Latvia', None),
7112
('Lithuania', 'Poland', None), ('Lithuania', 'Russia', None),
7113
('Luxembourg', 'Belgium', None), ('Luxembourg', 'Germany', None),
7114
('Macau', 'China', None), ('Macedonia', 'Greece', None),
7115
('Macedonia', 'Serbia', None), ('Malaysia', 'Brunei', None),
7116
('Malaysia', 'Indonesia', None), ('Malaysia', 'Thailand', None),
7117
('Mali', 'Algeria', None), ('Mali', 'Guinea', None),
7118
('Mali', 'Niger', None), ('Mali', 'Senegal', None),
7119
('Mauritania', 'Algeria', None), ('Mauritania', 'Mali', None),
7120
('Mauritania', 'Senegal', None), ('Mauritania', 'Western Sahara', None),
7121
('Monaco', 'France', None), ('Montenegro', 'Croatia', None),
7122
('Montenegro', 'Kosovo', None), ('Montenegro', 'Serbia', None),
7123
('Morocco', 'Spain', None), ('Mozambique', 'Malawi', None),
7124
('Mozambique', 'Zambia', None), ('Mozambique', 'Zimbabwe', None),
7125
('Namibia', 'Botswana', None), ('Namibia', 'Zambia', None),
7126
('Netherlands', 'Germany', None), ('Niger', 'Algeria', None),
7127
('Niger', 'Nigeria', None), ('Norway', 'Finland', None),
7128
('Norway', 'Russia', None), ('Norway', 'Sweden', None),
7129
('Oman', 'United Arab Emirates', None), ('Oman', 'Yemen', None),
7130
('Pakistan', 'Afghanistan', None), ('Pakistan', 'China', None),
7131
('Pakistan', 'India', None), ('Pakistan', 'Iran', None),
7132
('Panama', 'Colombia', None), ('Panama', 'Costa Rica', None),
7133
('Paraguay', 'Brazil', None), ('Peru', 'Brazil', None),
7134
('Peru', 'Chile', None), ('Peru', 'Colombia', None),
7135
('Peru', 'Ecuador', None), ('Poland', 'Belarus', None),
7136
('Poland', 'Germany', None), ('Portugal', 'Spain', None),
7137
('Republic of the Congo', 'Angola', None), ('Republic of the Congo', 'Central African Republic', None),
7138
('Republic of the Congo', 'Democratic Republic of the Congo', None), ('Romania', 'Hungary', None),
7139
('Romania', 'Moldova', None), ('Romania', 'Serbia', None),
7140
('Russia', 'Belarus', None), ('Russia', 'Estonia', None),
7141
('Russia', 'Georgia', None), ('Russia', 'Kazakhstan', None),
7142
('Russia', 'Latvia', None), ('Russia', 'Mongolia', None),
7143
('Russia', 'North Korea', None), ('Russia', 'Poland', None),
7144
('Rwanda', 'Burundi', None), ('Rwanda', 'Democratic Republic of the Congo', None),
7145
('Rwanda', 'Uganda', None), ('Saint Martin', 'Netherlands Antilles', None),
7146
('San Marino', 'Italy', None), ('Saudi Arabia', 'Iraq', None),
7147
('Saudi Arabia', 'Jordan', None), ('Saudi Arabia', 'Kuwait', None),
7148
('Saudi Arabia', 'Oman', None), ('Saudi Arabia', 'Qatar', None),
7149
('Saudi Arabia', 'United Arab Emirates', None), ('Saudi Arabia', 'Yemen', None),
7150
('Senegal', 'Guinea', None), ('Serbia', 'Croatia', None),
7151
('Slovakia', 'Austria', None), ('Slovakia', 'Czech Republic', None),
7152
('Slovakia', 'Hungary', None), ('Slovakia', 'Poland', None),
7153
('Slovakia', 'Ukraine', None), ('Slovenia', 'Austria', None),
7154
('Slovenia', 'Croatia', None), ('Slovenia', 'Hungary', None),
7155
('Slovenia', 'Italy', None), ('Somalia', 'Djibouti', None),
7156
('South Africa', 'Botswana', None), ('South Africa', 'Mozambique', None),
7157
('South Africa', 'Namibia', None), ('South Africa', 'Zimbabwe', None),
7158
('South Korea', 'North Korea', None), ('Sudan', 'Democratic Republic of the Congo', None),
7159
('Sudan', 'Egypt', None), ('Sudan', 'Eritrea', None),
7160
('Suriname', 'Guyana', None), ('Swaziland', 'Mozambique', None),
7161
('Swaziland', 'South Africa', None), ('Switzerland', 'Austria', None),
7162
('Switzerland', 'Germany', None), ('Switzerland', 'Italy', None),
7163
('Syria', 'Iraq', None), ('Syria', 'Israel', None),
7164
('Syria', 'Lebanon', None), ('Tajikistan', 'Afghanistan', None),
7165
('Tajikistan', 'China', None), ('Tajikistan', 'Kyrgyzstan', None),
7166
('Tajikistan', 'Uzbekistan', None), ('Tanzania', 'Burundi', None),
7167
('Tanzania', 'Democratic Republic of the Congo', None), ('Tanzania', 'Kenya', None),
7168
('Tanzania', 'Malawi', None), ('Tanzania', 'Mozambique', None),
7169
('Tanzania', 'Rwanda', None), ('Tanzania', 'Uganda', None),
7170
('Tanzania', 'Zambia', None), ('Thailand', 'Burma', None),
7171
('The Gambia', 'Senegal', None), ('Timor-Leste', 'Indonesia', None),
7172
('Turkey', 'Armenia', None), ('Turkey', 'Bulgaria', None),
7173
('Turkey', 'Georgia', None), ('Turkey', 'Greece', None),
7174
('Turkey', 'Iran', None), ('Turkey', 'Iraq', None),
7175
('Turkey', 'Syria', None), ('Turkmenistan', 'Afghanistan', None),
7176
('Turkmenistan', 'Iran', None), ('Turkmenistan', 'Kazakhstan', None),
7177
('Turkmenistan', 'Uzbekistan', None), ('Uganda', 'Democratic Republic of the Congo', None),
7178
('Uganda', 'Sudan', None), ('Ukraine', 'Belarus', None),
7179
('Ukraine', 'Hungary', None), ('Ukraine', 'Moldova', None),
7180
('Ukraine', 'Poland', None), ('Ukraine', 'Romania', None),
7181
('Ukraine', 'Russia', None), ('United States', 'Mexico', None),
7182
('Uruguay', 'Brazil', None), ('Uzbekistan', 'Kazakhstan', None),
7183
('Uzbekistan', 'Kyrgyzstan', None), ('Vatican City', 'Italy', None),
7184
('Venezuela', 'Guyana', None), ('West Bank', 'Israel', None),
7185
('Western Sahara', 'Algeria', None), ('Western Sahara', 'Morocco', None),
7186
('Zambia', 'Malawi', None), ('Zambia', 'Zimbabwe', None),
7187
('Zimbabwe', 'Botswana', None)
7188
]
7189
gps_coordinates = {
7190
'Canada': [[60, 'N'], [95, 'W']],
7191
'Saint Martin': [[18, 'N'], [63, 'W']],
7192
'Sao Tome and Principe': [[1, 'N'], [7, 'E']],
7193
'Turkmenistan': [[40, 'N'], [60, 'E']],
7194
'Saint Helena': [[15, 'S'], [5, 'W']],
7195
'Lithuania': [[56, 'N'], [24, 'E']],
7196
'Cambodia': [[13, 'N'], [105, 'E']],
7197
'Saint Kitts and Nevis': [[17, 'N'], [62, 'W']],
7198
'Ethiopia': [[8, 'N'], [38, 'E']],
7199
'The Gambia': [[13, 'N'], [16, 'W']],
7200
'Aruba': [[12, 'N'], [69, 'W']],
7201
'Swaziland': [[26, 'S'], [31, 'E']],
7202
'Guinea-Bissau': [[12, 'N'], [15, 'W']],
7203
'Argentina': [[34, 'S'], [64, 'W']],
7204
'Bolivia': [[17, 'S'], [65, 'W']],
7205
'Bahamas, The': [[24, 'N'], [76, 'W']],
7206
'Spratly Islands': [[8, 'N'], [111, 'E']],
7207
'Ghana': [[8, 'N'], [2, 'W']],
7208
'Saudi Arabia': [[25, 'N'], [45, 'E']],
7209
'American Samoa': [[14, 'S'], [170, 'W']],
7210
'Cocos (Keeling) Islands': [[12, 'S'], [96, 'E']],
7211
'Slovenia': [[46, 'N'], [14, 'E']],
7212
'Guatemala': [[15, 'N'], [90, 'W']],
7213
'Bosnia and Herzegovina': [[44, 'N'], [18, 'E']],
7214
'Kuwait': [[29, 'N'], [45, 'E']],
7215
'Jordan': [[31, 'N'], [36, 'E']],
7216
'Saint Barthelemy': [[17, 'N'], [62, 'W']],
7217
'Ashmore and Cartier Islands': [[12, 'S'], [123, 'E']],
7218
'Dominica': [[15, 'N'], [61, 'W']],
7219
'Liberia': [[6, 'N'], [9, 'W']],
7220
'Maldives': [[3, 'N'], [73, 'E']],
7221
'Micronesia, Federated States of': [[6, 'N'], [158, 'E']],
7222
'Pakistan': [[30, 'N'], [70, 'E']],
7223
'Oman': [[21, 'N'], [57, 'E']],
7224
'Tanzania': [[6, 'S'], [35, 'E']],
7225
'Albania': [[41, 'N'], [20, 'E']],
7226
'Gabon': [[1, 'S'], [11, 'E']],
7227
'Niue': [[19, 'S'], [169, 'W']],
7228
'Monaco': [[43, 'N'], [7, 'E']],
7229
'Wallis and Futuna': [[13, 'S'], [176, 'W']],
7230
'New Zealand': [[41, 'S'], [174, 'E']],
7231
'Yemen': [[15, 'N'], [48, 'E']],
7232
'Jersey': [[49, 'N'], [2, 'W']],
7233
'Jamaica': [[18, 'N'], [77, 'W']],
7234
'Greenland': [[72, 'N'], [40, 'W']],
7235
'West Bank': [[32, 'N'], [35, 'E']],
7236
'Macau': [[22, 'N'], [113, 'E']],
7237
'Jan Mayen': [[71, 'N'], [8, 'W']],
7238
'United Arab Emirates': [[24, 'N'], [54, 'E']],
7239
'Guam': [[13, 'N'], [144, 'E']],
7240
'Uruguay': [[33, 'S'], [56, 'W']],
7241
'India': [[20, 'N'], [77, 'E']],
7242
'Azerbaijan': [[40, 'N'], [47, 'E']],
7243
'Lesotho': [[29, 'S'], [28, 'E']],
7244
'Saint Vincent and the Grenadines': [[13, 'N'], [61, 'W']],
7245
'Kenya': [[1, 'N'], [38, 'E']],
7246
'South Korea': [[37, 'N'], [127, 'E']],
7247
'Tajikistan': [[39, 'N'], [71, 'E']],
7248
'Turkey': [[39, 'N'], [35, 'E']],
7249
'Afghanistan': [[33, 'N'], [65, 'E']],
7250
'Paraguay': [[23, 'S'], [58, 'W']],
7251
'Bangladesh': [[24, 'N'], [90, 'E']],
7252
'Mauritania': [[20, 'N'], [12, 'W']],
7253
'Solomon Islands': [[8, 'S'], [159, 'E']],
7254
'Saint Pierre and Miquelon': [[46, 'N'], [56, 'W']],
7255
'Gaza Strip': [[31, 'N'], [34, 'E']],
7256
'San Marino': [[43, 'N'], [12, 'E']],
7257
'French Polynesia': [[15, 'S'], [140, 'W']],
7258
'France': [[46, 'N'], [2, 'E']],
7259
'Fiji': [[18, 'S'], [175, 'E']],
7260
'Rwanda': [[2, 'S'], [30, 'E']],
7261
'Slovakia': [[48, 'N'], [19, 'E']],
7262
'Somalia': [[10, 'N'], [49, 'E']],
7263
'Peru': [[10, 'S'], [76, 'W']],
7264
'Laos': [[18, 'N'], [105, 'E']],
7265
'Nauru': [[0, 'S'], [166, 'E']],
7266
'Seychelles': [[4, 'S'], [55, 'E']],
7267
'Norway': [[62, 'N'], [10, 'E']],
7268
"Cote d'Ivoire": [[8, 'N'], [5, 'W']],
7269
'Cook Islands': [[21, 'S'], [159, 'W']],
7270
'Benin': [[9, 'N'], [2, 'E']],
7271
'Western Sahara': [[24, 'N'], [13, 'W']],
7272
'Cuba': [[21, 'N'], [80, 'W']],
7273
'Cameroon': [[6, 'N'], [12, 'E']],
7274
'Montenegro': [[42, 'N'], [19, 'E']],
7275
'Republic of the Congo': [[1, 'S'], [15, 'E']],
7276
'Burkina Faso': [[13, 'N'], [2, 'W']],
7277
'Togo': [[8, 'N'], [1, 'E']],
7278
'Virgin Islands': [[18, 'N'], [64, 'W']],
7279
'China': [[35, 'N'], [105, 'E']],
7280
'Armenia': [[40, 'N'], [45, 'E']],
7281
'Timor-Leste': [[8, 'S'], [125, 'E']],
7282
'Dominican Republic': [[19, 'N'], [70, 'W']],
7283
'Ukraine': [[49, 'N'], [32, 'E']],
7284
'Bahrain': [[26, 'N'], [50, 'E']],
7285
'Tonga': [[20, 'S'], [175, 'W']],
7286
'Finland': [[64, 'N'], [26, 'E']],
7287
'Libya': [[25, 'N'], [17, 'E']],
7288
'Cayman Islands': [[19, 'N'], [80, 'W']],
7289
'Central African Republic': [[7, 'N'], [21, 'E']],
7290
'New Caledonia': [[21, 'S'], [165, 'E']],
7291
'Mauritius': [[20, 'S'], [57, 'E']],
7292
'Liechtenstein': [[47, 'N'], [9, 'E']],
7293
'Vietnam': [[16, 'N'], [107, 'E']],
7294
'British Virgin Islands': [[18, 'N'], [64, 'W']],
7295
'Mali': [[17, 'N'], [4, 'W']],
7296
'Vatican City': [[41, 'N'], [12, 'E']],
7297
'Russia': [[60, 'N'], [100, 'E']],
7298
'Bulgaria': [[43, 'N'], [25, 'E']],
7299
'United States': [[38, 'N'], [97, 'W']],
7300
'Romania': [[46, 'N'], [25, 'E']],
7301
'Angola': [[12, 'S'], [18, 'E']],
7302
'Chad': [[15, 'N'], [19, 'E']],
7303
'South Africa': [[29, 'S'], [24, 'E']],
7304
'Tokelau': [[9, 'S'], [172, 'W']],
7305
'Turks and Caicos Islands': [[21, 'N'], [71, 'W']],
7306
'South Georgia and the South Sandwich Islands': [[54, 'S'], [37, 'W']],
7307
'Sweden': [[62, 'N'], [15, 'E']],
7308
'Qatar': [[25, 'N'], [51, 'E']],
7309
'Malaysia': [[2, 'N'], [112, 'E']],
7310
'Senegal': [[14, 'N'], [14, 'W']],
7311
'Latvia': [[57, 'N'], [25, 'E']],
7312
'Clipperton Island': [[10, 'N'], [109, 'W']],
7313
'Uganda': [[1, 'N'], [32, 'E']],
7314
'Japan': [[36, 'N'], [138, 'E']],
7315
'Niger': [[16, 'N'], [8, 'E']],
7316
'Brazil': [[10, 'S'], [55, 'W']],
7317
'Faroe Islands': [[62, 'N'], [7, 'W']],
7318
'Guinea': [[11, 'N'], [10, 'W']],
7319
'Panama': [[9, 'N'], [80, 'W']],
7320
'Costa Rica': [[10, 'N'], [84, 'W']],
7321
'Luxembourg': [[49, 'N'], [6, 'E']],
7322
'Cape Verde': [[16, 'N'], [24, 'W']],
7323
'Andorra': [[42, 'N'], [1, 'E']],
7324
'Gibraltar': [[36, 'N'], [5, 'W']],
7325
'Ireland': [[53, 'N'], [8, 'W']],
7326
'Syria': [[35, 'N'], [38, 'E']],
7327
'Palau': [[7, 'N'], [134, 'E']],
7328
'Nigeria': [[10, 'N'], [8, 'E']],
7329
'Ecuador': [[2, 'S'], [77, 'W']],
7330
'Northern Mariana Islands': [[15, 'N'], [145, 'E']],
7331
'Brunei': [[4, 'N'], [114, 'E']],
7332
'Mozambique': [[18, 'S'], [35, 'E']],
7333
'Australia': [[27, 'S'], [133, 'E']],
7334
'Iran': [[32, 'N'], [53, 'E']],
7335
'Algeria': [[28, 'N'], [3, 'E']],
7336
'Svalbard': [[78, 'N'], [20, 'E']],
7337
'El Salvador': [[13, 'N'], [88, 'W']],
7338
'Tuvalu': [[8, 'S'], [178, 'E']],
7339
'Pitcairn Islands': [[25, 'S'], [130, 'W']],
7340
'Czech Republic': [[49, 'N'], [15, 'E']],
7341
'Marshall Islands': [[9, 'N'], [168, 'E']],
7342
'Chile': [[30, 'S'], [71, 'W']],
7343
'Puerto Rico': [[18, 'N'], [66, 'W']],
7344
'Belgium': [[50, 'N'], [4, 'E']],
7345
'Kiribati': [[1, 'N'], [173, 'E']],
7346
'Haiti': [[19, 'N'], [72, 'W']],
7347
'Belize': [[17, 'N'], [88, 'W']],
7348
'Hong Kong': [[22, 'N'], [114, 'E']],
7349
'Saint Lucia': [[13, 'N'], [60, 'W']],
7350
'Georgia': [[42, 'N'], [43, 'E']],
7351
'Mexico': [[23, 'N'], [102, 'W']],
7352
'Denmark': [[56, 'N'], [10, 'E']],
7353
'Poland': [[52, 'N'], [20, 'E']],
7354
'Moldova': [[47, 'N'], [29, 'E']],
7355
'Morocco': [[32, 'N'], [5, 'W']],
7356
'Namibia': [[22, 'S'], [17, 'E']],
7357
'Mongolia': [[46, 'N'], [105, 'E']],
7358
'Guernsey': [[49, 'N'], [2, 'W']],
7359
'Thailand': [[15, 'N'], [100, 'E']],
7360
'Switzerland': [[47, 'N'], [8, 'E']],
7361
'Grenada': [[12, 'N'], [61, 'W']],
7362
'Navassa Island': [[18, 'N'], [75, 'W']],
7363
'Isle of Man': [[54, 'N'], [4, 'W']],
7364
'Portugal': [[39, 'N'], [8, 'W']],
7365
'Estonia': [[59, 'N'], [26, 'E']],
7366
'Kosovo': [[42, 'N'], [21, 'E']],
7367
'Norfolk Island': [[29, 'S'], [167, 'E']],
7368
'Bouvet Island': [[54, 'S'], [3, 'E']],
7369
'Lebanon': [[33, 'N'], [35, 'E']],
7370
'Sierra Leone': [[8, 'N'], [11, 'W']],
7371
'Uzbekistan': [[41, 'N'], [64, 'E']],
7372
'Tunisia': [[34, 'N'], [9, 'E']],
7373
'Djibouti': [[11, 'N'], [43, 'E']],
7374
'Heard Island and McDonald Islands': [[53, 'S'], [72, 'E']],
7375
'Antigua and Barbuda': [[17, 'N'], [61, 'W']],
7376
'Spain': [[40, 'N'], [4, 'W']],
7377
'Colombia': [[4, 'N'], [72, 'W']],
7378
'Burundi': [[3, 'S'], [30, 'E']],
7379
'Taiwan': [[23, 'N'], [121, 'E']],
7380
'Cyprus': [[35, 'N'], [33, 'E']],
7381
'Barbados': [[13, 'N'], [59, 'W']],
7382
'Falkland Islands (Islas Malvinas)': [[51, 'S'], [59, 'W']],
7383
'Madagascar': [[20, 'S'], [47, 'E']],
7384
'Italy': [[42, 'N'], [12, 'E']],
7385
'Bhutan': [[27, 'N'], [90, 'E']],
7386
'Sudan': [[15, 'N'], [30, 'E']],
7387
'Vanuatu': [[16, 'S'], [167, 'E']],
7388
'Malta': [[35, 'N'], [14, 'E']],
7389
'Hungary': [[47, 'N'], [20, 'E']],
7390
'Democratic Republic of the Congo': [[0, 'N'], [25, 'E']],
7391
'Netherlands': [[52, 'N'], [5, 'E']],
7392
'Bermuda': [[32, 'N'], [64, 'W']],
7393
'Suriname': [[4, 'N'], [56, 'W']],
7394
'Anguilla': [[18, 'N'], [63, 'W']],
7395
'Venezuela': [[8, 'N'], [66, 'W']],
7396
'Netherlands Antilles': [[12, 'N'], [69, 'W']],
7397
'Israel': [[31, 'N'], [34, 'E']],
7398
'Paracel Islands': [[16, 'N'], [112, 'E']],
7399
'Wake Island': [[19, 'N'], [166, 'E']],
7400
'Indonesia': [[5, 'S'], [120, 'E']],
7401
'Iceland': [[65, 'N'], [18, 'W']],
7402
'Zambia': [[15, 'S'], [30, 'E']],
7403
'Samoa': [[13, 'S'], [172, 'W']],
7404
'Austria': [[47, 'N'], [13, 'E']],
7405
'Papua New Guinea': [[6, 'S'], [147, 'E']],
7406
'Malawi': [[13, 'S'], [34, 'E']],
7407
'Zimbabwe': [[20, 'S'], [30, 'E']],
7408
'Germany': [[51, 'N'], [9, 'E']],
7409
'Dhekelia': [[34, 'N'], [33, 'E']],
7410
'Kazakhstan': [[48, 'N'], [68, 'E']],
7411
'Philippines': [[13, 'N'], [122, 'E']],
7412
'Eritrea': [[15, 'N'], [39, 'E']],
7413
'Kyrgyzstan': [[41, 'N'], [75, 'E']],
7414
'Mayotte': [[12, 'S'], [45, 'E']],
7415
'Iraq': [[33, 'N'], [44, 'E']],
7416
'Montserrat': [[16, 'N'], [62, 'W']],
7417
'Coral Sea Islands': [[18, 'S'], [152, 'E']],
7418
'Macedonia': [[41, 'N'], [22, 'E']],
7419
'British Indian Ocean Territory': [[6, 'S'], [71, 'E']],
7420
'North Korea': [[40, 'N'], [127, 'E']],
7421
'Trinidad and Tobago': [[11, 'N'], [61, 'W']],
7422
'Akrotiri': [[34, 'N'], [32, 'E']],
7423
'Guyana': [[5, 'N'], [59, 'W']],
7424
'Belarus': [[53, 'N'], [28, 'E']],
7425
'Nepal': [[28, 'N'], [84, 'E']],
7426
'Burma': [[22, 'N'], [98, 'E']],
7427
'Honduras': [[15, 'N'], [86, 'W']],
7428
'Equatorial Guinea': [[2, 'N'], [10, 'E']],
7429
'Egypt': [[27, 'N'], [30, 'E']],
7430
'Nicaragua': [[13, 'N'], [85, 'W']],
7431
'Singapore': [[1, 'N'], [103, 'E']],
7432
'Serbia': [[44, 'N'], [21, 'E']],
7433
'Botswana': [[22, 'S'], [24, 'E']],
7434
'United Kingdom': [[54, 'N'], [2, 'W']],
7435
'Antarctica': [[90, 'S'], [0, 'E']],
7436
'Christmas Island': [[10, 'S'], [105, 'E']],
7437
'Greece': [[39, 'N'], [22, 'E']],
7438
'Sri Lanka': [[7, 'N'], [81, 'E']],
7439
'Croatia': [[45, 'N'], [15, 'E']],
7440
'Comoros': [[12, 'S'], [44, 'E']]
7441
}
7442
g = Graph()
7443
g.add_edges(edges)
7444
g.gps_coordinates = gps_coordinates
7445
g.name("World Map")
7446
return g
7447
7448
7449
7450
################################################################################
7451
# Graphs with a given degree sequence
7452
################################################################################
7453
7454
def DegreeSequence(self, deg_sequence):
7455
"""
7456
Returns a graph with the given degree sequence. Raises a NetworkX
7457
error if the proposed degree sequence cannot be that of a graph.
7458
7459
Graph returned is the one returned by the Havel-Hakimi algorithm,
7460
which constructs a simple graph by connecting vertices of highest
7461
degree to other vertices of highest degree, resorting the remaining
7462
vertices by degree and repeating the process. See Theorem 1.4 in
7463
[1].
7464
7465
INPUT:
7466
7467
7468
- ``deg_sequence`` - a list of integers with each
7469
entry corresponding to the degree of a different vertex.
7470
7471
7472
EXAMPLES::
7473
7474
sage: G = graphs.DegreeSequence([3,3,3,3])
7475
sage: G.edges(labels=False)
7476
[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
7477
sage: G.show() # long time
7478
7479
::
7480
7481
sage: G = graphs.DegreeSequence([3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3])
7482
sage: G.show() # long time
7483
7484
::
7485
7486
sage: G = graphs.DegreeSequence([4,4,4,4,4,4,4,4])
7487
sage: G.show() # long time
7488
7489
::
7490
7491
sage: G = graphs.DegreeSequence([1,2,3,4,3,4,3,2,3,2,1])
7492
sage: G.show() # long time
7493
7494
REFERENCE:
7495
7496
- [1] Chartrand, G. and Lesniak, L. Graphs and Digraphs.
7497
Chapman and Hall/CRC, 1996.
7498
"""
7499
import networkx
7500
return graph.Graph(networkx.havel_hakimi_graph([int(i) for i in deg_sequence]))
7501
7502
def DegreeSequenceBipartite(self, s1 ,s2 ):
7503
r"""
7504
Returns a bipartite graph whose two sets have the given
7505
degree sequences.
7506
7507
Given two different sequences of degrees `s_1` and `s_2`,
7508
this functions returns ( if possible ) a bipartite graph
7509
on sets `A` and `B` such that the vertices in `A` have
7510
`s_1` as their degree sequence, while `s_2` is the degree
7511
sequence of the vertices in `B`.
7512
7513
INPUT:
7514
7515
- ``s_1`` -- list of integers corresponding to the degree
7516
sequence of the first set.
7517
- ``s_2`` -- list of integers corresponding to the degree
7518
sequence of the second set.
7519
7520
ALGORITHM:
7521
7522
This function works through the computation of the matrix
7523
given by the Gale-Ryser theorem, which is in this case
7524
the adjacency matrix of the bipartite graph.
7525
7526
EXAMPLES:
7527
7528
If we are given as sequences ``[2,2,2,2,2]`` and ``[5,5]``
7529
we are given as expected the complete bipartite
7530
graph `K_{2,5}` ::
7531
7532
sage: g = graphs.DegreeSequenceBipartite([2,2,2,2,2],[5,5])
7533
sage: g.is_isomorphic(graphs.CompleteBipartiteGraph(5,2))
7534
True
7535
7536
Some sequences being incompatible if, for example, their sums
7537
are different, the functions raises a ``ValueError`` when no
7538
graph corresponding to the degree sequences exists. ::
7539
7540
sage: g = graphs.DegreeSequenceBipartite([2,2,2,2,1],[5,5])
7541
Traceback (most recent call last):
7542
...
7543
ValueError: There exists no bipartite graph corresponding to the given degree sequences
7544
7545
TESTS:
7546
7547
Trac ticket #12155::
7548
7549
sage: graphs.DegreeSequenceBipartite([2,2,2,2,2],[5,5]).complement()
7550
complement(): Graph on 7 vertices
7551
"""
7552
7553
from sage.combinat.integer_vector import gale_ryser_theorem
7554
from sage.graphs.graph import Graph
7555
from sage.graphs.bipartite_graph import BipartiteGraph
7556
7557
s1 = sorted(s1, reverse = True)
7558
s2 = sorted(s2, reverse = True)
7559
7560
m = gale_ryser_theorem(s1,s2)
7561
7562
if m is False:
7563
raise ValueError("There exists no bipartite graph corresponding to the given degree sequences")
7564
else:
7565
return Graph(BipartiteGraph(m))
7566
7567
def DegreeSequenceConfigurationModel(self, deg_sequence, seed=None):
7568
"""
7569
Returns a random pseudograph with the given degree sequence. Raises
7570
a NetworkX error if the proposed degree sequence cannot be that of
7571
a graph with multiple edges and loops.
7572
7573
One requirement is that the sum of the degrees must be even, since
7574
every edge must be incident with two vertices.
7575
7576
INPUT:
7577
7578
7579
- ``deg_sequence`` - a list of integers with each
7580
entry corresponding to the expected degree of a different vertex.
7581
7582
- ``seed`` - for the random number generator.
7583
7584
7585
EXAMPLES::
7586
7587
sage: G = graphs.DegreeSequenceConfigurationModel([1,1])
7588
sage: G.adjacency_matrix()
7589
[0 1]
7590
[1 0]
7591
7592
Note: as of this writing, plotting of loops and multiple edges is
7593
not supported, and the output is allowed to contain both types of
7594
edges.
7595
7596
::
7597
7598
sage: G = graphs.DegreeSequenceConfigurationModel([3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3])
7599
sage: G.edges(labels=False)
7600
[(0, 2), (0, 10), (0, 15), (1, 6), (1, 16), (1, 17), (2, 5), (2, 19), (3, 7), (3, 14), (3, 14), (4, 9), (4, 13), (4, 19), (5, 6), (5, 15), (6, 11), (7, 11), (7, 17), (8, 11), (8, 18), (8, 19), (9, 12), (9, 13), (10, 15), (10, 18), (12, 13), (12, 16), (14, 17), (16, 18)]
7601
sage: G.show() # long time
7602
7603
REFERENCE:
7604
7605
- [1] Newman, M.E.J. The Structure and function of complex
7606
networks, SIAM Review vol. 45, no. 2 (2003), pp. 167-256.
7607
"""
7608
if seed is None:
7609
seed = current_randstate().long_seed()
7610
import networkx
7611
return graph.Graph(networkx.configuration_model([int(i) for i in deg_sequence], seed=seed), loops=True, multiedges=True, sparse=True)
7612
7613
def DegreeSequenceTree(self, deg_sequence):
7614
"""
7615
Returns a tree with the given degree sequence. Raises a NetworkX
7616
error if the proposed degree sequence cannot be that of a tree.
7617
7618
Since every tree has one more vertex than edge, the degree sequence
7619
must satisfy len(deg_sequence) - sum(deg_sequence)/2 == 1.
7620
7621
INPUT:
7622
7623
7624
- ``deg_sequence`` - a list of integers with each
7625
entry corresponding to the expected degree of a different vertex.
7626
7627
7628
EXAMPLE::
7629
7630
sage: G = graphs.DegreeSequenceTree([3,1,3,3,1,1,1,2,1])
7631
sage: G.show() # long time
7632
"""
7633
import networkx
7634
return graph.Graph(networkx.degree_sequence_tree([int(i) for i in deg_sequence]))
7635
7636
def DegreeSequenceExpected(self, deg_sequence, seed=None):
7637
"""
7638
Returns a random graph with expected given degree sequence. Raises
7639
a NetworkX error if the proposed degree sequence cannot be that of
7640
a graph.
7641
7642
One requirement is that the sum of the degrees must be even, since
7643
every edge must be incident with two vertices.
7644
7645
INPUT:
7646
7647
7648
- ``deg_sequence`` - a list of integers with each
7649
entry corresponding to the expected degree of a different vertex.
7650
7651
- ``seed`` - for the random number generator.
7652
7653
7654
EXAMPLE::
7655
7656
sage: G = graphs.DegreeSequenceExpected([1,2,3,2,3])
7657
sage: G.edges(labels=False)
7658
[(0, 2), (1, 1), (1, 3), (2, 2), (2, 4), (3, 3)]
7659
sage: G.show() # long time
7660
7661
REFERENCE:
7662
7663
- [1] Chung, Fan and Lu, L. Connected components in random
7664
graphs with given expected degree
7665
sequences. Ann. Combinatorics (6), 2002 pp. 125-145.
7666
"""
7667
if seed is None:
7668
seed = current_randstate().long_seed()
7669
import networkx
7670
return graph.Graph(networkx.expected_degree_graph([int(i) for i in deg_sequence], seed=seed), loops=True)
7671
7672
###########################################################################
7673
# Graph Iterators
7674
###########################################################################
7675
7676
def __call__(self, vertices=None, property=lambda x: True, augment='edges',
7677
size=None, deg_seq=None, degree_sequence=None, loops=False, implementation='c_graph',
7678
sparse=True, copy = True):
7679
"""
7680
Accesses the generator of isomorphism class representatives.
7681
Iterates over distinct, exhaustive representatives. See the docstring
7682
of this class for full documentation.
7683
7684
EXAMPLES:
7685
7686
Print graphs on 3 or less vertices::
7687
7688
sage: for G in graphs(3, augment='vertices'):
7689
... print G
7690
Graph on 0 vertices
7691
Graph on 1 vertex
7692
Graph on 2 vertices
7693
Graph on 3 vertices
7694
Graph on 3 vertices
7695
Graph on 3 vertices
7696
Graph on 2 vertices
7697
Graph on 3 vertices
7698
7699
::
7700
7701
sage: for g in graphs():
7702
... if g.num_verts() > 3: break
7703
... print g
7704
Graph on 0 vertices
7705
Graph on 1 vertex
7706
Graph on 2 vertices
7707
Graph on 2 vertices
7708
Graph on 3 vertices
7709
Graph on 3 vertices
7710
Graph on 3 vertices
7711
Graph on 3 vertices
7712
7713
For more examples, see the class level documentation, or type::
7714
7715
sage: graphs? # not tested
7716
7717
REFERENCE:
7718
7719
- Brendan D. McKay, Isomorph-Free Exhaustive generation.
7720
Journal of Algorithms Volume 26, Issue 2, February 1998,
7721
pages 306-324.
7722
"""
7723
from sage.graphs.all import Graph
7724
from sage.misc.misc import deprecation
7725
from copy import copy as copyfun
7726
7727
if deg_seq is not None:
7728
deprecation("The argument name deg_seq is deprecated. It will be "
7729
"removed in a future release of Sage. So, please use "
7730
"degree_sequence instead.")
7731
if degree_sequence is None:
7732
degree_sequence=deg_seq
7733
if degree_sequence is not None:
7734
if vertices is None:
7735
raise NotImplementedError
7736
if len(degree_sequence) != vertices or sum(degree_sequence)%2 or sum(degree_sequence) > vertices*(vertices-1):
7737
raise ValueError("Invalid degree sequence.")
7738
degree_sequence = sorted(degree_sequence)
7739
if augment == 'edges':
7740
property = lambda x: all([degree_sequence[i] >= d for i,d in enumerate(sorted(x.degree()))])
7741
extra_property = lambda x: degree_sequence == sorted(x.degree())
7742
else:
7743
property = lambda x: all([degree_sequence[i] >= d for i,d in enumerate(sorted(x.degree() + [0]*(vertices-x.num_verts()) ))])
7744
extra_property = lambda x: x.num_verts() == vertices and degree_sequence == sorted(x.degree())
7745
elif size is not None:
7746
extra_property = lambda x: x.size() == size
7747
else:
7748
extra_property = lambda x: True
7749
if augment == 'vertices':
7750
if vertices is None:
7751
raise NotImplementedError
7752
g = Graph(loops=loops, implementation=implementation, sparse=sparse)
7753
for gg in canaug_traverse_vert(g, [], vertices, property, loops=loops, implementation=implementation, sparse=sparse):
7754
if extra_property(gg):
7755
yield copyfun(gg) if copy else gg
7756
elif augment == 'edges':
7757
if vertices is None:
7758
from sage.rings.all import Integer
7759
vertices = Integer(0)
7760
while True:
7761
for g in self(vertices, loops=loops, implementation=implementation, sparse=sparse):
7762
yield copyfun(g) if copy else g
7763
vertices += 1
7764
g = Graph(vertices, loops=loops, implementation=implementation, sparse=sparse)
7765
gens = []
7766
for i in range(vertices-1):
7767
gen = range(i)
7768
gen.append(i+1); gen.append(i)
7769
gen += range(i+2, vertices)
7770
gens.append(gen)
7771
for gg in canaug_traverse_edge(g, gens, property, loops=loops, implementation=implementation, sparse=sparse):
7772
if extra_property(gg):
7773
yield copyfun(gg) if copy else gg
7774
else:
7775
raise NotImplementedError
7776
7777
def line_graph_forbidden_subgraphs(self):
7778
r"""
7779
Returns the 9 forbidden subgraphs of a line graph.
7780
7781
`Wikipedia article on the line graphs
7782
<http://en.wikipedia.org/wiki/Line_graph>`_
7783
7784
The graphs are returned in the ordering given by the Wikipedia
7785
drawing, read from left to right and from top to bottom.
7786
7787
EXAMPLE::
7788
7789
sage: graphs.line_graph_forbidden_subgraphs()
7790
[Claw graph: Graph on 4 vertices,
7791
Graph on 6 vertices,
7792
Graph on 6 vertices,
7793
Graph on 5 vertices,
7794
Graph on 6 vertices,
7795
Graph on 6 vertices,
7796
Graph on 6 vertices,
7797
Graph on 6 vertices,
7798
Graph on 5 vertices]
7799
7800
"""
7801
from sage.graphs.all import Graph
7802
graphs = [self.ClawGraph()]
7803
7804
graphs.append(Graph({
7805
0: [1, 2, 3],
7806
1: [2, 3],
7807
4: [2],
7808
5: [3]
7809
}))
7810
7811
graphs.append(Graph({
7812
0: [1, 2, 3, 4],
7813
1: [2, 3, 4],
7814
3: [4],
7815
2: [5]
7816
}))
7817
7818
graphs.append(Graph({
7819
0: [1, 2, 3],
7820
1: [2, 3],
7821
4: [2, 3]
7822
}))
7823
7824
graphs.append(Graph({
7825
0: [1, 2, 3],
7826
1: [2, 3],
7827
4: [2],
7828
5: [3, 4]
7829
}))
7830
7831
graphs.append(Graph({
7832
0: [1, 2, 3, 4],
7833
1: [2, 3, 4],
7834
3: [4],
7835
5: [2, 0, 1]
7836
}))
7837
7838
graphs.append(Graph({
7839
5: [0, 1, 2, 3, 4],
7840
0: [1, 4],
7841
2: [1, 3],
7842
3: [4]
7843
}))
7844
7845
graphs.append(Graph({
7846
1: [0, 2, 3, 4],
7847
3: [0, 4],
7848
2: [4, 5],
7849
4: [5]
7850
}))
7851
7852
graphs.append(Graph({
7853
0: [1, 2, 3],
7854
1: [2, 3, 4],
7855
2: [3, 4],
7856
3: [4]
7857
}))
7858
7859
return graphs
7860
7861
def trees(self, vertices):
7862
r"""
7863
Returns a generator of the distinct trees on a fixed number of vertices.
7864
7865
INPUT:
7866
7867
- ``vertices`` - the size of the trees created.
7868
7869
OUTPUT:
7870
7871
A generator which creates an exhaustive, duplicate-free listing
7872
of the connected free (unlabeled) trees with ``vertices`` number
7873
of vertices. A tree is a graph with no cycles.
7874
7875
ALGORITHM:
7876
7877
Uses an algorithm that generates each new tree
7878
in constant time. See the documentation for, and implementation
7879
of, the :mod:`sage.graphs.trees` module, including a citation.
7880
7881
EXAMPLES:
7882
7883
We create an iterator, then loop over its elements. ::
7884
7885
sage: tree_iterator = graphs.trees(7)
7886
sage: for T in tree_iterator:
7887
... print T.degree_sequence()
7888
[2, 2, 2, 2, 2, 1, 1]
7889
[3, 2, 2, 2, 1, 1, 1]
7890
[3, 2, 2, 2, 1, 1, 1]
7891
[4, 2, 2, 1, 1, 1, 1]
7892
[3, 3, 2, 1, 1, 1, 1]
7893
[3, 3, 2, 1, 1, 1, 1]
7894
[4, 3, 1, 1, 1, 1, 1]
7895
[3, 2, 2, 2, 1, 1, 1]
7896
[4, 2, 2, 1, 1, 1, 1]
7897
[5, 2, 1, 1, 1, 1, 1]
7898
[6, 1, 1, 1, 1, 1, 1]
7899
7900
The number of trees on the first few vertex counts.
7901
This is sequence A000055 in Sloane's OEIS. ::
7902
7903
sage: [len(list(graphs.trees(i))) for i in range(0, 15)]
7904
[1, 1, 1, 1, 2, 3, 6, 11, 23, 47, 106, 235, 551, 1301, 3159]
7905
"""
7906
from trees import TreeIterator
7907
return iter(TreeIterator(vertices))
7908
7909
def nauty_geng(self, options="", debug=False):
7910
r"""
7911
Returns a generator which creates graphs from nauty's geng program.
7912
7913
.. note::
7914
7915
Due to license restrictions, the nauty package is distributed
7916
as a Sage optional package. At a system command line, execute
7917
``sage -i nauty`` to see the nauty license and install the
7918
package.
7919
7920
INPUT:
7921
7922
- ``options`` - a string passed to geng as if it was run at
7923
a system command line. At a minimum, you *must* pass the
7924
number of vertices you desire. Sage expects the graphs to be
7925
in nauty's "graph6" format, do not set an option to change
7926
this default or results will be unpredictable.
7927
7928
- ``debug`` - default: ``False`` - if ``True`` the first line of
7929
geng's output to standard error is captured and the first call
7930
to the generator's ``next()`` function will return this line
7931
as a string. A line leading with ">A" indicates a successful
7932
initiation of the program with some information on the arguments,
7933
while a line beginning with ">E" indicates an error with the input.
7934
7935
The possible options, obtained as output of ``geng --help``::
7936
7937
n : the number of vertices
7938
mine:maxe : a range for the number of edges
7939
#:0 means '# or more' except in the case 0:0
7940
res/mod : only generate subset res out of subsets 0..mod-1
7941
7942
-c : only write connected graphs
7943
-C : only write biconnected graphs
7944
-t : only generate triangle-free graphs
7945
-f : only generate 4-cycle-free graphs
7946
-b : only generate bipartite graphs
7947
(-t, -f and -b can be used in any combination)
7948
-m : save memory at the expense of time (only makes a
7949
difference in the absence of -b, -t, -f and n <= 28).
7950
-d# : a lower bound for the minimum degree
7951
-D# : a upper bound for the maximum degree
7952
-v : display counts by number of edges
7953
-l : canonically label output graphs
7954
7955
-q : suppress auxiliary output (except from -v)
7956
7957
Options which cause geng to use an output format different
7958
than the graph6 format are not listed above (-u, -g, -s, -y, -h)
7959
as they will confuse the creation of a Sage graph. The res/mod
7960
option can be useful when using the output in a routine run
7961
several times in parallel.
7962
7963
OUTPUT:
7964
7965
A generator which will produce the graphs as Sage graphs.
7966
These will be simple graphs: no loops, no multiple edges, no
7967
directed edges.
7968
7969
EXAMPLES:
7970
7971
The generator can be used to construct graphs for testing,
7972
one at a time (usually inside a loop). Or it can be used to
7973
create an entire list all at once if there is sufficient memory
7974
to contain it. ::
7975
7976
sage: gen = graphs.nauty_geng("2") # optional nauty
7977
sage: gen.next() # optional nauty
7978
Graph on 2 vertices
7979
sage: gen.next() # optional nauty
7980
Graph on 2 vertices
7981
sage: gen.next() # optional nauty
7982
Traceback (most recent call last):
7983
...
7984
StopIteration: Exhausted list of graphs from nauty geng
7985
7986
A list of all graphs on 7 vertices. This agrees with
7987
Sloane's OEIS sequence A000088. ::
7988
7989
sage: gen = graphs.nauty_geng("7") # optional nauty
7990
sage: len(list(gen)) # optional nauty
7991
1044
7992
7993
A list of just the connected graphs on 7 vertices. This agrees with
7994
Sloane's OEIS sequence A001349. ::
7995
7996
sage: gen = graphs.nauty_geng("7 -c") # optional nauty
7997
sage: len(list(gen)) # optional nauty
7998
853
7999
8000
The ``debug`` switch can be used to examine geng's reaction
8001
to the input in the ``options`` string. We illustrate success.
8002
(A failure will be a string beginning with ">E".) Passing the
8003
"-q" switch to geng will supress the indicator of a
8004
successful initiation. ::
8005
8006
sage: gen = graphs.nauty_geng("4", debug=True) # optional nauty
8007
sage: print gen.next() # optional nauty
8008
>A nauty-geng -d0D3 n=4 e=0-6
8009
"""
8010
import subprocess
8011
from sage.misc.package import is_package_installed
8012
if not is_package_installed("nauty"):
8013
raise TypeError, "the optional nauty package is not installed"
8014
sp = subprocess.Popen("nauty-geng {0}".format(options), shell=True,
8015
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
8016
stderr=subprocess.PIPE, close_fds=True)
8017
if debug:
8018
yield sp.stderr.readline()
8019
gen = sp.stdout
8020
while True:
8021
try:
8022
s = gen.next()
8023
except StopIteration:
8024
raise StopIteration("Exhausted list of graphs from nauty geng")
8025
G = graph.Graph(s[:-1], format='graph6')
8026
yield G
8027
8028
8029
def cospectral_graphs(self, vertices, matrix_function=lambda g: g.adjacency_matrix(), graphs=None):
8030
r"""
8031
Find all sets of graphs on ``vertices`` vertices (with
8032
possible restrictions) which are cospectral with respect to a
8033
constructed matrix.
8034
8035
INPUT:
8036
8037
- ``vertices`` - The number of vertices in the graphs to be tested
8038
8039
- ``matrix_function`` - A function taking a graph and giving back
8040
a matrix. This defaults to the adjacency matrix. The spectra
8041
examined are the spectra of these matrices.
8042
8043
- ``graphs`` - One of three things:
8044
8045
- ``None`` (default) - test all graphs having ``vertices``
8046
vertices
8047
8048
- a function taking a graph and returning ``True`` or ``False``
8049
- test only the graphs on ``vertices`` vertices for which
8050
the function returns ``True``
8051
8052
- a list of graphs (or other iterable object) - these graphs
8053
are tested for cospectral sets. In this case,
8054
``vertices`` is ignored.
8055
8056
OUTPUT:
8057
8058
A list of lists of graphs. Each sublist will be a list of
8059
cospectral graphs (lists of cadinality 1 being omitted).
8060
8061
8062
EXAMPLES::
8063
8064
sage: g=graphs.cospectral_graphs(5)
8065
sage: sorted(sorted(g.graph6_string() for g in glist) for glist in g)
8066
[['Dr?', 'Ds_']]
8067
sage: g[0][1].am().charpoly()==g[0][1].am().charpoly()
8068
True
8069
8070
There are two sets of cospectral graphs on six vertices with no isolated vertices::
8071
8072
sage: g=graphs.cospectral_graphs(6, graphs=lambda x: min(x.degree())>0)
8073
sage: sorted(sorted(g.graph6_string() for g in glist) for glist in g)
8074
[['Ep__', 'Er?G'], ['ExGg', 'ExoG']]
8075
sage: g[0][1].am().charpoly()==g[0][1].am().charpoly()
8076
True
8077
sage: g[1][1].am().charpoly()==g[1][1].am().charpoly()
8078
True
8079
8080
There is one pair of cospectral trees on eight vertices::
8081
8082
sage: g=graphs.cospectral_graphs(6, graphs=graphs.trees(8))
8083
sage: sorted(sorted(g.graph6_string() for g in glist) for glist in g)
8084
[['GiPC?C', 'GiQCC?']]
8085
sage: g[0][1].am().charpoly()==g[0][1].am().charpoly()
8086
True
8087
8088
There are two sets of cospectral graphs (with respect to the
8089
Laplacian matrix) on six vertices::
8090
8091
sage: g=graphs.cospectral_graphs(6, matrix_function=lambda g: g.laplacian_matrix())
8092
sage: sorted(sorted(g.graph6_string() for g in glist) for glist in g)
8093
[['Edq_', 'ErcG'], ['Exoo', 'EzcG']]
8094
sage: g[0][1].laplacian_matrix().charpoly()==g[0][1].laplacian_matrix().charpoly()
8095
True
8096
sage: g[1][1].laplacian_matrix().charpoly()==g[1][1].laplacian_matrix().charpoly()
8097
True
8098
8099
To find cospectral graphs with respect to the normalized
8100
Laplacian, assuming the graphs do not have an isolated vertex, it
8101
is enough to check the spectrum of the matrix `D^{-1}A`, where `D`
8102
is the diagonal matrix of vertex degrees, and A is the adjacency
8103
matrix. We find two such cospectral graphs (for the normalized
8104
Laplacian) on five vertices::
8105
8106
sage: def DinverseA(g):
8107
... A=g.adjacency_matrix().change_ring(QQ)
8108
... for i in range(g.order()):
8109
... A.rescale_row(i, 1/len(A.nonzero_positions_in_row(i)))
8110
... return A
8111
sage: g=graphs.cospectral_graphs(5, matrix_function=DinverseA, graphs=lambda g: min(g.degree())>0)
8112
sage: sorted(sorted(g.graph6_string() for g in glist) for glist in g)
8113
[['Dlg', 'Ds_']]
8114
sage: g[0][1].laplacian_matrix(normalized=True).charpoly()==g[0][1].laplacian_matrix(normalized=True).charpoly()
8115
True
8116
"""
8117
from sage.graphs.all import graphs as graph_gen
8118
if graphs is None:
8119
graph_list=graph_gen(vertices)
8120
elif callable(graphs):
8121
graph_list=iter(g for g in graph_gen(vertices) if graphs(g))
8122
else:
8123
graph_list=iter(graphs)
8124
8125
from collections import defaultdict
8126
charpolys=defaultdict(list)
8127
for g in graph_list:
8128
cp=matrix_function(g).charpoly()
8129
charpolys[cp].append(g)
8130
8131
cospectral_graphs=[]
8132
for cp,g_list in charpolys.items():
8133
if len(g_list)>1:
8134
cospectral_graphs.append(g_list)
8135
8136
return cospectral_graphs
8137
8138
8139
def canaug_traverse_vert(g, aut_gens, max_verts, property, dig=False, loops=False, implementation='c_graph', sparse=True):
8140
"""
8141
Main function for exhaustive generation. Recursive traversal of a
8142
canonically generated tree of isomorph free (di)graphs satisfying a
8143
given property.
8144
8145
INPUT:
8146
8147
8148
- ``g`` - current position on the tree.
8149
8150
- ``aut_gens`` - list of generators of Aut(g), in
8151
list notation.
8152
8153
- ``max_verts`` - when to retreat.
8154
8155
- ``property`` - check before traversing below g.
8156
8157
- ``degree_sequence`` - specify a degree sequence to try to
8158
obtain.
8159
8160
8161
EXAMPLES::
8162
8163
sage: from sage.graphs.graph_generators import canaug_traverse_vert
8164
sage: list(canaug_traverse_vert(Graph(), [], 3, lambda x: True))
8165
[Graph on 0 vertices, ... Graph on 3 vertices]
8166
8167
The best way to access this function is through the graphs()
8168
iterator:
8169
8170
Print graphs on 3 or less vertices.
8171
8172
::
8173
8174
sage: for G in graphs(3, augment='vertices'):
8175
... print G
8176
...
8177
Graph on 0 vertices
8178
Graph on 1 vertex
8179
Graph on 2 vertices
8180
Graph on 3 vertices
8181
Graph on 3 vertices
8182
Graph on 3 vertices
8183
Graph on 2 vertices
8184
Graph on 3 vertices
8185
8186
Print digraphs on 2 or less vertices.
8187
8188
::
8189
8190
sage: for D in digraphs(2, augment='vertices'):
8191
... print D
8192
...
8193
Digraph on 0 vertices
8194
Digraph on 1 vertex
8195
Digraph on 2 vertices
8196
Digraph on 2 vertices
8197
Digraph on 2 vertices
8198
"""
8199
from sage.graphs.generic_graph_pyx import binary
8200
from sage.groups.perm_gps.partn_ref.refinement_graphs import search_tree
8201
8202
8203
if not property(g):
8204
return
8205
yield g
8206
8207
n = g.order()
8208
if n < max_verts:
8209
8210
# build a list representing C(g) - the vertex to be added
8211
# is at the end, so only specify which edges...
8212
# in the case of graphs, there are n possibilities,
8213
# and in the case of digraphs, there are 2*n.
8214
if dig:
8215
possibilities = 2*n
8216
else:
8217
possibilities = n
8218
num_roots = 2**possibilities
8219
children = [-1]*num_roots
8220
8221
# union-find C(g) under Aut(g)
8222
for gen in aut_gens:
8223
for i in xrange(len(children)):
8224
k = 0
8225
for j in xrange(possibilities):
8226
if (1 << j)&i:
8227
if dig and j >= n:
8228
k += (1 << (gen[j-n]+n))
8229
else:
8230
k += (1 << gen[j])
8231
while children[k] != -1:
8232
k = children[k]
8233
while children[i] != -1:
8234
i = children[i]
8235
if i != k:
8236
# union i & k
8237
smaller, larger = sorted([i,k])
8238
children[larger] = smaller
8239
num_roots -= 1
8240
8241
# find representatives of orbits of C(g)
8242
roots = []
8243
found_roots = 0
8244
i = 0
8245
while found_roots < num_roots:
8246
if children[i] == -1:
8247
found_roots += 1
8248
roots.append(i)
8249
i += 1
8250
for i in roots:
8251
# construct a z for each number in roots...
8252
z = g.copy(implementation=implementation, sparse=sparse)
8253
z.add_vertex(n)
8254
edges = []
8255
if dig:
8256
index = 0
8257
while index < possibilities/2:
8258
if (1 << index)&i:
8259
edges.append((index,n))
8260
index += 1
8261
while index < possibilities:
8262
if (1 << index)&i:
8263
edges.append((n,index-n))
8264
index += 1
8265
else:
8266
index = 0
8267
while (1 << index) <= i:
8268
if (1 << index)&i:
8269
edges.append((index,n))
8270
index += 1
8271
z.add_edges(edges)
8272
z_s = []
8273
if property(z):
8274
z_s.append(z)
8275
if loops:
8276
z = z.copy(implementation=implementation, sparse=sparse)
8277
z.add_edge((n,n))
8278
if property(z):
8279
z_s.append(z)
8280
for z in z_s:
8281
z_aut_gens, _, canonical_relabeling = search_tree(z, [z.vertices()], certify=True, dig=(dig or loops))
8282
cut_vert = 0
8283
while canonical_relabeling[cut_vert] != n:
8284
cut_vert += 1
8285
sub_verts = [v for v in z if v != cut_vert]
8286
m_z = z.subgraph(sub_verts)
8287
8288
if m_z == g:
8289
for a in canaug_traverse_vert(z, z_aut_gens, max_verts, property, dig=dig, loops=loops, implementation=implementation, sparse=sparse):
8290
yield a
8291
else:
8292
for possibility in check_aut(z_aut_gens, cut_vert, n):
8293
if m_z.relabel(possibility, inplace=False) == g:
8294
for a in canaug_traverse_vert(z, z_aut_gens, max_verts, property, dig=dig, loops=loops, implementation=implementation, sparse=sparse):
8295
yield a
8296
break
8297
8298
def check_aut(aut_gens, cut_vert, n):
8299
"""
8300
Helper function for exhaustive generation.
8301
8302
At the start, check_aut is given a set of generators for the
8303
automorphism group, aut_gens. We already know we are looking for
8304
an element of the auto- morphism group that sends cut_vert to n,
8305
and check_aut generates these for the canaug_traverse function.
8306
8307
EXAMPLE: Note that the last two entries indicate that none of the
8308
automorphism group has yet been searched - we are starting at the
8309
identity [0, 1, 2, 3] and so far that is all we have seen. We
8310
return automorphisms mapping 2 to 3.
8311
8312
::
8313
8314
sage: from sage.graphs.graph_generators import check_aut
8315
sage: list( check_aut( [ [0, 3, 2, 1], [1, 0, 3, 2], [2, 1, 0, 3] ], 2, 3))
8316
[[1, 0, 3, 2], [1, 2, 3, 0]]
8317
"""
8318
from copy import copy
8319
perm = range(n+1)
8320
seen_perms = [perm]
8321
unchecked_perms = [perm]
8322
while len(unchecked_perms) != 0:
8323
perm = unchecked_perms.pop(0)
8324
for gen in aut_gens:
8325
new_perm = copy(perm)
8326
for i in xrange(len(perm)):
8327
new_perm[i] = gen[perm[i]]
8328
if new_perm not in seen_perms:
8329
seen_perms.append(new_perm)
8330
unchecked_perms.append(new_perm)
8331
if new_perm[cut_vert] == n:
8332
yield new_perm
8333
8334
def canaug_traverse_edge(g, aut_gens, property, dig=False, loops=False, implementation='c_graph', sparse=True):
8335
"""
8336
Main function for exhaustive generation. Recursive traversal of a
8337
canonically generated tree of isomorph free graphs satisfying a
8338
given property.
8339
8340
INPUT:
8341
8342
8343
- ``g`` - current position on the tree.
8344
8345
- ``aut_gens`` - list of generators of Aut(g), in
8346
list notation.
8347
8348
- ``property`` - check before traversing below g.
8349
8350
8351
EXAMPLES::
8352
8353
sage: from sage.graphs.graph_generators import canaug_traverse_edge
8354
sage: G = Graph(3)
8355
sage: list(canaug_traverse_edge(G, [], lambda x: True))
8356
[Graph on 3 vertices, ... Graph on 3 vertices]
8357
8358
The best way to access this function is through the graphs()
8359
iterator:
8360
8361
Print graphs on 3 or less vertices.
8362
8363
::
8364
8365
sage: for G in graphs(3):
8366
... print G
8367
...
8368
Graph on 3 vertices
8369
Graph on 3 vertices
8370
Graph on 3 vertices
8371
Graph on 3 vertices
8372
8373
Print digraphs on 3 or less vertices.
8374
8375
::
8376
8377
sage: for G in digraphs(3):
8378
... print G
8379
...
8380
Digraph on 3 vertices
8381
Digraph on 3 vertices
8382
...
8383
Digraph on 3 vertices
8384
Digraph on 3 vertices
8385
"""
8386
from sage.graphs.generic_graph_pyx import binary
8387
from sage.groups.perm_gps.partn_ref.refinement_graphs import search_tree
8388
if not property(g):
8389
return
8390
yield g
8391
n = g.order()
8392
if dig:
8393
max_size = n*(n-1)
8394
else:
8395
max_size = (n*(n-1))>>1 # >> 1 is just / 2 (this is n choose 2)
8396
if loops: max_size += n
8397
if g.size() < max_size:
8398
# build a list representing C(g) - the edge to be added
8399
# is one of max_size choices
8400
if dig:
8401
children = [[(j,i) for i in xrange(n)] for j in xrange(n)]
8402
else:
8403
children = [[(j,i) for i in xrange(j)] for j in xrange(n)]
8404
# union-find C(g) under Aut(g)
8405
orbits = range(n)
8406
for gen in aut_gens:
8407
for iii in xrange(n):
8408
if orbits[gen[iii]] != orbits[iii]:
8409
temp = orbits[gen[iii]]
8410
for jjj in xrange(n):
8411
if orbits[jjj] == temp:
8412
orbits[jjj] = orbits[iii]
8413
if dig:
8414
jjj_range = range(iii) + range(iii+1, n)
8415
else:
8416
jjj_range = xrange(iii) # iii > jjj
8417
for jjj in jjj_range:
8418
i, j = iii, jjj
8419
if dig:
8420
x, y = gen[i], gen[j]
8421
else:
8422
y, x = sorted([gen[i], gen[j]])
8423
if children[i][j] != children[x][y]:
8424
x_val, y_val = x, y
8425
i_val, j_val = i, j
8426
if dig:
8427
while (x_val, y_val) != children[x_val][y_val]:
8428
x_val, y_val = children[x_val][y_val]
8429
while (i_val, j_val) != children[i_val][j_val]:
8430
i_val, j_val = children[i_val][j_val]
8431
else:
8432
while (x_val, y_val) != children[x_val][y_val]:
8433
y_val, x_val = sorted(children[x_val][y_val])
8434
while (i_val, j_val) != children[i_val][j_val]:
8435
j_val, i_val = sorted(children[i_val][j_val])
8436
while (x, y) != (x_val, y_val):
8437
xx, yy = x, y
8438
x, y = children[x][y]
8439
children[xx][yy] = (x_val, y_val)
8440
while (i, j) != (i_val, j_val):
8441
ii, jj = i, j
8442
i, j = children[i][j]
8443
children[ii][jj] = (i_val, j_val)
8444
if x < i:
8445
children[i][j] = (x, y)
8446
elif x > i:
8447
children[x][y] = (i, j)
8448
elif y < j:
8449
children[i][j] = (x, y)
8450
elif y > j:
8451
children[x][y] = (i, j)
8452
else:
8453
continue
8454
# find representatives of orbits of C(g)
8455
roots = []
8456
for i in range(n):
8457
if dig:
8458
j_range = range(i) + range(i+1, n)
8459
else:
8460
j_range = range(i)
8461
for j in j_range:
8462
if children[i][j] == (i, j):
8463
roots.append((i,j))
8464
if loops:
8465
seen = []
8466
for i in xrange(n):
8467
if orbits[i] not in seen:
8468
roots.append((i,i))
8469
seen.append(orbits[i])
8470
for i, j in roots:
8471
if g.has_edge(i, j):
8472
continue
8473
# construct a z for each edge in roots...
8474
z = g.copy(implementation=implementation, sparse=sparse)
8475
z.add_edge(i, j)
8476
if not property(z):
8477
continue
8478
z_aut_gens, _, canonical_relabeling = search_tree(z, [z.vertices()], certify=True, dig=(dig or loops))
8479
relabel_inverse = [0]*n
8480
for ii in xrange(n):
8481
relabel_inverse[canonical_relabeling[ii]] = ii
8482
z_can = z.relabel(canonical_relabeling, inplace=False)
8483
cut_edge_can = z_can.edges(labels=False, sort=True)[-1]
8484
cut_edge = [relabel_inverse[cut_edge_can[0]], relabel_inverse[cut_edge_can[1]]]
8485
if dig:
8486
cut_edge = tuple(cut_edge)
8487
else:
8488
cut_edge = tuple(sorted(cut_edge))
8489
8490
from copy import copy
8491
m_z = copy(z)
8492
m_z.delete_edge(cut_edge)
8493
if m_z == g:
8494
for a in canaug_traverse_edge(z, z_aut_gens, property, dig=dig, loops=loops, implementation=implementation, sparse=sparse):
8495
yield a
8496
else:
8497
for possibility in check_aut_edge(z_aut_gens, cut_edge, i, j, n, dig=dig):
8498
if m_z.relabel(possibility, inplace=False) == g:
8499
for a in canaug_traverse_edge(z, z_aut_gens, property, dig=dig, loops=loops, implementation=implementation, sparse=sparse):
8500
yield a
8501
break
8502
8503
def check_aut_edge(aut_gens, cut_edge, i, j, n, dig=False):
8504
"""
8505
Helper function for exhaustive generation.
8506
8507
At the start, check_aut_edge is given a set of generators for the
8508
automorphism group, aut_gens. We already know we are looking for
8509
an element of the auto- morphism group that sends cut_edge to {i,
8510
j}, and check_aut generates these for the canaug_traverse
8511
function.
8512
8513
EXAMPLE: Note that the last two entries indicate that none of the
8514
automorphism group has yet been searched - we are starting at the
8515
identity [0, 1, 2, 3] and so far that is all we have seen. We
8516
return automorphisms mapping 2 to 3.
8517
8518
::
8519
8520
sage: from sage.graphs.graph_generators import check_aut
8521
sage: list( check_aut( [ [0, 3, 2, 1], [1, 0, 3, 2], [2, 1, 0, 3] ], 2, 3))
8522
[[1, 0, 3, 2], [1, 2, 3, 0]]
8523
"""
8524
from copy import copy
8525
perm = range(n)
8526
seen_perms = [perm]
8527
unchecked_perms = [perm]
8528
while len(unchecked_perms) != 0:
8529
perm = unchecked_perms.pop(0)
8530
for gen in aut_gens:
8531
new_perm = copy(perm)
8532
for ii in xrange(n):
8533
new_perm[ii] = gen[perm[ii]]
8534
if new_perm not in seen_perms:
8535
seen_perms.append(new_perm)
8536
unchecked_perms.append(new_perm)
8537
if new_perm[cut_edge[0]] == i and new_perm[cut_edge[1]] == j:
8538
yield new_perm
8539
if not dig and new_perm[cut_edge[0]] == j and new_perm[cut_edge[1]] == i:
8540
yield new_perm
8541
8542
8543
# Easy access to the graph generators from the command line:
8544
graphs = GraphGenerators()
8545
8546
8547
####################
8548
# Helper functions #
8549
####################
8550
8551
def _circle_embedding(g, vertices, center=(0, 0), radius=1, shift=0):
8552
r"""
8553
Set some vertices on a circle in the embedding of a graph G.
8554
8555
This method modifies the graph's embedding so that the vertices
8556
listed in ``vertices`` appear in this ordering on a circle of given
8557
radius and center. The ``shift`` parameter is actually a rotation of
8558
the circle. A value of ``shift=1`` will replace in the drawing the
8559
`i`-th element of the list by the `(i-1)`-th. Non-integer values are
8560
admissible, and a value of `\alpha` corresponds to a rotation of the
8561
circle by an angle of `\alpha 2\pi/n` (where `n` is the number of
8562
vertices set on the circle).
8563
8564
EXAMPLE::
8565
8566
sage: from sage.graphs.graph_generators import _circle_embedding
8567
sage: g = graphs.CycleGraph(5)
8568
sage: _circle_embedding(g, [0, 2, 4, 1, 3], radius=2, shift=.5)
8569
sage: g.show()
8570
"""
8571
c_x, c_y = center
8572
n = len(vertices)
8573
d = g.get_pos()
8574
8575
for i,v in enumerate(vertices):
8576
i += shift
8577
v_x = c_x + radius * cos(2*i*pi / n)
8578
v_y = c_y + radius * sin(2*i*pi / n)
8579
d[v] = (v_x, v_y)
8580
8581
g.set_pos(d)
8582
8583
def _line_embedding(g, vertices, first=(0, 0), last=(0, 1)):
8584
r"""
8585
Sets some vertices on a line in the embedding of a graph G.
8586
8587
This method modifies the graph's embedding so that the vertices of
8588
``vertices`` appear on a line, where the position of ``vertices[0]``
8589
is the pair ``first`` and the position of ``vertices[-1]`` is
8590
``last``. The vertices are evenly spaced.
8591
8592
EXAMPLE::
8593
8594
sage: from sage.graphs.graph_generators import _line_embedding
8595
sage: g = graphs.PathGraph(5)
8596
sage: _line_embedding(g, [0, 2, 4, 1, 3], first=(-1, -1), last=(1, 1))
8597
sage: g.show()
8598
"""
8599
n = len(vertices) - 1.
8600
8601
fx, fy = first
8602
dx = (last[0] - first[0])/n
8603
dy = (last[1] - first[1])/n
8604
8605
d = g.get_pos()
8606
for v in vertices:
8607
d[v] = (fx, fy)
8608
fx += dx
8609
fy += dy
8610
8611