Skip to content

Commit 4b3a854

Browse files
author
Release Manager
committed
gh-40535: Implement construction of hyperelliptic curves from defining equation <!-- ^ Please provide a concise and informative title. --> <!-- ^ Don't put issue numbers in the title, do this in the PR description below. --> <!-- ^ For example, instead of "Fixes #12345" use "Introduce new method to calculate 1 + 2". --> <!-- v Describe your changes below in detail. --> <!-- v Why is this change required? What problem does it solve? --> <!-- v If this PR resolves an open issue, please link to it here. For example, "Fixes #12345". --> ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> - [ ] The title is concise and informative. - [ ] The description explains in detail what this PR is about. - [ ] I have linked a relevant issue or discussion. - [ ] 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: #40535 Reported by: user202729 Reviewer(s): Travis Scrimshaw
2 parents c08c3c3 + 2d16a83 commit 4b3a854

File tree

2 files changed

+92
-35
lines changed

2 files changed

+92
-35
lines changed

src/sage/schemes/elliptic_curves/constructor.py

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ class EllipticCurveFactory(UniqueFactory):
161161
162162
sage: R.<x,y> = GF(5)[]
163163
sage: EllipticCurve(x^3 + x^2 + 2 - y^2 - y*x)
164-
Elliptic Curve defined by y^2 + x*y = x^3 + x^2 + 2 over Finite Field of size 5
164+
Elliptic Curve defined by y^2 + x*y = x^3 + x^2 + 2 over Finite Field of size 5
165165
166166
We can also create elliptic curves by giving a smooth plane cubic with a rational point::
167167
@@ -579,41 +579,19 @@ def coefficients_from_Weierstrass_polynomial(f):
579579
sage: R.<w,z> = QQ[]
580580
sage: coefficients_from_Weierstrass_polynomial(-w^2 + z^3 + 1)
581581
[0, 0, 0, 0, 1]
582+
sage: R.<u,v> = GF(13)[]
583+
sage: EllipticCurve(u^2 + 2*v*u + 3*u - (v^3 + 4*v^2 + 5*v + 6)) # indirect doctest
584+
Elliptic Curve defined by y^2 + 2*x*y + 3*y = x^3 + 4*x^2 + 5*x + 6 over Finite Field of size 13
582585
"""
583-
R = f.parent()
584-
cubic_variables = [ x for x in R.gens() if f.degree(x) == 3 ]
585-
quadratic_variables = [ y for y in R.gens() if f.degree(y) == 2 ]
586-
try:
587-
x = cubic_variables[0]
588-
y = quadratic_variables[0]
589-
except IndexError:
586+
from sage.schemes.hyperelliptic_curves.constructor import _parse_multivariate_defining_equation
587+
f, h = _parse_multivariate_defining_equation(f)
588+
# OUTPUT: tuple (f, h), each of them given as a list of coefficients.
589+
if len(f) != 4 or len(h) > 2:
590590
raise ValueError('polynomial is not in long Weierstrass form')
591-
592-
a1 = a2 = a3 = a4 = a6 = 0
593-
x3 = y2 = None
594-
for coeff, mon in f:
595-
if mon == x**3:
596-
x3 = coeff
597-
elif mon == x**2:
598-
a2 = coeff
599-
elif mon == x:
600-
a4 = coeff
601-
elif mon == 1:
602-
a6 = coeff
603-
elif mon == y**2:
604-
y2 = -coeff
605-
elif mon == x*y:
606-
a1 = -coeff
607-
elif mon == y:
608-
a3 = -coeff
609-
else:
610-
raise ValueError('polynomial is not in long Weierstrass form')
611-
612-
if x3 != y2:
591+
if not f[3].is_one():
613592
raise ValueError('the coefficient of x^3 and -y^2 must be the same')
614-
elif x3 != 1:
615-
a1, a2, a3, a4, a6 = a1/x3, a2/x3, a3/x3, a4/x3, a6/x3
616-
return [a1, a2, a3, a4, a6]
593+
h += [0] * (2 - len(h))
594+
return [h[1], f[2], h[0], f[1], f[0]]
617595

618596

619597
def EllipticCurve_from_c4c6(c4, c6):
@@ -625,7 +603,7 @@ def EllipticCurve_from_c4c6(c4, c6):
625603
626604
sage: E = EllipticCurve_from_c4c6(17, -2005)
627605
sage: E
628-
Elliptic Curve defined by y^2 = x^3 - 17/48*x + 2005/864 over Rational Field
606+
Elliptic Curve defined by y^2 = x^3 - 17/48*x + 2005/864 over Rational Field
629607
sage: E.c_invariants()
630608
(17, -2005)
631609
"""

