Various fixes in polynomial solver and its unit tests:

- cleanup noise in imaginary part of real roots
 - take into account the magnitude of the derivative to check roots.
 - use <= instead of < at appropriate places
This commit is contained in:
Gael Guennebaud 2018-12-09 22:54:39 +01:00
parent 348bb386d1
commit 450dc97c6b
3 changed files with 46 additions and 8 deletions

View File

@ -75,8 +75,7 @@ class companion
void setPolynomial( const VectorType& poly ) void setPolynomial( const VectorType& poly )
{ {
const Index deg = poly.size()-1; const Index deg = poly.size()-1;
m_monic = Scalar(-1)/poly[deg] * poly.head(deg); m_monic = -poly.head(deg)/poly[deg];
//m_bl_diag.setIdentity( deg-1 );
m_bl_diag.setOnes(deg-1); m_bl_diag.setOnes(deg-1);
} }

View File

@ -126,7 +126,7 @@ class PolynomialSolverBase
for( Index i=0; i<m_roots.size(); ++i ) for( Index i=0; i<m_roots.size(); ++i )
{ {
if( abs( m_roots[i].imag() ) < absImaginaryThreshold ) if( abs( m_roots[i].imag() ) <= absImaginaryThreshold )
{ {
if( !hasArealRoot ) if( !hasArealRoot )
{ {
@ -144,10 +144,10 @@ class PolynomialSolverBase
} }
} }
} }
else else if(!hasArealRoot)
{ {
if( abs( m_roots[i].imag() ) < abs( m_roots[res].imag() ) ){ if( abs( m_roots[i].imag() ) < abs( m_roots[res].imag() ) ){
res = i; } res = i;}
} }
} }
return numext::real_ref(m_roots[res]); return numext::real_ref(m_roots[res]);
@ -167,7 +167,7 @@ class PolynomialSolverBase
for( Index i=0; i<m_roots.size(); ++i ) for( Index i=0; i<m_roots.size(); ++i )
{ {
if( abs( m_roots[i].imag() ) < absImaginaryThreshold ) if( abs( m_roots[i].imag() ) <= absImaginaryThreshold )
{ {
if( !hasArealRoot ) if( !hasArealRoot )
{ {
@ -340,6 +340,7 @@ class PolynomialSolver : public PolynomialSolverBase<_Scalar,_Deg>
typedef typename internal::conditional<NumTraits<Scalar>::IsComplex, typedef typename internal::conditional<NumTraits<Scalar>::IsComplex,
ComplexEigenSolver<CompanionMatrixType>, ComplexEigenSolver<CompanionMatrixType>,
EigenSolver<CompanionMatrixType> >::type EigenSolverType; EigenSolver<CompanionMatrixType> >::type EigenSolverType;
typedef typename internal::conditional<NumTraits<Scalar>::IsComplex, Scalar, std::complex<Scalar> >::type ComplexScalar;
public: public:
/** Computes the complex roots of a new polynomial. */ /** Computes the complex roots of a new polynomial. */
@ -354,6 +355,27 @@ class PolynomialSolver : public PolynomialSolverBase<_Scalar,_Deg>
companion.balance(); companion.balance();
m_eigenSolver.compute( companion.denseMatrix() ); m_eigenSolver.compute( companion.denseMatrix() );
m_roots = m_eigenSolver.eigenvalues(); m_roots = m_eigenSolver.eigenvalues();
MatrixXcd A = companion.denseMatrix();
// cleanup noise in imaginary part of real roots:
// if the imaginary part is rather small compared to the real part
// and that cancelling the imaginary part yield a smaller evaluation,
// then it's safe to keep the real part only.
RealScalar coarse_prec = std::pow(4,poly.size()+1)*NumTraits<RealScalar>::epsilon();
std::cout << coarse_prec << "\n";
for(Index i = 0; i<m_roots.size(); ++i)
{
if( internal::isMuchSmallerThan(numext::abs(numext::imag(m_roots[i])),
numext::abs(numext::real(m_roots[i])),
coarse_prec) )
{
ComplexScalar as_real_root = ComplexScalar(numext::real(m_roots[i]));
if( numext::abs(poly_eval(poly, as_real_root))
<= numext::abs(poly_eval(poly, m_roots[i])))
{
m_roots[i] = as_real_root;
}
}
}
} }
else if(poly.size () == 2) else if(poly.size () == 2)
{ {

View File

@ -26,6 +26,16 @@ struct increment_if_fixed_size
} }
} }
template<typename PolynomialType>
PolynomialType polyder(const PolynomialType& p)
{
typedef typename PolynomialType::Scalar Scalar;
PolynomialType res(p.size());
for(Index i=1; i<p.size(); ++i)
res[i-1] = p[i]*Scalar(i);
res[p.size()-1] = 0.;
return res;
}
template<int Deg, typename POLYNOMIAL, typename SOLVER> template<int Deg, typename POLYNOMIAL, typename SOLVER>
bool aux_evalSolver( const POLYNOMIAL& pols, SOLVER& psolve ) bool aux_evalSolver( const POLYNOMIAL& pols, SOLVER& psolve )
@ -44,10 +54,17 @@ bool aux_evalSolver( const POLYNOMIAL& pols, SOLVER& psolve )
psolve.compute( pols ); psolve.compute( pols );
const RootsType& roots( psolve.roots() ); const RootsType& roots( psolve.roots() );
EvalRootsType evr( deg ); EvalRootsType evr( deg );
POLYNOMIAL pols_der = polyder(pols);
EvalRootsType der( deg );
for( int i=0; i<roots.size(); ++i ){ for( int i=0; i<roots.size(); ++i ){
evr[i] = std::abs( poly_eval( pols, roots[i] ) ); } evr[i] = std::abs( poly_eval( pols, roots[i] ) );
der[i] = numext::maxi(RealScalar(1.), std::abs( poly_eval( pols_der, roots[i] ) ));
}
bool evalToZero = evr.isZero( test_precision<Scalar>() ); // we need to divide by the magnitude of the derivative because
// with a high derivative is very small error in the value of the root
// yiels a very large error in the polynomial evaluation.
bool evalToZero = (evr.cwiseQuotient(der)).isZero( test_precision<Scalar>() );
if( !evalToZero ) if( !evalToZero )
{ {
cerr << "WRONG root: " << endl; cerr << "WRONG root: " << endl;