Skip to content

Commit b47cd75

Browse files
author
Release Manager
committed
gh-40536: Implement HyperellipticCurve.random_element() over finite fields Move the function from EllipticCurve implementation, since the same idea works verbatim here. depends on #40535 to fix the test failure. ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> - [x] The title is concise and informative. - [x] The description explains in detail what this PR is about. - [x] I have linked a relevant issue or discussion. - [x] I have created tests covering the changes. - [ ] I have updated the documentation and checked the documentation preview. ### ⌛ Dependencies <!-- List all open PRs that this PR logically depends on. For example, --> <!-- - #12345: short description why this is a dependency --> <!-- - #34567: ... --> URL: #40536 Reported by: user202729 Reviewer(s):
2 parents 4b3a854 + 9cf3440 commit b47cd75

File tree

2 files changed

+127
-112
lines changed

2 files changed

+127
-112
lines changed

src/sage/schemes/curves/projective_curve.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2272,6 +2272,131 @@ def rational_points(self, algorithm='enum', sort=True):
22722272

22732273
raise ValueError(f"No algorithm '{algorithm}' known")
22742274

2275+
def random_element(self):
2276+
"""
2277+
Return a random point on this elliptic/hyperelliptic curve, uniformly chosen
2278+
among all rational points.
2279+
2280+
ALGORITHM:
2281+
2282+
Choose the point at infinity with probability `1/(2q + 1)`.
2283+
Otherwise, take a random element from the field as x-coordinate
2284+
and compute the possible y-coordinates. Return the i-th
2285+
possible y-coordinate, where i is randomly chosen to be 0 or 1.
2286+
If the i-th y-coordinate does not exist (either there is no
2287+
point with the given x-coordinate or we hit a 2-torsion point
2288+
with i == 1), try again.
2289+
2290+
This gives a uniform distribution because you can imagine
2291+
`2q + 1` buckets, one for the point at infinity and 2 for each
2292+
element of the field (representing the x-coordinates). This
2293+
gives a 1-to-1 map of (hyper)elliptic curve points into buckets. At
2294+
every iteration, we simply choose a random bucket until we find
2295+
a bucket containing a point.
2296+
2297+
AUTHORS:
2298+
2299+
- Jeroen Demeyer (2014-09-09): choose points uniformly random,
2300+
see :issue:`16951`.
2301+
2302+
EXAMPLES::
2303+
2304+
sage: k = GF(next_prime(7^5))
2305+
sage: E = EllipticCurve(k,[2,4])
2306+
sage: P = E.random_element(); P # random
2307+
(16740 : 12486 : 1)
2308+
sage: type(P)
2309+
<class 'sage.schemes.elliptic_curves.ell_point.EllipticCurvePoint_finite_field'>
2310+
sage: P in E
2311+
True
2312+
2313+
::
2314+
2315+
sage: # needs sage.rings.finite_rings
2316+
sage: k.<a> = GF(7^5)
2317+
sage: E = EllipticCurve(k,[2,4])
2318+
sage: P = E.random_element(); P # random
2319+
(5*a^4 + 3*a^3 + 2*a^2 + a + 4 : 2*a^4 + 3*a^3 + 4*a^2 + a + 5 : 1)
2320+
sage: type(P)
2321+
<class 'sage.schemes.elliptic_curves.ell_point.EllipticCurvePoint_finite_field'>
2322+
sage: P in E
2323+
True
2324+
2325+
::
2326+
2327+
sage: # needs sage.rings.finite_rings
2328+
sage: k.<a> = GF(2^5)
2329+
sage: E = EllipticCurve(k,[a^2,a,1,a+1,1])
2330+
sage: P = E.random_element(); P # random
2331+
(a^4 + a : a^4 + a^3 + a^2 : 1)
2332+
sage: type(P)
2333+
<class 'sage.schemes.elliptic_curves.ell_point.EllipticCurvePoint_finite_field'>
2334+
sage: P in E
2335+
True
2336+
2337+
Ensure that the entire point set is reachable::
2338+
2339+
sage: E = EllipticCurve(GF(11), [2,1])
2340+
sage: S = set()
2341+
sage: while len(S) < E.cardinality():
2342+
....: S.add(E.random_element())
2343+
2344+
TESTS:
2345+
2346+
See :issue:`8311`::
2347+
2348+
sage: E = EllipticCurve(GF(3), [0,0,0,2,2])
2349+
sage: E.random_element()
2350+
(0 : 1 : 0)
2351+
sage: E.cardinality()
2352+
1
2353+
2354+
sage: E = EllipticCurve(GF(2), [0,0,1,1,1])
2355+
sage: E.random_point()
2356+
(0 : 1 : 0)
2357+
sage: E.cardinality()
2358+
1
2359+
2360+
sage: # needs sage.rings.finite_rings
2361+
sage: F.<a> = GF(4)
2362+
sage: E = EllipticCurve(F, [0, 0, 1, 0, a])
2363+
sage: E.random_point()
2364+
(0 : 1 : 0)
2365+
sage: E.cardinality()
2366+
1
2367+
2368+
Sampling from points on a hyperelliptic curve::
2369+
2370+
sage: R.<x,y> = GF(13)[]
2371+
sage: C = HyperellipticCurve(y^2 + 3*x^2*y - (x^5 + x + 1))
2372+
sage: P = C.random_element(); P # random
2373+
(0 : 1 : 0)
2374+
sage: P in C
2375+
True
2376+
"""
2377+
from sage.schemes.elliptic_curves.ell_finite_field import EllipticCurve_finite_field
2378+
from sage.schemes.hyperelliptic_curves.hyperelliptic_finite_field import HyperellipticCurve_finite_field
2379+
if not isinstance(self, (EllipticCurve_finite_field, HyperellipticCurve_finite_field)):
2380+
raise NotImplementedError("only implemented for elliptic and hyperelliptic curves over finite fields")
2381+
2382+
k = self.base_ring()
2383+
n = 2 * k.order() + 1
2384+
2385+
from sage.rings.integer_ring import ZZ
2386+
while True:
2387+
# Choose the point at infinity with probability 1/(2q + 1)
2388+
i = ZZ.random_element(n)
2389+
if not i:
2390+
return self(0, 1, 0)
2391+
2392+
v = self.lift_x(k.random_element(), all=True)
2393+
try:
2394+
return v[i % 2]
2395+
except IndexError:
2396+
pass
2397+
2398+
random_point = random_element
2399+
22752400

