You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
5.4 KiB
168 lines
5.4 KiB
5 months ago
|
"""
|
||
|
Functions and wrapper object to call assumption property and predicate
|
||
|
query with same syntax.
|
||
|
|
||
|
In SymPy, there are two assumption systems. Old assumption system is
|
||
|
defined in sympy/core/assumptions, and it can be accessed by attribute
|
||
|
such as ``x.is_even``. New assumption system is defined in
|
||
|
sympy/assumptions, and it can be accessed by predicates such as
|
||
|
``Q.even(x)``.
|
||
|
|
||
|
Old assumption is fast, while new assumptions can freely take local facts.
|
||
|
In general, old assumption is used in evaluation method and new assumption
|
||
|
is used in refinement method.
|
||
|
|
||
|
In most cases, both evaluation and refinement follow the same process, and
|
||
|
the only difference is which assumption system is used. This module provides
|
||
|
``is_[...]()`` functions and ``AssumptionsWrapper()`` class which allows
|
||
|
using two systems with same syntax so that parallel code implementation can be
|
||
|
avoided.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
For multiple use, use ``AssumptionsWrapper()``.
|
||
|
|
||
|
>>> from sympy import Q, Symbol
|
||
|
>>> from sympy.assumptions.wrapper import AssumptionsWrapper
|
||
|
>>> x = Symbol('x')
|
||
|
>>> _x = AssumptionsWrapper(x, Q.even(x))
|
||
|
>>> _x.is_integer
|
||
|
True
|
||
|
>>> _x.is_odd
|
||
|
False
|
||
|
|
||
|
For single use, use ``is_[...]()`` functions.
|
||
|
|
||
|
>>> from sympy.assumptions.wrapper import is_infinite
|
||
|
>>> a = Symbol('a')
|
||
|
>>> print(is_infinite(a))
|
||
|
None
|
||
|
>>> is_infinite(a, Q.finite(a))
|
||
|
False
|
||
|
|
||
|
"""
|
||
|
|
||
|
from sympy.assumptions import ask, Q
|
||
|
from sympy.core.basic import Basic
|
||
|
from sympy.core.sympify import _sympify
|
||
|
|
||
|
|
||
|
def make_eval_method(fact):
|
||
|
def getit(self):
|
||
|
try:
|
||
|
pred = getattr(Q, fact)
|
||
|
ret = ask(pred(self.expr), self.assumptions)
|
||
|
return ret
|
||
|
except AttributeError:
|
||
|
return None
|
||
|
return getit
|
||
|
|
||
|
|
||
|
# we subclass Basic to use the fact deduction and caching
|
||
|
class AssumptionsWrapper(Basic):
|
||
|
"""
|
||
|
Wrapper over ``Basic`` instances to call predicate query by
|
||
|
``.is_[...]`` property
|
||
|
|
||
|
Parameters
|
||
|
==========
|
||
|
|
||
|
expr : Basic
|
||
|
|
||
|
assumptions : Boolean, optional
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import Q, Symbol
|
||
|
>>> from sympy.assumptions.wrapper import AssumptionsWrapper
|
||
|
>>> x = Symbol('x', even=True)
|
||
|
>>> AssumptionsWrapper(x).is_integer
|
||
|
True
|
||
|
>>> y = Symbol('y')
|
||
|
>>> AssumptionsWrapper(y, Q.even(y)).is_integer
|
||
|
True
|
||
|
|
||
|
With ``AssumptionsWrapper``, both evaluation and refinement can be supported
|
||
|
by single implementation.
|
||
|
|
||
|
>>> from sympy import Function
|
||
|
>>> class MyAbs(Function):
|
||
|
... @classmethod
|
||
|
... def eval(cls, x, assumptions=True):
|
||
|
... _x = AssumptionsWrapper(x, assumptions)
|
||
|
... if _x.is_nonnegative:
|
||
|
... return x
|
||
|
... if _x.is_negative:
|
||
|
... return -x
|
||
|
... def _eval_refine(self, assumptions):
|
||
|
... return MyAbs.eval(self.args[0], assumptions)
|
||
|
>>> MyAbs(x)
|
||
|
MyAbs(x)
|
||
|
>>> MyAbs(x).refine(Q.positive(x))
|
||
|
x
|
||
|
>>> MyAbs(Symbol('y', negative=True))
|
||
|
-y
|
||
|
|
||
|
"""
|
||
|
def __new__(cls, expr, assumptions=None):
|
||
|
if assumptions is None:
|
||
|
return expr
|
||
|
obj = super().__new__(cls, expr, _sympify(assumptions))
|
||
|
obj.expr = expr
|
||
|
obj.assumptions = assumptions
|
||
|
return obj
|
||
|
|
||
|
_eval_is_algebraic = make_eval_method("algebraic")
|
||
|
_eval_is_antihermitian = make_eval_method("antihermitian")
|
||
|
_eval_is_commutative = make_eval_method("commutative")
|
||
|
_eval_is_complex = make_eval_method("complex")
|
||
|
_eval_is_composite = make_eval_method("composite")
|
||
|
_eval_is_even = make_eval_method("even")
|
||
|
_eval_is_extended_negative = make_eval_method("extended_negative")
|
||
|
_eval_is_extended_nonnegative = make_eval_method("extended_nonnegative")
|
||
|
_eval_is_extended_nonpositive = make_eval_method("extended_nonpositive")
|
||
|
_eval_is_extended_nonzero = make_eval_method("extended_nonzero")
|
||
|
_eval_is_extended_positive = make_eval_method("extended_positive")
|
||
|
_eval_is_extended_real = make_eval_method("extended_real")
|
||
|
_eval_is_finite = make_eval_method("finite")
|
||
|
_eval_is_hermitian = make_eval_method("hermitian")
|
||
|
_eval_is_imaginary = make_eval_method("imaginary")
|
||
|
_eval_is_infinite = make_eval_method("infinite")
|
||
|
_eval_is_integer = make_eval_method("integer")
|
||
|
_eval_is_irrational = make_eval_method("irrational")
|
||
|
_eval_is_negative = make_eval_method("negative")
|
||
|
_eval_is_noninteger = make_eval_method("noninteger")
|
||
|
_eval_is_nonnegative = make_eval_method("nonnegative")
|
||
|
_eval_is_nonpositive = make_eval_method("nonpositive")
|
||
|
_eval_is_nonzero = make_eval_method("nonzero")
|
||
|
_eval_is_odd = make_eval_method("odd")
|
||
|
_eval_is_polar = make_eval_method("polar")
|
||
|
_eval_is_positive = make_eval_method("positive")
|
||
|
_eval_is_prime = make_eval_method("prime")
|
||
|
_eval_is_rational = make_eval_method("rational")
|
||
|
_eval_is_real = make_eval_method("real")
|
||
|
_eval_is_transcendental = make_eval_method("transcendental")
|
||
|
_eval_is_zero = make_eval_method("zero")
|
||
|
|
||
|
|
||
|
# one shot functions which are faster than AssumptionsWrapper
|
||
|
|
||
|
def is_infinite(obj, assumptions=None):
|
||
|
if assumptions is None:
|
||
|
return obj.is_infinite
|
||
|
return ask(Q.infinite(obj), assumptions)
|
||
|
|
||
|
|
||
|
def is_extended_real(obj, assumptions=None):
|
||
|
if assumptions is None:
|
||
|
return obj.is_extended_real
|
||
|
return ask(Q.extended_real(obj), assumptions)
|
||
|
|
||
|
|
||
|
def is_extended_nonnegative(obj, assumptions=None):
|
||
|
if assumptions is None:
|
||
|
return obj.is_extended_nonnegative
|
||
|
return ask(Q.extended_nonnegative(obj), assumptions)
|