Skip to content

Commit

Permalink
Trac #32826: scalar-multiplication endomorphisms of elliptic curves
Browse files Browse the repository at this point in the history
This ticket adds `EllipticCurveHom_scalar`, a new class encapsulating
scalar multiplications on elliptic curves. This serves two main
purposes:

1. It solves one of the motivations behind #8014 (faster
`multiplication_by_m_isogeny`).
2. Wrapping scalar multiplications as an `EllipticCurveHom` is an
important step towards implementing endomorphism rings (see #7368).

We also deprecate `.multiplication_by_m_isogeny()`: It should be
replaceable by `.scalar_multiplication()` in all cases.

URL: https://trac.sagemath.org/32826
Reported by: lorenz
Ticket author(s): Lorenz Panny
Reviewer(s): John Cremona
  • Loading branch information
Release Manager committed Jan 2, 2023
2 parents 4f11a75 + 884263e commit 98b22eb
Show file tree
Hide file tree
Showing 9 changed files with 712 additions and 46 deletions.
1 change: 1 addition & 0 deletions src/doc/en/reference/arithmetic_curves/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Maps between them
sage/schemes/elliptic_curves/ell_curve_isogeny
sage/schemes/elliptic_curves/hom_velusqrt
sage/schemes/elliptic_curves/hom_composite
sage/schemes/elliptic_curves/hom_scalar
sage/schemes/elliptic_curves/isogeny_small_degree


Expand Down
34 changes: 28 additions & 6 deletions src/sage/schemes/elliptic_curves/ell_curve_isogeny.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
from sage.schemes.elliptic_curves.ell_generic import is_EllipticCurve

from sage.schemes.elliptic_curves.weierstrass_morphism \
import WeierstrassIsomorphism, _isomorphisms, baseWI
import WeierstrassIsomorphism, _isomorphisms, baseWI, negation_morphism

#
# Private function for parsing input to determine the type of
Expand Down Expand Up @@ -1336,9 +1336,7 @@ def __neg__(self):
((x^2 + (-a)*x - 2)/(x + (-a)), (-x^2*y + (2*a)*x*y - y)/(x^2 + (-2*a)*x - 1))
"""
output = copy(self)
E2 = output._codomain
iso = WeierstrassIsomorphism(E2, (-1, 0, -E2.a1(), -E2.a3()))
output._set_post_isomorphism(iso)
output._set_post_isomorphism(negation_morphism(output._codomain))
return output

#
Expand Down Expand Up @@ -1402,8 +1400,8 @@ def __clear_cached_values(self):
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
sage: phi = EllipticCurveIsogeny(E, x)
sage: old_ratl_maps = phi.rational_maps()
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
sage: phi._set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(), (-1,0,0,0)))
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import negation_morphism
sage: phi._set_post_isomorphism(negation_morphism(phi.codomain()))
sage: old_ratl_maps == phi.rational_maps()
False
sage: old_ratl_maps[1] == -phi.rational_maps()[1]
Expand Down Expand Up @@ -2729,6 +2727,30 @@ def kernel_polynomial(self):
return self.__kernel_polynomial


def is_separable(self):
r"""
Determine whether or not this isogeny is separable.
Since :class:`EllipticCurveIsogeny` only implements
separable isogenies, this method always returns ``True``.
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
"""
return True


def _set_pre_isomorphism(self, preWI):
"""
Modify this isogeny by pre-composing with a
Expand Down
31 changes: 29 additions & 2 deletions src/sage/schemes/elliptic_curves/ell_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2324,6 +2324,7 @@ def multiplication_by_m_isogeny(self, m):
sage: E = EllipticCurve('11a1')
sage: E.multiplication_by_m_isogeny(7)
doctest:warning ... DeprecationWarning: ...
Isogeny of degree 49 from Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field
TESTS:
Expand Down Expand Up @@ -2363,6 +2364,9 @@ def multiplication_by_m_isogeny(self, m):
sage: all(mu(-m) == -mu(m) for m in (1,2,3,5,7))
True
"""
from sage.misc.superseded import deprecation
deprecation(32826, 'The .multiplication_by_m_isogeny() method is superseded by .scalar_multiplication().')

mx, my = self.multiplication_by_m(m)

torsion_poly = self.torsion_polynomial(abs(m)).monic()
Expand All @@ -2377,6 +2381,29 @@ def multiplication_by_m_isogeny(self, m):

assert False, 'bug in multiplication_by_m_isogeny()'

def scalar_multiplication(self, m):
r"""
Return the scalar-multiplication map `[m]` on this elliptic
curve as a
:class:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar`
object.
EXAMPLES::
sage: E = EllipticCurve('77a1')
sage: m = E.scalar_multiplication(-7); m
Scalar-multiplication endomorphism [-7] of Elliptic Curve defined by y^2 + y = x^3 + 2*x over Rational Field
sage: m.degree()
49
sage: P = E(2,3)
sage: m(P)
(-26/225 : -2132/3375 : 1)
sage: m.rational_maps() == E.multiplication_by_m(-7)
True
"""
from sage.schemes.elliptic_curves.hom_scalar import EllipticCurveHom_scalar
return EllipticCurveHom_scalar(self, m)

def isomorphism_to(self, other):
"""
Given another weierstrass model ``other`` of self, return an
Expand Down Expand Up @@ -2489,9 +2516,9 @@ def automorphisms(self, field=None):
....: continue
....: break
sage: Aut = E.automorphisms()
sage: Aut[0] == E.multiplication_by_m_isogeny(1)
sage: Aut[0] == E.scalar_multiplication(1)
True
sage: Aut[1] == E.multiplication_by_m_isogeny(-1)
sage: Aut[1] == E.scalar_multiplication(-1)
True
sage: sorted(Aut) == Aut
True
Expand Down
52 changes: 27 additions & 25 deletions src/sage/schemes/elliptic_curves/hom.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- :class:`~sage.schemes.elliptic_curves.ell_curve_isogeny.EllipticCurveIsogeny`
- :class:`~sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism`
- :class:`~sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite`
- :class:`~sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar`
- :class:`~sage.schemes.elliptic_curves.hom_velusqrt.EllipticCurveHom_velusqrt`
AUTHORS:
Expand All @@ -30,6 +31,8 @@
from sage.rings.finite_rings import finite_field_base
from sage.rings.number_field import number_field_base

import sage.schemes.elliptic_curves.weierstrass_morphism as wm


class EllipticCurveHom(Morphism):
"""
Expand Down Expand Up @@ -174,13 +177,14 @@ def _richcmp_(self, other, op):
::
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism, identity_morphism
sage: E = EllipticCurve([9,9])
sage: F = E.change_ring(GF(71))
sage: wE = WeierstrassIsomorphism(E, (1,0,0,0))
sage: wF = WeierstrassIsomorphism(F, (1,0,0,0))
sage: mE = E.multiplication_by_m_isogeny(1)
sage: wE = identity_morphism(E)
sage: wF = identity_morphism(F)
sage: mE = E.scalar_multiplication(1)
sage: mF = F.multiplication_by_m_isogeny(1)
doctest:warning ... DeprecationWarning: ...
sage: [mE == wE, mF == wF]
[True, True]
sage: [a == b for a in (wE,mE) for b in (wF,mF)]
Expand Down Expand Up @@ -285,6 +289,7 @@ def kernel_polynomial(self):
- :meth:`EllipticCurveIsogeny.kernel_polynomial`
- :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.kernel_polynomial`
- :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.kernel_polynomial`
- :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.kernel_polynomial`
TESTS::
Expand All @@ -305,6 +310,7 @@ def dual(self):
- :meth:`EllipticCurveIsogeny.dual`
- :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.dual`
- :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.dual`
- :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.dual`
TESTS::
Expand All @@ -327,6 +333,7 @@ def rational_maps(self):
- :meth:`EllipticCurveIsogeny.rational_maps`
- :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.rational_maps`
- :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.rational_maps`
- :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.rational_maps`
TESTS::
Expand All @@ -348,6 +355,7 @@ def x_rational_map(self):
- :meth:`EllipticCurveIsogeny.x_rational_map`
- :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.x_rational_map`
- :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.x_rational_map`
- :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.x_rational_map`
TESTS::
Expand Down Expand Up @@ -377,6 +385,7 @@ def scaling_factor(self):
- :meth:`EllipticCurveIsogeny.scaling_factor`
- :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.scaling_factor`
- :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.scaling_factor`
- :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.scaling_factor`
TESTS::
Expand Down Expand Up @@ -521,27 +530,22 @@ def is_separable(self):
r"""
Determine whether or not this morphism is separable.
.. NOTE::
This method currently always returns ``True`` as Sage does
not yet implement inseparable isogenies. This will probably
change in the future.
EXAMPLES::
Implemented by child classes. For examples, see:
sage: E = EllipticCurve(GF(17), [0,0,0,3,0])
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
sage: phi.is_separable()
True
- :meth:`EllipticCurveIsogeny.is_separable`
- :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.is_separable`
- :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.is_separable`
- :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.is_separable`
::
TESTS::
sage: E = EllipticCurve('11a1')
sage: phi = EllipticCurveIsogeny(E, E.torsion_points())
sage: phi.is_separable()
True
sage: from sage.schemes.elliptic_curves.hom import EllipticCurveHom
sage: EllipticCurveHom.is_separable(None)
Traceback (most recent call last):
...
NotImplementedError: ...
"""
return True
raise NotImplementedError('children must implement')

def is_surjective(self):
r"""
Expand Down Expand Up @@ -648,9 +652,7 @@ def __neg__(self):
sage: psi.rational_maps() == (f, -g)
True
"""
import sage.schemes.elliptic_curves.weierstrass_morphism as wm
a1,_,a3,_,_ = self.codomain().a_invariants()
return wm.WeierstrassIsomorphism(self.codomain(), (-1,0,-a1,-a3)) * self
return wm.negation_morphism(self.codomain()) * self

@cached_method
def __hash__(self):
Expand Down Expand Up @@ -745,7 +747,7 @@ def compare_via_evaluation(left, right):
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.multiplication_by_m_isogeny(7))
sage: compare_via_evaluation(mu, E.scalar_multiplication(7))
True
.. SEEALSO::
Expand Down
14 changes: 7 additions & 7 deletions src/sage/schemes/elliptic_curves/hom_composite.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
from sage.schemes.elliptic_curves.ell_generic import EllipticCurve_generic
from sage.schemes.elliptic_curves.hom import EllipticCurveHom, compare_via_evaluation
from sage.schemes.elliptic_curves.ell_curve_isogeny import EllipticCurveIsogeny
from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism, identity_morphism

