LU and PartialLU decomposition interface unification.

Added default ctor and public compute method as well
as safe-guards against uninitialized usage.
Added unit tests for the safe-guards.
This commit is contained in:
Hauke Heibel 2009-05-22 14:27:58 +02:00
parent 5c5789cf0f
commit 2c247fc8a8
3 changed files with 140 additions and 16 deletions

View File

@ -92,12 +92,22 @@ template<typename MatrixType> class LU
MatrixType::MaxColsAtCompileTime // so it has the same number of rows and at most as many columns.
> ImageResultType;
/**
* \brief Default Constructor.
*
* The default constructor is useful in cases in which the user intends to
* perform decompositions via LU::compute(const MatrixType&).
*/
LU();
/** Constructor.
*
* \param matrix the matrix of which to compute the LU decomposition.
*/
LU(const MatrixType& matrix);
void compute(const MatrixType& matrix);
/** \returns the LU decomposition matrix: the upper-triangular part is U, the
* unit-lower-triangular part is L (at least for square matrices; in the non-square
* case, special care is needed, see the documentation of class LU).
@ -106,6 +116,7 @@ template<typename MatrixType> class LU
*/
inline const MatrixType& matrixLU() const
{
ei_assert(m_originalMatrix != 0 && "LU is not initialized.");
return m_lu;
}
@ -117,6 +128,7 @@ template<typename MatrixType> class LU
*/
inline const IntColVectorType& permutationP() const
{
ei_assert(m_originalMatrix != 0 && "LU is not initialized.");
return m_p;
}
@ -128,6 +140,7 @@ template<typename MatrixType> class LU
*/
inline const IntRowVectorType& permutationQ() const
{
ei_assert(m_originalMatrix != 0 && "LU is not initialized.");
return m_q;
}
@ -243,6 +256,7 @@ template<typename MatrixType> class LU
*/
inline int rank() const
{
ei_assert(m_originalMatrix != 0 && "LU is not initialized.");
return m_rank;
}
@ -253,6 +267,7 @@ template<typename MatrixType> class LU
*/
inline int dimensionOfKernel() const
{
ei_assert(m_originalMatrix != 0 && "LU is not initialized.");
return m_lu.cols() - m_rank;
}
@ -264,6 +279,7 @@ template<typename MatrixType> class LU
*/
inline bool isInjective() const
{
ei_assert(m_originalMatrix != 0 && "LU is not initialized.");
return m_rank == m_lu.cols();
}
@ -275,6 +291,7 @@ template<typename MatrixType> class LU
*/
inline bool isSurjective() const
{
ei_assert(m_originalMatrix != 0 && "LU is not initialized.");
return m_rank == m_lu.rows();
}
@ -285,6 +302,7 @@ template<typename MatrixType> class LU
*/
inline bool isInvertible() const
{
ei_assert(m_originalMatrix != 0 && "LU is not initialized.");
return isInjective() && isSurjective();
}
@ -317,7 +335,7 @@ template<typename MatrixType> class LU
}
protected:
const MatrixType& m_originalMatrix;
const MatrixType* m_originalMatrix;
MatrixType m_lu;
IntColVectorType m_p;
IntRowVectorType m_q;
@ -327,12 +345,38 @@ template<typename MatrixType> class LU
};
template<typename MatrixType>
LU<MatrixType>::LU(const MatrixType& matrix)
: m_originalMatrix(matrix),
m_lu(matrix),
m_p(matrix.rows()),
m_q(matrix.cols())
LU<MatrixType>::LU()
: m_originalMatrix(0),
m_lu(),
m_p(),
m_q(),
m_det_pq(0),
m_rank(-1),
m_precision(precision<RealScalar>())
{
}
template<typename MatrixType>
LU<MatrixType>::LU(const MatrixType& matrix)
: m_originalMatrix(0),
m_lu(),
m_p(),
m_q(),
m_det_pq(0),
m_rank(-1),
m_precision(precision<RealScalar>())
{
compute(matrix);
}
template<typename MatrixType>
void LU<MatrixType>::compute(const MatrixType& matrix)
{
m_originalMatrix = &matrix;
m_lu = matrix;
m_p.resize(matrix.rows());
m_q.resize(matrix.cols());
const int size = matrix.diagonalSize();
const int rows = matrix.rows();
const int cols = matrix.cols();
@ -409,6 +453,7 @@ LU<MatrixType>::LU(const MatrixType& matrix)
template<typename MatrixType>
typename ei_traits<MatrixType>::Scalar LU<MatrixType>::determinant() const
{
ei_assert(m_originalMatrix != 0 && "LU is not initialized.");
return Scalar(m_det_pq) * m_lu.diagonal().prod();
}
@ -462,17 +507,20 @@ template<typename MatrixType>
template<typename ImageMatrixType>
void LU<MatrixType>::computeImage(ImageMatrixType *result) const
{
ei_assert(m_rank > 0);
result->resize(m_originalMatrix.rows(), m_rank);
// can be caused by a rank deficient matrix or by calling computeImage on an
// unitialized LU object
ei_assert(m_rank > 0 && "LU is not initialized or Matrix has rank zero.");
result->resize(m_originalMatrix->rows(), m_rank);
for(int i = 0; i < m_rank; ++i)
result->col(i) = m_originalMatrix.col(m_q.coeff(i));
result->col(i) = m_originalMatrix->col(m_q.coeff(i));
}
template<typename MatrixType>
const typename LU<MatrixType>::ImageResultType
LU<MatrixType>::image() const
{
ImageResultType result(m_originalMatrix.rows(), m_rank);
ei_assert(m_originalMatrix != 0 && "LU is not initialized.");
ImageResultType result(m_originalMatrix->rows(), m_rank);
computeImage(&result);
return result;
}
@ -484,6 +532,8 @@ bool LU<MatrixType>::solve(
ResultType *result
) const
{
ei_assert(m_originalMatrix != 0 && "LU is not initialized.");
/* The decomposition PAQ = LU can be rewritten as A = P^{-1} L U Q^{-1}.
* So we proceed as follows:
* Step 1: compute c = Pb.

View File

@ -70,6 +70,14 @@ template<typename MatrixType> class PartialLU
MatrixType::MaxRowsAtCompileTime)
};
/**
* \brief Default Constructor.
*
* The default constructor is useful in cases in which the user intends to
* perform decompositions via PartialLU::compute(const MatrixType&).
*/
PartialLU();
/** Constructor.
*
* \param matrix the matrix of which to compute the LU decomposition.
@ -79,6 +87,8 @@ template<typename MatrixType> class PartialLU
*/
PartialLU(const MatrixType& matrix);
void compute(const MatrixType& matrix);
/** \returns the LU decomposition matrix: the upper-triangular part is U, the
* unit-lower-triangular part is L (at least for square matrices; in the non-square
* case, special care is needed, see the documentation of class LU).
@ -87,8 +97,9 @@ template<typename MatrixType> class PartialLU
*/
inline const MatrixType& matrixLU() const
{
ei_assert(m_isInitialized && "PartialLU is not initialized.");
return m_lu;
}
}
/** \returns a vector of integers, whose size is the number of rows of the matrix being decomposed,
* representing the P permutation i.e. the permutation of the rows. For its precise meaning,
@ -96,6 +107,7 @@ template<typename MatrixType> class PartialLU
*/
inline const IntColVectorType& permutationP() const
{
ei_assert(m_isInitialized && "PartialLU is not initialized.");
return m_p;
}
@ -164,18 +176,37 @@ template<typename MatrixType> class PartialLU
}
protected:
const MatrixType& m_originalMatrix;
MatrixType m_lu;
IntColVectorType m_p;
int m_det_p;
bool m_isInitialized;
};
template<typename MatrixType>
PartialLU<MatrixType>::PartialLU(const MatrixType& matrix)
: m_originalMatrix(matrix),
m_lu(matrix),
m_p(matrix.rows())
PartialLU<MatrixType>::PartialLU()
: m_lu(),
m_p(),
m_det_p(0),
m_isInitialized(false)
{
}
template<typename MatrixType>
PartialLU<MatrixType>::PartialLU(const MatrixType& matrix)
: m_lu(),
m_p(),
m_det_p(0),
m_isInitialized(false)
{
compute(matrix);
}
template<typename MatrixType>
void PartialLU<MatrixType>::compute(const MatrixType& matrix)
{
m_lu = matrix;
m_p.resize(matrix.rows());
ei_assert(matrix.rows() == matrix.cols() && "PartialLU is only for square (and moreover invertible) matrices");
const int size = matrix.rows();
@ -213,11 +244,14 @@ PartialLU<MatrixType>::PartialLU(const MatrixType& matrix)
std::swap(m_p.coeffRef(k), m_p.coeffRef(rows_transpositions.coeff(k)));
m_det_p = (number_of_transpositions%2) ? -1 : 1;
m_isInitialized = true;
}
template<typename MatrixType>
typename ei_traits<MatrixType>::Scalar PartialLU<MatrixType>::determinant() const
{
ei_assert(m_isInitialized && "PartialLU is not initialized.");
return Scalar(m_det_p) * m_lu.diagonal().prod();
}
@ -228,6 +262,8 @@ void PartialLU<MatrixType>::solve(
ResultType *result
) const
{
ei_assert(m_isInitialized && "PartialLU is not initialized.");
/* The decomposition PA = LU can be rewritten as A = P^{-1} L U.
* So we proceed as follows:
* Step 1: compute c = Pb.

View File

@ -92,6 +92,37 @@ template<typename MatrixType> void lu_invertible()
VERIFY(lu.solve(m3, &m2));
}
template<typename MatrixType> void lu_verify_assert()
{
MatrixType tmp;
LU<MatrixType> lu;
VERIFY_RAISES_ASSERT(lu.matrixLU())
VERIFY_RAISES_ASSERT(lu.permutationP())
VERIFY_RAISES_ASSERT(lu.permutationQ())
VERIFY_RAISES_ASSERT(lu.computeKernel(&tmp))
VERIFY_RAISES_ASSERT(lu.computeImage(&tmp))
VERIFY_RAISES_ASSERT(lu.kernel())
VERIFY_RAISES_ASSERT(lu.image())
VERIFY_RAISES_ASSERT(lu.solve(tmp,&tmp))
VERIFY_RAISES_ASSERT(lu.determinant())
VERIFY_RAISES_ASSERT(lu.rank())
VERIFY_RAISES_ASSERT(lu.dimensionOfKernel())
VERIFY_RAISES_ASSERT(lu.isInjective())
VERIFY_RAISES_ASSERT(lu.isSurjective())
VERIFY_RAISES_ASSERT(lu.isInvertible())
VERIFY_RAISES_ASSERT(lu.computeInverse(&tmp))
VERIFY_RAISES_ASSERT(lu.inverse())
PartialLU<MatrixType> plu;
VERIFY_RAISES_ASSERT(plu.matrixLU())
VERIFY_RAISES_ASSERT(plu.permutationP())
VERIFY_RAISES_ASSERT(plu.solve(tmp,&tmp))
VERIFY_RAISES_ASSERT(plu.determinant())
VERIFY_RAISES_ASSERT(plu.computeInverse(&tmp))
VERIFY_RAISES_ASSERT(plu.inverse())
}
void test_lu()
{
for(int i = 0; i < g_repeat; i++) {
@ -104,4 +135,11 @@ void test_lu()
CALL_SUBTEST( lu_invertible<MatrixXcf>() );
CALL_SUBTEST( lu_invertible<MatrixXcd>() );
}
CALL_SUBTEST( lu_verify_assert<Matrix3f>() );
CALL_SUBTEST( lu_verify_assert<Matrix3d>() );
CALL_SUBTEST( lu_verify_assert<MatrixXf>() );
CALL_SUBTEST( lu_verify_assert<MatrixXd>() );
CALL_SUBTEST( lu_verify_assert<MatrixXcf>() );
CALL_SUBTEST( lu_verify_assert<MatrixXcd>() );
}