Complex multiplication for elliptic curves

This module implements the functions

  • hilbert_class_polynomial

  • is_HCP

  • cm_j_invariants

  • cm_orders

  • discriminants_with_bounded_class_number

  • cm_j_invariants_and_orders

  • largest_fundamental_disc_with_class_number

  • is_cm_j_invariant

AUTHORS:

  • Robert Bradshaw

  • John Cremona

  • William Stein

sage.schemes.elliptic_curves.cm.OrderClassNumber(D0, h0, f)[source]

Return the class number h(f**2 * D0), given h(D0)=h0.

INPUT:

  • D0 – integer; a negative fundamental discriminant

  • h0 – integer; the class number of the (maximal) imaginary quadratic order of discriminant D0

  • f – positive integer

OUTPUT:

(integer) the class number of the imaginary quadratic order of discriminant D0*f**2

ALGORITHM:

We use the formula for the class number of the order \(\mathcal{O}_{D}\) in terms of the class number of the

maximal order \(\mathcal{O}_{D_0}\); see [Cox1989] Theorem 7.24:

\[h(D) = \frac{h(D_0)f}{[\mathcal{O}_{D_0}^\times:\mathcal{O}_{D}^\times]}\prod_{p\,|\,f}\left(1-\left(\frac{D_0}{p}\right)\frac{1}{p}\right)\]

EXAMPLES:

sage: # needs sage.libs.pari
sage: from sage.schemes.elliptic_curves.cm import OrderClassNumber
sage: D0 = -4
sage: h = D0.class_number()
sage: [OrderClassNumber(D0,h,f) for f in srange(1,20)]
[1, 1, 2, 2, 2, 4, 4, 4, 6, 4, 6, 8, 6, 8, 8, 8, 8, 12, 10]
sage: all([OrderClassNumber(D0,h,f) == (D0*f**2).class_number() for f in srange(1,20)])
True
>>> from sage.all import *
>>> # needs sage.libs.pari
>>> from sage.schemes.elliptic_curves.cm import OrderClassNumber
>>> D0 = -Integer(4)
>>> h = D0.class_number()
>>> [OrderClassNumber(D0,h,f) for f in srange(Integer(1),Integer(20))]
[1, 1, 2, 2, 2, 4, 4, 4, 6, 4, 6, 8, 6, 8, 8, 8, 8, 12, 10]
>>> all([OrderClassNumber(D0,h,f) == (D0*f**Integer(2)).class_number() for f in srange(Integer(1),Integer(20))])
True
# needs sage.libs.pari
from sage.schemes.elliptic_curves.cm import OrderClassNumber
D0 = -4
h = D0.class_number()
[OrderClassNumber(D0,h,f) for f in srange(1,20)]
all([OrderClassNumber(D0,h,f) == (D0*f**2).class_number() for f in srange(1,20)])
sage.schemes.elliptic_curves.cm.cm_j_invariants(proof=None)[source]

Return a list of all CM \(j\)-invariants in the field \(K\).

INPUT:

  • K – a number field

  • proof – (default: proof.number_field())

OUTPUT:

(list) – A list of CM \(j\)-invariants in the field \(K\).

EXAMPLES:

sage: cm_j_invariants(QQ)
[-262537412640768000, -147197952000, -884736000, -12288000, -884736,
 -32768, -3375, 0, 1728, 8000, 54000, 287496, 16581375]
>>> from sage.all import *
>>> cm_j_invariants(QQ)
[-262537412640768000, -147197952000, -884736000, -12288000, -884736,
 -32768, -3375, 0, 1728, 8000, 54000, 287496, 16581375]
cm_j_invariants(QQ)

Over imaginary quadratic fields there are no more than over \(QQ\):

sage: cm_j_invariants(QuadraticField(-1, 'i'))                                  # needs sage.rings.number_field
[-262537412640768000, -147197952000, -884736000, -12288000, -884736,
 -32768, -3375, 0, 1728, 8000, 54000, 287496, 16581375]
>>> from sage.all import *
>>> cm_j_invariants(QuadraticField(-Integer(1), 'i'))                                  # needs sage.rings.number_field
[-262537412640768000, -147197952000, -884736000, -12288000, -884736,
 -32768, -3375, 0, 1728, 8000, 54000, 287496, 16581375]
cm_j_invariants(QuadraticField(-1, 'i'))                                  # needs sage.rings.number_field

Over real quadratic fields there may be more, for example:

sage: len(cm_j_invariants(QuadraticField(5, 'a')))                              # needs sage.rings.number_field
31
>>> from sage.all import *
>>> len(cm_j_invariants(QuadraticField(Integer(5), 'a')))                              # needs sage.rings.number_field
31
len(cm_j_invariants(QuadraticField(5, 'a')))                              # needs sage.rings.number_field

Over number fields K of many higher degrees this also works:

