Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/graphs/planarity.pyx
4079 views
1
"""
2
Wrapper for Boyer's (C) planarity algorithm.
3
"""
4
5
cdef extern from "planarity/graph.h":
6
ctypedef struct graphNode:
7
int v
8
int link[2]
9
ctypedef graphNode * graphNodeP
10
11
ctypedef struct BM_graph:
12
graphNodeP G
13
int N
14
ctypedef BM_graph * graphP
15
16
cdef int OK, EMBEDFLAGS_PLANAR, NONEMBEDDABLE, NOTOK
17
18
cdef graphP gp_New()
19
cdef void gp_Free(graphP *pGraph)
20
cdef int gp_InitGraph(graphP theGraph, int N)
21
cdef int gp_AddEdge(graphP theGraph, int u, int ulink, int v, int vlink)
22
cdef int gp_Embed(graphP theGraph, int embedFlags)
23
cdef int gp_SortVertices(graphP theGraph)
24
25
def is_planar(g, kuratowski=False, set_pos=False, set_embedding=False, circular=False):
26
"""
27
Calls Boyer's planarity algorithm to determine whether g is
28
planar. If kuratowski is False, returns True if g is planar,
29
False otherwise. If kuratowski is True, returns a tuple, first
30
entry is a boolean (whether or not the graph is planar) and second
31
entry is a Kuratowski subgraph/minor (if not planar) or None (if
32
planar). Also, will set an _embedding attribute for the graph g
33
if set_embedding is set to True.
34
35
INPUT:
36
kuratowski -- If True, return a tuple of a boolean and either None
37
or a Kuratowski subgraph or minor
38
set_pos -- if True, uses Schnyder's algorithm to determine positions
39
set_embedding -- if True, records the combinatorial embedding returned
40
(see g.get_embedding())
41
circular -- if True, test for circular planarity
42
43
EXAMPLES::
44
45
sage: G = graphs.DodecahedralGraph()
46
sage: from sage.graphs.planarity import is_planar
47
sage: is_planar(G)
48
True
49
sage: Graph('@').is_planar()
50
True
51
52
TESTS:
53
54
We try checking the planarity of all graphs on 7 or fewer
55
vertices. In fact, to try to track down a segfault, we do it
56
twice. ::
57
58
sage: import networkx.generators.atlas # long time
59
sage: atlas_graphs = [Graph(i) for i in networkx.generators.atlas.graph_atlas_g()] # long time
60
sage: a = [i for i in [1..1252] if atlas_graphs[i].is_planar()] # long time
61
sage: b = [i for i in [1..1252] if atlas_graphs[i].is_planar()] # long time
62
sage: a == b # long time
63
True
64
65
There were some problems with ``set_pos`` stability in the past,
66
so let's check if this this runs without exception::
67
68
sage: for i,g in enumerate(atlas_graphs): # long time
69
... if (not g.is_connected() or i==0): # long time
70
... continue # long time
71
... try: # long time
72
... _ = g.is_planar(set_embedding=True, set_pos=True) # long time
73
... except: # long time
74
... print "There is something wrong here !" # long time
75
... break # long time
76
"""
77
if set_pos and not g.is_connected():
78
raise ValueError("is_planar() cannot set vertex positions for a disconnected graph")
79
80
# First take care of a trivial cases
81
if g.size() == 0: # There are no edges
82
if set_embedding:
83
g._embedding = dict((v, []) for v in g.vertices())
84
return (True, None) if kuratowski else True
85
if len(g) == 2 and g.is_connected(): # P_2 is too small to be triangulated
86
u,v = g.vertices()
87
if set_embedding:
88
g._embedding = { u: [v], v: [u] }
89
if set_pos:
90
g._pos = { u: [0,0], v: [0,1] }
91
return (True, None) if kuratowski else True
92
93
# create to and from mappings to relabel vertices to the set {0,...,n-1}
94
cdef int i
95
listto = g.vertices()
96
ffrom = {}
97
for vvv in listto:
98
ffrom[vvv] = listto.index(vvv)
99
to = {}
100
for i from 0 <= i < len(listto):
101
to[i] = listto[i]
102
g.relabel(ffrom)
103
104
cdef graphP theGraph
105
theGraph = gp_New()
106
cdef int status
107
status = gp_InitGraph(theGraph, g.order())
108
if status != OK:
109
raise RuntimeError("gp_InitGraph status is not ok.")
110
for u, v, _ in g.edge_iterator():
111
status = gp_AddEdge(theGraph, u, 0, v, 0)
112
if status == NOTOK:
113
raise RuntimeError("gp_AddEdge status is not ok.")
114
elif status == NONEMBEDDABLE:
115
# We now know that the graph is nonplanar.
116
if not kuratowski:
117
return False
118
# With just the current edges, we have a nonplanar graph,
119
# so to isolate a kuratowski subgraph, just keep going.
120
break
121
122
status = gp_Embed(theGraph, EMBEDFLAGS_PLANAR)
123
gp_SortVertices(theGraph)
124
125
# use to and from mappings to relabel vertices back from the set {0,...,n-1}
126
g.relabel(to)
127
128
if status == NOTOK:
129
raise RuntimeError("Status is not ok.")
130
elif status == NONEMBEDDABLE:
131
# Kuratowski subgraph isolator
132
g_dict = {}
133
from sage.graphs.graph import Graph
134
for i from 0 <= i < theGraph.N:
135
linked_list = []
136
j = theGraph.G[i].link[1]
137
while j >= theGraph.N:
138
linked_list.append(to[theGraph.G[j].v])
139
j = theGraph.G[j].link[1]
140
if len(linked_list) > 0:
141
g_dict[to[i]] = linked_list
142
G = Graph(g_dict)
143
gp_Free(&theGraph)
144
if kuratowski:
145
return (False, G)
146
else:
147
return False
148
else:
149
if not circular:
150
if set_embedding:
151
emb_dict = {}
152
#for i in range(theGraph.N):
153
for i from 0 <= i < theGraph.N:
154
linked_list = []
155
j = theGraph.G[i].link[1]
156
while j >= theGraph.N:
157
linked_list.append(to[theGraph.G[j].v])
158
j = theGraph.G[j].link[1]
159
emb_dict[to[i]] = linked_list
160
g._embedding = emb_dict
161
if set_pos:
162
g.set_planar_positions()
163
else:
164
if set_embedding:
165
# Take counter-clockwise embedding if circular planar test
166
# Also, pos must be set after removing extra vertex and edges
167
168
# This is separated out here for now because in the circular case,
169
# setting positions would have to come into play while the extra
170
# "wheel" or "star" is still part of the graph.
171
172
emb_dict = {}
173
#for i in range(theGraph.N):
174
for i from 0 <= i < theGraph.N:
175
linked_list = []
176
j = theGraph.G[i].link[0]
177
while j >= theGraph.N:
178
linked_list.append(to[theGraph.G[j].v])
179
j = theGraph.G[j].link[0]
180
emb_dict[to[i]] = linked_list
181
g._embedding = emb_dict
182
gp_Free(&theGraph)
183
if kuratowski:
184
return (True,None)
185
else:
186
return True
187
188