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