Repository: sagemath/sage

Issue:
`PolynomialSequence.coefficient_matrix()` returns a matrix instead of a vector as second element
### Steps To Reproduce

```
sage: from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence
sage: R.<x,y> = QQ[]
sage: PolynomialSequence([x+2*y+3, 4*x, 5]).coefficient_matrix()
(
[1 2 3]  [x]
[4 0 0]  [y]
[0 0 5], [1]
)
sage: _[1].parent()
Full MatrixSpace of 3 by 1 dense matrices over Multivariate Polynomial Ring in x, y over Rational Field
```

### Expected Behavior

The second element should be a vector - it is the vector of monomials appearing in the sequence.

### Actual Behavior

It returns a 1-column matrix.

### Additional Information

The problem is that iterating over a vector gives the elements, whereas iterating over a 1-column matrix gives vectors.

### Environment

```markdown
irrelevant.
```


### Checklist

- [X] I have searched the existing issues for a bug report that matches the one I want to file, without success.
- [X] I have read the documentation and troubleshoot guide


Discussion:
I am not sure how to deprecate this properly.  The current behaviour is likely worked around in a lot of user code, so deprecation is certainly necessary.
I think we should not change this because then operations like below will not work 

```python
sage: sage: from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence
....: sage: R.<x,y> = QQ[]
....: sage: B = PolynomialSequence([x+2*y+3, 4*x, 5]).coefficient_matrix()
sage: B[0]*B[1]
[x + 2*y + 3]
[        4*x]
[          5]
```

If we return a vector it will not give us the same answer as above.
Of course not - it will return a vector, which makes more sense I think, and also follows the documentation:
```
sage: from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence
sage: R.<x,y> = QQ[]
sage: m, v = PolynomialSequence([x+2*y+3, 4*x, 5]).coefficient_matrix()
sage: m
[1 2 3]
[4 0 0]
[0 0 5]
sage: v
[x]
[y]
[1]
sage: vector(v)
(x, y, 1)
sage: m*vector(v)
(x + 2*y + 3, 4*x, 5)
```
Ya, you are right. This makes more sense !

Resolving patch:
diff --git a/src/sage/groups/matrix_gps/finitely_generated_gap.py b/src/sage/groups/matrix_gps/finitely_generated_gap.py
--- a/src/sage/groups/matrix_gps/finitely_generated_gap.py
+++ b/src/sage/groups/matrix_gps/finitely_generated_gap.py
@@ -936,11 +936,11 @@ def _new_invariant_is_linearly_independent(F, invariants):
         sage: gens = [matrix(QQ, [[-1,1],[-1,0]]), matrix(QQ, [[0,1],[1,0]])]
         sage: G = MatrixGroup(gens)
         sage: s = Sequence(G.invariants_of_degree(14))                                  # needs sage.rings.number_field
