Path: blob/master/sage/graphs/planarity/graphPreprocess.c
4057 views
/*1Planarity-Related Graph Algorithms Project2Copyright (c) 1997-2010, John M. Boyer3All rights reserved. Includes a reference implementation of the following:45* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding,6Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation,7University of Victoria, 2001.89* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n)10Planarity by Edge Addition". Journal of Graph Algorithms and Applications,11Vol. 8, No. 3, pp. 241-273, 2004.1213* John M. Boyer. "A New Method for Efficiently Generating Planar Graph14Visibility Representations". In P. Eades and P. Healy, editors,15Proceedings of the 13th International Conference on Graph Drawing 2005,16Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006.1718Redistribution and use in source and binary forms, with or without modification,19are permitted provided that the following conditions are met:2021* Redistributions of source code must retain the above copyright notice, this22list of conditions and the following disclaimer.2324* Redistributions in binary form must reproduce the above copyright notice, this25list of conditions and the following disclaimer in the documentation and/or26other materials provided with the distribution.2728* Neither the name of the Planarity-Related Graph Algorithms Project nor the names29of its contributors may be used to endorse or promote products derived from this30software without specific prior written permission.3132THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"33AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE34IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE35DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR36ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES37(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;38LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON39ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT40(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS41SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.42*/4344#define GRAPHPREPROCESS_C4546#include "graph.h"4748/********************************************************************49gp_CreateDFSTree50Assigns Depth First Index (DFI) to each vertex. Also records parent51of each vertex in the DFS tree, and marks DFS tree edges that go from52parent to child. Forward arc cycle edges are also distinguished from53edges leading from a DFS tree descendant to an ancestor-- both DFS tree54edges and back arcs. The forward arcs are moved to the end of the55adjacency list to make the set easier to find and process.56********************************************************************/5758#include "platformTime.h"5960int gp_CreateDFSTree(graphP theGraph)61{62stackP theStack;63int N, DFI = 0, I, uparent, u, e, J;6465#ifdef PROFILE66platform_time start, end;67platform_GetTime(start);68#endif6970if (theGraph==NULL) return NOTOK;71if (theGraph->internalFlags & FLAGS_DFSNUMBERED) return OK;7273gp_LogLine("\ngraphPreprocess.c/gp_CreateDFSTree() start");7475N = theGraph->N;76theStack = theGraph->theStack;7778/* There are 2M edge records (arcs) and for each we can push 2 integers,79so a stack of 2 * arcCapacity integers suffices.80This is already in theGraph structure, so we make sure it's empty,81then clear all visited flags in prep for the Depth first search. */8283if (sp_GetCapacity(theStack) < 2*gp_GetArcCapacity(theGraph))84return NOTOK;8586sp_ClearStack(theStack);8788for (I=0; I < N; I++)89theGraph->G[I].visited = 0;9091/* This outer loop causes the connected subgraphs of a disconnected92graph to be numbered */9394for (I=0; I < N && DFI < N; I++)95{96if (theGraph->V[I].DFSParent != NIL)97continue;9899sp_Push2(theStack, NIL, NIL);100while (sp_NonEmpty(theStack))101{102sp_Pop2(theStack, uparent, e);103u = uparent == NIL ? I : theGraph->G[e].v;104105if (!theGraph->G[u].visited)106{107gp_LogLine(gp_MakeLogStr3("V=%d, DFI=%d, Parent=%d", u, DFI, uparent));108109theGraph->G[u].visited = 1;110theGraph->G[u].v = DFI++;111theGraph->V[u].DFSParent = uparent;112if (e != NIL)113{114theGraph->G[e].type = EDGE_DFSCHILD;115theGraph->G[gp_GetTwinArc(theGraph, e)].type = EDGE_DFSPARENT;116117// We want the child arcs to be at the beginning118// of the adjacency list.119gp_MoveArcToFirst(theGraph, uparent, e);120}121122/* Push edges to all unvisited neighbors. These will be either123tree edges to children or forward arcs of back edges */124125J = gp_GetFirstArc(theGraph, u);126while (gp_IsArc(theGraph, J))127{128if (!theGraph->G[theGraph->G[J].v].visited)129sp_Push2(theStack, u, J);130J = gp_GetNextArc(theGraph, J);131}132}133else134{135// If the edge leads to a visited vertex, then it is136// the forward arc of a back edge.137theGraph->G[e].type = EDGE_FORWARD;138theGraph->G[gp_GetTwinArc(theGraph, e)].type = EDGE_BACK;139140// We want all of the forward edges to descendants to141// be at the end of the adjacency list.142// The tree edge to the parent and the back edges to ancestors143// are in the middle, between the child edges and forward edges.144gp_MoveArcToLast(theGraph, uparent, e);145}146}147}148149gp_LogLine("graphPreprocess.c/gp_CreateDFSTree() end\n");150151theGraph->internalFlags |= FLAGS_DFSNUMBERED;152153#ifdef PROFILE154platform_GetTime(end);155printf("DFS in %.3lf seconds.\n", platform_GetDuration(start,end));156#endif157158return OK;159}160161/********************************************************************162gp_SortVertices()163Once depth first numbering has been applied to the graph, the v member164of each vertex contains the DFI. This routine can reorder the vertices165in linear time so that they appear in ascending order by DFI. Note166that the field v is then used to store the original number of the167vertex.168Note that this function is not underscored (i.e. not private). We169export it because it can be called again at a later point to reorder170the vertices back into the original numbering with the DFI values171stored in the v fields (in other words this function is its own inverse).172********************************************************************/173174int gp_SortVertices(graphP theGraph)175{176return theGraph->functions.fpSortVertices(theGraph);177}178179int _SortVertices(graphP theGraph)180{181int I, N, M, e, J, srcPos, dstPos;182vertexRec tempV;183graphNode tempG;184185#ifdef PROFILE186platform_time start, end;187platform_GetTime(start);188#endif189190if (theGraph == NULL) return NOTOK;191if (!(theGraph->internalFlags&FLAGS_DFSNUMBERED))192if (gp_CreateDFSTree(theGraph) != OK)193return NOTOK;194195/* Cache number of vertices and edges into local variables */196197N = theGraph->N;198M = theGraph->M + sp_GetCurrentSize(theGraph->edgeHoles);199200/* Change labels of edges from v to DFI(v)-- or vice versa201Also, if any links go back to locations 0 to n-1, then they202need to be changed because we are reordering the vertices */203204for (e=0, J=theGraph->edgeOffset; e < M; e++, J+=2)205{206if (theGraph->G[J].v != NIL)207{208theGraph->G[J].v = theGraph->G[theGraph->G[J].v].v;209theGraph->G[J+1].v = theGraph->G[theGraph->G[J+1].v].v;210}211}212213/* Convert DFSParent from v to DFI(v) or vice versa */214215for (I=0; I < N; I++)216if (theGraph->V[I].DFSParent != NIL)217theGraph->V[I].DFSParent = theGraph->G[theGraph->V[I].DFSParent].v;218219/* Sort by 'v using constant time random access. Move each vertex to its220destination 'v', and store its source location in 'v'. */221222/* First we clear the visitation flags. We need these to help mark223visited vertices because we change the 'v' field to be the source224location, so we cannot use index==v as a test for whether the225correct vertex is in location 'index'. */226227for (I=0; I < N; I++)228theGraph->G[I].visited = 0;229230/* We visit each vertex location, skipping those marked as visited since231we've already moved the correct vertex into that location. The232inner loop swaps the vertex at location I into the correct position,233G[I].v, marks that location as visited, then sets its 'v' field to234be the location from whence we obtained the vertex record. */235236for (I=0; I < N; I++)237{238srcPos = I;239while (!theGraph->G[I].visited)240{241dstPos = theGraph->G[I].v;242243tempG = theGraph->G[dstPos];244tempV = theGraph->V[dstPos];245theGraph->G[dstPos] = theGraph->G[I];246theGraph->V[dstPos] = theGraph->V[I];247theGraph->G[I] = tempG;248theGraph->V[I] = tempV;249250theGraph->G[dstPos].visited = 1;251theGraph->G[dstPos].v = srcPos;252253srcPos = dstPos;254}255}256257/* Invert the bit that records the sort order of the graph */258259if (theGraph->internalFlags & FLAGS_SORTEDBYDFI)260theGraph->internalFlags &= ~FLAGS_SORTEDBYDFI;261else theGraph->internalFlags |= FLAGS_SORTEDBYDFI;262263#ifdef PROFILE264platform_GetTime(end);265printf("SortVertices in %.3lf seconds.\n", platform_GetDuration(start,end));266#endif267268return OK;269}270271/********************************************************************272gp_LowpointAndLeastAncestor()273leastAncestor: min(DFI of neighbors connected by backedge)274Lowpoint: min(leastAncestor, Lowpoint of DFSChildren)275276This implementation requires that the vertices be sorted in DFI order277(such that the edge records contain DFI values in their v fields). An278implementation could be made to run before sorting using the fact that279the value G[G[e].v].v before sorting is equal to G[e].v after the sort.280281For computing Lowpoint, we must do a post-order traversal of the DFS tree.282We push the root of the DFS tree, then we loop while the stack is not empty.283We pop a vertex; if it is not marked, then we are on our way down the DFS284tree, so we mark it and push it back on, followed by pushing its285DFS children. The next time we pop the node, all of its children286will have been popped, marked+children pushed, and popped again. On287the second pop of the vertex, we can therefore compute the Lowpoint288values based on the childrens' Lowpoints and the edges in the vertex's289adjacency list.290291A stack of size N suffices because we push each vertex only once.292********************************************************************/293294int gp_LowpointAndLeastAncestor(graphP theGraph)295{296stackP theStack = theGraph->theStack;297int I, u, uneighbor, J, L, leastAncestor;298int totalVisited = 0;299300#ifdef PROFILE301platform_time start, end;302platform_GetTime(start);303#endif304305sp_ClearStack(theStack);306307for (I=0; I < theGraph->N; I++)308theGraph->G[I].visited = 0;309310/* This outer loop causes the connected subgraphs of a disconnected311graph to be processed */312313for (I=0; I < theGraph->N && totalVisited < theGraph->N; I++)314{315if (theGraph->G[I].visited)316continue;317318sp_Push(theStack, I);319while (sp_NonEmpty(theStack))320{321sp_Pop(theStack, u);322if (!theGraph->G[u].visited)323{324/* Mark u as visited, then push it back on the stack */325theGraph->G[u].visited = 1;326totalVisited++;327sp_Push(theStack, u);328329/* Push DFS children */330J = gp_GetFirstArc(theGraph, u);331while (gp_IsArc(theGraph, J))332{333if (theGraph->G[J].type == EDGE_DFSCHILD)334{335sp_Push(theStack, theGraph->G[J].v);336}337else break;338339J = gp_GetNextArc(theGraph, J);340}341}342else343{344/* Start with high values because we are doing a min function */345L = leastAncestor = u;346347/* Compute L and leastAncestor */348J = gp_GetFirstArc(theGraph, u);349while (gp_IsArc(theGraph, J))350{351uneighbor = theGraph->G[J].v;352if (theGraph->G[J].type == EDGE_DFSCHILD)353{354if (L > theGraph->V[uneighbor].Lowpoint)355L = theGraph->V[uneighbor].Lowpoint;356}357else if (theGraph->G[J].type == EDGE_BACK)358{359if (leastAncestor > uneighbor)360leastAncestor = uneighbor;361}362else if (theGraph->G[J].type == EDGE_FORWARD)363break;364365J = gp_GetNextArc(theGraph, J);366}367368/* Assign leastAncestor and Lowpoint to the vertex */369theGraph->V[u].leastAncestor = leastAncestor;370theGraph->V[u].Lowpoint = leastAncestor < L ? leastAncestor : L;371}372}373}374375#ifdef PROFILE376platform_GetTime(end);377printf("Lowpoint in %.3lf seconds.\n", platform_GetDuration(start,end));378#endif379380return OK;381}382383384385