Skip to content

Commit

Permalink
gh-35762: ideals of non-maximal orders in number fields
Browse files Browse the repository at this point in the history
    
Resolves #34198.
    
URL: #35762
Reported by: Lorenz Panny
Reviewer(s): Peter Bruin
  • Loading branch information
Release Manager committed Jan 20, 2024
2 parents 439065e + 68b4462 commit a9558bb
Show file tree
Hide file tree
Showing 11 changed files with 878 additions and 75 deletions.
1 change: 1 addition & 0 deletions src/doc/en/reference/number_fields/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Orders, Ideals and Ideal Classes
sage/rings/number_field/order
sage/rings/number_field/number_field_ideal
sage/rings/number_field/number_field_ideal_rel
sage/rings/number_field/order_ideal
sage/rings/number_field/class_group
sage/rings/number_field/unit_group
sage/rings/number_field/S_unit_solver
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,12 @@ with ideals in non-maximal orders.
sage: K.<a> = NumberField(x^3 + 2)
sage: R = K.order(3*a)
sage: R.ideal(5)
doctest:warning ... FutureWarning: ...
Ideal (5, 15*a, 45*a^2) of Order generated by 3*a in Number Field in a with defining polynomial x^3 + 2
sage: R.ideal(5).factor()
Traceback (most recent call last):
...
NotImplementedError: ideals of non-maximal orders not
yet supported.
AttributeError: 'NumberFieldOrderIdeal_generic' object has no attribute 'factor'


Relative Extensions
Expand Down
2 changes: 1 addition & 1 deletion src/sage/categories/pushout.py
Original file line number Diff line number Diff line change
Expand Up @@ -2990,7 +2990,7 @@ def _apply_functor(self, R):
R = I.ring()
else:
R = pushout(R, I.ring().base_ring())
I = [R.one() * t for t in I.gens()] * R
I = R.ideal([R.one() * t for t in I.gens()], warn=False)
try:
Q = R.quo(I, names=self.names, **self.kwds)
except IndexError: # That may happen!
Expand Down
18 changes: 6 additions & 12 deletions src/sage/rings/ideal.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ class Ideal_generic(MonoidElement):
See :func:`Ideal()`.
"""
def __init__(self, ring, gens, coerce=True):
def __init__(self, ring, gens, coerce=True, **kwds):
"""
Initialize this ideal.
Expand Down Expand Up @@ -1461,19 +1461,13 @@ class Ideal_pid(Ideal_principal):
An ideal of a principal ideal domain.
See :func:`Ideal()`.
"""
def __init__(self, ring, gen):
"""
Initialize ``self``.
EXAMPLES::
sage: I = 8*ZZ
sage: I
Principal ideal (8) of Integer Ring
"""
Ideal_principal.__init__(self, ring, gen)
EXAMPLES::
sage: I = 8*ZZ
sage: I
Principal ideal (8) of Integer Ring
"""
def __add__(self, other):
"""
Add the two ideals.
Expand Down
16 changes: 7 additions & 9 deletions src/sage/rings/number_field/class_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,11 @@
sage: I * I.ideal() # ideal classes coerce to their representative ideal
Fractional ideal (4, 1/2*a + 3/2)
sage: O = K.OK(); O
Maximal Order generated by 1/2*a + 1/2 in Number Field in a with defining polynomial x^2 + 23
sage: O*(2, 1/2*a + 1/2)
sage: K.fractional_ideal([2, 1/2*a + 1/2])
Fractional ideal (2, 1/2*a + 1/2)
sage: (O*(2, 1/2*a + 1/2)).is_principal()
sage: K.fractional_ideal([2, 1/2*a + 1/2]).is_principal()
False
sage: (O*(2, 1/2*a + 1/2))^3
sage: K.fractional_ideal([2, 1/2*a + 1/2])^3
Fractional ideal (1/2*a - 3/2)
"""

