53#include <initializer_list>
57#include <unordered_map>
58#include <unordered_set>
63template<
typename TWeight>
64class MatchingBlossomV;
68#define OGDF_BLOSSOMV_PRINT_STATS
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)
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)
91template<
typename TWeight>
102#ifdef OGDF_BLOSSOMV_PRINT_STATS
113 long long ms() {
return time / 1000000; }
117 std::unordered_map<std::string, stats>
m_stats;
120 std::chrono::high_resolution_clock::time_point
now() {
121 return std::chrono::high_resolution_clock::now();
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();
145#ifdef OGDF_HEAVY_DEBUG
147 void assertConsistency() {
151 auto tree = auxNode->tree();
163 if (v !=
tree.root()) {
173 for (
auto adj : auxNode->graphNode()->adjEntries) {
178 int matchedNodes = 0;
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);
195 == (
size_t)
m_helper.graph().numberOfNodes());
197 auto checkAllPQs = [&](
edge e, BlossomPQ<edge, TWeight>* shouldContain =
nullptr) {
199 for (
auto* pq : {&auxNode->evenFreeEdges(), &auxNode->evenEvenEdges()}) {
200 OGDF_ASSERT(pq->contains(e) == (pq == shouldContain));
204 for (
auto* pq : {&auxEdge->evenEvenEdges(), &auxEdge->evenOddEdges(),
205 &auxEdge->oddEvenEdges()}) {
206 OGDF_ASSERT(pq->contains(e) == (pq == shouldContain));
209 if (shouldContain !=
nullptr) {
214 bool sourceInGraph =
m_helper.repr(e->source()) == e->source();
215 bool targetInGraph =
m_helper.repr(e->target()) == e->target();
217 if (!sourceInGraph) {
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);
229 if (sourceNode ==
nullptr && targetNode ==
nullptr) {
232 }
else if (sourceNode ==
nullptr) {
234 checkAllPQs(e, &targetNode->evenFreeEdges());
236 }
else if (targetNode ==
nullptr) {
238 checkAllPQs(e, &sourceNode->evenFreeEdges());
240 }
else if (sourceNode == targetNode) {
241 if (sourceIsEven && targetIsEven) {
242 checkAllPQs(e, &sourceNode->evenEvenEdges());
247 if (!sourceIsEven && !targetIsEven) {
251 sourceNode->graphNode(), targetNode->graphNode());
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));
259 checkAllPQs(e, &auxEdge->evenOddEdgesFromPerspective(targetNode));
264 for (
auto entry :
m_helper.pseudonodes()) {
265 if (
m_helper.repr(entry.first) == entry.first) {
267 auto auxNode =
m_auxGraph.treeAuxNode(entry.first);
269 OGDF_ASSERT(other->oddPseudonodes().contains(entry.first)
270 == (other == auxNode && auxNode->tree().isOdd(entry.first)));
287 std::unordered_set<edge>& matching) {
288 return _doCall(G, weights, matching);
296 template<
class WeightContainer>
297 bool _doCall(
const Graph& G,
const WeightContainer& weights, std::unordered_set<edge>& matching) {
299 lout() <<
"start init" << std::endl;
300#ifdef OGDF_BLOSSOMV_PRINT_STATS
308 lout() <<
"finish init" << std::endl;
309#ifdef OGDF_BLOSSOMV_PRINT_STATS
310 louth() <<
"Trees: " <<
m_auxGraph.graph().numberOfNodes() << std::endl;
322#ifdef OGDF_BLOSSOMV_PRINT_STATS
323 std::chrono::high_resolution_clock::time_point last_path, last_four_paths;
326 while (p <
m_auxGraph.graph().numberOfNodes()) {
331 while (
m_auxGraph.graph().numberOfNodes() > 0) {
332#ifdef OGDF_HEAVY_DEBUG
335#ifdef OGDF_BLOSSOMV_PRINT_STATS
336 if (
m_auxGraph.graph().numberOfNodes() == p / 2) {
338 lout() <<
"." << p << std::flush;
342 last_four_paths =
now();
350 lout() <<
"found matching" << std::endl;
351#ifdef OGDF_BLOSSOMV_PRINT_STATS
355 m_helper.getOriginalMatching(matching);
357#ifdef OGDF_BLOSSOMV_PRINT_STATS
358 m_stats[
"extraLastPathTime"].add(
end(last_path));
359 m_stats[
"extraLastFourPathsTime"].add(
end(last_four_paths));
367#ifdef OGDF_HEAVY_DEBUG
368 std::unordered_map<edge, TWeight> edgeWeights;
369 m_helper.getReducedEdgeWeights(edgeWeights);
384#ifdef OGDF_HEAVY_DEBUG
385 m_helper.assertConsistentEdgeWeights(edgeWeights);
412 if (minEdge !=
nullptr) {
430 if (minEdge !=
nullptr) {
447 if (minEdge !=
nullptr) {
464 if (minNode !=
nullptr) {
479 TWeight delta, otherDelta;
485 std::vector<std::unordered_set<node>> components;
487 for (
auto& component : components) {
488 delta = infinity<TWeight>();
490 for (
node v : component) {
493 delta = std::min(delta,
498 for (
auto& adj : v->adjEntries) {
501 if (component.find(w) != component.end()) {
504 delta = std::min(delta,
508 otherDelta = deltas[w];
510 delta = std::min(delta,
515 if (!evenOddPQ.empty()) {
516 delta = std::min(delta,
517 m_helper.getRealTopPriority(evenOddPQ) + otherDelta);
527 if (delta > 0 && delta < infinity<TWeight>()) {
528 for (
auto& v : component) {
536 delta = deltas[auxNode->graphNode()];
539 auxNode->addDelta(delta);
541 lout() <<
"Delta for " << auxNode->tree().root() <<
": " << delta << std::endl;
551 for (
auto augmentationEdgeNode : augmentationEdge->
nodes()) {
552 auto auxNode =
m_auxGraph.treeAuxNode(augmentationEdgeNode);
553 auto oppositeAuxNode =
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)}) {
565 edgesToUpdate.push_back(e);
570 auxNode->tree().augmentMatching(augmentationEdge);
575 for (
edge e : edgesToUpdate) {
577 an->addEvenFreeEdge(e);
581 lout() <<
"Matching augmented with " << augmentationEdge << std::endl;
587 auto auxNode =
m_auxGraph.auxNodeForEdge(newEdge);
588 auto&
tree = auxNode->tree();
594 tree.grow(u, newEdge, matchingEdge);
604 auxNode->addOddPseudonode(v);
607 node x = adj->twinNode();
608 edge e = adj->theEdge();
610 if (xAuxNode !=
nullptr && xAuxNode->tree().isEven(x)) {
612 xAuxNode->evenFreeEdges().remove(e);
614 if (xAuxNode != auxNode) {
615 m_auxGraph.assertCurrentEdge(xAuxNode, auxNode)
616 ->addEvenOddEdgeFromPerspective(e, xAuxNode);
620 edge augmentationEdge =
nullptr;
622 node x = adj->twinNode();
623 edge e = adj->theEdge();
625 if (xAuxNode ==
nullptr) {
627 auxNode->addEvenFreeEdge(e);
628 }
else if (xAuxNode == auxNode) {
630 if (
tree.isEven(x)) {
631 auxNode->evenFreeEdges().remove(e);
632 auxNode->addEvenEvenEdge(e);
635 auto auxEdge =
m_auxGraph.assertCurrentEdge(xAuxNode, auxNode);
636 auto xTree = xAuxNode->tree();
637 if (xTree.isOdd(x)) {
638 auxEdge->addEvenOddEdgeFromPerspective(e, auxNode);
640 xAuxNode->evenFreeEdges().remove(e);
641 auxEdge->addEvenEvenEdge(e);
642 if (augmentationEdge ==
nullptr &&
m_helper.isEqualityEdge(e)) {
643 augmentationEdge = e;
649 lout() <<
"Tree augmented with " << newEdge << std::endl;
650 if (augmentationEdge !=
nullptr) {
659 auto auxNode =
m_auxGraph.auxNodeForEdge(cycleEdge);
660 auto&
tree = auxNode->tree();
665 std::vector<edge> oddEdges;
666 std::unordered_map<node, edge> bestEdges;
667 edge augmentationEdge =
nullptr;
669 bool isOdd =
tree.isOdd(u);
670 for (
auto adj : u->adjEntries) {
671 edge e = adj->theEdge();
672 node v = adj->twinNode();
675 oddEdges.push_back(e);
680 auxNode->evenEvenEdges().remove(e);
684 if (isOdd &&
m_helper.isPseudonode(u)) {
685 auxNode->oddPseudonodes().remove(u);
698 std::vector<std::tuple<edge, bool>> selfLoops;
706 m_helper.y(newNode) = -auxNode->delta();
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);
715 hiddenEdges.insert(e);
717 if (other ==
nullptr) {
719 auxNode->evenFreeEdges().remove(e);
721 }
else if (other == auxNode) {
722 if (isEven &&
tree.isEven(v)) {
723 auxNode->evenEvenEdges().remove(e);
726 if (isEven && other->tree().isEven(v)) {
727 other->currentEdge()->evenEvenEdges().remove(e);
729 if (isEven && other->tree().isOdd(v)) {
730 other->currentEdge()->evenOddEdgesFromPerspective(auxNode).remove(e);
732 if (!isEven && other->tree().isEven(v)) {
733 other->currentEdge()->evenOddEdgesFromPerspective(other).remove(e);
739 for (
edge e : oddEdges) {
740 if (hiddenEdges.find(e) != hiddenEdges.end()) {
743 node v = e->opposite(newNode);
745 if (other ==
nullptr) {
747 auxNode->addEvenFreeEdge(e);
748 }
else if (other->tree().isEven(v)) {
749 if (other == auxNode) {
750 auxNode->addEvenEvenEdge(e);
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;
761 }
else if (other != auxNode) {
762 m_auxGraph.assertCurrentEdge(other, auxNode)->addEvenOddEdgeFromPerspective(e, auxNode);
768 lout() << std::endl <<
"Shrank at " << cycleEdge <<
" into " << newNode << std::endl;
769 if (augmentationEdge !=
nullptr) {
779 auto&
tree = auxNode->tree();
780 auto cycle = pseudonode->
cycle;
782 tree.expand(pseudonode);
785 for (
node u : cycle->nodes()) {
786 if (
tree.contains(u)) {
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();
800 if (other ==
nullptr) {
802 auxNode->addEvenFreeEdge(e);
803 }
else if (other == auxNode) {
807 if (
tree.isEven(v) && (!cycle->contains(v) || e->
source() == v)) {
808 auxNode->addEvenEvenEdge(e);
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);
816 auxEdge->addEvenEvenEdge(e);
817 if (augmentationEdge ==
nullptr &&
m_helper.isEqualityEdge(e)) {
818 augmentationEdge = e;
821 auxEdge->addEvenOddEdgeFromPerspective(e, auxNode);
825 }
else if (
tree.isOdd(u)) {
827 auxNode->addOddPseudonode(u);
829 for (
auto adj : u->adjEntries) {
830 edge e = adj->theEdge();
831 node v = adj->twinNode();
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);
842 for (
auto adj : u->adjEntries) {
843 edge e = adj->theEdge();
844 node v = adj->twinNode();
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);
854 if (!cycle->contains(v)) {
855 other->addEvenFreeEdge(e);
863 lout() << std::endl <<
"Expanded " << nodeIndex << std::endl;
864 if (augmentationEdge !=
nullptr) {
870#ifdef OGDF_BLOSSOMV_PRINT_STATS
874 for (std::string key : {
"initialize",
"augment",
"grow",
"shrink",
"expand",
"dualChange",
875 "findAugment",
"findGrow",
"findShrink",
"findExpand"}) {
879 std::vector<std::string> extraKeys;
880 std::vector<std::string> otherKeys;
882 if (entry.first.substr(0, 5) ==
"extra") {
883 extraKeys.push_back(entry.first);
885 otherKeys.push_back(entry.first);
888 std::sort(extraKeys.begin(), extraKeys.end());
889 std::sort(otherKeys.begin(), otherKeys.end());
891 for (
auto key : otherKeys) {
894 louth() <<
"Total tracked time: " << total <<
" ms" << std::endl;
895 for (
auto key : extraKeys) {
902 int max = 0,
sum = 0, total = 0;
903 std::unordered_map<int, std::unordered_map<int, int>> edgeCount;
905 if (
m_helper.repr(e->source()) == e->source()
906 &&
m_helper.repr(e->target()) == e->target()) {
909 int i = std::min(e->source()->index(), e->target()->index());
910 int j = std::max(e->source()->index(), e->target()->index());
914 for (
auto iEntry : edgeCount) {
915 for (
auto jEntry : iEntry.second) {
916 int count = jEntry.second;
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;
Implementation of the auxiliary graph as well as the edges and nodes of it for the Blossom V algorith...
Utility class for the Blossom V algorithm.
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 class GraphAttributes which extends a Graph by additional attributes.
Decralation of GraphElement and GraphList classes.
Contains logging functionality.
#define OGDF_BLOSSOMV_ADD_STAT(stat)
#define OGDF_BLOSSOMV_END_NAMED_TIMER(timer, stat)
#define OGDF_BLOSSOMV_START_TIMER()
#define OGDF_BLOSSOMV_END_TIMER(stat)
#define OGDF_BLOSSOMV_START_NAMED_TIMER(timer)
Declaration of interface for matching algorithms.
An extension of the standard priority queue with additional features.
Helper structure representing a pseudonode in the Blossom algorithm.
Basic declarations, included by all source files.
Class for the representation of edges.
node opposite(node v) const
Returns the adjacent node different from v.
const Graph * graphOf() const
Returns the graph containing this node (debug only).
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.
node source() const
Returns the source node of the edge.
Stores additional attributes of a graph (like layout information).
const Graph & constGraph() const
Returns a reference to the associated graph.
Data type for general directed graphs (adjacency list representation).
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 ))).
std::ostream & lout(Level level=Level::Default, bool indent=true) const
stream for logging-output (local)
Implementation of the Blossom-V algorithm by Kolmogorov (2009) for Minimum Weight Perfect Matching.
bool primalChange()
Executes a primal change step.
MatchingBlossomV(bool greedyInit=true)
Construct a MatchingBlossomV instance.
long long end(std::chrono::high_resolution_clock::time_point start)
Get the time difference between start and now in nanoseconds.
AuxGraph< TWeight > m_auxGraph
bool findMatchingAugmentation(AuxNode< TWeight > *auxNode)
Finds and executes a matching augmentation in auxNode, if possible.
EpsilonTest m_eps
The epsilon test for floating point comparisons.
AuxNode< TWeight > * m_currentAuxNode
The current aux node in the main loop.
void expand(Pseudonode *pseudonode)
Expand the given pseudonode.
std::unordered_map< std::string, stats > m_stats
A mapping of all statistic names to their values.
bool findTreeAugmentation(AuxNode< TWeight > *auxNode)
Finds and executes a tree augmentation in auxNode, if possible.
bool findExpandablePseudonode(AuxNode< TWeight > *auxNode)
Finds and expands an odd pseudonode in auxNode, if possible.
void shrink(edge cycleEdge)
Shrink the odd cycle induced by cycleEdge and the tree determined by its endpoints.
void augment(edge augmentationEdge)
Augment the matching with augmentationEdge.
void printParallelEdgesStats()
Print additional statistics about parallel edges.
bool dualChange()
Executes a dual change step.
std::ostream & louth()
Helper function to log with high priority.
bool findShrinkableCycle(AuxNode< TWeight > *auxNode)
Finds and shrinks an odd cycle in auxNode, if possible.
std::ostream & lout(Level level=Level::Default, bool indent=true) const
stream for logging-output (local)
void grow(edge newEdge)
Augment the corresponding tree with augmentationEdge.
void printStatistics()
Print all statistics.
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.
BlossomVHelper< TWeight > m_helper
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.
bool doCall(const GraphAttributes &GA, std::unordered_set< edge > &matching)
Main call function for the algorithm with GraphAttrubtes as weight container.
bool findMatching(std::unordered_set< edge > &matching)
Main function of the algorithm.
void advanceCurrentAuxNode()
Helper function to advance the current aux node to the next one.
long long processStatisticEntry(const std::string &key)
Print a single statistics entry.
std::chrono::high_resolution_clock::time_point now()
Get the current time point.
Class for the representation of nodes.
internal::GraphObjectContainer< AdjElement > adjEntries
The container containing all entries in the adjacency list of this node.
int index() const
Returns the (unique) node index.
bool empty() const
Checks whether the queue is empty.
RegisteredArray for edges of a graph, specialized for EdgeArray<edge>.
RegisteredArray for nodes, edges and adjEntries of a graph.
EdgePQ & evenOddEdgesFromPerspective(AuxNode< TWeight > *auxNode)
Returns evenOddEdges or oddEvenEdges, depending on the perspective of auxNode (the even node).
NodePQ & oddPseudonodes()
void setCurrentEdge(AuxEdge< TWeight > *edge)
Sets the current edge of this tree to edge and update the iteration to the current one.
void pop()
Removes the topmost element from the queue.
Helper class for the blossom matching algorithms.
bool contains(node v)
Whether the cycle contains the node v or not.
const std::unordered_set< node > & nodes()
Helper class representing a pseudonode in the Blossom algorithm.
Cycle * cycle
The odd-length cycle that this pseudonode represents.
node graphNode
The node in the graph that this pseudonode is linked to.
Utility functions and classes regarding map access and iteration.
NodeElement * node
The type of nodes.
EdgeElement * edge
The type of edges.
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
The namespace for all OGDF objects.
@ tree
for every hyperedge e a minimal subcubic tree connecting all hypernodes incident with e together is a...
Structure to store statistics.