Repository: sympy/sympy (Python)

Issue:
nsimplify should not be used in series calculations
The use of `nsimplify` here leads to incorrect results:
https://github.com/sympy/sympy/blob/e2cadc140cc969fea038240a39961a66a2f3dd6d/sympy/functions/elementary/exponential.py#L1047-L1048

This series should just be all zero terms:
```python
In [3]: g1 = 2*acosh(1+2*z/3)-acosh(S(5)/3-S(8)/3/(z+4))

In [4]: g3 = 2*log(sqrt((z+4)/3)*(sqrt(z+3)+sqrt(z))**2/(2*sqrt(z+3)+sqrt(z)))

In [5]: s = (g1 - g3).series(z)

In [6]: s
Out[6]: 
     ⎛                    11  31   61  47⎞         ⎛     372  19  443  193                   17        
     ⎜                    ──  ──  ───  ──⎟         ⎜     ───  ──  ───  ───                   ──        
     ⎜                    16  32  192  48⎟         ⎜     485  97  485  485             7/59  59  9/59  
 9/2 ⎜  115963⋅√3   1000⋅2  ⋅3  ⋅5   ⋅7  ⎟    11/2 ⎜  2⋅2   ⋅3  ⋅5   ⋅7      19131876⋅2    ⋅3  ⋅5    ⋅7
z   ⋅⎜- ───────── + ─────────────────────⎟ + z    ⋅⎜- ──────────────────── + ──────────────────────────
     ⎝    746496            194481       ⎠         ⎝           45                    820654296875      

53             ⎞        
──             ⎟        
59             ⎟        
     4194103⋅√3⎟    ⎛ 6⎞
── + ──────────⎟ + O⎝z ⎠
      8211456  ⎠    
```
Internally `nsimplify` is called with:
```python
In [1]: nsimplify(-403*sqrt(3)/2737152)
Out[1]: 
                 17        53 
                 ──        ── 
           7/59  59  9/59  59 
-19131876⋅2    ⋅3  ⋅5    ⋅7   
──────────────────────────────
         820654296875 
```
This output from `nsimplify` is not equal to the input which could be considered a bug in `nsimplify` but then again `nsimplify` is only supposed to be approximate. Really the problem is that the series code should not use nsimplify at all. This is presumably added to handle floats but that should be done elsewhere.

The call to `nsimplify` was added in gh-23798 (CC @anutosh491).


Discussion:

Patch:
diff --git a/sympy/functions/elementary/exponential.py b/sympy/functions/elementary/exponential.py
index 3f4f462150e6..9f7c9ccce8de 100644
--- a/sympy/functions/elementary/exponential.py
+++ b/sympy/functions/elementary/exponential.py
@@ -1002,7 +1002,7 @@ def coeff_exp(term, x):
             except ValueError:
                 a, b = s.removeO().as_leading_term(t, cdir=1), S.Zero
 
-        p = (z/(a*t**b) - 1)._eval_nseries(t, n=n, logx=logx, cdir=1)
+        p = (z/(a*t**b) - 1).cancel()._eval_nseries(t, n=n, logx=logx, cdir=1)
         if p.has(exp):
             p = logcombine(p)
         if isinstance(p, Order):
@@ -1047,14 +1047,13 @@ def mul(d1, d2):
         while k*d < n:
             coeff = -S.NegativeOne**k/k
             for ex in pk:
-                _ = terms.get(ex, S.Zero) + coeff*pk[ex]
-                terms[ex] = _.nsimplify()
+                terms[ex] = terms.get(ex, S.Zero) + coeff*pk[ex]
             pk = mul(pk, pterms)
             k += S.One
 
         res = log(a) - b*log(cdir) + b*logx
         for ex in terms:
-            res += terms[ex]*t**(ex)
+            res += terms[ex].cancel()*t**(ex)
 
         if a.is_negative and im(z) != 0:
             from sympy.functions.special.delta_functions import Heaviside


