make HouseholderQR uses the Householder module

This commit is contained in:
Gael Guennebaud 2009-08-16 19:22:15 +02:00
parent ef13c3e754
commit 737bed19c1
7 changed files with 96 additions and 102 deletions

View File

@ -7,6 +7,7 @@
#include "Cholesky" #include "Cholesky"
#include "Jacobi" #include "Jacobi"
#include "Householder"
// Note that EIGEN_HIDE_HEAVY_CODE has to be defined per module // Note that EIGEN_HIDE_HEAVY_CODE has to be defined per module
#if (defined EIGEN_EXTERN_INSTANTIATIONS) && (EIGEN_EXTERN_INSTANTIATIONS>=2) #if (defined EIGEN_EXTERN_INSTANTIATIONS) && (EIGEN_EXTERN_INSTANTIATIONS>=2)

View File

@ -786,15 +786,18 @@ template<typename Derived> class MatrixBase
////////// Householder module /////////// ////////// Householder module ///////////
void makeHouseholderInPlace(RealScalar *tau, Scalar *beta);
template<typename EssentialPart> template<typename EssentialPart>
void makeHouseholder(EssentialPart *essential, void makeHouseholder(EssentialPart *essential,
RealScalar *beta) const; RealScalar *tau, Scalar *beta) const;
template<typename EssentialPart> template<typename EssentialPart>
void applyHouseholderOnTheLeft(const EssentialPart& essential, void applyHouseholderOnTheLeft(const EssentialPart& essential,
const RealScalar& beta); const RealScalar& tau,
Scalar* workspace);
template<typename EssentialPart> template<typename EssentialPart>
void applyHouseholderOnTheRight(const EssentialPart& essential, void applyHouseholderOnTheRight(const EssentialPart& essential,
const RealScalar& beta); const RealScalar& tau,
Scalar* workspace);
///////// Jacobi module ///////// ///////// Jacobi module /////////

View File

@ -48,26 +48,26 @@ class NoAlias
/** Behaves like MatrixBase::lazyAssign(other) /** Behaves like MatrixBase::lazyAssign(other)
* \sa MatrixBase::lazyAssign() */ * \sa MatrixBase::lazyAssign() */
template<typename OtherDerived> template<typename OtherDerived>
ExpressionType& operator=(const MatrixBase<OtherDerived>& other) EIGEN_STRONG_INLINE ExpressionType& operator=(const MatrixBase<OtherDerived>& other)
{ return m_expression.lazyAssign(other.derived()); } { return m_expression.lazyAssign(other.derived()); }
/** \sa MatrixBase::operator+= */ /** \sa MatrixBase::operator+= */
template<typename OtherDerived> template<typename OtherDerived>
ExpressionType& operator+=(const MatrixBase<OtherDerived>& other) EIGEN_STRONG_INLINE ExpressionType& operator+=(const MatrixBase<OtherDerived>& other)
{ return m_expression.lazyAssign(m_expression + other.derived()); } { return m_expression.lazyAssign(m_expression + other.derived()); }
/** \sa MatrixBase::operator-= */ /** \sa MatrixBase::operator-= */
template<typename OtherDerived> template<typename OtherDerived>
ExpressionType& operator-=(const MatrixBase<OtherDerived>& other) EIGEN_STRONG_INLINE ExpressionType& operator-=(const MatrixBase<OtherDerived>& other)
{ return m_expression.lazyAssign(m_expression - other.derived()); } { return m_expression.lazyAssign(m_expression - other.derived()); }
#ifndef EIGEN_PARSED_BY_DOXYGEN #ifndef EIGEN_PARSED_BY_DOXYGEN
template<typename ProductDerived, typename Lhs, typename Rhs> template<typename ProductDerived, typename Lhs, typename Rhs>
ExpressionType& operator+=(const ProductBase<ProductDerived, Lhs,Rhs>& other) EIGEN_STRONG_INLINE ExpressionType& operator+=(const ProductBase<ProductDerived, Lhs,Rhs>& other)
{ other.derived().addTo(m_expression); return m_expression; } { other.derived().addTo(m_expression); return m_expression; }
template<typename ProductDerived, typename Lhs, typename Rhs> template<typename ProductDerived, typename Lhs, typename Rhs>
ExpressionType& operator-=(const ProductBase<ProductDerived, Lhs,Rhs>& other) EIGEN_STRONG_INLINE ExpressionType& operator-=(const ProductBase<ProductDerived, Lhs,Rhs>& other)
{ other.derived().subTo(m_expression); return m_expression; } { other.derived().subTo(m_expression); return m_expression; }
#endif #endif

View File

