From: Fabian Seoane <fab...@fseoane.net> implemented factoring on negative roots, now (-8)**Rational(1,3) returns 2*(-1)**Rational(1,3). This is important, for example, in solve, so that it doesen't return uneecesarry long roots
removed unnecesary nesting, renamed variables b -> base, e -> exp --- sympy/core/numbers.py | 189 ++++++++++++++++++++------------------ sympy/core/power.py | 7 +- sympy/core/tests/test_eval.py | 6 + sympy/core/tests/test_numbers.py | 4 - 4 files changed, 109 insertions(+), 97 deletions(-) diff --git a/sympy/core/numbers.py b/sympy/core/numbers.py index 865bce9..73daa51 100644 --- a/sympy/core/numbers.py +++ b/sympy/core/numbers.py @@ -894,100 +894,107 @@ class Integer(Rational): def _eval_is_odd(self): return bool(self.p % 2) - def _eval_power(b, e): - if isinstance(e, Number): - if e is S.NaN: return S.NaN - if isinstance(e, Real): - return b._eval_evalf(e._prec) ** e - if e.is_negative: - # (3/4)**-2 -> (4/3)**2 - ne = -e - if ne is S.One: - return Rational(1, b.p) - return Rational(1, b.p) ** ne - if e is S.Infinity: - if b.p > 1: - # (3)**oo -> oo - return S.Infinity - if b.p < -1: - # (-3)**oo -> oo + I*oo - return S.Infinity + S.Infinity * S.ImaginaryUnit - return S.Zero - if isinstance(e, Integer): - # (4/3)**2 -> 4**2 / 3**2 - return Integer(b.p ** e.p) - if isinstance(e, Rational): - if b == -1: # any one has tested this ??? - # calculate the roots of -1 - if e.q.is_odd: - return -1 - r = cos(pi/e.q) + S.ImaginaryUnit*sin(pi/e.q) - return r**e.p - if b >= 0: - x, xexact = integer_nthroot(b.p, e.q) - if xexact: - res = Integer(x ** abs(e.p)) - if e >= 0: - return res - else: - return 1/res - else: - if b > 2**32: #Prevent from factorizing too big integers: - for i in xrange(2, e.q//2 + 1): #OLD CODE - if e.q % i == 0: - x, xexact = integer_nthroot(b.p, i) - if xexact: - return Integer(x)**(e * i) - # Try to get some part of the base out, if exponent > 1 - if e.p > e.q: - i = e.p // e.q - r = e.p % e.q - return b**i * b**Rational(r, e.q) - return - - - dict = b.factors() - - out_int = 1 - sqr_int = 1 - sqr_gcd = 0 - - sqr_dict = {} - - for prime,exponent in dict.iteritems(): - exponent *= e.p - div_e = exponent // e.q - div_m = exponent % e.q - - if div_e > 0: - out_int *= prime**div_e - if div_m > 0: - sqr_dict[prime] = div_m - - for p,ex in sqr_dict.iteritems(): - if sqr_gcd == 0: - sqr_gcd = ex - else: - sqr_gcd = igcd(sqr_gcd, ex) - - for k,v in sqr_dict.iteritems(): - sqr_int *= k**(v // sqr_gcd) - - if sqr_int == b.p and out_int == 1: - return None - - return out_int * Pow(sqr_int , Rational(sqr_gcd, e.q)) + def _eval_power(base, exp): + """ + Tries to do some simplifications on base ** exp, where base is + an instance of Rational + + Returns None if no further simplifications can be done + + When exponent is a fraction (so we have for example a square root), + we try to find the simplest possible representation, so that + - 4**Rational(1,2) becomes 2 + - (-4)**Rational(1,2) becomes 2*I + """ + + MAX_INT_FACTOR = 4294967296 # Prevent from factorizing too big integers + if not isinstance(exp, Number): + c,t = base.as_coeff_terms() + if exp.is_even and isinstance(c, Number) and c < 0: + return (-c * Mul(*t)) ** exp + if exp is S.NaN: return S.NaN + if isinstance(exp, Real): + return base._eval_evalf(exp._prec) ** exp + if exp.is_negative: + # (3/4)**-2 -> (4/3)**2 + ne = -exp + if ne is S.One: + return Rational(1, base.p) + return Rational(1, base.p) ** ne + if exp is S.Infinity: + if base.p > 1: + # (3)**oo -> oo + return S.Infinity + if base.p < -1: + # (-3)**oo -> oo + I*oo + return S.Infinity + S.Infinity * S.ImaginaryUnit + return S.Zero + if isinstance(exp, Integer): + # (4/3)**2 -> 4**2 / 3**2 + return Integer(base.p ** exp.p) + if exp == S.Half and base.is_negative: + # sqrt(-2) -> I*sqrt(2) + return S.ImaginaryUnit * ((-base)**exp) + if isinstance(exp, Rational): + # 4**Rational(1,2) -> 2 + result = None + x, xexact = integer_nthroot(abs(base.p), exp.q) + if xexact: + res = Integer(x ** abs(exp.p)) + if exp >= 0: + result = res else: - if e.q == 2: - return S.ImaginaryUnit ** e.p * (-b)**e + result = 1/res + else: + if base > MAX_INT_FACTOR: + for i in xrange(2, exp.q//2 + 1): #OLD CODE + if exp.q % i == 0: + x, xexact = integer_nthroot(abs(base.p), i) + if xexact: + result = Integer(x)**(e * i) + break + # Try to get some part of the base out, if exponent > 1 + if exp.p > exp.q: + i = exp.p // exp.q + r = exp.p % exp.q + result = base**i * b**Rational(r, exp.q) + return + + dict = base.factors() + out_int = 1 + sqr_int = 1 + sqr_gcd = 0 + sqr_dict = {} + for prime,exponent in dict.iteritems(): + exponent *= exp.p + div_e = exponent // exp.q + div_m = exponent % exp.q + if div_e > 0: + out_int *= prime**div_e + if div_m > 0: + sqr_dict[prime] = div_m + for p,ex in sqr_dict.iteritems(): + if sqr_gcd == 0: + sqr_gcd = ex else: - return None - - c,t = b.as_coeff_terms() - if e.is_even and isinstance(c, Number) and c < 0: - return (-c * Mul(*t)) ** e + sqr_gcd = igcd(sqr_gcd, ex) + for k,v in sqr_dict.iteritems(): + sqr_int *= k**(v // sqr_gcd) + if sqr_int == base.p and out_int == 1: + result = None + else: + result = out_int * Pow(sqr_int , Rational(sqr_gcd, exp.q)) - return + if result is not None: + if base.is_positive: + return result + elif exp.q == 2: + return result * S.ImaginaryUnit + else: + return result * ((-1)**exp) + else: + if exp == S.Half and base.is_negative: + return S.ImaginaryUnit * Pow(-base, exp) def _eval_is_prime(self): if self.p < 0: diff --git a/sympy/core/power.py b/sympy/core/power.py index 893f614..aefdd4c 100644 --- a/sympy/core/power.py +++ b/sympy/core/power.py @@ -141,9 +141,12 @@ class Pow(Basic): def _eval_is_integer(self): c1 = self.base.is_integer - if c1 is None: return c2 = self.exp.is_integer - if c2 is None: return + if c1 is None or c2 is None: + return None + if not c1: + if self.exp.is_nonnegative: + return False if c1 and c2: if self.exp.is_nonnegative or self.exp.is_positive: return True diff --git a/sympy/core/tests/test_eval.py b/sympy/core/tests/test_eval.py index 15e5fc4..1e9eb67 100644 --- a/sympy/core/tests/test_eval.py +++ b/sympy/core/tests/test_eval.py @@ -34,12 +34,18 @@ def test_pow_eval(): assert sqrt(-4) == 2*I assert sqrt( 4) == 2 assert (8)**Rational(1,3) == 2 + assert (-8)**Rational(1,3) == 2*((-1)**Rational(1,3)) assert sqrt(-2) == I*sqrt(2) assert (-1)**Rational(1,3) != I assert (-10)**Rational(1,3) != I*((10)**Rational(1,3)) assert (-2)**Rational(1,4) != (2)**Rational(1,4) + assert 64**(Rational(1)/3) == 4 + assert 64**(Rational(2)/3) == 16 + assert 24*64**(-Rational(1)/2) == 3 + assert (-27)**Rational(1,3) == 3*(-1)**Rational(1,3) + assert (cos(2) / tan(2))**2 == (cos(2) / tan(2))**2 @XFAIL diff --git a/sympy/core/tests/test_numbers.py b/sympy/core/tests/test_numbers.py index 44f4bf5..894101c 100644 --- a/sympy/core/tests/test_numbers.py +++ b/sympy/core/tests/test_numbers.py @@ -172,10 +172,6 @@ def test_powers(): assert integer_nthroot(c2+1, 2) == (c, False) assert integer_nthroot(c2-1, 2) == (c-1, False) - assert 64**(Rational(1)/3)==4 - assert 64**(Rational(2)/3)==16 - assert 24*64**(-Rational(1)/2)==3 - assert Rational(5**3, 8**3)**Rational(4,3) == Rational(5**4, 8**4) assert Rational(-4,7)**Rational(1,2) == I*Rational(4,7)**Rational(1,2) -- 1.6.0.2 --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "sympy-patches" group. To post to this group, send email to sympy-patches@googlegroups.com To unsubscribe from this group, send email to sympy-patches+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sympy-patches?hl=en -~----------~----~----~----~------~----~------~--~---