145 lines
4.6 KiB
Python
145 lines
4.6 KiB
Python
|
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)
|