@@ -21639,15 +21639,16 @@ def layout_graphviz(self, dim=2, prog='dot', **options):
2163921639
2164021640 return {key_to_vertex[key]: pos for key, pos in positions.items()}
2164121641
21642- def layout_tutte(self, external_face, external_face_pos=None, **options):
21642+ def layout_tutte(self, external_face=None , external_face_pos=None, **options):
2164321643 r"""
2164421644 Compute graph layout based on a Tutte embedding.
2164521645
2164621646 The graph must be 3-connected and planar.
2164721647
2164821648 INPUT:
2164921649
21650- - ``external_face`` -- list; the external face to be made a polygon
21650+ - ``external_face`` -- list (default: ``None``); the external face to
21651+ be made a polygon
2165121652
2165221653 - ``external_face_pos`` -- dictionary (default: ``None``); the positions
2165321654 of the vertices of the external face. If ``None``, will automatically
@@ -21680,42 +21681,42 @@ def layout_tutte(self, external_face, external_face_pos=None, **options):
2168021681 from sage.matrix.constructor import zero_matrix
2168121682 from sage.rings.real_mpfr import RR
2168221683
21683- if len(external_face) < 3:
21684- raise ValueError("External face must have at least 3 vertices")
21684+ if (external_face is not None) and ( len(external_face) < 3) :
21685+ raise ValueError("external face must have at least 3 vertices")
2168521686
21686- if ( not self.is_planar() ):
21687- raise ValueError("Graph must be planar")
21687+ if not self.is_planar():
21688+ raise ValueError("graph must be planar")
2168821689
21689- C = self.subgraph(vertices=external_face)
21690- if (not C.is_cycle(directed_cycle=False)):
21691- raise ValueError("External face must be a cycle")
21692- external_face_ordered = C.depth_first_search(start=external_face[0], ignore_direction=False)
21690+ if not self.vertex_connectivity(k=3):
21691+ raise ValueError("graph must be 3-connected")
2169321692
21694- from sage.graphs.connectivity import vertex_connectivity
21695- if (not vertex_connectivity(self, k=3)):
21696- raise ValueError("Graph must be 3-connected")
21693+ if external_face is None:
21694+ from sage.graphs.graph import Graph
21695+ H = Graph(self) # take a (undirected) copy H of the graph
21696+ u, v = next(H.edge_iterator(labels=False)) # take any edge (u, v) of H
21697+ H.delete_edge(u, v) # remove edge (u, v) from H
21698+ external_face = H.shortest_path(v, u)
21699+ # Compute a shortest path from v to u in H minus (u, v)H = G.
21700+ # Cycle existence is guaranteed since G is 3-connected.
21701+ else:
21702+ C = self.subgraph(vertices=external_face)
21703+ if (not C.is_cycle(directed_cycle=False)):
21704+ raise ValueError("external face must be a cycle")
21705+ external_face = C.depth_first_search(start=external_face[0], ignore_direction=False)
2169721706
21698- from math import sin, cos, pi
2169921707 pos = dict()
21700-
2170121708 if external_face_pos is None:
21702- external_face_length = len(external_face)
21703- a0 = pi/external_face_length + pi/2
21704- for i, vertex in enumerate(external_face_ordered):
21705- ai = a0 + pi*2*i/external_face_length
21706- pos[vertex] = (cos(ai), sin(ai))
21709+ pos = self._circle_embedding(external_face, return_dict=True)
2170721710 else:
2170821711 for v, p in external_face_pos.items():
2170921712 pos[v] = p
2171021713
21711- V = self.vertices()
21712- n = len(V)
21714+ n = self.order()
2171321715 M = zero_matrix(RR, n, n)
2171421716 b = zero_matrix(RR, n, 2)
2171521717
21716- vertices_to_indices = {v:I for I, v in enumerate(V)}
21717- for i in range(n):
21718- v = V[i]
21718+ vertices_to_indices = {v: i for i, v in enumerate(self)}
21719+ for i, v in enumerate(self):
2171921720 if v in pos:
2172021721 M[i, i] = 1
2172121722 b[i, 0] = pos[v][0]
@@ -21728,7 +21729,7 @@ def layout_tutte(self, external_face, external_face_pos=None, **options):
2172821729 M[i, i] = len(nv)
2172921730
2173021731 sol = M.pseudoinverse()*b
21731- return {V[i]:sol[i] for i in range(n)}
21732+ return dict(zip(self, sol))
2173221733
2173321734 def _layout_bounding_box(self, pos):
2173421735 """
0 commit comments