Repository: sympy/sympy (Python)

Issue:
Multiple subs() yield incorrect results
Given the SymPy code (using latest master as of Oct 15, 2019)

```python
from sympy import *
AMPLRHO,SINHWRHO,xx0,SINHWZ,xx2,AMPLZ,const_dz,const_drho = symbols('AMPLRHO SINHWRHO xx0 SINHWZ xx2 AMPLZ const_dz,const_drho',real=True)
x0,x1,x2,x3 = symbols('x0 x1 x2 x3',real=True)

expr = sqrt(AMPLRHO**2*(const_drho*xx0 + 0.851960487902352830680680629342/(exp(x0) - exp(-x0)))**2 + AMPLZ**2*(const_dz*xx2 + (exp(x3) - exp(-x3))/(exp(x2) - exp(-x2)))**2)

expr = expr.subs(x1,0.414047984412679975621343929773)
expr = expr.subs(AMPLRHO,0.446696088287483306089598045219)
expr = expr.subs(xx0,0.41187635273495315502145786013)
expr = expr.subs(AMPLZ,0.588118850910659518582690452604)
expr = expr.subs(x2,1.24435070438552085937065080003)
expr = expr.subs(x0,1.00527253303887608436429294321)
expr = expr.subs(const_drho,0.595088792659128618112163167098)
expr = expr.subs(xx2,0.540732016614090760242561373161)
expr = expr.subs(const_dz,0.182385133527420140175934193394)
expr = expr.subs(x3, 0.672860265757547005562015751411)
print(expr)
```

the result from the ```print()``` statement at the bottom is
```0.581139663779179```

which is incorrect. Bisecting the substitution a bit (by converting to mathematica_code() and performing all substitutions on the expressions after each substitution above), I find the culprit is near the const_dz line.

Using the following Mathematica code, 
```mathematica
(AMPLRHO^2*(constdrho*xx0 + 
                 0.851960487902353/(Exp[x0] - Exp[-x0]))^2 + 
             AMPLZ^2*(constdz*
                  xx2 + (Exp[x3] - Exp[-x3])/(Exp[x2] - 
                    Exp[-x2]))^2)^(1/2) /. 
          x1 -> 0.414047984412679975621343929773 /. 
         AMPLRHO -> 0.446696088287483306089598045219 /. 
        xx0 -> 0.41187635273495315502145786013 /. 
       AMPLZ -> 0.588118850910659518582690452604 /. 
      x2 -> 1.24435070438552085937065080003 /. 
     x0 -> 1.00527253303887608436429294321 /. 
    constdrho -> 0.595088792659128618112163167098 /. 
   xx2 -> 0.540732016614090760242561373161 /. 
  constdz -> 0.182385133527420140175934193394 /. 
 x3 -> 0.672860265757547005562015751411
```
I get the correct answer of 
```0.4233805076587912```

which also appears if I rearrange the order of the subs above (using random.org):
```python
from sympy import *
AMPLRHO,SINHWRHO,xx0,SINHWZ,xx2,AMPLZ,const_dz,const_drho = symbols('AMPLRHO SINHWRHO xx0 SINHWZ xx2 AMPLZ const_dz,const_drho',real=True)
x0,x1,x2,x3 = symbols('x0 x1 x2 x3',real=True)

expr = sqrt(AMPLRHO**2*(const_drho*xx0 + 0.851960487902352830680680629342/(exp(x0) - exp(-x0)))**2 + AMPLZ**2*(const_dz*xx2 + (exp(x3) - exp(-x3))/(exp(x2) - exp(-x2)))**2)

expr = expr.subs(x0,1.00527253303887608436429294321)
expr = expr.subs(xx2,0.540732016614090760242561373161)
expr = expr.subs(x2,1.24435070438552085937065080003)
expr = expr.subs(xx0,0.41187635273495315502145786013)
expr = expr.subs(x1,0.414047984412679975621343929773)
expr = expr.subs(x3, 0.672860265757547005562015751411)
expr = expr.subs(const_dz,0.182385133527420140175934193394)
expr = expr.subs(const_drho,0.595088792659128618112163167098)
expr = expr.subs(AMPLZ,0.588118850910659518582690452604)
expr = expr.subs(AMPLRHO,0.446696088287483306089598045219)
print(expr)
```

