52 #include <initializer_list>
56 #include <unordered_map>
57 #include <unordered_set>
62 template<
typename TWeight>
67 #define OGDF_BLOSSOMV_PRINT_STATS
70 #ifdef OGDF_BLOSSOMV_PRINT_STATS
71 # define OGDF_BLOSSOMV_START_TIMER() OGDF_BLOSSOMV_START_NAMED_TIMER(__timestamp)
72 # define OGDF_BLOSSOMV_START_NAMED_TIMER(timer) auto timer = now()
73 # define OGDF_BLOSSOMV_END_TIMER(stat) OGDF_BLOSSOMV_END_NAMED_TIMER(__timestamp, stat)
74 # define OGDF_BLOSSOMV_END_NAMED_TIMER(timer, stat) m_stats[stat].add(end(timer))
75 # define OGDF_BLOSSOMV_ADD_STAT(stat) m_stats[stat].add(0)
77 # define OGDF_BLOSSOMV_START_TIMER()
78 # define OGDF_BLOSSOMV_START_NAMED_TIMER(timer)
79 # define OGDF_BLOSSOMV_END_TIMER(name)
80 # define OGDF_BLOSSOMV_END_NAMED_TIMER(timer, stat)
81 # define OGDF_BLOSSOMV_ADD_STAT(stat)
90 template<
typename TWeight>
91 class MatchingBlossomV :
public MatchingModule<TWeight> {
101 #ifdef OGDF_BLOSSOMV_PRINT_STATS
112 long long ms() {
return time / 1000000; }
116 std::unordered_map<std::string, stats>
m_stats;
119 std::chrono::high_resolution_clock::time_point
now() {
120 return std::chrono::high_resolution_clock::now();
124 long long end(std::chrono::high_resolution_clock::time_point start) {
125 auto end = std::chrono::high_resolution_clock::now();
126 return std::chrono::duration_cast<std::chrono::nanoseconds>(
end - start).count();
144 #ifdef OGDF_HEAVY_DEBUG
145 void assertConsistency() {
150 auto tree = auxNode->tree();
155 for (
node v : tree.oddNodes) {
162 if (v !=
tree.root()) {
172 for (
auto adj : auxNode->graphNode()->adjEntries) {
177 int matchedNodes = 0;
186 std::unordered_set<edge> hiddenEdges;
187 for (
auto entry :
m_helper.pseudonodes()) {
188 auto pseudonode = entry.second;
189 for (
edge e : pseudonode->cycle->edgeOrder()) {
190 hiddenEdges.insert(e);
194 == (
size_t)
m_helper.graph().numberOfNodes());
196 auto checkAllPQs = [&](
edge e, BlossomPQ<edge, TWeight>* shouldContain =
nullptr) {
198 for (
auto* pq : {&auxNode->evenFreeEdges(), &auxNode->evenEvenEdges()}) {
199 OGDF_ASSERT(pq->contains(e) == (pq == shouldContain));
203 for (
auto* pq : {&auxEdge->evenEvenEdges(), &auxEdge->evenOddEdges(),
204 &auxEdge->oddEvenEdges()}) {
205 OGDF_ASSERT(pq->contains(e) == (pq == shouldContain));
208 if (shouldContain !=
nullptr) {
213 bool sourceInGraph =
m_helper.repr(e->source()) == e->source();
214 bool targetInGraph =
m_helper.repr(e->target()) == e->target();
216 if (!sourceInGraph) {
221 auto sourceNode =
m_auxGraph.treeAuxNode(e->source());
222 auto targetNode =
m_auxGraph.treeAuxNode(e->target());
223 bool sourceIsEven = sourceNode && sourceNode->tree().isEven(e->source());
224 bool targetIsEven = targetNode && targetNode->tree().isEven(e->target());
225 if (hiddenEdges.find(e) != hiddenEdges.end()) {
226 OGDF_ASSERT(sourceNode ==
nullptr && targetNode ==
nullptr);
228 if (sourceNode ==
nullptr && targetNode ==
nullptr) {
231 }
else if (sourceNode ==
nullptr) {
233 checkAllPQs(e, &targetNode->evenFreeEdges());
235 }
else if (targetNode ==
nullptr) {
237 checkAllPQs(e, &sourceNode->evenFreeEdges());
239 }
else if (sourceNode == targetNode) {
240 if (sourceIsEven && targetIsEven) {
241 checkAllPQs(e, &sourceNode->evenEvenEdges());
246 if (!sourceIsEven && !targetIsEven) {
250 sourceNode->graphNode(), targetNode->graphNode());
252 auto auxEdge =
m_auxGraph.auxEdge(graphAuxEdge);
253 if (sourceIsEven && targetIsEven) {
254 checkAllPQs(e, &auxEdge->evenEvenEdges());
255 }
else if (sourceIsEven) {
256 checkAllPQs(e, &auxEdge->evenOddEdgesFromPerspective(sourceNode));
258 checkAllPQs(e, &auxEdge->evenOddEdgesFromPerspective(targetNode));
263 for (
auto entry :
m_helper.pseudonodes()) {
264 if (
m_helper.repr(entry.first) == entry.first) {
266 auto auxNode =
m_auxGraph.treeAuxNode(entry.first);
268 OGDF_ASSERT(other->oddPseudonodes().contains(entry.first)
269 == (other == auxNode && auxNode->tree().isOdd(entry.first)));
286 std::unordered_set<edge>& matching) {
287 return _doCall(G, weights, matching);
295 template<
class WeightContainer>
296 bool _doCall(
const Graph& G,
const WeightContainer& weights, std::unordered_set<edge>& matching) {
298 lout() <<
"start init" << std::endl;
299 #ifdef OGDF_BLOSSOMV_PRINT_STATS
307 lout() <<
"finish init" << std::endl;
308 #ifdef OGDF_BLOSSOMV_PRINT_STATS
309 louth() <<
"Trees: " <<
m_auxGraph.graph().numberOfNodes() << std::endl;
321 #ifdef OGDF_BLOSSOMV_PRINT_STATS
322 std::chrono::high_resolution_clock::time_point last_path, last_four_paths;
325 while (p <
m_auxGraph.graph().numberOfNodes()) {
330 while (
m_auxGraph.graph().numberOfNodes() > 0) {
331 #ifdef OGDF_HEAVY_DEBUG
334 #ifdef OGDF_BLOSSOMV_PRINT_STATS
335 if (
m_auxGraph.graph().numberOfNodes() == p / 2) {
337 lout() <<
"." << p << std::flush;
341 last_four_paths =
now();
349 lout() <<
"found matching" << std::endl;
350 #ifdef OGDF_BLOSSOMV_PRINT_STATS
354 m_helper.getOriginalMatching(matching);
356 #ifdef OGDF_BLOSSOMV_PRINT_STATS
357 m_stats[
"extraLastPathTime"].add(
end(last_path));
358 m_stats[
"extraLastFourPathsTime"].add(
end(last_four_paths));
366 #ifdef OGDF_HEAVY_DEBUG
367 std::unordered_map<edge, TWeight> edgeWeights;
368 m_helper.getReducedEdgeWeights(edgeWeights);
383 #ifdef OGDF_HEAVY_DEBUG
384 m_helper.assertConsistentEdgeWeights(edgeWeights);
403 for (
auto adj : auxNode->
graphNode()->adjEntries) {
411 if (minEdge !=
nullptr) {
429 if (minEdge !=
nullptr) {
446 if (minEdge !=
nullptr) {
463 if (minNode !=
nullptr) {
478 TWeight delta, otherDelta;
484 std::vector<std::unordered_set<node>> components;
486 for (
auto& component : components) {
487 delta = infinity<TWeight>();
489 for (
node v : component) {
492 delta = std::min(delta,
497 for (
auto& adj : v->adjEntries) {
500 if (component.find(w) != component.end()) {
503 delta = std::min(delta,
507 otherDelta = deltas[w];
509 delta = std::min(delta,
514 if (!evenOddPQ.empty()) {
515 delta = std::min(delta,
516 m_helper.getRealTopPriority(evenOddPQ) + otherDelta);
526 if (delta > 0 && delta < infinity<TWeight>()) {
527 for (
auto& v : component) {
535 delta = deltas[auxNode->graphNode()];
538 auxNode->addDelta(delta);
540 lout() <<
"Delta for " << auxNode->tree().root() <<
": " << delta << std::endl;
550 for (
auto augmentationEdgeNode : augmentationEdge->
nodes()) {
551 auto auxNode =
m_auxGraph.treeAuxNode(augmentationEdgeNode);
552 auto oppositeAuxNode =
555 std::vector<edge> edgesToUpdate;
556 for (
auto adj : auxNode->graphNode()->adjEntries) {
557 auto other =
m_auxGraph.auxNode(adj->twinNode());
558 if (other != auxNode && other != oppositeAuxNode) {
559 auto otherEdge =
m_auxGraph.auxEdge(adj->theEdge());
560 for (
auto pq : {&otherEdge->evenEvenEdges(),
561 &otherEdge->evenOddEdgesFromPerspective(other)}) {
564 edgesToUpdate.push_back(e);
569 auxNode->tree().augmentMatching(augmentationEdge);
574 for (
edge e : edgesToUpdate) {
576 an->addEvenFreeEdge(e);
580 lout() <<
"Matching augmented with " << augmentationEdge << std::endl;
586 auto auxNode =
m_auxGraph.auxNodeForEdge(newEdge);
587 auto&
tree = auxNode->tree();
593 tree.grow(u, newEdge, matchingEdge);
603 auxNode->addOddPseudonode(v);
606 node x = adj->twinNode();
607 edge e = adj->theEdge();
609 if (xAuxNode !=
nullptr && xAuxNode->tree().isEven(x)) {
611 xAuxNode->evenFreeEdges().remove(e);
613 if (xAuxNode != auxNode) {
614 m_auxGraph.assertCurrentEdge(xAuxNode, auxNode)
615 ->addEvenOddEdgeFromPerspective(e, xAuxNode);
619 edge augmentationEdge =
nullptr;
621 node x = adj->twinNode();
622 edge e = adj->theEdge();
624 if (xAuxNode ==
nullptr) {
626 auxNode->addEvenFreeEdge(e);
627 }
else if (xAuxNode == auxNode) {
629 if (
tree.isEven(x)) {
630 auxNode->evenFreeEdges().remove(e);
631 auxNode->addEvenEvenEdge(e);
634 auto auxEdge =
m_auxGraph.assertCurrentEdge(xAuxNode, auxNode);
635 auto xTree = xAuxNode->tree();
636 if (xTree.isOdd(x)) {
637 auxEdge->addEvenOddEdgeFromPerspective(e, auxNode);
639 xAuxNode->evenFreeEdges().remove(e);
640 auxEdge->addEvenEvenEdge(e);
641 if (augmentationEdge ==
nullptr &&
m_helper.isEqualityEdge(e)) {
642 augmentationEdge = e;
648 lout() <<
"Tree augmented with " << newEdge << std::endl;
649 if (augmentationEdge !=
nullptr) {
658 auto auxNode =
m_auxGraph.auxNodeForEdge(cycleEdge);
659 auto&
tree = auxNode->tree();
664 std::vector<edge> oddEdges;
665 std::unordered_map<node, edge> bestEdges;
666 edge augmentationEdge =
nullptr;
668 bool isOdd =
tree.isOdd(u);
669 for (
auto adj : u->adjEntries) {
670 edge e = adj->theEdge();
671 node v = adj->twinNode();
674 oddEdges.push_back(e);
679 auxNode->evenEvenEdges().remove(e);
683 if (isOdd &&
m_helper.isPseudonode(u)) {
684 auxNode->oddPseudonodes().remove(u);
697 std::vector<std::tuple<edge, bool>> selfLoops;
705 m_helper.y(newNode) = -auxNode->delta();
708 std::unordered_set<edge> hiddenEdges;
709 for (
auto& entry : selfLoops) {
710 edge e = std::get<0>(entry);
711 bool isEven = std::get<1>(entry);
714 hiddenEdges.insert(e);
716 if (other ==
nullptr) {
718 auxNode->evenFreeEdges().remove(e);
720 }
else if (other == auxNode) {
721 if (isEven &&
tree.isEven(v)) {
722 auxNode->evenEvenEdges().remove(e);
725 if (isEven && other->tree().isEven(v)) {
726 other->currentEdge()->evenEvenEdges().remove(e);
728 if (isEven && other->tree().isOdd(v)) {
729 other->currentEdge()->evenOddEdgesFromPerspective(auxNode).remove(e);
731 if (!isEven && other->tree().isEven(v)) {
732 other->currentEdge()->evenOddEdgesFromPerspective(other).remove(e);
738 for (
edge e : oddEdges) {
739 if (hiddenEdges.find(e) != hiddenEdges.end()) {
742 node v = e->opposite(newNode);
744 if (other ==
nullptr) {
746 auxNode->addEvenFreeEdge(e);
747 }
else if (other->tree().isEven(v)) {
748 if (other == auxNode) {
749 auxNode->addEvenEvenEdge(e);
753 auto auxEdge =
m_auxGraph.assertCurrentEdge(other, auxNode);
754 auxEdge->evenOddEdgesFromPerspective(other).remove(e);
755 auxEdge->addEvenEvenEdge(e);
756 if (augmentationEdge ==
nullptr &&
m_helper.isEqualityEdge(e)) {
757 augmentationEdge = e;
760 }
else if (other != auxNode) {
761 m_auxGraph.assertCurrentEdge(other, auxNode)->addEvenOddEdgeFromPerspective(e, auxNode);
767 lout() << std::endl <<
"Shrank at " << cycleEdge <<
" into " << newNode << std::endl;
768 if (augmentationEdge !=
nullptr) {
778 auto&
tree = auxNode->tree();
779 auto cycle = pseudonode->
cycle;
781 tree.expand(pseudonode);
784 for (
node u : cycle->nodes()) {
785 if (
tree.contains(u)) {
792 edge augmentationEdge =
nullptr;
793 for (
node u : cycle->nodes()) {
794 if (
tree.isEven(u)) {
795 for (
auto adj : u->adjEntries) {
796 edge e = adj->theEdge();
797 node v = adj->twinNode();
799 if (other ==
nullptr) {
801 auxNode->addEvenFreeEdge(e);
802 }
else if (other == auxNode) {
806 if (
tree.isEven(v) && (!cycle->contains(v) || e->
source() == v)) {
807 auxNode->addEvenEvenEdge(e);
810 auto auxEdge =
m_auxGraph.assertCurrentEdge(other, auxNode);
811 if (other->tree().isEven(v)) {
812 if (auxEdge->evenOddEdgesFromPerspective(other).contains(e)) {
813 auxEdge->evenOddEdgesFromPerspective(other).remove(e);
815 auxEdge->addEvenEvenEdge(e);
816 if (augmentationEdge ==
nullptr &&
m_helper.isEqualityEdge(e)) {
817 augmentationEdge = e;
820 auxEdge->addEvenOddEdgeFromPerspective(e, auxNode);
824 }
else if (
tree.isOdd(u)) {
826 auxNode->addOddPseudonode(u);
828 for (
auto adj : u->adjEntries) {
829 edge e = adj->theEdge();
830 node v = adj->twinNode();
832 if (other && other != auxNode && other->tree().isEven(v)) {
833 auto auxEdge =
m_auxGraph.assertCurrentEdge(other, auxNode);
834 if (!auxEdge->evenOddEdgesFromPerspective(other).contains(e)) {
835 auxEdge->addEvenOddEdgeFromPerspective(e, other);
841 for (
auto adj : u->adjEntries) {
842 edge e = adj->theEdge();
843 node v = adj->twinNode();
845 if (other && other->tree().isEven(v)) {
846 if (other != auxNode) {
847 if (other->currentEdge()->evenOddEdgesFromPerspective(other).contains(e)) {
848 other->currentEdge()->evenOddEdgesFromPerspective(other).remove(e);
853 if (!cycle->contains(v)) {
854 other->addEvenFreeEdge(e);
862 lout() << std::endl <<
"Expanded " << nodeIndex << std::endl;
863 if (augmentationEdge !=
nullptr) {
869 #ifdef OGDF_BLOSSOMV_PRINT_STATS
873 for (std::string key : {
"initialize",
"augment",
"grow",
"shrink",
"expand",
"dualChange",
874 "findAugment",
"findGrow",
"findShrink",
"findExpand"}) {
878 std::vector<std::string> extraKeys;
879 std::vector<std::string> otherKeys;
881 if (entry.first.substr(0, 5) ==
"extra") {
882 extraKeys.push_back(entry.first);
884 otherKeys.push_back(entry.first);
887 std::sort(extraKeys.begin(), extraKeys.end());
888 std::sort(otherKeys.begin(), otherKeys.end());
890 for (
auto key : otherKeys) {
893 louth() <<
"Total tracked time: " << total <<
" ms" << std::endl;
894 for (
auto key : extraKeys) {
901 int max = 0, sum = 0, total = 0;
902 std::unordered_map<int, std::unordered_map<int, int>> edgeCount;
904 if (
m_helper.repr(e->source()) == e->source()
905 &&
m_helper.repr(e->target()) == e->target()) {
908 int i = std::min(e->source()->index(), e->target()->index());
909 int j = std::max(e->source()->index(), e->target()->index());
913 for (
auto iEntry : edgeCount) {
914 for (
auto jEntry : iEntry.second) {
915 int count = jEntry.second;
922 louth() <<
"Max number of parallel edges: " << max << std::endl;
923 louth() <<
"Total number of parallel edges: " << sum <<
" (" << ((double)sum / total * 100)
924 <<
"%)" << std::endl;