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);
294void 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);
315int HungarianOptimizer::FindStarInRow(
int row)
const {
316 for (
int col = 0;
col < matrix_size_; ++
col) {
317 if (IsStarred(
row,
col)) {
322 return kHungarianOptimizerColNotFound;
327int 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;
344int HungarianOptimizer::FindPrimeInRow(
int row)
const {
345 for (
int col = 0;
col < matrix_size_; ++
col) {
351 return kHungarianOptimizerColNotFound;
355void HungarianOptimizer::ClearPrimes() {
356 for (
int row = 0;
row < matrix_size_; ++
row) {
357 for (
int col = 0;
col < matrix_size_; ++
col) {
366void HungarianOptimizer::ClearCovers() {
367 for (
int x = 0; x < matrix_size_; x++) {
374double 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)) {
396bool 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) {
419void 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)) {
437void HungarianOptimizer::DoMunkres() {
438 state_ = &HungarianOptimizer::ReduceRows;
439 while (state_ !=
nullptr) {
447void 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;
463void HungarianOptimizer::StarZeroes() {
466 for (
int row = 0;
row < matrix_size_; ++
row) {
467 if (RowCovered(
row)) {
471 for (
int col = 0;
col < matrix_size_; ++
col) {
472 if (ColCovered(
col)) {
476 if (costs_[
row][
col] == 0) {
486 state_ = &HungarianOptimizer::CoverStarredZeroes;
493void HungarianOptimizer::CoverStarredZeroes() {
496 for (
int col = 0;
col < matrix_size_; ++
col) {
497 if (ColContainsStar(
col)) {
503 if (num_covered >= matrix_size_) {
507 state_ = &HungarianOptimizer::PrimeZeroes;
516void HungarianOptimizer::PrimeZeroes() {
524 int zero_row, zero_col;
525 if (!FindZero(&zero_row, &zero_col)) {
527 state_ = &HungarianOptimizer::AugmentPath;
531 Prime(zero_row, zero_col);
532 int star_col = FindStarInRow(zero_row);
534 if (star_col != kHungarianOptimizerColNotFound) {
536 UncoverCol(star_col);
538 preimage_[0] = zero_row;
539 image_[0] = zero_col;
540 state_ = &HungarianOptimizer::MakeAugmentingPath;
555void HungarianOptimizer::MakeAugmentingPath() {
583 int row = FindStarInCol(image_[count]);
585 if (
row != kHungarianOptimizerRowNotFound) {
587 preimage_[count] =
row;
588 image_[count] = image_[count - 1];
594 int col = FindPrimeInRow(preimage_[count]);
596 preimage_[count] = preimage_[count - 1];
602 for (
int i = 0; i <= count; ++i) {
603 int row = preimage_[i];
606 if (IsStarred(
row,
col)) {
615 state_ = &HungarianOptimizer::CoverStarredZeroes;
622void HungarianOptimizer::AugmentPath() {
623 double minval = FindSmallestUncovered();
625 for (
int row = 0;
row < matrix_size_; ++
row) {
626 for (
int col = 0;
col < matrix_size_; ++
col) {
627 if (RowCovered(
row)) {
628 costs_[
row][
col] += minval;
631 if (!ColCovered(
col)) {
632 costs_[
row][
col] -= minval;
637 state_ = &HungarianOptimizer::PrimeZeroes;
641 for (
const auto& subvector :
input) {
642 for (
const auto& num : subvector) {
643 if (std::isnan(num)) {
644 LOG(
ERROR) <<
"The provided input contains " << num <<
".";
653 const std::vector<std::vector<double>>&
cost,
654 absl::flat_hash_map<int, int>* direct_assignment,
655 absl::flat_hash_map<int, int>* reverse_assignment) {
657 LOG(
ERROR) <<
"Returning before invoking the Hungarian optimizer.";
660 std::vector<int> agent;
661 std::vector<int> task;
663 hungarian_optimizer.
Minimize(&agent, &task);
664 for (
int i = 0; i < agent.size(); ++i) {
665 (*direct_assignment)[agent[i]] = task[i];
666 (*reverse_assignment)[task[i]] = agent[i];
671 const std::vector<std::vector<double>>&
cost,
672 absl::flat_hash_map<int, int>* direct_assignment,
673 absl::flat_hash_map<int, int>* reverse_assignment) {
675 LOG(
ERROR) <<
"Returning before invoking the Hungarian optimizer.";
678 std::vector<int> agent;
679 std::vector<int> task;
681 hungarian_optimizer.
Maximize(&agent, &task);
682 for (
int i = 0; i < agent.size(); ++i) {
683 (*direct_assignment)[agent[i]] = task[i];
684 (*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 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)
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)
static int input(yyscan_t yyscanner)