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 55809f3
Showing 1 changed file with 67 additions and 4 deletions.
71 changes: 67 additions & 4 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 @@ -75,8 +77,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 +95,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 +108,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 +191,30 @@ 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
sage: A = QuaternionAlgebra(K, [P,Q], [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 @@ -257,11 +294,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,Rational(1/2)])):
raise ValueError("list of local invariants specifying ramification should contain only 0 and 1/2")
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")
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 = Rational(a)
b = Rational(b)
else:
if len(arg2) != len(K.real_places()):
raise ValueError("must specify ramification at the real places of %s" % K)
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])
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 55809f3

Please sign in to comment.