Skip to content
This repository was archived by the owner on Jan 30, 2023. It is now read-only.

Commit f089c13

Browse files
committed
22090: Gosper algorithm and Wilf-Zeilberger certificate
1 parent 89e6ef4 commit f089c13

File tree

3 files changed

+369
-1
lines changed

3 files changed

+369
-1
lines changed

src/sage/libs/pynac/pynac.pxd

+6-1
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,12 @@ cdef extern from "sage/libs/pynac/wrap.h":
150150
py_object_from_numeric(GEx e) except +
151151

152152
# Algorithms
153-
GEx g_gcd "gcd"(GEx a, GEx b) except +
153+
GEx g_gcd "gcd"(GEx a, GEx b) except +
154+
GEx g_gosper_term "gosper_term"(GEx the_ex, GEx n) except +
155+
GEx g_gosper_sum_definite "gosper_sum_definite"(GEx the_ex,
156+
GEx n, GEx a, GEx b, int* p) except +
157+
GEx g_gosper_sum_indefinite "gosper_sum_indefinite"(GEx the_ex,
158+
GEx n, int* p) except +
154159
GEx to_gamma(GEx expr) except +
155160
GEx gamma_normalize(GEx expr) except +
156161
GEx g_resultant "resultant"(GEx a, GEx b, GEx v) except +

src/sage/symbolic/expression.pyx

+147
Original file line numberDiff line numberDiff line change
@@ -6654,6 +6654,153 @@ cdef class Expression(CommutativeRingElement):
66546654
sig_off()
66556655
return new_Expression_from_GEx(self._parent, x)
66566656

