Repository: sympy/sympy
Pull request: degree(multivariate): multivariate expression now requires generator to specified
multivariate expressions could be passed into the degree expression, in
which case the degree was returned for the first generator. As of this pull
request, when giving the degree function multivariate expressions, a
generator is mandatory to be specified, else a TypeError is raised.

Example:
```
>>> degree(x+x**2)
2
>>>degree(x**2+y**3,y)
3
>>> degree(x**2+y)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "sympy/polys/polytools.py", line 4414, in degree
    raise TypeError("generator needs to be specified for degree() for multivariate polynomials")
TypeError: generator needs to be specified for degree() for multivariate polynomials
```

<!-- Please give this pull request a descriptive title. Pull requests with descriptive titles are more likely to receive reviews. Describe what you changed! A title that only references an issue number is not descriptive. -->

<!-- If this pull request fixes an issue please indicate which issue by typing "Fixes #NNNN" below. -->
Fixes #13129 


Problem statement:
degree(multivariate) -> degree of first generator
When giving the degree function a multivariate expression, the default degree returned is for the first generator which is chosen canonically but arbitrarily. When the degree method of Poly is called, this is not so bad because one would/should know what the generators are.  For the case of using the function, however, I believe it allows ambiguity to pass silently by not requiring the generator to be specified.

```
degree(x + x**2) -> 2 this is ok, there is no ambiguity
```
Here, the return value depends on the symbols chosen

```
degree(a + b**2) -> 1
degree(b + a**2) -> 2
```

Hints from issue discussion:

Commit message: degree(multivariate): Modified if clause and edited error message

The if clause added previously is changed to an elif clause. The error
message is modified so as to give an example specific to the user's passed
multivariate expression, instead of a generic expression. White spaces are
added to test_util.py where needed.

Patch to review:
diff --git a/sympy/polys/polytools.py b/sympy/polys/polytools.py
index ff6fedc17b71..364f933d7623 100644
--- a/sympy/polys/polytools.py
+++ b/sympy/polys/polytools.py
@@ -45,7 +45,7 @@
     GeneratorsError,
 )
 
-from sympy.utilities import group, sift, public
+from sympy.utilities import group, sift, public, filldedent
 
 import sympy.polys
 import mpmath
@@ -56,8 +56,7 @@
 
 from sympy.polys import polyoptions as options
 
-from sympy.core.compatibility import iterable, range
-
+from sympy.core.compatibility import iterable, range, ordered
 
 @public
 class Poly(Expr):
@@ -4427,6 +4426,13 @@ def degree(f, gen=0):
         if gen not in p.gens:
             return S.Zero
 
+    elif len(f.free_symbols) > 1 and  sympify(gen).is_Number and not f.is_Poly:
+        raise TypeError(filldedent('''
+         A symbolic generator of interest is required for a multivariate
+         expression like func = %s, e.g. degree(func, gen = %s) instead of
+         degree(func, gen = %s).
+        ''' % (f, next(ordered(f.free_symbols)), gen)))
+
     return Integer(p.degree(gen))
 
 
diff --git a/sympy/polys/tests/test_polytools.py b/sympy/polys/tests/test_polytools.py
index 0af574396244..f448bf201e65 100644
--- a/sympy/polys/tests/test_polytools.py
+++ b/sympy/polys/tests/test_polytools.py
@@ -1166,10 +1166,10 @@ def test_Poly_degree():
     assert degree(x*y**2, y) == 2
     assert degree(x*y**2, z) == 0
 
-    assert degree(y**2+x**3) == 3
-    assert degree(y**2+x**3, 1) == 2
     assert degree(pi) == 1
 
+    raises(TypeError, lambda: degree(y**2 + x**3))
+    raises(TypeError, lambda: degree(y**2 + x**3, 1))
     raises(PolynomialError, lambda: degree(x, 1.1))
 
     assert degree(Poly(0,x),z) == -oo


Code review comments:
diff_hunk: @@ -4427,6 +4426,13 @@ def degree(f, gen=0):
         if gen not in p.gens:
             return S.Zero
 
+    elif len(f.free_symbols) > 1 and  sympify(gen).is_Number and not f.is_Poly:
path: sympy/polys/polytools.py
text: @user1:
This `elif` should read `elif not f.is_Poly and len(f.free_symbols) > 1:` because we already know that `sympify(gen).is_Number` is true because of the `if` clause. Also, there is no need to traverse the expression if we know that f is a Poly (and that, I assume is faster than finding the free symbols) -- put cheaper tests first.

@author:
Thank you. Will take care of this next time.