Rewrite balancer to avoid overflows.

The previous balancer overflowed for large row/column norms.
Modified to prevent that.

Fixes #2273.
This commit is contained in:
Antonio Sanchez 2021-06-18 14:24:11 -07:00 committed by Rasmus Munk Larsen
parent 35a367d557
commit e9ab4278b7

View File

@ -20,12 +20,6 @@ namespace internal {
#ifndef EIGEN_PARSED_BY_DOXYGEN #ifndef EIGEN_PARSED_BY_DOXYGEN
template <typename T>
T radix(){ return 2; }
template <typename T>
T radix2(){ return radix<T>()*radix<T>(); }
template<int Size> template<int Size>
struct decrement_if_fixed_size struct decrement_if_fixed_size
{ {
@ -141,7 +135,10 @@ inline
bool companion<_Scalar,_Deg>::balanced( RealScalar colNorm, RealScalar rowNorm, bool companion<_Scalar,_Deg>::balanced( RealScalar colNorm, RealScalar rowNorm,
bool& isBalanced, RealScalar& colB, RealScalar& rowB ) bool& isBalanced, RealScalar& colB, RealScalar& rowB )
{ {
if( RealScalar(0) == colNorm || RealScalar(0) == rowNorm ){ return true; } if( RealScalar(0) == colNorm || RealScalar(0) == rowNorm
|| !(numext::isfinite)(colNorm) || !(numext::isfinite)(rowNorm)){
return true;
}
else else
{ {
//To find the balancing coefficients, if the radix is 2, //To find the balancing coefficients, if the radix is 2,
@ -149,33 +146,41 @@ bool companion<_Scalar,_Deg>::balanced( RealScalar colNorm, RealScalar rowNorm,
// \f$ 2^{2\sigma-1} < rowNorm / colNorm \le 2^{2\sigma+1} \f$ // \f$ 2^{2\sigma-1} < rowNorm / colNorm \le 2^{2\sigma+1} \f$
// then the balancing coefficient for the row is \f$ 1/2^{\sigma} \f$ // then the balancing coefficient for the row is \f$ 1/2^{\sigma} \f$
// and the balancing coefficient for the column is \f$ 2^{\sigma} \f$ // and the balancing coefficient for the column is \f$ 2^{\sigma} \f$
rowB = rowNorm / radix<RealScalar>(); const RealScalar radix = RealScalar(2);
const RealScalar radix2 = RealScalar(4);
rowB = rowNorm / radix;
colB = RealScalar(1); colB = RealScalar(1);
const RealScalar s = colNorm + rowNorm; const RealScalar s = colNorm + rowNorm;
while (colNorm < rowB) // Find sigma s.t. rowNorm / 2 <= 2^(2*sigma) * colNorm
RealScalar scout = colNorm;
while (scout < rowB)
{ {
colB *= radix<RealScalar>(); colB *= radix;
colNorm *= radix2<RealScalar>(); scout *= radix2;
} }
rowB = rowNorm * radix<RealScalar>(); // We now have an upper-bound for sigma, try to lower it.
// Find sigma s.t. 2^(2*sigma) * colNorm / 2 < rowNorm
while (colNorm >= rowB) scout = colNorm * (colB / radix) * colB; // Avoid overflow.
while (scout >= rowNorm)
{ {
colB /= radix<RealScalar>(); colB /= radix;
colNorm /= radix2<RealScalar>(); scout /= radix2;
} }
//This line is used to avoid insubstantial balancing // This line is used to avoid insubstantial balancing.
if ((rowNorm + colNorm) < RealScalar(0.95) * s * colB) if ((rowNorm + radix * scout) < RealScalar(0.95) * s * colB)
{ {
isBalanced = false; isBalanced = false;
rowB = RealScalar(1) / colB; rowB = RealScalar(1) / colB;
return false; return false;
} }
else{ else
return true; } {
return true;
}
} }
} }