OR-Tools  9.1
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 
22 namespace operations_research {
23 
24 void 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 
115 const 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 
542 void 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 
551 void 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 
643 int 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 
654 void 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) {
676  std::swap(tail, head);
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 
832 BlossomGraph::EdgeIndex BlossomGraph::FindTightExternalEdgeBetweenNodes(
834  DCHECK_NE(tail, head);
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 
1150 const 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 
1197 std::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 
1207 std::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 
1232 CostValue BlossomGraph::Slack(const Edge& edge) const {
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).
1250 CostValue BlossomGraph::Dual(const Node& node) const {
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 
1266 void 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 head
NodeIndex Match(NodeIndex n) const
#define CHECK(condition)
Definition: base/logging.h:491
const bool DEBUG_MODE
Definition: macros.h:24
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK_GE(val1, val2)
Definition: base/logging.h:702
const int FATAL
Definition: log_severity.h:32
CostValue Slack(const Edge &edge) const
CostValue ComputeMaxCommonTreeDualDeltaAndResetPrimalEdgeQueue()
#define CHECK_GT(val1, val2)
Definition: base/logging.h:703
void AddEdge(NodeIndex tail, NodeIndex head, CostValue cost)
#define VLOG(verboselevel)
Definition: base/logging.h:979
void UpdateAllTrees(CostValue delta)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:263
#define LOG(severity)
Definition: base/logging.h:416
std::string EdgeDebugString(EdgeIndex e) const
void DebugUpdateNodeDual(NodeIndex n, CostValue delta)
static const EdgeIndex kNoEdgeIndex
int64_t tail
NodeIndex OtherEnd(NodeIndex n) const
CostValue Dual(const Node &node) const
int64_t max
Definition: alldiff_cst.cc:140
bool DebugEdgeIsTightAndExternal(const Edge &edge) const
void resize(size_type new_size)
int64_t CapAdd(int64_t x, int64_t y)
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:887
void AddEdgeWithCost(int tail, int head, int64_t cost)
ABSL_MUST_USE_RESULT bool Initialize()
void Grow(EdgeIndex e, NodeIndex tail, NodeIndex head)
void push_back(const value_type &x)
int index
Definition: pack.cc:509
std::string NodeDebugString(NodeIndex n) const
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:890
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
int64_t delta
Definition: resource.cc:1692
int64_t cost
#define DCHECK(condition)
Definition: base/logging.h:885
static const NodeIndex kNoNodeIndex
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:886
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:888
Collection of objects used to extend the Constraint Solver library.
bool NodeIsMatched(NodeIndex n) const
static const CostValue kMaxCostValue
void Expand(NodeIndex to_expand)
int64_t value
#define CHECK_NE(val1, val2)
Definition: base/logging.h:699
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:889