LibGAP-based Groups#

This module provides helper class for wrapping GAP groups via libgap. See free_group for an example how they are used.

The parent class keeps track of the GAP element object, to use it in your Python parent you have to derive both from the suitable group parent and ParentLibGAP

sage: from sage.groups.libgap_wrapper import ElementLibGAP, ParentLibGAP
sage: from sage.groups.group import Group
sage: class FooElement(ElementLibGAP):
....:     pass
sage: class FooGroup(Group, ParentLibGAP):
....:     Element = FooElement
....:     def __init__(self):
....:         lg = libgap(libgap.CyclicGroup(3))    # dummy
....:         ParentLibGAP.__init__(self, lg)
....:         Group.__init__(self)

Note how we call the constructor of both superclasses to initialize Group and ParentLibGAP separately. The parent class implements its output via LibGAP:

sage: FooGroup()
<pc group of size 3 with 1 generator>
sage: type(FooGroup().gap())
<class 'sage.libs.gap.element.GapElement'>

The element class is a subclass of MultiplicativeGroupElement. To use it, you just inherit from ElementLibGAP

sage: element = FooGroup().an_element()
sage: element
f1

The element class implements group operations and printing via LibGAP:

sage: element._repr_()
'f1'
sage: element * element
f1^2

AUTHORS:

  • Volker Braun

class sage.groups.libgap_wrapper.ElementLibGAP#

Bases: MultiplicativeGroupElement

A class for LibGAP-based Sage group elements

INPUT:

  • parent – the Sage parent

  • libgap_element – the libgap element that is being wrapped

EXAMPLES:

sage: from sage.groups.libgap_wrapper import ElementLibGAP, ParentLibGAP
sage: from sage.groups.group import Group
sage: class FooElement(ElementLibGAP):
....:     pass
sage: class FooGroup(Group, ParentLibGAP):
....:     Element = FooElement
....:     def __init__(self):
....:         lg = libgap(libgap.CyclicGroup(3))    # dummy
....:         ParentLibGAP.__init__(self, lg)
....:         Group.__init__(self)
sage: FooGroup()
<pc group of size 3 with 1 generator>
sage: FooGroup().gens()
(f1,)
gap()#

Return a LibGAP representation of the element.

OUTPUT:

A GapElement

EXAMPLES:

sage: G.<a,b> = FreeGroup('a, b')
sage: x = G([1, 2, -1, -2])
sage: x
a*b*a^-1*b^-1
sage: xg = x.gap()
sage: xg
a*b*a^-1*b^-1
sage: type(xg)
<class 'sage.libs.gap.element.GapElement'>
inverse()#

Return the inverse of self.

is_conjugate(other)#

Return whether the elements self and other are conjugate.

EXAMPLES:

sage: from sage.groups.libgap_group import GroupLibGAP
sage: G = GroupLibGAP(libgap.GL(2, 3))
sage: a,b = G.gens()
sage: a.is_conjugate(b)
False
sage: a.is_conjugate((a*b^2) * a * ~(a*b^2))
True
is_one()#

Test whether the group element is the trivial element.

OUTPUT:

Boolean.

EXAMPLES:

sage: G.<a,b> = FreeGroup('a, b')
sage: x = G([1, 2, -1, -2])
sage: x.is_one()
False
sage: (x * ~x).is_one()
True
multiplicative_order()#

Return the multiplicative order.

EXAMPLES:

sage: from sage.groups.libgap_group import GroupLibGAP
sage: G = GroupLibGAP(libgap.GL(2, 3))
sage: a,b = G.gens()
sage: print(a.order())
2
sage: print(a.multiplicative_order())
2

sage: z = Mod(0, 3)
sage: o = Mod(1, 3)
sage: G(libgap([[o,o],[z,o]])).order()
3
normalizer()#

Return the normalizer of the cyclic group generated by this element.

EXAMPLES:

sage: from sage.groups.libgap_group import GroupLibGAP
sage: G = GroupLibGAP(libgap.GL(3,3))
sage: a,b = G.gens()
sage: H = a.normalizer()
sage: H
<group of 3x3 matrices over GF(3)>
sage: H.cardinality()
96
sage: all(g*a == a*g for g in H)
True
nth_roots(n)#

Return the set of n-th roots of this group element.

EXAMPLES:

sage: from sage.groups.libgap_group import GroupLibGAP
sage: G = GroupLibGAP(libgap.GL(3,3))
sage: a,b = G.gens()
sage: g = a*b**2*a*~b
sage: r = g.nth_roots(4)
sage: r
[[ [ Z(3), Z(3), Z(3)^0 ], [ Z(3)^0, Z(3)^0, 0*Z(3) ], [ 0*Z(3), Z(3), 0*Z(3) ] ],
 [ [ Z(3)^0, Z(3)^0, Z(3) ], [ Z(3), Z(3), 0*Z(3) ], [ 0*Z(3), Z(3)^0, 0*Z(3) ] ]]
sage: r[0]**4 == r[1]**4 == g
True
order()#

Return the multiplicative order.

EXAMPLES:

sage: from sage.groups.libgap_group import GroupLibGAP
sage: G = GroupLibGAP(libgap.GL(2, 3))
sage: a,b = G.gens()
sage: print(a.order())
2
sage: print(a.multiplicative_order())
2

sage: z = Mod(0, 3)
sage: o = Mod(1, 3)
sage: G(libgap([[o,o],[z,o]])).order()
3
class sage.groups.libgap_wrapper.ParentLibGAP(libgap_parent, ambient=None)#

Bases: SageObject

A class for parents to keep track of the GAP parent.

This is not a complete group in Sage, this class is only a base class that you can use to implement your own groups with LibGAP. See libgap_group for a minimal example of a group that is actually usable.

Your implementation definitely needs to supply

  • __reduce__(): serialize the LibGAP group. Since GAP does not support Python pickles natively, you need to figure out yourself how you can recreate the group from a pickle.

INPUT:

  • libgap_parent – the libgap element that is the parent in GAP.

  • ambient – A derived class of ParentLibGAP or None (default). The ambient class if libgap_parent has been defined as a subgroup.

EXAMPLES:

sage: from sage.groups.libgap_wrapper import ElementLibGAP, ParentLibGAP
sage: from sage.groups.group import Group
sage: class FooElement(ElementLibGAP):
....:     pass
sage: class FooGroup(Group, ParentLibGAP):
....:     Element = FooElement
....:     def __init__(self):
....:         lg = libgap(libgap.CyclicGroup(3))    # dummy
....:         ParentLibGAP.__init__(self, lg)
....:         Group.__init__(self)
sage: FooGroup()
<pc group of size 3 with 1 generator>
ambient()#

Return the ambient group of a subgroup.

OUTPUT:

A group containing self. If self has not been defined as a subgroup, we just return self.

EXAMPLES:

sage: G = FreeGroup(3)
sage: G.ambient() is G
True
gap()#

Return the gap representation of self.

OUTPUT:

A GapElement

EXAMPLES:

sage: G = FreeGroup(3);  G
Free Group on generators {x0, x1, x2}
sage: G.gap()
<free group on the generators [ x0, x1, x2 ]>
sage: G.gap().parent()
C library interface to GAP
sage: type(G.gap())
<class 'sage.libs.gap.element.GapElement'>

This can be useful, for example, to call GAP functions that are not wrapped in Sage:

sage: G = FreeGroup(3)
sage: H = G.gap()
sage: H.DirectProduct(H)
<fp group on the generators [ f1, f2, f3, f4, f5, f6 ]>
sage: H.DirectProduct(H).RelatorsOfFpGroup()
[ f1^-1*f4^-1*f1*f4, f1^-1*f5^-1*f1*f5, f1^-1*f6^-1*f1*f6, f2^-1*f4^-1*f2*f4,
  f2^-1*f5^-1*f2*f5, f2^-1*f6^-1*f2*f6, f3^-1*f4^-1*f3*f4, f3^-1*f5^-1*f3*f5,
  f3^-1*f6^-1*f3*f6 ]