sage: # needs sage.rings.number_field
sage: x = polygen(ZZ, 'x')
sage: K.<a> = NumberField(x^3 - 2)
sage: cm_j_invariants(K)
[-262537412640768000, -147197952000, -884736000, -884736, -32768,
 8000, -3375, 16581375, 1728, 287496, 0, 54000, -12288000,
 31710790944000*a^2 + 39953093016000*a + 50337742902000]
sage: K.<a> = NumberField(x^4 - 2)
sage: len(cm_j_invariants(K))
23
>>> from sage.all import *
>>> # needs sage.rings.number_field
>>> x = polygen(ZZ, 'x')
>>> K = NumberField(x**Integer(3) - Integer(2), names=('a',)); (a,) = K._first_ngens(1)
>>> cm_j_invariants(K)
[-262537412640768000, -147197952000, -884736000, -884736, -32768,
 8000, -3375, 16581375, 1728, 287496, 0, 54000, -12288000,
 31710790944000*a^2 + 39953093016000*a + 50337742902000]
>>> K = NumberField(x**Integer(4) - Integer(2), names=('a',)); (a,) = K._first_ngens(1)
>>> len(cm_j_invariants(K))
23
# needs sage.rings.number_field
x = polygen(ZZ, 'x')
K.<a> = NumberField(x^3 - 2)
cm_j_invariants(K)
K.<a> = NumberField(x^4 - 2)
len(cm_j_invariants(K))
sage.schemes.elliptic_curves.cm.cm_j_invariants_and_orders(proof=None)[source]

Return a list of all CM \(j\)-invariants in the field \(K\), together with the associated orders.

INPUT:

  • K – a number field

  • proof – (default: proof.number_field())

OUTPUT:

A list of 3-tuples \((D,f,j)\) where \(j\) is a CM \(j\)-invariant in \(K\) with quadratic fundamental discriminant \(D\) and conductor \(f\).

EXAMPLES:

sage: cm_j_invariants_and_orders(QQ)
[(-3, 3, -12288000), (-3, 2, 54000), (-3, 1, 0), (-4, 2, 287496), (-4, 1, 1728),
 (-7, 2, 16581375), (-7, 1, -3375), (-8, 1, 8000), (-11, 1, -32768),
 (-19, 1, -884736), (-43, 1, -884736000), (-67, 1, -147197952000),
 (-163, 1, -262537412640768000)]
>>> from sage.all import *
>>> cm_j_invariants_and_orders(QQ)
[(-3, 3, -12288000), (-3, 2, 54000), (-3, 1, 0), (-4, 2, 287496), (-4, 1, 1728),
 (-7, 2, 16581375), (-7, 1, -3375), (-8, 1, 8000), (-11, 1, -32768),
 (-19, 1, -884736), (-43, 1, -884736000), (-67, 1, -147197952000),
 (-163, 1, -262537412640768000)]
cm_j_invariants_and_orders(QQ)

Over an imaginary quadratic field there are no more than over \(QQ\):

sage: cm_j_invariants_and_orders(QuadraticField(-1, 'i'))                       # needs sage.rings.number_field
[(-163, 1, -262537412640768000), (-67, 1, -147197952000),
 (-43, 1, -884736000), (-19, 1, -884736), (-11, 1, -32768),
 (-8, 1, 8000), (-7, 1, -3375), (-7, 2, 16581375), (-4, 1, 1728),
 (-4, 2, 287496), (-3, 1, 0), (-3, 2, 54000), (-3, 3, -12288000)]
>>> from sage.all import *
>>> cm_j_invariants_and_orders(QuadraticField(-Integer(1), 'i'))                       # needs sage.rings.number_field
[(-163, 1, -262537412640768000), (-67, 1, -147197952000),
 (-43, 1, -884736000), (-19, 1, -884736), (-11, 1, -32768),
 (-8, 1, 8000), (-7, 1, -3375), (-7, 2, 16581375), (-4, 1, 1728),
 (-4, 2, 287496), (-3, 1, 0), (-3, 2, 54000), (-3, 3, -12288000)]
cm_j_invariants_and_orders(QuadraticField(-1, 'i'))                       # needs sage.rings.number_field

Over real quadratic fields there may be more:

sage: v = cm_j_invariants_and_orders(QuadraticField(5,'a')); len(v)             # needs sage.rings.number_field
31
sage: [(D, f) for D, f, j in v if j not in QQ]                                  # needs sage.rings.number_field
[(-235, 1), (-235, 1), (-115, 1), (-115, 1), (-40, 1), (-40, 1),
 (-35, 1), (-35, 1), (-20, 1), (-20, 1), (-15, 1), (-15, 1), (-15, 2),
 (-15, 2), (-4, 5), (-4, 5), (-3, 5), (-3, 5)]
