Ejemplo n.º 1
0
//---------------------------------------------------------
//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
Ejemplo n.º 2
0
void DfsMakeBiconnected::doCall(Graph &G, List<edge> &L)
{
	makeBiconnected(G,L);
}