Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

Loading...
Searching...
No Matches
PlanarSubgraphFast.h
Go to the documentation of this file.
1
32#pragma once
33
34#include <ogdf/basic/Array.h>
35#include <ogdf/basic/Graph.h>
37#include <ogdf/basic/List.h>
38#include <ogdf/basic/Module.h>
39#include <ogdf/basic/SList.h>
41#include <ogdf/basic/Thread.h>
42#include <ogdf/basic/basic.h>
49
50#include <algorithm>
51#include <atomic>
52#include <limits>
53#include <mutex>
54#include <utility>
55
56namespace ogdf {
57
81template<typename TCost>
83 using BlockType = std::pair<Graph*, EdgeArray<edge>*>;
84
91 std::atomic<int> m_runs;
92 std::mutex m_mutex;
93
94 public:
96 : m_bestSolution(block.size())
97 , m_bestDelEdges(block.size())
98 , m_nBlocks(block.size())
99 , m_block(block)
100 , m_pCost(pCost)
101 , m_runs(runs) {
102 for (int i = 0; i < m_nBlocks; ++i) {
103 m_bestDelEdges[i] = nullptr;
104 m_bestSolution[i] =
105 (m_block[i].first != nullptr) ? std::numeric_limits<int>::max() : 0;
106 }
107 }
108
109 int numBlocks() const { return m_nBlocks; }
110
111 const Graph& block(int i) const { return *m_block[i].first; }
112
113 bool considerBlock(int i) const { return m_bestSolution[i] > 1; }
114
115 List<edge>* postNewResult(int i, List<edge>* pNewDelEdges) {
116 TCost newSolution = pNewDelEdges->size();
117 if (m_pCost != nullptr) {
118 const EdgeArray<edge>& origEdge = *m_block[i].second;
119 newSolution = TCost(0);
120 for (edge e : *pNewDelEdges) {
121 newSolution += (*m_pCost)[origEdge[e]];
122 }
123 }
124
125 // m_mutex is automatically released when guard goes out of scope
126 std::lock_guard<std::mutex> guard(m_mutex);
127
128 if (newSolution < m_bestSolution[i]) {
129 std::swap(pNewDelEdges, m_bestDelEdges[i]);
130 m_bestSolution[i] = newSolution;
131 }
132
133 return pNewDelEdges;
134 }
135
136 // creates a solution for the original graph from best solutions per block
137 // also deletes (intermediate) solution lists
138 void buildSolution(List<edge>& delEdges) {
139 for (int i = 0; i < m_nBlocks; ++i) {
140 if (m_bestDelEdges[i] != nullptr) {
141 const EdgeArray<edge>& origEdge = *m_block[i].second;
142 for (edge e : *m_bestDelEdges[i]) {
143 delEdges.pushBack(origEdge[e]);
144 }
145 delete m_bestDelEdges[i];
146 }
147 }
148 }
149
150 bool getNextRun() { return --m_runs >= 0; }
151 };
152
153 class Worker {
154 ThreadMaster* m_pMaster; // associated master
155
156 public:
157 Worker(ThreadMaster* pMaster) : m_pMaster(pMaster) { }
158
159 ~Worker() = default;
160
162
163 private:
164 Worker(const Worker& other); // = delete
165 Worker& operator=(const Worker& other); // = delete
166 };
167
168public:
171
173 virtual PlanarSubgraphFast* clone() const override {
174 auto* res = new PlanarSubgraphFast<TCost>(*this);
175 res->m_nRuns = m_nRuns;
176 return res;
177 }
178
180 void runs(int nRuns) { m_nRuns = nRuns; }
181
183 int runs() const { return m_nRuns; }
184
185
186protected:
188
191 virtual Module::ReturnType doCall(const Graph& G, const List<edge>& preferedEdges,
192 List<edge>& delEdges, const EdgeArray<TCost>* pCost, bool preferedImplyPlanar) override {
193 delEdges.clear();
194
195 if (G.numberOfEdges() < 9) {
197 }
198
199 // Determine Biconnected Components
200 EdgeArray<int> componentID(G);
201 int nBlocks = biconnectedComponents(G, componentID);
202
203 // Determine edges per biconnected component
204 Array<SList<edge>> blockEdges(0, nBlocks - 1);
205 for (edge e : G.edges) {
206 if (!e->isSelfLoop()) {
207 blockEdges[componentID[e]].pushFront(e);
208 }
209 }
210
211 // Build non-trivial blocks
212 Array<BlockType> block(nBlocks);
213 NodeArray<node> copyV(G, nullptr);
214
215 for (int i = 0; i < nBlocks; i++) {
216 if (blockEdges[i].size() < 9) {
217 block[i] = BlockType((Graph*)nullptr,
218 (EdgeArray<edge>*)nullptr); // explicit casts required for VS2010
219 continue;
220 }
221
222 Graph* bc = new Graph;
223 EdgeArray<edge>* origE = new EdgeArray<edge>(*bc, nullptr);
224 block[i] = BlockType(bc, origE);
225
226 SList<node> marked;
227 for (edge e : blockEdges[i]) {
228 if (copyV[e->source()] == nullptr) {
229 copyV[e->source()] = bc->newNode();
230 marked.pushBack(e->source());
231 }
232 if (copyV[e->target()] == nullptr) {
233 copyV[e->target()] = bc->newNode();
234 marked.pushBack(e->target());
235 }
236
237 (*origE)[bc->newEdge(copyV[e->source()], copyV[e->target()])] = e;
238 }
239
240 for (node v : marked) {
241 copyV[v] = nullptr;
242 }
243 }
244 copyV.init();
245
246 int nRuns = max(1, m_nRuns);
247 unsigned int nThreads = min(this->maxThreads(), (unsigned int)nRuns);
248
249 if (nThreads == 1) {
250 seqCall(block, pCost, nRuns, (m_nRuns == 0), delEdges);
251 } else {
252 parCall(block, pCost, nRuns, nThreads, delEdges);
253 }
254
255 // clean-up
256 for (int i = 0; i < nBlocks; i++) {
257 delete block[i].first;
258 delete block[i].second;
259 }
260
262 }
263
264
265private:
267
269 void seqCall(const Array<BlockType>& block, const EdgeArray<TCost>* pCost, int nRuns,
270 bool randomize, List<edge>& delEdges) {
271 const int nBlocks = block.size();
272
273 Array<TCost> bestSolution(nBlocks);
274 Array<List<edge>*> bestDelEdges(nBlocks);
275
276 for (int i = 0; i < nBlocks; ++i) {
277 bestDelEdges[i] = nullptr;
278 bestSolution[i] = (block[i].first != nullptr) ? std::numeric_limits<TCost>::max() : 0;
279 }
280
281 for (int run = 0; run < nRuns; ++run) {
282 for (int i = 0; i < nBlocks; ++i) {
283 if (bestSolution[i] > 1) {
284 const Graph& B = *block[i].first;
285 const EdgeArray<edge>& origEdge = *block[i].second;
286
287 // compute (randomized) st-numbering
288 NodeArray<int> numbering(B, 0);
289 computeSTNumbering(B, numbering, nullptr, nullptr, randomize);
290
291 List<edge>* pCurrentDelEdges = new List<edge>;
292 planarize(B, numbering, *pCurrentDelEdges);
293
294 TCost currentSolution;
295 if (pCost == nullptr) {
296 currentSolution = pCurrentDelEdges->size();
297 } else {
298 currentSolution = 0;
299 for (edge e : *pCurrentDelEdges) {
300 currentSolution += (*pCost)[origEdge[e]];
301 }
302 }
303
304 if (currentSolution < bestSolution[i]) {
305 delete bestDelEdges[i];
306 bestDelEdges[i] = pCurrentDelEdges;
307 bestSolution[i] = currentSolution;
308 } else {
309 delete pCurrentDelEdges;
310 }
311 }
312 }
313 }
314
315 // build final solution from block solutions
316 for (int i = 0; i < nBlocks; ++i) {
317 if (bestDelEdges[i] != nullptr) {
318 const EdgeArray<edge>& origEdge = *block[i].second;
319 for (edge e : *bestDelEdges[i]) {
320 delEdges.pushBack(origEdge[e]);
321 }
322 delete bestDelEdges[i];
323 }
324 }
325 }
326
328 void parCall(const Array<BlockType>& block, const EdgeArray<TCost>* pCost, int nRuns,
329 unsigned int nThreads, List<edge>& delEdges) {
330 ThreadMaster master(block, pCost, nRuns - nThreads);
331
332 Array<Worker*> worker(nThreads - 1);
333 Array<Thread> thread(nThreads - 1);
334 for (unsigned int i = 0; i < nThreads - 1; ++i) {
335 worker[i] = new Worker(&master);
336 thread[i] = Thread(*worker[i]);
337 }
338
339 doWorkHelper(master);
340
341 for (unsigned int i = 0; i < nThreads - 1; ++i) {
342 thread[i].join();
343 delete worker[i];
344 }
345
346 master.buildSolution(delEdges);
347 }
348
350
352 static void planarize(const Graph& G, NodeArray<int>& numbering, List<edge>& delEdges) {
353 using PlanarLeafKey = booth_lueker::PlanarLeafKey<whaInfo*>;
354
357 Array<node> table(G.numberOfNodes() + 1);
358
359 for (node v : G.nodes) {
360 for (adjEntry adj : v->adjEntries) {
361 edge e = adj->theEdge();
362 if (numbering[e->opposite(v)] > numbering[v]) { // sideeffect: ignores selfloops
363 PlanarLeafKey* L = new PlanarLeafKey(e);
364 inLeaves[v].pushFront(L);
365 }
366 }
367 table[numbering[v]] = v;
368 }
369
370 for (node v : G.nodes) {
371 for (PlanarLeafKey* L : inLeaves[v]) {
372 outLeaves[L->userStructKey()->opposite(v)].pushFront(L);
373 }
374 }
375
376 SList<PQLeafKey<edge, whaInfo*, bool>*> totalEliminatedKeys;
377
379 T.Initialize(inLeaves[table[1]]);
380 for (int i = 2; i < G.numberOfNodes(); i++) {
382 T.Reduction(outLeaves[table[i]], eliminatedKeys);
383 totalEliminatedKeys.conc(eliminatedKeys);
384 T.ReplaceRoot(inLeaves[table[i]]);
386 }
387
388 for (PQLeafKey<edge, whaInfo*, bool>* key : totalEliminatedKeys) {
389 edge e = key->userStructKey();
390 delEdges.pushBack(e);
391 }
392
393 //cleanup
394 for (node v : G.nodes) {
395 while (!inLeaves[v].empty()) {
396 PlanarLeafKey* L = inLeaves[v].popFrontRet();
397 delete L;
398 }
399 }
400
401 T.Cleanup(); // Explicit call for destructor necessary. This allows to call virtual
402 // function CleanNode for freeing node information class.
403 }
404
405 static void doWorkHelper(ThreadMaster& master) {
406 const int nBlocks = master.numBlocks();
407
408 do {
409 for (int i = 0; i < nBlocks; ++i) {
410 if (master.considerBlock(i)) {
411 const Graph& B = master.block(i);
412
413 NodeArray<int> numbering(B, 0); // compute (randomized) st-numbering
414 computeSTNumbering(B, numbering, nullptr, nullptr, true);
415
416 List<edge>* pCurrentDelEdges = new List<edge>;
417 planarize(B, numbering, *pCurrentDelEdges);
418
419 pCurrentDelEdges = master.postNewResult(i, pCurrentDelEdges);
420 delete pCurrentDelEdges;
421 }
422 }
423
424 } while (master.getNextRun());
425 }
426};
427
428}
Declaration and implementation of Array class and Array algorithms.
Includes declaration of graph class.
Decralation of GraphElement and GraphList classes.
Declaration of doubly linked lists and iterators.
Declares base class for all module types.
Declaration and implementation of the class PQLeafKey.
Declaration of class PlanarLeafKey.
Declaration of interface for planar subgraph algorithms.
Declaration of class PlanarSubgraphPQTree.
Declaration of singly linked lists and iterators.
Declaration of st-Numbering functions.
Declaration of Thread class representing threads.
Basic declarations, included by all source files.
Class for adjacency list elements.
Definition Graph_d.h:143
The parameterized class Array implements dynamic arrays of type E.
Definition Array.h:219
INDEX size() const
Returns the size (number of elements) of the array.
Definition Array.h:302
Class for the representation of edges.
Definition Graph_d.h:364
node opposite(node v) const
Returns the adjacent node different from v.
Definition Graph_d.h:414
Data type for general directed graphs (adjacency list representation).
Definition Graph_d.h:866
node newNode(int index=-1)
Creates a new node and returns it.
Definition Graph_d.h:1061
edge newEdge(node v, node w, int index=-1)
Creates a new edge (v,w) and returns it.
Definition Graph_d.h:1080
Doubly linked lists (maintaining the length of the list).
Definition List.h:1451
int size() const
Returns the number of elements in the list.
Definition List.h:1488
void clear()
Removes all elements from the list.
Definition List.h:1626
iterator pushBack(const E &x)
Adds element x at the end of the list.
Definition List.h:1547
virtual void emptyAllPertinentNodes()
Does a clean up after a reduction.
ReturnType
The return type of a module.
Definition Module.h:52
@ Feasible
The solution is feasible.
@ Optimal
The solution is optimal.
Class for the representation of nodes.
Definition Graph_d.h:241
The class template PQLeafKey is a derived class of class template PQBasicKey.
Definition PQLeafKey.h:84
virtual void Cleanup()
Removes the entire PQ-tree.
Definition PQTree.h:1474
ThreadMaster(const Array< BlockType > &block, const EdgeArray< TCost > *pCost, int runs)
Array< List< edge > * > m_bestDelEdges
best solution for block
List< edge > * postNewResult(int i, List< edge > *pNewDelEdges)
const Array< BlockType > & m_block
the blocks (graph and edge mapping)
std::mutex m_mutex
thread synchronization
const EdgeArray< TCost > * m_pCost
edge cost (may be 0)
void buildSolution(List< edge > &delEdges)
Array< TCost > m_bestSolution
value of best solution for block
Worker & operator=(const Worker &other)
Worker(const Worker &other)
Computation of a planar subgraph using PQ-trees.
virtual PlanarSubgraphFast * clone() const override
Returns a new instance of fast planar subgraph with the same option settings.
static void doWorkHelper(ThreadMaster &master)
virtual Module::ReturnType doCall(const Graph &G, const List< edge > &preferedEdges, List< edge > &delEdges, const EdgeArray< TCost > *pCost, bool preferedImplyPlanar) override
Returns true, if G is planar, false otherwise.
int runs() const
Returns the current number of randomized runs.
static void planarize(const Graph &G, NodeArray< int > &numbering, List< edge > &delEdges)
Performs a planarization on a biconnected component of G.
PlanarSubgraphFast()
Creates an instance of the fast planar subgraph algorithm with default settings (runs = 10).
void seqCall(const Array< BlockType > &block, const EdgeArray< TCost > *pCost, int nRuns, bool randomize, List< edge > &delEdges)
Realizes the sequential implementation.
void runs(int nRuns)
Sets the number of randomized runs to nRuns.
void parCall(const Array< BlockType > &block, const EdgeArray< TCost > *pCost, int nRuns, unsigned int nThreads, List< edge > &delEdges)
Realizes the parallel implementation.
std::pair< Graph *, EdgeArray< edge > * > BlockType
int m_nRuns
The number of runs for randomization.
Interface for planar subgraph algorithms.
unsigned int maxThreads() const
Returns the maximal number of used threads.
virtual int Initialize(SListPure< PlanarLeafKey * > &leafKeys)
Initializes a new PQ-tree with a set of leaves.
void ReplaceRoot(SListPure< PlanarLeafKey * > &leafKeys)
Replaces the pertinent subtree by a set of new leaves.
virtual bool Reduction(SListPure< PlanarLeafKey * > &leafKeys, SList< PQLeafKey< edge, whaInfo *, bool > * > &eliminatedKeys)
Reduces a set of leaves.
void init(const Base *base=nullptr)
Reinitializes the array. Associates the array with the matching registry of base.
Singly linked lists (maintaining the length of the list).
Definition SList.h:845
void conc(SList< E > &L2)
Appends L2 to this list and makes L2 empty.
Definition SList.h:1018
SListIterator< E > pushBack(const E &x)
Adds element x at the end of the list.
Definition SList.h:939
Threads supporting OGDF's memory management.
Definition Thread.h:53
RegisteredArray for edges of a graph, specialized for EdgeArray<edge>.
Definition Graph_d.h:717
RegisteredArray for nodes, edges and adjEntries of a graph.
Definition Graph_d.h:659
int biconnectedComponents(const Graph &G, EdgeArray< int > &component, int &nonEmptyComponents)
Computes the biconnected components of G.
int computeSTNumbering(const Graph &G, NodeArray< int > &numbering, node s=nullptr, node t=nullptr, bool randomized=false)
Computes an st-Numbering of G.
The namespace for all OGDF objects.
Declaration of simple graph algorithms.
Declaration of class whaInfo.