22762401
class IntegralProjectiveCurve(ProjectiveCurve_field):
22772402
"""

src/sage/schemes/elliptic_curves/ell_finite_field.py

Lines changed: 2 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,15 @@
4141
from sage.rings.integer_ring import ZZ
4242
from sage.rings.polynomial.polynomial_ring import polygen
4343
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
44-
from sage.schemes.curves.projective_curve import Hasse_bounds
44+
from sage.schemes.curves.projective_curve import Hasse_bounds, ProjectivePlaneCurve_finite_field
4545
from sage.structure.element import Element
4646

4747
from . import ell_point
4848
from .constructor import EllipticCurve
4949
from .ell_field import EllipticCurve_field
5050

5151

52-
class EllipticCurve_finite_field(EllipticCurve_field):
52+
class EllipticCurve_finite_field(EllipticCurve_field, ProjectivePlaneCurve_finite_field):
5353
r"""
5454
Elliptic curve over a finite field.
5555
@@ -287,116 +287,6 @@ def count_points(self, n=1):
287287

288288
return [self.cardinality(extension_degree=i) for i in range(1, n + 1)]
289289

290-
def random_element(self):
291-
"""
292-
Return a random point on this elliptic curve, uniformly chosen
293-
among all rational points.
294-
295-
ALGORITHM:
296-
297-
Choose the point at infinity with probability `1/(2q + 1)`.
298-
Otherwise, take a random element from the field as x-coordinate
299-
and compute the possible y-coordinates. Return the i-th
300-
possible y-coordinate, where i is randomly chosen to be 0 or 1.
301-
If the i-th y-coordinate does not exist (either there is no
302-
point with the given x-coordinate or we hit a 2-torsion point
303-
with i == 1), try again.
304-
305-
This gives a uniform distribution because you can imagine
306-
`2q + 1` buckets, one for the point at infinity and 2 for each
307-
element of the field (representing the x-coordinates). This
308-
gives a 1-to-1 map of elliptic curve points into buckets. At
309-
every iteration, we simply choose a random bucket until we find
310-
a bucket containing a point.
311-
312-
AUTHORS:
313-
314-
- Jeroen Demeyer (2014-09-09): choose points uniformly random,
315-
see :issue:`16951`.
316-
317-
EXAMPLES::
318-
319-
sage: k = GF(next_prime(7^5))
320-
sage: E = EllipticCurve(k,[2,4])
321-
sage: P = E.random_element(); P # random
322-
(16740 : 12486 : 1)
323-
sage: type(P)
324-
<class 'sage.schemes.elliptic_curves.ell_point.EllipticCurvePoint_finite_field'>
325-
sage: P in E
326-
True
327-
328-
::
329-
330-
sage: # needs sage.rings.finite_rings
331-
sage: k.<a> = GF(7^5)
332-
sage: E = EllipticCurve(k,[2,4])
333-
sage: P = E.random_element(); P # random
334-
(5*a^4 + 3*a^3 + 2*a^2 + a + 4 : 2*a^4 + 3*a^3 + 4*a^2 + a + 5 : 1)
335-
sage: type(P)
336-
<class 'sage.schemes.elliptic_curves.ell_point.EllipticCurvePoint_finite_field'>
337-
sage: P in E
338-
True
339-
340-
::
341-
342-
sage: # needs sage.rings.finite_rings
343-
sage: k.<a> = GF(2^5)
344-
sage: E = EllipticCurve(k,[a^2,a,1,a+1,1])
345-
sage: P = E.random_element(); P # random
346-
(a^4 + a : a^4 + a^3 + a^2 : 1)
347-
sage: type(P)
348-
<class 'sage.schemes.elliptic_curves.ell_point.EllipticCurvePoint_finite_field'>
349-
sage: P in E
350-
True
351-
352-
Ensure that the entire point set is reachable::
353-
354-
sage: E = EllipticCurve(GF(11), [2,1])
355-
sage: S = set()
356-
sage: while len(S) < E.cardinality():
357-
....: S.add(E.random_element())
358-
359-
TESTS:
360-
361-
See :issue:`8311`::
362-
363-
sage: E = EllipticCurve(GF(3), [0,0,0,2,2])
364-
sage: E.random_element()
365-
(0 : 1 : 0)
366-
sage: E.cardinality()
367-
1
368-
369-
sage: E = EllipticCurve(GF(2), [0,0,1,1,1])
370-
sage: E.random_point()
371-
(0 : 1 : 0)
372-
sage: E.cardinality()
373-
1
374-
375-
sage: # needs sage.rings.finite_rings
376-
sage: F.<a> = GF(4)
377-
sage: E = EllipticCurve(F, [0, 0, 1, 0, a])
378-
sage: E.random_point()
379-
(0 : 1 : 0)
380-
sage: E.cardinality()
381-
1
382-
"""
383-
k = self.base_field()
384-
n = 2 * k.order() + 1
385-
386-
while True:
387-
# Choose the point at infinity with probability 1/(2q + 1)
388-
i = ZZ.random_element(n)
389-
if not i:
390-
return self.point(0)
391-
392-
v = self.lift_x(k.random_element(), all=True)
393-
try:
394-
return v[i % 2]
395-
except IndexError:
396-
pass
397-
398-
random_point = random_element
399-
400290
def trace_of_frobenius(self):
401291
r"""
402292
Return the trace of Frobenius acting on this elliptic curve.

0 commit comments

Comments
 (0)