Skip to content

Commit

Permalink
gh-37164: Fixes and simplifications for .ramified_primes(), `.discr…
Browse files Browse the repository at this point in the history
…iminant()` and `.is_isomorphic` of quaternion algebras

    
1. Removed unnecessary product and factorization for
`.ramified_primes()`
2. Adapted `is_isomorphic` to reduce unnecessary calculations
3. Fixed a bug in `.discriminant()` and `.ramified_primes()` where
rational invariants caused errors
4. Removed `.hilbert_conductor` from `sage.arith.misc` import list and
added `.hilbert_symbol`
5. Reduced restriction of `.ramified_primes()` to rational quaternion
algebras in docstring in preparation for planned `.ramified_places()`
function over number fields (currently being worked on)

In more detail:
1. The original workflow for `.ramified_primes()` went as follows:
    - Call `.discriminant()`, which calls `.hilbert_conductor`
    - Inside `.hilbert_conductor`, the ramified primes are computed and
their product (the discriminant of the quaternion algebra) is returned
    - Finally, factor the discriminant back into its prime factors

    Hence we have a redundant product and, more crucially, a redundant
prime factorization. This fix modifies `.ramified_primes()` to instead
directly build the list computed in `.hilbert_conductor()` (up to a bug
fix described in 3.) and return it; the list might not always be sorted
by magnitude of primes, so an optional argument `sorted` (set to `False`
by default) allows to enforce this (small to large) sorting.
Furthermore, `.discriminant()` has been adapted to directly take the
product of the list returned by `.ramified_primes()` (only in the
rational case, for now - see 5.)

2. Since the `.discriminant()`-function needs to compute all (finite)
ramified primes (this was also true before this PR, it was just hidden
inside `.hilbert_conductor()` instead), the function `.is_isomorphic()`
now compares the unsorted lists of finite ramified primes to decide
whether two rational quaternion algebras are isomorphic.

3. The function `sage.arith.misc.hilbert_conductor` requires its
arguments to be integers (to create certain lists of prime divisors);
since it was originally used to determine the discriminant (and, as
explained in 1., the ramified primes), it raises an error when the
invariants are proper rational numbers. To get around the analogous
error for the method `.hilbert_symbol`, we instead look at the
numerators and denominators of both invariants separately, using the
fact that we can (purely on a mathematical level) rescale both
invariants by the squares of their respective denominators without
leaving the isomorphism class of the algebra.

4. The only call to `sage.arith.misc.hilbert_conductor` in
quaternion_algebra.py was given in the old computation of the
discriminant (the other `.hilbert_conductor` in the code, also in
`.discriminant()`, refers to the one in `sage.rings.number_field`), so
it was removed from the import list. The new approach to
`.ramified_primes()` requires `sage.arith.misc.hilbert_symbol`, which
was added to the import list.

5. As of now the `.ramified_primes()`-method is only supported for
rational quaternion algebras. I'm currently working on a version over
number fields, but once it works correctly this will be implemented as a
new function `.ramified_places` (Update: see #37173) ~~to distinguish
between different formats (prime numbers vs ideals) over $\mathbb{Q}$~~
(Update: this wasn't really feasible, see the issues discussed in #7596;
thanks to @yyyyx4 for pointing me towards this discussion) ~~and,
furthermore,~~ to not cause confusion using the term "primes" for the
Archimedean real places where a quaternion algebra might ramify. Hence
the implementation restriction in the docstring of `.ramified_primes()`
was removed, but the method still throws a ValueError if not called with
a quaternion algebra defined over the rational numbers.

#sd123
    
URL: #37164
Reported by: Sebastian Spindler
Reviewer(s): grhkm21, Sebastian Spindler
  • Loading branch information
Release Manager committed Feb 1, 2024
2 parents ad3f10b + f5fc230 commit f40ab15
Showing 1 changed file with 23 additions and 12 deletions.
35 changes: 23 additions & 12 deletions src/sage/algebras/quatalg/quaternion_algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@
# ****************************************************************************

from sage.arith.misc import (hilbert_conductor_inverse,
hilbert_conductor,
hilbert_symbol,
factor,
gcd,
kronecker as kronecker_symbol,
prime_divisors,
valuation)
from sage.rings.real_mpfr import RR
from sage.rings.integer import Integer
Expand Down Expand Up @@ -1032,9 +1033,8 @@ def inner_product_matrix(self):
@cached_method
def discriminant(self):
"""
Given a quaternion algebra `A` defined over a number field,
return the discriminant of `A`, i.e. the
product of the ramified primes of `A`.
Return the discriminant of this quaternion algebra, i.e. the product of the finite
primes it ramifies at.
EXAMPLES::
Expand All @@ -1059,25 +1059,36 @@ def discriminant(self):
except NotImplementedError:
raise ValueError("base field must be rational numbers or number field")
else:
return hilbert_conductor(self._a, self._b)
return ZZ.prod(self.ramified_primes())

@cached_method
def ramified_primes(self):
"""
Return the primes that ramify in this quaternion algebra.
Return the (finite) primes that ramify in this rational quaternion algebra.
OUTPUT:
Currently only implemented over the rational numbers.
The list of prime numbers at which ``self`` ramifies (given as integers), sorted by their
magnitude (small to large).
EXAMPLES::
sage: QuaternionAlgebra(QQ, -1, -1).ramified_primes()
[2]
sage: QuaternionAlgebra(QQ, -58, -69).ramified_primes()
[3, 23, 29]
"""
# TODO: more examples
return [f[0] for f in factor(self.discriminant())]
if not is_RationalField(self.base_ring()):
raise ValueError("base field must be the rational numbers")

return sorted([p for p in set([2]).union(prime_divisors(self._a.numerator()),
prime_divisors(self._a.denominator()), prime_divisors(self._b.numerator()),
prime_divisors(self._b.denominator())) if hilbert_symbol(self._a, self._b, p) == -1])

def is_isomorphic(self, A) -> bool:
r"""
Return ``True`` if ``self`` and ``A`` are isomorphic quaternion algebras over Q.
"""
Return ``True`` if (and only if) ``self`` and ``A`` are isomorphic quaternion algebras over Q.
INPUT:
Expand All @@ -1098,7 +1109,7 @@ def is_isomorphic(self, A) -> bool:
if self.base_ring() != QQ or A.base_ring() != QQ:
raise NotImplementedError("isomorphism check only implemented for rational quaternion algebras")

return self.discriminant() == A.discriminant()
return self.ramified_primes() == A.ramified_primes()

def _magma_init_(self, magma):
"""
Expand Down

0 comments on commit f40ab15

Please sign in to comment.