Repository: sympy/sympy
Pull request: Fix limit evaluation for 2**x and E**x at -oo
Fix incorrect limit for exponential functions with numeric base

#### References to other Issues or PRs

Fixes #28130

#### Brief description of what is fixed or changed

This PR fixes a bug in the evaluation of limits for exponential expressions like `2**x` as `x → -oo`, which previously returned `oo` instead of the correct result `0`.

The root cause was that the `pow_heuristics` method retrieved `z0` from `self.args`, which does not reflect the transformed value of `z0` (e.g., `-oo` → `oo`) inside `doit()`. This led to incorrect behavior for expressions with numeric (non-`E`) bases.

The fix involves modifying `doit()` to pass the updated `z` and `z0` explicitly to `pow_heuristics()`:

```python
# In Limit.doit():
if e.is_Pow:
    r = self.pow_heuristics(e, z, z0)
    if r is not None:
        return r
```

```python
# pow_heuristics updated to:
def pow_heuristics(self, e, z, z0):
    b1, e1 = e.base, e.exp
    if not b1.has(z):
        res = limit(e1 * log(b1), z, z0)
        return exp(res)
```

#### Other comments

After this change, both symbolic and numeric exponential limits now behave consistently:

```python
>>> from sympy import E, exp, simplify, limit, symbols, oo, S, log, Dummy
>>> x = symbols('x')
>>> limit(2**x, x, -oo)
0
>>> limit(E**x, x, -oo)
0
```

#### Release Notes

<!-- BEGIN RELEASE NOTES -->

* series

  * Fixed incorrect result for `limit(2**x, x, -oo)` which previously returned `oo` instead of `0`.

<!-- END RELEASE NOTES -->



Problem statement:
limit of exponential with non-E base gives oo instead of zero
Taking a limit of an exponential function with non-E base gives oo instead of zero:
```python
In [5]: limit(E**x, x, -oo)
Out[5]: 0

In [6]: limit(2**x, x, -oo)
Out[6]: ∞
```

Hints from issue discussion:
I’ve been looking into this issue and I think I’ve found a possible explanation.

The problem seems to stem from how the `Limit.doit()` method handles the `z0` (the limit point). When `z0` is infinite , the method updates `z0` locally in this block:

```python
if z0.is_infinite:
    cdir = sign(z0)
    cdir = cdir / abs(cdir)
    e = e.subs(z, cdir*z)
    dir = "-"
    z0 = S.Infinity  # z0 is updated here locally
```

However, later in the method, when handling `Pow` expressions using `pow_heuristics()`, the original `z0` is retrieved from `self.args`:

```python
def pow_heuristics(self, e):
    _, z, z0, _ = self.args
```

This means that inside `pow_heuristics()`, the old `z0` (e.g. `-oo`) is used, **not** the updated one (`oo`). As a result, the direction (`dir`) and the transformation logic for limits at infinity are mismatched, which leads to incorrect evaluation, such as:

```python
limit(2**x, x, -oo)  # returns ∞ instead of 0
```

In contrast, `limit(E**x, x, -oo)` works correctly likely due to different internal handling or simplification of `E**x`.

So, the issue seems to be that `pow_heuristics()` is using stale data by referencing `self.args` instead of the transformed and updated `z0`.
A possible fix would be to pass the updated `z0` (and `z`) explicitly to the `pow_heuristics()` method instead of relying on `self.args`. For example:

```python
# In doit():
if e.is_Pow:
    r = self.pow_heuristics(e, z, z0)
    if r is not None:
        return r
```

And update the signature of `pow_heuristics` to:

```python
def pow_heuristics(self, e, z, z0):
    b1, e1 = e.base, e.exp
    if not b1.has(z):
        res = limit(e1 * log(b1), z, z0)
        return exp(res)
```

This ensures the method uses the correctly transformed `z0` (like `oo` instead of `-oo`) when computing the limit.

After applying this change, the issue is resolved:

```python
>>> from sympy import E, exp, simplify, limit, symbols, oo, S, log, Dummy
>>> x = symbols('x')
>>> limit(2**x, x, -oo)
0
>>> limit(E**x, x, -oo)
0
```
Let me know if this is the right direction — I’d be happy open a PR

That sounds reasonable.

Commit message: Added entries to .mailmap

Patch to review:
diff --git a/.mailmap b/.mailmap
index 42cf0d8f59df..b038691709cf 100644
--- a/.mailmap
+++ b/.mailmap
@@ -781,6 +781,8 @@ Jason Siefken <siefkenj@gmail.com>
 Jason Tokayer <jason.tokayer@gmail.com>
 Jason Tokayer <jason.tokayer@gmail.com> <jason.tokayer@capitalone.com>
 Jatin Bhardwaj <bhardwajjatin093@gmail.com> <148186488+Jatinbhardwaj-093@users.noreply.github.com>
+Jatin Gaur <jatin22101@iiitnr.edu.in> <jatin22101@iiitnr.edu.in>
+Jatin Gaur <jatingaur18@users.noreply.github.com> <134034110+jatingaur18@users.noreply.github.com>
 Jatin Yadav <jatinyadav25@gmail.com>
 Javed Nissar <javednissar@gmail.com>
 Jay Patankar <patankarjays@gmail.com> Jay-Patankar <107458263+Jay-Patankar@users.noreply.github.com>
diff --git a/sympy/series/limits.py b/sympy/series/limits.py
index b7109ea3ce10..46d892c4ca96 100644
--- a/sympy/series/limits.py
+++ b/sympy/series/limits.py
@@ -177,8 +177,7 @@ def free_symbols(self):
         return isyms
 
 
-    def pow_heuristics(self, e):
-        _, z, z0, _ = self.args
+    def pow_heuristics(self, e, z, z0):
         b1, e1 = e.base, e.exp
         if not b1.has(z):
             res = limit(e1*log(b1), z, z0)
@@ -339,7 +338,7 @@ def set_signs(expr):
             from sympy.simplify.powsimp import powsimp
             e = powsimp(e)
             if e.is_Pow:
-                r = self.pow_heuristics(e)
+                r = self.pow_heuristics(e, z, z0)
                 if r is not None:
                     return r
             try:


Code review comments:
diff_hunk: @@ -781,6 +781,8 @@ Jason Siefken <siefkenj@gmail.com>
 Jason Tokayer <jason.tokayer@gmail.com>
 Jason Tokayer <jason.tokayer@gmail.com> <jason.tokayer@capitalone.com>
 Jatin Bhardwaj <bhardwajjatin093@gmail.com> <148186488+Jatinbhardwaj-093@users.noreply.github.com>
+Jatin Gaur <jatin22101@iiitnr.edu.in> <jatin22101@iiitnr.edu.in>
+Jatin Gaur <jatingaur18@users.noreply.github.com> <134034110+jatingaur18@users.noreply.github.com>
path: .mailmap
text: @user3:
Change this to something like:
```
Jatin Gaur <jatin22101@user1.edu.in> <jatin22101@user1.edu.in>
Jatin Gaur <jatin22101@user1.edu.in> <134034110+jatingaur18@user2.noreply.github.com>
```
Both lines should begin with the same name and email address and that will be the name that is recorded in the AUTHORS file:
https://github.com/sympy/sympy/blob/e17302a7e58f564550f0517061bf9ad376e5957f/.mailmap#L138-L148