wrapper.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. """
  2. Functions and wrapper object to call assumption property and predicate
  3. query with same syntax.
  4. In SymPy, there are two assumption systems. Old assumption system is
  5. defined in sympy/core/assumptions, and it can be accessed by attribute
  6. such as ``x.is_even``. New assumption system is defined in
  7. sympy/assumptions, and it can be accessed by predicates such as
  8. ``Q.even(x)``.
  9. Old assumption is fast, while new assumptions can freely take local facts.
  10. In general, old assumption is used in evaluation method and new assumption
  11. is used in refinement method.
  12. In most cases, both evaluation and refinement follow the same process, and
  13. the only difference is which assumption system is used. This module provides
  14. ``is_[...]()`` functions and ``AssumptionsWrapper()`` class which allows
  15. using two systems with same syntax so that parallel code implementation can be
  16. avoided.
  17. Examples
  18. ========
  19. For multiple use, use ``AssumptionsWrapper()``.
  20. >>> from sympy import Q, Symbol
  21. >>> from sympy.assumptions.wrapper import AssumptionsWrapper
  22. >>> x = Symbol('x')
  23. >>> _x = AssumptionsWrapper(x, Q.even(x))
  24. >>> _x.is_integer
  25. True
  26. >>> _x.is_odd
  27. False
  28. For single use, use ``is_[...]()`` functions.
  29. >>> from sympy.assumptions.wrapper import is_infinite
  30. >>> a = Symbol('a')
  31. >>> print(is_infinite(a))
  32. None
  33. >>> is_infinite(a, Q.finite(a))
  34. False
  35. """
  36. from sympy.assumptions import ask, Q
  37. from sympy.core.basic import Basic
  38. from sympy.core.sympify import _sympify
  39. def make_eval_method(fact):
  40. def getit(self):
  41. try:
  42. pred = getattr(Q, fact)
  43. ret = ask(pred(self.expr), self.assumptions)
  44. return ret
  45. except AttributeError:
  46. return None
  47. return getit
  48. # we subclass Basic to use the fact deduction and caching
  49. class AssumptionsWrapper(Basic):
  50. """
  51. Wrapper over ``Basic`` instances to call predicate query by
  52. ``.is_[...]`` property
  53. Parameters
  54. ==========
  55. expr : Basic
  56. assumptions : Boolean, optional
  57. Examples
  58. ========
  59. >>> from sympy import Q, Symbol
  60. >>> from sympy.assumptions.wrapper import AssumptionsWrapper
  61. >>> x = Symbol('x', even=True)
  62. >>> AssumptionsWrapper(x).is_integer
  63. True
  64. >>> y = Symbol('y')
  65. >>> AssumptionsWrapper(y, Q.even(y)).is_integer
  66. True
  67. With ``AssumptionsWrapper``, both evaluation and refinement can be supported
  68. by single implementation.
  69. >>> from sympy import Function
  70. >>> class MyAbs(Function):
  71. ... @classmethod
  72. ... def eval(cls, x, assumptions=True):
  73. ... _x = AssumptionsWrapper(x, assumptions)
  74. ... if _x.is_nonnegative:
  75. ... return x
  76. ... if _x.is_negative:
  77. ... return -x
  78. ... def _eval_refine(self, assumptions):
  79. ... return MyAbs.eval(self.args[0], assumptions)
  80. >>> MyAbs(x)
  81. MyAbs(x)
  82. >>> MyAbs(x).refine(Q.positive(x))
  83. x
  84. >>> MyAbs(Symbol('y', negative=True))
  85. -y
  86. """
  87. def __new__(cls, expr, assumptions=None):
  88. if assumptions is None:
  89. return expr
  90. obj = super().__new__(cls, expr, _sympify(assumptions))
  91. obj.expr = expr
  92. obj.assumptions = assumptions
  93. return obj
  94. _eval_is_algebraic = make_eval_method("algebraic")
  95. _eval_is_antihermitian = make_eval_method("antihermitian")
  96. _eval_is_commutative = make_eval_method("commutative")
  97. _eval_is_complex = make_eval_method("complex")
  98. _eval_is_composite = make_eval_method("composite")
  99. _eval_is_even = make_eval_method("even")
  100. _eval_is_extended_negative = make_eval_method("extended_negative")
  101. _eval_is_extended_nonnegative = make_eval_method("extended_nonnegative")
  102. _eval_is_extended_nonpositive = make_eval_method("extended_nonpositive")
  103. _eval_is_extended_nonzero = make_eval_method("extended_nonzero")
  104. _eval_is_extended_positive = make_eval_method("extended_positive")
  105. _eval_is_extended_real = make_eval_method("extended_real")
  106. _eval_is_finite = make_eval_method("finite")
  107. _eval_is_hermitian = make_eval_method("hermitian")
  108. _eval_is_imaginary = make_eval_method("imaginary")
  109. _eval_is_infinite = make_eval_method("infinite")
  110. _eval_is_integer = make_eval_method("integer")
  111. _eval_is_irrational = make_eval_method("irrational")
  112. _eval_is_negative = make_eval_method("negative")
  113. _eval_is_noninteger = make_eval_method("noninteger")
  114. _eval_is_nonnegative = make_eval_method("nonnegative")
  115. _eval_is_nonpositive = make_eval_method("nonpositive")
  116. _eval_is_nonzero = make_eval_method("nonzero")
  117. _eval_is_odd = make_eval_method("odd")
  118. _eval_is_polar = make_eval_method("polar")
  119. _eval_is_positive = make_eval_method("positive")
  120. _eval_is_prime = make_eval_method("prime")
  121. _eval_is_rational = make_eval_method("rational")
  122. _eval_is_real = make_eval_method("real")
  123. _eval_is_transcendental = make_eval_method("transcendental")
  124. _eval_is_zero = make_eval_method("zero")
  125. # one shot functions which are faster than AssumptionsWrapper
  126. def is_infinite(obj, assumptions=None):
  127. if assumptions is None:
  128. return obj.is_infinite
  129. return ask(Q.infinite(obj), assumptions)
  130. def is_extended_real(obj, assumptions=None):
  131. if assumptions is None:
  132. return obj.is_extended_real
  133. return ask(Q.extended_real(obj), assumptions)
  134. def is_extended_nonnegative(obj, assumptions=None):
  135. if assumptions is None:
  136. return obj.is_extended_nonnegative
  137. return ask(Q.extended_nonnegative(obj), assumptions)