limits.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. from sympy.calculus.accumulationbounds import AccumBounds
  2. from sympy.core import S, Symbol, Add, sympify, Expr, PoleError, Mul
  3. from sympy.core.exprtools import factor_terms
  4. from sympy.core.numbers import Float
  5. from sympy.functions.combinatorial.factorials import factorial
  6. from sympy.functions.elementary.complexes import (Abs, sign)
  7. from sympy.functions.elementary.exponential import (exp, log)
  8. from sympy.functions.special.gamma_functions import gamma
  9. from sympy.polys import PolynomialError, factor
  10. from sympy.series.order import Order
  11. from sympy.simplify.powsimp import powsimp
  12. from sympy.simplify.ratsimp import ratsimp
  13. from sympy.simplify.simplify import nsimplify, together
  14. from .gruntz import gruntz
  15. def limit(e, z, z0, dir="+"):
  16. """Computes the limit of ``e(z)`` at the point ``z0``.
  17. Parameters
  18. ==========
  19. e : expression, the limit of which is to be taken
  20. z : symbol representing the variable in the limit.
  21. Other symbols are treated as constants. Multivariate limits
  22. are not supported.
  23. z0 : the value toward which ``z`` tends. Can be any expression,
  24. including ``oo`` and ``-oo``.
  25. dir : string, optional (default: "+")
  26. The limit is bi-directional if ``dir="+-"``, from the right
  27. (z->z0+) if ``dir="+"``, and from the left (z->z0-) if
  28. ``dir="-"``. For infinite ``z0`` (``oo`` or ``-oo``), the ``dir``
  29. argument is determined from the direction of the infinity
  30. (i.e., ``dir="-"`` for ``oo``).
  31. Examples
  32. ========
  33. >>> from sympy import limit, sin, oo
  34. >>> from sympy.abc import x
  35. >>> limit(sin(x)/x, x, 0)
  36. 1
  37. >>> limit(1/x, x, 0) # default dir='+'
  38. oo
  39. >>> limit(1/x, x, 0, dir="-")
  40. -oo
  41. >>> limit(1/x, x, 0, dir='+-')
  42. zoo
  43. >>> limit(1/x, x, oo)
  44. 0
  45. Notes
  46. =====
  47. First we try some heuristics for easy and frequent cases like "x", "1/x",
  48. "x**2" and similar, so that it's fast. For all other cases, we use the
  49. Gruntz algorithm (see the gruntz() function).
  50. See Also
  51. ========
  52. limit_seq : returns the limit of a sequence.
  53. """
  54. return Limit(e, z, z0, dir).doit(deep=False)
  55. def heuristics(e, z, z0, dir):
  56. """Computes the limit of an expression term-wise.
  57. Parameters are the same as for the ``limit`` function.
  58. Works with the arguments of expression ``e`` one by one, computing
  59. the limit of each and then combining the results. This approach
  60. works only for simple limits, but it is fast.
  61. """
  62. rv = None
  63. if abs(z0) is S.Infinity:
  64. rv = limit(e.subs(z, 1/z), z, S.Zero, "+" if z0 is S.Infinity else "-")
  65. if isinstance(rv, Limit):
  66. return
  67. elif e.is_Mul or e.is_Add or e.is_Pow or e.is_Function:
  68. r = []
  69. for a in e.args:
  70. l = limit(a, z, z0, dir)
  71. if l.has(S.Infinity) and l.is_finite is None:
  72. if isinstance(e, Add):
  73. m = factor_terms(e)
  74. if not isinstance(m, Mul): # try together
  75. m = together(m)
  76. if not isinstance(m, Mul): # try factor if the previous methods failed
  77. m = factor(e)
  78. if isinstance(m, Mul):
  79. return heuristics(m, z, z0, dir)
  80. return
  81. return
  82. elif isinstance(l, Limit):
  83. return
  84. elif l is S.NaN:
  85. return
  86. else:
  87. r.append(l)
  88. if r:
  89. rv = e.func(*r)
  90. if rv is S.NaN and e.is_Mul and any(isinstance(rr, AccumBounds) for rr in r):
  91. r2 = []
  92. e2 = []
  93. for ii in range(len(r)):
  94. if isinstance(r[ii], AccumBounds):
  95. r2.append(r[ii])
  96. else:
  97. e2.append(e.args[ii])
  98. if len(e2) > 0:
  99. e3 = Mul(*e2).simplify()
  100. l = limit(e3, z, z0, dir)
  101. rv = l * Mul(*r2)
  102. if rv is S.NaN:
  103. try:
  104. rat_e = ratsimp(e)
  105. except PolynomialError:
  106. return
  107. if rat_e is S.NaN or rat_e == e:
  108. return
  109. return limit(rat_e, z, z0, dir)
  110. return rv
  111. class Limit(Expr):
  112. """Represents an unevaluated limit.
  113. Examples
  114. ========
  115. >>> from sympy import Limit, sin
  116. >>> from sympy.abc import x
  117. >>> Limit(sin(x)/x, x, 0)
  118. Limit(sin(x)/x, x, 0)
  119. >>> Limit(1/x, x, 0, dir="-")
  120. Limit(1/x, x, 0, dir='-')
  121. """
  122. def __new__(cls, e, z, z0, dir="+"):
  123. e = sympify(e)
  124. z = sympify(z)
  125. z0 = sympify(z0)
  126. if z0 is S.Infinity:
  127. dir = "-"
  128. elif z0 is S.NegativeInfinity:
  129. dir = "+"
  130. if(z0.has(z)):
  131. raise NotImplementedError("Limits approaching a variable point are"
  132. " not supported (%s -> %s)" % (z, z0))
  133. if isinstance(dir, str):
  134. dir = Symbol(dir)
  135. elif not isinstance(dir, Symbol):
  136. raise TypeError("direction must be of type basestring or "
  137. "Symbol, not %s" % type(dir))
  138. if str(dir) not in ('+', '-', '+-'):
  139. raise ValueError("direction must be one of '+', '-' "
  140. "or '+-', not %s" % dir)
  141. obj = Expr.__new__(cls)
  142. obj._args = (e, z, z0, dir)
  143. return obj
  144. @property
  145. def free_symbols(self):
  146. e = self.args[0]
  147. isyms = e.free_symbols
  148. isyms.difference_update(self.args[1].free_symbols)
  149. isyms.update(self.args[2].free_symbols)
  150. return isyms
  151. def pow_heuristics(self, e):
  152. _, z, z0, _ = self.args
  153. b1, e1 = e.base, e.exp
  154. if not b1.has(z):
  155. res = limit(e1*log(b1), z, z0)
  156. return exp(res)
  157. ex_lim = limit(e1, z, z0)
  158. base_lim = limit(b1, z, z0)
  159. if base_lim is S.One:
  160. if ex_lim in (S.Infinity, S.NegativeInfinity):
  161. res = limit(e1*(b1 - 1), z, z0)
  162. return exp(res)
  163. if base_lim is S.NegativeInfinity and ex_lim is S.Infinity:
  164. return S.ComplexInfinity
  165. def doit(self, **hints):
  166. """Evaluates the limit.
  167. Parameters
  168. ==========
  169. deep : bool, optional (default: True)
  170. Invoke the ``doit`` method of the expressions involved before
  171. taking the limit.
  172. hints : optional keyword arguments
  173. To be passed to ``doit`` methods; only used if deep is True.
  174. """
  175. e, z, z0, dir = self.args
  176. if z0 is S.ComplexInfinity:
  177. raise NotImplementedError("Limits at complex "
  178. "infinity are not implemented")
  179. if hints.get('deep', True):
  180. e = e.doit(**hints)
  181. z = z.doit(**hints)
  182. z0 = z0.doit(**hints)
  183. if e == z:
  184. return z0
  185. if not e.has(z):
  186. return e
  187. if z0 is S.NaN:
  188. return S.NaN
  189. if e.has(S.Infinity, S.NegativeInfinity, S.ComplexInfinity, S.NaN):
  190. return self
  191. if e.is_Order:
  192. return Order(limit(e.expr, z, z0), *e.args[1:])
  193. cdir = 0
  194. if str(dir) == "+":
  195. cdir = 1
  196. elif str(dir) == "-":
  197. cdir = -1
  198. def set_signs(expr):
  199. if not expr.args:
  200. return expr
  201. newargs = tuple(set_signs(arg) for arg in expr.args)
  202. if newargs != expr.args:
  203. expr = expr.func(*newargs)
  204. abs_flag = isinstance(expr, Abs)
  205. sign_flag = isinstance(expr, sign)
  206. if abs_flag or sign_flag:
  207. sig = limit(expr.args[0], z, z0, dir)
  208. if sig.is_zero:
  209. sig = limit(1/expr.args[0], z, z0, dir)
  210. if sig.is_extended_real:
  211. if (sig < 0) == True:
  212. return -expr.args[0] if abs_flag else S.NegativeOne
  213. elif (sig > 0) == True:
  214. return expr.args[0] if abs_flag else S.One
  215. return expr
  216. if e.has(Float):
  217. # Convert floats like 0.5 to exact SymPy numbers like S.Half, to
  218. # prevent rounding errors which can lead to unexpected execution
  219. # of conditional blocks that work on comparisons
  220. # Also see comments in https://github.com/sympy/sympy/issues/19453
  221. e = nsimplify(e)
  222. e = set_signs(e)
  223. if e.is_meromorphic(z, z0):
  224. if abs(z0) is S.Infinity:
  225. newe = e.subs(z, 1/z)
  226. # cdir changes sign as oo- should become 0+
  227. cdir = -cdir
  228. else:
  229. newe = e.subs(z, z + z0)
  230. try:
  231. coeff, ex = newe.leadterm(z, cdir=cdir)
  232. except ValueError:
  233. pass
  234. else:
  235. if ex > 0:
  236. return S.Zero
  237. elif ex == 0:
  238. return coeff
  239. if cdir == 1 or not(int(ex) & 1):
  240. return S.Infinity*sign(coeff)
  241. elif cdir == -1:
  242. return S.NegativeInfinity*sign(coeff)
  243. else:
  244. return S.ComplexInfinity
  245. if abs(z0) is S.Infinity:
  246. if e.is_Mul:
  247. e = factor_terms(e)
  248. newe = e.subs(z, 1/z)
  249. # cdir changes sign as oo- should become 0+
  250. cdir = -cdir
  251. else:
  252. newe = e.subs(z, z + z0)
  253. try:
  254. coeff, ex = newe.leadterm(z, cdir=cdir)
  255. except (ValueError, NotImplementedError, PoleError):
  256. # The NotImplementedError catching is for custom functions
  257. e = powsimp(e)
  258. if e.is_Pow:
  259. r = self.pow_heuristics(e)
  260. if r is not None:
  261. return r
  262. else:
  263. if coeff.has(S.Infinity, S.NegativeInfinity, S.ComplexInfinity):
  264. return self
  265. if not coeff.has(z):
  266. if ex.is_positive:
  267. return S.Zero
  268. elif ex == 0:
  269. return coeff
  270. elif ex.is_negative:
  271. if ex.is_integer:
  272. if cdir == 1 or ex.is_even:
  273. return S.Infinity*sign(coeff)
  274. elif cdir == -1:
  275. return S.NegativeInfinity*sign(coeff)
  276. else:
  277. return S.ComplexInfinity
  278. else:
  279. if cdir == 1:
  280. return S.Infinity*sign(coeff)
  281. elif cdir == -1:
  282. return S.Infinity*sign(coeff)*S.NegativeOne**ex
  283. else:
  284. return S.ComplexInfinity
  285. # gruntz fails on factorials but works with the gamma function
  286. # If no factorial term is present, e should remain unchanged.
  287. # factorial is defined to be zero for negative inputs (which
  288. # differs from gamma) so only rewrite for positive z0.
  289. if z0.is_extended_positive:
  290. e = e.rewrite(factorial, gamma)
  291. l = None
  292. try:
  293. if str(dir) == '+-':
  294. r = gruntz(e, z, z0, '+')
  295. l = gruntz(e, z, z0, '-')
  296. if l != r:
  297. raise ValueError("The limit does not exist since "
  298. "left hand limit = %s and right hand limit = %s"
  299. % (l, r))
  300. else:
  301. r = gruntz(e, z, z0, dir)
  302. if r is S.NaN or l is S.NaN:
  303. raise PoleError()
  304. except (PoleError, ValueError):
  305. if l is not None:
  306. raise
  307. r = heuristics(e, z, z0, dir)
  308. if r is None:
  309. return self
  310. return r