|
from sympy.calculus.accumulationbounds import AccumBounds |
|
from sympy.core import S, Symbol, Add, sympify, Expr, PoleError, Mul |
|
from sympy.core.exprtools import factor_terms |
|
from sympy.core.numbers import Float, _illegal |
|
from sympy.core.function import AppliedUndef |
|
from sympy.core.symbol import Dummy |
|
from sympy.functions.combinatorial.factorials import factorial |
|
from sympy.functions.elementary.complexes import (Abs, sign, arg, re) |
|
from sympy.functions.elementary.exponential import (exp, log) |
|
from sympy.functions.special.gamma_functions import gamma |
|
from sympy.polys import PolynomialError, factor |
|
from sympy.series.order import Order |
|
from .gruntz import gruntz |
|
|
|
def limit(e, z, z0, dir="+"): |
|
"""Computes the limit of ``e(z)`` at the point ``z0``. |
|
|
|
Parameters |
|
========== |
|
|
|
e : expression, the limit of which is to be taken |
|
|
|
z : symbol representing the variable in the limit. |
|
Other symbols are treated as constants. Multivariate limits |
|
are not supported. |
|
|
|
z0 : the value toward which ``z`` tends. Can be any expression, |
|
including ``oo`` and ``-oo``. |
|
|
|
dir : string, optional (default: "+") |
|
The limit is bi-directional if ``dir="+-"``, from the right |
|
(z->z0+) if ``dir="+"``, and from the left (z->z0-) if |
|
``dir="-"``. For infinite ``z0`` (``oo`` or ``-oo``), the ``dir`` |
|
argument is determined from the direction of the infinity |
|
(i.e., ``dir="-"`` for ``oo``). |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import limit, sin, oo |
|
>>> from sympy.abc import x |
|
>>> limit(sin(x)/x, x, 0) |
|
1 |
|
>>> limit(1/x, x, 0) # default dir='+' |
|
oo |
|
>>> limit(1/x, x, 0, dir="-") |
|
-oo |
|
>>> limit(1/x, x, 0, dir='+-') |
|
zoo |
|
>>> 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). |
|
|
|
See Also |
|
======== |
|
|
|
limit_seq : returns the limit of a sequence. |
|
""" |
|
|
|
return Limit(e, z, z0, dir).doit(deep=False) |
|
|
|
|
|
def heuristics(e, z, z0, dir): |
|
"""Computes the limit of an expression term-wise. |
|
Parameters are the same as for the ``limit`` function. |
|
Works with the arguments of expression ``e`` one by one, computing |
|
the limit of each and then combining the results. This approach |
|
works only for simple limits, but it is fast. |
|
""" |
|
|
|
rv = None |
|
if z0 is S.Infinity: |
|
rv = limit(e.subs(z, 1/z), z, S.Zero, "+") |
|
if isinstance(rv, Limit): |
|
return |
|
elif (e.is_Mul or e.is_Add or e.is_Pow or (e.is_Function and not isinstance(e, AppliedUndef))): |
|
r = [] |
|
from sympy.simplify.simplify import together |
|
for a in e.args: |
|
l = limit(a, z, z0, dir) |
|
if l.has(S.Infinity) and l.is_finite is None: |
|
if isinstance(e, Add): |
|
m = factor_terms(e) |
|
if not isinstance(m, Mul): |
|
m = together(m) |
|
if not isinstance(m, Mul): |
|
m = factor(e) |
|
if isinstance(m, Mul): |
|
return heuristics(m, z, z0, dir) |
|
return |
|
return |
|
elif isinstance(l, Limit): |
|
return |
|
elif l is S.NaN: |
|
return |
|
else: |
|
r.append(l) |
|
if r: |
|
rv = e.func(*r) |
|
if rv is S.NaN and e.is_Mul and any(isinstance(rr, AccumBounds) for rr in r): |
|
r2 = [] |
|
e2 = [] |
|
for ii, rval in enumerate(r): |
|
if isinstance(rval, AccumBounds): |
|
r2.append(rval) |
|
else: |
|
e2.append(e.args[ii]) |
|
|
|
if len(e2) > 0: |
|
e3 = Mul(*e2).simplify() |
|
l = limit(e3, z, z0, dir) |
|
rv = l * Mul(*r2) |
|
|
|
if rv is S.NaN: |
|
try: |
|
from sympy.simplify.ratsimp import ratsimp |
|
rat_e = ratsimp(e) |
|
except PolynomialError: |
|
return |
|
if rat_e is S.NaN or rat_e == e: |
|
return |
|
return limit(rat_e, z, z0, dir) |
|
return rv |
|
|
|
|
|
class Limit(Expr): |
|
"""Represents an unevaluated limit. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import Limit, sin |
|
>>> from sympy.abc import x |
|
>>> Limit(sin(x)/x, x, 0) |
|
Limit(sin(x)/x, x, 0, dir='+') |
|
>>> Limit(1/x, x, 0, dir="-") |
|
Limit(1/x, x, 0, dir='-') |
|
|
|
""" |
|
|
|
def __new__(cls, e, z, z0, dir="+"): |
|
e = sympify(e) |
|
z = sympify(z) |
|
z0 = sympify(z0) |
|
|
|
if z0 in (S.Infinity, S.ImaginaryUnit*S.Infinity): |
|
dir = "-" |
|
elif z0 in (S.NegativeInfinity, S.ImaginaryUnit*S.NegativeInfinity): |
|
dir = "+" |
|
|
|
if(z0.has(z)): |
|
raise NotImplementedError("Limits approaching a variable point are" |
|
" not supported (%s -> %s)" % (z, z0)) |
|
if isinstance(dir, str): |
|
dir = Symbol(dir) |
|
elif not isinstance(dir, Symbol): |
|
raise TypeError("direction must be of type basestring or " |
|
"Symbol, not %s" % type(dir)) |
|
if str(dir) not in ('+', '-', '+-'): |
|
raise ValueError("direction must be one of '+', '-' " |
|
"or '+-', not %s" % dir) |
|
|
|
obj = Expr.__new__(cls) |
|
obj._args = (e, z, z0, dir) |
|
return obj |
|
|
|
|
|
@property |
|
def free_symbols(self): |
|
e = self.args[0] |
|
isyms = e.free_symbols |
|
isyms.difference_update(self.args[1].free_symbols) |
|
isyms.update(self.args[2].free_symbols) |
|
return isyms |
|
|
|
|
|
def pow_heuristics(self, e): |
|
_, z, z0, _ = self.args |
|
b1, e1 = e.base, e.exp |
|
if not b1.has(z): |
|
res = limit(e1*log(b1), 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) |
|
if base_lim is S.NegativeInfinity and ex_lim is S.Infinity: |
|
return S.ComplexInfinity |
|
|
|
|
|
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 str(dir) == '+-': |
|
r = limit(e, z, z0, dir='+') |
|
l = limit(e, z, z0, dir='-') |
|
if isinstance(r, Limit) and isinstance(l, Limit): |
|
if r.args[0] == l.args[0]: |
|
return self |
|
if r == l: |
|
return l |
|
if r.is_infinite and l.is_infinite: |
|
return S.ComplexInfinity |
|
raise ValueError("The limit does not exist since " |
|
"left hand limit = %s and right hand limit = %s" |
|
% (l, r)) |
|
|
|
if z0 is S.ComplexInfinity: |
|
raise NotImplementedError("Limits at complex " |
|
"infinity are not implemented") |
|
|
|
if z0.is_infinite: |
|
cdir = sign(z0) |
|
cdir = cdir/abs(cdir) |
|
e = e.subs(z, cdir*z) |
|
dir = "-" |
|
z0 = S.Infinity |
|
|
|
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 = S.Zero |
|
if str(dir) == "+": |
|
cdir = S.One |
|
elif str(dir) == "-": |
|
cdir = S.NegativeOne |
|
|
|
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) |
|
arg_flag = isinstance(expr, arg) |
|
sign_flag = isinstance(expr, sign) |
|
if abs_flag or sign_flag or arg_flag: |
|
try: |
|
sig = limit(expr.args[0], z, z0, dir) |
|
if sig.is_zero: |
|
sig = limit(1/expr.args[0], z, z0, dir) |
|
except NotImplementedError: |
|
return expr |
|
else: |
|
if sig.is_extended_real: |
|
if (sig < 0) == True: |
|
return (-expr.args[0] if abs_flag else |
|
S.NegativeOne if sign_flag else S.Pi) |
|
elif (sig > 0) == True: |
|
return (expr.args[0] if abs_flag else |
|
S.One if sign_flag else S.Zero) |
|
return expr |
|
|
|
if e.has(Float): |
|
|
|
|
|
|
|
|
|
from sympy.simplify.simplify import nsimplify |
|
e = nsimplify(e) |
|
e = set_signs(e) |
|
|
|
|
|
if e.is_meromorphic(z, z0): |
|
if z0 is S.Infinity: |
|
newe = e.subs(z, 1/z) |
|
|
|
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 z0 is S.Infinity: |
|
if e.is_Mul: |
|
e = factor_terms(e) |
|
dummy = Dummy('z', positive=z.is_positive, negative=z.is_negative, real=z.is_real) |
|
newe = e.subs(z, 1/dummy) |
|
|
|
cdir = -cdir |
|
newz = dummy |
|
else: |
|
newe = e.subs(z, z + z0) |
|
newz = z |
|
try: |
|
coeff, ex = newe.leadterm(newz, cdir=cdir) |
|
except (ValueError, NotImplementedError, PoleError): |
|
|
|
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 |
|
try: |
|
coeff = newe.as_leading_term(newz, cdir=cdir) |
|
if coeff != newe and (coeff.has(exp) or coeff.has(S.Exp1)): |
|
return gruntz(coeff, newz, 0, "-" if re(cdir).is_negative else "+") |
|
except (ValueError, NotImplementedError, PoleError): |
|
pass |
|
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(newz): |
|
if ex.is_positive: |
|
return S.Zero |
|
elif ex == 0: |
|
return coeff |
|
elif ex.is_negative: |
|
if cdir == 1: |
|
return S.Infinity*sign(coeff) |
|
elif cdir == -1: |
|
return S.NegativeInfinity*sign(coeff)*S.NegativeOne**(S.One + ex) |
|
else: |
|
return S.ComplexInfinity |
|
else: |
|
raise NotImplementedError("Not sure of sign of %s" % ex) |
|
|
|
|
|
|
|
|
|
|
|
if z0.is_extended_nonnegative: |
|
e = e.rewrite(factorial, gamma) |
|
|
|
l = None |
|
|
|
try: |
|
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 |
|
|