singularity_functions.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. from sympy.core import S, oo, diff
  2. from sympy.core.function import Function, ArgumentIndexError
  3. from sympy.core.logic import fuzzy_not
  4. from sympy.core.relational import Eq
  5. from sympy.functions.elementary.complexes import im
  6. from sympy.functions.elementary.piecewise import Piecewise
  7. from sympy.functions.special.delta_functions import Heaviside
  8. ###############################################################################
  9. ############################# SINGULARITY FUNCTION ############################
  10. ###############################################################################
  11. class SingularityFunction(Function):
  12. r"""
  13. Singularity functions are a class of discontinuous functions.
  14. Explanation
  15. ===========
  16. Singularity functions take a variable, an offset, and an exponent as
  17. arguments. These functions are represented using Macaulay brackets as:
  18. SingularityFunction(x, a, n) := <x - a>^n
  19. The singularity function will automatically evaluate to
  20. ``Derivative(DiracDelta(x - a), x, -n - 1)`` if ``n < 0``
  21. and ``(x - a)**n*Heaviside(x - a)`` if ``n >= 0``.
  22. Examples
  23. ========
  24. >>> from sympy import SingularityFunction, diff, Piecewise, DiracDelta, Heaviside, Symbol
  25. >>> from sympy.abc import x, a, n
  26. >>> SingularityFunction(x, a, n)
  27. SingularityFunction(x, a, n)
  28. >>> y = Symbol('y', positive=True)
  29. >>> n = Symbol('n', nonnegative=True)
  30. >>> SingularityFunction(y, -10, n)
  31. (y + 10)**n
  32. >>> y = Symbol('y', negative=True)
  33. >>> SingularityFunction(y, 10, n)
  34. 0
  35. >>> SingularityFunction(x, 4, -1).subs(x, 4)
  36. oo
  37. >>> SingularityFunction(x, 10, -2).subs(x, 10)
  38. oo
  39. >>> SingularityFunction(4, 1, 5)
  40. 243
  41. >>> diff(SingularityFunction(x, 1, 5) + SingularityFunction(x, 1, 4), x)
  42. 4*SingularityFunction(x, 1, 3) + 5*SingularityFunction(x, 1, 4)
  43. >>> diff(SingularityFunction(x, 4, 0), x, 2)
  44. SingularityFunction(x, 4, -2)
  45. >>> SingularityFunction(x, 4, 5).rewrite(Piecewise)
  46. Piecewise(((x - 4)**5, x > 4), (0, True))
  47. >>> expr = SingularityFunction(x, a, n)
  48. >>> y = Symbol('y', positive=True)
  49. >>> n = Symbol('n', nonnegative=True)
  50. >>> expr.subs({x: y, a: -10, n: n})
  51. (y + 10)**n
  52. The methods ``rewrite(DiracDelta)``, ``rewrite(Heaviside)``, and
  53. ``rewrite('HeavisideDiracDelta')`` returns the same output. One can use any
  54. of these methods according to their choice.
  55. >>> expr = SingularityFunction(x, 4, 5) + SingularityFunction(x, -3, -1) - SingularityFunction(x, 0, -2)
  56. >>> expr.rewrite(Heaviside)
  57. (x - 4)**5*Heaviside(x - 4) + DiracDelta(x + 3) - DiracDelta(x, 1)
  58. >>> expr.rewrite(DiracDelta)
  59. (x - 4)**5*Heaviside(x - 4) + DiracDelta(x + 3) - DiracDelta(x, 1)
  60. >>> expr.rewrite('HeavisideDiracDelta')
  61. (x - 4)**5*Heaviside(x - 4) + DiracDelta(x + 3) - DiracDelta(x, 1)
  62. See Also
  63. ========
  64. DiracDelta, Heaviside
  65. References
  66. ==========
  67. .. [1] https://en.wikipedia.org/wiki/Singularity_function
  68. """
  69. is_real = True
  70. def fdiff(self, argindex=1):
  71. """
  72. Returns the first derivative of a DiracDelta Function.
  73. Explanation
  74. ===========
  75. The difference between ``diff()`` and ``fdiff()`` is: ``diff()`` is the
  76. user-level function and ``fdiff()`` is an object method. ``fdiff()`` is
  77. a convenience method available in the ``Function`` class. It returns
  78. the derivative of the function without considering the chain rule.
  79. ``diff(function, x)`` calls ``Function._eval_derivative`` which in turn
  80. calls ``fdiff()`` internally to compute the derivative of the function.
  81. """
  82. if argindex == 1:
  83. x, a, n = self.args
  84. if n in (S.Zero, S.NegativeOne):
  85. return self.func(x, a, n-1)
  86. elif n.is_positive:
  87. return n*self.func(x, a, n-1)
  88. else:
  89. raise ArgumentIndexError(self, argindex)
  90. @classmethod
  91. def eval(cls, variable, offset, exponent):
  92. """
  93. Returns a simplified form or a value of Singularity Function depending
  94. on the argument passed by the object.
  95. Explanation
  96. ===========
  97. The ``eval()`` method is automatically called when the
  98. ``SingularityFunction`` class is about to be instantiated and it
  99. returns either some simplified instance or the unevaluated instance
  100. depending on the argument passed. In other words, ``eval()`` method is
  101. not needed to be called explicitly, it is being called and evaluated
  102. once the object is called.
  103. Examples
  104. ========
  105. >>> from sympy import SingularityFunction, Symbol, nan
  106. >>> from sympy.abc import x, a, n
  107. >>> SingularityFunction(x, a, n)
  108. SingularityFunction(x, a, n)
  109. >>> SingularityFunction(5, 3, 2)
  110. 4
  111. >>> SingularityFunction(x, a, nan)
  112. nan
  113. >>> SingularityFunction(x, 3, 0).subs(x, 3)
  114. 1
  115. >>> SingularityFunction(4, 1, 5)
  116. 243
  117. >>> x = Symbol('x', positive = True)
  118. >>> a = Symbol('a', negative = True)
  119. >>> n = Symbol('n', nonnegative = True)
  120. >>> SingularityFunction(x, a, n)
  121. (-a + x)**n
  122. >>> x = Symbol('x', negative = True)
  123. >>> a = Symbol('a', positive = True)
  124. >>> SingularityFunction(x, a, n)
  125. 0
  126. """
  127. x = variable
  128. a = offset
  129. n = exponent
  130. shift = (x - a)
  131. if fuzzy_not(im(shift).is_zero):
  132. raise ValueError("Singularity Functions are defined only for Real Numbers.")
  133. if fuzzy_not(im(n).is_zero):
  134. raise ValueError("Singularity Functions are not defined for imaginary exponents.")
  135. if shift is S.NaN or n is S.NaN:
  136. return S.NaN
  137. if (n + 2).is_negative:
  138. raise ValueError("Singularity Functions are not defined for exponents less than -2.")
  139. if shift.is_extended_negative:
  140. return S.Zero
  141. if n.is_nonnegative and shift.is_extended_nonnegative:
  142. return (x - a)**n
  143. if n in (S.NegativeOne, -2):
  144. if shift.is_negative or shift.is_extended_positive:
  145. return S.Zero
  146. if shift.is_zero:
  147. return oo
  148. def _eval_rewrite_as_Piecewise(self, *args, **kwargs):
  149. '''
  150. Converts a Singularity Function expression into its Piecewise form.
  151. '''
  152. x, a, n = self.args
  153. if n in (S.NegativeOne, S(-2)):
  154. return Piecewise((oo, Eq((x - a), 0)), (0, True))
  155. elif n.is_nonnegative:
  156. return Piecewise(((x - a)**n, (x - a) > 0), (0, True))
  157. def _eval_rewrite_as_Heaviside(self, *args, **kwargs):
  158. '''
  159. Rewrites a Singularity Function expression using Heavisides and DiracDeltas.
  160. '''
  161. x, a, n = self.args
  162. if n == -2:
  163. return diff(Heaviside(x - a), x.free_symbols.pop(), 2)
  164. if n == -1:
  165. return diff(Heaviside(x - a), x.free_symbols.pop(), 1)
  166. if n.is_nonnegative:
  167. return (x - a)**n*Heaviside(x - a)
  168. def _eval_as_leading_term(self, x, logx=None, cdir=0):
  169. z, a, n = self.args
  170. shift = (z - a).subs(x, 0)
  171. if n < 0:
  172. return S.Zero
  173. elif n.is_zero and shift.is_zero:
  174. return S.Zero if cdir == -1 else S.One
  175. elif shift.is_positive:
  176. return shift**n
  177. return S.Zero
  178. def _eval_nseries(self, x, n, logx=None, cdir=0):
  179. z, a, n = self.args
  180. shift = (z - a).subs(x, 0)
  181. if n < 0:
  182. return S.Zero
  183. elif n.is_zero and shift.is_zero:
  184. return S.Zero if cdir == -1 else S.One
  185. elif shift.is_positive:
  186. return ((z - a)**n)._eval_nseries(x, n, logx=logx, cdir=cdir)
  187. return S.Zero
  188. _eval_rewrite_as_DiracDelta = _eval_rewrite_as_Heaviside
  189. _eval_rewrite_as_HeavisideDiracDelta = _eval_rewrite_as_Heaviside