Skip to content

Commit

Permalink
gh-37644: Corrections in .normalize_basis_at_p and `.maximal_order(…
Browse files Browse the repository at this point in the history
…)` of `quaternion_algebra.py`

    
- Corrected in `.normalize_basis_at_p` the wrong assignments of `f0,
f1`: The idea is to replace `f0` by `f0 + f1` and `f1` by `f0`
_simultaneously_ - the original code, however, first replaced `f0` by
`f0 + f1` and then copied the value to `f1`, hence causing a decrease in
dimension
- Corrected an index error just before the recursive call of
`normalize_basis_at_p` in the off-diagnonal case for `p = 2` (the
`else`-block)
- In `.maximal_order()` the intermediate basis `e_n` might not define an
order anymore (as seen in the comments, this was known to the author(s)
of the method) - therefore we need to manually compute the discriminant
in the loop instead of relying on the `.discriminant()`-method of
quaternion orders
- Adapted an example in `.maximal_order()` to use the new method
`.is_maximal()`

Fixes #37217 (both parts) and #37417.

Disclaimer:
I am aware of #37239, but since progress on that PR seems to have
halted, I took the liberty to fix the issues myself :)
    
URL: #37644
Reported by: Sebastian A. Spindler
Reviewer(s): Matthias Köppe
  • Loading branch information
Release Manager committed Apr 10, 2024
2 parents 3552314 + 792a98c commit adb7605
Showing 1 changed file with 47 additions and 15 deletions.
62 changes: 47 additions & 15 deletions src/sage/algebras/quatalg/quaternion_algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -757,8 +757,7 @@ def maximal_order(self, take_shortcuts=True):
sage: for d in ( m for m in range(1, 750) if is_squarefree(m) ): # long time (3s)
....: A = QuaternionAlgebra(d)
....: R = A.maximal_order(take_shortcuts=False)
....: assert A.discriminant() == R.discriminant()
....: assert A.maximal_order(take_shortcuts=False).is_maximal()
We do not support number fields other than the rationals yet::
Expand All @@ -768,9 +767,24 @@ def maximal_order(self, take_shortcuts=True):
...
NotImplementedError: maximal order only implemented
for rational quaternion algebras
TESTS:
Check that :issue:`37417` and the first part of :issue:`37217` are fixed::
sage: invars = [(-4, -28), (-292, -732), (-48, -564), (-436, -768),
....: (-752, -708), (885, 545), (411, -710), (-411, 593),
....: (805, -591), (-921, 353), (409, 96), (394, 873),
....: (353, -722), (730, 830), (-466, -427), (-213, -630),
....: (-511, 608), (493, 880), (105, -709), (-213, 530),
....: (97, 745)]
sage: all(QuaternionAlgebra(a, b).maximal_order().is_maximal()
....: for (a, b) in invars)
True
"""
if self.base_ring() != QQ:
raise NotImplementedError("maximal order only implemented for rational quaternion algebras")
raise NotImplementedError("maximal order only implemented for "
"rational quaternion algebras")

d_A = self.discriminant()

Expand Down Expand Up @@ -813,15 +827,17 @@ def maximal_order(self, take_shortcuts=True):

# The following code should always work (over QQ)
# Start with <1,i,j,k>
R = self.quaternion_order((1,) + self.gens())
order_basis = (self.one(),) + self.gens()
R = self.quaternion_order(order_basis)
d_R = R.discriminant()

e_new_gens = []

# For each prime at which R is not yet maximal, make it bigger
for p, _ in d_R.factor():
e = R.basis()
while self.quaternion_order(e).discriminant().valuation(p) > d_A.valuation(p):
e = order_basis
disc = d_R
while disc.valuation(p) > d_A.valuation(p):
# Compute a normalized basis at p
f = normalize_basis_at_p(list(e), p)

Expand Down Expand Up @@ -894,13 +910,18 @@ def maximal_order(self, take_shortcuts=True):
e_n = basis_for_quaternion_lattice(list(e) + e_n[1:], reverse=True)

# e_n now contains elements that locally at p give a bigger order,
# but the basis may be messed up at other primes (it might not even
# be an order). We will join them all together at the end
# but the basis may be messed up at other primes (it might not
# even be an order). We will join them all together at the end
e = e_n

# Since e might not define an order at this point, we need to
# manually calculate the updated discriminant
L = [[x.pair(y) for y in e] for x in e]
disc = matrix(QQ, 4, 4, L).determinant().sqrt()

e_new_gens.extend(e[1:])

e_new = basis_for_quaternion_lattice(list(R.basis()) + e_new_gens, reverse=True)
e_new = basis_for_quaternion_lattice(list(order_basis) + e_new_gens, reverse=True)
return self.quaternion_order(e_new)

def order_with_level(self, level):
Expand Down Expand Up @@ -1895,8 +1916,9 @@ def discriminant(self):
sage: type(S.discriminant())
<... 'sage.rings.rational.Rational'>
"""
L = [[d.pair(e) for e in self.basis()] for d in self.basis()]
return (MatrixSpace(QQ, 4, 4)(L)).determinant().sqrt()
e = self.basis()
L = [[x.pair(y) for y in e] for x in e]
return matrix(QQ, 4, 4, L).determinant().sqrt()

def is_maximal(self) -> bool:
r"""
Expand Down Expand Up @@ -3910,7 +3932,18 @@ def normalize_basis_at_p(e, p, B=QuaternionAlgebraElement_abstract.pair):
sage: e = [A(1), k, j, 1/2 + 1/2*i + 1/2*j + 1/2*k]
sage: normalize_basis_at_p(e, 2)
[(1, 0), (1/2 + 1/2*i + 1/2*j + 1/2*k, 0), (-34/105*i - 463/735*j + 71/105*k, 1),
(-34/105*i - 463/735*j + 71/105*k, 1)]
(1/7*i - 8/49*j + 1/7*k, 1)]
TESTS:
We check that the second part of :issue:`37217` is fixed::
sage: A.<i,j,k> = QuaternionAlgebra(-1,-7)
sage: e = [A(1), k, j, 1/2 + 1/2*i + 1/2*j + 1/2*k]
sage: e_norm = normalize_basis_at_p(e, 2)
sage: V = QQ**4
sage: V.span([V(x.coefficient_tuple()) for (x,_) in e_norm]).dimension()
4
"""

N = len(e)
Expand Down Expand Up @@ -3958,8 +3991,7 @@ def normalize_basis_at_p(e, p, B=QuaternionAlgebraElement_abstract.pair):

# Ensures that (B(f0,f0)/2).valuation(p) <= B(f0,f1).valuation(p)
if B(f0, f1).valuation(p) + 1 < B(f0, f0).valuation(p):
f0 += f1
f1 = f0
f0, f1 = f0 + f1, f0

# Make remaining vectors orthogonal to span of f0, f1
e[min_m] = e[0]
Expand All @@ -3972,7 +4004,7 @@ def normalize_basis_at_p(e, p, B=QuaternionAlgebraElement_abstract.pair):
tu = [(B01 * B(f1, e[l]) - B11 * B(f0, e[l]),
B01 * B(f0, e[l]) - B00 * B(f1, e[l])) for l in range(2, N)]

e[2:n] = [e[l] + tu[l-2][0]/d * f0 + tu[l-2][1]/d * f1 for l in range(2, N)]
e[2:N] = [e[l] + tu[l-2][0]/d * f0 + tu[l-2][1]/d * f1 for l in range(2, N)]

# Recursively normalize remaining vectors
f = normalize_basis_at_p(e[2:N], p)
Expand Down

0 comments on commit adb7605

Please sign in to comment.