Elliptic-curve morphisms#
This class serves as a common parent for various specializations of morphisms between elliptic curves, with the aim of providing a common interface regardless of implementation details.
Current implementations of elliptic-curve morphisms (child classes):
AUTHORS:
See authors of
EllipticCurveIsogeny
. Some of the code in this class was lifted from there.Lorenz Panny (2021): Refactor isogenies and isomorphisms into the common
EllipticCurveHom
interface.
- class sage.schemes.elliptic_curves.hom.EllipticCurveHom(*args, **kwds)#
Bases:
Morphism
Base class for elliptic-curve morphisms.
- as_morphism()#
Return
self
as a morphism of projective schemes.EXAMPLES:
sage: k = GF(11) sage: E = EllipticCurve(k, [1,1]) sage: Q = E(6,5) sage: phi = E.isogeny(Q) sage: mor = phi.as_morphism() sage: mor.domain() == E True sage: mor.codomain() == phi.codomain() True sage: mor(Q) == phi(Q) True
- degree()#
Return the degree of this elliptic-curve morphism.
EXAMPLES:
sage: E = EllipticCurve(QQ, [0,0,0,1,0]) sage: phi = EllipticCurveIsogeny(E, E((0,0))) sage: phi.degree() 2 sage: phi = EllipticCurveIsogeny(E, [0,1,0,1]) sage: phi.degree() 4 sage: E = EllipticCurve(GF(31), [1,0,0,1,2]) sage: phi = EllipticCurveIsogeny(E, [17, 1]) sage: phi.degree() 3
Degrees are multiplicative, so the degree of a composite isogeny is the product of the degrees of the individual factors:
sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite sage: E = EllipticCurve(GF(419), [1,0]) sage: P, = E.gens() sage: phi = EllipticCurveHom_composite(E, P+P) sage: phi.degree() 210 sage: phi.degree() == prod(f.degree() for f in phi.factors()) True
Isomorphisms always have degree
by definition:sage: E1 = EllipticCurve([1,2,3,4,5]) sage: E2 = EllipticCurve_from_j(E1.j_invariant()) sage: E1.isomorphism_to(E2).degree() 1
- dual()#
Return the dual of this elliptic-curve morphism.
Implemented by child classes. For examples, see:
sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.dual()
sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.dual()
sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.dual()
sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.dual()
- formal(prec=20)#
Return the formal isogeny associated to this elliptic-curve morphism as a power series in the variable
on the domain curve.INPUT:
prec
– (default: 20), the precision with which the computations in the formal group are carried out.
EXAMPLES:
sage: E = EllipticCurve(GF(13),[1,7]) sage: phi = E.isogeny(E(10,4)) sage: phi.formal() t + 12*t^13 + 2*t^17 + 8*t^19 + 2*t^21 + O(t^23)
sage: E = EllipticCurve([0,1]) sage: phi = E.isogeny(E(2,3)) sage: phi.formal(prec=10) t + 54*t^5 + 255*t^7 + 2430*t^9 + 19278*t^11 + O(t^13)
sage: E = EllipticCurve('11a2') sage: R.<x> = QQ[] sage: phi = E.isogeny(x^2 + 101*x + 12751/5) sage: phi.formal(prec=7) t - 2724/5*t^5 + 209046/5*t^7 - 4767/5*t^8 + 29200946/5*t^9 + O(t^10)
- is_injective()#
Determine whether or not this morphism has trivial kernel.
EXAMPLES:
sage: E = EllipticCurve('11a1') sage: R.<x> = QQ[] sage: f = x^2 + x - 29/5 sage: phi = EllipticCurveIsogeny(E, f) sage: phi.is_injective() False sage: phi = EllipticCurveIsogeny(E, R(1)) sage: phi.is_injective() True
sage: F = GF(7) sage: E = EllipticCurve(j=F(0)) sage: phi = EllipticCurveIsogeny(E, [ E((0,-1)), E((0,1))]) sage: phi.is_injective() False sage: phi = EllipticCurveIsogeny(E, E(0)) sage: phi.is_injective() True
- is_normalized()#
Determine whether this morphism is a normalized isogeny.
Note
An isogeny
between two given Weierstrass equations is said to be normalized if the , where and are the invariant differentials on and corresponding to the given equation.EXAMPLES:
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism sage: E = EllipticCurve(GF(7), [0,0,0,1,0]) sage: R.<x> = GF(7)[] sage: phi = EllipticCurveIsogeny(E, x) sage: phi.is_normalized() True sage: isom = WeierstrassIsomorphism(phi.codomain(), (3, 0, 0, 0)) sage: phi = isom * phi sage: phi.is_normalized() False sage: isom = WeierstrassIsomorphism(phi.codomain(), (5, 0, 0, 0)) sage: phi = isom * phi sage: phi.is_normalized() True sage: isom = WeierstrassIsomorphism(phi.codomain(), (1, 1, 1, 1)) sage: phi = isom * phi sage: phi.is_normalized() True
sage: F = GF(2^5, 'alpha'); alpha = F.gen() sage: E = EllipticCurve(F, [1,0,1,1,1]) sage: R.<x> = F[] sage: phi = EllipticCurveIsogeny(E, x+1) sage: isom = WeierstrassIsomorphism(phi.codomain(), (alpha, 0, 0, 0)) sage: phi.is_normalized() True sage: phi = isom * phi sage: phi.is_normalized() False sage: isom = WeierstrassIsomorphism(phi.codomain(), (1/alpha, 0, 0, 0)) sage: phi = isom * phi sage: phi.is_normalized() True sage: isom = WeierstrassIsomorphism(phi.codomain(), (1, 1, 1, 1)) sage: phi = isom * phi sage: phi.is_normalized() True
sage: E = EllipticCurve('11a1') sage: R.<x> = QQ[] sage: f = x^3 - x^2 - 10*x - 79/4 sage: phi = EllipticCurveIsogeny(E, f) sage: isom = WeierstrassIsomorphism(phi.codomain(), (2, 0, 0, 0)) sage: phi.is_normalized() True sage: phi = isom * phi sage: phi.is_normalized() False sage: isom = WeierstrassIsomorphism(phi.codomain(), (1/2, 0, 0, 0)) sage: phi = isom * phi sage: phi.is_normalized() True sage: isom = WeierstrassIsomorphism(phi.codomain(), (1, 1, 1, 1)) sage: phi = isom * phi sage: phi.is_normalized() True
ALGORITHM: We check if
scaling_factor()
returns .
- is_separable()#
Determine whether or not this morphism is separable.
Implemented by child classes. For examples, see:
sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.is_separable()
sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.is_separable()
sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.is_separable()
sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.is_separable()
- is_surjective()#
Determine whether or not this morphism is surjective.
Note
This method currently always returns
True
, since a non-constant map of algebraic curves must be surjective, and Sage does not yet implement the constant zero map. This will probably change in the future.EXAMPLES:
sage: E = EllipticCurve('11a1') sage: R.<x> = QQ[] sage: f = x^2 + x - 29/5 sage: phi = EllipticCurveIsogeny(E, f) sage: phi.is_surjective() True
sage: E = EllipticCurve(GF(7), [0,0,0,1,0]) sage: phi = EllipticCurveIsogeny(E, E((0,0))) sage: phi.is_surjective() True
sage: F = GF(2^5, 'omega') sage: E = EllipticCurve(j=F(0)) sage: R.<x> = F[] sage: phi = EllipticCurveIsogeny(E, x) sage: phi.is_surjective() True
- is_zero()#
Check whether this elliptic-curve morphism is the zero map.
Note
This function currently always returns
True
as Sage does not yet implement the constant zero morphism. This will probably change in the future.EXAMPLES:
sage: E = EllipticCurve(j=GF(7)(0)) sage: phi = EllipticCurveIsogeny(E, [E(0,1), E(0,-1)]) sage: phi.is_zero() False
- kernel_polynomial()#
Return the kernel polynomial of this elliptic-curve morphism.
Implemented by child classes. For examples, see:
sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.kernel_polynomial()
sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.kernel_polynomial()
sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.kernel_polynomial()
sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.kernel_polynomial()
- rational_maps()#
Return the pair of explicit rational maps defining this elliptic-curve morphism as fractions of bivariate polynomials in
and .Implemented by child classes. For examples, see:
sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.rational_maps()
sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.rational_maps()
sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.rational_maps()
sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.rational_maps()
- scaling_factor()#
Return the Weierstrass scaling factor associated to this elliptic-curve morphism.
The scaling factor is the constant
(in the base field) such that , where is this morphism and are the standard Weierstrass differentials on defined by .Implemented by child classes. For examples, see:
- x_rational_map()#
Return the
-coordinate rational map of this elliptic-curve morphism as a univariate rational expression in .Implemented by child classes. For examples, see:
sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.x_rational_map()
sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.x_rational_map()
sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.x_rational_map()
sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.x_rational_map()
- sage.schemes.elliptic_curves.hom.compare_via_evaluation(left, right)#
Test if two elliptic-curve morphisms are equal by evaluating them at enough points.
INPUT:
left
,right
–EllipticCurveHom
objects
ALGORITHM:
We use the fact that two isogenies of equal degree
must be the same if and only if they behave identically on more than points. (It suffices to check this on a few points that generate a large enough subgroup.)If the domain curve does not have sufficiently many rational points, the base field is extended first: Taking an extension of degree
suffices.EXAMPLES:
sage: E = EllipticCurve(GF(83), [1,0]) sage: phi = E.isogeny(12*E.0, model='montgomery'); phi Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 83 to Elliptic Curve defined by y^2 = x^3 + 70*x^2 + x over Finite Field of size 83 sage: psi = phi.dual(); psi Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + 70*x^2 + x over Finite Field of size 83 to Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 83 sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite sage: mu = EllipticCurveHom_composite.from_factors([phi, psi]) sage: from sage.schemes.elliptic_curves.hom import compare_via_evaluation sage: compare_via_evaluation(mu, E.scalar_multiplication(7)) True
See also
sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite._richcmp_()
- sage.schemes.elliptic_curves.hom.find_post_isomorphism(phi, psi)#
Given two isogenies
and which are equal up to post-isomorphism defined over the same field, find that isomorphism.In other words, this function computes an isomorphism
such that .ALGORITHM:
Start with a list of all isomorphisms
. Then repeatedly evaluate and at random points to filter the list for isomorphisms with . Once only one candidate is left, return it. Periodically extend the base field to avoid getting stuck (say, if all candidate isomorphisms act the same on all rational points).EXAMPLES:
sage: from sage.schemes.elliptic_curves.hom import find_post_isomorphism sage: E = EllipticCurve(GF(7^2), [1,0]) sage: f = E.scalar_multiplication(1) sage: g = choice(E.automorphisms()) sage: find_post_isomorphism(f, g) == g True
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite sage: F.<i> = GF(883^2, modulus=x^2+1) sage: E = EllipticCurve(F, [1,0]) sage: P = E.lift_x(117) sage: Q = E.lift_x(774) sage: w = WeierstrassIsomorphism(E, [i,0,0,0]) sage: phi = EllipticCurveHom_composite(E, [P,w(Q)]) * w sage: psi = EllipticCurveHom_composite(E, [Q,w(P)]) sage: phi.kernel_polynomial() == psi.kernel_polynomial() True sage: find_post_isomorphism(phi, psi) Elliptic-curve morphism: From: Elliptic Curve defined by y^2 = x^3 + 320*x + 482 over Finite Field in i of size 883^2 To: Elliptic Curve defined by y^2 = x^3 + 320*x + 401 over Finite Field in i of size 883^2 Via: (u,r,s,t) = (882*i, 0, 0, 0)