Eigen  5.0.1
 
Loading...
Searching...
No Matches
BDCSVD.h
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// Copyright (C) 2013 Jitse Niesen <jitse@maths.leeds.ac.uk>
14// Copyright (C) 2014-2017 Gael Guennebaud <gael.guennebaud@inria.fr>
15//
16// Source Code Form is subject to the terms of the Mozilla
17// Public License v. 2.0. If a copy of the MPL was not distributed
18// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
19// SPDX-License-Identifier: MPL-2.0
20
21#ifndef EIGEN_BDCSVD_H
22#define EIGEN_BDCSVD_H
23
24// IWYU pragma: private
25#include "./InternalHeaderCheck.h"
26
27// Internal D&C implementation, templated only on RealScalar.
28#include "BDCSVDImpl.h"
29
30namespace Eigen {
31
32template <typename MatrixType_, int Options>
33class BDCSVD;
34
35namespace internal {
36
37template <typename MatrixType_, int Options>
38struct traits<BDCSVD<MatrixType_, Options> > : svd_traits<MatrixType_, Options> {
39 using MatrixType = MatrixType_;
40};
41
42} // end namespace internal
43
75template <typename MatrixType_, int Options_>
76class BDCSVD : public SVDBase<BDCSVD<MatrixType_, Options_> > {
77 using Base = SVDBase<BDCSVD>;
78
79 public:
80 using Base::cols;
81 using Base::computeU;
82 using Base::computeV;
83 using Base::diagSize;
84 using Base::rows;
85
86 using MatrixType = MatrixType_;
87 using Scalar = typename Base::Scalar;
88 using RealScalar = typename Base::RealScalar;
89 using Literal = typename NumTraits<RealScalar>::Literal;
90 using Index = typename Base::Index;
91 enum {
92 Options = Options_,
93 QRDecomposition = internal::get_qr_preconditioner(Options),
94 ComputationOptions = internal::get_computation_options(Options),
95 RowsAtCompileTime = Base::RowsAtCompileTime,
96 ColsAtCompileTime = Base::ColsAtCompileTime,
97 DiagSizeAtCompileTime = Base::DiagSizeAtCompileTime,
98 MaxRowsAtCompileTime = Base::MaxRowsAtCompileTime,
99 MaxColsAtCompileTime = Base::MaxColsAtCompileTime,
100 MaxDiagSizeAtCompileTime = Base::MaxDiagSizeAtCompileTime,
101 MatrixOptions = Base::MatrixOptions
102 };
103
104 using MatrixUType = typename Base::MatrixUType;
105 using MatrixVType = typename Base::MatrixVType;
106 using SingularValuesType = typename Base::SingularValuesType;
107
110 using VectorType = Matrix<RealScalar, Dynamic, 1>;
111 using ArrayXr = Array<RealScalar, Dynamic, 1>;
112 using ArrayXi = Array<Index, 1, Dynamic>;
113 using ArrayRef = Ref<ArrayXr>;
114 using IndicesRef = Ref<ArrayXi>;
115
121 BDCSVD() : m_isTranspose(false), m_numIters(0) {}
122
129 BDCSVD(Index rows, Index cols) : m_numIters(0) { allocate(rows, cols, internal::get_computation_options(Options)); }
130
147 EIGEN_DEPRECATED_WITH_REASON("Options should be specified using the class template parameter.")
148 BDCSVD(Index rows, Index cols, unsigned int computationOptions) : m_numIters(0) {
149 internal::check_svd_options_assertions<MatrixType, Options>(computationOptions, rows, cols);
150 allocate(rows, cols, computationOptions);
151 }
152
158 template <typename Derived>
159 BDCSVD(const MatrixBase<Derived>& matrix) : m_numIters(0) {
160 compute_impl(matrix, internal::get_computation_options(Options));
161 }
162
172 template <typename DerivedD, typename DerivedE>
173 BDCSVD(const MatrixBase<DerivedD>& diagonal, const MatrixBase<DerivedE>& superdiagonal) : m_numIters(0) {
174 compute_bidiagonal_impl(diagonal, superdiagonal, internal::get_computation_options(Options));
175 }
176
189 template <typename Derived>
190 EIGEN_DEPRECATED_WITH_REASON("Options should be specified using the class template parameter.")
191 BDCSVD(const MatrixBase<Derived>& matrix, unsigned int computationOptions) : m_numIters(0) {
192 internal::check_svd_options_assertions<MatrixType, Options>(computationOptions, matrix.rows(), matrix.cols());
193 compute_impl(matrix, computationOptions);
194 }
195
201 template <typename Derived>
203 return compute_impl(matrix, m_computationOptions);
204 }
205
215 template <typename Derived>
216 EIGEN_DEPRECATED_WITH_REASON("Options should be specified using the class template parameter.")
217 BDCSVD& compute(const MatrixBase<Derived>& matrix, unsigned int computationOptions) {
218 internal::check_svd_options_assertions<MatrixType, Options>(computationOptions, matrix.rows(), matrix.cols());
219 return compute_impl(matrix, computationOptions);
220 }
221
231 template <typename DerivedD, typename DerivedE>
232 BDCSVD& compute(const MatrixBase<DerivedD>& diagonal, const MatrixBase<DerivedE>& superdiagonal) {
233 return compute_bidiagonal_impl(diagonal, superdiagonal, m_computationOptions);
234 }
235
236 void setSwitchSize(int s) {
237 eigen_assert(s >= 3 && "BDCSVD the size of the algo switch has to be at least 3.");
238 if (s == m_impl.algoSwap()) return;
239 m_impl.setAlgoSwap(s);
240 // smallSvd is only allocated for sizes below the switch, so the next compute must reallocate.
241 m_isAllocated = false;
242 }
243
244 private:
245 template <typename Derived>
246 BDCSVD& compute_impl(const MatrixBase<Derived>& matrix, unsigned int computationOptions);
247 template <typename Derived>
248 BDCSVD& compute_impl(const MatrixBase<Derived>& matrix, unsigned int computationOptions, internal::true_type);
249 template <typename Derived>
250 BDCSVD& compute_impl(const MatrixBase<Derived>& matrix, unsigned int computationOptions, internal::false_type);
251 template <typename DerivedD, typename DerivedE>
252 BDCSVD& compute_bidiagonal_impl(const MatrixBase<DerivedD>& diagonal, const MatrixBase<DerivedE>& superdiagonal,
253 unsigned int computationOptions);
254 template <typename HouseholderU, typename HouseholderV, typename NaiveU, typename NaiveV>
255 void copyUV(const HouseholderU& householderU, const HouseholderV& householderV, const NaiveU& naiveU,
256 const NaiveV& naivev);
257
258 protected:
259 void allocate(Index rows, Index cols, unsigned int computationOptions);
260 void allocate_small(Index rows, Index cols, unsigned int computationOptions);
261 internal::bdcsvd_impl<RealScalar> m_impl;
262 bool m_isTranspose, m_useQrDecomp;
263 // Only PreconditionSquareMatrix is forwarded: BDCSVD's QR bits configure its own R-bidiagonalization, and smallSvd
264 // needs its QR preconditioner to reduce non-square inputs. The unitaries are requested at runtime in allocate().
265 JacobiSVD<MatrixX, (Options & int(PreconditionSquareMatrix))> smallSvd;
266 HouseholderQR<MatrixX> qrDecomp;
267 internal::UpperBidiagonalization<MatrixX> bid;
268 MatrixX copyWorkspace;
269 MatrixX reducedTriangle;
270 // Reused workspace for HouseholderSequence::applyThisOnTheLeft in copyUV().
271 // Without this, each apply allocates a fresh row vector.
272 Matrix<Scalar, 1, Dynamic, RowMajor> m_householderWorkspace;
273
274 using Base::m_computationOptions;
275 using Base::m_computeThinU;
276 using Base::m_computeThinV;
277 using Base::m_info;
278 using Base::m_isAllocated;
279 using Base::m_isInitialized;
280 using Base::m_matrixU;
281 using Base::m_matrixV;
282 using Base::m_nonzeroSingularValues;
283 using Base::m_singularValues;
284
285 public:
286 int m_numIters;
287}; // end class BDCSVD
288
289// Method to allocate and initialize matrix and attributes
290template <typename MatrixType, int Options>
291void BDCSVD<MatrixType, Options>::allocate(Index rows, Index cols, unsigned int computationOptions) {
292 if (Base::allocate(rows, cols, computationOptions)) return;
293
294 if (cols < m_impl.algoSwap())
295 smallSvd.allocate(rows, cols, internal::get_computation_options(Options | computationOptions));
296
297 m_isTranspose = (cols > rows);
298
299 bool compU = computeV();
300 bool compV = computeU();
301 if (m_isTranspose) std::swap(compU, compV);
302
303 m_impl.allocate(diagSize(), compU, compV);
304
305 // kMinAspectRatio is the crossover point that determines if we perform R-Bidiagonalization
306 // or bidiagonalize the input matrix directly.
307 // It is based off of LAPACK's dgesdd routine, which uses 11.0/6.0
308 // we use a larger scalar to prevent a regression for relatively square matrices.
309 constexpr Index kMinAspectRatio = 4;
310 constexpr bool disableQrDecomp = static_cast<int>(QRDecomposition) == static_cast<int>(DisableQRDecomposition);
311 m_useQrDecomp = !disableQrDecomp && ((rows / kMinAspectRatio >= cols) || (cols / kMinAspectRatio >= rows));
312 if (m_useQrDecomp) {
313 qrDecomp = HouseholderQR<MatrixX>((std::max)(rows, cols), (std::min)(rows, cols));
314 reducedTriangle = MatrixX(diagSize(), diagSize());
315 }
316
317 copyWorkspace = MatrixX(m_isTranspose ? cols : rows, m_isTranspose ? rows : cols);
318 bid = internal::UpperBidiagonalization<MatrixX>(m_useQrDecomp ? diagSize() : copyWorkspace.rows(),
319 m_useQrDecomp ? diagSize() : copyWorkspace.cols());
320} // end allocate
321
322template <typename MatrixType, int Options>
323void BDCSVD<MatrixType, Options>::allocate_small(Index rows, Index cols, unsigned int computationOptions) {
324 if (Base::allocate(rows, cols, computationOptions)) return;
325
326 smallSvd.allocate(rows, cols, internal::get_computation_options(Options | computationOptions));
327 m_isTranspose = (cols > rows);
328}
329
330template <typename MatrixType, int Options>
331template <typename Derived>
332BDCSVD<MatrixType, Options>& BDCSVD<MatrixType, Options>::compute_impl(const MatrixBase<Derived>& matrix,
333 unsigned int computationOptions) {
334 EIGEN_STATIC_ASSERT_SAME_MATRIX_SIZE(Derived, MatrixType);
335 EIGEN_STATIC_ASSERT((std::is_same<typename Derived::Scalar, typename MatrixType::Scalar>::value),
336 Input matrix must have the same Scalar type as the BDCSVD object.);
337
338 // setSwitchSize() enforces a minimum of 3, so these types can only use the JacobiSVD fallback.
339 typedef internal::bool_constant<(MaxColsAtCompileTime != Dynamic && MaxColsAtCompileTime < 3)> AlwaysUseSmallSvd;
340 return compute_impl(matrix, computationOptions, AlwaysUseSmallSvd());
341}
342
343template <typename MatrixType, int Options>
344template <typename Derived>
345EIGEN_DONT_INLINE BDCSVD<MatrixType, Options>& BDCSVD<MatrixType, Options>::compute_impl(
346 const MatrixBase<Derived>& matrix, unsigned int computationOptions, internal::true_type) {
347 allocate_small(matrix.rows(), matrix.cols(), computationOptions);
348
349 smallSvd.compute(matrix);
350 m_isInitialized = true;
351 m_info = smallSvd.info();
352 if (m_info == Success || m_info == NoConvergence) {
353 if (computeU()) m_matrixU = smallSvd.matrixU();
354 if (computeV()) m_matrixV = smallSvd.matrixV();
355 m_singularValues = smallSvd.singularValues();
356 m_nonzeroSingularValues = smallSvd.nonzeroSingularValues();
357 }
358 return *this;
359}
360
361template <typename MatrixType, int Options>
362template <typename Derived>
363EIGEN_DONT_INLINE BDCSVD<MatrixType, Options>& BDCSVD<MatrixType, Options>::compute_impl(
364 const MatrixBase<Derived>& matrix, unsigned int computationOptions, internal::false_type) {
365 using std::abs;
366
367 allocate(matrix.rows(), matrix.cols(), computationOptions);
368
369 const RealScalar considerZero = (std::numeric_limits<RealScalar>::min)();
370
371 //**** step -1 - If the problem is too small, directly falls back to JacobiSVD and return
372 if (matrix.cols() < m_impl.algoSwap()) {
373 smallSvd.compute(matrix);
374 m_isInitialized = true;
375 m_info = smallSvd.info();
376 if (m_info == Success || m_info == NoConvergence) {
377 if (computeU()) m_matrixU = smallSvd.matrixU();
378 if (computeV()) m_matrixV = smallSvd.matrixV();
379 m_singularValues = smallSvd.singularValues();
380 m_nonzeroSingularValues = smallSvd.nonzeroSingularValues();
381 }
382 return *this;
383 }
384
385 //**** step 0 - Copy the input matrix and apply scaling to reduce over/under-flows
386 // A SIMD unit that flushes subnormal inputs reads an all-subnormal matrix as zero; recover its maximum from the
387 // representation so that the scaling still brings it into the normal range.
388 const RealScalar maxCoeff = internal::safe_scaling<RealScalar>::recover_flushed_max_coeff(
389 matrix.derived(), matrix.cwiseAbs().template maxCoeff<PropagateNaN>());
390 if (!(numext::isfinite)(maxCoeff)) {
391 m_isInitialized = true;
392 m_info = InvalidInput;
393 return *this;
394 }
395
396 const auto factors = m_isTranspose
397 ? internal::safe_scaling<RealScalar>::scale_to(copyWorkspace, matrix.adjoint(), maxCoeff)
398 : internal::safe_scaling<RealScalar>::scale_to(copyWorkspace, matrix, maxCoeff);
399
400 //**** step 1 - Bidiagonalization.
401 // If the problem is sufficiently rectangular, we perform R-Bidiagonalization: compute A = Q(R/0)
402 // and then bidiagonalize R. Otherwise, if the problem is relatively square, we
403 // bidiagonalize the input matrix directly.
404 if (m_useQrDecomp) {
405 qrDecomp.compute(copyWorkspace);
406 reducedTriangle = qrDecomp.matrixQR().topRows(diagSize());
407 reducedTriangle.template triangularView<StrictlyLower>().setZero();
408 bid.compute(reducedTriangle);
409 } else {
410 bid.compute(copyWorkspace);
411 }
412
413 //**** step 2 - Divide & Conquer
414 m_impl.naiveU().setZero();
415 m_impl.naiveV().setZero();
416 // The transposed bidiagonal has only the main diagonal and one sub-diagonal;
417 // fill those directly instead of materializing a dense temporary.
418 // Note: BandMatrix::diagonal<N>() const has a latent type bug (returns
419 // Block<CoefficientsType, ...> instead of Block<const CoefficientsType, ...>),
420 // so use the index-based overload which is correctly const-qualified.
421 m_impl.computed().setZero();
422 m_impl.computed().topRows(diagSize()).diagonal() = bid.bidiagonal().diagonal();
423 m_impl.computed().topRows(diagSize()).template diagonal<-1>() = bid.bidiagonal().diagonal(1);
424 m_impl.splitNegligibleSuperdiagonal(diagSize());
425 m_impl.divide(0, diagSize() - 1, 0, 0, 0);
426 m_info = m_impl.info();
427 m_numIters = m_impl.numIters();
428 if (m_info != Success && m_info != NoConvergence) {
429 m_isInitialized = true;
430 return *this;
431 }
432
433 //**** step 3 - Copy singular values and vectors
434 for (int i = 0; i < diagSize(); i++) {
435 RealScalar a = abs(m_impl.computed().coeff(i, i));
436 m_singularValues.coeffRef(i) = a;
437 if (a < considerZero) {
438 m_nonzeroSingularValues = i;
439 m_singularValues.tail(diagSize() - i - 1).setZero();
440 break;
441 } else if (i == diagSize() - 1) {
442 m_nonzeroSingularValues = i + 1;
443 break;
444 }
445 }
446 // Unscaling with maxCoeff keeps singular values that land in the subnormal range under FTZ.
447 internal::safe_scaling<RealScalar>::unscale_in_place(m_singularValues, maxCoeff, factors);
448
449 //**** step 4 - Finalize unitaries U and V
450 if (m_isTranspose)
451 copyUV(bid.householderV(), bid.householderU(), m_impl.naiveV(), m_impl.naiveU());
452 else
453 copyUV(bid.householderU(), bid.householderV(), m_impl.naiveU(), m_impl.naiveV());
454
455 if (m_useQrDecomp) {
456 if (m_isTranspose && computeV())
457 m_matrixV.applyOnTheLeft(qrDecomp.householderQ());
458 else if (!m_isTranspose && computeU())
459 m_matrixU.applyOnTheLeft(qrDecomp.householderQ());
460 }
461
462 m_isInitialized = true;
463 return *this;
464} // end compute
465
466template <typename MatrixType, int Options>
467template <typename HouseholderU, typename HouseholderV, typename NaiveU, typename NaiveV>
468EIGEN_DONT_INLINE void BDCSVD<MatrixType, Options>::copyUV(const HouseholderU& householderU,
469 const HouseholderV& householderV, const NaiveU& naiveU,
470 const NaiveV& naiveV) {
471 // Note exchange of U and V: m_matrixU is set from m_naiveV and vice versa.
472 // Cast the diagSize x diagSize block (rather than the full naive matrix) to avoid materializing
473 // a full-size temporary when Scalar != RealScalar; reuse m_householderWorkspace across the two
474 // applyThisOnTheLeft calls so each does not allocate a fresh row vector.
475 if (computeU()) {
476 Index Ucols = m_computeThinU ? diagSize() : rows();
477 m_matrixU = MatrixX::Identity(rows(), Ucols);
478 m_matrixU.topLeftCorner(diagSize(), diagSize()) =
479 naiveV.topLeftCorner(diagSize(), diagSize()).template cast<Scalar>();
480 if (m_useQrDecomp) {
481 auto sub = m_matrixU.topLeftCorner(householderU.cols(), diagSize());
482 householderU.applyThisOnTheLeft(sub, m_householderWorkspace);
483 } else {
484 householderU.applyThisOnTheLeft(m_matrixU, m_householderWorkspace);
485 }
486 }
487 if (computeV()) {
488 Index Vcols = m_computeThinV ? diagSize() : cols();
489 m_matrixV = MatrixX::Identity(cols(), Vcols);
490 m_matrixV.topLeftCorner(diagSize(), diagSize()) =
491 naiveU.topLeftCorner(diagSize(), diagSize()).template cast<Scalar>();
492 if (m_useQrDecomp) {
493 auto sub = m_matrixV.topLeftCorner(householderV.cols(), diagSize());
494 householderV.applyThisOnTheLeft(sub, m_householderWorkspace);
495 } else {
496 householderV.applyThisOnTheLeft(m_matrixV, m_householderWorkspace);
497 }
498 }
499}
500
501template <typename MatrixType, int Options>
502template <typename DerivedD, typename DerivedE>
503EIGEN_DONT_INLINE BDCSVD<MatrixType, Options>& BDCSVD<MatrixType, Options>::compute_bidiagonal_impl(
504 const MatrixBase<DerivedD>& diagonal, const MatrixBase<DerivedE>& superdiagonal, unsigned int computationOptions) {
505 EIGEN_STATIC_ASSERT(DerivedD::IsVectorAtCompileTime, THIS_METHOD_IS_ONLY_FOR_VECTORS);
506 EIGEN_STATIC_ASSERT(DerivedE::IsVectorAtCompileTime, THIS_METHOD_IS_ONLY_FOR_VECTORS);
507 EIGEN_STATIC_ASSERT((NumTraits<typename DerivedD::Scalar>::IsComplex == 0),
508 THIS_FUNCTION_IS_NOT_FOR_COMPLEX_VALUED_MATRICES);
509 EIGEN_STATIC_ASSERT((NumTraits<typename DerivedE::Scalar>::IsComplex == 0),
510 THIS_FUNCTION_IS_NOT_FOR_COMPLEX_VALUED_MATRICES);
511
512 using std::abs;
513 const Index n = diagonal.size();
514 eigen_assert((n == 0 || superdiagonal.size() == n - 1) && "superdiagonal must have size diagonal.size() - 1");
515
516 // For a bidiagonal matrix, rows == cols == n.
517 allocate(n, n, computationOptions);
518
519 if (n == 0) {
520 m_isInitialized = true;
521 m_info = Success;
522 m_nonzeroSingularValues = 0;
523 return *this;
524 }
525
526 // Check for non-finite inputs. The rescan recovers an all-subnormal input that a flushing SIMD unit reads as zero.
527 const RealScalar diagScale = internal::safe_scaling<RealScalar>::recover_flushed_max_coeff(
528 diagonal.derived(), diagonal.cwiseAbs().template maxCoeff<PropagateNaN>());
529 const RealScalar superdiagScale =
530 n > 1 ? internal::safe_scaling<RealScalar>::recover_flushed_max_coeff(
531 superdiagonal.derived(), superdiagonal.cwiseAbs().template maxCoeff<PropagateNaN>())
532 : RealScalar(0);
533 const RealScalar maxCoeff = internal::max_preserving_subnormals(diagScale, superdiagScale);
534 if (!(numext::isfinite)(maxCoeff)) {
535 m_isInitialized = true;
536 m_info = InvalidInput;
537 return *this;
538 }
539
540 const RealScalar considerZero = (std::numeric_limits<RealScalar>::min)();
541 //**** Small problem: build dense bidiagonal and delegate to JacobiSVD.
542 if (n < m_impl.algoSwap()) {
543 // Build the dense upper bidiagonal matrix.
544 MatrixX B = MatrixX::Zero(n, n);
545 auto diagonalDest = B.diagonal();
546 const auto factors =
547 internal::safe_scaling<RealScalar>::scale_to(diagonalDest, diagonal.template cast<Scalar>(), maxCoeff);
548 if (n > 1) {
549 auto superdiagonalDest = B.diagonal(1);
550 internal::safe_scaling<RealScalar>::scale_to(superdiagonalDest, superdiagonal.template cast<Scalar>(), maxCoeff,
551 factors);
552 }
553 smallSvd.compute(B);
554 m_isInitialized = true;
555 m_info = smallSvd.info();
556 if (m_info == Success || m_info == NoConvergence) {
557 internal::safe_scaling<RealScalar>::unscale_to(m_singularValues, smallSvd.singularValues(), maxCoeff, factors);
558 m_nonzeroSingularValues = smallSvd.nonzeroSingularValues();
559 if (computeU()) m_matrixU = smallSvd.matrixU();
560 if (computeV()) m_matrixV = smallSvd.matrixV();
561 }
562 return *this;
563 }
564
565 //**** Fill m_computed with transposed bidiagonal format.
566 // D&C operates on B^T: m_computed(i,i) = d_i, m_computed(i+1,i) = e_i.
567 m_impl.naiveU().setZero();
568 m_impl.naiveV().setZero();
569 m_impl.computed().setZero();
570 auto diagonalDest = m_impl.computed().diagonal();
571 const auto factors =
572 internal::safe_scaling<RealScalar>::scale_to(diagonalDest, diagonal.template cast<RealScalar>(), maxCoeff);
573 if (n > 1) {
574 auto superdiagonalDest = m_impl.computed().template diagonal<-1>().head(n - 1);
575 internal::safe_scaling<RealScalar>::scale_to(superdiagonalDest, superdiagonal.template cast<RealScalar>(), maxCoeff,
576 factors);
577 }
578
579 m_isTranspose = false;
580
581 //**** Run D&C.
582 m_impl.splitNegligibleSuperdiagonal(n);
583 m_impl.divide(0, n - 1, 0, 0, 0);
584 m_info = m_impl.info();
585 m_numIters = m_impl.numIters();
586 if (m_info != Success && m_info != NoConvergence) {
587 m_isInitialized = true;
588 return *this;
589 }
590
591 //**** Extract singular values.
592 for (int i = 0; i < diagSize(); i++) {
593 RealScalar a = abs(m_impl.computed().coeff(i, i));
594 m_singularValues.coeffRef(i) = a;
595 if (a < considerZero) {
596 m_nonzeroSingularValues = i;
597 m_singularValues.tail(diagSize() - i - 1).setZero();
598 break;
599 } else if (i == diagSize() - 1) {
600 m_nonzeroSingularValues = i + 1;
601 break;
602 }
603 }
604 // Unscaling with maxCoeff keeps singular values that land in the subnormal range under FTZ.
605 internal::safe_scaling<RealScalar>::unscale_in_place(m_singularValues, maxCoeff, factors);
606
607 //**** Copy U and V directly (no Householder to apply).
608 // D&C computes B^T = naiveU * S * naiveV^T, so B = naiveV * S * naiveU^T.
609 // Thus U_of_B = naiveV, V_of_B = naiveU.
610 if (computeU()) {
611 Index Ucols = m_computeThinU ? diagSize() : rows();
612 m_matrixU = MatrixX::Identity(rows(), Ucols);
613 m_matrixU.topLeftCorner(diagSize(), diagSize()) =
614 m_impl.naiveV().template cast<Scalar>().topLeftCorner(diagSize(), diagSize());
615 }
616 if (computeV()) {
617 Index Vcols = m_computeThinV ? diagSize() : cols();
618 m_matrixV = MatrixX::Identity(cols(), Vcols);
619 m_matrixV.topLeftCorner(diagSize(), diagSize()) =
620 m_impl.naiveU().template cast<Scalar>().topLeftCorner(diagSize(), diagSize());
621 }
622
623 m_isInitialized = true;
624 return *this;
625}
626
633template <typename Derived>
634template <int Options>
635BDCSVD<typename MatrixBase<Derived>::PlainObject, Options> MatrixBase<Derived>::bdcSvd() const {
636 return BDCSVD<PlainObject, Options>(*this);
637}
638
645template <typename Derived>
646template <int Options>
647BDCSVD<typename MatrixBase<Derived>::PlainObject, Options> MatrixBase<Derived>::bdcSvd(
648 unsigned int computationOptions) const {
649 return BDCSVD<PlainObject, Options>(*this, computationOptions);
650}
651
652} // end namespace Eigen
653
654#endif
General-purpose arrays with easy API for coefficient-wise operations.
Definition Array.h:55
class Bidiagonal Divide and Conquer SVD
Definition BDCSVD.h:76
BDCSVD(const MatrixBase< DerivedD > &diagonal, const MatrixBase< DerivedE > &superdiagonal)
Constructor performing the SVD of an upper bidiagonal matrix given its diagonal and superdiagonal.
Definition BDCSVD.h:173
BDCSVD()
Default Constructor.
Definition BDCSVD.h:121
BDCSVD(const MatrixBase< Derived > &matrix)
Constructor performing the decomposition of given matrix, using the custom options specified with the...
Definition BDCSVD.h:159
BDCSVD & compute(const MatrixBase< Derived > &matrix)
Method performing the decomposition of given matrix. Computes Thin/Full unitaries U/V if specified us...
Definition BDCSVD.h:202
BDCSVD & compute(const MatrixBase< DerivedD > &diagonal, const MatrixBase< DerivedE > &superdiagonal)
Compute the SVD of an upper bidiagonal matrix given its diagonal and superdiagonal.
Definition BDCSVD.h:232
BDCSVD(Index rows, Index cols)
Default Constructor with memory preallocation.
Definition BDCSVD.h:129
Householder QR decomposition of a matrix.
Definition HouseholderQR.h:77
Base class for all dense matrices, vectors, and expressions.
Definition MatrixBase.h:53
The matrix class, also used for vectors and row-vectors.
Definition Matrix.h:188
A matrix or vector expression mapping an existing expression.
Definition Ref.h:262
bool computeV() const
Definition SVDBase.h:278
bool computeU() const
Definition SVDBase.h:276
@ DisableQRDecomposition
Definition Constants.h:434
@ PreconditionSquareMatrix
Definition Constants.h:437
@ InvalidInput
Definition Constants.h:464
@ Success
Definition Constants.h:457
@ NoConvergence
Definition Constants.h:461
Matrix< Type, Dynamic, Dynamic > MatrixX
Dynamic×Dynamic matrix of type Type.
Definition Matrix.h:524
Eigen::Index Index
The interface type of indices.
Definition EigenBase.h:44