mirror of
https://gitlab.com/libeigen/eigen.git
synced 2025-01-30 17:40:05 +08:00
New scoring functor to select the pivot.
This is can be useful for non-floating point scalars, where choosing the biggest element is generally not the best choice.
This commit is contained in:
parent
ccc1277a42
commit
37a93c4263
@ -55,6 +55,34 @@ struct functor_traits<scalar_abs_op<Scalar> >
|
||||
};
|
||||
};
|
||||
|
||||
/** \internal
|
||||
* \brief Template functor to compute the score of a scalar, to chose a pivot
|
||||
*
|
||||
* \sa class CwiseUnaryOp
|
||||
*/
|
||||
template<typename Scalar> struct scalar_score_coeff_op : scalar_abs_op<Scalar>
|
||||
{
|
||||
typedef void Score_is_abs;
|
||||
};
|
||||
template<typename Scalar>
|
||||
struct functor_traits<scalar_score_coeff_op<Scalar> > : functor_traits<scalar_abs_op<Scalar> > {};
|
||||
|
||||
/* Avoid recomputing abs when we know the score and they are the same. Not a true Eigen functor. */
|
||||
template<typename Scalar, typename=void> struct abs_knowing_score
|
||||
{
|
||||
EIGEN_EMPTY_STRUCT_CTOR(abs_knowing_score)
|
||||
typedef typename NumTraits<Scalar>::Real result_type;
|
||||
template<typename Score>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const result_type operator() (const Scalar& a, const Score&) const { using std::abs; return abs(a); }
|
||||
};
|
||||
template<typename Scalar> struct abs_knowing_score<Scalar, typename scalar_score_coeff_op<Scalar>::Score_is_abs>
|
||||
{
|
||||
EIGEN_EMPTY_STRUCT_CTOR(abs_knowing_score)
|
||||
typedef typename NumTraits<Scalar>::Real result_type;
|
||||
template<typename Scal>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const result_type operator() (const Scal&, const result_type& a) const { return a; }
|
||||
};
|
||||
|
||||
/** \internal
|
||||
* \brief Template functor to compute the squared absolute value of a scalar
|
||||
*
|
||||
|
@ -459,14 +459,16 @@ FullPivLU<MatrixType>& FullPivLU<MatrixType>::compute(const MatrixType& matrix)
|
||||
|
||||
// biggest coefficient in the remaining bottom-right corner (starting at row k, col k)
|
||||
Index row_of_biggest_in_corner, col_of_biggest_in_corner;
|
||||
RealScalar biggest_in_corner;
|
||||
typedef internal::scalar_score_coeff_op<Scalar> Scoring;
|
||||
typedef typename Scoring::result_type Score;
|
||||
Score biggest_in_corner;
|
||||
biggest_in_corner = m_lu.bottomRightCorner(rows-k, cols-k)
|
||||
.cwiseAbs()
|
||||
.unaryExpr(Scoring())
|
||||
.maxCoeff(&row_of_biggest_in_corner, &col_of_biggest_in_corner);
|
||||
row_of_biggest_in_corner += k; // correct the values! since they were computed in the corner,
|
||||
col_of_biggest_in_corner += k; // need to add k to them.
|
||||
|
||||
if(biggest_in_corner==RealScalar(0))
|
||||
if(biggest_in_corner==Score(0))
|
||||
{
|
||||
// before exiting, make sure to initialize the still uninitialized transpositions
|
||||
// in a sane state without destroying what we already have.
|
||||
@ -479,7 +481,8 @@ FullPivLU<MatrixType>& FullPivLU<MatrixType>::compute(const MatrixType& matrix)
|
||||
break;
|
||||
}
|
||||
|
||||
if(biggest_in_corner > m_maxpivot) m_maxpivot = biggest_in_corner;
|
||||
RealScalar abs_pivot = internal::abs_knowing_score<Scalar>()(m_lu(row_of_biggest_in_corner, col_of_biggest_in_corner), biggest_in_corner);
|
||||
if(abs_pivot > m_maxpivot) m_maxpivot = abs_pivot;
|
||||
|
||||
// Now that we've found the pivot, we need to apply the row/col swaps to
|
||||
// bring it to the location (k,k).
|
||||
|
@ -275,6 +275,8 @@ struct partial_lu_impl
|
||||
*/
|
||||
static Index unblocked_lu(MatrixType& lu, PivIndex* row_transpositions, PivIndex& nb_transpositions)
|
||||
{
|
||||
typedef scalar_score_coeff_op<Scalar> Scoring;
|
||||
typedef typename Scoring::result_type Score;
|
||||
const Index rows = lu.rows();
|
||||
const Index cols = lu.cols();
|
||||
const Index size = (std::min)(rows,cols);
|
||||
@ -286,13 +288,13 @@ struct partial_lu_impl
|
||||
Index rcols = cols-k-1;
|
||||
|
||||
Index row_of_biggest_in_col;
|
||||
RealScalar biggest_in_corner
|
||||
= lu.col(k).tail(rows-k).cwiseAbs().maxCoeff(&row_of_biggest_in_col);
|
||||
Score biggest_in_corner
|
||||
= lu.col(k).tail(rows-k).unaryExpr(Scoring()).maxCoeff(&row_of_biggest_in_col);
|
||||
row_of_biggest_in_col += k;
|
||||
|
||||
row_transpositions[k] = PivIndex(row_of_biggest_in_col);
|
||||
|
||||
if(biggest_in_corner != RealScalar(0))
|
||||
if(biggest_in_corner != Score(0))
|
||||
{
|
||||
if(k != row_of_biggest_in_col)
|
||||
{
|
||||
|
@ -443,13 +443,15 @@ FullPivHouseholderQR<MatrixType>& FullPivHouseholderQR<MatrixType>::compute(cons
|
||||
for (Index k = 0; k < size; ++k)
|
||||
{
|
||||
Index row_of_biggest_in_corner, col_of_biggest_in_corner;
|
||||
RealScalar biggest_in_corner;
|
||||
typedef internal::scalar_score_coeff_op<Scalar> Scoring;
|
||||
typedef typename Scoring::result_type Score;
|
||||
|
||||
biggest_in_corner = m_qr.bottomRightCorner(rows-k, cols-k)
|
||||
.cwiseAbs()
|
||||
.maxCoeff(&row_of_biggest_in_corner, &col_of_biggest_in_corner);
|
||||
Score score = m_qr.bottomRightCorner(rows-k, cols-k)
|
||||
.unaryExpr(Scoring())
|
||||
.maxCoeff(&row_of_biggest_in_corner, &col_of_biggest_in_corner);
|
||||
row_of_biggest_in_corner += k;
|
||||
col_of_biggest_in_corner += k;
|
||||
RealScalar biggest_in_corner = internal::abs_knowing_score<Scalar>()(m_qr(row_of_biggest_in_corner, col_of_biggest_in_corner), score);
|
||||
if(k==0) biggest = biggest_in_corner;
|
||||
|
||||
// if the corner is negligible, then we have less than full rank, and we can finish early
|
||||
|
@ -157,6 +157,67 @@ inline adouble abs2(const adouble& x) { return x*x; }
|
||||
#endif // ADOLCSUPPORT_H
|
||||
\endcode
|
||||
|
||||
This other example adds support for the \c mpq_class type from <a href="https://gmplib.org/">GMP</a>. It shows in particular how to change the way Eigen picks the best pivot during LU factorization. It selects the coefficient with the highest score, where the score is by default the absolute value of a number, but we can define a different score, for instance to prefer pivots with a more compact representation (this is an example, not a recommendation). Note that the scores should always be non-negative and only zero is allowed to have a score of zero. Also, this can interact badly with thresholds for inexact scalar types.
|
||||
|
||||
\code
|
||||
#include <gmpxx.h>
|
||||
#include <Eigen/Core>
|
||||
#include <boost/operators.hpp>
|
||||
|
||||
namespace Eigen {
|
||||
template<class> struct NumTraits;
|
||||
template<> struct NumTraits<mpq_class>
|
||||
{
|
||||
typedef mpq_class Real;
|
||||
typedef mpq_class NonInteger;
|
||||
typedef mpq_class Nested;
|
||||
|
||||
static inline Real epsilon() { return 0; }
|
||||
static inline Real dummy_precision() { return 0; }
|
||||
|
||||
enum {
|
||||
IsInteger = 0,
|
||||
IsSigned = 1,
|
||||
IsComplex = 0,
|
||||
RequireInitialization = 1,
|
||||
ReadCost = 6,
|
||||
AddCost = 150,
|
||||
MulCost = 100
|
||||
};
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
template<>
|
||||
struct significant_decimals_impl<mpq_class>
|
||||
{
|
||||
// Infinite precision when printing
|
||||
static inline int run() { return 0; }
|
||||
};
|
||||
|
||||
template<> struct scalar_score_coeff_op<mpq_class> {
|
||||
struct result_type : boost::totally_ordered1<result_type> {
|
||||
std::size_t len;
|
||||
result_type(int i = 0) : len(i) {} // Eigen uses Score(0) and Score()
|
||||
result_type(mpq_class const& q) :
|
||||
len(mpz_size(q.get_num_mpz_t())+
|
||||
mpz_size(q.get_den_mpz_t())-1) {}
|
||||
friend bool operator<(result_type x, result_type y) {
|
||||
// 0 is the worst possible pivot
|
||||
if (x.len == 0) return y.len > 0;
|
||||
if (y.len == 0) return false;
|
||||
// Prefer a pivot with a small representation
|
||||
return x.len > y.len;
|
||||
}
|
||||
friend bool operator==(result_type x, result_type y) {
|
||||
// Only used to test if the score is 0
|
||||
return x.len == y.len;
|
||||
}
|
||||
};
|
||||
result_type operator()(mpq_class const& x) const { return x; }
|
||||
};
|
||||
}
|
||||
}
|
||||
\endcode
|
||||
|
||||
\sa \ref TopicPreprocessorDirectives
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user