From 0c2232e5d972986ed90c917b68fb24eef372841b Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Mon, 6 Jul 2009 13:47:41 +0200 Subject: quick reimplementation of SVD from the numeral recipes book: this is still not Eigen style code but at least it works for n>m and it is more accurate than the JAMA based version. (I needed it now, this is why I did that) --- Eigen/src/Geometry/Quaternion.h | 5 +- Eigen/src/SVD/SVD.h | 555 +++++++++++++++++----------------------- test/svd.cpp | 2 +- 3 files changed, 233 insertions(+), 329 deletions(-) diff --git a/Eigen/src/Geometry/Quaternion.h b/Eigen/src/Geometry/Quaternion.h index a76ccbdaf..8d1bbf9d2 100644 --- a/Eigen/src/Geometry/Quaternion.h +++ b/Eigen/src/Geometry/Quaternion.h @@ -371,13 +371,14 @@ inline Quaternion& Quaternion::setFromTwoVectors(const MatrixBas if (ei_isApprox(c,Scalar(-1))) { c = std::max(c,-1); - - SVD > svd(v0 * v0.transpose() + v1 * v1.transpose()); + Matrix m; m << v0.transpose(), v1.transpose(); + SVD > svd(m); Vector3 axis = svd.matrixV().col(2); Scalar w2 = (Scalar(1)+c)*Scalar(0.5); this->w() = ei_sqrt(w2); this->vec() = axis * ei_sqrt(Scalar(1) - w2); + return *this; } diff --git a/Eigen/src/SVD/SVD.h b/Eigen/src/SVD/SVD.h index 45a7fbfa5..71f6763a8 100644 --- a/Eigen/src/SVD/SVD.h +++ b/Eigen/src/SVD/SVD.h @@ -34,9 +34,7 @@ * * \param MatrixType the type of the matrix of which we are computing the SVD decomposition * - * This class performs a standard SVD decomposition of a real matrix A of size \c M x \c N - * with \c M \>= \c N. - * + * This class performs a standard SVD decomposition of a real matrix A of size \c M x \c N. * * \sa MatrixBase::SVD() */ @@ -55,13 +53,13 @@ template class SVD typedef Matrix ColVector; typedef Matrix RowVector; - typedef Matrix MatrixUType; + typedef Matrix MatrixUType; typedef Matrix MatrixVType; - typedef Matrix SingularValuesType; + typedef Matrix SingularValuesType; public: - /** + /** * \brief Default Constructor. * * The default constructor is useful in cases in which the user intends to @@ -70,9 +68,9 @@ template class SVD SVD() : m_matU(), m_matV(), m_sigma(), m_isInitialized(false) {} SVD(const MatrixType& matrix) - : m_matU(matrix.rows(), std::min(matrix.rows(), matrix.cols())), + : m_matU(matrix.rows(), matrix.rows()), m_matV(matrix.cols(),matrix.cols()), - m_sigma(std::min(matrix.rows(),matrix.cols())), + m_sigma(matrix.cols()), m_isInitialized(false) { compute(matrix); @@ -81,22 +79,22 @@ template class SVD template bool solve(const MatrixBase &b, ResultType* result) const; - const MatrixUType& matrixU() const - { + const MatrixUType& matrixU() const + { ei_assert(m_isInitialized && "SVD is not initialized."); - return m_matU; + return m_matU; } - const SingularValuesType& singularValues() const + const SingularValuesType& singularValues() const { ei_assert(m_isInitialized && "SVD is not initialized."); - return m_sigma; + return m_sigma; } - const MatrixVType& matrixV() const + const MatrixVType& matrixV() const { ei_assert(m_isInitialized && "SVD is not initialized."); - return m_matV; + return m_matV; } void compute(const MatrixType& matrix); @@ -111,6 +109,23 @@ template class SVD template void computeScalingRotation(ScalingType *positive, RotationType *unitary) const; + protected: + // Computes (a^2 + b^2)^(1/2) without destructive underflow or overflow. + inline static Scalar pythagora(Scalar a, Scalar b) + { + Scalar abs_a = ei_abs(a); + Scalar abs_b = ei_abs(b); + if (abs_a > abs_b) + return abs_a*ei_sqrt(Scalar(1.0)+ei_abs2(abs_b/abs_a)); + else + return (abs_b == Scalar(0.0) ? Scalar(0.0) : abs_b*ei_sqrt(Scalar(1.0)+ei_abs2(abs_a/abs_b))); + } + + inline static Scalar sign(Scalar a, Scalar b) + { + return (b >= Scalar(0.0) ? ei_abs(a) : -ei_abs(a)); + } + protected: /** \internal */ MatrixUType m_matU; @@ -123,7 +138,7 @@ template class SVD /** Computes / recomputes the SVD decomposition A = U S V^* of \a matrix * - * \note this code has been adapted from JAMA (public domain) + * \note this code has been adapted from Numerical Recipes, second edition. */ template void SVD::compute(const MatrixType& matrix) @@ -132,371 +147,259 @@ void SVD::compute(const MatrixType& matrix) const int n = matrix.cols(); const int nu = std::min(m,n); - m_matU.resize(m, nu); + m_matU.resize(m, m); m_matU.setZero(); - m_sigma.resize(std::min(m,n)); + m_sigma.resize(n); m_matV.resize(n,n); - RowVector e(n); - ColVector work(m); - MatrixType matA(matrix); - const bool wantu = true; - const bool wantv = true; - int i=0, j=0, k=0; - - // Reduce A to bidiagonal form, storing the diagonal elements - // in s and the super-diagonal elements in e. - int nct = std::min(m-1,n); - int nrt = std::max(0,std::min(n-2,m)); - for (k = 0; k < std::max(nct,nrt); ++k) - { - if (k < nct) - { - // Compute the transformation for the k-th column and - // place the k-th diagonal in m_sigma[k]. - m_sigma[k] = matA.col(k).end(m-k).norm(); - if (m_sigma[k] != 0.0) // FIXME - { - if (matA(k,k) < 0.0) - m_sigma[k] = -m_sigma[k]; - matA.col(k).end(m-k) /= m_sigma[k]; - matA(k,k) += 1.0; - } - m_sigma[k] = -m_sigma[k]; - } - - for (j = k+1; j < n; ++j) - { - if ((k < nct) && (m_sigma[k] != 0.0)) - { - // Apply the transformation. - Scalar t = matA.col(k).end(m-k).dot(matA.col(j).end(m-k)); // FIXME dot product or cwise prod + .sum() ?? - t = -t/matA(k,k); - matA.col(j).end(m-k) += t * matA.col(k).end(m-k); - } + int max_iters = 30; - // Place the k-th row of A into e for the - // subsequent calculation of the row transformation. - e[j] = matA(k,j); - } + MatrixVType& V = m_matV; + MatrixType A = matrix; + SingularValuesType& W = m_sigma; - // Place the transformation in U for subsequent back multiplication. - if (wantu & (k < nct)) - m_matU.col(k).end(m-k) = matA.col(k).end(m-k); + int flag,i,its,j,jj,k,l,nm; + Scalar anorm, c, f, g, h, s, scale, x, y, z; + bool convergence = true; - if (k < nrt) + Matrix rv1(n); + g = scale = anorm = 0; + // Householder reduction to bidiagonal form. + for (i=0; i= 0; k--) + W[i] = scale *g; + g = s = scale = 0.0; + if (i < m && i != (n-1)) { - if (m_sigma[k] != 0.0) + scale = A.row(i).end(n-l).cwise().abs().sum(); + if (scale) { - for (j = k+1; j < nu; ++j) + for (k=l; k0) - m_matU.col(k).start(k-1).setZero(); - } - else - { - m_matU.col(k).setZero(); - m_matU(k,k) = 1.0; + f = A(i, l); + g = -sign(ei_sqrt(s),f); + h = f*g - s; + A(i, l) = f-g; + for (k=l; k=0; i--) { - for (k = n-1; k >= 0; k--) + //Accumulation of right-hand transformations. + if (i < (n-1)) { - if ((k < nrt) & (e[k] != 0.0)) + if (g) { - for (j = k+1; j < nu; ++j) + for (j=l; j::ret ? Scalar(-23) : Scalar(-52)); - while (p > 0) + // Accumulation of left-hand transformations. + for (i=std::min(m,n)-1; i>=0; i--) { - int k=0; - int kase=0; - - // Here is where a test for too many iterations would go. - - // This section of the program inspects for - // negligible elements in the s and e arrays. On - // completion the variables kase and k are set as follows. - - // kase = 1 if s(p) and e[k-1] are negligible and k

= -1; --k) + l = i+1; + g = W[i]; + for (j=l; j=0; k--) + { + for (its=1; its<=max_iters; its++) { - int ks; - for (ks = p-1; ks >= k; --ks) + flag=1; + for (l=k; l>=0; l--) { - if (ks == k) - break; - Scalar t = (ks != p ? ei_abs(e[ks]) : Scalar(0)) + (ks != k+1 ? ei_abs(e[ks-1]) : Scalar(0)); - if (ei_abs(m_sigma[ks]) <= eps*t) + // Test for splitting. + nm=l-1; + // Note that rv1[1] is always zero. + if ((double)(ei_abs(rv1[l])+anorm) == anorm) { - m_sigma[ks] = 0.0; + flag=0; break; } + if ((double)(ei_abs(W[nm])+anorm) == anorm) + break; } - if (ks == k) - { - kase = 3; - } - else if (ks == p-1) - { - kase = 1; - } - else - { - kase = 2; - k = ks; - } - } - ++k; - - // Perform the task indicated by kase. - switch (kase) - { - - // Deflate negligible s(p). - case 1: + if (flag) { - Scalar f(e[p-2]); - e[p-2] = 0.0; - for (j = p-2; j >= k; --j) + c=0.0; //Cancellation of rv1[l], if l > 1. + s=1.0; + for (i=l ;i<=k; i++) { - Scalar t(ei_hypot(m_sigma[j],f)); - Scalar cs(m_sigma[j]/t); - Scalar sn(f/t); - m_sigma[j] = t; - if (j != k) - { - f = -sn*e[j-1]; - e[j-1] = cs*e[j-1]; - } - if (wantv) + f = s*rv1[i]; + rv1[i] = c*rv1[i]; + if ((double)(ei_abs(f)+anorm) == anorm) + break; + g = W[i]; + h = pythagora(f,g); + W[i] = h; + h = (Scalar)1.0/h; + c = g*h; + s = -f*h; + for (j=0; j= m_sigma[k+1]) - break; - Scalar t = m_sigma[k]; - m_sigma[k] = m_sigma[k+1]; - m_sigma[k+1] = t; - if (wantv && (k < n-1)) - m_matV.col(k).swap(m_matV.col(k+1)); - if (wantu && (k < m-1)) - m_matU.col(k).swap(m_matU.col(k+1)); - ++k; - } - iter = 0; - p--; + // sort the singular values: + { + for (int i=0; i=n) + m_matU.block(0,0,m,n) = A; + else + m_matU = A.block(0,0,m,m); m_isInitialized = true; } diff --git a/test/svd.cpp b/test/svd.cpp index 2eb7881a0..93e5ea017 100644 --- a/test/svd.cpp +++ b/test/svd.cpp @@ -50,7 +50,7 @@ template void svd(const MatrixType& m) MatrixType sigma = MatrixType::Zero(rows,cols); MatrixType matU = MatrixType::Zero(rows,rows); sigma.block(0,0,cols,cols) = svd.singularValues().asDiagonal(); - matU.block(0,0,rows,cols) = svd.matrixU(); + matU = svd.matrixU(); VERIFY_IS_APPROX(a, matU * sigma * svd.matrixV().transpose()); } -- cgit v1.2.3