Test patch:
diff --git a/sympy/functions/elementary/tests/test_exponential.py b/sympy/functions/elementary/tests/test_exponential.py
index 686ae3b363f1..25d7f4e3431d 100644
--- a/sympy/functions/elementary/tests/test_exponential.py
+++ b/sympy/functions/elementary/tests/test_exponential.py
@@ -524,7 +524,7 @@ def test_log_nseries():
     assert log(-2*x + (3 - I)*x**2)._eval_nseries(x, 3, None, -1) == -I*pi + log(2) + log(x) - \
     x*(S(3)/2 - I/2) + x**2*(-1 + 3*I/4) + O(x**3)
     assert log(sqrt(-I*x**2 - 3)*sqrt(-I*x**2 - 1) - 2)._eval_nseries(x, 3, None, 1) == -I*pi + \
-    log(sqrt(3) + 2) + I*x**2*(-2 + 4*sqrt(3)/3) + O(x**3)
+    log(sqrt(3) + 2) + 2*sqrt(3)*I*x**2/(3*sqrt(3) + 6) + O(x**3)
     assert log(-1/(1 - x))._eval_nseries(x, 3, None, 1) == I*pi + x + x**2/2 + O(x**3)
     assert log(-1/(1 - x))._eval_nseries(x, 3, None, -1) == I*pi + x + x**2/2 + O(x**3)
 
diff --git a/sympy/functions/elementary/tests/test_hyperbolic.py b/sympy/functions/elementary/tests/test_hyperbolic.py
index 5b4365d8217d..1ad9f1d51598 100644
--- a/sympy/functions/elementary/tests/test_hyperbolic.py
+++ b/sympy/functions/elementary/tests/test_hyperbolic.py
@@ -603,12 +603,12 @@ def test_asinh_series():
 def test_asinh_nseries():
     x = Symbol('x')
     # Tests concerning branch points
-    assert asinh(x + I)._eval_nseries(x, 4, None) == I*pi/2 + \
-    sqrt(x)*(1 - I) + x**(S(3)/2)*(S(1)/12 + I/12) + x**(S(5)/2)*(-S(3)/160 + 3*I/160) + \
-    x**(S(7)/2)*(-S(5)/896 - 5*I/896) + O(x**4)
+    assert asinh(x + I)._eval_nseries(x, 4, None) == I*pi/2 - \
+    sqrt(2)*sqrt(I)*I*sqrt(x) + sqrt(2)*sqrt(I)*x**(S(3)/2)/12 + 3*sqrt(2)*sqrt(I)*I*x**(S(5)/2)/160 - \
+    5*sqrt(2)*sqrt(I)*x**(S(7)/2)/896 + O(x**4)
     assert asinh(x - I)._eval_nseries(x, 4, None) == -I*pi/2 + \
-    sqrt(x)*(1 + I) + x**(S(3)/2)*(S(1)/12 - I/12) + x**(S(5)/2)*(-S(3)/160 - 3*I/160) + \
-    x**(S(7)/2)*(-S(5)/896 + 5*I/896) + O(x**4)
+    sqrt(2)*I*sqrt(x)*sqrt(-I) + sqrt(2)*x**(S(3)/2)*sqrt(-I)/12 - \
+    3*sqrt(2)*I*x**(S(5)/2)*sqrt(-I)/160 - 5*sqrt(2)*x**(S(7)/2)*sqrt(-I)/896 + O(x**4)
     # Tests concerning points lying on branch cuts
     assert asinh(x + 2*I)._eval_nseries(x, 4, None, cdir=1) == I*asin(2) - \
     sqrt(3)*I*x/3 + sqrt(3)*x**2/9 + sqrt(3)*I*x**3/18 + O(x**4)
@@ -619,8 +619,9 @@ def test_asinh_nseries():
     assert asinh(x - 2*I)._eval_nseries(x, 4, None, cdir=-1) == -I*asin(2) - \
     sqrt(3)*I*x/3 - sqrt(3)*x**2/9 + sqrt(3)*I*x**3/18 + O(x**4)
     # Tests concerning re(ndir) == 0