>>> from sage.all import *
>>> v = cm_j_invariants_and_orders(QuadraticField(Integer(5),'a')); len(v)             # needs sage.rings.number_field
31
>>> [(D, f) for D, f, j in v if j not in QQ]                                  # needs sage.rings.number_field
[(-235, 1), (-235, 1), (-115, 1), (-115, 1), (-40, 1), (-40, 1),
 (-35, 1), (-35, 1), (-20, 1), (-20, 1), (-15, 1), (-15, 1), (-15, 2),
 (-15, 2), (-4, 5), (-4, 5), (-3, 5), (-3, 5)]
v = cm_j_invariants_and_orders(QuadraticField(5,'a')); len(v)             # needs sage.rings.number_field
[(D, f) for D, f, j in v if j not in QQ]                                  # needs sage.rings.number_field

Over number fields K of many higher degrees this also works:

sage: x = polygen(ZZ, 'x')
sage: K.<a> = NumberField(x^3 - 2)                                              # needs sage.rings.number_field
sage: cm_j_invariants_and_orders(K)                                             # needs sage.rings.number_field
[(-163, 1, -262537412640768000), (-67, 1, -147197952000),
 (-43, 1, -884736000), (-19, 1, -884736), (-11, 1, -32768),
 (-8, 1, 8000), (-7, 1, -3375), (-7, 2, 16581375), (-4, 1, 1728),
 (-4, 2, 287496), (-3, 1, 0), (-3, 2, 54000), (-3, 3, -12288000),
 (-3, 6, 31710790944000*a^2 + 39953093016000*a + 50337742902000)]
>>> from sage.all import *
>>> x = polygen(ZZ, 'x')
>>> K = NumberField(x**Integer(3) - Integer(2), names=('a',)); (a,) = K._first_ngens(1)# needs sage.rings.number_field
>>> cm_j_invariants_and_orders(K)                                             # needs sage.rings.number_field
[(-163, 1, -262537412640768000), (-67, 1, -147197952000),
 (-43, 1, -884736000), (-19, 1, -884736), (-11, 1, -32768),
 (-8, 1, 8000), (-7, 1, -3375), (-7, 2, 16581375), (-4, 1, 1728),
 (-4, 2, 287496), (-3, 1, 0), (-3, 2, 54000), (-3, 3, -12288000),
 (-3, 6, 31710790944000*a^2 + 39953093016000*a + 50337742902000)]
x = polygen(ZZ, 'x')
K.<a> = NumberField(x^3 - 2)                                              # needs sage.rings.number_field
cm_j_invariants_and_orders(K)                                             # needs sage.rings.number_field
sage.schemes.elliptic_curves.cm.cm_orders(proof=None)[source]

Return a list of all pairs \((D,f)\) where there is a CM order of discriminant \(D f^2\) with class number h, with \(D\) a fundamental discriminant.

INPUT:

  • h – positive integer

  • proof – (default: proof.number_field())

OUTPUT: list of 2-tuples \((D,f)\) sorted lexicographically by \((|D|, f)\)

EXAMPLES:

sage: cm_orders(0)
[]
sage: v = cm_orders(1); v
[(-3, 1), (-3, 2), (-3, 3), (-4, 1), (-4, 2), (-7, 1), (-7, 2), (-8, 1),
 (-11, 1), (-19, 1), (-43, 1), (-67, 1), (-163, 1)]
sage: type(v[0][0]), type(v[0][1])
(<... 'sage.rings.integer.Integer'>, <... 'sage.rings.integer.Integer'>)
sage: # needs sage.libs.pari
sage: v = cm_orders(2); v
 [(-3, 4), (-3, 5), (-3, 7), (-4, 3), (-4, 4), (-4, 5), (-7, 4), (-8, 2),
  (-8, 3), (-11, 3), (-15, 1), (-15, 2), (-20, 1), (-24, 1), (-35, 1),
  (-40, 1), (-51, 1), (-52, 1), (-88, 1), (-91, 1), (-115, 1), (-123, 1),
  (-148, 1), (-187, 1), (-232, 1), (-235, 1), (-267, 1), (-403, 1), (-427, 1)]
sage: len(v)
29
sage: set([hilbert_class_polynomial(D*f^2).degree() for D,f in v])
{2}
>>> from sage.all import *
>>> cm_orders(Integer(0))
[]
>>> v = cm_orders(Integer(1)); v
[(-3, 1), (-3, 2), (-3, 3), (-4, 1), (-4, 2), (-7, 1), (-7, 2), (-8, 1),
 (-11, 1), (-19, 1), (-43, 1), (-67, 1), (-163, 1)]
