Coverage for /usr/lib/python3/dist-packages/mpmath/functions/orthogonal.py: 13%

241 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1from .functions import defun, defun_wrapped 

2 

3def _hermite_param(ctx, n, z, parabolic_cylinder): 

4 """ 

5 Combined calculation of the Hermite polynomial H_n(z) (and its 

6 generalization to complex n) and the parabolic cylinder 

7 function D. 

8 """ 

9 n, ntyp = ctx._convert_param(n) 

10 z = ctx.convert(z) 

11 q = -ctx.mpq_1_2 

12 # For re(z) > 0, 2F0 -- http://functions.wolfram.com/ 

13 # HypergeometricFunctions/HermiteHGeneral/06/02/0009/ 

14 # Otherwise, there is a reflection formula 

15 # 2F0 + http://functions.wolfram.com/HypergeometricFunctions/ 

16 # HermiteHGeneral/16/01/01/0006/ 

17 # 

18 # TODO: 

19 # An alternative would be to use 

20 # http://functions.wolfram.com/HypergeometricFunctions/ 

21 # HermiteHGeneral/06/02/0006/ 

22 # 

23 # Also, the 1F1 expansion 

24 # http://functions.wolfram.com/HypergeometricFunctions/ 

25 # HermiteHGeneral/26/01/02/0001/ 

26 # should probably be used for tiny z 

27 if not z: 

28 T1 = [2, ctx.pi], [n, 0.5], [], [q*(n-1)], [], [], 0 

29 if parabolic_cylinder: 

30 T1[1][0] += q*n 

31 return T1, 

32 can_use_2f0 = ctx.isnpint(-n) or ctx.re(z) > 0 or \ 

33 (ctx.re(z) == 0 and ctx.im(z) > 0) 

34 expprec = ctx.prec*4 + 20 

35 if parabolic_cylinder: 

36 u = ctx.fmul(ctx.fmul(z,z,prec=expprec), -0.25, exact=True) 

37 w = ctx.fmul(z, ctx.sqrt(0.5,prec=expprec), prec=expprec) 

38 else: 

39 w = z 

40 w2 = ctx.fmul(w, w, prec=expprec) 

41 rw2 = ctx.fdiv(1, w2, prec=expprec) 

42 nrw2 = ctx.fneg(rw2, exact=True) 

43 nw = ctx.fneg(w, exact=True) 

44 if can_use_2f0: 

45 T1 = [2, w], [n, n], [], [], [q*n, q*(n-1)], [], nrw2 

46 terms = [T1] 

47 else: 

48 T1 = [2, nw], [n, n], [], [], [q*n, q*(n-1)], [], nrw2 

49 T2 = [2, ctx.pi, nw], [n+2, 0.5, 1], [], [q*n], [q*(n-1)], [1-q], w2 

50 terms = [T1,T2] 

51 # Multiply by prefactor for D_n 

52 if parabolic_cylinder: 

53 expu = ctx.exp(u) 

54 for i in range(len(terms)): 

55 terms[i][1][0] += q*n 

56 terms[i][0].append(expu) 

57 terms[i][1].append(1) 

58 return tuple(terms) 

59 

60@defun 

61def hermite(ctx, n, z, **kwargs): 

62 return ctx.hypercomb(lambda: _hermite_param(ctx, n, z, 0), [], **kwargs) 

63 

64@defun 

65def pcfd(ctx, n, z, **kwargs): 

66 r""" 

67 Gives the parabolic cylinder function in Whittaker's notation 

68 `D_n(z) = U(-n-1/2, z)` (see :func:`~mpmath.pcfu`). 

69 It solves the differential equation 

70 

71 .. math :: 

72 

73 y'' + \left(n + \frac{1}{2} - \frac{1}{4} z^2\right) y = 0. 

74 

75 and can be represented in terms of Hermite polynomials 

76 (see :func:`~mpmath.hermite`) as 

77 

78 .. math :: 

79 

80 D_n(z) = 2^{-n/2} e^{-z^2/4} H_n\left(\frac{z}{\sqrt{2}}\right). 

81 

82 **Plots** 

83 

84 .. literalinclude :: /plots/pcfd.py 

85 .. image :: /plots/pcfd.png 

86 

87 **Examples** 

88 

89 >>> from mpmath import * 

90 >>> mp.dps = 25; mp.pretty = True 

91 >>> pcfd(0,0); pcfd(1,0); pcfd(2,0); pcfd(3,0) 

92 1.0 

93 0.0 

94 -1.0 

95 0.0 

96 >>> pcfd(4,0); pcfd(-3,0) 

97 3.0 

98 0.6266570686577501256039413 

99 >>> pcfd('1/2', 2+3j) 

100 (-5.363331161232920734849056 - 3.858877821790010714163487j) 

101 >>> pcfd(2, -10) 

102 1.374906442631438038871515e-9 

103 

104 Verifying the differential equation:: 

105 

106 >>> n = mpf(2.5) 

107 >>> y = lambda z: pcfd(n,z) 

108 >>> z = 1.75 

109 >>> chop(diff(y,z,2) + (n+0.5-0.25*z**2)*y(z)) 

110 0.0 

111 

112 Rational Taylor series expansion when `n` is an integer:: 

113 

114 >>> taylor(lambda z: pcfd(5,z), 0, 7) 

115 [0.0, 15.0, 0.0, -13.75, 0.0, 3.96875, 0.0, -0.6015625] 

116 

117 """ 

118 return ctx.hypercomb(lambda: _hermite_param(ctx, n, z, 1), [], **kwargs) 

119 

120@defun 

121def pcfu(ctx, a, z, **kwargs): 

122 r""" 

123 Gives the parabolic cylinder function `U(a,z)`, which may be 

124 defined for `\Re(z) > 0` in terms of the confluent 

125 U-function (see :func:`~mpmath.hyperu`) by 

126 

127 .. math :: 

128 

129 U(a,z) = 2^{-\frac{1}{4}-\frac{a}{2}} e^{-\frac{1}{4} z^2} 

130 U\left(\frac{a}{2}+\frac{1}{4}, 

131 \frac{1}{2}, \frac{1}{2}z^2\right) 

132 

133 or, for arbitrary `z`, 

134 

135 .. math :: 

136 

137 e^{-\frac{1}{4}z^2} U(a,z) = 

138 U(a,0) \,_1F_1\left(-\tfrac{a}{2}+\tfrac{1}{4}; 

139 \tfrac{1}{2}; -\tfrac{1}{2}z^2\right) + 

140 U'(a,0) z \,_1F_1\left(-\tfrac{a}{2}+\tfrac{3}{4}; 

141 \tfrac{3}{2}; -\tfrac{1}{2}z^2\right). 

142 

143 **Examples** 

144 

145 Connection to other functions:: 

146 

147 >>> from mpmath import * 

148 >>> mp.dps = 25; mp.pretty = True 

149 >>> z = mpf(3) 

150 >>> pcfu(0.5,z) 

151 0.03210358129311151450551963 

152 >>> sqrt(pi/2)*exp(z**2/4)*erfc(z/sqrt(2)) 

153 0.03210358129311151450551963 

154 >>> pcfu(0.5,-z) 

155 23.75012332835297233711255 

156 >>> sqrt(pi/2)*exp(z**2/4)*erfc(-z/sqrt(2)) 

157 23.75012332835297233711255 

158 >>> pcfu(0.5,-z) 

159 23.75012332835297233711255 

160 >>> sqrt(pi/2)*exp(z**2/4)*erfc(-z/sqrt(2)) 

161 23.75012332835297233711255 

162 

163 """ 

164 n, _ = ctx._convert_param(a) 

165 return ctx.pcfd(-n-ctx.mpq_1_2, z) 

166 

167@defun 

168def pcfv(ctx, a, z, **kwargs): 

169 r""" 

170 Gives the parabolic cylinder function `V(a,z)`, which can be 

171 represented in terms of :func:`~mpmath.pcfu` as 

172 

173 .. math :: 

174 

175 V(a,z) = \frac{\Gamma(a+\tfrac{1}{2}) (U(a,-z)-\sin(\pi a) U(a,z)}{\pi}. 

176 

177 **Examples** 

178 

179 Wronskian relation between `U` and `V`:: 

180 

181 >>> from mpmath import * 

182 >>> mp.dps = 25; mp.pretty = True 

183 >>> a, z = 2, 3 

184 >>> pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z) 

185 0.7978845608028653558798921 

186 >>> sqrt(2/pi) 

187 0.7978845608028653558798921 

188 >>> a, z = 2.5, 3 

189 >>> pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z) 

190 0.7978845608028653558798921 

191 >>> a, z = 0.25, -1 

192 >>> pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z) 

193 0.7978845608028653558798921 

194 >>> a, z = 2+1j, 2+3j 

195 >>> chop(pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z)) 

196 0.7978845608028653558798921 

197 

198 """ 

199 n, ntype = ctx._convert_param(a) 

200 z = ctx.convert(z) 

201 q = ctx.mpq_1_2 

202 r = ctx.mpq_1_4 

203 if ntype == 'Q' and ctx.isint(n*2): 

204 # Faster for half-integers 

205 def h(): 

206 jz = ctx.fmul(z, -1j, exact=True) 

207 T1terms = _hermite_param(ctx, -n-q, z, 1) 

208 T2terms = _hermite_param(ctx, n-q, jz, 1) 

209 for T in T1terms: 

210 T[0].append(1j) 

211 T[1].append(1) 

212 T[3].append(q-n) 

213 u = ctx.expjpi((q*n-r)) * ctx.sqrt(2/ctx.pi) 

214 for T in T2terms: 

215 T[0].append(u) 

216 T[1].append(1) 

217 return T1terms + T2terms 

218 v = ctx.hypercomb(h, [], **kwargs) 

219 if ctx._is_real_type(n) and ctx._is_real_type(z): 

220 v = ctx._re(v) 

221 return v 

222 else: 

223 def h(n): 

224 w = ctx.square_exp_arg(z, -0.25) 

225 u = ctx.square_exp_arg(z, 0.5) 

226 e = ctx.exp(w) 

227 l = [ctx.pi, q, ctx.exp(w)] 

228 Y1 = l, [-q, n*q+r, 1], [r-q*n], [], [q*n+r], [q], u 

229 Y2 = l + [z], [-q, n*q-r, 1, 1], [1-r-q*n], [], [q*n+1-r], [1+q], u 

230 c, s = ctx.cospi_sinpi(r+q*n) 

231 Y1[0].append(s) 

232 Y2[0].append(c) 

233 for Y in (Y1, Y2): 

234 Y[1].append(1) 

235 Y[3].append(q-n) 

236 return Y1, Y2 

237 return ctx.hypercomb(h, [n], **kwargs) 

238 

239 

240@defun 

241def pcfw(ctx, a, z, **kwargs): 

242 r""" 

243 Gives the parabolic cylinder function `W(a,z)` defined in (DLMF 12.14). 

244 

245 **Examples** 

246 

247 Value at the origin:: 

248 

249 >>> from mpmath import * 

250 >>> mp.dps = 25; mp.pretty = True 

251 >>> a = mpf(0.25) 

252 >>> pcfw(a,0) 

253 0.9722833245718180765617104 

254 >>> power(2,-0.75)*sqrt(abs(gamma(0.25+0.5j*a)/gamma(0.75+0.5j*a))) 

255 0.9722833245718180765617104 

256 >>> diff(pcfw,(a,0),(0,1)) 

257 -0.5142533944210078966003624 

258 >>> -power(2,-0.25)*sqrt(abs(gamma(0.75+0.5j*a)/gamma(0.25+0.5j*a))) 

259 -0.5142533944210078966003624 

260 

261 """ 

