22#include "absl/strings/str_format.h"
23#include "absl/types/span.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 absl::Span<
const 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_)) {
247 max_cost_ = std::max(max_cost_, costs_[
row][
col]);
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);
297void 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);
318int HungarianOptimizer::FindStarInRow(
int row)
const {
319 for (
int col = 0;
col < matrix_size_; ++
col) {
320 if (IsStarred(
row,
col)) {
325 return kHungarianOptimizerColNotFound;
330int 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;
347int HungarianOptimizer::FindPrimeInRow(
int row)
const {
348 for (
int col = 0;
col < matrix_size_; ++
col) {
354 return kHungarianOptimizerColNotFound;
358void HungarianOptimizer::ClearPrimes() {
359 for (
int row = 0;
row < matrix_size_; ++
row) {
360 for (
int col = 0;
col < matrix_size_; ++
col) {
369void HungarianOptimizer::ClearCovers() {
370 for (
int x = 0;
x < matrix_size_;
x++) {
377double HungarianOptimizer::FindSmallestUncovered()
const {
378 double minval = std::numeric_limits<double>::max();
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)) {
390 minval = std::min(minval, costs_[
row][
col]);
399bool 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) {
422void 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)) {
440void HungarianOptimizer::DoMunkres() {
441 state_ = &HungarianOptimizer::ReduceRows;
442 while (state_ !=
nullptr) {
450void 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) {
454 min_cost = std::min(min_cost, costs_[
row][
col]);
456 for (
int col = 0;
col < matrix_size_; ++
col) {
457 costs_[
row][
col] -= min_cost;
460 state_ = &HungarianOptimizer::StarZeroes;
466void 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;
496void 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;
519void 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;
558void 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;
625void 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 absl::Span<
const 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 absl::Span<
const 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];
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)