I am done

This commit is contained in:
2024-10-30 22:14:35 +01:00
parent 720dc28c09
commit 40e2a747cf
36901 changed files with 5011519 additions and 0 deletions

View File

@ -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)

View File

@ -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)])]

View File

@ -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))

View File

@ -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)

View File

@ -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)

View File

@ -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))

View File

@ -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)

View File

@ -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