示例#1
0
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
示例#2
0
文件: ring_series.py 项目: wyom/sympy
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
示例#3
0
    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
示例#4
0
文件: limits.py 项目: ness01/sympy
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))
示例#5
0
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
示例#6
0
文件: limits.py 项目: ness01/sympy
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
示例#7
0
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
示例#8
0
文件: limits.py 项目: sidhu1012/sympy
    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
示例#9
0
文件: limits.py 项目: zalois/sympy
    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
示例#10
0
    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
示例#11
0
    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
示例#12
0
文件: limits.py 项目: daico007/sympy
    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
示例#13
0
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
示例#14
0
    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
示例#15
0
    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
示例#16
0
文件: limits.py 项目: vchekan/sympy
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