modeling_simple / .venv /lib /python3.14 /site-packages /sympy /functions /special /gamma_functions.py
| from math import prod | |
| from sympy.core import Add, S, Dummy, expand_func | |
| from sympy.core.expr import Expr | |
| from sympy.core.function import DefinedFunction, ArgumentIndexError, PoleError | |
| from sympy.core.logic import fuzzy_and, fuzzy_not | |
| from sympy.core.numbers import Rational, pi, oo, I | |
| from sympy.core.power import Pow | |
| from sympy.functions.special.zeta_functions import zeta | |
| from sympy.functions.special.error_functions import erf, erfc, Ei | |
| from sympy.functions.elementary.complexes import re, unpolarify | |
| from sympy.functions.elementary.exponential import exp, log | |
| from sympy.functions.elementary.integers import ceiling, floor | |
| from sympy.functions.elementary.miscellaneous import sqrt | |
| from sympy.functions.elementary.trigonometric import sin, cos, cot | |
| from sympy.functions.combinatorial.numbers import bernoulli, harmonic | |
| from sympy.functions.combinatorial.factorials import factorial, rf, RisingFactorial | |
| from sympy.utilities.misc import as_int | |
| from mpmath import mp, workprec | |
| from mpmath.libmp.libmpf import prec_to_dps | |
| def intlike(n): | |
| try: | |
| as_int(n, strict=False) | |
| return True | |
| except ValueError: | |
| return False | |
| ############################################################################### | |
| ############################ COMPLETE GAMMA FUNCTION ########################## | |
| ############################################################################### | |
| class gamma(DefinedFunction): | |
| r""" | |
| The gamma function | |
| .. math:: | |
| \Gamma(x) := \int^{\infty}_{0} t^{x-1} e^{-t} \mathrm{d}t. | |
| Explanation | |
| =========== | |
| The ``gamma`` function implements the function which passes through the | |
| values of the factorial function (i.e., $\Gamma(n) = (n - 1)!$ when n is | |
| an integer). More generally, $\Gamma(z)$ is defined in the whole complex | |
| plane except at the negative integers where there are simple poles. | |
| Examples | |
| ======== | |
| >>> from sympy import S, I, pi, gamma | |
| >>> from sympy.abc import x | |
| Several special values are known: | |
| >>> gamma(1) | |
| 1 | |
| >>> gamma(4) | |
| 6 | |
| >>> gamma(S(3)/2) | |
| sqrt(pi)/2 | |
| The ``gamma`` function obeys the mirror symmetry: | |
| >>> from sympy import conjugate | |
| >>> conjugate(gamma(x)) | |
| gamma(conjugate(x)) | |
| Differentiation with respect to $x$ is supported: | |
| >>> from sympy import diff | |
| >>> diff(gamma(x), x) | |
| gamma(x)*polygamma(0, x) | |
| Series expansion is also supported: | |
| >>> from sympy import series | |
| >>> series(gamma(x), x, 0, 3) | |
| 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) | |
| We can numerically evaluate the ``gamma`` function to arbitrary precision | |
| on the whole complex plane: | |
| >>> gamma(pi).evalf(40) | |
| 2.288037795340032417959588909060233922890 | |
| >>> gamma(1+I).evalf(20) | |
| 0.49801566811835604271 - 0.15494982830181068512*I | |
| See Also | |
| ======== | |
| lowergamma: Lower incomplete gamma function. | |
| uppergamma: Upper incomplete gamma function. | |
| polygamma: Polygamma function. | |
| loggamma: Log Gamma function. | |
| digamma: Digamma function. | |
| trigamma: Trigamma function. | |
| sympy.functions.special.beta_functions.beta: Euler Beta function. | |
| References | |
| ========== | |
| .. [1] https://en.wikipedia.org/wiki/Gamma_function | |
| .. [2] https://dlmf.nist.gov/5 | |
| .. [3] https://mathworld.wolfram.com/GammaFunction.html | |
| .. [4] https://functions.wolfram.com/GammaBetaErf/Gamma/ | |
| """ | |
| unbranched = True | |
| _singularities = (S.ComplexInfinity,) | |
| def fdiff(self, argindex=1): | |
| if argindex == 1: | |
| return self.func(self.args[0])*polygamma(0, self.args[0]) | |
| else: | |
| raise ArgumentIndexError(self, argindex) | |
| def eval(cls, arg): | |
| if arg.is_Number: | |
| if arg is S.NaN: | |
| return S.NaN | |
| elif arg is oo: | |
| return oo | |
| elif intlike(arg): | |
| if arg.is_positive: | |
| return factorial(arg - 1) | |
| else: | |
| return S.ComplexInfinity | |
| elif arg.is_Rational: | |
| if arg.q == 2: | |
| n = abs(arg.p) // arg.q | |
| if arg.is_positive: | |
| k, coeff = n, S.One | |
| else: | |
| n = k = n + 1 | |
| if n & 1 == 0: | |
| coeff = S.One | |
| else: | |
| coeff = S.NegativeOne | |
| coeff *= prod(range(3, 2*k, 2)) | |
| if arg.is_positive: | |
| return coeff*sqrt(pi) / 2**n | |
| else: | |
| return 2**n*sqrt(pi) / coeff | |
| def _eval_expand_func(self, **hints): | |
| arg = self.args[0] | |
| if arg.is_Rational: | |
| if abs(arg.p) > arg.q: | |
| x = Dummy('x') | |
| n = arg.p // arg.q | |
| p = arg.p - n*arg.q | |
| return self.func(x + n)._eval_expand_func().subs(x, Rational(p, arg.q)) | |
| if arg.is_Add: | |
| coeff, tail = arg.as_coeff_add() | |
| if coeff and coeff.q != 1: | |
| intpart = floor(coeff) | |
| tail = (coeff - intpart,) + tail | |
| coeff = intpart | |
| tail = arg._new_rawargs(*tail, reeval=False) | |
| return self.func(tail)*RisingFactorial(tail, coeff) | |
| return self.func(*self.args) | |
| def _eval_conjugate(self): | |
| return self.func(self.args[0].conjugate()) | |
| def _eval_is_real(self): | |
| x = self.args[0] | |
| if x.is_nonpositive and x.is_integer: | |
| return False | |
| if intlike(x) and x <= 0: | |
| return False | |
| if x.is_positive or x.is_noninteger: | |
| return True | |
| def _eval_is_positive(self): | |
| x = self.args[0] | |
| if x.is_positive: | |
| return True | |
| elif x.is_noninteger: | |
| return floor(x).is_even | |
| def _eval_rewrite_as_tractable(self, z, limitvar=None, **kwargs): | |
| return exp(loggamma(z)) | |
| def _eval_rewrite_as_factorial(self, z, **kwargs): | |
| return factorial(z - 1) | |
| def _eval_nseries(self, x, n, logx, cdir=0): | |
| x0 = self.args[0].limit(x, 0) | |
| if not (x0.is_Integer and x0 <= 0): | |
| return super()._eval_nseries(x, n, logx) | |
| t = self.args[0] - x0 | |
| return (self.func(t + 1)/rf(self.args[0], -x0 + 1))._eval_nseries(x, n, logx) | |
| def _eval_as_leading_term(self, x, logx, cdir): | |
| arg = self.args[0] | |
| x0 = arg.subs(x, 0) | |
| if x0.is_integer and x0.is_nonpositive: | |
| n = -x0 | |
| res = S.NegativeOne**n/self.func(n + 1) | |
| return res/(arg + n).as_leading_term(x) | |
| elif not x0.is_infinite: | |
| return self.func(x0) | |
| raise PoleError() | |
| ############################################################################### | |
| ################## LOWER and UPPER INCOMPLETE GAMMA FUNCTIONS ################# | |
| ############################################################################### | |
| class lowergamma(DefinedFunction): | |
| r""" | |
| The lower incomplete gamma function. | |
| Explanation | |
| =========== | |
| It can be defined as the meromorphic continuation of | |
| .. math:: | |
| \gamma(s, x) := \int_0^x t^{s-1} e^{-t} \mathrm{d}t = \Gamma(s) - \Gamma(s, x). | |
| This can be shown to be the same as | |
| .. math:: | |
| \gamma(s, x) = \frac{x^s}{s} {}_1F_1\left({s \atop s+1} \middle| -x\right), | |
| where ${}_1F_1$ is the (confluent) hypergeometric function. | |
| Examples | |
| ======== | |
| >>> from sympy import lowergamma, S | |
| >>> from sympy.abc import s, x | |
| >>> lowergamma(s, x) | |
| lowergamma(s, x) | |
| >>> lowergamma(3, x) | |
| -2*(x**2/2 + x + 1)*exp(-x) + 2 | |
| >>> lowergamma(-S(1)/2, x) | |
| -2*sqrt(pi)*erf(sqrt(x)) - 2*exp(-x)/sqrt(x) | |
| See Also | |
| ======== | |
| gamma: Gamma function. | |
| uppergamma: Upper incomplete gamma function. | |
| polygamma: Polygamma function. | |
| loggamma: Log Gamma function. | |
| digamma: Digamma function. | |
| trigamma: Trigamma function. | |
| sympy.functions.special.beta_functions.beta: Euler Beta function. | |
| References | |
| ========== | |
| .. [1] https://en.wikipedia.org/wiki/Incomplete_gamma_function#Lower_incomplete_gamma_function | |
| .. [2] Abramowitz, Milton; Stegun, Irene A., eds. (1965), Chapter 6, | |
| Section 5, Handbook of Mathematical Functions with Formulas, Graphs, | |
| and Mathematical Tables | |
| .. [3] https://dlmf.nist.gov/8 | |
| .. [4] https://functions.wolfram.com/GammaBetaErf/Gamma2/ | |
| .. [5] https://functions.wolfram.com/GammaBetaErf/Gamma3/ | |
| """ | |
| def fdiff(self, argindex=2): | |
| from sympy.functions.special.hyper import meijerg | |
| if argindex == 2: | |
| a, z = self.args | |
| return exp(-unpolarify(z))*z**(a - 1) | |
| elif argindex == 1: | |
| a, z = self.args | |
| return gamma(a)*digamma(a) - log(z)*uppergamma(a, z) \ | |
| - meijerg([], [1, 1], [0, 0, a], [], z) | |
| else: | |
| raise ArgumentIndexError(self, argindex) | |
| def eval(cls, a, x): | |
| # For lack of a better place, we use this one to extract branching | |
| # information. The following can be | |
| # found in the literature (c/f references given above), albeit scattered: | |
| # 1) For fixed x != 0, lowergamma(s, x) is an entire function of s | |
| # 2) For fixed positive integers s, lowergamma(s, x) is an entire | |
| # function of x. | |
| # 3) For fixed non-positive integers s, | |
| # lowergamma(s, exp(I*2*pi*n)*x) = | |
| # 2*pi*I*n*(-1)**(-s)/factorial(-s) + lowergamma(s, x) | |
| # (this follows from lowergamma(s, x).diff(x) = x**(s-1)*exp(-x)). | |
| # 4) For fixed non-integral s, | |
| # lowergamma(s, x) = x**s*gamma(s)*lowergamma_unbranched(s, x), | |
| # where lowergamma_unbranched(s, x) is an entire function (in fact | |
| # of both s and x), i.e. | |
| # lowergamma(s, exp(2*I*pi*n)*x) = exp(2*pi*I*n*a)*lowergamma(a, x) | |
| if x is S.Zero: | |
| return S.Zero | |
| nx, n = x.extract_branch_factor() | |
| if a.is_integer and a.is_positive: | |
| nx = unpolarify(x) | |
| if nx != x: | |
| return lowergamma(a, nx) | |
| elif a.is_integer and a.is_nonpositive: | |
| if n != 0: | |
| return 2*pi*I*n*S.NegativeOne**(-a)/factorial(-a) + lowergamma(a, nx) | |
| elif n != 0: | |
| return exp(2*pi*I*n*a)*lowergamma(a, nx) | |
| # Special values. | |
| if a.is_Number: | |
| if a is S.One: | |
| return S.One - exp(-x) | |
| elif a is S.Half: | |
| return sqrt(pi)*erf(sqrt(x)) | |
| elif a.is_Integer or (2*a).is_Integer: | |
| b = a - 1 | |
| if b.is_positive: | |
| if a.is_integer: | |
| return factorial(b) - exp(-x) * factorial(b) * Add(*[x ** k / factorial(k) for k in range(a)]) | |
| else: | |
| 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)])) | |
| if not a.is_Integer: | |
| 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)]) | |
| if x.is_zero: | |
| return S.Zero | |
| def _eval_evalf(self, prec): | |
| if all(x.is_number for x in self.args): | |
| a = self.args[0]._to_mpmath(prec) | |
| z = self.args[1]._to_mpmath(prec) | |
| with workprec(prec): | |
| res = mp.gammainc(a, 0, z) | |
| return Expr._from_mpmath(res, prec) | |
| else: | |
| return self | |
| def _eval_conjugate(self): | |
| x = self.args[1] | |
| if x not in (S.Zero, S.NegativeInfinity): | |
| return self.func(self.args[0].conjugate(), x.conjugate()) | |
| def _eval_is_meromorphic(self, x, a): | |
| # By https://en.wikipedia.org/wiki/Incomplete_gamma_function#Holomorphic_extension, | |
| # lowergamma(s, z) = z**s*gamma(s)*gammastar(s, z), | |
| # where gammastar(s, z) is holomorphic for all s and z. | |
| # Hence the singularities of lowergamma are z = 0 (branch | |
| # point) and nonpositive integer values of s (poles of gamma(s)). | |
| s, z = self.args | |
| args_merom = fuzzy_and([z._eval_is_meromorphic(x, a), | |
| s._eval_is_meromorphic(x, a)]) | |
| if not args_merom: | |
| return args_merom | |
| z0 = z.subs(x, a) | |
| if s.is_integer: | |
| return fuzzy_and([s.is_positive, z0.is_finite]) | |
| s0 = s.subs(x, a) | |
| return fuzzy_and([s0.is_finite, z0.is_finite, fuzzy_not(z0.is_zero)]) | |
| def _eval_aseries(self, n, args0, x, logx): | |
| from sympy.series.order import O | |
| s, z = self.args | |
| if args0[0] is oo and not z.has(x): | |
| coeff = z**s*exp(-z) | |
| sum_expr = sum(z**k/rf(s, k + 1) for k in range(n - 1)) | |
| o = O(z**s*s**(-n)) | |
| return coeff*sum_expr + o | |
| return super()._eval_aseries(n, args0, x, logx) | |
| def _eval_rewrite_as_uppergamma(self, s, x, **kwargs): | |
| return gamma(s) - uppergamma(s, x) | |
| def _eval_rewrite_as_expint(self, s, x, **kwargs): | |
| from sympy.functions.special.error_functions import expint | |
| if s.is_integer and s.is_nonpositive: | |
| return self | |
| return self.rewrite(uppergamma).rewrite(expint) | |
| def _eval_is_zero(self): | |
| x = self.args[1] | |
| if x.is_zero: | |
| return True | |
| class uppergamma(DefinedFunction): | |
| r""" | |
| The upper incomplete gamma function. | |
| Explanation | |
| =========== | |
| It can be defined as the meromorphic continuation of | |
| .. math:: | |
| \Gamma(s, x) := \int_x^\infty t^{s-1} e^{-t} \mathrm{d}t = \Gamma(s) - \gamma(s, x). | |
| where $\gamma(s, x)$ is the lower incomplete gamma function, | |
| :class:`lowergamma`. This can be shown to be the same as | |
| .. math:: | |
| \Gamma(s, x) = \Gamma(s) - \frac{x^s}{s} {}_1F_1\left({s \atop s+1} \middle| -x\right), | |
| where ${}_1F_1$ is the (confluent) hypergeometric function. | |
| The upper incomplete gamma function is also essentially equivalent to the | |
| generalized exponential integral: | |
| .. math:: | |
| \operatorname{E}_{n}(x) = \int_{1}^{\infty}{\frac{e^{-xt}}{t^n} \, dt} = x^{n-1}\Gamma(1-n,x). | |
| Examples | |
| ======== | |
| >>> from sympy import uppergamma, S | |
| >>> from sympy.abc import s, x | |
| >>> uppergamma(s, x) | |
| uppergamma(s, x) | |
| >>> uppergamma(3, x) | |
| 2*(x**2/2 + x + 1)*exp(-x) | |
| >>> uppergamma(-S(1)/2, x) | |
| -2*sqrt(pi)*erfc(sqrt(x)) + 2*exp(-x)/sqrt(x) | |
| >>> uppergamma(-2, x) | |
| expint(3, x)/x**2 | |
| See Also | |
| ======== | |
| gamma: Gamma function. | |
| lowergamma: Lower incomplete gamma function. | |
| polygamma: Polygamma function. | |
| loggamma: Log Gamma function. | |
| digamma: Digamma function. | |
| trigamma: Trigamma function. | |
| sympy.functions.special.beta_functions.beta: Euler Beta function. | |
| References | |
| ========== | |
| .. [1] https://en.wikipedia.org/wiki/Incomplete_gamma_function#Upper_incomplete_gamma_function | |
| .. [2] Abramowitz, Milton; Stegun, Irene A., eds. (1965), Chapter 6, | |
| Section 5, Handbook of Mathematical Functions with Formulas, Graphs, | |
| and Mathematical Tables | |
| .. [3] https://dlmf.nist.gov/8 | |
| .. [4] https://functions.wolfram.com/GammaBetaErf/Gamma2/ | |
| .. [5] https://functions.wolfram.com/GammaBetaErf/Gamma3/ | |
| .. [6] https://en.wikipedia.org/wiki/Exponential_integral#Relation_with_other_functions | |
| """ | |
| def fdiff(self, argindex=2): | |
| from sympy.functions.special.hyper import meijerg | |
| if argindex == 2: | |
| a, z = self.args | |
| return -exp(-unpolarify(z))*z**(a - 1) | |
| elif argindex == 1: | |
| a, z = self.args | |
| return uppergamma(a, z)*log(z) + meijerg([], [1, 1], [0, 0, a], [], z) | |
| else: | |
| raise ArgumentIndexError(self, argindex) | |
| def _eval_evalf(self, prec): | |
| if all(x.is_number for x in self.args): | |
| a = self.args[0]._to_mpmath(prec) | |
| z = self.args[1]._to_mpmath(prec) | |
| with workprec(prec): | |
| res = mp.gammainc(a, z, mp.inf) | |
| return Expr._from_mpmath(res, prec) | |
| return self | |
| def eval(cls, a, z): | |
| from sympy.functions.special.error_functions import expint | |
| if z.is_Number: | |
| if z is S.NaN: | |
| return S.NaN | |
| elif z is oo: | |
| return S.Zero | |
| elif z.is_zero: | |
| if re(a).is_positive: | |
| return gamma(a) | |
| # We extract branching information here. C/f lowergamma. | |
| nx, n = z.extract_branch_factor() | |
| if a.is_integer and a.is_positive: | |
| nx = unpolarify(z) | |
| if z != nx: | |
| return uppergamma(a, nx) | |
| elif a.is_integer and a.is_nonpositive: | |
| if n != 0: | |
| return -2*pi*I*n*S.NegativeOne**(-a)/factorial(-a) + uppergamma(a, nx) | |
| elif n != 0: | |
| return gamma(a)*(1 - exp(2*pi*I*n*a)) + exp(2*pi*I*n*a)*uppergamma(a, nx) | |
| # Special values. | |
| if a.is_Number: | |
| if a is S.Zero and z.is_positive: | |
| return -Ei(-z) | |
| elif a is S.One: | |
| return exp(-z) | |
| elif a is S.Half: | |
| return sqrt(pi)*erfc(sqrt(z)) | |
| elif a.is_Integer or (2*a).is_Integer: | |
| b = a - 1 | |
| if b.is_positive: | |
| if a.is_integer: | |
| return exp(-z) * factorial(b) * Add(*[z**k / factorial(k) | |
| for k in range(a)]) | |
| else: | |
| return (gamma(a) * erfc(sqrt(z)) + | |
| S.NegativeOne**(a - S(3)/2) * exp(-z) * sqrt(z) | |
| * Add(*[gamma(-S.Half - k) * (-z)**k / gamma(1-a) | |
| for k in range(a - S.Half)])) | |
| elif b.is_Integer: | |
| return expint(-b, z)*unpolarify(z)**(b + 1) | |
| if not a.is_Integer: | |
| return (S.NegativeOne**(S.Half - a) * pi*erfc(sqrt(z))/gamma(1-a) | |
| - z**a * exp(-z) * Add(*[z**k * gamma(a) / gamma(a+k+1) | |
| for k in range(S.Half - a)])) | |
| if a.is_zero and z.is_positive: | |
| return -Ei(-z) | |
| if z.is_zero and re(a).is_positive: | |
| return gamma(a) | |
| def _eval_conjugate(self): | |
| z = self.args[1] | |
| if z not in (S.Zero, S.NegativeInfinity): | |
| return self.func(self.args[0].conjugate(), z.conjugate()) | |
| def _eval_is_meromorphic(self, x, a): | |
| return lowergamma._eval_is_meromorphic(self, x, a) | |
| def _eval_rewrite_as_lowergamma(self, s, x, **kwargs): | |
| return gamma(s) - lowergamma(s, x) | |
| def _eval_rewrite_as_tractable(self, s, x, **kwargs): | |
| return exp(loggamma(s)) - lowergamma(s, x) | |
| def _eval_rewrite_as_expint(self, s, x, **kwargs): | |
| from sympy.functions.special.error_functions import expint | |
| return expint(1 - s, x)*x**s | |
| ############################################################################### | |
| ###################### POLYGAMMA and LOGGAMMA FUNCTIONS ####################### | |
| ############################################################################### | |
| class polygamma(DefinedFunction): | |
| r""" | |
| The function ``polygamma(n, z)`` returns ``log(gamma(z)).diff(n + 1)``. | |
| Explanation | |
| =========== | |
| It is a meromorphic function on $\mathbb{C}$ and defined as the $(n+1)$-th | |
| derivative of the logarithm of the gamma function: | |
| .. math:: | |
| \psi^{(n)} (z) := \frac{\mathrm{d}^{n+1}}{\mathrm{d} z^{n+1}} \log\Gamma(z). | |
| For `n` not a nonnegative integer the generalization by Espinosa and Moll [5]_ | |
| is used: | |
| .. math:: \psi(s,z) = \frac{\zeta'(s+1, z) + (\gamma + \psi(-s)) \zeta(s+1, z)} | |
| {\Gamma(-s)} | |
| Examples | |
| ======== | |
| Several special values are known: | |
| >>> from sympy import S, polygamma | |
| >>> polygamma(0, 1) | |
| -EulerGamma | |
| >>> polygamma(0, 1/S(2)) | |
| -2*log(2) - EulerGamma | |
| >>> polygamma(0, 1/S(3)) | |
| -log(3) - sqrt(3)*pi/6 - EulerGamma - log(sqrt(3)) | |
| >>> polygamma(0, 1/S(4)) | |
| -pi/2 - log(4) - log(2) - EulerGamma | |
| >>> polygamma(0, 2) | |
| 1 - EulerGamma | |
| >>> polygamma(0, 23) | |
| 19093197/5173168 - EulerGamma | |
| >>> from sympy import oo, I | |
| >>> polygamma(0, oo) | |
| oo | |
| >>> polygamma(0, -oo) | |
| oo | |
| >>> polygamma(0, I*oo) | |
| oo | |
| >>> polygamma(0, -I*oo) | |
| oo | |
| Differentiation with respect to $x$ is supported: | |
| >>> from sympy import Symbol, diff | |
| >>> x = Symbol("x") | |
| >>> diff(polygamma(0, x), x) | |
| polygamma(1, x) | |
| >>> diff(polygamma(0, x), x, 2) | |
| polygamma(2, x) | |
| >>> diff(polygamma(0, x), x, 3) | |
| polygamma(3, x) | |
| >>> diff(polygamma(1, x), x) | |
| polygamma(2, x) | |
| >>> diff(polygamma(1, x), x, 2) | |
| polygamma(3, x) | |
| >>> diff(polygamma(2, x), x) | |
| polygamma(3, x) | |
| >>> diff(polygamma(2, x), x, 2) | |
| polygamma(4, x) | |
| >>> n = Symbol("n") | |
| >>> diff(polygamma(n, x), x) | |
| polygamma(n + 1, x) | |
| >>> diff(polygamma(n, x), x, 2) | |
| polygamma(n + 2, x) | |
| We can rewrite ``polygamma`` functions in terms of harmonic numbers: | |
| >>> from sympy import harmonic | |
| >>> polygamma(0, x).rewrite(harmonic) | |
| harmonic(x - 1) - EulerGamma | |
| >>> polygamma(2, x).rewrite(harmonic) | |
| 2*harmonic(x - 1, 3) - 2*zeta(3) | |
| >>> ni = Symbol("n", integer=True) | |
| >>> polygamma(ni, x).rewrite(harmonic) | |
| (-1)**(n + 1)*(-harmonic(x - 1, n + 1) + zeta(n + 1))*factorial(n) | |
| See Also | |
| ======== | |
| gamma: Gamma function. | |
| lowergamma: Lower incomplete gamma function. | |
| uppergamma: Upper incomplete gamma function. | |
| loggamma: Log Gamma function. | |
| digamma: Digamma function. | |
| trigamma: Trigamma function. | |
| sympy.functions.special.beta_functions.beta: Euler Beta function. | |
| References | |
| ========== | |
| .. [1] https://en.wikipedia.org/wiki/Polygamma_function | |
| .. [2] https://mathworld.wolfram.com/PolygammaFunction.html | |
| .. [3] https://functions.wolfram.com/GammaBetaErf/PolyGamma/ | |
| .. [4] https://functions.wolfram.com/GammaBetaErf/PolyGamma2/ | |
| .. [5] O. Espinosa and V. Moll, "A generalized polygamma function", | |
| *Integral Transforms and Special Functions* (2004), 101-115. | |
| """ | |
| def eval(cls, n, z): | |
| if n is S.NaN or z is S.NaN: | |
| return S.NaN | |
| elif z is oo: | |
| return oo if n.is_zero else S.Zero | |
| elif z.is_Integer and z.is_nonpositive: | |
| return S.ComplexInfinity | |
| elif n is S.NegativeOne: | |
| return loggamma(z) - log(2*pi) / 2 | |
| elif n.is_zero: | |
| if z is -oo or z.extract_multiplicatively(I) in (oo, -oo): | |
| return oo | |
| elif z.is_Integer: | |
| return harmonic(z-1) - S.EulerGamma | |
| elif z.is_Rational: | |
| # TODO n == 1 also can do some rational z | |
| p, q = z.as_numer_denom() | |
| # only expand for small denominators to avoid creating long expressions | |
| if q <= 6: | |
| return expand_func(polygamma(S.Zero, z, evaluate=False)) | |
| elif n.is_integer and n.is_nonnegative: | |
| nz = unpolarify(z) | |
| if z != nz: | |
| return polygamma(n, nz) | |
| if z.is_Integer: | |
| return S.NegativeOne**(n+1) * factorial(n) * zeta(n+1, z) | |
| elif z is S.Half: | |
| return S.NegativeOne**(n+1) * factorial(n) * (2**(n+1)-1) * zeta(n+1) | |
| def _eval_is_real(self): | |
| if self.args[0].is_positive and self.args[1].is_positive: | |
| return True | |
| def _eval_is_complex(self): | |
| z = self.args[1] | |
| is_negative_integer = fuzzy_and([z.is_negative, z.is_integer]) | |
| return fuzzy_and([z.is_complex, fuzzy_not(is_negative_integer)]) | |
| def _eval_is_positive(self): | |
| n, z = self.args | |
| if n.is_positive: | |
| if n.is_odd and z.is_real: | |
| return True | |
| if n.is_even and z.is_positive: | |
| return False | |
| def _eval_is_negative(self): | |
| n, z = self.args | |
| if n.is_positive: | |
| if n.is_even and z.is_positive: | |
| return True | |
| if n.is_odd and z.is_real: | |
| return False | |
| def _eval_expand_func(self, **hints): | |
| n, z = self.args | |
| if n.is_Integer and n.is_nonnegative: | |
| if z.is_Add: | |
| coeff = z.args[0] | |
| if coeff.is_Integer: | |
| e = -(n + 1) | |
| if coeff > 0: | |
| tail = Add(*[Pow( | |
| z - i, e) for i in range(1, int(coeff) + 1)]) | |
| else: | |
| tail = -Add(*[Pow( | |
| z + i, e) for i in range(int(-coeff))]) | |
| return polygamma(n, z - coeff) + S.NegativeOne**n*factorial(n)*tail | |
| elif z.is_Mul: | |
| coeff, z = z.as_two_terms() | |
| if coeff.is_Integer and coeff.is_positive: | |
| tail = [polygamma(n, z + Rational( | |
| i, coeff)) for i in range(int(coeff))] | |
| if n == 0: | |
| return Add(*tail)/coeff + log(coeff) | |
| else: | |
| return Add(*tail)/coeff**(n + 1) | |
| z *= coeff | |
| if n == 0 and z.is_Rational: | |
| p, q = z.as_numer_denom() | |
| # Reference: | |
| # Values of the polygamma functions at rational arguments, J. Choi, 2007 | |
| part_1 = -S.EulerGamma - pi * cot(p * pi / q) / 2 - log(q) + Add( | |
| *[cos(2 * k * pi * p / q) * log(2 * sin(k * pi / q)) for k in range(1, q)]) | |
| if z > 0: | |
| n = floor(z) | |
| z0 = z - n | |
| return part_1 + Add(*[1 / (z0 + k) for k in range(n)]) | |
| elif z < 0: | |
| n = floor(1 - z) | |
| z0 = z + n | |
| return part_1 - Add(*[1 / (z0 - 1 - k) for k in range(n)]) | |
| if n == -1: | |
| return loggamma(z) - log(2*pi) / 2 | |
| if n.is_integer is False or n.is_nonnegative is False: | |
| s = Dummy("s") | |
| dzt = zeta(s, z).diff(s).subs(s, n+1) | |
| return (dzt + (S.EulerGamma + digamma(-n)) * zeta(n+1, z)) / gamma(-n) | |
| return polygamma(n, z) | |
| def _eval_rewrite_as_zeta(self, n, z, **kwargs): | |
| if n.is_integer and n.is_positive: | |
| return S.NegativeOne**(n + 1)*factorial(n)*zeta(n + 1, z) | |
| def _eval_rewrite_as_harmonic(self, n, z, **kwargs): | |
| if n.is_integer: | |
| if n.is_zero: | |
| return harmonic(z - 1) - S.EulerGamma | |
| else: | |
| return S.NegativeOne**(n+1) * factorial(n) * (zeta(n+1) - harmonic(z-1, n+1)) | |
| def _eval_as_leading_term(self, x, logx, cdir): | |
| from sympy.series.order import Order | |
| n, z = [a.as_leading_term(x) for a in self.args] | |
| o = Order(z, x) | |
| if n == 0 and o.contains(1/x): | |
| logx = log(x) if logx is None else logx | |
| return o.getn() * logx | |
| else: | |
| return self.func(n, z) | |
| def fdiff(self, argindex=2): | |
| if argindex == 2: | |
| n, z = self.args[:2] | |
| return polygamma(n + 1, z) | |
| else: | |
| raise ArgumentIndexError(self, argindex) | |
| def _eval_aseries(self, n, args0, x, logx): | |
| from sympy.series.order import Order | |
| if args0[1] != oo or not \ | |
| (self.args[0].is_Integer and self.args[0].is_nonnegative): | |
| return super()._eval_aseries(n, args0, x, logx) | |
| z = self.args[1] | |
| N = self.args[0] | |
| if N == 0: | |
| # digamma function series | |
| # Abramowitz & Stegun, p. 259, 6.3.18 | |
| r = log(z) - 1/(2*z) | |
| o = None | |
| if n < 2: | |
| o = Order(1/z, x) | |
| else: | |
| m = ceiling((n + 1)//2) | |
| l = [bernoulli(2*k) / (2*k*z**(2*k)) for k in range(1, m)] | |
| r -= Add(*l) | |
| o = Order(1/z**n, x) | |
| return r._eval_nseries(x, n, logx) + o | |
| else: | |
| # proper polygamma function | |
| # Abramowitz & Stegun, p. 260, 6.4.10 | |
| # We return terms to order higher than O(x**n) on purpose | |
| # -- otherwise we would not be able to return any terms for | |
| # quite a long time! | |
| fac = gamma(N) | |
| e0 = fac + N*fac/(2*z) | |
| m = ceiling((n + 1)//2) | |
| for k in range(1, m): | |
| fac = fac*(2*k + N - 1)*(2*k + N - 2) / ((2*k)*(2*k - 1)) | |
| e0 += bernoulli(2*k)*fac/z**(2*k) | |
| o = Order(1/z**(2*m), x) | |
| if n == 0: | |
| o = Order(1/z, x) | |
| elif n == 1: | |
| o = Order(1/z**2, x) | |
| r = e0._eval_nseries(z, n, logx) + o | |
| return (-1 * (-1/z)**N * r)._eval_nseries(x, n, logx) | |
| def _eval_evalf(self, prec): | |
| if not all(i.is_number for i in self.args): | |
| return | |
| s = self.args[0]._to_mpmath(prec+12) | |
| z = self.args[1]._to_mpmath(prec+12) | |
| if mp.isint(z) and z <= 0: | |
| return S.ComplexInfinity | |
| with workprec(prec+12): | |
| if mp.isint(s) and s >= 0: | |
| res = mp.polygamma(s, z) | |
| else: | |
| zt = mp.zeta(s+1, z) | |
| dzt = mp.zeta(s+1, z, 1) | |
| res = (dzt + (mp.euler + mp.digamma(-s)) * zt) * mp.rgamma(-s) | |
| return Expr._from_mpmath(res, prec) | |
| class loggamma(DefinedFunction): | |
| r""" | |
| The ``loggamma`` function implements the logarithm of the | |
| gamma function (i.e., $\log\Gamma(x)$). | |
| Examples | |
| ======== | |
| Several special values are known. For numerical integral | |
| arguments we have: | |
| >>> from sympy import loggamma | |
| >>> loggamma(-2) | |
| oo | |
| >>> loggamma(0) | |
| oo | |
| >>> loggamma(1) | |
| 0 | |
| >>> loggamma(2) | |
| 0 | |
| >>> loggamma(3) | |
| log(2) | |
| And for symbolic values: | |
| >>> from sympy import Symbol | |
| >>> n = Symbol("n", integer=True, positive=True) | |
| >>> loggamma(n) | |
| log(gamma(n)) | |
| >>> loggamma(-n) | |
| oo | |
| For half-integral values: | |
| >>> from sympy import S | |
| >>> loggamma(S(5)/2) | |
| log(3*sqrt(pi)/4) | |
| >>> loggamma(n/2) | |
| log(2**(1 - n)*sqrt(pi)*gamma(n)/gamma(n/2 + 1/2)) | |
| And general rational arguments: | |
| >>> from sympy import expand_func | |
| >>> L = loggamma(S(16)/3) | |
| >>> expand_func(L).doit() | |
| -5*log(3) + loggamma(1/3) + log(4) + log(7) + log(10) + log(13) | |
| >>> L = loggamma(S(19)/4) | |
| >>> expand_func(L).doit() | |
| -4*log(4) + loggamma(3/4) + log(3) + log(7) + log(11) + log(15) | |
| >>> L = loggamma(S(23)/7) | |
| >>> expand_func(L).doit() | |
| -3*log(7) + log(2) + loggamma(2/7) + log(9) + log(16) | |
| The ``loggamma`` function has the following limits towards infinity: | |
| >>> from sympy import oo | |
| >>> loggamma(oo) | |
| oo | |
| >>> loggamma(-oo) | |
| zoo | |
| The ``loggamma`` function obeys the mirror symmetry | |
| if $x \in \mathbb{C} \setminus \{-\infty, 0\}$: | |
| >>> from sympy.abc import x | |
| >>> from sympy import conjugate | |
| >>> conjugate(loggamma(x)) | |
| loggamma(conjugate(x)) | |
| Differentiation with respect to $x$ is supported: | |
| >>> from sympy import diff | |
| >>> diff(loggamma(x), x) | |
| polygamma(0, x) | |
| Series expansion is also supported: | |
| >>> from sympy import series | |
| >>> series(loggamma(x), x, 0, 4).cancel() | |
| -log(x) - EulerGamma*x + pi**2*x**2/12 - x**3*zeta(3)/3 + O(x**4) | |
| We can numerically evaluate the ``loggamma`` function | |
| to arbitrary precision on the whole complex plane: | |
| >>> from sympy import I | |
| >>> loggamma(5).evalf(30) | |
| 3.17805383034794561964694160130 | |
| >>> loggamma(I).evalf(20) | |
| -0.65092319930185633889 - 1.8724366472624298171*I | |
| See Also | |
| ======== | |
| gamma: Gamma function. | |
| lowergamma: Lower incomplete gamma function. | |
| uppergamma: Upper incomplete gamma function. | |
| polygamma: Polygamma function. | |
| digamma: Digamma function. | |
| trigamma: Trigamma function. | |
| sympy.functions.special.beta_functions.beta: Euler Beta function. | |
| References | |
| ========== | |
| .. [1] https://en.wikipedia.org/wiki/Gamma_function | |
| .. [2] https://dlmf.nist.gov/5 | |
| .. [3] https://mathworld.wolfram.com/LogGammaFunction.html | |
| .. [4] https://functions.wolfram.com/GammaBetaErf/LogGamma/ | |
| """ | |
| def eval(cls, z): | |
| if z.is_integer: | |
| if z.is_nonpositive: | |
| return oo | |
| elif z.is_positive: | |
| return log(gamma(z)) | |
| elif z.is_rational: | |
| p, q = z.as_numer_denom() | |
| # Half-integral values: | |
| if p.is_positive and q == 2: | |
| return log(sqrt(pi) * 2**(1 - p) * gamma(p) / gamma((p + 1)*S.Half)) | |
| if z is oo: | |
| return oo | |
| elif abs(z) is oo: | |
| return S.ComplexInfinity | |
| if z is S.NaN: | |
| return S.NaN | |
| def _eval_expand_func(self, **hints): | |
| from sympy.concrete.summations import Sum | |
| z = self.args[0] | |
| if z.is_Rational: | |
| p, q = z.as_numer_denom() | |
| # General rational arguments (u + p/q) | |
| # Split z as n + p/q with p < q | |
| n = p // q | |
| p = p - n*q | |
| if p.is_positive and q.is_positive and p < q: | |
| k = Dummy("k") | |
| if n.is_positive: | |
| return loggamma(p / q) - n*log(q) + Sum(log((k - 1)*q + p), (k, 1, n)) | |
| elif n.is_negative: | |
| return loggamma(p / q) - n*log(q) + pi*I*n - Sum(log(k*q - p), (k, 1, -n)) | |
| elif n.is_zero: | |
| return loggamma(p / q) | |
| return self | |
| def _eval_nseries(self, x, n, logx=None, cdir=0): | |
| x0 = self.args[0].limit(x, 0) | |
| if x0.is_zero: | |
| f = self._eval_rewrite_as_intractable(*self.args) | |
| return f._eval_nseries(x, n, logx) | |
| return super()._eval_nseries(x, n, logx) | |
| def _eval_aseries(self, n, args0, x, logx): | |
| from sympy.series.order import Order | |
| if args0[0] != oo: | |
| return super()._eval_aseries(n, args0, x, logx) | |
| z = self.args[0] | |
| r = log(z)*(z - S.Half) - z + log(2*pi)/2 | |
| l = [bernoulli(2*k) / (2*k*(2*k - 1)*z**(2*k - 1)) for k in range(1, n)] | |
| o = None | |
| if n == 0: | |
| o = Order(1, x) | |
| else: | |
| o = Order(1/z**n, x) | |
| # It is very inefficient to first add the order and then do the nseries | |
| return (r + Add(*l))._eval_nseries(x, n, logx) + o | |
| def _eval_rewrite_as_intractable(self, z, **kwargs): | |
| return log(gamma(z)) | |
| def _eval_is_real(self): | |
| z = self.args[0] | |
| if z.is_positive: | |
| return True | |
| elif z.is_nonpositive: | |
| return False | |
| def _eval_conjugate(self): | |
| z = self.args[0] | |
| if z not in (S.Zero, S.NegativeInfinity): | |
| return self.func(z.conjugate()) | |
| def fdiff(self, argindex=1): | |
| if argindex == 1: | |
| return polygamma(0, self.args[0]) | |
| else: | |
| raise ArgumentIndexError(self, argindex) | |
| class digamma(DefinedFunction): | |
| r""" | |
| The ``digamma`` function is the first derivative of the ``loggamma`` | |
| function | |
| .. math:: | |
| \psi(x) := \frac{\mathrm{d}}{\mathrm{d} z} \log\Gamma(z) | |
| = \frac{\Gamma'(z)}{\Gamma(z) }. | |
| In this case, ``digamma(z) = polygamma(0, z)``. | |
| Examples | |
| ======== | |
| >>> from sympy import digamma | |
| >>> digamma(0) | |
| zoo | |
| >>> from sympy import Symbol | |
| >>> z = Symbol('z') | |
| >>> digamma(z) | |
| polygamma(0, z) | |
| To retain ``digamma`` as it is: | |
| >>> digamma(0, evaluate=False) | |
| digamma(0) | |
| >>> digamma(z, evaluate=False) | |
| digamma(z) | |
| See Also | |
| ======== | |
| gamma: Gamma function. | |
| lowergamma: Lower incomplete gamma function. | |
| uppergamma: Upper incomplete gamma function. | |
| polygamma: Polygamma function. | |
| loggamma: Log Gamma function. | |
| trigamma: Trigamma function. | |
| sympy.functions.special.beta_functions.beta: Euler Beta function. | |
| References | |
| ========== | |
| .. [1] https://en.wikipedia.org/wiki/Digamma_function | |
| .. [2] https://mathworld.wolfram.com/DigammaFunction.html | |
| .. [3] https://functions.wolfram.com/GammaBetaErf/PolyGamma2/ | |
| """ | |
| def _eval_evalf(self, prec): | |
| z = self.args[0] | |
| nprec = prec_to_dps(prec) | |
| return polygamma(0, z).evalf(n=nprec) | |
| def fdiff(self, argindex=1): | |
| z = self.args[0] | |
| return polygamma(0, z).fdiff() | |
| def _eval_is_real(self): | |
| z = self.args[0] | |
| return polygamma(0, z).is_real | |
| def _eval_is_positive(self): | |
| z = self.args[0] | |
| return polygamma(0, z).is_positive | |
| def _eval_is_negative(self): | |
| z = self.args[0] | |
| return polygamma(0, z).is_negative | |
| def _eval_aseries(self, n, args0, x, logx): | |
| as_polygamma = self.rewrite(polygamma) | |
| args0 = [S.Zero,] + args0 | |
| return as_polygamma._eval_aseries(n, args0, x, logx) | |
| def eval(cls, z): | |
| return polygamma(0, z) | |
| def _eval_expand_func(self, **hints): | |
| z = self.args[0] | |
| return polygamma(0, z).expand(func=True) | |
| def _eval_rewrite_as_harmonic(self, z, **kwargs): | |
| return harmonic(z - 1) - S.EulerGamma | |
| def _eval_rewrite_as_polygamma(self, z, **kwargs): | |
| return polygamma(0, z) | |
| def _eval_as_leading_term(self, x, logx, cdir): | |
| z = self.args[0] | |
| return polygamma(0, z).as_leading_term(x) | |
| class trigamma(DefinedFunction): | |
| r""" | |
| The ``trigamma`` function is the second derivative of the ``loggamma`` | |
| function | |
| .. math:: | |
| \psi^{(1)}(z) := \frac{\mathrm{d}^{2}}{\mathrm{d} z^{2}} \log\Gamma(z). | |
| In this case, ``trigamma(z) = polygamma(1, z)``. | |
| Examples | |
| ======== | |
| >>> from sympy import trigamma | |
| >>> trigamma(0) | |
| zoo | |
| >>> from sympy import Symbol | |
| >>> z = Symbol('z') | |
| >>> trigamma(z) | |
| polygamma(1, z) | |
| To retain ``trigamma`` as it is: | |
| >>> trigamma(0, evaluate=False) | |
| trigamma(0) | |
| >>> trigamma(z, evaluate=False) | |
| trigamma(z) | |
| See Also | |
| ======== | |
| gamma: Gamma function. | |
| lowergamma: Lower incomplete gamma function. | |
| uppergamma: Upper incomplete gamma function. | |
| polygamma: Polygamma function. | |
| loggamma: Log Gamma function. | |
| digamma: Digamma function. | |
| sympy.functions.special.beta_functions.beta: Euler Beta function. | |
| References | |
| ========== | |
| .. [1] https://en.wikipedia.org/wiki/Trigamma_function | |
| .. [2] https://mathworld.wolfram.com/TrigammaFunction.html | |
| .. [3] https://functions.wolfram.com/GammaBetaErf/PolyGamma2/ | |
| """ | |
| def _eval_evalf(self, prec): | |
| z = self.args[0] | |
| nprec = prec_to_dps(prec) | |
| return polygamma(1, z).evalf(n=nprec) | |
| def fdiff(self, argindex=1): | |
| z = self.args[0] | |
| return polygamma(1, z).fdiff() | |
| def _eval_is_real(self): | |
| z = self.args[0] | |
| return polygamma(1, z).is_real | |
| def _eval_is_positive(self): | |
| z = self.args[0] | |
| return polygamma(1, z).is_positive | |
| def _eval_is_negative(self): | |
| z = self.args[0] | |
| return polygamma(1, z).is_negative | |
| def _eval_aseries(self, n, args0, x, logx): | |
| as_polygamma = self.rewrite(polygamma) | |
| args0 = [S.One,] + args0 | |
| return as_polygamma._eval_aseries(n, args0, x, logx) | |
| def eval(cls, z): | |
| return polygamma(1, z) | |
| def _eval_expand_func(self, **hints): | |
| z = self.args[0] | |
| return polygamma(1, z).expand(func=True) | |
| def _eval_rewrite_as_zeta(self, z, **kwargs): | |
| return zeta(2, z) | |
| def _eval_rewrite_as_polygamma(self, z, **kwargs): | |
| return polygamma(1, z) | |
| def _eval_rewrite_as_harmonic(self, z, **kwargs): | |
| return -harmonic(z - 1, 2) + pi**2 / 6 | |
| def _eval_as_leading_term(self, x, logx, cdir): | |
| z = self.args[0] | |
| return polygamma(1, z).as_leading_term(x) | |
| ############################################################################### | |
| ##################### COMPLETE MULTIVARIATE GAMMA FUNCTION #################### | |
| ############################################################################### | |
| class multigamma(DefinedFunction): | |
| r""" | |
| The multivariate gamma function is a generalization of the gamma function | |
| .. math:: | |
| \Gamma_p(z) = \pi^{p(p-1)/4}\prod_{k=1}^p \Gamma[z + (1 - k)/2]. | |
| In a special case, ``multigamma(x, 1) = gamma(x)``. | |
| Examples | |
| ======== | |
| >>> from sympy import S, multigamma | |
| >>> from sympy import Symbol | |
| >>> x = Symbol('x') | |
| >>> p = Symbol('p', positive=True, integer=True) | |
| >>> multigamma(x, p) | |
| pi**(p*(p - 1)/4)*Product(gamma(-_k/2 + x + 1/2), (_k, 1, p)) | |
| Several special values are known: | |
| >>> multigamma(1, 1) | |
| 1 | |
| >>> multigamma(4, 1) | |
| 6 | |
| >>> multigamma(S(3)/2, 1) | |
| sqrt(pi)/2 | |
| Writing ``multigamma`` in terms of the ``gamma`` function: | |
| >>> multigamma(x, 1) | |
| gamma(x) | |
| >>> multigamma(x, 2) | |
| sqrt(pi)*gamma(x)*gamma(x - 1/2) | |
| >>> multigamma(x, 3) | |
| pi**(3/2)*gamma(x)*gamma(x - 1)*gamma(x - 1/2) | |
| Parameters | |
| ========== | |
| p : order or dimension of the multivariate gamma function | |
| See Also | |
| ======== | |
| gamma, lowergamma, uppergamma, polygamma, loggamma, digamma, trigamma, | |
| sympy.functions.special.beta_functions.beta | |
| References | |
| ========== | |
| .. [1] https://en.wikipedia.org/wiki/Multivariate_gamma_function | |
| """ | |
| unbranched = True | |
| def fdiff(self, argindex=2): | |
| from sympy.concrete.summations import Sum | |
| if argindex == 2: | |
| x, p = self.args | |
| k = Dummy("k") | |
| return self.func(x, p)*Sum(polygamma(0, x + (1 - k)/2), (k, 1, p)) | |
| else: | |
| raise ArgumentIndexError(self, argindex) | |
| def eval(cls, x, p): | |
| from sympy.concrete.products import Product | |
| if p.is_positive is False or p.is_integer is False: | |
| raise ValueError('Order parameter p must be positive integer.') | |
| k = Dummy("k") | |
| return (pi**(p*(p - 1)/4)*Product(gamma(x + (1 - k)/2), | |
| (k, 1, p))).doit() | |
| def _eval_conjugate(self): | |
| x, p = self.args | |
| return self.func(x.conjugate(), p) | |
| def _eval_is_real(self): | |
| x, p = self.args | |
| y = 2*x | |
| if y.is_integer and (y <= (p - 1)) is True: | |
| return False | |
| if intlike(y) and (y <= (p - 1)): | |
| return False | |
| if y > (p - 1) or y.is_noninteger: | |
| return True | |