Skip to content

Commit

Permalink
Merge pull request #1298 from skirpichev/fix-limit-O
Browse files Browse the repository at this point in the history
calculus: fix handling nested Order() terms in Limit.doit()
  • Loading branch information
skirpichev committed Mar 7, 2023
2 parents ed6c0f5 + a0b9adb commit 1f2b470
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 34 deletions.
14 changes: 9 additions & 5 deletions diofant/calculus/limits.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from ..core import Dummy, Expr, Float, PoleError, Rational, nan, oo, sympify
from ..core import (Add, Dummy, Expr, Float, PoleError, Rational, nan, oo,
sympify)
from ..core.function import UndefinedFunction
from ..sets import Reals
from .gruntz import limitinf
from .order import Order


def limit(expr, z, z0, dir=None):
Expand Down Expand Up @@ -154,9 +154,13 @@ def doit(self, **hints):
if not e.has(z):
return e

if e.has(Order) and (order := e.getO()) and z == order.var and z0 == order.point:
order = limit(order.expr, z, z0, dir)
e = e.removeO() + order
for t in e.atoms(Add):
if (o := t.getO()) and o.var == z and o.point == z0:
e = e.xreplace({t: t.removeO()})
if e.is_Order and e.var == z and e.point == z0:
if limit(e.expr, z, z0, dir):
return e
return Rational(0)

# Convert to the limit z->oo and use Gruntz algorithm.
e = e.subs({z: dir*z})
Expand Down
2 changes: 1 addition & 1 deletion diofant/core/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ def _eval_nseries(self, x, n, logx):
and possible:
>>> loggamma(1/x)._eval_nseries(x, 0, None)
-1/x - log(x)/x + log(x)/2 + O(1, x)
-log(x)/x + O(1/x)
"""
from ..calculus import Order
Expand Down
2 changes: 1 addition & 1 deletion diofant/core/power.py
Original file line number Diff line number Diff line change
Expand Up @@ -1172,7 +1172,7 @@ def _eval_nseries(self, x, n, logx):
if c.is_negative:
if t.is_Order:
return self._eval_nseries(x, n + 1, logx)
l = floor(arg(t.removeO()*c)/(2*pi)).limit(x, 0)
l = floor(arg(t*c)/2/pi).limit(x, 0)
if l.is_finite:
factor *= exp(2*pi*I*self.exp*l)
else:
Expand Down
37 changes: 18 additions & 19 deletions diofant/functions/elementary/exponential.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,35 +356,34 @@ def _eval_is_zero(self):

def _eval_nseries(self, x, n, logx):
from ...calculus import Order
from .complexes import arg
from .complexes import arg as argument
from .integers import floor
if not logx:
logx = log(x)
arg_series = self.args[0].nseries(x, n=n, logx=logx)
arg = self.args[0]
arg_series = arg.nseries(x, n=n, logx=logx)
while arg_series.is_Order:
n += 1
arg_series = self.args[0].nseries(x, n=n, logx=logx)
arg_series = arg.nseries(x, n=n, logx=logx)
arg0 = arg_series.as_leading_term(x)
c, e = arg0.as_coeff_exponent(x)
t = (arg_series/arg0 - 1).cancel().nseries(x, n=n, logx=logx)
res = term = t = ((arg_series - arg0)/arg0).nseries(x, n=n, logx=logx)
# series of log(1 + t) in t
log_series = term = t
for i in range(1, n):
term *= -i*t/(i + 1)
term = term.nseries(x, n=n, logx=logx)
log_series += term
if t != 0:
log_series += Order(t**n, x)
# branch handling
if c.is_negative:
if t.is_Order:
return self._eval_nseries(x, n + 1, logx)
l = floor(arg(t.removeO()*c)/(2*pi)).limit(x, 0)
if l.is_finite:
log_series += 2*I*pi*l
else:
raise NotImplementedError
return log_series + log(c) + e*logx
term = term.series(x, n=n, logx=logx)
res += term
res += Order(t**n, x)
# branch handling
if c.is_negative:
if t.is_Order:
return self._eval_nseries(x, n + 1, logx)
l = 2*pi*I*floor(argument(t*c)/2/pi).limit(x, 0)
if l.is_finite:
res += l
else:
raise NotImplementedError
return res + log(c) + e*logx

def _eval_as_leading_term(self, x):
arg = self.args[0].as_leading_term(x)
Expand Down
2 changes: 2 additions & 0 deletions diofant/tests/calculus/test_limits.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ def test_basic1():
limit(Sum(1/x, (x, 1, y)) - 1/y, y, oo)
assert limit(nan, x, -oo) == nan
assert limit(O(2, x)*x, x, nan) == nan
assert limit(O(x), x, 0) == 0
assert limit(O(1, x), x, 0) != 1
assert limit(sin(O(x)), x, 0) == 0
assert limit(1/(x - 1), x, 1) == oo
assert limit(1/(x - 1), x, 1, dir=1) == -oo
Expand Down
3 changes: 1 addition & 2 deletions diofant/tests/core/test_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,8 +409,7 @@ def test_function__eval_nseries():
sqrt(2)*x**Rational(3, 2)/12 + O(x**2)
assert acos(1 + x)._eval_nseries(x, 4, None) == sqrt(2)*I*sqrt(x) - \
sqrt(2)*I*x**(3/2)/12 + O(x**2)
assert loggamma(1/x)._eval_nseries(x, 0, None) == \
log(x)/2 - log(x)/x - 1/x + O(1, x)
assert loggamma(1/x)._eval_nseries(x, 0, None) == O(1/x) - log(x)/x
assert loggamma(log(1/x)).series(x, n=1, logx=y) == loggamma(-y)

# issue sympy/sympy#6725:
Expand Down
12 changes: 6 additions & 6 deletions diofant/tests/functions/test_gamma_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,12 +400,12 @@ def test_loggamma():

def tN(N, M):
assert loggamma(1/x)._eval_nseries(x, n=N).getn() == M
tN(0, 0)
tN(1, 1)
tN(2, 3)
tN(3, 3)
tN(4, 5)
tN(5, 5)
tN(0, -1)
tN(1, +1)
tN(2, +3)
tN(3, +3)
tN(4, +5)
tN(5, +5)


def test_polygamma_expansion():
Expand Down

0 comments on commit 1f2b470

Please sign in to comment.