@ -187,7 +187,7 @@ class GeneralProduct<Lhs, Rhs, OuterProduct>
template<> struct ei_outer_product_selector<ColMajor> { template<> struct ei_outer_product_selector<ColMajor> {
template<typename ProductType, typename Dest> template<typename ProductType, typename Dest>
static void run(const ProductType& prod, Dest& dest, typename ProductType::Scalar alpha) { EIGEN_DONT_INLINE static void run(const ProductType& prod, Dest& dest, typename ProductType::Scalar alpha) {
// FIXME make sure lhs is sequentially stored // FIXME make sure lhs is sequentially stored
const int cols = dest.cols(); const int cols = dest.cols();
for (int j=0; j<cols; ++j) for (int j=0; j<cols; ++j)
@ -197,7 +197,7 @@ template<> struct ei_outer_product_selector<ColMajor> {
template<> struct ei_outer_product_selector<RowMajor> { template<> struct ei_outer_product_selector<RowMajor> {
template<typename ProductType, typename Dest> template<typename ProductType, typename Dest>
static void run(const ProductType& prod, Dest& dest, typename ProductType::Scalar alpha) { EIGEN_DONT_INLINE static void run(const ProductType& prod, Dest& dest, typename ProductType::Scalar alpha) {
// FIXME make sure rhs is sequentially stored // FIXME make sure rhs is sequentially stored
const int rows = dest.rows(); const int rows = dest.rows();
for (int i=0; i<rows; ++i) for (int i=0; i<rows; ++i)

View File

@ -41,11 +41,34 @@ void makeTrivialHouseholder(
essential->setZero(); essential->setZero();
} }
template<typename Derived>
void MatrixBase<Derived>::makeHouseholderInPlace(RealScalar *tau, Scalar *beta)
{
VectorBlock<Derived, ei_decrement_size<SizeAtCompileTime>::ret> essentialPart(derived(), 1, size()-1);
makeHouseholder(&essentialPart, tau, beta);
}
/** Computes the elementary reflector H such that:
* \f$ H *this = [ beta 0 ... 0]^T \f$
* where the transformation H is:
* \f$ H = I - tau v v^*\f$
* and the vector v is:
* \f$ v^T = [1 essential^T] \f$
*
* On output:
* \param essential the essential part of the vector \c v
* \param tau the scaling factor of the householder transformation
* \param beta the result of H * \c *this
*
* \sa MatrixBase::makeHouseholderInPlace(), MatrixBase::applyHouseholderOnTheLeft(),
* MatrixBase::applyHouseholderOnTheRight()
*/
template<typename Derived> template<typename Derived>
template<typename EssentialPart> template<typename EssentialPart>
void MatrixBase<Derived>::makeHouseholder( void MatrixBase<Derived>::makeHouseholder(
EssentialPart *essential, EssentialPart *essential,
RealScalar *beta) const RealScalar *tau,
Scalar *beta) const
{ {
EIGEN_STATIC_ASSERT_VECTOR_ONLY(EssentialPart) EIGEN_STATIC_ASSERT_VECTOR_ONLY(EssentialPart)
RealScalar _squaredNorm = squaredNorm(); RealScalar _squaredNorm = squaredNorm();
@ -62,35 +85,38 @@ void MatrixBase<Derived>::makeHouseholder(
VectorBlock<Derived, EssentialPart::SizeAtCompileTime> tail(derived(), 1, size()-1); VectorBlock<Derived, EssentialPart::SizeAtCompileTime> tail(derived(), 1, size()-1);
*essential = tail / c0; *essential = tail / c0;
const RealScalar c0abs2 = ei_abs2(c0); const RealScalar c0abs2 = ei_abs2(c0);
*beta = RealScalar(2) * c0abs2 / (c0abs2 + _squaredNorm - ei_abs2(coeff(0))); *tau = RealScalar(2) * c0abs2 / (c0abs2 + _squaredNorm - ei_abs2(coeff(0)));
*beta = coeff(0) - c0;
} }
template<typename Derived> template<typename Derived>
template<typename EssentialPart> template<typename EssentialPart>
void MatrixBase<Derived>::applyHouseholderOnTheLeft( void MatrixBase<Derived>::applyHouseholderOnTheLeft(
const EssentialPart& essential, const EssentialPart& essential,
const RealScalar& beta) const RealScalar& tau,
Scalar* workspace)
{ {
Matrix<Scalar, 1, ColsAtCompileTime, PlainMatrixType::Options, 1, MaxColsAtCompileTime> tmp(cols()); Map<Matrix<Scalar, 1, ColsAtCompileTime, PlainMatrixType::Options, 1, MaxColsAtCompileTime> > tmp(workspace,cols());
Block<Derived, EssentialPart::SizeAtCompileTime, Derived::ColsAtCompileTime> bottom(derived(), 1, 0, rows()-1, cols()); Block<Derived, EssentialPart::SizeAtCompileTime, Derived::ColsAtCompileTime> bottom(derived(), 1, 0, rows()-1, cols());
tmp = (essential.adjoint() * bottom).lazy(); tmp.noalias() = essential.adjoint() * bottom;
tmp += row(0); tmp += row(0);
row(0) -= beta * tmp; row(0) -= tau * tmp;
bottom -= beta * essential * tmp; bottom.noalias() -= tau * essential * tmp;
} }
template<typename Derived> template<typename Derived>
template<typename EssentialPart> template<typename EssentialPart>
void MatrixBase<Derived>::applyHouseholderOnTheRight( void MatrixBase<Derived>::applyHouseholderOnTheRight(
const EssentialPart& essential, const EssentialPart& essential,
const RealScalar& beta) const RealScalar& tau,
Scalar* workspace)
{ {
Matrix<Scalar, RowsAtCompileTime, 1, PlainMatrixType::Options, MaxRowsAtCompileTime, 1> tmp(rows()); Map<Matrix<Scalar, RowsAtCompileTime, 1, PlainMatrixType::Options, MaxRowsAtCompileTime, 1> > tmp(workspace,rows());
Block<Derived, Derived::RowsAtCompileTime, EssentialPart::SizeAtCompileTime> right(derived(), 0, 1, rows(), cols()-1); Block<Derived, Derived::RowsAtCompileTime, EssentialPart::SizeAtCompileTime> right(derived(), 0, 1, rows(), cols()-1);
tmp = (right * essential.conjugate()).lazy(); tmp.noalias() = right * essential.conjugate();
tmp += col(0); tmp += col(0);
col(0) -= beta * tmp; col(0) -= tau * tmp;
right -= beta * tmp * essential.transpose(); right.noalias() -= tau * tmp * essential.transpose();
} }
#endif // EIGEN_HOUSEHOLDER_H #endif // EIGEN_HOUSEHOLDER_H

View File

@ -45,11 +45,15 @@ template<typename MatrixType> class HouseholderQR
{ {
public: public:
enum {
MinSizeAtCompileTime = EIGEN_ENUM_MIN(MatrixType::ColsAtCompileTime,MatrixType::RowsAtCompileTime)
};
typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::RealScalar RealScalar; typedef typename MatrixType::RealScalar RealScalar;
typedef Block<MatrixType, MatrixType::ColsAtCompileTime, MatrixType::ColsAtCompileTime> MatrixRBlockType; typedef Block<MatrixType, MatrixType::ColsAtCompileTime, MatrixType::ColsAtCompileTime> MatrixRBlockType;
typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, MatrixType::ColsAtCompileTime> MatrixTypeR; typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, MatrixType::ColsAtCompileTime> MatrixTypeR;
typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, 1> VectorType; typedef Matrix<RealScalar, MinSizeAtCompileTime, 1> HCoeffsType;
/** /**
* \brief Default Constructor. * \brief Default Constructor.
@ -61,7 +65,7 @@ template<typename MatrixType> class HouseholderQR
HouseholderQR(const MatrixType& matrix) HouseholderQR(const MatrixType& matrix)
: m_qr(matrix.rows(), matrix.cols()), : m_qr(matrix.rows(), matrix.cols()),
m_hCoeffs(matrix.cols()), m_hCoeffs(std::min(matrix.rows(),matrix.cols())),
m_isInitialized(false) m_isInitialized(false)
{ {
compute(matrix); compute(matrix);
@ -105,7 +109,7 @@ template<typename MatrixType> class HouseholderQR
protected: protected:
MatrixType m_qr; MatrixType m_qr;
VectorType m_hCoeffs; HCoeffsType m_hCoeffs;
bool m_isInitialized; bool m_isInitialized;
}; };
@ -114,65 +118,25 @@ template<typename MatrixType> class HouseholderQR
template<typename MatrixType> template<typename MatrixType>
HouseholderQR<MatrixType>& HouseholderQR<MatrixType>::compute(const MatrixType& matrix) HouseholderQR<MatrixType>& HouseholderQR<MatrixType>::compute(const MatrixType& matrix)
{ {
m_qr = matrix;
m_hCoeffs.resize(matrix.cols());
int rows = matrix.rows(); int rows = matrix.rows();
int cols = matrix.cols(); int cols = matrix.cols();
RealScalar eps2 = precision<RealScalar>()*precision<RealScalar>(); int size = std::min(rows,cols);
m_qr = matrix;
m_hCoeffs.resize(size);
Matrix<Scalar,1,MatrixType::ColsAtCompileTime> temp(cols); Matrix<Scalar,1,MatrixType::ColsAtCompileTime> temp(cols);
for (int k = 0; k < cols; ++k) for (int k = 0; k < size; ++k)
{ {
int remainingSize = rows-k; int remainingRows = rows - k;
RealScalar beta;
Scalar v0 = m_qr.col(k).coeff(k);
if (remainingSize==1)
{
if (NumTraits<Scalar>::IsComplex)
{
// Householder transformation on the remaining single scalar
beta = ei_abs(v0);
if (ei_real(v0)>0)
beta = -beta;
m_qr.coeffRef(k,k) = beta;
m_hCoeffs.coeffRef(k) = (beta - v0) / beta;
}
else
{
m_hCoeffs.coeffRef(k) = 0;
}
}
else if ((beta=m_qr.col(k).end(remainingSize-1).squaredNorm())>eps2)
// FIXME what about ei_imag(v0) ??
{
// form k-th Householder vector
beta = ei_sqrt(ei_abs2(v0)+beta);
if (ei_real(v0)>=0.)
beta = -beta;
m_qr.col(k).end(remainingSize-1) /= v0-beta;
m_qr.coeffRef(k,k) = beta;
Scalar h = m_hCoeffs.coeffRef(k) = (beta - v0) / beta;
// apply the Householder transformation (I - h v v') to remaining columns, i.e.,
// R <- (I - h v v') * R where v = [1,m_qr(k+1,k), m_qr(k+2,k), ...]
int remainingCols = cols - k -1; int remainingCols = cols - k -1;
if (remainingCols>0)
{ m_qr.col(k).end(remainingRows).makeHouseholderInPlace(&m_hCoeffs.coeffRef(k), &m_qr.coeffRef(k,k));
m_qr.coeffRef(k,k) = Scalar(1);
temp.end(remainingCols) = (m_qr.col(k).end(remainingSize).adjoint() if (remainingRows>1 && remainingCols>0)
* m_qr.corner(BottomRight, remainingSize, remainingCols)).lazy(); m_qr.corner(BottomRight, remainingRows, remainingCols)
m_qr.corner(BottomRight, remainingSize, remainingCols) -= (ei_conj(h) * m_qr.col(k).end(remainingSize) .applyHouseholderOnTheLeft(m_qr.col(k).end(remainingRows-1), m_hCoeffs.coeffRef(k), &temp.coeffRef(k+1));
* temp.end(remainingCols)).lazy();
m_qr.coeffRef(k,k) = beta;
}
}
else
{
m_hCoeffs.coeffRef(k) = 0;
}
} }
m_isInitialized = true; m_isInitialized = true;
return *this; return *this;
@ -187,12 +151,20 @@ void HouseholderQR<MatrixType>::solve(
{ {
ei_assert(m_isInitialized && "HouseholderQR is not initialized."); ei_assert(m_isInitialized && "HouseholderQR is not initialized.");
const int rows = m_qr.rows(); const int rows = m_qr.rows();
const int cols = b.cols();
ei_assert(b.rows() == rows); ei_assert(b.rows() == rows);
result->resize(rows, b.cols()); result->resize(rows, cols);
// TODO(keir): There is almost certainly a faster way to multiply by *result = b;
// Q^T without explicitly forming matrixQ(). Investigate.
*result = matrixQ().transpose()*b; Matrix<Scalar,1,MatrixType::ColsAtCompileTime> temp(cols);
for (int k = 0; k < cols; ++k)
{
int remainingSize = rows-k;
result->corner(BottomRight, remainingSize, cols)
.applyHouseholderOnTheLeft(m_qr.col(k).end(remainingSize-1), ei_real(m_hCoeffs.coeff(k)), &temp.coeffRef(0));
}
const int rank = std::min(result->rows(), result->cols()); const int rank = std::min(result->rows(), result->cols());
m_qr.corner(TopLeft, rank, rank) m_qr.corner(TopLeft, rank, rank)
@ -214,15 +186,10 @@ MatrixType HouseholderQR<MatrixType>::matrixQ() const
Matrix<Scalar,1,MatrixType::ColsAtCompileTime> temp(cols); Matrix<Scalar,1,MatrixType::ColsAtCompileTime> temp(cols);
for (int k = cols-1; k >= 0; k--) for (int k = cols-1; k >= 0; k--)
{ {
// to make easier the computation of the transformation, let's temporarily int remainingSize = rows-k;
// overwrite m_qr(k,k) such that the end of m_qr.col(k) is exactly our Householder vector.
Scalar beta = m_qr.coeff(k,k);
m_qr.const_cast_derived().coeffRef(k,k) = 1;
int endLength = rows-k;
temp.end(cols-k) = (m_qr.col(k).end(endLength).adjoint() * res.corner(BottomRight,endLength, cols-k)).lazy(); res.corner(BottomRight, remainingSize, cols-k)
res.corner(BottomRight,endLength, cols-k) -= ((m_hCoeffs.coeff(k) * m_qr.col(k).end(endLength)) * temp.end(cols-k)).lazy(); .applyHouseholderOnTheLeft(m_qr.col(k).end(remainingSize-1), ei_real(m_hCoeffs.coeff(k)), &temp.coeffRef(k));
m_qr.const_cast_derived().coeffRef(k,k) = beta;
} }
return res; return res;
} }

View File

@ -37,8 +37,8 @@ template<typename MatrixType> void qr(const MatrixType& m)
MatrixType a = MatrixType::Random(rows,cols); MatrixType a = MatrixType::Random(rows,cols);
HouseholderQR<MatrixType> qrOfA(a); HouseholderQR<MatrixType> qrOfA(a);
VERIFY_IS_APPROX(a, qrOfA.matrixQ() * qrOfA.matrixR()); VERIFY_IS_APPROX(a, qrOfA.matrixQ() * qrOfA.matrixR().toDense());
VERIFY_IS_NOT_APPROX(a+MatrixType::Identity(rows, cols), qrOfA.matrixQ() * qrOfA.matrixR()); VERIFY_IS_NOT_APPROX(a+MatrixType::Identity(rows, cols), qrOfA.matrixQ() * qrOfA.matrixR().toDense());
SquareMatrixType b = a.adjoint() * a; SquareMatrixType b = a.adjoint() * a;
@ -59,7 +59,7 @@ template<typename MatrixType> void qr_invertible()
{ {
/* this test covers the following files: QR.h */ /* this test covers the following files: QR.h */
typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar; typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar;
int size = ei_random<int>(10,200); int size = ei_random<int>(10,50);
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); m1 = MatrixType::Random(size,size);
@ -74,7 +74,6 @@ template<typename MatrixType> void qr_invertible()
HouseholderQR<MatrixType> qr(m1); HouseholderQR<MatrixType> qr(m1);
m3 = MatrixType::Random(size,size); m3 = MatrixType::Random(size,size);
qr.solve(m3, &m2); qr.solve(m3, &m2);
//std::cerr << m3 - m1*m2 << "\n\n";
VERIFY_IS_APPROX(m3, m1*m2); VERIFY_IS_APPROX(m3, m1*m2);
} }
@ -91,20 +90,18 @@ template<typename MatrixType> void qr_verify_assert()
void test_qr() void test_qr()
{ {
for(int i = 0; i < 1; i++) { for(int i = 0; i < 1; i++) {
// FIXME : very weird bug here
// CALL_SUBTEST( qr(Matrix2f()) ); // CALL_SUBTEST( qr(Matrix2f()) );
// CALL_SUBTEST( qr(Matrix4d()) ); CALL_SUBTEST( qr(Matrix4d()) );
// CALL_SUBTEST( qr(MatrixXf(12,8)) ); CALL_SUBTEST( qr(MatrixXcd(17,7)) );
// CALL_SUBTEST( qr(MatrixXcd(5,5)) ); CALL_SUBTEST( qr(MatrixXf(47,40)) );
// CALL_SUBTEST( qr(MatrixXcd(7,3)) );
CALL_SUBTEST( qr(MatrixXf(47,47)) );
} }
for(int i = 0; i < g_repeat; i++) { for(int i = 0; i < g_repeat; i++) {
CALL_SUBTEST( qr_invertible<MatrixXf>() ); CALL_SUBTEST( qr_invertible<MatrixXf>() );
CALL_SUBTEST( qr_invertible<MatrixXd>() ); CALL_SUBTEST( qr_invertible<MatrixXd>() );
// TODO fix issue with complex CALL_SUBTEST( qr_invertible<MatrixXcf>() );
// CALL_SUBTEST( qr_invertible<MatrixXcf>() ); CALL_SUBTEST( qr_invertible<MatrixXcd>() );
// CALL_SUBTEST( qr_invertible<MatrixXcd>() );
} }
CALL_SUBTEST(qr_verify_assert<Matrix3f>()); CALL_SUBTEST(qr_verify_assert<Matrix3f>());