Repository: sagemath/sage

Issue:
Bug in `AbelianGroup.Subgroup`
### Steps To Reproduce

```python
sage: G, (g0, g1) = AbelianGroup(2, [48, 0]).objgens()
....: print(G, g0, g1)
....: assert g0.order() == 48 and g1.order() == oo
....: 
....: G0 = G.subgroup([g0])
....: print(G0, G0.gens(), G0.gens_orders())
....: print(len(G0.gens()) == len(G0.gens_orders()))
Multiplicative Abelian group isomorphic to C48 x Z f0 f1
Multiplicative Abelian subgroup isomorphic to C3 x C16 generated by {f0} (f0,) (3, 16)
False
```

Problem: The length of `G0.gens()` and `G0.gens_orders()` should be the same.

### Additional Information

The `Subgroup` is computed [here](https://github.com/sagemath/sage/blob/develop/src/sage/groups/abelian_gps/abelian_group.py#L1710). The issue seems to be that the GAP functions `AbelianInvariants` doesn't actually return the orders of the generators in `GeneratorsOfGroup`, as demonstrated below:
```python
sage: G, (g0, g1) = AbelianGroup(2, [48, 0]).objgens()
....: H = libgap(G).Subgroup(gens)
....: H_invs = H.AbelianInvariants().sage()
....: H_gens = H.GeneratorsOfGroup()
....: print(H, H_invs, H_gens)
Pcp-group with orders [ 48 ] [3, 16] [ g1 ]
```

The obvious fix would be to replace `.AbelianInvariants` on line 1710 with, well, `[g.Order().sage() for g in H.GeneratorsOfGroup()]` or something like that. If any GAP users have a better solution that returns the order of all (finite?) generators, please suggest it, since I haven't used GAP before.



Discussion:

Resolving patch:
diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py
--- a/src/sage/groups/abelian_gps/abelian_group.py
+++ b/src/sage/groups/abelian_gps/abelian_group.py
@@ -380,6 +380,7 @@ def _normalize(n, gens_orders=None, names="f"):
         names = tuple(names)
     return (gens_orders, names)
 
+
 def AbelianGroup(n, gens_orders=None, names="f"):
     r"""
     Create the multiplicative abelian group in `n` generators
@@ -444,6 +445,7 @@ def AbelianGroup(n, gens_orders=None, names="f"):
     M = AbelianGroup_class(gens_orders, names)
     return M
 
+
 def is_AbelianGroup(x):
     """
     Return True if ``x`` is an Abelian group.
@@ -535,7 +537,7 @@ def __init__(self, generator_orders, names, category=None):
         """
         assert isinstance(names, (str, tuple))
         assert isinstance(generator_orders, tuple)
-        assert all(isinstance(order,Integer) for order in generator_orders)
+        assert all(isinstance(order, Integer) for order in generator_orders)
         self._gens_orders = generator_orders
         n = len(generator_orders)
         names = normalize_names(n, names)
@@ -943,7 +945,7 @@ def gens(self):
             sage: [g.order() for g in F.gens()]
             [+Infinity, +Infinity, +Infinity, 3, 2]
         """
-        return tuple( self.gen(i) for i in range(self.ngens()) )
+        return tuple(self.gen(i) for i in range(self.ngens()))
 
     def gens_orders(self):
         """
@@ -978,6 +980,10 @@ def gens_orders(self):
 
         TESTS::
 
+            sage: G, (g0, g1) = AbelianGroup(2, [48, 0]).objgens()
+            sage: G0 = G.subgroup([g0])
+            sage: len(G0.gens()) == len(G0.gens_orders())
+            True
             sage: F = AbelianGroup(3, [2], names='abc')
             sage: list(map(type, F.gens_orders()))
             [<class 'sage.rings.integer.Integer'>,
@@ -1175,7 +1181,7 @@ def random_element(self):
             order = g.order()
             if order is infinity:
                 order = 42  # infinite order; randomly chosen maximum
-            result *= g**(randint(0, order))
+            result *= g ** (randint(0, order))
         return result
 
     def _repr_(self) -> str:
@@ -1554,8 +1560,8 @@ def subgroup_reduced(self, elts, verbose=False):
         rel_lattice = X.span([X.gen(i) * self.gens_orders()[i] for i in range(d)])
         isect = elt_lattice.intersection(rel_lattice)
         mat = matrix([elt_lattice.coordinate_vector(x) for x in isect.gens()]).change_ring(ZZ)
-        D,U,V = mat.smith_form()
-        new_basis = [(elt_lattice.linear_combination_of_basis((~V).row(i)).list(), D[i,i]) for i in range(U.ncols())]
+        D, U, V = mat.smith_form()
+        new_basis = [(elt_lattice.linear_combination_of_basis((~V).row(i)).list(), D[i, i]) for i in range(U.ncols())]
         return self.subgroup([self([x[0][i] % self.gens_orders()[i]
                                     for i in range(d)]) for x in new_basis if x[1] != 1])
 
@@ -1658,7 +1664,7 @@ def __init__(self, ambient, gens, names="f", category=None):
             sage: B = A.subgroup([a^3, b, c, d, e^2]); B
             Multiplicative Abelian subgroup isomorphic to C4 x C5 x C5 x C7 generated by {b, c, d, e^2}
             sage: B.gens_orders()
-            (4, 5, 5, 7)
+            (5, 5, 7, 4)
             sage: A = AbelianGroup(4,[1009, 2003, 3001, 4001], names="abcd")
             sage: a,b,c,d = A.gens()
             sage: B = A.subgroup([a^3,b,c,d])
@@ -1683,7 +1689,7 @@ def __init__(self, ambient, gens, names="f", category=None):
             Multiplicative Abelian subgroup isomorphic to C2 x C3 x Z
              generated by {a, b^2, c}
             sage: F.gens_orders()
-            (2, 3, 0)
+            (3, 2, 0)
             sage: F.gens()
             (a, b^2, c)
             sage: F.order()
@@ -1709,16 +1715,20 @@ def __init__(self, ambient, gens, names="f", category=None):
         H = libgap(ambient).Subgroup(H_gens)
 
         invs = H.TorsionSubgroup().AbelianInvariants().sage()
+        gens_orders = tuple([ZZ(order_sage) for g in H.GeneratorsOfGroup()
+                            if (order_sage := g.Order().sage()) is not infinity])
+
         rank = len([1 for g in H.GeneratorsOfGroup()
                     if g.Order().sage() is infinity])
         invs += [0] * rank
 
+        gens_orders += (ZZ.zero(),) * rank
         self._abinvs = invs
         invs = tuple(ZZ(i) for i in invs)
 
         if category is None:
             category = Groups().Commutative().Subobjects()
-        AbelianGroup_class.__init__(self, invs, names, category=category)
+        AbelianGroup_class.__init__(self, gens_orders, names, category=category)
 
     def __contains__(self, x):
         """


Test patch: