Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix .multiplication_by_m when m is not coprime to characteristic (#6413) #37096

Merged
merged 12 commits into from
Feb 13, 2024
18 changes: 18 additions & 0 deletions src/sage/schemes/elliptic_curves/ell_finite_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,24 @@ def cardinality(self, algorithm=None, extension_degree=1):

order = cardinality # alias

@cached_method
def multiplication_by_p_isogeny(self):
r"""
Return the multiplication-by-`p` isogeny.

EXAMPLES::

sage: p = 23
sage: K.<a> = GF(p^3)
sage: E = EllipticCurve(K, [K.random_element(), K.random_element()])
grhkm21 marked this conversation as resolved.
Show resolved Hide resolved
sage: phi = E.multiplication_by_p_isogeny()
sage: assert phi.degree() == p**2
sage: P = E.random_element()
sage: assert phi(P) == P * p
"""
frob = self.frobenius_isogeny()
return frob.dual() * frob

def frobenius_polynomial(self):
r"""
Return the characteristic polynomial of Frobenius.
Expand Down
66 changes: 57 additions & 9 deletions src/sage/schemes/elliptic_curves/ell_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
# ****************************************************************************

import math
from sage.arith.misc import valuation

import sage.rings.abc
from sage.rings.finite_rings.integer_mod import mod
Expand All @@ -66,8 +67,6 @@

from sage.arith.functions import lcm
from sage.rings.integer import Integer
from sage.rings.big_oh import O
from sage.rings.infinity import Infinity as oo
from sage.rings.rational import Rational
from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF
from sage.rings.rational_field import RationalField
Expand Down Expand Up @@ -2421,15 +2420,53 @@ def multiplication_by_m(self, m, x_only=False):
sage: my_eval = lambda f,P: [fi(P[0],P[1]) for fi in f]
sage: f = E.multiplication_by_m(2)
sage: assert(E(eval(f,P)) == 2*P)

The following test shows that :trac:`6413` is fixed for elliptic curves over finite fields::
sage: p = 7
sage: K.<a> = GF(p^2)
sage: E = EllipticCurve(K, [a + 3, 5 - a])
sage: k = p^2 * 3
sage: f, g = E.multiplication_by_m(k)
sage: for _ in range(100):
....: P = E.random_point()
....: if P * k == 0:
....: continue
....: Qx = f.subs(x=P[0])
....: Qy = g.subs(x=P[0], y=P[1])
....: assert (P * k).xy() == (Qx, Qy)

However, it still fails for elliptic curves over positive-characteristic fields::

sage: F.<a> = FunctionField(GF(7))
sage: E = EllipticCurve(F, [a, 1 / a])
sage: E.multiplication_by_m(7)
Traceback (most recent call last):
...
NotImplementedError: multiplication by integer not coprime to p is only implemented for curves over finite fields

::

sage: p = 7
sage: K.<a> = GF(p^2)
sage: E = EllipticCurve(K, [K.random_element(), K.random_element()])
sage: E.multiplication_by_m(p * 2)[0] == E.multiplication_by_m(p * 2, x_only=True)
True
Comment on lines +2449 to +2453
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test fails at random with probability ~ 1/49 or something like that (i.e. when the discriminant is 0).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will fix this in a separate PR since this is merged already. Sorry for not spotting this earlier (there's one similar above)

"""
# Coerce the input m to be an integer
m = Integer(m)
p = self.base_ring().characteristic()

if m == 0:
raise ValueError("m must be a non-zero integer")

if x_only:
x = polygen(self.base_ring(), 'x')
else:
from sage.rings.finite_rings.finite_field_base import FiniteField as FiniteField_generic
if m % p == 0 and not isinstance(self.base_ring(), FiniteField_generic):
grhkm21 marked this conversation as resolved.
Show resolved Hide resolved
# TODO: Implement the correct formula?
raise NotImplementedError("multiplication by integer not coprime to p "
"is only implemented for curves over finite fields")
x, y = polygens(self.base_ring(), 'x,y')

# Special case of multiplication by 1 is easy.
Expand All @@ -2440,30 +2477,41 @@ def multiplication_by_m(self, m, x_only=False):
return x

# Grab curve invariants
a1, a2, a3, a4, a6 = self.a_invariants()
a1, _, a3, _, _ = self.a_invariants()

if m == -1:
if not x_only:
return (x, -y-a1*x-a3)
else:
return x

# the x-coordinate does not depend on the sign of m. The work
# If we only require the x coordinate, it is faster to use the recursive formula
# since substituting polynomials is quite slow.
v_p = 0 if p == 0 else valuation(m, p)
if not x_only:
m //= p**v_p

# the x-coordinate does not depend on the sign of m. The work
# here is done by functions defined earlier:

mx = (x.parent()(self._multiple_x_numerator(m.abs(), x))
/ x.parent()(self._multiple_x_denominator(m.abs(), x)))

if x_only:
# Return it if the optional parameter x_only is set.
return mx

# Consideration of the invariant differential
# w=dx/(2*y+a1*x+a3) shows that m*w = d(mx)/(2*my+a1*mx+a3)
# and hence 2*my+a1*mx+a3 = (1/m)*(2*y+a1*x+a3)*d(mx)/dx

# Consideration of the invariant differential
# w=dx/(2*y+a1*x+a3) shows that m*w = d(mx)/(2*my+a1*mx+a3)
# and hence 2*my+a1*mx+a3 = (1/m)*(2*y+a1*x+a3)*d(mx)/dx
my = ((2*y+a1*x+a3)*mx.derivative(x)/m - a1*mx-a3)/2

if v_p > 0:
isog = self.multiplication_by_p_isogeny()**v_p
fx, fy = isog.rational_maps()
# slow...
mx = mx.subs(x=fx)
my = my.subs(x=fx, y=fy)

return mx, my

def multiplication_by_m_isogeny(self, m):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I acknowledge that we are technically "allowed" to remove this method now, but I'm wondering if it's a good idea at this point: Debian and Ubuntu are still on Sage 9.5, so most of their users have never even seen the deprecation warning. Since keeping this method in Sage a little longer is essentially free of harm, I'd suggest to do so.

Expand Down