262 n, _ = ctx._convert_param(a) 

263 z = ctx.convert(z) 

264 def terms(): 

265 phi2 = ctx.arg(ctx.gamma(0.5 + ctx.j*n)) 

266 phi2 = (ctx.loggamma(0.5+ctx.j*n) - ctx.loggamma(0.5-ctx.j*n))/2j 

267 rho = ctx.pi/8 + 0.5*phi2 

268 # XXX: cancellation computing k 

269 k = ctx.sqrt(1 + ctx.exp(2*ctx.pi*n)) - ctx.exp(ctx.pi*n) 

270 C = ctx.sqrt(k/2) * ctx.exp(0.25*ctx.pi*n) 

271 yield C * ctx.expj(rho) * ctx.pcfu(ctx.j*n, z*ctx.expjpi(-0.25)) 

272 yield C * ctx.expj(-rho) * ctx.pcfu(-ctx.j*n, z*ctx.expjpi(0.25)) 

273 v = ctx.sum_accurately(terms) 

274 if ctx._is_real_type(n) and ctx._is_real_type(z): 

275 v = ctx._re(v) 

276 return v 

277 

278""" 

279Even/odd PCFs. Useful? 

280 

281@defun 

282def pcfy1(ctx, a, z, **kwargs): 

283 a, _ = ctx._convert_param(n) 

284 z = ctx.convert(z) 

285 def h(): 

286 w = ctx.square_exp_arg(z) 

287 w1 = ctx.fmul(w, -0.25, exact=True) 

288 w2 = ctx.fmul(w, 0.5, exact=True) 

289 e = ctx.exp(w1) 

290 return [e], [1], [], [], [ctx.mpq_1_2*a+ctx.mpq_1_4], [ctx.mpq_1_2], w2 

291 return ctx.hypercomb(h, [], **kwargs) 

292 

293@defun 

294def pcfy2(ctx, a, z, **kwargs): 

295 a, _ = ctx._convert_param(n) 

296 z = ctx.convert(z) 

297 def h(): 

298 w = ctx.square_exp_arg(z) 

299 w1 = ctx.fmul(w, -0.25, exact=True) 

300 w2 = ctx.fmul(w, 0.5, exact=True) 

301 e = ctx.exp(w1) 

302 return [e, z], [1, 1], [], [], [ctx.mpq_1_2*a+ctx.mpq_3_4], \ 

303 [ctx.mpq_3_2], w2 

304 return ctx.hypercomb(h, [], **kwargs) 

305""" 

306 

307@defun_wrapped 

308def gegenbauer(ctx, n, a, z, **kwargs): 

309 # Special cases: a+0.5, a*2 poles 

310 if ctx.isnpint(a): 

311 return 0*(z+n) 

312 if ctx.isnpint(a+0.5): 

313 # TODO: something else is required here 

314 # E.g.: gegenbauer(-2, -0.5, 3) == -12 

315 if ctx.isnpint(n+1): 

316 raise NotImplementedError("Gegenbauer function with two limits") 

317 def h(a): 

318 a2 = 2*a 

319 T = [], [], [n+a2], [n+1, a2], [-n, n+a2], [a+0.5], 0.5*(1-z) 

320 return [T] 

321 return ctx.hypercomb(h, [a], **kwargs) 

322 def h(n): 

323 a2 = 2*a 

324 T = [], [], [n+a2], [n+1, a2], [-n, n+a2], [a+0.5], 0.5*(1-z) 

325 return [T] 

326 return ctx.hypercomb(h, [n], **kwargs) 

327 

328@defun_wrapped 

329def jacobi(ctx, n, a, b, x, **kwargs): 

330 if not ctx.isnpint(a): 

331 def h(n): 

332 return (([], [], [a+n+1], [n+1, a+1], [-n, a+b+n+1], [a+1], (1-x)*0.5),) 

333 return ctx.hypercomb(h, [n], **kwargs) 

334 if not ctx.isint(b): 

335 def h(n, a): 

336 return (([], [], [-b], [n+1, -b-n], [-n, a+b+n+1], [b+1], (x+1)*0.5),) 

337 return ctx.hypercomb(h, [n, a], **kwargs) 

338 # XXX: determine appropriate limit 

339 return ctx.binomial(n+a,n) * ctx.hyp2f1(-n,1+n+a+b,a+1,(1-x)/2, **kwargs) 

340 

341@defun_wrapped 

342def laguerre(ctx, n, a, z, **kwargs): 

343 # XXX: limits, poles 

344 #if ctx.isnpint(n): 

345 # return 0*(a+z) 

346 def h(a): 

347 return (([], [], [a+n+1], [a+1, n+1], [-n], [a+1], z),) 

348 return ctx.hypercomb(h, [a], **kwargs) 

349 

350@defun_wrapped 

351def legendre(ctx, n, x, **kwargs): 

352 if ctx.isint(n): 

353 n = int(n) 

354 # Accuracy near zeros 

355 if (n + (n < 0)) & 1: 

356 if not x: 

357 return x 

358 mag = ctx.mag(x) 

359 if mag < -2*ctx.prec-10: 

360 return x 

361 if mag < -5: 

362 ctx.prec += -mag 

363 return ctx.hyp2f1(-n,n+1,1,(1-x)/2, **kwargs) 

364 

365@defun 

366def legenp(ctx, n, m, z, type=2, **kwargs): 

367 # Legendre function, 1st kind 

368 n = ctx.convert(n) 

369 m = ctx.convert(m) 

370 # Faster 

371 if not m: 

372 return ctx.legendre(n, z, **kwargs) 

373 # TODO: correct evaluation at singularities 

374 if type == 2: 

375 def h(n,m): 

376 g = m*0.5 

377 T = [1+z, 1-z], [g, -g], [], [1-m], [-n, n+1], [1-m], 0.5*(1-z) 

378 return (T,) 

379 return ctx.hypercomb(h, [n,m], **kwargs) 

380 if type == 3: 

381 def h(n,m): 

382 g = m*0.5 

383 T = [z+1, z-1], [g, -g], [], [1-m], [-n, n+1], [1-m], 0.5*(1-z) 

384 return (T,) 

385 return ctx.hypercomb(h, [n,m], **kwargs) 

386 raise ValueError("requires type=2 or type=3") 

387 

388@defun 

389def legenq(ctx, n, m, z, type=2, **kwargs): 

390 # Legendre function, 2nd kind 

391 n = ctx.convert(n) 

392 m = ctx.convert(m) 

393 z = ctx.convert(z) 

394 if z in (1, -1): 

395 #if ctx.isint(m): 

396 # return ctx.nan 

397 #return ctx.inf # unsigned 

398 return ctx.nan 

399 if type == 2: 

400 def h(n, m): 

401 cos, sin = ctx.cospi_sinpi(m) 

402 s = 2 * sin / ctx.pi 

403 c = cos 

404 a = 1+z 

405 b = 1-z 

406 u = m/2 

407 w = (1-z)/2 

408 T1 = [s, c, a, b], [-1, 1, u, -u], [], [1-m], \ 

409 [-n, n+1], [1-m], w 

410 T2 = [-s, a, b], [-1, -u, u], [n+m+1], [n-m+1, m+1], \ 

411 [-n, n+1], [m+1], w 

412 return T1, T2 

413 return ctx.hypercomb(h, [n, m], **kwargs) 

414 if type == 3: 

415 # The following is faster when there only is a single series 

416 # Note: not valid for -1 < z < 0 (?) 

417 if abs(z) > 1: 

418 def h(n, m): 

419 T1 = [ctx.expjpi(m), 2, ctx.pi, z, z-1, z+1], \ 

420 [1, -n-1, 0.5, -n-m-1, 0.5*m, 0.5*m], \ 

421 [n+m+1], [n+1.5], \ 

