Coverage for /usr/lib/python3/dist-packages/sympy/functions/special/gamma_functions.py: 20%

579 statements  

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

1from math import prod 

2 

3from sympy.core import Add, S, Dummy, expand_func 

4from sympy.core.expr import Expr 

5from sympy.core.function import Function, ArgumentIndexError, PoleError 

6from sympy.core.logic import fuzzy_and, fuzzy_not 

7from sympy.core.numbers import Rational, pi, oo, I 

8from sympy.core.power import Pow 

9from sympy.functions.special.zeta_functions import zeta 

10from sympy.functions.special.error_functions import erf, erfc, Ei 

11from sympy.functions.elementary.complexes import re, unpolarify 

12from sympy.functions.elementary.exponential import exp, log 

13from sympy.functions.elementary.integers import ceiling, floor 

14from sympy.functions.elementary.miscellaneous import sqrt 

15from sympy.functions.elementary.trigonometric import sin, cos, cot 

16from sympy.functions.combinatorial.numbers import bernoulli, harmonic 

17from sympy.functions.combinatorial.factorials import factorial, rf, RisingFactorial 

18from sympy.utilities.misc import as_int 

19 

20from mpmath import mp, workprec 

21from mpmath.libmp.libmpf import prec_to_dps 

22 

23def intlike(n): 

24 try: 

25 as_int(n, strict=False) 

26 return True 

27 except ValueError: 

28 return False 

29 

30############################################################################### 

31############################ COMPLETE GAMMA FUNCTION ########################## 

32############################################################################### 

33 

34class gamma(Function): 

35 r""" 

36 The gamma function 

37 

38 .. math:: 

39 \Gamma(x) := \int^{\infty}_{0} t^{x-1} e^{-t} \mathrm{d}t. 

40 

41 Explanation 

42 =========== 

43 

44 The ``gamma`` function implements the function which passes through the 

45 values of the factorial function (i.e., $\Gamma(n) = (n - 1)!$ when n is 

46 an integer). More generally, $\Gamma(z)$ is defined in the whole complex 

47 plane except at the negative integers where there are simple poles. 

48 

49 Examples 

50 ======== 

51 

52 >>> from sympy import S, I, pi, gamma 

53 >>> from sympy.abc import x 

54 

55 Several special values are known: 

56 

57 >>> gamma(1) 

58 1 

59 >>> gamma(4) 

60 6 

61 >>> gamma(S(3)/2) 

62 sqrt(pi)/2 

63 

64 The ``gamma`` function obeys the mirror symmetry: 

65 

66 >>> from sympy import conjugate 

67 >>> conjugate(gamma(x)) 

68 gamma(conjugate(x)) 

69 

70 Differentiation with respect to $x$ is supported: 

71 

72 >>> from sympy import diff 

73 >>> diff(gamma(x), x) 

74 gamma(x)*polygamma(0, x) 

75 

76 Series expansion is also supported: 

77 

78 >>> from sympy import series 

79 >>> series(gamma(x), x, 0, 3) 

80 1/x - EulerGamma + x*(EulerGamma**2/2 + pi**2/12) + x**2*(-EulerGamma*pi**2/12 - zeta(3)/3 - EulerGamma**3/6) + O(x**3) 

81 

82 We can numerically evaluate the ``gamma`` function to arbitrary precision 

83 on the whole complex plane: 

84 

85 >>> gamma(pi).evalf(40) 

86 2.288037795340032417959588909060233922890 

87 >>> gamma(1+I).evalf(20) 

88 0.49801566811835604271 - 0.15494982830181068512*I 

89 

90 See Also 

91 ======== 

92 

93 lowergamma: Lower incomplete gamma function. 

94 uppergamma: Upper incomplete gamma function. 

95 polygamma: Polygamma function. 

96 loggamma: Log Gamma function. 

97 digamma: Digamma function. 

98 trigamma: Trigamma function. 

99 beta: Euler Beta function. 

100 

101 References 

102 ========== 

103 

104 .. [1] https://en.wikipedia.org/wiki/Gamma_function 

105 .. [2] https://dlmf.nist.gov/5 

106 .. [3] https://mathworld.wolfram.com/GammaFunction.html 

107 .. [4] https://functions.wolfram.com/GammaBetaErf/Gamma/ 

108 

109 """ 

110 

111 unbranched = True 

112 _singularities = (S.ComplexInfinity,) 

113 

114 def fdiff(self, argindex=1): 

115 if argindex == 1: 

116 return self.func(self.args[0])*polygamma(0, self.args[0]) 

117 else: 

118 raise ArgumentIndexError(self, argindex) 

119 

120 @classmethod 

121 def eval(cls, arg): 

122 if arg.is_Number: 

123 if arg is S.NaN: 

124 return S.NaN 

125 elif arg is oo: 

126 return oo 

127 elif intlike(arg): 

128 if arg.is_positive: 

129 return factorial(arg - 1) 

130 else: 

131 return S.ComplexInfinity 

132 elif arg.is_Rational: 

133 if arg.q == 2: 

134 n = abs(arg.p) // arg.q 

135 

136 if arg.is_positive: 

137 k, coeff = n, S.One 

138 else: 

139 n = k = n + 1 

140 

141 if n & 1 == 0: 

142 coeff = S.One 

143 else: 

144 coeff = S.NegativeOne 

145 

146 coeff *= prod(range(3, 2*k, 2)) 

147 

148 if arg.is_positive: 

149 return coeff*sqrt(pi) / 2**n 

150 else: 

151 return 2**n*sqrt(pi) / coeff 

152 

153 def _eval_expand_func(self, **hints): 

154 arg = self.args[0] 

155 if arg.is_Rational: 

156 if abs(arg.p) > arg.q: 

157 x = Dummy('x') 

158 n = arg.p // arg.q 

159 p = arg.p - n*arg.q 

160 return self.func(x + n)._eval_expand_func().subs(x, Rational(p, arg.q)) 

161 

162 if arg.is_Add: 

163 coeff, tail = arg.as_coeff_add() 

164 if coeff and coeff.q != 1: 

165 intpart = floor(coeff) 

166 tail = (coeff - intpart,) + tail 

167 coeff = intpart 

168 tail = arg._new_rawargs(*tail, reeval=False) 

169 return self.func(tail)*RisingFactorial(tail, coeff) 

170 

171 return self.func(*self.args) 

172 

173 def _eval_conjugate(self): 

174 return self.func(self.args[0].conjugate()) 

175 

176 def _eval_is_real(self): 

177 x = self.args[0] 

178 if x.is_nonpositive and x.is_integer: 

179 return False 

180 if intlike(x) and x <= 0: 

181 return False 

182 if x.is_positive or x.is_noninteger: 

183 return True 

184 

185 def _eval_is_positive(self): 

186 x = self.args[0] 

187 if x.is_positive: 

188 return True 

189 elif x.is_noninteger: 

190 return floor(x).is_even 

191 

192 def _eval_rewrite_as_tractable(self, z, limitvar=None, **kwargs): 

193 return exp(loggamma(z)) 

194 

195 def _eval_rewrite_as_factorial(self, z, **kwargs): 

196 return factorial(z - 1) 

197 

198 def _eval_nseries(self, x, n, logx, cdir=0): 

199 x0 = self.args[0].limit(x, 0) 

200 if not (x0.is_Integer and x0 <= 0): 

201 return super()._eval_nseries(x, n, logx) 

202 t = self.args[0] - x0 

203 return (self.func(t + 1)/rf(self.args[0], -x0 + 1))._eval_nseries(x, n, logx) 

204 

205 def _eval_as_leading_term(self, x, logx=None, cdir=0): 

206 arg = self.args[0] 

207 x0 = arg.subs(x, 0) 

208 

209 if x0.is_integer and x0.is_nonpositive: 

210 n = -x0 

211 res = S.NegativeOne**n/self.func(n + 1) 

212 return res/(arg + n).as_leading_term(x) 

213 elif not x0.is_infinite: 

214 return self.func(x0) 

215 raise PoleError() 

216 

217 

218############################################################################### 

219################## LOWER and UPPER INCOMPLETE GAMMA FUNCTIONS ################# 

220############################################################################### 

221 

222class lowergamma(Function): 

223 r""" 

224 The lower incomplete gamma function. 

225 

226 Explanation 

227 =========== 

228 

229 It can be defined as the meromorphic continuation of 

230 

231 .. math:: 

232 \gamma(s, x) := \int_0^x t^{s-1} e^{-t} \mathrm{d}t = \Gamma(s) - \Gamma(s, x). 

233 

234 This can be shown to be the same as 

235 

236 .. math:: 

237 \gamma(s, x) = \frac{x^s}{s} {}_1F_1\left({s \atop s+1} \middle| -x\right), 

238 

239 where ${}_1F_1$ is the (confluent) hypergeometric function. 

240 

241 Examples 

242 ======== 

243 

244 >>> from sympy import lowergamma, S 

245 >>> from sympy.abc import s, x 

246 >>> lowergamma(s, x) 

247 lowergamma(s, x) 

248 >>> lowergamma(3, x) 

249 -2*(x**2/2 + x + 1)*exp(-x) + 2 

250 >>> lowergamma(-S(1)/2, x) 

251 -2*sqrt(pi)*erf(sqrt(x)) - 2*exp(-x)/sqrt(x) 

252 

253 See Also 

254 ======== 

255 

256 gamma: Gamma function. 

257 uppergamma: Upper incomplete gamma function. 

258 polygamma: Polygamma function. 

259 loggamma: Log Gamma function. 

260 digamma: Digamma function. 

261 trigamma: Trigamma function. 

262 beta: Euler Beta function. 

263 

264 References 

265 ========== 

266 

267 .. [1] https://en.wikipedia.org/wiki/Incomplete_gamma_function#Lower_incomplete_gamma_function 

268 .. [2] Abramowitz, Milton; Stegun, Irene A., eds. (1965), Chapter 6, 

269 Section 5, Handbook of Mathematical Functions with Formulas, Graphs, 

270 and Mathematical Tables 

271 .. [3] https://dlmf.nist.gov/8 

272 .. [4] https://functions.wolfram.com/GammaBetaErf/Gamma2/ 

273 .. [5] https://functions.wolfram.com/GammaBetaErf/Gamma3/ 

274 

275 """ 

276 

277 

278 def fdiff(self, argindex=2): 

279 from sympy.functions.special.hyper import meijerg 

280 if argindex == 2: 

281 a, z = self.args 

282 return exp(-unpolarify(z))*z**(a - 1) 

283 elif argindex == 1: 

284 a, z = self.args 

285 return gamma(a)*digamma(a) - log(z)*uppergamma(a, z) \ 

286 - meijerg([], [1, 1], [0, 0, a], [], z) 

287 

288 else: 

289 raise ArgumentIndexError(self, argindex) 

290 

291 @classmethod 

292 def eval(cls, a, x): 

293 # For lack of a better place, we use this one to extract branching 

294 # information. The following can be 

295 # found in the literature (c/f references given above), albeit scattered: 

296 # 1) For fixed x != 0, lowergamma(s, x) is an entire function of s 

297 # 2) For fixed positive integers s, lowergamma(s, x) is an entire 

298 # function of x. 

299 # 3) For fixed non-positive integers s, 

300 # lowergamma(s, exp(I*2*pi*n)*x) = 

301 # 2*pi*I*n*(-1)**(-s)/factorial(-s) + lowergamma(s, x) 

302 # (this follows from lowergamma(s, x).diff(x) = x**(s-1)*exp(-x)). 

303 # 4) For fixed non-integral s, 

304 # lowergamma(s, x) = x**s*gamma(s)*lowergamma_unbranched(s, x), 

305 # where lowergamma_unbranched(s, x) is an entire function (in fact 

306 # of both s and x), i.e. 

307 # lowergamma(s, exp(2*I*pi*n)*x) = exp(2*pi*I*n*a)*lowergamma(a, x) 

308 if x is S.Zero: 

309 return S.Zero 

310 nx, n = x.extract_branch_factor() 

311 if a.is_integer and a.is_positive: 

312 nx = unpolarify(x) 

313 if nx != x: 

314 return lowergamma(a, nx) 

315 elif a.is_integer and a.is_nonpositive: 

316 if n != 0: 

317 return 2*pi*I*n*S.NegativeOne**(-a)/factorial(-a) + lowergamma(a, nx) 

318 elif n != 0: 

319 return exp(2*pi*I*n*a)*lowergamma(a, nx) 

320 

321 # Special values. 

322 if a.is_Number: 

323 if a is S.One: 

324 return S.One - exp(-x) 

325 elif a is S.Half: 

326 return sqrt(pi)*erf(sqrt(x)) 

327 elif a.is_Integer or (2*a).is_Integer: 

328 b = a - 1 

329 if b.is_positive: 

330 if a.is_integer: 

331 return factorial(b) - exp(-x) * factorial(b) * Add(*[x ** k / factorial(k) for k in range(a)]) 

332 else: 

333 return gamma(a)*(lowergamma(S.Half, x)/sqrt(pi) - exp(-x)*Add(*[x**(k - S.Half)/gamma(S.Half + k) for k in range(1, a + S.Half)])) 

334 

335 if not a.is_Integer: 

336 return S.NegativeOne**(S.Half - a)*pi*erf(sqrt(x))/gamma(1 - a) + exp(-x)*Add(*[x**(k + a - 1)*gamma(a)/gamma(a + k) for k in range(1, Rational(3, 2) - a)]) 

337 

338 if x.is_zero: 

339 return S.Zero 

340 

341 def _eval_evalf(self, prec): 

342 if all(x.is_number for x in self.args): 

343 a = self.args[0]._to_mpmath(prec) 

344 z = self.args[1]._to_mpmath(prec) 

345 with workprec(prec): 

346 res = mp.gammainc(a, 0, z) 

347 return Expr._from_mpmath(res, prec) 

348 else: 

349 return self 

350 

351 def _eval_conjugate(self): 

352 x = self.args[1] 

353 if x not in (S.Zero, S.NegativeInfinity): 

354 return self.func(self.args[0].conjugate(), x.conjugate()) 

355 

356 def _eval_is_meromorphic(self, x, a): 

357 # By https://en.wikipedia.org/wiki/Incomplete_gamma_function#Holomorphic_extension, 

358 # lowergamma(s, z) = z**s*gamma(s)*gammastar(s, z), 

359 # where gammastar(s, z) is holomorphic for all s and z. 

360 # Hence the singularities of lowergamma are z = 0 (branch 

361 # point) and nonpositive integer values of s (poles of gamma(s)). 

362 s, z = self.args 

363 args_merom = fuzzy_and([z._eval_is_meromorphic(x, a), 

364 s._eval_is_meromorphic(x, a)]) 

365 if not args_merom: 

366 return args_merom 

367 z0 = z.subs(x, a) 

368 if s.is_integer: 

369 return fuzzy_and([s.is_positive, z0.is_finite]) 

370 s0 = s.subs(x, a) 

371 return fuzzy_and([s0.is_finite, z0.is_finite, fuzzy_not(z0.is_zero)]) 

372 

373 def _eval_aseries(self, n, args0, x, logx): 

374 from sympy.series.order import O 

375 s, z = self.args 

376 if args0[0] is oo and not z.has(x): 

377 coeff = z**s*exp(-z) 

378 sum_expr = sum(z**k/rf(s, k + 1) for k in range(n - 1)) 

379 o = O(z**s*s**(-n)) 

380 return coeff*sum_expr + o 

381 return super()._eval_aseries(n, args0, x, logx) 

382 

383 def _eval_rewrite_as_uppergamma(self, s, x, **kwargs): 

384 return gamma(s) - uppergamma(s, x) 

385 

386 def _eval_rewrite_as_expint(self, s, x, **kwargs): 

387 from sympy.functions.special.error_functions import expint 

388 if s.is_integer and s.is_nonpositive: 

389 return self 

390 return self.rewrite(uppergamma).rewrite(expint) 

391 

392 def _eval_is_zero(self): 

393 x = self.args[1] 

394 if x.is_zero: 

395 return True 

396 

397 

398class uppergamma(Function): 

399 r""" 

400 The upper incomplete gamma function. 

401 

402 Explanation 

403 =========== 

404 

405 It can be defined as the meromorphic continuation of 

406 

407 .. math:: 

408 \Gamma(s, x) := \int_x^\infty t^{s-1} e^{-t} \mathrm{d}t = \Gamma(s) - \gamma(s, x). 

409 

410 where $\gamma(s, x)$ is the lower incomplete gamma function, 

411 :class:`lowergamma`. This can be shown to be the same as 

412 

413 .. math:: 

414 \Gamma(s, x) = \Gamma(s) - \frac{x^s}{s} {}_1F_1\left({s \atop s+1} \middle| -x\right), 

415 

416 where ${}_1F_1$ is the (confluent) hypergeometric function. 

417 

418 The upper incomplete gamma function is also essentially equivalent to the 

419 generalized exponential integral: 

420 

421 .. math:: 

422 \operatorname{E}_{n}(x) = \int_{1}^{\infty}{\frac{e^{-xt}}{t^n} \, dt} = x^{n-1}\Gamma(1-n,x). 

423 

424 Examples 

425 ======== 

426 

427 >>> from sympy import uppergamma, S 

428 >>> from sympy.abc import s, x 

429 >>> uppergamma(s, x) 

430 uppergamma(s, x) 

431 >>> uppergamma(3, x) 

432 2*(x**2/2 + x + 1)*exp(-x) 

433 >>> uppergamma(-S(1)/2, x) 

434 -2*sqrt(pi)*erfc(sqrt(x)) + 2*exp(-x)/sqrt(x) 

435 >>> uppergamma(-2, x) 

436 expint(3, x)/x**2 

437 

438 See Also 

439 ======== 

440 

441 gamma: Gamma function. 

442 lowergamma: Lower incomplete gamma function. 

443 polygamma: Polygamma function. 

444 loggamma: Log Gamma function. 

445 digamma: Digamma function. 

446 trigamma: Trigamma function. 

447 beta: Euler Beta function. 

448 

449 References 

450 ========== 

451 

452 .. [1] https://en.wikipedia.org/wiki/Incomplete_gamma_function#Upper_incomplete_gamma_function 

453 .. [2] Abramowitz, Milton; Stegun, Irene A., eds. (1965), Chapter 6, 

454 Section 5, Handbook of Mathematical Functions with Formulas, Graphs, 

455 and Mathematical Tables 

456 .. [3] https://dlmf.nist.gov/8 

457 .. [4] https://functions.wolfram.com/GammaBetaErf/Gamma2/ 

458 .. [5] https://functions.wolfram.com/GammaBetaErf/Gamma3/ 

459 .. [6] https://en.wikipedia.org/wiki/Exponential_integral#Relation_with_other_functions 

460 

461 """ 

462 

463 

464 def fdiff(self, argindex=2): 

465 from sympy.functions.special.hyper import meijerg 

466 if argindex == 2: 

467 a, z = self.args 

468 return -exp(-unpolarify(z))*z**(a - 1) 

469 elif argindex == 1: 

470 a, z = self.args 

471 return uppergamma(a, z)*log(z) + meijerg([], [1, 1], [0, 0, a], [], z) 

472 else: 

473 raise ArgumentIndexError(self, argindex) 

474 

475 def _eval_evalf(self, prec): 

476 if all(x.is_number for x in self.args): 

477 a = self.args[0]._to_mpmath(prec) 

478 z = self.args[1]._to_mpmath(prec) 

479 with workprec(prec): 

480 res = mp.gammainc(a, z, mp.inf) 

481 return Expr._from_mpmath(res, prec) 

482 return self 

483 

484 @classmethod 

485 def eval(cls, a, z): 

486 from sympy.functions.special.error_functions import expint 

487 if z.is_Number: 

488 if z is S.NaN: 

489 return S.NaN 

490 elif z is oo: 

491 return S.Zero 

492 elif z.is_zero: 

493 if re(a).is_positive: 

494 return gamma(a) 

495 

496 # We extract branching information here. C/f lowergamma. 

497 nx, n = z.extract_branch_factor() 

498 if a.is_integer and a.is_positive: 

499 nx = unpolarify(z) 

500 if z != nx: 

501 return uppergamma(a, nx) 

502 elif a.is_integer and a.is_nonpositive: 

503 if n != 0: 

504 return -2*pi*I*n*S.NegativeOne**(-a)/factorial(-a) + uppergamma(a, nx) 

505 elif n != 0: 

506 return gamma(a)*(1 - exp(2*pi*I*n*a)) + exp(2*pi*I*n*a)*uppergamma(a, nx) 

507 

508 # Special values. 

509 if a.is_Number: 

510 if a is S.Zero and z.is_positive: 

511 return -Ei(-z) 

512 elif a is S.One: 

513 return exp(-z) 

514 elif a is S.Half: 

515 return sqrt(pi)*erfc(sqrt(z)) 

516 elif a.is_Integer or (2*a).is_Integer: 

517 b = a - 1 

518 if b.is_positive: 

519 if a.is_integer: 

520 return exp(-z) * factorial(b) * Add(*[z**k / factorial(k) 

521 for k in range(a)]) 

522 else: 

523 return (gamma(a) * erfc(sqrt(z)) + 

524 S.NegativeOne**(a - S(3)/2) * exp(-z) * sqrt(z) 

525 * Add(*[gamma(-S.Half - k) * (-z)**k / gamma(1-a) 

526 for k in range(a - S.Half)])) 

527 elif b.is_Integer: 

528 return expint(-b, z)*unpolarify(z)**(b + 1) 

529 

530 if not a.is_Integer: 

531 return (S.NegativeOne**(S.Half - a) * pi*erfc(sqrt(z))/gamma(1-a) 

532 - z**a * exp(-z) * Add(*[z**k * gamma(a) / gamma(a+k+1) 

533 for k in range(S.Half - a)])) 

534 

535 if a.is_zero and z.is_positive: 

536 return -Ei(-z) 

537 

538 if z.is_zero and re(a).is_positive: 

539 return gamma(a) 

540 

541 def _eval_conjugate(self): 

542 z = self.args[1] 

543 if z not in (S.Zero, S.NegativeInfinity): 

544 return self.func(self.args[0].conjugate(), z.conjugate()) 

545 

546 def _eval_is_meromorphic(self, x, a): 

547 return lowergamma._eval_is_meromorphic(self, x, a) 

548 

549 def _eval_rewrite_as_lowergamma(self, s, x, **kwargs): 

550 return gamma(s) - lowergamma(s, x) 

551 

552 def _eval_rewrite_as_tractable(self, s, x, **kwargs): 

553 return exp(loggamma(s)) - lowergamma(s, x) 

554 

555 def _eval_rewrite_as_expint(self, s, x, **kwargs): 

556 from sympy.functions.special.error_functions import expint 

557 return expint(1 - s, x)*x**s 

558 

559 

560############################################################################### 

561###################### POLYGAMMA and LOGGAMMA FUNCTIONS ####################### 

562############################################################################### 

563 

564class polygamma(Function): 

565 r""" 

566 The function ``polygamma(n, z)`` returns ``log(gamma(z)).diff(n + 1)``. 

567 

568 Explanation 

569 =========== 

570 

571 It is a meromorphic function on $\mathbb{C}$ and defined as the $(n+1)$-th 

572 derivative of the logarithm of the gamma function: 

573 

574 .. math:: 

575 \psi^{(n)} (z) := \frac{\mathrm{d}^{n+1}}{\mathrm{d} z^{n+1}} \log\Gamma(z). 

576 

577 For `n` not a nonnegative integer the generalization by Espinosa and Moll [5]_ 

578 is used: 

579 

580 .. math:: \psi(s,z) = \frac{\zeta'(s+1, z) + (\gamma + \psi(-s)) \zeta(s+1, z)} 

581 {\Gamma(-s)} 

582 

583 Examples 

584 ======== 

585 

586 Several special values are known: 

587 

588 >>> from sympy import S, polygamma 

589 >>> polygamma(0, 1) 

590 -EulerGamma 

591 >>> polygamma(0, 1/S(2)) 

592 -2*log(2) - EulerGamma 

593 >>> polygamma(0, 1/S(3)) 

594 -log(3) - sqrt(3)*pi/6 - EulerGamma - log(sqrt(3)) 

595 >>> polygamma(0, 1/S(4)) 

596 -pi/2 - log(4) - log(2) - EulerGamma 

597 >>> polygamma(0, 2) 

598 1 - EulerGamma 

599 >>> polygamma(0, 23) 

600 19093197/5173168 - EulerGamma 

601 

602 >>> from sympy import oo, I 

603 >>> polygamma(0, oo) 

604 oo 

605 >>> polygamma(0, -oo) 

606 oo 

607 >>> polygamma(0, I*oo) 

608 oo 

609 >>> polygamma(0, -I*oo) 

610 oo 

611 

612 Differentiation with respect to $x$ is supported: 

613 

614 >>> from sympy import Symbol, diff 

615 >>> x = Symbol("x") 

616 >>> diff(polygamma(0, x), x) 

617 polygamma(1, x) 

618 >>> diff(polygamma(0, x), x, 2) 

619 polygamma(2, x) 

620 >>> diff(polygamma(0, x), x, 3) 

621 polygamma(3, x) 

622 >>> diff(polygamma(1, x), x) 

623 polygamma(2, x) 

624 >>> diff(polygamma(1, x), x, 2) 

625 polygamma(3, x) 

626 >>> diff(polygamma(2, x), x) 

627 polygamma(3, x) 

628 >>> diff(polygamma(2, x), x, 2) 

629 polygamma(4, x) 

630 

631 >>> n = Symbol("n") 

632 >>> diff(polygamma(n, x), x) 

633 polygamma(n + 1, x) 

634 >>> diff(polygamma(n, x), x, 2) 

635 polygamma(n + 2, x) 

636 

637 We can rewrite ``polygamma`` functions in terms of harmonic numbers: 

638 

639 >>> from sympy import harmonic 

640 >>> polygamma(0, x).rewrite(harmonic) 

641 harmonic(x - 1) - EulerGamma 

642 >>> polygamma(2, x).rewrite(harmonic) 

643 2*harmonic(x - 1, 3) - 2*zeta(3) 

644 >>> ni = Symbol("n", integer=True) 

645 >>> polygamma(ni, x).rewrite(harmonic) 

646 (-1)**(n + 1)*(-harmonic(x - 1, n + 1) + zeta(n + 1))*factorial(n) 

647 

648 See Also 

649 ======== 

650 

651 gamma: Gamma function. 

652 lowergamma: Lower incomplete gamma function. 

653 uppergamma: Upper incomplete gamma function. 

654 loggamma: Log Gamma function. 

655 digamma: Digamma function. 

656 trigamma: Trigamma function. 

657 beta: Euler Beta function. 

658 

659 References 

660 ========== 

661 

662 .. [1] https://en.wikipedia.org/wiki/Polygamma_function 

663 .. [2] https://mathworld.wolfram.com/PolygammaFunction.html 

664 .. [3] https://functions.wolfram.com/GammaBetaErf/PolyGamma/ 

665 .. [4] https://functions.wolfram.com/GammaBetaErf/PolyGamma2/ 

666 .. [5] O. Espinosa and V. Moll, "A generalized polygamma function", 

667 *Integral Transforms and Special Functions* (2004), 101-115. 

668 

669 """ 

670 

671 @classmethod 

672 def eval(cls, n, z): 

673 if n is S.NaN or z is S.NaN: 

674 return S.NaN 

675 elif z is oo: 

676 return oo if n.is_zero else S.Zero 

677 elif z.is_Integer and z.is_nonpositive: 

678 return S.ComplexInfinity 

679 elif n is S.NegativeOne: 

680 return loggamma(z) - log(2*pi) / 2 

681 elif n.is_zero: 

682 if z is -oo or z.extract_multiplicatively(I) in (oo, -oo): 

683 return oo 

684 elif z.is_Integer: 

685 return harmonic(z-1) - S.EulerGamma 

686 elif z.is_Rational: 

687 # TODO n == 1 also can do some rational z 

688 p, q = z.as_numer_denom() 

689 # only expand for small denominators to avoid creating long expressions 

690 if q <= 6: 

691 return expand_func(polygamma(S.Zero, z, evaluate=False)) 

692 elif n.is_integer and n.is_nonnegative: 

693 nz = unpolarify(z) 

694 if z != nz: 

695 return polygamma(n, nz) 

696 if z.is_Integer: 

697 return S.NegativeOne**(n+1) * factorial(n) * zeta(n+1, z) 

698 elif z is S.Half: 

699 return S.NegativeOne**(n+1) * factorial(n) * (2**(n+1)-1) * zeta(n+1) 

700 

701 def _eval_is_real(self): 

702 if self.args[0].is_positive and self.args[1].is_positive: 

703 return True 

704 

705 def _eval_is_complex(self): 

706 z = self.args[1] 

707 is_negative_integer = fuzzy_and([z.is_negative, z.is_integer]) 

708 return fuzzy_and([z.is_complex, fuzzy_not(is_negative_integer)]) 

709 

710 def _eval_is_positive(self): 

711 n, z = self.args 

712 if n.is_positive: 

713 if n.is_odd and z.is_real: 

714 return True 

715 if n.is_even and z.is_positive: 

716 return False 

717 

718 def _eval_is_negative(self): 

719 n, z = self.args 

720 if n.is_positive: 

721 if n.is_even and z.is_positive: 

722 return True 

723 if n.is_odd and z.is_real: 

724 return False 

725 

726 def _eval_expand_func(self, **hints): 

727 n, z = self.args 

728 

729 if n.is_Integer and n.is_nonnegative: 

730 if z.is_Add: 

731 coeff = z.args[0] 

732 if coeff.is_Integer: 

733 e = -(n + 1) 

734 if coeff > 0: 

735 tail = Add(*[Pow( 

736 z - i, e) for i in range(1, int(coeff) + 1)]) 

737 else: 

738 tail = -Add(*[Pow( 

739 z + i, e) for i in range(int(-coeff))]) 

740 return polygamma(n, z - coeff) + S.NegativeOne**n*factorial(n)*tail 

741 

742 elif z.is_Mul: 

743 coeff, z = z.as_two_terms() 

744 if coeff.is_Integer and coeff.is_positive: 

745 tail = [polygamma(n, z + Rational( 

746 i, coeff)) for i in range(int(coeff))] 

747 if n == 0: 

748 return Add(*tail)/coeff + log(coeff) 

749 else: 

750 return Add(*tail)/coeff**(n + 1) 

751 z *= coeff 

752 

753 if n == 0 and z.is_Rational: 

754 p, q = z.as_numer_denom() 

755 

756 # Reference: 

757 # Values of the polygamma functions at rational arguments, J. Choi, 2007 

758 part_1 = -S.EulerGamma - pi * cot(p * pi / q) / 2 - log(q) + Add( 

759 *[cos(2 * k * pi * p / q) * log(2 * sin(k * pi / q)) for k in range(1, q)]) 

760 

761 if z > 0: 

762 n = floor(z) 

763 z0 = z - n 

764 return part_1 + Add(*[1 / (z0 + k) for k in range(n)]) 

765 elif z < 0: 

766 n = floor(1 - z) 

767 z0 = z + n 

768 return part_1 - Add(*[1 / (z0 - 1 - k) for k in range(n)]) 

769 

770 if n == -1: 

771 return loggamma(z) - log(2*pi) / 2 

772 if n.is_integer is False or n.is_nonnegative is False: 

773 s = Dummy("s") 

774 dzt = zeta(s, z).diff(s).subs(s, n+1) 

775 return (dzt + (S.EulerGamma + digamma(-n)) * zeta(n+1, z)) / gamma(-n) 

776 

777 return polygamma(n, z) 

778 

779 def _eval_rewrite_as_zeta(self, n, z, **kwargs): 

780 if n.is_integer and n.is_positive: 

781 return S.NegativeOne**(n + 1)*factorial(n)*zeta(n + 1, z) 

782 

783 def _eval_rewrite_as_harmonic(self, n, z, **kwargs): 

784 if n.is_integer: 

785 if n.is_zero: 

786 return harmonic(z - 1) - S.EulerGamma 

787 else: 

788 return S.NegativeOne**(n+1) * factorial(n) * (zeta(n+1) - harmonic(z-1, n+1)) 

789 

790 def _eval_as_leading_term(self, x, logx=None, cdir=0): 

791 from sympy.series.order import Order 

792 n, z = [a.as_leading_term(x) for a in self.args] 

793 o = Order(z, x) 

794 if n == 0 and o.contains(1/x): 

795 logx = log(x) if logx is None else logx 

796 return o.getn() * logx 

797 else: 

798 return self.func(n, z) 

799 

800 def fdiff(self, argindex=2): 

801 if argindex == 2: 

802 n, z = self.args[:2] 

803 return polygamma(n + 1, z) 

804 else: 

805 raise ArgumentIndexError(self, argindex) 

806 

807 def _eval_aseries(self, n, args0, x, logx): 

808 from sympy.series.order import Order 

809 if args0[1] != oo or not \ 

810 (self.args[0].is_Integer and self.args[0].is_nonnegative): 

