PR 567: makes all dense solvers inherit SoverBase (LU,Cholesky,QR,SVD).

This changeset also includes:
 * add HouseholderSequence::conjugateIf
 * define int as the StorageIndex type for all dense solvers
 * dedicated unit tests, including assertion checking
 * _check_solve_assertion(): this method can be implemented in derived solver classes to implement custom checks
 * CompleteOrthogonalDecompositions: add applyZOnTheLeftInPlace, fix scalar type in applyZAdjointOnTheLeftInPlace(), add missing assertions
 * Cholesky: add missing assertions
 * FullPivHouseholderQR: Corrected Scalar type in _solve_impl()
 * BDCSVD: Unambiguous return type for ternary operator
 * SVDBase: Corrected Scalar type in _solve_impl()
This commit is contained in:
Patrick Peltzer 2019-01-17 01:17:39 +01:00
parent 7f32109c11
commit 15e53d5d93
23 changed files with 576 additions and 239 deletions

View File

@ -16,6 +16,15 @@
namespace Eigen { namespace Eigen {
namespace internal { namespace internal {
template<typename _MatrixType, int _UpLo> struct traits<LDLT<_MatrixType, _UpLo> >
: traits<_MatrixType>
{
typedef MatrixXpr XprKind;
typedef SolverStorage StorageKind;
typedef int StorageIndex;
enum { Flags = 0 };
};
template<typename MatrixType, int UpLo> struct LDLT_Traits; template<typename MatrixType, int UpLo> struct LDLT_Traits;
// PositiveSemiDef means positive semi-definite and non-zero; same for NegativeSemiDef // PositiveSemiDef means positive semi-definite and non-zero; same for NegativeSemiDef
@ -48,20 +57,19 @@ namespace internal {
* \sa MatrixBase::ldlt(), SelfAdjointView::ldlt(), class LLT * \sa MatrixBase::ldlt(), SelfAdjointView::ldlt(), class LLT
*/ */
template<typename _MatrixType, int _UpLo> class LDLT template<typename _MatrixType, int _UpLo> class LDLT
: public SolverBase<LDLT<_MatrixType, _UpLo> >
{ {
public: public:
typedef _MatrixType MatrixType; typedef _MatrixType MatrixType;
typedef SolverBase<LDLT> Base;
friend class SolverBase<LDLT>;
EIGEN_GENERIC_PUBLIC_INTERFACE(LDLT)
enum { enum {
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
ColsAtCompileTime = MatrixType::ColsAtCompileTime,
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime, MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime, MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime,
UpLo = _UpLo UpLo = _UpLo
}; };
typedef typename MatrixType::Scalar Scalar;
typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar;
typedef Eigen::Index Index; ///< \deprecated since Eigen 3.3
typedef typename MatrixType::StorageIndex StorageIndex;
typedef Matrix<Scalar, RowsAtCompileTime, 1, 0, MaxRowsAtCompileTime, 1> TmpMatrixType; typedef Matrix<Scalar, RowsAtCompileTime, 1, 0, MaxRowsAtCompileTime, 1> TmpMatrixType;
typedef Transpositions<RowsAtCompileTime, MaxRowsAtCompileTime> TranspositionType; typedef Transpositions<RowsAtCompileTime, MaxRowsAtCompileTime> TranspositionType;
@ -180,6 +188,7 @@ template<typename _MatrixType, int _UpLo> class LDLT
return m_sign == internal::NegativeSemiDef || m_sign == internal::ZeroSign; return m_sign == internal::NegativeSemiDef || m_sign == internal::ZeroSign;
} }
#ifdef EIGEN_PARSED_BY_DOXYGEN
/** \returns a solution x of \f$ A x = b \f$ using the current decomposition of A. /** \returns a solution x of \f$ A x = b \f$ using the current decomposition of A.
* *
* This function also supports in-place solves using the syntax <tt>x = decompositionObject.solve(x)</tt> . * This function also supports in-place solves using the syntax <tt>x = decompositionObject.solve(x)</tt> .
@ -197,13 +206,8 @@ template<typename _MatrixType, int _UpLo> class LDLT
*/ */
template<typename Rhs> template<typename Rhs>
inline const Solve<LDLT, Rhs> inline const Solve<LDLT, Rhs>
solve(const MatrixBase<Rhs>& b) const solve(const MatrixBase<Rhs>& b) const;
{ #endif
eigen_assert(m_isInitialized && "LDLT is not initialized.");
eigen_assert(m_matrix.rows()==b.rows()
&& "LDLT::solve(): invalid number of rows of the right hand side matrix b");
return Solve<LDLT, Rhs>(*this, b.derived());
}
template<typename Derived> template<typename Derived>
bool solveInPlace(MatrixBase<Derived> &bAndX) const; bool solveInPlace(MatrixBase<Derived> &bAndX) const;
@ -259,6 +263,9 @@ template<typename _MatrixType, int _UpLo> class LDLT
#ifndef EIGEN_PARSED_BY_DOXYGEN #ifndef EIGEN_PARSED_BY_DOXYGEN
template<typename RhsType, typename DstType> template<typename RhsType, typename DstType>
void _solve_impl(const RhsType &rhs, DstType &dst) const; void _solve_impl(const RhsType &rhs, DstType &dst) const;
template<bool Conjugate, typename RhsType, typename DstType>
void _solve_impl_transposed(const RhsType &rhs, DstType &dst) const;
#endif #endif
protected: protected:
@ -559,14 +566,22 @@ template<typename _MatrixType, int _UpLo>
template<typename RhsType, typename DstType> template<typename RhsType, typename DstType>
void LDLT<_MatrixType,_UpLo>::_solve_impl(const RhsType &rhs, DstType &dst) const void LDLT<_MatrixType,_UpLo>::_solve_impl(const RhsType &rhs, DstType &dst) const
{ {
eigen_assert(rhs.rows() == rows()); _solve_impl_transposed<true>(rhs, dst);
}
template<typename _MatrixType,int _UpLo>
template<bool Conjugate, typename RhsType, typename DstType>
void LDLT<_MatrixType,_UpLo>::_solve_impl_transposed(const RhsType &rhs, DstType &dst) const
{
// dst = P b // dst = P b
dst = m_transpositions * rhs; dst = m_transpositions * rhs;
// dst = L^-1 (P b) // dst = L^-1 (P b)
matrixL().solveInPlace(dst); // dst = L^-*T (P b)
matrixL().template conjugateIf<!Conjugate>().solveInPlace(dst);
// dst = D^-1 (L^-1 P b) // dst = D^-* (L^-1 P b)
// dst = D^-1 (L^-*T P b)
// more precisely, use pseudo-inverse of D (see bug 241) // more precisely, use pseudo-inverse of D (see bug 241)
using std::abs; using std::abs;
const typename Diagonal<const MatrixType>::RealReturnType vecD(vectorD()); const typename Diagonal<const MatrixType>::RealReturnType vecD(vectorD());
@ -578,7 +593,6 @@ void LDLT<_MatrixType,_UpLo>::_solve_impl(const RhsType &rhs, DstType &dst) cons
// Moreover, Lapack's xSYTRS routines use 0 for the tolerance. // Moreover, Lapack's xSYTRS routines use 0 for the tolerance.
// Using numeric_limits::min() gives us more robustness to denormals. // Using numeric_limits::min() gives us more robustness to denormals.
RealScalar tolerance = (std::numeric_limits<RealScalar>::min)(); RealScalar tolerance = (std::numeric_limits<RealScalar>::min)();
for (Index i = 0; i < vecD.size(); ++i) for (Index i = 0; i < vecD.size(); ++i)
{ {
if(abs(vecD(i)) > tolerance) if(abs(vecD(i)) > tolerance)
@ -587,10 +601,12 @@ void LDLT<_MatrixType,_UpLo>::_solve_impl(const RhsType &rhs, DstType &dst) cons
dst.row(i).setZero(); dst.row(i).setZero();
} }
// dst = L^-T (D^-1 L^-1 P b) // dst = L^-* (D^-* L^-1 P b)
matrixU().solveInPlace(dst); // dst = L^-T (D^-1 L^-*T P b)
matrixL().transpose().template conjugateIf<Conjugate>().solveInPlace(dst);
// dst = P^-1 (L^-T D^-1 L^-1 P b) = A^-1 b // dst = P^T (L^-* D^-* L^-1 P b) = A^-1 b
// dst = P^-T (L^-T D^-1 L^-*T P b) = A^-1 b
dst = m_transpositions.transpose() * dst; dst = m_transpositions.transpose() * dst;
} }
#endif #endif

View File

@ -13,6 +13,16 @@
namespace Eigen { namespace Eigen {
namespace internal{ namespace internal{
template<typename _MatrixType, int _UpLo> struct traits<LLT<_MatrixType, _UpLo> >
: traits<_MatrixType>
{
typedef MatrixXpr XprKind;
typedef SolverStorage StorageKind;
typedef int StorageIndex;
enum { Flags = 0 };
};
template<typename MatrixType, int UpLo> struct LLT_Traits; template<typename MatrixType, int UpLo> struct LLT_Traits;
} }
@ -54,18 +64,17 @@ template<typename MatrixType, int UpLo> struct LLT_Traits;
* \sa MatrixBase::llt(), SelfAdjointView::llt(), class LDLT * \sa MatrixBase::llt(), SelfAdjointView::llt(), class LDLT
*/ */
template<typename _MatrixType, int _UpLo> class LLT template<typename _MatrixType, int _UpLo> class LLT
: public SolverBase<LLT<_MatrixType, _UpLo> >
{ {
public: public:
typedef _MatrixType MatrixType; typedef _MatrixType MatrixType;
typedef SolverBase<LLT> Base;
friend class SolverBase<LLT>;
EIGEN_GENERIC_PUBLIC_INTERFACE(LLT)
enum { enum {
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
ColsAtCompileTime = MatrixType::ColsAtCompileTime,
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
}; };
typedef typename MatrixType::Scalar Scalar;
typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar;
typedef Eigen::Index Index; ///< \deprecated since Eigen 3.3
typedef typename MatrixType::StorageIndex StorageIndex;
enum { enum {
PacketSize = internal::packet_traits<Scalar>::size, PacketSize = internal::packet_traits<Scalar>::size,
@ -129,6 +138,7 @@ template<typename _MatrixType, int _UpLo> class LLT
return Traits::getL(m_matrix); return Traits::getL(m_matrix);
} }
#ifdef EIGEN_PARSED_BY_DOXYGEN
/** \returns the solution x of \f$ A x = b \f$ using the current decomposition of A. /** \returns the solution x of \f$ A x = b \f$ using the current decomposition of A.
* *
* Since this LLT class assumes anyway that the matrix A is invertible, the solution * Since this LLT class assumes anyway that the matrix A is invertible, the solution
@ -141,13 +151,8 @@ template<typename _MatrixType, int _UpLo> class LLT
*/ */
template<typename Rhs> template<typename Rhs>
inline const Solve<LLT, Rhs> inline const Solve<LLT, Rhs>
solve(const MatrixBase<Rhs>& b) const solve(const MatrixBase<Rhs>& b) const;
{ #endif
eigen_assert(m_isInitialized && "LLT is not initialized.");
eigen_assert(m_matrix.rows()==b.rows()
&& "LLT::solve(): invalid number of rows of the right hand side matrix b");
return Solve<LLT, Rhs>(*this, b.derived());
}
template<typename Derived> template<typename Derived>
void solveInPlace(const MatrixBase<Derived> &bAndX) const; void solveInPlace(const MatrixBase<Derived> &bAndX) const;
@ -205,6 +210,9 @@ template<typename _MatrixType, int _UpLo> class LLT
#ifndef EIGEN_PARSED_BY_DOXYGEN #ifndef EIGEN_PARSED_BY_DOXYGEN
template<typename RhsType, typename DstType> template<typename RhsType, typename DstType>
void _solve_impl(const RhsType &rhs, DstType &dst) const; void _solve_impl(const RhsType &rhs, DstType &dst) const;
template<bool Conjugate, typename RhsType, typename DstType>
void _solve_impl_transposed(const RhsType &rhs, DstType &dst) const;
#endif #endif
protected: protected:
@ -475,9 +483,18 @@ LLT<_MatrixType,_UpLo> & LLT<_MatrixType,_UpLo>::rankUpdate(const VectorType& v,
template<typename _MatrixType,int _UpLo> template<typename _MatrixType,int _UpLo>
template<typename RhsType, typename DstType> template<typename RhsType, typename DstType>
void LLT<_MatrixType,_UpLo>::_solve_impl(const RhsType &rhs, DstType &dst) const void LLT<_MatrixType,_UpLo>::_solve_impl(const RhsType &rhs, DstType &dst) const
{
_solve_impl_transposed<true>(rhs, dst);
}
template<typename _MatrixType,int _UpLo>
template<bool Conjugate, typename RhsType, typename DstType>
void LLT<_MatrixType,_UpLo>::_solve_impl_transposed(const RhsType &rhs, DstType &dst) const
{ {
dst = rhs; dst = rhs;
solveInPlace(dst);
matrixL().template conjugateIf<!Conjugate>().solveInPlace(dst);
matrixU().template conjugateIf<!Conjugate>().solveInPlace(dst);
} }
#endif #endif

View File

@ -14,8 +14,35 @@ namespace Eigen {
namespace internal { namespace internal {
template<typename Derived>
struct solve_assertion {
template<bool Transpose_, typename Rhs>
static void run(const Derived& solver, const Rhs& b) { solver.template _check_solve_assertion<Transpose_>(b); }
};
template<typename Derived>
struct solve_assertion<Transpose<Derived> >
{
typedef Transpose<Derived> type;
template<bool Transpose_, typename Rhs>
static void run(const type& transpose, const Rhs& b)
{
internal::solve_assertion<typename internal::remove_all<Derived>::type>::template run<true>(transpose.nestedExpression(), b);
}
};
template<typename Scalar, typename Derived>
struct solve_assertion<CwiseUnaryOp<Eigen::internal::scalar_conjugate_op<Scalar>, const Transpose<Derived> > >
{
typedef CwiseUnaryOp<Eigen::internal::scalar_conjugate_op<Scalar>, const Transpose<Derived> > type;
template<bool Transpose_, typename Rhs>
static void run(const type& adjoint, const Rhs& b)
{
internal::solve_assertion<typename internal::remove_all<Transpose<Derived> >::type>::template run<true>(adjoint.nestedExpression(), b);
}
};
} // end namespace internal } // end namespace internal
/** \class SolverBase /** \class SolverBase
@ -35,7 +62,7 @@ namespace internal {
* *
* \warning Currently, any other usage of transpose() and adjoint() are not supported and will produce compilation errors. * \warning Currently, any other usage of transpose() and adjoint() are not supported and will produce compilation errors.
* *
* \sa class PartialPivLU, class FullPivLU * \sa class PartialPivLU, class FullPivLU, class HouseholderQR, class ColPivHouseholderQR, class FullPivHouseholderQR, class CompleteOrthogonalDecomposition, class LLT, class LDLT, class SVDBase
*/ */
template<typename Derived> template<typename Derived>
class SolverBase : public EigenBase<Derived> class SolverBase : public EigenBase<Derived>
@ -46,6 +73,9 @@ class SolverBase : public EigenBase<Derived>
typedef typename internal::traits<Derived>::Scalar Scalar; typedef typename internal::traits<Derived>::Scalar Scalar;
typedef Scalar CoeffReturnType; typedef Scalar CoeffReturnType;
template<typename Derived_>
friend struct internal::solve_assertion;
enum { enum {
RowsAtCompileTime = internal::traits<Derived>::RowsAtCompileTime, RowsAtCompileTime = internal::traits<Derived>::RowsAtCompileTime,
ColsAtCompileTime = internal::traits<Derived>::ColsAtCompileTime, ColsAtCompileTime = internal::traits<Derived>::ColsAtCompileTime,
@ -75,7 +105,7 @@ class SolverBase : public EigenBase<Derived>
inline const Solve<Derived, Rhs> inline const Solve<Derived, Rhs>
solve(const MatrixBase<Rhs>& b) const solve(const MatrixBase<Rhs>& b) const
{ {
eigen_assert(derived().rows()==b.rows() && "solve(): invalid number of rows of the right hand side matrix b"); internal::solve_assertion<typename internal::remove_all<Derived>::type>::template run<false>(derived(), b);
return Solve<Derived, Rhs>(derived(), b.derived()); return Solve<Derived, Rhs>(derived(), b.derived());
} }
@ -113,6 +143,12 @@ class SolverBase : public EigenBase<Derived>
} }
protected: protected:
template<bool Transpose_, typename Rhs>
void _check_solve_assertion(const Rhs& b) const {
eigen_assert(derived().m_isInitialized && "Solver is not initialized.");
eigen_assert((Transpose_?derived().cols():derived().rows())==b.rows() && "SolverBase::solve(): invalid number of rows of the right hand side matrix b");
}
}; };
namespace internal { namespace internal {

View File

@ -260,6 +260,7 @@ template<typename MatrixType> class HouseholderQR;
template<typename MatrixType> class ColPivHouseholderQR; template<typename MatrixType> class ColPivHouseholderQR;
template<typename MatrixType> class FullPivHouseholderQR; template<typename MatrixType> class FullPivHouseholderQR;
template<typename MatrixType> class CompleteOrthogonalDecomposition; template<typename MatrixType> class CompleteOrthogonalDecomposition;
template<typename MatrixType> class SVDBase;
template<typename MatrixType, int QRPreconditioner = ColPivHouseholderQRPreconditioner> class JacobiSVD; template<typename MatrixType, int QRPreconditioner = ColPivHouseholderQRPreconditioner> class JacobiSVD;
template<typename MatrixType> class BDCSVD; template<typename MatrixType> class BDCSVD;
template<typename MatrixType, int UpLo = Lower> class LLT; template<typename MatrixType, int UpLo = Lower> class LLT;

View File

@ -156,6 +156,12 @@ template<typename VectorsType, typename CoeffsType, int Side> class HouseholderS
Side Side
> TransposeReturnType; > TransposeReturnType;
typedef HouseholderSequence<
typename internal::add_const<VectorsType>::type,
typename internal::add_const<CoeffsType>::type,
Side
> ConstHouseholderSequence;
/** \brief Constructor. /** \brief Constructor.
* \param[in] v %Matrix containing the essential parts of the Householder vectors * \param[in] v %Matrix containing the essential parts of the Householder vectors
* \param[in] h Vector containing the Householder coefficients * \param[in] h Vector containing the Householder coefficients
@ -244,6 +250,18 @@ template<typename VectorsType, typename CoeffsType, int Side> class HouseholderS
.setShift(m_shift); .setShift(m_shift);
} }
/** \returns an expression of the complex conjugate of \c *this if Cond==true,
* returns \c *this otherwise.
*/
template<bool Cond>
EIGEN_DEVICE_FUNC
inline typename internal::conditional<Cond,ConjugateReturnType,ConstHouseholderSequence>::type
conjugateIf() const
{
typedef typename internal::conditional<Cond,ConjugateReturnType,ConstHouseholderSequence>::type ReturnType;
return ReturnType(m_vectors.template conjugateIf<Cond>(), m_coeffs.template conjugateIf<Cond>());
}
/** \brief Adjoint (conjugate transpose) of the Householder sequence. */ /** \brief Adjoint (conjugate transpose) of the Householder sequence. */
AdjointReturnType adjoint() const AdjointReturnType adjoint() const
{ {

View File

@ -63,6 +63,7 @@ template<typename _MatrixType> class FullPivLU
public: public:
typedef _MatrixType MatrixType; typedef _MatrixType MatrixType;
typedef SolverBase<FullPivLU> Base; typedef SolverBase<FullPivLU> Base;
friend class SolverBase<FullPivLU>;
EIGEN_GENERIC_PUBLIC_INTERFACE(FullPivLU) EIGEN_GENERIC_PUBLIC_INTERFACE(FullPivLU)
enum { enum {
@ -218,6 +219,7 @@ template<typename _MatrixType> class FullPivLU
return internal::image_retval<FullPivLU>(*this, originalMatrix); return internal::image_retval<FullPivLU>(*this, originalMatrix);
} }
#ifdef EIGEN_PARSED_BY_DOXYGEN
/** \return a solution x to the equation Ax=b, where A is the matrix of which /** \return a solution x to the equation Ax=b, where A is the matrix of which
* *this is the LU decomposition. * *this is the LU decomposition.
* *
@ -237,14 +239,10 @@ template<typename _MatrixType> class FullPivLU
* *
* \sa TriangularView::solve(), kernel(), inverse() * \sa TriangularView::solve(), kernel(), inverse()
*/ */
// FIXME this is a copy-paste of the base-class member to add the isInitialized assertion.
template<typename Rhs> template<typename Rhs>
inline const Solve<FullPivLU, Rhs> inline const Solve<FullPivLU, Rhs>
solve(const MatrixBase<Rhs>& b) const solve(const MatrixBase<Rhs>& b) const;
{ #endif
eigen_assert(m_isInitialized && "LU is not initialized.");
return Solve<FullPivLU, Rhs>(*this, b.derived());
}
/** \returns an estimate of the reciprocal condition number of the matrix of which \c *this is /** \returns an estimate of the reciprocal condition number of the matrix of which \c *this is
the LU decomposition. the LU decomposition.
@ -755,7 +753,6 @@ void FullPivLU<_MatrixType>::_solve_impl(const RhsType &rhs, DstType &dst) const
const Index rows = this->rows(), const Index rows = this->rows(),
cols = this->cols(), cols = this->cols(),
nonzero_pivots = this->rank(); nonzero_pivots = this->rank();
eigen_assert(rhs.rows() == rows);
const Index smalldim = (std::min)(rows, cols); const Index smalldim = (std::min)(rows, cols);
if(nonzero_pivots == 0) if(nonzero_pivots == 0)
@ -805,7 +802,6 @@ void FullPivLU<_MatrixType>::_solve_impl_transposed(const RhsType &rhs, DstType
const Index rows = this->rows(), cols = this->cols(), const Index rows = this->rows(), cols = this->cols(),
nonzero_pivots = this->rank(); nonzero_pivots = this->rank();
eigen_assert(rhs.rows() == cols);
const Index smalldim = (std::min)(rows, cols); const Index smalldim = (std::min)(rows, cols);
if(nonzero_pivots == 0) if(nonzero_pivots == 0)

View File

@ -80,6 +80,8 @@ template<typename _MatrixType> class PartialPivLU
typedef _MatrixType MatrixType; typedef _MatrixType MatrixType;
typedef SolverBase<PartialPivLU> Base; typedef SolverBase<PartialPivLU> Base;
friend class SolverBase<PartialPivLU>;
EIGEN_GENERIC_PUBLIC_INTERFACE(PartialPivLU) EIGEN_GENERIC_PUBLIC_INTERFACE(PartialPivLU)
enum { enum {
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime, MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
@ -152,6 +154,7 @@ template<typename _MatrixType> class PartialPivLU
return m_p; return m_p;
} }
#ifdef EIGEN_PARSED_BY_DOXYGEN
/** This method returns the solution x to the equation Ax=b, where A is the matrix of which /** This method returns the solution x to the equation Ax=b, where A is the matrix of which
* *this is the LU decomposition. * *this is the LU decomposition.
* *
@ -169,14 +172,10 @@ template<typename _MatrixType> class PartialPivLU
* *
* \sa TriangularView::solve(), inverse(), computeInverse() * \sa TriangularView::solve(), inverse(), computeInverse()
*/ */
// FIXME this is a copy-paste of the base-class member to add the isInitialized assertion.
template<typename Rhs> template<typename Rhs>
inline const Solve<PartialPivLU, Rhs> inline const Solve<PartialPivLU, Rhs>
solve(const MatrixBase<Rhs>& b) const solve(const MatrixBase<Rhs>& b) const;
{ #endif
eigen_assert(m_isInitialized && "PartialPivLU is not initialized.");
return Solve<PartialPivLU, Rhs>(*this, b.derived());
}
/** \returns an estimate of the reciprocal condition number of the matrix of which \c *this is /** \returns an estimate of the reciprocal condition number of the matrix of which \c *this is
the LU decomposition. the LU decomposition.
@ -231,8 +230,6 @@ template<typename _MatrixType> class PartialPivLU
* Step 3: replace c by the solution x to Ux = c. * Step 3: replace c by the solution x to Ux = c.
*/ */
eigen_assert(rhs.rows() == m_lu.rows());
// Step 1 // Step 1
dst = permutationP() * rhs; dst = permutationP() * rhs;

View File

@ -17,6 +17,9 @@ namespace internal {
template<typename _MatrixType> struct traits<ColPivHouseholderQR<_MatrixType> > template<typename _MatrixType> struct traits<ColPivHouseholderQR<_MatrixType> >
: traits<_MatrixType> : traits<_MatrixType>
{ {
typedef MatrixXpr XprKind;
typedef SolverStorage StorageKind;
typedef int StorageIndex;
enum { Flags = 0 }; enum { Flags = 0 };
}; };
@ -46,20 +49,19 @@ template<typename _MatrixType> struct traits<ColPivHouseholderQR<_MatrixType> >
* \sa MatrixBase::colPivHouseholderQr() * \sa MatrixBase::colPivHouseholderQr()
*/ */
template<typename _MatrixType> class ColPivHouseholderQR template<typename _MatrixType> class ColPivHouseholderQR
: public SolverBase<ColPivHouseholderQR<_MatrixType> >
{ {
public: public:
typedef _MatrixType MatrixType; typedef _MatrixType MatrixType;
typedef SolverBase<ColPivHouseholderQR> Base;
friend class SolverBase<ColPivHouseholderQR>;
EIGEN_GENERIC_PUBLIC_INTERFACE(ColPivHouseholderQR)
enum { enum {
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
ColsAtCompileTime = MatrixType::ColsAtCompileTime,
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime, MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
}; };
typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::RealScalar RealScalar;
// FIXME should be int
typedef typename MatrixType::StorageIndex StorageIndex;
typedef typename internal::plain_diag_type<MatrixType>::type HCoeffsType; typedef typename internal::plain_diag_type<MatrixType>::type HCoeffsType;
typedef PermutationMatrix<ColsAtCompileTime, MaxColsAtCompileTime> PermutationType; typedef PermutationMatrix<ColsAtCompileTime, MaxColsAtCompileTime> PermutationType;
typedef typename internal::plain_row_type<MatrixType, Index>::type IntRowVectorType; typedef typename internal::plain_row_type<MatrixType, Index>::type IntRowVectorType;
@ -156,6 +158,7 @@ template<typename _MatrixType> class ColPivHouseholderQR
computeInPlace(); computeInPlace();
} }
#ifdef EIGEN_PARSED_BY_DOXYGEN
/** This method finds a solution x to the equation Ax=b, where A is the matrix of which /** This method finds a solution x to the equation Ax=b, where A is the matrix of which
* *this is the QR decomposition, if any exists. * *this is the QR decomposition, if any exists.
* *
@ -172,11 +175,8 @@ template<typename _MatrixType> class ColPivHouseholderQR
*/ */
template<typename Rhs> template<typename Rhs>
inline const Solve<ColPivHouseholderQR, Rhs> inline const Solve<ColPivHouseholderQR, Rhs>
solve(const MatrixBase<Rhs>& b) const solve(const MatrixBase<Rhs>& b) const;
{ #endif
eigen_assert(m_isInitialized && "ColPivHouseholderQR is not initialized.");
return Solve<ColPivHouseholderQR, Rhs>(*this, b.derived());
}
HouseholderSequenceType householderQ() const; HouseholderSequenceType householderQ() const;
HouseholderSequenceType matrixQ() const HouseholderSequenceType matrixQ() const
@ -417,6 +417,9 @@ template<typename _MatrixType> class ColPivHouseholderQR
#ifndef EIGEN_PARSED_BY_DOXYGEN #ifndef EIGEN_PARSED_BY_DOXYGEN
template<typename RhsType, typename DstType> template<typename RhsType, typename DstType>
void _solve_impl(const RhsType &rhs, DstType &dst) const; void _solve_impl(const RhsType &rhs, DstType &dst) const;
template<bool Conjugate, typename RhsType, typename DstType>
void _solve_impl_transposed(const RhsType &rhs, DstType &dst) const;
#endif #endif
protected: protected:
@ -583,8 +586,6 @@ template<typename _MatrixType>
template<typename RhsType, typename DstType> template<typename RhsType, typename DstType>
void ColPivHouseholderQR<_MatrixType>::_solve_impl(const RhsType &rhs, DstType &dst) const void ColPivHouseholderQR<_MatrixType>::_solve_impl(const RhsType &rhs, DstType &dst) const
{ {
eigen_assert(rhs.rows() == rows());
const Index nonzero_pivots = nonzeroPivots(); const Index nonzero_pivots = nonzeroPivots();
if(nonzero_pivots == 0) if(nonzero_pivots == 0)
@ -604,6 +605,31 @@ void ColPivHouseholderQR<_MatrixType>::_solve_impl(const RhsType &rhs, DstType &
for(Index i = 0; i < nonzero_pivots; ++i) dst.row(m_colsPermutation.indices().coeff(i)) = c.row(i); for(Index i = 0; i < nonzero_pivots; ++i) dst.row(m_colsPermutation.indices().coeff(i)) = c.row(i);
for(Index i = nonzero_pivots; i < cols(); ++i) dst.row(m_colsPermutation.indices().coeff(i)).setZero(); for(Index i = nonzero_pivots; i < cols(); ++i) dst.row(m_colsPermutation.indices().coeff(i)).setZero();
} }
template<typename _MatrixType>
template<bool Conjugate, typename RhsType, typename DstType>
void ColPivHouseholderQR<_MatrixType>::_solve_impl_transposed(const RhsType &rhs, DstType &dst) const
{
const Index nonzero_pivots = nonzeroPivots();
if(nonzero_pivots == 0)
{
dst.setZero();
return;
}
typename RhsType::PlainObject c(m_colsPermutation.transpose()*rhs);
m_qr.topLeftCorner(nonzero_pivots, nonzero_pivots)
.template triangularView<Upper>()
.transpose().template conjugateIf<Conjugate>()
.solveInPlace(c.topRows(nonzero_pivots));
dst.topRows(nonzero_pivots) = c.topRows(nonzero_pivots);
dst.bottomRows(rows()-nonzero_pivots).setZero();
dst.applyOnTheLeft(householderQ().setLength(nonzero_pivots).template conjugateIf<!Conjugate>() );
}
#endif #endif
namespace internal { namespace internal {

View File

@ -16,6 +16,9 @@ namespace internal {
template <typename _MatrixType> template <typename _MatrixType>
struct traits<CompleteOrthogonalDecomposition<_MatrixType> > struct traits<CompleteOrthogonalDecomposition<_MatrixType> >
: traits<_MatrixType> { : traits<_MatrixType> {
typedef MatrixXpr XprKind;
typedef SolverStorage StorageKind;
typedef int StorageIndex;
enum { Flags = 0 }; enum { Flags = 0 };
}; };
@ -44,19 +47,21 @@ struct traits<CompleteOrthogonalDecomposition<_MatrixType> >
* *
* \sa MatrixBase::completeOrthogonalDecomposition() * \sa MatrixBase::completeOrthogonalDecomposition()
*/ */
template <typename _MatrixType> template <typename _MatrixType> class CompleteOrthogonalDecomposition
class CompleteOrthogonalDecomposition { : public SolverBase<CompleteOrthogonalDecomposition<_MatrixType> >
{
public: public:
typedef _MatrixType MatrixType; typedef _MatrixType MatrixType;
typedef SolverBase<CompleteOrthogonalDecomposition> Base;
template<typename Derived>
friend struct internal::solve_assertion;
EIGEN_GENERIC_PUBLIC_INTERFACE(CompleteOrthogonalDecomposition)
enum { enum {
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
ColsAtCompileTime = MatrixType::ColsAtCompileTime,
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime, MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
}; };
typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::RealScalar RealScalar;
typedef typename MatrixType::StorageIndex StorageIndex;
typedef typename internal::plain_diag_type<MatrixType>::type HCoeffsType; typedef typename internal::plain_diag_type<MatrixType>::type HCoeffsType;
typedef PermutationMatrix<ColsAtCompileTime, MaxColsAtCompileTime> typedef PermutationMatrix<ColsAtCompileTime, MaxColsAtCompileTime>
PermutationType; PermutationType;
@ -133,7 +138,7 @@ class CompleteOrthogonalDecomposition {
computeInPlace(); computeInPlace();
} }
#ifdef EIGEN_PARSED_BY_DOXYGEN
/** This method computes the minimum-norm solution X to a least squares /** This method computes the minimum-norm solution X to a least squares
* problem \f[\mathrm{minimize} \|A X - B\|, \f] where \b A is the matrix of * problem \f[\mathrm{minimize} \|A X - B\|, \f] where \b A is the matrix of
* which \c *this is the complete orthogonal decomposition. * which \c *this is the complete orthogonal decomposition.
@ -145,11 +150,8 @@ class CompleteOrthogonalDecomposition {
*/ */
template <typename Rhs> template <typename Rhs>
inline const Solve<CompleteOrthogonalDecomposition, Rhs> solve( inline const Solve<CompleteOrthogonalDecomposition, Rhs> solve(
const MatrixBase<Rhs>& b) const { const MatrixBase<Rhs>& b) const;
eigen_assert(m_cpqr.m_isInitialized && #endif
"CompleteOrthogonalDecomposition is not initialized.");
return Solve<CompleteOrthogonalDecomposition, Rhs>(*this, b.derived());
}
HouseholderSequenceType householderQ(void) const; HouseholderSequenceType householderQ(void) const;
HouseholderSequenceType matrixQ(void) const { return m_cpqr.householderQ(); } HouseholderSequenceType matrixQ(void) const { return m_cpqr.householderQ(); }
@ -158,8 +160,8 @@ class CompleteOrthogonalDecomposition {
*/ */
MatrixType matrixZ() const { MatrixType matrixZ() const {
MatrixType Z = MatrixType::Identity(m_cpqr.cols(), m_cpqr.cols()); MatrixType Z = MatrixType::Identity(m_cpqr.cols(), m_cpqr.cols());
applyZAdjointOnTheLeftInPlace(Z); applyZOnTheLeftInPlace<false>(Z);
return Z.adjoint(); return Z;
} }
/** \returns a reference to the matrix where the complete orthogonal /** \returns a reference to the matrix where the complete orthogonal
@ -275,6 +277,7 @@ class CompleteOrthogonalDecomposition {
*/ */
inline const Inverse<CompleteOrthogonalDecomposition> pseudoInverse() const inline const Inverse<CompleteOrthogonalDecomposition> pseudoInverse() const
{ {
eigen_assert(m_cpqr.m_isInitialized && "CompleteOrthogonalDecomposition is not initialized.");
return Inverse<CompleteOrthogonalDecomposition>(*this); return Inverse<CompleteOrthogonalDecomposition>(*this);
} }
@ -368,6 +371,9 @@ class CompleteOrthogonalDecomposition {
#ifndef EIGEN_PARSED_BY_DOXYGEN #ifndef EIGEN_PARSED_BY_DOXYGEN
template <typename RhsType, typename DstType> template <typename RhsType, typename DstType>
void _solve_impl(const RhsType& rhs, DstType& dst) const; void _solve_impl(const RhsType& rhs, DstType& dst) const;
template<bool Conjugate, typename RhsType, typename DstType>
void _solve_impl_transposed(const RhsType &rhs, DstType &dst) const;
#endif #endif
protected: protected:
@ -375,8 +381,21 @@ class CompleteOrthogonalDecomposition {
EIGEN_STATIC_ASSERT_NON_INTEGER(Scalar); EIGEN_STATIC_ASSERT_NON_INTEGER(Scalar);
} }
template<bool Transpose_, typename Rhs>
void _check_solve_assertion(const Rhs& b) const {
eigen_assert(m_cpqr.m_isInitialized && "CompleteOrthogonalDecomposition is not initialized.");
eigen_assert((Transpose_?derived().cols():derived().rows())==b.rows() && "CompleteOrthogonalDecomposition::solve(): invalid number of rows of the right hand side matrix b");
}
void computeInPlace(); void computeInPlace();
/** Overwrites \b rhs with \f$ \mathbf{Z} * \mathbf{rhs} \f$ or
* \f$ \mathbf{\overline Z} * \mathbf{rhs} \f$ if \c Conjugate
* is set to \c true.
*/
template <bool Conjugate, typename Rhs>
void applyZOnTheLeftInPlace(Rhs& rhs) const;
/** Overwrites \b rhs with \f$ \mathbf{Z}^* * \mathbf{rhs} \f$. /** Overwrites \b rhs with \f$ \mathbf{Z}^* * \mathbf{rhs} \f$.
*/ */
template <typename Rhs> template <typename Rhs>
@ -464,6 +483,28 @@ void CompleteOrthogonalDecomposition<MatrixType>::computeInPlace()
} }
} }
template <typename MatrixType>
template <bool Conjugate, typename Rhs>
void CompleteOrthogonalDecomposition<MatrixType>::applyZOnTheLeftInPlace(
Rhs& rhs) const {
const Index cols = this->cols();
const Index nrhs = rhs.cols();
const Index rank = this->rank();
Matrix<typename Rhs::Scalar, Dynamic, 1> temp((std::max)(cols, nrhs));
for (Index k = rank-1; k >= 0; --k) {
if (k != rank - 1) {
rhs.row(k).swap(rhs.row(rank - 1));
}
rhs.middleRows(rank - 1, cols - rank + 1)
.applyHouseholderOnTheLeft(
matrixQTZ().row(k).tail(cols - rank).transpose().template conjugateIf<!Conjugate>(), zCoeffs().template conjugateIf<Conjugate>()(k),
&temp(0));
if (k != rank - 1) {
rhs.row(k).swap(rhs.row(rank - 1));
}
}
}
template <typename MatrixType> template <typename MatrixType>
template <typename Rhs> template <typename Rhs>
void CompleteOrthogonalDecomposition<MatrixType>::applyZAdjointOnTheLeftInPlace( void CompleteOrthogonalDecomposition<MatrixType>::applyZAdjointOnTheLeftInPlace(
@ -471,7 +512,7 @@ void CompleteOrthogonalDecomposition<MatrixType>::applyZAdjointOnTheLeftInPlace(
const Index cols = this->cols(); const Index cols = this->cols();
const Index nrhs = rhs.cols(); const Index nrhs = rhs.cols();
const Index rank = this->rank(); const Index rank = this->rank();
Matrix<typename MatrixType::Scalar, Dynamic, 1> temp((std::max)(cols, nrhs)); Matrix<typename Rhs::Scalar, Dynamic, 1> temp((std::max)(cols, nrhs));
for (Index k = 0; k < rank; ++k) { for (Index k = 0; k < rank; ++k) {
if (k != rank - 1) { if (k != rank - 1) {
rhs.row(k).swap(rhs.row(rank - 1)); rhs.row(k).swap(rhs.row(rank - 1));
@ -491,8 +532,6 @@ template <typename _MatrixType>
template <typename RhsType, typename DstType> template <typename RhsType, typename DstType>
void CompleteOrthogonalDecomposition<_MatrixType>::_solve_impl( void CompleteOrthogonalDecomposition<_MatrixType>::_solve_impl(
const RhsType& rhs, DstType& dst) const { const RhsType& rhs, DstType& dst) const {
eigen_assert(rhs.rows() == this->rows());
const Index rank = this->rank(); const Index rank = this->rank();
if (rank == 0) { if (rank == 0) {
dst.setZero(); dst.setZero();
@ -520,6 +559,34 @@ void CompleteOrthogonalDecomposition<_MatrixType>::_solve_impl(
// Undo permutation to get x = P^{-1} * y. // Undo permutation to get x = P^{-1} * y.
dst = colsPermutation() * dst; dst = colsPermutation() * dst;
} }
template<typename _MatrixType>
template<bool Conjugate, typename RhsType, typename DstType>
void CompleteOrthogonalDecomposition<_MatrixType>::_solve_impl_transposed(const RhsType &rhs, DstType &dst) const
{
const Index rank = this->rank();
if (rank == 0) {
dst.setZero();
return;
}
typename RhsType::PlainObject c(colsPermutation().transpose()*rhs);
if (rank < cols()) {
applyZOnTheLeftInPlace<!Conjugate>(c);
}
matrixT().topLeftCorner(rank, rank)
.template triangularView<Upper>()
.transpose().template conjugateIf<Conjugate>()
.solveInPlace(c.topRows(rank));
dst.topRows(rank) = c.topRows(rank);
dst.bottomRows(rows()-rank).setZero();
dst.applyOnTheLeft(householderQ().setLength(rank).template conjugateIf<!Conjugate>() );
}
#endif #endif
namespace internal { namespace internal {

View File

@ -18,6 +18,9 @@ namespace internal {
template<typename _MatrixType> struct traits<FullPivHouseholderQR<_MatrixType> > template<typename _MatrixType> struct traits<FullPivHouseholderQR<_MatrixType> >
: traits<_MatrixType> : traits<_MatrixType>
{ {
typedef MatrixXpr XprKind;
typedef SolverStorage StorageKind;
typedef int StorageIndex;
enum { Flags = 0 }; enum { Flags = 0 };
}; };
@ -55,20 +58,19 @@ struct traits<FullPivHouseholderQRMatrixQReturnType<MatrixType> >
* \sa MatrixBase::fullPivHouseholderQr() * \sa MatrixBase::fullPivHouseholderQr()
*/ */
template<typename _MatrixType> class FullPivHouseholderQR template<typename _MatrixType> class FullPivHouseholderQR
: public SolverBase<FullPivHouseholderQR<_MatrixType> >
{ {
public: public:
typedef _MatrixType MatrixType; typedef _MatrixType MatrixType;
typedef SolverBase<FullPivHouseholderQR> Base;
friend class SolverBase<FullPivHouseholderQR>;
EIGEN_GENERIC_PUBLIC_INTERFACE(FullPivHouseholderQR)
enum { enum {
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
ColsAtCompileTime = MatrixType::ColsAtCompileTime,
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime, MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
}; };
typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::RealScalar RealScalar;
// FIXME should be int
typedef typename MatrixType::StorageIndex StorageIndex;
typedef internal::FullPivHouseholderQRMatrixQReturnType<MatrixType> MatrixQReturnType; typedef internal::FullPivHouseholderQRMatrixQReturnType<MatrixType> MatrixQReturnType;
typedef typename internal::plain_diag_type<MatrixType>::type HCoeffsType; typedef typename internal::plain_diag_type<MatrixType>::type HCoeffsType;
typedef Matrix<StorageIndex, 1, typedef Matrix<StorageIndex, 1,
@ -156,6 +158,7 @@ template<typename _MatrixType> class FullPivHouseholderQR
computeInPlace(); computeInPlace();
} }
#ifdef EIGEN_PARSED_BY_DOXYGEN
/** This method finds a solution x to the equation Ax=b, where A is the matrix of which /** This method finds a solution x to the equation Ax=b, where A is the matrix of which
* \c *this is the QR decomposition. * \c *this is the QR decomposition.
* *
@ -173,11 +176,8 @@ template<typename _MatrixType> class FullPivHouseholderQR
*/ */
template<typename Rhs> template<typename Rhs>
inline const Solve<FullPivHouseholderQR, Rhs> inline const Solve<FullPivHouseholderQR, Rhs>
solve(const MatrixBase<Rhs>& b) const solve(const MatrixBase<Rhs>& b) const;
{ #endif
eigen_assert(m_isInitialized && "FullPivHouseholderQR is not initialized.");
return Solve<FullPivHouseholderQR, Rhs>(*this, b.derived());
}
/** \returns Expression object representing the matrix Q /** \returns Expression object representing the matrix Q
*/ */
@ -396,6 +396,9 @@ template<typename _MatrixType> class FullPivHouseholderQR
#ifndef EIGEN_PARSED_BY_DOXYGEN #ifndef EIGEN_PARSED_BY_DOXYGEN
template<typename RhsType, typename DstType> template<typename RhsType, typename DstType>
void _solve_impl(const RhsType &rhs, DstType &dst) const; void _solve_impl(const RhsType &rhs, DstType &dst) const;
template<bool Conjugate, typename RhsType, typename DstType>
void _solve_impl_transposed(const RhsType &rhs, DstType &dst) const;
#endif #endif
protected: protected:
@ -498,15 +501,15 @@ void FullPivHouseholderQR<MatrixType>::computeInPlace()
m_nonzero_pivots = k; m_nonzero_pivots = k;
for(Index i = k; i < size; i++) for(Index i = k; i < size; i++)
{ {
m_rows_transpositions.coeffRef(i) = i; m_rows_transpositions.coeffRef(i) = internal::convert_index<StorageIndex>(i);
m_cols_transpositions.coeffRef(i) = i; m_cols_transpositions.coeffRef(i) = internal::convert_index<StorageIndex>(i);
m_hCoeffs.coeffRef(i) = Scalar(0); m_hCoeffs.coeffRef(i) = Scalar(0);
} }
break; break;
} }
m_rows_transpositions.coeffRef(k) = row_of_biggest_in_corner; m_rows_transpositions.coeffRef(k) = internal::convert_index<StorageIndex>(row_of_biggest_in_corner);
m_cols_transpositions.coeffRef(k) = col_of_biggest_in_corner; m_cols_transpositions.coeffRef(k) = internal::convert_index<StorageIndex>(col_of_biggest_in_corner);
if(k != row_of_biggest_in_corner) { if(k != row_of_biggest_in_corner) {
m_qr.row(k).tail(cols-k).swap(m_qr.row(row_of_biggest_in_corner).tail(cols-k)); m_qr.row(k).tail(cols-k).swap(m_qr.row(row_of_biggest_in_corner).tail(cols-k));
++number_of_transpositions; ++number_of_transpositions;
@ -540,7 +543,6 @@ template<typename _MatrixType>
template<typename RhsType, typename DstType> template<typename RhsType, typename DstType>
void FullPivHouseholderQR<_MatrixType>::_solve_impl(const RhsType &rhs, DstType &dst) const void FullPivHouseholderQR<_MatrixType>::_solve_impl(const RhsType &rhs, DstType &dst) const
{ {
eigen_assert(rhs.rows() == rows());
const Index l_rank = rank(); const Index l_rank = rank();
// FIXME introduce nonzeroPivots() and use it here. and more generally, // FIXME introduce nonzeroPivots() and use it here. and more generally,
@ -553,7 +555,7 @@ void FullPivHouseholderQR<_MatrixType>::_solve_impl(const RhsType &rhs, DstType
typename RhsType::PlainObject c(rhs); typename RhsType::PlainObject c(rhs);
Matrix<Scalar,1,RhsType::ColsAtCompileTime> temp(rhs.cols()); Matrix<typename RhsType::Scalar,1,RhsType::ColsAtCompileTime> temp(rhs.cols());
for (Index k = 0; k < l_rank; ++k) for (Index k = 0; k < l_rank; ++k)
{ {
Index remainingSize = rows()-k; Index remainingSize = rows()-k;
@ -570,6 +572,42 @@ void FullPivHouseholderQR<_MatrixType>::_solve_impl(const RhsType &rhs, DstType
for(Index i = 0; i < l_rank; ++i) dst.row(m_cols_permutation.indices().coeff(i)) = c.row(i); for(Index i = 0; i < l_rank; ++i) dst.row(m_cols_permutation.indices().coeff(i)) = c.row(i);
for(Index i = l_rank; i < cols(); ++i) dst.row(m_cols_permutation.indices().coeff(i)).setZero(); for(Index i = l_rank; i < cols(); ++i) dst.row(m_cols_permutation.indices().coeff(i)).setZero();
} }
template<typename _MatrixType>
template<bool Conjugate, typename RhsType, typename DstType>
void FullPivHouseholderQR<_MatrixType>::_solve_impl_transposed(const RhsType &rhs, DstType &dst) const
{
const Index l_rank = rank();
if(l_rank == 0)
{
dst.setZero();
return;
}
typename RhsType::PlainObject c(m_cols_permutation.transpose()*rhs);
m_qr.topLeftCorner(l_rank, l_rank)
.template triangularView<Upper>()
.transpose().template conjugateIf<Conjugate>()
.solveInPlace(c.topRows(l_rank));
dst.topRows(l_rank) = c.topRows(l_rank);
dst.bottomRows(rows()-l_rank).setZero();
Matrix<Scalar, 1, DstType::ColsAtCompileTime> temp(dst.cols());
const Index size = (std::min)(rows(), cols());
for (Index k = size-1; k >= 0; --k)
{
Index remainingSize = rows()-k;
dst.bottomRightCorner(remainingSize, dst.cols())
.applyHouseholderOnTheLeft(m_qr.col(k).tail(remainingSize-1).template conjugateIf<!Conjugate>(),
m_hCoeffs.template conjugateIf<Conjugate>().coeff(k), &temp.coeffRef(0));
dst.row(k).swap(dst.row(m_rows_transpositions.coeff(k)));
}
}
#endif #endif
namespace internal { namespace internal {

View File

@ -14,6 +14,18 @@
namespace Eigen { namespace Eigen {
namespace internal {
template<typename _MatrixType> struct traits<HouseholderQR<_MatrixType> >
: traits<_MatrixType>
{
typedef MatrixXpr XprKind;
typedef SolverStorage StorageKind;
typedef int StorageIndex;
enum { Flags = 0 };
};
} // end namespace internal
/** \ingroup QR_Module /** \ingroup QR_Module
* *
* *
@ -42,20 +54,19 @@ namespace Eigen {
* \sa MatrixBase::householderQr() * \sa MatrixBase::householderQr()
*/ */
template<typename _MatrixType> class HouseholderQR template<typename _MatrixType> class HouseholderQR
: public SolverBase<HouseholderQR<_MatrixType> >
{ {
public: public:
typedef _MatrixType MatrixType; typedef _MatrixType MatrixType;
typedef SolverBase<HouseholderQR> Base;
friend class SolverBase<HouseholderQR>;
EIGEN_GENERIC_PUBLIC_INTERFACE(HouseholderQR)
enum { enum {
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
ColsAtCompileTime = MatrixType::ColsAtCompileTime,
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime, MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
}; };
typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::RealScalar RealScalar;
// FIXME should be int
typedef typename MatrixType::StorageIndex StorageIndex;
typedef Matrix<Scalar, RowsAtCompileTime, RowsAtCompileTime, (MatrixType::Flags&RowMajorBit) ? RowMajor : ColMajor, MaxRowsAtCompileTime, MaxRowsAtCompileTime> MatrixQType; typedef Matrix<Scalar, RowsAtCompileTime, RowsAtCompileTime, (MatrixType::Flags&RowMajorBit) ? RowMajor : ColMajor, MaxRowsAtCompileTime, MaxRowsAtCompileTime> MatrixQType;
typedef typename internal::plain_diag_type<MatrixType>::type HCoeffsType; typedef typename internal::plain_diag_type<MatrixType>::type HCoeffsType;
typedef typename internal::plain_row_type<MatrixType>::type RowVectorType; typedef typename internal::plain_row_type<MatrixType>::type RowVectorType;
@ -121,6 +132,7 @@ template<typename _MatrixType> class HouseholderQR
computeInPlace(); computeInPlace();
} }
#ifdef EIGEN_PARSED_BY_DOXYGEN
/** This method finds a solution x to the equation Ax=b, where A is the matrix of which /** This method finds a solution x to the equation Ax=b, where A is the matrix of which
* *this is the QR decomposition, if any exists. * *this is the QR decomposition, if any exists.
* *
@ -137,11 +149,8 @@ template<typename _MatrixType> class HouseholderQR
*/ */
template<typename Rhs> template<typename Rhs>
inline const Solve<HouseholderQR, Rhs> inline const Solve<HouseholderQR, Rhs>
solve(const MatrixBase<Rhs>& b) const solve(const MatrixBase<Rhs>& b) const;
{ #endif
eigen_assert(m_isInitialized && "HouseholderQR is not initialized.");
return Solve<HouseholderQR, Rhs>(*this, b.derived());
}
/** This method returns an expression of the unitary matrix Q as a sequence of Householder transformations. /** This method returns an expression of the unitary matrix Q as a sequence of Householder transformations.
* *
@ -214,6 +223,9 @@ template<typename _MatrixType> class HouseholderQR
#ifndef EIGEN_PARSED_BY_DOXYGEN #ifndef EIGEN_PARSED_BY_DOXYGEN
template<typename RhsType, typename DstType> template<typename RhsType, typename DstType>
void _solve_impl(const RhsType &rhs, DstType &dst) const; void _solve_impl(const RhsType &rhs, DstType &dst) const;
template<bool Conjugate, typename RhsType, typename DstType>
void _solve_impl_transposed(const RhsType &rhs, DstType &dst) const;
#endif #endif
protected: protected:
@ -349,7 +361,6 @@ template<typename RhsType, typename DstType>
void HouseholderQR<_MatrixType>::_solve_impl(const RhsType &rhs, DstType &dst) const void HouseholderQR<_MatrixType>::_solve_impl(const RhsType &rhs, DstType &dst) const
{ {
const Index rank = (std::min)(rows(), cols()); const Index rank = (std::min)(rows(), cols());
eigen_assert(rhs.rows() == rows());
typename RhsType::PlainObject c(rhs); typename RhsType::PlainObject c(rhs);
@ -362,6 +373,25 @@ void HouseholderQR<_MatrixType>::_solve_impl(const RhsType &rhs, DstType &dst) c
dst.topRows(rank) = c.topRows(rank); dst.topRows(rank) = c.topRows(rank);
dst.bottomRows(cols()-rank).setZero(); dst.bottomRows(cols()-rank).setZero();
} }
template<typename _MatrixType>
template<bool Conjugate, typename RhsType, typename DstType>
void HouseholderQR<_MatrixType>::_solve_impl_transposed(const RhsType &rhs, DstType &dst) const
{
const Index rank = (std::min)(rows(), cols());
typename RhsType::PlainObject c(rhs);
m_qr.topLeftCorner(rank, rank)
.template triangularView<Upper>()
.transpose().template conjugateIf<Conjugate>()
.solveInPlace(c.topRows(rank));
dst.topRows(rank) = c.topRows(rank);
dst.bottomRows(rows()-rank).setZero();
dst.applyOnTheLeft(householderQ().setLength(rank).template conjugateIf<!Conjugate>() );
}
#endif #endif
/** Performs the QR factorization of the given matrix \a matrix. The result of /** Performs the QR factorization of the given matrix \a matrix. The result of

View File

@ -39,6 +39,7 @@ namespace internal {
template<typename _MatrixType> template<typename _MatrixType>
struct traits<BDCSVD<_MatrixType> > struct traits<BDCSVD<_MatrixType> >
: traits<_MatrixType>
{ {
typedef _MatrixType MatrixType; typedef _MatrixType MatrixType;
}; };
@ -1006,7 +1007,7 @@ void BDCSVD<MatrixType>::perturbCol0
#ifdef EIGEN_BDCSVD_SANITY_CHECKS #ifdef EIGEN_BDCSVD_SANITY_CHECKS
assert((std::isfinite)(tmp)); assert((std::isfinite)(tmp));
#endif #endif
zhat(k) = col0(k) > Literal(0) ? tmp : -tmp; zhat(k) = col0(k) > Literal(0) ? RealScalar(tmp) : RealScalar(-tmp);
} }
} }
} }

View File

@ -425,6 +425,7 @@ struct svd_precondition_2x2_block_to_be_real<MatrixType, QRPreconditioner, true>
template<typename _MatrixType, int QRPreconditioner> template<typename _MatrixType, int QRPreconditioner>
struct traits<JacobiSVD<_MatrixType,QRPreconditioner> > struct traits<JacobiSVD<_MatrixType,QRPreconditioner> >
: traits<_MatrixType>
{ {
typedef _MatrixType MatrixType; typedef _MatrixType MatrixType;
}; };

View File

@ -17,6 +17,18 @@
#define EIGEN_SVDBASE_H #define EIGEN_SVDBASE_H
namespace Eigen { namespace Eigen {
namespace internal {
template<typename Derived> struct traits<SVDBase<Derived> >
: traits<Derived>
{
typedef MatrixXpr XprKind;
typedef SolverStorage StorageKind;
typedef int StorageIndex;
enum { Flags = 0 };
};
}
/** \ingroup SVD_Module /** \ingroup SVD_Module
* *
* *
@ -44,15 +56,18 @@ namespace Eigen {
* terminate in finite (and reasonable) time. * terminate in finite (and reasonable) time.
* \sa class BDCSVD, class JacobiSVD * \sa class BDCSVD, class JacobiSVD
*/ */
template<typename Derived> template<typename Derived> class SVDBase
class SVDBase : public SolverBase<SVDBase<Derived> >
{ {
public: public:
template<typename Derived_>
friend struct internal::solve_assertion;
typedef typename internal::traits<Derived>::MatrixType MatrixType; typedef typename internal::traits<Derived>::MatrixType MatrixType;
typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::Scalar Scalar;
typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar; typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar;
typedef typename MatrixType::StorageIndex StorageIndex; typedef typename Eigen::internal::traits<SVDBase>::StorageIndex StorageIndex;
typedef Eigen::Index Index; ///< \deprecated since Eigen 3.3 typedef Eigen::Index Index; ///< \deprecated since Eigen 3.3
enum { enum {
RowsAtCompileTime = MatrixType::RowsAtCompileTime, RowsAtCompileTime = MatrixType::RowsAtCompileTime,
@ -194,6 +209,7 @@ public:
inline Index rows() const { return m_rows; } inline Index rows() const { return m_rows; }
inline Index cols() const { return m_cols; } inline Index cols() const { return m_cols; }
#ifdef EIGEN_PARSED_BY_DOXYGEN
/** \returns a (least squares) solution of \f$ A x = b \f$ using the current SVD decomposition of A. /** \returns a (least squares) solution of \f$ A x = b \f$ using the current SVD decomposition of A.
* *
* \param b the right-hand-side of the equation to solve. * \param b the right-hand-side of the equation to solve.
@ -205,16 +221,15 @@ public:
*/ */
template<typename Rhs> template<typename Rhs>
inline const Solve<Derived, Rhs> inline const Solve<Derived, Rhs>
solve(const MatrixBase<Rhs>& b) const solve(const MatrixBase<Rhs>& b) const;
{ #endif
eigen_assert(m_isInitialized && "SVD is not initialized.");
eigen_assert(computeU() && computeV() && "SVD::solve() requires both unitaries U and V to be computed (thin unitaries suffice).");
return Solve<Derived, Rhs>(derived(), b.derived());
}
#ifndef EIGEN_PARSED_BY_DOXYGEN #ifndef EIGEN_PARSED_BY_DOXYGEN
template<typename RhsType, typename DstType> template<typename RhsType, typename DstType>
void _solve_impl(const RhsType &rhs, DstType &dst) const; void _solve_impl(const RhsType &rhs, DstType &dst) const;
template<bool Conjugate, typename RhsType, typename DstType>
void _solve_impl_transposed(const RhsType &rhs, DstType &dst) const;
#endif #endif
protected: protected:
@ -224,6 +239,13 @@ protected:
EIGEN_STATIC_ASSERT_NON_INTEGER(Scalar); EIGEN_STATIC_ASSERT_NON_INTEGER(Scalar);
} }
template<bool Transpose_, typename Rhs>
void _check_solve_assertion(const Rhs& b) const {
eigen_assert(m_isInitialized && "SVD is not initialized.");
eigen_assert(computeU() && computeV() && "SVDBase::solve(): Both unitaries U and V are required to be computed (thin unitaries suffice).");
eigen_assert((Transpose_?cols():rows())==b.rows() && "SVDBase::solve(): invalid number of rows of the right hand side matrix b");
}
// return true if already allocated // return true if already allocated
bool allocate(Index rows, Index cols, unsigned int computationOptions) ; bool allocate(Index rows, Index cols, unsigned int computationOptions) ;
@ -263,17 +285,30 @@ template<typename Derived>
template<typename RhsType, typename DstType> template<typename RhsType, typename DstType>
void SVDBase<Derived>::_solve_impl(const RhsType &rhs, DstType &dst) const void SVDBase<Derived>::_solve_impl(const RhsType &rhs, DstType &dst) const
{ {
eigen_assert(rhs.rows() == rows());
// A = U S V^* // A = U S V^*
// So A^{-1} = V S^{-1} U^* // So A^{-1} = V S^{-1} U^*
Matrix<Scalar, Dynamic, RhsType::ColsAtCompileTime, 0, MatrixType::MaxRowsAtCompileTime, RhsType::MaxColsAtCompileTime> tmp; Matrix<typename RhsType::Scalar, Dynamic, RhsType::ColsAtCompileTime, 0, MatrixType::MaxRowsAtCompileTime, RhsType::MaxColsAtCompileTime> tmp;
Index l_rank = rank(); Index l_rank = rank();
tmp.noalias() = m_matrixU.leftCols(l_rank).adjoint() * rhs; tmp.noalias() = m_matrixU.leftCols(l_rank).adjoint() * rhs;
tmp = m_singularValues.head(l_rank).asDiagonal().inverse() * tmp; tmp = m_singularValues.head(l_rank).asDiagonal().inverse() * tmp;
dst = m_matrixV.leftCols(l_rank) * tmp; dst = m_matrixV.leftCols(l_rank) * tmp;
} }
template<typename Derived>
template<bool Conjugate, typename RhsType, typename DstType>
void SVDBase<Derived>::_solve_impl_transposed(const RhsType &rhs, DstType &dst) const
{
// A = U S V^*
// So A^{-*} = U S^{-1} V^*
// And A^{-T} = U_conj S^{-1} V^T
Matrix<typename RhsType::Scalar, Dynamic, RhsType::ColsAtCompileTime, 0, MatrixType::MaxRowsAtCompileTime, RhsType::MaxColsAtCompileTime> tmp;
Index l_rank = rank();
tmp.noalias() = m_matrixV.leftCols(l_rank).transpose().template conjugateIf<Conjugate>() * rhs;
tmp = m_singularValues.head(l_rank).asDiagonal().inverse() * tmp;
dst = m_matrixU.template conjugateIf<!Conjugate>().leftCols(l_rank) * tmp;
}
#endif #endif
template<typename MatrixType> template<typename MatrixType>

View File

@ -46,6 +46,8 @@ void bdcsvd_method()
VERIFY_RAISES_ASSERT(m.bdcSvd().matrixU()); VERIFY_RAISES_ASSERT(m.bdcSvd().matrixU());
VERIFY_RAISES_ASSERT(m.bdcSvd().matrixV()); VERIFY_RAISES_ASSERT(m.bdcSvd().matrixV());
VERIFY_IS_APPROX(m.bdcSvd(ComputeFullU|ComputeFullV).solve(m), m); VERIFY_IS_APPROX(m.bdcSvd(ComputeFullU|ComputeFullV).solve(m), m);
VERIFY_IS_APPROX(m.bdcSvd(ComputeFullU|ComputeFullV).transpose().solve(m), m);
VERIFY_IS_APPROX(m.bdcSvd(ComputeFullU|ComputeFullV).adjoint().solve(m), m);
} }
// compare the Singular values returned with Jacobi and Bdc // compare the Singular values returned with Jacobi and Bdc

View File

@ -7,15 +7,12 @@
// Public License v. 2.0. If a copy of the MPL was not distributed // Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_NO_ASSERTION_CHECKING
#define EIGEN_NO_ASSERTION_CHECKING
#endif
#define TEST_ENABLE_TEMPORARY_TRACKING #define TEST_ENABLE_TEMPORARY_TRACKING
#include "main.h" #include "main.h"
#include <Eigen/Cholesky> #include <Eigen/Cholesky>
#include <Eigen/QR> #include <Eigen/QR>
#include "solverbase.h"
template<typename MatrixType, int UpLo> template<typename MatrixType, int UpLo>
typename MatrixType::RealScalar matrix_l1_norm(const MatrixType& m) { typename MatrixType::RealScalar matrix_l1_norm(const MatrixType& m) {
@ -81,15 +78,17 @@ template<typename MatrixType> void cholesky(const MatrixType& m)
} }
{ {
STATIC_CHECK(( internal::is_same<typename LLT<MatrixType,Lower>::StorageIndex,int>::value ));
STATIC_CHECK(( internal::is_same<typename LLT<MatrixType,Upper>::StorageIndex,int>::value ));
SquareMatrixType symmUp = symm.template triangularView<Upper>(); SquareMatrixType symmUp = symm.template triangularView<Upper>();
SquareMatrixType symmLo = symm.template triangularView<Lower>(); SquareMatrixType symmLo = symm.template triangularView<Lower>();
LLT<SquareMatrixType,Lower> chollo(symmLo); LLT<SquareMatrixType,Lower> chollo(symmLo);
VERIFY_IS_APPROX(symm, chollo.reconstructedMatrix()); VERIFY_IS_APPROX(symm, chollo.reconstructedMatrix());
vecX = chollo.solve(vecB);
VERIFY_IS_APPROX(symm * vecX, vecB); check_solverbase<VectorType, VectorType>(symm, chollo, rows, rows, 1);
matX = chollo.solve(matB); check_solverbase<MatrixType, MatrixType>(symm, chollo, rows, cols, rows);
VERIFY_IS_APPROX(symm * matX, matB);
const MatrixType symmLo_inverse = chollo.solve(MatrixType::Identity(rows,cols)); const MatrixType symmLo_inverse = chollo.solve(MatrixType::Identity(rows,cols));
RealScalar rcond = (RealScalar(1) / matrix_l1_norm<MatrixType, Lower>(symmLo)) / RealScalar rcond = (RealScalar(1) / matrix_l1_norm<MatrixType, Lower>(symmLo)) /
@ -143,6 +142,9 @@ template<typename MatrixType> void cholesky(const MatrixType& m)
// LDLT // LDLT
{ {
STATIC_CHECK(( internal::is_same<typename LDLT<MatrixType,Lower>::StorageIndex,int>::value ));
STATIC_CHECK(( internal::is_same<typename LDLT<MatrixType,Upper>::StorageIndex,int>::value ));
int sign = internal::random<int>()%2 ? 1 : -1; int sign = internal::random<int>()%2 ? 1 : -1;
if(sign == -1) if(sign == -1)
@ -156,10 +158,9 @@ template<typename MatrixType> void cholesky(const MatrixType& m)
LDLT<SquareMatrixType,Lower> ldltlo(symmLo); LDLT<SquareMatrixType,Lower> ldltlo(symmLo);
VERIFY(ldltlo.info()==Success); VERIFY(ldltlo.info()==Success);
VERIFY_IS_APPROX(symm, ldltlo.reconstructedMatrix()); VERIFY_IS_APPROX(symm, ldltlo.reconstructedMatrix());
vecX = ldltlo.solve(vecB);
VERIFY_IS_APPROX(symm * vecX, vecB); check_solverbase<VectorType, VectorType>(symm, ldltlo, rows, rows, 1);
matX = ldltlo.solve(matB); check_solverbase<MatrixType, MatrixType>(symm, ldltlo, rows, cols, rows);
VERIFY_IS_APPROX(symm * matX, matB);
const MatrixType symmLo_inverse = ldltlo.solve(MatrixType::Identity(rows,cols)); const MatrixType symmLo_inverse = ldltlo.solve(MatrixType::Identity(rows,cols));
RealScalar rcond = (RealScalar(1) / matrix_l1_norm<MatrixType, Lower>(symmLo)) / RealScalar rcond = (RealScalar(1) / matrix_l1_norm<MatrixType, Lower>(symmLo)) /
@ -313,10 +314,9 @@ template<typename MatrixType> void cholesky_cplx(const MatrixType& m)
LLT<RealMatrixType,Lower> chollo(symmLo); LLT<RealMatrixType,Lower> chollo(symmLo);
VERIFY_IS_APPROX(symm, chollo.reconstructedMatrix()); VERIFY_IS_APPROX(symm, chollo.reconstructedMatrix());
vecX = chollo.solve(vecB);
VERIFY_IS_APPROX(symm * vecX, vecB); check_solverbase<VectorType, VectorType>(symm, chollo, rows, rows, 1);
// matX = chollo.solve(matB); //check_solverbase<MatrixType, MatrixType>(symm, chollo, rows, cols, rows);
// VERIFY_IS_APPROX(symm * matX, matB);
} }
// LDLT // LDLT
@ -333,10 +333,9 @@ template<typename MatrixType> void cholesky_cplx(const MatrixType& m)
LDLT<RealMatrixType,Lower> ldltlo(symmLo); LDLT<RealMatrixType,Lower> ldltlo(symmLo);
VERIFY(ldltlo.info()==Success); VERIFY(ldltlo.info()==Success);
VERIFY_IS_APPROX(symm, ldltlo.reconstructedMatrix()); VERIFY_IS_APPROX(symm, ldltlo.reconstructedMatrix());
vecX = ldltlo.solve(vecB);
VERIFY_IS_APPROX(symm * vecX, vecB); check_solverbase<VectorType, VectorType>(symm, ldltlo, rows, rows, 1);
// matX = ldltlo.solve(matB); //check_solverbase<MatrixType, MatrixType>(symm, ldltlo, rows, cols, rows);
// VERIFY_IS_APPROX(symm * matX, matB);
} }
} }
@ -477,16 +476,20 @@ template<typename MatrixType> void cholesky_verify_assert()
VERIFY_RAISES_ASSERT(llt.matrixL()) VERIFY_RAISES_ASSERT(llt.matrixL())
VERIFY_RAISES_ASSERT(llt.matrixU()) VERIFY_RAISES_ASSERT(llt.matrixU())
VERIFY_RAISES_ASSERT(llt.solve(tmp)) VERIFY_RAISES_ASSERT(llt.solve(tmp))
VERIFY_RAISES_ASSERT(llt.solveInPlace(&tmp)) VERIFY_RAISES_ASSERT(llt.transpose().solve(tmp))
VERIFY_RAISES_ASSERT(llt.adjoint().solve(tmp))
VERIFY_RAISES_ASSERT(llt.solveInPlace(tmp))
LDLT<MatrixType> ldlt; LDLT<MatrixType> ldlt;
VERIFY_RAISES_ASSERT(ldlt.matrixL()) VERIFY_RAISES_ASSERT(ldlt.matrixL())
VERIFY_RAISES_ASSERT(ldlt.permutationP()) VERIFY_RAISES_ASSERT(ldlt.transpositionsP())
VERIFY_RAISES_ASSERT(ldlt.vectorD()) VERIFY_RAISES_ASSERT(ldlt.vectorD())
VERIFY_RAISES_ASSERT(ldlt.isPositive()) VERIFY_RAISES_ASSERT(ldlt.isPositive())
VERIFY_RAISES_ASSERT(ldlt.isNegative()) VERIFY_RAISES_ASSERT(ldlt.isNegative())
VERIFY_RAISES_ASSERT(ldlt.solve(tmp)) VERIFY_RAISES_ASSERT(ldlt.solve(tmp))
VERIFY_RAISES_ASSERT(ldlt.solveInPlace(&tmp)) VERIFY_RAISES_ASSERT(ldlt.transpose().solve(tmp))
VERIFY_RAISES_ASSERT(ldlt.adjoint().solve(tmp))
VERIFY_RAISES_ASSERT(ldlt.solveInPlace(tmp))
} }
EIGEN_DECLARE_TEST(cholesky) EIGEN_DECLARE_TEST(cholesky)

View File

@ -67,6 +67,8 @@ void jacobisvd_method()
VERIFY_RAISES_ASSERT(m.jacobiSvd().matrixU()); VERIFY_RAISES_ASSERT(m.jacobiSvd().matrixU());
VERIFY_RAISES_ASSERT(m.jacobiSvd().matrixV()); VERIFY_RAISES_ASSERT(m.jacobiSvd().matrixV());
VERIFY_IS_APPROX(m.jacobiSvd(ComputeFullU|ComputeFullV).solve(m), m); VERIFY_IS_APPROX(m.jacobiSvd(ComputeFullU|ComputeFullV).solve(m), m);
VERIFY_IS_APPROX(m.jacobiSvd(ComputeFullU|ComputeFullV).transpose().solve(m), m);
VERIFY_IS_APPROX(m.jacobiSvd(ComputeFullU|ComputeFullV).adjoint().solve(m), m);
} }
namespace Foo { namespace Foo {

View File

@ -9,6 +9,7 @@
#include "main.h" #include "main.h"
#include <Eigen/LU> #include <Eigen/LU>
#include "solverbase.h"
using namespace std; using namespace std;
template<typename MatrixType> template<typename MatrixType>
@ -96,32 +97,14 @@ template<typename MatrixType> void lu_non_invertible()
VERIFY(m1image.fullPivLu().rank() == rank); VERIFY(m1image.fullPivLu().rank() == rank);
VERIFY_IS_APPROX(m1 * m1.adjoint() * m1image, m1image); VERIFY_IS_APPROX(m1 * m1.adjoint() * m1image, m1image);
check_solverbase<CMatrixType, MatrixType>(m1, lu, rows, cols, cols2);
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);
// test that the code, which does resize(), may be applied to an xpr // test that the code, which does resize(), may be applied to an xpr
m2.block(0,0,m2.rows(),m2.cols()) = lu.solve(m3); m2.block(0,0,m2.rows(),m2.cols()) = lu.solve(m3);
VERIFY_IS_APPROX(m3, m1*m2); VERIFY_IS_APPROX(m3, m1*m2);
// test solve with transposed
m3 = MatrixType::Random(rows,cols2);
m2 = m1.transpose()*m3;
m3 = MatrixType::Random(rows,cols2);
lu.template _solve_impl_transposed<false>(m2, m3);
VERIFY_IS_APPROX(m2, m1.transpose()*m3);
m3 = MatrixType::Random(rows,cols2);
m3 = lu.transpose().solve(m2);
VERIFY_IS_APPROX(m2, m1.transpose()*m3);
// test solve with conjugate transposed
m3 = MatrixType::Random(rows,cols2);
m2 = m1.adjoint()*m3;
m3 = MatrixType::Random(rows,cols2);
lu.template _solve_impl_transposed<true>(m2, m3);
VERIFY_IS_APPROX(m2, m1.adjoint()*m3);
m3 = MatrixType::Random(rows,cols2);
m3 = lu.adjoint().solve(m2);
VERIFY_IS_APPROX(m2, m1.adjoint()*m3);
} }
template<typename MatrixType> void lu_invertible() template<typename MatrixType> void lu_invertible()
@ -150,10 +133,12 @@ template<typename MatrixType> void lu_invertible()
VERIFY(lu.isSurjective()); VERIFY(lu.isSurjective());
VERIFY(lu.isInvertible()); VERIFY(lu.isInvertible());
VERIFY(lu.image(m1).fullPivLu().isInvertible()); VERIFY(lu.image(m1).fullPivLu().isInvertible());
check_solverbase<MatrixType, MatrixType>(m1, lu, size, size, size);
MatrixType m1_inverse = lu.inverse();
m3 = MatrixType::Random(size,size); m3 = MatrixType::Random(size,size);
m2 = lu.solve(m3); m2 = lu.solve(m3);
VERIFY_IS_APPROX(m3, m1*m2);
MatrixType m1_inverse = lu.inverse();
VERIFY_IS_APPROX(m2, m1_inverse*m3); VERIFY_IS_APPROX(m2, m1_inverse*m3);
RealScalar rcond = (RealScalar(1) / matrix_l1_norm(m1)) / matrix_l1_norm(m1_inverse); RealScalar rcond = (RealScalar(1) / matrix_l1_norm(m1)) / matrix_l1_norm(m1_inverse);
@ -162,20 +147,6 @@ template<typename MatrixType> void lu_invertible()
// truth. // truth.
VERIFY(rcond_est > rcond / 10 && rcond_est < rcond * 10); VERIFY(rcond_est > rcond / 10 && rcond_est < rcond * 10);
// test solve with transposed
lu.template _solve_impl_transposed<false>(m3, m2);
VERIFY_IS_APPROX(m3, m1.transpose()*m2);
m3 = MatrixType::Random(size,size);
m3 = lu.transpose().solve(m2);
VERIFY_IS_APPROX(m2, m1.transpose()*m3);
// test solve with conjugate transposed
lu.template _solve_impl_transposed<true>(m3, m2);
VERIFY_IS_APPROX(m3, m1.adjoint()*m2);
m3 = MatrixType::Random(size,size);
m3 = lu.adjoint().solve(m2);
VERIFY_IS_APPROX(m2, m1.adjoint()*m3);
// Regression test for Bug 302 // Regression test for Bug 302
MatrixType m4 = MatrixType::Random(size,size); MatrixType m4 = MatrixType::Random(size,size);
VERIFY_IS_APPROX(lu.solve(m3*m4), lu.solve(m3)*m4); VERIFY_IS_APPROX(lu.solve(m3*m4), lu.solve(m3)*m4);
@ -197,30 +168,17 @@ template<typename MatrixType> void lu_partial_piv()
VERIFY_IS_APPROX(m1, plu.reconstructedMatrix()); VERIFY_IS_APPROX(m1, plu.reconstructedMatrix());
check_solverbase<MatrixType, MatrixType>(m1, plu, size, size, size);
MatrixType m1_inverse = plu.inverse();
m3 = MatrixType::Random(size,size); m3 = MatrixType::Random(size,size);
m2 = plu.solve(m3); m2 = plu.solve(m3);
VERIFY_IS_APPROX(m3, m1*m2);
MatrixType m1_inverse = plu.inverse();
VERIFY_IS_APPROX(m2, m1_inverse*m3); VERIFY_IS_APPROX(m2, m1_inverse*m3);
RealScalar rcond = (RealScalar(1) / matrix_l1_norm(m1)) / matrix_l1_norm(m1_inverse); RealScalar rcond = (RealScalar(1) / matrix_l1_norm(m1)) / matrix_l1_norm(m1_inverse);
const RealScalar rcond_est = plu.rcond(); const RealScalar rcond_est = plu.rcond();
// Verify that the estimate is within a factor of 10 of the truth. // Verify that the estimate is within a factor of 10 of the truth.
VERIFY(rcond_est > rcond / 10 && rcond_est < rcond * 10); VERIFY(rcond_est > rcond / 10 && rcond_est < rcond * 10);
// test solve with transposed
plu.template _solve_impl_transposed<false>(m3, m2);
VERIFY_IS_APPROX(m3, m1.transpose()*m2);
m3 = MatrixType::Random(size,size);
m3 = plu.transpose().solve(m2);
VERIFY_IS_APPROX(m2, m1.transpose()*m3);
// test solve with conjugate transposed
plu.template _solve_impl_transposed<true>(m3, m2);
VERIFY_IS_APPROX(m3, m1.adjoint()*m2);
m3 = MatrixType::Random(size,size);
m3 = plu.adjoint().solve(m2);
VERIFY_IS_APPROX(m2, m1.adjoint()*m3);
} }
template<typename MatrixType> void lu_verify_assert() template<typename MatrixType> void lu_verify_assert()
@ -234,6 +192,8 @@ template<typename MatrixType> void lu_verify_assert()
VERIFY_RAISES_ASSERT(lu.kernel()) VERIFY_RAISES_ASSERT(lu.kernel())
VERIFY_RAISES_ASSERT(lu.image(tmp)) VERIFY_RAISES_ASSERT(lu.image(tmp))
VERIFY_RAISES_ASSERT(lu.solve(tmp)) VERIFY_RAISES_ASSERT(lu.solve(tmp))
VERIFY_RAISES_ASSERT(lu.transpose().solve(tmp))
VERIFY_RAISES_ASSERT(lu.adjoint().solve(tmp))
VERIFY_RAISES_ASSERT(lu.determinant()) VERIFY_RAISES_ASSERT(lu.determinant())
VERIFY_RAISES_ASSERT(lu.rank()) VERIFY_RAISES_ASSERT(lu.rank())
VERIFY_RAISES_ASSERT(lu.dimensionOfKernel()) VERIFY_RAISES_ASSERT(lu.dimensionOfKernel())
@ -246,6 +206,8 @@ template<typename MatrixType> void lu_verify_assert()
VERIFY_RAISES_ASSERT(plu.matrixLU()) VERIFY_RAISES_ASSERT(plu.matrixLU())
VERIFY_RAISES_ASSERT(plu.permutationP()) VERIFY_RAISES_ASSERT(plu.permutationP())
VERIFY_RAISES_ASSERT(plu.solve(tmp)) VERIFY_RAISES_ASSERT(plu.solve(tmp))
VERIFY_RAISES_ASSERT(plu.transpose().solve(tmp))
VERIFY_RAISES_ASSERT(plu.adjoint().solve(tmp))
VERIFY_RAISES_ASSERT(plu.determinant()) VERIFY_RAISES_ASSERT(plu.determinant())
VERIFY_RAISES_ASSERT(plu.inverse()) VERIFY_RAISES_ASSERT(plu.inverse())
} }

View File

@ -9,6 +9,7 @@
#include "main.h" #include "main.h"
#include <Eigen/QR> #include <Eigen/QR>
#include "solverbase.h"
template<typename MatrixType> void qr(const MatrixType& m) template<typename MatrixType> void qr(const MatrixType& m)
{ {
@ -41,11 +42,7 @@ template<typename MatrixType, int Cols2> void qr_fixedsize()
VERIFY_IS_APPROX(m1, qr.householderQ() * r); VERIFY_IS_APPROX(m1, qr.householderQ() * r);
Matrix<Scalar,Cols,Cols2> m2 = Matrix<Scalar,Cols,Cols2>::Random(Cols,Cols2); check_solverbase<Matrix<Scalar,Cols,Cols2>, Matrix<Scalar,Rows,Cols2> >(m1, qr, Rows, Cols, Cols2);
Matrix<Scalar,Rows,Cols2> m3 = m1*m2;
m2 = Matrix<Scalar,Cols,Cols2>::Random(Cols,Cols2);
m2 = qr.solve(m3);
VERIFY_IS_APPROX(m3, m1*m2);
} }
template<typename MatrixType> void qr_invertible() template<typename MatrixType> void qr_invertible()
@ -57,6 +54,8 @@ template<typename MatrixType> void qr_invertible()
typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar; typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar;
typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::Scalar Scalar;
STATIC_CHECK(( internal::is_same<typename HouseholderQR<MatrixType>::StorageIndex,int>::value ));
int size = internal::random<int>(10,50); int size = internal::random<int>(10,50);
MatrixType m1(size, size), m2(size, size), m3(size, size); MatrixType m1(size, size), m2(size, size), m3(size, size);
@ -70,9 +69,8 @@ template<typename MatrixType> void qr_invertible()
} }
HouseholderQR<MatrixType> qr(m1); HouseholderQR<MatrixType> qr(m1);
m3 = MatrixType::Random(size,size);
m2 = qr.solve(m3); check_solverbase<MatrixType, MatrixType>(m1, qr, size, size, size);
VERIFY_IS_APPROX(m3, m1*m2);
// now construct a matrix with prescribed determinant // now construct a matrix with prescribed determinant
m1.setZero(); m1.setZero();
@ -95,6 +93,8 @@ template<typename MatrixType> void qr_verify_assert()
HouseholderQR<MatrixType> qr; HouseholderQR<MatrixType> qr;
VERIFY_RAISES_ASSERT(qr.matrixQR()) VERIFY_RAISES_ASSERT(qr.matrixQR())
VERIFY_RAISES_ASSERT(qr.solve(tmp)) VERIFY_RAISES_ASSERT(qr.solve(tmp))
VERIFY_RAISES_ASSERT(qr.transpose().solve(tmp))
VERIFY_RAISES_ASSERT(qr.adjoint().solve(tmp))
VERIFY_RAISES_ASSERT(qr.householderQ()) VERIFY_RAISES_ASSERT(qr.householderQ())
VERIFY_RAISES_ASSERT(qr.absDeterminant()) VERIFY_RAISES_ASSERT(qr.absDeterminant())
VERIFY_RAISES_ASSERT(qr.logAbsDeterminant()) VERIFY_RAISES_ASSERT(qr.logAbsDeterminant())

View File

@ -11,9 +11,12 @@
#include "main.h" #include "main.h"
#include <Eigen/QR> #include <Eigen/QR>
#include <Eigen/SVD> #include <Eigen/SVD>
#include "solverbase.h"
template <typename MatrixType> template <typename MatrixType>
void cod() { void cod() {
STATIC_CHECK(( internal::is_same<typename CompleteOrthogonalDecomposition<MatrixType>::StorageIndex,int>::value ));
Index rows = internal::random<Index>(2, EIGEN_TEST_MAX_SIZE); Index rows = internal::random<Index>(2, EIGEN_TEST_MAX_SIZE);
Index cols = internal::random<Index>(2, EIGEN_TEST_MAX_SIZE); Index cols = internal::random<Index>(2, EIGEN_TEST_MAX_SIZE);
Index cols2 = internal::random<Index>(2, EIGEN_TEST_MAX_SIZE); Index cols2 = internal::random<Index>(2, EIGEN_TEST_MAX_SIZE);
@ -46,12 +49,12 @@ void cod() {
MatrixType c = q * t * z * cod.colsPermutation().inverse(); MatrixType c = q * t * z * cod.colsPermutation().inverse();
VERIFY_IS_APPROX(matrix, c); VERIFY_IS_APPROX(matrix, c);
check_solverbase<MatrixType, MatrixType>(matrix, cod, rows, cols, cols2);
// Verify that we get the same minimum-norm solution as the SVD.
MatrixType exact_solution = MatrixType::Random(cols, cols2); MatrixType exact_solution = MatrixType::Random(cols, cols2);
MatrixType rhs = matrix * exact_solution; MatrixType rhs = matrix * exact_solution;
MatrixType cod_solution = cod.solve(rhs); MatrixType cod_solution = cod.solve(rhs);
VERIFY_IS_APPROX(rhs, matrix * cod_solution);
// Verify that we get the same minimum-norm solution as the SVD.
JacobiSVD<MatrixType> svd(matrix, ComputeThinU | ComputeThinV); JacobiSVD<MatrixType> svd(matrix, ComputeThinU | ComputeThinV);
MatrixType svd_solution = svd.solve(rhs); MatrixType svd_solution = svd.solve(rhs);
VERIFY_IS_APPROX(cod_solution, svd_solution); VERIFY_IS_APPROX(cod_solution, svd_solution);
@ -77,13 +80,13 @@ void cod_fixedsize() {
VERIFY(cod.isSurjective() == (rank == Cols)); VERIFY(cod.isSurjective() == (rank == Cols));
VERIFY(cod.isInvertible() == (cod.isInjective() && cod.isSurjective())); VERIFY(cod.isInvertible() == (cod.isInjective() && cod.isSurjective()));
check_solverbase<Matrix<Scalar, Cols, Cols2>, Matrix<Scalar, Rows, Cols2> >(matrix, cod, Rows, Cols, Cols2);
// Verify that we get the same minimum-norm solution as the SVD.
Matrix<Scalar, Cols, Cols2> exact_solution; Matrix<Scalar, Cols, Cols2> exact_solution;
exact_solution.setRandom(Cols, Cols2); exact_solution.setRandom(Cols, Cols2);
Matrix<Scalar, Rows, Cols2> rhs = matrix * exact_solution; Matrix<Scalar, Rows, Cols2> rhs = matrix * exact_solution;
Matrix<Scalar, Cols, Cols2> cod_solution = cod.solve(rhs); Matrix<Scalar, Cols, Cols2> cod_solution = cod.solve(rhs);
VERIFY_IS_APPROX(rhs, matrix * cod_solution);
// Verify that we get the same minimum-norm solution as the SVD.
JacobiSVD<MatrixType> svd(matrix, ComputeFullU | ComputeFullV); JacobiSVD<MatrixType> svd(matrix, ComputeFullU | ComputeFullV);
Matrix<Scalar, Cols, Cols2> svd_solution = svd.solve(rhs); Matrix<Scalar, Cols, Cols2> svd_solution = svd.solve(rhs);
VERIFY_IS_APPROX(cod_solution, svd_solution); VERIFY_IS_APPROX(cod_solution, svd_solution);
@ -93,6 +96,8 @@ template<typename MatrixType> void qr()
{ {
using std::sqrt; using std::sqrt;
STATIC_CHECK(( internal::is_same<typename ColPivHouseholderQR<MatrixType>::StorageIndex,int>::value ));
Index rows = internal::random<Index>(2,EIGEN_TEST_MAX_SIZE), cols = internal::random<Index>(2,EIGEN_TEST_MAX_SIZE), cols2 = internal::random<Index>(2,EIGEN_TEST_MAX_SIZE); Index rows = internal::random<Index>(2,EIGEN_TEST_MAX_SIZE), cols = internal::random<Index>(2,EIGEN_TEST_MAX_SIZE), cols2 = internal::random<Index>(2,EIGEN_TEST_MAX_SIZE);
Index rank = internal::random<Index>(1, (std::min)(rows, cols)-1); Index rank = internal::random<Index>(1, (std::min)(rows, cols)-1);
@ -133,13 +138,10 @@ template<typename MatrixType> void qr()
VERIFY_IS_APPROX_OR_LESS_THAN(y, x); VERIFY_IS_APPROX_OR_LESS_THAN(y, x);
} }
MatrixType m2 = MatrixType::Random(cols,cols2); check_solverbase<MatrixType, MatrixType>(m1, qr, rows, cols, cols2);
MatrixType m3 = m1*m2;
m2 = MatrixType::Random(cols,cols2);
m2 = qr.solve(m3);
VERIFY_IS_APPROX(m3, m1*m2);
{ {
MatrixType m2, m3;
Index size = rows; Index size = rows;
do { do {
m1 = MatrixType::Random(size,size); m1 = MatrixType::Random(size,size);
@ -173,11 +175,8 @@ template<typename MatrixType, int Cols2> void qr_fixedsize()
Matrix<Scalar,Rows,Cols> c = qr.householderQ() * r * qr.colsPermutation().inverse(); Matrix<Scalar,Rows,Cols> c = qr.householderQ() * r * qr.colsPermutation().inverse();
VERIFY_IS_APPROX(m1, c); VERIFY_IS_APPROX(m1, c);
Matrix<Scalar,Cols,Cols2> m2 = Matrix<Scalar,Cols,Cols2>::Random(Cols,Cols2); check_solverbase<Matrix<Scalar,Cols,Cols2>, Matrix<Scalar,Rows,Cols2> >(m1, qr, Rows, Cols, Cols2);
Matrix<Scalar,Rows,Cols2> m3 = m1*m2;
m2 = Matrix<Scalar,Cols,Cols2>::Random(Cols,Cols2);
m2 = qr.solve(m3);
VERIFY_IS_APPROX(m3, m1*m2);
// Verify that the absolute value of the diagonal elements in R are // Verify that the absolute value of the diagonal elements in R are
// non-increasing until they reache the singularity threshold. // non-increasing until they reache the singularity threshold.
RealScalar threshold = RealScalar threshold =
@ -264,9 +263,8 @@ template<typename MatrixType> void qr_invertible()
} }
ColPivHouseholderQR<MatrixType> qr(m1); ColPivHouseholderQR<MatrixType> qr(m1);
m3 = MatrixType::Random(size,size);
m2 = qr.solve(m3); check_solverbase<MatrixType, MatrixType>(m1, qr, size, size, size);
//VERIFY_IS_APPROX(m3, m1*m2);
// now construct a matrix with prescribed determinant // now construct a matrix with prescribed determinant
m1.setZero(); m1.setZero();
@ -286,6 +284,8 @@ template<typename MatrixType> void qr_verify_assert()
ColPivHouseholderQR<MatrixType> qr; ColPivHouseholderQR<MatrixType> qr;
VERIFY_RAISES_ASSERT(qr.matrixQR()) VERIFY_RAISES_ASSERT(qr.matrixQR())
VERIFY_RAISES_ASSERT(qr.solve(tmp)) VERIFY_RAISES_ASSERT(qr.solve(tmp))
VERIFY_RAISES_ASSERT(qr.transpose().solve(tmp))
VERIFY_RAISES_ASSERT(qr.adjoint().solve(tmp))
VERIFY_RAISES_ASSERT(qr.householderQ()) VERIFY_RAISES_ASSERT(qr.householderQ())
VERIFY_RAISES_ASSERT(qr.dimensionOfKernel()) VERIFY_RAISES_ASSERT(qr.dimensionOfKernel())
VERIFY_RAISES_ASSERT(qr.isInjective()) VERIFY_RAISES_ASSERT(qr.isInjective())
@ -296,6 +296,25 @@ template<typename MatrixType> void qr_verify_assert()
VERIFY_RAISES_ASSERT(qr.logAbsDeterminant()) VERIFY_RAISES_ASSERT(qr.logAbsDeterminant())
} }
template<typename MatrixType> void cod_verify_assert()
{
MatrixType tmp;
CompleteOrthogonalDecomposition<MatrixType> cod;
VERIFY_RAISES_ASSERT(cod.matrixQTZ())
VERIFY_RAISES_ASSERT(cod.solve(tmp))
VERIFY_RAISES_ASSERT(cod.transpose().solve(tmp))
VERIFY_RAISES_ASSERT(cod.adjoint().solve(tmp))
VERIFY_RAISES_ASSERT(cod.householderQ())
VERIFY_RAISES_ASSERT(cod.dimensionOfKernel())
VERIFY_RAISES_ASSERT(cod.isInjective())
VERIFY_RAISES_ASSERT(cod.isSurjective())
VERIFY_RAISES_ASSERT(cod.isInvertible())
VERIFY_RAISES_ASSERT(cod.pseudoInverse())
VERIFY_RAISES_ASSERT(cod.absDeterminant())
VERIFY_RAISES_ASSERT(cod.logAbsDeterminant())
}
EIGEN_DECLARE_TEST(qr_colpivoting) EIGEN_DECLARE_TEST(qr_colpivoting)
{ {
for(int i = 0; i < g_repeat; i++) { for(int i = 0; i < g_repeat; i++) {
@ -330,6 +349,13 @@ EIGEN_DECLARE_TEST(qr_colpivoting)
CALL_SUBTEST_6(qr_verify_assert<MatrixXcf>()); CALL_SUBTEST_6(qr_verify_assert<MatrixXcf>());
CALL_SUBTEST_3(qr_verify_assert<MatrixXcd>()); CALL_SUBTEST_3(qr_verify_assert<MatrixXcd>());
CALL_SUBTEST_7(cod_verify_assert<Matrix3f>());
CALL_SUBTEST_8(cod_verify_assert<Matrix3d>());
CALL_SUBTEST_1(cod_verify_assert<MatrixXf>());
CALL_SUBTEST_2(cod_verify_assert<MatrixXd>());
CALL_SUBTEST_6(cod_verify_assert<MatrixXcf>());
CALL_SUBTEST_3(cod_verify_assert<MatrixXcd>());
// Test problem size constructors // Test problem size constructors
CALL_SUBTEST_9(ColPivHouseholderQR<MatrixXf>(10, 20)); CALL_SUBTEST_9(ColPivHouseholderQR<MatrixXf>(10, 20));

View File

@ -10,9 +10,12 @@
#include "main.h" #include "main.h"
#include <Eigen/QR> #include <Eigen/QR>
#include "solverbase.h"
template<typename MatrixType> void qr() template<typename MatrixType> void qr()
{ {
STATIC_CHECK(( internal::is_same<typename FullPivHouseholderQR<MatrixType>::StorageIndex,int>::value ));
static const int Rows = MatrixType::RowsAtCompileTime, Cols = MatrixType::ColsAtCompileTime; static const int Rows = MatrixType::RowsAtCompileTime, Cols = MatrixType::ColsAtCompileTime;
Index max_size = EIGEN_TEST_MAX_SIZE; Index max_size = EIGEN_TEST_MAX_SIZE;
Index min_size = numext::maxi(1,EIGEN_TEST_MAX_SIZE/10); Index min_size = numext::maxi(1,EIGEN_TEST_MAX_SIZE/10);
@ -48,13 +51,10 @@ template<typename MatrixType> void qr()
MatrixType tmp; MatrixType tmp;
VERIFY_IS_APPROX(tmp.noalias() = qr.matrixQ() * r, (qr.matrixQ() * r).eval()); VERIFY_IS_APPROX(tmp.noalias() = qr.matrixQ() * r, (qr.matrixQ() * r).eval());
MatrixType m2 = MatrixType::Random(cols,cols2); check_solverbase<MatrixType, MatrixType>(m1, qr, rows, cols, cols2);
MatrixType m3 = m1*m2;
m2 = MatrixType::Random(cols,cols2);
m2 = qr.solve(m3);
VERIFY_IS_APPROX(m3, m1*m2);
{ {
MatrixType m2, m3;
Index size = rows; Index size = rows;
do { do {
m1 = MatrixType::Random(size,size); m1 = MatrixType::Random(size,size);
@ -93,9 +93,7 @@ template<typename MatrixType> void qr_invertible()
VERIFY(qr.isInvertible()); VERIFY(qr.isInvertible());
VERIFY(qr.isSurjective()); VERIFY(qr.isSurjective());
m3 = MatrixType::Random(size,size); check_solverbase<MatrixType, MatrixType>(m1, qr, size, size, size);
m2 = qr.solve(m3);
VERIFY_IS_APPROX(m3, m1*m2);
// now construct a matrix with prescribed determinant // now construct a matrix with prescribed determinant
m1.setZero(); m1.setZero();
@ -115,6 +113,8 @@ template<typename MatrixType> void qr_verify_assert()
FullPivHouseholderQR<MatrixType> qr; FullPivHouseholderQR<MatrixType> qr;
VERIFY_RAISES_ASSERT(qr.matrixQR()) VERIFY_RAISES_ASSERT(qr.matrixQR())
VERIFY_RAISES_ASSERT(qr.solve(tmp)) VERIFY_RAISES_ASSERT(qr.solve(tmp))
VERIFY_RAISES_ASSERT(qr.transpose().solve(tmp))
VERIFY_RAISES_ASSERT(qr.adjoint().solve(tmp))
VERIFY_RAISES_ASSERT(qr.matrixQ()) VERIFY_RAISES_ASSERT(qr.matrixQ())
VERIFY_RAISES_ASSERT(qr.dimensionOfKernel()) VERIFY_RAISES_ASSERT(qr.dimensionOfKernel())
VERIFY_RAISES_ASSERT(qr.isInjective()) VERIFY_RAISES_ASSERT(qr.isInjective())

36
test/solverbase.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef TEST_SOLVERBASE_H
#define TEST_SOLVERBASE_H
template<typename DstType, typename RhsType, typename MatrixType, typename SolverType>
void check_solverbase(const MatrixType& matrix, const SolverType& solver, Index rows, Index cols, Index cols2)
{
// solve
DstType m2 = DstType::Random(cols,cols2);
RhsType m3 = matrix*m2;
DstType solver_solution = DstType::Random(cols,cols2);
solver._solve_impl(m3, solver_solution);
VERIFY_IS_APPROX(m3, matrix*solver_solution);
solver_solution = DstType::Random(cols,cols2);
solver_solution = solver.solve(m3);
VERIFY_IS_APPROX(m3, matrix*solver_solution);
// test solve with transposed
m3 = RhsType::Random(rows,cols2);
m2 = matrix.transpose()*m3;
RhsType solver_solution2 = RhsType::Random(rows,cols2);
solver.template _solve_impl_transposed<false>(m2, solver_solution2);
VERIFY_IS_APPROX(m2, matrix.transpose()*solver_solution2);
solver_solution2 = RhsType::Random(rows,cols2);
solver_solution2 = solver.transpose().solve(m2);
VERIFY_IS_APPROX(m2, matrix.transpose()*solver_solution2);
// test solve with conjugate transposed
m3 = RhsType::Random(rows,cols2);
m2 = matrix.adjoint()*m3;
solver_solution2 = RhsType::Random(rows,cols2);
solver.template _solve_impl_transposed<true>(m2, solver_solution2);
VERIFY_IS_APPROX(m2, matrix.adjoint()*solver_solution2);
solver_solution2 = RhsType::Random(rows,cols2);
solver_solution2 = solver.adjoint().solve(m2);
VERIFY_IS_APPROX(m2, matrix.adjoint()*solver_solution2);
}
#endif // TEST_SOLVERBASE_H

View File

@ -17,6 +17,7 @@
#endif #endif
#include "svd_fill.h" #include "svd_fill.h"
#include "solverbase.h"
// Check that the matrix m is properly reconstructed and that the U and V factors are unitary // Check that the matrix m is properly reconstructed and that the U and V factors are unitary
// The SVD must have already been computed. // The SVD must have already been computed.
@ -219,12 +220,33 @@ void svd_min_norm(const MatrixType& m, unsigned int computationOptions)
VERIFY_IS_APPROX(x21, x3); VERIFY_IS_APPROX(x21, x3);
} }
template<typename MatrixType, typename SolverType>
void svd_test_solvers(const MatrixType& m, const SolverType& solver) {
Index rows, cols, cols2;
rows = m.rows();
cols = m.cols();
if(MatrixType::ColsAtCompileTime==Dynamic)
{
cols2 = internal::random<int>(2,EIGEN_TEST_MAX_SIZE);
}
else
{
cols2 = cols;
}
typedef Matrix<typename MatrixType::Scalar, MatrixType::ColsAtCompileTime, MatrixType::ColsAtCompileTime> CMatrixType;
check_solverbase<CMatrixType, MatrixType>(m, solver, rows, cols, cols2);
}
// Check full, compare_to_full, least_square, and min_norm for all possible compute-options // Check full, compare_to_full, least_square, and min_norm for all possible compute-options
template<typename SvdType, typename MatrixType> template<typename SvdType, typename MatrixType>
void svd_test_all_computation_options(const MatrixType& m, bool full_only) void svd_test_all_computation_options(const MatrixType& m, bool full_only)
{ {
// if (QRPreconditioner == NoQRPreconditioner && m.rows() != m.cols()) // if (QRPreconditioner == NoQRPreconditioner && m.rows() != m.cols())
// return; // return;
STATIC_CHECK(( internal::is_same<typename SvdType::StorageIndex,int>::value ));
SvdType fullSvd(m, ComputeFullU|ComputeFullV); SvdType fullSvd(m, ComputeFullU|ComputeFullV);
CALL_SUBTEST(( svd_check_full(m, fullSvd) )); CALL_SUBTEST(( svd_check_full(m, fullSvd) ));
CALL_SUBTEST(( svd_least_square<SvdType>(m, ComputeFullU | ComputeFullV) )); CALL_SUBTEST(( svd_least_square<SvdType>(m, ComputeFullU | ComputeFullV) ));
@ -234,6 +256,9 @@ void svd_test_all_computation_options(const MatrixType& m, bool full_only)
// remark #111: statement is unreachable // remark #111: statement is unreachable
#pragma warning disable 111 #pragma warning disable 111
#endif #endif
svd_test_solvers(m, fullSvd);
if(full_only) if(full_only)
return; return;
@ -448,6 +473,8 @@ void svd_verify_assert(const MatrixType& m)
VERIFY_RAISES_ASSERT(svd.singularValues()) VERIFY_RAISES_ASSERT(svd.singularValues())
VERIFY_RAISES_ASSERT(svd.matrixV()) VERIFY_RAISES_ASSERT(svd.matrixV())
VERIFY_RAISES_ASSERT(svd.solve(rhs)) VERIFY_RAISES_ASSERT(svd.solve(rhs))
VERIFY_RAISES_ASSERT(svd.transpose().solve(rhs))
VERIFY_RAISES_ASSERT(svd.adjoint().solve(rhs))
MatrixType a = MatrixType::Zero(rows, cols); MatrixType a = MatrixType::Zero(rows, cols);
a.setZero(); a.setZero();
svd.compute(a, 0); svd.compute(a, 0);