Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

Loading...
Searching...
No Matches
AlternatingTree.h
Go to the documentation of this file.
1
32#pragma once
33
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
51template<class TWeight>
52class BlossomHelper;
53} // namespace ogdf::matching_blossom
54
55namespace ogdf {
56namespace matching_blossom {
57
58template<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) {
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
133public:
135
137
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 {
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}
Utility class representing a cycle in the Blossom algorithm.
Compare floating point numbers with epsilons and integral numbers with normal compare operators.
Includes declaration of graph class.
Declaration of doubly linked lists and iterators.
Helper structure representing a pseudonode in the Blossom algorithm.
Basic declarations, included by all source files.
Class for the representation of edges.
Definition Graph_d.h:364
node opposite(node v) const
Returns the adjacent node different from v.
Definition Graph_d.h:414
node target() const
Returns the target node of the edge.
Definition Graph_d.h:402
node source() const
Returns the source node of the edge.
Definition Graph_d.h:399
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
Doubly linked lists (maintaining the length of the list).
Definition List.h:1451
iterator pushBack(const E &x)
Adds element x at the end of the list.
Definition List.h:1547
E popFrontRet()
Removes the first element from the list and returns it.
Definition List.h:1591
bool empty() const
Returns true iff the list is empty.
Definition List.h:286
Class for the representation of nodes.
Definition Graph_d.h:241
void adjEdges(EDGELIST &edgeList) const
Returns a list with all edges incident to this node.
Definition Graph_d.h:320
const Graph * graphOf() const
Returns the graph containing this node (debug only).
Definition Graph_d.h:345
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 ...
BlossomHelper< TWeight > & m_helper
Reference to the helper class.
Pseudonode * shrink(Cycle *cycle, std::vector< std::tuple< edge, bool > > &_selfLoops)
Shrink the cycle in this tree and return the generated pseudonode.
void grow(node u, edge e, edge f)
Grow the tree by adding e and f.
void moveEdge(edge e, node oldNode, node newNode)
Exchange the end node oldNode of edge e in graph graph for node newNode.
AlternatingTree(BlossomHelper< TWeight > &helper, node root=nullptr)
KeyIteratorContainer< node, edge > oddNodes
Cycle * getCycle(edge cycleEdge)
Return the odd-length cycle which is closed in this tree with cycleEdge.
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...
void augmentMatching(edge startingEdge)
Augment the matching along the path from startingEdge to the root. startingEdge must be incident to a...
node findCycleStartNode(edge e)
Find the node where the paths from both endpoints of e to the root first meet.
node m_root
The root of the tree. May be empty to signalize an invalid tree which needs to be rebuild.
EpsilonTest m_eps
Epsilon test for floating point numbers.
void reset(node root=nullptr)
Reset the tree with new the new root. If root is nullptr, the tree will be invalid.
std::function< void(edge, bool)> IteratorCallback
Type of callback function when iterating the tree.
std::unordered_map< node, edge > m_evenNodes
All even nodes, mapped to their respective back edge in the tree (or nullptr for the root).
KeyIteratorContainer< node, edge > evenNodes
void expand(Pseudonode *pseudonode)
Expand the given pseudonode in this tree.
std::unordered_map< node, edge > m_oddNodes
All odd nodes, mapped to their respective back edge in the tree.
void iterateTree(node start, node end, IteratorCallback callback)
Iterate the tree from start to end. callback is executed for each edge on the path....
Dummy class for scoped iteration of a std::unordered_map.
Definition utils.h:112
Helper class for the blossom matching algorithms.
bool contains(node v)
Whether the cycle contains the node v or not.
const std::unordered_set< node > & nodes()
void addEdge(edge e)
Use this method to add edges in cycle order.
std::unordered_set< edge > & selfLoops(edge ref)
Definition Pseudonode.h:68
void removeFromOther(edge selfLoop)
Remove the given selfLoop from the reference edges of the other pseudonode.
Definition Pseudonode.h:79
Helper class representing a pseudonode in the Blossom algorithm.
Definition Pseudonode.h:50
ReferenceEdges referenceEdges
The ReferenceEdges for self loops of this pseudonode.
Definition Pseudonode.h:101
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...
Cycle * cycle
The odd-length cycle that this pseudonode represents.
Definition Pseudonode.h:98
node graphNode
The node in the graph that this pseudonode is linked to.
Definition Pseudonode.h:95
Utility functions and classes regarding map access and iteration.
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition basic.h:52
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
The namespace for all OGDF objects.
HypergraphRegistry< HypernodeElement >::iterator end(const HypergraphRegistry< HypernodeElement > &self)