Coverage for /usr/lib/python3/dist-packages/sympy/calculus/singularities.py: 27%

51 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1""" 

2Singularities 

3============= 

4 

5This module implements algorithms for finding singularities for a function 

6and identifying types of functions. 

7 

8The differential calculus methods in this module include methods to identify 

9the following function types in the given ``Interval``: 

10- Increasing 

11- Strictly Increasing 

12- Decreasing 

13- Strictly Decreasing 

14- Monotonic 

15 

16""" 

17 

18from sympy.core.power import Pow 

19from sympy.core.singleton import S 

20from sympy.core.symbol import Symbol 

21from sympy.core.sympify import sympify 

22from sympy.functions.elementary.exponential import log 

23from sympy.functions.elementary.trigonometric import sec, csc, cot, tan, cos 

24from sympy.utilities.misc import filldedent 

25 

26 

27def singularities(expression, symbol, domain=None): 

28 """ 

29 Find singularities of a given function. 

30 

31 Parameters 

32 ========== 

33 

34 expression : Expr 

35 The target function in which singularities need to be found. 

36 symbol : Symbol 

37 The symbol over the values of which the singularity in 

38 expression in being searched for. 

39 

40 Returns 

41 ======= 

42 

43 Set 

44 A set of values for ``symbol`` for which ``expression`` has a 

45 singularity. An ``EmptySet`` is returned if ``expression`` has no 

46 singularities for any given value of ``Symbol``. 

47 

48 Raises 

49 ====== 

50 

51 NotImplementedError 

52 Methods for determining the singularities of this function have 

53 not been developed. 

54 

55 Notes 

56 ===== 

57 

58 This function does not find non-isolated singularities 

59 nor does it find branch points of the expression. 

60 

61 Currently supported functions are: 

62 - univariate continuous (real or complex) functions 

63 

64 References 

65 ========== 

66 

67 .. [1] https://en.wikipedia.org/wiki/Mathematical_singularity 

68 

69 Examples 

70 ======== 

71 

72 >>> from sympy import singularities, Symbol, log 

73 >>> x = Symbol('x', real=True) 

74 >>> y = Symbol('y', real=False) 

75 >>> singularities(x**2 + x + 1, x) 

76 EmptySet 

77 >>> singularities(1/(x + 1), x) 

78 {-1} 

79 >>> singularities(1/(y**2 + 1), y) 

80 {-I, I} 

81 >>> singularities(1/(y**3 + 1), y) 

82 {-1, 1/2 - sqrt(3)*I/2, 1/2 + sqrt(3)*I/2} 

83 >>> singularities(log(x), x) 

84 {0} 

85 

86 """ 

87 from sympy.solvers.solveset import solveset 

88 

89 if domain is None: 

90 domain = S.Reals if symbol.is_real else S.Complexes 

91 try: 

92 sings = S.EmptySet 

93 for i in expression.rewrite([sec, csc, cot, tan], cos).atoms(Pow): 

94 if i.exp.is_infinite: 

95 raise NotImplementedError 

96 if i.exp.is_negative: 

97 sings += solveset(i.base, symbol, domain) 

98 for i in expression.atoms(log): 

99 sings += solveset(i.args[0], symbol, domain) 

100 return sings 

101 except NotImplementedError: 

102 raise NotImplementedError(filldedent(''' 

103 Methods for determining the singularities 

104 of this function have not been developed.''')) 

105 

106 

107########################################################################### 

108# DIFFERENTIAL CALCULUS METHODS # 

109########################################################################### 

110 

111 

112def monotonicity_helper(expression, predicate, interval=S.Reals, symbol=None): 

113 """ 

114 Helper function for functions checking function monotonicity. 

115 

116 Parameters 

117 ========== 

118 

119 expression : Expr 

120 The target function which is being checked 

121 predicate : function 

122 The property being tested for. The function takes in an integer 

123 and returns a boolean. The integer input is the derivative and 

124 the boolean result should be true if the property is being held, 

125 and false otherwise. 

126 interval : Set, optional 

127 The range of values in which we are testing, defaults to all reals. 

128 symbol : Symbol, optional 

129 The symbol present in expression which gets varied over the given range. 

130 

131 It returns a boolean indicating whether the interval in which 

132 the function's derivative satisfies given predicate is a superset 

133 of the given interval. 

134 

135 Returns 

136 ======= 

137 

138 Boolean 

139 True if ``predicate`` is true for all the derivatives when ``symbol`` 

140 is varied in ``range``, False otherwise. 

141 

142 """ 

143 from sympy.solvers.solveset import solveset 

144 

145 expression = sympify(expression) 

146 free = expression.free_symbols 

147 

148 if symbol is None: 

149 if len(free) > 1: 

150 raise NotImplementedError( 

151 'The function has not yet been implemented' 

152 ' for all multivariate expressions.' 

153 ) 

154 

155 variable = symbol or (free.pop() if free else Symbol('x')) 

156 derivative = expression.diff(variable) 

157 predicate_interval = solveset(predicate(derivative), variable, S.Reals) 

158 return interval.is_subset(predicate_interval) 

159 

160 

161def is_increasing(expression, interval=S.Reals, symbol=None): 

162 """ 

163 Return whether the function is increasing in the given interval. 

164 

165 Parameters 

166 ========== 

167 

168 expression : Expr 

169 The target function which is being checked. 

170 interval : Set, optional 

171 The range of values in which we are testing (defaults to set of 

172 all real numbers). 

173 symbol : Symbol, optional 

174 The symbol present in expression which gets varied over the given range. 

175 

176 Returns 

177 ======= 

178 

179 Boolean 

180 True if ``expression`` is increasing (either strictly increasing or 

181 constant) in the given ``interval``, False otherwise. 

182 

183 Examples 

184 ======== 

185 

186 >>> from sympy import is_increasing 

187 >>> from sympy.abc import x, y 

188 >>> from sympy import S, Interval, oo 

189 >>> is_increasing(x**3 - 3*x**2 + 4*x, S.Reals) 

190 True 

191 >>> is_increasing(-x**2, Interval(-oo, 0)) 

192 True 

193 >>> is_increasing(-x**2, Interval(0, oo)) 

194 False 

195 >>> is_increasing(4*x**3 - 6*x**2 - 72*x + 30, Interval(-2, 3)) 

196 False 

197 >>> is_increasing(x**2 + y, Interval(1, 2), x) 

198 True 

199 

200 """ 

201 return monotonicity_helper(expression, lambda x: x >= 0, interval, symbol) 

202 

203 

204def is_strictly_increasing(expression, interval=S.Reals, symbol=None): 

205 """ 

206 Return whether the function is strictly increasing in the given interval. 

207 

208 Parameters 

209 ========== 

210 

211 expression : Expr 

212 The target function which is being checked. 

213 interval : Set, optional 

214 The range of values in which we are testing (defaults to set of 

215 all real numbers). 

216 symbol : Symbol, optional 

217 The symbol present in expression which gets varied over the given range. 

218 

219 Returns 

220 ======= 

221 

222 Boolean 

223 True if ``expression`` is strictly increasing in the given ``interval``, 

224 False otherwise. 

225 

226 Examples 

227 ======== 

228 

229 >>> from sympy import is_strictly_increasing 

230 >>> from sympy.abc import x, y 

231 >>> from sympy import Interval, oo 

232 >>> is_strictly_increasing(4*x**3 - 6*x**2 - 72*x + 30, Interval.Ropen(-oo, -2)) 

233 True 

234 >>> is_strictly_increasing(4*x**3 - 6*x**2 - 72*x + 30, Interval.Lopen(3, oo)) 

235 True 

236 >>> is_strictly_increasing(4*x**3 - 6*x**2 - 72*x + 30, Interval.open(-2, 3)) 

237 False 

238 >>> is_strictly_increasing(-x**2, Interval(0, oo)) 

239 False 

240 >>> is_strictly_increasing(-x**2 + y, Interval(-oo, 0), x) 

241 False 

242 

243 """ 

244 return monotonicity_helper(expression, lambda x: x > 0, interval, symbol) 

245 

246 

247def is_decreasing(expression, interval=S.Reals, symbol=None): 

248 """ 

249 Return whether the function is decreasing in the given interval. 

250 

251 Parameters 

252 ========== 

253 

254 expression : Expr 

255 The target function which is being checked. 

256 interval : Set, optional 

257 The range of values in which we are testing (defaults to set of 

258 all real numbers). 

259 symbol : Symbol, optional 

260 The symbol present in expression which gets varied over the given range. 

261 

262 Returns 

263 ======= 

264 

265 Boolean 

266 True if ``expression`` is decreasing (either strictly decreasing or 

267 constant) in the given ``interval``, False otherwise. 

268 

269 Examples 

270 ======== 

271 

272 >>> from sympy import is_decreasing 

273 >>> from sympy.abc import x, y 

274 >>> from sympy import S, Interval, oo 

275 >>> is_decreasing(1/(x**2 - 3*x), Interval.open(S(3)/2, 3)) 

276 True 

277 >>> is_decreasing(1/(x**2 - 3*x), Interval.open(1.5, 3)) 

278 True 

279 >>> is_decreasing(1/(x**2 - 3*x), Interval.Lopen(3, oo)) 

280 True 

281 >>> is_decreasing(1/(x**2 - 3*x), Interval.Ropen(-oo, S(3)/2)) 

282 False 

283 >>> is_decreasing(1/(x**2 - 3*x), Interval.Ropen(-oo, 1.5)) 

284 False 

285 >>> is_decreasing(-x**2, Interval(-oo, 0)) 

286 False 

287 >>> is_decreasing(-x**2 + y, Interval(-oo, 0), x) 

288 False 

289 

290 """ 

