Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
perfColVectorOperations.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 * Benchmark column vector operations.
32 */
33
37
38#include <visp3/core/vpConfig.h>
39
40#if defined(VISP_HAVE_CATCH2)
41
42#include <numeric>
43#include <catch_amalgamated.hpp>
44
45#include <visp3/core/vpColVector.h>
46
47#ifdef ENABLE_VISP_NAMESPACE
48using namespace VISP_NAMESPACE_NAME;
49#endif
50
51namespace
52{
53static bool g_runBenchmark = false;
54VP_ATTRIBUTE_NO_DESTROY static const std::vector<unsigned int> g_sizes = { 23, 127, 233, 419, 1153, 2749 };
55
56double getRandomValues(double min, double max) { return (max - min) * (static_cast<double>(rand()) / static_cast<double>(RAND_MAX)) + min; }
57
58vpColVector generateRandomVector(unsigned int rows, double min = -100, double max = 100)
59{
60 vpColVector v(rows);
61
62 for (unsigned int i = 0; i < v.getRows(); i++) {
63 v[i] = getRandomValues(min, max);
64 }
65
66 return v;
67}
68
69double stddev(const std::vector<double> &vec)
70{
71 double sum = std::accumulate(vec.begin(), vec.end(), 0.0);
72 double mean = sum / vec.size();
73
74 std::vector<double> diff(vec.size());
75 std::transform(vec.begin(), vec.end(), diff.begin(), [mean](double x) {
76 return x - mean; });
77 double sq_sum = std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0);
78 return std::sqrt(sq_sum / vec.size());
79}
80
81double computeRegularSum(const vpColVector &v)
82{
83 double sum = 0.0;
84
85 for (unsigned int i = 0; i < v.getRows(); i++) {
86 sum += v[i];
87 }
88
89 return sum;
90}
91
92double computeRegularSumSquare(const vpColVector &v)
93{
94 double sum_square = 0.0;
95
96 for (unsigned int i = 0; i < v.getRows(); i++) {
97 sum_square += v[i] * v[i];
98 }
99
100 return sum_square;
101}
102
103double computeRegularStdev(const vpColVector &v)
104{
105 double mean_value = computeRegularSum(v) / v.getRows();
106 double sum_squared_diff = 0.0;
107
108 for (unsigned int i = 0; i < v.size(); i++) {
109 sum_squared_diff += (v[i] - mean_value) * (v[i] - mean_value);
110 }
111
112 double divisor = static_cast<double>(v.size());
113
114 return std::sqrt(sum_squared_diff / divisor);
115}
116
117std::vector<double> computeHadamard(const std::vector<double> &v1, const std::vector<double> &v2)
118{
119 std::vector<double> result;
120 std::transform(v1.begin(), v1.end(), v2.begin(), std::back_inserter(result), std::multiplies<double>());
121 return result;
122}
123
124bool almostEqual(const vpColVector &v1, const vpColVector &v2, double tol = 1e-9)
125{
126 if (v1.getRows() != v2.getRows()) {
127 return false;
128 }
129
130 for (unsigned int i = 0; i < v1.getRows(); i++) {
131 if (!vpMath::equal(v1[i], v2[i], tol)) {
132 return false;
133 }
134 }
135
136 return true;
137}
138} // namespace
139
140TEST_CASE("Benchmark vpColVector::sum()", "[benchmark]")
141{
142 // Sanity checks
143 {
144 const double val = 11;
145 vpColVector v(1, val);
146 CHECK(v.sum() == Catch::Approx(val).epsilon(std::numeric_limits<double>::epsilon()));
147 }
148 {
149 const unsigned int size = 11;
150 std::vector<double> vec(size);
151 vpColVector v(size);
152 for (size_t i = 0; i < 11; i++) {
153 vec[i] = 2. * i;
154 v[static_cast<unsigned int>(i)] = vec[i];
155 }
156 CHECK(v.sum() == Catch::Approx(std::accumulate(vec.begin(), vec.end(), 0.0)).epsilon(std::numeric_limits<double>::epsilon()));
157 }
158
159 if (g_runBenchmark) {
160 for (auto size : g_sizes) {
161 vpColVector v = generateRandomVector(size);
162 std::vector<double> vec = v.toStdVector();
163
164 std::ostringstream oss;
165 oss << "Benchmark vpColVector::sum() with size: " << size << " (ViSP)";
166 double vp_sum = 0;
167 BENCHMARK(oss.str().c_str())
168 {
169 vp_sum = v.sum();
170 return vp_sum;
171 };
172
173 oss.str("");
174 oss << "Benchmark std::accumulate() with size: " << size << " (C++)";
175 double std_sum = 0;
176 BENCHMARK(oss.str().c_str())
177 {
178 std_sum = std::accumulate(vec.begin(), vec.end(), 0.0);
179 return std_sum;
180 };
181 CHECK(vp_sum == Catch::Approx(std_sum));
182
183 oss.str("");
184 oss << "Benchmark naive sum() with size: " << size;
185 double naive_sum = 0;
186 BENCHMARK(oss.str().c_str())
187 {
188 naive_sum = computeRegularSum(v);
189 return naive_sum;
190 };
191 CHECK(naive_sum == Catch::Approx(std_sum));
192 }
193 }
194}
195
196TEST_CASE("Benchmark vpColVector::sumSquare()", "[benchmark]")
197{
198 // Sanity checks
199 {
200 const double val = 11;
201 vpColVector v(1, val);
202 CHECK(v.sumSquare() == Catch::Approx(val * val).epsilon(std::numeric_limits<double>::epsilon()));
203 }
204 {
205 const unsigned int size = 11;
206 std::vector<double> vec(size);
207 vpColVector v(size);
208 for (size_t i = 0; i < 11; i++) {
209 vec[i] = 2. * i;
210 v[static_cast<unsigned int>(i)] = vec[i];
211 }
212 CHECK(v.sumSquare() == Catch::Approx(std::inner_product(vec.begin(), vec.end(), vec.begin(), 0.0))
213 .epsilon(std::numeric_limits<double>::epsilon()));
214 }
215
216 if (g_runBenchmark) {
217 for (auto size : g_sizes) {
218 vpColVector v = generateRandomVector(size);
219 std::vector<double> vec = v.toStdVector();
220
221 std::ostringstream oss;
222 oss << "Benchmark vpColVector::sumSquare() with size: " << size << " (ViSP)";
223 double vp_sq_sum = 0;
224 BENCHMARK(oss.str().c_str())
225 {
226 vp_sq_sum = v.sumSquare();
227 return vp_sq_sum;
228 };
229
230 oss.str("");
231 oss << "Benchmark std::inner_product with size: " << size << " (C++)";
232 double std_sq_sum = 0;
233 BENCHMARK(oss.str().c_str())
234 {
235 std_sq_sum = std::inner_product(vec.begin(), vec.end(), vec.begin(), 0.0);
236 return std_sq_sum;
237 };
238 CHECK(vp_sq_sum == Catch::Approx(std_sq_sum));
239
240 oss.str("");
241 oss << "Benchmark naive sumSquare() with size: " << size;
242 double naive_sq_sum = 0;
243 BENCHMARK(oss.str().c_str())
244 {
245 naive_sq_sum = computeRegularSumSquare(v);
246 return naive_sq_sum;
247 };
248 CHECK(naive_sq_sum == Catch::Approx(std_sq_sum));
249 }
250 }
251}
252
253TEST_CASE("Benchmark vpColVector::stdev()", "[benchmark]")
254{
255 // Sanity checks
256 {
257 vpColVector v(2);
258 v[0] = 11;
259 v[1] = 16;
260 std::vector<double> vec = v.toStdVector();
261 CHECK(vpColVector::stdev(v) == Catch::Approx(stddev(vec)).epsilon(std::numeric_limits<double>::epsilon()));
262 }
263 {
264 const unsigned int size = 11;
265 std::vector<double> vec(size);
266 vpColVector v(size);
267 for (size_t i = 0; i < 11; i++) {
268 vec[i] = 2. * i;
269 v[static_cast<unsigned int>(i)] = vec[i];
270 }
271 CHECK(vpColVector::stdev(v) == Catch::Approx(stddev(vec)).epsilon(std::numeric_limits<double>::epsilon()));
272 }
273
274 if (g_runBenchmark) {
275 for (auto size : g_sizes) {
276 vpColVector v = generateRandomVector(size);
277 std::vector<double> vec = v.toStdVector();
278
279 std::ostringstream oss;
280 oss << "Benchmark vpColVector::stdev() with size: " << size << " (ViSP)";
281 double vp_stddev = 0;
282 BENCHMARK(oss.str().c_str())
283 {
284 vp_stddev = vpColVector::stdev(v);
285 return vp_stddev;
286 };
287
288 oss.str("");
289 oss << "Benchmark C++ stddev impl with size: " << size << " (C++)";
290 double std_stddev = 0;
291 BENCHMARK(oss.str().c_str())
292 {
293 std_stddev = stddev(vec);
294 return std_stddev;
295 };
296 CHECK(vp_stddev == Catch::Approx(std_stddev));
297
298 oss.str("");
299 oss << "Benchmark naive stdev() with size: " << size;
300 double naive_stddev = 0;
301 BENCHMARK(oss.str().c_str())
302 {
303 naive_stddev = computeRegularStdev(v);
304 return naive_stddev;
305 };
306 CHECK(naive_stddev == Catch::Approx(std_stddev));
307 }
308 }
309}
310
311TEST_CASE("Benchmark vpColVector::hadamard()", "[benchmark]")
312{
313 // Sanity checks
314 {
315 vpColVector v1(2), v2(2);
316 v1[0] = 11;
317 v1[1] = 16;
318 v2[0] = 8;
319 v2[1] = 23;
320 vpColVector res1 = v1.hadamard(v2);
321 vpColVector res2(computeHadamard(v1.toStdVector(), v2.toStdVector()));
322 CHECK(almostEqual(res1, res2));
323 }
324 {
325 const unsigned int size = 11;
326 std::vector<double> vec1(size), vec2(size);
327 for (size_t i = 0; i < 11; i++) {
328 vec1[i] = 2. * i;
329 vec2[i] = 3. * i + 5.;
330 }
331 vpColVector v1(vec1), v2(vec2);
332 vpColVector res1 = v1.hadamard(v2);
333 vpColVector res2(computeHadamard(v1.toStdVector(), v2.toStdVector()));
334 CHECK(almostEqual(res1, res2));
335 }
336
337 if (g_runBenchmark) {
338 for (auto size : g_sizes) {
339 vpColVector v1 = generateRandomVector(size);
340 vpColVector v2 = generateRandomVector(size);
341 std::vector<double> vec1 = v1.toStdVector();
342 std::vector<double> vec2 = v2.toStdVector();
343
344 std::ostringstream oss;
345 oss << "Benchmark vpColVector::hadamard() with size: " << size << " (ViSP)";
346 vpColVector vp_res;
347 BENCHMARK(oss.str().c_str())
348 {
349 vp_res = v1.hadamard(v2);
350 return vp_res;
351 };
352
353 oss.str("");
354 oss << "Benchmark C++ element wise multiplication with size: " << size << " (C++)";
355 std::vector<double> std_res;
356 BENCHMARK(oss.str().c_str())
357 {
358 std_res = computeHadamard(vec1, vec2);
359 return std_res;
360 };
361 CHECK(almostEqual(vp_res, vpColVector(std_res)));
362 }
363 }
364}
365
366int main(int argc, char *argv[])
367{
368 // Set random seed explicitly to avoid confusion
369 // See: https://en.cppreference.com/w/cpp/numeric/random/srand
370 // If rand() is used before any calls to srand(), rand() behaves as if it was seeded with srand(1).
371 srand(1);
372
373 Catch::Session session;
374 auto cli = session.cli()
375 | Catch::Clara::Opt(g_runBenchmark)["--benchmark"]("run benchmark?");
376
377 session.cli(cli);
378 session.applyCommandLine(argc, argv);
379
380 int numFailed = session.run();
381 return numFailed;
382}
383#else
384#include <iostream>
385
386int main() { return EXIT_SUCCESS; }
387#endif
unsigned int getRows() const
Definition vpArray2D.h:433
Implementation of column vector and the associated operations.
vpColVector hadamard(const vpColVector &v) const
std::vector< double > toStdVector() const
static double stdev(const vpColVector &v, bool useBesselCorrection=false)
static bool equal(double x, double y, double threshold=0.001)
Definition vpMath.h:470