Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

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