Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

Loading...
Searching...
No Matches
BlossomHelper.h
Go to the documentation of this file.
1
33#pragma once
34
36#include <ogdf/basic/Graph.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
55namespace ogdf {
56namespace matching_blossom {
57
59template<class TWeight>
60class BlossomHelper : private Logger {
61protected:
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) {
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
166public:
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
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();
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
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}
Utility class representing a cycle in the Blossom algorithm.
Compare floating point numbers with epsilons and integral numbers with normal compare operators.
Includes declaration of graph class.
Declaration of graph copy classes.
Decralation of GraphElement and GraphList classes.
Contains logging functionality.
Helper structure representing a pseudonode in the Blossom algorithm.
Basic declarations, included by all source files.
Class for the representation of edges.
Definition Graph_d.h:364
node target() const
Returns the target node of the edge.
Definition Graph_d.h:402
node source() const
Returns the source node of the edge.
Definition Graph_d.h:399
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
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
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.
void delNode(node v) override
Removes node v.
Definition GraphCopy.h:206
void init(const Graph &G)
Re-initializes the copy using G, creating copies for all nodes and edges in G.
Definition GraphCopy.h:72
const Graph & original() const
Returns a reference to the original graph.
Definition GraphCopy.h:104
Copies of graphs with mapping between nodes and edges.
Definition GraphCopy.h:260
void clear() override
Removes all nodes and edges from this copy but does not break the link with the original graph.
edge copy(edge e) const override
Returns the edge in the graph copy corresponding to e.
Definition GraphCopy.h:294
Data type for general directed graphs (adjacency list representation).
Definition Graph_d.h:866
int numberOfNodes() const
Returns the number of nodes in the graph.
Definition Graph_d.h:979
internal::GraphObjectContainer< NodeElement > nodes
The container containing all node objects.
Definition Graph_d.h:929
internal::GraphObjectContainer< EdgeElement > edges
The container containing all edge objects.
Definition Graph_d.h:932
Centralized global and local logging facility working on streams like std::cout.
Definition Logger.h:102
std::ostream & lout(Level level=Level::Default, bool indent=true) const
stream for logging-output (local)
Definition Logger.h:160
Class for the representation of nodes.
Definition Graph_d.h:241
int index() const
Returns the (unique) node index.
Definition Graph_d.h:275
void init(const Base *base=nullptr)
Reinitializes the array. Associates the array with the matching registry of base.
RegisteredArray for edges of a graph, specialized for EdgeArray<edge>.
Definition Graph_d.h:717
RegisteredArray for nodes, edges and adjEntries of a graph.
Definition Graph_d.h:659
Helper class for the blossom matching algorithms.
bool m_greedyInit
Whether or not to use the greedy initialization.
std::vector< node > m_shortcuts
A shortcut array for the tree, mapping each node index to the penultimate node.
EdgeArray< TWeight > m_c
LP-induced data used by the algorithm.
bool isZeroCostNode(node v)
Checks whether v is a zero-cost node, i.e. the y value is 0.
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.
TWeight getReducedWeight(edge e)
Returns the reduced weight of e, taking into account the y values of the endpoints.
TWeight & c(edge e)
Returns the base edge weight of e.
node reprChild(node v)
Returns the child of the representative of v in the tree induced by the pseudonodes.
node findParentInRepr(node v, node child=nullptr)
Finds the parent of v in the tree induced by the pseudonodes.
TWeight & y(node v)
Returns the base y value of v.
std::unordered_map< node, Pseudonode * > & pseudonodes()
void getOriginalMatching(std::unordered_set< edge > &matching)
Fills matching with the original edges which correspond to the edges in m_matching after expanding it...
std::unordered_map< node, Pseudonode * > m_pseudonodes
A mapping of all pseudonodes in the graph.
std::vector< node > m_repr
The tree induced by the pseudonodes, mapping each node index to its parent.
NodeArray< edge > m_matching
The current matching, mapping both endpoints to the corresponding matching edge.
GraphCopySimple m_graph
A copy of the graph to work on.
void addToMatching(edge e)
Adds the edge e to the matching.
void addPseudonode(Pseudonode *pseudonode)
Adds a pseudonode to the current representation structure.
void deletePseudonodes()
Deletes all pseudonodes.
EpsilonTest m_eps
The epsilon test used for floating point comparisons.
virtual bool isEqualityEdge(edge e)
Checks whether e is an equality edge, i.e. the reduced weight is 0.
node getOppositeBaseNode(edge e, node v)
Returns the original end point of e where the other end point is currently represented by v.
node repr(node v)
Returns the representative of v in the tree induced by the pseudonodes.
node getBaseNode(edge e, node v)
Returns the original end point of e which is currently represented by v.
bool isZero(TWeight x)
Helper function to determine whether a floating point value is 0.
void expandRepr(Pseudonode *pseudonode)
Expands a pseudonode in the current representation structure.
Pseudonode * pseudonode(node v)
Returns the pseudonode corresponding to v, or nullptr if v is not a pseudonode.
virtual TWeight getRealReducedWeight(edge e)
Returns the real reduced weight. Can be overridden by subclasses to consider additional factors.
edge & matching(node v)
Returns the matching edge of v, or nullptr if v is not matched.
bool initDualSolution(NodeArray< TWeight > &minY)
Initializes the dual solution with the given y values.
void removePseudonode(Pseudonode *pseudonode)
Removes a pseudonode from the current representation structure.
BlossomHelper(bool greedyInit)
Construct a new Blossom V Helper object.
bool isPseudonode(node v)
Checks whether v is a pseudonode.
bool init(const Graph &graph, const WeightContainer &weights)
Reinitialize the helper class with a new graph and edge weights. Resets all helper members....
const std::unordered_set< node > & nodes()
Helper class representing a pseudonode in the Blossom algorithm.
Definition Pseudonode.h:50
Cycle * cycle
The odd-length cycle that this pseudonode represents.
Definition Pseudonode.h:98
node graphNode
The node in the graph that this pseudonode is linked to.
Definition Pseudonode.h:95
Utility functions and classes regarding map access and iteration.
EdgeElement * edge
The type of edges.
Definition Graph_d.h:75
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition basic.h:52
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
The namespace for all OGDF objects.