Skip to content

Commit

Permalink
One attempt to normalize the generators and check independence.
Browse files Browse the repository at this point in the history
  • Loading branch information
tscrim committed Apr 4, 2024
1 parent 1cd4990 commit c1420f2
Showing 1 changed file with 141 additions and 113 deletions.
254 changes: 141 additions & 113 deletions src/sage/groups/abelian_gps/abelian_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -1622,78 +1622,95 @@ class AbelianGroup_subgroup(AbelianGroup_class):
There should be a way to coerce an element of a subgroup
into the ambient group.
EXAMPLES::
sage: # needs sage.libs.gap # optional - gap_package_polycyclic
sage: F = AbelianGroup(5, [30,64,729], names=list("abcde"))
sage: a,b,c,d,e = F.gens()
sage: F.subgroup([a^3,b])
Multiplicative Abelian subgroup isomorphic to Z x Z generated by {a^3, b}
sage: F.subgroup([c])
Multiplicative Abelian subgroup isomorphic to C2 x C3 x C5 generated by {c}
sage: F.subgroup([a, c])
Multiplicative Abelian subgroup isomorphic to C2 x C3 x C5 x Z generated by {a, c}
sage: F.subgroup([a, b*c])
Multiplicative Abelian subgroup isomorphic to Z x Z generated by {a, b*c}
sage: F.subgroup([b*c, d])
Multiplicative Abelian subgroup isomorphic to C64 x Z generated by {b*c, d}
sage: F.subgroup([a*b, c^6, d], names=list("xyz"))
Multiplicative Abelian subgroup isomorphic to C5 x C64 x Z generated by {a*b, c^6, d}
sage: H.<x,y,z> = F.subgroup([a*b, c^6, d]); H
Multiplicative Abelian subgroup isomorphic to C5 x C64 x Z generated by {a*b, c^6, d}
sage: G = F.subgroup([a*b, c^6, d], names=list("xyz")); G
Multiplicative Abelian subgroup isomorphic to C5 x C64 x Z generated by {a*b, c^6, d}
sage: x,y,z = G.gens()
sage: x.order()
+Infinity
sage: y.order()
5
sage: z.order()
64
sage: A = AbelianGroup(5, [3, 5, 5, 7, 8], names="abcde")
sage: a,b,c,d,e = A.gens()
sage: A.subgroup([a,b])
Multiplicative Abelian subgroup isomorphic to C3 x C5 generated by {a, b}
sage: A.subgroup([a,b,c,d^2,e])
Multiplicative Abelian subgroup isomorphic to C3 x C5 x C5 x C7 x C8 generated by {a, b, c, d^2, e}
sage: A.subgroup([a, b, c, d^2, e^2])
Multiplicative Abelian subgroup isomorphic to C3 x C4 x C5 x C5 x C7 generated by {a, b, c, d^2, e^2}
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()
(5, 5, 7, 4)
sage: A = AbelianGroup(4,[1009, 2003, 3001, 4001], names="abcd")
sage: a,b,c,d = A.gens()
sage: # long time
sage: B = A.subgroup([a^3,b,c,d])
sage: B.gens_orders()
(1009, 2003, 3001, 4001)
sage: A.order()
24266473210027
sage: B.order()
24266473210027
sage: A = AbelianGroup(4, [1008, 2003, 3001, 4001], names="abcd")
sage: a,b,c,d = A.gens()
sage: B = A.subgroup([a^3,b,c,d]); B
Multiplicative Abelian subgroup isomorphic
to C3 x C7 x C16 x C2003 x C3001 x C4001 generated by {a^3, b, c, d}
Infinite groups can also be handled::
sage: # needs sage.libs.gap # optional - gap_package_polycyclic
sage: G = AbelianGroup([3,4,0], names="abc")
sage: a,b,c = G.gens()
sage: F = G.subgroup([a, b^2, c]); F
Multiplicative Abelian subgroup isomorphic to C2 x C3 x Z
generated by {a, b^2, c}
sage: F.gens_orders()
(3, 2, 0)
sage: F.gens()
(a, b^2, c)
sage: F.order()
+Infinity
"""
def __init__(self, ambient, gens, names="f", category=None):
"""
EXAMPLES::
Initialize ``self``.
sage: # needs sage.libs.gap # optional - gap_package_polycyclic
sage: F = AbelianGroup(5, [30,64,729], names=list("abcde"))
sage: a,b,c,d,e = F.gens()
sage: F.subgroup([a^3,b])
Multiplicative Abelian subgroup isomorphic to Z x Z generated by {a^3, b}
sage: F.subgroup([c])
Multiplicative Abelian subgroup isomorphic to C2 x C3 x C5 generated by {c}
sage: F.subgroup([a, c])
Multiplicative Abelian subgroup isomorphic to C2 x C3 x C5 x Z generated by {a, c}
sage: F.subgroup([a, b*c])
Multiplicative Abelian subgroup isomorphic to Z x Z generated by {a, b*c}
sage: F.subgroup([b*c, d])
Multiplicative Abelian subgroup isomorphic to C64 x Z generated by {b*c, d}
sage: F.subgroup([a*b, c^6, d], names=list("xyz"))
Multiplicative Abelian subgroup isomorphic to C5 x C64 x Z generated by {a*b, c^6, d}
sage: H.<x,y,z> = F.subgroup([a*b, c^6, d]); H
Multiplicative Abelian subgroup isomorphic to C5 x C64 x Z generated by {a*b, c^6, d}
sage: G = F.subgroup([a*b, c^6, d], names=list("xyz")); G
Multiplicative Abelian subgroup isomorphic to C5 x C64 x Z generated by {a*b, c^6, d}
sage: x,y,z = G.gens()
sage: x.order()
+Infinity
sage: y.order()
5
sage: z.order()
64
sage: A = AbelianGroup(5, [3, 5, 5, 7, 8], names="abcde")
sage: a,b,c,d,e = A.gens()
sage: A.subgroup([a,b])
Multiplicative Abelian subgroup isomorphic to C3 x C5 generated by {a, b}
sage: A.subgroup([a,b,c,d^2,e])
Multiplicative Abelian subgroup isomorphic to C3 x C5 x C5 x C7 x C8 generated by {a, b, c, d^2, e}
sage: A.subgroup([a, b, c, d^2, e^2])
Multiplicative Abelian subgroup isomorphic to C3 x C4 x C5 x C5 x C7 generated by {a, b, c, d^2, e^2}
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()
(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])
sage: B.gens_orders()
(1009, 2003, 3001, 4001)
sage: A.order()
24266473210027
sage: B.order()
24266473210027
sage: A = AbelianGroup(4, [1008, 2003, 3001, 4001], names="abcd")
sage: a,b,c,d = A.gens()
sage: B = A.subgroup([a^3,b,c,d]); B
Multiplicative Abelian subgroup isomorphic
to C3 x C7 x C16 x C2003 x C3001 x C4001 generated by {a^3, b, c, d}
Infinite groups can also be handled::
TESTS::
sage: # needs sage.libs.gap # optional - gap_package_polycyclic
sage: G = AbelianGroup([3,4,0], names="abc")
sage: a,b,c = G.gens()
sage: F = G.subgroup([a, b^2, c]); F
Multiplicative Abelian subgroup isomorphic to C2 x C3 x Z
generated by {a, b^2, c}
sage: F.gens_orders()
(3, 2, 0)
sage: F.gens()
(a, b^2, c)
sage: F.order()
+Infinity
sage: S.<a,b,c,d,e> = AbelianGroup(5, [30,64,729])
sage: H = G.subgroup([a^3,b])
sage: TestSuite(H).run() # long time
sage: G.<a,b,c> = AbelianGroup([3,4,0])
sage: H = G.subgroup([a, b^2, c^3])
sage: TestSuite(H).run() # long time
sage: G.<a,b,c,d> = AbelianGroup([0,0,6,3])
sage: H = G.subgroup([a*b, a/b, (a*b)^5, (a*b)^3, c^2, c*d])
sage: TestSuite(H).run() # long time
Testing issue :issue:`18863`::
Expand All @@ -1709,26 +1726,33 @@ def __init__(self, ambient, gens, names="f", category=None):
raise TypeError("gens (=%s) must be a tuple" % gens)