422 [0.5*(2+n+m), 0.5*(1+n+m)], [n+1.5], z**(-2) 

423 return [T1] 

424 return ctx.hypercomb(h, [n, m], **kwargs) 

425 else: 

426 # not valid for 1 < z < inf ? 

427 def h(n, m): 

428 s = 2 * ctx.sinpi(m) / ctx.pi 

429 c = ctx.expjpi(m) 

430 a = 1+z 

431 b = z-1 

432 u = m/2 

433 w = (1-z)/2 

434 T1 = [s, c, a, b], [-1, 1, u, -u], [], [1-m], \ 

435 [-n, n+1], [1-m], w 

436 T2 = [-s, c, a, b], [-1, 1, -u, u], [n+m+1], [n-m+1, m+1], \ 

437 [-n, n+1], [m+1], w 

438 return T1, T2 

439 return ctx.hypercomb(h, [n, m], **kwargs) 

440 raise ValueError("requires type=2 or type=3") 

441 

442@defun_wrapped 

443def chebyt(ctx, n, x, **kwargs): 

444 if (not x) and ctx.isint(n) and int(ctx._re(n)) % 2 == 1: 

445 return x * 0 

446 return ctx.hyp2f1(-n,n,(1,2),(1-x)/2, **kwargs) 

447 

448@defun_wrapped 

449def chebyu(ctx, n, x, **kwargs): 

450 if (not x) and ctx.isint(n) and int(ctx._re(n)) % 2 == 1: 

451 return x * 0 

452 return (n+1) * ctx.hyp2f1(-n, n+2, (3,2), (1-x)/2, **kwargs) 

453 

454@defun 

455def spherharm(ctx, l, m, theta, phi, **kwargs): 

456 l = ctx.convert(l) 

457 m = ctx.convert(m) 

458 theta = ctx.convert(theta) 

459 phi = ctx.convert(phi) 

460 l_isint = ctx.isint(l) 

461 l_natural = l_isint and l >= 0 

462 m_isint = ctx.isint(m) 

463 if l_isint and l < 0 and m_isint: 

464 return ctx.spherharm(-(l+1), m, theta, phi, **kwargs) 

465 if theta == 0 and m_isint and m < 0: 

466 return ctx.zero * 1j 

467 if l_natural and m_isint: 

468 if abs(m) > l: 

469 return ctx.zero * 1j 

470 # http://functions.wolfram.com/Polynomials/ 

471 # SphericalHarmonicY/26/01/02/0004/ 

472 def h(l,m): 

473 absm = abs(m) 

474 C = [-1, ctx.expj(m*phi), 

475 (2*l+1)*ctx.fac(l+absm)/ctx.pi/ctx.fac(l-absm), 

476 ctx.sin(theta)**2, 

477 ctx.fac(absm), 2] 

478 P = [0.5*m*(ctx.sign(m)+1), 1, 0.5, 0.5*absm, -1, -absm-1] 

479 return ((C, P, [], [], [absm-l, l+absm+1], [absm+1], 

480 ctx.sin(0.5*theta)**2),) 

481 else: 

482 # http://functions.wolfram.com/HypergeometricFunctions/ 

483 # SphericalHarmonicYGeneral/26/01/02/0001/ 

484 def h(l,m): 

485 if ctx.isnpint(l-m+1) or ctx.isnpint(l+m+1) or ctx.isnpint(1-m): 

486 return (([0], [-1], [], [], [], [], 0),) 

487 cos, sin = ctx.cos_sin(0.5*theta) 

488 C = [0.5*ctx.expj(m*phi), (2*l+1)/ctx.pi, 

489 ctx.gamma(l-m+1), ctx.gamma(l+m+1), 

490 cos**2, sin**2] 

491 P = [1, 0.5, 0.5, -0.5, 0.5*m, -0.5*m] 

492 return ((C, P, [], [1-m], [-l,l+1], [1-m], sin**2),) 

493 return ctx.hypercomb(h, [l,m], **kwargs)