Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/groups/abelian_gps/abelian_group.py
8815 views
1
r"""
2
Multiplicative Abelian Groups
3
4
This module lets you compute with finitely generated Abelian groups of the form
5
6
.. math::
7
8
G = \ZZ^r \oplus \ZZ_{k_1} \oplus \cdots \oplus \ZZ_{k_t}
9
10
It is customary to denote the infinite cyclic group `\ZZ` as having
11
order `0`, so the data defining the Abelian group can be written as an
12
integer vector
13
14
.. math::
15
16
\vec{k} = (0, \dots, 0, k_1, \dots, k_t)
17
18
where there are `r` zeroes and `t` non-zero values. To construct this
19
Abelian group in Sage, you can either specify all entries of `\vec{k}`
20
or only the non-zero entries together with the total number of
21
generators::
22
23
sage: AbelianGroup([0,0,0,2,3])
24
Multiplicative Abelian group isomorphic to Z x Z x Z x C2 x C3
25
sage: AbelianGroup(5, [2,3])
26
Multiplicative Abelian group isomorphic to Z x Z x Z x C2 x C3
27
28
It is also legal to specify `1` as the order. The corresponding
29
generator will be the neutral element, but it will still take up an
30
index in the labelling of the generators::
31
32
sage: G = AbelianGroup([2,1,3], names='g')
33
sage: G.gens()
34
(g0, 1, g2)
35
36
Note that this presentation is not unique, for example `\ZZ_6 = \ZZ_2
37
\times \ZZ_3`. The orders of the generators
38
`\vec{k}=(0,\dots,0,k_1,\dots, k_t)` has previously been called
39
invariants in Sage, even though they are not necessarily the (unique)
40
invariant factors of the group. You should now use
41
:meth:`~AbelianGroup_class.gens_orders` instead::
42
43
sage: J = AbelianGroup([2,0,3,2,4]); J
44
Multiplicative Abelian group isomorphic to C2 x Z x C3 x C2 x C4
45
sage: J.gens_orders() # use this instead
46
(2, 0, 3, 2, 4)
47
sage: J.invariants() # deprecated
48
(2, 0, 3, 2, 4)
49
sage: J.elementary_divisors() # these are the "invariant factors"
50
(2, 2, 12, 0)
51
sage: for i in range(J.ngens()):
52
... print i, J.gen(i), J.gen(i).order() # or use this form
53
0 f0 2
54
1 f1 +Infinity
55
2 f2 3
56
3 f3 2
57
4 f4 4
58
59
Background on invariant factors and the Smith normal form
60
(according to section 4.1 of [C1]): An abelian group is a
61
group A for which there exists an exact sequence
62
`\ZZ^k \rightarrow \ZZ^\ell \rightarrow A \rightarrow 1`,
63
for some positive integers
64
`k,\ell` with `k\leq \ell`. For example, a finite abelian group has a
65
decomposition
66
67
.. math::
68
69
A = \langle a_1\rangle \times \dots \times \langle a_\ell\rangle ,
70
71
where `ord(a_i)=p_i^{c_i}`, for some primes `p_i` and some
72
positive integers `c_i`, `i=1,...,\ell`. GAP calls the
73
list (ordered by size) of the `p_i^{c_i}` the *abelian invariants*.
74
In Sage they will be called *invariants*.
75
In this situation, `k=\ell` and `\phi: \ZZ^\ell \rightarrow A` is the map
76
`\phi(x_1,...,x_\ell) = a_1^{x_1}...a_\ell^{x_\ell}`,
77
for `(x_1,...,x_\ell)\in \ZZ^\ell`. The matrix of relations
78
`M:\ZZ^k \rightarrow \ZZ^\ell` is the matrix
79
whose rows generate the kernel of `\phi` as a `\ZZ`-module.
80
In other words, `M=(M_{ij})` is a `\ell\times \ell`
81
diagonal matrix with `M_{ii}=p_i^{c_i}`. Consider now the
82
subgroup `B\subset A` generated by
83
`b_1 = a_1^{f_{1,1}}...a_\ell^{f_{\ell,1}}`, ...,
84
`b_m = a_1^{f_{1,m}}...a_\ell^{f_{\ell,m}}`.
85
The kernel of the map `\phi_B: \ZZ^m \rightarrow B` defined by
86
`\phi_B(y_1,...,y_m) = b_1^{y_1}...b_m^{y_m}`,
87
for `(y_1,...,y_m)\in \ZZ^m`, is the kernel of the matrix
88
89
.. math::
90
91
F=
92
\left(
93
\begin{array}{cccc}
94
f_{11} & f_{12} & \dots & f_{1m}\\
95
f_{21} & f_{22} & \dots & f_{2m}\\
96
\vdots & & \ddots & \vdots \\
97
f_{\ell,1} & f_{\ell,2} & \dots & f_{\ell,m}
98
\end{array}
99
\right),
100
101
regarded as a map
102
`\ZZ^m\rightarrow (\ZZ/p_1^{c_1}\ZZ)\times ...\times (\ZZ/p_\ell^{c_\ell}\ZZ)`.
103
In particular, `B\cong \ZZ^m/ker(F)`. If `B=A` then the
104
Smith normal form (SNF) of a generator matrix of
105
`ker(F)` and the SNF of `M` are the same. The diagonal entries `s_i` of the
106
SNF `S = diag[s_1,s_2,s_3, ... s_r,0,0,...0]`,
107
are called *determinantal divisors* of `F`.
108
where `r` is the rank. The {\it invariant factors} of A are:
109
110
.. math::
111
112
s_1, s_2/s_1, s_3/s_2, ... s_r/s_{r-1}.
113
114
Sage supports multiplicative abelian groups on any prescribed finite
115
number `n \geq 0` of generators. Use the :func:`AbelianGroup`
116
function to create an abelian group, and the
117
:meth:`~AbelianGroup_class.gen` and :meth:`~AbelianGroup_class.gens`
118
methods to obtain the corresponding generators. You can print the
119
generators as arbitrary strings using the optional ``names`` argument
120
to the :func:`AbelianGroup` function.
121
122
EXAMPLE 1:
123
124
We create an abelian group in zero or more variables; the syntax T(1)
125
creates the identity element even in the rank zero case::
126
127
sage: T = AbelianGroup(0,[])
128
sage: T
129
Trivial Abelian group
130
sage: T.gens()
131
()
132
sage: T(1)
133
1
134
135
EXAMPLE 2:
136
137
An Abelian group uses a multiplicative representation of elements, but
138
the underlying representation is lists of integer exponents::
139
140
sage: F = AbelianGroup(5,[3,4,5,5,7],names = list("abcde"))
141
sage: F
142
Multiplicative Abelian group isomorphic to C3 x C4 x C5 x C5 x C7
143
sage: (a,b,c,d,e) = F.gens()
144
sage: a*b^2*e*d
145
a*b^2*d*e
146
sage: x = b^2*e*d*a^7
147
sage: x
148
a*b^2*d*e
149
sage: x.list()
150
[1, 2, 0, 1, 1]
151
152
REFERENCES:
153
154
- [C1] H. Cohen Advanced topics in computational number theory,
155
Springer, 2000.
156
157
- [C2] ----, A course in computational algebraic number theory,
158
Springer, 1996.
159
160
- [R] J. Rotman, An introduction to the theory of
161
groups, 4th ed, Springer, 1995.
162
163
.. warning::
164
165
Many basic properties for infinite abelian groups are not
166
implemented.
167
168
169
AUTHORS:
170
171
- William Stein, David Joyner (2008-12): added (user requested) is_cyclic,
172
fixed elementary_divisors.
173
174
- David Joyner (2006-03): (based on free abelian monoids by David
175
Kohel)
176
177
- David Joyner (2006-05) several significant bug fixes
178
179
- David Joyner (2006-08) trivial changes to docs, added random, fixed
180
bug in how invariants are recorded
181
182
- David Joyner (2006-10) added dual_group method
183
184
- David Joyner (2008-02) fixed serious bug in word_problem
185
186
- David Joyner (2008-03) fixed bug in trivial group case
187
188
- David Loeffler (2009-05) added subgroups method
189
190
- Volker Braun (2012-11) port to new Parent base. Use tuples for
191
immutables. Rename invariants to gens_orders.
192
"""
193
194
##########################################################################
195
# Copyright (C) 2006 William Stein <[email protected]>
196
# Copyright (C) 2006 David Joyner <[email protected]>
197
# Copyright (C) 2012 Volker Braun <[email protected]>
198
#
199
# Distributed under the terms of the GNU General Public License (GPL):
200
#
201
# http://www.gnu.org/licenses/
202
##########################################################################
203
204
205
from sage.rings.integer import Integer
206
from sage.rings.integer_ring import ZZ
207
from sage.structure.unique_representation import UniqueRepresentation
208
from sage.rings.infinity import infinity
209
from sage.rings.arith import divisors, gcd
210
from sage.groups.abelian_gps.abelian_group_element import AbelianGroupElement
211
from sage.misc.cachefunc import cached_method
212
from sage.misc.misc import prod
213
from sage.misc.mrange import mrange, cartesian_product_iterator
214
from sage.rings.arith import lcm
215
from sage.groups.group import AbelianGroup as AbelianGroupBase
216
217
218
# TODO: this uses perm groups - the AbelianGroupElement instance method
219
# uses a different implementation.
220
def word_problem(words, g, verbose = False):
221
r"""
222
G and H are abelian, g in G, H is a subgroup of G generated by a
223
list (words) of elements of G. If g is in H, return the expression
224
for g as a word in the elements of (words).
225
226
The 'word problem' for a finite abelian group G boils down to the
227
following matrix-vector analog of the Chinese remainder theorem.
228
229
Problem: Fix integers `1<n_1\leq n_2\leq ...\leq n_k`
230
(indeed, these `n_i` will all be prime powers), fix a
231
generating set `g_i=(a_{i1},...,a_{ik})` (with
232
`a_{ij}\in \mathrm{Z}/n_j\mathrm{Z}`), for `1\leq i\leq \ell`,
233
for the group `G`, and let `d = (d_1,...,d_k)` be
234
an element of the direct product
235
`\mathrm{Z}/n_1\mathrm{Z} \times ...\times \mathrm{Z}/n_k\mathrm{Z}`. Find, if they
236
exist, integers `c_1,...,c_\ell` such that
237
`c_1g_1+...+c_\ell g_\ell = d`. In other words, solve
238
the equation `cA=d` for `c\in \mathrm{Z}^\ell`, where
239
`A` is the matrix whose rows are the `g_i`'s. Of
240
course, it suffices to restrict the `c_i`'s to the range
241
`0\leq c_i\leq N-1`, where `N` denotes the least
242
common multiple of the integers `n_1,...,n_k`.
243
244
This function does not solve this directly, as perhaps it should.
245
Rather (for both speed and as a model for a similar function valid
246
for more general groups), it pushes it over to GAP, which has optimized
247
(non-deterministic) algorithms for the word problem. Essentially,
248
this function is a wrapper for the GAP function 'Factorization'.
249
250
EXAMPLE::
251
252
sage: G.<a,b,c> = AbelianGroup(3,[2,3,4]); G
253
Multiplicative Abelian group isomorphic to C2 x C3 x C4
254
sage: w = word_problem([a*b,a*c], b*c); w #random
255
[[a*b, 1], [a*c, 1]]
256
sage: prod([x^i for x,i in w]) == b*c
257
True
258
sage: w = word_problem([a*c,c],a); w #random
259
[[a*c, 1], [c, -1]]
260
sage: prod([x^i for x,i in w]) == a
261
True
262
sage: word_problem([a*c,c],a,verbose=True) #random
263
a = (a*c)^1*(c)^-1
264
[[a*c, 1], [c, -1]]
265
266
::
267
268
sage: A.<a,b,c,d,e> = AbelianGroup(5,[4, 5, 5, 7, 8])
269
sage: b1 = a^3*b*c*d^2*e^5
270
sage: b2 = a^2*b*c^2*d^3*e^3
271
sage: b3 = a^7*b^3*c^5*d^4*e^4
272
sage: b4 = a^3*b^2*c^2*d^3*e^5
273
sage: b5 = a^2*b^4*c^2*d^4*e^5
274
sage: w = word_problem([b1,b2,b3,b4,b5],e); w #random
275
[[a^3*b*c*d^2*e^5, 1], [a^2*b*c^2*d^3*e^3, 1], [a^3*b^3*d^4*e^4, 3], [a^2*b^4*c^2*d^4*e^5, 1]]
276
sage: prod([x^i for x,i in w]) == e
277
True
278
sage: word_problem([a,b,c,d,e],e)
279
[[e, 1]]
280
sage: word_problem([a,b,c,d,e],b)
281
[[b, 1]]
282
283
.. warning::
284
285
1. Might have unpleasant effect when the word problem
286
cannot be solved.
287
288
2. Uses permutation groups, so may be slow when group is large.
289
The instance method word_problem of the class
290
AbelianGroupElement is implemented differently (wrapping
291
GAP's 'EpimorphismFromFreeGroup' and
292
'PreImagesRepresentative') and may be faster.
293
"""
294
from sage.interfaces.all import gap
295
G = g.parent()
296
invs = map(str, G.gens_orders())
297
gap.eval("l:=One(Rationals)")
298
s1 = 'A:=AbelianGroup([' + ','.join(invs) + '])'
299
gap.eval(s1)
300
s2 = 'phi:=IsomorphismPermGroup(A)'
301
gap.eval(s2)
302
s3 = "gens := GeneratorsOfGroup(A)"
303
gap.eval(s3)
304
L = g.list()
305
gap.eval("L1:="+str(L))
306
s4 = "L2:=List([l..%s], i->gens[i]^L1[i]);"%len(L)
307
gap.eval(s4)
308
gap.eval("g:=Product(L2); gensH:=[]")
309
for w in words:
310
L = w.list()
311
gap.eval("L1:="+str(L))
312
s4 = "L2:=List([1..%s], i->gens[i]^L1[i]);"%len(L)
313
gap.eval(s4)
314
gap.eval("w:=Product(L2)")
315
gap.eval("gensH:=Concatenation(gensH,[w])")
316
s5 = 'H:=Group(gensH)'
317
gap.eval(s5)
318
gap.eval("x:=Factorization(H,g)")
319
l3 = eval(gap.eval("L3:=ExtRepOfObj(x)"))
320
nn = gap.eval("n:=Int(Length(L3)/2)")
321
LL = eval(gap.eval("L4:=List([l..n],i->L3[2*i])"))
322
if verbose:
323
#print gap.eval("x"), l3, nn, LL
324
v = '*'.join(['(%s)^%s'%(words[l3[2*i]-1], LL[i]) for i in range(len(LL))])
325
print '%s = %s'%(g, v)
326
return [[words[l3[2*i]-1],LL[i]] for i in range(len(LL))]
327
328
329
def _normalize(n, gens_orders=None, names="f"):
330
"""
331
Helper function for :func:`AbelianGroup`. Beat some sense into the
332
arguments.
333
334
This function is also used by some descendents of
335
:class:`AbelianGroup_class`.
336
337
INPUT:
338
339
See :func:`AbelianGroup`
340
341
OUTPUT:
342
343
Unique data for defining a :class:`AbelianGroup_class`. Raises an
344
exception if the input is invalid.
345
346
EXAMPLES::
347
348
sage: from sage.groups.abelian_gps.abelian_group import _normalize
349
sage: _normalize(5, [2,1,0], names='abc')
350
((0, 0, 2, 1, 0), 'abc')
351
sage: _normalize(5, (2.0, 1.0, 0/1), names=list('abc'))
352
((0, 0, 2, 1, 0), ('a', 'b', 'c'))
353
sage: _normalize([0,2,1,0], names='a')
354
((0, 2, 1, 0), 'a')
355
356
TESTS::
357
358
sage: _normalize(5, (2.0, 1.5, 0/1), names=list('abc'))
359
Traceback (most recent call last):
360
...
361
TypeError: Attempt to coerce non-integral RealNumber to Integer
362
sage: _normalize('1', '[2]', names='a')
363
Traceback (most recent call last):
364
...
365
TypeError: unable to convert x (=[) to an integer
366
sage: _normalize(3, 'str', names='a')
367
Traceback (most recent call last):
368
...
369
TypeError: unable to convert x (=s) to an integer
370
"""
371
if gens_orders is None:
372
if isinstance(n, (list, tuple)):
373
gens_orders = n
374
n = len(n)
375
else:
376
gens_orders = []
377
n = ZZ(n)
378
if len(gens_orders) < n:
379
gens_orders = [0] * (n - len(gens_orders)) + list(gens_orders)
380
gens_orders = tuple(ZZ(i) for i in gens_orders)
381
if len(gens_orders) > n:
382
raise ValueError('gens_orders (='+str(gens_orders)+') must have length n (='+str(n)+')')
383
if isinstance(names, list):
384
names = tuple(names)
385
return (gens_orders, names)
386
387
def AbelianGroup(n, gens_orders=None, names="f"):
388
r"""
389
Create the multiplicative abelian group in `n` generators
390
with given orders of generators (which need not be prime powers).
391
392
INPUT:
393
394
- ``n`` -- integer (optional). If not specified, will be derived
395
from ``gens_orders``.
396
397
- ``gens_orders`` -- a list of non-negative integers in the form
398
`[a_0, a_1, \dots, a_{n-1}]`, typically written in increasing
399
order. This list is padded with zeros if it has length less
400
than n. The orders of the commuting generators, with `0`
401
denoting an infinite cyclic factor.
402
403
- ``names`` -- (optional) names of generators
404
405
Alternatively, you can also give input in the form
406
``AbelianGroup(gens_orders, names="f")``, where the names keyword
407
argument must be explicitly named.
408
409
OUTPUT:
410
411
Abelian group with generators and invariant type. The default name
412
for generator ``A.i`` is ``fi``, as in GAP.
413
414
EXAMPLES::
415
416
sage: F = AbelianGroup(5, [5,5,7,8,9], names='abcde')
417
sage: F(1)
418
1
419
sage: (a, b, c, d, e) = F.gens()
420
sage: mul([ a, b, a, c, b, d, c, d ], F(1))
421
a^2*b^2*c^2*d^2
422
sage: d * b**2 * c**3
423
b^2*c^3*d
424
sage: F = AbelianGroup(3,[2]*3); F
425
Multiplicative Abelian group isomorphic to C2 x C2 x C2
426
sage: H = AbelianGroup([2,3], names="xy"); H
427
Multiplicative Abelian group isomorphic to C2 x C3
428
sage: AbelianGroup(5)
429
Multiplicative Abelian group isomorphic to Z x Z x Z x Z x Z
430
sage: AbelianGroup(5).order()
431
+Infinity
432
433
Notice that `0`'s are prepended if necessary::
434
435
sage: G = AbelianGroup(5, [2,3,4]); G
436
Multiplicative Abelian group isomorphic to Z x Z x C2 x C3 x C4
437
sage: G.gens_orders()
438
(0, 0, 2, 3, 4)
439
440
The invariant list must not be longer than the number of generators::
441
442
sage: AbelianGroup(2, [2,3,4])
443
Traceback (most recent call last):
444
...
445
ValueError: gens_orders (=(2, 3, 4)) must have length n (=2)
446
"""
447
gens_orders, names = _normalize(n, gens_orders, names)
448
M = AbelianGroup_class(gens_orders, names)
449
return M
450
451
def is_AbelianGroup(x):
452
"""
453
Return True if ``x`` is an Abelian group.
454
455
EXAMPLES::
456
457
sage: from sage.groups.abelian_gps.abelian_group import is_AbelianGroup
458
sage: F = AbelianGroup(5,[5,5,7,8,9],names = list("abcde")); F
459
Multiplicative Abelian group isomorphic to C5 x C5 x C7 x C8 x C9
460
sage: is_AbelianGroup(F)
461
True
462
sage: is_AbelianGroup(AbelianGroup(7, [3]*7))
463
True
464
"""
465
return isinstance(x, AbelianGroup_class)
466
467
468
class AbelianGroup_class(UniqueRepresentation, AbelianGroupBase):
469
"""
470
The parent for Abelian groups with chosen generator orders.
471
472
.. warning::
473
474
You should use :func:`AbelianGroup` to construct Abelian
475
groups and not instantiate this class directly.
476
477
INPUT:
478
479
- ``generator_orders`` -- list of integers. The orders of the
480
(commuting) generators. Zero denotes an infinite cyclic
481
generator.
482
483
- ``names`` -- names of the group generators (optional).
484
485
EXAMPLES::
486
487
sage: Z2xZ3 = AbelianGroup([2,3])
488
sage: Z6 = AbelianGroup([6])
489
sage: Z2xZ3 is Z2xZ3, Z6 is Z6
490
(True, True)
491
sage: Z2xZ3 is Z6
492
False
493
sage: Z2xZ3 == Z6
494
True
495
496
sage: F = AbelianGroup(5,[5,5,7,8,9],names = list("abcde")); F
497
Multiplicative Abelian group isomorphic to C5 x C5 x C7 x C8 x C9
498
sage: F = AbelianGroup(5,[2, 4, 12, 24, 120],names = list("abcde")); F
499
Multiplicative Abelian group isomorphic to C2 x C4 x C12 x C24 x C120
500
sage: F.elementary_divisors()
501
(2, 4, 12, 24, 120)
502
503
sage: F.category()
504
Category of groups
505
506
TESTS::
507
508
sage: AbelianGroup([]).gens_orders()
509
()
510
sage: AbelianGroup([1]).gens_orders()
511
(1,)
512
sage: AbelianGroup([1,1]).gens_orders()
513
(1, 1)
514
sage: AbelianGroup(0).gens_orders()
515
()
516
"""
517
Element = AbelianGroupElement
518
519
def __init__(self, generator_orders, names):
520
"""
521
The Python constructor
522
523
TESTS::
524
525
sage: G = AbelianGroup([0,5,0,7],names = list("abcd")); G
526
Multiplicative Abelian group isomorphic to Z x C5 x Z x C7
527
sage: TestSuite(G).run()
528
"""
529
assert isinstance(names, (basestring, tuple))
530
assert isinstance(generator_orders, tuple)
531
assert all(isinstance(order,Integer) for order in generator_orders)
532
self._gens_orders = generator_orders
533
n = ZZ(len(generator_orders))
534
names = self.normalize_names(n, names)
535
self._assign_names(names)
536
AbelianGroupBase.__init__(self) # TODO: category=CommutativeGroups()
537
538
def is_isomorphic(left, right):
539
"""
540
Check whether ``left`` and ``right`` are isomorphic
541
542
INPUT:
543
544
- ``right`` -- anything.
545
546
OUTPUT:
547
548
Boolean. Whether ``left`` and ``right`` are isomorphic as abelian groups.
549
550
EXAMPLES::
551
552
sage: G1 = AbelianGroup([2,3,4,5])
553
sage: G2 = AbelianGroup([2,3,4,5,1])
554
sage: G1.is_isomorphic(G2)
555
True
556
sage: G1 == G2 # syntactic sugar
557
True
558
"""
559
if not is_AbelianGroup(right):
560
return False
561
return left.elementary_divisors() == right.elementary_divisors()
562
563
__eq__ = is_isomorphic
564
565
def __ne__(left, right):
566
"""
567
Check whether ``left`` and ``right`` are not isomorphic
568
569
OUTPUT:
570
571
Boolean.
572
573
EXAMPLES::
574
575
sage: G1 = AbelianGroup([2,3,4,5])
576
sage: G2 = AbelianGroup([2,3,4,5,1])
577
sage: G1 != G2
578
False
579
sage: G1.__ne__(G2)
580
False
581
"""
582
return not left.is_isomorphic(right)
583
584
def is_subgroup(left, right):
585
"""
586
Test whether ``left`` is a subgroup of ``right``.
587
588
EXAMPLES::
589
590
sage: G = AbelianGroup([2,3,4,5])
591
sage: G.is_subgroup(G)
592
True
593
594
sage: H = G.subgroup([G.1])
595
sage: H.is_subgroup(G)
596
True
597
598
sage: G.<a, b> = AbelianGroup(2)
599
sage: H.<c> = AbelianGroup(1)
600
sage: H < G
601
False
602
"""
603
for l in left.gens():
604
if l not in right:
605
return False
606
return True
607
608
__le__ = is_subgroup
609
610
def __ge__(left, right):
611
"""
612
Test whether ``right`` is a subgroup of ``left``
613
614
EXAMPLE::
615
616
sage: G.<a, b> = AbelianGroup(2)
617
sage: H.<c> = AbelianGroup(1)
618
sage: G >= H
619
False
620
"""
621
return right.__le__(left)
622
623
def __lt__(left, right):
624
"""
625
Test whether ``left`` is a strict subgroup of ``right``
626
627
EXAMPLE::
628
629
sage: G.<a, b> = AbelianGroup(2)
630
sage: H.<c> = AbelianGroup(1)
631
sage: H < G
632
False
633
"""
634
return left <= right and left != right
635
636
def __gt__(left, right):
637
"""
638
Test whether ``right`` is a strict subgroup of ``left``
639
640
EXAMPLE::
641
642
sage: G.<a, b> = AbelianGroup(2)
643
sage: H.<c> = AbelianGroup(1)
644
sage: G > H
645
False
646
"""
647
return left >= right and left != right
648
649
def is_trivial(self):
650
"""
651
Return whether the group is trivial
652
653
A group is trivial if it has precisely one element.
654
655
EXAMPLES::
656
657
sage: AbelianGroup([2, 3]).is_trivial()
658
False
659
sage: AbelianGroup([1, 1]).is_trivial()
660
True
661
"""
662
return self.elementary_divisors() == ()
663
664
def __nonzero__(self):
665
"""
666
Returns True if this group is nontrivial.
667
668
EXAMPLES::
669
670
sage: T = AbelianGroup([2, 3])
671
sage: bool(T) # indirect doctest
672
True
673
sage: bool(AbelianGroup([]))
674
False
675
sage: bool(AbelianGroup([1,1,1]))
676
False
677
"""
678
return not self.is_trivial()
679
680
@cached_method
681
def dual_group(self, names="X", base_ring=None):
682
"""
683
Returns the dual group.
684
685
INPUT:
686
687
- ``names`` -- string or list of strings. The generator names
688
for the dual group.
689
690
- ``base_ring`` -- the base ring. If ``None`` (default), then
691
a suitable cyclotomic field is picked automatically.
692
693
OUTPUT:
694
695
The
696
:class:`~sage.groups.abelian_gps.dual_abelian_group.DualAbelianGroup_class
697
<dual abelian group>`
698
699
EXAMPLES::
700
701
sage: G = AbelianGroup([2])
702
sage: G.dual_group()
703
Dual of Abelian Group isomorphic to Z/2Z over Cyclotomic Field of order 2 and degree 1
704
sage: G.dual_group().gens()
705
(X,)
706
sage: G.dual_group(names='Z').gens()
707
(Z,)
708
709
sage: G.dual_group(base_ring=QQ)
710
Dual of Abelian Group isomorphic to Z/2Z over Rational Field
711
712
713
TESTS::
714
715
sage: H = AbelianGroup(1)
716
sage: H.dual_group()
717
Traceback (most recent call last):
718
...
719
ValueError: the group must be finite
720
"""
721
from sage.groups.abelian_gps.dual_abelian_group import DualAbelianGroup_class
722
if not self.is_finite():
723
raise ValueError('the group must be finite')
724
if base_ring is None:
725
from sage.rings.number_field.number_field import CyclotomicField
726
from sage.rings.arith import LCM
727
base_ring = CyclotomicField(LCM(self.gens_orders()))
728
return DualAbelianGroup_class(self, names=names, base_ring=base_ring)
729
730
@cached_method
731
def elementary_divisors(self):
732
r"""
733
This returns the elementary divisors of the group, using Pari.
734
735
.. note::
736
737
Here is another algorithm for computing the elementary divisors
738
`d_1, d_2, d_3, \ldots`, of a finite abelian group (where `d_1 | d_2 | d_3 | \ldots`
739
are composed of prime powers dividing the invariants of the group
740
in a way described below). Just factor the invariants `a_i` that
741
define the abelian group. Then the biggest `d_i` is the product
742
of the maximum prime powers dividing some `a_j`. In other words, the
743
largest `d_i` is the product of `p^v`, where `v = max(ord_p(a_j) \mathrm{ for all } j`).
744
Now divide out all those `p^v`'s into the list of invariants `a_i`,
745
and get a new list of "smaller invariants"". Repeat the above procedure
746
on these ""smaller invariants"" to compute `d_{i-1}`, and so on.
747
(Thanks to Robert Miller for communicating this algorithm.)
748
749
OUTPUT:
750
751
A tuple of integers.
752
753
EXAMPLES::
754
755
sage: G = AbelianGroup(2,[2,3])
756
sage: G.elementary_divisors()
757
(6,)
758
sage: G = AbelianGroup(1, [6])
759
sage: G.elementary_divisors()
760
(6,)
761
sage: G = AbelianGroup(2,[2,6])
762
sage: G
763
Multiplicative Abelian group isomorphic to C2 x C6
764
sage: G.gens_orders()
765
(2, 6)
766
sage: G.elementary_divisors()
767
(2, 6)
768
sage: J = AbelianGroup([1,3,5,12])
769
sage: J.elementary_divisors()
770
(3, 60)
771
sage: G = AbelianGroup(2,[0,6])
772
sage: G.elementary_divisors()
773
(6, 0)
774
sage: AbelianGroup([3,4,5]).elementary_divisors()
775
(60,)
776
"""
777
from sage.matrix.constructor import diagonal_matrix
778
ed = diagonal_matrix(ZZ, self.gens_orders()).elementary_divisors()
779
return tuple(d for d in ed if d!=1)
780
781
@cached_method
782
def exponent(self):
783
"""
784
Return the exponent of this abelian group.
785
786
EXAMPLES::
787
788
sage: G = AbelianGroup([2,3,7]); G
789
Multiplicative Abelian group isomorphic to C2 x C3 x C7
790
sage: G.exponent()
791
42
792
sage: G = AbelianGroup([2,4,6]); G
793
Multiplicative Abelian group isomorphic to C2 x C4 x C6
794
sage: G.exponent()
795
12
796
"""
797
return lcm(self.gens_orders())
798
799
def identity(self):
800
r"""
801
Return the identity element of this group.
802
803
EXAMPLES::
804
805
sage: G = AbelianGroup([2,2])
806
sage: e = G.identity()
807
sage: e
808
1
809
sage: g = G.gen(0)
810
sage: g*e
811
f0
812
sage: e*g
813
f0
814
"""
815
return self(1)
816
817
def _group_notation(self, eldv):
818
"""
819
Return abstract group notation for generator orders ``eldv``
820
821
INPUT:
822
823
- ``eldv`` -- iterable of integers.
824
825
OUTPUT:
826
827
String.
828
829
EXAMPLES::
830
831
sage: G = AbelianGroup([2,2])
832
sage: G._group_notation([0,1,2,3])
833
'Z x C1 x C2 x C3'
834
"""
835
v = []
836
for x in eldv:
837
if x:
838
v.append("C%s"%x)
839
else:
840
v.append("Z")
841
return ' x '.join(v)
842
843
def _latex_(self):
844
r"""
845
Return latex representation of this group.
846
847
EXAMPLES::
848
849
sage: F = AbelianGroup(10, [2]*10)
850
sage: F._latex_()
851
'$\\mathrm{AbelianGroup}( 10, (2, 2, 2, 2, 2, 2, 2, 2, 2, 2) )$'
852
"""
853
s = "$\mathrm{AbelianGroup}( %s, %s )$"%(self.ngens(), self.gens_orders())
854
return s
855
856
def _gap_init_(self):
857
r"""
858
Return string that defines corresponding abelian group in GAP.
859
860
EXAMPLES::
861
862
sage: G = AbelianGroup([2,3,9])
863
sage: G._gap_init_()
864
'AbelianGroup([2, 3, 9])'
865
sage: gap(G)
866
Group( [ f1, f2, f3 ] )
867
868
Only works for finite groups::
869
870
sage: G = AbelianGroup(3,[0,3,4],names="abc"); G
871
Multiplicative Abelian group isomorphic to Z x C3 x C4
872
sage: G._gap_init_()
873
Traceback (most recent call last):
874
...
875
TypeError: abelian groups in GAP are finite, but self is infinite
876
"""
877
# TODO: Use the package polycyclic has AbelianPcpGroup, which can handle
878
# the infinite case but it is a GAP package not GPL'd.
879
# Use this when the group is infinite...
880
# return 'AbelianPcpGroup(%s)'%list(self.invariants())
881
if not self.is_finite():
882
raise TypeError('abelian groups in GAP are finite, but self is infinite')
883
return 'AbelianGroup(%s)'%list(self.gens_orders())
884
885
def gen(self, i=0):
886
"""
887
The `i`-th generator of the abelian group.
888
889
EXAMPLES::
890
891
sage: F = AbelianGroup(5,[],names='a')
892
sage: F.0
893
a0
894
sage: F.2
895
a2
896
sage: F.gens_orders()
897
(0, 0, 0, 0, 0)
898
899
sage: G = AbelianGroup([2,1,3])
900
sage: G.gens()
901
(f0, 1, f2)
902
"""
903
n = self.ngens()
904
if i < 0 or i >= n:
905
raise IndexError, "Argument i (= %s) must be between 0 and %s."%(i, n-1)
906
x = [0]*n
907
if self._gens_orders[i] != 1:
908
x[i] = 1
909
return self.element_class(self, x)
910
911
def gens(self):
912
"""
913
Return the generators of the group.
914
915
OUTPUT:
916
917
A tuple of group elements. The generators according to the
918
chosen :meth:`gens_orders`.
919
920
EXAMPLES::
921
922
sage: F = AbelianGroup(5,[3,2],names='abcde')
923
sage: F.gens()
924
(a, b, c, d, e)
925
sage: [ g.order() for g in F.gens() ]
926
[+Infinity, +Infinity, +Infinity, 3, 2]
927
"""
928
return tuple( self.gen(i) for i in range(self.ngens()) )
929
930
def gens_orders(self):
931
"""
932
Return the orders of the cyclic factors that this group has
933
been defined with.
934
935
Use :meth:`elementary_divisors` if you are looking for an
936
invariant of the group.
937
938
OUTPUT:
939
940
A tuple of integers.
941
942
EXAMPLES::
943
944
sage: Z2xZ3 = AbelianGroup([2,3])
945
sage: Z2xZ3.gens_orders()
946
(2, 3)
947
sage: Z2xZ3.elementary_divisors()
948
(6,)
949
950
sage: Z6 = AbelianGroup([6])
951
sage: Z6.gens_orders()
952
(6,)
953
sage: Z6.elementary_divisors()
954
(6,)
955
956
sage: Z2xZ3.is_isomorphic(Z6)
957
True
958
sage: Z2xZ3 is Z6
959
False
960
961
TESTS::
962
963
sage: F = AbelianGroup(3, [2], names='abc')
964
sage: map(type, F.gens_orders())
965
[<type 'sage.rings.integer.Integer'>,
966
<type 'sage.rings.integer.Integer'>,
967
<type 'sage.rings.integer.Integer'>]
968
"""
969
return self._gens_orders
970
971
def invariants(self):
972
"""
973
Return the orders of the cyclic factors that this group has
974
been defined with.
975
976
For historical reasons this has been called invariants in
977
Sage, even though they are not necessarily the invariant
978
factors of the group. Use :meth:`gens_orders` instead::
979
980
sage: J = AbelianGroup([2,0,3,2,4]); J
981
Multiplicative Abelian group isomorphic to C2 x Z x C3 x C2 x C4
982
sage: J.invariants() # deprecated
983
(2, 0, 3, 2, 4)
984
sage: J.gens_orders() # use this instead
985
(2, 0, 3, 2, 4)
986
sage: for i in range(J.ngens()):
987
... print i, J.gen(i), J.gen(i).order() # or use this
988
0 f0 2
989
1 f1 +Infinity
990
2 f2 3
991
3 f3 2
992
4 f4 4
993
994
Use :meth:`elementary_divisors` if you are looking for an
995
invariant of the group.
996
997
OUTPUT:
998
999
A tuple of integers. Zero means infinite cyclic factor.
1000
1001
EXAMPLES::
1002
1003
sage: J = AbelianGroup([2,3])
1004
sage: J.invariants()
1005
(2, 3)
1006
sage: J.elementary_divisors()
1007
(6,)
1008
1009
TESTS::
1010
1011
sage: F = AbelianGroup(3, [2], names='abc')
1012
sage: map(type, F.gens_orders())
1013
[<type 'sage.rings.integer.Integer'>,
1014
<type 'sage.rings.integer.Integer'>,
1015
<type 'sage.rings.integer.Integer'>]
1016
"""
1017
# TODO: deprecate
1018
return self.gens_orders()
1019
1020
def is_cyclic(self):
1021
"""
1022
Return True if the group is a cyclic group.
1023
1024
EXAMPLES::
1025
1026
sage: J = AbelianGroup([2,3])
1027
sage: J.gens_orders()
1028
(2, 3)
1029
sage: J.elementary_divisors()
1030
(6,)
1031
sage: J.is_cyclic()
1032
True
1033
sage: G = AbelianGroup([6])
1034
sage: G.gens_orders()
1035
(6,)
1036
sage: G.is_cyclic()
1037
True
1038
sage: H = AbelianGroup([2,2])
1039
sage: H.gens_orders()
1040
(2, 2)
1041
sage: H.is_cyclic()
1042
False
1043
sage: H = AbelianGroup([2,4])
1044
sage: H.elementary_divisors()
1045
(2, 4)
1046
sage: H.is_cyclic()
1047
False
1048
sage: H.permutation_group().is_cyclic()
1049
False
1050
sage: T = AbelianGroup([])
1051
sage: T.is_cyclic()
1052
True
1053
sage: T = AbelianGroup(1,[0]); T
1054
Multiplicative Abelian group isomorphic to Z
1055
sage: T.is_cyclic()
1056
True
1057
sage: B = AbelianGroup([3,4,5])
1058
sage: B.is_cyclic()
1059
True
1060
"""
1061
return len(self.elementary_divisors()) <= 1
1062
1063
@cached_method
1064
def ngens(self):
1065
"""
1066
The number of free generators of the abelian group.
1067
1068
EXAMPLES::
1069
1070
sage: F = AbelianGroup(10000)
1071
sage: F.ngens()
1072
10000
1073
"""
1074
return len(self.gens_orders())
1075
1076
@cached_method
1077
def order(self):
1078
"""
1079
Return the order of this group.
1080
1081
EXAMPLES::
1082
1083
sage: G = AbelianGroup(2,[2,3])
1084
sage: G.order()
1085
6
1086
sage: G = AbelianGroup(3,[2,3,0])
1087
sage: G.order()
1088
+Infinity
1089
"""
1090
from sage.rings.all import infinity
1091
length = prod(self.gens_orders())
1092
if length == 0:
1093
return infinity
1094
return length
1095
1096
def permutation_group(self):
1097
r"""
1098
Return the permutation group isomorphic to this abelian group.
1099
1100
If the invariants are `q_1, \ldots, q_n` then the
1101
generators of the permutation will be of order
1102
`q_1, \ldots, q_n`, respectively.
1103
1104
EXAMPLES::
1105
1106
sage: G = AbelianGroup(2,[2,3]); G
1107
Multiplicative Abelian group isomorphic to C2 x C3
1108
sage: G.permutation_group()
1109
Permutation Group with generators [(3,4,5), (1,2)]
1110
"""
1111
from sage.groups.perm_gps.permgroup import PermutationGroup
1112
s = 'Image(IsomorphismPermGroup(%s))'%self._gap_init_()
1113
return PermutationGroup(gap_group=s)
1114
1115
def is_commutative(self):
1116
"""
1117
Return True since this group is commutative.
1118
1119
EXAMPLES::
1120
1121
sage: G = AbelianGroup([2,3,9, 0])
1122
sage: G.is_commutative()
1123
True
1124
sage: G.is_abelian()
1125
True
1126
"""
1127
return True
1128
1129
def random_element(self):
1130
"""
1131
Return a random element of this group.
1132
1133
EXAMPLES::
1134
1135
sage: G = AbelianGroup([2,3,9])
1136
sage: G.random_element()
1137
f1^2
1138
"""
1139
from sage.misc.prandom import randint
1140
result = self.one()
1141
for g in self.gens():
1142
order = g.order()
1143
if order not in ZZ:
1144
order = 42 # infinite order; randomly chosen maximum
1145
result *= g**(randint(0,order))
1146
return result
1147
1148
def _repr_(self):
1149
"""
1150
Return a string representation of ``self``.
1151
1152
EXAMPLES::
1153
1154
sage: G = AbelianGroup([2,3,9])
1155
sage: G._repr_()
1156
'Multiplicative Abelian group isomorphic to C2 x C3 x C9'
1157
"""
1158
eldv = self.gens_orders()
1159
if len(eldv) == 0:
1160
return "Trivial Abelian group"
1161
g = self._group_notation(eldv)
1162
return "Multiplicative Abelian group isomorphic to " + g
1163
1164
def subgroup(self, gensH, names="f"):
1165
"""
1166
Create a subgroup of this group. The "big" group must be defined
1167
using "named" generators.
1168
1169
INPUT:
1170
1171
- ``gensH`` -- list of elements which are products of the
1172
generators of the ambient abelian group G = self
1173
1174
EXAMPLES::
1175
1176
sage: G.<a,b,c> = AbelianGroup(3, [2,3,4]); G
1177
Multiplicative Abelian group isomorphic to C2 x C3 x C4
1178
sage: H = G.subgroup([a*b,a]); H
1179
Multiplicative Abelian subgroup isomorphic to C2 x C3 generated by {a*b, a}
1180
sage: H < G
1181
True
1182
sage: F = G.subgroup([a,b^2])
1183
sage: F
1184
Multiplicative Abelian subgroup isomorphic to C2 x C3 generated by {a, b^2}
1185
sage: F.gens()
1186
(a, b^2)
1187
sage: F = AbelianGroup(5,[30,64,729],names = list("abcde"))
1188
sage: a,b,c,d,e = F.gens()
1189
sage: F.subgroup([a,b])
1190
Multiplicative Abelian subgroup isomorphic to Z x Z generated by {a, b}
1191
sage: F.subgroup([c,e])
1192
Multiplicative Abelian subgroup isomorphic to C2 x C3 x C5 x C729 generated by {c, e}
1193
"""
1194
G = self
1195
gensH = tuple(gensH)
1196
if isinstance(names, list):
1197
names = tuple(names)
1198
for h in gensH:
1199
if h not in G:
1200
raise TypeError('Subgroup generators must belong to the given group.')
1201
return AbelianGroup_subgroup(self, gensH, names)
1202
1203
@cached_method
1204
def list(self):
1205
"""
1206
Return tuple of all elements of this group.
1207
1208
EXAMPLES::
1209
1210
sage: G = AbelianGroup([2,3], names = "ab")
1211
sage: G.list()
1212
(1, b, b^2, a, a*b, a*b^2)
1213
1214
::
1215
1216
sage: G = AbelianGroup([]); G
1217
Trivial Abelian group
1218
sage: G.list()
1219
(1,)
1220
"""
1221
if not(self.is_finite()):
1222
raise NotImplementedError, "Group must be finite"
1223
return tuple(self.__iter__())
1224
1225
def __iter__(self):
1226
"""
1227
Return an iterator over the elements of this group.
1228
1229
EXAMPLES::
1230
1231
sage: G = AbelianGroup([2,3], names = "ab")
1232
sage: [a for a in G]
1233
[1, b, b^2, a, a*b, a*b^2]
1234
sage: L = list(G); L
1235
[1, b, b^2, a, a*b, a*b^2]
1236
1237
The returned list is a reference; mutating it does not allow the
1238
user to (accidentally?) alter the computed generators::
1239
1240
sage: L[0] = 0
1241
sage: list(G)
1242
[1, b, b^2, a, a*b, a*b^2]
1243
sage: G = AbelianGroup([1], names="a")
1244
sage: list(G)
1245
[1]
1246
sage: G = AbelianGroup([])
1247
sage: G.list()
1248
(1,)
1249
sage: list(G)
1250
[1]
1251
"""
1252
invs = self.gens_orders()
1253
for t in mrange(invs):
1254
yield self(t)
1255
1256
def subgroups(self, check=False):
1257
r"""
1258
Compute all the subgroups of this abelian group (which must be finite).
1259
1260
TODO: This is *many orders of magnitude* slower than Magma.
1261
1262
INPUT:
1263
1264
- check: if True, performs the same computation in GAP and checks that
1265
the number of subgroups generated is the same. (I don't know how to
1266
convert GAP's output back into Sage, so we don't actually compare the
1267
subgroups).
1268
1269
ALGORITHM:
1270
1271
If the group is cyclic, the problem is easy. Otherwise, write it as
1272
a direct product A x B, where B is cyclic. Compute the subgroups of
1273
A (by recursion).
1274
1275
Now, for every subgroup C of A x B, let G be its *projection onto*
1276
A and H its *intersection with* B. Then there is a well-defined
1277
homomorphism f: G -> B/H that sends a in G to the class mod H of b,
1278
where (a,b) is any element of C lifting a; and every subgroup C
1279
arises from a unique triple (G, H, f).
1280
1281
EXAMPLES::
1282
1283
sage: AbelianGroup([2,3]).subgroups()
1284
[Multiplicative Abelian subgroup isomorphic to C2 x C3 generated by {f0*f1^2},
1285
Multiplicative Abelian subgroup isomorphic to C2 generated by {f0},
1286
Multiplicative Abelian subgroup isomorphic to C3 generated by {f1},
1287
Trivial Abelian subgroup]
1288
sage: len(AbelianGroup([2,4,8]).subgroups())
1289
81
1290
1291
TESTS::
1292
1293
sage: AbelianGroup([]).subgroups()
1294
[Trivial Abelian group]
1295
"""
1296
if not self.is_finite(): raise ValueError, "Group must be finite"
1297
from sage.misc.misc import verbose
1298
1299
if self.is_trivial():
1300
return [self]
1301
if self.ngens() == 1:
1302
n = self.gen(0).order()
1303
return [ self.subgroup([self.gen(0)**i]) for i in divisors(n) ]
1304
1305
v = self.gens_orders()
1306
A = AbelianGroup(v[:-1])
1307
x = v[-1]
1308
1309
Wsubs = A.subgroups()
1310
1311
subgps = []
1312
for G in Wsubs:
1313
verbose("G = subgp generated by %s" % list(G.gens()))
1314
verbose("invariants are:", [t.order() for t in G.gens()])
1315
for H in divisors(x):
1316
# H = the subgroup of *index* H.
1317
its = [xrange(0, H, H/gcd(H, G.gen(i).order())) for i in xrange(len(G.gens()))]
1318
for f in cartesian_product_iterator(its):
1319
verbose("using hom from G to C_%s sending gens to %s" % (H,f))
1320
new_sub = []
1321
for a in xrange(len(G.gens())):
1322
new_sub.append(G.gen(a).list() + [f[a]])
1323
if H != x:
1324
new_sub.append([0]*A.ngens() + [H])
1325
subgps.append(self.subgroup_reduced(new_sub))
1326
1327
if check:
1328
from sage.interfaces.all import gap
1329
verbose("Running Gap cross-check")
1330
t = ZZ(gap.eval("Size(SubgroupsSolvableGroup(AbelianGroup(%s)))" % v))
1331
if t != len(subgps):
1332
raise ArithmeticError, "For %s Gap finds %s subgroups, I found %s" % (v, t, len(subgps))
1333
verbose("Gap check OK for %s: %s" % (v, t))
1334
return subgps
1335
1336
def subgroup_reduced(self,elts, verbose=False):
1337
r"""
1338
Given a list of lists of integers (corresponding to elements of self),
1339
find a set of independent generators for the subgroup generated by
1340
these elements, and return the subgroup with these as generators,
1341
forgetting the original generators.
1342
1343
This is used by the ``subgroups`` routine.
1344
1345
An error will be raised if the elements given are not linearly
1346
independent over QQ.
1347
1348
EXAMPLE::
1349
1350
sage: G = AbelianGroup([4,4])
1351
sage: G.subgroup( [ G([1,0]), G([1,2]) ])
1352
Multiplicative Abelian subgroup isomorphic to C2 x C4
1353
generated by {f0, f0*f1^2}
1354
sage: AbelianGroup([4,4]).subgroup_reduced( [ [1,0], [1,2] ])
1355
Multiplicative Abelian subgroup isomorphic to C2 x C4
1356
generated by {f1^2, f0}
1357
"""
1358
from sage.matrix.constructor import matrix
1359
d = self.ngens()
1360
X = ZZ**d
1361
try:
1362
elt_lattice = X.submodule_with_basis(elts)
1363
except ValueError, e:
1364
# can't happen?
1365
print "Vectors not LI: ", elts
1366
raise e
1367
rel_lattice = X.span([X.gen(i) * self.gens_orders()[i] for i in xrange(d)])
1368
isect = elt_lattice.intersection(rel_lattice)
1369
mat = matrix([elt_lattice.coordinate_vector(x) for x in isect.gens()]).change_ring(ZZ)
1370
D,U,V = mat.smith_form()
1371
new_basis = [(elt_lattice.linear_combination_of_basis((~V).row(i)).list(), D[i,i]) for i in xrange(U.ncols())]
1372
return self.subgroup([self([x[0][i] % self.gens_orders()[i]
1373
for i in xrange(d)]) for x in new_basis if x[1] != 1])
1374
1375
class AbelianGroup_subgroup(AbelianGroup_class):
1376
"""
1377
Subgroup subclass of AbelianGroup_class, so instance methods are
1378
inherited.
1379
1380
TODO:
1381
1382
- There should be a way to coerce an element of a subgroup
1383
into the ambient group.
1384
"""
1385
def __init__(self, ambient, gens, names="f"):
1386
"""
1387
EXAMPLES::
1388
1389
sage: F = AbelianGroup(5,[30,64,729],names = list("abcde"))
1390
sage: a,b,c,d,e = F.gens()
1391
sage: F.subgroup([a^3,b])
1392
Multiplicative Abelian subgroup isomorphic to Z x Z generated by {a^3, b}
1393
sage: F.subgroup([c])
1394
Multiplicative Abelian subgroup isomorphic to C2 x C3 x C5 generated by {c}
1395
sage: F.subgroup([a, c])
1396
Multiplicative Abelian subgroup isomorphic to C2 x C3 x C5 x Z generated by {a, c}
1397
sage: F.subgroup([a, b*c])
1398
Multiplicative Abelian subgroup isomorphic to Z x Z generated by {a, b*c}
1399
sage: F.subgroup([b*c, d])
1400
Multiplicative Abelian subgroup isomorphic to C64 x Z generated by {b*c, d}
1401
sage: F.subgroup([a*b, c^6, d],names=list("xyz"))
1402
Multiplicative Abelian subgroup isomorphic to C5 x C64 x Z generated by {a*b, c^6, d}
1403
sage: H.<x,y,z> = F.subgroup([a*b, c^6, d]); H
1404
Multiplicative Abelian subgroup isomorphic to C5 x C64 x Z generated by {a*b, c^6, d}
1405
sage: G = F.subgroup([a*b, c^6, d],names = list("xyz")); G
1406
Multiplicative Abelian subgroup isomorphic to C5 x C64 x Z generated by {a*b, c^6, d}
1407
sage: x,y,z = G.gens()
1408
sage: x.order()
1409
+Infinity
1410
sage: y.order()
1411
5
1412
sage: z.order()
1413
64
1414
sage: A = AbelianGroup(5,[3, 5, 5, 7, 8], names = "abcde")
1415
sage: a,b,c,d,e = A.gens()
1416
sage: A.subgroup([a,b])
1417
Multiplicative Abelian subgroup isomorphic to C3 x C5 generated by {a, b}
1418
sage: A.subgroup([a,b,c,d^2,e])
1419
Multiplicative Abelian subgroup isomorphic to C3 x C5 x C5 x C7 x C8 generated by {a, b, c, d^2, e}
1420
sage: A.subgroup([a, b, c, d^2, e^2])
1421
Multiplicative Abelian subgroup isomorphic to C3 x C4 x C5 x C5 x C7 generated by {a, b, c, d^2, e^2}
1422
sage: B = A.subgroup([a^3, b, c, d, e^2]); B
1423
Multiplicative Abelian subgroup isomorphic to C4 x C5 x C5 x C7 generated by {b, c, d, e^2}
1424
sage: B.gens_orders()
1425
(4, 5, 5, 7)
1426
sage: A = AbelianGroup(4,[1009, 2003, 3001, 4001], names = "abcd")
1427
sage: a,b,c,d = A.gens()
1428
sage: B = A.subgroup([a^3,b,c,d])
1429
sage: B.gens_orders()
1430
(1009, 2003, 3001, 4001)
1431
sage: A.order()
1432
24266473210027
1433
sage: B.order()
1434
24266473210027
1435
sage: A = AbelianGroup(4,[1008, 2003, 3001, 4001], names = "abcd")
1436
sage: a,b,c,d = A.gens()
1437
sage: B = A.subgroup([a^3,b,c,d]); B
1438
Multiplicative Abelian subgroup isomorphic
1439
to C3 x C7 x C16 x C2003 x C3001 x C4001 generated by {a^3, b, c, d}
1440
1441
Infinite groups can also be handled::
1442
1443
sage: G = AbelianGroup([3,4,0], names = "abc")
1444
sage: a,b,c = G.gens()
1445
sage: F = G.subgroup([a, b^2, c]); F
1446
Multiplicative Abelian subgroup isomorphic to C2 x C3 x Z generated by {a, b^2, c}
1447
1448
sage: F.gens_orders()
1449
(2, 3, 0)
1450
sage: F.gens()
1451
(a, b^2, c)
1452
sage: F.order()
1453
+Infinity
1454
"""
1455
from sage.interfaces.all import gap
1456
if not isinstance(ambient, AbelianGroup_class):
1457
raise TypeError, "ambient (=%s) must be an abelian group."%ambient
1458
if not isinstance(gens, tuple):
1459
raise TypeError, "gens (=%s) must be a tuple"%gens
1460
1461
self._ambient_group = ambient
1462
Hgens = tuple(x for x in gens if x != ambient.one()) ## in case someone puts 1 in the list of generators
1463
self._gens = Hgens
1464
m = len(gens)
1465
ell = len(ambient.gens())
1466
ambient_invs = ambient.gens_orders()
1467
invsf = [x for x in ambient_invs if x > 0] ## fixes the problem with
1468
invs0 = [x for x in ambient_invs if x == 0] ## the infinite parts
1469
Ggens = list(ambient.variable_names())
1470
if invs0!=[]:
1471
Gfgens = [x for x in ambient.variable_names() if
1472
ambient_invs[Ggens.index(x)] != 0]
1473
Ggens0 = [x for x in ambient.variable_names() if
1474
ambient_invs[Ggens.index(x)] == 0]
1475
## ^^ only look at "finite" names
1476
Gf = AbelianGroup(invsf, names=Gfgens)
1477
s1 = "G:= %s; gens := GeneratorsOfGroup(G)"%Gf._gap_init_()
1478
gap.eval(s1)
1479
Hgensf = [x for x in Hgens if len(set(Ggens0).intersection(set(list(str(x)))))==0]
1480
# computes the gens of H which do not occur ^^ in the infinite part of G
1481
Hgens0 = [x for x in Hgens if not(x in Hgensf)]
1482
# the "infinite" generators of H
1483
for i in range(len(Gfgens)):
1484
cmd = ("%s := gens["+str(i+1)+"]")%Gfgens[i]
1485
gap.eval(cmd)
1486
else: # invs0==[]:
1487
Hgensf = Hgens
1488
Hgens0 = [] # added for consistency
1489
G = ambient
1490
s1 = "G:= %s; gens := GeneratorsOfGroup(G)"%G._gap_init_()
1491
gap.eval(s1)
1492
for i in range(len(Ggens)):
1493
cmd = '%s := gens[%s]'%(Ggens[i], i+1)
1494
#print i," \n",cmd
1495
gap.eval(cmd)
1496
s2 = "gensH:=%s"%list(Hgensf) #### remove from this the ones <--> 0 invar
1497
gap.eval(s2)
1498
s3 = 'H:=Subgroup(G,gensH)'
1499
gap.eval(s3)
1500
# a GAP command which returns the "invariants" of the
1501
# subgroup as an AbelianPcpGroup, RelativeOrdersOfPcp(Pcp(G)),
1502
# works if G is the subgroup declared as a AbelianPcpGroup
1503
self._abinvs = eval(gap.eval("AbelianInvariants(H)"))
1504
invs = self._abinvs
1505
#print s3, invs
1506
if Hgens0 != []:
1507
for x in Hgens0:
1508
invs.append(0)
1509
invs = tuple(ZZ(i) for i in invs)
1510
AbelianGroup_class.__init__(self, invs, names)
1511
1512
def __contains__(self, x):
1513
"""
1514
Test whether ``x`` is an element of this subgroup.
1515
1516
EXAMPLES::
1517
1518
sage: G.<a,b> = AbelianGroup(2)
1519
sage: A = G.subgroup([a])
1520
sage: a in G
1521
True
1522
sage: a in A
1523
True
1524
"""
1525
if not isinstance(x, AbelianGroupElement):
1526
return False
1527
if x.parent() is self:
1528
return True
1529
elif x in self.ambient_group():
1530
amb_inv = self.ambient_group().gens_orders()
1531
for a in xrange(len(amb_inv)):
1532
if amb_inv[a] == 0 and x.list()[a] != 0:
1533
for g in self._gens:
1534
if g.list()[a] == 0:
1535
continue
1536
if abs(x.list()[a]%g.list()[a]) < abs(x.list()[a]):
1537
if g.list()[a]*x.list()[a] < 0:
1538
x *= g**(x.list()[a]/g.list()[a])
1539
else:
1540
x *= g**((-1)*(x.list()[a]/g.list()[a]))
1541
if x.list()[a] == 0:
1542
break
1543
elif x.list()[a] != 0:
1544
for g in self._gens:
1545
if g.list()[a] == 0:
1546
continue
1547
if abs(x.list()[a]%g.list()[a])%abs(amb_inv[a]) < x.list()[a]%abs(amb_inv[a]):
1548
x *= g**((-1)*(x.list()[a]/g.list()[a]))
1549
if x.list()[a] == 0:
1550
break
1551
return x == x.parent()(1)
1552
1553
def ambient_group(self):
1554
"""
1555
Return the ambient group related to self.
1556
1557
OUTPUT:
1558
1559
A multiplicative Abelian group.
1560
1561
EXAMPLES::
1562
1563
sage: G.<a,b,c> = AbelianGroup([2,3,4])
1564
sage: H = G.subgroup([a, b^2])
1565
sage: H.ambient_group() is G
1566
True
1567
"""
1568
return self._ambient_group
1569
1570
def equals(left, right):
1571
"""
1572
Check whether ``left`` and ``right`` are the same (sub)group.
1573
1574
INPUT:
1575
1576
- ``right`` -- anything.
1577
1578
OUTPUT:
1579
1580
Boolean. If ``right`` is a subgroup, test whether ``left`` and
1581
``right`` are the same subset of the ambient group. If
1582
``right`` is not a subgroup, test whether they are isomorphic
1583
groups, see :meth:`~AbelianGroup_class.is_isomorphic`.
1584
1585
EXAMPLES::
1586
1587
sage: G = AbelianGroup(3, [2,3,4], names="abc"); G
1588
Multiplicative Abelian group isomorphic to C2 x C3 x C4
1589
sage: a,b,c = G.gens()
1590
sage: F = G.subgroup([a,b^2]); F
1591
Multiplicative Abelian subgroup isomorphic to C2 x C3 generated by {a, b^2}
1592
sage: F<G
1593
True
1594
1595
sage: A = AbelianGroup(1, [6])
1596
sage: A.subgroup(list(A.gens())) == A
1597
True
1598
1599
sage: G.<a,b> = AbelianGroup(2)
1600
sage: A = G.subgroup([a])
1601
sage: B = G.subgroup([b])
1602
sage: A.equals(B)
1603
False
1604
sage: A == B # sames as A.equals(B)
1605
False
1606
sage: A.is_isomorphic(B)
1607
True
1608
"""
1609
left_ambient = left.ambient_group()
1610
try:
1611
right_ambient = right.ambient_group()
1612
except AttributeError:
1613
# right is not a subgroup
1614
return left.is_isomorphic(right)
1615
if left_ambient is not right_ambient:
1616
return False
1617
return left <= right and right <= left
1618
1619
__eq__ = equals
1620
1621
def _repr_(self):
1622
"""
1623
Return a string representation
1624
1625
EXAMPLES::
1626
1627
sage: G.<a,b> = AbelianGroup(2)
1628
sage: G._repr_()
1629
'Multiplicative Abelian group isomorphic to Z x Z'
1630
sage: A = G.subgroup([a])
1631
sage: A._repr_()
1632
'Multiplicative Abelian subgroup isomorphic to Z generated by {a}'
1633
"""
1634
eldv = self.gens_orders()
1635
if self.is_trivial():
1636
return "Trivial Abelian subgroup"
1637
s = 'Multiplicative Abelian subgroup isomorphic to '
1638
s += self._group_notation(eldv)
1639
s += ' generated by '
1640
s += '{' + ', '.join(map(str, self.gens())) + '}'
1641
return s
1642
1643
def gens(self):
1644
"""
1645
Return the generators for this subgroup.
1646
1647
OUTPUT:
1648
1649
A tuple of group elements generating the subgroup.
1650
1651
EXAMPLES::
1652
1653
sage: G.<a,b> = AbelianGroup(2)
1654
sage: A = G.subgroup([a])
1655
sage: G.gens()
1656
(a, b)
1657
sage: A.gens()
1658
(a,)
1659
"""
1660
return self._gens
1661
1662
def gen(self, n):
1663
"""
1664
Return the nth generator of this subgroup.
1665
1666
EXAMPLE::
1667
1668
sage: G.<a,b> = AbelianGroup(2)
1669
sage: A = G.subgroup([a])
1670
sage: A.gen(0)
1671
a
1672
"""
1673
return self._gens[n]
1674
1675
1676