self._ambient_group = ambient
H_gens = tuple(x for x in gens if x != ambient.one()) # clean entry
self._gens = H_gens

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
# get rid of the trivial identities
gens = [x for x in gens if x != ambient.one()]

G = libgap(ambient)
H = G.Subgroup(gens)
indep_gens = H.IndependentGeneratorsOfAbelianGroup()
# The internal structure in GAP for finite and infinite Abelian groups
# are different, so our reconstruction of the elements depends on this.
if G.IsFinite():
lifted_gens = []
amb_gens = ambient.gens()
for h in indep_gens:
x = libgap.Factorization(G, h)
data = libgap.ExtRepOfObj(x).sage() # (indice, power, indice, power, etc)
indices = data[0::2]
powers = data[1::2]
elt = ambient.prod(amb_gens[i-1] ** e for i, e in zip(indices, powers))
lifted_gens.append(elt)
self._lifted_gens = tuple(lifted_gens)
else:
self._lifted_gens = tuple([ambient(h.Exponents().sage())
for h in indep_gens])
invs = [ZZ(ord) if (ord := h.Order().sage()) is not infinity else ZZ.zero()
for h in indep_gens]

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, gens_orders, names, category=category)
category = Groups().Commutative().Subobjects().or_subcategory(category)
AbelianGroup_class.__init__(self, tuple(invs), names, category=category)

