limitseq.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. """Limits of sequences"""
  2. from sympy.calculus.accumulationbounds import AccumulationBounds
  3. from sympy.core.add import Add
  4. from sympy.core.function import PoleError
  5. from sympy.core.power import Pow
  6. from sympy.core.singleton import S
  7. from sympy.core.symbol import Dummy
  8. from sympy.core.sympify import sympify
  9. from sympy.functions.combinatorial.numbers import fibonacci
  10. from sympy.functions.combinatorial.factorials import factorial, subfactorial
  11. from sympy.functions.special.gamma_functions import gamma
  12. from sympy.functions.elementary.complexes import Abs
  13. from sympy.functions.elementary.miscellaneous import Max, Min
  14. from sympy.functions.elementary.trigonometric import cos, sin
  15. from sympy.series.limits import Limit
  16. def difference_delta(expr, n=None, step=1):
  17. """Difference Operator.
  18. Explanation
  19. ===========
  20. Discrete analog of differential operator. Given a sequence x[n],
  21. returns the sequence x[n + step] - x[n].
  22. Examples
  23. ========
  24. >>> from sympy import difference_delta as dd
  25. >>> from sympy.abc import n
  26. >>> dd(n*(n + 1), n)
  27. 2*n + 2
  28. >>> dd(n*(n + 1), n, 2)
  29. 4*n + 6
  30. References
  31. ==========
  32. .. [1] https://reference.wolfram.com/language/ref/DifferenceDelta.html
  33. """
  34. expr = sympify(expr)
  35. if n is None:
  36. f = expr.free_symbols
  37. if len(f) == 1:
  38. n = f.pop()
  39. elif len(f) == 0:
  40. return S.Zero
  41. else:
  42. raise ValueError("Since there is more than one variable in the"
  43. " expression, a variable must be supplied to"
  44. " take the difference of %s" % expr)
  45. step = sympify(step)
  46. if step.is_number is False or step.is_finite is False:
  47. raise ValueError("Step should be a finite number.")
  48. if hasattr(expr, '_eval_difference_delta'):
  49. result = expr._eval_difference_delta(n, step)
  50. if result:
  51. return result
  52. return expr.subs(n, n + step) - expr
  53. def dominant(expr, n):
  54. """Finds the dominant term in a sum, that is a term that dominates
  55. every other term.
  56. Explanation
  57. ===========
  58. If limit(a/b, n, oo) is oo then a dominates b.
  59. If limit(a/b, n, oo) is 0 then b dominates a.
  60. Otherwise, a and b are comparable.
  61. If there is no unique dominant term, then returns ``None``.
  62. Examples
  63. ========
  64. >>> from sympy import Sum
  65. >>> from sympy.series.limitseq import dominant
  66. >>> from sympy.abc import n, k
  67. >>> dominant(5*n**3 + 4*n**2 + n + 1, n)
  68. 5*n**3
  69. >>> dominant(2**n + Sum(k, (k, 0, n)), n)
  70. 2**n
  71. See Also
  72. ========
  73. sympy.series.limitseq.dominant
  74. """
  75. terms = Add.make_args(expr.expand(func=True))
  76. term0 = terms[-1]
  77. comp = [term0] # comparable terms
  78. for t in terms[:-1]:
  79. r = term0/t
  80. e = r.gammasimp()
  81. if e == r:
  82. e = r.factor()
  83. l = limit_seq(e, n)
  84. if l is None:
  85. return None
  86. elif l.is_zero:
  87. term0 = t
  88. comp = [term0]
  89. elif l not in [S.Infinity, S.NegativeInfinity]:
  90. comp.append(t)
  91. if len(comp) > 1:
  92. return None
  93. return term0
  94. def _limit_inf(expr, n):
  95. try:
  96. return Limit(expr, n, S.Infinity).doit(deep=False)
  97. except (NotImplementedError, PoleError):
  98. return None
  99. def _limit_seq(expr, n, trials):
  100. from sympy.concrete.summations import Sum
  101. for i in range(trials):
  102. if not expr.has(Sum):
  103. result = _limit_inf(expr, n)
  104. if result is not None:
  105. return result
  106. num, den = expr.as_numer_denom()
  107. if not den.has(n) or not num.has(n):
  108. result = _limit_inf(expr.doit(), n)
  109. if result is not None:
  110. return result
  111. return None
  112. num, den = (difference_delta(t.expand(), n) for t in [num, den])
  113. expr = (num / den).gammasimp()
  114. if not expr.has(Sum):
  115. result = _limit_inf(expr, n)
  116. if result is not None:
  117. return result
  118. num, den = expr.as_numer_denom()
  119. num = dominant(num, n)
  120. if num is None:
  121. return None
  122. den = dominant(den, n)
  123. if den is None:
  124. return None
  125. expr = (num / den).gammasimp()
  126. def limit_seq(expr, n=None, trials=5):
  127. """Finds the limit of a sequence as index ``n`` tends to infinity.
  128. Parameters
  129. ==========
  130. expr : Expr
  131. SymPy expression for the ``n-th`` term of the sequence
  132. n : Symbol, optional
  133. The index of the sequence, an integer that tends to positive
  134. infinity. If None, inferred from the expression unless it has
  135. multiple symbols.
  136. trials: int, optional
  137. The algorithm is highly recursive. ``trials`` is a safeguard from
  138. infinite recursion in case the limit is not easily computed by the
  139. algorithm. Try increasing ``trials`` if the algorithm returns ``None``.
  140. Admissible Terms
  141. ================
  142. The algorithm is designed for sequences built from rational functions,
  143. indefinite sums, and indefinite products over an indeterminate n. Terms of
  144. alternating sign are also allowed, but more complex oscillatory behavior is
  145. not supported.
  146. Examples
  147. ========
  148. >>> from sympy import limit_seq, Sum, binomial
  149. >>> from sympy.abc import n, k, m
  150. >>> limit_seq((5*n**3 + 3*n**2 + 4) / (3*n**3 + 4*n - 5), n)
  151. 5/3
  152. >>> limit_seq(binomial(2*n, n) / Sum(binomial(2*k, k), (k, 1, n)), n)
  153. 3/4
  154. >>> limit_seq(Sum(k**2 * Sum(2**m/m, (m, 1, k)), (k, 1, n)) / (2**n*n), n)
  155. 4
  156. See Also
  157. ========
  158. sympy.series.limitseq.dominant
  159. References
  160. ==========
  161. .. [1] Computing Limits of Sequences - Manuel Kauers
  162. """
  163. from sympy.concrete.summations import Sum
  164. if n is None:
  165. free = expr.free_symbols
  166. if len(free) == 1:
  167. n = free.pop()
  168. elif not free:
  169. return expr
  170. else:
  171. raise ValueError("Expression has more than one variable. "
  172. "Please specify a variable.")
  173. elif n not in expr.free_symbols:
  174. return expr
  175. expr = expr.rewrite(fibonacci, S.GoldenRatio)
  176. expr = expr.rewrite(factorial, subfactorial, gamma)
  177. n_ = Dummy("n", integer=True, positive=True)
  178. n1 = Dummy("n", odd=True, positive=True)
  179. n2 = Dummy("n", even=True, positive=True)
  180. # If there is a negative term raised to a power involving n, or a
  181. # trigonometric function, then consider even and odd n separately.
  182. powers = (p.as_base_exp() for p in expr.atoms(Pow))
  183. if (any(b.is_negative and e.has(n) for b, e in powers) or
  184. expr.has(cos, sin)):
  185. L1 = _limit_seq(expr.xreplace({n: n1}), n1, trials)
  186. if L1 is not None:
  187. L2 = _limit_seq(expr.xreplace({n: n2}), n2, trials)
  188. if L1 != L2:
  189. if L1.is_comparable and L2.is_comparable:
  190. return AccumulationBounds(Min(L1, L2), Max(L1, L2))
  191. else:
  192. return None
  193. else:
  194. L1 = _limit_seq(expr.xreplace({n: n_}), n_, trials)
  195. if L1 is not None:
  196. return L1
  197. else:
  198. if expr.is_Add:
  199. limits = [limit_seq(term, n, trials) for term in expr.args]
  200. if any(result is None for result in limits):
  201. return None
  202. else:
  203. return Add(*limits)
  204. # Maybe the absolute value is easier to deal with (though not if
  205. # it has a Sum). If it tends to 0, the limit is 0.
  206. elif not expr.has(Sum):
  207. lim = _limit_seq(Abs(expr.xreplace({n: n_})), n_, trials)
  208. if lim is not None and lim.is_zero:
  209. return S.Zero