-        sage: s.coefficient_matrix()[0].rank()                                          # needs sage.rings.number_field
+        sage: s.coefficients_monomials()[0].rank()                                      # needs sage.rings.number_field
         3
         sage: len(s)                                                                    # needs sage.rings.number_field
         3
     """
     if len(invariants) == 0:
         return True
-    return PolynomialSequence(invariants).coefficient_matrix()[0].rank() != PolynomialSequence(list(invariants)+[F]).coefficient_matrix()[0].rank()
+    return PolynomialSequence(invariants).coefficients_monomials()[0].rank() != PolynomialSequence(list(invariants)+[F]).coefficients_monomials()[0].rank()
diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py
--- a/src/sage/rings/polynomial/multi_polynomial_ideal.py
+++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py
@@ -5333,7 +5333,7 @@ def random_element(self, degree, compute_gb=False, *args, **kwds):
 
         2. We linearize and compute the echelon form::
 
-               sage: A, v = F.coefficient_matrix()
+               sage: A, v = F.coefficients_monomials()
                sage: A.echelonize()
 
         3. The result is the desired Gröbner basis::
diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py
--- a/src/sage/rings/polynomial/multi_polynomial_sequence.py
+++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py
@@ -114,7 +114,7 @@
 
 and compute the coefficient matrix::
 
-    sage: A,v = Sequence(r2).coefficient_matrix()                                       # needs sage.rings.polynomial.pbori
+    sage: A,v = Sequence(r2).coefficients_monomials()                                   # needs sage.rings.polynomial.pbori
     sage: A.rank()                                                                      # needs sage.rings.polynomial.pbori
     32
 
@@ -134,14 +134,14 @@
     sage: len(monomials)
     190
     sage: F2 = Sequence(map(mul, cartesian_product_iterator((monomials, F))))
-    sage: A, v = F2.coefficient_matrix(sparse=False)
+    sage: A, v = F2.coefficients_monomials(sparse=False)
     sage: A.echelonize()
     sage: A
     6840 x 4474 dense matrix over Finite Field of size 2...
     sage: A.rank()
     4056
     sage: A[4055] * v
-    (k001*k003)
+    k001*k003
 
 TESTS::
 
@@ -707,6 +707,67 @@ def algebraic_dependence(self):
         RRR = PolynomialRing(K,r,tuple(Ts),order='negdeglex')
         return RRR.ideal(JJ.gens())
 
+    def coefficients_monomials(self, order=None, sparse=True):
+        """
+        Return the matrix of coefficients ``A`` and
+        the matching vector of monomials ``v``, such that ``A*v == vector(self)``.
+
+        Thus value of ``A[i,j]`` corresponds the coefficient of the
+        monomial ``v[j]`` in the ``i``-th polynomial in this system.
+
+        Monomials are ordered w.r.t. the term ordering of ``order``
+        if given; otherwise, they are ordered w.r.t. ``self.ring()``
+        in reverse order, i.e., such that the smallest entry comes last.
+
+        INPUT:
+
+        - ``sparse`` - construct a sparse matrix (default: ``True``)
+        - ``order`` - a list or tuple specifying the order of monomials (default: ``None``)
+
+        EXAMPLES::
+
+            sage: # needs sage.libs.singular
+            sage: P.<a,b,c,d> = PolynomialRing(GF(127), 4)
+            sage: I = sage.rings.ideal.Katsura(P)
+            sage: I.gens()
+            [a + 2*b + 2*c + 2*d - 1,
+             a^2 + 2*b^2 + 2*c^2 + 2*d^2 - a,
+             2*a*b + 2*b*c + 2*c*d - b,
+             b^2 + 2*a*c + 2*b*d - c]
+            sage: F = Sequence(I)
+            sage: A,v = F.coefficients_monomials()
+            sage: A
+            [  0   0   0   0   0   0   0   0   0   1   2   2   2 126]
+            [  1   0   2   0   0   2   0   0   2 126   0   0   0   0]
+            [  0   2   0   0   2   0   0   2   0   0 126   0   0   0]
+            [  0   0   1   2   0   0   2   0   0   0   0 126   0   0]
+            sage: v
+            (a^2, a*b, b^2, a*c, b*c, c^2, b*d, c*d, d^2, a, b, c, d, 1)
+            sage: A*v
+            (a + 2*b + 2*c + 2*d - 1, a^2 + 2*b^2 + 2*c^2 + 2*d^2 - a,
+             2*a*b + 2*b*c + 2*c*d - b, b^2 + 2*a*c + 2*b*d - c)
+        """
+        from sage.modules.free_module_element import vector
+        from sage.matrix.constructor import Matrix
+
+        if order is None:
+            v = sorted(self.monomials(), reverse=True)
+        else:
+            if isinstance(order, (list, tuple)):
+                v = order
+            else:
+                raise ValueError("order argument can only accept list or tuple")
+
+        y = dict(zip(v, range(len(v))))  # construct dictionary for fast lookups
+        A = Matrix(self.ring().base_ring(), len(self), len(v), sparse=sparse)
+        for x, poly in enumerate(self):
+            for c, m in poly:
+                try:
+                    A[x, y[m]] = c
+                except KeyError:
+                    raise ValueError("order argument does not contain all monomials")
+        return A, vector(v)
+
     def coefficient_matrix(self, sparse=True):
         """
         Return tuple ``(A,v)`` where ``A`` is the coefficient matrix
