Coverage for /usr/lib/python3/dist-packages/mpmath/functions/qfunctions.py: 9%
97 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1from .functions import defun, defun_wrapped
3@defun
4def qp(ctx, a, q=None, n=None, **kwargs):
5 r"""
6 Evaluates the q-Pochhammer symbol (or q-rising factorial)
8 .. math ::
10 (a; q)_n = \prod_{k=0}^{n-1} (1-a q^k)
12 where `n = \infty` is permitted if `|q| < 1`. Called with two arguments,
13 ``qp(a,q)`` computes `(a;q)_{\infty}`; with a single argument, ``qp(q)``
14 computes `(q;q)_{\infty}`. The special case
16 .. math ::
18 \phi(q) = (q; q)_{\infty} = \prod_{k=1}^{\infty} (1-q^k) =
19 \sum_{k=-\infty}^{\infty} (-1)^k q^{(3k^2-k)/2}
21 is also known as the Euler function, or (up to a factor `q^{-1/24}`)
22 the Dedekind eta function.
24 **Examples**
26 If `n` is a positive integer, the function amounts to a finite product::
28 >>> from mpmath import *
29 >>> mp.dps = 25; mp.pretty = True
30 >>> qp(2,3,5)
31 -725305.0
32 >>> fprod(1-2*3**k for k in range(5))
33 -725305.0
34 >>> qp(2,3,0)
35 1.0
37 Complex arguments are allowed::
39 >>> qp(2-1j, 0.75j)
40 (0.4628842231660149089976379 + 4.481821753552703090628793j)
42 The regular Pochhammer symbol `(a)_n` is obtained in the
43 following limit as `q \to 1`::
45 >>> a, n = 4, 7
46 >>> limit(lambda q: qp(q**a,q,n) / (1-q)**n, 1)
47 604800.0
48 >>> rf(a,n)
49 604800.0
51 The Taylor series of the reciprocal Euler function gives
52 the partition function `P(n)`, i.e. the number of ways of writing
53 `n` as a sum of positive integers::
55 >>> taylor(lambda q: 1/qp(q), 0, 10)
56 [1.0, 1.0, 2.0, 3.0, 5.0, 7.0, 11.0, 15.0, 22.0, 30.0, 42.0]
58 Special values include::
60 >>> qp(0)
61 1.0
62 >>> findroot(diffun(qp), -0.4) # location of maximum
63 -0.4112484791779547734440257
64 >>> qp(_)
65 1.228348867038575112586878
67 The q-Pochhammer symbol is related to the Jacobi theta functions.
68 For example, the following identity holds::
70 >>> q = mpf(0.5) # arbitrary
71 >>> qp(q)
72 0.2887880950866024212788997
73 >>> root(3,-2)*root(q,-24)*jtheta(2,pi/6,root(q,6))
74 0.2887880950866024212788997
76 """
77 a = ctx.convert(a)
78 if n is None:
79 n = ctx.inf
80 else:
81 n = ctx.convert(n)
82 if n < 0:
83 raise ValueError("n cannot be negative")
84 if q is None:
85 q = a
86 else:
87 q = ctx.convert(q)
88 if n == 0:
89 return ctx.one + 0*(a+q)
90 infinite = (n == ctx.inf)
91 same = (a == q)
92 if infinite:
93 if abs(q) >= 1:
94 if same and (q == -1 or q == 1):
95 return ctx.zero * q
96 raise ValueError("q-function only defined for |q| < 1")
97 elif q == 0:
98 return ctx.one - a
99 maxterms = kwargs.get('maxterms', 50*ctx.prec)
100 if infinite and same:
101 # Euler's pentagonal theorem
102 def terms():
103 t = 1
104 yield t
105 k = 1
106 x1 = q
107 x2 = q**2
108 while 1:
109 yield (-1)**k * x1
110 yield (-1)**k * x2
111 x1 *= q**(3*k+1)
112 x2 *= q**(3*k+2)
113 k += 1
114 if k > maxterms:
115 raise ctx.NoConvergence
116 return ctx.sum_accurately(terms)
117 # return ctx.nprod(lambda k: 1-a*q**k, [0,n-1])
118 def factors():
119 k = 0
120 r = ctx.one
121 while 1:
122 yield 1 - a*r
123 r *= q
124 k += 1
125 if k >= n:
126 return
127 if k > maxterms:
128 raise ctx.NoConvergence
129 return ctx.mul_accurately(factors)
131@defun_wrapped
132def qgamma(ctx, z, q, **kwargs):
133 r"""
134 Evaluates the q-gamma function
136 .. math ::
138 \Gamma_q(z) = \frac{(q; q)_{\infty}}{(q^z; q)_{\infty}} (1-q)^{1-z}.
141 **Examples**
143 Evaluation for real and complex arguments::
145 >>> from mpmath import *
146 >>> mp.dps = 25; mp.pretty = True
147 >>> qgamma(4,0.75)
148 4.046875
149 >>> qgamma(6,6)
150 121226245.0
151 >>> qgamma(3+4j, 0.5j)
152 (0.1663082382255199834630088 + 0.01952474576025952984418217j)
154 The q-gamma function satisfies a functional equation similar
155 to that of the ordinary gamma function::
157 >>> q = mpf(0.25)
158 >>> z = mpf(2.5)
159 >>> qgamma(z+1,q)
160 1.428277424823760954685912
161 >>> (1-q**z)/(1-q)*qgamma(z,q)
162 1.428277424823760954685912
164 """
165 if abs(q) > 1:
166 return ctx.qgamma(z,1/q)*q**((z-2)*(z-1)*0.5)
167 return ctx.qp(q, q, None, **kwargs) / \
168 ctx.qp(q**z, q, None, **kwargs) * (1-q)**(1-z)
170@defun_wrapped
171def qfac(ctx, z, q, **kwargs):
172 r"""
173 Evaluates the q-factorial,
175 .. math ::
177 [n]_q! = (1+q)(1+q+q^2)\cdots(1+q+\cdots+q^{n-1})
179 or more generally
181 .. math ::
183 [z]_q! = \frac{(q;q)_z}{(1-q)^z}.
185 **Examples**
187 >>> from mpmath import *
188 >>> mp.dps = 25; mp.pretty = True
189 >>> qfac(0,0)
190 1.0
191 >>> qfac(4,3)
192 2080.0
193 >>> qfac(5,6)
194 121226245.0
195 >>> qfac(1+1j, 2+1j)
196 (0.4370556551322672478613695 + 0.2609739839216039203708921j)
198 """
199 if ctx.isint(z) and ctx._re(z) > 0:
200 n = int(ctx._re(z))
201 return ctx.qp(q, q, n, **kwargs) / (1-q)**n
202 return ctx.qgamma(z+1, q, **kwargs)
204@defun
205def qhyper(ctx, a_s, b_s, q, z, **kwargs):
206 r"""
207 Evaluates the basic hypergeometric series or hypergeometric q-series
209 .. math ::
211 \,_r\phi_s \left[\begin{matrix}
212 a_1 & a_2 & \ldots & a_r \\
213 b_1 & b_2 & \ldots & b_s
214 \end{matrix} ; q,z \right] =
215 \sum_{n=0}^\infty
216 \frac{(a_1;q)_n, \ldots, (a_r;q)_n}
217 {(b_1;q)_n, \ldots, (b_s;q)_n}
218 \left((-1)^n q^{n\choose 2}\right)^{1+s-r}
219 \frac{z^n}{(q;q)_n}
221 where `(a;q)_n` denotes the q-Pochhammer symbol (see :func:`~mpmath.qp`).
223 **Examples**
225 Evaluation works for real and complex arguments::
227 >>> from mpmath import *
228 >>> mp.dps = 25; mp.pretty = True
229 >>> qhyper([0.5], [2.25], 0.25, 4)
230 -0.1975849091263356009534385
231 >>> qhyper([0.5], [2.25], 0.25-0.25j, 4)
232 (2.806330244925716649839237 + 3.568997623337943121769938j)
233 >>> qhyper([1+j], [2,3+0.5j], 0.25, 3+4j)
234 (9.112885171773400017270226 - 1.272756997166375050700388j)
236 Comparing with a summation of the defining series, using
237 :func:`~mpmath.nsum`::
239 >>> b, q, z = 3, 0.25, 0.5
240 >>> qhyper([], [b], q, z)
241 0.6221136748254495583228324
242 >>> nsum(lambda n: z**n / qp(q,q,n)/qp(b,q,n) * q**(n*(n-1)), [0,inf])
243 0.6221136748254495583228324
245 """
246 #a_s = [ctx._convert_param(a)[0] for a in a_s]
247 #b_s = [ctx._convert_param(b)[0] for b in b_s]
248 #q = ctx._convert_param(q)[0]
249 a_s = [ctx.convert(a) for a in a_s]
250 b_s = [ctx.convert(b) for b in b_s]
251 q = ctx.convert(q)
252 z = ctx.convert(z)
253 r = len(a_s)
254 s = len(b_s)
255 d = 1+s-r
256 maxterms = kwargs.get('maxterms', 50*ctx.prec)
257 def terms():
258 t = ctx.one
259 yield t
260 qk = 1
261 k = 0
262 x = 1
263 while 1:
264 for a in a_s:
265 p = 1 - a*qk
266 t *= p
267 for b in b_s:
268 p = 1 - b*qk
269 if not p:
270 raise ValueError
271 t /= p
272 t *= z
273 x *= (-1)**d * qk ** d
274 qk *= q
275 t /= (1 - qk)
276 k += 1
277 yield t * x
278 if k > maxterms:
279 raise ctx.NoConvergence
280 return ctx.sum_accurately(terms)