Merged latest updates from the Eigen trunk.

This commit is contained in:
Benoit Steiner 2014-09-15 09:18:16 -07:00
commit 10a79ca3a3
66 changed files with 882 additions and 1343 deletions

View File

@ -42,7 +42,7 @@
#define EIGEN_USING_STD_MATH(FUNC) using std::FUNC; #define EIGEN_USING_STD_MATH(FUNC) using std::FUNC;
#endif #endif
#if (defined(_CPPUNWIND) || defined(__EXCEPTIONS)) && !defined(__CUDA_ARCH__) #if (defined(_CPPUNWIND) || defined(__EXCEPTIONS)) && !defined(__CUDA_ARCH__) && !defined(EIGEN_EXCEPTIONS)
#define EIGEN_EXCEPTIONS #define EIGEN_EXCEPTIONS
#endif #endif

View File

@ -21,6 +21,7 @@
*/ */
#include "src/misc/Solve.h" #include "src/misc/Solve.h"
#include "src/SVD/SVDBase.h"
#include "src/SVD/JacobiSVD.h" #include "src/SVD/JacobiSVD.h"
#if defined(EIGEN_USE_LAPACKE) && !defined(EIGEN_USE_LAPACKE_STRICT) #if defined(EIGEN_USE_LAPACKE) && !defined(EIGEN_USE_LAPACKE_STRICT)
#include "src/SVD/JacobiSVD_MKL.h" #include "src/SVD/JacobiSVD_MKL.h"

View File

@ -29,6 +29,11 @@ struct traits<ArrayWrapper<ExpressionType> >
: public traits<typename remove_all<typename ExpressionType::Nested>::type > : public traits<typename remove_all<typename ExpressionType::Nested>::type >
{ {
typedef ArrayXpr XprKind; typedef ArrayXpr XprKind;
// Let's remove NestByRefBit
enum {
Flags0 = traits<typename remove_all<typename ExpressionType::Nested>::type >::Flags,
Flags = Flags0 & ~NestByRefBit
};
}; };
} }
@ -166,6 +171,11 @@ struct traits<MatrixWrapper<ExpressionType> >
: public traits<typename remove_all<typename ExpressionType::Nested>::type > : public traits<typename remove_all<typename ExpressionType::Nested>::type >
{ {
typedef MatrixXpr XprKind; typedef MatrixXpr XprKind;
// Let's remove NestByRefBit
enum {
Flags0 = traits<typename remove_all<typename ExpressionType::Nested>::type >::Flags,
Flags = Flags0 & ~NestByRefBit
};
}; };
} }

View File

@ -231,7 +231,7 @@ EIGEN_DONT_INLINE void outer_product_selector_run(const ProductType& prod, Dest&
// FIXME not very good if rhs is real and lhs complex while alpha is real too // FIXME not very good if rhs is real and lhs complex while alpha is real too
const Index cols = dest.cols(); const Index cols = dest.cols();
for (Index j=0; j<cols; ++j) for (Index j=0; j<cols; ++j)
func(dest.col(j), prod.rhs().coeff(j) * prod.lhs()); func(dest.col(j), prod.rhs().coeff(0,j) * prod.lhs());
} }
// Row major // Row major
@ -242,7 +242,7 @@ EIGEN_DONT_INLINE void outer_product_selector_run(const ProductType& prod, Dest&
// FIXME not very good if lhs is real and rhs complex while alpha is real too // FIXME not very good if lhs is real and rhs complex while alpha is real too
const Index rows = dest.rows(); const Index rows = dest.rows();
for (Index i=0; i<rows; ++i) for (Index i=0; i<rows; ++i)
func(dest.row(i), prod.lhs().coeff(i) * prod.rhs()); func(dest.row(i), prod.lhs().coeff(i,0) * prod.rhs());
} }
template<typename Lhs, typename Rhs> template<typename Lhs, typename Rhs>

View File

