gosper.py 5.4 KB

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