Further LU test improvements. I'm not aware of any test failures anymore, not even with huge numbers of repetitions.

Finally the createRandomMatrixOfRank() function is renamed to createRandomPIMatrixOfRank, where PI stands for 'partial isometry', that is, a matrix whose singular values are 0 or 1.
This commit is contained in:
Benoit Jacob 2010-02-23 15:40:24 -05:00
parent 7dc75380c1
commit d92df336ad
6 changed files with 42 additions and 50 deletions

View File

@ -422,8 +422,11 @@ FullPivLU<MatrixType>& FullPivLU<MatrixType>::compute(const MatrixType& matrix)
// when k==0, biggest_in_corner is the biggest coeff absolute value in the original matrix // when k==0, biggest_in_corner is the biggest coeff absolute value in the original matrix
if(k == 0) cutoff = biggest_in_corner * NumTraits<Scalar>::epsilon(); if(k == 0) cutoff = biggest_in_corner * NumTraits<Scalar>::epsilon();
// if the pivot (hence the corner) is exactly zero, terminate to avoid generating nan/inf values // if the pivot (hence the corner) is "zero", terminate to avoid generating nan/inf values.
if(ei_abs(biggest_in_corner) < cutoff) // Notice that using an exact comparison (biggest_in_corner==0) here, as Golub-van Loan do in
// their pseudo-code, results in numerical instability! The cutoff here has been validated
// by running the unit test 'lu' with many repetitions.
if(biggest_in_corner < cutoff)
{ {
// before exiting, make sure to initialize the still uninitialized transpositions // before exiting, make sure to initialize the still uninitialized transpositions
// in a sane state without destroying what we already have. // in a sane state without destroying what we already have.

View File

@ -42,7 +42,7 @@ template<typename MatrixType> void inverse(const MatrixType& m)
m2(rows, cols), m2(rows, cols),
mzero = MatrixType::Zero(rows, cols), mzero = MatrixType::Zero(rows, cols),
identity = MatrixType::Identity(rows, rows); identity = MatrixType::Identity(rows, rows);
createRandomProjectionOfRank(rows,rows,rows,m1); createRandomPIMatrixOfRank(rows,rows,rows,m1);
m2 = m1.inverse(); m2 = m1.inverse();
VERIFY_IS_APPROX(m1, m2.inverse() ); VERIFY_IS_APPROX(m1, m2.inverse() );

View File

@ -28,9 +28,6 @@ using namespace std;
template<typename MatrixType> void lu_non_invertible() template<typename MatrixType> void lu_non_invertible()
{ {
static int times_called = 0;
times_called++;
typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::RealScalar RealScalar; typedef typename MatrixType::RealScalar RealScalar;
/* this test covers the following files: /* this test covers the following files:
@ -55,11 +52,16 @@ template<typename MatrixType> void lu_non_invertible()
cols2 = cols = MatrixType::ColsAtCompileTime; cols2 = cols = MatrixType::ColsAtCompileTime;
} }
enum {
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
ColsAtCompileTime = MatrixType::ColsAtCompileTime
};
typedef typename ei_kernel_retval_base<FullPivLU<MatrixType> >::ReturnType KernelMatrixType; typedef typename ei_kernel_retval_base<FullPivLU<MatrixType> >::ReturnType KernelMatrixType;
typedef typename ei_image_retval_base<FullPivLU<MatrixType> >::ReturnType ImageMatrixType; typedef typename ei_image_retval_base<FullPivLU<MatrixType> >::ReturnType ImageMatrixType;
typedef Matrix<typename MatrixType::Scalar, Dynamic, Dynamic> DynamicMatrixType; typedef Matrix<typename MatrixType::Scalar, ColsAtCompileTime, ColsAtCompileTime>
typedef Matrix<typename MatrixType::Scalar, MatrixType::ColsAtCompileTime, MatrixType::ColsAtCompileTime>
CMatrixType; CMatrixType;
typedef Matrix<typename MatrixType::Scalar, RowsAtCompileTime, RowsAtCompileTime>
RMatrixType;
int rank = ei_random<int>(1, std::min(rows, cols)-1); int rank = ei_random<int>(1, std::min(rows, cols)-1);
@ -68,26 +70,21 @@ template<typename MatrixType> void lu_non_invertible()
MatrixType m1(rows, cols), m3(rows, cols2); MatrixType m1(rows, cols), m3(rows, cols2);
CMatrixType m2(cols, cols2); CMatrixType m2(cols, cols2);
createRandomProjectionOfRank(rank, rows, cols, m1); createRandomPIMatrixOfRank(rank, rows, cols, m1);
FullPivLU<MatrixType> lu; FullPivLU<MatrixType> lu;
// The special value 0.01 below works well in tests. Keep in mind that we're only computing the rank of projections. // The special value 0.01 below works well in tests. Keep in mind that we're only computing the rank
// So it's not clear at all the epsilon should play any role there. // of singular values are either 0 or 1.
// So it's not clear at all that the epsilon should play any role there.
lu.setThreshold(RealScalar(0.01)); lu.setThreshold(RealScalar(0.01));
lu.compute(m1); lu.compute(m1);
// FIXME need better way to construct trapezoid matrices. extend triangularView to support rectangular. MatrixType u(rows,cols);
DynamicMatrixType u(rows,cols); u = lu.matrixLU().template triangularView<Upper>();
for(int i = 0; i < rows; i++) RMatrixType l = RMatrixType::Identity(rows,rows);
for(int j = 0; j < cols; j++) l.block(0,0,rows,std::min(rows,cols)).template triangularView<StrictlyLower>()
u(i,j) = i>j ? Scalar(0) : lu.matrixLU()(i,j); = lu.matrixLU().block(0,0,rows,std::min(rows,cols));
DynamicMatrixType l(rows,rows);
for(int i = 0; i < rows; i++)
for(int j = 0; j < rows; j++)
l(i,j) = (i<j || j>=cols) ? Scalar(0)
: i==j ? Scalar(1)
: lu.matrixLU()(i,j);
VERIFY_IS_APPROX(lu.permutationP() * m1 * lu.permutationQ(), l*u); VERIFY_IS_APPROX(lu.permutationP() * m1 * lu.permutationQ(), l*u);
@ -101,20 +98,8 @@ template<typename MatrixType> void lu_non_invertible()
VERIFY(!lu.isSurjective()); VERIFY(!lu.isSurjective());
VERIFY((m1 * m1kernel).isMuchSmallerThan(m1)); VERIFY((m1 * m1kernel).isMuchSmallerThan(m1));
VERIFY(m1image.fullPivLu().rank() == rank); VERIFY(m1image.fullPivLu().rank() == rank);
VERIFY_IS_APPROX(m1 * m1.adjoint() * m1image, m1image);
// The following test is damn hard to get to succeed over a large number of repetitions.
// We're checking that the image is indeed the image, i.e. adding it as new columns doesn't increase the rank.
// Since we've already tested rank() above, the point here is not to test rank(), it is to test image().
// Since image() is implemented in a very simple way that doesn't leave much room for choice, the occasional
// errors that we get here (one in 1e+4 repetitions roughly) are probably just a sign that it's a really
// hard test, so we just limit how many times it's run.
if(times_called < 100)
{
DynamicMatrixType sidebyside(m1.rows(), m1.cols() + m1image.cols());
sidebyside << m1, m1image;
VERIFY(sidebyside.fullPivLu().rank() == rank);
}
m2 = CMatrixType::Random(cols,cols2); m2 = CMatrixType::Random(cols,cols2);
m3 = m1*m2; m3 = m1*m2;
m2 = CMatrixType::Random(cols,cols2); m2 = CMatrixType::Random(cols,cols2);
@ -128,20 +113,18 @@ template<typename MatrixType> void lu_invertible()
/* this test covers the following files: /* this test covers the following files:
LU.h LU.h
*/ */
typedef typename MatrixType::Scalar Scalar;
typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar; typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar;
int size = ei_random<int>(1,200); int size = ei_random<int>(1,200);
MatrixType m1(size, size), m2(size, size), m3(size, size); MatrixType m1(size, size), m2(size, size), m3(size, size);
m1 = MatrixType::Random(size,size); FullPivLU<MatrixType> lu;
lu.setThreshold(0.01);
do {
m1 = MatrixType::Random(size,size);
lu.compute(m1);
} while(!lu.isInvertible());
if (ei_is_same_type<RealScalar,float>::ret)
{
// let's build a matrix more stable to inverse
MatrixType a = MatrixType::Random(size,size*2);
m1 += a * a.adjoint();
}
FullPivLU<MatrixType> lu(m1);
VERIFY(0 == lu.dimensionOfKernel()); VERIFY(0 == lu.dimensionOfKernel());
VERIFY(lu.kernel().cols() == 1); // the kernel() should consist of a single (zero) column vector VERIFY(lu.kernel().cols() == 1); // the kernel() should consist of a single (zero) column vector
VERIFY(size == lu.rank()); VERIFY(size == lu.rank());

View File

@ -148,7 +148,7 @@ namespace Eigen
#define EIGEN_INTERNAL_DEBUGGING #define EIGEN_INTERNAL_DEBUGGING
#define EIGEN_NICE_RANDOM #define EIGEN_NICE_RANDOM
#include <Eigen/QR> // required for createRandomProjectionOfRank #include <Eigen/QR> // required for createRandomPIMatrixOfRank
#define VERIFY(a) do { if (!(a)) { \ #define VERIFY(a) do { if (!(a)) { \
@ -342,8 +342,13 @@ inline bool test_isUnitary(const MatrixBase<Derived>& m)
return m.isUnitary(test_precision<typename ei_traits<Derived>::Scalar>()); return m.isUnitary(test_precision<typename ei_traits<Derived>::Scalar>());
} }
/** Creates a random Partial Isometry matrix of given rank.
*
* A partial isometry is a matrix all of whose singular values are either 0 or 1.
* This is very useful to test rank-revealing algorithms.
*/
template<typename MatrixType> template<typename MatrixType>
void createRandomProjectionOfRank(int desired_rank, int rows, int cols, MatrixType& m) void createRandomPIMatrixOfRank(int desired_rank, int rows, int cols, MatrixType& m)
{ {
typedef typename ei_traits<MatrixType>::Scalar Scalar; typedef typename ei_traits<MatrixType>::Scalar Scalar;
enum { Rows = MatrixType::RowsAtCompileTime, Cols = MatrixType::ColsAtCompileTime }; enum { Rows = MatrixType::RowsAtCompileTime, Cols = MatrixType::ColsAtCompileTime };
@ -360,7 +365,8 @@ void createRandomProjectionOfRank(int desired_rank, int rows, int cols, MatrixTy
if(desired_rank == 1) if(desired_rank == 1)
{ {
m = VectorType::Random(rows) * VectorType::Random(cols).transpose(); // here we normalize the vectors to get a partial isometry
m = VectorType::Random(rows).normalized() * VectorType::Random(cols).normalized().transpose();
return; return;
} }

View File

@ -36,7 +36,7 @@ template<typename MatrixType> void qr()
typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> MatrixQType; typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> MatrixQType;
typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, 1> VectorType; typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, 1> VectorType;
MatrixType m1; MatrixType m1;
createRandomProjectionOfRank(rank,rows,cols,m1); createRandomPIMatrixOfRank(rank,rows,cols,m1);
ColPivHouseholderQR<MatrixType> qr(m1); ColPivHouseholderQR<MatrixType> qr(m1);
VERIFY_IS_APPROX(rank, qr.rank()); VERIFY_IS_APPROX(rank, qr.rank());
VERIFY(cols - qr.rank() == qr.dimensionOfKernel()); VERIFY(cols - qr.rank() == qr.dimensionOfKernel());
@ -64,7 +64,7 @@ template<typename MatrixType, int Cols2> void qr_fixedsize()
typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::Scalar Scalar;
int rank = ei_random<int>(1, std::min(int(Rows), int(Cols))-1); int rank = ei_random<int>(1, std::min(int(Rows), int(Cols))-1);
Matrix<Scalar,Rows,Cols> m1; Matrix<Scalar,Rows,Cols> m1;
createRandomProjectionOfRank(rank,Rows,Cols,m1); createRandomPIMatrixOfRank(rank,Rows,Cols,m1);
ColPivHouseholderQR<Matrix<Scalar,Rows,Cols> > qr(m1); ColPivHouseholderQR<Matrix<Scalar,Rows,Cols> > qr(m1);
VERIFY_IS_APPROX(rank, qr.rank()); VERIFY_IS_APPROX(rank, qr.rank());
VERIFY(Cols - qr.rank() == qr.dimensionOfKernel()); VERIFY(Cols - qr.rank() == qr.dimensionOfKernel());

View File

@ -35,7 +35,7 @@ template<typename MatrixType> void qr()
typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> MatrixQType; typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> MatrixQType;
typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, 1> VectorType; typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, 1> VectorType;
MatrixType m1; MatrixType m1;
createRandomProjectionOfRank(rank,rows,cols,m1); createRandomPIMatrixOfRank(rank,rows,cols,m1);
FullPivHouseholderQR<MatrixType> qr(m1); FullPivHouseholderQR<MatrixType> qr(m1);
VERIFY_IS_APPROX(rank, qr.rank()); VERIFY_IS_APPROX(rank, qr.rank());
VERIFY(cols - qr.rank() == qr.dimensionOfKernel()); VERIFY(cols - qr.rank() == qr.dimensionOfKernel());