12#ifndef EIGEN_JACOBISVD_H
13#define EIGEN_JACOBISVD_H
16#include "./InternalHeaderCheck.h"
24template <typename MatrixType, int Options, bool IsComplex = NumTraits<typename MatrixType::Scalar>::IsComplex>
25struct svd_precondition_2x2_block_to_be_real {};
34enum { PreconditionIfMoreColsThanRows, PreconditionIfMoreRowsThanCols };
37constexpr bool svd_precondition_more_rows(
int options, Index rows, Index cols) {
38 return rows > cols || (should_svd_precondition_square_matrix(options) && rows == cols);
41template <
typename MatrixType,
int QRPreconditioner,
int Case,
bool PreconditionSquare>
42struct qr_preconditioner_should_do_anything
43 : bool_constant<!((QRPreconditioner == NoQRPreconditioner) ||
44 (Case == PreconditionIfMoreColsThanRows && MatrixType::RowsAtCompileTime != Dynamic &&
45 MatrixType::ColsAtCompileTime != Dynamic &&
46 MatrixType::ColsAtCompileTime <= MatrixType::RowsAtCompileTime) ||
47 (Case == PreconditionIfMoreRowsThanCols && MatrixType::RowsAtCompileTime != Dynamic &&
48 MatrixType::ColsAtCompileTime != Dynamic &&
49 (PreconditionSquare ? MatrixType::RowsAtCompileTime < MatrixType::ColsAtCompileTime
50 : MatrixType::RowsAtCompileTime <= MatrixType::ColsAtCompileTime)))> {};
52template <typename MatrixType, int Options, int QRPreconditioner, int Case,
53 bool DoAnything = qr_preconditioner_should_do_anything<MatrixType, QRPreconditioner, Case,
54 should_svd_precondition_square_matrix(Options)>::value>
55struct qr_preconditioner_impl {};
57template <typename MatrixType, int Options, int QRPreconditioner, int Case>
58class qr_preconditioner_impl<MatrixType, Options, QRPreconditioner, Case, false> {
60 void allocate(const JacobiSVD<MatrixType, Options>&) {}
61 template <typename Xpr>
62 bool run(JacobiSVD<MatrixType, Options>&, const Xpr&) {
69template <typename MatrixType, int Options>
70class qr_preconditioner_impl<MatrixType, Options, FullPivHouseholderQRPreconditioner, PreconditionIfMoreRowsThanCols,
73 using Scalar = typename MatrixType::Scalar;
74 using SVDType = JacobiSVD<MatrixType, Options>;
76 enum { WorkspaceSize = MatrixType::RowsAtCompileTime, MaxWorkspaceSize = MatrixType::MaxRowsAtCompileTime };
78 using WorkspaceType = Matrix<Scalar, 1, WorkspaceSize, RowMajor, 1, MaxWorkspaceSize>;
80 void allocate(const SVDType& svd) {
81 if (svd.rows() != m_qr.rows() || svd.cols() != m_qr.cols()) {
82 internal::destroy_at(&m_qr);
83 internal::construct_at(&m_qr, svd.rows(), svd.cols());
85 if (svd.m_computeFullU) m_workspace.resize(svd.rows());
87 template <typename Xpr>
88 bool run(SVDType& svd, const Xpr& matrix) {
89 if (svd_precondition_more_rows(Options, matrix.rows(), matrix.cols())) {
91 svd.m_workMatrix = m_qr.matrixQR().block(0, 0, matrix.cols(), matrix.cols()).template triangularView<Upper>();
92 if (svd.m_computeFullU) m_qr.matrixQ().evalTo(svd.m_matrixU, m_workspace);
93 if (svd.computeV()) svd.m_matrixV = m_qr.colsPermutation();
100 using QRType = FullPivHouseholderQR<MatrixType>;
102 WorkspaceType m_workspace;
105template <typename MatrixType, int Options>
106class qr_preconditioner_impl<MatrixType, Options, FullPivHouseholderQRPreconditioner, PreconditionIfMoreColsThanRows,
109 using Scalar = typename MatrixType::Scalar;
110 using SVDType = JacobiSVD<MatrixType, Options>;
113 RowsAtCompileTime = MatrixType::RowsAtCompileTime,
114 ColsAtCompileTime = MatrixType::ColsAtCompileTime,
115 MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
116 MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime,
117 MatrixOptions = traits<MatrixType>::Options
120 using TransposeTypeWithSameStorageOrder =
121 typename internal::make_proper_matrix_type<Scalar, ColsAtCompileTime, RowsAtCompileTime, MatrixOptions,
122 MaxColsAtCompileTime, MaxRowsAtCompileTime>::type;
124 void allocate(const SVDType& svd) {
125 if (svd.cols() != m_qr.rows() || svd.rows() != m_qr.cols()) {
126 internal::destroy_at(&m_qr);
127 internal::construct_at(&m_qr, svd.cols(), svd.rows());
129 if (svd.m_computeFullV) m_workspace.resize(svd.cols());
131 template <typename Xpr>
132 bool run(SVDType& svd, const Xpr& matrix) {
133 if (matrix.cols() > matrix.rows()) {
134 m_qr.compute(matrix.adjoint());
136 m_qr.matrixQR().block(0, 0, matrix.rows(), matrix.rows()).template triangularView<Upper>().adjoint();
137 if (svd.m_computeFullV) m_qr.matrixQ().evalTo(svd.m_matrixV, m_workspace);
138 if (svd.computeU()) svd.m_matrixU = m_qr.colsPermutation();
145 using QRType = FullPivHouseholderQR<TransposeTypeWithSameStorageOrder>;
147 typename plain_row_type<MatrixType>::type m_workspace;
152template <typename MatrixType, int Options>
153class qr_preconditioner_impl<MatrixType, Options, ColPivHouseholderQRPreconditioner, PreconditionIfMoreRowsThanCols,
156 using Scalar = typename MatrixType::Scalar;
157 using SVDType = JacobiSVD<MatrixType, Options>;
160 WorkspaceSize = internal::traits<SVDType>::MatrixUColsAtCompileTime,
161 MaxWorkspaceSize = internal::traits<SVDType>::MatrixUMaxColsAtCompileTime
164 using WorkspaceType = Matrix<Scalar, 1, WorkspaceSize, RowMajor, 1, MaxWorkspaceSize>;
166 void allocate(const SVDType& svd) {
167 if (svd.rows() != m_qr.rows() || svd.cols() != m_qr.cols()) {
168 internal::destroy_at(&m_qr);
169 internal::construct_at(&m_qr, svd.rows(), svd.cols());
171 if (svd.m_computeFullU)
172 m_workspace.resize(svd.rows());
173 else if (svd.m_computeThinU)
174 m_workspace.resize(svd.cols());
176 template <typename Xpr>
177 bool run(SVDType& svd, const Xpr& matrix) {
178 if (svd_precondition_more_rows(Options, matrix.rows(), matrix.cols())) {
179 m_qr.compute(matrix);
180 svd.m_workMatrix = m_qr.matrixQR().block(0, 0, matrix.cols(), matrix.cols()).template triangularView<Upper>();
181 if (svd.m_computeFullU)
182 m_qr.householderQ().evalTo(svd.m_matrixU, m_workspace);
183 else if (svd.m_computeThinU) {
184 svd.m_matrixU.setIdentity(matrix.rows(), matrix.cols());
185 m_qr.householderQ().applyThisOnTheLeft(svd.m_matrixU, m_workspace);
187 if (svd.computeV()) svd.m_matrixV = m_qr.colsPermutation();
194 using QRType = ColPivHouseholderQR<MatrixType>;
196 WorkspaceType m_workspace;
199template <typename MatrixType, int Options>
200class qr_preconditioner_impl<MatrixType, Options, ColPivHouseholderQRPreconditioner, PreconditionIfMoreColsThanRows,
203 using Scalar = typename MatrixType::Scalar;
204 using SVDType = JacobiSVD<MatrixType, Options>;
207 RowsAtCompileTime = MatrixType::RowsAtCompileTime,
208 ColsAtCompileTime = MatrixType::ColsAtCompileTime,
209 MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
210 MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime,
211 MatrixOptions = internal::traits<MatrixType>::Options,
212 WorkspaceSize = internal::traits<SVDType>::MatrixVColsAtCompileTime,
213 MaxWorkspaceSize = internal::traits<SVDType>::MatrixVMaxColsAtCompileTime
216 using WorkspaceType = Matrix<Scalar, WorkspaceSize, 1, ColMajor, MaxWorkspaceSize, 1>;
218 using TransposeTypeWithSameStorageOrder =
219 typename internal::make_proper_matrix_type<Scalar, ColsAtCompileTime, RowsAtCompileTime, MatrixOptions,
220 MaxColsAtCompileTime, MaxRowsAtCompileTime>::type;
222 void allocate(const SVDType& svd) {
223 if (svd.cols() != m_qr.rows() || svd.rows() != m_qr.cols()) {
224 internal::destroy_at(&m_qr);
225 internal::construct_at(&m_qr, svd.cols(), svd.rows());
227 if (svd.m_computeFullV)
228 m_workspace.resize(svd.cols());
229 else if (svd.m_computeThinV)
230 m_workspace.resize(svd.rows());
232 template <typename Xpr>
233 bool run(SVDType& svd, const Xpr& matrix) {
234 if (matrix.cols() > matrix.rows()) {
235 m_qr.compute(matrix.adjoint());
238 m_qr.matrixQR().block(0, 0, matrix.rows(), matrix.rows()).template triangularView<Upper>().adjoint();
239 if (svd.m_computeFullV)
240 m_qr.householderQ().evalTo(svd.m_matrixV, m_workspace);
241 else if (svd.m_computeThinV) {
242 svd.m_matrixV.setIdentity(matrix.cols(), matrix.rows());
243 m_qr.householderQ().applyThisOnTheLeft(svd.m_matrixV, m_workspace);
245 if (svd.computeU()) svd.m_matrixU = m_qr.colsPermutation();
252 using QRType = ColPivHouseholderQR<TransposeTypeWithSameStorageOrder>;
254 WorkspaceType m_workspace;
259template <typename MatrixType, int Options>
260class qr_preconditioner_impl<MatrixType, Options, HouseholderQRPreconditioner, PreconditionIfMoreRowsThanCols, true> {
262 using Scalar = typename MatrixType::Scalar;
263 using SVDType = JacobiSVD<MatrixType, Options>;
266 WorkspaceSize = internal::traits<SVDType>::MatrixUColsAtCompileTime,
267 MaxWorkspaceSize = internal::traits<SVDType>::MatrixUMaxColsAtCompileTime
270 using WorkspaceType = Matrix<Scalar, 1, WorkspaceSize, RowMajor, 1, MaxWorkspaceSize>;
272 void allocate(const SVDType& svd) {
273 if (svd.rows() != m_qr.rows() || svd.cols() != m_qr.cols()) {
274 internal::destroy_at(&m_qr);
275 internal::construct_at(&m_qr, svd.rows(), svd.cols());
277 if (svd.m_computeFullU)
278 m_workspace.resize(svd.rows());
279 else if (svd.m_computeThinU)
280 m_workspace.resize(svd.cols());
282 template <typename Xpr>
283 bool run(SVDType& svd, const Xpr& matrix) {
284 if (svd_precondition_more_rows(Options, matrix.rows(), matrix.cols())) {
285 m_qr.compute(matrix);
286 svd.m_workMatrix = m_qr.matrixQR().block(0, 0, matrix.cols(), matrix.cols()).template triangularView<Upper>();
287 if (svd.m_computeFullU)
288 m_qr.householderQ().evalTo(svd.m_matrixU, m_workspace);
289 else if (svd.m_computeThinU) {
290 svd.m_matrixU.setIdentity(matrix.rows(), matrix.cols());
291 m_qr.householderQ().applyThisOnTheLeft(svd.m_matrixU, m_workspace);
293 if (svd.computeV()) svd.m_matrixV.setIdentity(matrix.cols(), matrix.cols());
300 using QRType = HouseholderQR<MatrixType>;
302 WorkspaceType m_workspace;
305template <typename MatrixType, int Options>
306class qr_preconditioner_impl<MatrixType, Options, HouseholderQRPreconditioner, PreconditionIfMoreColsThanRows, true> {
308 using Scalar = typename MatrixType::Scalar;
309 using SVDType = JacobiSVD<MatrixType, Options>;
312 RowsAtCompileTime = MatrixType::RowsAtCompileTime,
313 ColsAtCompileTime = MatrixType::ColsAtCompileTime,
314 MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
315 MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime,
316 MatrixOptions = internal::traits<MatrixType>::Options,
317 WorkspaceSize = internal::traits<SVDType>::MatrixVColsAtCompileTime,
318 MaxWorkspaceSize = internal::traits<SVDType>::MatrixVMaxColsAtCompileTime
321 using WorkspaceType = Matrix<Scalar, WorkspaceSize, 1, ColMajor, MaxWorkspaceSize, 1>;
323 using TransposeTypeWithSameStorageOrder =
324 typename internal::make_proper_matrix_type<Scalar, ColsAtCompileTime, RowsAtCompileTime, MatrixOptions,
325 MaxColsAtCompileTime, MaxRowsAtCompileTime>::type;
327 void allocate(const SVDType& svd) {
328 if (svd.cols() != m_qr.rows() || svd.rows() != m_qr.cols()) {
329 internal::destroy_at(&m_qr);
330 internal::construct_at(&m_qr, svd.cols(), svd.rows());
332 if (svd.m_computeFullV)
333 m_workspace.resize(svd.cols());
334 else if (svd.m_computeThinV)
335 m_workspace.resize(svd.rows());
338 template <typename Xpr>
339 bool run(SVDType& svd, const Xpr& matrix) {
340 if (matrix.cols() > matrix.rows()) {
341 m_qr.compute(matrix.adjoint());
344 m_qr.matrixQR().block(0, 0, matrix.rows(), matrix.rows()).template triangularView<Upper>().adjoint();
345 if (svd.m_computeFullV)
346 m_qr.householderQ().evalTo(svd.m_matrixV, m_workspace);
347 else if (svd.m_computeThinV) {
348 svd.m_matrixV.setIdentity(matrix.cols(), matrix.rows());
349 m_qr.householderQ().applyThisOnTheLeft(svd.m_matrixV, m_workspace);
351 if (svd.computeU()) svd.m_matrixU.setIdentity(matrix.rows(), matrix.rows());
360 WorkspaceType m_workspace;
368template <
typename MatrixType,
int Options>
369struct svd_precondition_2x2_block_to_be_real<MatrixType, Options, false> {
370 using SVD = JacobiSVD<MatrixType, Options>;
371 using RealScalar =
typename MatrixType::RealScalar;
372 static bool run(
typename SVD::WorkMatrixType&, SVD&, Index, Index, RealScalar&) {
return true; }
375template <
typename MatrixType,
int Options>
376struct svd_precondition_2x2_block_to_be_real<MatrixType, Options, true> {
377 using SVD = JacobiSVD<MatrixType, Options>;
378 using Scalar =
typename MatrixType::Scalar;
379 using RealScalar =
typename MatrixType::RealScalar;
380 static bool run(
typename SVD::WorkMatrixType& work_matrix, SVD& svd, Index p, Index q, RealScalar& maxDiagEntry) {
384 JacobiRotation<Scalar> rot;
385 RealScalar n = sqrt(numext::abs2(work_matrix.coeff(p, p)) + numext::abs2(work_matrix.coeff(q, p)));
387 const RealScalar considerAsZero = (std::numeric_limits<RealScalar>::min)();
388 const RealScalar precision = NumTraits<Scalar>::epsilon();
390 if (numext::is_exactly_zero(n)) {
392 work_matrix.coeffRef(p, p) = work_matrix.coeffRef(q, p) = Scalar(0);
394 if (abs(numext::imag(work_matrix.coeff(p, q))) > considerAsZero) {
397 z = abs(work_matrix.coeff(p, q)) / work_matrix.coeff(p, q);
398 work_matrix.row(p) *= z;
399 if (svd.computeU()) svd.m_matrixU.col(p) *= conj(z);
401 if (abs(numext::imag(work_matrix.coeff(q, q))) > considerAsZero) {
402 z = abs(work_matrix.coeff(q, q)) / work_matrix.coeff(q, q);
403 work_matrix.row(q) *= z;
404 if (svd.computeU()) svd.m_matrixU.col(q) *= conj(z);
408 rot.c() = conj(work_matrix.coeff(p, p)) / n;
409 rot.s() = work_matrix.coeff(q, p) / n;
410 work_matrix.applyOnTheLeft(p, q, rot);
411 if (svd.computeU()) svd.m_matrixU.applyOnTheRight(p, q, rot.adjoint());
412 if (abs(numext::imag(work_matrix.coeff(p, q))) > considerAsZero) {
413 z = abs(work_matrix.coeff(p, q)) / work_matrix.coeff(p, q);
414 work_matrix.col(q) *= z;
415 if (svd.computeV()) svd.m_matrixV.col(q) *= z;
417 if (abs(numext::imag(work_matrix.coeff(q, q))) > considerAsZero) {
418 z = abs(work_matrix.coeff(q, q)) / work_matrix.coeff(q, q);
419 work_matrix.row(q) *= z;
420 if (svd.computeU()) svd.m_matrixU.col(q) *= conj(z);
425 maxDiagEntry = numext::maxi<RealScalar>(
426 maxDiagEntry, numext::maxi<RealScalar>(abs(work_matrix.coeff(p, p)), abs(work_matrix.coeff(q, q))));
428 RealScalar threshold = numext::maxi<RealScalar>(considerAsZero, precision * maxDiagEntry);
429 return abs(work_matrix.coeff(p, q)) > threshold || abs(work_matrix.coeff(q, p)) > threshold;
433template <
typename WorkMatrixType,
typename MatrixUType,
typename MatrixVType>
434EIGEN_DONT_INLINE
bool jacobi_svd_nonblocking_sweep(WorkMatrixType& work_matrix, MatrixUType& matrix_u,
435 MatrixVType& matrix_v,
bool compute_u,
bool compute_v,
436 typename WorkMatrixType::RealScalar considerAsZero,
437 typename WorkMatrixType::RealScalar precision,
438 typename WorkMatrixType::RealScalar& maxDiagEntry) {
441 using Scalar =
typename WorkMatrixType::Scalar;
442 using RealScalar =
typename WorkMatrixType::RealScalar;
443 const Index n = work_matrix.rows();
444 bool notFinished =
false;
446 EIGEN_IF_CONSTEXPR (NumTraits<Scalar>::IsComplex) {
448 for (Index p = 1; p < n; ++p) {
449 for (Index q = 0; q < p; ++q) {
450 RealScalar threshold = numext::maxi<RealScalar>(considerAsZero, precision * maxDiagEntry);
451 if (abs(work_matrix.coeff(p, q)) > threshold || abs(work_matrix.coeff(q, p)) > threshold) {
454 bool doRealSvd =
true;
455 RealScalar nn = sqrt(numext::abs2(work_matrix.coeff(p, p)) + numext::abs2(work_matrix.coeff(q, p)));
457 if (numext::is_exactly_zero(nn)) {
459 work_matrix.coeffRef(p, p) = work_matrix.coeffRef(q, p) = Scalar(0);
461 if (abs(numext::imag(work_matrix.coeff(p, q))) > considerAsZero) {
464 z = abs(work_matrix.coeff(p, q)) / work_matrix.coeff(p, q);
465 work_matrix.row(p) *= z;
466 if (compute_u) matrix_u.col(p) *= numext::conj(z);
468 if (abs(numext::imag(work_matrix.coeff(q, q))) > considerAsZero) {
469 z = abs(work_matrix.coeff(q, q)) / work_matrix.coeff(q, q);
470 work_matrix.row(q) *= z;
471 if (compute_u) matrix_u.col(q) *= numext::conj(z);
474 JacobiRotation<Scalar> rot;
475 rot.c() = numext::conj(work_matrix.coeff(p, p)) / nn;
476 rot.s() = work_matrix.coeff(q, p) / nn;
477 work_matrix.applyOnTheLeft(p, q, rot);
478 if (compute_u) matrix_u.applyOnTheRight(p, q, rot.adjoint());
479 if (abs(numext::imag(work_matrix.coeff(p, q))) > considerAsZero) {
480 z = abs(work_matrix.coeff(p, q)) / work_matrix.coeff(p, q);
481 work_matrix.col(q) *= z;
482 if (compute_v) matrix_v.col(q) *= z;
484 if (abs(numext::imag(work_matrix.coeff(q, q))) > considerAsZero) {
485 z = abs(work_matrix.coeff(q, q)) / work_matrix.coeff(q, q);
486 work_matrix.row(q) *= z;
487 if (compute_u) matrix_u.col(q) *= numext::conj(z);
491 maxDiagEntry = numext::maxi<RealScalar>(
492 maxDiagEntry, numext::maxi<RealScalar>(abs(work_matrix.coeff(p, p)), abs(work_matrix.coeff(q, q))));
493 threshold = numext::maxi<RealScalar>(considerAsZero, NumTraits<Scalar>::epsilon() * maxDiagEntry);
494 doRealSvd = abs(work_matrix.coeff(p, q)) > threshold || abs(work_matrix.coeff(q, p)) > threshold;
497 JacobiRotation<RealScalar> j_left, j_right;
498 internal::real_2x2_jacobi_svd(work_matrix, p, q, &j_left, &j_right);
499 work_matrix.applyOnTheLeft(p, q, j_left);
500 if (compute_u) matrix_u.applyOnTheRight(p, q, j_left.transpose());
501 work_matrix.applyOnTheRight(p, q, j_right);
502 if (compute_v) matrix_v.applyOnTheRight(p, q, j_right);
503 maxDiagEntry = numext::maxi<RealScalar>(
504 maxDiagEntry, numext::maxi<RealScalar>(abs(work_matrix.coeff(p, p)), abs(work_matrix.coeff(q, q))));
511 RealScalar threshold = numext::maxi<RealScalar>(considerAsZero, precision * maxDiagEntry);
512 for (Index p = 1; p < n; ++p) {
513 for (Index q = 0; q < p; ++q) {
514 if (abs(work_matrix.coeff(p, q)) > threshold || abs(work_matrix.coeff(q, p)) > threshold) {
516 JacobiRotation<RealScalar> j_left, j_right;
517 internal::real_2x2_jacobi_svd(work_matrix, p, q, &j_left, &j_right);
518 work_matrix.applyOnTheLeft(p, q, j_left);
519 if (compute_u) matrix_u.applyOnTheRight(p, q, j_left.transpose());
520 work_matrix.applyOnTheRight(p, q, j_right);
521 if (compute_v) matrix_v.applyOnTheRight(p, q, j_right);
522 maxDiagEntry = numext::maxi<RealScalar>(
523 maxDiagEntry, numext::maxi<RealScalar>(abs(work_matrix.coeff(p, p)), abs(work_matrix.coeff(q, q))));
524 threshold = numext::maxi<RealScalar>(considerAsZero, precision * maxDiagEntry);
533template <
typename MatrixType_,
int Options>
534struct traits<JacobiSVD<MatrixType_, Options>> : svd_traits<MatrixType_, Options> {
535 using MatrixType = MatrixType_;
612template <
typename MatrixType_,
int Options_>
617 using MatrixType = MatrixType_;
618 using Scalar =
typename Base::Scalar;
619 using RealScalar =
typename Base::RealScalar;
622 QRPreconditioner = internal::get_qr_preconditioner(Options),
623 RowsAtCompileTime = Base::RowsAtCompileTime,
624 ColsAtCompileTime = Base::ColsAtCompileTime,
625 DiagSizeAtCompileTime = Base::DiagSizeAtCompileTime,
626 MaxRowsAtCompileTime = Base::MaxRowsAtCompileTime,
627 MaxColsAtCompileTime = Base::MaxColsAtCompileTime,
628 MaxDiagSizeAtCompileTime = Base::MaxDiagSizeAtCompileTime,
629 MatrixOptions = Base::MatrixOptions
632 using MatrixUType =
typename Base::MatrixUType;
633 using MatrixVType =
typename Base::MatrixVType;
634 using SingularValuesType =
typename Base::SingularValuesType;
635 using WorkMatrixType =
Matrix<Scalar, DiagSizeAtCompileTime, DiagSizeAtCompileTime, MatrixOptions,
636 MaxDiagSizeAtCompileTime, MaxDiagSizeAtCompileTime>;
652 JacobiSVD(Index rows, Index cols) { allocate(rows, cols, internal::get_computation_options(Options)); }
670 EIGEN_DEPRECATED_WITH_REASON(
"Options should be specified using the class template parameter.")
671 JacobiSVD(Index rows, Index cols,
unsigned int computationOptions) {
672 internal::check_svd_options_assertions<MatrixType, Options>(computationOptions, rows, cols);
673 allocate(rows, cols, computationOptions);
681 template <
typename Derived>
683 compute_impl(matrix, internal::get_computation_options(Options));
686 template <
typename Derived>
688 compute_impl(matrix, internal::get_computation_options(Options));
704 template <
typename Derived>
706 internal::check_svd_options_assertions<MatrixType, Options>(computationOptions, matrix.rows(), matrix.cols());
707 compute_impl(matrix, computationOptions);
715 template <
typename Derived>
717 return compute_impl(matrix, m_computationOptions);
720 template <
typename Derived>
722 return compute_impl(matrix, m_computationOptions);
734 template <
typename Derived>
735 EIGEN_DEPRECATED_WITH_REASON(
"Options should be specified using the class template parameter.")
737 internal::check_svd_options_assertions<MatrixType, Options>(m_computationOptions, matrix.rows(), matrix.cols());
738 return compute_impl(matrix, computationOptions);
742 using Base::computeU;
743 using Base::computeV;
744 using Base::diagSize;
748 void allocate(Index rows_, Index cols_,
unsigned int computationOptions) {
749 if (Base::allocate(rows_, cols_, computationOptions))
return;
752 "JacobiSVD: can't compute thin U or thin V with the FullPivHouseholderQR preconditioner. "
753 "Use the ColPivHouseholderQR preconditioner instead.");
755 m_workMatrix.resize(diagSize(), diagSize());
756 if (cols() > rows()) m_qr_precond_morecols.allocate(*
this);
757 if (internal::svd_precondition_more_rows(Options, rows(), cols())) m_qr_precond_morerows.allocate(*
this);
761 template <
typename Derived>
762 JacobiSVD& compute_impl(
const TriangularBase<Derived>& matrix,
unsigned int computationOptions);
763 template <
typename Derived>
764 JacobiSVD& compute_impl(
const MatrixBase<Derived>& matrix,
unsigned int computationOptions);
770 EIGEN_DONT_INLINE
bool blocked_sweep(RealScalar considerAsZero, RealScalar precision, RealScalar& maxDiagEntry);
773 using Base::m_computationOptions;
774 using Base::m_computeFullU;
775 using Base::m_computeFullV;
776 using Base::m_computeThinU;
777 using Base::m_computeThinV;
779 using Base::m_isAllocated;
780 using Base::m_isInitialized;
781 using Base::m_matrixU;
782 using Base::m_matrixV;
783 using Base::m_nonzeroSingularValues;
784 using Base::m_prescribedThreshold;
785 using Base::m_singularValues;
786 using Base::m_usePrescribedThreshold;
787 using Base::ShouldComputeThinU;
788 using Base::ShouldComputeThinV;
790 EIGEN_STATIC_ASSERT(!(ShouldComputeThinU &&
int(QRPreconditioner) ==
int(FullPivHouseholderQRPreconditioner)) &&
791 !(ShouldComputeThinV &&
int(QRPreconditioner) ==
int(FullPivHouseholderQRPreconditioner)),
792 "JacobiSVD: can't compute thin U or thin V with the FullPivHouseholderQR preconditioner. "
793 "Use the ColPivHouseholderQR preconditioner instead.")
794 EIGEN_STATIC_ASSERT(!(internal::should_svd_precondition_square_matrix(Options) &&
795 int(QRPreconditioner) ==
int(NoQRPreconditioner)),
796 "JacobiSVD: PreconditionSquareMatrix requires a QR preconditioner other than NoQRPreconditioner.")
798 template <typename MatrixType__,
int Options__,
bool IsComplex_>
799 friend struct internal::svd_precondition_2x2_block_to_be_real;
800 template <typename MatrixType__,
int Options__,
int QRPreconditioner_,
int Case_,
bool DoAnything_>
801 friend struct internal::qr_preconditioner_impl;
803 internal::qr_preconditioner_impl<MatrixType, Options, QRPreconditioner, internal::PreconditionIfMoreColsThanRows>
804 m_qr_precond_morecols;
805 internal::qr_preconditioner_impl<MatrixType, Options, QRPreconditioner, internal::PreconditionIfMoreRowsThanCols>
806 m_qr_precond_morerows;
807 WorkMatrixType m_workMatrix;
810#ifdef EIGEN_JACOBI_SVD_BLOCK_SIZE
811 static constexpr Index kDefaultBlockSize = EIGEN_JACOBI_SVD_BLOCK_SIZE;
813 static constexpr Index kDefaultBlockSize = 32;
817 static constexpr Index kBlockSize = internal::min_size_prefer_fixed(kDefaultBlockSize, MaxDiagSizeAtCompileTime);
820template <
typename MatrixType,
int Options>
821template <
typename Derived>
822JacobiSVD<MatrixType, Options>& JacobiSVD<MatrixType, Options>::compute_impl(
const TriangularBase<Derived>& matrix,
823 unsigned int computationOptions) {
824 return compute_impl(matrix.toDenseMatrix(), computationOptions);
827template <
typename MatrixType,
int Options>
828template <
typename Derived>
829JacobiSVD<MatrixType, Options>& JacobiSVD<MatrixType, Options>::compute_impl(
const MatrixBase<Derived>& matrix,
830 unsigned int computationOptions) {
831 EIGEN_STATIC_ASSERT_SAME_MATRIX_SIZE(Derived, MatrixType);
832 EIGEN_STATIC_ASSERT((std::is_same<typename Derived::Scalar, typename MatrixType::Scalar>::value),
833 Input matrix must have the same Scalar type as the JacobiSVD
object.);
837 allocate(matrix.rows(), matrix.cols(), computationOptions);
841 const RealScalar precision = RealScalar(2) * NumTraits<Scalar>::epsilon();
844 const RealScalar considerAsZero = (std::numeric_limits<RealScalar>::min)();
848 const RealScalar maxCoeff = matrix.size() == 0
850 : internal::safe_scaling<RealScalar>::recover_flushed_max_coeff(
851 matrix.derived(), matrix.cwiseAbs().template maxCoeff<
PropagateNaN>());
852 if (!(numext::isfinite)(maxCoeff)) {
853 m_isInitialized =
true;
855 m_nonzeroSingularValues = 0;
856 m_singularValues.setZero();
859 internal::safe_scaling_factors<RealScalar> factors;
863 if (rows() != cols() || internal::should_svd_precondition_square_matrix(Options)) {
865 internal::safe_scaling<RealScalar>::with_scaled(matrix.derived(), maxCoeff, [&](
const auto& scaledMatrix) {
866 m_qr_precond_morecols.run(*this, scaledMatrix);
867 m_qr_precond_morerows.run(*this, scaledMatrix);
870 factors = internal::safe_scaling<RealScalar>::scale_to(
872 matrix.template topLeftCorner<DiagSizeAtCompileTime, DiagSizeAtCompileTime>(diagSize(), diagSize()), maxCoeff);
873 if (m_computeFullU) m_matrixU.setIdentity(rows(), rows());
874 if (m_computeThinU) m_matrixU.setIdentity(rows(), diagSize());
875 if (m_computeFullV) m_matrixV.setIdentity(cols(), cols());
876 if (m_computeThinV) m_matrixV.setIdentity(cols(), diagSize());
880 RealScalar maxDiagEntry = diagSize() == 0 ? RealScalar(0) : m_workMatrix.cwiseAbs().diagonal().maxCoeff();
882 bool finished =
false;
886 EIGEN_IF_CONSTEXPR (MaxDiagSizeAtCompileTime == Dynamic || MaxDiagSizeAtCompileTime > kBlockSize) {
895 const Index n = diagSize();
896#ifdef EIGEN_JACOBI_SVD_BLOCKING_THRESHOLD
897 const Index blockingThreshold = EIGEN_JACOBI_SVD_BLOCKING_THRESHOLD;
899 const Index blockingThreshold =
900 static_cast<Index
>(numext::sqrt(
static_cast<double>(l2CacheSize() /
sizeof(
float))));
903 if (n >= blockingThreshold) {
907 finished = !blocked_sweep(considerAsZero, precision, maxDiagEntry);
911 finished = !internal::jacobi_svd_nonblocking_sweep(m_workMatrix, m_matrixU, m_matrixV, computeU(), computeV(),
912 considerAsZero, precision, maxDiagEntry);
918 for (Index i = 0; i < diagSize(); ++i) {
922 bool diagonal_has_imaginary_part =
false;
923 EIGEN_IF_CONSTEXPR (NumTraits<Scalar>::IsComplex) {
924 diagonal_has_imaginary_part = abs(numext::imag(m_workMatrix.coeff(i, i))) > considerAsZero;
926 if (diagonal_has_imaginary_part) {
927 RealScalar a = abs(m_workMatrix.coeff(i, i));
928 m_singularValues.coeffRef(i) = abs(a);
929 if (computeU()) m_matrixU.col(i) *= m_workMatrix.coeff(i, i) / a;
933 RealScalar a = numext::real(m_workMatrix.coeff(i, i));
934 m_singularValues.coeffRef(i) = internal::abs_preserving_subnormals(a);
935 if (computeU() && internal::is_negative_preserving_subnormals(a)) m_matrixU.col(i) = -m_matrixU.col(i);
943 for (Index i = 0; i < diagSize(); i++) {
945 RealScalar maxRemainingSingularValue = m_singularValues.tail(diagSize() - i).maxCoeff(&pos);
946 if (internal::is_zero_or_subnormal_magnitude(maxRemainingSingularValue)) {
947 pos = internal::safe_scaling<RealScalar>::recover_flushed_max_coeff_index(m_singularValues.tail(diagSize() - i),
949 if (numext::is_exactly_zero_no_flush(m_singularValues.coeff(i + pos)))
break;
953 std::swap(m_singularValues.coeffRef(i), m_singularValues.coeffRef(pos));
954 if (computeU()) m_matrixU.col(pos).swap(m_matrixU.col(i));
955 if (computeV()) m_matrixV.col(pos).swap(m_matrixV.col(i));
960 internal::safe_scaling<RealScalar>::unscale_in_place(m_singularValues, maxCoeff, factors);
961 m_nonzeroSingularValues = diagSize();
962 for (Index i = 0; i < diagSize(); i++) {
963 if (numext::is_exactly_zero_no_flush(m_singularValues.coeff(i))) {
964 m_nonzeroSingularValues = i;
969 m_isInitialized =
true;
990template <
typename MatrixType,
int Options>
991EIGEN_DONT_INLINE
bool JacobiSVD<MatrixType, Options>::blocked_sweep(RealScalar considerAsZero, RealScalar precision,
992 RealScalar& maxDiagEntry) {
995 const Index n = diagSize();
996 RealScalar threshold = numext::maxi<RealScalar>(considerAsZero, precision * maxDiagEntry);
997 bool notFinished =
false;
998 static constexpr Index kBlockBufferSize = (kBlockSize + 1) * (kBlockSize + 1);
999 ei_declare_aligned_stack_constructed_variable(Scalar, blockBufferPtr, kBlockBufferSize, 0);
1000 Map<Matrix<Scalar, kBlockSize + 1, kBlockSize + 1, MatrixOptions>, AlignedMax> blockBuffer(
1001 blockBufferPtr, kBlockSize + 1, kBlockSize + 1);
1003 ei_declare_aligned_stack_constructed_variable(Scalar, accumPtr, kBlockBufferSize, 0);
1004 Map<Matrix<Scalar, kBlockSize + 1, kBlockSize + 1, MatrixOptions>, AlignedMax> accum(accumPtr, kBlockSize + 1,
1006 Matrix<Scalar, 1, Dynamic, RowMajor, 1, MaxDiagSizeAtCompileTime> Mp_save;
1008 for (Index p = 1; p < n; ++p) {
1015 for (; q + kBlockSize <= p; q += kBlockSize) {
1018 blockBuffer.template topLeftCorner<kBlockSize, kBlockSize>() =
1019 m_workMatrix.template block<kBlockSize, kBlockSize>(q, q);
1020 blockBuffer.col(kBlockSize).template head<kBlockSize>() = m_workMatrix.col(p).template segment<kBlockSize>(q);
1021 blockBuffer.row(kBlockSize).template head<kBlockSize>() = m_workMatrix.row(p).template segment<kBlockSize>(q);
1022 blockBuffer(kBlockSize, kBlockSize) = m_workMatrix(p, p);
1028 accum.setIdentity(kBlockSize + 1, kBlockSize + 1);
1029 bool blockDirty =
false;
1031 for (Index qq = 0; qq < kBlockSize; ++qq) {
1032 if (abs(blockBuffer.coeff(kBlockSize, qq)) > threshold || abs(blockBuffer.coeff(qq, kBlockSize)) > threshold) {
1046 bool doRealSvd =
true;
1047 EIGEN_IF_CONSTEXPR (NumTraits<Scalar>::IsComplex) {
1050 RealScalar nn = sqrt(numext::abs2(blockBuffer.coeff(kBlockSize, kBlockSize)) +
1051 numext::abs2(blockBuffer.coeff(qq, kBlockSize)));
1053 if (numext::is_exactly_zero(nn)) {
1055 blockBuffer.coeffRef(kBlockSize, kBlockSize) = Scalar(0);
1056 blockBuffer.coeffRef(qq, kBlockSize) = Scalar(0);
1059 if (abs(numext::imag(blockBuffer.coeff(kBlockSize, qq))) > considerAsZero) {
1060 z = abs(blockBuffer.coeff(kBlockSize, qq)) / blockBuffer.coeff(kBlockSize, qq);
1061 blockBuffer.row(kBlockSize) *= z;
1062 accum.row(kBlockSize) *= z;
1063 if (computeU()) m_matrixU.col(p) *= numext::conj(z);
1065 if (abs(numext::imag(blockBuffer.coeff(qq, qq))) > considerAsZero) {
1066 z = abs(blockBuffer.coeff(qq, qq)) / blockBuffer.coeff(qq, qq);
1067 blockBuffer.row(qq) *= z;
1069 if (computeU()) m_matrixU.col(q + qq) *= numext::conj(z);
1076 JacobiRotation<Scalar> rot;
1077 rot.c() = numext::conj(blockBuffer.coeff(kBlockSize, kBlockSize)) / nn;
1078 rot.s() = blockBuffer.coeff(qq, kBlockSize) / nn;
1079 blockBuffer.applyOnTheLeft(kBlockSize, qq, rot);
1080 accum.applyOnTheLeft(kBlockSize, qq, rot);
1081 if (computeU()) m_matrixU.applyOnTheRight(p, q + qq, rot.adjoint());
1084 if (abs(numext::imag(blockBuffer.coeff(kBlockSize, qq))) > considerAsZero) {
1085 z = abs(blockBuffer.coeff(kBlockSize, qq)) / blockBuffer.coeff(kBlockSize, qq);
1086 blockBuffer.col(qq) *= z;
1087 m_workMatrix.col(q + qq) *= z;
1088 if (computeV()) m_matrixV.col(q + qq) *= z;
1091 if (abs(numext::imag(blockBuffer.coeff(qq, qq))) > considerAsZero) {
1092 z = abs(blockBuffer.coeff(qq, qq)) / blockBuffer.coeff(qq, qq);
1093 blockBuffer.row(qq) *= z;
1095 if (computeU()) m_matrixU.col(q + qq) *= numext::conj(z);
1099 maxDiagEntry = numext::maxi<RealScalar>(
1100 maxDiagEntry, numext::maxi<RealScalar>(abs(blockBuffer.coeff(kBlockSize, kBlockSize)),
1101 abs(blockBuffer.coeff(qq, qq))));
1103 RealScalar precondThreshold = numext::maxi<RealScalar>(considerAsZero, precision * maxDiagEntry);
1104 doRealSvd = abs(blockBuffer.coeff(kBlockSize, qq)) > precondThreshold ||
1105 abs(blockBuffer.coeff(qq, kBlockSize)) > precondThreshold;
1110 JacobiRotation<RealScalar> j_left, j_right;
1111 internal::real_2x2_jacobi_svd(blockBuffer, kBlockSize, qq, &j_left, &j_right);
1112 blockBuffer.applyOnTheLeft(kBlockSize, qq, j_left);
1113 blockBuffer.applyOnTheRight(kBlockSize, qq, j_right);
1116 accum.applyOnTheLeft(kBlockSize, qq, j_left);
1119 m_workMatrix.applyOnTheRight(p, q + qq, j_right);
1120 if (computeU()) m_matrixU.applyOnTheRight(p, q + qq, j_left.transpose());
1121 if (computeV()) m_matrixV.applyOnTheRight(p, q + qq, j_right);
1123 maxDiagEntry = numext::maxi<RealScalar>(
1124 maxDiagEntry, numext::maxi<RealScalar>(abs(blockBuffer.coeff(kBlockSize, kBlockSize)),
1125 abs(blockBuffer.coeff(qq, qq))));
1127 threshold = numext::maxi<RealScalar>(considerAsZero, precision * maxDiagEntry);
1138 if (p == q + kBlockSize) {
1139 m_workMatrix.template middleRows<kBlockSize + 1>(q) =
1140 accum * m_workMatrix.template middleRows<kBlockSize + 1>(q);
1142 const auto L11 = accum.template topLeftCorner<kBlockSize, kBlockSize>();
1143 const auto l12 = accum.col(kBlockSize).template head<kBlockSize>();
1144 const auto l21 = accum.row(kBlockSize).template head<kBlockSize>();
1145 const Scalar l22 = accum(kBlockSize, kBlockSize);
1146 auto Mq = m_workMatrix.template middleRows<kBlockSize>(q);
1147 auto Mp = m_workMatrix.row(p);
1149 Mp.noalias() = l21 * Mq + l22 * Mp_save;
1150 Mq = L11.template triangularView<Lower>() * Mq + l12 * Mp_save;
1156 for (; q < p; ++q) {
1157 if (abs(m_workMatrix.coeff(p, q)) > threshold || abs(m_workMatrix.coeff(q, p)) > threshold) {
1160 bool doRealSvd =
true;
1161 EIGEN_IF_CONSTEXPR (NumTraits<Scalar>::IsComplex) {
1162 doRealSvd = internal::svd_precondition_2x2_block_to_be_real<MatrixType, Options>::run(m_workMatrix, *
this, p,
1167 JacobiRotation<RealScalar> j_left, j_right;
1168 internal::real_2x2_jacobi_svd(m_workMatrix, p, q, &j_left, &j_right);
1169 m_workMatrix.applyOnTheLeft(p, q, j_left);
1170 if (computeU()) m_matrixU.applyOnTheRight(p, q, j_left.transpose());
1171 m_workMatrix.applyOnTheRight(p, q, j_right);
1172 if (computeV()) m_matrixV.applyOnTheRight(p, q, j_right);
1173 maxDiagEntry = numext::maxi<RealScalar>(
1174 maxDiagEntry, numext::maxi<RealScalar>(abs(m_workMatrix.coeff(p, p)), abs(m_workMatrix.coeff(q, q))));
1176 threshold = numext::maxi<RealScalar>(considerAsZero, precision * maxDiagEntry);
1191template <
typename Derived>
1192template <
int Options>
1197template <
typename Derived>
1198template <
int Options>
1200 unsigned int computationOptions)
const {
Householder QR decomposition of a matrix.
Definition HouseholderQR.h:77
Two-sided Jacobi SVD decomposition of a rectangular matrix.
Definition JacobiSVD.h:613
JacobiSVD()
Default Constructor.
Definition JacobiSVD.h:643
JacobiSVD & compute(const MatrixBase< Derived > &matrix)
Method performing the decomposition of given matrix. Computes Thin/Full unitaries U/V if specified us...
Definition JacobiSVD.h:716
JacobiSVD(Index rows, Index cols)
Default Constructor with memory preallocation.
Definition JacobiSVD.h:652
JacobiSVD(const MatrixBase< Derived > &matrix)
Constructor performing the decomposition of given matrix, using the custom options specified with the...
Definition JacobiSVD.h:682
JacobiSVD(const MatrixBase< Derived > &matrix, unsigned int computationOptions)
Constructor performing the decomposition of given matrix using specified options for computing unitar...
Definition JacobiSVD.h:705
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
SVDBase()
Definition SVDBase.h:352
Base class for triangular part in a matrix.
Definition TriangularMatrix.h:68
@ FullPivHouseholderQRPreconditioner
Definition Constants.h:432
@ PropagateNaN
Definition Constants.h:343
@ InvalidInput
Definition Constants.h:464