OR-Tools  9.2
hungarian_test.cc
Go to the documentation of this file.
1 // Copyright 2010-2021 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 // Test file for hungarian.h
15 
17 
18 #include <cstdint>
19 
20 #include "absl/container/flat_hash_map.h"
21 #include "gtest/gtest.h"
23 #include "ortools/base/macros.h"
24 #include "ortools/base/map_util.h"
25 
26 namespace operations_research {
27 
28 // Generic check function that checks consistency of a linear assignment
29 // result as well as whether the result is the expected one.
30 
31 void GenericCheck(const int expected_assignment_size,
32  const absl::flat_hash_map<int, int>& direct_assignment,
33  const absl::flat_hash_map<int, int>& reverse_assignment,
34  const int expected_agents[], const int expected_tasks[]) {
35  EXPECT_EQ(expected_assignment_size, direct_assignment.size());
36  EXPECT_EQ(expected_assignment_size, reverse_assignment.size());
37  for (int i = 0; i < expected_assignment_size; ++i) {
38  EXPECT_EQ(gtl::FindOrDie(direct_assignment, expected_agents[i]),
39  expected_tasks[i]);
40  EXPECT_EQ(gtl::FindOrDie(reverse_assignment, expected_tasks[i]),
41  expected_agents[i]);
42  }
43  for (const auto& direct_iter : direct_assignment) {
44  EXPECT_EQ(gtl::FindOrDie(reverse_assignment, direct_iter.second),
45  direct_iter.first)
46  << direct_iter.first << " -> " << direct_iter.second;
47  }
48 }
49 
50 void TestMinimization(const std::vector<std::vector<double>>& cost,
51  const int expected_assignment_size,
52  const int expected_agents[], const int expected_tasks[]) {
53  absl::flat_hash_map<int, int> direct_assignment;
54  absl::flat_hash_map<int, int> reverse_assignment;
55  MinimizeLinearAssignment(cost, &direct_assignment, &reverse_assignment);
56  SCOPED_TRACE("Minimization");
57  GenericCheck(expected_assignment_size, direct_assignment, reverse_assignment,
58  expected_agents, expected_tasks);
59 }
60 
61 void TestMaximization(const std::vector<std::vector<double>>& cost,
62  const int expected_assignment_size,
63  const int expected_agents[], const int expected_tasks[]) {
64  absl::flat_hash_map<int, int> direct_assignment;
65  absl::flat_hash_map<int, int> reverse_assignment;
66  MaximizeLinearAssignment(cost, &direct_assignment, &reverse_assignment);
67  SCOPED_TRACE("Maximization");
68  GenericCheck(expected_assignment_size, direct_assignment, reverse_assignment,
69  expected_agents, expected_tasks);
70 }
71 
72 // Test on an empty matrix
73 
74 TEST(LinearAssignmentTest, NullMatrix) {
75  std::vector<std::vector<double>> cost;
76  const int* expected_agents = nullptr;
77  const int* expected_tasks = nullptr;
78  TestMinimization(cost, 0, expected_agents, expected_tasks);
79  TestMaximization(cost, 0, expected_agents, expected_tasks);
80 }
81 
82 // Testing with NaN value in the input.
83 TEST(LinearAssignmentTest, InvalidMatrix) {
84  const std::vector<std::vector<double>> cost_nan = {{1, 2},
85  {-std::sqrt(-1), 3}};
86  const int* expected_agents = nullptr;
87  const int* expected_tasks = nullptr;
88  TestMaximization(cost_nan, 0, expected_agents, expected_tasks);
89  TestMinimization(cost_nan, 0, expected_agents, expected_tasks);
90 }
91 
92 #define MATRIX_TEST \
93  { \
94  std::vector<std::vector<double>> cost(kMatrixHeight); \
95  for (int row = 0; row < kMatrixHeight; ++row) { \
96  cost[row].resize(kMatrixWidth); \
97  for (int col = 0; col < kMatrixWidth; ++col) { \
98  cost[row][col] = kCost[row][col]; \
99  } \
100  } \
101  EXPECT_EQ(arraysize(expected_agents_for_min), \
102  arraysize(expected_tasks_for_min)); \
103  EXPECT_EQ(arraysize(expected_agents_for_max), \
104  arraysize(expected_tasks_for_max)); \
105  const int assignment_size = arraysize(expected_agents_for_max); \
106  TestMinimization(cost, assignment_size, expected_agents_for_min, \
107  expected_tasks_for_min); \
108  TestMaximization(cost, assignment_size, expected_agents_for_max, \
109  expected_tasks_for_max); \
110  }
111 
112 // Test on a 1x1 matrix
113 
114 TEST(LinearAssignmentTest, SizeOneMatrix) {
115  const int kMatrixHeight = 1;
116  const int kMatrixWidth = 1;
117  const double kCost[kMatrixHeight][kMatrixWidth] = {{4}};
118  const int expected_agents_for_min[] = {0};
119  const int expected_tasks_for_min[] = {0};
120  const int expected_agents_for_max[] = {0};
121  const int expected_tasks_for_max[] = {0};
122  MATRIX_TEST;
123 }
124 
125 // Test on a 4x4 matrix. Example taken at
126 // http://www.ee.oulu.fi/~mpa/matreng/eem1_2-1.htm
127 TEST(LinearAssignmentTest, Small4x4Matrix) {
128  const int kMatrixHeight = 4;
129  const int kMatrixWidth = 4;
130  const double kCost[kMatrixHeight][kMatrixWidth] = {{90, 75, 75, 80},
131  {35, 85, 55, 65},
132  {125, 95, 90, 105},
133  {45, 110, 95, 115}};
134  const int expected_agents_for_min[] = {0, 1, 2, 3};
135  const int expected_tasks_for_min[] = {3, 2, 1, 0};
136  const int expected_agents_for_max[] = {0, 1, 2, 3};
137  const int expected_tasks_for_max[] = {2, 1, 0, 3};
138  MATRIX_TEST;
139 }
140 
141 // Test on a 3x4 matrix. Sub-problem of Small4x4Matrix
142 TEST(LinearAssignmentTest, Small3x4Matrix) {
143  const int kMatrixHeight = 3;
144  const int kMatrixWidth = 4;
145  const double kCost[kMatrixHeight][kMatrixWidth] = {
146  {90, 75, 75, 80}, {35, 85, 55, 65}, {125, 95, 90, 105}};
147  const int expected_agents_for_min[] = {0, 1, 2};
148  const int expected_tasks_for_min[] = {1, 0, 2};
149  const int expected_agents_for_max[] = {0, 1, 2};
150  const int expected_tasks_for_max[] = {3, 1, 0};
151  MATRIX_TEST;
152 }
153 
154 // Test on a 4x3 matrix. Sub-problem of Small4x4Matrix
155 TEST(LinearAssignmentTest, Small4x3Matrix) {
156  const int kMatrixHeight = 4;
157  const int kMatrixWidth = 3;
158  const double kCost[kMatrixHeight][kMatrixWidth] = {
159  {90, 75, 75}, {35, 85, 55}, {125, 95, 90}, {45, 110, 95}};
160  const int expected_agents_for_min[] = {0, 1, 3};
161  const int expected_tasks_for_min[] = {1, 2, 0};
162  const int expected_agents_for_max[] = {0, 2, 3};
163  const int expected_tasks_for_max[] = {2, 0, 1};
164  MATRIX_TEST;
165 }
166 
167 #undef MATRIX_TEST
168 
169 } // namespace operations_research
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)
Definition: hungarian.cc:652
void GenericCheck(const int expected_assignment_size, const absl::flat_hash_map< int, int > &direct_assignment, const absl::flat_hash_map< int, int > &reverse_assignment, const int expected_agents[], const int expected_tasks[])
void TestMaximization(const std::vector< std::vector< double >> &cost, const int expected_assignment_size, const int expected_agents[], const int expected_tasks[])
TEST(LinearAssignmentTest, NullMatrix)
#define MATRIX_TEST
void TestMinimization(const std::vector< std::vector< double >> &cost, const int expected_assignment_size, const int expected_agents[], const int expected_tasks[])
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition: map_util.h:206
int64_t cost
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)
Definition: hungarian.cc:670