23#include "absl/strings/str_format.h"
24#include "absl/types/span.h"
30 static constexpr int kHungarianOptimizerRowNotFound = -1;
31 static constexpr int kHungarianOptimizerColNotFound = -2;
47 void Maximize(std::vector<int>* preimage, std::vector<int>* image);
50 void Minimize(std::vector<int>* preimage, std::vector<int>* image);
55 typedef enum : uint8_t { NONE, PRIME, STAR } Mark;
60 void FindAssignments(std::vector<int>* preimage, std::vector<int>* image);
63 bool IsStarred(
int row,
int col)
const {
return marks_[row][col] == STAR; }
66 void Star(
int row,
int col) {
67 marks_[row][col] = STAR;
72 void UnStar(
int row,
int col) {
73 marks_[row][col] = NONE;
79 int FindStarInRow(
int row)
const;
83 int FindStarInCol(
int col)
const;
86 bool IsPrimed(
int row,
int col)
const {
return marks_[row][col] == PRIME; }
89 void Prime(
int row,
int col) { marks_[row][col] = PRIME; }
93 int FindPrimeInRow(
int row)
const;
99 bool ColContainsStar(
int col)
const {
return stars_in_col_[col] > 0; }
102 bool RowCovered(
int row)
const {
return rows_covered_[row]; }
105 void CoverRow(
int row) { rows_covered_[row] =
true; }
108 void UncoverRow(
int row) { rows_covered_[row] =
false; }
111 bool ColCovered(
int col)
const {
return cols_covered_[col]; }
114 void CoverCol(
int col) { cols_covered_[col] =
true; }
117 void UncoverCol(
int col) { cols_covered_[col] =
false; }
123 double FindSmallestUncovered()
const;
127 bool FindZero(
int* zero_row,
int* zero_col)
const;
151 void CoverStarredZeroes();
169 void MakeAugmentingPath();
181 std::vector<std::vector<double>> costs_;
187 std::vector<bool> rows_covered_;
188 std::vector<bool> cols_covered_;
191 std::vector<std::vector<Mark>> marks_;
194 std::vector<int> stars_in_col_;
197 std::vector<int> preimage_;
198 std::vector<int> image_;
205 HungarianOptimizer::Step state_;
209 absl::Span<
const std::vector<double>> costs)
222 width_ = costs.size();
225 height_ = costs[0].size();
230 matrix_size_ = std::max(width_, height_);
237 costs_.resize(matrix_size_);
238 for (
int row = 0; row < matrix_size_; ++row) {
239 costs_[row].resize(matrix_size_);
242 for (
int row = 0; row < matrix_size_; ++row) {
243 for (
int col = 0; col < matrix_size_; ++col) {
244 if ((row >= width_) || (col >= height_)) {
245 costs_[row][col] = 0;
247 costs_[row][col] = costs[row][col];
248 max_cost_ = std::max(max_cost_, costs_[row][col]);
254 marks_.resize(matrix_size_);
255 for (
int row = 0; row < matrix_size_; ++row) {
256 marks_[row].resize(matrix_size_);
257 for (
int col = 0; col < matrix_size_; ++col) {
258 marks_[row][col] = NONE;
262 stars_in_col_.resize(matrix_size_);
264 rows_covered_.resize(matrix_size_);
265 cols_covered_.resize(matrix_size_);
267 preimage_.resize(matrix_size_ * 2);
268 image_.resize(matrix_size_ * 2);
275 std::vector<int>* image) {
278 for (
int row = 0; row < width_; ++row) {
279 for (
int col = 0; col < height_; ++col) {
280 costs_[row][col] = max_cost_ - costs_[row][col];
290 std::vector<int>* image) {
292 FindAssignments(preimage, image);
298void HungarianOptimizer::FindAssignments(std::vector<int>* preimage,
299 std::vector<int>* image) {
302 for (
int row = 0; row < width_; ++row) {
303 for (
int col = 0; col < height_; ++col) {
304 if (IsStarred(row, col)) {
305 preimage->push_back(row);
306 image->push_back(col);
319int HungarianOptimizer::FindStarInRow(
int row)
const {
320 for (
int col = 0; col < matrix_size_; ++col) {
321 if (IsStarred(row, col)) {
326 return kHungarianOptimizerColNotFound;
331int HungarianOptimizer::FindStarInCol(
int col)
const {
332 if (!ColContainsStar(col)) {
333 return kHungarianOptimizerRowNotFound;
336 for (
int row = 0; row < matrix_size_; ++row) {
337 if (IsStarred(row, col)) {
343 return kHungarianOptimizerRowNotFound;
348int HungarianOptimizer::FindPrimeInRow(
int row)
const {
349 for (
int col = 0; col < matrix_size_; ++col) {
350 if (IsPrimed(row, col)) {
355 return kHungarianOptimizerColNotFound;
359void HungarianOptimizer::ClearPrimes() {
360 for (
int row = 0; row < matrix_size_; ++row) {
361 for (
int col = 0; col < matrix_size_; ++col) {
362 if (IsPrimed(row, col)) {
363 marks_[row][col] = NONE;
370void HungarianOptimizer::ClearCovers() {
371 for (
int x = 0;
x < matrix_size_;
x++) {
378double HungarianOptimizer::FindSmallestUncovered()
const {
379 double minval = std::numeric_limits<double>::max();
381 for (
int row = 0; row < matrix_size_; ++row) {
382 if (RowCovered(row)) {
386 for (
int col = 0; col < matrix_size_; ++col) {
387 if (ColCovered(col)) {
391 minval = std::min(minval, costs_[row][col]);
400bool HungarianOptimizer::FindZero(
int* zero_row,
int* zero_col)
const {
401 for (
int row = 0; row < matrix_size_; ++row) {
402 if (RowCovered(row)) {
406 for (
int col = 0; col < matrix_size_; ++col) {
407 if (ColCovered(col)) {
411 if (costs_[row][col] == 0) {
423void HungarianOptimizer::PrintMatrix() {
424 for (
int row = 0; row < matrix_size_; ++row) {
425 for (
int col = 0; col < matrix_size_; ++col) {
426 absl::PrintF(
"%g ", costs_[row][col]);
428 if (IsStarred(row, col)) {
432 if (IsPrimed(row, col)) {
441void HungarianOptimizer::DoMunkres() {
442 state_ = &HungarianOptimizer::ReduceRows;
443 while (state_ !=
nullptr) {
451void HungarianOptimizer::ReduceRows() {
452 for (
int row = 0; row < matrix_size_; ++row) {
453 double min_cost = costs_[row][0];
454 for (
int col = 1; col < matrix_size_; ++col) {
455 min_cost = std::min(min_cost, costs_[row][col]);
457 for (
int col = 0; col < matrix_size_; ++col) {
458 costs_[row][col] -= min_cost;
461 state_ = &HungarianOptimizer::StarZeroes;
467void HungarianOptimizer::StarZeroes() {
470 for (
int row = 0; row < matrix_size_; ++row) {
471 if (RowCovered(row)) {
475 for (
int col = 0; col < matrix_size_; ++col) {
476 if (ColCovered(col)) {
480 if (costs_[row][col] == 0) {
490 state_ = &HungarianOptimizer::CoverStarredZeroes;
497void HungarianOptimizer::CoverStarredZeroes() {
500 for (
int col = 0; col < matrix_size_; ++col) {
501 if (ColContainsStar(col)) {
507 if (num_covered >= matrix_size_) {
511 state_ = &HungarianOptimizer::PrimeZeroes;
520void HungarianOptimizer::PrimeZeroes() {
528 int zero_row, zero_col;
529 if (!FindZero(&zero_row, &zero_col)) {
531 state_ = &HungarianOptimizer::AugmentPath;
535 Prime(zero_row, zero_col);
536 int star_col = FindStarInRow(zero_row);
538 if (star_col != kHungarianOptimizerColNotFound) {
540 UncoverCol(star_col);
542 preimage_[0] = zero_row;
543 image_[0] = zero_col;
544 state_ = &HungarianOptimizer::MakeAugmentingPath;
559void HungarianOptimizer::MakeAugmentingPath() {
587 int row = FindStarInCol(image_[count]);
589 if (row != kHungarianOptimizerRowNotFound) {
591 preimage_[count] = row;
592 image_[count] = image_[count - 1];
598 int col = FindPrimeInRow(preimage_[count]);
600 preimage_[count] = preimage_[count - 1];
606 for (
int i = 0;
i <= count; ++
i) {
607 int row = preimage_[
i];
610 if (IsStarred(row, col)) {
619 state_ = &HungarianOptimizer::CoverStarredZeroes;
626void HungarianOptimizer::AugmentPath() {
627 double minval = FindSmallestUncovered();
629 for (
int row = 0; row < matrix_size_; ++row) {
630 for (
int col = 0; col < matrix_size_; ++col) {
631 if (RowCovered(row)) {
632 costs_[row][col] += minval;
635 if (!ColCovered(col)) {
636 costs_[row][col] -= minval;
641 state_ = &HungarianOptimizer::PrimeZeroes;
645 for (
const auto& subvector :
input) {
646 for (
const auto& num : subvector) {
647 if (std::isnan(num)) {
648 LOG(ERROR) <<
"The provided input contains " << num <<
".";
657 absl::Span<
const std::vector<double>> cost,
658 absl::flat_hash_map<int, int>* direct_assignment,
659 absl::flat_hash_map<int, int>* reverse_assignment) {
661 LOG(ERROR) <<
"Returning before invoking the Hungarian optimizer.";
664 std::vector<int> agent;
665 std::vector<int> task;
667 hungarian_optimizer.
Minimize(&agent, &task);
668 for (
int i = 0; i < agent.size(); ++i) {
669 (*direct_assignment)[agent[i]] = task[i];
670 (*reverse_assignment)[task[i]] = agent[i];
675 absl::Span<
const std::vector<double>> cost,
676 absl::flat_hash_map<int, int>* direct_assignment,
677 absl::flat_hash_map<int, int>* reverse_assignment) {
679 LOG(ERROR) <<
"Returning before invoking the Hungarian optimizer.";
682 std::vector<int> agent;
683 std::vector<int> task;
685 hungarian_optimizer.
Maximize(&agent, &task);
686 for (
int i = 0; i < agent.size(); ++i) {
687 (*direct_assignment)[agent[i]] = task[i];
688 (*reverse_assignment)[task[i]] = agent[i];
void Minimize(std::vector< int > *preimage, std::vector< int > *image)
Like Maximize(), but minimizing the cost instead.
HungarianOptimizer(absl::Span< const std::vector< double > > costs)
Setup the initial conditions for the algorithm.
void Maximize(std::vector< int > *preimage, std::vector< int > *image)
In SWIG mode, we don't want anything besides these top-level includes.
bool InputContainsNan(absl::Span< const std::vector< double > > input)
void MinimizeLinearAssignment(absl::Span< const std::vector< double > > cost, absl::flat_hash_map< int, int > *direct_assignment, absl::flat_hash_map< int, int > *reverse_assignment)
See IMPORTANT NOTE at the top of the file.
void MaximizeLinearAssignment(absl::Span< const std::vector< double > > cost, absl::flat_hash_map< int, int > *direct_assignment, absl::flat_hash_map< int, int > *reverse_assignment)
See IMPORTANT NOTE at the top of the file.
static int input(yyscan_t yyscanner)