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

limit(1/x, x, 0) == oo ?? #11667

Open
latot opened this issue Sep 27, 2016 · 28 comments
Open

limit(1/x, x, 0) == oo ?? #11667

latot opened this issue Sep 27, 2016 · 28 comments

Comments

@latot
Copy link
Contributor

latot commented Sep 27, 2016

Hi, well, what definition of limit are you using? or this is a bug.

>>> limit(1/x, x, 0)
oo
>>> limit(1/x, x, 0, '+')
oo
>>> limit(1/x, x, 0, '-')
-oo

I search if this issue exist and i can't found it, if i search bad close it please.

Thx. Cya.

@viditjain1
Copy link

I want to work on this. Can someone give me a hint on where to start..?

@latot
Copy link
Contributor Author

latot commented Sep 30, 2016

I don't know to much about Sympy, but the logic say to me, when you don't send a direction you should calc the limit in both sides, then compare it, if are equals return the value, if not return nan.

@asmeurer
Copy link
Member

asmeurer commented Oct 1, 2016

There are some older issues about this, if you search. It would be nice to make limit bidirectional by default.

@viditjain1
Copy link

bidirectional by default meaning the same logic that is described in the previous comment by latot?

@asmeurer
Copy link
Member

asmeurer commented Oct 3, 2016

I think it would make more sense to raise an exception when the different sided limits differ.

@latot
Copy link
Contributor Author

latot commented Oct 3, 2016

Hi, sadly i'm not agree, conceptually the limit of an discontinuous functions is simply not defined, i think that is nan, or the expression you like, but an exception i think its too heavy, you can check the limit basically for tree reasons, get the right limit, get the left limit and get the limit of a point, in this last case its the same to check if the limit exist, actually Sympy works with discontinuous functions, at least to me don't have to much sense send the exception with this in mind.

Thx.Cya.

@asmeurer
Copy link
Member

asmeurer commented Oct 3, 2016

The nice thing about an exception is that it could contain a message indicating to the user why the limit failed. nan is, in some sense, a non-failure state. It is designed to propagate to further calculations without error.

@latot
Copy link
Contributor Author

latot commented Oct 3, 2016

mmm, if i'm right.. zoo represent an indeterminate point?, the main reason of why i don't like the exception is because if you use that the must start checking exceptions...

so, can be zoo an option? or some expression?

only in a practical case, i start working with discontinuos function, think in piecewise or 1/x format, if i try calc limits or diff, i will start getting a lot of exceptions...

@asmeurer
Copy link
Member

asmeurer commented Oct 3, 2016

zoo is the value of the complex limit of 1/x, but it doesn't make sense as a generic value. You were right that nan is the most correct value for "doesn't exist".

I don't see why you wouldn't need to check for nan too. If you are taking a limit that doesn't exist, you'd want to know that fact. Maybe I'm not seeing your use case properly where it would be more useful to have a nan propagate.

@latot
Copy link
Contributor Author

latot commented Oct 3, 2016

mm, i think an example will be better, lets imagine we have a n count of functions, f1, f2, f3.....
joined in one piecewise, now we wan't check all discontinuous points, if we use nan we only need check if its nan the result, if we use a exception we must start using try/catch and that things, i don't think its appropriate in this context.

Thx. Cya.

@asmeurer
Copy link
Member

asmeurer commented Oct 3, 2016

Note that limit already raises exceptions for other things, like if the limit doesn't exist or can't be computed (although IMHO it ought to be returning Limit objects whenever possible).

@latot
Copy link
Contributor Author

latot commented Oct 4, 2016

Hi, i try calc the limit with this but instead have a raise of the limit don't exist i get a NotImplementedError:

>>> a=Piecewise((x+1, x>1), (x, x<=-1))
>>> limit(a, x, 0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python2.7/site-packages/sympy/series/limits.py", line 45, in limit
    return Limit(e, z, z0, dir).doit(deep=False)
  File "/usr/lib64/python2.7/site-packages/sympy/series/limits.py", line 187, in doit
    raise NotImplementedError()
NotImplementedError

for now i think conceptually its fine send an exception if the limit can't be calculated, that is because sympy can continue be improved, but an undefined value i think have other meaning.

@viditjain1
Copy link

Yes, i also think raising exception will be more appropriate. I will try to implement it.

@apalmer1377
Copy link
Contributor

Hi, I submitted a pull request for a patch to this a few hours ago #11694 . Currently, if the bidirectional limit doesn't exist, the function returns None, since there is no limit at that point.

@sachin-4099
Copy link
Contributor

This is not a bug.
By default, when the dir variable is not provided, then if the limit variable tends to a value other than oo or -oo, as in this case it is 0, the dir is set as +.

If you want to calculate the result from both sides, set the dir variable to +-.
In this case, the result we get on the master when dir is set to +- is:

In [1]: limit(1/x, x, 0, dir='+-')
Out[1]: zoo

@latot
Copy link
Contributor Author

latot commented May 29, 2020

mm, i think is very very not intuituve, from the "default" concept of limit, when you eval it, should be from both directions, usually asking a limit means compare the limits from all directions to get it, and if all are the same return that value.

Sympy have a default value for dir when is not given, but that default value is different from what usually i think we expect from getting in the limit function.

@sachin-4099
Copy link
Contributor

The docstring says:

    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``).

