19#include "absl/memory/memory.h"
25 graph_ = absl::make_unique<BlossomGraph>(num_nodes);
27 matches_.assign(num_nodes, -1);
31 CHECK_GE(
cost, 0) <<
"Not supported for now, just shift your costs.";
33 VLOG(1) <<
"Ignoring self-arc: " <<
tail <<
" <-> " <<
head
43 optimal_solution_found_ =
false;
56 int64_t overflow_detection =
CapAdd(maximum_edge_cost_, maximum_edge_cost_);
58 return Status::INTEGER_OVERFLOW;
61 const int num_nodes = matches_.size();
62 if (!graph_->Initialize())
return Status::INFEASIBLE;
63 VLOG(2) << graph_->DebugString();
64 VLOG(1) <<
"num_unmatched: " << num_nodes - graph_->NumMatched()
65 <<
" dual_objective: " << graph_->DualObjective();
67 while (graph_->NumMatched() != num_nodes) {
68 graph_->PrimalUpdates();
70 graph_->DebugCheckNoPossiblePrimalUpdates();
73 VLOG(1) <<
"num_unmatched: " << num_nodes - graph_->NumMatched()
74 <<
" dual_objective: " << graph_->DualObjective();
75 if (graph_->NumMatched() == num_nodes)
break;
78 graph_->ComputeMaxCommonTreeDualDeltaAndResetPrimalEdgeQueue();
79 overflow_detection =
CapAdd(overflow_detection, std::abs(
delta.value()));
81 return Status::INTEGER_OVERFLOW;
84 if (
delta == 0)
break;
85 graph_->UpdateAllTrees(
delta);
88 VLOG(1) <<
"End: " << graph_->NumMatched() <<
" / " << num_nodes;
89 graph_->DisplayStats();
90 if (graph_->NumMatched() < num_nodes) {
91 return Status::INFEASIBLE;
93 VLOG(2) << graph_->DebugString();
94 CHECK(graph_->DebugDualsAreFeasible());
98 graph_->ExpandAllBlossoms();
99 for (
int i = 0; i < num_nodes; ++i) {
103 optimal_solution_found_ =
true;
104 optimal_cost_ = graph_->DualObjective().value();
106 return Status::COST_OVERFLOW;
107 return Status::OPTIMAL;
116 BlossomGraph::EdgeIndex(-1);
123 root_blossom_node_.
resize(num_nodes);
124 for (
NodeIndex n(0); n < num_nodes; ++n) {
125 root_blossom_node_[n] = n;
137 const EdgeIndex
index(edges_.size());
148 CHECK(!is_initialized_);
149 is_initialized_ =
true;
152 if (graph_[n].empty())
return false;
159 for (
const EdgeIndex e : graph_[n]) {
160 min_cost =
std::min(min_cost, edges_[e].pseudo_slack);
163 nodes_[n].pseudo_dual = min_cost / 2;
171 for (EdgeIndex e(0); e < edges_.size(); ++e) {
172 Edge& mutable_edge = edges_[e];
174 nodes_[mutable_edge.
head].pseudo_dual;
184 for (
const EdgeIndex e : graph_[n]) {
185 min_slack =
std::min(min_slack, edges_[e].pseudo_slack);
189 nodes_[n].pseudo_dual += min_slack;
190 for (
const EdgeIndex e : graph_[n]) {
191 edges_[e].pseudo_slack -= min_slack;
199 for (
const EdgeIndex e : graph_[n]) {
200 const Edge& edge = edges_[e];
203 nodes_[edge.
tail].type = 0;
204 nodes_[edge.
tail].match = edge.
head;
205 nodes_[edge.
head].type = 0;
206 nodes_[edge.
head].match = edge.
tail;
215 unmatched_nodes_.push_back(n);
228 nodes_[n].pseudo_dual *= 2;
229 AddToDualObjective(nodes_[n].pseudo_dual);
231 nodes_[n].dual = nodes_[n].pseudo_dual;
234 for (EdgeIndex e(0); e < edges_.size(); ++e) {
236 edges_[e].pseudo_slack *= 2;
238 edges_[e].slack = edges_[e].pseudo_slack;
244 if (!unmatched_nodes_.empty()) {
245 primal_update_edge_queue_.clear();
246 for (EdgeIndex e(0); e < edges_.size(); ++e) {
247 Edge& edge = edges_[e];
248 const bool tail_is_plus = nodes_[edge.
tail].IsPlus();
249 const bool head_is_plus = nodes_[edge.
head].IsPlus();
250 if (tail_is_plus && head_is_plus) {
251 plus_plus_pq_.Add(&edge);
252 if (edge.
pseudo_slack == 0) primal_update_edge_queue_.push_back(e);
253 }
else if (tail_is_plus || head_is_plus) {
254 plus_free_pq_.Add(&edge);
255 if (edge.
pseudo_slack == 0) primal_update_edge_queue_.push_back(e);
267 const Node& node = nodes_[n];
274 CHECK(!unmatched_nodes_.empty());
275 const CostValue tree_delta = nodes_[unmatched_nodes_.front()].tree_dual_delta;
277 if (!plus_plus_pq_.IsEmpty()) {
278 DCHECK_EQ(plus_plus_pq_.Top()->pseudo_slack % 2, 0) <<
"Non integer bound!";
279 plus_plus_slack = plus_plus_pq_.Top()->pseudo_slack / 2 - tree_delta;
280 best_update =
std::min(best_update, plus_plus_slack);
283 if (!plus_free_pq_.IsEmpty()) {
284 plus_free_slack = plus_free_pq_.Top()->pseudo_slack - tree_delta;
285 best_update =
std::min(best_update, plus_free_slack);
296 primal_update_edge_queue_.clear();
297 if (plus_plus_slack == best_update) {
298 plus_plus_pq_.AllTop(&tmp_all_tops_);
299 for (
const Edge* pt : tmp_all_tops_) {
300 primal_update_edge_queue_.push_back(EdgeIndex(pt - &edges_.front()));
303 if (plus_free_slack == best_update) {
304 plus_free_pq_.AllTop(&tmp_all_tops_);
305 for (
const Edge* pt : tmp_all_tops_) {
306 primal_update_edge_queue_.push_back(EdgeIndex(pt - &edges_.front()));
318 for (
const NodeIndex n : unmatched_nodes_) {
320 AddToDualObjective(
delta);
321 nodes_[n].tree_dual_delta +=
delta;
326 const Node& node = nodes_[n];
335 const Node& node = nodes_[n];
337 return node.
match != n;
341 const Node& node = nodes_[n];
352 for (EdgeIndex e(0); e < edges_.size(); ++e) {
353 const Edge& edge = edges_[e];
354 if (Head(edge) == Tail(edge))
continue;
356 CHECK(!nodes_[Tail(edge)].is_internal);
357 CHECK(!nodes_[Head(edge)].is_internal);
358 if (
Slack(edge) != 0)
continue;
364 if (!nodes_[
tail].IsPlus())
continue;
366 if (nodes_[
head].IsFree()) {
370 if (nodes_[
head].IsPlus()) {
371 if (nodes_[
tail].root == nodes_[
head].root) {
378 for (
const Node& node : nodes_) {
379 if (node.IsMinus() && node.IsBlossom() &&
Dual(node) == 0) {
391 possible_shrink_.clear();
394 while (!primal_update_edge_queue_.empty()) {
395 const EdgeIndex e = primal_update_edge_queue_.back();
396 primal_update_edge_queue_.pop_back();
402 const Edge& edge = edges_[e];
403 if (
Slack(edge) != 0)
continue;
408 if (!nodes_[
tail].IsPlus())
continue;
410 if (nodes_[
head].IsFree()) {
412 }
else if (nodes_[
head].IsPlus()) {
413 if (nodes_[
tail].root != nodes_[
head].root) {
416 possible_shrink_.push_back(e);
422 for (
const EdgeIndex e : possible_shrink_) {
423 const Edge& edge = edges_[e];
426 const Node& tail_node = nodes_[
tail];
427 const Node& head_node = nodes_[
head];
435 if (!primal_update_edge_queue_.empty())
continue;
444 const Node& node = nodes_[n];
450 if (num_expands == 0)
break;
456 for (
const Edge& edge : edges_) {
457 if (
Slack(edge) < 0)
return false;
461 for (
const Node& node : nodes_) {
462 if (node.IsBlossom() &&
Dual(node) < 0)
return false;
468 if (Tail(edge) == Head(edge))
return false;
469 if (nodes_[Tail(edge)].IsInternal())
return false;
470 if (nodes_[Head(edge)].IsInternal())
return false;
471 return Slack(edge) == 0;
487 head_node.
root = root;
492 const CostValue tree_dual = nodes_[root].tree_dual_delta;
495 for (
const EdgeIndex e : graph_[subnode]) {
496 Edge& edge = edges_[e];
497 const NodeIndex other_end = OtherEnd(edge, subnode);
498 if (other_end ==
head)
continue;
500 if (plus_free_pq_.Contains(&edge)) plus_free_pq_.Remove(&edge);
504 Node& leaf_node = nodes_[leaf];
505 leaf_node.
root = root;
511 for (
const NodeIndex subnode : SubNodes(leaf)) {
512 for (
const EdgeIndex e : graph_[subnode]) {
513 Edge& edge = edges_[e];
514 const NodeIndex other_end = OtherEnd(edge, subnode);
515 if (other_end == leaf)
continue;
517 const Node& other_node = nodes_[other_end];
518 if (other_node.
IsPlus()) {
520 DCHECK(plus_free_pq_.Contains(&edge));
521 DCHECK(!plus_plus_pq_.Contains(&edge));
522 plus_free_pq_.Remove(&edge);
523 plus_plus_pq_.Add(&edge);
526 primal_update_edge_queue_.push_back(e);
528 }
else if (other_node.
IsFree()) {
530 DCHECK(!plus_free_pq_.Contains(&edge));
531 DCHECK(!plus_plus_pq_.Contains(&edge));
532 plus_free_pq_.Add(&edge);
535 primal_update_edge_queue_.push_back(e);
542void BlossomGraph::AppendNodePathToRoot(
NodeIndex n,
543 std::vector<NodeIndex>* path)
const {
546 n = nodes_[n].parent;
547 if (n == path->back())
break;
554 const Edge& edge = edges_[e];
555 VLOG(2) <<
"Augment " << Tail(edge) <<
" -> " << Head(edge);
557 DCHECK(nodes_[Tail(edge)].IsPlus());
558 DCHECK(nodes_[Head(edge)].IsPlus());
560 const NodeIndex root_a = nodes_[Tail(edge)].root;
561 const NodeIndex root_b = nodes_[Head(edge)].root;
565 std::vector<NodeIndex> node_path;
566 AppendNodePathToRoot(Tail(edge), &node_path);
567 std::reverse(node_path.begin(), node_path.end());
568 AppendNodePathToRoot(Head(edge), &node_path);
571 const CostValue delta_a = nodes_[root_a].tree_dual_delta;
572 const CostValue delta_b = nodes_[root_b].tree_dual_delta;
573 nodes_[root_a].tree_dual_delta = 0;
574 nodes_[root_b].tree_dual_delta = 0;
588 Node& node = nodes_[n];
591 if (root != root_a && root != root_b)
continue;
595 for (
const NodeIndex subnode : SubNodes(n)) {
596 for (
const EdgeIndex e : graph_[subnode]) {
597 Edge& edge = edges_[e];
598 const NodeIndex other_end = OtherEnd(edge, subnode);
599 if (other_end == n)
continue;
605 const Node& other_node = nodes_[other_end];
606 if (other_node.
root != root_a && other_node.
root != root_b &&
608 if (plus_plus_pq_.Contains(&edge)) plus_plus_pq_.Remove(&edge);
609 DCHECK(!plus_free_pq_.Contains(&edge));
610 plus_free_pq_.Add(&edge);
611 if (
Slack(edge) == 0) primal_update_edge_queue_.push_back(e);
613 if (plus_plus_pq_.Contains(&edge)) plus_plus_pq_.Remove(&edge);
614 if (plus_free_pq_.Contains(&edge)) plus_free_pq_.Remove(&edge);
625 for (
int i = 0; i < node_path.size(); i += 2) {
626 nodes_[node_path[i]].match = node_path[i + 1];
627 nodes_[node_path[i + 1]].match = node_path[i];
636 for (
const NodeIndex n : unmatched_nodes_) {
639 CHECK_EQ(unmatched_nodes_.size(), new_size + 2);
640 unmatched_nodes_.resize(new_size);
643int BlossomGraph::GetDepth(
NodeIndex n)
const {
646 const NodeIndex parent = nodes_[n].parent;
647 if (parent == n)
break;
657 const Edge& edge = edges_[e];
659 DCHECK(nodes_[Tail(edge)].IsPlus());
660 DCHECK(nodes_[Head(edge)].IsPlus());
661 DCHECK_EQ(nodes_[Tail(edge)].root, nodes_[Head(edge)].root);
663 CHECK_NE(Tail(edge), Head(edge)) << e;
668 std::vector<NodeIndex> tail_path;
669 std::vector<NodeIndex> head_path;
673 int tail_depth = GetDepth(
tail);
674 int head_depth = GetDepth(
head);
675 if (tail_depth > head_depth) {
681 while (head_depth > tail_depth) {
682 head_path.push_back(
head);
694 tail_path.push_back(
tail);
701 VLOG(2) <<
"LCA " << lca_index;
703 Node& lca = nodes_[lca_index];
707 std::vector<NodeIndex> blossom = {lca_index};
708 std::reverse(head_path.begin(), head_path.end());
709 blossom.insert(blossom.end(), head_path.begin(), head_path.end());
710 blossom.insert(blossom.end(), tail_path.begin(), tail_path.end());
713 const CostValue tree_dual = nodes_[lca.
root].tree_dual_delta;
717 Node& backup_node = nodes_[blossom[1]];
735 if (n != lca_index) {
736 nodes_[n].is_internal =
true;
742 Node& mutable_node = nodes_[n];
743 const bool was_minus = mutable_node.
IsMinus();
745 mutable_node.
IsMinus() ? tree_dual : -tree_dual;
746 if (n != lca_index) {
751 mutable_node.
type = 0;
753 for (
const NodeIndex subnode : SubNodes(n)) {
757 root_blossom_node_[subnode] = lca_index;
759 for (
const EdgeIndex e : graph_[subnode]) {
760 Edge& edge = edges_[e];
761 const NodeIndex other_end = OtherEnd(edge, subnode);
764 if (other_end == n)
continue;
768 if (other_end == lca_index) {
781 Node& mutable_other_node = nodes_[other_end];
783 DCHECK(!plus_free_pq_.Contains(&edge));
784 if (plus_plus_pq_.Contains(&edge)) plus_plus_pq_.Remove(&edge);
787 mutable_other_node.
IsMinus() ? tree_dual : -tree_dual;
792 if (mutable_other_node.
parent == n) {
793 mutable_other_node.
parent = lca_index;
803 DCHECK(!plus_plus_pq_.Contains(&edge));
804 DCHECK(!plus_free_pq_.Contains(&edge));
805 if (mutable_other_node.
IsPlus()) {
806 plus_plus_pq_.Add(&edge);
808 primal_update_edge_queue_.push_back(e);
810 }
else if (mutable_other_node.
IsFree()) {
811 plus_free_pq_.Add(&edge);
813 primal_update_edge_queue_.push_back(e);
827 lca.
blossom = std::move(blossom);
832BlossomGraph::EdgeIndex BlossomGraph::FindTightExternalEdgeBetweenNodes(
838 for (
const EdgeIndex e : graph_[subnode]) {
839 const Edge& edge = edges_[e];
840 const NodeIndex other_end = OtherEnd(edge, subnode);
841 if (other_end ==
head &&
Slack(edge) == 0) {
851 VLOG(2) <<
"Expand " << to_expand;
853 Node& node_to_expand = nodes_[to_expand];
858 const EdgeIndex match_edge_index =
859 FindTightExternalEdgeBetweenNodes(to_expand, node_to_expand.
match);
860 const EdgeIndex parent_edge_index =
861 FindTightExternalEdgeBetweenNodes(to_expand, node_to_expand.
parent);
864 Node& backup_node = nodes_[node_to_expand.
blossom[1]];
869 std::vector<NodeIndex> blossom = std::move(node_to_expand.
blossom);
875 for (
const NodeIndex subnode : SubNodes(n)) {
876 root_blossom_node_[subnode] = n;
888 int blossom_path_start = -1;
889 int blossom_path_end = -1;
890 const NodeIndex start_node = OtherEndFromExternalNode(
891 edges_[parent_edge_index], node_to_expand.
parent);
893 OtherEndFromExternalNode(edges_[match_edge_index], node_to_expand.
match);
894 for (
int i = 0; i < blossom.size(); ++i) {
895 if (blossom[i] == start_node) blossom_path_start = i;
896 if (blossom[i] == end_node) blossom_path_end = i;
901 const std::vector<NodeIndex>& cycle = blossom;
902 std::vector<NodeIndex> path1;
903 std::vector<NodeIndex> path2;
905 const int end_offset =
906 (blossom_path_end + cycle.size() - blossom_path_start) % cycle.size();
907 for (
int offset = 0; offset <= cycle.size(); ++offset) {
909 cycle[(blossom_path_start + offset) % cycle.size()];
910 if (offset <= end_offset) path1.push_back(node);
911 if (offset >= end_offset) path2.push_back(node);
916 std::reverse(path2.begin(), path2.end());
919 if (path1.size() % 2 == 0) path1.swap(path2);
922 std::vector<NodeIndex>& path_in_tree = path1;
923 const std::vector<NodeIndex>& free_pairs = path2;
926 path2.erase(path2.begin());
931 << absl::StrJoin(path_in_tree,
", ", absl::StreamFormatter())
932 <<
"] === " << blossom_matched_node;
934 << absl::StrJoin(free_pairs,
", ", absl::StreamFormatter()) <<
"]";
940 path_in_tree.push_back(blossom_matched_node);
941 CHECK_EQ(path_in_tree.size() % 2, 0);
942 const CostValue tree_dual = nodes_[node_to_expand.
root].tree_dual_delta;
943 for (
int i = 0; i < path_in_tree.size(); ++i) {
945 const bool node_is_plus = i % 2;
951 DCHECK(node_to_expand.
parent != to_expand || n == to_expand);
952 nodes_[n].parent = node_to_expand.
parent;
954 nodes_[n].parent = path_in_tree[i - 1];
958 nodes_[n].root = node_to_expand.
root;
959 nodes_[n].type = node_is_plus ? 1 : -1;
960 nodes_[n].match = path_in_tree[node_is_plus ? i - 1 : i + 1];
963 if (i + 1 == path_in_tree.size())
continue;
968 const CostValue adjust = node_is_plus ? -tree_dual : tree_dual;
969 nodes_[n].pseudo_dual += adjust;
970 for (
const NodeIndex subnode : SubNodes(n)) {
971 for (
const EdgeIndex e : graph_[subnode]) {
972 Edge& edge = edges_[e];
973 const NodeIndex other_end = OtherEnd(edge, subnode);
974 if (other_end == n)
continue;
980 if (other_end != to_expand && !nodes_[other_end].is_internal) {
987 if (nodes_[other_end].type == 0)
continue;
992 const Node& other_node = nodes_[other_end];
993 DCHECK(!plus_plus_pq_.Contains(&edge));
994 DCHECK(!plus_free_pq_.Contains(&edge));
995 if (other_node.
IsPlus()) {
996 plus_plus_pq_.Add(&edge);
998 primal_update_edge_queue_.push_back(e);
1000 }
else if (other_node.
IsFree()) {
1001 plus_free_pq_.Add(&edge);
1003 primal_update_edge_queue_.push_back(e);
1014 nodes_[n].parent = n;
1018 for (
const NodeIndex subnode : SubNodes(n)) {
1019 for (
const EdgeIndex e : graph_[subnode]) {
1020 Edge& edge = edges_[e];
1021 const NodeIndex other_end = OtherEnd(edge, subnode);
1022 if (other_end == n)
continue;
1026 if (other_end != to_expand && !nodes_[other_end].is_internal) {
1032 DCHECK(!plus_plus_pq_.Contains(&edge));
1033 DCHECK(!plus_free_pq_.Contains(&edge));
1034 if (nodes_[other_end].IsPlus()) {
1035 plus_free_pq_.Add(&edge);
1037 primal_update_edge_queue_.push_back(e);
1045 CHECK_EQ(free_pairs.size() % 2, 0);
1046 for (
int i = 0; i < free_pairs.size(); i += 2) {
1047 nodes_[free_pairs[i]].match = free_pairs[i + 1];
1048 nodes_[free_pairs[i + 1]].match = free_pairs[i];
1054 nodes_[n].is_internal =
false;
1060 std::vector<NodeIndex> queue;
1062 Node& node = nodes_[n];
1068 if (node.
IsBlossom()) queue.push_back(n);
1072 while (!queue.empty()) {
1073 const NodeIndex to_expand = queue.back();
1076 Node& node_to_expand = nodes_[to_expand];
1080 const EdgeIndex match_edge_index =
1081 FindTightExternalEdgeBetweenNodes(to_expand, node_to_expand.
match);
1084 Node& backup_node = nodes_[node_to_expand.
blossom[1]];
1090 std::vector<NodeIndex> blossom = std::move(node_to_expand.
blossom);
1096 for (
const NodeIndex subnode : SubNodes(n)) {
1097 root_blossom_node_[subnode] = n;
1102 int internal_matched_index = -1;
1103 const NodeIndex matched_node = OtherEndFromExternalNode(
1104 edges_[match_edge_index], node_to_expand.
match);
1105 const int size = blossom.size();
1106 for (
int i = 0; i < size; ++i) {
1107 if (blossom[i] == matched_node) {
1108 internal_matched_index = i;
1112 CHECK_NE(internal_matched_index, -1);
1116 std::vector<NodeIndex> free_pairs;
1117 for (
int i = (internal_matched_index + 1) % size;
1118 i != internal_matched_index; i = (i + 1) % size) {
1119 free_pairs.push_back(blossom[i]);
1123 for (
const NodeIndex to_clear : blossom) {
1124 nodes_[to_clear].type = 0;
1125 nodes_[to_clear].is_internal =
false;
1126 nodes_[to_clear].parent = to_clear;
1127 nodes_[to_clear].root = to_clear;
1132 const NodeIndex internal_matched_node = blossom[internal_matched_index];
1133 nodes_[internal_matched_node].match = external_matched_node;
1134 nodes_[external_matched_node].match = internal_matched_node;
1137 CHECK_EQ(free_pairs.size() % 2, 0);
1138 for (
int i = 0; i < free_pairs.size(); i += 2) {
1139 nodes_[free_pairs[i]].match = free_pairs[i + 1];
1140 nodes_[free_pairs[i + 1]].match = free_pairs[i];
1145 if (nodes_[n].IsBlossom()) queue.
push_back(n);
1150const std::vector<NodeIndex>& BlossomGraph::SubNodes(
NodeIndex n) {
1154 DCHECK(nodes_[n].saved_blossom.empty());
1159 for (
int i = 0; i < subnodes_.size(); ++i) {
1160 const Node& node = nodes_[subnodes_[i]];
1164 if (!node.blossom.empty()) {
1165 subnodes_.
insert(subnodes_.end(), node.blossom.begin() + 1,
1166 node.blossom.end());
1173 if (!node.saved_blossom.empty()) {
1174 subnodes_.insert(subnodes_.end(), node.saved_blossom.begin() + 1,
1175 node.saved_blossom.end());
1182 const Node& node = nodes_[n];
1184 return absl::StrCat(
"[I] #", n.value());
1187 : node.
type == 1 ?
"[+]"
1188 : node.
type == -1 ?
"[-]"
1190 return absl::StrCat(
1191 type,
" #", n.value(),
" dual: ",
Dual(node).
value(),
1192 " parent: ", node.
parent.value(),
" match: ", node.
match.value(),
1193 " blossom: [", absl::StrJoin(node.
blossom,
", ", absl::StreamFormatter()),
1198 const Edge& edge = edges_[e];
1199 if (nodes_[Tail(edge)].is_internal || nodes_[Head(edge)].is_internal) {
1200 return absl::StrCat(Tail(edge).
value(),
"<->", Head(edge).
value(),
1203 return absl::StrCat(Tail(edge).
value(),
"<->", Head(edge).
value(),
1208 std::string result =
"Graph:\n";
1212 for (EdgeIndex e(0); e < edges_.size(); ++e) {
1220 nodes_[n].dual +=
delta;
1221 for (
const NodeIndex subnode : SubNodes(n)) {
1222 for (
const EdgeIndex e : graph_[subnode]) {
1223 Edge& edge = edges_[e];
1224 const NodeIndex other_end = OtherEnd(edge, subnode);
1225 if (other_end == n)
continue;
1226 edges_[e].slack -=
delta;
1233 const Node& tail_node = nodes_[Tail(edge)];
1234 const Node& head_node = nodes_[Head(edge)];
1236 if (Tail(edge) == Head(edge))
return slack;
1239 slack -= tail_node.
type * nodes_[tail_node.
root].tree_dual_delta +
1240 head_node.
type * nodes_[head_node.
root].tree_dual_delta;
1244 <<
" " << Tail(edge) <<
"<->" << Head(edge);
1263 return dual_objective_ / 2;
1272 VLOG(1) <<
"num_grows: " << num_grows_;
1273 VLOG(1) <<
"num_augments: " << num_augments_;
1274 VLOG(1) <<
"num_shrinks: " << num_shrinks_;
1275 VLOG(1) <<
"num_expands: " << num_expands_;
1276 VLOG(1) <<
"num_dual_updates: " << num_dual_updates_;
#define DCHECK_LE(val1, val2)
#define DCHECK_NE(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define CHECK_GT(val1, val2)
#define DCHECK_GE(val1, val2)
#define CHECK_NE(val1, val2)
#define DCHECK_LT(val1, val2)
#define DCHECK(condition)
#define DCHECK_EQ(val1, val2)
#define VLOG(verboselevel)
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