issubset.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. from sympy.core.singleton import S
  2. from sympy.core.symbol import Symbol
  3. from sympy.core.logic import fuzzy_and, fuzzy_bool, fuzzy_not, fuzzy_or
  4. from sympy.core.relational import Eq
  5. from sympy.sets.sets import FiniteSet, Interval, Set, Union, ProductSet
  6. from sympy.sets.fancysets import Complexes, Reals, Range, Rationals
  7. from sympy.multipledispatch import Dispatcher
  8. _inf_sets = [S.Naturals, S.Naturals0, S.Integers, S.Rationals, S.Reals, S.Complexes]
  9. is_subset_sets = Dispatcher('is_subset_sets')
  10. @is_subset_sets.register(Set, Set)
  11. def _(a, b):
  12. return None
  13. @is_subset_sets.register(Interval, Interval)
  14. def _(a, b):
  15. # This is correct but can be made more comprehensive...
  16. if fuzzy_bool(a.start < b.start):
  17. return False
  18. if fuzzy_bool(a.end > b.end):
  19. return False
  20. if (b.left_open and not a.left_open and fuzzy_bool(Eq(a.start, b.start))):
  21. return False
  22. if (b.right_open and not a.right_open and fuzzy_bool(Eq(a.end, b.end))):
  23. return False
  24. @is_subset_sets.register(Interval, FiniteSet)
  25. def _(a_interval, b_fs):
  26. # An Interval can only be a subset of a finite set if it is finite
  27. # which can only happen if it has zero measure.
  28. if fuzzy_not(a_interval.measure.is_zero):
  29. return False
  30. @is_subset_sets.register(Interval, Union)
  31. def _(a_interval, b_u):
  32. if all(isinstance(s, (Interval, FiniteSet)) for s in b_u.args):
  33. intervals = [s for s in b_u.args if isinstance(s, Interval)]
  34. if all(fuzzy_bool(a_interval.start < s.start) for s in intervals):
  35. return False
  36. if all(fuzzy_bool(a_interval.end > s.end) for s in intervals):
  37. return False
  38. if a_interval.measure.is_nonzero:
  39. no_overlap = lambda s1, s2: fuzzy_or([
  40. fuzzy_bool(s1.end <= s2.start),
  41. fuzzy_bool(s1.start >= s2.end),
  42. ])
  43. if all(no_overlap(s, a_interval) for s in intervals):
  44. return False
  45. @is_subset_sets.register(Range, Range)
  46. def _(a, b):
  47. if a.step == b.step == 1:
  48. return fuzzy_and([fuzzy_bool(a.start >= b.start),
  49. fuzzy_bool(a.stop <= b.stop)])
  50. @is_subset_sets.register(Range, Interval)
  51. def _(a_range, b_interval):
  52. if a_range.step.is_positive:
  53. if b_interval.left_open and a_range.inf.is_finite:
  54. cond_left = a_range.inf > b_interval.left
  55. else:
  56. cond_left = a_range.inf >= b_interval.left
  57. if b_interval.right_open and a_range.sup.is_finite:
  58. cond_right = a_range.sup < b_interval.right
  59. else:
  60. cond_right = a_range.sup <= b_interval.right
  61. return fuzzy_and([cond_left, cond_right])
  62. @is_subset_sets.register(Range, FiniteSet)
  63. def _(a_range, b_finiteset):
  64. try:
  65. a_size = a_range.size
  66. except ValueError:
  67. # symbolic Range of unknown size
  68. return None
  69. if a_size > len(b_finiteset):
  70. return False
  71. elif any(arg.has(Symbol) for arg in a_range.args):
  72. return fuzzy_and(b_finiteset.contains(x) for x in a_range)
  73. else:
  74. # Checking A \ B == EmptySet is more efficient than repeated naive
  75. # membership checks on an arbitrary FiniteSet.
  76. a_set = set(a_range)
  77. b_remaining = len(b_finiteset)
  78. # Symbolic expressions and numbers of unknown type (integer or not) are
  79. # all counted as "candidates", i.e. *potentially* matching some a in
  80. # a_range.
  81. cnt_candidate = 0
  82. for b in b_finiteset:
  83. if b.is_Integer:
  84. a_set.discard(b)
  85. elif fuzzy_not(b.is_integer):
  86. pass
  87. else:
  88. cnt_candidate += 1
  89. b_remaining -= 1
  90. if len(a_set) > b_remaining + cnt_candidate:
  91. return False
  92. if len(a_set) == 0:
  93. return True
  94. return None
  95. @is_subset_sets.register(Interval, Range)
  96. def _(a_interval, b_range):
  97. if a_interval.measure.is_extended_nonzero:
  98. return False
  99. @is_subset_sets.register(Interval, Rationals)
  100. def _(a_interval, b_rationals):
  101. if a_interval.measure.is_extended_nonzero:
  102. return False
  103. @is_subset_sets.register(Range, Complexes)
  104. def _(a, b):
  105. return True
  106. @is_subset_sets.register(Complexes, Interval)
  107. def _(a, b):
  108. return False
  109. @is_subset_sets.register(Complexes, Range)
  110. def _(a, b):
  111. return False
  112. @is_subset_sets.register(Complexes, Rationals)
  113. def _(a, b):
  114. return False
  115. @is_subset_sets.register(Rationals, Reals)
  116. def _(a, b):
  117. return True
  118. @is_subset_sets.register(Rationals, Range)
  119. def _(a, b):
  120. return False
  121. @is_subset_sets.register(ProductSet, FiniteSet)
  122. def _(a_ps, b_fs):
  123. return fuzzy_and(b_fs.contains(x) for x in a_ps)