src/sage/schemes/hyperelliptic_curves/constructor.py

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,69 @@
2929
from sage.structure.dynamic_class import dynamic_class
3030

3131

32-
def HyperellipticCurve(f, h=0, names=None, PP=None, check_squarefree=True):
32+
def _parse_multivariate_defining_equation(g):
33+
"""
34+
Parse a defining equation for a hyperelliptic curve.
35+
The input `g` should have the form `g(x, y) = y^2 + h(x) y - f(x)`,
36+
or a constant multiple of that.
37+
38+
OUTPUT: tuple (f, h), each of them given as a list of coefficients.
39+
40+
TESTS::
41+
42+
sage: from sage.schemes.hyperelliptic_curves.constructor import _parse_multivariate_defining_equation
43+
sage: R.<x,y> = QQ[]
44+
sage: _parse_multivariate_defining_equation(y^2 + 3*x^2*y - (x^5 + x + 1))
45+
([1, 1, 0, 0, 0, 1], [0, 0, 3])
46+
sage: _parse_multivariate_defining_equation(2*y^2 + 3*x^2*y - (x^5 + x + 1))
47+
([1/2, 1/2, 0, 0, 0, 1/2], [0, 0, 3/2])
48+
49+
The variable names are arbitrary::
50+
51+
sage: S.<z,t> = GF(13)[]
52+
sage: _parse_multivariate_defining_equation(2*t^2 + 3*z^2*t - (z^5 + z + 1))
53+
([7, 7, 0, 0, 0, 7], [0, 0, 8])
54+
"""
55+
from sage.rings.polynomial.multi_polynomial import MPolynomial
56+
if not isinstance(g, MPolynomial):
57+
raise ValueError("must be a multivariate polynomial")
58+
59+
variables = g.variables()
60+
if len(variables) != 2:
61+
raise ValueError("must be a polynomial in two variables")
62+
63+
y, x = sorted(variables, key=g.degree)
64+
if g.degree(y) != 2:
65+
raise ValueError("must be a polynomial of degree 2 in a variable")
66+
67+
f = []
68+
h = []
69+
for k, v in g:
70+
dx = v.degree(x)
71+
dy = v.degree(y)
72+
if dy == 2:
73+
if dx != 0:
74+
raise ValueError(f"cannot have a term y*x^{dx}")
75+
y2 = k
76+
elif dy == 1:
77+
while len(h) <= dx:
78+
h.append(0)
79+
h[dx] = k
80+
else:
81+
assert dy == 0
82+
while len(f) <= dx:
83+
f.append(0)
84+
f[dx] = -k
85+
86+
if not y2.is_one():
87+
y2_inv = y2.inverse_of_unit()
88+
f = [c * y2_inv for c in f]
89+
h = [c * y2_inv for c in h]
90+
91+
return f, h
92+
93+
94+
def HyperellipticCurve(f, h=None, names=None, PP=None, check_squarefree=True):
3395
r"""
3496
Return the hyperelliptic curve `y^2 + h y = f`, for
3597
univariate polynomials `h` and `f`. If `h`
@@ -76,6 +138,12 @@ def HyperellipticCurve(f, h=0, names=None, PP=None, check_squarefree=True):
76138
Hyperelliptic Curve over Finite Field in a of size 3^2
77139
defined by y^2 + (x + a)*y = x^3 + x + 2
78140
141+
Construct from defining polynomial::
142+
143+
sage: R.<x,y> = QQ[]
144+
sage: HyperellipticCurve(y^2 + 3*x^2*y - (x^5 + x + 1))
145+
Hyperelliptic Curve over Rational Field defined by y^2 + 3*x^2*y = x^5 + x + 1
146+
79147
Characteristic two::
80148
81149
sage: # needs sage.rings.finite_rings
@@ -200,6 +268,17 @@ def HyperellipticCurve(f, h=0, names=None, PP=None, check_squarefree=True):
200268
"""
201269
# F is the discriminant; use this for the type check
202270
# rather than f and h, one of which might be constant.
271+
if h is None:
272+
from sage.rings.polynomial.multi_polynomial import MPolynomial
273+
if isinstance(f, MPolynomial) and len(f.parent().gens()) == 2:
274+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
275+
from sage.structure.element import get_coercion_model
276+
P = PolynomialRing(f.base_ring(), 'x')
277+
f, h = _parse_multivariate_defining_equation(f)
278+
f, h = P(f), P(h)
279+
else:
280+
h = 0
281+
203282
F = h**2 + 4 * f
204283
if not isinstance(F, Polynomial):
205284
raise TypeError(f"arguments f = {f} and h = {h} must be polynomials")

0 commit comments

Comments
 (0)