modeling_simple / .venv /lib /python3.14 /site-packages /sympy /functions /special /delta_functions.py
| from sympy.core import S, diff | |
| from sympy.core.function import DefinedFunction, ArgumentIndexError | |
| from sympy.core.logic import fuzzy_not | |
| from sympy.core.relational import Eq, Ne | |
| from sympy.functions.elementary.complexes import im, sign | |
| from sympy.functions.elementary.piecewise import Piecewise | |
| from sympy.polys.polyerrors import PolynomialError | |
| from sympy.polys.polyroots import roots | |
| from sympy.utilities.misc import filldedent | |
| ############################################################################### | |
| ################################ DELTA FUNCTION ############################### | |
| ############################################################################### | |
| class DiracDelta(DefinedFunction): | |
| r""" | |
| The DiracDelta function and its derivatives. | |
| Explanation | |
| =========== | |
| DiracDelta is not an ordinary function. It can be rigorously defined either | |
| as a distribution or as a measure. | |
| DiracDelta only makes sense in definite integrals, and in particular, | |
| integrals of the form ``Integral(f(x)*DiracDelta(x - x0), (x, a, b))``, | |
| where it equals ``f(x0)`` if ``a <= x0 <= b`` and ``0`` otherwise. Formally, | |
| DiracDelta acts in some ways like a function that is ``0`` everywhere except | |
| at ``0``, but in many ways it also does not. It can often be useful to treat | |
| DiracDelta in formal ways, building up and manipulating expressions with | |
| delta functions (which may eventually be integrated), but care must be taken | |
| to not treat it as a real function. SymPy's ``oo`` is similar. It only | |
| truly makes sense formally in certain contexts (such as integration limits), | |
| but SymPy allows its use everywhere, and it tries to be consistent with | |
| operations on it (like ``1/oo``), but it is easy to get into trouble and get | |
| wrong results if ``oo`` is treated too much like a number. Similarly, if | |
| DiracDelta is treated too much like a function, it is easy to get wrong or | |
| nonsensical results. | |
| DiracDelta function has the following properties: | |
| 1) $\frac{d}{d x} \theta(x) = \delta(x)$ | |
| 2) $\int_{-\infty}^\infty \delta(x - a)f(x)\, dx = f(a)$ and $\int_{a- | |
| \epsilon}^{a+\epsilon} \delta(x - a)f(x)\, dx = f(a)$ | |
| 3) $\delta(x) = 0$ for all $x \neq 0$ | |
| 4) $\delta(g(x)) = \sum_i \frac{\delta(x - x_i)}{\|g'(x_i)\|}$ where $x_i$ | |
| are the roots of $g$ | |
| 5) $\delta(-x) = \delta(x)$ | |
| Derivatives of ``k``-th order of DiracDelta have the following properties: | |
| 6) $\delta(x, k) = 0$ for all $x \neq 0$ | |
| 7) $\delta(-x, k) = -\delta(x, k)$ for odd $k$ | |
| 8) $\delta(-x, k) = \delta(x, k)$ for even $k$ | |
| Examples | |
| ======== | |
| >>> from sympy import DiracDelta, diff, pi | |
| >>> from sympy.abc import x, y | |
| >>> DiracDelta(x) | |
| DiracDelta(x) | |
| >>> DiracDelta(1) | |
| 0 | |
| >>> DiracDelta(-1) | |
| 0 | |
| >>> DiracDelta(pi) | |
| 0 | |
| >>> DiracDelta(x - 4).subs(x, 4) | |
| DiracDelta(0) | |
| >>> diff(DiracDelta(x)) | |
| DiracDelta(x, 1) | |
| >>> diff(DiracDelta(x - 1), x, 2) | |
| DiracDelta(x - 1, 2) | |
| >>> diff(DiracDelta(x**2 - 1), x, 2) | |
| 2*(2*x**2*DiracDelta(x**2 - 1, 2) + DiracDelta(x**2 - 1, 1)) | |
| >>> DiracDelta(3*x).is_simple(x) | |
| True | |
| >>> DiracDelta(x**2).is_simple(x) | |
| False | |
| >>> DiracDelta((x**2 - 1)*y).expand(diracdelta=True, wrt=x) | |
| DiracDelta(x - 1)/(2*Abs(y)) + DiracDelta(x + 1)/(2*Abs(y)) | |
| See Also | |
| ======== | |
| Heaviside | |
| sympy.simplify.simplify.simplify, is_simple | |
| sympy.functions.special.tensor_functions.KroneckerDelta | |
| References | |
| ========== | |
| .. [1] https://mathworld.wolfram.com/DeltaFunction.html | |
| """ | |
| is_real = True | |
| def fdiff(self, argindex=1): | |
| """ | |
| Returns the first derivative of a DiracDelta Function. | |
| Explanation | |
| =========== | |
| The difference between ``diff()`` and ``fdiff()`` is: ``diff()`` is the | |
| user-level function and ``fdiff()`` is an object method. ``fdiff()`` is | |
| a convenience method available in the ``Function`` class. It returns | |
| the derivative of the function without considering the chain rule. | |
| ``diff(function, x)`` calls ``Function._eval_derivative`` which in turn | |
| calls ``fdiff()`` internally to compute the derivative of the function. | |
| Examples | |
| ======== | |
| >>> from sympy import DiracDelta, diff | |
| >>> from sympy.abc import x | |
| >>> DiracDelta(x).fdiff() | |
| DiracDelta(x, 1) | |
| >>> DiracDelta(x, 1).fdiff() | |
| DiracDelta(x, 2) | |
| >>> DiracDelta(x**2 - 1).fdiff() | |
| DiracDelta(x**2 - 1, 1) | |
| >>> diff(DiracDelta(x, 1)).fdiff() | |
| DiracDelta(x, 3) | |
| Parameters | |
| ========== | |
| argindex : integer | |
| degree of derivative | |
| """ | |
| if argindex == 1: | |
| #I didn't know if there is a better way to handle default arguments | |
| k = 0 | |
| if len(self.args) > 1: | |
| k = self.args[1] | |
| return self.func(self.args[0], k + 1) | |
| else: | |
| raise ArgumentIndexError(self, argindex) | |
| def eval(cls, arg, k=S.Zero): | |
| """ | |
| Returns a simplified form or a value of DiracDelta depending on the | |
| argument passed by the DiracDelta object. | |
| Explanation | |
| =========== | |
| The ``eval()`` method is automatically called when the ``DiracDelta`` | |
| class is about to be instantiated and it returns either some simplified | |
| instance or the unevaluated instance depending on the argument passed. | |
| In other words, ``eval()`` method is not needed to be called explicitly, | |
| it is being called and evaluated once the object is called. | |
| Examples | |
| ======== | |
| >>> from sympy import DiracDelta, S | |
| >>> from sympy.abc import x | |
| >>> DiracDelta(x) | |
| DiracDelta(x) | |
| >>> DiracDelta(-x, 1) | |
| -DiracDelta(x, 1) | |
| >>> DiracDelta(1) | |
| 0 | |
| >>> DiracDelta(5, 1) | |
| 0 | |
| >>> DiracDelta(0) | |
| DiracDelta(0) | |
| >>> DiracDelta(-1) | |
| 0 | |
| >>> DiracDelta(S.NaN) | |
| nan | |
| >>> DiracDelta(x - 100).subs(x, 5) | |
| 0 | |
| >>> DiracDelta(x - 100).subs(x, 100) | |
| DiracDelta(0) | |
| Parameters | |
| ========== | |
| k : integer | |
| order of derivative | |
| arg : argument passed to DiracDelta | |
| """ | |
| if not k.is_Integer or k.is_negative: | |
| raise ValueError("Error: the second argument of DiracDelta must be \ | |
| a non-negative integer, %s given instead." % (k,)) | |
| if arg is S.NaN: | |
| return S.NaN | |
| if arg.is_nonzero: | |
| return S.Zero | |
| if fuzzy_not(im(arg).is_zero): | |
| raise ValueError(filldedent(''' | |
| Function defined only for Real Values. | |
| Complex part: %s found in %s .''' % ( | |
| repr(im(arg)), repr(arg)))) | |
| c, nc = arg.args_cnc() | |
| if c and c[0] is S.NegativeOne: | |
| # keep this fast and simple instead of using | |
| # could_extract_minus_sign | |
| if k.is_odd: | |
| return -cls(-arg, k) | |
| elif k.is_even: | |
| return cls(-arg, k) if k else cls(-arg) | |
| elif k.is_zero: | |
| return cls(arg, evaluate=False) | |
| def _eval_expand_diracdelta(self, **hints): | |
| """ | |
| Compute a simplified representation of the function using | |
| property number 4. Pass ``wrt`` as a hint to expand the expression | |
| with respect to a particular variable. | |
| Explanation | |
| =========== | |
| ``wrt`` is: | |
| - a variable with respect to which a DiracDelta expression will | |
| get expanded. | |
| Examples | |
| ======== | |
| >>> from sympy import DiracDelta | |
| >>> from sympy.abc import x, y | |
| >>> DiracDelta(x*y).expand(diracdelta=True, wrt=x) | |
| DiracDelta(x)/Abs(y) | |
| >>> DiracDelta(x*y).expand(diracdelta=True, wrt=y) | |
| DiracDelta(y)/Abs(x) | |
| >>> DiracDelta(x**2 + x - 2).expand(diracdelta=True, wrt=x) | |
| DiracDelta(x - 1)/3 + DiracDelta(x + 2)/3 | |
| See Also | |
| ======== | |
| is_simple, Diracdelta | |
| """ | |
| wrt = hints.get('wrt', None) | |
| if wrt is None: | |
| free = self.free_symbols | |
| if len(free) == 1: | |
| wrt = free.pop() | |
| else: | |
| raise TypeError(filldedent(''' | |
| When there is more than 1 free symbol or variable in the expression, | |
| the 'wrt' keyword is required as a hint to expand when using the | |
| DiracDelta hint.''')) | |
| if not self.args[0].has(wrt) or (len(self.args) > 1 and self.args[1] != 0 ): | |
| return self | |
| try: | |
| argroots = roots(self.args[0], wrt) | |
| result = 0 | |
| valid = True | |
| darg = abs(diff(self.args[0], wrt)) | |
| for r, m in argroots.items(): | |
| if r.is_real is not False and m == 1: | |
| result += self.func(wrt - r)/darg.subs(wrt, r) | |
| else: | |
| # don't handle non-real and if m != 1 then | |
| # a polynomial will have a zero in the derivative (darg) | |
| # at r | |
| valid = False | |
| break | |
| if valid: | |
| return result | |
| except PolynomialError: | |
| pass | |
| return self | |
| def is_simple(self, x): | |
| """ | |
| Tells whether the argument(args[0]) of DiracDelta is a linear | |
| expression in *x*. | |
| Examples | |
| ======== | |
| >>> from sympy import DiracDelta, cos | |
| >>> from sympy.abc import x, y | |
| >>> DiracDelta(x*y).is_simple(x) | |
| True | |
| >>> DiracDelta(x*y).is_simple(y) | |
| True | |
| >>> DiracDelta(x**2 + x - 2).is_simple(x) | |
| False | |
| >>> DiracDelta(cos(x)).is_simple(x) | |
| False | |
| Parameters | |
| ========== | |
| x : can be a symbol | |
| See Also | |
| ======== | |
| sympy.simplify.simplify.simplify, DiracDelta | |
| """ | |
| p = self.args[0].as_poly(x) | |
| if p: | |
| return p.degree() == 1 | |
| return False | |
| def _eval_rewrite_as_Piecewise(self, *args, **kwargs): | |
| """ | |
| Represents DiracDelta in a piecewise form. | |
| Examples | |
| ======== | |
| >>> from sympy import DiracDelta, Piecewise, Symbol | |
| >>> x = Symbol('x') | |
| >>> DiracDelta(x).rewrite(Piecewise) | |
| Piecewise((DiracDelta(0), Eq(x, 0)), (0, True)) | |
| >>> DiracDelta(x - 5).rewrite(Piecewise) | |
| Piecewise((DiracDelta(0), Eq(x, 5)), (0, True)) | |
| >>> DiracDelta(x**2 - 5).rewrite(Piecewise) | |
| Piecewise((DiracDelta(0), Eq(x**2, 5)), (0, True)) | |
| >>> DiracDelta(x - 5, 4).rewrite(Piecewise) | |
| DiracDelta(x - 5, 4) | |
| """ | |
| if len(args) == 1: | |
| return Piecewise((DiracDelta(0), Eq(args[0], 0)), (0, True)) | |
| def _eval_rewrite_as_SingularityFunction(self, *args, **kwargs): | |
| """ | |
| Returns the DiracDelta expression written in the form of Singularity | |
| Functions. | |
| """ | |
| from sympy.solvers import solve | |
| from sympy.functions.special.singularity_functions import SingularityFunction | |
| if self == DiracDelta(0): | |
| return SingularityFunction(0, 0, -1) | |
| if self == DiracDelta(0, 1): | |
| return SingularityFunction(0, 0, -2) | |
| free = self.free_symbols | |
| if len(free) == 1: | |
| x = (free.pop()) | |
| if len(args) == 1: | |
| return SingularityFunction(x, solve(args[0], x)[0], -1) | |
| return SingularityFunction(x, solve(args[0], x)[0], -args[1] - 1) | |
| else: | |
| # I don't know how to handle the case for DiracDelta expressions | |
| # having arguments with more than one variable. | |
| raise TypeError(filldedent(''' | |
| rewrite(SingularityFunction) does not support | |
| arguments with more that one variable.''')) | |
| ############################################################################### | |
| ############################## HEAVISIDE FUNCTION ############################# | |
| ############################################################################### | |
| class Heaviside(DefinedFunction): | |
| r""" | |
| Heaviside step function. | |
| Explanation | |
| =========== | |
| The Heaviside step function has the following properties: | |
| 1) $\frac{d}{d x} \theta(x) = \delta(x)$ | |
| 2) $\theta(x) = \begin{cases} 0 & \text{for}\: x < 0 \\ \frac{1}{2} & | |
| \text{for}\: x = 0 \\1 & \text{for}\: x > 0 \end{cases}$ | |
| 3) $\frac{d}{d x} \max(x, 0) = \theta(x)$ | |
| Heaviside(x) is printed as $\theta(x)$ with the SymPy LaTeX printer. | |
| The value at 0 is set differently in different fields. SymPy uses 1/2, | |
| which is a convention from electronics and signal processing, and is | |
| consistent with solving improper integrals by Fourier transform and | |
| convolution. | |
| To specify a different value of Heaviside at ``x=0``, a second argument | |
| can be given. Using ``Heaviside(x, nan)`` gives an expression that will | |
| evaluate to nan for x=0. | |
| .. versionchanged:: 1.9 ``Heaviside(0)`` now returns 1/2 (before: undefined) | |
| Examples | |
| ======== | |
| >>> from sympy import Heaviside, nan | |
| >>> from sympy.abc import x | |
| >>> Heaviside(9) | |
| 1 | |
| >>> Heaviside(-9) | |
| 0 | |
| >>> Heaviside(0) | |
| 1/2 | |
| >>> Heaviside(0, nan) | |
| nan | |
| >>> (Heaviside(x) + 1).replace(Heaviside(x), Heaviside(x, 1)) | |
| Heaviside(x, 1) + 1 | |
| See Also | |
| ======== | |
| DiracDelta | |
| References | |
| ========== | |
| .. [1] https://mathworld.wolfram.com/HeavisideStepFunction.html | |
| .. [2] https://dlmf.nist.gov/1.16#iv | |
| """ | |
| is_real = True | |
| def fdiff(self, argindex=1): | |
| """ | |
| Returns the first derivative of a Heaviside Function. | |
| Examples | |
| ======== | |
| >>> from sympy import Heaviside, diff | |
| >>> from sympy.abc import x | |
| >>> Heaviside(x).fdiff() | |
| DiracDelta(x) | |
| >>> Heaviside(x**2 - 1).fdiff() | |
| DiracDelta(x**2 - 1) | |
| >>> diff(Heaviside(x)).fdiff() | |
| DiracDelta(x, 1) | |
| Parameters | |
| ========== | |
| argindex : integer | |
| order of derivative | |
| """ | |
| if argindex == 1: | |
| return DiracDelta(self.args[0]) | |
| else: | |
| raise ArgumentIndexError(self, argindex) | |
| def __new__(cls, arg, H0=S.Half, **options): | |
| if isinstance(H0, Heaviside) and len(H0.args) == 1: | |
| H0 = S.Half | |
| return super(cls, cls).__new__(cls, arg, H0, **options) | |
| def pargs(self): | |
| """Args without default S.Half""" | |
| args = self.args | |
| if args[1] is S.Half: | |
| args = args[:1] | |
| return args | |
| def eval(cls, arg, H0=S.Half): | |
| """ | |
| Returns a simplified form or a value of Heaviside depending on the | |
| argument passed by the Heaviside object. | |
| Explanation | |
| =========== | |
| The ``eval()`` method is automatically called when the ``Heaviside`` | |
| class is about to be instantiated and it returns either some simplified | |
| instance or the unevaluated instance depending on the argument passed. | |
| In other words, ``eval()`` method is not needed to be called explicitly, | |
| it is being called and evaluated once the object is called. | |
| Examples | |
| ======== | |
| >>> from sympy import Heaviside, S | |
| >>> from sympy.abc import x | |
| >>> Heaviside(x) | |
| Heaviside(x) | |
| >>> Heaviside(19) | |
| 1 | |
| >>> Heaviside(0) | |
| 1/2 | |
| >>> Heaviside(0, 1) | |
| 1 | |
| >>> Heaviside(-5) | |
| 0 | |
| >>> Heaviside(S.NaN) | |
| nan | |
| >>> Heaviside(x - 100).subs(x, 5) | |
| 0 | |
| >>> Heaviside(x - 100).subs(x, 105) | |
| 1 | |
| Parameters | |
| ========== | |
| arg : argument passed by Heaviside object | |
| H0 : value of Heaviside(0) | |
| """ | |
| if arg.is_extended_negative: | |
| return S.Zero | |
| elif arg.is_extended_positive: | |
| return S.One | |
| elif arg.is_zero: | |
| return H0 | |
| elif arg is S.NaN: | |
| return S.NaN | |
| elif fuzzy_not(im(arg).is_zero): | |
| raise ValueError("Function defined only for Real Values. Complex part: %s found in %s ." % (repr(im(arg)), repr(arg)) ) | |
| def _eval_rewrite_as_Piecewise(self, arg, H0=None, **kwargs): | |
| """ | |
| Represents Heaviside in a Piecewise form. | |
| Examples | |
| ======== | |
| >>> from sympy import Heaviside, Piecewise, Symbol, nan | |
| >>> x = Symbol('x') | |
| >>> Heaviside(x).rewrite(Piecewise) | |
| Piecewise((0, x < 0), (1/2, Eq(x, 0)), (1, True)) | |
| >>> Heaviside(x,nan).rewrite(Piecewise) | |
| Piecewise((0, x < 0), (nan, Eq(x, 0)), (1, True)) | |
| >>> Heaviside(x - 5).rewrite(Piecewise) | |
| Piecewise((0, x < 5), (1/2, Eq(x, 5)), (1, True)) | |
| >>> Heaviside(x**2 - 1).rewrite(Piecewise) | |
| Piecewise((0, x**2 < 1), (1/2, Eq(x**2, 1)), (1, True)) | |
| """ | |
| if H0 == 0: | |
| return Piecewise((0, arg <= 0), (1, True)) | |
| if H0 == 1: | |
| return Piecewise((0, arg < 0), (1, True)) | |
| return Piecewise((0, arg < 0), (H0, Eq(arg, 0)), (1, True)) | |
| def _eval_rewrite_as_sign(self, arg, H0=S.Half, **kwargs): | |
| """ | |
| Represents the Heaviside function in the form of sign function. | |
| Explanation | |
| =========== | |
| The value of Heaviside(0) must be 1/2 for rewriting as sign to be | |
| strictly equivalent. For easier usage, we also allow this rewriting | |
| when Heaviside(0) is undefined. | |
| Examples | |
| ======== | |
| >>> from sympy import Heaviside, Symbol, sign, nan | |
| >>> x = Symbol('x', real=True) | |
| >>> y = Symbol('y') | |
| >>> Heaviside(x).rewrite(sign) | |
| sign(x)/2 + 1/2 | |
| >>> Heaviside(x, 0).rewrite(sign) | |
| Piecewise((sign(x)/2 + 1/2, Ne(x, 0)), (0, True)) | |
| >>> Heaviside(x, nan).rewrite(sign) | |
| Piecewise((sign(x)/2 + 1/2, Ne(x, 0)), (nan, True)) | |
| >>> Heaviside(x - 2).rewrite(sign) | |
| sign(x - 2)/2 + 1/2 | |
| >>> Heaviside(x**2 - 2*x + 1).rewrite(sign) | |
| sign(x**2 - 2*x + 1)/2 + 1/2 | |
| >>> Heaviside(y).rewrite(sign) | |
| Heaviside(y) | |
| >>> Heaviside(y**2 - 2*y + 1).rewrite(sign) | |
| Heaviside(y**2 - 2*y + 1) | |
| See Also | |
| ======== | |
| sign | |
| """ | |
| if arg.is_extended_real: | |
| pw1 = Piecewise( | |
| ((sign(arg) + 1)/2, Ne(arg, 0)), | |
| (Heaviside(0, H0=H0), True)) | |
| pw2 = Piecewise( | |
| ((sign(arg) + 1)/2, Eq(Heaviside(0, H0=H0), S.Half)), | |
| (pw1, True)) | |
| return pw2 | |
| def _eval_rewrite_as_SingularityFunction(self, args, H0=S.Half, **kwargs): | |
| """ | |
| Returns the Heaviside expression written in the form of Singularity | |
| Functions. | |
| """ | |
| from sympy.solvers import solve | |
| from sympy.functions.special.singularity_functions import SingularityFunction | |
| if self == Heaviside(0): | |
| return SingularityFunction(0, 0, 0) | |
| free = self.free_symbols | |
| if len(free) == 1: | |
| x = (free.pop()) | |
| return SingularityFunction(x, solve(args, x)[0], 0) | |
| # TODO | |
| # ((x - 5)**3*Heaviside(x - 5)).rewrite(SingularityFunction) should output | |
| # SingularityFunction(x, 5, 0) instead of (x - 5)**3*SingularityFunction(x, 5, 0) | |
| else: | |
| # I don't know how to handle the case for Heaviside expressions | |
| # having arguments with more than one variable. | |
| raise TypeError(filldedent(''' | |
| rewrite(SingularityFunction) does not | |
| support arguments with more that one variable.''')) | |