Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/schemes/toric/weierstrass_covering.py
8820 views
1
r"""
2
Map to the Weierstrass form of a toric elliptic curve.
3
4
There are 16 reflexive polygons in 2-d. Each defines a toric Fano
5
variety, which (since it is 2-d) has a unique crepant resolution to a smooth
6
toric surface. An anticanonical hypersurface defines a genus one curve
7
`C` in this ambient space, with Jacobian elliptic curve `J(C)` which
8
can be defined by the Weierstrass model `y^2 = x^3 + f x + g`. The
9
coefficients `f` and `g` can be computed with the
10
:mod:`~sage.schemes.toric.weierstrass` module. The purpose of this
11
model is to give an explicit rational map `C \to J(C)`. This is an
12
`n^2`-cover, where `n` is the minimal multi-section of `C`.
13
14
Since it is technically often easier to deal with polynomials than
15
with fractions, we return the rational map in terms of homogeneous
16
coordinates. That is, the ambient space for the Weierstrass model is
17
the weighted projective space `\mathbb{P}^2[2,3,1]` with homogeneous
18
coordinates `[X:Y:Z] = [\lambda^2 X, \lambda^3 Y, \lambda Z]`. The
19
homogenized Weierstrass equation is
20
21
.. math::
22
23
Y^2 = X^3 + f X Z^4 + g Z^6
24
25
EXAMPLES::
26
27
sage: R.<x,y> = QQ[]
28
sage: cubic = x^3 + y^3 + 1
29
sage: f, g = WeierstrassForm(cubic); (f,g)
30
(0, -27/4)
31
32
That is, this hypersurface `C \in \mathbb{P}^2` has a Weierstrass
33
equation `Y^2 = X^3 + 0 \cdot X Z^4 - \frac{27}{4} Z^6` where
34
`[X:Y:Z]` are projective coordinates on `\mathbb{P}^2[2,3,1]`. The
35
form of the map `C\to J(C)` is::
36
37
sage: X,Y,Z = WeierstrassForm(cubic, transformation=True); (X,Y,Z)
38
(-x^3*y^3 - x^3 - y^3,
39
1/2*x^6*y^3 - 1/2*x^3*y^6 - 1/2*x^6 + 1/2*y^6 + 1/2*x^3 - 1/2*y^3,
40
x*y)
41
42
Note that plugging in `[X:Y:Z]` to the Weierstrass equation is a
43
complicated polynomial, but contains the hypersurface equation as a
44
factor::
45
46
sage: -Y^2 + X^3 + f*X*Z^4 + g*Z^6
47
-1/4*x^12*y^6 - 1/2*x^9*y^9 - 1/4*x^6*y^12 + 1/2*x^12*y^3
48
- 7/2*x^9*y^6 - 7/2*x^6*y^9 + 1/2*x^3*y^12 - 1/4*x^12 - 7/2*x^9*y^3
49
- 45/4*x^6*y^6 - 7/2*x^3*y^9 - 1/4*y^12 - 1/2*x^9 - 7/2*x^6*y^3
50
- 7/2*x^3*y^6 - 1/2*y^9 - 1/4*x^6 + 1/2*x^3*y^3 - 1/4*y^6
51
sage: cubic.divides(-Y^2 + X^3 + f*X*Z^4 + g*Z^6)
52
True
53
54
If you prefer you can also use homogeneous coordinates for `C \in
55
\mathbb{P}^2` ::
56
57
sage: R.<x,y,z> = QQ[]
58
sage: cubic = x^3 + y^3 + z^3
59
sage: f, g = WeierstrassForm(cubic); (f,g)
60
(0, -27/4)
61
sage: X,Y,Z = WeierstrassForm(cubic, transformation=True)
62
sage: cubic.divides(-Y^2 + X^3 + f*X*Z^4 + g*Z^6)
63
True
64
65
The 16 toric surfaces corresponding to the 16 reflexive polygons can
66
all be blown down to `\mathbb{P}^2`, `\mathbb{P}^1\times\mathbb{P}^1`,
67
or `\mathbb{P}^{2}[1,1,2]`. Their (and hence in all 16 cases)
68
anticanonical hypersurface can equally be brought into Weierstrass
69
form. For example, here is an anticanonical hypersurface in
70
`\mathbb{P}^{2}[1,1,2]` ::
71
72
sage: P2_112 = toric_varieties.P2_112()
73
sage: C = P2_112.anticanonical_hypersurface(coefficients=[1]*4); C
74
Closed subscheme of 2-d CPR-Fano toric variety
75
covered by 3 affine patches defined by:
76
z0^4 + z2^4 + z0*z1*z2 + z1^2
77
sage: eq = C.defining_polynomials()[0]
78
sage: f, g = WeierstrassForm(eq)
79
sage: X,Y,Z = WeierstrassForm(eq, transformation=True)
80
sage: (-Y^2 + X^3 + f*X*Z^4 + g*Z^6).reduce(C.defining_ideal())
81
0
82
83
Finally, you sometimes have to manually specify the variables to
84
use. This is either because the equation is degenerate or because it
85
contains additional variables that you want to treat as coefficients::
86
87
sage: R.<a, x,y,z> = QQ[]
88
sage: cubic = x^3 + y^3 + z^3 + a*x*y*z
89
sage: f, g = WeierstrassForm(cubic, variables=[x,y,z])
90
sage: X,Y,Z = WeierstrassForm(cubic, variables=[x,y,z], transformation=True)
91
sage: cubic.divides(-Y^2 + X^3 + f*X*Z^4 + g*Z^6)
92
True
93
94
REFERENCES:
95
96
.. [AnEtAl]
97
An, Sang Yook et al:
98
Jacobians of Genus One Curves,
99
Journal of Number Theory 90 (2002), pp.304--315,
100
http://www.math.arizona.edu/~wmc/Research/JacobianFinal.pdf
101
"""
102
103
########################################################################
104
# Copyright (C) 2012 Volker Braun <[email protected]>
105
#
106
# Distributed under the terms of the GNU General Public License (GPL)
107
#
108
# http://www.gnu.org/licenses/
109
########################################################################
110
111
from sage.rings.all import ZZ
112
from sage.modules.all import vector
113
from sage.rings.all import invariant_theory
114
from sage.schemes.toric.weierstrass import (
115
_partial_discriminant,
116
_check_polynomial_P2,
117
_check_polynomial_P1xP1,
118
_check_polynomial_P2_112,
119
)
120
121
122
######################################################################
123
124
def WeierstrassMap(polynomial, variables=None):
125
r"""
126
Return the Weierstrass form of an anticanonical hypersurface.
127
128
You should use
129
:meth:`sage.schemes.toric.weierstrass.WeierstrassForm` with
130
``transformation=True`` to get the transformation. This function
131
is only for internal use.
132
133
INPUT:
134
135
- ``polynomial`` -- a polynomial. The toric hypersurface
136
equation. Can be either a cubic, a biquadric, or the
137
hypersurface in `\mathbb{P}^2[1,1,2]`. The equation need not be
138
in any standard form, only its Newton polyhedron is used.
139
140
- ``variables`` -- a list of variables of the parent polynomial
141
ring or ``None`` (default). In the latter case, all variables
142
are taken to be polynomial ring variables. If a subset of
143
polynomial ring variables are given, the Weierstrass form is
144
determined over the function field generated by the remaining
145
variables.
146
147
OUTPUT:
148
149
A triple `(X,Y,Z)` of polynomials defining a rational map of the
150
toric hypersurface to its Weierstrass form in
151
`\mathbb{P}^2[2,3,1]`. That is, the triple satisfies
152
153
.. math::
154
155
Y^2 = X^3 + f X Z^4 + g Z^6
156
157
when restricted to the toric hypersurface.
158
159
EXAMPLES::
160
161
sage: R.<x,y,z> = QQ[]
162
sage: cubic = x^3 + y^3 + z^3
163
sage: X,Y,Z = WeierstrassForm(cubic, transformation=True); (X,Y,Z)
164
(-x^3*y^3 - x^3*z^3 - y^3*z^3,
165
1/2*x^6*y^3 - 1/2*x^3*y^6 - 1/2*x^6*z^3 + 1/2*y^6*z^3
166
+ 1/2*x^3*z^6 - 1/2*y^3*z^6,
167
x*y*z)
168
sage: f, g = WeierstrassForm(cubic); (f,g)
169
(0, -27/4)
170
sage: cubic.divides(-Y^2 + X^3 + f*X*Z^4 + g*Z^6)
171
True
172
173
Only the affine span of the Newton polytope of the polynomial
174
matters. For example::
175
176
sage: WeierstrassForm(cubic.subs(z=1), transformation=True)
177
(-x^3*y^3 - x^3 - y^3,
178
1/2*x^6*y^3 - 1/2*x^3*y^6 - 1/2*x^6
179
+ 1/2*y^6 + 1/2*x^3 - 1/2*y^3,
180
x*y)
181
sage: WeierstrassForm(x * cubic, transformation=True)
182
(-x^3*y^3 - x^3*z^3 - y^3*z^3,
183
1/2*x^6*y^3 - 1/2*x^3*y^6 - 1/2*x^6*z^3 + 1/2*y^6*z^3
184
+ 1/2*x^3*z^6 - 1/2*y^3*z^6,
185
x*y*z)
186
187
This allows you to work with either homogeneous or inhomogeneous
188
variables. For example, here is the del Pezzo surface of degree 8::
189
190
sage: dP8 = toric_varieties.dP8()
191
sage: dP8.inject_variables()
192
Defining t, x, y, z
193
sage: WeierstrassForm(x*y^2 + y^2*z + t^2*x^3 + t^2*z^3, transformation=True)
194
(-1/27*t^4*x^6 - 2/27*t^4*x^5*z - 5/27*t^4*x^4*z^2
195
- 8/27*t^4*x^3*z^3 - 5/27*t^4*x^2*z^4 - 2/27*t^4*x*z^5
196
- 1/27*t^4*z^6 - 4/81*t^2*x^4*y^2 - 4/81*t^2*x^3*y^2*z
197
- 4/81*t^2*x*y^2*z^3 - 4/81*t^2*y^2*z^4 - 2/81*x^2*y^4
198
- 4/81*x*y^4*z - 2/81*y^4*z^2,
199
0,
200
1/3*t^2*x^2*z + 1/3*t^2*x*z^2 - 1/9*x*y^2 - 1/9*y^2*z)
201
sage: WeierstrassForm(x*y^2 + y^2 + x^3 + 1, transformation=True)
202
(-1/27*x^6 - 4/81*x^4*y^2 - 2/81*x^2*y^4 - 2/27*x^5
203
- 4/81*x^3*y^2 - 4/81*x*y^4 - 5/27*x^4 - 2/81*y^4 - 8/27*x^3
204
- 4/81*x*y^2 - 5/27*x^2 - 4/81*y^2 - 2/27*x - 1/27,
205
0,
206
-1/9*x*y^2 + 1/3*x^2 - 1/9*y^2 + 1/3*x)
207
208
By specifying only certain variables we can compute the
209
Weierstrass form over the function field generated by the
210
remaining variables. For example, here is a cubic over `\QQ[a]` ::
211
212
sage: R.<a, x,y,z> = QQ[]
213
sage: cubic = x^3 + a*y^3 + a^2*z^3
214
sage: WeierstrassForm(cubic, variables=[x,y,z], transformation=True)
215
(-a^9*y^3*z^3 - a^8*x^3*z^3 - a^7*x^3*y^3,
216
-1/2*a^14*y^3*z^6 + 1/2*a^13*y^6*z^3 + 1/2*a^13*x^3*z^6
217
- 1/2*a^11*x^3*y^6 - 1/2*a^11*x^6*z^3 + 1/2*a^10*x^6*y^3,
218
a^3*x*y*z)
219
220
TESTS::
221
222
sage: for P in ReflexivePolytopes(2):
223
....: S = ToricVariety(FaceFan(P))
224
....: p = sum( (-S.K()).sections_monomials() )
225
....: f, g = WeierstrassForm(p)
226
....: X,Y,Z = WeierstrassForm(p, transformation=True)
227
....: print P, p.divides(-Y^2 + X^3 + f*X*Z^4 + g*Z^6)
228
Reflexive polytope 0: 2-dimensional, 3 vertices. True
229
Reflexive polytope 1: 2-dimensional, 3 vertices. True
230
Reflexive polytope 2: 2-dimensional, 4 vertices. True
231
Reflexive polytope 3: 2-dimensional, 4 vertices. True
232
Reflexive polytope 4: 2-dimensional, 4 vertices. True
233
Reflexive polytope 5: 2-dimensional, 5 vertices. True
234
Reflexive polytope 6: 2-dimensional, 3 vertices. True
235
Reflexive polytope 7: 2-dimensional, 4 vertices. True
236
Reflexive polytope 8: 2-dimensional, 5 vertices. True
237
Reflexive polytope 9: 2-dimensional, 6 vertices. True
238
Reflexive polytope 10: 2-dimensional, 4 vertices. True
239
Reflexive polytope 11: 2-dimensional, 5 vertices. True
240
Reflexive polytope 12: 2-dimensional, 3 vertices. True
241
Reflexive polytope 13: 2-dimensional, 4 vertices. True
242
Reflexive polytope 14: 2-dimensional, 4 vertices. True
243
Reflexive polytope 15: 2-dimensional, 3 vertices. True
244
"""
245
if variables is None:
246
variables = polynomial.variables()
247
# switch to suitable inhomogeneous coordinates
248
from sage.geometry.polyhedron.ppl_lattice_polygon import (
249
polar_P2_polytope, polar_P1xP1_polytope, polar_P2_112_polytope )
250
from sage.schemes.toric.weierstrass import Newton_polygon_embedded
251
newton_polytope, polynomial_aff, variables_aff = \
252
Newton_polygon_embedded(polynomial, variables)
253
polygon = newton_polytope.embed_in_reflexive_polytope('polytope')
254
# Compute the map in inhomogeneous coordinates
255
if polygon is polar_P2_polytope():
256
X,Y,Z = WeierstrassMap_P2(polynomial_aff, variables_aff)
257
elif polygon is polar_P1xP1_polytope():
258
X,Y,Z = WeierstrassMap_P1xP1(polynomial_aff, variables_aff)
259
elif polygon is polar_P2_112_polytope():
260
X,Y,Z = WeierstrassMap_P2_112(polynomial_aff, variables_aff)
261
else:
262
assert False, 'Newton polytope is not contained in a reflexive polygon'
263
# homogenize again
264
R = polynomial.parent()
265
x = R.gens().index(variables_aff[0])
266
y = R.gens().index(variables_aff[1])
267
hom = newton_polytope.embed_in_reflexive_polytope('hom')
268
def homogenize(inhomog, degree):
269
e = tuple(hom._A * vector(ZZ,[inhomog[x], inhomog[y]]) + degree * hom._b)
270
result = list(inhomog)
271
for i, var in enumerate(variables):
272
result[R.gens().index(var)] = e[i]
273
result = vector(ZZ, result)
274
result.set_immutable()
275
return result
276
X_dict = dict( (homogenize(e,2), v) for e,v in X.dict().iteritems() )
277
Y_dict = dict( (homogenize(e,3), v) for e,v in Y.dict().iteritems() )
278
Z_dict = dict( (homogenize(e,1), v) for e,v in Z.dict().iteritems() )
279
# shift to non-negative exponents if necessary
280
min_deg = [0]*R.ngens()
281
for var in variables:
282
i = R.gens().index(var)
283
min_X = min([ e[i] for e in X_dict ]) if len(X_dict)>0 else 0
284
min_Y = min([ e[i] for e in Y_dict ]) if len(Y_dict)>0 else 0
285
min_Z = min([ e[i] for e in Z_dict ]) if len(Z_dict)>0 else 0
286
min_deg[i] = min( min_X/2, min_Y/3, min_Z )
287
min_deg = vector(min_deg)
288
X_dict = dict( (tuple(e-2*min_deg), v) for e,v in X_dict.iteritems() )
289
Y_dict = dict( (tuple(e-3*min_deg), v) for e,v in Y_dict.iteritems() )
290
Z_dict = dict( (tuple(e-1*min_deg), v) for e,v in Z_dict.iteritems() )
291
return (R(X_dict), R(Y_dict), R(Z_dict))
292
293
294
######################################################################
295
#
296
# Weierstrass form of cubic in P^2
297
#
298
######################################################################
299
300
def WeierstrassMap_P2(polynomial, variables=None):
301
r"""
302
Map a cubic to its Weierstrass form
303
304
Input/output is the same as :func:`WeierstrassMap`, except that
305
the input polynomial must be a cubic in `\mathbb{P}^2`,
306
307
.. math::
308
309
\begin{split}
310
p(x,y) =&\;
311
a_{30} x^{3} + a_{21} x^{2} y + a_{12} x y^{2} +
312
a_{03} y^{3} + a_{20} x^{2} +
313
\\ &\;
314
a_{11} x y +
315
a_{02} y^{2} + a_{10} x + a_{01} y + a_{00}
316
\end{split}
317
318
EXAMPLES::
319
320
sage: from sage.schemes.toric.weierstrass import WeierstrassForm_P2
321
sage: from sage.schemes.toric.weierstrass_covering import WeierstrassMap_P2
322
sage: R.<x,y,z> = QQ[]
323
sage: equation = x^3+y^3+z^3+x*y*z
324
sage: f, g = WeierstrassForm_P2(equation)
325
sage: X,Y,Z = WeierstrassMap_P2(equation)
326
sage: equation.divides(-Y^2 + X^3 + f*X*Z^4 + g*Z^6)
327
True
328
329
sage: from sage.schemes.toric.weierstrass import WeierstrassForm_P2
330
sage: from sage.schemes.toric.weierstrass_covering import WeierstrassMap_P2
331
sage: R.<x,y> = QQ[]
332
sage: equation = x^3+y^3+1
333
sage: f, g = WeierstrassForm_P2(equation)
334
sage: X,Y,Z = WeierstrassMap_P2(equation)
335
sage: equation.divides(-Y^2 + X^3 + f*X*Z^4 + g*Z^6)
336
True
337
"""
338
x,y,z = _check_polynomial_P2(polynomial, variables)
339
cubic = invariant_theory.ternary_cubic(polynomial, x,y,z)
340
H = cubic.Hessian()
341
Theta = cubic.Theta_covariant()
342
J = cubic.J_covariant()
343
F = polynomial.parent().base_ring()
344
return (Theta, J/F(2), H)
345
346
347
######################################################################
348
#
349
# Weierstrass form of biquadric in P1 x P1
350
#
351
######################################################################
352
353
def WeierstrassMap_P1xP1(polynomial, variables=None):
354
r"""
355
Map an anticanonical hypersurface in
356
`\mathbb{P}^1 \times \mathbb{P}^1` into Weierstrass form.
357
358
Input/output is the same as :func:`WeierstrassMap`, except that
359
the input polynomial must be a standard anticanonical hypersurface
360
in the toric surface `\mathbb{P}^1 \times \mathbb{P}^1`:
361
362
EXAMPLES::
363
364
sage: from sage.schemes.toric.weierstrass_covering import WeierstrassMap_P1xP1
365
sage: from sage.schemes.toric.weierstrass import WeierstrassForm_P1xP1
366
sage: R.<x0,x1,y0,y1,a>= QQ[]
367
sage: biquadric = ( x0^2*y0^2 + x1^2*y0^2 + x0^2*y1^2 + x1^2*y1^2 +
368
....: a * x0*x1*y0*y1*5 )
369
sage: f, g = WeierstrassForm_P1xP1(biquadric, [x0, x1, y0, y1]); (f,g)
370
(-625/48*a^4 + 25/3*a^2 - 16/3, 15625/864*a^6 - 625/36*a^4 - 100/9*a^2 + 128/27)
371
sage: X, Y, Z = WeierstrassMap_P1xP1(biquadric, [x0, x1, y0, y1])
372
sage: (-Y^2 + X^3 + f*X*Z^4 + g*Z^6).reduce(R.ideal(biquadric))
373
0
374
375
sage: R = PolynomialRing(QQ, 'x,y,s,t', order='lex')
376
sage: R.inject_variables()
377
Defining x, y, s, t
378
sage: equation = ( s^2*(x^2+2*x*y+3*y^2) + s*t*(4*x^2+5*x*y+6*y^2)
379
....: + t^2*(7*x^2+8*x*y+9*y^2) )
380
sage: X, Y, Z = WeierstrassMap_P1xP1(equation, [x,y,s,t])
381
sage: f, g = WeierstrassForm_P1xP1(equation, variables=[x,y,s,t])
382
sage: (-Y^2 + X^3 + f*X*Z^4 + g*Z^6).reduce(R.ideal(equation))
383
0
384
385
sage: R = PolynomialRing(QQ, 'x,s', order='lex')
386
sage: R.inject_variables()
387
Defining x, s
388
sage: equation = s^2*(x^2+2*x+3) + s*(4*x^2+5*x+6) + (7*x^2+8*x+9)
389
sage: X, Y, Z = WeierstrassMap_P1xP1(equation)
390
sage: f, g = WeierstrassForm_P1xP1(equation)
391
sage: (-Y^2 + X^3 + f*X*Z^4 + g*Z^6).reduce(R.ideal(equation))
392
0
393
"""
394
x,y,s,t = _check_polynomial_P1xP1(polynomial, variables)
395
a00 = polynomial.coefficient({s:2})
396
V = polynomial.coefficient({s:1})
397
U = - _partial_discriminant(polynomial, s, t) / 4
398
Q = invariant_theory.binary_quartic(U, x, y)
399
g = Q.g_covariant()
400
h = Q.h_covariant()
401
if t is None:
402
t = 1
403
return ( 4*g*t**2, 4*h*t**3, (a00*s+V/2) )
404
405
406
######################################################################
407
#
408
# Weierstrass form of anticanonical hypersurface in WP2[1,1,2]
409
#
410
######################################################################
411
412
def WeierstrassMap_P2_112(polynomial, variables=None):
413
r"""
414
Map an anticanonical hypersurface in `\mathbb{P}^2[1,1,2]` into Weierstrass form.
415
416
Input/output is the same as :func:`WeierstrassMap`, except that
417
the input polynomial must be a standard anticanonical hypersurface
418
in weighted projective space `\mathbb{P}^2[1,1,2]`:
419
420
.. math::
421
422
\begin{split}
423
p(x,y) =&\;
424
a_{40} x^4 +
425
a_{30} x^3 +
426
a_{21} x^2 y +
427
a_{20} x^2 +
428
\\ &\;
429
a_{11} x y +
430
a_{02} y^2 +
431
a_{10} x +
432
a_{01} y +
433
a_{00}
434
\end{split}
435
436
EXAMPLES::
437
438
sage: from sage.schemes.toric.weierstrass_covering import WeierstrassMap_P2_112
439
sage: from sage.schemes.toric.weierstrass import WeierstrassForm_P2_112
440
sage: R = PolynomialRing(QQ, 'x,y,a0,a1,a2,a3,a4', order='lex')
441
sage: R.inject_variables()
442
Defining x, y, a0, a1, a2, a3, a4
443
sage: equation = y^2 + a0*x^4 + 4*a1*x^3 + 6*a2*x^2 + 4*a3*x + a4
444
sage: X, Y, Z = WeierstrassMap_P2_112(equation, [x,y])
445
sage: f, g = WeierstrassForm_P2_112(equation, variables=[x,y])
446
sage: (-Y^2 + X^3 + f*X*Z^4 + g*Z^6).reduce(R.ideal(equation))
447
0
448
449
Another example, this time in homogeneous coordinates::
450
451
sage: fan = Fan(rays=[(1,0),(0,1),(-1,-2),(0,-1)],cones=[[0,1],[1,2],[2,3],[3,0]])
452
sage: P112.<x,y,z,t> = ToricVariety(fan)
453
sage: (-P112.K()).sections_monomials()
454
(z^4*t^2, x*z^3*t^2, x^2*z^2*t^2, x^3*z*t^2,
455
x^4*t^2, y*z^2*t, x*y*z*t, x^2*y*t, y^2)
456
sage: C_eqn = sum(_)
457
sage: C = P112.subscheme(C_eqn)
458
sage: WeierstrassForm_P2_112(C_eqn, [x,y,z,t])
459
(-97/48, 17/864)
460
sage: X, Y, Z = WeierstrassMap_P2_112(C_eqn, [x,y,z,t])
461
sage: (-Y^2 + X^3 - 97/48*X*Z^4 + 17/864*Z^6).reduce(C.defining_ideal())
462
0
463
"""
464
x,y,z,t = _check_polynomial_P2_112(polynomial, variables)
465
a00 = polynomial.coefficient({y:2})
466
V = polynomial.coefficient({y:1})
467
U = - _partial_discriminant(polynomial, y, t) / 4
468
Q = invariant_theory.binary_quartic(U, x, z)
469
g = Q.g_covariant()
470
h = Q.h_covariant()
471
if t is None:
472
t = 1
473
return ( 4*g*t**2, 4*h*t**3, (a00*y+V/2) )
474
475