Program Listing for File Utils_Poly.hxx¶
↰ Return to documentation for file (Utils/Utils_Poly.hxx)
/*--------------------------------------------------------------------------*\
| |
| Copyright (C) 2022 |
| |
| , __ , __ |
| /|/ \ /|/ \ |
| | __/ _ ,_ | __/ _ ,_ |
| | \|/ / | | | | \|/ / | | | |
| |(__/|__/ |_/ \_/|/|(__/|__/ |_/ \_/|/ |
| /| /| |
| \| \| |
| |
| Enrico Bertolazzi |
| Dipartimento di Ingegneria Industriale |
| Universita` degli Studi di Trento |
| email: enrico.bertolazzi@unitn.it |
| |
\*--------------------------------------------------------------------------*/
#ifndef DOXYGEN_SHOULD_SKIP_THIS
#define UTILS_POLY_INTANTIATE( REAL ) \
namespace Utils { \
template class Poly<REAL>; \
template class Sturm<REAL>; \
\
template Poly<REAL> operator + ( Poly<REAL> const & a, Poly<REAL> const & b ); \
template Poly<REAL> operator + ( Poly<REAL> const & a, REAL b ); \
template Poly<REAL> operator + ( REAL a, Poly<REAL> const & b ); \
template Poly<REAL> operator - ( Poly<REAL> const & a, Poly<REAL> const & b ); \
template Poly<REAL> operator - ( Poly<REAL> const & a, REAL b ); \
template Poly<REAL> operator - ( REAL a, Poly<REAL> const & b ); \
template Poly<REAL> operator * ( Poly<REAL> const & a, Poly<REAL> const & b ); \
template Poly<REAL> operator * ( REAL a, Poly<REAL> const & b ); \
template Poly<REAL> operator * ( Poly<REAL> const & a, REAL b ); \
template void divide( Poly<REAL> const & p, Poly<REAL> const & q, Poly<REAL> & M, Poly<REAL> & R ); \
template void GCD( Poly<REAL> const & p, Poly<REAL> const & q, Poly<REAL> & g ); \
}
#endif
namespace Utils {
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Poly<Real> &
Poly<Real>::set_scalar( Real a ) {
this->resize(1);
this->coeffRef(0) = a;
m_order = 1;
return *this;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Poly<Real> &
Poly<Real>::set_monomial( Real a ) {
this->resize(2);
this->coeffRef(0) = a;
this->coeffRef(1) = 1;
m_order = 2;
return *this;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Real
Poly<Real>::eval( Real x ) const {
// Calcolo il polinomio usando il metodo di Horner
Integer n = m_order-1;
Real res = this->coeff(n);
while ( n-- > 0 ) res = res*x+this->coeff(n);
return res;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Real
Poly<Real>::eval_D( Real x ) const {
// Calcolo il polinomio usando il metodo di Horner
Integer n = m_order-1;
Real Dp = this->coeff(n)*n;
while ( --n > 0 ) Dp = Dp*x+this->coeff(n)*n;
return Dp;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
void
Poly<Real>::eval( Real x, Real & p, Real & Dp ) const {
// Calcolo il polinomio usando il metodo di Horner
Integer n = m_order-1;
p = this->coeff(n);
Dp = this->coeff(n)*n;
while ( --n > 0 ) {
p = p*x+this->coeff(n);
Dp = Dp*x+this->coeff(n)*n;
}
p = p*x+this->coeff(0);
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
void
Poly<Real>::derivative( Poly<Real> & der ) const {
der.resize( m_order-1 ); // nuovo polinomio contenente il risultato
for( Integer i = 1; i < m_order; ++i )
der.coeffRef(i-1) = i * this->coeff(i);
der.m_order = m_order-1;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
void
Poly<Real>::integral( Poly<Real> & itg ) const {
itg.resize( m_order+1 ); // nuovo polinomio contenente il risultato
itg.coeffRef(0) = 0;
for ( Integer i = 1; i <= m_order; ++i )
itg.coeffRef(i) = this->coeff(i-1)/i;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Real
Poly<Real>::normalize() {
// search max module coeff
Real S = this->cwiseAbs().maxCoeff();
if ( S > 0 ) this->to_eigen() /= S;
return S;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
void
Poly<Real>::purge( Real epsi ) {
for ( Integer i = 0; i < m_order; ++i ) {
Real & ai = this->coeffRef(i);
Real absi = std::abs( ai );
if ( absi <= epsi ) ai = 0;
}
adjust_degree();
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
void
Poly<Real>::adjust_degree() {
while ( m_order > 0 && this->coeff(m_order-1) == 0 ) --m_order;
this->conservativeResize( m_order );
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
typename Poly<Real>::Integer
Poly<Real>::sign_variations() const {
Integer sign_var = 0;
Integer last_sign = 0;
for ( Integer i=0 ; i < m_order; ++i ) {
Real v = this->coeff(i);
if ( v > 0 ) {
if ( last_sign == -1 ) ++sign_var;
last_sign = 1;
} else if ( v < 0 ) {
if ( last_sign == 1 ) ++sign_var;
last_sign = -1;
}
}
return sign_var;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Poly<Real> &
Poly<Real>::operator = ( Poly<Real> const & b ) {
this->resize( b.m_order );
this->to_eigen().noalias() = b.to_eigen();
m_order = b.m_order;
return *this;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Poly<Real> &
Poly<Real>::operator += ( Poly<Real> const & b ) {
Integer max_order = max( m_order, b.m_order );
Integer min_order = min( m_order, b.m_order );
// ridimensiona vettore coefficienti senza distruggere il contenuto
this->conservativeResize( max_order );
// somma i coefficienti fino al grado comune ad entrambi i polinomi
this->head( min_order ).noalias() += b.head(min_order);
Integer n_tail = b.m_order - m_order;
if ( n_tail > 0 ) this->tail( n_tail ).noalias() = b.tail(n_tail);
m_order = max_order;
return *this;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Poly<Real> &
Poly<Real>::operator += ( Real b ) {
if ( m_order > 0 ) this->coeffRef(0) += b;
else {
this->resize(1);
this->coeffRef(0) = b;
m_order = 1;
}
return *this;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Poly<Real> &
Poly<Real>::operator -= ( Poly<Real> const & b ) {
Integer max_order = max( m_order, b.m_order );
Integer min_order = min( m_order, b.m_order );
// ridimensiona vettore coefficienti senza distruggere il contenuto
this->conservativeResize( max_order );
// somma i coefficienti fino al grado comune ad entrambi i polinomi
this->head( min_order ).noalias() -= b.head(min_order);
Integer n_tail = b.m_order - m_order;
if ( n_tail > 0 ) this->tail( n_tail ).noalias() = -b.tail(n_tail);
m_order = max_order;
return *this;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Poly<Real> &
Poly<Real>::operator -= ( Real b ) {
if ( m_order > 0 ) this->coeffRef(0) -= b;
else {
this->resize(1);
this->coeffRef(0) = -b;
m_order = 1;
}
return *this;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Poly<Real> &
Poly<Real>::operator *= ( Poly<Real> const & b ) {
dvec_t a(this->to_eigen()); // fa una copia dei coefficienti del vettore
Integer new_order = m_order + b.m_order - 1;
this->resize( m_order + b.m_order - 1 ); // nuovo polinomio contenente il risultato
this->setZero();
for( Integer i=0; i<m_order; ++i )
for( Integer j=0; j<b.m_order; ++j )
this->coeffRef(i+j) += a.coeff(i) * b.coeff(j);
m_order = new_order;
return *this;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Poly<Real> &
Poly<Real>::operator *= ( Real b ) {
this->to_eigen() *= b;
return *this;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Poly<Real>
operator + ( Poly<Real> const & a, Poly<Real> const & b ) {
typedef typename Poly<Real>::Integer Integer;
Integer max_order = max( a.order(), b.order() );
Integer min_order = min( a.order(), b.order() );
Poly<Real> sum( max_order ); // nuovo polinomio contenente la somma
// somma i coefficienti fino al grado comune ad entrambi i polinomi
sum.head( min_order ).noalias() = a.head(min_order) + b.head(min_order);
Integer n_tail = max_order - min_order;
if ( n_tail > 0 ) {
if ( a.order() > b.order() ) sum.tail( n_tail ).noalias() = a.tail(n_tail);
else sum.tail( n_tail ).noalias() = b.tail(n_tail);
}
return sum;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Poly<Real>
operator + ( Poly<Real> const & a, Real b ) {
typedef typename Poly<Real>::Integer Integer;
Integer max_order = max( a.order(), 1 );
Poly<Real> sum( max_order ); // nuovo polinomio contenente la somma
// somma i coefficienti fino al grado comune ad entrambi i polinomi
if ( a.order() > 0 ) {
sum.coeffRef(0) = a.coeff(0) + b;
if ( a.order() > 1 ) sum.tail( a.order()-1 ).noalias() = a.tail(a.order()-1);
} else {
sum.coeffRef(0) = b;
}
return sum;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Poly<Real>
operator + ( Real a, Poly<Real> const & b ) {
typedef typename Poly<Real>::Integer Integer;
Integer max_order = max( b.order(), 1 );
Poly<Real> sum( max_order ); // nuovo polinomio contenente la somma
// somma i coefficienti fino al grado comune ad entrambi i polinomi
if ( b.order() > 0 ) {
sum.coeffRef(0) = a + b.coeff(0);
if ( b.order() > 1 ) sum.tail( b.order()-1 ).noalias() = b.tail(b.order()-1);
} else {
sum.coeffRef(0) = a;
}
return sum;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Poly<Real>
operator - ( Poly<Real> const & a, Poly<Real> const & b ) {
typedef typename Poly<Real>::Integer Integer;
Integer max_order = max( a.order(), b.order() );
Integer min_order = min( a.order(), b.order() );
Poly<Real> sum( max_order ); // nuovo polinomio contenente la somma
// somma i coefficienti fino al grado comune ad entrambi i polinomi
sum.head( min_order ).noalias() = a.head(min_order) - b.head(min_order);
Integer n_tail = max_order - min_order;
if ( n_tail > 0 ) {
if ( a.order() > b.order() ) sum.tail( n_tail ).noalias() = a.tail(n_tail);
else sum.tail( n_tail ).noalias() = -b.tail(n_tail);
}
return sum;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Poly<Real>
operator - ( Poly<Real> const & a, Real b ) {
typedef typename Poly<Real>::Integer Integer;
Integer max_order = max( a.order(), 1 );
Poly<Real> sum( max_order ); // nuovo polinomio contenente la somma
// somma i coefficienti fino al grado comune ad entrambi i polinomi
if ( a.order() > 0 ) {
sum.coeffRef(0) = a.coeff(0) - b;
if ( a.order() > 1 ) sum.tail( a.order()-1 ).noalias() = a.tail(a.order()-1);
} else {
sum.coeffRef(0) = -b;
}
return sum;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Poly<Real>
operator - ( Real a, Poly<Real> const & b ) {
typedef typename Poly<Real>::Integer Integer;
Integer max_order = max( b.order(), 1 );
Poly<Real> sum( max_order ); // nuovo polinomio contenente la somma
// somma i coefficienti fino al grado comune ad entrambi i polinomi
if ( b.order() > 0 ) {
sum.coeffRef(0) = a - b.coeff(0);
if ( b.order() > 1 ) sum.tail( b.order()-1 ).noalias() = -b.tail(b.order()-1);
} else {
sum.coeffRef(0) = a;
}
return sum;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Poly<Real>
operator * ( Poly<Real> const & a, Poly<Real> const & b ) {
typedef typename Poly<Real>::Integer Integer;
Poly<Real> prd( a.order() + b.order() - 1 ); // nuovo polinomio contenente il risultato
for( Integer i = 0; i < a.order(); ++i )
for( Integer j = 0; j < b.order(); ++j )
prd.coeffRef(i+j) += a.coeff(i) * b.coeff(j);
return prd;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Poly<Real>
operator * ( Real a, Poly<Real> const & b ) {
Poly<Real> prd( b.order() ); // nuovo polinomio contenente il risultato
prd.noalias() = a*b.to_eigen();
return prd;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
Poly<Real>
operator * ( Poly<Real> const & a, Real b ) {
Poly<Real> prd( a.order() ); // nuovo polinomio contenente il risultato
prd.noalias() = a.to_eigen()*b;
return prd;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
void
divide(
Poly<Real> const & p,
Poly<Real> const & q,
Poly<Real> & M,
Poly<Real> & R
) {
static Real epsi = 100*std::numeric_limits<Real>::epsilon();
typedef typename Poly<Real>::Integer Integer;
Poly<Real> P(p), Q(q);
//
// scale polynomials
//
// P(x) = p(x) / scaleP, Q(x) = q(x) / scaleQ
//
Real scaleP = P.normalize();
Real scaleQ = Q.normalize();
//
// P(x) = Q(x) * M(x) + R(x)
//
R = P;
Real lcQ = Q.leading_coeff();
Integer dd = R.order() - Q.order();
Integer R_degree = R.degree();
M.set_order(dd+1);
UTILS_ASSERT0(
!isZero(lcQ),
"Poly::divide(p,q,M,R), leading coefficient of q(x) is 0!"
);
while ( dd >= 0 && R_degree >= 0 ) {
Real lcR = R(R_degree);
Real bf = lcR/lcQ;
M.coeffRef(dd) = bf;
R.segment(dd,Q.degree()).noalias() -= bf*Q.head(Q.degree());
R.coeffRef(R_degree) = 0;
--R_degree;
--dd;
}
// adjust degree or remainder
R.purge(epsi);
R.adjust_degree();
// scale back polinomials
//
// P(x) = Q(x) * M(x) + R(x)
// p(x) / scaleP = q(x) / scaleQ * M(x) + R(x)
// p(x) = q(x) * (scaleP/scaleQ) * M(x) + scaleP*R(x)
//
M *= scaleP/scaleQ;
R *= scaleP;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
void
GCD(
Poly<Real> const & p,
Poly<Real> const & q,
Poly<Real> & g
) {
if ( q.order() > 0 ) {
Poly<Real> M, R;
divide( p, q, M, R );
GCD( q, R, g );
} else {
g = p;
}
g.normalize();
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
void
Sturm<Real>::build( Poly_t const & P ) {
m_intervals.clear();
Poly_t DP, M, R;
P.derivative( DP );
m_sturm.clear();
m_sturm.reserve(P.order());
m_sturm.emplace_back(P); m_sturm.back().adjust_degree();
m_sturm.emplace_back(DP); m_sturm.back().adjust_degree();
Integer ns = 1;
while ( true ) {
divide( m_sturm[ns-1], m_sturm[ns], M, R );
if ( R.order() <= 0 ) break;
m_sturm.push_back(-R);
++ns;
}
// divide by GCD
for ( Integer i = 0; i < ns; ++i ) {
divide( m_sturm[i], m_sturm[ns], M, R );
M.normalize();
m_sturm[i] = M;
}
m_sturm[ns].set_scalar(1);
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
typename Sturm<Real>::Integer
Sturm<Real>::sign_variations( Real x, bool & on_root ) const {
Integer sign_var = 0;
Integer last_sign = 0;
Integer npoly = Integer(m_sturm.size());
Real v = m_sturm[0].eval(x);
on_root = false;
if ( v > 0 ) last_sign = 1;
else if ( v < 0 ) last_sign = -1;
else {
on_root = true;
last_sign = 0;
}
for ( Integer i = 1; i < npoly; ++i ) {
v = m_sturm[i].eval(x);
if ( v > 0 ) {
if ( last_sign == -1 ) ++sign_var;
last_sign = 1;
} else if ( v < 0 ) {
if ( last_sign == 1 ) ++sign_var;
last_sign = -1;
}
}
return sign_var;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
typename Sturm<Real>::Integer
Sturm<Real>::separate_roots( Real a, Real b ) {
m_a = a;
m_b = b;
bool on_root_a, on_root_b;
Integer va = sign_variations(a,on_root_a);
Integer vb = sign_variations(b,on_root_b);
while ( on_root_a ) {
// on root, move interval a
a -= 1e-8*(b-a);
va = sign_variations(a,on_root_a);
}
while ( on_root_b ) {
// on root, move interval a
b += 1e-8*(b-a);
vb = sign_variations(b,on_root_b);
}
Integer n_roots = std::abs( va - vb );
if ( n_roots == 0 ) return 0;
m_intervals.resize( n_roots );
Interval & I0 = m_intervals[0];
I0.a = a; I0.va = va;
I0.b = b; I0.vb = vb;
if ( n_roots == 1 ) return 1;
// search intervals
Integer i_pos = 0;
Integer n_seg = 1;
while ( i_pos < n_roots ) {
Interval & I = m_intervals[i_pos];
// refine segment
Real c = (I.a+I.b)/2;
bool on_root_c;
Integer vc = sign_variations(c,on_root_c);
if ( on_root_c ) {
for ( Integer iter = 2; iter <= 20 && on_root_c; ++iter ) {
c = (I.a*iter+I.b)/(1+iter);
vc = sign_variations(c,on_root_c);
if ( on_root_c ) {
c = (I.a+I.b*iter)/(1+iter);
vc = sign_variations(c,on_root_c);
}
}
}
UTILS_ASSERT(
!on_root_c,
"Sturm<Real>::separate_roots(a={},b={}), failed\n",
m_a, m_b
);
if ( I.va == vc ) { // LO <- c
I.a = c;
I.va = vc;
} else if ( I.vb == vc ) { // HI <- c
I.b = c;
I.vb = vc;
} else { // split interval!
// second interval
Interval & I1 = m_intervals[n_seg];
I1.a = c; I1.va = vc;
I1.b = I.b; I1.vb = I.vb;
// first interval
I.b = c; I.vb = vc;
++n_seg;
// skip interval with sign variation == 1
while ( i_pos < n_seg ) {
Interval const & I2 = m_intervals[i_pos];
if ( std::abs( I2.vb - I2.va ) > 1 ) break; // found interval to be analysed
++i_pos;
};
}
}
// sort intervals
std::sort(
m_intervals.begin(),
m_intervals.end(),
[]( Interval const & Sa, Interval const & Sb ) { return Sa.a < Sb.a; }
);
return n_roots;
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
template <typename Real>
void
Sturm<Real>::refine_roots( Real tol ) {
Real x = 0, dx;
Poly_t const & P = m_sturm[0];
m_roots.resize( m_intervals.size() );
Integer n = 0;
for ( auto & I : m_intervals ) {
Real a = I.a;
Real b = I.b;
// ------------------------
Real fa = P.eval( a );
Real fb = P.eval( b );
// controlla consistenza dati del problema
UTILS_ASSERT( fa * fb <= 0, "ERRORE" );
Integer num_ok = 0;
for ( Integer i = 0; i < m_max_iter; ++i ) {
if ( std::abs(fa) < std::abs(fb) ) {
dx = fa/P.eval_D(a);
x = a - dx;
} else {
dx = fb/P.eval_D(b);
x = b - dx;
}
// If Newton failed use bisection
Real ba = b-a;
Real a_min = a+0.1*ba;
Real b_max = b-0.1*ba;
if ( x < a_min || x > b_max ) { x = (a+b)/2; num_ok = 0; } // if using bisection reset quasi ok iteration count
//if ( ! ( x > a && x < b ) ) { x = (a+b)/2 }
Real fx = P.eval( x );
bool ok1 = std::abs(fx) < tol;
bool ok2 = std::abs(dx) < tol;
if ( ok1 && ok2 ) break;
if ( ok1 || ok2 ) { if ( ++num_ok > 3 ) break; }
else { num_ok = 0; }
if ( fa*fx < 0 ) { b = x; fb = fx; } // interval [a,x]
else { a = x; fa = fx; } // interval [c,b]
}
m_roots.coeffRef(n++) = x;
}
}
/*
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
}