mirror of
https://gitlab.com/libeigen/eigen.git
synced 2025-02-17 18:09:55 +08:00
Add R-Bidiagonalization step to BDCSVD
This commit is contained in:
parent
e99163e732
commit
705ae70646
@ -430,7 +430,9 @@ enum QRPreconditioners {
|
||||
/** Use a QR decomposition without pivoting as the first step. */
|
||||
HouseholderQRPreconditioner = 0x80,
|
||||
/** Use a QR decomposition with full pivoting as the first step. */
|
||||
FullPivHouseholderQRPreconditioner = 0xC0
|
||||
FullPivHouseholderQRPreconditioner = 0xC0,
|
||||
/** Used to disable the QR Preconditioner in BDCSVD. */
|
||||
DisableQRDecomposition = NoQRPreconditioner
|
||||
};
|
||||
|
||||
#ifdef Success
|
||||
|
@ -76,9 +76,11 @@ struct allocate_small_svd<MatrixType, 0> {
|
||||
* \tparam MatrixType_ the type of the matrix of which we are computing the SVD decomposition
|
||||
*
|
||||
* \tparam Options_ this optional parameter allows one to specify options for computing unitaries \a U and \a V.
|
||||
* Possible values are #ComputeThinU, #ComputeThinV, #ComputeFullU, #ComputeFullV.
|
||||
* It is not possible to request both the thin and full version of \a U or \a V.
|
||||
* By default, unitaries are not computed.
|
||||
* Possible values are #ComputeThinU, #ComputeThinV, #ComputeFullU, #ComputeFullV, and
|
||||
* #DisableQRDecomposition. It is not possible to request both the thin and full version of \a U or
|
||||
* \a V. By default, unitaries are not computed. BDCSVD uses R-Bidiagonalization to improve
|
||||
* performance on tall and wide matrices. For backwards compatility, the option
|
||||
* #DisableQRDecomposition can be used to disable this optimization.
|
||||
*
|
||||
* This class first reduces the input matrix to bi-diagonal form using class UpperBidiagonalization,
|
||||
* and then performs a divide-and-conquer diagonalization. Small blocks are diagonalized using class JacobiSVD.
|
||||
@ -110,6 +112,8 @@ public:
|
||||
typedef typename Base::Index Index;
|
||||
enum {
|
||||
Options = Options_,
|
||||
QRDecomposition = Options & internal::QRPreconditionerBits,
|
||||
ComputationOptions = Options & internal::ComputationOptionsBits,
|
||||
RowsAtCompileTime = Base::RowsAtCompileTime,
|
||||
ColsAtCompileTime = Base::ColsAtCompileTime,
|
||||
DiagSizeAtCompileTime = Base::DiagSizeAtCompileTime,
|
||||
@ -251,8 +255,12 @@ private:
|
||||
ArrayXr m_workspace;
|
||||
ArrayXi m_workspaceI;
|
||||
int m_algoswap;
|
||||
bool m_isTranspose, m_compU, m_compV;
|
||||
JacobiSVD<MatrixType, Options> smallSvd;
|
||||
bool m_isTranspose, m_compU, m_compV, m_useQrDecomp;
|
||||
JacobiSVD<MatrixType, ComputationOptions> smallSvd;
|
||||
HouseholderQR<MatrixX> qrDecomp;
|
||||
internal::UpperBidiagonalization<MatrixX> bid;
|
||||
MatrixX copyWorkspace;
|
||||
MatrixX reducedTriangle;
|
||||
|
||||
using Base::m_computationOptions;
|
||||
using Base::m_computeThinU;
|
||||
@ -276,7 +284,7 @@ void BDCSVD<MatrixType, Options>::allocate(Index rows, Index cols, unsigned int
|
||||
return;
|
||||
|
||||
if (cols < m_algoswap)
|
||||
internal::allocate_small_svd<MatrixType, Options>::run(smallSvd, rows, cols, computationOptions);
|
||||
internal::allocate_small_svd<MatrixType, ComputationOptions>::run(smallSvd, rows, cols, computationOptions);
|
||||
|
||||
m_computed = MatrixXr::Zero(m_diagSize + 1, m_diagSize );
|
||||
m_compU = computeV();
|
||||
@ -285,6 +293,22 @@ void BDCSVD<MatrixType, Options>::allocate(Index rows, Index cols, unsigned int
|
||||
if (m_isTranspose)
|
||||
std::swap(m_compU, m_compV);
|
||||
|
||||
// kMinAspectRatio is the crossover point that determines if we perform R-Bidiagonalization
|
||||
// or bidiagonalize the input matrix directly.
|
||||
// It is based off of LAPACK's dgesdd routine, which uses 11.0/6.0
|
||||
// we use a larger scalar to prevent a regression for relatively square matrices.
|
||||
constexpr Index kMinAspectRatio = 4;
|
||||
constexpr bool disableQrDecomp = static_cast<int>(QRDecomposition) == static_cast<int>(DisableQRDecomposition);
|
||||
m_useQrDecomp = !disableQrDecomp && ((rows / kMinAspectRatio > cols) || (cols / kMinAspectRatio > rows));
|
||||
if (m_useQrDecomp) {
|
||||
qrDecomp = HouseholderQR<MatrixX>((std::max)(rows, cols), (std::min)(rows, cols));
|
||||
reducedTriangle = MatrixX(m_diagSize, m_diagSize);
|
||||
}
|
||||
|
||||
copyWorkspace = MatrixX(m_isTranspose ? cols : rows, m_isTranspose ? rows : cols);
|
||||
bid = internal::UpperBidiagonalization<MatrixX>(m_useQrDecomp ? m_diagSize : copyWorkspace.rows(),
|
||||
m_useQrDecomp ? m_diagSize : copyWorkspace.cols());
|
||||
|
||||
if (m_compU) m_naiveU = MatrixXr::Zero(m_diagSize + 1, m_diagSize + 1 );
|
||||
else m_naiveU = MatrixXr::Zero(2, m_diagSize + 1 );
|
||||
|
||||
@ -330,13 +354,22 @@ BDCSVD<MatrixType, Options>& BDCSVD<MatrixType, Options>::compute_impl(const Mat
|
||||
}
|
||||
|
||||
if(numext::is_exactly_zero(scale)) scale = Literal(1);
|
||||
MatrixX copy;
|
||||
if (m_isTranspose) copy = matrix.adjoint()/scale;
|
||||
else copy = matrix/scale;
|
||||
|
||||
//**** step 1 - Bidiagonalization
|
||||
// FIXME this line involves temporaries
|
||||
internal::UpperBidiagonalization<MatrixX> bid(copy);
|
||||
if (m_isTranspose) copyWorkspace = matrix.adjoint() / scale;
|
||||
else copyWorkspace = matrix / scale;
|
||||
|
||||
//**** step 1 - Bidiagonalization.
|
||||
// If the problem is sufficiently rectangular, we perform R-Bidiagonalization: compute A = Q(R/0)
|
||||
// and then bidiagonalize R. Otherwise, if the problem is relatively square, we
|
||||
// bidiagonalize the input matrix directly.
|
||||
if (m_useQrDecomp) {
|
||||
qrDecomp.compute(copyWorkspace);
|
||||
reducedTriangle = qrDecomp.matrixQR().topRows(m_diagSize);
|
||||
reducedTriangle.template triangularView<StrictlyLower>().setZero();
|
||||
bid.compute(reducedTriangle);
|
||||
} else {
|
||||
bid.compute(copyWorkspace);
|
||||
}
|
||||
|
||||
//**** step 2 - Divide & Conquer
|
||||
m_naiveU.setZero();
|
||||
@ -368,13 +401,15 @@ BDCSVD<MatrixType, Options>& BDCSVD<MatrixType, Options>::compute_impl(const Mat
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE
|
||||
// std::cout << "m_naiveU\n" << m_naiveU << "\n\n";
|
||||
// std::cout << "m_naiveV\n" << m_naiveV << "\n\n";
|
||||
#endif
|
||||
//**** step 4 - Finalize unitaries U and V
|
||||
if(m_isTranspose) copyUV(bid.householderV(), bid.householderU(), m_naiveV, m_naiveU);
|
||||
else copyUV(bid.householderU(), bid.householderV(), m_naiveU, m_naiveV);
|
||||
|
||||
if (m_useQrDecomp) {
|
||||
if (m_isTranspose && computeV()) m_matrixV.applyOnTheLeft(qrDecomp.householderQ());
|
||||
else if (!m_isTranspose && computeU()) m_matrixU.applyOnTheLeft(qrDecomp.householderQ());
|
||||
}
|
||||
|
||||
m_isInitialized = true;
|
||||
return *this;
|
||||
} // end compute
|
||||
@ -386,17 +421,21 @@ void BDCSVD<MatrixType, Options>::copyUV(const HouseholderU& householderU, const
|
||||
// Note exchange of U and V: m_matrixU is set from m_naiveV and vice versa
|
||||
if (computeU())
|
||||
{
|
||||
Index Ucols = m_computeThinU ? m_diagSize : householderU.cols();
|
||||
m_matrixU = MatrixX::Identity(householderU.cols(), Ucols);
|
||||
Index Ucols = m_computeThinU ? m_diagSize : rows();
|
||||
m_matrixU = MatrixX::Identity(rows(), Ucols);
|
||||
m_matrixU.topLeftCorner(m_diagSize, m_diagSize) = naiveV.template cast<Scalar>().topLeftCorner(m_diagSize, m_diagSize);
|
||||
householderU.applyThisOnTheLeft(m_matrixU); // FIXME this line involves a temporary buffer
|
||||
// FIXME the following conditionals involve temporary buffers
|
||||
if (m_useQrDecomp) m_matrixU.topLeftCorner(householderU.cols(), m_diagSize).applyOnTheLeft(householderU);
|
||||
else m_matrixU.applyOnTheLeft(householderU);
|
||||
}
|
||||
if (computeV())
|
||||
{
|
||||
Index Vcols = m_computeThinV ? m_diagSize : householderV.cols();
|
||||
m_matrixV = MatrixX::Identity(householderV.cols(), Vcols);
|
||||
Index Vcols = m_computeThinV ? m_diagSize : cols();
|
||||
m_matrixV = MatrixX::Identity(cols(), Vcols);
|
||||
m_matrixV.topLeftCorner(m_diagSize, m_diagSize) = naiveU.template cast<Scalar>().topLeftCorner(m_diagSize, m_diagSize);
|
||||
householderV.applyThisOnTheLeft(m_matrixV); // FIXME this line involves a temporary buffer
|
||||
// FIXME the following conditionals involve temporary buffers
|
||||
if (m_useQrDecomp) m_matrixV.topLeftCorner(householderV.cols(), m_diagSize).applyOnTheLeft(householderV);
|
||||
else m_matrixV.applyOnTheLeft(householderV);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ template<typename MatrixType_> class UpperBidiagonalization
|
||||
* The default constructor is useful in cases in which the user intends to
|
||||
* perform decompositions via Bidiagonalization::compute(const MatrixType&).
|
||||
*/
|
||||
UpperBidiagonalization() : m_householder(), m_bidiagonal(), m_isInitialized(false) {}
|
||||
UpperBidiagonalization() : m_householder(), m_bidiagonal(0, 0), m_isInitialized(false) {}
|
||||
|
||||
explicit UpperBidiagonalization(const MatrixType& matrix)
|
||||
: m_householder(matrix.rows(), matrix.cols()),
|
||||
@ -62,7 +62,13 @@ template<typename MatrixType_> class UpperBidiagonalization
|
||||
{
|
||||
compute(matrix);
|
||||
}
|
||||
|
||||
|
||||
UpperBidiagonalization(Index rows, Index cols)
|
||||
: m_householder(rows, cols),
|
||||
m_bidiagonal(cols, cols),
|
||||
m_isInitialized(false)
|
||||
{}
|
||||
|
||||
UpperBidiagonalization& compute(const MatrixType& matrix);
|
||||
UpperBidiagonalization& computeUnblocked(const MatrixType& matrix);
|
||||
|
||||
|
@ -46,10 +46,17 @@ void bdcsvd_method()
|
||||
VERIFY_IS_APPROX(m.bdcSvd(ComputeFullU|ComputeFullV).solve(m), m);
|
||||
VERIFY_IS_APPROX(m.bdcSvd(ComputeFullU|ComputeFullV).transpose().solve(m), m);
|
||||
VERIFY_IS_APPROX(m.bdcSvd(ComputeFullU|ComputeFullV).adjoint().solve(m), m);
|
||||
|
||||
VERIFY_IS_APPROX(m.template bdcSvd<DisableQRDecomposition>(ComputeFullU|ComputeFullV).solve(m), m);
|
||||
VERIFY_IS_APPROX(m.template bdcSvd<DisableQRDecomposition>(ComputeFullU|ComputeFullV).transpose().solve(m), m);
|
||||
VERIFY_IS_APPROX(m.template bdcSvd<DisableQRDecomposition>(ComputeFullU|ComputeFullV).adjoint().solve(m), m);
|
||||
|
||||
VERIFY_IS_APPROX(m.template bdcSvd<ComputeFullU | ComputeFullV>().solve(m), m);
|
||||
VERIFY_IS_APPROX(m.template bdcSvd<ComputeFullU | ComputeFullV>().transpose().solve(m), m);
|
||||
VERIFY_IS_APPROX(m.template bdcSvd<ComputeFullU | ComputeFullV>().adjoint().solve(m), m);
|
||||
|
||||
VERIFY_IS_APPROX(m.template bdcSvd<ComputeFullU | ComputeFullV | DisableQRDecomposition>().solve(m), m);
|
||||
VERIFY_IS_APPROX(m.template bdcSvd<ComputeFullU | ComputeFullV | DisableQRDecomposition>().transpose().solve(m), m);
|
||||
VERIFY_IS_APPROX(m.template bdcSvd<ComputeFullU | ComputeFullV | DisableQRDecomposition>().adjoint().solve(m), m);
|
||||
}
|
||||
|
||||
// compare the Singular values returned with Jacobi and Bdc
|
||||
@ -88,7 +95,7 @@ void compare_bdc_jacobi_instance(bool structure_as_m, int algoswap = 16)
|
||||
|
||||
template <typename MatrixType>
|
||||
void bdcsvd_all_options(const MatrixType& input = MatrixType()) {
|
||||
MatrixType m = input;
|
||||
MatrixType m(input.rows(), input.cols());
|
||||
svd_fill_random(m);
|
||||
svd_option_checks<MatrixType, 0>(m);
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ void jacobisvd_method()
|
||||
|
||||
template <typename MatrixType>
|
||||
void jacobisvd_all_options(const MatrixType& input = MatrixType()) {
|
||||
MatrixType m = input;
|
||||
MatrixType m(input.rows(), input.cols());
|
||||
svd_fill_random(m);
|
||||
svd_option_checks<MatrixType, 0>(m);
|
||||
svd_option_checks<MatrixType, ColPivHouseholderQRPreconditioner>(m);
|
||||
|
Loading…
Reference in New Issue
Block a user