437 lines
12 KiB
Python
437 lines
12 KiB
Python
"""
|
|
Handlers related to order relations: positive, negative, etc.
|
|
"""
|
|
|
|
from sympy.assumptions import Q, ask
|
|
from sympy.core import Add, Basic, Expr, Mul, Pow
|
|
from sympy.core.logic import fuzzy_not, fuzzy_and, fuzzy_or
|
|
from sympy.core.numbers import E, ImaginaryUnit, NaN, I, pi
|
|
from sympy.functions import Abs, acos, acot, asin, atan, exp, factorial, log
|
|
from sympy.matrices import Determinant, Trace
|
|
from sympy.matrices.expressions.matexpr import MatrixElement
|
|
|
|
from sympy.multipledispatch import MDNotImplementedError
|
|
|
|
from ..predicates.order import (NegativePredicate, NonNegativePredicate,
|
|
NonZeroPredicate, ZeroPredicate, NonPositivePredicate, PositivePredicate,
|
|
ExtendedNegativePredicate, ExtendedNonNegativePredicate,
|
|
ExtendedNonPositivePredicate, ExtendedNonZeroPredicate,
|
|
ExtendedPositivePredicate,)
|
|
|
|
|
|
# NegativePredicate
|
|
|
|
def _NegativePredicate_number(expr, assumptions):
|
|
r, i = expr.as_real_imag()
|
|
# If the imaginary part can symbolically be shown to be zero then
|
|
# we just evaluate the real part; otherwise we evaluate the imaginary
|
|
# part to see if it actually evaluates to zero and if it does then
|
|
# we make the comparison between the real part and zero.
|
|
if not i:
|
|
r = r.evalf(2)
|
|
if r._prec != 1:
|
|
return r < 0
|
|
else:
|
|
i = i.evalf(2)
|
|
if i._prec != 1:
|
|
if i != 0:
|
|
return False
|
|
r = r.evalf(2)
|
|
if r._prec != 1:
|
|
return r < 0
|
|
|
|
@NegativePredicate.register(Basic)
|
|
def _(expr, assumptions):
|
|
if expr.is_number:
|
|
return _NegativePredicate_number(expr, assumptions)
|
|
|
|
@NegativePredicate.register(Expr)
|
|
def _(expr, assumptions):
|
|
ret = expr.is_negative
|
|
if ret is None:
|
|
raise MDNotImplementedError
|
|
return ret
|
|
|
|
@NegativePredicate.register(Add)
|
|
def _(expr, assumptions):
|
|
"""
|
|
Positive + Positive -> Positive,
|
|
Negative + Negative -> Negative
|
|
"""
|
|
if expr.is_number:
|
|
return _NegativePredicate_number(expr, assumptions)
|
|
|
|
r = ask(Q.real(expr), assumptions)
|
|
if r is not True:
|
|
return r
|
|
|
|
nonpos = 0
|
|
for arg in expr.args:
|
|
if ask(Q.negative(arg), assumptions) is not True:
|
|
if ask(Q.positive(arg), assumptions) is False:
|
|
nonpos += 1
|
|
else:
|
|
break
|
|
else:
|
|
if nonpos < len(expr.args):
|
|
return True
|
|
|
|
@NegativePredicate.register(Mul)
|
|
def _(expr, assumptions):
|
|
if expr.is_number:
|
|
return _NegativePredicate_number(expr, assumptions)
|
|
result = None
|
|
for arg in expr.args:
|
|
if result is None:
|
|
result = False
|
|
if ask(Q.negative(arg), assumptions):
|
|
result = not result
|
|
elif ask(Q.positive(arg), assumptions):
|
|
pass
|
|
else:
|
|
return
|
|
return result
|
|
|
|
@NegativePredicate.register(Pow)
|
|
def _(expr, assumptions):
|
|
"""
|
|
Real ** Even -> NonNegative
|
|
Real ** Odd -> same_as_base
|
|
NonNegative ** Positive -> NonNegative
|
|
"""
|
|
if expr.base == E:
|
|
# Exponential is always positive:
|
|
if ask(Q.real(expr.exp), assumptions):
|
|
return False
|
|
return
|
|
|
|
if expr.is_number:
|
|
return _NegativePredicate_number(expr, assumptions)
|
|
if ask(Q.real(expr.base), assumptions):
|
|
if ask(Q.positive(expr.base), assumptions):
|
|
if ask(Q.real(expr.exp), assumptions):
|
|
return False
|
|
if ask(Q.even(expr.exp), assumptions):
|
|
return False
|
|
if ask(Q.odd(expr.exp), assumptions):
|
|
return ask(Q.negative(expr.base), assumptions)
|
|
|
|
@NegativePredicate.register_many(Abs, ImaginaryUnit)
|
|
def _(expr, assumptions):
|
|
return False
|
|
|
|
@NegativePredicate.register(exp)
|
|
def _(expr, assumptions):
|
|
if ask(Q.real(expr.exp), assumptions):
|
|
return False
|
|
raise MDNotImplementedError
|
|
|
|
|
|
# NonNegativePredicate
|
|
|
|
@NonNegativePredicate.register(Basic)
|
|
def _(expr, assumptions):
|
|
if expr.is_number:
|
|
notnegative = fuzzy_not(_NegativePredicate_number(expr, assumptions))
|
|
if notnegative:
|
|
return ask(Q.real(expr), assumptions)
|
|
else:
|
|
return notnegative
|
|
|
|
@NonNegativePredicate.register(Expr)
|
|
def _(expr, assumptions):
|
|
ret = expr.is_nonnegative
|
|
if ret is None:
|
|
raise MDNotImplementedError
|
|
return ret
|
|
|
|
|
|
# NonZeroPredicate
|
|
|
|
@NonZeroPredicate.register(Expr)
|
|
def _(expr, assumptions):
|
|
ret = expr.is_nonzero
|
|
if ret is None:
|
|
raise MDNotImplementedError
|
|
return ret
|
|
|
|
@NonZeroPredicate.register(Basic)
|
|
def _(expr, assumptions):
|
|
if ask(Q.real(expr)) is False:
|
|
return False
|
|
if expr.is_number:
|
|
# if there are no symbols just evalf
|
|
i = expr.evalf(2)
|
|
def nonz(i):
|
|
if i._prec != 1:
|
|
return i != 0
|
|
return fuzzy_or(nonz(i) for i in i.as_real_imag())
|
|
|
|
@NonZeroPredicate.register(Add)
|
|
def _(expr, assumptions):
|
|
if all(ask(Q.positive(x), assumptions) for x in expr.args) \
|
|
or all(ask(Q.negative(x), assumptions) for x in expr.args):
|
|
return True
|
|
|
|
@NonZeroPredicate.register(Mul)
|
|
def _(expr, assumptions):
|
|
for arg in expr.args:
|
|
result = ask(Q.nonzero(arg), assumptions)
|
|
if result:
|
|
continue
|
|
return result
|
|
return True
|
|
|
|
@NonZeroPredicate.register(Pow)
|
|
def _(expr, assumptions):
|
|
return ask(Q.nonzero(expr.base), assumptions)
|
|
|
|
@NonZeroPredicate.register(Abs)
|
|
def _(expr, assumptions):
|
|
return ask(Q.nonzero(expr.args[0]), assumptions)
|
|
|
|
@NonZeroPredicate.register(NaN)
|
|
def _(expr, assumptions):
|
|
return None
|
|
|
|
|
|
# ZeroPredicate
|
|
|
|
@ZeroPredicate.register(Expr)
|
|
def _(expr, assumptions):
|
|
ret = expr.is_zero
|
|
if ret is None:
|
|
raise MDNotImplementedError
|
|
return ret
|
|
|
|
@ZeroPredicate.register(Basic)
|
|
def _(expr, assumptions):
|
|
return fuzzy_and([fuzzy_not(ask(Q.nonzero(expr), assumptions)),
|
|
ask(Q.real(expr), assumptions)])
|
|
|
|
@ZeroPredicate.register(Mul)
|
|
def _(expr, assumptions):
|
|
# TODO: This should be deducible from the nonzero handler
|
|
return fuzzy_or(ask(Q.zero(arg), assumptions) for arg in expr.args)
|
|
|
|
|
|
# NonPositivePredicate
|
|
|
|
@NonPositivePredicate.register(Expr)
|
|
def _(expr, assumptions):
|
|
ret = expr.is_nonpositive
|
|
if ret is None:
|
|
raise MDNotImplementedError
|
|
return ret
|
|
|
|
@NonPositivePredicate.register(Basic)
|
|
def _(expr, assumptions):
|
|
if expr.is_number:
|
|
notpositive = fuzzy_not(_PositivePredicate_number(expr, assumptions))
|
|
if notpositive:
|
|
return ask(Q.real(expr), assumptions)
|
|
else:
|
|
return notpositive
|
|
|
|
|
|
# PositivePredicate
|
|
|
|
def _PositivePredicate_number(expr, assumptions):
|
|
r, i = expr.as_real_imag()
|
|
# If the imaginary part can symbolically be shown to be zero then
|
|
# we just evaluate the real part; otherwise we evaluate the imaginary
|
|
# part to see if it actually evaluates to zero and if it does then
|
|
# we make the comparison between the real part and zero.
|
|
if not i:
|
|
r = r.evalf(2)
|
|
if r._prec != 1:
|
|
return r > 0
|
|
else:
|
|
i = i.evalf(2)
|
|
if i._prec != 1:
|
|
if i != 0:
|
|
return False
|
|
r = r.evalf(2)
|
|
if r._prec != 1:
|
|
return r > 0
|
|
|
|
@PositivePredicate.register(Expr)
|
|
def _(expr, assumptions):
|
|
ret = expr.is_positive
|
|
if ret is None:
|
|
raise MDNotImplementedError
|
|
return ret
|
|
|
|
@PositivePredicate.register(Basic)
|
|
def _(expr, assumptions):
|
|
if expr.is_number:
|
|
return _PositivePredicate_number(expr, assumptions)
|
|
|
|
@PositivePredicate.register(Mul)
|
|
def _(expr, assumptions):
|
|
if expr.is_number:
|
|
return _PositivePredicate_number(expr, assumptions)
|
|
result = True
|
|
for arg in expr.args:
|
|
if ask(Q.positive(arg), assumptions):
|
|
continue
|
|
elif ask(Q.negative(arg), assumptions):
|
|
result = result ^ True
|
|
else:
|
|
return
|
|
return result
|
|
|
|
@PositivePredicate.register(Add)
|
|
def _(expr, assumptions):
|
|
if expr.is_number:
|
|
return _PositivePredicate_number(expr, assumptions)
|
|
|
|
r = ask(Q.real(expr), assumptions)
|
|
if r is not True:
|
|
return r
|
|
|
|
nonneg = 0
|
|
for arg in expr.args:
|
|
if ask(Q.positive(arg), assumptions) is not True:
|
|
if ask(Q.negative(arg), assumptions) is False:
|
|
nonneg += 1
|
|
else:
|
|
break
|
|
else:
|
|
if nonneg < len(expr.args):
|
|
return True
|
|
|
|
@PositivePredicate.register(Pow)
|
|
def _(expr, assumptions):
|
|
if expr.base == E:
|
|
if ask(Q.real(expr.exp), assumptions):
|
|
return True
|
|
if ask(Q.imaginary(expr.exp), assumptions):
|
|
return ask(Q.even(expr.exp/(I*pi)), assumptions)
|
|
return
|
|
|
|
if expr.is_number:
|
|
return _PositivePredicate_number(expr, assumptions)
|
|
if ask(Q.positive(expr.base), assumptions):
|
|
if ask(Q.real(expr.exp), assumptions):
|
|
return True
|
|
if ask(Q.negative(expr.base), assumptions):
|
|
if ask(Q.even(expr.exp), assumptions):
|
|
return True
|
|
if ask(Q.odd(expr.exp), assumptions):
|
|
return False
|
|
|
|
@PositivePredicate.register(exp)
|
|
def _(expr, assumptions):
|
|
if ask(Q.real(expr.exp), assumptions):
|
|
return True
|
|
if ask(Q.imaginary(expr.exp), assumptions):
|
|
return ask(Q.even(expr.exp/(I*pi)), assumptions)
|
|
|
|
@PositivePredicate.register(log)
|
|
def _(expr, assumptions):
|
|
r = ask(Q.real(expr.args[0]), assumptions)
|
|
if r is not True:
|
|
return r
|
|
if ask(Q.positive(expr.args[0] - 1), assumptions):
|
|
return True
|
|
if ask(Q.negative(expr.args[0] - 1), assumptions):
|
|
return False
|
|
|
|
@PositivePredicate.register(factorial)
|
|
def _(expr, assumptions):
|
|
x = expr.args[0]
|
|
if ask(Q.integer(x) & Q.positive(x), assumptions):
|
|
return True
|
|
|
|
@PositivePredicate.register(ImaginaryUnit)
|
|
def _(expr, assumptions):
|
|
return False
|
|
|
|
@PositivePredicate.register(Abs)
|
|
def _(expr, assumptions):
|
|
return ask(Q.nonzero(expr), assumptions)
|
|
|
|
@PositivePredicate.register(Trace)
|
|
def _(expr, assumptions):
|
|
if ask(Q.positive_definite(expr.arg), assumptions):
|
|
return True
|
|
|
|
@PositivePredicate.register(Determinant)
|
|
def _(expr, assumptions):
|
|
if ask(Q.positive_definite(expr.arg), assumptions):
|
|
return True
|
|
|
|
@PositivePredicate.register(MatrixElement)
|
|
def _(expr, assumptions):
|
|
if (expr.i == expr.j
|
|
and ask(Q.positive_definite(expr.parent), assumptions)):
|
|
return True
|
|
|
|
@PositivePredicate.register(atan)
|
|
def _(expr, assumptions):
|
|
return ask(Q.positive(expr.args[0]), assumptions)
|
|
|
|
@PositivePredicate.register(asin)
|
|
def _(expr, assumptions):
|
|
x = expr.args[0]
|
|
if ask(Q.positive(x) & Q.nonpositive(x - 1), assumptions):
|
|
return True
|
|
if ask(Q.negative(x) & Q.nonnegative(x + 1), assumptions):
|
|
return False
|
|
|
|
@PositivePredicate.register(acos)
|
|
def _(expr, assumptions):
|
|
x = expr.args[0]
|
|
if ask(Q.nonpositive(x - 1) & Q.nonnegative(x + 1), assumptions):
|
|
return True
|
|
|
|
@PositivePredicate.register(acot)
|
|
def _(expr, assumptions):
|
|
return ask(Q.real(expr.args[0]), assumptions)
|
|
|
|
@PositivePredicate.register(NaN)
|
|
def _(expr, assumptions):
|
|
return None
|
|
|
|
|
|
# ExtendedNegativePredicate
|
|
|
|
@ExtendedNegativePredicate.register(object)
|
|
def _(expr, assumptions):
|
|
return ask(Q.negative(expr) | Q.negative_infinite(expr), assumptions)
|
|
|
|
|
|
# ExtendedPositivePredicate
|
|
|
|
@ExtendedPositivePredicate.register(object)
|
|
def _(expr, assumptions):
|
|
return ask(Q.positive(expr) | Q.positive_infinite(expr), assumptions)
|
|
|
|
|
|
# ExtendedNonZeroPredicate
|
|
|
|
@ExtendedNonZeroPredicate.register(object)
|
|
def _(expr, assumptions):
|
|
return ask(
|
|
Q.negative_infinite(expr) | Q.negative(expr) | Q.positive(expr) | Q.positive_infinite(expr),
|
|
assumptions)
|
|
|
|
|
|
# ExtendedNonPositivePredicate
|
|
|
|
@ExtendedNonPositivePredicate.register(object)
|
|
def _(expr, assumptions):
|
|
return ask(
|
|
Q.negative_infinite(expr) | Q.negative(expr) | Q.zero(expr),
|
|
assumptions)
|
|
|
|
|
|
# ExtendedNonNegativePredicate
|
|
|
|
@ExtendedNonNegativePredicate.register(object)
|
|
def _(expr, assumptions):
|
|
return ask(
|
|
Q.zero(expr) | Q.positive(expr) | Q.positive_infinite(expr),
|
|
assumptions)
|