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):
EllipticCurveHom_sum
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.Lorenz Panny (2022):
matrix_on_subgroup()
Lorenz Panny (2023):
trace()
,characteristic_polynomial()
- class sage.schemes.elliptic_curves.hom.EllipticCurveHom(*args, **kwds)[source]#
Bases:
Morphism
Base class for elliptic-curve morphisms.
- as_morphism()[source]#
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
>>> from sage.all import * >>> k = GF(Integer(11)) >>> E = EllipticCurve(k, [Integer(1),Integer(1)]) >>> Q = E(Integer(6),Integer(5)) >>> phi = E.isogeny(Q) >>> mor = phi.as_morphism() >>> mor.domain() == E True >>> mor.codomain() == phi.codomain() True >>> mor(Q) == phi(Q) True
- characteristic_polynomial()[source]#
Return the characteristic polynomial of this elliptic-curve morphism, which must be an endomorphism.
EXAMPLES:
sage: E = EllipticCurve(QQ, [42, 42]) sage: m5 = E.scalar_multiplication(5) sage: m5.characteristic_polynomial() x^2 - 10*x + 25
>>> from sage.all import * >>> E = EllipticCurve(QQ, [Integer(42), Integer(42)]) >>> m5 = E.scalar_multiplication(Integer(5)) >>> m5.characteristic_polynomial() x^2 - 10*x + 25
sage: E = EllipticCurve(GF(71), [42, 42]) sage: pi = E.frobenius_endomorphism() sage: pi.characteristic_polynomial() x^2 - 8*x + 71 sage: E.frobenius().charpoly() x^2 - 8*x + 71
>>> from sage.all import * >>> E = EllipticCurve(GF(Integer(71)), [Integer(42), Integer(42)]) >>> pi = E.frobenius_endomorphism() >>> pi.characteristic_polynomial() x^2 - 8*x + 71 >>> E.frobenius().charpoly() x^2 - 8*x + 71
- degree()[source]#
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
>>> from sage.all import * >>> E = EllipticCurve(QQ, [Integer(0),Integer(0),Integer(0),Integer(1),Integer(0)]) >>> phi = EllipticCurveIsogeny(E, E((Integer(0),Integer(0)))) >>> phi.degree() 2 >>> phi = EllipticCurveIsogeny(E, [Integer(0),Integer(1),Integer(0),Integer(1)]) >>> phi.degree() 4 >>> E = EllipticCurve(GF(Integer(31)), [Integer(1),Integer(0),Integer(0),Integer(1),Integer(2)]) >>> phi = EllipticCurveIsogeny(E, [Integer(17), Integer(1)]) >>> 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
>>> from sage.all import * >>> from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite >>> E = EllipticCurve(GF(Integer(419)), [Integer(1),Integer(0)]) >>> P, = E.gens() >>> phi = EllipticCurveHom_composite(E, P+P) >>> phi.degree() 210 >>> phi.degree() == prod(f.degree() for f in phi.factors()) True
Isomorphisms always have degree \(1\) 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
>>> from sage.all import * >>> E1 = EllipticCurve([Integer(1),Integer(2),Integer(3),Integer(4),Integer(5)]) >>> E2 = EllipticCurve_from_j(E1.j_invariant()) >>> E1.isomorphism_to(E2).degree() 1
- dual()[source]#
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_sum.EllipticCurveHom_sum.dual()
sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.dual()
sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.dual()
- formal(prec=20)[source]#
Return the formal isogeny associated to this elliptic-curve morphism as a power series in the variable \(t=-x/y\) 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)
>>> from sage.all import * >>> E = EllipticCurve(GF(Integer(13)),[Integer(1),Integer(7)]) >>> phi = E.isogeny(E(Integer(10),Integer(4))) >>> 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)
>>> from sage.all import * >>> E = EllipticCurve([Integer(0),Integer(1)]) >>> phi = E.isogeny(E(Integer(2),Integer(3))) >>> phi.formal(prec=Integer(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)
>>> from sage.all import * >>> E = EllipticCurve('11a2') >>> R = QQ['x']; (x,) = R._first_ngens(1) >>> phi = E.isogeny(x**Integer(2) + Integer(101)*x + Integer(12751)/Integer(5)) >>> phi.formal(prec=Integer(7)) t - 2724/5*t^5 + 209046/5*t^7 - 4767/5*t^8 + 29200946/5*t^9 + O(t^10)
- inseparable_degree()[source]#
Return the inseparable degree of this isogeny.
Implemented by child classes. For examples, see:
sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.inseparable_degree()
sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.inseparable_degree()
sage.schemes.elliptic_curves.hom_sum.EllipticCurveHom_sum.inseparable_degree()
sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.inseparable_degree()
sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.inseparable_degree()
- is_injective()[source]#
Determine whether or not this morphism has trivial kernel.
The kernel is trivial if and only if this morphism is a purely inseparable isogeny.
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
>>> from sage.all import * >>> E = EllipticCurve('11a1') >>> R = QQ['x']; (x,) = R._first_ngens(1) >>> f = x**Integer(2) + x - Integer(29)/Integer(5) >>> phi = EllipticCurveIsogeny(E, f) >>> phi.is_injective() False >>> phi = EllipticCurveIsogeny(E, R(Integer(1))) >>> 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
>>> from sage.all import * >>> F = GF(Integer(7)) >>> E = EllipticCurve(j=F(Integer(0))) >>> phi = EllipticCurveIsogeny(E, [ E((Integer(0),-Integer(1))), E((Integer(0),Integer(1)))]) >>> phi.is_injective() False >>> phi = EllipticCurveIsogeny(E, E(Integer(0))) >>> phi.is_injective() True
sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite sage: E = EllipticCurve([1,0]) sage: phi = EllipticCurveHom_composite(E, E(0,0)) sage: phi.is_injective() False sage: E = EllipticCurve_from_j(GF(3).algebraic_closure()(0)) sage: nu = EllipticCurveHom_composite.from_factors(E.automorphisms()) sage: nu Composite morphism of degree 1 = 1^12: From: Elliptic Curve defined by y^2 = x^3 + x over Algebraic closure of Finite Field of size 3 To: Elliptic Curve defined by y^2 = x^3 + x over Algebraic closure of Finite Field of size 3 sage: nu.is_injective() True
>>> from sage.all import * >>> from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite >>> E = EllipticCurve([Integer(1),Integer(0)]) >>> phi = EllipticCurveHom_composite(E, E(Integer(0),Integer(0))) >>> phi.is_injective() False >>> E = EllipticCurve_from_j(GF(Integer(3)).algebraic_closure()(Integer(0))) >>> nu = EllipticCurveHom_composite.from_factors(E.automorphisms()) >>> nu Composite morphism of degree 1 = 1^12: From: Elliptic Curve defined by y^2 = x^3 + x over Algebraic closure of Finite Field of size 3 To: Elliptic Curve defined by y^2 = x^3 + x over Algebraic closure of Finite Field of size 3 >>> nu.is_injective() True
sage: E = EllipticCurve(GF(23), [1,0]) sage: E.scalar_multiplication(4).is_injective() False sage: E.scalar_multiplication(5).is_injective() False sage: E.scalar_multiplication(1).is_injective() True sage: E.scalar_multiplication(-1).is_injective() True sage: E.scalar_multiplication(23).is_injective() True sage: E.scalar_multiplication(-23).is_injective() True sage: E.scalar_multiplication(0).is_injective() False
>>> from sage.all import * >>> E = EllipticCurve(GF(Integer(23)), [Integer(1),Integer(0)]) >>> E.scalar_multiplication(Integer(4)).is_injective() False >>> E.scalar_multiplication(Integer(5)).is_injective() False >>> E.scalar_multiplication(Integer(1)).is_injective() True >>> E.scalar_multiplication(-Integer(1)).is_injective() True >>> E.scalar_multiplication(Integer(23)).is_injective() True >>> E.scalar_multiplication(-Integer(23)).is_injective() True >>> E.scalar_multiplication(Integer(0)).is_injective() False
sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius sage: E = EllipticCurve(GF(11), [1,1]) sage: pi = EllipticCurveHom_frobenius(E, 5) sage: pi.is_injective() True
>>> from sage.all import * >>> from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius >>> E = EllipticCurve(GF(Integer(11)), [Integer(1),Integer(1)]) >>> pi = EllipticCurveHom_frobenius(E, Integer(5)) >>> pi.is_injective() True
- is_normalized()[source]#
Determine whether this morphism is a normalized isogeny.
Note
An isogeny \(\varphi\colon E_1\to E_2\) between two given Weierstrass equations is said to be normalized if the \(\varphi^*(\omega_2) = \omega_1\), where \(\omega_1\) and \(\omega_2\) are the invariant differentials on \(E_1\) and \(E_2\) 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
>>> from sage.all import * >>> from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism >>> E = EllipticCurve(GF(Integer(7)), [Integer(0),Integer(0),Integer(0),Integer(1),Integer(0)]) >>> R = GF(Integer(7))['x']; (x,) = R._first_ngens(1) >>> phi = EllipticCurveIsogeny(E, x) >>> phi.is_normalized() True >>> isom = WeierstrassIsomorphism(phi.codomain(), (Integer(3), Integer(0), Integer(0), Integer(0))) >>> phi = isom * phi >>> phi.is_normalized() False >>> isom = WeierstrassIsomorphism(phi.codomain(), (Integer(5), Integer(0), Integer(0), Integer(0))) >>> phi = isom * phi >>> phi.is_normalized() True >>> isom = WeierstrassIsomorphism(phi.codomain(), (Integer(1), Integer(1), Integer(1), Integer(1))) >>> phi = isom * phi >>> 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
>>> from sage.all import * >>> F = GF(Integer(2)**Integer(5), 'alpha'); alpha = F.gen() >>> E = EllipticCurve(F, [Integer(1),Integer(0),Integer(1),Integer(1),Integer(1)]) >>> R = F['x']; (x,) = R._first_ngens(1) >>> phi = EllipticCurveIsogeny(E, x+Integer(1)) >>> isom = WeierstrassIsomorphism(phi.codomain(), (alpha, Integer(0), Integer(0), Integer(0))) >>> phi.is_normalized() True >>> phi = isom * phi >>> phi.is_normalized() False >>> isom = WeierstrassIsomorphism(phi.codomain(), (Integer(1)/alpha, Integer(0), Integer(0), Integer(0))) >>> phi = isom * phi >>> phi.is_normalized() True >>> isom = WeierstrassIsomorphism(phi.codomain(), (Integer(1), Integer(1), Integer(1), Integer(1))) >>> phi = isom * phi >>> 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
>>> from sage.all import * >>> E = EllipticCurve('11a1') >>> R = QQ['x']; (x,) = R._first_ngens(1) >>> f = x**Integer(3) - x**Integer(2) - Integer(10)*x - Integer(79)/Integer(4) >>> phi = EllipticCurveIsogeny(E, f) >>> isom = WeierstrassIsomorphism(phi.codomain(), (Integer(2), Integer(0), Integer(0), Integer(0))) >>> phi.is_normalized() True >>> phi = isom * phi >>> phi.is_normalized() False >>> isom = WeierstrassIsomorphism(phi.codomain(), (Integer(1)/Integer(2), Integer(0), Integer(0), Integer(0))) >>> phi = isom * phi >>> phi.is_normalized() True >>> isom = WeierstrassIsomorphism(phi.codomain(), (Integer(1), Integer(1), Integer(1), Integer(1))) >>> phi = isom * phi >>> phi.is_normalized() True
ALGORITHM: We check if
scaling_factor()
returns \(1\).
- is_separable()[source]#
Determine whether or not this morphism is a separable isogeny.
EXAMPLES:
sage: E = EllipticCurve(GF(17), [0,0,0,3,0]) sage: phi = EllipticCurveIsogeny(E, E((0,0))) sage: phi.is_separable() True
>>> from sage.all import * >>> E = EllipticCurve(GF(Integer(17)), [Integer(0),Integer(0),Integer(0),Integer(3),Integer(0)]) >>> phi = EllipticCurveIsogeny(E, E((Integer(0),Integer(0)))) >>> phi.is_separable() True
sage: E = EllipticCurve('11a1') sage: phi = EllipticCurveIsogeny(E, E.torsion_points()) sage: phi.is_separable() True
>>> from sage.all import * >>> E = EllipticCurve('11a1') >>> phi = EllipticCurveIsogeny(E, E.torsion_points()) >>> phi.is_separable() True
sage: E = EllipticCurve(GF(31337), [0,1]) # needs sage.rings.finite_rings sage: {f.is_separable() for f in E.automorphisms()} # needs sage.rings.finite_rings {True}
>>> from sage.all import * >>> E = EllipticCurve(GF(Integer(31337)), [Integer(0),Integer(1)]) # needs sage.rings.finite_rings >>> {f.is_separable() for f in E.automorphisms()} # needs sage.rings.finite_rings {True}
sage: # needs sage.rings.finite_rings sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite sage: E = EllipticCurve(GF(7^2), [3,2]) sage: P = E.lift_x(1) sage: phi = EllipticCurveHom_composite(E, P); phi Composite morphism of degree 7: From: Elliptic Curve defined by y^2 = x^3 + 3*x + 2 over Finite Field in z2 of size 7^2 To: Elliptic Curve defined by y^2 = x^3 + 3*x + 2 over Finite Field in z2 of size 7^2 sage: phi.is_separable() True
>>> from sage.all import * >>> # needs sage.rings.finite_rings >>> from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite >>> E = EllipticCurve(GF(Integer(7)**Integer(2)), [Integer(3),Integer(2)]) >>> P = E.lift_x(Integer(1)) >>> phi = EllipticCurveHom_composite(E, P); phi Composite morphism of degree 7: From: Elliptic Curve defined by y^2 = x^3 + 3*x + 2 over Finite Field in z2 of size 7^2 To: Elliptic Curve defined by y^2 = x^3 + 3*x + 2 over Finite Field in z2 of size 7^2 >>> phi.is_separable() True
sage: E = EllipticCurve(GF(11), [4,4]) sage: E.scalar_multiplication(11).is_separable() False sage: E.scalar_multiplication(-11).is_separable() False sage: E.scalar_multiplication(777).is_separable() True sage: E.scalar_multiplication(-1).is_separable() True sage: E.scalar_multiplication(77).is_separable() False sage: E.scalar_multiplication(121).is_separable() False
>>> from sage.all import * >>> E = EllipticCurve(GF(Integer(11)), [Integer(4),Integer(4)]) >>> E.scalar_multiplication(Integer(11)).is_separable() False >>> E.scalar_multiplication(-Integer(11)).is_separable() False >>> E.scalar_multiplication(Integer(777)).is_separable() True >>> E.scalar_multiplication(-Integer(1)).is_separable() True >>> E.scalar_multiplication(Integer(77)).is_separable() False >>> E.scalar_multiplication(Integer(121)).is_separable() False
sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius sage: E = EllipticCurve(GF(11), [1,1]) sage: pi = EllipticCurveHom_frobenius(E) sage: pi.degree() 11 sage: pi.is_separable() False sage: pi = EllipticCurveHom_frobenius(E, 0) sage: pi.degree() 1 sage: pi.is_separable() True
>>> from sage.all import * >>> from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius >>> E = EllipticCurve(GF(Integer(11)), [Integer(1),Integer(1)]) >>> pi = EllipticCurveHom_frobenius(E) >>> pi.degree() 11 >>> pi.is_separable() False >>> pi = EllipticCurveHom_frobenius(E, Integer(0)) >>> pi.degree() 1 >>> pi.is_separable() True
sage: E = EllipticCurve(GF(17), [0,0,0,3,0]) sage: phi = E.isogeny(E((1,2)), algorithm='velusqrt') sage: phi.is_separable() True
>>> from sage.all import * >>> E = EllipticCurve(GF(Integer(17)), [Integer(0),Integer(0),Integer(0),Integer(3),Integer(0)]) >>> phi = E.isogeny(E((Integer(1),Integer(2))), algorithm='velusqrt') >>> phi.is_separable() True
- is_surjective()[source]#
Determine whether or not this morphism is surjective.
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
>>> from sage.all import * >>> E = EllipticCurve('11a1') >>> R = QQ['x']; (x,) = R._first_ngens(1) >>> f = x**Integer(2) + x - Integer(29)/Integer(5) >>> phi = EllipticCurveIsogeny(E, f) >>> 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
>>> from sage.all import * >>> E = EllipticCurve(GF(Integer(7)), [Integer(0),Integer(0),Integer(0),Integer(1),Integer(0)]) >>> phi = EllipticCurveIsogeny(E, E((Integer(0),Integer(0)))) >>> 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
>>> from sage.all import * >>> F = GF(Integer(2)**Integer(5), 'omega') >>> E = EllipticCurve(j=F(Integer(0))) >>> R = F['x']; (x,) = R._first_ngens(1) >>> phi = EllipticCurveIsogeny(E, x) >>> phi.is_surjective() True
- is_zero()[source]#
Check whether this elliptic-curve morphism is the zero map.
EXAMPLES:
sage: E = EllipticCurve(j=GF(7)(0)) sage: phi = EllipticCurveIsogeny(E, [E(0,1), E(0,-1)]) sage: phi.is_zero() False
>>> from sage.all import * >>> E = EllipticCurve(j=GF(Integer(7))(Integer(0))) >>> phi = EllipticCurveIsogeny(E, [E(Integer(0),Integer(1)), E(Integer(0),-Integer(1))]) >>> phi.is_zero() False
- kernel_polynomial()[source]#
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_sum.EllipticCurveHom_sum.kernel_polynomial()
sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.kernel_polynomial()
sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.kernel_polynomial()
- matrix_on_subgroup(domain_gens, codomain_gens=None)[source]#
Return the matrix by which this isogeny acts on the \(n\)-torsion subgroup with respect to the given bases.
INPUT:
domain_gens
– basis \((P,Q)\) of some \(n\)-torsion subgroup on the domain of this elliptic-curve morphismcodomain_gens
– basis \((R,S)\) of the \(n\)-torsion on the codomain of this morphism, or (default)None
ifself
is an endomorphism
OUTPUT:
A \(2\times 2\) matrix \(M\) over \(\ZZ/n\), such that the image of any point \([a]P + [b]Q\) under this morphism equals \([c]R + [d]S\) where \((c\ d)^T = (a\ b) M\).
EXAMPLES:
sage: F.<i> = GF(419^2, modulus=[1,0,1]) sage: E = EllipticCurve(F, [1,0]) sage: P = E(3, 176*i) sage: Q = E(i+7, 67*i+48) sage: P.weil_pairing(Q, 420).multiplicative_order() 420 sage: iota = E.automorphisms()[2]; iota Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + x over Finite Field in i of size 419^2 Via: (u,r,s,t) = (i, 0, 0, 0) sage: iota^2 == E.scalar_multiplication(-1) True sage: mat = iota.matrix_on_subgroup((P,Q)); mat [301 386] [ 83 119] sage: mat.parent() Full MatrixSpace of 2 by 2 dense matrices over Ring of integers modulo 420 sage: iota(P) == 301*P + 386*Q True sage: iota(Q) == 83*P + 119*Q True sage: a,b = 123, 456 sage: c,d = vector((a,b)) * mat; (c,d) (111, 102) sage: iota(a*P + b*Q) == c*P + d*Q True
>>> from sage.all import * >>> F = GF(Integer(419)**Integer(2), modulus=[Integer(1),Integer(0),Integer(1)], names=('i',)); (i,) = F._first_ngens(1) >>> E = EllipticCurve(F, [Integer(1),Integer(0)]) >>> P = E(Integer(3), Integer(176)*i) >>> Q = E(i+Integer(7), Integer(67)*i+Integer(48)) >>> P.weil_pairing(Q, Integer(420)).multiplicative_order() 420 >>> iota = E.automorphisms()[Integer(2)]; iota Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + x over Finite Field in i of size 419^2 Via: (u,r,s,t) = (i, 0, 0, 0) >>> iota**Integer(2) == E.scalar_multiplication(-Integer(1)) True >>> mat = iota.matrix_on_subgroup((P,Q)); mat [301 386] [ 83 119] >>> mat.parent() Full MatrixSpace of 2 by 2 dense matrices over Ring of integers modulo 420 >>> iota(P) == Integer(301)*P + Integer(386)*Q True >>> iota(Q) == Integer(83)*P + Integer(119)*Q True >>> a,b = Integer(123), Integer(456) >>> c,d = vector((a,b)) * mat; (c,d) (111, 102) >>> iota(a*P + b*Q) == c*P + d*Q True
One important application of this is to compute generators of the kernel subgroup of an isogeny, when the \(n\)-torsion subgroup containing the kernel is accessible:
sage: K = E(83*i-16, 9*i-147) sage: K.order() 7 sage: phi = E.isogeny(K) sage: R,S = phi.codomain().gens() sage: mat = phi.matrix_on_subgroup((P,Q), (R,S)) sage: mat # random -- depends on R,S [124 263] [115 141] sage: kermat = mat.left_kernel_matrix(); kermat [300 60] sage: ker = [ZZ(v[0])*P + ZZ(v[1])*Q for v in kermat] sage: {phi(T) for T in ker} {(0 : 1 : 0)} sage: phi == E.isogeny(ker) True
>>> from sage.all import * >>> K = E(Integer(83)*i-Integer(16), Integer(9)*i-Integer(147)) >>> K.order() 7 >>> phi = E.isogeny(K) >>> R,S = phi.codomain().gens() >>> mat = phi.matrix_on_subgroup((P,Q), (R,S)) >>> mat # random -- depends on R,S [124 263] [115 141] >>> kermat = mat.left_kernel_matrix(); kermat [300 60] >>> ker = [ZZ(v[Integer(0)])*P + ZZ(v[Integer(1)])*Q for v in kermat] >>> {phi(T) for T in ker} {(0 : 1 : 0)} >>> phi == E.isogeny(ker) True
We can also compute the matrix of a Frobenius endomorphism (
EllipticCurveHom_frobenius
) on a large enough subgroup to verify point-counting results:sage: F.<a> = GF((101, 36)) sage: E = EllipticCurve(GF(101), [1,1]) sage: EE = E.change_ring(F) sage: P,Q = EE.torsion_basis(37) sage: pi = EE.frobenius_isogeny() sage: M = pi.matrix_on_subgroup((P,Q)) sage: M.parent() Full MatrixSpace of 2 by 2 dense matrices over Ring of integers modulo 37 sage: M.trace() 34 sage: E.trace_of_frobenius() -3
>>> from sage.all import * >>> F = GF((Integer(101), Integer(36)), names=('a',)); (a,) = F._first_ngens(1) >>> E = EllipticCurve(GF(Integer(101)), [Integer(1),Integer(1)]) >>> EE = E.change_ring(F) >>> P,Q = EE.torsion_basis(Integer(37)) >>> pi = EE.frobenius_isogeny() >>> M = pi.matrix_on_subgroup((P,Q)) >>> M.parent() Full MatrixSpace of 2 by 2 dense matrices over Ring of integers modulo 37 >>> M.trace() 34 >>> E.trace_of_frobenius() -3
See also
To compute a basis of the \(n\)-torsion, you may use
torsion_basis()
.
- rational_maps()[source]#
Return the pair of explicit rational maps defining this elliptic-curve morphism as fractions of bivariate polynomials in \(x\) and \(y\).
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_sum.EllipticCurveHom_sum.rational_maps()
sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.rational_maps()
sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.rational_maps()
- scaling_factor()[source]#
Return the Weierstrass scaling factor associated to this elliptic-curve morphism.
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 morphism and \(\omega_i\) are the standard Weierstrass differentials on \(E_i\) defined by \(\mathrm dx/(2y+a_1x+a_3)\).
Implemented by child classes. For examples, see:
sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.scaling_factor()
sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.scaling_factor()
sage.schemes.elliptic_curves.hom_sum.EllipticCurveHom_sum.scaling_factor()
sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.scaling_factor()
- separable_degree()[source]#
Return the separable degree of this isogeny.
The separable degree is the result of dividing the
degree()
by theinseparable_degree()
.EXAMPLES:
sage: E = EllipticCurve(GF(11), [5,5]) sage: E.is_supersingular() False sage: E.scalar_multiplication(-77).separable_degree() 539 sage: E = EllipticCurve(GF(11), [5,0]) sage: E.is_supersingular() True sage: E.scalar_multiplication(-77).separable_degree() 49
>>> from sage.all import * >>> E = EllipticCurve(GF(Integer(11)), [Integer(5),Integer(5)]) >>> E.is_supersingular() False >>> E.scalar_multiplication(-Integer(77)).separable_degree() 539 >>> E = EllipticCurve(GF(Integer(11)), [Integer(5),Integer(0)]) >>> E.is_supersingular() True >>> E.scalar_multiplication(-Integer(77)).separable_degree() 49
- trace()[source]#
Return the trace of this elliptic-curve morphism, which must be an endomorphism.
ALGORITHM:
compute_trace_generic()
EXAMPLES:
sage: E = EllipticCurve(QQ, [42, 42]) sage: m5 = E.scalar_multiplication(5) sage: m5.trace() 10
>>> from sage.all import * >>> E = EllipticCurve(QQ, [Integer(42), Integer(42)]) >>> m5 = E.scalar_multiplication(Integer(5)) >>> m5.trace() 10
sage: E = EllipticCurve(GF(71^2), [45, 45]) sage: P = E.lift_x(27) sage: P.order() 71 sage: tau = E.isogeny(P, codomain=E) sage: tau.trace() -1
>>> from sage.all import * >>> E = EllipticCurve(GF(Integer(71)**Integer(2)), [Integer(45), Integer(45)]) >>> P = E.lift_x(Integer(27)) >>> P.order() 71 >>> tau = E.isogeny(P, codomain=E) >>> tau.trace() -1
- x_rational_map()[source]#
Return the \(x\)-coordinate rational map of this elliptic-curve morphism as a univariate rational expression in \(x\).
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_sum.EllipticCurveHom_sum.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)[source]#
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 \(d\) must be the same if and only if they behave identically on more than \(4d\) 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 \(O(\log(d))\) 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
>>> from sage.all import * >>> E = EllipticCurve(GF(Integer(83)), [Integer(1),Integer(0)]) >>> phi = E.isogeny(Integer(12)*E.gen(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 >>> 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 >>> from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite >>> mu = EllipticCurveHom_composite.from_factors([phi, psi]) >>> from sage.schemes.elliptic_curves.hom import compare_via_evaluation >>> compare_via_evaluation(mu, E.scalar_multiplication(Integer(7))) True
See also
sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite._richcmp_()
- sage.schemes.elliptic_curves.hom.compute_trace_generic(phi)[source]#
Compute the trace of the given elliptic-curve endomorphism.
ALGORITHM: Simple variant of Schoof’s algorithm. For enough small primes \(\ell\), we find an order-\(\ell\) point \(P\) on \(E\) and use a discrete-logarithm calculation to find the unique scalar \(t_\ell \in \{0,...,\ell-1\}\) such that \(\varphi^2(P)+[\deg(\varphi)]P = [t_\ell]\varphi(P)\). Then \(t_\ell\) equals the trace of \(\varphi\) modulo \(\ell\), which can therefore be recovered using the Chinese remainder theorem.
EXAMPLES:
It works over finite fields:
sage: from sage.schemes.elliptic_curves.hom import compute_trace_generic sage: E = EllipticCurve(GF(31337), [1,1]) sage: compute_trace_generic(E.frobenius_endomorphism()) 314
>>> from sage.all import * >>> from sage.schemes.elliptic_curves.hom import compute_trace_generic >>> E = EllipticCurve(GF(Integer(31337)), [Integer(1),Integer(1)]) >>> compute_trace_generic(E.frobenius_endomorphism()) 314
It works over \(\QQ\):
sage: from sage.schemes.elliptic_curves.hom import compute_trace_generic sage: E = EllipticCurve(QQ, [1,2,3,4,5]) sage: dbl = E.scalar_multiplication(2) sage: compute_trace_generic(dbl) 4
>>> from sage.all import * >>> from sage.schemes.elliptic_curves.hom import compute_trace_generic >>> E = EllipticCurve(QQ, [Integer(1),Integer(2),Integer(3),Integer(4),Integer(5)]) >>> dbl = E.scalar_multiplication(Integer(2)) >>> compute_trace_generic(dbl) 4
It works over number fields (for a CM curve):
sage: from sage.schemes.elliptic_curves.hom import compute_trace_generic sage: x = polygen(QQ) sage: K.<t> = NumberField(5*x^2 - 2*x + 1) sage: E = EllipticCurve(K, [1,0]) sage: phi = E.isogeny([t,0,1], codomain=E) # phi = 2 + i sage: compute_trace_generic(phi) 4
>>> from sage.all import * >>> from sage.schemes.elliptic_curves.hom import compute_trace_generic >>> x = polygen(QQ) >>> K = NumberField(Integer(5)*x**Integer(2) - Integer(2)*x + Integer(1), names=('t',)); (t,) = K._first_ngens(1) >>> E = EllipticCurve(K, [Integer(1),Integer(0)]) >>> phi = E.isogeny([t,Integer(0),Integer(1)], codomain=E) # phi = 2 + i >>> compute_trace_generic(phi) 4
- sage.schemes.elliptic_curves.hom.find_post_isomorphism(phi, psi)[source]#
Given two isogenies \(\phi: E\to E'\) and \(\psi: E\to E''\) which are equal up to post-isomorphism defined over the same field, find that isomorphism.
In other words, this function computes an isomorphism \(\alpha: E'\to E''\) such that \(\alpha\circ\phi = \psi\).
ALGORITHM:
Start with a list of all isomorphisms \(E'\to E''\). Then repeatedly evaluate \(\phi\) and \(\psi\) at random points \(P\) to filter the list for isomorphisms \(\alpha\) with \(\alpha(\phi(P)) = \psi(P)\). 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
>>> from sage.all import * >>> from sage.schemes.elliptic_curves.hom import find_post_isomorphism >>> E = EllipticCurve(GF(Integer(7)**Integer(2)), [Integer(1),Integer(0)]) >>> f = E.scalar_multiplication(Integer(1)) >>> g = choice(E.automorphisms()) >>> 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: x = polygen(ZZ, 'x') 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)
>>> from sage.all import * >>> from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism >>> from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite >>> x = polygen(ZZ, 'x') >>> F = GF(Integer(883)**Integer(2), modulus=x**Integer(2)+Integer(1), names=('i',)); (i,) = F._first_ngens(1) >>> E = EllipticCurve(F, [Integer(1),Integer(0)]) >>> P = E.lift_x(Integer(117)) >>> Q = E.lift_x(Integer(774)) >>> w = WeierstrassIsomorphism(E, [i,Integer(0),Integer(0),Integer(0)]) >>> phi = EllipticCurveHom_composite(E, [P,w(Q)]) * w >>> psi = EllipticCurveHom_composite(E, [Q,w(P)]) >>> phi.kernel_polynomial() == psi.kernel_polynomial() True >>> 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)