OR-Tools  9.3
perfect_matching.cc
Go to the documentation of this file.
1// Copyright 2010-2021 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <cstdint>
17#include <limits>
18
19#include "absl/memory/memory.h"
21
22namespace operations_research {
23
24void MinCostPerfectMatching::Reset(int num_nodes) {
25 graph_ = absl::make_unique<BlossomGraph>(num_nodes);
26 optimal_cost_ = 0;
27 matches_.assign(num_nodes, -1);
28}
29
31 CHECK_GE(cost, 0) << "Not supported for now, just shift your costs.";
32 if (tail == head) {
33 VLOG(1) << "Ignoring self-arc: " << tail << " <-> " << head
34 << " cost: " << cost;
35 return;
36 }
37 maximum_edge_cost_ = std::max(maximum_edge_cost_, cost);
40}
41
43 optimal_solution_found_ = false;
44
45 // We want all dual and all slack value to never overflow. After Initialize()
46 // they are both bounded by the 2 * maximum cost. And we track an upper bound
47 // on these quantities. The factor two is because of the re-scaling we do
48 // internally since all our dual values are actually multiple of 1/2.
49 //
50 // Note that since the whole code in BlossomGraph assumes that dual/slack have
51 // a magnitude that is always lower than kMaxCostValue it is important to use
52 // it here since there is no reason it cannot be smaller than kint64max.
53 //
54 // TODO(user): Improve the overflow detection if needed. The current one seems
55 // ok though.
56 int64_t overflow_detection = CapAdd(maximum_edge_cost_, maximum_edge_cost_);
57 if (overflow_detection >= BlossomGraph::kMaxCostValue) {
58 return Status::INTEGER_OVERFLOW;
59 }
60
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();
66
67 while (graph_->NumMatched() != num_nodes) {
68 graph_->PrimalUpdates();
69 if (DEBUG_MODE) {
70 graph_->DebugCheckNoPossiblePrimalUpdates();
71 }
72
73 VLOG(1) << "num_unmatched: " << num_nodes - graph_->NumMatched()
74 << " dual_objective: " << graph_->DualObjective();
75 if (graph_->NumMatched() == num_nodes) break;
76
78 graph_->ComputeMaxCommonTreeDualDeltaAndResetPrimalEdgeQueue();
79 overflow_detection = CapAdd(overflow_detection, std::abs(delta.value()));
80 if (overflow_detection >= BlossomGraph::kMaxCostValue) {
81 return Status::INTEGER_OVERFLOW;
82 }
83
84 if (delta == 0) break; // Infeasible!
85 graph_->UpdateAllTrees(delta);
86 }
87
88 VLOG(1) << "End: " << graph_->NumMatched() << " / " << num_nodes;
89 graph_->DisplayStats();
90 if (graph_->NumMatched() < num_nodes) {
91 return Status::INFEASIBLE;
92 }
93 VLOG(2) << graph_->DebugString();
94 CHECK(graph_->DebugDualsAreFeasible());
95
96 // TODO(user): Maybe there is a faster/better way to recover the mapping
97 // in the presence of blossoms.
98 graph_->ExpandAllBlossoms();
99 for (int i = 0; i < num_nodes; ++i) {
100 matches_[i] = graph_->Match(BlossomGraph::NodeIndex(i)).value();
101 }
102
103 optimal_solution_found_ = true;
104 optimal_cost_ = graph_->DualObjective().value();
105 if (optimal_cost_ == std::numeric_limits<int64_t>::max())
106 return Status::COST_OVERFLOW;
107 return Status::OPTIMAL;
108}
109
112
115const BlossomGraph::EdgeIndex BlossomGraph::kNoEdgeIndex =
116 BlossomGraph::EdgeIndex(-1);
119
121 graph_.resize(num_nodes);
122 nodes_.reserve(num_nodes);
123 root_blossom_node_.resize(num_nodes);
124 for (NodeIndex n(0); n < num_nodes; ++n) {
125 root_blossom_node_[n] = n;
126 nodes_.push_back(Node(n));
127 }
128}
129
131 DCHECK_GE(tail, 0);
132 DCHECK_LT(tail, nodes_.size());
133 DCHECK_GE(head, 0);
134 DCHECK_LT(head, nodes_.size());
135 DCHECK_GE(cost, 0);
136 DCHECK(!is_initialized_);
137 const EdgeIndex index(edges_.size());
138 edges_.push_back(Edge(tail, head, cost));
139 graph_[tail].push_back(index);
140 graph_[head].push_back(index);
141}
142
143// TODO(user): Code the more advanced "Fractional matching initialization"
144// heuristic.
145//
146// TODO(user): Add a preprocessing step that performs the 'forced' matches?
148 CHECK(!is_initialized_);
149 is_initialized_ = true;
150
151 for (NodeIndex n(0); n < nodes_.size(); ++n) {
152 if (graph_[n].empty()) return false; // INFEASIBLE.
153 CostValue min_cost = kMaxCostValue;
154
155 // Initialize the dual of each nodes to min_cost / 2.
156 //
157 // TODO(user): We might be able to do better for odd min_cost, but then
158 // we might need to scale by 4? think about it.
159 for (const EdgeIndex e : graph_[n]) {
160 min_cost = std::min(min_cost, edges_[e].pseudo_slack);
161 }
162 DCHECK_NE(min_cost, kMaxCostValue);
163 nodes_[n].pseudo_dual = min_cost / 2;
164
165 // Starts with all nodes as tree roots.
166 nodes_[n].type = 1;
167 }
168
169 // Update the slack of each edges now that nodes might have non-zero duals.
170 // Note that we made sure that all updated slacks are non-negative.
171 for (EdgeIndex e(0); e < edges_.size(); ++e) {
172 Edge& mutable_edge = edges_[e];
173 mutable_edge.pseudo_slack -= nodes_[mutable_edge.tail].pseudo_dual +
174 nodes_[mutable_edge.head].pseudo_dual;
175 DCHECK_GE(mutable_edge.pseudo_slack, 0);
176 }
177
178 for (NodeIndex n(0); n < nodes_.size(); ++n) {
179 if (NodeIsMatched(n)) continue;
180
181 // After this greedy update, there will be at least an edge with a
182 // slack of zero.
183 CostValue min_slack = kMaxCostValue;
184 for (const EdgeIndex e : graph_[n]) {
185 min_slack = std::min(min_slack, edges_[e].pseudo_slack);
186 }
187 DCHECK_NE(min_slack, kMaxCostValue);
188 if (min_slack > 0) {
189 nodes_[n].pseudo_dual += min_slack;
190 for (const EdgeIndex e : graph_[n]) {
191 edges_[e].pseudo_slack -= min_slack;
192 }
193 DebugUpdateNodeDual(n, min_slack);
194 }
195
196 // Match this node if possible.
197 //
198 // TODO(user): Optimize by merging this loop with the one above?
199 for (const EdgeIndex e : graph_[n]) {
200 const Edge& edge = edges_[e];
201 if (edge.pseudo_slack != 0) continue;
202 if (!NodeIsMatched(edge.OtherEnd(n))) {
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;
207 break;
208 }
209 }
210 }
211
212 // Initialize unmatched_nodes_.
213 for (NodeIndex n(0); n < nodes_.size(); ++n) {
214 if (NodeIsMatched(n)) continue;
215 unmatched_nodes_.push_back(n);
216 }
217
218 // Scale everything by 2 and update the dual cost. Note that we made sure that
219 // there cannot be an integer overflow at the beginning of Solve().
220 //
221 // This scaling allows to only have integer weights during the algorithm
222 // because the slack of [+] -- [+] edges will always stay even.
223 //
224 // TODO(user): Reduce the number of loops we do in the initialization. We
225 // could likely just scale the edge cost as we fill them.
226 for (NodeIndex n(0); n < nodes_.size(); ++n) {
227 DCHECK_LE(nodes_[n].pseudo_dual, kMaxCostValue / 2);
228 nodes_[n].pseudo_dual *= 2;
229 AddToDualObjective(nodes_[n].pseudo_dual);
230#ifndef NDEBUG
231 nodes_[n].dual = nodes_[n].pseudo_dual;
232#endif
233 }
234 for (EdgeIndex e(0); e < edges_.size(); ++e) {
235 DCHECK_LE(edges_[e].pseudo_slack, kMaxCostValue / 2);
236 edges_[e].pseudo_slack *= 2;
237#ifndef NDEBUG
238 edges_[e].slack = edges_[e].pseudo_slack;
239#endif
240 }
241
242 // Initialize the edge priority queues and the primal update queue.
243 // We only need to do that if we have unmatched nodes.
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);
256 }
257 }
258 }
259
260 return true;
261}
262
264 // TODO(user): Avoid this linear loop.
265 CostValue best_update = kMaxCostValue;
266 for (NodeIndex n(0); n < nodes_.size(); ++n) {
267 const Node& node = nodes_[n];
268 if (node.IsBlossom() && node.IsMinus()) {
269 best_update = std::min(best_update, Dual(node));
270 }
271 }
272
273 // This code only works because all tree_dual_delta are the same.
274 CHECK(!unmatched_nodes_.empty());
275 const CostValue tree_delta = nodes_[unmatched_nodes_.front()].tree_dual_delta;
276 CostValue plus_plus_slack = kMaxCostValue;
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);
281 }
282 CostValue plus_free_slack = kMaxCostValue;
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);
286 }
287
288 // This means infeasible, and returning zero will abort the search.
289 if (best_update == kMaxCostValue) return CostValue(0);
290
291 // Initialize primal_update_edge_queue_ with all the edges that will have a
292 // slack of zero once we apply the update.
293 //
294 // NOTE(user): If we want more "determinism" and be independent on the PQ
295 // algorithm, we could std::sort() the primal_update_edge_queue_ here.
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()));
301 }
302 }
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()));
307 }
308 }
309
310 return best_update;
311}
312
314 ++num_dual_updates_;
315
316 // Reminder: the tree roots are exactly the unmatched nodes.
317 CHECK_GE(delta, 0);
318 for (const NodeIndex n : unmatched_nodes_) {
319 CHECK(!NodeIsMatched(n));
320 AddToDualObjective(delta);
321 nodes_[n].tree_dual_delta += delta;
322 }
323
324 if (DEBUG_MODE) {
325 for (NodeIndex n(0); n < nodes_.size(); ++n) {
326 const Node& node = nodes_[n];
327 if (node.IsPlus()) DebugUpdateNodeDual(n, delta);
328 if (node.IsMinus()) DebugUpdateNodeDual(n, -delta);
329 }
330 }
331}
332
334 // An unmatched node must be a tree root.
335 const Node& node = nodes_[n];
336 CHECK(node.match != n || (node.root == n && node.IsPlus()));
337 return node.match != n;
338}
339
341 const Node& node = nodes_[n];
342 if (DEBUG_MODE) {
343 if (node.IsMinus()) CHECK_EQ(node.parent, node.match);
344 if (node.IsPlus()) CHECK_EQ(n, node.match);
345 }
346 return node.match;
347}
348
349// Meant to only be used in DEBUG to make sure our queue in PrimalUpdates()
350// do not miss any potential edges.
352 for (EdgeIndex e(0); e < edges_.size(); ++e) {
353 const Edge& edge = edges_[e];
354 if (Head(edge) == Tail(edge)) continue;
355
356 CHECK(!nodes_[Tail(edge)].is_internal);
357 CHECK(!nodes_[Head(edge)].is_internal);
358 if (Slack(edge) != 0) continue;
359
360 // Make sure tail is a plus node if possible.
361 NodeIndex tail = Tail(edge);
362 NodeIndex head = Head(edge);
363 if (!nodes_[tail].IsPlus()) std::swap(tail, head);
364 if (!nodes_[tail].IsPlus()) continue;
365
366 if (nodes_[head].IsFree()) {
367 VLOG(2) << DebugString();
368 LOG(FATAL) << "Possible Grow! " << tail << " " << head;
369 }
370 if (nodes_[head].IsPlus()) {
371 if (nodes_[tail].root == nodes_[head].root) {
372 LOG(FATAL) << "Possible Shrink!";
373 } else {
374 LOG(FATAL) << "Possible augment!";
375 }
376 }
377 }
378 for (const Node& node : nodes_) {
379 if (node.IsMinus() && node.IsBlossom() && Dual(node) == 0) {
380 LOG(FATAL) << "Possible expand!";
381 }
382 }
383}
384
386 // Any Grow/Augment/Shrink/Expand operation can add new tight edges that need
387 // to be explored again.
388 //
389 // TODO(user): avoid adding duplicates?
390 while (true) {
391 possible_shrink_.clear();
392
393 // First, we Grow/Augment as much as possible.
394 while (!primal_update_edge_queue_.empty()) {
395 const EdgeIndex e = primal_update_edge_queue_.back();
396 primal_update_edge_queue_.pop_back();
397
398 // Because of the Expand() operation, the edge may have become un-tight
399 // since it has been inserted in the tight edges queue. It's cheaper to
400 // detect it here and skip it than it would be to dynamically update the
401 // queue to only keep actually tight edges at all times.
402 const Edge& edge = edges_[e];
403 if (Slack(edge) != 0) continue;
404
405 NodeIndex tail = Tail(edge);
406 NodeIndex head = Head(edge);
407 if (!nodes_[tail].IsPlus()) std::swap(tail, head);
408 if (!nodes_[tail].IsPlus()) continue;
409
410 if (nodes_[head].IsFree()) {
411 Grow(e, tail, head);
412 } else if (nodes_[head].IsPlus()) {
413 if (nodes_[tail].root != nodes_[head].root) {
414 Augment(e);
415 } else {
416 possible_shrink_.push_back(e);
417 }
418 }
419 }
420
421 // Shrink all potential Blossom.
422 for (const EdgeIndex e : possible_shrink_) {
423 const Edge& edge = edges_[e];
424 const NodeIndex tail = Tail(edge);
425 const NodeIndex head = Head(edge);
426 const Node& tail_node = nodes_[tail];
427 const Node& head_node = nodes_[head];
428 if (tail_node.IsPlus() && head_node.IsPlus() &&
429 tail_node.root == head_node.root && tail != head) {
430 Shrink(e);
431 }
432 }
433
434 // Delay expand if any blossom was created.
435 if (!primal_update_edge_queue_.empty()) continue;
436
437 // Expand Blossom if any.
438 //
439 // TODO(user): Avoid doing a O(num_nodes). Also expand all blossom
440 // recursively? I am not sure it is a good heuristic to expand all possible
441 // blossom before trying the other operations though.
442 int num_expands = 0;
443 for (NodeIndex n(0); n < nodes_.size(); ++n) {
444 const Node& node = nodes_[n];
445 if (node.IsMinus() && node.IsBlossom() && Dual(node) == 0) {
446 ++num_expands;
447 Expand(n);
448 }
449 }
450 if (num_expands == 0) break;
451 }
452}
453
455 // The slack of all edge must be non-negative.
456 for (const Edge& edge : edges_) {
457 if (Slack(edge) < 0) return false;
458 }
459
460 // The dual of all Blossom must be non-negative.
461 for (const Node& node : nodes_) {
462 if (node.IsBlossom() && Dual(node) < 0) return false;
463 }
464 return true;
465}
466
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;
472}
473
475 ++num_grows_;
476 VLOG(2) << "Grow " << tail << " -> " << head << " === " << Match(head);
477
479 DCHECK(nodes_[tail].IsPlus());
480 DCHECK(nodes_[head].IsFree());
482
483 const NodeIndex root = nodes_[tail].root;
484 const NodeIndex leaf = Match(head);
485
486 Node& head_node = nodes_[head];
487 head_node.root = root;
488 head_node.parent = tail;
489 head_node.type = -1;
490
491 // head was free and is now a [-] node.
492 const CostValue tree_dual = nodes_[root].tree_dual_delta;
493 head_node.pseudo_dual += tree_dual;
494 for (const NodeIndex subnode : SubNodes(head)) {
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;
499 edge.pseudo_slack -= tree_dual;
500 if (plus_free_pq_.Contains(&edge)) plus_free_pq_.Remove(&edge);
501 }
502 }
503
504 Node& leaf_node = nodes_[leaf];
505 leaf_node.root = root;
506 leaf_node.parent = head;
507 leaf_node.type = +1;
508
509 // leaf was free and is now a [+] node.
510 leaf_node.pseudo_dual -= tree_dual;
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;
516 edge.pseudo_slack += tree_dual;
517 const Node& other_node = nodes_[other_end];
518 if (other_node.IsPlus()) {
519 // The edge switch from [+] -- [0] to [+] -- [+].
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);
524 if (edge.pseudo_slack == 2 * tree_dual) {
525 DCHECK_EQ(Slack(edge), 0);
526 primal_update_edge_queue_.push_back(e);
527 }
528 } else if (other_node.IsFree()) {
529 // We have a new [+] -- [0] edge.
530 DCHECK(!plus_free_pq_.Contains(&edge));
531 DCHECK(!plus_plus_pq_.Contains(&edge));
532 plus_free_pq_.Add(&edge);
533 if (edge.pseudo_slack == tree_dual) {
534 DCHECK_EQ(Slack(edge), 0);
535 primal_update_edge_queue_.push_back(e);
536 }
537 }
538 }
539 }
540}
541
542void BlossomGraph::AppendNodePathToRoot(NodeIndex n,
543 std::vector<NodeIndex>* path) const {
544 while (true) {
545 path->push_back(n);
546 n = nodes_[n].parent;
547 if (n == path->back()) break;
548 }
549}
550
551void BlossomGraph::Augment(EdgeIndex e) {
552 ++num_augments_;
553
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());
559
560 const NodeIndex root_a = nodes_[Tail(edge)].root;
561 const NodeIndex root_b = nodes_[Head(edge)].root;
562 DCHECK_NE(root_a, root_b);
563
564 // Compute the path from root_a to root_b.
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);
569
570 // TODO(user): Check all dual/slack same after primal op?
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;
575
576 // Make all the nodes from both trees free while keeping the
577 // current matching.
578 //
579 // TODO(user): It seems that we may waste some computation since the part of
580 // the tree not in the path between roots can lead to the same Grow()
581 // operations later when one of its node is ratched to a new root.
582 //
583 // TODO(user): Reduce this O(num_nodes) complexity. We might be able to
584 // even do O(num_node_in_path) with lazy updates. Note that this operation
585 // will only be performed at most num_initial_unmatched_nodes / 2 times
586 // though.
587 for (NodeIndex n(0); n < nodes_.size(); ++n) {
588 Node& node = nodes_[n];
589 if (node.IsInternal()) continue;
590 const NodeIndex root = node.root;
591 if (root != root_a && root != root_b) continue;
592
593 const CostValue delta = node.type * (root == root_a ? delta_a : delta_b);
594 node.pseudo_dual += delta;
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;
600 edge.pseudo_slack -= delta;
601
602 // If the other end is not in one of the two trees, and it is a plus
603 // node, we add it the plus_free queue. All previous [+]--[0] and
604 // [+]--[+] edges need to be removed from the queues.
605 const Node& other_node = nodes_[other_end];
606 if (other_node.root != root_a && other_node.root != root_b &&
607 other_node.IsPlus()) {
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);
612 } else {
613 if (plus_plus_pq_.Contains(&edge)) plus_plus_pq_.Remove(&edge);
614 if (plus_free_pq_.Contains(&edge)) plus_free_pq_.Remove(&edge);
615 }
616 }
617 }
618
619 node.type = 0;
620 node.parent = node.root = n;
621 }
622
623 // Change the matching of nodes along node_path.
624 CHECK_EQ(node_path.size() % 2, 0);
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];
628 }
629
630 // Update unmatched_nodes_.
631 //
632 // TODO(user): This could probably be optimized if needed. But we do usually
633 // iterate a lot more over it than we update it. Note that as long as we use
634 // the same delta for all trees, this is not even needed.
635 int new_size = 0;
636 for (const NodeIndex n : unmatched_nodes_) {
637 if (!NodeIsMatched(n)) unmatched_nodes_[new_size++] = n;
638 }
639 CHECK_EQ(unmatched_nodes_.size(), new_size + 2);
640 unmatched_nodes_.resize(new_size);
641}
642
643int BlossomGraph::GetDepth(NodeIndex n) const {
644 int depth = 0;
645 while (true) {
646 const NodeIndex parent = nodes_[n].parent;
647 if (parent == n) break;
648 ++depth;
649 n = parent;
650 }
651 return depth;
652}
653
654void BlossomGraph::Shrink(EdgeIndex e) {
655 ++num_shrinks_;
656
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);
662
663 CHECK_NE(Tail(edge), Head(edge)) << e;
664
665 // Find lowest common ancestor and the two node paths to reach it. Note that
666 // we do not add it to the paths.
667 NodeIndex lca_index = kNoNodeIndex;
668 std::vector<NodeIndex> tail_path;
669 std::vector<NodeIndex> head_path;
670 {
671 NodeIndex tail = Tail(edge);
672 NodeIndex head = Head(edge);
673 int tail_depth = GetDepth(tail);
674 int head_depth = GetDepth(head);
675 if (tail_depth > head_depth) {
677 std::swap(tail_depth, head_depth);
678 }
679 VLOG(2) << "Shrink " << tail << " <-> " << head;
680
681 while (head_depth > tail_depth) {
682 head_path.push_back(head);
683 head = nodes_[head].parent;
684 --head_depth;
685 }
686 while (tail != head) {
687 DCHECK_EQ(tail_depth, head_depth);
688 DCHECK_GE(tail_depth, 0);
689 if (DEBUG_MODE) {
690 --tail_depth;
691 --head_depth;
692 }
693
694 tail_path.push_back(tail);
695 tail = nodes_[tail].parent;
696
697 head_path.push_back(head);
698 head = nodes_[head].parent;
699 }
700 lca_index = tail;
701 VLOG(2) << "LCA " << lca_index;
702 }
703 Node& lca = nodes_[lca_index];
704 DCHECK(lca.IsPlus());
705
706 // Fill the cycle.
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());
711 CHECK_EQ(blossom.size() % 2, 1);
712
713 const CostValue tree_dual = nodes_[lca.root].tree_dual_delta;
714
715 // Save all values that will be needed if we expand this Blossom later.
716 CHECK_GT(blossom.size(), 1);
717 Node& backup_node = nodes_[blossom[1]];
718#ifndef NDEBUG
719 backup_node.saved_dual = lca.dual;
720#endif
721 backup_node.saved_pseudo_dual = lca.pseudo_dual + tree_dual;
722
723 // Set the new dual of the node to zero.
724#ifndef NDEBUG
725 lca.dual = 0;
726#endif
727 lca.pseudo_dual = -tree_dual;
728 CHECK_EQ(Dual(lca), 0);
729
730 // Mark node as internal, but do not change their type to zero yet.
731 // We need to do that first to properly detect edges between two internal
732 // nodes in the second loop below.
733 for (const NodeIndex n : blossom) {
734 VLOG(2) << "blossom-node: " << NodeDebugString(n);
735 if (n != lca_index) {
736 nodes_[n].is_internal = true;
737 }
738 }
739
740 // Update the dual of all edges and the priority queueus.
741 for (const NodeIndex n : blossom) {
742 Node& mutable_node = nodes_[n];
743 const bool was_minus = mutable_node.IsMinus();
744 const CostValue slack_adjust =
745 mutable_node.IsMinus() ? tree_dual : -tree_dual;
746 if (n != lca_index) {
747 mutable_node.pseudo_dual -= slack_adjust;
748#ifndef NDEBUG
749 DCHECK_EQ(mutable_node.dual, mutable_node.pseudo_dual);
750#endif
751 mutable_node.type = 0;
752 }
753 for (const NodeIndex subnode : SubNodes(n)) {
754 // Subtle: We update root_blossom_node_ while we loop, so for new internal
755 // edges, depending if an edge "other end" appear after or before, it will
756 // not be updated. We use this to only process internal edges once.
757 root_blossom_node_[subnode] = lca_index;
758
759 for (const EdgeIndex e : graph_[subnode]) {
760 Edge& edge = edges_[e];
761 const NodeIndex other_end = OtherEnd(edge, subnode);
762
763 // Skip edge that are already internal.
764 if (other_end == n) continue;
765
766 // This internal edge was already processed from its other end, so we
767 // can just skip it.
768 if (other_end == lca_index) {
769#ifndef NDEBUG
770 DCHECK_EQ(edge.slack, Slack(edge));
771#endif
772 continue;
773 }
774
775 // This is a new-internal edge that we didn't process yet.
776 //
777 // TODO(user): It would be nicer to not to have to read the memory of
778 // the other node at all. It might be possible once we store the
779 // parent edge instead of the parent node since then we will only need
780 // to know if this edges point to a new-internal node or not.
781 Node& mutable_other_node = nodes_[other_end];
782 if (mutable_other_node.is_internal) {
783 DCHECK(!plus_free_pq_.Contains(&edge));
784 if (plus_plus_pq_.Contains(&edge)) plus_plus_pq_.Remove(&edge);
785 edge.pseudo_slack += slack_adjust;
786 edge.pseudo_slack +=
787 mutable_other_node.IsMinus() ? tree_dual : -tree_dual;
788 continue;
789 }
790
791 // Replace the parent of any child of n by lca_index.
792 if (mutable_other_node.parent == n) {
793 mutable_other_node.parent = lca_index;
794 }
795
796 // Adjust when the edge used to be connected to a [-] node now that we
797 // attach it to a [+] node. Note that if the node was [+] then the
798 // non-internal incident edges slack and type do not change.
799 if (was_minus) {
800 edge.pseudo_slack += 2 * tree_dual;
801
802 // Add it to the correct PQ.
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);
807 if (edge.pseudo_slack == 2 * tree_dual) {
808 primal_update_edge_queue_.push_back(e);
809 }
810 } else if (mutable_other_node.IsFree()) {
811 plus_free_pq_.Add(&edge);
812 if (edge.pseudo_slack == tree_dual) {
813 primal_update_edge_queue_.push_back(e);
814 }
815 }
816 }
817
818#ifndef NDEBUG
819 DCHECK_EQ(edge.slack, Slack(edge));
820#endif
821 }
822 }
823 }
824
825 DCHECK(backup_node.saved_blossom.empty());
826 backup_node.saved_blossom = std::move(lca.blossom);
827 lca.blossom = std::move(blossom);
828
829 VLOG(2) << "S result " << NodeDebugString(lca_index);
830}
831
832BlossomGraph::EdgeIndex BlossomGraph::FindTightExternalEdgeBetweenNodes(
835 DCHECK_EQ(tail, root_blossom_node_[tail]);
836 DCHECK_EQ(head, root_blossom_node_[head]);
837 for (const NodeIndex subnode : SubNodes(tail)) {
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) {
842 return e;
843 }
844 }
845 }
846 return kNoEdgeIndex;
847}
848
850 ++num_expands_;
851 VLOG(2) << "Expand " << to_expand;
852
853 Node& node_to_expand = nodes_[to_expand];
854 DCHECK(node_to_expand.IsBlossom());
855 DCHECK(node_to_expand.IsMinus());
856 DCHECK_EQ(Dual(node_to_expand), 0);
857
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);
862
863 // First, restore the saved fields.
864 Node& backup_node = nodes_[node_to_expand.blossom[1]];
865#ifndef NDEBUG
866 node_to_expand.dual = backup_node.saved_dual;
867#endif
868 node_to_expand.pseudo_dual = backup_node.saved_pseudo_dual;
869 std::vector<NodeIndex> blossom = std::move(node_to_expand.blossom);
870 node_to_expand.blossom = std::move(backup_node.saved_blossom);
871 backup_node.saved_blossom.clear();
872
873 // Restore the edges Head()/Tail().
874 for (const NodeIndex n : blossom) {
875 for (const NodeIndex subnode : SubNodes(n)) {
876 root_blossom_node_[subnode] = n;
877 }
878 }
879
880 // Now we try to find a 'blossom path' that will replace the blossom node in
881 // the alternating tree: the blossom's parent [+] node in the tree will be
882 // attached to a blossom subnode (the "path start"), the blossom's child in
883 // the tree will be attached to a blossom subnode (the "path end", which could
884 // be the same subnode or a different one), and, using the blossom cycle,
885 // we'll get a path with an odd number of blossom subnodes to connect the two
886 // (since the cycle is odd, one of the two paths will be odd too). The other
887 // subnodes of the blossom will then be made free nodes matched pairwise.
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);
892 const NodeIndex end_node =
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;
897 }
898
899 // Split the cycle in two halves: nodes in [start..end] in path1, and
900 // nodes in [end..start] in path2. Note the inclusive intervals.
901 const std::vector<NodeIndex>& cycle = blossom;
902 std::vector<NodeIndex> path1;
903 std::vector<NodeIndex> path2;
904 {
905 const int end_offset =
906 (blossom_path_end + cycle.size() - blossom_path_start) % cycle.size();
907 for (int offset = 0; offset <= /*or equal*/ cycle.size(); ++offset) {
908 const NodeIndex node =
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);
912 }
913 }
914
915 // Reverse path2 to also make it go from start to end.
916 std::reverse(path2.begin(), path2.end());
917
918 // Swap if necessary so that path1 is the odd-length one.
919 if (path1.size() % 2 == 0) path1.swap(path2);
920
921 // Use better aliases than 'path1' and 'path2' in the code below.
922 std::vector<NodeIndex>& path_in_tree = path1;
923 const std::vector<NodeIndex>& free_pairs = path2;
924
925 // Strip path2 from the start and end, which aren't needed.
926 path2.erase(path2.begin());
927 path2.pop_back();
928
929 const NodeIndex blossom_matched_node = node_to_expand.match;
930 VLOG(2) << "Path ["
931 << absl::StrJoin(path_in_tree, ", ", absl::StreamFormatter())
932 << "] === " << blossom_matched_node;
933 VLOG(2) << "Pairs ["
934 << absl::StrJoin(free_pairs, ", ", absl::StreamFormatter()) << "]";
935
936 // Restore the path in the tree, note that we append the blossom_matched_node
937 // to simplify the code:
938 // <---- Blossom ---->
939 // [-] === [+] --- [-] === [+]
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) {
944 const NodeIndex n = path_in_tree[i];
945 const bool node_is_plus = i % 2;
946
947 // Update the parent.
948 if (i == 0) {
949 // This is the path start and its parent is either itself or the parent of
950 // to_expand if there was one.
951 DCHECK(node_to_expand.parent != to_expand || n == to_expand);
952 nodes_[n].parent = node_to_expand.parent;
953 } else {
954 nodes_[n].parent = path_in_tree[i - 1];
955 }
956
957 // Update the types and matches.
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];
961
962 // Ignore the blossom_matched_node for the code below.
963 if (i + 1 == path_in_tree.size()) continue;
964
965 // Update the duals, depending on whether we have a new [+] or [-] node.
966 // Note that this is also needed for the 'root' blossom node (i=0), because
967 // we've restored its pseudo-dual from its old saved value above.
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;
975
976 edge.pseudo_slack -= adjust;
977
978 // non-internal edges used to be attached to the [-] node_to_expand,
979 // so we adjust their dual.
980 if (other_end != to_expand && !nodes_[other_end].is_internal) {
981 edge.pseudo_slack += tree_dual;
982 } else {
983 // This was an internal edges. For the PQ code below to be correct, we
984 // wait for its other end to have been processed by this loop already.
985 // We detect that using the fact that the type of unprocessed internal
986 // node is still zero.
987 if (nodes_[other_end].type == 0) continue;
988 }
989
990 // Update edge queues.
991 if (node_is_plus) {
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);
997 if (edge.pseudo_slack == 2 * tree_dual) {
998 primal_update_edge_queue_.push_back(e);
999 }
1000 } else if (other_node.IsFree()) {
1001 plus_free_pq_.Add(&edge);
1002 if (edge.pseudo_slack == tree_dual) {
1003 primal_update_edge_queue_.push_back(e);
1004 }
1005 }
1006 }
1007 }
1008 }
1009 }
1010
1011 // Update free nodes.
1012 for (const NodeIndex n : free_pairs) {
1013 nodes_[n].type = 0;
1014 nodes_[n].parent = n;
1015 nodes_[n].root = n;
1016
1017 // Update edges slack and priority queue for the adjacent edges.
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;
1023
1024 // non-internal edges used to be attached to the [-] node_to_expand,
1025 // so we adjust their dual.
1026 if (other_end != to_expand && !nodes_[other_end].is_internal) {
1027 edge.pseudo_slack += tree_dual;
1028 }
1029
1030 // Update PQ. Note that since this was attached to a [-] node it cannot
1031 // be in any queue. We will also never process twice the same edge here.
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);
1036 if (edge.pseudo_slack == tree_dual) {
1037 primal_update_edge_queue_.push_back(e);
1038 }
1039 }
1040 }
1041 }
1042 }
1043
1044 // Matches the free pair together.
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];
1049 }
1050
1051 // Mark all node as external. We do that last so we could easily detect old
1052 // internal edges that are now external.
1053 for (const NodeIndex n : blossom) {
1054 nodes_[n].is_internal = false;
1055 }
1056}
1057
1059 // Queue of blossoms to expand.
1060 std::vector<NodeIndex> queue;
1061 for (NodeIndex n(0); n < nodes_.size(); ++n) {
1062 Node& node = nodes_[n];
1063 if (node.IsInternal()) continue;
1064
1065 // When this is called, there should be no more trees.
1066 CHECK(node.IsFree());
1067
1068 if (node.IsBlossom()) queue.push_back(n);
1069 }
1070
1071 // TODO(user): remove duplication with expand?
1072 while (!queue.empty()) {
1073 const NodeIndex to_expand = queue.back();
1074 queue.pop_back();
1075
1076 Node& node_to_expand = nodes_[to_expand];
1077 DCHECK(node_to_expand.IsBlossom());
1078
1079 // Find the edge used to match to_expand with Match(to_expand).
1080 const EdgeIndex match_edge_index =
1081 FindTightExternalEdgeBetweenNodes(to_expand, node_to_expand.match);
1082
1083 // Restore the saved data.
1084 Node& backup_node = nodes_[node_to_expand.blossom[1]];
1085#ifndef NDEBUG
1086 node_to_expand.dual = backup_node.saved_dual;
1087#endif
1088 node_to_expand.pseudo_dual = backup_node.saved_pseudo_dual;
1089
1090 std::vector<NodeIndex> blossom = std::move(node_to_expand.blossom);
1091 node_to_expand.blossom = std::move(backup_node.saved_blossom);
1092 backup_node.saved_blossom.clear();
1093
1094 // Restore the edges Head()/Tail().
1095 for (const NodeIndex n : blossom) {
1096 for (const NodeIndex subnode : SubNodes(n)) {
1097 root_blossom_node_[subnode] = n;
1098 }
1099 }
1100
1101 // Find the index of matched_node in the blossom list.
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;
1109 break;
1110 }
1111 }
1112 CHECK_NE(internal_matched_index, -1);
1113
1114 // Amongst the node_to_expand.blossom nodes, internal_matched_index is
1115 // matched with external_matched_node and the other are matched together.
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]);
1120 }
1121
1122 // Clear root/parent/type of all internal nodes.
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;
1128 }
1129
1130 // Matches the internal node with external one.
1131 const NodeIndex external_matched_node = node_to_expand.match;
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;
1135
1136 // Matches the free pair together.
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];
1141 }
1142
1143 // Now that the expansion is done, add to the queue any sub-blossoms.
1144 for (const NodeIndex n : blossom) {
1145 if (nodes_[n].IsBlossom()) queue.push_back(n);
1146 }
1147 }
1148}
1149
1150const std::vector<NodeIndex>& BlossomGraph::SubNodes(NodeIndex n) {
1151 // This should be only called on an external node. However, in Shrink() we
1152 // mark the node as internal early, so we just make sure the node as no saved
1153 // blossom field here.
1154 DCHECK(nodes_[n].saved_blossom.empty());
1155
1156 // Expand all the inner nodes under the node n. This will not be n iff node is
1157 // is in fact a blossom.
1158 subnodes_ = {n};
1159 for (int i = 0; i < subnodes_.size(); ++i) {
1160 const Node& node = nodes_[subnodes_[i]];
1161
1162 // Since the first node in each list is always the node above, we just
1163 // skip it to avoid listing twice the nodes.
1164 if (!node.blossom.empty()) {
1165 subnodes_.insert(subnodes_.end(), node.blossom.begin() + 1,
1166 node.blossom.end());
1167 }
1168
1169 // We also need to recursively expand the sub-blossom nodes, which are (if
1170 // any) in the "saved_blossom" field of the first internal node of each
1171 // blossom. Since we iterate on all internal nodes here, we simply consult
1172 // the "saved_blossom" field of all subnodes, and it works the same.
1173 if (!node.saved_blossom.empty()) {
1174 subnodes_.insert(subnodes_.end(), node.saved_blossom.begin() + 1,
1175 node.saved_blossom.end());
1176 }
1177 }
1178 return subnodes_;
1179}
1180
1182 const Node& node = nodes_[n];
1183 if (node.is_internal) {
1184 return absl::StrCat("[I] #", n.value());
1185 }
1186 const std::string type = !NodeIsMatched(n) ? "[*]"
1187 : node.type == 1 ? "[+]"
1188 : node.type == -1 ? "[-]"
1189 : "[0]";
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()),
1194 "]");
1195}
1196
1197std::string BlossomGraph::EdgeDebugString(EdgeIndex e) const {
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(),
1201 " internal ");
1202 }
1203 return absl::StrCat(Tail(edge).value(), "<->", Head(edge).value(),
1204 " slack: ", Slack(edge).value());
1205}
1206
1207std::string BlossomGraph::DebugString() const {
1208 std::string result = "Graph:\n";
1209 for (NodeIndex n(0); n < nodes_.size(); ++n) {
1210 absl::StrAppend(&result, NodeDebugString(n), "\n");
1211 }
1212 for (EdgeIndex e(0); e < edges_.size(); ++e) {
1213 absl::StrAppend(&result, EdgeDebugString(e), "\n");
1214 }
1215 return result;
1216}
1217
1219#ifndef NDEBUG
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;
1227 }
1228 }
1229#endif
1230}
1231
1233 const Node& tail_node = nodes_[Tail(edge)];
1234 const Node& head_node = nodes_[Head(edge)];
1235 CostValue slack = edge.pseudo_slack;
1236 if (Tail(edge) == Head(edge)) return slack; // Internal...
1237
1238 if (!tail_node.is_internal && !head_node.is_internal) {
1239 slack -= tail_node.type * nodes_[tail_node.root].tree_dual_delta +
1240 head_node.type * nodes_[head_node.root].tree_dual_delta;
1241 }
1242#ifndef NDEBUG
1243 DCHECK_EQ(slack, edge.slack) << tail_node.type << " " << head_node.type
1244 << " " << Tail(edge) << "<->" << Head(edge);
1245#endif
1246 return slack;
1247}
1248
1249// Returns the dual value of the given node (which might be a pseudo-node).
1251 const CostValue dual =
1252 node.pseudo_dual + node.type * nodes_[node.root].tree_dual_delta;
1253#ifndef NDEBUG
1254 DCHECK_EQ(dual, node.dual);
1255#endif
1256 return dual;
1257}
1258
1260 if (dual_objective_ == std::numeric_limits<int64_t>::max())
1262 CHECK_EQ(dual_objective_ % 2, 0);
1263 return dual_objective_ / 2;
1264}
1265
1266void BlossomGraph::AddToDualObjective(CostValue delta) {
1267 CHECK_GE(delta, 0);
1268 dual_objective_ = CostValue(CapAdd(dual_objective_.value(), delta.value()));
1269}
1270
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_;
1277}
1278
1279} // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK(condition)
Definition: base/logging.h:495
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:893
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:892
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:703
#define CHECK_GE(val1, val2)
Definition: base/logging.h:707
#define CHECK_GT(val1, val2)
Definition: base/logging.h:708
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:895
#define CHECK_NE(val1, val2)
Definition: base/logging.h:704
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:894
#define LOG(severity)
Definition: base/logging.h:420
#define DCHECK(condition)
Definition: base/logging.h:890
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:891
#define VLOG(verboselevel)
Definition: base/logging.h:984
iterator insert(const_iterator pos, const value_type &x)
void resize(size_type new_size)
void reserve(size_type n)
size_type size() const
void push_back(const value_type &x)
ABSL_MUST_USE_RESULT bool Initialize()
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
CostValue Dual(const Node &node) const
static const EdgeIndex kNoEdgeIndex
static const NodeIndex kNoNodeIndex
void Expand(NodeIndex to_expand)
void UpdateAllTrees(CostValue delta)
void DebugUpdateNodeDual(NodeIndex n, CostValue delta)
void AddEdgeWithCost(int tail, int head, int64_t cost)
int64_t value
int index
const int FATAL
Definition: log_severity.h:32
const bool DEBUG_MODE
Definition: macros.h:24
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:262
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
int64_t delta
Definition: resource.cc:1694
int64_t tail
int64_t cost
int64_t head
NodeIndex OtherEnd(NodeIndex n) const