passagemath-groups 10.6.45__cp314-cp314-macosx_13_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. passagemath_groups/.dylibs/libgap.10.dylib +0 -0
  2. passagemath_groups/.dylibs/libgmp.10.dylib +0 -0
  3. passagemath_groups/.dylibs/libreadline.8.2.dylib +0 -0
  4. passagemath_groups/.dylibs/libz.1.3.1.dylib +0 -0
  5. passagemath_groups/__init__.py +3 -0
  6. passagemath_groups-10.6.45.dist-info/METADATA +113 -0
  7. passagemath_groups-10.6.45.dist-info/RECORD +40 -0
  8. passagemath_groups-10.6.45.dist-info/WHEEL +6 -0
  9. passagemath_groups-10.6.45.dist-info/top_level.txt +3 -0
  10. sage/all__sagemath_groups.py +21 -0
  11. sage/geometry/all__sagemath_groups.py +1 -0
  12. sage/geometry/palp_normal_form.cpython-314-darwin.so +0 -0
  13. sage/geometry/palp_normal_form.pyx +401 -0
  14. sage/groups/abelian_gps/all.py +25 -0
  15. sage/groups/all.py +5 -0
  16. sage/groups/all__sagemath_groups.py +32 -0
  17. sage/groups/artin.py +1074 -0
  18. sage/groups/braid.py +3806 -0
  19. sage/groups/cactus_group.py +1001 -0
  20. sage/groups/cubic_braid.py +2052 -0
  21. sage/groups/finitely_presented.py +1896 -0
  22. sage/groups/finitely_presented_catalog.py +27 -0
  23. sage/groups/finitely_presented_named.py +592 -0
  24. sage/groups/fqf_orthogonal.py +579 -0
  25. sage/groups/free_group.py +944 -0
  26. sage/groups/group_exp.py +360 -0
  27. sage/groups/group_semidirect_product.py +504 -0
  28. sage/groups/kernel_subgroup.py +231 -0
  29. sage/groups/lie_gps/all.py +1 -0
  30. sage/groups/lie_gps/catalog.py +8 -0
  31. sage/groups/lie_gps/nilpotent_lie_group.py +945 -0
  32. sage/groups/misc_gps/all.py +1 -0
  33. sage/groups/misc_gps/misc_groups.py +11 -0
  34. sage/groups/misc_gps/misc_groups_catalog.py +33 -0
  35. sage/groups/raag.py +866 -0
  36. sage/groups/semimonomial_transformations/all.py +1 -0
  37. sage/groups/semimonomial_transformations/semimonomial_transformation.cpython-314-darwin.so +0 -0
  38. sage/groups/semimonomial_transformations/semimonomial_transformation.pxd +9 -0
  39. sage/groups/semimonomial_transformations/semimonomial_transformation.pyx +346 -0
  40. sage/groups/semimonomial_transformations/semimonomial_transformation_group.py +512 -0
