OR-Tools  9.1
hungarian.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 <algorithm>
17 #include <cstdio>
18 #include <limits>
19 
20 #include "absl/strings/str_format.h"
21 #include "ortools/base/logging.h"
22 
23 namespace operations_research {
24 
26  static constexpr int kHungarianOptimizerRowNotFound = -1;
27  static constexpr int kHungarianOptimizerColNotFound = -2;
28 
29  public:
30  // Setup the initial conditions for the algorithm.
31 
32  // Parameters: costs is a matrix of the cost of assigning each agent to
33  // each task. costs[i][j] is the cost of assigning agent i to task j.
34  // All the costs must be non-negative. This matrix does not have to
35  // be square (i.e. we can have different numbers of agents and tasks), but it
36  // must be regular (i.e. there must be the same number of entries in each row
37  // of the matrix).
38  explicit HungarianOptimizer(const std::vector<std::vector<double>>& costs);
39 
40  // Find an assignment which maximizes the total cost.
41  // Returns the assignment in the two vectors passed as argument.
42  // preimage[i] is assigned to image[i].
43  void Maximize(std::vector<int>* preimage, std::vector<int>* image);
44 
45  // Like Maximize(), but minimizing the cost instead.
46  void Minimize(std::vector<int>* preimage, std::vector<int>* image);
47 
48  private:
49  typedef void (HungarianOptimizer::*Step)();
50 
51  typedef enum { NONE, PRIME, STAR } Mark;
52 
53  // Convert the final cost matrix into a set of assignments of preimage->image.
54  // Returns the assignment in the two vectors passed as argument, the same as
55  // Minimize and Maximize
56  void FindAssignments(std::vector<int>* preimage, std::vector<int>* image);
57 
58  // Is the cell (row, col) starred?
59  bool IsStarred(int row, int col) const { return marks_[row][col] == STAR; }
60 
61  // Mark cell (row, col) with a star
62  void Star(int row, int col) {
63  marks_[row][col] = STAR;
64  stars_in_col_[col]++;
65  }
66 
67  // Remove a star from cell (row, col)
68  void UnStar(int row, int col) {
69  marks_[row][col] = NONE;
70  stars_in_col_[col]--;
71  }
72 
73  // Find a column in row 'row' containing a star, or return
74  // kHungarianOptimizerColNotFound if no such column exists.
75  int FindStarInRow(int row) const;
76 
77  // Find a row in column 'col' containing a star, or return
78  // kHungarianOptimizerRowNotFound if no such row exists.
79  int FindStarInCol(int col) const;
80 
81  // Is cell (row, col) marked with a prime?
82  bool IsPrimed(int row, int col) const { return marks_[row][col] == PRIME; }
83 
84  // Mark cell (row, col) with a prime.
85  void Prime(int row, int col) { marks_[row][col] = PRIME; }
86 
87  // Find a column in row containing a prime, or return
88  // kHungarianOptimizerColNotFound if no such column exists.
89  int FindPrimeInRow(int row) const;
90 
91  // Remove the prime marks_ from every cell in the matrix.
92  void ClearPrimes();
93 
94  // Does column col contain a star?
95  bool ColContainsStar(int col) const { return stars_in_col_[col] > 0; }
96 
97  // Is row 'row' covered?
98  bool RowCovered(int row) const { return rows_covered_[row]; }
99 
100  // Cover row 'row'.
101  void CoverRow(int row) { rows_covered_[row] = true; }
102 
103  // Uncover row 'row'.
104  void UncoverRow(int row) { rows_covered_[row] = false; }
105 
106  // Is column col covered?
107  bool ColCovered(int col) const { return cols_covered_[col]; }
108 
109  // Cover column col.
110  void CoverCol(int col) { cols_covered_[col] = true; }
111 
112  // Uncover column col.
113  void UncoverCol(int col) { cols_covered_[col] = false; }
114 
115  // Uncover ever row and column in the matrix.
116  void ClearCovers();
117 
118  // Find the smallest uncovered cell in the matrix.
119  double FindSmallestUncovered() const;
120 
121  // Find an uncovered zero and store its coordinates in (zeroRow_, zeroCol_)
122  // and return true, or return false if no such cell exists.
123  bool FindZero(int* zero_row, int* zero_col) const;
124 
125  // Print the matrix to stdout (for debugging.)
126  void PrintMatrix();
127 
128  // Run the Munkres algorithm!
129  void DoMunkres();
130 
131  // Step 1.
132  // For each row of the matrix, find the smallest element and subtract it
133  // from every element in its row. Go to Step 2.
134  void ReduceRows();
135 
136  // Step 2.
137  // Find a zero (Z) in the matrix. If there is no starred zero in its row
138  // or column, star Z. Repeat for every element in the matrix. Go to step 3.
139  // Note: profiling shows this method to use 9.2% of the CPU - the next
140  // slowest step takes 0.6%. I can't think of a way of speeding it up though.
141  void StarZeroes();
142 
143  // Step 3.
144  // Cover each column containing a starred zero. If all columns are
145  // covered, the starred zeros describe a complete set of unique assignments.
146  // In this case, terminate the algorithm. Otherwise, go to step 4.
147  void CoverStarredZeroes();
148 
149  // Step 4.
150  // Find a noncovered zero and prime it. If there is no starred zero in the
151  // row containing this primed zero, Go to Step 5. Otherwise, cover this row
152  // and uncover the column containing the starred zero. Continue in this manner
153  // until there are no uncovered zeros left, then go to Step 6.
154  void PrimeZeroes();
155 
156  // Step 5.
157  // Construct a series of alternating primed and starred zeros as follows.
158  // Let Z0 represent the uncovered primed zero found in Step 4. Let Z1 denote
159  // the starred zero in the column of Z0 (if any). Let Z2 denote the primed
160  // zero in the row of Z1 (there will always be one). Continue until the
161  // series terminates at a primed zero that has no starred zero in its column.
162  // Unstar each starred zero of the series, star each primed zero of the
163  // series, erase all primes and uncover every line in the matrix. Return to
164  // Step 3.
165  void MakeAugmentingPath();
166 
167  // Step 6.
168  // Add the smallest uncovered value in the matrix to every element of each
169  // covered row, and subtract it from every element of each uncovered column.
170  // Return to Step 4 without altering any stars, primes, or covered lines.
171  void AugmentPath();
172 
173  // The size of the problem, i.e. max(#agents, #tasks).
174  int matrix_size_;
175 
176  // The expanded cost matrix.
177  std::vector<std::vector<double>> costs_;
178 
179  // The greatest cost in the initial cost matrix.
180  double max_cost_;
181 
182  // Which rows and columns are currently covered.
183  std::vector<bool> rows_covered_;
184  std::vector<bool> cols_covered_;
185 
186  // The marks_ (star/prime/none) on each element of the cost matrix.
187  std::vector<std::vector<Mark>> marks_;
188 
189  // The number of stars in each column - used to speed up coverStarredZeroes.
190  std::vector<int> stars_in_col_;
191 
192  // Representation of a path_ through the matrix - used in step 5.
193  std::vector<int> preimage_; // i.e. the agents
194  std::vector<int> image_; // i.e. the tasks
195 
196  // The width_ and height_ of the initial (non-expanded) cost matrix.
197  int width_;
198  int height_;
199 
200  // The current state of the algorithm
201  HungarianOptimizer::Step state_;
202 };
203 
205  const std::vector<std::vector<double>>& costs)
206  : matrix_size_(0),
207  costs_(),
208  max_cost_(0),
209  rows_covered_(),
210  cols_covered_(),
211  marks_(),
212  stars_in_col_(),
213  preimage_(),
214  image_(),
215  width_(0),
216  height_(0),
217  state_(nullptr) {
218  width_ = costs.size();
219 
220  if (width_ > 0) {
221  height_ = costs[0].size();
222  } else {
223  height_ = 0;
224  }
225 
226  matrix_size_ = std::max(width_, height_);
227  max_cost_ = 0;
228 
229  // Generate the expanded cost matrix by adding extra 0-valued elements in
230  // order to make a square matrix. At the same time, find the greatest cost
231  // in the matrix (used later if we want to maximize rather than minimize the
232  // overall cost.)
233  costs_.resize(matrix_size_);
234  for (int row = 0; row < matrix_size_; ++row) {
235  costs_[row].resize(matrix_size_);
236  }
237 
238  for (int row = 0; row < matrix_size_; ++row) {
239  for (int col = 0; col < matrix_size_; ++col) {
240  if ((row >= width_) || (col >= height_)) {
241  costs_[row][col] = 0;
242  } else {
243  costs_[row][col] = costs[row][col];
244  max_cost_ = std::max(max_cost_, costs_[row][col]);
245  }
246  }
247  }
248 
249  // Initially, none of the cells of the matrix are marked.
250  marks_.resize(matrix_size_);
251  for (int row = 0; row < matrix_size_; ++row) {
252  marks_[row].resize(matrix_size_);
253  for (int col = 0; col < matrix_size_; ++col) {
254  marks_[row][col] = NONE;
255  }
256  }
257 
258  stars_in_col_.resize(matrix_size_);
259 
260  rows_covered_.resize(matrix_size_);
261  cols_covered_.resize(matrix_size_);
262 
263  preimage_.resize(matrix_size_ * 2);
264  image_.resize(matrix_size_ * 2);
265 }
266 
267 // Find an assignment which maximizes the total cost.
268 // Return an array of pairs of integers. Each pair (i, j) corresponds to
269 // assigning agent i to task j.
270 void HungarianOptimizer::Maximize(std::vector<int>* preimage,
271  std::vector<int>* image) {
272  // Find a maximal assignment by subtracting each of the
273  // original costs from max_cost_ and then minimizing.
274  for (int row = 0; row < width_; ++row) {
275  for (int col = 0; col < height_; ++col) {
276  costs_[row][col] = max_cost_ - costs_[row][col];
277  }
278  }
279  Minimize(preimage, image);
280 }
281 
282 // Find an assignment which minimizes the total cost.
283 // Return an array of pairs of integers. Each pair (i, j) corresponds to
284 // assigning agent i to task j.
285 void HungarianOptimizer::Minimize(std::vector<int>* preimage,
286  std::vector<int>* image) {
287  DoMunkres();
288  FindAssignments(preimage, image);
289 }
290 
291 // Convert the final cost matrix into a set of assignments of agents -> tasks.
292 // Return an array of pairs of integers, the same as the return values of
293 // Minimize() and Maximize()
294 void HungarianOptimizer::FindAssignments(std::vector<int>* preimage,
295  std::vector<int>* image) {
296  preimage->clear();
297  image->clear();
298  for (int row = 0; row < width_; ++row) {
299  for (int col = 0; col < height_; ++col) {
300  if (IsStarred(row, col)) {
301  preimage->push_back(row);
302  image->push_back(col);
303  break;
304  }
305  }
306  }
307  // TODO(user)
308  // result_size = min(width_, height_);
309  // CHECK image.size() == result_size
310  // CHECK preimage.size() == result_size
311 }
312 
313 // Find a column in row 'row' containing a star, or return
314 // kHungarianOptimizerColNotFound if no such column exists.
315 int HungarianOptimizer::FindStarInRow(int row) const {
316  for (int col = 0; col < matrix_size_; ++col) {
317  if (IsStarred(row, col)) {
318  return col;
319  }
320  }
321 
322  return kHungarianOptimizerColNotFound;
323 }
324 
325 // Find a row in column 'col' containing a star, or return
326 // kHungarianOptimizerRowNotFound if no such row exists.
327 int HungarianOptimizer::FindStarInCol(int col) const {
328  if (!ColContainsStar(col)) {
329  return kHungarianOptimizerRowNotFound;
330  }
331 
332  for (int row = 0; row < matrix_size_; ++row) {
333  if (IsStarred(row, col)) {
334  return row;
335  }
336  }
337 
338  // NOTREACHED
339  return kHungarianOptimizerRowNotFound;
340 }
341 
342 // Find a column in row containing a prime, or return
343 // kHungarianOptimizerColNotFound if no such column exists.
344 int HungarianOptimizer::FindPrimeInRow(int row) const {
345  for (int col = 0; col < matrix_size_; ++col) {
346  if (IsPrimed(row, col)) {
347  return col;
348  }
349  }
350 
351  return kHungarianOptimizerColNotFound;
352 }
353 
354 // Remove the prime marks from every cell in the matrix.
355 void HungarianOptimizer::ClearPrimes() {
356  for (int row = 0; row < matrix_size_; ++row) {
357  for (int col = 0; col < matrix_size_; ++col) {
358  if (IsPrimed(row, col)) {
359  marks_[row][col] = NONE;
360  }
361  }
362  }
363 }
364 
365 // Uncovery ever row and column in the matrix.
366 void HungarianOptimizer::ClearCovers() {
367  for (int x = 0; x < matrix_size_; x++) {
368  UncoverRow(x);
369  UncoverCol(x);
370  }
371 }
372 
373 // Find the smallest uncovered cell in the matrix.
374 double HungarianOptimizer::FindSmallestUncovered() const {
375  double minval = std::numeric_limits<double>::max();
376 
377  for (int row = 0; row < matrix_size_; ++row) {
378  if (RowCovered(row)) {
379  continue;
380  }
381 
382  for (int col = 0; col < matrix_size_; ++col) {
383  if (ColCovered(col)) {
384  continue;
385  }
386 
387  minval = std::min(minval, costs_[row][col]);
388  }
389  }
390 
391  return minval;
392 }
393 
394 // Find an uncovered zero and store its co-ordinates in (zeroRow, zeroCol)
395 // and return true, or return false if no such cell exists.
396 bool HungarianOptimizer::FindZero(int* zero_row, int* zero_col) const {
397  for (int row = 0; row < matrix_size_; ++row) {
398  if (RowCovered(row)) {
399  continue;
400  }
401 
402  for (int col = 0; col < matrix_size_; ++col) {
403  if (ColCovered(col)) {
404  continue;
405  }
406 
407  if (costs_[row][col] == 0) {
408  *zero_row = row;
409  *zero_col = col;
410  return true;
411  }
412  }
413  }
414 
415  return false;
416 }
417 
418 // Print the matrix to stdout (for debugging.)
419 void HungarianOptimizer::PrintMatrix() {
420  for (int row = 0; row < matrix_size_; ++row) {
421  for (int col = 0; col < matrix_size_; ++col) {
422  absl::PrintF("%g ", costs_[row][col]);
423 
424  if (IsStarred(row, col)) {
425  absl::PrintF("*");
426  }
427 
428  if (IsPrimed(row, col)) {
429  absl::PrintF("'");
430  }
431  }
432  absl::PrintF("\n");
433  }
434 }
435 
436 // Run the Munkres algorithm!
437 void HungarianOptimizer::DoMunkres() {
438  state_ = &HungarianOptimizer::ReduceRows;
439  while (state_ != nullptr) {
440  (this->*state_)();
441  }
442 }
443 
444 // Step 1.
445 // For each row of the matrix, find the smallest element and subtract it
446 // from every element in its row. Go to Step 2.
447 void HungarianOptimizer::ReduceRows() {
448  for (int row = 0; row < matrix_size_; ++row) {
449  double min_cost = costs_[row][0];
450  for (int col = 1; col < matrix_size_; ++col) {
451  min_cost = std::min(min_cost, costs_[row][col]);
452  }
453  for (int col = 0; col < matrix_size_; ++col) {
454  costs_[row][col] -= min_cost;
455  }
456  }
457  state_ = &HungarianOptimizer::StarZeroes;
458 }
459 
460 // Step 2.
461 // Find a zero (Z) in the matrix. If there is no starred zero in its row
462 // or column, star Z. Repeat for every element in the matrix. Go to step 3.
463 // Note (user): profiling shows this method to use 9.2%
464 // of the CPU - the next slowest step takes 0.6%. I can't think of a way
465 // of speeding it up though.
466 void HungarianOptimizer::StarZeroes() {
467  // Since no rows or columns are covered on entry to this step, we use the
468  // covers as a quick way of marking which rows & columns have stars in them.
469  for (int row = 0; row < matrix_size_; ++row) {
470  if (RowCovered(row)) {
471  continue;
472  }
473 
474  for (int col = 0; col < matrix_size_; ++col) {
475  if (ColCovered(col)) {
476  continue;
477  }
478 
479  if (costs_[row][col] == 0) {
480  Star(row, col);
481  CoverRow(row);
482  CoverCol(col);
483  break;
484  }
485  }
486  }
487 
488  ClearCovers();
489  state_ = &HungarianOptimizer::CoverStarredZeroes;
490 }
491 
492 // Step 3.
493 // Cover each column containing a starred zero. If all columns are
494 // covered, the starred zeros describe a complete set of unique assignments.
495 // In this case, terminate the algorithm. Otherwise, go to step 4.
496 void HungarianOptimizer::CoverStarredZeroes() {
497  int num_covered = 0;
498 
499  for (int col = 0; col < matrix_size_; ++col) {
500  if (ColContainsStar(col)) {
501  CoverCol(col);
502  num_covered++;
503  }
504  }
505 
506  if (num_covered >= matrix_size_) {
507  state_ = nullptr;
508  return;
509  }
510  state_ = &HungarianOptimizer::PrimeZeroes;
511 }
512 
513 // Step 4.
514 // Find a noncovered zero and prime it. If there is no starred zero in the
515 // row containing this primed zero, Go to Step 5. Otherwise, cover this row
516 // and uncover the column containing the starred zero. Continue in this manner
517 // until there are no uncovered zeros left, then go to Step 6.
518 
519 void HungarianOptimizer::PrimeZeroes() {
520  // This loop is guaranteed to terminate in at most matrix_size_ iterations,
521  // as findZero() returns a location only if there is at least one uncovered
522  // zero in the matrix. Each iteration, either one row is covered or the
523  // loop terminates. Since there are matrix_size_ rows, after that many
524  // iterations there are no uncovered cells and hence no uncovered zeroes,
525  // so the loop terminates.
526  for (;;) {
527  int zero_row, zero_col;
528  if (!FindZero(&zero_row, &zero_col)) {
529  // No uncovered zeroes.
530  state_ = &HungarianOptimizer::AugmentPath;
531  return;
532  }
533 
534  Prime(zero_row, zero_col);
535  int star_col = FindStarInRow(zero_row);
536 
537  if (star_col != kHungarianOptimizerColNotFound) {
538  CoverRow(zero_row);
539  UncoverCol(star_col);
540  } else {
541  preimage_[0] = zero_row;
542  image_[0] = zero_col;
543  state_ = &HungarianOptimizer::MakeAugmentingPath;
544  return;
545  }
546  }
547 }
548 
549 // Step 5.
550 // Construct a series of alternating primed and starred zeros as follows.
551 // Let Z0 represent the uncovered primed zero found in Step 4. Let Z1 denote
552 // the starred zero in the column of Z0 (if any). Let Z2 denote the primed
553 // zero in the row of Z1 (there will always be one). Continue until the
554 // series terminates at a primed zero that has no starred zero in its column.
555 // Unstar each starred zero of the series, star each primed zero of the
556 // series, erase all primes and uncover every line in the matrix. Return to
557 // Step 3.
558 void HungarianOptimizer::MakeAugmentingPath() {
559  bool done = false;
560  int count = 0;
561 
562  // Note: this loop is guaranteed to terminate within matrix_size_ iterations
563  // because:
564  // 1) on entry to this step, there is at least 1 column with no starred zero
565  // (otherwise we would have terminated the algorithm already.)
566  // 2) each row containing a star also contains exactly one primed zero.
567  // 4) each column contains at most one starred zero.
568  //
569  // Since the path_ we construct visits primed and starred zeroes alternately,
570  // and terminates if we reach a primed zero in a column with no star, our
571  // path_ must either contain matrix_size_ or fewer stars (in which case the
572  // loop iterates fewer than matrix_size_ times), or it contains more. In
573  // that case, because (1) implies that there are fewer than
574  // matrix_size_ stars, we must have visited at least one star more than once.
575  // Consider the first such star that we visit more than once; it must have
576  // been reached immediately after visiting a prime in the same row. By (2),
577  // this prime is unique and so must have also been visited more than once.
578  // Therefore, that prime must be in the same column as a star that has been
579  // visited more than once, contradicting the assumption that we chose the
580  // first multiply visited star, or it must be in the same column as more
581  // than one star, contradicting (3). Therefore, we never visit any star
582  // more than once and the loop terminates within matrix_size_ iterations.
583 
584  while (!done) {
585  // First construct the alternating path...
586  int row = FindStarInCol(image_[count]);
587 
588  if (row != kHungarianOptimizerRowNotFound) {
589  count++;
590  preimage_[count] = row;
591  image_[count] = image_[count - 1];
592  } else {
593  done = true;
594  }
595 
596  if (!done) {
597  int col = FindPrimeInRow(preimage_[count]);
598  count++;
599  preimage_[count] = preimage_[count - 1];
600  image_[count] = col;
601  }
602  }
603 
604  // Then modify it.
605  for (int i = 0; i <= count; ++i) {
606  int row = preimage_[i];
607  int col = image_[i];
608 
609  if (IsStarred(row, col)) {
610  UnStar(row, col);
611  } else {
612  Star(row, col);
613  }
614  }
615 
616  ClearCovers();
617  ClearPrimes();
618  state_ = &HungarianOptimizer::CoverStarredZeroes;
619 }
620 
621 // Step 6
622 // Add the smallest uncovered value in the matrix to every element of each
623 // covered row, and subtract it from every element of each uncovered column.
624 // Return to Step 4 without altering any stars, primes, or covered lines.
625 void HungarianOptimizer::AugmentPath() {
626  double minval = FindSmallestUncovered();
627 
628  for (int row = 0; row < matrix_size_; ++row) {
629  for (int col = 0; col < matrix_size_; ++col) {
630  if (RowCovered(row)) {
631  costs_[row][col] += minval;
632  }
633 
634  if (!ColCovered(col)) {
635  costs_[row][col] -= minval;
636  }
637  }
638  }
639 
640  state_ = &HungarianOptimizer::PrimeZeroes;
641 }
642 
643 bool InputContainsNan(const std::vector<std::vector<double>>& input) {
644  for (const auto& subvector : input) {
645  for (const auto& num : subvector) {
646  if (std::isnan(num)) {
647  LOG(ERROR) << "The provided input contains " << num << ".";
648  return true;
649  }
650  }
651  }
652  return false;
653 }
654 
656  const std::vector<std::vector<double>>& cost,
657  absl::flat_hash_map<int, int>* direct_assignment,
658  absl::flat_hash_map<int, int>* reverse_assignment) {
659  if (InputContainsNan(cost)) {
660  LOG(ERROR) << "Returning before invoking the Hungarian optimizer.";
661  return;
662  }
663  std::vector<int> agent;
664  std::vector<int> task;
665  HungarianOptimizer hungarian_optimizer(cost);
666  hungarian_optimizer.Minimize(&agent, &task);
667  for (int i = 0; i < agent.size(); ++i) {
668  (*direct_assignment)[agent[i]] = task[i];
669  (*reverse_assignment)[task[i]] = agent[i];
670  }
671 }
672 
674  const std::vector<std::vector<double>>& cost,
675  absl::flat_hash_map<int, int>* direct_assignment,
676  absl::flat_hash_map<int, int>* reverse_assignment) {
677  if (InputContainsNan(cost)) {
678  LOG(ERROR) << "Returning before invoking the Hungarian optimizer.";
679  return;
680  }
681  std::vector<int> agent;
682  std::vector<int> task;
683  HungarianOptimizer hungarian_optimizer(cost);
684  hungarian_optimizer.Maximize(&agent, &task);
685  for (int i = 0; i < agent.size(); ++i) {
686  (*direct_assignment)[agent[i]] = task[i];
687  (*reverse_assignment)[task[i]] = agent[i];
688  }
689 }
690 
691 } // namespace operations_research
bool InputContainsNan(const std::vector< std::vector< double >> &input)
Definition: hungarian.cc:643
void MinimizeLinearAssignment(const std::vector< std::vector< double >> &cost, absl::flat_hash_map< int, int > *direct_assignment, absl::flat_hash_map< int, int > *reverse_assignment)
Definition: hungarian.cc:655
int64_t min
Definition: alldiff_cst.cc:139
HungarianOptimizer(const std::vector< std::vector< double >> &costs)
Definition: hungarian.cc:204
const int ERROR
Definition: log_severity.h:32
#define LOG(severity)
Definition: base/logging.h:416
ColIndex col
Definition: markowitz.cc:183
RowIndex row
Definition: markowitz.cc:182
int64_t max
Definition: alldiff_cst.cc:140
static int input(yyscan_t yyscanner)
int64_t cost
Collection of objects used to extend the Constraint Solver library.
void Minimize(std::vector< int > *preimage, std::vector< int > *image)
Definition: hungarian.cc:285
void MaximizeLinearAssignment(const std::vector< std::vector< double >> &cost, absl::flat_hash_map< int, int > *direct_assignment, absl::flat_hash_map< int, int > *reverse_assignment)
Definition: hungarian.cc:673
void Maximize(std::vector< int > *preimage, std::vector< int > *image)
Definition: hungarian.cc:270