@ -12,6 +12,15 @@
namespace Eigen { namespace Eigen {
// On WINCE, std::abs is defined for int only, so let's defined our own overloads:
// This issue has been confirmed with MSVC 2008 only, but the issue might exist for more recent versions too.
#if defined(_WIN32_WCE) && defined(_MSC_VER) && _MSC_VER<=1500
long abs(long x) { return (labs(x)); }
double abs(double x) { return (fabs(x)); }
float abs(float x) { return (fabsf(x)); }
long double abs(long double x) { return (fabsl(x)); }
#endif
namespace internal { namespace internal {
/** \internal \struct global_math_functions_filtering_base /** \internal \struct global_math_functions_filtering_base
@ -308,10 +317,17 @@ struct hypot_impl
using std::sqrt; using std::sqrt;
RealScalar _x = abs(x); RealScalar _x = abs(x);
RealScalar _y = abs(y); RealScalar _y = abs(y);
RealScalar p = (max)(_x, _y); Scalar p, qp;
if(p==RealScalar(0)) return 0; if(_x>_y)
RealScalar q = (min)(_x, _y); {
RealScalar qp = q/p; p = _x;
qp = _y / p;
}
else
{
p = _y;
qp = _x / p;
}
return p * sqrt(RealScalar(1) + qp*qp); return p * sqrt(RealScalar(1) + qp*qp);
} }
}; };

View File

@ -81,6 +81,7 @@ template<typename Derived> class MatrixBase
using Base::operator-=; using Base::operator-=;
using Base::operator*=; using Base::operator*=;
using Base::operator/=; using Base::operator/=;
using Base::operator*;
typedef typename Base::CoeffReturnType CoeffReturnType; typedef typename Base::CoeffReturnType CoeffReturnType;
typedef typename Base::ConstTransposeReturnType ConstTransposeReturnType; typedef typename Base::ConstTransposeReturnType ConstTransposeReturnType;

View File

@ -207,7 +207,7 @@ struct redux_impl<Func, Derived, LinearVectorizedTraversal, NoUnrolling>
const Index packetSize = packet_traits<Scalar>::size; const Index packetSize = packet_traits<Scalar>::size;
const Index alignedStart = internal::first_aligned(mat); const Index alignedStart = internal::first_aligned(mat);
enum { enum {
alignment = bool(Derived::Flags & DirectAccessBit) || bool(Derived::Flags & AlignedBit) alignment = (bool(Derived::Flags & DirectAccessBit) && bool(packet_traits<Scalar>::AlignedOnScalar)) || bool(Derived::Flags & AlignedBit)
? Aligned : Unaligned ? Aligned : Unaligned
}; };
const Index alignedSize2 = ((size-alignedStart)/(2*packetSize))*(2*packetSize); const Index alignedSize2 = ((size-alignedStart)/(2*packetSize))*(2*packetSize);

View File

@ -209,15 +209,9 @@ inline Derived& ArrayBase<Derived>::operator-=(const Scalar& other)
template<typename Derived> template<typename Derived>
inline Derived& DenseBase<Derived>::operator/=(const Scalar& other) inline Derived& DenseBase<Derived>::operator/=(const Scalar& other)
{ {
typedef typename internal::conditional<NumTraits<Scalar>::IsInteger,
internal::scalar_quotient_op<Scalar>,
internal::scalar_product_op<Scalar> >::type BinOp;
typedef typename Derived::PlainObject PlainObject; typedef typename Derived::PlainObject PlainObject;
SelfCwiseBinaryOp<BinOp, Derived, typename PlainObject::ConstantReturnType> tmp(derived()); SelfCwiseBinaryOp<internal::scalar_quotient_op<Scalar>, Derived, typename PlainObject::ConstantReturnType> tmp(derived());
Scalar actual_other; tmp = PlainObject::Constant(rows(),cols(), other);
if(NumTraits<Scalar>::IsInteger) actual_other = other;
else actual_other = Scalar(1)/other;
tmp = PlainObject::Constant(rows(),cols(), actual_other);
return derived(); return derived();
} }

View File

@ -20,7 +20,7 @@ inline void stable_norm_kernel(const ExpressionType& bl, Scalar& ssq, Scalar& sc
using std::max; using std::max;
Scalar maxCoeff = bl.cwiseAbs().maxCoeff(); Scalar maxCoeff = bl.cwiseAbs().maxCoeff();
if (maxCoeff>scale) if(maxCoeff>scale)
{ {
ssq = ssq * numext::abs2(scale/maxCoeff); ssq = ssq * numext::abs2(scale/maxCoeff);
Scalar tmp = Scalar(1)/maxCoeff; Scalar tmp = Scalar(1)/maxCoeff;
@ -29,12 +29,21 @@ inline void stable_norm_kernel(const ExpressionType& bl, Scalar& ssq, Scalar& sc
invScale = NumTraits<Scalar>::highest(); invScale = NumTraits<Scalar>::highest();
scale = Scalar(1)/invScale; scale = Scalar(1)/invScale;
} }
else if(maxCoeff>NumTraits<Scalar>::highest()) // we got a INF
{
invScale = Scalar(1);
scale = maxCoeff;
}
else else
{ {
scale = maxCoeff; scale = maxCoeff;
invScale = tmp; invScale = tmp;
} }
} }
else if(maxCoeff!=maxCoeff) // we got a NaN
{
scale = maxCoeff;
}
// TODO if the maxCoeff is much much smaller than the current scale, // TODO if the maxCoeff is much much smaller than the current scale,
// then we can neglect this sub vector // then we can neglect this sub vector
@ -55,7 +64,7 @@ blueNorm_impl(const EigenBase<Derived>& _vec)
using std::abs; using std::abs;
const Derived& vec(_vec.derived()); const Derived& vec(_vec.derived());
static bool initialized = false; static bool initialized = false;
static RealScalar b1, b2, s1m, s2m, overfl, rbig, relerr; static RealScalar b1, b2, s1m, s2m, rbig, relerr;
if(!initialized) if(!initialized)
{ {
int ibeta, it, iemin, iemax, iexp; int ibeta, it, iemin, iemax, iexp;
@ -84,7 +93,6 @@ blueNorm_impl(const EigenBase<Derived>& _vec)
iexp = - ((iemax+it)/2); iexp = - ((iemax+it)/2);
s2m = RealScalar(pow(RealScalar(ibeta),RealScalar(iexp))); // scaling factor for upper range s2m = RealScalar(pow(RealScalar(ibeta),RealScalar(iexp))); // scaling factor for upper range
overfl = rbig*s2m; // overflow boundary for abig
eps = RealScalar(pow(double(ibeta), 1-it)); eps = RealScalar(pow(double(ibeta), 1-it));
relerr = sqrt(eps); // tolerance for neglecting asml relerr = sqrt(eps); // tolerance for neglecting asml
initialized = true; initialized = true;
@ -101,13 +109,13 @@ blueNorm_impl(const EigenBase<Derived>& _vec)
else if(ax < b1) asml += numext::abs2(ax*s1m); else if(ax < b1) asml += numext::abs2(ax*s1m);
else amed += numext::abs2(ax); else amed += numext::abs2(ax);
} }
if(amed!=amed)
return amed; // we got a NaN
if(abig > RealScalar(0)) if(abig > RealScalar(0))
{ {
abig = sqrt(abig); abig = sqrt(abig);
if(abig > overfl) if(abig > rbig) // overflow, or *this contains INF values
{ return abig; // return INF
return rbig;
}
if(amed > RealScalar(0)) if(amed > RealScalar(0))
{ {
abig = abig/s2m; abig = abig/s2m;

View File

@ -143,7 +143,7 @@ template<> EIGEN_STRONG_INLINE Packet8f pmadd(const Packet8f& a, const Packet8f&
// so let's enforce it to generate a vfmadd231ps instruction since the most common use case is to accumulate // so let's enforce it to generate a vfmadd231ps instruction since the most common use case is to accumulate
// the result of the product. // the result of the product.
Packet8f res = c; Packet8f res = c;
asm("vfmadd231ps %[a], %[b], %[c]" : [c] "+x" (res) : [a] "x" (a), [b] "x" (b)); __asm__("vfmadd231ps %[a], %[b], %[c]" : [c] "+x" (res) : [a] "x" (a), [b] "x" (b));
return res; return res;
#else #else
return _mm256_fmadd_ps(a,b,c); return _mm256_fmadd_ps(a,b,c);
@ -153,7 +153,7 @@ template<> EIGEN_STRONG_INLINE Packet4d pmadd(const Packet4d& a, const Packet4d&
#if defined(__clang__) || defined(__GNUC__) #if defined(__clang__) || defined(__GNUC__)
// see above // see above
Packet4d res = c; Packet4d res = c;
asm("vfmadd231pd %[a], %[b], %[c]" : [c] "+x" (res) : [a] "x" (a), [b] "x" (b)); __asm__("vfmadd231pd %[a], %[b], %[c]" : [c] "+x" (res) : [a] "x" (a), [b] "x" (b));
return res; return res;
#else #else
return _mm256_fmadd_pd(a,b,c); return _mm256_fmadd_pd(a,b,c);

View File

@ -52,12 +52,12 @@ typedef uint32x4_t Packet4ui;
// arm64 does have the pld instruction. If available, let's trust the __builtin_prefetch built-in function // arm64 does have the pld instruction. If available, let's trust the __builtin_prefetch built-in function
// which available on LLVM and GCC (at least) // which available on LLVM and GCC (at least)
#if (defined(__has_builtin) && __has_builtin(__builtin_prefetch)) || defined(__GNUC__) #if EIGEN_HAS_BUILTIN(__builtin_prefetch) || defined(__GNUC__)
#define EIGEN_ARM_PREFETCH(ADDR) __builtin_prefetch(ADDR); #define EIGEN_ARM_PREFETCH(ADDR) __builtin_prefetch(ADDR);
#elif defined __pld #elif defined __pld
#define EIGEN_ARM_PREFETCH(ADDR) __pld(ADDR) #define EIGEN_ARM_PREFETCH(ADDR) __pld(ADDR)
#elif !defined(__aarch64__) #elif !defined(__aarch64__)
#define EIGEN_ARM_PREFETCH(ADDR) asm volatile ( " pld [%[addr]]\n" :: [addr] "r" (ADDR) : "cc" ); #define EIGEN_ARM_PREFETCH(ADDR) __asm__ __volatile__ ( " pld [%[addr]]\n" :: [addr] "r" (ADDR) : "cc" );
#else #else
// by default no explicit prefetching // by default no explicit prefetching
#define EIGEN_ARM_PREFETCH(ADDR) #define EIGEN_ARM_PREFETCH(ADDR)

View File

@ -167,9 +167,17 @@ template<typename Scalar> struct scalar_hypot_op {
EIGEN_USING_STD_MATH(max); EIGEN_USING_STD_MATH(max);
EIGEN_USING_STD_MATH(min); EIGEN_USING_STD_MATH(min);
using std::sqrt; using std::sqrt;
Scalar p = (max)(_x, _y); Scalar p, qp;
Scalar q = (min)(_x, _y); if(_x>_y)
Scalar qp = q/p; {
p = _x;
qp = _y / p;
}
else
{
p = _y;
qp = _x / p;
}
return p * sqrt(Scalar(1) + qp*qp); return p * sqrt(Scalar(1) + qp*qp);
} }
}; };

View File

@ -53,6 +53,8 @@ template< \
int RhsStorageOrder, bool ConjugateRhs> \ int RhsStorageOrder, bool ConjugateRhs> \
struct general_matrix_matrix_product<Index,EIGTYPE,LhsStorageOrder,ConjugateLhs,EIGTYPE,RhsStorageOrder,ConjugateRhs,ColMajor> \ struct general_matrix_matrix_product<Index,EIGTYPE,LhsStorageOrder,ConjugateLhs,EIGTYPE,RhsStorageOrder,ConjugateRhs,ColMajor> \
{ \ { \
typedef gebp_traits<EIGTYPE,EIGTYPE> Traits; \
\
static void run(Index rows, Index cols, Index depth, \ static void run(Index rows, Index cols, Index depth, \
const EIGTYPE* _lhs, Index lhsStride, \ const EIGTYPE* _lhs, Index lhsStride, \
const EIGTYPE* _rhs, Index rhsStride, \ const EIGTYPE* _rhs, Index rhsStride, \

View File

@ -109,7 +109,7 @@ struct product_triangular_matrix_matrix_trmm<EIGTYPE,Index,Mode,true, \
/* Non-square case - doesn't fit to MKL ?TRMM. Fall to default triangular product or call MKL ?GEMM*/ \ /* Non-square case - doesn't fit to MKL ?TRMM. Fall to default triangular product or call MKL ?GEMM*/ \
if (rows != depth) { \ if (rows != depth) { \
\ \
int nthr = mkl_domain_get_max_threads(MKL_BLAS); \ int nthr = mkl_domain_get_max_threads(EIGEN_MKL_DOMAIN_BLAS); \
\ \
if (((nthr==1) && (((std::max)(rows,depth)-diagSize)/(double)diagSize < 0.5))) { \ if (((nthr==1) && (((std::max)(rows,depth)-diagSize)/(double)diagSize < 0.5))) { \
/* Most likely no benefit to call TRMM or GEMM from MKL*/ \ /* Most likely no benefit to call TRMM or GEMM from MKL*/ \
@ -223,7 +223,7 @@ struct product_triangular_matrix_matrix_trmm<EIGTYPE,Index,Mode,false, \
/* Non-square case - doesn't fit to MKL ?TRMM. Fall to default triangular product or call MKL ?GEMM*/ \ /* Non-square case - doesn't fit to MKL ?TRMM. Fall to default triangular product or call MKL ?GEMM*/ \
if (cols != depth) { \ if (cols != depth) { \
\ \
int nthr = mkl_domain_get_max_threads(MKL_BLAS); \ int nthr = mkl_domain_get_max_threads(EIGEN_MKL_DOMAIN_BLAS); \
\ \
if ((nthr==1) && (((std::max)(cols,depth)-diagSize)/(double)diagSize < 0.5)) { \ if ((nthr==1) && (((std::max)(cols,depth)-diagSize)/(double)diagSize < 0.5)) { \
/* Most likely no benefit to call TRMM or GEMM from MKL*/ \ /* Most likely no benefit to call TRMM or GEMM from MKL*/ \

View File

@ -76,6 +76,38 @@
#include <mkl_lapacke.h> #include <mkl_lapacke.h>
#define EIGEN_MKL_VML_THRESHOLD 128 #define EIGEN_MKL_VML_THRESHOLD 128
/* MKL_DOMAIN_BLAS, etc are defined only in 10.3 update 7 */
/* MKL_BLAS, etc are not defined in 11.2 */
#ifdef MKL_DOMAIN_ALL
#define EIGEN_MKL_DOMAIN_ALL MKL_DOMAIN_ALL
#else
#define EIGEN_MKL_DOMAIN_ALL MKL_ALL
#endif
#ifdef MKL_DOMAIN_BLAS
#define EIGEN_MKL_DOMAIN_BLAS MKL_DOMAIN_BLAS
#else
#define EIGEN_MKL_DOMAIN_BLAS MKL_BLAS
#endif
#ifdef MKL_DOMAIN_FFT
#define EIGEN_MKL_DOMAIN_FFT MKL_DOMAIN_FFT
#else
#define EIGEN_MKL_DOMAIN_FFT MKL_FFT
#endif
#ifdef MKL_DOMAIN_VML
#define EIGEN_MKL_DOMAIN_VML MKL_DOMAIN_VML
#else
#define EIGEN_MKL_DOMAIN_VML MKL_VML
#endif
#ifdef MKL_DOMAIN_PARDISO
#define EIGEN_MKL_DOMAIN_PARDISO MKL_DOMAIN_PARDISO
#else
#define EIGEN_MKL_DOMAIN_PARDISO MKL_PARDISO
#endif
namespace Eigen { namespace Eigen {
typedef std::complex<double> dcomplex; typedef std::complex<double> dcomplex;

View File

@ -107,6 +107,13 @@
#define EIGEN_DEFAULT_DENSE_INDEX_TYPE std::ptrdiff_t #define EIGEN_DEFAULT_DENSE_INDEX_TYPE std::ptrdiff_t
#endif #endif
// Cross compiler wrapper around LLVM's __has_builtin
#ifdef __has_builtin
# define EIGEN_HAS_BUILTIN(x) __has_builtin(x)
#else
# define EIGEN_HAS_BUILTIN(x) 0
#endif
// A Clang feature extension to determine compiler features. // A Clang feature extension to determine compiler features.
// We use it to determine 'cxx_rvalue_references' // We use it to determine 'cxx_rvalue_references'
#ifndef __has_feature #ifndef __has_feature
@ -277,7 +284,7 @@ namespace Eigen {
#if !defined(EIGEN_ASM_COMMENT) #if !defined(EIGEN_ASM_COMMENT)
#if (defined __GNUC__) && ( defined(__i386__) || defined(__x86_64__) ) #if (defined __GNUC__) && ( defined(__i386__) || defined(__x86_64__) )
#define EIGEN_ASM_COMMENT(X) asm("#" X) #define EIGEN_ASM_COMMENT(X) __asm__("#" X)
#else #else
#define EIGEN_ASM_COMMENT(X) #define EIGEN_ASM_COMMENT(X)
#endif #endif

View File

@ -64,7 +64,7 @@
// Currently, let's include it only on unix systems: // Currently, let's include it only on unix systems:
#if defined(__unix__) || defined(__unix) #if defined(__unix__) || defined(__unix)
#include <unistd.h> #include <unistd.h>
#if ((defined __QNXNTO__) || (defined _GNU_SOURCE) || ((defined _XOPEN_SOURCE) && (_XOPEN_SOURCE >= 600))) && (defined _POSIX_ADVISORY_INFO) && (_POSIX_ADVISORY_INFO > 0) #if ((defined __QNXNTO__) || (defined _GNU_SOURCE) || (defined __PGI) || ((defined _XOPEN_SOURCE) && (_XOPEN_SOURCE >= 600))) && (defined _POSIX_ADVISORY_INFO) && (_POSIX_ADVISORY_INFO > 0)
#define EIGEN_HAS_POSIX_MEMALIGN 1 #define EIGEN_HAS_POSIX_MEMALIGN 1
#endif #endif
#endif #endif

View File

@ -383,13 +383,21 @@ struct special_scalar_op_base<Derived,Scalar,OtherScalar,true> : public DenseCo
const CwiseUnaryOp<scalar_multiple2_op<Scalar,OtherScalar>, Derived> const CwiseUnaryOp<scalar_multiple2_op<Scalar,OtherScalar>, Derived>
operator*(const OtherScalar& scalar) const operator*(const OtherScalar& scalar) const
{ {
#ifdef EIGEN_SPECIAL_SCALAR_MULTIPLE_PLUGIN
EIGEN_SPECIAL_SCALAR_MULTIPLE_PLUGIN
#endif
return CwiseUnaryOp<scalar_multiple2_op<Scalar,OtherScalar>, Derived> return CwiseUnaryOp<scalar_multiple2_op<Scalar,OtherScalar>, Derived>
(*static_cast<const Derived*>(this), scalar_multiple2_op<Scalar,OtherScalar>(scalar)); (*static_cast<const Derived*>(this), scalar_multiple2_op<Scalar,OtherScalar>(scalar));
} }
inline friend const CwiseUnaryOp<scalar_multiple2_op<Scalar,OtherScalar>, Derived> inline friend const CwiseUnaryOp<scalar_multiple2_op<Scalar,OtherScalar>, Derived>
operator*(const OtherScalar& scalar, const Derived& matrix) operator*(const OtherScalar& scalar, const Derived& matrix)
{ return static_cast<const special_scalar_op_base&>(matrix).operator*(scalar); } {
#ifdef EIGEN_SPECIAL_SCALAR_MULTIPLE_PLUGIN
EIGEN_SPECIAL_SCALAR_MULTIPLE_PLUGIN
#endif
return static_cast<const special_scalar_op_base&>(matrix).operator*(scalar);
}
}; };
template<typename XprType, typename CastType> struct cast_return_type template<typename XprType, typename CastType> struct cast_return_type

View File

@ -605,7 +605,6 @@ template<typename SolverType> struct direct_selfadjoint_eigenvalues<SolverType,3
if(computeEigenvectors) if(computeEigenvectors)
{ {
Scalar safeNorm2 = Eigen::NumTraits<Scalar>::epsilon(); Scalar safeNorm2 = Eigen::NumTraits<Scalar>::epsilon();
safeNorm2 *= safeNorm2;
if((eivals(2)-eivals(0))<=Eigen::NumTraits<Scalar>::epsilon()) if((eivals(2)-eivals(0))<=Eigen::NumTraits<Scalar>::epsilon())
{ {
eivecs.setIdentity(); eivecs.setIdentity();
@ -619,7 +618,7 @@ template<typename SolverType> struct direct_selfadjoint_eigenvalues<SolverType,3
Scalar d0 = eivals(2) - eivals(1); Scalar d0 = eivals(2) - eivals(1);
Scalar d1 = eivals(1) - eivals(0); Scalar d1 = eivals(1) - eivals(0);
int k = d0 > d1 ? 2 : 0; int k = d0 > d1 ? 2 : 0;
d0 = d0 > d1 ? d1 : d0; d0 = d0 > d1 ? d0 : d1;
tmp.diagonal().array () -= eivals(k); tmp.diagonal().array () -= eivals(k);
VectorType cross; VectorType cross;
@ -627,19 +626,25 @@ template<typename SolverType> struct direct_selfadjoint_eigenvalues<SolverType,3
n = (cross = tmp.row(0).cross(tmp.row(1))).squaredNorm(); n = (cross = tmp.row(0).cross(tmp.row(1))).squaredNorm();
if(n>safeNorm2) if(n>safeNorm2)
{
eivecs.col(k) = cross / sqrt(n); eivecs.col(k) = cross / sqrt(n);
}
else else
{ {
n = (cross = tmp.row(0).cross(tmp.row(2))).squaredNorm(); n = (cross = tmp.row(0).cross(tmp.row(2))).squaredNorm();
if(n>safeNorm2) if(n>safeNorm2)
{
eivecs.col(k) = cross / sqrt(n); eivecs.col(k) = cross / sqrt(n);
}
else else
{ {
n = (cross = tmp.row(1).cross(tmp.row(2))).squaredNorm(); n = (cross = tmp.row(1).cross(tmp.row(2))).squaredNorm();
if(n>safeNorm2) if(n>safeNorm2)
{
eivecs.col(k) = cross / sqrt(n); eivecs.col(k) = cross / sqrt(n);
}
else else
{ {
// the input matrix and/or the eigenvaues probably contains some inf/NaN, // the input matrix and/or the eigenvaues probably contains some inf/NaN,
@ -659,12 +664,16 @@ template<typename SolverType> struct direct_selfadjoint_eigenvalues<SolverType,3
tmp.diagonal().array() -= eivals(1); tmp.diagonal().array() -= eivals(1);
if(d0<=Eigen::NumTraits<Scalar>::epsilon()) if(d0<=Eigen::NumTraits<Scalar>::epsilon())
{
eivecs.col(1) = eivecs.col(k).unitOrthogonal(); eivecs.col(1) = eivecs.col(k).unitOrthogonal();
}
else else
{ {
n = (cross = eivecs.col(k).cross(tmp.row(0).normalized())).squaredNorm(); n = (cross = eivecs.col(k).cross(tmp.row(0))).squaredNorm();
if(n>safeNorm2) if(n>safeNorm2)
{
eivecs.col(1) = cross / sqrt(n); eivecs.col(1) = cross / sqrt(n);
}
else else
{ {
n = (cross = eivecs.col(k).cross(tmp.row(1))).squaredNorm(); n = (cross = eivecs.col(k).cross(tmp.row(1))).squaredNorm();
@ -678,13 +687,14 @@ template<typename SolverType> struct direct_selfadjoint_eigenvalues<SolverType,3
else else
{ {
// we should never reach this point, // we should never reach this point,
// if so the last two eigenvalues are likely to ve very closed to each other // if so the last two eigenvalues are likely to be very close to each other
eivecs.col(1) = eivecs.col(k).unitOrthogonal(); eivecs.col(1) = eivecs.col(k).unitOrthogonal();
} }
} }
} }
// make sure that eivecs[1] is orthogonal to eivecs[2] // make sure that eivecs[1] is orthogonal to eivecs[2]
// FIXME: this step should not be needed
Scalar d = eivecs.col(1).dot(eivecs.col(k)); Scalar d = eivecs.col(1).dot(eivecs.col(k));
eivecs.col(1) = (eivecs.col(1) - d * eivecs.col(k)).normalized(); eivecs.col(1) = (eivecs.col(1) - d * eivecs.col(k)).normalized();
} }

View File

@ -120,7 +120,7 @@ template<typename MatrixType,int _Direction> class Homogeneous
* Example: \include MatrixBase_homogeneous.cpp * Example: \include MatrixBase_homogeneous.cpp
* Output: \verbinclude MatrixBase_homogeneous.out * Output: \verbinclude MatrixBase_homogeneous.out
* *
* \sa class Homogeneous * \sa VectorwiseOp::homogeneous(), class Homogeneous
*/ */
template<typename Derived> template<typename Derived>
inline typename MatrixBase<Derived>::HomogeneousReturnType inline typename MatrixBase<Derived>::HomogeneousReturnType
@ -137,7 +137,7 @@ MatrixBase<Derived>::homogeneous() const
* Example: \include VectorwiseOp_homogeneous.cpp * Example: \include VectorwiseOp_homogeneous.cpp
* Output: \verbinclude VectorwiseOp_homogeneous.out * Output: \verbinclude VectorwiseOp_homogeneous.out
* *
* \sa MatrixBase::homogeneous() */ * \sa MatrixBase::homogeneous(), class Homogeneous */
template<typename ExpressionType, int Direction> template<typename ExpressionType, int Direction>
inline Homogeneous<ExpressionType,Direction> inline Homogeneous<ExpressionType,Direction>
VectorwiseOp<ExpressionType,Direction>::homogeneous() const VectorwiseOp<ExpressionType,Direction>::homogeneous() const

View File

@ -39,7 +39,6 @@ bool bicgstab(const MatrixType& mat, const Rhs& rhs, Dest& x,
int maxIters = iters; int maxIters = iters;
int n = mat.cols(); int n = mat.cols();
x = precond.solve(x);
VectorType r = rhs - mat * x; VectorType r = rhs - mat * x;
VectorType r0 = r; VectorType r0 = r;

View File

@ -2,6 +2,7 @@
// for linear algebra. // for linear algebra.
// //
// Copyright (C) 2009-2010 Benoit Jacob <jacob.benoit.1@gmail.com> // Copyright (C) 2009-2010 Benoit Jacob <jacob.benoit.1@gmail.com>
// Copyright (C) 2013-2014 Gael Guennebaud <gael.guennebaud@inria.fr>
// //
// This Source Code Form is subject to the terms of the Mozilla // This Source Code Form is subject to the terms of the Mozilla
// 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
@ -424,24 +425,31 @@ void real_2x2_jacobi_svd(const MatrixType& matrix, Index p, Index q,
JacobiRotation<RealScalar> rot1; JacobiRotation<RealScalar> rot1;
RealScalar t = m.coeff(0,0) + m.coeff(1,1); RealScalar t = m.coeff(0,0) + m.coeff(1,1);
RealScalar d = m.coeff(1,0) - m.coeff(0,1); RealScalar d = m.coeff(1,0) - m.coeff(0,1);
if(t == RealScalar(0))
if(d == RealScalar(0))
{ {
rot1.c() = RealScalar(0); rot1.s() = RealScalar(0);
rot1.s() = d > RealScalar(0) ? RealScalar(1) : RealScalar(-1); rot1.c() = RealScalar(1);
} }
else else
{ {
RealScalar t2d2 = numext::hypot(t,d); // If d!=0, then t/d cannot overflow because the magnitude of the
rot1.c() = abs(t)/t2d2; // entries forming d are not too small compared to the ones forming t.
rot1.s() = d/t2d2; RealScalar u = t / d;
if(t<RealScalar(0)) rot1.s() = RealScalar(1) / sqrt(RealScalar(1) + numext::abs2(u));
rot1.s() = -rot1.s(); rot1.c() = rot1.s() * u;
} }
m.applyOnTheLeft(0,1,rot1); m.applyOnTheLeft(0,1,rot1);
j_right->makeJacobi(m,0,1); j_right->makeJacobi(m,0,1);
*j_left = rot1 * j_right->transpose(); *j_left = rot1 * j_right->transpose();
} }
template<typename _MatrixType, int QRPreconditioner>
struct traits<JacobiSVD<_MatrixType,QRPreconditioner> >
{
typedef _MatrixType MatrixType;
};
} // end namespace internal } // end namespace internal
/** \ingroup SVD_Module /** \ingroup SVD_Module
@ -498,7 +506,9 @@ void real_2x2_jacobi_svd(const MatrixType& matrix, Index p, Index q,
* \sa MatrixBase::jacobiSvd() * \sa MatrixBase::jacobiSvd()
*/ */
template<typename _MatrixType, int QRPreconditioner> class JacobiSVD template<typename _MatrixType, int QRPreconditioner> class JacobiSVD
: public SVDBase<JacobiSVD<_MatrixType,QRPreconditioner> >
{ {
typedef SVDBase<JacobiSVD> Base;
public: public:
typedef _MatrixType MatrixType; typedef _MatrixType MatrixType;
@ -515,13 +525,10 @@ template<typename _MatrixType, int QRPreconditioner> class JacobiSVD
MatrixOptions = MatrixType::Options MatrixOptions = MatrixType::Options
}; };
typedef Matrix<Scalar, RowsAtCompileTime, RowsAtCompileTime, typedef typename Base::MatrixUType MatrixUType;
MatrixOptions, MaxRowsAtCompileTime, MaxRowsAtCompileTime> typedef typename Base::MatrixVType MatrixVType;
MatrixUType; typedef typename Base::SingularValuesType SingularValuesType;
typedef Matrix<Scalar, ColsAtCompileTime, ColsAtCompileTime,
MatrixOptions, MaxColsAtCompileTime, MaxColsAtCompileTime>
MatrixVType;
typedef typename internal::plain_diag_type<MatrixType, RealScalar>::type SingularValuesType;
typedef typename internal::plain_row_type<MatrixType>::type RowType; typedef typename internal::plain_row_type<MatrixType>::type RowType;
typedef typename internal::plain_col_type<MatrixType>::type ColType; typedef typename internal::plain_col_type<MatrixType>::type ColType;
typedef Matrix<Scalar, DiagSizeAtCompileTime, DiagSizeAtCompileTime, typedef Matrix<Scalar, DiagSizeAtCompileTime, DiagSizeAtCompileTime,
@ -534,11 +541,6 @@ template<typename _MatrixType, int QRPreconditioner> class JacobiSVD
* perform decompositions via JacobiSVD::compute(const MatrixType&). * perform decompositions via JacobiSVD::compute(const MatrixType&).
*/ */
JacobiSVD() JacobiSVD()
: m_isInitialized(false),
m_isAllocated(false),
m_usePrescribedThreshold(false),
m_computationOptions(0),
m_rows(-1), m_cols(-1), m_diagSize(0)
{} {}
@ -549,11 +551,6 @@ template<typename _MatrixType, int QRPreconditioner> class JacobiSVD
* \sa JacobiSVD() * \sa JacobiSVD()
*/ */
JacobiSVD(Index rows, Index cols, unsigned int computationOptions = 0) JacobiSVD(Index rows, Index cols, unsigned int computationOptions = 0)
: m_isInitialized(false),
m_isAllocated(false),
m_usePrescribedThreshold(false),
m_computationOptions(0),
m_rows(-1), m_cols(-1)
{ {
allocate(rows, cols, computationOptions); allocate(rows, cols, computationOptions);
} }
@ -569,11 +566,6 @@ template<typename _MatrixType, int QRPreconditioner> class JacobiSVD
* available with the (non-default) FullPivHouseholderQR preconditioner. * available with the (non-default) FullPivHouseholderQR preconditioner.
*/ */
JacobiSVD(const MatrixType& matrix, unsigned int computationOptions = 0) JacobiSVD(const MatrixType& matrix, unsigned int computationOptions = 0)
: m_isInitialized(false),
m_isAllocated(false),
m_usePrescribedThreshold(false),
m_computationOptions(0),
m_rows(-1), m_cols(-1)
{ {
compute(matrix, computationOptions); compute(matrix, computationOptions);
} }
@ -601,54 +593,6 @@ template<typename _MatrixType, int QRPreconditioner> class JacobiSVD
return compute(matrix, m_computationOptions); return compute(matrix, m_computationOptions);
} }
/** \returns the \a U matrix.
*
* For the SVD decomposition of a n-by-p matrix, letting \a m be the minimum of \a n and \a p,
* the U matrix is n-by-n if you asked for #ComputeFullU, and is n-by-m if you asked for #ComputeThinU.
*
* The \a m first columns of \a U are the left singular vectors of the matrix being decomposed.
*
* This method asserts that you asked for \a U to be computed.
*/
const MatrixUType& matrixU() const
{
eigen_assert(m_isInitialized && "JacobiSVD is not initialized.");
eigen_assert(computeU() && "This JacobiSVD decomposition didn't compute U. Did you ask for it?");
return m_matrixU;
}
/** \returns the \a V matrix.
*
* For the SVD decomposition of a n-by-p matrix, letting \a m be the minimum of \a n and \a p,
* the V matrix is p-by-p if you asked for #ComputeFullV, and is p-by-m if you asked for ComputeThinV.
*
* The \a m first columns of \a V are the right singular vectors of the matrix being decomposed.
*
* This method asserts that you asked for \a V to be computed.
*/
const MatrixVType& matrixV() const
{
eigen_assert(m_isInitialized && "JacobiSVD is not initialized.");
eigen_assert(computeV() && "This JacobiSVD decomposition didn't compute V. Did you ask for it?");
return m_matrixV;
}
/** \returns the vector of singular values.
*
* For the SVD decomposition of a n-by-p matrix, letting \a m be the minimum of \a n and \a p, the
* returned vector has size \a m. Singular values are always sorted in decreasing order.
*/
const SingularValuesType& singularValues() const
{
eigen_assert(m_isInitialized && "JacobiSVD is not initialized.");
return m_singularValues;
}
/** \returns true if \a U (full or thin) is asked for in this SVD decomposition */
inline bool computeU() const { return m_computeFullU || m_computeThinU; }
/** \returns true if \a V (full or thin) is asked for in this SVD decomposition */
inline bool computeV() const { return m_computeFullV || m_computeThinV; }
/** \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.
@ -667,93 +611,30 @@ template<typename _MatrixType, int QRPreconditioner> class JacobiSVD
return internal::solve_retval<JacobiSVD, Rhs>(*this, b.derived()); return internal::solve_retval<JacobiSVD, Rhs>(*this, b.derived());
} }
/** \returns the number of singular values that are not exactly 0 */ using Base::computeU;
Index nonzeroSingularValues() const using Base::computeV;
{
eigen_assert(m_isInitialized && "JacobiSVD is not initialized.");
return m_nonzeroSingularValues;
}
/** \returns the rank of the matrix of which \c *this is the SVD.
*
* \note This method has to determine which singular values should be considered nonzero.
* For that, it uses the threshold value that you can control by calling
* setThreshold(const RealScalar&).
*/
inline Index rank() const
{
using std::abs;
eigen_assert(m_isInitialized && "JacobiSVD is not initialized.");
if(m_singularValues.size()==0) return 0;
RealScalar premultiplied_threshold = m_singularValues.coeff(0) * threshold();
Index i = m_nonzeroSingularValues-1;
while(i>=0 && m_singularValues.coeff(i) < premultiplied_threshold) --i;
return i+1;
}
/** Allows to prescribe a threshold to be used by certain methods, such as rank() and solve(),
* which need to determine when singular values are to be considered nonzero.
* This is not used for the SVD decomposition itself.
*
* When it needs to get the threshold value, Eigen calls threshold().
* The default is \c NumTraits<Scalar>::epsilon()
*
* \param threshold The new value to use as the threshold.
*
* A singular value will be considered nonzero if its value is strictly greater than
* \f$ \vert singular value \vert \leqslant threshold \times \vert max singular value \vert \f$.
*
* If you want to come back to the default behavior, call setThreshold(Default_t)
*/
JacobiSVD& setThreshold(const RealScalar& threshold)
{
m_usePrescribedThreshold = true;
m_prescribedThreshold = threshold;
return *this;
}
/** Allows to come back to the default behavior, letting Eigen use its default formula for
* determining the threshold.
*
* You should pass the special object Eigen::Default as parameter here.
* \code svd.setThreshold(Eigen::Default); \endcode
*
* See the documentation of setThreshold(const RealScalar&).
*/
JacobiSVD& setThreshold(Default_t)
{
m_usePrescribedThreshold = false;
return *this;
}
/** Returns the threshold that will be used by certain methods such as rank().
*
* See the documentation of setThreshold(const RealScalar&).
*/
RealScalar threshold() const
{
eigen_assert(m_isInitialized || m_usePrescribedThreshold);
return m_usePrescribedThreshold ? m_prescribedThreshold
: (std::max<Index>)(1,m_diagSize)*NumTraits<Scalar>::epsilon();
}
inline Index rows() const { return m_rows; }
inline Index cols() const { return m_cols; }
private: private:
void allocate(Index rows, Index cols, unsigned int computationOptions); void allocate(Index rows, Index cols, unsigned int computationOptions);
protected: protected:
MatrixUType m_matrixU; using Base::m_matrixU;
MatrixVType m_matrixV; using Base::m_matrixV;
SingularValuesType m_singularValues; using Base::m_singularValues;
using Base::m_isInitialized;
using Base::m_isAllocated;
using Base::m_usePrescribedThreshold;
using Base::m_computeFullU;
using Base::m_computeThinU;
using Base::m_computeFullV;
using Base::m_computeThinV;
using Base::m_computationOptions;
using Base::m_nonzeroSingularValues;
using Base::m_rows;
using Base::m_cols;
using Base::m_diagSize;
using Base::m_prescribedThreshold;
WorkMatrixType m_workMatrix; WorkMatrixType m_workMatrix;
bool m_isInitialized, m_isAllocated, m_usePrescribedThreshold;
bool m_computeFullU, m_computeThinU;
bool m_computeFullV, m_computeThinV;
unsigned int m_computationOptions;
Index m_nonzeroSingularValues, m_rows, m_cols, m_diagSize;
RealScalar m_prescribedThreshold;
template<typename __MatrixType, int _QRPreconditioner, bool _IsComplex> template<typename __MatrixType, int _QRPreconditioner, bool _IsComplex>
friend struct internal::svd_precondition_2x2_block_to_be_real; friend struct internal::svd_precondition_2x2_block_to_be_real;
@ -861,7 +742,8 @@ JacobiSVD<MatrixType, QRPreconditioner>::compute(const MatrixType& matrix, unsig
EIGEN_USING_STD_MATH(max); EIGEN_USING_STD_MATH(max);
RealScalar threshold = (max)(considerAsZero, precision * (max)(abs(m_workMatrix.coeff(p,p)), RealScalar threshold = (max)(considerAsZero, precision * (max)(abs(m_workMatrix.coeff(p,p)),
abs(m_workMatrix.coeff(q,q)))); abs(m_workMatrix.coeff(q,q))));
if((max)(abs(m_workMatrix.coeff(p,q)),abs(m_workMatrix.coeff(q,p))) > threshold) // We compare both values to threshold instead of calling max to be robust to NaN (See bug 791)
if(abs(m_workMatrix.coeff(p,q))>threshold || abs(m_workMatrix.coeff(q,p)) > threshold)
{ {
finished = false; finished = false;

View File

@ -2,6 +2,7 @@
// for linear algebra. // for linear algebra.
// //
// Copyright (C) 2009-2010 Benoit Jacob <jacob.benoit.1@gmail.com> // Copyright (C) 2009-2010 Benoit Jacob <jacob.benoit.1@gmail.com>
// Copyright (C) 2014 Gael Guennebaud <gael.guennebaud@inria.fr>
// //
// Copyright (C) 2013 Gauthier Brun <brun.gauthier@gmail.com> // Copyright (C) 2013 Gauthier Brun <brun.gauthier@gmail.com>
// Copyright (C) 2013 Nicolas Carre <nicolas.carre@ensimag.fr> // Copyright (C) 2013 Nicolas Carre <nicolas.carre@ensimag.fr>
@ -12,8 +13,8 @@
// 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_SVD_H #ifndef EIGEN_SVDBASE_H
#define EIGEN_SVD_H #define EIGEN_SVDBASE_H
namespace Eigen { namespace Eigen {
/** \ingroup SVD_Module /** \ingroup SVD_Module
@ -21,9 +22,10 @@ namespace Eigen {
* *
* \class SVDBase * \class SVDBase
* *
* \brief Mother class of SVD classes algorithms * \brief Base class of SVD algorithms
*
* \tparam Derived the type of the actual SVD decomposition
* *
* \param MatrixType the type of the matrix of which we are computing the SVD decomposition
* SVD decomposition consists in decomposing any n-by-p matrix \a A as a product * SVD decomposition consists in decomposing any n-by-p matrix \a A as a product
* \f[ A = U S V^* \f] * \f[ A = U S V^* \f]
* where \a U is a n-by-n unitary, \a V is a p-by-p unitary, and \a S is a n-by-p real positive matrix which is zero outside of its main diagonal; * where \a U is a n-by-n unitary, \a V is a p-by-p unitary, and \a S is a n-by-p real positive matrix which is zero outside of its main diagonal;
@ -42,12 +44,12 @@ namespace Eigen {
* terminate in finite (and reasonable) time. * terminate in finite (and reasonable) time.
* \sa MatrixBase::genericSvd() * \sa MatrixBase::genericSvd()
*/ */
template<typename _MatrixType> template<typename Derived>
class SVDBase class SVDBase
{ {
public: public:
typedef _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::Index Index; typedef typename MatrixType::Index Index;
@ -61,46 +63,16 @@ public:
MatrixOptions = MatrixType::Options MatrixOptions = MatrixType::Options
}; };
typedef Matrix<Scalar, RowsAtCompileTime, RowsAtCompileTime, typedef Matrix<Scalar, RowsAtCompileTime, RowsAtCompileTime, MatrixOptions, MaxRowsAtCompileTime, MaxRowsAtCompileTime> MatrixUType;
MatrixOptions, MaxRowsAtCompileTime, MaxRowsAtCompileTime> typedef Matrix<Scalar, ColsAtCompileTime, ColsAtCompileTime, MatrixOptions, MaxColsAtCompileTime, MaxColsAtCompileTime> MatrixVType;
MatrixUType;
typedef Matrix<Scalar, ColsAtCompileTime, ColsAtCompileTime,
MatrixOptions, MaxColsAtCompileTime, MaxColsAtCompileTime>
MatrixVType;
typedef typename internal::plain_diag_type<MatrixType, RealScalar>::type SingularValuesType; typedef typename internal::plain_diag_type<MatrixType, RealScalar>::type SingularValuesType;
typedef typename internal::plain_row_type<MatrixType>::type RowType;
typedef typename internal::plain_col_type<MatrixType>::type ColType;
typedef Matrix<Scalar, DiagSizeAtCompileTime, DiagSizeAtCompileTime,
MatrixOptions, MaxDiagSizeAtCompileTime, MaxDiagSizeAtCompileTime>
WorkMatrixType;
Derived& derived() { return *static_cast<Derived*>(this); }
const Derived& derived() const { return *static_cast<const Derived*>(this); }
/** \brief Method performing the decomposition of given matrix using custom options.
*
* \param matrix the matrix to decompose
* \param computationOptions optional parameter allowing to specify if you want full or thin U or V unitaries to be computed.
* By default, none is computed. This is a bit-field, the possible bits are #ComputeFullU, #ComputeThinU,
* #ComputeFullV, #ComputeThinV.
*
* Thin unitaries are only available if your matrix type has a Dynamic number of columns (for example MatrixXf). They also are not
* available with the (non-default) FullPivHouseholderQR preconditioner.
*/
SVDBase& compute(const MatrixType& matrix, unsigned int computationOptions);
/** \brief Method performing the decomposition of given matrix using current options.
*
* \param matrix the matrix to decompose
*
* This method uses the current \a computationOptions, as already passed to the constructor or to compute(const MatrixType&, unsigned int).
*/
//virtual SVDBase& compute(const MatrixType& matrix) = 0;
SVDBase& compute(const MatrixType& matrix);
/** \returns the \a U matrix. /** \returns the \a U matrix.
* *
* For the SVDBase decomposition of a n-by-p matrix, letting \a m be the minimum of \a n and \a p, * For the SVD decomposition of a n-by-p matrix, letting \a m be the minimum of \a n and \a p,
* the U matrix is n-by-n if you asked for #ComputeFullU, and is n-by-m if you asked for #ComputeThinU. * the U matrix is n-by-n if you asked for #ComputeFullU, and is n-by-m if you asked for #ComputeThinU.
* *
* The \a m first columns of \a U are the left singular vectors of the matrix being decomposed. * The \a m first columns of \a U are the left singular vectors of the matrix being decomposed.
@ -141,8 +113,6 @@ public:
return m_singularValues; return m_singularValues;
} }
/** \returns the number of singular values that are not exactly 0 */ /** \returns the number of singular values that are not exactly 0 */
Index nonzeroSingularValues() const Index nonzeroSingularValues() const
{ {
@ -150,17 +120,77 @@ public:
return m_nonzeroSingularValues; return m_nonzeroSingularValues;
} }
/** \returns the rank of the matrix of which \c *this is the SVD.
*
* \note This method has to determine which singular values should be considered nonzero.
* For that, it uses the threshold value that you can control by calling
* setThreshold(const RealScalar&).
*/
inline Index rank() const
{
using std::abs;
eigen_assert(m_isInitialized && "JacobiSVD is not initialized.");
if(m_singularValues.size()==0) return 0;
RealScalar premultiplied_threshold = m_singularValues.coeff(0) * threshold();
Index i = m_nonzeroSingularValues-1;
while(i>=0 && m_singularValues.coeff(i) < premultiplied_threshold) --i;
return i+1;
}
/** Allows to prescribe a threshold to be used by certain methods, such as rank() and solve(),
* which need to determine when singular values are to be considered nonzero.
* This is not used for the SVD decomposition itself.
*
* When it needs to get the threshold value, Eigen calls threshold().
* The default is \c NumTraits<Scalar>::epsilon()
*
* \param threshold The new value to use as the threshold.
*
* A singular value will be considered nonzero if its value is strictly greater than
* \f$ \vert singular value \vert \leqslant threshold \times \vert max singular value \vert \f$.
*
* If you want to come back to the default behavior, call setThreshold(Default_t)
*/
Derived& setThreshold(const RealScalar& threshold)
{
m_usePrescribedThreshold = true;
m_prescribedThreshold = threshold;
return derived();
}
/** Allows to come back to the default behavior, letting Eigen use its default formula for
* determining the threshold.
*
* You should pass the special object Eigen::Default as parameter here.
* \code svd.setThreshold(Eigen::Default); \endcode
*
* See the documentation of setThreshold(const RealScalar&).
*/
Derived& setThreshold(Default_t)
{
m_usePrescribedThreshold = false;
return derived();
}
/** Returns the threshold that will be used by certain methods such as rank().
*
* See the documentation of setThreshold(const RealScalar&).
*/
RealScalar threshold() const
{
eigen_assert(m_isInitialized || m_usePrescribedThreshold);
return m_usePrescribedThreshold ? m_prescribedThreshold
: (std::max<Index>)(1,m_diagSize)*NumTraits<Scalar>::epsilon();
}
/** \returns true if \a U (full or thin) is asked for in this SVD decomposition */ /** \returns true if \a U (full or thin) is asked for in this SVD decomposition */
inline bool computeU() const { return m_computeFullU || m_computeThinU; } inline bool computeU() const { return m_computeFullU || m_computeThinU; }
/** \returns true if \a V (full or thin) is asked for in this SVD decomposition */ /** \returns true if \a V (full or thin) is asked for in this SVD decomposition */
inline bool computeV() const { return m_computeFullV || m_computeThinV; } inline bool computeV() const { return m_computeFullV || m_computeThinV; }
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; }
protected: protected:
// 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) ;
@ -168,12 +198,12 @@ protected:
MatrixUType m_matrixU; MatrixUType m_matrixU;
MatrixVType m_matrixV; MatrixVType m_matrixV;
SingularValuesType m_singularValues; SingularValuesType m_singularValues;
bool m_isInitialized, m_isAllocated; bool m_isInitialized, m_isAllocated, m_usePrescribedThreshold;
bool m_computeFullU, m_computeThinU; bool m_computeFullU, m_computeThinU;
bool m_computeFullV, m_computeThinV; bool m_computeFullV, m_computeThinV;
unsigned int m_computationOptions; unsigned int m_computationOptions;
Index m_nonzeroSingularValues, m_rows, m_cols, m_diagSize; Index m_nonzeroSingularValues, m_rows, m_cols, m_diagSize;
RealScalar m_prescribedThreshold;
/** \brief Default Constructor. /** \brief Default Constructor.
* *
@ -182,8 +212,9 @@ protected:
SVDBase() SVDBase()
: m_isInitialized(false), : m_isInitialized(false),
m_isAllocated(false), m_isAllocated(false),
m_usePrescribedThreshold(false),
m_computationOptions(0), m_computationOptions(0),
m_rows(-1), m_cols(-1) m_rows(-1), m_cols(-1), m_diagSize(0)
{} {}
@ -220,17 +251,13 @@ bool SVDBase<MatrixType>::allocate(Index rows, Index cols, unsigned int computat
m_diagSize = (std::min)(m_rows, m_cols); m_diagSize = (std::min)(m_rows, m_cols);
m_singularValues.resize(m_diagSize); m_singularValues.resize(m_diagSize);
if(RowsAtCompileTime==Dynamic) if(RowsAtCompileTime==Dynamic)
m_matrixU.resize(m_rows, m_computeFullU ? m_rows m_matrixU.resize(m_rows, m_computeFullU ? m_rows : m_computeThinU ? m_diagSize : 0);
: m_computeThinU ? m_diagSize
: 0);
if(ColsAtCompileTime==Dynamic) if(ColsAtCompileTime==Dynamic)
m_matrixV.resize(m_cols, m_computeFullV ? m_cols m_matrixV.resize(m_cols, m_computeFullV ? m_cols : m_computeThinV ? m_diagSize : 0);
: m_computeThinV ? m_diagSize
: 0);
return false; return false;
} }
}// end namespace }// end namespace
#endif // EIGEN_SVD_H #endif // EIGEN_SVDBASE_H

View File

@ -2,6 +2,7 @@
// for linear algebra. // for linear algebra.
// //
// Copyright (C) 2010 Benoit Jacob <jacob.benoit.1@gmail.com> // Copyright (C) 2010 Benoit Jacob <jacob.benoit.1@gmail.com>
// Copyright (C) 2013-2014 Gael Guennebaud <gael.guennebaud@inria.fr>
// //
// This Source Code Form is subject to the terms of the Mozilla // This Source Code Form is subject to the terms of the Mozilla
// 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
@ -153,14 +154,19 @@ void upperbidiagonalization_blocked_helper(MatrixType& A,
typename MatrixType::RealScalar *diagonal, typename MatrixType::RealScalar *diagonal,
typename MatrixType::RealScalar *upper_diagonal, typename MatrixType::RealScalar *upper_diagonal,
typename MatrixType::Index bs, typename MatrixType::Index bs,
Ref<Matrix<typename MatrixType::Scalar, Dynamic, Dynamic> > X, Ref<Matrix<typename MatrixType::Scalar, Dynamic, Dynamic,
Ref<Matrix<typename MatrixType::Scalar, Dynamic, Dynamic> > Y) traits<MatrixType>::Flags & RowMajorBit> > X,
Ref<Matrix<typename MatrixType::Scalar, Dynamic, Dynamic,
traits<MatrixType>::Flags & RowMajorBit> > Y)
{ {
typedef typename MatrixType::Index Index; typedef typename MatrixType::Index Index;
typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::Scalar Scalar;
typedef Ref<Matrix<Scalar, Dynamic, 1> > SubColumnType; enum { StorageOrder = traits<MatrixType>::Flags & RowMajorBit };
typedef Ref<Matrix<Scalar, 1, Dynamic>, 0, InnerStride<> > SubRowType; typedef InnerStride<int(StorageOrder) == int(ColMajor) ? 1 : Dynamic> ColInnerStride;
typedef Ref<Matrix<Scalar, Dynamic, Dynamic> > SubMatType; typedef InnerStride<int(StorageOrder) == int(ColMajor) ? Dynamic : 1> RowInnerStride;
typedef Ref<Matrix<Scalar, Dynamic, 1>, 0, ColInnerStride> SubColumnType;
typedef Ref<Matrix<Scalar, 1, Dynamic>, 0, RowInnerStride> SubRowType;
typedef Ref<Matrix<Scalar, Dynamic, Dynamic, StorageOrder > > SubMatType;
Index brows = A.rows(); Index brows = A.rows();
Index bcols = A.cols(); Index bcols = A.cols();
@ -287,8 +293,18 @@ void upperbidiagonalization_inplace_blocked(MatrixType& A, BidiagType& bidiagona
Index cols = A.cols(); Index cols = A.cols();
Index size = (std::min)(rows, cols); Index size = (std::min)(rows, cols);
Matrix<Scalar,MatrixType::RowsAtCompileTime,Dynamic,ColMajor,MatrixType::MaxRowsAtCompileTime> X(rows,maxBlockSize); // X and Y are work space
Matrix<Scalar,MatrixType::ColsAtCompileTime,Dynamic,ColMajor,MatrixType::MaxColsAtCompileTime> Y(cols,maxBlockSize); enum { StorageOrder = traits<MatrixType>::Flags & RowMajorBit };
Matrix<Scalar,
MatrixType::RowsAtCompileTime,
Dynamic,
StorageOrder,
MatrixType::MaxRowsAtCompileTime> X(rows,maxBlockSize);
Matrix<Scalar,
MatrixType::ColsAtCompileTime,
Dynamic,
StorageOrder,
MatrixType::MaxColsAtCompileTime> Y(cols,maxBlockSize);
Index blockSize = (std::min)(maxBlockSize,size); Index blockSize = (std::min)(maxBlockSize,size);
Index k = 0; Index k = 0;

View File

@ -15,7 +15,7 @@ namespace Eigen {
namespace internal { namespace internal {
template<typename Lhs, typename Rhs, typename ResultType> template<typename Lhs, typename Rhs, typename ResultType>
static void conservative_sparse_sparse_product_impl(const Lhs& lhs, const Rhs& rhs, ResultType& res) static void conservative_sparse_sparse_product_impl(const Lhs& lhs, const Rhs& rhs, ResultType& res, bool sortedInsertion = false)
{ {
typedef typename remove_all<Lhs>::type::Scalar Scalar; typedef typename remove_all<Lhs>::type::Scalar Scalar;
typedef typename remove_all<Lhs>::type::Index Index; typedef typename remove_all<Lhs>::type::Index Index;
@ -25,9 +25,11 @@ static void conservative_sparse_sparse_product_impl(const Lhs& lhs, const Rhs& r
Index cols = rhs.outerSize(); Index cols = rhs.outerSize();
eigen_assert(lhs.outerSize() == rhs.innerSize()); eigen_assert(lhs.outerSize() == rhs.innerSize());
std::vector<bool> mask(rows,false); ei_declare_aligned_stack_constructed_variable(bool, mask, rows, 0);
Matrix<Scalar,Dynamic,1> values(rows); ei_declare_aligned_stack_constructed_variable(Scalar, values, rows, 0);
Matrix<Index,Dynamic,1> indices(rows); ei_declare_aligned_stack_constructed_variable(Index, indices, rows, 0);
std::memset(mask,0,sizeof(bool)*rows);
// estimate the number of non zero entries // estimate the number of non zero entries
// given a rhs column containing Y non zeros, we assume that the respective Y columns // given a rhs column containing Y non zeros, we assume that the respective Y columns
@ -64,7 +66,8 @@ static void conservative_sparse_sparse_product_impl(const Lhs& lhs, const Rhs& r
values[i] += x * y; values[i] += x * y;
} }
} }
if(!sortedInsertion)
{
// unordered insertion // unordered insertion
for(Index k=0; k<nnz; ++k) for(Index k=0; k<nnz; ++k)
{ {
@ -72,24 +75,22 @@ static void conservative_sparse_sparse_product_impl(const Lhs& lhs, const Rhs& r
res.insertBackByOuterInnerUnordered(j,i) = values[i]; res.insertBackByOuterInnerUnordered(j,i) = values[i];
mask[i] = false; mask[i] = false;
} }
}
#if 0 else
{
// alternative ordered insertion code: // alternative ordered insertion code:
const Index t200 = rows/11; // 11 == (log2(200)*1.39)
Index t200 = rows/(log2(200)*1.39); const Index t = (rows*100)/139;
Index t = (rows*100)/139;
// FIXME reserve nnz non zeros // FIXME reserve nnz non zeros
// FIXME implement fast sort algorithms for very small nnz // FIXME implement faster sorting algorithms for very small nnz
// if the result is sparse enough => use a quick sort // if the result is sparse enough => use a quick sort
// otherwise => loop through the entire vector // otherwise => loop through the entire vector
// In order to avoid to perform an expensive log2 when the // In order to avoid to perform an expensive log2 when the
// result is clearly very sparse we use a linear bound up to 200. // result is clearly very sparse we use a linear bound up to 200.
//if((nnz<200 && nnz<t200) || nnz * log2(nnz) < t) if((nnz<200 && nnz<t200) || nnz * log2(nnz) < t)
//res.startVec(j);
if(true)
{ {
if(nnz>1) std::sort(indices.data(),indices.data()+nnz); if(nnz>1) std::sort(indices,indices+nnz);
for(Index k=0; k<nnz; ++k) for(Index k=0; k<nnz; ++k)
{ {
Index i = indices[k]; Index i = indices[k];
@ -109,8 +110,7 @@ static void conservative_sparse_sparse_product_impl(const Lhs& lhs, const Rhs& r
} }
} }
} }
#endif }
} }
res.finalize(); res.finalize();
} }
@ -135,12 +135,25 @@ struct conservative_sparse_sparse_product_selector<Lhs,Rhs,ResultType,ColMajor,C
static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res) static void run(const Lhs& lhs, const Rhs& rhs, ResultType& res)
{ {
typedef SparseMatrix<typename ResultType::Scalar,RowMajor,typename ResultType::Index> RowMajorMatrix; typedef SparseMatrix<typename ResultType::Scalar,RowMajor,typename ResultType::Index> RowMajorMatrix;
typedef SparseMatrix<typename ResultType::Scalar,ColMajor,typename ResultType::Index> ColMajorMatrix; typedef SparseMatrix<typename ResultType::Scalar,ColMajor,typename ResultType::Index> ColMajorMatrixAux;
typedef typename sparse_eval<ColMajorMatrixAux,ResultType::RowsAtCompileTime,ResultType::ColsAtCompileTime>::type ColMajorMatrix;
// FIXME, the following heuristic is probably not very good.
if(lhs.rows()>=rhs.cols())
{
ColMajorMatrix resCol(lhs.rows(),rhs.cols()); ColMajorMatrix resCol(lhs.rows(),rhs.cols());
internal::conservative_sparse_sparse_product_impl<Lhs,Rhs,ColMajorMatrix>(lhs, rhs, resCol); // perform sorted insertion
// sort the non zeros: internal::conservative_sparse_sparse_product_impl<Lhs,Rhs,ColMajorMatrix>(lhs, rhs, resCol, true);
res = resCol.markAsRValue();
}
else
{
ColMajorMatrixAux resCol(lhs.rows(),rhs.cols());
// ressort to transpose to sort the entries
internal::conservative_sparse_sparse_product_impl<Lhs,Rhs,ColMajorMatrixAux>(lhs, rhs, resCol, false);
RowMajorMatrix resRow(resCol); RowMajorMatrix resRow(resCol);
res = resRow; res = resRow.markAsRValue();
}
} }
}; };

View File

@ -318,15 +318,6 @@ class DenseTimeSparseProduct
DenseTimeSparseProduct& operator=(const DenseTimeSparseProduct&); DenseTimeSparseProduct& operator=(const DenseTimeSparseProduct&);
}; };
// sparse * dense
template<typename Derived>
template<typename OtherDerived>
inline const typename SparseDenseProductReturnType<Derived,OtherDerived>::Type
SparseMatrixBase<Derived>::operator*(const MatrixBase<OtherDerived> &other) const
{
return typename SparseDenseProductReturnType<Derived,OtherDerived>::Type(derived(), other.derived());
}
} // end namespace Eigen } // end namespace Eigen
#endif // EIGEN_SPARSEDENSEPRODUCT_H #endif // EIGEN_SPARSEDENSEPRODUCT_H

View File

@ -358,7 +358,8 @@ template<typename Derived> class SparseMatrixBase : public EigenBase<Derived>
/** sparse * dense (returns a dense object unless it is an outer product) */ /** sparse * dense (returns a dense object unless it is an outer product) */
template<typename OtherDerived> template<typename OtherDerived>
const typename SparseDenseProductReturnType<Derived,OtherDerived>::Type const typename SparseDenseProductReturnType<Derived,OtherDerived>::Type
operator*(const MatrixBase<OtherDerived> &other) const; operator*(const MatrixBase<OtherDerived> &other) const
{ return typename SparseDenseProductReturnType<Derived,OtherDerived>::Type(derived(), other.derived()); }
/** \returns an expression of P H P^-1 where H is the matrix represented by \c *this */ /** \returns an expression of P H P^-1 where H is the matrix represented by \c *this */
SparseSymmetricPermutationProduct<Derived,Upper|Lower> twistedBy(const PermutationMatrix<Dynamic,Dynamic,Index>& perm) const SparseSymmetricPermutationProduct<Derived,Upper|Lower> twistedBy(const PermutationMatrix<Dynamic,Dynamic,Index>& perm) const

View File

@ -109,7 +109,7 @@ class SparseVector
inline Scalar& coeffRef(Index row, Index col) inline Scalar& coeffRef(Index row, Index col)
{ {
eigen_assert(IsColVector ? (col==0 && row>=0 && row<m_size) : (row==0 && col>=0 && col<m_size)); eigen_assert(IsColVector ? (col==0 && row>=0 && row<m_size) : (row==0 && col>=0 && col<m_size));
return coeff(IsColVector ? row : col); return coeffRef(IsColVector ? row : col);
} }
/** \returns a reference to the coefficient value at given index \a i /** \returns a reference to the coefficient value at given index \a i
@ -152,6 +152,18 @@ class SparseVector
return m_data.value(m_data.size()-1); return m_data.value(m_data.size()-1);
} }
Scalar& insertBackByOuterInnerUnordered(Index outer, Index inner)
{
EIGEN_UNUSED_VARIABLE(outer);
eigen_assert(outer==0);
return insertBackUnordered(inner);
}
inline Scalar& insertBackUnordered(Index i)
{
m_data.append(0, i);
return m_data.value(m_data.size()-1);
}
inline Scalar& insert(Index row, Index col) inline Scalar& insert(Index row, Index col)
{ {
eigen_assert(IsColVector ? (col==0 && row>=0 && row<m_size) : (row==0 && col>=0 && col<m_size)); eigen_assert(IsColVector ? (col==0 && row>=0 && row<m_size) : (row==0 && col>=0 && col<m_size));

View File

@ -75,7 +75,7 @@ class SparseQR
typedef Matrix<Scalar, Dynamic, 1> ScalarVector; typedef Matrix<Scalar, Dynamic, 1> ScalarVector;
typedef PermutationMatrix<Dynamic, Dynamic, Index> PermutationType; typedef PermutationMatrix<Dynamic, Dynamic, Index> PermutationType;
public: public:
SparseQR () : m_isInitialized(false), m_analysisIsok(false), m_lastError(""), m_useDefaultThreshold(true),m_isQSorted(false) SparseQR () : m_isInitialized(false), m_analysisIsok(false), m_lastError(""), m_useDefaultThreshold(true),m_isQSorted(false),m_isEtreeOk(false)
{ } { }
/** Construct a QR factorization of the matrix \a mat. /** Construct a QR factorization of the matrix \a mat.
@ -84,7 +84,7 @@ class SparseQR
* *
* \sa compute() * \sa compute()
*/ */
SparseQR(const MatrixType& mat) : m_isInitialized(false), m_analysisIsok(false), m_lastError(""), m_useDefaultThreshold(true),m_isQSorted(false) SparseQR(const MatrixType& mat) : m_isInitialized(false), m_analysisIsok(false), m_lastError(""), m_useDefaultThreshold(true),m_isQSorted(false),m_isEtreeOk(false)
{ {
compute(mat); compute(mat);
} }
@ -262,6 +262,7 @@ class SparseQR
IndexVector m_etree; // Column elimination tree IndexVector m_etree; // Column elimination tree
IndexVector m_firstRowElt; // First element in each row IndexVector m_firstRowElt; // First element in each row
bool m_isQSorted; // whether Q is sorted or not bool m_isQSorted; // whether Q is sorted or not
bool m_isEtreeOk; // whether the elimination tree match the initial input matrix
template <typename, typename > friend struct SparseQR_QProduct; template <typename, typename > friend struct SparseQR_QProduct;
template <typename > friend struct SparseQRMatrixQReturnType; template <typename > friend struct SparseQRMatrixQReturnType;
@ -297,6 +298,7 @@ void SparseQR<MatrixType,OrderingType>::analyzePattern(const MatrixType& mat)
// Compute the column elimination tree of the permuted matrix // Compute the column elimination tree of the permuted matrix
m_outputPerm_c = m_perm_c.inverse(); m_outputPerm_c = m_perm_c.inverse();
internal::coletree(mat, m_etree, m_firstRowElt, m_outputPerm_c.indices().data()); internal::coletree(mat, m_etree, m_firstRowElt, m_outputPerm_c.indices().data());
m_isEtreeOk = true;
m_R.resize(m, n); m_R.resize(m, n);
m_Q.resize(m, diagSize); m_Q.resize(m, diagSize);
@ -331,6 +333,15 @@ void SparseQR<MatrixType,OrderingType>::factorize(const MatrixType& mat)
ScalarVector tval(m); // The dense vector used to compute the current column ScalarVector tval(m); // The dense vector used to compute the current column
RealScalar pivotThreshold = m_threshold; RealScalar pivotThreshold = m_threshold;
m_R.setZero();
m_Q.setZero();
if(!m_isEtreeOk)
{
m_outputPerm_c = m_perm_c.inverse();
internal::coletree(mat, m_etree, m_firstRowElt, m_outputPerm_c.indices().data());
m_isEtreeOk = true;
}
m_pmat = mat; m_pmat = mat;
m_pmat.uncompress(); // To have the innerNonZeroPtr allocated m_pmat.uncompress(); // To have the innerNonZeroPtr allocated
// Apply the fill-in reducing permutation lazily: // Apply the fill-in reducing permutation lazily:
@ -447,7 +458,7 @@ void SparseQR<MatrixType,OrderingType>::factorize(const MatrixType& mat)
} }
} // End update current column } // End update current column
Scalar tau; Scalar tau = RealScalar(0);
RealScalar beta = 0; RealScalar beta = 0;
if(nonzeroCol < diagSize) if(nonzeroCol < diagSize)
@ -461,7 +472,6 @@ void SparseQR<MatrixType,OrderingType>::factorize(const MatrixType& mat)
for (Index itq = 1; itq < nzcolQ; ++itq) sqrNorm += numext::abs2(tval(Qidx(itq))); for (Index itq = 1; itq < nzcolQ; ++itq) sqrNorm += numext::abs2(tval(Qidx(itq)));
if(sqrNorm == RealScalar(0) && numext::imag(c0) == RealScalar(0)) if(sqrNorm == RealScalar(0) && numext::imag(c0) == RealScalar(0))
{ {
tau = RealScalar(0);
beta = numext::real(c0); beta = numext::real(c0);
tval(Qidx(0)) = 1; tval(Qidx(0)) = 1;
} }
@ -514,6 +524,7 @@ void SparseQR<MatrixType,OrderingType>::factorize(const MatrixType& mat)
// Recompute the column elimination tree // Recompute the column elimination tree
internal::coletree(m_pmat, m_etree, m_firstRowElt, m_pivotperm.indices().data()); internal::coletree(m_pmat, m_etree, m_firstRowElt, m_pivotperm.indices().data());
m_isEtreeOk = false;
} }
} }

View File

@ -29,6 +29,9 @@ abs2() const
} }
/** \returns an expression of the coefficient-wise exponential of *this. /** \returns an expression of the coefficient-wise exponential of *this.
*
* This function computes the coefficient-wise exponential. The function MatrixBase::exp() in the
* unsupported module MatrixFunctions computes the matrix exponential.
* *
* Example: \include Cwise_exp.cpp * Example: \include Cwise_exp.cpp
* Output: \verbinclude Cwise_exp.out * Output: \verbinclude Cwise_exp.out
@ -43,6 +46,9 @@ exp() const
} }
/** \returns an expression of the coefficient-wise logarithm of *this. /** \returns an expression of the coefficient-wise logarithm of *this.
*
* This function computes the coefficient-wise logarithm. The function MatrixBase::log() in the
* unsupported module MatrixFunctions computes the matrix logarithm.
* *
* Example: \include Cwise_log.cpp * Example: \include Cwise_log.cpp
* Output: \verbinclude Cwise_log.out * Output: \verbinclude Cwise_log.out
@ -57,6 +63,9 @@ log() const
} }
/** \returns an expression of the coefficient-wise square root of *this. /** \returns an expression of the coefficient-wise square root of *this.
*
* This function computes the coefficient-wise square root. The function MatrixBase::sqrt() in the
* unsupported module MatrixFunctions computes the matrix square root.
* *
* Example: \include Cwise_sqrt.cpp * Example: \include Cwise_sqrt.cpp
* Output: \verbinclude Cwise_sqrt.out * Output: \verbinclude Cwise_sqrt.out
@ -71,6 +80,9 @@ sqrt() const
} }
/** \returns an expression of the coefficient-wise cosine of *this. /** \returns an expression of the coefficient-wise cosine of *this.
*
* This function computes the coefficient-wise cosine. The function MatrixBase::cos() in the
* unsupported module MatrixFunctions computes the matrix cosine.
* *
* Example: \include Cwise_cos.cpp * Example: \include Cwise_cos.cpp
* Output: \verbinclude Cwise_cos.out * Output: \verbinclude Cwise_cos.out
@ -86,6 +98,9 @@ cos() const
/** \returns an expression of the coefficient-wise sine of *this. /** \returns an expression of the coefficient-wise sine of *this.
*
* This function computes the coefficient-wise sine. The function MatrixBase::sin() in the
* unsupported module MatrixFunctions computes the matrix sine.
* *
* Example: \include Cwise_sin.cpp * Example: \include Cwise_sin.cpp
* Output: \verbinclude Cwise_sin.out * Output: \verbinclude Cwise_sin.out
@ -155,6 +170,9 @@ atan() const
} }
/** \returns an expression of the coefficient-wise power of *this to the given exponent. /** \returns an expression of the coefficient-wise power of *this to the given exponent.
*
* This function computes the coefficient-wise power. The function MatrixBase::pow() in the
* unsupported module MatrixFunctions computes the matrix power.
* *
* Example: \include Cwise_pow.cpp * Example: \include Cwise_pow.cpp
* Output: \verbinclude Cwise_pow.out * Output: \verbinclude Cwise_pow.out

View File

@ -6,19 +6,25 @@ using namespace Eigen;
using namespace std; using namespace std;
template<typename T> template<typename T>
EIGEN_DONT_INLINE typename T::Scalar sqsumNorm(const T& v) EIGEN_DONT_INLINE typename T::Scalar sqsumNorm(T& v)
{ {
return v.norm(); return v.norm();
} }
template<typename T> template<typename T>
EIGEN_DONT_INLINE typename T::Scalar hypotNorm(const T& v) EIGEN_DONT_INLINE typename T::Scalar stableNorm(T& v)
{
return v.stableNorm();
}
template<typename T>
EIGEN_DONT_INLINE typename T::Scalar hypotNorm(T& v)
{ {
return v.hypotNorm(); return v.hypotNorm();
} }
template<typename T> template<typename T>
EIGEN_DONT_INLINE typename T::Scalar blueNorm(const T& v) EIGEN_DONT_INLINE typename T::Scalar blueNorm(T& v)
{ {
return v.blueNorm(); return v.blueNorm();
} }
@ -217,20 +223,21 @@ EIGEN_DONT_INLINE typename T::Scalar pblueNorm(const T& v)
} }
#define BENCH_PERF(NRM) { \ #define BENCH_PERF(NRM) { \
float af = 0; double ad = 0; std::complex<float> ac = 0; \
Eigen::BenchTimer tf, td, tcf; tf.reset(); td.reset(); tcf.reset();\ Eigen::BenchTimer tf, td, tcf; tf.reset(); td.reset(); tcf.reset();\
for (int k=0; k<tries; ++k) { \ for (int k=0; k<tries; ++k) { \
tf.start(); \ tf.start(); \
for (int i=0; i<iters; ++i) NRM(vf); \ for (int i=0; i<iters; ++i) { af += NRM(vf); } \
tf.stop(); \ tf.stop(); \
} \ } \
for (int k=0; k<tries; ++k) { \ for (int k=0; k<tries; ++k) { \
td.start(); \ td.start(); \
for (int i=0; i<iters; ++i) NRM(vd); \ for (int i=0; i<iters; ++i) { ad += NRM(vd); } \
td.stop(); \ td.stop(); \
} \ } \
/*for (int k=0; k<std::max(1,tries/3); ++k) { \ /*for (int k=0; k<std::max(1,tries/3); ++k) { \
tcf.start(); \ tcf.start(); \
for (int i=0; i<iters; ++i) NRM(vcf); \ for (int i=0; i<iters; ++i) { ac += NRM(vcf); } \
tcf.stop(); \ tcf.stop(); \
} */\ } */\
std::cout << #NRM << "\t" << tf.value() << " " << td.value() << " " << tcf.value() << "\n"; \ std::cout << #NRM << "\t" << tf.value() << " " << td.value() << " " << tcf.value() << "\n"; \
@ -316,14 +323,17 @@ int main(int argc, char** argv)
std::cout << "\n"; std::cout << "\n";
} }
y = 1;
std::cout.precision(4); std::cout.precision(4);
std::cerr << "Performance (out of cache):\n"; int s1 = 1024*1024*32;
std::cerr << "Performance (out of cache, " << s1 << "):\n";
{ {
int iters = 1; int iters = 1;
VectorXf vf = VectorXf::Random(1024*1024*32) * y; VectorXf vf = VectorXf::Random(s1) * y;
VectorXd vd = VectorXd::Random(1024*1024*32) * y; VectorXd vd = VectorXd::Random(s1) * y;
VectorXcf vcf = VectorXcf::Random(1024*1024*32) * y; VectorXcf vcf = VectorXcf::Random(s1) * y;
BENCH_PERF(sqsumNorm); BENCH_PERF(sqsumNorm);
BENCH_PERF(stableNorm);
BENCH_PERF(blueNorm); BENCH_PERF(blueNorm);
BENCH_PERF(pblueNorm); BENCH_PERF(pblueNorm);
BENCH_PERF(lapackNorm); BENCH_PERF(lapackNorm);
@ -332,13 +342,14 @@ int main(int argc, char** argv)
BENCH_PERF(bl2passNorm); BENCH_PERF(bl2passNorm);
} }
std::cerr << "\nPerformance (in cache):\n"; std::cerr << "\nPerformance (in cache, " << 512 << "):\n";
{ {
int iters = 100000; int iters = 100000;
VectorXf vf = VectorXf::Random(512) * y; VectorXf vf = VectorXf::Random(512) * y;
VectorXd vd = VectorXd::Random(512) * y; VectorXd vd = VectorXd::Random(512) * y;
VectorXcf vcf = VectorXcf::Random(512) * y; VectorXcf vcf = VectorXcf::Random(512) * y;
BENCH_PERF(sqsumNorm); BENCH_PERF(sqsumNorm);
BENCH_PERF(stableNorm);
BENCH_PERF(blueNorm); BENCH_PERF(blueNorm);
BENCH_PERF(pblueNorm); BENCH_PERF(pblueNorm);
BENCH_PERF(lapackNorm); BENCH_PERF(lapackNorm);

View File

@ -9,6 +9,12 @@
# EIGEN3_FOUND - system has eigen lib with correct version # EIGEN3_FOUND - system has eigen lib with correct version
# EIGEN3_INCLUDE_DIR - the eigen include directory # EIGEN3_INCLUDE_DIR - the eigen include directory
# EIGEN3_VERSION - eigen version # EIGEN3_VERSION - eigen version
#
# This module reads hints about search locations from
# the following enviroment variables:
#
# EIGEN3_ROOT
# EIGEN3_ROOT_DIR
# Copyright (c) 2006, 2007 Montel Laurent, <montel@kde.org> # Copyright (c) 2006, 2007 Montel Laurent, <montel@kde.org>
# Copyright (c) 2008, 2009 Gael Guennebaud, <g.gael@free.fr> # Copyright (c) 2008, 2009 Gael Guennebaud, <g.gael@free.fr>
@ -62,6 +68,9 @@ if (EIGEN3_INCLUDE_DIR)
else (EIGEN3_INCLUDE_DIR) else (EIGEN3_INCLUDE_DIR)
find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library
HINTS
ENV EIGEN3_ROOT
ENV EIGEN3_ROOT_DIR
PATHS PATHS
${CMAKE_INSTALL_PREFIX}/include ${CMAKE_INSTALL_PREFIX}/include
${KDE4_INCLUDE_DIR} ${KDE4_INCLUDE_DIR}

View File

@ -97,6 +97,7 @@ add_dependencies(doc-unsupported-prerequisites unsupported_snippets unsupported_
add_custom_target(doc ALL add_custom_target(doc ALL
COMMAND doxygen COMMAND doxygen
COMMAND doxygen Doxyfile-unsupported COMMAND doxygen Doxyfile-unsupported
COMMAND ${CMAKE_COMMAND} -E copy ${Eigen_BINARY_DIR}/doc/html/group__TopicUnalignedArrayAssert.html ${Eigen_BINARY_DIR}/doc/html/TopicUnalignedArrayAssert.html
COMMAND ${CMAKE_COMMAND} -E rename html eigen-doc COMMAND ${CMAKE_COMMAND} -E rename html eigen-doc
COMMAND ${CMAKE_COMMAND} -E remove eigen-doc/eigen-doc.tgz COMMAND ${CMAKE_COMMAND} -E remove eigen-doc/eigen-doc.tgz
COMMAND ${CMAKE_COMMAND} -E tar cfz eigen-doc/eigen-doc.tgz eigen-doc COMMAND ${CMAKE_COMMAND} -E tar cfz eigen-doc/eigen-doc.tgz eigen-doc

View File

@ -6,12 +6,10 @@ foreach(example_src ${examples_SRCS})
if(EIGEN_STANDARD_LIBRARIES_TO_LINK_TO) if(EIGEN_STANDARD_LIBRARIES_TO_LINK_TO)
target_link_libraries(${example} ${EIGEN_STANDARD_LIBRARIES_TO_LINK_TO}) target_link_libraries(${example} ${EIGEN_STANDARD_LIBRARIES_TO_LINK_TO})
endif() endif()
get_target_property(example_executable
${example} LOCATION)
add_custom_command( add_custom_command(
TARGET ${example} TARGET ${example}
POST_BUILD POST_BUILD
COMMAND ${example_executable} COMMAND ${example}
ARGS >${CMAKE_CURRENT_BINARY_DIR}/${example}.out ARGS >${CMAKE_CURRENT_BINARY_DIR}/${example}.out
) )
add_dependencies(all_examples ${example}) add_dependencies(all_examples ${example})

View File

@ -14,12 +14,10 @@ foreach(snippet_src ${snippets_SRCS})
if(EIGEN_STANDARD_LIBRARIES_TO_LINK_TO) if(EIGEN_STANDARD_LIBRARIES_TO_LINK_TO)
target_link_libraries(${compile_snippet_target} ${EIGEN_STANDARD_LIBRARIES_TO_LINK_TO}) target_link_libraries(${compile_snippet_target} ${EIGEN_STANDARD_LIBRARIES_TO_LINK_TO})
endif() endif()
get_target_property(compile_snippet_executable
${compile_snippet_target} LOCATION)
add_custom_command( add_custom_command(
TARGET ${compile_snippet_target} TARGET ${compile_snippet_target}
POST_BUILD POST_BUILD
COMMAND ${compile_snippet_executable} COMMAND ${compile_snippet_target}
ARGS >${CMAKE_CURRENT_BINARY_DIR}/${snippet}.out ARGS >${CMAKE_CURRENT_BINARY_DIR}/${snippet}.out
) )
add_dependencies(all_snippets ${compile_snippet_target}) add_dependencies(all_snippets ${compile_snippet_target})

View File

@ -0,0 +1,7 @@
typedef Matrix<double,4,Dynamic> Matrix4Xd;
Matrix4Xd M = Matrix4Xd::Random(4,5);
Projective3d P(Matrix4d::Random());
cout << "The matrix M is:" << endl << M << endl << endl;
cout << "M.colwise().hnormalized():" << endl << M.colwise().hnormalized() << endl << endl;
cout << "P*M:" << endl << P*M << endl << endl;
cout << "(P*M).colwise().hnormalized():" << endl << (P*M).colwise().hnormalized() << endl << endl;

View File

@ -0,0 +1,6 @@
Vector4d v = Vector4d::Random();
Projective3d P(Matrix4d::Random());
cout << "v = " << v.transpose() << "]^T" << endl;
cout << "v.hnormalized() = " << v.hnormalized().transpose() << "]^T" << endl;
cout << "P*v = " << (P*v).transpose() << "]^T" << endl;
cout << "(P*v).hnormalized() = " << (P*v).hnormalized().transpose() << "]^T" << endl;

View File

@ -0,0 +1,6 @@
Vector3d v = Vector3d::Random(), w;
Projective3d P(Matrix4d::Random());
cout << "v = [" << v.transpose() << "]^T" << endl;
cout << "h.homogeneous() = [" << v.homogeneous().transpose() << "]^T" << endl;
cout << "(P * v.homogeneous()) = [" << (P * v.homogeneous()).transpose() << "]^T" << endl;
cout << "(P * v.homogeneous()).hnormalized() = [" << (P * v.homogeneous()).eval().hnormalized().transpose() << "]^T" << endl;

View File

@ -0,0 +1,7 @@
typedef Matrix<double,3,Dynamic> Matrix3Xd;
Matrix3Xd M = Matrix3Xd::Random(3,5);
Projective3d P(Matrix4d::Random());
cout << "The matrix M is:" << endl << M << endl << endl;
cout << "M.colwise().homogeneous():" << endl << M.colwise().homogeneous() << endl << endl;
cout << "P * M.colwise().homogeneous():" << endl << P * M.colwise().homogeneous() << endl << endl;
cout << "P * M.colwise().homogeneous().hnormalized(): " << endl << (P * M.colwise().homogeneous()).colwise().hnormalized() << endl << endl;

View File

@ -1,4 +1,3 @@
if(NOT EIGEN_TEST_NOQT) if(NOT EIGEN_TEST_NOQT)
find_package(Qt4) find_package(Qt4)
if(QT4_FOUND) if(QT4_FOUND)
@ -6,7 +5,6 @@ if(NOT EIGEN_TEST_NOQT)
endif() endif()
endif(NOT EIGEN_TEST_NOQT) endif(NOT EIGEN_TEST_NOQT)
if(QT4_FOUND) if(QT4_FOUND)
add_executable(Tutorial_sparse_example Tutorial_sparse_example.cpp Tutorial_sparse_example_details.cpp) add_executable(Tutorial_sparse_example Tutorial_sparse_example.cpp Tutorial_sparse_example_details.cpp)
target_link_libraries(Tutorial_sparse_example ${EIGEN_STANDARD_LIBRARIES_TO_LINK_TO} ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY}) target_link_libraries(Tutorial_sparse_example ${EIGEN_STANDARD_LIBRARIES_TO_LINK_TO} ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY})
@ -27,14 +25,10 @@ if(EIGEN_COMPILER_SUPPORT_CPP11)
add_dependencies(all_examples random_cpp11) add_dependencies(all_examples random_cpp11)
ei_add_target_property(random_cpp11 COMPILE_FLAGS "-std=c++11") ei_add_target_property(random_cpp11 COMPILE_FLAGS "-std=c++11")
get_target_property(random_cpp11_exec
random_cpp11 LOCATION)
add_custom_command( add_custom_command(
TARGET random_cpp11 TARGET random_cpp11
POST_BUILD POST_BUILD
COMMAND ${random_cpp11_exec} COMMAND random_cpp11
ARGS >${CMAKE_CURRENT_BINARY_DIR}/random_cpp11.out ARGS >${CMAKE_CURRENT_BINARY_DIR}/random_cpp11.out
) )
endif() endif()

View File

@ -29,7 +29,21 @@ template<typename MatrixType> void selfadjointeigensolver(const MatrixType& m)
MatrixType a = MatrixType::Random(rows,cols); MatrixType a = MatrixType::Random(rows,cols);
MatrixType a1 = MatrixType::Random(rows,cols); MatrixType a1 = MatrixType::Random(rows,cols);
MatrixType symmA = a.adjoint() * a + a1.adjoint() * a1; MatrixType symmA = a.adjoint() * a + a1.adjoint() * a1;
MatrixType symmC = symmA;
// randomly nullify some rows/columns
{
Index count = 1;//internal::random<Index>(-cols,cols);
for(Index k=0; k<count; ++k)
{
Index i = internal::random<Index>(0,cols-1);
symmA.row(i).setZero();
symmA.col(i).setZero();
}
}
symmA.template triangularView<StrictlyUpper>().setZero(); symmA.template triangularView<StrictlyUpper>().setZero();
symmC.template triangularView<StrictlyUpper>().setZero();
MatrixType b = MatrixType::Random(rows,cols); MatrixType b = MatrixType::Random(rows,cols);
MatrixType b1 = MatrixType::Random(rows,cols); MatrixType b1 = MatrixType::Random(rows,cols);
@ -40,7 +54,7 @@ template<typename MatrixType> void selfadjointeigensolver(const MatrixType& m)
SelfAdjointEigenSolver<MatrixType> eiDirect; SelfAdjointEigenSolver<MatrixType> eiDirect;
eiDirect.computeDirect(symmA); eiDirect.computeDirect(symmA);
// generalized eigen pb // generalized eigen pb
GeneralizedSelfAdjointEigenSolver<MatrixType> eiSymmGen(symmA, symmB); GeneralizedSelfAdjointEigenSolver<MatrixType> eiSymmGen(symmC, symmB);
VERIFY_IS_EQUAL(eiSymm.info(), Success); VERIFY_IS_EQUAL(eiSymm.info(), Success);
VERIFY((symmA.template selfadjointView<Lower>() * eiSymm.eigenvectors()).isApprox( VERIFY((symmA.template selfadjointView<Lower>() * eiSymm.eigenvectors()).isApprox(
@ -57,27 +71,28 @@ template<typename MatrixType> void selfadjointeigensolver(const MatrixType& m)
VERIFY_IS_APPROX(eiSymm.eigenvalues(), eiSymmNoEivecs.eigenvalues()); VERIFY_IS_APPROX(eiSymm.eigenvalues(), eiSymmNoEivecs.eigenvalues());
// generalized eigen problem Ax = lBx // generalized eigen problem Ax = lBx
eiSymmGen.compute(symmA, symmB,Ax_lBx); eiSymmGen.compute(symmC, symmB,Ax_lBx);
VERIFY_IS_EQUAL(eiSymmGen.info(), Success); VERIFY_IS_EQUAL(eiSymmGen.info(), Success);
VERIFY((symmA.template selfadjointView<Lower>() * eiSymmGen.eigenvectors()).isApprox( VERIFY((symmC.template selfadjointView<Lower>() * eiSymmGen.eigenvectors()).isApprox(
symmB.template selfadjointView<Lower>() * (eiSymmGen.eigenvectors() * eiSymmGen.eigenvalues().asDiagonal()), largerEps)); symmB.template selfadjointView<Lower>() * (eiSymmGen.eigenvectors() * eiSymmGen.eigenvalues().asDiagonal()), largerEps));
// generalized eigen problem BAx = lx // generalized eigen problem BAx = lx
eiSymmGen.compute(symmA, symmB,BAx_lx); eiSymmGen.compute(symmC, symmB,BAx_lx);
VERIFY_IS_EQUAL(eiSymmGen.info(), Success); VERIFY_IS_EQUAL(eiSymmGen.info(), Success);
VERIFY((symmB.template selfadjointView<Lower>() * (symmA.template selfadjointView<Lower>() * eiSymmGen.eigenvectors())).isApprox( VERIFY((symmB.template selfadjointView<Lower>() * (symmC.template selfadjointView<Lower>() * eiSymmGen.eigenvectors())).isApprox(
(eiSymmGen.eigenvectors() * eiSymmGen.eigenvalues().asDiagonal()), largerEps)); (eiSymmGen.eigenvectors() * eiSymmGen.eigenvalues().asDiagonal()), largerEps));
// generalized eigen problem ABx = lx // generalized eigen problem ABx = lx
eiSymmGen.compute(symmA, symmB,ABx_lx); eiSymmGen.compute(symmC, symmB,ABx_lx);
VERIFY_IS_EQUAL(eiSymmGen.info(), Success); VERIFY_IS_EQUAL(eiSymmGen.info(), Success);
VERIFY((symmA.template selfadjointView<Lower>() * (symmB.template selfadjointView<Lower>() * eiSymmGen.eigenvectors())).isApprox( VERIFY((symmC.template selfadjointView<Lower>() * (symmB.template selfadjointView<Lower>() * eiSymmGen.eigenvectors())).isApprox(
(eiSymmGen.eigenvectors() * eiSymmGen.eigenvalues().asDiagonal()), largerEps)); (eiSymmGen.eigenvectors() * eiSymmGen.eigenvalues().asDiagonal()), largerEps));
eiSymm.compute(symmC);
MatrixType sqrtSymmA = eiSymm.operatorSqrt(); MatrixType sqrtSymmA = eiSymm.operatorSqrt();
VERIFY_IS_APPROX(MatrixType(symmA.template selfadjointView<Lower>()), sqrtSymmA*sqrtSymmA); VERIFY_IS_APPROX(MatrixType(symmC.template selfadjointView<Lower>()), sqrtSymmA*sqrtSymmA);
VERIFY_IS_APPROX(sqrtSymmA, symmA.template selfadjointView<Lower>()*eiSymm.operatorInverseSqrt()); VERIFY_IS_APPROX(sqrtSymmA, symmC.template selfadjointView<Lower>()*eiSymm.operatorInverseSqrt());
MatrixType id = MatrixType::Identity(rows, cols); MatrixType id = MatrixType::Identity(rows, cols);
VERIFY_IS_APPROX(id.template selfadjointView<Lower>().operatorNorm(), RealScalar(1)); VERIFY_IS_APPROX(id.template selfadjointView<Lower>().operatorNorm(), RealScalar(1));
@ -95,9 +110,9 @@ template<typename MatrixType> void selfadjointeigensolver(const MatrixType& m)
VERIFY_RAISES_ASSERT(eiSymmUninitialized.operatorInverseSqrt()); VERIFY_RAISES_ASSERT(eiSymmUninitialized.operatorInverseSqrt());
// test Tridiagonalization's methods // test Tridiagonalization's methods
Tridiagonalization<MatrixType> tridiag(symmA); Tridiagonalization<MatrixType> tridiag(symmC);
// FIXME tridiag.matrixQ().adjoint() does not work // FIXME tridiag.matrixQ().adjoint() does not work
VERIFY_IS_APPROX(MatrixType(symmA.template selfadjointView<Lower>()), tridiag.matrixQ() * tridiag.matrixT().eval() * MatrixType(tridiag.matrixQ()).adjoint()); VERIFY_IS_APPROX(MatrixType(symmC.template selfadjointView<Lower>()), tridiag.matrixQ() * tridiag.matrixT().eval() * MatrixType(tridiag.matrixQ()).adjoint());
// Test computation of eigenvalues from tridiagonal matrix // Test computation of eigenvalues from tridiagonal matrix
if(rows > 1) if(rows > 1)
@ -111,8 +126,8 @@ template<typename MatrixType> void selfadjointeigensolver(const MatrixType& m)
if (rows > 1) if (rows > 1)
{ {
// Test matrix with NaN // Test matrix with NaN
symmA(0,0) = std::numeric_limits<typename MatrixType::RealScalar>::quiet_NaN(); symmC(0,0) = std::numeric_limits<typename MatrixType::RealScalar>::quiet_NaN();
SelfAdjointEigenSolver<MatrixType> eiSymmNaN(symmA); SelfAdjointEigenSolver<MatrixType> eiSymmNaN(symmC);
VERIFY_IS_EQUAL(eiSymmNaN.info(), NoConvergence); VERIFY_IS_EQUAL(eiSymmNaN.info(), NoConvergence);
} }
} }
@ -122,8 +137,10 @@ void test_eigensolver_selfadjoint()
int s = 0; int s = 0;
for(int i = 0; i < g_repeat; i++) { for(int i = 0; i < g_repeat; i++) {
// very important to test 3x3 and 2x2 matrices since we provide special paths for them // very important to test 3x3 and 2x2 matrices since we provide special paths for them
CALL_SUBTEST_1( selfadjointeigensolver(Matrix2f()) );
CALL_SUBTEST_1( selfadjointeigensolver(Matrix2d()) ); CALL_SUBTEST_1( selfadjointeigensolver(Matrix2d()) );
CALL_SUBTEST_1( selfadjointeigensolver(Matrix3f()) ); CALL_SUBTEST_1( selfadjointeigensolver(Matrix3f()) );
CALL_SUBTEST_1( selfadjointeigensolver(Matrix3d()) );
CALL_SUBTEST_2( selfadjointeigensolver(Matrix4d()) ); CALL_SUBTEST_2( selfadjointeigensolver(Matrix4d()) );
s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/4); s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/4);
CALL_SUBTEST_3( selfadjointeigensolver(MatrixXf(s,s)) ); CALL_SUBTEST_3( selfadjointeigensolver(MatrixXf(s,s)) );

View File

@ -315,16 +315,30 @@ void jacobisvd_inf_nan()
VERIFY(sub(some_inf, some_inf) != sub(some_inf, some_inf)); VERIFY(sub(some_inf, some_inf) != sub(some_inf, some_inf));
svd.compute(MatrixType::Constant(10,10,some_inf), ComputeFullU | ComputeFullV); svd.compute(MatrixType::Constant(10,10,some_inf), ComputeFullU | ComputeFullV);
Scalar some_nan = zero<Scalar>() / zero<Scalar>(); Scalar nan = std::numeric_limits<Scalar>::quiet_NaN();
VERIFY(some_nan != some_nan); VERIFY(nan != nan);
svd.compute(MatrixType::Constant(10,10,some_nan), ComputeFullU | ComputeFullV); svd.compute(MatrixType::Constant(10,10,nan), ComputeFullU | ComputeFullV);
MatrixType m = MatrixType::Zero(10,10); MatrixType m = MatrixType::Zero(10,10);
m(internal::random<int>(0,9), internal::random<int>(0,9)) = some_inf; m(internal::random<int>(0,9), internal::random<int>(0,9)) = some_inf;
svd.compute(m, ComputeFullU | ComputeFullV); svd.compute(m, ComputeFullU | ComputeFullV);
m = MatrixType::Zero(10,10); m = MatrixType::Zero(10,10);
m(internal::random<int>(0,9), internal::random<int>(0,9)) = some_nan; m(internal::random<int>(0,9), internal::random<int>(0,9)) = nan;
svd.compute(m, ComputeFullU | ComputeFullV);
// regression test for bug 791
m.resize(3,3);
m << 0, 2*NumTraits<Scalar>::epsilon(), 0.5,
0, -0.5, 0,
nan, 0, 0;
svd.compute(m, ComputeFullU | ComputeFullV);
m.resize(4,4);
m << 1, 0, 0, 0,
0, 3, 1, 2e-308,
1, 0, 1, nan,
0, nan, nan, 0;
svd.compute(m, ComputeFullU | ComputeFullV); svd.compute(m, ComputeFullU | ComputeFullV);
} }
@ -340,11 +354,33 @@ void jacobisvd_underoverflow()
Matrix2d M; Matrix2d M;
M << -7.90884e-313, -4.94e-324, M << -7.90884e-313, -4.94e-324,
0, 5.60844e-313; 0, 5.60844e-313;
JacobiSVD<Matrix2d> svd;
svd.compute(M,ComputeFullU|ComputeFullV);
jacobisvd_check_full(M,svd);
VectorXd value_set(9);
value_set << 0, 1, -1, 5.60844e-313, -5.60844e-313, 4.94e-324, -4.94e-324, -4.94e-223, 4.94e-223;
Array4i id(0,0,0,0);
int k = 0;
do
{
M << value_set(id(0)), value_set(id(1)), value_set(id(2)), value_set(id(3));
svd.compute(M,ComputeFullU|ComputeFullV);
jacobisvd_check_full(M,svd);
id(k)++;
if(id(k)>=value_set.size())
{
while(k<3 && id(k)>=value_set.size()) id(++k)++;
id.head(k).setZero();
k=0;
}
} while((id<int(value_set.size())).all());
#if defined __INTEL_COMPILER #if defined __INTEL_COMPILER
#pragma warning pop #pragma warning pop
#endif #endif
JacobiSVD<Matrix2d> svd;
svd.compute(M); // just check we don't loop indefinitely
// Check for overflow: // Check for overflow:
Matrix3d M3; Matrix3d M3;
@ -353,7 +389,8 @@ void jacobisvd_underoverflow()
-8.7190887618028355e+307, -7.3453213709232193e+307, -2.4367363684472105e+307; -8.7190887618028355e+307, -7.3453213709232193e+307, -2.4367363684472105e+307;
JacobiSVD<Matrix3d> svd3; JacobiSVD<Matrix3d> svd3;
svd3.compute(M3); // just check we don't loop indefinitely svd3.compute(M3,ComputeFullU|ComputeFullV); // just check we don't loop indefinitely
jacobisvd_check_full(M3,svd3);
} }
void jacobisvd_preallocate() void jacobisvd_preallocate()
@ -437,6 +474,7 @@ void test_jacobisvd()
// Test on inf/nan matrix // Test on inf/nan matrix
CALL_SUBTEST_7( jacobisvd_inf_nan<MatrixXf>() ); CALL_SUBTEST_7( jacobisvd_inf_nan<MatrixXf>() );
CALL_SUBTEST_10( jacobisvd_inf_nan<MatrixXd>() );
} }
CALL_SUBTEST_7(( jacobisvd<MatrixXf>(MatrixXf(internal::random<int>(EIGEN_TEST_MAX_SIZE/4, EIGEN_TEST_MAX_SIZE/2), internal::random<int>(EIGEN_TEST_MAX_SIZE/4, EIGEN_TEST_MAX_SIZE/2))) )); CALL_SUBTEST_7(( jacobisvd<MatrixXf>(MatrixXf(internal::random<int>(EIGEN_TEST_MAX_SIZE/4, EIGEN_TEST_MAX_SIZE/2), internal::random<int>(EIGEN_TEST_MAX_SIZE/4, EIGEN_TEST_MAX_SIZE/2))) ));

View File

@ -2,11 +2,16 @@
// for linear algebra. // for linear algebra.
// //
// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> // Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com>
// Copyright (C) 2014 Gael Guennebaud <gael.guennebaud@inria.fr>
// //
// This Source Code Form is subject to the terms of the Mozilla // This Source Code Form is subject to the terms of the Mozilla
// 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/.
static bool g_called;
#define EIGEN_SPECIAL_SCALAR_MULTIPLE_PLUGIN { g_called = true; }
#include "main.h" #include "main.h"
template<typename MatrixType> void linearStructure(const MatrixType& m) template<typename MatrixType> void linearStructure(const MatrixType& m)
@ -68,6 +73,24 @@ template<typename MatrixType> void linearStructure(const MatrixType& m)
VERIFY_IS_APPROX(m1.block(0,0,rows,cols) * s1, m1 * s1); VERIFY_IS_APPROX(m1.block(0,0,rows,cols) * s1, m1 * s1);
} }
// Make sure that complex * real and real * complex are properly optimized
template<typename MatrixType> void real_complex(DenseIndex rows = MatrixType::RowsAtCompileTime, DenseIndex cols = MatrixType::ColsAtCompileTime)
{
typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::RealScalar RealScalar;
RealScalar s = internal::random<RealScalar>();
MatrixType m1 = MatrixType::Random(rows, cols);
g_called = false;
VERIFY_IS_APPROX(s*m1, Scalar(s)*m1);
VERIFY(g_called && "real * matrix<complex> not properly optimized");
g_called = false;
VERIFY_IS_APPROX(m1*s, m1*Scalar(s));
VERIFY(g_called && "matrix<complex> * real not properly optimized");
}
void test_linearstructure() void test_linearstructure()
{ {
for(int i = 0; i < g_repeat; i++) { for(int i = 0; i < g_repeat; i++) {
@ -80,5 +103,8 @@ void test_linearstructure()
CALL_SUBTEST_7( linearStructure(MatrixXi (internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); CALL_SUBTEST_7( linearStructure(MatrixXi (internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) );
CALL_SUBTEST_8( linearStructure(MatrixXcd(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2), internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2))) ); CALL_SUBTEST_8( linearStructure(MatrixXcd(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2), internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2))) );
CALL_SUBTEST_9( linearStructure(ArrayXXf (internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); CALL_SUBTEST_9( linearStructure(ArrayXXf (internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) );
CALL_SUBTEST_10( real_complex<Matrix4cd>() );
CALL_SUBTEST_10( real_complex<MatrixXcf>(10,10) );
} }
} }

View File

@ -17,13 +17,36 @@
#include <sstream> #include <sstream>
#include <vector> #include <vector>
#include <typeinfo> #include <typeinfo>
// The following includes of STL headers have to be done _before_ the
// definition of macros min() and max(). The reason is that many STL
// implementations will not work properly as the min and max symbols collide
// with the STL functions std:min() and std::max(). The STL headers may check
// for the macro definition of min/max and issue a warning or undefine the
// macros.
//
// Still, Windows defines min() and max() in windef.h as part of the regular
// Windows system interfaces and many other Windows APIs depend on these
// macros being available. To prevent the macro expansion of min/max and to
// make Eigen compatible with the Windows environment all function calls of
// std::min() and std::max() have to be written with parenthesis around the
// function name.
//
// All STL headers used by Eigen should be included here. Because main.h is
// included before any Eigen header and because the STL headers are guarded
// against multiple inclusions, no STL header will see our own min/max macro
// definitions.
#include <limits> #include <limits>
#include <algorithm> #include <algorithm>
#include <sstream>
#include <complex> #include <complex>
#include <deque> #include <deque>
#include <queue> #include <queue>
#include <list>
// To test that all calls from Eigen code to std::min() and std::max() are
// protected by parenthesis against macro expansion, the min()/max() macros
// are defined here and any not-parenthesized min/max call will cause a
// compiler error.
#define min(A,B) please_protect_your_min_with_parentheses #define min(A,B) please_protect_your_min_with_parentheses
#define max(A,B) please_protect_your_max_with_parentheses #define max(A,B) please_protect_your_max_with_parentheses
@ -76,6 +99,10 @@ namespace Eigen
#define EIGEN_DEFAULT_IO_FORMAT IOFormat(4, 0, " ", "\n", "", "", "", "") #define EIGEN_DEFAULT_IO_FORMAT IOFormat(4, 0, " ", "\n", "", "", "", "")
#if (defined(_CPPUNWIND) || defined(__EXCEPTIONS)) && !defined(__CUDA_ARCH__)
#define EIGEN_EXCEPTIONS
#endif
#ifndef EIGEN_NO_ASSERTION_CHECKING #ifndef EIGEN_NO_ASSERTION_CHECKING
namespace Eigen namespace Eigen
@ -172,7 +199,7 @@ namespace Eigen
#ifndef VERIFY_RAISES_ASSERT #ifndef VERIFY_RAISES_ASSERT
#define VERIFY_RAISES_ASSERT(a) \ #define VERIFY_RAISES_ASSERT(a) \
std::cout << "Can't VERIFY_RAISES_ASSERT( " #a " ) with exceptions disabled"; std::cout << "Can't VERIFY_RAISES_ASSERT( " #a " ) with exceptions disabled\n";
#endif #endif
#if !defined(__CUDACC__) #if !defined(__CUDACC__)

View File

@ -139,4 +139,12 @@ template<typename MatrixType> void product(const MatrixType& m)
// inner product // inner product
Scalar x = square2.row(c) * square2.col(c2); Scalar x = square2.row(c) * square2.col(c2);
VERIFY_IS_APPROX(x, square2.row(c).transpose().cwiseProduct(square2.col(c2)).sum()); VERIFY_IS_APPROX(x, square2.row(c).transpose().cwiseProduct(square2.col(c2)).sum());
// outer product
VERIFY_IS_APPROX(m1.col(c) * m1.row(r), m1.block(0,c,rows,1) * m1.block(r,0,1,cols));
VERIFY_IS_APPROX(m1.row(r).transpose() * m1.col(c).transpose(), m1.block(r,0,1,cols).transpose() * m1.block(0,c,rows,1).transpose());
VERIFY_IS_APPROX(m1.block(0,c,rows,1) * m1.row(r), m1.block(0,c,rows,1) * m1.block(r,0,1,cols));
VERIFY_IS_APPROX(m1.col(c) * m1.block(r,0,1,cols), m1.block(0,c,rows,1) * m1.block(r,0,1,cols));
VERIFY_IS_APPROX(m1.leftCols(1) * m1.row(r), m1.block(0,0,rows,1) * m1.block(r,0,1,cols));
VERIFY_IS_APPROX(m1.col(c) * m1.topRows(1), m1.block(0,c,rows,1) * m1.block(0,0,1,cols));
} }

View File

@ -54,6 +54,8 @@ template<typename Scalar> void test_sparseqr_scalar()
b = dA * DenseVector::Random(A.cols()); b = dA * DenseVector::Random(A.cols());
solver.compute(A); solver.compute(A);
if(internal::random<float>(0,1)>0.5)
solver.factorize(A); // this checks that calling analyzePattern is not needed if the pattern do not change.
if (solver.info() != Success) if (solver.info() != Success)
{ {
std::cerr << "sparse QR factorization failed\n"; std::cerr << "sparse QR factorization failed\n";

View File

@ -1,7 +1,7 @@
// This file is part of Eigen, a lightweight C++ template library // This file is part of Eigen, a lightweight C++ template library
// for linear algebra. // for linear algebra.
// //
// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr> // Copyright (C) 2009-2014 Gael Guennebaud <gael.guennebaud@inria.fr>
// //
// This Source Code Form is subject to the terms of the Mozilla // This Source Code Form is subject to the terms of the Mozilla
// 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
@ -14,6 +14,21 @@ template<typename T> bool isNotNaN(const T& x)
return x==x; return x==x;
} }
template<typename T> bool isNaN(const T& x)
{
return x!=x;
}
template<typename T> bool isInf(const T& x)
{
return x > NumTraits<T>::highest();
}
template<typename T> bool isMinusInf(const T& x)
{
return x < NumTraits<T>::lowest();
}
// workaround aggressive optimization in ICC // workaround aggressive optimization in ICC
template<typename T> EIGEN_DONT_INLINE T sub(T a, T b) { return a - b; } template<typename T> EIGEN_DONT_INLINE T sub(T a, T b) { return a - b; }
@ -106,6 +121,58 @@ template<typename MatrixType> void stable_norm(const MatrixType& m)
VERIFY_IS_APPROX(vrand.rowwise().stableNorm(), vrand.rowwise().norm()); VERIFY_IS_APPROX(vrand.rowwise().stableNorm(), vrand.rowwise().norm());
VERIFY_IS_APPROX(vrand.rowwise().blueNorm(), vrand.rowwise().norm()); VERIFY_IS_APPROX(vrand.rowwise().blueNorm(), vrand.rowwise().norm());
VERIFY_IS_APPROX(vrand.rowwise().hypotNorm(), vrand.rowwise().norm()); VERIFY_IS_APPROX(vrand.rowwise().hypotNorm(), vrand.rowwise().norm());
// test NaN, +inf, -inf
MatrixType v;
Index i = internal::random<Index>(0,rows-1);
Index j = internal::random<Index>(0,cols-1);
// NaN
{
v = vrand;
v(i,j) = RealScalar(0)/RealScalar(0);
VERIFY(!isFinite(v.squaredNorm())); VERIFY(isNaN(v.squaredNorm()));
VERIFY(!isFinite(v.norm())); VERIFY(isNaN(v.norm()));
VERIFY(!isFinite(v.stableNorm())); VERIFY(isNaN(v.stableNorm()));
VERIFY(!isFinite(v.blueNorm())); VERIFY(isNaN(v.blueNorm()));
VERIFY(!isFinite(v.hypotNorm())); VERIFY(isNaN(v.hypotNorm()));
}
// +inf
{
v = vrand;
v(i,j) = RealScalar(1)/RealScalar(0);
VERIFY(!isFinite(v.squaredNorm())); VERIFY(isInf(v.squaredNorm()));
VERIFY(!isFinite(v.norm())); VERIFY(isInf(v.norm()));
VERIFY(!isFinite(v.stableNorm())); VERIFY(isInf(v.stableNorm()));
VERIFY(!isFinite(v.blueNorm())); VERIFY(isInf(v.blueNorm()));
VERIFY(!isFinite(v.hypotNorm())); VERIFY(isInf(v.hypotNorm()));
}
// -inf
{
v = vrand;
v(i,j) = RealScalar(-1)/RealScalar(0);
VERIFY(!isFinite(v.squaredNorm())); VERIFY(isInf(v.squaredNorm()));
VERIFY(!isFinite(v.norm())); VERIFY(isInf(v.norm()));
VERIFY(!isFinite(v.stableNorm())); VERIFY(isInf(v.stableNorm()));
VERIFY(!isFinite(v.blueNorm())); VERIFY(isInf(v.blueNorm()));
VERIFY(!isFinite(v.hypotNorm())); VERIFY(isInf(v.hypotNorm()));
}
// mix
{
Index i2 = internal::random<Index>(0,rows-1);
Index j2 = internal::random<Index>(0,cols-1);
v = vrand;
v(i,j) = RealScalar(-1)/RealScalar(0);
v(i2,j2) = RealScalar(0)/RealScalar(0);
VERIFY(!isFinite(v.squaredNorm())); VERIFY(isNaN(v.squaredNorm()));
VERIFY(!isFinite(v.norm())); VERIFY(isNaN(v.norm()));
VERIFY(!isFinite(v.stableNorm())); VERIFY(isNaN(v.stableNorm()));
VERIFY(!isFinite(v.blueNorm())); VERIFY(isNaN(v.blueNorm()));
VERIFY(!isFinite(v.hypotNorm())); VERIFY(isNaN(v.hypotNorm()));
}
} }
void test_stable_norm() void test_stable_norm()

View File

@ -35,7 +35,7 @@ void test_upperbidiagonalization()
CALL_SUBTEST_1( upperbidiag(MatrixXf(3,3)) ); CALL_SUBTEST_1( upperbidiag(MatrixXf(3,3)) );
CALL_SUBTEST_2( upperbidiag(MatrixXd(17,12)) ); CALL_SUBTEST_2( upperbidiag(MatrixXd(17,12)) );
CALL_SUBTEST_3( upperbidiag(MatrixXcf(20,20)) ); CALL_SUBTEST_3( upperbidiag(MatrixXcf(20,20)) );
CALL_SUBTEST_4( upperbidiag(MatrixXcd(16,15)) ); CALL_SUBTEST_4( upperbidiag(Matrix<std::complex<double>,Dynamic,Dynamic,RowMajor>(16,15)) );
CALL_SUBTEST_5( upperbidiag(Matrix<float,6,4>()) ); CALL_SUBTEST_5( upperbidiag(Matrix<float,6,4>()) );
CALL_SUBTEST_6( upperbidiag(Matrix<float,5,5>()) ); CALL_SUBTEST_6( upperbidiag(Matrix<float,5,5>()) );
CALL_SUBTEST_7( upperbidiag(Matrix<double,4,3>()) ); CALL_SUBTEST_7( upperbidiag(Matrix<double,4,3>()) );

26
unsupported/Eigen/BDCSVD Normal file
View File

@ -0,0 +1,26 @@
#ifndef EIGEN_BDCSVD_MODULE_H
#define EIGEN_BDCSVD_MODULE_H
#include <Eigen/SVD>
#include "../../Eigen/src/Core/util/DisableStupidWarnings.h"
/** \defgroup BDCSVD_Module BDCSVD module
*
*
*
* This module provides Divide & Conquer SVD decomposition for matrices (both real and complex).
* This decomposition is accessible via the following MatrixBase method:
* - MatrixBase::bdcSvd()
*
* \code
* #include <Eigen/BDCSVD>
* \endcode
*/
#include "src/BDCSVD/BDCSVD.h"
#include "../../Eigen/src/Core/util/ReenableStupidWarnings.h"
#endif // EIGEN_BDCSVD_MODULE_H
/* vim: set filetype=cpp et sw=2 ts=2 ai: */

View File

@ -82,7 +82,9 @@ const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::cos() const
\param[in] M a square matrix. \param[in] M a square matrix.
\returns expression representing \f$ \cos(M) \f$. \returns expression representing \f$ \cos(M) \f$.
This function calls \ref matrixbase_matrixfunction "matrixFunction()" with StdStemFunctions::cos(). This function computes the matrix cosine. Use ArrayBase::cos() for computing the entry-wise cosine.
The implementation calls \ref matrixbase_matrixfunction "matrixFunction()" with StdStemFunctions::cos().
\sa \ref matrixbase_sin "sin()" for an example. \sa \ref matrixbase_sin "sin()" for an example.
@ -123,6 +125,9 @@ differential equations: the solution of \f$ y' = My \f$ with the
initial condition \f$ y(0) = y_0 \f$ is given by initial condition \f$ y(0) = y_0 \f$ is given by
\f$ y(t) = \exp(M) y_0 \f$. \f$ y(t) = \exp(M) y_0 \f$.
The matrix exponential is different from applying the exp function to all the entries in the matrix.
Use ArrayBase::exp() if you want to do the latter.
The cost of the computation is approximately \f$ 20 n^3 \f$ for The cost of the computation is approximately \f$ 20 n^3 \f$ for
matrices of size \f$ n \f$. The number 20 depends weakly on the matrices of size \f$ n \f$. The number 20 depends weakly on the
norm of the matrix. norm of the matrix.
@ -177,6 +182,9 @@ the scalar logarithm, the equation \f$ \exp(X) = M \f$ may have
multiple solutions; this function returns a matrix whose eigenvalues multiple solutions; this function returns a matrix whose eigenvalues
have imaginary part in the interval \f$ (-\pi,\pi] \f$. have imaginary part in the interval \f$ (-\pi,\pi] \f$.
The matrix logarithm is different from applying the log function to all the entries in the matrix.
Use ArrayBase::log() if you want to do the latter.
In the real case, the matrix \f$ M \f$ should be invertible and In the real case, the matrix \f$ M \f$ should be invertible and
it should have no eigenvalues which are real and negative (pairs of it should have no eigenvalues which are real and negative (pairs of
complex conjugate eigenvalues are allowed). In the complex case, it complex conjugate eigenvalues are allowed). In the complex case, it
@ -232,7 +240,8 @@ const MatrixPowerReturnValue<Derived> MatrixBase<Derived>::pow(RealScalar p) con
The matrix power \f$ M^p \f$ is defined as \f$ \exp(p \log(M)) \f$, The matrix power \f$ M^p \f$ is defined as \f$ \exp(p \log(M)) \f$,
where exp denotes the matrix exponential, and log denotes the matrix where exp denotes the matrix exponential, and log denotes the matrix
logarithm. logarithm. This is different from raising all the entries in the matrix
to the p-th power. Use ArrayBase::pow() if you want to do the latter.
If \p p is complex, the scalar type of \p M should be the type of \p If \p p is complex, the scalar type of \p M should be the type of \p
p . \f$ M^p \f$ simply evaluates into \f$ \exp(p \log(M)) \f$. p . \f$ M^p \f$ simply evaluates into \f$ \exp(p \log(M)) \f$.
@ -391,7 +400,9 @@ const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::sin() const
\param[in] M a square matrix. \param[in] M a square matrix.
\returns expression representing \f$ \sin(M) \f$. \returns expression representing \f$ \sin(M) \f$.
This function calls \ref matrixbase_matrixfunction "matrixFunction()" with StdStemFunctions::sin(). This function computes the matrix sine. Use ArrayBase::sin() for computing the entry-wise sine.
The implementation calls \ref matrixbase_matrixfunction "matrixFunction()" with StdStemFunctions::sin().
Example: \include MatrixSine.cpp Example: \include MatrixSine.cpp
Output: \verbinclude MatrixSine.out Output: \verbinclude MatrixSine.out
@ -428,7 +439,9 @@ const MatrixSquareRootReturnValue<Derived> MatrixBase<Derived>::sqrt() const
The matrix square root of \f$ M \f$ is the matrix \f$ M^{1/2} \f$ The matrix square root of \f$ M \f$ is the matrix \f$ M^{1/2} \f$
whose square is the original matrix; so if \f$ S = M^{1/2} \f$ then whose square is the original matrix; so if \f$ S = M^{1/2} \f$ then
\f$ S^2 = M \f$. \f$ S^2 = M \f$. This is different from taking the square root of all
the entries in the matrix; use ArrayBase::sqrt() if you want to do the
latter.
In the <b>real case</b>, the matrix \f$ M \f$ should be invertible and In the <b>real case</b>, the matrix \f$ M \f$ should be invertible and
it should have no eigenvalues which are real and negative (pairs of it should have no eigenvalues which are real and negative (pairs of

View File

@ -1,35 +0,0 @@
#ifndef EIGEN_SVD_MODULE_H
#define EIGEN_SVD_MODULE_H
#include <Eigen/QR>
#include <Eigen/Householder>
#include <Eigen/Jacobi>
#include "../../Eigen/src/Core/util/DisableStupidWarnings.h"
/** \defgroup SVD_Module SVD module
*
*
*
* This module provides SVD decomposition for matrices (both real and complex).
* This decomposition is accessible via the following MatrixBase method:
* - MatrixBase::jacobiSvd()
*
* \code
* #include <Eigen/SVD>
* \endcode
*/
#include "../../Eigen/src/misc/Solve.h"
#include "../../Eigen/src/SVD/UpperBidiagonalization.h"
#include "src/SVD/SVDBase.h"
#include "src/SVD/JacobiSVD.h"
#include "src/SVD/BDCSVD.h"
#if defined(EIGEN_USE_LAPACKE) && !defined(EIGEN_USE_LAPACKE_STRICT)
#include "../../Eigen/src/SVD/JacobiSVD_MKL.h"
#endif
#include "../../Eigen/src/Core/util/ReenableStupidWarnings.h"
#endif // EIGEN_SVD_MODULE_H
/* vim: set filetype=cpp et sw=2 ts=2 ai: */

View File

@ -24,6 +24,20 @@
#define ALGOSWAP 16 #define ALGOSWAP 16
namespace Eigen { namespace Eigen {
template<typename _MatrixType> class BDCSVD;
namespace internal {
template<typename _MatrixType>
struct traits<BDCSVD<_MatrixType> >
{
typedef _MatrixType MatrixType;
};
} // end namespace internal
/** \ingroup SVD_Module /** \ingroup SVD_Module
* *
* *
@ -36,9 +50,9 @@ namespace Eigen {
* It should be used to speed up the calcul of SVD for big matrices. * It should be used to speed up the calcul of SVD for big matrices.
*/ */
template<typename _MatrixType> template<typename _MatrixType>
class BDCSVD : public SVDBase<_MatrixType> class BDCSVD : public SVDBase<BDCSVD<_MatrixType> >
{ {
typedef SVDBase<_MatrixType> Base; typedef SVDBase<BDCSVD> Base;
public: public:
using Base::rows; using Base::rows;
@ -77,9 +91,7 @@ public:
* The default constructor is useful in cases in which the user intends to * The default constructor is useful in cases in which the user intends to
* perform decompositions via BDCSVD::compute(const MatrixType&). * perform decompositions via BDCSVD::compute(const MatrixType&).
*/ */
BDCSVD() BDCSVD() : algoswap(ALGOSWAP), m_numIters(0)
: SVDBase<_MatrixType>::SVDBase(),
algoswap(ALGOSWAP), m_numIters(0)
{} {}
@ -90,8 +102,7 @@ public:
* \sa BDCSVD() * \sa BDCSVD()
*/ */
BDCSVD(Index rows, Index cols, unsigned int computationOptions = 0) BDCSVD(Index rows, Index cols, unsigned int computationOptions = 0)
: SVDBase<_MatrixType>::SVDBase(), : algoswap(ALGOSWAP), m_numIters(0)
algoswap(ALGOSWAP), m_numIters(0)
{ {
allocate(rows, cols, computationOptions); allocate(rows, cols, computationOptions);
} }
@ -107,8 +118,7 @@ public:
* available with the (non - default) FullPivHouseholderQR preconditioner. * available with the (non - default) FullPivHouseholderQR preconditioner.
*/ */
BDCSVD(const MatrixType& matrix, unsigned int computationOptions = 0) BDCSVD(const MatrixType& matrix, unsigned int computationOptions = 0)
: SVDBase<_MatrixType>::SVDBase(), : algoswap(ALGOSWAP), m_numIters(0)
algoswap(ALGOSWAP), m_numIters(0)
{ {
compute(matrix, computationOptions); compute(matrix, computationOptions);
} }
@ -116,6 +126,7 @@ public:
~BDCSVD() ~BDCSVD()
{ {
} }
/** \brief Method performing the decomposition of given matrix using custom options. /** \brief Method performing the decomposition of given matrix using custom options.
* *
* \param matrix the matrix to decompose * \param matrix the matrix to decompose
@ -126,7 +137,7 @@ public:
* Thin unitaries are only available if your matrix type has a Dynamic number of columns (for example MatrixXf). They also are not * Thin unitaries are only available if your matrix type has a Dynamic number of columns (for example MatrixXf). They also are not
* available with the (non - default) FullPivHouseholderQR preconditioner. * available with the (non - default) FullPivHouseholderQR preconditioner.
*/ */
SVDBase<MatrixType>& compute(const MatrixType& matrix, unsigned int computationOptions); BDCSVD& compute(const MatrixType& matrix, unsigned int computationOptions);
/** \brief Method performing the decomposition of given matrix using current options. /** \brief Method performing the decomposition of given matrix using current options.
* *
@ -134,7 +145,7 @@ public:
* *
* This method uses the current \a computationOptions, as already passed to the constructor or to compute(const MatrixType&, unsigned int). * This method uses the current \a computationOptions, as already passed to the constructor or to compute(const MatrixType&, unsigned int).
*/ */
SVDBase<MatrixType>& compute(const MatrixType& matrix) BDCSVD& compute(const MatrixType& matrix)
{ {
return compute(matrix, this->m_computationOptions); return compute(matrix, this->m_computationOptions);
} }
@ -160,7 +171,7 @@ public:
solve(const MatrixBase<Rhs>& b) const solve(const MatrixBase<Rhs>& b) const
{ {
eigen_assert(this->m_isInitialized && "BDCSVD is not initialized."); eigen_assert(this->m_isInitialized && "BDCSVD is not initialized.");
eigen_assert(SVDBase<_MatrixType>::computeU() && SVDBase<_MatrixType>::computeV() && eigen_assert(computeU() && computeV() &&
"BDCSVD::solve() requires both unitaries U and V to be computed (thin unitaries suffice)."); "BDCSVD::solve() requires both unitaries U and V to be computed (thin unitaries suffice).");
return internal::solve_retval<BDCSVD, Rhs>(*this, b.derived()); return internal::solve_retval<BDCSVD, Rhs>(*this, b.derived());
} }
@ -196,6 +207,9 @@ public:
} }
} }
using Base::computeU;
using Base::computeV;
private: private:
void allocate(Index rows, Index cols, unsigned int computationOptions); void allocate(Index rows, Index cols, unsigned int computationOptions);
void divide(Index firstCol, Index lastCol, Index firstRowW, Index firstColW, Index shift); void divide(Index firstCol, Index lastCol, Index firstRowW, Index firstColW, Index shift);
@ -229,7 +243,7 @@ template<typename MatrixType>
void BDCSVD<MatrixType>::allocate(Index rows, Index cols, unsigned int computationOptions) void BDCSVD<MatrixType>::allocate(Index rows, Index cols, unsigned int computationOptions)
{ {
isTranspose = (cols > rows); isTranspose = (cols > rows);
if (SVDBase<MatrixType>::allocate(rows, cols, computationOptions)) return; if (Base::allocate(rows, cols, computationOptions)) return;
m_computed = MatrixXr::Zero(this->m_diagSize + 1, this->m_diagSize ); m_computed = MatrixXr::Zero(this->m_diagSize + 1, this->m_diagSize );
if (isTranspose){ if (isTranspose){
compU = this->computeU(); compU = this->computeU();
@ -262,8 +276,7 @@ void BDCSVD<MatrixType>::allocate(Index rows, Index cols, unsigned int computati
// Methode which compute the BDCSVD for the int // Methode which compute the BDCSVD for the int
template<> template<>
SVDBase<Matrix<int, Dynamic, Dynamic> >& BDCSVD<Matrix<int, Dynamic, Dynamic> >& BDCSVD<Matrix<int, Dynamic, Dynamic> >::compute(const MatrixType& matrix, unsigned int computationOptions) {
BDCSVD<Matrix<int, Dynamic, Dynamic> >::compute(const MatrixType& matrix, unsigned int computationOptions) {
allocate(matrix.rows(), matrix.cols(), computationOptions); allocate(matrix.rows(), matrix.cols(), computationOptions);
this->m_nonzeroSingularValues = 0; this->m_nonzeroSingularValues = 0;
m_computed = Matrix<int, Dynamic, Dynamic>::Zero(rows(), cols()); m_computed = Matrix<int, Dynamic, Dynamic>::Zero(rows(), cols());
@ -279,8 +292,7 @@ BDCSVD<Matrix<int, Dynamic, Dynamic> >::compute(const MatrixType& matrix, unsign
// Methode which compute the BDCSVD // Methode which compute the BDCSVD
template<typename MatrixType> template<typename MatrixType>
SVDBase<MatrixType>& BDCSVD<MatrixType>& BDCSVD<MatrixType>::compute(const MatrixType& matrix, unsigned int computationOptions)
BDCSVD<MatrixType>::compute(const MatrixType& matrix, unsigned int computationOptions)
{ {
allocate(matrix.rows(), matrix.cols(), computationOptions); allocate(matrix.rows(), matrix.cols(), computationOptions);
using std::abs; using std::abs;

View File

@ -0,0 +1,6 @@
FILE(GLOB Eigen_BDCSVD_SRCS "*.h")
INSTALL(FILES
${Eigen_BDCSVD_SRCS}
DESTINATION ${INCLUDE_INSTALL_DIR}unsupported/Eigen/src/BDCSVD COMPONENT Devel
)

View File

@ -12,3 +12,4 @@ ADD_SUBDIRECTORY(Skyline)
ADD_SUBDIRECTORY(SparseExtra) ADD_SUBDIRECTORY(SparseExtra)
ADD_SUBDIRECTORY(KroneckerProduct) ADD_SUBDIRECTORY(KroneckerProduct)
ADD_SUBDIRECTORY(Splines) ADD_SUBDIRECTORY(Splines)
ADD_SUBDIRECTORY(BDCSVD)

View File

@ -27,7 +27,7 @@ namespace internal {
* \param iters on input: maximum number of iterations to perform * \param iters on input: maximum number of iterations to perform
* on output: number of iterations performed * on output: number of iterations performed
* \param restart number of iterations for a restart * \param restart number of iterations for a restart
* \param tol_error on input: residual tolerance * \param tol_error on input: relative residual tolerance
* on output: residuum achieved * on output: residuum achieved
* *
* \sa IterativeMethods::bicgstab() * \sa IterativeMethods::bicgstab()
@ -70,18 +70,24 @@ bool gmres(const MatrixType & mat, const Rhs & rhs, Dest & x, const Precondition
const int m = mat.rows(); const int m = mat.rows();
VectorType p0 = rhs - mat*x; // residual and preconditioned residual
const VectorType p0 = rhs - mat*x;
VectorType r0 = precond.solve(p0); VectorType r0 = precond.solve(p0);
const RealScalar r0Norm = r0.norm();
// is initial guess already good enough? // is initial guess already good enough?
if(abs(r0.norm()) < tol) { if(r0Norm == 0) {
tol_error=0;
return true; return true;
} }
// storage for Hessenberg matrix and Householder data
FMatrixType H = FMatrixType::Zero(m, restart + 1);
VectorType w = VectorType::Zero(restart + 1); VectorType w = VectorType::Zero(restart + 1);
FMatrixType H = FMatrixType::Zero(m, restart + 1); // Hessenberg matrix
VectorType tau = VectorType::Zero(restart + 1); VectorType tau = VectorType::Zero(restart + 1);
// storage for Jacobi rotations
std::vector < JacobiRotation < Scalar > > G(restart); std::vector < JacobiRotation < Scalar > > G(restart);
// generate first Householder vector // generate first Householder vector
@ -112,7 +118,6 @@ bool gmres(const MatrixType & mat, const Rhs & rhs, Dest & x, const Precondition
} }
if (v.tail(m - k).norm() != 0.0) { if (v.tail(m - k).norm() != 0.0) {
if (k <= restart) { if (k <= restart) {
// generate new Householder vector // generate new Householder vector
@ -135,19 +140,19 @@ bool gmres(const MatrixType & mat, const Rhs & rhs, Dest & x, const Precondition
} }
if (k<m && v(k) != (Scalar) 0) { if (k<m && v(k) != (Scalar) 0) {
// determine next Givens rotation // determine next Givens rotation
G[k - 1].makeGivens(v(k - 1), v(k)); G[k - 1].makeGivens(v(k - 1), v(k));
// apply Givens rotation to v and w // apply Givens rotation to v and w
v.applyOnTheLeft(k - 1, k, G[k - 1].adjoint()); v.applyOnTheLeft(k - 1, k, G[k - 1].adjoint());
w.applyOnTheLeft(k - 1, k, G[k - 1].adjoint()); w.applyOnTheLeft(k - 1, k, G[k - 1].adjoint());
} }
// insert coefficients into upper matrix triangle // insert coefficients into upper matrix triangle
H.col(k - 1).head(k) = v.head(k); H.col(k - 1).head(k) = v.head(k);
bool stop=(k==m || abs(w(k)) < tol || iters == maxIters); bool stop=(k==m || abs(w(k)) < tol * r0Norm || iters == maxIters);
if (stop || k == restart) { if (stop || k == restart) {
@ -174,13 +179,13 @@ bool gmres(const MatrixType & mat, const Rhs & rhs, Dest & x, const Precondition
} else { } else {
k=0; k=0;
// reset data for a restart r0 = rhs - mat * x; // reset data for restart
VectorType p0=mat*x; const VectorType p0 = rhs - mat*x;
VectorType p1=precond.solve(p0); r0 = precond.solve(p0);
r0 = rhs - p1;
// r0_sqnorm = r0.squaredNorm(); // clear Hessenberg matrix and Householder data
w = VectorType::Zero(restart + 1);
H = FMatrixType::Zero(m, restart + 1); H = FMatrixType::Zero(m, restart + 1);
w = VectorType::Zero(restart + 1);
tau = VectorType::Zero(restart + 1); tau = VectorType::Zero(restart + 1);
// generate first Householder vector // generate first Householder vector
@ -194,7 +199,6 @@ bool gmres(const MatrixType & mat, const Rhs & rhs, Dest & x, const Precondition
} }
} }
return false; return false;

View File

@ -145,10 +145,12 @@ class LevenbergMarquardt : internal::no_assignment_operator
/** Sets the default parameters */ /** Sets the default parameters */
void resetParameters() void resetParameters()
{ {
using std::sqrt;
m_factor = 100.; m_factor = 100.;
m_maxfev = 400; m_maxfev = 400;
m_ftol = std::sqrt(NumTraits<RealScalar>::epsilon()); m_ftol = sqrt(NumTraits<RealScalar>::epsilon());
m_xtol = std::sqrt(NumTraits<RealScalar>::epsilon()); m_xtol = sqrt(NumTraits<RealScalar>::epsilon());
m_gtol = 0. ; m_gtol = 0. ;
m_epsfcn = 0. ; m_epsfcn = 0. ;
} }

View File

@ -45,6 +45,12 @@ namespace LevenbergMarquardtSpace {
template<typename FunctorType, typename Scalar=double> template<typename FunctorType, typename Scalar=double>
class LevenbergMarquardt class LevenbergMarquardt
{ {
static Scalar sqrt_epsilon()
{
using std::sqrt;
return sqrt(NumTraits<Scalar>::epsilon());
}
public: public:
LevenbergMarquardt(FunctorType &_functor) LevenbergMarquardt(FunctorType &_functor)
: functor(_functor) { nfev = njev = iter = 0; fnorm = gnorm = 0.; useExternalScaling=false; } : functor(_functor) { nfev = njev = iter = 0; fnorm = gnorm = 0.; useExternalScaling=false; }
@ -55,8 +61,8 @@ public:
Parameters() Parameters()
: factor(Scalar(100.)) : factor(Scalar(100.))
, maxfev(400) , maxfev(400)
, ftol(std::sqrt(NumTraits<Scalar>::epsilon())) , ftol(sqrt_epsilon())
, xtol(std::sqrt(NumTraits<Scalar>::epsilon())) , xtol(sqrt_epsilon())
, gtol(Scalar(0.)) , gtol(Scalar(0.))
, epsfcn(Scalar(0.)) {} , epsfcn(Scalar(0.)) {}
Scalar factor; Scalar factor;
@ -72,7 +78,7 @@ public:
LevenbergMarquardtSpace::Status lmder1( LevenbergMarquardtSpace::Status lmder1(
FVectorType &x, FVectorType &x,
const Scalar tol = std::sqrt(NumTraits<Scalar>::epsilon()) const Scalar tol = sqrt_epsilon()
); );
LevenbergMarquardtSpace::Status minimize(FVectorType &x); LevenbergMarquardtSpace::Status minimize(FVectorType &x);
@ -83,12 +89,12 @@ public:
FunctorType &functor, FunctorType &functor,
FVectorType &x, FVectorType &x,
Index *nfev, Index *nfev,
const Scalar tol = std::sqrt(NumTraits<Scalar>::epsilon()) const Scalar tol = sqrt_epsilon()
); );
LevenbergMarquardtSpace::Status lmstr1( LevenbergMarquardtSpace::Status lmstr1(
FVectorType &x, FVectorType &x,
const Scalar tol = std::sqrt(NumTraits<Scalar>::epsilon()) const Scalar tol = sqrt_epsilon()
); );
LevenbergMarquardtSpace::Status minimizeOptimumStorage(FVectorType &x); LevenbergMarquardtSpace::Status minimizeOptimumStorage(FVectorType &x);
@ -109,6 +115,7 @@ public:
Scalar lm_param(void) { return par; } Scalar lm_param(void) { return par; }
private: private:
FunctorType &functor; FunctorType &functor;
Index n; Index n;
Index m; Index m;

View File

@ -1,6 +0,0 @@
FILE(GLOB Eigen_SVD_SRCS "*.h")
INSTALL(FILES
${Eigen_SVD_SRCS}
DESTINATION ${INCLUDE_INSTALL_DIR}unsupported/Eigen/src/SVD COMPONENT Devel
)

View File

@ -1,782 +0,0 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009-2010 Benoit Jacob <jacob.benoit.1@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// 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/.
#ifndef EIGEN_JACOBISVD_H
#define EIGEN_JACOBISVD_H
namespace Eigen {
namespace internal {
// forward declaration (needed by ICC)
// the empty body is required by MSVC
template<typename MatrixType, int QRPreconditioner,
bool IsComplex = NumTraits<typename MatrixType::Scalar>::IsComplex>
struct svd_precondition_2x2_block_to_be_real {};
/*** QR preconditioners (R-SVD)
***
*** Their role is to reduce the problem of computing the SVD to the case of a square matrix.
*** This approach, known as R-SVD, is an optimization for rectangular-enough matrices, and is a requirement for
*** JacobiSVD which by itself is only able to work on square matrices.
***/
enum { PreconditionIfMoreColsThanRows, PreconditionIfMoreRowsThanCols };
template<typename MatrixType, int QRPreconditioner, int Case>
struct qr_preconditioner_should_do_anything
{
enum { a = MatrixType::RowsAtCompileTime != Dynamic &&
MatrixType::ColsAtCompileTime != Dynamic &&
MatrixType::ColsAtCompileTime <= MatrixType::RowsAtCompileTime,
b = MatrixType::RowsAtCompileTime != Dynamic &&
MatrixType::ColsAtCompileTime != Dynamic &&
MatrixType::RowsAtCompileTime <= MatrixType::ColsAtCompileTime,
ret = !( (QRPreconditioner == NoQRPreconditioner) ||
(Case == PreconditionIfMoreColsThanRows && bool(a)) ||
(Case == PreconditionIfMoreRowsThanCols && bool(b)) )
};
};
template<typename MatrixType, int QRPreconditioner, int Case,
bool DoAnything = qr_preconditioner_should_do_anything<MatrixType, QRPreconditioner, Case>::ret
> struct qr_preconditioner_impl {};
template<typename MatrixType, int QRPreconditioner, int Case>
class qr_preconditioner_impl<MatrixType, QRPreconditioner, Case, false>
{
public:
typedef typename MatrixType::Index Index;
void allocate(const JacobiSVD<MatrixType, QRPreconditioner>&) {}
bool run(JacobiSVD<MatrixType, QRPreconditioner>&, const MatrixType&)
{
return false;
}
};
/*** preconditioner using FullPivHouseholderQR ***/
template<typename MatrixType>
class qr_preconditioner_impl<MatrixType, FullPivHouseholderQRPreconditioner, PreconditionIfMoreRowsThanCols, true>
{
public:
typedef typename MatrixType::Index Index;
typedef typename MatrixType::Scalar Scalar;
enum
{
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime
};
typedef Matrix<Scalar, 1, RowsAtCompileTime, RowMajor, 1, MaxRowsAtCompileTime> WorkspaceType;
void allocate(const JacobiSVD<MatrixType, FullPivHouseholderQRPreconditioner>& svd)
{
if (svd.rows() != m_qr.rows() || svd.cols() != m_qr.cols())
{
m_qr.~QRType();
::new (&m_qr) QRType(svd.rows(), svd.cols());
}
if (svd.m_computeFullU) m_workspace.resize(svd.rows());
}
bool run(JacobiSVD<MatrixType, FullPivHouseholderQRPreconditioner>& svd, const MatrixType& matrix)
{
if(matrix.rows() > matrix.cols())
{
m_qr.compute(matrix);
svd.m_workMatrix = m_qr.matrixQR().block(0,0,matrix.cols(),matrix.cols()).template triangularView<Upper>();
if(svd.m_computeFullU) m_qr.matrixQ().evalTo(svd.m_matrixU, m_workspace);
if(svd.computeV()) svd.m_matrixV = m_qr.colsPermutation();
return true;
}
return false;
}
private:
typedef FullPivHouseholderQR<MatrixType> QRType;
QRType m_qr;
WorkspaceType m_workspace;
};
template<typename MatrixType>
class qr_preconditioner_impl<MatrixType, FullPivHouseholderQRPreconditioner, PreconditionIfMoreColsThanRows, true>
{
public:
typedef typename MatrixType::Index Index;
typedef typename MatrixType::Scalar Scalar;
enum
{
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
ColsAtCompileTime = MatrixType::ColsAtCompileTime,
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime,
Options = MatrixType::Options
};
typedef Matrix<Scalar, ColsAtCompileTime, RowsAtCompileTime, Options, MaxColsAtCompileTime, MaxRowsAtCompileTime>
TransposeTypeWithSameStorageOrder;
void allocate(const JacobiSVD<MatrixType, FullPivHouseholderQRPreconditioner>& svd)
{
if (svd.cols() != m_qr.rows() || svd.rows() != m_qr.cols())
{
m_qr.~QRType();
::new (&m_qr) QRType(svd.cols(), svd.rows());
}
m_adjoint.resize(svd.cols(), svd.rows());
if (svd.m_computeFullV) m_workspace.resize(svd.cols());
}
bool run(JacobiSVD<MatrixType, FullPivHouseholderQRPreconditioner>& svd, const MatrixType& matrix)
{
if(matrix.cols() > matrix.rows())
{
m_adjoint = matrix.adjoint();
m_qr.compute(m_adjoint);
svd.m_workMatrix = m_qr.matrixQR().block(0,0,matrix.rows(),matrix.rows()).template triangularView<Upper>().adjoint();
if(svd.m_computeFullV) m_qr.matrixQ().evalTo(svd.m_matrixV, m_workspace);
if(svd.computeU()) svd.m_matrixU = m_qr.colsPermutation();
return true;
}
else return false;
}
private:
typedef FullPivHouseholderQR<TransposeTypeWithSameStorageOrder> QRType;
QRType m_qr;
TransposeTypeWithSameStorageOrder m_adjoint;
typename internal::plain_row_type<MatrixType>::type m_workspace;
};
/*** preconditioner using ColPivHouseholderQR ***/
template<typename MatrixType>
class qr_preconditioner_impl<MatrixType, ColPivHouseholderQRPreconditioner, PreconditionIfMoreRowsThanCols, true>
{
public:
typedef typename MatrixType::Index Index;
void allocate(const JacobiSVD<MatrixType, ColPivHouseholderQRPreconditioner>& svd)
{
if (svd.rows() != m_qr.rows() || svd.cols() != m_qr.cols())
{
m_qr.~QRType();
::new (&m_qr) QRType(svd.rows(), svd.cols());
}
if (svd.m_computeFullU) m_workspace.resize(svd.rows());
else if (svd.m_computeThinU) m_workspace.resize(svd.cols());
}
bool run(JacobiSVD<MatrixType, ColPivHouseholderQRPreconditioner>& svd, const MatrixType& matrix)
{
if(matrix.rows() > matrix.cols())
{
m_qr.compute(matrix);
svd.m_workMatrix = m_qr.matrixQR().block(0,0,matrix.cols(),matrix.cols()).template triangularView<Upper>();
if(svd.m_computeFullU) m_qr.householderQ().evalTo(svd.m_matrixU, m_workspace);
else if(svd.m_computeThinU)
{
svd.m_matrixU.setIdentity(matrix.rows(), matrix.cols());
m_qr.householderQ().applyThisOnTheLeft(svd.m_matrixU, m_workspace);
}
if(svd.computeV()) svd.m_matrixV = m_qr.colsPermutation();
return true;
}
return false;
}
private:
typedef ColPivHouseholderQR<MatrixType> QRType;
QRType m_qr;
typename internal::plain_col_type<MatrixType>::type m_workspace;
};
template<typename MatrixType>
class qr_preconditioner_impl<MatrixType, ColPivHouseholderQRPreconditioner, PreconditionIfMoreColsThanRows, true>
{
public:
typedef typename MatrixType::Index Index;
typedef typename MatrixType::Scalar Scalar;
enum
{
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
ColsAtCompileTime = MatrixType::ColsAtCompileTime,
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime,
Options = MatrixType::Options
};
typedef Matrix<Scalar, ColsAtCompileTime, RowsAtCompileTime, Options, MaxColsAtCompileTime, MaxRowsAtCompileTime>
TransposeTypeWithSameStorageOrder;
void allocate(const JacobiSVD<MatrixType, ColPivHouseholderQRPreconditioner>& svd)
{
if (svd.cols() != m_qr.rows() || svd.rows() != m_qr.cols())
{
m_qr.~QRType();
::new (&m_qr) QRType(svd.cols(), svd.rows());
}
if (svd.m_computeFullV) m_workspace.resize(svd.cols());
else if (svd.m_computeThinV) m_workspace.resize(svd.rows());
m_adjoint.resize(svd.cols(), svd.rows());
}
bool run(JacobiSVD<MatrixType, ColPivHouseholderQRPreconditioner>& svd, const MatrixType& matrix)
{
if(matrix.cols() > matrix.rows())
{
m_adjoint = matrix.adjoint();
m_qr.compute(m_adjoint);
svd.m_workMatrix = m_qr.matrixQR().block(0,0,matrix.rows(),matrix.rows()).template triangularView<Upper>().adjoint();
if(svd.m_computeFullV) m_qr.householderQ().evalTo(svd.m_matrixV, m_workspace);
else if(svd.m_computeThinV)
{
svd.m_matrixV.setIdentity(matrix.cols(), matrix.rows());
m_qr.householderQ().applyThisOnTheLeft(svd.m_matrixV, m_workspace);
}
if(svd.computeU()) svd.m_matrixU = m_qr.colsPermutation();
return true;
}
else return false;
}
private:
typedef ColPivHouseholderQR<TransposeTypeWithSameStorageOrder> QRType;
QRType m_qr;
TransposeTypeWithSameStorageOrder m_adjoint;
typename internal::plain_row_type<MatrixType>::type m_workspace;
};
/*** preconditioner using HouseholderQR ***/
template<typename MatrixType>
class qr_preconditioner_impl<MatrixType, HouseholderQRPreconditioner, PreconditionIfMoreRowsThanCols, true>
{
public:
typedef typename MatrixType::Index Index;
void allocate(const JacobiSVD<MatrixType, HouseholderQRPreconditioner>& svd)
{
if (svd.rows() != m_qr.rows() || svd.cols() != m_qr.cols())
{
m_qr.~QRType();
::new (&m_qr) QRType(svd.rows(), svd.cols());
}
if (svd.m_computeFullU) m_workspace.resize(svd.rows());
else if (svd.m_computeThinU) m_workspace.resize(svd.cols());
}
bool run(JacobiSVD<MatrixType, HouseholderQRPreconditioner>& svd, const MatrixType& matrix)
{
if(matrix.rows() > matrix.cols())
{
m_qr.compute(matrix);
svd.m_workMatrix = m_qr.matrixQR().block(0,0,matrix.cols(),matrix.cols()).template triangularView<Upper>();
if(svd.m_computeFullU) m_qr.householderQ().evalTo(svd.m_matrixU, m_workspace);
else if(svd.m_computeThinU)
{
svd.m_matrixU.setIdentity(matrix.rows(), matrix.cols());
m_qr.householderQ().applyThisOnTheLeft(svd.m_matrixU, m_workspace);
}
if(svd.computeV()) svd.m_matrixV.setIdentity(matrix.cols(), matrix.cols());
return true;
}
return false;
}
private:
typedef HouseholderQR<MatrixType> QRType;
QRType m_qr;
typename internal::plain_col_type<MatrixType>::type m_workspace;
};
template<typename MatrixType>
class qr_preconditioner_impl<MatrixType, HouseholderQRPreconditioner, PreconditionIfMoreColsThanRows, true>
{
public:
typedef typename MatrixType::Index Index;
typedef typename MatrixType::Scalar Scalar;
enum
{
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
ColsAtCompileTime = MatrixType::ColsAtCompileTime,
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime,
Options = MatrixType::Options
};
typedef Matrix<Scalar, ColsAtCompileTime, RowsAtCompileTime, Options, MaxColsAtCompileTime, MaxRowsAtCompileTime>
TransposeTypeWithSameStorageOrder;
void allocate(const JacobiSVD<MatrixType, HouseholderQRPreconditioner>& svd)
{
if (svd.cols() != m_qr.rows() || svd.rows() != m_qr.cols())
{
m_qr.~QRType();
::new (&m_qr) QRType(svd.cols(), svd.rows());
}
if (svd.m_computeFullV) m_workspace.resize(svd.cols());
else if (svd.m_computeThinV) m_workspace.resize(svd.rows());
m_adjoint.resize(svd.cols(), svd.rows());
}
bool run(JacobiSVD<MatrixType, HouseholderQRPreconditioner>& svd, const MatrixType& matrix)
{
if(matrix.cols() > matrix.rows())
{
m_adjoint = matrix.adjoint();
m_qr.compute(m_adjoint);
svd.m_workMatrix = m_qr.matrixQR().block(0,0,matrix.rows(),matrix.rows()).template triangularView<Upper>().adjoint();
if(svd.m_computeFullV) m_qr.householderQ().evalTo(svd.m_matrixV, m_workspace);
else if(svd.m_computeThinV)
{
svd.m_matrixV.setIdentity(matrix.cols(), matrix.rows());
m_qr.householderQ().applyThisOnTheLeft(svd.m_matrixV, m_workspace);
}
if(svd.computeU()) svd.m_matrixU.setIdentity(matrix.rows(), matrix.rows());
return true;
}
else return false;
}
private:
typedef HouseholderQR<TransposeTypeWithSameStorageOrder> QRType;
QRType m_qr;
TransposeTypeWithSameStorageOrder m_adjoint;
typename internal::plain_row_type<MatrixType>::type m_workspace;
};
/*** 2x2 SVD implementation
***
*** JacobiSVD consists in performing a series of 2x2 SVD subproblems
***/
template<typename MatrixType, int QRPreconditioner>
struct svd_precondition_2x2_block_to_be_real<MatrixType, QRPreconditioner, false>
{
typedef JacobiSVD<MatrixType, QRPreconditioner> SVD;
typedef typename SVD::Index Index;
static void run(typename SVD::WorkMatrixType&, SVD&, Index, Index) {}
};
template<typename MatrixType, int QRPreconditioner>
struct svd_precondition_2x2_block_to_be_real<MatrixType, QRPreconditioner, true>
{
typedef JacobiSVD<MatrixType, QRPreconditioner> SVD;
typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::RealScalar RealScalar;
typedef typename SVD::Index Index;
static void run(typename SVD::WorkMatrixType& work_matrix, SVD& svd, Index p, Index q)
{
using std::sqrt;
Scalar z;
JacobiRotation<Scalar> rot;
RealScalar n = sqrt(numext::abs2(work_matrix.coeff(p,p)) + numext::abs2(work_matrix.coeff(q,p)));
if(n==0)
{
z = abs(work_matrix.coeff(p,q)) / work_matrix.coeff(p,q);
work_matrix.row(p) *= z;
if(svd.computeU()) svd.m_matrixU.col(p) *= conj(z);
z = abs(work_matrix.coeff(q,q)) / work_matrix.coeff(q,q);
work_matrix.row(q) *= z;
if(svd.computeU()) svd.m_matrixU.col(q) *= conj(z);
}
else
{
rot.c() = conj(work_matrix.coeff(p,p)) / n;
rot.s() = work_matrix.coeff(q,p) / n;
work_matrix.applyOnTheLeft(p,q,rot);
if(svd.computeU()) svd.m_matrixU.applyOnTheRight(p,q,rot.adjoint());
if(work_matrix.coeff(p,q) != Scalar(0))
{
Scalar z = abs(work_matrix.coeff(p,q)) / work_matrix.coeff(p,q);
work_matrix.col(q) *= z;
if(svd.computeV()) svd.m_matrixV.col(q) *= z;
}
if(work_matrix.coeff(q,q) != Scalar(0))
{
z = abs(work_matrix.coeff(q,q)) / work_matrix.coeff(q,q);
work_matrix.row(q) *= z;
if(svd.computeU()) svd.m_matrixU.col(q) *= conj(z);
}
}
}
};
template<typename MatrixType, typename RealScalar, typename Index>
void real_2x2_jacobi_svd(const MatrixType& matrix, Index p, Index q,
JacobiRotation<RealScalar> *j_left,
JacobiRotation<RealScalar> *j_right)
{
using std::sqrt;
Matrix<RealScalar,2,2> m;
m << numext::real(matrix.coeff(p,p)), numext::real(matrix.coeff(p,q)),
numext::real(matrix.coeff(q,p)), numext::real(matrix.coeff(q,q));
JacobiRotation<RealScalar> rot1;
RealScalar t = m.coeff(0,0) + m.coeff(1,1);
RealScalar d = m.coeff(1,0) - m.coeff(0,1);
if(t == RealScalar(0))
{
rot1.c() = RealScalar(0);
rot1.s() = d > RealScalar(0) ? RealScalar(1) : RealScalar(-1);
}
else
{
RealScalar u = d / t;
rot1.c() = RealScalar(1) / sqrt(RealScalar(1) + numext::abs2(u));
rot1.s() = rot1.c() * u;
}
m.applyOnTheLeft(0,1,rot1);
j_right->makeJacobi(m,0,1);
*j_left = rot1 * j_right->transpose();
}
} // end namespace internal
/** \ingroup SVD_Module
*
*
* \class JacobiSVD
*
* \brief Two-sided Jacobi SVD decomposition of a rectangular matrix
*
* \param MatrixType the type of the matrix of which we are computing the SVD decomposition
* \param QRPreconditioner this optional parameter allows to specify the type of QR decomposition that will be used internally
* for the R-SVD step for non-square matrices. See discussion of possible values below.
*
* SVD decomposition consists in decomposing any n-by-p matrix \a A as a product
* \f[ A = U S V^* \f]
* where \a U is a n-by-n unitary, \a V is a p-by-p unitary, and \a S is a n-by-p real positive matrix which is zero outside of its main diagonal;
* the diagonal entries of S are known as the \em singular \em values of \a A and the columns of \a U and \a V are known as the left
* and right \em singular \em vectors of \a A respectively.
*
* Singular values are always sorted in decreasing order.
*
* This JacobiSVD decomposition computes only the singular values by default. If you want \a U or \a V, you need to ask for them explicitly.
*
* You can ask for only \em thin \a U or \a V to be computed, meaning the following. In case of a rectangular n-by-p matrix, letting \a m be the
* smaller value among \a n and \a p, there are only \a m singular vectors; the remaining columns of \a U and \a V do not correspond to actual
* singular vectors. Asking for \em thin \a U or \a V means asking for only their \a m first columns to be formed. So \a U is then a n-by-m matrix,
* and \a V is then a p-by-m matrix. Notice that thin \a U and \a V are all you need for (least squares) solving.
*
* Here's an example demonstrating basic usage:
* \include JacobiSVD_basic.cpp
* Output: \verbinclude JacobiSVD_basic.out
*
* This JacobiSVD class is a two-sided Jacobi R-SVD decomposition, ensuring optimal reliability and accuracy. The downside is that it's slower than
* bidiagonalizing SVD algorithms for large square matrices; however its complexity is still \f$ O(n^2p) \f$ where \a n is the smaller dimension and
* \a p is the greater dimension, meaning that it is still of the same order of complexity as the faster bidiagonalizing R-SVD algorithms.
* In particular, like any R-SVD, it takes advantage of non-squareness in that its complexity is only linear in the greater dimension.
*
* If the input matrix has inf or nan coefficients, the result of the computation is undefined, but the computation is guaranteed to
* terminate in finite (and reasonable) time.
*
* The possible values for QRPreconditioner are:
* \li ColPivHouseholderQRPreconditioner is the default. In practice it's very safe. It uses column-pivoting QR.
* \li FullPivHouseholderQRPreconditioner, is the safest and slowest. It uses full-pivoting QR.
* Contrary to other QRs, it doesn't allow computing thin unitaries.
* \li HouseholderQRPreconditioner is the fastest, and less safe and accurate than the pivoting variants. It uses non-pivoting QR.
* This is very similar in safety and accuracy to the bidiagonalization process used by bidiagonalizing SVD algorithms (since bidiagonalization
* is inherently non-pivoting). However the resulting SVD is still more reliable than bidiagonalizing SVDs because the Jacobi-based iterarive
* process is more reliable than the optimized bidiagonal SVD iterations.
* \li NoQRPreconditioner allows not to use a QR preconditioner at all. This is useful if you know that you will only be computing
* JacobiSVD decompositions of square matrices. Non-square matrices require a QR preconditioner. Using this option will result in
* faster compilation and smaller executable code. It won't significantly speed up computation, since JacobiSVD is always checking
* if QR preconditioning is needed before applying it anyway.
*
* \sa MatrixBase::jacobiSvd()
*/
template<typename _MatrixType, int QRPreconditioner>
class JacobiSVD : public SVDBase<_MatrixType>
{
public:
typedef _MatrixType MatrixType;
typedef typename MatrixType::Scalar Scalar;
typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar;
typedef typename MatrixType::Index Index;
enum {
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
ColsAtCompileTime = MatrixType::ColsAtCompileTime,
DiagSizeAtCompileTime = EIGEN_SIZE_MIN_PREFER_DYNAMIC(RowsAtCompileTime,ColsAtCompileTime),
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime,
MaxDiagSizeAtCompileTime = EIGEN_SIZE_MIN_PREFER_FIXED(MaxRowsAtCompileTime,MaxColsAtCompileTime),
MatrixOptions = MatrixType::Options
};
typedef Matrix<Scalar, RowsAtCompileTime, RowsAtCompileTime,
MatrixOptions, MaxRowsAtCompileTime, MaxRowsAtCompileTime>
MatrixUType;
typedef Matrix<Scalar, ColsAtCompileTime, ColsAtCompileTime,
MatrixOptions, MaxColsAtCompileTime, MaxColsAtCompileTime>
MatrixVType;
typedef typename internal::plain_diag_type<MatrixType, RealScalar>::type SingularValuesType;
typedef typename internal::plain_row_type<MatrixType>::type RowType;
typedef typename internal::plain_col_type<MatrixType>::type ColType;
typedef Matrix<Scalar, DiagSizeAtCompileTime, DiagSizeAtCompileTime,
MatrixOptions, MaxDiagSizeAtCompileTime, MaxDiagSizeAtCompileTime>
WorkMatrixType;
/** \brief Default Constructor.
*
* The default constructor is useful in cases in which the user intends to
* perform decompositions via JacobiSVD::compute(const MatrixType&).
*/
JacobiSVD()
: SVDBase<_MatrixType>::SVDBase()
{}
/** \brief Default Constructor with memory preallocation
*
* Like the default constructor but with preallocation of the internal data
* according to the specified problem size.
* \sa JacobiSVD()
*/
JacobiSVD(Index rows, Index cols, unsigned int computationOptions = 0)
: SVDBase<_MatrixType>::SVDBase()
{
allocate(rows, cols, computationOptions);
}
/** \brief Constructor performing the decomposition of given matrix.
*
* \param matrix the matrix to decompose
* \param computationOptions optional parameter allowing to specify if you want full or thin U or V unitaries to be computed.
* By default, none is computed. This is a bit-field, the possible bits are #ComputeFullU, #ComputeThinU,
* #ComputeFullV, #ComputeThinV.
*
* Thin unitaries are only available if your matrix type has a Dynamic number of columns (for example MatrixXf). They also are not
* available with the (non-default) FullPivHouseholderQR preconditioner.
*/
JacobiSVD(const MatrixType& matrix, unsigned int computationOptions = 0)
: SVDBase<_MatrixType>::SVDBase()
{
compute(matrix, computationOptions);
}
/** \brief Method performing the decomposition of given matrix using custom options.
*
* \param matrix the matrix to decompose
* \param computationOptions optional parameter allowing to specify if you want full or thin U or V unitaries to be computed.
* By default, none is computed. This is a bit-field, the possible bits are #ComputeFullU, #ComputeThinU,
* #ComputeFullV, #ComputeThinV.
*
* Thin unitaries are only available if your matrix type has a Dynamic number of columns (for example MatrixXf). They also are not
* available with the (non-default) FullPivHouseholderQR preconditioner.
*/
SVDBase<MatrixType>& compute(const MatrixType& matrix, unsigned int computationOptions);
/** \brief Method performing the decomposition of given matrix using current options.
*
* \param matrix the matrix to decompose
*
* This method uses the current \a computationOptions, as already passed to the constructor or to compute(const MatrixType&, unsigned int).
*/
SVDBase<MatrixType>& compute(const MatrixType& matrix)
{
return compute(matrix, this->m_computationOptions);
}
/** \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.
*
* \note Solving requires both U and V to be computed. Thin U and V are enough, there is no need for full U or V.
*
* \note SVD solving is implicitly least-squares. Thus, this method serves both purposes of exact solving and least-squares solving.
* In other words, the returned solution is guaranteed to minimize the Euclidean norm \f$ \Vert A x - b \Vert \f$.
*/
template<typename Rhs>
inline const internal::solve_retval<JacobiSVD, Rhs>
solve(const MatrixBase<Rhs>& b) const
{
eigen_assert(this->m_isInitialized && "JacobiSVD is not initialized.");
eigen_assert(SVDBase<MatrixType>::computeU() && SVDBase<MatrixType>::computeV() && "JacobiSVD::solve() requires both unitaries U and V to be computed (thin unitaries suffice).");
return internal::solve_retval<JacobiSVD, Rhs>(*this, b.derived());
}
private:
void allocate(Index rows, Index cols, unsigned int computationOptions);
protected:
WorkMatrixType m_workMatrix;
template<typename __MatrixType, int _QRPreconditioner, bool _IsComplex>
friend struct internal::svd_precondition_2x2_block_to_be_real;
template<typename __MatrixType, int _QRPreconditioner, int _Case, bool _DoAnything>
friend struct internal::qr_preconditioner_impl;
internal::qr_preconditioner_impl<MatrixType, QRPreconditioner, internal::PreconditionIfMoreColsThanRows> m_qr_precond_morecols;
internal::qr_preconditioner_impl<MatrixType, QRPreconditioner, internal::PreconditionIfMoreRowsThanCols> m_qr_precond_morerows;
};
template<typename MatrixType, int QRPreconditioner>
void JacobiSVD<MatrixType, QRPreconditioner>::allocate(Index rows, Index cols, unsigned int computationOptions)
{
if (SVDBase<MatrixType>::allocate(rows, cols, computationOptions)) return;
if (QRPreconditioner == FullPivHouseholderQRPreconditioner)
{
eigen_assert(!(this->m_computeThinU || this->m_computeThinV) &&
"JacobiSVD: can't compute thin U or thin V with the FullPivHouseholderQR preconditioner. "
"Use the ColPivHouseholderQR preconditioner instead.");
}
m_workMatrix.resize(this->m_diagSize, this->m_diagSize);
if(this->m_cols>this->m_rows) m_qr_precond_morecols.allocate(*this);
if(this->m_rows>this->m_cols) m_qr_precond_morerows.allocate(*this);
}
template<typename MatrixType, int QRPreconditioner>
SVDBase<MatrixType>&
JacobiSVD<MatrixType, QRPreconditioner>::compute(const MatrixType& matrix, unsigned int computationOptions)
{
using std::abs;
allocate(matrix.rows(), matrix.cols(), computationOptions);
// currently we stop when we reach precision 2*epsilon as the last bit of precision can require an unreasonable number of iterations,
// only worsening the precision of U and V as we accumulate more rotations
const RealScalar precision = RealScalar(2) * NumTraits<Scalar>::epsilon();
// limit for very small denormal numbers to be considered zero in order to avoid infinite loops (see bug 286)
const RealScalar considerAsZero = RealScalar(2) * std::numeric_limits<RealScalar>::denorm_min();
/*** step 1. The R-SVD step: we use a QR decomposition to reduce to the case of a square matrix */
if(!m_qr_precond_morecols.run(*this, matrix) && !m_qr_precond_morerows.run(*this, matrix))
{
m_workMatrix = matrix.block(0,0,this->m_diagSize,this->m_diagSize);
if(this->m_computeFullU) this->m_matrixU.setIdentity(this->m_rows,this->m_rows);
if(this->m_computeThinU) this->m_matrixU.setIdentity(this->m_rows,this->m_diagSize);
if(this->m_computeFullV) this->m_matrixV.setIdentity(this->m_cols,this->m_cols);
if(this->m_computeThinV) this->m_matrixV.setIdentity(this->m_cols, this->m_diagSize);
}
/*** step 2. The main Jacobi SVD iteration. ***/
bool finished = false;
while(!finished)
{
finished = true;
// do a sweep: for all index pairs (p,q), perform SVD of the corresponding 2x2 sub-matrix
for(Index p = 1; p < this->m_diagSize; ++p)
{
for(Index q = 0; q < p; ++q)
{
// if this 2x2 sub-matrix is not diagonal already...
// notice that this comparison will evaluate to false if any NaN is involved, ensuring that NaN's don't
// keep us iterating forever. Similarly, small denormal numbers are considered zero.
using std::max;
RealScalar threshold = (max)(considerAsZero, precision * (max)(abs(m_workMatrix.coeff(p,p)),
abs(m_workMatrix.coeff(q,q))));
if((max)(abs(m_workMatrix.coeff(p,q)),abs(m_workMatrix.coeff(q,p))) > threshold)
{
finished = false;
// perform SVD decomposition of 2x2 sub-matrix corresponding to indices p,q to make it diagonal
internal::svd_precondition_2x2_block_to_be_real<MatrixType, QRPreconditioner>::run(m_workMatrix, *this, p, q);
JacobiRotation<RealScalar> j_left, j_right;
internal::real_2x2_jacobi_svd(m_workMatrix, p, q, &j_left, &j_right);
// accumulate resulting Jacobi rotations
m_workMatrix.applyOnTheLeft(p,q,j_left);
if(SVDBase<MatrixType>::computeU()) this->m_matrixU.applyOnTheRight(p,q,j_left.transpose());
m_workMatrix.applyOnTheRight(p,q,j_right);
if(SVDBase<MatrixType>::computeV()) this->m_matrixV.applyOnTheRight(p,q,j_right);
}
}
}
}
/*** step 3. The work matrix is now diagonal, so ensure it's positive so its diagonal entries are the singular values ***/
for(Index i = 0; i < this->m_diagSize; ++i)
{
RealScalar a = abs(m_workMatrix.coeff(i,i));
this->m_singularValues.coeffRef(i) = a;
if(SVDBase<MatrixType>::computeU() && (a!=RealScalar(0))) this->m_matrixU.col(i) *= this->m_workMatrix.coeff(i,i)/a;
}
/*** step 4. Sort singular values in descending order and compute the number of nonzero singular values ***/
this->m_nonzeroSingularValues = this->m_diagSize;
for(Index i = 0; i < this->m_diagSize; i++)
{
Index pos;
RealScalar maxRemainingSingularValue = this->m_singularValues.tail(this->m_diagSize-i).maxCoeff(&pos);
if(maxRemainingSingularValue == RealScalar(0))
{
this->m_nonzeroSingularValues = i;
break;
}
if(pos)
{
pos += i;
std::swap(this->m_singularValues.coeffRef(i), this->m_singularValues.coeffRef(pos));
if(SVDBase<MatrixType>::computeU()) this->m_matrixU.col(pos).swap(this->m_matrixU.col(i));
if(SVDBase<MatrixType>::computeV()) this->m_matrixV.col(pos).swap(this->m_matrixV.col(i));
}
}
this->m_isInitialized = true;
return *this;
}
namespace internal {
template<typename _MatrixType, int QRPreconditioner, typename Rhs>
struct solve_retval<JacobiSVD<_MatrixType, QRPreconditioner>, Rhs>
: solve_retval_base<JacobiSVD<_MatrixType, QRPreconditioner>, Rhs>
{
typedef JacobiSVD<_MatrixType, QRPreconditioner> JacobiSVDType;
EIGEN_MAKE_SOLVE_HELPERS(JacobiSVDType,Rhs)
template<typename Dest> void evalTo(Dest& dst) const
{
eigen_assert(rhs().rows() == dec().rows());
// A = U S V^*
// So A^{-1} = V S^{-1} U^*
Index diagSize = (std::min)(dec().rows(), dec().cols());
typename JacobiSVDType::SingularValuesType invertedSingVals(diagSize);
Index nonzeroSingVals = dec().nonzeroSingularValues();
invertedSingVals.head(nonzeroSingVals) = dec().singularValues().head(nonzeroSingVals).array().inverse();
invertedSingVals.tail(diagSize - nonzeroSingVals).setZero();
dst = dec().matrixV().leftCols(diagSize)
* invertedSingVals.asDiagonal()
* dec().matrixU().leftCols(diagSize).adjoint()
* rhs();
}
};
} // end namespace internal
/** \svd_module
*
* \return the singular value decomposition of \c *this computed by two-sided
* Jacobi transformations.
*
* \sa class JacobiSVD
*/
template<typename Derived>
JacobiSVD<typename MatrixBase<Derived>::PlainObject>
MatrixBase<Derived>::jacobiSvd(unsigned int computationOptions) const
{
return JacobiSVD<PlainObject>(*this, computationOptions);
}
} // end namespace Eigen
#endif // EIGEN_JACOBISVD_H

View File

@ -10,12 +10,10 @@ FOREACH(example_src ${examples_SRCS})
if(EIGEN_STANDARD_LIBRARIES_TO_LINK_TO) if(EIGEN_STANDARD_LIBRARIES_TO_LINK_TO)
target_link_libraries(example_${example} ${EIGEN_STANDARD_LIBRARIES_TO_LINK_TO}) target_link_libraries(example_${example} ${EIGEN_STANDARD_LIBRARIES_TO_LINK_TO})
endif() endif()
GET_TARGET_PROPERTY(example_executable
example_${example} LOCATION)
ADD_CUSTOM_COMMAND( ADD_CUSTOM_COMMAND(
TARGET example_${example} TARGET example_${example}
POST_BUILD POST_BUILD
COMMAND ${example_executable} COMMAND example_${example}
ARGS >${CMAKE_CURRENT_BINARY_DIR}/${example}.out ARGS >${CMAKE_CURRENT_BINARY_DIR}/${example}.out
) )
ADD_DEPENDENCIES(unsupported_examples example_${example}) ADD_DEPENDENCIES(unsupported_examples example_${example})

View File

@ -14,12 +14,10 @@ FOREACH(snippet_src ${snippets_SRCS})
if(EIGEN_STANDARD_LIBRARIES_TO_LINK_TO) if(EIGEN_STANDARD_LIBRARIES_TO_LINK_TO)
target_link_libraries(${compile_snippet_target} ${EIGEN_STANDARD_LIBRARIES_TO_LINK_TO}) target_link_libraries(${compile_snippet_target} ${EIGEN_STANDARD_LIBRARIES_TO_LINK_TO})
endif() endif()
GET_TARGET_PROPERTY(compile_snippet_executable
${compile_snippet_target} LOCATION)
ADD_CUSTOM_COMMAND( ADD_CUSTOM_COMMAND(
TARGET ${compile_snippet_target} TARGET ${compile_snippet_target}
POST_BUILD POST_BUILD
COMMAND ${compile_snippet_executable} COMMAND ${compile_snippet_target}
ARGS >${CMAKE_CURRENT_BINARY_DIR}/${snippet}.out ARGS >${CMAKE_CURRENT_BINARY_DIR}/${snippet}.out
) )
ADD_DEPENDENCIES(unsupported_snippets ${compile_snippet_target}) ADD_DEPENDENCIES(unsupported_snippets ${compile_snippet_target})

View File

@ -1022,7 +1022,8 @@ void testNistLanczos1(void)
VERIFY_IS_EQUAL(lm.nfev, 79); VERIFY_IS_EQUAL(lm.nfev, 79);
VERIFY_IS_EQUAL(lm.njev, 72); VERIFY_IS_EQUAL(lm.njev, 72);
// check norm^2 // check norm^2
VERIFY_IS_APPROX(lm.fvec.squaredNorm(), 1.430899764097e-25); // should be 1.4307867721E-25, but nist results are on 128-bit floats std::cout.precision(30);
VERIFY_IS_APPROX(lm.fvec.squaredNorm(), 1.4290986055242372e-25); // should be 1.4307867721E-25, but nist results are on 128-bit floats
// check x // check x
VERIFY_IS_APPROX(x[0], 9.5100000027E-02); VERIFY_IS_APPROX(x[0], 9.5100000027E-02);
VERIFY_IS_APPROX(x[1], 1.0000000001E+00); VERIFY_IS_APPROX(x[1], 1.0000000001E+00);
@ -1043,7 +1044,7 @@ void testNistLanczos1(void)
VERIFY_IS_EQUAL(lm.nfev, 9); VERIFY_IS_EQUAL(lm.nfev, 9);
VERIFY_IS_EQUAL(lm.njev, 8); VERIFY_IS_EQUAL(lm.njev, 8);
// check norm^2 // check norm^2
VERIFY_IS_APPROX(lm.fvec.squaredNorm(), 1.428595533845e-25); // should be 1.4307867721E-25, but nist results are on 128-bit floats VERIFY_IS_APPROX(lm.fvec.squaredNorm(), 1.430571737783119393e-25); // should be 1.4307867721E-25, but nist results are on 128-bit floats
// check x // check x
VERIFY_IS_APPROX(x[0], 9.5100000027E-02); VERIFY_IS_APPROX(x[0], 9.5100000027E-02);
VERIFY_IS_APPROX(x[1], 1.0000000001E+00); VERIFY_IS_APPROX(x[1], 1.0000000001E+00);
@ -1262,8 +1263,8 @@ void testNistBoxBOD(void)
// check return value // check return value
VERIFY_IS_EQUAL(info, 1); VERIFY_IS_EQUAL(info, 1);
VERIFY_IS_EQUAL(lm.nfev, 31); VERIFY(lm.nfev < 31); // 31
VERIFY_IS_EQUAL(lm.njev, 25); VERIFY(lm.njev < 25); // 25
// check norm^2 // check norm^2
VERIFY_IS_APPROX(lm.fvec.squaredNorm(), 1.1680088766E+03); VERIFY_IS_APPROX(lm.fvec.squaredNorm(), 1.1680088766E+03);
// check x // check x
@ -1342,10 +1343,6 @@ void testNistMGH17(void)
lm.parameters.maxfev = 1000; lm.parameters.maxfev = 1000;
info = lm.minimize(x); info = lm.minimize(x);
// check return value
VERIFY_IS_EQUAL(info, 2);
VERIFY_IS_EQUAL(lm.nfev, 602 );
VERIFY_IS_EQUAL(lm.njev, 545 );
// check norm^2 // check norm^2
VERIFY_IS_APPROX(lm.fvec.squaredNorm(), 5.4648946975E-05); VERIFY_IS_APPROX(lm.fvec.squaredNorm(), 5.4648946975E-05);
// check x // check x
@ -1355,6 +1352,11 @@ void testNistMGH17(void)
VERIFY_IS_APPROX(x[3], 1.2867534640E-02); VERIFY_IS_APPROX(x[3], 1.2867534640E-02);
VERIFY_IS_APPROX(x[4], 2.2122699662E-02); VERIFY_IS_APPROX(x[4], 2.2122699662E-02);
// check return value
VERIFY_IS_EQUAL(info, 2);
VERIFY(lm.nfev < 650); // 602
VERIFY(lm.njev < 600); // 545
/* /*
* Second try * Second try
*/ */
@ -1832,8 +1834,8 @@ void test_NonLinearOptimization()
// NIST tests, level of difficulty = "Average" // NIST tests, level of difficulty = "Average"
CALL_SUBTEST/*_5*/(testNistHahn1()); CALL_SUBTEST/*_5*/(testNistHahn1());
CALL_SUBTEST/*_6*/(testNistMisra1d()); CALL_SUBTEST/*_6*/(testNistMisra1d());
// CALL_SUBTEST/*_7*/(testNistMGH17()); CALL_SUBTEST/*_7*/(testNistMGH17());
// CALL_SUBTEST/*_8*/(testNistLanczos1()); CALL_SUBTEST/*_8*/(testNistLanczos1());
// // NIST tests, level of difficulty = "Higher" // // NIST tests, level of difficulty = "Higher"
CALL_SUBTEST/*_9*/(testNistRat42()); CALL_SUBTEST/*_9*/(testNistRat42());

View File

@ -787,10 +787,6 @@ void testNistMGH10(void)
LevenbergMarquardt<MGH10_functor> lm(functor); LevenbergMarquardt<MGH10_functor> lm(functor);
info = lm.minimize(x); info = lm.minimize(x);
// check return value
VERIFY_IS_EQUAL(info, 1);
VERIFY_IS_EQUAL(lm.nfev(), 284 );
VERIFY_IS_EQUAL(lm.njev(), 249 );
// check norm^2 // check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 8.7945855171E+01); VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 8.7945855171E+01);
// check x // check x
@ -798,6 +794,11 @@ void testNistMGH10(void)
VERIFY_IS_APPROX(x[1], 6.1813463463E+03); VERIFY_IS_APPROX(x[1], 6.1813463463E+03);
VERIFY_IS_APPROX(x[2], 3.4522363462E+02); VERIFY_IS_APPROX(x[2], 3.4522363462E+02);
// check return value
//VERIFY_IS_EQUAL(info, 1);
VERIFY_IS_EQUAL(lm.nfev(), 284 );
VERIFY_IS_EQUAL(lm.njev(), 249 );
/* /*
* Second try * Second try
*/ */
@ -805,16 +806,17 @@ void testNistMGH10(void)
// do the computation // do the computation
info = lm.minimize(x); info = lm.minimize(x);
// check return value
VERIFY_IS_EQUAL(info, 1);
VERIFY_IS_EQUAL(lm.nfev(), 126);
VERIFY_IS_EQUAL(lm.njev(), 116);
// check norm^2 // check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 8.7945855171E+01); VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 8.7945855171E+01);
// check x // check x
VERIFY_IS_APPROX(x[0], 5.6096364710E-03); VERIFY_IS_APPROX(x[0], 5.6096364710E-03);
VERIFY_IS_APPROX(x[1], 6.1813463463E+03); VERIFY_IS_APPROX(x[1], 6.1813463463E+03);
VERIFY_IS_APPROX(x[2], 3.4522363462E+02); VERIFY_IS_APPROX(x[2], 3.4522363462E+02);
// check return value
//VERIFY_IS_EQUAL(info, 1);
VERIFY_IS_EQUAL(lm.nfev(), 126);
VERIFY_IS_EQUAL(lm.njev(), 116);
} }
@ -866,16 +868,17 @@ void testNistBoxBOD(void)
lm.setFactor(10); lm.setFactor(10);
info = lm.minimize(x); info = lm.minimize(x);
// check return value
VERIFY_IS_EQUAL(info, 1);
VERIFY_IS_EQUAL(lm.nfev(), 31);
VERIFY_IS_EQUAL(lm.njev(), 25);
// check norm^2 // check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 1.1680088766E+03); VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 1.1680088766E+03);
// check x // check x
VERIFY_IS_APPROX(x[0], 2.1380940889E+02); VERIFY_IS_APPROX(x[0], 2.1380940889E+02);
VERIFY_IS_APPROX(x[1], 5.4723748542E-01); VERIFY_IS_APPROX(x[1], 5.4723748542E-01);
// check return value
VERIFY_IS_EQUAL(info, 1);
VERIFY(lm.nfev() < 31); // 31
VERIFY(lm.njev() < 25); // 25
/* /*
* Second try * Second try
*/ */
@ -948,10 +951,6 @@ void testNistMGH17(void)
lm.setMaxfev(1000); lm.setMaxfev(1000);
info = lm.minimize(x); info = lm.minimize(x);
// check return value
// VERIFY_IS_EQUAL(info, 2); //FIXME Use (lm.info() == Success)
// VERIFY_IS_EQUAL(lm.nfev(), 602 );
VERIFY_IS_EQUAL(lm.njev(), 545 );
// check norm^2 // check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 5.4648946975E-05); VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 5.4648946975E-05);
// check x // check x
@ -961,6 +960,11 @@ void testNistMGH17(void)
VERIFY_IS_APPROX(x[3], 1.2867534640E-02); VERIFY_IS_APPROX(x[3], 1.2867534640E-02);
VERIFY_IS_APPROX(x[4], 2.2122699662E-02); VERIFY_IS_APPROX(x[4], 2.2122699662E-02);
// check return value
// VERIFY_IS_EQUAL(info, 2); //FIXME Use (lm.info() == Success)
VERIFY(lm.nfev() < 700 ); // 602
VERIFY(lm.njev() < 600 ); // 545
/* /*
* Second try * Second try
*/ */
@ -1035,10 +1039,6 @@ void testNistMGH09(void)
lm.setMaxfev(1000); lm.setMaxfev(1000);
info = lm.minimize(x); info = lm.minimize(x);
// check return value
VERIFY_IS_EQUAL(info, 1);
VERIFY_IS_EQUAL(lm.nfev(), 490 );
VERIFY_IS_EQUAL(lm.njev(), 376 );
// check norm^2 // check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 3.0750560385E-04); VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 3.0750560385E-04);
// check x // check x
@ -1046,6 +1046,10 @@ void testNistMGH09(void)
VERIFY_IS_APPROX(x[1], 0.19126423573); // should be 1.9128232873E-01 VERIFY_IS_APPROX(x[1], 0.19126423573); // should be 1.9128232873E-01
VERIFY_IS_APPROX(x[2], 0.12305309914); // should be 1.2305650693E-01 VERIFY_IS_APPROX(x[2], 0.12305309914); // should be 1.2305650693E-01
VERIFY_IS_APPROX(x[3], 0.13605395375); // should be 1.3606233068E-01 VERIFY_IS_APPROX(x[3], 0.13605395375); // should be 1.3606233068E-01
// check return value
VERIFY_IS_EQUAL(info, 1);
VERIFY(lm.nfev() < 510 ); // 490
VERIFY(lm.njev() < 400 ); // 376
/* /*
* Second try * Second try

View File

@ -18,7 +18,7 @@
#define EIGEN_RUNTIME_NO_MALLOC #define EIGEN_RUNTIME_NO_MALLOC
#include "main.h" #include "main.h"
#include <unsupported/Eigen/SVD> #include <unsupported/Eigen/BDCSVD>
#include <Eigen/LU> #include <Eigen/LU>