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