1 /** \file
2 * \brief Implements the class BitonicOrdering...
3 *
4 * ... that computes a bitonic st ordering as described by Gronemann
5 * in Bitonic st-orderings of biconnected planar graphs
6 *
7 * \author Martin Gronemann
8 *
9 * \par License:
10 * This file is part of the Open Graph Drawing Framework (OGDF).
11 *
12 * \par
13 * Copyright (C)<br>
14 * See README.md in the OGDF root directory for details.
15 *
16 * \par
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License
19 * Version 2 or 3 as published by the Free Software Foundation;
20 * see the file LICENSE.txt included in the packaging of this file
21 * for details.
22 *
23 * \par
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * \par
30 * You should have received a copy of the GNU General Public
31 * License along with this program; if not, see
32 * http://www.gnu.org/copyleft/gpl.html
33 */
34
35 #include <ogdf/planarlayout/BitonicOrdering.h>
36
37 using namespace ogdf;
38
BitonicOrdering(Graph & G,adjEntry adj_st_edge)39 BitonicOrdering::BitonicOrdering(Graph& G, adjEntry adj_st_edge)
40 : m_graph(G)
41 , m_currLabel(0)
42 , m_orderIndex(G,-1)
43 , m_indexToNode(G.numberOfNodes())
44 , m_tree(G, adj_st_edge->theEdge(), true)
45 {
46 // set all tree nodes to non flipped
47 m_flipped.init(m_tree.tree(), false);
48
49 // s in the graph
50 node s_G = adj_st_edge->theNode();
51 node t_G = adj_st_edge->twinNode();
52
53 // we label s here manually: set the label
54 m_orderIndex[s_G] = m_currLabel++;
55 // and update the other map
56 m_indexToNode[m_orderIndex[s_G]] = s_G;
57
58 // label everything else except t
59 handleCase(m_tree.rootNode());
60
61 // we label s here manually: set the label
62 m_orderIndex[t_G] = m_currLabel++;
63 // and update the other map
64 m_indexToNode[m_orderIndex[t_G]] = t_G;
65
66 // finally embedd G
67 m_tree.embed(m_graph);
68 }
69
70
71 // used to distinguish between the three cases below
handleCase(node v_T)72 void BitonicOrdering::handleCase(node v_T)
73 {
74 // check if this skeleton has been flipped in some R-node
75 // above temporarily
76 if (isFlipped(v_T)) {
77 // reverse embedding of the skeleton
78 m_tree.reverse(v_T);
79 }
80
81 // the switch statement to check what type of node
82 // v_T is
83 switch (m_tree.typeOf(v_T)) {
84 case ogdf::SPQRTree::NodeType::SNode:
85 handleSerialCase(v_T);
86 break;
87
88 case ogdf::SPQRTree::NodeType::PNode:
89 handleParallelCase(v_T);
90 break;
91
92 case ogdf::SPQRTree::NodeType::RNode:
93 handleRigidCase(v_T);
94 break;
95
96 default:
97 break;
98 }
99
100 // if we flipped it before, we now reverse the reversing
101 if (isFlipped(v_T)) {
102 m_tree.reverse(v_T);
103 }
104
105 }
106
107 // helper function that finds the st-adjEntry in the skeleton of v_T
getAdjST(node v_T) const108 adjEntry BitonicOrdering::getAdjST(node v_T) const
109 {
110 // the reference edge in G_skel
111 edge e_ref = m_tree.skeleton(v_T).referenceEdge();
112
113 // it is either e_ref's source or target
114 // we try this one first
115 adjEntry adj_st = e_ref->adjSource();
116
117 // by our invariant s is labeled but t not
118 // hence check if source is labeled
119 if (getLabel(v_T, adj_st->theNode()) < 0) {
120 // it is not, then it is the other one.
121 adj_st = adj_st->twin();
122 }
123
124 // this is the element pointing from s to t
125 return adj_st;
126 }
127
128 // the S-node case
handleSerialCase(node v_T)129 void BitonicOrdering::handleSerialCase(node v_T)
130 {
131 // the skeleton instance of the tree node
132 const Skeleton& skel = m_tree.skeleton(v_T);
133
134 // the adjElement pointing from s to t
135 adjEntry adj_st = getAdjST(v_T);
136
137 // the t of st
138 node t = adj_st->twinNode();
139
140 // now we traverse the cycle of skel counter clockwise from s to t
141 for (adjEntry adj = adj_st->cyclicSucc();
142 adj != adj_st->twin();
143 adj = adj->twin()->cyclicSucc()) {
144 // the current edge of the cycle
145 edge e = adj->theEdge();
146
147 // check if this is a virtual one, i.e. is there something we
148 // have to label first?
149 if (skel.isVirtual(e)) {
150 // the corresponding child of v_T
151 node w_T = skel.twinTreeNode(e);
152
153 // mark the child as flipped iff this node is flipped
154 setFlipped(w_T, isFlipped(v_T));
155
156 // go and label those nodes first
157 handleCase(w_T);
158 }
159
160 // the node which can be s
161 node v = adj->twinNode();
162
163 // assign v a label unless it is t.
164 if (v != t) assignLabel(v_T, v);
165 }
166 }
167
168 // the P-node case
handleParallelCase(node v_T)169 void BitonicOrdering::handleParallelCase(node v_T)
170 {
171 // the skeleton instance of the tree node
172 const Skeleton& skel = m_tree.skeleton(v_T);
173
174 // the adjElement pointing from s to t
175 adjEntry adj_st = getAdjST(v_T);
176
177 // a possible real st edge
178 adjEntry adj_real = nullptr;
179
180 // first we check if there is any real edge here
181 for (adjEntry adj = adj_st->cyclicPred(); adj != adj_st; adj = adj->cyclicPred()) {
182 // check if it is a real edge
183 if (!skel.isVirtual(adj->theEdge())
184 && adj != adj_st->cyclicSucc()) {
185 // we are only interested in a real edge that is not the cyclic succ of the reference adj
186 adj_real = adj; // we found one, this is moved
187 }
188 }
189
190 // we have to change the embedding now
191 if (adj_real) {
192 // swap it with the one right after the st edge
193 m_tree.swap(v_T, adj_st->cyclicSucc(), adj_real);
194 }
195
196 // for all incident edges in reverse order
197 for (adjEntry adj = adj_st->cyclicPred(); adj != adj_st; adj = adj->cyclicPred()) {
198 // the edge
199 edge e = adj->theEdge();
200
201 // check if this is a virtual one, i.e. is there something we
202 // have to label first?
203 if (skel.isVirtual(e)) {
204 // the corresponding child of v_T
205 node w_T = skel.twinTreeNode(e);
206
207 // mark the child as flipped iff this node is flipped
208 setFlipped(w_T, isFlipped(v_T));
209
210 // go and label those nodes first
211 handleCase(w_T);
212 }
213 }
214 }
215
216 // transforms the result of the canonical ordering into two arrays,
217 // one holding the index in the temporary order for a node,
218 // the other is the reverse map. Function assumes proper init for indices and vertices
partitionToOrderIndices(const List<List<node>> & partitionlist,NodeArray<int> & indices,Array<node> & vertices) const219 void BitonicOrdering::partitionToOrderIndices(const List<List<node> >& partitionlist,
220 NodeArray<int>& indices,
221 Array<node>& vertices) const
222 {
223 // counter for the index of a node
224 int currIndex = 0;
225
226 // for all parititons
227 for (List<List<node> >::const_iterator lit = partitionlist.begin(); lit.valid(); ++lit) {
228 // a chain or singleton
229 const List<node>& partition = *lit;
230
231 // for all nodes in the chain or singleton
232 for (List<node>::const_iterator it = partition.begin(); it.valid(); ++it) {
233 // the node
234 node v = *it;
235
236 // assign it a number
237 indices[v] = currIndex;
238
239 // mark it in the map
240 vertices[currIndex] = v;
241
242 // increment the index
243 currIndex++;
244 }
245 }
246 }
247
248 // the R-node case
handleRigidCase(node v_T)249 void BitonicOrdering::handleRigidCase(node v_T)
250 {
251 // the skeleton instance of the tree node
252 const Skeleton& skel = m_tree.skeleton(v_T);
253
254 // the adjElement pointing from s to t
255 adjEntry adj_st = getAdjST(v_T);
256
257 // s and t
258 node s = adj_st->theNode();
259 node t = adj_st->twinNode();
260
261 // the skeleton graph of v_T
262 const Graph& G_skel = skel.getGraph();
263
264 // canonical ordeirng
265 LeftistOrdering leftistOrder;
266
267 // the result of the leftist order
268 List<List<node> > temporaryPartition;
269
270 // get the order
271 leftistOrder.call(G_skel, adj_st, temporaryPartition);
272
273 // index for every skeleton node
274 NodeArray<int> vertexIndex(G_skel, -1);
275
276 // the temporary order
277 Array<node> order(G_skel.numberOfNodes());
278
279 // partition to order indices
280 partitionToOrderIndices(temporaryPartition, vertexIndex, order);
281
282 // now traverse the order from 1 to V
283 for (int i = 0; i < G_skel.numberOfNodes(); ++i) {
284 // the ith node
285 node w = order[i];
286
287 // for all incident edges
288 for (adjEntry adj = w->firstAdj(); adj; adj = adj->succ()) {
289 // the neighbour
290 node v = adj->twinNode();
291
292 // check if this is an incoming edge in the temporary order
293 if (vertexIndex[v] < vertexIndex[w] ) {
294 // yes it is, w is the successor of v
295 // time for some recursion
296 edge e = adj->theEdge();
297
298 // check if this is a virtual one, i.e. is there something we
299 // have to label first? however, make sure not to recurse on the parent
300 if (skel.isVirtual(e) && ( e != skel.referenceEdge())) {
301 // the corresponding child of v_T
302 node w_T = skel.twinTreeNode(e);
303
304 // the successor of w in the embedding clockwise at v
305 node w_next = adj->twin()->cyclicSucc()->twinNode();
306
307 // we now check if the successor of w at v has a higher index then w
308 // i.e. w is in the increasing partition at v and requires a flip in the embeddig
309 // however, we have to be careful at s to not wrap around (the st edge is the cyclicSucc
310 // of the v_1,v_2 edge. we just skip v = s = v_1 completly since it is
311 // decreasing by corallary 1
312 if ((vertexIndex[v] > 0) && (vertexIndex[w] < vertexIndex[w_next])) {
313 // w is in the increasing partition
314 // we mark the child as flipped if we are not flipped
315 setFlipped(w_T, !isFlipped(v_T));
316 // reverseRecursive(skel.twinTreeNode(e));
317 } else {
318 // mark the child as flipped iff this node is flipped
319 setFlipped(w_T, isFlipped(v_T));
320 }
321
322 // go and label those nodes first
323 handleCase(w_T);
324 }
325 }
326 }
327
328 //now we are ready to label v
329 if ((w != t) && (w != s)) assignLabel(v_T, w);
330 }
331 }
332
333 #ifdef OGDF_DEBUG
consistencyCheck(GraphAttributes & GA) const334 void BitonicOrdering::consistencyCheck(GraphAttributes& GA) const
335 {
336 GA.init(m_graph, GraphAttributes::nodeLabel);
337 bool allBitonic = true;
338
339 for (node v : m_graph.nodes) {
340 std::stringstream str;
341 str << v << "(" << m_orderIndex[v] << ")";
342 GA.label(v) = str.str();
343
344 if (m_orderIndex[v] < 0) {
345 Logger::slout() << "Node not assigned" << std::endl;
346 }
347 }
348
349 // for all nodes we check now for bitonic indices
350 for (node v : m_graph.nodes) {
351 bool isNodeBitonic = true;
352 // we skip t and s though
353 if (m_orderIndex[v] != 0 && m_orderIndex[v] != m_graph.numberOfNodes() - 1) {
354 adjEntry adj_first_succ = nullptr;
355 adjEntry adj_last_succ = nullptr;
356
357
358 for (adjEntry adj = v->firstAdj(); adj; adj = adj->succ()) {
359 // and its cyclic succ
360 node w_prev = adj->cyclicPred()->twinNode();
361 // the other node
362 node w = adj->twinNode();
363 // and its cyclic succ
364 node w_next = adj->cyclicSucc()->twinNode();
365
366 if ((m_orderIndex[v] > m_orderIndex[w_prev]) && (m_orderIndex[v] < m_orderIndex[w]))
367 adj_first_succ = adj;
368
369 if ((m_orderIndex[v] > m_orderIndex[w_next]) && (m_orderIndex[v] < m_orderIndex[w]))
370 adj_last_succ = adj;
371
372 }
373
374
375 // we are going to look for bitonic succ lists
376 for (adjEntry adj = v->firstAdj(); adj; adj = adj->succ()) {
377 // and its cyclic succ
378 node w_prev = adj->cyclicPred()->twinNode();
379 // the other node
380 node w = adj->twinNode();
381 // and its cyclic succ
382 node w_next = adj->cyclicSucc()->twinNode();
383
384 if (m_orderIndex[v] <= max(m_orderIndex[w_prev], max(m_orderIndex[w], m_orderIndex[w_next]))) {
385 // all succs, lets check for bitonic indices
386
387 if ((m_orderIndex[w_prev] >= m_orderIndex[w]) && (m_orderIndex[w_next] >= m_orderIndex[w]) &&
388 (m_orderIndex[v] > 0)) {
389 isNodeBitonic = false;
390 Logger::slout()
391 << "[BitonicOrder:] " << "NOT BITONIC SUCC LIST " << v << "(" << m_orderIndex[v] << ")"
392 << std::endl
393 << "[BitonicOrder:] " << w_prev << "(" << m_orderIndex[w_prev] << ") "
394 << w << "(" << m_orderIndex[w] << ") "
395 << w_next << "(" << m_orderIndex[w_next] << ")" << std::endl
396 << std::endl;
397 };
398 }
399 }
400
401 if (!isNodeBitonic) {
402 for (adjEntry adj = adj_first_succ; adj != adj_last_succ->cyclicSucc(); adj = adj->cyclicSucc()) {
403 Logger::slout() << "(" << m_orderIndex[adj->twinNode()] << ") ";
404 }
405 }
406
407 OGDF_ASSERT(allBitonic && isNodeBitonic);
408 }
409 }
410 }
411 #endif
412