We can also convert directly to libgap:

sage: libgap(GL(2, ZZ))
GL(2,Integers)
gen(i)#

Return the \(i\)-th generator of self.

Warning

Indexing starts at \(0\) as usual in Sage/Python. Not as in GAP, where indexing starts at \(1\).

INPUT:

  • i – integer between \(0\) (inclusive) and ngens() (exclusive). The index of the generator.

OUTPUT:

The \(i\)-th generator of the group.

EXAMPLES:

sage: G = FreeGroup('a, b')
sage: G.gen(0)
a
sage: G.gen(1)
b
generators()#

Return the generators of the group.

EXAMPLES:

sage: G = FreeGroup(2)
sage: G.gens()
(x0, x1)
sage: H = FreeGroup('a, b, c')
sage: H.gens()
(a, b, c)

generators() is an alias for gens()

sage: G = FreeGroup('a, b')
sage: G.generators()
(a, b)
sage: H = FreeGroup(3, 'x')
sage: H.generators()
(x0, x1, x2)
gens()#

Return the generators of the group.

EXAMPLES:

sage: G = FreeGroup(2)
sage: G.gens()
(x0, x1)
sage: H = FreeGroup('a, b, c')
sage: H.gens()
(a, b, c)

generators() is an alias for gens()

sage: G = FreeGroup('a, b')
sage: G.generators()
(a, b)
sage: H = FreeGroup(3, 'x')
sage: H.generators()
(x0, x1, x2)
is_subgroup()#

Return whether the group was defined as a subgroup of a bigger group.

You can access the containing group with ambient().

OUTPUT:

Boolean.

EXAMPLES:

sage: G = FreeGroup(3)
sage: G.is_subgroup()
False
maximal_normal_subgroups()#

Return the maximal proper normal subgroups of self.

This raises an error if \(G/[G, G]\) is infinite, yielding infinitely many maximal normal subgroups.

EXAMPLES:

sage: SL(2,GF(49)).minimal_normal_subgroups()
[Subgroup with 1 generators (
 [6 0]
 [0 6]
 ) of Special Linear Group of degree 2 over Finite Field in z2 of size 7^2]
minimal_normal_subgroups()#

Return the nontrivial minimal normal subgroups of self.

EXAMPLES:

sage: SL(2,GF(49)).minimal_normal_subgroups()
[Subgroup with 1 generators (
 [6 0]
 [0 6]
 ) of Special Linear Group of degree 2 over Finite Field in z2 of size 7^2]
ngens()#

Return the number of generators of self.

OUTPUT:

Integer.

EXAMPLES:

sage: G = FreeGroup(2)
sage: G.ngens()
2
one()#

Return the identity element of self.

EXAMPLES:

sage: G = FreeGroup(3)
sage: G.one()
1
sage: G.one() == G([])
True
sage: G.one().Tietze()
()
subgroup(generators)#

Return the subgroup generated.

INPUT:

  • generators – a list/tuple/iterable of group elements.

OUTPUT:

The subgroup generated by generators.

EXAMPLES:

sage: F.<a,b> = FreeGroup()
sage: G = F.subgroup([a^2*b]);  G
Group([ a^2*b ])
sage: G.gens()
(a^2*b,)

We check that coercions between the subgroup and its ambient group work:

sage: F.0 * G.0
a^3*b

Checking that github issue #19270 is fixed:

sage: gens = [w.matrix() for w in WeylGroup(['B', 3])]
sage: G = MatrixGroup(gens)
sage: import itertools
sage: diagonals = itertools.product((1,-1), repeat=3)
sage: subgroup_gens = [diagonal_matrix(L) for L in diagonals]
sage: G.subgroup(subgroup_gens)
Subgroup with 8 generators of Matrix group over Rational Field with 48 generators