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