Repository: sympy/sympy
Pull request: Fix: Improve Q.infinite(Expr) handling for symbolic conditions and complex infinity
<!-- Your title above should be a short description of what
was changed. Do not include the issue number in the title. -->

#### References to other Issues or PRs
<!-- If this pull request fixes an issue, write "Fixes #NNNN" in that exact
format, e.g. "Fixes #1234" (see
https://tinyurl.com/auto-closing for more information). Also, please
write a comment on that issue linking back to this pull request once it is
open. -->"Fixes #27610"


#### Brief description of what is fixed or changed
**Issue:**
ask(Q.infinite(I * oo)) returns None instead of True.
**Fix:**
Added Q.infinite(Expr) to handle symbolic conditions more effectively.
Ensured better handling of cases like x * oo, x + I * oo, and 1 / x under different assumptions

#### Other comments
Enhanced Q.infinite(Expr) to handle symbolic conditions correctly-

1) Fixes issue where ask(Q.infinite(I * oo)) returned None instead of True.
2) Added extensive test coverage for symbolic expressions.
3) Ensures consistency for operations involving infinity (oo, I * oo, zoo).
4) Handles cases where Expr includes symbols with Q.finite, Q.infinite, or Q.complex conditions.

#### Release Notes

<!-- Write the release notes for this release below between the BEGIN and END
statements. The basic format is a bulleted list with the name of the subpackage
and the release note for this PR. For example:

* solvers
  * Added a new solver for logarithmic equations.

* functions
  * Fixed a bug with log of integers. Formerly, `log(-x)` incorrectly gave `-log(x)`.

* physics.units
  * Corrected a semantical error in the conversion between volt and statvolt which
    reported the volt as being larger than the statvolt.

or if no release note(s) should be included use:

NO ENTRY

See https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more
information on how to write release notes. The bot will check your release
notes automatically to see if they are formatted correctly. -->

<!-- BEGIN RELEASE NOTES -->

* assumptions
  * ```ask(Q.infinite(I*oo))``` now returns ```True``` instead of ```None```.

<!-- END RELEASE NOTES -->


Problem statement:
ask(Q.infinite(I*oo)) gives None
The `Q.infinite` predicate returns `None` for expressions like `1 + I * oo`, 
which involve complex numbers where the imaginary part is infinite. 

This behavior is unexpected because the magnitude of the complex number should be 
considered infinite due to the presence of `I * oo`.

**Expected Behavior**: The predicate should return `True` when the magnitude of a complex number is infinite, such as `1 + I * oo`.

**Code Snippet**:
   ```python
   from sympy import I, oo, Q, ask
   print(ask(Q.infinite(1 + I * oo)))  # Expected: True, but returns None
   ```

Hints from issue discussion:
The Q.infinite predicate in SymPy is designed to identify expressions that are explicitly infinite, such as oo, -oo, or zoo (complex infinity). In your example, 1 + I * oo, the expression combines a finite real part (1) with an infinite imaginary part (I * oo). SymPy does not automatically interpret this combination as an infinite magnitude, which is why ask(Q.infinite(1 + I * oo)) returns None.

To address this, you can manually check if any component of the complex number is infinite and then determine the magnitude accordingly. 

```
from sympy import I, oo, Q, ask, Abs, re, im, Or

# Define the complex expression
expr = 1 + I * oo

# Check if the real or imaginary part is infinite
is_real_infinite = ask(Q.infinite(re(expr)))
is_imag_infinite = ask(Q.infinite(im(expr)))

# Determine if the magnitude is infinite
is_magnitude_infinite = Or(is_real_infinite, is_imag_infinite)

print(is_magnitude_infinite)  # Outputs: True

```

In this code, re(expr) and im(expr) extract the real and imaginary parts of the expression, respectively. The ask(Q.infinite(...)) function checks if each part is infinite. The Or function then evaluates to True if either component is infinite, indicating that the magnitude of the complex number is indeed infinite.
You are welcome to make a PR which fixes this.
Note that oo*I is not the same thing as zoo. See https://en.wikipedia.org/wiki/Directed_infinity.

Also, the current behavior isn't wrong. Returning None is always correct. But it would be nice to be able to handle this case. 
It's also worth mentioning the old assumptions are able to handle this case:
```python
(I*oo).is_infinite # gives True
(I*oo).is_finite # gives False
```

Commit message: Removed whitespace