The result in this case is the correct one, ```0.4233805076587912```.

**Note that this issue does not appear to exist in SymPy 1.4.**


Discussion:
After some bisection, the offending commit appears to be:

> commit b69a54dc958e1d291c8785261a659f7bb2a8a0e4
> Author: czgdp1807 <singh.23@iitj.ac.in>
> Date:   Wed Jul 3 18:29:49 2019 +0530
> 
>     as_relational added in Range
> 

(the previous commit in ```git log```, ```c351017b7e7ea7348e86ba008242c0c132f9ce6f``` does not exhibit this issue).
One issue I found in master is the following,

```python
from sympy import *
x = symbols('x')
expr = 0.5*exp(x) + 0.1 - 0.5*exp(-x)
print(expr)
print(expr**2)
```
There's an errorneous sign flip

My original bisection may or may not have been correct. I performed the test across many more commits surrounding b69a54d and found some commits yield the incorrect result of ```0.581139663779179```, and some yield the correct result of ```0.423380507658791```. There isn't a clear pattern due to nonlinearities in time in git log.

Using the same ordering as ```git log```, here are the results from this test:

```
d4359d8d8fb20c33a1ff6db4a8494e95bfae23ea 0.581139663779179
922536c920d1e53f2cb23c35b8d94b324f666f5a 0.581139663779179
411757c159e16a56b5ad8141cb07dc32f4277caa 0.581139663779179
1a8470addb44d175c6064e2a0aa4ac6b35d29e5d 0.581139663779179
1bb112f4d9d9001dc5c7619d5f9dd9040e35b79f 0.581139663779179
21cbaeacacc5f15c30337061125899fdd3c39939 0.581139663779179
7013081ba5219bd629e18cfe53ca21f654b4a474 0.581139663779179
52c645f49f75ceb106e11c8c3110afbc70c9e7db 0.581139663779179
9874d6448688fd9864ee71a423814c3fb7ca1a78 0.581139663779179
729c21cef31a6ad19ed1589f424eb0bbd76ff00b 0.581139663779179
8d32be3954cda703389a76821ea68b4838db1c32 0.423380507658791
e20f0defabb9fe389a24b27f638263fe4a1d44e4 0.423380507658791
2e7095c6cb2b29c5417046d1c27f3060c5cd1b32 0.423380507658791
ebcf0ed6f3b7748c8289f963087b44c561bf7e65 0.423380507658791
**b69a54dc958e1d291c8785261a659f7bb2a8a0e4 0.581139663779179**
c351017b7e7ea7348e86ba008242c0c132f9ce6f 0.423380507658791
94fb8990195c4849e2dab91f99d8dda89596fdab 0.423380507658791
4a301315c8a6143dd60649736b10a75c160aa11e 0.423380507658791
16015783387a8cd8930260172672503238b26570 0.423380507658791
d43ea32fcf7f202260cb4211e004e8a609da2af1 0.423380507658791
a9853ab3d9b949be43ef577b54c1dc60823ad1cb 0.423380507658791
f8d2b553c8ac823c3aa76f7a6dfe72c0b7535d2b 0.423380507658791
da6d75e5dc3c7730417282432821e8a0a74ada95 0.423380507658791
b9061ba5df98a9d2a47f404c1748f544af19390a 0.581139663779179
12d8f5354db591ba6600c50f4188b7b973a859fc 0.581139663779179
e346786eebca765334e46920568b2f40c64433e6 0.581139663779179
920ccc0ceb7d7db49270eedcd95f12b4ccd0d4ad 0.423380507658791
4729a80f0b89acd542f6c1b461fe6ec245000a75 0.423380507658791
4e910a730956fc35bbfed226161001de4ea79dd0 0.423380507658791
```
I'll perform a longer test and update this comment.
@zachetienne, `git bisect` command is an easier way.