>>> type(v[Integer(0)][Integer(0)]), type(v[Integer(0)][Integer(1)])
(<... 'sage.rings.integer.Integer'>, <... 'sage.rings.integer.Integer'>)
>>> # needs sage.libs.pari
>>> v = cm_orders(Integer(2)); v
 [(-3, 4), (-3, 5), (-3, 7), (-4, 3), (-4, 4), (-4, 5), (-7, 4), (-8, 2),
  (-8, 3), (-11, 3), (-15, 1), (-15, 2), (-20, 1), (-24, 1), (-35, 1),
  (-40, 1), (-51, 1), (-52, 1), (-88, 1), (-91, 1), (-115, 1), (-123, 1),
  (-148, 1), (-187, 1), (-232, 1), (-235, 1), (-267, 1), (-403, 1), (-427, 1)]
>>> len(v)
29
>>> set([hilbert_class_polynomial(D*f**Integer(2)).degree() for D,f in v])
{2}
cm_orders(0)
v = cm_orders(1); v
type(v[0][0]), type(v[0][1])
# needs sage.libs.pari
v = cm_orders(2); v
len(v)
set([hilbert_class_polynomial(D*f^2).degree() for D,f in v])

Any degree up to 100 is implemented, but may be slow:

sage: # needs sage.libs.pari
sage: cm_orders(3)
[(-3, 6), (-3, 9), (-11, 2), (-19, 2), (-23, 1), (-23, 2), (-31, 1), (-31, 2),
 (-43, 2), (-59, 1), (-67, 2), (-83, 1), (-107, 1), (-139, 1), (-163, 2),
 (-211, 1), (-283, 1), (-307, 1), (-331, 1), (-379, 1), (-499, 1), (-547, 1),
 (-643, 1), (-883, 1), (-907, 1)]
sage: len(cm_orders(4))
84
>>> from sage.all import *
>>> # needs sage.libs.pari
>>> cm_orders(Integer(3))
[(-3, 6), (-3, 9), (-11, 2), (-19, 2), (-23, 1), (-23, 2), (-31, 1), (-31, 2),
 (-43, 2), (-59, 1), (-67, 2), (-83, 1), (-107, 1), (-139, 1), (-163, 2),
 (-211, 1), (-283, 1), (-307, 1), (-331, 1), (-379, 1), (-499, 1), (-547, 1),
 (-643, 1), (-883, 1), (-907, 1)]
>>> len(cm_orders(Integer(4)))
84
# needs sage.libs.pari
cm_orders(3)
len(cm_orders(4))
sage.schemes.elliptic_curves.cm.discriminants_with_bounded_class_number(hmax, B=None, proof=None)[source]

Return a dictionary with keys class numbers \(h\le hmax\) and values the list of all pairs \((D_0, f)\), with \(D_0<0\) a fundamental discriminant such that \(D=D_0f^2\) has class number \(h\). If the optional bound \(B\) is given, return only those pairs with \(|D| \le B\).

INPUT:

  • hmax – integer

  • B – integer or None; if None returns all pairs

  • proof – this code calls the PARI function pari:qfbclassno, so it could give wrong answers when proof``==``False (though only for discriminants greater than \(2\cdot10^{10}\)). The default is the current value of proof.number_field().

OUTPUT: dictionary

Note

In case \(B\) is not given, then hmax must be at most 100; we use the tables from [Watkins2004] and [Klaise2012] to compute a \(B\) that captures all \(h\) up to \(hmax\).

EXAMPLES:

sage: # needs sage.libs.pari
sage: from sage.schemes.elliptic_curves.cm import discriminants_with_bounded_class_number
sage: v = discriminants_with_bounded_class_number(3)
sage: sorted(v)
[1, 2, 3]
sage: v[1]
[(-3, 1), (-3, 2), (-3, 3), (-4, 1), (-4, 2), (-7, 1), (-7, 2), (-8, 1),
 (-11, 1), (-19, 1), (-43, 1), (-67, 1), (-163, 1)]
sage: v[2]
[(-3, 4), (-3, 5), (-3, 7), (-4, 3), (-4, 4), (-4, 5), (-7, 4), (-8, 2),
 (-8, 3), (-11, 3), (-15, 1), (-15, 2), (-20, 1), (-24, 1), (-35, 1), (-40, 1),
 (-51, 1), (-52, 1), (-88, 1), (-91, 1), (-115, 1), (-123, 1), (-148, 1),
 (-187, 1), (-232, 1), (-235, 1), (-267, 1), (-403, 1), (-427, 1)]
sage: v[3]
[(-3, 6), (-3, 9), (-11, 2), (-19, 2), (-23, 1), (-23, 2), (-31, 1), (-31, 2),
 (-43, 2), (-59, 1), (-67, 2), (-83, 1), (-107, 1), (-139, 1), (-163, 2),
 (-211, 1), (-283, 1), (-307, 1), (-331, 1), (-379, 1), (-499, 1), (-547, 1),
 (-643, 1), (-883, 1), (-907, 1)]
