20 #include "absl/strings/str_format.h" 26 static constexpr
int kHungarianOptimizerRowNotFound = -1;
27 static constexpr
int kHungarianOptimizerColNotFound = -2;
43 void Maximize(std::vector<int>* preimage, std::vector<int>* image);
46 void Minimize(std::vector<int>* preimage, std::vector<int>* image);
51 typedef enum { NONE, PRIME, STAR } Mark;
56 void FindAssignments(std::vector<int>* preimage, std::vector<int>* image);
59 bool IsStarred(
int row,
int col)
const {
return marks_[
row][
col] == STAR; }
62 void Star(
int row,
int col) {
68 void UnStar(
int row,
int col) {
75 int FindStarInRow(
int row)
const;
79 int FindStarInCol(
int col)
const;
82 bool IsPrimed(
int row,
int col)
const {
return marks_[
row][
col] == PRIME; }
85 void Prime(
int row,
int col) { marks_[
row][
col] = PRIME; }
89 int FindPrimeInRow(
int row)
const;
95 bool ColContainsStar(
int col)
const {
return stars_in_col_[
col] > 0; }
98 bool RowCovered(
int row)
const {
return rows_covered_[
row]; }
101 void CoverRow(
int row) { rows_covered_[
row] =
true; }
104 void UncoverRow(
int row) { rows_covered_[
row] =
false; }
107 bool ColCovered(
int col)
const {
return cols_covered_[
col]; }
110 void CoverCol(
int col) { cols_covered_[
col] =
true; }
113 void UncoverCol(
int col) { cols_covered_[
col] =
false; }
119 double FindSmallestUncovered()
const;
123 bool FindZero(
int* zero_row,
int* zero_col)
const;
147 void CoverStarredZeroes();
165 void MakeAugmentingPath();
177 std::vector<std::vector<double>> costs_;
183 std::vector<bool> rows_covered_;
184 std::vector<bool> cols_covered_;
187 std::vector<std::vector<Mark>> marks_;
190 std::vector<int> stars_in_col_;
193 std::vector<int> preimage_;
194 std::vector<int> image_;
201 HungarianOptimizer::Step state_;
205 const std::vector<std::vector<double>>& costs)
218 width_ = costs.size();
221 height_ = costs[0].size();
226 matrix_size_ =
std::max(width_, height_);
233 costs_.resize(matrix_size_);
234 for (
int row = 0;
row < matrix_size_; ++
row) {
235 costs_[
row].resize(matrix_size_);
238 for (
int row = 0;
row < matrix_size_; ++
row) {
239 for (
int col = 0;
col < matrix_size_; ++
col) {
240 if ((
row >= width_) || (
col >= height_)) {
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) {
258 stars_in_col_.resize(matrix_size_);
260 rows_covered_.resize(matrix_size_);
261 cols_covered_.resize(matrix_size_);
263 preimage_.resize(matrix_size_ * 2);
264 image_.resize(matrix_size_ * 2);
271 std::vector<int>* image) {
275 for (
int col = 0;
col < height_; ++
col) {
286 std::vector<int>* image) {
288 FindAssignments(preimage, image);
294 void HungarianOptimizer::FindAssignments(std::vector<int>* preimage,
295 std::vector<int>* image) {
299 for (
int col = 0;
col < height_; ++
col) {
300 if (IsStarred(
row,
col)) {
301 preimage->push_back(
row);
302 image->push_back(
col);
315 int HungarianOptimizer::FindStarInRow(
int row)
const {
316 for (
int col = 0;
col < matrix_size_; ++
col) {
317 if (IsStarred(
row,
col)) {
322 return kHungarianOptimizerColNotFound;
327 int HungarianOptimizer::FindStarInCol(
int col)
const {
328 if (!ColContainsStar(
col)) {
329 return kHungarianOptimizerRowNotFound;
332 for (
int row = 0;
row < matrix_size_; ++
row) {
333 if (IsStarred(
row,
col)) {
339 return kHungarianOptimizerRowNotFound;
344 int HungarianOptimizer::FindPrimeInRow(
int row)
const {
345 for (
int col = 0;
col < matrix_size_; ++
col) {
351 return kHungarianOptimizerColNotFound;
355 void HungarianOptimizer::ClearPrimes() {
356 for (
int row = 0;
row < matrix_size_; ++
row) {
357 for (
int col = 0;
col < matrix_size_; ++
col) {
366 void HungarianOptimizer::ClearCovers() {
367 for (
int x = 0; x < matrix_size_; x++) {
374 double HungarianOptimizer::FindSmallestUncovered()
const {
377 for (
int row = 0;
row < matrix_size_; ++
row) {
378 if (RowCovered(
row)) {
382 for (
int col = 0;
col < matrix_size_; ++
col) {
383 if (ColCovered(
col)) {
396 bool HungarianOptimizer::FindZero(
int* zero_row,
int* zero_col)
const {
397 for (
int row = 0;
row < matrix_size_; ++
row) {
398 if (RowCovered(
row)) {
402 for (
int col = 0;
col < matrix_size_; ++
col) {
403 if (ColCovered(
col)) {
407 if (costs_[
row][
col] == 0) {
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]);
424 if (IsStarred(
row,
col)) {
437 void HungarianOptimizer::DoMunkres() {
438 state_ = &HungarianOptimizer::ReduceRows;
439 while (state_ !=
nullptr) {
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) {
453 for (
int col = 0;
col < matrix_size_; ++
col) {
454 costs_[
row][
col] -= min_cost;
457 state_ = &HungarianOptimizer::StarZeroes;
466 void HungarianOptimizer::StarZeroes() {
469 for (
int row = 0;
row < matrix_size_; ++
row) {
470 if (RowCovered(
row)) {
474 for (
int col = 0;
col < matrix_size_; ++
col) {
475 if (ColCovered(
col)) {
479 if (costs_[
row][
col] == 0) {
489 state_ = &HungarianOptimizer::CoverStarredZeroes;
496 void HungarianOptimizer::CoverStarredZeroes() {
499 for (
int col = 0;
col < matrix_size_; ++
col) {
500 if (ColContainsStar(
col)) {
506 if (num_covered >= matrix_size_) {
510 state_ = &HungarianOptimizer::PrimeZeroes;
519 void HungarianOptimizer::PrimeZeroes() {
527 int zero_row, zero_col;
528 if (!FindZero(&zero_row, &zero_col)) {
530 state_ = &HungarianOptimizer::AugmentPath;
534 Prime(zero_row, zero_col);
535 int star_col = FindStarInRow(zero_row);
537 if (star_col != kHungarianOptimizerColNotFound) {
539 UncoverCol(star_col);
541 preimage_[0] = zero_row;
542 image_[0] = zero_col;
543 state_ = &HungarianOptimizer::MakeAugmentingPath;
558 void HungarianOptimizer::MakeAugmentingPath() {
586 int row = FindStarInCol(image_[count]);
588 if (
row != kHungarianOptimizerRowNotFound) {
590 preimage_[count] =
row;
591 image_[count] = image_[count - 1];
597 int col = FindPrimeInRow(preimage_[count]);
599 preimage_[count] = preimage_[count - 1];
605 for (
int i = 0; i <= count; ++i) {
606 int row = preimage_[i];
609 if (IsStarred(
row,
col)) {
618 state_ = &HungarianOptimizer::CoverStarredZeroes;
625 void HungarianOptimizer::AugmentPath() {
626 double minval = FindSmallestUncovered();
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;
634 if (!ColCovered(
col)) {
635 costs_[
row][
col] -= minval;
640 state_ = &HungarianOptimizer::PrimeZeroes;
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 <<
".";
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) {
660 LOG(
ERROR) <<
"Returning before invoking the Hungarian optimizer.";
663 std::vector<int> agent;
664 std::vector<int> task;
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];
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) {
678 LOG(
ERROR) <<
"Returning before invoking the Hungarian optimizer.";
681 std::vector<int> agent;
682 std::vector<int> task;
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];
bool InputContainsNan(const std::vector< std::vector< double >> &input)
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)
HungarianOptimizer(const std::vector< std::vector< double >> &costs)
static int input(yyscan_t yyscanner)
Collection of objects used to extend the Constraint Solver library.
void Minimize(std::vector< int > *preimage, std::vector< int > *image)
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 Maximize(std::vector< int > *preimage, std::vector< int > *image)