Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
vpMatrix.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 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  * Matrix manipulation.
32  */
33 
39 #include <algorithm>
40 #include <assert.h>
41 #include <cmath> // std::fabs
42 #include <fstream>
43 #include <limits> // numeric_limits
44 #include <sstream>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <string>
49 #include <vector>
50 
51 #include <visp3/core/vpCPUFeatures.h>
52 #include <visp3/core/vpColVector.h>
53 #include <visp3/core/vpConfig.h>
54 #include <visp3/core/vpException.h>
55 #include <visp3/core/vpMath.h>
56 #include <visp3/core/vpMatrix.h>
57 #include <visp3/core/vpTranslationVector.h>
58 
59 #if defined(VISP_HAVE_SIMDLIB)
60 #include <Simd/SimdLib.h>
61 #endif
62 
63 #ifdef VISP_HAVE_LAPACK
64 #ifdef VISP_HAVE_GSL
65 #include <gsl/gsl_eigen.h>
66 #include <gsl/gsl_linalg.h>
67 #include <gsl/gsl_math.h>
68 #elif defined(VISP_HAVE_MKL)
69 #include <mkl.h>
70 #endif
71 #endif
72 
74 
75 #ifdef VISP_HAVE_LAPACK
76 #ifdef VISP_HAVE_GSL
77 #elif defined(VISP_HAVE_MKL)
78 typedef MKL_INT integer;
79 
80 void vpMatrix::blas_dsyev(char jobz, char uplo, unsigned int n_, double *a_data, unsigned int lda_, double *w_data,
81  double *work_data, int lwork_, int &info_)
82 {
83  MKL_INT n = static_cast<MKL_INT>(n_);
84  MKL_INT lda = static_cast<MKL_INT>(lda_);
85  MKL_INT lwork = static_cast<MKL_INT>(lwork_);
86  MKL_INT info = static_cast<MKL_INT>(info_);
87 
88  dsyev(&jobz, &uplo, &n, a_data, &lda, w_data, work_data, &lwork, &info);
89 }
90 
91 #else
92 #if defined(VISP_HAVE_LAPACK_BUILT_IN)
93 typedef long int integer;
94 #else
95 typedef int integer;
96 #endif
97 extern "C" integer dsyev_(char *jobz, char *uplo, integer *n, double *a, integer *lda, double *w, double *WORK,
98  integer *lwork, integer *info);
99 
100 void vpMatrix::blas_dsyev(char jobz, char uplo, unsigned int n_, double *a_data, unsigned int lda_, double *w_data,
101  double *work_data, int lwork_, int &info_)
102 {
103  integer n = static_cast<integer>(n_);
104  integer lda = static_cast<integer>(lda_);
105  integer lwork = static_cast<integer>(lwork_);
106  integer info = static_cast<integer>(info_);
107 
108  dsyev_(&jobz, &uplo, &n, a_data, &lda, w_data, work_data, &lwork, &info);
109 
110  lwork_ = static_cast<int>(lwork);
111  info_ = static_cast<int>(info);
112 }
113 #endif
114 #endif
115 
116 #if !defined(VISP_USE_MSVC) || (defined(VISP_USE_MSVC) && !defined(VISP_BUILD_SHARED_LIBS))
117 const unsigned int vpMatrix::m_lapack_min_size_default = 0;
118 unsigned int vpMatrix::m_lapack_min_size = vpMatrix::m_lapack_min_size_default;
119 #endif
120 
121 // Prototypes of specific functions
122 vpMatrix subblock(const vpMatrix &M, unsigned int col, unsigned int row);
123 
128 vpMatrix::vpMatrix(const vpMatrix &M, unsigned int r, unsigned int c, unsigned int nrows, unsigned int ncols)
129  : vpArray2D<double>()
130 {
131  if (((r + nrows) > M.rowNum) || ((c + ncols) > M.colNum)) {
133  "Cannot construct a sub matrix (%dx%d) starting at "
134  "position (%d,%d) that is not contained in the "
135  "original matrix (%dx%d)",
136  nrows, ncols, r, c, M.rowNum, M.colNum));
137  }
138 
139  init(M, r, c, nrows, ncols);
140 }
141 
147 {
148  *this = M;
149 }
150 
156 {
157  *this = V;
158 }
159 
165 {
166  *this = F;
167 }
168 
174 {
175  *this = R;
176 }
177 
183 {
184  *this = v;
185 }
186 
192 {
193  *this = v;
194 }
195 
201 {
202  *this = t;
203 }
204 
205 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
207 {
208  rowNum = A.rowNum;
209  colNum = A.colNum;
210  rowPtrs = A.rowPtrs;
211  dsize = A.dsize;
212  data = A.data;
213 
214  A.rowNum = 0;
215  A.colNum = 0;
216  A.rowPtrs = nullptr;
217  A.dsize = 0;
218  A.data = nullptr;
219 }
220 
248 vpMatrix::vpMatrix(const std::initializer_list<double> &list) : vpArray2D<double>(list) { }
249 
276 vpMatrix::vpMatrix(unsigned int nrows, unsigned int ncols, const std::initializer_list<double> &list)
277  : vpArray2D<double>(nrows, ncols, list)
278 { }
279 
304 vpMatrix::vpMatrix(const std::initializer_list<std::initializer_list<double> > &lists) : vpArray2D<double>(lists) { }
305 #endif
306 
357 void vpMatrix::init(const vpMatrix &M, unsigned int r, unsigned int c, unsigned int nrows, unsigned int ncols)
358 {
359  unsigned int rnrows = r + nrows;
360  unsigned int cncols = c + ncols;
361 
362  if (rnrows > M.getRows()) {
363  throw(vpException(vpException::dimensionError, "Bad row dimension (%d > %d) used to initialize vpMatrix", rnrows,
364  M.getRows()));
365  }
366  if (cncols > M.getCols()) {
367  throw(vpException(vpException::dimensionError, "Bad column dimension (%d > %d) used to initialize vpMatrix", cncols,
368  M.getCols()));
369  }
370  resize(nrows, ncols, false, false);
371 
372  if (this->rowPtrs == nullptr) { // Fix coverity scan: explicit null dereferenced
373  return; // Noting to do
374  }
375  for (unsigned int i = 0; i < nrows; ++i) {
376  memcpy((*this)[i], &M[i + r][c], ncols * sizeof(double));
377  }
378 }
379 
424 vpMatrix vpMatrix::extract(unsigned int r, unsigned int c, unsigned int nrows, unsigned int ncols) const
425 {
426  unsigned int rnrows = r + nrows;
427  unsigned int cncols = c + ncols;
428 
429  if (rnrows > getRows()) {
430  throw(vpException(vpException::dimensionError, "Bad row dimension (%d > %d) used to initialize vpMatrix", rnrows,
431  getRows()));
432  }
433  if (cncols > getCols()) {
434  throw(vpException(vpException::dimensionError, "Bad column dimension (%d > %d) used to initialize vpMatrix", cncols,
435  getCols()));
436  }
437 
438  vpMatrix M;
439  M.resize(nrows, ncols, false, false);
440  for (unsigned int i = 0; i < nrows; ++i) {
441  memcpy(M[i], &(*this)[i + r][c], ncols * sizeof(double));
442  }
443 
444  return M;
445 }
446 
492 vpColVector vpMatrix::getCol(unsigned int j, unsigned int i_begin, unsigned int column_size) const
493 {
494  if (((i_begin + column_size) > getRows()) || (j >= getCols())) {
495  throw(vpException(vpException::dimensionError, "Unable to extract column %u from the %ux%u matrix", j, getRows(),
496  getCols()));
497  }
498  vpColVector c(column_size);
499  for (unsigned int i = 0; i < column_size; ++i) {
500  c[i] = (*this)[i_begin + i][j];
501  }
502  return c;
503 }
504 
548 vpColVector vpMatrix::getCol(unsigned int j) const { return getCol(j, 0, rowNum); }
549 
590 vpRowVector vpMatrix::getRow(unsigned int i) const { return getRow(i, 0, colNum); }
591 
635 vpRowVector vpMatrix::getRow(unsigned int i, unsigned int j_begin, unsigned int row_size) const
636 {
637  if (((j_begin + row_size) > colNum) || (i >= rowNum)) {
638  throw(vpException(vpException::dimensionError, "Unable to extract a row vector from the matrix"));
639  }
640  vpRowVector r(row_size);
641  if ((r.data != nullptr) && (data != nullptr)) {
642  memcpy(r.data, (*this)[i] + j_begin, row_size * sizeof(double));
643  }
644 
645  return r;
646 }
647 
688 {
689  unsigned int min_size = std::min<unsigned int>(rowNum, colNum);
691 
692  if (min_size > 0) {
693  diag.resize(min_size, false);
694 
695  for (unsigned int i = 0; i < min_size; ++i) {
696  diag[i] = (*this)[i][i];
697  }
698  }
699 
700  return diag;
701 }
702 
715 vpMatrix vpMatrix::insert(const vpMatrix &A, const vpMatrix &B, unsigned int r, unsigned int c)
716 {
718 
719  vpArray2D<double>::insert(A, B, C, r, c);
720 
721  return vpMatrix(C);
722 }
723 
737 void vpMatrix::insert(const vpMatrix &A, const vpMatrix &B, vpMatrix &C, unsigned int r, unsigned int c)
738 {
739  vpArray2D<double> C_array;
740 
741  vpArray2D<double>::insert(A, B, C_array, r, c);
742 
743  C = C_array;
744 }
745 
758 {
759  vpMatrix C;
760 
761  juxtaposeMatrices(A, B, C);
762 
763  return C;
764 }
765 
779 {
780  unsigned int nca = A.getCols();
781  unsigned int ncb = B.getCols();
782 
783  if (nca != 0) {
784  if (A.getRows() != B.getRows()) {
785  throw(vpException(vpException::dimensionError, "Cannot juxtapose (%dx%d) matrix with (%dx%d) matrix", A.getRows(),
786  A.getCols(), B.getRows(), B.getCols()));
787  }
788  }
789 
790  if ((B.getRows() == 0) || ((nca + ncb) == 0)) {
791  std::cerr << "B.getRows() == 0 || nca+ncb == 0" << std::endl;
792  return;
793  }
794 
795  C.resize(B.getRows(), nca + ncb, false, false);
796 
797  C.insert(A, 0, 0);
798  C.insert(B, 0, nca);
799 }
800 
801 //--------------------------------------------------------------------
802 // Output
803 //--------------------------------------------------------------------
804 
824 int vpMatrix::print(std::ostream &s, unsigned int length, const std::string &intro) const
825 {
826  typedef std::string::size_type size_type;
827 
828  unsigned int m = getRows();
829  unsigned int n = getCols();
830 
831  std::vector<std::string> values(m * n);
832  std::ostringstream oss;
833  std::ostringstream ossFixed;
834  std::ios_base::fmtflags original_flags = oss.flags();
835 
836  ossFixed.setf(std::ios::fixed, std::ios::floatfield);
837 
838  size_type maxBefore = 0; // the length of the integral part
839  size_type maxAfter = 0; // number of decimals plus
840  // one place for the decimal point
841  for (unsigned int i = 0; i < m; ++i) {
842  for (unsigned int j = 0; j < n; ++j) {
843  oss.str("");
844  oss << (*this)[i][j];
845  if (oss.str().find("e") != std::string::npos) {
846  ossFixed.str("");
847  ossFixed << (*this)[i][j];
848  oss.str(ossFixed.str());
849  }
850 
851  values[(i * n) + j] = oss.str();
852  size_type thislen = values[(i * n) + j].size();
853  size_type p = values[(i * n) + j].find('.');
854 
855  if (p == std::string::npos) {
856  maxBefore = vpMath::maximum(maxBefore, thislen);
857  // maxAfter remains the same
858  }
859  else {
860  maxBefore = vpMath::maximum(maxBefore, p);
861  maxAfter = vpMath::maximum(maxAfter, thislen - p);
862  }
863  }
864  }
865 
866  size_type totalLength = length;
867  // increase totalLength according to maxBefore
868  totalLength = vpMath::maximum(totalLength, maxBefore);
869  // decrease maxAfter according to totalLength
870  maxAfter = std::min<size_type>(maxAfter, totalLength - maxBefore);
871 
872  if (!intro.empty()) {
873  s << intro;
874  }
875  s << "[" << m << "," << n << "]=\n";
876 
877  for (unsigned int i = 0; i < m; ++i) {
878  s << " ";
879  for (unsigned int j = 0; j < n; ++j) {
880  size_type p = values[(i * n) + j].find('.');
881  s.setf(std::ios::right, std::ios::adjustfield);
882  s.width(static_cast<std::streamsize>(maxBefore));
883  s << values[(i * n) + j].substr(0, p).c_str();
884 
885  if (maxAfter > 0) {
886  s.setf(std::ios::left, std::ios::adjustfield);
887  if (p != std::string::npos) {
888  s.width(static_cast<std::streamsize>(maxAfter));
889  s << values[(i * n) + j].substr(p, maxAfter).c_str();
890  }
891  else {
892  s.width(static_cast<std::streamsize>(maxAfter));
893  s << ".0";
894  }
895  }
896 
897  s << ' ';
898  }
899  s << std::endl;
900  }
901 
902  s.flags(original_flags); // restore s to standard state
903 
904  return static_cast<int>(maxBefore + maxAfter);
905 }
906 
947 std::ostream &vpMatrix::matlabPrint(std::ostream &os) const
948 {
949  unsigned int this_rows = this->getRows();
950  unsigned int this_col = this->getCols();
951  os << "[ ";
952  for (unsigned int i = 0; i < this_rows; ++i) {
953  for (unsigned int j = 0; j < this_col; ++j) {
954  os << (*this)[i][j] << ", ";
955  }
956  if (this->getRows() != (i + 1)) {
957  os << ";" << std::endl;
958  }
959  else {
960  os << "]" << std::endl;
961  }
962  }
963  return os;
964 }
965 
998 std::ostream &vpMatrix::maplePrint(std::ostream &os) const
999 {
1000  unsigned int this_rows = this->getRows();
1001  unsigned int this_col = this->getCols();
1002  os << "([ " << std::endl;
1003  for (unsigned int i = 0; i < this_rows; ++i) {
1004  os << "[";
1005  for (unsigned int j = 0; j < this_col; ++j) {
1006  os << (*this)[i][j] << ", ";
1007  }
1008  os << "]," << std::endl;
1009  }
1010  os << "])" << std::endl;
1011  return os;
1012 }
1013 
1045 std::ostream &vpMatrix::csvPrint(std::ostream &os) const
1046 {
1047  unsigned int this_rows = this->getRows();
1048  unsigned int this_col = this->getCols();
1049  for (unsigned int i = 0; i < this_rows; ++i) {
1050  for (unsigned int j = 0; j < this_col; ++j) {
1051  os << (*this)[i][j];
1052  if (!(j == (this->getCols() - 1))) {
1053  os << ", ";
1054  }
1055  }
1056  os << std::endl;
1057  }
1058  return os;
1059 }
1060 
1100 std::ostream &vpMatrix::cppPrint(std::ostream &os, const std::string &matrixName, bool octet) const
1101 {
1102  os << "vpMatrix " << matrixName << " (" << this->getRows() << ", " << this->getCols() << "); " << std::endl;
1103 
1104  unsigned int this_rows = this->getRows();
1105  unsigned int this_col = this->getCols();
1106  for (unsigned int i = 0; i < this_rows; ++i) {
1107  for (unsigned int j = 0; j < this_col; ++j) {
1108  if (!octet) {
1109  os << matrixName << "[" << i << "][" << j << "] = " << (*this)[i][j] << "; " << std::endl;
1110  }
1111  else {
1112  for (unsigned int k = 0; k < sizeof(double); ++k) {
1113  os << "((unsigned char*)&(" << matrixName << "[" << i << "][" << j << "]) )[" << k << "] = 0x" << std::hex
1114  << static_cast<unsigned int>(((unsigned char *)&((*this)[i][j]))[k]) << "; " << std::endl;
1115  }
1116  }
1117  }
1118  os << std::endl;
1119  }
1120  return os;
1121 }
1122 
1133 void vpMatrix::insert(const vpMatrix &A, unsigned int r, unsigned int c)
1134 {
1135  if (((r + A.getRows()) <= rowNum) && ((c + A.getCols()) <= colNum)) {
1136  if ((A.colNum == colNum) && (data != nullptr) && (A.data != nullptr) && (A.data != data)) {
1137  memcpy(data + (r * colNum), A.data, sizeof(double) * A.size());
1138  }
1139  else if ((data != nullptr) && (A.data != nullptr) && (A.data != data)) {
1140  unsigned int a_rows = A.getRows();
1141  for (unsigned int i = r; i < (r + a_rows); ++i) {
1142  memcpy(data + (i * colNum) + c, A.data + ((i - r) * A.colNum), sizeof(double) * A.colNum);
1143  }
1144  }
1145  }
1146  else {
1147  throw vpException(vpException::dimensionError, "Cannot insert (%dx%d) matrix in (%dx%d) matrix at position (%d,%d)",
1148  A.getRows(), A.getCols(), rowNum, colNum, r, c);
1149  }
1150 }
1151 
1193 {
1194  vpColVector evalue(rowNum); // Eigen values
1195 
1196  if (rowNum != colNum) {
1197  throw(vpException(vpException::dimensionError, "Cannot compute eigen values on a non square matrix (%dx%d)", rowNum,
1198  colNum));
1199  }
1200 
1201  // Check if the matrix is symmetric: At - A = 0
1202  vpMatrix At_A = (*this).t() - (*this);
1203  for (unsigned int i = 0; i < rowNum; ++i) {
1204  for (unsigned int j = 0; j < rowNum; ++j) {
1205  // in comment: if (At_A[i][j] != 0) {
1206  if (std::fabs(At_A[i][j]) > std::numeric_limits<double>::epsilon()) {
1207  throw(vpException(vpException::fatalError, "Cannot compute eigen values on a non symmetric matrix"));
1208  }
1209  }
1210  }
1211 
1212 #if defined(VISP_HAVE_LAPACK)
1213 #if defined(VISP_HAVE_GSL) /* be careful of the copy below */
1214  {
1215  gsl_vector *eval = gsl_vector_alloc(rowNum);
1216  gsl_matrix *evec = gsl_matrix_alloc(rowNum, colNum);
1217 
1218  gsl_eigen_symmv_workspace *w = gsl_eigen_symmv_alloc(rowNum);
1219  gsl_matrix *m = gsl_matrix_alloc(rowNum, colNum);
1220 
1221  unsigned int Atda = (unsigned int)m->tda;
1222  for (unsigned int i = 0; i < rowNum; i++) {
1223  unsigned int k = i * Atda;
1224  for (unsigned int j = 0; j < colNum; j++)
1225  m->data[k + j] = (*this)[i][j];
1226  }
1227  gsl_eigen_symmv(m, eval, evec, w);
1228 
1229  gsl_eigen_symmv_sort(eval, evec, GSL_EIGEN_SORT_ABS_ASC);
1230 
1231  for (unsigned int i = 0; i < rowNum; i++) {
1232  evalue[i] = gsl_vector_get(eval, i);
1233  }
1234 
1235  gsl_eigen_symmv_free(w);
1236  gsl_vector_free(eval);
1237  gsl_matrix_free(m);
1238  gsl_matrix_free(evec);
1239  }
1240 #else
1241  {
1242  const char jobz = 'N';
1243  const char uplo = 'U';
1244  vpMatrix A = (*this);
1245  vpColVector WORK;
1246  int lwork = -1;
1247  int info = 0;
1248  double wkopt;
1249  vpMatrix::blas_dsyev(jobz, uplo, rowNum, A.data, colNum, evalue.data, &wkopt, lwork, info);
1250  lwork = static_cast<int>(wkopt);
1251  WORK.resize(lwork);
1252  vpMatrix::blas_dsyev(jobz, uplo, rowNum, A.data, colNum, evalue.data, WORK.data, lwork, info);
1253  }
1254 #endif
1255 #else
1256  {
1257  throw(vpException(vpException::functionNotImplementedError, "Eigen values computation is not implemented. "
1258  "You should install Lapack 3rd party"));
1259  }
1260 #endif
1261  return evalue;
1262 }
1263 
1317 void vpMatrix::eigenValues(vpColVector &evalue, vpMatrix &evector) const
1318 {
1319  if (rowNum != colNum) {
1320  throw(vpException(vpException::dimensionError, "Cannot compute eigen values on a non square matrix (%dx%d)", rowNum,
1321  colNum));
1322  }
1323 
1324  // Check if the matrix is symmetric: At - A = 0
1325  vpMatrix At_A = (*this).t() - (*this);
1326  for (unsigned int i = 0; i < rowNum; ++i) {
1327  for (unsigned int j = 0; j < rowNum; ++j) {
1328  // -- in comment: if (At_A[i][j] != 0) {
1329  if (std::fabs(At_A[i][j]) > std::numeric_limits<double>::epsilon()) {
1330  throw(vpException(vpException::fatalError, "Cannot compute eigen values on a non symmetric matrix"));
1331  }
1332  }
1333  }
1334 
1335  // Resize the output matrices
1336  evalue.resize(rowNum);
1337  evector.resize(rowNum, colNum);
1338 
1339 #if defined(VISP_HAVE_LAPACK)
1340 #if defined(VISP_HAVE_GSL) /* be careful of the copy below */
1341  {
1342  gsl_vector *eval = gsl_vector_alloc(rowNum);
1343  gsl_matrix *evec = gsl_matrix_alloc(rowNum, colNum);
1344 
1345  gsl_eigen_symmv_workspace *w = gsl_eigen_symmv_alloc(rowNum);
1346  gsl_matrix *m = gsl_matrix_alloc(rowNum, colNum);
1347 
1348  unsigned int Atda = (unsigned int)m->tda;
1349  for (unsigned int i = 0; i < rowNum; i++) {
1350  unsigned int k = i * Atda;
1351  for (unsigned int j = 0; j < colNum; j++)
1352  m->data[k + j] = (*this)[i][j];
1353  }
1354  gsl_eigen_symmv(m, eval, evec, w);
1355 
1356  gsl_eigen_symmv_sort(eval, evec, GSL_EIGEN_SORT_ABS_ASC);
1357 
1358  for (unsigned int i = 0; i < rowNum; i++) {
1359  evalue[i] = gsl_vector_get(eval, i);
1360  }
1361  Atda = (unsigned int)evec->tda;
1362  for (unsigned int i = 0; i < rowNum; i++) {
1363  unsigned int k = i * Atda;
1364  for (unsigned int j = 0; j < rowNum; j++) {
1365  evector[i][j] = evec->data[k + j];
1366  }
1367  }
1368 
1369  gsl_eigen_symmv_free(w);
1370  gsl_vector_free(eval);
1371  gsl_matrix_free(m);
1372  gsl_matrix_free(evec);
1373  }
1374 #else // defined(VISP_HAVE_GSL)
1375  {
1376  const char jobz = 'V';
1377  const char uplo = 'U';
1378  vpMatrix A = (*this);
1379  vpColVector WORK;
1380  int lwork = -1;
1381  int info = 0;
1382  double wkopt;
1383  vpMatrix::blas_dsyev(jobz, uplo, rowNum, A.data, colNum, evalue.data, &wkopt, lwork, info);
1384  lwork = static_cast<int>(wkopt);
1385  WORK.resize(lwork);
1386  vpMatrix::blas_dsyev(jobz, uplo, rowNum, A.data, colNum, evalue.data, WORK.data, lwork, info);
1387  evector = A.t();
1388  }
1389 #endif // defined(VISP_HAVE_GSL)
1390 #else
1391  {
1392  throw(vpException(vpException::functionNotImplementedError, "Eigen values computation is not implemented. "
1393  "You should install Lapack 3rd party"));
1394  }
1395 #endif
1396 }
1397 
1416 unsigned int vpMatrix::kernel(vpMatrix &kerAt, double svThreshold) const
1417 {
1418  unsigned int nbline = getRows();
1419  unsigned int nbcol = getCols();
1420 
1421  vpMatrix U, V; // Copy of the matrix, SVD function is destructive
1422  vpColVector sv;
1423  sv.resize(nbcol, false); // singular values
1424  V.resize(nbcol, nbcol, false); // V matrix of singular value decomposition
1425 
1426  // Copy and resize matrix to have at least as many rows as columns
1427  // kernel is computed in svd method only if the matrix has more rows than
1428  // columns
1429 
1430  if (nbline < nbcol) {
1431  U.resize(nbcol, nbcol, true);
1432  }
1433  else {
1434  U.resize(nbline, nbcol, false);
1435  }
1436 
1437  U.insert(*this, 0, 0);
1438 
1439  U.svd(sv, V);
1440 
1441  // Compute the highest singular value and rank of the matrix
1442  double maxsv = 0;
1443  for (unsigned int i = 0; i < nbcol; ++i) {
1444  if (sv[i] > maxsv) {
1445  maxsv = sv[i];
1446  }
1447  }
1448 
1449  unsigned int rank = 0;
1450  for (unsigned int i = 0; i < nbcol; ++i) {
1451  if (sv[i] >(maxsv * svThreshold)) {
1452  ++rank;
1453  }
1454  }
1455 
1456  kerAt.resize(nbcol - rank, nbcol);
1457  if (rank != nbcol) {
1458  unsigned int k = 0;
1459  for (unsigned int j = 0; j < nbcol; ++j) {
1460  // if( v.col(j) in kernel and non zero )
1461  if ((sv[j] <= (maxsv * svThreshold)) &&
1462  (std::fabs(V.getCol(j).sumSquare()) > std::numeric_limits<double>::epsilon())) {
1463  unsigned int v_rows = V.getRows();
1464  for (unsigned int i = 0; i < v_rows; ++i) {
1465  kerAt[k][i] = V[i][j];
1466  }
1467  ++k;
1468  }
1469  }
1470  }
1471 
1472  return rank;
1473 }
1474 
1491 unsigned int vpMatrix::nullSpace(vpMatrix &kerA, double svThreshold) const
1492 {
1493  unsigned int nbrow = getRows();
1494  unsigned int nbcol = getCols();
1495 
1496  vpMatrix U, V; // Copy of the matrix, SVD function is destructive
1497  vpColVector sv;
1498  sv.resize(nbcol, false); // singular values
1499  V.resize(nbcol, nbcol, false); // V matrix of singular value decomposition
1500 
1501  // Copy and resize matrix to have at least as many rows as columns
1502  // kernel is computed in svd method only if the matrix has more rows than
1503  // columns
1504 
1505  if (nbrow < nbcol) {
1506  U.resize(nbcol, nbcol, true);
1507  }
1508  else {
1509  U.resize(nbrow, nbcol, false);
1510  }
1511 
1512  U.insert(*this, 0, 0);
1513 
1514  U.svd(sv, V);
1515 
1516  // Compute the highest singular value and rank of the matrix
1517  double maxsv = sv[0];
1518 
1519  unsigned int rank = 0;
1520  for (unsigned int i = 0; i < nbcol; ++i) {
1521  if (sv[i] >(maxsv * svThreshold)) {
1522  ++rank;
1523  }
1524  }
1525 
1526  kerA.resize(nbcol, nbcol - rank);
1527  if (rank != nbcol) {
1528  unsigned int k = 0;
1529  for (unsigned int j = 0; j < nbcol; ++j) {
1530  // if( v.col(j) in kernel and non zero )
1531  if (sv[j] <= (maxsv * svThreshold)) {
1532  for (unsigned int i = 0; i < nbcol; ++i) {
1533  kerA[i][k] = V[i][j];
1534  }
1535  ++k;
1536  }
1537  }
1538  }
1539 
1540  return (nbcol - rank);
1541 }
1542 
1559 unsigned int vpMatrix::nullSpace(vpMatrix &kerA, int dim) const
1560 {
1561  unsigned int nbrow = getRows();
1562  unsigned int nbcol = getCols();
1563  unsigned int dim_ = static_cast<unsigned int>(dim);
1564 
1565  vpMatrix U, V; // Copy of the matrix, SVD function is destructive
1566  vpColVector sv;
1567  sv.resize(nbcol, false); // singular values
1568  V.resize(nbcol, nbcol, false); // V matrix of singular value decomposition
1569 
1570  // Copy and resize matrix to have at least as many rows as columns
1571  // kernel is computed in svd method only if the matrix has more rows than
1572  // columns
1573 
1574  if (nbrow < nbcol) {
1575  U.resize(nbcol, nbcol, true);
1576  }
1577  else {
1578  U.resize(nbrow, nbcol, false);
1579  }
1580 
1581  U.insert(*this, 0, 0);
1582 
1583  U.svd(sv, V);
1584 
1585  kerA.resize(nbcol, dim_);
1586  if (dim_ != 0) {
1587  unsigned int rank = nbcol - dim_;
1588  for (unsigned int k = 0; k < dim_; ++k) {
1589  unsigned int j = k + rank;
1590  for (unsigned int i = 0; i < nbcol; ++i) {
1591  kerA[i][k] = V[i][j];
1592  }
1593  }
1594  }
1595 
1596  double maxsv = sv[0];
1597  unsigned int rank = 0;
1598  for (unsigned int i = 0; i < nbcol; ++i) {
1599  if (sv[i] >(maxsv * 1e-6)) {
1600  ++rank;
1601  }
1602  }
1603  return (nbcol - rank);
1604 }
1605 
1641 double vpMatrix::det(vpDetMethod method) const
1642 {
1643  double det = 0.;
1644 
1645  if (method == LU_DECOMPOSITION) {
1646  det = this->detByLU();
1647  }
1648 
1649  return det;
1650 }
1651 
1653 {
1654 #if defined(VISP_HAVE_EIGEN3)
1655  return choleskyByEigen3();
1656 #elif defined(VISP_HAVE_LAPACK)
1657  return choleskyByLapack();
1658 #elif defined(VISP_HAVE_OPENCV)
1659  return choleskyByOpenCV();
1660 #else
1661  throw(vpException(vpException::fatalError, "Cannot compute matrix Chloesky decomposition. "
1662  "Install Lapack, Eigen3 or OpenCV 3rd party"));
1663 #endif
1664 }
1665 
1674 {
1675  if (colNum != rowNum) {
1676  throw(vpException(vpException::dimensionError, "Cannot compute the exponential of a non square (%dx%d) matrix",
1677  rowNum, colNum));
1678  }
1679  else {
1680 #ifdef VISP_HAVE_GSL
1681  size_t size_ = rowNum * colNum;
1682  double *b = new double[size_];
1683  for (size_t i = 0; i < size_; i++)
1684  b[i] = 0.;
1685  gsl_matrix_view m = gsl_matrix_view_array(this->data, rowNum, colNum);
1686  gsl_matrix_view em = gsl_matrix_view_array(b, rowNum, colNum);
1687  gsl_linalg_exponential_ss(&m.matrix, &em.matrix, 0);
1688  // gsl_matrix_fprintf(stdout, &em.matrix, "%g");
1689  vpMatrix expA;
1690  expA.resize(rowNum, colNum, false);
1691  memcpy(expA.data, b, size_ * sizeof(double));
1692 
1693  delete[] b;
1694  return expA;
1695 #else
1696  vpMatrix v_expE(rowNum, colNum, false);
1697  vpMatrix v_expD(rowNum, colNum, false);
1698  vpMatrix v_expX(rowNum, colNum, false);
1699  vpMatrix v_expcX(rowNum, colNum, false);
1700  vpMatrix v_eye(rowNum, colNum, false);
1701 
1702  v_eye.eye();
1703  vpMatrix exp(*this);
1704 
1705  // -- in comment: double f;
1706  int e;
1707  double c = 0.5;
1708  int q = 6;
1709  int p = 1;
1710 
1711  double nA = 0;
1712  for (unsigned int i = 0; i < rowNum; ++i) {
1713  double sum = 0;
1714  for (unsigned int j = 0; j < colNum; ++j) {
1715  sum += fabs((*this)[i][j]);
1716  }
1717  if ((sum > nA) || (i == 0)) {
1718  nA = sum;
1719  }
1720  }
1721 
1722  /* f = */ frexp(nA, &e);
1723  // -- in comment: double s = (0 > e+1)?0:e+1;
1724  double s = e + 1;
1725 
1726  double sca = 1.0 / pow(2.0, s);
1727  exp = sca * exp;
1728  v_expX = *this;
1729  v_expE = (c * exp) + v_eye;
1730  v_expD = (-c * exp) + v_eye;
1731  for (int k = 2; k <= q; ++k) {
1732  c = (c * (static_cast<double>((q - k) + 1))) / (static_cast<double>(k * (((2 * q) - k) + 1)));
1733  v_expcX = exp * v_expX;
1734  v_expX = v_expcX;
1735  v_expcX = c * v_expX;
1736  v_expE = v_expE + v_expcX;
1737  if (p) {
1738  v_expD = v_expD + v_expcX;
1739  }
1740  else {
1741  v_expD = v_expD - v_expcX;
1742  }
1743  p = !p;
1744  }
1745  v_expX = v_expD.inverseByLU();
1746  exp = v_expX * v_expE;
1747  for (int k = 1; k <= s; ++k) {
1748  v_expE = exp * exp;
1749  exp = v_expE;
1750  }
1751  return exp;
1752 #endif
1753  }
1754 }
1755 
1756 /**************************************************************************************************************/
1757 /**************************************************************************************************************/
1758 
1759 // Specific functions
1760 
1761 /*
1762  input:: matrix M(nCols,nRows), nCols > 3, nRows > 3 , nCols == nRows.
1763 
1764  output:: the complement matrix of the element (rowNo,colNo).
1765  This is the matrix obtained from M after elimenating the row rowNo and column
1766  colNo
1767 
1768  example:
1769  1 2 3
1770  M = 4 5 6
1771  7 8 9
1772  1 3
1773  subblock(M, 1, 1) give the matrix 7 9
1774 */
1775 vpMatrix subblock(const vpMatrix &M, unsigned int col, unsigned int row)
1776 {
1777  vpMatrix M_comp;
1778  M_comp.resize(M.getRows() - 1, M.getCols() - 1, false);
1779 
1780  unsigned int m_rows = M.getRows();
1781  unsigned int m_col = M.getCols();
1782  for (unsigned int i = 0; i < col; ++i) {
1783  for (unsigned int j = 0; j < row; ++j) {
1784  M_comp[i][j] = M[i][j];
1785  }
1786  for (unsigned int j = row + 1; j < m_rows; ++j) {
1787  M_comp[i][j - 1] = M[i][j];
1788  }
1789  }
1790  for (unsigned int i = col + 1; i < m_col; ++i) {
1791  for (unsigned int j = 0; j < row; ++j) {
1792  M_comp[i - 1][j] = M[i][j];
1793  }
1794  for (unsigned int j = row + 1; j < m_rows; ++j) {
1795  M_comp[i - 1][j - 1] = M[i][j];
1796  }
1797  }
1798  return M_comp;
1799 }
1800 
1810 double vpMatrix::cond(double svThreshold) const
1811 {
1812  unsigned int nbline = getRows();
1813  unsigned int nbcol = getCols();
1814 
1815  vpMatrix U, V; // Copy of the matrix, SVD function is destructive
1816  vpColVector sv;
1817  sv.resize(nbcol); // singular values
1818  V.resize(nbcol, nbcol, false); // V matrix of singular value decomposition
1819 
1820  // Copy and resize matrix to have at least as many rows as columns
1821  // kernel is computed in svd method only if the matrix has more rows than
1822  // columns
1823 
1824  if (nbline < nbcol) {
1825  U.resize(nbcol, nbcol, true);
1826  }
1827  else {
1828  U.resize(nbline, nbcol, false);
1829  }
1830 
1831  U.insert(*this, 0, 0);
1832 
1833  U.svd(sv, V);
1834 
1835  // Compute the highest singular value
1836  double maxsv = 0;
1837  for (unsigned int i = 0; i < nbcol; ++i) {
1838  if (sv[i] > maxsv) {
1839  maxsv = sv[i];
1840  }
1841  }
1842 
1843  // Compute the rank of the matrix
1844  unsigned int rank = 0;
1845  for (unsigned int i = 0; i < nbcol; ++i) {
1846  if (sv[i] >(maxsv * svThreshold)) {
1847  ++rank;
1848  }
1849  }
1850 
1851  // Compute the lowest singular value
1852  double minsv = maxsv;
1853  for (unsigned int i = 0; i < rank; ++i) {
1854  if (sv[i] < minsv) {
1855  minsv = sv[i];
1856  }
1857  }
1858 
1859  if (std::fabs(minsv) > std::numeric_limits<double>::epsilon()) {
1860  return maxsv / minsv;
1861  }
1862  else {
1863  return std::numeric_limits<double>::infinity();
1864  }
1865 }
1866 
1873 void vpMatrix::computeHLM(const vpMatrix &H, const double &alpha, vpMatrix &HLM)
1874 {
1875  if (H.getCols() != H.getRows()) {
1876  throw(vpException(vpException::dimensionError, "Cannot compute HLM on a non square matrix (%dx%d)", H.getRows(),
1877  H.getCols()));
1878  }
1879 
1880  HLM = H;
1881  unsigned int h_col = H.getCols();
1882  for (unsigned int i = 0; i < h_col; ++i) {
1883  HLM[i][i] += alpha * H[i][i];
1884  }
1885 }
1886 
1895 {
1896  double norm = 0.0;
1897  for (unsigned int i = 0; i < dsize; ++i) {
1898  double x = *(data + i);
1899  norm += x * x;
1900  }
1901 
1902  return sqrt(norm);
1903 }
1904 
1914 {
1915  if (this->dsize != 0) {
1916  vpMatrix v;
1917  vpColVector w;
1918 
1919  vpMatrix M = *this;
1920 
1921  M.svd(w, v);
1922 
1923  double max = w[0];
1924  unsigned int maxRank = std::min<unsigned int>(this->getCols(), this->getRows());
1925  // The maximum reachable rank is either the number of columns or the number of rows
1926  // of the matrix.
1927  unsigned int boundary = std::min<unsigned int>(maxRank, w.size());
1928  // boundary is here to ensure that the number of singular values used for the com-
1929  // putation of the euclidean norm of the matrix is not greater than the maximum
1930  // reachable rank. Indeed, some svd library pad the singular values vector with 0s
1931  // if the input matrix is non-square.
1932  for (unsigned int i = 0; i < boundary; ++i) {
1933  if (max < w[i]) {
1934  max = w[i];
1935  }
1936  }
1937  return max;
1938  }
1939  else {
1940  return 0.;
1941  }
1942 }
1943 
1955 {
1956  double norm = 0.0;
1957  for (unsigned int i = 0; i < rowNum; ++i) {
1958  double x = 0;
1959  for (unsigned int j = 0; j < colNum; ++j) {
1960  x += fabs(*(*(rowPtrs + i) + j));
1961  }
1962  if (x > norm) {
1963  norm = x;
1964  }
1965  }
1966  return norm;
1967 }
1968 
1975 double vpMatrix::sumSquare() const
1976 {
1977  double sum_square = 0.0;
1978  double x;
1979 
1980  for (unsigned int i = 0; i < rowNum; ++i) {
1981  for (unsigned int j = 0; j < colNum; ++j) {
1982  x = rowPtrs[i][j];
1983  sum_square += x * x;
1984  }
1985  }
1986 
1987  return sum_square;
1988 }
1989 #if defined(VISP_BUILD_DEPRECATED_FUNCTIONS)
1999 double vpMatrix::euclideanNorm() const { return frobeniusNorm(); }
2000 
2020 {
2021  vpRowVector c(getCols());
2022 
2023  for (unsigned int j = 0; j < getCols(); ++j) {
2024  c[j] = (*this)[i - 1][j];
2025  }
2026  return c;
2027 }
2028 
2047 {
2048  vpColVector c(getRows());
2049 
2050  for (unsigned int i = 0; i < getRows(); ++i) {
2051  c[i] = (*this)[i][j - 1];
2052  }
2053  return c;
2054 }
2055 
2062 void vpMatrix::setIdentity(const double &val)
2063 {
2064  for (unsigned int i = 0; i < rowNum; ++i) {
2065  for (unsigned int j = 0; j < colNum; ++j) {
2066  if (i == j) {
2067  (*this)[i][j] = val;
2068  }
2069  else {
2070  (*this)[i][j] = 0;
2071  }
2072  }
2073  }
2074 }
2075 
2076 #endif //#if defined(VISP_BUILD_DEPRECATED_FUNCTIONS)
2077 
2078 END_VISP_NAMESPACE
Implementation of a generic 2D array used as base class for matrices and vectors.
Definition: vpArray2D.h:145
unsigned int getCols() const
Definition: vpArray2D.h:337
double * data
Address of the first element of the data array.
Definition: vpArray2D.h:148
double ** rowPtrs
Address of the first element of each rows.
Definition: vpArray2D.h:1102
void insert(const vpArray2D< Type > &A, unsigned int r, unsigned int c)
Definition: vpArray2D.h:494
void resize(unsigned int nrows, unsigned int ncols, bool flagNullify=true, bool recopy_=true)
Definition: vpArray2D.h:362
unsigned int rowNum
Number of rows in the array.
Definition: vpArray2D.h:1098
unsigned int dsize
Current array size (rowNum * colNum)
Definition: vpArray2D.h:1104
unsigned int size() const
Return the number of elements of the 2D array.
Definition: vpArray2D.h:349
unsigned int getRows() const
Definition: vpArray2D.h:347
unsigned int colNum
Number of columns in the array.
Definition: vpArray2D.h:1100
Implementation of column vector and the associated operations.
Definition: vpColVector.h:191
double sumSquare() const
void resize(unsigned int i, bool flagNullify=true)
Definition: vpColVector.h:1143
error that can be emitted by ViSP classes.
Definition: vpException.h:60
@ functionNotImplementedError
Function not implemented.
Definition: vpException.h:66
@ dimensionError
Bad dimension.
Definition: vpException.h:71
@ fatalError
Fatal error.
Definition: vpException.h:72
Implementation of an homogeneous matrix and operations on such kind of matrices.
static Type maximum(const Type &a, const Type &b)
Definition: vpMath.h:254
Implementation of a matrix and operations on matrices.
Definition: vpMatrix.h:169
vpColVector eigenValues() const
Definition: vpMatrix.cpp:1192
std::ostream & cppPrint(std::ostream &os, const std::string &matrixName="A", bool octet=false) const
Definition: vpMatrix.cpp:1100
double cond(double svThreshold=1e-6) const
Definition: vpMatrix.cpp:1810
vpMatrix expm() const
Definition: vpMatrix.cpp:1673
VP_DEPRECATED void setIdentity(const double &val=1.0)
Definition: vpMatrix.cpp:2062
int print(std::ostream &s, unsigned int length, const std::string &intro="") const
Definition: vpMatrix.cpp:824
std::ostream & maplePrint(std::ostream &os) const
Definition: vpMatrix.cpp:998
vpMatrix cholesky() const
Definition: vpMatrix.cpp:1652
unsigned int kernel(vpMatrix &kerAt, double svThreshold=1e-6) const
Definition: vpMatrix.cpp:1416
vpMatrix inverseByLU() const
static vpMatrix juxtaposeMatrices(const vpMatrix &A, const vpMatrix &B)
Definition: vpMatrix.cpp:757
VP_DEPRECATED double euclideanNorm() const
Definition: vpMatrix.cpp:1999
VP_DEPRECATED vpColVector column(unsigned int j)
Definition: vpMatrix.cpp:2046
void svd(vpColVector &w, vpMatrix &V)
double sum() const
void diag(const double &val=1.0)
vpRowVector getRow(unsigned int i) const
Definition: vpMatrix.cpp:590
vpMatrix()
Definition: vpMatrix.h:185
vpMatrix choleskyByOpenCV() const
Compute the Cholesky decomposition of a Hermitian positive-definite matrix using OpenCV library.
vpMatrix choleskyByEigen3() const
Compute the Cholesky decomposition of a Hermitian positive-definite matrix using Eigen3 library.
double inducedL2Norm() const
Definition: vpMatrix.cpp:1913
double infinityNorm() const
Definition: vpMatrix.cpp:1954
double frobeniusNorm() const
Definition: vpMatrix.cpp:1894
vpColVector getDiag() const
Definition: vpMatrix.cpp:687
static void computeHLM(const vpMatrix &H, const double &alpha, vpMatrix &HLM)
Definition: vpMatrix.cpp:1873
VP_DEPRECATED vpRowVector row(unsigned int i)
Definition: vpMatrix.cpp:2019
vpColVector getCol(unsigned int j) const
Definition: vpMatrix.cpp:548
double detByLU() const
vpMatrix choleskyByLapack() const
Compute the Cholesky decomposition of a Hermitian positive-definite matrix using Lapack library.
double det(vpDetMethod method=LU_DECOMPOSITION) const
Definition: vpMatrix.cpp:1641
double sumSquare() const
Definition: vpMatrix.cpp:1975
void insert(const vpMatrix &A, unsigned int r, unsigned int c)
Definition: vpMatrix.cpp:1133
VP_DEPRECATED void init()
Definition: vpMatrix.h:1082
@ LU_DECOMPOSITION
Definition: vpMatrix.h:177
vpMatrix t() const
std::ostream & csvPrint(std::ostream &os) const
Definition: vpMatrix.cpp:1045
unsigned int nullSpace(vpMatrix &kerA, double svThreshold=1e-6) const
Definition: vpMatrix.cpp:1491
std::ostream & matlabPrint(std::ostream &os) const
Definition: vpMatrix.cpp:947
vpMatrix extract(unsigned int r, unsigned int c, unsigned int nrows, unsigned int ncols) const
Definition: vpMatrix.cpp:424
Implementation of a rotation matrix and operations on such kind of matrices.
Implementation of row vector and the associated operations.
Definition: vpRowVector.h:124
Class that consider the case of a translation vector.