gosper.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. """Gosper's algorithm for hypergeometric summation. """
  2. from sympy.core import S, Dummy, symbols
  3. from sympy.polys import Poly, parallel_poly_from_expr, factor
  4. from sympy.utilities.iterables import is_sequence
  5. def gosper_normal(f, g, n, polys=True):
  6. r"""
  7. Compute the Gosper's normal form of ``f`` and ``g``.
  8. Explanation
  9. ===========
  10. Given relatively prime univariate polynomials ``f`` and ``g``,
  11. rewrite their quotient to a normal form defined as follows:
  12. .. math::
  13. \frac{f(n)}{g(n)} = Z \cdot \frac{A(n) C(n+1)}{B(n) C(n)}
  14. where ``Z`` is an arbitrary constant and ``A``, ``B``, ``C`` are
  15. monic polynomials in ``n`` with the following properties:
  16. 1. `\gcd(A(n), B(n+h)) = 1 \forall h \in \mathbb{N}`
  17. 2. `\gcd(B(n), C(n+1)) = 1`
  18. 3. `\gcd(A(n), C(n)) = 1`
  19. This normal form, or rational factorization in other words, is a
  20. crucial step in Gosper's algorithm and in solving of difference
  21. equations. It can be also used to decide if two hypergeometric
  22. terms are similar or not.
  23. This procedure will return a tuple containing elements of this
  24. factorization in the form ``(Z*A, B, C)``.
  25. Examples
  26. ========
  27. >>> from sympy.concrete.gosper import gosper_normal
  28. >>> from sympy.abc import n
  29. >>> gosper_normal(4*n+5, 2*(4*n+1)*(2*n+3), n, polys=False)
  30. (1/4, n + 3/2, n + 1/4)
  31. """
  32. (p, q), opt = parallel_poly_from_expr(
  33. (f, g), n, field=True, extension=True)
  34. a, A = p.LC(), p.monic()
  35. b, B = q.LC(), q.monic()
  36. C, Z = A.one, a/b
  37. h = Dummy('h')
  38. D = Poly(n + h, n, h, domain=opt.domain)
  39. R = A.resultant(B.compose(D))
  40. roots = {r for r in R.ground_roots().keys() if r.is_Integer and r >= 0}
  41. for i in sorted(roots):
  42. d = A.gcd(B.shift(+i))
  43. A = A.quo(d)
  44. B = B.quo(d.shift(-i))
  45. for j in range(1, i + 1):
  46. C *= d.shift(-j)
  47. A = A.mul_ground(Z)
  48. if not polys:
  49. A = A.as_expr()
  50. B = B.as_expr()
  51. C = C.as_expr()
  52. return A, B, C
  53. def gosper_term(f, n):
  54. r"""
  55. Compute Gosper's hypergeometric term for ``f``.
  56. Explanation
  57. ===========
  58. Suppose ``f`` is a hypergeometric term such that:
  59. .. math::
  60. s_n = \sum_{k=0}^{n-1} f_k
  61. and `f_k` does not depend on `n`. Returns a hypergeometric
  62. term `g_n` such that `g_{n+1} - g_n = f_n`.
  63. Examples
  64. ========
  65. >>> from sympy.concrete.gosper import gosper_term
  66. >>> from sympy import factorial
  67. >>> from sympy.abc import n
  68. >>> gosper_term((4*n + 1)*factorial(n)/factorial(2*n + 1), n)
  69. (-n - 1/2)/(n + 1/4)
  70. """
  71. from sympy.simplify import hypersimp
  72. r = hypersimp(f, n)
  73. if r is None:
  74. return None # 'f' is *not* a hypergeometric term
  75. p, q = r.as_numer_denom()
  76. A, B, C = gosper_normal(p, q, n)
  77. B = B.shift(-1)
  78. N = S(A.degree())
  79. M = S(B.degree())
  80. K = S(C.degree())
  81. if (N != M) or (A.LC() != B.LC()):
  82. D = {K - max(N, M)}
  83. elif not N:
  84. D = {K - N + 1, S.Zero}
  85. else:
  86. D = {K - N + 1, (B.nth(N - 1) - A.nth(N - 1))/A.LC()}
  87. for d in set(D):
  88. if not d.is_Integer or d < 0:
  89. D.remove(d)
  90. if not D:
  91. return None # 'f(n)' is *not* Gosper-summable
  92. d = max(D)
  93. coeffs = symbols('c:%s' % (d + 1), cls=Dummy)
  94. domain = A.get_domain().inject(*coeffs)
  95. x = Poly(coeffs, n, domain=domain)
  96. H = A*x.shift(1) - B*x - C
  97. from sympy.solvers.solvers import solve
  98. solution = solve(H.coeffs(), coeffs)
  99. if solution is None:
  100. return None # 'f(n)' is *not* Gosper-summable
  101. x = x.as_expr().subs(solution)
  102. for coeff in coeffs:
  103. if coeff not in solution:
  104. x = x.subs(coeff, 0)
  105. if x.is_zero:
  106. return None # 'f(n)' is *not* Gosper-summable
  107. else:
  108. return B.as_expr()*x/C.as_expr()
  109. def gosper_sum(f, k):
  110. r"""
  111. Gosper's hypergeometric summation algorithm.
  112. Explanation
  113. ===========
  114. Given a hypergeometric term ``f`` such that:
  115. .. math ::
  116. s_n = \sum_{k=0}^{n-1} f_k
  117. and `f(n)` does not depend on `n`, returns `g_{n} - g(0)` where
  118. `g_{n+1} - g_n = f_n`, or ``None`` if `s_n` cannot be expressed
  119. in closed form as a sum of hypergeometric terms.
  120. Examples
  121. ========
  122. >>> from sympy.concrete.gosper import gosper_sum
  123. >>> from sympy import factorial
  124. >>> from sympy.abc import n, k
  125. >>> f = (4*k + 1)*factorial(k)/factorial(2*k + 1)
  126. >>> gosper_sum(f, (k, 0, n))
  127. (-factorial(n) + 2*factorial(2*n + 1))/factorial(2*n + 1)
  128. >>> _.subs(n, 2) == sum(f.subs(k, i) for i in [0, 1, 2])
  129. True
  130. >>> gosper_sum(f, (k, 3, n))
  131. (-60*factorial(n) + factorial(2*n + 1))/(60*factorial(2*n + 1))
  132. >>> _.subs(n, 5) == sum(f.subs(k, i) for i in [3, 4, 5])
  133. True
  134. References
  135. ==========
  136. .. [1] Marko Petkovsek, Herbert S. Wilf, Doron Zeilberger, A = B,
  137. AK Peters, Ltd., Wellesley, MA, USA, 1997, pp. 73--100
  138. """
  139. indefinite = False
  140. if is_sequence(k):
  141. k, a, b = k
  142. else:
  143. indefinite = True
  144. g = gosper_term(f, k)
  145. if g is None:
  146. return None
  147. if indefinite:
  148. result = f*g
  149. else:
  150. result = (f*(g + 1)).subs(k, b) - (f*g).subs(k, a)
  151. if result is S.NaN:
  152. try:
  153. result = (f*(g + 1)).limit(k, b) - (f*g).limit(k, a)
  154. except NotImplementedError:
  155. result = None
  156. return factor(result)