def find_elliptic_curve(k, lT): ''' Find elliptic curve with given trace mod l. INPUT: - ``k`` -- a base field, - ``lT`` -- a pair of an integer and a set of candidates for the trace. EXAMPLES:: sage: from ellrains import find_elliptic_curve sage: lT = (229,[2]) sage: find_elliptic_curve(GF(5), lT) Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 5 ''' l, T = lT L = GF(l) for j in k: # Don't mess with complicated cases if j == k(0) or j == k(1728): continue E = EllipticCurve(j = j) t = L(E.trace_of_frobenius()) if t in T: return E elif -t in T: return E.quadratic_twist() return None
def __init__(self, A, C=None): """ Cy² = Cx³ + Ax + Cx """ # Internally, we represent it as a Sage elliptic curve. a = A / C if C is not None else A self.ec = EllipticCurve([0, a._sage(), 0, 1, 0])
def test_semi_stable_frobenius_polynomial_t(): # an example where the frobenius polynomial depends on the purely ramified extension # we make x = polygen(QQ) K = QQ.extension(x - 1, "one") E = EllipticCurve(K, [49, 343]) assert E.discriminant() == -(2**4) * 31 * 7**6 assert E.j_invariant() == K(2**8 * 3**3) / 31 f1 = semi_stable_frobenius_polynomial(E, K * 7, 1) f2 = semi_stable_frobenius_polynomial(E, K * 7, -1)(x=-x) assert f1 == f2
def __scale_by_periods_only__(self): r""" If we fail to scale with ``_find_scaling_L_ratio``, we drop here to try and find the scaling by the quotient of the periods to the `X_0`-optimal curve. The resulting ``_scaling`` is not guaranteed to be correct, but could well be. EXAMPLES:: sage: E = EllipticCurve('19a1') sage: m = E.modular_symbol(sign=+1) sage: m.__scale_by_periods_only__() Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by -1, 2 or -2. sage: m._scaling 1 sage: E = EllipticCurve('19a2') sage: m = E.modular_symbol(sign=+1) sage: m._scaling 3/2 sage: m.__scale_by_periods_only__() Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by -1, 2 or -2. sage: m._scaling 3 """ # we only do this inside the cremona-tables. try: crla = parse_cremona_label(self._E.label()) except RuntimeError: # raised when curve is outside of the table print( "Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by a rational number." ) self._scaling = 1 else: print( "Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by -1, 2 or -2." ) cr0 = Integer(crla[0]).str() + crla[1] + '1' E0 = EllipticCurve(cr0) if self._sign == 1: q = E0.period_lattice().basis()[0] / self._E.period_lattice( ).basis()[0] else: q = E0.period_lattice().basis()[1].imag( ) / self._E.period_lattice().basis()[1].imag() if E0.real_components() == 1: q *= 2 if self._E.real_components() == 1: q /= 2 q = ZZ(int(round(q * 200))) / 200 verbose('scale modular symbols by %s' % q) self._scaling = q
def getcoords(E, u, prec=20, R=None): if R is None: R = u.parent() u = R(u) p = R.prime() jE = E.j_invariant() # Calculate the Tate parameter E4 = EisensteinForms(weight=4).basis()[0] Delta = CuspForms(weight=12).basis()[0] j = (E4.q_expansion(prec + 7)) ** 3 / Delta.q_expansion(prec + 7) qE = (1 / j).power_series().reversion()(R(1 / jE)) # Normalize the period by appropriate powers of qE un = u * qE ** (-(u.valuation() / qE.valuation()).floor()) precn = (prec / qE.valuation()).floor() + 4 # formulas in Silverman II (Advanced Topics in the Arithmetic of Elliptic curves, p. 425) xx = un / (1 - un) ** 2 + sum( [ qE ** n * un / (1 - qE ** n * un) ** 2 + qE ** n / un / (1 - qE ** n / un) ** 2 - 2 * qE ** n / (1 - qE ** n) ** 2 for n in range(1, precn) ] ) yy = un ** 2 / (1 - un) ** 3 + sum( [ qE ** (2 * n) * un ** 2 / (1 - qE ** n * un) ** 3 - qE ** n / un / (1 - qE ** n / un) ** 3 + qE ** n / (1 - qE ** n) ** 2 for n in range(1, precn) ] ) sk = lambda q, k, pprec: sum([n ** k * q ** n / (1 - q ** n) for n in range(1, pprec + 1)]) n = qE.valuation() precp = ((prec + 4) / n).floor() + 2 tate_a4 = -5 * sk(qE, 3, precp) tate_a6 = (tate_a4 - 7 * sk(qE, 5, precp)) / 12 Eq = EllipticCurve([R(1), R(0), R(0), tate_a4, tate_a6]) C2 = (Eq.c4() * E.c6()) / (Eq.c6() * E.c4()) C = R(C2).square_root() s = (C - R(E.a1())) / R(2) r = (s * (C - s) - R(E.a2())) / 3 t = (r * (2 * s - C) - R(E.a3())) / 2 return (r + C2 * xx, t + s * C2 * xx + C * C2 * yy)
def _find_scaling_period(self): r""" Uses the integral period map of the modular symbol implementation in sage in order to determine the scaling. The resulting modular symbol is correct only for the `X_0`-optimal curve, at least up to a possible factor +-1 or +-2. EXAMPLES:: sage: E = EllipticCurve('11a1') sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period') sage: m._e (1/5, 1) sage: E = EllipticCurve('11a2') sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period') sage: m._e (1, 5) sage: E = EllipticCurve('121b2') sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period') sage: m._e (0, 11/2, 0, 11/2, 11/2, 0, 0, -3, 2, 1/2, -1, 3/2) """ P = self._modsym.integral_period_mapping() self._e = P.matrix().transpose().row(0) self._e /= 2 E = self._E try: crla = parse_cremona_label(E.label()) except RuntimeError: # raised when curve is outside of the table self._scaling = 1 else: cr0 = Integer(crla[0]).str() + crla[1] + '1' E0 = EllipticCurve(cr0) if self._sign == 1: q = E0.period_lattice().basis()[0] / E.period_lattice().basis( )[0] else: q = E0.period_lattice().basis()[1].imag() / E.period_lattice( ).basis()[1].imag() if E0.real_components() == 1: q *= 2 if E.real_components() == 1: q /= 2 q = QQ(int(round(q * 200))) / 200 verbose('scale modular symbols by %s' % q) self._scaling = q self._e *= self._scaling
def curve(self, prec=20): r""" Returns the `p`-adic elliptic curve of the form `y^2+x y = x^3 + s_4 x+s_6`. This curve with split multiplicative reduction is isomorphic to the given curve over the algebraic closure of `\QQ_p`. INPUT: - ``prec`` - the `p`-adic precision, default is 20. EXAMPLES:: sage: eq = EllipticCurve('130a1').tate_curve(5) sage: eq.curve(prec=5) Elliptic Curve defined by y^2 + (1+O(5^5))*x*y = x^3 + (2*5^4+5^5+2*5^6+5^7+3*5^8+O(5^9))*x + (2*5^3+5^4+2*5^5+5^7+O(5^8)) over 5-adic Field with capped relative precision 5 """ try: Eq = self.__curve if Eq.a6().absolute_precision() >= prec: return Eq except AttributeError: pass qE = self.parameter(prec=prec) n = qE.valuation() precp = (prec / n).floor() + 2 R = qE.parent() tate_a4 = -5 * self.__sk(3, precp) tate_a6 = (tate_a4 - 7 * self.__sk(5, precp)) / 12 Eq = EllipticCurve([R(1), R(0), R(0), tate_a4, tate_a6]) self.__curve = Eq return Eq
class EC(object): def __init__(self, A, C=None): """ Cy² = Cx³ + Ax + Cx """ # Internally, we represent it as a Sage elliptic curve. a = A / C if C is not None else A self.ec = EllipticCurve([0, a._sage(), 0, 1, 0]) def j(self): j = self.ec.j_invariant() return GF(j.parent().characteristic(), *j.polynomial().list()) def point(self, x): return Point(self, x) def four_isogeny(self, gen): pass def three_isogeny(self, gen): pass def secret_isogeny(self, gen): pass def __repr__(self): return repr(self.ec) def _sage(self): return self.ec
def _find_scaling_period(self): r""" Uses the integral period map of the modular symbol implementation in sage in order to determine the scaling. The resulting modular symbol is correct only for the `X_0`-optimal curve, at least up to a possible factor +-1 or +-2. EXAMPLES:: sage: E = EllipticCurve('11a1') sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period') sage: m._e (1/5, 1) sage: E = EllipticCurve('11a2') sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period') sage: m._e (1, 5) sage: E = EllipticCurve('121b2') sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period') sage: m._e (0, 11/2, 0, 11/2, 11/2, 0, 0, -3, 2, 1/2, -1, 3/2) """ P = self._modsym.integral_period_mapping() self._e = P.matrix().transpose().row(0) self._e /= 2 E = self._E try : crla = parse_cremona_label(E.label()) except RuntimeError: # raised when curve is outside of the table self._scaling = 1 else : cr0 = Integer(crla[0]).str() + crla[1] + '1' E0 = EllipticCurve(cr0) if self._sign == 1: q = E0.period_lattice().basis()[0]/E.period_lattice().basis()[0] else: q = E0.period_lattice().basis()[1].imag()/E.period_lattice().basis()[1].imag() if E0.real_components() == 1: q *= 2 if E.real_components() == 1: q /= 2 q = QQ(int(round(q*200)))/200 verbose('scale modular symbols by %s'%q) self._scaling = q self._e *= self._scaling
def __scale_by_periods_only__(self): r""" If we fail to scale with ``_find_scaling_L_ratio``, we drop here to try and find the scaling by the quotient of the periods to the `X_0`-optimal curve. The resulting ``_scaling`` is not guaranteed to be correct, but could well be. EXAMPLES:: sage: E = EllipticCurve('19a1') sage: m = E.modular_symbol(sign=+1) sage: m.__scale_by_periods_only__() Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by -1, 2 or -2. sage: m._scaling 1 sage: E = EllipticCurve('19a2') sage: m = E.modular_symbol(sign=+1) sage: m._scaling 3/2 sage: m.__scale_by_periods_only__() Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by -1, 2 or -2. sage: m._scaling 3 """ # we only do this inside the cremona-tables. try : crla = parse_cremona_label(self._E.label()) except RuntimeError: # raised when curve is outside of the table print("Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by a rational number.") self._scaling = 1 else : print("Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by -1, 2 or -2.") cr0 = Integer(crla[0]).str() + crla[1] + '1' E0 = EllipticCurve(cr0) if self._sign == 1: q = E0.period_lattice().basis()[0]/self._E.period_lattice().basis()[0] else: q = E0.period_lattice().basis()[1].imag()/self._E.period_lattice().basis()[1].imag() if E0.real_components() == 1: q *= 2 if self._E.real_components() == 1: q /= 2 q = ZZ(int(round(q*200)))/200 verbose('scale modular symbols by %s'%q) self._scaling = q
def weierstrass(self) : ''' INPUT: OUTPUT: * E the elliptic curve in Weierstrass model (y^2 = x^3+a*x+b) ''' return EllipticCurve(self.Fp2, [1-(self.a**2)/3, self.a*(2*(self.a**2)/9-1)/3])
def getcoords(E, u, prec=20, R=None): if R is None: R = u.parent() u = R(u) p = R.prime() jE = E.j_invariant() # Calculate the Tate parameter E4 = EisensteinForms(weight=4).basis()[0] Delta = CuspForms(weight=12).basis()[0] j = (E4.q_expansion(prec + 7))**3 / Delta.q_expansion(prec + 7) qE = (1 / j).power_series().reversion()(R(1 / jE)) # Normalize the period by appropriate powers of qE un = u * qE**(-(u.valuation() / qE.valuation()).floor()) precn = (prec / qE.valuation()).floor() + 4 # formulas in Silverman II (Advanced Topics in the Arithmetic of Elliptic curves, p. 425) xx = un / (1 - un)**2 + sum([ qE**n * un / (1 - qE**n * un)**2 + qE**n / un / (1 - qE**n / un)**2 - 2 * qE**n / (1 - qE**n)**2 for n in range(1, precn) ]) yy = un**2 / (1 - un)**3 + sum([ qE**(2 * n) * un**2 / (1 - qE**n * un)**3 - qE**n / un / (1 - qE**n / un)**3 + qE**n / (1 - qE**n)**2 for n in range(1, precn) ]) sk = lambda q, k, pprec: sum( [n**k * q**n / (1 - q**n) for n in range(1, pprec + 1)]) n = qE.valuation() precp = ((prec + 4) / n).floor() + 2 tate_a4 = -5 * sk(qE, 3, precp) tate_a6 = (tate_a4 - 7 * sk(qE, 5, precp)) / 12 Eq = EllipticCurve([R(1), R(0), R(0), tate_a4, tate_a6]) C2 = (Eq.c4() * E.c6()) / (Eq.c6() * E.c4()) C = R(C2).square_root() s = (C - R(E.a1())) / R(2) r = (s * (C - s) - R(E.a2())) / 3 t = (r * (2 * s - C) - R(E.a3())) / 2 return (r + C2 * xx, t + s * C2 * xx + C * C2 * yy)
def reduction(E: EllipticCurve_number_field, p: NumberFieldFractionalIdeal): if not E.has_good_reduction(p): raise ValueError(f"The curve must have good reduction at the place.") Ep = E.local_data(p).minimal_model() K = E.base_ring() Fp = K.residue_field(p) Fp2 = GF(Fp.order(), "abar", modulus=Fp.modulus()) iso = Fp.hom([Fp2.gen(0)], Fp2) Ebar = EllipticCurve([iso(Fp(ai)) for ai in Ep.ainvs()]) return Ebar
def EC_group_new(p, a_bin, b_bin, xG_bin, yG_bin, h): F = GF(p) a, b = F(a_bin), F(b_bin) C = EllipticCurve(F, [a, b]) # create base point xG, yG = F(xG_bin), F(yG_bin) G = C(xG, yG) n = G.order() return (p, C, n, h, G)
def change_iso_curve(self, a): """ INPUT: * a the montgomery a coefficient of the target curve OUTPUT: * the point P seen in the new curve """ a1 = self.curve.Fp2(self.curve.a) if (a**2 - 4) * (a1**2 - 3)**3 != (a1**2 - 4) * (a**2 - 3)**3: # i.e if 256*(a1**2 - 3)**3/(a1**2 - 4) != 256*(a**2 - 3)**3/(a**2 - 4) raise RuntimeError('the curves are not isomorphic.') E_a1 = EllipticCurve(self.curve.Fp2, [0, a1, 0, 1, 0]) E_a = EllipticCurve(self.curve.Fp2, [0, a, 0, 1, 0]) xP = self.x / self.z yP = sqrt(xP**3 + a1 * xP**2 + xP) P_ws = E_a1(xP, yP) iso = E_a1.isomorphism_to(E_a) curve_target = copy(self.curve) curve_target.a = a return Point(iso(P_ws)[0], 1, curve_target)
def __init__(self, Elabel, modulus, number): """ tensor product of an elliptic curve E over Q and a Dirichlet character chi given by a modulus and a number. (more to come) """ self.modulus = modulus self.number = number self.Elabel = Elabel C = lmfdb.base.getDBConnection() Edata = C.elliptic_curves.curves.find_one({'lmfdb_label': Elabel}) if Edata is None: raise ValueError ainvs = [int(a) for a in Edata['ainvs']] E = EllipticCurve(ainvs) cremona_label = Edata['label'] lmfdb_label = Edata['lmfdb_label'] args = {'type':'Dirichlet', 'modulus':modulus, 'number':number} chi = WebDirichletCharacter(**args) chi = chi.H[number] #chi_sage = chi.chi.sage_character().primitive_character() chi = chi.primitive_character() NE = E.conductor() Nchi = chi.conductor() # test hypothesis if not NE.gcd(Nchi ** 2).is_squarefree(): raise ValueError self.E = E self.chi = chi self.NE = NE self.Nchi = Nchi self.N = self.conductor()
def curve(self, prec=20): r""" Return the `p`-adic elliptic curve of the form `y^2+x y = x^3 + s_4 x+s_6`. This curve with split multiplicative reduction is isomorphic to the given curve over the algebraic closure of `\QQ_p`. INPUT: - ``prec`` -- the `p`-adic precision, default is 20. EXAMPLES:: sage: eq = EllipticCurve('130a1').tate_curve(5) sage: eq.curve(prec=5) Elliptic Curve defined by y^2 + (1+O(5^5))*x*y = x^3 + (2*5^4+5^5+2*5^6+5^7+3*5^8+O(5^9))*x + (2*5^3+5^4+2*5^5+5^7+O(5^8)) over 5-adic Field with capped relative precision 5 """ Eq = getattr(self, "__curve", None) if Eq and Eq.a6().precision_relative() >= prec: return Eq.change_ring(Qp(self._p, prec)) qE = self.parameter(prec=prec) precp = prec + 2 tate_a4 = -5 * self.__sk(3, precp) tate_a6 = (tate_a4 - 7 * self.__sk(5, precp)) / 12 R = qE.parent() Eq = EllipticCurve( [R.one(), R.zero(), R.zero(), R(tate_a4), R(tate_a6)]) self.__curve = Eq return Eq
def galois_rep_from_path(p): C = getDBConnection() if p[0] == 'EllipticCurve': # create the sage elliptic curve then create Galois rep object data = C.elliptic_curves.curves.find_one({'lmfdb_label': p[2]}) ainvs = [int(a) for a in data['ainvs']] E = EllipticCurve(ainvs) return GaloisRepresentation(E) elif (p[0] == 'Character' and p[1] == 'Dirichlet'): dirichletArgs = { 'type': 'Dirichlet', 'modulus': int(p[2]), 'number': int(p[3]) } chi = WebDirichletCharacter(**dirichletArgs) return GaloisRepresentation(chi) elif (p[0] == 'ModularForm'): N = int(p[4]) k = int(p[5]) chi = p[6] # this should be zero; TODO check this is the case label = p[7] # this is a, b, c, etc.; chooses the galois orbit embedding = p[8] # this is the embedding of that galois orbit form = WebNewForm(N, k, chi=chi, label=label) return GaloisRepresentation([form, ZZ(embedding)]) elif (p[0] == 'ArtinRepresentation'): dim = p[1] conductor = p[2] index = p[3] rho = ArtinRepresentation(dim, conductor, index) return GaloisRepresentation(rho) else: return
def compareXWithWeierstrass(self, other): ''' INPUT: * other a point of an elliptic curve of SageMath object EllipticCurve OUTPUT: * True of False if self corresponds to a point on a Weierstrass curve with the same x-coordinate as other ''' field = self.curve.Fp2 selfW = self.weierstrass() if selfW.curve() != other.curve(): # we are in an isomorphic curve # let's move to the right one a1, b1 = selfW.curve().a4(), selfW.curve().a6() C2 = EllipticCurve(field, [ field(other.curve().a4().polynomial().list()), field(other.curve().a6().polynomial().list()) ]) iso = WeierstrassIsomorphism(E=selfW.curve(), F=C2) selfW = iso(selfW) x = field(selfW[0].polynomial().list()) z = field(selfW[2].polynomial().list()) X = field(other[0].polynomial().list()) Z = field(other[2].polynomial().list()) return x * Z == z * X
def two_selmer(self,curves,rank=True,reduced=False, output_filename="rank_output.txt",problems_filename="rank_problems.txt",\ return_data=True,print_timing=True): r""" Compute rank or size of two-Selmer for a list of curves ordered by height. INPUT: - ``curves`` -- A list of height/a-invariant tuples of curves, as returned by the coefficients_over_height_range() method. Each tuple is of the form (H, [a1,a2,a3,a4,a6]) where H is the height of the curve, and [a1,...,a6] the curve's a-invariants - ``rank`` -- (Default True) Compute the rank versus size of the curve's 2-Selmer group. If True, rank is computed; if set to False size (i.e. 2^rank) is computed instead. - ``reduced`` -- (Default False) Compute full 2-Selmer or reduced 2-Selmer. If True, full 2-Selmer is computed; if False, the reduced group rank/size (i.e. 2-Selmer rank - 2-torsion rank or 2^(2-Selmer rank - 2-torsion rank) as per whether 'rank' is set to True or False) is computed. - ``output_filename`` -- String, the name of the file to which the output will be saved. Each line of the save file describes a single curve, and consists of seven tab-separated integers: the first is the height of the curve; the following five are the curve's a-invariants, and the final integer is the curve's rank. - ``problems_filename`` -- String: the file name to which problem curves will be written. These are curves for which rank could not be computed. Write format is the same as above, except rank is omitted at the end. - ``return_data`` -- (Default True): If set to False, the data is not returned at the end of computation; only written to file. - ``print_timing`` -- (Default True): If set to False, wall time of total computation will not be printed. OUTPUT: - Writes data to file. Each line of the written file consists of seven tab separated entries of the form H, a1, a2, a3, a4, a6, d H: The curve's height a1,...,a6: The curve's a-invariants d: The computed datum for that curve - (only if return_data==True) A list consisting of two lists: The first is a list of triples of the form (H, (a1,a2,a3,a4,a6), d) where the entries are as above. The second is a list of curve for which the datum could not be provably computed; each entry of this list is just a pair consisting of height and a-invariants. EXAMPLES:: sage: from sage.schemes.elliptic_curves.curve_enumerator import * sage: C = CurveEnumerator(family="short_weierstrass") sage: L = C.coefficients_over_height_range(0,4) sage: R = C.two_selmer(L,rank=True,return_data=True,print_timing=False) sage: R[1] [] sage: for r in R[0]: print(r) ....: (4, [0, 0, 0, -1, -2], 1) (4, [0, 0, 0, -1, 2], 0) (4, [0, 0, 0, 0, -2], 1) (4, [0, 0, 0, 0, 2], 1) (4, [0, 0, 0, 1, -2], 1) (4, [0, 0, 0, 1, 2], 1) sage: R = C.two_selmer(L,rank=False,return_data=True,print_timing=False) sage: for r in R[0]: print(r) ....: (4, [0, 0, 0, -1, -2], 2) (4, [0, 0, 0, -1, 2], 1) (4, [0, 0, 0, 0, -2], 2) (4, [0, 0, 0, 0, 2], 2) (4, [0, 0, 0, 1, -2], 2) (4, [0, 0, 0, 1, 2], 2) sage: R = C.two_selmer(L,reduced=True,print_timing=False) sage: for r in R[0]: print(r) ....: (4, [0, 0, 0, -1, -2], 1) (4, [0, 0, 0, -1, 2], 0) (4, [0, 0, 0, 0, -2], 1) (4, [0, 0, 0, 0, 2], 1) (4, [0, 0, 0, 1, -2], 0) (4, [0, 0, 0, 1, 2], 0) sage: R = C.two_selmer(L,rank=False,reduced=True,print_timing=False) sage: for r in R[0]: print(r) ....: (4, [0, 0, 0, -1, -2], 2) (4, [0, 0, 0, -1, 2], 1) (4, [0, 0, 0, 0, -2], 2) (4, [0, 0, 0, 0, 2], 2) (4, [0, 0, 0, 1, -2], 1) (4, [0, 0, 0, 1, 2], 1) """ if print_timing: t = time.time() out_file = open(output_filename, "w") prob_file = open(problems_filename, "w") if return_data: output = [] problems = [] for C in curves: # Attempt to compute datum and write curve+datum to file try: E = EllipticCurve(C[1]) d = E.selmer_rank() if reduced: d -= E.two_torsion_rank() if not rank: d = 2**d out_file.write(str(C[0]) + "\t") for a in C[1]: out_file.write(str(a) + "\t") out_file.write(str(d) + "\n") out_file.flush() if return_data: output.append((C[0], C[1], d)) # Write to problem file if fail except: prob_file.write(str(C[0]) + "\t") for a in C[1]: prob_file.write(str(a) + "\t") prob_file.write("\n") prob_file.flush() if return_data: problems.append(C) out_file.close() prob_file.close() if print_timing: print(time.time() - t) if return_data: return output, problems
def rank(self,curves,output_filename="rank_output.txt",\ problems_filename="rank_problems.txt",\ return_data=True,use_database=True,use_only_mwrank=False,\ print_timing=True): r""" Compute the algebraic rank for a list of curves ordered by height. INPUT: - ``curves`` -- A list of height/a-invariant tuples of curves, as returned by the coefficients_over_height_range() method Each tuple is of the form (H, [a1,a2,a3,a4,a6]) where H is the height of the curve, and [a1,...,a6] the curve's a-invariants - ``output_filename`` -- String, the name of the file to which the output will be saved. Each line of the save file describes a single curve, and consists of seven tab-separated integers: the first is the height of the curve; the following five are the curve's a-invariants, and the final integer is the curve's rank. - ``problems_filename`` -- String: the file name to which problem curves will be written. These are curves for which rank could not be computed. Write format is the same as above, except rank is omitted at the end. - ``return_data`` -- (Default True): If set to False, the data is not returned at the end of computation; only written to file. - ``use_database`` -- (Default True): If set to False, the Cremona database is not used to look up curve ranks. - ``use_only_mwrank`` -- (Default False): If set to True, will not try to use analytic methods to compute rank before consulting the Cremona database - ``print_timing`` -- (Default True): If set to False, wall time of total computation will not be printed. OUTPUT: - Writes data to file. Each line of the written file consists of seven tab separated entries of the form H, a1, a2, a3, a4, a6, d H: The curve's height a1,...,a6: The curve's a-invariants d: The computed datum for that curve - (only if return_data==True) A list consisting of two lists: The first is a list of triples of the form (H, (a1,a2,a3,a4,a6), d) where the entries are as above. The second is a list of curve for which the datum could not be provably computed; each entry of this list is just a pair consisting of height and a-invariants. EXAMPLES:: sage: from sage.schemes.elliptic_curves.curve_enumerator import * sage: C = CurveEnumerator(family="short_weierstrass") sage: L = C.coefficients_over_height_range(0,4) sage: R = C.rank(L,return_data=True,print_timing=False) sage: R[1] [] sage: for r in R[0]: print(r) ....: (1, [0, 0, 0, -1, 0], 0) (1, [0, 0, 0, 1, 0], 0) (1, [0, 0, 0, 0, -1], 0) (1, [0, 0, 0, 0, 1], 0) (1, [0, 0, 0, -1, -1], 0) (1, [0, 0, 0, -1, 1], 1) (1, [0, 0, 0, 1, -1], 1) (1, [0, 0, 0, 1, 1], 1) (4, [0, 0, 0, -1, -2], 1) (4, [0, 0, 0, -1, 2], 0) (4, [0, 0, 0, 0, -2], 1) (4, [0, 0, 0, 0, 2], 1) (4, [0, 0, 0, 1, -2], 0) (4, [0, 0, 0, 1, 2], 0) """ if print_timing: t = time.time() out_file = open(output_filename, "w") prob_file = open(problems_filename, "w") if return_data: output = [] problems = [] for C in curves: # Attempt to compute rank and write curve+rank to file try: E = EllipticCurve(C[1]) d = E.rank(use_database=use_database, only_use_mwrank=use_only_mwrank) out_file.write(str(C[0]) + "\t") for a in C[1]: out_file.write(str(a) + "\t") out_file.write(str(d) + "\n") out_file.flush() if return_data: output.append((C[0], C[1], d)) # Write to problem file if fail except: prob_file.write(str(C[0]) + "\t") for a in C[1]: prob_file.write(str(a) + "\t") prob_file.write("\n") prob_file.flush() if return_data: problems.append(C) out_file.close() prob_file.close() if print_timing: print(time.time() - t) if return_data: return output, problems
def is_Q_curve(E, maxp=100, certificate=False, verbose=False): r""" Return whether ``E`` is a `\QQ`-curve, with optional certificate. INPUT: - ``E`` (elliptic curve) -- an elliptic curve over a number field. - ``maxp`` (int, 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`` (bool, 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 non-negative; 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: 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} A non-`\QQ`-curve over a quartic field. The local data at bad primes above `3` is inconsistent:: 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])) sage: E = EllipticCurve([K([-3,-4,1,1]),K([4,-1,-1,0]),K([-2,0,1,0]),K([-621,778,138,-178]),K([9509,2046,-24728,10380])]) sage: is_Q_curve(E, certificate=True, verbose=True) 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: K.<a> = NumberField(R([-10, 0, 1])) sage: E = EllipticCurve([K([0,1]),K([-1,-1]),K([0,0]),K([-236,40]),K([-1840,464])]) sage: is_Q_curve(E, certificate=True, verbose=True) 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: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = NumberField(R([-1, -1, 1])) sage: E = EllipticCurve([K([1,0]),K([-1,0]),K([0,1]),K([0,-2]),K([0,1])]) sage: is_Q_curve(E, certificate=True, verbose=True) 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: 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} 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: 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} """ from sage.rings.number_field.number_field_base import is_NumberField if verbose: print("Checking whether {} is a Q-curve".format(E)) try: assert is_NumberField(E.base_field()) except (AttributeError, AssertionError): raise TypeError( "{} must be an elliptic curve defined over a number field in is_Q_curve()" ) from sage.rings.integer_ring import ZZ from sage.arith.functions import lcm from sage.libs.pari import pari from sage.rings.number_field.number_field import NumberField from sage.schemes.elliptic_curves.constructor import EllipticCurve from sage.schemes.elliptic_curves.cm import cm_j_invariants_and_orders, is_cm_j_invariant # Step 1 # all curves with rational j-invariant are Q-curves: jE = E.j_invariant() if jE in QQ: if verbose: print("Yes: j(E) is in QQ") if certificate: # test for CM for d, f, j in cm_j_invariants_and_orders(QQ): if jE == j: return True, {'CM': d * f**2} # else not CM return True, { 'CM': ZZ(0), 'r': ZZ(0), 'rho': ZZ(0), 'N': ZZ(1), 'core_poly': polygen(QQ) } else: return True # CM curves are Q-curves: flag, df = is_cm_j_invariant(jE) if flag: d, f = df D = d * f**2 if verbose: print("Yes: E is CM (discriminant {})".format(D)) if certificate: return True, {'CM': D} else: return True # Step 2: replace E by a curve defined over Q(j(E)): K = E.base_field() jpoly = jE.minpoly() if jpoly.degree() < K.degree(): if verbose: print("switching to smaller base field: j's minpoly is {}".format( jpoly)) f = pari(jpoly).polredbest().sage({'x': jpoly.parent().gen()}) K2 = NumberField(f, 'b') jE = jpoly.roots(K2)[0][0] if verbose: print("New j is {} over {}, with minpoly {}".format( jE, K2, jE.minpoly())) #assert jE.minpoly()==jpoly E = EllipticCurve(j=jE) K = K2 if verbose: print("New test curve is {}".format(E)) # Step 3: check primes of bad reduction NN = E.conductor().norm() for p in NN.support(): Plist = K.primes_above(p) if len(Plist) < 2: continue # pot_mult = potential multiplicative reduction pot_mult = [jE.valuation(P) < 0 for P in Plist] consistent = all(pot_mult) or not any(pot_mult) if not consistent: if verbose: print("No: inconsistency at the {} primes dividing {}".format( len(Plist), p)) print(" - potentially multiplicative: {}".format(pot_mult)) if certificate: return False, p else: return False # Step 4 check: primes P of good reduction above p<=B: if verbose: print("Applying local tests at good primes above p<={}".format(maxp)) res4, p = Step4Test(E, B=maxp, oldB=0, verbose=verbose) if not res4: if verbose: print("No: local test at p={} failed".format(p)) if certificate: return False, p else: return False if verbose: print("...all local tests pass for p<={}".format(maxp)) # Step 5: compute the (partial) K-isogeny class of E and test the # set of j-invariants in the class: C = E.isogeny_class(algorithm='heuristic', minimal_models=False) jC = [E2.j_invariant() for E2 in C] centrejpols = conjugacy_test(jC, verbose=verbose) if centrejpols: if verbose: print( "Yes: the isogeny class contains a complete conjugacy class of j-invariants" ) if certificate: for f in centrejpols: rho = f.degree().valuation(2) centre_indices = [i for i, j in enumerate(jC) if f(j) == 0] M = C.matrix() core_degs = [M[centre_indices[0], i] for i in centre_indices] level = lcm(core_degs) if level.is_squarefree(): r = len(level.prime_divisors()) cert = { 'CM': ZZ(0), 'core_poly': f, 'rho': rho, 'r': r, 'N': level, 'core_degs': core_degs } return True, cert print("No central curve found") else: return True # Now we are undecided. This can happen if either (1) E is not a # Q-curve but we did not use enough primes in Step 4 to detect # this, or (2) E is a Q-curve but in Step 5 we did not compute the # complete isogeny class. Case (2) is most unlikely since the # heuristic bound used in computing isogeny classes means that we # have all isogenous curves linked to E by an isogeny of degree # supported on primes<1000. # We first rerun Step 4 with a larger bound. xmaxp = 10 * maxp if verbose: print( "Undecided after first round, so we apply more local tests, up to {}" .format(xmaxp)) res4, p = Step4Test(E, B=xmaxp, oldB=maxp, verbose=verbose) if not res4: if verbose: print("No: local test at p={} failed".format(p)) if certificate: return False, p else: return False # Now we rerun Step 5 using a rigorous computation of the complete # isogeny class. This will probably contain no more curves than # before, in which case -- since we already tested that the set of # j-invariants does not contain a complete Galois conjugacy class # -- we can deduce that E is not a Q-curve. if verbose: print("...all local tests pass for p<={}".format(xmaxp)) print("We now compute the complete isogeny class...") Cfull = E.isogeny_class(minimal_models=False) jCfull = [E2.j_invariant() for E2 in Cfull] if len(jC) == len(jCfull): if verbose: print("...and find that we already had the complete class:so No") if certificate: return False, 0 else: return False if verbose: print( "...and find that the class contains {} curves, not just the {} we computed originally" .format(len(jCfull), len(jC))) centrejpols = conjugacy_test(jCfull, verbose=verbose) if cert: if verbose: print( "Yes: the isogeny class contains a complete conjugacy class of j-invariants" ) if certificate: return True, centrejpols else: return True if verbose: print( "No: the isogeny class does *not* contain a complete conjugacy class of j-invariants" ) if certificate: return False, 0 else: return False
def rank(self,curves,output_filename="rank_output.txt",\ problems_filename="rank_problems.txt",\ return_data=True,use_database=True,use_only_mwrank=False,\ print_timing=True): r""" Compute the algebraic rank for a list of curves ordered by height. INPUT: - ``curves`` -- A list of height/a-invariant tuples of curves, as returned by the coefficients_over_height_range() method Each tuple is of the form (H, [a1,a2,a3,a4,a6]) where H is the height of the curve, and [a1,...,a6] the curve's a-invariants - ``output_filename`` -- String, the name of the file to which the output will be saved. Each line of the save file describes a single curve, and consists of seven tab-separated integers: the first is the height of the curve; the following five are the curve's a-invariants, and the final integer is the curve's rank. - ``problems_filename`` -- String: the file name to which problem curves will be written. These are curves for which rank could not be computed. Write format is the same as above, except rank is omitted at the end. - ``return_data`` -- (Default True): If set to False, the data is not returned at the end of computation; only written to file. - ``use_database`` -- (Default True): If set to False, the Cremona database is not used to look up curve ranks. - ``use_only_mwrank`` -- (Default False): If set to True, will not try to use analytic methods to compute rank before consulting the Cremona database - ``print_timing`` -- (Default True): If set to False, wall time of total computation will not be printed. OUTPUT: - Writes data to file. Each line of the written file consists of seven tab separated entries of the form H, a1, a2, a3, a4, a6, d H: The curve's height a1,...,a6: The curve's a-invariants d: The computed datum for that curve - (only if return_data==True) A list consisting of two lists: The first is a list of triples of the form (H, (a1,a2,a3,a4,a6), d) where the entries are as above. The second is a list of curve for which the datum could not be provably computed; each entry of this list is just a pair consisting of height and a-invariants. EXAMPLES:: sage: from sage.schemes.elliptic_curves.curve_enumerator import * sage: C = CurveEnumerator(family="short_weierstrass") sage: L = C.coefficients_over_height_range(0,4) sage: R = C.rank(L,return_data=True,print_timing=False) sage: R[1] [] sage: for r in R[0]: print(r) ....: (1, [0, 0, 0, -1, 0], 0) (1, [0, 0, 0, 1, 0], 0) (1, [0, 0, 0, 0, -1], 0) (1, [0, 0, 0, 0, 1], 0) (1, [0, 0, 0, -1, -1], 0) (1, [0, 0, 0, -1, 1], 1) (1, [0, 0, 0, 1, -1], 1) (1, [0, 0, 0, 1, 1], 1) (4, [0, 0, 0, -1, -2], 1) (4, [0, 0, 0, -1, 2], 0) (4, [0, 0, 0, 0, -2], 1) (4, [0, 0, 0, 0, 2], 1) (4, [0, 0, 0, 1, -2], 0) (4, [0, 0, 0, 1, 2], 0) """ if print_timing: t = time.time() out_file = open(output_filename,"w") prob_file = open(problems_filename,"w") if return_data: output = [] problems = [] for C in curves: # Attempt to compute rank and write curve+rank to file try: E = EllipticCurve(C[1]) d = E.rank(use_database=use_database,only_use_mwrank=use_only_mwrank) out_file.write(str(C[0])+"\t") for a in C[1]: out_file.write(str(a)+"\t") out_file.write(str(d)+"\n") out_file.flush() if return_data: output.append((C[0],C[1],d)) # Write to problem file if fail except: prob_file.write(str(C[0])+"\t") for a in C[1]: prob_file.write(str(a)+"\t") prob_file.write("\n") prob_file.flush() if return_data: problems.append(C) out_file.close() prob_file.close() if print_timing: print(time.time()-t) if return_data: return output,problems
def two_selmer(self,curves,rank=True,reduced=False, output_filename="rank_output.txt",problems_filename="rank_problems.txt",\ return_data=True,print_timing=True): r""" Compute rank or size of two-Selmer for a list of curves ordered by height. INPUT: - ``curves`` -- A list of height/a-invariant tuples of curves, as returned by the coefficients_over_height_range() method. Each tuple is of the form (H, [a1,a2,a3,a4,a6]) where H is the height of the curve, and [a1,...,a6] the curve's a-invariants - ``rank`` -- (Default True) Compute the rank versus size of the curve's 2-Selmer group. If True, rank is computed; if set to False size (i.e. 2^rank) is computed instead. - ``reduced`` -- (Default False) Compute full 2-Selmer or reduced 2-Selmer. If True, full 2-Selmer is computed; if False, the reduced group rank/size (i.e. 2-Selmer rank - 2-torsion rank or 2^(2-Selmer rank - 2-torsion rank) as per whether 'rank' is set to True or False) is computed. - ``output_filename`` -- String, the name of the file to which the output will be saved. Each line of the save file describes a single curve, and consists of seven tab-separated integers: the first is the height of the curve; the following five are the curve's a-invariants, and the final integer is the curve's rank. - ``problems_filename`` -- String: the file name to which problem curves will be written. These are curves for which rank could not be computed. Write format is the same as above, except rank is omitted at the end. - ``return_data`` -- (Default True): If set to False, the data is not returned at the end of computation; only written to file. - ``print_timing`` -- (Default True): If set to False, wall time of total computation will not be printed. OUTPUT: - Writes data to file. Each line of the written file consists of seven tab separated entries of the form H, a1, a2, a3, a4, a6, d H: The curve's height a1,...,a6: The curve's a-invariants d: The computed datum for that curve - (only if return_data==True) A list consisting of two lists: The first is a list of triples of the form (H, (a1,a2,a3,a4,a6), d) where the entries are as above. The second is a list of curve for which the datum could not be provably computed; each entry of this list is just a pair consisting of height and a-invariants. EXAMPLES:: sage: from sage.schemes.elliptic_curves.curve_enumerator import * sage: C = CurveEnumerator(family="short_weierstrass") sage: L = C.coefficients_over_height_range(0,4) sage: R = C.two_selmer(L,rank=True,return_data=True,print_timing=False) sage: R[1] [] sage: for r in R[0]: print(r) ....: (4, [0, 0, 0, -1, -2], 1) (4, [0, 0, 0, -1, 2], 0) (4, [0, 0, 0, 0, -2], 1) (4, [0, 0, 0, 0, 2], 1) (4, [0, 0, 0, 1, -2], 1) (4, [0, 0, 0, 1, 2], 1) sage: R = C.two_selmer(L,rank=False,return_data=True,print_timing=False) sage: for r in R[0]: print(r) ....: (4, [0, 0, 0, -1, -2], 2) (4, [0, 0, 0, -1, 2], 1) (4, [0, 0, 0, 0, -2], 2) (4, [0, 0, 0, 0, 2], 2) (4, [0, 0, 0, 1, -2], 2) (4, [0, 0, 0, 1, 2], 2) sage: R = C.two_selmer(L,reduced=True,print_timing=False) sage: for r in R[0]: print(r) ....: (4, [0, 0, 0, -1, -2], 1) (4, [0, 0, 0, -1, 2], 0) (4, [0, 0, 0, 0, -2], 1) (4, [0, 0, 0, 0, 2], 1) (4, [0, 0, 0, 1, -2], 0) (4, [0, 0, 0, 1, 2], 0) sage: R = C.two_selmer(L,rank=False,reduced=True,print_timing=False) sage: for r in R[0]: print(r) ....: (4, [0, 0, 0, -1, -2], 2) (4, [0, 0, 0, -1, 2], 1) (4, [0, 0, 0, 0, -2], 2) (4, [0, 0, 0, 0, 2], 2) (4, [0, 0, 0, 1, -2], 1) (4, [0, 0, 0, 1, 2], 1) """ if print_timing: t = time.time() out_file = open(output_filename,"w") prob_file = open(problems_filename,"w") if return_data: output = [] problems = [] for C in curves: # Attempt to compute datum and write curve+datum to file try: E = EllipticCurve(C[1]) d = E.selmer_rank() if reduced: d -= E.two_torsion_rank() if not rank: d = 2**d out_file.write(str(C[0])+"\t") for a in C[1]: out_file.write(str(a)+"\t") out_file.write(str(d)+"\n") out_file.flush() if return_data: output.append((C[0],C[1],d)) # Write to problem file if fail except: prob_file.write(str(C[0])+"\t") for a in C[1]: prob_file.write(str(a)+"\t") prob_file.write("\n") prob_file.flush() if return_data: problems.append(C) out_file.close() prob_file.close() if print_timing: print(time.time()-t) if return_data: return output,problems
def Jacobian_of_equation(polynomial, variables=None, curve=None): r""" Construct the Jacobian of a genus-one curve given by a polynomial. INPUT: - ``F`` -- a polynomial defining a plane curve of genus one. May be homogeneous or inhomogeneous. - ``variables`` -- list of two or three variables or ``None`` (default). The inhomogeneous or homogeneous coordinates. By default, all variables in the polynomial are used. - ``curve`` -- the genus-one curve defined by ``polynomial`` or # ``None`` (default). If specified, suitable morphism from the jacobian elliptic curve to the curve is returned. OUTPUT: An elliptic curve in short Weierstrass form isomorphic to the curve ``polynomial=0``. If the optional argument ``curve`` is specified, a rational multicover from the Jacobian elliptic curve to the genus-one curve is returned. EXAMPLES:: sage: R.<a,b,c> = QQ[] sage: f = a^3+b^3+60*c^3 sage: Jacobian(f) Elliptic Curve defined by y^2 = x^3 - 24300 over Rational Field sage: Jacobian(f.subs(c=1)) Elliptic Curve defined by y^2 = x^3 - 24300 over Rational Field If we specify the domain curve the birational covering is returned:: sage: h = Jacobian(f, curve=Curve(f)); h Scheme morphism: From: Projective Plane Curve over Rational Field defined by a^3 + b^3 + 60*c^3 To: Elliptic Curve defined by y^2 = x^3 - 24300 over Rational Field Defn: Defined on coordinates by sending (a : b : c) to (-216000*a^4*b^4*c - 12960000*a^4*b*c^4 - 12960000*a*b^4*c^4 : 108000*a^6*b^3 - 108000*a^3*b^6 - 6480000*a^6*c^3 + 6480000*b^6*c^3 + 388800000*a^3*c^6 - 388800000*b^3*c^6 : 216000*a^3*b^3*c^3) sage: h([1,-1,0]) (0 : 1 : 0) Plugging in the polynomials defining `h` allows us to verify that it is indeed a rational morphism to the elliptic curve:: sage: E = h.codomain() sage: E.defining_polynomial()(h.defining_polynomials()).factor() (2519424000000000) * c^3 * b^3 * a^3 * (a^3 + b^3 + 60*c^3) * (a^9*b^6 + a^6*b^9 - 120*a^9*b^3*c^3 + 900*a^6*b^6*c^3 - 120*a^3*b^9*c^3 + 3600*a^9*c^6 + 54000*a^6*b^3*c^6 + 54000*a^3*b^6*c^6 + 3600*b^9*c^6 + 216000*a^6*c^9 - 432000*a^3*b^3*c^9 + 216000*b^6*c^9) By specifying the variables, we can also construct an elliptic curve over a polynomial ring:: sage: R.<u,v,t> = QQ[] sage: Jacobian(u^3+v^3+t, variables=[u,v]) Elliptic Curve defined by y^2 = x^3 + (-27/4*t^2) over Multivariate Polynomial Ring in u, v, t over Rational Field TESTS:: sage: from sage.schemes.elliptic_curves.jacobian import Jacobian_of_equation sage: Jacobian_of_equation(f, variables=[a,b,c]) Elliptic Curve defined by y^2 = x^3 - 24300 over Rational Field """ from sage.schemes.toric.weierstrass import WeierstrassForm f, g = WeierstrassForm(polynomial, variables=variables) try: K = polynomial.base_ring() f = K(f) g = K(g) except (TypeError, ValueError): pass E = EllipticCurve([f, g]) if curve is None: return E X, Y, Z = WeierstrassForm(polynomial, variables=variables, transformation=True) from sage.schemes.elliptic_curves.weierstrass_transform import WeierstrassTransformation return WeierstrassTransformation(curve, E, [X*Z, Y, Z**3], 1)
def find_elliptic_curve(k, K, m_t): ''' INPUT: - ``k`` -- a base field - ``K`` -- an extension of K of degree n. - ``m_t`` -- a list of tuple containing a integer and a set of candidates for the trace. OUTPUT: - An elliptic curve defined over k with the required properties. - An integer case, depending on the value of the j-invariant of said elliptic curve. - An integer m statisfying the properties described in isom_elliptic which we will be using to compute Gaussian periods. ..NOTE:: The case j = 0 or 1728 are not implemented yet. They shall raise NotImplementedError. EXAMPLES: sage: R.<X> = PolynomialRing(GF(5)) sage: f = R.irreducible_element(19, algorithm = 'random') sage: K = GF(5**19, names = 'x', modulus = f) sage: m_t = [(229,{0, 1, 3})] sage: find_elliptic(GF(5), K, m_t) (Elliptic Curve defined by y^2 = x^3 + x + 2 over Finite Field of size 5, 0, 229) ALGORITHM: TODO : Doc is deprecated, to be redone. Function that finds an elliptic curve with the required charateristics, those given in the function isom_elliptic. First, we have to determine if m is composite, a prime power or a power of p, the characteristic of the base field. The first case is not implemented yet. We also note that the m given should satisfies several conditions based on the characteristic and the degree of K. See the docstrings of isom_elliptic for more information. - If m is a power of p, the charateristic of the base field k, then we shall proceed as follow : We pick a random curve E/k and we set down t = Tr_k(Fr_E), for the curve to be what we want, we need : - t not zero, - (Z/m)* = <t> x S or #<t> = n - m divides #E/K but not #E/L, for any intermediate extension L of K/k; so we can construct points of order m such that their abscissas or ordinates span exactly K. Or that we haven't any point of order m in any sub-extension. Then we test those conditions for both E and its quadratic twist, if one of them meet the requirements, we return it and its trace. If no elliptic curves are found an error is returned. - If m is primer power, then we shall proceed as follow : We have m = l^r, for l a prime. For this method to work, we need l to be an Elkies prime. A prime l is an Elkies prime for an elliptic curve if the charateristic polynomial of the aforesaid elliptic curve splits in GF(l). For now, we pick a random curve E/k and for it to work, if we set down t = Tr_k(Fr_E), we need the following : - We have x**2 - tx + q = (x - a)(x - b) mod m, meaning the polynomial splits in Z/m, - (Z/m)* = <a> x S, with #<a> = n, - ord_m(a) < ord_m(b), - m divides #E/K but not #E/L, for any intermediate extension L of K/k; so we can construct points of order m such that their abscissas or ordinates span exactly K. Once again, we test all that for both E and its quadratic twist; if one them meet the requirements, we return it, its trace and a tuple containing the root a and t mod m. If none are found, there is something wrong. - If m is composite, TODO. ''' p = k.characteristic() q = k.cardinality() n = K.degree() m = m_t[0] S_t = m_t[1] compteur = 0 #We start by the special cases j = 1728, 0 E_j1728 = EllipticCurve(j = k(1728)) if q%4 != 1: # If q != 1 mod 4, then there's no 4th root of unity, then magically # all the quartic twist are already in k and the trace is 0. We just # have to test the only curve y² = x³ + x. compteur += 1 if 0 in S_t: return E_j1728, 0, compteur else: # If q = 1 mod 4, then the trace is not 0, and we have to try four # trace to see which is the best candidate. g = k.unit_gens()[0] c = g**((q-1)/4) t = E_j1728.trace_of_frobenius() L = [(t*(c**i).lift(), g**i) for i in range(4)] for i in range(4): compteur += 1 if Integers(m)(L[i][0]) in S_t: # E, case, t return E_j1728.quartic_twist(L[i][1]), 1, compteur E_j0 = EllipticCurve(j = k(0)) if q%6 != 1: # Same as before, if q != 1 mod 6, there's no 6th root of unity in # GF(q) and the trace is 0 (that's pretty quick reasoning.. :D). # Justification will come later. compteur += 1 if 0 in S_t: return E_j0, 0, compteur else: g = k.unit_gens()[0] c = g**((q-1)/6) t = E_j0.trace_of_frobenius() L = [(t*(c**i).lift(), g**i) for i in range(6)] for l in L: if Integers(m)(l[0]) in S_t: return E_j0.sextic_twist(l[1]), 2, compteur # General case for j in k: if j == 0 or j == k(1728): continue E = EllipticCurve(j = j) t = E.trace_of_frobenius() L = [(t, E), (-t, E.quadratic_twist())] for l in L: compteur +=1 if Integers(m)(l[0]) in S_t: return l[1], 0, compteur # If no elliptic curve has been found. return None, -1
def find_elliptic_curve(k, K, m_t): ''' INPUT: - ``k`` -- a base field, - ``K`` -- an extension of K of degree n, - ``m_t`` -- a list of tuple containing an integer and a set of candidates for the trace. OUTPUT: - An elliptic curve defined over k with the required properties. - An integer case, depending on the value of the j-invariant or the the supersingularity of said elliptic curve. EXAMPLES: sage: R.<X> = PolynomialRing(GF(5)) sage: f = R.irreducible_element(19, algorithm = 'random') sage: K = GF(5**19, names = 'x', modulus = f) sage: m_t = (229,{2}) sage: find_elliptic_curve(GF(5), K, m_t) (Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 5, 1) ALGORITHM: The goal is to pick an elliptic curve of which the trace of its Frobenius t is among the right class modulo m, the ones in S_t. We do that in order to have point of order m only on E/GF(q^n) or above, since then the abscissas of a point of order m will span GF(q^n) and we'll be able to compute the elliptic periods as it was planned. We start by looking at the two special cases j = 1728 or 0. If q is not 1 modulo 4 and 3 respectively, then the curves are supersingular (t = 0) and if 0 is among the good traces, they are to be treated like the other curves. If for j = 0 we have q = 1 mod 3, then we have to tests E(j = 0) and all of its sextic twists. Once again if t is in S_t, then we return the right curves and the case 2 to compute the periods accordingly. If for j = 1728 we have q = 1 mod 4, then we have to tests E(j = 1728) and all of its quartic twists. If one the trace is in S_t, we return the right curves and the case 1. If j != 0 and 1728, then we tests all the elements of GF(q) to find the right curve. For each j we test if t or -t is in S_t and return the curve accordingly plus the case 0. If no curves are found, we return None and the case -1, which will raise an runtimeError in the main function. ''' p = k.characteristic() q = k.cardinality() m = m_t[0] S_t = m_t[1] #We start by the special cases j = 1728, 0 E_j1728 = EllipticCurve(j = k(1728)) if q%4 != 1: # If q != 1 mod 4, then there's no 4th root of unity, then magically # all the quartic twist are already in k and the trace is 0. We just # have to test the only curve y� = x� + x. if 0 in S_t: return E_j1728, 0 else: # If q = 1 mod 4, then the trace is not 0, and we have to try four # trace to see which is the best candidate. g = k.unit_gens()[0] c = g**((q-1)/4) t = E_j1728.trace_of_frobenius() L = [(t*(c**i).centerlift(), g**i) for i in range(4)] for i in range(4): if Integers(m)(L[i][0]) in S_t: # E, case, t return E_j1728.quartic_twist(L[i][1]), 1 E_j0 = EllipticCurve(j = k(0)) if q%3 != 1: # Same as before, if q != 1 mod 6, there's no 6th root of unity in # GF(q) and the trace is 0 (that's pretty quick reasoning.. :D). # Justification will come later. Since q = 1 mod 2, if q = 1 mod 3 # then q = 1 mod 6. if 0 in S_t: return E_j0, 0 else: g = k.unit_gens()[0] c = g**((q-1)/6) t = E_j0.trace_of_frobenius() L = [(t*(c**i).centerlift(), g**i) for i in range(6)] for l in L: if Integers(m)(l[0]) in S_t: return E_j0.sextic_twist(l[1]), 2 # General case for j in k: if j == 0 or j == k(1728): continue E = EllipticCurve(j = j) t = E.trace_of_frobenius() L = [(t, E), (-t, E.quadratic_twist())] for l in L: if Integers(m)(l[0]) in S_t: return l[1], 0 # If no elliptic curve has been found. return None, -1
def _find_scaling_period(self): r""" Uses the integral period map of the modular symbol implementation in sage in order to determine the scaling. The resulting modular symbol is correct only for the `X_0`-optimal curve, at least up to a possible factor +- a power of 2. EXAMPLES:: sage: E = EllipticCurve('11a1') sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period') sage: m._e (1/5, 1/2) sage: E = EllipticCurve('11a2') sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period') sage: m._e (1, 5/2) sage: E = EllipticCurve('121b2') sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period') sage: m._e (0, 0, 0, 11/2, 11/2, 11/2, 11/2, -3, 3/2, 1/2, -1, 2) TESTS:: sage: E = EllipticCurve('19a1') sage: m = E.modular_symbol(sign=+1, implementation='sage', normalize='none') sage: m._find_scaling_period() sage: m._scaling 1 sage: E = EllipticCurve('19a2') sage: m = E.modular_symbol(sign=+1, implementation='sage', normalize='none') sage: m._scaling 1 sage: m._find_scaling_period() sage: m._scaling 3 """ P = self._modsym.integral_period_mapping() self._e = P.matrix().transpose().row(0) self._e /= 2 E = self._E try: crla = parse_cremona_label(E.label()) except RuntimeError: # raised when curve is outside of the table print( "Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by a rational number." ) self._scaling = 1 else: cr0 = Integer(crla[0]).str() + crla[1] + '1' E0 = EllipticCurve(cr0) if self._sign == 1: q = E0.period_lattice().basis()[0] / E.period_lattice().basis( )[0] else: q = E0.period_lattice().basis()[1].imag() / E.period_lattice( ).basis()[1].imag() if E0.real_components() == 1: q *= 2 if E.real_components() == 1: q /= 2 q = QQ((q * 200).round()) / 200 verbose('scale modular symbols by %s' % q) self._scaling = q c = self(0) # required, to change base point from oo to 0 if c < 0: c *= -1 self._scaling *= -1 self._at_zero = c self._e *= self._scaling
def _find_scaling_period(self): r""" Uses the integral period map of the modular symbol implementation in sage in order to determine the scaling. The resulting modular symbol is correct only for the `X_0`-optimal curve, at least up to a possible factor +- a power of 2. EXAMPLES:: sage: E = EllipticCurve('11a1') sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period') sage: m._e (1/5, 1) sage: E = EllipticCurve('11a2') sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period') sage: m._e (1, 5) sage: E = EllipticCurve('121b2') sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period') sage: m._e (0, 11/2, 0, 11/2, 11/2, 0, 0, -3, 2, 1/2, -1, 3/2) TESTS:: sage: E = EllipticCurve('19a1') sage: m = E.modular_symbol(sign=+1, implementation='sage', normalize='none') sage: m._find_scaling_period() sage: m._scaling 1 sage: E = EllipticCurve('19a2') sage: m = E.modular_symbol(sign=+1, implementation='sage', normalize='none') sage: m._scaling 1 sage: m._find_scaling_period() sage: m._scaling 3 """ P = self._modsym.integral_period_mapping() self._e = P.matrix().transpose().row(0) self._e /= 2 E = self._E try : crla = parse_cremona_label(E.label()) except RuntimeError: # raised when curve is outside of the table print("Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by a rational number.") self._scaling = 1 else : cr0 = Integer(crla[0]).str() + crla[1] + '1' E0 = EllipticCurve(cr0) if self._sign == 1: q = E0.period_lattice().basis()[0]/E.period_lattice().basis()[0] else: q = E0.period_lattice().basis()[1].imag()/E.period_lattice().basis()[1].imag() if E0.real_components() == 1: q *= 2 if E.real_components() == 1: q /= 2 q = QQ(int(round(q*200)))/200 verbose('scale modular symbols by %s'%q) self._scaling = q c = self(0) # required, to change base point from oo to 0 if c<0: c *= -1 self._scaling *= -1 self._at_zero = c self._e *= self._scaling
def torsion_bound(E, number_of_places=20): r""" Return an upper bound on the order of the torsion subgroup. INPUT: - ``E`` -- an elliptic curve over `\QQ` or a number field - ``number_of_places`` (positive integer, default = 20) -- the number of places that will be used to find the bound OUTPUT: (integer) An upper bound on the torsion order. ALGORITHM: An upper bound on the order of the torsion group of the elliptic curve is obtained by counting points modulo several primes of good reduction. Note that the upper bound returned by this function is a multiple of the order of the torsion group, and in general will be greater than the order. To avoid nontrivial arithmetic in the base field (in particular, to avoid having to compute the maximal order) we only use prime `P` above rational primes `p` which do not divide the discriminant of the equation order. EXAMPLES:: sage: CDB = CremonaDatabase() sage: from sage.schemes.elliptic_curves.ell_torsion import torsion_bound sage: [torsion_bound(E) for E in CDB.iter([14])] [6, 6, 6, 6, 6, 6] sage: [E.torsion_order() for E in CDB.iter([14])] [6, 6, 2, 6, 2, 6] An example over a relative number field (see :trac:`16011`):: sage: R.<x> = QQ[] sage: F.<a> = QuadraticField(5) sage: K.<b> = F.extension(x^2-3) sage: E = EllipticCurve(K,[0,0,0,b,1]) sage: E.torsion_subgroup().order() 1 An example of a base-change curve from `\QQ` to a degree 16 field:: sage: from sage.schemes.elliptic_curves.ell_torsion import torsion_bound sage: f = PolynomialRing(QQ,'x')([5643417737593488384,0, ....: -11114515801179776,0,-455989850911004,0,379781901872, ....: 0,14339154953,0,-1564048,0,-194542,0,-32,0,1]) sage: K = NumberField(f,'a') sage: E = EllipticCurve(K, [1, -1, 1, 824579, 245512517]) sage: torsion_bound(E) 16 sage: E.torsion_subgroup().invariants() (4, 4) """ from sage.rings.all import ZZ, GF from sage.schemes.elliptic_curves.constructor import EllipticCurve K = E.base_field() # Special case K = QQ if K is RationalField(): bound = ZZ.zero() k = 0 p = ZZ(2) # so we start with 3 E = E.integral_model() disc_E = E.discriminant() while k < number_of_places: p = p.next_prime() if p.divides(disc_E): continue k += 1 Fp = GF(p) new_bound = E.reduction(p).cardinality() bound = bound.gcd(new_bound) if bound == 1: return bound return bound # In case K is a relative extension we absolutize: absK = K.absolute_field('a_') f = absK.defining_polynomial() abs_map = absK.structure()[1] # Ensure f is monic and in ZZ[x] f = f.monic() den = f.denominator() if den != 1: x = f.parent().gen() n = f.degree() f = den**n * f(x / den) disc_f = f.discriminant() d = K.absolute_degree() # Now f is monic in ZZ[x] of degree d and defines the extension K = Q(a) # Make sure that we have a model for E with coefficients in ZZ[a] E = E.integral_model() disc_E = E.discriminant().norm() ainvs = [abs_map(c) for c in E.a_invariants()] bound = ZZ.zero() k = 0 p = ZZ(2) # so we start with 3 try: # special case, useful for base-changes from QQ ainvs = [ZZ(ai) for ai in ainvs] while k < number_of_places: p = p.next_prime() if p.divides(disc_E) or p.divides(disc_f): continue k += 1 for fi, ei in f.factor_mod(p): di = fi.degree() Fp = GF(p) new_bound = EllipticCurve( Fp, ainvs).cardinality(extension_degree=di) bound = bound.gcd(new_bound) if bound == 1: return bound return bound except (ValueError, TypeError): pass # General case while k < number_of_places: p = p.next_prime() if p.divides(disc_E) or p.divides(disc_f): continue k += 1 for fi, ei in f.factor_mod(p): di = fi.degree() Fq = GF(p**di) ai = fi.roots(Fq, multiplicities=False)[0] def red(c): return Fq.sum(Fq(c[j]) * ai**j for j in range(d)) new_bound = EllipticCurve([red(c) for c in ainvs]).cardinality() bound = bound.gcd(new_bound) if bound == 1: return bound return bound