appellseqs.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. r"""
  2. Efficient functions for generating Appell sequences.
  3. An Appell sequence is a zero-indexed sequence of polynomials `p_i(x)`
  4. satisfying `p_{i+1}'(x)=(i+1)p_i(x)` for all `i`. This definition leads
  5. to the following iterative algorithm:
  6. .. math :: p_0(x) = c_0,\ p_i(x) = i \int_0^x p_{i-1}(t)\,dt + c_i
  7. The constant coefficients `c_i` are usually determined from the
  8. just-evaluated integral and `i`.
  9. Appell sequences satisfy the following identity from umbral calculus:
  10. .. math :: p_n(x+y) = \sum_{k=0}^n \binom{n}{k} p_k(x) y^{n-k}
  11. References
  12. ==========
  13. .. [1] https://en.wikipedia.org/wiki/Appell_sequence
  14. .. [2] Peter Luschny, "An introduction to the Bernoulli function",
  15. https://arxiv.org/abs/2009.06743
  16. """
  17. from sympy.polys.densearith import dup_mul_ground, dup_sub_ground, dup_quo_ground
  18. from sympy.polys.densetools import dup_eval, dup_integrate
  19. from sympy.polys.domains import ZZ, QQ
  20. from sympy.polys.polytools import named_poly
  21. from sympy.utilities import public
  22. def dup_bernoulli(n, K):
  23. """Low-level implementation of Bernoulli polynomials."""
  24. if n < 1:
  25. return [K.one]
  26. p = [K.one, K(-1,2)]
  27. for i in range(2, n+1):
  28. p = dup_integrate(dup_mul_ground(p, K(i), K), 1, K)
  29. if i % 2 == 0:
  30. p = dup_sub_ground(p, dup_eval(p, K(1,2), K) * K(1<<(i-1), (1<<i)-1), K)
  31. return p
  32. @public
  33. def bernoulli_poly(n, x=None, polys=False):
  34. r"""Generates the Bernoulli polynomial `\operatorname{B}_n(x)`.
  35. `\operatorname{B}_n(x)` is the unique polynomial satisfying
  36. .. math :: \int_{x}^{x+1} \operatorname{B}_n(t) \,dt = x^n.
  37. Based on this, we have for nonnegative integer `s` and integer
  38. `a` and `b`
  39. .. math :: \sum_{k=a}^{b} k^s = \frac{\operatorname{B}_{s+1}(b+1) -
  40. \operatorname{B}_{s+1}(a)}{s+1}
  41. which is related to Jakob Bernoulli's original motivation for introducing
  42. the Bernoulli numbers, the values of these polynomials at `x = 1`.
  43. Examples
  44. ========
  45. >>> from sympy import summation
  46. >>> from sympy.abc import x
  47. >>> from sympy.polys import bernoulli_poly
  48. >>> bernoulli_poly(5, x)
  49. x**5 - 5*x**4/2 + 5*x**3/3 - x/6
  50. >>> def psum(p, a, b):
  51. ... return (bernoulli_poly(p+1,b+1) - bernoulli_poly(p+1,a)) / (p+1)
  52. >>> psum(4, -6, 27)
  53. 3144337
  54. >>> summation(x**4, (x, -6, 27))
  55. 3144337
  56. >>> psum(1, 1, x).factor()
  57. x*(x + 1)/2
  58. >>> psum(2, 1, x).factor()
  59. x*(x + 1)*(2*x + 1)/6
  60. >>> psum(3, 1, x).factor()
  61. x**2*(x + 1)**2/4
  62. Parameters
  63. ==========
  64. n : int
  65. Degree of the polynomial.
  66. x : optional
  67. polys : bool, optional
  68. If True, return a Poly, otherwise (default) return an expression.
  69. See Also
  70. ========
  71. sympy.functions.combinatorial.numbers.bernoulli
  72. References
  73. ==========
  74. .. [1] https://en.wikipedia.org/wiki/Bernoulli_polynomials
  75. """
  76. return named_poly(n, dup_bernoulli, QQ, "Bernoulli polynomial", (x,), polys)
  77. def dup_bernoulli_c(n, K):
  78. """Low-level implementation of central Bernoulli polynomials."""
  79. p = [K.one]
  80. for i in range(1, n+1):
  81. p = dup_integrate(dup_mul_ground(p, K(i), K), 1, K)
  82. if i % 2 == 0:
  83. p = dup_sub_ground(p, dup_eval(p, K.one, K) * K((1<<(i-1))-1, (1<<i)-1), K)
  84. return p
  85. @public
  86. def bernoulli_c_poly(n, x=None, polys=False):
  87. r"""Generates the central Bernoulli polynomial `\operatorname{B}_n^c(x)`.
  88. These are scaled and shifted versions of the plain Bernoulli polynomials,
  89. done in such a way that `\operatorname{B}_n^c(x)` is an even or odd function
  90. for even or odd `n` respectively:
  91. .. math :: \operatorname{B}_n^c(x) = 2^n \operatorname{B}_n
  92. \left(\frac{x+1}{2}\right)
  93. Parameters
  94. ==========
  95. n : int
  96. Degree of the polynomial.
  97. x : optional
  98. polys : bool, optional
  99. If True, return a Poly, otherwise (default) return an expression.
  100. """
  101. return named_poly(n, dup_bernoulli_c, QQ, "central Bernoulli polynomial", (x,), polys)
  102. def dup_genocchi(n, K):
  103. """Low-level implementation of Genocchi polynomials."""
  104. if n < 1:
  105. return [K.zero]
  106. p = [-K.one]
  107. for i in range(2, n+1):
  108. p = dup_integrate(dup_mul_ground(p, K(i), K), 1, K)
  109. if i % 2 == 0:
  110. p = dup_sub_ground(p, dup_eval(p, K.one, K) // K(2), K)
  111. return p
  112. @public
  113. def genocchi_poly(n, x=None, polys=False):
  114. r"""Generates the Genocchi polynomial `\operatorname{G}_n(x)`.
  115. `\operatorname{G}_n(x)` is twice the difference between the plain and
  116. central Bernoulli polynomials, so has degree `n-1`:
  117. .. math :: \operatorname{G}_n(x) = 2 (\operatorname{B}_n(x) -
  118. \operatorname{B}_n^c(x))
  119. The factor of 2 in the definition endows `\operatorname{G}_n(x)` with
  120. integer coefficients.
  121. Parameters
  122. ==========
  123. n : int
  124. Degree of the polynomial plus one.
  125. x : optional
  126. polys : bool, optional
  127. If True, return a Poly, otherwise (default) return an expression.
  128. See Also
  129. ========
  130. sympy.functions.combinatorial.numbers.genocchi
  131. """
  132. return named_poly(n, dup_genocchi, ZZ, "Genocchi polynomial", (x,), polys)
  133. def dup_euler(n, K):
  134. """Low-level implementation of Euler polynomials."""
  135. return dup_quo_ground(dup_genocchi(n+1, ZZ), K(-n-1), K)
  136. @public
  137. def euler_poly(n, x=None, polys=False):
  138. r"""Generates the Euler polynomial `\operatorname{E}_n(x)`.
  139. These are scaled and reindexed versions of the Genocchi polynomials:
  140. .. math :: \operatorname{E}_n(x) = -\frac{\operatorname{G}_{n+1}(x)}{n+1}
  141. Parameters
  142. ==========
  143. n : int
  144. Degree of the polynomial.
  145. x : optional
  146. polys : bool, optional
  147. If True, return a Poly, otherwise (default) return an expression.
  148. See Also
  149. ========
  150. sympy.functions.combinatorial.numbers.euler
  151. """
  152. return named_poly(n, dup_euler, QQ, "Euler polynomial", (x,), polys)
  153. def dup_andre(n, K):
  154. """Low-level implementation of Andre polynomials."""
  155. p = [K.one]
  156. for i in range(1, n+1):
  157. p = dup_integrate(dup_mul_ground(p, K(i), K), 1, K)
  158. if i % 2 == 0:
  159. p = dup_sub_ground(p, dup_eval(p, K.one, K), K)
  160. return p
  161. @public
  162. def andre_poly(n, x=None, polys=False):
  163. r"""Generates the Andre polynomial `\mathcal{A}_n(x)`.
  164. This is the Appell sequence where the constant coefficients form the sequence
  165. of Euler numbers ``euler(n)``. As such they have integer coefficients
  166. and parities matching the parity of `n`.
  167. Luschny calls these the *Swiss-knife polynomials* because their values
  168. at 0 and 1 can be simply transformed into both the Bernoulli and Euler
  169. numbers. Here they are called the Andre polynomials because
  170. `|\mathcal{A}_n(n\bmod 2)|` for `n \ge 0` generates what Luschny calls
  171. the *Andre numbers*, A000111 in the OEIS.
  172. Examples
  173. ========
  174. >>> from sympy import bernoulli, euler, genocchi
  175. >>> from sympy.abc import x
  176. >>> from sympy.polys import andre_poly
  177. >>> andre_poly(9, x)
  178. x**9 - 36*x**7 + 630*x**5 - 5124*x**3 + 12465*x
  179. >>> [andre_poly(n, 0) for n in range(11)]
  180. [1, 0, -1, 0, 5, 0, -61, 0, 1385, 0, -50521]
  181. >>> [euler(n) for n in range(11)]
  182. [1, 0, -1, 0, 5, 0, -61, 0, 1385, 0, -50521]
  183. >>> [andre_poly(n-1, 1) * n / (4**n - 2**n) for n in range(1, 11)]
  184. [1/2, 1/6, 0, -1/30, 0, 1/42, 0, -1/30, 0, 5/66]
  185. >>> [bernoulli(n) for n in range(1, 11)]
  186. [1/2, 1/6, 0, -1/30, 0, 1/42, 0, -1/30, 0, 5/66]
  187. >>> [-andre_poly(n-1, -1) * n / (-2)**(n-1) for n in range(1, 11)]
  188. [-1, -1, 0, 1, 0, -3, 0, 17, 0, -155]
  189. >>> [genocchi(n) for n in range(1, 11)]
  190. [-1, -1, 0, 1, 0, -3, 0, 17, 0, -155]
  191. >>> [abs(andre_poly(n, n%2)) for n in range(11)]
  192. [1, 1, 1, 2, 5, 16, 61, 272, 1385, 7936, 50521]
  193. Parameters
  194. ==========
  195. n : int
  196. Degree of the polynomial.
  197. x : optional
  198. polys : bool, optional
  199. If True, return a Poly, otherwise (default) return an expression.
  200. See Also
  201. ========
  202. sympy.functions.combinatorial.numbers.andre
  203. References
  204. ==========
  205. .. [1] Peter Luschny, "An introduction to the Bernoulli function",
  206. https://arxiv.org/abs/2009.06743
  207. """
  208. return named_poly(n, dup_andre, ZZ, "Andre polynomial", (x,), polys)