123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- from sympy.core.mul import Mul
- from sympy.core.singleton import S
- from sympy.core.sorting import default_sort_key
- from sympy.functions import DiracDelta, Heaviside
- from .integrals import Integral, integrate
- from sympy.solvers import solve
- def change_mul(node, x):
- """change_mul(node, x)
- Rearranges the operands of a product, bringing to front any simple
- DiracDelta expression.
- Explanation
- ===========
- If no simple DiracDelta expression was found, then all the DiracDelta
- expressions are simplified (using DiracDelta.expand(diracdelta=True, wrt=x)).
- Return: (dirac, new node)
- Where:
- o dirac is either a simple DiracDelta expression or None (if no simple
- expression was found);
- o new node is either a simplified DiracDelta expressions or None (if it
- could not be simplified).
- Examples
- ========
- >>> from sympy import DiracDelta, cos
- >>> from sympy.integrals.deltafunctions import change_mul
- >>> from sympy.abc import x, y
- >>> change_mul(x*y*DiracDelta(x)*cos(x), x)
- (DiracDelta(x), x*y*cos(x))
- >>> change_mul(x*y*DiracDelta(x**2 - 1)*cos(x), x)
- (None, x*y*cos(x)*DiracDelta(x - 1)/2 + x*y*cos(x)*DiracDelta(x + 1)/2)
- >>> change_mul(x*y*DiracDelta(cos(x))*cos(x), x)
- (None, None)
- See Also
- ========
- sympy.functions.special.delta_functions.DiracDelta
- deltaintegrate
- """
- new_args = []
- dirac = None
- #Sorting is needed so that we consistently collapse the same delta;
- #However, we must preserve the ordering of non-commutative terms
- c, nc = node.args_cnc()
- sorted_args = sorted(c, key=default_sort_key)
- sorted_args.extend(nc)
- for arg in sorted_args:
- if arg.is_Pow and isinstance(arg.base, DiracDelta):
- new_args.append(arg.func(arg.base, arg.exp - 1))
- arg = arg.base
- if dirac is None and (isinstance(arg, DiracDelta) and arg.is_simple(x)):
- dirac = arg
- else:
- new_args.append(arg)
- if not dirac: # there was no simple dirac
- new_args = []
- for arg in sorted_args:
- if isinstance(arg, DiracDelta):
- new_args.append(arg.expand(diracdelta=True, wrt=x))
- elif arg.is_Pow and isinstance(arg.base, DiracDelta):
- new_args.append(arg.func(arg.base.expand(diracdelta=True, wrt=x), arg.exp))
- else:
- new_args.append(arg)
- if new_args != sorted_args:
- nnode = Mul(*new_args).expand()
- else: # if the node didn't change there is nothing to do
- nnode = None
- return (None, nnode)
- return (dirac, Mul(*new_args))
- def deltaintegrate(f, x):
- """
- deltaintegrate(f, x)
- Explanation
- ===========
- The idea for integration is the following:
- - If we are dealing with a DiracDelta expression, i.e. DiracDelta(g(x)),
- we try to simplify it.
- If we could simplify it, then we integrate the resulting expression.
- We already know we can integrate a simplified expression, because only
- simple DiracDelta expressions are involved.
- If we couldn't simplify it, there are two cases:
- 1) The expression is a simple expression: we return the integral,
- taking care if we are dealing with a Derivative or with a proper
- DiracDelta.
- 2) The expression is not simple (i.e. DiracDelta(cos(x))): we can do
- nothing at all.
- - If the node is a multiplication node having a DiracDelta term:
- First we expand it.
- If the expansion did work, then we try to integrate the expansion.
- If not, we try to extract a simple DiracDelta term, then we have two
- cases:
- 1) We have a simple DiracDelta term, so we return the integral.
- 2) We didn't have a simple term, but we do have an expression with
- simplified DiracDelta terms, so we integrate this expression.
- Examples
- ========
- >>> from sympy.abc import x, y, z
- >>> from sympy.integrals.deltafunctions import deltaintegrate
- >>> from sympy import sin, cos, DiracDelta
- >>> deltaintegrate(x*sin(x)*cos(x)*DiracDelta(x - 1), x)
- sin(1)*cos(1)*Heaviside(x - 1)
- >>> deltaintegrate(y**2*DiracDelta(x - z)*DiracDelta(y - z), y)
- z**2*DiracDelta(x - z)*Heaviside(y - z)
- See Also
- ========
- sympy.functions.special.delta_functions.DiracDelta
- sympy.integrals.integrals.Integral
- """
- if not f.has(DiracDelta):
- return None
- # g(x) = DiracDelta(h(x))
- if f.func == DiracDelta:
- h = f.expand(diracdelta=True, wrt=x)
- if h == f: # can't simplify the expression
- #FIXME: the second term tells whether is DeltaDirac or Derivative
- #For integrating derivatives of DiracDelta we need the chain rule
- if f.is_simple(x):
- if (len(f.args) <= 1 or f.args[1] == 0):
- return Heaviside(f.args[0])
- else:
- return (DiracDelta(f.args[0], f.args[1] - 1) /
- f.args[0].as_poly().LC())
- else: # let's try to integrate the simplified expression
- fh = integrate(h, x)
- return fh
- elif f.is_Mul or f.is_Pow: # g(x) = a*b*c*f(DiracDelta(h(x)))*d*e
- g = f.expand()
- if f != g: # the expansion worked
- fh = integrate(g, x)
- if fh is not None and not isinstance(fh, Integral):
- return fh
- else:
- # no expansion performed, try to extract a simple DiracDelta term
- deltaterm, rest_mult = change_mul(f, x)
- if not deltaterm:
- if rest_mult:
- fh = integrate(rest_mult, x)
- return fh
- else:
- deltaterm = deltaterm.expand(diracdelta=True, wrt=x)
- if deltaterm.is_Mul: # Take out any extracted factors
- deltaterm, rest_mult_2 = change_mul(deltaterm, x)
- rest_mult = rest_mult*rest_mult_2
- point = solve(deltaterm.args[0], x)[0]
- # Return the largest hyperreal term left after
- # repeated integration by parts. For example,
- #
- # integrate(y*DiracDelta(x, 1),x) == y*DiracDelta(x,0), not 0
- #
- # This is so Integral(y*DiracDelta(x).diff(x),x).doit()
- # will return y*DiracDelta(x) instead of 0 or DiracDelta(x),
- # both of which are correct everywhere the value is defined
- # but give wrong answers for nested integration.
- n = (0 if len(deltaterm.args)==1 else deltaterm.args[1])
- m = 0
- while n >= 0:
- r = S.NegativeOne**n*rest_mult.diff(x, n).subs(x, point)
- if r.is_zero:
- n -= 1
- m += 1
- else:
- if m == 0:
- return r*Heaviside(x - point)
- else:
- return r*DiracDelta(x,m-1)
- # In some very weak sense, x=0 is still a singularity,
- # but we hope will not be of any practical consequence.
- return S.Zero
- return None
|