deltafunctions.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. from sympy.core.mul import Mul
  2. from sympy.core.singleton import S
  3. from sympy.core.sorting import default_sort_key
  4. from sympy.functions import DiracDelta, Heaviside
  5. from .integrals import Integral, integrate
  6. from sympy.solvers import solve
  7. def change_mul(node, x):
  8. """change_mul(node, x)
  9. Rearranges the operands of a product, bringing to front any simple
  10. DiracDelta expression.
  11. Explanation
  12. ===========
  13. If no simple DiracDelta expression was found, then all the DiracDelta
  14. expressions are simplified (using DiracDelta.expand(diracdelta=True, wrt=x)).
  15. Return: (dirac, new node)
  16. Where:
  17. o dirac is either a simple DiracDelta expression or None (if no simple
  18. expression was found);
  19. o new node is either a simplified DiracDelta expressions or None (if it
  20. could not be simplified).
  21. Examples
  22. ========
  23. >>> from sympy import DiracDelta, cos
  24. >>> from sympy.integrals.deltafunctions import change_mul
  25. >>> from sympy.abc import x, y
  26. >>> change_mul(x*y*DiracDelta(x)*cos(x), x)
  27. (DiracDelta(x), x*y*cos(x))
  28. >>> change_mul(x*y*DiracDelta(x**2 - 1)*cos(x), x)
  29. (None, x*y*cos(x)*DiracDelta(x - 1)/2 + x*y*cos(x)*DiracDelta(x + 1)/2)
  30. >>> change_mul(x*y*DiracDelta(cos(x))*cos(x), x)
  31. (None, None)
  32. See Also
  33. ========
  34. sympy.functions.special.delta_functions.DiracDelta
  35. deltaintegrate
  36. """
  37. new_args = []
  38. dirac = None
  39. #Sorting is needed so that we consistently collapse the same delta;
  40. #However, we must preserve the ordering of non-commutative terms
  41. c, nc = node.args_cnc()
  42. sorted_args = sorted(c, key=default_sort_key)
  43. sorted_args.extend(nc)
  44. for arg in sorted_args:
  45. if arg.is_Pow and isinstance(arg.base, DiracDelta):
  46. new_args.append(arg.func(arg.base, arg.exp - 1))
  47. arg = arg.base
  48. if dirac is None and (isinstance(arg, DiracDelta) and arg.is_simple(x)):
  49. dirac = arg
  50. else:
  51. new_args.append(arg)
  52. if not dirac: # there was no simple dirac
  53. new_args = []
  54. for arg in sorted_args:
  55. if isinstance(arg, DiracDelta):
  56. new_args.append(arg.expand(diracdelta=True, wrt=x))
  57. elif arg.is_Pow and isinstance(arg.base, DiracDelta):
  58. new_args.append(arg.func(arg.base.expand(diracdelta=True, wrt=x), arg.exp))
  59. else:
  60. new_args.append(arg)
  61. if new_args != sorted_args:
  62. nnode = Mul(*new_args).expand()
  63. else: # if the node didn't change there is nothing to do
  64. nnode = None
  65. return (None, nnode)
  66. return (dirac, Mul(*new_args))
  67. def deltaintegrate(f, x):
  68. """
  69. deltaintegrate(f, x)
  70. Explanation
  71. ===========
  72. The idea for integration is the following:
  73. - If we are dealing with a DiracDelta expression, i.e. DiracDelta(g(x)),
  74. we try to simplify it.
  75. If we could simplify it, then we integrate the resulting expression.
  76. We already know we can integrate a simplified expression, because only
  77. simple DiracDelta expressions are involved.
  78. If we couldn't simplify it, there are two cases:
  79. 1) The expression is a simple expression: we return the integral,
  80. taking care if we are dealing with a Derivative or with a proper
  81. DiracDelta.
  82. 2) The expression is not simple (i.e. DiracDelta(cos(x))): we can do
  83. nothing at all.
  84. - If the node is a multiplication node having a DiracDelta term:
  85. First we expand it.
  86. If the expansion did work, then we try to integrate the expansion.
  87. If not, we try to extract a simple DiracDelta term, then we have two
  88. cases:
  89. 1) We have a simple DiracDelta term, so we return the integral.
  90. 2) We didn't have a simple term, but we do have an expression with
  91. simplified DiracDelta terms, so we integrate this expression.
  92. Examples
  93. ========
  94. >>> from sympy.abc import x, y, z
  95. >>> from sympy.integrals.deltafunctions import deltaintegrate
  96. >>> from sympy import sin, cos, DiracDelta
  97. >>> deltaintegrate(x*sin(x)*cos(x)*DiracDelta(x - 1), x)
  98. sin(1)*cos(1)*Heaviside(x - 1)
  99. >>> deltaintegrate(y**2*DiracDelta(x - z)*DiracDelta(y - z), y)
  100. z**2*DiracDelta(x - z)*Heaviside(y - z)
  101. See Also
  102. ========
  103. sympy.functions.special.delta_functions.DiracDelta
  104. sympy.integrals.integrals.Integral
  105. """
  106. if not f.has(DiracDelta):
  107. return None
  108. # g(x) = DiracDelta(h(x))
  109. if f.func == DiracDelta:
  110. h = f.expand(diracdelta=True, wrt=x)
  111. if h == f: # can't simplify the expression
  112. #FIXME: the second term tells whether is DeltaDirac or Derivative
  113. #For integrating derivatives of DiracDelta we need the chain rule
  114. if f.is_simple(x):
  115. if (len(f.args) <= 1 or f.args[1] == 0):
  116. return Heaviside(f.args[0])
  117. else:
  118. return (DiracDelta(f.args[0], f.args[1] - 1) /
  119. f.args[0].as_poly().LC())
  120. else: # let's try to integrate the simplified expression
  121. fh = integrate(h, x)
  122. return fh
  123. elif f.is_Mul or f.is_Pow: # g(x) = a*b*c*f(DiracDelta(h(x)))*d*e
  124. g = f.expand()
  125. if f != g: # the expansion worked
  126. fh = integrate(g, x)
  127. if fh is not None and not isinstance(fh, Integral):
  128. return fh
  129. else:
  130. # no expansion performed, try to extract a simple DiracDelta term
  131. deltaterm, rest_mult = change_mul(f, x)
  132. if not deltaterm:
  133. if rest_mult:
  134. fh = integrate(rest_mult, x)
  135. return fh
  136. else:
  137. deltaterm = deltaterm.expand(diracdelta=True, wrt=x)
  138. if deltaterm.is_Mul: # Take out any extracted factors
  139. deltaterm, rest_mult_2 = change_mul(deltaterm, x)
  140. rest_mult = rest_mult*rest_mult_2
  141. point = solve(deltaterm.args[0], x)[0]
  142. # Return the largest hyperreal term left after
  143. # repeated integration by parts. For example,
  144. #
  145. # integrate(y*DiracDelta(x, 1),x) == y*DiracDelta(x,0), not 0
  146. #
  147. # This is so Integral(y*DiracDelta(x).diff(x),x).doit()
  148. # will return y*DiracDelta(x) instead of 0 or DiracDelta(x),
  149. # both of which are correct everywhere the value is defined
  150. # but give wrong answers for nested integration.
  151. n = (0 if len(deltaterm.args)==1 else deltaterm.args[1])
  152. m = 0
  153. while n >= 0:
  154. r = S.NegativeOne**n*rest_mult.diff(x, n).subs(x, point)
  155. if r.is_zero:
  156. n -= 1
  157. m += 1
  158. else:
  159. if m == 0:
  160. return r*Heaviside(x - point)
  161. else:
  162. return r*DiracDelta(x,m-1)
  163. # In some very weak sense, x=0 is still a singularity,
  164. # but we hope will not be of any practical consequence.
  165. return S.Zero
  166. return None