Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/groups/libgap_wrapper.pyx
8814 views
1
"""
2
LibGAP-based Groups
3
4
This module provides helper class for wrapping GAP groups via
5
:mod:`~sage.libs.gap.libgap`. See :mod:`~sage.groups.free_group` for an
6
example how they are used.
7
8
The parent class keeps track of the libGAP element object, to use it
9
in your Python parent you have to derive both from the suitable group
10
parent and :class:`ParentLibGAP` ::
11
12
sage: from sage.groups.libgap_wrapper import ElementLibGAP, ParentLibGAP
13
sage: from sage.groups.group import Group
14
sage: class FooElement(ElementLibGAP):
15
... pass
16
sage: class FooGroup(Group, ParentLibGAP):
17
... Element = FooElement
18
... def __init__(self):
19
... lg = libgap(libgap.CyclicGroup(3)) # dummy
20
... ParentLibGAP.__init__(self, lg)
21
... Group.__init__(self)
22
23
Note how we call the constructor of both superclasses to initialize
24
``Group`` and ``ParentLibGAP`` separately. The parent class implements
25
its output via LibGAP::
26
27
sage: FooGroup()
28
<pc group of size 3 with 1 generators>
29
sage: type(FooGroup().gap())
30
<type 'sage.libs.gap.element.GapElement'>
31
32
The element class is a subclass of
33
:class:`~sage.structure.element.MultiplicativeGroupElement`. To use
34
it, you just inherit from :class:`ElementLibGAP` ::
35
36
sage: element = FooGroup().an_element()
37
sage: element
38
f1
39
40
The element class implements group operations and printing via LibGAP::
41
42
sage: element._repr_()
43
'f1'
44
sage: element * element
45
f1^2
46
47
AUTHORS:
48
49
- Volker Braun
50
"""
51
52
##############################################################################
53
# Copyright (C) 2012 Volker Braun <[email protected]>
54
#
55
# Distributed under the terms of the GNU General Public License (GPL)
56
#
57
# The full text of the GPL is available at:
58
#
59
# http://www.gnu.org/licenses/
60
##############################################################################
61
62
from sage.libs.gap.element cimport GapElement
63
from sage.rings.integer import Integer
64
from sage.rings.integer_ring import IntegerRing
65
from sage.misc.cachefunc import cached_method
66
from sage.structure.sage_object import SageObject
67
from sage.structure.element cimport Element
68
69
70
class ParentLibGAP(SageObject):
71
"""
72
A class for parents to keep track of the GAP parent.
73
74
This is not a complete group in Sage, this class is only a base
75
class that you can use to implement your own groups with
76
LibGAP. See :mod:`~sage.groups.libgap_group` for a minimal example
77
of a group that is actually usable.
78
79
Your implementation definitely needs to supply
80
81
* ``__reduce__()``: serialize the LibGAP group. Since GAP does not
82
support Python pickles natively, you need to figure out yourself
83
how you can recreate the group from a pickle.
84
85
INPUT:
86
87
- ``libgap_parent`` -- the libgap element that is the parent in
88
GAP.
89
90
- ``ambient`` -- A derived class of :class:`ParentLibGAP` or
91
``None`` (default). The ambient class if ``libgap_parent`` has
92
been defined as a subgroup.
93
94
EXAMPLES::
95
96
sage: from sage.groups.libgap_wrapper import ElementLibGAP, ParentLibGAP
97
sage: from sage.groups.group import Group
98
sage: class FooElement(ElementLibGAP):
99
... pass
100
sage: class FooGroup(Group, ParentLibGAP):
101
... Element = FooElement
102
... def __init__(self):
103
... lg = libgap(libgap.CyclicGroup(3)) # dummy
104
... ParentLibGAP.__init__(self, lg)
105
... Group.__init__(self)
106
sage: FooGroup()
107
<pc group of size 3 with 1 generators>
108
"""
109
110
def __init__(self, libgap_parent, ambient=None):
111
"""
112
The Python constructor.
113
114
TESTS::
115
116
sage: G = FreeGroup(3)
117
sage: TestSuite(G).run()
118
"""
119
assert isinstance(libgap_parent, GapElement)
120
self._libgap = libgap_parent
121
self._ambient = ambient
122
123
def ambient(self):
124
"""
125
Return the ambient group of a subgroup.
126
127
OUTPUT:
128
129
A group containing ``self``. If ``self`` has not been defined
130
as a subgroup, we just return ``self``.
131
132
EXAMPLES::
133
134
sage: G = FreeGroup(3)
135
sage: G.ambient() is G
136
True
137
"""
138
if self._ambient is None:
139
return self
140
else:
141
return self._ambient
142
143
def is_subgroup(self):
144
"""
145
Return whether the group was defined as a subgroup of a bigger
146
group.
147
148
You can access the contaning group with :meth:`ambient`.
149
150
OUTPUT:
151
152
Boolean.
153
154
EXAMPLES::
155
156
sage: G = FreeGroup(3)
157
sage: G.is_subgroup()
158
False
159
"""
160
return self._ambient is not None
161
162
def _subgroup_constructor(self, libgap_subgroup):
163
"""
164
Return the class of a subgroup.
165
166
You should override this with a derived class. Its constructor
167
must accept the same arguments as :meth:`__init__`.
168
169
OUTPUT:
170
171
A new instance of a group (derived class of
172
:class:`ParentLibGAP`).
173
174
TESTS::
175
176
sage: F.<a,b> = FreeGroup()
177
sage: G = F.subgroup([a^2*b]); G
178
Group([ a^2*b ])
179
sage: F._subgroup_constructor(G.gap())._repr_()
180
'Group([ a^2*b ])'
181
"""
182
from sage.groups.libgap_group import GroupLibGAP
183
return GroupLibGAP(libgap_subgroup, ambient=self)
184
185
def subgroup(self, generators):
186
"""
187
Return the subgroup generated.
188
189
INPUT:
190
191
- ``generators`` -- a list/tuple/iterable of group elements.
192
193
OUTPUT:
194
195
The subgroup generated by ``generators``.
196
197
EXAMPLES::
198
199
sage: F.<a,b> = FreeGroup()
200
sage: G = F.subgroup([a^2*b]); G
201
Group([ a^2*b ])
202
sage: G.gens()
203
(a^2*b,)
204
"""
205
generators = [ g if isinstance(g, GapElement) else g.gap()
206
for g in generators ]
207
G = self.gap()
208
H = G.Subgroup(generators)
209
return self._subgroup_constructor(H)
210
211
def gap(self):
212
"""
213
Returns the gap representation of self
214
215
OUTPUT:
216
217
A :class:`~sage.libs.gap.element.GapElement`
218
219
EXAMPLES::
220
221
sage: G = FreeGroup(3); G
222
Free Group on generators {x0, x1, x2}
223
sage: G.gap()
224
<free group on the generators [ x0, x1, x2 ]>
225
sage: G.gap().parent()
226
C library interface to GAP
227
sage: type(G.gap())
228
<type 'sage.libs.gap.element.GapElement'>
229
230
This can be useful, for example, to call GAP functions that
231
are not wrapped in Sage::
232
233
sage: G = FreeGroup(3)
234
sage: H = G.gap()
235
sage: H.DirectProduct(H)
236
<fp group on the generators [ f1, f2, f3, f4, f5, f6 ]>
237
sage: H.DirectProduct(H).RelatorsOfFpGroup()
238
[ f1^-1*f4^-1*f1*f4, f1^-1*f5^-1*f1*f5, f1^-1*f6^-1*f1*f6, f2^-1*f4^-1*f2*f4,
239
f2^-1*f5^-1*f2*f5, f2^-1*f6^-1*f2*f6, f3^-1*f4^-1*f3*f4, f3^-1*f5^-1*f3*f5,
240
f3^-1*f6^-1*f3*f6 ]
241
"""
242
return self._libgap
243
244
_gap_ = gap
245
246
@cached_method
247
def _gap_gens(self):
248
"""
249
Return the generators as a LibGAP object
250
251
OUTPUT:
252
253
A :class:`~sage.libs.gap.element.GapElement`
254
255
EXAMPLES:
256
257
sage: G = FreeGroup(2)
258
sage: G._gap_gens()
259
[ x0, x1 ]
260
sage: type(_)
261
<type 'sage.libs.gap.element.GapElement_List'>
262
"""
263
return self._libgap.GeneratorsOfGroup()
264
265
@cached_method
266
def ngens(self):
267
"""
268
Return the number of generators of self.
269
270
OUTPUT:
271
272
Integer.
273
274
EXAMPLES::
275
276
sage: G = FreeGroup(2)
277
sage: G.ngens()
278
2
279
280
TESTS::
281
282
sage: type(G.ngens())
283
<type 'sage.rings.integer.Integer'>
284
"""
285
return self._gap_gens().Length().sage()
286
287
def _repr_(self):
288
"""
289
Return a string representation
290
291
OUTPUT:
292
293
String.
294
295
TESTS::
296
297
sage: from sage.groups.libgap_wrapper import ElementLibGAP, ParentLibGAP
298
sage: G.<a,b> =FreeGroup()
299
sage: ParentLibGAP._repr_(G)
300
'<free group on the generators [ a, b ]>'
301
"""
302
return self._libgap._repr_()
303
304
def gen(self, i):
305
"""
306
Return the `i`-th generator of self.
307
308
.. warning::
309
310
Indexing starts at `0` as usual in Sage/Python. Not as in
311
GAP, where indexing starts at `1`.
312
313
INPUT:
314
315
- ``i`` -- integer between `0` (inclusive) and :meth:`ngens`
316
(exclusive). The index of the generator.
317
318
OUTPUT:
319
320
The `i`-th generator of the group.
321
322
EXAMPLES::
323
324
sage: G = FreeGroup('a, b')
325
sage: G.gen(0)
326
a
327
sage: G.gen(1)
328
b
329
"""
330
if not (0 <= i < self.ngens()):
331
raise ValueError('i must be in range(ngens)')
332
gap = self._gap_gens()[i]
333
return self.element_class(self, gap)
334
335
@cached_method
336
def gens(self):
337
"""
338
Returns the generators of the group.
339
340
EXAMPLES::
341
342
sage: G = FreeGroup(2)
343
sage: G.gens()
344
(x0, x1)
345
sage: H = FreeGroup('a, b, c')
346
sage: H.gens()
347
(a, b, c)
348
349
:meth:`generators` is an alias for :meth:`gens` ::
350
351
sage: G = FreeGroup('a, b')
352
sage: G.generators()
353
(a, b)
354
sage: H = FreeGroup(3, 'x')
355
sage: H.generators()
356
(x0, x1, x2)
357
"""
358
return tuple( self.gen(i) for i in range(self.ngens()) )
359
360
generators = gens
361
362
@cached_method
363
def one(self):
364
"""
365
Returns the identity element of self
366
367
EXAMPLES::
368
369
sage: G = FreeGroup(3)
370
sage: G.one()
371
1
372
sage: G.one() == G([])
373
True
374
sage: G.one().Tietze()
375
()
376
"""
377
return self.element_class(self, self.gap().Identity())
378
379
def _an_element_(self):
380
"""
381
Returns an element of self.
382
383
EXAMPLES::
384
385
sage: G.<a,b> = FreeGroup()
386
sage: G._an_element_()
387
a*b
388
"""
389
from sage.misc.all import prod
390
return prod(self.gens())
391
392
393
394
cdef class ElementLibGAP(MultiplicativeGroupElement):
395
"""
396
A class for LibGAP-based Sage group elements
397
398
INPUT:
399
400
- ``parent`` -- the Sage parent
401
402
- ``libgap_element`` -- the libgap element that is being wrapped
403
404
EXAMPLES::
405
406
sage: from sage.groups.libgap_wrapper import ElementLibGAP, ParentLibGAP
407
sage: from sage.groups.group import Group
408
sage: class FooElement(ElementLibGAP):
409
... pass
410
sage: class FooGroup(Group, ParentLibGAP):
411
... Element = FooElement
412
... def __init__(self):
413
... lg = libgap(libgap.CyclicGroup(3)) # dummy
414
... ParentLibGAP.__init__(self, lg)
415
... Group.__init__(self)
416
sage: FooGroup()
417
<pc group of size 3 with 1 generators>
418
sage: FooGroup().gens()
419
(f1,)
420
"""
421
422
def __init__(self, parent, libgap_element):
423
"""
424
The Python constructor
425
426
TESTS::
427
428
sage: G = FreeGroup(2)
429
sage: g = G.an_element()
430
sage: TestSuite(g).run()
431
"""
432
MultiplicativeGroupElement.__init__(self, parent)
433
assert isinstance(parent, ParentLibGAP)
434
if isinstance(libgap_element, GapElement):
435
self._libgap = libgap_element
436
else:
437
if libgap_element == 1:
438
self._libgap = self.parent().gap().Identity()
439
else:
440
raise TypeError('need a libgap group element or "1" in constructor')
441
442
cpdef GapElement gap(self):
443
"""
444
Returns a LibGAP representation of the element
445
446
OUTPUT:
447
448
A :class:`~sage.libs.gap.element.GapElement`
449
450
EXAMPLES::
451
452
sage: G.<a,b> = FreeGroup('a, b')
453
sage: x = G([1, 2, -1, -2])
454
sage: x
455
a*b*a^-1*b^-1
456
sage: xg = x.gap()
457
sage: xg
458
a*b*a^-1*b^-1
459
sage: type(xg)
460
<type 'sage.libs.gap.element.GapElement'>
461
"""
462
return self._libgap
463
464
_gap_ = gap
465
466
def is_one(self):
467
"""
468
Test whether the group element is the trivial element.
469
470
OUTPUT:
471
472
Boolean.
473
474
EXAMPLES::
475
476
sage: G.<a,b> = FreeGroup('a, b')
477
sage: x = G([1, 2, -1, -2])
478
sage: x.is_one()
479
False
480
sage: (x * ~x).is_one()
481
True
482
"""
483
return self == self.parent().one()
484
485
def _repr_(self):
486
"""
487
Return a string representation.
488
489
OUTPUT:
490
491
String.
492
493
EXAMPLES::
494
495
sage: G.<a,b> = FreeGroup()
496
sage: a._repr_()
497
'a'
498
sage: type(a)
499
<class 'sage.groups.free_group.FreeGroup_class_with_category.element_class'>
500
501
sage: x = G([1, 2, -1, -2])
502
sage: x._repr_()
503
'a*b*a^-1*b^-1'
504
sage: y = G([2, 2, 2, 1, -2, -2, -2])
505
sage: y._repr_()
506
'b^3*a*b^-3'
507
508
sage: G.one()
509
1
510
"""
511
if self.is_one():
512
return '1'
513
else:
514
return self._libgap._repr_()
515
516
def _latex_(self):
517
"""
518
Return a LaTeX representation
519
520
OUTPUT:
521
522
String. A valid LaTeX math command sequence.
523
524
EXAMPLES::
525
526
sage: from sage.groups.libgap_group import GroupLibGAP
527
sage: G = GroupLibGAP(libgap.FreeGroup('a', 'b'))
528
sage: g = G.gen(0) * G.gen(1)
529
sage: g._latex_()
530
"ab%\n"
531
"""
532
try:
533
return self.gap().LaTeX()
534
except ValueError:
535
from sage.misc.latex import latex
536
return latex(self._repr_())
537
538
cpdef MonoidElement _mul_(left, MonoidElement right):
539
"""
540
Multiplication of group elements
541
542
TESTS::
543
544
sage: G = FreeGroup('a, b')
545
sage: x = G([1, 2, -1, -2])
546
sage: y = G([2, 2, 2, 1, -2, -2, -2])
547
sage: x*y # indirect doctest
548
a*b*a^-1*b^2*a*b^-3
549
sage: y*x # indirect doctest
550
b^3*a*b^-3*a*b*a^-1*b^-1
551
sage: x*y == x._mul_(y)
552
True
553
sage: y*x == y._mul_(x)
554
True
555
"""
556
P = left.parent()
557
return P.element_class(P, left.gap() * right.gap())
558
559
cdef int _cmp_c_impl(left, Element right):
560
"""
561
This method implements comparison.
562
563
TESTS::
564
565
sage: G.<a,b> = FreeGroup('a, b')
566
sage: x = G([1, 2, -1, -2])
567
sage: y = G([2, 2, 2, 1, -2, -2, -2])
568
sage: x == x*y*y^(-1) # indirect doctest
569
True
570
sage: cmp(x,y)
571
-1
572
sage: x < y
573
True
574
"""
575
return cmp((<ElementLibGAP>left)._libgap,
576
(<ElementLibGAP>right)._libgap)
577
578
def __richcmp__(left, right, int op):
579
"""
580
Boilerplate for Cython elements
581
582
See :mod:`~sage.structure.element` for details.
583
584
EXAMPLES::
585
586
sage: G.<a,b> = FreeGroup('a, b')
587
sage: G_gap = G.gap()
588
sage: G_gap == G_gap # indirect doctest
589
True
590
"""
591
return (<Element>left)._richcmp(right, op)
592
593
def __cmp__(left, right):
594
"""
595
Boilerplate for Cython elements
596
597
See :mod:`~sage.structure.element` for details.
598
599
EXAMPLES::
600
601
sage: G.<a,b> = FreeGroup('a, b')
602
sage: cmp(G.gap(), G.gap()) # indirect doctest
603
0
604
"""
605
return (<Element>left)._cmp(right)
606
607
cpdef MultiplicativeGroupElement _div_(left, MultiplicativeGroupElement right):
608
"""
609
Division of group elements.
610
611
TESTS::
612
613
sage: G = FreeGroup('a, b')
614
sage: x = G([1, 2, -1, -2])
615
sage: y = G([2, 2, 2, 1, -2, -2, -2])
616
sage: x/y # indirect doctest
617
a*b*a^-1*b^2*a^-1*b^-3
618
sage: y/x # indirect doctest
619
b^3*a*b^-2*a*b^-1*a^-1
620
sage: x/y == x.__div__(y)
621
True
622
sage: x/y == y.__div__(x)
623
False
624
"""
625
P = left.parent()
626
return P.element_class(P, left.gap() / right.gap())
627
628
def __pow__(self, n, dummy):
629
"""
630
Implement exponentiation.
631
632
TESTS::
633
634
sage: G = FreeGroup('a, b')
635
sage: x = G([1, 2, -1, -2])
636
sage: y = G([2, 2, 2, 1, -2, -2, -2])
637
sage: y^(2) # indirect doctest
638
b^3*a^2*b^-3
639
sage: x^(-3) # indirect doctest
640
(b*a*b^-1*a^-1)^3
641
sage: y^3 == y.__pow__(3)
642
True
643
"""
644
if n not in IntegerRing():
645
raise TypeError("exponent must be an integer")
646
P = self.parent()
647
return P.element_class(P, self.gap().__pow__(n))
648
649
def __invert__(self):
650
"""
651
Return the inverse of self.
652
653
TESTS::
654
655
sage: G = FreeGroup('a, b')
656
sage: x = G([1, 2, -1, -2])
657
sage: y = G([2, 2, 2, 1, -2, -2, -2])
658
sage: x.__invert__()
659
b*a*b^-1*a^-1
660
sage: y.__invert__()
661
b^3*a^-1*b^-3
662
sage: ~x
663
b*a*b^-1*a^-1
664
sage: x.inverse()
665
b*a*b^-1*a^-1
666
"""
667
P = self.parent()
668
return P.element_class(P, self.gap().Inverse())
669
670
inverse = __invert__
671
672
673