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