Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
catchMunkres.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 by Inria. All rights reserved.
4 *
5 * This software is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 * See the file LICENSE.txt at the root directory of this source
10 * distribution for additional information about the GNU GPL.
11 *
12 * For using ViSP with software that can not be combined with the GNU
13 * GPL, please contact Inria about acquiring a ViSP Professional
14 * Edition License.
15 *
16 * See https://visp.inria.fr for more information.
17 *
18 * This software was developed at:
19 * Inria Rennes - Bretagne Atlantique
20 * Campus Universitaire de Beaulieu
21 * 35042 Rennes Cedex
22 * France
23 *
24 * If you have questions regarding the use of this file, please contact
25 * Inria at visp@inria.fr
26 *
27 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 *
30 * Description:
31 * Test Munkres assignment algorithm.
32 */
38
39#include <visp3/core/vpMunkres.h>
40
41// Check if std:c++17 or higher
42#if ((__cplusplus >= 201703L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L)))
43
44// System
45#include <iostream>
46
47#ifndef DOXYGEN_SHOULD_SKIP_THIS
48namespace std
49{
50
51// Helper to output a Munkres pair
52ostream &operator<<(ostream &os, const pair<unsigned int, unsigned int> &val)
53{
54 os << "[" << val.first << "," << val.second << "]";
55 return os;
56}
57} // namespace std
58#endif // DOXYGEN_SHOULD_SKIP_THIS
59
60#if defined(VISP_HAVE_CATCH2)
61
62#include <catch_amalgamated.hpp>
63
64#ifdef ENABLE_VISP_NAMESPACE
65using namespace VISP_NAMESPACE_NAME;
66#endif
67
68TEST_CASE("Check Munkres-based assignment", "[visp_munkres]")
69{
70 auto testMunkres = [](const std::vector<std::vector<double> > &cost_matrix,
71 const std::vector<std::pair<unsigned int, unsigned int> > &expected_pairs) {
72 const auto munkres_pairs = vpMunkres::run(cost_matrix);
73 REQUIRE(expected_pairs.size() == munkres_pairs.size());
74 for (auto i = 0u; i < munkres_pairs.size(); i++) {
75 REQUIRE(expected_pairs.at(i) == munkres_pairs.at(i));
76 }
77 };
78
79 SECTION("Square cost matrix")
80 {
81 std::vector<std::vector<double> > costs {};
82 costs.push_back(std::vector<double>{3, 1, 2});
83 costs.push_back(std::vector<double>{2, 3, 1});
84 costs.push_back(std::vector<double>{1, 2, 3});
85
86 std::vector<std::pair<unsigned int, unsigned int> > expected_pairs {};
87 expected_pairs.emplace_back(0, 1);
88 expected_pairs.emplace_back(1, 2);
89 expected_pairs.emplace_back(2, 0);
90
91 testMunkres(costs, expected_pairs);
92 }
93
94 SECTION("Horizontal cost matrix")
95 {
96 std::vector<std::vector<double> > costs {};
97 costs.push_back(std::vector<double>{4, 1, 2, 3});
98 costs.push_back(std::vector<double>{3, 4, 1, 2});
99 costs.push_back(std::vector<double>{2, 3, 4, 1});
100
101 std::vector<std::pair<unsigned int, unsigned int> > expected_pairs {};
102 expected_pairs.emplace_back(0, 1);
103 expected_pairs.emplace_back(1, 2);
104 expected_pairs.emplace_back(2, 3);
105
106 testMunkres(costs, expected_pairs);
107 }
108
109 SECTION("Vertical cost matrix")
110 {
111 std::vector<std::vector<double> > costs {};
112 costs.push_back(std::vector<double>{4, 1, 2});
113 costs.push_back(std::vector<double>{3, 4, 1});
114 costs.push_back(std::vector<double>{2, 3, 4});
115 costs.push_back(std::vector<double>{1, 2, 3});
116
117 std::vector<std::pair<unsigned int, unsigned int> > expected_pairs {};
118 expected_pairs.emplace_back(0, 1);
119 expected_pairs.emplace_back(1, 2);
120 expected_pairs.emplace_back(3, 0);
121
122 testMunkres(costs, expected_pairs);
123 }
124}
125
126int main(int argc, char *argv[])
127{
128 Catch::Session session;
129 session.applyCommandLine(argc, argv);
130 int numFailed = session.run();
131 return numFailed;
132}
133
134#else
135// Fallback to classic tests
136
137bool testMunkres(const std::vector<std::vector<double> > &costs_matrix,
138 const std::vector<std::pair<unsigned int, unsigned int> > &expected_pairs)
139{
140 const auto pairs = vpMunkres::run(costs_matrix);
141
142 if (pairs.size() != expected_pairs.size()) {
143 // clang-format off
144 std::cerr << "Expected nb of association | Munkres nb of association: "
145 << expected_pairs.size() << " | " << pairs.size()
146 << std::endl;
147 // clang-format on
148 return false;
149 }
150
151 for (auto i = 0u; i < pairs.size(); i++) {
152 if (expected_pairs.at(i) != pairs.at(i)) {
153
154 // Output the cost matrix
155 std::cout << "Cost matrix:" << std::endl;
156 for (const auto &cost_row : costs_matrix) {
157 std::cout << "| ";
158 for (const auto &cost : cost_row) {
159 std::cout << cost << " | ";
160 }
161 std::cout << std::endl;
162 }
163 std::cout << std::endl;
164
165 // Output the pair which fails
166 std::cerr << "FAIL: "
167 << "Expected association | Munkres association: " << expected_pairs.at(i) << " | " << pairs.at(i)
168 << std::endl;
169
170 return false;
171 }
172 }
173
174 return true;
175}
176
177bool testSquareMat()
178{
179 std::vector<std::vector<double> > costs {};
180 costs.push_back(std::vector<double>{3, 4, 1, 2});
181 costs.push_back(std::vector<double>{3, 4, 2, 1});
182 costs.push_back(std::vector<double>{1, 2, 3, 4});
183 costs.push_back(std::vector<double>{2, 1, 4, 3});
184
185 std::vector<std::pair<unsigned int, unsigned int> > pairs {};
186 pairs.emplace_back(0, 2);
187 pairs.emplace_back(1, 3);
188 pairs.emplace_back(2, 0);
189 pairs.emplace_back(3, 1);
190
191 return testMunkres(costs, pairs);
192}
193
194bool testVertMat()
195{
196 std::vector<std::vector<double> > costs {};
197 costs.push_back(std::vector<double>{3, 2, 1});
198 costs.push_back(std::vector<double>{4, 3, 2});
199 costs.push_back(std::vector<double>{1, 4, 3});
200 costs.push_back(std::vector<double>{2, 1, 4});
201
202 std::vector<std::pair<unsigned int, unsigned int> > pairs {};
203 pairs.emplace_back(0, 2);
204 pairs.emplace_back(2, 0);
205 pairs.emplace_back(3, 1);
206
207 return testMunkres(costs, pairs);
208}
209
210bool testHorMat()
211{
212 std::vector<std::vector<double> > costs {};
213 costs.push_back(std::vector<double>{2, 3, 4, 1});
214 costs.push_back(std::vector<double>{4, 1, 2, 3});
215 costs.push_back(std::vector<double>{1, 2, 3, 4});
216
217 std::vector<std::pair<unsigned int, unsigned int> > pairs {};
218 pairs.emplace_back(0, 3);
219 pairs.emplace_back(1, 1);
220 pairs.emplace_back(2, 0);
221
222 return testMunkres(costs, pairs);
223}
224
225int main()
226{
227 if (not testSquareMat()) {
228 return EXIT_FAILURE;
229 }
230
231 if (not testVertMat()) {
232 return EXIT_FAILURE;
233 }
234
235 if (not testHorMat()) {
236 return EXIT_FAILURE;
237 }
238
239 return EXIT_SUCCESS;
240}
241#endif
242
243#else
244int main() { return EXIT_SUCCESS; }
245#endif
static std::vector< std::pair< unsigned int, unsigned int > > run(std::vector< std::vector< Type > > costs)
Definition vpMunkres.h:323