deltafunctions.py 7.3 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. def change_mul(node, x):
  7. """change_mul(node, x)
  8. Rearranges the operands of a product, bringing to front any simple
  9. DiracDelta expression.
  10. Explanation
  11. ===========
  12. If no simple DiracDelta expression was found, then all the DiracDelta
  13. expressions are simplified (using DiracDelta.expand(diracdelta=True, wrt=x)).
  14. Return: (dirac, new node)
  15. Where:
  16. o dirac is either a simple DiracDelta expression or None (if no simple
  17. expression was found);
  18. o new node is either a simplified DiracDelta expressions or None (if it
  19. could not be simplified).
  20. Examples
  21. ========
  22. >>> from sympy import DiracDelta, cos
  23. >>> from sympy.integrals.deltafunctions import change_mul
  24. >>> from sympy.abc import x, y
  25. >>> change_mul(x*y*DiracDelta(x)*cos(x), x)
  26. (DiracDelta(x), x*y*cos(x))
  27. >>> change_mul(x*y*DiracDelta(x**2 - 1)*cos(x), x)
  28. (None, x*y*cos(x)*DiracDelta(x - 1)/2 + x*y*cos(x)*DiracDelta(x + 1)/2)
  29. >>> change_mul(x*y*DiracDelta(cos(x))*cos(x), x)
  30. (None, None)
  31. See Also
  32. ========
  33. sympy.functions.special.delta_functions.DiracDelta
  34. deltaintegrate
  35. """
  36. new_args = []
  37. dirac = None
  38. #Sorting is needed so that we consistently collapse the same delta;
  39. #However, we must preserve the ordering of non-commutative terms
  40. c, nc = node.args_cnc()
  41. sorted_args = sorted(c, key=default_sort_key)
  42. sorted_args.extend(nc)
  43. for arg in sorted_args:
  44. if arg.is_Pow and isinstance(arg.base, DiracDelta):
  45. new_args.append(arg.func(arg.base, arg.exp - 1))
  46. arg = arg.base
  47. if dirac is None and (isinstance(arg, DiracDelta) and arg.is_simple(x)):
  48. dirac = arg
  49. else:
  50. new_args.append(arg)
  51. if not dirac: # there was no simple dirac
  52. new_args = []
  53. for arg in sorted_args:
  54. if isinstance(arg, DiracDelta):
  55. new_args.append(arg.expand(diracdelta=True, wrt=x))
  56. elif arg.is_Pow and isinstance(arg.base, DiracDelta):
  57. new_args.append(arg.func(arg.base.expand(diracdelta=True, wrt=x), arg.exp))
  58. else:
  59. new_args.append(arg)
  60. if new_args != sorted_args:
  61. nnode = Mul(*new_args).expand()
  62. else: # if the node didn't change there is nothing to do
  63. nnode = None
  64. return (None, nnode)
  65. return (dirac, Mul(*new_args))
  66. def deltaintegrate(f, x):
  67. """
  68. deltaintegrate(f, x)
  69. Explanation
  70. ===========
  71. The idea for integration is the following:
  72. - If we are dealing with a DiracDelta expression, i.e. DiracDelta(g(x)),
  73. we try to simplify it.
  74. If we could simplify it, then we integrate the resulting expression.
  75. We already know we can integrate a simplified expression, because only
  76. simple DiracDelta expressions are involved.
  77. If we couldn't simplify it, there are two cases:
  78. 1) The expression is a simple expression: we return the integral,
  79. taking care if we are dealing with a Derivative or with a proper
  80. DiracDelta.
  81. 2) The expression is not simple (i.e. DiracDelta(cos(x))): we can do
  82. nothing at all.
  83. - If the node is a multiplication node having a DiracDelta term:
  84. First we expand it.
  85. If the expansion did work, then we try to integrate the expansion.
  86. If not, we try to extract a simple DiracDelta term, then we have two
  87. cases:
  88. 1) We have a simple DiracDelta term, so we return the integral.
  89. 2) We didn't have a simple term, but we do have an expression with
  90. simplified DiracDelta terms, so we integrate this expression.
  91. Examples
  92. ========
  93. >>> from sympy.abc import x, y, z
  94. >>> from sympy.integrals.deltafunctions import deltaintegrate
  95. >>> from sympy import sin, cos, DiracDelta
  96. >>> deltaintegrate(x*sin(x)*cos(x)*DiracDelta(x - 1), x)
  97. sin(1)*cos(1)*Heaviside(x - 1)
  98. >>> deltaintegrate(y**2*DiracDelta(x - z)*DiracDelta(y - z), y)
  99. z**2*DiracDelta(x - z)*Heaviside(y - z)
  100. See Also
  101. ========
  102. sympy.functions.special.delta_functions.DiracDelta
  103. sympy.integrals.integrals.Integral
  104. """
  105. if not f.has(DiracDelta):
  106. return None
  107. # g(x) = DiracDelta(h(x))
  108. if f.func == DiracDelta:
  109. h = f.expand(diracdelta=True, wrt=x)
  110. if h == f: # can't simplify the expression
  111. #FIXME: the second term tells whether is DeltaDirac or Derivative
  112. #For integrating derivatives of DiracDelta we need the chain rule
  113. if f.is_simple(x):
  114. if (len(f.args) <= 1 or f.args[1] == 0):
  115. return Heaviside(f.args[0])
  116. else:
  117. return (DiracDelta(f.args[0], f.args[1] - 1) /
  118. f.args[0].as_poly().LC())
  119. else: # let's try to integrate the simplified expression
  120. fh = integrate(h, x)
  121. return fh
  122. elif f.is_Mul or f.is_Pow: # g(x) = a*b*c*f(DiracDelta(h(x)))*d*e
  123. g = f.expand()
  124. if f != g: # the expansion worked
  125. fh = integrate(g, x)
  126. if fh is not None and not isinstance(fh, Integral):
  127. return fh
  128. else:
  129. # no expansion performed, try to extract a simple DiracDelta term
  130. deltaterm, rest_mult = change_mul(f, x)
  131. if not deltaterm:
  132. if rest_mult:
  133. fh = integrate(rest_mult, x)
  134. return fh
  135. else:
  136. from sympy.solvers import solve
  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