1 /** \file
2 * \brief Implementation of class PlanarSPQRTree
3 *
4 * \author 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
33 #include <ogdf/decomposition/PlanarSPQRTree.h>
34 #include <ogdf/basic/extended_graph_alg.h>
35
36
37 namespace ogdf {
38
39 //
40 // initialization: additionally embeds skeleton graphs or adpots embedding
41 // given by original graph
init(bool isEmbedded)42 void PlanarSPQRTree::init(bool isEmbedded)
43 {
44 m_finished = true;
45 if (isEmbedded) {
46 adoptEmbedding();
47
48 } else {
49
50 for (node v : tree().nodes)
51 planarEmbed(skeleton(v).getGraph());
52 }
53 }
54
55
adoptEmbedding()56 void PlanarSPQRTree::adoptEmbedding()
57 {
58 OGDF_HEAVY_ASSERT(originalGraph().representsCombEmbedding());
59
60 // ordered list of adjacency entries (for one original node) in all
61 // skeletons (where this node occurs)
62 NodeArray<SListPure<adjEntry> > adjEdges(tree());
63 // copy in skeleton of current original node
64 NodeArray<node> currentCopy(tree(),nullptr);
65 NodeArray<adjEntry> lastAdj(tree(),nullptr);
66 SListPure<node> current; // currently processed nodes
67
68 for (node vOrig : originalGraph().nodes)
69 {
70 for(adjEntry adjOrig : vOrig->adjEntries)
71 {
72 edge eOrig = adjOrig->theEdge();
73 const Skeleton &S = skeletonOfReal(eOrig);
74 edge eCopy = copyOfReal(eOrig);
75
76 adjEntry adjCopy = (S.original(eCopy->source()) == vOrig) ?
77 eCopy->adjSource() : eCopy->adjTarget();
78
79 setPosInEmbedding(adjEdges,currentCopy,lastAdj,current,S,adjCopy);
80 }
81
82 for(node vT : current) {
83 skeleton(vT).getGraph().sort(currentCopy[vT],adjEdges[vT]);
84
85 adjEdges[vT].clear();
86 currentCopy[vT] = nullptr;
87 }
88
89 current.clear();
90 }
91 }
92
93
setPosInEmbedding(NodeArray<SListPure<adjEntry>> & adjEdges,NodeArray<node> & currentCopy,NodeArray<adjEntry> & lastAdj,SListPure<node> & current,const Skeleton & S,adjEntry adj)94 void PlanarSPQRTree::setPosInEmbedding(
95 NodeArray<SListPure<adjEntry> > &adjEdges,
96 NodeArray<node> ¤tCopy,
97 NodeArray<adjEntry> &lastAdj,
98 SListPure<node> ¤t,
99 const Skeleton &S,
100 adjEntry adj)
101 {
102 node vT = S.treeNode();
103
104 adjEdges[vT].pushBack(adj);
105
106 node vCopy = adj->theNode();
107 node vOrig = S.original(vCopy);
108
109 if(currentCopy[vT] == nullptr) {
110 currentCopy[vT] = vCopy;
111 current.pushBack(vT);
112
113 for (adjEntry adjVirt : vCopy->adjEntries) {
114 edge eCopy = S.twinEdge(adjVirt->theEdge());
115 if (eCopy == nullptr) continue;
116 if (adjVirt == adj) {
117 lastAdj[vT] = adj;
118 continue;
119 }
120
121 const Skeleton &STwin = skeleton(S.twinTreeNode(adjVirt->theEdge()));
122
123 adjEntry adjCopy = (STwin.original(eCopy->source()) == vOrig) ?
124 eCopy->adjSource() : eCopy->adjTarget();
125
126 setPosInEmbedding(adjEdges,currentCopy,lastAdj,current,
127 STwin, adjCopy);
128 }
129
130 } else if (lastAdj[vT] != nullptr && lastAdj[vT] != adj) {
131 adjEntry adjVirt = lastAdj[vT];
132 edge eCopy = S.twinEdge(adjVirt->theEdge());
133
134 const Skeleton &STwin = skeleton(S.twinTreeNode(adjVirt->theEdge()));
135
136 adjEntry adjCopy = (STwin.original(eCopy->source()) == vOrig) ?
137 eCopy->adjSource() : eCopy->adjTarget();
138
139 setPosInEmbedding(adjEdges,currentCopy,lastAdj,current,
140 STwin, adjCopy);
141
142 lastAdj[vT] = nullptr;
143 }
144
145 }
146
147
148 //
149 // embed original graph according to embedding of skeletons
150 //
151 // The procedure also handles the case when some (real or virtual)
152 // edges are reversed (used in upward-planarity algorithms)
embed(Graph & G)153 void PlanarSPQRTree::embed(Graph &G)
154 {
155 OGDF_ASSERT(&G == &originalGraph());
156
157 const Skeleton &S = skeleton(rootNode());
158 const Graph &M = S.getGraph();
159
160 for (node v : M.nodes)
161 {
162 node vOrig = S.original(v);
163 SListPure<adjEntry> adjEdges;
164
165 for (adjEntry adj : v->adjEntries) {
166 edge e = adj->theEdge();
167 edge eOrig = S.realEdge(e);
168
169 if (eOrig != nullptr) {
170 adjEntry adjOrig = (vOrig == eOrig->source()) ?
171 eOrig->adjSource() : eOrig->adjTarget();
172 OGDF_ASSERT(adjOrig->theNode() == S.original(v));
173 adjEdges.pushBack(adjOrig);
174
175 } else {
176 node wT = S.twinTreeNode(e);
177 edge eTwin = S.twinEdge(e);
178 expandVirtualEmbed(wT,
179 (vOrig == skeleton(wT).original(eTwin->source())) ?
180 eTwin->adjSource() : eTwin->adjTarget(),
181 adjEdges);
182 }
183 }
184
185 G.sort(vOrig,adjEdges);
186 }
187
188
189 for(adjEntry adj : rootNode()->adjEntries) {
190 node wT = adj->theEdge()->target();
191 if (wT != rootNode())
192 createInnerVerticesEmbed(G, wT);
193 }
194 }
195
196
expandVirtualEmbed(node vT,adjEntry adjVirt,SListPure<adjEntry> & adjEdges)197 void PlanarSPQRTree::expandVirtualEmbed(node vT,
198 adjEntry adjVirt,
199 SListPure<adjEntry> &adjEdges)
200 {
201 const Skeleton &S = skeleton(vT);
202
203 node v = adjVirt->theNode();
204 node vOrig = S.original(v);
205
206 adjEntry adj;
207 for (adj = adjVirt->cyclicSucc(); adj != adjVirt; adj = adj->cyclicSucc())
208 {
209 edge e = adj->theEdge();
210 edge eOrig = S.realEdge(e);
211
212 if (eOrig != nullptr) {
213 adjEntry adjOrig = (vOrig == eOrig->source()) ?
214 eOrig->adjSource() : eOrig->adjTarget();
215 OGDF_ASSERT(adjOrig->theNode() == S.original(v));
216 adjEdges.pushBack(adjOrig);
217
218 } else {
219 node wT = S.twinTreeNode(e);
220 edge eTwin = S.twinEdge(e);
221 expandVirtualEmbed(wT,
222 (vOrig == skeleton(wT).original(eTwin->source())) ?
223 eTwin->adjSource() : eTwin->adjTarget(),
224 adjEdges);
225 }
226 }
227 }
228
229
createInnerVerticesEmbed(Graph & G,node vT)230 void PlanarSPQRTree::createInnerVerticesEmbed(Graph &G, node vT)
231 {
232 const Skeleton &S = skeleton(vT);
233 const Graph& M = S.getGraph();
234
235 node src = S.referenceEdge()->source();
236 node tgt = S.referenceEdge()->target();
237
238 for (node v : M.nodes)
239 {
240 if (v == src || v == tgt) continue;
241
242 node vOrig = S.original(v);
243 SListPure<adjEntry> adjEdges;
244
245 for (adjEntry adj : v->adjEntries) {
246 edge e = adj->theEdge();
247 edge eOrig = S.realEdge(e);
248
249 if (eOrig != nullptr) {
250 adjEntry adjOrig = (vOrig == eOrig->source()) ?
251 eOrig->adjSource() : eOrig->adjTarget();
252 OGDF_ASSERT(adjOrig->theNode() == S.original(v));
253 adjEdges.pushBack(adjOrig);
254 } else {
255 node wT = S.twinTreeNode(e);
256 edge eTwin = S.twinEdge(e);
257 expandVirtualEmbed(wT,
258 (vOrig == skeleton(wT).original(eTwin->source())) ?
259 eTwin->adjSource() : eTwin->adjTarget(),
260 adjEdges);
261 }
262 }
263
264 G.sort(vOrig,adjEdges);
265 }
266
267 for(adjEntry adj : vT->adjEntries) {
268 node wT = adj->theEdge()->target();
269 if (wT != vT)
270 createInnerVerticesEmbed(G, wT);
271 }
272 }
273
274
275
276 //
277 // basic update functions for manipulating embeddings
278
279 // reversing the skeleton of an R- or P-node
reverse(node vT)280 void PlanarSPQRTree::reverse(node vT)
281 {
282 skeleton(vT).getGraph().reverseAdjEdges();
283 }
284
285
286 // swapping two adjacency entries in the skeleton of a P-node
swap(node vT,adjEntry adj1,adjEntry adj2)287 void PlanarSPQRTree::swap(node vT, adjEntry adj1, adjEntry adj2)
288 {
289 OGDF_ASSERT(typeOf(vT) == NodeType::PNode);
290
291 Graph &M = skeleton(vT).getGraph();
292
293 M.swapAdjEdges(adj1,adj2);
294 M.swapAdjEdges(adj1->twin(),adj2->twin());
295 }
296
297
298 // swapping two edges in the skeleton of a P-node
swap(node vT,edge e1,edge e2)299 void PlanarSPQRTree::swap(node vT, edge e1, edge e2)
300 {
301 OGDF_ASSERT(typeOf(vT) == NodeType::PNode);
302
303 if (e1->source() == e2->source())
304 swap(vT,e1->adjSource(),e2->adjSource());
305 else
306 swap(vT,e1->adjSource(),e2->adjTarget());
307 }
308
309
310 //
311 // number of possible embeddings of original graph
312 //
numberOfEmbeddings(node vT) const313 double PlanarSPQRTree::numberOfEmbeddings(node vT) const
314 {
315 double num = 1.0;
316
317 switch(typeOf(vT)) {
318 case NodeType::RNode:
319 num = 2; break;
320 case NodeType::PNode:
321 #if 0
322 node vFirst = skeleton(vT).getGraph().firstNode();
323 #endif
324 for (int i = skeleton(vT).getGraph().firstNode()->degree()-1; i >= 2; --i)
325 num *= i;
326 break;
327 case NodeType::SNode:
328 break;
329 }
330
331 for(adjEntry adj : vT->adjEntries) {
332 node wT = adj->theEdge()->target();
333 if(wT != vT)
334 num *= numberOfEmbeddings(wT);
335 }
336
337 return num;
338 }
339
340
341
342 //
343 // randomly embed skeleton graphs
344 //
randomEmbed()345 void PlanarSPQRTree::randomEmbed()
346 {
347 for (node vT : tree().nodes) {
348 if (typeOf(vT) == NodeType::RNode) {
349 int doReverse = randomNumber(0,1);
350
351 if (doReverse == 1)
352 reverse(vT);
353
354 } else if (typeOf(vT) == NodeType::PNode) {
355 const Skeleton &S = skeleton(vT);
356 adjEntry adjRef = S.referenceEdge()->adjSource();
357
358 SList<adjEntry> adjEdges;
359 adjEntry adj;
360 for (adj = adjRef->cyclicSucc(); adj != adjRef; adj = adj->cyclicSucc())
361 adjEdges.pushBack(adj);
362
363 adjEdges.permute();
364
365 adj = adjRef->cyclicSucc();
366 for (adjEntry adjNext : adjEdges)
367 {
368 if (adjNext != adj) {
369 swap(vT,adj,adjNext);
370 adj = adjNext;
371 }
372 adj = adj->cyclicSucc();
373 }
374 }
375 }
376 }
377
378
numberOfNodeEmbeddings(node vT) const379 long long PlanarSPQRTree::numberOfNodeEmbeddings(node vT) const {
380
381 long long num = 1;
382
383 switch(typeOf(vT)) {
384 case NodeType::RNode:
385 num = 2;
386 break;
387 case NodeType::PNode:
388 for (int i = skeleton(vT).getGraph().firstNode()->degree()-1; i >= 2; --i)
389 num *= i;
390 break;
391 case NodeType::SNode:
392 break;
393 }
394
395 return num;
396 }
397
embed(node & vT,long long x)398 void PlanarSPQRTree::embed(node &vT, long long x) {
399
400 OGDF_ASSERT(x >= 0);
401 OGDF_ASSERT(x < numberOfNodeEmbeddings(vT));
402
403 //if it is a P-node
404 if (typeOf(vT) == NodeType::PNode) {
405 //encode the id of the permutation
406 long long id = x;
407 //number of elements of the permutation
408 const int p = skeleton(vT).getGraph().numberOfEdges() - 1;
409 //sequence for the edges
410 Array<long long> seq(p);
411 //base
412 long long b = 2;
413 //compute the correct sequence of the edges
414 for (int i = 0; i < p - 1; i++) {
415 seq[i] = id % b;
416 id /= b;
417 b++;
418 }
419 //swap the sequence
420 int low = 0;
421 int high = p-2;
422 while (low < high) {
423 long long t = seq[low];
424 seq[low] = seq[high]; seq[high] = t;
425 low++; high--;
426 }
427 seq[p-1] = 0;
428 //encode the sequence to the permutation
429 Array<int> list(p);
430 Array<int> permutation(p);
431 Array<bool> set(0,p-1,false);
432 for (int i = 0; i < p; i++) list[i] = i;
433 for (int i = 0; i < p; i++) {
434 long long e = seq[i];
435 long long cnt = 0;
436 int ind;
437 for (ind = 0; ind < p; ind++) {
438 if (!set[ind]) {
439 if (cnt == e) break;
440 ++cnt;
441 }
442 }
443 permutation[ind] = list[i];
444 set[ind] = true;
445 }
446 //adopt the permutation to the skeleton
447 //first vertex
448 List<adjEntry> order;
449 node nP = skeleton(vT).getGraph().firstNode();
450 nP->allAdjEntries(order);
451 TargetComparer<AdjElement,AdjElement> comp;
452 order.quicksort(comp);
453 Array<adjEntry> normalized(p);
454 List<adjEntry> newOrder;
455 newOrder.pushBack(order.popFrontRet());
456 for (int i = 0; i < p; i++) normalized[i] = order.popFrontRet();
457 for (int i = 0; i < p; i++) newOrder.pushBack(normalized[permutation[i]]);
458 skeleton(vT).getGraph().sort(nP,newOrder);
459 //second vertex
460 List<adjEntry> newOrderLast;
461 ListIterator<adjEntry> it = newOrder.begin();
462 while (it.valid()) {
463 newOrderLast.pushFront((*it)->twin());
464 ++it;
465 }
466 skeleton(vT).getGraph().sort(skeleton(vT).getGraph().lastNode(),newOrderLast);
467 //P-node ends here
468 }
469 //if it is a R-node
470 if (typeOf(vT) == NodeType::RNode) {
471 node nP = skeleton(vT).getGraph().firstNode();
472 if (x == 0 && nP->firstAdj()->index() > nP->lastAdj()->index()) reverse(vT);
473 if (x == 1 && nP->firstAdj()->index() < nP->lastAdj()->index()) reverse(vT);
474 //R-node ends here
475 }
476
477 }
478
479 //
480 // compute the first embedding of the original graph
481 //
firstEmbedding(Graph & G)482 void PlanarSPQRTree::firstEmbedding(Graph &G)
483 {
484 OGDF_ASSERT(&G == &originalGraph());
485
486 m_finished = false;
487 for (node vT : tree().nodes) {
488 firstEmbedding(vT);
489 }
490 embed(G);
491 }
492
493
494 //
495 // compute the next embedding of the original graph
496 //
nextEmbedding(Graph & G)497 bool PlanarSPQRTree::nextEmbedding(Graph &G)
498 {
499 OGDF_ASSERT(&G == &originalGraph());
500
501 //if there is at least one new embedding: compute it using
502 //nextEmbedding(n) to the first node of the SPQR-tree (represented by a list-iterator)
503 List<node> nodes;
504 tree().allNodes(nodes);
505 if (!m_finished && nextEmbedding(nodes.begin())) {
506 //compute the actual embedding of the original graph
507 embed(G);
508 return true;
509 }
510 m_finished = true;
511 return false;
512 }
513
514
515 //
516 // computes the first embedding of the skeleton of node vT
517 //
firstEmbedding(node & vT)518 void PlanarSPQRTree::firstEmbedding(node &vT)
519 {
520 //if vT is a R-node
521 if (typeOf(vT) == NodeType::RNode) {
522 //if the R-node were reversed in former steps
523 //then reverse it to its original embedding
524 node nP = skeleton(vT).getGraph().firstNode();
525 if (nP->firstAdj()->index() > nP->lastAdj()->index()) reverse(vT);
526 }
527 //if vT is a P-node
528 if (typeOf(vT) == NodeType::PNode) {
529 //then sort the adjEntries by their indices
530 //first vertex
531 List<adjEntry> order;
532 node nP = skeleton(vT).getGraph().firstNode();
533 nP->allAdjEntries(order);
534 TargetComparer<AdjElement,AdjElement> comp;
535 order.quicksort(comp);
536 skeleton(vT).getGraph().sort(nP,order);
537 //second vertex
538 List<adjEntry> newOrderLast;
539 for(adjEntry adj : order) {
540 newOrderLast.pushFront(adj->twin());
541 }
542 skeleton(vT).getGraph().sort(skeleton(vT).getGraph().lastNode(),newOrderLast);
543 }
544
545 }
546
reverse(node & vP,adjEntry & first,adjEntry & last)547 void PlanarSPQRTree::reverse(node &vP, adjEntry &first, adjEntry &last)
548 {
549 //swap the first and the last adjEntry
550 adjEntry it_f = first;
551 adjEntry it_l = last;
552 swap(vP,it_f,it_l);
553 adjEntry temp = it_f;
554 it_f = it_l->succ();
555 it_l = temp->pred();
556 //while there are swapable adjEntries: swap it, i.e.
557 //if left == right or left->pred() == right->succ() then stop
558 while (it_f != it_l && it_l->succ() != it_f) {
559 swap(vP,it_f,it_l);
560 temp = it_f;
561 it_f = it_l->succ();
562 it_l = temp->pred();
563 }
564 }
565
566
567 //
568 // computes the next embedding of the skeleton of node vT
569 //
nextEmbedding(node & vT)570 bool PlanarSPQRTree::nextEmbedding(node &vT)
571 {
572 //if vT is a R-node
573 if (typeOf(vT) == NodeType::RNode) {
574 node nP = skeleton(vT).getGraph().firstNode();
575 //compute the next embedding (might be the first embedding)
576 //of the sekeleton of vT
577 reverse(vT);
578 //if next embedding = first embedding then return false
579 //otherwise return true
580 return nP->firstAdj()->index() > nP->lastAdj()->index();
581 }
582 //if vT is a P-node
583 if (typeOf(vT) == NodeType::PNode) {
584 //take on of the two nodes of the skeleton of vT
585 node nP = skeleton(vT).getGraph().firstNode();
586 //if its degree is two then there is only one embedding
587 if (nP->degree() < 3) return false;
588 //otherwise compute the next embedding by computing the next
589 //permutation of the adjEntries of nP -- excluding the first adjEntry
590 adjEntry last;
591 adjEntry it = nP->lastAdj();
592 //compute the largest adjEntry s.t. its index is smaller than its successor
593 //largest means that the adjEntry is near the last adjEntry
594 while (it->index() < it->pred()->index()) it = it->pred();
595 //if such adjEntry was not found then all permutations were computed -- thus
596 //compute the first embedding by reversing the order of the adjEntries
597 //beginning at the second adjEntry
598 if (it == nP->firstAdj()->succ()) {
599 last = nP->lastAdj();
600 reverse(vT,it,last);
601 return false;
602 }
603 //otherwise compute the next permutation (embedding)
604 it = it->pred();
605 adjEntry it_max = nP->lastAdj();
606 //compute the largest adjEntry s.t. its index is smaller than the index of
607 //the adjEntry computed above -- the existence is guaranteed
608 while (it->index() > it_max->index()) it_max = it_max->pred();
609 //swap both adjEntries found above
610 swap(vT,it,it_max);
611 //reverse the order of the AdjEntries between the found adjEnrty
612 //and the last adjEntry of nP
613 last = nP->lastAdj();
614 it_max = it_max->succ();
615 if (it_max != nullptr && it_max != last) reverse(vT,it_max,last);
616 //the next permutation (embedding) was computed
617 return true;
618 }
619 //if vT is a S-node there is only one embedding
620 return false;
621 }
622
623 //
624 // computes the next embedding of the skeleton of node *it
625 //
nextEmbedding(ListIterator<node> it)626 bool PlanarSPQRTree::nextEmbedding(ListIterator<node> it)
627 {
628 //if the last embedding of the actual SPQR-node *it was computed: compute
629 //the first embedding of *it and the next embedding of *it++
630 //otherwise: compute the next embedding of *it
631 if (!nextEmbedding(*it)) {
632 ++it;
633 //if there is a valid successor of *it in the list of P- and R-nodes: compute
634 //its next embedding
635 //otherwise: all embeddings are computed
636 if (it.valid()) return nextEmbedding(it);
637 else return false;
638 }
639 return true;
640 }
641
642 }
643