# TODO: Implement sparse strategies? (cf. the SIKE cryptosystem)

Expand Down Expand Up @@ -272,7 +272,7 @@ def __init__(self, E, kernel, codomain=None, model=None):
self._phis = _compute_factored_isogeny(kernel)

if not self._phis:
self._phis = [WeierstrassIsomorphism(E, (1, 0, 0, 0))]
self._phis = [identity_morphism(E)]

if model is not None:
if codomain is not None:
Expand Down Expand Up @@ -350,7 +350,7 @@ def from_factors(cls, maps, E=None, strict=True):
TESTS::
sage: E = EllipticCurve('4730k1')
sage: EllipticCurveHom_composite.from_factors([], E) == E.multiplication_by_m_isogeny(1)
sage: EllipticCurveHom_composite.from_factors([], E) == E.scalar_multiplication(1)
True
::
Expand All @@ -375,7 +375,7 @@ def from_factors(cls, maps, E=None, strict=True):
E = phi.codomain()

if not maps:
maps = (WeierstrassIsomorphism(E, (1,0,0,0)),)
maps = (identity_morphism(E),)

if len(maps) == 1 and not strict:
return maps[0]
Expand Down Expand Up @@ -563,7 +563,7 @@ def _comparison_impl(left, right, op):
sage: psi = phi.codomain().isogeny(phi(Q))
sage: psi = psi.codomain().isomorphism_to(E) * psi
sage: comp = psi * phi
sage: mu = E.multiplication_by_m_isogeny(phi.degree())
sage: mu = E.scalar_multiplication(phi.degree())
sage: sum(a*comp == mu for a in E.automorphisms())
1
Expand Down Expand Up @@ -680,9 +680,9 @@ def dual(self):
Composite morphism of degree 9 = 3^2:
From: Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 28339*x + 59518 over Finite Field of size 65537
To: Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Finite Field of size 65537
sage: psi * phi == phi.domain().multiplication_by_m_isogeny(phi.degree())
sage: psi * phi == phi.domain().scalar_multiplication(phi.degree())
True
sage: phi * psi == psi.domain().multiplication_by_m_isogeny(psi.degree())
sage: phi * psi == psi.domain().scalar_multiplication(psi.degree())
True
"""
phis = (phi.dual() for phi in self._phis[::-1])
Expand Down
Loading

0 comments on commit 98b22eb

Please sign in to comment.