GiNaCRA
0.6.4
|
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