Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

AlternatingTree.h
Go to the documentation of this file.
1 
32 #pragma once
33 
34 #include <ogdf/basic/Graph.h>
35 #include <ogdf/basic/List.h>
40 
41 #include <tuple>
42 #include <unordered_map>
43 #include <unordered_set>
44 #include <vector>
45 
46 namespace ogdf {
47 namespace matching_blossom {
48 
49 template<class TWeight>
52  using IteratorCallback = std::function<void(edge, bool)>;
53 
56 
59 
61  std::unordered_map<node, edge> m_evenNodes;
62 
64  std::unordered_map<node, edge> m_oddNodes;
65 
68 
71  std::unordered_set<node> potentialIntersections = {e->source(), e->target()};
72  std::vector<node> nodes = {e->source(), e->target()};
73  while (true) {
74  // iterate both nodes in the vector by reference and update them to the next even node
75  for (node& n : nodes) {
76  if (n != m_root) {
77  n = getNextEvenNode(n);
78  if (potentialIntersections.find(n) != potentialIntersections.end()) {
79  return n;
80  }
81  potentialIntersections.insert(n);
82  }
83  }
84  }
85  }
86 
89  node getNextEvenNode(node v, IteratorCallback callback = nullptr) {
90  OGDF_ASSERT(isEven(v));
91  if (v == m_root) {
92  return nullptr;
93  }
94  edge e = evenBackEdge(v);
95  if (callback) {
96  callback(e, true);
97  }
98  v = e->opposite(v);
99  e = oddBackEdge(v);
100  if (callback) {
101  callback(e, false);
102  }
103  return e->opposite(v);
104  }
105 
108  void iterateTree(node start, node end, IteratorCallback callback) {
109  OGDF_ASSERT(isEven(start) && isEven(end));
110  while (start != end) {
111  start = getNextEvenNode(start, callback);
112  }
113  }
114 
116  void moveEdge(edge e, node oldNode, node newNode) {
117  if (oldNode == e->source()) {
118  m_helper.graph().moveSource(e, newNode);
119  } else {
120  m_helper.graph().moveTarget(e, newNode);
121  }
122  }
123 
124 public:
126 
128 
131  reset(root);
132  }
133 
134  node root() { return m_root; }
135 
136  bool hasRoot() { return m_root != nullptr; }
137 
139 
140  bool isEven(node v) { return m_evenNodes.find(v) != m_evenNodes.end(); }
141 
143 
144  bool isOdd(node v) { return m_oddNodes.find(v) != m_oddNodes.end(); }
145 
146  bool contains(node v) { return isEven(v) || isOdd(v); }
147 
151  if (contains(e->source())) {
152  return e->source();
153  } else {
154  OGDF_ASSERT(contains(e->target()));
155  return e->target();
156  }
157  }
158 
160  void reset(node root = nullptr) {
161  m_root = root;
162  m_evenNodes.clear();
163  m_oddNodes.clear();
164  if (m_root != nullptr) {
165  OGDF_ASSERT(m_root->graphOf() == &m_helper.graph());
166  m_evenNodes[m_root] = nullptr;
167  }
168  }
169 
177  void grow(node u, edge e, edge f) {
178  node v = e->opposite(u);
179  m_oddNodes[v] = e;
180  node w = f->opposite(v);
181  m_evenNodes[w] = f;
182  }
183 
185  Cycle* getCycle(edge cycleEdge) {
186  // Follow the path in the direction of the root node given by the tree for both
187  // end nodes of the found edge until an intersection is found to form the cycle.
188  Cycle* cycle = new Cycle(cycleEdge);
189  node startNode = findCycleStartNode(cycleEdge);
190  iterateTree(cycleEdge->source(), startNode, [&](edge e, bool isEven) { cycle->addEdge(e); });
191  std::vector<edge> stack;
192  iterateTree(cycleEdge->target(), startNode, [&](edge e, bool isEven) { stack.push_back(e); });
193  for (auto it = stack.rbegin(); it != stack.rend(); ++it) {
194  cycle->addEdge(*it);
195  }
196  return cycle;
197  }
198 
201  void augmentMatching(edge startingEdge) {
202  m_helper.addToMatching(startingEdge);
203  iterateTree(commonNode(startingEdge), m_root, [&](edge e, bool isEven) {
204  if (!isEven) {
205  m_helper.addToMatching(e);
206  }
207  });
208  }
209 
211  Pseudonode* shrink(Cycle* cycle, std::vector<std::tuple<edge, bool>>& _selfLoops) {
212  node newNode = m_helper.graph().newNode();
213  Pseudonode* pseudonode = new Pseudonode(newNode, cycle);
214  std::unordered_map<node, edge> edgeMap; // map non-cycle nodes to their best edge
215  std::unordered_map<node, std::vector<edge>> selfLoops; // map cycle nodes to all edges that will become self loops
216  node matchingNode = nullptr, backNode = nullptr;
217  for (node u : cycle->nodes()) {
218  if (!matchingNode) {
219  edge matchingEdge = m_helper.matching(u);
220  if (matchingEdge != nullptr && !cycle->contains(matchingEdge->opposite(u))) {
221  matchingNode = matchingEdge->opposite(u);
222  }
223  }
224  if (!backNode) {
225  edge backEdge = evenBackEdge(u);
226  if (backEdge != nullptr && !cycle->contains(backEdge->opposite(u))) {
227  backNode = backEdge->opposite(u);
228  }
229  }
230  m_helper.matching(u) = nullptr;
231  // since the adjacency list is modified, we have to copy it
232  List<edge> edges;
233  u->adjEdges(edges);
234  for (edge e : edges) {
235  node v = e->opposite(u);
236  if (!cycle->contains(v)) {
237  auto it = edgeMap.find(v);
238  bool firstEdge = it == edgeMap.end();
239  bool betterEdge = firstEdge
240  || m_eps.less(m_helper.getRealReducedWeight(e),
241  m_helper.getRealReducedWeight(it->second));
242  if (!firstEdge) {
243  edge selfLoop = betterEdge ? it->second : e;
244  node loopNode = selfLoop->opposite(v);
245  selfLoops[loopNode].push_back(selfLoop);
246  }
247  if (betterEdge) {
248  edgeMap[v] = e;
249  }
250  }
251  }
252  if (u == m_root) {
253  m_root = newNode;
254  }
255  }
256  // move self loops
257  std::unordered_map<node, edge> selfLoopMap;
258  for (node u : cycle->nodes()) {
259  bool even = isEven(u);
260  for (edge e : selfLoops[u]) {
261  node v = e->opposite(u);
262  edge bestEdge = edgeMap[v];
263  node w = bestEdge->opposite(v);
264  // set edge weight to difference between reduced weights of this edge to used edge
265  // in edge map
266  // this means the reduced weight of the edge becomes invalid, but self loops are
267  // simply ignored in all calculations
268  m_helper.c(e) -= m_helper.c(bestEdge) + m_helper.y(u) - m_helper.y(w);
269  pseudonode->addReference(bestEdge, e, m_helper.pseudonode(v));
270  moveEdge(e, v, u);
271  _selfLoops.push_back(std::make_tuple(e, even));
272  if (evenBackEdge(v) == e) {
273  m_evenNodes[v] = bestEdge;
274  } else if (oddBackEdge(v) == e) {
275  m_oddNodes[v] = bestEdge;
276  }
277  }
278  if (even) {
279  m_evenNodes.erase(u);
280  } else {
281  m_oddNodes.erase(u);
282  }
283  }
284 
285  for (auto entry : edgeMap) {
286  node v = entry.first;
287  edge e = entry.second;
288  node u = e->opposite(v);
289  // edge between a cycle node and a non-cycle node: bend it to new node
290  moveEdge(e, u, newNode);
291  m_helper.c(e) -= m_helper.y(u);
292  }
293  m_evenNodes[newNode] = backNode ? edgeMap[backNode] : nullptr;
294  if (matchingNode) {
295  m_helper.addToMatching(edgeMap[matchingNode]);
296  }
297  m_helper.addPseudonode(pseudonode);
298  return pseudonode;
299  }
300 
302  void expand(Pseudonode* pseudonode) {
303  node graphNode = pseudonode->graphNode;
304  auto cycle = pseudonode->cycle;
305 
306  // move edges back to old nodes
307  List<edge> refEdges;
308  graphNode->adjEdges(refEdges);
309  for (edge e : refEdges) {
310  node uOrig = m_helper.getBaseNode(e, graphNode);
311  node u = m_helper.reprChild(uOrig);
312  OGDF_ASSERT(m_helper.repr(u) == graphNode);
313  m_helper.c(e) += m_helper.y(u);
314  moveEdge(e, graphNode, u);
315  }
316  // move back self loops
317  while (!refEdges.empty()) {
318  edge refEdge = refEdges.popFrontRet();
319  for (edge e : pseudonode->referenceEdges.selfLoops(refEdge)) {
320  refEdges.pushBack(e);
321  node source, target;
322  std::tie(source, target) = m_helper.getBaseNodes(e);
323  node currentSource = m_helper.repr(source);
324  node u, v;
325  if (currentSource == graphNode) {
326  u = m_helper.reprChild(source);
327  v = m_helper.repr(target);
328  m_helper.graph().moveSource(e, u);
329  m_helper.graph().moveTarget(e, v);
330  } else {
331  u = m_helper.reprChild(target);
332  v = currentSource;
333  m_helper.graph().moveSource(e, v);
334  m_helper.graph().moveTarget(e, u);
335  }
336  OGDF_ASSERT(m_helper.repr(u) == graphNode);
337  node w = refEdge->opposite(v);
338  m_helper.c(e) += m_helper.c(refEdge) + m_helper.y(u) - m_helper.y(w);
339  pseudonode->referenceEdges.removeFromOther(e);
340  }
341  }
342 #ifdef OGDF_DEBUG
343  // Check that all self loops have been moved
344  for (node u : cycle->nodes()) {
345  List<edge> inEdges;
346  u->inEdges(inEdges);
347  for (edge e : inEdges) {
348  OGDF_ASSERT(!e->isSelfLoop());
349  }
350  }
351 #endif
352 
353  // find the matching edges which go in and out of the pseudonode in the current tree and
354  // mark the respective nodes in the cycle as start and end node
355  // the tree enters the pseudonode/cycle at startCycleNode with a non-matching edge since
356  // the pseudonode was odd and leaves it at endCycleNode with a matching edge
357  node startCycleNode = cycle->startNode();
358  auto it = m_oddNodes.find(graphNode);
359  if (it != m_oddNodes.end()) {
360  edge e = it->second;
361  node origStart = m_helper.getBaseNode(e, graphNode);
362  startCycleNode = m_helper.reprChild(origStart);
363  OGDF_ASSERT(cycle->contains(startCycleNode));
364  m_oddNodes[startCycleNode] = e;
365  }
366  edge matchingEdge = m_helper.matching(graphNode);
367  node origEnd = m_helper.getBaseNode(matchingEdge, graphNode);
368  node endCycleNode = m_helper.reprChild(origEnd);
369  OGDF_ASSERT(cycle->contains(endCycleNode));
370  m_helper.addToMatching(matchingEdge);
371 
372  size_t startNodeEdgeIndex, endNodeEdgeIndex;
373  std::tie(startNodeEdgeIndex, endNodeEdgeIndex) = cycle->indexOf(startCycleNode, endCycleNode);
374 
375  // We start at the endCycleNode and iterate the even-length path to the startCycleNode and
376  // then back via the odd-length path and add every second edge to the matching.
377  // Simultanously, we add the nodes and edges of the even length path to the tree.
378  // the direction of the iteration depends the direction of the cycle and the order of the
379  // start/end nodes therein.
380  auto edges = cycle->edgeOrder();
381  bool moveForward = (endNodeEdgeIndex - startNodeEdgeIndex) % 2
382  == (startNodeEdgeIndex < endNodeEdgeIndex);
383  node currentNode = endCycleNode;
384  for (size_t i = 0; i < edges.size() - 1; ++i) {
385  int index = endNodeEdgeIndex;
386  if (moveForward) {
387  index += i + 1;
388  } else {
389  index += edges.size() - i;
390  }
391  edge e = edges[index % edges.size()];
392  if (i % 2 == 1) {
393  m_helper.addToMatching(e);
394  }
395  if (currentNode != startCycleNode) {
396  if (i % 2 == 0) {
397  m_oddNodes[currentNode] = e;
398  } else {
399  m_evenNodes[currentNode] = e;
400  }
401  currentNode = e->opposite(currentNode);
402  }
403  }
404 
405  // remove pseudonode from all data structures
406  m_helper.removePseudonode(pseudonode);
407  m_oddNodes.erase(graphNode);
408  }
409 };
410 
411 }
412 }
ogdf
The namespace for all OGDF objects.
Definition: AugmentationModule.h:36
ogdf::matching_blossom::BaseIteratorContainer
Dummy class for scoped iteration of a std::unordered_map.
Definition: utils.h:109
ogdf::matching_blossom::AlternatingTree
Definition: AlternatingTree.h:50
ogdf::matching_blossom::Pseudonode
Helper class representing a pseudonode in the Blossom algorithm.
Definition: Pseudonode.h:45
ogdf::matching_blossom::AlternatingTree::oddBackEdge
edge oddBackEdge(node v)
Definition: AlternatingTree.h:142
ogdf::matching_blossom::AlternatingTree::augmentMatching
void augmentMatching(edge startingEdge)
Augment the matching along the path from startingEdge to the root. startingEdge must be incident to a...
Definition: AlternatingTree.h:201
Graph.h
Includes declaration of graph class.
ogdf::matching_blossom::Pseudonode::referenceEdges
ReferenceEdges referenceEdges
The ReferenceEdges for self loops of this pseudonode.
Definition: Pseudonode.h:96
ogdf::matching_blossom::Cycle::addEdge
void addEdge(edge e)
Use this method to add edges in cycle order.
Pseudonode.h
Helper structure representing a pseudonode in the Blossom algorithm.
ogdf::EpsilonTest
Definition: EpsilonTest.h:41
ogdf::matching_blossom::AlternatingTree::getCycle
Cycle * getCycle(edge cycleEdge)
Return the odd-length cycle which is closed in this tree with cycleEdge.
Definition: AlternatingTree.h:185
ogdf::matching_blossom::AlternatingTree::m_helper
BlossomHelper< TWeight > & m_helper
Reference to the helper class.
Definition: AlternatingTree.h:55
OGDF_ASSERT
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition: basic.h:54
ogdf::matching_blossom::AlternatingTree::m_evenNodes
std::unordered_map< node, edge > m_evenNodes
All even nodes, mapped to their respective back edge in the tree (or nullptr for the root).
Definition: AlternatingTree.h:61
ogdf::matching_blossom::AlternatingTree::reset
void reset(node root=nullptr)
Reset the tree with new the new root. If root is nullptr, the tree will be invalid.
Definition: AlternatingTree.h:160
ogdf::matching_blossom::AlternatingTree::hasRoot
bool hasRoot()
Definition: AlternatingTree.h:136
ogdf::matching_blossom::Cycle
Definition: Cycle.h:43
ogdf::matching_blossom::AlternatingTree::m_eps
EpsilonTest m_eps
Epsilon test for floating point numbers.
Definition: AlternatingTree.h:67
ogdf::matching_blossom::AlternatingTree::IteratorCallback
std::function< void(edge, bool)> IteratorCallback
Type of callback function when iterating the tree.
Definition: AlternatingTree.h:52
ogdf::matching_blossom::BlossomHelper
Helper class for the blossom matching algorithms.
Definition: BlossomHelper.h:55
ogdf::matching_blossom::Cycle::nodes
const std::unordered_set< node > & nodes()
ogdf::matching_blossom::AlternatingTree::commonNode
node commonNode(edge e)
Finds the common node between e and the tree. If both nodes are in the tree, only the source node is ...
Definition: AlternatingTree.h:150
BlossomHelper.h
Utility class for the Blossom algorithm, providiing access to all important data structures.
ogdf::matching_blossom::AlternatingTree::contains
bool contains(node v)
Definition: AlternatingTree.h:146
ogdf::EpsilonTest::less
std::enable_if< std::is_integral< T >::value, bool >::type less(const T &x, const T &y) const
Compare if x is LESS than y for integral types.
Definition: EpsilonTest.h:57
ogdf::matching_blossom::AlternatingTree::evenBackEdge
edge evenBackEdge(node v)
Definition: AlternatingTree.h:138
Cycle.h
Utility class representing a cycle in the Blossom algorithm.
ogdf::matching_blossom::AlternatingTree::m_root
node m_root
The root of the tree. May be empty to signalize an invalid tree which needs to be rebuild.
Definition: AlternatingTree.h:58
ogdf::matching_blossom::Pseudonode::addReference
void addReference(edge ref, edge selfLoop, Pseudonode *other)
Add a self loop selfLoop which was removed because of the reference edge ref and pointed previously t...
ogdf::matching_blossom::AlternatingTree::grow
void grow(node u, edge e, edge f)
Grow the tree by adding e and f.
Definition: AlternatingTree.h:177
ogdf::matching_blossom::AlternatingTree::evenNodes
KeyIteratorContainer< node, edge > evenNodes
Definition: AlternatingTree.h:125
ogdf::matching_blossom::Pseudonode::cycle
Cycle * cycle
The odd-length cycle that this pseudonode represents.
Definition: Pseudonode.h:93
ogdf::matching_blossom::AlternatingTree::isOdd
bool isOdd(node v)
Definition: AlternatingTree.h:144
ogdf::matching_blossom::Pseudonode::ReferenceEdges::selfLoops
std::unordered_set< edge > & selfLoops(edge ref)
Definition: Pseudonode.h:63
ogdf::matching_blossom::AlternatingTree::oddNodes
KeyIteratorContainer< node, edge > oddNodes
Definition: AlternatingTree.h:127
ogdf::EdgeElement::source
node source() const
Returns the source node of the edge.
Definition: Graph_d.h:391
ogdf::List< edge >
ogdf::matching_blossom::tryGetPointerFromMap
V * tryGetPointerFromMap(const std::unordered_map< K, V * > &map, const K &key)
Return the pointer belonging to key key int the given map map, or nullptr if key does not exist.
Definition: utils.h:51
ogdf::matching_blossom::Pseudonode::graphNode
node graphNode
The node in the graph that this pseudonode is linked to.
Definition: Pseudonode.h:90
ogdf::matching_blossom::AlternatingTree::AlternatingTree
AlternatingTree(BlossomHelper< TWeight > &helper, node root=nullptr)
Definition: AlternatingTree.h:129
ogdf::NodeElement::adjEdges
void adjEdges(EDGELIST &edgeList) const
Returns a list with all edges incident to this node.
Definition: Graph_d.h:312
ogdf::matching_blossom::AlternatingTree::iterateTree
void iterateTree(node start, node end, IteratorCallback callback)
Iterate the tree from start to end. callback is executed for each edge on the path....
Definition: AlternatingTree.h:108
ogdf::matching_blossom::Pseudonode::ReferenceEdges::removeFromOther
void removeFromOther(edge selfLoop)
Remove the given selfLoop from the reference edges of the other pseudonode.
Definition: Pseudonode.h:74
ogdf::end
HypergraphRegistry< HypernodeElement >::iterator end(const HypergraphRegistry< HypernodeElement > &self)
ogdf::matching_blossom::Cycle::contains
bool contains(node v)
Whether the cycle contains the node v or not.
utils.h
Utility functions and classes regarding map access and iteration.
ogdf::matching_blossom::AlternatingTree::findCycleStartNode
node findCycleStartNode(edge e)
Find the node where the paths from both endpoints of e to the root first meet.
Definition: AlternatingTree.h:70
ogdf::matching_blossom::AlternatingTree::moveEdge
void moveEdge(edge e, node oldNode, node newNode)
Exchange the end node oldNode of edge e in graph graph for node newNode.
Definition: AlternatingTree.h:116
ogdf::EdgeElement::opposite
node opposite(node v) const
Returns the adjacent node different from v.
Definition: Graph_d.h:406
ogdf::matching_blossom::AlternatingTree::root
node root()
Definition: AlternatingTree.h:134
ogdf::EdgeElement
Class for the representation of edges.
Definition: Graph_d.h:356
List.h
Declaration of doubly linked lists and iterators.
ogdf::NodeElement::graphOf
const Graph * graphOf() const
Returns the graph containing this node (debug only).
Definition: Graph_d.h:337
ogdf::EdgeElement::target
node target() const
Returns the target node of the edge.
Definition: Graph_d.h:394
ogdf::matching_blossom::AlternatingTree::shrink
Pseudonode * shrink(Cycle *cycle, std::vector< std::tuple< edge, bool >> &_selfLoops)
Shrink the cycle in this tree and return the generated pseudonode.
Definition: AlternatingTree.h:211
ogdf::matching_blossom::AlternatingTree::m_oddNodes
std::unordered_map< node, edge > m_oddNodes
All odd nodes, mapped to their respective back edge in the tree.
Definition: AlternatingTree.h:64
ogdf::NodeElement
Class for the representation of nodes.
Definition: Graph_d.h:233
ogdf::matching_blossom::AlternatingTree::getNextEvenNode
node getNextEvenNode(node v, IteratorCallback callback=nullptr)
Return the next even node in root direction. v must be even. callback is executed for both edges on t...
Definition: AlternatingTree.h:89
ogdf::matching_blossom::AlternatingTree::expand
void expand(Pseudonode *pseudonode)
Expand the given pseudonode in this tree.
Definition: AlternatingTree.h:302
ogdf::matching_blossom::AlternatingTree::isEven
bool isEven(node v)
Definition: AlternatingTree.h:140