24 #include "absl/memory/memory.h"
30 graph_ = std::make_unique<BlossomGraph>(num_nodes);
32 matches_.assign(num_nodes, -1);
36 CHECK_GE(
cost, 0) <<
"Not supported for now, just shift your costs.";
38 VLOG(1) <<
"Ignoring self-arc: " <<
tail <<
" <-> " <<
head
48 optimal_solution_found_ =
false;
61 int64_t overflow_detection =
CapAdd(maximum_edge_cost_, maximum_edge_cost_);
63 return Status::INTEGER_OVERFLOW;
66 const int num_nodes = matches_.size();
68 VLOG(2) << graph_->DebugString();
69 VLOG(1) <<
"num_unmatched: " << num_nodes - graph_->NumMatched()
70 <<
" dual_objective: " << graph_->DualObjective();
72 while (graph_->NumMatched() != num_nodes) {
73 graph_->PrimalUpdates();
75 graph_->DebugCheckNoPossiblePrimalUpdates();
78 VLOG(1) <<
"num_unmatched: " << num_nodes - graph_->NumMatched()
79 <<
" dual_objective: " << graph_->DualObjective();
80 if (graph_->NumMatched() == num_nodes)
break;
83 graph_->ComputeMaxCommonTreeDualDeltaAndResetPrimalEdgeQueue();
84 overflow_detection =
CapAdd(overflow_detection, std::abs(
delta.value()));
86 return Status::INTEGER_OVERFLOW;
89 if (
delta == 0)
break;
90 graph_->UpdateAllTrees(
delta);
93 VLOG(1) <<
"End: " << graph_->NumMatched() <<
" / " << num_nodes;
94 graph_->DisplayStats();
95 if (graph_->NumMatched() < num_nodes) {
98 VLOG(2) << graph_->DebugString();
99 CHECK(graph_->DebugDualsAreFeasible());
103 graph_->ExpandAllBlossoms();
104 for (
int i = 0; i < num_nodes; ++i) {
108 optimal_solution_found_ =
true;
109 optimal_cost_ = graph_->DualObjective().value();
111 return Status::COST_OVERFLOW;
121 BlossomGraph::EdgeIndex(-1);
128 root_blossom_node_.
resize(num_nodes);
129 for (
NodeIndex n(0); n < num_nodes; ++n) {
130 root_blossom_node_[n] = n;
141 DCHECK(!is_initialized_);
142 const EdgeIndex
index(edges_.size());
153 CHECK(!is_initialized_);
154 is_initialized_ =
true;
157 if (graph_[n].empty())
return false;
164 for (
const EdgeIndex e : graph_[n]) {
165 min_cost =
std::min(min_cost, edges_[e].pseudo_slack);
168 nodes_[n].pseudo_dual = min_cost / 2;
176 for (EdgeIndex e(0); e < edges_.size(); ++e) {
177 Edge& mutable_edge = edges_[e];
179 nodes_[mutable_edge.
head].pseudo_dual;
189 for (
const EdgeIndex e : graph_[n]) {
190 min_slack =
std::min(min_slack, edges_[e].pseudo_slack);
194 nodes_[n].pseudo_dual += min_slack;
195 for (
const EdgeIndex e : graph_[n]) {
196 edges_[e].pseudo_slack -= min_slack;
204 for (
const EdgeIndex e : graph_[n]) {
205 const Edge& edge = edges_[e];
208 nodes_[edge.
tail].type = 0;
209 nodes_[edge.
tail].match = edge.
head;
210 nodes_[edge.
head].type = 0;
211 nodes_[edge.
head].match = edge.
tail;
220 unmatched_nodes_.push_back(n);
233 nodes_[n].pseudo_dual *= 2;
234 AddToDualObjective(nodes_[n].pseudo_dual);
236 nodes_[n].dual = nodes_[n].pseudo_dual;
239 for (EdgeIndex e(0); e < edges_.size(); ++e) {
241 edges_[e].pseudo_slack *= 2;
243 edges_[e].slack = edges_[e].pseudo_slack;
249 if (!unmatched_nodes_.empty()) {
250 primal_update_edge_queue_.clear();
251 for (EdgeIndex e(0); e < edges_.size(); ++e) {
252 Edge& edge = edges_[e];
253 const bool tail_is_plus = nodes_[edge.
tail].IsPlus();
254 const bool head_is_plus = nodes_[edge.
head].IsPlus();
255 if (tail_is_plus && head_is_plus) {
256 plus_plus_pq_.Add(&edge);
257 if (edge.
pseudo_slack == 0) primal_update_edge_queue_.push_back(e);
258 }
else if (tail_is_plus || head_is_plus) {
259 plus_free_pq_.Add(&edge);
260 if (edge.
pseudo_slack == 0) primal_update_edge_queue_.push_back(e);
272 const Node& node = nodes_[n];
279 CHECK(!unmatched_nodes_.empty());
280 const CostValue tree_delta = nodes_[unmatched_nodes_.front()].tree_dual_delta;
282 if (!plus_plus_pq_.IsEmpty()) {
283 DCHECK_EQ(plus_plus_pq_.Top()->pseudo_slack % 2, 0) <<
"Non integer bound!";
284 plus_plus_slack = plus_plus_pq_.Top()->pseudo_slack / 2 - tree_delta;
285 best_update =
std::min(best_update, plus_plus_slack);
288 if (!plus_free_pq_.IsEmpty()) {
289 plus_free_slack = plus_free_pq_.Top()->pseudo_slack - tree_delta;
290 best_update =
std::min(best_update, plus_free_slack);
301 primal_update_edge_queue_.clear();
302 if (plus_plus_slack == best_update) {
303 plus_plus_pq_.AllTop(&tmp_all_tops_);
304 for (
const Edge* pt : tmp_all_tops_) {
305 primal_update_edge_queue_.push_back(EdgeIndex(pt - &edges_.front()));
308 if (plus_free_slack == best_update) {
309 plus_free_pq_.AllTop(&tmp_all_tops_);
310 for (
const Edge* pt : tmp_all_tops_) {
311 primal_update_edge_queue_.push_back(EdgeIndex(pt - &edges_.front()));
323 for (
const NodeIndex n : unmatched_nodes_) {
325 AddToDualObjective(
delta);
326 nodes_[n].tree_dual_delta +=
delta;
331 const Node& node = nodes_[n];
340 const Node& node = nodes_[n];
342 return node.
match != n;
346 const Node& node = nodes_[n];
357 for (EdgeIndex e(0); e < edges_.size(); ++e) {
358 const Edge& edge = edges_[e];
359 if (Head(edge) == Tail(edge))
continue;
361 CHECK(!nodes_[Tail(edge)].is_internal);
362 CHECK(!nodes_[Head(edge)].is_internal);
363 if (
Slack(edge) != 0)
continue;
369 if (!nodes_[
tail].IsPlus())
continue;
371 if (nodes_[
head].IsFree()) {
373 LOG(FATAL) <<
"Possible Grow! " <<
tail <<
" " <<
head;
375 if (nodes_[
head].IsPlus()) {
376 if (nodes_[
tail].root == nodes_[
head].root) {
377 LOG(FATAL) <<
"Possible Shrink!";
379 LOG(FATAL) <<
"Possible augment!";
383 for (
const Node& node : nodes_) {
384 if (node.IsMinus() && node.IsBlossom() &&
Dual(node) == 0) {
385 LOG(FATAL) <<
"Possible expand!";
396 possible_shrink_.clear();
399 while (!primal_update_edge_queue_.empty()) {
400 const EdgeIndex e = primal_update_edge_queue_.back();
401 primal_update_edge_queue_.pop_back();
407 const Edge& edge = edges_[e];
408 if (
Slack(edge) != 0)
continue;
413 if (!nodes_[
tail].IsPlus())
continue;
415 if (nodes_[
head].IsFree()) {
417 }
else if (nodes_[
head].IsPlus()) {
418 if (nodes_[
tail].root != nodes_[
head].root) {
421 possible_shrink_.push_back(e);
427 for (
const EdgeIndex e : possible_shrink_) {
428 const Edge& edge = edges_[e];
431 const Node& tail_node = nodes_[
tail];
432 const Node& head_node = nodes_[
head];
440 if (!primal_update_edge_queue_.empty())
continue;
449 const Node& node = nodes_[n];
455 if (num_expands == 0)
break;
461 for (
const Edge& edge : edges_) {
462 if (
Slack(edge) < 0)
return false;
466 for (
const Node& node : nodes_) {
467 if (node.IsBlossom() &&
Dual(node) < 0)
return false;
473 if (Tail(edge) == Head(edge))
return false;
474 if (nodes_[Tail(edge)].IsInternal())
return false;
475 if (nodes_[Head(edge)].IsInternal())
return false;
476 return Slack(edge) == 0;
484 DCHECK(nodes_[
tail].IsPlus());
485 DCHECK(nodes_[
head].IsFree());
492 head_node.
root = root;
497 const CostValue tree_dual = nodes_[root].tree_dual_delta;
500 for (
const EdgeIndex e : graph_[subnode]) {
501 Edge& edge = edges_[e];
502 const NodeIndex other_end = OtherEnd(edge, subnode);
503 if (other_end ==
head)
continue;
505 if (plus_free_pq_.Contains(&edge)) plus_free_pq_.Remove(&edge);
509 Node& leaf_node = nodes_[leaf];
510 leaf_node.
root = root;
516 for (
const NodeIndex subnode : SubNodes(leaf)) {
517 for (
const EdgeIndex e : graph_[subnode]) {
518 Edge& edge = edges_[e];
519 const NodeIndex other_end = OtherEnd(edge, subnode);
520 if (other_end == leaf)
continue;
522 const Node& other_node = nodes_[other_end];
523 if (other_node.
IsPlus()) {
525 DCHECK(plus_free_pq_.Contains(&edge));
526 DCHECK(!plus_plus_pq_.Contains(&edge));
527 plus_free_pq_.Remove(&edge);
528 plus_plus_pq_.Add(&edge);
530 DCHECK_EQ(
Slack(edge), 0);
531 primal_update_edge_queue_.push_back(e);
533 }
else if (other_node.
IsFree()) {
535 DCHECK(!plus_free_pq_.Contains(&edge));
536 DCHECK(!plus_plus_pq_.Contains(&edge));
537 plus_free_pq_.Add(&edge);
539 DCHECK_EQ(
Slack(edge), 0);
540 primal_update_edge_queue_.push_back(e);
547 void BlossomGraph::AppendNodePathToRoot(
NodeIndex n,
548 std::vector<NodeIndex>* path)
const {
551 n = nodes_[n].parent;
552 if (n == path->back())
break;
559 const Edge& edge = edges_[e];
560 VLOG(2) <<
"Augment " << Tail(edge) <<
" -> " << Head(edge);
562 DCHECK(nodes_[Tail(edge)].IsPlus());
563 DCHECK(nodes_[Head(edge)].IsPlus());
565 const NodeIndex root_a = nodes_[Tail(edge)].root;
566 const NodeIndex root_b = nodes_[Head(edge)].root;
567 DCHECK_NE(root_a, root_b);
570 std::vector<NodeIndex> node_path;
571 AppendNodePathToRoot(Tail(edge), &node_path);
572 std::reverse(node_path.begin(), node_path.end());
573 AppendNodePathToRoot(Head(edge), &node_path);
576 const CostValue delta_a = nodes_[root_a].tree_dual_delta;
577 const CostValue delta_b = nodes_[root_b].tree_dual_delta;
578 nodes_[root_a].tree_dual_delta = 0;
579 nodes_[root_b].tree_dual_delta = 0;
593 Node& node = nodes_[n];
596 if (root != root_a && root != root_b)
continue;
600 for (
const NodeIndex subnode : SubNodes(n)) {
601 for (
const EdgeIndex e : graph_[subnode]) {
602 Edge& edge = edges_[e];
603 const NodeIndex other_end = OtherEnd(edge, subnode);
604 if (other_end == n)
continue;
610 const Node& other_node = nodes_[other_end];
611 if (other_node.
root != root_a && other_node.
root != root_b &&
613 if (plus_plus_pq_.Contains(&edge)) plus_plus_pq_.Remove(&edge);
614 DCHECK(!plus_free_pq_.Contains(&edge));
615 plus_free_pq_.Add(&edge);
616 if (
Slack(edge) == 0) primal_update_edge_queue_.push_back(e);
618 if (plus_plus_pq_.Contains(&edge)) plus_plus_pq_.Remove(&edge);
619 if (plus_free_pq_.Contains(&edge)) plus_free_pq_.Remove(&edge);
629 CHECK_EQ(node_path.size() % 2, 0);
630 for (
int i = 0; i < node_path.size(); i += 2) {
631 nodes_[node_path[i]].match = node_path[i + 1];
632 nodes_[node_path[i + 1]].match = node_path[i];
641 for (
const NodeIndex n : unmatched_nodes_) {
644 CHECK_EQ(unmatched_nodes_.size(), new_size + 2);
645 unmatched_nodes_.resize(new_size);
648 int BlossomGraph::GetDepth(
NodeIndex n)
const {
651 const NodeIndex parent = nodes_[n].parent;
652 if (parent == n)
break;
662 const Edge& edge = edges_[e];
664 DCHECK(nodes_[Tail(edge)].IsPlus());
665 DCHECK(nodes_[Head(edge)].IsPlus());
666 DCHECK_EQ(nodes_[Tail(edge)].root, nodes_[Head(edge)].root);
668 CHECK_NE(Tail(edge), Head(edge)) << e;
673 std::vector<NodeIndex> tail_path;
674 std::vector<NodeIndex> head_path;
678 int tail_depth = GetDepth(
tail);
679 int head_depth = GetDepth(
head);
680 if (tail_depth > head_depth) {
686 while (head_depth > tail_depth) {
687 head_path.push_back(
head);
692 DCHECK_EQ(tail_depth, head_depth);
693 DCHECK_GE(tail_depth, 0);
699 tail_path.push_back(
tail);
706 VLOG(2) <<
"LCA " << lca_index;
708 Node& lca = nodes_[lca_index];
712 std::vector<NodeIndex> blossom = {lca_index};
713 std::reverse(head_path.begin(), head_path.end());
714 blossom.insert(blossom.end(), head_path.begin(), head_path.end());
715 blossom.insert(blossom.end(), tail_path.begin(), tail_path.end());
716 CHECK_EQ(blossom.size() % 2, 1);
718 const CostValue tree_dual = nodes_[lca.
root].tree_dual_delta;
721 CHECK_GT(blossom.size(), 1);
722 Node& backup_node = nodes_[blossom[1]];
733 CHECK_EQ(
Dual(lca), 0);
740 if (n != lca_index) {
741 nodes_[n].is_internal =
true;
747 Node& mutable_node = nodes_[n];
748 const bool was_minus = mutable_node.
IsMinus();
750 mutable_node.
IsMinus() ? tree_dual : -tree_dual;
751 if (n != lca_index) {
756 mutable_node.
type = 0;
758 for (
const NodeIndex subnode : SubNodes(n)) {
762 root_blossom_node_[subnode] = lca_index;
764 for (
const EdgeIndex e : graph_[subnode]) {
765 Edge& edge = edges_[e];
766 const NodeIndex other_end = OtherEnd(edge, subnode);
769 if (other_end == n)
continue;
773 if (other_end == lca_index) {
786 Node& mutable_other_node = nodes_[other_end];
788 DCHECK(!plus_free_pq_.Contains(&edge));
789 if (plus_plus_pq_.Contains(&edge)) plus_plus_pq_.Remove(&edge);
792 mutable_other_node.
IsMinus() ? tree_dual : -tree_dual;
797 if (mutable_other_node.
parent == n) {
798 mutable_other_node.
parent = lca_index;
808 DCHECK(!plus_plus_pq_.Contains(&edge));
809 DCHECK(!plus_free_pq_.Contains(&edge));
810 if (mutable_other_node.
IsPlus()) {
811 plus_plus_pq_.Add(&edge);
813 primal_update_edge_queue_.push_back(e);
815 }
else if (mutable_other_node.
IsFree()) {
816 plus_free_pq_.Add(&edge);
818 primal_update_edge_queue_.push_back(e);
832 lca.
blossom = std::move(blossom);
837 BlossomGraph::EdgeIndex BlossomGraph::FindTightExternalEdgeBetweenNodes(
840 DCHECK_EQ(
tail, root_blossom_node_[
tail]);
841 DCHECK_EQ(
head, root_blossom_node_[
head]);
843 for (
const EdgeIndex e : graph_[subnode]) {
844 const Edge& edge = edges_[e];
845 const NodeIndex other_end = OtherEnd(edge, subnode);
846 if (other_end ==
head &&
Slack(edge) == 0) {
856 VLOG(2) <<
"Expand " << to_expand;
858 Node& node_to_expand = nodes_[to_expand];
860 DCHECK(node_to_expand.
IsMinus());
861 DCHECK_EQ(
Dual(node_to_expand), 0);
863 const EdgeIndex match_edge_index =
864 FindTightExternalEdgeBetweenNodes(to_expand, node_to_expand.
match);
865 const EdgeIndex parent_edge_index =
866 FindTightExternalEdgeBetweenNodes(to_expand, node_to_expand.
parent);
869 Node& backup_node = nodes_[node_to_expand.
blossom[1]];
874 std::vector<NodeIndex> blossom = std::move(node_to_expand.
blossom);
880 for (
const NodeIndex subnode : SubNodes(n)) {
881 root_blossom_node_[subnode] = n;
893 int blossom_path_start = -1;
894 int blossom_path_end = -1;
895 const NodeIndex start_node = OtherEndFromExternalNode(
896 edges_[parent_edge_index], node_to_expand.
parent);
898 OtherEndFromExternalNode(edges_[match_edge_index], node_to_expand.
match);
899 for (
int i = 0; i < blossom.size(); ++i) {
900 if (blossom[i] == start_node) blossom_path_start = i;
901 if (blossom[i] == end_node) blossom_path_end = i;
906 const std::vector<NodeIndex>& cycle = blossom;
907 std::vector<NodeIndex> path1;
908 std::vector<NodeIndex> path2;
910 const int end_offset =
911 (blossom_path_end + cycle.size() - blossom_path_start) % cycle.size();
912 for (
int offset = 0; offset <= cycle.size(); ++offset) {
914 cycle[(blossom_path_start + offset) % cycle.size()];
915 if (offset <= end_offset) path1.push_back(node);
916 if (offset >= end_offset) path2.push_back(node);
921 std::reverse(path2.begin(), path2.end());
924 if (path1.size() % 2 == 0) path1.swap(path2);
927 std::vector<NodeIndex>& path_in_tree = path1;
928 const std::vector<NodeIndex>& free_pairs = path2;
931 path2.erase(path2.begin());
936 << absl::StrJoin(path_in_tree,
", ", absl::StreamFormatter())
937 <<
"] === " << blossom_matched_node;
939 << absl::StrJoin(free_pairs,
", ", absl::StreamFormatter()) <<
"]";
945 path_in_tree.push_back(blossom_matched_node);
946 CHECK_EQ(path_in_tree.size() % 2, 0);
947 const CostValue tree_dual = nodes_[node_to_expand.
root].tree_dual_delta;
948 for (
int i = 0; i < path_in_tree.size(); ++i) {
950 const bool node_is_plus = i % 2;
956 DCHECK(node_to_expand.
parent != to_expand || n == to_expand);
957 nodes_[n].parent = node_to_expand.
parent;
959 nodes_[n].parent = path_in_tree[i - 1];
963 nodes_[n].root = node_to_expand.
root;
964 nodes_[n].type = node_is_plus ? 1 : -1;
965 nodes_[n].match = path_in_tree[node_is_plus ? i - 1 : i + 1];
968 if (i + 1 == path_in_tree.size())
continue;
973 const CostValue adjust = node_is_plus ? -tree_dual : tree_dual;
974 nodes_[n].pseudo_dual += adjust;
975 for (
const NodeIndex subnode : SubNodes(n)) {
976 for (
const EdgeIndex e : graph_[subnode]) {
977 Edge& edge = edges_[e];
978 const NodeIndex other_end = OtherEnd(edge, subnode);
979 if (other_end == n)
continue;
985 if (other_end != to_expand && !nodes_[other_end].is_internal) {
992 if (nodes_[other_end].type == 0)
continue;
997 const Node& other_node = nodes_[other_end];
998 DCHECK(!plus_plus_pq_.Contains(&edge));
999 DCHECK(!plus_free_pq_.Contains(&edge));
1000 if (other_node.
IsPlus()) {
1001 plus_plus_pq_.Add(&edge);
1003 primal_update_edge_queue_.push_back(e);
1005 }
else if (other_node.
IsFree()) {
1006 plus_free_pq_.Add(&edge);
1008 primal_update_edge_queue_.push_back(e);
1019 nodes_[n].parent = n;
1023 for (
const NodeIndex subnode : SubNodes(n)) {
1024 for (
const EdgeIndex e : graph_[subnode]) {
1025 Edge& edge = edges_[e];
1026 const NodeIndex other_end = OtherEnd(edge, subnode);
1027 if (other_end == n)
continue;
1031 if (other_end != to_expand && !nodes_[other_end].is_internal) {
1037 DCHECK(!plus_plus_pq_.Contains(&edge));
1038 DCHECK(!plus_free_pq_.Contains(&edge));
1039 if (nodes_[other_end].IsPlus()) {
1040 plus_free_pq_.Add(&edge);
1042 primal_update_edge_queue_.push_back(e);
1050 CHECK_EQ(free_pairs.size() % 2, 0);
1051 for (
int i = 0; i < free_pairs.size(); i += 2) {
1052 nodes_[free_pairs[i]].match = free_pairs[i + 1];
1053 nodes_[free_pairs[i + 1]].match = free_pairs[i];
1059 nodes_[n].is_internal =
false;
1065 std::vector<NodeIndex> queue;
1067 Node& node = nodes_[n];
1073 if (node.
IsBlossom()) queue.push_back(n);
1077 while (!queue.empty()) {
1078 const NodeIndex to_expand = queue.back();
1081 Node& node_to_expand = nodes_[to_expand];
1085 const EdgeIndex match_edge_index =
1086 FindTightExternalEdgeBetweenNodes(to_expand, node_to_expand.
match);
1089 Node& backup_node = nodes_[node_to_expand.
blossom[1]];
1095 std::vector<NodeIndex> blossom = std::move(node_to_expand.
blossom);
1101 for (
const NodeIndex subnode : SubNodes(n)) {
1102 root_blossom_node_[subnode] = n;
1107 int internal_matched_index = -1;
1108 const NodeIndex matched_node = OtherEndFromExternalNode(
1109 edges_[match_edge_index], node_to_expand.
match);
1110 const int size = blossom.size();
1111 for (
int i = 0; i < size; ++i) {
1112 if (blossom[i] == matched_node) {
1113 internal_matched_index = i;
1117 CHECK_NE(internal_matched_index, -1);
1121 std::vector<NodeIndex> free_pairs;
1122 for (
int i = (internal_matched_index + 1) % size;
1123 i != internal_matched_index; i = (i + 1) % size) {
1124 free_pairs.push_back(blossom[i]);
1128 for (
const NodeIndex to_clear : blossom) {
1129 nodes_[to_clear].type = 0;
1130 nodes_[to_clear].is_internal =
false;
1131 nodes_[to_clear].parent = to_clear;
1132 nodes_[to_clear].root = to_clear;
1137 const NodeIndex internal_matched_node = blossom[internal_matched_index];
1138 nodes_[internal_matched_node].match = external_matched_node;
1139 nodes_[external_matched_node].match = internal_matched_node;
1142 CHECK_EQ(free_pairs.size() % 2, 0);
1143 for (
int i = 0; i < free_pairs.size(); i += 2) {
1144 nodes_[free_pairs[i]].match = free_pairs[i + 1];
1145 nodes_[free_pairs[i + 1]].match = free_pairs[i];
1150 if (nodes_[n].IsBlossom()) queue.
push_back(n);
1155 const std::vector<NodeIndex>& BlossomGraph::SubNodes(
NodeIndex n) {
1159 DCHECK(nodes_[n].saved_blossom.empty());
1164 for (
int i = 0; i < subnodes_.size(); ++i) {
1165 const Node& node = nodes_[subnodes_[i]];
1169 if (!node.blossom.empty()) {
1170 subnodes_.
insert(subnodes_.end(), node.blossom.begin() + 1,
1171 node.blossom.end());
1178 if (!node.saved_blossom.empty()) {
1179 subnodes_.insert(subnodes_.end(), node.saved_blossom.begin() + 1,
1180 node.saved_blossom.end());
1187 const Node& node = nodes_[n];
1189 return absl::StrCat(
"[I] #", n.value());
1192 : node.
type == 1 ?
"[+]"
1193 : node.
type == -1 ?
"[-]"
1195 return absl::StrCat(
1196 type,
" #", n.value(),
" dual: ",
Dual(node).
value(),
1197 " parent: ", node.
parent.value(),
" match: ", node.
match.value(),
1198 " blossom: [", absl::StrJoin(node.
blossom,
", ", absl::StreamFormatter()),
1203 const Edge& edge = edges_[e];
1204 if (nodes_[Tail(edge)].is_internal || nodes_[Head(edge)].is_internal) {
1205 return absl::StrCat(Tail(edge).
value(),
"<->", Head(edge).
value(),
1208 return absl::StrCat(Tail(edge).
value(),
"<->", Head(edge).
value(),
1213 std::string result =
"Graph:\n";
1217 for (EdgeIndex e(0); e < edges_.size(); ++e) {
1225 nodes_[n].dual +=
delta;
1226 for (
const NodeIndex subnode : SubNodes(n)) {
1227 for (
const EdgeIndex e : graph_[subnode]) {
1228 Edge& edge = edges_[e];
1229 const NodeIndex other_end = OtherEnd(edge, subnode);
1230 if (other_end == n)
continue;
1231 edges_[e].slack -=
delta;
1238 const Node& tail_node = nodes_[Tail(edge)];
1239 const Node& head_node = nodes_[Head(edge)];
1241 if (Tail(edge) == Head(edge))
return slack;
1244 slack -= tail_node.
type * nodes_[tail_node.
root].tree_dual_delta +
1245 head_node.
type * nodes_[head_node.
root].tree_dual_delta;
1248 DCHECK_EQ(slack, edge.
slack) << tail_node.
type <<
" " << head_node.
type
1249 <<
" " << Tail(edge) <<
"<->" << Head(edge);
1259 DCHECK_EQ(dual, node.
dual);
1267 CHECK_EQ(dual_objective_ % 2, 0);
1268 return dual_objective_ / 2;
1277 VLOG(1) <<
"num_grows: " << num_grows_;
1278 VLOG(1) <<
"num_augments: " << num_augments_;
1279 VLOG(1) <<
"num_shrinks: " << num_shrinks_;
1280 VLOG(1) <<
"num_expands: " << num_expands_;
1281 VLOG(1) <<
"num_dual_updates: " << num_dual_updates_;
iterator insert(const_iterator pos, const value_type &x)
void resize(size_type new_size)
void reserve(size_type n)
void push_back(const value_type &x)
bool DebugDualsAreFeasible() const
ABSL_MUST_USE_RESULT bool Initialize()
CostValue DualObjective() const
void Grow(EdgeIndex e, NodeIndex tail, NodeIndex head)
CostValue ComputeMaxCommonTreeDualDeltaAndResetPrimalEdgeQueue()
bool DebugEdgeIsTightAndExternal(const Edge &edge) const
static const CostValue kMaxCostValue
NodeIndex Match(NodeIndex n) const
void AddEdge(NodeIndex tail, NodeIndex head, CostValue cost)
bool NodeIsMatched(NodeIndex n) const
CostValue Slack(const Edge &edge) const
std::string NodeDebugString(NodeIndex n) const
std::string EdgeDebugString(EdgeIndex e) const
void DebugCheckNoPossiblePrimalUpdates()
std::string DebugString() const
void Augment(EdgeIndex e)
CostValue Dual(const Node &node) const
static const EdgeIndex kNoEdgeIndex
static const NodeIndex kNoNodeIndex
void Expand(NodeIndex to_expand)
void UpdateAllTrees(CostValue delta)
void DisplayStats() const
void DebugUpdateNodeDual(NodeIndex n, CostValue delta)
BlossomGraph(int num_nodes)
ABSL_MUST_USE_RESULT Status Solve()
void AddEdgeWithCost(int tail, int head, int64_t cost)
void Reset(int num_nodes)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
NodeIndex OtherEnd(NodeIndex n) const
std::vector< NodeIndex > blossom
CostValue saved_pseudo_dual
std::vector< NodeIndex > saved_blossom
#define VLOG(verboselevel)