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:

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
characteristic_polynomial()[source]

Return the characteristic polynomial of this elliptic-curve morphism, which must be an endomorphism.

See also

EXAMPLES:

sage: E = EllipticCurve(QQ, [42, 42])
sage: m5 = E.scalar_multiplication(5)
sage: 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
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

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 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
divide_left(psi)[source]

Return an isogeny χ such that ψχ=φ, where φ is this isogeny, if such a χ exists.

EXAMPLES:

sage: E = EllipticCurve('54.b2')
sage: K = next(T for T in E.torsion_points() if T.order() == 9)
sage: phi, psi = E.isogeny(K).factors()
sage: chain = psi * phi; chain
Composite morphism of degree 9 = 3^2:
  From: Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 14*x + 29 over Rational Field
  To:   Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 2324*x - 43091 over Rational Field
sage: chain.divide_right(phi)
Fractional elliptic-curve morphism of degree 3:
  Numerator:   Composite morphism of degree 27 = 3^3:
  From: Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 + 106*x - 323 over Rational Field
  To:   Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 2324*x - 43091 over Rational Field
  Denominator: 3
sage: chain.divide_right(phi) == psi
True
divide_right(psi)[source]

Return an isogeny χ such that χψ=φ, where φ is this isogeny, if such a χ exists.

EXAMPLES:

sage: E = EllipticCurve('54.b2')
sage: K = next(T for T in E.torsion_points() if T.order() == 9)
sage: phi, psi = E.isogeny(K).factors()
sage: chain = psi * phi; chain
Composite morphism of degree 9 = 3^2:
  From: Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 14*x + 29 over Rational Field
  To:   Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 2324*x - 43091 over Rational Field
sage: chain.divide_left(psi)
Fractional elliptic-curve morphism of degree 3:
  Numerator:   Composite morphism of degree 27 = 3^3:
  From: Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 14*x + 29 over Rational Field
  To:   Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 + 106*x - 323 over Rational Field
  Denominator: 3
sage: chain.divide_left(psi) == phi
True
dual()[source]

Return the dual of this elliptic-curve morphism.

Implemented by child classes. For examples, see:

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)
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)
inseparable_degree()[source]

Return the inseparable degree of this isogeny.

Implemented by child classes. For examples, see:

inverse_image(Q, all)[source]

Return an arbitrary element P in the domain such that self(P) == Q, or raise ValueError if no such element exists.

INPUT:

  • Q – a point

  • all – if true, returns an iterator over all points in the inverse image

EXAMPLES:

sage: E.<P, Q> = EllipticCurve(GF(5^2), [1, 2, 3, 3, 1])
sage: f = E.isogeny([P*3])
sage: f(f.inverse_image(f(Q))) == f(Q)
True
sage: E.scalar_multiplication(-1).inverse_image(P) == -P
True
sage: f.inverse_image(f.codomain().0)
Traceback (most recent call last):
...
ValueError: ...
sage: len(list(f.inverse_image(f(Q), all=True)))
2

Check that the result is consistent with division_points():

sage: E = EllipticCurve('37a'); E
Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
sage: P = E(0, -1)
sage: (P * 5).division_points(5)
[(0 : -1 : 1)]
sage: E.scalar_multiplication(5).inverse_image(P * 5)
(0 : -1 : 1)

Points from wrong curves cannot be passed in:

sage: f.inverse_image(Q)
Traceback (most recent call last):
...
TypeError: input must be a point in the codomain
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
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
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
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
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
is_normalized()[source]

Determine whether this morphism is a normalized isogeny.

Note

