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 <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 
62 namespace ogdf {
63 template<typename TWeight>
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 
85 namespace ogdf {
86 
91 template<typename TWeight>
92 class MatchingBlossomV : public MatchingModule<TWeight> {
95 
98 
101 
102 #ifdef OGDF_BLOSSOMV_PRINT_STATS
103  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
146  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 
277 public:
283  MatchingBlossomV(bool greedyInit = true) : m_helper(greedyInit), m_auxGraph(m_helper) { }
284 
285 private:
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));
360  printStatistics();
361 #endif
362  return true;
363  }
364 
366  bool primalChange() {
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  }
648  OGDF_BLOSSOMV_END_TIMER("grow");
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
871  void printStatistics() {
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 }
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:399
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:64
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:901
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:94
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:137
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:97
ogdf::MatchingBlossomV::stats::add
void add(long long t)
Definition: MatchingBlossomV.h:108
OGDF_BLOSSOMV_END_TIMER
#define OGDF_BLOSSOMV_END_TIMER(stat)
Definition: MatchingBlossomV.h:74
ogdf::MatchingBlossomV::dualChange
bool dualChange()
Executes a dual change step.
Definition: MatchingBlossomV.h:476
ogdf::MatchingBlossomV::m_eps
EpsilonTest m_eps
The epsilon test for floating point comparisons.
Definition: MatchingBlossomV.h:100
MatchingModule.h
Declaration of interface for matching algorithms.
ogdf::MatchingBlossomV::MatchingBlossomV
MatchingBlossomV(bool greedyInit=true)
Construct a MatchingBlossomV instance.
Definition: MatchingBlossomV.h:283
ogdf::MatchingBlossomV::findExpandablePseudonode
bool findExpandablePseudonode(AuxNode< TWeight > *auxNode)
Finds and expands an odd pseudonode in auxNode, if possible.
Definition: MatchingBlossomV.h:461
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:76
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:106
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:657
ogdf::MatchingBlossomV::processStatisticEntry
long long processStatisticEntry(const std::string &key)
Print a single statistics entry.
Definition: MatchingBlossomV.h:929
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:125
GraphList.h
Decralation of GraphElement and GraphList classes.
ogdf::MatchingBlossomV::augment
void augment(edge augmentationEdge)
Augment the matching with augmentationEdge.
Definition: MatchingBlossomV.h:548
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:444
ogdf::MatchingBlossomV::printStatistics
void printStatistics()
Print all statistics.
Definition: MatchingBlossomV.h:872
ogdf::MatchingBlossomV::findMatching
bool findMatching(std::unordered_set< edge > &matching)
Main function of the algorithm.
Definition: MatchingBlossomV.h:321
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:93
ogdf::MatchingBlossomV::grow
void grow(edge newEdge)
Augment the corresponding tree with augmentationEdge.
Definition: MatchingBlossomV.h:585
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:297
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:113
ogdf::MatchingBlossomV::m_stats
std::unordered_map< std::string, stats > m_stats
A mapping of all statistic names to their values.
Definition: MatchingBlossomV.h:117
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:104
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:75
ogdf::matching_blossom::AuxNode
Definition: AuxGraph.h:55
ogdf::MatchingBlossomV::doCall
bool doCall(const GraphAttributes &GA, std::unordered_set< edge > &matching)
Definition: MatchingBlossomV.h:291
ogdf::MatchingBlossomV::primalChange
bool primalChange()
Executes a primal change step.
Definition: MatchingBlossomV.h:366
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:105
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:776
BlossomVHelper.h
Utility class for the Blossom V algorithm.
OGDF_BLOSSOMV_START_TIMER
#define OGDF_BLOSSOMV_START_TIMER()
Definition: MatchingBlossomV.h:72
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:134
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:73
ogdf::MatchingBlossomV::doCall
bool doCall(const Graph &G, const EdgeArray< TWeight > &weights, std::unordered_set< edge > &matching)
Definition: MatchingBlossomV.h:286
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:120
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:427