Eigen  5.0.1
 
Loading...
Searching...
No Matches
SVDBase.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2009-2010 Benoit Jacob <jacob.benoit.1@gmail.com>
5// Copyright (C) 2014 Gael Guennebaud <gael.guennebaud@inria.fr>
6//
7// Copyright (C) 2013 Gauthier Brun <brun.gauthier@gmail.com>
8// Copyright (C) 2013 Nicolas Carre <nicolas.carre@ensimag.fr>
9// Copyright (C) 2013 Jean Ceccato <jean.ceccato@ensimag.fr>
10// Copyright (C) 2013 Pierre Zoppitelli <pierre.zoppitelli@ensimag.fr>
11//
12// This Source Code Form is subject to the terms of the Mozilla
13// Public License v. 2.0. If a copy of the MPL was not distributed
14// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
15// SPDX-License-Identifier: MPL-2.0
16
17#ifndef EIGEN_SVDBASE_H
18#define EIGEN_SVDBASE_H
19
20// IWYU pragma: private
21#include "./InternalHeaderCheck.h"
22
23namespace Eigen {
24
25namespace internal {
26
27enum OptionsMasks {
30 ComputationOptionsBits = ComputeThinU | ComputeFullU | ComputeThinV | ComputeFullV
31};
32
33constexpr int get_qr_preconditioner(int options) { return options & QRPreconditionerBits; }
34
35constexpr int get_computation_options(int options) { return options & ComputationOptionsBits; }
36
37constexpr bool should_svd_precondition_square_matrix(int options) { return (options & PreconditionSquareMatrix) != 0; }
38
39constexpr bool should_svd_compute_thin_u(int options) { return (options & ComputeThinU) != 0; }
40constexpr bool should_svd_compute_full_u(int options) { return (options & ComputeFullU) != 0; }
41constexpr bool should_svd_compute_thin_v(int options) { return (options & ComputeThinV) != 0; }
42constexpr bool should_svd_compute_full_v(int options) { return (options & ComputeFullV) != 0; }
43
44template <typename MatrixType, int Options>
45void check_svd_options_assertions(unsigned int computationOptions, Index rows, Index cols) {
46 EIGEN_STATIC_ASSERT((Options & ComputationOptionsBits) == 0,
47 "SVDBase: Cannot request U or V using both static and runtime options, even if they match. "
48 "Requesting unitaries at runtime is DEPRECATED: "
49 "Prefer requesting unitaries statically, using the Options template parameter.");
50 eigen_assert(
51 !(should_svd_compute_thin_u(computationOptions) && cols < rows && MatrixType::RowsAtCompileTime != Dynamic) &&
52 !(should_svd_compute_thin_v(computationOptions) && rows < cols && MatrixType::ColsAtCompileTime != Dynamic) &&
53 "SVDBase: If thin U is requested at runtime, your matrix must have more rows than columns or a dynamic number of "
54 "rows."
55 "Similarly, if thin V is requested at runtime, you matrix must have more columns than rows or a dynamic number "
56 "of columns.");
57 (void)computationOptions;
58 (void)rows;
59 (void)cols;
60}
61
62template <typename Derived>
63struct traits<SVDBase<Derived> > : traits<Derived> {
64 using XprKind = MatrixXpr;
65 using StorageKind = SolverStorage;
66 using StorageIndex = int;
67 enum { Flags = 0 };
68};
69
70template <typename MatrixType, int Options_>
71struct svd_traits : traits<MatrixType> {
72 static constexpr int Options = Options_;
73 static constexpr bool ShouldComputeFullU = internal::should_svd_compute_full_u(Options);
74 static constexpr bool ShouldComputeThinU = internal::should_svd_compute_thin_u(Options);
75 static constexpr bool ShouldComputeFullV = internal::should_svd_compute_full_v(Options);
76 static constexpr bool ShouldComputeThinV = internal::should_svd_compute_thin_v(Options);
77 enum {
78 DiagSizeAtCompileTime =
79 internal::min_size_prefer_dynamic(MatrixType::RowsAtCompileTime, MatrixType::ColsAtCompileTime),
80 MaxDiagSizeAtCompileTime =
81 internal::min_size_prefer_dynamic(MatrixType::MaxRowsAtCompileTime, MatrixType::MaxColsAtCompileTime),
82 MatrixUColsAtCompileTime = ShouldComputeThinU ? DiagSizeAtCompileTime : MatrixType::RowsAtCompileTime,
83 MatrixVColsAtCompileTime = ShouldComputeThinV ? DiagSizeAtCompileTime : MatrixType::ColsAtCompileTime,
84 MatrixUMaxColsAtCompileTime = ShouldComputeThinU ? MaxDiagSizeAtCompileTime : MatrixType::MaxRowsAtCompileTime,
85 MatrixVMaxColsAtCompileTime = ShouldComputeThinV ? MaxDiagSizeAtCompileTime : MatrixType::MaxColsAtCompileTime
86 };
87};
88} // namespace internal
89
121template <typename Derived>
122class SVDBase : public SolverBase<SVDBase<Derived> > {
123 public:
124 template <typename Derived_>
125 friend struct internal::solve_assertion;
126
127 using MatrixType = typename internal::traits<Derived>::MatrixType;
128 using Scalar = typename MatrixType::Scalar;
129 using RealScalar = typename NumTraits<typename MatrixType::Scalar>::Real;
130 using StorageIndex = typename Eigen::internal::traits<SVDBase>::StorageIndex;
131
132 static constexpr bool ShouldComputeFullU = internal::traits<Derived>::ShouldComputeFullU;
133 static constexpr bool ShouldComputeThinU = internal::traits<Derived>::ShouldComputeThinU;
134 static constexpr bool ShouldComputeFullV = internal::traits<Derived>::ShouldComputeFullV;
135 static constexpr bool ShouldComputeThinV = internal::traits<Derived>::ShouldComputeThinV;
136
137 enum {
138 RowsAtCompileTime = MatrixType::RowsAtCompileTime,
139 ColsAtCompileTime = MatrixType::ColsAtCompileTime,
140 DiagSizeAtCompileTime = internal::min_size_prefer_dynamic(RowsAtCompileTime, ColsAtCompileTime),
141 MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
142 MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime,
143 MaxDiagSizeAtCompileTime = internal::min_size_prefer_fixed(MaxRowsAtCompileTime, MaxColsAtCompileTime),
144 MatrixOptions = internal::traits<MatrixType>::Options,
145 MatrixUColsAtCompileTime = internal::traits<Derived>::MatrixUColsAtCompileTime,
146 MatrixVColsAtCompileTime = internal::traits<Derived>::MatrixVColsAtCompileTime,
147 MatrixUMaxColsAtCompileTime = internal::traits<Derived>::MatrixUMaxColsAtCompileTime,
148 MatrixVMaxColsAtCompileTime = internal::traits<Derived>::MatrixVMaxColsAtCompileTime
149 };
150
151 EIGEN_STATIC_ASSERT(!(ShouldComputeFullU && ShouldComputeThinU), "SVDBase: Cannot request both full and thin U")
152 EIGEN_STATIC_ASSERT(!(ShouldComputeFullV && ShouldComputeThinV), "SVDBase: Cannot request both full and thin V")
153
154 using MatrixUType =
155 typename internal::make_proper_matrix_type<Scalar, RowsAtCompileTime, MatrixUColsAtCompileTime, MatrixOptions,
156 MaxRowsAtCompileTime, MatrixUMaxColsAtCompileTime>::type;
157 using MatrixVType =
158 typename internal::make_proper_matrix_type<Scalar, ColsAtCompileTime, MatrixVColsAtCompileTime, MatrixOptions,
159 MaxColsAtCompileTime, MatrixVMaxColsAtCompileTime>::type;
160
161 using SingularValuesType = typename internal::plain_diag_type<MatrixType, RealScalar>::type;
162
163 Derived& derived() { return *static_cast<Derived*>(this); }
164 const Derived& derived() const { return *static_cast<const Derived*>(this); }
165
176 const MatrixUType& matrixU() const {
177 _check_compute_assertions();
178 eigen_assert(computeU() && "This SVD decomposition didn't compute U. Did you ask for it?");
179 return m_matrixU;
180 }
181
192 const MatrixVType& matrixV() const {
193 _check_compute_assertions();
194 eigen_assert(computeV() && "This SVD decomposition didn't compute V. Did you ask for it?");
195 return m_matrixV;
196 }
197
203 const SingularValuesType& singularValues() const {
204 _check_compute_assertions();
205 return m_singularValues;
206 }
207
210 _check_compute_assertions();
211 return m_nonzeroSingularValues;
212 }
213
220 inline Index rank() const {
221 using std::abs;
222 _check_compute_assertions();
223 if (m_singularValues.size() == 0) return 0;
224 RealScalar premultiplied_threshold =
225 numext::maxi<RealScalar>(m_singularValues.coeff(0) * threshold(), (std::numeric_limits<RealScalar>::min)());
226 Index i = m_nonzeroSingularValues - 1;
227 while (i >= 0 && m_singularValues.coeff(i) < premultiplied_threshold) --i;
228 return i + 1;
229 }
230
245 Derived& setThreshold(const RealScalar& threshold) {
246 m_usePrescribedThreshold = true;
247 m_prescribedThreshold = threshold;
248 return derived();
249 }
250
259 Derived& setThreshold(Default_t) {
260 m_usePrescribedThreshold = false;
261 return derived();
262 }
263
268 RealScalar threshold() const {
269 eigen_assert(m_isInitialized || m_usePrescribedThreshold);
270 // this temporary is needed to workaround a MSVC issue
271 Index diagSize = (std::max<Index>)(1, m_diagSize);
272 return m_usePrescribedThreshold ? m_prescribedThreshold : RealScalar(diagSize) * NumTraits<Scalar>::epsilon();
273 }
274
276 inline bool computeU() const { return m_computeFullU || m_computeThinU; }
278 inline bool computeV() const { return m_computeFullV || m_computeThinV; }
279
280 inline Index rows() const { return m_rows.value(); }
281 inline Index cols() const { return m_cols.value(); }
282 inline Index diagSize() const { return m_diagSize.value(); }
283
284#ifdef EIGEN_PARSED_BY_DOXYGEN
295 template <typename Rhs>
297#endif
298
303 EIGEN_DEVICE_FUNC ComputationInfo info() const {
304 eigen_assert(m_isInitialized && "SVD is not initialized.");
305 return m_info;
306 }
307
308#ifndef EIGEN_PARSED_BY_DOXYGEN
309 template <typename RhsType, typename DstType>
310 void _solve_impl(const RhsType& rhs, DstType& dst) const;
311
312 template <bool Conjugate, typename RhsType, typename DstType>
313 void _solve_impl_transposed(const RhsType& rhs, DstType& dst) const;
314#endif
315
316 protected:
317 EIGEN_STATIC_ASSERT_NON_INTEGER(Scalar)
318
319 void _check_compute_assertions() const { eigen_assert(m_isInitialized && "SVD is not initialized."); }
320
321 template <bool Transpose_, typename Rhs>
322 void _check_solve_assertion(const Rhs& b) const {
323 EIGEN_ONLY_USED_FOR_DEBUG(b);
324 _check_compute_assertions();
325 eigen_assert(computeU() && computeV() &&
326 "SVDBase::solve(): Both unitaries U and V are required to be computed (thin unitaries suffice).");
327 eigen_assert((Transpose_ ? cols() : rows()) == b.rows() &&
328 "SVDBase::solve(): invalid number of rows of the right hand side matrix b");
329 }
330
331 // return true if already allocated
332 bool allocate(Index rows, Index cols, unsigned int computationOptions);
333
334 MatrixUType m_matrixU;
335 MatrixVType m_matrixV;
336 SingularValuesType m_singularValues;
337 ComputationInfo m_info;
338 bool m_isInitialized, m_isAllocated, m_usePrescribedThreshold;
339 bool m_computeFullU, m_computeThinU;
340 bool m_computeFullV, m_computeThinV;
341 unsigned int m_computationOptions;
342 Index m_nonzeroSingularValues;
343 internal::variable_if_dynamic<Index, RowsAtCompileTime> m_rows;
344 internal::variable_if_dynamic<Index, ColsAtCompileTime> m_cols;
345 internal::variable_if_dynamic<Index, DiagSizeAtCompileTime> m_diagSize;
346 RealScalar m_prescribedThreshold;
347
353 : m_matrixU(MatrixUType()),
354 m_matrixV(MatrixVType()),
355 m_singularValues(SingularValuesType()),
356 m_info(Success),
357 m_isInitialized(false),
358 m_isAllocated(false),
359 m_usePrescribedThreshold(false),
360 m_computeFullU(ShouldComputeFullU),
361 m_computeThinU(ShouldComputeThinU),
362 m_computeFullV(ShouldComputeFullV),
363 m_computeThinV(ShouldComputeThinV),
364 m_computationOptions(internal::traits<Derived>::Options),
365 m_nonzeroSingularValues(0),
366 m_rows(RowsAtCompileTime),
367 m_cols(ColsAtCompileTime),
368 m_diagSize(DiagSizeAtCompileTime),
369 m_prescribedThreshold(0) {}
370};
371
372#ifndef EIGEN_PARSED_BY_DOXYGEN
373template <typename Derived>
374template <typename RhsType, typename DstType>
375void SVDBase<Derived>::_solve_impl(const RhsType& rhs, DstType& dst) const {
376 // A = U S V^*
377 // So A^{-1} = V S^{-1} U^*
378
379 Matrix<typename RhsType::Scalar, Dynamic, RhsType::ColsAtCompileTime, 0, MatrixType::MaxRowsAtCompileTime,
380 RhsType::MaxColsAtCompileTime>
381 tmp;
382 Index l_rank = rank();
383 tmp.noalias() = m_matrixU.leftCols(l_rank).adjoint() * rhs;
384 tmp = m_singularValues.head(l_rank).asDiagonal().inverse() * tmp;
385 dst.noalias() = m_matrixV.leftCols(l_rank) * tmp;
386}
387
388template <typename Derived>
389template <bool Conjugate, typename RhsType, typename DstType>
390void SVDBase<Derived>::_solve_impl_transposed(const RhsType& rhs, DstType& dst) const {
391 // A = U S V^*
392 // So A^{-*} = U S^{-1} V^*
393 // And A^{-T} = U_conj S^{-1} V^T
394 Matrix<typename RhsType::Scalar, Dynamic, RhsType::ColsAtCompileTime, 0, MatrixType::MaxRowsAtCompileTime,
395 RhsType::MaxColsAtCompileTime>
396 tmp;
397 Index l_rank = rank();
398
399 tmp.noalias() = m_matrixV.leftCols(l_rank).transpose().template conjugateIf<Conjugate>() * rhs;
400 tmp = m_singularValues.head(l_rank).asDiagonal().inverse() * tmp;
401 dst = m_matrixU.template conjugateIf<!Conjugate>().leftCols(l_rank) * tmp;
402}
403#endif
404
405template <typename Derived>
406bool SVDBase<Derived>::allocate(Index rows, Index cols, unsigned int computationOptions) {
407 eigen_assert(rows >= 0 && cols >= 0);
408
409 // Reset the status on every call: a recompute of the same shape must not keep the InvalidInput of a previous one.
410 m_info = Success;
411 if (m_isAllocated && rows == m_rows.value() && cols == m_cols.value() && computationOptions == m_computationOptions) {
412 return true;
413 }
414
415 m_rows.setValue(rows);
416 m_cols.setValue(cols);
417 m_isInitialized = false;
418 m_isAllocated = true;
419 m_computationOptions = computationOptions;
420 m_computeFullU = ShouldComputeFullU || internal::should_svd_compute_full_u(computationOptions);
421 m_computeThinU = ShouldComputeThinU || internal::should_svd_compute_thin_u(computationOptions);
422 m_computeFullV = ShouldComputeFullV || internal::should_svd_compute_full_v(computationOptions);
423 m_computeThinV = ShouldComputeThinV || internal::should_svd_compute_thin_v(computationOptions);
424
425 eigen_assert(!(m_computeFullU && m_computeThinU) && "SVDBase: you can't ask for both full and thin U");
426 eigen_assert(!(m_computeFullV && m_computeThinV) && "SVDBase: you can't ask for both full and thin V");
427
428 m_diagSize.setValue(numext::mini(m_rows.value(), m_cols.value()));
429 m_singularValues.resize(m_diagSize.value());
430 EIGEN_IF_CONSTEXPR (RowsAtCompileTime == Dynamic) {
431 m_matrixU.resize(m_rows.value(), m_computeFullU ? m_rows.value() : m_computeThinU ? m_diagSize.value() : 0);
432 }
433 EIGEN_IF_CONSTEXPR (ColsAtCompileTime == Dynamic) {
434 m_matrixV.resize(m_cols.value(), m_computeFullV ? m_cols.value() : m_computeThinV ? m_diagSize.value() : 0);
435 }
436
437 return false;
438}
439
440} // namespace Eigen
441
442#endif // EIGEN_SVDBASE_H
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
Base class of SVD algorithms.
Definition SVDBase.h:122
ComputationInfo info() const
Reports whether previous computation was successful.
Definition SVDBase.h:303
Derived & setThreshold(const RealScalar &threshold)
Definition SVDBase.h:245
Index rank() const
Definition SVDBase.h:220
bool computeV() const
Definition SVDBase.h:278
Solve< Derived, Rhs > solve(const MatrixBase< Rhs > &b) const
bool computeU() const
Definition SVDBase.h:276
Derived & setThreshold(Default_t)
Definition SVDBase.h:259
RealScalar threshold() const
Definition SVDBase.h:268
SVDBase()
Default Constructor.
Definition SVDBase.h:352
const SingularValuesType & singularValues() const
Definition SVDBase.h:203
const MatrixUType & matrixU() const
Definition SVDBase.h:176
const MatrixVType & matrixV() const
Definition SVDBase.h:192
Index nonzeroSingularValues() const
Definition SVDBase.h:209
Pseudo expression representing a solving operation.
Definition Solve.h:63
ComputationInfo
Definition Constants.h:455
@ NoQRPreconditioner
Definition Constants.h:428
@ HouseholderQRPreconditioner
Definition Constants.h:430
@ ColPivHouseholderQRPreconditioner
Definition Constants.h:426
@ FullPivHouseholderQRPreconditioner
Definition Constants.h:432
@ PreconditionSquareMatrix
Definition Constants.h:437
@ Success
Definition Constants.h:457
@ ComputeFullV
Definition Constants.h:398
@ ComputeThinV
Definition Constants.h:400
@ ComputeFullU
Definition Constants.h:394
@ ComputeThinU
Definition Constants.h:396
Eigen::Index Index
The interface type of indices.
Definition EigenBase.h:44