ViSP  2.9.0
vpMatrix_svd.cpp
1 /****************************************************************************
2  *
3  * $Id: vpMatrix_svd.cpp 4574 2014-01-09 08:48:51Z fspindle $
4  *
5  * This file is part of the ViSP software.
6  * Copyright (C) 2005 - 2014 by INRIA. All rights reserved.
7  *
8  * This software is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * ("GPL") version 2 as published by the Free Software Foundation.
11  * See the file LICENSE.txt at the root directory of this source
12  * distribution for additional information about the GNU GPL.
13  *
14  * For using ViSP with software that can not be combined with the GNU
15  * GPL, please contact INRIA about acquiring a ViSP Professional
16  * Edition License.
17  *
18  * See http://www.irisa.fr/lagadic/visp/visp.html for more information.
19  *
20  * This software was developed at:
21  * INRIA Rennes - Bretagne Atlantique
22  * Campus Universitaire de Beaulieu
23  * 35042 Rennes Cedex
24  * France
25  * http://www.irisa.fr/lagadic
26  *
27  * If you have questions regarding the use of this file, please contact
28  * INRIA at visp@inria.fr
29  *
30  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
31  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
32  *
33  *
34  * Description:
35  * Matrix SVD decomposition.
36  *
37  * Authors:
38  * Eric Marchand
39  *
40  *****************************************************************************/
41 
42 #include <visp/vpConfig.h>
43 
44 #ifndef DOXYGEN_SHOULD_SKIP_THIS
45 
46 #include <visp/vpMatrix.h>
47 #include <visp/vpMath.h>
48 #include <visp/vpColVector.h>
49 #include <visp/vpException.h>
50 #include <visp/vpMatrixException.h>
51 #include <visp/vpDebug.h>
52 
53 
54 #include <cmath> // std::fabs
55 #include <limits> // numeric_limits
56 #include <iostream>
57 
58 /*---------------------------------------------------------------------
59 
60 SVD related functions
61 
62 ---------------------------------------------------------------------*/
63 
64 
65 static double pythag(double a, double b)
66 {
67  double absa, absb;
68  absa = fabs(a);
69  absb = fabs(b);
70  if (absa > absb) return absa*sqrt(1.0+vpMath::sqr(absb/absa));
71  //else return (absb == 0.0 ? 0.0 : absb*sqrt(1.0+vpMath::sqr(absa/absb)));
72  else return (std::fabs(absb) <= std::numeric_limits<double>::epsilon() ? 0.0 : absb*sqrt(1.0+vpMath::sqr(absa/absb)));
73 }
74 
75 #ifdef SIGN
76 #undef SIGN
77 #endif
78 #define SIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
79 
100 #define MAX_ITER_SVD 50
101 void vpMatrix::svdNr(vpColVector& W, vpMatrix& V)
102 {
103 
104  unsigned int m = rowNum;
105  unsigned int n = colNum;
106  double epsilon = 10*std::numeric_limits<double>::epsilon();
107 
108  unsigned int flag,i,its,j,jj,k,l=0,nm=0;
109  double c,f,h,s,x,y,z;
110  double anorm=0.0,g=0.0,scale=0.0;
111 
112  // So that the original NRC code (using 1..n indexing) can be used
113  // This should be considered as a temporary fix.
114  double **a = new double*[m+1];
115  double **v = new double*[n+1];
116  // double **w = W.rowPtrs;
117  // w--;
118 
119  double *w = new double[n+1];
120  for (i=0;i<n;i++) w[i+1] = 0.0;
121 
122  for (i=1;i<=m;i++) {
123  a[i] = this->rowPtrs[i-1]-1;
124  }
125  for (i=1;i<=n;i++) {
126  v[i] = V.rowPtrs[i-1]-1;
127  }
128 
129  if (m < n)
130  {
131  delete[] w;
132  delete[] a;
133  delete[] v;
134  vpERROR_TRACE("\n\t\tSVDcmp: You must augment A with extra zero rows") ;
136  "\n\t\tSVDcmp: You must augment A with "
137  "extra zero rows")) ;
138  }
139  double* rv1=new double[n+1];
140 
141  for (i=1;i<=n;i++) {
142  l=i+1;
143  rv1[i]=scale*g;
144  g=s=scale=0.0;
145  if (i <= m) {
146  for (k=i;k<=m;k++) scale += fabs(a[k][i]);
147  //if ((scale != 0.0) || (fabs(scale) > EPS_SVD)) {
148  if ((std::fabs(scale) > epsilon)/* || (fabs(scale) > EPS_SVD)*/) {
149  for (k=i;k<=m;k++) {
150  a[k][i] /= scale;
151  s += a[k][i]*a[k][i];
152  }
153  f=a[i][i];
154  g = -SIGN(sqrt(s),f);
155  h=f*g-s;
156  a[i][i]=f-g;
157  if (i != n) {
158  for (j=l;j<=n;j++) {
159  for (s=0.0,k=i;k<=m;k++) s += a[k][i]*a[k][j];
160  f=s/h;
161  for (k=i;k<=m;k++) a[k][j] += f*a[k][i];
162  }
163  }
164  for (k=i;k<=m;k++) a[k][i] *= scale;
165  }
166  }
167  w[i]=scale*g;
168  g=s=scale=0.0;
169  if (i <= m && i != n) {
170  for (k=l;k<=n;k++) scale += fabs(a[i][k]);
171  //if ((scale != 0.0) || (fabs(scale) > EPS_SVD)) {
172  if ((std::fabs(scale) > epsilon) /*|| (fabs(scale) > EPS_SVD)*/) {
173  for (k=l;k<=n;k++) {
174  a[i][k] /= scale;
175  s += a[i][k]*a[i][k];
176  }
177  f=a[i][l];
178  g = -SIGN(sqrt(s),f);
179  h=f*g-s;
180  a[i][l]=f-g;
181  for (k=l;k<=n;k++) rv1[k]=a[i][k]/h;
182  if (i != m) {
183  for (j=l;j<=m;j++) {
184  for (s=0.0,k=l;k<=n;k++) s += a[j][k]*a[i][k];
185  for (k=l;k<=n;k++) a[j][k] += s*rv1[k];
186  }
187  }
188  for (k=l;k<=n;k++) a[i][k] *= scale;
189  }
190  }
191  anorm=vpMath::maximum(anorm,(fabs(w[i])+fabs(rv1[i])));
192  }
193  for (i=n;i>=1;i--) {
194  if (i < n) {
195  //if ((g) || (fabs(g) > EPS_SVD)) {
196  if ((std::fabs(g) > epsilon) /*|| (fabs(g) > EPS_SVD)*/) {
197  for (j=l;j<=n;j++)
198  v[j][i]=(a[i][j]/a[i][l])/g;
199  for (j=l;j<=n;j++) {
200  for (s=0.0,k=l;k<=n;k++) s += a[i][k]*v[k][j];
201  for (k=l;k<=n;k++) v[k][j] += s*v[k][i];
202  }
203  }
204  for (j=l;j<=n;j++) v[i][j]=v[j][i]=0.0;
205  }
206  v[i][i]=1.0;
207  g=rv1[i];
208  l=i;
209  }
210  for (i=n;i>=1;i--) {
211  l=i+1;
212  g=w[i];
213  if (i < n)
214  for (j=l;j<=n;j++) a[i][j]=0.0;
215  //if ((g != 0.0) || (fabs(g) > EPS_SVD)) {
216  if ((std::fabs(g) > epsilon) /*|| (fabs(g) > EPS_SVD)*/) {
217  g=1.0/g;
218  if (i != n) {
219  for (j=l;j<=n;j++) {
220  for (s=0.0,k=l;k<=m;k++) s += a[k][i]*a[k][j];
221  f=(s/a[i][i])*g;
222  for (k=i;k<=m;k++) a[k][j] += f*a[k][i];
223  }
224  }
225  for (j=i;j<=m;j++) a[j][i] *= g;
226  } else {
227  for (j=i;j<=m;j++) a[j][i]=0.0;
228  }
229  ++a[i][i];
230  }
231  for (k=n;k>=1;k--) {
232  for (its=1;its<=MAX_ITER_SVD;its++) {
233  flag=1;
234  for (l=k;l>=1;l--) {
235  nm=l-1;
236  //if ((fabs(rv1[l])+anorm == anorm) || (fabs(rv1[l]) <= EPS_SVD)) {
237  if ((std::fabs(rv1[l]) <= epsilon) /*|| (fabs(rv1[l]) <= EPS_SVD)*/) {
238  flag=0;
239  break;
240  }
241  //if ((fabs(w[nm])+anorm == anorm) || (fabs(w[nm]) <= EPS_SVD)) break;
242  if ((std::fabs(w[nm]) <= epsilon) /*|| (fabs(w[nm]) <= EPS_SVD)*/) break;
243  }
244  if (flag) {
245  c=0.0;
246  s=1.0;
247  for (i=l;i<=k;i++) {
248  f=s*rv1[i];
249  //if ((fabs(f)+anorm != anorm) || (fabs(f) <= EPS_SVD)) {
250  if ((std::fabs(f) > epsilon) /*|| (fabs(f) <= EPS_SVD)*/) {
251  g=w[i];
252  h=pythag(f,g);
253  w[i]=h;
254  h=1.0/h;
255  c=g*h;
256  s=(-f*h);
257  for (j=1;j<=m;j++) {
258  y=a[j][nm];
259  z=a[j][i];
260  a[j][nm]=y*c+z*s;
261  a[j][i]=z*c-y*s;
262  }
263  }
264  }
265  }
266  z=w[k];
267  if (l == k) {
268  if (z < 0.0) {
269  w[k] = -z;
270  for (j=1;j<=n;j++) v[j][k]=(-v[j][k]);
271  }
272  break;
273  }
274  if (its == MAX_ITER_SVD)
275  {
276  for (i=0;i<n;i++) W[i] = w[i+1];
277 
278  vpERROR_TRACE("\n\t\t No convergence in SVDcmp ") ;
279  std::cout << *this <<std::endl ;
280  // throw(vpMatrixException(vpMatrixException::matrixError,
281  // "\n\t\t No convergence in SVDcmp ")) ;
282  }
283  x=w[l];
284  nm=k-1;
285  y=w[nm];
286  g=rv1[nm];
287  h=rv1[k];
288  f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y);
289  g=pythag(f,1.0);
290  f=((x-z)*(x+z)+h*((y/(f+SIGN(g,f)))-h))/x;
291  c=s=1.0;
292  for (j=l;j<=nm;j++) {
293  i=j+1;
294  g=rv1[i];
295  y=w[i];
296  h=s*g;
297  g=c*g;
298  z=pythag(f,h);
299  rv1[j]=z;
300  if ((std::fabs(z) > epsilon) /*|| (fabs(z) > EPS_SVD)*/) {
301  c=f/z;
302  s=h/z;
303  }
304  f=x*c+g*s;
305  g=g*c-x*s;
306  h=y*s;
307  y=y*c;
308  for (jj=1;jj<=n;jj++) {
309  x=v[jj][j];
310  z=v[jj][i];
311  v[jj][j]=x*c+z*s;
312  v[jj][i]=z*c-x*s;
313  }
314  z=pythag(f,h);
315  w[j]=z;
316  //if ((z != 0.0) || (fabs(z) > EPS_SVD)) {
317  if ((std::fabs(z) > epsilon) /*|| (fabs(z) > EPS_SVD)*/) {
318  z=1.0/z;
319  c=f*z;
320  s=h*z;
321  }
322  f=(c*g)+(s*y);
323  x=(c*y)-(s*g);
324  for (jj=1;jj<=m;jj++) {
325  y=a[jj][j];
326  z=a[jj][i];
327  a[jj][j]=y*c+z*s;
328  a[jj][i]=z*c-y*s;
329  }
330  }
331  rv1[l]=0.0;
332  rv1[k]=f;
333  w[k]=x;
334  }
335  }
336  for (i=0;i<n;i++) W[i] = w[i+1];
337 
338 
339  delete[] w;
340  delete[] rv1;
341  delete[] a;
342  delete[] v;
343 
344 }
345 
346 #undef SIGN
347 #undef PYTHAG
348 
366 void vpMatrix::SVBksb( const vpColVector& w,
367  const vpMatrix& v,
368  const vpColVector& b, vpColVector& x)
369 {
370  unsigned int m = this->rowNum;
371  unsigned int n = this->colNum;
372  double** u = rowPtrs;
373 
374  unsigned int jj,j,i;
375  double s,*tmp;
376 
377  tmp=new double[n];
378  for (j=0;j<n;j++) {
379  s=0.0;
380  //if (w[j])
381  if (std::fabs(w[j]) > std::numeric_limits<double>::epsilon())
382  {
383  for (i=0;i<m;i++) s += u[i][j]*b[i];
384  s /= w[j];
385  }
386  tmp[j]=s;
387  }
388  for (j=0;j<n;j++) {
389  s=0.0;
390  for (jj=0;jj<n;jj++) s += v[j][jj]*tmp[jj];
391  x[j]=s;
392  }
393  delete [] tmp;
394 }
395 
396 #define TOL 1.0e-5
397 
417 #define TOLERANCE 1.0e-7
418 
419 static
420 void svd_internal_use(double *U, double *S, double *V,
421  unsigned int nRow, unsigned int nCol)
422 {
423  unsigned int i, j, k, EstColRank, RotCount, SweepCount, slimit;
424  double eps, e2, tol, vt, p, x0, y0, q, r, c0, s0, d1, d2;
425 
426  eps = TOLERANCE;
427  slimit = nCol / 4;
428  if (slimit < 6.0)
429  slimit = 6;
430  SweepCount = 0;
431  e2 = 10.0 * nRow * eps * eps;
432  tol = eps * .1;
433  EstColRank = nCol;
434  if(V)
435  for (i = 0; i < nCol; i++)
436  for (j = 0; j < nCol; j++) {
437  V[nCol * i + j] = 0.0;
438  V[nCol * i + i] = 1.0;
439  }
440  RotCount = EstColRank * (EstColRank - 1) / 2;
441  while (RotCount != 0 && SweepCount <= slimit) {
442  RotCount = EstColRank * (EstColRank - 1) / 2;
443  SweepCount++;
444  for (j = 0; j < EstColRank - 1; j++) {
445  for (k = j + 1; k < EstColRank; k++) {
446  p = q = r = 0.0;
447  for (i = 0; i < nRow; i++) {
448  x0 = U[nCol * i + j];
449  y0 = U[nCol * i + k];
450  p += x0 * y0;
451  q += x0 * x0;
452  r += y0 * y0;
453  }
454  S[j] = q;
455  S[k] = r;
456  if (q >= r) {
457  if (q <= e2 * S[0] || fabs(p) <= tol * q)
458  RotCount--;
459  else {
460  p /= q;
461  r = 1 - r / q;
462  vt = sqrt(4 * p * p + r * r);
463  c0 = sqrt(fabs(.5 * (1 + r / vt)));
464  s0 = p / (vt * c0);
465  for (i = 0; i < nRow; i++) {
466  d1 = U[nCol * i + j];
467  d2 = U[nCol * i + k];
468  U[nCol * i + j] = d1 * c0 + d2 * s0;
469  U[nCol * i + k] = -d1 * s0 + d2 * c0;
470  }
471  if(V)
472  for (i = 0; i < nCol; i++) {
473  d1 = V[nCol * i + j];
474  d2 = V[nCol * i + k];
475  V[nCol * i + j] = d1 * c0 + d2 * s0;
476  V[nCol * i + k] = -d1 * s0 + d2 * c0;
477  }
478  }
479  }
480  else {
481  p /= r;
482  q = q / r - 1;
483  vt = sqrt(4 * p * p + q * q);
484  s0 = sqrt(fabs(.5 * (1 - q / vt)));
485  if (p < 0)
486  s0 = -s0;
487  c0 = p / (vt * s0);
488  for (i = 0; i < nRow; i++) {
489  d1 = U[nCol * i + j];
490  d2 = U[nCol * i + k];
491  U[nCol * i + j] = d1 * c0 + d2 * s0;
492  U[nCol * i + k] = -d1 * s0 + d2 * c0;
493  }
494  if(V)
495  for (i = 0; i < nCol; i++) {
496  d1 = V[nCol * i + j];
497  d2 = V[nCol * i + k];
498  V[nCol * i + j] = d1 * c0 + d2 * s0;
499  V[nCol * i + k] = -d1 * s0 + d2 * c0;
500  }
501  }
502  }
503  }
504  while (EstColRank >= 3 && S[(EstColRank - 1)] <= S[0] * tol + tol * tol)
505  EstColRank--;
506  }
507  for(i = 0; i < nCol; i++)
508  S[i] = sqrt(S[i]);
509  for(i = 0; i < nCol; i++)
510  for(j = 0; j < nRow; j++)
511  U[nCol * j + i] = U[nCol * j + i] / S[i];
512 }
513 
539 void vpMatrix::svdFlake(vpColVector &W, vpMatrix &V)
540 {
541 
542 
543  svd_internal_use(data, W.data, V.data, getRows(), getCols());
544 }
545 
546 
547 #if (VISP_HAVE_OPENCV_VERSION >= 0x020101) // Require opencv >= 2.1.1
548 # include <opencv2/core/core.hpp>
549 void vpMatrix::svdOpenCV(vpColVector& w, vpMatrix& v){
550  int rows = (int)this->getRows();
551  int cols = (int)this->getCols();
552  cv::Mat m(rows, cols, CV_64F, this->data);
553  cv::SVD opencvSVD(m);
554  cv::Mat opencvV = opencvSVD.vt;
555  cv::Mat opencvW = opencvSVD.w;
556  v.resize((unsigned int)opencvV.rows, (unsigned int)opencvV.cols);
557  w.resize((unsigned int)(opencvW.rows*opencvW.cols));
558 
559  memcpy(v.data, opencvV.data, (size_t)(8*opencvV.rows*opencvV.cols));
560  v=v.transpose();
561  memcpy(w.data, opencvW.data, (size_t)(8*opencvW.rows*opencvW.cols));
562  this->resize((unsigned int)opencvSVD.u.rows, (unsigned int)opencvSVD.u.cols);
563  memcpy(this->data,opencvSVD.u.data, (size_t)(8*opencvSVD.u.rows*opencvSVD.u.cols));
564 }
565 
566 #endif
567 
568 #ifdef VISP_HAVE_LAPACK
569 extern "C" int dgesdd_(char *jobz, int *m, int *n, double *a, int *lda, double *s, double *u, int *ldu, double *vt, int *ldvt, double *work, int *lwork, int *iwork, int *info);
570 #include <stdio.h>
571 #include <string.h>
572 
573 void vpMatrix::svdLapack(vpColVector& W, vpMatrix& V){
574  /* unsigned */ int m = static_cast<int>(this->getCols()), n = static_cast<int>(this->getRows()), lda = m, ldu = m, ldvt = std::min(m,n);
575  int info, lwork;
576 
577  double wkopt;
578  double* work;
579 
580  int* iwork = new int[8*static_cast<unsigned int>(std::min(n,m))];
581 
582  double *s = W.data;
583  double* a = new double[static_cast<unsigned int>(lda*n)];
584  memcpy(a,this->data,this->getRows()*this->getCols()*sizeof(double));
585  double* u = V.data;
586  double* vt = this->data;
587 
588 
589 
590  lwork = -1;
591  dgesdd_( (char*)"S", &m, &n, a, &lda, s, u, &ldu, vt, &ldvt, &wkopt, &lwork, iwork, &info );
592  lwork = (int)wkopt;
593  work = new double[static_cast<unsigned int>(lwork)];
594 
595  dgesdd_( (char*)"S", &m, &n, a, &lda, s, u, &ldu, vt, &ldvt, work, &lwork, iwork, &info );
596 
597  if( info > 0 ) {
598  vpTRACE("The algorithm computing SVD failed to converge.");
599  }
600 
601  V=V.transpose();
602  delete[] work;
603  delete[] iwork;
604  delete[] a;
605 }
606 #endif
607 
608 #ifdef VISP_HAVE_GSL
609 #include <gsl/gsl_linalg.h>
610 
611 void
612 vpMatrix::svdGsl(vpColVector& w, vpMatrix& v)
613 {
614 
615 #if 0
616  // premier test avec la gsl 1. on recopie...
617  int i,j ;
618 
619  int nc = getCols() ;
620  int nr = getRows() ;
621  gsl_matrix *A = gsl_matrix_alloc(nr, nc) ;
622 
623  int Atda = A->tda ;
624  for (i=0 ; i < nr ; i++)
625  {
626  int k = i*Atda ;
627  for (j=0 ; j < nc ; j++)
628  A->data[k+j] = (*this)[i][j] ;
629  }
630  // gsl_matrix_set(A,i,j,(*this)[i][j]) ;
631 
632  gsl_matrix *V = gsl_matrix_alloc(nc, nc) ;
633  gsl_vector *S = gsl_vector_alloc(nc) ;
634  gsl_vector *work = gsl_vector_alloc(nc) ;
635 
636  gsl_linalg_SV_decomp(A,V,S, work) ;
637 // gsl_linalg_SV_decomp_jacobi(A,V,S) ;
638 
639 
640  //l'acces par gsl_matrix_get est tres lourd, voir si on peut pas faire
641  // autremement (surement !)
642 
643  Atda = A->tda ;
644  for (i=0 ; i < nr ; i++)
645  for (j=0 ; j < nc ; j++)
646  (*this)[i][j] = gsl_matrix_get(A,i,j) ;
647 
648  int Vtda = V->tda ;
649  for (i=0 ; i < nc ; i++)
650  {
651  int k = i*Vtda ;
652  for (j=0 ; j < nc ; j++)
653  v[i][j] = V->data[k+j] ;
654  }
655 
656  for (j=0 ; j < nc ; j++)
657  w[j] = gsl_vector_get(S,j) ;
658 
659 
660  gsl_matrix_free(V) ;
661  gsl_matrix_free(A) ;
662  gsl_vector_free(S) ;
663  gsl_vector_free(work) ;
664 
665 #else //optimisation Anthony 20/03/2008
666 
667  unsigned int nc = getCols() ;
668  unsigned int nr = getRows() ;
669  gsl_vector *work = gsl_vector_alloc(nc) ;
670 
671 // gsl_linalg_SV_decomp_jacobi(A,V,S) ;
672 
673 
674  //l'acces par gsl_matrix_get est tres lourd, voir si on peut pas faire
675  // autremement (surement !)
676 
677  gsl_matrix A;
678  A.size1 = nr;
679  A.size2 = nc;
680  A.tda = A.size2;
681  A.data = this->data;
682  A.owner = 0;
683  A.block = 0;
684 
685  gsl_matrix V;
686  V.size1 = nc;
687  V.size2 = nc;
688  V.tda = V.size2;
689  V.data = v.data;
690  V.owner = 0;
691  V.block = 0;
692 
693  gsl_vector S;
694  S.size = nc;
695  S.stride = 1;
696  S.data = w.data;
697  S.owner = 0;
698  S.block = 0;
699 
700  gsl_linalg_SV_decomp(&A,&V,&S, work) ;
701 
702  gsl_vector_free(work) ;
703 
704 #endif
705 }
706 #endif // # #GSL
707 
708 
709 #undef TOL
710 #undef TOLERANCE
711 
712 #undef MAX_ITER_SVD
713 
714 #endif // doxygen should skip this
Definition of the vpMatrix class.
Definition: vpMatrix.h:98
void resize(const unsigned int nrows, const unsigned int ncols, const bool nullify=true)
Definition: vpMatrix.cpp:183
#define vpERROR_TRACE
Definition: vpDebug.h:395
#define vpTRACE
Definition: vpDebug.h:418
static Type maximum(const Type &a, const Type &b)
Definition: vpMath.h:137
double * data
address of the first element of the data array
Definition: vpMatrix.h:118
double ** rowPtrs
address of the first element of each rows
Definition: vpMatrix.h:121
static double sqr(double x)
Definition: vpMath.h:106
vpMatrix transpose() const
Definition: vpMatrix.cpp:1255
unsigned int rowNum
number of rows
Definition: vpMatrix.h:112
Class that provides a data structure for the column vectors as well as a set of operations on these v...
Definition: vpColVector.h:72
unsigned int getCols() const
Return the number of columns of the matrix.
Definition: vpMatrix.h:163
error that can be emited by the vpMatrix class and its derivates
unsigned int colNum
number of columns
Definition: vpMatrix.h:114
unsigned int getRows() const
Return the number of rows of the matrix.
Definition: vpMatrix.h:161
void resize(const unsigned int i, const bool flagNullify=true)
Definition: vpColVector.h:94