sage: v = discriminants_with_bounded_class_number(8, proof=False)
sage: sorted(len(v[h]) for h in v)
[13, 25, 29, 29, 38, 84, 101, 208]
>>> from sage.all import *
>>> # needs sage.libs.pari
>>> from sage.schemes.elliptic_curves.cm import discriminants_with_bounded_class_number
>>> v = discriminants_with_bounded_class_number(Integer(3))
>>> sorted(v)
[1, 2, 3]
>>> v[Integer(1)]
[(-3, 1), (-3, 2), (-3, 3), (-4, 1), (-4, 2), (-7, 1), (-7, 2), (-8, 1),
 (-11, 1), (-19, 1), (-43, 1), (-67, 1), (-163, 1)]
>>> v[Integer(2)]
[(-3, 4), (-3, 5), (-3, 7), (-4, 3), (-4, 4), (-4, 5), (-7, 4), (-8, 2),
 (-8, 3), (-11, 3), (-15, 1), (-15, 2), (-20, 1), (-24, 1), (-35, 1), (-40, 1),
 (-51, 1), (-52, 1), (-88, 1), (-91, 1), (-115, 1), (-123, 1), (-148, 1),
 (-187, 1), (-232, 1), (-235, 1), (-267, 1), (-403, 1), (-427, 1)]
>>> v[Integer(3)]
[(-3, 6), (-3, 9), (-11, 2), (-19, 2), (-23, 1), (-23, 2), (-31, 1), (-31, 2),
 (-43, 2), (-59, 1), (-67, 2), (-83, 1), (-107, 1), (-139, 1), (-163, 2),
 (-211, 1), (-283, 1), (-307, 1), (-331, 1), (-379, 1), (-499, 1), (-547, 1),
 (-643, 1), (-883, 1), (-907, 1)]
>>> v = discriminants_with_bounded_class_number(Integer(8), proof=False)
>>> sorted(len(v[h]) for h in v)
[13, 25, 29, 29, 38, 84, 101, 208]
# needs sage.libs.pari
from sage.schemes.elliptic_curves.cm import discriminants_with_bounded_class_number
v = discriminants_with_bounded_class_number(3)
sorted(v)
v[1]
v[2]
v[3]
v = discriminants_with_bounded_class_number(8, proof=False)
sorted(len(v[h]) for h in v)

Find all class numbers for discriminant up to 50:

sage: sage.schemes.elliptic_curves.cm.discriminants_with_bounded_class_number(hmax=5, B=50)
{1: [(-3, 1), (-3, 2), (-3, 3), (-4, 1), (-4, 2), (-7, 1), (-7, 2), (-8, 1), (-11, 1), (-19, 1), (-43, 1)], 2: [(-3, 4), (-4, 3), (-8, 2), (-15, 1), (-20, 1), (-24, 1), (-35, 1), (-40, 1)], 3: [(-11, 2), (-23, 1), (-31, 1)], 4: [(-39, 1)], 5: [(-47, 1)]}
>>> from sage.all import *
>>> sage.schemes.elliptic_curves.cm.discriminants_with_bounded_class_number(hmax=Integer(5), B=Integer(50))
{1: [(-3, 1), (-3, 2), (-3, 3), (-4, 1), (-4, 2), (-7, 1), (-7, 2), (-8, 1), (-11, 1), (-19, 1), (-43, 1)], 2: [(-3, 4), (-4, 3), (-8, 2), (-15, 1), (-20, 1), (-24, 1), (-35, 1), (-40, 1)], 3: [(-11, 2), (-23, 1), (-31, 1)], 4: [(-39, 1)], 5: [(-47, 1)]}
sage.schemes.elliptic_curves.cm.discriminants_with_bounded_class_number(hmax=5, B=50)
sage.schemes.elliptic_curves.cm.hilbert_class_polynomial(algorithm=None)[source]

Return the Hilbert class polynomial for discriminant \(D\).

INPUT:

  • D – negative integer congruent to 0 or 1 modulo 4

  • algorithm – string (default: None)

OUTPUT:

(integer polynomial) The Hilbert class polynomial for the discriminant \(D\).

ALGORITHM:

  • If algorithm = “arb” (default): Use FLINT’s implementation inherited from Arb which uses complex interval arithmetic.

  • If algorithm = “sage”: Use complex approximations to the roots.

  • If algorithm = “magma”: Call the appropriate Magma function (if available).

AUTHORS:

  • Sage implementation originally by Eduardo Ocampo Alvarez and AndreyTimofeev

  • Sage implementation corrected by John Cremona (using corrected precision bounds from Andreas Enge)

  • Magma implementation by David Kohel

EXAMPLES:

