Eigen  5.0.1
 
Loading...
Searching...
No Matches
UpperBidiagonalization.h
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// SPDX-License-Identifier: MPL-2.0
11
12#ifndef EIGEN_BIDIAGONALIZATION_H
13#define EIGEN_BIDIAGONALIZATION_H
14
15// IWYU pragma: private
16#include "./InternalHeaderCheck.h"
17
18namespace Eigen {
19
20namespace internal {
21// UpperBidiagonalization may be replaced by a Bidiagonalization class; not part of stable API.
22// Kept for now as it is one of the few tests exercising the BandMatrix class.
23
24template <typename MatrixType_>
25class UpperBidiagonalization {
26 public:
27 using MatrixType = MatrixType_;
28 enum {
29 RowsAtCompileTime = MatrixType::RowsAtCompileTime,
30 ColsAtCompileTime = MatrixType::ColsAtCompileTime,
31 ColsAtCompileTimeMinusOne = internal::decrement_size<ColsAtCompileTime>::value
32 };
33 using Scalar = typename MatrixType::Scalar;
34 using RealScalar = typename MatrixType::RealScalar;
35 using Index = Eigen::Index;
36 using RowVectorType = Matrix<Scalar, 1, ColsAtCompileTime>;
37 using ColVectorType = Matrix<Scalar, RowsAtCompileTime, 1>;
38 using BidiagonalType = BandMatrix<RealScalar, ColsAtCompileTime, ColsAtCompileTime, 1, 0, RowMajor>;
39 using DiagVectorType = Matrix<Scalar, ColsAtCompileTime, 1>;
40 using SuperDiagVectorType = Matrix<Scalar, ColsAtCompileTimeMinusOne, 1>;
41 using HouseholderUSequenceType =
42 HouseholderSequence<const MatrixType,
43 const internal::remove_all_t<typename Diagonal<const MatrixType, 0>::ConjugateReturnType>>;
44 using HouseholderVSequenceType =
45 HouseholderSequence<const internal::remove_all_t<typename MatrixType::ConjugateReturnType>,
46 Diagonal<const MatrixType, 1>, OnTheRight>;
47
54 UpperBidiagonalization()
55 : m_householder(), m_bidiagonal(m_householder.cols(), m_householder.cols()), m_isInitialized(false) {}
56
57 explicit UpperBidiagonalization(const MatrixType& matrix)
58 : m_householder(matrix.rows(), matrix.cols()),
59 m_bidiagonal(matrix.cols(), matrix.cols()),
60 m_isInitialized(false) {
61 compute(matrix);
62 }
63
64 UpperBidiagonalization(Index rows, Index cols)
65 : m_householder(rows, cols), m_bidiagonal(cols, cols), m_isInitialized(false) {}
66
67 UpperBidiagonalization& compute(const MatrixType& matrix);
68 UpperBidiagonalization& computeUnblocked(const MatrixType& matrix);
69
70 const MatrixType& householder() const { return m_householder; }
71 const BidiagonalType& bidiagonal() const { return m_bidiagonal; }
72
73 const HouseholderUSequenceType householderU() const {
74 eigen_assert(m_isInitialized && "UpperBidiagonalization is not initialized.");
75 return HouseholderUSequenceType(m_householder, m_householder.diagonal().conjugate());
76 }
77
78 const HouseholderVSequenceType householderV() // const here gives nasty errors and i'm lazy
79 {
80 eigen_assert(m_isInitialized && "UpperBidiagonalization is not initialized.");
81 return HouseholderVSequenceType(m_householder.conjugate(), m_householder.const_derived().template diagonal<1>())
82 .setLength(m_householder.cols() - 1)
83 .setShift(1);
84 }
85
86 protected:
87 MatrixType m_householder;
88 BidiagonalType m_bidiagonal;
89 bool m_isInitialized;
90};
91
92// Standard upper bidiagonalization without fancy optimizations
93// This version should be faster for small matrix size
94template <typename MatrixType>
95void upperbidiagonalization_inplace_unblocked(MatrixType& mat, typename MatrixType::RealScalar* diagonal,
96 typename MatrixType::RealScalar* upper_diagonal,
97 typename MatrixType::Scalar* tempData = 0) {
98 using Scalar = typename MatrixType::Scalar;
99
100 Index rows = mat.rows();
101 Index cols = mat.cols();
102
103 using TempType = Matrix<Scalar, Dynamic, 1, ColMajor, MatrixType::MaxRowsAtCompileTime, 1>;
104 TempType tempVector;
105 if (tempData == 0) {
106 tempVector.resize(rows);
107 tempData = tempVector.data();
108 }
109
110 for (Index k = 0; /* breaks at k==cols-1 below */; ++k) {
111 Index remainingRows = rows - k;
112 Index remainingCols = cols - k - 1;
113
114 // construct left householder transform in-place in A
115 mat.col(k).tail(remainingRows).makeHouseholderInPlace(mat.coeffRef(k, k), diagonal[k]);
116 // apply householder transform to remaining part of A on the left
117 mat.bottomRightCorner(remainingRows, remainingCols)
118 .applyHouseholderOnTheLeft(mat.col(k).tail(remainingRows - 1), mat.coeff(k, k), tempData);
119
120 if (k == cols - 1) break;
121
122 // construct right householder transform in-place in mat
123 mat.row(k).tail(remainingCols).makeHouseholderInPlace(mat.coeffRef(k, k + 1), upper_diagonal[k]);
124 // apply householder transform to remaining part of mat on the left
125 mat.bottomRightCorner(remainingRows - 1, remainingCols)
126 .applyHouseholderOnTheRight(mat.row(k).tail(remainingCols - 1).adjoint(), mat.coeff(k, k + 1), tempData);
127 }
128}
129
147template <typename MatrixType>
148void upperbidiagonalization_blocked_helper(
149 MatrixType& A, typename MatrixType::RealScalar* diagonal, typename MatrixType::RealScalar* upper_diagonal, Index bs,
150 Ref<Matrix<typename MatrixType::Scalar, Dynamic, Dynamic, traits<MatrixType>::Flags & RowMajorBit> > X,
151 Ref<Matrix<typename MatrixType::Scalar, Dynamic, Dynamic, traits<MatrixType>::Flags & RowMajorBit> > Y) {
152 using Scalar = typename MatrixType::Scalar;
153 using RealScalar = typename MatrixType::RealScalar;
154 using Literal = typename NumTraits<RealScalar>::Literal;
155 static constexpr int StorageOrder = (traits<MatrixType>::Flags & RowMajorBit) ? RowMajor : ColMajor;
156 using ColInnerStride = InnerStride<StorageOrder == ColMajor ? 1 : Dynamic>;
157 using RowInnerStride = InnerStride<StorageOrder == ColMajor ? Dynamic : 1>;
158 using SubColumnType = Ref<Matrix<Scalar, Dynamic, 1>, 0, ColInnerStride>;
159 using SubRowType = Ref<Matrix<Scalar, 1, Dynamic>, 0, RowInnerStride>;
160 using SubMatType = Ref<Matrix<Scalar, Dynamic, Dynamic, StorageOrder>>;
161
162 Index brows = A.rows();
163 Index bcols = A.cols();
164
165 Scalar tau_u, tau_u_prev(0), tau_v;
166
167 for (Index k = 0; k < bs; ++k) {
168 Index remainingRows = brows - k;
169 Index remainingCols = bcols - k - 1;
170
171 SubMatType X_k1(X.block(k, 0, remainingRows, k));
172 SubMatType V_k1(A.block(k, 0, remainingRows, k));
173
174 // 1 - update the k-th column of A
175 SubColumnType v_k = A.col(k).tail(remainingRows);
176 if (k) {
177 v_k.noalias() -= V_k1 * Y.row(k).head(k).adjoint();
178 v_k.noalias() -= X_k1 * A.col(k).head(k);
179 }
180
181 // 2 - construct left Householder transform in-place
182 v_k.makeHouseholderInPlace(tau_v, diagonal[k]);
183
184 if (k + 1 < bcols) {
185 SubMatType Y_k(Y.block(k + 1, 0, remainingCols, k + 1));
186 SubMatType U_k1(A.block(0, k + 1, k, remainingCols));
187
188 // this eases the application of Householder transformations
189 // A(k,k) will store tau_v later
190 A(k, k) = Scalar(1);
191
192 // 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 )
193 {
194 SubColumnType y_k(Y.col(k).tail(remainingCols));
195
196 // let's use the beginning of column k of Y as a temporary vector
197 SubColumnType tmp(Y.col(k).head(k));
198 y_k.noalias() = A.block(k, k + 1, remainingRows, remainingCols).adjoint() * v_k; // bottleneck
199 tmp.noalias() = V_k1.adjoint() * v_k;
200 y_k.noalias() -= Y_k.leftCols(k) * tmp;
201 tmp.noalias() = X_k1.adjoint() * v_k;
202 y_k.noalias() -= U_k1.adjoint() * tmp;
203 y_k *= numext::conj(tau_v);
204 }
205
206 // 4 - update k-th row of A (it will become u_k)
207 SubRowType u_k(A.row(k).tail(remainingCols));
208 u_k = u_k.conjugate();
209 {
210 u_k.noalias() -= Y_k * A.row(k).head(k + 1).adjoint();
211 if (k) u_k.noalias() -= U_k1.adjoint() * X.row(k).head(k).adjoint();
212 }
213
214 // 5 - construct right Householder transform in-place
215 u_k.makeHouseholderInPlace(tau_u, upper_diagonal[k]);
216
217 // this eases the application of Householder transformations
218 // A(k,k+1) will store tau_u later
219 A(k, k + 1) = Scalar(1);
220
221 // 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 )
222 {
223 SubColumnType x_k(X.col(k).tail(remainingRows - 1));
224
225 // let's use the beginning of column k of X as a temporary vectors
226 // note that tmp0 and tmp1 overlaps
227 SubColumnType tmp0(X.col(k).head(k)), tmp1(X.col(k).head(k + 1));
228
229 x_k.noalias() = A.block(k + 1, k + 1, remainingRows - 1, remainingCols) * u_k.transpose(); // bottleneck
230 tmp0.noalias() = U_k1 * u_k.transpose();
231 x_k.noalias() -= X_k1.bottomRows(remainingRows - 1) * tmp0;
232 tmp1.noalias() = Y_k.adjoint() * u_k.transpose();
233 x_k.noalias() -= A.block(k + 1, 0, remainingRows - 1, k + 1) * tmp1;
234 x_k *= numext::conj(tau_u);
235 tau_u = numext::conj(tau_u);
236 u_k = u_k.conjugate();
237 }
238
239 if (k > 0) A.coeffRef(k - 1, k) = tau_u_prev;
240 tau_u_prev = tau_u;
241 } else
242 A.coeffRef(k - 1, k) = tau_u_prev;
243
244 A.coeffRef(k, k) = tau_v;
245 }
246
247 if (bs < bcols) A.coeffRef(bs - 1, bs) = tau_u_prev;
248
249 // update A22
250 if (bcols > bs && brows > bs) {
251 SubMatType A11(A.bottomRightCorner(brows - bs, bcols - bs));
252 SubMatType A10(A.block(bs, 0, brows - bs, bs));
253 SubMatType A01(A.block(0, bs, bs, bcols - bs));
254 Scalar tmp = A01(bs - 1, 0);
255 A01(bs - 1, 0) = Literal(1);
256 A11.noalias() -= A10 * Y.topLeftCorner(bcols, bs).bottomRows(bcols - bs).adjoint();
257 A11.noalias() -= X.topLeftCorner(brows, bs).bottomRows(brows - bs) * A01;
258 A01(bs - 1, 0) = tmp;
259 }
260}
261
269template <typename MatrixType, typename BidiagType>
270void upperbidiagonalization_inplace_blocked(MatrixType& A, BidiagType& bidiagonal, Index maxBlockSize = 16,
271 typename MatrixType::Scalar* /*tempData*/ = 0) {
272 using Scalar = typename MatrixType::Scalar;
273 using BlockType = Block<MatrixType, Dynamic, Dynamic>;
274
275 Index rows = A.rows();
276 Index cols = A.cols();
277 Index size = (std::min)(rows, cols);
278
279 // X and Y are work space
280 static constexpr int StorageOrder = (traits<MatrixType>::Flags & RowMajorBit) ? RowMajor : ColMajor;
281 Matrix<Scalar, MatrixType::RowsAtCompileTime, Dynamic, StorageOrder, MatrixType::MaxRowsAtCompileTime> X(
282 rows, maxBlockSize);
283 Matrix<Scalar, MatrixType::ColsAtCompileTime, Dynamic, StorageOrder, MatrixType::MaxColsAtCompileTime> Y(
284 cols, maxBlockSize);
285 Index blockSize = (std::min)(maxBlockSize, size);
286
287 Index k = 0;
288 for (k = 0; k < size; k += blockSize) {
289 Index bs = (std::min)(size - k, blockSize); // actual size of the block
290 Index brows = rows - k; // rows of the block
291 Index bcols = cols - k; // columns of the block
292
293 // partition the matrix A:
294 //
295 // | A00 A01 A02 |
296 // | |
297 // A = | A10 A11 A12 |
298 // | |
299 // | A20 A21 A22 |
300 //
301 // where A11 is a bs x bs diagonal block,
302 // and let:
303 // | A11 A12 |
304 // B = | |
305 // | A21 A22 |
306
307 BlockType B = A.block(k, k, brows, bcols);
308
309 // This stage performs the bidiagonalization of A11, A21, A12, and updating of A22.
310 // Finally, the algorithm continue on the updated A22.
311 //
312 // However, if B is too small, or A22 empty, then let's use an unblocked strategy
313
314 auto upper_diagonal = bidiagonal.template diagonal<1>();
315 typename MatrixType::RealScalar* upper_diagonal_ptr =
316 upper_diagonal.size() > 0 ? &upper_diagonal.coeffRef(k) : nullptr;
317
318 if (k + bs == cols || bcols < 2 * blockSize) // fall back to unblocked for small trailing submatrices
319 {
320 upperbidiagonalization_inplace_unblocked(B, &(bidiagonal.template diagonal<0>().coeffRef(k)), upper_diagonal_ptr,
321 X.data());
322 break; // We're done
323 } else {
324 upperbidiagonalization_blocked_helper<BlockType>(B, &(bidiagonal.template diagonal<0>().coeffRef(k)),
325 upper_diagonal_ptr, bs, X.topLeftCorner(brows, bs),
326 Y.topLeftCorner(bcols, bs));
327 }
328 }
329}
330
331template <typename MatrixType_>
332UpperBidiagonalization<MatrixType_>& UpperBidiagonalization<MatrixType_>::computeUnblocked(const MatrixType_& matrix) {
333 Index rows = matrix.rows();
334 Index cols = matrix.cols();
335 EIGEN_ONLY_USED_FOR_DEBUG(cols);
336
337 eigen_assert(rows >= cols && "UpperBidiagonalization is only for matrices satisfying rows>=cols.");
338
339 m_householder = matrix;
340
341 ColVectorType temp(rows);
342
343 upperbidiagonalization_inplace_unblocked(m_householder, &(m_bidiagonal.template diagonal<0>().coeffRef(0)),
344 &(m_bidiagonal.template diagonal<1>().coeffRef(0)), temp.data());
345
346 m_isInitialized = true;
347 return *this;
348}
349
350template <typename MatrixType_>
351UpperBidiagonalization<MatrixType_>& UpperBidiagonalization<MatrixType_>::compute(const MatrixType_& matrix) {
352 Index rows = matrix.rows();
353 Index cols = matrix.cols();
354 EIGEN_ONLY_USED_FOR_DEBUG(rows);
355 EIGEN_ONLY_USED_FOR_DEBUG(cols);
356
357 eigen_assert(rows >= cols && "UpperBidiagonalization is only for matrices satisfying rows>=cols.");
358
359 m_householder = matrix;
360 upperbidiagonalization_inplace_blocked(m_householder, m_bidiagonal);
361
362 m_isInitialized = true;
363 return *this;
364}
365
366} // end namespace internal
367
368} // end namespace Eigen
369
370#endif // EIGEN_BIDIAGONALIZATION_H
@ ColMajor
Definition Constants.h:319
@ RowMajor
Definition Constants.h:321
@ OnTheRight
Definition Constants.h:334
constexpr unsigned int RowMajorBit
Definition Constants.h:71