def _log_StirlingNegativePowers_(var, precision): r""" Helper function to calculate the logarithm of Stirling's approximation formula from the negative powers of ``var`` on, i.e., it skips the summands `n \log n - n + (\log n)/2 + \log(2\pi)/2`. INPUT: - ``var`` -- a string for the variable name. - ``precision`` -- an integer specifying the number of exact summands. If this is negative, then the result is `0`. OUTPUT: An asymptotic expansion. TESTS:: sage: asymptotic_expansions._log_StirlingNegativePowers_( ....: 'm', precision=-1) 0 sage: asymptotic_expansions._log_StirlingNegativePowers_( ....: 'm', precision=0) O(m^(-1)) sage: asymptotic_expansions._log_StirlingNegativePowers_( ....: 'm', precision=3) 1/12*m^(-1) - 1/360*m^(-3) + 1/1260*m^(-5) + O(m^(-7)) sage: _.parent() Asymptotic Ring <m^ZZ> over Rational Field """ from asymptotic_ring import AsymptoticRing from sage.rings.rational_field import QQ A = AsymptoticRing(growth_group='{n}^ZZ'.format(n=var), coefficient_ring=QQ) if precision < 0: return A.zero() n = A.gen() from sage.arith.all import bernoulli from sage.arith.srange import srange result = sum((bernoulli(k) / k / (k-1) / n**(k-1) for k in srange(2, 2*precision + 2, 2)), A.zero()) return result + (1 / n**(2*precision + 1)).O()
def _log_StirlingNegativePowers_(var, precision): r""" Helper function to calculate the logarithm of Stirling's approximation formula from the negative powers of ``var`` on, i.e., it skips the summands `n \log n - n + (\log n)/2 + \log(2\pi)/2`. INPUT: - ``var`` -- a string for the variable name. - ``precision`` -- an integer specifying the number of exact summands. If this is negative, then the result is `0`. OUTPUT: An asymptotic expansion. TESTS:: sage: asymptotic_expansions._log_StirlingNegativePowers_( ....: 'm', precision=-1) 0 sage: asymptotic_expansions._log_StirlingNegativePowers_( ....: 'm', precision=0) O(m^(-1)) sage: asymptotic_expansions._log_StirlingNegativePowers_( ....: 'm', precision=3) 1/12*m^(-1) - 1/360*m^(-3) + 1/1260*m^(-5) + O(m^(-7)) sage: _.parent() Asymptotic Ring <m^ZZ> over Rational Field """ from asymptotic_ring import AsymptoticRing from sage.rings.rational_field import QQ A = AsymptoticRing(growth_group='{n}^ZZ'.format(n=var), coefficient_ring=QQ) if precision < 0: return A.zero() n = A.gen() from sage.arith.all import bernoulli from sage.arith.srange import srange result = sum((bernoulli(k) / k / (k - 1) / n**(k - 1) for k in srange(2, 2 * precision + 2, 2)), A.zero()) return result + (1 / n**(2 * precision + 1)).O()
def HarmonicNumber(var, precision=None, skip_constant_summand=False): r""" Return the asymptotic expansion of a harmonic number. INPUT: - ``var`` -- a string for the variable name. - ``precision`` -- (default: ``None``) an integer. If ``None``, then the default precision of the asymptotic ring is used. - ``skip_constant_summand`` -- (default: ``False``) a boolean. If set, then the constant summand ``euler_gamma`` is left out. As a consequence, the coefficient ring of the output changes from ``Symbolic Constants Subring`` (if ``False``) to ``Rational Field`` (if ``True``). OUTPUT: An asymptotic expansion. EXAMPLES:: sage: asymptotic_expansions.HarmonicNumber('n', precision=5) log(n) + euler_gamma + 1/2*n^(-1) - 1/12*n^(-2) + 1/120*n^(-4) + O(n^(-6)) TESTS:: sage: ex = asymptotic_expansions.HarmonicNumber('n', precision=5) sage: n = ex.parent().gen() sage: ex.compare_with_values(n, # rel tol 1e-6 ....: lambda x: sum(1/k for k in srange(1, x+1)), [5, 10, 20]) [(5, 0.0038125360?), (10, 0.00392733?), (20, 0.0039579?)] sage: asymptotic_expansions.HarmonicNumber('n') log(n) + euler_gamma + 1/2*n^(-1) - 1/12*n^(-2) + 1/120*n^(-4) - 1/252*n^(-6) + 1/240*n^(-8) - 1/132*n^(-10) + 691/32760*n^(-12) - 1/12*n^(-14) + 3617/8160*n^(-16) - 43867/14364*n^(-18) + 174611/6600*n^(-20) - 77683/276*n^(-22) + 236364091/65520*n^(-24) - 657931/12*n^(-26) + 3392780147/3480*n^(-28) - 1723168255201/85932*n^(-30) + 7709321041217/16320*n^(-32) - 151628697551/12*n^(-34) + O(n^(-36)) sage: _.parent() Asymptotic Ring <n^ZZ * log(n)^ZZ> over Symbolic Constants Subring :: sage: asymptotic_expansions.HarmonicNumber( ....: 'n', precision=5, skip_constant_summand=True) log(n) + 1/2*n^(-1) - 1/12*n^(-2) + 1/120*n^(-4) + O(n^(-6)) sage: _.parent() Asymptotic Ring <n^ZZ * log(n)^ZZ> over Rational Field sage: for p in range(5): ....: print asymptotic_expansions.HarmonicNumber( ....: 'n', precision=p) O(log(n)) log(n) + O(1) log(n) + euler_gamma + O(n^(-1)) log(n) + euler_gamma + 1/2*n^(-1) + O(n^(-2)) log(n) + euler_gamma + 1/2*n^(-1) - 1/12*n^(-2) + O(n^(-4)) sage: asymptotic_expansions.HarmonicNumber('m', precision=5) log(m) + euler_gamma + 1/2*m^(-1) - 1/12*m^(-2) + 1/120*m^(-4) + O(m^(-6)) """ if not skip_constant_summand: from sage.symbolic.ring import SR coefficient_ring = SR.subring(no_variables=True) else: from sage.rings.rational_field import QQ coefficient_ring = QQ from asymptotic_ring import AsymptoticRing A = AsymptoticRing(growth_group='{n}^ZZ * log({n})^ZZ'.format(n=var), coefficient_ring=coefficient_ring) n = A.gen() if precision is None: precision = A.default_prec from sage.functions.log import log result = A.zero() if precision >= 1: result += log(n) if precision >= 2 and not skip_constant_summand: from sage.symbolic.constants import euler_gamma result += coefficient_ring(euler_gamma) if precision >= 3: result += 1 / (2 * n) from sage.arith.srange import srange from sage.arith.all import bernoulli for k in srange(2, 2*precision - 4, 2): result += -bernoulli(k) / k / n**k if precision < 1: result += (log(n)).O() elif precision == 1: result += A(1).O() elif precision == 2: result += (1 / n).O() else: result += (1 / n**(2*precision - 4)).O() return result
def log_Stirling(var, precision=None, skip_constant_summand=False): r""" Return the logarithm of Stirling's approximation formula for factorials. INPUT: - ``var`` -- a string for the variable name. - ``precision`` -- (default: ``None``) an integer. If ``None``, then the default precision of the asymptotic ring is used. - ``skip_constant_summand`` -- (default: ``False``) a boolean. If set, then the constant summand `\log(2\pi)/2` is left out. As a consequence, the coefficient ring of the output changes from ``Symbolic Constants Subring`` (if ``False``) to ``Rational Field`` (if ``True``). OUTPUT: An asymptotic expansion. EXAMPLES:: sage: asymptotic_expansions.log_Stirling('n', precision=7) n*log(n) - n + 1/2*log(n) + 1/2*log(2*pi) + 1/12*n^(-1) - 1/360*n^(-3) + 1/1260*n^(-5) + O(n^(-7)) .. SEEALSO:: :meth:`Stirling`, :meth:`~sage.rings.asymptotic.asymptotic_ring.AsymptoticExpansion.factorial`. TESTS:: sage: expansion = asymptotic_expansions.log_Stirling('n', precision=7) sage: n = expansion.parent().gen() sage: expansion.compare_with_values(n, lambda x: x.factorial().log(), [5, 10, 20]) # rel tol 1e-6 [(5, 0.000564287?), (10, 0.0005870?), (20, 0.0006?)] sage: asymptotic_expansions.log_Stirling('n') n*log(n) - n + 1/2*log(n) + 1/2*log(2*pi) + 1/12*n^(-1) - 1/360*n^(-3) + 1/1260*n^(-5) - 1/1680*n^(-7) + 1/1188*n^(-9) - 691/360360*n^(-11) + 1/156*n^(-13) - 3617/122400*n^(-15) + 43867/244188*n^(-17) - 174611/125400*n^(-19) + 77683/5796*n^(-21) - 236364091/1506960*n^(-23) + 657931/300*n^(-25) - 3392780147/93960*n^(-27) + 1723168255201/2492028*n^(-29) - 7709321041217/505920*n^(-31) + O(n^(-33)) sage: _.parent() Asymptotic Ring <n^ZZ * log(n)^ZZ> over Symbolic Constants Subring :: sage: asymptotic_expansions.log_Stirling( ....: 'n', precision=7, skip_constant_summand=True) n*log(n) - n + 1/2*log(n) + 1/12*n^(-1) - 1/360*n^(-3) + 1/1260*n^(-5) + O(n^(-7)) sage: _.parent() Asymptotic Ring <n^ZZ * log(n)^ZZ> over Rational Field sage: asymptotic_expansions.log_Stirling( ....: 'n', precision=0) O(n*log(n)) sage: asymptotic_expansions.log_Stirling( ....: 'n', precision=1) n*log(n) + O(n) sage: asymptotic_expansions.log_Stirling( ....: 'n', precision=2) n*log(n) - n + O(log(n)) sage: asymptotic_expansions.log_Stirling( ....: 'n', precision=3) n*log(n) - n + 1/2*log(n) + O(1) sage: asymptotic_expansions.log_Stirling( ....: 'n', precision=4) n*log(n) - n + 1/2*log(n) + 1/2*log(2*pi) + O(n^(-1)) sage: asymptotic_expansions.log_Stirling( ....: 'n', precision=5) n*log(n) - n + 1/2*log(n) + 1/2*log(2*pi) + 1/12*n^(-1) + O(n^(-3)) sage: asymptotic_expansions.log_Stirling( ....: 'm', precision=7, skip_constant_summand=True) m*log(m) - m + 1/2*log(m) + 1/12*m^(-1) - 1/360*m^(-3) + 1/1260*m^(-5) + O(m^(-7)) """ if not skip_constant_summand: from sage.symbolic.ring import SR coefficient_ring = SR.subring(no_variables=True) else: from sage.rings.rational_field import QQ coefficient_ring = QQ from asymptotic_ring import AsymptoticRing A = AsymptoticRing(growth_group='{n}^ZZ * log({n})^ZZ'.format(n=var), coefficient_ring=coefficient_ring) n = A.gen() if precision is None: precision = AsymptoticRing.__default_prec__ from sage.functions.log import log result = A.zero() if precision >= 1: result += n * log(n) if precision >= 2: result += -n if precision >= 3: result += log(n) / 2 if precision >= 4 and not skip_constant_summand: result += log(2*coefficient_ring('pi')) / 2 result += AsymptoticExpansionGenerators._log_StirlingNegativePowers_( var, precision - 4) if precision < 1: result += (n * log(n)).O() elif precision == 1: result += n.O() elif precision == 2: result += log(n).O() elif precision == 3: result += A(1).O() return result
def HarmonicNumber(var, precision=None, skip_constant_summand=False): r""" Return the asymptotic expansion of a harmonic number. INPUT: - ``var`` -- a string for the variable name. - ``precision`` -- (default: ``None``) an integer. If ``None``, then the default precision of the asymptotic ring is used. - ``skip_constant_summand`` -- (default: ``False``) a boolean. If set, then the constant summand ``euler_gamma`` is left out. As a consequence, the coefficient ring of the output changes from ``Symbolic Constants Subring`` (if ``False``) to ``Rational Field`` (if ``True``). OUTPUT: An asymptotic expansion. EXAMPLES:: sage: asymptotic_expansions.HarmonicNumber('n', precision=5) log(n) + euler_gamma + 1/2*n^(-1) - 1/12*n^(-2) + 1/120*n^(-4) + O(n^(-6)) TESTS:: sage: ex = asymptotic_expansions.HarmonicNumber('n', precision=5) sage: n = ex.parent().gen() sage: ex.compare_with_values(n, # rel tol 1e-6 ....: lambda x: sum(1/k for k in srange(1, x+1)), [5, 10, 20]) [(5, 0.0038125360?), (10, 0.00392733?), (20, 0.0039579?)] sage: asymptotic_expansions.HarmonicNumber('n') log(n) + euler_gamma + 1/2*n^(-1) - 1/12*n^(-2) + 1/120*n^(-4) - 1/252*n^(-6) + 1/240*n^(-8) - 1/132*n^(-10) + 691/32760*n^(-12) - 1/12*n^(-14) + 3617/8160*n^(-16) - 43867/14364*n^(-18) + 174611/6600*n^(-20) - 77683/276*n^(-22) + 236364091/65520*n^(-24) - 657931/12*n^(-26) + 3392780147/3480*n^(-28) - 1723168255201/85932*n^(-30) + 7709321041217/16320*n^(-32) - 151628697551/12*n^(-34) + O(n^(-36)) sage: _.parent() Asymptotic Ring <n^ZZ * log(n)^ZZ> over Symbolic Constants Subring :: sage: asymptotic_expansions.HarmonicNumber( ....: 'n', precision=5, skip_constant_summand=True) log(n) + 1/2*n^(-1) - 1/12*n^(-2) + 1/120*n^(-4) + O(n^(-6)) sage: _.parent() Asymptotic Ring <n^ZZ * log(n)^ZZ> over Rational Field sage: for p in range(5): ....: print asymptotic_expansions.HarmonicNumber( ....: 'n', precision=p) O(log(n)) log(n) + O(1) log(n) + euler_gamma + O(n^(-1)) log(n) + euler_gamma + 1/2*n^(-1) + O(n^(-2)) log(n) + euler_gamma + 1/2*n^(-1) - 1/12*n^(-2) + O(n^(-4)) sage: asymptotic_expansions.HarmonicNumber('m', precision=5) log(m) + euler_gamma + 1/2*m^(-1) - 1/12*m^(-2) + 1/120*m^(-4) + O(m^(-6)) """ if not skip_constant_summand: from sage.symbolic.ring import SR coefficient_ring = SR.subring(no_variables=True) else: from sage.rings.rational_field import QQ coefficient_ring = QQ from asymptotic_ring import AsymptoticRing A = AsymptoticRing(growth_group='{n}^ZZ * log({n})^ZZ'.format(n=var), coefficient_ring=coefficient_ring) n = A.gen() if precision is None: precision = A.default_prec from sage.functions.log import log result = A.zero() if precision >= 1: result += log(n) if precision >= 2 and not skip_constant_summand: from sage.symbolic.constants import euler_gamma result += coefficient_ring(euler_gamma) if precision >= 3: result += 1 / (2 * n) from sage.arith.srange import srange from sage.arith.all import bernoulli for k in srange(2, 2 * precision - 4, 2): result += -bernoulli(k) / k / n**k if precision < 1: result += (log(n)).O() elif precision == 1: result += A(1).O() elif precision == 2: result += (1 / n).O() else: result += (1 / n**(2 * precision - 4)).O() return result
def log_Stirling(var, precision=None, skip_constant_summand=False): r""" Return the logarithm of Stirling's approximation formula for factorials. INPUT: - ``var`` -- a string for the variable name. - ``precision`` -- (default: ``None``) an integer. If ``None``, then the default precision of the asymptotic ring is used. - ``skip_constant_summand`` -- (default: ``False``) a boolean. If set, then the constant summand `\log(2\pi)/2` is left out. As a consequence, the coefficient ring of the output changes from ``Symbolic Constants Subring`` (if ``False``) to ``Rational Field`` (if ``True``). OUTPUT: An asymptotic expansion. EXAMPLES:: sage: asymptotic_expansions.log_Stirling('n', precision=7) n*log(n) - n + 1/2*log(n) + 1/2*log(2*pi) + 1/12*n^(-1) - 1/360*n^(-3) + 1/1260*n^(-5) + O(n^(-7)) .. SEEALSO:: :meth:`Stirling`, :meth:`~sage.rings.asymptotic.asymptotic_ring.AsymptoticExpansion.factorial`. TESTS:: sage: expansion = asymptotic_expansions.log_Stirling('n', precision=7) sage: n = expansion.parent().gen() sage: expansion.compare_with_values(n, lambda x: x.factorial().log(), [5, 10, 20]) # rel tol 1e-6 [(5, 0.000564287?), (10, 0.0005870?), (20, 0.0006?)] sage: asymptotic_expansions.log_Stirling('n') n*log(n) - n + 1/2*log(n) + 1/2*log(2*pi) + 1/12*n^(-1) - 1/360*n^(-3) + 1/1260*n^(-5) - 1/1680*n^(-7) + 1/1188*n^(-9) - 691/360360*n^(-11) + 1/156*n^(-13) - 3617/122400*n^(-15) + 43867/244188*n^(-17) - 174611/125400*n^(-19) + 77683/5796*n^(-21) - 236364091/1506960*n^(-23) + 657931/300*n^(-25) - 3392780147/93960*n^(-27) + 1723168255201/2492028*n^(-29) - 7709321041217/505920*n^(-31) + O(n^(-33)) sage: _.parent() Asymptotic Ring <n^ZZ * log(n)^ZZ> over Symbolic Constants Subring :: sage: asymptotic_expansions.log_Stirling( ....: 'n', precision=7, skip_constant_summand=True) n*log(n) - n + 1/2*log(n) + 1/12*n^(-1) - 1/360*n^(-3) + 1/1260*n^(-5) + O(n^(-7)) sage: _.parent() Asymptotic Ring <n^ZZ * log(n)^ZZ> over Rational Field sage: asymptotic_expansions.log_Stirling( ....: 'n', precision=0) O(n*log(n)) sage: asymptotic_expansions.log_Stirling( ....: 'n', precision=1) n*log(n) + O(n) sage: asymptotic_expansions.log_Stirling( ....: 'n', precision=2) n*log(n) - n + O(log(n)) sage: asymptotic_expansions.log_Stirling( ....: 'n', precision=3) n*log(n) - n + 1/2*log(n) + O(1) sage: asymptotic_expansions.log_Stirling( ....: 'n', precision=4) n*log(n) - n + 1/2*log(n) + 1/2*log(2*pi) + O(n^(-1)) sage: asymptotic_expansions.log_Stirling( ....: 'n', precision=5) n*log(n) - n + 1/2*log(n) + 1/2*log(2*pi) + 1/12*n^(-1) + O(n^(-3)) sage: asymptotic_expansions.log_Stirling( ....: 'm', precision=7, skip_constant_summand=True) m*log(m) - m + 1/2*log(m) + 1/12*m^(-1) - 1/360*m^(-3) + 1/1260*m^(-5) + O(m^(-7)) """ if not skip_constant_summand: from sage.symbolic.ring import SR coefficient_ring = SR.subring(no_variables=True) else: from sage.rings.rational_field import QQ coefficient_ring = QQ from asymptotic_ring import AsymptoticRing A = AsymptoticRing(growth_group='{n}^ZZ * log({n})^ZZ'.format(n=var), coefficient_ring=coefficient_ring) n = A.gen() if precision is None: precision = AsymptoticRing.__default_prec__ from sage.functions.log import log result = A.zero() if precision >= 1: result += n * log(n) if precision >= 2: result += -n if precision >= 3: result += log(n) / 2 if precision >= 4 and not skip_constant_summand: result += log(2 * coefficient_ring('pi')) / 2 result += AsymptoticExpansionGenerators._log_StirlingNegativePowers_( var, precision - 4) if precision < 1: result += (n * log(n)).O() elif precision == 1: result += n.O() elif precision == 2: result += log(n).O() elif precision == 3: result += A(1).O() return result