53 #include <initializer_list>
57 #include <unordered_map>
58 #include <unordered_set>
63 template<
typename TWeight>
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)
91 template<
typename TWeight>
92 class MatchingBlossomV :
public MatchingModule<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
146 void assertConsistency() {
151 auto tree = auxNode->tree();
156 for (
node v : tree.oddNodes) {
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);
404 for (
auto adj : auxNode->
graphNode()->adjEntries) {
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;