Ondrej Certik wrote:
> On Wed, Aug 12, 2009 at 11:28 AM, Ondrej Certik<ond...@certik.cz> wrote:
>> On Wed, Aug 12, 2009 at 8:00 AM, Vinzent
>> Steinberg<vinzent.steinb...@googlemail.com> wrote:
>>> 2009/8/12 Fabian Pedregosa <fab...@fseoane.net>
>>>> Ondrej Certik wrote:
>>>>> This still fails in the latest branch:
>>>>>
>>>>> bin/test sympy/concrete/tests/test_sums_products.py -k
>>>>> test_composite_sums
>>>>>
>>>>> I just sent you a login info to my amd64 box offlist, please debug it.
>>>>
>>>> This can be fixed by:
>>>>
>>>> diff --git a/sympy/core/assumptions.py b/sympy/core/assumptions.py
>>>> index e8a9fd6..8220550 100644
>>>> --- a/sympy/core/assumptions.py
>>>> +++ b/sympy/core/assumptions.py
>>>> @@ -101,7 +101,6 @@ class AssumeMeths(object):
>>>>          'complex        ->  commutative',
>>>>
>>>>          'odd            ==  integer & !even',
>>>> -        'even           ==  integer & !odd',
>>>>
>>>>          'real           ==  negative | zero | positive',
>>>>
>>>> sadly this makes other tests to fail.
>>> Any idea why this is necessary?
>>>
>>>> I propose pushing patches 1-4 (after fixing Vinzent's comments), work on
>>>> performance and then start removing the old system.
>>> +1
>>> As long as we don't drop the old system, we don't depend on functionality
>>> and performance of the new one.
>> Please prepare patch series, that can go in, I will look at it.
> 
> Any progress? Scipy09 is coming, so every day counts.

yes, I attach the reworked patches. You can also pull from my master branch:

git pull http://fseoane.net/git/sympy.git master

or see them online:

http://fseoane.net/cgi-bin/gitweb.cgi?p=sympy.git;a=summary


Thanks,


> 
> Ondrej
> 
> > 


--~--~---------~--~----~------------~-------~--~----~
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
-~----------~----~----~----~------~----~------~--~---

>From 862845c0cd0214d6f6bd808483ce8abe7b129276 Mon Sep 17 00:00:00 2001
From: Fabian Pedregosa <fab...@fseoane.net>
Date: Mon, 10 Aug 2009 09:10:52 +0200
Subject: [PATCH 1/4] logic module improvements

Speed improvements and function fuzzy_not implemented
---
 sympy/logic/__init__.py |    2 +-
 sympy/logic/boolalg.py  |   32 +++++++++++++++++++++++++-------
 2 files changed, 26 insertions(+), 8 deletions(-)

diff --git a/sympy/logic/__init__.py b/sympy/logic/__init__.py
index c09f403..d47f04c 100644
--- a/sympy/logic/__init__.py
+++ b/sympy/logic/__init__.py
@@ -1,2 +1,2 @@
 from boolalg import to_cnf
-from inference import satisfiable
\ No newline at end of file
+from inference import satisfiable
diff --git a/sympy/logic/boolalg.py b/sympy/logic/boolalg.py
index 02e8106..95a3a4b 100755
--- a/sympy/logic/boolalg.py
+++ b/sympy/logic/boolalg.py
@@ -76,12 +76,12 @@ def eval(cls, *args):
             return map(cls, args)
         arg = args[0]
         # apply De Morgan Rules
-        if isinstance(arg, And):
+        if type(arg) is  And:
             return Or( *[Not(a) for a in arg.args])
-        if isinstance(arg, Or):
+        if type(arg) is Or:
             return And(*[Not(a) for a in arg.args])
-        if isinstance(arg, bool): return not arg
-        if isinstance(arg, Not):
+        if type(arg) is bool: return not arg
+        if type(arg) is Not:
             return arg.args[0]
 
 class Nand(BooleanFunction):
@@ -127,6 +127,23 @@ def eval(cls, *args):
 
 ### end class definitions. Some useful methods
 
+def fuzzy_not(arg):
+    """
+    Not in fuzzy logic
+
+    will return Not if arg is a boolean value, and None if argument
+    is None
+
+    >>> from sympy import *
+    >>> fuzzy_not(True)
+    False
+    >>> fuzzy_not(None)
+    >>> fuzzy_not(False)
+    True
+    """
+    if arg is None: return
+    return not arg
+
 def conjuncts(expr):
     """Return a list of the conjuncts in the expr s.
     >>> from sympy import symbols
@@ -136,10 +153,11 @@ def conjuncts(expr):
     >>> conjuncts(A | B)
     [Or(A, B)]
     """
-    if isinstance(expr, And):
-        return list(expr.args)
-    else:
+    if expr:
+        if type(expr) is And:
+            return list(expr.args)
         return [expr]
+    return []
 
 def disjuncts(expr):
     """Return a list of the disjuncts in the sentence s.
-- 
1.6.4

>From 61cb07c0dc2b4f1629010c2fa4ba0bd98e544d2d Mon Sep 17 00:00:00 2001
From: Fabian Pedregosa <fab...@fseoane.net>
Date: Mon, 10 Aug 2009 09:11:05 +0200
Subject: [PATCH 2/4] queries module improvements

Some code has been simplified, added docstring, bugs fixed and test added.
---
 sympy/queries/__init__.py          |   15 ++++-----------
 sympy/queries/handlers/__init__.py |    4 +++-
 sympy/queries/handlers/calculus.py |    4 +++-
 sympy/queries/handlers/ntheory.py  |    2 +-
 sympy/queries/handlers/order.py    |   29 +++++++++++++++++++++--------
 sympy/queries/handlers/sets.py     |    2 +-
 sympy/queries/tests/test_query.py  |    5 +++++
 7 files changed, 38 insertions(+), 23 deletions(-)

diff --git a/sympy/queries/__init__.py b/sympy/queries/__init__.py
index d8146fd..8954268 100644
--- a/sympy/queries/__init__.py
+++ b/sympy/queries/__init__.py
@@ -28,7 +28,7 @@ class Q:
     odd = 'odd'
 
 # TODO: maybe this should be moved to another file?
-def ask(expr, key, assumptions=[]):
+def ask(expr, key, assumptions=True):
     """
     Method for inferring properties about objects.
 
@@ -58,13 +58,7 @@ def ask(expr, key, assumptions=[]):
         the official release
     """
     expr = sympify(expr)
-
-    if assumptions:
-        assumptions = And(assumptions, And(*global_assumptions))
-    elif global_assumptions:
-        assumptions = And(*global_assumptions)
-    if not isinstance(assumptions, (list, tuple)):
-        assumptions = conjuncts(to_cnf(assumptions))
+    assumptions = And(assumptions, And(*global_assumptions))
 
     # direct resolution method, no logic
     resolutors = []
@@ -82,8 +76,7 @@ def ask(expr, key, assumptions=[]):
     if res is not None:
         return res
 
-    if assumptions: pass
-    else: return
+    if assumptions is True: return
 
     # use logic inference
     if not expr.is_Atom: return
@@ -91,8 +84,8 @@ def ask(expr, key, assumptions=[]):
     for k, values in known_facts_dict.iteritems():
         for v in values:
             clauses.append(Equivalent(compile_rule(k), compile_rule(v)))
-    result = None
 
+    assumptions = conjuncts(to_cnf(assumptions))
     # add assumptions to the knowledge base
     for assump in assumptions:
         conj = eliminate_assume(assump, symbol=expr)
diff --git a/sympy/queries/handlers/__init__.py b/sympy/queries/handlers/__init__.py
index 7f36e1b..474cd92 100644
--- a/sympy/queries/handlers/__init__.py
+++ b/sympy/queries/handlers/__init__.py
@@ -1,3 +1,4 @@
+from sympy.logic.boolalg import conjuncts
 from sympy.queries import Q, ask
 
 class AskHandler(object):
@@ -19,7 +20,8 @@ class AskCommutativeHandler(CommonHandler):
     @staticmethod
     def Symbol(expr, assumptions):
         """Objects are expected to be commutative unless otherwise stated"""
-        for assump in assumptions:
+        if assumptions is True: return True
+        for assump in conjuncts(assumptions):
             if assump.expr == expr and assump.key == 'commutative':
                 return assump.value
         return True
diff --git a/sympy/queries/handlers/calculus.py b/sympy/queries/handlers/calculus.py
index 17449db..dfa4d65 100644
--- a/sympy/queries/handlers/calculus.py
+++ b/sympy/queries/handlers/calculus.py
@@ -2,6 +2,7 @@
 This module contains query handlers resposible for calculus queries:
 infinitesimal, bounded, etc.
 """
+from sympy.logic.boolalg import conjuncts
 from sympy.queries import Q, ask
 from sympy.queries.handlers import CommonHandler
 
@@ -60,7 +61,8 @@ class AskBoundedHandler(CommonHandler):
 
     @staticmethod
     def Symbol(expr, assumptions):
-        for assump in assumptions:
+        if assumptions is True: return False
+        for assump in conjuncts(assumptions):
             if assump.expr == expr and assump.key == 'bounded':
                 return assump.value
         return False
diff --git a/sympy/queries/handlers/ntheory.py b/sympy/queries/handlers/ntheory.py
index 772a9bd..0c62c47 100644
--- a/sympy/queries/handlers/ntheory.py
+++ b/sympy/queries/handlers/ntheory.py
@@ -160,7 +160,7 @@ def Add(expr, assumptions):
 
     @staticmethod
     def Integer(expr, assumptions):
-        return expr % 2 == 0
+        return not bool(expr.p & 1)
 
     @staticmethod
     def Rational(expr, assumptions):
diff --git a/sympy/queries/handlers/order.py b/sympy/queries/handlers/order.py
index 4b59202..6fc3864 100644
--- a/sympy/queries/handlers/order.py
+++ b/sympy/queries/handlers/order.py
@@ -8,8 +8,17 @@
 
 class AskNegativeHandler(CommonHandler):
     """
-    Handler for key 'negative'
-    Test that an expression is less (strict) than zero
+    This is called by ask() when key='negative'
+
+    Test that an expression is less (strict) than zero.
+
+    Examples:
+
+    >>> from sympy import *
+    >>> ask(pi+1, Q.negative) # this calls AskNegativeHandler.Add
+    False
+    >>> ask(pi**2, Q.negative) # this calls AskNegativeHandler.Pow
+    False
     """
 
     @staticmethod
@@ -54,16 +63,20 @@ def Mul(expr, assumptions):
 
     @staticmethod
     def Pow(expr, assumptions):
+        """
+        Real ** Even -> NonNegative
+        Real ** Odd  -> same_as_base
+        NonNegative ** Positive -> NonNegative
+        """
         if expr.is_number:
             return AskNegativeHandler._number(expr, assumptions)
-        if ask(expr.base, Q.negative, assumptions):
-            if ask(expr.exp, Q.odd, assumptions):
-                return True
-            if ask(expr.exp, Q.even, assumptions):
+        if ask(expr.base, Q.real, assumptions):
+            if ask(expr.base, Q.positive, assumptions):
                 return False
-        elif ask(expr.base, Q.positive, assumptions):
-            if ask(expr.exp, Q.real, assumptions):
+            if ask(expr.exp, Q.even, assumptions):
                 return False
+            if ask(expr.exp, Q.odd, assumptions):
+                return ask(expr.base, Q.negative, assumptions)
 
     @staticmethod
     def ImaginaryUnit(expr, assumptions):
diff --git a/sympy/queries/handlers/sets.py b/sympy/queries/handlers/sets.py
index 23de759..50ecc51 100644
--- a/sympy/queries/handlers/sets.py
+++ b/sympy/queries/handlers/sets.py
@@ -44,7 +44,7 @@ def Mul(expr, assumptions):
                 if arg.is_Rational:
                     if arg.q == 2:
                         return ask(2*expr, Q.even, assumptions)
-                    if arg.q % 2 == 1:
+                    if ~(arg.q & 1):
                         return None
                 elif ask(arg, Q.irrational, assumptions):
                     if _output:
diff --git a/sympy/queries/tests/test_query.py b/sympy/queries/tests/test_query.py
index e96ce7e..5de69df 100644
--- a/sympy/queries/tests/test_query.py
+++ b/sympy/queries/tests/test_query.py
@@ -687,6 +687,7 @@ def test_integer():
     assert ask(2*x, Q.integer, Assume(x, Q.prime)) == True
     assert ask(2*x, Q.integer, Assume(x, Q.rational)) == None
     assert ask(2*x, Q.integer, Assume(x, Q.real)) == None
+    assert ask(sqrt(2)*x, Q.integer, Assume(x, Q.integer)) == False
 
     assert ask(x/2, Q.integer, Assume(x, Q.odd)) == False
     assert ask(x/2, Q.integer, Assume(x, Q.even)) == True
@@ -712,6 +713,10 @@ def test_negative():
     assert ask(x+y, Q.negative, Assume(x, Q.negative) &\
                      Assume(y, Q.negative)) == True
 
+    assert ask(x**2, Q.negative) == None
+    assert ask(x**2, Q.negative, Assume(x, Q.real)) == False
+    assert ask(x**1.4, Q.negative, Assume(x, Q.real)) == None
+
     assert ask(x*y, Q.negative) == None
     assert ask(x*y, Q.negative, Assume(x, Q.positive) & \
                      Assume(y, Q.positive)) == False
-- 
1.6.4

>From ea7d017cbac151e57bf6875f6c71f7f126567f43 Mon Sep 17 00:00:00 2001
From: Fabian Pedregosa <fab...@fseoane.net>
Date: Thu, 16 Jul 2009 19:14:17 +0200
Subject: [PATCH 3/4] Implement __mod__ in class Infinity

oo % number now returns NaN instead of rasing an exception
---
 sympy/core/numbers.py            |    5 +++++
 sympy/core/tests/test_numbers.py |    2 ++
 2 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/sympy/core/numbers.py b/sympy/core/numbers.py
index 11a2b72..ea46ee7 100644
--- a/sympy/core/numbers.py
+++ b/sympy/core/numbers.py
@@ -1190,6 +1190,11 @@ def __le__(a, b):
             return True
         return False
 
+    def __mod__(self, other):
+        return S.NaN
+
+    __rmod__ = __mod__
+
 
 class NegativeInfinity(Rational):
     __metaclass__ = SingletonMeta
diff --git a/sympy/core/tests/test_numbers.py b/sympy/core/tests/test_numbers.py
index 876d782..06bfcf7 100644
--- a/sympy/core/tests/test_numbers.py
+++ b/sympy/core/tests/test_numbers.py
@@ -132,6 +132,8 @@ def test_Infinity():
     assert 1/oo  == 0
     assert 1/(-oo)  == 0
     assert 8/oo  == 0
+    assert oo % 2 == nan
+    assert 2 % oo == nan
 
 def test_Infinity_2():
     x = Symbol('x')
-- 
1.6.4

>From d4419c61cdd7bf735c9812155b308546e5ac7930 Mon Sep 17 00:00:00 2001
From: Fabian Pedregosa <fab...@fseoane.net>
Date: Thu, 9 Jul 2009 01:42:36 +0200
Subject: [PATCH 4/4] Implement refine module

This module is responsible for simplifying an expression given
assumptions on its objects.
---
 setup.py                          |    1 +
 sympy/__init__.py                 |    1 +
 sympy/refine/__init__.py          |   82 +++++++++++++++++++++++++++++++++++++
 sympy/refine/tests/test_refine.py |   39 +++++++++++++++++
 4 files changed, 123 insertions(+), 0 deletions(-)
 create mode 100644 sympy/refine/__init__.py
 create mode 100644 sympy/refine/tests/test_refine.py

diff --git a/setup.py b/setup.py
index 753d291..43fcd23 100755
--- a/setup.py
+++ b/setup.py
@@ -67,6 +67,7 @@
     'sympy.polys',
     'sympy.printing',
     'sympy.printing.pretty',
+    'sympy.refine',
     'sympy.series',
     'sympy.simplify',
     'sympy.solvers',
diff --git a/sympy/__init__.py b/sympy/__init__.py
index e01ee96..9722736 100644
--- a/sympy/__init__.py
+++ b/sympy/__init__.py
@@ -27,6 +27,7 @@ def __sympy_debug():
 from functions import *
 from ntheory import *
 from concrete import *
+from refine import refine
 from simplify import *
 from solvers import *
 from matrices import *
diff --git a/sympy/refine/__init__.py b/sympy/refine/__init__.py
new file mode 100644
index 0000000..7f41921
--- /dev/null
+++ b/sympy/refine/__init__.py
@@ -0,0 +1,82 @@
+from sympy.core import S, Symbol, sympify
+from sympy.utilities.source import get_class
+from sympy.queries import Q, ask
+from sympy.logic.boolalg import fuzzy_not
+
+def refine(expr, assumptions=True):
+    """
+    Simplify an expression using assumptions
+
+    Gives the form of expr that would be obtained if symbols
+    in it were replaced by explicit numerical expressions satisfying
+    the assumptions.
+
+    Examples:
+    >>> from sympy import *
+    >>> x = Symbol('x')
+    >>> refine(sqrt(x**2), Assume(x, Q.real))
+    abs(x)
+    >>> refine(sqrt(x**2), Assume(x, Q.positive))
+    x
+    """
+    if not expr.is_Atom:
+        args = map(refine, expr.args, [assumptions]*len(expr.args))
+        # TODO: this will probably not work with Integral or Polynomial
+        expr = type(expr)(*args)
+    name = expr.__class__.__name__
+    handler = handlers_dict.get(name, None)
+    if handler is None: return expr
+    new_expr = handler(expr, assumptions)
+    if (new_expr is None) or (expr == new_expr):
+        return expr
+    return refine(new_expr, assumptions)
+
+def refine_abs(expr, assumptions):
+    """handler for the absolute value"""
+    arg = expr.args[0]
+    if ask(arg, Q.real, assumptions) and \
+            fuzzy_not(ask(arg, Q.negative, assumptions)):
+        # if it's nonnegative
+        return arg
+    if ask(arg, Q.negative, assumptions):
+        return -arg
+
+def refine_Pow(expr, assumptions):
+    """
+    abs(x)**2 --> x**2 if x real
+    """
+    from sympy.core import Pow, Rational
+    from sympy.functions import sign
+    if ask(expr.base, Q.real, assumptions):
+        if expr.base.is_number:
+            if ask(expr.exp, Q.even, assumptions):
+                return abs(expr.base) ** expr.exp
+            if ask(expr.exp, Q.odd, assumptions):
+                return sign(expr.base) * abs(expr.base) ** expr.exp
+        if isinstance(expr.exp, Rational):
+            if type(expr.base) is Pow:
+                return abs(expr.base.base) ** (expr.base.exp * expr.exp)
+
+def refine_exp(expr, assumptions):
+    """handler for exponential function
+    Some rules to simplify complex exponentials
+    """
+    arg = expr.args[0]
+    if arg.is_Mul:
+        coeff = arg.as_coefficient(S.Pi*S.ImaginaryUnit)
+        if coeff:
+            if ask(2*coeff, Q.integer, assumptions):
+                if ask(coeff, Q.even, assumptions):
+                    return S.One
+                elif ask(coeff, Q.odd, assumptions):
+                    return S.NegativeOne
+                elif ask(coeff + S.Half, Q.even, assumptions):
+                    return -S.ImaginaryUnit
+                elif ask(coeff + S.Half, Q.odd, assumptions):
+                    return S.ImaginaryUnit
+
+handlers_dict = {
+    'abs'        : refine_abs,
+    'Pow'        : refine_Pow,
+    'exp'        : refine_exp,
+}
diff --git a/sympy/refine/tests/test_refine.py b/sympy/refine/tests/test_refine.py
new file mode 100644
index 0000000..f0dbe08
--- /dev/null
+++ b/sympy/refine/tests/test_refine.py
@@ -0,0 +1,39 @@
+from sympy import S, symbols, Assume, exp, pi, sqrt, Rational, I, Q
+from sympy.refine import refine
+from sympy.utilities.pytest import XFAIL
+
+def test_abs():
+    x = symbols('x')
+    assert refine(abs(x), Assume(x, Q.positive)) == x
+    assert refine(1+abs(x), Assume(x, Q.positive)) == 1+x
+    assert refine(abs(x), Assume(x, Q.negative)) == -x
+    assert refine(1+abs(x), Assume(x, Q.negative)) == 1-x
+
+    assert refine(abs(x**2)) != x**2
+    assert refine(abs(x**2), Assume(x, Q.real)) == x**2
+
+def test_pow():
+    x, y = symbols('x y')
+    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
+
+    # nested powers
+    assert refine(sqrt(x**2)) != abs(x)
+    assert refine(sqrt(x**2), Assume(x, Q.complex)) != abs(x)
+    assert refine(sqrt(x**2), Assume(x, Q.real)) == abs(x)
+    assert refine(sqrt(x**2), Assume(x, Q.positive)) == x
+    assert refine((x**3)**(S(1)/3)) != x
+
+    assert refine((x**3)**(S(1)/3), Assume(x, Q.real)) != x
+    assert refine((x**3)**(S(1)/3), Assume(x, Q.positive)) == x
+
+    assert refine(sqrt(1/x), Assume(x, Q.real)) != 1/sqrt(x)
+    assert refine(sqrt(1/x), Assume(x, Q.positive)) == 1/sqrt(x)
+
+def test_exp():
+    x = symbols('x')
+    assert refine(exp(pi*I*2*x), Assume(x, Q.integer)) == 1
+    assert refine(exp(pi*I*2*(x+Rational(1,2))), Assume(x, Q.integer)) == -1
+    assert refine(exp(pi*I*2*(x+Rational(1,4))), Assume(x, Q.integer)) == I
+    assert refine(exp(pi*I*2*(x+Rational(3,4))), Assume(x, Q.integer)) == -I
-- 
1.6.4

Reply via email to