Repository: sympy/sympy
Pull request: watch for Add->Mul in as_numer_denom
<!-- 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 #22837 

#### Brief description of what is fixed or changed

```python
formerly

>>> Add(0,(x+y)/z/-2,evaluate=0).as_numer_denom()
(z*(x + y - 1) + 1, 2*z)

now
>>> Add(0,(x+y)/z/-2,evaluate=0).as_numer_denom()
(-x - y, 2*z)
```
#### Other comments


#### 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.

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 -->
NO ENTRY
<!-- END RELEASE NOTES -->


Problem statement:
Solve simplest algebraic equations with dummy parameter
Very strange behaviour of `solve` command in SymPy, when I try to solve quadratic equation with parameter `a` (which is dummy here, because solution doesn't depends on it):
```
from sympy import *
x, a = symbols("x a")
eq = Eq(0, (4 - 4*x + x**2)/(4*a**2))
print(solve(eq, x))
print(solve(simplify(eq), x))
```
Output:
```
[2 - sqrt(a**2 - 1)/a, 2 + sqrt(a**2 - 1)/a]
[2]
```
Just `solve` gives two (!) solutions, which depend on `a`. After symplifying it gives only solution `x=2`, which is correct. What happens? Command `solveset` works correctly, but I am interested in using `solve` command. My SymPy version is 1.8.

Hints from issue discussion:
Simpler example:
```
from sympy import *
x, a = symbols("x a")
eq = Eq(0, x / (2 * a))
print(solve(eq, x))
print(solve(simplify(eq), x))
``` 
Output:
```
[(a - 1)/a]
[0]
```
But if we swap sides of the equation `eq = Eq(x / (2 * a), 0)`, then `solve` solves it correctly!
Not sure what happens, but if you do 
```
eq = (4 - 4*x + x**2)/(4*a**2)
print(solve(eq, x))
```
You get the expected result.

I guess that there should be something like
```
if isinstance(expr, Eq):
   expr = expr.rhs - expr.lhs
```
in `solve`, but haven't checked (and it looks here like not).

Also
```
eq = Eq((4 - 4*x + x**2)/(4*a**2), 0)
print(solve(eq, x))
```
works, so looks like a bug.
Thank you, Oscar! I obtained this quadratic equation (with much more parameters) after substitution in the solution of ordinary differential equation, so it's not very convenient for me to do these manual reorderings and simplifications, I just want to solve it as is. The problem is somewhere in the order of parts of the equation: `x/(2*a)=0` solved correctly, but `0=x/(2*a)` - incorrectly. Another hint: `0=x/(1*a)` gives correct answer, but using any other constant instead of 1 and -1 in denominator leads to wrong answer.
```python
>>> from sympy import *
>>> x, a = symbols("x a")
>>> eq = Eq(0, (4 - 4*x + x**2)/(4*a**2))
>>> print(solveset(eq, x))
>>> print(solveset(simplify(eq), x))
FiniteSet(2)
FiniteSet(2)
```
`solveset` seems to give the right answer in the given example.
The `solve` function has lots of weird code paths depending on exactly how the input is provided e.g. you can get different results for `solve(eq, x)` vs `solve([eq], x)` etc or different results for `Eq` vs `Expr`:
```python
In [2]: solve(eq, x)
Out[2]: 
⎡       ________         ________⎤
⎢      ╱  2             ╱  2     ⎥
⎢    ╲╱  a  - 1       ╲╱  a  - 1 ⎥
⎢2 - ───────────, 2 + ───────────⎥
⎣         a                a     ⎦

In [3]: solve([eq], x)
Out[3]: [(2,)]
```
The main branch is here:
https://github.com/sympy/sympy/blob/d822fcba181155b85ff2b29fe525adbafb22b448/sympy/solvers/solvers.py#L1106-L1109
Try working backwards though and see if you can figure out when `bare_f` is true or false or what the point of it is.
It's a problem with `as_numer_denom()`

Commit message: do not return 0 arg in Eq.rewrite(Add)

Patch to review:
diff --git a/sympy/core/add.py b/sympy/core/add.py
index 890d31369227..88dfbbc0c298 100644
--- a/sympy/core/add.py
+++ b/sympy/core/add.py
@@ -613,6 +613,8 @@ def as_numer_denom(self):
         """
         # clear rational denominator
         content, expr = self.primitive()
+        if not isinstance(expr, self.func):
+            return Mul(content, expr, evaluate=False).as_numer_denom()
         ncon, dcon = content.as_numer_denom()
 
         # collect numerators and denominators of the terms
diff --git a/sympy/core/relational.py b/sympy/core/relational.py
index 7a4be766fdb1..3f551dda3352 100644
--- a/sympy/core/relational.py
+++ b/sympy/core/relational.py
@@ -578,7 +578,8 @@ def _eval_rewrite_as_Add(self, *args, **kwargs):
         the result set pass `evaluate=True` to give L - R;
         if `evaluate=None` then terms in L and R will not cancel
         but they will be listed in canonical order; otherwise
-        non-canonical args will be returned.
+        non-canonical args will be returned. If one side is 0, the
+        non-zero side will be returned.
 
         Examples
         ========
@@ -595,6 +596,10 @@ def _eval_rewrite_as_Add(self, *args, **kwargs):
         """
         from .add import _unevaluated_Add, Add
         L, R = args
+        if L == 0:
+            return R
+        if R == 0:
+            return L
         evaluate = kwargs.get('evaluate', True)
         if evaluate:
             # allow cancellation of args
diff --git a/sympy/core/tests/test_expr.py b/sympy/core/tests/test_expr.py
index b332f89d3346..eccb45ad90d0 100644
--- a/sympy/core/tests/test_expr.py
+++ b/sympy/core/tests/test_expr.py
@@ -827,6 +827,10 @@ def test_as_numer_denom():
     assert ((A*B*C)**-1).as_numer_denom() == ((A*B*C)**-1, 1)
     assert ((A*B*C)**-1/x).as_numer_denom() == ((A*B*C)**-1, x)
 
+    # the following morphs from Add to Mul during processing
+    assert Add(0, (x + y)/z/-2, evaluate=False).as_numer_denom(
+        ) == (-x - y, 2*z)
+
 
 def test_trunc():
     import math
diff --git a/sympy/core/tests/test_relational.py b/sympy/core/tests/test_relational.py
index 21fcaf97118d..57d5d65d2d48 100644
--- a/sympy/core/tests/test_relational.py
+++ b/sympy/core/tests/test_relational.py
@@ -1034,6 +1034,9 @@ def test_Equality_rewrite_as_Add():
     assert eq.rewrite(Add) == 2*x
     assert eq.rewrite(Add, evaluate=None).args == (x, x, y, -y)
     assert eq.rewrite(Add, evaluate=False).args == (x, y, x, -y)
+    for e in (True, False, None):
+        assert Eq(x, 0, evaluate=e).rewrite(Add) == x
+        assert Eq(0, x, evaluate=e).rewrite(Add) == x
 
 
 def test_issue_15847():


Code review comments:
diff_hunk: @@ -613,6 +613,8 @@ def as_numer_denom(self):
         """
         # clear rational denominator
         content, expr = self.primitive()
+        if not isinstance(expr, self.func):
path: sympy/core/add.py
text: @author:
```suggestion
        if not isinstance(expr, Add):
```