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.

437 lines
12 KiB

5 months ago
"""
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)