-    assert asinh(2*I + I*x - x**2)._eval_nseries(x, 4, None) == I*pi/2 + log(2 - sqrt(3)) - \
-    sqrt(3)*x/3 + x**2*(sqrt(3)/9 - sqrt(3)*I/3) + x**3*(-sqrt(3)/18 + 2*sqrt(3)*I/9) + O(x**4)
+    assert asinh(2*I + I*x - x**2)._eval_nseries(x, 4, None) == I*pi/2 + log(2 - sqrt(3)) + \
+    x*(-3 + 2*sqrt(3))/(-6 + 3*sqrt(3)) + x**2*(12 - 36*I + sqrt(3)*(-7 + 21*I))/(-63 + \
+    36*sqrt(3)) + x**3*(-168 + sqrt(3)*(97 - 388*I) + 672*I)/(-1746 + 1008*sqrt(3)) + O(x**4)
 
 
 def test_asinh_fdiff():
@@ -757,8 +758,9 @@ def test_acosh_nseries():
     assert acosh(1/(I*x - 3))._eval_nseries(x, 4, None, cdir=-1) == acosh(-S(1)/3) - \
     sqrt(2)*x/12 - 17*sqrt(2)*I*x**2/576 + 443*sqrt(2)*x**3/41472 + O(x**4)
     # Tests concerning im(ndir) == 0
-    assert acosh(-I*x**2 + x - 2)._eval_nseries(x, 4, None) == -I*pi + log(sqrt(3) + 2) - \
-    sqrt(3)*x/3 + x**2*(-sqrt(3)/9 + sqrt(3)*I/3) + x**3*(-sqrt(3)/18 + 2*sqrt(3)*I/9) + O(x**4)
+    assert acosh(-I*x**2 + x - 2)._eval_nseries(x, 4, None) == -I*pi + log(sqrt(3) + 2) + \
+    x*(-2*sqrt(3) - 3)/(3*sqrt(3) + 6) + x**2*(-12 + 36*I + sqrt(3)*(-7 + 21*I))/(36*sqrt(3) + \
+    63) + x**3*(-168 + 672*I + sqrt(3)*(-97 + 388*I))/(1008*sqrt(3) + 1746) + O(x**4)
 
 
 def test_acosh_fdiff():
@@ -880,8 +882,9 @@ def test_asech_nseries():
     assert asech(-I*x - 3)._eval_nseries(x, 4, None) == asech(-3) - sqrt(2)*x/12 + \
     17*sqrt(2)*I*x**2/576 + 443*sqrt(2)*x**3/41472 + O(x**4)
     # Tests concerning im(ndir) == 0
-    assert asech(-I*x**2 + x - 2)._eval_nseries(x, 3, None) == 2*I*pi/3 + sqrt(3)*I*x/6 + \
-    x**2*(sqrt(3)/6 + 7*sqrt(3)*I/72) + O(x**3)
+    assert asech(-I*x**2 + x - 2)._eval_nseries(x, 3, None) == 2*I*pi/3 + \
+    x*(-sqrt(3) + 3*I)/(6*sqrt(3) + 6*I) + x**2*(36 + sqrt(3)*(7 - 12*I) + 21*I)/(72*sqrt(3) - \
+    72*I) + O(x**3)
 
 
 def test_asech_rewrite():
@@ -1002,12 +1005,12 @@ def test_acsch_series():
 def test_acsch_nseries():
     x = Symbol('x')
     # Tests concerning branch points
