22 #include "absl/strings/str_format.h"
28 static constexpr
int kHungarianOptimizerRowNotFound = -1;
29 static constexpr
int kHungarianOptimizerColNotFound = -2;
45 void Maximize(std::vector<int>* preimage, std::vector<int>* image);
48 void Minimize(std::vector<int>* preimage, std::vector<int>* image);
53 typedef enum { NONE, PRIME, STAR } Mark;
58 void FindAssignments(std::vector<int>* preimage, std::vector<int>* image);
61 bool IsStarred(
int row,
int col)
const {
return marks_[
row][
col] == STAR; }
64 void Star(
int row,
int col) {
70 void UnStar(
int row,
int col) {
77 int FindStarInRow(
int row)
const;
81 int FindStarInCol(
int col)
const;
84 bool IsPrimed(
int row,
int col)
const {
return marks_[
row][
col] == PRIME; }
87 void Prime(
int row,
int col) { marks_[
row][
col] = PRIME; }
91 int FindPrimeInRow(
int row)
const;
97 bool ColContainsStar(
int col)
const {
return stars_in_col_[
col] > 0; }
100 bool RowCovered(
int row)
const {
return rows_covered_[
row]; }
103 void CoverRow(
int row) { rows_covered_[
row] =
true; }
106 void UncoverRow(
int row) { rows_covered_[
row] =
false; }
109 bool ColCovered(
int col)
const {
return cols_covered_[
col]; }
112 void CoverCol(
int col) { cols_covered_[
col] =
true; }
115 void UncoverCol(
int col) { cols_covered_[
col] =
false; }
121 double FindSmallestUncovered()
const;
125 bool FindZero(
int* zero_row,
int* zero_col)
const;
149 void CoverStarredZeroes();
167 void MakeAugmentingPath();
179 std::vector<std::vector<double>> costs_;
185 std::vector<bool> rows_covered_;
186 std::vector<bool> cols_covered_;
189 std::vector<std::vector<Mark>> marks_;
192 std::vector<int> stars_in_col_;
195 std::vector<int> preimage_;
196 std::vector<int> image_;
203 HungarianOptimizer::Step state_;
207 const std::vector<std::vector<double>>& costs)
220 width_ = costs.size();
223 height_ = costs[0].size();
228 matrix_size_ =
std::max(width_, height_);
235 costs_.resize(matrix_size_);
236 for (
int row = 0;
row < matrix_size_; ++
row) {
237 costs_[
row].resize(matrix_size_);
240 for (
int row = 0;
row < matrix_size_; ++
row) {
241 for (
int col = 0;
col < matrix_size_; ++
col) {
242 if ((
row >= width_) || (
col >= height_)) {
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) {
260 stars_in_col_.resize(matrix_size_);
262 rows_covered_.resize(matrix_size_);
263 cols_covered_.resize(matrix_size_);
265 preimage_.resize(matrix_size_ * 2);
266 image_.resize(matrix_size_ * 2);
273 std::vector<int>* image) {
277 for (
int col = 0;
col < height_; ++
col) {
288 std::vector<int>* image) {
290 FindAssignments(preimage, image);
296 void HungarianOptimizer::FindAssignments(std::vector<int>* preimage,
297 std::vector<int>* image) {
301 for (
int col = 0;
col < height_; ++
col) {
302 if (IsStarred(
row,
col)) {
303 preimage->push_back(
row);
304 image->push_back(
col);
317 int HungarianOptimizer::FindStarInRow(
int row)
const {
318 for (
int col = 0;
col < matrix_size_; ++
col) {
319 if (IsStarred(
row,
col)) {
324 return kHungarianOptimizerColNotFound;
329 int HungarianOptimizer::FindStarInCol(
int col)
const {
330 if (!ColContainsStar(
col)) {
331 return kHungarianOptimizerRowNotFound;
334 for (
int row = 0;
row < matrix_size_; ++
row) {
335 if (IsStarred(
row,
col)) {
341 return kHungarianOptimizerRowNotFound;
346 int HungarianOptimizer::FindPrimeInRow(
int row)
const {
347 for (
int col = 0;
col < matrix_size_; ++
col) {
353 return kHungarianOptimizerColNotFound;
357 void HungarianOptimizer::ClearPrimes() {
358 for (
int row = 0;
row < matrix_size_; ++
row) {
359 for (
int col = 0;
col < matrix_size_; ++
col) {
368 void HungarianOptimizer::ClearCovers() {
369 for (
int x = 0; x < matrix_size_; x++) {
376 double HungarianOptimizer::FindSmallestUncovered()
const {
379 for (
int row = 0;
row < matrix_size_; ++
row) {
380 if (RowCovered(
row)) {
384 for (
int col = 0;
col < matrix_size_; ++
col) {
385 if (ColCovered(
col)) {
398 bool HungarianOptimizer::FindZero(
int* zero_row,
int* zero_col)
const {
399 for (
int row = 0;
row < matrix_size_; ++
row) {
400 if (RowCovered(
row)) {
404 for (
int col = 0;
col < matrix_size_; ++
col) {
405 if (ColCovered(
col)) {
409 if (costs_[
row][
col] == 0) {
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]);
426 if (IsStarred(
row,
col)) {
439 void HungarianOptimizer::DoMunkres() {
440 state_ = &HungarianOptimizer::ReduceRows;
441 while (state_ !=
nullptr) {
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) {
455 for (
int col = 0;
col < matrix_size_; ++
col) {
456 costs_[
row][
col] -= min_cost;
459 state_ = &HungarianOptimizer::StarZeroes;
465 void HungarianOptimizer::StarZeroes() {
468 for (
int row = 0;
row < matrix_size_; ++
row) {
469 if (RowCovered(
row)) {
473 for (
int col = 0;
col < matrix_size_; ++
col) {
474 if (ColCovered(
col)) {
478 if (costs_[
row][
col] == 0) {
488 state_ = &HungarianOptimizer::CoverStarredZeroes;
495 void HungarianOptimizer::CoverStarredZeroes() {
498 for (
int col = 0;
col < matrix_size_; ++
col) {
499 if (ColContainsStar(
col)) {
505 if (num_covered >= matrix_size_) {
509 state_ = &HungarianOptimizer::PrimeZeroes;
518 void HungarianOptimizer::PrimeZeroes() {
526 int zero_row, zero_col;
527 if (!FindZero(&zero_row, &zero_col)) {
529 state_ = &HungarianOptimizer::AugmentPath;
533 Prime(zero_row, zero_col);
534 int star_col = FindStarInRow(zero_row);
536 if (star_col != kHungarianOptimizerColNotFound) {
538 UncoverCol(star_col);
540 preimage_[0] = zero_row;
541 image_[0] = zero_col;
542 state_ = &HungarianOptimizer::MakeAugmentingPath;
557 void HungarianOptimizer::MakeAugmentingPath() {
585 int row = FindStarInCol(image_[count]);
587 if (
row != kHungarianOptimizerRowNotFound) {
589 preimage_[count] =
row;
590 image_[count] = image_[count - 1];
596 int col = FindPrimeInRow(preimage_[count]);
598 preimage_[count] = preimage_[count - 1];
604 for (
int i = 0; i <= count; ++i) {
605 int row = preimage_[i];
608 if (IsStarred(
row,
col)) {
617 state_ = &HungarianOptimizer::CoverStarredZeroes;
624 void HungarianOptimizer::AugmentPath() {
625 double minval = FindSmallestUncovered();
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;
633 if (!ColCovered(
col)) {
634 costs_[
row][
col] -= minval;
639 state_ = &HungarianOptimizer::PrimeZeroes;
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 <<
".";
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) {
659 LOG(ERROR) <<
"Returning before invoking the Hungarian optimizer.";
662 std::vector<int> agent;
663 std::vector<int> task;
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];
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) {
677 LOG(ERROR) <<
"Returning before invoking the Hungarian optimizer.";
680 std::vector<int> agent;
681 std::vector<int> task;
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];
HungarianOptimizer(const std::vector< std::vector< double >> &costs)
void Minimize(std::vector< int > *preimage, std::vector< int > *image)
void Maximize(std::vector< int > *preimage, std::vector< int > *image)
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)
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)
bool InputContainsNan(const std::vector< std::vector< double >> &input)
static int input(yyscan_t yyscanner)