Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

Loading...
Searching...
No Matches
MatchingBlossomV.h
Go to the documentation of this file.
1
32#pragma once
33
34
36#include <ogdf/basic/Graph.h>
39#include <ogdf/basic/Logger.h>
40#include <ogdf/basic/basic.h>
48
49#include <algorithm>
50#include <array>
51#include <chrono>
52#include <cstddef>
53#include <initializer_list>
54#include <iostream>
55#include <string>
56#include <tuple>
57#include <unordered_map>
58#include <unordered_set>
59#include <utility>
60#include <vector>
61
62namespace ogdf {
63template<typename TWeight>
64class MatchingBlossomV;
65} // namespace ogdf
66
67// Uncomment to print statistics
68#define OGDF_BLOSSOMV_PRINT_STATS
69
70// Helper macros for statistics
71#ifdef OGDF_BLOSSOMV_PRINT_STATS
72# define OGDF_BLOSSOMV_START_TIMER() OGDF_BLOSSOMV_START_NAMED_TIMER(__timestamp)
73# define OGDF_BLOSSOMV_START_NAMED_TIMER(timer) auto timer = now()
74# define OGDF_BLOSSOMV_END_TIMER(stat) OGDF_BLOSSOMV_END_NAMED_TIMER(__timestamp, stat)
75# define OGDF_BLOSSOMV_END_NAMED_TIMER(timer, stat) m_stats[stat].add(end(timer))
76# define OGDF_BLOSSOMV_ADD_STAT(stat) m_stats[stat].add(0)
77#else
78# define OGDF_BLOSSOMV_START_TIMER()
79# define OGDF_BLOSSOMV_START_NAMED_TIMER(timer)
80# define OGDF_BLOSSOMV_END_TIMER(name)
81# define OGDF_BLOSSOMV_END_NAMED_TIMER(timer, stat)
82# define OGDF_BLOSSOMV_ADD_STAT(stat)
83#endif
84
85namespace ogdf {
86
91template<typename TWeight>
92class MatchingBlossomV : public MatchingModule<TWeight> {
95
98
101
102#ifdef OGDF_BLOSSOMV_PRINT_STATS
104 struct stats {
105 int count = 0;
106 long long time = 0;
107
108 void add(long long t) {
109 count++;
110 time += t;
111 }
112
113 long long ms() { return time / 1000000; }
114 };
115
117 std::unordered_map<std::string, stats> m_stats;
118
120 std::chrono::high_resolution_clock::time_point now() {
121 return std::chrono::high_resolution_clock::now();
122 }
123
125 long long end(std::chrono::high_resolution_clock::time_point start) {
126 auto end = std::chrono::high_resolution_clock::now();
127 return std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
128 }
129#endif
130
131 using Logger::lout;
132
134 std::ostream& louth() { return lout(Logger::Level::High); }
135
138 auto succ = m_currentAuxNode->graphNode()->succ();
139 if (!succ) {
140 succ = m_auxGraph.graph().firstNode();
141 }
142 m_currentAuxNode = m_auxGraph.auxNode(succ);
143 }
144
145#ifdef OGDF_HEAVY_DEBUG
147 void assertConsistency() {
148 // aux graph & trees
149 for (auto auxNode : m_auxGraph.nodes()) {
150 OGDF_ASSERT(m_auxGraph.auxNode(auxNode->graphNode()) == auxNode);
151 auto tree = auxNode->tree();
152 for (node v : tree.evenNodes) {
153 OGDF_ASSERT(m_auxGraph.treeAuxNode(v) == auxNode);
154 OGDF_ASSERT(!tree.isOdd(v));
155 }
156 for (node v : tree.oddNodes) {
157 OGDF_ASSERT(m_auxGraph.treeAuxNode(v) == auxNode);
158 OGDF_ASSERT(!tree.isEven(v));
159 }
160 // tree structure
161 for (node v : tree.evenNodes) {
162 // all even nodes except the root have a corresponding matching edge
163 if (v != tree.root()) {
164 OGDF_ASSERT(m_helper.matching(v) == tree.evenBackEdge(v));
165 }
166 }
167 for (node v : tree.oddNodes) {
168 // all odd nodes have both a back edge in the tree and a forward matching
169 // edge, since the matching edges are always defined for both ends
170 node w = m_helper.matching(v)->opposite(v);
171 OGDF_ASSERT(tree.isEven(w));
172 }
173 for (auto adj : auxNode->graphNode()->adjEntries) {
174 OGDF_ASSERT(m_auxGraph.auxEdge(adj->theEdge()) != nullptr);
175 }
176 }
177 // assert that all matching edges are correctly defined on both ends
178 int matchedNodes = 0;
179 for (node v : m_helper.graph().nodes) {
180 if (edge matchingEdge = m_helper.matching(v)) {
181 matchedNodes++;
182 OGDF_ASSERT(matchingEdge->isIncident(v));
183 OGDF_ASSERT(m_helper.matching(matchingEdge->opposite(v)) == matchingEdge);
184 }
185 }
186 // matching
187 std::unordered_set<edge> hiddenEdges;
188 for (auto entry : m_helper.pseudonodes()) {
189 auto pseudonode = entry.second;
190 for (edge e : pseudonode->cycle->edgeOrder()) {
191 hiddenEdges.insert(e);
192 }
193 }
194 OGDF_ASSERT(matchedNodes + m_auxGraph.graph().numberOfNodes() + hiddenEdges.size()
195 == (size_t)m_helper.graph().numberOfNodes());
196 // priority queues
197 auto checkAllPQs = [&](edge e, BlossomPQ<edge, TWeight>* shouldContain = nullptr) {
198 for (auto auxNode : m_auxGraph.nodes()) {
199 for (auto* pq : {&auxNode->evenFreeEdges(), &auxNode->evenEvenEdges()}) {
200 OGDF_ASSERT(pq->contains(e) == (pq == shouldContain));
201 }
202 }
203 for (auto auxEdge : m_auxGraph.edges()) {
204 for (auto* pq : {&auxEdge->evenEvenEdges(), &auxEdge->evenOddEdges(),
205 &auxEdge->oddEvenEdges()}) {
206 OGDF_ASSERT(pq->contains(e) == (pq == shouldContain));
207 }
208 }
209 if (shouldContain != nullptr) {
210 OGDF_ASSERT(shouldContain->priority(e) == m_helper.getReducedWeight(e));
211 }
212 };
213 for (edge e : m_helper.graph().edges) {
214 bool sourceInGraph = m_helper.repr(e->source()) == e->source();
215 bool targetInGraph = m_helper.repr(e->target()) == e->target();
216 OGDF_ASSERT(sourceInGraph == targetInGraph);
217 if (!sourceInGraph) {
218 checkAllPQs(e);
219 continue;
220 }
221 OGDF_ASSERT(m_helper.getRealReducedWeight(e) >= 0);
222 auto sourceNode = m_auxGraph.treeAuxNode(e->source());
223 auto targetNode = m_auxGraph.treeAuxNode(e->target());
224 bool sourceIsEven = sourceNode && sourceNode->tree().isEven(e->source());
225 bool targetIsEven = targetNode && targetNode->tree().isEven(e->target());
226 if (hiddenEdges.find(e) != hiddenEdges.end()) {
227 OGDF_ASSERT(sourceNode == nullptr && targetNode == nullptr);
228 }
229 if (sourceNode == nullptr && targetNode == nullptr) {
230 checkAllPQs(e);
231 OGDF_ASSERT(m_helper.getRealReducedWeight(e) == m_helper.getReducedWeight(e));
232 } else if (sourceNode == nullptr) {
233 if (targetIsEven) {
234 checkAllPQs(e, &targetNode->evenFreeEdges());
235 }
236 } else if (targetNode == nullptr) {
237 if (sourceIsEven) {
238 checkAllPQs(e, &sourceNode->evenFreeEdges());
239 }
240 } else if (sourceNode == targetNode) {
241 if (sourceIsEven && targetIsEven) {
242 checkAllPQs(e, &sourceNode->evenEvenEdges());
243 } else {
244 checkAllPQs(e);
245 }
246 } else {
247 if (!sourceIsEven && !targetIsEven) {
248 checkAllPQs(e);
249 } else {
250 edge graphAuxEdge = sourceNode->graphNode()->graphOf()->searchEdge(
251 sourceNode->graphNode(), targetNode->graphNode());
252 OGDF_ASSERT(graphAuxEdge != nullptr);
253 auto auxEdge = m_auxGraph.auxEdge(graphAuxEdge);
254 if (sourceIsEven && targetIsEven) {
255 checkAllPQs(e, &auxEdge->evenEvenEdges());
256 } else if (sourceIsEven) {
257 checkAllPQs(e, &auxEdge->evenOddEdgesFromPerspective(sourceNode));
258 } else {
259 checkAllPQs(e, &auxEdge->evenOddEdgesFromPerspective(targetNode));
260 }
261 }
262 }
263 }
264 for (auto entry : m_helper.pseudonodes()) {
265 if (m_helper.repr(entry.first) == entry.first) {
266 OGDF_ASSERT(m_helper.realY(entry.first) >= 0);
267 auto auxNode = m_auxGraph.treeAuxNode(entry.first);
268 for (auto other : m_auxGraph.nodes()) {
269 OGDF_ASSERT(other->oddPseudonodes().contains(entry.first)
270 == (other == auxNode && auxNode->tree().isOdd(entry.first)));
271 }
272 }
273 }
274 }
275#endif
276
277public:
283 MatchingBlossomV(bool greedyInit = true) : m_helper(greedyInit), m_auxGraph(m_helper) { }
284
285private:
286 bool doCall(const Graph& G, const EdgeArray<TWeight>& weights,
287 std::unordered_set<edge>& matching) {
288 return _doCall(G, weights, matching);
289 }
290
291 bool doCall(const GraphAttributes& GA, std::unordered_set<edge>& matching) {
292 return _doCall(GA.constGraph(), GA, matching);
293 }
294
296 template<class WeightContainer>
297 bool _doCall(const Graph& G, const WeightContainer& weights, std::unordered_set<edge>& matching) {
298 // init
299 lout() << "start init" << std::endl;
300#ifdef OGDF_BLOSSOMV_PRINT_STATS
301 m_stats.clear();
302#endif
304 if (!m_helper.init(G, weights, &m_auxGraph)) {
305 return false;
306 };
307 OGDF_BLOSSOMV_END_TIMER("initialize");
308 lout() << "finish init" << std::endl;
309#ifdef OGDF_BLOSSOMV_PRINT_STATS
310 louth() << "Trees: " << m_auxGraph.graph().numberOfNodes() << std::endl;
311#endif
312 return findMatching(matching);
313 }
314
321 bool findMatching(std::unordered_set<edge>& matching) {
322#ifdef OGDF_BLOSSOMV_PRINT_STATS
323 std::chrono::high_resolution_clock::time_point last_path, last_four_paths;
324 // Calculate the next power of two for the number of aux nodes for logging purposes
325 int p = 1;
326 while (p < m_auxGraph.graph().numberOfNodes()) {
327 p <<= 1;
328 }
329#endif
330 // main loop
331 while (m_auxGraph.graph().numberOfNodes() > 0) {
332#ifdef OGDF_HEAVY_DEBUG
333 assertConsistency();
334#endif
335#ifdef OGDF_BLOSSOMV_PRINT_STATS
336 if (m_auxGraph.graph().numberOfNodes() == p / 2) {
337 p /= 2;
338 lout() << "." << p << std::flush;
339 if (p == 2) {
340 last_path = now();
341 } else if (p == 8) {
342 last_four_paths = now();
343 }
344 }
345#endif
346 if (!primalChange() && !dualChange()) {
347 return false;
348 }
349 }
350 lout() << "found matching" << std::endl;
351#ifdef OGDF_BLOSSOMV_PRINT_STATS
353#endif
355 m_helper.getOriginalMatching(matching);
356 OGDF_BLOSSOMV_END_TIMER("getOriginalMatching");
357#ifdef OGDF_BLOSSOMV_PRINT_STATS
358 m_stats["extraLastPathTime"].add(end(last_path));
359 m_stats["extraLastFourPathsTime"].add(end(last_four_paths));
361#endif
362 return true;
363 }
364
367#ifdef OGDF_HEAVY_DEBUG
368 std::unordered_map<edge, TWeight> edgeWeights;
369 m_helper.getReducedEdgeWeights(edgeWeights);
370#endif
371 if (!m_currentAuxNode) {
372 m_currentAuxNode = m_auxGraph.auxNode(m_auxGraph.graph().firstNode());
373 }
374 auto start = m_currentAuxNode;
375 AuxNode<TWeight>* auxNode;
376 // iterate all aux nodes in cyclic order
377 do {
378 auxNode = m_currentAuxNode;
380 // invalidate currentEdges of all aux nodes
381 m_helper.currentIteration++;
382 if (findMatchingAugmentation(auxNode) || findTreeAugmentation(auxNode)
383 || findShrinkableCycle(auxNode) || findExpandablePseudonode(auxNode)) {
384#ifdef OGDF_HEAVY_DEBUG
385 m_helper.assertConsistentEdgeWeights(edgeWeights);
386#endif
387 return true;
388 }
389 } while (m_currentAuxNode != start);
390 m_currentAuxNode = nullptr;
391 return false;
392 }
393
401 AuxEdge<TWeight>* auxEdge;
402 AuxNode<TWeight>* other;
403 edge minEdge;
404 for (auto adj : auxNode->graphNode()->adjEntries) {
405 auxEdge = m_auxGraph.auxEdge(adj->theEdge());
406 other = m_auxGraph.auxNode(adj->twinNode());
407
408 // set the currentEdge for all future operations in this iteration
409 other->setCurrentEdge(auxEdge);
410 // check if augmentation is possible
411 minEdge = m_helper.getTopEligibleElement(auxEdge->evenEvenEdges());
412 if (minEdge != nullptr) {
413 OGDF_BLOSSOMV_END_TIMER("findAugment");
414 augment(minEdge);
415 return true;
416 }
417 }
418 OGDF_BLOSSOMV_END_TIMER("findAugment");
419 return false;
420 }
421
429 edge minEdge = m_helper.getTopEligibleElement(auxNode->evenFreeEdges());
430 if (minEdge != nullptr) {
431 OGDF_BLOSSOMV_END_TIMER("findGrow");
432 grow(minEdge);
433 return true;
434 }
435 OGDF_BLOSSOMV_END_TIMER("findGrow");
436 return false;
437 }
438
446 edge minEdge = m_helper.getTopEligibleElement(auxNode->evenEvenEdges());
447 if (minEdge != nullptr) {
448 OGDF_BLOSSOMV_END_TIMER("findShrink");
449 shrink(minEdge);
450 return true;
451 }
452 OGDF_BLOSSOMV_END_TIMER("findShrink");
453 return false;
454 }
455
463 node minNode = m_helper.getTopEligibleElement(auxNode->oddPseudonodes());
464 if (minNode != nullptr) {
465 auxNode->oddPseudonodes().pop();
466 Pseudonode* pseudonode = m_helper.pseudonode(minNode);
467 OGDF_BLOSSOMV_END_TIMER("findExpand");
468 expand(pseudonode);
469 return true;
470 }
471 OGDF_BLOSSOMV_END_TIMER("findExpand");
472 return false;
473 }
474
476 bool dualChange() {
478 NodeArray<TWeight> deltas(m_auxGraph.graph(), 0);
479 TWeight delta, otherDelta;
480 AuxEdge<TWeight>* auxEdge;
481 node w;
482
483 // Calculates the connected components of the aux graph and sets the same delta for nodes in
484 // the same component.
485 std::vector<std::unordered_set<node>> components;
486 m_auxGraph.connectedComponents(components);
487 for (auto& component : components) {
488 delta = infinity<TWeight>();
489 AuxNode<TWeight>* auxNode;
490 for (node v : component) {
491 auxNode = m_auxGraph.auxNode(v);
492 if (!auxNode->evenEvenEdges().empty()) {
493 delta = std::min(delta,
494 m_helper.getRealTopPriority(auxNode->evenEvenEdges()) / 2);
495 }
496 delta = std::min({delta, m_helper.getRealTopPriority(auxNode->evenFreeEdges()),
497 m_helper.getRealTopPriority(auxNode->oddPseudonodes())});
498 for (auto& adj : v->adjEntries) {
499 w = adj->twinNode();
500 auxEdge = m_auxGraph.auxEdge(adj->theEdge());
501 if (component.find(w) != component.end()) {
502 // same component = same delta
503 if (!auxEdge->evenEvenEdges().empty()) {
504 delta = std::min(delta,
505 m_helper.getRealTopPriority(auxEdge->evenEvenEdges()) / 2);
506 }
507 } else {
508 otherDelta = deltas[w];
509 if (!auxEdge->evenEvenEdges().empty()) {
510 delta = std::min(delta,
511 m_helper.getRealTopPriority(auxEdge->evenEvenEdges())
512 - otherDelta);
513 }
514 auto& evenOddPQ = auxEdge->evenOddEdgesFromPerspective(auxNode);
515 if (!evenOddPQ.empty()) {
516 delta = std::min(delta,
517 m_helper.getRealTopPriority(evenOddPQ) + otherDelta);
518 }
519 }
520 // if already at zero, no dual change can be made in this tree
521 if (m_helper.isZero(delta)) {
522 break;
523 }
524 }
525 }
526 // apply delta to all nodes of the component
527 if (delta > 0 && delta < infinity<TWeight>()) {
528 for (auto& v : component) {
529 deltas[v] = delta;
530 }
531 }
532 }
533
534 bool dualChange = false;
535 for (auto& auxNode : m_auxGraph.nodes()) {
536 delta = deltas[auxNode->graphNode()];
537 if (delta > 0) {
538 dualChange = true;
539 auxNode->addDelta(delta);
540 }
541 lout() << "Delta for " << auxNode->tree().root() << ": " << delta << std::endl;
542 }
543 OGDF_BLOSSOMV_END_TIMER("dualChange");
544 return dualChange;
545 }
546
548 void augment(edge augmentationEdge) {
550 OGDF_ASSERT(augmentationEdge->graphOf() == &m_helper.graph());
551 for (auto augmentationEdgeNode : augmentationEdge->nodes()) {
552 auto auxNode = m_auxGraph.treeAuxNode(augmentationEdgeNode);
553 auto oppositeAuxNode =
554 m_auxGraph.treeAuxNode(augmentationEdge->opposite(augmentationEdgeNode));
555 // merge PQs
556 std::vector<edge> edgesToUpdate;
557 for (auto adj : auxNode->graphNode()->adjEntries) {
558 auto other = m_auxGraph.auxNode(adj->twinNode());
559 if (other != auxNode && other != oppositeAuxNode) {
560 auto otherEdge = m_auxGraph.auxEdge(adj->theEdge());
561 for (auto pq : {&otherEdge->evenEvenEdges(),
562 &otherEdge->evenOddEdgesFromPerspective(other)}) {
563 for (edge e : *pq) {
564 // delay insertion into PQs until the weights have been updated
565 edgesToUpdate.push_back(e);
566 }
567 }
568 }
569 }
570 auxNode->tree().augmentMatching(augmentationEdge);
571 if (m_currentAuxNode == auxNode) {
573 }
574 m_auxGraph.deleteNode(auxNode);
575 for (edge e : edgesToUpdate) {
576 auto an = m_auxGraph.auxNodeForEdge(e);
577 an->addEvenFreeEdge(e);
578 }
579 }
580 OGDF_BLOSSOMV_END_TIMER("augment");
581 lout() << "Matching augmented with " << augmentationEdge << std::endl;
582 }
583
585 void grow(edge newEdge) {
587 auto auxNode = m_auxGraph.auxNodeForEdge(newEdge);
588 auto& tree = auxNode->tree();
589 // (tree - u) - v -- w, where v -- w is the matching edge
590 node u = tree.commonNode(newEdge);
591 node v = newEdge->opposite(u);
592 edge matchingEdge = m_helper.matching(v);
593 node w = matchingEdge->opposite(v);
594 tree.grow(u, newEdge, matchingEdge);
595
596 // adjust y values
597 for (node x : matchingEdge->nodes()) {
598 m_auxGraph.setAuxNode(x, auxNode);
599 m_helper.y(x) -= auxNode->delta(x);
600 }
601
602 // update data priority queues
603 if (m_helper.isPseudonode(v)) {
604 auxNode->addOddPseudonode(v);
605 }
606 for (auto adj : v->adjEntries) {
607 node x = adj->twinNode();
608 edge e = adj->theEdge();
609 auto xAuxNode = m_auxGraph.treeAuxNode(x);
610 if (xAuxNode != nullptr && xAuxNode->tree().isEven(x)) {
611 if (x != w) {
612 xAuxNode->evenFreeEdges().remove(e);
613 }
614 if (xAuxNode != auxNode) {
615 m_auxGraph.assertCurrentEdge(xAuxNode, auxNode)
616 ->addEvenOddEdgeFromPerspective(e, xAuxNode);
617 }
618 }
619 }
620 edge augmentationEdge = nullptr;
621 for (auto adj : w->adjEntries) {
622 node x = adj->twinNode();
623 edge e = adj->theEdge();
624 auto xAuxNode = m_auxGraph.treeAuxNode(x);
625 if (xAuxNode == nullptr) {
626 // x is free
627 auxNode->addEvenFreeEdge(e);
628 } else if (xAuxNode == auxNode) {
629 // x belongs to the same tree
630 if (tree.isEven(x)) {
631 auxNode->evenFreeEdges().remove(e);
632 auxNode->addEvenEvenEdge(e);
633 }
634 } else {
635 auto auxEdge = m_auxGraph.assertCurrentEdge(xAuxNode, auxNode);
636 auto xTree = xAuxNode->tree();
637 if (xTree.isOdd(x)) {
638 auxEdge->addEvenOddEdgeFromPerspective(e, auxNode);
639 } else {
640 xAuxNode->evenFreeEdges().remove(e);
641 auxEdge->addEvenEvenEdge(e);
642 if (augmentationEdge == nullptr && m_helper.isEqualityEdge(e)) {
643 augmentationEdge = e;
644 }
645 }
646 }
647 }
649 lout() << "Tree augmented with " << newEdge << std::endl;
650 if (augmentationEdge != nullptr) {
651 OGDF_BLOSSOMV_ADD_STAT("extraAugmentShortcut");
652 augment(augmentationEdge);
653 }
654 }
655
657 void shrink(edge cycleEdge) {
659 auto auxNode = m_auxGraph.auxNodeForEdge(cycleEdge);
660 auto& tree = auxNode->tree();
661 Cycle* cycle = tree.getCycle(cycleEdge);
662 OGDF_BLOSSOMV_END_TIMER("extraGetCycle");
663 OGDF_BLOSSOMV_START_NAMED_TIMER(beforeShrinkTimer);
664 // update priority queues
665 std::vector<edge> oddEdges;
666 std::unordered_map<node, edge> bestEdges;
667 edge augmentationEdge = nullptr;
668 for (node u : cycle->nodes()) {
669 bool isOdd = tree.isOdd(u);
670 for (auto adj : u->adjEntries) {
671 edge e = adj->theEdge();
672 node v = adj->twinNode();
673 if (isOdd) {
674 if (!cycle->contains(v)) {
675 oddEdges.push_back(e);
676 OGDF_BLOSSOMV_ADD_STAT("extraShrinkFoundOddEdge");
677 }
678 } else if (e->source() == v && tree.isEven(v) && cycle->contains(v)) {
679 // only remove if v is the source of the edge to prevent duplicate removal
680 auxNode->evenEvenEdges().remove(e);
681 OGDF_BLOSSOMV_ADD_STAT("extraShrinkRemoveEvenEvenEdge");
682 }
683 }
684 if (isOdd && m_helper.isPseudonode(u)) {
685 auxNode->oddPseudonodes().remove(u);
686 OGDF_BLOSSOMV_ADD_STAT("extraShrinkOddPseudonodeChild");
687 }
688 }
689
690 // update y values and tree associations
691 for (node u : cycle->nodes()) {
692 m_helper.y(u) = m_helper.realY(u);
693 m_auxGraph.setAuxNode(u, nullptr);
694 }
695 OGDF_BLOSSOMV_END_NAMED_TIMER(beforeShrinkTimer, "extraShrinkUpdatePQsBefore");
696
697 // do the actual shrinking
698 std::vector<std::tuple<edge, bool>> selfLoops;
699 OGDF_BLOSSOMV_START_NAMED_TIMER(actualShrinkTimer);
700 Pseudonode* pseudonode = tree.shrink(cycle, selfLoops);
701 OGDF_BLOSSOMV_END_NAMED_TIMER(actualShrinkTimer, "extraActualShrink");
702 OGDF_BLOSSOMV_START_NAMED_TIMER(afterShrinkTimer);
703 node newNode = pseudonode->graphNode;
704 m_auxGraph.setAuxNode(newNode, auxNode);
705 // set y to -delta so that realY is 0 (newNode is always even)
706 m_helper.y(newNode) = -auxNode->delta();
707
708 // remove self loops from pqs
709 std::unordered_set<edge> hiddenEdges;
710 for (auto& entry : selfLoops) {
711 edge e = std::get<0>(entry);
712 bool isEven = std::get<1>(entry);
713 node vInner = m_helper.getOppositeBaseNode(e, newNode);
714 node v = m_helper.repr(vInner);
715 hiddenEdges.insert(e);
716 auto other = m_auxGraph.treeAuxNode(v);
717 if (other == nullptr) {
718 if (isEven) {
719 auxNode->evenFreeEdges().remove(e);
720 }
721 } else if (other == auxNode) {
722 if (isEven && tree.isEven(v)) {
723 auxNode->evenEvenEdges().remove(e);
724 }
725 } else {
726 if (isEven && other->tree().isEven(v)) {
727 other->currentEdge()->evenEvenEdges().remove(e);
728 }
729 if (isEven && other->tree().isOdd(v)) {
730 other->currentEdge()->evenOddEdgesFromPerspective(auxNode).remove(e);
731 }
732 if (!isEven && other->tree().isEven(v)) {
733 other->currentEdge()->evenOddEdgesFromPerspective(other).remove(e);
734 }
735 }
736 }
737
738 // add edges back to correct priority queues after the costs have been updated
739 for (edge e : oddEdges) {
740 if (hiddenEdges.find(e) != hiddenEdges.end()) {
741 continue;
742 }
743 node v = e->opposite(newNode);
744 auto other = m_auxGraph.treeAuxNode(v);
745 if (other == nullptr) {
746 // v is free -> add edge to evenFreeEdges
747 auxNode->addEvenFreeEdge(e);
748 } else if (other->tree().isEven(v)) {
749 if (other == auxNode) {
750 auxNode->addEvenEvenEdge(e);
751 } else {
752 // u-v is in the evenOddEdges/oddEvenEdges of other->currentEdge and has to
753 // be switched to the evenEvenEdges, since the new pseudonode is even
754 auto auxEdge = m_auxGraph.assertCurrentEdge(other, auxNode);
755 auxEdge->evenOddEdgesFromPerspective(other).remove(e);
756 auxEdge->addEvenEvenEdge(e);
757 if (augmentationEdge == nullptr && m_helper.isEqualityEdge(e)) {
758 augmentationEdge = e;
759 }
760 }
761 } else if (other != auxNode) {
762 m_auxGraph.assertCurrentEdge(other, auxNode)->addEvenOddEdgeFromPerspective(e, auxNode);
763 }
764 }
765 OGDF_BLOSSOMV_END_NAMED_TIMER(afterShrinkTimer, "extraShrinkUpdatePQsAfter");
766 OGDF_BLOSSOMV_END_TIMER("shrink");
767 OGDF_BLOSSOMV_ADD_STAT("extraShrinkSize" + std::to_string(cycle->nodes().size()));
768 lout() << std::endl << "Shrank at " << cycleEdge << " into " << newNode << std::endl;
769 if (augmentationEdge != nullptr) {
770 OGDF_BLOSSOMV_ADD_STAT("extraAugmentShortcut");
771 augment(augmentationEdge);
772 }
773 }
774
776 void expand(Pseudonode* pseudonode) {
778 auto auxNode = m_auxGraph.treeAuxNode(pseudonode->graphNode);
779 auto& tree = auxNode->tree();
780 auto cycle = pseudonode->cycle;
781 int nodeIndex = pseudonode->graphNode->index();
782 tree.expand(pseudonode);
783
784 // update y values and tree associations
785 for (node u : cycle->nodes()) {
786 if (tree.contains(u)) {
787 m_auxGraph.setAuxNode(u, auxNode);
788 m_helper.y(u) -= auxNode->delta(u);
789 }
790 }
791
792 // update priority queues
793 edge augmentationEdge = nullptr;
794 for (node u : cycle->nodes()) {
795 if (tree.isEven(u)) {
796 for (auto adj : u->adjEntries) {
797 edge e = adj->theEdge();
798 node v = adj->twinNode();
799 auto other = m_auxGraph.treeAuxNode(v);
800 if (other == nullptr) {
801 // v is free
802 auxNode->addEvenFreeEdge(e);
803 } else if (other == auxNode) {
804 // v belongs to the same tree
805 // prevent double adding for cycle edges by only adding if either v is not
806 // part of the cycle or v is the source of the edge
807 if (tree.isEven(v) && (!cycle->contains(v) || e->source() == v)) {
808 auxNode->addEvenEvenEdge(e);
809 }
810 } else {
811 auto auxEdge = m_auxGraph.assertCurrentEdge(other, auxNode);
812 if (other->tree().isEven(v)) {
813 if (auxEdge->evenOddEdgesFromPerspective(other).contains(e)) {
814 auxEdge->evenOddEdgesFromPerspective(other).remove(e);
815 }
816 auxEdge->addEvenEvenEdge(e);
817 if (augmentationEdge == nullptr && m_helper.isEqualityEdge(e)) {
818 augmentationEdge = e;
819 }
820 } else {
821 auxEdge->addEvenOddEdgeFromPerspective(e, auxNode);
822 }
823 }
824 }
825 } else if (tree.isOdd(u)) {
826 if (m_helper.isPseudonode(u)) {
827 auxNode->addOddPseudonode(u);
828 }
829 for (auto adj : u->adjEntries) {
830 edge e = adj->theEdge();
831 node v = adj->twinNode();
832 auto other = m_auxGraph.treeAuxNode(v);
833 if (other && other != auxNode && other->tree().isEven(v)) {
834 auto auxEdge = m_auxGraph.assertCurrentEdge(other, auxNode);
835 if (!auxEdge->evenOddEdgesFromPerspective(other).contains(e)) {
836 auxEdge->addEvenOddEdgeFromPerspective(e, other);
837 }
838 }
839 }
840 } else {
841 // u is free
842 for (auto adj : u->adjEntries) {
843 edge e = adj->theEdge();
844 node v = adj->twinNode();
845 auto other = m_auxGraph.treeAuxNode(v);
846 if (other && other->tree().isEven(v)) {
847 if (other != auxNode) {
848 if (other->currentEdge()->evenOddEdgesFromPerspective(other).contains(e)) {
849 other->currentEdge()->evenOddEdgesFromPerspective(other).remove(e);
850 }
851 }
852 // only add if v is not part of the cycle, otherwise it is already added in
853 // the if-clause above
854 if (!cycle->contains(v)) {
855 other->addEvenFreeEdge(e);
856 }
857 }
858 }
859 }
860 }
861 delete pseudonode;
862 OGDF_BLOSSOMV_END_TIMER("expand");
863 lout() << std::endl << "Expanded " << nodeIndex << std::endl;
864 if (augmentationEdge != nullptr) {
865 OGDF_BLOSSOMV_ADD_STAT("extraAugmentShortcut");
866 augment(augmentationEdge);
867 }
868 }
869
870#ifdef OGDF_BLOSSOMV_PRINT_STATS
873 long long total = 0;
874 for (std::string key : {"initialize", "augment", "grow", "shrink", "expand", "dualChange",
875 "findAugment", "findGrow", "findShrink", "findExpand"}) {
876 total += processStatisticEntry(key);
877 }
878
879 std::vector<std::string> extraKeys;
880 std::vector<std::string> otherKeys;
881 for (auto entry : m_stats) {
882 if (entry.first.substr(0, 5) == "extra") {
883 extraKeys.push_back(entry.first);
884 } else {
885 otherKeys.push_back(entry.first);
886 }
887 }
888 std::sort(extraKeys.begin(), extraKeys.end());
889 std::sort(otherKeys.begin(), otherKeys.end());
890
891 for (auto key : otherKeys) {
892 total += processStatisticEntry(key);
893 }
894 louth() << "Total tracked time: " << total << " ms" << std::endl;
895 for (auto key : extraKeys) {
897 }
898 }
899
902 int max = 0, sum = 0, total = 0;
903 std::unordered_map<int, std::unordered_map<int, int>> edgeCount;
904 for (edge e : m_helper.graph().edges) {
905 if (m_helper.repr(e->source()) == e->source()
906 && m_helper.repr(e->target()) == e->target()) {
907 OGDF_ASSERT(!e->isSelfLoop());
908 total++;
909 int i = std::min(e->source()->index(), e->target()->index());
910 int j = std::max(e->source()->index(), e->target()->index());
911 edgeCount[i][j]++;
912 }
913 }
914 for (auto iEntry : edgeCount) {
915 for (auto jEntry : iEntry.second) {
916 int count = jEntry.second;
917 if (count > max) {
918 max = count;
919 }
920 sum += count - 1;
921 }
922 }
923 louth() << "Max number of parallel edges: " << max << std::endl;
924 louth() << "Total number of parallel edges: " << sum << " (" << ((double)sum / total * 100)
925 << "%)" << std::endl;
926 }
927
929 long long processStatisticEntry(const std::string& key) {
930 louth() << key << ": " << m_stats[key].count << " (" << m_stats[key].ms() << " ms)"
931 << std::endl;
932 auto time = m_stats[key].ms();
933 m_stats.erase(key);
934 return time;
935 }
936#endif
937};
938
939}
Implementation of the auxiliary graph as well as the edges and nodes of it for the Blossom V algorith...
Utility class for the Blossom V algorithm.
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 class GraphAttributes which extends a Graph by additional attributes.
Decralation of GraphElement and GraphList classes.
Contains logging functionality.
#define OGDF_BLOSSOMV_ADD_STAT(stat)
#define OGDF_BLOSSOMV_END_NAMED_TIMER(timer, stat)
#define OGDF_BLOSSOMV_START_TIMER()
#define OGDF_BLOSSOMV_END_TIMER(stat)
#define OGDF_BLOSSOMV_START_NAMED_TIMER(timer)
Declaration of interface for matching algorithms.
An extension of the standard priority queue with additional features.
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
const Graph * graphOf() const
Returns the graph containing this node (debug only).
Definition Graph_d.h:441
std::array< node, 2 > nodes() const
Returns a list of adjacent nodes. If this edge is a self-loop, both entries will be the same node.
Definition Graph_d.h:405
node source() const
Returns the source node of the edge.
Definition Graph_d.h:399
Stores additional attributes of a graph (like layout information).
const Graph & constGraph() const
Returns a reference to the associated graph.
Data type for general directed graphs (adjacency list representation).
Definition Graph_d.h:866
edge searchEdge(node v, node w, bool directed=false) const
Searches and returns an edge connecting nodes v and w in time O( min(deg(v ), deg(w ))).
std::ostream & lout(Level level=Level::Default, bool indent=true) const
stream for logging-output (local)
Definition Logger.h:160
Implementation of the Blossom-V algorithm by Kolmogorov (2009) for Minimum Weight Perfect Matching.
bool primalChange()
Executes a primal change step.
MatchingBlossomV(bool greedyInit=true)
Construct a MatchingBlossomV instance.
long long end(std::chrono::high_resolution_clock::time_point start)
Get the time difference between start and now in nanoseconds.
AuxGraph< TWeight > m_auxGraph
bool findMatchingAugmentation(AuxNode< TWeight > *auxNode)
Finds and executes a matching augmentation in auxNode, if possible.
EpsilonTest m_eps
The epsilon test for floating point comparisons.
AuxNode< TWeight > * m_currentAuxNode
The current aux node in the main loop.
void expand(Pseudonode *pseudonode)
Expand the given pseudonode.
std::unordered_map< std::string, stats > m_stats
A mapping of all statistic names to their values.
bool findTreeAugmentation(AuxNode< TWeight > *auxNode)
Finds and executes a tree augmentation in auxNode, if possible.
bool findExpandablePseudonode(AuxNode< TWeight > *auxNode)
Finds and expands an odd pseudonode in auxNode, if possible.
void shrink(edge cycleEdge)
Shrink the odd cycle induced by cycleEdge and the tree determined by its endpoints.
void augment(edge augmentationEdge)
Augment the matching with augmentationEdge.
void printParallelEdgesStats()
Print additional statistics about parallel edges.
bool dualChange()
Executes a dual change step.
std::ostream & louth()
Helper function to log with high priority.
bool findShrinkableCycle(AuxNode< TWeight > *auxNode)
Finds and shrinks an odd cycle in auxNode, if possible.
std::ostream & lout(Level level=Level::Default, bool indent=true) const
stream for logging-output (local)
Definition Logger.h:160
void grow(edge newEdge)
Augment the corresponding tree with augmentationEdge.
void printStatistics()
Print all statistics.
bool doCall(const Graph &G, const EdgeArray< TWeight > &weights, std::unordered_set< edge > &matching)
Main call function for the algorithm with an EdgeArray as weight container.
BlossomVHelper< TWeight > m_helper
bool _doCall(const Graph &G, const WeightContainer &weights, std::unordered_set< edge > &matching)
Helper for the main call function since abstract functions cannot be templated.
bool doCall(const GraphAttributes &GA, std::unordered_set< edge > &matching)
Main call function for the algorithm with GraphAttrubtes as weight container.
bool findMatching(std::unordered_set< edge > &matching)
Main function of the algorithm.
void advanceCurrentAuxNode()
Helper function to advance the current aux node to the next one.
long long processStatisticEntry(const std::string &key)
Print a single statistics entry.
std::chrono::high_resolution_clock::time_point now()
Get the current time point.
Class for the representation of nodes.
Definition Graph_d.h:241
internal::GraphObjectContainer< AdjElement > adjEntries
The container containing all entries in the adjacency list of this node.
Definition Graph_d.h:272
int index() const
Returns the (unique) node index.
Definition Graph_d.h:275
bool empty() const
Checks whether the queue is empty.
RegisteredArray for edges of a graph, specialized for EdgeArray<edge>.
Definition Graph_d.h:717
RegisteredArray for nodes, edges and adjEntries of a graph.
Definition Graph_d.h:659
EdgePQ & evenOddEdgesFromPerspective(AuxNode< TWeight > *auxNode)
Returns evenOddEdges or oddEvenEdges, depending on the perspective of auxNode (the even node).
Definition AuxGraph.h:190
void setCurrentEdge(AuxEdge< TWeight > *edge)
Sets the current edge of this tree to edge and update the iteration to the current one.
Definition AuxGraph.h:104
void pop()
Removes the topmost element from the queue.
Definition PQ.h:87
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()
Helper class representing a pseudonode in the Blossom algorithm.
Definition Pseudonode.h:50
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.
NodeElement * node
The type of nodes.
Definition Graph_d.h:71
EdgeElement * edge
The type of edges.
Definition Graph_d.h:75
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition basic.h:52
The namespace for all OGDF objects.
@ tree
for every hyperedge e a minimal subcubic tree connecting all hypernodes incident with e together is a...
Structure to store statistics.