mirror of
https://gitlab.com/libeigen/eigen.git
synced 2025-08-12 11:49:02 +08:00
Merged in rmlarsen/eigen (pull request PR-174)
Add matrix condition number estimation module.
This commit is contained in:
commit
ea7087ef31
@ -440,6 +440,7 @@ using std::ptrdiff_t;
|
|||||||
#include "src/Core/products/TriangularSolverVector.h"
|
#include "src/Core/products/TriangularSolverVector.h"
|
||||||
#include "src/Core/BandMatrix.h"
|
#include "src/Core/BandMatrix.h"
|
||||||
#include "src/Core/CoreIterators.h"
|
#include "src/Core/CoreIterators.h"
|
||||||
|
#include "src/Core/ConditionEstimator.h"
|
||||||
|
|
||||||
#include "src/Core/BooleanRedux.h"
|
#include "src/Core/BooleanRedux.h"
|
||||||
#include "src/Core/Select.h"
|
#include "src/Core/Select.h"
|
||||||
|
@ -192,6 +192,15 @@ template<typename _MatrixType, int _UpLo> class LDLT
|
|||||||
template<typename InputType>
|
template<typename InputType>
|
||||||
LDLT& compute(const EigenBase<InputType>& matrix);
|
LDLT& compute(const EigenBase<InputType>& matrix);
|
||||||
|
|
||||||
|
/** \returns an estimate of the reciprocal condition number of the matrix of
|
||||||
|
* which *this is the LDLT decomposition.
|
||||||
|
*/
|
||||||
|
RealScalar rcond() const
|
||||||
|
{
|
||||||
|
eigen_assert(m_isInitialized && "LDLT is not initialized.");
|
||||||
|
return ReciprocalConditionNumberEstimate(m_l1_norm, *this);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Derived>
|
template <typename Derived>
|
||||||
LDLT& rankUpdate(const MatrixBase<Derived>& w, const RealScalar& alpha=1);
|
LDLT& rankUpdate(const MatrixBase<Derived>& w, const RealScalar& alpha=1);
|
||||||
|
|
||||||
@ -207,6 +216,11 @@ template<typename _MatrixType, int _UpLo> class LDLT
|
|||||||
|
|
||||||
MatrixType reconstructedMatrix() const;
|
MatrixType reconstructedMatrix() const;
|
||||||
|
|
||||||
|
/** \returns the decomposition itself to allow generic code to do
|
||||||
|
* ldlt.adjoint().solve(rhs).
|
||||||
|
*/
|
||||||
|
const LDLT<MatrixType, UpLo>& adjoint() const { return *this; };
|
||||||
|
|
||||||
inline Index rows() const { return m_matrix.rows(); }
|
inline Index rows() const { return m_matrix.rows(); }
|
||||||
inline Index cols() const { return m_matrix.cols(); }
|
inline Index cols() const { return m_matrix.cols(); }
|
||||||
|
|
||||||
@ -241,6 +255,7 @@ template<typename _MatrixType, int _UpLo> class LDLT
|
|||||||
* is not stored), and the diagonal entries correspond to D.
|
* is not stored), and the diagonal entries correspond to D.
|
||||||
*/
|
*/
|
||||||
MatrixType m_matrix;
|
MatrixType m_matrix;
|
||||||
|
RealScalar m_l1_norm;
|
||||||
TranspositionType m_transpositions;
|
TranspositionType m_transpositions;
|
||||||
TmpMatrixType m_temporary;
|
TmpMatrixType m_temporary;
|
||||||
internal::SignMatrix m_sign;
|
internal::SignMatrix m_sign;
|
||||||
@ -439,6 +454,26 @@ LDLT<MatrixType,_UpLo>& LDLT<MatrixType,_UpLo>::compute(const EigenBase<InputTyp
|
|||||||
|
|
||||||
m_matrix = a.derived();
|
m_matrix = a.derived();
|
||||||
|
|
||||||
|
// Compute matrix L1 norm = max abs column sum.
|
||||||
|
m_l1_norm = RealScalar(0);
|
||||||
|
if (_UpLo == Lower) {
|
||||||
|
for (int col = 0; col < size; ++col) {
|
||||||
|
const RealScalar abs_col_sum = m_matrix.col(col).tail(size - col).template lpNorm<1>() +
|
||||||
|
m_matrix.row(col).head(col).template lpNorm<1>();
|
||||||
|
if (abs_col_sum > m_l1_norm) {
|
||||||
|
m_l1_norm = abs_col_sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int col = 0; col < a.cols(); ++col) {
|
||||||
|
const RealScalar abs_col_sum = m_matrix.col(col).head(col).template lpNorm<1>() +
|
||||||
|
m_matrix.row(col).tail(size - col).template lpNorm<1>();
|
||||||
|
if (abs_col_sum > m_l1_norm) {
|
||||||
|
m_l1_norm = abs_col_sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_transpositions.resize(size);
|
m_transpositions.resize(size);
|
||||||
m_isInitialized = false;
|
m_isInitialized = false;
|
||||||
m_temporary.resize(size);
|
m_temporary.resize(size);
|
||||||
|
@ -135,6 +135,16 @@ template<typename _MatrixType, int _UpLo> class LLT
|
|||||||
template<typename InputType>
|
template<typename InputType>
|
||||||
LLT& compute(const EigenBase<InputType>& matrix);
|
LLT& compute(const EigenBase<InputType>& matrix);
|
||||||
|
|
||||||
|
/** \returns an estimate of the reciprocal condition number of the matrix of
|
||||||
|
* which *this is the Cholesky decomposition.
|
||||||
|
*/
|
||||||
|
RealScalar rcond() const
|
||||||
|
{
|
||||||
|
eigen_assert(m_isInitialized && "LLT is not initialized.");
|
||||||
|
eigen_assert(m_info == Success && "LLT failed because matrix appears to be negative");
|
||||||
|
return ReciprocalConditionNumberEstimate(m_l1_norm, *this);
|
||||||
|
}
|
||||||
|
|
||||||
/** \returns the LLT decomposition matrix
|
/** \returns the LLT decomposition matrix
|
||||||
*
|
*
|
||||||
* TODO: document the storage layout
|
* TODO: document the storage layout
|
||||||
@ -159,6 +169,11 @@ template<typename _MatrixType, int _UpLo> class LLT
|
|||||||
return m_info;
|
return m_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \returns the decomposition itself to allow generic code to do
|
||||||
|
* llt.adjoint().solve(rhs).
|
||||||
|
*/
|
||||||
|
const LLT<MatrixType, UpLo>& adjoint() const { return *this; };
|
||||||
|
|
||||||
inline Index rows() const { return m_matrix.rows(); }
|
inline Index rows() const { return m_matrix.rows(); }
|
||||||
inline Index cols() const { return m_matrix.cols(); }
|
inline Index cols() const { return m_matrix.cols(); }
|
||||||
|
|
||||||
@ -183,6 +198,7 @@ template<typename _MatrixType, int _UpLo> class LLT
|
|||||||
* The strict upper part is not used and even not initialized.
|
* The strict upper part is not used and even not initialized.
|
||||||
*/
|
*/
|
||||||
MatrixType m_matrix;
|
MatrixType m_matrix;
|
||||||
|
RealScalar m_l1_norm;
|
||||||
bool m_isInitialized;
|
bool m_isInitialized;
|
||||||
ComputationInfo m_info;
|
ComputationInfo m_info;
|
||||||
};
|
};
|
||||||
@ -393,6 +409,26 @@ LLT<MatrixType,_UpLo>& LLT<MatrixType,_UpLo>::compute(const EigenBase<InputType>
|
|||||||
m_matrix.resize(size, size);
|
m_matrix.resize(size, size);
|
||||||
m_matrix = a.derived();
|
m_matrix = a.derived();
|
||||||
|
|
||||||
|
// Compute matrix L1 norm = max abs column sum.
|
||||||
|
m_l1_norm = RealScalar(0);
|
||||||
|
if (_UpLo == Lower) {
|
||||||
|
for (int col = 0; col < size; ++col) {
|
||||||
|
const RealScalar abs_col_sum = m_matrix.col(col).tail(size - col).template lpNorm<1>() +
|
||||||
|
m_matrix.row(col).head(col).template lpNorm<1>();
|
||||||
|
if (abs_col_sum > m_l1_norm) {
|
||||||
|
m_l1_norm = abs_col_sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int col = 0; col < a.cols(); ++col) {
|
||||||
|
const RealScalar abs_col_sum = m_matrix.col(col).head(col).template lpNorm<1>() +
|
||||||
|
m_matrix.row(col).tail(size - col).template lpNorm<1>();
|
||||||
|
if (abs_col_sum > m_l1_norm) {
|
||||||
|
m_l1_norm = abs_col_sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_isInitialized = true;
|
m_isInitialized = true;
|
||||||
bool ok = Traits::inplace_decomposition(m_matrix);
|
bool ok = Traits::inplace_decomposition(m_matrix);
|
||||||
m_info = ok ? Success : NumericalIssue;
|
m_info = ok ? Success : NumericalIssue;
|
||||||
|
207
Eigen/src/Core/ConditionEstimator.h
Normal file
207
Eigen/src/Core/ConditionEstimator.h
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
// This file is part of Eigen, a lightweight C++ template library
|
||||||
|
// for linear algebra.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2016 Rasmus Munk Larsen (rmlarsen@google.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_CONDITIONESTIMATOR_H
|
||||||
|
#define EIGEN_CONDITIONESTIMATOR_H
|
||||||
|
|
||||||
|
namespace Eigen {
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
template <typename MatrixType>
|
||||||
|
inline typename MatrixType::RealScalar MatrixL1Norm(const MatrixType& matrix) {
|
||||||
|
return matrix.cwiseAbs().colwise().sum().maxCoeff();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Vector>
|
||||||
|
inline typename Vector::RealScalar VectorL1Norm(const Vector& v) {
|
||||||
|
return v.template lpNorm<1>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Vector, typename RealVector, bool IsComplex>
|
||||||
|
struct SignOrUnity {
|
||||||
|
static inline Vector run(const Vector& v) {
|
||||||
|
const RealVector v_abs = v.cwiseAbs();
|
||||||
|
return (v_abs.array() == static_cast<typename Vector::RealScalar>(0))
|
||||||
|
.select(Vector::Ones(v.size()), v.cwiseQuotient(v_abs));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Partial specialization to avoid elementwise division for real vectors.
|
||||||
|
template <typename Vector>
|
||||||
|
struct SignOrUnity<Vector, Vector, false> {
|
||||||
|
static inline Vector run(const Vector& v) {
|
||||||
|
return (v.array() < static_cast<typename Vector::RealScalar>(0))
|
||||||
|
.select(-Vector::Ones(v.size()), Vector::Ones(v.size()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
/** \class ConditionEstimator
|
||||||
|
* \ingroup Core_Module
|
||||||
|
*
|
||||||
|
* \brief Condition number estimator.
|
||||||
|
*
|
||||||
|
* Computing a decomposition of a dense matrix takes O(n^3) operations, while
|
||||||
|
* this method estimates the condition number quickly and reliably in O(n^2)
|
||||||
|
* operations.
|
||||||
|
*
|
||||||
|
* \returns an estimate of the reciprocal condition number
|
||||||
|
* (1 / (||matrix||_1 * ||inv(matrix)||_1)) of matrix, given the matrix and
|
||||||
|
* its decomposition. Supports the following decompositions: FullPivLU,
|
||||||
|
* PartialPivLU, LDLT, and LLT.
|
||||||
|
*
|
||||||
|
* \sa FullPivLU, PartialPivLU, LDLT, LLT.
|
||||||
|
*/
|
||||||
|
template <typename Decomposition>
|
||||||
|
typename Decomposition::RealScalar ReciprocalConditionNumberEstimate(
|
||||||
|
const typename Decomposition::MatrixType& matrix,
|
||||||
|
const Decomposition& dec) {
|
||||||
|
eigen_assert(matrix.rows() == dec.rows());
|
||||||
|
eigen_assert(matrix.cols() == dec.cols());
|
||||||
|
if (dec.rows() == 0) return typename Decomposition::RealScalar(1);
|
||||||
|
return ReciprocalConditionNumberEstimate(MatrixL1Norm(matrix), dec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \class ConditionEstimator
|
||||||
|
* \ingroup Core_Module
|
||||||
|
*
|
||||||
|
* \brief Condition number estimator.
|
||||||
|
*
|
||||||
|
* Computing a decomposition of a dense matrix takes O(n^3) operations, while
|
||||||
|
* this method estimates the condition number quickly and reliably in O(n^2)
|
||||||
|
* operations.
|
||||||
|
*
|
||||||
|
* \returns an estimate of the reciprocal condition number
|
||||||
|
* (1 / (||matrix||_1 * ||inv(matrix)||_1)) of matrix, given ||matrix||_1 and
|
||||||
|
* its decomposition. Supports the following decompositions: FullPivLU,
|
||||||
|
* PartialPivLU, LDLT, and LLT.
|
||||||
|
*
|
||||||
|
* \sa FullPivLU, PartialPivLU, LDLT, LLT.
|
||||||
|
*/
|
||||||
|
template <typename Decomposition>
|
||||||
|
typename Decomposition::RealScalar ReciprocalConditionNumberEstimate(
|
||||||
|
typename Decomposition::RealScalar matrix_norm, const Decomposition& dec) {
|
||||||
|
typedef typename Decomposition::RealScalar RealScalar;
|
||||||
|
eigen_assert(dec.rows() == dec.cols());
|
||||||
|
if (dec.rows() == 0) return RealScalar(1);
|
||||||
|
if (matrix_norm == RealScalar(0)) return RealScalar(0);
|
||||||
|
if (dec.rows() == 1) return RealScalar(1);
|
||||||
|
const typename Decomposition::RealScalar inverse_matrix_norm =
|
||||||
|
InverseMatrixL1NormEstimate(dec);
|
||||||
|
return (inverse_matrix_norm == RealScalar(0)
|
||||||
|
? RealScalar(0)
|
||||||
|
: (RealScalar(1) / inverse_matrix_norm) / matrix_norm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \returns an estimate of ||inv(matrix)||_1 given a decomposition of
|
||||||
|
* matrix that implements .solve() and .adjoint().solve() methods.
|
||||||
|
*
|
||||||
|
* The method implements Algorithms 4.1 and 5.1 from
|
||||||
|
* http://www.maths.manchester.ac.uk/~higham/narep/narep135.pdf
|
||||||
|
* which also forms the basis for the condition number estimators in
|
||||||
|
* LAPACK. Since at most 10 calls to the solve method of dec are
|
||||||
|
* performed, the total cost is O(dims^2), as opposed to O(dims^3)
|
||||||
|
* needed to compute the inverse matrix explicitly.
|
||||||
|
*
|
||||||
|
* The most common usage is in estimating the condition number
|
||||||
|
* ||matrix||_1 * ||inv(matrix)||_1. The first term ||matrix||_1 can be
|
||||||
|
* computed directly in O(n^2) operations.
|
||||||
|
*
|
||||||
|
* Supports the following decompositions: FullPivLU, PartialPivLU, LDLT, and
|
||||||
|
* LLT.
|
||||||
|
*
|
||||||
|
* \sa FullPivLU, PartialPivLU, LDLT, LLT.
|
||||||
|
*/
|
||||||
|
template <typename Decomposition>
|
||||||
|
typename Decomposition::RealScalar InverseMatrixL1NormEstimate(
|
||||||
|
const Decomposition& dec) {
|
||||||
|
typedef typename Decomposition::MatrixType MatrixType;
|
||||||
|
typedef typename Decomposition::Scalar Scalar;
|
||||||
|
typedef typename Decomposition::RealScalar RealScalar;
|
||||||
|
typedef typename internal::plain_col_type<MatrixType>::type Vector;
|
||||||
|
typedef typename internal::plain_col_type<MatrixType, RealScalar>::type
|
||||||
|
RealVector;
|
||||||
|
const bool is_complex = (NumTraits<Scalar>::IsComplex != 0);
|
||||||
|
|
||||||
|
eigen_assert(dec.rows() == dec.cols());
|
||||||
|
const Index n = dec.rows();
|
||||||
|
if (n == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Vector v = dec.solve(Vector::Ones(n) / Scalar(n));
|
||||||
|
|
||||||
|
// lower_bound is a lower bound on
|
||||||
|
// ||inv(matrix)||_1 = sup_v ||inv(matrix) v||_1 / ||v||_1
|
||||||
|
// and is the objective maximized by the ("super-") gradient ascent
|
||||||
|
// algorithm below.
|
||||||
|
RealScalar lower_bound = internal::VectorL1Norm(v);
|
||||||
|
if (n == 1) {
|
||||||
|
return lower_bound;
|
||||||
|
}
|
||||||
|
// Gradient ascent algorithm follows: We know that the optimum is achieved at
|
||||||
|
// one of the simplices v = e_i, so in each iteration we follow a
|
||||||
|
// super-gradient to move towards the optimal one.
|
||||||
|
RealScalar old_lower_bound = lower_bound;
|
||||||
|
Vector sign_vector(n);
|
||||||
|
Vector old_sign_vector;
|
||||||
|
Index v_max_abs_index = -1;
|
||||||
|
Index old_v_max_abs_index = v_max_abs_index;
|
||||||
|
for (int k = 0; k < 4; ++k) {
|
||||||
|
sign_vector = internal::SignOrUnity<Vector, RealVector, is_complex>::run(v);
|
||||||
|
if (k > 0 && !is_complex && sign_vector == old_sign_vector) {
|
||||||
|
// Break if the solution stagnated.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// v_max_abs_index = argmax |real( inv(matrix)^T * sign_vector )|
|
||||||
|
v = dec.adjoint().solve(sign_vector);
|
||||||
|
v.real().cwiseAbs().maxCoeff(&v_max_abs_index);
|
||||||
|
if (v_max_abs_index == old_v_max_abs_index) {
|
||||||
|
// Break if the solution stagnated.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Move to the new simplex e_j, where j = v_max_abs_index.
|
||||||
|
v = dec.solve(Vector::Unit(n, v_max_abs_index)); // v = inv(matrix) * e_j.
|
||||||
|
lower_bound = internal::VectorL1Norm(v);
|
||||||
|
if (lower_bound <= old_lower_bound) {
|
||||||
|
// Break if the gradient step did not increase the lower_bound.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!is_complex) {
|
||||||
|
old_sign_vector = sign_vector;
|
||||||
|
}
|
||||||
|
old_v_max_abs_index = v_max_abs_index;
|
||||||
|
old_lower_bound = lower_bound;
|
||||||
|
}
|
||||||
|
// The following calculates an independent estimate of ||matrix||_1 by
|
||||||
|
// multiplying matrix by a vector with entries of slowly increasing
|
||||||
|
// magnitude and alternating sign:
|
||||||
|
// v_i = (-1)^{i} (1 + (i / (dim-1))), i = 0,...,dim-1.
|
||||||
|
// This improvement to Hager's algorithm above is due to Higham. It was
|
||||||
|
// added to make the algorithm more robust in certain corner cases where
|
||||||
|
// large elements in the matrix might otherwise escape detection due to
|
||||||
|
// exact cancellation (especially when op and op_adjoint correspond to a
|
||||||
|
// sequence of backsubstitutions and permutations), which could cause
|
||||||
|
// Hager's algorithm to vastly underestimate ||matrix||_1.
|
||||||
|
Scalar alternating_sign(RealScalar(1));
|
||||||
|
for (Index i = 0; i < n; ++i) {
|
||||||
|
v[i] = alternating_sign *
|
||||||
|
(RealScalar(1) + (RealScalar(i) / (RealScalar(n - 1))));
|
||||||
|
alternating_sign = -alternating_sign;
|
||||||
|
}
|
||||||
|
v = dec.solve(v);
|
||||||
|
const RealScalar alternate_lower_bound =
|
||||||
|
(2 * internal::VectorL1Norm(v)) / (3 * RealScalar(n));
|
||||||
|
return numext::maxi(lower_bound, alternate_lower_bound);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Eigen
|
||||||
|
|
||||||
|
#endif
|
@ -231,6 +231,15 @@ template<typename _MatrixType> class FullPivLU
|
|||||||
return Solve<FullPivLU, Rhs>(*this, b.derived());
|
return Solve<FullPivLU, Rhs>(*this, b.derived());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \returns an estimate of the reciprocal condition number of the matrix of which *this is
|
||||||
|
the LU decomposition.
|
||||||
|
*/
|
||||||
|
inline RealScalar rcond() const
|
||||||
|
{
|
||||||
|
eigen_assert(m_isInitialized && "PartialPivLU is not initialized.");
|
||||||
|
return ReciprocalConditionNumberEstimate(m_l1_norm, *this);
|
||||||
|
}
|
||||||
|
|
||||||
/** \returns the determinant of the matrix of which
|
/** \returns the determinant of the matrix of which
|
||||||
* *this is the LU decomposition. It has only linear complexity
|
* *this is the LU decomposition. It has only linear complexity
|
||||||
* (that is, O(n) where n is the dimension of the square matrix)
|
* (that is, O(n) where n is the dimension of the square matrix)
|
||||||
@ -410,6 +419,7 @@ template<typename _MatrixType> class FullPivLU
|
|||||||
IntColVectorType m_rowsTranspositions;
|
IntColVectorType m_rowsTranspositions;
|
||||||
IntRowVectorType m_colsTranspositions;
|
IntRowVectorType m_colsTranspositions;
|
||||||
Index m_det_pq, m_nonzero_pivots;
|
Index m_det_pq, m_nonzero_pivots;
|
||||||
|
RealScalar m_l1_norm;
|
||||||
RealScalar m_maxpivot, m_prescribedThreshold;
|
RealScalar m_maxpivot, m_prescribedThreshold;
|
||||||
bool m_isInitialized, m_usePrescribedThreshold;
|
bool m_isInitialized, m_usePrescribedThreshold;
|
||||||
};
|
};
|
||||||
@ -455,11 +465,12 @@ FullPivLU<MatrixType>& FullPivLU<MatrixType>::compute(const EigenBase<InputType>
|
|||||||
// the permutations are stored as int indices, so just to be sure:
|
// the permutations are stored as int indices, so just to be sure:
|
||||||
eigen_assert(matrix.rows()<=NumTraits<int>::highest() && matrix.cols()<=NumTraits<int>::highest());
|
eigen_assert(matrix.rows()<=NumTraits<int>::highest() && matrix.cols()<=NumTraits<int>::highest());
|
||||||
|
|
||||||
m_isInitialized = true;
|
|
||||||
m_lu = matrix.derived();
|
m_lu = matrix.derived();
|
||||||
|
m_l1_norm = m_lu.cwiseAbs().colwise().sum().maxCoeff();
|
||||||
|
|
||||||
computeInPlace();
|
computeInPlace();
|
||||||
|
|
||||||
|
m_isInitialized = true;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +76,6 @@ template<typename _MatrixType> class PartialPivLU
|
|||||||
typedef Transpositions<RowsAtCompileTime, MaxRowsAtCompileTime> TranspositionType;
|
typedef Transpositions<RowsAtCompileTime, MaxRowsAtCompileTime> TranspositionType;
|
||||||
typedef typename MatrixType::PlainObject PlainObject;
|
typedef typename MatrixType::PlainObject PlainObject;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Default Constructor.
|
* \brief Default Constructor.
|
||||||
*
|
*
|
||||||
@ -152,6 +151,15 @@ template<typename _MatrixType> class PartialPivLU
|
|||||||
return Solve<PartialPivLU, Rhs>(*this, b.derived());
|
return Solve<PartialPivLU, Rhs>(*this, b.derived());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \returns an estimate of the reciprocal condition number of the matrix of which *this is
|
||||||
|
the LU decomposition.
|
||||||
|
*/
|
||||||
|
inline RealScalar rcond() const
|
||||||
|
{
|
||||||
|
eigen_assert(m_isInitialized && "PartialPivLU is not initialized.");
|
||||||
|
return ReciprocalConditionNumberEstimate(m_l1_norm, *this);
|
||||||
|
}
|
||||||
|
|
||||||
/** \returns the inverse of the matrix of which *this is the LU decomposition.
|
/** \returns the inverse of the matrix of which *this is the LU decomposition.
|
||||||
*
|
*
|
||||||
* \warning The matrix being decomposed here is assumed to be invertible. If you need to check for
|
* \warning The matrix being decomposed here is assumed to be invertible. If you need to check for
|
||||||
@ -178,7 +186,7 @@ template<typename _MatrixType> class PartialPivLU
|
|||||||
*
|
*
|
||||||
* \sa MatrixBase::determinant()
|
* \sa MatrixBase::determinant()
|
||||||
*/
|
*/
|
||||||
typename internal::traits<MatrixType>::Scalar determinant() const;
|
Scalar determinant() const;
|
||||||
|
|
||||||
MatrixType reconstructedMatrix() const;
|
MatrixType reconstructedMatrix() const;
|
||||||
|
|
||||||
@ -247,6 +255,7 @@ template<typename _MatrixType> class PartialPivLU
|
|||||||
PermutationType m_p;
|
PermutationType m_p;
|
||||||
TranspositionType m_rowsTranspositions;
|
TranspositionType m_rowsTranspositions;
|
||||||
Index m_det_p;
|
Index m_det_p;
|
||||||
|
RealScalar m_l1_norm;
|
||||||
bool m_isInitialized;
|
bool m_isInitialized;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -256,6 +265,7 @@ PartialPivLU<MatrixType>::PartialPivLU()
|
|||||||
m_p(),
|
m_p(),
|
||||||
m_rowsTranspositions(),
|
m_rowsTranspositions(),
|
||||||
m_det_p(0),
|
m_det_p(0),
|
||||||
|
m_l1_norm(0),
|
||||||
m_isInitialized(false)
|
m_isInitialized(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -266,6 +276,7 @@ PartialPivLU<MatrixType>::PartialPivLU(Index size)
|
|||||||
m_p(size),
|
m_p(size),
|
||||||
m_rowsTranspositions(size),
|
m_rowsTranspositions(size),
|
||||||
m_det_p(0),
|
m_det_p(0),
|
||||||
|
m_l1_norm(0),
|
||||||
m_isInitialized(false)
|
m_isInitialized(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -277,6 +288,7 @@ PartialPivLU<MatrixType>::PartialPivLU(const EigenBase<InputType>& matrix)
|
|||||||
m_p(matrix.rows()),
|
m_p(matrix.rows()),
|
||||||
m_rowsTranspositions(matrix.rows()),
|
m_rowsTranspositions(matrix.rows()),
|
||||||
m_det_p(0),
|
m_det_p(0),
|
||||||
|
m_l1_norm(0),
|
||||||
m_isInitialized(false)
|
m_isInitialized(false)
|
||||||
{
|
{
|
||||||
compute(matrix.derived());
|
compute(matrix.derived());
|
||||||
@ -467,6 +479,7 @@ PartialPivLU<MatrixType>& PartialPivLU<MatrixType>::compute(const EigenBase<Inpu
|
|||||||
eigen_assert(matrix.rows()<NumTraits<int>::highest());
|
eigen_assert(matrix.rows()<NumTraits<int>::highest());
|
||||||
|
|
||||||
m_lu = matrix.derived();
|
m_lu = matrix.derived();
|
||||||
|
m_l1_norm = m_lu.cwiseAbs().colwise().sum().maxCoeff();
|
||||||
|
|
||||||
eigen_assert(matrix.rows() == matrix.cols() && "PartialPivLU is only for square (and moreover invertible) matrices");
|
eigen_assert(matrix.rows() == matrix.cols() && "PartialPivLU is only for square (and moreover invertible) matrices");
|
||||||
const Index size = matrix.rows();
|
const Index size = matrix.rows();
|
||||||
@ -484,7 +497,7 @@ PartialPivLU<MatrixType>& PartialPivLU<MatrixType>::compute(const EigenBase<Inpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename MatrixType>
|
template<typename MatrixType>
|
||||||
typename internal::traits<MatrixType>::Scalar PartialPivLU<MatrixType>::determinant() const
|
typename PartialPivLU<MatrixType>::Scalar PartialPivLU<MatrixType>::determinant() const
|
||||||
{
|
{
|
||||||
eigen_assert(m_isInitialized && "PartialPivLU is not initialized.");
|
eigen_assert(m_isInitialized && "PartialPivLU is not initialized.");
|
||||||
return Scalar(m_det_p) * m_lu.diagonal().prod();
|
return Scalar(m_det_p) * m_lu.diagonal().prod();
|
||||||
|
@ -17,6 +17,12 @@
|
|||||||
#include <Eigen/Cholesky>
|
#include <Eigen/Cholesky>
|
||||||
#include <Eigen/QR>
|
#include <Eigen/QR>
|
||||||
|
|
||||||
|
template<typename MatrixType, int UpLo>
|
||||||
|
typename MatrixType::RealScalar matrix_l1_norm(const MatrixType& m) {
|
||||||
|
MatrixType symm = m.template selfadjointView<UpLo>();
|
||||||
|
return symm.cwiseAbs().colwise().sum().maxCoeff();
|
||||||
|
}
|
||||||
|
|
||||||
template<typename MatrixType,template <typename,int> class CholType> void test_chol_update(const MatrixType& symm)
|
template<typename MatrixType,template <typename,int> class CholType> void test_chol_update(const MatrixType& symm)
|
||||||
{
|
{
|
||||||
typedef typename MatrixType::Scalar Scalar;
|
typedef typename MatrixType::Scalar Scalar;
|
||||||
@ -85,6 +91,14 @@ template<typename MatrixType> void cholesky(const MatrixType& m)
|
|||||||
matX = chollo.solve(matB);
|
matX = chollo.solve(matB);
|
||||||
VERIFY_IS_APPROX(symm * matX, matB);
|
VERIFY_IS_APPROX(symm * matX, matB);
|
||||||
|
|
||||||
|
const MatrixType symmLo_inverse = chollo.solve(MatrixType::Identity(rows,cols));
|
||||||
|
RealScalar rcond = (RealScalar(1) / matrix_l1_norm<MatrixType, Lower>(symmLo)) /
|
||||||
|
matrix_l1_norm<MatrixType, Lower>(symmLo_inverse);
|
||||||
|
RealScalar rcond_est = chollo.rcond();
|
||||||
|
// Verify that the estimated condition number is within a factor of 10 of the
|
||||||
|
// truth.
|
||||||
|
VERIFY(rcond_est > rcond / 10 && rcond_est < rcond * 10);
|
||||||
|
|
||||||
// test the upper mode
|
// test the upper mode
|
||||||
LLT<SquareMatrixType,Upper> cholup(symmUp);
|
LLT<SquareMatrixType,Upper> cholup(symmUp);
|
||||||
VERIFY_IS_APPROX(symm, cholup.reconstructedMatrix());
|
VERIFY_IS_APPROX(symm, cholup.reconstructedMatrix());
|
||||||
@ -93,6 +107,15 @@ template<typename MatrixType> void cholesky(const MatrixType& m)
|
|||||||
matX = cholup.solve(matB);
|
matX = cholup.solve(matB);
|
||||||
VERIFY_IS_APPROX(symm * matX, matB);
|
VERIFY_IS_APPROX(symm * matX, matB);
|
||||||
|
|
||||||
|
// Verify that the estimated condition number is within a factor of 10 of the
|
||||||
|
// truth.
|
||||||
|
const MatrixType symmUp_inverse = cholup.solve(MatrixType::Identity(rows,cols));
|
||||||
|
rcond = (RealScalar(1) / matrix_l1_norm<MatrixType, Upper>(symmUp)) /
|
||||||
|
matrix_l1_norm<MatrixType, Upper>(symmUp_inverse);
|
||||||
|
rcond_est = cholup.rcond();
|
||||||
|
VERIFY(rcond_est > rcond / 10 && rcond_est < rcond * 10);
|
||||||
|
|
||||||
|
|
||||||
MatrixType neg = -symmLo;
|
MatrixType neg = -symmLo;
|
||||||
chollo.compute(neg);
|
chollo.compute(neg);
|
||||||
VERIFY(chollo.info()==NumericalIssue);
|
VERIFY(chollo.info()==NumericalIssue);
|
||||||
@ -137,6 +160,15 @@ template<typename MatrixType> void cholesky(const MatrixType& m)
|
|||||||
matX = ldltlo.solve(matB);
|
matX = ldltlo.solve(matB);
|
||||||
VERIFY_IS_APPROX(symm * matX, matB);
|
VERIFY_IS_APPROX(symm * matX, matB);
|
||||||
|
|
||||||
|
const MatrixType symmLo_inverse = ldltlo.solve(MatrixType::Identity(rows,cols));
|
||||||
|
RealScalar rcond = (RealScalar(1) / matrix_l1_norm<MatrixType, Lower>(symmLo)) /
|
||||||
|
matrix_l1_norm<MatrixType, Lower>(symmLo_inverse);
|
||||||
|
RealScalar rcond_est = ldltlo.rcond();
|
||||||
|
// Verify that the estimated condition number is within a factor of 10 of the
|
||||||
|
// truth.
|
||||||
|
VERIFY(rcond_est > rcond / 10 && rcond_est < rcond * 10);
|
||||||
|
|
||||||
|
|
||||||
LDLT<SquareMatrixType,Upper> ldltup(symmUp);
|
LDLT<SquareMatrixType,Upper> ldltup(symmUp);
|
||||||
VERIFY_IS_APPROX(symm, ldltup.reconstructedMatrix());
|
VERIFY_IS_APPROX(symm, ldltup.reconstructedMatrix());
|
||||||
vecX = ldltup.solve(vecB);
|
vecX = ldltup.solve(vecB);
|
||||||
@ -144,6 +176,14 @@ template<typename MatrixType> void cholesky(const MatrixType& m)
|
|||||||
matX = ldltup.solve(matB);
|
matX = ldltup.solve(matB);
|
||||||
VERIFY_IS_APPROX(symm * matX, matB);
|
VERIFY_IS_APPROX(symm * matX, matB);
|
||||||
|
|
||||||
|
// Verify that the estimated condition number is within a factor of 10 of the
|
||||||
|
// truth.
|
||||||
|
const MatrixType symmUp_inverse = ldltup.solve(MatrixType::Identity(rows,cols));
|
||||||
|
rcond = (RealScalar(1) / matrix_l1_norm<MatrixType, Upper>(symmUp)) /
|
||||||
|
matrix_l1_norm<MatrixType, Upper>(symmUp_inverse);
|
||||||
|
rcond_est = ldltup.rcond();
|
||||||
|
VERIFY(rcond_est > rcond / 10 && rcond_est < rcond * 10);
|
||||||
|
|
||||||
VERIFY_IS_APPROX(MatrixType(ldltlo.matrixL().transpose().conjugate()), MatrixType(ldltlo.matrixU()));
|
VERIFY_IS_APPROX(MatrixType(ldltlo.matrixL().transpose().conjugate()), MatrixType(ldltlo.matrixU()));
|
||||||
VERIFY_IS_APPROX(MatrixType(ldltlo.matrixU().transpose().conjugate()), MatrixType(ldltlo.matrixL()));
|
VERIFY_IS_APPROX(MatrixType(ldltlo.matrixU().transpose().conjugate()), MatrixType(ldltlo.matrixL()));
|
||||||
VERIFY_IS_APPROX(MatrixType(ldltup.matrixL().transpose().conjugate()), MatrixType(ldltup.matrixU()));
|
VERIFY_IS_APPROX(MatrixType(ldltup.matrixL().transpose().conjugate()), MatrixType(ldltup.matrixU()));
|
||||||
|
23
test/lu.cpp
23
test/lu.cpp
@ -11,6 +11,11 @@
|
|||||||
#include <Eigen/LU>
|
#include <Eigen/LU>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
template<typename MatrixType>
|
||||||
|
typename MatrixType::RealScalar matrix_l1_norm(const MatrixType& m) {
|
||||||
|
return m.cwiseAbs().colwise().sum().maxCoeff();
|
||||||
|
}
|
||||||
|
|
||||||
template<typename MatrixType> void lu_non_invertible()
|
template<typename MatrixType> void lu_non_invertible()
|
||||||
{
|
{
|
||||||
typedef typename MatrixType::Index Index;
|
typedef typename MatrixType::Index Index;
|
||||||
@ -143,7 +148,14 @@ template<typename MatrixType> void lu_invertible()
|
|||||||
m3 = MatrixType::Random(size,size);
|
m3 = MatrixType::Random(size,size);
|
||||||
m2 = lu.solve(m3);
|
m2 = lu.solve(m3);
|
||||||
VERIFY_IS_APPROX(m3, m1*m2);
|
VERIFY_IS_APPROX(m3, m1*m2);
|
||||||
VERIFY_IS_APPROX(m2, lu.inverse()*m3);
|
MatrixType m1_inverse = lu.inverse();
|
||||||
|
VERIFY_IS_APPROX(m2, m1_inverse*m3);
|
||||||
|
|
||||||
|
RealScalar rcond = (RealScalar(1) / matrix_l1_norm(m1)) / matrix_l1_norm(m1_inverse);
|
||||||
|
const RealScalar rcond_est = lu.rcond();
|
||||||
|
// Verify that the estimated condition number is within a factor of 10 of the
|
||||||
|
// truth.
|
||||||
|
VERIFY(rcond_est > rcond / 10 && rcond_est < rcond * 10);
|
||||||
|
|
||||||
// test solve with transposed
|
// test solve with transposed
|
||||||
lu.template _solve_impl_transposed<false>(m3, m2);
|
lu.template _solve_impl_transposed<false>(m3, m2);
|
||||||
@ -170,6 +182,7 @@ template<typename MatrixType> void lu_partial_piv()
|
|||||||
PartialPivLU.h
|
PartialPivLU.h
|
||||||
*/
|
*/
|
||||||
typedef typename MatrixType::Index Index;
|
typedef typename MatrixType::Index Index;
|
||||||
|
typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar;
|
||||||
Index size = internal::random<Index>(1,4);
|
Index size = internal::random<Index>(1,4);
|
||||||
|
|
||||||
MatrixType m1(size, size), m2(size, size), m3(size, size);
|
MatrixType m1(size, size), m2(size, size), m3(size, size);
|
||||||
@ -181,7 +194,13 @@ template<typename MatrixType> void lu_partial_piv()
|
|||||||
m3 = MatrixType::Random(size,size);
|
m3 = MatrixType::Random(size,size);
|
||||||
m2 = plu.solve(m3);
|
m2 = plu.solve(m3);
|
||||||
VERIFY_IS_APPROX(m3, m1*m2);
|
VERIFY_IS_APPROX(m3, m1*m2);
|
||||||
VERIFY_IS_APPROX(m2, plu.inverse()*m3);
|
MatrixType m1_inverse = plu.inverse();
|
||||||
|
VERIFY_IS_APPROX(m2, m1_inverse*m3);
|
||||||
|
|
||||||
|
RealScalar rcond = (RealScalar(1) / matrix_l1_norm(m1)) / matrix_l1_norm(m1_inverse);
|
||||||
|
const RealScalar rcond_est = plu.rcond();
|
||||||
|
// Verify that the estimate is within a factor of 10 of the truth.
|
||||||
|
VERIFY(rcond_est > rcond / 10 && rcond_est < rcond * 10);
|
||||||
|
|
||||||
// test solve with transposed
|
// test solve with transposed
|
||||||
plu.template _solve_impl_transposed<false>(m3, m2);
|
plu.template _solve_impl_transposed<false>(m3, m2);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user