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

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 

8 

9############################################################################### 

10############################# SINGULARITY FUNCTION ############################ 

11############################################################################### 

12 

13 

14class SingularityFunction(Function): 

15 r""" 

16 Singularity functions are a class of discontinuous functions. 

17 

18 Explanation 

19 =========== 

20 

21 Singularity functions take a variable, an offset, and an exponent as 

22 arguments. These functions are represented using Macaulay brackets as: 

23 

24 SingularityFunction(x, a, n) := <x - a>^n 

25 

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``. 

29 

30 Examples 

31 ======== 

32 

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 

61 

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. 

65 

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) 

73 

74 See Also 

75 ======== 

76 

77 DiracDelta, Heaviside 

78 

79 References 

80 ========== 

81 

82 .. [1] https://en.wikipedia.org/wiki/Singularity_function 

83 

84 """ 

85 

86 is_real = True 

87 

88 def fdiff(self, argindex=1): 

89 """ 

90 Returns the first derivative of a DiracDelta Function. 

91 

92 Explanation 

93 =========== 

94 

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. 

101 

102 """ 

103 

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) 

112 

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. 

118 

119 Explanation 

120 =========== 

121 

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. 

128 

129 Examples 

130 ======== 

131 

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 

153 

154 """ 

155 

156 x = variable 

157 a = offset 

158 n = exponent 

159 shift = (x - a) 

160 

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 

178 

179 def _eval_rewrite_as_Piecewise(self, *args, **kwargs): 

180 ''' 

181 Converts a Singularity Function expression into its Piecewise form. 

182 

183 ''' 

184 x, a, n = self.args 

185 

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)) 

190 

191 def _eval_rewrite_as_Heaviside(self, *args, **kwargs): 

192 ''' 

193 Rewrites a Singularity Function expression using Heavisides and DiracDeltas. 

194 

195 ''' 

196 x, a, n = self.args 

197 

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) 

204 

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 

215 

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 

226 

227 _eval_rewrite_as_DiracDelta = _eval_rewrite_as_Heaviside 

228 _eval_rewrite_as_HeavisideDiracDelta = _eval_rewrite_as_Heaviside