Expand Down Expand Up @@ -66,7 +64,7 @@ class FractionalIdealClass(AbelianGroupWithValuesElement):
sage: K.<w> = QuadraticField(-23)
sage: OK = K.ring_of_integers()
sage: C = OK.class_group()
sage: P2a, P2b = [P for P, e in (2*OK).factor()]
sage: P2a, P2b = [P for P, e in (2*K).factor()]
sage: c = C(P2a); c
Fractional ideal class (2, 1/2*w - 1/2)
sage: c.gens()
Expand Down Expand Up @@ -208,7 +206,7 @@ def is_principal(self):
sage: K.<w> = QuadraticField(-23)
sage: OK = K.ring_of_integers()
sage: C = OK.class_group()
sage: P2a, P2b = [P for P, e in (2*OK).factor()]
sage: P2a, P2b = [P for P, e in (2*K).factor()]
sage: c = C(P2a)
sage: c.is_principal()
False
Expand Down Expand Up @@ -250,7 +248,7 @@ def ideal(self):
sage: K.<w> = QuadraticField(-23)
sage: OK = K.ring_of_integers()
sage: C = OK.class_group()
sage: P2a, P2b = [P for P, e in (2*OK).factor()]
sage: P2a, P2b = [P for P, e in (2*K).factor()]
sage: c = C(P2a); c
Fractional ideal class (2, 1/2*w - 1/2)
sage: c.ideal()
Expand Down Expand Up @@ -315,7 +313,7 @@ def gens(self):
sage: K.<w> = QuadraticField(-23)
sage: OK = K.ring_of_integers()
sage: C = OK.class_group()
sage: P2a, P2b = [P for P, e in (2*OK).factor()]
sage: P2a, P2b = [P for P, e in (2*K).factor()]
sage: c = C(P2a); c
Fractional ideal class (2, 1/2*w - 1/2)
sage: c.gens()
Expand Down
13 changes: 5 additions & 8 deletions src/sage/rings/number_field/number_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -10163,11 +10163,11 @@ def hilbert_symbol(self, a, b, P=None):
More local examples::
sage: K.hilbert_symbol(a, 0, K.ideal(5))
sage: K.hilbert_symbol(a, 0, K.fractional_ideal(5))
0
sage: K.hilbert_symbol(a, a + 5, K.ideal(5))
sage: K.hilbert_symbol(a, a + 5, K.fractional_ideal(5))
1
sage: K.hilbert_symbol(a + 1, 13, (a+6)*K.maximal_order())
sage: K.hilbert_symbol(a + 1, 13, (a+6)*K)
-1
sage: [emb1, emb2] = K.embeddings(AA)
sage: K.hilbert_symbol(a, -1, emb1)
Expand Down Expand Up @@ -10199,8 +10199,7 @@ def hilbert_symbol(self, a, b, P=None):
Primes above 2::
sage: K.<a> = NumberField(x^5 - 23)
sage: O = K.maximal_order()
sage: p = [p[0] for p in (2*O).factor() if p[0].norm() == 16][0]
sage: p = [p[0] for p in (2*K).factor() if p[0].norm() == 16][0]
sage: K.hilbert_symbol(a, a + 5, p)
1
sage: K.hilbert_symbol(a, 2, p)
Expand Down Expand Up @@ -10248,8 +10247,7 @@ def hilbert_symbol(self, a, b, P=None):
`a` and `b` do not have to be integral or coprime::
sage: K.<i> = QuadraticField(-1)
sage: O = K.maximal_order()
sage: K.hilbert_symbol(1/2, 1/6, 3*O)
sage: K.hilbert_symbol(1/2, 1/6, 3*K)
1
sage: p = 1 + i
sage: K.hilbert_symbol(p, p, p)
Expand Down Expand Up @@ -12762,7 +12760,6 @@ def _splitting_classes_gens_(K, m, d):
"""
from sage.groups.abelian_gps.abelian_group import AbelianGroup

R = K.ring_of_integers()
Zm = IntegerModRing(m)
unit_gens = Zm.unit_gens()
Zmstar = AbelianGroup(len(unit_gens), [x.multiplicative_order() for x in unit_gens])
Expand Down
21 changes: 8 additions & 13 deletions src/sage/rings/number_field/number_field_element.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,13 @@ def _inverse_mod_generic(elt, I):
sage: x = polygen(ZZ, 'x')
sage: OE.<w> = EquationOrder(x^3 - x + 2)
sage: from sage.rings.number_field.number_field_element import _inverse_mod_generic
sage: _inverse_mod_generic(w, 13*OE)
sage: _inverse_mod_generic(w, 13)
6*w^2 - 6
"""
from sage.matrix.constructor import matrix
R = elt.parent()
if not R.is_maximal():
raise NotImplementedError('not implemented for non-maximal orders')
I = R.number_field().fractional_ideal(I)
if not I.is_integral():
raise ValueError("inverse is only defined modulo integral ideals")
Expand Down Expand Up @@ -2053,16 +2055,9 @@ cdef class NumberFieldElement(NumberFieldElement_base):
sage: R = K.maximal_order()
sage: R(i+1).gcd(2)
i + 1
Non-maximal orders are not supported::
sage: R = K.order(2*i)
sage: R(1).gcd(R(4*i))
Traceback (most recent call last):
...
NotImplementedError: gcd() for Order of conductor 2 generated by 2*i
in Number Field in i with defining polynomial x^2 + 1 with i = 1*I
is not implemented
1
The following field has class number 3, but if the ideal
``(self, other)`` happens to be principal, this still works::
Expand Down Expand Up @@ -2094,7 +2089,7 @@ cdef class NumberFieldElement(NumberFieldElement_base):
return R.one()

from sage.rings.number_field.order import is_NumberFieldOrder
if not is_NumberFieldOrder(R) or not R.is_maximal():
if not is_NumberFieldOrder(R):
raise NotImplementedError("gcd() for %r is not implemented" % R)

K = R.number_field()
Expand Down Expand Up @@ -5304,13 +5299,13 @@ cdef class OrderElement_absolute(NumberFieldElement_absolute):
sage: x = polygen(ZZ, 'x')
sage: OE.<w> = EquationOrder(x^3 - x + 2)
sage: w.inverse_mod(13*OE)
sage: w.inverse_mod(13)
6*w^2 - 6
sage: w * (w.inverse_mod(13)) - 1 in 13*OE
sage: w * (w.inverse_mod(13)) - 1 in 13*OE.number_field()
True
sage: w.inverse_mod(13).parent() == OE
True
sage: w.inverse_mod(2*OE)
sage: w.inverse_mod(2)
Traceback (most recent call last):
...
ZeroDivisionError: w is not invertible modulo Fractional ideal (2)
Expand Down
7 changes: 3 additions & 4 deletions src/sage/rings/number_field/number_field_ideal.py
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,7 @@ def gens_reduced(self, proof=None):
def gens_two(self):
r"""
Express this ideal using exactly two generators, the first of
which is a generator for the intersection of the ideal with `Q`.
which is a generator for the intersection of the ideal with `\QQ`.
ALGORITHM: uses PARI's :pari:`idealtwoelt` function, which runs in
randomized polynomial time and is very fast in practice.
Expand Down Expand Up @@ -1538,7 +1538,7 @@ def inertia_group(self):

def random_element(self, *args, **kwds):
r"""
Return a random element of this order.
Return a random element of this ideal.
INPUT:
Expand Down Expand Up @@ -2498,8 +2498,7 @@ def is_coprime(self, other):
See :trac:`4536`::
sage: E.<a> = NumberField(x^5 + 7*x^4 + 18*x^2 + x - 3)
sage: OE = E.ring_of_integers()
sage: i,j,k = [u[0] for u in factor(3*OE)]
sage: i,j,k = [u[0] for u in factor(3*E)]
sage: (i/j).is_coprime(j/k)
False
sage: (j/k).is_coprime(j/k)
Expand Down
57 changes: 33 additions & 24 deletions src/sage/rings/number_field/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
We compute a basis for an order in a relative extension
that is generated by 2 elements::
sage: K.<a,b> = NumberField([x^2 + 1, x^2 - 3]); O = K.order([3*a, 2*b])
sage: K.<a,b> = NumberField([x^2 + 1, x^2 - 3])
sage: O = K.order([3*a, 2*b])
sage: O.basis()
[1, 3*a - 2*b, -6*b*a + 6, 3*a]
Expand Down Expand Up @@ -516,32 +517,41 @@ def ideal(self, *args, **kwds):
Fractional ideal (7)
sage: R = K.order(4*a)
sage: R.ideal(8)
Traceback (most recent call last):
...
NotImplementedError: ideals of non-maximal orders not yet supported.
doctest:warning ... FutureWarning: ...
Ideal (8, 32*a) of Order of conductor 8 generated by 4*a
in Number Field in a with defining polynomial x^2 + 7
This function is called implicitly below::
sage: R = EquationOrder(x^2 + 2, 'a'); R
Maximal Order generated by a in Number Field in a with defining polynomial x^2 + 2
sage: (3,15)*R
doctest:warning ... DeprecationWarning: ...
Fractional ideal (3)
The zero ideal is handled properly::
sage: R.ideal(0)
Ideal (0) of Number Field in a with defining polynomial x^2 + 2
"""
if not self.is_maximal():
raise NotImplementedError("ideals of non-maximal orders not yet supported.")
from sage.misc.superseded import deprecation
deprecation(34806, 'In the future, constructing an ideal of the ring of '
'integers of a number field will use an implementation '
'compatible with ideals of other (non-maximal) orders, '
'rather than returning an integral fractional ideal of '
'its containing number field. Use .fractional_ideal(), '
'together with an .is_integral() check if desired, to '
'avoid your code breaking with future changes to Sage.')
if kwds.get('future', False) or not self.is_maximal():
if 'future' in kwds:
del kwds['future']
from sage.rings.number_field.order_ideal import NumberFieldOrderIdeal
return NumberFieldOrderIdeal(self, *args, **kwds)
if kwds.get('warn', True):
if 'warn' in kwds:
del kwds['warn']
from sage.misc.superseded import deprecation
deprecation(34198, 'In the future, constructing an ideal of the ring of '
'integers of a number field will use an implementation '
'compatible with ideals of other (non-maximal) orders, '
'rather than returning an integral fractional ideal of '
'its containing number field. Use .fractional_ideal(), '
'together with an .is_integral() check if desired, to '
'emulate the current behavior.\nSet warn=0 to silence '
'this warning, and future=1 to activate the upcoming '
'behavior already.')
I = self.number_field().ideal(*args, **kwds)
if not I.is_integral():
raise ValueError("ideal must be integral; use fractional_ideal to create a non-integral ideal.")
Expand All @@ -565,7 +575,7 @@ def _coerce_map_from_(self, R):

def __mul__(self, right):
"""
Create an ideal in this order using the notation ``Ok*gens``
Create an ideal in this order using the syntax ``O * gens``
EXAMPLES::
Expand All @@ -577,17 +587,16 @@ def __mul__(self, right):
sage: Ok = k.maximal_order(); Ok
Maximal Order generated by a in Number Field in a with defining polynomial x^2 + 5077
sage: Ok * (11, a + 7)
doctest:warning ... DeprecationWarning: ...
Fractional ideal (11, a + 7)
sage: (11, a + 7) * Ok
Fractional ideal (11, a + 7)
"""
if self.is_maximal():
return self._K.ideal(right)
raise TypeError
return self.ideal(right)

def __rmul__(self, left):
"""
Create an ideal in this order using the notation ``gens*Ok``.
Create an ideal in this order using the syntax ``gens * O``.
EXAMPLES::
Expand All @@ -603,7 +612,7 @@ def __rmul__(self, left):
sage: 17*Ok
Fractional ideal (17)
"""
return self * left
return self.ideal(left)

def is_field(self, proof=True):
r"""
Expand Down Expand Up @@ -752,7 +761,7 @@ def basis(self): # this must be defined in derived class
sage: O.basis()
[1, 1/4*a^2 + 1/4*a, a^2]
"""
raise NotImplementedError
raise NotImplementedError('child classes must implement')

def coordinates(self, x):
r"""
Expand Down Expand Up @@ -1026,8 +1035,8 @@ def residue_field(self, prime, names=None, check=False):
if self.is_maximal():
return self.number_field().residue_field(prime, names, check=check)

raise NotImplementedError("Residue fields of non-maximal orders "
"are not yet supported.")
raise NotImplementedError("residue fields of non-maximal orders "
"are not yet supported")

def fraction_field(self):
"""
Expand Down Expand Up @@ -1141,7 +1150,7 @@ def class_group(self, proof=None, names='c'):
if self.is_maximal():
return self.number_field().class_group(proof=proof, names=names)
else:
raise NotImplementedError
raise NotImplementedError('non-maximal orders are not yet supported')

def is_suborder(self, other):
"""
Expand Down
Loading

0 comments on commit a9558bb

Please sign in to comment.