Skip to content

Commit

Permalink
Add quaternion algebra constructor from ramification.
Browse files Browse the repository at this point in the history
  • Loading branch information
Eloitor committed Jan 31, 2024
1 parent 3dd953c commit 2657bae
Showing 1 changed file with 65 additions and 7 deletions.
72 changes: 65 additions & 7 deletions src/sage/algebras/quatalg/quaternion_algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
- Peter Bruin (2021): do not require the base ring to be a field
- Eloi Torrents (2024): construct quaternion algebras over number fields from ramification
This code is partly based on Sage code by David Kohel from 2005.
TESTS:
Expand Down Expand Up @@ -44,7 +46,6 @@
from sage.rings.real_mpfr import RR
from sage.rings.integer import Integer
from sage.rings.integer_ring import ZZ
from sage.rings.rational import Rational
from sage.rings.finite_rings.finite_field_constructor import GF

from sage.rings.ring import Algebra
Expand Down Expand Up @@ -75,8 +76,12 @@
from sage.modular.modsym.p1list import P1List

from sage.misc.cachefunc import cached_method
from sage.misc.functional import is_odd, category

from sage.categories.algebras import Algebras
from sage.categories.number_fields import NumberFields

from sage.libs.pari.all import pari

########################################################
# Constructor
Expand All @@ -89,7 +94,7 @@ class QuaternionAlgebraFactory(UniqueFactory):
INPUT:
There are three input formats:
There are four input formats:
- ``QuaternionAlgebra(a, b)``, where `a` and `b` can be coerced to
units in a common field `K` of characteristic different from 2.
Expand All @@ -102,6 +107,13 @@ class QuaternionAlgebraFactory(UniqueFactory):
`D` over `K = \QQ`. Suitable nonzero rational numbers `a`, `b`
as above are deduced from `D`.
- ``QuaternionAlgebra(K, primes, inv_archimedean)``, where `primes`
is a list of prime ideals and `inv_archimedean` is a list of local
invariants (0 or 1/2) specifying the ramification at the (infinite)
real places of `K`. This constructs a quaternion algebra ramified
exacly at the places in `primes` and those in `K.real_embeddings()`
indexed by `i` with `inv_archimedean[i]=1/2`.
OUTPUT:
The quaternion algebra `(a, b)_K` over `K` generated by `i`, `j`
Expand Down Expand Up @@ -178,6 +190,26 @@ class QuaternionAlgebraFactory(UniqueFactory):
sage: QuaternionAlgebra(2*3*5*7)
Quaternion Algebra (-22, 210) with base ring Rational Field
``QuaternionAlgebra(K, primes, inv_archimedean)`` -- return the quaternion
algebra over `K` with the specified ramification::
sage: QuaternionAlgebra(QQ, [(2), (3)], [0])
Quaternion Algebra (-1, 3) with base ring Rational Field
sage: QuaternionAlgebra(QQ, [(2), (3)], [1/2])
Traceback (most recent call last):
...
ValueError: Quaternion algebra over the rationals must have an even number of ramified places
sage: K.<w> = NumberField(x^2-x-1)
sage: P = K.prime_above(2)
sage: Q = K.prime_above(3)
sage: A = QuaternionAlgebra(K, [P,Q], [0,0])
sage: A.discriminant()
Fractional ideal (6)
sage: A = QuaternionAlgebra(K, [P,Q], [1/2,0])
Traceback (most recent call last):
...
ValueError: Quaternion algebra over the rationals must have an even number of ramified places
If the coefficients `a` and `b` in the definition of the quaternion
algebra are not integral, then a slower generic type is used for
arithmetic::
Expand Down Expand Up @@ -234,8 +266,8 @@ def create_key(self, arg0, arg1=None, arg2=None, names='i,j,k'):
K = QQ
D = Integer(arg0)
a, b = hilbert_conductor_inverse(D)
a = Rational(a)
b = Rational(b)
a = QQ(a)
b = QQ(b)

elif arg2 is None:
# If arg0 or arg1 are Python data types, coerce them
Expand All @@ -257,11 +289,37 @@ def create_key(self, arg0, arg1=None, arg2=None, names='i,j,k'):
a = K(v[0])
b = K(v[1])

# QuaternionAlgebra(K, a, b)
else:
# QuaternionAlgebra(K, primes, inv_archimedean)
K = arg0
a = K(arg1)
b = K(arg2)
if category(K) is not NumberFields():
raise ValueError("quaternion algbera must be defined over a number field")
if isinstance(arg1, list) and isinstance(arg2, list):
if not set(arg2).issubset(set([0,QQ(1/2)])):
raise ValueError("list of local invariants specifying ramification should contain only 0 and 1/2")

Check warning on line 299 in src/sage/algebras/quatalg/quaternion_algebra.py

View check run for this annotation

Codecov / codecov/patch

src/sage/algebras/quatalg/quaternion_algebra.py#L299

Added line #L299 was not covered by tests
arg1 = list(set(arg1))
if not all([p.is_prime() for p in arg1]):
raise ValueError("quaternion algebra constructor requires a list of primes specifying the ramification")

Check warning on line 302 in src/sage/algebras/quatalg/quaternion_algebra.py

View check run for this annotation

Codecov / codecov/patch

src/sage/algebras/quatalg/quaternion_algebra.py#L302

Added line #L302 was not covered by tests
if is_RationalField(K):
if len(arg2) > 1 or (len(arg2) == 1 and is_odd(len(arg1) + 2*arg2[0])):
raise ValueError("quaternion algebra over the rationals must have an even number of ramified places")
D = ZZ.ideal_monoid().prod(arg1).gen()
a, b = hilbert_conductor_inverse(D)
a = QQ(a)
b = QQ(b)

Check warning on line 309 in src/sage/algebras/quatalg/quaternion_algebra.py

View check run for this annotation

Codecov / codecov/patch

src/sage/algebras/quatalg/quaternion_algebra.py#L304-L309

Added lines #L304 - L309 were not covered by tests
else:
if len(arg2) != len(K.real_places()):
raise ValueError("must specify ramification at the real places of %s" % K)

Check warning on line 312 in src/sage/algebras/quatalg/quaternion_algebra.py

View check run for this annotation

Codecov / codecov/patch

src/sage/algebras/quatalg/quaternion_algebra.py#L312

Added line #L312 was not covered by tests
if is_odd(len(arg1) + 2 * sum(arg2)):
raise ValueError("quaternion algebra over the rationals must have an even number of ramified places")
fin_places_pari = [I.pari_prime() for I in arg1]
A = pari(K).alginit([2, [fin_places_pari, [QQ(1/2)] * len(fin_places_pari)], arg2], maxord=0)
a = K(A.algsplittingfield().disc()[1])
b = K(A.algb())
else:
# QuaternionAlgebra(K, a, b)
a = K(arg1)
b = K(arg2)

if not K(2).is_unit():
raise ValueError("2 is not invertible in %s" % K)
Expand Down

0 comments on commit 2657bae

Please sign in to comment.