def __contains__(self, x):
"""
Expand Down Expand Up @@ -1778,16 +1802,16 @@ def __contains__(self, x):
amb_inv = self.ambient_group().gens_orders()
inv_basis = diagonal_matrix(ZZ, amb_inv)
gens_basis = matrix(
ZZ, len(self._gens), len(amb_inv),
[g.list() for g in self._gens]
ZZ, len(self._lifted_gens), len(amb_inv),
[g.list() for g in self._lifted_gens]
)
return (vector(ZZ, x.list())
in inv_basis.stack(gens_basis).row_module())
return False

def ambient_group(self):
"""
Return the ambient group related to self.
Return the ambient group related to ``self``.
OUTPUT:
Expand Down Expand Up @@ -1825,8 +1849,8 @@ def equals(left, right):
Multiplicative Abelian group isomorphic to C2 x C3 x C4
sage: a,b,c = G.gens()
sage: F = G.subgroup([a,b^2]); F
Multiplicative Abelian subgroup isomorphic to C2 x C3 generated by {a, b^2}
sage: F<G
Multiplicative Abelian subgroup isomorphic to C2 x C3 generated by {a, b}
sage: F < G
True
sage: A = AbelianGroup(1, [6])
sage: A.subgroup(list(A.gens())) == A
Expand All @@ -1853,6 +1877,19 @@ def equals(left, right):

__eq__ = equals

def __hash__(self):
r"""
Return the hash of ``self``.
EXAMPLES::
sage: G.<f0,f1> = AbelianGroup([4, 4])
sage: H = G.subgroup(list(G))
sage: hash(H) == hash(G.subgroup(G.gens()))
True
"""
return hash((self._ambient_group, self._lifted_gens))

def _repr_(self):
"""
Return a string representation
Expand All @@ -1866,48 +1903,39 @@ def _repr_(self):
sage: A._repr_() # needs sage.libs.gap # optional - gap_package_polycyclic
'Multiplicative Abelian subgroup isomorphic to Z generated by {a}'
"""
eldv = self._abinvs
if self.is_trivial():
return "Trivial Abelian subgroup"
s = 'Multiplicative Abelian subgroup isomorphic to '
s += self._group_notation(eldv)
s += self._group_notation(self.gens_orders())
s += ' generated by '
s += '{' + ', '.join(map(str, self.gens())) + '}'
s += '{' + ', '.join(map(str, self._lifted_gens)) + '}'
return s

def gens(self) -> tuple:
"""
Return the generators for this subgroup.
OUTPUT:
A tuple of group elements generating the subgroup.
Return the generators of ``self`` (as elements in the ambient group).
EXAMPLES::
sage: # needs sage.libs.gap # optional - gap_package_polycyclic
sage: G.<a,b> = AbelianGroup(2)
sage: A = G.subgroup([a])
sage: G.gens()
(a, b)
sage: A.gens()
(a,)
sage: G.<a,b,c,d> = AbelianGroup([0,0,6,3])
sage: H = G.subgroup([a*b, a/b, (a*b)^5, (a*b)^3, c^2, c*d])
sage: H.gens()
(b^2, a^-1*b^-3*d, c^3, d, c^4*d^2)
"""
return self._gens
return self._lifted_gens

def gen(self, n):
"""
Return the nth generator of this subgroup.
Return the ``n``-th generator of this subgroup.
EXAMPLES::
sage: # needs sage.libs.gap # optional - gap_package_polycyclic
sage: G.<a,b> = AbelianGroup(2)
sage: A = G.subgroup([a])
sage: A.gen(0)
a
sage: G.<a,b,c,d> = AbelianGroup([0,0,6,3])
sage: H = G.subgroup([a*b, a/b, (a*b)^5, (a*b)^3, c^2, c*d])
sage: [H.gen(n) for n in range(len(H.gens()))]
[b^2, a^-1*b^-3*d, c^3, d, c^4*d^2]
"""
return self._gens[n]
return self._lifted_gens[n]


# We allow subclasses to override this, analogous to Element
Expand Down

0 comments on commit c1420f2

Please sign in to comment.