@@ -0,0 +1,579 @@
1
+ # sage_setup: distribution = sagemath-groups
2
+ r"""
3
+ Orthogonal Groups of Torsion Quadratic Forms
4
+
5
+ The orthogonal group of a torsion quadratic module `T`
6
+ consists of all linear self-maps of `T` which preserve
7
+ the torsion quadratic form.
8
+
9
+ EXAMPLES::
10
+
11
+ sage: L = IntegralLattice("A2").twist(2) # needs sage.graphs
12
+ sage: T = L.discriminant_group() # needs sage.graphs
13
+ sage: Oq = T.orthogonal_group() # needs sage.graphs
14
+
15
+ The isometries act on elements of their domain::
16
+
17
+ sage: g = Oq(matrix(ZZ, 2, [0, 3, 1, 2])) # needs sage.graphs
18
+ sage: T.gen(0) * g # needs sage.graphs
19
+ (0, 3)
20
+
21
+ Isometries are represented with respect to
22
+ the Smith form generators of `T`::
23
+
24
+ sage: # needs sage.graphs
25
+ sage: L = IntegralLattice("A2").twist(2).direct_sum(IntegralLattice('U'))
26
+ sage: T = L.discriminant_group().normal_form()
27
+ sage: OT = T.orthogonal_group()
28
+ sage: g = matrix(2, 2, [1, 3, 1, 2])
29
+ sage: g = OT(g)
30
+ sage: g
31
+ [1 3]
32
+ [1 2]
33
+ sage: sf = T.smith_form_gens()
34
+ sage: matrix([s * g for s in T.smith_form_gens()])
35
+ [1 3]
36
+ [1 2]
37
+
38
+ AUTHORS:
39
+
40
+ - Simon Brandhorst (2020-01-08): initial version
41
+ """
42
+
43
+ # ****************************************************************************
44
+ # Copyright (C) 2020 Simon Brandhorst <sbrandhorst@web.de>
45
+ #
46
+ # This program is free software: you can redistribute it and/or modify
47
+ # it under the terms of the GNU General Public License as published by
48
+ # the Free Software Foundation, either version 2 of the License, or
49
+ # (at your option) any later version.
50
+ # https://www.gnu.org/licenses/
51
+ # ****************************************************************************
52
+ from sage.libs.gap.libgap import libgap
53
+ from sage.groups.abelian_gps.abelian_aut import AbelianGroupAutomorphismGroup_subgroup, AbelianGroupAutomorphism, AbelianGroupAutomorphismGroup_gap
54
+ from sage.modules.torsion_quadratic_module import TorsionQuadraticModule
55
+ from sage.rings.integer_ring import ZZ
56
+ from sage.matrix.constructor import matrix
57
+ from sage.categories.action import Action
58
+
59
+
60
+ class FqfIsometry(AbelianGroupAutomorphism):
61
+ r"""
62
+ Isometry of a finite quadratic/bilinear form.
63
+
64
+ INPUT:
65
+
66
+ - ``parent`` -- the parent :class:`~FqfOrthogonalGroup`
67
+ - ``x`` -- a libgap element
68
+ - ``check`` -- boolean (default: ``True``)
69
+
70
+ EXAMPLES::
71
+
72
+ sage: q = matrix.diagonal([2/3])
73
+ sage: q = TorsionQuadraticForm(q)
74
+ sage: G = q.orthogonal_group()
75
+ sage: g = G(matrix(ZZ, 1, [2]))
76
+ sage: g
77
+ [2]
78
+ """
79
+
80
+ def _repr_(self):
81
+ r"""
82
+ Return the string representation of ``self``.
83
+
84
+ EXAMPLES::
85
+
86
+ sage: q = matrix.diagonal([2/3, 4/3])
87
+ sage: q = TorsionQuadraticForm(q)
88
+ sage: G = q.orthogonal_group()
89
+ sage: g = G.one()
90
+ sage: g
91
+ [1 0]
92
+ [0 1]
93
+ """
94
+ return str(self.matrix())
95
+
96
+ def __call__(self, x):
97
+ r"""
98
+ Return the image of ``x`` under ``self``.
99
+
100
+ EXAMPLES::
101
+
102
+ sage: q = matrix.diagonal([2/3, 4/3])
103
+ sage: q = TorsionQuadraticForm(q)
104
+ sage: G = q.orthogonal_group()
105
+ sage: g = G(matrix(ZZ, 2, [1, 0, 0, 2]))
106
+ sage: g
107
+ [1 0]
108
+ [0 2]
109
+ sage: x = q.0
110
+ sage: g(x)
111
+ (1, 0)
112
+ """
113
+ if x in self.parent().invariant_form():
114
+ return x * self
115
+ else:
116
+ return AbelianGroupAutomorphism.__call__(self, x)
117
+
118
+
119
+ class FqfOrthogonalGroup(AbelianGroupAutomorphismGroup_subgroup):
120
+ r"""
121
+ Return a group of isometries of this torsion quadratic form.
122
+
123
+ Do not call this class directly instead use
124
+ :meth:`sage.modules.torsion_quadratic_module.orthogonal_group`.
125
+
126
+ INPUT:
127
+
128
+ - ``T`` -- a non degenerate torsion quadratic module
129
+
130
+ EXAMPLES::
131
+
132
+ sage: q = matrix.diagonal(QQ, [2/3, 2/3, 4/3])
133
+ sage: T = TorsionQuadraticForm(q)
134
+ sage: T
135
+ Finite quadratic module over Integer Ring with invariants (3, 3, 3)
136
+ Gram matrix of the quadratic form with values in Q/2Z:
137
+ [2/3 0 0]
138
+ [ 0 2/3 0]
139
+ [ 0 0 4/3]
140
+ sage: T.orthogonal_group()
141
+ Group of isometries of
142
+ Finite quadratic module over Integer Ring with invariants (3, 3, 3)
143
+ Gram matrix of the quadratic form with values in Q/2Z:
144
+ [2/3 0 0]
145
+ [ 0 2/3 0]
146
+ [ 0 0 4/3]
147
+ generated by 3 elements
148
+ sage: q = matrix.diagonal(QQ, [3/2, 1/4, 1/4])
149
+ sage: T = TorsionQuadraticForm(q)
150
+ sage: T.orthogonal_group().order()
151
+ 8
152
+
153
+ Action on an invariant subquotient::
154
+
155
+ sage: T = TorsionQuadraticForm(matrix.diagonal([2/9] + [2/27]))
156
+ sage: S1 = 3 * T
157
+ sage: S2 = 9 * T
158
+ sage: Q = S1/S2
159
+ sage: G = T.orthogonal_group()
160
+ sage: g = G(matrix(ZZ, 2, [8, 0, 0, 1]))
161
+ sage: Q.1 * g
162
+ (0, 1)
163
+ """
164
+ Element = FqfIsometry
165
+
166
+ def __init__(self, ambient, gens, fqf, check=False):
167
+ r"""
168
+ TESTS::
169
+
170
+ sage: q = matrix.diagonal(QQ, [2/3, 2/3, 4/3])
171
+ sage: T = TorsionQuadraticForm(q)
172
+ sage: Oq = T.orthogonal_group()
173
+ sage: TestSuite(Oq).run()
174
+ """
175
+ # We act on the smith form generators
176
+ # because they are independent
177
+ if not isinstance(fqf, TorsionQuadraticModule):
178
+ raise TypeError("input must be a torsion quadratic module")
179
+ if not isinstance(ambient, AbelianGroupAutomorphismGroup_gap):
180
+ raise TypeError("input must be a torsion quadratic module")
181
+ if not fqf.invariants() == ambient.domain().gens_orders():
182
+ raise ValueError("invariants of the abelian groups do not match")
183
+ gens = [ambient(g) for g in gens]
184
+ self._invariant_form = fqf
185
+ AbelianGroupAutomorphismGroup_subgroup.__init__(self, ambient, gens)
186
+ if check and any(not self._preserves_form(g) for g in self.gens()):
187
+ raise ValueError("a generator does not preserve the quadratic form")
188
+
189
+ def invariant_form(self):
190
+ r"""
191
+ Return the torsion quadratic form left invariant.
192
+
193
+ EXAMPLES::
194
+
195
+ sage: q = matrix.diagonal(QQ, [2/3])
196
+ sage: T = TorsionQuadraticForm(q)
197
+ sage: Oq = T.orthogonal_group()
198
+ sage: Oq.invariant_form() is T
199
+ True
200
+ """
201
+ return self._invariant_form
202
+
203
+ def _element_constructor_(self, x, check=True):
204
+ r"""
205
+ Construct an element from ``x`` and handle conversions.
206
+
207
+ INPUT:
208
+
209
+ - ``x`` -- something that converts in can be:
210
+
211
+ * a libgap element
212
+ * an integer matrix in the covering matrix ring
213
+ * a class:`sage.modules.fg_pid.fgp_morphism.FGP_Morphism`
214
+ defining an automorphism -- the domain of ``x`` must have
215
+ invariants equal to ``self.domain().gens_orders()``
216
+ * something that acts on the invariant form module
217
+
218
+ EXAMPLES::
219
+
220
+ sage: # needs sage.graphs
221
+ sage: L = IntegralLattice("A2").twist(2).direct_sum(IntegralLattice("A2"))
222
+ sage: q = L.discriminant_group()
223
+ sage: OL = L.orthogonal_group()
224
+ sage: Oq = q.orthogonal_group()
225
+ sage: f = matrix(ZZ, 2, [0, 1, -1, -1])
226
+ sage: f = matrix.block_diagonal([f, f])
227
+ sage: f = OL(f)
228
+ sage: fbar = Oq(f)
229
+ sage: fbar
230
+ [1 3]
231
+ [3 4]
232
+
233
+ Note that the following does not work since it may lead to ambiguities, see :issue:`30669`::
234
+
235
+ sage: Oq(f.matrix()) # needs sage.graphs
236
+ Traceback (most recent call last):
237
+ ...
238
+ ValueError: ...
239
+
240
+ But a matrix in the covering works::
241
+
242
+ sage: fbar == Oq(fbar.matrix()) # needs sage.graphs
243
+ True
244
+
245
+ TESTS::
246
+
247
+ sage: # needs sage.graphs
248
+ sage: all(x*f==x*fbar for x in q.gens())
249
+ True
250
+ sage: L = IntegralLattice("A2").twist(3)
251
+ sage: OL = L.orthogonal_group()
252
+ sage: assert OL(OL.0.matrix()) == OL.0
253
+ sage: q = L.discriminant_group()
254
+ sage: Oq = q.orthogonal_group()
255
+ sage: assert Oq(Oq.0.matrix()) == Oq.0
256
+ """
257
+ from sage.libs.gap.element import GapElement
258
+ if not isinstance(x, GapElement):
259
+ try:
260
+ # if there is an action try that
261
+ gen = self.invariant_form().smith_form_gens()
262
+ x = matrix(ZZ, [(g*x).vector() for g in gen])
263
+ except TypeError:
264
+ pass
265
+ f = AbelianGroupAutomorphismGroup_subgroup._element_constructor_(self, x, check=True)
266
+ if check:
267
+ # double check that the form is preserved
268
+ # this is expensive
269
+ if not self._preserves_form(f):
270
+ raise ValueError("not an isometry")
271
+ return f
272
+
273
+ def _preserves_form(self, f):
274
+ r"""
275
+ Return if ``f`` preserves the form.
276
+
277
+ INPUT:
278
+
279
+ - ``f`` -- something that acts on the domain
280
+
281
+ EXAMPLES::
282
+
283
+ sage: T = TorsionQuadraticForm(matrix([1/4]))
284
+ sage: G = T.orthogonal_group()
285
+ sage: g = G.gen(0)
286
+ sage: G._preserves_form(g)
287
+ True
288
+ """
289
+ g = self.invariant_form().smith_form_gens()
290
+ gf = tuple(h*f for h in g)
291
+ n = len(g)
292
+ for i in range(n):
293
+ if gf[i].q() != g[i].q():
294
+ return False
295
+ for j in range(i+1, n):
296
+ if g[i].b(g[j]) != gf[i].b(gf[j]):
297
+ return False
298
+ return True
299
+
300
+ def _get_action_(self, S, op, self_on_left):
301
+ r"""
302
+ Provide the coercion system with an action.
303
+
304
+ EXAMPLES::
305
+
306
+ sage: q = matrix.diagonal([2/3, 4/3])
307
+ sage: q = TorsionQuadraticForm(q)
308
+ sage: G = q.orthogonal_group()
309
+ sage: G._get_action_(q, operator.mul, False)
310
+ Right action by Group of isometries of
311
+ Finite quadratic module over Integer Ring with invariants (3, 3)
312
+ Gram matrix of the quadratic form with values in Q/2Z:
313
+ [2/3 0]
314
+ [ 0 4/3]
315
+ generated by 2 elements on Finite quadratic module over Integer Ring with invariants (3, 3)
316
+ Gram matrix of the quadratic form with values in Q/2Z:
317
+ [2/3 0]
318
+ [ 0 4/3]
319
+ """
320
+ import operator
321
+ if op == operator.mul and not self_on_left:
322
+ T = self.invariant_form()
323
+ if S == T:
324
+ return ActionOnFqf(self, S)
325
+ try:
326
+ if S.is_submodule(T):
327
+ # check if the submodule is invariant
328
+ if all(T(s)*g in S for s in S.gens() for g in self.gens()):
329
+ return ActionOnFqf(self, S, on_subquotient=True)
330
+ elif S.V().is_submodule(T.V()) and T.W().is_submodule(S.W()): # is a subquotient
331
+ Q1 = S.V()/T.W()
332
+ Q2 = S.W()/T.W()
333
+ if (
334
+ all(T(q) * g in Q1 for q in Q1.gens() for g in self.gens()) and
335
+ all(T(q) * g in Q2 for q in Q2.gens() for g in self.gens())
336
+ ):
337
+ return ActionOnFqf(self, S, on_subquotient=True)
338
+ except AttributeError:
339
+ pass
340
+ try:
341
+ return AbelianGroupAutomorphismGroup_subgroup._get_action_(self, S, op, self_on_left)
342
+ except AttributeError:
343
+ pass
344
+
345
+ def _subgroup_constructor(self, libgap_subgroup):
346
+ r"""
347
+ Create a subgroup from the input.
348
+
349
+ See :class:`~sage.groups.libgap_wrapper`. Override this in derived
350
+ classes.
351
+
352
+ EXAMPLES::
353
+
354
+ sage: q = TorsionQuadraticForm(matrix.diagonal([2/3, 2/3]))
355
+ sage: G = q.orthogonal_group()
356
+ sage: G.subgroup(G.gens()[:1]) # indirect doctest
357
+ Group of isometries of
358
+ Finite quadratic module over Integer Ring with invariants (3, 3)
359
+ Gram matrix of the quadratic form with values in Q/2Z:
360
+ [2/3 0]
361
+ [ 0 2/3]
362
+ generated by 1 elements
363
+ """
364
+ generators = libgap_subgroup.GeneratorsOfGroup()
365
+ generators = tuple(self(g, check=False) for g in generators)
366
+ return FqfOrthogonalGroup(self, generators, self.invariant_form(), check=False)
367
+
368
+ def _repr_(self):
369
+ r"""
370
+ The string representation of ``self``.
371
+
372
+ EXAMPLES::
373
+
374
+ sage: q = TorsionQuadraticForm(matrix.diagonal([2/3,2/3]))
375
+ sage: q.orthogonal_group()
376
+ Group of isometries of
377
+ Finite quadratic module over Integer Ring with invariants (3, 3)
378
+ Gram matrix of the quadratic form with values in Q/2Z:
379
+ [2/3 0]
380
+ [ 0 2/3]
381
+ generated by 2 elements
382
+ """
383
+ return "Group of isometries of \n%s\ngenerated by %s elements" % (self.invariant_form(), len(self.gens()))
384
+
385
+
386
+ class ActionOnFqf(Action):
387
+ r"""
388
+ Action on a finite quadratic module.
389
+
390
+ INPUT:
391
+
392
+ - ``orthogonal_grp`` -- an instance of :class:`GroupOfIsometries`
393
+ - ``fqf`` -- a torsion quadratic module
394
+ - ``on_subquotient`` -- boolean (default: ``False``)
395
+ - ``is_left`` -- boolean (default: ``False``)
396
+
397
+ EXAMPLES::
398
+
399
+ sage: q = matrix.diagonal([2/3, 4/3])
400
+ sage: q = TorsionQuadraticForm(q)
401
+ sage: G = q.orthogonal_group()
402
+ sage: g = G(matrix.diagonal([2, 2]))
403
+ sage: g
404
+ [2 0]
405
+ [0 2]
406
+ sage: x = q.0
407
+ sage: x * g
408
+ (2, 0)
409
+ """
410
+ def __init__(self, orthogonal_grp, fqf, on_subquotient=False, is_left=False):
411
+ r"""
412
+ Initialize the action.
413
+
414
+ TESTS::
415
+
416
+ sage: from sage.groups.fqf_orthogonal import ActionOnFqf
417
+ sage: q = matrix.diagonal([2/3, 4/3])
418
+ sage: q = TorsionQuadraticForm(q)
419
+ sage: G = q.orthogonal_group()
420
+ sage: A = ActionOnFqf(G, q, is_left=True)
421
+ Traceback (most recent call last):
422
+ ...
423
+ ValueError: the action is from the right
424
+ """
425
+ import operator
426
+ self._on_subquotient = on_subquotient
427
+ if is_left:
428
+ raise ValueError("the action is from the right")
429
+ Action.__init__(self, orthogonal_grp, fqf, is_left, operator.mul)
430
+
431
+ def _act_(self, g, a):
432
+ r"""
433
+ This defines the group action.
434
+
435
+ INPUT:
436
+
437
+ - ``a`` -- an element of the invariant submodule
438
+ - ``g`` -- an element of the acting group
439
+
440
+ OUTPUT: an element of the invariant submodule
441
+
442
+ EXAMPLES::
443
+
444
+ sage: from sage.groups.fqf_orthogonal import ActionOnFqf
445
+ sage: q = matrix.diagonal([2/3, 4/3])
446
+ sage: q = TorsionQuadraticForm(q)
447
+ sage: g1 = 2 * matrix.identity(2)
448
+ sage: g2 = matrix(ZZ, 2, [1, 0, 0, 2])
449
+ sage: G = q.orthogonal_group(gens=[g1, g2])
450
+ sage: A = ActionOnFqf(G, q)
451
+ sage: A
452
+ Right action by Group of isometries of
453
+ Finite quadratic module over Integer Ring with invariants (3, 3)
454
+ Gram matrix of the quadratic form with values in Q/2Z:
455
+ [2/3 0]
456
+ [ 0 4/3]
457
+ generated by 2 elements on Finite quadratic module over Integer Ring with invariants (3, 3)
458
+ Gram matrix of the quadratic form with values in Q/2Z:
459
+ [2/3 0]
460
+ [ 0 4/3]
461
+ sage: x = q.an_element()
462
+ sage: g = G.an_element()
463
+ sage: A(x, g).parent()
464
+ Finite quadratic module over Integer Ring with invariants (3, 3)
465
+ Gram matrix of the quadratic form with values in Q/2Z:
466
+ [2/3 0]
467
+ [ 0 4/3]
468
+ sage: q = TorsionQuadraticForm(matrix.diagonal([2/3, 2/3, 6/8, 1/4]))
469
+ sage: G = q.orthogonal_group()
470
+ sage: q2 = q.primary_part(2)
471
+ sage: g = G(matrix.diagonal([1, 7]))
472
+ sage: q2.gen(1) * g
473
+ (0, 3)
474
+ """
475
+ if self.is_left():
476
+ pass
477
+ # this would be a left action but... we do not allow it.
478
+ # v = (a.vector()*g.matrix().inverse())
479
+ # P = a.parent()
480
+ # return P.linear_combination_of_smith_form_gens(v)
481
+ elif self._on_subquotient:
482
+ S = a.parent()
483
+ T = g.parent().invariant_form()
484
+ return S(T(a)*g)
485
+ else:
486
+ v = (a.vector()*g.matrix())
487
+ P = a.parent()
488
+ return P.linear_combination_of_smith_form_gens(v)
489
+
490
+
491
+ def _isom_fqf(A, B=None):
492
+ r"""
493
+ Return isometries from `A` to `B`.
494
+
495
+ INPUT:
496
+
497
+ - ``A`` -- a torsion quadratic module
498
+ - ``B`` -- (default: ``None``) a torsion quadratic module
499
+
500
+ OUTPUT: list of generators of the orthogonal group of A
501
+
502
+ If ``B`` is given, this returns instead a single isometry of `A` and `B`
503
+ or raises a :exc:`ValueError` if `A` and `B` are not isometric.
504
+
505
+ EXAMPLES::
506
+
507
+ sage: q = matrix.diagonal(QQ, 3*[2/3])
508
+ sage: q = TorsionQuadraticForm(q)
509
+ sage: gens = sage.groups.fqf_orthogonal._isom_fqf(q, q)
510
+ sage: q1 = q.submodule_with_gens(gens)
511
+ sage: q1.gram_matrix_quadratic() == q.gram_matrix_quadratic()
512
+ True
513
+
514
+ TESTS::
515
+
516
+ sage: for p in primes_first_n(7)[1:]: # long time
517
+ ....: q = matrix.diagonal(QQ, 3 * [2/p])
518
+ ....: q = TorsionQuadraticForm(q)
519
+ ....: assert q.orthogonal_group().order()==GO(3, p).order()
520
+ """
521
+ def orbits(G, L):
522
+ r"""
523
+ Return the orbits of `L` under `G`.
524
+
525
+ INPUT:
526
+
527
+ - ``G`` -- an fqf_orthognal group
528
+ - ``L`` -- list of tuples of elements of the domain of ``G``
529
+
530
+ OUTPUT: list of orbit representatives of `L`
531
+ """
532
+ D = G.invariant_form()
533
+ A = G.domain()
534
+ L = libgap([[A(g).gap() for g in f] for f in L])
535
+ orb = G.gap().Orbits(L,libgap.OnTuples)
536
+ orb = [g[0] for g in orb]
537
+ orb = [[D.linear_combination_of_smith_form_gens(A(g).exponents()) for g in f] for f in orb]
538
+ return orb
539
+
540
+ if B is None:
541
+ B = A
542
+ automorphisms = True
543
+ else:
544
+ automorphisms = False
545
+ if A.invariants() != B.invariants():
546
+ raise ValueError("torsion quadratic modules are not isometric")
547
+ n = len(A.smith_form_gens())
548
+ # separating the different primes here would speed things up
549
+ b_cand = [[b for b in B if b.q() == a.q() and b.order() == a.order()] for a in A.smith_form_gens()]
550
+
551
+ G = B.orthogonal_group(())
552
+ ambient = G.ambient()
553
+ waiting = [[]]
554
+ while len(waiting) > 0:
555
+ # f is an i-partial isometry
556
+ f = waiting.pop()
557
+ i = len(f)
558
+ if i == n:
559
+ # f is a full isometry
560
+ if not automorphisms:
561
+ return f
562
+ g = ambient(matrix(f))
563
+ if g not in G:
564
+ G = B.orthogonal_group(tuple(ambient(s.matrix()) for s in G.gens())+(g,))
565
+ waiting = orbits(G, waiting)
566
+ continue
567
+ # extend f to an i+1 - partial isometry in all possible ways
568
+ a = A.smith_form_gens()[i]
569
+ card = ZZ.prod(A.smith_form_gen(k).order() for k in range(i+1))
570
+ for b in b_cand[i]:
571
+ if all(b.b(f[k]) == a.b(A.smith_form_gens()[k]) for k in range(i)):
572
+ fnew = f + [b]
573
+ # check that the elements of fnew are independent
574
+ if B.submodule(fnew).cardinality() == card:
575
+ waiting.append(fnew)
576
+ if not automorphisms:
577
+ raise ValueError("torsion quadratic modules are not isometric")
578
+ gens = G.gap().SmallGeneratingSet()
579
+ return [G(g).matrix() for g in gens]