Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/groups/matrix_gps/finitely_generated.py
8815 views
1
"""
2
Finitely Generated Matrix Groups
3
4
This class is designed for computing with matrix groups defined by a
5
finite set of generating matrices.
6
7
EXAMPLES::
8
9
sage: F = GF(3)
10
sage: gens = [matrix(F,2, [1,0, -1,1]), matrix(F,2, [1,1,0,1])]
11
sage: G = MatrixGroup(gens)
12
sage: G.conjugacy_class_representatives()
13
(
14
[1 0] [0 1] [0 1] [0 2] [0 2] [0 1] [2 0]
15
[0 1], [2 1], [2 2], [1 1], [1 2], [2 0], [0 2]
16
)
17
18
The finitely generated matrix groups can also be constructed as
19
subgroups of matrix groups::
20
21
sage: SL2Z = SL(2,ZZ)
22
sage: S, T = SL2Z.gens()
23
sage: SL2Z.subgroup([T^2])
24
Matrix group over Integer Ring with 1 generators (
25
[1 2]
26
[0 1]
27
)
28
29
AUTHORS:
30
31
- William Stein: initial version
32
33
- David Joyner (2006-03-15): degree, base_ring, _contains_, list,
34
random, order methods; examples
35
36
- William Stein (2006-12): rewrite
37
38
- David Joyner (2007-12): Added invariant_generators (with Martin
39
Albrecht and Simon King)
40
41
- David Joyner (2008-08): Added module_composition_factors (interface
42
to GAP's MeatAxe implementation) and as_permutation_group (returns
43
isomorphic PermutationGroup).
44
45
- Simon King (2010-05): Improve invariant_generators by using GAP
46
for the construction of the Reynolds operator in Singular.
47
48
- Volker Braun (2013-1) port to new Parent, libGAP.
49
"""
50
51
##############################################################################
52
# Copyright (C) 2006 David Joyner and William Stein <[email protected]>
53
# Copyright (C) 2013 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.groups.group import Group
63
from sage.rings.all import ZZ
64
from sage.rings.integer import is_Integer
65
from sage.rings.ring import is_Ring
66
from sage.rings.finite_rings.constructor import is_FiniteField
67
from sage.interfaces.gap import gap
68
from sage.matrix.matrix import is_Matrix
69
from sage.matrix.matrix_space import MatrixSpace, is_MatrixSpace
70
from sage.matrix.all import matrix
71
from sage.misc.latex import latex
72
from sage.structure.sequence import Sequence
73
from sage.misc.cachefunc import cached_method
74
75
from sage.groups.matrix_gps.matrix_group import (
76
is_MatrixGroup, MatrixGroup_generic, MatrixGroup_gap )
77
from sage.groups.matrix_gps.group_element import (
78
is_MatrixGroupElement, MatrixGroupElement_generic, MatrixGroupElement_gap)
79
80
81
82
83
def normalize_square_matrices(matrices):
84
"""
85
Find a common space for all matrices.
86
87
OUTPUT:
88
89
A list of matrices, all elements of the same matrix space.
90
91
EXAMPLES::
92
93
sage: from sage.groups.matrix_gps.finitely_generated import normalize_square_matrices
94
sage: m1 = [[1,2],[3,4]]
95
sage: m2 = [2, 3, 4, 5]
96
sage: m3 = matrix(QQ, [[1/2,1/3],[1/4,1/5]])
97
sage: m4 = MatrixGroup(m3).gen(0)
98
sage: normalize_square_matrices([m1, m2, m3, m4])
99
[
100
[1 2] [2 3] [1/2 1/3] [1/2 1/3]
101
[3 4], [4 5], [1/4 1/5], [1/4 1/5]
102
]
103
"""
104
deg = []
105
gens = []
106
for m in matrices:
107
if is_MatrixGroupElement(m):
108
deg.append(m.parent().degree())
109
gens.append(m.matrix())
110
continue
111
if is_Matrix(m):
112
if not m.is_square():
113
raise TypeError('matrix must be square')
114
deg.append(m.ncols())
115
gens.append(m)
116
continue
117
try:
118
m = list(m)
119
except TypeError:
120
gens.append(m)
121
continue
122
if isinstance(m[0], (list, tuple)):
123
m = map(list, m)
124
degree = ZZ(len(m))
125
else:
126
degree, rem = ZZ(len(m)).sqrtrem()
127
if rem!=0:
128
raise ValueError('list of plain numbers must have square integer length')
129
deg.append(degree)
130
gens.append(matrix(degree, degree, m))
131
deg = set(deg)
132
if len(set(deg)) != 1:
133
raise ValueError('not all matrices have the same size')
134
gens = Sequence(gens, immutable=True)
135
MS = gens.universe()
136
if not is_MatrixSpace(MS):
137
raise TypeError('all generators must be matrices')
138
if MS.nrows() != MS.ncols():
139
raise ValueError('matrices must be square')
140
return gens
141
142
def QuaternionMatrixGroupGF3():
143
r"""
144
The quaternion group as a set of `2\times 2` matrices over `GF(3)`.
145
146
OUTPUT:
147
148
A matrix group consisting of `2\times 2` matrices with
149
elements from the finite field of order 3. The group is
150
the quaternion group, the nonabelian group of order 8 that
151
is not isomorphic to the group of symmetries of a square
152
(the dihedral group `D_4`).
153
154
.. note::
155
This group is most easily available via ``groups.matrix.QuaternionGF3()``.
156
157
EXAMPLES:
158
159
The generators are the matrix representations of the
160
elements commonly called `I` and `J`, while `K`
161
is the product of `I` and `J`. ::
162
163
sage: from sage.groups.matrix_gps.finitely_generated import QuaternionMatrixGroupGF3
164
sage: Q = QuaternionMatrixGroupGF3()
165
sage: Q.order()
166
8
167
sage: aye = Q.gens()[0]; aye
168
[1 1]
169
[1 2]
170
sage: jay = Q.gens()[1]; jay
171
[2 1]
172
[1 1]
173
sage: kay = aye*jay; kay
174
[0 2]
175
[1 0]
176
177
TESTS::
178
179
sage: groups.matrix.QuaternionGF3()
180
Matrix group over Finite Field of size 3 with 2 generators (
181
[1 1] [2 1]
182
[1 2], [1 1]
183
)
184
185
sage: Q = QuaternionMatrixGroupGF3()
186
sage: QP = Q.as_permutation_group()
187
sage: QP.is_isomorphic(QuaternionGroup())
188
True
189
sage: H = DihedralGroup(4)
190
sage: H.order()
191
8
192
sage: QP.is_abelian(), H.is_abelian()
193
(False, False)
194
sage: QP.is_isomorphic(H)
195
False
196
"""
197
from sage.rings.finite_rings.constructor import FiniteField
198
from sage.matrix.matrix_space import MatrixSpace
199
MS = MatrixSpace(FiniteField(3), 2)
200
aye = MS([1,1,1,2])
201
jay = MS([2,1,1,1])
202
return MatrixGroup([aye, jay])
203
204
def MatrixGroup(*gens, **kwds):
205
r"""
206
Return the matrix group with given generators.
207
208
INPUT:
209
210
- ``*gens`` -- matrices, or a single list/tuple/iterable of
211
matrices, or a matrix group.
212
213
- ``check`` -- boolean keyword argument (optional, default:
214
``True``). Whether to check that each matrix is invertible.
215
216
EXAMPLES::
217
218
sage: F = GF(5)
219
sage: gens = [matrix(F,2,[1,2, -1, 1]), matrix(F,2, [1,1, 0,1])]
220
sage: G = MatrixGroup(gens); G
221
Matrix group over Finite Field of size 5 with 2 generators (
222
[1 2] [1 1]
223
[4 1], [0 1]
224
)
225
226
In the second example, the generators are a matrix over
227
`\ZZ`, a matrix over a finite field, and the integer
228
`2`. Sage determines that they both canonically map to
229
matrices over the finite field, so creates that matrix group
230
there::
231
232
sage: gens = [matrix(2,[1,2, -1, 1]), matrix(GF(7), 2, [1,1, 0,1]), 2]
233
sage: G = MatrixGroup(gens); G
234
Matrix group over Finite Field of size 7 with 3 generators (
235
[1 2] [1 1] [2 0]
236
[6 1], [0 1], [0 2]
237
)
238
239
Each generator must be invertible::
240
241
sage: G = MatrixGroup([matrix(ZZ,2,[1,2,3,4])])
242
Traceback (most recent call last):
243
...
244
ValueError: each generator must be an invertible matrix
245
246
sage: F = GF(5); MS = MatrixSpace(F,2,2)
247
sage: MatrixGroup([MS.0])
248
Traceback (most recent call last):
249
...
250
ValueError: each generator must be an invertible matrix
251
sage: MatrixGroup([MS.0], check=False) # works formally but is mathematical nonsense
252
Matrix group over Finite Field of size 5 with 1 generators (
253
[1 0]
254
[0 0]
255
)
256
257
Some groups are not supported, or do not have much functionality
258
implemented::
259
260
sage: G = SL(0, QQ)
261
Traceback (most recent call last):
262
...
263
ValueError: the degree must be at least 1
264
265
sage: SL2C = SL(2, CC); SL2C
266
Special Linear Group of degree 2 over Complex Field with 53 bits of precision
267
sage: SL2C.gens()
268
Traceback (most recent call last):
269
...
270
AttributeError: 'LinearMatrixGroup_generic_with_category' object has no attribute 'gens'
271
"""
272
if isinstance(gens[-1], dict): # hack for unpickling
273
kwds.update(gens[-1])
274
gens = gens[:-1]
275
check = kwds.get('check', True)
276
if len(gens) == 1:
277
if isinstance(gens[0], (list, tuple)):
278
gens = list(gens[0])
279
else:
280
try:
281
gens = [g.matrix() for g in gens[0]]
282
except AttributeError:
283
pass
284
if len(gens) == 0:
285
raise ValueError('need at least one generator')
286
gens = normalize_square_matrices(gens)
287
if check and any(not g.is_invertible() for g in gens):
288
raise ValueError('each generator must be an invertible matrix')
289
MS = gens.universe()
290
base_ring = MS.base_ring()
291
degree = ZZ(MS.ncols()) # == MS.nrows()
292
from sage.libs.gap.libgap import libgap
293
try:
294
gap_gens = [libgap(matrix_gen) for matrix_gen in gens]
295
gap_group = libgap.Group(gap_gens)
296
return FinitelyGeneratedMatrixGroup_gap(degree, base_ring, gap_group)
297
except (TypeError, ValueError):
298
return FinitelyGeneratedMatrixGroup_generic(degree, base_ring, gens)
299
300
###################################################################
301
#
302
# Matrix group over a generic ring
303
#
304
###################################################################
305
306
class FinitelyGeneratedMatrixGroup_generic(MatrixGroup_generic):
307
308
def __init__(self, degree, base_ring, generator_matrices, category=None):
309
"""
310
Matrix group generated by a finite number of matrices.
311
312
EXAMPLES::
313
314
sage: m1 = matrix(SR, [[1,2],[3,4]])
315
sage: m2 = matrix(SR, [[1,3],[-1,0]])
316
sage: G = MatrixGroup(m1, m2)
317
sage: TestSuite(G).run()
318
sage: type(G)
319
<class 'sage.groups.matrix_gps.finitely_generated.FinitelyGeneratedMatrixGroup_generic_with_category'>
320
321
sage: from sage.groups.matrix_gps.finitely_generated import \
322
....: FinitelyGeneratedMatrixGroup_generic
323
sage: G = FinitelyGeneratedMatrixGroup_generic(2, QQ, [matrix(QQ,[[1,2],[3,4]])])
324
sage: G.gens()
325
(
326
[1 2]
327
[3 4]
328
)
329
"""
330
self._gens_matrix = generator_matrices
331
MatrixGroup_generic.__init__(self, degree, base_ring, category=category)
332
333
def __cmp__(self, other):
334
"""
335
Implement comparison.
336
337
EXAMPLES::
338
339
sage: m1 = matrix(SR, [[1,2],[3,4]])
340
sage: m2 = matrix(SR, [[1,3],[-1,0]])
341
sage: cmp(MatrixGroup(m1), MatrixGroup(m1))
342
0
343
sage: abs(cmp(MatrixGroup(m1), MatrixGroup(m1.change_ring(QQ))))
344
1
345
sage: abs(cmp(MatrixGroup(m1), MatrixGroup(m2)))
346
1
347
sage: abs(cmp(MatrixGroup(m1, m2), MatrixGroup(m2, m1)))
348
1
349
"""
350
c = super(FinitelyGeneratedMatrixGroup_generic, self).__cmp__(other)
351
if c != 0:
352
return c
353
return cmp(self._gens_matrix, other._gens_matrix)
354
355
@cached_method
356
def gens(self):
357
"""
358
Return the generators of the matrix group.
359
360
EXAMPLES::
361
362
sage: F = GF(3); MS = MatrixSpace(F,2,2)
363
sage: gens = [MS([[1,0],[0,1]]), MS([[1,1],[0,1]])]
364
sage: G = MatrixGroup(gens)
365
sage: gens[0] in G
366
True
367
sage: gens = G.gens()
368
sage: gens[0] in G
369
True
370
sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])]
371
372
sage: F = GF(5); MS = MatrixSpace(F,2,2)
373
sage: G = MatrixGroup([MS(1), MS([1,2,3,4])])
374
sage: G
375
Matrix group over Finite Field of size 5 with 2 generators (
376
[1 0] [1 2]
377
[0 1], [3 4]
378
)
379
sage: G.gens()
380
(
381
[1 0] [1 2]
382
[0 1], [3 4]
383
)
384
"""
385
return tuple(self.element_class(self, x, check=False, convert=False)
386
for x in self._gens_matrix)
387
388
def gen(self, i):
389
"""
390
Return the `i`-th generator
391
392
OUTPUT:
393
394
The `i`-th generator of the group.
395
396
EXAMPLES::
397
398
sage: H = GL(2, GF(3))
399
sage: h1, h2 = H([[1,0],[2,1]]), H([[1,1],[0,1]])
400
sage: G = H.subgroup([h1, h2])
401
sage: G.gen(0)
402
[1 0]
403
[2 1]
404
sage: G.gen(0).matrix() == h1.matrix()
405
True
406
"""
407
return self.gens()[i]
408
409
def ngens(self):
410
"""
411
Return the number of generators
412
413
OUTPUT:
414
415
An integer. The number of generators.
416
417
EXAMPLES::
418
419
sage: H = GL(2, GF(3))
420
sage: h1, h2 = H([[1,0],[2,1]]), H([[1,1],[0,1]])
421
sage: G = H.subgroup([h1, h2])
422
sage: G.ngens()
423
2
424
"""
425
return len(self._gens_matrix)
426
427
def _test_matrix_generators(self, **options):
428
"""
429
EXAMPLES::
430
431
sage: m1 = matrix(SR, [[1,2],[3,4]])
432
sage: m2 = matrix(SR, [[1,3],[-1,0]])
433
sage: G = MatrixGroup(m1, m2)
434
sage: G._test_matrix_generators()
435
"""
436
tester = self._tester(**options)
437
for g,h in zip(self.gens(), MatrixGroup(self.gens()).gens()):
438
tester.assertEqual(g.matrix(), h.matrix())
439
440
###################################################################
441
#
442
# Matrix group over a ring that GAP understands
443
#
444
###################################################################
445
446
class FinitelyGeneratedMatrixGroup_gap(MatrixGroup_gap):
447
"""
448
Matrix group generated by a finite number of matrices.
449
450
EXAMPLES::
451
452
sage: m1 = matrix(GF(11), [[1,2],[3,4]])
453
sage: m2 = matrix(GF(11), [[1,3],[10,0]])
454
sage: G = MatrixGroup(m1, m2); G
455
Matrix group over Finite Field of size 11 with 2 generators (
456
[1 2] [ 1 3]
457
[3 4], [10 0]
458
)
459
sage: type(G)
460
<class 'sage.groups.matrix_gps.finitely_generated.FinitelyGeneratedMatrixGroup_gap_with_category'>
461
sage: TestSuite(G).run()
462
"""
463
464
def __reduce__(self):
465
"""
466
Implement pickling.
467
468
EXAMPLES::
469
470
sage: m1 = matrix(QQ, [[1,2],[3,4]])
471
sage: m2 = matrix(QQ, [[1,3],[-1,0]])
472
sage: loads(MatrixGroup(m1, m2).dumps())
473
Matrix group over Rational Field with 2 generators (
474
[1 2] [ 1 3]
475
[3 4], [-1 0]
476
)
477
"""
478
return (MatrixGroup,
479
tuple(g.matrix() for g in self.gens()) + ({'check':False},))
480
481
def __cmp__(self, other):
482
"""
483
Implement comparison.
484
485
EXAMPLES::
486
487
sage: m1 = matrix(QQ, [[1,2],[3,4]])
488
sage: m2 = matrix(QQ, [[1,3],[-1,0]])
489
sage: cmp(MatrixGroup(m1), MatrixGroup(m1))
490
0
491
sage: abs(cmp(MatrixGroup(m1), MatrixGroup(m2)))
492
1
493
sage: abs(cmp(MatrixGroup(m1, m2), MatrixGroup(m2, m1)))
494
1
495
496
sage: G = GL(2, GF(3))
497
sage: H = G.as_matrix_group()
498
sage: cmp(H, G), cmp(G, H)
499
(0, 0)
500
sage: H == G, G == H
501
(True, True)
502
"""
503
c = super(FinitelyGeneratedMatrixGroup_gap, self).__cmp__(other)
504
if c != 0:
505
return c
506
try:
507
other_ngens = other.ngens
508
other_gen = other.gen
509
except AttributeError:
510
return 1
511
n = self.ngens()
512
m = other_ngens()
513
if n != m:
514
return cmp(n, m)
515
for i in range(n):
516
g = self.gen(i)
517
h = other_gen(i)
518
c = cmp(g.gap(), h.gap())
519
if c != 0:
520
return c
521
return 0
522
523
def as_permutation_group(self, algorithm=None):
524
r"""
525
Return a permutation group representation for the group.
526
527
In most cases occurring in practice, this is a permutation
528
group of minimal degree (the degree begin determined from
529
orbits under the group action). When these orbits are hard to
530
compute, the procedure can be time-consuming and the degree
531
may not be minimal.
532
533
INPUT:
534
535
- ``algorithm`` -- ``None`` or ``'smaller'``. In the latter
536
case, try harder to find a permutation representation of
537
small degree.
538
539
OUTPUT:
540
541
A permutation group isomorphic to ``self``. The
542
``algorithm='smaller'`` option tries to return an isomorphic
543
group of low degree, but is not guaranteed to find the
544
smallest one.
545
546
EXAMPLES::
547
548
sage: MS = MatrixSpace(GF(2), 5, 5)
549
sage: A = MS([[0,0,0,0,1],[0,0,0,1,0],[0,0,1,0,0],[0,1,0,0,0],[1,0,0,0,0]])
550
sage: G = MatrixGroup([A])
551
sage: G.as_permutation_group()
552
Permutation Group with generators [(1,2)]
553
sage: MS = MatrixSpace( GF(7), 12, 12)
554
sage: GG = gap("ImfMatrixGroup( 12, 3 )")
555
sage: GG.GeneratorsOfGroup().Length()
556
3
557
sage: g1 = MS(eval(str(GG.GeneratorsOfGroup()[1]).replace("\n","")))
558
sage: g2 = MS(eval(str(GG.GeneratorsOfGroup()[2]).replace("\n","")))
559
sage: g3 = MS(eval(str(GG.GeneratorsOfGroup()[3]).replace("\n","")))
560
sage: G = MatrixGroup([g1, g2, g3])
561
sage: G.cardinality()
562
21499084800
563
sage: set_random_seed(0); current_randstate().set_seed_gap()
564
sage: P = G.as_permutation_group()
565
sage: P.cardinality()
566
21499084800
567
sage: P.degree() # random output
568
144
569
sage: set_random_seed(3); current_randstate().set_seed_gap()
570
sage: Psmaller = G.as_permutation_group(algorithm="smaller")
571
sage: Psmaller.cardinality()
572
21499084800
573
sage: Psmaller.degree() # random output
574
108
575
576
In this case, the "smaller" option returned an isomorphic group of
577
lower degree. The above example used GAP's library of irreducible
578
maximal finite ("imf") integer matrix groups to construct the
579
MatrixGroup G over GF(7). The section "Irreducible Maximal Finite
580
Integral Matrix Groups" in the GAP reference manual has more
581
details.
582
"""
583
# Note that the output of IsomorphismPermGroup() depends on
584
# memory locations and will change if you change the order of
585
# doctests and/or architecture
586
from sage.groups.perm_gps.permgroup import PermutationGroup
587
if not self.is_finite():
588
raise NotImplementedError, "Group must be finite."
589
n = self.degree()
590
MS = MatrixSpace(self.base_ring(), n, n)
591
mats = [] # initializing list of mats by which the gens act on self
592
for g in self.gens():
593
p = MS(g.matrix())
594
m = p.rows()
595
mats.append(m)
596
mats_str = str(gap([[list(r) for r in m] for m in mats]))
597
gap.eval("iso:=IsomorphismPermGroup(Group("+mats_str+"))")
598
if algorithm == "smaller":
599
gap.eval("small:= SmallerDegreePermutationRepresentation( Image( iso ) );")
600
C = gap("Image( small )")
601
else:
602
C = gap("Image( iso )")
603
return PermutationGroup(gap_group=C)
604
605
def module_composition_factors(self, algorithm=None):
606
r"""
607
Return a list of triples consisting of [base field, dimension,
608
irreducibility], for each of the Meataxe composition factors
609
modules. The ``algorithm="verbose"`` option returns more information,
610
but in Meataxe notation.
611
612
EXAMPLES::
613
614
sage: F=GF(3);MS=MatrixSpace(F,4,4)
615
sage: M=MS(0)
616
sage: M[0,1]=1;M[1,2]=1;M[2,3]=1;M[3,0]=1
617
sage: G = MatrixGroup([M])
618
sage: G.module_composition_factors()
619
[(Finite Field of size 3, 1, True),
620
(Finite Field of size 3, 1, True),
621
(Finite Field of size 3, 2, True)]
622
sage: F = GF(7); MS = MatrixSpace(F,2,2)
623
sage: gens = [MS([[0,1],[-1,0]]),MS([[1,1],[2,3]])]
624
sage: G = MatrixGroup(gens)
625
sage: G.module_composition_factors()
626
[(Finite Field of size 7, 2, True)]
627
628
Type ``G.module_composition_factors(algorithm='verbose')`` to get a
629
more verbose version.
630
631
For more on MeatAxe notation, see
632
http://www.gap-system.org/Manuals/doc/ref/chap69.html
633
"""
634
from sage.misc.sage_eval import sage_eval
635
F = self.base_ring()
636
if not(F.is_finite()):
637
raise NotImplementedError, "Base ring must be finite."
638
q = F.cardinality()
639
gens = self.gens()
640
n = self.degree()
641
MS = MatrixSpace(F,n,n)
642
mats = [] # initializing list of mats by which the gens act on self
643
W = self.matrix_space().row_space()
644
for g in gens:
645
p = MS(g.matrix())
646
m = p.rows()
647
mats.append(m)
648
mats_str = str(gap([[list(r) for r in m] for m in mats]))
649
gap.eval("M:=GModuleByMats("+mats_str+", GF("+str(q)+"))")
650
gap.eval("MCFs := MTX.CompositionFactors( M )")
651
N = eval(gap.eval("Length(MCFs)"))
652
if algorithm == "verbose":
653
print gap.eval('MCFs')+"\n"
654
L = []
655
for i in range(1,N+1):
656
gap.eval("MCF := MCFs[%s]"%i)
657
L.append(tuple([sage_eval(gap.eval("MCF.field")),
658
eval(gap.eval("MCF.dimension")),
659
sage_eval(gap.eval("MCF.IsIrreducible")) ]))
660
return sorted(L)
661
662
def invariant_generators(self):
663
r"""
664
Return invariant ring generators.
665
666
Computes generators for the polynomial ring
667
`F[x_1,\ldots,x_n]^G`, where `G` in `GL(n,F)` is a finite matrix
668
group.
669
670
In the "good characteristic" case the polynomials returned
671
form a minimal generating set for the algebra of `G`-invariant
672
polynomials. In the "bad" case, the polynomials returned
673
are primary and secondary invariants, forming a not
674
necessarily minimal generating set for the algebra of
675
`G`-invariant polynomials.
676
677
ALGORITHM:
678
679
Wraps Singular's ``invariant_algebra_reynolds`` and ``invariant_ring``
680
in ``finvar.lib``.
681
682
EXAMPLES::
683
684
sage: F = GF(7); MS = MatrixSpace(F,2,2)
685
sage: gens = [MS([[0,1],[-1,0]]),MS([[1,1],[2,3]])]
686
sage: G = MatrixGroup(gens)
687
sage: G.invariant_generators()
688
[x1^7*x2 - x1*x2^7, x1^12 - 2*x1^9*x2^3 - x1^6*x2^6 + 2*x1^3*x2^9 + x2^12, x1^18 + 2*x1^15*x2^3 + 3*x1^12*x2^6 + 3*x1^6*x2^12 - 2*x1^3*x2^15 + x2^18]
689
sage: q = 4; a = 2
690
sage: MS = MatrixSpace(QQ, 2, 2)
691
sage: gen1 = [[1/a,(q-1)/a],[1/a, -1/a]]; gen2 = [[1,0],[0,-1]]; gen3 = [[-1,0],[0,1]]
692
sage: G = MatrixGroup([MS(gen1),MS(gen2),MS(gen3)])
693
sage: G.cardinality()
694
12
695
sage: G.invariant_generators()
696
[x1^2 + 3*x2^2, x1^6 + 15*x1^4*x2^2 + 15*x1^2*x2^4 + 33*x2^6]
697
sage: F = GF(5); MS = MatrixSpace(F,2,2)
698
sage: gens = [MS([[1,2],[-1,1]]),MS([[1,1],[-1,1]])]
699
sage: G = MatrixGroup(gens)
700
sage: G.invariant_generators() # long time (67s on sage.math, 2012)
701
[x1^20 + x1^16*x2^4 + x1^12*x2^8 + x1^8*x2^12 + x1^4*x2^16 + x2^20, x1^20*x2^4 + x1^16*x2^8 + x1^12*x2^12 + x1^8*x2^16 + x1^4*x2^20]
702
sage: F=CyclotomicField(8)
703
sage: z=F.gen()
704
sage: a=z+1/z
705
sage: b=z^2
706
sage: MS=MatrixSpace(F,2,2)
707
sage: g1=MS([[1/a,1/a],[1/a,-1/a]])
708
sage: g2=MS([[1,0],[0,b]])
709
sage: g3=MS([[b,0],[0,1]])
710
sage: G=MatrixGroup([g1,g2,g3])
711
sage: G.invariant_generators() # long time (12s on sage.math, 2011)
712
[x1^8 + 14*x1^4*x2^4 + x2^8,
713
x1^24 + 10626/1025*x1^20*x2^4 + 735471/1025*x1^16*x2^8 + 2704156/1025*x1^12*x2^12 + 735471/1025*x1^8*x2^16 + 10626/1025*x1^4*x2^20 + x2^24]
714
715
AUTHORS:
716
717
- David Joyner, Simon King and Martin Albrecht.
718
719
REFERENCES:
720
721
- Singular reference manual
722
723
- B. Sturmfels, "Algorithms in invariant theory", Springer-Verlag,
724
1993.
725
726
- S. King, "Minimal Generating Sets of non-modular invariant
727
rings of finite groups", :arxiv:`math/0703035`.
728
"""
729
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
730
from sage.interfaces.singular import singular
731
gens = self.gens()
732
singular.LIB("finvar.lib")
733
n = self.degree() #len((gens[0].matrix()).rows())
734
F = self.base_ring()
735
q = F.characteristic()
736
## test if the field is admissible
737
if F.gen()==1: # we got the rationals or GF(prime)
738
FieldStr = str(F.characteristic())
739
elif hasattr(F,'polynomial'): # we got an algebraic extension
740
if len(F.gens())>1:
741
raise NotImplementedError("can only deal with finite fields and (simple algebraic extensions of) the rationals")
742
FieldStr = '(%d,%s)'%(F.characteristic(),str(F.gen()))
743
else: # we have a transcendental extension
744
FieldStr = '(%d,%s)'%(F.characteristic(),','.join([str(p) for p in F.gens()]))
745
746
## Setting Singular's variable names
747
## We need to make sure that field generator and variables get different names.
748
if str(F.gen())[0]=='x':
749
VarStr = 'y'
750
else:
751
VarStr = 'x'
752
VarNames='('+','.join((VarStr+str(i+1) for i in range(n)))+')'
753
R=singular.ring(FieldStr,VarNames,'dp')
754
if hasattr(F,'polynomial') and F.gen()!=1: # we have to define minpoly
755
singular.eval('minpoly = '+str(F.polynomial()).replace('x',str(F.gen())))
756
A = [singular.matrix(n,n,str((x.matrix()).list())) for x in gens]
757
Lgens = ','.join((x.name() for x in A))
758
PR = PolynomialRing(F,n,[VarStr+str(i) for i in range(1,n+1)])
759
760
if q == 0 or (q > 0 and self.cardinality()%q != 0):
761
from sage.all import Integer, Matrix
762
try:
763
elements = [ g.matrix() for g in self.list() ]
764
except (TypeError,ValueError):
765
elements
766
if elements is not None:
767
ReyName = 't'+singular._next_var_name()
768
singular.eval('matrix %s[%d][%d]'%(ReyName,self.cardinality(),n))
769
for i in range(1,self.cardinality()+1):
770
M = Matrix(elements[i-1],F)
771
D = [{} for foobar in range(self.degree())]
772
for x,y in M.dict().items():
773
D[x[0]][x[1]] = y
774
for row in range(self.degree()):
775
for t in D[row].items():
776
singular.eval('%s[%d,%d]=%s[%d,%d]+(%s)*var(%d)'
777
%(ReyName,i,row+1,ReyName,i,row+1, repr(t[1]),t[0]+1))
778
foobar = singular(ReyName)
779
IRName = 't'+singular._next_var_name()
780
singular.eval('matrix %s = invariant_algebra_reynolds(%s)'%(IRName,ReyName))
781
else:
782
ReyName = 't'+singular._next_var_name()
783
singular.eval('list %s=group_reynolds((%s))'%(ReyName,Lgens))
784
IRName = 't'+singular._next_var_name()
785
singular.eval('matrix %s = invariant_algebra_reynolds(%s[1])'%(IRName,ReyName))
786
787
OUT = [singular.eval(IRName+'[1,%d]'%(j))
788
for j in range(1,1+singular('ncols('+IRName+')'))]
789
return [PR(gen) for gen in OUT]
790
if self.cardinality()%q == 0:
791
PName = 't'+singular._next_var_name()
792
SName = 't'+singular._next_var_name()
793
singular.eval('matrix %s,%s=invariant_ring(%s)'%(PName,SName,Lgens))
794
OUT = [
795
singular.eval(PName+'[1,%d]'%(j))
796
for j in range(1,1+singular('ncols('+PName+')'))
797
] + [
798
singular.eval(SName+'[1,%d]'%(j)) for j in range(2,1+singular('ncols('+SName+')'))
799
]
800
return [PR(gen) for gen in OUT]
801
802
803