you can pull from http://fseoane.net/git/sympy.git, branch proposed
diff --git a/sympy/utilities/source.py b/sympy/utilities/source.py
index 56ca7f7..1470759 100644
--- a/sympy/utilities/source.py
+++ b/sympy/utilities/source.py
@@ -10,3 +10,29 @@ def source(object):
"""
print 'In file: %s' % inspect.getsourcefile(object)
print inspect.getsource(object)
+
+def get_class(lookup_view):
+ """
+ Convert a string version of a class name to the object.
+
+ This is used by the query method
+ """
+ if isinstance(lookup_view, str):
+ # Bail early for non-ASCII strings (they can't be functions).
+ lookup_view = lookup_view.encode('ascii')
+ mod_name, func_name = get_mod_func(lookup_view)
+ if func_name != '':
+ lookup_view = getattr(__import__(mod_name, {}, {}, ['']), func_name)
+ if not callable(lookup_view):
+ raise AttributeError("'%s.%s' is not a callable." % (mod_name, func_name))
+ return lookup_view
+
+
+def get_mod_func(callback):
+ # Converts 'sympy.core.query.QueryIntegerHandler' to
+ # ['sympy.core.query', 'QueryIntegerHandler']
+ try:
+ dot = callback.rindex('.')
+ except ValueError:
+ return callback, ''
+ return callback[:dot], callback[dot+1:]
\ No newline at end of file
--
1.6.1.2
It has unacceptable slow performance. This is implemented just as
workaround for the new assumption system, but eventually a complete
backward-chaining algorithm should be implemented.
---
sympy/core/facts.py | 42 ++++++++++++++++++++++++++++++++++
sympy/core/tests/test_assumptions.py | 28 +++++++++++++++++++++-
sympy/core/tests/test_facts.py | 10 ++++++++
3 files changed, 79 insertions(+), 1 deletions(-)
diff --git a/sympy/core/facts.py b/sympy/core/facts.py
index 2e9d666..27bf0c6 100644
--- a/sympy/core/facts.py
+++ b/sympy/core/facts.py
@@ -817,6 +817,48 @@ def __init__(self, rules):
# TODO: add proper support for U (None), i.e.
# integer=U -> rational=U ??? (XXX i'm not sure)
+ def bc_ask(self, goal, theta={}):
+ """Backward-chaining algorithm. Backward-chaining reasoners process
+ queries and return proofs for the answers they provide.
+
+ This is a very naive implementation. It constructs EVERY possible query
+ with propositions from self.defined_facts and tries every one to see
+ if it succeeds.
+
+ It has unacceptable slow performance. This is implemented just as
+ workaround for the new assumption system. Do *NOT* commit to the
+ main repo
+ """
+
+ if len(goal.keys()) > 1:
+ raise NotImplementedError
+
+ key = goal.keys()[0]
+ _facts = []
+ from sympy.utilities.iterables import subsets, variations
+ for k in range(len(self.defined_facts)):
+ k_subsets = list(subsets(self.defined_facts, k))
+ for k_sub in k_subsets:
+ if not key in k_sub:
+ # it won't be of any use if goal is in answers
+ for _pos in variations([True, False], len(k_sub), repetition=True):
+ _res = dict()
+ for k, i in enumerate(k_sub):
+ _res[i] = _pos[k]
+ _facts.append(_res)
+
+ answers = []
+ for _f in _facts:
+ try:
+ _deduced = self.deduce_all_facts(_f)
+
+ if _deduced.has_key(key) and _deduced[key] == goal[key]:
+ answers.append(_f)
+ except AssertionError:
+ pass
+
+ return answers
+
def deduce_all_facts(self, facts, base=None):
"""Deduce all facts from known facts ({} or [] of (k,v))
diff --git a/sympy/core/tests/test_assumptions.py b/sympy/core/tests/test_assumptions.py
index cb1c0d5..a4d7ca8 100644
--- a/sympy/core/tests/test_assumptions.py
+++ b/sympy/core/tests/test_assumptions.py
@@ -1,4 +1,30 @@
-from sympy.core import Symbol, S, Rational, Integer
+from sympy.core.assumptions import Assume
+from sympy.core import Symbol
+
+def test_assume():
+ x = Symbol('x')
+ assump = Assume(x, 'integer')
+ assert assump.expr == x
+ assert assump.key == 'integer'
+ assert assump.bool_key == True
+
+def test_assume_False():
+ x = Symbol('x')
+ assump = Assume(x, 'integer', False)
+ assert assump.expr == x
+ assert assump.key == 'integer'
+ assert assump.bool_key == False
+
+def test_assume_equal():
+ x = Symbol('x')
+ assert Assume(x, 'positive') == Assume(x, 'positive')
+ assert Assume(x, 'positive') != Assume(x, 'positive', False)
+ assert Assume(x, 'positive', False) == Assume(x, 'positive', False)
+
+### Also check the old assumption system until it is completely
+### removed
+
+from sympy.core import S, Rational, Integer
from sympy.utilities.pytest import XFAIL, raises
def test_symbol_unset():
diff --git a/sympy/core/tests/test_facts.py b/sympy/core/tests/test_facts.py
index b86346f..f7a129e 100644
--- a/sympy/core/tests/test_facts.py
+++ b/sympy/core/tests/test_facts.py
@@ -335,3 +335,13 @@ def X_test_FactRules_deduce_cow():
assert X == {'real': T, 'neg': F, 'zero': F, 'pos': T}
#assert set(new_knowledge) == set([ ('zero',F), ('pos',T) ]) # XXX disabled
+
+def test_bc_ask():
+ # test backward chaining
+ f = FactRules(['negative == real & !zero & !pos'])
+ f.bc_ask({'negative' : True}) == [{'pos': False, 'real': True, 'zero': False}]
+ f = FactRules(['Odd -> Integer'])
+ f.bc_ask({'Integer' : True}) == {'Odd':True, 'Integer':True}
+
+ f = FactRules(['positive == real & !negative & !zero'])
+ f.bc_ask({'positive' : True}) == {'negative' : False, 'real' : True, 'zero' : False}
--
1.6.1.2
diff --git a/sympy/utilities/iterables.py b/sympy/utilities/iterables.py
index 6179622..61879a6 100644
--- a/sympy/utilities/iterables.py
+++ b/sympy/utilities/iterables.py
@@ -173,3 +173,39 @@ def recursion(result, M, k):
for elem in recursion([item], M[i + 1:], k - 1):
yield elem
+
+def cartes(seq0, seq1, modus='pair'):
+ """ return the Cartesian Product of two sequences """
+ if modus == 'pair':
+ return [[item0, item1] for item0 in seq0 for item1 in seq1]
+ elif modus == 'triple':
+ return [item0 + [item1] for item0 in seq0 for item1 in seq1]
+
+def variations(seq, n, repetition=False):
+ """Returns all the variations of the list of size n.
+
+ variations(seq, n, True) will return all the variations of the list of
+ size n with repetitions
+
+ variations(seq, n, False) will return all the variations of the list of
+ size n without repetitions
+ """
+ def setrep(seq): # remove sets with duplicates (repetition is relevant)
+ def delrep(seq): # remove duplicates while maintaining order
+ result = []
+ for item in seq:
+ if item not in result:
+ result.append(item)
+ return result
+ return [item for item in seq if item == delrep(item)]
+
+ if n == 1:
+ return [[item] for item in seq]
+ result = range(len(seq))
+ cartesmodus = 'pair'
+ for i in range(n-1):
+ result = cartes(result, range(len(seq)), cartesmodus)
+ if not repetition:
+ result = setrep(result)
+ cartesmodus = 'triple'
+ return [[seq[index] for index in indices] for indices in result]
diff --git a/sympy/utilities/tests/test_iterables.py b/sympy/utilities/tests/test_iterables.py
index 1eea497..959c900 100644
--- a/sympy/utilities/tests/test_iterables.py
+++ b/sympy/utilities/tests/test_iterables.py
@@ -1,6 +1,6 @@
from sympy import symbols
from sympy.utilities.iterables import postorder_traversal, \
- preorder_traversal, flatten, subsets
+ preorder_traversal, flatten, subsets, variations
w,x,y,z= symbols('wxyz')
@@ -31,3 +31,7 @@ def test_subsets():
assert list(subsets([1, 2, 3], 2)) == [[1, 2], [1,3], [2, 3]]
assert list(subsets([1, 2, 3], 3)) == [[1, 2, 3]]
+def test_variations():
+ assert variations([1,2,3], 2) == [[1, 2], [1, 3], [2, 1], [2, 3], [3, 1], [3, 2]]
+ assert variations([1,2,3], 2, True) == [[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], \
+ [3,1], [3,2], [3,3]]
--
1.6.1.2
1. Can't assume relations. It is not possible to specify relations between different
objects. For example, you can assume that x>0 and y>0, but you can't
assume x>y
2. Verbose code splitted across multiple classes. Each class must override
query methods (.is_*).
3. Extensibility. To add new assumptions you have to add code to
the core
The assumption system implemented in this series of patches relies on these
principles:
1. Assumptions are separate from objects. Assumptions are instances of Assume, which is
a class that holds a reference to the expression and to the 'key' (what it assumes)
2. Queries out of the core. No more .is_* methods. Specific queries are implemented
as subclasses of QueryHandler, and the query function calls the appropriate
subclass of QueryHandler
query -> QueryHandler -> QueryNegativeHandler
-> QueryIntegerHandler
-> QueryBoundedHandler
...
That way, creating new queries is a matter of subclassing QueryHandler and override
the apropriate methods.
In this patch, two fundamental objects were created for this task:
- sympy.core.assumptions.Assume, which takes track of the
object's assumptions.
- a query method: sympy.query.query, which replaces the old
expr.is_* syntax. Also the relevat handlers were created to resolve
some queries, in sympy.queries.handlers
The old assumptions system was not modified, but should be removed
gradually
---
sympy/__init__.py | 1 +
sympy/core/__init__.py | 1 +
sympy/core/assumptions.py | 37 +++
sympy/query/__init__.py | 154 ++++++++++
sympy/query/handlers/__init__.py | 169 +++++++++++
sympy/query/handlers/calculus.py | 125 ++++++++
sympy/query/handlers/ntheory.py | 63 ++++
sympy/query/handlers/order.py | 168 +++++++++++
sympy/query/handlers/sets.py | 239 +++++++++++++++
sympy/query/tests/test_query.py | 594 ++++++++++++++++++++++++++++++++++++++
10 files changed, 1551 insertions(+), 0 deletions(-)
create mode 100644 sympy/query/__init__.py
create mode 100644 sympy/query/handlers/__init__.py
create mode 100644 sympy/query/handlers/calculus.py
create mode 100644 sympy/query/handlers/ntheory.py
create mode 100644 sympy/query/handlers/order.py
create mode 100644 sympy/query/handlers/sets.py
create mode 100644 sympy/query/tests/test_query.py
diff --git a/sympy/__init__.py b/sympy/__init__.py
index b0b637f..3cef873 100644
--- a/sympy/__init__.py
+++ b/sympy/__init__.py
@@ -33,6 +33,7 @@ def __sympy_debug():
from geometry import *
from utilities import *
from integrals import *
+from query import query
# This module is slow to import:
#from physics import units
from plotting import Plot, textplot
diff --git a/sympy/core/__init__.py b/sympy/core/__init__.py
index 6b9355e..0fd8976 100644
--- a/sympy/core/__init__.py
+++ b/sympy/core/__init__.py
@@ -15,6 +15,7 @@
Function, expand, PoleError
from interval import Interval
from evalf import PrecisionExhausted, N
+from assumptions import Assume
# expose singletons like exp, log, oo, I, etc.
for _n, _cls in Basic.singleton.items():
diff --git a/sympy/core/assumptions.py b/sympy/core/assumptions.py
index e8a9fd6..c528cfc 100644
--- a/sympy/core/assumptions.py
+++ b/sympy/core/assumptions.py
@@ -372,3 +372,40 @@ def getit(self):
#dis(getit)
return getit
+
+
+class Assume(object):
+ """New-style assumptions
+
+ >>> from sympy import Symbol, Assume
+ >>> x = Symbol('x')
+ >>> Assume(x, 'integer')
+ Assume( x, integer, True )
+ >>> Assume(x, 'integer', False)
+ Assume( x, integer, False )
+ """
+
+ def __init__(self, expr, key, bool_key=True):
+ if not expr.__class__.__name__ == "Symbol":
+ raise NotImplementedError
+ self._args = (expr, key, bool_key)
+
+ @property
+ def expr(self):
+ return self._args[0]
+
+ @property
+ def key(self):
+ return self._args[1]
+
+ @property
+ def bool_key(self):
+ return self._args[2]
+
+ def __repr__(self):
+ return u"Assume( %s, %s, %s )" % (self.expr, self.key, self.bool_key)
+
+ def __eq__(self, other):
+ if isinstance(other, type(self)):
+ return self._args == other._args
+ return False
diff --git a/sympy/query/__init__.py b/sympy/query/__init__.py
new file mode 100644
index 0000000..efcc891
--- /dev/null
+++ b/sympy/query/__init__.py
@@ -0,0 +1,154 @@
+# -*- coding: utf-8 -*-
+import copy
+import inspect
+from sympy.core import S
+from sympy.core.facts import FactRules
+from sympy.utilities.source import get_class
+
+# query_dict tells us what query handler we should use
+# for a particular key
+query_dict = {
+ 'bounded' : 'sympy.query.handlers.QueryBoundedHandler',
+ 'commutative' : 'sympy.query.handlers.QueryCommutativeHandler',
+ 'comparable' : 'sympy.query.handlers.QueryComparableHadler',
+ 'complex' : 'sympy.query.handlers.sets.QueryComplexHandler',
+ 'composite' : 'sympy.query.handlers.ntheory.QueryCompositeHandler',
+ 'even' : 'sympy.query.handlers.ntheory.QueryEvenHandler',
+ 'finite' : 'sympy.query.handlers.QueryFiniteHandler',
+ 'imaginary' : 'sympy.query.handlers.sets.QueryImaginaryHandler',
+ 'infinitesimal' : 'sympy.query.handlers.QueryInfinitesimalHandler',
+ 'integer' : 'sympy.query.handlers.sets.QueryIntegerHandler',
+ 'irrational' : 'sympy.query.handlers.sets.QueryIrrationalHandler',
+ 'rational' : 'sympy.query.handlers.sets.QueryRationalHandler',
+ 'nonnegative' : 'sympy.query.handlers.order.QueryNonNegativeHandler',
+ 'noninteger' : 'sympy.query.handlers.sets.QueryNonIntegerHandler',
+ 'nonpositive' : 'sympy.query.handlers.order.QueryNonPositiveHandler',
+ 'nonzero' : 'sympy.query.handlers.order.QueryNonZeroHandler',
+ 'negative' : 'sympy.query.handlers.order.QueryNegativeHandler',
+ 'positive' : 'sympy.query.handlers.order.QueryPositiveHandler',
+ 'prime' : 'sympy.query.handlers.ntheory.QueryPrimeHandler',
+ 'real' : 'sympy.query.handlers.sets.QueryRealHandler',
+ 'odd' : 'sympy.query.handlers.ntheory.QueryOddHandler',
+ 'unbounded' : 'sympy.query.handlers.QueryUnboundedHandler',
+ 'zero' : 'sympy.query.handlers.order.QueryZeroHandler'
+}
+
+
+def query(expr, key, assumptions=[], qdict={}, nesting=True):
+ """
+ Examples:
+ >>> query(2, 'negative')
+ False
+ >>> query(x + y, 'complex')
+ True
+ >>> query(x + y, 'real', Assume(x, 'real'))
+ True
+
+ Strategy:
+ - Phase 1: we call the handler for this key
+ - Phase 2: we try to deduce that the query is False by showing
+ that some prerequisite is not met
+ - Phase 3: try to deduce the query is True by showing that other requisite
+ that implies this queries does verify.
+ """
+ resolution = None
+ expr = S(expr)
+
+ if not isinstance(assumptions, (list, tuple)):
+ assumptions = [assumptions]
+
+ _assumptions = []
+ for assump in assumptions:
+ if expr.has(assump.expr):
+ # logic inference is expensive, so we only use the assumptions
+ # that are relevant
+ _assumptions.append(assump)
+
+ _query_dict = query_dict.copy()
+
+ if len(qdict) > 0:
+ _query_dict.update(qdict)
+
+ resolutor = get_class(_query_dict[key])(expr, key, _assumptions)
+
+ if resolutor.resolution is not None:
+ return resolutor.resolution
+
+ # Run this class handler
+ if hasattr(resolutor, expr.__class__.__name__):
+ resolution = getattr(resolutor, expr.__class__.__name__)()
+ else:
+ # we try for method defined for subclasses
+ mro = inspect.getmro(expr.__class__)
+ for subclass in mro:
+ if hasattr(resolutor, subclass.__name__):
+ resolution = getattr(resolutor, subclass.__name__)()
+ break
+ else:
+ # if no resolution maybe we could skip some steps
+ pass
+
+ if resolution is not None:
+ # if resolution is False or True there's no need to continue
+ return resolution
+
+ if not nesting:
+ return None
+
+ if resolutor.facts is None:
+ # initialize facts
+ facts = []
+ else:
+ facts = copy.copy(resolutor.facts)
+ # add to self.facts all the assumption's facts
+ # so we get a tree that connects the assumptions facts
+ # with the query's facts
+ for assump in _assumptions:
+ if not expr.has(assump.expr):
+ continue
+ if _query_dict.has_key(assump.key):
+ _resolutor = get_class(_query_dict[assump.key])
+ if _resolutor.facts is not None:
+ facts.extend(_resolutor.facts )
+ if len(facts) == 0:
+ # thre are no facts and we've finished
+ return None
+
+ compiled_rules = FactRules(facts)
+
+ #phase 2, deduce it is false by fc
+ fc_query = compiled_rules.deduce_all_facts({key : True})
+ fc_query.pop(key) # we do not want to fall into infinite recursion
+
+ for k in fc_query:
+ _qresult = query(expr, k, _assumptions, qdict, nesting=False)
+ if _qresult is None:
+ continue
+ elif not (_qresult == fc_query[k]):
+ return False
+
+ # Phase 3: deduce that the query is True backward-chaining
+ for bc_query in compiled_rules.bc_ask({key : True}):
+ for k in bc_query:
+ if query(expr, k, _assumptions, qdict, nesting=False) != bc_query[k]:
+ return
+ else:
+ return True
+
+class QueryHandler(object):
+ """Base class that all Query Handlers must inherit"""
+
+ facts = None
+ resolution = None
+
+ def __init__(self, expr, key, assumptions=None):
+ """
+ TODO: assumptions are not always expr
+
+ speed up putting facts locally to avoid extra attribute access
+
+ We **need** beta-rules in facts.FactorRules
+ """
+ self.expr = expr
+ self.key = key
+ self.assumptions = assumptions
diff --git a/sympy/query/handlers/__init__.py b/sympy/query/handlers/__init__.py
new file mode 100644
index 0000000..758a6e4
--- /dev/null
+++ b/sympy/query/handlers/__init__.py
@@ -0,0 +1,169 @@
+
+from sympy.core.assumptions import Assume
+from sympy.query import query, QueryHandler
+
+
+"""Facts must only have one level of indirection to avoid infinite recursion"""
+
+class CommonHandler(QueryHandler):
+ """Defines some useful methods common to most Handlers """
+ def Symbol(self):
+ if self.assumptions is None:
+ return None
+ for assump in self.assumptions:
+ if assump.expr == self.expr and assump.key == self.key:
+ return assump.bool_key
+ return None
+
+
+class QueryCommutativeHandler(CommonHandler):
+ def Basic(self):
+ for a in self.expr.args:
+ if query(a, self.key, self.assumptions) == False:
+ return False
+ else:
+ return True
+
+class QueryBoundedHandler(CommonHandler):
+
+ def Add(self):
+ """
+ Bounded + Bounded -> Bounded
+ Unbounded + Bounded -> Unbounded
+ Unbounded + Unbounded -> ?
+ """
+ _output = True
+ for arg in self.expr.args:
+ _result = query(arg, self.key, self.assumptions)
+ if _result is None:
+ return
+ elif _result is False:
+ if _output:
+ _output = False
+ else:
+ return
+ return _output
+
+ def Mul(self):
+ """
+ Bounded * Bounded -> Bounded
+ (NotInfinitesimal & Bounded) * Unbounded -> Unbounded
+ Bounded * Unbounded -> ?
+ """
+ _output = True
+ for arg in self.expr.args:
+ if not query(arg, self.key, self.assumptions):
+ if not query(arg, 'infinitesimal', self.assumptions):
+ if _output:
+ _output = False
+ else:
+ return
+ else:
+ return
+ return _output
+
+ def Number(self):
+ return True
+
+ def Infinity(self):
+ return False
+
+ def NegativeInfinity(self):
+ return False
+
+ def Pi(self):
+ return True
+
+ def Exp1(self):
+ return True
+
+ def ImaginaryUnit(self):
+ return True
+
+ def NaN(self):
+ return None
+
+class QueryUnboundedHandler(CommonHandler):
+ facts = ['unbounded == !bounded']
+
+class QueryFiniteHandler(CommonHandler):
+
+ def Mul(self):
+ for arg in self.expr.args:
+ _result = query(arg, self.key, self.assumptions)
+ if not _result:
+ return _result
+ return True
+
+ Add = Mul
+
+ def Number(self):
+ return True
+
+ def Infinity(self):
+ return False
+
+ def NegativeInfinity(self):
+ return False
+
+ def NumberSymbol(self):
+ return True
+
+ def ImaginaryUnit(self):
+ return True
+
+ def NaN(self):
+ return None
+
+class QueryInfinitesimalHandler(CommonHandler):
+
+ def Mul(self):
+ for arg in self.expr.args:
+ _result = query(arg, self.key, self.assumptions)
+ if not _result:
+ return _result
+ return True
+
+ Add = Mul
+
+ def Number(self):
+ return self.expr == 0
+
+ def Pi(self):
+ return False
+
+ def Exp1(self):
+ return False
+
+ def ImaginaryUnit(self):
+ return False
+
+ def NaN(self):
+ return None
+
+class QueryComparableHadler(CommonHandler):
+
+ def Mul(self):
+ for arg in self.expr.args:
+ _result = query(arg, self.key, self.assumptions)
+ if not _result:
+ return _result
+ return True
+
+ Add = Mul
+
+ def Number(self):
+ return True
+
+ def ImaginaryUnit(self):
+ return False
+
+ def Pi(self):
+ return True
+
+ def Exp1(self):
+ return True
+
+ def NaN(self):
+ return False
+
diff --git a/sympy/query/handlers/calculus.py b/sympy/query/handlers/calculus.py
new file mode 100644
index 0000000..5b10980
--- /dev/null
+++ b/sympy/query/handlers/calculus.py
@@ -0,0 +1,125 @@
+"""This module contains query handlers resposible for calculus queries:
+infinitesimal, bounded, etc.
+"""
+
+from sympy.query.handlers import CommonHandler
+
+class QueryInfinitesimalHandler(CommonHandler):
+
+ def Mul(self):
+ for arg in self.expr.args:
+ _result = query(arg, self.key, self.assumptions)
+ if not _result:
+ return _result
+ return True
+
+ Add = Mul
+
+ def Number(self):
+ return self.expr == 0
+
+ def Pi(self):
+ return False
+
+ def Exp1(self):
+ return False
+
+ def ImaginaryUnit(self):
+ return False
+
+ def NaN(self):
+ return None
+
+
+class QueryBoundedHandler(CommonHandler):
+
+ def Add(self):
+ """
+ Bounded + Bounded -> Bounded
+ Unbounded + Bounded -> Unbounded
+ Unbounded + Unbounded -> ?
+ """
+ _output = True
+ for arg in self.expr.args:
+ _result = query(arg, self.key, self.assumptions)
+ if _result is None:
+ return
+ elif _result is False:
+ if _output:
+ _output = False
+ else:
+ return
+ return _output
+
+ def Mul(self):
+ """
+ Bounded * Bounded -> Bounded
+ (NotInfinitesimal & Bounded) * Unbounded -> Unbounded
+ Bounded * Unbounded -> ?
+ """
+ _output = True
+ for arg in self.expr.args:
+ if not query(arg, self.key, self.assumptions):
+ if not query(arg, 'infinitesimal', self.assumptions):
+ if _output:
+ _output = False
+ else:
+ return
+ else:
+ return
+ return _output
+
+ def Number(self):
+ return True
+
+ def Infinity(self):
+ return False
+
+ def NegativeInfinity(self):
+ return False
+
+ def Pi(self):
+ return True
+
+ def Exp1(self):
+ return True
+
+ def ImaginaryUnit(self):
+ return True
+
+ def NaN(self):
+ return None
+
+
+class QueryUnboundedHandler(CommonHandler):
+ facts = ['unbounded == !bounded']
+
+
+class QueryFiniteHandler(CommonHandler):
+
+ def Mul(self):
+ for arg in self.expr.args:
+ _result = query(arg, self.key, self.assumptions)
+ if not _result:
+ return _result
+ return True
+
+ Add = Mul
+
+ def Number(self):
+ return True
+
+ def Infinity(self):
+ return False
+
+ def NegativeInfinity(self):
+ return False
+
+ def NumberSymbol(self):
+ return True
+
+ def ImaginaryUnit(self):
+ return True
+
+ def NaN(self):
+ return None
\ No newline at end of file
diff --git a/sympy/query/handlers/ntheory.py b/sympy/query/handlers/ntheory.py
new file mode 100644
index 0000000..ca8c6f7
--- /dev/null
+++ b/sympy/query/handlers/ntheory.py
@@ -0,0 +1,63 @@
+from sympy.query import query
+from sympy.query.handlers import CommonHandler
+
+
+class QueryPrimeHandler(CommonHandler):
+ facts = ['prime -> integer & positive',
+ 'prime -> nonnegative']
+
+ def Integer(self):
+ from sympy.ntheory import isprime
+ return isprime(self.expr)
+
+ def Rational(self):
+ return False
+
+ def Real(self):
+ if query(self.expr, 'integer'):
+ return query(int(self.expr), self.key)
+ else:
+ return False
+
+ def Infinity(self):
+ return None
+
+ def NegativeInfinity(self):
+ return None
+
+ def ImginaryUnit(self):
+ return False
+
+ def NaN(self):
+ return None
+
+class QueryCompositeHandler(CommonHandler):
+ facts = ['composite == positive & integer & !prime']
+
+
+class QueryEvenHandler(CommonHandler):
+ facts = ['even -> integer']
+
+ def Integer(self):
+ return self.expr % 2 == 0
+
+ def Rational(self):
+ return False
+
+ def Real(self):
+ if query(self.expr, 'integer'):
+ return int(self.expr) % 2 == 0
+ else:
+ return False
+
+ def Infinity(self):
+ return None
+
+ def NegativeInfinity(self):
+ return None
+
+ def NaN(self):
+ return None
+
+class QueryOddHandler(CommonHandler):
+ facts = ['odd == integer & !even']
\ No newline at end of file
diff --git a/sympy/query/handlers/order.py b/sympy/query/handlers/order.py
new file mode 100644
index 0000000..ff0f129
--- /dev/null
+++ b/sympy/query/handlers/order.py
@@ -0,0 +1,168 @@
+from sympy.core import Symbol
+from sympy.query import query
+from sympy.query.handlers import CommonHandler
+
+
+class QueryNegativeHandler(CommonHandler):
+ """http://en.wikipedia.org/wiki/Negative_number"""
+
+ facts = ['negative -> real',
+ 'negative -> !positive',
+ 'negative -> !zero']
+
+ def __init__(self, expr, key, assumptions=None):
+ CommonHandler.__init__(self, expr, key, assumptions)
+ if len(expr.atoms(Symbol)) == 0 and query(expr, 'comparable'):
+ # if there are no symbols just evalf
+ self.resolution = expr.evalf() < 0
+
+ def Mul(self):
+ _nonpositives = 0
+ for arg in self.expr.args:
+ _result = query(arg, 'nonpositive', self.assumptions)
+ if _result == True:
+ _nonpositives += 1
+ elif _result is None:
+ return
+ if _nonpositives % 2 == 0:
+ # we don't care about zero
+ return False
+ else:
+ if query(self.expr, 'zero', self.assumptions) == False:
+ return True
+ else:
+ return None
+
+ def Add(self):
+ """
+ Positive + Positive -> Positive,
+ Negative + Negative -> Negative
+ """
+ for arg in self.expr.args:
+ if query(arg, self.key, self.assumptions) != True:
+ break
+ else:
+ # if all argument's are positive
+ return True
+
+ def Power(self):
+ if query(self.expr.base, 'negative', self.assumptions):
+ if query(self.expr.exp, 'odd', self.assumptions):
+ return True
+ if query(self.expr.exp, 'even', self.assumptions):
+ return False
+ elif query(self.expr.base, 'positive'):
+ if query(self.expr.exp, 'real'):
+ return False
+ return None
+
+ def Nan(self):
+ return None
+
+class QueryNonNegativeHandler(CommonHandler):
+ facts = ['nonnegative == real & !negative']
+
+ def __init__(self, expr, key, assumptions=None):
+ CommonHandler.__init__(self, expr, key, assumptions)
+ if len(expr.atoms(Symbol)) == 0 and query(expr, 'comparable'):
+ # if there are no symbols just evalf
+ self.resolution = expr.evalf() >= 0
+
+class QueryZeroHandler(CommonHandler):
+ facts = ['zero -> real',
+ 'zero -> nonpositive',
+ 'zero -> nonnegative']
+
+ def __init__(self, expr, key, assumptions=None):
+ CommonHandler.__init__(self, expr, key, assumptions)
+ if len(expr.atoms(Symbol)) == 0 and query(expr, 'comparable'):
+ # if there are no symbols just evalf
+ self.resolution = expr.evalf() == 0
+
+ def Add(self):
+ return None
+
+ def Mul(self):
+ for arg in self.expr.args:
+ result = query(arg, self.key, self.assumptions)
+ if result == True:
+ return True
+ elif result is None:
+ return None
+ else:
+ return False
+
+ def Pow(self):
+ if query(self.expr.exp, self.key, self.assumptions) == False:
+ return query(self.expr.base, self.key, self.assumptions)
+
+ def NaN(self):
+ return None
+
+class QueryNonZeroHandler(CommonHandler):
+ facts = ['nonzero == real & !zero']
+
+class QueryPositiveHandler(CommonHandler):
+ facts = ['positive -> real',
+ 'positive -> !negative',
+ 'positive -> !zero']
+
+ def __init__(self, expr, key, assumptions=None):
+ CommonHandler.__init__(self, expr, key, assumptions)
+ if len(expr.atoms(Symbol)) == 0 and query(expr, 'comparable'):
+ # if there are no symbols just evalf
+ self.resolution = expr.evalf() > 0
+
+
+ def Mul(self):
+ _result = True
+ parity = 0
+ for arg in self.expr.args:
+ _result = query(arg, self.key, self.assumptions)
+ if _result == False:
+ _is_zero = query(arg, 'zero', self.assumptions)
+ if _is_zero or (_is_zero is None):
+ # if this is True or None
+ return None
+ parity += 1
+ elif _result is None:
+ return None
+ return parity % 2 == 0
+
+ def Add(self):
+ for arg in self.expr.args:
+ if query(arg, self.key, self.assumptions) != True:
+ break
+ else:
+ # if all argument's are positive
+ return True
+
+ def Power(self):
+ if query(self.expr.base, self.key, self.assumptions):
+ if query(self.expr.exp, 'odd', self.assumptions):
+ return False
+ if query(self.expr.exp, 'even', self.assumptions):
+ return False
+ elif query(self.expr.base, 'positive'):
+ if query(self.expr.exp, 'real'):
+ return True
+ return None
+
+ def abs(self):
+ return True
+
+ def exp(self):
+ if query(self.expr.args[0], 'real', self.assumptions):
+ return True
+
+ def NaN(self):
+ return None
+
+class QueryNonPositiveHandler(CommonHandler):
+ facts = ['nonpositive == real & !positive']
+
+ def __init__(self, expr, key, assumptions=None):
+ CommonHandler.__init__(self, expr, key, assumptions)
+ if len(expr.atoms(Symbol)) == 0 and query(expr, 'comparable'):
+ # if there are no symbols just evalf
+ self.resolution = expr.evalf() <= 0
diff --git a/sympy/query/handlers/sets.py b/sympy/query/handlers/sets.py
new file mode 100644
index 0000000..1cf6099
--- /dev/null
+++ b/sympy/query/handlers/sets.py
@@ -0,0 +1,239 @@
+from sympy.query import query
+from sympy.query.handlers import CommonHandler
+
+class QueryIntegerHandler(CommonHandler):
+ facts = ['integer -> rational',
+ 'integer -> real',
+ 'integer -> complex']
+
+ def Add(self):
+ """
+ Integer + Integer -> Integer
+ NonInteger + Integer -> Noninteger
+ NonInteger + NonInteger -> ?
+ """
+ _output = True
+ for arg in self.expr.args:
+ _result = query(arg, self.key, self.assumptions)
+ if _result is None:
+ return
+ elif _result is False:
+ if _output:
+ _output = False
+ else:
+ return
+ else:
+ return _output
+
+ def Mul(self):
+ """
+ Integer*Integer -> Integer
+ Integer*Irrational -> NonInteger
+ Integer*Rational -> ?
+ """
+ _output = True
+ for arg in self.expr.args:
+ if not query(arg, self.key, self.assumptions):
+ if query(arg, 'irrational', self.assumptions):
+ if _output:
+ _output = False
+ else:
+ return
+ else:
+ return
+ else:
+ return _output
+
+ def Pow(self):
+ for arg in self.expr.args:
+ if not query(arg, self.key, self.assumptions):
+ return
+ else:
+ return True
+
+ def int(self):
+ return True
+
+ def Integer(self):
+ return True
+
+ def Rational(self):
+ # rationals with denominator one get
+ # evaluated to Integers
+ return False
+
+ def Real(self):
+ return int(self.expr) == self.expr
+
+ def Pi(self):
+ return False
+
+ def Exp1(self):
+ return False
+
+ def Infinity(self):
+ return None
+
+ def NegativeInfinity(self):
+ return None
+
+ def ImaginaryUnit(self):
+ return False
+
+ def NaN(self):
+ return None
+
+
+class QueryNonIntegerHandler(CommonHandler):
+ facts = ['noninteger == real & !integer']
+
+ def Pi(self):
+ return True
+
+ def Exp1(self):
+ return True
+
+
+class QueryRationalHandler(CommonHandler):
+ facts = ['rational -> real']
+
+ def Add(self):
+ """
+ Rational + Rational -> Integer
+ Irrational + Rational -> Rational
+ Irrational + Irrational -> ?
+ """
+ _output = True
+ for arg in self.expr.args:
+ _result = query(arg, self.key, self.assumptions)
+ if _result is None:
+ return
+ elif _result is False:
+ if _output:
+ _output = False
+ else:
+ return
+ else:
+ return _output
+
+ def Mul(self):
+ """
+ Rational*Rational -> Rational
+ Rational*Irrational -> Irrational
+ Irrational*Irrational -> ?
+ """
+ _output = True
+ for arg in self.expr.args:
+ _result = query(arg, self.key, self.assumptions)
+ if _result is None:
+ return
+ elif _result is False:
+ if _output:
+ _output = False
+ else:
+ return
+ else:
+ return _output
+
+ def Rational(self):
+ return True
+
+ def Real(self):
+ # it's finite-precission
+ return True
+
+ def Infinity(self):
+ return None
+
+ def NegativeInfinity(self):
+ return None
+
+ def Pi(self):
+ return False
+
+ def Exp1(self):
+ return False
+
+ def NaN(self):
+ return None
+
+class QueryIrrationalHandler(CommonHandler):
+ facts = ['irrational == real & !rational']
+
+class QueryRealHandler(CommonHandler):
+ facts = ['real -> complex']
+
+ def Add(self):
+ for arg in self.expr.args:
+ if query(arg, self.key, self.assumptions) != True:
+ break
+ else:
+ return True
+
+ def Mul(self):
+ for arg in self.expr.args:
+ if query(arg, self.key, self.assumptions) != True:
+ break
+ else:
+ return True
+
+ def Rational(self):
+ return True
+
+ def Real(self):
+ return True
+
+ def Pi(self):
+ return True
+
+ def Exp1(self):
+ return True
+
+ def abs(self):
+ return True
+
+ def ImaginaryUnit(self):
+ return False
+
+ def NaN(self):
+ return None
+
+ def sin(self):
+ if query(self.expr.args[0], 'real', self.assumptions):
+ return True
+ cos = sin
+
+class QueryComplexHandler(CommonHandler):
+
+ def Add(self):
+ for arg in self.expr.args:
+ if not query(arg, self.key, self.assumptions):
+ return None
+ else:
+ return True
+
+ Mul = Add
+
+ def Number(self):
+ return True
+
+ def NumberSymbol(self):
+ return True
+
+ def abs(self):
+ return True
+
+ def ImaginaryUnit(self):
+ return True
+
+ def NaN(self):
+ return None
+
+class QueryImaginaryHandler(CommonHandler):
+ facts = ['imaginary == complex & !real']
+
+ def Number(self):
+ return not (self.expr.as_real_imag()[1] == 0)
+
+ def NaN(self):
+ return None
diff --git a/sympy/query/tests/test_query.py b/sympy/query/tests/test_query.py
new file mode 100644
index 0000000..6fcb210
--- /dev/null
+++ b/sympy/query/tests/test_query.py
@@ -0,0 +1,594 @@
+from sympy.core import Symbol, S, Rational, Integer, Assume
+from sympy.query import query
+
+def test_symbol_complex():
+ x = Symbol('x')
+ assert query(x, 'complex', Assume(x, 'real')) == True
+
+
+def test_integer():
+ z = 1
+ assert query(z, 'commutative') == True
+ assert query(z, 'integer') == True
+ assert query(z, 'rational') == True
+ assert query(z, 'real') == True
+ assert query(z, 'complex') == True
+ assert query(z, 'noninteger') == False
+ assert query(z, 'irrational') == False
+ assert query(z, 'imaginary') == False
+ assert query(z, 'positive') == True
+ assert query(z, 'negative') == False
+ assert query(z, 'nonpositive') == False
+ assert query(z, 'nonnegative') == True
+ assert query(z, 'even') == False
+ assert query(z, 'odd') == True
+ assert query(z, 'bounded') == True
+ assert query(z, 'unbounded') == False
+ assert query(z, 'finite') == True
+ assert query(z, 'infinitesimal') == False
+ assert query(z, 'comparable') == True
+ assert query(z, 'prime') == False
+ assert query(z, 'composite') == True
+
+def test_float():
+ z = 1.0
+ assert query(z, 'commutative') == True
+ assert query(z, 'integer') == True
+ assert query(z, 'rational') == True
+ assert query(z, 'real') == True
+ assert query(z, 'complex') == True
+ assert query(z, 'noninteger') == False
+ assert query(z, 'irrational') == False
+ assert query(z, 'imaginary') == False
+ assert query(z, 'positive') == True
+ assert query(z, 'negative') == False
+ assert query(z, 'nonpositive') == False
+ assert query(z, 'nonnegative') == True
+ assert query(z, 'even') == False
+ assert query(z, 'odd') == True
+ assert query(z, 'bounded') == True
+ assert query(z, 'unbounded') == False
+ assert query(z, 'finite') == True
+ assert query(z, 'infinitesimal') == False
+ assert query(z, 'comparable') == True
+ assert query(z, 'prime') == False
+ assert query(z, 'composite') == True
+
+def test_zero():
+ z = Integer(0)
+ assert query(z, 'zero') == True
+ assert query(z, 'commutative') == True
+ assert query(z, 'integer') == True
+ assert query(z, 'rational') == True
+ assert query(z, 'real') == True
+ assert query(z, 'complex') == True
+ assert query(z, 'imaginary') == False
+ assert query(z, 'positive') == False
+ assert query(z, 'negative') == False
+ assert query(z, 'even') == True
+ assert query(z, 'odd') == False
+ assert query(z, 'bounded') == True
+ assert query(z, 'finite') == True
+ assert query(z, 'infinitesimal') == True
+ assert query(z, 'comparable') == True
+ assert query(z, 'prime') == False
+ assert query(z, 'composite') == False
+
+def test_one():
+ z = Integer(1)
+ assert query(z, 'commutative') == True
+ assert query(z, 'integer') == True
+ assert query(z, 'rational') == True
+ assert query(z, 'real') == True
+ assert query(z, 'complex') == True
+ assert query(z, 'noninteger') == False
+ assert query(z, 'irrational') == False
+ assert query(z, 'imaginary') == False
+ assert query(z, 'positive') == True
+ assert query(z, 'negative') == False
+ assert query(z, 'nonpositive') == False
+ assert query(z, 'nonnegative') == True
+ assert query(z, 'even') == False
+ assert query(z, 'odd') == True
+ assert query(z, 'bounded') == True
+ assert query(z, 'unbounded') == False
+ assert query(z, 'finite') == True
+ assert query(z, 'infinitesimal') == False
+ assert query(z, 'comparable') == True
+ assert query(z, 'prime') == False
+ assert query(z, 'composite') == True
+
+def test_negativeone():
+ z = Integer(-1)
+ assert query(z, 'commutative') == True
+ assert query(z, 'integer') == True
+ assert query(z, 'rational') == True
+ assert query(z, 'real') == True
+ assert query(z, 'complex') == True
+ assert query(z, 'noninteger') == False
+ assert query(z, 'irrational') == False
+ assert query(z, 'imaginary') == False
+ assert query(z, 'positive') == False
+ assert query(z, 'negative') == True
+ assert query(z, 'nonpositive') == True
+ assert query(z, 'nonnegative') == False
+ assert query(z, 'even') == False
+ assert query(z, 'odd') == True
+ assert query(z, 'bounded') == True
+ assert query(z, 'unbounded') == False
+ assert query(z, 'finite') == True
+ assert query(z, 'infinitesimal') == False
+ assert query(z, 'comparable') == True
+ assert query(z, 'prime') == False
+ assert query(z, 'composite') == False
+
+def test_infinity():
+ oo = S.Infinity
+
+ assert query(oo, 'commutative') == True
+ assert query(oo, 'integer') == None
+ assert query(oo, 'rational') == None
+ assert query(oo, 'real') == True
+ assert query(oo, 'complex') == True
+ assert query(oo, 'noninteger') == None
+ assert query(oo, 'irrational') == None
+ assert query(oo, 'imaginary') == False
+ assert query(oo, 'positive') == True
+ assert query(oo, 'negative') == False
+ assert query(oo, 'nonpositive') == False
+ assert query(oo, 'nonnegative') == True
+ assert query(oo, 'even') == None
+ assert query(oo, 'odd') == None
+ assert query(oo, 'bounded') == False
+ assert query(oo, 'unbounded') == True
+ assert query(oo, 'finite') == False
+ assert query(oo, 'infinitesimal') == False
+ assert query(oo, 'comparable') == True
+ assert query(oo, 'prime') == None
+ assert query(oo, 'composite') == None
+
+def test_neg_infinity():
+ mm = S.NegativeInfinity
+
+ assert query(mm, 'commutative') == True
+ assert query(mm, 'integer') == None
+ assert query(mm, 'rational') == None
+ assert query(mm, 'real') == True
+ assert query(mm, 'complex') == True
+ assert query(mm, 'noninteger') == None
+ assert query(mm, 'irrational') == None
+ assert query(mm, 'imaginary') == False
+ assert query(mm, 'positive') == False
+ assert query(mm, 'negative') == True
+ assert query(mm, 'nonpositive') == True
+ assert query(mm, 'nonnegative') == False
+ assert query(mm, 'even') == None
+ assert query(mm, 'odd') == None
+ assert query(mm, 'bounded') == False
+ assert query(mm, 'unbounded') == True
+ assert query(mm, 'finite') == False
+ assert query(mm, 'infinitesimal') == False
+ assert query(mm, 'comparable') == True
+ assert query(mm, 'prime') == False
+ assert query(mm, 'composite') == False
+
+def test_nan():
+ nan = S.NaN
+
+ assert query(nan, 'commutative') == True
+ assert query(nan, 'integer') == None
+ assert query(nan, 'rational') == None
+ assert query(nan, 'real') == None
+ assert query(nan, 'complex') == None
+ assert query(nan, 'noninteger') == None
+ assert query(nan, 'irrational') == None
+ assert query(nan, 'imaginary') == None
+ assert query(nan, 'positive') == None
+ assert query(nan, 'negative') == None
+ assert query(nan, 'nonpositive') == None
+ assert query(nan, 'zero') == None
+ assert query(nan, 'nonnegative') == None
+ assert query(nan, 'even') == None
+ assert query(nan, 'odd') == None
+ assert query(nan, 'bounded') == None
+ assert query(nan, 'unbounded') == None
+ assert query(nan, 'finite') == None
+ assert query(nan, 'infinitesimal') == None
+ assert query(nan, 'comparable') == False
+ assert query(nan, 'prime') == None
+ assert query(nan, 'composite') == None
+
+def test_pos_rational():
+ r = Rational(3,4)
+ assert query(r, 'commutative') == True
+ assert query(r, 'integer') == False
+ assert query(r, 'rational') == True
+ assert query(r, 'real') == True
+ assert query(r, 'complex') == True
+ assert query(r, 'noninteger') == True
+ assert query(r, 'irrational') == False
+ assert query(r, 'imaginary') == False
+ assert query(r, 'positive') == True
+ assert query(r, 'negative') == False
+ assert query(r, 'nonpositive') == False
+ assert query(r, 'nonnegative') == True
+ assert query(r, 'even') == False
+ assert query(r, 'odd') == False
+ assert query(r, 'bounded') == True
+ assert query(r, 'unbounded') == False
+ assert query(r, 'finite') == True
+ assert query(r, 'infinitesimal') == False
+ assert query(r, 'comparable') == True
+ assert query(r, 'prime') == False
+ assert query(r, 'composite') == False
+
+ r = Rational(1,4)
+ assert query(r, 'nonpositive') == False
+ assert query(r, 'positive') == True
+ assert query(r, 'negative') == False
+ assert query(r, 'nonnegative') == True
+
+ r = Rational(5,4)
+ assert query(r, 'negative') == False
+ assert query(r, 'positive') == True
+ assert query(r, 'nonpositive') == False
+ assert query(r, 'nonnegative') == True
+
+ r = Rational(5,3)
+ assert query(r, 'nonnegative') == True
+ assert query(r, 'positive') == True
+ assert query(r, 'negative') == False
+ assert query(r, 'nonpositive') == False
+
+def test_neg_rational():
+ r = Rational(-3,4)
+ assert query(r, 'positive') == False
+ assert query(r, 'nonpositive') == True
+ assert query(r, 'negative') == True
+ assert query(r, 'nonnegative') == False
+
+ r = Rational(-1,4)
+ assert query(r, 'nonpositive') == True
+ assert query(r, 'positive') == False
+ assert query(r, 'negative') == True
+ assert query(r, 'nonnegative') == False
+
+ r = Rational(-5,4)
+ assert query(r, 'negative') == True
+ assert query(r, 'positive') == False
+ assert query(r, 'nonpositive') == True
+ assert query(r, 'nonnegative') == False
+
+ r = Rational(-5,3)
+ assert query(r, 'nonnegative') == False
+ assert query(r, 'positive') == False
+ assert query(r, 'negative') == True
+ assert query(r, 'nonpositive') == True
+
+def test_pi():
+ z = S.Pi
+ assert query(z, 'commutative') == True
+ assert query(z, 'integer') == False
+ assert query(z, 'rational') == False
+ assert query(z, 'real') == True
+ assert query(z, 'complex') == True
+ assert query(z, 'noninteger') == True
+ assert query(z, 'irrational') == True
+ assert query(z, 'imaginary') == False
+ assert query(z, 'positive') == True
+ assert query(z, 'negative') == False
+ assert query(z, 'nonpositive') == False
+ assert query(z, 'nonnegative') == True
+ assert query(z, 'even') == False
+ assert query(z, 'odd') == False
+ assert query(z, 'bounded') == True
+ assert query(z, 'unbounded') == False
+ assert query(z, 'finite') == True
+ assert query(z, 'infinitesimal') == False
+ assert query(z, 'comparable') == True
+ assert query(z, 'prime') == False
+ assert query(z, 'composite') == False
+
+def test_pi_add():
+ z = S.Pi + 1
+ assert query(z, 'commutative') == True
+ assert query(z, 'integer') == False
+ assert query(z, 'rational') == False
+ assert query(z, 'real') == True
+ assert query(z, 'complex') == True
+ assert query(z, 'noninteger') == True
+ assert query(z, 'irrational') == True
+ assert query(z, 'imaginary') == False
+ assert query(z, 'positive') == True
+ assert query(z, 'negative') == False
+ assert query(z, 'nonpositive') == False
+ assert query(z, 'nonnegative') == True
+ assert query(z, 'even') == False
+ assert query(z, 'odd') == False
+ assert query(z, 'bounded') == True
+ assert query(z, 'unbounded') == False
+ assert query(z, 'finite') == True
+ assert query(z, 'infinitesimal') == False
+ assert query(z, 'comparable') == True
+ assert query(z, 'prime') == False
+ assert query(z, 'composite') == False
+
+def test_pi_mul():
+ z = 2*S.Pi
+ assert query(z, 'commutative') == True
+ assert query(z, 'integer') == False
+ assert query(z, 'rational') == False
+ assert query(z, 'real') == True
+ assert query(z, 'complex') == True
+ assert query(z, 'noninteger') == True
+ assert query(z, 'irrational') == True
+ assert query(z, 'imaginary') == False
+ assert query(z, 'positive') == True
+ assert query(z, 'negative') == False
+ assert query(z, 'nonpositive') == False
+ assert query(z, 'nonnegative') == True
+ assert query(z, 'even') == False
+ assert query(z, 'odd') == False
+ assert query(z, 'bounded') == True
+ assert query(z, 'unbounded') == False
+ assert query(z, 'finite') == True
+ assert query(z, 'infinitesimal') == False
+ assert query(z, 'comparable') == True
+ assert query(z, 'prime') == False
+ assert query(z, 'composite') == False
+
+def test_E():
+ z = S.Exp1
+ assert query(z, 'commutative') == True
+ assert query(z, 'integer') == False
+ assert query(z, 'rational') == False
+ assert query(z, 'real') == True
+ assert query(z, 'complex') == True
+ assert query(z, 'noninteger') == True
+ assert query(z, 'irrational') == True
+ assert query(z, 'imaginary') == False
+ assert query(z, 'positive') == True
+ assert query(z, 'negative') == False
+ assert query(z, 'nonpositive') == False
+ assert query(z, 'nonnegative') == True
+ assert query(z, 'even') == False
+ assert query(z, 'odd') == False
+ assert query(z, 'bounded') == True
+ assert query(z, 'unbounded') == False
+ assert query(z, 'finite') == True
+ assert query(z, 'infinitesimal') == False
+ assert query(z, 'comparable') == True
+ assert query(z, 'prime') == False
+ assert query(z, 'composite') == False
+
+def test_I():
+ z = S.ImaginaryUnit
+ assert query(z, 'comparable') == False
+ assert query(z, 'commutative') == True
+ assert query(z, 'integer') == False
+ assert query(z, 'rational') == False
+ assert query(z, 'real') == False
+ assert query(z, 'complex') == True
+ assert query(z, 'noninteger') == False
+ assert query(z, 'irrational') == False
+ assert query(z, 'imaginary') == True
+ assert query(z, 'positive') == False
+ assert query(z, 'negative') == False
+ assert query(z, 'nonpositive') == False
+ assert query(z, 'nonnegative') == False
+ assert query(z, 'even') == False
+ assert query(z, 'odd') == False
+ assert query(z, 'bounded') == True
+ assert query(z, 'unbounded') == False
+ assert query(z, 'finite') == True
+ assert query(z, 'infinitesimal') == False
+ assert query(z, 'prime') == False
+ assert query(z, 'composite') == False
+
+def test_symbol_zero():
+ x = Symbol('x')
+ assumptions = Assume(x, 'zero')
+ assert query(x, 'zero', assumptions) == True
+ assert query(x, 'nonpositive', assumptions) == True
+ assert query(x, 'negative', assumptions) == False
+ assert query(x, 'nonnegative', assumptions) == True
+ assert query(x, 'zero', assumptions) == True
+ assert query(x, 'nonzero', assumptions) == False
+
+def test_symbol_positive():
+ x = Symbol('x')
+ assumptions = Assume(x, 'positive')
+ assert query( x, 'positive', assumptions) == True
+ assert query( x, 'nonpositive', assumptions) == False
+ assert query( x, 'negative', assumptions) == False
+ assert query( x, 'nonnegative', assumptions) == True
+ assert query( x, 'zero', assumptions) == False
+ assert query( x, 'nonzero', assumptions) == True
+
+ #test -x
+ assert query(-x, 'positive', assumptions) == False
+ assert query(-x, 'nonpositive', assumptions) == True
+ assert query(-x, 'negative', assumptions) == True
+ assert query(-x, 'nonnegative', assumptions) == False
+ assert query(-x, 'zero', assumptions) == False
+
+def test_symbol_positive_multiple():
+ #test with multiple symbols
+ x, y, z, w = Symbol('x'), Symbol('y'), Symbol('z'), Symbol('w')
+ assumptions = [ Assume(x, 'positive'),
+ Assume(y, 'negative'),
+ Assume(z, 'negative'),
+ Assume(w, 'positive')]
+ assert query( x*y*z, 'positive') == None
+ assert query( x*y*z, 'positive', assumptions) == True
+ assert query(-x*y*z, 'positive', assumptions) == False
+
+ assert query( y+z, 'negative', assumptions) == True
+ assert query( y+z, 'nonpositive', assumptions) == True
+ assert query( x+y, 'positive', assumptions) == None
+ assert query( x+y, 'negative', assumptions) == None
+
+def test_symbol_nonpositive():
+ x = Symbol('x')
+ assumptions = Assume(x, 'nonpositive')
+ assert query(x, 'nonpositive') == None
+ assert query(x, 'positive', assumptions) == False
+ assert query(x, 'nonpositive', assumptions) == True
+ assert query(x, 'negative', assumptions) == None
+ assert query(x, 'nonnegative', assumptions) == None
+ assert query(x, 'zero', assumptions) == None
+ assert query(x, 'nonzero', assumptions) == None
+
+def test_neg__symbol_nonpositive():
+ x = Symbol('x')
+ assumptions = Assume(x, 'nonpositive')
+ assert query(-x, 'positive', assumptions) == None
+ assert query(-x, 'nonpositive', assumptions) == None
+ assert query(-x, 'negative', assumptions) == False
+ assert query(-x, 'nonnegative', assumptions) == True
+ assert query(-x, 'zero', assumptions) == None
+ assert query(-x, 'nonzero', assumptions) == None
+
+def test_symbol_negative():
+ x = Symbol('x')
+ y = Symbol('y')
+ assert query(x, 'negative', Assume(x, 'negative')) == True
+ assert query(x, 'negative', Assume(x, 'positive')) == False
+ assert query(x+y, 'negative', [Assume(x, 'negative'), Assume(y, 'negative')]) == True
+
+def test_prime_symbol():
+ x = Symbol('x')
+ assumptions = Assume(x, 'prime')
+ assert query(x, 'prime', assumptions) == True
+ assert query(x, 'integer', assumptions) == True
+ assert query(x, 'positive', assumptions) == True
+ assert query(x, 'negative', assumptions) == False
+ assert query(x, 'nonpositive', assumptions) == False
+ assert query(x, 'nonnegative', assumptions) == True
+
+def test_not_prime_symbol():
+ # assumptions with False bool_key are not implemented
+ x = Symbol('x')
+ assumptions = Assume(x, 'prime', False)
+ assert query(x, 'prime', assumptions) == False
+ assert query(x, 'integer', assumptions) == None
+ assert query(x, 'positive', assumptions) == None
+ assert query(x, 'negative', assumptions) == None
+ assert query(x, 'nonpositive', assumptions) == None
+ assert query(x, 'nonnegative', assumptions) == None
+
+def test_symbol_integer():
+ x = Symbol('x')
+ assumptions = Assume(x, 'integer')
+ assert query(x, 'integer', assumptions) == True
+ assert query(x, 'real', assumptions) == True
+ assert query(x, 'complex', assumptions) == True
+ assert query(2*x, 'integer', assumptions) == True
+ assert query(x**2, 'integer', assumptions) == True
+ assert query(x**x, 'integer', assumptions) == True
+
+def test_symbol_integer_nonnegative():
+ x = Symbol('x')
+ assumptions = [Assume(x, 'integer'), Assume(x, 'nonnegative')]
+ assert query(x, 'integer', assumptions) == True
+ assert query(x, 'nonnegative', assumptions) == True
+ assert query(x, 'negative', assumptions) == False
+ assert query(x, 'positive', assumptions) == None
+
+def test_symbol_integer_nonpositive():
+ x = Symbol('x')
+ assumptions = [Assume(x, 'integer'), Assume(x, 'nonpositive')]
+ assert query(x, 'integer', assumptions) == True
+ assert query(x, 'nonpositive', assumptions) == True
+ assert query(x, 'positive', assumptions) == False
+ assert query(x, 'negative', assumptions) == None
+
+def test_symbol_odd():
+ x = Symbol('x')
+ assumptions = Assume(x, 'odd')
+ assert query(x, 'odd', assumptions) == True
+ assert query(x, 'even', assumptions) == False
+ assert query(x, 'integer', assumptions) == True
+
+def test_symbol_even():
+ x = Symbol('x')
+ assumptions = Assume(x, 'even')
+ assert query(x, 'even', assumptions) == True
+ assert query(x, 'odd', assumptions) == False
+ assert query(x, 'integer', assumptions) == True
+
+def test_symbol_integer_nonnegative():
+ x = Symbol('x')
+ assumptions = [Assume(x, 'integer'), Assume(x, 'nonnegative')]
+ assert query(x, 'integer', assumptions) == True
+ assert query(x, 'nonnegative', assumptions) == True
+
+def test_symbol_integer_nonpositive():
+ x = Symbol('x')
+ assumptions = [Assume(x, 'integer'), Assume(x, 'nonpositive')]
+ assert query(x, 'integer', assumptions) == True
+ assert query(x, 'nonpositive', assumptions) == True
+
+def test_symbol_odd_False():
+ # XXX x.is_even currently will be True
+ x = Symbol('x')
+ assumptions = Assume(x, 'odd', False)
+ assert query(x, 'odd', assumptions) == False
+ assert query(x, 'even', assumptions) == None
+ assert query(x, 'integer', assumptions) == None
+
+def test_symbol_symbol_fail2():
+ # XXX x.is_odd currently will be True
+ x = Symbol('x')
+ assumptions = Assume(x, 'even', False)
+ assert query(x, 'even', assumptions) == False
+ assert query(x, 'odd', assumptions) == None
+ assert query(x, 'integer', assumptions) == None
+
+def test_symbol_real_False():
+ a = Symbol('a')
+
+ assumptions = Assume(a, 'real', False)
+ assert query(a, 'real', assumptions) == False
+ assert query(a, 'integer', assumptions) == False
+ assert query(a, 'negative', assumptions) == False
+ assert query(a, 'positive', assumptions) == False
+ assert query(a, 'nonnegative', assumptions) == False
+ assert query(a, 'nonpositive', assumptions) == False
+ assert query(a, 'zero', assumptions) == False
+
+def test_abs():
+ x = Symbol('x')
+ assert query(abs(x), 'positive') == True
+ assert query(abs(x), 'positive', Assume(x, 'negative')) == True
+ assert query(abs(x), 'negative') == False
+ assert query(abs(x), 'real') == True
+ assert query(abs(x), 'complex') == True
+ assert query(abs(x), 'odd') == None
+
+def test_trigonometric():
+ from sympy.functions import sin, cos
+ x = Symbol('x')
+ assert query(sin(x), 'real') == None
+ assert query(cos(x), 'real') == None
+ assert query(sin(x), 'real', Assume(x, 'real')) == True
+ assert query(cos(x), 'real', Assume(x, 'real')) == True
+
+def test_x_exp():
+ from sympy.functions import exp
+ x = Symbol('x')
+ assert query(exp(x), 'positive', Assume(x, 'real')) == True
+ assert query(x + exp(x), 'positive', Assume(x, 'real')) == None
+
+def test_hash_vs_eq():
+ """catch: different hash for equal objects"""
+ a = 1 + S.Pi # important: do not fold it into a Number instance
+ ha= hash(a) # it should be Add/Mul/... to trigger the bug
+ b = a.expand(trig=True)
+ hb= hash(b)
+
+ assert query(a, 'positive') == True # this uses .evalf() and deduces it is positive
+ # be sure that hash stayed the same
+ assert a == b
+ assert ha == hash(a)
+ assert ha== hb
--
1.6.1.2
diff --git a/doc/src/modules.txt b/doc/src/modules.txt
index 0232b4d..d13a655 100644
--- a/doc/src/modules.txt
+++ b/doc/src/modules.txt
@@ -27,6 +27,7 @@ access any SymPy module, or use this contens:
modules/geometry.txt
modules/galgebra/GA/GAsympy.txt
modules/statistics.txt
+ modules/query.txt
modules/concrete.txt
modules/solvers.txt
diff --git a/doc/src/modules/query.txt b/doc/src/modules/query.txt
new file mode 100644
index 0000000..df9aaa1
--- /dev/null
+++ b/doc/src/modules/query.txt
@@ -0,0 +1,85 @@
+Query module
+============
+
+This module is responsible for inferring properties from objects
+
+Supported queries
+-----------------
+
+ - *bounded*
+ - *commutative*
+ - *comparable*
+ - *complex*
+ - *composite*
+ - *even*
+ - *finite*
+ - *imaginary*
+ - *infinitesimal*
+ - *integer*
+ - *irrational*
+ - *rational*
+ - *nonnegative*
+ - *noninteger*
+ - *nonpositive*
+ - *nonzero*
+ - *negative*
+ - *positive*
+ - *prime*
+ - *real*
+ - *odd*
+ - *unbounded*
+ - *zero*
+
+Examples
+--------
+ >>> from sympy import Symbol, Assume, query
+ >>> query(2, 'integer')
+ True
+ >>> query(2, 'rational')
+ True
+ >>> query(1.5, 'integer')
+ Flase
+ >>> x = Symbol('x')
+ >>> query(x, 'integer', Assume(x, 'even'))
+ True
+
+You can find more examples in the in the form of test under directory sympy/query/tests/
+
+
+
+Design
+------
+Each time query is called, the appropriate Handler for the current key is called. This is
+always a subclass of sympy.query.QueryHandler. It's classmethods have the name's of the classes
+it supports. For example,
+
+For example, a (simplified) QueryHandler for the query 'positive' would look like this::
+
+ class QueryPositiveHandler(CommonHandler):
+
+ def Mul(self):
+ # return True if all argument's in self.expr.args are positive
+ ...
+
+ def Add(self):
+ for arg in self.expr.args:
+ if query(arg, self.key, self.assumptions) != True:
+ break
+ else:
+ # if all argument's are positive
+ return True
+ ...
+
+So that the .Mul() method would be called when self.expr is an instance of Mul, the Add method
+would be called when self.expr is an instance of Add and so on.
+
+
+Extensibility
+-------------
+You can define your own queries by subclassing sympy.query.QueryHandler and passing
+to query a a qdict argument consting of the query's string and the appropriate
+handler. For example::
+
+ query(expr, 'my_query', qdict={'my_query': MyQueryHandler})
+
+would call MyQueryHandler to resolve the query 'my_query'
\ No newline at end of file
--
1.6.1.2
Maybe add an example:
>>> variations([1,2,3], 2, True)
[[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], \
[3,1], [3,2], [3,3]]
> +
> + Â Â variations(seq, n, False) will return all the variations of the list of
> + Â Â Â Â size n without repetitions
> + Â Â """
Maybe add an example
>>> variations([1,2,3], 2)
[[1, 2], [1, 3], [2, 1], [2, 3], [3, 1], [3, 2]]
Otherwise +1
Ondrej
Could there be tests for both of the functions above? It's not exactly
clear what they are doing from the docstrings. Also code like this is
really hackish:
getattr(__import__(mod_name, {}, {}, ['']), func_name)
If this is needed, I think it deserves a thorough docstrings and
comments why this is needed and what it does.
Ondrej
I want to play with this a bit more in the coming days in your branch.
Probably more docstrings could be written, if I find time, I'll write
some and send a patch.
Ondrej
On Mon, Mar 16, 2009 at 8:13 AM, Fabian Seoane <fab...@fseoane.net> wrote:
>
yeah, I know it looks hackish, but it is copied from a similar utility
in the django codebase, so i'm
quite sure that there isn't a better way of doing it
It sure lacks docstrings and tests, so I'll send a reworked patch or
this and the other modules soon.
Thanks for the review,