1 /** \file
2  * \brief Implementation of class ogdf::CliqueFinderModule.
3  *
4  * \author Karsten Klein, Max Ilsen
5  *
6  * \par License:
7  * This file is part of the Open Graph Drawing Framework (OGDF).
8  *
9  * \par
10  * Copyright (C)<br>
11  * See README.md in the OGDF root directory for details.
12  *
13  * \par
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * Version 2 or 3 as published by the Free Software Foundation;
17  * see the file LICENSE.txt included in the packaging of this file
18  * for details.
19  *
20  * \par
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * \par
27  * You should have received a copy of the GNU General Public
28  * License along with this program; if not, see
29  * http://www.gnu.org/copyleft/gpl.html
30  */
31 
32 #include <ogdf/clique/CliqueFinderModule.h>
33 #include <ogdf/basic/simple_graph_alg.h>
34 
35 namespace ogdf {
36 
call(const Graph & G,NodeArray<int> & cliqueNumber)37 void CliqueFinderModule::call(const Graph &G, NodeArray<int> &cliqueNumber)
38 {
39 	beginCall(G);
40 	setResults(cliqueNumber);
41 	endCall();
42 }
43 
call(const Graph & G,List<List<node> * > & cliqueLists)44 void CliqueFinderModule::call(const Graph &G, List<List<node>*> &cliqueLists)
45 {
46 	beginCall(G);
47 	setResults(cliqueLists);
48 	endCall();
49 }
50 
beginCall(const Graph & G)51 void CliqueFinderModule::beginCall(const Graph &G)
52 {
53 	m_pGraph = &G;
54 	m_pCopy = new GraphCopy(G);
55 	makeSimpleUndirected(*m_pCopy); // ignore multi-edges
56 	m_copyCliqueNumber.init(*m_pCopy, -1);
57 
58 	if (!handleTrivialCases()) {
59 		doCall();
60 	}
61 }
62 
endCall()63 void CliqueFinderModule::endCall()
64 {
65 	m_copyCliqueNumber.init();
66 	m_pGraph = nullptr;
67 	delete m_pCopy;
68 }
69 
setResults(NodeArray<int> & cliqueNum)70 void CliqueFinderModule::setResults(NodeArray<int> &cliqueNum)
71 {
72 	cliqueNum.fill(-1);
73 
74 	for (node v : m_pGraph->nodes) {
75 		node w = m_pCopy->copy(v);
76 		// The algorithm may have deleted nodes from m_pCopy.
77 		// These do not belong to any clique.
78 		if (w != nullptr) {
79 			cliqueNum[v] = m_copyCliqueNumber[w];
80 		}
81 	}
82 }
83 
setResults(List<List<node> * > & cliqueLists)84 void CliqueFinderModule::setResults(List<List<node>*> &cliqueLists)
85 {
86 	cliqueLists.clear();
87 
88 	List<List<node>*> copyCliqueLists;
89 	cliqueNumberToList(*m_pCopy, m_copyCliqueNumber, copyCliqueLists);
90 	for (List<node> *copyClique : copyCliqueLists) {
91 		List<node> *clique = new List<node>();
92 		for (node vCopy : *copyClique) {
93 			clique->pushBack(m_pCopy->original(vCopy));
94 		}
95 		cliqueLists.pushBack(clique);
96 		delete copyClique;
97 	}
98 }
99 
handleTrivialCases()100 bool CliqueFinderModule::handleTrivialCases() {
101 	int nodeNum = m_pGraph->numberOfNodes();
102 
103 	// If there are not enough nodes for a clique of min size, return.
104 	if (nodeNum < m_minDegree) {
105 		return true;
106 	}
107 
108 	if (nodeNum < 3) {
109 		node v = m_pCopy->firstNode();
110 		if (nodeNum == 2) {
111 			if (m_minDegree <= 1 && m_pGraph->numberOfEdges() >= 1) {
112 				m_copyCliqueNumber[v] = 0;
113 				m_copyCliqueNumber[v->succ()] = 0;
114 			} else if (m_minDegree == 0) {
115 				m_copyCliqueNumber[v] = 0;
116 				m_copyCliqueNumber[v->succ()] = 1;
117 			}
118 		} else if (nodeNum == 1 && m_minDegree == 0) {
119 			m_copyCliqueNumber[v] = 0;
120 		}
121 
122 		return true;
123 	}
124 
125 	return false;
126 }
127 
cliqueListToNumber(const Graph & G,const List<List<node> * > & cliqueLists,NodeArray<int> & cliqueNumber)128 void CliqueFinderModule::cliqueListToNumber(const Graph &G,
129 		const List<List<node>*> &cliqueLists,
130 		NodeArray<int> &cliqueNumber)
131 {
132 	int nextCliqueNum = 0;
133 	cliqueNumber.init(G, -1);
134 
135 	for (List<node> *clique : cliqueLists) {
136 		for (node v : *clique) {
137 			cliqueNumber[v] = nextCliqueNum;
138 		}
139 		nextCliqueNum++;
140 	}
141 }
142 
cliqueNumberToList(const Graph & G,const NodeArray<int> & cliqueNumber,List<List<node> * > & cliqueLists)143 void CliqueFinderModule::cliqueNumberToList(const Graph &G,
144 		const NodeArray<int> &cliqueNumber,
145 		List<List<node>*> &cliqueLists)
146 {
147 	cliqueLists.clear();
148 
149 	List<node> nodesByCliqueNumber;
150 	G.allNodes(nodesByCliqueNumber);
151 	nodesByCliqueNumber.quicksort(GenericComparer<node, int>(cliqueNumber));
152 
153 	List<node> *curClique = nullptr;
154 	for (auto itNode = nodesByCliqueNumber.begin(); itNode.valid(); itNode++) {
155 		node v = *itNode;
156 		// Ignore nodes with clique number -1.
157 		if (cliqueNumber[v] >= 0) {
158 			// Create a new clique if necessary.
159 			if (curClique == nullptr) {
160 				curClique = new List<node>();
161 			}
162 			curClique->pushBack(v);
163 
164 			// If we are at the end or the next node is in a new clique,
165 			// push the clique to cliqueLists and reset the current clique.
166 			if (!itNode.succ().valid() ||
167 			    cliqueNumber[v] != cliqueNumber[*(itNode.succ())]) {
168 				cliqueLists.pushBack(curClique);
169 				curClique = nullptr;
170 			}
171 		}
172 	}
173 }
174 
cliqueGraphAttributes(const Graph & G,const NodeArray<int> & cliqueNumber,GraphAttributes & GA)175 void CliqueFinderModule::cliqueGraphAttributes(const Graph &G,
176 		const NodeArray<int> &cliqueNumber,
177 		GraphAttributes &GA)
178 {
179 	const int RGB_MAX = 256;
180 	const int RGB_MAX_HALF = RGB_MAX / 2;
181 
182 	GA.addAttributes(GraphAttributes::nodeGraphics
183 		| GraphAttributes::nodeStyle
184 		| GraphAttributes::nodeLabel
185 	);
186 
187 	for (node v : G.nodes) {
188 		int num = cliqueNumber[v];
189 		int colVals[3];
190 
191 		setSeed(num);
192 		for (int i = 0; i < 3; ++i) {
193 			colVals[i] = num < 0 ? RGB_MAX - 1 :
194 				randomNumber(0, RGB_MAX_HALF) + RGB_MAX_HALF;
195 		}
196 
197 		GA.fillColor(v) = Color(colVals[0], colVals[1], colVals[2]);
198 		GA.label(v) = to_string(num);
199 	}
200 }
201 
cliqueOK(const Graph & G,List<node> * clique,double density)202 bool CliqueFinderModule::cliqueOK(const Graph &G,
203 		List<node> *clique,
204 		double density)
205 {
206 	// Get desired number of edges in clique/dense subgraph (times two).
207 	int k = clique->size();
208 	int desiredCliqueEdges = int(ceil(density * k * (k-1)));
209 
210 	NodeArray<int> inClique(G, 0);
211 	for (node v : *clique) {
212 		inClique[v] = true;
213 	}
214 
215 	// Count each edge between clique members (twice).
216 	int cliqueEdges = 0;
217 	for (node v : *clique) {
218 		for (adjEntry adj : v->adjEntries) {
219 			if (inClique[adj->twinNode()]) {
220 				cliqueEdges++;
221 			}
222 		}
223 	}
224 
225 	return cliqueEdges >= desiredCliqueEdges;
226 }
227 
228 }
229