Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

BlossomHelper.h
Go to the documentation of this file.
1 
33 #pragma once
34 
35 #include <ogdf/basic/EdgeArray.h>
36 #include <ogdf/basic/EpsilonTest.h>
37 #include <ogdf/basic/Graph.h>
38 #include <ogdf/basic/GraphCopy.h>
39 #include <ogdf/basic/Logger.h>
40 #include <ogdf/basic/NodeArray.h>
43 
44 #include <algorithm>
45 #include <iostream>
46 #include <limits>
47 #include <unordered_map>
48 #include <unordered_set>
49 
50 namespace ogdf {
51 namespace matching_blossom {
52 
54 template<class TWeight>
55 class BlossomHelper : private Logger {
56 protected:
59 
62 
66 
69 
71  std::unordered_map<node, Pseudonode*> m_pseudonodes;
72 
74  std::vector<node> m_repr;
75 
77  std::vector<node> m_shortcuts;
78 
81 
82  // Multiply all integer weights by 4 to ensure that all primal and dual values stay integral.
83  static const int WEIGHT_FACTOR = std::numeric_limits<TWeight>::is_integer ? 4 : 1;
84 
87  // for _some_ reason, the array has to be initialized with 0, otherwise the algorithm fails
88  m_y.init(m_graph, 0);
89  for (node v : m_graph.nodes) {
90  if (v->degree() == 0) {
91  return false;
92  }
93  m_y[v] = minY[v];
94  }
95  if (!m_greedyInit) {
96  return true;
97  }
98  for (node v : m_graph.nodes) {
99  if (matching(v) != nullptr) {
100  continue;
101  }
102  edge matchingEdge = nullptr;
103  TWeight maxChange = infinity<TWeight>();
104  for (auto adj : v->adjEntries) {
105  edge e = adj->theEdge();
106  node w = adj->twinNode();
107  TWeight reducedWeight = getReducedWeight(e);
108  if (m_eps.leq(reducedWeight, maxChange)) {
109  maxChange = reducedWeight;
110  if (!matching(w)) {
111  matchingEdge = e;
112  }
113  }
114  }
115  y(v) += maxChange;
116  if (matchingEdge != nullptr && isEqualityEdge(matchingEdge)) {
117  addToMatching(matchingEdge);
118  }
119  }
120 #ifdef OGDF_DEBUG
121  for (node v : m_graph.nodes) {
122  lout() << "initial y for node " << v << ": " << y(v) << std::endl;
123  edge e = matching(v);
124  if (e && e->source() == v) {
125  OGDF_ASSERT(e == matching(e->target()));
127  lout() << "initial matching edge: " << e << std::endl;
128  }
129  }
130  for (edge e : m_graph.edges) {
131  OGDF_ASSERT(getReducedWeight(e) >= 0);
132  if (isEqualityEdge(e)) {
133  OGDF_ASSERT(matching(e->source()) || matching(e->target()));
134  }
135  }
136 #endif
137  return true;
138  }
139 
141  node findParentInRepr(node v, node child = nullptr) {
142  node parent = m_repr[v->index()];
143  OGDF_ASSERT(parent != nullptr);
144  if (parent == v) {
145  return child;
146  } else {
147  node grandparent = findParentInRepr(parent, v);
148  m_shortcuts[v->index()] = grandparent;
149  return grandparent;
150  }
151  }
152 
155  for (auto entry : m_pseudonodes) {
156  delete entry.second;
157  }
158  m_pseudonodes.clear();
159  }
160 
161 public:
162 #ifdef OGDF_HEAVY_DEBUG
163  void getReducedEdgeWeights(std::unordered_map<edge, TWeight>& edges) {
164  for (edge e : m_graph.edges) {
165  if (repr(e->source()) == e->source() && repr(e->target()) == e->target()) {
166  edges[e] = getRealReducedWeight(e);
167  }
168  }
169  }
170 
171  void assertConsistentEdgeWeights(std::unordered_map<edge, TWeight>& edges) {
172  for (edge e : m_graph.edges) {
173  if (repr(e->source()) == e->source() && repr(e->target()) == e->target()
174  && edges.find(e) != edges.end()) {
175  OGDF_ASSERT(edges[e] == getRealReducedWeight(e));
176  }
177  }
178  }
179 #endif
180 
185  BlossomHelper(bool greedyInit) : m_greedyInit(greedyInit) { }
186 
188 
191  template<class WeightContainer>
192  bool init(const Graph& graph, const WeightContainer& weights) {
193  if (graph.numberOfNodes() % 2 == 1) {
194  return false;
195  }
196  m_graph.clear();
197  m_graph.init(graph);
198  m_c.init(m_graph);
199  NodeArray<TWeight> minY(m_graph, -1);
200  for (edge e : m_graph.edges) {
201  if (e->isSelfLoop()) {
202  throw std::runtime_error("Self-loops are not supported.");
203  }
204  // copy initial edge costs from original edge, multiplied by WEIGHT_FACTOR
205  TWeight weight = WEIGHT_FACTOR * getWeight<TWeight>(m_graph.original(e), weights);
206  m_c[e] = weight;
207  // init dual variables for all nodes
208  auto halfWeight = weight / 2;
209  for (node v : e->nodes()) {
210  if (minY[v] == -1 || m_eps.less(halfWeight, minY[v])) {
211  minY[v] = halfWeight;
212  }
213  }
214  }
215  m_matching.init(m_graph, nullptr);
217  m_repr.clear();
218  m_shortcuts.clear();
219  for (node v : m_graph.nodes) {
220  m_repr.push_back(v);
221  m_shortcuts.push_back(v);
222  }
223  return initDualSolution(minY);
224  }
225 
226  /* Getters */
227 
229 
231  TWeight& c(edge e) { return m_c[e]; }
232 
234  TWeight& y(node v) { return m_y[v]; }
235 
237 
239  edge& matching(node v) { return m_matching[v]; }
240 
241  std::unordered_map<node, Pseudonode*>& pseudonodes() { return m_pseudonodes; }
242 
245 
247  bool isPseudonode(node v) { return pseudonode(v) != nullptr; }
248 
249  /* End of getters */
250 
253  node parent = m_repr[v->index()];
254  if (parent == v || parent == nullptr) {
255  return v;
256  }
257  node child = reprChild(v);
258  parent = m_repr[child->index()];
259  if (parent == nullptr) {
260  return child;
261  } else {
262  return parent;
263  }
264  }
265 
268  node shortcut = m_shortcuts[v->index()];
269  node parent = m_repr[shortcut->index()];
270  if (parent == shortcut || parent == nullptr) {
271  return findParentInRepr(v);
272  } else {
273  parent = findParentInRepr(shortcut);
274  m_shortcuts[v->index()] = parent;
275  return parent;
276  }
277  }
278 
282  m_repr.push_back(pseudonode->graphNode);
283  m_shortcuts.push_back(pseudonode->graphNode);
285  for (node v : pseudonode->cycle->nodes()) {
286  m_repr[v->index()] = pseudonode->graphNode;
287  }
288  }
289 
292  node theNode = pseudonode->graphNode;
293  m_pseudonodes.erase(theNode);
294  m_matching[theNode] = nullptr;
295  m_repr[theNode->index()] = nullptr;
296  m_graph.delNode(theNode);
297  for (node v : pseudonode->cycle->nodes()) {
298  m_repr[v->index()] = v;
299  }
300  }
301 
304  m_repr[pseudonode->graphNode->index()] = nullptr;
305  for (node v : pseudonode->cycle->nodes()) {
306  m_repr[v->index()] = v;
307  }
308  }
309 
311  node getBaseNode(edge e, node v) { return getBaseNodes(e, v).first; }
312 
314  node getOppositeBaseNode(edge e, node v) { return getBaseNodes(e, v).second; }
315 
317  std::pair<node, node> getBaseNodes(edge e, node v = nullptr) {
318  edge eOrig = m_graph.original(e);
319  node source = m_graph.copy(eOrig->source());
320  node target = m_graph.copy(eOrig->target());
321  if (v != nullptr && repr(target) == v) {
322  return {target, source};
323  } else {
324  OGDF_ASSERT(v == nullptr || repr(source) == v);
325  return {source, target};
326  }
327  }
328 
330  bool isZero(TWeight x) { return m_eps.equal(x, (TWeight)0); }
331 
333  TWeight getReducedWeight(edge e) { return c(e) - y(e->source()) - y(e->target()); }
334 
336  virtual TWeight getRealReducedWeight(edge e) { return getReducedWeight(e); }
337 
339  virtual bool isEqualityEdge(edge e) { return isZero(getRealReducedWeight(e)); }
340 
342  bool isZeroCostNode(node v) { return isZero(y(v)); }
343 
346  void getOriginalMatching(std::unordered_set<edge>& matching) {
347  // Extend matching in the copy of the graph with the edges in the contracted cycles
348  std::vector<Pseudonode*> stack;
349  for (auto entry : m_pseudonodes) {
350  if (repr(entry.first) == entry.first) {
351  stack.push_back(entry.second);
352  }
353  }
354 
355  while (!stack.empty()) {
356  auto pseudonode = stack.back();
357  stack.pop_back();
358  node graphNode = pseudonode->graphNode;
359  auto cycle = pseudonode->cycle;
360  // Find the edge entering the cycle
361  edge matchingEdge = m_matching[graphNode];
362  // Find the node in the cycle where the matching edge enters
363  node matchedNodeInner = getBaseNode(matchingEdge, graphNode);
364  node matchedNode = reprChild(matchedNodeInner);
365  // Set the matching edge of the node in case it is a pseudonode and also needs to be
366  // expanded later. This breaks the invariant that m_matching always contains exactly the two
367  // endpoints pointing to the same edge, but since this is the last step of the algorithm, it
368  // doesn't matter.
369  m_matching[matchedNode] = matchingEdge;
370  auto startIndex = cycle->indexOf(matchedNode);
371  auto edges = cycle->edgeOrder();
372  // Iterate the edges and add every second one from the perspective of the matchedNode to the
373  // matching. Since startIndex is the index of the edge _before_ the matchedNode, we start at
374  // index 2.
375  for (size_t i = 2; i < edges.size(); i += 2) {
376  edge e = edges[(startIndex + i) % edges.size()];
377  addToMatching(e);
378  }
379  // Also expand all children
380  for (node v : cycle->nodes()) {
381  if (auto child = this->pseudonode(v)) {
382  stack.push_back(child);
383  }
384  }
386  }
387  // Add all respective edges of the original graph to the output parameter
388  for (edge e : m_matching) {
389  matching.insert(m_graph.original(e));
390  }
391  }
392 
394  void addToMatching(edge e) { m_matching[e->source()] = m_matching[e->target()] = e; }
395 };
396 
397 }
398 }
ogdf::matching_blossom::BlossomHelper::m_greedyInit
bool m_greedyInit
Whether or not to use the greedy initialization.
Definition: BlossomHelper.h:58
ogdf
The namespace for all OGDF objects.
Definition: AugmentationModule.h:36
ogdf::matching_blossom::BlossomHelper::m_matching
NodeArray< edge > m_matching
The current matching, mapping both endpoints to the corresponding matching edge.
Definition: BlossomHelper.h:68
ogdf::matching_blossom::BlossomHelper::m_eps
EpsilonTest m_eps
The epsilon test used for floating point comparisons.
Definition: BlossomHelper.h:80
ogdf::matching_blossom::Pseudonode
Helper class representing a pseudonode in the Blossom algorithm.
Definition: Pseudonode.h:45
ogdf::matching_blossom::BlossomHelper::getReducedWeight
TWeight getReducedWeight(edge e)
Returns the reduced weight of e, taking into account the y values of the endpoints.
Definition: BlossomHelper.h:333
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::matching_blossom::BlossomHelper::graph
GraphCopySimple & graph()
Definition: BlossomHelper.h:228
ogdf::EpsilonTest
Definition: EpsilonTest.h:41
ogdf::GraphCopySimple::copy
edge copy(edge e) const override
Returns the edge in the graph copy corresponding to e.
Definition: GraphCopy.h:288
OGDF_ASSERT
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition: basic.h:54
ogdf::matching_blossom::BlossomHelper::BlossomHelper
BlossomHelper(bool greedyInit)
Construct a new Blossom V Helper object.
Definition: BlossomHelper.h:185
ogdf::matching_blossom::BlossomHelper::m_graph
GraphCopySimple m_graph
A copy of the graph to work on.
Definition: BlossomHelper.h:61
ogdf::NodeElement::index
int index() const
Returns the (unique) node index.
Definition: Graph_d.h:267
ogdf::matching_blossom::BlossomHelper::pseudonodes
std::unordered_map< node, Pseudonode * > & pseudonodes()
Definition: BlossomHelper.h:241
ogdf::matching_blossom::BlossomHelper::m_y
NodeArray< TWeight > m_y
Definition: BlossomHelper.h:65
ogdf::matching_blossom::BlossomHelper::addPseudonode
void addPseudonode(Pseudonode *pseudonode)
Adds a pseudonode to the current representation structure.
Definition: BlossomHelper.h:280
ogdf::matching_blossom::BlossomHelper::pseudonode
Pseudonode * pseudonode(node v)
Returns the pseudonode corresponding to v, or nullptr if v is not a pseudonode.
Definition: BlossomHelper.h:244
ogdf::matching_blossom::BlossomHelper::getBaseNodes
std::pair< node, node > getBaseNodes(edge e, node v=nullptr)
Return both original end points of e where the first end point is currently represented by v.
Definition: BlossomHelper.h:317
ogdf::matching_blossom::BlossomHelper::y
TWeight & y(node v)
Returns the base y value of v.
Definition: BlossomHelper.h:234
ogdf::GraphCopySimple
Copies of graphs with mapping between nodes and edges.
Definition: GraphCopy.h:254
ogdf::matching_blossom::BlossomHelper
Helper class for the blossom matching algorithms.
Definition: BlossomHelper.h:55
ogdf::matching_blossom::BlossomHelper::deletePseudonodes
void deletePseudonodes()
Deletes all pseudonodes.
Definition: BlossomHelper.h:154
ogdf::matching_blossom::Cycle::nodes
const std::unordered_set< node > & nodes()
ogdf::matching_blossom::BlossomHelper::getOppositeBaseNode
node getOppositeBaseNode(edge e, node v)
Returns the original end point of e where the other end point is currently represented by v.
Definition: BlossomHelper.h:314
ogdf::matching_blossom::BlossomHelper::addToMatching
void addToMatching(edge e)
Adds the edge e to the matching.
Definition: BlossomHelper.h:394
ogdf::EpsilonTest::less
std::enable_if< std::is_integral< T >::value, bool >::type less(const T &x, const T &y) const
Compare if x is LESS than y for integral types.
Definition: EpsilonTest.h:57
ogdf::matching_blossom::BlossomHelper::isZero
bool isZero(TWeight x)
Helper function to determine whether a floating point value is 0.
Definition: BlossomHelper.h:330
Logger.h
Contains logging functionality.
ogdf::matching_blossom::BlossomHelper::expandRepr
void expandRepr(Pseudonode *pseudonode)
Expands a pseudonode in the current representation structure.
Definition: BlossomHelper.h:303
ogdf::matching_blossom::BlossomHelper::m_pseudonodes
std::unordered_map< node, Pseudonode * > m_pseudonodes
A mapping of all pseudonodes in the graph.
Definition: BlossomHelper.h:71
ogdf::edge
EdgeElement * edge
The type of edges.
Definition: Graph_d.h:67
ogdf::GraphCopyBase::init
void init(const Graph &G)
Re-initializes the copy using G, creating copies for all nodes and edges in G.
Definition: GraphCopy.h:66
ogdf::matching_blossom::BlossomHelper::isZeroCostNode
bool isZeroCostNode(node v)
Checks whether v is a zero-cost node, i.e. the y value is 0.
Definition: BlossomHelper.h:342
ogdf::Graph::nodes
internal::GraphObjectContainer< NodeElement > nodes
The container containing all node objects.
Definition: Graph_d.h:924
ogdf::EdgeElement::isSelfLoop
bool isSelfLoop() const
Returns true iff the edge is a self-loop (source node = target node).
Definition: Graph_d.h:412
ogdf::NodeElement::degree
int degree() const
Returns the degree of the node (indegree + outdegree).
Definition: Graph_d.h:276
ogdf::Graph::numberOfNodes
int numberOfNodes() const
Returns the number of nodes in the graph.
Definition: Graph_d.h:974
ogdf::matching_blossom::BlossomHelper::repr
node repr(node v)
Returns the representative of v in the tree induced by the pseudonodes.
Definition: BlossomHelper.h:252
ogdf::matching_blossom::BlossomHelper::getRealReducedWeight
virtual TWeight getRealReducedWeight(edge e)
Returns the real reduced weight. Can be overridden by subclasses to consider additional factors.
Definition: BlossomHelper.h:336
ogdf::NodeElement::adjEntries
internal::GraphObjectContainer< AdjElement > adjEntries
The container containing all entries in the adjacency list of this node.
Definition: Graph_d.h:264
ogdf::matching_blossom::BlossomHelper::WEIGHT_FACTOR
static const int WEIGHT_FACTOR
Definition: BlossomHelper.h:83
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::BlossomHelper::initDualSolution
bool initDualSolution(NodeArray< TWeight > &minY)
Initializes the dual solution with the given y values.
Definition: BlossomHelper.h:86
ogdf::EdgeElement::source
node source() const
Returns the source node of the edge.
Definition: Graph_d.h:391
GraphCopy.h
Declaration of graph copy classes.
ogdf::matching_blossom::BlossomHelper::removePseudonode
void removePseudonode(Pseudonode *pseudonode)
Removes a pseudonode from the current representation structure.
Definition: BlossomHelper.h:291
ogdf::internal::GraphRegisteredArray
RegisteredArray for nodes, edges and adjEntries of a graph.
Definition: Graph_d.h:651
ogdf::GraphCopyBase::delNode
void delNode(node v) override
Removes node v.
Definition: GraphCopy.h:200
ogdf::Graph
Data type for general directed graphs (adjacency list representation).
Definition: Graph_d.h:862
ogdf::matching_blossom::tryGetPointerFromMap
V * tryGetPointerFromMap(const std::unordered_map< K, V * > &map, const K &key)
Return the pointer belonging to key key int the given map map, or nullptr if key does not exist.
Definition: utils.h:51
ogdf::matching_blossom::Pseudonode::graphNode
node graphNode
The node in the graph that this pseudonode is linked to.
Definition: Pseudonode.h:90
ogdf::EpsilonTest::leq
std::enable_if< std::is_integral< T >::value, bool >::type leq(const T &x, const T &y) const
Compare if x is LEQ than y for integral types.
Definition: EpsilonTest.h:83
NodeArray.h
Declaration and implementation of NodeArray class.
ogdf::matching_blossom::BlossomHelper::getOriginalMatching
void getOriginalMatching(std::unordered_set< edge > &matching)
Fills matching with the original edges which correspond to the edges in m_matching after expanding it...
Definition: BlossomHelper.h:346
ogdf::GraphCopySimple::clear
void clear() override
Removes all nodes and edges from this copy but does not break the link with the original graph.
ogdf::matching_blossom::BlossomHelper::init
bool init(const Graph &graph, const WeightContainer &weights)
Reinitialize the helper class with a new graph and edge weights. Resets all helper members....
Definition: BlossomHelper.h:192
ogdf::matching_blossom::BlossomHelper::m_c
EdgeArray< TWeight > m_c
LP-induced data used by the algorithm.
Definition: BlossomHelper.h:64
ogdf::EpsilonTest::equal
std::enable_if< std::is_integral< T >::value, bool >::type equal(const T &x, const T &y) const
Compare if x is EQUAL to y for integral types.
Definition: EpsilonTest.h:109
ogdf::matching_blossom::BlossomHelper::isEqualityEdge
virtual bool isEqualityEdge(edge e)
Checks whether e is an equality edge, i.e. the reduced weight is 0.
Definition: BlossomHelper.h:339
ogdf::matching_blossom::BlossomHelper::m_shortcuts
std::vector< node > m_shortcuts
A shortcut array for the tree, mapping each node index to the penultimate node.
Definition: BlossomHelper.h:77
ogdf::Graph::edges
internal::GraphObjectContainer< EdgeElement > edges
The container containing all edge objects.
Definition: Graph_d.h:927
ogdf::matching_blossom::BlossomHelper::m_repr
std::vector< node > m_repr
The tree induced by the pseudonodes, mapping each node index to its parent.
Definition: BlossomHelper.h:74
ogdf::matching_blossom::BlossomHelper::reprChild
node reprChild(node v)
Returns the child of the representative of v in the tree induced by the pseudonodes.
Definition: BlossomHelper.h:267
ogdf::matching_blossom::BlossomHelper::findParentInRepr
node findParentInRepr(node v, node child=nullptr)
Finds the parent of v in the tree induced by the pseudonodes.
Definition: BlossomHelper.h:141
ogdf::matching_blossom::BlossomHelper::matching
NodeArray< edge > & matching()
Definition: BlossomHelper.h:236
utils.h
Utility functions and classes regarding map access and iteration.
ogdf::matching_blossom::BlossomHelper::matching
edge & matching(node v)
Returns the matching edge of v, or nullptr if v is not matched.
Definition: BlossomHelper.h:239
ogdf::EdgeElement
Class for the representation of edges.
Definition: Graph_d.h:356
ogdf::matching_blossom::BlossomHelper::c
TWeight & c(edge e)
Returns the base edge weight of e.
Definition: BlossomHelper.h:231
ogdf::matching_blossom::BlossomHelper::getBaseNode
node getBaseNode(edge e, node v)
Returns the original end point of e which is currently represented by v.
Definition: BlossomHelper.h:311
ogdf::Logger
Centralized global and local logging facility working on streams like std::cout.
Definition: Logger.h:100
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::EdgeElement::target
node target() const
Returns the target node of the edge.
Definition: Graph_d.h:394
ogdf::NodeElement
Class for the representation of nodes.
Definition: Graph_d.h:233
ogdf::matching_blossom::BlossomHelper::isPseudonode
bool isPseudonode(node v)
Checks whether v is a pseudonode.
Definition: BlossomHelper.h:247
ogdf::GraphCopyBase::original
const Graph & original() const
Returns a reference to the original graph.
Definition: GraphCopy.h:98
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
ogdf::matching_blossom::BlossomHelper::~BlossomHelper
~BlossomHelper()
Definition: BlossomHelper.h:187