811 return super()._eval_aseries(n, args0, x, logx) 

812 z = self.args[1] 

813 N = self.args[0] 

814 

815 if N == 0: 

816 # digamma function series 

817 # Abramowitz & Stegun, p. 259, 6.3.18 

818 r = log(z) - 1/(2*z) 

819 o = None 

820 if n < 2: 

821 o = Order(1/z, x) 

822 else: 

823 m = ceiling((n + 1)//2) 

824 l = [bernoulli(2*k) / (2*k*z**(2*k)) for k in range(1, m)] 

825 r -= Add(*l) 

826 o = Order(1/z**n, x) 

827 return r._eval_nseries(x, n, logx) + o 

828 else: 

829 # proper polygamma function 

830 # Abramowitz & Stegun, p. 260, 6.4.10 

831 # We return terms to order higher than O(x**n) on purpose 

832 # -- otherwise we would not be able to return any terms for 

833 # quite a long time! 

834 fac = gamma(N) 

835 e0 = fac + N*fac/(2*z) 

836 m = ceiling((n + 1)//2) 

837 for k in range(1, m): 

838 fac = fac*(2*k + N - 1)*(2*k + N - 2) / ((2*k)*(2*k - 1)) 

839 e0 += bernoulli(2*k)*fac/z**(2*k) 

840 o = Order(1/z**(2*m), x) 

841 if n == 0: 

842 o = Order(1/z, x) 

843 elif n == 1: 

844 o = Order(1/z**2, x) 

845 r = e0._eval_nseries(z, n, logx) + o 

846 return (-1 * (-1/z)**N * r)._eval_nseries(x, n, logx) 

847 

848 def _eval_evalf(self, prec): 

849 if not all(i.is_number for i in self.args): 

850 return 

851 s = self.args[0]._to_mpmath(prec+12) 

852 z = self.args[1]._to_mpmath(prec+12) 

853 if mp.isint(z) and z <= 0: 

854 return S.ComplexInfinity 

855 with workprec(prec+12): 

856 if mp.isint(s) and s >= 0: 

857 res = mp.polygamma(s, z) 

858 else: 

859 zt = mp.zeta(s+1, z) 

860 dzt = mp.zeta(s+1, z, 1) 

861 res = (dzt + (mp.euler + mp.digamma(-s)) * zt) * mp.rgamma(-s) 

862 return Expr._from_mpmath(res, prec) 

863 

864 

865class loggamma(Function): 

866 r""" 

867 The ``loggamma`` function implements the logarithm of the 

868 gamma function (i.e., $\log\Gamma(x)$). 

869 

870 Examples 

871 ======== 

872 

873 Several special values are known. For numerical integral 

874 arguments we have: 

875 

876 >>> from sympy import loggamma 

877 >>> loggamma(-2) 

878 oo 

879 >>> loggamma(0) 

880 oo 

881 >>> loggamma(1) 

882 0 

883 >>> loggamma(2) 

884 0 

885 >>> loggamma(3) 

886 log(2) 

887 

888 And for symbolic values: 

889 

890 >>> from sympy import Symbol 

891 >>> n = Symbol("n", integer=True, positive=True) 

892 >>> loggamma(n) 

893 log(gamma(n)) 

894 >>> loggamma(-n) 

895 oo 

896 

897 For half-integral values: 

898 

899 >>> from sympy import S 

900 >>> loggamma(S(5)/2) 

901 log(3*sqrt(pi)/4) 

902 >>> loggamma(n/2) 

903 log(2**(1 - n)*sqrt(pi)*gamma(n)/gamma(n/2 + 1/2)) 

904 

905 And general rational arguments: 

906 

907 >>> from sympy import expand_func 

908 >>> L = loggamma(S(16)/3) 

909 >>> expand_func(L).doit() 

910 -5*log(3) + loggamma(1/3) + log(4) + log(7) + log(10) + log(13) 

911 >>> L = loggamma(S(19)/4) 

912 >>> expand_func(L).doit() 

913 -4*log(4) + loggamma(3/4) + log(3) + log(7) + log(11) + log(15) 

914 >>> L = loggamma(S(23)/7) 

915 >>> expand_func(L).doit() 

916 -3*log(7) + log(2) + loggamma(2/7) + log(9) + log(16) 

917 

918 The ``loggamma`` function has the following limits towards infinity: 

919 

920 >>> from sympy import oo 

921 >>> loggamma(oo) 

922 oo 

923 >>> loggamma(-oo) 

924 zoo 

925 

926 The ``loggamma`` function obeys the mirror symmetry 

927 if $x \in \mathbb{C} \setminus \{-\infty, 0\}$: 

928 

929 >>> from sympy.abc import x 

930 >>> from sympy import conjugate 

931 >>> conjugate(loggamma(x)) 

932 loggamma(conjugate(x)) 

933 

934 Differentiation with respect to $x$ is supported: 

935 

936 >>> from sympy import diff 

937 >>> diff(loggamma(x), x) 

938 polygamma(0, x) 

939 

940 Series expansion is also supported: 

941 

942 >>> from sympy import series 

943 >>> series(loggamma(x), x, 0, 4).cancel() 

944 -log(x) - EulerGamma*x + pi**2*x**2/12 - x**3*zeta(3)/3 + O(x**4) 

945 

946 We can numerically evaluate the ``loggamma`` function 

947 to arbitrary precision on the whole complex plane: 

948 

949 >>> from sympy import I 

950 >>> loggamma(5).evalf(30) 

951 3.17805383034794561964694160130 

952 >>> loggamma(I).evalf(20) 

953 -0.65092319930185633889 - 1.8724366472624298171*I 

954 

955 See Also 

956 ======== 

957 

958 gamma: Gamma function. 

959 lowergamma: Lower incomplete gamma function. 

960 uppergamma: Upper incomplete gamma function. 

961 polygamma: Polygamma function. 

962 digamma: Digamma function. 

963 trigamma: Trigamma function. 

964 beta: Euler Beta function. 

965 

966 References 

967 ========== 

968 

969 .. [1] https://en.wikipedia.org/wiki/Gamma_function 

970 .. [2] https://dlmf.nist.gov/5 

971 .. [3] https://mathworld.wolfram.com/LogGammaFunction.html 

972 .. [4] https://functions.wolfram.com/GammaBetaErf/LogGamma/ 

973 

974 """ 

975 @classmethod 

976 def eval(cls, z): 

977 if z.is_integer: 

978 if z.is_nonpositive: 

979 return oo 

980 elif z.is_positive: 

981 return log(gamma(z)) 

982 elif z.is_rational: 

983 p, q = z.as_numer_denom() 

984 # Half-integral values: 

985 if p.is_positive and q == 2: 

986 return log(sqrt(pi) * 2**(1 - p) * gamma(p) / gamma((p + 1)*S.Half)) 

987 

988 if z is oo: 

989 return oo 

990 elif abs(z) is oo: 

991 return S.ComplexInfinity 

992 if z is S.NaN: 

993 return S.NaN 

994 

995 def _eval_expand_func(self, **hints): 

996 from sympy.concrete.summations import Sum 

997 z = self.args[0] 

998 

999 if z.is_Rational: 

1000 p, q = z.as_numer_denom() 

1001 # General rational arguments (u + p/q) 

1002 # Split z as n + p/q with p < q 

1003 n = p // q 

1004 p = p - n*q 

1005 if p.is_positive and q.is_positive and p < q: 

1006 k = Dummy("k") 

1007 if n.is_positive: 

1008 return loggamma(p / q) - n*log(q) + Sum(log((k - 1)*q + p), (k, 1, n)) 

1009 elif n.is_negative: 

1010 return loggamma(p / q) - n*log(q) + pi*I*n - Sum(log(k*q - p), (k, 1, -n)) 

1011 elif n.is_zero: 

1012 return loggamma(p / q) 

1013 

1014 return self 

1015 

1016 def _eval_nseries(self, x, n, logx=None, cdir=0): 

1017 x0 = self.args[0].limit(x, 0) 

1018 if x0.is_zero: 

1019 f = self._eval_rewrite_as_intractable(*self.args) 

1020 return f._eval_nseries(x, n, logx) 

1021 return super()._eval_nseries(x, n, logx) 

1022 

1023 def _eval_aseries(self, n, args0, x, logx): 

1024 from sympy.series.order import Order 

1025 if args0[0] != oo: 

1026 return super()._eval_aseries(n, args0, x, logx) 

1027 z = self.args[0] 

1028 r = log(z)*(z - S.Half) - z + log(2*pi)/2 

1029 l = [bernoulli(2*k) / (2*k*(2*k - 1)*z**(2*k - 1)) for k in range(1, n)] 

1030 o = None 

1031 if n == 0: 

1032 o = Order(1, x) 

1033 else: 

1034 o = Order(1/z**n, x) 

1035 # It is very inefficient to first add the order and then do the nseries 

1036 return (r + Add(*l))._eval_nseries(x, n, logx) + o 

1037 

1038 def _eval_rewrite_as_intractable(self, z, **kwargs): 

1039 return log(gamma(z)) 

1040 

1041 def _eval_is_real(self): 

1042 z = self.args[0] 

1043 if z.is_positive: 

1044 return True 

1045 elif z.is_nonpositive: 

1046 return False 

1047 

1048 def _eval_conjugate(self): 

1049 z = self.args[0] 

1050 if z not in (S.Zero, S.NegativeInfinity): 

1051 return self.func(z.conjugate()) 

1052 

1053 def fdiff(self, argindex=1): 

1054 if argindex == 1: 

1055 return polygamma(0, self.args[0]) 

1056 else: 

1057 raise ArgumentIndexError(self, argindex) 

1058 

1059 

1060class digamma(Function): 

1061 r""" 

1062 The ``digamma`` function is the first derivative of the ``loggamma`` 

1063 function 

1064 

1065 .. math:: 

1066 \psi(x) := \frac{\mathrm{d}}{\mathrm{d} z} \log\Gamma(z) 

1067 = \frac{\Gamma'(z)}{\Gamma(z) }. 

1068 

1069 In this case, ``digamma(z) = polygamma(0, z)``. 

1070 

1071 Examples 

1072 ======== 

1073 

1074 >>> from sympy import digamma 

1075 >>> digamma(0) 

1076 zoo 

1077 >>> from sympy import Symbol 

1078 >>> z = Symbol('z') 

1079 >>> digamma(z) 

1080 polygamma(0, z) 

1081 

1082 To retain ``digamma`` as it is: 

1083 

1084 >>> digamma(0, evaluate=False) 

1085 digamma(0) 

1086 >>> digamma(z, evaluate=False) 

1087 digamma(z) 

1088 

1089 See Also 

1090 ======== 

1091 

1092 gamma: Gamma function. 

1093 lowergamma: Lower incomplete gamma function. 

1094 uppergamma: Upper incomplete gamma function. 

1095 polygamma: Polygamma function. 

1096 loggamma: Log Gamma function. 

1097 trigamma: Trigamma function. 

1098 beta: Euler Beta function. 

1099 

1100 References 

1101 ========== 

1102 

1103 .. [1] https://en.wikipedia.org/wiki/Digamma_function 

1104 .. [2] https://mathworld.wolfram.com/DigammaFunction.html 

1105 .. [3] https://functions.wolfram.com/GammaBetaErf/PolyGamma2/ 

1106 

1107 """ 

1108 def _eval_evalf(self, prec): 

1109 z = self.args[0] 

1110 nprec = prec_to_dps(prec) 

1111 return polygamma(0, z).evalf(n=nprec) 

1112 

1113 def fdiff(self, argindex=1): 

1114 z = self.args[0] 

1115 return polygamma(0, z).fdiff() 

1116 

1117 def _eval_is_real(self): 

1118 z = self.args[0] 

1119 return polygamma(0, z).is_real 

1120 

1121 def _eval_is_positive(self): 

1122 z = self.args[0] 

1123 return polygamma(0, z).is_positive 

1124 

1125 def _eval_is_negative(self): 

1126 z = self.args[0] 

1127 return polygamma(0, z).is_negative 

1128 

1129 def _eval_aseries(self, n, args0, x, logx): 

1130 as_polygamma = self.rewrite(polygamma) 

1131 args0 = [S.Zero,] + args0 

1132 return as_polygamma._eval_aseries(n, args0, x, logx) 

1133 

1134 @classmethod 

1135 def eval(cls, z): 

1136 return polygamma(0, z) 

1137 

1138 def _eval_expand_func(self, **hints): 

1139 z = self.args[0] 

1140 return polygamma(0, z).expand(func=True) 

1141 

1142 def _eval_rewrite_as_harmonic(self, z, **kwargs): 

1143 return harmonic(z - 1) - S.EulerGamma 

1144 

1145 def _eval_rewrite_as_polygamma(self, z, **kwargs): 

1146 return polygamma(0, z) 

1147 

1148 def _eval_as_leading_term(self, x, logx=None, cdir=0): 

1149 z = self.args[0] 

1150 return polygamma(0, z).as_leading_term(x) 

1151 

1152 

1153 

1154class trigamma(Function): 

1155 r""" 

1156 The ``trigamma`` function is the second derivative of the ``loggamma`` 

1157 function 

1158 

1159 .. math:: 

1160 \psi^{(1)}(z) := \frac{\mathrm{d}^{2}}{\mathrm{d} z^{2}} \log\Gamma(z). 

1161 

1162 In this case, ``trigamma(z) = polygamma(1, z)``. 

1163 

1164 Examples 

1165 ======== 

1166 

1167 >>> from sympy import trigamma 

1168 >>> trigamma(0) 

1169 zoo 

1170 >>> from sympy import Symbol 

1171 >>> z = Symbol('z') 

1172 >>> trigamma(z) 

1173 polygamma(1, z) 

1174 

1175 To retain ``trigamma`` as it is: 

1176 

1177 >>> trigamma(0, evaluate=False) 

1178 trigamma(0) 

1179 >>> trigamma(z, evaluate=False) 

1180 trigamma(z) 

1181 

1182 

1183 See Also 

1184 ======== 

1185 

1186 gamma: Gamma function. 

1187 lowergamma: Lower incomplete gamma function. 

1188 uppergamma: Upper incomplete gamma function. 

1189 polygamma: Polygamma function. 

1190 loggamma: Log Gamma function. 

1191 digamma: Digamma function. 

1192 beta: Euler Beta function. 

1193 

1194 References 

1195 ========== 

1196 

1197 .. [1] https://en.wikipedia.org/wiki/Trigamma_function 

1198 .. [2] https://mathworld.wolfram.com/TrigammaFunction.html 

1199 .. [3] https://functions.wolfram.com/GammaBetaErf/PolyGamma2/ 

1200 

1201 """ 

1202 def _eval_evalf(self, prec): 

1203 z = self.args[0] 

1204 nprec = prec_to_dps(prec) 

1205 return polygamma(1, z).evalf(n=nprec) 

1206 

1207 def fdiff(self, argindex=1): 

1208 z = self.args[0] 

1209 return polygamma(1, z).fdiff() 

1210 

1211 def _eval_is_real(self): 

1212 z = self.args[0] 

1213 return polygamma(1, z).is_real 

1214 

1215 def _eval_is_positive(self): 

1216 z = self.args[0] 

1217 return polygamma(1, z).is_positive 

1218 

1219 def _eval_is_negative(self): 

1220 z = self.args[0] 

1221 return polygamma(1, z).is_negative 

1222 

1223 def _eval_aseries(self, n, args0, x, logx): 

1224 as_polygamma = self.rewrite(polygamma) 

1225 args0 = [S.One,] + args0 

1226 return as_polygamma._eval_aseries(n, args0, x, logx) 

1227 

1228 @classmethod 

1229 def eval(cls, z): 

1230 return polygamma(1, z) 

1231 

1232 def _eval_expand_func(self, **hints): 

1233 z = self.args[0] 

1234 return polygamma(1, z).expand(func=True) 

1235 

1236 def _eval_rewrite_as_zeta(self, z, **kwargs): 

1237 return zeta(2, z) 

1238 

1239 def _eval_rewrite_as_polygamma(self, z, **kwargs): 

1240 return polygamma(1, z) 

1241 

1242 def _eval_rewrite_as_harmonic(self, z, **kwargs): 

1243 return -harmonic(z - 1, 2) + pi**2 / 6 

1244 

1245 def _eval_as_leading_term(self, x, logx=None, cdir=0): 

1246 z = self.args[0] 

1247 return polygamma(1, z).as_leading_term(x) 

1248 

1249 

1250############################################################################### 

1251##################### COMPLETE MULTIVARIATE GAMMA FUNCTION #################### 

1252############################################################################### 

1253 

1254 

1255class multigamma(Function): 

1256 r""" 

1257 The multivariate gamma function is a generalization of the gamma function 

1258 

1259 .. math:: 

1260 \Gamma_p(z) = \pi^{p(p-1)/4}\prod_{k=1}^p \Gamma[z + (1 - k)/2]. 

1261 

1262 In a special case, ``multigamma(x, 1) = gamma(x)``. 

1263 

1264 Examples 

1265 ======== 

1266 

1267 >>> from sympy import S, multigamma 

1268 >>> from sympy import Symbol 

1269 >>> x = Symbol('x') 

1270 >>> p = Symbol('p', positive=True, integer=True) 

1271 

1272 >>> multigamma(x, p) 

1273 pi**(p*(p - 1)/4)*Product(gamma(-_k/2 + x + 1/2), (_k, 1, p)) 

1274 

1275 Several special values are known: 

1276 

1277 >>> multigamma(1, 1) 

1278 1 

1279 >>> multigamma(4, 1) 

1280 6 

1281 >>> multigamma(S(3)/2, 1) 

1282 sqrt(pi)/2 

1283 

1284 Writing ``multigamma`` in terms of the ``gamma`` function: 

1285 

1286 >>> multigamma(x, 1) 

1287 gamma(x) 

1288 

1289 >>> multigamma(x, 2) 

1290 sqrt(pi)*gamma(x)*gamma(x - 1/2) 

1291 

1292 >>> multigamma(x, 3) 

1293 pi**(3/2)*gamma(x)*gamma(x - 1)*gamma(x - 1/2) 

1294 

1295 Parameters 

1296 ========== 

1297 

1298 p : order or dimension of the multivariate gamma function 

1299 

1300 See Also 

1301 ======== 

1302 

1303 gamma, lowergamma, uppergamma, polygamma, loggamma, digamma, trigamma, 

1304 beta 

1305 

1306 References 

1307 ========== 

1308 

1309 .. [1] https://en.wikipedia.org/wiki/Multivariate_gamma_function 

1310 

1311 """ 

1312 unbranched = True 

1313 

1314 def fdiff(self, argindex=2): 

1315 from sympy.concrete.summations import Sum 

1316 if argindex == 2: 

1317 x, p = self.args 

1318 k = Dummy("k") 

1319 return self.func(x, p)*Sum(polygamma(0, x + (1 - k)/2), (k, 1, p)) 

1320 else: 

1321 raise ArgumentIndexError(self, argindex) 

1322 

1323 @classmethod 

1324 def eval(cls, x, p): 

1325 from sympy.concrete.products import Product 

1326 if p.is_positive is False or p.is_integer is False: 

1327 raise ValueError('Order parameter p must be positive integer.') 

1328 k = Dummy("k") 

1329 return (pi**(p*(p - 1)/4)*Product(gamma(x + (1 - k)/2), 

1330 (k, 1, p))).doit() 

1331 

1332 def _eval_conjugate(self): 

1333 x, p = self.args 

1334 return self.func(x.conjugate(), p) 

1335 

1336 def _eval_is_real(self): 

1337 x, p = self.args 

1338 y = 2*x 

1339 if y.is_integer and (y <= (p - 1)) is True: 

1340 return False 

1341 if intlike(y) and (y <= (p - 1)): 

1342 return False 

1343 if y > (p - 1) or y.is_noninteger: 

1344 return True