Repository: sympy/sympy

Issue:
minimal_polynomial using Groebner basis can give wrong result
Consider this real algebraic number and its minimal polynomial
```python
>>> a = 1/(sqrt(1+sqrt(2)) - sqrt(2)*sqrt(1+sqrt(2))) + 1
>>> p_min = minimal_polynomial(a); p_min
_x**4 - 4*_x**3 + 4*_x**2 - 2
```
When computing the minimal polynomial not using resultants but groebner bases, we get a different result:
```python
>>> p_min2 = minimal_polynomial(a, compose=False); p_min2
_x**4 - 4*_x**3 + 52*_x**2 - 96*_x - 2
```
Looking at the numerical approximations we see that the second minimal polynomial is wrong:
```python
>>> N(a)
-0.553773974030037
>>> list(map(N, real_roots(p_min)))
[-0.553773974030037, 2.55377397403004]
>>> list(map(N, real_roots(p_min2)))
[-0.0206030375922932, 2.02060303759229]
```
The bug seems to be in `numberfields.py` in the local function `bottom_up_scan` of function `_minpoly_groebner` in the handling of case `elif ex.is_Pow:`.

Discussion:

Patch:
diff --git a/sympy/polys/numberfields.py b/sympy/polys/numberfields.py
index d10f04eb1e..f2509e03bb 100644
--- a/sympy/polys/numberfields.py
+++ b/sympy/polys/numberfields.py
@@ -729,20 +729,15 @@ def bottom_up_scan(ex):
             return Mul(*[ bottom_up_scan(g) for g in ex.args ])
         elif ex.is_Pow:
             if ex.exp.is_Rational:
-                if ex.exp < 0 and ex.base.is_Add:
-                    coeff, terms = ex.base.as_coeff_add()
-                    elt, _ = primitive_element(terms, polys=True)
-
-                    alg = ex.base - coeff
-
-                    # XXX: turn this into eval()
-                    inverse = invert(elt.gen + coeff, elt).as_expr()
-                    base = inverse.subs(elt.gen, alg).expand()
+                if ex.exp < 0:
+                    minpoly_base = _minpoly_groebner(ex.base, x, cls)
+                    inverse = invert(x, minpoly_base).as_expr()
+                    base_inv = inverse.subs(x, ex.base).expand()
 
                     if ex.exp == -1:
-                        return bottom_up_scan(base)
+                        return bottom_up_scan(base_inv)
                     else:
-                        ex = base**(-ex.exp)
+                        ex = base_inv**(-ex.exp)
                 if not ex.exp.is_Integer:
                     base, exp = (
                         ex.base**ex.exp.p).expand(), Rational(1, ex.exp.q)


Test patch:
diff --git a/sympy/polys/tests/test_numberfields.py b/sympy/polys/tests/test_numberfields.py
index 151b2f1a83..29aa928e2a 100644
--- a/sympy/polys/tests/test_numberfields.py
+++ b/sympy/polys/tests/test_numberfields.py
@@ -782,3 +782,11 @@ def test_issue_13230():
     + 9*sqrt(5)/29 + sqrt(196*sqrt(35) + 1941)/29), -2*sqrt(7)/29 + 9*sqrt(5)/29
     + sqrt(196*sqrt(35) + 1941)/29), Point2D(-1 + (-sqrt(7) + sqrt(5))*(-sqrt(196*sqrt(35)
     + 1941)/29 - 2*sqrt(7)/29 + 9*sqrt(5)/29), -sqrt(196*sqrt(35) + 1941)/29 - 2*sqrt(7)/29 + 9*sqrt(5)/29)]
+
+def test_issue_19760():
+    e = 1/(sqrt(1 + sqrt(2)) - sqrt(2)*sqrt(1 + sqrt(2))) + 1
+    mp_expected = x**4 - 4*x**3 + 4*x**2 - 2
+
+    for comp in (True, False):
+        mp = Poly(minimal_polynomial(e, compose=comp))
+        assert mp(x) == mp_expected, "minimal_polynomial(e, compose=%s) = %s; %s expected" % (comp, mp(x), mp_expected)