bool LocalBiconnectedMerger::canMerge( Graph &G, node parent, node mergePartner, int testStrength ) { if ( parent->degree() <= 2 || mergePartner->degree() <= 2 || m_isCut[parent] || m_isCut[mergePartner] ) { return true; } unsigned int nodeLimit = (int)log((double)G.numberOfNodes()) * 2 + 50; unsigned int visitedNodes = 0; m_realNodeMarks.clear(); HashArray<node, int> nodeMark(-1); HashArray<node, bool> seen(false); seen[parent] = true; seen[mergePartner] = true; HashArray<node, int> neighborStatus(0); neighborStatus[parent] = -1; neighborStatus[mergePartner] = -1; List<node> bfsQueue; List<node> neighbors; int minIndex = numeric_limits<int>::max(); adjEntry adj; forall_adj(adj, parent) { node temp = adj->twinNode(); bfsQueue.pushBack(temp); nodeMark[temp] = temp->index(); if(neighborStatus[temp] == 0) { neighbors.pushBack(temp); neighborStatus[temp] = 1; if (temp->index() < minIndex) { minIndex = temp->index(); } } }
void SubgraphUpwardPlanarizer::dfsMerge( const GraphCopy &GC, BCTree &BC, NodeArray<GraphCopy> &biComps, NodeArray<UpwardPlanRep> &uprs, UpwardPlanRep &UPR_res, node parent_BC, node current_BC, NodeArray<bool> &nodesDone) { // only one component. if (current_BC->degree() == 0) { merge(GC, UPR_res, biComps[current_BC], uprs[current_BC]); return; } for(adjEntry adj : current_BC->adjEntries) { node next_BC = adj->twin()->theNode(); if (BC.typeOfBNode(current_BC) == BCTree::CComp) { if (parent_BC != nullptr && !nodesDone[parent_BC]) { merge(GC, UPR_res, biComps[parent_BC], uprs[parent_BC]); nodesDone[parent_BC] = true; } if (!nodesDone[next_BC]) { merge(GC, UPR_res, biComps[next_BC], uprs[next_BC]); nodesDone[next_BC] = true; } } if (next_BC != parent_BC ) dfsMerge(GC, BC, biComps, uprs, UPR_res, current_BC, next_BC, nodesDone); } }
//insert a copy for original node v void SimpleIncNodeInserter::insertCopyNode(node v, Graph::NodeType vTyp) { OGDF_ASSERT(m_planRep->copy(v) == 0) //insert a new node copy node vCopy = m_planRep->newCopy(v, vTyp); if (v->degree() == 0) return; //insert all adjacent edges to already inserted nodes adjEntry adjOrig = v->firstAdj(); do { node wOrig = adjOrig->twinNode(); node wCopy = m_planRep->copy(wOrig); edge e = adjOrig->theEdge(); if (wCopy && (m_planRep->chain(e).size() == 0)) { //inserted edge copy //edge eCopy; //newCopy can cope with zero value for adjEntry if (v == e->source()) /* eCopy = */ m_planRep->newCopy(vCopy, wCopy->firstAdj(), e); else /* eCopy = */ m_planRep->newCopy(wCopy, vCopy->firstAdj(), e); //TODO: update component number in planrepinc }//if edge to be inserted adjOrig = adjOrig->cyclicSucc(); } while (adjOrig != v->firstAdj()); }//insertCopyNode
void biconnectivity::new_start_handler(graph& /*G*/, node& st) { cut_count[st] = -1; // // If this node has no adjacent edges, we // must write down the component right here. This is because // then the method after_recursive_call_handle is never // executed. // // 28/2/2002 MR // if (st.degree() == 0) { ++num_of_components; if (store_comp) { component_iterator li = components.insert( components.end(), std::pair<nodes_t, edges_t>(nodes_t(), edges_t())); li->first.push_back(st); in_component[st] = li; } } }
void UpwardPlanRep::constructSinkArcs(face f, node t) { List<adjEntry> srcList; if (f != m_Gamma.externalFace()) { for(adjEntry adj : f->entries) { node v = adj->theNode(); if (v == adj->theEdge()->target() && v == adj->faceCyclePred()->theEdge()->target() && v != t) { srcList.pushBack(adj); // XXX: where is adjTgt used later? // do we have to set adjTgt = adj (top-sink-switch of f) if v != t? } } // contruct the sink arcs while(!srcList.empty()) { adjEntry adjSrc = srcList.popFrontRet(); edge eNew; if (t->degree() == 0) eNew = m_Gamma.addEdgeToIsolatedNode(adjSrc, t); else { adjEntry adjTgt = getAdjEntry(m_Gamma, t, m_Gamma.rightFace(adjSrc)); eNew = m_Gamma.splitFace(adjSrc, adjTgt); } m_isSinkArc[eNew] = true; } } else { for(adjEntry adj : f->entries) { node v = adj->theNode(); OGDF_ASSERT(s_hat != nullptr); if (v->outdeg() == 0 && v != t_hat) srcList.pushBack(adj); } // contruct the sink arcs while(!srcList.empty()) { adjEntry adjSrc = srcList.popFrontRet(); adjEntry adjTgt; if (adjSrc->theNode() == adjSrc->theEdge()->source()) // on the right face part of the ext. face adjTgt = extFaceHandle; else adjTgt = extFaceHandle->cyclicPred(); // on the left face part auto eNew = m_Gamma.splitFace(adjSrc, adjTgt); m_isSinkArc[eNew] = true; } } }
forall_nodes(v,PG) { if (PG.typeOf(v) == PlanRepUML::dummy && v->degree() == 4) { adjEntry adj = v->firstAdj(); edge e1 = adj->theEdge(); edge e2 = adj->succ()->theEdge(); if (PG.typeOf(e1) == Graph::generalization && PG.typeOf(e2) == Graph::generalization) return false; } }
void PlanRep::removeCrossing(node v) { OGDF_ASSERT(v->degree() == 4) OGDF_ASSERT(isCrossingType(v)) adjEntry a1 = v->firstAdj(); adjEntry b1 = a1->cyclicSucc(); adjEntry a2 = b1->cyclicSucc(); adjEntry b2 = a2->cyclicSucc(); removeUnnecessaryCrossing(a1, a2, b1, b2); }//removeCrossing
bool MultilevelGraph::postMerge(NodeMerge * NM, node merged) { // merged has no more edges! int index = merged->index(); if (merged->degree() == 0 && NM->m_changedNodes.size() > 0) { NM->m_mergedNode = index; NM->m_radius[index] = m_radius[index]; m_changes.push_back(NM); m_G->delNode(merged); m_reverseNodeIndex[index] = nullptr; return true; } else { return false; } }
void CombinatorialEmbedding::removeDeg1(node v) { OGDF_ASSERT(v->degree() == 1); adjEntry adj = v->firstAdj(); face f = m_rightFace[adj]; if (f->entries.m_adjFirst == adj || f->entries.m_adjFirst == adj->twin()) f->entries.m_adjFirst = adj->faceCycleSucc(); f->m_size -= 2; m_pGraph->delNode(v); OGDF_ASSERT_IF(dlConsistencyChecks, consistencyCheck()); }
//-- //----------------- //incremental stuff //special version of the above function doing a pushback of the new edge //on the adjacency list of v making it possible to insert new degree 0 //nodes into a face, end node v edge CombinatorialEmbedding::splitFace(adjEntry adjSrc, node v) { adjEntry adjTgt = v->lastAdj(); edge e = nullptr; bool degZ = v->degree() == 0; if (degZ) { e = m_pGraph->newEdge(adjSrc, v); } else { OGDF_ASSERT(m_rightFace[adjSrc] == m_rightFace[adjTgt]) OGDF_ASSERT(adjSrc != adjTgt) e = m_pGraph->newEdge(adjSrc, adjTgt); //could use ne(v,ad) here, too } face f1 = m_rightFace[adjSrc]; //if v already had an adjacent edge, we split the face in two faces int subSize = 0; if (!degZ) { face f2 = createFaceElement(adjTgt); adjEntry adj = adjTgt; do { m_rightFace[adj] = f2; f2->m_size++; adj = adj->faceCycleSucc(); } while (adj != adjTgt); subSize = f2->m_size; }//if not zero degree else { m_rightFace[e->adjSource()] = f1; } f1->entries.m_adjFirst = adjSrc; f1->m_size += (2 - subSize); m_rightFace[e->adjTarget()] = f1; OGDF_ASSERT_IF(dlConsistencyChecks, consistencyCheck()); return e; }//splitface
//compute a drawing of the clique around node center and save its size //the call to circular will later be replaced by an dedicated computation DRect UMLGraph::circularBound(node center) { //TODO: hier computecliqueposition(0,...) benutzen, rest weglassen DRect bb; CircularLayout cl; Graph G; GraphAttributes AG(G); NodeArray<node> umlOriginal(G); //TODO: we need to assure that the circular drawing //parameters fit the drawing parameters of the whole graph //umlgraph clique parameter members? OGDF_ASSERT(center->degree() > 0) node lastNode = nullptr; node firstNode = nullptr; adjEntry ae = center->firstAdj(); do { node w = ae->twinNode(); node v = G.newNode(); umlOriginal[v] = w; if (!firstNode) firstNode = v; AG.width(v) = width(w); AG.height(v) = height(w); ae = ae->cyclicSucc(); if (lastNode != nullptr) G.newEdge(lastNode, v); lastNode = v; } while (ae != center->firstAdj()); G.newEdge(lastNode, firstNode); cl.call(AG); for(node v : G.nodes) { m_cliqueCirclePos[umlOriginal[v]] = DPoint(AG.x(v), AG.y(v)); } bb = AG.boundingBox(); return bb; }//circularBound
static inline bool writeNode( std::ostream &out, const int &depth, const GraphAttributes *GA, const node &v ) { // Write a node iff it has some attributes or has no edges. if(!GA && v->degree() > 0) { return false; } GraphIO::indent(out, depth) << v; if(GA) { out << " "; writeAttributes(out, *GA, v); } out << "\n"; return true; }
m_outv .init (E, 0); m_oute .init (E, 0); m_seqp .init (E, 0); m_virtSrc .init (E, 0); m_fLink .init (E, ListIterator<face>()); m_fUpdate .init (E, false); m_isSf .init (E, false); m_outerNodes .init (E); m_onBase .init (G, false); initVInFStruct(E); // initialization of degree node v, w; forall_nodes(v,G) m_deg[v] = v->degree(); // initialization of m_onBase[v] adjEntry adj; for(adj = m_adjRight; adj != m_adjLeft; adj = adj->faceCyclePred()) m_onBase[adj->theNode()] = true; m_onBase [m_vLeft] = m_onBase [m_vRight] = true; adj = m_adjLeft; do { v = adj->theNode(); adjEntry adj2; forall_adj(adj2,v) { face f = E.rightFace(adj2); if (f != m_extFace) {
std::vector<edge> MultilevelGraph::moveEdgesToParent(NodeMerge * NM, node theNode, node parent, bool deleteDoubleEdges, int adjustEdgeLengths) { OGDF_ASSERT(theNode != parent); std::vector<edge> doubleEdges; std::vector<edge> adjEdges; for(adjEntry adj : theNode->adjEntries) { adjEdges.push_back(adj->theEdge()); } double nodeToParentLen = 0.0; for (edge e : adjEdges) { node newSource = e->source(); node newTarget = e->target(); if ((newSource == theNode && newTarget == parent) || (newSource == parent && newTarget == theNode)){ nodeToParentLen = m_weight[e]; break; } } for (edge e : adjEdges) { node newSource = e->source(); node newTarget = e->target(); if (newSource == theNode) { newSource = parent; } if (newTarget == theNode) { newTarget = parent; } bool exists = false; edge twinEdge = nullptr; for(adjEntry adj : parent->adjEntries) { if (adj->twinNode() != parent && (adj->twinNode() == newSource || adj->twinNode() == newTarget)) { exists = true; twinEdge = adj->theEdge(); double extraLength = 0.0; if(adjustEdgeLengths != 0) { extraLength = m_weight[twinEdge] + adjustEdgeLengths * nodeToParentLen; } changeEdge(NM, twinEdge, (m_weight[twinEdge] + m_weight[e] + extraLength) * 0.5f, twinEdge->source(), twinEdge->target()); break; } } // has this edge already if (exists || newSource == newTarget) { doubleEdges.push_back(e); } else { changeEdge(NM, e, m_weight[e], newSource, newTarget); } } if (deleteDoubleEdges) { while (!doubleEdges.empty()) { deleteEdge(NM, doubleEdges.back()); doubleEdges.pop_back(); } } OGDF_ASSERT(theNode->degree() == (int)doubleEdges.size()); // not deleted edges that are adjacent to theNode are returned. return doubleEdges; }
//computes relative positions of all nodes in List cList on a minimum size //circle (needed to compute positions with different ordering than given in *this). //Precondition: nodes in adjNodes are adjacent to center //first node in adjNodes is positioned to the right void UMLGraph::computeCliquePosition(List<node> &adjNodes, node center, double rectMin)//, const adjEntry &startAdj) { DRect boundingBox; OGDF_ASSERT(center->degree() > 0) OGDF_ASSERT(center->degree() == adjNodes.size()) node v; double radius = 0.0; //TODO: member, parameter //const double minDist = 1.0; //TODO: necessary? double minCCDist = 20.0; ListIterator<node> itNode = adjNodes.begin(); //-------------------------------------------------------------------------- //for the temporary solution (scale clique to fixed rect if possible instead //of guaranteeing the rect size in compaction) we check in advance if the sum //of diameters plus dists fits into the given rect by heuristic estimate (biggest //node size + radius) if (rectMin > 0.0) { double rectDist = m_cliqueCenterSize; //dist to rect border todo: parameter double rectBound = rectMin - 2.0*rectDist; double maxSize = 0.0; double pureSumDiameters = 0.0; while (itNode.valid()) { node q =(*itNode); double d = sqrt( width(q)*width(q) + height(q)*height(q)); pureSumDiameters += d; if (d > maxSize) maxSize = d; ++itNode; }//while double totalSum = pureSumDiameters+(center->degree()-1)*minDist; //TODO: scling, not just counting while (totalSum/Math::pi < rectBound*0.75) { minDist = minDist + 1.0; totalSum += (center->degree()-1.0); }//while if (minDist > 1.1) minDist -= 1.0; //do not use larger value than cliquecentersize (used with separation) //if (minDist > m_cliqueCenterSize) minDist = m_cliqueCenterSize; itNode = adjNodes.begin(); } //temporary part ends------------------------------------------------------- //------------------------------------------ //first, we compute the radius of the circle const int n = center->degree(); //sum of all diameters around the nodes and the max diameter radius double sumDiameters = 0.0, maxR = 0; //list of angles for all nodes List<double> angles; //node at startAdj gets 0.0 double lastDiameter = 0.0; //temporary storage of space needed for previous node bool first = true; while (itNode.valid()) { v =(*itNode); double d = sqrt( width(v)*width(v) + height(v)*height(v)); sumDiameters += d; if (d/2.0 > maxR) maxR = d/2.0; //save current position relative to startadj //later on, compute angle out of these values if (first) { angles.pushBack(0.0); first = false; } else { angles.pushBack(lastDiameter+d/2.0+minDist+angles.back()); } lastDiameter = d/2.0; //its only half diameter... ++itNode; }//while OGDF_ASSERT(adjNodes.size() == angles.size()) if(n == 1) { radius = 0; } else if (n == 2) { radius = 0.5*minDist + sumDiameters / 4; } else { double perimeter = (n*minDist + sumDiameters); radius = perimeter / (2*Math::pi); ListIterator<double> it = angles.begin(); itNode = adjNodes.begin(); while (it.valid()) { (*it) = (*it)*360.0/perimeter; node w = *itNode; double angle = Math::pi*(*it)/180.0; m_cliqueCirclePos[w].m_x = radius*cos(angle); m_cliqueCirclePos[w].m_y = radius*sin(angle); ++itNode; ++it; }//while }//if n>2 //now we normalize the values (start with 0.0) and //derive the bounding box v = adjNodes.front(); double minX = m_cliqueCirclePos[v].m_x, maxX = m_cliqueCirclePos[v].m_x; double minY = m_cliqueCirclePos[v].m_y, maxY = m_cliqueCirclePos[v].m_y; itNode = adjNodes.begin(); while (itNode.valid()) { node w = *itNode; double wx = m_cliqueCirclePos[w].m_x; double wy = m_cliqueCirclePos[w].m_y; if(wx-width (w)/2.0 < minX) minX = wx-width(w)/2.0; if(wx+width (w)/2.0 > maxX) maxX = wx+width(w)/2.0; if(wy-height(w)/2.0 < minY) minY = wy-height(w)/2.0; if(wy+height(w)/2.0 > maxY) maxY = wy+height(w)/2.0; ++itNode; } //allow distance minX -= minCCDist; minY -= minCCDist; //normalize //cout<<"\n"; itNode = adjNodes.begin(); while (itNode.valid()) { node w = *itNode; //cout<<"x1:"<<m_cliqueCirclePos[w].m_x<<":y:"<<m_cliqueCirclePos[w].m_y<<"\n"; m_cliqueCirclePos[w].m_x -= minX; m_cliqueCirclePos[w].m_y -= minY; //cout<<"x:"<<m_cliqueCirclePos[w].m_x<<":y:"<<m_cliqueCirclePos[w].m_y<<"\n"; ++itNode; } //reassign the size, this time it is the final value m_cliqueCircleSize[center] = DRect(0.0, 0.0, maxX-minX, maxY-minY); }//computecliqueposition
// Returns conductance of best cluster double ModifiedNibbleClusterer::findBestCluster(NodeArray<bool> &isActive, std::vector<node> &activeNodes, std::vector<node> &cluster) { ArrayBuffer< Prioritized<int> > sortedPairs((int)activeNodes.size()); cluster.clear(); // collect pairs and sort them for (int i = 0; i < (int)activeNodes.size(); ++i) { const node v = activeNodes.at(i); sortedPairs.push(Prioritized<int>(i, - m_prob[v] / v->degree())); } sortedPairs.quicksort(); int maxSize = min(static_cast<int>(activeNodes.size()), static_cast<int>(m_maxClusterSize)); // Now we search for the best cluster in the list (paper description is ambiguous) NodeArray<bool> inCluster(*m_pGC, false); // costs dearly but hard to avoid without knowing sizes // Save list entry in frontier list of current set to delete nodes that are added to the list NodeArray< ListIterator<node> > frontierEntry(*m_pGC, nullptr); List<node> frontier; //frontier of current set (next node in list doesn't need to be a member!) // Save nodes that become abandoned, i.e. only have neighbors in a current cluster set. // These will never again become non-abandoned, i.e. can be collected, but could become // part of the cluster, i.e. we need to take care as there might be duplicates in cluster plus // abandoned list (but deleting would make it impossible to simply store the index for the solution). std::vector<node> abandoned; NodeArray<bool> wasAbandoned(*m_pGC, false); // As the cluster and the list of abandoned nodes might overlap, we cannot simply add their size long numRealAband = 0; int volume = 0; long cutSize = 0; long bestindex = 0; double bestConductance = std::numeric_limits<double>::max(); long bestindexaband = -1; for (int run = 0; run < maxSize; ++run) { //Check the conductance of the current set node next = activeNodes.at(sortedPairs[run].item()); inCluster[next] = true; OGDF_ASSERT(numRealAband >= 0); //in case the node was in our frontier make sure we won't consider it later if (!(((ListIterator<node>)frontierEntry[next]) == (ListIterator<node>)nullptr)){ frontier.del(frontierEntry[next]); frontierEntry[next] = (ListIterator<node>)nullptr; //will never be used again } //volume changes by degree of node if not already taken into account // as previously abandoned node if (wasAbandoned[next]) numRealAband--; else volume += next->degree(); //cutsize changes according to new nodes adjacency for(adjEntry adj : next->adjEntries){ node w = adj->theEdge()->opposite(next); if (inCluster[w]) { cutSize--; } else { cutSize++; frontierEntry[w] = frontier.pushBack(w); } } // We add the abandoned nodes here, i.e. nodes that only have // neighbors in the current set left. ListIterator<node> itn = frontier.begin(); while (itn.valid()) { node t = (*itn); if (wasAbandoned[t]) { ++itn; continue; } bool aband = true; for(adjEntry adj : t->adjEntries){ if (!inCluster[adj->theEdge()->opposite(t)]) { aband = false; break; } } if (aband) { wasAbandoned[t] = true; abandoned.push_back(t); numRealAband++; // Abandoned nodes are part of the prospective cluster // Adding them does not change the cut, but the volume volume += t->degree(); } ++itn; } OGDF_ASSERT(cutSize >= 0); if (run + numRealAband > m_maxClusterSize) break; // Can only get bigger now // Calculate conductance double conductance = static_cast<double>(cutSize) / static_cast<double>(min(volume, max(1, 2*m_pGC->numberOfEdges() - volume))); if (conductance < bestConductance) { bestConductance = conductance; bestindex = run; bestindexaband = (long)(abandoned.size()-1); } } // Put together our result for (int run = 0; run <= bestindex; ++run) { node next = activeNodes.at(sortedPairs[run].item()); if (!wasAbandoned[next]) cluster.push_back(next); } for (int run = 0; run <= bestindexaband; ++run) { cluster.push_back(abandoned.at(run)); } #if 0 std::cout << "Cluster found "<<cluster.size()<< " " << bestConductance<<"\n"; #endif #ifdef OGDF_DEBUG NodeArray<bool> test(*m_pGC, false); for (node v : cluster) { OGDF_ASSERT(!test[v]); test[v] = true; } #endif return bestConductance; }