void RadialTreeLayout::Grouping::computeAdd(double &D, double &W) { D = W = 0; ListIterator<Group> it; for(it = begin(); it.valid(); ++it) { Group &g = *it; D += g.m_sumD; if(g.m_leafGroup == true) continue; W += g.m_sumW; ListIterator<Group> itL; itL = it.pred(); if(itL.valid() == false) { g.m_leftAdd = 0.0; } else { ListIterator<Group> itR = itL.pred(); if(itR.valid() == false) g.m_leftAdd = (*itL).m_sumD; else g.m_leftAdd = (*itL).m_sumD * g.m_sumW / (*itR).m_sumW; } itL = it.succ(); if(itL.valid() == false) { g.m_leftAdd = 0.0; } else { ListIterator<Group> itR = itL.succ(); if(itR.valid() == false) g.m_leftAdd = (*itL).m_sumD; else g.m_leftAdd = (*itL).m_sumD * g.m_sumW / (*itR).m_sumW; } } }
//--------------------------------------------------------- //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