53 optimal_solution_found_ =
false;
66 int64_t overflow_detection =
CapAdd(maximum_edge_cost_, maximum_edge_cost_);
71 const int num_nodes = matches_.size();
73 VLOG(2) << graph_->DebugString();
74 VLOG(1) <<
"num_unmatched: " << num_nodes - graph_->NumMatched()
75 <<
" dual_objective: " << graph_->DualObjective();
77 while (graph_->NumMatched() != num_nodes) {
78 graph_->PrimalUpdates();
80 graph_->DebugCheckNoPossiblePrimalUpdates();
83 VLOG(1) <<
"num_unmatched: " << num_nodes - graph_->NumMatched()
84 <<
" dual_objective: " << graph_->DualObjective();
85 if (graph_->NumMatched() == num_nodes)
break;
87 const BlossomGraph::CostValue
delta =
88 graph_->ComputeMaxCommonTreeDualDeltaAndResetPrimalEdgeQueue();
89 overflow_detection =
CapAdd(overflow_detection, std::abs(
delta.value()));
94 if (
delta == 0)
break;
95 graph_->UpdateAllTrees(
delta);
98 VLOG(1) <<
"End: " << graph_->NumMatched() <<
" / " << num_nodes;
99 graph_->DisplayStats();
100 if (graph_->NumMatched() < num_nodes) {
103 VLOG(2) << graph_->DebugString();
104 CHECK(graph_->DebugDualsAreFeasible());
108 graph_->ExpandAllBlossoms();
109 for (
int i = 0; i < num_nodes; ++i) {
110 matches_[i] = graph_->Match(BlossomGraph::NodeIndex(i)).value();
113 optimal_solution_found_ =
true;
114 optimal_cost_ = graph_->DualObjective().value();
115 if (optimal_cost_ == std::numeric_limits<int64_t>::max())
158 CHECK(!is_initialized_);
159 is_initialized_ =
true;
161 for (
NodeIndex n(0); n < nodes_.size(); ++n) {
162 if (graph_[n].empty())
return false;
169 for (
const EdgeIndex e : graph_[n]) {
170 min_cost = std::min(min_cost, edges_[e].pseudo_slack);
173 nodes_[n].pseudo_dual = min_cost / 2;
181 for (EdgeIndex e(0); e < edges_.size(); ++e) {
182 Edge& mutable_edge = edges_[e];
184 nodes_[mutable_edge.
head].pseudo_dual;
188 for (
NodeIndex n(0); n < nodes_.size(); ++n) {
194 for (
const EdgeIndex e : graph_[n]) {
195 min_slack = std::min(min_slack, edges_[e].pseudo_slack);
199 nodes_[n].pseudo_dual += min_slack;
200 for (
const EdgeIndex e : graph_[n]) {
201 edges_[e].pseudo_slack -= min_slack;
209 for (
const EdgeIndex e : graph_[n]) {
223 for (
NodeIndex n(0); n < nodes_.size(); ++n) {
225 unmatched_nodes_.push_back(n);
236 for (
NodeIndex n(0); n < nodes_.size(); ++n) {
238 nodes_[n].pseudo_dual *= 2;
239 AddToDualObjective(nodes_[n].pseudo_dual);
241 nodes_[n].dual = nodes_[n].pseudo_dual;
244 for (EdgeIndex e(0); e < edges_.size(); ++e) {
246 edges_[e].pseudo_slack *= 2;
248 edges_[e].slack = edges_[e].pseudo_slack;
254 if (!unmatched_nodes_.empty()) {
255 primal_update_edge_queue_.clear();
256 for (EdgeIndex e(0); e < edges_.size(); ++e) {
258 const bool tail_is_plus = nodes_[
edge.
tail].IsPlus();
259 const bool head_is_plus = nodes_[
edge.
head].IsPlus();
260 if (tail_is_plus && head_is_plus) {
261 plus_plus_pq_.Add(&
edge);
263 }
else if (tail_is_plus || head_is_plus) {
264 plus_free_pq_.Add(&
edge);
276 for (
NodeIndex n(0); n < nodes_.size(); ++n) {
277 const Node& node = nodes_[n];
279 best_update = std::min(best_update,
Dual(node));
284 CHECK(!unmatched_nodes_.empty());
285 const CostValue tree_delta = nodes_[unmatched_nodes_.front()].tree_dual_delta;
287 if (!plus_plus_pq_.IsEmpty()) {
288 DCHECK_EQ(plus_plus_pq_.Top()->pseudo_slack % 2, 0) <<
"Non integer bound!";
289 plus_plus_slack = plus_plus_pq_.Top()->pseudo_slack / 2 - tree_delta;
290 best_update = std::min(best_update, plus_plus_slack);
293 if (!plus_free_pq_.IsEmpty()) {
294 plus_free_slack = plus_free_pq_.Top()->pseudo_slack - tree_delta;
295 best_update = std::min(best_update, plus_free_slack);
306 primal_update_edge_queue_.clear();
307 if (plus_plus_slack == best_update) {
308 plus_plus_pq_.AllTop(&tmp_all_tops_);
309 for (
const Edge* pt : tmp_all_tops_) {
310 primal_update_edge_queue_.push_back(EdgeIndex(pt - &edges_.front()));
313 if (plus_free_slack == best_update) {
314 plus_free_pq_.AllTop(&tmp_all_tops_);
315 for (
const Edge* pt : tmp_all_tops_) {
316 primal_update_edge_queue_.push_back(EdgeIndex(pt - &edges_.front()));
489 DCHECK(nodes_[
tail].IsPlus());
490 DCHECK(nodes_[
head].IsFree());
497 head_node.
root = root;
502 const CostValue tree_dual = nodes_[root].tree_dual_delta;
505 for (
const EdgeIndex e : graph_[subnode]) {
508 if (other_end ==
head)
continue;
510 if (plus_free_pq_.Contains(&
edge)) plus_free_pq_.Remove(&
edge);
514 Node& leaf_node = nodes_[leaf];
515 leaf_node.
root = root;
521 for (
const NodeIndex subnode : SubNodes(leaf)) {
522 for (
const EdgeIndex e : graph_[subnode]) {
525 if (other_end == leaf)
continue;
527 const Node& other_node = nodes_[other_end];
528 if (other_node.
IsPlus()) {
530 DCHECK(plus_free_pq_.Contains(&
edge));
531 DCHECK(!plus_plus_pq_.Contains(&
edge));
532 plus_free_pq_.Remove(&
edge);
533 plus_plus_pq_.Add(&
edge);
536 primal_update_edge_queue_.push_back(e);
538 }
else if (other_node.
IsFree()) {
540 DCHECK(!plus_free_pq_.Contains(&
edge));
541 DCHECK(!plus_plus_pq_.Contains(&
edge));
542 plus_free_pq_.Add(&
edge);
545 primal_update_edge_queue_.push_back(e);
565 VLOG(2) <<
"Augment " << Tail(
edge) <<
" -> " << Head(
edge);
567 DCHECK(nodes_[Tail(
edge)].IsPlus());
568 DCHECK(nodes_[Head(
edge)].IsPlus());
572 DCHECK_NE(root_a, root_b);
575 std::vector<NodeIndex> node_path;
576 AppendNodePathToRoot(Tail(
edge), &node_path);
577 std::reverse(node_path.begin(), node_path.end());
578 AppendNodePathToRoot(Head(
edge), &node_path);
581 const CostValue delta_a = nodes_[root_a].tree_dual_delta;
582 const CostValue delta_b = nodes_[root_b].tree_dual_delta;
583 nodes_[root_a].tree_dual_delta = 0;
584 nodes_[root_b].tree_dual_delta = 0;
597 for (
NodeIndex n(0); n < nodes_.size(); ++n) {
598 Node& node = nodes_[n];
601 if (root != root_a && root != root_b)
continue;
605 for (
const NodeIndex subnode : SubNodes(n)) {
606 for (
const EdgeIndex e : graph_[subnode]) {
609 if (other_end == n)
continue;
615 const Node& other_node = nodes_[other_end];
616 if (other_node.
root != root_a && other_node.
root != root_b &&
618 if (plus_plus_pq_.Contains(&
edge)) plus_plus_pq_.Remove(&
edge);
619 DCHECK(!plus_free_pq_.Contains(&
edge));
620 plus_free_pq_.Add(&
edge);
621 if (
Slack(
edge) == 0) primal_update_edge_queue_.push_back(e);
623 if (plus_plus_pq_.Contains(&
edge)) plus_plus_pq_.Remove(&
edge);
624 if (plus_free_pq_.Contains(&
edge)) plus_free_pq_.Remove(&
edge);
634 CHECK_EQ(node_path.size() % 2, 0);
635 for (
int i = 0; i < node_path.size(); i += 2) {
636 nodes_[node_path[i]].match = node_path[i + 1];
637 nodes_[node_path[i + 1]].match = node_path[i];
646 for (
const NodeIndex n : unmatched_nodes_) {
649 CHECK_EQ(unmatched_nodes_.size(), new_size + 2);
650 unmatched_nodes_.resize(new_size);
669 DCHECK(nodes_[Tail(
edge)].IsPlus());
670 DCHECK(nodes_[Head(
edge)].IsPlus());
671 DCHECK_EQ(nodes_[Tail(
edge)].root, nodes_[Head(
edge)].root);
673 CHECK_NE(Tail(
edge), Head(
edge)) << e;
678 std::vector<NodeIndex> tail_path;
679 std::vector<NodeIndex> head_path;
683 int tail_depth = GetDepth(
tail);
684 int head_depth = GetDepth(
head);
685 if (tail_depth > head_depth) {
687 std::swap(tail_depth, head_depth);
689 VLOG(2) <<
"Shrink " <<
tail <<
" <-> " <<
head;
691 while (head_depth > tail_depth) {
692 head_path.push_back(
head);
697 DCHECK_EQ(tail_depth, head_depth);
698 DCHECK_GE(tail_depth, 0);
704 tail_path.push_back(
tail);
707 head_path.push_back(
head);
711 VLOG(2) <<
"LCA " << lca_index;
713 Node& lca = nodes_[lca_index];
717 std::vector<NodeIndex> blossom = {lca_index};
718 std::reverse(head_path.begin(), head_path.end());
719 blossom.insert(blossom.end(), head_path.begin(), head_path.end());
720 blossom.insert(blossom.end(), tail_path.begin(), tail_path.end());
721 CHECK_EQ(blossom.size() % 2, 1);
723 const CostValue tree_dual = nodes_[lca.
root].tree_dual_delta;
726 CHECK_GT(blossom.size(), 1);
727 Node& backup_node = nodes_[blossom[1]];
738 CHECK_EQ(
Dual(lca), 0);
745 if (n != lca_index) {
746 nodes_[n].is_internal =
true;
752 Node& mutable_node = nodes_[n];
753 const bool was_minus = mutable_node.
IsMinus();
755 mutable_node.
IsMinus() ? tree_dual : -tree_dual;
756 if (n != lca_index) {
761 mutable_node.
type = 0;
763 for (
const NodeIndex subnode : SubNodes(n)) {
767 root_blossom_node_[subnode] = lca_index;
769 for (
const EdgeIndex e : graph_[subnode]) {
774 if (other_end == n)
continue;
778 if (other_end == lca_index) {
791 Node& mutable_other_node = nodes_[other_end];
793 DCHECK(!plus_free_pq_.Contains(&
edge));
794 if (plus_plus_pq_.Contains(&
edge)) plus_plus_pq_.Remove(&
edge);
797 mutable_other_node.
IsMinus() ? tree_dual : -tree_dual;
802 if (mutable_other_node.
parent == n) {
803 mutable_other_node.
parent = lca_index;
813 DCHECK(!plus_plus_pq_.Contains(&
edge));
814 DCHECK(!plus_free_pq_.Contains(&
edge));
815 if (mutable_other_node.
IsPlus()) {
816 plus_plus_pq_.Add(&
edge);
818 primal_update_edge_queue_.push_back(e);
820 }
else if (mutable_other_node.
IsFree()) {
821 plus_free_pq_.Add(&
edge);
823 primal_update_edge_queue_.push_back(e);
837 lca.
blossom = std::move(blossom);
861 VLOG(2) <<
"Expand " << to_expand;
863 Node& node_to_expand = nodes_[to_expand];
865 DCHECK(node_to_expand.
IsMinus());
866 DCHECK_EQ(
Dual(node_to_expand), 0);
868 const EdgeIndex match_edge_index =
869 FindTightExternalEdgeBetweenNodes(to_expand, node_to_expand.
match);
870 const EdgeIndex parent_edge_index =
871 FindTightExternalEdgeBetweenNodes(to_expand, node_to_expand.
parent);
874 Node& backup_node = nodes_[node_to_expand.
blossom[1]];
879 std::vector<NodeIndex> blossom = std::move(node_to_expand.
blossom);
885 for (
const NodeIndex subnode : SubNodes(n)) {
886 root_blossom_node_[subnode] = n;
898 int blossom_path_start = -1;
899 int blossom_path_end = -1;
900 const NodeIndex start_node = OtherEndFromExternalNode(
901 edges_[parent_edge_index], node_to_expand.
parent);
903 OtherEndFromExternalNode(edges_[match_edge_index], node_to_expand.
match);
904 for (
int i = 0; i < blossom.size(); ++i) {
905 if (blossom[i] == start_node) blossom_path_start = i;
906 if (blossom[i] == end_node) blossom_path_end = i;
911 const std::vector<NodeIndex>& cycle = blossom;
912 std::vector<NodeIndex> path1;
913 std::vector<NodeIndex> path2;
915 const int end_offset =
916 (blossom_path_end + cycle.size() - blossom_path_start) % cycle.size();
917 for (
int offset = 0; offset <= cycle.size(); ++offset) {
919 cycle[(blossom_path_start + offset) % cycle.size()];
920 if (offset <= end_offset) path1.push_back(node);
921 if (offset >= end_offset) path2.push_back(node);
926 std::reverse(path2.begin(), path2.end());
929 if (path1.size() % 2 == 0) path1.swap(path2);
932 std::vector<NodeIndex>& path_in_tree = path1;
933 const std::vector<NodeIndex>& free_pairs = path2;
936 path2.erase(path2.begin());
941 << absl::StrJoin(path_in_tree,
", ", absl::StreamFormatter())
942 <<
"] === " << blossom_matched_node;
944 << absl::StrJoin(free_pairs,
", ", absl::StreamFormatter()) <<
"]";
950 path_in_tree.push_back(blossom_matched_node);
951 CHECK_EQ(path_in_tree.size() % 2, 0);
952 const CostValue tree_dual = nodes_[node_to_expand.
root].tree_dual_delta;
953 for (
int i = 0; i < path_in_tree.size(); ++i) {
955 const bool node_is_plus = i % 2;
961 DCHECK(node_to_expand.
parent != to_expand || n == to_expand);
962 nodes_[n].parent = node_to_expand.
parent;
964 nodes_[n].parent = path_in_tree[i - 1];
968 nodes_[n].root = node_to_expand.
root;
969 nodes_[n].type = node_is_plus ? 1 : -1;
970 nodes_[n].match = path_in_tree[node_is_plus ? i - 1 : i + 1];
973 if (i + 1 == path_in_tree.size())
continue;
978 const CostValue adjust = node_is_plus ? -tree_dual : tree_dual;
979 nodes_[n].pseudo_dual += adjust;
980 for (
const NodeIndex subnode : SubNodes(n)) {
981 for (
const EdgeIndex e : graph_[subnode]) {
984 if (other_end == n)
continue;
990 if (other_end != to_expand && !nodes_[other_end].is_internal) {
997 if (nodes_[other_end].type == 0)
continue;
1002 const Node& other_node = nodes_[other_end];
1003 DCHECK(!plus_plus_pq_.Contains(&
edge));
1004 DCHECK(!plus_free_pq_.Contains(&
edge));
1005 if (other_node.
IsPlus()) {
1006 plus_plus_pq_.Add(&
edge);
1008 primal_update_edge_queue_.push_back(e);
1010 }
else if (other_node.
IsFree()) {
1011 plus_free_pq_.Add(&
edge);
1013 primal_update_edge_queue_.push_back(e);
1024 nodes_[n].parent = n;
1028 for (
const NodeIndex subnode : SubNodes(n)) {
1029 for (
const EdgeIndex e : graph_[subnode]) {
1032 if (other_end == n)
continue;
1036 if (other_end != to_expand && !nodes_[other_end].is_internal) {
1042 DCHECK(!plus_plus_pq_.Contains(&
edge));
1043 DCHECK(!plus_free_pq_.Contains(&
edge));
1044 if (nodes_[other_end].IsPlus()) {
1045 plus_free_pq_.Add(&
edge);
1047 primal_update_edge_queue_.push_back(e);
1055 CHECK_EQ(free_pairs.size() % 2, 0);
1056 for (
int i = 0; i < free_pairs.size(); i += 2) {
1057 nodes_[free_pairs[i]].match = free_pairs[i + 1];
1058 nodes_[free_pairs[i + 1]].match = free_pairs[i];
1064 nodes_[n].is_internal =
false;
1070 std::vector<NodeIndex> queue;
1071 for (
NodeIndex n(0); n < nodes_.size(); ++n) {
1072 Node& node = nodes_[n];
1078 if (node.
IsBlossom()) queue.push_back(n);
1082 while (!queue.empty()) {
1083 const NodeIndex to_expand = queue.back();
1086 Node& node_to_expand = nodes_[to_expand];
1090 const EdgeIndex match_edge_index =
1091 FindTightExternalEdgeBetweenNodes(to_expand, node_to_expand.
match);
1094 Node& backup_node = nodes_[node_to_expand.
blossom[1]];
1100 std::vector<NodeIndex> blossom = std::move(node_to_expand.
blossom);
1106 for (
const NodeIndex subnode : SubNodes(n)) {
1107 root_blossom_node_[subnode] = n;
1112 int internal_matched_index = -1;
1113 const NodeIndex matched_node = OtherEndFromExternalNode(
1114 edges_[match_edge_index], node_to_expand.
match);
1115 const int size = blossom.size();
1116 for (
int i = 0; i <
size; ++i) {
1117 if (blossom[i] == matched_node) {
1118 internal_matched_index = i;
1122 CHECK_NE(internal_matched_index, -1);
1126 std::vector<NodeIndex> free_pairs;
1127 for (
int i = (internal_matched_index + 1) %
size;
1128 i != internal_matched_index; i = (i + 1) %
size) {
1129 free_pairs.push_back(blossom[i]);
1133 for (
const NodeIndex to_clear : blossom) {
1134 nodes_[to_clear].type = 0;
1135 nodes_[to_clear].is_internal =
false;
1136 nodes_[to_clear].parent = to_clear;
1137 nodes_[to_clear].root = to_clear;
1142 const NodeIndex internal_matched_node = blossom[internal_matched_index];
1143 nodes_[internal_matched_node].match = external_matched_node;
1144 nodes_[external_matched_node].match = internal_matched_node;
1147 CHECK_EQ(free_pairs.size() % 2, 0);
1148 for (
int i = 0; i < free_pairs.size(); i += 2) {
1149 nodes_[free_pairs[i]].match = free_pairs[i + 1];
1150 nodes_[free_pairs[i + 1]].match = free_pairs[i];
1155 if (nodes_[n].IsBlossom()) queue.push_back(n);