Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/algebras/quatalg/quaternion_algebra.py
4158 views
1
"""
2
Quaternion Algebras
3
4
AUTHORS:
5
6
- Jon Bobber -- 2009 rewrite
7
8
- William Stein -- 2009 rewrite
9
10
This code is partly based on Sage code by David Kohel from 2005.
11
12
TESTS:
13
14
Pickling test::
15
16
sage: Q.<i,j,k> = QuaternionAlgebra(QQ,-5,-2)
17
sage: Q == loads(dumps(Q))
18
True
19
"""
20
21
########################################################################
22
# Copyright (C) 2009 William Stein <[email protected]>
23
# Copyright (C) 2009 Jonathon Bober <[email protected]>
24
#
25
# Distributed under the terms of the GNU General Public License (GPL)
26
#
27
# This code is distributed in the hope that it will be useful,
28
# but WITHOUT ANY WARRANTY; without even the implied warranty of
29
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30
# General Public License for more details.
31
#
32
# The full text of the GPL is available at:
33
#
34
# http://www.gnu.org/licenses/
35
########################################################################
36
37
38
from sage.rings.arith import (GCD,
39
hilbert_conductor_inverse, hilbert_conductor,
40
factor, gcd, lcm)
41
from sage.rings.all import RR, Integer
42
from sage.rings.integer_ring import ZZ
43
from sage.rings.rational import Rational
44
from sage.rings.finite_rings.constructor import GF
45
46
from sage.rings.ring import Algebra, is_Field
47
from sage.rings.ideal import Ideal_fractional
48
from sage.rings.rational_field import is_RationalField, QQ
49
from sage.rings.number_field.number_field import is_NumberField
50
from sage.structure.parent_gens import ParentWithGens, normalize_names
51
from sage.matrix.matrix_space import MatrixSpace
52
from sage.matrix.constructor import diagonal_matrix, matrix
53
from sage.structure.sequence import Sequence
54
from sage.structure.element import is_RingElement
55
from sage.modules.free_module import VectorSpace, FreeModule
56
from sage.modules.free_module_element import vector
57
58
import quaternion_algebra_element
59
import quaternion_algebra_cython
60
61
from sage.modular.modsym.p1list import P1List
62
63
from sage.misc.cachefunc import cached_method
64
65
########################################################
66
# Constructor
67
########################################################
68
69
_cache = {}
70
71
def QuaternionAlgebra(arg0, arg1=None, arg2=None, names='i,j,k'):
72
"""
73
There are three input formats:
74
75
- ``QuaternionAlgebra(a, b)``: quaternion algebra generated by ``i``, ``j``
76
subject to `i^2 = a`, `j^2 = b`, `j * i = -i * j`.
77
78
- ``QuaternionAlgebra(K, a, b)``: same as above but over a field ``K``.
79
Here, ``a`` and ``b`` are nonzero elements of a field (``K``) of
80
characteristic not 2, and we set `k = i * j`.
81
82
- ``QuaternionAlgebra(D)``: a rational quaternion algebra with
83
discriminant ``D``, where `D > 1` is a squarefree integer.
84
85
EXAMPLES:
86
87
``QuaternionAlgebra(a, b)`` - return quaternion algebra over the
88
*smallest* field containing the nonzero elements ``a`` and ``b`` with
89
generators ``i``, ``j``, ``k`` with `i^2=a`, `j^2=b` and `j*i=-i*j`::
90
91
sage: QuaternionAlgebra(-2,-3)
92
Quaternion Algebra (-2, -3) with base ring Rational Field
93
sage: QuaternionAlgebra(GF(5)(2), GF(5)(3))
94
Quaternion Algebra (2, 3) with base ring Finite Field of size 5
95
sage: QuaternionAlgebra(2, GF(5)(3))
96
Quaternion Algebra (2, 3) with base ring Finite Field of size 5
97
sage: QuaternionAlgebra(QQ[sqrt(2)](-1), -5)
98
Quaternion Algebra (-1, -5) with base ring Number Field in sqrt2 with defining polynomial x^2 - 2
99
sage: QuaternionAlgebra(sqrt(-1), sqrt(-3))
100
Quaternion Algebra (I, sqrt(-3)) with base ring Symbolic Ring
101
sage: QuaternionAlgebra(1r,1)
102
Quaternion Algebra (1, 1) with base ring Rational Field
103
104
Python ints, longs and floats may be passed to the ``QuaternionAlgebra(a, b)`` constructor, as may
105
all pairs of nonzero elements of a ring not of characteristic 2. The following tests address
106
the issues raised in trac ticket 10601::
107
108
sage: QuaternionAlgebra(1r,1)
109
Quaternion Algebra (1, 1) with base ring Rational Field
110
sage: QuaternionAlgebra(1,1.0r)
111
Quaternion Algebra (1.00000000000000, 1.00000000000000) with base ring Real Field with 53 bits of precision
112
sage: QuaternionAlgebra(0,0)
113
Traceback (most recent call last):
114
...
115
ValueError: a and b must be nonzero
116
sage: QuaternionAlgebra(GF(2)(1),1)
117
Traceback (most recent call last):
118
...
119
ValueError: a and b must be elements of a ring with characteristic not 2
120
sage: a = PermutationGroupElement([1,2,3])
121
sage: QuaternionAlgebra(a, a)
122
Traceback (most recent call last):
123
...
124
ValueError: a and b must be elements of a ring with characteristic not 2
125
126
``QuaternionAlgebra(K, a, b)`` - return quaternion algebra over the
127
field ``K`` with generators ``i``, ``j``, ``k`` with `i^2=a`, `j^2=b`
128
and `i*j=-j*i`::
129
130
sage: QuaternionAlgebra(QQ, -7, -21)
131
Quaternion Algebra (-7, -21) with base ring Rational Field
132
sage: QuaternionAlgebra(QQ[sqrt(2)], -2,-3)
133
Quaternion Algebra (-2, -3) with base ring Number Field in sqrt2 with defining polynomial x^2 - 2
134
135
``QuaternionAlgebra(D)`` - ``D`` is a squarefree integer; returns a
136
rational quaternion algebra of discriminant ``D``::
137
138
sage: QuaternionAlgebra(1)
139
Quaternion Algebra (-1, 1) with base ring Rational Field
140
sage: QuaternionAlgebra(2)
141
Quaternion Algebra (-1, -1) with base ring Rational Field
142
sage: QuaternionAlgebra(7)
143
Quaternion Algebra (-1, -7) with base ring Rational Field
144
sage: QuaternionAlgebra(2*3*5*7)
145
Quaternion Algebra (-22, 210) with base ring Rational Field
146
147
If the coefficients `a` and `b` in the definition of the quaternion
148
algebra are not integral, then a slower generic type is used for
149
arithmetic::
150
151
sage: type(QuaternionAlgebra(-1,-3).0)
152
<type 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_rational_field'>
153
sage: type(QuaternionAlgebra(-1,-3/2).0)
154
<type 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'>
155
156
Make sure caching is sane::
157
158
sage: A = QuaternionAlgebra(2,3); A
159
Quaternion Algebra (2, 3) with base ring Rational Field
160
sage: B = QuaternionAlgebra(GF(5)(2),GF(5)(3)); B
161
Quaternion Algebra (2, 3) with base ring Finite Field of size 5
162
sage: A is QuaternionAlgebra(2,3)
163
True
164
sage: B is QuaternionAlgebra(GF(5)(2),GF(5)(3))
165
True
166
sage: Q = QuaternionAlgebra(2); Q
167
Quaternion Algebra (-1, -1) with base ring Rational Field
168
sage: Q is QuaternionAlgebra(QQ,-1,-1)
169
True
170
sage: Q is QuaternionAlgebra(-1,-1)
171
True
172
sage: Q.<ii,jj,kk> = QuaternionAlgebra(15); Q.variable_names()
173
('ii', 'jj', 'kk')
174
sage: QuaternionAlgebra(15).variable_names()
175
('i', 'j', 'k')
176
177
TESTS:
178
179
Verify that bug found when working on trac 12006 involving coercing invariants
180
into the base field is fixed::
181
182
sage: Q = QuaternionAlgebra(-1,-1); Q
183
Quaternion Algebra (-1, -1) with base ring Rational Field
184
sage: parent(Q._a)
185
Rational Field
186
sage: parent(Q._b)
187
Rational Field
188
"""
189
190
# QuaternionAlgebra(D)
191
if arg1 is None and arg2 is None:
192
K = QQ
193
D = Integer(arg0)
194
a, b = hilbert_conductor_inverse(D)
195
a = Rational(a); b = Rational(b)
196
197
elif arg2 is None:
198
# If arg0 or arg1 are Python data types, coerce them
199
# to the relevant Sage types. This is a bit inelegant.
200
L = []
201
for a in [arg0,arg1]:
202
if is_RingElement(a):
203
L.append(a)
204
elif isinstance(a, int) or isinstance(a, long):
205
L.append(Integer(a))
206
elif isinstance(a, float):
207
L.append(RR(a))
208
else:
209
raise ValueError("a and b must be elements of a ring with characteristic not 2")
210
211
# QuaternionAlgebra(a, b)
212
v = Sequence(L)
213
K = v.universe().fraction_field()
214
a = K(v[0])
215
b = K(v[1])
216
217
# QuaternionAlgebra(K, a, b)
218
else:
219
K = arg0
220
if not is_Field(K):
221
raise TypeError("base ring of quaternion algebra must be a field")
222
a = K(arg1)
223
b = K(arg2)
224
225
if K.characteristic() == 2:
226
# Lameness!
227
raise ValueError("a and b must be elements of a ring with characteristic not 2")
228
if a == 0 or b == 0:
229
raise ValueError("a and b must be nonzero")
230
231
global _cache
232
names = normalize_names(3, names)
233
key = (K, a, b, names)
234
if key in _cache:
235
return _cache[key]
236
A = QuaternionAlgebra_ab(K, a, b, names=names)
237
A._key = key
238
_cache[key] = A
239
return A
240
241
242
243
244
########################################################
245
# Classes
246
########################################################
247
248
def is_QuaternionAlgebra(A):
249
"""
250
Return ``True`` if ``A`` is of the QuaternionAlgebra data type.
251
252
EXAMPLES::
253
254
sage: sage.algebras.quatalg.quaternion_algebra.is_QuaternionAlgebra(QuaternionAlgebra(QQ,-1,-1))
255
True
256
sage: sage.algebras.quatalg.quaternion_algebra.is_QuaternionAlgebra(ZZ)
257
False
258
"""
259
return isinstance(A, QuaternionAlgebra_abstract)
260
261
class QuaternionAlgebra_abstract(Algebra):
262
def _repr_(self):
263
"""
264
EXAMPLES::
265
266
sage: sage.algebras.quatalg.quaternion_algebra.QuaternionAlgebra_abstract(QQ)._repr_()
267
'Quaternion Algebra with base ring Rational Field'
268
"""
269
return "Quaternion Algebra with base ring %s"%self.base_ring()
270
271
def ngens(self):
272
"""
273
Return the number of generators of the quaternion algebra as a K-vector
274
space, not including 1. This value is always 3: the algebra is spanned
275
by the standard basis `1`, `i`, `j`, `k`.
276
277
EXAMPLES::
278
279
sage: Q.<i,j,k> = QuaternionAlgebra(QQ,-5,-2)
280
sage: Q.ngens()
281
3
282
sage: Q.gens()
283
[i, j, k]
284
"""
285
return 3
286
287
def basis(self):
288
"""
289
Return the fixed basis of ``self``, which is `1`, `i`, `j`, `k`, where
290
`i`, `j`, `k` are the generators of ``self``.
291
292
EXAMPLES::
293
294
sage: Q.<i,j,k> = QuaternionAlgebra(QQ,-5,-2)
295
sage: Q.basis()
296
(1, i, j, k)
297
298
sage: Q.<xyz,abc,theta> = QuaternionAlgebra(GF(9,'a'),-5,-2)
299
sage: Q.basis()
300
(1, xyz, abc, theta)
301
302
The basis is cached::
303
304
sage: Q.basis() is Q.basis()
305
True
306
"""
307
try:
308
return self.__basis
309
except AttributeError:
310
self.__basis = tuple([self(1)] + list(self.gens()))
311
return self.__basis
312
313
def inner_product_matrix(self):
314
"""
315
Return the inner product matrix associated to ``self``, i.e. the
316
Gram matrix of the reduced norm as a quadratic form on ``self``.
317
The standard basis `1`, `i`, `j`, `k` is orthogonal, so this matrix
318
is just the diagonal matrix with diagonal entries `2`, `2a`, `2b`,
319
`2ab`.
320
321
EXAMPLES::
322
323
sage: Q.<i,j,k> = QuaternionAlgebra(-5,-19)
324
sage: Q.inner_product_matrix()
325
[ 2 0 0 0]
326
[ 0 10 0 0]
327
[ 0 0 38 0]
328
[ 0 0 0 190]
329
"""
330
try: return self.__inner_product_matrix
331
except AttributeError: pass
332
333
a, b = self._a, self._b
334
M = diagonal_matrix(self.base_ring(), [2, -2*a, -2*b, 2*a*b])
335
M.set_immutable()
336
self.__inner_product_matrix = M
337
return M
338
339
def is_commutative(self):
340
"""
341
Return ``False`` always, since all quaternion algebras are
342
noncommutative.
343
344
EXAMPLES::
345
346
sage: Q.<i,j,k> = QuaternionAlgebra(QQ, -3,-7)
347
sage: Q.is_commutative()
348
False
349
"""
350
return False
351
352
def is_division_algebra(self):
353
"""
354
Return ``True`` if the quaternion algebra is a division algebra (i.e.
355
every nonzero element in ``self`` is invertible), and ``False`` if the
356
quaternion algebra is isomorphic to the 2x2 matrix algebra.
357
358
EXAMPLES::
359
360
sage: QuaternionAlgebra(QQ,-5,-2).is_division_algebra()
361
True
362
sage: QuaternionAlgebra(1).is_division_algebra()
363
False
364
sage: QuaternionAlgebra(2,9).is_division_algebra()
365
False
366
sage: QuaternionAlgebra(RR(2.),1).is_division_algebra()
367
Traceback (most recent call last):
368
...
369
NotImplementedError: base field must be rational numbers
370
"""
371
return self.discriminant() != 1
372
373
def is_matrix_ring(self):
374
"""
375
Return ``True`` if the quaternion algebra is isomorphic to the 2x2
376
matrix ring, and ``False`` if ``self`` is a division algebra (i.e.
377
every nonzero element in ``self`` is invertible).
378
379
EXAMPLES::
380
381
sage: QuaternionAlgebra(QQ,-5,-2).is_matrix_ring()
382
False
383
sage: QuaternionAlgebra(1).is_matrix_ring()
384
True
385
sage: QuaternionAlgebra(2,9).is_matrix_ring()
386
True
387
sage: QuaternionAlgebra(RR(2.),1).is_matrix_ring()
388
Traceback (most recent call last):
389
...
390
NotImplementedError: base field must be rational numbers
391
392
"""
393
return self.discriminant() == 1
394
395
def is_exact(self):
396
"""
397
Return ``True`` if elements of this quaternion algebra are represented
398
exactly, i.e. there is no precision loss when doing arithmetic. A
399
quaternion algebra is exact if and only if its base field is
400
exact.
401
402
EXAMPLES::
403
404
sage: Q.<i,j,k> = QuaternionAlgebra(QQ, -3, -7)
405
sage: Q.is_exact()
406
True
407
sage: Q.<i,j,k> = QuaternionAlgebra(Qp(7), -3, -7)
408
sage: Q.is_exact()
409
False
410
"""
411
return self.base_ring().is_exact()
412
413
def is_field(self, proof = True):
414
"""
415
Return ``False`` always, since all quaternion algebras are
416
noncommutative and all fields are commutative.
417
418
EXAMPLES::
419
420
sage: Q.<i,j,k> = QuaternionAlgebra(QQ, -3, -7)
421
sage: Q.is_field()
422
False
423
"""
424
return False
425
426
def is_finite(self):
427
"""
428
Return ``True`` if the quaternion algebra is finite as a set.
429
430
Algorithm: A quaternion algebra is finite if and only if the
431
base field is finite.
432
433
EXAMPLES::
434
435
sage: Q.<i,j,k> = QuaternionAlgebra(QQ, -3, -7)
436
sage: Q.is_finite()
437
False
438
sage: Q.<i,j,k> = QuaternionAlgebra(GF(5), -3, -7)
439
sage: Q.is_finite()
440
True
441
"""
442
return self.base_ring().is_finite()
443
444
def is_integral_domain(self, proof = True):
445
"""
446
Return ``False`` always, since all quaternion algebras are
447
noncommutative and integral domains are commutative (in Sage).
448
449
EXAMPLES::
450
451
sage: Q.<i,j,k> = QuaternionAlgebra(QQ, -3, -7)
452
sage: Q.is_integral_domain()
453
False
454
"""
455
return False
456
457
def is_noetherian(self):
458
"""
459
Return ``True`` always, since any quaternion algebra is a noetherian
460
ring (because it is a finitely generated module over a field).
461
462
EXAMPLES::
463
464
sage: Q.<i,j,k> = QuaternionAlgebra(QQ, -3, -7)
465
sage: Q.is_noetherian()
466
True
467
"""
468
return True
469
470
def order(self):
471
"""
472
Return the number of elements of the quaternion algebra, or
473
``+Infinity`` if the algebra is not finite.
474
475
EXAMPLES::
476
477
sage: Q.<i,j,k> = QuaternionAlgebra(QQ, -3, -7)
478
sage: Q.order()
479
+Infinity
480
sage: Q.<i,j,k> = QuaternionAlgebra(GF(5), -3, -7)
481
sage: Q.order()
482
625
483
"""
484
return (self.base_ring().order())**4
485
486
def random_element(self, *args, **kwds):
487
"""
488
Return a random element of this quaternion algebra.
489
490
The ``args`` and ``kwds`` are passed to the ``random_element`` method
491
of the base ring.
492
493
EXAMPLES::
494
495
sage: QuaternionAlgebra(QQ[sqrt(2)],-3,7).random_element()
496
(sqrt2 + 2)*i + (-12*sqrt2 - 2)*j + (-sqrt2 + 1)*k
497
sage: QuaternionAlgebra(-3,19).random_element()
498
-1 + 2*i - j - 6/5*k
499
sage: QuaternionAlgebra(GF(17)(2),3).random_element()
500
14 + 10*i + 4*j + 7*k
501
502
Specify the numerator and denominator bounds::
503
504
sage: QuaternionAlgebra(-3,19).random_element(10^6,10^6)
505
-979933/553629 + 255525/657688*i - 3511/6929*j - 700105/258683*k
506
"""
507
K = self.base_ring()
508
return self([ K.random_element(*args, **kwds) for _ in range(4) ])
509
510
def vector_space(self):
511
"""
512
Return the vector space associated to ``self`` with inner product given
513
by the reduced norm.
514
515
EXAMPLES::
516
517
sage: QuaternionAlgebra(-3,19).vector_space()
518
Ambient quadratic space of dimension 4 over Rational Field
519
Inner product matrix:
520
[ 2 0 0 0]
521
[ 0 6 0 0]
522
[ 0 0 -38 0]
523
[ 0 0 0 -114]
524
"""
525
try:
526
return self.__vector_space
527
except AttributeError:
528
V = VectorSpace(self.base_ring(), 4, inner_product_matrix = self.inner_product_matrix())
529
self.__vector_space = V
530
return V
531
532
533
class QuaternionAlgebra_ab(QuaternionAlgebra_abstract):
534
"""
535
The quaternion algebra of the form `(a, b/K)`, where `i^2=a`, `j^2 = b`,
536
and `j*i = -i*j`. ``K`` is a field not of characteristic 2 and ``a``,
537
``b`` are nonzero elements of ``K``.
538
539
See ``QuaternionAlgebra`` for many more examples.
540
541
EXAMPLES::
542
543
sage: QuaternionAlgebra(QQ, -7, -21) # indirect doctest
544
Quaternion Algebra (-7, -21) with base ring Rational Field
545
"""
546
def __init__(self, base_ring, a, b, names='i,j,k'):
547
"""
548
Create the quaternion algebra with `i^2 = a`, `j^2 = b`, and
549
`i*j = -j*i = k`.
550
551
INPUT:
552
553
- ``base_ring`` - commutative ring
554
- ``a, b`` - elements of ``base_ring``
555
- ``names`` - string (optional, default 'i, j, k') names of the generators
556
557
TESTS:
558
559
Test making quaternion elements (using the element constructor)::
560
561
sage: Q.<i,j,k> = QuaternionAlgebra(QQ,-1,-2)
562
sage: a = Q(2/3); a
563
2/3
564
sage: type(a)
565
<type 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_rational_field'>
566
sage: Q(a)
567
2/3
568
sage: Q([1,2,3,4])
569
1 + 2*i + 3*j + 4*k
570
sage: Q((1,2,3,4))
571
1 + 2*i + 3*j + 4*k
572
sage: Q(-3/5)
573
-3/5
574
575
The base ring must be a field::
576
577
sage: Q.<ii,jj,kk> = QuaternionAlgebra(ZZ,-5,-19)
578
Traceback (most recent call last):
579
...
580
TypeError: base ring of quaternion algebra must be a field
581
"""
582
ParentWithGens.__init__(self, base_ring, names=names)
583
self._a = a
584
self._b = b
585
if not is_Field(base_ring):
586
raise TypeError("base ring of quaternion algebra must be a field")
587
if is_RationalField(base_ring) and a.denominator() == 1 and b.denominator() == 1:
588
element_constructor = quaternion_algebra_element.QuaternionAlgebraElement_rational_field
589
elif is_NumberField(base_ring) and base_ring.degree() > 2 and base_ring.is_absolute() and \
590
a.denominator() == 1 and b.denominator() == 1 and base_ring.defining_polynomial().is_monic():
591
# This QuaternionAlgebraElement_number_field class is not
592
# designed to work with elements of a quadratic field. To
593
# do that, the main thing would be to implement
594
# __getitem__, etc. This would maybe give a factor of 2
595
# (or more?) speedup. Much care must be taken because the
596
# underlying representation of quadratic fields is a bit
597
# tricky.
598
element_constructor = quaternion_algebra_element.QuaternionAlgebraElement_number_field
599
else:
600
element_constructor = quaternion_algebra_element.QuaternionAlgebraElement_generic
601
self._populate_coercion_lists_(coerce_list=[base_ring], element_constructor=element_constructor)
602
self._gens = [self([0,1,0,0]), self([0,0,1,0]), self([0,0,0,1])]
603
604
def maximal_order(self):
605
"""
606
Return a maximal order in this quaternion algebra.
607
608
OUTPUT: an order in this quaternion algebra
609
610
EXAMPLES::
611
612
sage: QuaternionAlgebra(-1,-7).maximal_order()
613
Order of Quaternion Algebra (-1, -7) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
614
"""
615
try: return self.__maximal_order
616
except AttributeError: pass
617
if self.base_ring() == QQ and self.discriminant().is_prime():
618
from sage.modular.quatalg.brandt import maximal_order
619
R = maximal_order(self)
620
self.__maximal_order = R
621
return R
622
raise NotImplementedError("maximal order only implemented for rational quaternion algebras of prime discriminant")
623
624
def invariants(self):
625
"""
626
Return the structural invariants `a`, `b` of this quaternion
627
algebra: ``self`` is generated by `i`, `j` subject to
628
`i^2 = a`, `j^2 = b` and `j*i = -i*j`.
629
630
EXAMPLES::
631
632
sage: Q.<i,j,k> = QuaternionAlgebra(15)
633
sage: Q.invariants()
634
(-3, 5)
635
sage: i^2
636
-3
637
sage: j^2
638
5
639
"""
640
return self._a, self._b
641
642
def __cmp__(self, other):
643
"""
644
Compare self and other.
645
646
EXAMPLES::
647
648
sage: cmp(QuaternionAlgebra(-1,-7), QuaternionAlgebra(-1,-7))
649
0
650
sage: cmp(QuaternionAlgebra(-1,-7), QuaternionAlgebra(-1,-5))
651
-1
652
sage: cmp(QuaternionAlgebra(-1,-7), QuaternionAlgebra(-1,-10))
653
1
654
"""
655
if not isinstance(other, QuaternionAlgebra_abstract):
656
return cmp(type(self), type(other))
657
c = cmp(self.base_ring(), other.base_ring())
658
if c: return c
659
return cmp((self._a, self._b), (other._a, other._b))
660
661
def __reduce__(self):
662
"""
663
Internal method used for pickling.
664
665
TESTS::
666
667
sage: QuaternionAlgebra(QQ,-1,-2).__reduce__()
668
(<function unpickle_QuaternionAlgebra_v0 at ...>, (Rational Field, -1, -2, ('i', 'j', 'k')))
669
670
Test uniqueness of parent::
671
672
sage: Q = QuaternionAlgebra(QQ,-1,-2)
673
sage: loads(dumps(Q)) is Q
674
True
675
"""
676
return unpickle_QuaternionAlgebra_v0, self._key
677
678
def gen(self, i=0):
679
"""
680
Return the `i^{th}` generator of ``self``.
681
682
INPUT:
683
684
- ``i`` - integer (optional, default 0)
685
686
EXAMPLES::
687
688
sage: Q.<ii,jj,kk> = QuaternionAlgebra(QQ,-1,-2); Q
689
Quaternion Algebra (-1, -2) with base ring Rational Field
690
sage: Q.gen(0)
691
ii
692
sage: Q.gen(1)
693
jj
694
sage: Q.gen(2)
695
kk
696
sage: Q.gens()
697
[ii, jj, kk]
698
"""
699
return self._gens[i]
700
701
def _repr_(self):
702
"""
703
Print representation.
704
705
TESTS::
706
707
sage: Q.<i,j,k> = QuaternionAlgebra(QQ,-5,-2)
708
sage: type(Q)
709
<class 'sage.algebras.quatalg.quaternion_algebra.QuaternionAlgebra_ab'>
710
sage: Q._repr_()
711
'Quaternion Algebra (-5, -2) with base ring Rational Field'
712
sage: Q
713
Quaternion Algebra (-5, -2) with base ring Rational Field
714
sage: print Q
715
Quaternion Algebra (-5, -2) with base ring Rational Field
716
sage: str(Q)
717
'Quaternion Algebra (-5, -2) with base ring Rational Field'
718
"""
719
return "Quaternion Algebra (%r, %r) with base ring %s"%(self._a, self._b, self.base_ring())
720
721
def inner_product_matrix(self):
722
"""
723
Return the inner product matrix associated to ``self``, i.e. the
724
Gram matrix of the reduced norm as a quadratic form on ``self``.
725
The standard basis `1`, `i`, `j`, `k` is orthogonal, so this matrix
726
is just the diagonal matrix with diagonal entries `1`, `a`, `b`, `ab`.
727
728
EXAMPLES::
729
730
sage: Q.<i,j,k> = QuaternionAlgebra(-5,-19)
731
sage: Q.inner_product_matrix()
732
[ 2 0 0 0]
733
[ 0 10 0 0]
734
[ 0 0 38 0]
735
[ 0 0 0 190]
736
737
sage: R.<a,b> = QQ[]; Q.<i,j,k> = QuaternionAlgebra(Frac(R),a,b)
738
sage: Q.inner_product_matrix()
739
[ 2 0 0 0]
740
[ 0 -2*a 0 0]
741
[ 0 0 -2*b 0]
742
[ 0 0 0 2*a*b]
743
"""
744
a, b = self._a, self._b
745
return diagonal_matrix(self.base_ring(), [2, -2*a, -2*b, 2*a*b])
746
747
def discriminant(self):
748
"""
749
Given a quaternion algebra `A` defined over the field of
750
rational numbers, return the discriminant of `A`, i.e. the
751
product of the ramified primes of `A`.
752
753
EXAMPLES::
754
755
sage: QuaternionAlgebra(210,-22).discriminant()
756
210
757
sage: QuaternionAlgebra(19).discriminant()
758
19
759
760
This raises a ``NotImplementedError`` if the base field is not
761
the rational numbers::
762
763
sage: QuaternionAlgebra(QQ[sqrt(2)],3,19).discriminant()
764
Traceback (most recent call last):
765
...
766
NotImplementedError: base field must be rational numbers
767
"""
768
try: return self.__discriminant
769
except AttributeError: pass
770
if not is_RationalField(self.base_ring()):
771
raise NotImplementedError("base field must be rational numbers")
772
self.__discriminant = hilbert_conductor(self._a, self._b)
773
return self.__discriminant
774
775
def ramified_primes(self):
776
"""
777
Return the primes that ramify in this quaternion algebra. Currently
778
only implemented over the rational numbers.
779
780
EXAMPLES::
781
782
sage: QuaternionAlgebra(QQ, -1, -1).ramified_primes()
783
[2]
784
"""
785
#TODO: more examples
786
787
return [f[0] for f in factor(self.discriminant())]
788
789
def _magma_init_(self, magma):
790
"""
791
Return Magma version of this quaternion algebra.
792
793
EXAMPLES::
794
795
sage: Q = QuaternionAlgebra(-1,-1); Q
796
Quaternion Algebra (-1, -1) with base ring Rational Field
797
sage: Q._magma_init_(magma) # optional - magma
798
'QuaternionAlgebra(_sage_[...],-1/1,-1/1)'
799
sage: A = magma(Q); A # optional - magma
800
Quaternion Algebra with base ring Rational Field, defined by i^2 = -1, j^2 = -1
801
sage: A.RamifiedPlaces() # optional - magma
802
[
803
Ideal of Integer Ring generated by 2
804
]
805
806
A more complicated example involving a quaternion algebra over a number field::
807
808
sage: K.<a> = QQ[sqrt(2)]; Q = QuaternionAlgebra(K,-1,a); Q
809
Quaternion Algebra (-1, sqrt2) with base ring Number Field in sqrt2 with defining polynomial x^2 - 2
810
sage: magma(Q) # optional - magma
811
Quaternion Algebra with base ring Number Field with defining polynomial x^2 - 2 over the Rational Field, defined by i^2 = -1, j^2 = sqrt2
812
sage: Q._magma_init_(magma) # optional - magma
813
'QuaternionAlgebra(_sage_[...],(_sage_[...]![-1, 0]),(_sage_[...]![0, 1]))'
814
"""
815
R = magma(self.base_ring())
816
return 'QuaternionAlgebra(%s,%s,%s)'%(R.name(),
817
self._a._magma_init_(magma),
818
self._b._magma_init_(magma))
819
820
def quaternion_order(self, basis, check=True):
821
"""
822
Return the order of this quaternion order with given basis.
823
824
INPUT:
825
826
- ``basis`` - list of 4 elements of ``self``
827
- ``check`` - bool (default: ``True``)
828
829
EXAMPLES::
830
831
sage: Q.<i,j,k> = QuaternionAlgebra(-11,-1)
832
sage: Q.quaternion_order([1,i,j,k])
833
Order of Quaternion Algebra (-11, -1) with base ring Rational Field with basis (1, i, j, k)
834
835
We test out ``check=False``::
836
837
sage: Q.quaternion_order([1,i,j,k], check=False)
838
Order of Quaternion Algebra (-11, -1) with base ring Rational Field with basis [1, i, j, k]
839
sage: Q.quaternion_order([i,j,k], check=False)
840
Order of Quaternion Algebra (-11, -1) with base ring Rational Field with basis [i, j, k]
841
"""
842
return QuaternionOrder(self, basis, check=check)
843
844
def ideal(self, gens, left_order=None, right_order=None, check=True, **kwds):
845
r"""
846
Return the quaternion ideal with given gens over `\ZZ`.
847
Neither a left or right order structure need be specified.
848
849
INPUT:
850
851
- ``gens`` -- a list of elements of this quaternion order
852
853
- ``check`` -- bool (default: ``True``); if ``False``, then ``gens`` must
854
4-tuple that forms a Hermite basis for an ideal
855
856
- ``left_order`` -- a quaternion order or ``None``
857
858
- ``right_order`` -- a quaternion order or ``None``
859
860
EXAMPLES::
861
862
sage: R = QuaternionAlgebra(-11,-1)
863
sage: R.ideal([2*a for a in R.basis()])
864
Fractional ideal (2, 2*i, 2*j, 2*k)
865
"""
866
if self.base_ring() == QQ:
867
return QuaternionFractionalIdeal_rational(gens, left_order=left_order, right_order=right_order, check=check)
868
else:
869
raise NotImplementedError("ideal only implemented for quaternion algebras over QQ")
870
871
@cached_method
872
def modp_splitting_data(self, p):
873
r"""
874
Return mod `p` splitting data for this quaternion algebra at
875
the unramified prime `p`. This is `2\times 2`
876
matrices `I`, `J`, `K` over the finite field `\GF{p}` such that if
877
the quaternion algebra has generators `i, j, k`, then `I^2 =
878
i^2`, `J^2 = j^2`, `IJ=K` and `IJ=-JI`.
879
880
.. note::
881
882
Currently only implemented when `p` is odd and the base
883
ring is `\QQ`.
884
885
INPUT:
886
887
- `p` -- unramified odd prime
888
889
OUTPUT:
890
891
- 2-tuple of matrices over finite field
892
893
EXAMPLES::
894
895
sage: Q = QuaternionAlgebra(-15, -19)
896
sage: Q.modp_splitting_data(7)
897
(
898
[0 6] [6 1] [6 6]
899
[1 0], [1 1], [6 1]
900
)
901
sage: Q.modp_splitting_data(next_prime(10^5))
902
(
903
[ 0 99988] [97311 4] [99999 59623]
904
[ 1 0], [13334 2692], [97311 4]
905
)
906
sage: I,J,K = Q.modp_splitting_data(23)
907
sage: I
908
[0 8]
909
[1 0]
910
sage: I^2
911
[8 0]
912
[0 8]
913
sage: J
914
[19 2]
915
[17 4]
916
sage: J^2
917
[4 0]
918
[0 4]
919
sage: I*J == -J*I
920
True
921
sage: I*J == K
922
True
923
924
The following is a good test because of the asserts in the code::
925
926
sage: v = [Q.modp_splitting_data(p) for p in primes(20,1000)]
927
928
929
Proper error handling::
930
931
sage: Q.modp_splitting_data(5)
932
Traceback (most recent call last):
933
...
934
NotImplementedError: algorithm for computing local splittings not implemented in general (currently require the first invariant to be coprime to p)
935
936
sage: Q.modp_splitting_data(2)
937
Traceback (most recent call last):
938
...
939
NotImplementedError: p must be odd
940
"""
941
if self.base_ring() != QQ:
942
raise NotImplementedError("must be rational quaternion algebra")
943
p = ZZ(p)
944
if not p.is_prime():
945
raise ValueError("p (=%s) must be prime"%p)
946
if p == 2:
947
raise NotImplementedError("p must be odd")
948
if self.discriminant() % p == 0:
949
raise ValueError("p (=%s) must be an unramified prime"%p)
950
951
i, j, k = self.gens()
952
F = GF(p)
953
i2 = F(i*i)
954
j2 = F(j*j)
955
956
M = MatrixSpace(F, 2)
957
I = M([0,i2,1,0])
958
if i2 == 0:
959
raise NotImplementedError("algorithm for computing local splittings not implemented in general (currently require the first invariant to be coprime to p)")
960
i2inv = 1/i2
961
a = None
962
for b in list(F):
963
if not b: continue
964
c = j2 + i2inv * b*b
965
if c.is_square():
966
a = -c.sqrt()
967
break
968
969
if a is None:
970
# do a fallback search, maybe needed in char 3 sometimes.
971
for J in M:
972
K = I*J
973
if J*J == j2 and K == -J*I:
974
return I, J, K
975
976
J = M([a,b,(j2-a*a)/b, -a])
977
K = I*J
978
assert K == -J*I, "bug in that I,J don't skew commute"
979
return I, J, K
980
981
def modp_splitting_map(self, p):
982
r"""
983
Return Python map from the (`p`-integral) quaternion algebra to
984
the set of `2\times 2` matrices over `\GF{p}`.
985
986
INPUT:
987
988
- `p` -- prime number
989
990
EXAMPLES::
991
992
sage: Q.<i,j,k> = QuaternionAlgebra(-1, -7)
993
sage: f = Q.modp_splitting_map(13)
994
sage: a = 2+i-j+3*k; b = 7+2*i-4*j+k
995
sage: f(a*b)
996
[12 3]
997
[10 5]
998
sage: f(a)*f(b)
999
[12 3]
1000
[10 5]
1001
"""
1002
I, J, K = self.modp_splitting_data(p)
1003
F = I.base_ring()
1004
def phi(q):
1005
v = [F(a) for a in q.coefficient_tuple()]
1006
return v[0] + I*v[1] + J*v[2] + K*v[3]
1007
return phi
1008
1009
1010
############################################################
1011
# Unpickling
1012
############################################################
1013
def unpickle_QuaternionAlgebra_v0(*key):
1014
"""
1015
The 0th version of pickling for quaternion algebras.
1016
1017
EXAMPLES::
1018
1019
sage: Q = QuaternionAlgebra(-5,-19)
1020
sage: f, t = Q.__reduce__()
1021
sage: sage.algebras.quatalg.quaternion_algebra.unpickle_QuaternionAlgebra_v0(*t)
1022
Quaternion Algebra (-5, -19) with base ring Rational Field
1023
sage: loads(dumps(Q)) == Q
1024
True
1025
sage: loads(dumps(Q)) is Q
1026
True
1027
"""
1028
return QuaternionAlgebra(*key)
1029
1030
1031
class QuaternionOrder(Algebra):
1032
"""
1033
An order in a quaternion algebra.
1034
1035
EXAMPLES::
1036
1037
sage: QuaternionAlgebra(-1,-7).maximal_order()
1038
Order of Quaternion Algebra (-1, -7) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
1039
sage: type(QuaternionAlgebra(-1,-7).maximal_order())
1040
<class 'sage.algebras.quatalg.quaternion_algebra.QuaternionOrder'>
1041
"""
1042
def __init__(self, A, basis, check=True):
1043
"""
1044
INPUT:
1045
1046
- ``A`` - a quaternion algebra
1047
- ``basis`` - list of 4 integral quaternions in ``A``
1048
- ``check`` - whether to do type and other consistency checks
1049
1050
.. note::
1051
1052
** TODO: We do *not* currently check that basis is
1053
closed under multiplication!! **
1054
1055
EXAMPLES::
1056
1057
sage: A.<i,j,k> = QuaternionAlgebra(-3,-5)
1058
sage: sage.algebras.quatalg.quaternion_algebra.QuaternionOrder(A, [1,i,j,k])
1059
Order of Quaternion Algebra (-3, -5) with base ring Rational Field with basis (1, i, j, k)
1060
sage: R = sage.algebras.quatalg.quaternion_algebra.QuaternionOrder(A, [1,2*i,2*j,2*k]); R
1061
Order of Quaternion Algebra (-3, -5) with base ring Rational Field with basis (1, 2*i, 2*j, 2*k)
1062
sage: type(R)
1063
<class 'sage.algebras.quatalg.quaternion_algebra.QuaternionOrder'>
1064
"""
1065
if check:
1066
# right data type
1067
if not isinstance(basis, (list, tuple)):
1068
raise TypeError("basis must be a list or tuple")
1069
# right length
1070
if len(basis) != 4:
1071
raise ValueError("basis must have length 4")
1072
# coerce to common parent
1073
basis = tuple([A(x) for x in basis])
1074
self.__basis = basis
1075
self.__quaternion_algebra = A
1076
ParentWithGens.__init__(self, ZZ, names=None)
1077
1078
def gens(self):
1079
"""
1080
Return generators for self.
1081
1082
EXAMPLES::
1083
1084
sage: QuaternionAlgebra(-1,-7).maximal_order().gens()
1085
(1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
1086
"""
1087
return self.__basis
1088
1089
def ngens(self):
1090
"""
1091
Return the number of generators (which is 4).
1092
1093
EXAMPLES::
1094
1095
sage: QuaternionAlgebra(-1,-7).maximal_order().ngens()
1096
4
1097
"""
1098
return 4
1099
1100
def gen(self, n):
1101
"""
1102
Return the n-th generator.
1103
1104
INPUT:
1105
1106
- ``n`` - an integer between 0 and 3, inclusive.
1107
1108
EXAMPLES::
1109
1110
sage: R = QuaternionAlgebra(-11,-1).maximal_order(); R
1111
Order of Quaternion Algebra (-11, -1) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
1112
sage: R.gen(0)
1113
1/2 + 1/2*j
1114
sage: R.gen(1)
1115
1/2*i + 1/2*k
1116
sage: R.gen(2)
1117
j
1118
sage: R.gen(3)
1119
k
1120
"""
1121
return self.__basis[n]
1122
1123
def __cmp__(self, R):
1124
"""
1125
Compare orders self and other. Two orders are equal if they
1126
have the same basis and are in the same quaternion algebra.
1127
1128
EXAMPLES::
1129
1130
sage: R = QuaternionAlgebra(-11,-1).maximal_order()
1131
sage: R == R # indirect doctest
1132
True
1133
sage: R == QuaternionAlgebra(-13,-1).maximal_order()
1134
False
1135
sage: R==5
1136
False
1137
"""
1138
if not isinstance(R, QuaternionOrder):
1139
return cmp(type(self), type(R))
1140
c = cmp(self.__quaternion_algebra, R.__quaternion_algebra)
1141
if c: return c
1142
return cmp(self.__basis, R.__basis)
1143
1144
1145
def basis(self):
1146
"""
1147
Return fix choice of basis for this quaternion order.
1148
1149
EXAMPLES::
1150
1151
sage: QuaternionAlgebra(-11,-1).maximal_order().basis()
1152
(1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
1153
"""
1154
return self.__basis
1155
1156
def quaternion_algebra(self):
1157
"""
1158
Return ambient quaternion algebra that contains this quaternion order.
1159
1160
EXAMPLES::
1161
1162
sage: QuaternionAlgebra(-11,-1).maximal_order().quaternion_algebra()
1163
Quaternion Algebra (-11, -1) with base ring Rational Field
1164
"""
1165
return self.__quaternion_algebra
1166
1167
def _repr_(self):
1168
"""
1169
Return string representation of this order.
1170
1171
EXAMPLES::
1172
1173
sage: QuaternionAlgebra(-11,-1).maximal_order()._repr_()
1174
'Order of Quaternion Algebra (-11, -1) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)'
1175
sage: QuaternionAlgebra(-11,-1).maximal_order()
1176
Order of Quaternion Algebra (-11, -1) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
1177
"""
1178
return 'Order of %s with basis %s'%(self.quaternion_algebra(), self.basis())
1179
1180
def random_element(self, *args, **kwds):
1181
"""
1182
Return a random element of this order.
1183
1184
The args and kwds are passed to the random_element method of
1185
the integer ring, and we return an element of the form
1186
1187
.. math::
1188
1189
ae_1 + be_2 + ce_3 + de_4
1190
1191
where `e_1`, ..., `e_4` are the basis of this order and `a`,
1192
`b`, `c`, `d` are random integers.
1193
1194
EXAMPLES::
1195
1196
sage: QuaternionAlgebra(-11,-1).maximal_order().random_element()
1197
-4 + i - 4*j + k
1198
sage: QuaternionAlgebra(-11,-1).maximal_order().random_element(-10,10)
1199
-9/2 - 7/2*i - 7/2*j + 3/2*k
1200
"""
1201
return sum( (ZZ.random_element(*args, **kwds) * b for b in self.basis()) )
1202
1203
def intersection(self, other):
1204
"""
1205
Return the intersection of this order with other.
1206
1207
INPUT:
1208
1209
- ``other`` - a quaternion order in the same ambient quaternion algebra
1210
1211
OUTPUT: a quaternion order
1212
1213
EXAMPLES::
1214
1215
sage: R = QuaternionAlgebra(-11,-1).maximal_order()
1216
sage: R.intersection(R)
1217
Order of Quaternion Algebra (-11, -1) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
1218
1219
We intersect various orders in the quaternion algebra ramified at 11::
1220
1221
sage: B = BrandtModule(11,3)
1222
sage: R = B.maximal_order(); S = B.order_of_level_N()
1223
sage: R.intersection(S)
1224
Order of Quaternion Algebra (-1, -11) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 5/2*k, j, 3*k)
1225
sage: R.intersection(S) == S
1226
True
1227
sage: B = BrandtModule(11,5)
1228
sage: T = B.order_of_level_N()
1229
sage: S.intersection(T)
1230
Order of Quaternion Algebra (-1, -11) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 23/2*k, j, 15*k)
1231
"""
1232
if not isinstance(other, QuaternionOrder):
1233
raise TypeError("other must be a QuaternionOrder")
1234
1235
A = self.quaternion_algebra()
1236
if other.quaternion_algebra() != A:
1237
raise ValueError("self and other must be in the same ambient quaternion algebra")
1238
1239
V = A.base_ring()**4
1240
1241
B = V.span([V(list(g)) for g in self.basis()], ZZ)
1242
C = V.span([V(list(g)) for g in other.basis()], ZZ)
1243
1244
# todo -- A(list(e)) could be A(e)
1245
return QuaternionOrder(A, [A(list(e)) for e in B.intersection(C).basis()])
1246
1247
def free_module(self):
1248
"""
1249
Return the free `\\ZZ`-module that corresponds to this order
1250
inside the vector space corresponding to the ambient
1251
quaternion algebra.
1252
1253
OUTPUT: a free `\\ZZ`-module of rank 4
1254
1255
EXAMPLES::
1256
1257
sage: R = QuaternionAlgebra(-11,-1).maximal_order()
1258
sage: R.basis()
1259
(1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
1260
sage: R.free_module()
1261
Free module of degree 4 and rank 4 over Integer Ring
1262
Echelon basis matrix:
1263
[1/2 0 1/2 0]
1264
[ 0 1/2 0 1/2]
1265
[ 0 0 1 0]
1266
[ 0 0 0 1]
1267
"""
1268
try: return self.__free_module
1269
except AttributeError: pass
1270
V = self.quaternion_algebra().base_ring()**4
1271
M = V.span([V(list(g)) for g in self.basis()], ZZ)
1272
self.__free_module = M
1273
return M
1274
1275
def discriminant(self):
1276
r"""
1277
Return the discriminant of this order, which we define as
1278
`\sqrt{ det ( Tr(e_i \bar{e_j} ) ) }`, where `\{e_i\}` is the
1279
basis of the order.
1280
1281
OUTPUT: rational number
1282
1283
EXAMPLES::
1284
1285
sage: QuaternionAlgebra(-11,-1).maximal_order().discriminant()
1286
11
1287
sage: S = BrandtModule(11,5).order_of_level_N()
1288
sage: S.discriminant()
1289
55
1290
sage: type(S.discriminant())
1291
<type 'sage.rings.rational.Rational'>
1292
"""
1293
L = []
1294
for d in self.basis():
1295
MM = []
1296
for e in self.basis():
1297
MM.append( (d * e.conjugate()).reduced_trace() )
1298
L.append(MM)
1299
1300
return (MatrixSpace(QQ, 4, 4)(L)).determinant().sqrt()
1301
1302
def left_ideal(self, gens, check=True):
1303
r"""
1304
Return the ideal with given gens over `\ZZ`.
1305
1306
INPUT:
1307
1308
- ``gens`` -- a list of elements of this quaternion order
1309
1310
- ``check`` -- bool (default: ``True``); if ``False``, then ``gens`` must
1311
4-tuple that forms a Hermite basis for an ideal
1312
1313
EXAMPLES::
1314
1315
sage: R = QuaternionAlgebra(-11,-1).maximal_order()
1316
sage: R.left_ideal([2*a for a in R.basis()])
1317
Fractional ideal (1 + j, i + k, 2*j, 2*k)
1318
"""
1319
if self.base_ring() == ZZ:
1320
return QuaternionFractionalIdeal_rational(gens, left_order=self, check=check)
1321
else:
1322
raise NotImplementedError("ideal only implemented for quaternion algebras over QQ")
1323
1324
def right_ideal(self, gens, check=True):
1325
r"""
1326
Return the ideal with given gens over `\ZZ`.
1327
1328
INPUT:
1329
1330
- ``gens`` -- a list of elements of this quaternion order
1331
1332
- ``check`` -- bool (default: ``True``); if ``False``, then ``gens`` must
1333
4-tuple that forms a Hermite basis for an ideal
1334
1335
EXAMPLES::
1336
1337
sage: R = QuaternionAlgebra(-11,-1).maximal_order()
1338
sage: R.right_ideal([2*a for a in R.basis()])
1339
Fractional ideal (1 + j, i + k, 2*j, 2*k)
1340
"""
1341
if self.base_ring() == ZZ:
1342
return QuaternionFractionalIdeal_rational(gens, right_order=self, check=check)
1343
else:
1344
raise NotImplementedError("ideal only implemented for quaternion algebras over QQ")
1345
1346
def unit_ideal(self):
1347
"""
1348
Return the unit ideal in this quaternion order.
1349
1350
EXAMPLES::
1351
1352
sage: R = QuaternionAlgebra(-11,-1).maximal_order()
1353
sage: I = R.unit_ideal(); I
1354
Fractional ideal (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
1355
"""
1356
if self.base_ring() == ZZ:
1357
return QuaternionFractionalIdeal_rational(self.basis(), left_order=self, right_order=self, check=False)
1358
else:
1359
raise NotImplementedError("ideal only implemented for quaternion algebras over QQ")
1360
1361
def quadratic_form(self):
1362
"""
1363
Return the normalized quadratic form associated to this quaternion order.
1364
1365
OUTPUT: quadratic form
1366
1367
EXAMPLES::
1368
1369
sage: R = BrandtModule(11,13).order_of_level_N()
1370
sage: Q = R.quadratic_form(); Q
1371
Quadratic form in 4 variables over Rational Field with coefficients:
1372
[ 14 253 55 286 ]
1373
[ * 1455 506 3289 ]
1374
[ * * 55 572 ]
1375
[ * * * 1859 ]
1376
sage: Q.theta_series(10)
1377
1 + 2*q + 2*q^4 + 4*q^6 + 4*q^8 + 2*q^9 + O(q^10)
1378
"""
1379
return self.unit_ideal().quadratic_form()
1380
1381
def ternary_quadratic_form(self, include_basis=False):
1382
"""
1383
Return the ternary quadratic form associated to this order.
1384
1385
INPUT:
1386
1387
- ``include_basis`` -- bool (default: False), if True also
1388
return a basis for the dimension 3 subspace `G`
1389
1390
OUTPUT:
1391
1392
- QuadraticForm
1393
1394
- optional basis for dimension 3 subspace
1395
1396
This function computes the positive definition quadratic form
1397
obtained by letting G be the trace zero subspace of `\ZZ` +
1398
2* ``self``, which has rank 3, and restricting the pairing
1399
1400
(x,y) = (x.conjugate()*y).reduced_trace()
1401
1402
to `G`.
1403
1404
APPLICATIONS: Ternary quadratic forms associated to an order
1405
in a rational quaternion algebra are useful in computing with
1406
Gross points, in decided whether quaternion orders have
1407
embeddings from orders in quadratic imaginary fields, and in
1408
computing elements of the Kohnen plus subspace of modular
1409
forms of weight 3/2.
1410
1411
EXAMPLES::
1412
1413
sage: R = BrandtModule(11,13).order_of_level_N()
1414
sage: Q = R.ternary_quadratic_form(); Q
1415
Quadratic form in 3 variables over Rational Field with coefficients:
1416
[ 5820 1012 13156 ]
1417
[ * 55 1144 ]
1418
[ * * 7436 ]
1419
sage: factor(Q.disc())
1420
2^4 * 11^2 * 13^2
1421
1422
The following theta series is a modular form of weight 3/2 and level 4*11*13::
1423
1424
sage: Q.theta_series(100)
1425
1 + 2*q^23 + 2*q^55 + 2*q^56 + 2*q^75 + 4*q^92 + O(q^100)
1426
"""
1427
if self.base_ring() != ZZ:
1428
raise NotImplementedError("ternary quadratic form of order only implemented for quaternion algebras over QQ")
1429
1430
Q = self.quaternion_algebra()
1431
# 2*R + ZZ
1432
twoR = self.free_module().scale(2)
1433
A = twoR.ambient_module()
1434
Z = twoR.span( [Q(1).coefficient_tuple()], ZZ)
1435
S = twoR + Z
1436
# Now we intersect with the trace 0 submodule
1437
v = [b.reduced_trace() for b in Q.basis()]
1438
M = matrix(QQ,4,1,v)
1439
tr0 = M.kernel()
1440
G = tr0.intersection(S)
1441
B = [Q(a) for a in G.basis()]
1442
m = matrix(QQ,[[x.pair(y) for x in B] for y in B])
1443
from sage.quadratic_forms.quadratic_form import QuadraticForm
1444
Q = QuadraticForm(m)
1445
if include_basis:
1446
return Q, B
1447
else:
1448
return Q
1449
1450
class QuaternionFractionalIdeal(Ideal_fractional):
1451
pass
1452
1453
class QuaternionFractionalIdeal_rational(QuaternionFractionalIdeal):
1454
"""
1455
A fractional ideal in a rational quaternion algebra.
1456
"""
1457
def __init__(self, basis, left_order=None, right_order=None, check=True):
1458
"""
1459
INPUT:
1460
1461
- ``left_order`` -- a quaternion order or ``None``
1462
1463
- ``right_order`` -- a quaternion order or ``None``
1464
1465
- ``basis`` -- tuple of length 4 of elements in of ambient
1466
quaternion algebra whose `\\ZZ`-span is an ideal
1467
1468
- ``check`` -- bool (default: ``True``); if ``False``, do no type
1469
checking, and the input basis *must* be in Hermite form.
1470
1471
EXAMPLES::
1472
1473
sage: R = QuaternionAlgebra(-11,-1).maximal_order()
1474
sage: R.right_ideal(R.basis())
1475
Fractional ideal (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
1476
sage: R.right_ideal(tuple(R.basis()), check=False)
1477
Fractional ideal (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
1478
"""
1479
if check:
1480
if left_order is not None and not isinstance(left_order, QuaternionOrder):
1481
raise TypeError("left_order must be a quaternion order or None")
1482
if right_order is not None and not isinstance(right_order, QuaternionOrder):
1483
raise TypeError("right_order must be a quaternion order or None")
1484
if not isinstance(basis, (list, tuple)):
1485
raise TypeError("basis must be a list or tuple")
1486
1487
self.__left_order = left_order
1488
self.__right_order = right_order
1489
1490
if check:
1491
try:
1492
Q = self.quaternion_order().quaternion_algebra()
1493
except RuntimeError:
1494
Q = basis[0].parent()
1495
basis = tuple([Q(v) for v in
1496
(QQ**4).span([Q(v).coefficient_tuple() for v in basis], ZZ).basis()])
1497
self.__basis = basis
1498
1499
def scale(self, alpha, left=False):
1500
r"""
1501
Scale the fractional ideal self by multiplying the basis by alpha.
1502
1503
INPUT:
1504
1505
- `\alpha` -- element of quaternion algebra
1506
1507
- ``left`` -- bool (default: False); if true multiply
1508
`\alpha` on the left, otherwise multiply `\alpha` on the
1509
right.
1510
1511
OUTPUT:
1512
1513
- a new fractional ideal
1514
1515
1516
EXAMPLES::
1517
1518
sage: B = BrandtModule(5,37); I = B.right_ideals()[0]; i,j,k = B.quaternion_algebra().gens(); I
1519
Fractional ideal (2 + 2*j + 106*k, i + 2*j + 105*k, 4*j + 64*k, 148*k)
1520
sage: I.scale(i)
1521
Fractional ideal [2*i + 212*j - 2*k, -2 + 210*j - 2*k, 128*j - 4*k, 296*j]
1522
sage: I.scale(i, left=True)
1523
Fractional ideal [2*i - 212*j + 2*k, -2 - 210*j + 2*k, -128*j + 4*k, -296*j]
1524
sage: I.scale(i, left=False)
1525
Fractional ideal [2*i + 212*j - 2*k, -2 + 210*j - 2*k, 128*j - 4*k, 296*j]
1526
sage: i * I.gens()[0]
1527
2*i - 212*j + 2*k
1528
sage: I.gens()[0] * i
1529
2*i + 212*j - 2*k
1530
"""
1531
1532
Q = self.quaternion_algebra()
1533
alpha = Q(alpha)
1534
if left:
1535
gens = [alpha*b for b in self.basis()]
1536
else:
1537
gens = [b*alpha for b in self.basis()]
1538
return Q.ideal(gens, left_order = self.__left_order,
1539
right_order = self.__right_order, check=False)
1540
1541
def quaternion_algebra(self):
1542
"""
1543
Return the ambient quaternion algebra that contains this fractional ideal.
1544
1545
OUTPUT: a quaternion algebra
1546
1547
EXAMPLES::
1548
1549
sage: I = BrandtModule(3,5).right_ideals()[1]; I
1550
Fractional ideal (2 + 6*j + 4*k, 2*i + 4*j + 34*k, 8*j + 32*k, 40*k)
1551
sage: I.quaternion_algebra()
1552
Quaternion Algebra (-1, -3) with base ring Rational Field
1553
"""
1554
try: return self.__quaternion_algebra
1555
except AttributeError: pass
1556
A = self.__basis[0].parent()
1557
self.__quaternion_algebra = A
1558
return A
1559
1560
def _compute_order(self, side='left'):
1561
r"""
1562
Used internally to compute either the left or right order
1563
associated to an ideal in a quaternion algebra. If
1564
action='right', compute the left order, and if action='left'
1565
compute the right order.
1566
1567
INPUT:
1568
1569
- ``side`` -- 'left' or 'right'
1570
1571
EXAMPLES::
1572
1573
sage: R.<i,j,k> = QuaternionAlgebra(-1,-11)
1574
sage: I = R.ideal([2 + 2*j + 140*k, 2*i + 4*j + 150*k, 8*j + 104*k, 152*k])
1575
sage: Ol = I._compute_order('left'); Ol
1576
Order of Quaternion Algebra (-1, -11) with base ring Rational Field with basis (1/2 + 1/2*j + 35*k, 1/4*i + 1/2*j + 75/4*k, j + 32*k, 38*k)
1577
sage: Or = I._compute_order('right'); Or
1578
Order of Quaternion Algebra (-1, -11) with base ring Rational Field with basis (1/2 + 1/2*j + 16*k, 1/2*i + 11/2*k, j + 13*k, 19*k)
1579
sage: Ol.discriminant()
1580
209
1581
sage: Or.discriminant()
1582
209
1583
sage: I.left_order() == Ol
1584
True
1585
sage: I.right_order() == Or
1586
True
1587
1588
ALGORITHM: Let `b_1, b_2, b_3, b_3` be a basis for this
1589
fractional ideal `I`, and assume we want to compute the left
1590
order of `I` in the quaternion algebra `Q`. Then
1591
multiplication by `b_i` on the right defines a map `B_i:Q \to
1592
Q`. We have
1593
`R = B_1^{-1}(I) \cap B_2^{-1}(I) \cap B_3^{-1}(I)\cap B_4^{-1}(I).`
1594
This is because
1595
`B_n^{-1}(I) = \{\alpha \in Q : \alpha b_n \in I \},`
1596
and
1597
`R = \{\alpha \in Q : \alpha b_n \in I, n=1,2,3,4\}.`
1598
"""
1599
if side == 'left': action = 'right'
1600
elif side == 'right': action = 'left'
1601
else: ValueError, "side must be 'left' or 'right'"
1602
Q = self.quaternion_algebra()
1603
if Q.base_ring() != QQ:
1604
raise NotImplementedError("computation of left and right orders only implemented over QQ")
1605
M = [(~b).matrix(action=action) for b in self.basis()]
1606
B = self.basis_matrix()
1607
invs = [B*m for m in M]
1608
# Now intersect the row spans of each matrix in invs
1609
ISB = [Q(v) for v in intersection_of_row_modules_over_ZZ(invs).row_module(ZZ).basis()]
1610
return Q.quaternion_order(ISB)
1611
1612
def left_order(self):
1613
"""
1614
Return the left order associated to this fractional ideal.
1615
1616
OUTPUT: an order in a quaternion algebra
1617
1618
EXAMPLES::
1619
1620
sage: B = BrandtModule(11)
1621
sage: R = B.maximal_order()
1622
sage: I = R.unit_ideal()
1623
sage: I.left_order()
1624
Order of Quaternion Algebra (-1, -11) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
1625
1626
We do a consistency check::
1627
1628
sage: B = BrandtModule(11,19); R = B.right_ideals()
1629
sage: print [r.left_order().discriminant() for r in R]
1630
[209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209]
1631
"""
1632
if self.__left_order is None:
1633
self.__left_order = self._compute_order(side='left')
1634
return self.__left_order
1635
1636
def right_order(self):
1637
"""
1638
Return the right order associated to this fractional ideal.
1639
1640
OUTPUT: an order in a quaternion algebra
1641
1642
EXAMPLES::
1643
1644
sage: I = BrandtModule(389).right_ideals()[1]; I
1645
Fractional ideal (2 + 6*j + 2*k, i + 2*j + k, 8*j, 8*k)
1646
sage: I.right_order()
1647
Order of Quaternion Algebra (-2, -389) with base ring Rational Field with basis (1/2 + 1/2*j + 1/2*k, 1/4*i + 1/2*j + 1/4*k, j, k)
1648
sage: I.left_order()
1649
Order of Quaternion Algebra (-2, -389) with base ring Rational Field with basis (1/2 + 1/2*j + 3/2*k, 1/8*i + 1/4*j + 9/8*k, j + k, 2*k)
1650
1651
The following is a big consistency check. We take reps for
1652
all the right ideal classes of a certain order, take the
1653
corresponding left orders, then take ideals in the left orders
1654
and from those compute the right order again::
1655
1656
sage: B = BrandtModule(11,19); R = B.right_ideals()
1657
sage: O = [r.left_order() for r in R]
1658
sage: J = [O[i].left_ideal(R[i].basis()) for i in range(len(R))]
1659
sage: len(set(J))
1660
18
1661
sage: len(set([I.right_order() for I in J]))
1662
1
1663
sage: J[0].right_order() == B.order_of_level_N()
1664
True
1665
"""
1666
if self.__right_order is None:
1667
self.__right_order = self._compute_order(side='right')
1668
return self.__right_order
1669
1670
def __repr__(self):
1671
"""
1672
Return string representation of this quaternion fractional ideal.
1673
1674
EXAMPLES::
1675
1676
sage: I = BrandtModule(11).right_ideals()[1]
1677
sage: type(I)
1678
<class 'sage.algebras.quatalg.quaternion_algebra.QuaternionFractionalIdeal_rational'>
1679
sage: I.__repr__()
1680
'Fractional ideal (2 + 6*j + 4*k, 2*i + 4*j + 2*k, 8*j, 8*k)'
1681
"""
1682
return 'Fractional ideal %s'%(self.gens(),)
1683
1684
def quaternion_order(self):
1685
"""
1686
Return the order for which this ideal is a left or right
1687
fractional ideal. If this ideal has both a left and right
1688
ideal structure, then the left order is returned. If it has
1689
neither structure, then an error is raised.
1690
1691
OUTPUT: QuaternionOrder
1692
1693
EXAMPLES::
1694
1695
sage: R = QuaternionAlgebra(-11,-1).maximal_order()
1696
sage: R.unit_ideal().quaternion_order() is R
1697
True
1698
"""
1699
try: return self.__quaternion_order
1700
except AttributeError: pass
1701
if self.__left_order is not None:
1702
A = self.__left_order
1703
elif self.__right_order is not None:
1704
A = self.__right_order
1705
else:
1706
raise RuntimeError("unable to determine quaternion order of ideal without known order")
1707
self.__quaternion_order = A
1708
return A
1709
1710
def ring(self):
1711
"""
1712
Return ring that this is a fractional ideal for.
1713
1714
EXAMPLES::
1715
1716
sage: R = QuaternionAlgebra(-11,-1).maximal_order()
1717
sage: R.unit_ideal().ring() is R
1718
True
1719
"""
1720
return self.quaternion_order()
1721
1722
def basis(self):
1723
"""
1724
Return basis for this fractional ideal. The basis is in Hermite form.
1725
1726
OUTPUT: tuple
1727
1728
EXAMPLES::
1729
1730
sage: QuaternionAlgebra(-11,-1).maximal_order().unit_ideal().basis()
1731
(1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
1732
"""
1733
return self.__basis
1734
1735
def gens(self):
1736
"""
1737
Return the generators for this ideal, which are the same as
1738
the `\\ZZ`-basis for this ideal.
1739
1740
EXAMPLES::
1741
1742
sage: QuaternionAlgebra(-11,-1).maximal_order().unit_ideal().gens()
1743
(1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
1744
"""
1745
return self.__basis
1746
1747
def __cmp__(self, right):
1748
"""
1749
Compare this fractional quaternion ideal to ``right``. If
1750
``right`` is not a fractional quaternion ideal a TypeError is
1751
raised. If the fractional ideals are in different ambient
1752
quaternion algebras, then the quaternion algebras themselves
1753
are compared.
1754
1755
INPUT:
1756
1757
- ``right`` - another fractional quaternion ideal
1758
1759
EXAMPLES::
1760
1761
sage: I = QuaternionAlgebra(-11,-1).maximal_order().unit_ideal()
1762
sage: I == I # indirect doctest
1763
True
1764
sage: I == 5
1765
False
1766
"""
1767
if not isinstance(right, QuaternionFractionalIdeal_rational):
1768
raise TypeError
1769
return cmp(self.__basis, right.__basis)
1770
1771
def basis_matrix(self):
1772
"""
1773
Return basis matrix `M` in Hermite normal form for self as a
1774
matrix with rational entries.
1775
1776
If `Q` is the ambient quaternion algebra, then the `\\ZZ`-span of
1777
the rows of `M` viewed as linear combinations of Q.basis() =
1778
`[1,i,j,k]` is the fractional ideal self. Also, M *
1779
M.denominator() is an integer matrix in Hermite normal form.
1780
1781
OUTPUT: matrix over `\\QQ`
1782
1783
EXAMPLES::
1784
1785
sage: QuaternionAlgebra(-11,-1).maximal_order().unit_ideal().basis_matrix()
1786
[1/2 0 1/2 0]
1787
[ 0 1/2 0 1/2]
1788
[ 0 0 1 0]
1789
[ 0 0 0 1]
1790
"""
1791
try: return self.__hermite_basis_matrix
1792
except AttributeError: pass
1793
B = quaternion_algebra_cython.rational_matrix_from_rational_quaternions(self.__basis)
1794
self.__hermite_basis_matrix = B
1795
return B
1796
1797
def free_module(self):
1798
"""
1799
Return the free module associated to this quaternionic
1800
fractional ideal, viewed as a submodule of
1801
``Q.free_module()``, where ``Q`` is the ambient quaternion
1802
algebra.
1803
1804
OUTPUT: free `\\ZZ`-module of rank 4 embedded in an ambient `\\QQ^4`.
1805
1806
EXAMPLES::
1807
1808
sage: QuaternionAlgebra(-11,-1).maximal_order().unit_ideal().basis_matrix()
1809
[1/2 0 1/2 0]
1810
[ 0 1/2 0 1/2]
1811
[ 0 0 1 0]
1812
[ 0 0 0 1]
1813
1814
This shows that the issue at trac ticket 6760 is fixed::
1815
1816
sage: R.<i,j,k> = QuaternionAlgebra(-1, -13)
1817
sage: I = R.ideal([2+i, 3*i, 5*j, j+k]); I
1818
Fractional ideal (2 + i, 3*i, j + k, 5*k)
1819
sage: I.free_module()
1820
Free module of degree 4 and rank 4 over Integer Ring
1821
Echelon basis matrix:
1822
[2 1 0 0]
1823
[0 3 0 0]
1824
[0 0 1 1]
1825
[0 0 0 5]
1826
"""
1827
try: return self.__free_module
1828
except AttributeError:
1829
M = self.basis_matrix().row_module(ZZ)
1830
self.__free_module = M
1831
return M
1832
1833
def theta_series_vector(self, B):
1834
"""
1835
Return theta series coefficients of self, as a vector of `B` integers.
1836
1837
INPUT:
1838
1839
- ``B`` -- positive integer
1840
1841
OUTPUT: vector over `\\ZZ` with ``B`` entries
1842
1843
EXAMPLES::
1844
1845
sage: I = BrandtModule(37).right_ideals()[1]; I
1846
Fractional ideal (2 + 6*j + 2*k, i + 2*j + k, 8*j, 8*k)
1847
sage: I.theta_series_vector(5)
1848
(1, 0, 2, 2, 6)
1849
sage: I.theta_series_vector(10)
1850
(1, 0, 2, 2, 6, 4, 8, 6, 10, 10)
1851
sage: I.theta_series_vector(5)
1852
(1, 0, 2, 2, 6)
1853
"""
1854
B = Integer(B)
1855
try:
1856
if len(self.__theta_series_vector)>= B: return self.__theta_series_vector[:B]
1857
except AttributeError: pass
1858
V = FreeModule(ZZ, B)
1859
Q = self.quadratic_form()
1860
v = V(Q.representation_number_list(B))
1861
self.__theta_series_vector = v
1862
return v
1863
1864
def quadratic_form(self):
1865
"""
1866
Return the normalized quadratic form associated to this quaternion ideal.
1867
1868
OUTPUT: quadratic form
1869
1870
EXAMPLES::
1871
1872
sage: I = BrandtModule(11).right_ideals()[1]
1873
sage: Q = I.quadratic_form(); Q
1874
Quadratic form in 4 variables over Rational Field with coefficients:
1875
[ 18 22 33 22 ]
1876
[ * 7 22 11 ]
1877
[ * * 22 0 ]
1878
[ * * * 22 ]
1879
sage: Q.theta_series(10)
1880
1 + 12*q^2 + 12*q^3 + 12*q^4 + 12*q^5 + 24*q^6 + 24*q^7 + 36*q^8 + 36*q^9 + O(q^10)
1881
sage: I.theta_series(10)
1882
1 + 12*q^2 + 12*q^3 + 12*q^4 + 12*q^5 + 24*q^6 + 24*q^7 + 36*q^8 + 36*q^9 + O(q^10)
1883
"""
1884
try: return self.__quadratic_form
1885
except AttributeError: pass
1886
from sage.quadratic_forms.quadratic_form import QuadraticForm
1887
# first get the gram matrix
1888
gram_matrix = self.gram_matrix()
1889
# rescale so that there are no denominators
1890
gram_matrix, _ = gram_matrix._clear_denom()
1891
# Make sure gcd of all entries is 1.
1892
g = gram_matrix.gcd()
1893
if g != 1:
1894
gram_matrix = gram_matrix / g
1895
# now get the quadratic form
1896
Q = QuadraticForm(gram_matrix)
1897
self.__quadratic_form = Q
1898
return Q
1899
1900
def theta_series(self, B, var='q'):
1901
"""
1902
Return normalized theta series of self, as a power series over
1903
`\\ZZ` in the variable ``var``, which is 'q' by default.
1904
1905
The normalized theta series is by definition
1906
1907
.. math::
1908
1909
\\theta_I(q)=\\sum_{x \\in I} q^{\\frac{N(x)}{N(I)}}
1910
1911
INPUT:
1912
1913
- ``B`` -- positive integer
1914
- ``var`` -- string (default: 'q')
1915
1916
OUTPUT: power series
1917
1918
EXAMPLES::
1919
1920
sage: I = BrandtModule(11).right_ideals()[1]; I
1921
Fractional ideal (2 + 6*j + 4*k, 2*i + 4*j + 2*k, 8*j, 8*k)
1922
sage: I.norm()
1923
64
1924
sage: I.theta_series(5)
1925
1 + 12*q^2 + 12*q^3 + 12*q^4 + O(q^5)
1926
sage: I.theta_series(5,'T')
1927
1 + 12*T^2 + 12*T^3 + 12*T^4 + O(T^5)
1928
sage: I.theta_series(3)
1929
1 + 12*q^2 + O(q^3)
1930
"""
1931
try:
1932
if self.__theta_series.prec() >= B:
1933
if var == self.__theta_series.variable():
1934
return self.__theta_series.add_bigoh(B)
1935
else:
1936
ZZ[[var]](self.__theta_series.list()[:B+1])
1937
except AttributeError: pass
1938
v = self.theta_series_vector(B)
1939
theta = ZZ[[var]](v.list()).add_bigoh(B)
1940
self.__theta_series = theta
1941
return theta
1942
1943
def gram_matrix(self):
1944
"""
1945
Return the Gram matrix of this fractional ideal.
1946
1947
OUTPUT: 4x4 matrix over `\\QQ`.
1948
1949
EXAMPLES::
1950
1951
sage: I = BrandtModule(3,5).right_ideals()[1]; I
1952
Fractional ideal (2 + 6*j + 4*k, 2*i + 4*j + 34*k, 8*j + 32*k, 40*k)
1953
sage: I.gram_matrix()
1954
[ 640 1920 2112 1920]
1955
[ 1920 14080 13440 16320]
1956
[ 2112 13440 13056 15360]
1957
[ 1920 16320 15360 19200]
1958
"""
1959
try: return self.__gram_matrix
1960
except AttributeError: pass
1961
M = []
1962
A = self.__basis
1963
B = [z.conjugate() for z in self.__basis]
1964
two = QQ(2)
1965
m = [two*(a*b).reduced_trace() for b in B for a in A]
1966
M44 = MatrixSpace(QQ, 4)
1967
G = M44(m,coerce=False)
1968
self.__gram_matrix = G
1969
return G
1970
1971
def norm(self):
1972
"""
1973
Return the norm of this fractional ideal.
1974
1975
OUTPUT: rational number
1976
1977
EXAMPLES::
1978
1979
sage: C = BrandtModule(37).right_ideals()
1980
sage: [I.norm() for I in C]
1981
[32, 64, 64]
1982
"""
1983
G = self.gram_matrix()
1984
r = G.det().abs()
1985
assert r.is_square(), "first is bad!"
1986
r = r.sqrt()
1987
r/= self.quaternion_order().discriminant()
1988
assert r.is_square(), "second is bad!"
1989
return r.sqrt()
1990
1991
def conjugate(self):
1992
"""
1993
Return the ideal with generators the conjugates of the generators for self.
1994
1995
OUTPUT: a quaternionic fractional ideal
1996
1997
EXAMPLES::
1998
1999
sage: I = BrandtModule(3,5).right_ideals()[1]; I
2000
Fractional ideal (2 + 6*j + 4*k, 2*i + 4*j + 34*k, 8*j + 32*k, 40*k)
2001
sage: I.conjugate()
2002
Fractional ideal (2 + 2*j + 28*k, 2*i + 4*j + 34*k, 8*j + 32*k, 40*k)
2003
"""
2004
return self.quaternion_algebra().ideal([b.conjugate() for b in self.basis()],
2005
left_order=self.__right_order,
2006
right_order=self.__left_order)
2007
2008
def __mul__(self, right):
2009
"""
2010
Return the product of the fractional ideals ``self`` and ``right``.
2011
2012
.. note::
2013
2014
We do not keep track of left or right order structure.
2015
2016
EXAMPLES::
2017
2018
sage: I = BrandtModule(3,5).right_ideals()[1]; I
2019
Fractional ideal (2 + 6*j + 4*k, 2*i + 4*j + 34*k, 8*j + 32*k, 40*k)
2020
sage: I*I
2021
Fractional ideal (8 + 24*j + 16*k, 8*i + 16*j + 136*k, 32*j + 128*k, 160*k)
2022
sage: I*I.conjugate()
2023
Fractional ideal (16 + 16*j + 224*k, 8*i + 16*j + 136*k, 32*j + 128*k, 320*k)
2024
sage: I.multiply_by_conjugate(I)
2025
Fractional ideal (16 + 16*j + 224*k, 8*i + 16*j + 136*k, 32*j + 128*k, 320*k)
2026
"""
2027
if not isinstance(right, QuaternionFractionalIdeal_rational):
2028
return self._scale(right, left=False)
2029
gens = [a*b for a in self.basis() for b in right.basis()]
2030
#if self.__right_order == right.__left_order:
2031
# left_order = self.__left_order
2032
# right_order = right.__right_order
2033
basis = tuple(basis_for_quaternion_lattice(gens))
2034
A = self.quaternion_algebra()
2035
return A.ideal(basis, check=False)
2036
2037
@cached_method
2038
def free_module(self):
2039
r"""
2040
Return the underlying free `\ZZ`-module corresponding to this ideal.
2041
2042
EXAMPLES::
2043
2044
sage: X = BrandtModule(3,5).right_ideals()
2045
sage: X[0]
2046
Fractional ideal (2 + 2*j + 8*k, 2*i + 18*k, 4*j + 16*k, 20*k)
2047
sage: X[0].free_module()
2048
Free module of degree 4 and rank 4 over Integer Ring
2049
Echelon basis matrix:
2050
[ 2 0 2 8]
2051
[ 0 2 0 18]
2052
[ 0 0 4 16]
2053
[ 0 0 0 20]
2054
sage: X[0].scale(1/7).free_module()
2055
Free module of degree 4 and rank 4 over Integer Ring
2056
Echelon basis matrix:
2057
[ 2/7 0 2/7 8/7]
2058
[ 0 2/7 0 18/7]
2059
[ 0 0 4/7 16/7]
2060
[ 0 0 0 20/7]
2061
2062
The free module method is also useful since it allows for checking if one ideal
2063
is contained in another, computing quotients I/J, etc.::
2064
2065
sage: X = BrandtModule(3,17).right_ideals()
2066
sage: I = X[0].intersection(X[2]); I
2067
Fractional ideal (2 + 2*j + 164*k, 2*i + 4*j + 46*k, 16*j + 224*k, 272*k)
2068
sage: I.free_module().is_submodule(X[3].free_module())
2069
False
2070
sage: I.free_module().is_submodule(X[1].free_module())
2071
True
2072
sage: X[0].free_module() / I.free_module()
2073
Finitely generated module V/W over Integer Ring with invariants (4, 4)
2074
"""
2075
return self.basis_matrix().row_module(ZZ)
2076
2077
def intersection(self, J):
2078
"""
2079
Return the intersection of the ideals self and `J`.
2080
2081
EXAMPLES::
2082
2083
sage: X = BrandtModule(3,5).right_ideals()
2084
sage: I = X[0].intersection(X[1]); I
2085
Fractional ideal (2 + 6*j + 4*k, 2*i + 4*j + 34*k, 8*j + 32*k, 40*k)
2086
2087
"""
2088
V = self.free_module().intersection(J.free_module())
2089
H,d = V.basis_matrix()._clear_denom()
2090
A = self.quaternion_algebra()
2091
gens = quaternion_algebra_cython.rational_quaternions_from_integral_matrix_and_denom(A, H, d)
2092
return A.ideal(gens)
2093
2094
def multiply_by_conjugate(self, J):
2095
"""
2096
Return product of self and the conjugate Jbar of `J`.
2097
2098
INPUT:
2099
2100
- ``J`` -- a quaternion ideal.
2101
2102
OUTPUT: a quaternionic fractional ideal.
2103
2104
EXAMPLES::
2105
2106
sage: R = BrandtModule(3,5).right_ideals()
2107
sage: R[0].multiply_by_conjugate(R[1])
2108
Fractional ideal (8 + 8*j + 112*k, 8*i + 16*j + 136*k, 32*j + 128*k, 160*k)
2109
sage: R[0]*R[1].conjugate()
2110
Fractional ideal (8 + 8*j + 112*k, 8*i + 16*j + 136*k, 32*j + 128*k, 160*k)
2111
"""
2112
Jbar = [b.conjugate() for b in J.basis()]
2113
gens = [a*b for a in self.basis() for b in Jbar]
2114
basis = tuple(basis_for_quaternion_lattice(gens))
2115
R = self.quaternion_algebra()
2116
return R.ideal(basis, check=False)
2117
2118
def is_equivalent(I, J, B=10):
2119
"""
2120
Return ``True`` if ``I`` and ``J`` are equivalent as right ideals.
2121
2122
INPUT:
2123
2124
- ``I`` -- a fractional quaternion ideal (self)
2125
2126
- ``J`` -- a fractional quaternion ideal with same order as ``I``
2127
2128
- ``B`` -- a bound to compute and compare theta series before
2129
doing the full equivalence test
2130
2131
OUTPUT: bool
2132
2133
EXAMPLES::
2134
2135
sage: R = BrandtModule(3,5).right_ideals(); len(R)
2136
2
2137
sage: R[0].is_equivalent(R[1])
2138
False
2139
sage: R[0].is_equivalent(R[0])
2140
True
2141
sage: OO = R[0].quaternion_order()
2142
sage: S = OO.right_ideal([3*a for a in R[0].basis()])
2143
sage: R[0].is_equivalent(S)
2144
True
2145
"""
2146
if not isinstance(I, QuaternionFractionalIdeal_rational):
2147
return False
2148
2149
if I.right_order() != J.right_order():
2150
raise ValueError("I and J must be right ideals")
2151
2152
# Just test theta series first. If the theta series are
2153
# different, the ideals are definitely not equivalent.
2154
if B > 0 and I.theta_series_vector(B) != J.theta_series_vector(B):
2155
return False
2156
2157
# The theta series are the same, so perhaps the ideals are
2158
# equivalent. We use Prop 1.18 of [Pizer, 1980] to decide.
2159
# 1. Compute I * Jbar
2160
# see Prop. 1.17 in Pizer. Note that we use IJbar instead of
2161
# JbarI since we work with right ideals
2162
IJbar = I.multiply_by_conjugate(J)
2163
2164
# 2. Determine if there is alpha in K such
2165
# that N(alpha) = N(I)*N(J) as explained by Pizer.
2166
c = IJbar.theta_series_vector(2)[1]
2167
return c != 0
2168
2169
def __contains__(self, x):
2170
"""
2171
Returns whether x is in self.
2172
2173
EXAMPLES::
2174
sage: R.<i,j,k> = QuaternionAlgebra(-3, -13)
2175
sage: I = R.ideal([2+i, 3*i, 5*j, j+k])
2176
sage: 2+i in I
2177
True
2178
sage: 2+i+j+k in I
2179
True
2180
sage: 1+i in I
2181
False
2182
sage: 101*j + k in I
2183
True
2184
"""
2185
try:
2186
x = self.quaternion_algebra()(x)
2187
return self.basis_matrix().transpose().solve_right(vector(x)) in ZZ**4
2188
except (ValueError, TypeError):
2189
return False
2190
2191
@cached_method
2192
def cyclic_right_subideals(self, p, alpha=None):
2193
r"""
2194
Let `I` = ``self``. This function returns the right subideals
2195
`J` of `I` such that `I/J` is an `\GF{p}`-vector space of
2196
dimension 2.
2197
2198
INPUT:
2199
2200
- `p` -- prime number (see below)
2201
2202
- `alpha` -- (default: None) element of quaternion algebra,
2203
which can be used to parameterize the order of the
2204
ideals `J`. More precisely the `J`'s are the right annihilators
2205
of `(1,0) \alpha^i` for `i=0,1,2,...,p`
2206
2207
OUTPUT:
2208
2209
- list of right ideals
2210
2211
.. note::
2212
2213
Currently, `p` must satisfy a bunch of conditions, or a
2214
NotImplementedError is raised. In particular, `p` must be
2215
odd and unramified in the quaternion algebra, must be
2216
coprime to the index of the right order in the maximal
2217
order, and also coprime to the normal of self. (The Brandt
2218
modules code has a more general algorithm in some cases.)
2219
2220
EXAMPLES::
2221
2222
sage: B = BrandtModule(2,37); I = B.right_ideals()[0]
2223
sage: I.cyclic_right_subideals(3)
2224
[Fractional ideal (2 + 2*i + 10*j + 90*k, 4*i + 4*j + 152*k, 12*j + 132*k, 444*k), Fractional ideal (2 + 2*i + 2*j + 150*k, 4*i + 8*j + 196*k, 12*j + 132*k, 444*k), Fractional ideal (2 + 2*i + 6*j + 194*k, 4*i + 8*j + 344*k, 12*j + 132*k, 444*k), Fractional ideal (2 + 2*i + 6*j + 46*k, 4*i + 4*j + 4*k, 12*j + 132*k, 444*k)]
2225
2226
sage: B = BrandtModule(5,389); I = B.right_ideals()[0]
2227
sage: C = I.cyclic_right_subideals(3); C
2228
[Fractional ideal (2 + 10*j + 546*k, i + 6*j + 133*k, 12*j + 3456*k, 4668*k), Fractional ideal (2 + 2*j + 2910*k, i + 6*j + 3245*k, 12*j + 3456*k, 4668*k), Fractional ideal (2 + i + 2295*k, 3*i + 2*j + 3571*k, 4*j + 2708*k, 4668*k), Fractional ideal (2 + 2*i + 2*j + 4388*k, 3*i + 2*j + 2015*k, 4*j + 4264*k, 4668*k)]
2229
sage: [(I.free_module()/J.free_module()).invariants() for J in C]
2230
[(3, 3), (3, 3), (3, 3), (3, 3)]
2231
sage: I.scale(3).cyclic_right_subideals(3)
2232
[Fractional ideal (6 + 30*j + 1638*k, 3*i + 18*j + 399*k, 36*j + 10368*k, 14004*k), Fractional ideal (6 + 6*j + 8730*k, 3*i + 18*j + 9735*k, 36*j + 10368*k, 14004*k), Fractional ideal (6 + 3*i + 6885*k, 9*i + 6*j + 10713*k, 12*j + 8124*k, 14004*k), Fractional ideal (6 + 6*i + 6*j + 13164*k, 9*i + 6*j + 6045*k, 12*j + 12792*k, 14004*k)]
2233
sage: C = I.scale(1/9).cyclic_right_subideals(3); C
2234
[Fractional ideal (2/9 + 10/9*j + 182/3*k, 1/9*i + 2/3*j + 133/9*k, 4/3*j + 384*k, 1556/3*k), Fractional ideal (2/9 + 2/9*j + 970/3*k, 1/9*i + 2/3*j + 3245/9*k, 4/3*j + 384*k, 1556/3*k), Fractional ideal (2/9 + 1/9*i + 255*k, 1/3*i + 2/9*j + 3571/9*k, 4/9*j + 2708/9*k, 1556/3*k), Fractional ideal (2/9 + 2/9*i + 2/9*j + 4388/9*k, 1/3*i + 2/9*j + 2015/9*k, 4/9*j + 4264/9*k, 1556/3*k)]
2235
sage: [(I.scale(1/9).free_module()/J.free_module()).invariants() for J in C]
2236
[(3, 3), (3, 3), (3, 3), (3, 3)]
2237
2238
sage: Q.<i,j,k> = QuaternionAlgebra(-2,-5)
2239
sage: I = Q.ideal([Q(1),i,j,k])
2240
sage: I.cyclic_right_subideals(3)
2241
[Fractional ideal (1 + 2*j, i + k, 3*j, 3*k), Fractional ideal (1 + j, i + 2*k, 3*j, 3*k), Fractional ideal (1 + 2*i, 3*i, j + 2*k, 3*k), Fractional ideal (1 + i, 3*i, j + k, 3*k)]
2242
2243
The general algorithm is not yet implemented here::
2244
2245
sage: I.cyclic_right_subideals(3)[0].cyclic_right_subideals(3)
2246
Traceback (most recent call last):
2247
...
2248
NotImplementedError: general algorithm not implemented (The given basis vectors must be linearly independent.)
2249
"""
2250
R = self.right_order()
2251
Q = self.quaternion_algebra()
2252
f = Q.modp_splitting_map(p)
2253
if alpha is not None:
2254
alpha = f(alpha)
2255
W = GF(p)**4
2256
try:
2257
A = W.span_of_basis([W(f(a).list()) for a in self.basis()])
2258
scale = 1
2259
IB = self.basis_matrix()
2260
except (ValueError, ZeroDivisionError):
2261
# try rescaling the ideal.
2262
B, d = self.basis_matrix()._clear_denom()
2263
g = gcd(B.list())
2264
IB = B/g
2265
scale = g/d
2266
try:
2267
A = W.span_of_basis([W(f(Q(a.list())).list()) for a in IB.rows()])
2268
except (ValueError, ZeroDivisionError) as msg:
2269
# Here we could replace the ideal by an *equivalent*
2270
# ideal that works. This is always possible.
2271
# However, I haven't implemented that algorithm yet.
2272
raise NotImplementedError("general algorithm not implemented (%s)"%msg)
2273
2274
Ai = A.basis_matrix()**(-1)
2275
AiB = Ai.change_ring(QQ) * IB
2276
2277
# Do not care about the denominator since we're really working in I/p*I.
2278
AiB, _ = AiB._clear_denom()
2279
2280
pB = p*IB
2281
pB, d = pB._clear_denom()
2282
2283
ans = []
2284
Z = matrix(ZZ,2,4)
2285
2286
P1 = P1List(p)
2287
if alpha is None:
2288
lines = P1
2289
else:
2290
x = alpha
2291
lines = []
2292
for i in range(p+1):
2293
lines.append(P1.normalize(x[0,0], x[0,1]))
2294
x *= alpha
2295
2296
for u,v in lines:
2297
# The following does:
2298
# z = matrix(QQ,2,4,[0,-v,0,u, -v,0,u,0],check=False) * AiB
2299
Z[0,1]=-v; Z[0,3]=u; Z[1,0]=-v; Z[1,2]=u
2300
z = Z * AiB
2301
# Now construct submodule of the ideal I spanned by the
2302
# linear combinations given by z of the basis for J along
2303
# with p*I.
2304
G = (d*z).stack(pB) # have to multiply by d since we divide by it below in the "gens = " line.
2305
H = G._hnf_pari(0, include_zero_rows=False)
2306
gens = tuple(quaternion_algebra_cython.rational_quaternions_from_integral_matrix_and_denom(Q, H, d))
2307
if scale != 1:
2308
gens = tuple([scale*g for g in gens])
2309
J = R.right_ideal(gens, check=False)
2310
ans.append(J)
2311
return ans
2312
2313
#######################################################################
2314
# Some utility functions that are needed here and are too
2315
# specialized to go elsewhere.
2316
#######################################################################
2317
2318
def basis_for_quaternion_lattice(gens):
2319
"""
2320
Return a basis for the `\\ZZ`-lattice in a quaternion algebra
2321
spanned by the given gens.
2322
2323
INPUT:
2324
2325
- ``gens`` -- list of elements of a single quaternion algebra
2326
2327
EXAMPLES::
2328
2329
sage: A.<i,j,k> = QuaternionAlgebra(-1,-7)
2330
sage: sage.algebras.quatalg.quaternion_algebra.basis_for_quaternion_lattice([i+j, i-j, 2*k, A(1/3)])
2331
[1/3, i + j, 2*j, 2*k]
2332
"""
2333
if len(gens) == 0: return []
2334
Z, d = quaternion_algebra_cython.integral_matrix_and_denom_from_rational_quaternions(gens)
2335
H = Z._hnf_pari(0, include_zero_rows=False)
2336
A = gens[0].parent()
2337
return quaternion_algebra_cython.rational_quaternions_from_integral_matrix_and_denom(A, H, d)
2338
2339
2340
def intersection_of_row_modules_over_ZZ(v):
2341
"""
2342
Intersects the `\ZZ`-modules with basis matrices the full rank 4x4
2343
`\QQ`-matrices in the list v. The returned intersection is
2344
represented by a 4x4 matrix over `\QQ`. This can also be done using
2345
modules and intersection, but that would take over twice as long
2346
because of overhead, hence this function.
2347
2348
EXAMPLES::
2349
2350
sage: a = matrix(QQ,4,[-2, 0, 0, 0, 0, -1, -1, 1, 2, -1/2, 0, 0, 1, 1, -1, 0])
2351
sage: b = matrix(QQ,4,[0, -1/2, 0, -1/2, 2, 1/2, -1, -1/2, 1, 2, 1, -2, 0, -1/2, -2, 0])
2352
sage: c = matrix(QQ,4,[0, 1, 0, -1/2, 0, 0, 2, 2, 0, -1/2, 1/2, -1, 1, -1, -1/2, 0])
2353
sage: v = [a,b,c]
2354
sage: from sage.algebras.quatalg.quaternion_algebra import intersection_of_row_modules_over_ZZ
2355
sage: M = intersection_of_row_modules_over_ZZ(v); M
2356
[ -2 0 1 1]
2357
[ -4 1 1 -3]
2358
[ -3 19/2 -1 -4]
2359
[ 2 -3 -8 4]
2360
sage: M2 = a.row_module(ZZ).intersection(b.row_module(ZZ)).intersection(c.row_module(ZZ))
2361
sage: M.row_module(ZZ) == M2
2362
True
2363
"""
2364
if len(v) <= 0:
2365
raise ValueError("v must have positive length")
2366
if len(v) == 1:
2367
return v[0]
2368
elif len(v) == 2:
2369
# real work - the base case
2370
a, b = v
2371
s,_ = a.stack(b)._clear_denom()
2372
s = s.transpose()
2373
K = s.right_kernel_matrix(algorithm='pari', basis='computed')
2374
n = a.nrows()
2375
return K.matrix_from_columns(range(n)) * a
2376
else:
2377
# induct
2378
w = intersection_of_row_modules_over_ZZ(v[:2])
2379
return intersection_of_row_modules_over_ZZ([w] + v[2:])
2380
2381
2382