OR-Tools  9.3
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"
25
26namespace 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
31void 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
50void 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
61void 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
74TEST(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.
83TEST(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
114TEST(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};
123}
124
125// Test on a 4x4 matrix. Example taken at
126// http://www.ee.oulu.fi/~mpa/matreng/eem1_2-1.htm
127TEST(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};
139}
140
141// Test on a 3x4 matrix. Sub-problem of Small4x4Matrix
142TEST(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};
152}
153
154// Test on a 4x3 matrix. Sub-problem of Small4x4Matrix
155TEST(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};
165}
166
167#undef MATRIX_TEST
168
169} // 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 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 TestMaximization(const std::vector< std::vector< double > > &cost, const int expected_assignment_size, const int expected_agents[], const int expected_tasks[])
TEST(LinearAssignmentTest, NullMatrix)
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[])
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
int64_t cost