sage: # needs sage.libs.flint
sage: hilbert_class_polynomial(-4)
x - 1728
sage: hilbert_class_polynomial(-7)
x + 3375
sage: hilbert_class_polynomial(-23)
x^3 + 3491750*x^2 - 5151296875*x + 12771880859375
sage: hilbert_class_polynomial(-37*4)
x^2 - 39660183801072000*x - 7898242515936467904000000
sage: hilbert_class_polynomial(-37*4, algorithm='magma') # optional - magma
x^2 - 39660183801072000*x - 7898242515936467904000000
sage: hilbert_class_polynomial(-163)
x + 262537412640768000
sage: hilbert_class_polynomial(-163, algorithm='sage')
x + 262537412640768000
sage: hilbert_class_polynomial(-163, algorithm='magma') # optional - magma
x + 262537412640768000
>>> from sage.all import *
>>> # needs sage.libs.flint
>>> hilbert_class_polynomial(-Integer(4))
x - 1728
>>> hilbert_class_polynomial(-Integer(7))
x + 3375
>>> hilbert_class_polynomial(-Integer(23))
x^3 + 3491750*x^2 - 5151296875*x + 12771880859375
>>> hilbert_class_polynomial(-Integer(37)*Integer(4))
x^2 - 39660183801072000*x - 7898242515936467904000000
>>> hilbert_class_polynomial(-Integer(37)*Integer(4), algorithm='magma') # optional - magma
x^2 - 39660183801072000*x - 7898242515936467904000000
>>> hilbert_class_polynomial(-Integer(163))
x + 262537412640768000
>>> hilbert_class_polynomial(-Integer(163), algorithm='sage')
x + 262537412640768000
>>> hilbert_class_polynomial(-Integer(163), algorithm='magma') # optional - magma
x + 262537412640768000
# needs sage.libs.flint
hilbert_class_polynomial(-4)
hilbert_class_polynomial(-7)
hilbert_class_polynomial(-23)
hilbert_class_polynomial(-37*4)
hilbert_class_polynomial(-37*4, algorithm='magma') # optional - magma
hilbert_class_polynomial(-163)
hilbert_class_polynomial(-163, algorithm='sage')
hilbert_class_polynomial(-163, algorithm='magma') # optional - magma
sage.schemes.elliptic_curves.cm.is_HCP(f, check_monic_irreducible=True)[source]

Determine whether a polynomial is a Hilbert Class Polynomial.

INPUT:

  • f – a polynomial in \(\ZZ[X]\)

  • check_monic_irreducible – boolean (default: True); if True, check that f is a monic, irreducible, integer polynomial

OUTPUT:

(integer) – either \(D\) if f is the Hilbert Class Polynomial \(H_D\) for discriminant \(D\), or \(0\) if not an HCP.

ALGORITHM:

Cremona and Sutherland: Algorithm 2 of [CreSuth2023].

EXAMPLES:

Even for large degrees this is fast. We test the largest discriminant of class number 100, for which the HCP has coefficients with thousands of digits:

sage: from sage.schemes.elliptic_curves.cm import is_HCP
sage: D = -1856563
sage: D.class_number()                                                          # needs sage.libs.pari
100

sage: # needs sage.libs.flint
sage: H = hilbert_class_polynomial(D)
sage: H.degree()
100
sage: max(H).ndigits()
2774
sage: is_HCP(H)
-1856563
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.cm import is_HCP
>>> D = -Integer(1856563)
>>> D.class_number()                                                          # needs sage.libs.pari
100

>>> # needs sage.libs.flint
>>> H = hilbert_class_polynomial(D)
>>> H.degree()
100
>>> max(H).ndigits()
2774
>>> is_HCP(H)
-1856563
from sage.schemes.elliptic_curves.cm import is_HCP
D = -1856563
D.class_number()                                                          # needs sage.libs.pari
# needs sage.libs.flint
H = hilbert_class_polynomial(D)
H.degree()
max(H).ndigits()
is_HCP(H)

Testing polynomials which are not HCPs is faster:

sage: is_HCP(H+1)                                                               # needs sage.libs.flint
0
>>> from sage.all import *
>>> is_HCP(H+Integer(1))                                                               # needs sage.libs.flint
0
is_HCP(H+1)                                                               # needs sage.libs.flint
sage.schemes.elliptic_curves.cm.is_cm_j_invariant(algorithm='CremonaSutherland', method=None)[source]

Return whether or not this is a CM \(j\)-invariant, and the CM discriminant if it is.

INPUT:

  • j – an element of a number field \(K\)

  • algorithm – string (default: 'CremonaSutherland'); the algorithm used, either 'CremonaSutherland' (the default, very much faster for all but very small degrees), 'exhaustive' or 'reduction'

  • method – string; deprecated name for algorithm

OUTPUT:

A pair (bool, (d,f)) which is either (False, None) if \(j\) is not a CM j-invariant or (True, (d,f)) if \(j\) is the \(j\)-invariant of the imaginary quadratic order of discriminant \(D=df^2\) where \(d\) is the associated fundamental discriminant and \(f\) the index.

ALGORITHM:

The default algorithm used is to test whether the minimal polynomial of j is a Hilbert CLass Polynomail, using is_HCP() which implements Algorithm 2 of [CreSuth2023] by Cremona and Sutherland.