Patch to review:
diff --git a/sympy/assumptions/handlers/calculus.py b/sympy/assumptions/handlers/calculus.py
index 263bed6da00c..aac68c7d875d 100644
--- a/sympy/assumptions/handlers/calculus.py
+++ b/sympy/assumptions/handlers/calculus.py
@@ -4,7 +4,7 @@
 """
 
 from sympy.assumptions import Q, ask
-from sympy.core import Add, Mul, Pow, Symbol
+from sympy.core import Expr,Add, Mul, Pow, Symbol
 from sympy.core.numbers import (NegativeInfinity, GoldenRatio,
     Infinity, Exp1, ComplexInfinity, ImaginaryUnit, NaN, Number, Pi, E,
     TribonacciConstant)
@@ -232,6 +232,15 @@ def _(expr, assumptions):
     return True
 
 
+@InfinitePredicate.register(Expr)
+def _(expr, assumptions):
+    if assumptions is True:
+        result = ask(~Q.finite(expr))
+    else:
+        result = ask(~Q.finite(expr),assumptions)
+    return result
+
+
 # PositiveInfinitePredicate
 
 
diff --git a/sympy/assumptions/tests/test_query.py b/sympy/assumptions/tests/test_query.py
index 4bf385d8575b..ae3832666e15 100644
--- a/sympy/assumptions/tests/test_query.py
+++ b/sympy/assumptions/tests/test_query.py
@@ -1107,6 +1107,21 @@ def test_bounded():
     assert ask(Q.finite(cos(x) + sin(x))) is True
 
 
+def test_unbounded_expr():
+    # https://github.com/sympy/sympy/issues/27610
+    assert ask(Q.infinite(I * oo)) is True
+    assert ask(Q.infinite(1 + I*oo)) is True
+    assert ask(Q.infinite(3 * (I * oo))) is True
+    assert ask(Q.infinite(-I * oo)) is True
+    assert ask(Q.infinite(1 + zoo)) is True
+    assert ask(Q.infinite(I * zoo)) is True
+    assert ask(Q.infinite(x / y), Q.infinite(x) & Q.finite(y) & ~Q.zero(y)) is True
+    assert ask(Q.infinite(I * oo - I * oo)) is None
+    assert ask(Q.infinite(x * I * oo)) is None
+    assert ask(Q.infinite(1 / x), Q.finite(x) & ~Q.zero(x)) is False
+    assert ask(Q.infinite(1 / (I * oo))) is False
+
+
 def test_issue_27441():
     # https://github.com/sympy/sympy/issues/27441
     assert ask(Q.composite(y), Q.integer(y) & Q.positive(y) & ~Q.prime(y)) is None


Code review comments:
diff_hunk: @@ -232,6 +232,15 @@ def _(expr, assumptions):
     return True
 
 
+@InfinitePredicate.register(Expr)
+def _(expr, assumptions):
+    if assumptions is True:
+        result = ask(~Q.finite(expr))
+    else:
+        result = ask(~Q.finite(expr),assumptions)
+    return result
path: sympy/assumptions/handlers/calculus.py
text: @user1:
The following should accomplish exactly the same thing:
```python
return ask(~Q.finite(expr),assumptions)
```


@author:
Yes even I thought so but it does not which is the reason when no assumptions have been passed I have to handle it a bit differently. As without any assumptions when I ask Q.infinite(I*oo) it calls the finite handler with assumptions as True, which actually changes the result from False in this case to None(that is asking if I*oo is finite or not). It you still think though something better can be done, please do let me know thanks. 

@user1:
I'll take a look at it. By the way, in the future try to have shorter branch names. The branch name should be easy to type. 

@user1:
I tried changing it and it seems to work

diff_hunk: @@ -1107,6 +1107,21 @@ def test_bounded():
     assert ask(Q.finite(cos(x) + sin(x))) is True
 
 
+def test_unbounded_expr():
+    # https://github.com/sympy/sympy/issues/27610
path: sympy/assumptions/tests/test_query.py
text: @user1:
A link to the issue isn't needed as this is not a bug fix

@author:
I have removed the link to the issue here.

diff_hunk: @@ -1107,6 +1107,21 @@ def test_bounded():
     assert ask(Q.finite(cos(x) + sin(x))) is True
 
 
+def test_unbounded_expr():
path: sympy/assumptions/tests/test_query.py
text: @user1:
I think it would be better to title this "test_unbounded"

@author:
I have changed the name of the test function as told.