OR-Tools  9.6
hungarian_test.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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 <cmath>
19 #include <cstdint>
20 #include <random>
21 #include <vector>
22 
23 #include "absl/container/flat_hash_map.h"
24 #include "absl/random/distributions.h"
25 #include "gtest/gtest.h"
27 #include "ortools/base/macros.h"
28 #include "ortools/base/map_util.h"
29 
30 namespace operations_research {
31 
32 // Generic check function that checks consistency of a linear assignment
33 // result as well as whether the result is the expected one.
34 
35 void GenericCheck(const int expected_assignment_size,
36  const absl::flat_hash_map<int, int>& direct_assignment,
37  const absl::flat_hash_map<int, int>& reverse_assignment,
38  const int expected_agents[], const int expected_tasks[]) {
39  EXPECT_EQ(expected_assignment_size, direct_assignment.size());
40  EXPECT_EQ(expected_assignment_size, reverse_assignment.size());
41  for (int i = 0; i < expected_assignment_size; ++i) {
42  EXPECT_EQ(gtl::FindOrDie(direct_assignment, expected_agents[i]),
43  expected_tasks[i]);
44  EXPECT_EQ(gtl::FindOrDie(reverse_assignment, expected_tasks[i]),
45  expected_agents[i]);
46  }
47  for (const auto& direct_iter : direct_assignment) {
48  EXPECT_EQ(gtl::FindOrDie(reverse_assignment, direct_iter.second),
49  direct_iter.first)
50  << direct_iter.first << " -> " << direct_iter.second;
51  }
52 }
53 
54 void TestMinimization(const std::vector<std::vector<double>>& cost,
55  const int expected_assignment_size,
56  const int expected_agents[], const int expected_tasks[]) {
57  absl::flat_hash_map<int, int> direct_assignment;
58  absl::flat_hash_map<int, int> reverse_assignment;
59  MinimizeLinearAssignment(cost, &direct_assignment, &reverse_assignment);
60  SCOPED_TRACE("Minimization");
61  GenericCheck(expected_assignment_size, direct_assignment, reverse_assignment,
62  expected_agents, expected_tasks);
63 }
64 
65 void TestMaximization(const std::vector<std::vector<double>>& cost,
66  const int expected_assignment_size,
67  const int expected_agents[], const int expected_tasks[]) {
68  absl::flat_hash_map<int, int> direct_assignment;
69  absl::flat_hash_map<int, int> reverse_assignment;
70  MaximizeLinearAssignment(cost, &direct_assignment, &reverse_assignment);
71  SCOPED_TRACE("Maximization");
72  GenericCheck(expected_assignment_size, direct_assignment, reverse_assignment,
73  expected_agents, expected_tasks);
74 }
75 
76 // Test on an empty matrix
77 
78 TEST(LinearAssignmentTest, NullMatrix) {
79  std::vector<std::vector<double>> cost;
80  const int* expected_agents = nullptr;
81  const int* expected_tasks = nullptr;
82  TestMinimization(cost, 0, expected_agents, expected_tasks);
83  TestMaximization(cost, 0, expected_agents, expected_tasks);
84 }
85 
86 // Testing with NaN value in the input.
87 TEST(LinearAssignmentTest, InvalidMatrix) {
88  const std::vector<std::vector<double>> cost_nan = {{1, 2},
89  {-std::sqrt(-1), 3}};
90  const int* expected_agents = nullptr;
91  const int* expected_tasks = nullptr;
92  TestMaximization(cost_nan, 0, expected_agents, expected_tasks);
93  TestMinimization(cost_nan, 0, expected_agents, expected_tasks);
94 }
95 
96 #define MATRIX_TEST \
97  { \
98  std::vector<std::vector<double>> cost(kMatrixHeight); \
99  for (int row = 0; row < kMatrixHeight; ++row) { \
100  cost[row].resize(kMatrixWidth); \
101  for (int col = 0; col < kMatrixWidth; ++col) { \
102  cost[row][col] = kCost[row][col]; \
103  } \
104  } \
105  EXPECT_EQ(arraysize(expected_agents_for_min), \
106  arraysize(expected_tasks_for_min)); \
107  EXPECT_EQ(arraysize(expected_agents_for_max), \
108  arraysize(expected_tasks_for_max)); \
109  const int assignment_size = arraysize(expected_agents_for_max); \
110  TestMinimization(cost, assignment_size, expected_agents_for_min, \
111  expected_tasks_for_min); \
112  TestMaximization(cost, assignment_size, expected_agents_for_max, \
113  expected_tasks_for_max); \
114  }
115 
116 // Test on a 1x1 matrix
117 
118 TEST(LinearAssignmentTest, SizeOneMatrix) {
119  const int kMatrixHeight = 1;
120  const int kMatrixWidth = 1;
121  const double kCost[kMatrixHeight][kMatrixWidth] = {{4}};
122  const int expected_agents_for_min[] = {0};
123  const int expected_tasks_for_min[] = {0};
124  const int expected_agents_for_max[] = {0};
125  const int expected_tasks_for_max[] = {0};
126  MATRIX_TEST;
127 }
128 
129 // Test on a 4x4 matrix. Example taken at
130 // http://www.ee.oulu.fi/~mpa/matreng/eem1_2-1.htm
131 TEST(LinearAssignmentTest, Small4x4Matrix) {
132  const int kMatrixHeight = 4;
133  const int kMatrixWidth = 4;
134  const double kCost[kMatrixHeight][kMatrixWidth] = {{90, 75, 75, 80},
135  {35, 85, 55, 65},
136  {125, 95, 90, 105},
137  {45, 110, 95, 115}};
138  const int expected_agents_for_min[] = {0, 1, 2, 3};
139  const int expected_tasks_for_min[] = {3, 2, 1, 0};
140  const int expected_agents_for_max[] = {0, 1, 2, 3};
141  const int expected_tasks_for_max[] = {2, 1, 0, 3};
142  MATRIX_TEST;
143 }
144 
145 // Test on a 3x4 matrix. Sub-problem of Small4x4Matrix
146 TEST(LinearAssignmentTest, Small3x4Matrix) {
147  const int kMatrixHeight = 3;
148  const int kMatrixWidth = 4;
149  const double kCost[kMatrixHeight][kMatrixWidth] = {
150  {90, 75, 75, 80}, {35, 85, 55, 65}, {125, 95, 90, 105}};
151  const int expected_agents_for_min[] = {0, 1, 2};
152  const int expected_tasks_for_min[] = {1, 0, 2};
153  const int expected_agents_for_max[] = {0, 1, 2};
154  const int expected_tasks_for_max[] = {3, 1, 0};
155  MATRIX_TEST;
156 }
157 
158 // Test on a 4x3 matrix. Sub-problem of Small4x4Matrix
159 TEST(LinearAssignmentTest, Small4x3Matrix) {
160  const int kMatrixHeight = 4;
161  const int kMatrixWidth = 3;
162  const double kCost[kMatrixHeight][kMatrixWidth] = {
163  {90, 75, 75}, {35, 85, 55}, {125, 95, 90}, {45, 110, 95}};
164  const int expected_agents_for_min[] = {0, 1, 3};
165  const int expected_tasks_for_min[] = {1, 2, 0};
166  const int expected_agents_for_max[] = {0, 2, 3};
167  const int expected_tasks_for_max[] = {2, 0, 1};
168  MATRIX_TEST;
169 }
170 
171 #undef MATRIX_TEST
172 
173 } // namespace operations_research
#define MATRIX_TEST
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition: map_util.h:206
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:672
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:654
TEST(LinearAssignmentTest, NullMatrix)
void TestMaximization(const std::vector< std::vector< double >> &cost, const int expected_assignment_size, const int expected_agents[], const int expected_tasks[])
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 TestMinimization(const std::vector< std::vector< double >> &cost, const int expected_assignment_size, const int expected_agents[], const int expected_tasks[])
int64_t cost