STK++ 0.9.13
STK_Svd.h
Go to the documentation of this file.
1/*--------------------------------------------------------------------*/
2/* Copyright (C) 2004-2016 Serge Iovleff, Université Lille 1, Inria
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with this program; if not, write to the
16 Free Software Foundation, Inc.,
17 59 Temple Place,
18 Suite 330,
19 Boston, MA 02111-1307
20 USA
21
22 Contact : S..._Dot_I..._At_stkpp_Dot_org (see copyright for ...)
23*/
24
25/*
26 * Project: stkpp::Algebra
27 * Purpose: Define The Svd Class.
28 * Author: Serge Iovleff, S..._Dot_I..._At_stkpp_Dot_org (see copyright for ...)
29 *
30 **/
31
36#ifndef STK_SVD_H
37#define STK_SVD_H
38
43
44#include "STK_ISvd.h"
45#include "STK_Householder.h"
46#include "STK_Givens.h"
47
48
49#define MAX_ITER 30
50
51namespace STK
52{
53// forward declaration
54template<class Array> class Svd;
55
56namespace hidden
57{
61template<class Array_>
63{
64 typedef Array_ ArrayU;
67};
68
69} // namespace hidden
70
79template<class Array>
80static Real bidiag(const Array& M, ArrayDiagonalX& D, VectorX& F);
89template<class Array>
90static void leftEliminate( ArrayDiagonalX& D
91 , VectorX& F
92 , int const& nrow
93 , Array& U
94 , bool withU = true
96 );
115template<class Array>
116class Svd: public ISvd<Svd<Array> >
117{
118 public :
122 using Base::U_;
123 using Base::D_;
124 using Base::V_;
125 using Base::withU_;
126 using Base::withV_;
127 using Base::nrowU;
128 using Base::ncolU;
129 using Base::ncolV;
130 using Base::norm_;
131 using Base::rank_;
132
139 inline Svd( Array const& A, bool ref= false, bool withU= true, bool withV= true)
140 : Base(A, ref, withU, withV)
141 {}
147 template<class OtherArray>
148 inline Svd( ArrayBase<OtherArray> const& A, bool withU = true, bool withV = true)
149 : Base(A, withU, withV) {}
153 Svd( const Svd &S);
155 inline virtual ~Svd() {}
159 Svd& operator=(const Svd &S);
161 bool runImpl();
171 static bool diag( ArrayDiagonalX& D
172 , VectorX& F
173 , Array& U
174 , ArraySquareX& V
175 , bool withU = true
176 , bool withV = true
178 );
187 static void rightEliminate( ArrayDiagonalX& D
188 , VectorX& F
189 , int const& nrow
190 , ArraySquareX& V
191 , bool withV = true
193 );
194 private:
198 bool computeSvd();
200 void compU();
202 void compV();
203};
204
205
206/* Copy constructor */
207template<class Array>
208Svd<Array>::Svd( const Svd &S): Base(S), F_(S.F_) {}
209
210/* Operator = : overwrite the Svd with S.*/
211template<class Array>
213{
214 U_ = S.U_;
215 V_ = S.V_;
216 D_ = S.D_;
217 withU_ = S.withU_;
218 withV_ = S.withV_;
219 norm_ = S.norm_;
220 rank_ = S.rank_;
221 return *this;
222}
223
224/* run the Svd */
225template<class Array>
227{
228 if (!computeSvd()) return false;
229 return true;
230}
231
232
233/* Main method for the svd computation. */
234template<class Array>
236{
237 // if the container is empty, there is nothing to do
238 if (U_.empty())
239 { rank_ = 0;
240 norm_ = 0.0;
241 return true;
242 }
243 int beginRow = U_.beginRows(), beginCol = U_.beginCols();
244 // if U_ is just a copy of A, translate begin to 1
245 // if U_ is a ref on A, this can generate an error
246 U_.shift(1,1);
247 // Bidiagonalize (U_)
248 norm_ = bidiag(U_, D_, F_);
249 // right householder vectors are in upper part of U_
250 // We need to create V_ before rightEliminate
251 if (withV_) { compV();}
252 // rightEliminate last element of F_ if any
253 if (nrowU() < ncolV())
254 { rightEliminate(D_, F_, nrowU(), V_, withV_, norm_);}
255 // If (U_) is not needed, we can destroy the storage
256 if (withU_) { compU();}
257 // Diagonalize
258 bool error = diag(D_, F_, U_, V_, withU_, withV_, norm_);
259 // The sub diagonal is now zero
260 F_.resize(0,0);
261 U_.shift(beginRow, beginCol);
262 D_.shift(beginCol);
263 V_.shift(beginCol);
264 return error;
265}
266
267
268/* Bidiagonalization of the matrix M. */
269template<class Array>
270Real bidiag(const Array& M, ArrayDiagonalX& D, VectorX& F)
271{
272 typedef typename hidden::Traits<Array>::Col ColVector;
273 typedef typename hidden::Traits<Array>::Row RowVector;
274 // norm of the matrix M
275 Real norm = 0.0;
276 // compute the number of iteration
277 int begin_iter = M.beginCols();
278 int last_iter = M.beginCols() + std::min(M.sizeCols(), M.sizeRows()) -1;
279 // Diagonal values
281 // Upper diagonal values
282 F.resize(Range(begin_iter-1, last_iter, 0));
283 F.front() = 0.0;
284 // Bidiagonalization of M
285 // loop on the cols and rows
286 Range rowRange0(M.rows())
287 , rowRange1(Range(M.beginRows()+1, M.lastIdxRows(), 0))
288 , colRange1(Range(M.beginCols()+1, M.lastIdxCols(), 0));
289 for ( int iter=begin_iter ; iter<=last_iter
290 ; iter++
291 , rowRange0.incFirst(1)
292 , rowRange1.incFirst(1)
293 , colRange1.incFirst(1)
294 )
295 {
296 // reference on the current column iter
297 ColVector X( M, rowRange0, iter);
298 // Left Householder
299 D[iter] = house(X);
300 // apply Householder to next cols
302 // check if there is a row
303 if ((iter < last_iter)||(M.sizeCols()>M.sizeRows()))
304 {
305 // ref on the current row iter
306 RowVector P(M, colRange1, iter);
307 // Right Householder
308 F[iter] = house(P);
309 // apply Householder to next rows
311 }
312 else
313 F[iter] = 0.0;
314 // Estimation of the norm of M
315 norm = std::max(std::abs(D[iter])+std::abs(F[iter]), norm);
316 }
317 // return estimated norm
318 return norm;
319}
320
321
322/* computation of V_ */
323template<class Array>
325{
326 // Construction of V_
327 V_.resize(U_.cols());
328 // Number of right Householder rotations
329 int niter = (ncolV()>nrowU()) ? (nrowU()) : (ncolV()-1);
330 // initialization of the remaining rows and cols of V_ to Identity
331 for (int iter=niter+2; iter<=ncolV(); iter++)
332 {
333 VectorX W(V_, V_.cols(), iter);
334 W = 0.0;
335 W[iter] = 1.0;
336 }
337
338 Range range1(niter+1, ncolV(), 0), range2(niter+2, ncolV(), 0);
339 for ( int iter0=niter, iter1=niter+1, iter2=niter+2; iter0>=1
340 ; iter0--, iter1--, iter2--
341 , range1.decFirst(1), range2.decFirst(1)
342 )
343 {
344 // get beta and test
345 Real beta = U_(iter0, iter1);
346 if (beta)
347 {
348 // ref on the row iter1 of V_
349 PointX Vrow1(V_, range1, iter1);
350 // diagonal element
351 Vrow1[iter1] = 1.0+beta;
352 // ref on the column iter1
354 // get the Householder vector
355 Vcol1 = RowVector(U_, range2, iter0);
356 // Apply housholder to next cols
357 for (int j=iter2; j<=ncolV(); j++)
358 {
359 Real aux;
360 // ref on the column j
361 VectorX Vcolj( V_, range2, j);
362 // update column j
363 Vrow1[j] = (aux = dot(Vcol1, Vcolj) * beta);
364 for (int i= iter2; i <= ncolV(); ++i)
365 { Vcolj[i] += Vcol1[i] * aux;}
366 }
367 // compute the Householder vector
368 Vcol1 *= beta;
369 }
370 else // nothing to do
371 {
372 V_(range2, iter1) = 0.0;
373 V_(iter1, iter1) = 1.0;
374 V_(iter1, range2) = 0.0;
375 }
376 }
377 // First column and rows
378 V_(1,1) =1.0;
379 V_(Range(2,ncolV(), 0),1) =0.0;
380 V_(1,Range(2,ncolV(), 0)) =0.0;
381}
382
383
384/* computation of U_ */
385template<class Array>
387{
388 int niter = D_.size(); // Number of iterations
389 int ncol = std::min(nrowU(), ncolU()); // number of non zero cols of U_
390
391 // initialization of the remaining cols of U_ to 0.0
392 // put 0 to unused cols
393 U_.col(Range(ncol+1, ncolU(), 0)) = 0.0;
394 // Computation of U_
395 for (int iter=niter, iter1=niter+1; iter>=1; iter--, iter1--)
396 {
397 // ref of the column iter
398 ColVector X(U_, Range(iter1,nrowU(), 0), iter);
399 // ref of the row iter
400 RowVector P(U_, Range(iter,ncolU(), 0), iter);
401 // Get beta and test
402 Real beta = P[iter];
403 if (beta)
404 {
405 // update the column iter
406 P[iter] = 1.0 + beta;
407 // Updating the cols iter+1 to ncolU_
408 for (int j=iter1; j<=niter; j++)
409 { // product of U_iter by U_j
410 Real aux;
411 ColVector Y(U_, Range(iter1, nrowU(), 0), j); // ref on the column j
412 // U_j = aux = beta * X'Y
413 P[j] = (aux = dot( X, Y) *beta);
414 // U^j += aux * U^iter
415 for (int i= iter1; i <= nrowU(); ++i)
416 {
417 Y[i] += X[i] * aux;
418 }
419 }
420 // compute the vector v
421 X *= beta;
422 }
423 else // U^iter = identity
424 {
425 P[iter] = 1.0;
426 X = 0.0;
427 }
428 // update the column iter
429 U_.col(Range(1,iter-1, 0), iter) = 0.0;
430 }
431}
432
433
434/* eliminate the element of the subdiagonal with right rotations */
435template<class Array>
437 , ArraySquareX& V, bool withV, Real const& tol
438 )
439{
440 // the element to eliminate
441 Real z = F[nrow];
442 // if the element is not 0.0
443 if (std::abs(z)+tol != tol)
444 {
445 // column of the element to eliminate
446 int ncol1 = nrow+1;
447 // begin the Givens rotations
448 for (int k=nrow, k1=nrow-1; k>=1 ; k--, k1--)
449 {
450 // compute and apply Givens rotation to the rows (k, k+1)
452 Real y = D[k];
453 D[k] = (aux = norm(y,z));
454 z = (sinus = -z/aux) * F[k1];
455 F[k1] *= (cosinus = y/aux);
456 // Update V_
457 if (withV)
459 // if 0.0 we can break now
460 if (std::abs(z)+tol == tol) break;
461 }
462 }
463 // the element is now 0
464 F[nrow] = 0.0; // is 0.0
465}
466
467
468/* eliminate the element of the subdiagonal with left rotations */
469template<class Array>
470void leftEliminate( ArrayDiagonalX& D, VectorX& F
471 , int const& nrow
472 , Array& U
473 , bool withU
474 , Real const& tol
475 )
476{
477 //the element to eliminate
478 Real z = F[nrow];
479 // if the element is not 0.0
480 if (std::abs(z)+tol != tol)
481 {
482 // begin the Givens rotations
483 for (int k=nrow+1; k <D.end(); k++)
484 {
485 // compute and apply Givens rotation to the rows (nrow, k)
486 Real y = D[k];
488 D[k] = (aux = norm(y,z));
489 z = (sinus = -z/aux) * F[k];
490 F[k] *= (cosinus = y/aux);
491 // Update U_
492 if (withU)
493 rightGivens(U, nrow, k, cosinus, sinus);
494 if (std::abs(z)+tol == tol) break;
495 }
496 }
497 F[nrow] = 0.0;
498}
499
500
501/* diagonalization of the bidiag matrix */
502template<class Array>
504 , VectorX& F
505 , Array& U
506 , ArraySquareX& V
507 , bool withU
508 , bool withV
509 , Real const& tol
510 )
511{
512 // result of the diag process
513 bool error = false;
514 // Diagonalization of A : Reduction of la matrice bidiagonale
515 for (int end=D.lastIdx(); end>=D.begin(); --end)
516 { // 30 iter max
517 int iter;
518 for (iter=1; iter<=MAX_ITER; iter++)
519 { // if the last element of the subdiagonale is 0.0
520 // stop the iterations
521 int beg;
522 if (std::abs(F[end-1])+tol == tol) { F[end-1] = 0.0; break;}
523 // now F[end-1] !=0
524 // if D[end] == 0, we can annulate F[end-1]
525 // with rotations of the columns.
526 if (std::abs(D[end])+tol == tol)
527 {
528 D[end] = 0.0;
529 rightEliminate(D, F, end-1, V, withV, tol);
530 break; // Last element of the subdiagonal is 0
531 }
532 // now D[end] != 0 and F[end-1] != 0
533 // look for the greatest matrix such that all the elements
534 // of the diagonal and subdiagonal are not zeros
535 for (beg = end-1; beg>D.begin(); --beg)
536 {
537 if ((std::abs(D[beg])+tol == tol)||(std::abs(F[beg])+tol == tol))
538 break;
539 }
540 // now F[beg-1]!=0
541 // if D[beg] == 0 and F[beg] != 0,
542 // we can eliminate the element F[beg]
543 // with rotations of the rows
544 if ((std::abs(D[beg])+tol == tol) && (std::abs(F[beg])+tol != tol))
545 {
546 D[beg] = 0.0;
547 leftEliminate(D, F, beg, U, withU, tol);
548 }
549
550 // Si F[beg]==0, on augmente beg
551 if (std::abs(F[beg])+tol == tol) { F[beg] = 0.0; beg++;}
552
553 // On peut commencer les rotations QR entre les lignes beg et end
554 // Shift computation
555 // easy shift : commented
556 // Real aux = norm(D[end],F[end-1]);
557 // Real y = (D[beg]+aux)*(D[beg]-aux);
558 // Real z = D[beg]*F[beg];
559 // Wilkinson shift : look at the doc
560 Real dd1 = D[end-1]; // d_1
561 Real dd2 = D[end]; // d_2
562 Real ff1 = F[end-2]; // f_1
563 Real ff2 = F[end-1]; // f_2
564 // g
565 Real aux = ( (dd1+dd2)*(dd1-dd2)
566 + (ff1-ff2)*(ff1+ff2))/(2*dd1*ff2);
567 // A - mu
568 Real d1 = D[beg];
569 Real f1 = F[beg];
570 Real y = (d1+dd2)*(d1-dd2)
571 + ff2*(dd1/(aux+sign(aux,norm(1.0,aux)))- ff2);
572 Real z = d1*f1;
573 // chase no null element
574 int k, k1;
575 for (k=beg, k1 = beg+1; k<end; ++k, ++k1)
576 { // Rotation colonnes (k,k+1)
577 // Input : d1 contient D[k],
578 // f1 contient F[k],
579 // y contient F[k-1]
580 // Output : d1 contient F[k],
581 // d2 contient D[k+1],
582 // y contient D[k]
583 Real cosinus=1., sinus=0.;
584 Real d2 = D[k1];
585 F[k-1] = (aux = norm(y,z)); // F[k-1]
586 // arbitrary rotation if y = z = 0.0
587 if (aux)
588 y = (cosinus = y/aux) * d1 - (sinus = -z/aux) * f1; // D[k]
589 else
590 y = cosinus * d1 - sinus * f1; // D[k]
591
592 z = -sinus * d2; // z
593 d1 = sinus * d1 + cosinus * f1; // F[k]
594 d2 *= cosinus; // D[k+1]
595 // Update V
596 if (withV)
597 rightGivens(V, k1, k, cosinus, sinus);
598 // avoid underflow
599 // Rotation lignes (k,k+1)
600 // Input : d1 contient F[k],
601 // d2 contient D[k+1],
602 // y contient D[k]
603 // Output : d1 contient D[k+1],
604 // f1 contient F[k+1],
605 // y contient F[k]
606 f1 = F[k1];
607 D[k] = (aux = norm(y,z)); // D[k]
608 // arbitrary rotation if y = z = 0.0
609 if (aux)
610 y = (cosinus = y/aux) * d1 - (sinus = -z/aux) * d2; // F[k]
611 else
612 y = cosinus * d1 - sinus * d2; // F[k]
613
614 z = -sinus * f1; // z
615 d1 = sinus *d1 + cosinus * d2; // D[k+1]
616 f1 *= cosinus; // F[k+1]
617 // Update U
618 if (withU)
619 rightGivens(U, k1, k, cosinus, sinus);
620 } // end of the QR updating iteration
621 D[end] = d1;
622 F[end-1] = y;
623 F[beg-1] = 0.0; // F[beg-1] is overwritten, we have to set 0.0
624 } // iter
625 // too many iterations
626 if (iter >= 30) { error = true;}
627 // positive singular value only
628 if (D[end]< 0.0)
629 {
630 // change sign of the singular value
631 D[end]= -D[end];
632 // change sign of the column end of V
633 if (withV)
634 {
635 for (int i= V.beginRows(); i <= V.lastIdxRows(); ++i)
636 { V(i,end) = -V(i,end);}
637 }
638 }
639
640 // We have to sort the singular value : we use a basic strategy
641 Real z = D[end]; // current value
642 for (int i=end+1; i<=D.lastIdx(); i++)
643 { if (D[i]> z) // if the ith singular value is greater
644 { D.swap(i-1, i); // swap the cols
645 if (withU) U.swapCols(i-1, i);
646 if (withV) V.swapCols(i-1, i);
647 }
648 else break;
649 } // end sort
650 } // boucle end
651 return error;
652}
653
654} // namespace STK
655
656#undef MAX_ITER
657
658#endif
659// STK_SVD_H
In this file, we define Array2DDiagonal class.
In this file, we define Array2DSquare class.
A Array2DVector is a one dimensional horizontal container.
In this file we implement the functors for performing operations on Array2D arrays.
#define d2(z)
#define d1(z)
In this file we define Givens methods used by the Algebra classes.
In this file we define the Housholder methods used by the Algebra classes.
In this file we define the interface class ISvd.
#define MAX_ITER
Definition STK_Svd.h:49
int beginRows() const
void swap(int i, int j)
swap two elements: only for vectors and points
Derived & resize(Range const &I, Range const &J)
resize the array.
int lastIdxRows() const
void swapCols(int pos1, int pos2)
Swapping two columns.
String const & error() const
get the last error message.
Definition STK_IRunner.h:82
Compute the Singular Value Decomposition of an array.
Definition STK_ISvd.h:61
bool withU_
Compute U_ ?
Definition STK_ISvd.h:195
Type norm_
trace norm
Definition STK_ISvd.h:199
ArrayD const & D() const
Definition STK_ISvd.h:152
ArrayD D_
Diagonal array of the singular values.
Definition STK_ISvd.h:193
ArrayV V_
V_ matrix.
Definition STK_ISvd.h:191
ArrayU U_
U_ matrix.
Definition STK_ISvd.h:189
ArrayU const & U() const
Definition STK_ISvd.h:148
bool withV_
Compute V_ ?
Definition STK_ISvd.h:197
ArrayV const & V() const
Definition STK_ISvd.h:150
The MultidimRegression class allows to regress a multidimensional output variable among a multivariat...
The class Svd compute the Singular Value Decomposition of a Array with the Golub-Reinsch Algorithm.
Definition STK_Svd.h:117
ISvd< Svd< Array > > Base
Definition STK_Svd.h:119
Svd(Array const &A, bool ref=false, bool withU=true, bool withV=true)
Default constructor.
Definition STK_Svd.h:139
hidden::Traits< Array >::Col ColVector
Definition STK_Svd.h:120
static bool diag(ArrayDiagonalX &D, VectorX &F, Array &U, ArraySquareX &V, bool withU=true, bool withV=true, Real const &tol=Arithmetic< Real >::epsilon())
Computing the diagonalization of a bi-diagonal matrix.
Definition STK_Svd.h:503
hidden::Traits< Array >::Row RowVector
Definition STK_Svd.h:121
bool computeSvd()
Svd main steps.
Definition STK_Svd.h:235
void compV()
Compute V (if withV_ is true)
Definition STK_Svd.h:324
void compU()
Compute U (if withU_ is true)
Definition STK_Svd.h:386
VectorX F_
Values of the Sub-diagonal.
Definition STK_Svd.h:196
Svd(ArrayBase< OtherArray > const &A, bool withU=true, bool withV=true)
constructor with other kind of array/expression
Definition STK_Svd.h:148
bool runImpl()
run the Svd
Definition STK_Svd.h:226
Svd & operator=(const Svd &S)
Operator = : overwrite the Svd with S.
Definition STK_Svd.h:212
virtual ~Svd()
destructor.
Definition STK_Svd.h:155
static void rightEliminate(ArrayDiagonalX &D, VectorX &F, int const &nrow, ArraySquareX &V, bool withV=true, Real const &tol=Arithmetic< Real >::epsilon())
right eliminate the element on the subdiagonal of the row nrow
Definition STK_Svd.h:436
Index sub-vector region: Specialization when the size is unknown.
Definition STK_Range.h:265
void rightGivens(ArrayBase< TContainer2D > &M, int j1, int j2, typename TContainer2D::Type const &cosinus, typename TContainer2D::Type const &sinus)
Apply Givens rotation.
Definition STK_Givens.h:119
void applyLeftHouseholderVector(ArrayBase< Lhs > const &M, ExprBase< Rhs > const &v)
left multiplication by a Householder vector.
void applyRightHouseholderVector(ArrayBase< Lhs > const &M, ExprBase< Rhs > const &v)
right multiplication by a Householder vector.
Real house(ArrayBase< Vector > &x)
Compute the Householder vector v of a vector x.
Real dot(ExprBase< Container1D1 > const &x, ExprBase< Container1D2 > const &y)
Compute the dot product.
Type sign(Type const &x, Type const &y=Type(1))
template sign value sign(x) * y: Type should be an integral type
Definition STK_Misc.h:53
Real norm(Real const &x, Real const &y)
Computation of sqrt(x^2 + y^2) without underflow or overflow.
Definition STK_Misc.h:93
double Real
STK fundamental type of Real values.
The namespace STK is the main domain space of the Statistical ToolKit project.
TRange< UnknownSize > Range
Definition STK_Range.h:59
Arithmetic properties of STK fundamental types.
traits class for the algebra methods.