singularities.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. """
  2. Singularities
  3. =============
  4. This module implements algorithms for finding singularities for a function
  5. and identifying types of functions.
  6. The differential calculus methods in this module include methods to identify
  7. the following function types in the given ``Interval``:
  8. - Increasing
  9. - Strictly Increasing
  10. - Decreasing
  11. - Strictly Decreasing
  12. - Monotonic
  13. """
  14. from sympy.core.power import Pow
  15. from sympy.core.singleton import S
  16. from sympy.core.symbol import Symbol
  17. from sympy.core.sympify import sympify
  18. from sympy.functions.elementary.exponential import log
  19. from sympy.functions.elementary.trigonometric import sec, csc, cot, tan, cos
  20. from sympy.utilities.misc import filldedent
  21. def singularities(expression, symbol, domain=None):
  22. """
  23. Find singularities of a given function.
  24. Parameters
  25. ==========
  26. expression : Expr
  27. The target function in which singularities need to be found.
  28. symbol : Symbol
  29. The symbol over the values of which the singularity in
  30. expression in being searched for.
  31. Returns
  32. =======
  33. Set
  34. A set of values for ``symbol`` for which ``expression`` has a
  35. singularity. An ``EmptySet`` is returned if ``expression`` has no
  36. singularities for any given value of ``Symbol``.
  37. Raises
  38. ======
  39. NotImplementedError
  40. Methods for determining the singularities of this function have
  41. not been developed.
  42. Notes
  43. =====
  44. This function does not find non-isolated singularities
  45. nor does it find branch points of the expression.
  46. Currently supported functions are:
  47. - univariate continuous (real or complex) functions
  48. References
  49. ==========
  50. .. [1] https://en.wikipedia.org/wiki/Mathematical_singularity
  51. Examples
  52. ========
  53. >>> from sympy import singularities, Symbol, log
  54. >>> x = Symbol('x', real=True)
  55. >>> y = Symbol('y', real=False)
  56. >>> singularities(x**2 + x + 1, x)
  57. EmptySet
  58. >>> singularities(1/(x + 1), x)
  59. {-1}
  60. >>> singularities(1/(y**2 + 1), y)
  61. {-I, I}
  62. >>> singularities(1/(y**3 + 1), y)
  63. {-1, 1/2 - sqrt(3)*I/2, 1/2 + sqrt(3)*I/2}
  64. >>> singularities(log(x), x)
  65. {0}
  66. """
  67. from sympy.solvers.solveset import solveset
  68. if domain is None:
  69. domain = S.Reals if symbol.is_real else S.Complexes
  70. try:
  71. sings = S.EmptySet
  72. for i in expression.rewrite([sec, csc, cot, tan], cos).atoms(Pow):
  73. if i.exp.is_infinite:
  74. raise NotImplementedError
  75. if i.exp.is_negative:
  76. sings += solveset(i.base, symbol, domain)
  77. for i in expression.atoms(log):
  78. sings += solveset(i.args[0], symbol, domain)
  79. return sings
  80. except NotImplementedError:
  81. raise NotImplementedError(filldedent('''
  82. Methods for determining the singularities
  83. of this function have not been developed.'''))
  84. ###########################################################################
  85. # DIFFERENTIAL CALCULUS METHODS #
  86. ###########################################################################
  87. def monotonicity_helper(expression, predicate, interval=S.Reals, symbol=None):
  88. """
  89. Helper function for functions checking function monotonicity.
  90. Parameters
  91. ==========
  92. expression : Expr
  93. The target function which is being checked
  94. predicate : function
  95. The property being tested for. The function takes in an integer
  96. and returns a boolean. The integer input is the derivative and
  97. the boolean result should be true if the property is being held,
  98. and false otherwise.
  99. interval : Set, optional
  100. The range of values in which we are testing, defaults to all reals.
  101. symbol : Symbol, optional
  102. The symbol present in expression which gets varied over the given range.
  103. It returns a boolean indicating whether the interval in which
  104. the function's derivative satisfies given predicate is a superset
  105. of the given interval.
  106. Returns
  107. =======
  108. Boolean
  109. True if ``predicate`` is true for all the derivatives when ``symbol``
  110. is varied in ``range``, False otherwise.
  111. """
  112. from sympy.solvers.solveset import solveset
  113. expression = sympify(expression)
  114. free = expression.free_symbols
  115. if symbol is None:
  116. if len(free) > 1:
  117. raise NotImplementedError(
  118. 'The function has not yet been implemented'
  119. ' for all multivariate expressions.'
  120. )
  121. variable = symbol or (free.pop() if free else Symbol('x'))
  122. derivative = expression.diff(variable)
  123. predicate_interval = solveset(predicate(derivative), variable, S.Reals)
  124. return interval.is_subset(predicate_interval)
  125. def is_increasing(expression, interval=S.Reals, symbol=None):
  126. """
  127. Return whether the function is increasing in the given interval.
  128. Parameters
  129. ==========
  130. expression : Expr
  131. The target function which is being checked.
  132. interval : Set, optional
  133. The range of values in which we are testing (defaults to set of
  134. all real numbers).
  135. symbol : Symbol, optional
  136. The symbol present in expression which gets varied over the given range.
  137. Returns
  138. =======
  139. Boolean
  140. True if ``expression`` is increasing (either strictly increasing or
  141. constant) in the given ``interval``, False otherwise.
  142. Examples
  143. ========
  144. >>> from sympy import is_increasing
  145. >>> from sympy.abc import x, y
  146. >>> from sympy import S, Interval, oo
  147. >>> is_increasing(x**3 - 3*x**2 + 4*x, S.Reals)
  148. True
  149. >>> is_increasing(-x**2, Interval(-oo, 0))
  150. True
  151. >>> is_increasing(-x**2, Interval(0, oo))
  152. False
  153. >>> is_increasing(4*x**3 - 6*x**2 - 72*x + 30, Interval(-2, 3))
  154. False
  155. >>> is_increasing(x**2 + y, Interval(1, 2), x)
  156. True
  157. """
  158. return monotonicity_helper(expression, lambda x: x >= 0, interval, symbol)
  159. def is_strictly_increasing(expression, interval=S.Reals, symbol=None):
  160. """
  161. Return whether the function is strictly increasing in the given interval.
  162. Parameters
  163. ==========
  164. expression : Expr
  165. The target function which is being checked.
  166. interval : Set, optional
  167. The range of values in which we are testing (defaults to set of
  168. all real numbers).
  169. symbol : Symbol, optional
  170. The symbol present in expression which gets varied over the given range.
  171. Returns
  172. =======
  173. Boolean
  174. True if ``expression`` is strictly increasing in the given ``interval``,
  175. False otherwise.
  176. Examples
  177. ========
  178. >>> from sympy import is_strictly_increasing
  179. >>> from sympy.abc import x, y
  180. >>> from sympy import Interval, oo
  181. >>> is_strictly_increasing(4*x**3 - 6*x**2 - 72*x + 30, Interval.Ropen(-oo, -2))
  182. True
  183. >>> is_strictly_increasing(4*x**3 - 6*x**2 - 72*x + 30, Interval.Lopen(3, oo))
  184. True
  185. >>> is_strictly_increasing(4*x**3 - 6*x**2 - 72*x + 30, Interval.open(-2, 3))
  186. False
  187. >>> is_strictly_increasing(-x**2, Interval(0, oo))
  188. False
  189. >>> is_strictly_increasing(-x**2 + y, Interval(-oo, 0), x)
  190. False
  191. """
  192. return monotonicity_helper(expression, lambda x: x > 0, interval, symbol)
  193. def is_decreasing(expression, interval=S.Reals, symbol=None):
  194. """
  195. Return whether the function is decreasing in the given interval.
  196. Parameters
  197. ==========
  198. expression : Expr
  199. The target function which is being checked.
  200. interval : Set, optional
  201. The range of values in which we are testing (defaults to set of
  202. all real numbers).
  203. symbol : Symbol, optional
  204. The symbol present in expression which gets varied over the given range.
  205. Returns
  206. =======
  207. Boolean
  208. True if ``expression`` is decreasing (either strictly decreasing or
  209. constant) in the given ``interval``, False otherwise.
  210. Examples
  211. ========
  212. >>> from sympy import is_decreasing
  213. >>> from sympy.abc import x, y
  214. >>> from sympy import S, Interval, oo
  215. >>> is_decreasing(1/(x**2 - 3*x), Interval.open(S(3)/2, 3))
  216. True
  217. >>> is_decreasing(1/(x**2 - 3*x), Interval.open(1.5, 3))
  218. True
  219. >>> is_decreasing(1/(x**2 - 3*x), Interval.Lopen(3, oo))
  220. True
  221. >>> is_decreasing(1/(x**2 - 3*x), Interval.Ropen(-oo, S(3)/2))
  222. False
  223. >>> is_decreasing(1/(x**2 - 3*x), Interval.Ropen(-oo, 1.5))
  224. False
  225. >>> is_decreasing(-x**2, Interval(-oo, 0))
  226. False
  227. >>> is_decreasing(-x**2 + y, Interval(-oo, 0), x)
  228. False
  229. """
  230. return monotonicity_helper(expression, lambda x: x <= 0, interval, symbol)
  231. def is_strictly_decreasing(expression, interval=S.Reals, symbol=None):
  232. """
  233. Return whether the function is strictly decreasing in the given interval.
  234. Parameters
  235. ==========
  236. expression : Expr
  237. The target function which is being checked.
  238. interval : Set, optional
  239. The range of values in which we are testing (defaults to set of
  240. all real numbers).
  241. symbol : Symbol, optional
  242. The symbol present in expression which gets varied over the given range.
  243. Returns
  244. =======
  245. Boolean
  246. True if ``expression`` is strictly decreasing in the given ``interval``,
  247. False otherwise.
  248. Examples
  249. ========
  250. >>> from sympy import is_strictly_decreasing
  251. >>> from sympy.abc import x, y
  252. >>> from sympy import S, Interval, oo
  253. >>> is_strictly_decreasing(1/(x**2 - 3*x), Interval.Lopen(3, oo))
  254. True
  255. >>> is_strictly_decreasing(1/(x**2 - 3*x), Interval.Ropen(-oo, S(3)/2))
  256. False
  257. >>> is_strictly_decreasing(1/(x**2 - 3*x), Interval.Ropen(-oo, 1.5))
  258. False
  259. >>> is_strictly_decreasing(-x**2, Interval(-oo, 0))
  260. False
  261. >>> is_strictly_decreasing(-x**2 + y, Interval(-oo, 0), x)
  262. False
  263. """
  264. return monotonicity_helper(expression, lambda x: x < 0, interval, symbol)
  265. def is_monotonic(expression, interval=S.Reals, symbol=None):
  266. """
  267. Return whether the function is monotonic in the given interval.
  268. Parameters
  269. ==========
  270. expression : Expr
  271. The target function which is being checked.
  272. interval : Set, optional
  273. The range of values in which we are testing (defaults to set of
  274. all real numbers).
  275. symbol : Symbol, optional
  276. The symbol present in expression which gets varied over the given range.
  277. Returns
  278. =======
  279. Boolean
  280. True if ``expression`` is monotonic in the given ``interval``,
  281. False otherwise.
  282. Raises
  283. ======
  284. NotImplementedError
  285. Monotonicity check has not been implemented for the queried function.
  286. Examples
  287. ========
  288. >>> from sympy import is_monotonic
  289. >>> from sympy.abc import x, y
  290. >>> from sympy import S, Interval, oo
  291. >>> is_monotonic(1/(x**2 - 3*x), Interval.open(S(3)/2, 3))
  292. True
  293. >>> is_monotonic(1/(x**2 - 3*x), Interval.open(1.5, 3))
  294. True
  295. >>> is_monotonic(1/(x**2 - 3*x), Interval.Lopen(3, oo))
  296. True
  297. >>> is_monotonic(x**3 - 3*x**2 + 4*x, S.Reals)
  298. True
  299. >>> is_monotonic(-x**2, S.Reals)
  300. False
  301. >>> is_monotonic(x**2 + y + 1, Interval(1, 2), x)
  302. True
  303. """
  304. from sympy.solvers.solveset import solveset
  305. expression = sympify(expression)
  306. free = expression.free_symbols
  307. if symbol is None and len(free) > 1:
  308. raise NotImplementedError(
  309. 'is_monotonic has not yet been implemented'
  310. ' for all multivariate expressions.'
  311. )
  312. variable = symbol or (free.pop() if free else Symbol('x'))
  313. turning_points = solveset(expression.diff(variable), variable, interval)
  314. return interval.intersection(turning_points) is S.EmptySet