Testing whether elliptic curves over number fields are \(\QQ\)-curves - Elliptic curves

Skip to content
Back to top
View this page
Edit this page

# Testing whether elliptic curves over number fields are \(\QQ\)-curves

AUTHORS:

- John Cremona (February 2021)

The code here implements the algorithm of Cremona and Najman presented in [CrNa2020].

sage.schemes.elliptic_curves.Qcurves.Step4Test(E, B, oldB=0, verbose=False)

  Apply local Q-curve test to E at all primes up to B.

  INPUT:

  - `E` – elliptic curve defined over a number field

  - `B` – integer; upper bound on primes to test

  - `oldB` – integer (default: 0); lower bound on primes to test

  - `verbose` – boolean (default: `False`); verbosity flag

  OUTPUT:

  Either (`False`, \(p\)), if the local test at \(p\) proves that \(E\) is not a \(\QQ\)-curve, or (`True`, \(0\)) if all local tests at primes between `oldB` and `B` fail to prove that \(E\) is not a \(\QQ\)-curve.

  ALGORITHM (see [CrNa2020] for details):

  This local test at \(p\) only applies if \(E\) has good reduction at all of the primes lying above \(p\) in the base field \(K\) of \(E\). It tests whether (1) \(E\) is either ordinary at all \(P\mid p\), or supersingular at all; (2) if ordinary at all, it tests that the squarefree part of \(a_P^2-4N(P)\) is the same for all \(P\mid p\).

  EXAMPLES:

  A non-\(\QQ\)-curve over a quartic field (with LMFDB label ‘4.4.8112.1-12.1-a1’) fails this test at \(p=13\):

  Sage

      sage: from sage.schemes.elliptic_curves.Qcurves import Step4Test
      sage: R.<x> = PolynomialRing(QQ)
      sage: K.<a> = NumberField(R([3, 0, -5, 0, 1]))                                  # needs sage.rings.number_field
      sage: E = EllipticCurve([K([-3,-4,1,1]), K([4,-1,-1,0]), K([-2,0,1,0]),         # needs sage.rings.number_field
      ....:                    K([-621,778,138,-178]), K([9509,2046,-24728,10380])])
      sage: Step4Test(E, 100, verbose=True)                                           # needs sage.rings.number_field
      No: inconsistency at the 2 ordinary primes dividing 13
      - Frobenius discriminants mod squares: [-3, -1]
      (False, 13)

  Python

      >>> from sage.all import *
      >>> from sage.schemes.elliptic_curves.Qcurves import Step4Test
      >>> R = PolynomialRing(QQ, names=('x',)); (x,) = R._first_ngens(1)
      >>> K = NumberField(R([Integer(3), Integer(0), -Integer(5), Integer(0), Integer(1)]), names=('a',)); (a,) = K._first_ngens(1)# needs sage.rings.number_field
      >>> E = EllipticCurve([K([-Integer(3),-Integer(4),Integer(1),Integer(1)]), K([Integer(4),-Integer(1),-Integer(1),Integer(0)]), K([-Integer(2),Integer(0),Integer(1),Integer(0)]),         # needs sage.rings.number_field
      ...                    K([-Integer(621),Integer(778),Integer(138),-Integer(178)]), K([Integer(9509),Integer(2046),-Integer(24728),Integer(10380)])])
      >>> Step4Test(E, Integer(100), verbose=True)                                           # needs sage.rings.number_field
      No: inconsistency at the 2 ordinary primes dividing 13
      - Frobenius discriminants mod squares: [-3, -1]
      (False, 13)

  A \(\QQ\)-curve over a sextic field (with LMFDB label ‘6.6.1259712.1-64.1-a6’) passes this test for all \(p<100\):

  Sage

      sage: from sage.schemes.elliptic_curves.Qcurves import Step4Test
      sage: R.<x> = PolynomialRing(QQ)
      sage: K.<a> = NumberField(R([-3, 0, 9, 0, -6, 0, 1]))                           # needs sage.rings.number_field
      sage: E = EllipticCurve([K([1,-3,0,1,0,0]), K([5,-3,-6,1,1,0]),                 # needs sage.rings.number_field
      ....:                    K([1,-3,0,1,0,0]), K([-139,-129,331,277,-76,-63]),
      ....:                    K([2466,1898,-5916,-4582,1361,1055])])
      sage: Step4Test(E, 100, verbose=True)                                           # needs sage.rings.number_field
      (True, 0)

  [Python]

      >>> from sage.all import *
      >>> from sage.schemes.elliptic_curves.Qcurves import Step4Test
      >>> R = PolynomialRing(QQ, names=('x',)); (x,) = R._first_ngens(1)
      >>> K = NumberField(R([-Integer(3), Integer(0), Integer(9), Integer(0), -Integer(6), Integer(0), Integer(1)]), names=('a',)); (a,) = K._first_ngens(1)# needs sage.rings.number_field
      >>> E = EllipticCurve([K([Integer(1),-Integer(3),Integer(0),Integer(1),Integer(0),Integer(0)]), K([Integer(5),-Integer(3),-Integer(6),Integer(1),Integer(1),Integer(0)]),                 # needs sage.rings.number_field
      ...                    K([Integer(1),-Integer(3),Integer(0),Integer(1),Integer(0),Integer(0)]), K([-Integer(139),-Integer(129),Integer(331),Integer(277),-Integer(76),-Integer(63)]),
      ...                    K([Integer(2466),Integer(1898),-Integer(5916),-Integer(4582),Integer(1361),Integer(1055)])])
      >>> Step4Test(E, Integer(100), verbose=True)                                           # needs sage.rings.number_field
      (True, 0)

