//to be called AFTER calling read(G, AG) bool GmlParser::readAttributedCluster( Graph &G, ClusterGraph& CG, ClusterGraphAttributes& ACG) { OGDF_ASSERT(&CG.constGraph() == &G) //now we need the cluster object GmlObject *rootObject = m_objectTree; for(; rootObject; rootObject = rootObject->m_pBrother) if (id(rootObject) == rootClusterPredefKey) break; if(rootObject == nullptr) return true; if (id(rootObject) != rootClusterPredefKey) { setError("missing rootcluster key"); return false; } if (rootObject->m_valueType != gmlListBegin) return false; attributedClusterRead(rootObject, CG, ACG); return true; }//readAttributedCluster
// write cluster graph structure with clusters static void write_ogml_graph(const ClusterGraph &C, ostream &os) { GraphIO::indent(os,2) << "<structure>\n"; write_ogml_graph(C.rootCluster(), 0, os); write_ogml_graph_edges(C.constGraph(), os); GraphIO::indent(os,2) << "</structure>\n"; }
void CPlanarSubClusteredST::call(const ClusterGraph &CG, EdgeArray<bool>& inST) { initialize(CG); inST.fill(false); //representationsgraphs for every cluster, on clustergraph ClusterArray<Graph*> l_clusterRepGraph(CG, nullptr); computeRepresentationGraphs(CG, l_clusterRepGraph); //now we compute the spanning trees on the representation graphs //we should save the selection info on the original edge //are statically on the repgraphedges (we only have edge -> repedge //information) but ClusterArray< EdgeArray<bool> > l_inTree(CG); for(cluster c : CG.clusters) { l_inTree[c].init(*l_clusterRepGraph[c], false); //compute STs NodeArray<bool> visited(*l_clusterRepGraph[c], false); dfsBuildSpanningTree(l_clusterRepGraph[c]->firstNode(), l_inTree[c], visited); } OGDF_ASSERT(isConnected(CG.constGraph())); //compute the subclustered graph by constructing a spanning tree //using only the representation edges used in STs on the repgraphs NodeArray<bool> visited(CG, false); dfsBuildOriginalST(CG.constGraph().firstNode(), l_inTree, inST, visited); //unregister the edgearrays to avoid destructor failure after //representation graph deletion for(cluster c : CG.clusters) { l_inTree[c].init(); } deleteRepresentationGraphs(CG, l_clusterRepGraph); }
// true <=> C is C-connected bool isCConnected(const ClusterGraph &C) { if(C.constGraph().empty()) return true; Graph G; ClusterGraph Cp(C,G); cluster root = Cp.rootCluster(); SListPure<node> compNodes; NodeArray<bool> mark(G,false); return cConnectTest(Cp,root,mark,G); }
bool GraphIO::writeDOT(const ClusterGraph &C, std::ostream &out) { const Graph &G = C.constGraph(); int id = 1; // Assign a list of edges for each cluster. Perhaps usage of std::vector // here needs reconsideration - vector is fast but usage of STL iterators // is ugly without C++11 for-each loop. ClusterArray< std::vector<edge> > edgeMap(C); for(edge e : G.edges) { const node s = e->source(), t = e->target(); edgeMap[C.commonCluster(s, t)].push_back(e); } return dot::writeCluster(out, 0, edgeMap, C, nullptr, C.rootCluster(), id); }
ClusterGraph::ClusterGraph(const ClusterGraph &C) : GraphObserver(&(C.constGraph())), m_lcaSearch(0), m_vAncestor(0), m_wAncestor(0) { m_clusterIdCount = 0; m_postOrderStart = 0; m_rootCluster = 0; m_allowEmptyClusters = true; m_updateDepth = false; m_depthUpToDate = false; m_nClusters = 0; m_lcaNumber = 0; m_clusterArrayTableSize = C.m_clusterArrayTableSize; shallowCopy(C); }
static void writeCluster( std::ostream &out, int depth, const ClusterGraph &C, const ClusterGraphAttributes *CA, cluster c) { if(C.rootCluster() != c) { GraphIO::indent(out, depth) << "<node " << "id=\"cluster" << c->index() << "\"" << ">\n"; } else { const std::string dir = (CA && !CA->directed()) ? "undirected" : "directed"; GraphIO::indent(out, depth) << "<graph " << "mode=\"static\"" << "defaultedgetype=\"" << dir << "\"" << ">\n"; if(CA) { defineAttributes(out, depth + 1, *CA); } } GraphIO::indent(out, depth + 1) << "<nodes>\n"; for(ListConstIterator<cluster> cit = c->cBegin(); cit.valid(); ++cit) { writeCluster(out, depth + 2, C, CA, *cit); } for(ListConstIterator<node> nit = c->nBegin(); nit.valid(); ++nit) { writeNode(out, depth + 2, CA, *nit); } GraphIO::indent(out, depth + 1) << "</nodes>\n"; if(C.rootCluster() != c) { GraphIO::indent(out, depth) << "</node>\n"; } else { writeEdges(out, C.constGraph(), CA); GraphIO::indent(out, depth) << "</graph>\n"; } }
//the clustergraph has to be initialized on G!!, //no clusters other then root cluster may exist, which holds all nodes bool GmlParser::readCluster(Graph &G, ClusterGraph& CG) { OGDF_ASSERT(&CG.constGraph() == &G) //now we need the cluster object GmlObject *rootObject = m_objectTree; for(; rootObject; rootObject = rootObject->m_pBrother) if (id(rootObject) == rootClusterPredefKey) break; //we have to check if the file does really contain clusters //otherwise, rootcluster will suffice if (rootObject == nullptr) return true; if (id(rootObject) != rootClusterPredefKey) { setError("missing rootcluster key"); return false; } if (rootObject->m_valueType != gmlListBegin) return false; clusterRead(rootObject, CG); return true; }//read clustergraph
void CPlanarSubClusteredST::call(const ClusterGraph& CG, EdgeArray<bool>& inST, EdgeArray<double>& weight) { initialize(CG); //representationsgraphs for every cluster, on clustergraph ClusterArray<Graph*> l_clusterRepGraph(CG, nullptr); computeRepresentationGraphs(CG, l_clusterRepGraph); //Now we compute the spanning trees on the representation graphs //are statically on the repgraphedges (we only have edge -> repedge //information) ClusterArray< EdgeArray<bool> > l_inTree(CG); //Weight of the representation edges ClusterArray< EdgeArray<double> > l_repWeight(CG); //Copy the weight for(cluster c : CG.clusters) { l_repWeight[c].init(*l_clusterRepGraph[c], 0.0); } for(edge e : CG.constGraph().edges) { l_repWeight[m_allocCluster[e]][m_repEdge[e]] = weight[e]; } for(cluster c : CG.clusters) { l_inTree[c].init(*l_clusterRepGraph[c], false); //compute STs computeMinST(*l_clusterRepGraph[c], l_repWeight[c], l_inTree[c]); } OGDF_ASSERT(isConnected(CG.constGraph())); //Compute the subclustered graph for(edge e : CG.constGraph().edges) { if (l_inTree[m_allocCluster[e]][m_repEdge[e]]) inST[e] = true; else inST[e] = false; } #ifdef OGDF_DEBUG GraphCopy cg(CG.constGraph()); for(edge e : CG.constGraph().edges) { if (!inST[e]) cg.delEdge(cg.copy(e)); } OGDF_ASSERT(isConnected(cg)); OGDF_ASSERT(cg.numberOfEdges() == cg.numberOfNodes()-1); #endif //unregister the edgearrays to avoid destructor failure after //representation graph deletion for(cluster c : CG.clusters) { l_inTree[c].init(); l_repWeight[c].init(); } deleteRepresentationGraphs(CG, l_clusterRepGraph); }
MaxCPlanarMaster::MaxCPlanarMaster( const ClusterGraph &C, int heuristicLevel, int heuristicRuns, double heuristicOEdgeBound, int heuristicNPermLists, int kuratowskiIterations, int subdivisions, int kSupportGraphs, double kHigh, double kLow, bool perturbation, double branchingGap, const char *time, bool dopricing, bool checkCPlanar, int numAddVariables, double strongConstraintViolation, double strongVariableViolation) : Master("MaxCPlanar", true, dopricing, OptSense::Max), m_numAddVariables(numAddVariables), m_strongConstraintViolation(strongConstraintViolation), m_strongVariableViolation(strongVariableViolation), m_fastHeuristicRuns(25), m_cutConnPool(nullptr), m_cutKuraPool(nullptr), m_useDefaultCutPool(true), m_checkCPlanar(checkCPlanar), m_porta(false) { // Reference to the given ClusterGraph and the underlying Graph. m_C = &C; m_G = &(C.constGraph()); // Create a copy of the graph as we need to modify it m_solutionGraph = new GraphCopy(*m_G); // Define the maximum number of variables needed. // The actual number needed may be much smaller, so there // is room for improvement... //ToDo: Just count how many vars are added //Max number of edges //KK: Check this change, added div 2 int nComplete = (m_G->numberOfNodes()*(m_G->numberOfNodes()-1)) / 2; m_nMaxVars = nComplete; //to use less variables in case we have only the root cluster, //we temporarily set m_nMaxVars to the number of edges if ( (m_C->numberOfClusters() == 1) && (isConnected(*m_G)) ) m_nMaxVars = m_G->numberOfEdges(); // Computing the main objective function coefficient for the connection edges. //int nConnectionEdges = nComplete - m_G->numberOfEdges(); m_epsilon = (double)(0.2/(2*(m_G->numberOfNodes()))); // Setting parameters m_nKuratowskiIterations = kuratowskiIterations; m_nSubdivisions = subdivisions; m_nKuratowskiSupportGraphs = kSupportGraphs; m_heuristicLevel = heuristicLevel; m_nHeuristicRuns = heuristicRuns; m_usePerturbation = perturbation; m_kuratowskiBoundHigh = kHigh; m_kuratowskiBoundLow = kLow; m_branchingGap = branchingGap; m_maxCpuTime = new string(time); m_heuristicFractionalBound = heuristicOEdgeBound; m_nHeuristicPermutationLists = heuristicNPermLists; m_mpHeuristic = true; // Further settings m_nCConsAdded = 0; m_nKConsAdded = 0; m_solvesLP = 0; m_varsInit = 0; m_varsAdded = 0; m_varsPotential = 0; m_varsMax = 0; m_varsCut = 0; m_varsKura = 0; m_varsPrice = 0; m_varsBranch = 0; m_activeRepairs = 0; m_repairStat.init(100); }
void CPlanarSubClusteredGraph::call(const ClusterGraph &CGO, EdgeArray<bool>& inSub, //original edges in subgraph? List<edge>& leftOver, //original edges not in subgraph EdgeArray<double>& edgeWeight) //prefer lightweight edges { leftOver.clear(); //we compute a c-planar subclustered graph by calling //CPlanarSubClusteredST and then perform reinsertion on //a copy of the computed subclustered graph //initialize "call-global" info arrays //edge status const Graph& origG = CGO.constGraph(); m_edgeStatus.init(origG, 0); CPlanarSubClusteredST CPST; if (edgeWeight.valid()) CPST.call(CGO, inSub, edgeWeight); else CPST.call(CGO, inSub); //now construct the copy //we should create a clusterGraph copy function that //builds a clustergraph upon a subgraph of the //original graph, preliminarily use fullcopy and delete edges ClusterArray<cluster> clusterCopy(CGO); NodeArray<node> nodeCopy(origG); EdgeArray<edge> edgeCopy(origG); Graph testG; ClusterGraph CG(CGO, testG, clusterCopy, nodeCopy, edgeCopy); CconnectClusterPlanar CCCP; //------------------------------------- //perform reinsertion of leftover edges //fill list of uninserted edges EdgeArray<bool> visited(origG,false); //delete the non-ST edges edge e; forall_edges(e, origG) { if (!inSub[e]) { leftOver.pushBack(e); //original edges testG.delEdge(edgeCopy[e]); }//if }//foralledges //todo: cope with preferred edges //simple reinsertion strategy: just iterate over list and test ListIterator<edge> itE = leftOver.begin(); while (itE.valid()) { //testG=CG.getGraph() edge newCopy = testG.newEdge(nodeCopy[(*itE)->source()], nodeCopy[(*itE)->target()]); edgeCopy[*itE] = newCopy; bool cplanar = CCCP.call(CG); if (!cplanar) { testG.delEdge(newCopy); itE++; }//if else { ListIterator<edge> itDel = itE; itE++; leftOver.del(itDel); } }//while /* ListConstIterator<edge> it; for(it = preferedEdges.begin(); it.valid(); ++it) { edge eG = *it; visited[eG] = true; edge eH = testG.newEdge(toTestG[eG->source()],toTestG[eG->target()]); if (preferedImplyPlanar == false && isPlanar(H) == false) { testG.delEdge(eH); delEdges.pushBack(eG); } } */ }//call
bool ClusterPlanarity::isClusterPlanar(const ClusterGraph &CG, List<nodePair> &addedEdges) { m_optStatus = Master::Optimal; // We first check if there is more to do then just checking planarity on the // input graph. // Simple shortcut: With < 5 vertices, no non-planarity is possible... bool result = isPlanar(CG.constGraph()); if (!result || (CG.numberOfClusters() == 1)) { // Either non-planar or only root cluster exists, which does not restrict c-planarity. return result; } // We first create a copy of input G, and work solely on the copy // In case of the sm_new solution method, we partition the graph in // independent parts and test them separately // For all parts we test until non-c-planar or all tested. if (m_solmeth==sm_new) { // We use the ClusterAnalysis to search for independent bags // Here is the idea: We detect all bags that are minimum wrt // cluster inclusion (i.e. if a cluster contains a cluster c with // a single bag, we don't add the cluster c itself) but do // not contain an outeractive vertex wrt to smallest containing cluster // (i.e. the cluster used in the definition of bag). // The clustered subgraphs induced by these bags can be tested independently, // as we can move them freely in the drawing area of their enclosing parent cluster. ClusterAnalysis ca(CG, true); //Compute all structures, indyBags too // We can solve the c-planarity testing for all indyBags independently, // and in case all are c-planar, also our input c-graph is c-planar. const int numIndyBags = ca.numberOfIndyBags(); GraphCopy** theGraphs = new GraphCopy * [numIndyBags]; //Stores copies for the bag graphs. #ifdef OGDF_DEBUG cout << "Number of IndyBags "<<numIndyBags<<"\n"; #endif Logger::slout() << "Number of IndyBags "<<numIndyBags<<"\n"; Array<List<node> > nodesInBag(numIndyBags); //Stores vertices for the bag graphs. const Graph & G = CG.constGraph(); for(node v : G.nodes){ nodesInBag[ca.indyBagIndex(v)].pushBack(v); } for (int i = 0; i < numIndyBags && m_optStatus == Master::Optimal; i++) { // Create underlying graph theGraphs[i] = new GraphCopy(); theGraphs[i]->createEmpty(G); // Judging from the interface and the description, there are two // methods in GraphCopy that allow to construct parts based on a // set of vertices, initByNodes and initByActiveNodes, where the // latter one seems to be appropriate and can be used with an // additional 3n work to initialize the NodeArray and mark the vertices. // However, even though the former is meant to be used for connected // components, it also works for set of connected components, and // an independent bag is such a creature. EdgeArray<edge> eCopy(G); theGraphs[i]->initByNodes(nodesInBag[i], eCopy); ClusterGraph bagCG(*theGraphs[i]); //node v; ClusterArray<List<node> > cNodes(CG); ClusterArray<List<cluster> > cChildren(CG); ClusterArray<cluster> cCopy(CG); // Run through all original vertices and store // lists of copies at each cluster that is part of the bag. // Note: We should not add an enclosing parent cluster below // root, i.e., when the root does only have a single child // and no vertices, we delete the child again. for(node u : nodesInBag[i]) { cluster ct = CG.clusterOf(u); cNodes[ct].pushBack(theGraphs[i]->copy(u)); // Check if we need to store the parent relation on the path // to the root. Indicator is: We have just added the first element. while ((ct != CG.rootCluster()) && (cNodes[ct].size() + cChildren[ct].size() == 1)) { cChildren[ct->parent()].pushBack(ct); ct = ct->parent(); } } // Create cluster structure // For each vertex in the indyBag we create the cluster path // to the bag root if necessary. // Now build the cluster structure top down // Lists of root are never both empty List<cluster> queue; queue.pushBack(ca.indyBagRoot(i)); cCopy[queue.front()] = bagCG.rootCluster(); while (!queue.empty()) { cluster c = queue.popFrontRet(); //vertices are assigned to root by construction if (cCopy[c] != bagCG.rootCluster()) { for(node u : cNodes[c]) bagCG.reassignNode(u, cCopy[c]); } for(cluster ci : cChildren[c]) { cCopy[ci] = bagCG.newCluster(cCopy[c]); queue.pushBack(ci); } } #ifdef OGDF_DEBUG Logger::slout() << "Created clustered graph for indy bag with "<<theGraphs[i]->numberOfNodes()<< " nodes and "<< bagCG.numberOfClusters()<<" clusters\n"; // Make sure the cluster structure is a rooted tree cluster t = bagCG.rootCluster(); int ccnt = 0; List<cluster> cqueue; cqueue.pushBack(t); while (!cqueue.empty()) { t = cqueue.popFrontRet(); for(cluster c : t->children) { cqueue.pushBack(c); } ccnt++; } OGDF_ASSERT(ccnt == bagCG.numberOfClusters()); string filename = string("IndySubcgraph") + to_string(i) + ".gml"; ClusterGraphAttributes CGA(bagCG); GraphIO::writeGML(CGA, filename); #endif //now the actual test, similar to the one below... if (theGraphs[i]->numberOfNodes() > 2) //could even start at 4... { makeParallelFreeUndirected(*theGraphs[i]); //Could do this while creating copy Logger::slout()<< "IndyBag of size n m c"<<theGraphs[i]->numberOfNodes()<< " "<< theGraphs[i]->numberOfEdges()<< " "<< bagCG.numberOfClusters()<<"\n"; List<nodePair> ae; //Todo: Add an interface here that allows to transfer bag and // activity information to the master, otherwise we have to // compute this info twice. bool imresult = doTest(bagCG, ae); #ifdef OGDF_DEBUG Logger::slout() << "IndyBag number "<<i<<" is "<< (imresult ? "" : "non-") <<"c-planar\n"; Logger::slout() << "Number of edges added for IndyBag: "<<ae.size()<<"\n"; #endif result = result && imresult; if (!result) return result; for(const nodePair &np : ae) { addedEdges.emplaceBack(theGraphs[i]->original(np.v1), theGraphs[i]->original(np.v2)); } } #ifdef OGDF_DEBUG else { Logger::slout() << "IndyBag number "<<i<<" skipped due to size\n"; } #endif }//for indy bags for (int i = 0; i < numIndyBags; i++) { delete theGraphs[i]; } delete [] theGraphs; // We test consistency by summing up the number of vertices. } else { //todo: can be joined again, as it is a special case wo cluster analysis //otherwise we just make a copy of the whole graph Graph G; ClusterArray<cluster> clusterCopy(CG); NodeArray<node> nodeCopy(CG.constGraph()); EdgeArray<edge> edgeCopy(CG.constGraph()); ClusterGraph C(CG,G,clusterCopy, nodeCopy, edgeCopy); makeParallelFreeUndirected(G); NodeArray<node> nodeOrig(G); for(node v : CG.constGraph().nodes) { nodeOrig[nodeCopy[v]] = v; } //Could use same list here for both graphs. addedEdges.clear(); List<nodePair> ae; result = doTest(C,ae); //nodepairs are for the copy, store original nodes here for (const nodePair &np : ae) { addedEdges.emplaceBack(nodeOrig[np.v1], nodeOrig[np.v2]); } } return result; }
bool ClusterPlanarity::doTest(const ClusterGraph &G, List<nodePair> &addedEdges) { // if (m_solmeth==sm_new) return doFastTest(G,addedEdges); // We could take care of multiedges, but as long this is // not done, we do not allow this. OGDF_ASSERT(isParallelFreeUndirected(G)); // Graph has to be simple #ifdef OGDF_DEBUG cout << "Creating new Masterproblem for clustergraph with "<<G.constGraph().numberOfNodes()<<" nodes\n"; #endif CP_MasterBase* cplanMaster; cplanMaster = new CPlanarityMaster(G, m_heuristicLevel, m_heuristicRuns, m_heuristicOEdgeBound, m_heuristicNPermLists, m_kuratowskiIterations, m_subdivisions, m_kSupportGraphs, m_kuratowskiHigh, m_kuratowskiLow, m_perturbation); if (m_solmeth == sm_new) static_cast<CPlanarityMaster*>(cplanMaster)->setSearchSpaceShrinking(true); else static_cast<CPlanarityMaster*>(cplanMaster)->setSearchSpaceShrinking(false); //new CPlanarMaster(G,m_heuristicLevel,m_heuristicRuns,m_heuristicOEdgeBound,m_heuristicNPermLists,m_kuratowskiIterations, //m_subdivisions,m_kSupportGraphs,m_kuratowskiHigh, m_kuratowskiLow,m_perturbation,m_branchingGap,m_time, m_pricing, //m_numAddVariables,m_strongConstraintViolation,m_strongVariableViolation,m_ol); cplanMaster->setTimeLimit(m_time.c_str()); cplanMaster->setPortaFile(m_portaOutput); cplanMaster->useDefaultCutPool() = m_defaultCutPool; #ifdef OGDF_DEBUG cout << "Starting Optimization\n"; #endif Master::STATUS abastatus; try { abastatus = cplanMaster->optimize(); } catch (...) { #ifdef OGDF_DEBUG cerr << "ABACUS Optimization failed...\n"; #endif } m_optStatus = abastatus; m_totalTime = getDoubleTime(*cplanMaster->totalTime()); m_heurTime = getDoubleTime(*cplanMaster->improveTime()); m_sepTime = getDoubleTime(*cplanMaster->separationTime()); m_lpTime = getDoubleTime(*cplanMaster->lpTime()); m_lpSolverTime = getDoubleTime(*cplanMaster->lpSolverTime()); m_totalWTime = getDoubleTime(*cplanMaster->totalCowTime()); m_numKCons = cplanMaster->addedKConstraints(); m_numCCons = cplanMaster->addedCConstraints(); m_numLPs = cplanMaster->nLp(); m_numBCs = cplanMaster->nSub(); m_numSubSelected = cplanMaster->nSubSelected(); m_numVars = cplanMaster->nMaxVars()-cplanMaster->getNumInactiveVars(); #ifdef OGDF_DEBUG m_solByHeuristic = cplanMaster->m_solByHeuristic; #endif #ifdef OGDF_DEBUG if(cplanMaster->pricing()) Logger::slout() << "Pricing was ON\n"; Logger::slout()<<"ABACUS returned with status '"<< Master::STATUS_[abastatus] <<"'\n"<<flush; #endif cplanMaster->getConnectionOptimalSolutionEdges(addedEdges); //int addE = addedEdges.size(); #ifdef OGDF_DEBUG cout<<"-Number of added edges "<< addedEdges.size()<<"\n"; #endif if (m_portaOutput) { writeFeasible(getPortaFileName(), *cplanMaster, abastatus); } CP_MasterBase::solutionstate status = cplanMaster->m_solState; delete cplanMaster; switch (status) { case CP_MasterBase::ss_cp: return true; break; //Todo: catch and publish errors here case CP_MasterBase::ss_ncp: return false; break; default: cerr<<"** Undefined optimization result for c-planarity computation **\n"; break; }//switch return false; //Todo: Throw error here if eg outofmemory etc }//doTest