-    assert acsch(x + I)._eval_nseries(x, 4, None) == -I*pi/2 + I*sqrt(x) + \
-    sqrt(x) + 5*I*x**(S(3)/2)/12 - 5*x**(S(3)/2)/12 - 43*I*x**(S(5)/2)/160 - \
-    43*x**(S(5)/2)/160 - 177*I*x**(S(7)/2)/896 + 177*x**(S(7)/2)/896 + O(x**4)
-    assert acsch(x - I)._eval_nseries(x, 4, None) == I*pi/2 - I*sqrt(x) + \
-    sqrt(x) - 5*I*x**(S(3)/2)/12 - 5*x**(S(3)/2)/12 + 43*I*x**(S(5)/2)/160 - \
-    43*x**(S(5)/2)/160 + 177*I*x**(S(7)/2)/896 + 177*x**(S(7)/2)/896 + O(x**4)
+    assert acsch(x + I)._eval_nseries(x, 4, None) == -I*pi/2 + \
+    sqrt(2)*I*sqrt(x)*sqrt(-I) - 5*x**(S(3)/2)*(1 - I)/12 - \
+    43*sqrt(2)*I*x**(S(5)/2)*sqrt(-I)/160 + 177*x**(S(7)/2)*(1 - I)/896 + O(x**4)
+    assert acsch(x - I)._eval_nseries(x, 4, None) == I*pi/2 - \
+    sqrt(2)*sqrt(I)*I*sqrt(x) - 5*x**(S(3)/2)*(1 + I)/12 + \
+    43*sqrt(2)*sqrt(I)*I*x**(S(5)/2)/160 + 177*x**(S(7)/2)*(1 + I)/896 + O(x**4)
     # Tests concerning points lying on branch cuts
     assert acsch(x + I/2)._eval_nseries(x, 4, None, cdir=1) == -acsch(I/2) - \
     I*pi + 4*sqrt(3)*I*x/3 - 8*sqrt(3)*x**2/9 - 16*sqrt(3)*I*x**3/9 + O(x**4)
@@ -1017,10 +1020,11 @@ def test_acsch_nseries():
     4*sqrt(3)*I*x/3 - 8*sqrt(3)*x**2/9 + 16*sqrt(3)*I*x**3/9 + O(x**4)
     assert acsch(x - I/2)._eval_nseries(x, 4, None, cdir=-1) == I*pi + \
     acsch(I/2) + 4*sqrt(3)*I*x/3 + 8*sqrt(3)*x**2/9 - 16*sqrt(3)*I*x**3/9 + O(x**4)
-    # TODO: Tests concerning re(ndir) == 0
+    # Tests concerning re(ndir) == 0
     assert acsch(I/2 + I*x - x**2)._eval_nseries(x, 4, None) == -I*pi/2 + \
-    log(2 - sqrt(3)) + 4*sqrt(3)*x/3 + x**2*(-8*sqrt(3)/9 + 4*sqrt(3)*I/3) + \
-    x**3*(16*sqrt(3)/9 - 16*sqrt(3)*I/9) + O(x**4)
+    log(2 - sqrt(3)) + x*(12 - 8*sqrt(3))/(-6 + 3*sqrt(3)) + x**2*(-96 + \
+    sqrt(3)*(56 - 84*I) + 144*I)/(-63 + 36*sqrt(3)) + x**3*(2688 - 2688*I + \
+    sqrt(3)*(-1552 + 1552*I))/(-873 + 504*sqrt(3)) + O(x**4)
 
 
 def test_acsch_rewrite():
@@ -1540,3 +1544,10 @@ def test_issue_25847():
     #acsch
     assert acsch(sin(x)/x).as_leading_term(x) == log(1 + sqrt(2))
     raises(PoleError, lambda: acsch(exp(1/x)).as_leading_term(x))
+
+
+def test_issue_25175():
+    x = Symbol('x')
+    g1 = 2*acosh(1 + 2*x/3) - acosh(S(5)/3 - S(8)/3/(x + 4))
+    g2 = 2*log(sqrt((x + 4)/3)*(sqrt(x + 3)+sqrt(x))**2/(2*sqrt(x + 3) + sqrt(x)))
+    assert (g1 - g2).series(x) == O(x**6)