From 8cbb73e266096e02efc917545aab36d067b7e063 Mon Sep 17 00:00:00 2001 From: Luca De Feo Date: Fri, 18 Apr 2014 11:19:33 +0200 Subject: [PATCH] 11474: Make elliptic curves unique parents. --- src/sage/databases/cremona.py | 8 +- .../schemes/elliptic_curves/constructor.py | 90 +++++++++++++--- .../elliptic_curves/ell_finite_field.py | 37 +++++-- .../schemes/elliptic_curves/ell_generic.py | 42 +++++--- .../elliptic_curves/ell_number_field.py | 51 +++++---- .../elliptic_curves/ell_padic_field.py | 47 +++++--- .../elliptic_curves/ell_rational_field.py | 100 +++++++++++++----- 7 files changed, 274 insertions(+), 101 deletions(-) diff --git a/src/sage/databases/cremona.py b/src/sage/databases/cremona.py index 67dfde890b9..a4817013263 100644 --- a/src/sage/databases/cremona.py +++ b/src/sage/databases/cremona.py @@ -866,7 +866,13 @@ def elliptic_curve(self, label): + "USING(class) WHERE curve=?",(label,)) try: c = q.next() - F = elliptic.EllipticCurve(eval(c[0])) + from sage.all import QQ + from sage.structure.sequence import Sequence + from sage.schemes.elliptic_curves.ell_rational_field import EllipticCurve_rational_field + #F = elliptic.EllipticCurve(eval(c[0])) + F = EllipticCurve_rational_field.__new__(EllipticCurve_rational_field) + EllipticCurve_rational_field.__init__(F, QQ, + Sequence(eval(c[0]),universe=QQ,immutable=True)) F._set_cremona_label(label) F._set_rank(c[1]) F._set_torsion_order(c[2]) diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index 6aba291e078..a6d8eb0e98b 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -6,6 +6,8 @@ - William Stein (2005): Initial version - John Cremona (2008-01): EllipticCurve(j) fixed for all cases + +- Simon King (2011-06): Make elliptic curves unique parents (trac ticket #11474) """ #***************************************************************************** @@ -90,15 +92,49 @@ def EllipticCurve(x=None, y=None, j=None, minimal_twist=True): sage: EllipticCurve([0,0,1,-1,0]) Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field - + sage: EllipticCurve([0,0,1,-1,0]) is EllipticCurve([0,0,1,-1,0]) + True + We create a curve from a Cremona label:: - + sage: EllipticCurve('37b2') Elliptic Curve defined by y^2 + y = x^3 + x^2 - 1873*x - 31833 over Rational Field - sage: EllipticCurve('5077a') + sage: E = EllipticCurve('5077a'); E Elliptic Curve defined by y^2 + y = x^3 - 7*x + 6 over Rational Field - sage: EllipticCurve('389a') - Elliptic Curve defined by y^2 + y = x^3 + x^2 - 2*x over Rational Field + + Note that elliptic curves almost are unique parent structures, by trac + ticket #11461: Two elliptic curves have equal `a`-invariants and are defined + over the same field then they are not only equal but identical:: + + sage: E is EllipticCurve('5077a') is EllipticCurve(QQ, E.a_invariants()) is EllipticCurve(j = E.j_invariant()) + True + + The only exception can occur if an elliptic curve is equal to a + curve from the database: If an elliptic curve found in the cache + has data that are different from the data provided by the + database, then the two elliptic curves are equal but distinct:: + + sage: E = EllipticCurve([0, 1, 1, -2, 0]) + sage: E is EllipticCurve('389a') + True + sage: E._EllipticCurve_rational_field__cremona_label = 'bogus' + sage: E is EllipticCurve('389a') + False + sage: E.label() + 'bogus' + sage: EllipticCurve('389a').label() + '389a1' + + However, attributes that are provided by the database are + automatically added to the curve found in the cache, if these + attributes have not been previously assigned. + :: + + sage: del E._EllipticCurve_rational_field__cremona_label + sage: E is EllipticCurve('389a') + True + sage: E._EllipticCurve_rational_field__cremona_label + '389 a 1' Old Cremona labels are allowed:: @@ -114,16 +150,20 @@ def EllipticCurve(x=None, y=None, j=None, minimal_twist=True): sage: EllipticCurve([GF(5)(0),0,1,-1,0]) Elliptic Curve defined by y^2 + y = x^3 + 4*x over Finite Field of size 5 - sage: EllipticCurve(GF(5), [0, 0,1,-1,0]) - Elliptic Curve defined by y^2 + y = x^3 + 4*x over Finite Field of size 5 + + The same object can be obtained by providing the data in a slightly + different way:: + + sage: EllipticCurve([GF(5)(0),0,1,-1,0]) is EllipticCurve(GF(5), [0, 0,1,-1,0]) + True Elliptic curves over `\ZZ/N\ZZ` with `N` prime are of type "elliptic curve over a finite field":: sage: F = Zmod(101) - sage: EllipticCurve(F, [2, 3]) + sage: E = EllipticCurve(F, [2, 3]); E Elliptic Curve defined by y^2 = x^3 + 2*x + 3 over Ring of integers modulo 101 - sage: E = EllipticCurve([F(2), F(3)]) + sage: E is EllipticCurve([F(2), F(3)]) sage: type(E) sage: E.category() @@ -133,9 +173,10 @@ def EllipticCurve(x=None, y=None, j=None, minimal_twist=True): are of type "generic elliptic curve":: sage: F = Zmod(95) - sage: EllipticCurve(F, [2, 3]) + sage: E = EllipticCurve(F, [2, 3]); E Elliptic Curve defined by y^2 = x^3 + 2*x + 3 over Ring of integers modulo 95 - sage: E = EllipticCurve([F(2), F(3)]) + sage: E is EllipticCurve([F(2), F(3)]) + True sage: type(E) sage: E.category() @@ -149,26 +190,41 @@ def EllipticCurve(x=None, y=None, j=None, minimal_twist=True): sage: E.j_invariant() 2988.97297297297 - We can also create elliptic curves by giving the Weierstrass equation:: + We can also create elliptic curves by giving the Weierstrass equation, and again + we find that elliptic curves are unique:: sage: x, y = var('x,y') - sage: EllipticCurve(y^2 + y == x^3 + x - 9) + sage: E = EllipticCurve(y^2 + y == x^3 + x - 9); E Elliptic Curve defined by y^2 + y = x^3 + x - 9 over Rational Field - + sage: E is EllipticCurve(E.base_ring(), E.a_invariants()) + True sage: R. = GF(5)[] - sage: EllipticCurve(x^3 + x^2 + 2 - y^2 - y*x) + sage: E = EllipticCurve(x^3 + x^2 + 2 - y^2 - y*x); E Elliptic Curve defined by y^2 + x*y = x^3 + x^2 + 2 over Finite Field of size 5 + sage: E is EllipticCurve(E.base_ring(), E.a_invariants()) + True - We can explicitly specify the `j`-invariant:: + We can explicitly specify the `j`-invariant, again in a unique way:: - sage: E = EllipticCurve(j=1728); E; E.j_invariant(); E.label() + sage: E = EllipticCurve(j=1728); E; E.j_invariant(); E.a_invariants(); E.label() Elliptic Curve defined by y^2 = x^3 - x over Rational Field 1728 + (0, 0, 0, -1, 0) '32a2' + sage: E is EllipticCurve(E.base_ring(), E.a_invariants()) + True + sage: E is EllipticCurve(E.label()) + True + sage: E is EllipticCurve(j = E.j_invariant()) + True + + :: sage: E = EllipticCurve(j=GF(5)(2)); E; E.j_invariant() Elliptic Curve defined by y^2 = x^3 + x + 1 over Finite Field of size 5 2 + sage: E is EllipticCurve(E.base_ring(), E.a_invariants()) + True See :trac:`6657` :: diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index d860dcae6a9..b22bee78edd 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -40,7 +40,7 @@ import sage.groups.generic as generic import ell_point from sage.rings.arith import gcd, lcm -from sage.structure.sequence import Sequence +from sage.structure.sequence import Sequence, Sequence_generic import sage.plot.all as plot @@ -51,10 +51,33 @@ class EllipticCurve_finite_field(EllipticCurve_field, HyperellipticCurve_finite_ """ Elliptic curve over a finite field. """ - def __init__(self, x, y=None): + @staticmethod + def __classcall__(cls, x, y=None): """ - Special constructor for elliptic curves over a finite field + Preprocess arguments such as to obtain a unique descriptor. + + TESTS:: + + sage: E = EllipticCurve(GF(101),[2,3]) + sage: type(E) + + sage: E is EllipticCurve(E.base_ring(), E.a_invariants()) # indirect doctest + True + """ + if y is None: + if isinstance(x, Sequence_generic) and x.is_immutable(): + ainvs = x + else: + ainvs = Sequence(x, immutable=True) + else: + ainvs = Sequence(y, universe=x, immutable=True) + return super(EllipticCurve_finite_field, cls).__classcall__(cls, ainvs.universe(), ainvs) + + def __init__(self, field, ainvs): + """ + Special constructor for elliptic curves over a finite field + EXAMPLES:: sage: EllipticCurve(GF(101),[2,3]) @@ -91,16 +114,10 @@ def __init__(self, x, y=None): Category of schemes over Ring of integers modulo 95 sage: TestSuite(E).run(skip=["_test_elements"]) """ - if isinstance(x, list): - seq = Sequence(x) - else: - seq = Sequence(y, universe=x) - ainvs = list(seq) - field = seq.universe() if not isinstance(field, ring.Ring): raise TypeError - EllipticCurve_field.__init__(self, ainvs) + EllipticCurve_field.__init__(self, field, ainvs) self._point = ell_point.EllipticCurvePoint_finite_field diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index ba6fcf615c4..b09e8531838 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -70,6 +70,8 @@ import formal_group import weierstrass_morphism as wm +from sage.structure.sequence import Sequence, Sequence_generic +from sage.structure.unique_representation import UniqueRepresentation factor = arith.factor sqrt = math.sqrt @@ -97,7 +99,7 @@ def is_EllipticCurve(x): """ return isinstance(x, EllipticCurve_generic) -class EllipticCurve_generic(plane_curve.ProjectiveCurve_generic): +class EllipticCurve_generic(UniqueRepresentation, plane_curve.ProjectiveCurve_generic): r""" Elliptic curve over a generic base ring. @@ -112,14 +114,38 @@ class EllipticCurve_generic(plane_curve.ProjectiveCurve_generic): sage: -5*P (179051/80089 : -91814227/22665187 : 1) """ - def __init__(self, ainvs, extra=None): + @staticmethod + def __classcall__(cls, ainvs, extra=None): + """ + Preprocess arguments such as to obtain a unique descriptor. + + TESTS:: + + sage: E = EllipticCurve(IntegerModRing(91),[1,2,3,4,5]) + sage: type(E) + + sage: E is EllipticCurve(E.base_ring(), E.a_invariants()) # indirect doctest + True + + """ + if extra != None: # possibility of two arguments + K, ainvs = ainvs, extra + else: + K = ainvs[0].parent() + assert len(ainvs) == 2 or len(ainvs) == 5 + if len(ainvs) == 2: + ainvs = [K(0),K(0),K(0)] + ainvs + if not (isinstance(ainvs,Sequence_generic) and ainvs.is_immutable()): + ainvs = Sequence(ainvs, universe=K, immutable=True) + return super(EllipticCurve_generic,cls).__classcall__(cls, K, ainvs) + + def __init__(self, K, ainvs): r""" Constructor from `a`-invariants (long or short Weierstrass coefficients). INPUT: - - ``ainvs`` (list) -- either `[a_1,a_2,a_3,a_4,a_6]` or - `[a_4,a_6]` (with `a_1=a_2=a_3=0` in the second case). + - ``ainvs`` (immutable Sequence) -- `[a_1,a_2,a_3,a_4,a_6]` .. note:: @@ -142,15 +168,7 @@ def __init__(self, ainvs, extra=None): sage: EllipticCurve(IntegerModRing(91),[1,2,3,4,5]) Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Ring of integers modulo 91 """ - if extra != None: # possibility of two arguments - K, ainvs = ainvs, extra - else: - K = ainvs[0].parent() - assert len(ainvs) == 2 or len(ainvs) == 5 self.__base_ring = K - ainvs = [K(x) for x in ainvs] - if len(ainvs) == 2: - ainvs = [K(0),K(0),K(0)] + ainvs self.__ainvs = tuple(ainvs) if self.discriminant() == 0: raise ArithmeticError, \ diff --git a/src/sage/schemes/elliptic_curves/ell_number_field.py b/src/sage/schemes/elliptic_curves/ell_number_field.py index 6582830a866..aeaa42f5f52 100644 --- a/src/sage/schemes/elliptic_curves/ell_number_field.py +++ b/src/sage/schemes/elliptic_curves/ell_number_field.py @@ -106,8 +106,8 @@ from sage.misc.misc import verbose, forall from sage.rings.integer import Integer from sage.rings.arith import valuation - import gal_reps_number_field +from sage.structure.sequence import Sequence class EllipticCurve_number_field(EllipticCurve_field): r""" @@ -119,7 +119,36 @@ class EllipticCurve_number_field(EllipticCurve_field): sage: EllipticCurve([i, i - 1, i + 1, 24*i + 15, 14*i + 35]) Elliptic Curve defined by y^2 + i*x*y + (i+1)*y = x^3 + (i-1)*x^2 + (24*i+15)*x + (14*i+35) over Number Field in i with defining polynomial x^2 + 1 """ - def __init__(self, x, y=None): + @staticmethod + def __classcall__(cls, x, y=None): + """ + Preprocess arguments such as to obtain a unique descriptor. + + TESTS:: + + sage: K.=NumberField(x^2+1) + sage: E = EllipticCurve([i, i - 1, i + 1, 24*i + 15, 14*i + 35]) + sage: E is EllipticCurve(E.base_ring(),E.a_invariants()) # indirect doctest + True + + """ + if y is None: + if isinstance(x, (list,tuple)): + field = x[0].parent() + ainvs = Sequence(x, universe=field, immutable=True) + else: + if isinstance(y, str): + field = x + X = sage.databases.cremona.CremonaDatabase()[y] + ainvs = Sequence(X.a_invariants(), universe=field, immutable=True) + else: + field = x + ainvs = Sequence(y, universe=field, immutable=True) + if not (isinstance(field, Ring)): + raise TypeError, "Can not determine the ring for that elliptic curve" + return super(EllipticCurve_number_field,cls).__classcall__(cls, field, ainvs) + + def __init__(self, K, ainvs): r""" Allow some ways to create an elliptic curve over a number field in addition to the generic ones. @@ -142,23 +171,7 @@ def __init__(self, x, y=None): Elliptic Curve defined by y^2 + y = x^3 + (-1)*x^2 over Number Field in i with defining polynomial x^2 + 1 """ - if y is None: - if isinstance(x, list): - ainvs = x - field = ainvs[0].parent() - else: - if isinstance(y, str): - from sage.databases.cremona import CremonaDatabase - field = x - X = CremonaDatabase()[y] - ainvs = list(X.a_invariants()) - else: - field = x - ainvs = y - if not (isinstance(field, Ring) and isinstance(ainvs,list)): - raise TypeError - - EllipticCurve_field.__init__(self, [field(x) for x in ainvs]) + EllipticCurve_field.__init__(self, K, ainvs) self._point = ell_point.EllipticCurvePoint_number_field def simon_two_descent(self, verbose=0, lim1=2, lim3=4, limtriv=2, maxprob=20, limbigprime=30): diff --git a/src/sage/schemes/elliptic_curves/ell_padic_field.py b/src/sage/schemes/elliptic_curves/ell_padic_field.py index 5946698ba6d..1c7f9886235 100644 --- a/src/sage/schemes/elliptic_curves/ell_padic_field.py +++ b/src/sage/schemes/elliptic_curves/ell_padic_field.py @@ -30,40 +30,61 @@ from sage.schemes.hyperelliptic_curves.hyperelliptic_padic_field import HyperellipticCurve_padic_field import sage.databases.cremona - +from sage.structure.sequence import Sequence, Sequence_generic class EllipticCurve_padic_field(EllipticCurve_field, HyperellipticCurve_padic_field): """ Elliptic curve over a padic field. """ - def __init__(self, x, y=None): + @staticmethod + def __classcall__(cls, x, y=None): """ - Constructor from [a1,a2,a3,a4,a6] or [a4,a6]. + Preprocess arguments such as to obtain a unique descriptor. - EXAMPLES:: + TESTS::: sage: Qp=pAdicField(17) sage: E=EllipticCurve(Qp,[2,3]); E - Elliptic Curve defined by y^2 = x^3 + (2+O(17^20))*x + (3+O(17^20)) over 17-adic Field with capped relative precision 20 - sage: E == loads(dumps(E)) + Elliptic Curve defined by y^2 = x^3 + (2+O(17^20))*x + (3+O(17^20)) over 17-adic Field with capped relative precision 20 + sage: E is EllipticCurve(E.base_ring(), E.a_invariants()) # indirect doctest True + """ if y is None: - if isinstance(x, list): + if isinstance(x, Sequence_generic) and x.is_immutable(): ainvs = x - field = ainvs[0].parent() + field = ainvs.universe() + elif isinstance(x, (list,tuple)): # that covers sequences as well + field = x[0].parent() + ainvs = Sequence(x, universe=field, immutable=True) + else: + raise TypeError, "Since the second argument is not provided, the first must be a list or tuple" else: if isinstance(y, str): field = x X = sage.databases.cremona.CremonaDatabase()[y] - ainvs = [field(a) for a in X.a_invariants()] + ainvs = Sequence(X.a_invariants(), universe=field, immutable=True) else: field = x - ainvs = y - if not (isinstance(field, ring.Ring) and isinstance(ainvs,list)): - raise TypeError + ainvs = Sequence(y, universe=field, immutable=True) + if not (isinstance(field, ring.Ring)): + raise TypeError, "Can not determine the ring for that elliptic curve" + return super(EllipticCurve_padic_field,cls).__classcall__(cls, field, ainvs) + + def __init__(self, field, ainvs): + """ + Constructor from [a1,a2,a3,a4,a6] or [a4,a6]. - EllipticCurve_field.__init__(self, [field(x) for x in ainvs]) + EXAMPLES:: + + sage: Qp=pAdicField(17) + sage: E=EllipticCurve(Qp,[2,3]); E + Elliptic Curve defined by y^2 = x^3 + (2+O(17^20))*x + (3+O(17^20)) over 17-adic Field with capped relative precision 20 + sage: E is loads(dumps(E)) + True + +""" + EllipticCurve_field.__init__(self, field, ainvs) self._point = ell_point.EllipticCurvePoint_field self._genus = 1 diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 86a7516ed79..1dcd5646f5a 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -93,6 +93,7 @@ from sage.interfaces.all import gp from sage.misc.cachefunc import cached_method from copy import copy +from sage.structure.sequence import Sequence, Sequence_generic Q = RationalField() C = ComplexField() @@ -144,19 +145,78 @@ class EllipticCurve_rational_field(EllipticCurve_number_field): Elliptic Curve defined by y^2 + y = x^3 + x^2 - 2*x over Rational Field """ - def __init__(self, ainvs, extra=None): + @staticmethod + def __classcall__(cls, ainvs, extra=None): + """ + Preprocess arguments such as to obtain a unique descriptor. + + TESTS:: + + sage: E = EllipticCurve(j = 2019487744/1012581) + sage: E + Elliptic Curve defined by y^2 + y = x^3 + x^2 - 26*x - 28 over Rational Field + sage: E.label() + '1389a1' + sage: E is EllipticCurve(E.label()) # indirect doctest + True + sage: E is EllipticCurve(E.base_ring(), E.a_invariants()) # indirect doctest + True + + """ + if extra != None: # possibility of two arguments (the first would be the field) + field = ainvs + if field != Q: + raise ValueError, "The given field must be the rational field" + if isinstance(extra,Sequence_generic) and extra.is_immutable(): + AINVS = extra + else: + AINVS = Sequence(extra, universe=field, immutable=True) + return super(EllipticCurve_rational_field, cls).__classcall__(cls, AINVS) + if isinstance(ainvs, str): + #label = ainvs + X = sage.databases.cremona.CremonaDatabase()[ainvs] + AINVS = Sequence(X.a_invariants(), universe=Q, immutable=True) + out = super(EllipticCurve_rational_field, cls).__classcall__(cls, Q, AINVS) + for attr in ['rank', 'torsion_order', 'cremona_label', 'conductor', + 'modular_degree', 'gens', 'regulator']: + s = "_EllipticCurve_rational_field__"+attr + if hasattr(X,s): + if hasattr(out,s): + attr = getattr(out,s) + if attr and getattr(X, s) != getattr(out,s): + return X # breaking the unique parent condition + else: + if attr == 'gens': # see #10999 + gens_dict = getattr(X, s) + for boo in gens_dict.keys(): + gens_dict[boo] = [out(P) for P in gens_dict[boo]] + setattr(out, s, gens_dict) + else: + setattr(out, s, getattr(X, s)) + if hasattr(X,'_lmfdb_label'): + out._lmfdb_label = X._lmfdb_label + return out + if isinstance(ainvs, Sequence_generic) and ainvs.is_immutable(): + AINVS = ainvs + else: + field = Q + AINVS = Sequence(ainvs, universe=Q, immutable=True) + return super(EllipticCurve_rational_field, cls).__classcall__(cls, Q, AINVS) + + def __init__(self, field, ainvs): r""" Constructor for the EllipticCurve_rational_field class. INPUT: - - ``ainvs`` (list or string) -- either `[a_1,a_2,a_3,a_4,a_6]` - or `[a_4,a_6]` (with `a_1=a_2=a_3=0`) or a valid label from - the database. + - ``ainvs`` (list or string) -- `[a_1,a_2,a_3,a_4,a_6]` .. note:: - See constructor.py for more variants. + See constructor.py for more variants. There is a __classcall__ + method that will, in particular, take getting an elliptic curve + out of the database, given its label. + EXAMPLES:: @@ -190,8 +250,9 @@ def __init__(self, ainvs, extra=None): [True, True] """ - if extra != None: # possibility of two arguments (the first would be the field) - ainvs = extra + if field != Q: + raise ValueError, "The given field must be the rational field" + EllipticCurve_number_field.__init__(self, field, ainvs) self.__np = {} self.__gens = {} self.__rank = {} @@ -199,24 +260,6 @@ def __init__(self, ainvs, extra=None): self.__generalized_modular_degree = {} self.__generalized_congruence_number = {} self._isoclass = {} - if isinstance(ainvs, str): - label = ainvs - X = sage.databases.cremona.CremonaDatabase()[label] - EllipticCurve_number_field.__init__(self, Q, list(X.a_invariants())) - for attr in ['rank', 'torsion_order', 'cremona_label', 'conductor', - 'modular_degree', 'gens', 'regulator']: - s = "_EllipticCurve_rational_field__"+attr - if hasattr(X,s): - if attr == 'gens': # see #10999 - gens_dict = getattr(X, s) - for boo in gens_dict.keys(): - gens_dict[boo] = [self(P) for P in gens_dict[boo]] - setattr(self, s, gens_dict) - else: - setattr(self, s, getattr(X, s)) - if hasattr(X,'_lmfdb_label'): - self._lmfdb_label = X._lmfdb_label - return EllipticCurve_number_field.__init__(self, Q, ainvs) if self.base_ring() != Q: raise TypeError, "Base field (=%s) must be the Rational Field."%self.base_ring() @@ -349,11 +392,10 @@ def _set_gens(self, gens): sage: E.gens() # random [(-2 : 3 : 1), (-7/4 : 25/8 : 1), (1 : -1 : 1)] sage: E._set_gens([]) # bogus list - sage: E.rank() # unchanged - 3 - sage: E._set_gens([E(-2,3), E(-1,3), E(0,2)]) sage: E.gens() - [(-2 : 3 : 1), (-1 : 3 : 1), (0 : 2 : 1)] + [] + sage: E.rank() # unchanged + 3 """ self.__gens = {} self.__gens[True] = [self.point(x, check=True) for x in gens]