sage.schemes.elliptic_curves.Qcurves.conjugacy_test(jlist, verbose=False)

  Test whether a list of algebraic numbers contains a complete conjugacy class of 2-power degree.

  INPUT:

  - `jlist` – list of algebraic numbers in the same field

  - `verbose` – boolean (default: `False`); verbosity flag

  OUTPUT:

  A possibly empty list of irreducible polynomials over \(\QQ\) of 2-power degree all of whose roots are in the list.

  EXAMPLES:

  Sage

      sage: # needs sage.rings.number_field
      sage: from sage.schemes.elliptic_curves.Qcurves import conjugacy_test
      sage: conjugacy_test([3])
      [x - 3]
      sage: K.<a> = QuadraticField(2)
      sage: conjugacy_test([K(3), a])
      [x - 3]
      sage: conjugacy_test([K(3), 3 + a])
      [x - 3]
      sage: conjugacy_test([3 + a])
      []
      sage: conjugacy_test([3 + a, 3 - a])
      [x^2 - 6*x + 7]
      sage: x = polygen(QQ)
      sage: f = x^3 - 3
      sage: K.<a> = f.splitting_field()
      sage: js = f.roots(K, multiplicities=False)
      sage: conjugacy_test(js)
      []
      sage: f = x^4 - 3
      sage: K.<a> = NumberField(f)
      sage: js = f.roots(K, multiplicities=False)
      sage: conjugacy_test(js)
      []
      sage: K.<a> = f.splitting_field()
      sage: js = f.roots(K, multiplicities=False)
      sage: conjugacy_test(js)
      [x^4 - 3]

  Python

      >>> from sage.all import *
      >>> # needs sage.rings.number_field
      >>> from sage.schemes.elliptic_curves.Qcurves import conjugacy_test
      >>> conjugacy_test([Integer(3)])
      [x - 3]
      >>> K = QuadraticField(Integer(2), names=('a',)); (a,) = K._first_ngens(1)
      >>> conjugacy_test([K(Integer(3)), a])
      [x - 3]
      >>> conjugacy_test([K(Integer(3)), Integer(3) + a])
      [x - 3]
      >>> conjugacy_test([Integer(3) + a])
      []
      >>> conjugacy_test([Integer(3) + a, Integer(3) - a])
      [x^2 - 6*x + 7]
      >>> x = polygen(QQ)
      >>> f = x**Integer(3) - Integer(3)
      >>> K = f.splitting_field(names=('a',)); (a,) = K._first_ngens(1)
      >>> js = f.roots(K, multiplicities=False)
      >>> conjugacy_test(js)
      []
      >>> f = x**Integer(4) - Integer(3)
      >>> K = NumberField(f, names=('a',)); (a,) = K._first_ngens(1)
      >>> js = f.roots(K, multiplicities=False)
      >>> conjugacy_test(js)
      []
      >>> K = f.splitting_field(names=('a',)); (a,) = K._first_ngens(1)
      >>> js = f.roots(K, multiplicities=False)
      >>> conjugacy_test(js)
      [x^4 - 3]

sage.schemes.elliptic_curves.Qcurves.is_Q_curve(E, maxp=100, certificate=False, verbose=False)

  Return whether `E` is a \(\QQ\)-curve, with optional certificate.

  INPUT:

  - `E` – elliptic curve over a number field

  - `maxp` – integer (default: 100); bound on primes used for checking necessary local conditions. The result will not depend on this, but using a larger value may return `False` faster.

  - `certificate` – boolean (default: `False`); if `True` then a second value is returned giving a certificate for the \(\QQ\)-curve property

  OUTPUT:

  If `certificate` is `False`: either `True` (if \(E\) is a \(\QQ\)-curve), or `False`.

  If `certificate` is `True`: a tuple consisting of a boolean flag as before and a certificate, defined as follows:

  - when the flag is `True`, so \(E\) is a \(\QQ\)-curve:

    - either {‘CM’:\(D\)} where \(D\) is a negative discriminant, when \(E\) has potential CM with discriminant \(D\);

    - otherwise {‘CM’: \(0\), ‘core_poly’: \(f\), ‘rho’: \(\rho\), ‘r’: \(r\), ‘N’: \(N\)}, when \(E\) is a non-CM \(\QQ\)-curve, where the core polynomial \(f\) is an irreducible monic polynomial over \(QQ\) of degree \(2^\rho\), all of whose roots are \(j\)-invariants of curves isogenous to \(E\), the core level \(N\) is a square-free integer with \(r\) prime factors which is the LCM of the degrees of the isogenies between these conjugates. For example, if there exists a curve \(E'\) isogenous to \(E\) with \(j(E')=j\in\QQ\), then the certificate is {‘CM’:0, ‘r’:0, ‘rho’:0, ‘core_poly’: x-j, ‘N’:1}.

  - when the flag is `False`, so \(E\) is not a \(\QQ\)-curve, the certificate is a prime \(p\) such that the reductions of \(E\) at the primes dividing \(p\) are inconsistent with the property of being a \(\QQ\)-curve. See the ALGORITHM section for details.

  ALGORITHM:

  See [CrNa2020] for details.

  1. If \(E\) has rational \(j\)-invariant, or has CM, then return `True`.

  2. Replace \(E\) by a curve defined over \(K=\QQ(j(E))\). Let \(N\) be the conductor norm.

  3. For all primes \(p\mid N\) check that the valuations of \(j\) at all \(P\mid p\) are either all negative or all nonnegative; if not, return `False`.

  4. For \(p\le maxp\), \(p\not\mid N\), check that either \(E\) is ordinary mod \(P\) for all \(P\mid p\), or \(E\) is supersingular mod \(P\) for all \(P\mid p\); if neither, return `False`. If all are ordinary, check that the integers \(a_P(E)^2-4N(P)\) have the same square-free part; if not, return `False`.

  5. Compute the \(K\)-isogeny class of \(E\) using the “heuristic” option (which is faster, but not guaranteed to be complete). Check whether the set of \(j\)-invariants of curves in the class of \(2\)-power degree contains a complete Galois orbit. If so, return `True`.

  6. Otherwise repeat step 4 for more primes, and if still undecided, repeat Step 5 without the “heuristic” option, to get the complete \(K\)-isogeny class (which will probably be no bigger than before). Now return `True` if the set of \(j\)-invariants of curves in the class contains a complete Galois orbit, otherwise return `False`.

  EXAMPLES:

  A non-CM curve over \(\QQ\) and a CM curve over \(\QQ\) are both trivially \(\QQ\)-curves:

  Sage

      sage: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve
      sage: E = EllipticCurve([1,2,3,4,5])
      sage: flag, cert = is_Q_curve(E, certificate=True)
      sage: flag
      True
      sage: cert
      {'CM': 0, 'N': 1, 'core_poly': x, 'r': 0, 'rho': 0}

      sage: E = EllipticCurve(j=8000)
      sage: flag, cert = is_Q_curve(E, certificate=True)
      sage: flag
      True
      sage: cert
      {'CM': -8}

  Python

      >>> from sage.all import *
      >>> from sage.schemes.elliptic_curves.Qcurves import is_Q_curve
      >>> E = EllipticCurve([Integer(1),Integer(2),Integer(3),Integer(4),Integer(5)])
      >>> flag, cert = is_Q_curve(E, certificate=True)
      >>> flag
      True
      >>> cert
      {'CM': 0, 'N': 1, 'core_poly': x, 'r': 0, 'rho': 0}

      >>> E = EllipticCurve(j=Integer(8000))
      >>> flag, cert = is_Q_curve(E, certificate=True)
      >>> flag
      True
      >>> cert
      {'CM': -8}

  A non-\(\QQ\)-curve over a quartic field. The local data at bad primes above \(3\) is inconsistent:

  Sage

      sage: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve
      sage: R.<x> = PolynomialRing(QQ)
      sage: K.<a> = NumberField(R([3, 0, -5, 0, 1]))                                  # needs sage.rings.number_field
      sage: E = EllipticCurve([K([-3,-4,1,1]), K([4,-1,-1,0]), K([-2,0,1,0]),         # needs sage.rings.number_field
      ....:                    K([-621,778,138,-178]), K([9509,2046,-24728,10380])])
      sage: is_Q_curve(E, certificate=True, verbose=True)                             # needs sage.rings.number_field
      Checking whether Elliptic Curve defined by y^2 + (a^3+a^2-4*a-3)*x*y + (a^2-2)*y = x^3 + (-a^2-a+4)*x^2 + (-178*a^3+138*a^2+778*a-621)*x + (10380*a^3-24728*a^2+2046*a+9509) over Number Field in a with defining polynomial x^4 - 5*x^2 + 3 is a Q-curve
      No: inconsistency at the 2 primes dividing 3
      - potentially multiplicative: [True, False]
      (False, 3)

  [Python]

      >>> from sage.all import *
      >>> from sage.schemes.elliptic_curves.Qcurves import is_Q_curve
      >>> R = PolynomialRing(QQ, names=('x',)); (x,) = R._first_ngens(1)
      >>> K = NumberField(R([Integer(3), Integer(0), -Integer(5), Integer(0), Integer(1)]), names=('a',)); (a,) = K._first_ngens(1)# needs sage.rings.number_field
      >>> E = EllipticCurve([K([-Integer(3),-Integer(4),Integer(1),Integer(1)]), K([Integer(4),-Integer(1),-Integer(1),Integer(0)]), K([-Integer(2),Integer(0),Integer(1),Integer(0)]),         # needs sage.rings.number_field
      ...                    K([-Integer(621),Integer(778),Integer(138),-Integer(178)]), K([Integer(9509),Integer(2046),-Integer(24728),Integer(10380)])])
      >>> is_Q_curve(E, certificate=True, verbose=True)                             # needs sage.rings.number_field
      Checking whether Elliptic Curve defined by y^2 + (a^3+a^2-4*a-3)*x*y + (a^2-2)*y = x^3 + (-a^2-a+4)*x^2 + (-178*a^3+138*a^2+778*a-621)*x + (10380*a^3-24728*a^2+2046*a+9509) over Number Field in a with defining polynomial x^4 - 5*x^2 + 3 is a Q-curve
      No: inconsistency at the 2 primes dividing 3
      - potentially multiplicative: [True, False]
      (False, 3)

  A non-\(\QQ\)-curve over a quadratic field. The local data at bad primes is consistent, but the local test at good primes above \(13\) is not:

  Sage

      sage: K.<a> = NumberField(R([-10, 0, 1]))                                       # needs sage.rings.number_field
      sage: E = EllipticCurve([K([0,1]), K([-1,-1]), K([0,0]),                        # needs sage.rings.number_field
      ....:                    K([-236,40]), K([-1840,464])])
      sage: is_Q_curve(E, certificate=True, verbose=True)                             # needs sage.rings.number_field
      Checking whether Elliptic Curve defined by y^2 + a*x*y = x^3 + (-a-1)*x^2 + (40*a-236)*x + (464*a-1840) over Number Field in a with defining polynomial x^2 - 10 is a Q-curve
      Applying local tests at good primes above p<=100
      No: inconsistency at the 2 ordinary primes dividing 13
      - Frobenius discriminants mod squares: [-1, -3]
      No: local test at p=13 failed
      (False, 13)

  Python

      >>> from sage.all import *
      >>> K = NumberField(R([-Integer(10), Integer(0), Integer(1)]), names=('a',)); (a,) = K._first_ngens(1)# needs sage.rings.number_field
      >>> E = EllipticCurve([K([Integer(0),Integer(1)]), K([-Integer(1),-Integer(1)]), K([Integer(0),Integer(0)]),                        # needs sage.rings.number_field
      ...                    K([-Integer(236),Integer(40)]), K([-Integer(1840),Integer(464)])])
      >>> is_Q_curve(E, certificate=True, verbose=True)                             # needs sage.rings.number_field
      Checking whether Elliptic Curve defined by y^2 + a*x*y = x^3 + (-a-1)*x^2 + (40*a-236)*x + (464*a-1840) over Number Field in a with defining polynomial x^2 - 10 is a Q-curve
      Applying local tests at good primes above p<=100
      No: inconsistency at the 2 ordinary primes dividing 13
      - Frobenius discriminants mod squares: [-1, -3]
      No: local test at p=13 failed
      (False, 13)

  A quadratic \(\QQ\)-curve with CM discriminant \(-15\) (\(j\)-invariant not in \(\QQ\)):

  Sage

      sage: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve
      sage: R.<x> = PolynomialRing(QQ)
      sage: K.<a> = NumberField(R([-1, -1, 1]))                                       # needs sage.rings.number_field
      sage: E = EllipticCurve([K([1,0]), K([-1,0]), K([0,1]), K([0,-2]), K([0,1])])   # needs sage.rings.number_field
      sage: is_Q_curve(E, certificate=True, verbose=True)                             # needs sage.rings.number_field
      Checking whether Elliptic Curve defined by y^2 + x*y + a*y = x^3 + (-1)*x^2 + (-2*a)*x + a over Number Field in a with defining polynomial x^2 - x - 1 is a Q-curve
      Yes: E is CM (discriminant -15)
      (True, {'CM': -15})

  [Python]

      >>> from sage.all import *
      >>> from sage.schemes.elliptic_curves.Qcurves import is_Q_curve
      >>> R = PolynomialRing(QQ, names=('x',)); (x,) = R._first_ngens(1)
      >>> K = NumberField(R([-Integer(1), -Integer(1), Integer(1)]), names=('a',)); (a,) = K._first_ngens(1)# needs sage.rings.number_field
      >>> E = EllipticCurve([K([Integer(1),Integer(0)]), K([-Integer(1),Integer(0)]), K([Integer(0),Integer(1)]), K([Integer(0),-Integer(2)]), K([Integer(0),Integer(1)])])   # needs sage.rings.number_field
      >>> is_Q_curve(E, certificate=True, verbose=True)                             # needs sage.rings.number_field
      Checking whether Elliptic Curve defined by y^2 + x*y + a*y = x^3 + (-1)*x^2 + (-2*a)*x + a over Number Field in a with defining polynomial x^2 - x - 1 is a Q-curve
      Yes: E is CM (discriminant -15)
      (True, {'CM': -15})

  An example over \(\QQ(\sqrt{2},\sqrt{3})\). The \(j\)-invariant is in \(\QQ(\sqrt{6})\), so computations will be done over that field, and in fact there is an isogenous curve with rational \(j\), so we have a so-called rational \(\QQ\)-curve:

  Sage

      sage: # needs sage.rings.number_field
      sage: K.<a> = NumberField(R([1, 0, -4, 0, 1]))
      sage: E = EllipticCurve([K([-2,-4,1,1]), K([0,1,0,0]), K([0,1,0,0]),
      ....:                    K([-4780,9170,1265,-2463]),
      ....:                    K([163923,-316598,-43876,84852])])
      sage: flag, cert = is_Q_curve(E, certificate=True)
      sage: flag
      True
      sage: cert
      {'CM': 0, 'N': 1, 'core_degs': [1], 'core_poly': x - 85184/3, 'r': 0, 'rho': 0}

  Python

      >>> from sage.all import *
      >>> # needs sage.rings.number_field
      >>> K = NumberField(R([Integer(1), Integer(0), -Integer(4), Integer(0), Integer(1)]), names=('a',)); (a,) = K._first_ngens(1)
      >>> E = EllipticCurve([K([-Integer(2),-Integer(4),Integer(1),Integer(1)]), K([Integer(0),Integer(1),Integer(0),Integer(0)]), K([Integer(0),Integer(1),Integer(0),Integer(0)]),
      ...                    K([-Integer(4780),Integer(9170),Integer(1265),-Integer(2463)]),
      ...                    K([Integer(163923),-Integer(316598),-Integer(43876),Integer(84852)])])
      >>> flag, cert = is_Q_curve(E, certificate=True)
      >>> flag
      True
      >>> cert
      {'CM': 0, 'N': 1, 'core_degs': [1], 'core_poly': x - 85184/3, 'r': 0, 'rho': 0}

  Over the same field, a so-called strict \(\QQ\)-curve which is not isogenous to one with rational \(j\), but whose core field is quadratic. In fact the isogeny class over \(K\) consists of \(6\) curves, four with conjugate quartic \(j\)-invariants and \(2\) with quadratic conjugate \(j\)-invariants in \(\QQ(\sqrt{3})\) (but which are not base-changes from the quadratic subfield):

  Sage

      sage: # needs sage.rings.number_field
      sage: E = EllipticCurve([K([0,-3,0,1]), K([1,4,0,-1]), K([0,0,0,0]),
      ....:                    K([-2,-16,0,4]), K([-19,-32,4,8])])
      sage: flag, cert = is_Q_curve(E, certificate=True)
      sage: flag
      True
      sage: cert
      {'CM': 0,
       'N': 2,
       'core_degs': [1, 2],
       'core_poly': x^2 - 840064*x + 1593413632,
       'r': 1,
       'rho': 1}

  [Python]

      >>> from sage.all import *
      >>> # needs sage.rings.number_field
      >>> E = EllipticCurve([K([Integer(0),-Integer(3),Integer(0),Integer(1)]), K([Integer(1),Integer(4),Integer(0),-Integer(1)]), K([Integer(0),Integer(0),Integer(0),Integer(0)]),
      ...                    K([-Integer(2),-Integer(16),Integer(0),Integer(4)]), K([-Integer(19),-Integer(32),Integer(4),Integer(8)])])
      >>> flag, cert = is_Q_curve(E, certificate=True)
      >>> flag
      True
      >>> cert
      {'CM': 0,
       'N': 2,
       'core_degs': [1, 2],
       'core_poly': x^2 - 840064*x + 1593413632,
       'r': 1,
       'rho': 1}