1 // This file is part of Eigen, a lightweight C++ template library
2 // for linear algebra.
3 //
4 // We used the "A Divide-And-Conquer Algorithm for the Bidiagonal SVD"
5 // research report written by Ming Gu and Stanley C.Eisenstat
6 // The code variable names correspond to the names they used in their
7 // report
8 //
9 // Copyright (C) 2013 Gauthier Brun <brun.gauthier@gmail.com>
10 // Copyright (C) 2013 Nicolas Carre <nicolas.carre@ensimag.fr>
11 // Copyright (C) 2013 Jean Ceccato <jean.ceccato@ensimag.fr>
12 // Copyright (C) 2013 Pierre Zoppitelli <pierre.zoppitelli@ensimag.fr>
13 //
14 // Source Code Form is subject to the terms of the Mozilla
15 // Public License v. 2.0. If a copy of the MPL was not distributed
16 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
17
18 #ifndef EIGEN_BDCSVD_H
19 #define EIGEN_BDCSVD_H
20
21 #define EPSILON 0.0000000000000001
22
23 #define ALGOSWAP 32
24
25 namespace Eigen {
26 /** \ingroup SVD_Module
27 *
28 *
29 * \class BDCSVD
30 *
31 * \brief class Bidiagonal Divide and Conquer SVD
32 *
33 * \param MatrixType the type of the matrix of which we are computing the SVD decomposition
34 * We plan to have a very similar interface to JacobiSVD on this class.
35 * It should be used to speed up the calcul of SVD for big matrices.
36 */
37 template<typename _MatrixType>
38 class BDCSVD : public SVDBase<_MatrixType>
39 {
40 typedef SVDBase<_MatrixType> Base;
41
42 public:
43 using Base::rows;
44 using Base::cols;
45
46 typedef _MatrixType MatrixType;
47 typedef typename MatrixType::Scalar Scalar;
48 typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar;
49 typedef typename MatrixType::Index Index;
50 enum {
51 RowsAtCompileTime = MatrixType::RowsAtCompileTime,
52 ColsAtCompileTime = MatrixType::ColsAtCompileTime,
53 DiagSizeAtCompileTime = EIGEN_SIZE_MIN_PREFER_DYNAMIC(RowsAtCompileTime, ColsAtCompileTime),
54 MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
55 MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime,
56 MaxDiagSizeAtCompileTime = EIGEN_SIZE_MIN_PREFER_FIXED(MaxRowsAtCompileTime, MaxColsAtCompileTime),
57 MatrixOptions = MatrixType::Options
58 };
59
60 typedef Matrix<Scalar, RowsAtCompileTime, RowsAtCompileTime,
61 MatrixOptions, MaxRowsAtCompileTime, MaxRowsAtCompileTime>
62 MatrixUType;
63 typedef Matrix<Scalar, ColsAtCompileTime, ColsAtCompileTime,
64 MatrixOptions, MaxColsAtCompileTime, MaxColsAtCompileTime>
65 MatrixVType;
66 typedef typename internal::plain_diag_type<MatrixType, RealScalar>::type SingularValuesType;
67 typedef typename internal::plain_row_type<MatrixType>::type RowType;
68 typedef typename internal::plain_col_type<MatrixType>::type ColType;
69 typedef Matrix<Scalar, Dynamic, Dynamic> MatrixX;
70 typedef Matrix<RealScalar, Dynamic, Dynamic> MatrixXr;
71 typedef Matrix<RealScalar, Dynamic, 1> VectorType;
72
73 /** \brief Default Constructor.
74 *
75 * The default constructor is useful in cases in which the user intends to
76 * perform decompositions via BDCSVD::compute(const MatrixType&).
77 */
BDCSVD()78 BDCSVD()
79 : SVDBase<_MatrixType>::SVDBase(),
80 algoswap(ALGOSWAP)
81 {}
82
83
84 /** \brief Default Constructor with memory preallocation
85 *
86 * Like the default constructor but with preallocation of the internal data
87 * according to the specified problem size.
88 * \sa BDCSVD()
89 */
90 BDCSVD(Index rows, Index cols, unsigned int computationOptions = 0)
SVDBase()91 : SVDBase<_MatrixType>::SVDBase(),
92 algoswap(ALGOSWAP)
93 {
94 allocate(rows, cols, computationOptions);
95 }
96
97 /** \brief Constructor performing the decomposition of given matrix.
98 *
99 * \param matrix the matrix to decompose
100 * \param computationOptions optional parameter allowing to specify if you want full or thin U or V unitaries to be computed.
101 * By default, none is computed. This is a bit - field, the possible bits are #ComputeFullU, #ComputeThinU,
102 * #ComputeFullV, #ComputeThinV.
103 *
104 * Thin unitaries are only available if your matrix type has a Dynamic number of columns (for example MatrixXf). They also are not
105 * available with the (non - default) FullPivHouseholderQR preconditioner.
106 */
107 BDCSVD(const MatrixType& matrix, unsigned int computationOptions = 0)
SVDBase()108 : SVDBase<_MatrixType>::SVDBase(),
109 algoswap(ALGOSWAP)
110 {
111 compute(matrix, computationOptions);
112 }
113
~BDCSVD()114 ~BDCSVD()
115 {
116 }
117 /** \brief Method performing the decomposition of given matrix using custom options.
118 *
119 * \param matrix the matrix to decompose
120 * \param computationOptions optional parameter allowing to specify if you want full or thin U or V unitaries to be computed.
121 * By default, none is computed. This is a bit - field, the possible bits are #ComputeFullU, #ComputeThinU,
122 * #ComputeFullV, #ComputeThinV.
123 *
124 * Thin unitaries are only available if your matrix type has a Dynamic number of columns (for example MatrixXf). They also are not
125 * available with the (non - default) FullPivHouseholderQR preconditioner.
126 */
127 SVDBase<MatrixType>& compute(const MatrixType& matrix, unsigned int computationOptions);
128
129 /** \brief Method performing the decomposition of given matrix using current options.
130 *
131 * \param matrix the matrix to decompose
132 *
133 * This method uses the current \a computationOptions, as already passed to the constructor or to compute(const MatrixType&, unsigned int).
134 */
compute(const MatrixType & matrix)135 SVDBase<MatrixType>& compute(const MatrixType& matrix)
136 {
137 return compute(matrix, this->m_computationOptions);
138 }
139
setSwitchSize(int s)140 void setSwitchSize(int s)
141 {
142 eigen_assert(s>3 && "BDCSVD the size of the algo switch has to be greater than 4");
143 algoswap = s;
144 }
145
146
147 /** \returns a (least squares) solution of \f$ A x = b \f$ using the current SVD decomposition of A.
148 *
149 * \param b the right - hand - side of the equation to solve.
150 *
151 * \note Solving requires both U and V to be computed. Thin U and V are enough, there is no need for full U or V.
152 *
153 * \note SVD solving is implicitly least - squares. Thus, this method serves both purposes of exact solving and least - squares solving.
154 * In other words, the returned solution is guaranteed to minimize the Euclidean norm \f$ \Vert A x - b \Vert \f$.
155 */
156 template<typename Rhs>
157 inline const internal::solve_retval<BDCSVD, Rhs>
solve(const MatrixBase<Rhs> & b)158 solve(const MatrixBase<Rhs>& b) const
159 {
160 eigen_assert(this->m_isInitialized && "BDCSVD is not initialized.");
161 eigen_assert(SVDBase<_MatrixType>::computeU() && SVDBase<_MatrixType>::computeV() &&
162 "BDCSVD::solve() requires both unitaries U and V to be computed (thin unitaries suffice).");
163 return internal::solve_retval<BDCSVD, Rhs>(*this, b.derived());
164 }
165
166
matrixU()167 const MatrixUType& matrixU() const
168 {
169 eigen_assert(this->m_isInitialized && "SVD is not initialized.");
170 if (isTranspose){
171 eigen_assert(this->computeV() && "This SVD decomposition didn't compute U. Did you ask for it?");
172 return this->m_matrixV;
173 }
174 else
175 {
176 eigen_assert(this->computeU() && "This SVD decomposition didn't compute U. Did you ask for it?");
177 return this->m_matrixU;
178 }
179
180 }
181
182
matrixV()183 const MatrixVType& matrixV() const
184 {
185 eigen_assert(this->m_isInitialized && "SVD is not initialized.");
186 if (isTranspose){
187 eigen_assert(this->computeU() && "This SVD decomposition didn't compute V. Did you ask for it?");
188 return this->m_matrixU;
189 }
190 else
191 {
192 eigen_assert(this->computeV() && "This SVD decomposition didn't compute V. Did you ask for it?");
193 return this->m_matrixV;
194 }
195 }
196
197 private:
198 void allocate(Index rows, Index cols, unsigned int computationOptions);
199 void divide (Index firstCol, Index lastCol, Index firstRowW,
200 Index firstColW, Index shift);
201 void deflation43(Index firstCol, Index shift, Index i, Index size);
202 void deflation44(Index firstColu , Index firstColm, Index firstRowW, Index firstColW, Index i, Index j, Index size);
203 void deflation(Index firstCol, Index lastCol, Index k, Index firstRowW, Index firstColW, Index shift);
204 void copyUV(MatrixXr naiveU, MatrixXr naiveV, MatrixX householderU, MatrixX houseHolderV);
205
206 protected:
207 MatrixXr m_naiveU, m_naiveV;
208 MatrixXr m_computed;
209 Index nRec;
210 int algoswap;
211 bool isTranspose, compU, compV;
212
213 }; //end class BDCSVD
214
215
216 // Methode to allocate ans initialize matrix and attributs
217 template<typename MatrixType>
allocate(Index rows,Index cols,unsigned int computationOptions)218 void BDCSVD<MatrixType>::allocate(Index rows, Index cols, unsigned int computationOptions)
219 {
220 isTranspose = (cols > rows);
221 if (SVDBase<MatrixType>::allocate(rows, cols, computationOptions)) return;
222 m_computed = MatrixXr::Zero(this->m_diagSize + 1, this->m_diagSize );
223 if (isTranspose){
224 compU = this->computeU();
225 compV = this->computeV();
226 }
227 else
228 {
229 compV = this->computeU();
230 compU = this->computeV();
231 }
232 if (compU) m_naiveU = MatrixXr::Zero(this->m_diagSize + 1, this->m_diagSize + 1 );
233 else m_naiveU = MatrixXr::Zero(2, this->m_diagSize + 1 );
234
235 if (compV) m_naiveV = MatrixXr::Zero(this->m_diagSize, this->m_diagSize);
236
237
238 //should be changed for a cleaner implementation
239 if (isTranspose){
240 bool aux;
241 if (this->computeU()||this->computeV()){
242 aux = this->m_computeFullU;
243 this->m_computeFullU = this->m_computeFullV;
244 this->m_computeFullV = aux;
245 aux = this->m_computeThinU;
246 this->m_computeThinU = this->m_computeThinV;
247 this->m_computeThinV = aux;
248 }
249 }
250 }// end allocate
251
252 // Methode which compute the BDCSVD for the int
253 template<>
254 SVDBase<Matrix<int, Dynamic, Dynamic> >&
compute(const MatrixType & matrix,unsigned int computationOptions)255 BDCSVD<Matrix<int, Dynamic, Dynamic> >::compute(const MatrixType& matrix, unsigned int computationOptions) {
256 allocate(matrix.rows(), matrix.cols(), computationOptions);
257 this->m_nonzeroSingularValues = 0;
258 m_computed = Matrix<int, Dynamic, Dynamic>::Zero(rows(), cols());
259 for (int i=0; i<this->m_diagSize; i++) {
260 this->m_singularValues.coeffRef(i) = 0;
261 }
262 if (this->m_computeFullU) this->m_matrixU = Matrix<int, Dynamic, Dynamic>::Zero(rows(), rows());
263 if (this->m_computeFullV) this->m_matrixV = Matrix<int, Dynamic, Dynamic>::Zero(cols(), cols());
264 this->m_isInitialized = true;
265 return *this;
266 }
267
268
269 // Methode which compute the BDCSVD
270 template<typename MatrixType>
271 SVDBase<MatrixType>&
compute(const MatrixType & matrix,unsigned int computationOptions)272 BDCSVD<MatrixType>::compute(const MatrixType& matrix, unsigned int computationOptions)
273 {
274 allocate(matrix.rows(), matrix.cols(), computationOptions);
275 using std::abs;
276
277 //**** step 1 Bidiagonalization isTranspose = (matrix.cols()>matrix.rows()) ;
278 MatrixType copy;
279 if (isTranspose) copy = matrix.adjoint();
280 else copy = matrix;
281
282 internal::UpperBidiagonalization<MatrixX > bid(copy);
283
284 //**** step 2 Divide
285 // this is ugly and has to be redone (care of complex cast)
286 MatrixXr temp;
287 temp = bid.bidiagonal().toDenseMatrix().transpose();
288 m_computed.setZero();
289 for (int i=0; i<this->m_diagSize - 1; i++) {
290 m_computed(i, i) = temp(i, i);
291 m_computed(i + 1, i) = temp(i + 1, i);
292 }
293 m_computed(this->m_diagSize - 1, this->m_diagSize - 1) = temp(this->m_diagSize - 1, this->m_diagSize - 1);
294 divide(0, this->m_diagSize - 1, 0, 0, 0);
295
296 //**** step 3 copy
297 for (int i=0; i<this->m_diagSize; i++) {
298 RealScalar a = abs(m_computed.coeff(i, i));
299 this->m_singularValues.coeffRef(i) = a;
300 if (a == 0){
301 this->m_nonzeroSingularValues = i;
302 break;
303 }
304 else if (i == this->m_diagSize - 1)
305 {
306 this->m_nonzeroSingularValues = i + 1;
307 break;
308 }
309 }
310 copyUV(m_naiveV, m_naiveU, bid.householderU(), bid.householderV());
311 this->m_isInitialized = true;
312 return *this;
313 }// end compute
314
315
316 template<typename MatrixType>
copyUV(MatrixXr naiveU,MatrixXr naiveV,MatrixX householderU,MatrixX householderV)317 void BDCSVD<MatrixType>::copyUV(MatrixXr naiveU, MatrixXr naiveV, MatrixX householderU, MatrixX householderV){
318 if (this->computeU()){
319 MatrixX temp = MatrixX::Zero(naiveU.rows(), naiveU.cols());
320 temp.real() = naiveU;
321 if (this->m_computeThinU){
322 this->m_matrixU = MatrixX::Identity(householderU.cols(), this->m_nonzeroSingularValues );
323 this->m_matrixU.block(0, 0, this->m_diagSize, this->m_nonzeroSingularValues) =
324 temp.block(0, 0, this->m_diagSize, this->m_nonzeroSingularValues);
325 this->m_matrixU = householderU * this->m_matrixU ;
326 }
327 else
328 {
329 this->m_matrixU = MatrixX::Identity(householderU.cols(), householderU.cols());
330 this->m_matrixU.block(0, 0, this->m_diagSize, this->m_diagSize) = temp.block(0, 0, this->m_diagSize, this->m_diagSize);
331 this->m_matrixU = householderU * this->m_matrixU ;
332 }
333 }
334 if (this->computeV()){
335 MatrixX temp = MatrixX::Zero(naiveV.rows(), naiveV.cols());
336 temp.real() = naiveV;
337 if (this->m_computeThinV){
338 this->m_matrixV = MatrixX::Identity(householderV.cols(),this->m_nonzeroSingularValues );
339 this->m_matrixV.block(0, 0, this->m_nonzeroSingularValues, this->m_nonzeroSingularValues) =
340 temp.block(0, 0, this->m_nonzeroSingularValues, this->m_nonzeroSingularValues);
341 this->m_matrixV = householderV * this->m_matrixV ;
342 }
343 else
344 {
345 this->m_matrixV = MatrixX::Identity(householderV.cols(), householderV.cols());
346 this->m_matrixV.block(0, 0, this->m_diagSize, this->m_diagSize) = temp.block(0, 0, this->m_diagSize, this->m_diagSize);
347 this->m_matrixV = householderV * this->m_matrixV;
348 }
349 }
350 }
351
352 // The divide algorithm is done "in place", we are always working on subsets of the same matrix. The divide methods takes as argument the
353 // place of the submatrix we are currently working on.
354
355 //@param firstCol : The Index of the first column of the submatrix of m_computed and for m_naiveU;
356 //@param lastCol : The Index of the last column of the submatrix of m_computed and for m_naiveU;
357 // lastCol + 1 - firstCol is the size of the submatrix.
358 //@param firstRowW : The Index of the first row of the matrix W that we are to change. (see the reference paper section 1 for more information on W)
359 //@param firstRowW : Same as firstRowW with the column.
360 //@param shift : Each time one takes the left submatrix, one must add 1 to the shift. Why? Because! We actually want the last column of the U submatrix
361 // to become the first column (*coeff) and to shift all the other columns to the right. There are more details on the reference paper.
362 template<typename MatrixType>
divide(Index firstCol,Index lastCol,Index firstRowW,Index firstColW,Index shift)363 void BDCSVD<MatrixType>::divide (Index firstCol, Index lastCol, Index firstRowW,
364 Index firstColW, Index shift)
365 {
366 // requires nbRows = nbCols + 1;
367 using std::pow;
368 using std::sqrt;
369 using std::abs;
370 const Index n = lastCol - firstCol + 1;
371 const Index k = n/2;
372 RealScalar alphaK;
373 RealScalar betaK;
374 RealScalar r0;
375 RealScalar lambda, phi, c0, s0;
376 MatrixXr l, f;
377 // We use the other algorithm which is more efficient for small
378 // matrices.
379 if (n < algoswap){
380 JacobiSVD<MatrixXr> b(m_computed.block(firstCol, firstCol, n + 1, n),
381 ComputeFullU | (ComputeFullV * compV)) ;
382 if (compU) m_naiveU.block(firstCol, firstCol, n + 1, n + 1).real() << b.matrixU();
383 else
384 {
385 m_naiveU.row(0).segment(firstCol, n + 1).real() << b.matrixU().row(0);
386 m_naiveU.row(1).segment(firstCol, n + 1).real() << b.matrixU().row(n);
387 }
388 if (compV) m_naiveV.block(firstRowW, firstColW, n, n).real() << b.matrixV();
389 m_computed.block(firstCol + shift, firstCol + shift, n + 1, n).setZero();
390 for (int i=0; i<n; i++)
391 {
392 m_computed(firstCol + shift + i, firstCol + shift +i) = b.singularValues().coeffRef(i);
393 }
394 return;
395 }
396 // We use the divide and conquer algorithm
397 alphaK = m_computed(firstCol + k, firstCol + k);
398 betaK = m_computed(firstCol + k + 1, firstCol + k);
399 // The divide must be done in that order in order to have good results. Divide change the data inside the submatrices
400 // and the divide of the right submatrice reads one column of the left submatrice. That's why we need to treat the
401 // right submatrix before the left one.
402 divide(k + 1 + firstCol, lastCol, k + 1 + firstRowW, k + 1 + firstColW, shift);
403 divide(firstCol, k - 1 + firstCol, firstRowW, firstColW + 1, shift + 1);
404 if (compU)
405 {
406 lambda = m_naiveU(firstCol + k, firstCol + k);
407 phi = m_naiveU(firstCol + k + 1, lastCol + 1);
408 }
409 else
410 {
411 lambda = m_naiveU(1, firstCol + k);
412 phi = m_naiveU(0, lastCol + 1);
413 }
414 r0 = sqrt((abs(alphaK * lambda) * abs(alphaK * lambda))
415 + abs(betaK * phi) * abs(betaK * phi));
416 if (compU)
417 {
418 l = m_naiveU.row(firstCol + k).segment(firstCol, k);
419 f = m_naiveU.row(firstCol + k + 1).segment(firstCol + k + 1, n - k - 1);
420 }
421 else
422 {
423 l = m_naiveU.row(1).segment(firstCol, k);
424 f = m_naiveU.row(0).segment(firstCol + k + 1, n - k - 1);
425 }
426 if (compV) m_naiveV(firstRowW+k, firstColW) = 1;
427 if (r0 == 0)
428 {
429 c0 = 1;
430 s0 = 0;
431 }
432 else
433 {
434 c0 = alphaK * lambda / r0;
435 s0 = betaK * phi / r0;
436 }
437 if (compU)
438 {
439 MatrixXr q1 (m_naiveU.col(firstCol + k).segment(firstCol, k + 1));
440 // we shiftW Q1 to the right
441 for (Index i = firstCol + k - 1; i >= firstCol; i--)
442 {
443 m_naiveU.col(i + 1).segment(firstCol, k + 1) << m_naiveU.col(i).segment(firstCol, k + 1);
444 }
445 // we shift q1 at the left with a factor c0
446 m_naiveU.col(firstCol).segment( firstCol, k + 1) << (q1 * c0);
447 // last column = q1 * - s0
448 m_naiveU.col(lastCol + 1).segment(firstCol, k + 1) << (q1 * ( - s0));
449 // first column = q2 * s0
450 m_naiveU.col(firstCol).segment(firstCol + k + 1, n - k) <<
451 m_naiveU.col(lastCol + 1).segment(firstCol + k + 1, n - k) *s0;
452 // q2 *= c0
453 m_naiveU.col(lastCol + 1).segment(firstCol + k + 1, n - k) *= c0;
454 }
455 else
456 {
457 RealScalar q1 = (m_naiveU(0, firstCol + k));
458 // we shift Q1 to the right
459 for (Index i = firstCol + k - 1; i >= firstCol; i--)
460 {
461 m_naiveU(0, i + 1) = m_naiveU(0, i);
462 }
463 // we shift q1 at the left with a factor c0
464 m_naiveU(0, firstCol) = (q1 * c0);
465 // last column = q1 * - s0
466 m_naiveU(0, lastCol + 1) = (q1 * ( - s0));
467 // first column = q2 * s0
468 m_naiveU(1, firstCol) = m_naiveU(1, lastCol + 1) *s0;
469 // q2 *= c0
470 m_naiveU(1, lastCol + 1) *= c0;
471 m_naiveU.row(1).segment(firstCol + 1, k).setZero();
472 m_naiveU.row(0).segment(firstCol + k + 1, n - k - 1).setZero();
473 }
474 m_computed(firstCol + shift, firstCol + shift) = r0;
475 m_computed.col(firstCol + shift).segment(firstCol + shift + 1, k) << alphaK * l.transpose().real();
476 m_computed.col(firstCol + shift).segment(firstCol + shift + k + 1, n - k - 1) << betaK * f.transpose().real();
477
478
479 // the line below do the deflation of the matrix for the third part of the algorithm
480 // Here the deflation is commented because the third part of the algorithm is not implemented
481 // the third part of the algorithm is a fast SVD on the matrix m_computed which works thanks to the deflation
482
483 deflation(firstCol, lastCol, k, firstRowW, firstColW, shift);
484
485 // Third part of the algorithm, since the real third part of the algorithm is not implemeted we use a JacobiSVD
486 JacobiSVD<MatrixXr> res= JacobiSVD<MatrixXr>(m_computed.block(firstCol + shift, firstCol +shift, n + 1, n),
487 ComputeFullU | (ComputeFullV * compV)) ;
488 if (compU) m_naiveU.block(firstCol, firstCol, n + 1, n + 1) *= res.matrixU();
489 else m_naiveU.block(0, firstCol, 2, n + 1) *= res.matrixU();
490
491 if (compV) m_naiveV.block(firstRowW, firstColW, n, n) *= res.matrixV();
492 m_computed.block(firstCol + shift, firstCol + shift, n, n) << MatrixXr::Zero(n, n);
493 for (int i=0; i<n; i++)
494 m_computed(firstCol + shift + i, firstCol + shift +i) = res.singularValues().coeffRef(i);
495 // end of the third part
496
497
498 }// end divide
499
500
501 // page 12_13
502 // i >= 1, di almost null and zi non null.
503 // We use a rotation to zero out zi applied to the left of M
504 template <typename MatrixType>
deflation43(Index firstCol,Index shift,Index i,Index size)505 void BDCSVD<MatrixType>::deflation43(Index firstCol, Index shift, Index i, Index size){
506 using std::abs;
507 using std::sqrt;
508 using std::pow;
509 RealScalar c = m_computed(firstCol + shift, firstCol + shift);
510 RealScalar s = m_computed(i, firstCol + shift);
511 RealScalar r = sqrt(pow(abs(c), 2) + pow(abs(s), 2));
512 if (r == 0){
513 m_computed(i, i)=0;
514 return;
515 }
516 c/=r;
517 s/=r;
518 m_computed(firstCol + shift, firstCol + shift) = r;
519 m_computed(i, firstCol + shift) = 0;
520 m_computed(i, i) = 0;
521 if (compU){
522 m_naiveU.col(firstCol).segment(firstCol,size) =
523 c * m_naiveU.col(firstCol).segment(firstCol, size) -
524 s * m_naiveU.col(i).segment(firstCol, size) ;
525
526 m_naiveU.col(i).segment(firstCol, size) =
527 (c + s*s/c) * m_naiveU.col(i).segment(firstCol, size) +
528 (s/c) * m_naiveU.col(firstCol).segment(firstCol,size);
529 }
530 }// end deflation 43
531
532
533 // page 13
534 // i,j >= 1, i != j and |di - dj| < epsilon * norm2(M)
535 // We apply two rotations to have zj = 0;
536 template <typename MatrixType>
deflation44(Index firstColu,Index firstColm,Index firstRowW,Index firstColW,Index i,Index j,Index size)537 void BDCSVD<MatrixType>::deflation44(Index firstColu , Index firstColm, Index firstRowW, Index firstColW, Index i, Index j, Index size){
538 using std::abs;
539 using std::sqrt;
540 using std::conj;
541 using std::pow;
542 RealScalar c = m_computed(firstColm, firstColm + j - 1);
543 RealScalar s = m_computed(firstColm, firstColm + i - 1);
544 RealScalar r = sqrt(pow(abs(c), 2) + pow(abs(s), 2));
545 if (r==0){
546 m_computed(firstColm + i, firstColm + i) = m_computed(firstColm + j, firstColm + j);
547 return;
548 }
549 c/=r;
550 s/=r;
551 m_computed(firstColm + i, firstColm) = r;
552 m_computed(firstColm + i, firstColm + i) = m_computed(firstColm + j, firstColm + j);
553 m_computed(firstColm + j, firstColm) = 0;
554 if (compU){
555 m_naiveU.col(firstColu + i).segment(firstColu, size) =
556 c * m_naiveU.col(firstColu + i).segment(firstColu, size) -
557 s * m_naiveU.col(firstColu + j).segment(firstColu, size) ;
558
559 m_naiveU.col(firstColu + j).segment(firstColu, size) =
560 (c + s*s/c) * m_naiveU.col(firstColu + j).segment(firstColu, size) +
561 (s/c) * m_naiveU.col(firstColu + i).segment(firstColu, size);
562 }
563 if (compV){
564 m_naiveV.col(firstColW + i).segment(firstRowW, size - 1) =
565 c * m_naiveV.col(firstColW + i).segment(firstRowW, size - 1) +
566 s * m_naiveV.col(firstColW + j).segment(firstRowW, size - 1) ;
567
568 m_naiveV.col(firstColW + j).segment(firstRowW, size - 1) =
569 (c + s*s/c) * m_naiveV.col(firstColW + j).segment(firstRowW, size - 1) -
570 (s/c) * m_naiveV.col(firstColW + i).segment(firstRowW, size - 1);
571 }
572 }// end deflation 44
573
574
575
576 template <typename MatrixType>
deflation(Index firstCol,Index lastCol,Index k,Index firstRowW,Index firstColW,Index shift)577 void BDCSVD<MatrixType>::deflation(Index firstCol, Index lastCol, Index k, Index firstRowW, Index firstColW, Index shift){
578 //condition 4.1
579 RealScalar EPS = EPSILON * (std::max<RealScalar>(m_computed(firstCol + shift + 1, firstCol + shift + 1), m_computed(firstCol + k, firstCol + k)));
580 const Index length = lastCol + 1 - firstCol;
581 if (m_computed(firstCol + shift, firstCol + shift) < EPS){
582 m_computed(firstCol + shift, firstCol + shift) = EPS;
583 }
584 //condition 4.2
585 for (Index i=firstCol + shift + 1;i<=lastCol + shift;i++){
586 if (std::abs(m_computed(i, firstCol + shift)) < EPS){
587 m_computed(i, firstCol + shift) = 0;
588 }
589 }
590
591 //condition 4.3
592 for (Index i=firstCol + shift + 1;i<=lastCol + shift; i++){
593 if (m_computed(i, i) < EPS){
594 deflation43(firstCol, shift, i, length);
595 }
596 }
597
598 //condition 4.4
599
600 Index i=firstCol + shift + 1, j=firstCol + shift + k + 1;
601 //we stock the final place of each line
602 Index *permutation = new Index[length];
603
604 for (Index p =1; p < length; p++) {
605 if (i> firstCol + shift + k){
606 permutation[p] = j;
607 j++;
608 } else if (j> lastCol + shift)
609 {
610 permutation[p] = i;
611 i++;
612 }
613 else
614 {
615 if (m_computed(i, i) < m_computed(j, j)){
616 permutation[p] = j;
617 j++;
618 }
619 else
620 {
621 permutation[p] = i;
622 i++;
623 }
624 }
625 }
626 //we do the permutation
627 RealScalar aux;
628 //we stock the current index of each col
629 //and the column of each index
630 Index *realInd = new Index[length];
631 Index *realCol = new Index[length];
632 for (int pos = 0; pos< length; pos++){
633 realCol[pos] = pos + firstCol + shift;
634 realInd[pos] = pos;
635 }
636 const Index Zero = firstCol + shift;
637 VectorType temp;
638 for (int i = 1; i < length - 1; i++){
639 const Index I = i + Zero;
640 const Index realI = realInd[i];
641 const Index j = permutation[length - i] - Zero;
642 const Index J = realCol[j];
643
644 //diag displace
645 aux = m_computed(I, I);
646 m_computed(I, I) = m_computed(J, J);
647 m_computed(J, J) = aux;
648
649 //firstrow displace
650 aux = m_computed(I, Zero);
651 m_computed(I, Zero) = m_computed(J, Zero);
652 m_computed(J, Zero) = aux;
653
654 // change columns
655 if (compU) {
656 temp = m_naiveU.col(I - shift).segment(firstCol, length + 1);
657 m_naiveU.col(I - shift).segment(firstCol, length + 1) <<
658 m_naiveU.col(J - shift).segment(firstCol, length + 1);
659 m_naiveU.col(J - shift).segment(firstCol, length + 1) << temp;
660 }
661 else
662 {
663 temp = m_naiveU.col(I - shift).segment(0, 2);
664 m_naiveU.col(I - shift).segment(0, 2) <<
665 m_naiveU.col(J - shift).segment(0, 2);
666 m_naiveU.col(J - shift).segment(0, 2) << temp;
667 }
668 if (compV) {
669 const Index CWI = I + firstColW - Zero;
670 const Index CWJ = J + firstColW - Zero;
671 temp = m_naiveV.col(CWI).segment(firstRowW, length);
672 m_naiveV.col(CWI).segment(firstRowW, length) << m_naiveV.col(CWJ).segment(firstRowW, length);
673 m_naiveV.col(CWJ).segment(firstRowW, length) << temp;
674 }
675
676 //update real pos
677 realCol[realI] = J;
678 realCol[j] = I;
679 realInd[J - Zero] = realI;
680 realInd[I - Zero] = j;
681 }
682 for (Index i = firstCol + shift + 1; i<lastCol + shift;i++){
683 if ((m_computed(i + 1, i + 1) - m_computed(i, i)) < EPS){
684 deflation44(firstCol ,
685 firstCol + shift,
686 firstRowW,
687 firstColW,
688 i - Zero,
689 i + 1 - Zero,
690 length);
691 }
692 }
693 delete [] permutation;
694 delete [] realInd;
695 delete [] realCol;
696
697 }//end deflation
698
699
700 namespace internal{
701
702 template<typename _MatrixType, typename Rhs>
703 struct solve_retval<BDCSVD<_MatrixType>, Rhs>
704 : solve_retval_base<BDCSVD<_MatrixType>, Rhs>
705 {
706 typedef BDCSVD<_MatrixType> BDCSVDType;
707 EIGEN_MAKE_SOLVE_HELPERS(BDCSVDType, Rhs)
708
709 template<typename Dest> void evalTo(Dest& dst) const
710 {
711 eigen_assert(rhs().rows() == dec().rows());
712 // A = U S V^*
713 // So A^{ - 1} = V S^{ - 1} U^*
714 Index diagSize = (std::min)(dec().rows(), dec().cols());
715 typename BDCSVDType::SingularValuesType invertedSingVals(diagSize);
716 Index nonzeroSingVals = dec().nonzeroSingularValues();
717 invertedSingVals.head(nonzeroSingVals) = dec().singularValues().head(nonzeroSingVals).array().inverse();
718 invertedSingVals.tail(diagSize - nonzeroSingVals).setZero();
719
720 dst = dec().matrixV().leftCols(diagSize)
721 * invertedSingVals.asDiagonal()
722 * dec().matrixU().leftCols(diagSize).adjoint()
723 * rhs();
724 return;
725 }
726 };
727
728 } //end namespace internal
729
730 /** \svd_module
731 *
732 * \return the singular value decomposition of \c *this computed by
733 * BDC Algorithm
734 *
735 * \sa class BDCSVD
736 */
737 /*
738 template<typename Derived>
739 BDCSVD<typename MatrixBase<Derived>::PlainObject>
740 MatrixBase<Derived>::bdcSvd(unsigned int computationOptions) const
741 {
742 return BDCSVD<PlainObject>(*this, computationOptions);
743 }
744 */
745
746 } // end namespace Eigen
747
748 #endif
749