GiNaCRA  0.6.4
RealAlgebraicNumberIR.cpp
Go to the documentation of this file.
00001 /*
00002  * GiNaCRA - GiNaC Real Algebra package
00003  * Copyright (C) 2010-2012  Ulrich Loup, Joachim Redies, Sebastian Junges
00004  *
00005  * This file is part of GiNaCRA.
00006  *
00007  * GiNaCRA is free software: you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License as published by
00009  * the Free Software Foundation, either version 3 of the License, or
00010  * (at your option) any later version.
00011  *
00012  * GiNaCRA is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with GiNaCRA.  If not, see <http://www.gnu.org/licenses/>.
00019  *
00020  */
00021 
00022 
00023 // #define GINACRA_INTERVALREPRESENTATION_DEBUG
00024 
00034 #include <assert.h>
00035 
00036 #include "RealAlgebraicNumberIR.h"
00037 
00038 using GiNaC::ZERO_SIGN;
00039 using GiNaC::POSITIVE_SIGN;
00040 using GiNaC::NEGATIVE_SIGN;
00041 using GiNaC::gcd;
00042 using std::cout;
00043 using std::endl;
00044 
00045 namespace GiNaCRA
00046 {
00047     // Call GiNaC macro (registrar.h) for completing the implementation into the basic type.
00048 
00049     GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(RealAlgebraicNumberIR, basic, print_func<print_context>( &RealAlgebraicNumberIR::do_print ))
00050 
00051     
00052     // Con- and destructors //
00054 
00055     RealAlgebraicNumberIR::RealAlgebraicNumberIR():
00056         RealAlgebraicNumber( true, true, 0 ),
00057         mPolynomial(),
00058         mInterval(),
00059         mSturmSequence( RationalUnivariatePolynomial::standardSturmSequence( mPolynomial, mPolynomial.diff() )),
00060         mRefinementCount( 0 )
00061     {
00062         setflag( GiNaC::status_flags::expanded );
00063     }
00064 
00065     RealAlgebraicNumberIR::RealAlgebraicNumberIR( const symbol& s ) throw ( invalid_argument ):
00066         RealAlgebraicNumber( true, true, 0 ),
00067         mPolynomial( s, s ),
00068         mInterval( 0, 0 ),
00069         mSturmSequence( RationalUnivariatePolynomial::standardSturmSequence( mPolynomial, mPolynomial.diff() )),
00070         mRefinementCount( 0 )
00071     {
00072         setflag( status_flags::expanded );
00073     }
00074 
00075     RealAlgebraicNumberIR::RealAlgebraicNumberIR( const RationalUnivariatePolynomial& p,
00076                                                   const OpenInterval& i,
00077                                                   const list<RationalUnivariatePolynomial>& seq,
00078                                                   const bool normalize,
00079                                                   const bool isRoot )
00080             throw ( invalid_argument ):
00081         RealAlgebraicNumber( isRoot,
00082                              false,
00083                              0 ),
00084 #ifdef GINACRA_INTERVALREPRESENTATION_OPT_NORMALIZE_POLYNOMIAL
00085         mPolynomial( p.sepapart() ),
00086 #else
00087         mPolynomial( p ),
00088 #endif
00089         mInterval( i ),
00090         mSturmSequence( seq.empty() ? RationalUnivariatePolynomial::standardSturmSequence( p, p.diff() ) : seq ),
00091         mRefinementCount( 0 )
00092     {
00093         if( mPolynomial.isConstant() )
00094             throw invalid_argument( "A real algebraic number must not been initialized with a constant polynomial." );
00095         if( normalize )
00096             normalizeInterval();
00097         if( mInterval.contains( 0 ))
00098             mIsNumeric = true;
00099         if( mPolynomial.degree() <= 1 )
00100         {
00101             mIsNumeric = true;
00102             numeric a  = mPolynomial.coeff( 1 );
00103             numeric b  = mPolynomial.coeff( 0 );
00104             mValue     = a == 0 ? b : -b / a;
00105             mInterval.setLeft( OpenInterval( mInterval.left(), mValue ).sampleFast() );
00106             mInterval.setRight( OpenInterval( mValue, mInterval.right() ).sampleFast() );
00107         }
00108         setflag( GiNaC::status_flags::expanded );
00109     }
00110 
00111     RealAlgebraicNumberIR::~RealAlgebraicNumberIR(){}
00112 
00113     RealAlgebraicNumberPtr RealAlgebraicNumberIR::clone() const
00114     {
00115         return RealAlgebraicNumberIRPtr( new RealAlgebraicNumberIR( *this ));
00116     }
00117 
00119     // Methods from basic  //
00121 
00122     int RealAlgebraicNumberIR::compare_same_type( const basic& other ) const
00123     {
00124         RealAlgebraicNumberIR o = static_cast<const RealAlgebraicNumberIR&>(other);
00125         // may not use isEqual since this method only works non-mutably, take heuristics
00126         if( mInterval.right() <= o.mInterval.left() )
00127             return -1;
00128         if( o.mInterval.right() <= mInterval.left() )
00129             return 1;
00130         return 0;
00131     }
00132 
00133     bool RealAlgebraicNumberIR::is_equal_same_type( const basic& other ) const
00134     {
00135         // for this method one may not use isEqual since this method only works non-mutably, take heuristics (if heuristics result in false, the result is correct, otherwise not necessarily
00136         RealAlgebraicNumberIR o = static_cast<const RealAlgebraicNumberIR&>(other);
00137         if( (mInterval.isZero() && o.interval().isZero()) || (mIsNumeric && o.mIsNumeric && mValue == o.mValue) )    // fast exact case
00138             return true;
00139         if( mInterval.right() <= o.mInterval.left() || o.mInterval.right() <= mInterval.left() )    // exact case without refinement
00140             return false;
00141         // now that intervals have nonzero intersection, check for possible common root
00142         //    ex ca = ex();
00143         //    ex cb = ex();
00144         //    if( gcd( o.polynomial( ), mPolynomial ) != 1 ) // polynomials have common factor (might still be a different root)
00145         //        return true;
00146         return true;
00147     }
00148 
00149     void RealAlgebraicNumberIR::do_print( const print_context& c, unsigned level ) const
00150     {
00151         // print_context::s is a reference to an ostream
00152         c.s << '{' << static_cast<UnivariatePolynomial>(mPolynomial) << ": " << mInterval << '}' << (mIsRoot ? "~" : "");
00153         if( mIsNumeric )
00154             c.s << " (" << mValue << ")";
00155     }
00156 
00157     ex RealAlgebraicNumberIR::evalf( int level ) const
00158     {
00159         if( level )
00160         {
00161             RealAlgebraicNumberIR copy( *this );
00162             for( int i = 0; i < level; ++i )
00163                 copy.refine();
00164             return copy.approximateValue();
00165         }
00166         return mInterval.midpoint();
00167     }
00168 
00169     unsigned RealAlgebraicNumberIR::calchash() const
00170     {
00171         return mPolynomial.gethash();
00172     }
00173 
00174     bool RealAlgebraicNumberIR::info( unsigned inf ) const
00175     {
00176         switch( inf )
00177         {
00178             case GiNaC::info_flags::numeric:
00179             case GiNaC::info_flags::polynomial:
00180             case GiNaC::info_flags::rational_function:
00181             case GiNaC::info_flags::expanded:
00182             case GiNaC::info_flags::real:
00183             case GiNaC::info_flags::algebraic:
00184                 return true;
00185         }
00186         return false;
00187     }
00188 
00190     // Operators //
00192 
00193     // assignment operators
00194 
00195     const RealAlgebraicNumberIR& RealAlgebraicNumberIR::operator = ( const RealAlgebraicNumberIR& o )
00196     {
00197         mInterval        = o.mInterval;
00198         mPolynomial      = o.mPolynomial;
00199         mSturmSequence   = o.mSturmSequence;
00200         mRefinementCount = o.mRefinementCount;
00201         if( mInterval.contains( 0 ))
00202             mIsNumeric = true;
00203         else
00204             mIsNumeric = o.mIsNumeric;
00205         mIsRoot = o.mIsRoot;
00206         mValue  = o.mValue;
00207         return *this;
00208     }
00209 
00211     // Operations //
00213 
00214     void RealAlgebraicNumberIR::normalizeInterval() throw ( invalid_argument )
00215     {
00216         // shift the right border below zero or set the zero interval
00217         numeric a = (1 + mPolynomial.maximumNorm()).inverse();
00218         if( RationalUnivariatePolynomial::signVariations( mSturmSequence, mInterval.left() )
00219                 > RationalUnivariatePolynomial::signVariations( mSturmSequence, -a ))    // zero is in ]left, -a[
00220             mInterval.setRight( -a );
00221         else if( RationalUnivariatePolynomial::signVariations( mSturmSequence, a )
00222                  > RationalUnivariatePolynomial::signVariations( mSturmSequence, mInterval.right() ))    // zero is in ]a, right[
00223             mInterval.setLeft( a );
00224         else if( !mInterval.contains( 0 ) &&!mInterval.isZero() )
00225             throw invalid_argument( "The interval is not suitable for this real algebraic number." );
00226         else    // zero is in ]-a, a[ => zero is 0
00227         {
00228             mInterval.setLeft( 0 );
00229             mInterval.setRight( 0 );
00230         }
00231     }
00232 
00233     void RealAlgebraicNumberIR::refine( RealAlgebraicNumberSettings::RefinementStrategy strategy )
00234     {
00235         if( mIsNumeric )
00236         {    // refine the interval based on the numeric value determined earlier
00237             mInterval.setLeft( OpenInterval( mInterval.left(), mValue ).sampleFast() );
00238             mInterval.setRight( OpenInterval( mValue, mInterval.right() ).sampleFast() );
00239             return;
00240         }
00241         numeric m = mInterval.midpoint();
00242         bool foundRootAlready = false;
00243         switch( strategy )
00244         {
00245             case RealAlgebraicNumberSettings::GENERIC_REFINEMENTSTRATEGY:
00246                 // m = mInterval.midpoint();
00247                 break;
00248             case RealAlgebraicNumberSettings::BINARYNEWTON_REFINEMENTSTRATEGY:
00249                 m = mPolynomial.approximateRealRoot( m, mInterval, 1 );    // try Newton's solution as root (proceed with next case)
00250             case RealAlgebraicNumberSettings::BINNARYMIDPOINTSAMPLE_REFINEMENTSTRATEGY:
00251                 if( mPolynomial.sgn( m ) == ZERO_SIGN )
00252                 {
00253                     foundRootAlready = true;
00254                     break;
00255                 }    // else: move on to BINARYSAMPLE_REFINEMENTSTRATEGY
00256             case RealAlgebraicNumberSettings::BINARYSAMPLE_REFINEMENTSTRATEGY:
00257                 if( mRefinementCount < RealAlgebraicNumberSettings::MAXREFINE_REFINEMENTSTRATEGY )    // only take MAXREFINE_REFINEMENTSTRATEGY
00258                     m = mInterval.sample();
00259                 break;
00260         }
00261         if( !foundRootAlready && mPolynomial.sgn( m ) != ZERO_SIGN )
00262         {    // split the interval
00263             if( RationalUnivariatePolynomial::signVariations( mSturmSequence, mInterval.left() )
00264                     > RationalUnivariatePolynomial::signVariations( mSturmSequence, m ))
00265                 mInterval.setRight( m );
00266             else
00267                 mInterval.setLeft( m );
00268         }
00269         else
00270         {    // split the interval including m and store m under mValue
00271             mInterval.setLeft( (mInterval.left() + m) / numeric( 2 ));    // in order to be compatible with algorithms purely working with refine
00272             mInterval.setRight( (mInterval.right() + m) / numeric( 2 ));    // in order to be compatible with algorithms purely working with refine
00273             mValue     = m;
00274             mIsNumeric = true;
00275         }
00276         ++mRefinementCount;
00277         assert( mInterval.left() < mInterval.right() );
00278     }
00279 
00280     bool RealAlgebraicNumberIR::refineAvoiding( numeric n )
00281     {
00282         //        cout << "Call: refine " << mPolynomial << "  " << mInterval << " avoiding " << n << " (" << *this << ")" << endl;
00283         if( mIsNumeric )    // refine the interval based on the numeric value determined earlier
00284         {
00285             if( !mInterval.meets( n ))
00286                 return false;
00287             if( mValue < n )
00288                 mInterval.setRight( OpenInterval( mValue, n ).sampleFast() );
00289             else if( mValue > n )
00290                 mInterval.setLeft( OpenInterval( n, mValue ).sampleFast() );
00291             else
00292                 return true;
00293             return false;
00294         }
00295         if( mInterval.contains( n ))
00296         {
00297             if( mPolynomial.sgn( n ) == ZERO_SIGN )
00298             {
00299                 mValue     = n;
00300                 mIsNumeric = true;
00301                 return true;
00302             }
00303             // n is no root and partitions the interval, choose the half which carries the root
00304             if( RationalUnivariatePolynomial::signVariations( mSturmSequence, mInterval.left() )
00305                     > RationalUnivariatePolynomial::signVariations( mSturmSequence, n ))    // ] left(), n [ has real roots
00306                 mInterval.setRight( n );
00307             else
00308                 mInterval.setLeft( n );
00309             ++mRefinementCount;
00310         }
00311         else if( mInterval.left() != n && mInterval.right() != n )    // <=> !mInterval.meets(n)
00312             return false;
00313         // here, n is one of the interval bounds and the interval contains a real root => avoid n by refinement
00314         bool isLeft = mInterval.left() == n;    // which bound needs to be refined?
00315         // initial guess for the new bound
00316         numeric newBound = mInterval.sampleFast();
00317         if( mPolynomial.sgn( newBound ) == ZERO_SIGN )
00318         {
00319             mValue     = newBound;
00320             mIsNumeric = true;
00321             if( isLeft )
00322                 mInterval.setLeft( OpenInterval( n, newBound ).sampleFast() );
00323             else
00324                 mInterval.setRight( OpenInterval( newBound, n ).sampleFast() );
00325             return false;
00326         }
00327         if( isLeft )
00328             mInterval.setLeft( newBound );
00329         else
00330             mInterval.setRight( newBound );
00331         while( RationalUnivariatePolynomial::countRealRoots( mSturmSequence, mInterval ) == 0 )
00332         {
00333             //            cout << "Loop: refine " << mInterval << " avoiding " << n << endl;
00334             // refine the bound meeting n
00335             if( isLeft )
00336             {
00337                 numeric oldBound = mInterval.left();
00338                 numeric newBound = OpenInterval( n, oldBound ).sampleFast();
00339                 if( mPolynomial.sgn( newBound ) == ZERO_SIGN )
00340                 {
00341                     mValue     = newBound;
00342                     mIsNumeric = true;
00343                     mInterval.setLeft( OpenInterval( n, newBound ).sampleFast() );
00344                     return false;
00345                 }
00346                 mInterval.setRight( oldBound );
00347                 mInterval.setLeft( newBound );
00348             }
00349             else
00350             {
00351                 numeric oldBound = mInterval.right();
00352                 numeric newBound = OpenInterval( oldBound, n ).sampleFast();
00353                 if( mPolynomial.sgn( newBound ) == ZERO_SIGN )
00354                 {
00355                     mValue     = newBound;
00356                     mIsNumeric = true;
00357                     mInterval.setRight( OpenInterval( newBound, n ).sampleFast() );
00358                     return false;
00359                 }
00360                 mInterval.setLeft( oldBound );
00361                 mInterval.setRight( newBound );
00362             }
00363         }
00364         return false;
00365     }
00366 
00367     GiNaC::sign RealAlgebraicNumberIR::sgn() const
00368     {
00369         if( mInterval.isZero() )
00370             return ZERO_SIGN;
00371         if( mInterval.left() < 0 )
00372             return NEGATIVE_SIGN;
00373         return POSITIVE_SIGN;
00374     }
00375 
00376     GiNaC::sign RealAlgebraicNumberIR::sgn( const RationalUnivariatePolynomial& p ) const
00377     {
00378         list<RationalUnivariatePolynomial> seq = RationalUnivariatePolynomial::standardSturmSequence(
00379                                                      mPolynomial,
00380                                                      mPolynomial.isCompatible( p )
00381                                                      ? (RationalUnivariatePolynomial)mPolynomial.diff() * p
00382                                                      : RationalUnivariatePolynomial(
00383                                                          mPolynomial.diff()
00384                                                          * p.subs(
00385                                                              GiNaC::lst( p.variable() ),
00386                                                              GiNaC::lst( mPolynomial.variable() )), mPolynomial.variable() ));
00387         switch( RationalUnivariatePolynomial::signVariations( seq, (mInterval).left() )
00388                 - RationalUnivariatePolynomial::signVariations( seq, (mInterval).right() ))
00389         {
00390             case 0:
00391                 return ZERO_SIGN;
00392             case 1:
00393                 return POSITIVE_SIGN;
00394         }
00395         //        case -1:
00396         return NEGATIVE_SIGN;
00397     }
00398 
00400     // Arithmetic Operations //
00402 
00403     RealAlgebraicNumberIR& RealAlgebraicNumberIR::add( RealAlgebraicNumberIR& o ) throw ( invalid_argument )
00404     {
00405         if( mInterval.isZero() || o.interval().isZero() )
00406             return o;
00407         const symbol x   = mPolynomial.variable();
00408         const ex     x_o = o.mPolynomial.variable();    // usually: x_o == x
00409         const symbol y   = symbol( "y" );
00410         ex res = UnivariatePolynomial( mPolynomial.subs( x == static_cast<ex>(x)-static_cast<ex>(y) ),
00411                                        y ).resultant( UnivariatePolynomial( o.mPolynomial.subs( x_o == y ), y ));
00412         RationalUnivariatePolynomial p = RationalUnivariatePolynomial( res, x ).primpart();
00413         list<RationalUnivariatePolynomial> seq = RationalUnivariatePolynomial::standardSturmSequence( p, p.diff() );
00414         OpenInterval i = mInterval + o.mInterval;    // interval of the new real algebraic number, possibly needs to be refined
00415         while( RationalUnivariatePolynomial::signVariations( seq, i.left() ) - RationalUnivariatePolynomial::signVariations( seq, i.right() ) > 1 )
00416         {    // refine as long as exactly one sign variation within the new interval
00417             refine();
00418             o.refine();
00419             i = mInterval + o.mInterval;    // refined interval of the new algebraic number
00420         }
00421         return *new RealAlgebraicNumberIR( p, i, seq );
00422     }
00423 
00424     RealAlgebraicNumberIR& RealAlgebraicNumberIR::minus() const
00425     {
00426         if( mInterval.isZero() )
00427             return *new RealAlgebraicNumberIR( *this );
00428         RationalUnivariatePolynomial p( mPolynomial.subs( mPolynomial.variable() == -static_cast<ex>(mPolynomial.variable())),
00429                                         mPolynomial.variable() );
00430         return *new RealAlgebraicNumberIR( p, -mInterval, list<RationalUnivariatePolynomial>(), false );    // prohibit normalization
00431     }
00432 
00433     RealAlgebraicNumberIR& RealAlgebraicNumberIR::mul( RealAlgebraicNumberIR& o ) throw ( invalid_argument )
00434     {
00435         if( mInterval.isZero() || o.interval().isZero() )
00436             return *zero( mPolynomial.variable() );
00437         const symbol x   = mPolynomial.variable();
00438         const ex     x_o = o.mPolynomial.variable();    // usually: x_o == x
00439         const symbol y   = symbol( "y" );
00440         RationalUnivariatePolynomial
00441         p = RationalUnivariatePolynomial( resultant( GiNaC::pow( y, mPolynomial.degree() )
00442                                                      * mPolynomial.subs( x == (static_cast<ex>(x) / static_cast<ex>(y)) ), o.mPolynomial.subs( x_o
00443                                                                          == y ), y ), x ).primpart();
00444         list<RationalUnivariatePolynomial> seq = RationalUnivariatePolynomial::standardSturmSequence( p, p.diff() );
00445         OpenInterval i = mInterval * o.mInterval;    // interval of the new real algebraic number, possibly needs to be refined
00446         while( RationalUnivariatePolynomial::signVariations( seq, i.left() ) - RationalUnivariatePolynomial::signVariations( seq, i.right() )
00447                 > numeric( 1 ))
00448         {    // refine as long as exactly one sign variation within the new interval
00449             refine();
00450             o.refine();
00451             i = mInterval * o.mInterval;    // refined interval of the new algebraic number
00452         }
00453         return *new RealAlgebraicNumberIR( p, i, seq );
00454     }
00455 
00456     RealAlgebraicNumberIR& RealAlgebraicNumberIR::inverse() const throw ( invalid_argument )
00457     {
00458         return *new RealAlgebraicNumberIR( RationalUnivariatePolynomial(
00459             (GiNaC::pow( mPolynomial.variable(), mPolynomial.degree() )
00460              * (mPolynomial.subs(
00461                  mPolynomial.variable() == (numeric( 1 ) / static_cast<ex>(mPolynomial.variable()))))).expand(), mPolynomial.variable() ).primpart(),
00462                                            OpenInterval( mInterval.right().inverse(), mInterval.left().inverse() ));
00463     }
00464 
00465     RealAlgebraicNumberIR& RealAlgebraicNumberIR::pow( int e ) throw ( invalid_argument )
00466     {
00467         // ugly workaround:
00468         RealAlgebraicNumberIR r = *this;
00469         for( int i = 1; i != e; ++i )
00470             r = r.mul( r );
00471         return *new RealAlgebraicNumberIR( r );
00472 
00473         //    RealAlgebraicNumberIR res;
00474         //    RealAlgebraicNumberIR curpot = *this;
00475         //
00476         //    while(e > 0)
00477         //    {
00478         //        if (e % 2 == 1)
00479         //        {
00480         //            if (res == NULL) {
00481         //                res = res * curpot;
00482         //            }
00483         //        }
00484         //        curpot = curpot * curpot;
00485         //        e /= 2;
00486         //    }
00487         //
00488         //    return *new RealAlgebraicNumberIR(res);
00489         //
00490     }
00491 
00493     // Relational Operations //
00495 
00496     const bool RealAlgebraicNumberIR::isEqual( RealAlgebraicNumberIR& o )
00497     {
00498         if( (mInterval.isZero() && o.interval().isZero()) || (mIsNumeric && o.mIsNumeric && mValue == o.mValue) )    // fast exact case
00499             return true;
00500         if( mInterval.right() <= o.mInterval.left() || o.mInterval.right() <= mInterval.left() )    // exact case without refinement
00501             return false;
00502         // otherwise: the two numbers are equal iff they subtract to zero, which is the number with the zero-interval
00503         RealAlgebraicNumberIR oMinus = o.minus();
00504         return this->add( oMinus ).interval().isZero();
00505     }
00506 
00507     const bool RealAlgebraicNumberIR::isLessWhileUnequal( RealAlgebraicNumberIR& o )
00508     {
00509         // the two intervals are refined until the interval bounds uniquely determine the ordering
00510         while( true )
00511         {
00516             if( mInterval.right() <= o.mInterval.left() )
00517                 return true;
00518 
00523             if( o.mInterval.right() <= mInterval.left() )
00524                 return false;
00525 
00527             this->refine();
00528             o.refine();
00529         }
00530     }
00531 
00532     const bool RealAlgebraicNumberIR::isLess( RealAlgebraicNumberIR& o )
00533     {
00534         if( this->isEqual( o ))
00535             return false;
00536         return this->isLessWhileUnequal( o );
00537     }
00538 
00540     // Static Methods //
00542 
00543     RealAlgebraicNumberIR* RealAlgebraicNumberIR::zero( const symbol& s )
00544     {
00545         return new RealAlgebraicNumberIR( s );
00546     }
00547 
00548 }    // namespace GiNaC
00549