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)
>>> from sage.all import *
>>> from sage.groups.libgap_wrapper import ElementLibGAP, ParentLibGAP
>>> from sage.groups.group import Group
>>> class FooElement(ElementLibGAP):
... pass
>>> class FooGroup(Group, ParentLibGAP):
... Element = FooElement
... def __init__(self):
... lg = libgap(libgap.CyclicGroup(Integer(3))) # dummy
... ParentLibGAP.__init__(self, lg)
... Group.__init__(self)
from sage.groups.libgap_wrapper import ElementLibGAP, ParentLibGAP from sage.groups.group import Group class FooElement(ElementLibGAP): pass 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'>
>>> from sage.all import *
>>> FooGroup()
<pc group of size 3 with 1 generator>
>>> type(FooGroup().gap())
<class 'sage.libs.gap.element.GapElement'>
FooGroup() type(FooGroup().gap())
The element class is a subclass of
MultiplicativeGroupElement
. To use
it, you just inherit from ElementLibGAP
sage: element = FooGroup().an_element()
sage: element
f1
>>> from sage.all import *
>>> element = FooGroup().an_element()
>>> element
f1
element = FooGroup().an_element() element
The element class implements group operations and printing via LibGAP:
sage: element._repr_()
'f1'
sage: element * element
f1^2
>>> from sage.all import *
>>> element._repr_()
'f1'
>>> element * element
f1^2
element._repr_() element * element
AUTHORS:
Volker Braun
- class sage.groups.libgap_wrapper.ElementLibGAP[source]¶
Bases:
MultiplicativeGroupElement
A class for LibGAP-based Sage group elements.
INPUT:
parent
– the Sage parentlibgap_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,)
>>> from sage.all import * >>> from sage.groups.libgap_wrapper import ElementLibGAP, ParentLibGAP >>> from sage.groups.group import Group >>> class FooElement(ElementLibGAP): ... pass >>> class FooGroup(Group, ParentLibGAP): ... Element = FooElement ... def __init__(self): ... lg = libgap(libgap.CyclicGroup(Integer(3))) # dummy ... ParentLibGAP.__init__(self, lg) ... Group.__init__(self) >>> FooGroup() <pc group of size 3 with 1 generator> >>> FooGroup().gens() (f1,)
from sage.groups.libgap_wrapper import ElementLibGAP, ParentLibGAP from sage.groups.group import Group class FooElement(ElementLibGAP): pass class FooGroup(Group, ParentLibGAP): Element = FooElement def __init__(self): lg = libgap(libgap.CyclicGroup(3)) # dummy ParentLibGAP.__init__(self, lg) Group.__init__(self) FooGroup() FooGroup().gens()
- gap()[source]¶
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'>
>>> from sage.all import * >>> G = FreeGroup('a, b', names=('a', 'b',)); (a, b,) = G._first_ngens(2) >>> x = G([Integer(1), Integer(2), -Integer(1), -Integer(2)]) >>> x a*b*a^-1*b^-1 >>> xg = x.gap() >>> xg a*b*a^-1*b^-1 >>> type(xg) <class 'sage.libs.gap.element.GapElement'>
G.<a,b> = FreeGroup('a, b') x = G([1, 2, -1, -2]) x xg = x.gap() xg type(xg)
- is_conjugate(other)[source]¶
Return whether the elements
self
andother
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
>>> from sage.all import * >>> from sage.groups.libgap_group import GroupLibGAP >>> G = GroupLibGAP(libgap.GL(Integer(2), Integer(3))) >>> a,b = G.gens() >>> a.is_conjugate(b) False >>> a.is_conjugate((a*b**Integer(2)) * a * ~(a*b**Integer(2))) True
from sage.groups.libgap_group import GroupLibGAP G = GroupLibGAP(libgap.GL(2, 3)) a,b = G.gens() a.is_conjugate(b) a.is_conjugate((a*b^2) * a * ~(a*b^2))
- is_one()[source]¶
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
>>> from sage.all import * >>> G = FreeGroup('a, b', names=('a', 'b',)); (a, b,) = G._first_ngens(2) >>> x = G([Integer(1), Integer(2), -Integer(1), -Integer(2)]) >>> x.is_one() False >>> (x * ~x).is_one() True
G.<a,b> = FreeGroup('a, b') x = G([1, 2, -1, -2]) x.is_one() (x * ~x).is_one()
- multiplicative_order()[source]¶
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
>>> from sage.all import * >>> from sage.groups.libgap_group import GroupLibGAP >>> G = GroupLibGAP(libgap.GL(Integer(2), Integer(3))) >>> a,b = G.gens() >>> print(a.order()) 2 >>> print(a.multiplicative_order()) 2 >>> z = Mod(Integer(0), Integer(3)) >>> o = Mod(Integer(1), Integer(3)) >>> G(libgap([[o,o],[z,o]])).order() 3
from sage.groups.libgap_group import GroupLibGAP G = GroupLibGAP(libgap.GL(2, 3)) a,b = G.gens() print(a.order()) print(a.multiplicative_order()) z = Mod(0, 3) o = Mod(1, 3) G(libgap([[o,o],[z,o]])).order()
- normalizer()[source]¶
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
>>> from sage.all import * >>> from sage.groups.libgap_group import GroupLibGAP >>> G = GroupLibGAP(libgap.GL(Integer(3),Integer(3))) >>> a,b = G.gens() >>> H = a.normalizer() >>> H <group of 3x3 matrices over GF(3)> >>> H.cardinality() 96 >>> all(g*a == a*g for g in H) True
from sage.groups.libgap_group import GroupLibGAP G = GroupLibGAP(libgap.GL(3,3)) a,b = G.gens() H = a.normalizer() H H.cardinality() all(g*a == a*g for g in H)
- nth_roots(n)[source]¶
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
>>> from sage.all import * >>> from sage.groups.libgap_group import GroupLibGAP >>> G = GroupLibGAP(libgap.GL(Integer(3),Integer(3))) >>> a,b = G.gens() >>> g = a*b**Integer(2)*a*~b >>> r = g.nth_roots(Integer(4)) >>> 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) ] ]] >>> r[Integer(0)]**Integer(4) == r[Integer(1)]**Integer(4) == g True
from sage.groups.libgap_group import GroupLibGAP G = GroupLibGAP(libgap.GL(3,3)) a,b = G.gens() g = a*b**2*a*~b r = g.nth_roots(4) r r[0]**4 == r[1]**4 == g
- order()[source]¶
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
>>> from sage.all import * >>> from sage.groups.libgap_group import GroupLibGAP >>> G = GroupLibGAP(libgap.GL(Integer(2), Integer(3))) >>> a,b = G.gens() >>> print(a.order()) 2 >>> print(a.multiplicative_order()) 2 >>> z = Mod(Integer(0), Integer(3)) >>> o = Mod(Integer(1), Integer(3)) >>> G(libgap([[o,o],[z,o]])).order() 3
from sage.groups.libgap_group import GroupLibGAP G = GroupLibGAP(libgap.GL(2, 3)) a,b = G.gens() print(a.order()) print(a.multiplicative_order()) z = Mod(0, 3) o = Mod(1, 3) G(libgap([[o,o],[z,o]])).order()
- class sage.groups.libgap_wrapper.ParentLibGAP(libgap_parent, ambient=None)[source]¶
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 GAPambient
– a derived class ofParentLibGAP
orNone
(default); the ambient class iflibgap_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>
>>> from sage.all import * >>> from sage.groups.libgap_wrapper import ElementLibGAP, ParentLibGAP >>> from sage.groups.group import Group >>> class FooElement(ElementLibGAP): ... pass >>> class FooGroup(Group, ParentLibGAP): ... Element = FooElement ... def __init__(self): ... lg = libgap(libgap.CyclicGroup(Integer(3))) # dummy ... ParentLibGAP.__init__(self, lg) ... Group.__init__(self) >>> FooGroup() <pc group of size 3 with 1 generator>
from sage.groups.libgap_wrapper import ElementLibGAP, ParentLibGAP from sage.groups.group import Group class FooElement(ElementLibGAP): pass class FooGroup(Group, ParentLibGAP): Element = FooElement def __init__(self): lg = libgap(libgap.CyclicGroup(3)) # dummy ParentLibGAP.__init__(self, lg) Group.__init__(self) FooGroup()
- ambient()[source]¶
Return the ambient group of a subgroup.
OUTPUT:
A group containing
self
. Ifself
has not been defined as a subgroup, we just returnself
.EXAMPLES:
sage: G = FreeGroup(3) sage: G.ambient() is G True
>>> from sage.all import * >>> G = FreeGroup(Integer(3)) >>> G.ambient() is G True
G = FreeGroup(3) G.ambient() is G
- gap()[source]¶
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'>
>>> from sage.all import * >>> G = FreeGroup(Integer(3)); G Free Group on generators {x0, x1, x2} >>> G.gap() <free group on the generators [ x0, x1, x2 ]> >>> G.gap().parent() C library interface to GAP >>> type(G.gap()) <class 'sage.libs.gap.element.GapElement'>
G = FreeGroup(3); G G.gap() G.gap().parent() type(G.gap())
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 ]
>>> from sage.all import * >>> G = FreeGroup(Integer(3)) >>> H = G.gap() >>> H.DirectProduct(H) <fp group on the generators [ f1, f2, f3, f4, f5, f6 ]> >>> 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 ]
G = FreeGroup(3) H = G.gap() H.DirectProduct(H) H.DirectProduct(H).RelatorsOfFpGroup()
We can also convert directly to libgap:
sage: libgap(GL(2, ZZ)) GL(2,Integers)
>>> from sage.all import * >>> libgap(GL(Integer(2), ZZ)) GL(2,Integers)
libgap(GL(2, ZZ))
- gen(i)[source]¶
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) andngens()
(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
>>> from sage.all import * >>> G = FreeGroup('a, b') >>> G.gen(Integer(0)) a >>> G.gen(Integer(1)) b
G = FreeGroup('a, b') G.gen(0) G.gen(1)
- generators()[source]¶
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)
>>> from sage.all import * >>> G = FreeGroup(Integer(2)) >>> G.gens() (x0, x1) >>> H = FreeGroup('a, b, c') >>> H.gens() (a, b, c)
G = FreeGroup(2) G.gens() H = FreeGroup('a, b, c') H.gens()
generators()
is an alias forgens()
sage: G = FreeGroup('a, b') sage: G.generators() (a, b) sage: H = FreeGroup(3, 'x') sage: H.generators() (x0, x1, x2)
>>> from sage.all import * >>> G = FreeGroup('a, b') >>> G.generators() (a, b) >>> H = FreeGroup(Integer(3), 'x') >>> H.generators() (x0, x1, x2)
G = FreeGroup('a, b') G.generators() H = FreeGroup(3, 'x') H.generators()
- gens()[source]¶
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)
>>> from sage.all import * >>> G = FreeGroup(Integer(2)) >>> G.gens() (x0, x1) >>> H = FreeGroup('a, b, c') >>> H.gens() (a, b, c)
G = FreeGroup(2) G.gens() H = FreeGroup('a, b, c') H.gens()
generators()
is an alias forgens()
sage: G = FreeGroup('a, b') sage: G.generators() (a, b) sage: H = FreeGroup(3, 'x') sage: H.generators() (x0, x1, x2)
>>> from sage.all import * >>> G = FreeGroup('a, b') >>> G.generators() (a, b) >>> H = FreeGroup(Integer(3), 'x') >>> H.generators() (x0, x1, x2)
G = FreeGroup('a, b') G.generators() H = FreeGroup(3, 'x') H.generators()
- is_subgroup()[source]¶
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
>>> from sage.all import * >>> G = FreeGroup(Integer(3)) >>> G.is_subgroup() False
G = FreeGroup(3) G.is_subgroup()
- maximal_normal_subgroups()[source]¶
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]
>>> from sage.all import * >>> SL(Integer(2),GF(Integer(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]
SL(2,GF(49)).minimal_normal_subgroups()
- minimal_normal_subgroups()[source]¶
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]
>>> from sage.all import * >>> SL(Integer(2),GF(Integer(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]
SL(2,GF(49)).minimal_normal_subgroups()
- ngens()[source]¶
Return the number of generators of
self
.OUTPUT: integer
EXAMPLES:
sage: G = FreeGroup(2) sage: G.ngens() 2
>>> from sage.all import * >>> G = FreeGroup(Integer(2)) >>> G.ngens() 2
G = FreeGroup(2) G.ngens()
- one()[source]¶
Return the identity element of
self
.EXAMPLES:
sage: G = FreeGroup(3) sage: G.one() 1 sage: G.one() == G([]) True sage: G.one().Tietze() ()
>>> from sage.all import * >>> G = FreeGroup(Integer(3)) >>> G.one() 1 >>> G.one() == G([]) True >>> G.one().Tietze() ()
G = FreeGroup(3) G.one() G.one() == G([]) G.one().Tietze()
- subgroup(generators)[source]¶
Return the subgroup generated.
INPUT:
generators
– 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,)
>>> from sage.all import * >>> F = FreeGroup(names=('a', 'b',)); (a, b,) = F._first_ngens(2) >>> G = F.subgroup([a**Integer(2)*b]); G Group([ a^2*b ]) >>> G.gens() (a^2*b,)
F.<a,b> = FreeGroup() G = F.subgroup([a^2*b]); G G.gens()
We check that coercions between the subgroup and its ambient group work:
sage: F.0 * G.0 a^3*b
>>> from sage.all import * >>> F.gen(0) * G.gen(0) a^3*b
F.0 * G.0
Checking that 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
>>> from sage.all import * >>> gens = [w.matrix() for w in WeylGroup(['B', Integer(3)])] >>> G = MatrixGroup(gens) >>> import itertools >>> diagonals = itertools.product((Integer(1),-Integer(1)), repeat=Integer(3)) >>> subgroup_gens = [diagonal_matrix(L) for L in diagonals] >>> G.subgroup(subgroup_gens) Subgroup with 8 generators of Matrix group over Rational Field with 48 generators
gens = [w.matrix() for w in WeylGroup(['B', 3])] G = MatrixGroup(gens) import itertools diagonals = itertools.product((1,-1), repeat=3) subgroup_gens = [diagonal_matrix(L) for L in diagonals] G.subgroup(subgroup_gens)