```
git bisect start
git reset --hard upstream/master
git bisect bad
git reset --hard sympy-1.4
git bisect good
git bisect run python test_17735.py
```
where `test_17735.py` has,

```
from sympy import *
x = symbols('x')
expr = 0.5*exp(x) + 0.1 - 0.5*exp(-x)
assert int((expr**2).subs(x, 1.0)) == 1
```

Gave me c1e25a445f1c27829ea8cf0e04dbdd6d749834ce as the offending commit and reverting that commit indeed fixes your problem.
@isuruf : @asmeurer had suggested I try git bisect, but I didn't know how to set it up. Thanks for that -- bisection using my code snippet uncovered the same offending commit!
@zachetienne, can you send a PR to fix the issue? That commit assumes that there is only one coefficient with the biggest absolute value and calculates the sign just once.
@isuruf : Sorry, my SymPy knowledge isn't sufficient to understand that patch. Since it fundamentally breaks SymPy and itself addresses an issue (https://github.com/sympy/sympy/issues/2993) regarding something relatively minor (how the output looks), I'd suggest we revert the PR associated with https://github.com/sympy/sympy/issues/2993 (i.e., https://github.com/sympy/sympy/pull/16762) until someone volunteers to fix.
The following diff should fix the issue:
```diff
diff --git a/sympy/core/add.py b/sympy/core/add.py
index dadf372..75f9925 100644
--- a/sympy/core/add.py
+++ b/sympy/core/add.py
@@ -389,13 +389,14 @@ def _eval_power(self, e):
             c, m = zip(*[i.as_coeff_Mul() for i in self.args])
             big = 0
             float = False
+            s = dict()
             for i in c:
                 float = float or i.is_Float
-                if abs(i) > big:
+                if abs(i) >= big:
                     big = 1.0*abs(i)
-                    s = -1 if i < 0 else 1
+                    s[i] = -1 if i < 0 else 1
             if float and big and big != 1:
-                addpow = Add(*[(s if abs(c[i]) == big else c[i]/big)*m[i]
+                addpow = Add(*[(s[c[i]] if abs(c[i]) == big else c[i]/big)*m[i]
                              for i in range(len(c))])**e
                 return big**e*addpow

diff --git a/sympy/core/tests/test_power.py b/sympy/core/tests/test_power.py
index d5c4817..69e5576 100644
--- a/sympy/core/tests/test_power.py
+++ b/sympy/core/tests/test_power.py
@@ -471,6 +471,9 @@ def test_issue_2993():
     eq = (2.3*x + 4)
     assert eq**2 == 16.0*(0.575*x + 1)**2
     assert (1/eq).args == (eq, -1)  # don't change trivial power
+    # issue 17735
+    q=.5*exp(x) - .5*exp(-x) + 0.1
+    assert int((q**2).subs(x, 1)) == 1
```
@smichr : Thanks for the patch! After applying it to the latest SymPy master, all of my code's unit tests (which validate hundreds of complicated SymPy expressions against trusted versions; https://github.com/zachetienne/nrpytutorial) now pass.
I just found another **wrong result** caused by the fix to issue #2993 that the latest patch (PR #17740 ) does not fix.

The following Python snippet should output ```0.561584136985528994504178506743``` to 30 significant digits (confirmed using both Mathematica and SymPy 1.4). I only get about 16 significant digits, and after bisecting the problem, it pointed precisely to the original commit (c1e25a445f1c27829ea8cf0e04dbdd6d749834ce). 

Indeed, after reverting the commit, I restore 30 significant digits.

