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