def _eval_(self, x): """ EXAMPLES:: sage: airy_bi_prime(0) 3^(1/6)/gamma(1/3) sage: airy_bi_prime(0.0) 0.448288357353826 """ if x == 0: one_sixth = ZZ(1) / 6 return 3 ** (one_sixth) / gamma(2 * one_sixth)
def _eval_(self, x): """ EXAMPLES:: sage: airy_ai_prime(0) -1/3*3^(2/3)/gamma(1/3) sage: airy_ai_prime(0.0) -0.258819403792807 """ if x == 0: r = ZZ(1) / 3 return -1 / (3 ** (r) * gamma(r))
def _derivative_(self, a, z, diff_param=None): """ EXAMPLES:: sage: diff(struve_L(1,x),x) 1/3*x/pi - 1/2*struve_L(2, x) + 1/2*struve_L(0, x) """ if diff_param == 0: raise ValueError("cannot differentiate struve_L in the first parameter") from sage.functions.other import sqrt, gamma return (z**a/(sqrt(pi)*2**a*gamma(a+Integer(3)/Integer(2)))-struve_L(a+1,z)+struve_L(a-1,z))/2
def _derivative_(self, a, z, diff_param=None): """ EXAMPLES:: sage: diff(struve_H(3/2,x),x) -1/2*sqrt(2)*sqrt(1/(pi*x))*(cos(x) - 1) + 1/16*sqrt(2)*x^(3/2)/sqrt(pi) - 1/2*struve_H(5/2, x) """ if diff_param == 0: raise ValueError("cannot differentiate struve_H in the first parameter") from sage.functions.other import sqrt, gamma return (z**a/(sqrt(pi)*2**a*gamma(a+Integer(3)/Integer(2)))-struve_H(a+1,z)+struve_H(a-1,z))/2
def inverse_gamma_derivative(shift, r): """ Return value of `r`-th derivative of 1/Gamma at alpha-shift. """ if r == 0: result = iga*falling_factorial(alpha-1, shift) else: result = limit((1/gamma(s)).diff(s, r), s=alpha-shift) try: return coefficient_ring(result) except TypeError: return result
def inverse_gamma_derivative(shift, r): """ Return value of `r`-th derivative of 1/Gamma at alpha-shift. """ if r == 0: result = iga * falling_factorial(alpha - 1, shift) else: result = limit((1 / gamma(s)).diff(s, r), s=alpha - shift) try: return coefficient_ring(result) except TypeError: return result
def _eval_(self, x): """ EXAMPLES:: sage: from sage.functions.airy import airy_ai_simple sage: airy_ai_simple(0) 1/3*3^(1/3)/gamma(2/3) sage: airy_ai_simple(0.0) 0.355028053887817 sage: airy_ai_simple(I) airy_ai(I) sage: airy_ai_simple(1.0 * I) 0.331493305432141 - 0.317449858968444*I """ if x == 0: r = ZZ(2) / 3 return 1 / (3 ** (r) * gamma(r))
def _eval_(self, x): """ EXAMPLES:: sage: from sage.functions.airy import airy_bi_simple sage: airy_bi_simple(0) 1/3*3^(5/6)/gamma(2/3) sage: airy_bi_simple(0.0) 0.614926627446001 sage: airy_bi_simple(0).n() == airy_bi(0.0) True sage: airy_bi_simple(I) airy_bi(I) sage: airy_bi_simple(1.0 * I) 0.648858208330395 + 0.344958634768048*I """ if x == 0: one_sixth = ZZ(1) / 6 return 1 / (3 ** (one_sixth) * gamma(4 * one_sixth))
def SingularityAnalysis(var, zeta=1, alpha=0, beta=0, delta=0, precision=None, normalized=True): r""" Return the asymptotic expansion of the coefficients of an power series with specified pole and logarithmic singularity. More precisely, this extracts the `n`-th coefficient .. MATH:: [z^n] \left(\frac{1}{1-z/\zeta}\right)^\alpha \left(\frac{1}{z/\zeta} \log \frac{1}{1-z/\zeta}\right)^\beta \left(\frac{1}{z/\zeta} \log \left(\frac{1}{z/\zeta} \log \frac{1}{1-z/\zeta}\right)\right)^\delta (if ``normalized=True``, the default) or .. MATH:: [z^n] \left(\frac{1}{1-z/\zeta}\right)^\alpha \left(\log \frac{1}{1-z/\zeta}\right)^\beta \left(\log \left(\frac{1}{z/\zeta} \log \frac{1}{1-z/\zeta}\right)\right)^\delta (if ``normalized=False``). INPUT: - ``var`` -- a string for the variable name. - ``zeta`` -- (default: `1`) the location of the singularity. - ``alpha`` -- (default: `0`) the pole order of the singularty. - ``beta`` -- (default: `0`) the order of the logarithmic singularity. - ``delta`` -- (default: `0`) the order of the log-log singularity. Not yet implemented for ``delta != 0``. - ``precision`` -- (default: ``None``) an integer. If ``None``, then the default precision of the asymptotic ring is used. - ``normalized`` -- (default: ``True``) a boolean, see above. OUTPUT: An asymptotic expansion. EXAMPLES:: sage: asymptotic_expansions.SingularityAnalysis('n', alpha=1) 1 sage: asymptotic_expansions.SingularityAnalysis('n', alpha=2) n + 1 sage: asymptotic_expansions.SingularityAnalysis('n', alpha=3) 1/2*n^2 + 3/2*n + 1 sage: _.parent() Asymptotic Ring <n^ZZ> over Rational Field :: sage: asymptotic_expansions.SingularityAnalysis('n', alpha=-3/2, ....: precision=3) 3/4/sqrt(pi)*n^(-5/2) + 45/32/sqrt(pi)*n^(-7/2) + 1155/512/sqrt(pi)*n^(-9/2) + O(n^(-11/2)) sage: asymptotic_expansions.SingularityAnalysis('n', alpha=-1/2, ....: precision=3) -1/2/sqrt(pi)*n^(-3/2) - 3/16/sqrt(pi)*n^(-5/2) - 25/256/sqrt(pi)*n^(-7/2) + O(n^(-9/2)) sage: asymptotic_expansions.SingularityAnalysis('n', alpha=1/2, ....: precision=4) 1/sqrt(pi)*n^(-1/2) - 1/8/sqrt(pi)*n^(-3/2) + 1/128/sqrt(pi)*n^(-5/2) + 5/1024/sqrt(pi)*n^(-7/2) + O(n^(-9/2)) sage: _.parent() Asymptotic Ring <n^QQ> over Symbolic Constants Subring :: sage: S = SR.subring(rejecting_variables=('n',)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=S.var('a'), ....: precision=4).map_coefficients(lambda c: c.factor()) 1/gamma(a)*n^(a - 1) + (1/2*(a - 1)*a/gamma(a))*n^(a - 2) + (1/24*(3*a - 1)*(a - 1)*(a - 2)*a/gamma(a))*n^(a - 3) + (1/48*(a - 1)^2*(a - 2)*(a - 3)*a^2/gamma(a))*n^(a - 4) + O(n^(a - 5)) sage: _.parent() Asymptotic Ring <n^(Symbolic Subring rejecting the variable n)> over Symbolic Subring rejecting the variable n :: sage: ae = asymptotic_expansions.SingularityAnalysis('n', ....: alpha=1/2, beta=1, precision=4); ae 1/sqrt(pi)*n^(-1/2)*log(n) + ((euler_gamma + 2*log(2))/sqrt(pi))*n^(-1/2) - 5/8/sqrt(pi)*n^(-3/2)*log(n) + (1/8*(3*euler_gamma + 6*log(2) - 8)/sqrt(pi) - (euler_gamma + 2*log(2) - 2)/sqrt(pi))*n^(-3/2) + O(n^(-5/2)*log(n)) sage: n = ae.parent().gen() sage: ae.subs(n=n-1).map_coefficients(lambda x: x.canonicalize_radical()) 1/sqrt(pi)*n^(-1/2)*log(n) + ((euler_gamma + 2*log(2))/sqrt(pi))*n^(-1/2) - 1/8/sqrt(pi)*n^(-3/2)*log(n) + (-1/8*(euler_gamma + 2*log(2))/sqrt(pi))*n^(-3/2) + O(n^(-5/2)*log(n)) :: sage: asymptotic_expansions.SingularityAnalysis('n', ....: alpha=1, beta=1/2, precision=4) log(n)^(1/2) + 1/2*euler_gamma*log(n)^(-1/2) + (-1/8*euler_gamma^2 + 1/48*pi^2)*log(n)^(-3/2) + (1/16*euler_gamma^3 - 1/32*euler_gamma*pi^2 + 1/8*zeta(3))*log(n)^(-5/2) + O(log(n)^(-7/2)) :: sage: ae = asymptotic_expansions.SingularityAnalysis('n', ....: alpha=0, beta=2, precision=14) sage: n = ae.parent().gen() sage: ae.subs(n=n-2) 2*n^(-1)*log(n) + 2*euler_gamma*n^(-1) - n^(-2) - 1/6*n^(-3) + O(n^(-5)) :: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=-1/2, beta=1, precision=2, normalized=False) -1/2/sqrt(pi)*n^(-3/2)*log(n) + (-1/2*(euler_gamma + 2*log(2) - 2)/sqrt(pi))*n^(-3/2) + O(n^(-5/2)*log(n)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1/2, alpha=0, beta=1, precision=3, normalized=False) 2^n*n^(-1) + O(2^n*n^(-2)) ALGORITHM: See [FS2009]_ together with the `errata list <http://algo.inria.fr/flajolet/Publications/AnaCombi/errata.pdf>`_. REFERENCES: .. [FS2009] Philippe Flajolet and Robert Sedgewick, `Analytic combinatorics <http://algo.inria.fr/flajolet/Publications/AnaCombi/book.pdf>`_. Cambridge University Press, Cambridge, 2009. TESTS:: sage: ex = asymptotic_expansions.SingularityAnalysis('n', alpha=-1/2, ....: precision=4) sage: n = ex.parent().gen() sage: coefficients = ((1-x)^(1/2)).series( ....: x, 21).truncate().coefficients(x, sparse=False) sage: ex.compare_with_values(n, # rel tol 1e-6 ....: lambda k: coefficients[k], [5, 10, 20]) [(5, 0.015778873294?), (10, 0.01498952777?), (20, 0.0146264622?)] sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=3, precision=2) 1/2*n^2 + 3/2*n + O(1) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=3, precision=3) 1/2*n^2 + 3/2*n + 1 sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=3, precision=4) 1/2*n^2 + 3/2*n + 1 :: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=0) Traceback (most recent call last): ... NotImplementedOZero: The error term in the result is O(0) which means 0 for sufficiently large n. sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=-1) Traceback (most recent call last): ... NotImplementedOZero: The error term in the result is O(0) which means 0 for sufficiently large n. :: sage: asymptotic_expansions.SingularityAnalysis( ....: 'm', alpha=-1/2, precision=3) -1/2/sqrt(pi)*m^(-3/2) - 3/16/sqrt(pi)*m^(-5/2) - 25/256/sqrt(pi)*m^(-7/2) + O(m^(-9/2)) sage: _.parent() Asymptotic Ring <m^QQ> over Symbolic Constants Subring Location of the singularity:: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=1, zeta=2, precision=3) (1/2)^n sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=1, zeta=1/2, precision=3) 2^n sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=1, zeta=CyclotomicField(3).gen(), ....: precision=3) (-zeta3 - 1)^n sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=4, zeta=2, precision=3) 1/6*(1/2)^n*n^3 + (1/2)^n*n^2 + 11/6*(1/2)^n*n + O((1/2)^n) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=-1, zeta=2, precision=3) Traceback (most recent call last): ... NotImplementedOZero: The error term in the result is O(0) which means 0 for sufficiently large n. sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=1/2, zeta=2, precision=3) 1/sqrt(pi)*(1/2)^n*n^(-1/2) - 1/8/sqrt(pi)*(1/2)^n*n^(-3/2) + 1/128/sqrt(pi)*(1/2)^n*n^(-5/2) + O((1/2)^n*n^(-7/2)) The following tests correspond to Table VI.5 in [FS2009]_. :: sage: A.<n> = AsymptoticRing('n^QQ * log(n)^QQ', QQ) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=-1/2, beta=1, precision=2, ....: normalized=False) * (- sqrt(pi*n^3)) 1/2*log(n) + 1/2*euler_gamma + log(2) - 1 + O(n^(-1)*log(n)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=0, beta=1, precision=3, ....: normalized=False) n^(-1) + O(n^(-2)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=0, beta=2, precision=14, ....: normalized=False) * n 2*log(n) + 2*euler_gamma - n^(-1) - 1/6*n^(-2) + O(n^(-4)) sage: (asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=1/2, beta=1, precision=4, ....: normalized=False) * sqrt(pi*n)).\ ....: map_coefficients(lambda x: x.expand()) log(n) + euler_gamma + 2*log(2) - 1/8*n^(-1)*log(n) + (-1/8*euler_gamma - 1/4*log(2))*n^(-1) + O(n^(-2)*log(n)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=1, beta=1, precision=13, ....: normalized=False) log(n) + euler_gamma + 1/2*n^(-1) - 1/12*n^(-2) + 1/120*n^(-4) + O(n^(-6)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=1, beta=2, precision=4, ....: normalized=False) log(n)^2 + 2*euler_gamma*log(n) + euler_gamma^2 - 1/6*pi^2 + O(n^(-1)*log(n)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=3/2, beta=1, precision=3, ....: normalized=False) * sqrt(pi/n) 2*log(n) + 2*euler_gamma + 4*log(2) - 4 + 3/4*n^(-1)*log(n) + O(n^(-1)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=2, beta=1, precision=5, ....: normalized=False) n*log(n) + (euler_gamma - 1)*n + log(n) + euler_gamma + 1/2 + O(n^(-1)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=2, beta=2, precision=4, ....: normalized=False) / n log(n)^2 + (2*euler_gamma - 2)*log(n) - 2*euler_gamma + euler_gamma^2 - 1/6*pi^2 + 2 + n^(-1)*log(n)^2 + O(n^(-1)*log(n)) Be aware that the last result does *not* coincide with [FS2009]_, they do have a different error term. Checking parameters:: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, 1, 1/2, precision=0, normalized=False) Traceback (most recent call last): ... ValueError: beta and delta must be integers sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, 1, 1, 1/2, normalized=False) Traceback (most recent call last): ... ValueError: beta and delta must be integers :: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=0, beta=0, delta=1, precision=3) Traceback (most recent call last): ... NotImplementedError: not implemented for delta!=0 """ from itertools import islice, count from asymptotic_ring import AsymptoticRing from growth_group import ExponentialGrowthGroup, \ MonomialGrowthGroup from sage.arith.all import falling_factorial from sage.categories.cartesian_product import cartesian_product from sage.functions.other import binomial, gamma from sage.calculus.calculus import limit from sage.misc.cachefunc import cached_function from sage.arith.srange import srange from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ from sage.symbolic.ring import SR SCR = SR.subring(no_variables=True) s = SR('s') iga = 1/gamma(alpha) if iga.parent() is SR: try: iga = SCR(iga) except TypeError: pass coefficient_ring = iga.parent() if beta != 0: coefficient_ring = SCR @cached_function def inverse_gamma_derivative(shift, r): """ Return value of `r`-th derivative of 1/Gamma at alpha-shift. """ if r == 0: result = iga*falling_factorial(alpha-1, shift) else: result = limit((1/gamma(s)).diff(s, r), s=alpha-shift) try: return coefficient_ring(result) except TypeError: return result if isinstance(alpha, int): alpha = ZZ(alpha) if isinstance(beta, int): beta = ZZ(beta) if isinstance(delta, int): delta = ZZ(delta) if precision is None: precision = AsymptoticRing.__default_prec__ if not normalized and not (beta in ZZ and delta in ZZ): raise ValueError("beta and delta must be integers") if delta != 0: raise NotImplementedError("not implemented for delta!=0") groups = [] if zeta != 1: groups.append(ExponentialGrowthGroup((1/zeta).parent(), var)) groups.append(MonomialGrowthGroup(alpha.parent(), var)) if beta != 0: groups.append(MonomialGrowthGroup(beta.parent(), 'log({})'.format(var))) group = cartesian_product(groups) A = AsymptoticRing(growth_group=group, coefficient_ring=coefficient_ring, default_prec=precision) n = A.gen() if zeta == 1: exponential_factor = 1 else: exponential_factor = n.rpow(1/zeta) if beta in ZZ and beta >= 0: it = ((k, r) for k in count() for r in srange(beta+1)) k_max = precision else: it = ((0, r) for r in count()) k_max = 0 if beta != 0: log_n = n.log() else: # avoid construction of log(n) # because it does not exist in growth group. log_n = 1 it = reversed(list(islice(it, precision+1))) if normalized: beta_denominator = beta else: beta_denominator = 0 L = _sa_coefficients_lambda_(max(1, k_max), beta=beta_denominator) (k, r) = next(it) result = (n**(-k) * log_n**(-r)).O() if alpha in ZZ and beta == 0: if alpha > 0 and alpha <= precision: result = A(0) elif alpha <= 0 and precision > 0: from misc import NotImplementedOZero raise NotImplementedOZero(A) for (k, r) in it: result += binomial(beta, r) * \ sum(L[(k, ell)] * (-1)**ell * inverse_gamma_derivative(ell, r) for ell in srange(k, 2*k+1) if (k, ell) in L) * \ n**(-k) * log_n**(-r) result *= exponential_factor * n**(alpha-1) * log_n**beta return result
def _closed_form(hyp): a, b, z = hyp.operands() a, b = a.operands(), b.operands() p, q = len(a), len(b) if z == 0: return Integer(1) if p == q == 0: return exp(z) if p == 1 and q == 0: return (1 - z) ** (-a[0]) if p == 0 and q == 1: # TODO: make this require only linear time def _0f1(b, z): F12 = cosh(2 * sqrt(z)) F32 = sinh(2 * sqrt(z)) / (2 * sqrt(z)) if 2 * b == 1: return F12 if 2 * b == 3: return F32 if 2 * b > 3: return ((b - 2) * (b - 1) / z * (_0f1(b - 2, z) - _0f1(b - 1, z))) if 2 * b < 1: return (_0f1(b + 1, z) + z / (b * (b + 1)) * _0f1(b + 2, z)) raise ValueError # Can evaluate 0F1 in terms of elementary functions when # the parameter is a half-integer if 2 * b[0] in ZZ and b[0] not in ZZ: return _0f1(b[0], z) # Confluent hypergeometric function if p == 1 and q == 1: aa, bb = a[0], b[0] if aa * 2 == 1 and bb * 2 == 3: t = sqrt(-z) return sqrt(pi) / 2 * erf(t) / t if a == 1 and b == 2: return (exp(z) - 1) / z n, m = aa, bb if n in ZZ and m in ZZ and m > 0 and n > 0: rf = rising_factorial if m <= n: return (exp(z) * sum(rf(m - n, k) * (-z) ** k / factorial(k) / rf(m, k) for k in xrange(n - m + 1))) else: T = sum(rf(n - m + 1, k) * z ** k / (factorial(k) * rf(2 - m, k)) for k in xrange(m - n)) U = sum(rf(1 - n, k) * (-z) ** k / (factorial(k) * rf(2 - m, k)) for k in xrange(n)) return (factorial(m - 2) * rf(1 - m, n) * z ** (1 - m) / factorial(n - 1) * (T - exp(z) * U)) if p == 2 and q == 1: R12 = QQ('1/2') R32 = QQ('3/2') def _2f1(a, b, c, z): """ Evaluation of 2F1(a, b; c; z), assuming a, b, c positive integers or half-integers """ if b == c: return (1 - z) ** (-a) if a == c: return (1 - z) ** (-b) if a == 0 or b == 0: return Integer(1) if a > b: a, b = b, a if b >= 2: F1 = _2f1(a, b - 1, c, z) F2 = _2f1(a, b - 2, c, z) q = (b - 1) * (z - 1) return (((c - 2 * b + 2 + (b - a - 1) * z) * F1 + (b - c - 1) * F2) / q) if c > 2: # how to handle this case? if a - c + 1 == 0 or b - c + 1 == 0: raise NotImplementedError F1 = _2f1(a, b, c - 1, z) F2 = _2f1(a, b, c - 2, z) r1 = (c - 1) * (2 - c - (a + b - 2 * c + 3) * z) r2 = (c - 1) * (c - 2) * (1 - z) q = (a - c + 1) * (b - c + 1) * z return (r1 * F1 + r2 * F2) / q if (a, b, c) == (R12, 1, 2): return (2 - 2 * sqrt(1 - z)) / z if (a, b, c) == (1, 1, 2): return -log(1 - z) / z if (a, b, c) == (1, R32, R12): return (1 + z) / (1 - z) ** 2 if (a, b, c) == (1, R32, 2): return 2 * (1 / sqrt(1 - z) - 1) / z if (a, b, c) == (R32, 2, R12): return (1 + 3 * z) / (1 - z) ** 3 if (a, b, c) == (R32, 2, 1): return (2 + z) / (2 * (sqrt(1 - z) * (1 - z) ** 2)) if (a, b, c) == (2, 2, 1): return (1 + z) / (1 - z) ** 3 raise NotImplementedError aa, bb = a cc, = b if z == 1: return (gamma(cc) * gamma(cc - aa - bb) / gamma(cc - aa) / gamma(cc - bb)) if ((aa * 2) in ZZ and (bb * 2) in ZZ and (cc * 2) in ZZ and aa > 0 and bb > 0 and cc > 0): try: return _2f1(aa, bb, cc, z) except NotImplementedError: pass return hyp
def _closed_form(hyp): a, b, z = hyp.operands() a, b = a.operands(), b.operands() p, q = len(a), len(b) if z == 0: return Integer(1) if p == q == 0: return exp(z) if p == 1 and q == 0: return (1 - z)**(-a[0]) if p == 0 and q == 1: # TODO: make this require only linear time def _0f1(b, z): F12 = cosh(2 * sqrt(z)) F32 = sinh(2 * sqrt(z)) / (2 * sqrt(z)) if 2 * b == 1: return F12 if 2 * b == 3: return F32 if 2 * b > 3: return ((b - 2) * (b - 1) / z * (_0f1(b - 2, z) - _0f1(b - 1, z))) if 2 * b < 1: return (_0f1(b + 1, z) + z / (b * (b + 1)) * _0f1(b + 2, z)) raise ValueError # Can evaluate 0F1 in terms of elementary functions when # the parameter is a half-integer if 2 * b[0] in ZZ and b[0] not in ZZ: return _0f1(b[0], z) # Confluent hypergeometric function if p == 1 and q == 1: aa, bb = a[0], b[0] if aa * 2 == 1 and bb * 2 == 3: t = sqrt(-z) return sqrt(pi) / 2 * erf(t) / t if a == 1 and b == 2: return (exp(z) - 1) / z n, m = aa, bb if n in ZZ and m in ZZ and m > 0 and n > 0: rf = rising_factorial if m <= n: return (exp(z) * sum( rf(m - n, k) * (-z)**k / factorial(k) / rf(m, k) for k in xrange(n - m + 1))) else: T = sum( rf(n - m + 1, k) * z**k / (factorial(k) * rf(2 - m, k)) for k in xrange(m - n)) U = sum( rf(1 - n, k) * (-z)**k / (factorial(k) * rf(2 - m, k)) for k in xrange(n)) return (factorial(m - 2) * rf(1 - m, n) * z**(1 - m) / factorial(n - 1) * (T - exp(z) * U)) if p == 2 and q == 1: R12 = QQ('1/2') R32 = QQ('3/2') def _2f1(a, b, c, z): """ Evaluation of 2F1(a, b, c, z), assuming a, b, c positive integers or half-integers """ if b == c: return (1 - z)**(-a) if a == c: return (1 - z)**(-b) if a == 0 or b == 0: return Integer(1) if a > b: a, b = b, a if b >= 2: F1 = _2f1(a, b - 1, c, z) F2 = _2f1(a, b - 2, c, z) q = (b - 1) * (z - 1) return (((c - 2 * b + 2 + (b - a - 1) * z) * F1 + (b - c - 1) * F2) / q) if c > 2: # how to handle this case? if a - c + 1 == 0 or b - c + 1 == 0: raise NotImplementedError F1 = _2f1(a, b, c - 1, z) F2 = _2f1(a, b, c - 2, z) r1 = (c - 1) * (2 - c - (a + b - 2 * c + 3) * z) r2 = (c - 1) * (c - 2) * (1 - z) q = (a - c + 1) * (b - c + 1) * z return (r1 * F1 + r2 * F2) / q if (a, b, c) == (R12, 1, 2): return (2 - 2 * sqrt(1 - z)) / z if (a, b, c) == (1, 1, 2): return -log(1 - z) / z if (a, b, c) == (1, R32, R12): return (1 + z) / (1 - z)**2 if (a, b, c) == (1, R32, 2): return 2 * (1 / sqrt(1 - z) - 1) / z if (a, b, c) == (R32, 2, R12): return (1 + 3 * z) / (1 - z)**3 if (a, b, c) == (R32, 2, 1): return (2 + z) / (2 * (sqrt(1 - z) * (1 - z)**2)) if (a, b, c) == (2, 2, 1): return (1 + z) / (1 - z)**3 raise NotImplementedError aa, bb = a cc, = b if z == 1: return (gamma(cc) * gamma(cc - aa - bb) / gamma(cc - aa) / gamma(cc - bb)) if ((aa * 2) in ZZ and (bb * 2) in ZZ and (cc * 2) in ZZ and aa > 0 and bb > 0 and cc > 0): try: return _2f1(aa, bb, cc, z) except NotImplementedError: pass return hyp
def SingularityAnalysis(var, zeta=1, alpha=0, beta=0, delta=0, precision=None, normalized=True): r""" Return the asymptotic expansion of the coefficients of an power series with specified pole and logarithmic singularity. More precisely, this extracts the `n`-th coefficient .. MATH:: [z^n] \left(\frac{1}{1-z/\zeta}\right)^\alpha \left(\frac{1}{z/\zeta} \log \frac{1}{1-z/\zeta}\right)^\beta \left(\frac{1}{z/\zeta} \log \left(\frac{1}{z/\zeta} \log \frac{1}{1-z/\zeta}\right)\right)^\delta (if ``normalized=True``, the default) or .. MATH:: [z^n] \left(\frac{1}{1-z/\zeta}\right)^\alpha \left(\log \frac{1}{1-z/\zeta}\right)^\beta \left(\log \left(\frac{1}{z/\zeta} \log \frac{1}{1-z/\zeta}\right)\right)^\delta (if ``normalized=False``). INPUT: - ``var`` -- a string for the variable name. - ``zeta`` -- (default: `1`) the location of the singularity. - ``alpha`` -- (default: `0`) the pole order of the singularty. - ``beta`` -- (default: `0`) the order of the logarithmic singularity. - ``delta`` -- (default: `0`) the order of the log-log singularity. Not yet implemented for ``delta != 0``. - ``precision`` -- (default: ``None``) an integer. If ``None``, then the default precision of the asymptotic ring is used. - ``normalized`` -- (default: ``True``) a boolean, see above. OUTPUT: An asymptotic expansion. EXAMPLES:: sage: asymptotic_expansions.SingularityAnalysis('n', alpha=1) 1 sage: asymptotic_expansions.SingularityAnalysis('n', alpha=2) n + 1 sage: asymptotic_expansions.SingularityAnalysis('n', alpha=3) 1/2*n^2 + 3/2*n + 1 sage: _.parent() Asymptotic Ring <n^ZZ> over Rational Field :: sage: asymptotic_expansions.SingularityAnalysis('n', alpha=-3/2, ....: precision=3) 3/4/sqrt(pi)*n^(-5/2) + 45/32/sqrt(pi)*n^(-7/2) + 1155/512/sqrt(pi)*n^(-9/2) + O(n^(-11/2)) sage: asymptotic_expansions.SingularityAnalysis('n', alpha=-1/2, ....: precision=3) -1/2/sqrt(pi)*n^(-3/2) - 3/16/sqrt(pi)*n^(-5/2) - 25/256/sqrt(pi)*n^(-7/2) + O(n^(-9/2)) sage: asymptotic_expansions.SingularityAnalysis('n', alpha=1/2, ....: precision=4) 1/sqrt(pi)*n^(-1/2) - 1/8/sqrt(pi)*n^(-3/2) + 1/128/sqrt(pi)*n^(-5/2) + 5/1024/sqrt(pi)*n^(-7/2) + O(n^(-9/2)) sage: _.parent() Asymptotic Ring <n^QQ> over Symbolic Constants Subring :: sage: S = SR.subring(rejecting_variables=('n',)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=S.var('a'), ....: precision=4).map_coefficients(lambda c: c.factor()) 1/gamma(a)*n^(a - 1) + (1/2*(a - 1)*a/gamma(a))*n^(a - 2) + (1/24*(3*a - 1)*(a - 1)*(a - 2)*a/gamma(a))*n^(a - 3) + (1/48*(a - 1)^2*(a - 2)*(a - 3)*a^2/gamma(a))*n^(a - 4) + O(n^(a - 5)) sage: _.parent() Asymptotic Ring <n^(Symbolic Subring rejecting the variable n)> over Symbolic Subring rejecting the variable n :: sage: ae = asymptotic_expansions.SingularityAnalysis('n', ....: alpha=1/2, beta=1, precision=4); ae 1/sqrt(pi)*n^(-1/2)*log(n) + ((euler_gamma + 2*log(2))/sqrt(pi))*n^(-1/2) - 5/8/sqrt(pi)*n^(-3/2)*log(n) + (1/8*(3*euler_gamma + 6*log(2) - 8)/sqrt(pi) - (euler_gamma + 2*log(2) - 2)/sqrt(pi))*n^(-3/2) + O(n^(-5/2)*log(n)) sage: n = ae.parent().gen() sage: ae.subs(n=n-1).map_coefficients(lambda x: x.canonicalize_radical()) 1/sqrt(pi)*n^(-1/2)*log(n) + ((euler_gamma + 2*log(2))/sqrt(pi))*n^(-1/2) - 1/8/sqrt(pi)*n^(-3/2)*log(n) + (-1/8*(euler_gamma + 2*log(2))/sqrt(pi))*n^(-3/2) + O(n^(-5/2)*log(n)) :: sage: asymptotic_expansions.SingularityAnalysis('n', ....: alpha=1, beta=1/2, precision=4) log(n)^(1/2) + 1/2*euler_gamma*log(n)^(-1/2) + (-1/8*euler_gamma^2 + 1/48*pi^2)*log(n)^(-3/2) + (1/16*euler_gamma^3 - 1/32*euler_gamma*pi^2 + 1/8*zeta(3))*log(n)^(-5/2) + O(log(n)^(-7/2)) :: sage: ae = asymptotic_expansions.SingularityAnalysis('n', ....: alpha=0, beta=2, precision=14) sage: n = ae.parent().gen() sage: ae.subs(n=n-2) 2*n^(-1)*log(n) + 2*euler_gamma*n^(-1) - n^(-2) - 1/6*n^(-3) + O(n^(-5)) :: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=-1/2, beta=1, precision=2, normalized=False) -1/2/sqrt(pi)*n^(-3/2)*log(n) + (-1/2*(euler_gamma + 2*log(2) - 2)/sqrt(pi))*n^(-3/2) + O(n^(-5/2)*log(n)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1/2, alpha=0, beta=1, precision=3, normalized=False) 2^n*n^(-1) + O(2^n*n^(-2)) ALGORITHM: See [FS2009]_ together with the `errata list <http://algo.inria.fr/flajolet/Publications/AnaCombi/errata.pdf>`_. REFERENCES: .. [FS2009] Philippe Flajolet and Robert Sedgewick, `Analytic combinatorics <http://algo.inria.fr/flajolet/Publications/AnaCombi/book.pdf>`_. Cambridge University Press, Cambridge, 2009. TESTS:: sage: ex = asymptotic_expansions.SingularityAnalysis('n', alpha=-1/2, ....: precision=4) sage: n = ex.parent().gen() sage: coefficients = ((1-x)^(1/2)).series( ....: x, 21).truncate().coefficients(x, sparse=False) sage: ex.compare_with_values(n, # rel tol 1e-6 ....: lambda k: coefficients[k], [5, 10, 20]) [(5, 0.015778873294?), (10, 0.01498952777?), (20, 0.0146264622?)] sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=3, precision=2) 1/2*n^2 + 3/2*n + O(1) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=3, precision=3) 1/2*n^2 + 3/2*n + 1 sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=3, precision=4) 1/2*n^2 + 3/2*n + 1 :: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=0) Traceback (most recent call last): ... NotImplementedOZero: The error term in the result is O(0) which means 0 for sufficiently large n. sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=-1) Traceback (most recent call last): ... NotImplementedOZero: The error term in the result is O(0) which means 0 for sufficiently large n. :: sage: asymptotic_expansions.SingularityAnalysis( ....: 'm', alpha=-1/2, precision=3) -1/2/sqrt(pi)*m^(-3/2) - 3/16/sqrt(pi)*m^(-5/2) - 25/256/sqrt(pi)*m^(-7/2) + O(m^(-9/2)) sage: _.parent() Asymptotic Ring <m^QQ> over Symbolic Constants Subring Location of the singularity:: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=1, zeta=2, precision=3) (1/2)^n sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=1, zeta=1/2, precision=3) 2^n sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=1, zeta=CyclotomicField(3).gen(), ....: precision=3) (-zeta3 - 1)^n sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=4, zeta=2, precision=3) 1/6*(1/2)^n*n^3 + (1/2)^n*n^2 + 11/6*(1/2)^n*n + O((1/2)^n) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=-1, zeta=2, precision=3) Traceback (most recent call last): ... NotImplementedOZero: The error term in the result is O(0) which means 0 for sufficiently large n. sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=1/2, zeta=2, precision=3) 1/sqrt(pi)*(1/2)^n*n^(-1/2) - 1/8/sqrt(pi)*(1/2)^n*n^(-3/2) + 1/128/sqrt(pi)*(1/2)^n*n^(-5/2) + O((1/2)^n*n^(-7/2)) The following tests correspond to Table VI.5 in [FS2009]_. :: sage: A.<n> = AsymptoticRing('n^QQ * log(n)^QQ', QQ) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=-1/2, beta=1, precision=2, ....: normalized=False) * (- sqrt(pi*n^3)) 1/2*log(n) + 1/2*euler_gamma + log(2) - 1 + O(n^(-1)*log(n)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=0, beta=1, precision=3, ....: normalized=False) n^(-1) + O(n^(-2)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=0, beta=2, precision=14, ....: normalized=False) * n 2*log(n) + 2*euler_gamma - n^(-1) - 1/6*n^(-2) + O(n^(-4)) sage: (asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=1/2, beta=1, precision=4, ....: normalized=False) * sqrt(pi*n)).\ ....: map_coefficients(lambda x: x.expand()) log(n) + euler_gamma + 2*log(2) - 1/8*n^(-1)*log(n) + (-1/8*euler_gamma - 1/4*log(2))*n^(-1) + O(n^(-2)*log(n)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=1, beta=1, precision=13, ....: normalized=False) log(n) + euler_gamma + 1/2*n^(-1) - 1/12*n^(-2) + 1/120*n^(-4) + O(n^(-6)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=1, beta=2, precision=4, ....: normalized=False) log(n)^2 + 2*euler_gamma*log(n) + euler_gamma^2 - 1/6*pi^2 + O(n^(-1)*log(n)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=3/2, beta=1, precision=3, ....: normalized=False) * sqrt(pi/n) 2*log(n) + 2*euler_gamma + 4*log(2) - 4 + 3/4*n^(-1)*log(n) + O(n^(-1)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=2, beta=1, precision=5, ....: normalized=False) n*log(n) + (euler_gamma - 1)*n + log(n) + euler_gamma + 1/2 + O(n^(-1)) sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, alpha=2, beta=2, precision=4, ....: normalized=False) / n log(n)^2 + (2*euler_gamma - 2)*log(n) - 2*euler_gamma + euler_gamma^2 - 1/6*pi^2 + 2 + n^(-1)*log(n)^2 + O(n^(-1)*log(n)) Be aware that the last result does *not* coincide with [FS2009]_, they do have a different error term. Checking parameters:: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, 1, 1/2, precision=0, normalized=False) Traceback (most recent call last): ... ValueError: beta and delta must be integers sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', 1, 1, 1, 1/2, normalized=False) Traceback (most recent call last): ... ValueError: beta and delta must be integers :: sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=0, beta=0, delta=1, precision=3) Traceback (most recent call last): ... NotImplementedError: not implemented for delta!=0 """ from itertools import islice, count from asymptotic_ring import AsymptoticRing from growth_group import ExponentialGrowthGroup, \ MonomialGrowthGroup from sage.arith.all import falling_factorial from sage.categories.cartesian_product import cartesian_product from sage.functions.other import binomial, gamma from sage.calculus.calculus import limit from sage.misc.cachefunc import cached_function from sage.arith.srange import srange from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ from sage.symbolic.ring import SR SCR = SR.subring(no_variables=True) s = SR('s') iga = 1 / gamma(alpha) if iga.parent() is SR: try: iga = SCR(iga) except TypeError: pass coefficient_ring = iga.parent() if beta != 0: coefficient_ring = SCR @cached_function def inverse_gamma_derivative(shift, r): """ Return value of `r`-th derivative of 1/Gamma at alpha-shift. """ if r == 0: result = iga * falling_factorial(alpha - 1, shift) else: result = limit((1 / gamma(s)).diff(s, r), s=alpha - shift) try: return coefficient_ring(result) except TypeError: return result if isinstance(alpha, int): alpha = ZZ(alpha) if isinstance(beta, int): beta = ZZ(beta) if isinstance(delta, int): delta = ZZ(delta) if precision is None: precision = AsymptoticRing.__default_prec__ if not normalized and not (beta in ZZ and delta in ZZ): raise ValueError("beta and delta must be integers") if delta != 0: raise NotImplementedError("not implemented for delta!=0") groups = [] if zeta != 1: groups.append(ExponentialGrowthGroup((1 / zeta).parent(), var)) groups.append(MonomialGrowthGroup(alpha.parent(), var)) if beta != 0: groups.append( MonomialGrowthGroup(beta.parent(), 'log({})'.format(var))) group = cartesian_product(groups) A = AsymptoticRing(growth_group=group, coefficient_ring=coefficient_ring, default_prec=precision) n = A.gen() if zeta == 1: exponential_factor = 1 else: exponential_factor = n.rpow(1 / zeta) if beta in ZZ and beta >= 0: it = ((k, r) for k in count() for r in srange(beta + 1)) k_max = precision else: it = ((0, r) for r in count()) k_max = 0 if beta != 0: log_n = n.log() else: # avoid construction of log(n) # because it does not exist in growth group. log_n = 1 it = reversed(list(islice(it, precision + 1))) if normalized: beta_denominator = beta else: beta_denominator = 0 L = _sa_coefficients_lambda_(max(1, k_max), beta=beta_denominator) (k, r) = next(it) result = (n**(-k) * log_n**(-r)).O() if alpha in ZZ and beta == 0: if alpha > 0 and alpha <= precision: result = A(0) elif alpha <= 0 and precision > 0: from misc import NotImplementedOZero raise NotImplementedOZero(A) for (k, r) in it: result += binomial(beta, r) * \ sum(L[(k, ell)] * (-1)**ell * inverse_gamma_derivative(ell, r) for ell in srange(k, 2*k+1) if (k, ell) in L) * \ n**(-k) * log_n**(-r) result *= exponential_factor * n**(alpha - 1) * log_n**beta return result