AI Newsletter Digest improvements: fixed QP soft line break decoding, URL extraction, and content cleaning
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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