Repository: sympy/sympy

Issue:
Dead code in sympy.polys.polyroots.roots() function
While messing around with `roots()` method I saw things...

https://github.com/sympy/sympy/blob/27613158382a668605a21616706a8d64a3573997/sympy/polys/polyroots.py#L891

Here `f` is a `Poly` instance, and `Poly.length()` is a function.
So this expression is always `False` and followed block of code is dead.
Not sure if this can affect `roots()` correct behavior.


Discussion:
sympy-bot: :white_check_mark:

Hi, I am the [SymPy bot](https://github.com/sympy/sympy-bot) (v162). I'm here to help you write a release notes entry. Please read the [guide on how to write release notes](https://github.com/sympy/sympy/wiki/Writing-Release-Notes).



Your release notes are in good order.

Here is what the release notes will look like:
* solvers
  * solve will give more factored results automatically when solving binomial expressions with similar powers in the constant term -- like when solving `x**3 - (1 - y)**6` for `x`. ([#22781](https://github.com/sympy/sympy/pull/22781) by [@smichr](https://github.com/smichr))

This will be added to https://github.com/sympy/sympy/wiki/Release-Notes-for-1.10.

<details><summary>Click here to see the pull request description that was parsed.</summary>

    Since Poly expands expressions, an equation like that added
    in the test will not benefit from correcting the dead code
    in polyroots. And when the bases are all Symbols the current
    preprocess_roots already takes care of the case that the
    dead code was supposed to handle. The place where this can be
    used in in _solve so it was moved there to intercept expressions
    that are in the form `x**n + other*foo**n` and return much
    simpler results (as shown).

    <!-- 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 #22768
    closes #22770 

    #### Brief description of what is fixed or changed


    #### 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 -->
    * solvers
        * solve will give more factored results automatically when solving binomial expressions with similar powers in the constant term -- like when solving `x**3 - (1 - y)**6` for `x`.
    <!-- END RELEASE NOTES -->


</details><p>

github-actions[bot]: Benchmark results from GitHub Actions

Lower numbers are good, higher numbers are bad. A ratio less than 1
means a speed up and greater than 1 means a slowdown. Green lines
beginning with `+` are slowdowns (the PR is slower then master or
master is slower than the previous release). Red lines beginning
with `-` are speedups.

Significantly changed benchmark results (PR vs master)
```diff

```
Significantly changed benchmark results (master vs previous release)
```diff
       before           after         ratio
     [907895ac]       [306c0f2c]
-         251±2ms        156±0.7ms     0.62  large_exprs.TimeLargeExpressionOperations.time_subs
-         258±3μs          123±3μs     0.48  matrices.TimeMatrixExpression.time_MatMul
-      17.1±0.4ms       8.93±0.2ms     0.52  matrices.TimeMatrixExpression.time_MatMul_doit
-       4.88±0.1s          354±2ms     0.07  polygon.PolygonArbitraryPoint.time_bench01
+     3.90±0.01ms       6.91±0.2ms     1.77  solve.TimeMatrixOperations.time_det(4, 2)
+     3.94±0.03ms       6.96±0.1ms     1.77  solve.TimeMatrixOperations.time_det_bareiss(4, 2)
+      44.4±0.9ms         80.7±4ms     1.82  solve.TimeMatrixSolvePyDySlow.time_linsolve(1)
+      44.5±0.5ms       77.6±0.4ms     1.74  solve.TimeMatrixSolvePyDySlow.time_solve(1)

```
Full benchmark results can be found as artifacts in GitHub Actions
(click on checks at the top of the PR).

Patch:
diff --git a/sympy/polys/polyroots.py b/sympy/polys/polyroots.py
index 40973d4606..429fbec635 100644
--- a/sympy/polys/polyroots.py
+++ b/sympy/polys/polyroots.py
@@ -888,35 +888,41 @@ def roots(f, *gens,
             F = Poly(f, *gens, **flags)
             if not isinstance(f, Poly) and not F.gen.is_Symbol:
                 raise PolynomialError("generator must be a Symbol")
-            else:
-                f = F
-            if f.length == 2 and f.degree() != 1:
-                # check for foo**n factors in the constant
-                n = f.degree()
-                npow_bases = []
-                others = []
-                expr = f.as_expr()
-                con = expr.as_independent(*gens)[0]
-                for p in Mul.make_args(con):
-                    if p.is_Pow and not p.exp % n:
-                        npow_bases.append(p.base**(p.exp/n))
-                    else:
-                        others.append(p)
-                    if npow_bases:
-                        b = Mul(*npow_bases)
-                        B = Dummy()
-                        d = roots(Poly(expr - con + B**n*Mul(*others), *gens,
-                            **flags), *gens, **flags)
-                        rv = {}
-                        for k, v in d.items():
-                            rv[k.subs(B, b)] = v
-                        return rv
-
+            f = F
         except GeneratorsNeeded:
             if multiple:
                 return []
             else:
                 return {}
+        else:
+            n = f.degree()
+            if f.length() == 2 and n > 2:
+                # check for foo**n in constant if dep is c*gen**m
+                con, dep = f.as_expr().as_independent(*f.gens)
+                fcon = -(-con).factor()
+                if fcon != con:
+                    con = fcon
+                    bases = []
+                    for i in Mul.make_args(con):
+                        if i.is_Pow:
+                            b, e = i.as_base_exp()
+                            if e.is_Integer and b.is_Add:
+                                bases.append((b, Dummy(positive=True)))
+                    if bases:
+                        rv = roots(Poly((dep + con).xreplace(dict(bases)),
+                            *f.gens), *F.gens,
+                            auto=auto,
+                            cubics=cubics,
+                            trig=trig,
+                            quartics=quartics,
+                            quintics=quintics,
+                            multiple=multiple,
+                            filter=filter,
+                            predicate=predicate,
+                            **flags)
+                        return {factor_terms(k.xreplace(
+                            {v: k for k, v in bases})
+                        ): v for k, v in rv.items()}
 
         if f.is_multivariate:
             raise PolynomialError('multivariate polynomials are not supported')


Test patch:
diff --git a/sympy/polys/tests/test_polyroots.py b/sympy/polys/tests/test_polyroots.py
index c54646d647..53d3042d3c 100644
--- a/sympy/polys/tests/test_polyroots.py
+++ b/sympy/polys/tests/test_polyroots.py
@@ -728,3 +728,12 @@ def test_issue_17454():
 def test_issue_20913():
     assert Poly(x + 9671406556917067856609794, x).real_roots() == [-9671406556917067856609794]
     assert Poly(x**3 + 4, x).real_roots() == [-2**(S(2)/3)]
+
+
+def test_issue_22768():
+    e = Rational(1, 3)
+    r = (-1/a)**e*(a + 1)**(5*e)
+    assert roots(Poly(a*x**3 + (a+1)**5, x)) == {
+        r: 1,
+        -r*(1 + sqrt(3)*I)/2: 1,
+        r*(-1 + sqrt(3)*I)/2: 1}
diff --git a/sympy/solvers/tests/test_solvers.py b/sympy/solvers/tests/test_solvers.py
index dee49278ff..6be10249b6 100644
--- a/sympy/solvers/tests/test_solvers.py
+++ b/sympy/solvers/tests/test_solvers.py
@@ -2477,6 +2477,13 @@ def test_solver_flags():
     assert root != rad
 
 
+def test_issue_22768():
+    eq = 2*x**3 - 16*(y - 1)**6*z**3
+    assert solve(eq.expand(), x, simplify=False
+        ) == [2*z*(y - 1)**2, z*(-1 + sqrt(3)*I)*(y - 1)**2,
+        -z*(1 + sqrt(3)*I)*(y - 1)**2]
+
+
 def test_issue_22717():
     assert solve((-y**2 + log(y**2/x) + 2, -2*x*y + 2*x/y)) == [
         {y: -1, x: E}, {y: 1, x: E}]