Two older algorithms are available, both of which are much slower except for very small degrees.

Method ‘exhaustive’ makes use of the complete and unconditionsl classification of all orders of class number up to 100, and hence will raise an error if \(j\) is an algebraic integer of degree greater than this.

Method ‘reduction’ constructs an elliptic curve over the number field \(\QQ(j)\) and computes its traces of Frobenius at several primes of degree 1.

EXAMPLES:

sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant
sage: is_cm_j_invariant(0)
(True, (-3, 1))
sage: is_cm_j_invariant(8000)
(True, (-8, 1))

sage: # needs sage.rings.number_field
sage: K.<a> = QuadraticField(5)
sage: is_cm_j_invariant(282880*a + 632000)
(True, (-20, 1))
sage: x = polygen(ZZ, 'x')
sage: K.<a> = NumberField(x^3 - 2)
sage: is_cm_j_invariant(31710790944000*a^2 + 39953093016000*a + 50337742902000)
(True, (-3, 6))
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.cm import is_cm_j_invariant
>>> is_cm_j_invariant(Integer(0))
(True, (-3, 1))
>>> is_cm_j_invariant(Integer(8000))
(True, (-8, 1))

>>> # needs sage.rings.number_field
>>> K = QuadraticField(Integer(5), names=('a',)); (a,) = K._first_ngens(1)
>>> is_cm_j_invariant(Integer(282880)*a + Integer(632000))
(True, (-20, 1))
>>> x = polygen(ZZ, 'x')
>>> K = NumberField(x**Integer(3) - Integer(2), names=('a',)); (a,) = K._first_ngens(1)
>>> is_cm_j_invariant(Integer(31710790944000)*a**Integer(2) + Integer(39953093016000)*a + Integer(50337742902000))
(True, (-3, 6))
from sage.schemes.elliptic_curves.cm import is_cm_j_invariant
is_cm_j_invariant(0)
is_cm_j_invariant(8000)
# needs sage.rings.number_field
K.<a> = QuadraticField(5)
is_cm_j_invariant(282880*a + 632000)
x = polygen(ZZ, 'x')
K.<a> = NumberField(x^3 - 2)
is_cm_j_invariant(31710790944000*a^2 + 39953093016000*a + 50337742902000)

An example of large degree. This is only possible using the default algorithm:

sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant
sage: D = -1856563
sage: H = hilbert_class_polynomial(D)                                           # needs sage.libs.flint
sage: H.degree()                                                                # needs sage.libs.flint
100
sage: K.<j> = NumberField(H)                                                    # needs sage.libs.flint sage.rings.number_field
sage: is_cm_j_invariant(j)                                                      # needs sage.libs.flint sage.rings.number_field
(True, (-1856563, 1))
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.cm import is_cm_j_invariant
>>> D = -Integer(1856563)
>>> H = hilbert_class_polynomial(D)                                           # needs sage.libs.flint
>>> H.degree()                                                                # needs sage.libs.flint
100
>>> K = NumberField(H, names=('j',)); (j,) = K._first_ngens(1)# needs sage.libs.flint sage.rings.number_field
>>> is_cm_j_invariant(j)                                                      # needs sage.libs.flint sage.rings.number_field
(True, (-1856563, 1))
from sage.schemes.elliptic_curves.cm import is_cm_j_invariant
D = -1856563
H = hilbert_class_polynomial(D)                                           # needs sage.libs.flint
H.degree()                                                                # needs sage.libs.flint
K.<j> = NumberField(H)                                                    # needs sage.libs.flint sage.rings.number_field
is_cm_j_invariant(j)                                                      # needs sage.libs.flint sage.rings.number_field
sage.schemes.elliptic_curves.cm.largest_disc_with_class_number(h)[source]

Return largest absolute value of any negative discriminant with class number \(h\), and the number of fundamental negative discriminants with that class number. This is known (unconditionally) for \(h\) up to 100, by work of Mark Watkins [Watkins2004] for fundamental discriminants, extended to all discriminants of class number \(h\le100\) by Klaise [Klaise2012].

Note

The class number of a negative discriminant \(D\) is the same as the class number of the unique imaginary quadratic order of discriminant \(D\), so this function gives the number of such orders of each class number \(h\le100\). It is easy to extend this to larger class number conditional on the GRH, but much harder to obyain unconditional results.

INPUT:

  • h – integer

EXAMPLES:

