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