Visual Servoing Platform  version 3.2.0 under development (2019-01-22)
testSvd.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2019 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See http://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Description:
32  * Test various svd decompositions.
33  *
34  * Authors:
35  * Eric Marchand
36  * Fabien Spindler
37  *
38  *****************************************************************************/
39 
45 #include <algorithm>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <vector>
49 
50 #include <visp3/core/vpColVector.h>
51 #include <visp3/core/vpMatrix.h>
52 #include <visp3/core/vpTime.h>
53 #include <visp3/io/vpParseArgv.h>
54 
55 // List of allowed command line options
56 #define GETOPTARGS "cdn:i:pf:R:C:vh"
57 
66 void usage(const char *name, const char *badparam)
67 {
68  fprintf(stdout, "\n\
69 Test matrix inversions\n\
70 using LU, QR and Cholesky methods as well as Pseudo-inverse.\n\
71 Outputs a comparison of these methods.\n\
72 \n\
73 SYNOPSIS\n\
74  %s [-n <number of matrices>] [-f <plot filename>]\n\
75  [-R <number of rows>] [-C <number of columns>]\n\
76  [-i <number of iterations>] [-p] [-h]\n", name);
77 
78  fprintf(stdout, "\n\
79 OPTIONS: Default\n\
80  -n <number of matrices> \n\
81  Number of matrices inverted during each test loop.\n\
82 \n\
83  -i <number of iterations> \n\
84  Number of iterations of the test.\n\
85 \n\
86  -f <plot filename> \n\
87  Set output path for plot output.\n\
88  The plot logs the times of \n\
89  the different inversion methods: \n\
90  QR,LU,Cholesky and Pseudo-inverse.\n\
91 \n\
92  -R <number of rows>\n\
93  Number of rows of the automatically generated matrices \n\
94  we test on.\n\
95 \n\
96  -C <number of columns>\n\
97  Number of colums of the automatically generated matrices \n\
98  we test on.\n\
99 \n\
100  -p \n\
101  Plot into filename in the gnuplot format. \n\
102  If this option is used, tests results will be logged \n\
103  into a filename specified with -f.\n\
104 \n\
105  -h\n\
106  Print the help.\n\n");
107 
108  if (badparam) {
109  fprintf(stderr, "ERROR: \n");
110  fprintf(stderr, "\nBad parameter [%s]\n", badparam);
111  }
112 }
113 
121 bool getOptions(int argc, const char **argv, unsigned int &nb_matrices, unsigned int &nb_iterations,
122  bool &use_plot_file, std::string &plotfile, unsigned int &nbrows, unsigned int &nbcols, bool &verbose)
123 {
124  const char *optarg_;
125  int c;
126  while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) {
127 
128  switch (c) {
129  case 'h':
130  usage(argv[0], NULL);
131  return false;
132  break;
133  case 'n':
134  nb_matrices = (unsigned int)atoi(optarg_);
135  break;
136  case 'i':
137  nb_iterations = (unsigned int)atoi(optarg_);
138  break;
139  case 'f':
140  plotfile = optarg_;
141  use_plot_file = true;
142  break;
143  case 'p':
144  use_plot_file = true;
145  break;
146  case 'R':
147  nbrows = (unsigned int)atoi(optarg_);
148  break;
149  case 'C':
150  nbcols = (unsigned int)atoi(optarg_);
151  break;
152  case 'v':
153  verbose = true;
154  break;
155  // add default options -c -d
156  case 'c':
157  break;
158  case 'd':
159  break;
160  default:
161  usage(argv[0], optarg_);
162  return false;
163  break;
164  }
165  }
166 
167  if ((c == 1) || (c == -1)) {
168  // standalone param or error
169  usage(argv[0], NULL);
170  std::cerr << "ERROR: " << std::endl;
171  std::cerr << " Bad argument " << optarg_ << std::endl << std::endl;
172  return false;
173  }
174 
175  return true;
176 }
177 
178 vpMatrix make_random_matrix(unsigned int nbrows, unsigned int nbcols)
179 {
180  vpMatrix A;
181  A.resize(nbrows, nbcols);
182 
183  for (unsigned int i = 0; i < A.getRows(); i++) {
184  for (unsigned int j = 0; j < A.getCols(); j++) {
185  A[i][j] = (double)rand() / (double)RAND_MAX;
186  }
187  }
188 
189  return A;
190 }
191 
192 void create_bench_random_matrix(unsigned int nb_matrices, unsigned int nb_rows, unsigned int nb_cols, bool verbose,
193  std::vector<vpMatrix> &bench)
194 {
195  if (verbose)
196  std::cout << "Create a bench of " << nb_matrices << " " << nb_rows << " by " << nb_cols << " matrices" << std::endl;
197  bench.clear();
198  for (unsigned int i = 0; i < nb_matrices; i++) {
199  vpMatrix M;
200  //#if defined(VISP_HAVE_EIGEN3) || defined(VISP_HAVE_LAPACK) ||
201  //(VISP_HAVE_OPENCV_VERSION >= 0x020101) || defined(VISP_HAVE_GSL)
202  // double det = 0.;
203  // // don't put singular matrices in the benchmark
204  // for(M = make_random_matrix(nb_rows, nb_cols);
205  // std::fabs(det=M.AtA().det())<.01; M = make_random_matrix(nb_rows,
206  // nb_cols)) {
207  // if(verbose) {
208  // std::cout << " Generated random matrix AtA=" << std::endl <<
209  // M.AtA() << std::endl; std::cout << " Generated random matrix
210  // not invertible: det=" << det << ". Retrying..." << std::endl;
211  // }
212  // }
213  //#else
214  M = make_random_matrix(nb_rows, nb_cols);
215  //#endif
216  bench.push_back(M);
217  }
218 }
219 
220 int test_svd(std::vector<vpMatrix> M, std::vector<vpMatrix> U, std::vector<vpColVector> s, std::vector<vpMatrix> V)
221 {
222  for (unsigned int i = 0; i < M.size(); i++) {
223  vpMatrix S;
224  S.diag(s[i]);
225  vpMatrix U_S_V = U[i] * S * V[i].t();
226  vpMatrix D = M[i] - U_S_V;
227  if (D.euclideanNorm() > 1e-6) {
228  std::cout << "SVD decomposition failed" << std::endl;
229  return EXIT_FAILURE;
230  }
231  }
232  return EXIT_SUCCESS;
233 }
234 
235 #if defined(VISP_HAVE_EIGEN3)
236 int test_svd_eigen3(bool verbose, const std::vector<vpMatrix> &bench, double &time)
237 {
238  if (verbose)
239  std::cout << "Test SVD using Eigen3 3rd party" << std::endl;
240  // Compute inverse
241  if (verbose)
242  std::cout << " SVD on a " << bench[0].getRows() << "x" << bench[0].getCols() << " matrix" << std::endl;
243 
244  std::vector<vpMatrix> U = bench;
245  std::vector<vpMatrix> V(bench.size());
246  std::vector<vpColVector> s(bench.size());
247 
248  double t = vpTime::measureTimeMs();
249  for (unsigned int i = 0; i < bench.size(); i++) {
250  U[i].svdEigen3(s[i], V[i]);
251  }
252 
253  time = vpTime::measureTimeMs() - t;
254 
255  return test_svd(bench, U, s, V);
256 }
257 #endif
258 
259 #if defined(VISP_HAVE_LAPACK)
260 int test_svd_lapack(bool verbose, const std::vector<vpMatrix> &bench, double &time)
261 {
262  if (verbose)
263  std::cout << "Test SVD using Lapack 3rd party" << std::endl;
264  // Compute inverse
265  if (verbose)
266  std::cout << " SVD on a " << bench[0].getRows() << "x" << bench[0].getCols() << " matrix" << std::endl;
267 
268  std::vector<vpMatrix> U = bench;
269  std::vector<vpMatrix> V(bench.size());
270  std::vector<vpColVector> s(bench.size());
271 
272  double t = vpTime::measureTimeMs();
273  for (unsigned int i = 0; i < bench.size(); i++) {
274  U[i].svdLapack(s[i], V[i]);
275  }
276  time = vpTime::measureTimeMs() - t;
277 
278  return test_svd(bench, U, s, V);
279 }
280 #endif
281 
282 #if defined(VISP_HAVE_GSL)
283 int test_svd_gsl(bool verbose, const std::vector<vpMatrix> &bench, double &time)
284 {
285  if (verbose)
286  std::cout << "Test SVD using GSL 3rd party" << std::endl;
287  // Compute inverse
288  if (verbose)
289  std::cout << " SVD on a " << bench[0].getRows() << "x" << bench[0].getCols() << " matrix" << std::endl;
290 
291  std::vector<vpMatrix> U = bench;
292  std::vector<vpMatrix> V(bench.size());
293  std::vector<vpColVector> s(bench.size());
294 
295  double t = vpTime::measureTimeMs();
296  for (unsigned int i = 0; i < bench.size(); i++) {
297  U[i].svdGsl(s[i], V[i]);
298  }
299 
300  time = vpTime::measureTimeMs() - t;
301 
302  return test_svd(bench, U, s, V);
303 }
304 #endif
305 
306 #if (VISP_HAVE_OPENCV_VERSION >= 0x020101)
307 int test_svd_opencv(bool verbose, const std::vector<vpMatrix> &bench, double &time)
308 {
309  if (verbose)
310  std::cout << "Test SVD using OpenCV 3rd party" << std::endl;
311  // Compute inverse
312  if (verbose)
313  std::cout << " SVD on a " << bench[0].getRows() << "x" << bench[0].getCols() << " matrix" << std::endl;
314 
315  std::vector<vpMatrix> U = bench;
316  std::vector<vpMatrix> V(bench.size());
317  std::vector<vpColVector> s(bench.size());
318 
319  double t = vpTime::measureTimeMs();
320  for (unsigned int i = 0; i < bench.size(); i++) {
321  U[i].svdOpenCV(s[i], V[i]);
322  }
323  time = vpTime::measureTimeMs() - t;
324 
325  return test_svd(bench, U, s, V);
326 }
327 #endif
328 
329 void save_time(const std::string &method, bool verbose, bool use_plot_file, std::ofstream &of, double time)
330 {
331  if (use_plot_file)
332  of << time << "\t";
333  if (verbose || !use_plot_file) {
334  std::cout << method << time << std::endl;
335  }
336 }
337 
338 int main(int argc, const char *argv[])
339 {
340  try {
341 #if defined(VISP_HAVE_EIGEN3) || defined(VISP_HAVE_LAPACK) || (VISP_HAVE_OPENCV_VERSION >= 0x020101) || \
342  defined(VISP_HAVE_GSL)
343  unsigned int nb_matrices = 100;
344  unsigned int nb_iterations = 10;
345  unsigned int nb_rows = 6;
346  unsigned int nb_cols = 6;
347  bool verbose = false;
348  std::string plotfile("plot-svd.csv");
349  bool use_plot_file = false;
350  std::ofstream of;
351 
352  // Read the command line options
353  if (getOptions(argc, argv, nb_matrices, nb_iterations, use_plot_file, plotfile, nb_rows, nb_cols, verbose) ==
354  false) {
355  exit(-1);
356  }
357 
358  if (use_plot_file) {
359  of.open(plotfile.c_str());
360  of << "iter"
361  << "\t";
362 
363 #if defined(VISP_HAVE_LAPACK)
364  of << "\"SVD Lapack\""
365  << "\t";
366 #endif
367 #if defined(VISP_HAVE_EIGEN3)
368  of << "\"SVD Eigen3\""
369  << "\t";
370 #endif
371 #if (VISP_HAVE_OPENCV_VERSION >= 0x020101)
372  of << "\"SVD OpenCV\""
373  << "\t";
374 #endif
375 #if defined(VISP_HAVE_GSL)
376  of << "\"SVD GSL\""
377  << "\t";
378 #endif
379  of << std::endl;
380  }
381 
382  int ret = EXIT_SUCCESS;
383  for (unsigned int iter = 0; iter < nb_iterations; iter++) {
384  std::vector<vpMatrix> bench_random_matrices;
385  create_bench_random_matrix(nb_matrices, nb_rows, nb_cols, verbose, bench_random_matrices);
386 
387  if (use_plot_file)
388  of << iter << "\t";
389  double time;
390 
391 #if defined(VISP_HAVE_LAPACK)
392  ret += test_svd_lapack(verbose, bench_random_matrices, time);
393  save_time("SVD (Lapack): ", verbose, use_plot_file, of, time);
394 #endif
395 
396 #if defined(VISP_HAVE_EIGEN3)
397  ret += test_svd_eigen3(verbose, bench_random_matrices, time);
398  save_time("SVD (Eigen3): ", verbose, use_plot_file, of, time);
399 #endif
400 
401 #if (VISP_HAVE_OPENCV_VERSION >= 0x020101)
402  ret += test_svd_opencv(verbose, bench_random_matrices, time);
403  save_time("SVD (OpenCV): ", verbose, use_plot_file, of, time);
404 #endif
405 
406 #if defined(VISP_HAVE_GSL)
407  ret += test_svd_gsl(verbose, bench_random_matrices, time);
408  save_time("SVD (GSL): ", verbose, use_plot_file, of, time);
409 #endif
410  if (use_plot_file)
411  of << std::endl;
412  }
413  if (use_plot_file) {
414  of.close();
415  std::cout << "Result saved in " << plotfile << std::endl;
416  }
417 
418  if (ret == EXIT_SUCCESS) {
419  std::cout << "Test succeed" << std::endl;
420  } else {
421  std::cout << "Test failed" << std::endl;
422  }
423 
424  return ret;
425 #else
426  (void)argc;
427  (void)argv;
428  std::cout << "Test does nothing since you dont't have Eigen3, Lapack, "
429  "OpenCV or GSL 3rd party"
430  << std::endl;
431  return EXIT_SUCCESS;
432 #endif
433  } catch (const vpException &e) {
434  std::cout << "Catch an exception: " << e.getStringMessage() << std::endl;
435  return EXIT_FAILURE;
436  }
437 }
Implementation of a matrix and operations on matrices.
Definition: vpMatrix.h:104
void resize(const unsigned int nrows, const unsigned int ncols, const bool flagNullify=true, const bool recopy_=true)
Definition: vpArray2D.h:171
error that can be emited by ViSP classes.
Definition: vpException.h:71
unsigned int getCols() const
Definition: vpArray2D.h:146
VISP_EXPORT double measureTimeMs()
Definition: vpTime.cpp:88
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
Definition: vpParseArgv.cpp:69
void diag(const double &val=1.0)
Definition: vpMatrix.cpp:693
unsigned int getRows() const
Definition: vpArray2D.h:156
vpMatrix t() const
Definition: vpMatrix.cpp:375
double euclideanNorm() const
Definition: vpMatrix.cpp:5096
const std::string & getStringMessage(void) const
Send a reference (constant) related the error message (can be empty).
Definition: vpException.cpp:92