So will have to work as the docstring says.

@latot
Copy link
Contributor Author

latot commented May 29, 2020

Hi, i don't think is a good idea, at least, is not following the limit concept, why not change the doc?

I think this can follow the actual definition of limit (in math), (in general when we talk about limit).

@gschintgen
Copy link
Contributor

See also: #8341, #14387, #8320.
Anyway, here's my personal take as a user:

  1. I'd very much prefer to have limits to be bidirectional by default, though it may not be straightforward to implement without doubling computation time. (I wouldn't care about this performance penalty, I could still just ask for a unilateral limit in the rare cases where that's what I'm interested in.)
  2. Currently limit is somewhat messy since it mixes real and compex limits in a non-obvious way. Its docstring exclusively uses z as the variable, which is conventionally used for complex variables, but then the direction can only be + or - which is indicative of real limits. But for such "somewhat real" limits, it's peculiar to have limit(1/x, x, 0, dir='+-') result in zoo. The two unilateral limits don't coincide so the limit (over the reals) does not exist. IMHO the limit function is in dire need of more precise documentation concerning its actual behavior and its treatment of real/complex limits.
  3. As for raising exceptions for non-existing limits: I'd rather have it return NaN or None. Then it's up to the user to investigate further. I thought exceptions are for cases where something unexpected comes up. But here it's a perfectly reasonable outcome for a limit computation. IMO it's not comparable to TypeErrors or NotImplementedErrors etc.: everything is implemented and there's a clear conclusion that SymPy is able to compute. Having it raise an exception in such a situation feels weird.

@asmeurer
Copy link
Member

My understanding is that complex limits aren't implemented at all, and it would require some work to implement them.

I would also like to see the default move to bi-directional. The main concern is how robust the bi-directional code is.

As for raising exceptions for non-existing limits: I'd rather have it return NaN or None. Then it's up to the user to investigate further. I thought exceptions are for cases where something unexpected comes up. But here it's a perfectly reasonable outcome for a limit computation. IMO it's not comparable to TypeErrors or NotImplementedErrors etc.: everything is implemented and there's a clear conclusion that SymPy is able to compute. Having it raise an exception in such a situation feels weird.

The proper thing to return would be Limit. Symbolic functions shouldn't return None, which isn't a SymPy object.

The advantage of an exception is that it can indicate in the message why the limit didn't work, e.g., for a bidirectional limit that the two-sided limits exist but were not equal.

@asmeurer
Copy link
Member

The main concern is how robust the bi-directional code is.

For example, the code that deals with bidirectional limits just does a simple == comparison:

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

Meaning if the limits are equal but don't come out exactly symbolically identical, it will think the limit doesn't exist.

I'm also interested to know if limit algorithm itself could work bi-directionally, rather than computing the whole limit twice. That would be more efficient and potentially more robust, depending on how the algorithm works.

My guess is that if we made dir='-+' the default before addressing these issues we would see a lot of bug reports related to it.

Finally, it should be noted that changing the default for dir would be a backwards incompatible break, and I'm not sure how we could reasonably deprecate it. Maybe compute both sides and raise a deprecation warning if they are unequal and dir isn't explicitly passed.

@gschintgen
Copy link
Contributor

The proper thing to return would be Limit. Symbolic functions shouldn't return None, which isn't a SymPy object.

The advantage of an exception is that it can indicate in the message why the limit didn't work, e.g., for a bidirectional limit that the two-sided limits exist but were not equal.

I guess what bothers me is that to me it feels a bit as if solveset raised an exception each time an equation doesn't have a solution. But I do agree that None would not be appropriate. A Limit could be interpreted as "I don't know how to compute that limit" when SymPy does in fact full well know it does not exist. Personally I still think that would be better than a stacktrace. Maybe a Limit and some textual warning that is printed. But that's not very typical for SymPy either. (IIRC Maxima does that sometimes: print a result but emit a warning at the same time.)

@gschintgen
Copy link
Contributor

