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/EpsilonTest.h>
36 #include <ogdf/basic/Graph.h>
37 #include <ogdf/basic/GraphCopy.h>
38 #include <ogdf/basic/GraphList.h>
39 #include <ogdf/basic/Logger.h>
40 #include <ogdf/basic/basic.h>
44 
45 #include <array>
46 #include <cstddef>
47 #include <iostream>
48 #include <limits>
49 #include <stdexcept>
50 #include <unordered_map>
51 #include <unordered_set>
52 #include <utility>
53 #include <vector>
54 
55 namespace ogdf {
56 namespace matching_blossom {
57 
59 template<class TWeight>
60 class BlossomHelper : private Logger {
61 protected:
64 
67 
71 
74 
76  std::unordered_map<node, Pseudonode*> m_pseudonodes;
77 
79  std::vector<node> m_repr;
80 
82  std::vector<node> m_shortcuts;
83 
86 
87  // Multiply all integer weights by 4 to ensure that all primal and dual values stay integral.
88  static const int WEIGHT_FACTOR = std::numeric_limits<TWeight>::is_integer ? 4 : 1;
89 
92  // for _some_ reason, the array has to be initialized with 0, otherwise the algorithm fails
93  m_y.init(m_graph, 0);
94  for (node v : m_graph.nodes) {
95  if (v->degree() == 0) {
96  return false;
97  }
98  m_y[v] = minY[v];
99  }
100  if (!m_greedyInit) {
101  return true;
102  }
103  for (node v : m_graph.nodes) {
104  if (matching(v) != nullptr) {
105  continue;
106  }
107  edge matchingEdge = nullptr;
108  TWeight maxChange = infinity<TWeight>();
109  for (auto adj : v->adjEntries) {
110  edge e = adj->theEdge();
111  node w = adj->twinNode();
112  TWeight reducedWeight = getReducedWeight(e);
113  if (m_eps.leq(reducedWeight, maxChange)) {
114  maxChange = reducedWeight;
115  if (!matching(w)) {
116  matchingEdge = e;
117  }
118  }
119  }
120  y(v) += maxChange;
121  if (matchingEdge != nullptr && isEqualityEdge(matchingEdge)) {
122  addToMatching(matchingEdge);
123  }
124  }
125 #ifdef OGDF_DEBUG
126  for (node v : m_graph.nodes) {
127  lout() << "initial y for node " << v << ": " << y(v) << std::endl;
128  edge e = matching(v);
129  if (e && e->source() == v) {
130  OGDF_ASSERT(e == matching(e->target()));
132  lout() << "initial matching edge: " << e << std::endl;
133  }
134  }
135  for (edge e : m_graph.edges) {
136  OGDF_ASSERT(getReducedWeight(e) >= 0);
137  if (isEqualityEdge(e)) {
138  OGDF_ASSERT(matching(e->source()) || matching(e->target()));
139  }
140  }
141 #endif
142  return true;
143  }
144 
146  node findParentInRepr(node v, node child = nullptr) {
147  node parent = m_repr[v->index()];
148  OGDF_ASSERT(parent != nullptr);
149  if (parent == v) {
150  return child;
151  } else {
152  node grandparent = findParentInRepr(parent, v);
153  m_shortcuts[v->index()] = grandparent;
154  return grandparent;
155  }
156  }
157 
160  for (auto entry : m_pseudonodes) {
161  delete entry.second;
162  }
163  m_pseudonodes.clear();
164  }
165 
166 public:
167 #ifdef OGDF_HEAVY_DEBUG
168  void getReducedEdgeWeights(std::unordered_map<edge, TWeight>& edges) {
169  for (edge e : m_graph.edges) {
170  if (repr(e->source()) == e->source() && repr(e->target()) == e->target()) {
171  edges[e] = getRealReducedWeight(e);
172  }
173  }
174  }
175 
176  void assertConsistentEdgeWeights(std::unordered_map<edge, TWeight>& edges) {
177  for (edge e : m_graph.edges) {
178  if (repr(e->source()) == e->source() && repr(e->target()) == e->target()
179  && edges.find(e) != edges.end()) {
180  OGDF_ASSERT(edges[e] == getRealReducedWeight(e));
181  }
182  }
183  }
184 #endif
185 
190  BlossomHelper(bool greedyInit) : m_greedyInit(greedyInit) { }
191 
193 
196  template<class WeightContainer>
197  bool init(const Graph& graph, const WeightContainer& weights) {
198  if (graph.numberOfNodes() % 2 == 1) {
199  return false;
200  }
201  m_graph.clear();
202  m_graph.init(graph);
203  m_c.init(m_graph);
204  NodeArray<TWeight> minY(m_graph, -1);
205  for (edge e : m_graph.edges) {
206  if (e->isSelfLoop()) {
207  throw std::runtime_error("Self-loops are not supported.");
208  }
209  // copy initial edge costs from original edge, multiplied by WEIGHT_FACTOR
210  TWeight weight = WEIGHT_FACTOR * getWeight<TWeight>(m_graph.original(e), weights);
211  m_c[e] = weight;
212  // init dual variables for all nodes
213  auto halfWeight = weight / 2;
214  for (node v : e->nodes()) {
215  if (minY[v] == -1 || m_eps.less(halfWeight, minY[v])) {
216  minY[v] = halfWeight;
217  }
218  }
219  }
220  m_matching.init(m_graph, nullptr);
222  m_repr.clear();
223  m_shortcuts.clear();
224  for (node v : m_graph.nodes) {
225  m_repr.push_back(v);
226  m_shortcuts.push_back(v);
227  }
228  return initDualSolution(minY);
229  }
230 
231  /* Getters */
232 
234 
236  TWeight& c(edge e) { return m_c[e]; }
237 
239  TWeight& y(node v) { return m_y[v]; }
240 
242 
244  edge& matching(node v) { return m_matching[v]; }
245 
246  std::unordered_map<node, Pseudonode*>& pseudonodes() { return m_pseudonodes; }
247 
250 
252  bool isPseudonode(node v) { return pseudonode(v) != nullptr; }
253 
254  /* End of getters */
255 
258  node parent = m_repr[v->index()];
259  if (parent == v || parent == nullptr) {
260  return v;
261  }
262  node child = reprChild(v);
263  parent = m_repr[child->index()];
264  if (parent == nullptr) {
265  return child;
266  } else {
267  return parent;
268  }
269  }
270 
273  node shortcut = m_shortcuts[v->index()];
274  node parent = m_repr[shortcut->index()];
275  if (parent == shortcut || parent == nullptr) {
276  return findParentInRepr(v);
277  } else {
278  parent = findParentInRepr(shortcut);
279  m_shortcuts[v->index()] = parent;
280  return parent;
281  }
282  }
283 
287  m_repr.push_back(pseudonode->graphNode);
288  m_shortcuts.push_back(pseudonode->graphNode);
290  for (node v : pseudonode->cycle->nodes()) {
291  m_repr[v->index()] = pseudonode->graphNode;
292  }
293  }
294 
297  node theNode = pseudonode->graphNode;
298  m_pseudonodes.erase(theNode);
299  m_matching[theNode] = nullptr;
300  m_repr[theNode->index()] = nullptr;
301  m_graph.delNode(theNode);
302  for (node v : pseudonode->cycle->nodes()) {
303  m_repr[v->index()] = v;
304  }
305  }
306 
309  m_repr[pseudonode->graphNode->index()] = nullptr;
310  for (node v : pseudonode->cycle->nodes()) {
311  m_repr[v->index()] = v;
312  }
313  }
314 
316  node getBaseNode(edge e, node v) { return getBaseNodes(e, v).first; }
317 
319  node getOppositeBaseNode(edge e, node v) { return getBaseNodes(e, v).second; }
320 
322  std::pair<node, node> getBaseNodes(edge e, node v = nullptr) {
323  edge eOrig = m_graph.original(e);
324  node source = m_graph.copy(eOrig->source());
325  node target = m_graph.copy(eOrig->target());
326  if (v != nullptr && repr(target) == v) {
327  return {target, source};
328  } else {
329  OGDF_ASSERT(v == nullptr || repr(source) == v);
330  return {source, target};
331  }
332  }
333 
335  bool isZero(TWeight x) { return m_eps.equal(x, (TWeight)0); }
336 
338  TWeight getReducedWeight(edge e) { return c(e) - y(e->source()) - y(e->target()); }
339 
341  virtual TWeight getRealReducedWeight(edge e) { return getReducedWeight(e); }
342 
344  virtual bool isEqualityEdge(edge e) { return isZero(getRealReducedWeight(e)); }
345 
347  bool isZeroCostNode(node v) { return isZero(y(v)); }
348 
351  void getOriginalMatching(std::unordered_set<edge>& matching) {
352  // Extend matching in the copy of the graph with the edges in the contracted cycles
353  std::vector<Pseudonode*> stack;
354  for (auto entry : m_pseudonodes) {
355  if (repr(entry.first) == entry.first) {
356  stack.push_back(entry.second);
357  }
358  }
359 
360  while (!stack.empty()) {
361  auto pseudonode = stack.back();
362  stack.pop_back();
363  node graphNode = pseudonode->graphNode;
364  auto cycle = pseudonode->cycle;
365  // Find the edge entering the cycle
366  edge matchingEdge = m_matching[graphNode];
367  // Find the node in the cycle where the matching edge enters
368  node matchedNodeInner = getBaseNode(matchingEdge, graphNode);
369  node matchedNode = reprChild(matchedNodeInner);
370  // Set the matching edge of the node in case it is a pseudonode and also needs to be
371  // expanded later. This breaks the invariant that m_matching always contains exactly the two
372  // endpoints pointing to the same edge, but since this is the last step of the algorithm, it
373  // doesn't matter.
374  m_matching[matchedNode] = matchingEdge;
375  auto startIndex = cycle->indexOf(matchedNode);
376  auto edges = cycle->edgeOrder();
377  // Iterate the edges and add every second one from the perspective of the matchedNode to the
378  // matching. Since startIndex is the index of the edge _before_ the matchedNode, we start at
379  // index 2.
380  for (size_t i = 2; i < edges.size(); i += 2) {
381  edge e = edges[(startIndex + i) % edges.size()];
382  addToMatching(e);
383  }
384  // Also expand all children
385  for (node v : cycle->nodes()) {
386  if (auto child = this->pseudonode(v)) {
387  stack.push_back(child);
388  }
389  }
391  }
392  // Add all respective edges of the original graph to the output parameter
393  for (edge e : m_matching) {
394  matching.insert(m_graph.original(e));
395  }
396  }
397 
399  void addToMatching(edge e) { m_matching[e->source()] = m_matching[e->target()] = e; }
400 };
401 
402 }
403 }
ogdf::matching_blossom::BlossomHelper::m_greedyInit
bool m_greedyInit
Whether or not to use the greedy initialization.
Definition: BlossomHelper.h:63
ogdf
The namespace for all OGDF objects.
Definition: multilevelmixer.cpp:39
ogdf::matching_blossom::BlossomHelper::m_matching
NodeArray< edge > m_matching
The current matching, mapping both endpoints to the corresponding matching edge.
Definition: BlossomHelper.h:73
ogdf::matching_blossom::BlossomHelper::m_eps
EpsilonTest m_eps
The epsilon test used for floating point comparisons.
Definition: BlossomHelper.h:85
ogdf::matching_blossom::Pseudonode
Helper class representing a pseudonode in the Blossom algorithm.
Definition: Pseudonode.h:50
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:338
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:233
ogdf::EpsilonTest
Definition: EpsilonTest.h:39
ogdf::GraphCopySimple::copy
edge copy(edge e) const override
Returns the edge in the graph copy corresponding to e.
Definition: GraphCopy.h:295
OGDF_ASSERT
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition: basic.h:66
ogdf::matching_blossom::BlossomHelper::BlossomHelper
BlossomHelper(bool greedyInit)
Construct a new Blossom V Helper object.
Definition: BlossomHelper.h:190
ogdf::matching_blossom::BlossomHelper::m_graph
GraphCopySimple m_graph
A copy of the graph to work on.
Definition: BlossomHelper.h:66
ogdf::NodeElement::index
int index() const
Returns the (unique) node index.
Definition: Graph_d.h:274
ogdf::matching_blossom::BlossomHelper::pseudonodes
std::unordered_map< node, Pseudonode * > & pseudonodes()
Definition: BlossomHelper.h:246
ogdf::matching_blossom::BlossomHelper::m_y
NodeArray< TWeight > m_y
Definition: BlossomHelper.h:70
ogdf::matching_blossom::BlossomHelper::addPseudonode
void addPseudonode(Pseudonode *pseudonode)
Adds a pseudonode to the current representation structure.
Definition: BlossomHelper.h:285
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:249
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:322
ogdf::matching_blossom::BlossomHelper::y
TWeight & y(node v)
Returns the base y value of v.
Definition: BlossomHelper.h:239
ogdf::GraphCopySimple
Copies of graphs with mapping between nodes and edges.
Definition: GraphCopy.h:261
ogdf::matching_blossom::BlossomHelper::deletePseudonodes
void deletePseudonodes()
Deletes all pseudonodes.
Definition: BlossomHelper.h:159
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:319
ogdf::matching_blossom::BlossomHelper::addToMatching
void addToMatching(edge e)
Adds the edge e to the matching.
Definition: BlossomHelper.h:399
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:55
ogdf::matching_blossom::BlossomHelper::isZero
bool isZero(TWeight x)
Helper function to determine whether a floating point value is 0.
Definition: BlossomHelper.h:335
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:308
ogdf::matching_blossom::BlossomHelper::m_pseudonodes
std::unordered_map< node, Pseudonode * > m_pseudonodes
A mapping of all pseudonodes in the graph.
Definition: BlossomHelper.h:76
ogdf::edge
EdgeElement * edge
The type of edges.
Definition: Graph_d.h:74
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:73
Cycle.h
Utility class representing a cycle in the Blossom algorithm.
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:347
ogdf::Graph::nodes
internal::GraphObjectContainer< NodeElement > nodes
The container containing all node objects.
Definition: Graph_d.h:932
ogdf::EdgeElement::isSelfLoop
bool isSelfLoop() const
Returns true iff the edge is a self-loop (source node = target node).
Definition: Graph_d.h:419
ogdf::NodeElement::degree
int degree() const
Returns the degree of the node (indegree + outdegree).
Definition: Graph_d.h:283
ogdf::Graph::numberOfNodes
int numberOfNodes() const
Returns the number of nodes in the graph.
Definition: Graph_d.h:982
ogdf::matching_blossom::BlossomHelper::repr
node repr(node v)
Returns the representative of v in the tree induced by the pseudonodes.
Definition: BlossomHelper.h:257
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:341
GraphList.h
Decralation of GraphElement and GraphList classes.
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::BlossomHelper::WEIGHT_FACTOR
static const int WEIGHT_FACTOR
Definition: BlossomHelper.h:88
ogdf::matching_blossom::Pseudonode::cycle
Cycle * cycle
The odd-length cycle that this pseudonode represents.
Definition: Pseudonode.h:98
ogdf::matching_blossom::BlossomHelper::initDualSolution
bool initDualSolution(NodeArray< TWeight > &minY)
Initializes the dual solution with the given y values.
Definition: BlossomHelper.h:91
ogdf::EdgeElement::source
node source() const
Returns the source node of the edge.
Definition: Graph_d.h:398
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:296
ogdf::internal::GraphRegisteredArray
RegisteredArray for nodes, edges and adjEntries of a graph.
Definition: Graph_d.h:658
ogdf::GraphCopyBase::delNode
void delNode(node v) override
Removes node v.
Definition: GraphCopy.h:207
ogdf::Graph
Data type for general directed graphs (adjacency list representation).
Definition: Graph_d.h:869
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:54
ogdf::matching_blossom::Pseudonode::graphNode
node graphNode
The node in the graph that this pseudonode is linked to.
Definition: Pseudonode.h:95
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:81
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:351
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:197
ogdf::matching_blossom::BlossomHelper::m_c
EdgeArray< TWeight > m_c
LP-induced data used by the algorithm.
Definition: BlossomHelper.h:69
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:107
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:344
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:82
ogdf::Graph::edges
internal::GraphObjectContainer< EdgeElement > edges
The container containing all edge objects.
Definition: Graph_d.h:935
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:79
basic.h
Basic declarations, included by all source files.
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:272
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:146
ogdf::matching_blossom::BlossomHelper::matching
NodeArray< edge > & matching()
Definition: BlossomHelper.h:241
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:244
ogdf::EdgeElement
Class for the representation of edges.
Definition: Graph_d.h:363
ogdf::matching_blossom::BlossomHelper::c
TWeight & c(edge e)
Returns the base edge weight of e.
Definition: BlossomHelper.h:236
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:316
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::EdgeElement::target
node target() const
Returns the target node of the edge.
Definition: Graph_d.h:401
ogdf::NodeElement
Class for the representation of nodes.
Definition: Graph_d.h:240
ogdf::matching_blossom::BlossomHelper::isPseudonode
bool isPseudonode(node v)
Checks whether v is a pseudonode.
Definition: BlossomHelper.h:252
ogdf::GraphCopyBase::original
const Graph & original() const
Returns a reference to the original graph.
Definition: GraphCopy.h:105
ogdf::internal::EdgeArrayBase2
RegisteredArray for edges of a graph, specialized for EdgeArray<edge>.
Definition: Graph_d.h:716
ogdf::matching_blossom::BlossomHelper::~BlossomHelper
~BlossomHelper()
Definition: BlossomHelper.h:192