switching to high quality piper tts and added label translations
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
from sympy.core.numbers import oo, Infinity, NegativeInfinity
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core import Basic, Expr
|
||||
from sympy.multipledispatch import Dispatcher
|
||||
from sympy.sets import Interval, FiniteSet
|
||||
|
||||
|
||||
|
||||
# XXX: The functions in this module are clearly not tested and are broken in a
|
||||
# number of ways.
|
||||
|
||||
_set_add = Dispatcher('_set_add')
|
||||
_set_sub = Dispatcher('_set_sub')
|
||||
|
||||
|
||||
@_set_add.register(Basic, Basic)
|
||||
def _(x, y):
|
||||
return None
|
||||
|
||||
|
||||
@_set_add.register(Expr, Expr)
|
||||
def _(x, y):
|
||||
return x+y
|
||||
|
||||
|
||||
@_set_add.register(Interval, Interval)
|
||||
def _(x, y):
|
||||
"""
|
||||
Additions in interval arithmetic
|
||||
https://en.wikipedia.org/wiki/Interval_arithmetic
|
||||
"""
|
||||
return Interval(x.start + y.start, x.end + y.end,
|
||||
x.left_open or y.left_open, x.right_open or y.right_open)
|
||||
|
||||
|
||||
@_set_add.register(Interval, Infinity)
|
||||
def _(x, y):
|
||||
if x.start is S.NegativeInfinity:
|
||||
return Interval(-oo, oo)
|
||||
return FiniteSet({S.Infinity})
|
||||
|
||||
@_set_add.register(Interval, NegativeInfinity)
|
||||
def _(x, y):
|
||||
if x.end is S.Infinity:
|
||||
return Interval(-oo, oo)
|
||||
return FiniteSet({S.NegativeInfinity})
|
||||
|
||||
|
||||
@_set_sub.register(Basic, Basic)
|
||||
def _(x, y):
|
||||
return None
|
||||
|
||||
|
||||
@_set_sub.register(Expr, Expr)
|
||||
def _(x, y):
|
||||
return x-y
|
||||
|
||||
|
||||
@_set_sub.register(Interval, Interval)
|
||||
def _(x, y):
|
||||
"""
|
||||
Subtractions in interval arithmetic
|
||||
https://en.wikipedia.org/wiki/Interval_arithmetic
|
||||
"""
|
||||
return Interval(x.start - y.end, x.end - y.start,
|
||||
x.left_open or y.right_open, x.right_open or y.left_open)
|
||||
|
||||
|
||||
@_set_sub.register(Interval, Infinity)
|
||||
def _(x, y):
|
||||
if x.start is S.NegativeInfinity:
|
||||
return Interval(-oo, oo)
|
||||
return FiniteSet(-oo)
|
||||
|
||||
@_set_sub.register(Interval, NegativeInfinity)
|
||||
def _(x, y):
|
||||
if x.start is S.NegativeInfinity:
|
||||
return Interval(-oo, oo)
|
||||
return FiniteSet(-oo)
|
||||
@@ -0,0 +1,53 @@
|
||||
from sympy.core.relational import Eq, is_eq
|
||||
from sympy.core.basic import Basic
|
||||
from sympy.core.logic import fuzzy_and, fuzzy_bool
|
||||
from sympy.logic.boolalg import And
|
||||
from sympy.multipledispatch import dispatch
|
||||
from sympy.sets.sets import tfn, ProductSet, Interval, FiniteSet, Set
|
||||
|
||||
|
||||
@dispatch(Interval, FiniteSet) # type:ignore
|
||||
def _eval_is_eq(lhs, rhs): # noqa: F811
|
||||
return False
|
||||
|
||||
|
||||
@dispatch(FiniteSet, Interval) # type:ignore
|
||||
def _eval_is_eq(lhs, rhs): # noqa: F811
|
||||
return False
|
||||
|
||||
|
||||
@dispatch(Interval, Interval) # type:ignore
|
||||
def _eval_is_eq(lhs, rhs): # noqa: F811
|
||||
return And(Eq(lhs.left, rhs.left),
|
||||
Eq(lhs.right, rhs.right),
|
||||
lhs.left_open == rhs.left_open,
|
||||
lhs.right_open == rhs.right_open)
|
||||
|
||||
@dispatch(FiniteSet, FiniteSet) # type:ignore
|
||||
def _eval_is_eq(lhs, rhs): # noqa: F811
|
||||
def all_in_both():
|
||||
s_set = set(lhs.args)
|
||||
o_set = set(rhs.args)
|
||||
yield fuzzy_and(lhs._contains(e) for e in o_set - s_set)
|
||||
yield fuzzy_and(rhs._contains(e) for e in s_set - o_set)
|
||||
|
||||
return tfn[fuzzy_and(all_in_both())]
|
||||
|
||||
|
||||
@dispatch(ProductSet, ProductSet) # type:ignore
|
||||
def _eval_is_eq(lhs, rhs): # noqa: F811
|
||||
if len(lhs.sets) != len(rhs.sets):
|
||||
return False
|
||||
|
||||
eqs = (is_eq(x, y) for x, y in zip(lhs.sets, rhs.sets))
|
||||
return tfn[fuzzy_and(map(fuzzy_bool, eqs))]
|
||||
|
||||
|
||||
@dispatch(Set, Basic) # type:ignore
|
||||
def _eval_is_eq(lhs, rhs): # noqa: F811
|
||||
return False
|
||||
|
||||
|
||||
@dispatch(Set, Set) # type:ignore
|
||||
def _eval_is_eq(lhs, rhs): # noqa: F811
|
||||
return tfn[fuzzy_and(a.is_subset(b) for a, b in [(lhs, rhs), (rhs, lhs)])]
|
||||
@@ -0,0 +1,262 @@
|
||||
from sympy.core.singleton import S
|
||||
from sympy.sets.sets import Set
|
||||
from sympy.calculus.singularities import singularities
|
||||
from sympy.core import Expr, Add
|
||||
from sympy.core.function import Lambda, FunctionClass, diff, expand_mul
|
||||
from sympy.core.numbers import Float, oo
|
||||
from sympy.core.symbol import Dummy, symbols, Wild
|
||||
from sympy.functions.elementary.exponential import exp, log
|
||||
from sympy.functions.elementary.miscellaneous import Min, Max
|
||||
from sympy.logic.boolalg import true
|
||||
from sympy.multipledispatch import Dispatcher
|
||||
from sympy.sets import (imageset, Interval, FiniteSet, Union, ImageSet,
|
||||
Intersection, Range, Complement)
|
||||
from sympy.sets.sets import EmptySet, is_function_invertible_in_set
|
||||
from sympy.sets.fancysets import Integers, Naturals, Reals
|
||||
from sympy.functions.elementary.exponential import match_real_imag
|
||||
|
||||
|
||||
_x, _y = symbols("x y")
|
||||
|
||||
FunctionUnion = (FunctionClass, Lambda)
|
||||
|
||||
_set_function = Dispatcher('_set_function')
|
||||
|
||||
|
||||
@_set_function.register(FunctionClass, Set)
|
||||
def _(f, x):
|
||||
return None
|
||||
|
||||
@_set_function.register(FunctionUnion, FiniteSet)
|
||||
def _(f, x):
|
||||
return FiniteSet(*map(f, x))
|
||||
|
||||
@_set_function.register(Lambda, Interval)
|
||||
def _(f, x):
|
||||
from sympy.solvers.solveset import solveset
|
||||
from sympy.series import limit
|
||||
# TODO: handle functions with infinitely many solutions (eg, sin, tan)
|
||||
# TODO: handle multivariate functions
|
||||
|
||||
expr = f.expr
|
||||
if len(expr.free_symbols) > 1 or len(f.variables) != 1:
|
||||
return
|
||||
var = f.variables[0]
|
||||
if not var.is_real:
|
||||
if expr.subs(var, Dummy(real=True)).is_real is False:
|
||||
return
|
||||
|
||||
if expr.is_Piecewise:
|
||||
result = S.EmptySet
|
||||
domain_set = x
|
||||
for (p_expr, p_cond) in expr.args:
|
||||
if p_cond is true:
|
||||
intrvl = domain_set
|
||||
else:
|
||||
intrvl = p_cond.as_set()
|
||||
intrvl = Intersection(domain_set, intrvl)
|
||||
|
||||
if p_expr.is_Number:
|
||||
image = FiniteSet(p_expr)
|
||||
else:
|
||||
image = imageset(Lambda(var, p_expr), intrvl)
|
||||
result = Union(result, image)
|
||||
|
||||
# remove the part which has been `imaged`
|
||||
domain_set = Complement(domain_set, intrvl)
|
||||
if domain_set is S.EmptySet:
|
||||
break
|
||||
return result
|
||||
|
||||
if not x.start.is_comparable or not x.end.is_comparable:
|
||||
return
|
||||
|
||||
try:
|
||||
from sympy.polys.polyutils import _nsort
|
||||
sing = list(singularities(expr, var, x))
|
||||
if len(sing) > 1:
|
||||
sing = _nsort(sing)
|
||||
except NotImplementedError:
|
||||
return
|
||||
|
||||
if x.left_open:
|
||||
_start = limit(expr, var, x.start, dir="+")
|
||||
elif x.start not in sing:
|
||||
_start = f(x.start)
|
||||
if x.right_open:
|
||||
_end = limit(expr, var, x.end, dir="-")
|
||||
elif x.end not in sing:
|
||||
_end = f(x.end)
|
||||
|
||||
if len(sing) == 0:
|
||||
soln_expr = solveset(diff(expr, var), var)
|
||||
if not (isinstance(soln_expr, FiniteSet)
|
||||
or soln_expr is S.EmptySet):
|
||||
return
|
||||
solns = list(soln_expr)
|
||||
|
||||
extr = [_start, _end] + [f(i) for i in solns
|
||||
if i.is_real and i in x]
|
||||
start, end = Min(*extr), Max(*extr)
|
||||
|
||||
left_open, right_open = False, False
|
||||
if _start <= _end:
|
||||
# the minimum or maximum value can occur simultaneously
|
||||
# on both the edge of the interval and in some interior
|
||||
# point
|
||||
if start == _start and start not in solns:
|
||||
left_open = x.left_open
|
||||
if end == _end and end not in solns:
|
||||
right_open = x.right_open
|
||||
else:
|
||||
if start == _end and start not in solns:
|
||||
left_open = x.right_open
|
||||
if end == _start and end not in solns:
|
||||
right_open = x.left_open
|
||||
|
||||
return Interval(start, end, left_open, right_open)
|
||||
else:
|
||||
return imageset(f, Interval(x.start, sing[0],
|
||||
x.left_open, True)) + \
|
||||
Union(*[imageset(f, Interval(sing[i], sing[i + 1], True, True))
|
||||
for i in range(0, len(sing) - 1)]) + \
|
||||
imageset(f, Interval(sing[-1], x.end, True, x.right_open))
|
||||
|
||||
@_set_function.register(FunctionClass, Interval)
|
||||
def _(f, x):
|
||||
if f == exp:
|
||||
return Interval(exp(x.start), exp(x.end), x.left_open, x.right_open)
|
||||
elif f == log:
|
||||
return Interval(log(x.start), log(x.end), x.left_open, x.right_open)
|
||||
return ImageSet(Lambda(_x, f(_x)), x)
|
||||
|
||||
@_set_function.register(FunctionUnion, Union)
|
||||
def _(f, x):
|
||||
return Union(*(imageset(f, arg) for arg in x.args))
|
||||
|
||||
@_set_function.register(FunctionUnion, Intersection)
|
||||
def _(f, x):
|
||||
# If the function is invertible, intersect the maps of the sets.
|
||||
if is_function_invertible_in_set(f, x):
|
||||
return Intersection(*(imageset(f, arg) for arg in x.args))
|
||||
else:
|
||||
return ImageSet(Lambda(_x, f(_x)), x)
|
||||
|
||||
@_set_function.register(FunctionUnion, EmptySet)
|
||||
def _(f, x):
|
||||
return x
|
||||
|
||||
@_set_function.register(FunctionUnion, Set)
|
||||
def _(f, x):
|
||||
return ImageSet(Lambda(_x, f(_x)), x)
|
||||
|
||||
@_set_function.register(FunctionUnion, Range)
|
||||
def _(f, self):
|
||||
if not self:
|
||||
return S.EmptySet
|
||||
if not isinstance(f.expr, Expr):
|
||||
return
|
||||
if self.size == 1:
|
||||
return FiniteSet(f(self[0]))
|
||||
if f is S.IdentityFunction:
|
||||
return self
|
||||
|
||||
x = f.variables[0]
|
||||
expr = f.expr
|
||||
# handle f that is linear in f's variable
|
||||
if x not in expr.free_symbols or x in expr.diff(x).free_symbols:
|
||||
return
|
||||
if self.start.is_finite:
|
||||
F = f(self.step*x + self.start) # for i in range(len(self))
|
||||
else:
|
||||
F = f(-self.step*x + self[-1])
|
||||
F = expand_mul(F)
|
||||
if F != expr:
|
||||
return imageset(x, F, Range(self.size))
|
||||
|
||||
@_set_function.register(FunctionUnion, Integers)
|
||||
def _(f, self):
|
||||
expr = f.expr
|
||||
if not isinstance(expr, Expr):
|
||||
return
|
||||
|
||||
n = f.variables[0]
|
||||
if expr == abs(n):
|
||||
return S.Naturals0
|
||||
|
||||
# f(x) + c and f(-x) + c cover the same integers
|
||||
# so choose the form that has the fewest negatives
|
||||
c = f(0)
|
||||
fx = f(n) - c
|
||||
f_x = f(-n) - c
|
||||
neg_count = lambda e: sum(_.could_extract_minus_sign()
|
||||
for _ in Add.make_args(e))
|
||||
if neg_count(f_x) < neg_count(fx):
|
||||
expr = f_x + c
|
||||
|
||||
a = Wild('a', exclude=[n])
|
||||
b = Wild('b', exclude=[n])
|
||||
match = expr.match(a*n + b)
|
||||
if match and match[a] and (
|
||||
not match[a].atoms(Float) and
|
||||
not match[b].atoms(Float)):
|
||||
# canonical shift
|
||||
a, b = match[a], match[b]
|
||||
if a in [1, -1]:
|
||||
# drop integer addends in b
|
||||
nonint = []
|
||||
for bi in Add.make_args(b):
|
||||
if not bi.is_integer:
|
||||
nonint.append(bi)
|
||||
b = Add(*nonint)
|
||||
if b.is_number and a.is_real:
|
||||
# avoid Mod for complex numbers, #11391
|
||||
br, bi = match_real_imag(b)
|
||||
if br and br.is_comparable and a.is_comparable:
|
||||
br %= a
|
||||
b = br + S.ImaginaryUnit*bi
|
||||
elif b.is_number and a.is_imaginary:
|
||||
br, bi = match_real_imag(b)
|
||||
ai = a/S.ImaginaryUnit
|
||||
if bi and bi.is_comparable and ai.is_comparable:
|
||||
bi %= ai
|
||||
b = br + S.ImaginaryUnit*bi
|
||||
expr = a*n + b
|
||||
|
||||
if expr != f.expr:
|
||||
return ImageSet(Lambda(n, expr), S.Integers)
|
||||
|
||||
|
||||
@_set_function.register(FunctionUnion, Naturals)
|
||||
def _(f, self):
|
||||
expr = f.expr
|
||||
if not isinstance(expr, Expr):
|
||||
return
|
||||
|
||||
x = f.variables[0]
|
||||
if not expr.free_symbols - {x}:
|
||||
if expr == abs(x):
|
||||
if self is S.Naturals:
|
||||
return self
|
||||
return S.Naturals0
|
||||
step = expr.coeff(x)
|
||||
c = expr.subs(x, 0)
|
||||
if c.is_Integer and step.is_Integer and expr == step*x + c:
|
||||
if self is S.Naturals:
|
||||
c += step
|
||||
if step > 0:
|
||||
if step == 1:
|
||||
if c == 0:
|
||||
return S.Naturals0
|
||||
elif c == 1:
|
||||
return S.Naturals
|
||||
return Range(c, oo, step)
|
||||
return Range(c, -oo, step)
|
||||
|
||||
|
||||
@_set_function.register(FunctionUnion, Reals)
|
||||
def _(f, self):
|
||||
expr = f.expr
|
||||
if not isinstance(expr, Expr):
|
||||
return
|
||||
return _set_function(f, Interval(-oo, oo))
|
||||
@@ -0,0 +1,533 @@
|
||||
from sympy.core.basic import _aresame
|
||||
from sympy.core.function import Lambda, expand_complex
|
||||
from sympy.core.mul import Mul
|
||||
from sympy.core.numbers import ilcm, Float
|
||||
from sympy.core.relational import Eq
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Dummy, symbols)
|
||||
from sympy.core.sorting import ordered
|
||||
from sympy.functions.elementary.complexes import sign
|
||||
from sympy.functions.elementary.integers import floor, ceiling
|
||||
from sympy.sets.fancysets import ComplexRegion
|
||||
from sympy.sets.sets import (FiniteSet, Intersection, Interval, Set, Union)
|
||||
from sympy.multipledispatch import Dispatcher
|
||||
from sympy.sets.conditionset import ConditionSet
|
||||
from sympy.sets.fancysets import (Integers, Naturals, Reals, Range,
|
||||
ImageSet, Rationals)
|
||||
from sympy.sets.sets import EmptySet, UniversalSet, imageset, ProductSet
|
||||
from sympy.simplify.radsimp import numer
|
||||
|
||||
|
||||
intersection_sets = Dispatcher('intersection_sets')
|
||||
|
||||
|
||||
@intersection_sets.register(ConditionSet, ConditionSet)
|
||||
def _(a, b):
|
||||
return None
|
||||
|
||||
@intersection_sets.register(ConditionSet, Set)
|
||||
def _(a, b):
|
||||
return ConditionSet(a.sym, a.condition, Intersection(a.base_set, b))
|
||||
|
||||
@intersection_sets.register(Naturals, Integers)
|
||||
def _(a, b):
|
||||
return a
|
||||
|
||||
@intersection_sets.register(Naturals, Naturals)
|
||||
def _(a, b):
|
||||
return a if a is S.Naturals else b
|
||||
|
||||
@intersection_sets.register(Interval, Naturals)
|
||||
def _(a, b):
|
||||
return intersection_sets(b, a)
|
||||
|
||||
@intersection_sets.register(ComplexRegion, Set)
|
||||
def _(self, other):
|
||||
if other.is_ComplexRegion:
|
||||
# self in rectangular form
|
||||
if (not self.polar) and (not other.polar):
|
||||
return ComplexRegion(Intersection(self.sets, other.sets))
|
||||
|
||||
# self in polar form
|
||||
elif self.polar and other.polar:
|
||||
r1, theta1 = self.a_interval, self.b_interval
|
||||
r2, theta2 = other.a_interval, other.b_interval
|
||||
new_r_interval = Intersection(r1, r2)
|
||||
new_theta_interval = Intersection(theta1, theta2)
|
||||
|
||||
# 0 and 2*Pi means the same
|
||||
if ((2*S.Pi in theta1 and S.Zero in theta2) or
|
||||
(2*S.Pi in theta2 and S.Zero in theta1)):
|
||||
new_theta_interval = Union(new_theta_interval,
|
||||
FiniteSet(0))
|
||||
return ComplexRegion(new_r_interval*new_theta_interval,
|
||||
polar=True)
|
||||
|
||||
|
||||
if other.is_subset(S.Reals):
|
||||
new_interval = []
|
||||
x = symbols("x", cls=Dummy, real=True)
|
||||
|
||||
# self in rectangular form
|
||||
if not self.polar:
|
||||
for element in self.psets:
|
||||
if S.Zero in element.args[1]:
|
||||
new_interval.append(element.args[0])
|
||||
new_interval = Union(*new_interval)
|
||||
return Intersection(new_interval, other)
|
||||
|
||||
# self in polar form
|
||||
elif self.polar:
|
||||
for element in self.psets:
|
||||
if S.Zero in element.args[1]:
|
||||
new_interval.append(element.args[0])
|
||||
if S.Pi in element.args[1]:
|
||||
new_interval.append(ImageSet(Lambda(x, -x), element.args[0]))
|
||||
if S.Zero in element.args[0]:
|
||||
new_interval.append(FiniteSet(0))
|
||||
new_interval = Union(*new_interval)
|
||||
return Intersection(new_interval, other)
|
||||
|
||||
@intersection_sets.register(Integers, Reals)
|
||||
def _(a, b):
|
||||
return a
|
||||
|
||||
@intersection_sets.register(Range, Interval)
|
||||
def _(a, b):
|
||||
# Check that there are no symbolic arguments
|
||||
if not all(i.is_number for i in a.args + b.args[:2]):
|
||||
return
|
||||
|
||||
# In case of null Range, return an EmptySet.
|
||||
if a.size == 0:
|
||||
return S.EmptySet
|
||||
|
||||
# trim down to self's size, and represent
|
||||
# as a Range with step 1.
|
||||
start = ceiling(max(b.inf, a.inf))
|
||||
if start not in b:
|
||||
start += 1
|
||||
end = floor(min(b.sup, a.sup))
|
||||
if end not in b:
|
||||
end -= 1
|
||||
return intersection_sets(a, Range(start, end + 1))
|
||||
|
||||
@intersection_sets.register(Range, Naturals)
|
||||
def _(a, b):
|
||||
return intersection_sets(a, Interval(b.inf, S.Infinity))
|
||||
|
||||
@intersection_sets.register(Range, Range)
|
||||
def _(a, b):
|
||||
# Check that there are no symbolic range arguments
|
||||
if not all(all(v.is_number for v in r.args) for r in [a, b]):
|
||||
return None
|
||||
|
||||
# non-overlap quick exits
|
||||
if not b:
|
||||
return S.EmptySet
|
||||
if not a:
|
||||
return S.EmptySet
|
||||
if b.sup < a.inf:
|
||||
return S.EmptySet
|
||||
if b.inf > a.sup:
|
||||
return S.EmptySet
|
||||
|
||||
# work with finite end at the start
|
||||
r1 = a
|
||||
if r1.start.is_infinite:
|
||||
r1 = r1.reversed
|
||||
r2 = b
|
||||
if r2.start.is_infinite:
|
||||
r2 = r2.reversed
|
||||
|
||||
# If both ends are infinite then it means that one Range is just the set
|
||||
# of all integers (the step must be 1).
|
||||
if r1.start.is_infinite:
|
||||
return b
|
||||
if r2.start.is_infinite:
|
||||
return a
|
||||
|
||||
from sympy.solvers.diophantine.diophantine import diop_linear
|
||||
|
||||
# this equation represents the values of the Range;
|
||||
# it's a linear equation
|
||||
eq = lambda r, i: r.start + i*r.step
|
||||
|
||||
# we want to know when the two equations might
|
||||
# have integer solutions so we use the diophantine
|
||||
# solver
|
||||
va, vb = diop_linear(eq(r1, Dummy('a')) - eq(r2, Dummy('b')))
|
||||
|
||||
# check for no solution
|
||||
no_solution = va is None and vb is None
|
||||
if no_solution:
|
||||
return S.EmptySet
|
||||
|
||||
# there is a solution
|
||||
# -------------------
|
||||
|
||||
# find the coincident point, c
|
||||
a0 = va.as_coeff_Add()[0]
|
||||
c = eq(r1, a0)
|
||||
|
||||
# find the first point, if possible, in each range
|
||||
# since c may not be that point
|
||||
def _first_finite_point(r1, c):
|
||||
if c == r1.start:
|
||||
return c
|
||||
# st is the signed step we need to take to
|
||||
# get from c to r1.start
|
||||
st = sign(r1.start - c)*step
|
||||
# use Range to calculate the first point:
|
||||
# we want to get as close as possible to
|
||||
# r1.start; the Range will not be null since
|
||||
# it will at least contain c
|
||||
s1 = Range(c, r1.start + st, st)[-1]
|
||||
if s1 == r1.start:
|
||||
pass
|
||||
else:
|
||||
# if we didn't hit r1.start then, if the
|
||||
# sign of st didn't match the sign of r1.step
|
||||
# we are off by one and s1 is not in r1
|
||||
if sign(r1.step) != sign(st):
|
||||
s1 -= st
|
||||
if s1 not in r1:
|
||||
return
|
||||
return s1
|
||||
|
||||
# calculate the step size of the new Range
|
||||
step = abs(ilcm(r1.step, r2.step))
|
||||
s1 = _first_finite_point(r1, c)
|
||||
if s1 is None:
|
||||
return S.EmptySet
|
||||
s2 = _first_finite_point(r2, c)
|
||||
if s2 is None:
|
||||
return S.EmptySet
|
||||
|
||||
# replace the corresponding start or stop in
|
||||
# the original Ranges with these points; the
|
||||
# result must have at least one point since
|
||||
# we know that s1 and s2 are in the Ranges
|
||||
def _updated_range(r, first):
|
||||
st = sign(r.step)*step
|
||||
if r.start.is_finite:
|
||||
rv = Range(first, r.stop, st)
|
||||
else:
|
||||
rv = Range(r.start, first + st, st)
|
||||
return rv
|
||||
r1 = _updated_range(a, s1)
|
||||
r2 = _updated_range(b, s2)
|
||||
|
||||
# work with them both in the increasing direction
|
||||
if sign(r1.step) < 0:
|
||||
r1 = r1.reversed
|
||||
if sign(r2.step) < 0:
|
||||
r2 = r2.reversed
|
||||
|
||||
# return clipped Range with positive step; it
|
||||
# can't be empty at this point
|
||||
start = max(r1.start, r2.start)
|
||||
stop = min(r1.stop, r2.stop)
|
||||
return Range(start, stop, step)
|
||||
|
||||
|
||||
@intersection_sets.register(Range, Integers)
|
||||
def _(a, b):
|
||||
return a
|
||||
|
||||
|
||||
@intersection_sets.register(Range, Rationals)
|
||||
def _(a, b):
|
||||
return a
|
||||
|
||||
|
||||
@intersection_sets.register(ImageSet, Set)
|
||||
def _(self, other):
|
||||
from sympy.solvers.diophantine import diophantine
|
||||
|
||||
# Only handle the straight-forward univariate case
|
||||
if (len(self.lamda.variables) > 1
|
||||
or self.lamda.signature != self.lamda.variables):
|
||||
return None
|
||||
base_set = self.base_sets[0]
|
||||
|
||||
# Intersection between ImageSets with Integers as base set
|
||||
# For {f(n) : n in Integers} & {g(m) : m in Integers} we solve the
|
||||
# diophantine equations f(n)=g(m).
|
||||
# If the solutions for n are {h(t) : t in Integers} then we return
|
||||
# {f(h(t)) : t in integers}.
|
||||
# If the solutions for n are {n_1, n_2, ..., n_k} then we return
|
||||
# {f(n_i) : 1 <= i <= k}.
|
||||
if base_set is S.Integers:
|
||||
gm = None
|
||||
if isinstance(other, ImageSet) and other.base_sets == (S.Integers,):
|
||||
gm = other.lamda.expr
|
||||
var = other.lamda.variables[0]
|
||||
# Symbol of second ImageSet lambda must be distinct from first
|
||||
m = Dummy('m')
|
||||
gm = gm.subs(var, m)
|
||||
elif other is S.Integers:
|
||||
m = gm = Dummy('m')
|
||||
if gm is not None:
|
||||
fn = self.lamda.expr
|
||||
n = self.lamda.variables[0]
|
||||
try:
|
||||
solns = list(diophantine(fn - gm, syms=(n, m), permute=True))
|
||||
except (TypeError, NotImplementedError):
|
||||
# TypeError if equation not polynomial with rational coeff.
|
||||
# NotImplementedError if correct format but no solver.
|
||||
return
|
||||
# 3 cases are possible for solns:
|
||||
# - empty set,
|
||||
# - one or more parametric (infinite) solutions,
|
||||
# - a finite number of (non-parametric) solution couples.
|
||||
# Among those, there is one type of solution set that is
|
||||
# not helpful here: multiple parametric solutions.
|
||||
if len(solns) == 0:
|
||||
return S.EmptySet
|
||||
elif any(s.free_symbols for tupl in solns for s in tupl):
|
||||
if len(solns) == 1:
|
||||
soln, solm = solns[0]
|
||||
(t,) = soln.free_symbols
|
||||
expr = fn.subs(n, soln.subs(t, n)).expand()
|
||||
return imageset(Lambda(n, expr), S.Integers)
|
||||
else:
|
||||
return
|
||||
else:
|
||||
return FiniteSet(*(fn.subs(n, s[0]) for s in solns))
|
||||
|
||||
if other == S.Reals:
|
||||
from sympy.solvers.solvers import denoms, solve_linear
|
||||
|
||||
def _solution_union(exprs, sym):
|
||||
# return a union of linear solutions to i in expr;
|
||||
# if i cannot be solved, use a ConditionSet for solution
|
||||
sols = []
|
||||
for i in exprs:
|
||||
x, xis = solve_linear(i, 0, [sym])
|
||||
if x == sym:
|
||||
sols.append(FiniteSet(xis))
|
||||
else:
|
||||
sols.append(ConditionSet(sym, Eq(i, 0)))
|
||||
return Union(*sols)
|
||||
|
||||
f = self.lamda.expr
|
||||
n = self.lamda.variables[0]
|
||||
|
||||
n_ = Dummy(n.name, real=True)
|
||||
f_ = f.subs(n, n_)
|
||||
|
||||
re, im = f_.as_real_imag()
|
||||
im = expand_complex(im)
|
||||
|
||||
re = re.subs(n_, n)
|
||||
im = im.subs(n_, n)
|
||||
ifree = im.free_symbols
|
||||
lam = Lambda(n, re)
|
||||
if im.is_zero:
|
||||
# allow re-evaluation
|
||||
# of self in this case to make
|
||||
# the result canonical
|
||||
pass
|
||||
elif im.is_zero is False:
|
||||
return S.EmptySet
|
||||
elif ifree != {n}:
|
||||
return None
|
||||
else:
|
||||
# univarite imaginary part in same variable;
|
||||
# use numer instead of as_numer_denom to keep
|
||||
# this as fast as possible while still handling
|
||||
# simple cases
|
||||
base_set &= _solution_union(
|
||||
Mul.make_args(numer(im)), n)
|
||||
# exclude values that make denominators 0
|
||||
base_set -= _solution_union(denoms(f), n)
|
||||
return imageset(lam, base_set)
|
||||
|
||||
elif isinstance(other, Interval):
|
||||
from sympy.solvers.solveset import (invert_real, invert_complex,
|
||||
solveset)
|
||||
|
||||
f = self.lamda.expr
|
||||
n = self.lamda.variables[0]
|
||||
new_inf, new_sup = None, None
|
||||
new_lopen, new_ropen = other.left_open, other.right_open
|
||||
|
||||
if f.is_real:
|
||||
inverter = invert_real
|
||||
else:
|
||||
inverter = invert_complex
|
||||
|
||||
g1, h1 = inverter(f, other.inf, n)
|
||||
g2, h2 = inverter(f, other.sup, n)
|
||||
|
||||
if all(isinstance(i, FiniteSet) for i in (h1, h2)):
|
||||
if g1 == n:
|
||||
if len(h1) == 1:
|
||||
new_inf = h1.args[0]
|
||||
if g2 == n:
|
||||
if len(h2) == 1:
|
||||
new_sup = h2.args[0]
|
||||
# TODO: Design a technique to handle multiple-inverse
|
||||
# functions
|
||||
|
||||
# Any of the new boundary values cannot be determined
|
||||
if any(i is None for i in (new_sup, new_inf)):
|
||||
return
|
||||
|
||||
|
||||
range_set = S.EmptySet
|
||||
|
||||
if all(i.is_real for i in (new_sup, new_inf)):
|
||||
# this assumes continuity of underlying function
|
||||
# however fixes the case when it is decreasing
|
||||
if new_inf > new_sup:
|
||||
new_inf, new_sup = new_sup, new_inf
|
||||
new_interval = Interval(new_inf, new_sup, new_lopen, new_ropen)
|
||||
range_set = base_set.intersect(new_interval)
|
||||
else:
|
||||
if other.is_subset(S.Reals):
|
||||
solutions = solveset(f, n, S.Reals)
|
||||
if not isinstance(range_set, (ImageSet, ConditionSet)):
|
||||
range_set = solutions.intersect(other)
|
||||
else:
|
||||
return
|
||||
|
||||
if range_set is S.EmptySet:
|
||||
return S.EmptySet
|
||||
elif isinstance(range_set, Range) and range_set.size is not S.Infinity:
|
||||
range_set = FiniteSet(*list(range_set))
|
||||
|
||||
if range_set is not None:
|
||||
return imageset(Lambda(n, f), range_set)
|
||||
return
|
||||
else:
|
||||
return
|
||||
|
||||
|
||||
@intersection_sets.register(ProductSet, ProductSet)
|
||||
def _(a, b):
|
||||
if len(b.args) != len(a.args):
|
||||
return S.EmptySet
|
||||
return ProductSet(*(i.intersect(j) for i, j in zip(a.sets, b.sets)))
|
||||
|
||||
|
||||
@intersection_sets.register(Interval, Interval)
|
||||
def _(a, b):
|
||||
# handle (-oo, oo)
|
||||
infty = S.NegativeInfinity, S.Infinity
|
||||
if a == Interval(*infty):
|
||||
l, r = a.left, a.right
|
||||
if l.is_real or l in infty or r.is_real or r in infty:
|
||||
return b
|
||||
|
||||
# We can't intersect [0,3] with [x,6] -- we don't know if x>0 or x<0
|
||||
if not a._is_comparable(b):
|
||||
return None
|
||||
|
||||
empty = False
|
||||
|
||||
if a.start <= b.end and b.start <= a.end:
|
||||
# Get topology right.
|
||||
if a.start < b.start:
|
||||
start = b.start
|
||||
left_open = b.left_open
|
||||
elif a.start > b.start:
|
||||
start = a.start
|
||||
left_open = a.left_open
|
||||
else:
|
||||
start = a.start
|
||||
if not _aresame(a.start, b.start):
|
||||
# For example Integer(2) != Float(2)
|
||||
# Prefer the Float boundary because Floats should be
|
||||
# contagious in calculations.
|
||||
if b.start.has(Float) and not a.start.has(Float):
|
||||
start = b.start
|
||||
elif a.start.has(Float) and not b.start.has(Float):
|
||||
start = a.start
|
||||
else:
|
||||
#this is to ensure that if Eq(a.start, b.start) but
|
||||
#type(a.start) != type(b.start) the order of a and b
|
||||
#does not matter for the result
|
||||
start = list(ordered([a,b]))[0].start
|
||||
left_open = a.left_open or b.left_open
|
||||
|
||||
if a.end < b.end:
|
||||
end = a.end
|
||||
right_open = a.right_open
|
||||
elif a.end > b.end:
|
||||
end = b.end
|
||||
right_open = b.right_open
|
||||
else:
|
||||
# see above for logic with start
|
||||
end = a.end
|
||||
if not _aresame(a.end, b.end):
|
||||
if b.end.has(Float) and not a.end.has(Float):
|
||||
end = b.end
|
||||
elif a.end.has(Float) and not b.end.has(Float):
|
||||
end = a.end
|
||||
else:
|
||||
end = list(ordered([a,b]))[0].end
|
||||
right_open = a.right_open or b.right_open
|
||||
|
||||
if end - start == 0 and (left_open or right_open):
|
||||
empty = True
|
||||
else:
|
||||
empty = True
|
||||
|
||||
if empty:
|
||||
return S.EmptySet
|
||||
|
||||
return Interval(start, end, left_open, right_open)
|
||||
|
||||
@intersection_sets.register(EmptySet, Set)
|
||||
def _(a, b):
|
||||
return S.EmptySet
|
||||
|
||||
@intersection_sets.register(UniversalSet, Set)
|
||||
def _(a, b):
|
||||
return b
|
||||
|
||||
@intersection_sets.register(FiniteSet, FiniteSet)
|
||||
def _(a, b):
|
||||
return FiniteSet(*(a._elements & b._elements))
|
||||
|
||||
@intersection_sets.register(FiniteSet, Set)
|
||||
def _(a, b):
|
||||
try:
|
||||
return FiniteSet(*[el for el in a if el in b])
|
||||
except TypeError:
|
||||
return None # could not evaluate `el in b` due to symbolic ranges.
|
||||
|
||||
@intersection_sets.register(Set, Set)
|
||||
def _(a, b):
|
||||
return None
|
||||
|
||||
@intersection_sets.register(Integers, Rationals)
|
||||
def _(a, b):
|
||||
return a
|
||||
|
||||
@intersection_sets.register(Naturals, Rationals)
|
||||
def _(a, b):
|
||||
return a
|
||||
|
||||
@intersection_sets.register(Rationals, Reals)
|
||||
def _(a, b):
|
||||
return a
|
||||
|
||||
def _intlike_interval(a, b):
|
||||
try:
|
||||
if b._inf is S.NegativeInfinity and b._sup is S.Infinity:
|
||||
return a
|
||||
s = Range(max(a.inf, ceiling(b.left)), floor(b.right) + 1)
|
||||
return intersection_sets(s, b) # take out endpoints if open interval
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
@intersection_sets.register(Integers, Interval)
|
||||
def _(a, b):
|
||||
return _intlike_interval(a, b)
|
||||
|
||||
@intersection_sets.register(Naturals, Interval)
|
||||
def _(a, b):
|
||||
return _intlike_interval(a, b)
|
||||
@@ -0,0 +1,144 @@
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.core.logic import fuzzy_and, fuzzy_bool, fuzzy_not, fuzzy_or
|
||||
from sympy.core.relational import Eq
|
||||
from sympy.sets.sets import FiniteSet, Interval, Set, Union, ProductSet
|
||||
from sympy.sets.fancysets import Complexes, Reals, Range, Rationals
|
||||
from sympy.multipledispatch import Dispatcher
|
||||
|
||||
|
||||
_inf_sets = [S.Naturals, S.Naturals0, S.Integers, S.Rationals, S.Reals, S.Complexes]
|
||||
|
||||
|
||||
is_subset_sets = Dispatcher('is_subset_sets')
|
||||
|
||||
|
||||
@is_subset_sets.register(Set, Set)
|
||||
def _(a, b):
|
||||
return None
|
||||
|
||||
@is_subset_sets.register(Interval, Interval)
|
||||
def _(a, b):
|
||||
# This is correct but can be made more comprehensive...
|
||||
if fuzzy_bool(a.start < b.start):
|
||||
return False
|
||||
if fuzzy_bool(a.end > b.end):
|
||||
return False
|
||||
if (b.left_open and not a.left_open and fuzzy_bool(Eq(a.start, b.start))):
|
||||
return False
|
||||
if (b.right_open and not a.right_open and fuzzy_bool(Eq(a.end, b.end))):
|
||||
return False
|
||||
|
||||
@is_subset_sets.register(Interval, FiniteSet)
|
||||
def _(a_interval, b_fs):
|
||||
# An Interval can only be a subset of a finite set if it is finite
|
||||
# which can only happen if it has zero measure.
|
||||
if fuzzy_not(a_interval.measure.is_zero):
|
||||
return False
|
||||
|
||||
@is_subset_sets.register(Interval, Union)
|
||||
def _(a_interval, b_u):
|
||||
if all(isinstance(s, (Interval, FiniteSet)) for s in b_u.args):
|
||||
intervals = [s for s in b_u.args if isinstance(s, Interval)]
|
||||
if all(fuzzy_bool(a_interval.start < s.start) for s in intervals):
|
||||
return False
|
||||
if all(fuzzy_bool(a_interval.end > s.end) for s in intervals):
|
||||
return False
|
||||
if a_interval.measure.is_nonzero:
|
||||
no_overlap = lambda s1, s2: fuzzy_or([
|
||||
fuzzy_bool(s1.end <= s2.start),
|
||||
fuzzy_bool(s1.start >= s2.end),
|
||||
])
|
||||
if all(no_overlap(s, a_interval) for s in intervals):
|
||||
return False
|
||||
|
||||
@is_subset_sets.register(Range, Range)
|
||||
def _(a, b):
|
||||
if a.step == b.step == 1:
|
||||
return fuzzy_and([fuzzy_bool(a.start >= b.start),
|
||||
fuzzy_bool(a.stop <= b.stop)])
|
||||
|
||||
@is_subset_sets.register(Range, Interval)
|
||||
def _(a_range, b_interval):
|
||||
if a_range.step.is_positive:
|
||||
if b_interval.left_open and a_range.inf.is_finite:
|
||||
cond_left = a_range.inf > b_interval.left
|
||||
else:
|
||||
cond_left = a_range.inf >= b_interval.left
|
||||
if b_interval.right_open and a_range.sup.is_finite:
|
||||
cond_right = a_range.sup < b_interval.right
|
||||
else:
|
||||
cond_right = a_range.sup <= b_interval.right
|
||||
return fuzzy_and([cond_left, cond_right])
|
||||
|
||||
@is_subset_sets.register(Range, FiniteSet)
|
||||
def _(a_range, b_finiteset):
|
||||
try:
|
||||
a_size = a_range.size
|
||||
except ValueError:
|
||||
# symbolic Range of unknown size
|
||||
return None
|
||||
if a_size > len(b_finiteset):
|
||||
return False
|
||||
elif any(arg.has(Symbol) for arg in a_range.args):
|
||||
return fuzzy_and(b_finiteset.contains(x) for x in a_range)
|
||||
else:
|
||||
# Checking A \ B == EmptySet is more efficient than repeated naive
|
||||
# membership checks on an arbitrary FiniteSet.
|
||||
a_set = set(a_range)
|
||||
b_remaining = len(b_finiteset)
|
||||
# Symbolic expressions and numbers of unknown type (integer or not) are
|
||||
# all counted as "candidates", i.e. *potentially* matching some a in
|
||||
# a_range.
|
||||
cnt_candidate = 0
|
||||
for b in b_finiteset:
|
||||
if b.is_Integer:
|
||||
a_set.discard(b)
|
||||
elif fuzzy_not(b.is_integer):
|
||||
pass
|
||||
else:
|
||||
cnt_candidate += 1
|
||||
b_remaining -= 1
|
||||
if len(a_set) > b_remaining + cnt_candidate:
|
||||
return False
|
||||
if len(a_set) == 0:
|
||||
return True
|
||||
return None
|
||||
|
||||
@is_subset_sets.register(Interval, Range)
|
||||
def _(a_interval, b_range):
|
||||
if a_interval.measure.is_extended_nonzero:
|
||||
return False
|
||||
|
||||
@is_subset_sets.register(Interval, Rationals)
|
||||
def _(a_interval, b_rationals):
|
||||
if a_interval.measure.is_extended_nonzero:
|
||||
return False
|
||||
|
||||
@is_subset_sets.register(Range, Complexes)
|
||||
def _(a, b):
|
||||
return True
|
||||
|
||||
@is_subset_sets.register(Complexes, Interval)
|
||||
def _(a, b):
|
||||
return False
|
||||
|
||||
@is_subset_sets.register(Complexes, Range)
|
||||
def _(a, b):
|
||||
return False
|
||||
|
||||
@is_subset_sets.register(Complexes, Rationals)
|
||||
def _(a, b):
|
||||
return False
|
||||
|
||||
@is_subset_sets.register(Rationals, Reals)
|
||||
def _(a, b):
|
||||
return True
|
||||
|
||||
@is_subset_sets.register(Rationals, Range)
|
||||
def _(a, b):
|
||||
return False
|
||||
|
||||
@is_subset_sets.register(ProductSet, FiniteSet)
|
||||
def _(a_ps, b_fs):
|
||||
return fuzzy_and(b_fs.contains(x) for x in a_ps)
|
||||
@@ -0,0 +1,79 @@
|
||||
from sympy.core import Basic, Expr
|
||||
from sympy.core.numbers import oo
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.multipledispatch import Dispatcher
|
||||
from sympy.sets.setexpr import set_mul
|
||||
from sympy.sets.sets import Interval, Set
|
||||
|
||||
|
||||
_x, _y = symbols("x y")
|
||||
|
||||
|
||||
_set_mul = Dispatcher('_set_mul')
|
||||
_set_div = Dispatcher('_set_div')
|
||||
|
||||
|
||||
@_set_mul.register(Basic, Basic)
|
||||
def _(x, y):
|
||||
return None
|
||||
|
||||
@_set_mul.register(Set, Set)
|
||||
def _(x, y):
|
||||
return None
|
||||
|
||||
@_set_mul.register(Expr, Expr)
|
||||
def _(x, y):
|
||||
return x*y
|
||||
|
||||
@_set_mul.register(Interval, Interval)
|
||||
def _(x, y):
|
||||
"""
|
||||
Multiplications in interval arithmetic
|
||||
https://en.wikipedia.org/wiki/Interval_arithmetic
|
||||
"""
|
||||
# TODO: some intervals containing 0 and oo will fail as 0*oo returns nan.
|
||||
comvals = (
|
||||
(x.start * y.start, bool(x.left_open or y.left_open)),
|
||||
(x.start * y.end, bool(x.left_open or y.right_open)),
|
||||
(x.end * y.start, bool(x.right_open or y.left_open)),
|
||||
(x.end * y.end, bool(x.right_open or y.right_open)),
|
||||
)
|
||||
# TODO: handle symbolic intervals
|
||||
minval, minopen = min(comvals)
|
||||
maxval, maxopen = max(comvals)
|
||||
return Interval(
|
||||
minval,
|
||||
maxval,
|
||||
minopen,
|
||||
maxopen
|
||||
)
|
||||
|
||||
@_set_div.register(Basic, Basic)
|
||||
def _(x, y):
|
||||
return None
|
||||
|
||||
@_set_div.register(Expr, Expr)
|
||||
def _(x, y):
|
||||
return x/y
|
||||
|
||||
@_set_div.register(Set, Set)
|
||||
def _(x, y):
|
||||
return None
|
||||
|
||||
@_set_div.register(Interval, Interval)
|
||||
def _(x, y):
|
||||
"""
|
||||
Divisions in interval arithmetic
|
||||
https://en.wikipedia.org/wiki/Interval_arithmetic
|
||||
"""
|
||||
if (y.start*y.end).is_negative:
|
||||
return Interval(-oo, oo)
|
||||
if y.start == 0:
|
||||
s2 = oo
|
||||
else:
|
||||
s2 = 1/y.start
|
||||
if y.end == 0:
|
||||
s1 = -oo
|
||||
else:
|
||||
s1 = 1/y.end
|
||||
return set_mul(x, Interval(s1, s2, y.right_open, y.left_open))
|
||||
@@ -0,0 +1,107 @@
|
||||
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)
|
||||
@@ -0,0 +1,147 @@
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.sympify import sympify
|
||||
from sympy.functions.elementary.miscellaneous import Min, Max
|
||||
from sympy.sets.sets import (EmptySet, FiniteSet, Intersection,
|
||||
Interval, ProductSet, Set, Union, UniversalSet)
|
||||
from sympy.sets.fancysets import (ComplexRegion, Naturals, Naturals0,
|
||||
Integers, Rationals, Reals)
|
||||
from sympy.multipledispatch import Dispatcher
|
||||
|
||||
|
||||
union_sets = Dispatcher('union_sets')
|
||||
|
||||
|
||||
@union_sets.register(Naturals0, Naturals)
|
||||
def _(a, b):
|
||||
return a
|
||||
|
||||
@union_sets.register(Rationals, Naturals)
|
||||
def _(a, b):
|
||||
return a
|
||||
|
||||
@union_sets.register(Rationals, Naturals0)
|
||||
def _(a, b):
|
||||
return a
|
||||
|
||||
@union_sets.register(Reals, Naturals)
|
||||
def _(a, b):
|
||||
return a
|
||||
|
||||
@union_sets.register(Reals, Naturals0)
|
||||
def _(a, b):
|
||||
return a
|
||||
|
||||
@union_sets.register(Reals, Rationals)
|
||||
def _(a, b):
|
||||
return a
|
||||
|
||||
@union_sets.register(Integers, Set)
|
||||
def _(a, b):
|
||||
intersect = Intersection(a, b)
|
||||
if intersect == a:
|
||||
return b
|
||||
elif intersect == b:
|
||||
return a
|
||||
|
||||
@union_sets.register(ComplexRegion, Set)
|
||||
def _(a, b):
|
||||
if b.is_subset(S.Reals):
|
||||
# treat a subset of reals as a complex region
|
||||
b = ComplexRegion.from_real(b)
|
||||
|
||||
if b.is_ComplexRegion:
|
||||
# a in rectangular form
|
||||
if (not a.polar) and (not b.polar):
|
||||
return ComplexRegion(Union(a.sets, b.sets))
|
||||
# a in polar form
|
||||
elif a.polar and b.polar:
|
||||
return ComplexRegion(Union(a.sets, b.sets), polar=True)
|
||||
return None
|
||||
|
||||
@union_sets.register(EmptySet, Set)
|
||||
def _(a, b):
|
||||
return b
|
||||
|
||||
|
||||
@union_sets.register(UniversalSet, Set)
|
||||
def _(a, b):
|
||||
return a
|
||||
|
||||
@union_sets.register(ProductSet, ProductSet)
|
||||
def _(a, b):
|
||||
if b.is_subset(a):
|
||||
return a
|
||||
if len(b.sets) != len(a.sets):
|
||||
return None
|
||||
if len(a.sets) == 2:
|
||||
a1, a2 = a.sets
|
||||
b1, b2 = b.sets
|
||||
if a1 == b1:
|
||||
return a1 * Union(a2, b2)
|
||||
if a2 == b2:
|
||||
return Union(a1, b1) * a2
|
||||
return None
|
||||
|
||||
@union_sets.register(ProductSet, Set)
|
||||
def _(a, b):
|
||||
if b.is_subset(a):
|
||||
return a
|
||||
return None
|
||||
|
||||
@union_sets.register(Interval, Interval)
|
||||
def _(a, b):
|
||||
if a._is_comparable(b):
|
||||
# Non-overlapping intervals
|
||||
end = Min(a.end, b.end)
|
||||
start = Max(a.start, b.start)
|
||||
if (end < start or
|
||||
(end == start and (end not in a and end not in b))):
|
||||
return None
|
||||
else:
|
||||
start = Min(a.start, b.start)
|
||||
end = Max(a.end, b.end)
|
||||
|
||||
left_open = ((a.start != start or a.left_open) and
|
||||
(b.start != start or b.left_open))
|
||||
right_open = ((a.end != end or a.right_open) and
|
||||
(b.end != end or b.right_open))
|
||||
return Interval(start, end, left_open, right_open)
|
||||
|
||||
@union_sets.register(Interval, UniversalSet)
|
||||
def _(a, b):
|
||||
return S.UniversalSet
|
||||
|
||||
@union_sets.register(Interval, Set)
|
||||
def _(a, b):
|
||||
# If I have open end points and these endpoints are contained in b
|
||||
# But only in case, when endpoints are finite. Because
|
||||
# interval does not contain oo or -oo.
|
||||
open_left_in_b_and_finite = (a.left_open and
|
||||
sympify(b.contains(a.start)) is S.true and
|
||||
a.start.is_finite)
|
||||
open_right_in_b_and_finite = (a.right_open and
|
||||
sympify(b.contains(a.end)) is S.true and
|
||||
a.end.is_finite)
|
||||
if open_left_in_b_and_finite or open_right_in_b_and_finite:
|
||||
# Fill in my end points and return
|
||||
open_left = a.left_open and a.start not in b
|
||||
open_right = a.right_open and a.end not in b
|
||||
new_a = Interval(a.start, a.end, open_left, open_right)
|
||||
return {new_a, b}
|
||||
return None
|
||||
|
||||
@union_sets.register(FiniteSet, FiniteSet)
|
||||
def _(a, b):
|
||||
return FiniteSet(*(a._elements | b._elements))
|
||||
|
||||
@union_sets.register(FiniteSet, Set)
|
||||
def _(a, b):
|
||||
# If `b` set contains one of my elements, remove it from `a`
|
||||
if any(b.contains(x) == True for x in a):
|
||||
return {
|
||||
FiniteSet(*[x for x in a if b.contains(x) != True]), b}
|
||||
return None
|
||||
|
||||
@union_sets.register(Set, Set)
|
||||
def _(a, b):
|
||||
return None
|
||||
Reference in New Issue
Block a user