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.

108 lines
3.1 KiB

from sympy.core import Basic, Expr
from sympy.core.function import Lambda
from sympy.core.numbers import oo, Infinity, NegativeInfinity, Zero, Integer
from sympy.core.singleton import S
from sympy.core.symbol import symbols
from sympy.functions.elementary.miscellaneous import (Max, Min)
from sympy.sets.fancysets import ImageSet
from sympy.sets.setexpr import set_div
from sympy.sets.sets import Set, Interval, FiniteSet, Union
from sympy.multipledispatch import Dispatcher
_x, _y = symbols("x y")
_set_pow = Dispatcher('_set_pow')
@_set_pow.register(Basic, Basic)
def _(x, y):
return None
@_set_pow.register(Set, Set)
def _(x, y):
return ImageSet(Lambda((_x, _y), (_x ** _y)), x, y)
@_set_pow.register(Expr, Expr)
def _(x, y):
return x**y
@_set_pow.register(Interval, Zero)
def _(x, z):
return FiniteSet(S.One)
@_set_pow.register(Interval, Integer)
def _(x, exponent):
"""
Powers in interval arithmetic
https://en.wikipedia.org/wiki/Interval_arithmetic
"""
s1 = x.start**exponent
s2 = x.end**exponent
if ((s2 > s1) if exponent > 0 else (x.end > -x.start)) == True:
left_open = x.left_open
right_open = x.right_open
# TODO: handle unevaluated condition.
sleft = s2
else:
# TODO: `s2 > s1` could be unevaluated.
left_open = x.right_open
right_open = x.left_open
sleft = s1
if x.start.is_positive:
return Interval(
Min(s1, s2),
Max(s1, s2), left_open, right_open)
elif x.end.is_negative:
return Interval(
Min(s1, s2),
Max(s1, s2), left_open, right_open)
# Case where x.start < 0 and x.end > 0:
if exponent.is_odd:
if exponent.is_negative:
if x.start.is_zero:
return Interval(s2, oo, x.right_open)
if x.end.is_zero:
return Interval(-oo, s1, True, x.left_open)
return Union(Interval(-oo, s1, True, x.left_open), Interval(s2, oo, x.right_open))
else:
return Interval(s1, s2, x.left_open, x.right_open)
elif exponent.is_even:
if exponent.is_negative:
if x.start.is_zero:
return Interval(s2, oo, x.right_open)
if x.end.is_zero:
return Interval(s1, oo, x.left_open)
return Interval(0, oo)
else:
return Interval(S.Zero, sleft, S.Zero not in x, left_open)
@_set_pow.register(Interval, Infinity)
def _(b, e):
# TODO: add logic for open intervals?
if b.start.is_nonnegative:
if b.end < 1:
return FiniteSet(S.Zero)
if b.start > 1:
return FiniteSet(S.Infinity)
return Interval(0, oo)
elif b.end.is_negative:
if b.start > -1:
return FiniteSet(S.Zero)
if b.end < -1:
return FiniteSet(-oo, oo)
return Interval(-oo, oo)
else:
if b.start > -1:
if b.end < 1:
return FiniteSet(S.Zero)
return Interval(0, oo)
return Interval(-oo, oo)
@_set_pow.register(Interval, NegativeInfinity)
def _(b, e):
return _set_pow(set_div(S.One, b), oo)