Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

MatchingBlossom.h
Go to the documentation of this file.
1 
32 #pragma once
33 
34 #include <ogdf/basic/EdgeArray.h>
35 #include <ogdf/basic/Graph.h>
36 #include <ogdf/basic/GraphCopy.h>
37 #include <ogdf/basic/List.h>
38 #include <ogdf/basic/Logger.h>
45 
46 #include <algorithm>
47 #include <iostream>
48 #include <limits>
49 #include <memory>
50 #include <unordered_map>
51 #include <unordered_set>
52 #include <vector>
53 
54 namespace ogdf {
55 
62 template<typename TWeight>
63 class MatchingBlossom : public MatchingModule<TWeight> {
64  using Logger::lout;
65 
68 
70  std::unique_ptr<Graph::HiddenEdgeSet> m_nonEqualityEdgesHiddenSet;
71 
73  std::unordered_set<edge> m_nonEqualityEdges;
74 
76  std::unordered_set<node> m_graphNodes;
77 
79  std::unordered_set<node> m_unmatchedNodes;
80 
82  std::unordered_set<node> m_pseudonodes;
83 
85  std::unique_ptr<AlternatingTree<TWeight>> m_tree;
86 
87 #ifdef OGDF_HEAVY_DEBUG
88  void assertConsistency() {
90  if (m_unmatchedNodes.empty()) {
91  return;
92  }
93  // graph nodes & euqality edges
94  int matchedNodes = 0;
95  for (node v : m_graphNodes) {
96  if (m_helper.matching(v)) {
97  matchedNodes++;
98  }
99  OGDF_ASSERT(v->graphOf() == &m_helper.graph());
100  }
102  for (edge e : m_helper.graph().edges) {
103  bool isGraphNode = m_graphNodes.find(e->source()) != m_graphNodes.end();
104  OGDF_ASSERT(isGraphNode == (m_graphNodes.find(e->target()) != m_graphNodes.end()));
105  if (isGraphNode) {
107  == m_helper.isEqualityEdge(e));
108  }
109  }
110  // tree structure
111  for (node v : m_tree->evenNodes) {
112  // all even nodes except the root have a corresponding matching edge
113  if (v != m_tree->root()) {
114  OGDF_ASSERT(m_helper.matching(v) == m_tree->evenBackEdge(v));
115  }
116  // and no back edge in the tree
117  OGDF_ASSERT(!m_tree->isOdd(v));
118  }
119  for (node v : m_tree->oddNodes) {
120  OGDF_ASSERT(!m_tree->isEven(v));
121  // all odd nodes have both a back edge in the tree and a forward matching
122  // edge, since the matching edges are always defined for both ends
123  node w = m_helper.matching(v)->opposite(v);
124  OGDF_ASSERT(m_tree->isEven(w));
125  }
126  // assert that all matching edges are correctly defined on both ends
127  for (node v : m_helper.graph().nodes) {
128  if (edge matchingEdge = m_helper.matching(v)) {
129  OGDF_ASSERT(matchingEdge->isIncident(v));
130  OGDF_ASSERT(m_helper.matching(matchingEdge->opposite(v)) == matchingEdge);
131  }
132  }
133  // matching
134  long unsigned int hiddenNodes = 0;
135  for (auto entry : m_helper.pseudonodes()) {
136  auto pseudonode = entry.second;
137  hiddenNodes += pseudonode->cycle->nodes().size();
138  for (node v : pseudonode->cycle->nodes()) {
139  OGDF_ASSERT(m_helper.matching(v) == nullptr);
140  }
141  }
142  OGDF_ASSERT(matchedNodes + m_unmatchedNodes.size() + hiddenNodes
143  == (size_t)m_helper.graph().numberOfNodes());
144  if (m_tree->hasRoot()) {
145  OGDF_ASSERT(m_unmatchedNodes.find(m_tree->root()) != m_unmatchedNodes.end());
146  }
147  }
148 #endif
149 
152  for (edge e : m_nonEqualityEdges) {
154  }
155  }
156 
159 
167  node getNewRoot() { return *m_unmatchedNodes.begin(); }
168 
169 public:
175  MatchingBlossom(bool greedyInit = true) : m_helper(greedyInit) { }
176 
177 private:
178  bool doCall(const Graph& G, const EdgeArray<TWeight>& weights,
179  std::unordered_set<edge>& matching) {
180  return _doCall(G, weights, matching);
181  }
182 
183  bool doCall(const GraphAttributes& GA, std::unordered_set<edge>& matching) {
184  return _doCall(GA.constGraph(), GA, matching);
185  }
186 
188  template<class WeightContainer>
189  bool _doCall(const Graph& G, const WeightContainer& weights, std::unordered_set<edge>& matching) {
190  // init
191  lout() << std::endl;
192  if (!m_helper.init(G, weights)) {
193  return false;
194  }
196  m_nonEqualityEdges.clear();
197  for (edge e : m_helper.graph().edges) {
198  // hide all non-equality edges
199  if (!m_helper.isEqualityEdge(e)) {
200  m_nonEqualityEdges.insert(e);
201  }
202  }
204  m_graphNodes.clear();
205  m_unmatchedNodes.clear();
206  for (node v : m_helper.graph().nodes) {
207  m_graphNodes.insert(v);
208  if (!m_helper.matching(v)) {
209  m_unmatchedNodes.insert(v);
210  }
211  }
213 
214  return findMatching(matching);
215  }
216 
223  bool findMatching(std::unordered_set<edge>& matching) {
224  // main loop
225  while (!m_unmatchedNodes.empty()) {
226 #ifdef OGDF_HEAVY_DEBUG
227  assertConsistency();
228 #endif
229  if (!m_tree->hasRoot()) {
230  // we need to generate a new root for the tree
231  m_tree->reset(getNewRoot());
232  lout() << "new root: " << m_tree->root() << std::endl;
233  }
234  if (!primalChange() && !dualChange()) {
235  return false;
236  }
237  }
238  // matching found
239  m_helper.getOriginalMatching(matching);
240  return true;
241  }
242 
244  bool primalChange() {
245 #ifdef OGDF_HEAVY_DEBUG
246  std::unordered_map<edge, TWeight> edgeWeights;
247  m_helper.getReducedEdgeWeights(edgeWeights);
248 #endif
251 #ifdef OGDF_HEAVY_DEBUG
252  m_helper.assertConsistentEdgeWeights(edgeWeights);
253 #endif
254  return result;
255  }
256 
263  for (node u : m_tree->evenNodes) {
264  for (adjEntry adj : u->adjEntries) {
265  node v = adj->twinNode();
266  if (!m_helper.matching(v) && v != m_tree->root()) {
267  // found free edge, augment matching
268  m_tree->augmentMatching(adj->theEdge());
269  m_unmatchedNodes.erase(m_tree->root());
270  m_unmatchedNodes.erase(v);
271  m_tree->reset();
272  lout() << "augmented matching with " << adj->theEdge() << std::endl;
273  return true;
274  }
275  }
276  }
277  return false;
278  }
279 
286  for (node u : m_tree->evenNodes) {
287  for (adjEntry adj : u->adjEntries) {
288  node v = adj->twinNode();
289  edge matchingEdge = m_helper.matching(v);
290  if (matchingEdge && !m_tree->contains(v)) {
291  m_tree->grow(u, adj->theEdge(), matchingEdge);
292  lout() << "augmented tree with " << adj->theEdge() << std::endl;
293  return true;
294  }
295  }
296  }
297  return false;
298  }
299 
306  for (node u : m_tree->evenNodes) {
307  for (adjEntry adj : u->adjEntries) {
308  node v = adj->twinNode();
309  if (m_tree->isEven(v)) {
310  // found an odd cycle: shrink it
311  shrink(adj->theEdge());
312  return true;
313  }
314  }
315  }
316  return false;
317  }
318 
325  for (node v : m_pseudonodes) {
326  if (m_tree->isOdd(v) && m_helper.isZeroCostNode(v)) {
327  expand(m_helper.pseudonode(v));
328  return true;
329  }
330  }
331  return false;
332  }
333 
335  bool dualChange() {
336  TWeight delta = infinity<TWeight>();
337  // restore all edges to find potential candidates
339  // find ++/+0 edges
340  for (node u : m_tree->evenNodes) {
341  for (adjEntry adj : u->adjEntries) {
342  node v = adj->twinNode();
343  TWeight potentialChange = m_helper.getReducedWeight(adj->theEdge());
344  if (m_tree->isEven(v)) {
345  // ++ edge: max dual change has to be halfed
346  potentialChange *= 0.5;
347  } else if (m_tree->isOdd(v)) {
348  // +- edge: ignore
349  continue;
350  }
351  // (modified) reduced weight gives an upper bound to the dual change
352  delta = std::min(delta, potentialChange);
353  }
354  }
355  // find - pseudonodes
356  for (node v : m_pseudonodes) {
357  if (m_tree->isOdd(v)) {
358  delta = std::min(delta, m_helper.y(v));
359  }
360  }
361  // no dual change possible, so no matching can be found
362  if (delta == infinity<TWeight>()) {
363  return false;
364  }
365  OGDF_ASSERT(delta > 0);
366 
367  // do update of dual variables
368  for (node v : m_tree->evenNodes) {
369  m_helper.y(v) += delta;
370  }
371  for (node v : m_tree->oddNodes) {
372  m_helper.y(v) -= delta;
373  }
374  // update non-equality edges
375  for (node v : m_tree->evenNodes) {
376  for (adjEntry adj : v->adjEntries) {
377  edge e = adj->theEdge();
378  if (m_helper.isEqualityEdge(e)) {
379  auto it = m_nonEqualityEdges.find(e);
380  if (it != m_nonEqualityEdges.end()) {
381  m_nonEqualityEdges.erase(it);
382  }
383  }
384  }
385  }
386  for (node u : m_tree->oddNodes) {
387  // no adjacent edge to a node outside of the current tree can be an equality edge anymore
388  for (adjEntry adj : u->adjEntries) {
389  node v = adj->twinNode();
390  edge e = adj->theEdge();
391  if (!m_tree->isEven(v)) {
392  m_nonEqualityEdges.insert(e);
393  }
394  }
395  }
397  lout() << "dual change: " << delta << std::endl;
398  return true;
399  }
400 
402  void shrink(edge cycleEdge) {
404  Cycle* cycle = m_tree->getCycle(cycleEdge);
405  std::vector<std::tuple<edge, bool>> selfLoops;
406  Pseudonode* pseudonode = m_tree->shrink(cycle, selfLoops);
407  node newNode = pseudonode->graphNode;
408 
409  for (node u : pseudonode->cycle->nodes()) {
410  m_unmatchedNodes.erase(u);
411  m_graphNodes.erase(u);
412  }
413  for (node u : pseudonode->cycle->nodes()) {
414  for (auto adj : u->adjEntries) {
415  edge e = adj->theEdge();
416  auto it = m_nonEqualityEdges.find(e);
417  if (it != m_nonEqualityEdges.end()) {
418  m_nonEqualityEdges.erase(it);
419  }
420  }
421  }
422  m_graphNodes.insert(newNode);
423  m_pseudonodes.insert(newNode);
424  if (m_helper.matching(newNode) == nullptr) {
425  m_unmatchedNodes.insert(newNode);
426  }
427  lout() << "shrunk odd cycle of length " << cycle->nodes().size() << " at " << cycleEdge
428  << " into pseudonode " << pseudonode->graphNode << std::endl;
430  }
431 
433  void expand(Pseudonode* pseudonode) {
434  node graphNode = pseudonode->graphNode;
435  m_pseudonodes.erase(graphNode);
436  m_graphNodes.erase(graphNode);
437  OGDF_ASSERT(m_unmatchedNodes.empty() || m_helper.isZeroCostNode(graphNode));
439  int pseudonodeIndex = graphNode->index();
440  m_tree->expand(pseudonode);
441  for (node u : pseudonode->cycle->nodes()) {
442  m_graphNodes.insert(u);
443  }
444  for (node u : pseudonode->cycle->nodes()) {
445  for (auto adj : u->adjEntries) {
446  edge e = adj->theEdge();
447  if (!m_helper.isEqualityEdge(e)) {
448  m_nonEqualityEdges.insert(e);
449  }
450  }
451  }
452  delete pseudonode;
454  lout() << "expanded pseudonode " << pseudonodeIndex << std::endl;
455  }
456 };
457 
458 }
ogdf::MatchingBlossom::expand
void expand(Pseudonode *pseudonode)
Expand the pseudonode pseudonode.
Definition: MatchingBlossom.h:433
ogdf::MatchingBlossom::primalChange
bool primalChange()
Executes a primal change step.
Definition: MatchingBlossom.h:244
ogdf
The namespace for all OGDF objects.
Definition: AugmentationModule.h:36
ogdf::GraphAttributes
Stores additional attributes of a graph (like layout information).
Definition: GraphAttributes.h:66
ogdf::MatchingBlossom::findMatching
bool findMatching(std::unordered_set< edge > &matching)
Main function of the algorithm.
Definition: MatchingBlossom.h:223
ogdf::matching_blossom::AlternatingTree
Definition: AlternatingTree.h:50
ogdf::matching_blossom::Pseudonode
Helper class representing a pseudonode in the Blossom algorithm.
Definition: Pseudonode.h:45
Graph.h
Includes declaration of graph class.
ogdf::MatchingBlossom::m_pseudonodes
std::unordered_set< node > m_pseudonodes
All top-level pseudonodes.
Definition: MatchingBlossom.h:82
Pseudonode.h
Helper structure representing a pseudonode in the Blossom algorithm.
OGDF_ASSERT
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition: basic.h:54
ogdf::MatchingBlossom::m_unmatchedNodes
std::unordered_set< node > m_unmatchedNodes
All nodes which are not part of the matching yet.
Definition: MatchingBlossom.h:79
ogdf::MatchingBlossom::dualChange
bool dualChange()
Executes a dual change step.
Definition: MatchingBlossom.h:335
ogdf::Graph::HiddenEdgeSet
Functionality for temporarily hiding edges in constant time.
Definition: Graph_d.h:1216
ogdf::NodeElement::index
int index() const
Returns the (unique) node index.
Definition: Graph_d.h:267
ogdf::MatchingBlossom::restoreNonEqualityEdges
void restoreNonEqualityEdges()
Helper function to restore all non-equality edges.
Definition: MatchingBlossom.h:158
MatchingModule.h
Declaration of interface for matching algorithms.
ogdf::matching_blossom::Cycle
Definition: Cycle.h:43
ogdf::MatchingBlossom::findMatchingAugmentation
bool findMatchingAugmentation()
Finds and executes a matching augmentation, if possible.
Definition: MatchingBlossom.h:262
ogdf::GraphAttributes::constGraph
const Graph & constGraph() const
Returns a reference to the associated graph.
Definition: GraphAttributes.h:217
ogdf::matching_blossom::BlossomHelper
Helper class for the blossom matching algorithms.
Definition: BlossomHelper.h:55
ogdf::matching_blossom::Cycle::nodes
const std::unordered_set< node > & nodes()
ogdf::MatchingBlossom::m_nonEqualityEdges
std::unordered_set< edge > m_nonEqualityEdges
Set of all non-equality edges.
Definition: MatchingBlossom.h:73
BlossomHelper.h
Utility class for the Blossom algorithm, providiing access to all important data structures.
ogdf::AdjElement
Class for adjacency list elements.
Definition: Graph_d.h:135
ogdf::MatchingBlossom::m_tree
std::unique_ptr< AlternatingTree< TWeight > > m_tree
The alternating tree.
Definition: MatchingBlossom.h:85
Logger.h
Contains logging functionality.
ogdf::MatchingBlossom::m_helper
BlossomHelper< TWeight > m_helper
Helper class to store the current state of the algorithm.
Definition: MatchingBlossom.h:67
Cycle.h
Utility class representing a cycle in the Blossom algorithm.
ogdf::AdjElement::theEdge
edge theEdge() const
Returns the edge associated with this adjacency entry.
Definition: Graph_d.h:153
ogdf::MatchingBlossom::shrink
void shrink(edge cycleEdge)
Shrink the odd cycle induced by the current tree together with cycleEdge.
Definition: MatchingBlossom.h:402
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::MatchingBlossom::doCall
bool doCall(const GraphAttributes &GA, std::unordered_set< edge > &matching)
Main call function for the algorithm with GraphAttrubtes as weight container.
Definition: MatchingBlossom.h:183
ogdf::matching_blossom::Pseudonode::cycle
Cycle * cycle
The odd-length cycle that this pseudonode represents.
Definition: Pseudonode.h:93
ogdf::MatchingBlossom
Implementation of the Blossom-I algorithm by Edmonds (1965) for Minimum Weight Perfect Matching.
Definition: MatchingBlossom.h:63
ogdf::MatchingBlossom::findShrinkableCycle
bool findShrinkableCycle()
Finds and shrinks an odd cycle, if possible.
Definition: MatchingBlossom.h:305
GraphCopy.h
Declaration of graph copy classes.
ogdf::Graph
Data type for general directed graphs (adjacency list representation).
Definition: Graph_d.h:862
ogdf::MatchingBlossom::m_nonEqualityEdgesHiddenSet
std::unique_ptr< Graph::HiddenEdgeSet > m_nonEqualityEdgesHiddenSet
Pointer to the HiddenEdgeSet containing all non-equality edges.
Definition: MatchingBlossom.h:70
ogdf::matching_blossom::Pseudonode::graphNode
node graphNode
The node in the graph that this pseudonode is linked to.
Definition: Pseudonode.h:90
ogdf::AdjElement::twinNode
node twinNode() const
Returns the associated node of the corresponding adjacency entry (shorthand for twin()->theNode()).
Definition: Graph_d.h:168
ogdf::MatchingBlossom::findExpandablePseudonode
bool findExpandablePseudonode()
Finds and expands an odd pseudonode, if possible.
Definition: MatchingBlossom.h:324
ogdf::MatchingBlossom::_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: MatchingBlossom.h:189
ogdf::MatchingBlossom::findTreeAugmentation
bool findTreeAugmentation()
Finds and executes a tree augmentation, if possible.
Definition: MatchingBlossom.h:285
ogdf::MatchingBlossom::MatchingBlossom
MatchingBlossom(bool greedyInit=true)
Construct a MatchingBlossom instance.
Definition: MatchingBlossom.h:175
ogdf::MatchingBlossom::getNewRoot
node getNewRoot()
Helper function to get a new root for the tree.
Definition: MatchingBlossom.h:167
ogdf::MatchingBlossom::m_graphNodes
std::unordered_set< node > m_graphNodes
All nodes currently present in the graph.
Definition: MatchingBlossom.h:76
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::EdgeElement
Class for the representation of edges.
Definition: Graph_d.h:356
List.h
Declaration of doubly linked lists and iterators.
ogdf::MatchingBlossom::hideNonEqualityEdges
void hideNonEqualityEdges()
Helper function to hide all non-equality edges.
Definition: MatchingBlossom.h:151
ogdf::MatchingBlossom::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: MatchingBlossom.h:178
ogdf::NodeElement
Class for the representation of nodes.
Definition: Graph_d.h:233
ogdf::MatchingModule
Definition: MatchingModule.h:51
ogdf::internal::EdgeArrayBase2
RegisteredArray for edges of a graph, specialized for EdgeArray<edge>.
Definition: Graph_d.h:709
ogdf::Logger::lout
std::ostream & lout(Level level=Level::Default, bool indent=true) const
stream for logging-output (local)
Definition: Logger.h:158