Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpMatrix_qr.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 QR decomposition.
32 */
33
34#include <algorithm> // for (std::min) and (std::max)
35#include <cmath> // For std::abs() on iOS
36#include <cstdlib> // For std::abs() on iOS
37#include <visp3/core/vpConfig.h>
38
39#include <visp3/core/vpColVector.h>
40#include <visp3/core/vpMath.h>
41#include <visp3/core/vpMatrix.h>
42
43// Exception
44#include <visp3/core/vpException.h>
45#include <visp3/core/vpMatrixException.h>
46
48#ifdef VISP_HAVE_LAPACK
49#ifdef VISP_HAVE_GSL
50#if !(GSL_MAJOR_VERSION >= 2 && GSL_MINOR_VERSION >= 2)
51// Needed for GSL_VERSION < 2.2 where gsl_linalg_tri_*_invert() not present
52#include <gsl/gsl_blas.h>
53#include <gsl/gsl_math.h>
54#include <gsl/gsl_matrix.h>
55#include <gsl/gsl_vector.h>
56#endif
57#include <gsl/gsl_linalg.h>
58#include <gsl/gsl_permutation.h>
59#endif
60#ifdef VISP_HAVE_MKL
61#include <mkl.h>
62typedef MKL_INT integer;
63
64integer allocate_work(double **work)
65{
66 integer dimWork = (integer)((*work)[0]);
67 delete[] * work;
68 *work = new double[dimWork];
69 return (integer)dimWork;
70}
71#elif !defined(VISP_HAVE_GSL)
72#ifdef VISP_HAVE_LAPACK_BUILT_IN
73typedef long int integer;
74#else
75typedef int integer;
76#endif
77extern "C" int dgeqrf_(integer *m, integer *n, double *a, integer *lda, double *tau, double *work, integer *lwork,
78 integer *info);
79extern "C" int dormqr_(char *side, char *trans, integer *m, integer *n, integer *k, double *a, integer *lda,
80 double *tau, double *c__, integer *ldc, double *work, integer *lwork, integer *info);
81extern "C" int dorgqr_(integer *, integer *, integer *, double *, integer *, double *, double *, integer *, integer *);
82extern "C" int dtrtri_(char *uplo, char *diag, integer *n, double *a, integer *lda, integer *info);
83extern "C" int dgeqp3_(integer *m, integer *n, double *a, integer *lda, integer *p, double *tau, double *work,
84 integer *lwork, integer *info);
85
86int allocate_work(double **work);
87
88int allocate_work(double **work)
89{
90 unsigned int dimWork = static_cast<unsigned int>((*work)[0]);
91 delete[] * work;
92 *work = new double[dimWork];
93 return static_cast<int>(dimWork);
94}
95#endif
96#endif
97
98#if defined(VISP_HAVE_GSL)
99#ifndef DOXYGEN_SHOULD_SKIP_THIS
100void display_gsl(gsl_matrix *M)
101{
102 // display
103 for (unsigned int i = 0; i < M->size1; ++i) {
104 unsigned int k = i * M->tda;
105 for (unsigned int j = 0; j < M->size2; ++j) {
106 std::cout << M->data[k + j] << " ";
107 }
108 std::cout << std::endl;
109 }
110}
111#endif
112#endif
113
114#if defined(VISP_HAVE_LAPACK)
149{
150#if defined(VISP_HAVE_GSL)
151 {
152 vpMatrix inv;
153 inv.resize(colNum, rowNum, false);
154 gsl_matrix *gsl_A, *gsl_Q, *gsl_R, gsl_inv;
155 gsl_vector *gsl_tau;
156
157 gsl_A = gsl_matrix_alloc(rowNum, colNum);
158 gsl_Q = gsl_matrix_alloc(rowNum, rowNum); // M by M
159 gsl_R = gsl_matrix_alloc(rowNum, colNum); // M by N
160 gsl_tau = gsl_vector_alloc(std::min<unsigned int>(rowNum, colNum));
161
162 gsl_inv.size1 = inv.rowNum;
163 gsl_inv.size2 = inv.colNum;
164 gsl_inv.tda = gsl_inv.size2;
165 gsl_inv.data = inv.data;
166 gsl_inv.owner = 0;
167 gsl_inv.block = 0;
168
169 // copy input matrix since gsl_linalg_QR_decomp() is destructive
170 unsigned int Atda = static_cast<unsigned int>(gsl_A->tda);
171 size_t len = sizeof(double) * colNum;
172 for (unsigned int i = 0; i < rowNum; ++i) {
173 unsigned int k = i * Atda;
174 memcpy(&gsl_A->data[k], (*this)[i], len);
175 }
176 gsl_linalg_QR_decomp(gsl_A, gsl_tau);
177 gsl_linalg_QR_unpack(gsl_A, gsl_tau, gsl_Q, gsl_R);
178#if (GSL_MAJOR_VERSION >= 2 && GSL_MINOR_VERSION >= 2)
179 gsl_linalg_tri_upper_invert(gsl_R);
180#else
181 {
182 gsl_matrix_view m;
183 gsl_vector_view v;
184 for (unsigned int i = 0; i < rowNum; ++i) {
185 double *Tii = gsl_matrix_ptr(gsl_R, i, i);
186 *Tii = 1.0 / (*Tii);
187 double aii = -(*Tii);
188 if (i > 0) {
189 m = gsl_matrix_submatrix(gsl_R, 0, 0, i, i);
190 v = gsl_matrix_subcolumn(gsl_R, i, 0, i);
191 gsl_blas_dtrmv(CblasUpper, CblasNoTrans, CblasNonUnit, &m.matrix, &v.vector);
192 gsl_blas_dscal(aii, &v.vector);
193 }
194 }
195 }
196#endif
197 gsl_matrix_transpose(gsl_Q);
198
199 gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, gsl_R, gsl_Q, 0, &gsl_inv);
200 unsigned int gsl_inv_tda = static_cast<unsigned int>(gsl_inv.tda);
201 size_t inv_len = sizeof(double) * inv.colNum;
202 for (unsigned int i = 0; i < inv.rowNum; ++i) {
203 unsigned int k = i * gsl_inv_tda;
204 memcpy(inv[i], &gsl_inv.data[k], inv_len);
205 }
206
207 gsl_matrix_free(gsl_A);
208 gsl_matrix_free(gsl_Q);
209 gsl_matrix_free(gsl_R);
210 gsl_vector_free(gsl_tau);
211
212 return inv;
213 }
214#else
215 {
216 if (rowNum != colNum) {
217 throw(vpMatrixException(vpMatrixException::matrixError, "Cannot inverse a non-square matrix (%ux%u) by QR",
218 rowNum, colNum));
219 }
220
221 integer rowNum_ = (integer)this->getRows();
222 integer colNum_ = (integer)this->getCols();
223 integer lda = (integer)rowNum_; // lda is the number of rows because we don't use a submatrix
224 integer dimTau = std::min<integer>(rowNum_, colNum_);
225 integer dimWork = -1;
226 double *tau = new double[dimTau];
227 double *work = new double[1];
228 integer info;
229 vpMatrix C;
230 vpMatrix A = *this;
231
232 try {
233 // 1) Extract householder reflections (useful to compute Q) and R
234 dgeqrf_(&rowNum_, // The number of rows of the matrix A. M >= 0.
235 &colNum_, // The number of columns of the matrix A. N >= 0.
236 A.data, /*On entry, the M-by-N matrix A.
237 On exit, the elements on and above the diagonal of
238 the array contain the min(M,N)-by-N upper trapezoidal
239 matrix R (R is upper triangular if m >= n); the
240 elements below the diagonal, with the array TAU,
241 represent the orthogonal matrix Q as a product of
242 min(m,n) elementary reflectors.
243 */
244 &lda, // The leading dimension of the array A. LDA >= max(1,M).
245 tau, /*Dimension (min(M,N))
246 The scalar factors of the elementary reflectors
247 */
248 work, // Internal working array. dimension (MAX(1,LWORK))
249 &dimWork, // The dimension of the array WORK. LWORK >= max(1,N).
250 &info // status
251 );
252
253 if (info != 0) {
254 std::cout << "dgeqrf_:Preparation:" << -info << "th element had an illegal value" << std::endl;
256 }
257 dimWork = allocate_work(&work);
258
259 dgeqrf_(&rowNum_, // The number of rows of the matrix A. M >= 0.
260 &colNum_, // The number of columns of the matrix A. N >= 0.
261 A.data, /*On entry, the M-by-N matrix A.
262 On exit, the elements on and above the diagonal of
263 the array contain the min(M,N)-by-N upper trapezoidal
264 matrix R (R is upper triangular if m >= n); the
265 elements below the diagonal, with the array TAU,
266 represent the orthogonal matrix Q as a product of
267 min(m,n) elementary reflectors.
268 */
269 &lda, // The leading dimension of the array A. LDA >= max(1,M).
270 tau, /*Dimension (min(M,N))
271 The scalar factors of the elementary reflectors
272 */
273 work, // Internal working array. dimension (MAX(1,LWORK))
274 &dimWork, // The dimension of the array WORK. LWORK >= max(1,N).
275 &info // status
276 );
277
278 if (info != 0) {
279 std::cout << "dgeqrf_:" << -info << "th element had an illegal value" << std::endl;
281 }
282
283 // A now contains the R matrix in its upper triangular (in lapack
284 // convention)
285 C = A;
286
287 // 2) Invert R
288 dtrtri_((char *)"U", (char *)"N", &dimTau, C.data, &lda, &info);
289 if (info != 0) {
290 if (info < 0)
291 std::cout << "dtrtri_:" << -info << "th element had an illegal value" << std::endl;
292 else if (info > 0) {
293 std::cout << "dtrtri_:R(" << info << "," << info << ")"
294 << " is exactly zero. The triangular matrix is singular "
295 "and its inverse can not be computed."
296 << std::endl;
297 std::cout << "R=" << std::endl << C << std::endl;
298 }
300 }
301
302 // 3) Zero-fill R^-1
303 // the matrix is upper triangular for lapack but lower triangular for visp
304 // we fill it with zeros above the diagonal (where we don't need the
305 // values)
306 for (unsigned int i = 0; i < C.getRows(); ++i)
307 for (unsigned int j = 0; j < C.getRows(); ++j)
308 if (j > i)
309 C[i][j] = 0.;
310
311 dimWork = -1;
312 integer ldc = lda;
313
314 // 4) Transpose Q and left-multiply it by R^-1
315 // get R^-1*tQ
316 // C contains R^-1
317 // A contains Q
318 dormqr_((char *)"R", (char *)"T", &rowNum_, &colNum_, &dimTau, A.data, &lda, tau, C.data, &ldc, work, &dimWork,
319 &info);
320 if (info != 0) {
321 std::cout << "dormqr_:Preparation" << -info << "th element had an illegal value" << std::endl;
323 }
324 dimWork = allocate_work(&work);
325
326 dormqr_((char *)"R", (char *)"T", &rowNum_, &colNum_, &dimTau, A.data, &lda, tau, C.data, &ldc, work, &dimWork,
327 &info);
328
329 if (info != 0) {
330 std::cout << "dormqr_:" << -info << "th element had an illegal value" << std::endl;
332 }
333 delete[] tau;
334 delete[] work;
335 }
336 catch (vpMatrixException &) {
337 delete[] tau;
338 delete[] work;
339 throw;
340 }
341
342 return C;
343 }
344#endif
345}
346#endif
347
383{
384#if defined(VISP_HAVE_LAPACK)
385 return inverseByQRLapack();
386#else
387 throw(vpException(vpException::fatalError, "Cannot inverse matrix by QR. Install Lapack 3rd party"));
388#endif
389}
390
449unsigned int vpMatrix::qr(vpMatrix &Q, vpMatrix &R, bool full, bool squareR, double tol) const
450{
451#if defined(VISP_HAVE_LAPACK)
452#if defined(VISP_HAVE_GSL)
453 unsigned int m = rowNum; // also rows of Q
454 unsigned int n = colNum; // also columns of R
455 unsigned int r = std::min<unsigned int>(n, m); // a priori non-null rows of R = rank of R
456 unsigned int q = r; // columns of Q and rows of R
457 unsigned int na = n; // columns of A
458
459 // cannot be full decomposition if m < n
460 if (full && m > n) {
461 q = m; // Q is square
462 na = m; // A is square
463 }
464
465 // prepare matrices and deal with r = 0
466 Q.resize(m, q, false);
467 if (squareR)
468 R.resize(r, r, false);
469 else
470 R.resize(r, n, false);
471 if (r == 0)
472 return 0;
473
474 gsl_matrix *gsl_A, *gsl_Q, *gsl_R, gsl_Qfull;
475 gsl_vector *gsl_tau;
476
477 gsl_A = gsl_matrix_alloc(rowNum, colNum);
478 gsl_R = gsl_matrix_alloc(rowNum, colNum); // M by N
479 gsl_tau = gsl_vector_alloc(std::min<unsigned int>(rowNum, colNum));
480
481 // copy input matrix since gsl_linalg_QR_decomp() is destructive
482 unsigned int Atda = static_cast<unsigned int>(gsl_A->tda);
483 size_t len = sizeof(double) * colNum;
484 for (unsigned int i = 0; i < rowNum; ++i) {
485 unsigned int k = i * Atda;
486 memcpy(&gsl_A->data[k], (*this)[i], len);
487 // for (unsigned int j = 0; j < colNum; ++j)
488 // gsl_A->data[k + j] = (*this)[i][j];
489 }
490
491 gsl_linalg_QR_decomp(gsl_A, gsl_tau);
492
493 if (full) {
494 // Q is M by M as expected by gsl_linalg_QR_unpack()
495 // for performance purpose dont allocate gsl_Q, but rather use gsl_Qfull = Q
496 gsl_Qfull.size1 = Q.rowNum;
497 gsl_Qfull.size2 = Q.colNum;
498 gsl_Qfull.tda = gsl_Qfull.size2;
499 gsl_Qfull.data = Q.data;
500 gsl_Qfull.owner = 0;
501 gsl_Qfull.block = 0;
502
503 gsl_linalg_QR_unpack(gsl_A, gsl_tau, &gsl_Qfull, gsl_R);
504 // std::cout << "gsl_Qfull:\n "; display_gsl(&gsl_Qfull);
505 // std::cout << "gsl_R:\n "; display_gsl(gsl_R);
506 }
507 else {
508 gsl_Q = gsl_matrix_alloc(rowNum, rowNum); // M by M
509
510 gsl_linalg_QR_unpack(gsl_A, gsl_tau, gsl_Q, gsl_R);
511 // std::cout << "gsl_Q:\n "; display_gsl(gsl_Q);
512 // std::cout << "gsl_R:\n "; display_gsl(gsl_R);
513
514 unsigned int Qtda = static_cast<unsigned int>(gsl_Q->tda);
515 size_t len = sizeof(double) * Q.colNum;
516 for (unsigned int i = 0; i < Q.rowNum; ++i) {
517 unsigned int k = i * Qtda;
518 memcpy(Q[i], &gsl_Q->data[k], len);
519 // for(unsigned int j = 0; j < Q.colNum; ++j) {
520 // Q[i][j] = gsl_Q->data[k + j];
521 // }
522 }
523 gsl_matrix_free(gsl_Q);
524 }
525
526 // copy useful part of R and update rank
527 na = std::min<unsigned int>(m, n);
528 unsigned int Rtda = static_cast<unsigned int>(gsl_R->tda);
529 size_t Rlen = sizeof(double) * R.colNum;
530 for (unsigned int i = 0; i < na; ++i) {
531 unsigned int k = i * Rtda;
532 memcpy(R[i], &gsl_R->data[k], Rlen);
533 // for(unsigned int j = i; j < na; ++j)
534 // R[i][j] = gsl_R->data[k + j];
535 if (std::abs(gsl_R->data[k + i]) < tol)
536 r--;
537 }
538
539 gsl_matrix_free(gsl_A);
540 gsl_matrix_free(gsl_R);
541 gsl_vector_free(gsl_tau);
542
543 return r;
544#else
545 integer m = (integer)rowNum; // also rows of Q
546 integer n = (integer)colNum; // also columns of R
547 integer r = std::min<integer>(n, m); // a priori non-null rows of R = rank of R
548 integer q = r; // columns of Q and rows of R
549 integer na = n; // columns of A
550
551 // cannot be full decomposition if m < n
552 if (full && m > n) {
553 q = m; // Q is square
554 na = m; // A is square
555 }
556
557 // prepare matrices and deal with r = 0
558 Q.resize(static_cast<unsigned int>(m), static_cast<unsigned int>(q));
559 if (squareR)
560 R.resize(static_cast<unsigned int>(r), static_cast<unsigned int>(r));
561 else
562 R.resize(static_cast<unsigned int>(r), static_cast<unsigned int>(n));
563 if (r == 0)
564 return 0;
565
566 integer dimWork = -1;
567 double *qrdata = new double[m * na];
568 double *tau = new double[std::min<integer>(m, q)];
569 double *work = new double[1];
570 integer info;
571
572 // copy this to qrdata in Lapack convention
573 for (integer i = 0; i < m; ++i) {
574 for (integer j = 0; j < n; ++j)
575 qrdata[i + m * j] = data[j + n * i];
576 for (integer j = n; j < na; ++j)
577 qrdata[i + m * j] = 0;
578 }
579
580 // work = new double[1];
581 // 1) Extract householder reflections (useful to compute Q) and R
582 dgeqrf_(&m, // The number of rows of the matrix A. M >= 0.
583 &na, // The number of columns of the matrix A. N >= 0.
584 qrdata, &m, tau,
585 work, // Internal working array. dimension (MAX(1,LWORK))
586 &dimWork, // The dimension of the array WORK. LWORK >= max(1,N).
587 &info // status
588 );
589
590 if (info != 0) {
591 std::cout << "dgeqrf_:Preparation:" << -info << "th element had an illegal value" << std::endl;
592 delete[] qrdata;
593 delete[] work;
594 delete[] tau;
596 }
597 dimWork = allocate_work(&work);
598
599 dgeqrf_(&m, // The number of rows of the matrix A. M >= 0.
600 &na, // The number of columns of the matrix A. N >= 0.
601 qrdata,
602 &m, // The leading dimension of the array A. LDA >= max(1,M).
603 tau,
604 work, // Internal working array. dimension (MAX(1,LWORK))
605 &dimWork, // The dimension of the array WORK. LWORK >= max(1,N).
606 &info // status
607 );
608
609 if (info != 0) {
610 std::cout << "dgeqrf_:" << -info << "th element had an illegal value" << std::endl;
611 delete[] qrdata;
612 delete[] work;
613 delete[] tau;
615 }
616
617 // data now contains the R matrix in its upper triangular (in lapack convention)
618
619 // copy useful part of R from Q and update rank
620 na = std::min<integer>(m, n);
621 if (squareR) {
622 for (int i = 0; i < na; ++i) {
623 for (int j = i; j < na; ++j)
624 R[i][j] = qrdata[i + m * j];
625 if (std::abs(qrdata[i + m * i]) < tol)
626 r--;
627 }
628 }
629 else {
630 for (int i = 0; i < na; ++i) {
631 for (int j = i; j < n; ++j)
632 R[i][j] = qrdata[i + m * j];
633 if (std::abs(qrdata[i + m * i]) < tol)
634 r--;
635 }
636 }
637
638 // extract Q
639 dorgqr_(&m, // The number of rows of the matrix Q. M >= 0.
640 &q, // The number of columns of the matrix Q. M >= N >= 0.
641 &q, qrdata,
642 &m, // The leading dimension of the array A. LDA >= max(1,M).
643 tau,
644 work, // Internal working array. dimension (MAX(1,LWORK))
645 &dimWork, // The dimension of the array WORK. LWORK >= max(1,N).
646 &info // status
647 );
648
649 // write qrdata into Q
650 for (int i = 0; i < m; ++i)
651 for (int j = 0; j < q; ++j)
652 Q[i][j] = qrdata[i + m * j];
653
654 delete[] qrdata;
655 delete[] work;
656 delete[] tau;
657 return static_cast<unsigned int>(r);
658#endif // defined(VISP_HAVE_GSL)
659#else
660 (void)Q;
661 (void)R;
662 (void)full;
663 (void)squareR;
664 (void)tol;
665 throw(vpException(vpException::fatalError, "Cannot perform QR decomposition. Install Lapack 3rd party"));
666#endif
667}
668
669#if defined(VISP_HAVE_LAPACK)
670#if !defined(VISP_HAVE_GSL)
736unsigned int vpMatrix::qrPivotLapack(vpMatrix &Q, vpMatrix &R, vpMatrix &P, bool full, bool squareR, double tol) const
737{
738 integer m = static_cast<integer>(rowNum); // also rows of Q
739 integer n = static_cast<integer>(colNum); // also columns of R
740 integer r = std::min<integer>(n, m); // a priori non-null rows of R = rank of R
741 integer q = r; // columns of Q and rows of R
742 integer na = n; // columns of A
743
744 // cannot be full decomposition if m < n
745 // cannot be full decomposition if m < n
746 if (full && m > n) {
747 q = m; // Q is square
748 na = m; // A is square
749 }
750
751 // prepare Q and deal with r = 0
752 Q.resize(static_cast<unsigned int>(m), static_cast<unsigned int>(q));
753 if (r == 0) {
754 if (squareR) {
755 R.resize(0, 0);
756 P.resize(0, static_cast<unsigned int>(n));
757 }
758 else {
759 R.resize(static_cast<unsigned int>(r), static_cast<unsigned int>(n));
760 P.resize(static_cast<unsigned int>(n), static_cast<unsigned int>(n));
761 }
762 return 0;
763 }
764
765 integer dimWork = -1;
766 integer min_q_m = std::min<integer>(q, m);
767 double *qrdata = new double[m * na];
768 double *tau = new double[min_q_m];
769 double *work = new double[1];
770 integer *p = new integer[na];
771 for (int i = 0; i < na; ++i) {
772 p[i] = 0;
773 }
774
775 integer info;
776
777 // copy this to qrdata in Lapack convention
778 for (integer i = 0; i < m; ++i) {
779 for (integer j = 0; j < n; ++j) {
780 qrdata[i + m * j] = data[j + n * i];
781 }
782 for (integer j = n; j < na; ++j) {
783 qrdata[i + m * j] = 0;
784 }
785 }
786
787 // 1) Extract householder reflections (useful to compute Q) and R
788 // m: The number of rows of the matrix A. M >= 0.
789 // na: The number of columns of the matrix A. N >= 0.
790 // qrdata: On entry, the M-by-N matrix A.
791 // m: The leading dimension of the array A. LDA >= max(1,M).
792 // p: Dimension N
793 // tau: dimension (min(M,N))
794 // work: Internal working array. dimension (3*N)
795 // info: status
796 dgeqp3_(&m, &na, qrdata, &m, p, tau, work, &dimWork, &info);
797
798 if (info != 0) {
799 std::cout << "dgeqp3_:Preparation:" << -info << "th element had an illegal value" << std::endl;
800 delete[] qrdata;
801 delete[] work;
802 delete[] tau;
803 delete[] p;
805 }
806
807 dimWork = allocate_work(&work);
808
809 // m: The number of rows of the matrix A. M >= 0.
810 // na: The number of columns of the matrix A. N >= 0.
811 // qrdata: On entry, the M-by-N matrix A.
812 // m: The leading dimension of the array A. LDA >= max(1,M).
813 // p: Dimension N
814 // tau: Dimension (min(M,N))
815 // work: Internal working array. dimension (3*N)
816 // info: status
817 dgeqp3_(&m, &na, qrdata, &m, p, tau, work, &dimWork, &info);
818
819 if (info != 0) {
820 std::cout << "dgeqp3_:" << -info << " th element had an illegal value" << std::endl;
821 delete[] qrdata;
822 delete[] work;
823 delete[] tau;
824 delete[] p;
826 }
827
828 // data now contains the R matrix in its upper triangular (in lapack convention)
829 // get rank of R in r
830 na = std::min<integer>(n, m);
831 for (int i = 0; i < na; ++i) {
832 if (std::abs(qrdata[i + m * i]) < tol) {
833 --r;
834 }
835 }
836
837 // write R
838 if (squareR) // R r x r
839 {
840 R.resize(static_cast<unsigned int>(r), static_cast<unsigned int>(r));
841 for (int i = 0; i < r; ++i) {
842 for (int j = i; j < r; ++j) {
843 R[i][j] = qrdata[i + m * j];
844 }
845 }
846
847 // write P
848 P.resize(static_cast<unsigned int>(r), static_cast<unsigned int>(n));
849 for (int i = 0; i < r; ++i) {
850 P[i][p[i] - 1] = 1;
851 }
852 }
853 else // R is min(m,n) x n of rank r
854 {
855 R.resize(static_cast<unsigned int>(na), static_cast<unsigned int>(n));
856 for (int i = 0; i < na; ++i) {
857 for (int j = i; j < n; ++j) {
858 R[i][j] = qrdata[i + m * j];
859 }
860 }
861 // write P
862 P.resize(static_cast<unsigned int>(n), static_cast<unsigned int>(n));
863 for (int i = 0; i < n; ++i) {
864 P[i][p[i] - 1] = 1;
865 }
866 }
867
868 // extract Q
869 // m: The number of rows of the matrix Q. M >= 0.
870 // q: The number of columns of the matrix Q. M >= N >= 0.
871 // m: The leading dimension of the array A. LDA >= max(1,M).
872 // ork: Internal working array. dimension (MAX(1,LWORK))
873 // dimWork: The dimension of the array WORK. LWORK >= max(1,N).
874 // info; status
875 dorgqr_(&m, &q, &q, qrdata, &m, tau, work, &dimWork, &info);
876
877 // write qrdata into Q
878 for (int i = 0; i < m; ++i) {
879 for (int j = 0; j < q; ++j) {
880 Q[i][j] = qrdata[i + m * j];
881 }
882 }
883
884 delete[] qrdata;
885 delete[] work;
886 delete[] tau;
887 delete[] p;
888 return static_cast<unsigned int>(r);
889}
890#endif // VISP_HAVE_GSL
891
892#ifdef VISP_HAVE_GSL
958unsigned int vpMatrix::qrPivotLapackGSL(vpMatrix &Q, vpMatrix &R, vpMatrix &P, bool full, bool squareR, double tol) const
959{
960 unsigned int m = rowNum; // also rows of Q
961 unsigned int n = colNum; // also columns of R
962 unsigned int r = std::min<unsigned int>(n, m); // a priori non-null rows of R = rank of R
963 unsigned int q = r; // columns of Q and rows of R
964 unsigned int na = n; // columns of A
965
966 // cannot be full decomposition if m < n
967 if (full && m > n) {
968 q = m; // Q is square
969 na = m; // A is square
970 }
971
972 // prepare Q and deal with r = 0
973 Q.resize(m, q, false);
974 if (r == 0) {
975 if (squareR) {
976 R.resize(0, 0, false);
977 P.resize(0, n, false);
978 }
979 else {
980 R.resize(r, n, false);
981 P.resize(n, n);
982 }
983 return 0;
984 }
985
986 gsl_matrix gsl_A, *gsl_Q, *gsl_R, gsl_Qfull;
987 gsl_vector *gsl_tau;
988 gsl_permutation *gsl_p;
989 gsl_vector *gsl_norm;
990 int gsl_signum;
991
992 gsl_A.size1 = rowNum;
993 gsl_A.size2 = colNum;
994 gsl_A.tda = gsl_A.size2;
995 gsl_A.data = this->data;
996 gsl_A.owner = 0;
997 gsl_A.block = 0;
998
999 gsl_R = gsl_matrix_alloc(rowNum, colNum); // M by N
1000 gsl_tau = gsl_vector_alloc(std::min<unsigned int>(rowNum, colNum));
1001 gsl_norm = gsl_vector_alloc(colNum);
1002 gsl_p = gsl_permutation_alloc(n);
1003
1004 if (full) {
1005 // Q is M by M as expected by gsl_linalg_QR_unpack()
1006 // for performance purpose dont allocate gsl_Q, but rather use gsl_Qfull = Q
1007 gsl_Qfull.size1 = Q.rowNum;
1008 gsl_Qfull.size2 = Q.colNum;
1009 gsl_Qfull.tda = gsl_Qfull.size2;
1010 gsl_Qfull.data = Q.data;
1011 gsl_Qfull.owner = 0;
1012 gsl_Qfull.block = 0;
1013
1014 gsl_linalg_QRPT_decomp2(&gsl_A, &gsl_Qfull, gsl_R, gsl_tau, gsl_p, &gsl_signum, gsl_norm);
1015 // std::cout << "gsl_Qfull:\n "; display_gsl(&gsl_Qfull);
1016 // std::cout << "gsl_R:\n "; display_gsl(gsl_R);
1017 }
1018 else {
1019 gsl_Q = gsl_matrix_alloc(rowNum, rowNum); // M by M
1020
1021 gsl_linalg_QRPT_decomp2(&gsl_A, gsl_Q, gsl_R, gsl_tau, gsl_p, &gsl_signum, gsl_norm);
1022 // std::cout << "gsl_Q:\n "; display_gsl(gsl_Q);
1023 // std::cout << "gsl_R:\n "; display_gsl(gsl_R);
1024
1025 unsigned int Qtda = static_cast<unsigned int>(gsl_Q->tda);
1026 size_t len = sizeof(double) * Q.colNum;
1027 for (unsigned int i = 0; i < Q.rowNum; ++i) {
1028 unsigned int k = i * Qtda;
1029 memcpy(Q[i], &gsl_Q->data[k], len);
1030 // for(unsigned int j = 0; j < Q.colNum; ++j) {
1031 // Q[i][j] = gsl_Q->data[k + j];
1032 // }
1033 }
1034 gsl_matrix_free(gsl_Q);
1035 }
1036
1037 // update rank
1038 na = std::min<unsigned int>(m, n);
1039 unsigned int Rtda = static_cast<unsigned int>(gsl_R->tda);
1040 for (unsigned int i = 0; i < na; ++i) {
1041 unsigned int k = i * Rtda;
1042 if (std::abs(gsl_R->data[k + i]) < tol)
1043 r--;
1044 }
1045
1046 if (squareR) {
1047 R.resize(r, r, false); // R r x r
1048 P.resize(r, n);
1049 for (unsigned int i = 0; i < r; ++i)
1050 P[i][gsl_p->data[i]] = 1;
1051 }
1052 else {
1053 R.resize(na, n, false); // R is min(m,n) x n of rank r
1054 P.resize(n, n);
1055 for (unsigned int i = 0; i < n; ++i)
1056 P[i][gsl_p->data[i]] = 1;
1057 }
1058
1059 // copy useful part of R
1060 size_t Rlen = sizeof(double) * R.colNum;
1061 for (unsigned int i = 0; i < na; ++i) {
1062 unsigned int k = i * Rtda;
1063 memcpy(R[i], &gsl_R->data[k], Rlen);
1064 }
1065
1066 gsl_matrix_free(gsl_R);
1067 gsl_vector_free(gsl_tau);
1068 gsl_vector_free(gsl_norm);
1069 gsl_permutation_free(gsl_p);
1070
1071 return r;
1072}
1073#endif
1074#endif
1075
1142unsigned int vpMatrix::qrPivot(vpMatrix &Q, vpMatrix &R, vpMatrix &P, bool full, bool squareR, double tol) const
1143{
1144#if defined(VISP_HAVE_LAPACK)
1145#if defined(VISP_HAVE_GSL)
1146 return qrPivotLapackGSL(Q, R, P, full, squareR, tol);
1147#else
1148 return qrPivotLapack(Q, R, P, full, squareR, tol);
1149#endif
1150#else
1151 (void)Q;
1152 (void)R;
1153 (void)P;
1154 (void)full;
1155 (void)squareR;
1156 (void)tol;
1157 throw(vpException(vpException::fatalError, "Cannot perform QR decomposition. Install Lapack 3rd party"));
1158#endif
1159}
1160
1173{
1174 if ((rowNum != colNum) || (rowNum == 0)) {
1175 throw(vpException(vpException::dimensionError, "Cannot inverse a triangular matrix (%d, %d) that is not square",
1176 rowNum, colNum));
1177 }
1178#if defined(VISP_HAVE_LAPACK)
1179#if defined(VISP_HAVE_GSL)
1180 vpMatrix inv;
1181 inv.resize(colNum, rowNum, false);
1182 gsl_matrix gsl_inv;
1183
1184 gsl_inv.size1 = inv.rowNum;
1185 gsl_inv.size2 = inv.colNum;
1186 gsl_inv.tda = gsl_inv.size2;
1187 gsl_inv.data = inv.data;
1188 gsl_inv.owner = 0;
1189 gsl_inv.block = 0;
1190
1191 unsigned int tda = static_cast<unsigned int>(gsl_inv.tda);
1192 size_t len = sizeof(double) * inv.colNum;
1193 for (unsigned int i = 0; i < rowNum; ++i) {
1194 unsigned int k = i * tda;
1195 memcpy(&gsl_inv.data[k], (*this)[i], len);
1196 }
1197
1198 if (upper) {
1199#if (GSL_MAJOR_VERSION >= 2 && GSL_MINOR_VERSION >= 2)
1200 gsl_linalg_tri_upper_invert(&gsl_inv);
1201#else
1202 {
1203 gsl_matrix_view m;
1204 gsl_vector_view v;
1205 for (unsigned int i = 0; i < rowNum; ++i) {
1206 double *Tii = gsl_matrix_ptr(&gsl_inv, i, i);
1207 *Tii = 1.0 / *Tii;
1208 double aii = -(*Tii);
1209 if (i > 0) {
1210 m = gsl_matrix_submatrix(&gsl_inv, 0, 0, i, i);
1211 v = gsl_matrix_subcolumn(&gsl_inv, i, 0, i);
1212 gsl_blas_dtrmv(CblasUpper, CblasNoTrans, CblasNonUnit, &m.matrix, &v.vector);
1213 gsl_blas_dscal(aii, &v.vector);
1214 }
1215 }
1216 }
1217#endif
1218 }
1219 else {
1220#if (GSL_MAJOR_VERSION >= 2 && GSL_MINOR_VERSION >= 2)
1221 gsl_linalg_tri_lower_invert(&gsl_inv);
1222#else
1223 {
1224 gsl_matrix_view m;
1225 gsl_vector_view v;
1226 for (unsigned int i = 0; i < rowNum; ++i) {
1227 size_t j = rowNum - i - 1;
1228 double *Tjj = gsl_matrix_ptr(&gsl_inv, j, j);
1229 *Tjj = 1.0 / (*Tjj);
1230 double ajj = -(*Tjj);
1231 if (j < rowNum - 1) {
1232 m = gsl_matrix_submatrix(&gsl_inv, j + 1, j + 1, rowNum - j - 1, rowNum - j - 1);
1233 v = gsl_matrix_subcolumn(&gsl_inv, j, j + 1, rowNum - j - 1);
1234 gsl_blas_dtrmv(CblasLower, CblasNoTrans, CblasNonUnit, &m.matrix, &v.vector);
1235 gsl_blas_dscal(ajj, &v.vector);
1236 }
1237 }
1238 }
1239#endif
1240 }
1241
1242 return inv;
1243#else
1244 integer n = (integer)rowNum; // lda is the number of rows because we don't use a submatrix
1245
1246 vpMatrix R = *this;
1247 integer info;
1248
1249 // we just use the other (upper / lower) method from Lapack
1250 if (rowNum > 1 && upper) // upper triangular
1251 dtrtri_((char *)"L", (char *)"N", &n, R.data, &n, &info);
1252 else
1253 dtrtri_((char *)"U", (char *)"N", &n, R.data, &n, &info);
1254
1255 if (info != 0) {
1256 if (info < 0)
1257 std::cout << "dtrtri_:" << -info << "th element had an illegal value" << std::endl;
1258 else if (info > 0) {
1259 std::cout << "dtrtri_:R(" << info << "," << info << ")"
1260 << " is exactly zero. The triangular matrix is singular "
1261 "and its inverse can not be computed."
1262 << std::endl;
1264 }
1266 }
1267 return R;
1268#endif
1269#else
1270 (void)upper;
1271 throw(vpException(vpException::fatalError, "Cannot perform triangular inverse. Install Lapack 3rd party"));
1272#endif
1273}
1274
1323{
1324 vpMatrix Q, R, P;
1325 unsigned int r = t().qrPivot(Q, R, P, false, true);
1326 x = Q.extract(0, 0, colNum, r) * R.inverseTriangular().t() * P * b;
1327}
1328
1378{
1380 solveByQR(b, x);
1381 return x;
1382}
1383END_VISP_NAMESPACE
unsigned int getCols() const
Definition vpArray2D.h:423
Type * data
Address of the first element of the data array.
Definition vpArray2D.h:149
void resize(unsigned int nrows, unsigned int ncols, bool flagNullify=true, bool recopy_=true)
Definition vpArray2D.h:448
unsigned int rowNum
Definition vpArray2D.h:1201
unsigned int getRows() const
Definition vpArray2D.h:433
unsigned int colNum
Definition vpArray2D.h:1203
Implementation of column vector and the associated operations.
error that can be emitted by ViSP classes.
Definition vpException.h:60
@ badValue
Used to indicate that a value is not in the allowed range.
Definition vpException.h:73
@ dimensionError
Bad dimension.
Definition vpException.h:71
@ fatalError
Fatal error.
Definition vpException.h:72
error that can be emitted by the vpMatrix class and its derivatives
@ rankDeficient
Rank deficient.
@ matrixError
Matrix operation error.
Implementation of a matrix and operations on matrices.
Definition vpMatrix.h:175
unsigned int qr(vpMatrix &Q, vpMatrix &R, bool full=false, bool squareR=false, double tol=1e-6) const
vpMatrix inverseTriangular(bool upper=true) const
unsigned int qrPivot(vpMatrix &Q, vpMatrix &R, vpMatrix &P, bool full=false, bool squareR=false, double tol=1e-6) const
vpMatrix inverseByQRLapack() const
void solveByQR(const vpColVector &b, vpColVector &x) const
vpMatrix inverseByQR() const
vpMatrix t() const