STK++ 0.9.13
STK_SymEigen.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 SymEigen Class.
28 * Author: Serge Iovleff, S..._Dot_I..._At_stkpp_Dot_org (see copyright for ...)
29 **/
30
35#ifndef STK_SYMEIGEN_H
36#define STK_SYMEIGEN_H
37
38#include "STK_ISymEigen.h"
39#include "Arrays/include/STK_Array2DVector.h" // for F_
40#include "STK_Householder.h"
41#include "STK_Givens.h"
42
43#define MAXITER 30
44
45namespace STK
46{
47// forward declaration
48template<class SquareArray> class SymEigen;
49
50namespace hidden
51{
56template<class SquareArray_>
61
62} // namespace hidden
63
84template<class SquareArray>
85class SymEigen: public ISymEigen<SymEigen<SquareArray> >
86{
87 public:
91 using Base::range_;
92 using Base::norm_;
93 using Base::rank_;
94 using Base::det_;
95
103 SymEigen( SquareArray const& data, bool ref =false);
107 template<class Derived>
114 virtual ~SymEigen() {}
115
119 bool runImpl();
124 inline SymEigen& operator=(const SymEigen &S)
125 {
127 return *this;
128 }
129
130 private:
136 void compHouse();
139};
140
141
142/* @brief Default Constructor */
143template<class SquareArray>
145/* @brief Constructor
146 * @param data reference on a symmetric square matrix
147 * @param ref @c true if we overwrite the data set, @c false otherwise
148 */
149template<class SquareArray>
150SymEigen<SquareArray>::SymEigen( SquareArray const& data, bool ref)
151 : Base(data)
152{}
153/* @brief Constructor
154 * @param data reference on a symmetric square matrix
155 * @param ref @c true if we overwrite the data set, @c false otherwise
156 */
157template<>
158inline SymEigen<CSquareX>::SymEigen( CSquareX const& data, bool ref)
159 : Base(data, ref)
160{}
161
162/* constructor.
163 * @param data A reference on the symmetric matrix to decompose.
164 **/
165template<class SquareArray>
166template<class Derived>
168/* Copy constructor.
169* @param S the EigenValue to copy
170**/
171template<class SquareArray>
173
174/* Main methods. */
175/* Compute diagonalization of the symmetric matrix */
176template<class SquareArray>
178{
179#ifdef STK_ALGEBRA_VERBOSE
180 stk_cout << _T("Entering in SymEigen::run()\n");
181#endif
182 try
183 {
184 // copy data
185 eigenValues_.resize(eigenVectors_.range());
186 norm_ = 0.;
187 rank_ = 0;
188 det_ = 0.;
189#ifdef STK_ALGEBRA_VERBOSE
190 stk_cout << _T("calling SymEigen::tridiagonalize()\n");
191#endif
192 // tridiagonalize eigenVectors_
193 tridiagonalize();
194#ifdef STK_ALGEBRA_VERBOSE
195 stk_cout << _T("calling SymEigen::compHouse()\n");
196#endif
197 // compute eigenVectors_
198 compHouse();
199#ifdef STK_ALGEBRA_VERBOSE
200 stk_cout << _T("calling SymEigen::diagonalize()\n");
201#endif
202 // Diagonalize
203 diagonalize();
204#ifdef STK_ALGEBRA_VERBOSE
205 stk_cout << _T("calling SymEigen::finalize()\n");
206#endif
207 // compute rank, norm and determinant
208 this->finalizeStep();
209 }
210 catch (Exception const& e)
211 {
212 this->msg_error_ = e.error();
213 return false;
214 }
215 return true;
216}
217
218/* tridiagonalization of the symmetric matrix eigenVectors_. Only the lower
219 * part of eigenVectors_ used. eigenVectors_ is overwritten with the Householder
220 * vectors. eigenValues_ contains the diagonal.
221 **/
222template<class SquareArray>
224{
225 // Upper diagonal values
226 F_.resize(Range(range_.begin()-1, range_.lastIdx(), 0));
227 F_.front() = 0.0; F_.back() =0.0;
228 // initial range of the Householder vectors
229 Range range1(Range(range_.begin()+1, range_.lastIdx(), 0));
230 Range range2(Range(range_.begin()+2, range_.lastIdx(), 0));
231 // Bidiagonalization of eigenVectors_
232 // loop on the cols and rows
233 for ( int i=range_.begin(), i1=range_.begin()+1, i2=range_.begin()+2; i<range_.lastIdx()
234 ; i++, i1++, i2++, range1.incFirst(1), range2.incFirst(1)
235 )
236 {
237 // ref on the current column iter in the range iter1:last
238 typename hidden::Traits<CSquareX>::Col v(eigenVectors_.col(range1, i));
239 // Compute Householder vector and get subdiagonal element
240 F_[i] = house(v);
241 // Save diagonal element
242 eigenValues_[i] = eigenVectors_(i,i);
243 // get beta
244 Real beta = v.front();
245 if (beta)
246 {
247 // ref on the current column iter1 in the range iter1:last
248 typename hidden::Traits<CSquareX>::Col M1(eigenVectors_.col(range1, i1));
249 // aux1 will contain <v,p>
250 Real aux1 = 0.0;
251 // apply left and right Householder to eigenVectors_
252 // compute D(iter1:range_.last_) = p and aux1 = <p,v>
253 // Computation of p_iter1 = beta * eigenVectors_ v using the lower part of eigenVectors_
254 // save p_iter1 in the unused part of eigenValues_ and compute p'v
255 aux1 += (eigenValues_[i1] = beta*(M1[i1] + M1.sub(range2).dot(v.sub(range2)))) /* *1.0 */;
256 // other cols
257 for (int k=i2; k<range_.end(); k++)
258 {
259 // Computation of p_i = beta * eigenVectors_ v using the lower part of eigenVectors_
260 // save p_i in the unusued part of eigenValues_ and compute p'v
261 Real aux = M1[k] /* *1.0 */;
262 for (int j=i2; j<=k; j++) { aux += eigenVectors_(k,j)*v[j];}
263 for (int j=k+1; j<range_.end(); j++) { aux += eigenVectors_(j,k)*v[j];}
264 // save p_i in the unusued part of eigenValues_ and compute p'v
265 aux1 += (eigenValues_[k] = beta*aux) * v[k];
266 }
267 // update diagonal element M_ii+= 2 v_i * q_i = 2* q_i (i=iter1)
268 // aux = q_iter1 and aux1 = beta*<p,v>/2 (we don't save aux in eigenValues_)
269 Real aux = (eigenValues_[i1] + (aux1 *= (beta/2.0)));
270 M1[i1] += 2.0*aux;
271 // update lower part: all rows
272 // compute q_i and update the lower part of eigenVectors_
273 for (int k=i2; k<range_.end(); k++)
274 {
275 // get q_i and save it in eigenValues_i=q_i = p_i + <p,v> * beta * v_i/2
276 eigenValues_[k] += aux1 * v[k];
277 // Compute eigenVectors_ + u q' + q u',
278 // update the row i, range_.begin() element
279 M1[k] += eigenValues_[k] /* *1.0 */+ v[k] * aux;
280 // update the row i: all cols under the diagonal
281 for (int j=i2; j<=k; j++)
282 eigenVectors_(k,j) += v[j] * eigenValues_[k] + v[k] * eigenValues_[j];
283 }
284 }
285 }
286 // Last col
287 eigenValues_[range_.lastIdx()] = eigenVectors_(range_.lastIdx(),range_.lastIdx());
288}
289
290// Compute eigenVectors_ from the Householder rotations
291template<class SquareArray>
293{
294 // compute eigenVectors_
295 // iter0 is the column of the Householder vector
296 // iter is the current column to compute
297 for ( int iter0=range_.lastIdx()-1, iter=range_.lastIdx(), iter1=range_.lastIdx()+1
298 ; iter0>=range_.begin()
299 ; iter0--, iter--, iter1--)
300 {
301 // reference on the Householder vector
302 typename hidden::Traits<CSquareX>::Col v(eigenVectors_.col(Range(iter, range_.lastIdx(), 0), iter0));
303 // Get Beta
304 Real beta = v[iter];
305 if (beta)
306 {
307 // Compute Col iter -> eigenVectors_ e_{iter}= e_{iter}+ beta v
308 eigenVectors_(iter, iter) = 1.0 + beta /* *1.0 */;
309 for (int i=iter1; i<range_.end(); i++)
310 { eigenVectors_(i, iter) = beta * v[i];}
311
312 // Update the other Cols
313 for (int i=iter1; i<range_.end(); i++)
314 {
315 // compute beta*<v, eigenVectors_^i>
316 Real aux = 0.0;
317 for (int j=iter1; j<range_.end(); j++)
318 { aux += eigenVectors_(j, i) * v[j]; }
319 aux *= beta;
320 // range_.begin() row (iter)
321 eigenVectors_(iter, i) = aux;
322 // other rows
323 for (int j=iter1; j<range_.end(); j++)
324 { eigenVectors_(j, i) += aux * v[j];}
325 }
326 }
327 else // beta = 0, nothing to do
328 { eigenVectors_(iter,iter)=1.0;
329 for (int j=iter1; j<range_.end(); j++)
330 { eigenVectors_(iter, j) =0.0; eigenVectors_(j, iter) = 0.0;}
331 }
332 }
333 // range_.begin() row and range_.begin() col
334 eigenVectors_(range_.begin(), range_.begin()) = 1.0;
335 for (int j=range_.begin()+1; j<range_.end(); j++)
336 { eigenVectors_(range_.begin(), j) = 0.0; eigenVectors_(j, range_.begin()) = 0.0;}
337}
338
339// diagonalize eigenValues_ and F_
340template<class SquareArray>
342{
343 // Diagonalisation of eigenVectors_
344 for (int iend=range_.lastIdx(); iend>=range_.begin()+1; iend--)
345 {
346 int iter;
347 for (iter=0; iter<MAXITER; iter++) // fix the number of iterations max
348 {
349 // check cv for the last element
350 Real sum = std::abs(eigenValues_[iend]) + std::abs(eigenValues_[iend-1]);
351 // if the first element of the small subdiagonal
352 // is 0. we stop the QR iterations and increment iend
353 if (std::abs(F_[iend-1])+sum == sum)
354 { F_[iend-1] = 0.0 ; break;}
355 // look for a single small subdiagonal element to split the matrix
356 int ibeg = iend-1;
357 while (ibeg>range_.begin())
358 {
359 ibeg--;
360 // if a subdiagonal is zero, we get a sub matrix unreduced
361 //sum = std::abs(eigenValues_[ibeg])+std::abs(eigenValues_[ibeg+1]);
362 if (std::abs(F_[ibeg])+std::abs(eigenValues_[ibeg]) == std::abs(eigenValues_[ibeg]))
363 { F_[ibeg] = 0.; ibeg++; break;}
364 };
365 // QR rotations between the rows/cols ibeg et iend
366 // Computation of the Wilkinson's shift
367 Real aux = (eigenValues_[iend-1] - eigenValues_[iend])/(2.0 * F_[iend-1]);
368 // initialisation of the matrix of rotation
369 // y is the current F_[k-1],
370 Real y = eigenValues_[ibeg]-eigenValues_[iend] + F_[iend-1]/sign(aux, STK::norm(aux,1.0));
371 // z is the element to annulate
372 Real z = F_[ibeg];
373 // Fk is the temporary F_[k]
374 Real Fk = z;
375 // temporary DeltaD(k)
376 Real DeltaDk = 0.0;
377 // Index of the columns
378 int k,k1;
379 // Givens rotation to restore tridiaonal form
380 for (k=ibeg, k1=ibeg+1; k<iend; k++, k1++)
381 {
382 // underflow ? we have a tridiagonal form exit now
383 if (z == 0.0) { F_[k]=Fk; break;}
384 // Rotation columns (k,k+1)
385 F_[k-1] = (aux = STK::norm(y,z)); // compute final F_[k-1]
386 // compute cosinus and sinus
387 Real cosinus = y/aux, sinus = -z/aux;
388 Real Dk = eigenValues_[k] + DeltaDk; // compute current D[k]
389 Real aux1 = 2.0 * cosinus * Fk + sinus * (Dk - eigenValues_[k1]);
390 // compute DeltaD(k+1)
391 eigenValues_[k] = Dk - (DeltaDk = sinus * aux1); // compute eigenValues_[k]
392 y = cosinus*aux1 - Fk; // temporary F_[k]
393 Fk = cosinus * F_[k1]; // temporary F_[k+1]
394 z = -sinus * F_[k1]; // temporary z
395 // update eigenVectors_
396 rightGivens(eigenVectors_, k1, k, cosinus, sinus);
397 }
398 // k=iend if z!=0 and k<iend if z==0
399 eigenValues_[k] += DeltaDk ; F_[k-1] = y;
400 // restore F[ibeg-1]
401 F_[ibeg-1] = 0.;
402 } // iter
403 if (iter == MAXITER)
404 { this->msg_error_ = _T("Warning, max iteration reached in SymEigen::diagonalize()\n");}
405 // We have to sort the eigenvalues : we use a basic strategy
406 Real z = eigenValues_[iend]; // current value
407 for (int i=iend+1; i<eigenValues_.end(); i++)
408 { if (eigenValues_[i] > z) // if the ith eigenvalue is greater
409 { eigenValues_.swap(i-1, i); // swap the cols
410 eigenVectors_.swapCols(i-1, i);
411 }
412 else break;
413 } // end sort
414 } // iend
415 // sort first value
416 Real z = eigenValues_[range_.begin()]; // current value
417 for (int i=range_.begin()+1; i<eigenValues_.end(); i++)
418 { if (eigenValues_[i] > z) // if the ith eigenvalue is greater
419 {
420 eigenValues_.swap(i-1, i); // swap the cols
421 eigenVectors_.swapCols(i-1, i);
422 }
423 else break;
424 } // end sort
425 // clear auxiliary array
426 F_.clear();
427}
428
429} // namespace STK
430
431#undef MAXITER
432
433#endif //STK_SYMEIGEN_H
A Array2DVector is a one dimensional horizontal container.
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 ISymEigen class (for a symmetric matrix).
#define MAXITER
#define stk_cout
Standard stk output stream.
#define _T(x)
Let x unmodified.
Sdk class for all library Exceptions.
virtual const String error() const
Returns a C-style character string describing the general cause of the current error.
virtual bool finalizeStep()
perform any computation needed after the call of the regression method.
String msg_error_
String with the last error message.
Definition STK_IRunner.h:96
The class ISymEigen is an interface class for the method computing the eigenvalue Decomposition of a ...
hidden::AlgebraTraits< Derived >::SquareArray SquareArray
Range range_
range of the original data set.
CArrayVector< Type, size_ > eigenValues_
Array of the eigenvalues.
ISymEigen & operator=(ISymEigen const &eigen)
Operator = : overwrite the ISymEigen with eigen.
CArraySquare< Type, size_ > eigenVectors_
Square matrix or the eigenvectors.
The MultidimRegression class allows to regress a multidimensional output variable among a multivariat...
The class SymEigen compute the eigenvalue Decomposition of a symmetric ArrayXX.
void tridiagonalize()
compute the tri-diagonalization of eigenVectors_
bool runImpl()
Diagonalization of eigenVectors_.
ISymEigen< SymEigen< SquareArray > > Base
SymEigen(SymEigen const &S)
Copy constructor.
virtual ~SymEigen()
virtual destructor
void compHouse()
compute the Householder matrix and P
SymEigen & operator=(const SymEigen &S)
Operator = : overwrite the SymEigen with S.
VectorX F_
Temporary vector.
SymEigen(ExprBase< Derived > const &data)
constructor.
SymEigen(SquareArray const &data, bool ref=false)
Constructor.
void diagonalize()
computing the diagonalization of eigenValues_ and F_
SymEigen()
Default Constructor.
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
Real house(ArrayBase< Vector > &x)
Compute the Householder vector v of a vector x.
Arrays::SumOp< Lhs, Rhs >::result_type sum(Lhs const &lhs, Rhs const &rhs)
convenience function for summing two arrays
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
traits class for the algebra methods.