wrapper.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  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 definded 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.assumptions import (_assume_defined, as_property,
  38. ManagedProperties)
  39. from sympy.core.basic import Basic
  40. from sympy.core.sympify import _sympify
  41. class AssumptionsWrapperMeta(ManagedProperties):
  42. """
  43. Metaclass to give _eval_is_[...] attributes to AssumptionsWrapper
  44. """
  45. def __init__(cls, *args, **kws):
  46. for fact in _assume_defined:
  47. pname = "_eval_%s" % as_property(fact)
  48. setattr(cls, pname, make_eval_method(fact))
  49. super().__init__(cls, *args, **kws)
  50. def make_eval_method(fact):
  51. def getit(self):
  52. try:
  53. pred = getattr(Q, fact)
  54. ret = ask(pred(self.expr), self.assumptions)
  55. return ret
  56. except AttributeError:
  57. return None
  58. return getit
  59. # we subclass Basic to use the fact deduction and caching
  60. class AssumptionsWrapper(Basic, metaclass=AssumptionsWrapperMeta):
  61. """
  62. Wrapper over ``Basic`` instances to call predicate query by
  63. ``.is_[...]`` property
  64. Parameters
  65. ==========
  66. expr : Basic
  67. assumptions : Boolean, optional
  68. Examples
  69. ========
  70. >>> from sympy import Q, Symbol
  71. >>> from sympy.assumptions.wrapper import AssumptionsWrapper
  72. >>> x = Symbol('x', even=True)
  73. >>> AssumptionsWrapper(x).is_integer
  74. True
  75. >>> y = Symbol('y')
  76. >>> AssumptionsWrapper(y, Q.even(y)).is_integer
  77. True
  78. With ``AssumptionsWrapper``, both evaluation and refinement can be supported
  79. by single implementation.
  80. >>> from sympy import Function
  81. >>> class MyAbs(Function):
  82. ... @classmethod
  83. ... def eval(cls, x, assumptions=True):
  84. ... _x = AssumptionsWrapper(x, assumptions)
  85. ... if _x.is_nonnegative:
  86. ... return x
  87. ... if _x.is_negative:
  88. ... return -x
  89. ... def _eval_refine(self, assumptions):
  90. ... return MyAbs.eval(self.args[0], assumptions)
  91. >>> MyAbs(x)
  92. MyAbs(x)
  93. >>> MyAbs(x).refine(Q.positive(x))
  94. x
  95. >>> MyAbs(Symbol('y', negative=True))
  96. -y
  97. """
  98. def __new__(cls, expr, assumptions=None):
  99. if assumptions is None:
  100. return expr
  101. obj = super().__new__(cls, expr, _sympify(assumptions))
  102. obj.expr = expr
  103. obj.assumptions = assumptions
  104. return obj
  105. # one shot functions which are faster than AssumptionsWrapper
  106. def is_infinite(obj, assumptions=None):
  107. if assumptions is None:
  108. return obj.is_infinite
  109. return ask(Q.infinite(obj), assumptions)
  110. def is_extended_real(obj, assumptions=None):
  111. if assumptions is None:
  112. return obj.is_extended_real
  113. return ask(Q.extended_real(obj), assumptions)
  114. def is_extended_nonnegative(obj, assumptions=None):
  115. if assumptions is None:
  116. return obj.is_extended_nonnegative
  117. return ask(Q.extended_nonnegative(obj), assumptions)