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

1from .functions import defun, defun_wrapped 

2 

3@defun 

4def qp(ctx, a, q=None, n=None, **kwargs): 

5 r""" 

6 Evaluates the q-Pochhammer symbol (or q-rising factorial) 

7 

8 .. math :: 

9 

10 (a; q)_n = \prod_{k=0}^{n-1} (1-a q^k) 

11 

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 

15 

16 .. math :: 

17 

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} 

20 

21 is also known as the Euler function, or (up to a factor `q^{-1/24}`) 

22 the Dedekind eta function. 

23 

24 **Examples** 

25 

26 If `n` is a positive integer, the function amounts to a finite product:: 

27 

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 

36 

37 Complex arguments are allowed:: 

38 

39 >>> qp(2-1j, 0.75j) 

40 (0.4628842231660149089976379 + 4.481821753552703090628793j) 

41 

42 The regular Pochhammer symbol `(a)_n` is obtained in the 

43 following limit as `q \to 1`:: 

44 

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 

50 

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:: 

54 

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] 

57 

58 Special values include:: 

59 

60 >>> qp(0) 

61 1.0 

62 >>> findroot(diffun(qp), -0.4) # location of maximum 

63 -0.4112484791779547734440257 

64 >>> qp(_) 

65 1.228348867038575112586878 

66 

67 The q-Pochhammer symbol is related to the Jacobi theta functions. 

68 For example, the following identity holds:: 

69 

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 

75 

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) 

130 

131@defun_wrapped 

132def qgamma(ctx, z, q, **kwargs): 

133 r""" 

134 Evaluates the q-gamma function 

135 

136 .. math :: 

137 

138 \Gamma_q(z) = \frac{(q; q)_{\infty}}{(q^z; q)_{\infty}} (1-q)^{1-z}. 

139 

140 

141 **Examples** 

142 

143 Evaluation for real and complex arguments:: 

144 

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) 

153 

154 The q-gamma function satisfies a functional equation similar 

155 to that of the ordinary gamma function:: 

156 

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 

163 

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) 

169 

170@defun_wrapped 

171def qfac(ctx, z, q, **kwargs): 

172 r""" 

173 Evaluates the q-factorial, 

174 

175 .. math :: 

176 

177 [n]_q! = (1+q)(1+q+q^2)\cdots(1+q+\cdots+q^{n-1}) 

178 

179 or more generally 

180 

181 .. math :: 

182 

183 [z]_q! = \frac{(q;q)_z}{(1-q)^z}. 

184 

185 **Examples** 

186 

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) 

197 

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) 

203 

204@defun 

205def qhyper(ctx, a_s, b_s, q, z, **kwargs): 

206 r""" 

207 Evaluates the basic hypergeometric series or hypergeometric q-series 

208 

209 .. math :: 

210 

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} 

220 

221 where `(a;q)_n` denotes the q-Pochhammer symbol (see :func:`~mpmath.qp`). 

222 

223 **Examples** 

224 

225 Evaluation works for real and complex arguments:: 

226 

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) 

235 

236 Comparing with a summation of the defining series, using 

237 :func:`~mpmath.nsum`:: 

238 

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 

244 

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)