1 // This file is part of Eigen, a lightweight C++ template library
2 // for linear algebra.
3 //
4 // Copyright (C) 2010 Benoit Jacob <jacob.benoit.1@gmail.com>
5 // Copyright (C) 2013-2014 Gael Guennebaud <gael.guennebaud@inria.fr>
6 //
7 // This Source Code Form is subject to the terms of the Mozilla
8 // Public License v. 2.0. If a copy of the MPL was not distributed
9 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 
11 #ifndef EIGEN_BIDIAGONALIZATION_H
12 #define EIGEN_BIDIAGONALIZATION_H
13 
14 namespace Eigen {
15 
16 namespace internal {
17 // UpperBidiagonalization will probably be replaced by a Bidiagonalization class, don't want to make it stable API.
18 // At the same time, it's useful to keep for now as it's about the only thing that is testing the BandMatrix class.
19 
20 template<typename _MatrixType> class UpperBidiagonalization
21 {
22   public:
23 
24     typedef _MatrixType MatrixType;
25     enum {
26       RowsAtCompileTime = MatrixType::RowsAtCompileTime,
27       ColsAtCompileTime = MatrixType::ColsAtCompileTime,
28       ColsAtCompileTimeMinusOne = internal::decrement_size<ColsAtCompileTime>::ret
29     };
30     typedef typename MatrixType::Scalar Scalar;
31     typedef typename MatrixType::RealScalar RealScalar;
32     typedef Eigen::Index Index; ///< \deprecated since Eigen 3.3
33     typedef Matrix<Scalar, 1, ColsAtCompileTime> RowVectorType;
34     typedef Matrix<Scalar, RowsAtCompileTime, 1> ColVectorType;
35     typedef BandMatrix<RealScalar, ColsAtCompileTime, ColsAtCompileTime, 1, 0, RowMajor> BidiagonalType;
36     typedef Matrix<Scalar, ColsAtCompileTime, 1> DiagVectorType;
37     typedef Matrix<Scalar, ColsAtCompileTimeMinusOne, 1> SuperDiagVectorType;
38     typedef HouseholderSequence<
39               const MatrixType,
40               const typename internal::remove_all<typename Diagonal<const MatrixType,0>::ConjugateReturnType>::type
41             > HouseholderUSequenceType;
42     typedef HouseholderSequence<
43               const typename internal::remove_all<typename MatrixType::ConjugateReturnType>::type,
44               Diagonal<const MatrixType,1>,
45               OnTheRight
46             > HouseholderVSequenceType;
47 
48     /**
49     * \brief Default Constructor.
50     *
51     * The default constructor is useful in cases in which the user intends to
52     * perform decompositions via Bidiagonalization::compute(const MatrixType&).
53     */
UpperBidiagonalization()54     UpperBidiagonalization() : m_householder(), m_bidiagonal(), m_isInitialized(false) {}
55 
UpperBidiagonalization(const MatrixType & matrix)56     explicit UpperBidiagonalization(const MatrixType& matrix)
57       : m_householder(matrix.rows(), matrix.cols()),
58         m_bidiagonal(matrix.cols(), matrix.cols()),
59         m_isInitialized(false)
60     {
61       compute(matrix);
62     }
63 
64     UpperBidiagonalization& compute(const MatrixType& matrix);
65     UpperBidiagonalization& computeUnblocked(const MatrixType& matrix);
66 
householder()67     const MatrixType& householder() const { return m_householder; }
bidiagonal()68     const BidiagonalType& bidiagonal() const { return m_bidiagonal; }
69 
householderU()70     const HouseholderUSequenceType householderU() const
71     {
72       eigen_assert(m_isInitialized && "UpperBidiagonalization is not initialized.");
73       return HouseholderUSequenceType(m_householder, m_householder.diagonal().conjugate());
74     }
75 
householderV()76     const HouseholderVSequenceType householderV() // const here gives nasty errors and i'm lazy
77     {
78       eigen_assert(m_isInitialized && "UpperBidiagonalization is not initialized.");
79       return HouseholderVSequenceType(m_householder.conjugate(), m_householder.const_derived().template diagonal<1>())
80              .setLength(m_householder.cols()-1)
81              .setShift(1);
82     }
83 
84   protected:
85     MatrixType m_householder;
86     BidiagonalType m_bidiagonal;
87     bool m_isInitialized;
88 };
89 
90 // Standard upper bidiagonalization without fancy optimizations
91 // This version should be faster for small matrix size
92 template<typename MatrixType>
93 void upperbidiagonalization_inplace_unblocked(MatrixType& mat,
94                                               typename MatrixType::RealScalar *diagonal,
95                                               typename MatrixType::RealScalar *upper_diagonal,
96                                               typename MatrixType::Scalar* tempData = 0)
97 {
98   typedef typename MatrixType::Scalar Scalar;
99 
100   Index rows = mat.rows();
101   Index cols = mat.cols();
102 
103   typedef Matrix<Scalar,Dynamic,1,ColMajor,MatrixType::MaxRowsAtCompileTime,1> TempType;
104   TempType tempVector;
105   if(tempData==0)
106   {
107     tempVector.resize(rows);
108     tempData = tempVector.data();
109   }
110 
111   for (Index k = 0; /* breaks at k==cols-1 below */ ; ++k)
112   {
113     Index remainingRows = rows - k;
114     Index remainingCols = cols - k - 1;
115 
116     // construct left householder transform in-place in A
117     mat.col(k).tail(remainingRows)
118        .makeHouseholderInPlace(mat.coeffRef(k,k), diagonal[k]);
119     // apply householder transform to remaining part of A on the left
120     mat.bottomRightCorner(remainingRows, remainingCols)
121        .applyHouseholderOnTheLeft(mat.col(k).tail(remainingRows-1), mat.coeff(k,k), tempData);
122 
123     if(k == cols-1) break;
124 
125     // construct right householder transform in-place in mat
126     mat.row(k).tail(remainingCols)
127        .makeHouseholderInPlace(mat.coeffRef(k,k+1), upper_diagonal[k]);
128     // apply householder transform to remaining part of mat on the left
129     mat.bottomRightCorner(remainingRows-1, remainingCols)
130        .applyHouseholderOnTheRight(mat.row(k).tail(remainingCols-1).transpose(), mat.coeff(k,k+1), tempData);
131   }
132 }
133 
134 /** \internal
135   * Helper routine for the block reduction to upper bidiagonal form.
136   *
137   * Let's partition the matrix A:
138   *
139   *      | A00 A01 |
140   *  A = |         |
141   *      | A10 A11 |
142   *
143   * This function reduces to bidiagonal form the left \c rows x \a blockSize vertical panel [A00/A10]
144   * and the \a blockSize x \c cols horizontal panel [A00 A01] of the matrix \a A. The bottom-right block A11
145   * is updated using matrix-matrix products:
146   *   A22 -= V * Y^T - X * U^T
147   * where V and U contains the left and right Householder vectors. U and V are stored in A10, and A01
148   * respectively, and the update matrices X and Y are computed during the reduction.
149   *
150   */
151 template<typename MatrixType>
upperbidiagonalization_blocked_helper(MatrixType & A,typename MatrixType::RealScalar * diagonal,typename MatrixType::RealScalar * upper_diagonal,Index bs,Ref<Matrix<typename MatrixType::Scalar,Dynamic,Dynamic,traits<MatrixType>::Flags & RowMajorBit>> X,Ref<Matrix<typename MatrixType::Scalar,Dynamic,Dynamic,traits<MatrixType>::Flags & RowMajorBit>> Y)152 void upperbidiagonalization_blocked_helper(MatrixType& A,
153                                            typename MatrixType::RealScalar *diagonal,
154                                            typename MatrixType::RealScalar *upper_diagonal,
155                                            Index bs,
156                                            Ref<Matrix<typename MatrixType::Scalar, Dynamic, Dynamic,
157                                                       traits<MatrixType>::Flags & RowMajorBit> > X,
158                                            Ref<Matrix<typename MatrixType::Scalar, Dynamic, Dynamic,
159                                                       traits<MatrixType>::Flags & RowMajorBit> > Y)
160 {
161   typedef typename MatrixType::Scalar Scalar;
162   enum { StorageOrder = traits<MatrixType>::Flags & RowMajorBit };
163   typedef InnerStride<int(StorageOrder) == int(ColMajor) ? 1 : Dynamic> ColInnerStride;
164   typedef InnerStride<int(StorageOrder) == int(ColMajor) ? Dynamic : 1> RowInnerStride;
165   typedef Ref<Matrix<Scalar, Dynamic, 1>, 0, ColInnerStride>    SubColumnType;
166   typedef Ref<Matrix<Scalar, 1, Dynamic>, 0, RowInnerStride>    SubRowType;
167   typedef Ref<Matrix<Scalar, Dynamic, Dynamic, StorageOrder > > SubMatType;
168 
169   Index brows = A.rows();
170   Index bcols = A.cols();
171 
172   Scalar tau_u, tau_u_prev(0), tau_v;
173 
174   for(Index k = 0; k < bs; ++k)
175   {
176     Index remainingRows = brows - k;
177     Index remainingCols = bcols - k - 1;
178 
179     SubMatType X_k1( X.block(k,0, remainingRows,k) );
180     SubMatType V_k1( A.block(k,0, remainingRows,k) );
181 
182     // 1 - update the k-th column of A
183     SubColumnType v_k = A.col(k).tail(remainingRows);
184           v_k -= V_k1 * Y.row(k).head(k).adjoint();
185     if(k) v_k -= X_k1 * A.col(k).head(k);
186 
187     // 2 - construct left Householder transform in-place
188     v_k.makeHouseholderInPlace(tau_v, diagonal[k]);
189 
190     if(k+1<bcols)
191     {
192       SubMatType Y_k  ( Y.block(k+1,0, remainingCols, k+1) );
193       SubMatType U_k1 ( A.block(0,k+1, k,remainingCols) );
194 
195       // this eases the application of Householder transforAions
196       // A(k,k) will store tau_v later
197       A(k,k) = Scalar(1);
198 
199       // 3 - Compute y_k^T = tau_v * ( A^T*v_k - Y_k-1*V_k-1^T*v_k - U_k-1*X_k-1^T*v_k )
200       {
201         SubColumnType y_k( Y.col(k).tail(remainingCols) );
202 
203         // let's use the begining of column k of Y as a temporary vector
204         SubColumnType tmp( Y.col(k).head(k) );
205         y_k.noalias()  = A.block(k,k+1, remainingRows,remainingCols).adjoint() * v_k; // bottleneck
206         tmp.noalias()  = V_k1.adjoint()  * v_k;
207         y_k.noalias() -= Y_k.leftCols(k) * tmp;
208         tmp.noalias()  = X_k1.adjoint()  * v_k;
209         y_k.noalias() -= U_k1.adjoint()  * tmp;
210         y_k *= numext::conj(tau_v);
211       }
212 
213       // 4 - update k-th row of A (it will become u_k)
214       SubRowType u_k( A.row(k).tail(remainingCols) );
215       u_k = u_k.conjugate();
216       {
217         u_k -= Y_k * A.row(k).head(k+1).adjoint();
218         if(k) u_k -= U_k1.adjoint() * X.row(k).head(k).adjoint();
219       }
220 
221       // 5 - construct right Householder transform in-place
222       u_k.makeHouseholderInPlace(tau_u, upper_diagonal[k]);
223 
224       // this eases the application of Householder transformations
225       // A(k,k+1) will store tau_u later
226       A(k,k+1) = Scalar(1);
227 
228       // 6 - Compute x_k = tau_u * ( A*u_k - X_k-1*U_k-1^T*u_k - V_k*Y_k^T*u_k )
229       {
230         SubColumnType x_k ( X.col(k).tail(remainingRows-1) );
231 
232         // let's use the begining of column k of X as a temporary vectors
233         // note that tmp0 and tmp1 overlaps
234         SubColumnType tmp0 ( X.col(k).head(k) ),
235                       tmp1 ( X.col(k).head(k+1) );
236 
237         x_k.noalias()   = A.block(k+1,k+1, remainingRows-1,remainingCols) * u_k.transpose(); // bottleneck
238         tmp0.noalias()  = U_k1 * u_k.transpose();
239         x_k.noalias()  -= X_k1.bottomRows(remainingRows-1) * tmp0;
240         tmp1.noalias()  = Y_k.adjoint() * u_k.transpose();
241         x_k.noalias()  -= A.block(k+1,0, remainingRows-1,k+1) * tmp1;
242         x_k *= numext::conj(tau_u);
243         tau_u = numext::conj(tau_u);
244         u_k = u_k.conjugate();
245       }
246 
247       if(k>0) A.coeffRef(k-1,k) = tau_u_prev;
248       tau_u_prev = tau_u;
249     }
250     else
251       A.coeffRef(k-1,k) = tau_u_prev;
252 
253     A.coeffRef(k,k) = tau_v;
254   }
255 
256   if(bs<bcols)
257     A.coeffRef(bs-1,bs) = tau_u_prev;
258 
259   // update A22
260   if(bcols>bs && brows>bs)
261   {
262     SubMatType A11( A.bottomRightCorner(brows-bs,bcols-bs) );
263     SubMatType A10( A.block(bs,0, brows-bs,bs) );
264     SubMatType A01( A.block(0,bs, bs,bcols-bs) );
265     Scalar tmp = A01(bs-1,0);
266     A01(bs-1,0) = 1;
267     A11.noalias() -= A10 * Y.topLeftCorner(bcols,bs).bottomRows(bcols-bs).adjoint();
268     A11.noalias() -= X.topLeftCorner(brows,bs).bottomRows(brows-bs) * A01;
269     A01(bs-1,0) = tmp;
270   }
271 }
272 
273 /** \internal
274   *
275   * Implementation of a block-bidiagonal reduction.
276   * It is based on the following paper:
277   *   The Design of a Parallel Dense Linear Algebra Software Library: Reduction to Hessenberg, Tridiagonal, and Bidiagonal Form.
278   *   by Jaeyoung Choi, Jack J. Dongarra, David W. Walker. (1995)
279   *   section 3.3
280   */
281 template<typename MatrixType, typename BidiagType>
282 void upperbidiagonalization_inplace_blocked(MatrixType& A, BidiagType& bidiagonal,
283                                             Index maxBlockSize=32,
284                                             typename MatrixType::Scalar* /*tempData*/ = 0)
285 {
286   typedef typename MatrixType::Scalar Scalar;
287   typedef Block<MatrixType,Dynamic,Dynamic> BlockType;
288 
289   Index rows = A.rows();
290   Index cols = A.cols();
291   Index size = (std::min)(rows, cols);
292 
293   // X and Y are work space
294   enum { StorageOrder = traits<MatrixType>::Flags & RowMajorBit };
295   Matrix<Scalar,
296          MatrixType::RowsAtCompileTime,
297          Dynamic,
298          StorageOrder,
299          MatrixType::MaxRowsAtCompileTime> X(rows,maxBlockSize);
300   Matrix<Scalar,
301          MatrixType::ColsAtCompileTime,
302          Dynamic,
303          StorageOrder,
304          MatrixType::MaxColsAtCompileTime> Y(cols,maxBlockSize);
305   Index blockSize = (std::min)(maxBlockSize,size);
306 
307   Index k = 0;
308   for(k = 0; k < size; k += blockSize)
309   {
310     Index bs = (std::min)(size-k,blockSize);  // actual size of the block
311     Index brows = rows - k;                   // rows of the block
312     Index bcols = cols - k;                   // columns of the block
313 
314     // partition the matrix A:
315     //
316     //      | A00 A01 A02 |
317     //      |             |
318     // A  = | A10 A11 A12 |
319     //      |             |
320     //      | A20 A21 A22 |
321     //
322     // where A11 is a bs x bs diagonal block,
323     // and let:
324     //      | A11 A12 |
325     //  B = |         |
326     //      | A21 A22 |
327 
328     BlockType B = A.block(k,k,brows,bcols);
329 
330     // This stage performs the bidiagonalization of A11, A21, A12, and updating of A22.
331     // Finally, the algorithm continue on the updated A22.
332     //
333     // However, if B is too small, or A22 empty, then let's use an unblocked strategy
334     if(k+bs==cols || bcols<48) // somewhat arbitrary threshold
335     {
336       upperbidiagonalization_inplace_unblocked(B,
337                                                &(bidiagonal.template diagonal<0>().coeffRef(k)),
338                                                &(bidiagonal.template diagonal<1>().coeffRef(k)),
339                                                X.data()
340                                               );
341       break; // We're done
342     }
343     else
344     {
345       upperbidiagonalization_blocked_helper<BlockType>( B,
346                                                         &(bidiagonal.template diagonal<0>().coeffRef(k)),
347                                                         &(bidiagonal.template diagonal<1>().coeffRef(k)),
348                                                         bs,
349                                                         X.topLeftCorner(brows,bs),
350                                                         Y.topLeftCorner(bcols,bs)
351                                                       );
352     }
353   }
354 }
355 
356 template<typename _MatrixType>
computeUnblocked(const _MatrixType & matrix)357 UpperBidiagonalization<_MatrixType>& UpperBidiagonalization<_MatrixType>::computeUnblocked(const _MatrixType& matrix)
358 {
359   Index rows = matrix.rows();
360   Index cols = matrix.cols();
361   EIGEN_ONLY_USED_FOR_DEBUG(cols);
362 
363   eigen_assert(rows >= cols && "UpperBidiagonalization is only for Arices satisfying rows>=cols.");
364 
365   m_householder = matrix;
366 
367   ColVectorType temp(rows);
368 
369   upperbidiagonalization_inplace_unblocked(m_householder,
370                                            &(m_bidiagonal.template diagonal<0>().coeffRef(0)),
371                                            &(m_bidiagonal.template diagonal<1>().coeffRef(0)),
372                                            temp.data());
373 
374   m_isInitialized = true;
375   return *this;
376 }
377 
378 template<typename _MatrixType>
compute(const _MatrixType & matrix)379 UpperBidiagonalization<_MatrixType>& UpperBidiagonalization<_MatrixType>::compute(const _MatrixType& matrix)
380 {
381   Index rows = matrix.rows();
382   Index cols = matrix.cols();
383   EIGEN_ONLY_USED_FOR_DEBUG(rows);
384   EIGEN_ONLY_USED_FOR_DEBUG(cols);
385 
386   eigen_assert(rows >= cols && "UpperBidiagonalization is only for Arices satisfying rows>=cols.");
387 
388   m_householder = matrix;
389   upperbidiagonalization_inplace_blocked(m_householder, m_bidiagonal);
390 
391   m_isInitialized = true;
392   return *this;
393 }
394 
395 #if 0
396 /** \return the Householder QR decomposition of \c *this.
397   *
398   * \sa class Bidiagonalization
399   */
400 template<typename Derived>
401 const UpperBidiagonalization<typename MatrixBase<Derived>::PlainObject>
402 MatrixBase<Derived>::bidiagonalization() const
403 {
404   return UpperBidiagonalization<PlainObject>(eval());
405 }
406 #endif
407 
408 } // end namespace internal
409 
410 } // end namespace Eigen
411 
412 #endif // EIGEN_BIDIAGONALIZATION_H
413