1 /** \file
2 * \brief implements class MultiEdgeApproxInserter
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/planarity/MultiEdgeApproxInserter.h>
34 #include <ogdf/basic/simple_graph_alg.h>
35 #include <ogdf/basic/Queue.h>
36 #include <ogdf/planarity/FixedEmbeddingInserter.h>
37 #include <ogdf/planarity/VariableEmbeddingInserter.h>
38 #include <ogdf/basic/extended_graph_alg.h>
39 #include <ogdf/basic/tuples.h>
40
41
42 namespace ogdf {
43
44 class MultiEdgeApproxInserter::EmbeddingPreference
45 {
46 public:
47 enum class Type { None, RNode, PNode };
48
49 // constructs an embedding preference of type none (= irrelevant)
EmbeddingPreference()50 EmbeddingPreference() : m_type(Type::None), m_mirror(false) { }
51
52 // constructs an embedding preference for an R-node
EmbeddingPreference(bool mirror)53 explicit EmbeddingPreference(bool mirror) : m_type(Type::RNode), m_mirror(mirror) { }
54
EmbeddingPreference(adjEntry a1,adjEntry a2)55 EmbeddingPreference(adjEntry a1, adjEntry a2) : m_type(Type::PNode), m_mirror(false), m_adj1(a1), m_adj2(a2) { }
56 // constructs an embedding preference for a P-node
57
type() const58 Type type() const { return m_type; }
isNull() const59 bool isNull() const { return m_type == Type::None; }
60
mirror() const61 bool mirror() const { return m_mirror; }
adj1() const62 adjEntry adj1() const { return m_adj1; }
adj2() const63 adjEntry adj2() const { return m_adj2; }
64
65 // flip embedding preference
flip()66 void flip() {
67 m_mirror = !m_mirror;
68 std::swap(m_adj1, m_adj2);
69 }
70
71 static const EmbeddingPreference s_none;
72
print(std::ostream & os) const73 std::ostream &print(std::ostream &os) const {
74 switch(type()) {
75 case MultiEdgeApproxInserter::EmbeddingPreference::Type::None:
76 os << "none";
77 break;
78 case MultiEdgeApproxInserter::EmbeddingPreference::Type::PNode:
79 os << "PNode: " << adj1()->index() << "->" << adj2()->index();
80 break;
81 case MultiEdgeApproxInserter::EmbeddingPreference::Type::RNode:
82 os << "RNode: " << mirror();
83 break;
84 }
85 return os;
86 }
87
88 private:
89 Type m_type;
90 bool m_mirror; // skeleton of an R-node must be mirrored (yes/no)
91
92 adjEntry m_adj1, m_adj2;
93 // these two adj entries of first node in skeleton of a P-node must be in clockwise order adj1 -> adj2
94 };
95
96 const MultiEdgeApproxInserter::EmbeddingPreference MultiEdgeApproxInserter::EmbeddingPreference::s_none;
97
98
99 class MultiEdgeApproxInserter::Block : public Graph
100 {
101 public:
102 struct SPQRPath {
SPQRPathogdf::MultiEdgeApproxInserter::Block::SPQRPath103 SPQRPath() : m_start(nullptr) { }
104 node m_start; // first node in SPQR-tree (its skeleton contains v1)
105 List<edge> m_edges; // actual path (empty if v1 and v2 are in skeleton of m_start)
106 List<EmbeddingPreference> m_prefs; // embeding preferences along the path
107 };
108
109 struct PathElement {
PathElementogdf::MultiEdgeApproxInserter::Block::PathElement110 PathElement() : m_node(nullptr), m_pref(&EmbeddingPreference::s_none) { }
111
112 node m_node;
113 const EmbeddingPreference *m_pref;
114 };
115
116
117 // constructor
Block()118 Block() : m_spqr(nullptr), m_embB(nullptr), m_dualB(nullptr), m_faceNodeB(nullptr), m_primalAdjB(nullptr), m_vS(nullptr), m_vT(nullptr)
119 {
120 m_BCtoG.init(*this);
121 m_cost .init(*this,1);
122 }
123
124 // destructoe
~Block()125 ~Block() {
126 delete m_primalAdjB;
127 delete m_faceNodeB;
128 delete m_dualB;
129 delete m_embB;
130 delete m_spqr;
131 }
132
133 // returns true iff block is just a bridge (in this case, there is no SPQR-tree!)
isBridge() const134 bool isBridge() const { return numberOfEdges() < 3; }
135
cost(edge e) const136 int cost(edge e) const { return m_cost[e]; }
137
138 // initialize SPQR-tree; compute allocation nodes
139 void initSPQR(int m);
140
141 // returns SPQR-tree
spqr() const142 const StaticPlanarSPQRTree &spqr() const { return *m_spqr; }
spqr()143 StaticPlanarSPQRTree &spqr() { return *m_spqr; }
144
145 // compute traversing costs in skeleton of n; omit skeleton edges e1, e2
146 void computeTraversingCosts(node n, edge e1, edge e2);
147
148 int findShortestPath(node n, edge eRef);
149
150 // compute costs of a subpath through skeleton(n) while connecting s with t
151 int costsSubpath(node n, edge eIn, edge eOut, node s, node t, PathDir &dirFrom, PathDir &dirTo);
152
153 void pathToArray(int i, Array<PathElement> &path);
154
155 bool embPrefAgree(node n, const EmbeddingPreference &p_pick, const EmbeddingPreference &p_e);
156
157 bool switchingPair(node n, node m,
158 const EmbeddingPreference &p_pick_n, const EmbeddingPreference &p_n,
159 const EmbeddingPreference &p_pick_m, const EmbeddingPreference &p_m);
160
161 int findBestFaces(node s, node t, adjEntry &adj_s, adjEntry &adj_t);
162 adjEntry findBestFace(node s, node t, int &len);
163
164 AdjEntryArray<adjEntry> m_BCtoG; //!< maps adjacency entries in block to original graph
165 EdgeArray<int> m_cost; //!< costs of an edge (as given for edges in original graph)
166 NodeArray<ArrayBuffer<node>> m_allocNodes; //!< allocation nodes
167 Array<SPQRPath> m_pathSPQR; //!< insertion path in SPQR-tree
168
169 private:
170 struct RNodeInfo {
RNodeInfoogdf::MultiEdgeApproxInserter::Block::RNodeInfo171 RNodeInfo() : m_emb(nullptr), m_dual(nullptr), m_faceNode(nullptr), m_primalAdj(nullptr) { }
~RNodeInfoogdf::MultiEdgeApproxInserter::Block::RNodeInfo172 ~RNodeInfo() {
173 delete m_primalAdj;
174 delete m_faceNode;
175 delete m_dual;
176 delete m_emb;
177 }
178
179 ConstCombinatorialEmbedding *m_emb; // combinatorial embedding of skeleton graph
180 Graph *m_dual; // dual graph
181 FaceArray<node> *m_faceNode; // mapping dual node -> face
182 AdjEntryArray<adjEntry> *m_primalAdj; // mapping dual adjEntry -> primal adjEntry
183 };
184
185 int recTC(node n, edge eRef);
186 void constructDual(node n);
187
188 public:
189 void constructDualBlock();
190 private:
191
192 StaticPlanarSPQRTree *m_spqr;
193 NodeArray<EdgeArray<int> > m_tc; // traversing costs
194
195 NodeArray<RNodeInfo> m_info; // additional data for R-node skeletons
196
197 ConstCombinatorialEmbedding *m_embB;
198 Graph *m_dualB;
199 FaceArray<node> *m_faceNodeB;
200 AdjEntryArray<adjEntry> *m_primalAdjB;
201 node m_vS, m_vT;
202 };
203
204
pathToArray(int i,Array<PathElement> & path)205 void MultiEdgeApproxInserter::Block::pathToArray(int i, Array<PathElement> &path)
206 {
207 SPQRPath &sp = m_pathSPQR[i];
208
209 if(sp.m_start == nullptr) {
210 path.init();
211 return;
212 }
213
214 path.init(1+sp.m_edges.size());
215
216 ListConstIterator<edge> itE = sp.m_edges.begin();
217 ListConstIterator<EmbeddingPreference> itP = sp.m_prefs.begin();
218
219 node n = sp.m_start;
220
221 path[0].m_node = n;
222 if(m_spqr->typeOf(n) != SPQRTree::NodeType::SNode)
223 path[0].m_pref = &(*itP++);
224
225 int j;
226 for(j = 1; itE.valid(); ++j)
227 {
228 n = (*itE++)->opposite(n);
229 path[j].m_node = n;
230
231 if(m_spqr->typeOf(n) != SPQRTree::NodeType::SNode)
232 path[j].m_pref = &(*itP++);
233 }
234
235 OGDF_ASSERT(j == path.size());
236 }
237
238
embPrefAgree(node n,const EmbeddingPreference & p_pick,const EmbeddingPreference & p_e)239 bool MultiEdgeApproxInserter::Block::embPrefAgree(node n, const EmbeddingPreference &p_pick, const EmbeddingPreference &p_e)
240 {
241 switch(m_spqr->typeOf(n)) {
242 case SPQRTree::NodeType::RNode:
243 return p_pick.mirror() == p_e.mirror(); // check if mirroring is the same
244
245 case SPQRTree::NodeType::PNode:
246 // if p_e okay: check if adj entries in (embedded) P-Node are in the right order
247 return p_e.isNull() ? true : p_e.adj1()->cyclicSucc() == p_e.adj2();
248
249 default:
250 return true; // any other case "agrees"
251 }
252 }
253
254
switchingPair(node n,node m,const EmbeddingPreference & p_pick_n,const EmbeddingPreference & p_n,const EmbeddingPreference & p_pick_m,const EmbeddingPreference & p_m)255 bool MultiEdgeApproxInserter::Block::switchingPair(
256 node n, node m,
257 const EmbeddingPreference &p_pick_n, const EmbeddingPreference &p_n,
258 const EmbeddingPreference &p_pick_m, const EmbeddingPreference &p_m)
259 {
260 EmbeddingPreference p_n_f = p_n;
261 EmbeddingPreference p_m_f = p_m;
262
263 p_n_f.flip();
264 p_m_f.flip();
265
266 return
267 (embPrefAgree(n, p_pick_n, p_n) && embPrefAgree(m, p_pick_m, p_m_f)) ||
268 (embPrefAgree(n, p_pick_n, p_n_f) && embPrefAgree(m, p_pick_m, p_m));
269 }
270
271
272 // create SPQR-tree and compute allocation nodes
initSPQR(int m)273 void MultiEdgeApproxInserter::Block::initSPQR(int m)
274 {
275 if(m_spqr == nullptr) {
276 m_spqr = new StaticPlanarSPQRTree(*this,true);
277 m_pathSPQR.init(m);
278
279 const Graph &tree = m_spqr->tree();
280 m_tc.init(tree);
281 m_info.init(tree);
282
283 // compute allocation nodes
284 m_allocNodes.init(*this);
285
286 for(node n : tree.nodes)
287 {
288 const Skeleton &S = m_spqr->skeleton(n);
289 const Graph &M = S.getGraph();
290
291 EdgeArray<int> &tcS = m_tc[n];
292 tcS.init(M,-1);
293
294 for(node x : M.nodes)
295 m_allocNodes[S.original(x)].push(n);
296
297 for(edge e : M.edges) {
298 edge eOrig = S.realEdge(e);
299 if(eOrig != nullptr) tcS[e] = 1;
300 }
301 }
302 }
303 }
304
305
306 // construct dual graph of skeleton of n
constructDual(node n)307 void MultiEdgeApproxInserter::Block::constructDual(node n)
308 {
309 const StaticSkeleton &S = *dynamic_cast<StaticSkeleton*>(&m_spqr->skeleton(n));
310 const Graph &M = S.getGraph();
311
312 OGDF_ASSERT(m_info[n].m_dual == nullptr);
313
314 ConstCombinatorialEmbedding *emb = m_info[n].m_emb = new ConstCombinatorialEmbedding(M);
315 Graph *dual = m_info[n].m_dual = new Graph;
316 FaceArray<node> *faceNode = m_info[n].m_faceNode = new FaceArray<node>(*emb);
317 AdjEntryArray<adjEntry> *primalAdj = m_info[n].m_primalAdj = new AdjEntryArray<adjEntry>(*dual);
318
319 // constructs nodes (for faces in emb)
320 for(face f : emb->faces) {
321 (*faceNode)[f] = dual->newNode();
322 }
323
324 // construct dual edges (for primal edges in M)
325 for(node v : M.nodes)
326 {
327 for(adjEntry adj : v->adjEntries)
328 {
329 if(adj->index() & 1) {
330 node vLeft = (*faceNode)[emb->leftFace (adj)];
331 node vRight = (*faceNode)[emb->rightFace(adj)];
332
333 edge eDual = dual->newEdge(vLeft,vRight);
334 (*primalAdj)[eDual->adjSource()] = adj;
335 (*primalAdj)[eDual->adjTarget()] = adj->twin();
336 }
337 }
338 }
339 }
340
341
constructDualBlock()342 void MultiEdgeApproxInserter::Block::constructDualBlock()
343 {
344 m_embB = new ConstCombinatorialEmbedding(*this);
345 m_dualB = new Graph;
346 m_faceNodeB = new FaceArray<node>(*m_embB);
347 m_primalAdjB = new AdjEntryArray<adjEntry>(*m_dualB);
348
349 // constructs nodes (for faces in m_embB)
350 for(face f : m_embB->faces) {
351 (*m_faceNodeB)[f] = m_dualB->newNode();
352 }
353
354 // construct dual edges (for primal edges in block)
355 for(node v : nodes)
356 {
357 for(adjEntry adj : v->adjEntries)
358 {
359 if(adj->index() & 1) {
360 node vLeft = (*m_faceNodeB)[m_embB->leftFace (adj)];
361 node vRight = (*m_faceNodeB)[m_embB->rightFace(adj)];
362
363 edge eDual = m_dualB->newEdge(vLeft,vRight);
364 (*m_primalAdjB)[eDual->adjSource()] = adj;
365 (*m_primalAdjB)[eDual->adjTarget()] = adj->twin();
366 }
367 }
368 }
369
370 m_vS = m_dualB->newNode();
371 m_vT = m_dualB->newNode();
372 }
373
374
findBestFace(node s,node t,int & len)375 adjEntry MultiEdgeApproxInserter::Block::findBestFace(node s, node t, int &len)
376 {
377 if(isBridge()) {
378 len = 0;
379 return s->firstAdj();
380 }
381
382 adjEntry adj_s, adj_t;
383 len = findBestFaces(s,t,adj_s,adj_t);
384 return adj_s;
385 }
386
387
findBestFaces(node s,node t,adjEntry & adj_s,adjEntry & adj_t)388 int MultiEdgeApproxInserter::Block::findBestFaces(
389 node s, node t, adjEntry &adj_s, adjEntry &adj_t)
390 {
391 if(m_dualB == nullptr)
392 constructDualBlock();
393
394 NodeArray<adjEntry> spPred(*m_dualB, nullptr);
395 QueuePure<adjEntry> queue;
396 int oldIdCount = m_dualB->maxEdgeIndex();
397
398 // augment dual by edges from s to all adjacent faces of s ...
399 for(adjEntry adj : s->adjEntries) {
400 // starting edges of bfs-search are all edges leaving s
401 edge eDual = m_dualB->newEdge(m_vS, (*m_faceNodeB)[m_embB->rightFace(adj)]);
402 (*m_primalAdjB)[eDual->adjSource()] = adj;
403 (*m_primalAdjB)[eDual->adjTarget()] = nullptr;
404 queue.append(eDual->adjSource());
405 }
406
407 // ... and from all adjacent faces of t to t
408 for(adjEntry adj : t->adjEntries) {
409 edge eDual = m_dualB->newEdge((*m_faceNodeB)[m_embB->rightFace(adj)], m_vT);
410 (*m_primalAdjB)[eDual->adjSource()] = adj;
411 (*m_primalAdjB)[eDual->adjTarget()] = nullptr;
412 }
413
414 // actual search (using bfs on directed dual)
415 int len = -2;
416 for( ; ;)
417 {
418 // next candidate edge
419 adjEntry adjCand = queue.pop();
420 node v = adjCand->twinNode();
421
422 // leads to an unvisited node?
423 if (spPred[v] == nullptr)
424 {
425 // yes, then we set v's predecessor in search tree
426 spPred[v] = adjCand;
427
428 // have we reached t ...
429 if (v == m_vT)
430 {
431 // ... then search is done.
432 adj_t = (*m_primalAdjB)[adjCand];
433
434 adjEntry adj;
435 do {
436 adj = spPred[v];
437 ++len;
438 v = adj->theNode();
439 } while(v != m_vS);
440 adj_s = (*m_primalAdjB)[adj];
441
442 break;
443 }
444
445 // append next candidate edges to queue
446 // (all edges leaving v)
447 for(adjEntry adj : v->adjEntries) {
448 if(adj->twinNode() != m_vS)
449 queue.append(adj);
450 }
451 }
452 }
453
454 // remove augmented edges again
455 adjEntry adj;
456 while ((adj = m_vS->firstAdj()) != nullptr)
457 m_dualB->delEdge(adj->theEdge());
458
459 while ((adj = m_vT->firstAdj()) != nullptr)
460 m_dualB->delEdge(adj->theEdge());
461
462 m_dualB->resetEdgeIdCount(oldIdCount);
463
464 return len;
465 }
466
467
468 // compute traversing costs in skelton
recTC(node n,edge eRef)469 int MultiEdgeApproxInserter::Block::recTC(node n, edge eRef)
470 {
471 const StaticSkeleton &S = *dynamic_cast<StaticSkeleton*>(&m_spqr->skeleton(n));
472 const Graph &M = S.getGraph();
473 EdgeArray<int> &tcS = m_tc[n];
474
475 for(edge e : M.edges) {
476 if(tcS[e] == -1 && e != eRef) {
477 edge eT = S.treeEdge(e);
478
479 node nC;
480 edge eRefC;
481 if(n == eT->source()) {
482 nC = eT->target(); eRefC = m_spqr->skeletonEdgeTgt(eT);
483 } else {
484 nC = eT->source(); eRefC = m_spqr->skeletonEdgeSrc(eT);
485 }
486
487 tcS[e] = recTC(nC,eRefC);
488 }
489 }
490
491 int c = 1;
492 switch(m_spqr->typeOf(n))
493 {
494 case SPQRTree::NodeType::SNode:
495 c = std::numeric_limits<int>::max();
496 for(edge e : M.edges)
497 if(e != eRef) c = min(c, tcS[e]);
498 break;
499
500 case SPQRTree::NodeType::PNode:
501 c = 0;
502 for(edge e : M.edges)
503 if(e != eRef) c += tcS[e];
504 break;
505
506 case SPQRTree::NodeType::RNode:
507 if(m_info[n].m_dual == nullptr)
508 constructDual(n);
509
510 c = findShortestPath(n, eRef);
511 break;
512 }
513
514 return c;
515 }
516
computeTraversingCosts(node n,edge e1,edge e2)517 void MultiEdgeApproxInserter::Block::computeTraversingCosts(node n, edge e1, edge e2)
518 {
519 const StaticSkeleton &S = *dynamic_cast<StaticSkeleton*>(&m_spqr->skeleton(n));
520 const Graph &M = S.getGraph();
521 EdgeArray<int> &tcS = m_tc[n];
522
523 for(edge e : M.edges) {
524 if(tcS[e] == -1 && e != e1 && e != e2) {
525 edge eT = S.treeEdge(e);
526
527 node nC;
528 edge eRef;
529 if(n == eT->source()) {
530 nC = eT->target(); eRef = m_spqr->skeletonEdgeTgt(eT);
531 } else {
532 nC = eT->source(); eRef = m_spqr->skeletonEdgeSrc(eT);
533 }
534
535 tcS[e] = recTC(nC,eRef);
536 }
537 }
538 }
539
540
findShortestPath(node n,edge eRef)541 int MultiEdgeApproxInserter::Block::findShortestPath(node n, edge eRef)
542 {
543 const StaticSkeleton &S = *dynamic_cast<StaticSkeleton*>(&m_spqr->skeleton(n));
544 const Graph &M = S.getGraph();
545 const EdgeArray<int> &tcS = m_tc[n];
546
547 const Graph &dual = *m_info[n].m_dual;
548 const ConstCombinatorialEmbedding &emb = *m_info[n].m_emb;
549 const FaceArray<node> faceNode = *m_info[n].m_faceNode;
550 const AdjEntryArray<adjEntry> primalAdj = *m_info[n].m_primalAdj;
551
552 int maxTC = 0;
553 for(edge e : M.edges)
554 Math::updateMax(maxTC, tcS[e]);
555
556 ++maxTC;
557 Array<SListPure<adjEntry> > nodesAtDist(maxTC);
558
559 NodeArray<adjEntry> spPred(dual,nullptr); // predecessor in shortest path tree
560
561 node vS = faceNode[emb.rightFace(eRef->adjSource())];
562 node vT = faceNode[emb.rightFace(eRef->adjTarget())];
563
564 // start with all edges leaving from vS
565 for(adjEntry adj : vS->adjEntries) {
566 edge eOrig = primalAdj[adj]->theEdge();
567 if(eOrig != eRef)
568 nodesAtDist[tcS[eOrig]].pushBack(adj);
569 }
570
571 int currentDist = 0;
572 for( ; ; ) {
573 // next candidate edge
574 while(nodesAtDist[currentDist % maxTC].empty())
575 ++currentDist;
576
577 adjEntry adjCand = nodesAtDist[currentDist % maxTC].popFrontRet();
578 node v = adjCand->twinNode();
579
580 // leads to an unvisited node ?
581 if (spPred[v] == nullptr) {
582 // yes, then we set v's predecessor in search tree
583 spPred[v] = adjCand;
584
585 // have we reached t ...
586 if (v == vT) {
587 // ... then search is done.
588 return currentDist;
589 }
590
591 // append next candidates to end of queue
592 for(adjEntry adj : v->adjEntries) {
593 int listPos = (currentDist + tcS[primalAdj[adj]->theEdge()]) % maxTC;
594 nodesAtDist[listPos].pushBack(adj);
595 }
596 }
597 }
598 }
599
600
costsSubpath(node n,edge eIn,edge eOut,node s,node t,PathDir & dirFrom,PathDir & dirTo)601 int MultiEdgeApproxInserter::Block::costsSubpath(node n, edge eIn, edge eOut, node s, node t, PathDir &dirFrom, PathDir &dirTo)
602 {
603 if(m_info[n].m_dual == nullptr)
604 constructDual(n);
605
606 const StaticSkeleton &S = *dynamic_cast<StaticSkeleton*>(&m_spqr->skeleton(n));
607 const Graph &M = S.getGraph();
608 const EdgeArray<int> &tcS = m_tc[n];
609
610 const Graph &dual = *m_info[n].m_dual;
611 const ConstCombinatorialEmbedding &emb = *m_info[n].m_emb;
612 const FaceArray<node> faceNode = *m_info[n].m_faceNode;
613 const AdjEntryArray<adjEntry> primalAdj = *m_info[n].m_primalAdj;
614
615 node v1 = nullptr, v2 = nullptr;
616 if(eIn == nullptr || eOut == nullptr) {
617 for(node v : M.nodes) {
618 node vOrig = S.original(v);
619 if(vOrig == s) v1 = v;
620 if(vOrig == t) v2 = v;
621 }
622 }
623
624 edge e1 = (eIn == nullptr) ? nullptr : ((n != eIn ->source()) ? m_spqr->skeletonEdgeTgt(eIn) : m_spqr->skeletonEdgeSrc(eIn));
625 edge e2 = (eOut == nullptr) ? nullptr : ((n != eOut->source()) ? m_spqr->skeletonEdgeTgt(eOut) : m_spqr->skeletonEdgeSrc(eOut));
626
627 computeTraversingCosts(n, e1, e2);
628
629 int maxTC = 0;
630 for(edge e : M.edges)
631 Math::updateMax(maxTC, tcS[e]);
632
633 ++maxTC;
634 Array<SListPure<Tuple2<node,node> > > nodesAtDist(maxTC);
635
636 // start vertices
637 if(e1 != nullptr) {
638 nodesAtDist[0].pushBack( Tuple2<node,node>(faceNode[emb.rightFace(e1->adjSource())], nullptr) );
639 nodesAtDist[0].pushBack( Tuple2<node,node>(faceNode[emb.rightFace(e1->adjTarget())], nullptr) );
640 } else {
641 for(adjEntry adj : v1->adjEntries)
642 nodesAtDist[0].pushBack( Tuple2<node,node>(faceNode[emb.rightFace(adj)], nullptr) );
643 }
644
645 // stop vertices
646 NodeArray<bool> stopVertex(dual,false);
647
648 if(e2 != nullptr) {
649 stopVertex[faceNode[emb.rightFace(e2->adjSource())]] = true;
650 stopVertex[faceNode[emb.rightFace(e2->adjTarget())]] = true;
651 } else {
652 for(adjEntry adj : v2->adjEntries)
653 stopVertex[faceNode[emb.rightFace(adj)]] = true;
654 }
655
656 // actual shortest path search
657 NodeArray<bool> visited(dual,false);
658 NodeArray<node> spPred(dual,nullptr);
659
660 int currentDist = 0;
661 for( ; ; ) {
662 // next candidate
663 while(nodesAtDist[currentDist % maxTC].empty())
664 ++currentDist;
665
666 Tuple2<node,node> pair = nodesAtDist[currentDist % maxTC].popFrontRet();
667 node v = pair.x1();
668
669 // leads to an unvisited node ?
670 if (!visited[v]) {
671 visited[v] = true;
672 spPred[v] = pair.x2();
673
674 // have we reached t ...
675 if (stopVertex[v]) {
676 // ... then search is done.
677
678 // find start node w
679 node w = v;
680 while(spPred[w] != nullptr)
681 w = spPred[w];
682
683 // in which direction to we start
684 if(e1 == nullptr)
685 dirFrom = PathDir::None; // doesn't matter
686 else if(w == faceNode[emb.rightFace(e1->adjSource())])
687 dirFrom = PathDir::Right; // right face of eIn
688 else
689 dirFrom = PathDir::Left; // left face of eIn
690
691 // from which direction to we enter
692 if(e2 == nullptr)
693 dirTo = PathDir::None; // doesn't matter
694 else if(v == faceNode[emb.rightFace(e2->adjSource())])
695 dirTo = PathDir::Left; // right face of eOut (leaving to the right)
696 else
697 dirTo = PathDir::Right; // left face of eOut (leaving to the left)
698
699 return currentDist;
700 }
701
702 // append next candidates to end of queue
703 for(adjEntry adj : v->adjEntries) {
704 edge eM = primalAdj[adj]->theEdge();
705 if(eM != e1 && eM != e2) {
706 int listPos = (currentDist + tcS[eM]) % maxTC;
707 nodesAtDist[listPos].pushBack(
708 Tuple2<node,node>(adj->twinNode(), v) );
709 }
710 }
711 }
712 }
713 }
714
715
716 // sets default values for options
MultiEdgeApproxInserter()717 MultiEdgeApproxInserter::MultiEdgeApproxInserter()
718 {
719 // options
720 m_rrOptionFix = RemoveReinsertType::None;
721 m_rrOptionVar = RemoveReinsertType::None;
722 m_percentMostCrossedFix = 25;
723 m_percentMostCrossedVar = 25;
724 m_statistics = false;
725
726 // initialization
727 m_edge = nullptr;
728 m_sumInsertionCosts = 0;
729 m_sumFEInsertionCosts = 0;
730 }
731
732
733 // copy constructor
MultiEdgeApproxInserter(const MultiEdgeApproxInserter & inserter)734 MultiEdgeApproxInserter::MultiEdgeApproxInserter(const MultiEdgeApproxInserter &inserter)
735 : EdgeInsertionModule()
736 {
737 // options
738 m_rrOptionFix = inserter.m_rrOptionFix;
739 m_rrOptionVar = inserter.m_rrOptionVar;
740 m_percentMostCrossedFix = inserter.m_percentMostCrossedFix;
741 m_percentMostCrossedVar = inserter.m_percentMostCrossedVar;
742 m_statistics = inserter.m_statistics;
743
744 // initialization
745 m_edge = nullptr;
746 m_sumInsertionCosts = 0;
747 m_sumFEInsertionCosts = 0;
748 }
749
750
751 // clone method
clone() const752 EdgeInsertionModule *MultiEdgeApproxInserter::clone() const
753 {
754 return new MultiEdgeApproxInserter(*this);
755 }
756
757
758 // assignment operator
operator =(const MultiEdgeApproxInserter & inserter)759 MultiEdgeApproxInserter &MultiEdgeApproxInserter::operator=(const MultiEdgeApproxInserter &inserter)
760 {
761 // options
762 m_rrOptionFix = inserter.m_rrOptionFix;
763 m_rrOptionVar = inserter.m_rrOptionVar;
764 m_percentMostCrossedFix = inserter.m_percentMostCrossedFix;
765 m_percentMostCrossedVar = inserter.m_percentMostCrossedVar;
766 m_statistics = inserter.m_statistics;
767
768 return *this;
769 }
770
771
772 // construct graph of block i
773 // store mapping of original nodes to copies in blocks
constructBlock(int i)774 MultiEdgeApproxInserter::Block *MultiEdgeApproxInserter::constructBlock(int i)
775 {
776 Block *b = new Block;
777 SList<node> nodesG;
778
779 SListConstIterator<edge> itE;
780 for(itE = m_edgesB[i].begin(); itE.valid(); ++itE)
781 {
782 edge e = *itE;
783
784 if (m_GtoBC[e->source()] == nullptr) {
785 m_GtoBC[e->source()] = b->newNode();
786 nodesG.pushBack(e->source());
787 }
788 if (m_GtoBC[e->target()] == nullptr) {
789 m_GtoBC[e->target()] = b->newNode();
790 nodesG.pushBack(e->target());
791 }
792
793 edge eBC = b->newEdge(m_GtoBC[e->source()],m_GtoBC[e->target()]);
794 b->m_BCtoG[eBC->adjSource()] = e->adjSource();
795 b->m_BCtoG[eBC->adjTarget()] = e->adjTarget();
796
797 edge eOrig = m_pPG->original(e);
798 if(m_costOrig != nullptr)
799 b->m_cost[eBC] = (eOrig == nullptr) ? 0 : (*m_costOrig)[eOrig];
800 }
801
802 // store mapping orginal nodes -> copy in blocks and reset entries of GtoBC
803 SListConstIterator<node> itV;
804 for(itV = nodesG.begin(); itV.valid(); ++itV) {
805 node v = *itV;
806 m_copyInBlocks[v].pushBack(VertexBlock(m_GtoBC[v],i));
807 m_GtoBC[v] = nullptr;
808 }
809
810 planarEmbed(*b);
811
812 return b;
813 }
814
815
816 // returns copy of node vOrig in block b
copy(node vOrig,int b)817 node MultiEdgeApproxInserter::copy(node vOrig, int b)
818 {
819 SListConstIterator<VertexBlock> it;
820 for(it = m_copyInBlocks[vOrig].begin(); it.valid(); ++it) {
821 if((*it).m_block == b)
822 return (*it).m_vertex;
823 }
824
825 return nullptr;
826 }
827
828
829 // DFS-traversal for computing insertion path in SPQR-tree
dfsPathSPQR(node v,node v2,edge eParent,List<edge> & path)830 bool MultiEdgeApproxInserter::dfsPathSPQR(node v, node v2, edge eParent, List<edge> &path)
831 {
832 if(v == v2)
833 return true;
834
835 for(adjEntry adj : v->adjEntries) {
836 edge e = adj->theEdge();
837 if(e == eParent) continue;
838
839 if(dfsPathSPQR(e->opposite(v), v2, e, path)) {
840 path.pushFront(e);
841 return true;
842 }
843 }
844
845 return false;
846 }
847
848
849 // compute insertion path of edge edge k in SPQR-tree of block b
850 // from node vOrig to node wOrig
computePathSPQR(int b,node vOrig,node wOrig,int k)851 int MultiEdgeApproxInserter::computePathSPQR(int b, node vOrig, node wOrig, int k)
852 {
853 Block &B = *m_block[b];
854
855 node v = copy(vOrig,b);
856 node w = copy(wOrig,b);
857 OGDF_ASSERT(v != nullptr);
858 OGDF_ASSERT(w != nullptr);
859
860 B.initSPQR(m_edge->size());
861
862 const auto &vAllocNodes = B.m_allocNodes[v];
863 const auto &wAllocNodes = B.m_allocNodes[w];
864 List<edge> &path = B.m_pathSPQR[k].m_edges;
865
866 node v1 = vAllocNodes[0];
867 node v2 = wAllocNodes[0];
868
869 dfsPathSPQR( v1, v2, nullptr, path );
870
871 node x;
872 while(!path.empty() && vAllocNodes.linearSearch(x = path.front()->opposite(v1)) != -1) {
873 v1 = x;
874 path.popFront();
875 }
876
877 B.m_pathSPQR[k].m_start = v1;
878
879 while(!path.empty() && wAllocNodes.linearSearch(x = path.back()->opposite(v2)) != -1) {
880 v2 = x;
881 path.popBack();
882 }
883
884 // compute insertion costs
885 const StaticPlanarSPQRTree &spqr = B.spqr();
886 List<EmbeddingPreference> &prefs = B.m_pathSPQR[k].m_prefs;
887
888 PathDir dirFrom, dirTo;
889 PathDir curDir = PathDir::Right;
890 int c = 0;
891
892 switch(spqr.typeOf(v1)) {
893 case SPQRTree::NodeType::RNode:
894 c += B.costsSubpath(v1, nullptr, (path.empty()) ? nullptr : path.front(), v, w, dirFrom, dirTo);
895 prefs.pushBack(EmbeddingPreference(false));
896 curDir = dirTo;
897 break;
898
899 case SPQRTree::NodeType::PNode:
900 // in this case, the insertion path is a single P-node and we have no preference
901 prefs.pushBack(EmbeddingPreference());
902 break;
903
904 case SPQRTree::NodeType::SNode:
905 break; // nothing to do
906 }
907
908 node n = v1;
909 ListConstIterator<edge> it;
910 for(it = path.begin(); it.valid(); ++it)
911 {
912 edge e = *it;
913 n = e->opposite(n);
914
915 switch(spqr.typeOf(n)) {
916 case SPQRTree::NodeType::RNode:
917 c += B.costsSubpath(n, e, it.succ().valid() ? *it.succ() : nullptr, v, w, dirFrom, dirTo);
918
919 // do we need to mirror embedding of R-node?
920 prefs.pushBack(EmbeddingPreference(dirFrom != curDir));
921 curDir = dirFrom == curDir ? dirTo : oppDir(dirTo);
922 break;
923
924 case SPQRTree::NodeType::PNode:
925 {
926 edge eIn = e;
927 edge eOut = *(it.succ());
928
929 edge e1 = (n != eIn ->source()) ? spqr.skeletonEdgeTgt(eIn) : spqr.skeletonEdgeSrc(eIn);
930 edge e2 = (n != eOut->source()) ? spqr.skeletonEdgeTgt(eOut) : spqr.skeletonEdgeSrc(eOut);
931
932 const Graph &M = spqr.skeleton(n).getGraph();
933 node first = M.firstNode();
934
935 adjEntry a1 = (e1->source() == first) ? e1->adjSource() : e1->adjTarget();
936 adjEntry a2 = (e2->source() == first) ? e2->adjSource() : e2->adjTarget();
937
938 bool srcFits = (e1->source() == first);
939 if( (curDir == PathDir::Left && srcFits) || (curDir == PathDir::Right && !srcFits) )
940 std::swap(a1, a2);
941 prefs.pushBack(EmbeddingPreference(a1,a2));
942
943 if(e1->source() != e2->source())
944 curDir = oppDir(curDir);
945 }
946 break;
947
948 case SPQRTree::NodeType::SNode:
949 if(it.succ().valid()) {
950 edge eIn = e;
951 edge eOut = *(it.succ());
952
953 edge e1 = (n != eIn ->source()) ? spqr.skeletonEdgeTgt(eIn) : spqr.skeletonEdgeSrc(eIn);
954 edge e2 = (n != eOut->source()) ? spqr.skeletonEdgeTgt(eOut) : spqr.skeletonEdgeSrc(eOut);
955
956 adjEntry at = e1->adjSource();
957 while(at->theEdge() != e2)
958 at = at->twin()->cyclicSucc();
959
960 if(at == e2->adjSource())
961 curDir = oppDir(curDir);
962 }
963 break; // nothing to do
964 }
965 }
966
967 return c;
968 }
969
970
971 // DFS-traversal for computing insertion path in BC-tree
972 // (case vertex v)
dfsPathVertex(node v,int parent,int k,node t)973 bool MultiEdgeApproxInserter::dfsPathVertex(node v, int parent, int k, node t)
974 {
975 if(v == t) return true;
976
977 SListConstIterator<int> it;
978 for(it = m_compV[v].begin(); it.valid(); ++it) {
979 if(*it == parent) continue;
980 if(dfsPathBlock(*it,v,k,t)) return true;
981 }
982 return false;
983 }
984
985
986 // DFS-traversal for computing insertion path in BC-tree
987 // (case block b)
dfsPathBlock(int b,node parent,int k,node t)988 bool MultiEdgeApproxInserter::dfsPathBlock(int b, node parent, int k, node t)
989 {
990 SListConstIterator<node> it;
991 for(it = m_verticesB[b].begin(); it.valid(); ++it)
992 {
993 node c = *it;
994 if(c == parent) continue;
995 if(dfsPathVertex(c,b,k,t)) {
996 m_pathBCs[k].pushFront(VertexBlock(parent,b));
997
998 if(!m_block[b]->isBridge())
999 {
1000 // find path from parent to c in block b and compute embedding preferences
1001 m_insertionCosts[k] += computePathSPQR(b, parent, c, k);
1002 }
1003
1004 return true;
1005 }
1006 }
1007 return false;
1008 }
1009
1010
1011 // compute insertion path of edge edge k in BC-tree
computePathBC(int k)1012 void MultiEdgeApproxInserter::computePathBC(int k)
1013 {
1014 node s = m_pPG->copy((*m_edge)[k]->source());
1015 node t = m_pPG->copy((*m_edge)[k]->target());
1016
1017 bool found = dfsPathVertex(s, -1, k, t);
1018 if(!found) std::cout << "Could not find path in BC-tree!" << std::endl;
1019 }
1020
1021
1022 // Embeds block b according to combined embedding
1023 // preference s of the k insertion paths
embedBlock(int b,int m)1024 void MultiEdgeApproxInserter::embedBlock(int b, int m)
1025 {
1026 // Algorithm 3.7.
1027
1028 Block &B = *m_block[b];
1029 if(B.isBridge())
1030 return;
1031 StaticPlanarSPQRTree &spqr = B.spqr();
1032
1033 // A.
1034 NodeArray<EmbeddingPreference> pi_pick(spqr.tree());
1035 NodeArray<bool> visited(spqr.tree(), false); // mark nodes with embedding preferences
1036 NodeArray<bool> markFlipped(spqr.tree(), false); // mark nodes on current path to get flipped
1037 Array<Block::PathElement> p;
1038
1039 // B.
1040 for(int i = 0; i < m; ++i)
1041 {
1042 // (a)
1043 B.pathToArray(i, p);
1044 const int len = p.size();
1045 if(len == 0) continue; // this path does not go through B
1046
1047 int j = 0;
1048 do {
1049 node n = p[j].m_node;
1050
1051 // (b)
1052 bool newlySet = false;
1053 if(pi_pick[n].isNull()) {
1054 newlySet = true;
1055 pi_pick[n] = *p[j].m_pref;
1056
1057 if(spqr.typeOf(n) == SPQRTree::NodeType::PNode) {
1058 adjEntry a1 = pi_pick[n].adj1();
1059 adjEntry a2 = pi_pick[n].adj2();
1060 adjEntry adj = a1->cyclicSucc();
1061
1062 if(adj != a2)
1063 spqr.swap(n,adj,a2);
1064
1065 OGDF_ASSERT(a1->cyclicSucc() == a2);
1066 }
1067 }
1068
1069 // (c)
1070
1071 // determine non-S-node predecessor of n
1072 int j_mu = -1;
1073 if(j > 0) {
1074 node x = p[j-1].m_node;
1075 if(spqr.typeOf(x) != SPQRTree::NodeType::SNode)
1076 j_mu = j-1;
1077 else if(j > 1)
1078 j_mu = j-2;
1079 }
1080
1081 if(j_mu >= 0)
1082 {
1083 node mu = p[j_mu].m_node;
1084
1085 // do we have a switching pair (mu,n)?
1086 if(B.switchingPair(mu, n, pi_pick[mu], *p[j_mu].m_pref, pi_pick[n], *p[j].m_pref))
1087 markFlipped[mu] = true; // flip embedding at mu
1088 }
1089
1090 // (d)
1091 if(!newlySet) {
1092 // skip nodes in same embedding partition
1093 while(j+1 < len && visited[p[j+1].m_node])
1094 ++j;
1095 }
1096
1097 // next node
1098 if(++j >= len)
1099 break; // end of insertion path
1100
1101 // skip S-node (can only be one)
1102 if(spqr.typeOf(p[j].m_node) == SPQRTree::NodeType::SNode)
1103 ++j;
1104
1105 } while(j < len);
1106
1107 // flip embedding preferences
1108 bool flipping = false;
1109 for(int iter = len-1; iter >= 0; --iter)
1110 {
1111 node n = p[iter].m_node;
1112 if(markFlipped[n]) {
1113 flipping = !flipping;
1114 markFlipped[n] = false;
1115 }
1116
1117 if(flipping) {
1118 pi_pick[n].flip();
1119 if(pi_pick[n].type() == EmbeddingPreference::Type::PNode)
1120 spqr.reverse(n);
1121
1122 if(visited[n]) {
1123 node n_succ = (iter+1 < len) ? p[iter+1].m_node : nullptr;
1124 node n_pred = (iter-1 >= 0) ? p[iter-1].m_node : nullptr;
1125
1126 for(adjEntry adj : n->adjEntries) {
1127 node x = adj->twinNode();
1128 if(x != n_succ && x != n_pred && visited[x])
1129 recFlipPref(adj->twin(), pi_pick, visited, spqr);
1130 }
1131 }
1132 }
1133
1134 visited[n] = true;
1135 }
1136 }
1137
1138 // C.
1139 for(node n : spqr.tree().nodes)
1140 {
1141 const EmbeddingPreference &ep = pi_pick[n];
1142 if(ep.isNull())
1143 continue;
1144
1145 switch(spqr.typeOf(n)) {
1146 case SPQRTree::NodeType::SNode:
1147 break; // nothing to do
1148
1149 case SPQRTree::NodeType::PNode:
1150 break; // already embedded as desired
1151
1152 case SPQRTree::NodeType::RNode:
1153 if(ep.mirror())
1154 spqr.reverse(n);
1155 break;
1156 }
1157 }
1158
1159 spqr.embed(B);
1160 }
1161
1162
recFlipPref(adjEntry adjP,NodeArray<EmbeddingPreference> & pi_pick,const NodeArray<bool> & visited,StaticPlanarSPQRTree & spqr)1163 void MultiEdgeApproxInserter::recFlipPref(
1164 adjEntry adjP,
1165 NodeArray<EmbeddingPreference> &pi_pick,
1166 const NodeArray<bool> &visited,
1167 StaticPlanarSPQRTree &spqr)
1168 {
1169 node n = adjP->theNode();
1170
1171 EmbeddingPreference &pref = pi_pick[n];
1172 pref.flip();
1173 if(pref.type() == EmbeddingPreference::Type::PNode)
1174 spqr.reverse(n);
1175
1176 for(adjEntry adj : n->adjEntries) {
1177 if(adj != adjP && visited[adj->twinNode()])
1178 recFlipPref(adj->twin(), pi_pick, visited, spqr);
1179 }
1180 }
1181
1182
1183 // actual call method
1184 // currently not supported:
1185 // frobidCrossingGens, forbiddenEdgeOrig, edgeSubGraphs
1186 const char *strType[] = { "S", "P", "R" };
1187 //#define MEAI_OUTPUT
1188
1189 struct CutvertexPreference {
CutvertexPreferenceogdf::CutvertexPreference1190 CutvertexPreference(node v1, int b1, node v2, int b2)
1191 : m_v1(v1), m_v2(v2), m_b1(b1), m_b2(b2), m_len1(-1), m_len2(-1) { }
1192
1193 node m_v1, m_v2;
1194 int m_b1, m_b2;
1195 int m_len1, m_len2;
1196 };
1197
appendToList(SListPure<adjEntry> & adjList,adjEntry adj1,const AdjEntryArray<adjEntry> & BCtoG,AdjEntryArray<SListIterator<adjEntry>> & pos)1198 void appendToList(SListPure<adjEntry> &adjList, adjEntry adj1,
1199 const AdjEntryArray<adjEntry> &BCtoG,
1200 AdjEntryArray<SListIterator<adjEntry> > &pos)
1201 {
1202 adjEntry adj = adj1;
1203 do {
1204 adj = adj->cyclicSucc();
1205 adjEntry adjG = BCtoG[adj];
1206 pos[adjG] = adjList.pushBack(adjG);
1207 } while(adj != adj1);
1208 }
1209
insertAfterList(SListPure<adjEntry> & adjList,SListIterator<adjEntry> itBefore,adjEntry adj1,const AdjEntryArray<adjEntry> & BCtoG,AdjEntryArray<SListIterator<adjEntry>> & pos)1210 void insertAfterList(SListPure<adjEntry> &adjList, SListIterator<adjEntry> itBefore, adjEntry adj1,
1211 const AdjEntryArray<adjEntry> &BCtoG,
1212 AdjEntryArray<SListIterator<adjEntry> > &pos)
1213 {
1214 adjEntry adj = adj1;
1215 do {
1216 adj = adj->cyclicSucc();
1217 adjEntry adjG = BCtoG[adj];
1218 itBefore = pos[adjG] = adjList.insertAfter(adjG, itBefore);
1219 } while(adj != adj1);
1220 }
1221
doCall(PlanRepLight & PG,const Array<edge> & origEdges,const EdgeArray<int> * costOrig,const EdgeArray<bool> *,const EdgeArray<uint32_t> *)1222 Module::ReturnType MultiEdgeApproxInserter::doCall(
1223 PlanRepLight& PG,
1224 const Array<edge>& origEdges,
1225 const EdgeArray<int>* costOrig,
1226 const EdgeArray<bool>* /* unused parameter */,
1227 const EdgeArray<uint32_t>* /* unused parameter */)
1228 {
1229 m_pPG = &PG;
1230 m_costOrig = costOrig;
1231 const int m = origEdges.size();
1232
1233 m_edge = &origEdges;
1234 OGDF_ASSERT(m_edge->low() == 0);
1235 m_pathBCs.init(m);
1236 m_insertionCosts.init(0,m-1,0);
1237
1238 //
1239 // PHASE 1: Fix a good embedding
1240 //
1241
1242 // compute biconnected components of PG
1243 EdgeArray<int> compnum(PG);
1244 int c = biconnectedComponents(PG,compnum);
1245
1246 m_compV.init(PG);
1247 m_verticesB.init(c);
1248
1249 // edgesB[i] = list of edges in component i
1250 m_edgesB.init(c);
1251 for(edge e : PG.edges)
1252 m_edgesB[compnum[e]].pushBack(e);
1253
1254 // construct arrays compV and nodeB such that
1255 // m_compV[v] = list of components containing v
1256 // m_verticesB[i] = list of vertices in component i
1257 NodeArray<bool> mark(PG,false);
1258
1259 for(int i = 0; i < c; ++i) {
1260 for (edge e : m_edgesB[i]) {
1261 if (!mark[e->source()]) {
1262 mark[e->source()] = true;
1263 m_verticesB[i].pushBack(e->source());
1264 }
1265 if (!mark[e->target()]) {
1266 mark[e->target()] = true;
1267 m_verticesB[i].pushBack(e->target());
1268 }
1269 }
1270
1271 for (node v : m_verticesB[i]) {
1272 m_compV[v].pushBack(i);
1273 mark[v] = false;
1274 }
1275 }
1276 mark.init();
1277 m_GtoBC.init(PG,nullptr);
1278 m_copyInBlocks.init(PG);
1279
1280 m_block.init(c);
1281 for(int i = 0; i < c; ++i)
1282 m_block[i] = constructBlock(i);
1283
1284 m_sumInsertionCosts = 0;
1285 for(int i = 0; i < m; ++i) {
1286 computePathBC(i);
1287 m_sumInsertionCosts += m_insertionCosts[i];
1288
1289 #ifdef MEAI_OUTPUT
1290 std::cout << "(" << PG.copy((*m_edge)[i]->source()) << "," << PG.copy((*m_edge)[i]->target()) << ") c = " << m_insertionCosts[i] << ":\n";
1291 for (const auto &vblock : m_pathBCs[i]) {
1292 int b = vblock.m_block;
1293 const StaticSPQRTree &spqr = m_block[b]->spqr();
1294
1295 std::cout << " [ " << "V_" << vblock.m_vertex << ", B_" << b << " ]\n";
1296 std::cout << " ";
1297 if(m_block[b]->isBridge()) {
1298 std::cout << "BRIDGE";
1299 } else {
1300 node x = m_block[b]->m_pathSPQR[i].m_start;
1301 ListConstIterator<EmbeddingPreference> itP = m_block[b]->m_pathSPQR[i].m_prefs.begin();
1302 auto output = [&](node v) {
1303 SPQRTree::NodeType t = spqr.typeOf(v);
1304 std::cout << strType[static_cast<int>(t)] << "_" << v;
1305 if (t == SPQRTree::NodeType::RNode) {
1306 if ((*itP).type() == EmbeddingPreference::Type::None) {
1307 std::cout << " (NONE)";
1308 } else if ((*itP).mirror()) {
1309 std::cout << " (MIRROR)";
1310 } else {
1311 std::cout << " (KEEP)";
1312 }
1313 ++itP;
1314 } else if (t == SPQRTree::NodeType::PNode) {
1315 if ((*itP).type() == EmbeddingPreference::Type::None) {
1316 std::cout << " (NONE)";
1317 } else {
1318 std::cout << "(ADJ:" << (*itP).adj1()->index() << ";" << (*itP).adj2()->index() << ")";
1319 }
1320 ++itP;
1321 }
1322 };
1323 output(x);
1324
1325 for (edge e : m_block[b]->m_pathSPQR[i].m_edges) {
1326 node y = e->opposite(x);
1327 std::cout << " -> ";
1328 output(y);
1329 x = y;
1330 }
1331 }
1332 std::cout << std::endl;
1333 }
1334 #endif
1335 }
1336
1337 // embed blocks
1338 for(int i = 0; i < c; ++i)
1339 embedBlock(i,m);
1340
1341
1342 // find embedding preferences at cutvertices
1343 NodeArray<SList<CutvertexPreference> > cvPref(PG);
1344
1345 for(int i = 0; i < m; ++i)
1346 {
1347 const List<VertexBlock> &L = m_pathBCs[i];
1348 if(L.size() < 2)
1349 continue;
1350
1351 ListConstIterator<VertexBlock> it = L.begin();
1352 node last_v = (*it).m_vertex;
1353 int last_b = (*it).m_block;
1354 ++it;
1355 do {
1356 node v2 = (it.succ().valid()) ? (*it.succ()).m_vertex : PG.copy((*m_edge)[i]->target());
1357 cvPref[(*it).m_vertex].pushBack(CutvertexPreference(last_v,last_b,v2,(*it).m_block));
1358
1359 last_v = (*it).m_vertex;
1360 last_b = (*it).m_block;
1361 ++it;
1362 } while(it.valid());
1363 }
1364
1365
1366 // embedding graph
1367 Array<bool> blockSet(0,c-1,false);
1368 AdjEntryArray<SListIterator<adjEntry> > pos(PG,nullptr);
1369
1370 for(node v : PG.nodes)
1371 {
1372 SListPure<adjEntry> adjList;
1373 SList<VertexBlock> &copies = m_copyInBlocks[v];
1374
1375 // not a cut vertex?
1376 if(copies.size() == 1) {
1377 OGDF_ASSERT(cvPref[v].empty());
1378 const Block &B = *m_block[copies.front().m_block];
1379 node vB = copies.front().m_vertex;
1380
1381 for(adjEntry adj : vB->adjEntries)
1382 adjList.pushBack(B.m_BCtoG[adj]);
1383
1384 } else {
1385 SList<CutvertexPreference> &prefs = cvPref[v];
1386
1387 if(!prefs.empty()) {
1388 // always realize first cutvertex preference
1389 SListIterator<CutvertexPreference> it = prefs.begin();
1390
1391 int b1 = (*it).m_b1;
1392 int b2 = (*it).m_b2;
1393 blockSet[b1] = blockSet[b2] = true;
1394
1395 adjEntry adj1 = m_block[b1]->findBestFace(copy(v,b1),copy((*it).m_v1,b1), (*it).m_len1);
1396 appendToList(adjList, adj1, m_block[b1]->m_BCtoG, pos);
1397
1398 adjEntry adj2 = m_block[b2]->findBestFace(copy(v,b2),copy((*it).m_v2,b2), (*it).m_len2);
1399 appendToList(adjList, adj2, m_block[b2]->m_BCtoG, pos);
1400
1401 for(++it; it.valid(); ++it) {
1402 b1 = (*it).m_b1;
1403 b2 = (*it).m_b2;
1404
1405 if(!blockSet[b1] && !blockSet[b2]) {
1406 // none of the two blocks set yet
1407 adjEntry bestFace1 = m_block[b1]->findBestFace(copy(v,b1),copy((*it).m_v1,b1), (*it).m_len1);
1408 appendToList(adjList, bestFace1, m_block[b1]->m_BCtoG, pos);
1409
1410 adjEntry bestFace2 = m_block[b2]->findBestFace(copy(v,b2),copy((*it).m_v2,b2), (*it).m_len2);
1411 appendToList(adjList, bestFace2, m_block[b2]->m_BCtoG, pos);
1412
1413 blockSet[b1] = blockSet[b2] = true;
1414
1415 } else if(!blockSet[b1]) {
1416 // b2 is set, but b1 is not yet set
1417 adjEntry bestFace1 = m_block[b1]->findBestFace(copy(v,b1),copy((*it).m_v1,b1), (*it).m_len1);
1418 adjEntry bestFace2 = m_block[b2]->findBestFace(copy(v,b2),copy((*it).m_v2,b2), (*it).m_len2);
1419
1420 insertAfterList(adjList, pos[m_block[b2]->m_BCtoG[bestFace2]], bestFace1, m_block[b1]->m_BCtoG, pos);
1421 blockSet[b1] = true;
1422
1423 } else if(!blockSet[b2]) {
1424 // b1 is set, but b2 is not yet set
1425 adjEntry bestFace1 = m_block[b1]->findBestFace(copy(v,b1),copy((*it).m_v1,b1), (*it).m_len1);
1426 adjEntry bestFace2 = m_block[b2]->findBestFace(copy(v,b2),copy((*it).m_v2,b2), (*it).m_len2);
1427
1428 insertAfterList(adjList, pos[m_block[b1]->m_BCtoG[bestFace1]], bestFace2, m_block[b2]->m_BCtoG, pos);
1429 blockSet[b2] = true;
1430 }
1431 }
1432 }
1433
1434 // embed remaining blocks (if any)
1435 for (const auto &vblock : copies) {
1436 int b = vblock.m_block;
1437 if(blockSet[b])
1438 continue;
1439 appendToList(adjList, vblock.m_vertex->firstAdj(), m_block[b]->m_BCtoG, pos);
1440 }
1441
1442 // cleanup
1443 for (auto pref : prefs) {
1444 blockSet[pref.m_b1] = false;
1445 blockSet[pref.m_b2] = false;
1446 }
1447
1448 OGDF_ASSERT(adjList.size() == v->degree());
1449 }
1450
1451 PG.sort(v, adjList);
1452 }
1453
1454 OGDF_ASSERT(PG.representsCombEmbedding());
1455
1456 #if 0
1457 // arbitrary embedding for testing
1458 planarEmbed(PG);
1459 #endif
1460
1461 // generate further statistic information
1462 if(m_statistics) {
1463 constructDual(PG);
1464
1465 #if 0
1466 std::cout << "\ncutvertex preferences:\n" << std::endl;
1467 for(node v : PG.nodes)
1468 {
1469 SListConstIterator<CutvertexPreference> it = cvPref[v].begin();
1470 if(!it.valid()) continue;
1471
1472 std::cout << v << ":\n";
1473 for(; it.valid(); ++it) {
1474 const CutvertexPreference &p = *it;
1475
1476 int sp1 = findShortestPath(p.m_v1, v);
1477 int sp2 = findShortestPath(v, p.m_v2);
1478 std::cout << " ( v" << p.m_v1 << ", b" << p.m_b1 << "; v" << p.m_v2 << ", b" << p.m_b2 << " ) ";
1479 std::cout << "[ " << p.m_len1 << " / " << sp1 << " ; " << p.m_len2 << " / " << sp2 << " ]" << std::endl;
1480 }
1481 }
1482 #endif
1483
1484 m_sumFEInsertionCosts = 0;
1485 for(int i = 0; i < m; ++i) {
1486 node s = PG.copy((*m_edge)[i]->source());
1487 node t = PG.copy((*m_edge)[i]->target());
1488 int len = findShortestPath(s,t);
1489 m_sumFEInsertionCosts += len;
1490 }
1491 } else
1492 m_sumFEInsertionCosts = -1;
1493
1494
1495 // release no longer needed resources
1496 cleanup();
1497
1498
1499 //
1500 // PHASE 2: Perform edge insertion with fixed emebdding
1501 //
1502
1503 FixedEmbeddingInserter fei;
1504 fei.keepEmbedding(true);
1505 fei.removeReinsert(m_rrOptionFix);
1506 fei.percentMostCrossed(m_percentMostCrossedFix);
1507
1508 fei.call( PG, origEdges );
1509
1510 if(m_rrOptionVar != RemoveReinsertType::None && m_rrOptionVar != RemoveReinsertType::Incremental) {
1511 VariableEmbeddingInserter vei;
1512 vei.removeReinsert(m_rrOptionVar);
1513 vei.percentMostCrossed(m_percentMostCrossedFix);
1514 vei.callPostprocessing( PG, origEdges );
1515 }
1516
1517 return ReturnType::Feasible;
1518 }
1519
1520
1521 // cleanup: delete blocks, reset all auxiliary arrays
cleanup()1522 void MultiEdgeApproxInserter::cleanup()
1523 {
1524 int c = m_block.size();
1525 for(int i = 0; i < c; ++i)
1526 delete m_block[i];
1527 m_block.init();
1528
1529 m_GtoBC.init();
1530 m_edgesB.init();
1531 m_verticesB.init();
1532 m_compV.init();
1533
1534 m_edge = nullptr;
1535 m_pathBCs.init();
1536 m_insertionCosts.init();
1537 m_copyInBlocks.init();
1538
1539 m_primalAdj.init();
1540 m_faceNode.init();
1541 m_E.init();
1542 m_dual.clear();
1543 }
1544
1545
1546 // just for testing and additional statistics
constructDual(const PlanRepLight & PG)1547 void MultiEdgeApproxInserter::constructDual(const PlanRepLight &PG)
1548 {
1549 m_E.init(PG);
1550 m_faceNode.init(m_E);
1551 m_primalAdj.init(m_dual);
1552
1553 // constructs nodes (for faces in m_embB)
1554 for(face f : m_E.faces) {
1555 m_faceNode[f] = m_dual.newNode();
1556 }
1557
1558 // construct dual edges (for primal edges in block)
1559 for(node v : PG.nodes)
1560 {
1561 for(adjEntry adj : v->adjEntries)
1562 {
1563 if(adj->index() & 1) {
1564 node vLeft = m_faceNode[m_E.leftFace (adj)];
1565 node vRight = m_faceNode[m_E.rightFace(adj)];
1566
1567 edge eDual = m_dual.newEdge(vLeft,vRight);
1568 m_primalAdj[eDual->adjSource()] = adj;
1569 m_primalAdj[eDual->adjTarget()] = adj->twin();
1570 }
1571 }
1572 }
1573
1574 m_vS = m_dual.newNode();
1575 m_vT = m_dual.newNode();
1576 }
1577
1578
findShortestPath(node s,node t)1579 int MultiEdgeApproxInserter::findShortestPath(node s, node t)
1580 {
1581 NodeArray<adjEntry> spPred(m_dual, nullptr);
1582 QueuePure<adjEntry> queue;
1583 int oldIdCount = m_dual.maxEdgeIndex();
1584
1585 // augment dual by edges from s to all adjacent faces of s ...
1586 for(adjEntry adj : s->adjEntries) {
1587 // starting edges of bfs-search are all edges leaving s
1588 edge eDual = m_dual.newEdge(m_vS, m_faceNode[m_E.rightFace(adj)]);
1589 m_primalAdj[eDual->adjSource()] = adj;
1590 m_primalAdj[eDual->adjTarget()] = nullptr;
1591 queue.append(eDual->adjSource());
1592 }
1593
1594 // ... and from all adjacent faces of t to t
1595 for(adjEntry adj : t->adjEntries) {
1596 edge eDual = m_dual.newEdge(m_faceNode[m_E.rightFace(adj)], m_vT);
1597 m_primalAdj[eDual->adjSource()] = adj;
1598 m_primalAdj[eDual->adjTarget()] = nullptr;
1599 }
1600
1601 // actual search (using bfs on directed dual)
1602 int len = -2;
1603 for( ; ;)
1604 {
1605 // next candidate edge
1606 adjEntry adjCand = queue.pop();
1607 node v = adjCand->twinNode();
1608
1609 // leads to an unvisited node?
1610 if (spPred[v] == nullptr)
1611 {
1612 // yes, then we set v's predecessor in search tree
1613 spPred[v] = adjCand;
1614
1615 // have we reached t ...
1616 if (v == m_vT)
1617 {
1618 // ... then search is done.
1619
1620 adjEntry adj;
1621 do {
1622 adj = spPred[v];
1623 ++len;
1624 v = adj->theNode();
1625 } while(v != m_vS);
1626
1627 break;
1628 }
1629
1630 // append next candidate edges to queue
1631 // (all edges leaving v)
1632 for(adjEntry adj : v->adjEntries) {
1633 if(adj->twinNode() != m_vS)
1634 queue.append(adj);
1635 }
1636 }
1637 }
1638
1639 // remove augmented edges again
1640 adjEntry adj;
1641 while ((adj = m_vS->firstAdj()) != nullptr)
1642 m_dual.delEdge(adj->theEdge());
1643
1644 while ((adj = m_vT->firstAdj()) != nullptr)
1645 m_dual.delEdge(adj->theEdge());
1646
1647 m_dual.resetEdgeIdCount(oldIdCount);
1648
1649 return len;
1650 }
1651
1652 }
1653