Browse Source

Math/Algorithms: improve SVD docs, add a test for xform decomposition.

pull/268/head
Vladimír Vondruš 8 years ago
parent
commit
44b059bbc5
  1. 21
      src/Magnum/Math/Algorithms/Svd.h
  2. 29
      src/Magnum/Math/Algorithms/Test/SvdTest.cpp

21
src/Magnum/Math/Algorithms/Svd.h

@ -60,20 +60,25 @@ template<> constexpr Double smallestDelta<Double>() { return 1.0e-64; }
Performs [Thin SVD](https://en.wikipedia.org/wiki/Singular-value_decomposition#Thin_SVD) Performs [Thin SVD](https://en.wikipedia.org/wiki/Singular-value_decomposition#Thin_SVD)
on given matrix where @p rows >= `cols`: @f[ on given matrix where @p rows >= `cols`: @f[
M = U \Sigma V^* \boldsymbol{M} = \boldsymbol{U} \boldsymbol{\Sigma} \boldsymbol{V}^*
@f] @f]
Returns first @p cols column vectors of @f$ U @f$, diagonal of @f$ \Sigma @f$ Returns first @p cols column vectors of @f$ \boldsymbol{U} @f$, diagonal of
and non-transposed @f$ V @f$. If the solution doesn't converge, returns @f$ \boldsymbol{\Sigma} @f$ and non-transposed @f$ \boldsymbol{V} @f$. If the
zero matrices. solution doesn't converge, returns zero matrices.
Full @f$ U @f$, @f$ \Sigma @f$ matrices and original @f$ M @f$ matrix can be Full @f$ \boldsymbol{U} @f$, @f$ \boldsymbol{\Sigma} @f$ matrices and original
reconstructed from the values as following: @f$ \boldsymbol{M} @f$ matrix can be reconstructed from the values as
following:
@snippet MagnumMathAlgorithms.cpp svd @snippet MagnumMathAlgorithms.cpp svd
Implementation based on *Golub, G. H.; Reinsch, C. (1970). "Singular value One possible use is to decompose a transformation matrix into separate rotation
decomposition and least squares solutions"*. and scaling parts. Note, however, that the decomposition is not unique. See the
[associated test case](https://github.com/mosra/magnum/blob/master/src/Magnum/Math/Algorithms/Test/SvdTest.cpp)
for an example. Implementation based on *Golub, G. H.; Reinsch, C. (1970).
"Singular value decomposition and least squares solutions"*.
@see @ref qr(), @ref Matrix3::rotationShear(), @ref Matrix4::rotationShear()
*/ */
/* The matrix is passed by value because it is changed inside */ /* The matrix is passed by value because it is changed inside */
template<std::size_t cols, std::size_t rows, class T> std::tuple<RectangularMatrix<cols, rows, T>, Vector<cols, T>, Matrix<cols, T>> svd(RectangularMatrix<cols, rows, T> m) { template<std::size_t cols, std::size_t rows, class T> std::tuple<RectangularMatrix<cols, rows, T>, Vector<cols, T>, Matrix<cols, T>> svd(RectangularMatrix<cols, rows, T> m) {

29
src/Magnum/Math/Algorithms/Test/SvdTest.cpp

@ -25,6 +25,7 @@
#include <Corrade/TestSuite/Tester.h> #include <Corrade/TestSuite/Tester.h>
#include "Magnum/Math/Matrix4.h"
#include "Magnum/Math/Algorithms/Svd.h" #include "Magnum/Math/Algorithms/Svd.h"
namespace Magnum { namespace Math { namespace Algorithms { namespace Test { namespace Magnum { namespace Math { namespace Algorithms { namespace Test {
@ -33,6 +34,7 @@ struct SvdTest: Corrade::TestSuite::Tester {
explicit SvdTest(); explicit SvdTest();
template<class T> void test(); template<class T> void test();
void decomposeRotationShear();
}; };
template<class T> using Matrix5x8 = RectangularMatrix<5, 8, T>; template<class T> using Matrix5x8 = RectangularMatrix<5, 8, T>;
@ -42,8 +44,9 @@ template<class T> using Vector8 = Vector<8, T>;
template<class T> using Vector5 = Vector<5, T>; template<class T> using Vector5 = Vector<5, T>;
SvdTest::SvdTest() { SvdTest::SvdTest() {
addTests<SvdTest>({&SvdTest::test<Float>, addTests({&SvdTest::test<Float>,
&SvdTest::test<Double>}); &SvdTest::test<Double>,
&SvdTest::decomposeRotationShear});
} }
template<class T> void SvdTest::test() { template<class T> void SvdTest::test() {
@ -91,6 +94,28 @@ template<class T> void SvdTest::test() {
} }
} }
void SvdTest::decomposeRotationShear() {
typedef Math::Matrix4<Float> Matrix4;
typedef Math::Matrix3x3<Float> Matrix3x3;
typedef Math::Vector3<Float> Vector3;
using namespace Math::Literals;
Matrix4 a = Matrix4::scaling({1.5f, 2.0f, 1.0f})*Matrix4::rotationZ(35.0_degf);
Matrix3x3 u{NoInit};
Vector3 w{NoInit};
Matrix3x3 v{NoInit};
std::tie(u, w, v) = Algorithms::svd(a.rotationScaling());
CORRADE_COMPARE(u*Matrix3x3::fromDiagonal(w)*v.transposed(), a.rotationScaling());
/* U contains a flipped sign for Z, use it to remove the sign from the
transposed rotation matrix V */
CORRADE_COMPARE(w, (Vector3{1.5f, 2.0f, 1.0f}));
CORRADE_COMPARE(Matrix4::from(u*v.transposed(), {}), Matrix4::rotationZ(35.0_degf));
}
}}}} }}}}
CORRADE_TEST_MAIN(Magnum::Math::Algorithms::Test::SvdTest) CORRADE_TEST_MAIN(Magnum::Math::Algorithms::Test::SvdTest)

Loading…
Cancel
Save