def _eval_nseries(self, x, n, logx): # NOTE Please see the comment at the beginning of this file, labelled # IMPORTANT. from sympy import cancel if not logx: logx = log(x) if self.args[0] == x: return logx arg = self.args[0] k, l = Wild("k"), Wild("l") r = arg.match(k * x**l) if r is not None: #k = r.get(r, S.One) #l = r.get(l, S.Zero) k, l = r[k], r[l] if l != 0 and not l.has(x) and not k.has(x): r = log(k) + l * logx # XXX true regardless of assumptions? return r # TODO new and probably slow s = self.args[0].nseries(x, n=n, logx=logx) while s.is_Order: n += 1 s = self.args[0].nseries(x, n=n, logx=logx) a, b = s.leadterm(x) p = cancel(s / (a * x**b) - 1) g = None l = [] for i in xrange(n + 2): g = log.taylor_term(i, p, g) g = g.nseries(x, n=n, logx=logx) l.append(g) return log(a) + b * logx + Add(*l) + C.Order(p**n, x)
def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if x in arg.free_symbols and C.Order(1, x).contains(arg): return 2 * x / sqrt(pi) else: return self.func(arg)
def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if x in arg.free_symbols and C.Order(1, x).contains(arg): return S.ImaginaryUnit * S.Pi / 2 else: return self.func(arg)
def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if C.Order(1, x).contains(arg): return arg else: return self.func(arg)
def _eval_as_leading_term(self, x): n, z = [a.as_leading_term(x) for a in self.args] o = C.Order(z, x) if n == 0 and o.contains(1 / x): return o.getn() * log(x) else: return self.func(n, z)
def _taylor(self, x, n): l = [] g = None for i in xrange(n): g = self.taylor_term(i, self.args[0], g) g = g.nseries(x, n=n) l.append(g) return Add(*l) + C.Order(x**n, x)
def _eval_aseries(self, n, args0, x, logx): if args0[0] != oo: return super(loggamma, self)._eval_aseries(n, args0, x, logx) z = self.args[0] m = min(n, C.ceiling((n + S(1)) / 2)) r = log(z) * (z - S(1) / 2) - z + log(2 * pi) / 2 l = [ bernoulli(2 * k) / (2 * k * (2 * k - 1) * z**(2 * k - 1)) for k in range(1, m) ] o = None if m == 0: o = C.Order(1, x) else: o = C.Order(1 / z**(2 * m - 1), 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_as_leading_term(self, x): arg = self.args[0] if arg.is_Add: return Mul(*[exp(f).as_leading_term(x) for f in arg.args]) arg = self.args[0].as_leading_term(x) if C.Order(1, x).contains(arg): return S.One return exp(arg)
def doit(self, **hints): """Evaluates limit""" e, z, z0, dir = self.args if hints.get('deep', True): e = e.doit(**hints) z = z.doit(**hints) z0 = z0.doit(**hints) if e == z: return z0 if not e.has(z): return e # gruntz fails on factorials but works with the gamma function # If no factorial term is present, e should remain unchanged. # factorial is defined to be zero for negative inputs (which # differs from gamma) so only rewrite for positive z0. if z0.is_positive: e = e.rewrite(factorial, gamma) if e.is_Mul: if abs(z0) is S.Infinity: # XXX todo: this should probably be stated in the # negative -- i.e. to exclude expressions that should # not be handled this way but I'm not sure what that # condition is; when ok is True it means that the leading # term approach is going to succeed (hopefully) ok = lambda w: (z in w.free_symbols and any(a.is_polynomial(z) or any(z in m.free_symbols and m.is_polynomial(z) for m in Mul.make_args(a)) for a in Add.make_args(w))) if all(ok(w) for w in e.as_numer_denom()): u = C.Dummy(positive=(z0 is S.Infinity)) inve = e.subs(z, 1/u) r = limit(inve.as_leading_term(u), u, S.Zero, "+" if z0 is S.Infinity else "-") if isinstance(r, Limit): return self else: return r if e.is_Order: return C.Order(limit(e.expr, z, z0), *e.args[1:]) try: r = gruntz(e, z, z0, dir) if r is S.NaN: raise PoleError() except (PoleError, ValueError): r = heuristics(e, z, z0, dir) if r is None: return self return r
def _eval_aseries(self, n, args0, x, logx): if args0[1] != oo or not \ (self.args[0].is_Integer and self.args[0].is_nonnegative): return super(polygamma, self)._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 = C.Order(1 / z, x) else: m = C.ceiling((n + 1) / 2) l = [ bernoulli(2 * k) / (2 * k * z**(2 * k)) for k in range(1, m) ] r -= Add(*l) o = C.Order(1 / z**(2 * m), 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 = C.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 = C.Order(1 / z**(2 * m), x) if n == 0: o = C.Order(1 / z, x) elif n == 1: o = C.Order(1 / z**2, x) r = e0._eval_nseries(z, n, logx) + o return -1 * (-1 / z)**N * r
def _eval_aseries(self, n, args0, x, logx): if args0[0] != S.Infinity: return super(_erfs, self)._eval_aseries(n, args0, x, logx) z = self.args[0] l = [ 1/sqrt(S.Pi) * C.factorial(2*k)*(-S( 4))**(-k)/C.factorial(k) * (1/z)**(2*k + 1) for k in xrange(0, n) ] o = C.Order(1/z**(2*n + 1), x) # It is very inefficient to first add the order and then do the nseries return (Add(*l))._eval_nseries(x, n, logx) + o
def _eval_nseries(self, x, n, logx): # NOTE Please see the comment at the beginning of this file, labelled # IMPORTANT. from sympy import limit, oo, powsimp arg = self.args[0] arg_series = arg._eval_nseries(x, n=n, logx=logx) if arg_series.is_Order: return 1 + arg_series arg0 = limit(arg_series.removeO(), x, 0) if arg0 in [-oo, oo]: return self t = Dummy("t") exp_series = exp(t)._taylor(t, n) o = exp_series.getO() exp_series = exp_series.removeO() r = exp(arg0)*exp_series.subs(t, arg_series - arg0) r += C.Order(o.expr.subs(t, (arg_series - arg0)), x) r = r.expand() return powsimp(r, deep=True, combine='exp')
def limit(e, z, z0, dir="+"): """ Compute the limit of e(z) at the point z0. z0 can be any expression, including oo and -oo. For dir="+" (default) it calculates the limit from the right (z->z0+) and for dir="-" the limit from the left (z->z0-). For infinite z0 (oo or -oo), the dir argument doesn't matter. Examples ======== >>> from sympy import limit, sin, Symbol, oo >>> from sympy.abc import x >>> limit(sin(x)/x, x, 0) 1 >>> limit(1/x, x, 0, dir="+") oo >>> limit(1/x, x, 0, dir="-") -oo >>> limit(1/x, x, oo) 0 Notes ===== First we try some heuristics for easy and frequent cases like "x", "1/x", "x**2" and similar, so that it's fast. For all other cases, we use the Gruntz algorithm (see the gruntz() function). """ e = sympify(e) z = sympify(z) z0 = sympify(z0) if e == z: return z0 if not e.has(z): return e # gruntz fails on factorials but works with the gamma function # If no factorial term is present, e should remain unchanged. # factorial is defined to be zero for negative inputs (which # differs from gamma) so only rewrite for positive z0. if z0.is_positive: e = e.rewrite(factorial, gamma) if e.is_Mul: if abs(z0) is S.Infinity: # XXX todo: this should probably be stated in the # negative -- i.e. to exclude expressions that should # not be handled this way but I'm not sure what that # condition is; when ok is True it means that the leading # term approach is going to succeed (hopefully) ok = lambda w: (z in w.free_symbols and any( a.is_polynomial(z) or any( z in m.free_symbols and m.is_polynomial(z) for m in Mul.make_args(a)) for a in Add.make_args(w))) if all(ok(w) for w in e.as_numer_denom()): u = C.Dummy(positive=(z0 is S.Infinity)) inve = e.subs(z, 1 / u) return limit(inve.as_leading_term(u), u, S.Zero, "+" if z0 is S.Infinity else "-") if e.is_Order: return C.Order(limit(e.expr, z, z0), *e.args[1:]) try: r = gruntz(e, z, z0, dir) if r is S.NaN: raise PoleError() except (PoleError, ValueError): r = heuristics(e, z, z0, dir) return r
def limit(e, z, z0, dir="+"): """ Compute the limit of e(z) at the point z0. z0 can be any expression, including oo and -oo. For dir="+" (default) it calculates the limit from the right (z->z0+) and for dir="-" the limit from the left (z->z0-). For infinite z0 (oo or -oo), the dir argument doesn't matter. Examples ======== >>> from sympy import limit, sin, Symbol, oo >>> from sympy.abc import x >>> limit(sin(x)/x, x, 0) 1 >>> limit(1/x, x, 0, dir="+") oo >>> limit(1/x, x, 0, dir="-") -oo >>> limit(1/x, x, oo) 0 Notes ===== First we try some heuristics for easy and frequent cases like "x", "1/x", "x**2" and similar, so that it's fast. For all other cases, we use the Gruntz algorithm (see the gruntz() function). """ from sympy import Wild, log e = sympify(e) z = sympify(z) z0 = sympify(z0) if e == z: return z0 if e.is_Rational: return e if not e.has(z): return e # gruntz fails on factorials but works with the gamma function # If no factorial term is present, e should remain unchanged. # factorial is defined to be zero for negative inputs (which # differs from gamma) so only rewrite for positive z0. if z0.is_positive: e = e.rewrite(factorial, gamma) if e.func is tan: # discontinuity at odd multiples of pi/2; 0 at even disc = S.Pi/2 sign = 1 if dir == '-': sign *= -1 i = limit(sign*e.args[0], z, z0)/disc if i.is_integer: if i.is_even: return S.Zero elif i.is_odd: if dir == '+': return S.NegativeInfinity else: return S.Infinity if e.func is cot: # discontinuity at multiples of pi; 0 at odd pi/2 multiples disc = S.Pi sign = 1 if dir == '-': sign *= -1 i = limit(sign*e.args[0], z, z0)/disc if i.is_integer: if dir == '-': return S.NegativeInfinity else: return S.Infinity elif (2*i).is_integer: return S.Zero if e.is_Pow: b, ex = e.args c = None # records sign of b if b is +/-z or has a bounded value if b.is_Mul: c, b = b.as_two_terms() if c is S.NegativeOne and b == z: c = '-' elif b == z: c = '+' if ex.is_number: if c is None: base = b.subs(z, z0) if base != 0 and (ex.is_bounded or base is not S.One): return base**ex else: if z0 == 0 and ex < 0: if dir != c: # integer if ex.is_even: return S.Infinity elif ex.is_odd: return S.NegativeInfinity # rational elif ex.is_Rational: return (S.NegativeOne**ex)*S.Infinity else: return S.ComplexInfinity return S.Infinity return z0**ex if e.is_Mul or not z0 and e.is_Pow and b.func is log: if e.is_Mul: if abs(z0) is S.Infinity: n, d = e.as_numer_denom() # XXX todo: this should probably be stated in the # negative -- i.e. to exclude expressions that should # not be handled this way but I'm not sure what that # condition is; when ok is True it means that the leading # term approach is going to succeed (hopefully) ok = lambda w: (z in w.free_symbols and any(a.is_polynomial(z) or any(z in m.free_symbols and m.is_polynomial(z) for m in Mul.make_args(a)) for a in Add.make_args(w))) if all(ok(w) for w in (n, d)): u = C.Dummy(positive=(z0 is S.Infinity)) inve = (n/d).subs(z, 1/u) return limit(inve.as_leading_term(u), u, S.Zero, "+" if z0 is S.Infinity else "-") # weed out the z-independent terms i, d = e.as_independent(z) if i is not S.One and i.is_bounded: return i*limit(d, z, z0, dir) else: i, d = S.One, e if not z0: # look for log(z)**q or z**p*log(z)**q p, q = Wild("p"), Wild("q") r = d.match(z**p * log(z)**q) if r: p, q = [r.get(w, w) for w in [p, q]] if q and q.is_number and p.is_number: if q > 0: if p > 0: return S.Zero else: return -oo*i else: if p >= 0: return S.Zero else: return -oo*i if e.is_Add: if e.is_polynomial(): if not z0.is_unbounded: return Add(*[limit(term, z, z0, dir) for term in e.args]) elif e.is_rational_function(z): rval = Add(*[limit(term, z, z0, dir) for term in e.args]) if rval != S.NaN: return rval if not any([a.is_unbounded for a in e.args]): e = e.normal() # workaround for issue 3744 if e.is_Order: args = e.args return C.Order(limit(args[0], z, z0), *args[1:]) try: r = gruntz(e, z, z0, dir) if r is S.NaN: raise PoleError() except (PoleError, ValueError): r = heuristics(e, z, z0, dir) return r
def _eval_nseries(self, x, x0, n): from sympy import powsimp arg = self.args[0] k, l = Wild("k"), Wild("l") r = arg.match(k * x**l) if r is not None: k, l = r[k], r[l] if l != 0 and not l.has(x) and not k.has(x): r = log(k) + l * log(x) return r order = C.Order(x**n, x) arg = self.args[0] x = order.symbols[0] ln = C.log use_lt = not C.Order(1, x).contains(arg) if not use_lt: arg0 = arg.limit(x, 0) use_lt = (arg0 is S.Zero) if use_lt: # singularity, #example: self = log(sin(x)) # arg = (arg / lt) * lt lt = arg.as_leading_term(x) # arg = sin(x); lt = x a = powsimp((arg / lt).expand(), deep=True, combine='exp') # a = sin(x)/x # the idea is to recursively call ln(a).series(), but one needs to # make sure that ln(sin(x)/x) doesn't get "simplified" to # -log(x)+ln(sin(x)) and an infinite recursion occurs, see also the # issue 252. obj = ln(lt) + ln(a)._eval_nseries(x, x0, n) else: # arg -> arg0 + (arg - arg0) -> arg0 * (1 + (arg/arg0 - 1)) z = (arg / arg0 - 1) x = order.symbols[0] ln = C.log o = C.Order(z, x) if o is S.Zero: return ln(1 + z) + ln(arg0) if o.expr.is_number: e = ln(order.expr * x) / ln(x) else: e = ln(order.expr) / ln(o.expr) n = e.limit(x, 0) + 1 if n.is_unbounded: # requested accuracy gives infinite series, # order is probably nonpolynomial e.g. O(exp(-1/x), x). return ln(1 + z) + ln(arg0) try: n = int(n) except TypeError: #well, the n is something more complicated (like 1+log(2)) n = int(n.evalf()) + 1 assert n >= 0, ` n ` l = [] g = None for i in xrange(n + 2): g = ln.taylor_term(i, z, g) g = g.nseries(x, x0, n) l.append(g) obj = C.Add(*l) + ln(arg0) obj2 = expand_log(powsimp(obj, deep=True, combine='exp')) if obj2 != obj: r = obj2.nseries(x, x0, n) else: r = obj if r == self: return self return r + order
def limit(e, z, z0, dir="+"): """ Compute the limit of e(z) at the point z0. z0 can be any expression, including oo and -oo. For dir="+" (default) it calculates the limit from the right (z->z0+) and for dir="-" the limit from the left (z->z0-). For infinite z0 (oo or -oo), the dir argument doesn't matter. Examples ======== >>> from sympy import limit, sin, Symbol, oo >>> from sympy.abc import x >>> limit(sin(x)/x, x, 0) 1 >>> limit(1/x, x, 0, dir="+") oo >>> limit(1/x, x, 0, dir="-") -oo >>> limit(1/x, x, oo) 0 Notes ===== First we try some heuristics for easy and frequent cases like "x", "1/x", "x**2" and similar, so that it's fast. For all other cases, we use the Gruntz algorithm (see the gruntz() function). """ from sympy import Wild, log e = sympify(e) z = sympify(z) z0 = sympify(z0) if e == z: return z0 if e.is_Rational: return e if not e.has(z): return e if e.func is tan: # discontinuity at odd multiples of pi/2; 0 at even disc = S.Pi / 2 sign = 1 if dir == '-': sign *= -1 i = limit(sign * e.args[0], z, z0) / disc if i.is_integer: if i.is_even: return S.Zero elif i.is_odd: if dir == '+': return S.NegativeInfinity else: return S.Infinity if e.func is cot: # discontinuity at multiples of pi; 0 at odd pi/2 multiples disc = S.Pi sign = 1 if dir == '-': sign *= -1 i = limit(sign * e.args[0], z, z0) / disc if i.is_integer: if dir == '-': return S.NegativeInfinity else: return S.Infinity elif (2 * i).is_integer: return S.Zero if e.is_Pow: b, ex = e.args c = None # records sign of b if b is +/-z or has a bounded value if b.is_Mul: c, b = b.as_two_terms() if c is S.NegativeOne and b == z: c = '-' elif b == z: c = '+' if ex.is_number: if c is None: base = b.subs(z, z0) if base.is_bounded and (ex.is_bounded or base is not S.One): return base**ex else: if z0 == 0 and ex < 0: if dir != c: # integer if ex.is_even: return S.Infinity elif ex.is_odd: return S.NegativeInfinity # rational elif ex.is_Rational: return (S.NegativeOne**ex) * S.Infinity else: return S.ComplexInfinity return S.Infinity return z0**ex if e.is_Mul or not z0 and e.is_Pow and b.func is log: if e.is_Mul: # weed out the z-independent terms i, d = e.as_independent(z) if i is not S.One and i.is_bounded: return i * limit(d, z, z0, dir) else: i, d = S.One, e if not z0: # look for log(z)**q or z**p*log(z)**q p, q = Wild("p"), Wild("q") r = d.match(z**p * log(z)**q) if r: p, q = [r.get(w, w) for w in [p, q]] if q and q.is_number and p.is_number: if q > 0: if p > 0: return S.Zero else: return -oo * i else: if p >= 0: return S.Zero else: return -oo * i if e.is_Add: if e.is_polynomial() and not z0.is_unbounded: return Add(*[limit(term, z, z0, dir) for term in e.args]) # this is a case like limit(x*y+x*z, z, 2) == x*y+2*x # but we need to make sure, that the general gruntz() algorithm is # executed for a case like "limit(sqrt(x+1)-sqrt(x),x,oo)==0" unbounded = [] unbounded_result = [] finite = [] unknown = [] ok = True for term in e.args: if not term.has(z) and not term.is_unbounded: finite.append(term) continue result = term.subs(z, z0) bounded = result.is_bounded if bounded is False or result is S.NaN: if unknown: ok = False break unbounded.append(term) if result != S.NaN: # take result from direction given result = limit(term, z, z0, dir) unbounded_result.append(result) elif bounded: finite.append(result) else: if unbounded: ok = False break unknown.append(result) if not ok: # we won't be able to resolve this with unbounded # terms, e.g. Sum(1/k, (k, 1, n)) - log(n) as n -> oo: # since the Sum is unevaluated it's boundedness is # unknown and the log(n) is oo so you get Sum - oo # which is unsatisfactory. raise NotImplementedError('unknown boundedness for %s' % (unknown or result)) u = Add(*unknown) if unbounded: inf_limit = Add(*unbounded_result) if inf_limit is not S.NaN: return inf_limit + u if finite: return Add(*finite) + limit(Add(*unbounded), z, z0, dir) + u else: return Add(*finite) + u if e.is_Order: args = e.args return C.Order(limit(args[0], z, z0), *args[1:]) try: r = gruntz(e, z, z0, dir) if r is S.NaN: raise PoleError() except PoleError: r = heuristics(e, z, z0, dir) return r
def limit(e, z, z0, dir="+"): """ Compute the limit of e(z) at the point z0. z0 can be any expression, including oo and -oo. For dir="+" (default) it calculates the limit from the right (z->z0+) and for dir="-" the limit from the left (z->z0-). For infinite z0 (oo or -oo), the dir argument doesn't matter. Examples ======== >>> from sympy import limit, sin, Symbol, oo >>> from sympy.abc import x >>> limit(sin(x)/x, x, 0) 1 >>> limit(1/x, x, 0, dir="+") oo >>> limit(1/x, x, 0, dir="-") -oo >>> limit(1/x, x, oo) 0 Notes ===== First we try some heuristics for easy and frequent cases like "x", "1/x", "x**2" and similar, so that it's fast. For all other cases, we use the Gruntz algorithm (see the gruntz() function). """ from sympy import Wild, log e = sympify(e) z = sympify(z) z0 = sympify(z0) if e == z: return z0 if e.is_Rational: return e if not e.has(z): return e # gruntz fails on factorials but works with the gamma function # If no factorial term is present, e should remain unchanged. # factorial is defined to be zero for negative inputs (which # differs from gamma) so only rewrite for positive z0. if z0.is_positive: e = e.rewrite(factorial, gamma) if e.func is tan: # discontinuity at odd multiples of pi/2; 0 at even disc = S.Pi / 2 sign = 1 if dir == '-': sign *= -1 i = limit(sign * e.args[0], z, z0) / disc if i.is_integer: if i.is_even: return S.Zero elif i.is_odd: if dir == '+': return S.NegativeInfinity else: return S.Infinity if e.func is cot: # discontinuity at multiples of pi; 0 at odd pi/2 multiples disc = S.Pi sign = 1 if dir == '-': sign *= -1 i = limit(sign * e.args[0], z, z0) / disc if i.is_integer: if dir == '-': return S.NegativeInfinity else: return S.Infinity elif (2 * i).is_integer: return S.Zero if e.is_Pow: b, ex = e.args c = None # records sign of b if b is +/-z or has a bounded value if b.is_Mul: c, b = b.as_two_terms() if c is S.NegativeOne and b == z: c = '-' elif b == z: c = '+' if ex.is_number: if c is None: base = b.subs(z, z0) if base.is_finite and (ex.is_bounded or base is not S.One): return base**ex else: if z0 == 0 and ex < 0: if dir != c: # integer if ex.is_even: return S.Infinity elif ex.is_odd: return S.NegativeInfinity # rational elif ex.is_Rational: return (S.NegativeOne**ex) * S.Infinity else: return S.ComplexInfinity return S.Infinity return z0**ex if e.is_Mul or not z0 and e.is_Pow and b.func is log: if e.is_Mul: if abs(z0) is S.Infinity: n, d = e.as_numer_denom() # XXX todo: this should probably be stated in the # negative -- i.e. to exclude expressions that should # not be handled this way but I'm not sure what that # condition is; when ok is True it means that the leading # term approach is going to succeed (hopefully) ok = lambda w: (z in w.free_symbols and any( a.is_polynomial(z) or any( z in m.free_symbols and m.is_polynomial(z) for m in Mul.make_args(a)) for a in Add.make_args(w))) if all(ok(w) for w in (n, d)): u = C.Dummy(positive=(z0 is S.Infinity)) inve = (n / d).subs(z, 1 / u) return limit(inve.as_leading_term(u), u, S.Zero, "+" if z0 is S.Infinity else "-") # weed out the z-independent terms i, d = e.as_independent(z) if i is not S.One and i.is_bounded: return i * limit(d, z, z0, dir) else: i, d = S.One, e if not z0: # look for log(z)**q or z**p*log(z)**q p, q = Wild("p"), Wild("q") r = d.match(z**p * log(z)**q) if r: p, q = [r.get(w, w) for w in [p, q]] if q and q.is_number and p.is_number: if q > 0: if p > 0: return S.Zero else: return -oo * i else: if p >= 0: return S.Zero else: return -oo * i if e.is_Add: if e.is_polynomial() and not z0.is_unbounded: return Add(*[limit(term, z, z0, dir) for term in e.args]) # this is a case like limit(x*y+x*z, z, 2) == x*y+2*x # but we need to make sure, that the general gruntz() algorithm is # executed for a case like "limit(sqrt(x+1)-sqrt(x),x,oo)==0" unbounded = [] unbounded_result = [] unbounded_const = [] unknown = [] unknown_result = [] finite = [] zero = [] def _sift(term): if z not in term.free_symbols: if term.is_unbounded: unbounded_const.append(term) else: finite.append(term) else: result = term.subs(z, z0) bounded = result.is_bounded if bounded is False or result is S.NaN: unbounded.append(term) if result != S.NaN: # take result from direction given result = limit(term, z, z0, dir) unbounded_result.append(result) elif bounded: if result: finite.append(result) else: zero.append(term) else: unknown.append(term) unknown_result.append(result) for term in e.args: _sift(term) bad = bool(unknown and unbounded) if bad or len(unknown) > 1 or len(unbounded) > 1 and not zero: uu = unknown + unbounded # we won't be able to resolve this with unbounded # terms, e.g. Sum(1/k, (k, 1, n)) - log(n) as n -> oo: # since the Sum is unevaluated it's boundedness is # unknown and the log(n) is oo so you get Sum - oo # which is unsatisfactory. BUT...if there are both # unknown and unbounded terms (condition 'bad') or # there are multiple terms that are unknown, or # there are multiple symbolic unbounded terms they may # respond better if they are made into a rational # function, so give them a chance to do so before # reporting failure. u = Add(*uu) f = u.normal() if f != u: unknown = [] unbounded = [] unbounded_result = [] unknown_result = [] _sift(limit(f, z, z0, dir)) # We came in with a) unknown and unbounded terms or b) had multiple # unknown terms # At this point we've done one of 3 things. # (1) We did nothing with f so we now report the error # showing the troublesome terms which are now in uu. OR # (2) We did something with f but the result came back as unknown. # Normally this wouldn't be a problem, # but we had either multiple terms that were troublesome (unk and # unbounded or multiple unknown terms) so if we # weren't able to resolve the boundedness by now, that indicates a # problem so we report the error showing the troublesome terms which are # now in uu. if unknown: if bad: msg = 'unknown and unbounded terms present in %s' elif unknown: msg = 'multiple terms with unknown boundedness in %s' raise NotImplementedError(msg % uu) # OR # (3) the troublesome terms have been identified as finite or unbounded # and we proceed with the non-error code since the lists have been updated. u = Add(*unknown_result) if unbounded_result or unbounded_const: unbounded.extend(zero) inf_limit = Add(*(unbounded_result + unbounded_const)) if inf_limit is not S.NaN: return inf_limit + u if finite: return Add(*finite) + limit(Add(*unbounded), z, z0, dir) + u else: return Add(*finite) + u if e.is_Order: args = e.args return C.Order(limit(args[0], z, z0), *args[1:]) try: r = gruntz(e, z, z0, dir) if r is S.NaN: raise PoleError() except (PoleError, ValueError): r = heuristics(e, z, z0, dir) return r