6657+
def gosper_sum(self, *args):
6658+
"""
6659+
Return the summation of this hypergeometric expression using
6660+
Gosper's algorithm.
6661+
6662+
INPUT:
6663+
6664+
- a symbolic expression that may contain rational functions,
6665+
powers, factorials, gamma function terms, binomial
6666+
coefficients, and Pochhammer symbols that are rational-linear
6667+
in their arguments
6668+
6669+
- the main variable and, optionally, summation limits
6670+
6671+
EXAMPLES::
6672+
6673+
sage: a,b,k,m,n = var('a b k m n')
6674+
sage: SR(1).gosper_sum(n)
6675+
n
6676+
sage: SR(1).gosper_sum(n,5,8)
6677+
4
6678+
sage: n.gosper_sum(n)
6679+
1/2*(n - 1)*n
6680+
sage: n.gosper_sum(n,0,5)
6681+
15
6682+
sage: n.gosper_sum(n,0,m)
6683+
1/2*(m + 1)*m
6684+
sage: n.gosper_sum(n,a,b)
6685+
-1/2*(a + b)*(a - b - 1)
6686+
6687+
::
6688+
6689+
sage: (factorial(m + n)/factorial(n)).gosper_sum(n)
6690+
n*factorial(m + n)/((m + 1)*factorial(n))
6691+
sage: (binomial(m + n, n)).gosper_sum(n)
6692+
n*binomial(m + n, n)/(m + 1)
6693+
sage: (binomial(m + n, n)).gosper_sum(n, 0, a)
6694+
(a + m + 1)*binomial(a + m, a)/(m + 1)
6695+
sage: (binomial(m + n, n)).gosper_sum(n, 0, 5)
6696+
1/120*(m + 6)*(m + 5)*(m + 4)*(m + 3)*(m + 2)
6697+
sage: (rising_factorial(a,n)/rising_factorial(b,n)).gosper_sum(n)
6698+
(b + n - 1)*gamma(a + n)*gamma(b)/((a - b + 1)*gamma(a)*gamma(b + n))
6699+
sage: factorial(n).gosper_term(n)
6700+
Traceback (most recent call last):
6701+
...
6702+
ValueError: expression not Gosper-summable
6703+
"""
6704+
cdef Expression s, a, b
6705+
cdef GEx x
6706+
cdef int i = 1
6707+
if len(args) > 1:
6708+
n, l1, l2 = args
6709+
s = self.coerce_in(n)
6710+
a = self.coerce_in(l1)
6711+
b = self.coerce_in(l2)
6712+
sig_on()
6713+
try:
6714+
x = g_gosper_sum_definite(self._gobj, s._gobj,
6715+
a._gobj, b._gobj, &i)
6716+
finally:
6717+
sig_off()
6718+
if i == 0:
6719+
raise ValueError("expression not Gosper-summable")
6720+
return new_Expression_from_GEx(self._parent, x)
6721+
else:
6722+
s = self.coerce_in(args[0])
6723+
sig_on()
6724+
try:
6725+
x = g_gosper_sum_indefinite(self._gobj, s._gobj, &i)
6726+
finally:
6727+
sig_off()
6728+
if i == 0:
6729+
raise ValueError("expression not Gosper-summable")
6730+
return new_Expression_from_GEx(self._parent, x)
6731+
6732+
def gosper_term(self, n):
6733+
"""
6734+
Return Gosper's hypergeometric term for ``self``.
6735+
6736+
Suppose ``f``=``self`` is a hypergeometric term such that:
6737+
6738+
.. math::
6739+
s_n = \sum_{k=0}^{n-1} f_k
6740+
6741+
and `f_k` doesn't depend on `n`. Return a hypergeometric
6742+
term `g_n` such that `g_{n+1} - g_n = f_n`.
6743+
6744+
EXAMPLES::
6745+
6746+
sage: _ = var('n')
6747+
sage: SR(1).gosper_term(n)
6748+
n
6749+
sage: n.gosper_term(n)
6750+
1/2*(n^2 - n)/n
6751+
sage: (n*factorial(n)).gosper_term(n)
6752+
1/n
6753+
sage: factorial(n).gosper_term(n)
6754+
Traceback (most recent call last):
6755+
...
6756+
ValueError: expression not Gosper-summable
6757+
"""
6758+
cdef Expression s
6759+
cdef int i = 1
6760+
s = self.coerce_in(n)
6761+
sig_on()
6762+
try:
6763+
x = g_gosper_term(self._gobj, s._gobj)
6764+
except ValueError:
6765+
raise ValueError("expression not Gosper-summable")
6766+
finally:
6767+
sig_off()
6768+
return new_Expression_from_GEx(self._parent, x)
6769+
6770+
def WZ_certificate(self, n, k):
6771+
r"""
6772+
Return the Wilf-Zeilberger certificate for this hypergeometric
6773+
summand in ``n``, ``k``.
6774+
6775+
To prove the identity `\sum_k F(n,k)=\textrm{const}` it suffices
6776+
to show that `F(n+1,k)-F(n,k)=G(n,k+1)-G(n,k),` with `G=RF` and
6777+
`R` the WZ certificate.
6778+
6779+
EXAMPLES:
6780+
6781+
To show that `\sum_k \binom{n}{k} = 2^n` do::
6782+
6783+
sage: _ = var('k n')
6784+
sage: F(n,k) = binomial(n,k) / 2^n
6785+
sage: c = F(n,k).WZ_certificate(n,k); c
6786+
1/2*k/(k - n - 1)
6787+
sage: G(n,k) = c * F(n,k); G
6788+
(n, k) |--> 1/2*k*binomial(n, k)/(2^n*(k - n - 1))
6789+
sage: (F(n+1,k) - F(n,k) - G(n,k+1) + G(n,k)).simplify_full()
6790+
0
6791+
"""
6792+
cdef Expression s, f
6793+
cdef int i = 1
6794+
s = self.coerce_in(k)
6795+
f = self.subs(n == n+1) - self
6796+
sig_on()
6797+
try:
6798+
x = g_gosper_term(f._gobj, s._gobj)
6799+
finally:
6800+
sig_off()
6801+
g = new_Expression_from_GEx(self._parent, x)
6802+
return (f*g / self).to_gamma().gamma_normalize().simplify_full().factor()
6803+
66576804
def lcm(self, b):
66586805
"""
66596806
Return the lcm of self and b.

src/sage/tests/gosper-sum.py

+216
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
"""
2+
References
3+
==========
4+
5+
.. [1] Marko Petkovsek, Herbert S. Wilf, Doron Zeilberger, A = B,
6+
AK Peters, Ltd., Wellesley, MA, USA, 1997, pp. 73--100
7+
8+
TESTS::
9+
10+
sage: _ = var('a b k m n r')
11+
sage: SR(1).gosper_sum(k)
12+
k
13+
sage: SR(1).gosper_sum(k,0,n)
14+
n + 1
15+
sage: SR(1).gosper_sum(k, a, b)
16+
-a + b + 1
17+
sage: a.gosper_sum(k)
18+
a*k
19+
sage: a.gosper_sum(k,0,n)
20+
a*(n + 1)
21+
sage: a.gosper_sum(k,a,b)
22+
-(a - b - 1)*a
23+
sage: k.gosper_sum(k)
24+
1/2*(k - 1)*k
25+
sage: k.gosper_sum(k,0,n)
26+
1/2*(n + 1)*n
27+
sage: k.gosper_sum(k, a, b)
28+
-1/2*(a + b)*(a - b - 1)
29+
sage: k.gosper_sum(k, a+1, b)
30+
-1/2*(a + b + 1)*(a - b)
31+
sage: k.gosper_sum(k, a, b+1)
32+
-1/2*(a + b + 1)*(a - b - 2)
33+
sage: (k^3).gosper_sum(k)
34+
1/4*(k - 1)^2*k^2
35+
sage: (k^3).gosper_sum(k, a, b)
36+
-1/4*(a^2 + b^2 - a + b)*(a + b)*(a - b - 1)
37+
38+
sage: (1/k).gosper_sum(k)
39+
Traceback (most recent call last):
40+
...
41+
ValueError: expression not Gosper-summable
42+
sage: x = (1/k/(k+1)/(k+2)/(k+3)/(k+5)/(k+7)).gosper_sum(k,a,b)
43+
sage: y = sum(1/k/(k+1)/(k+2)/(k+3)/(k+5)/(k+7) for k in range(5,500))
44+
sage: assert x.subs(a==5,b==499) == y
45+
46+
The following are from A==B, p.78 ff. Since the resulting expressions
47+
get more and more complicated with many correct representations that
48+
may differ depending on future capabilities, we check correctness by
49+
doing random summations::
50+
51+
sage: def check(ex, var, val1, val2from, val2to):
52+
....: import random
53+
....: symb = SR.var('symb')
54+
....: s1 = ex.gosper_sum(var, val1, symb)
55+
....: R = random.SystemRandom
56+
....: val2 = R().randint(val2from, val2to)
57+
....: s2 = sum(ex.subs(var==i) for i in range(val1, val2+1))
58+
....: assert s1.subs(symb==val2) == s2
59+
60+
sage: def check_unsolvable(ex, *args):
61+
....: try:
62+
....: SR(ex).gosper_sum(*args)
63+
....: raise AssertionError
64+
....: except ValueError:
65+
....: pass
66+
67+
sage: check((4*n+1) * factorial(n)/factorial(2*n+1), n, 0, 10, 20)
68+
sage: check_unsolvable(factorial(k), k,0,n)
69+
sage: (k * factorial(k)).gosper_sum(k,1,n)
70+
n*factorial(n) + factorial(n) - 1
71+
sage: (k * factorial(k)).gosper_sum(k)
72+
factorial(k)
73+
sage: check_unsolvable(binomial(n,k), k,0,n)
74+
sage: ((-1)^k*binomial(n,k)).gosper_sum(k,0,n)
75+
0
76+
sage: ((-1)^k*binomial(n,k)).gosper_sum(k,0,a)
77+
-(-1)^a*(a - n)*binomial(n, a)/n
78+
sage: (binomial(1/2,a-k+1)*binomial(1/2,a+k)).gosper_sum(k,1,b)
79+
(2*a + 2*b - 1)*(a - b + 1)*b*binomial(1/2, a + b)*binomial(1/2, a - b + 1)/((2*a + 1)*a)
80+
sage: t = (binomial(2*k,k)/4^k).gosper_sum(k,0,n); t
81+
(2*n + 1)*binomial(2*n, n)/4^n
82+
sage: t = t.gosper_sum(n,0,n); t
83+
1/3*(2*n + 3)*(2*n + 1)*binomial(2*n, n)/4^n
84+
sage: t = t.gosper_sum(n,0,n); t
85+
1/15*(2*n + 5)*(2*n + 3)*(2*n + 1)*binomial(2*n, n)/4^n
86+
sage: t = t.gosper_sum(n,0,n); t
87+
1/105*(2*n + 7)*(2*n + 5)*(2*n + 3)*(2*n + 1)*binomial(2*n, n)/4^n
88+
sage: (binomial(2*k+2*a,2*a)*binomial(2*k,k)/binomial(k+a,a)/4^k).gosper_sum(k,0,n)
89+
(2*a + 2*n + 1)*binomial(2*a + 2*n, 2*a)*binomial(2*n, n)/(4^n*(2*a + 1)*binomial(a + n, a))
90+
sage: (4^k/binomial(2*k,k)).gosper_sum(k,0,n)
91+
1/3*(2*4^n*n + 2*4^n + binomial(2*n, n))/binomial(2*n, n)
92+
93+
# The following are from A==B, 5.7 Exercises
94+
sage: for k in range(1,5): (n^k).gosper_sum(n,0,m)
95+
1/2*(m + 1)*m
96+
1/6*(2*m + 1)*(m + 1)*m
97+
1/4*(m + 1)^2*m^2
98+
1/30*(3*m^2 + 3*m - 1)*(2*m + 1)*(m + 1)*m
99+
sage: for k in range(1,4): (n^k*2^n).gosper_sum(n,0,m)
100+
2*2^m*m - 2*2^m + 2
101+
2*2^m*m^2 - 4*2^m*m + 6*2^m - 6
102+
2*2^m*m^3 - 6*2^m*m^2 + 18*2^m*m - 26*2^m + 26
103+
sage: (1 / (n^2 + sqrt(5)*n - 1)).gosper_sum(n,0,m) # known bug
104+
....: # TODO: algebraic solutions
105+
sage: check((n^4 * 4^n / binomial(2*n, n)), n, 0, 10, 20)
106+
sage: check((factorial(3*n) / (factorial(n) * factorial(n+1) * factorial(n+2) * 27^n)), n,0,10,20)
107+
sage: (binomial(2*n, n)^2 / (n+1) / 4^(2*n)).gosper_sum(n,0,m)
108+
(2*m + 1)^2*binomial(2*m, m)^2/(4^(2*m)*(m + 1))
109+
sage: (((4*n-1) * binomial(2*n, n)^2) / (2*n-1)^2 / 4^(2*n)).gosper_sum(n,0,m)
110+
-binomial(2*m, m)^2/4^(2*m)
111+
sage: check(n * factorial(n-1/2)^2 / factorial(n+1)^2, n,0,10,20)
112+
113+
sage: (n^2 * a^n).gosper_sum(n,0,m)
114+
(a^2*a^m*m^2 - 2*a*a^m*m^2 - 2*a*a^m*m + a^m*m^2 + a*a^m + 2*a^m*m + a^m - a - 1)*a/(a - 1)^3
115+
sage: ((n - r/2)*binomial(r, n)).gosper_sum(n,0,m)
116+
1/2*(m - r)*binomial(r, m)
117+
sage: x = var('x')
118+
sage: (factorial(n-1)^2 / factorial(n-x) / factorial(n+x)).gosper_sum(n,1,m)
119+
(m^2*factorial(m - 1)^2*factorial(x + 1)*factorial(-x + 1) + x^2*factorial(m + x)*factorial(m - x) - factorial(m + x)*factorial(m - x))/(x^2*factorial(m + x)*factorial(m - x)*factorial(x + 1)*factorial(-x + 1))
120+
sage: ((n*(n+a+b)*a^n*b^n)/factorial(n+a)/factorial(n+b)).gosper_sum(n,1,m).simplify_full()
121+
-(a^(m + 1)*b^(m + 1)*factorial(a - 1)*factorial(b - 1) - factorial(a + m)*factorial(b + m))/(factorial(a + m)*factorial(a - 1)*factorial(b + m)*factorial(b - 1))
122+
123+
sage: check_unsolvable(1/n, n,1,m)
124+
sage: check_unsolvable(1/n^2, n,1,m)
125+
sage: check_unsolvable(1/n^3, n,1,m)
126+
sage: ((6*n + 3) / (4*n^4 + 8*n^3 + 8*n^2 + 4*n + 3)).gosper_sum(n,1,m)
127+
(m + 2)*m/(2*m^2 + 4*m + 3)
128+
sage: (2^n * (n^2 - 2*n - 1)/(n^2 * (n+1)^2)).gosper_sum(n,1,m)
129+
-2*(m^2 - 2^m + 2*m + 1)/(m + 1)^2
130+
sage: ((4^n * n^2)/((n+2) * (n+1))).gosper_sum(n,1,m)
131+
2/3*(2*4^m*m - 2*4^m + m + 2)/(m + 2)
132+
sage: check_unsolvable(2^n / (n+1), n,0,m-1)
133+
sage: check((4*(1-n) * (n^2-2*n-1) / n^2 / (n+1)^2 / (n-2)^2 / (n-3)^2), n, 4, 10, 20)
134+
sage: check(((n^4-14*n^2-24*n-9) * 2^n / n^2 / (n+1)^2 / (n+2)^2 / (n+3)^2), n, 1, 10, 20)
135+
136+
Exercises 3 (h), (i), (j) require symbolic product support so we leave
137+
them out for now.
138+
139+
::
140+
141+
sage: _ = var('a b k m n r')
142+
sage: check_unsolvable(binomial(2*n, n) * a^n, n)
143+
sage: (binomial(2*n,n)*(1/4)^n).gosper_sum(n)
144+
2*(1/4)^n*n*binomial(2*n, n)
145+
sage: ((k-1) / factorial(k)).gosper_sum(k)
146+
-k/factorial(k)
147+
sage: F(n, k) = binomial(n, k) / 2^n
148+
sage: check_unsolvable(F(n, k), k)
149+
sage: _ = (F(n+1,k)-F(n,k)).gosper_term(k)
150+
sage: F(n,k).WZ_certificate(n,k)
151+
1/2*k/(k - n - 1)
152+
sage: F(n, k) = binomial(n, k)^2 / binomial(2*n, n)
153+
sage: check_unsolvable(F(n, k), k)
154+
sage: _ =(F(n+1, k) - F(n, k)).gosper_term(k)
155+
sage: F(n,k).WZ_certificate(n,k)
156+
1/2*(2*k - 3*n - 3)*k^2/((k - n - 1)^2*(2*n + 1))
157+
sage: F(n, k) = binomial(n,k) * factorial(n) / factorial(k) / factorial(a-k) / factorial(a+n)
158+
sage: check_unsolvable(F(n, k), k)
159+
sage: _ = (F(n+1, k) - F(n, k)).gosper_term(k)
160+
sage: F(n,k).WZ_certificate(n,k)
161+
k^2/((a + n + 1)*(k - n - 1))
162+
163+
sage: (1/n/(n+1)/(n+2)/(n+5)).gosper_sum(n)
164+
1/720*(55*n^5 + 550*n^4 + 1925*n^3 + 2510*n^2 - 1728)/((n + 4)*(n + 3)*(n + 2)*(n + 1)*n)
165+
sage: (1/n/(n+1)/(n+2)/(n+7)).gosper_sum(n)
166+
1/1050*(91*n^7 + 1911*n^6 + 15925*n^5 + 66535*n^4 + 142534*n^3 + 132104*n^2 - 54000)/((n + 6)*(n + 5)*(n + 4)*(n + 3)*(n + 2)*(n + 1)*n)
167+
sage: (1/n/(n+1)/(n+2)/(n+5)/(n+7)).gosper_sum(n)
168+
1/10080*(133*n^7 + 2793*n^6 + 23275*n^5 + 97755*n^4 + 213472*n^3 + 206892*n^2 - 103680)/((n + 6)*(n + 5)*(n + 4)*(n + 3)*(n + 2)*(n + 1)*n)
169+
170+
The following are from A=B, 7.2 WZ Proofs of the hypergeometric database::
171+
172+
sage: _ = var('a b c i j k m n r')
173+
sage: F(n,k) = factorial(n+k)*factorial(b+k)*factorial(c-n-1)*factorial(c-b-1)/factorial(c+k)/factorial(n-1)/factorial(c-n-b-1)/factorial(k+1)/factorial(b-1)
174+
sage: F(n,k).WZ_certificate(n,k)
175+
-(c + k)*(k + 1)/((c - n - 1)*n)
176+
sage: F(n,k)=(-1)^(n+k)*factorial(2*n+c-1)*factorial(n)*factorial(n+c-1)/factorial(2*n+c-1-k)/factorial(2*n-k)/factorial(c+k-1)/factorial(k)
177+
sage: F(n,k).WZ_certificate(n,k)
178+
1/2*(c^2 - 2*c*k + k^2 + 7*c*n - 6*k*n + 10*n^2 + 4*c - 3*k + 10*n + 2)*(c + k - 1)*k/((c - k + 2*n + 1)*(c - k + 2*n)*(k - 2*n - 1)*(k - 2*n - 2))
179+
sage: F(n,k)=factorial(a+k-1)*factorial(b+k-1)*factorial(n)*factorial(n+c-b-a-k-1)*factorial(n+c-1)/factorial(k)/factorial(n-k)/factorial(k+c-1)/factorial(n+c-a-1)/factorial(n+c-b-1)
180+
sage: F(n,k).WZ_certificate(n,k)
181+
-(a + b - c + k - n)*(c + k - 1)*k/((a - c - n)*(b - c - n)*(k - n - 1))
182+
sage: F(n,k)=(-1)^k*binomial(n+b,n+k)*binomial(n+c,c+k)*binomial(b+c,b+k)/factorial(n+b+c)*factorial(n)*factorial(b)*factorial(c)
183+
sage: F(n,k).WZ_certificate(n,k)
184+
1/2*(b + k)*(c + k)/((b + c + n + 1)*(k - n - 1))
185+
sage: phi(t) = factorial(a+t-1)*factorial(b+t-1)/factorial(t)/factorial(-1/2+a+b+t)
186+
sage: psi(t) = factorial(t+a+b-1/2)*factorial(t)*factorial(t+2*a+2*b-1)/factorial(t+2*a-1)/factorial(t+a+b-1)/factorial(t+2*b-1)
187+
sage: F(n,k) = phi(k)*phi(n-k)*psi(n)
188+
sage: F(n,k).WZ_certificate(n,k)
189+
(2*a + 2*b + 2*k - 1)*(2*a + 2*b - 2*k + 3*n + 2)*(a - k + n)*(b - k + n)*k/((2*a + 2*b - 2*k + 2*n + 1)*(2*a + n)*(a + b + n)*(2*b + n)*(k - n - 1))
190+
191+
The following are also from A=B, 7 The WZ Phenomenon::
192+
193+
sage: F(n,k) = factorial(n-i)*factorial(n-j)*factorial(i-1)*factorial(j-1)/factorial(n-1)/factorial(k-1)/factorial(n-i-j+k)/factorial(i-k)/factorial(j-k)
194+
sage: F(n,k).WZ_certificate(n,k)
195+
(k - 1)/n
196+
sage: F(n,k) = binomial(3*n,k)/8^n
197+
sage: F(n,k).WZ_certificate(n,k)
198+
1/8*(4*k^2 - 30*k*n + 63*n^2 - 22*k + 93*n + 32)*k/((k - 3*n - 1)*(k - 3*n - 2)*(k - 3*n - 3))
199+
200+
Example 7.5.1 gets a different but correct certificate (the certificate
201+
in the book fails the proof)::
202+
203+
sage: F(n,k) = 2^(k+1)*(k+1)*factorial(2*n-k-2)*factorial(n)/factorial(n-k-1)/factorial(2*n)
204+
sage: c = F(n,k).WZ_certificate(n,k); c
205+
-1/2*(k - 2*n + 1)*k/((k - n)*(2*n + 1))
206+
sage: G(n,k) = c*F(n,k)
207+
sage: t = (F(n+1,k) - F(n,k) - G(n,k+1) + G(n,k))
208+
sage: t.simplify_full().is_trivial_zero()
209+
True
210+
sage: c = k/2/(-1+k-n)
211+
sage: GG(n,k) = c*F(n,k)
212+
sage: t = (F(n+1,k) - F(n,k) - GG(n,k+1) + GG(n,k))
213+
sage: t.simplify_full().is_trivial_zero()
214+
False
215+
"""
216+

0 commit comments

Comments
 (0)