291 return monotonicity_helper(expression, lambda x: x <= 0, interval, symbol) 

292 

293 

294def is_strictly_decreasing(expression, interval=S.Reals, symbol=None): 

295 """ 

296 Return whether the function is strictly decreasing in the given interval. 

297 

298 Parameters 

299 ========== 

300 

301 expression : Expr 

302 The target function which is being checked. 

303 interval : Set, optional 

304 The range of values in which we are testing (defaults to set of 

305 all real numbers). 

306 symbol : Symbol, optional 

307 The symbol present in expression which gets varied over the given range. 

308 

309 Returns 

310 ======= 

311 

312 Boolean 

313 True if ``expression`` is strictly decreasing in the given ``interval``, 

314 False otherwise. 

315 

316 Examples 

317 ======== 

318 

319 >>> from sympy import is_strictly_decreasing 

320 >>> from sympy.abc import x, y 

321 >>> from sympy import S, Interval, oo 

322 >>> is_strictly_decreasing(1/(x**2 - 3*x), Interval.Lopen(3, oo)) 

323 True 

324 >>> is_strictly_decreasing(1/(x**2 - 3*x), Interval.Ropen(-oo, S(3)/2)) 

325 False 

326 >>> is_strictly_decreasing(1/(x**2 - 3*x), Interval.Ropen(-oo, 1.5)) 

327 False 

328 >>> is_strictly_decreasing(-x**2, Interval(-oo, 0)) 

329 False 

330 >>> is_strictly_decreasing(-x**2 + y, Interval(-oo, 0), x) 

331 False 

332 

333 """ 

334 return monotonicity_helper(expression, lambda x: x < 0, interval, symbol) 

335 

336 

337def is_monotonic(expression, interval=S.Reals, symbol=None): 

338 """ 

339 Return whether the function is monotonic in the given interval. 

340 

341 Parameters 

342 ========== 

343 

344 expression : Expr 

345 The target function which is being checked. 

346 interval : Set, optional 

347 The range of values in which we are testing (defaults to set of 

348 all real numbers). 

349 symbol : Symbol, optional 

350 The symbol present in expression which gets varied over the given range. 

351 

352 Returns 

353 ======= 

354 

355 Boolean 

356 True if ``expression`` is monotonic in the given ``interval``, 

357 False otherwise. 

358 

359 Raises 

360 ====== 

361 

362 NotImplementedError 

363 Monotonicity check has not been implemented for the queried function. 

364 

365 Examples 

366 ======== 

367 

368 >>> from sympy import is_monotonic 

369 >>> from sympy.abc import x, y 

370 >>> from sympy import S, Interval, oo 

371 >>> is_monotonic(1/(x**2 - 3*x), Interval.open(S(3)/2, 3)) 

372 True 

373 >>> is_monotonic(1/(x**2 - 3*x), Interval.open(1.5, 3)) 

374 True 

375 >>> is_monotonic(1/(x**2 - 3*x), Interval.Lopen(3, oo)) 

376 True 

377 >>> is_monotonic(x**3 - 3*x**2 + 4*x, S.Reals) 

378 True 

379 >>> is_monotonic(-x**2, S.Reals) 

380 False 

381 >>> is_monotonic(x**2 + y + 1, Interval(1, 2), x) 

382 True 

383 

384 """ 

385 from sympy.solvers.solveset import solveset 

386 

387 expression = sympify(expression) 

388 

389 free = expression.free_symbols 

390 if symbol is None and len(free) > 1: 

391 raise NotImplementedError( 

392 'is_monotonic has not yet been implemented' 

393 ' for all multivariate expressions.' 

394 ) 

395 

396 variable = symbol or (free.pop() if free else Symbol('x')) 

397 turning_points = solveset(expression.diff(variable), variable, interval) 

398 return interval.intersection(turning_points) is S.EmptySet