Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
perfect_matching.cc
Go to the documentation of this file.
1// Copyright 2010-2024 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 <algorithm>
17#include <cstdint>
18#include <cstdlib>
19#include <limits>
20#include <memory>
21#include <string>
22#include <utility>
23#include <vector>
24
25#include "absl/base/log_severity.h"
26#include "absl/log/check.h"
27#include "absl/strings/str_cat.h"
28#include "absl/strings/str_join.h"
31
32namespace operations_research {
33
34void MinCostPerfectMatching::Reset(int num_nodes) {
35 graph_ = std::make_unique<BlossomGraph>(num_nodes);
36 optimal_cost_ = 0;
37 matches_.assign(num_nodes, -1);
38}
39
41 CHECK_GE(cost, 0) << "Not supported for now, just shift your costs.";
42 if (tail == head) {
43 VLOG(1) << "Ignoring self-arc: " << tail << " <-> " << head
44 << " cost: " << cost;
45 return;
46 }
47 maximum_edge_cost_ = std::max(maximum_edge_cost_, cost);
48 graph_->AddEdge(BlossomGraph::NodeIndex(tail), BlossomGraph::NodeIndex(head),
49 BlossomGraph::CostValue(cost));
50}
51
53 optimal_solution_found_ = false;
54
55 // We want all dual and all slack value to never overflow. After Initialize()
56 // they are both bounded by the 2 * maximum cost. And we track an upper bound
57 // on these quantities. The factor two is because of the re-scaling we do
58 // internally since all our dual values are actually multiple of 1/2.
59 //
60 // Note that since the whole code in BlossomGraph assumes that dual/slack have
61 // a magnitude that is always lower than kMaxCostValue it is important to use
62 // it here since there is no reason it cannot be smaller than kint64max.
63 //
64 // TODO(user): Improve the overflow detection if needed. The current one seems
65 // ok though.
66 int64_t overflow_detection = CapAdd(maximum_edge_cost_, maximum_edge_cost_);
67 if (overflow_detection >= BlossomGraph::kMaxCostValue) {
69 }
70
71 const int num_nodes = matches_.size();
72 if (!graph_->Initialize()) return Status::INFEASIBLE;
73 VLOG(2) << graph_->DebugString();
74 VLOG(1) << "num_unmatched: " << num_nodes - graph_->NumMatched()
75 << " dual_objective: " << graph_->DualObjective();
76
77 while (graph_->NumMatched() != num_nodes) {
78 graph_->PrimalUpdates();
79 if (DEBUG_MODE) {
80 graph_->DebugCheckNoPossiblePrimalUpdates();
81 }
82
83 VLOG(1) << "num_unmatched: " << num_nodes - graph_->NumMatched()
84 << " dual_objective: " << graph_->DualObjective();
85 if (graph_->NumMatched() == num_nodes) break;
86
87 const BlossomGraph::CostValue delta =
88 graph_->ComputeMaxCommonTreeDualDeltaAndResetPrimalEdgeQueue();
89 overflow_detection = CapAdd(overflow_detection, std::abs(delta.value()));
90 if (overflow_detection >= BlossomGraph::kMaxCostValue) {
92 }
93
94 if (delta == 0) break; // Infeasible!
95 graph_->UpdateAllTrees(delta);
96 }
97
98 VLOG(1) << "End: " << graph_->NumMatched() << " / " << num_nodes;
99 graph_->DisplayStats();
100 if (graph_->NumMatched() < num_nodes) {
101 return Status::INFEASIBLE;
102 }
103 VLOG(2) << graph_->DebugString();
104 CHECK(graph_->DebugDualsAreFeasible());
105
106 // TODO(user): Maybe there is a faster/better way to recover the mapping
107 // in the presence of blossoms.
108 graph_->ExpandAllBlossoms();
109 for (int i = 0; i < num_nodes; ++i) {
110 matches_[i] = graph_->Match(BlossomGraph::NodeIndex(i)).value();
111 }
112
113 optimal_solution_found_ = true;
114 optimal_cost_ = graph_->DualObjective().value();
115 if (optimal_cost_ == std::numeric_limits<int64_t>::max())
117 return Status::OPTIMAL;
118}
119
120using NodeIndex = BlossomGraph::NodeIndex;
121using CostValue = BlossomGraph::CostValue;
122
123const BlossomGraph::NodeIndex BlossomGraph::kNoNodeIndex =
124 BlossomGraph::NodeIndex(-1);
125const BlossomGraph::EdgeIndex BlossomGraph::kNoEdgeIndex =
126 BlossomGraph::EdgeIndex(-1);
127const BlossomGraph::CostValue BlossomGraph::kMaxCostValue =
128 BlossomGraph::CostValue(std::numeric_limits<int64_t>::max());
129
131 graph_.resize(num_nodes);
132 nodes_.reserve(num_nodes);
133 root_blossom_node_.resize(num_nodes);
134 for (NodeIndex n(0); n < num_nodes; ++n) {
135 root_blossom_node_[n] = n;
136 nodes_.push_back(Node(n));
137 }
138}
139
141 DCHECK_GE(tail, 0);
142 DCHECK_LT(tail, nodes_.size());
143 DCHECK_GE(head, 0);
144 DCHECK_LT(head, nodes_.size());
145 DCHECK_GE(cost, 0);
146 DCHECK(!is_initialized_);
147 const EdgeIndex index(edges_.size());
148 edges_.push_back(Edge(tail, head, cost));
149 graph_[tail].push_back(index);
150 graph_[head].push_back(index);
151}
152
153// TODO(user): Code the more advanced "Fractional matching initialization"
154// heuristic.
155//
156// TODO(user): Add a preprocessing step that performs the 'forced' matches?
158 CHECK(!is_initialized_);
159 is_initialized_ = true;
160
161 for (NodeIndex n(0); n < nodes_.size(); ++n) {
162 if (graph_[n].empty()) return false; // INFEASIBLE.
163 CostValue min_cost = kMaxCostValue;
164
165 // Initialize the dual of each nodes to min_cost / 2.
166 //
167 // TODO(user): We might be able to do better for odd min_cost, but then
168 // we might need to scale by 4? think about it.
169 for (const EdgeIndex e : graph_[n]) {
170 min_cost = std::min(min_cost, edges_[e].pseudo_slack);
171 }
172 DCHECK_NE(min_cost, kMaxCostValue);
173 nodes_[n].pseudo_dual = min_cost / 2;
174
175 // Starts with all nodes as tree roots.
176 nodes_[n].type = 1;
177 }
178
179 // Update the slack of each edges now that nodes might have non-zero duals.
180 // Note that we made sure that all updated slacks are non-negative.
181 for (EdgeIndex e(0); e < edges_.size(); ++e) {
182 Edge& mutable_edge = edges_[e];
183 mutable_edge.pseudo_slack -= nodes_[mutable_edge.tail].pseudo_dual +
184 nodes_[mutable_edge.head].pseudo_dual;
185 DCHECK_GE(mutable_edge.pseudo_slack, 0);
186 }
187
188 for (NodeIndex n(0); n < nodes_.size(); ++n) {
189 if (NodeIsMatched(n)) continue;
190
191 // After this greedy update, there will be at least an edge with a
192 // slack of zero.
193 CostValue min_slack = kMaxCostValue;
194 for (const EdgeIndex e : graph_[n]) {
195 min_slack = std::min(min_slack, edges_[e].pseudo_slack);
196 }
197 DCHECK_NE(min_slack, kMaxCostValue);
198 if (min_slack > 0) {
199 nodes_[n].pseudo_dual += min_slack;
200 for (const EdgeIndex e : graph_[n]) {
201 edges_[e].pseudo_slack -= min_slack;
202 }
203 DebugUpdateNodeDual(n, min_slack);
204 }
205
206 // Match this node if possible.
207 //
208 // TODO(user): Optimize by merging this loop with the one above?
209 for (const EdgeIndex e : graph_[n]) {
210 const Edge& edge = edges_[e];
211 if (edge.pseudo_slack != 0) continue;
212 if (!NodeIsMatched(edge.OtherEnd(n))) {
213 nodes_[edge.tail].type = 0;
214 nodes_[edge.tail].match = edge.head;
215 nodes_[edge.head].type = 0;
216 nodes_[edge.head].match = edge.tail;
217 break;
218 }
219 }
220 }
221
222 // Initialize unmatched_nodes_.
223 for (NodeIndex n(0); n < nodes_.size(); ++n) {
224 if (NodeIsMatched(n)) continue;
225 unmatched_nodes_.push_back(n);
226 }
227
228 // Scale everything by 2 and update the dual cost. Note that we made sure that
229 // there cannot be an integer overflow at the beginning of Solve().
230 //
231 // This scaling allows to only have integer weights during the algorithm
232 // because the slack of [+] -- [+] edges will always stay even.
233 //
234 // TODO(user): Reduce the number of loops we do in the initialization. We
235 // could likely just scale the edge cost as we fill them.
236 for (NodeIndex n(0); n < nodes_.size(); ++n) {
237 DCHECK_LE(nodes_[n].pseudo_dual, kMaxCostValue / 2);
238 nodes_[n].pseudo_dual *= 2;
239 AddToDualObjective(nodes_[n].pseudo_dual);
240#ifndef NDEBUG
241 nodes_[n].dual = nodes_[n].pseudo_dual;
242#endif
243 }
244 for (EdgeIndex e(0); e < edges_.size(); ++e) {
245 DCHECK_LE(edges_[e].pseudo_slack, kMaxCostValue / 2);
246 edges_[e].pseudo_slack *= 2;
247#ifndef NDEBUG
248 edges_[e].slack = edges_[e].pseudo_slack;
249#endif
250 }
251
252 // Initialize the edge priority queues and the primal update queue.
253 // We only need to do that if we have unmatched nodes.
254 if (!unmatched_nodes_.empty()) {
255 primal_update_edge_queue_.clear();
256 for (EdgeIndex e(0); e < edges_.size(); ++e) {
257 Edge& edge = edges_[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);
262 if (edge.pseudo_slack == 0) primal_update_edge_queue_.push_back(e);
263 } else if (tail_is_plus || head_is_plus) {
264 plus_free_pq_.Add(&edge);
265 if (edge.pseudo_slack == 0) primal_update_edge_queue_.push_back(e);
266 }
267 }
268 }
269
270 return true;
271}
272
274 // TODO(user): Avoid this linear loop.
275 CostValue best_update = kMaxCostValue;
276 for (NodeIndex n(0); n < nodes_.size(); ++n) {
277 const Node& node = nodes_[n];
278 if (node.IsBlossom() && node.IsMinus()) {
279 best_update = std::min(best_update, Dual(node));
280 }
281 }
282
283 // This code only works because all tree_dual_delta are the same.
284 CHECK(!unmatched_nodes_.empty());
285 const CostValue tree_delta = nodes_[unmatched_nodes_.front()].tree_dual_delta;
286 CostValue plus_plus_slack = kMaxCostValue;
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);
291 }
292 CostValue plus_free_slack = kMaxCostValue;
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);
296 }
297
298 // This means infeasible, and returning zero will abort the search.
299 if (best_update == kMaxCostValue) return CostValue(0);
300
301 // Initialize primal_update_edge_queue_ with all the edges that will have a
302 // slack of zero once we apply the update.
303 //
304 // NOTE(user): If we want more "determinism" and be independent on the PQ
305 // algorithm, we could std::sort() the primal_update_edge_queue_ here.
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()));
311 }
312 }
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()));
317 }
318 }
319
320 return best_update;
321}
322
324 ++num_dual_updates_;
325
326 // Reminder: the tree roots are exactly the unmatched nodes.
327 CHECK_GE(delta, 0);
328 for (const NodeIndex n : unmatched_nodes_) {
329 CHECK(!NodeIsMatched(n));
330 AddToDualObjective(delta);
331 nodes_[n].tree_dual_delta += delta;
332 }
333
334 if (DEBUG_MODE) {
335 for (NodeIndex n(0); n < nodes_.size(); ++n) {
336 const Node& node = nodes_[n];
337 if (node.IsPlus()) DebugUpdateNodeDual(n, delta);
338 if (node.IsMinus()) DebugUpdateNodeDual(n, -delta);
339 }
340 }
341}
342
344 // An unmatched node must be a tree root.
345 const Node& node = nodes_[n];
346 CHECK(node.match != n || (node.root == n && node.IsPlus()));
347 return node.match != n;
348}
349
351 const Node& node = nodes_[n];
352 if (DEBUG_MODE) {
353 if (node.IsMinus()) CHECK_EQ(node.parent, node.match);
354 if (node.IsPlus()) CHECK_EQ(n, node.match);
355 }
356 return node.match;
357}
358
359// Meant to only be used in DEBUG to make sure our queue in PrimalUpdates()
360// do not miss any potential edges.
362 for (EdgeIndex e(0); e < edges_.size(); ++e) {
363 const Edge& edge = edges_[e];
364 if (Head(edge) == Tail(edge)) continue;
365
366 CHECK(!nodes_[Tail(edge)].is_internal);
367 CHECK(!nodes_[Head(edge)].is_internal);
368 if (Slack(edge) != 0) continue;
369
370 // Make sure tail is a plus node if possible.
371 NodeIndex tail = Tail(edge);
372 NodeIndex head = Head(edge);
373 if (!nodes_[tail].IsPlus()) std::swap(tail, head);
374 if (!nodes_[tail].IsPlus()) continue;
375
376 if (nodes_[head].IsFree()) {
377 VLOG(2) << DebugString();
378 LOG(FATAL) << "Possible Grow! " << tail << " " << head;
379 }
380 if (nodes_[head].IsPlus()) {
381 if (nodes_[tail].root == nodes_[head].root) {
382 LOG(FATAL) << "Possible Shrink!";
383 } else {
384 LOG(FATAL) << "Possible augment!";
385 }
386 }
387 }
388 for (const Node& node : nodes_) {
389 if (node.IsMinus() && node.IsBlossom() && Dual(node) == 0) {
390 LOG(FATAL) << "Possible expand!";
391 }
392 }
393}
394
396 // Any Grow/Augment/Shrink/Expand operation can add new tight edges that need
397 // to be explored again.
398 //
399 // TODO(user): avoid adding duplicates?
400 while (true) {
401 possible_shrink_.clear();
402
403 // First, we Grow/Augment as much as possible.
404 while (!primal_update_edge_queue_.empty()) {
405 const EdgeIndex e = primal_update_edge_queue_.back();
406 primal_update_edge_queue_.pop_back();
407
408 // Because of the Expand() operation, the edge may have become un-tight
409 // since it has been inserted in the tight edges queue. It's cheaper to
410 // detect it here and skip it than it would be to dynamically update the
411 // queue to only keep actually tight edges at all times.
412 const Edge& edge = edges_[e];
413 if (Slack(edge) != 0) continue;
414
415 NodeIndex tail = Tail(edge);
416 NodeIndex head = Head(edge);
417 if (!nodes_[tail].IsPlus()) std::swap(tail, head);
418 if (!nodes_[tail].IsPlus()) continue;
419
420 if (nodes_[head].IsFree()) {
421 Grow(e, tail, head);
422 } else if (nodes_[head].IsPlus()) {
423 if (nodes_[tail].root != nodes_[head].root) {
424 Augment(e);
425 } else {
426 possible_shrink_.push_back(e);
427 }
428 }
429 }
430
431 // Shrink all potential Blossom.
432 for (const EdgeIndex e : possible_shrink_) {
433 const Edge& edge = edges_[e];
434 const NodeIndex tail = Tail(edge);
435 const NodeIndex head = Head(edge);
436 const Node& tail_node = nodes_[tail];
437 const Node& head_node = nodes_[head];
438 if (tail_node.IsPlus() && head_node.IsPlus() &&
439 tail_node.root == head_node.root && tail != head) {
440 Shrink(e);
441 }
442 }
443
444 // Delay expand if any blossom was created.
445 if (!primal_update_edge_queue_.empty()) continue;
446
447 // Expand Blossom if any.
448 //
449 // TODO(user): Avoid doing a O(num_nodes). Also expand all blossom
450 // recursively? I am not sure it is a good heuristic to expand all possible
451 // blossom before trying the other operations though.
452 int num_expands = 0;
453 for (NodeIndex n(0); n < nodes_.size(); ++n) {
454 const Node& node = nodes_[n];
455 if (node.IsMinus() && node.IsBlossom() && Dual(node) == 0) {
456 ++num_expands;
457 Expand(n);
458 }
459 }
460 if (num_expands == 0) break;
461 }
462}
463
465 // The slack of all edge must be non-negative.
466 for (const Edge& edge : edges_) {
467 if (Slack(edge) < 0) return false;
468 }
469
470 // The dual of all Blossom must be non-negative.
471 for (const Node& node : nodes_) {
472 if (node.IsBlossom() && Dual(node) < 0) return false;
473 }
474 return true;
475}
476
478 if (Tail(edge) == Head(edge)) return false;
479 if (nodes_[Tail(edge)].IsInternal()) return false;
480 if (nodes_[Head(edge)].IsInternal()) return false;
481 return Slack(edge) == 0;
482}
483
485 ++num_grows_;
486 VLOG(2) << "Grow " << tail << " -> " << head << " === " << Match(head);
487
488 DCHECK(DebugEdgeIsTightAndExternal(edges_[e]));
489 DCHECK(nodes_[tail].IsPlus());
490 DCHECK(nodes_[head].IsFree());
491 DCHECK(NodeIsMatched(head));
492
493 const NodeIndex root = nodes_[tail].root;
494 const NodeIndex leaf = Match(head);
495
496 Node& head_node = nodes_[head];
497 head_node.root = root;
498 head_node.parent = tail;
499 head_node.type = -1;
500
501 // head was free and is now a [-] node.
502 const CostValue tree_dual = nodes_[root].tree_dual_delta;
503 head_node.pseudo_dual += tree_dual;
504 for (const NodeIndex subnode : SubNodes(head)) {
505 for (const EdgeIndex e : graph_[subnode]) {
506 Edge& edge = edges_[e];
507 const NodeIndex other_end = OtherEnd(edge, subnode);
508 if (other_end == head) continue;
509 edge.pseudo_slack -= tree_dual;
510 if (plus_free_pq_.Contains(&edge)) plus_free_pq_.Remove(&edge);
511 }
512 }
513
514 Node& leaf_node = nodes_[leaf];
515 leaf_node.root = root;
516 leaf_node.parent = head;
517 leaf_node.type = +1;
518
519 // leaf was free and is now a [+] node.
520 leaf_node.pseudo_dual -= tree_dual;
521 for (const NodeIndex subnode : SubNodes(leaf)) {
522 for (const EdgeIndex e : graph_[subnode]) {
523 Edge& edge = edges_[e];
524 const NodeIndex other_end = OtherEnd(edge, subnode);
525 if (other_end == leaf) continue;
526 edge.pseudo_slack += tree_dual;
527 const Node& other_node = nodes_[other_end];
528 if (other_node.IsPlus()) {
529 // The edge switch from [+] -- [0] to [+] -- [+].
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);
534 if (edge.pseudo_slack == 2 * tree_dual) {
535 DCHECK_EQ(Slack(edge), 0);
536 primal_update_edge_queue_.push_back(e);
537 }
538 } else if (other_node.IsFree()) {
539 // We have a new [+] -- [0] edge.
540 DCHECK(!plus_free_pq_.Contains(&edge));
541 DCHECK(!plus_plus_pq_.Contains(&edge));
542 plus_free_pq_.Add(&edge);
543 if (edge.pseudo_slack == tree_dual) {
544 DCHECK_EQ(Slack(edge), 0);
545 primal_update_edge_queue_.push_back(e);
546 }
547 }
548 }
549 }
550}
551
552void BlossomGraph::AppendNodePathToRoot(NodeIndex n,
553 std::vector<NodeIndex>* path) const {
554 while (true) {
555 path->push_back(n);
556 n = nodes_[n].parent;
557 if (n == path->back()) break;
558 }
559}
560
561void BlossomGraph::Augment(EdgeIndex e) {
562 ++num_augments_;
563
564 const Edge& edge = edges_[e];
565 VLOG(2) << "Augment " << Tail(edge) << " -> " << Head(edge);
567 DCHECK(nodes_[Tail(edge)].IsPlus());
568 DCHECK(nodes_[Head(edge)].IsPlus());
569
570 const NodeIndex root_a = nodes_[Tail(edge)].root;
571 const NodeIndex root_b = nodes_[Head(edge)].root;
572 DCHECK_NE(root_a, root_b);
573
574 // Compute the path from root_a to 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);
579
580 // TODO(user): Check all dual/slack same after primal op?
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;
585
586 // Make all the nodes from both trees free while keeping the
587 // current matching.
588 //
589 // TODO(user): It seems that we may waste some computation since the part of
590 // the tree not in the path between roots can lead to the same Grow()
591 // operations later when one of its node is ratched to a new root.
592 //
593 // TODO(user): Reduce this O(num_nodes) complexity. We might be able to
594 // even do O(num_node_in_path) with lazy updates. Note that this operation
595 // will only be performed at most num_initial_unmatched_nodes / 2 times
596 // though.
597 for (NodeIndex n(0); n < nodes_.size(); ++n) {
598 Node& node = nodes_[n];
599 if (node.IsInternal()) continue;
600 const NodeIndex root = node.root;
601 if (root != root_a && root != root_b) continue;
602
603 const CostValue delta = node.type * (root == root_a ? delta_a : delta_b);
604 node.pseudo_dual += delta;
605 for (const NodeIndex subnode : SubNodes(n)) {
606 for (const EdgeIndex e : graph_[subnode]) {
607 Edge& edge = edges_[e];
608 const NodeIndex other_end = OtherEnd(edge, subnode);
609 if (other_end == n) continue;
611
612 // If the other end is not in one of the two trees, and it is a plus
613 // node, we add it the plus_free queue. All previous [+]--[0] and
614 // [+]--[+] edges need to be removed from the queues.
615 const Node& other_node = nodes_[other_end];
616 if (other_node.root != root_a && other_node.root != root_b &&
617 other_node.IsPlus()) {
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);
622 } else {
623 if (plus_plus_pq_.Contains(&edge)) plus_plus_pq_.Remove(&edge);
624 if (plus_free_pq_.Contains(&edge)) plus_free_pq_.Remove(&edge);
625 }
626 }
627 }
628
629 node.type = 0;
630 node.parent = node.root = n;
631 }
632
633 // Change the matching of nodes along node_path.
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];
638 }
639
640 // Update unmatched_nodes_.
641 //
642 // TODO(user): This could probably be optimized if needed. But we do usually
643 // iterate a lot more over it than we update it. Note that as long as we use
644 // the same delta for all trees, this is not even needed.
645 int new_size = 0;
646 for (const NodeIndex n : unmatched_nodes_) {
647 if (!NodeIsMatched(n)) unmatched_nodes_[new_size++] = n;
648 }
649 CHECK_EQ(unmatched_nodes_.size(), new_size + 2);
650 unmatched_nodes_.resize(new_size);
651}
652
653int BlossomGraph::GetDepth(NodeIndex n) const {
654 int depth = 0;
655 while (true) {
656 const NodeIndex parent = nodes_[n].parent;
657 if (parent == n) break;
658 ++depth;
659 n = parent;
660 }
661 return depth;
662}
663
664void BlossomGraph::Shrink(EdgeIndex e) {
665 ++num_shrinks_;
666
667 const Edge& edge = edges_[e];
669 DCHECK(nodes_[Tail(edge)].IsPlus());
670 DCHECK(nodes_[Head(edge)].IsPlus());
671 DCHECK_EQ(nodes_[Tail(edge)].root, nodes_[Head(edge)].root);
672
673 CHECK_NE(Tail(edge), Head(edge)) << e;
674
675 // Find lowest common ancestor and the two node paths to reach it. Note that
676 // we do not add it to the paths.
677 NodeIndex lca_index = kNoNodeIndex;
678 std::vector<NodeIndex> tail_path;
679 std::vector<NodeIndex> head_path;
680 {
681 NodeIndex tail = Tail(edge);
682 NodeIndex head = Head(edge);
683 int tail_depth = GetDepth(tail);
684 int head_depth = GetDepth(head);
685 if (tail_depth > head_depth) {
686 std::swap(tail, head);
687 std::swap(tail_depth, head_depth);
688 }
689 VLOG(2) << "Shrink " << tail << " <-> " << head;
690
691 while (head_depth > tail_depth) {
692 head_path.push_back(head);
693 head = nodes_[head].parent;
694 --head_depth;
695 }
696 while (tail != head) {
697 DCHECK_EQ(tail_depth, head_depth);
698 DCHECK_GE(tail_depth, 0);
699 if (DEBUG_MODE) {
700 --tail_depth;
701 --head_depth;
702 }
703
704 tail_path.push_back(tail);
705 tail = nodes_[tail].parent;
706
707 head_path.push_back(head);
708 head = nodes_[head].parent;
709 }
710 lca_index = tail;
711 VLOG(2) << "LCA " << lca_index;
712 }
713 Node& lca = nodes_[lca_index];
714 DCHECK(lca.IsPlus());
715
716 // Fill the cycle.
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);
722
723 const CostValue tree_dual = nodes_[lca.root].tree_dual_delta;
724
725 // Save all values that will be needed if we expand this Blossom later.
726 CHECK_GT(blossom.size(), 1);
727 Node& backup_node = nodes_[blossom[1]];
728#ifndef NDEBUG
729 backup_node.saved_dual = lca.dual;
730#endif
731 backup_node.saved_pseudo_dual = lca.pseudo_dual + tree_dual;
732
733 // Set the new dual of the node to zero.
734#ifndef NDEBUG
735 lca.dual = 0;
736#endif
737 lca.pseudo_dual = -tree_dual;
738 CHECK_EQ(Dual(lca), 0);
739
740 // Mark node as internal, but do not change their type to zero yet.
741 // We need to do that first to properly detect edges between two internal
742 // nodes in the second loop below.
743 for (const NodeIndex n : blossom) {
744 VLOG(2) << "blossom-node: " << NodeDebugString(n);
745 if (n != lca_index) {
746 nodes_[n].is_internal = true;
747 }
748 }
749
750 // Update the dual of all edges and the priority queueus.
751 for (const NodeIndex n : blossom) {
752 Node& mutable_node = nodes_[n];
753 const bool was_minus = mutable_node.IsMinus();
754 const CostValue slack_adjust =
755 mutable_node.IsMinus() ? tree_dual : -tree_dual;
756 if (n != lca_index) {
757 mutable_node.pseudo_dual -= slack_adjust;
758#ifndef NDEBUG
759 DCHECK_EQ(mutable_node.dual, mutable_node.pseudo_dual);
760#endif
761 mutable_node.type = 0;
762 }
763 for (const NodeIndex subnode : SubNodes(n)) {
764 // Subtle: We update root_blossom_node_ while we loop, so for new internal
765 // edges, depending if an edge "other end" appear after or before, it will
766 // not be updated. We use this to only process internal edges once.
767 root_blossom_node_[subnode] = lca_index;
768
769 for (const EdgeIndex e : graph_[subnode]) {
770 Edge& edge = edges_[e];
771 const NodeIndex other_end = OtherEnd(edge, subnode);
772
773 // Skip edge that are already internal.
774 if (other_end == n) continue;
775
776 // This internal edge was already processed from its other end, so we
777 // can just skip it.
778 if (other_end == lca_index) {
779#ifndef NDEBUG
780 DCHECK_EQ(edge.slack, Slack(edge));
781#endif
782 continue;
783 }
784
785 // This is a new-internal edge that we didn't process yet.
786 //
787 // TODO(user): It would be nicer to not to have to read the memory of
788 // the other node at all. It might be possible once we store the
789 // parent edge instead of the parent node since then we will only need
790 // to know if this edges point to a new-internal node or not.
791 Node& mutable_other_node = nodes_[other_end];
792 if (mutable_other_node.is_internal) {
793 DCHECK(!plus_free_pq_.Contains(&edge));
794 if (plus_plus_pq_.Contains(&edge)) plus_plus_pq_.Remove(&edge);
795 edge.pseudo_slack += slack_adjust;
797 mutable_other_node.IsMinus() ? tree_dual : -tree_dual;
798 continue;
799 }
800
801 // Replace the parent of any child of n by lca_index.
802 if (mutable_other_node.parent == n) {
803 mutable_other_node.parent = lca_index;
804 }
805
806 // Adjust when the edge used to be connected to a [-] node now that we
807 // attach it to a [+] node. Note that if the node was [+] then the
808 // non-internal incident edges slack and type do not change.
809 if (was_minus) {
810 edge.pseudo_slack += 2 * tree_dual;
811
812 // Add it to the correct PQ.
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);
817 if (edge.pseudo_slack == 2 * tree_dual) {
818 primal_update_edge_queue_.push_back(e);
819 }
820 } else if (mutable_other_node.IsFree()) {
821 plus_free_pq_.Add(&edge);
822 if (edge.pseudo_slack == tree_dual) {
823 primal_update_edge_queue_.push_back(e);
824 }
825 }
826 }
827
828#ifndef NDEBUG
829 DCHECK_EQ(edge.slack, Slack(edge));
830#endif
831 }
832 }
833 }
834
835 DCHECK(backup_node.saved_blossom.empty());
836 backup_node.saved_blossom = std::move(lca.blossom);
837 lca.blossom = std::move(blossom);
838
839 VLOG(2) << "S result " << NodeDebugString(lca_index);
840}
841
842BlossomGraph::EdgeIndex BlossomGraph::FindTightExternalEdgeBetweenNodes(
844 DCHECK_NE(tail, head);
845 DCHECK_EQ(tail, root_blossom_node_[tail]);
846 DCHECK_EQ(head, root_blossom_node_[head]);
847 for (const NodeIndex subnode : SubNodes(tail)) {
848 for (const EdgeIndex e : graph_[subnode]) {
849 const Edge& edge = edges_[e];
850 const NodeIndex other_end = OtherEnd(edge, subnode);
851 if (other_end == head && Slack(edge) == 0) {
852 return e;
853 }
854 }
855 }
856 return kNoEdgeIndex;
857}
858
860 ++num_expands_;
861 VLOG(2) << "Expand " << to_expand;
862
863 Node& node_to_expand = nodes_[to_expand];
864 DCHECK(node_to_expand.IsBlossom());
865 DCHECK(node_to_expand.IsMinus());
866 DCHECK_EQ(Dual(node_to_expand), 0);
867
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);
872
873 // First, restore the saved fields.
874 Node& backup_node = nodes_[node_to_expand.blossom[1]];
875#ifndef NDEBUG
876 node_to_expand.dual = backup_node.saved_dual;
877#endif
878 node_to_expand.pseudo_dual = backup_node.saved_pseudo_dual;
879 std::vector<NodeIndex> blossom = std::move(node_to_expand.blossom);
880 node_to_expand.blossom = std::move(backup_node.saved_blossom);
881 backup_node.saved_blossom.clear();
882
883 // Restore the edges Head()/Tail().
884 for (const NodeIndex n : blossom) {
885 for (const NodeIndex subnode : SubNodes(n)) {
886 root_blossom_node_[subnode] = n;
887 }
888 }
889
890 // Now we try to find a 'blossom path' that will replace the blossom node in
891 // the alternating tree: the blossom's parent [+] node in the tree will be
892 // attached to a blossom subnode (the "path start"), the blossom's child in
893 // the tree will be attached to a blossom subnode (the "path end", which could
894 // be the same subnode or a different one), and, using the blossom cycle,
895 // we'll get a path with an odd number of blossom subnodes to connect the two
896 // (since the cycle is odd, one of the two paths will be odd too). The other
897 // subnodes of the blossom will then be made free nodes matched pairwise.
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);
902 const NodeIndex end_node =
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;
907 }
908
909 // Split the cycle in two halves: nodes in [start..end] in path1, and
910 // nodes in [end..start] in path2. Note the inclusive intervals.
911 const std::vector<NodeIndex>& cycle = blossom;
912 std::vector<NodeIndex> path1;
913 std::vector<NodeIndex> path2;
914 {
915 const int end_offset =
916 (blossom_path_end + cycle.size() - blossom_path_start) % cycle.size();
917 for (int offset = 0; offset <= /*or equal*/ cycle.size(); ++offset) {
918 const NodeIndex node =
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);
922 }
923 }
924
925 // Reverse path2 to also make it go from start to end.
926 std::reverse(path2.begin(), path2.end());
927
928 // Swap if necessary so that path1 is the odd-length one.
929 if (path1.size() % 2 == 0) path1.swap(path2);
930
931 // Use better aliases than 'path1' and 'path2' in the code below.
932 std::vector<NodeIndex>& path_in_tree = path1;
933 const std::vector<NodeIndex>& free_pairs = path2;
934
935 // Strip path2 from the start and end, which aren't needed.
936 path2.erase(path2.begin());
937 path2.pop_back();
938
939 const NodeIndex blossom_matched_node = node_to_expand.match;
940 VLOG(2) << "Path ["
941 << absl::StrJoin(path_in_tree, ", ", absl::StreamFormatter())
942 << "] === " << blossom_matched_node;
943 VLOG(2) << "Pairs ["
944 << absl::StrJoin(free_pairs, ", ", absl::StreamFormatter()) << "]";
945
946 // Restore the path in the tree, note that we append the blossom_matched_node
947 // to simplify the code:
948 // <---- Blossom ---->
949 // [-] === [+] --- [-] === [+]
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) {
954 const NodeIndex n = path_in_tree[i];
955 const bool node_is_plus = i % 2;
956
957 // Update the parent.
958 if (i == 0) {
959 // This is the path start and its parent is either itself or the parent of
960 // to_expand if there was one.
961 DCHECK(node_to_expand.parent != to_expand || n == to_expand);
962 nodes_[n].parent = node_to_expand.parent;
963 } else {
964 nodes_[n].parent = path_in_tree[i - 1];
965 }
966
967 // Update the types and matches.
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];
971
972 // Ignore the blossom_matched_node for the code below.
973 if (i + 1 == path_in_tree.size()) continue;
974
975 // Update the duals, depending on whether we have a new [+] or [-] node.
976 // Note that this is also needed for the 'root' blossom node (i=0), because
977 // we've restored its pseudo-dual from its old saved value above.
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]) {
982 Edge& edge = edges_[e];
983 const NodeIndex other_end = OtherEnd(edge, subnode);
984 if (other_end == n) continue;
985
986 edge.pseudo_slack -= adjust;
987
988 // non-internal edges used to be attached to the [-] node_to_expand,
989 // so we adjust their dual.
990 if (other_end != to_expand && !nodes_[other_end].is_internal) {
991 edge.pseudo_slack += tree_dual;
992 } else {
993 // This was an internal edges. For the PQ code below to be correct, we
994 // wait for its other end to have been processed by this loop already.
995 // We detect that using the fact that the type of unprocessed internal
996 // node is still zero.
997 if (nodes_[other_end].type == 0) continue;
998 }
999
1000 // Update edge queues.
1001 if (node_is_plus) {
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);
1007 if (edge.pseudo_slack == 2 * tree_dual) {
1008 primal_update_edge_queue_.push_back(e);
1009 }
1010 } else if (other_node.IsFree()) {
1011 plus_free_pq_.Add(&edge);
1012 if (edge.pseudo_slack == tree_dual) {
1013 primal_update_edge_queue_.push_back(e);
1014 }
1015 }
1016 }
1017 }
1018 }
1019 }
1020
1021 // Update free nodes.
1022 for (const NodeIndex n : free_pairs) {
1023 nodes_[n].type = 0;
1024 nodes_[n].parent = n;
1025 nodes_[n].root = n;
1026
1027 // Update edges slack and priority queue for the adjacent edges.
1028 for (const NodeIndex subnode : SubNodes(n)) {
1029 for (const EdgeIndex e : graph_[subnode]) {
1030 Edge& edge = edges_[e];
1031 const NodeIndex other_end = OtherEnd(edge, subnode);
1032 if (other_end == n) continue;
1033
1034 // non-internal edges used to be attached to the [-] node_to_expand,
1035 // so we adjust their dual.
1036 if (other_end != to_expand && !nodes_[other_end].is_internal) {
1037 edge.pseudo_slack += tree_dual;
1038 }
1039
1040 // Update PQ. Note that since this was attached to a [-] node it cannot
1041 // be in any queue. We will also never process twice the same edge here.
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);
1046 if (edge.pseudo_slack == tree_dual) {
1047 primal_update_edge_queue_.push_back(e);
1048 }
1049 }
1050 }
1051 }
1052 }
1053
1054 // Matches the free pair together.
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];
1059 }
1060
1061 // Mark all node as external. We do that last so we could easily detect old
1062 // internal edges that are now external.
1063 for (const NodeIndex n : blossom) {
1064 nodes_[n].is_internal = false;
1065 }
1066}
1067
1069 // Queue of blossoms to expand.
1070 std::vector<NodeIndex> queue;
1071 for (NodeIndex n(0); n < nodes_.size(); ++n) {
1072 Node& node = nodes_[n];
1073 if (node.IsInternal()) continue;
1074
1075 // When this is called, there should be no more trees.
1076 CHECK(node.IsFree());
1077
1078 if (node.IsBlossom()) queue.push_back(n);
1079 }
1080
1081 // TODO(user): remove duplication with expand?
1082 while (!queue.empty()) {
1083 const NodeIndex to_expand = queue.back();
1084 queue.pop_back();
1085
1086 Node& node_to_expand = nodes_[to_expand];
1087 DCHECK(node_to_expand.IsBlossom());
1088
1089 // Find the edge used to match to_expand with Match(to_expand).
1090 const EdgeIndex match_edge_index =
1091 FindTightExternalEdgeBetweenNodes(to_expand, node_to_expand.match);
1092
1093 // Restore the saved data.
1094 Node& backup_node = nodes_[node_to_expand.blossom[1]];
1095#ifndef NDEBUG
1096 node_to_expand.dual = backup_node.saved_dual;
1097#endif
1098 node_to_expand.pseudo_dual = backup_node.saved_pseudo_dual;
1099
1100 std::vector<NodeIndex> blossom = std::move(node_to_expand.blossom);
1101 node_to_expand.blossom = std::move(backup_node.saved_blossom);
1102 backup_node.saved_blossom.clear();
1103
1104 // Restore the edges Head()/Tail().
1105 for (const NodeIndex n : blossom) {
1106 for (const NodeIndex subnode : SubNodes(n)) {
1107 root_blossom_node_[subnode] = n;
1108 }
1109 }
1110
1111 // Find the index of matched_node in the blossom list.
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;
1119 break;
1120 }
1121 }
1122 CHECK_NE(internal_matched_index, -1);
1123
1124 // Amongst the node_to_expand.blossom nodes, internal_matched_index is
1125 // matched with external_matched_node and the other are matched together.
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]);
1130 }
1131
1132 // Clear root/parent/type of all internal nodes.
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;
1138 }
1139
1140 // Matches the internal node with external one.
1141 const NodeIndex external_matched_node = node_to_expand.match;
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;
1145
1146 // Matches the free pair together.
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];
1151 }
1152
1153 // Now that the expansion is done, add to the queue any sub-blossoms.
1154 for (const NodeIndex n : blossom) {
1155 if (nodes_[n].IsBlossom()) queue.push_back(n);
1156 }
1157 }
1158}
1159
1160const std::vector<NodeIndex>& BlossomGraph::SubNodes(NodeIndex n) {
1161 // This should be only called on an external node. However, in Shrink() we
1162 // mark the node as internal early, so we just make sure the node as no saved
1163 // blossom field here.
1164 DCHECK(nodes_[n].saved_blossom.empty());
1165
1166 // Expand all the inner nodes under the node n. This will not be n iff node is
1167 // is in fact a blossom.
1168 subnodes_ = {n};
1169 for (int i = 0; i < subnodes_.size(); ++i) {
1170 const Node& node = nodes_[subnodes_[i]];
1171
1172 // Since the first node in each list is always the node above, we just
1173 // skip it to avoid listing twice the nodes.
1174 if (!node.blossom.empty()) {
1175 subnodes_.insert(subnodes_.end(), node.blossom.begin() + 1,
1176 node.blossom.end());
1177 }
1178
1179 // We also need to recursively expand the sub-blossom nodes, which are (if
1180 // any) in the "saved_blossom" field of the first internal node of each
1181 // blossom. Since we iterate on all internal nodes here, we simply consult
1182 // the "saved_blossom" field of all subnodes, and it works the same.
1183 if (!node.saved_blossom.empty()) {
1184 subnodes_.insert(subnodes_.end(), node.saved_blossom.begin() + 1,
1185 node.saved_blossom.end());
1186 }
1187 }
1188 return subnodes_;
1189}
1190
1192 const Node& node = nodes_[n];
1193 if (node.is_internal) {
1194 return absl::StrCat("[I] #", n.value());
1195 }
1196 const std::string type = !NodeIsMatched(n) ? "[*]"
1197 : node.type == 1 ? "[+]"
1198 : node.type == -1 ? "[-]"
1199 : "[0]";
1200 return absl::StrCat(
1201 type, " #", n.value(), " dual: ", Dual(node).value(),
1202 " parent: ", node.parent.value(), " match: ", node.match.value(),
1203 " blossom: [", absl::StrJoin(node.blossom, ", ", absl::StreamFormatter()),
1204 "]");
1205}
1206
1207std::string BlossomGraph::EdgeDebugString(EdgeIndex e) const {
1208 const Edge& edge = edges_[e];
1209 if (nodes_[Tail(edge)].is_internal || nodes_[Head(edge)].is_internal) {
1210 return absl::StrCat(Tail(edge).value(), "<->", Head(edge).value(),
1211 " internal ");
1212 }
1213 return absl::StrCat(Tail(edge).value(), "<->", Head(edge).value(),
1214 " slack: ", Slack(edge).value());
1215}
1216
1217std::string BlossomGraph::DebugString() const {
1218 std::string result = "Graph:\n";
1219 for (NodeIndex n(0); n < nodes_.size(); ++n) {
1220 absl::StrAppend(&result, NodeDebugString(n), "\n");
1221 }
1222 for (EdgeIndex e(0); e < edges_.size(); ++e) {
1223 absl::StrAppend(&result, EdgeDebugString(e), "\n");
1224 }
1225 return result;
1226}
1227
1229#ifndef NDEBUG
1230 nodes_[n].dual += delta;
1231 for (const NodeIndex subnode : SubNodes(n)) {
1232 for (const EdgeIndex e : graph_[subnode]) {
1233 Edge& edge = edges_[e];
1234 const NodeIndex other_end = OtherEnd(edge, subnode);
1235 if (other_end == n) continue;
1236 edges_[e].slack -= delta;
1237 }
1238 }
1239#endif
1240}
1241
1243 const Node& tail_node = nodes_[Tail(edge)];
1244 const Node& head_node = nodes_[Head(edge)];
1245 CostValue slack = edge.pseudo_slack;
1246 if (Tail(edge) == Head(edge)) return slack; // Internal...
1247
1248 if (!tail_node.is_internal && !head_node.is_internal) {
1249 slack -= tail_node.type * nodes_[tail_node.root].tree_dual_delta +
1250 head_node.type * nodes_[head_node.root].tree_dual_delta;
1251 }
1252#ifndef NDEBUG
1253 DCHECK_EQ(slack, edge.slack) << tail_node.type << " " << head_node.type
1254 << " " << Tail(edge) << "<->" << Head(edge);
1255#endif
1256 return slack;
1257}
1258
1259// Returns the dual value of the given node (which might be a pseudo-node).
1261 const CostValue dual =
1262 node.pseudo_dual + node.type * nodes_[node.root].tree_dual_delta;
1263#ifndef NDEBUG
1264 DCHECK_EQ(dual, node.dual);
1265#endif
1266 return dual;
1267}
1268
1270 if (dual_objective_ == std::numeric_limits<int64_t>::max())
1271 return CostValue(std::numeric_limits<int64_t>::max());
1272 CHECK_EQ(dual_objective_ % 2, 0);
1273 return dual_objective_ / 2;
1274}
1275
1276void BlossomGraph::AddToDualObjective(CostValue delta) {
1277 CHECK_GE(delta, 0);
1278 dual_objective_ = CostValue(CapAdd(dual_objective_.value(), delta.value()));
1279}
1280
1282 VLOG(1) << "num_grows: " << num_grows_;
1283 VLOG(1) << "num_augments: " << num_augments_;
1284 VLOG(1) << "num_shrinks: " << num_shrinks_;
1285 VLOG(1) << "num_expands: " << num_expands_;
1286 VLOG(1) << "num_dual_updates: " << num_dual_updates_;
1287}
1288
1289} // namespace operations_research
IntegerValue size
void AddEdge(NodeIndex tail, NodeIndex head, CostValue cost)
Same comment as MinCostPerfectMatching::AddEdgeWithCost() applies.
void ExpandAllBlossoms()
This must be called at the end of the algorithm to recover the matching.
void DisplayStats() const
Display to VLOG(1) some statistic about the solve.
CostValue ComputeMaxCommonTreeDualDeltaAndResetPrimalEdgeQueue()
CostValue Slack(const Edge &edge) const
Return the "slack" of the given edge.
NodeIndex Match(NodeIndex n) const
void DebugUpdateNodeDual(NodeIndex n, CostValue delta)
BlossomGraph(int num_nodes)
Creates a BlossomGraph on the given number of nodes.
std::string NodeDebugString(NodeIndex n) const
Display information for debugging.
static const EdgeIndex kNoEdgeIndex
std::string EdgeDebugString(EdgeIndex e) const
bool DebugEdgeIsTightAndExternal(const Edge &edge) const
static const NodeIndex kNoNodeIndex
CostValue Dual(const Node &node) const
Returns the dual value of the given node (which might be a pseudo-node).
void Expand(NodeIndex to_expand)
Expands a Blossom into its component.
bool NodeIsMatched(NodeIndex n) const
ABSL_MUST_USE_RESULT bool Initialize()
void Grow(EdgeIndex e, NodeIndex tail, NodeIndex head)
static const CostValue kMaxCostValue
void AddEdgeWithCost(int tail, int head, int64_t cost)
@ INFEASIBLE
There is no perfect matching in this graph.
@ OPTIMAL
A perfect matching with min-cost has been found.
void push_back(const value_type &val)
void reserve(size_type n)
void resize(size_type new_size)
int64_t value
Edge edge
int index
const bool DEBUG_MODE
Definition macros.h:24
In SWIG mode, we don't want anything besides these top-level includes.
int64_t CapAdd(int64_t x, int64_t y)
int64_t delta
Definition resource.cc:1709
int head
int tail
An undirected edge between two nodes: tail <-> head.
CostValue pseudo_slack
See the formula is Slack() used to derive the true slack of this edge.
NodeIndex OtherEnd(NodeIndex n) const
Returns the "other" end of this edge.
CostValue slack
We only maintain this in debug mode.
CostValue dual
The true dual of this node. We only maintain this in debug mode.
bool is_internal
Whether this node is part of a blossom.