cgal/Packages/Core/include/CORE/poly/Sturm.h

701 lines
20 KiB
C++

/******************************************************************
* Core Library Version 1.6, June 2003
* Copyright (c) 1995-2003 Exact Computation Project
File: Sturm.h
Description:
The templated class Sturm implements Sturm sequences.
Basic capabilities include:
counting number of roots in an interval,
isolating all roots in an interval
isolating the i-th largest (or smallest) root in interval
It is based on the Polynomial class.
> BigFloat intervals are used for this (new) version.
> It is very important that the BigFloats used in these intervals
> have no error at the beginning, and this is maintained
> by refinement. Note that if x, y are error-free BigFloats,
> then (x+y)/2 may not be error-free (in current implementaion.
> We have to call a special "exact divide by 2" method,
> (x+y).div2() for this purpose.
>
> TODO LIST and Potential Bugs:
> (1) Split an isolating interval to give definite sign
> (2) Should have a test for square-free polynomials
> (3) The copy constructor seems to fail sometimes?
Author: Chee Yap and Sylvain Pion, Vikram Sharma
Date: July 20, 2002
Core Library Version 1.5
$Id$
************************************** */
#include <CORE/BigFloat.h>
#include <CORE/poly/Poly.h>
CORE_BEGIN_NAMESPACE
// ==================================================
// Sturm Class
// ==================================================
template < class NT >
class Sturm {
public:
int len; // length of array
Polynomial<NT> * seq; // array of polynomials of length "len"
static int N_STOP_ITER; // Stop IterE after this many iterations. This is
// initialized below, outside the Newton class
bool NEWTON_DIV_BY_ZERO;
// This is set to true when there is divide by
// zero in Newton iteration (at critical value)
// User is responsible to check this and to reset.
typedef Polynomial<NT> PolyNT;
// CONSTRUCTORS
Sturm() : len(0), NEWTON_DIV_BY_ZERO(false){ // null constructor
}
Sturm(Polynomial<NT> pp) : NEWTON_DIV_BY_ZERO(false) { // constructor from polynomial
int ell = pp.getTrueDegree();
seq = new Polynomial<NT> [ell+1];
seq[0] = pp; // Primitive part is important
// to speed up large polynomials!
// However, for first 2 polymials, we
// DO NOT take primitive part, because we
// want to use them in Newton Iteration
seq[1] = differentiate(pp);
int i;
for (i=2; i <= ell; i++) {
seq[i] = seq[i-2];
seq[i].pseudoRemainder(seq[i-1]);
if (zeroP(seq[i])) break;
seq[i].primPart();
}
len = i;
}
// copy constructor
Sturm(const Sturm&s) : len(s.len), NEWTON_DIV_BY_ZERO(s.NEWTON_DIV_BY_ZERO) {
seq = new Polynomial<NT> [len];
for (int i=0; i<len; i++)
seq[i] = s.seq[i];
}
const Sturm &operator=(const Sturm &o){
if (this == &o) return *this;
if (len != 0) delete[] seq;
seq=NULL;
NEWTON_DIV_BY_ZERO=o.NEWTON_DIV_BY_ZERO;
len= o.len;
if (len !=0){
seq = new Polynomial<NT> [len];
for (int i=0; i<len; i++)
seq[i] = o.seq[i];
}
return *this;
}
~Sturm() {
if (len != 0) delete[] seq;
}
// METHODS
// dump functions
void dump(std::string msg) const {
std::cerr << msg << std::endl;
for (int i=0; i<len; i++)
std::cerr << " seq[" << i << "] = " << seq[i] << std::endl;
}
void dump() const {
dump("");
}
// signVariations(x, sx)
// where sx = sign of evaluating the first polynomial at x
// PRE-CONDITION: sx != 0
int signVariations(const BigFloat & x, int sx) const
{
assert(sx != 0);
int cnt = 0;
int last_sign = sx;
for (int i=0; i<len; i++)
{
int sgn = seq[i].eval(x).sign();
if (sgn*last_sign < 0)
cnt++;
if (sgn != 0)
last_sign = sgn;
}
return cnt;
}
// signVariations(x)
// --the first polynomial eval is not yet done
int signVariations(const BigFloat & x) const
{
int signx = seq[0].eval(x).sign();
if (signx == 0)
return (-1); // error!
return signVariations(x, signx);
}//signVariations(x)
// signVariation at +Infinity
int signVariationsAtPosInfty() const
{
int cnt = 0;
int last_sign = seq[0].coeff[seq[0].getTrueDegree()].sign();
assert(last_sign != 0);
for (int i=1; i<len; i++)
{
int sgn = seq[i].coeff[seq[i].getTrueDegree()].sign();
if (sgn*last_sign < 0)
cnt++;
if (sgn != 0)
last_sign = sgn;
}
return cnt;
}
// signVariation at -Infinity
int signVariationsAtNegInfty() const
{
int cnt = 0;
int last_sign = seq[0].coeff[seq[0].getTrueDegree()].sign();
if (seq[0].getTrueDegree() % 2 != 0) last_sign *= -1;
assert(last_sign != 0);
for (int i=1; i<len; i++)
{
int parity = (seq[i].getTrueDegree() % 2 == 0) ? 1 : -1;
int sgn = parity * seq[i].coeff[seq[i].getTrueDegree()].sign();
if (sgn*last_sign < 0)
cnt++;
if (sgn != 0)
last_sign = sgn;
}
return cnt;
}
// numberOfRoots(x,y):
// COUNT NUMBER OF ROOTS in the close interval [x,y]
// IMPORTANT: Must get it right even if x, y are roots
int numberOfRoots(const BigFloat &x, const BigFloat &y) const
{
// assert(x <= y); // we allow x=y (probabably never used)
int signx = seq[0].eval(x).sign();
int signy = seq[0].eval(y).sign();
// easy case: THIS SHOULD BE THE OVERWHELMING MAJORITY
if (signx != 0 && signy != 0)
return (signVariations(x, signx) - signVariations(y, signy));
// harder case: THIS SHOULD BE VERY INFREQUENT
BigFloat sep = (seq[0].sepBound())/2;
BigFloat newx, newy;
if (signx == 0) newx = x - sep; else newx = x;
if (signy == 0) newy = y + sep; else newy = y;
return (signVariations(newx, seq[0].eval(newx).sign())
- signVariations(newy, seq[0].eval(newy).sign()) );
}//numberOfRoots
// numberOfRoots above x:
// NOTE: should do a special case for x=0
///////////////////////////////////////////
int numberOfRootsAbove(const BigFloat &x) const
{
int signx = seq[0].eval(x).sign();
if (signx != 0)
return signVariations(x, signx) - signVariationsAtPosInfty();
BigFloat newx = x - (seq[0].sepBound())/2;
return signVariations(newx, seq[0].eval(newx).sign())
- signVariationsAtPosInfty();
}
// numberOfRoots below x:
// NOTE: should do a special case for x=0
///////////////////////////////////////////
int numberOfRootsBelow(const BigFloat &x) const
{
int signx = seq[0].eval(x).sign();
if (signx != 0)
return signVariationsAtNegInfty() - signVariations(x, signx);
BigFloat newx = x + (seq[0].sepBound())/2;
return signVariationsAtNegInfty()
- signVariations(newx, seq[0].eval(newx).sign());
}
// isolateRoots(x, y, v)
/// isolates all the roots in [x,y] and returns them in v.
/** v is a list of intervals
* [x,y] is the initial interval to be isolated
*/
void isolateRoots(const BigFloat &x, const BigFloat &y,
BFVecInterval &v) const
{
assert(x<=y);
int n = numberOfRoots(x,y);
if (n == 0)
return;
if (n == 1) {
if ((x <= 0) && (y >= 0)) { // if 0 is inside our interval
if (seq[0].coeff[0] == 0)
v.push_back(std::make_pair(BigFloat(0), BigFloat(0)));
else if (numberOfRoots(0,y) == 0)
v.push_back(std::make_pair(x, BigFloat(0)));
else
v.push_back(std::make_pair(BigFloat(0), y));
} else
v.push_back(std::make_pair(x, y));
} else {
BigFloat mid = (x+y).div2();
isolateRoots(x, mid, v);
isolateRoots(mid, y, v);
}
}
// isolateRoots(v)
/// isolates all roots and returns them in v
/** v is a vector of isolated intervals
*/
void isolateRoots(BFVecInterval &v) const
{
BigFloat bd = seq[0].CauchyUpperBound();
// Note: bd is an exact BigFloat (this is important)
isolateRoots(-bd, bd, v);
}
// isolateRoot(i)
/// isolates the i-th smallest root
BFInterval isolateRoot(int i) const
{
if (i == 0) return mainRoot();
BigFloat bd = seq[0].CauchyUpperBound();
return isolateRoot(i, -bd, bd);
}
// isolateRoot(i, x, y)
/// isolates the i-th smallest root in [x,y]
/** If i is negative, then we want the i-th largest root in [x,y]
* We assume i is not zero.
*/
BFInterval isolateRoot(int i, BigFloat x, BigFloat y) const
{
int n = numberOfRoots(x,y);
if (i < 0) {
i += n+1;
if (i <= 0) return BFInterval(1,0); // ERROR CONDITION
}
if (n < i) return BFInterval(1,0); // ERROR CONDITION INDICATED
if (n == 1) return BFInterval(x, y);
BigFloat m = (x+y).div2();
n = numberOfRoots(x, m);
if (n < i)
return isolateRoot(i-n, m, y);
else
return isolateRoot(i, x, m);
}
// same as isolateRoot(i).
BFInterval diamond(int i) const
{
return isolateRoot(i);
}
// First root above
BFInterval firstRootAbove(const BigFloat &e) const
{
return isolateRoot(1, e, seq[0].CauchyUpperBound());
}
// Main root (i.e., first root above 0)
BFInterval mainRoot() const
{
return isolateRoot(1, 0, seq[0].CauchyUpperBound());
}
// First root below
BFInterval firstRootBelow(const BigFloat &e) const
{
BigFloat bd = seq[0].CauchyUpperBound(); // bd is exact
int n = numberOfRoots(-bd, e);
if (n <= 0) return BFInterval(1,0);
BigFloat bdBF = BigFloat(ceil(bd));
if (n == 1) return BFInterval(-bdBF, e);
return isolateRoot(n, -bdBF, e);
}
// Refine an interval I to absolute precision 2^{-aprec}
// THIS USES bisection only! Use only for debugging (it is too slow)
//
BFInterval refine(const BFInterval& I, int aprec) const {
// assert( There is a unique root in I )
// We repeat binary search till the following holds
// width/2^n <= eps (eps = 2^(-aprec))
// => log(width/eps) <= n
// => n = ceil(log(width/eps)) this many steps of binary search
// will work.
// At each step we verify
// seq[0].eval(J.first) * seq[0].eval(J.second) < 0
BigFloat width = I.second - I.first;
BigFloat eps = BigFloat::exp2(-aprec); // eps = 2^{-aprec}
extLong n = width.uMSB() + (extLong)aprec;
BFInterval J = I; // Return value is the Interval J
BigFloat midpoint;
while(n >= 0){
midpoint = (J.second + J.first).div2();
BigFloat m = seq[0].eval(midpoint);
if (m == 0) {
J.first = J.second = midpoint;
return J;
}
if (seq[0].eval(J.first) * m < 0) {
J.second = midpoint;
} else {
J.first = midpoint;
}
n--;
}
return J;
}//End Refine
// Refine First root above
BFInterval refinefirstRootAbove(const BigFloat &e, int aprec) const
{
BFInterval I = firstRootAbove(e);
return refine(I,aprec);
}
// Refine First root below
BFInterval refinefirstRootBelow(const BigFloat &e, int aprec) const
{
BFInterval I = firstRootBelow(e);
return refine(I,aprec);
}
// refineAllRoots(v, aprec)
// will modify v so that v is a list of isolating intervals for
// the roots of the polynomial in *this. The size of these intervals
// are at most 2^{-aprec}.
// If v is non-null, we assume it is a list of initial isolating intervals.
// If v is null, we will first call isolateRoots(v) to set this up.
void refineAllRoots( BFVecInterval &v, int aprec){
BFVecInterval v1;
BFInterval J;
if (v.empty())
isolateRoots(v);
for (BFVecInterval::const_iterator it = v.begin();
it != v.end(); ++it) { // Iterate through all the intervals
//refine them to the given precision aprec
J = refine(BFInterval(it->first, it->second), aprec);
v1.push_back(std::make_pair(J.first, J.second));
}
v.swap(v1);
}//End of refineAllRoots
// This is the experimental version of "refineAllRoots"
// based on Newton iteration
//
void newtonRefineAllRoots( BFVecInterval &v, int aprec){
BFVecInterval v1;
BFInterval J;
if (v.empty())
isolateRoots(v);
for (BFVecInterval::const_iterator it = v.begin();
it != v.end(); ++it) { // Iterate through all the intervals
//refine them to the given precision aprec
J = newtonRefine(*it, aprec);
if (NEWTON_DIV_BY_ZERO) {
J.first = 1; J.second = 0; // indicating divide by zero
}
v1.push_back(std::make_pair(J.first, J.second));
}
v.swap(v1);
}//End of newtonRefineAllRoots
/////////////////////////////////////////////////////////////////
// DIAGNOSTIC TOOLS
/////////////////////////////////////////////////////////////////
// Polynomial tester: P is polynomial to be tested
// prec is the bit precision for root isolation
// n is the number of roots predicted
static void testSturm(PolyNT &P, int prec, int n = -1)
{
Sturm<NT> SS (P);
BFVecInterval v;
SS.refineAllRoots(v, prec);
cout << " Number of roots is " << v.size();
if ((n >= 0) & (v.size() == (unsigned)n))
cout << " (CORRECT!)" << endl;
else
cout << " (ERROR!) " << endl;
int i = 0;
for (BFVecInterval::const_iterator it = v.begin();
it != v.end(); ++it) {
cout << ++i << "th Root is in ["
<< it->first << " ; " << it->second << "]" << endl;
}
}// testSturm
// testNewtonSturm( Poly, aprec, n)
// will run the Newton-Sturm refinement to isolate the roots of Poly
// until absolute precision aprec.
// n is the predicated number of roots
// (will print an error message if n is wrong)
static void testNewtonSturm(PolyNT &P, int prec, int n = -1)
{
Sturm<NT> SS (P);
BFVecInterval v;
SS.newtonRefineAllRoots(v, prec);
cout << " Number of roots is " << v.size();
if ((n >= 0) & (v.size() == (unsigned)n))
cout << " (CORRECT!)" << endl;
else
cout << " (ERROR!) " << endl;
int i = 0;
for (BFVecInterval::const_iterator it = v.begin();
it != v.end(); ++it) {
cout << ++i << "th Root is in ["
<< it->first << " ; " << it->second << "]" << endl;
}
}// testNewtonSturm
// Newton(prec, bf):
// iterate until epsilon step size (i.e., epsilon < 2^{-prec})
// starting from initial bf
//
BigFloat newton(long prec, const BigFloat& bf) const {
// usually, prec is positive
int count = 0;
BigFloat val = bf;
extLong uMSB;
BigFloat del;
// newton iteration
do {
del = seq[0].eval(val)/seq[1].eval(val);
del.makeExact();
uMSB = del.uMSB();
val -= del;
val.makeExact();
count ++;
} while ((uMSB >= -prec) && (count < N_STOP_ITER)) ;
// N_STOP_ITER to prevent infinite loops
if (count == N_STOP_ITER) {
core_error("Newton Iteration reach limit",
__FILE__, __LINE__, true);
}
return val;
}
// v = newtonIterN(n, bf, del)
// v is the root after n iterations of Newton
// starting from initial value of bf
// Return by reference, "del" (difference between returned val and value
// in the previous Newton iteration)
BigFloat newtonIterN(long n, const BigFloat& bf, BigFloat& del) {
int count = 0;
BigFloat val = bf;
extLong uMSB = val.uMSB();
// newton iteration
for (int i=0; i<n; i++) {
BigFloat ff = seq[1].eval(val).makeExact();
if (ff == 0) {
NEWTON_DIV_BY_ZERO = true;
del = 0;
core_error("Zero divisor in Newton Iteration", __FILE__, __LINE__, false);
return 0;
}
BigFloat f = seq[0].eval(val).makeExact();
if (f == 0) {
NEWTON_DIV_BY_ZERO = false;
del = 0; // indicates that we have reached the exact root
return val; // val is the exact root, before the last iteration
}
del = (f/ff).makeExact();
uMSB = del.uMSB();
val -= del;
val.makeExact();
count ++;
}
return val;
}
// v = newtonIterE(prec, bf, del)
//
// return the value v which is obtained by Newton iteration until del.uMSB < -prec
// starting from initial value of bf
// Return by reference "del" (difference between returned val and value
// in the previous Newton iteration)
BigFloat newtonIterE(int prec, const BigFloat& bf, BigFloat& del) const{
// usually, prec is positive
int count = 0;
BigFloat val = bf;
do {
val = newtonIterN(1, val, del);
count++;
} while ((del != 0) && ((del.uMSB() >= -prec) && (count < N_STOP_ITER))) ;
// N_STOP_ITER to prevent infinite loops
return val;
}
// Smale's bound
// smalesBound(p, q) where q is the derivative of p
// returns Smale's original bound for convergence in range space
BigFloat smaleBound(const Polynomial<NT> & p, const Polynomial<NT> & q) const{
int deg = p.getTrueDegree();
BigFloat rho = 1/(1 + pow(q.length(), deg)
*pow(p.length() + 1, deg-1) );
return rho/13;
}
// Yap's bound
// yapsBound(p) returns the bound on size of isolating interval
// which will guarantee being in Newton zone
BigFloat yapsBound(const Polynomial<NT> & p) const{
int deg = p.getTrueDegree();
return 1/(1 + pow(BigFloat(deg), 3*deg+9)
*pow(BigFloat(2+p.height()),6*deg));
}
// newtonRefine(I, a) assumes I is an isolating interval for a root x^*,
// and will return an approximate root x which is guaranteed
// to be in the Newton basin of the root, and such that
// |x-x^*| < 2^{-a}.
BFInterval newtonRefine(const BFInterval I, int aprec) {
BFInterval J = I;
int leftSign = seq[0].eval(I.first).sign();
if (leftSign == 0) { J.first = J.second = I.first; return J;}
int rightSign = seq[0].eval(I.second).sign();
if (rightSign == 0) { J.first = J.second = I.second; return J;}
assert( leftSign * rightSign < 0 );
int steps = 1; //The number of times Newton is called without checking
// whether the iterations are still in the interval
BigFloat x;
BigFloat smale = smaleBound(seq[0], seq[1]);
BigFloat yap = yapsBound(seq[0]);
int N = steps;
BigFloat oldval = 0.0, temp;
BigFloat del;
x = (J.second + J.first).div2();
// x.makeExact();
while ( //seq[0].eval(x).abs() > smale && (J.second - J.first) > yap &&
del.uMSB() >= - aprec ){
oldval = x;
x = newtonIterN(N, x, del);
if (del == 0) { // either reached exact root or Divide-by-zero. It is
// the user's responsibility to check which is the case.
J.first = x; J.second = x;
return J;
}
del = core_abs(del);
if (
(J.first <= x && x <= J.second)
&&
((J.first < x - del) || (J.second > x+ del)))
{ // we can get a better root:
if (x-del > J.first) {
int lSign = seq[0].eval(x - del).sign();
if (lSign == 0) {
J.first = J.second = x-del; return J;
}
if (lSign == leftSign)
J.first = x - del;
else {
J.second = x - del;
x = (J.second + J.first).div2();
}
}
if (x+del < J.second) {
int rSign = seq[0].eval(x + del).sign();
if (rSign == 0) {
J.first = J.second = x+del; return J;
}
if (rSign == rightSign)
J.second = x+del;
else {
J.first = x + del;
x = (J.second + J.first).div2();
}
}
N ++; // be more confident or aggressive
} else {
N --;
if (N == 0) { // do bisection in this case
x = (J.second + J.first).div2(); // x.makeExact();
if(seq[0].eval(J.first) * seq[0].eval(x) < 0){
J.second = x;
}else if(seq[0].eval(J.second) * seq[0].eval(x) < 0){
J.first = x;
}else{
J.first = x;
J.second = x;
return J;
}
x = (J.second + J.first).div2(); // x.makeExact();
//Reset x to be the midpoint of the interval
N = steps; // restart
} else {
// Reset x to the oldval before doing Newton in this iteration
x = oldval;
}
}
}//end of outer while
/* For future work. We should exploit the fact that we have reached
the bounds. Could possible speed up things for high precision
if(del.uMSB() >= -aprec){// If any of the bounds are satisfied
//before reaching the required precision
x = newtonIterE(aprec, x, del);
J.first = x;
J.second = J.first;
}
*/
return(J);
}//End of newton refine
};// Sturm class
// ==================================================
// Static initialization
// ==================================================
template <class NT>
int Sturm<NT>:: N_STOP_ITER = 10000; // stop IterE after this many loops
// Reset this as needed
CORE_END_NAMESPACE