Path: blob/master/src/sage/graphs/generators/families.py
8815 views
# -*- coding: utf-8 -*-1r"""2Families of graphs34The methods defined here appear in :mod:`sage.graphs.graph_generators`.56AUTHORS:78- David Coudert (2012) Ringed Trees910"""1112###########################################################################13#14# Copyright (C) 2006 Robert L. Miller <[email protected]>15# and Emily A. Kirkman16# Copyright (C) 2009 Michael C. Yurko <[email protected]>17#18# Distributed under the terms of the GNU General Public License (GPL)19# http://www.gnu.org/licenses/20###########################################################################2122# import from Sage library23from sage.graphs.graph import Graph24from sage.graphs import graph25from math import sin, cos, pi2627def JohnsonGraph(n, k):28r"""29Returns the Johnson graph with parameters `n, k`.3031Johnson graphs are a special class of undirected graphs defined from systems32of sets. The vertices of the Johnson graph `J(n,k)` are the `k`-element33subsets of an `n`-element set; two vertices are adjacent when they meet in a34`(k-1)`-element set. For more information about Johnson graphs, see the35corresponding :wikipedia:`Wikipedia page <Johnson_graph>`.3637EXAMPLES:3839The Johnson graph is a Hamiltonian graph. ::4041sage: g = graphs.JohnsonGraph(7, 3)42sage: g.is_hamiltonian()43True4445Every Johnson graph is vertex transitive. ::4647sage: g = graphs.JohnsonGraph(6, 4)48sage: g.is_vertex_transitive()49True5051The complement of the Johnson graph `J(n,2)` is isomorphic to the Knesser52Graph `K(n,2)`. In paritcular the complement of `J(5,2)` is isomorphic to53the Petersen graph. ::5455sage: g = graphs.JohnsonGraph(5,2)56sage: g.complement().is_isomorphic(graphs.PetersenGraph())57True58"""5960g = Graph(name="Johnson graph with parameters "+str(n)+","+str(k))61from sage.combinat.subset import Set, Subsets6263S = Set(range(n))64g.add_vertices(Subsets(S, k))6566for sub in Subsets(S, k-1):67elem_left = S - sub68for i in elem_left:69for j in elem_left:70if j <= i:71continue72g.add_edge(sub+Set([i]),sub+Set([j]))7374return g757677def KneserGraph(n,k):78r"""79Returns the Kneser Graph with parameters `n, k`.8081The Kneser Graph with parameters `n,k` is the graph82whose vertices are the `k`-subsets of `[0,1,\dots,n-1]`, and such83that two vertices are adjacent if their corresponding sets84are disjoint.8586For example, the Petersen Graph can be defined87as the Kneser Graph with parameters `5,2`.8889EXAMPLE::9091sage: KG=graphs.KneserGraph(5,2)92sage: print KG.vertices()93[{4, 5}, {1, 3}, {2, 5}, {2, 3}, {3, 4}, {3, 5}, {1, 4}, {1, 5}, {1, 2}, {2, 4}]94sage: P=graphs.PetersenGraph()95sage: P.is_isomorphic(KG)96True9798TESTS::99100sage: KG=graphs.KneserGraph(0,0)101Traceback (most recent call last):102...103ValueError: Parameter n should be a strictly positive integer104sage: KG=graphs.KneserGraph(5,6)105Traceback (most recent call last):106...107ValueError: Parameter k should be a strictly positive integer inferior to n108"""109110if not n>0:111raise ValueError, "Parameter n should be a strictly positive integer"112if not (k>0 and k<=n):113raise ValueError, "Parameter k should be a strictly positive integer inferior to n"114115g = Graph(name="Kneser graph with parameters "+str(n)+","+str(k))116from sage.combinat.subset import Subsets117118if k>n/2:119g.add_vertices(Subsets(n,k).list())120121S = Subsets(n,k)122for s in S:123for t in Subsets(S.s.difference(s),k):124g.add_edge(s,t)125126return g127128def BalancedTree(r, h):129r"""130Returns the perfectly balanced tree of height `h \geq 1`,131whose root has degree `r \geq 2`.132133The number of vertices of this graph is134`1 + r + r^2 + \cdots + r^h`, that is,135`\frac{r^{h+1} - 1}{r - 1}`. The number of edges is one136less than the number of vertices.137138INPUT:139140- ``r`` -- positive integer `\geq 2`. The degree of the root node.141142- ``h`` -- positive integer `\geq 1`. The height of the balanced tree.143144OUTPUT:145146The perfectly balanced tree of height `h \geq 1` and whose root has147degree `r \geq 2`. A ``NetworkXError`` is returned if `r < 2` or148`h < 1`.149150ALGORITHM:151152Uses `NetworkX <http://networkx.lanl.gov>`_.153154EXAMPLES:155156A balanced tree whose root node has degree `r = 2`, and of height157`h = 1`, has order 3 and size 2::158159sage: G = graphs.BalancedTree(2, 1); G160Balanced tree: Graph on 3 vertices161sage: G.order(); G.size()16231632164sage: r = 2; h = 1165sage: v = 1 + r166sage: v; v - 116731682169170Plot a balanced tree of height 5, whose root node has degree `r = 3`::171172sage: G = graphs.BalancedTree(3, 5)173sage: G.show() # long time174175A tree is bipartite. If its vertex set is finite, then it is planar. ::176177sage: r = randint(2, 5); h = randint(1, 7)178sage: T = graphs.BalancedTree(r, h)179sage: T.is_bipartite()180True181sage: T.is_planar()182True183sage: v = (r^(h + 1) - 1) / (r - 1)184sage: T.order() == v185True186sage: T.size() == v - 1187True188189TESTS:190191Normally we would only consider balanced trees whose root node192has degree `r \geq 2`, but the construction degenerates193gracefully::194195sage: graphs.BalancedTree(1, 10)196Balanced tree: Graph on 2 vertices197198sage: graphs.BalancedTree(-1, 10)199Balanced tree: Graph on 1 vertex200201Similarly, we usually want the tree must have height `h \geq 1`202but the algorithm also degenerates gracefully here::203204sage: graphs.BalancedTree(3, 0)205Balanced tree: Graph on 1 vertex206207sage: graphs.BalancedTree(5, -2)208Balanced tree: Graph on 0 vertices209210sage: graphs.BalancedTree(-2,-2)211Balanced tree: Graph on 0 vertices212"""213import networkx214return Graph(networkx.balanced_tree(r, h), name="Balanced tree")215216def BarbellGraph(n1, n2):217r"""218Returns a barbell graph with ``2*n1 + n2`` nodes. The argument ``n1``219must be greater than or equal to 2.220221A barbell graph is a basic structure that consists of a path graph222of order ``n2`` connecting two complete graphs of order ``n1`` each.223224This constructor depends on `NetworkX <http://networkx.lanl.gov>`_225numeric labels. In this case, the ``n1``-th node connects to the226path graph from one complete graph and the ``n1 + n2 + 1``-th node227connects to the path graph from the other complete graph.228229INPUT:230231- ``n1`` -- integer `\geq 2`. The order of each of the two232complete graphs.233234- ``n2`` -- nonnegative integer. The order of the path graph235connecting the two complete graphs.236237OUTPUT:238239A barbell graph of order ``2*n1 + n2``. A ``ValueError`` is240returned if ``n1 < 2`` or ``n2 < 0``.241242ALGORITHM:243244Uses `NetworkX <http://networkx.lanl.gov>`_.245246PLOTTING:247248Upon construction, the position dictionary is filled to249override the spring-layout algorithm. By convention, each barbell250graph will be displayed with the two complete graphs in the251lower-left and upper-right corners, with the path graph connecting252diagonally between the two. Thus the ``n1``-th node will be drawn at a25345 degree angle from the horizontal right center of the first254complete graph, and the ``n1 + n2 + 1``-th node will be drawn 45255degrees below the left horizontal center of the second complete graph.256257EXAMPLES:258259Construct and show a barbell graph ``Bar = 4``, ``Bells = 9``::260261sage: g = graphs.BarbellGraph(9, 4); g262Barbell graph: Graph on 22 vertices263sage: g.show() # long time264265An ``n1 >= 2``, ``n2 >= 0`` barbell graph has order ``2*n1 + n2``. It266has the complete graph on ``n1`` vertices as a subgraph. It also has267the path graph on ``n2`` vertices as a subgraph. ::268269sage: n1 = randint(2, 2*10^2)270sage: n2 = randint(0, 2*10^2)271sage: g = graphs.BarbellGraph(n1, n2)272sage: v = 2*n1 + n2273sage: g.order() == v274True275sage: K_n1 = graphs.CompleteGraph(n1)276sage: P_n2 = graphs.PathGraph(n2)277sage: s_K = g.subgraph_search(K_n1, induced=True)278sage: s_P = g.subgraph_search(P_n2, induced=True)279sage: K_n1.is_isomorphic(s_K)280True281sage: P_n2.is_isomorphic(s_P)282True283284Create several barbell graphs in a Sage graphics array::285286sage: g = []287sage: j = []288sage: for i in range(6):289... k = graphs.BarbellGraph(i + 2, 4)290... g.append(k)291...292sage: for i in range(2):293... n = []294... for m in range(3):295... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))296... j.append(n)297...298sage: G = sage.plot.graphics.GraphicsArray(j)299sage: G.show() # long time300301TESTS:302303The input ``n1`` must be `\geq 2`::304305sage: graphs.BarbellGraph(1, randint(0, 10^6))306Traceback (most recent call last):307...308ValueError: Invalid graph description, n1 should be >= 2309sage: graphs.BarbellGraph(randint(-10^6, 1), randint(0, 10^6))310Traceback (most recent call last):311...312ValueError: Invalid graph description, n1 should be >= 2313314The input ``n2`` must be `\geq 0`::315316sage: graphs.BarbellGraph(randint(2, 10^6), -1)317Traceback (most recent call last):318...319ValueError: Invalid graph description, n2 should be >= 0320sage: graphs.BarbellGraph(randint(2, 10^6), randint(-10^6, -1))321Traceback (most recent call last):322...323ValueError: Invalid graph description, n2 should be >= 0324sage: graphs.BarbellGraph(randint(-10^6, 1), randint(-10^6, -1))325Traceback (most recent call last):326...327ValueError: Invalid graph description, n1 should be >= 2328"""329# sanity checks330if n1 < 2:331raise ValueError("Invalid graph description, n1 should be >= 2")332if n2 < 0:333raise ValueError("Invalid graph description, n2 should be >= 0")334335pos_dict = {}336337for i in range(n1):338x = float(cos((pi / 4) - ((2 * pi) / n1) * i) - (n2 / 2) - 1)339y = float(sin((pi / 4) - ((2 * pi) / n1) * i) - (n2 / 2) - 1)340j = n1 - 1 - i341pos_dict[j] = (x, y)342for i in range(n1, n1 + n2):343x = float(i - n1 - (n2 / 2) + 1)344y = float(i - n1 - (n2 / 2) + 1)345pos_dict[i] = (x, y)346for i in range(n1 + n2, (2 * n1) + n2):347x = float(348cos((5 * (pi / 4)) + ((2 * pi) / n1) * (i - n1 - n2))349+ (n2 / 2) + 2)350y = float(351sin((5 * (pi / 4)) + ((2 * pi) / n1) * (i - n1 - n2))352+ (n2 / 2) + 2)353pos_dict[i] = (x, y)354355import networkx356G = networkx.barbell_graph(n1, n2)357return Graph(G, pos=pos_dict, name="Barbell graph")358359def BubbleSortGraph(n):360r"""361Returns the bubble sort graph `B(n)`.362363The vertices of the bubble sort graph are the set of permutations on364`n` symbols. Two vertices are adjacent if one can be obtained from the365other by swapping the labels in the `i`-th and `(i+1)`-th positions for366`1 \leq i \leq n-1`. In total, `B(n)` has order `n!`. Thus, the order367of `B(n)` increases according to `f(n) = n!`.368369INPUT:370371- ``n`` -- positive integer. The number of symbols to permute.372373OUTPUT:374375The bubble sort graph `B(n)` on `n` symbols. If `n < 1`, a376``ValueError`` is returned.377378EXAMPLES::379380sage: g = graphs.BubbleSortGraph(4); g381Bubble sort: Graph on 24 vertices382sage: g.plot() # long time383384The bubble sort graph on `n = 1` symbol is the trivial graph `K_1`::385386sage: graphs.BubbleSortGraph(1)387Bubble sort: Graph on 1 vertex388389If `n \geq 1`, then the order of `B(n)` is `n!`::390391sage: n = randint(1, 8)392sage: g = graphs.BubbleSortGraph(n)393sage: g.order() == factorial(n)394True395396TESTS:397398Input ``n`` must be positive::399400sage: graphs.BubbleSortGraph(0)401Traceback (most recent call last):402...403ValueError: Invalid number of symbols to permute, n should be >= 1404sage: graphs.BubbleSortGraph(randint(-10^6, 0))405Traceback (most recent call last):406...407ValueError: Invalid number of symbols to permute, n should be >= 1408409AUTHORS:410411- Michael Yurko (2009-09-01)412"""413# sanity checks414if n < 1:415raise ValueError(416"Invalid number of symbols to permute, n should be >= 1")417if n == 1:418from sage.graphs.generators.basic import CompleteGraph419return Graph(CompleteGraph(n), name="Bubble sort")420from sage.combinat.permutation import Permutations421#create set from which to permute422label_set = [str(i) for i in xrange(1, n + 1)]423d = {}424#iterate through all vertices425for v in Permutations(label_set):426v = list(v) # So we can easily mutate it427tmp_dict = {}428#add all adjacencies429for i in xrange(n - 1):430#swap entries431v[i], v[i + 1] = v[i + 1], v[i]432#add new vertex433new_vert = ''.join(v)434tmp_dict[new_vert] = None435#swap back436v[i], v[i + 1] = v[i + 1], v[i]437#add adjacency dict438d[''.join(v)] = tmp_dict439return Graph(d, name="Bubble sort")440441def CirculantGraph(n, adjacency):442r"""443Returns a circulant graph with n nodes.444445A circulant graph has the property that the vertex `i` is connected446with the vertices `i+j` and `i-j` for each j in adj.447448INPUT:449450451- ``n`` - number of vertices in the graph452453- ``adjacency`` - the list of j values454455456PLOTTING: Upon construction, the position dictionary is filled to457override the spring-layout algorithm. By convention, each circulant458graph will be displayed with the first (0) node at the top, with459the rest following in a counterclockwise manner.460461Filling the position dictionary in advance adds O(n) to the462constructor.463464.. SEEALSO::465466* :meth:`sage.graphs.generic_graph.GenericGraph.is_circulant`467-- checks whether a (di)graph is circulant, and/or returns468all possible sets of parameters.469470EXAMPLES: Compare plotting using the predefined layout and471networkx::472473sage: import networkx474sage: n = networkx.cycle_graph(23)475sage: spring23 = Graph(n)476sage: posdict23 = graphs.CirculantGraph(23,2)477sage: spring23.show() # long time478sage: posdict23.show() # long time479480We next view many cycle graphs as a Sage graphics array. First we481use the ``CirculantGraph`` constructor, which fills in482the position dictionary::483484sage: g = []485sage: j = []486sage: for i in range(9):487... k = graphs.CirculantGraph(i+3,i)488... g.append(k)489...490sage: for i in range(3):491... n = []492... for m in range(3):493... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))494... j.append(n)495...496sage: G = sage.plot.graphics.GraphicsArray(j)497sage: G.show() # long time498499Compare to plotting with the spring-layout algorithm::500501sage: g = []502sage: j = []503sage: for i in range(9):504... spr = networkx.cycle_graph(i+3)505... k = Graph(spr)506... g.append(k)507...508sage: for i in range(3):509... n = []510... for m in range(3):511... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))512... j.append(n)513...514sage: G = sage.plot.graphics.GraphicsArray(j)515sage: G.show() # long time516517Passing a 1 into adjacency should give the cycle.518519::520521sage: graphs.CirculantGraph(6,1)==graphs.CycleGraph(6)522True523sage: graphs.CirculantGraph(7,[1,3]).edges(labels=false)524[(0, 1),525(0, 3),526(0, 4),527(0, 6),528(1, 2),529(1, 4),530(1, 5),531(2, 3),532(2, 5),533(2, 6),534(3, 4),535(3, 6),536(4, 5),537(5, 6)]538"""539from sage.graphs.graph_plot import _circle_embedding540541if not isinstance(adjacency,list):542adjacency=[adjacency]543544G = Graph(n, name="Circulant graph ("+str(adjacency)+")")545_circle_embedding(G, range(n))546547for v in G:548G.add_edges([(v,(v+j)%n) for j in adjacency])549550return G551552def CubeGraph(n):553r"""554Returns the hypercube in `n` dimensions.555556The hypercube in `n` dimension is build upon the binary557strings on `n` bits, two of them being adjacent if558they differ in exactly one bit. Hence, the distance559between two vertices in the hypercube is the Hamming560distance.561562EXAMPLES:563564The distance between `0100110` and `1011010` is565`5`, as expected ::566567sage: g = graphs.CubeGraph(7)568sage: g.distance('0100110','1011010')5695570571Plot several `n`-cubes in a Sage Graphics Array ::572573sage: g = []574sage: j = []575sage: for i in range(6):576... k = graphs.CubeGraph(i+1)577... g.append(k)578...579sage: for i in range(2):580... n = []581... for m in range(3):582... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))583... j.append(n)584...585sage: G = sage.plot.graphics.GraphicsArray(j)586sage: G.show(figsize=[6,4]) # long time587588Use the plot options to display larger `n`-cubes589590::591592sage: g = graphs.CubeGraph(9)593sage: g.show(figsize=[12,12],vertex_labels=False, vertex_size=20) # long time594595AUTHORS:596597- Robert Miller598"""599theta = float(pi/n)600601d = {'':[]}602dn={}603p = {'':(float(0),float(0))}604pn={}605606# construct recursively the adjacency dict and the positions607for i in xrange(n):608ci = float(cos(i*theta))609si = float(sin(i*theta))610for v,e in d.iteritems():611v0 = v+'0'612v1 = v+'1'613l0 = [v1]614l1 = [v0]615for m in e:616l0.append(m+'0')617l1.append(m+'1')618dn[v0] = l0619dn[v1] = l1620x,y = p[v]621pn[v0] = (x, y)622pn[v1] = (x+ci, y+si)623d,dn = dn,{}624p,pn = pn,{}625626# construct the graph627r = Graph(name="%d-Cube"%n)628r.add_vertices(d.keys())629for u,L in d.iteritems():630for v in L:631r.add_edge(u,v)632r.set_pos(p)633634return r635636def DorogovtsevGoltsevMendesGraph(n):637"""638Construct the n-th generation of the Dorogovtsev-Goltsev-Mendes639graph.640641EXAMPLE::642643sage: G = graphs.DorogovtsevGoltsevMendesGraph(8)644sage: G.size()6456561646647REFERENCE:648649- [1] Dorogovtsev, S. N., Goltsev, A. V., and Mendes, J.650F. F., Pseudofractal scale-free web, Phys. Rev. E 066122651(2002).652"""653import networkx654return Graph(networkx.dorogovtsev_goltsev_mendes_graph(n),\655name="Dorogovtsev-Goltsev-Mendes Graph, %d-th generation"%n)656657def FoldedCubeGraph(n):658r"""659Returns the folded cube graph of order `2^{n-1}`.660661The folded cube graph on `2^{n-1}` vertices can be obtained from a cube662graph on `2^n` vertices by merging together opposed663vertices. Alternatively, it can be obtained from a cube graph on664`2^{n-1}` vertices by adding an edge between opposed vertices. This665second construction is the one produced by this method.666667For more information on folded cube graphs, see the corresponding668:wikipedia:`Wikipedia page <Folded_cube_graph>`.669670EXAMPLES:671672The folded cube graph of order five is the Clebsch graph::673674sage: fc = graphs.FoldedCubeGraph(5)675sage: clebsch = graphs.ClebschGraph()676sage: fc.is_isomorphic(clebsch)677True678"""679680if n < 1:681raise ValueError("The value of n must be at least 2")682683g = CubeGraph(n-1)684g.name("Folded Cube Graph")685686# Complementing the binary word687def complement(x):688x = x.replace('0','a')689x = x.replace('1','0')690x = x.replace('a','1')691return x692693for x in g:694if x[0] == '0':695g.add_edge(x,complement(x))696697return g698699700def FriendshipGraph(n):701r"""702Returns the friendship graph `F_n`.703704The friendship graph is also known as the Dutch windmill graph. Let705`C_3` be the cycle graph on 3 vertices. Then `F_n` is constructed by706joining `n \geq 1` copies of `C_3` at a common vertex. If `n = 1`,707then `F_1` is isomorphic to `C_3` (the triangle graph). If `n = 2`,708then `F_2` is the butterfly graph, otherwise known as the bowtie709graph. For more information, see this710`Wikipedia article on the friendship graph <http://en.wikipedia.org/wiki/Friendship_graph>`_.711712INPUT:713714- ``n`` -- positive integer; the number of copies of `C_3` to use in715constructing `F_n`.716717OUTPUT:718719- The friendship graph `F_n` obtained from `n` copies of the cycle720graph `C_3`.721722.. seealso::723724- :meth:`GraphGenerators.ButterflyGraph`725726EXAMPLES:727728The first few friendship graphs. ::729730sage: A = []; B = []731sage: for i in range(9):732... g = graphs.FriendshipGraph(i + 1)733... A.append(g)734sage: for i in range(3):735... n = []736... for j in range(3):737... n.append(A[3*i + j].plot(vertex_size=20, vertex_labels=False))738... B.append(n)739sage: G = sage.plot.graphics.GraphicsArray(B)740sage: G.show() # long time741742For `n = 1`, the friendship graph `F_1` is isomorphic to the cycle743graph `C_3`, whose visual representation is a triangle. ::744745sage: G = graphs.FriendshipGraph(1); G746Friendship graph: Graph on 3 vertices747sage: G.show() # long time748sage: G.is_isomorphic(graphs.CycleGraph(3))749True750751For `n = 2`, the friendship graph `F_2` is isomorphic to the752butterfly graph, otherwise known as the bowtie graph. ::753754sage: G = graphs.FriendshipGraph(2); G755Friendship graph: Graph on 5 vertices756sage: G.is_isomorphic(graphs.ButterflyGraph())757True758759If `n \geq 1`, then the friendship graph `F_n` has `2n + 1` vertices760and `3n` edges. It has radius 1, diameter 2, girth 3, and761chromatic number 3. Furthermore, `F_n` is planar and Eulerian. ::762763sage: n = randint(1, 10^3)764sage: G = graphs.FriendshipGraph(n)765sage: G.order() == 2*n + 1766True767sage: G.size() == 3*n768True769sage: G.radius()7701771sage: G.diameter()7722773sage: G.girth()7743775sage: G.chromatic_number()7763777sage: G.is_planar()778True779sage: G.is_eulerian()780True781782TESTS:783784The input ``n`` must be a positive integer. ::785786sage: graphs.FriendshipGraph(randint(-10^5, 0))787Traceback (most recent call last):788...789ValueError: n must be a positive integer790"""791# sanity checks792if n < 1:793raise ValueError("n must be a positive integer")794# construct the friendship graph795if n == 1:796from sage.graphs.generators.basic import CycleGraph797G = CycleGraph(3)798G.name("Friendship graph")799return G800# build the edge and position dictionaries801from sage.functions.trig import cos, sin802from sage.rings.real_mpfr import RR803from sage.symbolic.constants import pi804N = 2*n + 1 # order of F_n805d = (2*pi) / (N - 1) # angle between external nodes806edge_dict = {}807pos_dict = {}808for i in range(N - 2):809if i & 1: # odd numbered node810edge_dict.setdefault(i, [i + 1, N - 1])811else: # even numbered node812edge_dict.setdefault(i, [N - 1])813pos_dict.setdefault(i, [RR(cos(i*d)), RR(sin(i*d))])814edge_dict.setdefault(N - 2, [0, N - 1])815pos_dict.setdefault(N - 2, [RR(cos(d * (N-2))), RR(sin(d * (N-2)))])816pos_dict.setdefault(N - 1, [0, 0])817return Graph(edge_dict, pos=pos_dict, name="Friendship graph")818819def FuzzyBallGraph(partition, q):820r"""821Construct a Fuzzy Ball graph with the integer partition822``partition`` and ``q`` extra vertices.823824Let `q` be an integer and let `m_1,m_2,...,m_k` be a set of positive825integers. Let `n=q+m_1+...+m_k`. The Fuzzy Ball graph with partition826`m_1,m_2,...,m_k` and `q` extra vertices is the graph constructed from the827graph `G=K_n` by attaching, for each `i=1,2,...,k`, a new vertex `a_i` to828`m_i` distinct vertices of `G`.829830For given positive integers `k` and `m` and nonnegative831integer `q`, the set of graphs ``FuzzyBallGraph(p, q)`` for832all partitions `p` of `m` with `k` parts are cospectral with833respect to the normalized Laplacian.834835EXAMPLES::836837sage: graphs.FuzzyBallGraph([3,1],2).adjacency_matrix()838[0 1 1 1 1 1 1 0]839[1 0 1 1 1 1 1 0]840[1 1 0 1 1 1 1 0]841[1 1 1 0 1 1 0 1]842[1 1 1 1 0 1 0 0]843[1 1 1 1 1 0 0 0]844[1 1 1 0 0 0 0 0]845[0 0 0 1 0 0 0 0]846847848Pick positive integers `m` and `k` and a nonnegative integer `q`.849All the FuzzyBallGraphs constructed from partitions of `m` with850`k` parts should be cospectral with respect to the normalized851Laplacian::852853sage: m=4; q=2; k=2854sage: g_list=[graphs.FuzzyBallGraph(p,q) for p in Partitions(m, length=k)]855sage: set([g.laplacian_matrix(normalized=True).charpoly() for g in g_list]) # long time (7s on sage.math, 2011)856set([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])857"""858from sage.graphs.generators.basic import CompleteGraph859if len(partition)<1:860raise ValueError, "partition must be a nonempty list of positive integers"861n=q+sum(partition)862g=CompleteGraph(n)863curr_vertex=0864for e,p in enumerate(partition):865g.add_edges([(curr_vertex+i, 'a{0}'.format(e+1)) for i in range(p)])866curr_vertex+=p867return g868869def FibonacciTree(n):870r"""871Returns the graph of the Fibonacci Tree `F_{i}` of order `n`.872`F_{i}` is recursively defined as the a tree with a root vertex873and two attached child trees `F_{i-1}` and `F_{i-2}`, where874`F_{1}` is just one vertex and `F_{0}` is empty.875876INPUT:877878- ``n`` - the recursion depth of the Fibonacci Tree879880EXAMPLES::881882sage: g = graphs.FibonacciTree(3)883sage: g.is_tree()884True885886::887888sage: l1 = [ len(graphs.FibonacciTree(_)) + 1 for _ in range(6) ]889sage: l2 = list(fibonacci_sequence(2,8))890sage: l1 == l2891True892893AUTHORS:894895- Harald Schilly and Yann Laigle-Chapuy (2010-03-25)896"""897T = Graph(name="Fibonacci-Tree-%d"%n)898if n == 1: T.add_vertex(0)899if n < 2: return T900901from sage.combinat.combinat import fibonacci_sequence902F = list(fibonacci_sequence(n + 2))903s = 1.618 ** (n / 1.618 - 1.618)904pos = {}905906def fib(level, node, y):907pos[node] = (node, y)908if level < 2: return909level -= 1910y -= s911diff = F[level]912T.add_edge(node, node - diff)913if level == 1: # only one child914pos[node - diff] = (node, y)915return916T.add_edge(node, node + diff)917fib(level, node - diff, y)918fib(level - 1, node + diff, y)919920T.add_vertices(xrange(sum(F[:-1])))921fib(n, F[n + 1] - 1, 0)922T.set_pos(pos)923924return T925926def GeneralizedPetersenGraph(n,k):927r"""928Returns a generalized Petersen graph with `2n` nodes. The variables929`n`, `k` are integers such that `n>2` and `0<k\leq\lfloor(n-1)`/`2\rfloor`930931For `k=1` the result is a graph isomorphic to the circular ladder graph932with the same `n`. The regular Petersen Graph has `n=5` and `k=2`.933Other named graphs that can be described using this notation include934the Desargues graph and the Moebius-Kantor graph.935936INPUT:937938- ``n`` - the number of nodes is `2*n`.939940- ``k`` - integer `0<k\leq\lfloor(n-1)`/`2\rfloor`. Decides941how inner vertices are connected.942943PLOTTING: Upon construction, the position dictionary is filled to944override the spring-layout algorithm. By convention, the generalized945Petersen graphs are displayed as an inner and outer cycle pair, with946the first n nodes drawn on the outer circle. The first (0) node is947drawn at the top of the outer-circle, moving counterclockwise after that.948The inner circle is drawn with the (n)th node at the top, then949counterclockwise as well.950951EXAMPLES: For `k=1` the resulting graph will be isomorphic to a circular952ladder graph. ::953954sage: g = graphs.GeneralizedPetersenGraph(13,1)955sage: g2 = graphs.CircularLadderGraph(13)956sage: g.is_isomorphic(g2)957True958959The Desargues graph::960961sage: g = graphs.GeneralizedPetersenGraph(10,3)962sage: g.girth()9636964sage: g.is_bipartite()965True966967AUTHORS:968969- Anders Jonsson (2009-10-15)970"""971if (n < 3):972raise ValueError("n must be larger than 2")973if (k < 1 or k>((n-1)/2)):974raise ValueError("k must be in 1<= k <=floor((n-1)/2)")975pos_dict = {}976G = Graph()977for i in range(n):978x = float(cos((pi/2) + ((2*pi)/n)*i))979y = float(sin((pi/2) + ((2*pi)/n)*i))980pos_dict[i] = (x,y)981for i in range(n, 2*n):982x = float(0.5*cos((pi/2) + ((2*pi)/n)*i))983y = float(0.5*sin((pi/2) + ((2*pi)/n)*i))984pos_dict[i] = (x,y)985for i in range(n):986G.add_edge(i, (i+1) % n)987G.add_edge(i, i+n)988G.add_edge(i+n, n + (i+k) % n)989return Graph(G, pos=pos_dict, name="Generalized Petersen graph (n="+str(n)+",k="+str(k)+")")990991def HararyGraph( k, n ):992r"""993Returns the Harary graph on `n` vertices and connectivity `k`, where994`2 \leq k < n`.995996A `k`-connected graph `G` on `n` vertices requires the minimum degree997`\delta(G)\geq k`, so the minimum number of edges `G` should have is998`\lceil kn/2\rceil`. Harary graphs achieve this lower bound, that is,999Harary graphs are minimal `k`-connected graphs on `n` vertices.10001001The construction provided uses the method CirculantGraph. For more1002details, see the book D. B. West, Introduction to Graph Theory, 2nd1003Edition, Prentice Hall, 2001, p. 150--151; or the `MathWorld article on1004Harary graphs <http://mathworld.wolfram.com/HararyGraph.html>`_.10051006EXAMPLES:10071008Harary graphs `H_{k,n}`::10091010sage: h = graphs.HararyGraph(5,9); h1011Harary graph 5, 9: Graph on 9 vertices1012sage: h.order()101391014sage: h.size()1015231016sage: h.vertex_connectivity()1017510181019TESTS:10201021Connectivity of some Harary graphs::10221023sage: n=101024sage: for k in range(2,n):1025... g = graphs.HararyGraph(k,n)1026... if k != g.vertex_connectivity():1027... print "Connectivity of Harary graphs not satisfied."1028"""1029if k < 2:1030raise ValueError("Connectivity parameter k should be at least 2.")1031if k >= n:1032raise ValueError("Number of vertices n should be greater than k.")10331034if k%2 == 0:1035G = CirculantGraph( n, range(1,k/2+1) )1036else:1037if n%2 == 0:1038G = CirculantGraph( n, range(1,(k-1)/2+1) )1039for i in range(n):1040G.add_edge( i, (i+n/2)%n )1041else:1042G = HararyGraph( k-1, n )1043for i in range((n-1)/2+1):1044G.add_edge( i, (i+(n-1)/2)%n )1045G.name('Harary graph {0}, {1}'.format(k,n))1046return G10471048def HyperStarGraph(n,k):1049r"""1050Returns the hyper-star graph HS(n,k).10511052The vertices of the hyper-star graph are the set of binary strings1053of length n which contain k 1s. Two vertices, u and v, are adjacent1054only if u can be obtained from v by swapping the first bit with a1055different symbol in another position.10561057INPUT:10581059- ``n``10601061- ``k``10621063EXAMPLES::10641065sage: g = graphs.HyperStarGraph(6,3)1066sage: g.plot() # long time10671068REFERENCES:10691070- Lee, Hyeong-Ok, Jong-Seok Kim, Eunseuk Oh, and Hyeong-Seok Lim.1071"Hyper-Star Graph: A New Interconnection Network Improving the1072Network Cost of the Hypercube." In Proceedings of the First EurAsian1073Conference on Information and Communication Technology, 858-865.1074Springer-Verlag, 2002.10751076AUTHORS:10771078- Michael Yurko (2009-09-01)1079"""1080from sage.combinat.combination import Combinations1081# dictionary associating the positions of the 1s to the corresponding1082# string: e.g. if n=6 and k=3, comb_to_str([0,1,4])=='110010'1083comb_to_str={}1084for c in Combinations(n,k):1085L = ['0']*n1086for i in c:1087L[i]='1'1088comb_to_str[tuple(c)] = ''.join(L)10891090g = Graph(name="HS(%d,%d)"%(n,k))1091g.add_vertices(comb_to_str.values())10921093for c in Combinations(range(1,n),k): # 0 is not in c1094L = []1095u = comb_to_str[tuple(c)]1096# switch 0 with the 1s1097for i in xrange(len(c)):1098v = tuple([0]+c[:i]+c[i+1:])1099g.add_edge( u , comb_to_str[v] )11001101return g11021103def LCFGraph(n, shift_list, repeats):1104"""1105Returns the cubic graph specified in LCF notation.11061107LCF (Lederberg-Coxeter-Fruchte) notation is a concise way of1108describing cubic Hamiltonian graphs. The way a graph is constructed1109is as follows. Since there is a Hamiltonian cycle, we first create1110a cycle on n nodes. The variable shift_list = [s_0, s_1, ...,1111s_k-1] describes edges to be created by the following scheme: for1112each i, connect vertex i to vertex (i + s_i). Then, repeats1113specifies the number of times to repeat this process, where on the1114jth repeat we connect vertex (i + j\*len(shift_list)) to vertex (1115i + j\*len(shift_list) + s_i).11161117INPUT:111811191120- ``n`` - the number of nodes.11211122- ``shift_list`` - a list of integer shifts mod n.11231124- ``repeats`` - the number of times to repeat the1125process.112611271128EXAMPLES::11291130sage: G = graphs.LCFGraph(4, [2,-2], 2)1131sage: G.is_isomorphic(graphs.TetrahedralGraph())1132True11331134::11351136sage: G = graphs.LCFGraph(20, [10,7,4,-4,-7,10,-4,7,-7,4], 2)1137sage: G.is_isomorphic(graphs.DodecahedralGraph())1138True11391140::11411142sage: G = graphs.LCFGraph(14, [5,-5], 7)1143sage: G.is_isomorphic(graphs.HeawoodGraph())1144True11451146The largest cubic nonplanar graph of diameter three::11471148sage: 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)1149sage: G.degree()1150[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]1151sage: G.diameter()115231153sage: G.show() # long time11541155PLOTTING: LCF Graphs are plotted as an n-cycle with edges in the1156middle, as described above.11571158REFERENCES:11591160- [1] Frucht, R. "A Canonical Representation of Trivalent1161Hamiltonian Graphs." J. Graph Th. 1, 45-60, 1976.11621163- [2] Grunbaum, B. Convex Polytope es. New York: Wiley,1164pp. 362-364, 1967.11651166- [3] Lederberg, J. 'DENDRAL-64: A System for Computer1167Construction, Enumeration and Notation of Organic Molecules1168as Tree Structures and Cyclic Graphs. Part II. Topology of1169Cyclic Graphs.' Interim Report to the National Aeronautics1170and Space Administration. Grant NsG 81-60. December 15,11711965. http://profiles.nlm.nih.gov/BB/A/B/I/U/_/bbabiu.pdf.1172"""1173import networkx1174pos_dict = {}1175for i in range(n):1176x = float(cos(pi/2 + ((2*pi)/n)*i))1177y = float(sin(pi/2 + ((2*pi)/n)*i))1178pos_dict[i] = [x,y]1179return Graph(networkx.LCF_graph(n, shift_list, repeats),\1180pos=pos_dict, name="LCF Graph")11811182def MycielskiGraph(k=1, relabel=True):1183r"""1184Returns the `k`-th Mycielski Graph.11851186The graph `M_k` is triangle-free and has chromatic number1187equal to `k`. These graphs show, constructively, that there1188are triangle-free graphs with arbitrarily high chromatic1189number.11901191The Mycielski graphs are built recursively starting with1192`M_0`, an empty graph; `M_1`, a single vertex graph; and `M_2`1193is the graph `K_2`. `M_{k+1}` is then built from `M_k`1194as follows:11951196If the vertices of `M_k` are `v_1,\ldots,v_n`, then the1197vertices of `M_{k+1}` are1198`v_1,\ldots,v_n,w_1,\ldots,w_n,z`. Vertices `v_1,\ldots,v_n`1199induce a copy of `M_k`. Vertices `w_1,\ldots,w_n` are an1200independent set. Vertex `z` is adjacent to all the1201`w_i`-vertices. Finally, vertex `w_i` is adjacent to vertex1202`v_j` iff `v_i` is adjacent to `v_j`.12031204INPUT:12051206- ``k`` Number of steps in the construction process.12071208- ``relabel`` Relabel the vertices so their names are the integers1209``range(n)`` where ``n`` is the number of vertices in the graph.12101211EXAMPLE:12121213The Mycielski graph `M_k` is triangle-free and has chromatic1214number equal to `k`. ::12151216sage: g = graphs.MycielskiGraph(5)1217sage: g.is_triangle_free()1218True1219sage: g.chromatic_number()1220512211222The graphs `M_4` is (isomorphic to) the Grotzsch graph. ::12231224sage: g = graphs.MycielskiGraph(4)1225sage: g.is_isomorphic(graphs.GrotzschGraph())1226True12271228REFERENCES:12291230- [1] Weisstein, Eric W. "Mycielski Graph."1231From MathWorld--A Wolfram Web Resource.1232http://mathworld.wolfram.com/MycielskiGraph.html12331234"""1235g = Graph()1236g.name("Mycielski Graph " + str(k))12371238if k<0:1239raise ValueError, "parameter k must be a nonnegative integer"12401241if k == 0:1242return g12431244if k == 1:1245g.add_vertex(0)1246return g12471248if k == 2:1249g.add_edge(0,1)1250return g12511252g0 = MycielskiGraph(k-1)1253g = MycielskiStep(g0)1254g.name("Mycielski Graph " + str(k))1255if relabel: g.relabel()12561257return g12581259def MycielskiStep(g):1260r"""1261Perform one iteration of the Mycielski construction.12621263See the documentation for ``MycielskiGraph`` which uses this1264method. We expose it to all users in case they may find it1265useful.12661267EXAMPLE. One iteration of the Mycielski step applied to the12685-cycle yields a graph isomorphic to the Grotzsch graph ::12691270sage: g = graphs.CycleGraph(5)1271sage: h = graphs.MycielskiStep(g)1272sage: h.is_isomorphic(graphs.GrotzschGraph())1273True1274"""12751276# Make a copy of the input graph g1277gg = g.copy()12781279# rename a vertex v of gg as (1,v)1280renamer = dict( [ (v, (1,v)) for v in g.vertices() ] )1281gg.relabel(renamer)12821283# add the w vertices to gg as (2,v)1284wlist = [ (2,v) for v in g.vertices() ]1285gg.add_vertices(wlist)12861287# add the z vertex as (0,0)1288gg.add_vertex((0,0))12891290# add the edges from z to w_i1291gg.add_edges( [ ( (0,0) , (2,v) ) for v in g.vertices() ] )12921293# make the v_i w_j edges1294for v in g.vertices():1295gg.add_edges( [ ((1,v),(2,vv)) for vv in g.neighbors(v) ] )12961297return gg12981299def NKStarGraph(n,k):1300r"""1301Returns the (n,k)-star graph.13021303The vertices of the (n,k)-star graph are the set of all arrangements of1304n symbols into labels of length k. There are two adjacency rules for1305the (n,k)-star graph. First, two vertices are adjacent if one can be1306obtained from the other by swapping the first symbol with another1307symbol. Second, two vertices are adjacent if one can be obtained from1308the other by swapping the first symbol with an external symbol (a1309symbol not used in the original label).13101311INPUT:13121313- ``n``13141315- ``k``13161317EXAMPLES::13181319sage: g = graphs.NKStarGraph(4,2)1320sage: g.plot() # long time13211322REFERENCES:13231324- Wei-Kuo, Chiang, and Chen Rong-Jaye. "The (n, k)-star graph: A1325generalized star graph." Information Processing Letters 56,1326no. 5 (December 8, 1995): 259-264.13271328AUTHORS:13291330- Michael Yurko (2009-09-01)1331"""1332from sage.combinat.permutation import Arrangements1333#set from which to permute1334set = [str(i) for i in xrange(1,n+1)]1335#create dict1336d = {}1337for v in Arrangements(set,k):1338v = list(v) # So we can easily mutate it1339tmp_dict = {}1340#add edges of dimension i1341for i in xrange(1,k):1342#swap 0th and ith element1343v[0], v[i] = v[i], v[0]1344#convert to str and add to list1345vert = "".join(v)1346tmp_dict[vert] = None1347#swap back1348v[0], v[i] = v[i], v[0]1349#add other edges1350tmp_bit = v[0]1351for i in set:1352#check if external1353if not (i in v):1354v[0] = i1355#add edge1356vert = "".join(v)1357tmp_dict[vert] = None1358v[0] = tmp_bit1359d["".join(v)] = tmp_dict1360return Graph(d, name="(%d,%d)-star"%(n,k))13611362def NStarGraph(n):1363r"""1364Returns the n-star graph.13651366The vertices of the n-star graph are the set of permutations on n1367symbols. There is an edge between two vertices if their labels differ1368only in the first and one other position.13691370INPUT:13711372- ``n``13731374EXAMPLES::13751376sage: g = graphs.NStarGraph(4)1377sage: g.plot() # long time13781379REFERENCES:13801381- S.B. Akers, D. Horel and B. Krishnamurthy, The star graph: An1382attractive alternative to the previous n-cube. In: Proc. Internat.1383Conf. on Parallel Processing (1987), pp. 393--400.13841385AUTHORS:13861387- Michael Yurko (2009-09-01)1388"""1389from sage.combinat.permutation import Permutations1390#set from which to permute1391set = [str(i) for i in xrange(1,n+1)]1392#create dictionary of lists1393#vertices are adjacent if the first element1394#is swapped with the ith element1395d = {}1396for v in Permutations(set):1397v = list(v) # So we can easily mutate it1398tmp_dict = {}1399for i in xrange(1,n):1400if v[0] != v[i]:1401#swap 0th and ith element1402v[0], v[i] = v[i], v[0]1403#convert to str and add to list1404vert = "".join(v)1405tmp_dict[vert] = None1406#swap back1407v[0], v[i] = v[i], v[0]1408d["".join(v)] = tmp_dict1409return Graph(d, name = "%d-star"%n)14101411def OddGraph(n):1412r"""1413Returns the Odd Graph with parameter `n`.14141415The Odd Graph with parameter `n` is defined as the1416Kneser Graph with parameters `2n-1,n-1`.1417Equivalently, the Odd Graph is the graph whose vertices1418are the `n-1`-subsets of `[0,1,\dots,2(n-1)]`, and such1419that two vertices are adjacent if their corresponding sets1420are disjoint.14211422For example, the Petersen Graph can be defined1423as the Odd Graph with parameter `3`.14241425EXAMPLE::14261427sage: OG=graphs.OddGraph(3)1428sage: print OG.vertices()1429[{4, 5}, {1, 3}, {2, 5}, {2, 3}, {3, 4}, {3, 5}, {1, 4}, {1, 5}, {1, 2}, {2, 4}]1430sage: P=graphs.PetersenGraph()1431sage: P.is_isomorphic(OG)1432True14331434TESTS::14351436sage: KG=graphs.OddGraph(1)1437Traceback (most recent call last):1438...1439ValueError: Parameter n should be an integer strictly greater than 11440"""14411442if not n>1:1443raise ValueError, "Parameter n should be an integer strictly greater than 1"1444g = KneserGraph(2*n-1,n-1)1445g.name("Odd Graph with parameter %s" % n)1446return g14471448def PaleyGraph(q):1449r"""1450Paley graph with `q` vertices14511452Parameter `q` must be the power of a prime number and congruent1453to 1 mod 4.14541455EXAMPLES::14561457sage: G=graphs.PaleyGraph(9);G1458Paley graph with parameter 9: Graph on 9 vertices1459sage: G.is_regular()1460True14611462A Paley graph is always self-complementary::14631464sage: G.complement().is_isomorphic(G)1465True1466"""1467from sage.rings.finite_rings.integer_mod import mod1468from sage.rings.finite_rings.constructor import FiniteField1469assert q.is_prime_power(), "Parameter q must be a prime power"1470assert mod(q,4)==1, "Parameter q must be congruent to 1 mod 4"1471g = Graph([FiniteField(q,'a'), lambda i,j: (i-j).is_square()],1472loops=False, name = "Paley graph with parameter %d"%q)1473return g14741475def HanoiTowerGraph(pegs, disks, labels=True, positions=True):1476r"""1477Returns the graph whose vertices are the states of the1478Tower of Hanoi puzzle, with edges representing legal moves between states.14791480INPUT:14811482- ``pegs`` - the number of pegs in the puzzle, 2 or greater1483- ``disks`` - the number of disks in the puzzle, 1 or greater1484- ``labels`` - default: ``True``, if ``True`` the graph contains1485more meaningful labels, see explanation below. For large instances,1486turn off labels for much faster creation of the graph.1487- ``positions`` - default: ``True``, if ``True`` the graph contains1488layout information. This creates a planar layout for the case1489of three pegs. For large instances, turn off layout information1490for much faster creation of the graph.14911492OUTPUT:14931494The Tower of Hanoi puzzle has a certain number of identical pegs1495and a certain number of disks, each of a different radius.1496Initially the disks are all on a single peg, arranged1497in order of their radii, with the largest on the bottom.14981499The goal of the puzzle is to move the disks to any other peg,1500arranged in the same order. The one constraint is that the1501disks resident on any one peg must always be arranged with larger1502radii lower down.15031504The vertices of this graph represent all the possible states1505of this puzzle. Each state of the puzzle is a tuple with length1506equal to the number of disks, ordered by largest disk first.1507The entry of the tuple is the peg where that disk resides.1508Since disks on a given peg must go down in size as we go1509up the peg, this totally describes the state of the puzzle.15101511For example ``(2,0,0)`` means the large disk is on peg 2, the1512medium disk is on peg 0, and the small disk is on peg 01513(and we know the small disk must be above the medium disk).1514We encode these tuples as integers with a base equal to1515the number of pegs, and low-order digits to the right.15161517Two vertices are adjacent if we can change the puzzle from1518one state to the other by moving a single disk. For example,1519``(2,0,0)`` is adjacent to ``(2,0,1)`` since we can move1520the small disk off peg 0 and onto (the empty) peg 1.1521So the solution to a 3-disk puzzle (with at least1522two pegs) can be expressed by the shortest path between1523``(0,0,0)`` and ``(1,1,1)``. For more on this representation1524of the graph, or its properties, see [ARETT-DOREE]_.15251526For greatest speed we create graphs with integer vertices,1527where we encode the tuples as integers with a base equal1528to the number of pegs, and low-order digits to the right.1529So for example, in a 3-peg puzzle with 5 disks, the1530state ``(1,2,0,1,1)`` is encoded as1531`1\ast 3^4 + 2\ast 3^3 + 0\ast 3^2 + 1\ast 3^1 + 1\ast 3^0 = 139`.15321533For smaller graphs, the labels that are the tuples are informative,1534but slow down creation of the graph. Likewise computing layout1535information also incurs a significant speed penalty. For maximum1536speed, turn off labels and layout and decode the1537vertices explicitly as needed. The1538:meth:`sage.rings.integer.Integer.digits`1539with the ``padsto`` option is a quick way to do this, though you1540may want to reverse the list that is output.15411542PLOTTING:15431544The layout computed when ``positions = True`` will1545look especially good for the three-peg case, when the graph is known1546to be planar. Except for two small cases on 4 pegs, the graph is1547otherwise not planar, and likely there is a better way to layout1548the vertices.15491550EXAMPLES:15511552A classic puzzle uses 3 pegs. We solve the 5 disk puzzle using1553integer labels and report the minimum number of moves required.1554Note that `3^5-1` is the state where all 5 disks1555are on peg 2. ::15561557sage: H = graphs.HanoiTowerGraph(3, 5, labels=False, positions=False)1558sage: H.distance(0, 3^5-1)15593115601561A slightly larger instance. ::15621563sage: H = graphs.HanoiTowerGraph(4, 6, labels=False, positions=False)1564sage: H.num_verts()156540961566sage: H.distance(0, 4^6-1)15671715681569For a small graph, labels and layout information can be useful.1570Here we explicitly list a solution as a list of states. ::15711572sage: H = graphs.HanoiTowerGraph(3, 3, labels=True, positions=True)1573sage: H.shortest_path((0,0,0), (1,1,1))1574[(0, 0, 0), (0, 0, 1), (0, 2, 1), (0, 2, 2), (1, 2, 2), (1, 2, 0), (1, 1, 0), (1, 1, 1)]15751576Some facts about this graph with `p` pegs and `d` disks:15771578- only automorphisms are the "obvious" ones - renumber the pegs.1579- chromatic number is less than or equal to `p`1580- independence number is `p^{d-1}`15811582::15831584sage: H = graphs.HanoiTowerGraph(3,4,labels=False,positions=False)1585sage: H.automorphism_group().is_isomorphic(SymmetricGroup(3))1586True1587sage: H.chromatic_number()158831589sage: len(H.independent_set()) == 3^(4-1)1590True15911592TESTS:15931594It is an error to have just one peg (or less). ::15951596sage: graphs.HanoiTowerGraph(1, 5)1597Traceback (most recent call last):1598...1599ValueError: Pegs for Tower of Hanoi graph should be two or greater (not 1)16001601It is an error to have zero disks (or less). ::16021603sage: graphs.HanoiTowerGraph(2, 0)1604Traceback (most recent call last):1605...1606ValueError: Disks for Tower of Hanoi graph should be one or greater (not 0)16071608.. rubric:: Citations16091610.. [ARETT-DOREE] Arett, Danielle and Doree, Suzanne1611"Coloring and counting on the Hanoi graphs"1612Mathematics Magazine, Volume 83, Number 3, June 2010, pages 200-9161316141615AUTHOR:16161617- Rob Beezer, (2009-12-26), with assistance from Su Doree16181619"""16201621# sanitize input1622from sage.rings.all import Integer1623pegs = Integer(pegs)1624if pegs < 2:1625raise ValueError("Pegs for Tower of Hanoi graph should be two or greater (not %d)" % pegs)1626disks = Integer(disks)1627if disks < 1:1628raise ValueError("Disks for Tower of Hanoi graph should be one or greater (not %d)" % disks)16291630# Each state of the puzzle is a tuple with length1631# equal to the number of disks, ordered by largest disk first1632# The entry of the tuple is the peg where that disk resides1633# Since disks on a given peg must go down in size as we go1634# up the peg, this totally describes the puzzle1635# We encode these tuples as integers with a base equal to1636# the number of pegs, and low-order digits to the right16371638# complete graph on number of pegs when just a single disk1639edges = [[i,j] for i in range(pegs) for j in range(i+1,pegs)]16401641nverts = 11642for d in range(2, disks+1):1643prevedges = edges # remember subgraph to build from1644nverts = pegs*nverts # pegs^(d-1)1645edges = []16461647# Take an edge, change its two states in the same way by adding1648# a large disk to the bottom of the same peg in each state1649# This is accomplished by adding a multiple of pegs^(d-1)1650for p in range(pegs):1651largedisk = p*nverts1652for anedge in prevedges:1653edges.append([anedge[0]+largedisk, anedge[1]+largedisk])16541655# Two new states may only differ in the large disk1656# being the only disk on two different pegs, thus1657# otherwise being a common state with one less disk1658# We construct all such pairs of new states and add as edges1659from sage.combinat.subset import Subsets1660for state in range(nverts):1661emptypegs = range(pegs)1662reduced_state = state1663for i in range(d-1):1664apeg = reduced_state % pegs1665if apeg in emptypegs:1666emptypegs.remove(apeg)1667reduced_state = reduced_state//pegs1668for freea, freeb in Subsets(emptypegs, 2):1669edges.append([freea*nverts+state,freeb*nverts+state])16701671H = Graph({}, loops=False, multiedges=False)1672H.add_edges(edges)167316741675# Making labels and/or computing positions can take a long time,1676# relative to just constructing the edges on integer vertices.1677# We try to minimize coercion overhead, but need Sage1678# Integers in order to use digits() for labels.1679# Getting the digits with custom code was no faster.1680# Layouts are circular (symmetric on the number of pegs)1681# radiating outward to the number of disks (radius)1682# Algorithm uses some combination of alternate1683# clockwise/counterclockwise placements, which1684# works well for three pegs (planar layout)1685#1686from sage.functions.trig import sin, cos, csc1687if labels or positions:1688mapping = {}1689pos = {}1690a = Integer(-1)1691one = Integer(1)1692if positions:1693radius_multiplier = 1 + csc(pi/pegs)1694sine = []; cosine = []1695for i in range(pegs):1696angle = 2*i*pi/float(pegs)1697sine.append(sin(angle))1698cosine.append(cos(angle))1699for i in range(pegs**disks):1700a += one1701state = a.digits(base=pegs, padto=disks)1702if labels:1703state.reverse()1704mapping[i] = tuple(state)1705state.reverse()1706if positions:1707locx = 0.0; locy = 0.01708radius = 1.01709parity = -1.01710for index in range(disks):1711p = state[index]1712radius *= radius_multiplier1713parity *= -1.01714locx_temp = cosine[p]*locx - parity*sine[p]*locy + radius*cosine[p]1715locy_temp = parity*sine[p]*locx + cosine[p]*locy - radius*parity*sine[p]1716locx = locx_temp1717locy = locy_temp1718pos[i] = (locx,locy)1719# set positions, then relabel (not vice versa)1720if positions:1721H.set_pos(pos)1722if labels:1723H.relabel(mapping)17241725return H17261727def line_graph_forbidden_subgraphs():1728r"""1729Returns the 9 forbidden subgraphs of a line graph.17301731`Wikipedia article on the line graphs1732<http://en.wikipedia.org/wiki/Line_graph>`_17331734The graphs are returned in the ordering given by the Wikipedia1735drawing, read from left to right and from top to bottom.17361737EXAMPLE::17381739sage: graphs.line_graph_forbidden_subgraphs()1740[Claw graph: Graph on 4 vertices,1741Graph on 6 vertices,1742Graph on 6 vertices,1743Graph on 5 vertices,1744Graph on 6 vertices,1745Graph on 6 vertices,1746Graph on 6 vertices,1747Graph on 6 vertices,1748Graph on 5 vertices]17491750"""1751from sage.graphs.all import Graph1752from sage.graphs.generators.basic import ClawGraph1753graphs = [ClawGraph()]17541755graphs.append(Graph({17560: [1, 2, 3],17571: [2, 3],17584: [2],17595: [3]1760}))17611762graphs.append(Graph({17630: [1, 2, 3, 4],17641: [2, 3, 4],17653: [4],17662: [5]1767}))17681769graphs.append(Graph({17700: [1, 2, 3],17711: [2, 3],17724: [2, 3]1773}))17741775graphs.append(Graph({17760: [1, 2, 3],17771: [2, 3],17784: [2],17795: [3, 4]1780}))17811782graphs.append(Graph({17830: [1, 2, 3, 4],17841: [2, 3, 4],17853: [4],17865: [2, 0, 1]1787}))17881789graphs.append(Graph({17905: [0, 1, 2, 3, 4],17910: [1, 4],17922: [1, 3],17933: [4]1794}))17951796graphs.append(Graph({17971: [0, 2, 3, 4],17983: [0, 4],17992: [4, 5],18004: [5]1801}))18021803graphs.append(Graph({18040: [1, 2, 3],18051: [2, 3, 4],18062: [3, 4],18073: [4]1808}))18091810return graphs18111812def WheelGraph(n):1813"""1814Returns a Wheel graph with n nodes.18151816A Wheel graph is a basic structure where one node is connected to1817all other nodes and those (outer) nodes are connected cyclically.18181819This constructor depends on NetworkX numeric labels.18201821PLOTTING: Upon construction, the position dictionary is filled to1822override the spring-layout algorithm. By convention, each wheel1823graph will be displayed with the first (0) node in the center, the1824second node at the top, and the rest following in a1825counterclockwise manner.18261827With the wheel graph, we see that it doesn't take a very large n at1828all for the spring-layout to give a counter-intuitive display. (See1829Graphics Array examples below).18301831EXAMPLES: We view many wheel graphs with a Sage Graphics Array,1832first with this constructor (i.e., the position dictionary1833filled)::18341835sage: g = []1836sage: j = []1837sage: for i in range(9):1838... k = graphs.WheelGraph(i+3)1839... g.append(k)1840...1841sage: for i in range(3):1842... n = []1843... for m in range(3):1844... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))1845... j.append(n)1846...1847sage: G = sage.plot.graphics.GraphicsArray(j)1848sage: G.show() # long time18491850Next, using the spring-layout algorithm::18511852sage: import networkx1853sage: g = []1854sage: j = []1855sage: for i in range(9):1856... spr = networkx.wheel_graph(i+3)1857... k = Graph(spr)1858... g.append(k)1859...1860sage: for i in range(3):1861... n = []1862... for m in range(3):1863... n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False))1864... j.append(n)1865...1866sage: G = sage.plot.graphics.GraphicsArray(j)1867sage: G.show() # long time18681869Compare the plotting::18701871sage: n = networkx.wheel_graph(23)1872sage: spring23 = Graph(n)1873sage: posdict23 = graphs.WheelGraph(23)1874sage: spring23.show() # long time1875sage: posdict23.show() # long time1876"""1877pos_dict = {}1878pos_dict[0] = (0,0)1879for i in range(1,n):1880x = float(cos((pi/2) + ((2*pi)/(n-1))*(i-1)))1881y = float(sin((pi/2) + ((2*pi)/(n-1))*(i-1)))1882pos_dict[i] = (x,y)1883import networkx1884G = networkx.wheel_graph(n)1885return Graph(G, pos=pos_dict, name="Wheel graph")18861887def trees(vertices):1888r"""1889Returns a generator of the distinct trees on a fixed number of vertices.18901891INPUT:18921893- ``vertices`` - the size of the trees created.18941895OUTPUT:18961897A generator which creates an exhaustive, duplicate-free listing1898of the connected free (unlabeled) trees with ``vertices`` number1899of vertices. A tree is a graph with no cycles.19001901ALGORITHM:19021903Uses an algorithm that generates each new tree1904in constant time. See the documentation for, and implementation1905of, the :mod:`sage.graphs.trees` module, including a citation.19061907EXAMPLES:19081909We create an iterator, then loop over its elements. ::19101911sage: tree_iterator = graphs.trees(7)1912sage: for T in tree_iterator:1913... print T.degree_sequence()1914[2, 2, 2, 2, 2, 1, 1]1915[3, 2, 2, 2, 1, 1, 1]1916[3, 2, 2, 2, 1, 1, 1]1917[4, 2, 2, 1, 1, 1, 1]1918[3, 3, 2, 1, 1, 1, 1]1919[3, 3, 2, 1, 1, 1, 1]1920[4, 3, 1, 1, 1, 1, 1]1921[3, 2, 2, 2, 1, 1, 1]1922[4, 2, 2, 1, 1, 1, 1]1923[5, 2, 1, 1, 1, 1, 1]1924[6, 1, 1, 1, 1, 1, 1]19251926The number of trees on the first few vertex counts.1927This is sequence A000055 in Sloane's OEIS. ::19281929sage: [len(list(graphs.trees(i))) for i in range(0, 15)]1930[1, 1, 1, 1, 2, 3, 6, 11, 23, 47, 106, 235, 551, 1301, 3159]1931"""1932from sage.graphs.trees import TreeIterator1933return iter(TreeIterator(vertices))19341935def RingedTree(k, vertex_labels = True):1936r"""1937Return the ringed tree on k-levels.19381939A ringed tree of level `k` is a binary tree with `k` levels (counting1940the root as a level), in which all vertices at the same level are connected1941by a ring.19421943More precisely, in each layer of the binary tree (i.e. a layer is the set of1944vertices `[2^i...2^{i+1}-1]`) two vertices `u,v` are adjacent if `u=v+1` or1945if `u=2^i` and `v=`2^{i+1}-1`.19461947Ringed trees are defined in [CFHM12]_.19481949INPUT:19501951- ``k`` -- the number of levels of the ringed tree.19521953- ``vertex_labels`` (boolean) -- whether to label vertices as binary words1954(default) or as integers.19551956EXAMPLE::19571958sage: G = graphs.RingedTree(5)1959sage: P = G.plot(vertex_labels=False, vertex_size=10)1960sage: P.show() # long time1961sage: G.vertices()1962['', '0', '00', '000', '0000', '0001', '001', '0010', '0011', '01',1963'010', '0100', '0101', '011', '0110', '0111', '1', '10', '100',1964'1000', '1001', '101', '1010', '1011', '11', '110', '1100', '1101',1965'111', '1110', '1111']19661967TEST::19681969sage: G = graphs.RingedTree(-1)1970Traceback (most recent call last):1971...1972ValueError: The number of levels must be >= 1.1973sage: G = graphs.RingedTree(5, vertex_labels = False)1974sage: G.vertices()1975[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,197618, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]19771978REFERENCES:19791980.. [CFHM12] On the Hyperbolicity of Small-World and Tree-Like Random Graphs1981Wei Chen, Wenjie Fang, Guangda Hu, Michael W. Mahoney1982http://arxiv.org/abs/1201.17171983"""1984if k<1:1985raise ValueError('The number of levels must be >= 1.')19861987from sage.graphs.graph_plot import _circle_embedding19881989# Creating the Balanced tree, which contains most edges already1990g = BalancedTree(2,k-1)1991g.name('Ringed Tree on '+str(k)+' levels')19921993# We consider edges layer by layer1994for i in range(1,k):1995vertices = range(2**(i)-1,2**(i+1)-1)19961997# Add the missing edges1998g.add_cycle(vertices)19992000# And set the vertices' positions2001radius = i if i <= 1 else 1.5**i2002shift = -2**(i-2)+.5 if i > 1 else 02003_circle_embedding(g, vertices, radius = radius, shift = shift)20042005# Specific position for the central vertex2006g.get_pos()[0] = (0,0.2)20072008# Relabel vertices as binary words2009if not vertex_labels:2010return g20112012vertices = ['']2013for i in range(k-1):2014for j in range(2**(i)-1,2**(i+1)-1):2015v = vertices[j]2016vertices.append(v+'0')2017vertices.append(v+'1')20182019g.relabel(vertices)20202021return g20222023def SymplecticGraph(d,q):2024r"""2025Returns the Symplectic graph `Sp(d,q)`20262027The Symplectic Graph `Sp(d,q)` is built from a projective space of dimension2028`d-1` over a field `F_q`, and a symplectic form `f`. Two vertices `u,v` are2029made adjacent if `f(u,v)=0`.20302031See the `page on symplectic graphs on Andries Brouwer's website2032<http://www.win.tue.nl/~aeb/graphs/Sp.html>`_.20332034INPUT:20352036- ``d,q`` (integers) -- note that only even values of `d` are accepted by2037the function.20382039EXAMPLES::20402041sage: g = graphs.SymplecticGraph(6,2)2042sage: g.is_strongly_regular(parameters=True)2043(63, 30, 13, 15)2044sage: set(g.spectrum()) == {-5, 3, 30}2045True2046"""2047from sage.rings.finite_rings.constructor import FiniteField2048from sage.modules.free_module import VectorSpace2049from sage.schemes.projective.projective_space import ProjectiveSpace2050from sage.matrix.constructor import identity_matrix, block_matrix, zero_matrix20512052if d < 1 or d%2 != 0:2053raise ValueError("d must be even and greater than 2")20542055F = FiniteField(q,"x")2056M = block_matrix(F, 2, 2,2057[zero_matrix(F,d/2),2058identity_matrix(F,d/2),2059-identity_matrix(F,d/2),2060zero_matrix(F,d/2)])20612062V = VectorSpace(F,d)2063PV = list(ProjectiveSpace(d-1,F))2064G = Graph([map(tuple,PV), lambda x,y:V(x)*(M*V(y)) == 0], loops = False)2065G.name("Symplectic Graph Sp("+str(d)+","+str(q)+")")2066G.relabel()2067return G2068206920702071