Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

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