|
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 |
|
|
|
|
|
|
|
|
|
|
|
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) |
|
|
|
@classmethod |
|
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() |
|
|
|
|
|
|
|
|
|
|
|
|
|
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) |
|
|
|
@classmethod |
|
def eval(cls, 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) |
|
|
|
|
|
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): |
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
@classmethod |
|
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) |
|
|
|
|
|
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) |
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
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. |
|
|
|
""" |
|
|
|
@classmethod |
|
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: |
|
|
|
p, q = z.as_numer_denom() |
|
|
|
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() |
|
|
|
|
|
|
|
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: |
|
|
|
|
|
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: |
|
|
|
|
|
|
|
|
|
|
|
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/ |
|
|
|
""" |
|
@classmethod |
|
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() |
|
|
|
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() |
|
|
|
|
|
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) |
|
|
|
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) |
|
|
|
@classmethod |
|
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) |
|
|
|
@classmethod |
|
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) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) |
|
|
|
@classmethod |
|
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 |
|
|