```python
from sympy import *
M,r_at_max_density,x1,x2,x5,x7,x8,x10,x11,x12 = symbols('M r_at_max_density x1 x2 x5 x7 x8 x10 x11 x12',real=True)

expr = sqrt(4*M*x2**2*x5*(r_at_max_density**4 + x1*x10 - 2*x1*x11 - x12*(-x1 + x10))**2/(r_at_max_density**3*x7*x8**2*(x10 - 3*x11 + 2*x12)**2) + 1)/2
expr = expr.subs(x5,N('0.0977149162432475031936229342404',30))
expr = expr.subs(M, N('0.35190117650401031568208054523',30))
expr = expr.subs(x2, N('0.230823341810098541835951942887',30))
expr = expr.subs(x7, N('0.428572274457365406350449494195',30))

expr = expr.subs(x8, N('0.0966594956075474769169134801223',30))
# print(expr)
## ^ above is where this breaks.

expr = expr.subs(r_at_max_density,N('0.390911862903463913632151616184',30))
expr = expr.subs(x10,N('0.152812084558656566271750185933',30))
expr = expr.subs(x11,N('0.137562344465103337106561623432',30))
expr = expr.subs(x12,N('0.174259178881496659302933610355',30))
expr = expr.subs(x1,N('0.220745448491223779615401870086',30))

print(expr)
print(Abs(expr - N('0.561584136985528994504178506743',30)))
assert Abs(expr - N('0.561584136985528994504178506743',30)) < 1e-29
```
Upon further inspection, I found that when the x8 subs is performed, the expression

```sqrt(1 + 0.0170992456333788667034850458615*(r_at_max_density**4 + x1*x10 - 2*x1*x11 - x12*(-x1 + x10))**2/(r_at_max_density**3*x8**2*(x10 - 3*x11 + 2*x12)**2))/2```

becomes

```0.676416129939598001037750342485*sqrt(0.546401770664503357632415162049 + (r_at_max_density**4 + x1*x10 - 2*x1*x11 - x12*(-x1 + x10))**2/(r_at_max_density**3*(x10 - 3*x11 + 2*x12)**2))```

In SymPy 1.4 (or if the original commit, c1e25a445f1c27829ea8cf0e04dbdd6d749834ce, is reverted), the post-subs() expression is very different (one might even argue that it is simpler):

```
sqrt(1 + 1.83015512336985250895612217047*(r_at_max_density**4 + x1*x10 - 2*x1*x11 - x12*(-x1 + x10))**2/(r_at_max_density**3*(x10 - 3*x11 + 2*x12)**2))/2
```



Patch:
diff --git a/sympy/core/add.py b/sympy/core/add.py
index dadf3726799f..75f99250d191 100644
--- a/sympy/core/add.py
+++ b/sympy/core/add.py
@@ -389,13 +389,14 @@ def _eval_power(self, e):
             c, m = zip(*[i.as_coeff_Mul() for i in self.args])
             big = 0
             float = False
+            s = dict()
             for i in c:
                 float = float or i.is_Float
-                if abs(i) > big:
+                if abs(i) >= big:
                     big = 1.0*abs(i)
-                    s = -1 if i < 0 else 1
+                    s[i] = -1 if i < 0 else 1
             if float and big and big != 1:
-                addpow = Add(*[(s if abs(c[i]) == big else c[i]/big)*m[i]
+                addpow = Add(*[(s[c[i]] if abs(c[i]) == big else c[i]/big)*m[i]
                              for i in range(len(c))])**e
                 return big**e*addpow
 


Test patch:
diff --git a/sympy/core/tests/test_power.py b/sympy/core/tests/test_power.py
index d5c481781829..69e557669f98 100644
--- a/sympy/core/tests/test_power.py
+++ b/sympy/core/tests/test_power.py
@@ -471,6 +471,9 @@ def test_issue_2993():
     eq = (2.3*x + 4)
     assert eq**2 == 16.0*(0.575*x + 1)**2
     assert (1/eq).args == (eq, -1)  # don't change trivial power
+    # issue 17735
+    q=.5*exp(x) - .5*exp(-x) + 0.1
+    assert int((q**2).subs(x, 1)) == 1
 
 
 def test_issue_17450():