bool CconnectClusterPlanar::preProcess(ClusterGraph &C,Graph &G) { if (!isCConnected(C)) { ogdf::sprintf(errorCode,124,"Graph is not C-connected \n"); m_errorCode = nonCConnected; return false; } PlanarModule Pm; if (!Pm.planarityTest(C)) { ogdf::sprintf(errorCode,124,"Graph is not planar\n"); m_errorCode = nonPlanar; return false; } cluster c; SListPure<node> selfLoops; makeLoopFree(G,selfLoops); c = C.rootCluster(); bool cPlanar = planarityTest(C,c,G); return cPlanar; }
bool CconnectClusterPlanar::preProcess(ClusterGraph &C,Graph &G) { if (!isCConnected(C)) { m_errorCode = nonCConnected; return false; } if (!isPlanar(C)) { m_errorCode = nonPlanar; return false; } cluster c; SListPure<node> selfLoops; makeLoopFree(G,selfLoops); c = C.rootCluster(); bool cPlanar = planarityTest(C,c,G); return cPlanar; }
// Prepares the planarity test and the planar embedding // Parallel edges: do not need to be ignored, they can be handled // by the planarity test. // Selfloops: need to be ignored. bool PlanarModule::preparation(Graph &G,bool embed) { if (G.numberOfEdges() < 9 && !embed) return true; else if (G.numberOfEdges() < 3 && embed) return true; node v; edge e; SListPure<node> selfLoops; makeLoopFree(G,selfLoops); prepareParallelEdges(G); int isolated = 0; forall_nodes(v,G) if (v->degree() == 0) isolated++; if (((G.numberOfNodes()-isolated) > 2) && ((3*(G.numberOfNodes()-isolated) -6) < (G.numberOfEdges() - m_parallelCount))) return false; bool planar = true; NodeArray<node> tableNodes(G,0); EdgeArray<edge> tableEdges(G,0); NodeArray<bool> mark(G,0); EdgeArray<int> componentID(G); // Determine Biconnected Components int bcCount = biconnectedComponents(G,componentID); // Determine edges per biconnected component Array<SList<edge> > blockEdges(0,bcCount-1); forall_edges(e,G) { blockEdges[componentID[e]].pushFront(e); }
void OptimalRanking::doCall( const Graph& G, NodeArray<int> &rank, EdgeArray<bool> &reversed, const EdgeArray<int> &length, const EdgeArray<int> &costOrig) { MinCostFlowReinelt<int> mcf; // construct min-cost flow problem GraphCopy GC; GC.createEmpty(G); // compute connected component of G NodeArray<int> component(G); int numCC = connectedComponents(G,component); // intialize the array of lists of nodes contained in a CC Array<List<node> > nodesInCC(numCC); for(node v : G.nodes) nodesInCC[component[v]].pushBack(v); EdgeArray<edge> auxCopy(G); rank.init(G); for(int i = 0; i < numCC; ++i) { GC.initByNodes(nodesInCC[i], auxCopy); makeLoopFree(GC); for(edge e : GC.edges) if(reversed[GC.original(e)]) GC.reverseEdge(e); // special cases: if(GC.numberOfNodes() == 1) { rank[GC.original(GC.firstNode())] = 0; continue; } else if(GC.numberOfEdges() == 1) { edge e = GC.original(GC.firstEdge()); rank[e->source()] = 0; rank[e->target()] = length[e]; continue; } EdgeArray<int> lowerBound(GC,0); EdgeArray<int> upperBound(GC,mcf.infinity()); EdgeArray<int> cost(GC); NodeArray<int> supply(GC); for(edge e : GC.edges) cost[e] = -length[GC.original(e)]; for(node v : GC.nodes) { int s = 0; edge e; forall_adj_edges(e,v) { if(v == e->source()) s += costOrig[GC.original(e)]; else s -= costOrig[GC.original(e)]; } supply[v] = s; } OGDF_ASSERT(isAcyclic(GC) == true); // find min-cost flow EdgeArray<int> flow(GC); NodeArray<int> dual(GC); #ifdef OGDF_DEBUG bool feasible = #endif mcf.call(GC, lowerBound, upperBound, cost, supply, flow, dual); OGDF_ASSERT(feasible); for(node v : GC.nodes) rank[GC.original(v)] = dual[v]; } }
//--------------------------------------------------------- //actual call //minDegree default 2, all other nodes are skipped //only high values have an impact because we only //work on triconnected components, skipping all low //degree nodes (but we make the test graphcopy biconnected //afterwards) void CliqueFinder::doCall(int minDegree) { //--------------------------------------------- //initialize structures and check preconditions //--------------------------------------------- m_copyCliqueNumber.init(*m_pCopy, -1); m_usedNode.init(*m_pCopy, false); makeParallelFreeUndirected(*m_pCopy); //it doesnt make sense to count loops makeLoopFree(*m_pCopy); //or parallel edges m_numberOfCliques = 0; //We first indentify the biconnected components of //the graph to allow the use of the SPQR-tree data //Structure. Latter then separates the different //triconnected components on which we finally work //TODO: delete all copy nodes with degree < minDegree int nodeNum = m_pGraph->numberOfNodes(); //TODO: change for non-cliques, where this is not necessary if (nodeNum < minDegree) return; //nothing to find for cliques //------------------------------------------------------- //Special cases: //Precondition for SPQR-trees: graph has at least 3 nodes //or 2 nodes and at least 3 edges //TODO: check this after makebiconnected //---------------------------- //set values for trivial cases if (nodeNum < 3) { //only set numbers for the special case if (nodeNum == 2) { if (m_pGraph->numberOfEdges() >= 1) //> 2) { node w = m_pCopy->firstNode(); m_copyCliqueNumber[w] = 0; w = w->succ(); m_copyCliqueNumber[w] = 0; } else { if (minDegree == 0) { node w = m_pCopy->firstNode(); m_copyCliqueNumber[w] = 0; w = w->succ(); m_copyCliqueNumber[w] = 1; }//if no mindegree } }//if two nodes else if ( (nodeNum == 1) && (minDegree <= 0)) m_copyCliqueNumber[m_pCopy->firstNode()] = 0; return; }//graph too small OGDF_ASSERT(m_pCopy != 0) //save the original edges EdgeArray<bool> originalEdge(*m_pCopy, true); List<edge> added; //we make the copy biconnected, this keeps the triconnected //components //------------------------------------------------------------- //store the original node degrees: //afterwards we want to be able to sort the nodes corresponding //to their real degree, not the one with the additional //connectivity edges NodeArray<int> realDegree(*m_pCopy, -1);//no isolated nodes exist here //relative degree, number of high degree neighbours NodeArray<int> relDegree(*m_pCopy, 0);//no isolated nodes exist here for(node v : m_pCopy->nodes) { realDegree[v] = v->degree(); if (v->degree() > 0) { adjEntry adRun = v->firstAdj(); while (adRun) { adjEntry succ = adRun->succ(); if (adRun->twinNode()->degree() >= minDegree) relDegree[v]++; adRun = succ; }//while }//if not isolated } makeBiconnected(*m_pCopy, added); //TODO: We can decrease node degrees by the number of adjacent //low degree nodes to sort them only by number of relevant connections //PARTIALLY DONE: relDegree //storing the component number, there are no isolated nodes now EdgeArray<int> component(*m_pCopy); StaticSPQRTree spqrTree(*m_pCopy); //Return if there are no R-nodes if (spqrTree.numberOfRNodes() == 0) { //TODO:set node numbers for cliques //that are not triconnected //each edge is a min. clique for mindegree 1 //each triangle for mindegree 2? return; }//if //the degree of the original node //within the triconnected component NodeArray<int> ccDegree(*m_pCopy, 0); for(node v : spqrTree.tree().nodes) { //we search for dense subgraphs in R-Nodes //heuristics: //sort the nodes by their degree within the component //in descending order, then start cliques by initializing //them with the first node and checking the remaining, //starting new cliques with nodes that don't fit in the //existing cliques (stored in cliqueList) if (spqrTree.typeOf(v) == SPQRTree::RNode) { //retrieve the skeleton Skeleton &s = spqrTree.skeleton(v); Graph &skeletonG = s.getGraph(); //list of cliques List< List<node>* > cliqueList; //we insert all nodes into a list to sort them List<node> sortList; //save the usable edges within the triconnected component EdgeArray<bool> usableEdge(*m_pCopy, false); //derive the degree of the original node //within the triconnected component for(node w : skeletonG.nodes) { node vOrig = s.original(w); edge eSkel; forall_adj_edges(eSkel, w) { edge goodEdge = s.realEdge(eSkel); bool isGoodEdge = goodEdge != nullptr; if (isGoodEdge) isGoodEdge = m_pCopy->original(goodEdge) != nullptr; //if (s.realEdge(eSkel)) if (isGoodEdge) { ccDegree[vOrig]++; usableEdge[goodEdge] = true; } }//foralladjedges sortList.pushBack(vOrig); } //sort the nodes corresponding to their degree NodeComparer<int> ncomp(ccDegree, false); sortList.quicksort(ncomp); ListIterator<node> itNode = sortList.begin(); while(itNode.valid()) { //hier erst vergleichen, ob Knoten Grad > aktcliquengroesse, //dann ob mit clique verbunden //alternativ koennte man stattdessen fuer jeden gewaehlten //Knoten nur noch seine Nachbarn als Kandidaten zulassen //hier sollte man mal ein paar Strategien testen, z.B. //streng nach Listenordnung vorgehen oder eine "Breitensuche" //vom Startknoten aus.. node vCand = *itNode; //node can appear in several 3connected components if (m_usedNode[vCand]) { ++itNode; continue; }//if already used //if there are only "small" degree nodes left, we stop //if (vCand->degree() < minDegree) if (ccDegree[vCand] < minDegree) break; //------------------------------------------------ //successively check the node against the existing //clique candidates //run through the clique candidates to find a matching //node set bool setFound = false; ListIterator< List<node>* > itCand = cliqueList.begin(); while (itCand.valid()) { //in the case of cliques, the node needs min degree //greater or equal to current clique size //TODO: adapt to dense subgraphs bool isCand = false; if (m_density == 100) isCand = (vCand->degree() >= (*itCand)->size()); else isCand = (vCand->degree() >= ceil(m_density*(*itCand)->size()/100.0)); if (isCand) { //TODO: insert adjacency oracle here to speed //up the check? //TODO: check if change from clique to dense subgraph criterion //violates some preconditions for our search if (allAdjacent(vCand, (*itCand))) { OGDF_ASSERT(m_usedNode[*itNode] == false) (*itCand)->pushBack(*itNode); setFound = true; m_usedNode[(*itNode)] = true; //bubble sort the clique after insertion of the node //while size > predsize swap positions ListIterator< List<node>* > itSearch = itCand.pred(); if (itSearch.valid()) { while (itSearch.valid() && ( (*itCand)->size() > (*itSearch)->size()) ) { --itSearch; } //If valid, move behind itSearch, else move to front if (!itSearch.valid()) cliqueList.moveToFront(itCand); else cliqueList.moveToSucc(itCand, itSearch); }//if valid break; }//if node fits into node set }//if sufficient degree //hier kann man mit else breaken, wenn Liste immer sortiert ist ++itCand; }//while clique candidates //create a new candidate if necessary if (!setFound) { List<node>* cliqueCandidate = OGDF_NEW List<node>(); itCand = cliqueList.pushBack(cliqueCandidate); OGDF_ASSERT(m_usedNode[*itNode] == false) cliqueCandidate->pushBack(*itNode); m_usedNode[(*itNode)] = true; }//if no candidate yet ++itNode; }//while valid //TODO: cliquelist vielleicht durch einen member ersetzen //und nicht das delete vergessen! #ifdef OGDF_DEBUG //int numC1 = cliqueList.size(); //int nodeNum = 0; //for (List<node> *pL : cliqueList) //{ // if ( pL->size() > minDegree ) // nodeNum = nodeNum + pL->size(); //} checkCliques(cliqueList, false); //double realTime; //ogdf::usedTime(realTime); #endif postProcessCliques(cliqueList, usableEdge); #ifdef OGDF_DEBUG //realTime = ogdf::usedTime(realTime); //int nodeNum2 = 0; //for (List<node> *pL : cliqueList) //{ // if ( pL->size() > minDegree ) // nodeNum2 = nodeNum2 + pL->size(); //} //if (nodeNum2 > nodeNum) //{ // cout<<"\nAnzahl Cliquen vor PP: "<<numC1<<"\n"; // cout<<"Anzahl Cliquen nach PP: "<<cliqueList.size()<<"\n"; // cout<<"Anzahl Knoten in grossen Cliquen: "<<nodeNum<<"\n"; // cout<<"Anzahl Knoten danach in grossen Cliquen: "<<nodeNum2<<"\n\n"; //} //cout << "Used postprocessing time: " << realTime << "\n" << flush; checkCliques(cliqueList, false); #endif //now we run through the list until the remaining node sets //are to small to be of interest for(List<node> *pCand : cliqueList) { if ( pCand->size() <= minDegree ) break; for(node u : *pCand) { OGDF_ASSERT(m_copyCliqueNumber[u] == -1) m_copyCliqueNumber[u] = m_numberOfCliques; }//for clique nodes m_numberOfCliques++; } //TODO: only set numbers if return value is not a list //of clique node lists setResults(cliqueList); //free the allocated memory for(List<node> *pCl : cliqueList) { delete pCl; } }//if