@@ -735,6 +796,9 @@ def coefficient_matrix(self, sparse=True):
              b^2 + 2*a*c + 2*b*d - c]
             sage: F = Sequence(I)
             sage: A,v = F.coefficient_matrix()
+            doctest:warning...
+            DeprecationWarning: the function coefficient_matrix is deprecated; use coefficients_monomials instead
+            See https://github.com/sagemath/sage/issues/37035 for details.
             sage: A
             [  0   0   0   0   0   0   0   0   0   1   2   2   2 126]
             [  1   0   2   0   0   2   0   0   2 126   0   0   0   0]
@@ -761,26 +825,13 @@ def coefficient_matrix(self, sparse=True):
             [      2*a*b + 2*b*c + 2*c*d - b]
             [        b^2 + 2*a*c + 2*b*d - c]
         """
-        R = self.ring()
-
-        m = sorted(self.monomials(),reverse=True)
-        nm = len(m)
-        f = tuple(self)
-        nf = len(f)
-
-        #construct dictionary for fast lookups
-        v = dict( zip( m , range(len(m)) ) )
-
         from sage.matrix.constructor import Matrix
+        from sage.misc.superseded import deprecation
+        deprecation(37035, "the function coefficient_matrix is deprecated; use coefficients_monomials instead")
 
-        A = Matrix( R.base_ring(), nf, nm, sparse=sparse )
-
-        for x in range( nf ):
-            poly = f[x]
-            for y in poly.monomials():
-                A[ x , v[y] ] = poly.monomial_coefficient(y)
-
-        return A, Matrix(R,nm,1,m)
+        R = self.ring()
+        A, v = self.coefficients_monomials(sparse=sparse)
+        return A, Matrix(R,len(v),1,v)
 
     def subs(self, *args, **kwargs):
         """
@@ -1643,6 +1694,60 @@ def reduced(self):
         else:
             return PolynomialSequence_generic.reduced(self)
 
+    def coefficients_monomials(self, order=None, sparse=True):
+        """
+        Return the matrix of coefficients ``A`` and
+        the matching vector of monomials ``v``, such that ``A*v == vector(self)``.
+
+        Thus value of ``A[i,j]`` corresponds the coefficient of the
+        monomial ``v[j]`` in the ``i``-th polynomial in this system.
+
+        Monomials are ordered w.r.t. the term ordering of ``order``
+        if given; otherwise, they are ordered w.r.t. ``self.ring()``
+        in reverse order, i.e., such that the smallest entry comes last.
+
+        INPUT:
+
+        - ``sparse`` - construct a sparse matrix (default: ``True``)
+        - ``order`` - a list or tuple specifying the order of monomials (default: ``None``)
+
+        EXAMPLES::
+
+            sage: # needs sage.rings.polynomial.pbori
+            sage: B.<x,y,z> = BooleanPolynomialRing()
+            sage: F = Sequence([x*y + y + 1, z + 1])
+            sage: A, v = F.coefficients_monomials()
+            sage: A
+            [1 1 0 1]
+            [0 0 1 1]
+            sage: v
+            (x*y, y, z, 1)
+            sage: A*v
+            (x*y + y + 1, z + 1)
+        """
+        from sage.modules.free_module_element import vector
+        from sage.matrix.constructor import Matrix
+
+        if order is None:
+            v = sorted(self.monomials(), reverse=True)
+        else:
+            if isinstance(order, (list, tuple)):
+                v = order
+            else:
+                raise ValueError("order argument can only accept list or tuple")
+
+        R = self.ring().base_ring()
+        one = R.one()
+        y = dict(zip(v, range(len(v))))  # construct dictionary for fast lookups
+        A = Matrix(R, len(self), len(v), sparse=sparse)
+        for x, poly in enumerate(self):
+            for m in poly:
+                try:
+                    A[x, y[m]] = one
+                except KeyError:
+                    raise ValueError("order argument does not contain all monomials")
+        return A, vector(v)
+
 
 class PolynomialSequence_gf2e(PolynomialSequence_generic):
     r"""


Test patch: