Repository: sagemath/sage

Issue:
FinitelyGeneratedMatrixGroup_gap.invariants_of_degree should return a basis of the space of invariants
### Is there an existing issue for this?

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

### Did you read the documentation and troubleshoot guide?

- [X] I have read the documentation and troubleshoot guide

### Environment

```markdown
- **OS**: Ubuntu 20.04
- **Sage Version**: 10.0
```


### Steps To Reproduce

A colleague of mine discovered a bug in FinitelyGeneratedMatrixGroup_gap.invariants_of_degree, and I am not completely sure how to fix it.  Here is the bug:
```
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))
sage: s.coefficient_matrix()[0].rank()
2
sage: len(s)
3
```

### Expected Behavior

The invariants returned should be linearly independent, i.e., the rank of the matrix above should be 3.

### Actual Behavior

The returned invariants are distinct, but not linearly independent.

### Additional Information

Dear all, especially Ben!

A colleague of mine discovered a bug in FinitelyGeneratedMatrixGroup_gap.invariants_of_degree, and I am not completely sure how to fix it.  Here is the bug:

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))
sage: s.coefficient_matrix()[0].rank()
2
Here is a fix, which I am not convinced of.  It does a brute force check, adding invariants only if the rank increases.

This is certainly very inefficient, but also possibly wrong, because it relies on `Sequence` finding the correct universe.

```#diff
diff --git a/src/sage/groups/matrix_gps/finitely_generated.py b/src/sage/groups/matrix_gps/finitely_generated.py
index 0b5f14014e..130d0bf4f1 100644
--- a/src/sage/groups/matrix_gps/finitely_generated.py
+++ b/src/sage/groups/matrix_gps/finitely_generated.py
@@ -1328,12 +1328,15 @@ class FinitelyGeneratedMatrixGroup_gap(MatrixGroup_gap):
        ms = self.molien_series(prec=deg+1,chi=chi)
        if ms[deg].is_zero():
            return []
-        inv = set()
+        inv = []
        for e in IntegerVectors(deg, D):
            F = self.reynolds_operator(R.monomial(*e), chi=chi)
            if not F.is_zero():
                F = F / F.lc()
-                inv.add(F)
-                if len(inv) == ms[deg]:
+                inv.append(F)
+                rk = Sequence(inv).coefficient_matrix()[0].rank()
+                if rk == ms[deg]:
                    break
+                if rk < len(inv):
+                    inv.pop()
        return list(inv)
```


Discussion:
@Bruno-TT is now doing a Google Summer of Code project on invariants of finite groups in Sage, with me as a mentor.
(he''ll start in few weeks full-time, now he has exams).

We see these kinds of bugs...
For what's worth, there is a check:
```
sage: G.molien_series()
1 + t^2 + t^3 + t^4 + t^5 + 2*t^6 + t^7 + 2*t^8 + 2*t^9 + 2*t^10 + 2*t^11 + 3*t^12 + 2*t^13 + 3*t^14 + 3*t^15 + 3*t^16 + 3*t^17 + 4*t^18 + 3*t^19 + O(t^20)
```
- showing that there should be 3 linearly independent invariants in degree 14.
Hi, I've pushed a fix for this, a `new_invariant_is_linearly_independent` function that must be satisfied for an invariant to be returned.

```
def new_invariant_is_linearly_independent(F, inv, R):
    if len(inv)==0: return True
    return PolynomialSequence(R, inv).coefficient_matrix()[0].rank() != PolynomialSequence(R, list(inv)+[F]).coefficient_matrix()[0].rank()
```

The above implementation works most of the time, but fails the following test

```
sage: sage: K.<i> = CyclotomicField(4)
sage: G =  MatrixGroup(CyclicPermutationGroup(3))
sage: chi = G.character(G.character_table()[1])
sage: R.<x,y,z> = K[]
sage: sorted(G.invariants_of_degree(2, R=R, chi=chi))
```
throwing this error `ValueError: No consistent embedding of Number Field in izeta3 with defining polynomial x^4 + 2*x^3 + 5*x^2 + 4*x + 1 with izeta3 = -0.500000000000000? - 0.1339745962155614?*I into Cyclotomic Field of order 4 and degree 2.
` when we try to instantiate the PolynomialRing object

For the time being I'm just catching the `ValueError` and defaulting to returning true (this is still no worse than how it was working previously), but hopefully someone else can improve upon this or provide pointers

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
@@ -38,6 +38,7 @@
 from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
 from sage.rings.power_series_ring import PowerSeriesRing
 from sage.rings.qqbar import QQbar
+from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence
 
 
 class FinitelyGeneratedMatrixGroup_gap(MatrixGroup_gap):
@@ -898,9 +899,23 @@ def invariants_of_degree(self, deg, chi=None, R=None):
         inv = set()
         for e in IntegerVectors(deg, D):
             F = self.reynolds_operator(R.monomial(*e), chi=chi)
-            if not F.is_zero():
-                F = F / F.lc()
+            if not F.is_zero() and _new_invariant_is_linearly_independent((F:=F/F.lc()), inv):
                 inv.add(F)
                 if len(inv) == ms[deg]:
                     break
         return list(inv)
+
+def _new_invariant_is_linearly_independent(F, invariants):
+    """
+    EXAMPLES ::
+        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))
+        sage: s.coefficient_matrix()[0].rank()
+        3
+        sage: len(s)
+        3
+    """
+    if len(invariants)==0:
+        return True
+    return PolynomialSequence(invariants).coefficient_matrix()[0].rank() != PolynomialSequence(list(invariants)+[F]).coefficient_matrix()[0].rank()


Test patch: