Isomorphisms between Weierstrass models of elliptic curves

AUTHORS:

  • Robert Bradshaw (2007): initial version

  • John Cremona (Jan 2008): isomorphisms, automorphisms and twists in all characteristics

  • Lorenz Panny (2021): EllipticCurveHom interface

class sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism(E=None, urst=None, F=None)[source]

Bases: EllipticCurveHom, baseWI

Class representing a Weierstrass isomorphism between two elliptic curves.

INPUT:

  • E – an EllipticCurve, or None (see below)

  • urst – a 4-tuple \((u,r,s,t)\), a baseWI object, or None (see below)

  • F – an EllipticCurve, or None (see below)

Given two Elliptic Curves E and F (represented by Weierstrass models as usual), and a transformation urst from E to F, construct an isomorphism from E to F. An exception is raised if urst(E) != F. At most one of E, F, urst can be None. In this case, the missing input is constructed from the others in such a way that urst(E) == F holds, and an exception is raised if this is impossible (typically because E and F are not isomorphic).

Users will not usually need to use this class directly, but instead use methods such as isomorphism_to() or isomorphisms().

Explicitly, the isomorphism defined by \((u,r,s,t)\) maps a point \((x,y)\) to the point

\[((x-r) / u^2, \; (y - s(x-r) - t) / u^3) .\]

If the domain \(E\) has Weierstrass coefficients \([a_1,a_2,a_3,a_4,a_6]\), the codomain \(F\) is given by

\[\begin{split}a_1' &= (a_1 + 2s) / u \\ a_2' &= (a_2 - a_1s + 3r - s^2) / u^2 \\ a_3' &= (a_3 + a_1r + 2t) / u^3 \\ a_4' &= (a_4 + 2a_2r - a_1(rs+t) - a_3s + 3r^2 - 2st) / u^4 \\ a_6' &= (a_6 - a_1rt + a_2r^2 - a_3t + a_4r + r^3 - t^2) / u^6 .\end{split}\]

EXAMPLES:

sage: from sage.schemes.elliptic_curves.weierstrass_morphism import *
sage: WeierstrassIsomorphism(EllipticCurve([0,1,2,3,4]), (-1,2,3,4))
Elliptic-curve morphism:
  From: Elliptic Curve defined by y^2 + 2*y = x^3 + x^2 + 3*x + 4 over Rational Field
  To:   Elliptic Curve defined by y^2 - 6*x*y - 10*y = x^3 - 2*x^2 - 11*x - 2 over Rational Field
  Via:  (u,r,s,t) = (-1, 2, 3, 4)
sage: E = EllipticCurve([0,1,2,3,4])
sage: F = EllipticCurve(E.cremona_label())
sage: WeierstrassIsomorphism(E, None, F)
Elliptic-curve morphism:
  From: Elliptic Curve defined by y^2 + 2*y = x^3 + x^2 + 3*x + 4 over Rational Field
  To:   Elliptic Curve defined by y^2  = x^3 + x^2 + 3*x + 5 over Rational Field
  Via:  (u,r,s,t) = (1, 0, 0, -1)
sage: w = WeierstrassIsomorphism(None, (1,0,0,-1), F)
sage: w._domain == E
True
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.weierstrass_morphism import *
>>> WeierstrassIsomorphism(EllipticCurve([Integer(0),Integer(1),Integer(2),Integer(3),Integer(4)]), (-Integer(1),Integer(2),Integer(3),Integer(4)))
Elliptic-curve morphism:
  From: Elliptic Curve defined by y^2 + 2*y = x^3 + x^2 + 3*x + 4 over Rational Field
  To:   Elliptic Curve defined by y^2 - 6*x*y - 10*y = x^3 - 2*x^2 - 11*x - 2 over Rational Field
  Via:  (u,r,s,t) = (-1, 2, 3, 4)
>>> E = EllipticCurve([Integer(0),Integer(1),Integer(2),Integer(3),Integer(4)])
>>> F = EllipticCurve(E.cremona_label())
>>> WeierstrassIsomorphism(E, None, F)
Elliptic-curve morphism:
  From: Elliptic Curve defined by y^2 + 2*y = x^3 + x^2 + 3*x + 4 over Rational Field
  To:   Elliptic Curve defined by y^2  = x^3 + x^2 + 3*x + 5 over Rational Field
  Via:  (u,r,s,t) = (1, 0, 0, -1)
>>> w = WeierstrassIsomorphism(None, (Integer(1),Integer(0),Integer(0),-Integer(1)), F)
>>> w._domain == E
True
from sage.schemes.elliptic_curves.weierstrass_morphism import *
WeierstrassIsomorphism(EllipticCurve([0,1,2,3,4]), (-1,2,3,4))
E = EllipticCurve([0,1,2,3,4])
F = EllipticCurve(E.cremona_label())
WeierstrassIsomorphism(E, None, F)
w = WeierstrassIsomorphism(None, (1,0,0,-1), F)
w._domain == E
dual()[source]

Return the dual isogeny of this isomorphism.

For isomorphisms, the dual is just the inverse.

EXAMPLES:

sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
sage: E = EllipticCurve(QuadraticField(-3), [0,1])                          # needs sage.rings.number_field
sage: w = WeierstrassIsomorphism(E, (CyclotomicField(3).gen(),0,0,0))       # needs sage.rings.number_field
sage: (w.dual() * w).rational_maps()                                        # needs sage.rings.number_field
(x, y)
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
>>> E = EllipticCurve(QuadraticField(-Integer(3)), [Integer(0),Integer(1)])                          # needs sage.rings.number_field
>>> w = WeierstrassIsomorphism(E, (CyclotomicField(Integer(3)).gen(),Integer(0),Integer(0),Integer(0)))       # needs sage.rings.number_field
>>> (w.dual() * w).rational_maps()                                        # needs sage.rings.number_field
(x, y)
from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
E = EllipticCurve(QuadraticField(-3), [0,1])                          # needs sage.rings.number_field
w = WeierstrassIsomorphism(E, (CyclotomicField(3).gen(),0,0,0))       # needs sage.rings.number_field
(w.dual() * w).rational_maps()                                        # needs sage.rings.number_field

sage: E1 = EllipticCurve([11,22,33,44,55])
sage: E2 = E1.short_weierstrass_model()
sage: iso = E1.isomorphism_to(E2)
sage: iso.dual() == ~iso
True
>>> from sage.all import *
>>> E1 = EllipticCurve([Integer(11),Integer(22),Integer(33),Integer(44),Integer(55)])
>>> E2 = E1.short_weierstrass_model()
>>> iso = E1.isomorphism_to(E2)
>>> iso.dual() == ~iso
True
E1 = EllipticCurve([11,22,33,44,55])
E2 = E1.short_weierstrass_model()
iso = E1.isomorphism_to(E2)
iso.dual() == ~iso
inseparable_degree()[source]

Return the inseparable degree of this Weierstrass isomorphism.

For isomorphisms, this method always returns one.

is_identity()[source]

Check if this Weierstrass isomorphism is the identity.

EXAMPLES:

sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
sage: p = 97
sage: Fp = GF(p)
sage: E = EllipticCurve(Fp, [1, 28])
sage: ws = WeierstrassIsomorphism(E, None, E)
sage: ws.is_identity()
False
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
>>> p = Integer(97)
>>> Fp = GF(p)
>>> E = EllipticCurve(Fp, [Integer(1), Integer(28)])
>>> ws = WeierstrassIsomorphism(E, None, E)
>>> ws.is_identity()
False
from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
p = 97
Fp = GF(p)
E = EllipticCurve(Fp, [1, 28])
ws = WeierstrassIsomorphism(E, None, E)
ws.is_identity()

sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
sage: p = 97
sage: Fp = GF(p)
sage: E = EllipticCurve(Fp, [1, 28])
sage: ws = WeierstrassIsomorphism(E, (1, 0, 0, 0), None)
sage: ws.is_identity()
True
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
>>> p = Integer(97)
>>> Fp = GF(p)
>>> E = EllipticCurve(Fp, [Integer(1), Integer(28)])
>>> ws = WeierstrassIsomorphism(E, (Integer(1), Integer(0), Integer(0), Integer(0)), None)
>>> ws.is_identity()
True
from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
p = 97
Fp = GF(p)
E = EllipticCurve(Fp, [1, 28])
ws = WeierstrassIsomorphism(E, (1, 0, 0, 0), None)
ws.is_identity()
kernel_polynomial()[source]

Return the kernel polynomial of this isomorphism.

Isomorphisms have trivial kernel by definition, hence this method always returns \(1\).

EXAMPLES:

sage: E1 = EllipticCurve([11,22,33,44,55])
sage: E2 = EllipticCurve_from_j(E1.j_invariant())
sage: iso = E1.isomorphism_to(E2)
sage: iso.kernel_polynomial()
1
sage: psi = E1.isogeny(iso.kernel_polynomial(), codomain=E2); psi
Isogeny of degree 1
 from Elliptic Curve defined by y^2 + 11*x*y + 33*y = x^3 + 22*x^2 + 44*x + 55
      over Rational Field
   to Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 684*x + 6681
      over Rational Field
sage: psi in {iso, -iso}
True
>>> from sage.all import *
>>> E1 = EllipticCurve([Integer(11),Integer(22),Integer(33),Integer(44),Integer(55)])
>>> E2 = EllipticCurve_from_j(E1.j_invariant())
>>> iso = E1.isomorphism_to(E2)
>>> iso.kernel_polynomial()
1
>>> psi = E1.isogeny(iso.kernel_polynomial(), codomain=E2); psi
Isogeny of degree 1
 from Elliptic Curve defined by y^2 + 11*x*y + 33*y = x^3 + 22*x^2 + 44*x + 55
      over Rational Field
   to Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 684*x + 6681
      over Rational Field
>>> psi in {iso, -iso}
True
E1 = EllipticCurve([11,22,33,44,55])
E2 = EllipticCurve_from_j(E1.j_invariant())
iso = E1.isomorphism_to(E2)
iso.kernel_polynomial()
psi = E1.isogeny(iso.kernel_polynomial(), codomain=E2); psi
psi in {iso, -iso}
order()[source]

Compute the order of this Weierstrass isomorphism if it is an automorphism.

A ValueError is raised if the domain is not equal to the codomain.

A NotImplementedError is raised if the order of the automorphism is not 1, 2, 3, 4 or 6.

EXAMPLES:

sage: from sage.schemes.elliptic_curves.weierstrass_morphism import *
sage: p = 97
sage: Fp = GF(p)
sage: E = EllipticCurve(Fp, [1, 28])
sage: ws = WeierstrassIsomorphism(E, None, E)
sage: ws.order()
2
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.weierstrass_morphism import *
>>> p = Integer(97)
>>> Fp = GF(p)
>>> E = EllipticCurve(Fp, [Integer(1), Integer(28)])
>>> ws = WeierstrassIsomorphism(E, None, E)
>>> ws.order()
2
from sage.schemes.elliptic_curves.weierstrass_morphism import *
p = 97
Fp = GF(p)
E = EllipticCurve(Fp, [1, 28])
ws = WeierstrassIsomorphism(E, None, E)
ws.order()
rational_maps()[source]

Return the pair of rational maps defining this isomorphism.

EXAMPLES:

sage: E1 = EllipticCurve([11,22,33,44,55])
sage: E2 = EllipticCurve_from_j(E1.j_invariant())
sage: iso = E1.isomorphism_to(E2); iso
Elliptic-curve morphism:
  From: Elliptic Curve defined by y^2 + 11*x*y + 33*y = x^3 + 22*x^2 + 44*x + 55
        over Rational Field
  To:   Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 684*x + 6681
        over Rational Field
  Via:  (u,r,s,t) = (1, -17, -5, 77)
sage: iso.rational_maps()
(x + 17, 5*x + y + 8)
sage: f = E2.defining_polynomial()(*iso.rational_maps(), 1)
sage: I = E1.defining_ideal()
sage: x,y,z = I.ring().gens()
sage: f in I + Ideal(z-1)
True
>>> from sage.all import *
>>> E1 = EllipticCurve([Integer(11),Integer(22),Integer(33),Integer(44),Integer(55)])
>>> E2 = EllipticCurve_from_j(E1.j_invariant())
>>> iso = E1.isomorphism_to(E2); iso
Elliptic-curve morphism:
  From: Elliptic Curve defined by y^2 + 11*x*y + 33*y = x^3 + 22*x^2 + 44*x + 55
        over Rational Field
  To:   Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 684*x + 6681
        over Rational Field
  Via:  (u,r,s,t) = (1, -17, -5, 77)
>>> iso.rational_maps()
(x + 17, 5*x + y + 8)
>>> f = E2.defining_polynomial()(*iso.rational_maps(), Integer(1))
>>> I = E1.defining_ideal()
>>> x,y,z = I.ring().gens()
>>> f in I + Ideal(z-Integer(1))
True
E1 = EllipticCurve([11,22,33,44,55])
E2 = EllipticCurve_from_j(E1.j_invariant())
iso = E1.isomorphism_to(E2); iso
iso.rational_maps()
f = E2.defining_polynomial()(*iso.rational_maps(), 1)
I = E1.defining_ideal()
x,y,z = I.ring().gens()
f in I + Ideal(z-1)

sage: # needs sage.rings.finite_rings
sage: E = EllipticCurve(GF(65537), [1,1,1,1,1])
sage: w = E.isomorphism_to(E.short_weierstrass_model())
sage: f,g = w.rational_maps()
sage: P = E.random_point()
sage: w(P).xy() == (f(P.xy()), g(P.xy()))
True
>>> from sage.all import *
>>> # needs sage.rings.finite_rings
>>> E = EllipticCurve(GF(Integer(65537)), [Integer(1),Integer(1),Integer(1),Integer(1),Integer(1)])
>>> w = E.isomorphism_to(E.short_weierstrass_model())
>>> f,g = w.rational_maps()
>>> P = E.random_point()
>>> w(P).xy() == (f(P.xy()), g(P.xy()))
True
# needs sage.rings.finite_rings
E = EllipticCurve(GF(65537), [1,1,1,1,1])
w = E.isomorphism_to(E.short_weierstrass_model())
f,g = w.rational_maps()
P = E.random_point()
w(P).xy() == (f(P.xy()), g(P.xy()))
scaling_factor()[source]

Return the Weierstrass scaling factor associated to this Weierstrass isomorphism.

The scaling factor is the constant \(u\) (in the base field) such that \(\varphi^* \omega_2 = u \omega_1\), where \(\varphi: E_1\to E_2\) is this isomorphism and \(\omega_i\) are the standard Weierstrass differentials on \(E_i\) defined by \(\mathrm dx/(2y+a_1x+a_3)\).

EXAMPLES:

sage: E = EllipticCurve(QQbar, [0,1])                                       # needs sage.rings.number_field
sage: all(f.scaling_factor() == f.formal()[1] for f in E.automorphisms())   # needs sage.rings.number_field
True
>>> from sage.all import *
>>> E = EllipticCurve(QQbar, [Integer(0),Integer(1)])                                       # needs sage.rings.number_field
>>> all(f.scaling_factor() == f.formal()[Integer(1)] for f in E.automorphisms())   # needs sage.rings.number_field
True
E = EllipticCurve(QQbar, [0,1])                                       # needs sage.rings.number_field
all(f.scaling_factor() == f.formal()[1] for f in E.automorphisms())   # needs sage.rings.number_field

ALGORITHM: The scaling factor equals the \(u\) component of the tuple \((u,r,s,t)\) defining the isomorphism.

x_rational_map()[source]

Return the \(x\)-coordinate rational map of this isomorphism.

EXAMPLES:

sage: E1 = EllipticCurve([11,22,33,44,55])
sage: E2 = EllipticCurve_from_j(E1.j_invariant())
sage: iso = E1.isomorphism_to(E2); iso
Elliptic-curve morphism:
  From: Elliptic Curve defined by y^2 + 11*x*y + 33*y = x^3 + 22*x^2 + 44*x + 55
        over Rational Field
  To:   Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 684*x + 6681
        over Rational Field
  Via:  (u,r,s,t) = (1, -17, -5, 77)
sage: iso.x_rational_map()
x + 17
sage: iso.x_rational_map() == iso.rational_maps()[0]
True
>>> from sage.all import *
>>> E1 = EllipticCurve([Integer(11),Integer(22),Integer(33),Integer(44),Integer(55)])
>>> E2 = EllipticCurve_from_j(E1.j_invariant())
>>> iso = E1.isomorphism_to(E2); iso
Elliptic-curve morphism:
  From: Elliptic Curve defined by y^2 + 11*x*y + 33*y = x^3 + 22*x^2 + 44*x + 55
        over Rational Field
  To:   Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 684*x + 6681
        over Rational Field
  Via:  (u,r,s,t) = (1, -17, -5, 77)
>>> iso.x_rational_map()
x + 17
>>> iso.x_rational_map() == iso.rational_maps()[Integer(0)]
True
E1 = EllipticCurve([11,22,33,44,55])
E2 = EllipticCurve_from_j(E1.j_invariant())
iso = E1.isomorphism_to(E2); iso
iso.x_rational_map()
iso.x_rational_map() == iso.rational_maps()[0]
class sage.schemes.elliptic_curves.weierstrass_morphism.baseWI(u=1, r=0, s=0, t=0)[source]

Bases: object

This class implements the basic arithmetic of isomorphisms between Weierstrass models of elliptic curves.

These are specified by lists of the form \([u,r,s,t]\) (with \(u \neq 0\)) which specifies a transformation \((x,y) \mapsto (x',y')\) where

\((x,y) = (u^2x'+r , u^3y' + su^2x' + t).\)

INPUT:

  • u, r, s, t – (default: \(1\), \(0\), \(0\), \(0\)); standard parameters of an isomorphism between Weierstrass models

EXAMPLES:

sage: from sage.schemes.elliptic_curves.weierstrass_morphism import *
sage: baseWI()
(1, 0, 0, 0)
sage: baseWI(2,3,4,5)
(2, 3, 4, 5)
sage: R.<u,r,s,t> = QQ[]
sage: baseWI(u,r,s,t)
(u, r, s, t)
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.weierstrass_morphism import *
>>> baseWI()
(1, 0, 0, 0)
>>> baseWI(Integer(2),Integer(3),Integer(4),Integer(5))
(2, 3, 4, 5)
>>> R = QQ['u, r, s, t']; (u, r, s, t,) = R._first_ngens(4)
>>> baseWI(u,r,s,t)
(u, r, s, t)
from sage.schemes.elliptic_curves.weierstrass_morphism import *
baseWI()
baseWI(2,3,4,5)
R.<u,r,s,t> = QQ[]
baseWI(u,r,s,t)
is_identity()[source]

Return True if this is the identity isomorphism.

EXAMPLES:

sage: from sage.schemes.elliptic_curves.weierstrass_morphism import *
sage: w = baseWI(); w.is_identity()
True
sage: w = baseWI(2,3,4,5); w.is_identity()
False
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.weierstrass_morphism import *
>>> w = baseWI(); w.is_identity()
True
>>> w = baseWI(Integer(2),Integer(3),Integer(4),Integer(5)); w.is_identity()
False
from sage.schemes.elliptic_curves.weierstrass_morphism import *
w = baseWI(); w.is_identity()
w = baseWI(2,3,4,5); w.is_identity()
tuple()[source]

Return the parameters \(u,r,s,t\) as a tuple.

EXAMPLES:

sage: from sage.schemes.elliptic_curves.weierstrass_morphism import *
sage: w = baseWI(2,3,4,5)
sage: w.tuple()
(2, 3, 4, 5)
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.weierstrass_morphism import *
>>> w = baseWI(Integer(2),Integer(3),Integer(4),Integer(5))
>>> w.tuple()
(2, 3, 4, 5)
from sage.schemes.elliptic_curves.weierstrass_morphism import *
w = baseWI(2,3,4,5)
w.tuple()
sage.schemes.elliptic_curves.weierstrass_morphism.identity_morphism(E)[source]

Given an elliptic curve \(E\), return the identity morphism on \(E\) as a WeierstrassIsomorphism.

EXAMPLES:

sage: from sage.schemes.elliptic_curves.weierstrass_morphism import identity_morphism
sage: E = EllipticCurve([5,6,7,8,9])
sage: id_ = identity_morphism(E)
sage: id_.rational_maps()
(x, y)
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.weierstrass_morphism import identity_morphism
>>> E = EllipticCurve([Integer(5),Integer(6),Integer(7),Integer(8),Integer(9)])
>>> id_ = identity_morphism(E)
>>> id_.rational_maps()
(x, y)
from sage.schemes.elliptic_curves.weierstrass_morphism import identity_morphism
E = EllipticCurve([5,6,7,8,9])
id_ = identity_morphism(E)
id_.rational_maps()
sage.schemes.elliptic_curves.weierstrass_morphism.negation_morphism(E)[source]

Given an elliptic curve \(E\), return the negation endomorphism \([-1]\) of \(E\) as a WeierstrassIsomorphism.

EXAMPLES:

sage: from sage.schemes.elliptic_curves.weierstrass_morphism import negation_morphism
sage: E = EllipticCurve([5,6,7,8,9])
sage: neg = negation_morphism(E)
sage: neg.rational_maps()
(x, -5*x - y - 7)
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.weierstrass_morphism import negation_morphism
>>> E = EllipticCurve([Integer(5),Integer(6),Integer(7),Integer(8),Integer(9)])
>>> neg = negation_morphism(E)
>>> neg.rational_maps()
(x, -5*x - y - 7)
from sage.schemes.elliptic_curves.weierstrass_morphism import negation_morphism
E = EllipticCurve([5,6,7,8,9])
neg = negation_morphism(E)
neg.rational_maps()