Meaning if the limits are equal but don't come out exactly symbolically identical, it will think the limit doesn't exist.

Except if execution doesn't end up in gruntz. With current master:

In [1]: limit(1/z, z, 0, dir='+')
Out[1]: ∞

In [2]: limit(1/z, z, 0, dir='-')
Out[2]: -∞

In [3]: limit(1/z, z, 0, dir='+-')
Out[3]: zoo

Whether or not differing left and right limits entail an exception or zoo seems to depend on the algorithm that ends up being called. That's not quite consistent with the quoted excerpt. Maybe that's a result of the new is_meromorphic handling in the limit code which is a priori a welcome addition. But in that case a short note in the docstring couldn't hurt.

@sachin-4099
Copy link
Contributor

sachin-4099 commented May 30, 2020

In the meromorphic case, the limit is not indeterminate, it is zoo from any direction.
I agree, a short note to the the docstring can be added.

@oscarbenjamin
Copy link
Contributor

Maybe there can be a special kind of UnequalBidirectionalLimit object representing the fact that the two sided limit does not exist but also giving a way to get what the each of the one sided limits is. Perhaps it could be slightly less conclusive and include the condition as Eq(l, r) so that you could e.g. call simplify or subs to resolve the Eq if it does not evaluate e.g.:

In [2]: p = Piecewise((x*cos(y), y<0), (1-y, y>=0))                                                                               

In [3]: p                                                                                                                         
Out[3]: 
⎧xcos(y)  for y < 0
⎨                   
⎩ 1 - y    otherwise

In [4]: p.limit(y, 0, '+')                                                                                                        
Out[4]: 1

In [5]: p.limit(y, 0, '-')                                                                                                        
Out[5]: x

In [6]: p.limit(y, 0, '+-')                                                                                                       
---------------------------------------------------------------------------
ValueError

This could perhaps return:

In [6]: L = p.limit(y, 0, '+-')                                                                                                       

In [6]: L
Out[6]: BidirectionalLimit(x, 1)

In [6]: L.subs(x, 1)                                                                                                       
Out[6]: 1

In [6]: L.exists
Out[6]: x = 1

In [6]: L.lhs
Out[6]: x

In [6]: L.subs(x, 2)
Out[6]: UnequalBidirectionalLimit(2, 1)

@oscarbenjamin
Copy link
Contributor

I guess UnequalBidrectionalLimit can just be nan.

@latot
Copy link
Contributor Author

latot commented May 30, 2020

Hi, just as a complement, the direction concept depdends in the numbers of vars we are working, so in the actual case of the issue, there is one var, so the only directions are (+) and (-), but if we have 2 var then we will have (var1, var2)... (+,+), (+,-), (-+), (-,-), so the number of directions would be 2^number of vars..., so when we have a lot of variables we will need check a lot of directions.

When we want the limit is just compare all the values i write above and if they are the same we can know there is a limit, this is just to consider not only the 1 var case.

@asmeurer
Copy link
Member

A Limit could be interpreted as "I don't know how to compute that limit" when SymPy does in fact full well know it does not exist.

There are other ways a limit might not exist other than the bidirectional limit case, and I think it's reasonable to assume SymPy could show it in some of those cases. We already have AccumBounds which is close to this for periodic limits. By the way, I noticed that integrate(1/x, (x, -1, 1)) currently gives nan, which is an example of an integral that doesn't exist, but I don't know if it's intentional.

Hi, just as a complement, the direction concept depdends in the numbers of vars we are working, so in the actual case of the issue, there is one var, so the only directions are (+) and (-), but if we have 2 var then we will have (var1, var2)... (+,+), (+,-), (-+), (-,-), so the number of directions would be 2^number of vars..., so when we have a lot of variables we will need check a lot of directions.

Multidimensional limits aren't really supported. There are more than 4 directions for 2 variables. There are an infinite number of directions. For a limit to exist you want it to be path independent. I don't think it's worth discussing multidimensional or complex limits too much until we can actually implement something useful for them.

Whether or not differing left and right limits entail an exception or zoo seems to depend on the algorithm that ends up being called. That's not quite consistent with the quoted excerpt. Maybe that's a result of the new is_meromorphic handling in the limit code which is a priori a welcome addition. But in that case a short note in the docstring couldn't hurt.

As for zoo, I think if a limit gives oo in one direction and -oo in another then zoo is a reasonable answer. Or at least it is for 1/z, where zoo is the complex limit. Maybe it would be more problematic in cases where the complex limit isn't always zoo.

skirpichev added a commit to skirpichev/diofant that referenced this issue Mar 28, 2021
skirpichev added a commit to skirpichev/diofant that referenced this issue Mar 28, 2021
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

7 participants