23 #include "absl/strings/str_format.h"
29 static constexpr
int kHungarianOptimizerRowNotFound = -1;
30 static constexpr
int kHungarianOptimizerColNotFound = -2;
46 void Maximize(std::vector<int>* preimage, std::vector<int>* image);
49 void Minimize(std::vector<int>* preimage, std::vector<int>* image);
54 typedef enum { NONE, PRIME, STAR } Mark;
59 void FindAssignments(std::vector<int>* preimage, std::vector<int>* image);
62 bool IsStarred(
int row,
int col)
const {
return marks_[
row][
col] == STAR; }
65 void Star(
int row,
int col) {
71 void UnStar(
int row,
int col) {
78 int FindStarInRow(
int row)
const;
82 int FindStarInCol(
int col)
const;
85 bool IsPrimed(
int row,
int col)
const {
return marks_[
row][
col] == PRIME; }
88 void Prime(
int row,
int col) { marks_[
row][
col] = PRIME; }
92 int FindPrimeInRow(
int row)
const;
98 bool ColContainsStar(
int col)
const {
return stars_in_col_[
col] > 0; }
101 bool RowCovered(
int row)
const {
return rows_covered_[
row]; }
104 void CoverRow(
int row) { rows_covered_[
row] =
true; }
107 void UncoverRow(
int row) { rows_covered_[
row] =
false; }
110 bool ColCovered(
int col)
const {
return cols_covered_[
col]; }
113 void CoverCol(
int col) { cols_covered_[
col] =
true; }
116 void UncoverCol(
int col) { cols_covered_[
col] =
false; }
122 double FindSmallestUncovered()
const;
126 bool FindZero(
int* zero_row,
int* zero_col)
const;
150 void CoverStarredZeroes();
168 void MakeAugmentingPath();
180 std::vector<std::vector<double>> costs_;
186 std::vector<bool> rows_covered_;
187 std::vector<bool> cols_covered_;
190 std::vector<std::vector<Mark>> marks_;
193 std::vector<int> stars_in_col_;
196 std::vector<int> preimage_;
197 std::vector<int> image_;
204 HungarianOptimizer::Step state_;
208 const std::vector<std::vector<double>>& costs)
221 width_ = costs.size();
224 height_ = costs[0].size();
229 matrix_size_ =
std::max(width_, height_);
236 costs_.resize(matrix_size_);
237 for (
int row = 0;
row < matrix_size_; ++
row) {
238 costs_[
row].resize(matrix_size_);
241 for (
int row = 0;
row < matrix_size_; ++
row) {
242 for (
int col = 0;
col < matrix_size_; ++
col) {
243 if ((
row >= width_) || (
col >= height_)) {
253 marks_.resize(matrix_size_);
254 for (
int row = 0;
row < matrix_size_; ++
row) {
255 marks_[
row].resize(matrix_size_);
256 for (
int col = 0;
col < matrix_size_; ++
col) {
261 stars_in_col_.resize(matrix_size_);
263 rows_covered_.resize(matrix_size_);
264 cols_covered_.resize(matrix_size_);
266 preimage_.resize(matrix_size_ * 2);
267 image_.resize(matrix_size_ * 2);
274 std::vector<int>* image) {
278 for (
int col = 0;
col < height_; ++
col) {
289 std::vector<int>* image) {
291 FindAssignments(preimage, image);
297 void HungarianOptimizer::FindAssignments(std::vector<int>* preimage,
298 std::vector<int>* image) {
302 for (
int col = 0;
col < height_; ++
col) {
303 if (IsStarred(
row,
col)) {
304 preimage->push_back(
row);
305 image->push_back(
col);
318 int HungarianOptimizer::FindStarInRow(
int row)
const {
319 for (
int col = 0;
col < matrix_size_; ++
col) {
320 if (IsStarred(
row,
col)) {
325 return kHungarianOptimizerColNotFound;
330 int HungarianOptimizer::FindStarInCol(
int col)
const {
331 if (!ColContainsStar(
col)) {
332 return kHungarianOptimizerRowNotFound;
335 for (
int row = 0;
row < matrix_size_; ++
row) {
336 if (IsStarred(
row,
col)) {
342 return kHungarianOptimizerRowNotFound;
347 int HungarianOptimizer::FindPrimeInRow(
int row)
const {
348 for (
int col = 0;
col < matrix_size_; ++
col) {
354 return kHungarianOptimizerColNotFound;
358 void HungarianOptimizer::ClearPrimes() {
359 for (
int row = 0;
row < matrix_size_; ++
row) {
360 for (
int col = 0;
col < matrix_size_; ++
col) {
369 void HungarianOptimizer::ClearCovers() {
370 for (
int x = 0; x < matrix_size_; x++) {
377 double HungarianOptimizer::FindSmallestUncovered()
const {
380 for (
int row = 0;
row < matrix_size_; ++
row) {
381 if (RowCovered(
row)) {
385 for (
int col = 0;
col < matrix_size_; ++
col) {
386 if (ColCovered(
col)) {
399 bool HungarianOptimizer::FindZero(
int* zero_row,
int* zero_col)
const {
400 for (
int row = 0;
row < matrix_size_; ++
row) {
401 if (RowCovered(
row)) {
405 for (
int col = 0;
col < matrix_size_; ++
col) {
406 if (ColCovered(
col)) {
410 if (costs_[
row][
col] == 0) {
422 void HungarianOptimizer::PrintMatrix() {
423 for (
int row = 0;
row < matrix_size_; ++
row) {
424 for (
int col = 0;
col < matrix_size_; ++
col) {
425 absl::PrintF(
"%g ", costs_[
row][
col]);
427 if (IsStarred(
row,
col)) {
440 void HungarianOptimizer::DoMunkres() {
441 state_ = &HungarianOptimizer::ReduceRows;
442 while (state_ !=
nullptr) {
450 void HungarianOptimizer::ReduceRows() {
451 for (
int row = 0;
row < matrix_size_; ++
row) {
452 double min_cost = costs_[
row][0];
453 for (
int col = 1;
col < matrix_size_; ++
col) {
456 for (
int col = 0;
col < matrix_size_; ++
col) {
457 costs_[
row][
col] -= min_cost;
460 state_ = &HungarianOptimizer::StarZeroes;
468 void HungarianOptimizer::StarZeroes() {
471 for (
int row = 0;
row < matrix_size_; ++
row) {
472 if (RowCovered(
row)) {
476 for (
int col = 0;
col < matrix_size_; ++
col) {
477 if (ColCovered(
col)) {
481 if (costs_[
row][
col] == 0) {
491 state_ = &HungarianOptimizer::CoverStarredZeroes;
498 void HungarianOptimizer::CoverStarredZeroes() {
501 for (
int col = 0;
col < matrix_size_; ++
col) {
502 if (ColContainsStar(
col)) {
508 if (num_covered >= matrix_size_) {
512 state_ = &HungarianOptimizer::PrimeZeroes;
521 void HungarianOptimizer::PrimeZeroes() {
529 int zero_row, zero_col;
530 if (!FindZero(&zero_row, &zero_col)) {
532 state_ = &HungarianOptimizer::AugmentPath;
536 Prime(zero_row, zero_col);
537 int star_col = FindStarInRow(zero_row);
539 if (star_col != kHungarianOptimizerColNotFound) {
541 UncoverCol(star_col);
543 preimage_[0] = zero_row;
544 image_[0] = zero_col;
545 state_ = &HungarianOptimizer::MakeAugmentingPath;
560 void HungarianOptimizer::MakeAugmentingPath() {
588 int row = FindStarInCol(image_[count]);
590 if (
row != kHungarianOptimizerRowNotFound) {
592 preimage_[count] =
row;
593 image_[count] = image_[count - 1];
599 int col = FindPrimeInRow(preimage_[count]);
601 preimage_[count] = preimage_[count - 1];
607 for (
int i = 0; i <= count; ++i) {
608 int row = preimage_[i];
611 if (IsStarred(
row,
col)) {
620 state_ = &HungarianOptimizer::CoverStarredZeroes;
627 void HungarianOptimizer::AugmentPath() {
628 double minval = FindSmallestUncovered();
630 for (
int row = 0;
row < matrix_size_; ++
row) {
631 for (
int col = 0;
col < matrix_size_; ++
col) {
632 if (RowCovered(
row)) {
633 costs_[
row][
col] += minval;
636 if (!ColCovered(
col)) {
637 costs_[
row][
col] -= minval;
642 state_ = &HungarianOptimizer::PrimeZeroes;
646 for (
const auto& subvector :
input) {
647 for (
const auto& num : subvector) {
648 if (std::isnan(num)) {
649 LOG(
ERROR) <<
"The provided input contains " << num <<
".";
658 const std::vector<std::vector<double>>&
cost,
659 absl::flat_hash_map<int, int>* direct_assignment,
660 absl::flat_hash_map<int, int>* reverse_assignment) {
662 LOG(
ERROR) <<
"Returning before invoking the Hungarian optimizer.";
665 std::vector<int> agent;
666 std::vector<int> task;
668 hungarian_optimizer.
Minimize(&agent, &task);
669 for (
int i = 0; i < agent.size(); ++i) {
670 (*direct_assignment)[agent[i]] = task[i];
671 (*reverse_assignment)[task[i]] = agent[i];
676 const std::vector<std::vector<double>>&
cost,
677 absl::flat_hash_map<int, int>* direct_assignment,
678 absl::flat_hash_map<int, int>* reverse_assignment) {
680 LOG(
ERROR) <<
"Returning before invoking the Hungarian optimizer.";
683 std::vector<int> agent;
684 std::vector<int> task;
686 hungarian_optimizer.
Maximize(&agent, &task);
687 for (
int i = 0; i < agent.size(); ++i) {
688 (*direct_assignment)[agent[i]] = task[i];
689 (*reverse_assignment)[task[i]] = agent[i];