Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/graphs/planarity/graphPreprocess.c
4057 views
1
/*
2
Planarity-Related Graph Algorithms Project
3
Copyright (c) 1997-2010, John M. Boyer
4
All rights reserved. Includes a reference implementation of the following:
5
6
* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding,
7
Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation,
8
University of Victoria, 2001.
9
10
* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n)
11
Planarity by Edge Addition". Journal of Graph Algorithms and Applications,
12
Vol. 8, No. 3, pp. 241-273, 2004.
13
14
* John M. Boyer. "A New Method for Efficiently Generating Planar Graph
15
Visibility Representations". In P. Eades and P. Healy, editors,
16
Proceedings of the 13th International Conference on Graph Drawing 2005,
17
Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006.
18
19
Redistribution and use in source and binary forms, with or without modification,
20
are permitted provided that the following conditions are met:
21
22
* Redistributions of source code must retain the above copyright notice, this
23
list of conditions and the following disclaimer.
24
25
* Redistributions in binary form must reproduce the above copyright notice, this
26
list of conditions and the following disclaimer in the documentation and/or
27
other materials provided with the distribution.
28
29
* Neither the name of the Planarity-Related Graph Algorithms Project nor the names
30
of its contributors may be used to endorse or promote products derived from this
31
software without specific prior written permission.
32
33
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
34
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
37
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
39
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
40
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
41
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43
*/
44
45
#define GRAPHPREPROCESS_C
46
47
#include "graph.h"
48
49
/********************************************************************
50
gp_CreateDFSTree
51
Assigns Depth First Index (DFI) to each vertex. Also records parent
52
of each vertex in the DFS tree, and marks DFS tree edges that go from
53
parent to child. Forward arc cycle edges are also distinguished from
54
edges leading from a DFS tree descendant to an ancestor-- both DFS tree
55
edges and back arcs. The forward arcs are moved to the end of the
56
adjacency list to make the set easier to find and process.
57
********************************************************************/
58
59
#include "platformTime.h"
60
61
int gp_CreateDFSTree(graphP theGraph)
62
{
63
stackP theStack;
64
int N, DFI = 0, I, uparent, u, e, J;
65
66
#ifdef PROFILE
67
platform_time start, end;
68
platform_GetTime(start);
69
#endif
70
71
if (theGraph==NULL) return NOTOK;
72
if (theGraph->internalFlags & FLAGS_DFSNUMBERED) return OK;
73
74
gp_LogLine("\ngraphPreprocess.c/gp_CreateDFSTree() start");
75
76
N = theGraph->N;
77
theStack = theGraph->theStack;
78
79
/* There are 2M edge records (arcs) and for each we can push 2 integers,
80
so a stack of 2 * arcCapacity integers suffices.
81
This is already in theGraph structure, so we make sure it's empty,
82
then clear all visited flags in prep for the Depth first search. */
83
84
if (sp_GetCapacity(theStack) < 2*gp_GetArcCapacity(theGraph))
85
return NOTOK;
86
87
sp_ClearStack(theStack);
88
89
for (I=0; I < N; I++)
90
theGraph->G[I].visited = 0;
91
92
/* This outer loop causes the connected subgraphs of a disconnected
93
graph to be numbered */
94
95
for (I=0; I < N && DFI < N; I++)
96
{
97
if (theGraph->V[I].DFSParent != NIL)
98
continue;
99
100
sp_Push2(theStack, NIL, NIL);
101
while (sp_NonEmpty(theStack))
102
{
103
sp_Pop2(theStack, uparent, e);
104
u = uparent == NIL ? I : theGraph->G[e].v;
105
106
if (!theGraph->G[u].visited)
107
{
108
gp_LogLine(gp_MakeLogStr3("V=%d, DFI=%d, Parent=%d", u, DFI, uparent));
109
110
theGraph->G[u].visited = 1;
111
theGraph->G[u].v = DFI++;
112
theGraph->V[u].DFSParent = uparent;
113
if (e != NIL)
114
{
115
theGraph->G[e].type = EDGE_DFSCHILD;
116
theGraph->G[gp_GetTwinArc(theGraph, e)].type = EDGE_DFSPARENT;
117
118
// We want the child arcs to be at the beginning
119
// of the adjacency list.
120
gp_MoveArcToFirst(theGraph, uparent, e);
121
}
122
123
/* Push edges to all unvisited neighbors. These will be either
124
tree edges to children or forward arcs of back edges */
125
126
J = gp_GetFirstArc(theGraph, u);
127
while (gp_IsArc(theGraph, J))
128
{
129
if (!theGraph->G[theGraph->G[J].v].visited)
130
sp_Push2(theStack, u, J);
131
J = gp_GetNextArc(theGraph, J);
132
}
133
}
134
else
135
{
136
// If the edge leads to a visited vertex, then it is
137
// the forward arc of a back edge.
138
theGraph->G[e].type = EDGE_FORWARD;
139
theGraph->G[gp_GetTwinArc(theGraph, e)].type = EDGE_BACK;
140
141
// We want all of the forward edges to descendants to
142
// be at the end of the adjacency list.
143
// The tree edge to the parent and the back edges to ancestors
144
// are in the middle, between the child edges and forward edges.
145
gp_MoveArcToLast(theGraph, uparent, e);
146
}
147
}
148
}
149
150
gp_LogLine("graphPreprocess.c/gp_CreateDFSTree() end\n");
151
152
theGraph->internalFlags |= FLAGS_DFSNUMBERED;
153
154
#ifdef PROFILE
155
platform_GetTime(end);
156
printf("DFS in %.3lf seconds.\n", platform_GetDuration(start,end));
157
#endif
158
159
return OK;
160
}
161
162
/********************************************************************
163
gp_SortVertices()
164
Once depth first numbering has been applied to the graph, the v member
165
of each vertex contains the DFI. This routine can reorder the vertices
166
in linear time so that they appear in ascending order by DFI. Note
167
that the field v is then used to store the original number of the
168
vertex.
169
Note that this function is not underscored (i.e. not private). We
170
export it because it can be called again at a later point to reorder
171
the vertices back into the original numbering with the DFI values
172
stored in the v fields (in other words this function is its own inverse).
173
********************************************************************/
174
175
int gp_SortVertices(graphP theGraph)
176
{
177
return theGraph->functions.fpSortVertices(theGraph);
178
}
179
180
int _SortVertices(graphP theGraph)
181
{
182
int I, N, M, e, J, srcPos, dstPos;
183
vertexRec tempV;
184
graphNode tempG;
185
186
#ifdef PROFILE
187
platform_time start, end;
188
platform_GetTime(start);
189
#endif
190
191
if (theGraph == NULL) return NOTOK;
192
if (!(theGraph->internalFlags&FLAGS_DFSNUMBERED))
193
if (gp_CreateDFSTree(theGraph) != OK)
194
return NOTOK;
195
196
/* Cache number of vertices and edges into local variables */
197
198
N = theGraph->N;
199
M = theGraph->M + sp_GetCurrentSize(theGraph->edgeHoles);
200
201
/* Change labels of edges from v to DFI(v)-- or vice versa
202
Also, if any links go back to locations 0 to n-1, then they
203
need to be changed because we are reordering the vertices */
204
205
for (e=0, J=theGraph->edgeOffset; e < M; e++, J+=2)
206
{
207
if (theGraph->G[J].v != NIL)
208
{
209
theGraph->G[J].v = theGraph->G[theGraph->G[J].v].v;
210
theGraph->G[J+1].v = theGraph->G[theGraph->G[J+1].v].v;
211
}
212
}
213
214
/* Convert DFSParent from v to DFI(v) or vice versa */
215
216
for (I=0; I < N; I++)
217
if (theGraph->V[I].DFSParent != NIL)
218
theGraph->V[I].DFSParent = theGraph->G[theGraph->V[I].DFSParent].v;
219
220
/* Sort by 'v using constant time random access. Move each vertex to its
221
destination 'v', and store its source location in 'v'. */
222
223
/* First we clear the visitation flags. We need these to help mark
224
visited vertices because we change the 'v' field to be the source
225
location, so we cannot use index==v as a test for whether the
226
correct vertex is in location 'index'. */
227
228
for (I=0; I < N; I++)
229
theGraph->G[I].visited = 0;
230
231
/* We visit each vertex location, skipping those marked as visited since
232
we've already moved the correct vertex into that location. The
233
inner loop swaps the vertex at location I into the correct position,
234
G[I].v, marks that location as visited, then sets its 'v' field to
235
be the location from whence we obtained the vertex record. */
236
237
for (I=0; I < N; I++)
238
{
239
srcPos = I;
240
while (!theGraph->G[I].visited)
241
{
242
dstPos = theGraph->G[I].v;
243
244
tempG = theGraph->G[dstPos];
245
tempV = theGraph->V[dstPos];
246
theGraph->G[dstPos] = theGraph->G[I];
247
theGraph->V[dstPos] = theGraph->V[I];
248
theGraph->G[I] = tempG;
249
theGraph->V[I] = tempV;
250
251
theGraph->G[dstPos].visited = 1;
252
theGraph->G[dstPos].v = srcPos;
253
254
srcPos = dstPos;
255
}
256
}
257
258
/* Invert the bit that records the sort order of the graph */
259
260
if (theGraph->internalFlags & FLAGS_SORTEDBYDFI)
261
theGraph->internalFlags &= ~FLAGS_SORTEDBYDFI;
262
else theGraph->internalFlags |= FLAGS_SORTEDBYDFI;
263
264
#ifdef PROFILE
265
platform_GetTime(end);
266
printf("SortVertices in %.3lf seconds.\n", platform_GetDuration(start,end));
267
#endif
268
269
return OK;
270
}
271
272
/********************************************************************
273
gp_LowpointAndLeastAncestor()
274
leastAncestor: min(DFI of neighbors connected by backedge)
275
Lowpoint: min(leastAncestor, Lowpoint of DFSChildren)
276
277
This implementation requires that the vertices be sorted in DFI order
278
(such that the edge records contain DFI values in their v fields). An
279
implementation could be made to run before sorting using the fact that
280
the value G[G[e].v].v before sorting is equal to G[e].v after the sort.
281
282
For computing Lowpoint, we must do a post-order traversal of the DFS tree.
283
We push the root of the DFS tree, then we loop while the stack is not empty.
284
We pop a vertex; if it is not marked, then we are on our way down the DFS
285
tree, so we mark it and push it back on, followed by pushing its
286
DFS children. The next time we pop the node, all of its children
287
will have been popped, marked+children pushed, and popped again. On
288
the second pop of the vertex, we can therefore compute the Lowpoint
289
values based on the childrens' Lowpoints and the edges in the vertex's
290
adjacency list.
291
292
A stack of size N suffices because we push each vertex only once.
293
********************************************************************/
294
295
int gp_LowpointAndLeastAncestor(graphP theGraph)
296
{
297
stackP theStack = theGraph->theStack;
298
int I, u, uneighbor, J, L, leastAncestor;
299
int totalVisited = 0;
300
301
#ifdef PROFILE
302
platform_time start, end;
303
platform_GetTime(start);
304
#endif
305
306
sp_ClearStack(theStack);
307
308
for (I=0; I < theGraph->N; I++)
309
theGraph->G[I].visited = 0;
310
311
/* This outer loop causes the connected subgraphs of a disconnected
312
graph to be processed */
313
314
for (I=0; I < theGraph->N && totalVisited < theGraph->N; I++)
315
{
316
if (theGraph->G[I].visited)
317
continue;
318
319
sp_Push(theStack, I);
320
while (sp_NonEmpty(theStack))
321
{
322
sp_Pop(theStack, u);
323
if (!theGraph->G[u].visited)
324
{
325
/* Mark u as visited, then push it back on the stack */
326
theGraph->G[u].visited = 1;
327
totalVisited++;
328
sp_Push(theStack, u);
329
330
/* Push DFS children */
331
J = gp_GetFirstArc(theGraph, u);
332
while (gp_IsArc(theGraph, J))
333
{
334
if (theGraph->G[J].type == EDGE_DFSCHILD)
335
{
336
sp_Push(theStack, theGraph->G[J].v);
337
}
338
else break;
339
340
J = gp_GetNextArc(theGraph, J);
341
}
342
}
343
else
344
{
345
/* Start with high values because we are doing a min function */
346
L = leastAncestor = u;
347
348
/* Compute L and leastAncestor */
349
J = gp_GetFirstArc(theGraph, u);
350
while (gp_IsArc(theGraph, J))
351
{
352
uneighbor = theGraph->G[J].v;
353
if (theGraph->G[J].type == EDGE_DFSCHILD)
354
{
355
if (L > theGraph->V[uneighbor].Lowpoint)
356
L = theGraph->V[uneighbor].Lowpoint;
357
}
358
else if (theGraph->G[J].type == EDGE_BACK)
359
{
360
if (leastAncestor > uneighbor)
361
leastAncestor = uneighbor;
362
}
363
else if (theGraph->G[J].type == EDGE_FORWARD)
364
break;
365
366
J = gp_GetNextArc(theGraph, J);
367
}
368
369
/* Assign leastAncestor and Lowpoint to the vertex */
370
theGraph->V[u].leastAncestor = leastAncestor;
371
theGraph->V[u].Lowpoint = leastAncestor < L ? leastAncestor : L;
372
}
373
}
374
}
375
376
#ifdef PROFILE
377
platform_GetTime(end);
378
printf("Lowpoint in %.3lf seconds.\n", platform_GetDuration(start,end));
379
#endif
380
381
return OK;
382
}
383
384
385