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