def heuristics(e, z, z0, dir): if abs(z0) is S.Infinity: return limit(e.subs(z, 1 / z), z, S.Zero, "+" if z0 is S.Infinity else "-") rv = None bad = (S.NaN, None) if e.is_Mul or e.is_Add or e.is_Pow or e.is_Function: r = [] for a in e.args: try: r.append(limit(a, z, z0, dir)) except PoleError: break if r[-1] in bad: break else: if r: rv = e.func(*r) if rv in bad: msg = "Don't know how to calculate the limit(%s, %s, %s, dir=%s), sorry." raise PoleError(msg % (e, z, z0, dir)) return rv
def _check_series_var(p, x, name): index = p.ring.gens.index(x) m = min(p, key=lambda k: k[index])[index] if m < 0: raise PoleError("Asymptotic expansion of %s around [oo] not " "implemented." % name) return index, m
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 heuristics(e, z, z0, dir): if z0 == oo: return limit(e.subs(z, 1 / z), z, sympify(0), "+") elif e.is_Mul: r = [] for a in e.args: if not a.is_bounded: r.append(a.limit(z, z0, dir)) if r: return Mul(*r) elif e.is_Add: r = [] for a in e.args: r.append(a.limit(z, z0, dir)) return Add(*r) elif e.is_Function: return e.subs(e.args[0], limit(e.args[0], z, z0, dir)) msg = "Don't know how to calculate the limit(%s, %s, %s, dir=%s), sorry." raise PoleError(msg % (e, z, z0, dir))
def heuristics(e, z, z0, dir): if abs(z0) is S.Infinity: return limit(e.subs(z, 1/z), z, S.Zero, "+" if z0 is S.Infinity else "-") rv = None bad = (S.Infinity, S.NegativeInfinity, S.NaN, None) if e.is_Mul: r = [] for a in e.args: if not a.is_bounded: r.append(a.limit(z, z0, dir)) if r[-1] in bad: break else: if r: rv = Mul(*r) if rv is None and (e.is_Add or e.is_Pow or e.is_Function): rv = e.func(*[limit(a, z, z0, dir) for a in e.args]) if rv in bad: msg = "Don't know how to calculate the limit(%s, %s, %s, dir=%s), sorry." raise PoleError(msg % (e, z, z0, dir)) return rv
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). """ 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 doit(self, **hints): """Evaluates the limit. Parameters ========== deep : bool, optional (default: True) Invoke the ``doit`` method of the expressions involved before taking the limit. hints : optional keyword arguments To be passed to ``doit`` methods; only used if deep is True. """ e, z, z0, dir = self.args if z0 is S.ComplexInfinity: raise NotImplementedError("Limits at complex " "infinity are not implemented") 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 if z0 is S.NaN: return S.NaN if e.has(*_illegal): return self if e.is_Order: return Order(limit(e.expr, z, z0), *e.args[1:]) cdir = 0 if str(dir) == "+": cdir = 1 elif str(dir) == "-": cdir = -1 def set_signs(expr): if not expr.args: return expr newargs = tuple(set_signs(arg) for arg in expr.args) if newargs != expr.args: expr = expr.func(*newargs) abs_flag = isinstance(expr, Abs) sign_flag = isinstance(expr, sign) if abs_flag or sign_flag: sig = limit(expr.args[0], z, z0, dir) if sig.is_zero: sig = limit(1 / expr.args[0], z, z0, dir) if sig.is_extended_real: if (sig < 0) == True: return -expr.args[0] if abs_flag else S.NegativeOne elif (sig > 0) == True: return expr.args[0] if abs_flag else S.One return expr if e.has(Float): # Convert floats like 0.5 to exact SymPy numbers like S.Half, to # prevent rounding errors which can lead to unexpected execution # of conditional blocks that work on comparisons # Also see comments in https://github.com/sympy/sympy/issues/19453 from sympy.simplify.simplify import nsimplify e = nsimplify(e) e = set_signs(e) if e.is_meromorphic(z, z0): if abs(z0) is S.Infinity: newe = e.subs(z, 1 / z) # cdir changes sign as oo- should become 0+ cdir = -cdir else: newe = e.subs(z, z + z0) try: coeff, ex = newe.leadterm(z, cdir=cdir) except ValueError: pass else: if ex > 0: return S.Zero elif ex == 0: return coeff if cdir == 1 or not (int(ex) & 1): return S.Infinity * sign(coeff) elif cdir == -1: return S.NegativeInfinity * sign(coeff) else: return S.ComplexInfinity if abs(z0) is S.Infinity: if e.is_Mul: e = factor_terms(e) newe = e.subs(z, 1 / z) # cdir changes sign as oo- should become 0+ cdir = -cdir else: newe = e.subs(z, z + z0) try: coeff, ex = newe.leadterm(z, cdir=cdir) except (ValueError, NotImplementedError, PoleError): # The NotImplementedError catching is for custom functions from sympy.simplify.powsimp import powsimp e = powsimp(e) if e.is_Pow: r = self.pow_heuristics(e) if r is not None: return r else: if isinstance(coeff, AccumBounds) and ex == S.Zero: return coeff if coeff.has(S.Infinity, S.NegativeInfinity, S.ComplexInfinity, S.NaN): return self if not coeff.has(z): if ex.is_positive: return S.Zero elif ex == 0: return coeff elif ex.is_negative: if ex.is_integer: if cdir == 1 or ex.is_even: return S.Infinity * sign(coeff) elif cdir == -1: return S.NegativeInfinity * sign(coeff) else: return S.ComplexInfinity else: if cdir == 1: return S.Infinity * sign(coeff) elif cdir == -1: return S.Infinity * sign(coeff) * S.NegativeOne**ex else: return S.ComplexInfinity # 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_extended_positive: e = e.rewrite(factorial, gamma) l = None try: if str(dir) == '+-': r = gruntz(e, z, z0, '+') l = gruntz(e, z, z0, '-') if l != r: raise ValueError( "The limit does not exist since " "left hand limit = %s and right hand limit = %s" % (l, r)) else: r = gruntz(e, z, z0, dir) if r is S.NaN or l is S.NaN: raise PoleError() except (PoleError, ValueError): if l is not None: raise r = heuristics(e, z, z0, dir) if r is None: return self return r
def doit(self, **hints): """Evaluates the limit. Parameters ========== deep : bool, optional (default: True) Invoke the ``doit`` method of the expressions involved before taking the limit. hints : optional keyword arguments To be passed to ``doit`` methods; only used if deep is True. """ from sympy.series.limitseq import limit_seq from sympy.functions import RisingFactorial e, z, z0, dir = self.args if z0 is S.ComplexInfinity: raise NotImplementedError("Limits at complex " "infinity are not implemented") 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_extended_positive: e = e.rewrite([factorial, RisingFactorial], gamma) if e.is_Mul: if abs(z0) is S.Infinity: e = factor_terms(e) e = e.rewrite(fibonacci, GoldenRatio) 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 = Dummy(positive=True) if z0 is S.NegativeInfinity: inve = e.subs(z, -1 / u) else: inve = e.subs(z, 1 / u) try: r = limit(inve.as_leading_term(u), u, S.Zero, "+") if isinstance(r, Limit): return self else: return r except ValueError: pass if e.is_Order: return 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 doit(self, **hints): """Evaluates limit""" from sympy.series.limitseq import limit_seq from sympy.functions import RisingFactorial e, z, z0, dir = self.args if z0 is S.ComplexInfinity: raise NotImplementedError("Limits at complex " "infinity are not implemented") 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, RisingFactorial], gamma) if e.is_Mul: if abs(z0) is S.Infinity: e = factor_terms(e) e = e.rewrite(fibonacci, GoldenRatio) 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 = Dummy(positive=True) if z0 is S.NegativeInfinity: inve = e.subs(z, -1 / u) else: inve = e.subs(z, 1 / u) r = limit(inve.as_leading_term(u), u, S.Zero, "+") if isinstance(r, Limit): return self else: return r if e.is_Order: return 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 except NotImplementedError: # Trying finding limits of sequences if hints.get('sequence', True) and z0 is S.Infinity: trials = hints.get('trials', 5) r = limit_seq(e, z, trials) if r is None: raise NotImplementedError() else: raise NotImplementedError() return r
def doit(self, **hints): """Evaluates the limit. Parameters ========== deep : bool, optional (default: True) Invoke the ``doit`` method of the expressions involved before taking the limit. hints : optional keyword arguments To be passed to ``doit`` methods; only used if deep is True or if seq is True. """ from sympy import Abs, exp, log, sign from sympy.calculus.util import AccumBounds e, z, z0, dir = self.args if z0 is S.ComplexInfinity: raise NotImplementedError("Limits at complex " "infinity are not implemented") 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 cdir = 0 if str(dir) == "+": cdir = 1 elif str(dir) == "-": cdir = -1 def remove_abs(expr): if not expr.args: return expr newargs = tuple(remove_abs(arg) for arg in expr.args) if newargs != expr.args: expr = expr.func(*newargs) if isinstance(expr, Abs): sig = limit(expr.args[0], z, z0, dir) if sig.is_zero: sig = limit(1/expr.args[0], z, z0, dir) if sig.is_extended_real: if (sig < 0) == True: return -expr.args[0] elif (sig > 0) == True: return expr.args[0] return expr e = remove_abs(e) if e.is_meromorphic(z, z0): if abs(z0) is S.Infinity: newe = e.subs(z, -1/z) else: newe = e.subs(z, z + z0) try: coeff, ex = newe.leadterm(z, cdir) except (ValueError, NotImplementedError): pass else: if ex > 0: return S.Zero elif ex == 0: return coeff if str(dir) == "+" or not(int(ex) & 1): return S.Infinity*sign(coeff) elif str(dir) == "-": return S.NegativeInfinity*sign(coeff) else: return S.ComplexInfinity # 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_extended_positive: e = e.rewrite(factorial, gamma) if e.is_Mul and abs(z0) is S.Infinity: e = factor_terms(e) u = Dummy('u', positive=True) if z0 is S.NegativeInfinity: inve = e.subs(z, -1/u) else: inve = e.subs(z, 1/u) try: f = inve.as_leading_term(u).gammasimp() if f.is_meromorphic(u, S.Zero): r = limit(f, u, S.Zero, "+") if isinstance(r, Limit): return self else: return r except (ValueError, NotImplementedError, PoleError): pass if e.is_Order: return Order(limit(e.expr, z, z0), *e.args[1:]) if e.is_Pow: if e.has(S.Infinity, S.NegativeInfinity, S.ComplexInfinity, S.NaN): return self b1, e1 = e.base, e.exp f1 = e1*log(b1) if f1.is_meromorphic(z, z0): res = limit(f1, z, z0) return exp(res) ex_lim = limit(e1, z, z0) base_lim = limit(b1, z, z0) if base_lim is S.One: if ex_lim in (S.Infinity, S.NegativeInfinity): res = limit(e1*(b1 - 1), z, z0) return exp(res) elif ex_lim.is_real: return S.One if base_lim in (S.Zero, S.Infinity, S.NegativeInfinity) and ex_lim is S.Zero: res = limit(f1, z, z0) return exp(res) if base_lim is S.NegativeInfinity: if ex_lim is S.NegativeInfinity: return S.Zero if ex_lim is S.Infinity: return S.ComplexInfinity if not isinstance(base_lim, AccumBounds) and not isinstance(ex_lim, AccumBounds): res = base_lim**ex_lim if res is not S.ComplexInfinity and not res.is_Pow: return res l = None try: if str(dir) == '+-': r = gruntz(e, z, z0, '+') l = gruntz(e, z, z0, '-') if l != r: raise ValueError("The limit does not exist since " "left hand limit = %s and right hand limit = %s" % (l, r)) else: r = gruntz(e, z, z0, dir) if r is S.NaN or l is S.NaN: raise PoleError() except SignException as er: sig = er.sign r = None # seq is True if and only if the function call originates from limitseq.py # rather than limits.py. In the former case, we return r as normal. if (hints.get('seq', True)): return r # The purpose of exception handling here is to attempt to compute limits of expressions # which do not exist for non-integer values (i.e. expressions like (-1)**x). The solution # is to compute the limit using limit_seq() from the limitseq module, but this is only # valid if we are computing a limit to infinity and, currently, if the expression has no # complex terms. if (z0 is not S.Infinity): raise NotImplementedError('Result depending on the sign of %s must be a limit toward oo' % sig) return r from sympy.core.numbers import I if (e.has(I) or len(e.free_symbols) > 1): raise NotImplementedError('Result depends on the sign of %s' % sig) return r from .limitseq import limit_seq try: return limit_seq(e, z) except (NotImplementedError): raise NotImplementedError('Result depends on the sign of %s' % sig) return r except (PoleError, ValueError): if l is not None: raise r = heuristics(e, z, z0, dir) if r is None: return self return r
def doit(self, **hints): """Evaluates the limit. Parameters ========== deep : bool, optional (default: True) Invoke the ``doit`` method of the expressions involved before taking the limit. hints : optional keyword arguments To be passed to ``doit`` methods; only used if deep is True. """ from sympy import Abs, exp, log, sign from sympy.calculus.util import AccumBounds from sympy.functions import RisingFactorial e, z, z0, dir = self.args if z0 is S.ComplexInfinity: raise NotImplementedError("Limits at complex " "infinity are not implemented") 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 cdir = 0 if str(dir) == "+": cdir = 1 elif str(dir) == "-": cdir = -1 def remove_abs(expr): if not expr.args: return expr newargs = tuple(remove_abs(arg) for arg in expr.args) if newargs != expr.args: expr = expr.func(*newargs) if isinstance(expr, Abs): sig = limit(expr.args[0], z, z0, dir) if sig.is_zero: sig = limit(1 / expr.args[0], z, z0, dir) if sig.is_extended_real: if (sig < 0) == True: return -expr.args[0] elif (sig > 0) == True: return expr.args[0] return expr e = remove_abs(e) if e.is_meromorphic(z, z0): if abs(z0) is S.Infinity: newe = e.subs(z, -1 / z) else: newe = e.subs(z, z + z0) try: coeff, ex = newe.leadterm(z, cdir) except (ValueError, NotImplementedError): pass else: if ex > 0: return S.Zero elif ex == 0: return coeff if str(dir) == "+" or not (int(ex) & 1): return S.Infinity * sign(coeff) elif str(dir) == "-": return S.NegativeInfinity * sign(coeff) else: return S.ComplexInfinity # 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_extended_positive: e = e.rewrite([factorial, RisingFactorial], gamma) if e.is_Mul and abs(z0) is S.Infinity: e = factor_terms(e) u = Dummy('u', positive=True) if z0 is S.NegativeInfinity: inve = e.subs(z, -1 / u) else: inve = e.subs(z, 1 / u) try: f = inve.as_leading_term(u).gammasimp() if f.is_meromorphic(u, S.Zero): r = limit(f, u, S.Zero, "+") if isinstance(r, Limit): return self else: return r except (ValueError, NotImplementedError, PoleError): pass if e.is_Order: return Order(limit(e.expr, z, z0), *e.args[1:]) if e.is_Pow: if e.has(S.Infinity, S.NegativeInfinity, S.ComplexInfinity, S.NaN): return self b1, e1 = e.base, e.exp f1 = e1 * log(b1) if f1.is_meromorphic(z, z0): res = limit(f1, z, z0) return exp(res) ex_lim = limit(e1, z, z0) base_lim = limit(b1, z, z0) if base_lim is S.One: if ex_lim in (S.Infinity, S.NegativeInfinity): res = limit(e1 * (b1 - 1), z, z0) return exp(res) elif ex_lim.is_real: return S.One if base_lim in (S.Zero, S.Infinity, S.NegativeInfinity) and ex_lim is S.Zero: res = limit(f1, z, z0) return exp(res) if base_lim is S.NegativeInfinity: if ex_lim is S.NegativeInfinity: return S.Zero if ex_lim is S.Infinity: return S.ComplexInfinity if not isinstance(base_lim, AccumBounds) and not isinstance( ex_lim, AccumBounds): res = base_lim**ex_lim if res is not S.ComplexInfinity and not res.is_Pow: return res l = None try: if str(dir) == '+-': r = gruntz(e, z, z0, '+') l = gruntz(e, z, z0, '-') if l != r: raise ValueError( "The limit does not exist since " "left hand limit = %s and right hand limit = %s" % (l, r)) else: r = gruntz(e, z, z0, dir) if r is S.NaN or l is S.NaN: raise PoleError() except (PoleError, ValueError): if l is not None: raise r = heuristics(e, z, z0, dir) if r is None: return self 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 doit(self, **hints): """Evaluates the limit. Parameters ========== deep : bool, optional (default: True) Invoke the ``doit`` method of the expressions involved before taking the limit. hints : optional keyword arguments To be passed to ``doit`` methods; only used if deep is True. """ from sympy import sign from sympy.functions import RisingFactorial e, z, z0, dir = self.args if z0 is S.ComplexInfinity: raise NotImplementedError("Limits at complex " "infinity are not implemented") 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 cdir = 0 if str(dir) == "+": cdir = 1 elif str(dir) == "-": cdir = -1 if e.is_meromorphic(z, z0): if abs(z0) is S.Infinity: newe = e.subs(z, -1 / z) else: newe = e.subs(z, z + z0) try: coeff, exp = newe.leadterm(z, cdir) except ValueError: pass else: if exp > 0: return S.Zero elif exp == 0: return coeff if str(dir) == "+" or not (int(exp) & 1): return S.Infinity * sign(coeff) elif str(dir) == "-": return S.NegativeInfinity * sign(coeff) else: return S.ComplexInfinity # 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_extended_positive: e = e.rewrite([factorial, RisingFactorial], gamma) if e.is_Mul and abs(z0) is S.Infinity: e = factor_terms(e) u = Dummy('u', positive=True) if z0 is S.NegativeInfinity: inve = e.subs(z, -1 / u) else: inve = e.subs(z, 1 / u) try: f = inve.as_leading_term(u).gammasimp() if f.is_meromorphic(u, S.Zero): r = limit(f, u, S.Zero, "+") if isinstance(r, Limit): return self else: return r except (ValueError, NotImplementedError, PoleError): pass if e.is_Order: return Order(limit(e.expr, z, z0), *e.args[1:]) l = None try: if str(dir) == '+-': r = gruntz(e, z, z0, '+') l = gruntz(e, z, z0, '-') if l != r: raise ValueError( "The limit does not exist since " "left hand limit = %s and right hand limit = %s" % (l, r)) else: r = gruntz(e, z, z0, dir) if r is S.NaN or l is S.NaN: raise PoleError() except (PoleError, ValueError): if l is not None: raise r = heuristics(e, z, z0, dir) if r is None: return self return r
def doit(self, **hints): """Evaluates the limit. Parameters ========== deep : bool, optional (default: True) Invoke the ``doit`` method of the expressions involved before taking the limit. hints : optional keyword arguments To be passed to ``doit`` methods; only used if deep is True. """ from sympy import Abs, exp, log, sign, floor, ceiling, binomial from sympy.calculus.util import AccumBounds e, z, z0, dir = self.args if z0 is S.ComplexInfinity: raise NotImplementedError("Limits at complex " "infinity are not implemented") 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 if z0 is S.NaN: return S.NaN if e.has(S.Infinity, S.NegativeInfinity, S.ComplexInfinity, S.NaN): return self cdir = 0 if str(dir) == "+": cdir = 1 elif str(dir) == "-": cdir = -1 def set_signs(expr): if not expr.args: return expr newargs = tuple(set_signs(arg) for arg in expr.args) if newargs != expr.args: expr = expr.func(*newargs) abs_flag = isinstance(expr, Abs) sign_flag = isinstance(expr, sign) if abs_flag or sign_flag: sig = limit(expr.args[0], z, z0, dir) if sig.is_zero: sig = limit(1 / expr.args[0], z, z0, dir) if sig.is_extended_real: if (sig < 0) == True: return -expr.args[0] if abs_flag else S.NegativeOne elif (sig > 0) == True: return expr.args[0] if abs_flag else S.One return expr e = set_signs(e) if e.is_meromorphic(z, z0): if abs(z0) is S.Infinity: newe = e.subs(z, -1 / z) else: newe = e.subs(z, z + z0) try: coeff, ex = newe.leadterm(z, cdir=cdir) except ValueError: pass else: if ex > 0: return S.Zero elif ex == 0: return coeff if str(dir) == "+" or not (int(ex) & 1): return S.Infinity * sign(coeff) elif str(dir) == "-": return S.NegativeInfinity * sign(coeff) else: return S.ComplexInfinity # is_meromorphic does not capture all meromorphic functions, and such # functions may have a leading term computation which can help find the # limit without entering gruntz if not e.has(floor, ceiling, factorial, binomial): if abs(z0) is S.Infinity: newe = e.subs(z, 1 / z) cdir = -cdir else: newe = e.subs(z, z + z0) try: # cdir changes sign as oo- should become 0+ coeff, ex = newe.leadterm(z, cdir=cdir) except (ValueError, NotImplementedError, PoleError, AttributeError): # The NotImplementedError catching may be removed after leading # term methods are defined for some special functions (_eis # and Ci) # AttributeError may be removed one TupleArg leading term # is handled pass else: if coeff.has(S.Infinity, S.NegativeInfinity, S.ComplexInfinity): return self if not coeff.has(z): if ex.is_positive: return S.Zero elif ex == 0: return coeff elif ex.is_negative: if ex.is_integer: if str(dir) == "-": return S.Infinity * sign(coeff) elif str(dir) == "+": return S.NegativeInfinity * sign(coeff) else: return S.ComplexInfinity else: if str(dir) == "+": return S.Infinity * sign(coeff) elif str(dir) == "-": return S.NegativeInfinity * sign( coeff) * S.NegativeOne**(S.One + ex) else: return S.ComplexInfinity # 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_extended_positive: e = e.rewrite(factorial, gamma) if e.is_Mul and abs(z0) is S.Infinity: e = factor_terms(e) u = Dummy('u', positive=True) if z0 is S.NegativeInfinity: inve = e.subs(z, -1 / u) else: inve = e.subs(z, 1 / u) try: f = inve.as_leading_term(u).gammasimp() if f.is_meromorphic(u, S.Zero): r = limit(f, u, S.Zero, "+") if isinstance(r, Limit): return self else: return r except (ValueError, NotImplementedError, PoleError): pass if e.is_Order: return Order(limit(e.expr, z, z0), *e.args[1:]) if e.is_Pow: if e.has(S.Infinity, S.NegativeInfinity, S.ComplexInfinity, S.NaN): return self b1, e1 = e.base, e.exp f1 = e1 * log(b1) if f1.is_meromorphic(z, z0): res = limit(f1, z, z0) return exp(res) ex_lim = limit(e1, z, z0) base_lim = limit(b1, z, z0) if base_lim is S.One: if ex_lim in (S.Infinity, S.NegativeInfinity): res = limit(e1 * (b1 - 1), z, z0) return exp(res) elif ex_lim.is_real: return S.One if base_lim in (S.Zero, S.Infinity, S.NegativeInfinity) and ex_lim is S.Zero: res = limit(f1, z, z0) return exp(res) if base_lim is S.NegativeInfinity: if ex_lim is S.NegativeInfinity: return S.Zero if ex_lim is S.Infinity: return S.ComplexInfinity if not isinstance(base_lim, AccumBounds) and not isinstance( ex_lim, AccumBounds): res = base_lim**ex_lim if res is not S.ComplexInfinity and not res.is_Pow: return res l = None try: if str(dir) == '+-': r = gruntz(e, z, z0, '+') l = gruntz(e, z, z0, '-') if l != r: raise ValueError( "The limit does not exist since " "left hand limit = %s and right hand limit = %s" % (l, r)) else: r = gruntz(e, z, z0, dir) if r is S.NaN or l is S.NaN: raise PoleError() except (PoleError, ValueError): if l is not None: raise r = heuristics(e, z, z0, dir) if r is None: return self 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