An isogeny φ:E1E2 between two given Weierstrass equations is said to be normalized if the φ(ω2)=ω1, where ω1 and ω2 are the invariant differentials on E1 and E2 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 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
sage: E = EllipticCurve('11a1')
sage: phi = EllipticCurveIsogeny(E, E.torsion_points())
sage: 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}
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
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
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
sage: E = EllipticCurve(GF(17), [0,0,0,3,0])
sage: phi = E.isogeny(E((1,2)), algorithm='velusqrt')
sage: 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
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()[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
kernel_points()[source]

Return an iterator over the points in the kernel of this elliptic-curve morphism.

EXAMPLES:

sage: E.<P, Q> = EllipticCurve(GF(5^2), [1, 2, 3, 3, 1])
sage: f = E.isogeny([P*3, Q*3])
sage: set(f.kernel_points())
{(0 : 1 : 0), (4 : 4 : 1), (2*z2 + 4 : 4*z2 + 4 : 1), (3*z2 + 1 : z2 + 3 : 1)}

In the inseparable case:

sage: E = EllipticCurve(GF(23), [1,1])
sage: set(E.scalar_multiplication(23).kernel_points())
{(0 : 1 : 0)}

Check that the result is consistent with division_points():

sage: set(E.scalar_multiplication(4).kernel_points()) == set(E(0).division_points(4))
True
kernel_polynomial()[source]

Return the kernel polynomial of this elliptic-curve morphism.

Implemented by child classes. For examples, see:

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 morphism

  • codomain_gens – basis (R,S) of the n-torsion on the codomain of this morphism, or (default) None if self is an endomorphism

OUTPUT:

A 2×2 matrix M over Z/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

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

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

See also

To compute a basis of the n-torsion, you may use torsion_basis().

minimal_polynomial()[source]

Return a minimal polynomial of the kernel subgroup of this isogeny, as defined in [EPSV2023], Definition 15: That is, some polynomial f such that the points on the domain curve whose x-coordinates are roots of f generate the kernel of this isogeny.

See also

EllipticCurve_field.kernel_polynomial_from_divisor()

EXAMPLES:

sage: E = EllipticCurve(GF(419), [32, 41])
sage: phi = E.isogeny(E.lift_x(30)); phi
Isogeny of degree 7
 from Elliptic Curve defined by y^2 = x^3 + 32*x + 41 over Finite Field of size 419
 to Elliptic Curve defined by y^2 = x^3 + 316*x + 241 over Finite Field of size 419
sage: f = phi.minimal_polynomial(); f  # random -- one of x+161, x+201, x+389
x + 161
sage: f.divides(phi.kernel_polynomial())
True
sage: E.kernel_polynomial_from_divisor(f, 7) == phi.kernel_polynomial()
True

It also works for rational isogenies with irrational kernel points:

sage: E = EllipticCurve(GF(127^2), [1,0])
sage: phi = E.isogenies_prime_degree(17)[0]; phi
Isogeny of degree 17
  from Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2
  to Elliptic Curve defined by y^2 = x^3 + (16*z2+26)*x over Finite Field in z2 of size 127^2
sage: phi.kernel_polynomial()
x^8 + (68*z2 + 97)*x^6 + (59*z2 + 40)*x^4 + (59*z2 + 38)*x^2 + 4*z2 + 13
sage: phi.kernel_polynomial().factor()
(x^4 + (11*z2 + 32)*x^2 + 48*z2 + 70) * (x^4 + (57*z2 + 65)*x^2 + 20*z2 + 25)
sage: phi.minimal_polynomial().factor()
x^4 + (57*z2 + 65)*x^2 + 20*z2 + 25
push_subgroup(f)[source]

Given a minimal polynomial (see minimal_polynomial()) of a subgroup G of the domain curve of this isogeny, return a minimal polynomial of the image of G under this isogeny.

ALGORITHM: [EPSV2023], Algorithm 5 (PushSubgroup)

EXAMPLES:

sage: E = EllipticCurve(GF(419), [32, 41])
sage: K = E.lift_x(30)
sage: phi = E.isogeny(K); phi
Isogeny of degree 7
  from Elliptic Curve defined by y^2 = x^3 + 32*x + 41 over Finite Field of size 419
  to Elliptic Curve defined by y^2 = x^3 + 316*x + 241 over Finite Field of size 419
sage: psi = E.isogeny(E.lift_x(54), algorithm='factored'); psi
Composite morphism of degree 15 = 3*5:
  From: Elliptic Curve defined by y^2 = x^3 + 32*x + 41 over Finite Field of size 419
  To:   Elliptic Curve defined by y^2 = x^3 + 36*x + 305 over Finite Field of size 419
sage: f = phi.minimal_polynomial(); f   # random -- one of x+161, x+201, x+389
x + 161
sage: g = psi.push_subgroup(f); g       # random -- one of x+148, x+333, x+249
x + 148
sage: h = psi.codomain().kernel_polynomial_from_divisor(g, phi.degree()); h
x^3 + 311*x^2 + 196*x + 44
sage: chi = psi.codomain().isogeny(h); chi
Isogeny of degree 7
  from Elliptic Curve defined by y^2 = x^3 + 36*x + 305 over Finite Field of size 419
  to Elliptic Curve defined by y^2 = x^3 + 186*x + 37 over Finite Field of size 419
sage: (chi * psi)(K)
(0 : 1 : 0)

It also works for rational isogenies with irrational kernel points:

sage: E = EllipticCurve(GF(127^2), [1,0])
sage: phi = E.isogenies_prime_degree(13)[0]; phi
Isogeny of degree 13
  from Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2
  to Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2
sage: psi = E.isogenies_prime_degree(17)[0]; psi
Isogeny of degree 17
  from Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2
  to Elliptic Curve defined by y^2 = x^3 + (16*z2+26)*x over Finite Field in z2 of size 127^2
sage: f_phi = phi.minimal_polynomial()
sage: g_phi = psi.push_subgroup(f_phi)
sage: h_phi = psi.codomain().kernel_polynomial_from_divisor(g_phi, phi.degree())
sage: phi_pushed = psi.codomain().isogeny(h_phi); phi_pushed
Isogeny of degree 13 from Elliptic Curve defined by y^2 = x^3 + (16*z2+26)*x over Finite Field in z2 of size 127^2 to Elliptic Curve defined by y^2 = x^3 + (110*z2+61)*x over Finite Field in z2 of size 127^2
sage: f_psi = psi.minimal_polynomial()
sage: g_psi = phi.push_subgroup(f_psi)
sage: h_psi = phi.codomain().kernel_polynomial_from_divisor(g_psi, psi.degree())
sage: psi_pushed = phi.codomain().isogeny(h_psi); psi_pushed
Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2 to Elliptic Curve defined by y^2 = x^3 + (16*z2+26)*x over Finite Field in z2 of size 127^2
sage: any(iso * psi_pushed * phi == phi_pushed * psi
....:     for iso in psi_pushed.codomain().isomorphisms(phi_pushed.codomain()))
True

If the subgroup represented by f intersects nontrivially with the kernel of this isogeny, the method still works correctly:

sage: E = EllipticCurve(GF(419), [1,0])
sage: phi = next(E.isogenies_degree(7)); phi
Isogeny of degree 7
  from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 419
  to Elliptic Curve defined by y^2 = x^3 + 285*x + 87 over Finite Field of size 419
sage: psi = next(E.isogenies_degree(21)); psi
Composite morphism of degree 21 = 7*3:
  From: Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 419
  To:   Elliptic Curve defined by y^2 = x^3 + 134*x + 230 over Finite Field of size 419
sage: phi.kernel_polynomial().gcd(psi.kernel_polynomial())
x^3 + 274*x^2 + 350*x + 6
sage: f = phi.minimal_polynomial()
sage: psi.push_subgroup(f)
1
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:

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 φω2=uω1, where φ:E1E2 is this morphism and ωi are the standard Weierstrass differentials on Ei defined by dx/(2y+a1x+a3).

Implemented by child classes. For examples, see:

separable_degree()[source]

Return the separable degree of this isogeny.

The separable degree is the result of dividing the degree() by the inseparable_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
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
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
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.hom.compare_via_evaluation(left, right)[source]

Test if two elliptic-curve morphisms are equal by evaluating them at enough points.

INPUT:

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

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 , we find an order- point P on E and use a discrete-logarithm calculation to find the unique scalar t{0,...,1} such that φ2(P)+[deg(φ)]P=[t]φ(P). Then t equals the trace of φ modulo , 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

It works over Q:

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

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
sage.schemes.elliptic_curves.hom.find_post_isomorphism(phi, psi)[source]

Given two isogenies ϕ:EE and ψ:EE which are equal up to post-isomorphism defined over the same field, find that isomorphism.

In other words, this function computes an isomorphism α:EE such that αϕ=ψ.

ALGORITHM:

Start with a list of all isomorphisms EE. Then repeatedly evaluate ϕ and ψ at random points P to filter the list for isomorphisms α with α(ϕ(P))=ψ(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
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)