1 /** \file
2 * \brief Implements class SubgraphPlanarizer.
3 *
4 * \author Markus Chimani, Carsten Gutwenger
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/planarity/SubgraphPlanarizer.h>
33 #include <ogdf/planarity/VariableEmbeddingInserter.h>
34 #include <ogdf/planarity/PlanarSubgraphFast.h>
35 #include <ogdf/basic/extended_graph_alg.h>
36 #include <ogdf/planarity/embedder/CrossingStructure.h>
37
38 using std::atomic;
39 using std::mutex;
40 using std::lock_guard;
41 using std::minstd_rand;
42
43 namespace ogdf {
44
45 using embedder::CrossingStructure;
46
47 class SubgraphPlanarizer::ThreadMaster {
48 CrossingStructure *m_pCS;
49 int m_bestCR;
50
51 const PlanRep &m_pr;
52 int m_cc;
53
54 const EdgeArray<int> *m_pCost;
55 const EdgeArray<bool> *m_pForbid;
56 const EdgeArray<uint32_t> *m_pEdgeSubGraph;
57 const List<edge> &m_delEdges;
58
59 int m_seed;
60 atomic<int> m_perms;
61 int64_t m_stopTime;
62 mutex m_mutex;
63
64 public:
65 ThreadMaster(
66 const PlanRep &pr,
67 int cc,
68 const EdgeArray<int> *pCost,
69 const EdgeArray<bool> *pForbid,
70 const EdgeArray<uint32_t> *pEdgeSubGraphs,
71 const List<edge> &delEdges,
72 int seed,
73 int perms,
74 int64_t stopTime);
75
~ThreadMaster()76 ~ThreadMaster() { delete m_pCS; }
77
planRep() const78 const PlanRep &planRep() const { return m_pr; }
currentCC() const79 int currentCC() const { return m_cc; }
80
cost() const81 const EdgeArray<int> *cost() const { return m_pCost; }
forbid() const82 const EdgeArray<bool> *forbid() const { return m_pForbid; }
edgeSubGraphs() const83 const EdgeArray<uint32_t> *edgeSubGraphs() const { return m_pEdgeSubGraph; }
delEdges() const84 const List<edge> &delEdges() const { return m_delEdges; }
85
rseed(long id) const86 int rseed(long id) const { return (int)id * m_seed; }
87
queryBestKnown() const88 int queryBestKnown() const { return m_bestCR; }
89 CrossingStructure *postNewResult(CrossingStructure *pCS);
90 bool getNextPerm();
91
92 void restore(PlanRep &pr, int &cr);
93 };
94
95
96 class SubgraphPlanarizer::Worker {
97
98 int m_id;
99 ThreadMaster *m_pMaster;
100 EdgeInsertionModule *m_pInserter;
101
102 public:
Worker(int id,ThreadMaster * pMaster,EdgeInsertionModule * pInserter)103 Worker(int id, ThreadMaster *pMaster, EdgeInsertionModule *pInserter) : m_id(id), m_pMaster(pMaster), m_pInserter(pInserter) { }
~Worker()104 ~Worker() { delete m_pInserter; }
105
106 void operator()();
107
108 private:
109 Worker(const Worker &other); // = delete
110 Worker &operator=(const Worker &other); // = delete
111 };
112
113
ThreadMaster(const PlanRep & pr,int cc,const EdgeArray<int> * pCost,const EdgeArray<bool> * pForbid,const EdgeArray<uint32_t> * pEdgeSubGraphs,const List<edge> & delEdges,int seed,int perms,int64_t stopTime)114 SubgraphPlanarizer::ThreadMaster::ThreadMaster(
115 const PlanRep &pr,
116 int cc,
117 const EdgeArray<int> *pCost,
118 const EdgeArray<bool> *pForbid,
119 const EdgeArray<uint32_t> *pEdgeSubGraphs,
120 const List<edge> &delEdges,
121 int seed,
122 int perms,
123 int64_t stopTime)
124 :
125 m_pCS(nullptr), m_bestCR(std::numeric_limits<int>::max()), m_pr(pr), m_cc(cc),
126 m_pCost(pCost), m_pForbid(pForbid), m_pEdgeSubGraph(pEdgeSubGraphs),
127 m_delEdges(delEdges), m_seed(seed), m_perms(perms), m_stopTime(stopTime)
128 { }
129
130
postNewResult(CrossingStructure * pCS)131 CrossingStructure *SubgraphPlanarizer::ThreadMaster::postNewResult(CrossingStructure *pCS)
132 {
133 int newCR = pCS->weightedCrossingNumber();
134
135 lock_guard<mutex> guard(m_mutex);
136
137 if(newCR < m_bestCR) {
138 std::swap(pCS, m_pCS);
139 m_bestCR = newCR;
140 }
141
142 return pCS;
143 }
144
145
getNextPerm()146 bool SubgraphPlanarizer::ThreadMaster::getNextPerm()
147 {
148 if(m_stopTime >= 0 && System::realTime() >= m_stopTime)
149 return false;
150
151 return --m_perms >= 0;
152 }
153
154
restore(PlanRep & pr,int & cr)155 void SubgraphPlanarizer::ThreadMaster::restore(PlanRep &pr, int &cr)
156 {
157 m_pCS->restore(pr, m_cc);
158 cr = m_bestCR;
159 }
160
161
doSinglePermutation(PlanRepLight & prl,int cc,const EdgeArray<int> * pCost,const EdgeArray<bool> * pForbid,const EdgeArray<uint32_t> * pEdgeSubGraphs,Array<edge> & deletedEdges,EdgeInsertionModule & inserter,minstd_rand & rng,int & crossingNumber)162 bool SubgraphPlanarizer::doSinglePermutation(
163 PlanRepLight &prl,
164 int cc,
165 const EdgeArray<int> *pCost,
166 const EdgeArray<bool> *pForbid,
167 const EdgeArray<uint32_t> *pEdgeSubGraphs,
168 Array<edge> &deletedEdges,
169 EdgeInsertionModule &inserter,
170 minstd_rand &rng,
171 int &crossingNumber)
172 {
173 prl.initCC(cc);
174
175 const int nG = prl.numberOfNodes();
176 const int high = deletedEdges.high();
177
178 for(int j = 0; j <= high; ++j)
179 prl.delEdge(prl.copy(deletedEdges[j]));
180
181 deletedEdges.permute(rng);
182
183 ReturnType ret = inserter.callEx(prl, deletedEdges, pCost, pForbid, pEdgeSubGraphs);
184
185 if(!isSolution(ret))
186 return false; // no solution found, that's bad...
187
188 if(pCost == nullptr)
189 crossingNumber = prl.numberOfNodes() - nG;
190 else {
191 crossingNumber = 0;
192 for(node n : prl.nodes) {
193 if(prl.original(n) == nullptr) { // dummy found -> calc cost
194 edge e1 = prl.original(n->firstAdj()->theEdge());
195 edge e2 = prl.original(n->lastAdj()->theEdge());
196 if(pEdgeSubGraphs != nullptr) {
197 int subgraphCounter = 0;
198 for(int i = 0; i < 32; i++) {
199 if((((*pEdgeSubGraphs)[e1] & (1<<i))!=0) && (((*pEdgeSubGraphs)[e2] & (1<<i)) != 0))
200 subgraphCounter++;
201 }
202 crossingNumber += (subgraphCounter * (*pCost)[e1] * (*pCost)[e2]);
203 } else
204 crossingNumber += (*pCost)[e1] * (*pCost)[e2];
205 }
206 }
207 }
208
209 return true;
210 }
211
doWorkHelper(ThreadMaster & master,EdgeInsertionModule & inserter,minstd_rand & rng)212 void SubgraphPlanarizer::doWorkHelper(ThreadMaster &master, EdgeInsertionModule &inserter, minstd_rand &rng)
213 {
214 const List<edge> &delEdges = master.delEdges();
215
216 const int m = delEdges.size();
217 Array<edge> deletedEdges(m);
218 int j = 0;
219 for(edge e : delEdges)
220 deletedEdges[j++] = e;
221
222 PlanRepLight prl(master.planRep());
223 int cc = master.currentCC();
224
225 const EdgeArray<int> *pCost = master.cost();
226 const EdgeArray<bool> *pForbid = master.forbid();
227 const EdgeArray<uint32_t> *pEdgeSubGraphs = master.edgeSubGraphs();
228
229 do {
230 int crossingNumber;
231 if(doSinglePermutation(prl, cc, pCost, pForbid, pEdgeSubGraphs, deletedEdges, inserter, rng, crossingNumber)
232 && crossingNumber < master.queryBestKnown())
233 {
234 CrossingStructure *pCS = new CrossingStructure;
235 pCS->init(prl, crossingNumber);
236 pCS = master.postNewResult(pCS);
237 delete pCS;
238 }
239
240 } while(master.getNextPerm());
241 }
242
243
operator ()()244 void SubgraphPlanarizer::Worker::operator()()
245 {
246 minstd_rand rng(m_pMaster->rseed(11+7*m_id)); // different seeds per thread
247 doWorkHelper(*m_pMaster, *m_pInserter, rng);
248 }
249
250
251 // default constructor
SubgraphPlanarizer()252 SubgraphPlanarizer::SubgraphPlanarizer()
253 {
254 auto* s = new PlanarSubgraphFast<int>();
255 s->runs(64);
256 m_subgraph.reset(s);
257
258 VariableEmbeddingInserter *pInserter = new VariableEmbeddingInserter();
259 pInserter->removeReinsert(RemoveReinsertType::All);
260 m_inserter.reset(pInserter);
261
262 m_permutations = 1;
263 m_setTimeout = true;
264
265 #ifdef OGDF_MEMORY_POOL_NTS
266 m_maxThreads = 1u;
267 #else
268 m_maxThreads = max(1u, Thread::hardware_concurrency());
269 #endif
270 }
271
272
273 // copy constructor
SubgraphPlanarizer(const SubgraphPlanarizer & planarizer)274 SubgraphPlanarizer::SubgraphPlanarizer(const SubgraphPlanarizer &planarizer)
275 : CrossingMinimizationModule(planarizer), Logger()
276 {
277 m_subgraph.reset(planarizer.m_subgraph->clone());
278 m_inserter.reset(planarizer.m_inserter->clone());
279
280 m_permutations = planarizer.m_permutations;
281 m_setTimeout = planarizer.m_setTimeout;
282 m_maxThreads = planarizer.m_maxThreads;
283 }
284
285
286 // clone method
clone() const287 CrossingMinimizationModule *SubgraphPlanarizer::clone() const {
288 return new SubgraphPlanarizer(*this);
289 }
290
291
292 // assignment operator
operator =(const SubgraphPlanarizer & planarizer)293 SubgraphPlanarizer &SubgraphPlanarizer::operator=(const SubgraphPlanarizer &planarizer)
294 {
295 m_timeLimit = planarizer.m_timeLimit;
296 m_subgraph.reset(planarizer.m_subgraph->clone());
297 m_inserter.reset(planarizer.m_inserter->clone());
298
299 m_permutations = planarizer.m_permutations;
300 m_setTimeout = planarizer.m_setTimeout;
301 m_maxThreads = planarizer.m_maxThreads;
302
303 return *this;
304 }
305
306
doCall(PlanRep & pr,int cc,const EdgeArray<int> * pCostOrig,const EdgeArray<bool> * pForbiddenOrig,const EdgeArray<uint32_t> * pEdgeSubGraphs,int & crossingNumber)307 Module::ReturnType SubgraphPlanarizer::doCall(
308 PlanRep &pr,
309 int cc,
310 const EdgeArray<int> *pCostOrig,
311 const EdgeArray<bool> *pForbiddenOrig,
312 const EdgeArray<uint32_t> *pEdgeSubGraphs,
313 int &crossingNumber)
314 {
315 OGDF_ASSERT(m_permutations >= 1);
316 crossingNumber = 0;
317
318 PlanarSubgraphModule<int> &subgraph = *m_subgraph;
319 EdgeInsertionModule &inserter = *m_inserter;
320
321 unsigned int nThreads = min(m_maxThreads, (unsigned int)m_permutations);
322
323 int64_t startTime;
324 System::usedRealTime(startTime);
325 int64_t stopTime = m_timeLimit >= 0 ? startTime + int64_t(1000.0*m_timeLimit) : -1;
326
327 //
328 // Compute subgraph
329 //
330 if(m_setTimeout)
331 subgraph.timeLimit(m_timeLimit);
332
333 pr.initCC(cc);
334
335 List<edge> delEdges;
336 ReturnType retValue;
337
338 if(pCostOrig) {
339 EdgeArray<int> costPG(pr);
340 for(edge e : pr.edges)
341 costPG[e] = (*pCostOrig)[pr.original(e)];
342
343 retValue = subgraph.call(pr, costPG, delEdges);
344 } else
345 retValue = subgraph.call(pr, delEdges);
346
347 if(!isSolution(retValue))
348 return retValue;
349
350 const int m = delEdges.size();
351 if(m == 0)
352 return ReturnType::Optimal; // graph is planar
353
354 for(edge &eDel : delEdges)
355 eDel = pr.original(eDel);
356
357 //
358 // Permutation phase
359 //
360
361 int seed = rand();
362 minstd_rand rng(seed);
363
364 if(nThreads > 1) {
365 //
366 // Parallel implementation
367 //
368 ThreadMaster master(
369 pr, cc,
370 pCostOrig, pForbiddenOrig, pEdgeSubGraphs,
371 delEdges,
372 seed,
373 m_permutations - nThreads,
374 stopTime);
375
376 Array<Worker *> worker(nThreads-1);
377 Array<Thread> thread(nThreads-1);
378 for(unsigned int i = 0; i < nThreads-1; ++i) {
379 worker[i] = new Worker(i, &master, inserter.clone());
380 thread[i] = Thread(*worker[i]);
381 }
382
383 doWorkHelper(master, inserter, rng);
384
385 for(unsigned int i = 0; i < nThreads-1; ++i) {
386 thread[i].join();
387 delete worker[i];
388 }
389
390 master.restore(pr, crossingNumber);
391
392 } else {
393 //
394 // Sequential implementation
395 //
396 PlanRepLight prl(pr);
397
398 Array<edge> deletedEdges(m);
399 int j = 0;
400 for(edge eDel : delEdges)
401 deletedEdges[j++] = eDel;
402
403 bool foundSolution = false;
404 CrossingStructure cs;
405 for(int i = 1; i <= m_permutations; ++i)
406 {
407 int cr;
408 bool ok = doSinglePermutation(prl, cc, pCostOrig, pForbiddenOrig, pEdgeSubGraphs, deletedEdges, inserter, rng, cr);
409
410 if(ok && (!foundSolution || cr < cs.weightedCrossingNumber())) {
411 foundSolution = true;
412 cs.init(prl, cr);
413 }
414
415 if(stopTime >= 0 && System::realTime() >= stopTime) {
416 if(!foundSolution)
417 return ReturnType::TimeoutInfeasible; // not able to find a solution...
418 break;
419 }
420 }
421
422 cs.restore(pr,cc); // restore best solution in pr
423 crossingNumber = cs.weightedCrossingNumber();
424
425 OGDF_ASSERT(isPlanar(pr));
426 }
427
428 return ReturnType::Feasible;
429 }
430
431 }
432