Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrong limit result for Abs((-n/(n+1))**n) #26513

Open
satels opened this issue Apr 16, 2024 · 7 comments
Open

Wrong limit result for Abs((-n/(n+1))**n) #26513

satels opened this issue Apr 16, 2024 · 7 comments

Comments

@satels
Copy link
Contributor

satels commented Apr 16, 2024

import sympy

from sympy import S, Symbol, limit, oo

print('sympy version:', sympy.__version__)

n = Symbol('n', real=True)

expr = (n/(n+1))**n
print(limit(expr, n, oo))  # Success

expr = (-n/(n+1))**n
print(limit(expr, n, oo))  # Wrong

expr = Abs((-n/(n+1))**n)
print(limit(expr, n, oo))  # Wrong

The result:

sympy version: 1.12
exp(-1)
0
0
@satels satels changed the title Wrong limit result Wrong limit result for Abs((-n/(n+1))**n) Apr 16, 2024
@anutosh491
Copy link
Member

Thanks for raising this. We do perform poorly when it comes to limits (bi-directional, or at infinities too ... you can easily find similar errors for -oo too ) involving the abs function.

skirpichev added a commit to skirpichev/diofant that referenced this issue Apr 19, 2024
@arnabnandikgp
Copy link
Contributor

It seems that the result for the third limit (the one with abs) is being returned correctly by gruntz but not for the second one

In [6]: gruntz((-n/(n+1))**n, n, oo)
Out[6]: 0

In [7]: gruntz(abs((-n/(n+1))**n), n, oo)
Out[7]: exp(-1)

though for the second one(negative one) we get value 0 though the expression (-n/(n+1))**n is not well defined at infinity, it seems the problem stems from the following lines:

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:
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 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

since gruntz returns wrong value for (-n/(n+1))**n i.e 0 the computation for Abs one also gets messed up.

@anutosh491
Copy link
Member

Ok these are my thoughts here

  1. So technically the gruntz is responsible for all 3 outputs here, which means instead of limit we can straight up call gruntz and if we get it to work, we are done.
  2. Talking about the 3rd limit

So the code flow tries to solve limit(Abs((-n/(n+1))**n), n, oo) through 2 approaches
i) calcualte limit((-n/(n+1))**n), n, oo) and then make a decision
ii) calculate gruntz(Abs((-n/(n+1))**n), n, oo)

Now as we can see, we don't solve limit((-n/(n+1))**n), n, oo) correctly and return 0 for the same, hence the case adding Abs goes wrong too

  1. So now we need to solve limit((-n/(n+1))**n), n, oo) which further depends on gruntz((-n/(n+1))**n), n, oo) which currently returns 0.

  2. Hence gruntz((-n/(n+1))**n), n, oo) is the whole issue here. Once that is solved we can backtrack and all other limits would work by themselves. Now we return 0 here but the expression is not well defined at infinity. I think we just need to return self here.

>>> limit((-n/(n+1))**n), n, oo)
Limit((-x/(x + 1))**x, x, oo)

@anutosh491 anutosh491 added the GSoC label Jun 4, 2024
@arnabnandikgp
Copy link
Contributor

Hence gruntz((-n/(n+1))**n), n, oo) is the whole issue here. Once that is solved we can backtrack and all other limits would work by themselves. Now we return 0 here but the expression is not well defined at infinity. I think we just need to return self here.

Yes exactly!

@arnabnandikgp
Copy link
Contributor

It seems that the following lines are the cause of the problem

elif isinstance(e, log):
return sign(e.args[0] - 1, x)

the function sign(e,x) computes the sign of e as x tends to infinity.
In master sign(log(-n/(n+1), n) computes to -1 which is wrong and should have been I.
It seems the above lines were added with the context that taylor series expansion of log(x+1) around 0 is ` x -x**2/2+ ...' but since here we are trying to find the sign of expression as x tends to oo so maybe this conditional isn't the right thing to do here.
Removing the conditional altogether solves the issue as sign of log expressions are calculated correctly using coefficient obtained from mrv_leadterm, here
c0, e0 = mrv_leadterm(e, x)
return sign(c0, x)

@oscarbenjamin
Copy link
Contributor

the function sign(e,x) computes the sign of e as x tends to infinity.
In master sign(log(-n/(n+1), n) computes to -1 which is wrong and should have been I.

Is this sign function supposed to be able to handle non-real inputs?

It gets this wrong:

In [12]: from sympy.series.gruntz import sign

In [13]: sign(exp(I*x), x)
Out[13]: 1

Is it that somewhere higher up such non-real expressions should be rejected?

@arnabnandikgp
Copy link
Contributor

arnabnandikgp commented Jun 5, 2024

Yeah, since scenarios which will require computation of sign(exp(I*x), x) will be simply arise from things like limit(exp(I*x), x,oo)and in limitinf we use the sign() on the coefficient(using mrv_leadterm) of rewritten(rewrite in mrv_leadterm) term where it will raise error,

sig = sign(g.exp, x)
if sig != 1 and sig != -1 and not sig.has(AccumBounds):
raise NotImplementedError('Result depends on the sign of %s' % sig)

even in case of nested exponentiation or power tower scenarios, routines in mrv ensure that we dont end up getting exp or logs in exponents. Nevertheless we will never come to the point of having to use this sign function on exponents of any symbolic expression directly I believe.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants