Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/graphs/planarity.pyx
8815 views
1
"""
2
Wrapper for Boyer's (C) planarity algorithm.
3
"""
4
5
cdef extern from "planarity_c/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):
70
....: continue
71
....: _ = g.is_planar(set_embedding=True, set_pos=True)
72
"""
73
if set_pos and not g.is_connected():
74
raise ValueError("is_planar() cannot set vertex positions for a disconnected graph")
75
76
# First take care of a trivial cases
77
if g.size() == 0: # There are no edges
78
if set_embedding:
79
g._embedding = dict((v, []) for v in g.vertices())
80
return (True, None) if kuratowski else True
81
if len(g) == 2 and g.is_connected(): # P_2 is too small to be triangulated
82
u,v = g.vertices()
83
if set_embedding:
84
g._embedding = { u: [v], v: [u] }
85
if set_pos:
86
g._pos = { u: [0,0], v: [0,1] }
87
return (True, None) if kuratowski else True
88
89
# create to and from mappings to relabel vertices to the set {0,...,n-1}
90
cdef int i
91
listto = g.vertices()
92
ffrom = {}
93
for vvv in listto:
94
ffrom[vvv] = listto.index(vvv)
95
to = {}
96
for i from 0 <= i < len(listto):
97
to[i] = listto[i]
98
g.relabel(ffrom)
99
100
cdef graphP theGraph
101
theGraph = gp_New()
102
cdef int status
103
status = gp_InitGraph(theGraph, g.order())
104
if status != OK:
105
raise RuntimeError("gp_InitGraph status is not ok.")
106
for u, v, _ in g.edge_iterator():
107
status = gp_AddEdge(theGraph, u, 0, v, 0)
108
if status == NOTOK:
109
raise RuntimeError("gp_AddEdge status is not ok.")
110
elif status == NONEMBEDDABLE:
111
# We now know that the graph is nonplanar.
112
if not kuratowski:
113
return False
114
# With just the current edges, we have a nonplanar graph,
115
# so to isolate a kuratowski subgraph, just keep going.
116
break
117
118
status = gp_Embed(theGraph, EMBEDFLAGS_PLANAR)
119
gp_SortVertices(theGraph)
120
121
# use to and from mappings to relabel vertices back from the set {0,...,n-1}
122
g.relabel(to)
123
124
if status == NOTOK:
125
raise RuntimeError("Status is not ok.")
126
elif status == NONEMBEDDABLE:
127
# Kuratowski subgraph isolator
128
g_dict = {}
129
from sage.graphs.graph import Graph
130
for i from 0 <= i < theGraph.N:
131
linked_list = []
132
j = theGraph.G[i].link[1]
133
while j >= theGraph.N:
134
linked_list.append(to[theGraph.G[j].v])
135
j = theGraph.G[j].link[1]
136
if len(linked_list) > 0:
137
g_dict[to[i]] = linked_list
138
G = Graph(g_dict)
139
gp_Free(&theGraph)
140
if kuratowski:
141
return (False, G)
142
else:
143
return False
144
else:
145
if not circular:
146
if set_embedding:
147
emb_dict = {}
148
#for i in range(theGraph.N):
149
for i from 0 <= i < theGraph.N:
150
linked_list = []
151
j = theGraph.G[i].link[1]
152
while j >= theGraph.N:
153
linked_list.append(to[theGraph.G[j].v])
154
j = theGraph.G[j].link[1]
155
emb_dict[to[i]] = linked_list
156
g._embedding = emb_dict
157
if set_pos:
158
g.set_planar_positions()
159
else:
160
if set_embedding:
161
# Take counter-clockwise embedding if circular planar test
162
# Also, pos must be set after removing extra vertex and edges
163
164
# This is separated out here for now because in the circular case,
165
# setting positions would have to come into play while the extra
166
# "wheel" or "star" is still part of the graph.
167
168
emb_dict = {}
169
#for i in range(theGraph.N):
170
for i from 0 <= i < theGraph.N:
171
linked_list = []
172
j = theGraph.G[i].link[0]
173
while j >= theGraph.N:
174
linked_list.append(to[theGraph.G[j].v])
175
j = theGraph.G[j].link[0]
176
emb_dict[to[i]] = linked_list
177
g._embedding = emb_dict
178
gp_Free(&theGraph)
179
if kuratowski:
180
return (True,None)
181
else:
182
return True
183
184