sage: from sage.schemes.elliptic_curves.cm import largest_disc_with_class_number
sage: largest_disc_with_class_number(0)
(0, 0)
sage: largest_disc_with_class_number(1)
(163, 13)
sage: largest_disc_with_class_number(2)
(427, 29)
sage: largest_disc_with_class_number(10)
(13843, 123)
sage: largest_disc_with_class_number(100)
(1856563, 2311)
sage: largest_disc_with_class_number(101)
Traceback (most recent call last):
...
NotImplementedError: largest discriminant not available for class number 101
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.cm import largest_disc_with_class_number
>>> largest_disc_with_class_number(Integer(0))
(0, 0)
>>> largest_disc_with_class_number(Integer(1))
(163, 13)
>>> largest_disc_with_class_number(Integer(2))
(427, 29)
>>> largest_disc_with_class_number(Integer(10))
(13843, 123)
>>> largest_disc_with_class_number(Integer(100))
(1856563, 2311)
>>> largest_disc_with_class_number(Integer(101))
Traceback (most recent call last):
...
NotImplementedError: largest discriminant not available for class number 101
from sage.schemes.elliptic_curves.cm import largest_disc_with_class_number
largest_disc_with_class_number(0)
largest_disc_with_class_number(1)
largest_disc_with_class_number(2)
largest_disc_with_class_number(10)
largest_disc_with_class_number(100)
largest_disc_with_class_number(101)

For most \(h\le100\), the largest fundamental discriminant with class number \(h\) is also the largest discriminant, but this is not the case for some \(h\):

sage: from sage.schemes.elliptic_curves.cm import largest_disc_with_class_number, largest_fundamental_disc_with_class_number
sage: [h for h in range(1,101) if largest_disc_with_class_number(h)[0] != largest_fundamental_disc_with_class_number(h)[0]]
[6, 8, 12, 16, 20, 30, 40, 42, 52, 70]
sage: largest_fundamental_disc_with_class_number(6)
(3763, 51)
sage: largest_disc_with_class_number(6)
(4075, 101)
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.cm import largest_disc_with_class_number, largest_fundamental_disc_with_class_number
>>> [h for h in range(Integer(1),Integer(101)) if largest_disc_with_class_number(h)[Integer(0)] != largest_fundamental_disc_with_class_number(h)[Integer(0)]]
[6, 8, 12, 16, 20, 30, 40, 42, 52, 70]
>>> largest_fundamental_disc_with_class_number(Integer(6))
(3763, 51)
>>> largest_disc_with_class_number(Integer(6))
(4075, 101)
from sage.schemes.elliptic_curves.cm import largest_disc_with_class_number, largest_fundamental_disc_with_class_number
[h for h in range(1,101) if largest_disc_with_class_number(h)[0] != largest_fundamental_disc_with_class_number(h)[0]]
largest_fundamental_disc_with_class_number(6)
largest_disc_with_class_number(6)
sage.schemes.elliptic_curves.cm.largest_fundamental_disc_with_class_number(h)[source]

Return largest absolute value of any fundamental negative discriminant with class number \(h\), and the number of fundamental negative discriminants with that class number. This is known (unconditionally) for \(h\) up to 100, by work of Mark Watkins ([Watkins2004]).

Note

The class number of a fundamental negative discriminant \(D\) is the same as the class number of the imaginary quadratic field \(\QQ(\sqrt{D})\), so this function gives the number of such fields of each class number \(h\le100\). It is easy to extend this to larger class number conditional on the GRH, but much harder to obtain unconditional results.

INPUT:

  • h – integer

EXAMPLES:

sage: from sage.schemes.elliptic_curves.cm import largest_fundamental_disc_with_class_number
sage: largest_fundamental_disc_with_class_number(0)
(0, 0)
sage: largest_fundamental_disc_with_class_number(1)
(163, 9)
sage: largest_fundamental_disc_with_class_number(2)
(427, 18)
sage: largest_fundamental_disc_with_class_number(10)
(13843, 87)
sage: largest_fundamental_disc_with_class_number(100)
(1856563, 1736)
sage: largest_fundamental_disc_with_class_number(101)
Traceback (most recent call last):
...
NotImplementedError: largest fundamental discriminant not available for class number 101
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.cm import largest_fundamental_disc_with_class_number
>>> largest_fundamental_disc_with_class_number(Integer(0))
(0, 0)
>>> largest_fundamental_disc_with_class_number(Integer(1))
(163, 9)
>>> largest_fundamental_disc_with_class_number(Integer(2))
(427, 18)
>>> largest_fundamental_disc_with_class_number(Integer(10))
(13843, 87)
>>> largest_fundamental_disc_with_class_number(Integer(100))
(1856563, 1736)
>>> largest_fundamental_disc_with_class_number(Integer(101))
Traceback (most recent call last):
...
NotImplementedError: largest fundamental discriminant not available for class number 101
from sage.schemes.elliptic_curves.cm import largest_fundamental_disc_with_class_number
largest_fundamental_disc_with_class_number(0)
largest_fundamental_disc_with_class_number(1)
largest_fundamental_disc_with_class_number(2)
largest_fundamental_disc_with_class_number(10)
largest_fundamental_disc_with_class_number(100)
largest_fundamental_disc_with_class_number(101)