Before, 'refine_Pow' would simplify (-1)**exp only if 'exp' as a whole could
be determined as even or odd.  A doctest like,

        >>> refine_Pow((-1)**(x+y), Assume(x, Q.even))
        (-1)**y

would fail.  This patch fixes that by treating powers of S.NegativeOne as a
special case.  Every term in the exponent is checked, and even terms are
simply discarded.  Odd terms are collected to an even quantity and then
discarded, possibly introducing a new term +1 in the exponent.  Finally,
a number N is replaced with N % 2.

We are now able to refine:

        >>> refine_Pow((-1)**(x+y+z), Assume(x, Q.odd) & Assume(z, Q.odd))
        (-1)**y
        >>> refine_Pow((-1)**(x+y+2), Assume(x, Q.odd))
        (-1)**(1 + y)
        >>> refine((-1)**(x+3))
        (-1)**(x+1)

These examples are implemented as doctests and regular tests.
---
 sympy/assumptions/refine.py            |   50 ++++++++++++++++++++++++++++++-
 sympy/assumptions/tests/test_refine.py |   10 ++++++-
 2 files changed, 57 insertions(+), 3 deletions(-)

diff --git a/sympy/assumptions/refine.py b/sympy/assumptions/refine.py
index a3ffd18..d81d817 100644
--- a/sympy/assumptions/refine.py
+++ b/sympy/assumptions/refine.py
@@ -1,4 +1,4 @@
-from sympy.core import S, Symbol, sympify
+from sympy.core import S, Symbol, sympify, Add
 from sympy.utilities.source import get_class
 from sympy.assumptions import Q, ask
 from sympy.logic.boolalg import fuzzy_not
@@ -63,13 +63,25 @@ def refine_Pow(expr, assumptions):
 
     >>> from sympy import Symbol, Assume, Q
     >>> from sympy.assumptions.refine import refine_Pow
-    >>> from sympy.abc import x
+    >>> from sympy.abc import x,y,z
     >>> refine_Pow((-1)**x, Assume(x, Q.real))
     >>> refine_Pow((-1)**x, Assume(x, Q.even))
     1
     >>> refine_Pow((-1)**x, Assume(x, Q.odd))
     -1
 
+    For powers of -1, even parts of the exponent can be simplified:
+
+    >>> refine_Pow((-1)**(x+y), Assume(x, Q.even))
+    (-1)**y
+    >>> refine_Pow((-1)**(x+y+z), Assume(x, Q.odd) & Assume(z, Q.odd))
+    (-1)**y
+    >>> refine_Pow((-1)**(x+y+2), Assume(x, Q.odd))
+    (-1)**(1 + y)
+    >>> refine_Pow((-1)**(x+3), True)
+    (-1)**(1 + x)
+
+
     """
     from sympy.core import Pow, Rational
     from sympy.functions import sign
@@ -83,6 +95,40 @@ def refine_Pow(expr, assumptions):
             if type(expr.base) is Pow:
                 return abs(expr.base.base) ** (expr.base.exp * expr.exp)
 
+        if expr.base is S.NegativeOne:
+            if expr.exp.is_Add:
+
+                # For powers of (-1) we can remove
+                #  - even terms
+                #  - pairs of odd terms
+                #  - a single odd term + 1
+                #  - A numerical constant N can be replaced with mod(N,2)
+
+                coeff, terms = expr.exp.as_coeff_factors()
+                terms = set(terms)
+                even_terms = set([])
+                odd_terms = set([])
+                initial_number_of_terms = len(terms)
+
+                for t in terms:
+                    if ask(t, Q.even, assumptions):
+                        even_terms.add(t)
+                    elif ask(t, Q.odd, assumptions):
+                        odd_terms.add(t)
+
+                terms -= even_terms
+                if len(odd_terms)%2:
+                    terms -= odd_terms
+                    new_coeff = (coeff + S.One) % 2
+                else:
+                    terms -= odd_terms
+                    new_coeff = coeff % 2
+
+                if new_coeff != coeff or len(terms) < initial_number_of_terms:
+                    terms.add(new_coeff)
+                    return expr.base**(Add(*terms))
+
+
 def refine_exp(expr, assumptions):
     """
     Handler for exponential function.
diff --git a/sympy/assumptions/tests/test_refine.py 
b/sympy/assumptions/tests/test_refine.py
index 58b24b8..59a6dc0 100644
--- a/sympy/assumptions/tests/test_refine.py
+++ b/sympy/assumptions/tests/test_refine.py
@@ -12,7 +12,7 @@ def test_abs():
     assert refine(abs(x**2), Assume(x, Q.real)) == x**2
 
 def test_pow():
-    x, y = symbols('x y')
+    x, y, z = symbols('x y z')
     assert refine((-1)**x, Assume(x, Q.even)) == 1
     assert refine((-1)**x, Assume(x, Q.odd)) == -1
     assert refine((-2)**x, Assume(x, Q.even)) == 2**x
@@ -30,6 +30,14 @@ def test_pow():
     assert refine(sqrt(1/x), Assume(x, Q.real)) != 1/sqrt(x)
     assert refine(sqrt(1/x), Assume(x, Q.positive)) == 1/sqrt(x)
 
+    # powers of (-1)
+    assert refine((-1)**(x+y), Assume(x, Q.even)) == (-1)**y
+    assert refine((-1)**(x+y+z), Assume(x, Q.odd)&Assume(z, Q.odd))==(-1)**y
+    assert refine((-1)**(x+y+1), Assume(x, Q.odd))==(-1)**y
+    assert refine((-1)**(x+y+2), Assume(x, Q.odd))==(-1)**(y+1)
+    assert refine((-1)**(x+3)) == (-1)**(x+1)
+
+
 def test_exp():
     x = symbols('x')
     assert refine(exp(pi*I*2*x), Assume(x, Q.integer)) == 1
-- 
1.6.5

-- 
You received this message because you are subscribed to the Google Groups 
"sympy-patches" group.
To post to this group, send email to sympy-patc...@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.

Reply via email to