limits.py 13 KB

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