Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/rings/invariant_theory.py
8817 views
1
r"""
2
Classical Invariant Theory
3
4
This module lists classical invariants and covariants of homogeneous
5
polynomials (also called algebraic forms) under the action of the
6
special linear group. That is, we are dealing with polynomials of
7
degree `d` in `n` variables. The special linear group `SL(n,\CC)` acts
8
on the variables `(x_1,\dots, x_n)` linearly,
9
10
.. math::
11
12
(x_1,\dots, x_n)^t \to A (x_1,\dots, x_n)^t
13
,\qquad
14
A \in SL(n,\CC)
15
16
The linear action on the variables transforms a polynomial `p`
17
generally into a different polynomial `gp`. We can think of it as an
18
action on the space of coefficients in `p`. An invariant is a
19
polynomial in the coefficients that is invariant under this action. A
20
covariant is a polynomial in the coefficients and the variables
21
`(x_1,\dots, x_n)` that is invariant under the combined action.
22
23
For example, the binary quadratic `p(x,y) = a x^2 + b x y + c y^2`
24
has as its invariant the discriminant `\mathop{disc}(p) = b^2 - 4 a
25
c`. This means that for any `SL(2,\CC)` coordinate change
26
27
.. math::
28
29
\begin{pmatrix} x' \\ y' \end{pmatrix}
30
=
31
\begin{pmatrix} \alpha & \beta \\ \gamma & \delta \end{pmatrix}
32
\begin{pmatrix} x \\ y \end{pmatrix}
33
\qquad
34
\alpha\delta-\beta\gamma=1
35
36
the discriminant is invariant, `\mathop{disc}\big(p(x',y')\big) =
37
\mathop{disc}\big(p(x,y)\big)`.
38
39
To use this module, you should use the factory object
40
:class:`invariant_theory <InvariantTheoryFactory>`. For example, take
41
the quartic::
42
43
sage: R.<x,y> = QQ[]
44
sage: q = x^4 + y^4
45
sage: quartic = invariant_theory.binary_quartic(q); quartic
46
Binary quartic with coefficients (1, 0, 0, 0, 1)
47
48
49
One invariant of a quartic is known as the Eisenstein
50
D-invariant. Since it is an invariant, it is a polynomial in the
51
coefficients (which are integers in this example)::
52
53
sage: quartic.EisensteinD()
54
1
55
56
One example of a covariant of a quartic is the so-called g-covariant
57
(actually, the Hessian). As with all covariants, it is a polynomial in
58
`x`, `y` and the coefficients::
59
60
sage: quartic.g_covariant()
61
-x^2*y^2
62
63
As usual, use tab completion and the online help to discover the
64
implemented invariants and covariants.
65
66
In general, the variables of the defining polynomial cannot be
67
guessed. For example, the zero polynomial can be thought of as a
68
homogeneous polynomial of any degree. Also, since we also want to
69
allow polynomial coefficients we cannot just take all variables of the
70
polynomial ring as the variables of the form. This is why you will
71
have to specify the variables explicitly if there is any potential
72
ambiguity. For example::
73
74
sage: invariant_theory.binary_quartic(R.zero(), [x,y])
75
Binary quartic with coefficients (0, 0, 0, 0, 0)
76
77
sage: invariant_theory.binary_quartic(x^4, [x,y])
78
Binary quartic with coefficients (0, 0, 0, 0, 1)
79
80
sage: R.<x,y,t> = QQ[]
81
sage: invariant_theory.binary_quartic(x^4 + y^4 + t*x^2*y^2, [x,y])
82
Binary quartic with coefficients (1, 0, t, 0, 1)
83
84
Finally, it is often convenient to use inhomogeneous polynomials where
85
it is understood that one wants to homogenize them. This is also
86
supported, just define the form with an inhomogeneous polynomial and
87
specify one less variable::
88
89
sage: R.<x,t> = QQ[]
90
sage: invariant_theory.binary_quartic(x^4 + 1 + t*x^2, [x])
91
Binary quartic with coefficients (1, 0, t, 0, 1)
92
93
REFERENCES:
94
95
.. [WpInvariantTheory]
96
http://en.wikipedia.org/wiki/Glossary_of_invariant_theory
97
"""
98
99
#*****************************************************************************
100
# Copyright (C) 2012 Volker Braun <[email protected]>
101
#
102
# Distributed under the terms of the GNU General Public License (GPL)
103
# as published by the Free Software Foundation; either version 2 of
104
# the License, or (at your option) any later version.
105
# http://www.gnu.org/licenses/
106
#*****************************************************************************
107
108
109
from sage.rings.all import QQ
110
from sage.misc.functional import is_odd
111
from sage.matrix.constructor import matrix
112
from sage.structure.sage_object import SageObject
113
from sage.misc.cachefunc import cached_method
114
115
116
117
######################################################################
118
def _guess_variables(polynomial, *args):
119
"""
120
Return the polynomial variables.
121
122
INPUT:
123
124
- ``polynomial`` -- a polynomial, or a list/tuple of polynomials
125
in the same polynomial ring.
126
127
- ``*args`` -- the variables. If none are specified, all variables
128
in ``polynomial`` are returned. If a list or tuple is passed,
129
the content is returned. If multiple arguments are passed, they
130
are returned.
131
132
OUTPUT:
133
134
A tuple of variables in the parent ring of the polynomial(s).
135
136
EXAMPLES::
137
138
sage: from sage.rings.invariant_theory import _guess_variables
139
sage: R.<x,y> = QQ[]
140
sage: _guess_variables(x^2+y^2)
141
(x, y)
142
sage: _guess_variables([x^2, y^2])
143
(x, y)
144
sage: _guess_variables(x^2+y^2, x)
145
(x,)
146
sage: _guess_variables(x^2+y^2, x,y)
147
(x, y)
148
sage: _guess_variables(x^2+y^2, [x,y])
149
(x, y)
150
"""
151
if isinstance(polynomial, (list, tuple)):
152
R = polynomial[0].parent()
153
if not all(p.parent() is R for p in polynomial):
154
raise ValueError('All input polynomials must be in the same ring.')
155
if len(args)==0 or (len(args)==1 and args[0] is None):
156
if isinstance(polynomial, (list, tuple)):
157
variables = set()
158
for p in polynomial:
159
variables.update(p.variables())
160
variables = list(variables)
161
variables.reverse() # to match polynomial.variables() behavior
162
return tuple(variables)
163
else:
164
return polynomial.variables()
165
elif len(args) == 1 and isinstance(args[0], (tuple, list)):
166
return tuple(args[0])
167
else:
168
return tuple(args)
169
170
171
######################################################################
172
173
class FormsBase(SageObject):
174
"""
175
The common base class of :class:`AlgebraicForm` and
176
:class:`SeveralAlgebraicForms`.
177
178
This is an abstract base class to provide common methods. It does
179
not make much sense to instantiate it.
180
181
TESTS::
182
183
sage: from sage.rings.invariant_theory import FormsBase
184
sage: FormsBase(None, None, None, None)
185
<class 'sage.rings.invariant_theory.FormsBase'>
186
"""
187
188
def __init__(self, n, homogeneous, ring, variables):
189
"""
190
The Python constructor.
191
192
TESTS::
193
194
sage: from sage.rings.invariant_theory import FormsBase
195
sage: FormsBase(None, None, None, None)
196
<class 'sage.rings.invariant_theory.FormsBase'>
197
"""
198
self._n = n
199
self._homogeneous = homogeneous
200
self._ring = ring
201
self._variables = variables
202
203
204
def _jacobian_determinant(self, *args):
205
"""
206
Return the Jacobian determinant.
207
208
INPUT:
209
210
- ``*args`` -- list of pairs of a polynomial and its
211
homogeneous degree. Must be a covariant, that is, polynomial
212
in the given :meth:`variables`
213
214
OUTPUT:
215
216
The Jacobian determinant with respect to the variables.
217
218
EXAMPLES::
219
220
221
sage: R.<x,y> = QQ[]
222
sage: from sage.rings.invariant_theory import FormsBase
223
sage: f = FormsBase(2, True, R, (x, y))
224
sage: f._jacobian_determinant((x^2+y^2, 2), (x*y, 2))
225
2*x^2 - 2*y^2
226
sage: f = FormsBase(2, False, R, (x, y))
227
sage: f._jacobian_determinant((x^2+1, 2), (x, 2))
228
2*x^2 - 2
229
230
sage: R.<x,y> = QQ[]
231
sage: cubic = invariant_theory.ternary_cubic(x^3+y^3+1)
232
sage: cubic.J_covariant()
233
x^6*y^3 - x^3*y^6 - x^6 + y^6 + x^3 - y^3
234
sage: 1 / 9 * cubic._jacobian_determinant(
235
....: [cubic.form(), 3], [cubic.Hessian(), 3], [cubic.Theta_covariant(), 6])
236
x^6*y^3 - x^3*y^6 - x^6 + y^6 + x^3 - y^3
237
"""
238
if self._homogeneous:
239
def diff(p, d):
240
return [p.derivative(x) for x in self._variables]
241
else:
242
def diff(p, d):
243
variables = self._variables[0:-1]
244
grad = [p.derivative(x) for x in variables]
245
dp_dz = d*p - sum(x*dp_dx for x, dp_dx in zip(variables, grad))
246
grad.append(dp_dz)
247
return grad
248
jac = [diff(p,d) for p,d in args]
249
return matrix(self._ring, jac).det()
250
251
252
def ring(self):
253
"""
254
Return the polynomial ring.
255
256
OUTPUT:
257
258
A polynomial ring. This is where the defining polynomial(s)
259
live. Note that the polynomials may be homogeneous or
260
inhomogeneous, depending on how the user constructed the
261
object.
262
263
EXAMPLES::
264
265
sage: R.<x,y,t> = QQ[]
266
sage: quartic = invariant_theory.binary_quartic(x^4+y^4+t*x^2*y^2, [x,y])
267
sage: quartic.ring()
268
Multivariate Polynomial Ring in x, y, t over Rational Field
269
270
sage: R.<x,y,t> = QQ[]
271
sage: quartic = invariant_theory.binary_quartic(x^4+1+t*x^2, [x])
272
sage: quartic.ring()
273
Multivariate Polynomial Ring in x, y, t over Rational Field
274
"""
275
return self._ring
276
277
278
def variables(self):
279
"""
280
Return the variables of the form.
281
282
OUTPUT:
283
284
A tuple of variables. If inhomogeneous notation is used for the
285
defining polynomial then the last entry will be ``None``.
286
287
EXAMPLES::
288
289
sage: R.<x,y,t> = QQ[]
290
sage: quartic = invariant_theory.binary_quartic(x^4+y^4+t*x^2*y^2, [x,y])
291
sage: quartic.variables()
292
(x, y)
293
294
sage: R.<x,y,t> = QQ[]
295
sage: quartic = invariant_theory.binary_quartic(x^4+1+t*x^2, [x])
296
sage: quartic.variables()
297
(x, None)
298
"""
299
return self._variables
300
301
302
def is_homogeneous(self):
303
"""
304
Return whether the forms were defined by homogeneous polynomials.
305
306
OUTPUT:
307
308
Boolean. Whether the user originally defined the form via
309
homogeneous variables.
310
311
EXAMPLES::
312
313
sage: R.<x,y,t> = QQ[]
314
sage: quartic = invariant_theory.binary_quartic(x^4+y^4+t*x^2*y^2, [x,y])
315
sage: quartic.is_homogeneous()
316
True
317
sage: quartic.form()
318
x^2*y^2*t + x^4 + y^4
319
320
sage: R.<x,y,t> = QQ[]
321
sage: quartic = invariant_theory.binary_quartic(x^4+1+t*x^2, [x])
322
sage: quartic.is_homogeneous()
323
False
324
sage: quartic.form()
325
x^4 + x^2*t + 1
326
"""
327
return self._homogeneous
328
329
330
######################################################################
331
332
class AlgebraicForm(FormsBase):
333
"""
334
The base class of algebraic forms (i.e. homogeneous polynomials).
335
336
You should only instantiate the derived classes of this base
337
class.
338
339
Derived classes must implement ``coeffs()`` and
340
``scaled_coeffs()``
341
342
INPUT:
343
344
- ``n`` -- The number of variables.
345
346
- ``d`` -- The degree of the polynomial.
347
348
- ``polynomial`` -- The polynomial.
349
350
- ``*args`` -- The variables, as a single list/tuple, multiple
351
arguments, or ``None`` to use all variables of the polynomial.
352
353
Derived classes must implement the same arguments for the
354
constructor.
355
356
EXAMPLES::
357
358
sage: from sage.rings.invariant_theory import AlgebraicForm
359
sage: R.<x,y> = QQ[]
360
sage: p = x^2 + y^2
361
sage: AlgebraicForm(2, 2, p).variables()
362
(x, y)
363
sage: AlgebraicForm(2, 2, p, None).variables()
364
(x, y)
365
sage: AlgebraicForm(3, 2, p).variables()
366
(x, y, None)
367
sage: AlgebraicForm(3, 2, p, None).variables()
368
(x, y, None)
369
370
sage: from sage.rings.invariant_theory import AlgebraicForm
371
sage: R.<x,y,s,t> = QQ[]
372
sage: p = s*x^2 + t*y^2
373
sage: AlgebraicForm(2, 2, p, [x,y]).variables()
374
(x, y)
375
sage: AlgebraicForm(2, 2, p, x,y).variables()
376
(x, y)
377
378
sage: AlgebraicForm(3, 2, p, [x,y,None]).variables()
379
(x, y, None)
380
sage: AlgebraicForm(3, 2, p, x,y,None).variables()
381
(x, y, None)
382
383
sage: AlgebraicForm(2, 1, p, [x,y]).variables()
384
Traceback (most recent call last):
385
...
386
ValueError: Polynomial is of the wrong degree.
387
388
sage: AlgebraicForm(2, 2, x^2+y, [x,y]).variables()
389
Traceback (most recent call last):
390
...
391
ValueError: Polynomial is not homogeneous.
392
"""
393
394
def __init__(self, n, d, polynomial, *args, **kwds):
395
"""
396
The Python constructor.
397
398
INPUT:
399
400
See the class documentation.
401
402
TESTS::
403
404
sage: from sage.rings.invariant_theory import AlgebraicForm
405
sage: R.<x,y> = QQ[]
406
sage: form = AlgebraicForm(2, 2, x^2 + y^2)
407
"""
408
self._d = d
409
self._polynomial = polynomial
410
variables = _guess_variables(polynomial, *args)
411
if len(variables) == n:
412
pass
413
elif len(variables) == n-1:
414
variables = variables + (None,)
415
else:
416
raise ValueError('Need '+str(n)+' or '+
417
str(n-1)+' variables, got '+str(variables))
418
ring = polynomial.parent()
419
homogeneous = variables[-1] is not None
420
super(AlgebraicForm, self).__init__(n, homogeneous, ring, variables)
421
self._check()
422
423
424
def _check(self):
425
"""
426
Check that the input is of the correct degree and number of
427
variables.
428
429
EXAMPLES::
430
431
sage: from sage.rings.invariant_theory import AlgebraicForm
432
sage: R.<x,y,t> = QQ[]
433
sage: p = x^2 + y^2
434
sage: inv = AlgebraicForm(3, 2, p, [x,y,None])
435
sage: inv._check()
436
"""
437
degrees = set()
438
R = self._ring
439
if R.ngens() == 1:
440
degrees.update(self._polynomial.exponents())
441
else:
442
for e in self._polynomial.exponents():
443
deg = sum([ e[R.gens().index(x)]
444
for x in self._variables if x is not None ])
445
degrees.add(deg)
446
if self._homogeneous and len(degrees)>1:
447
raise ValueError('Polynomial is not homogeneous.')
448
if degrees == set() or \
449
(self._homogeneous and degrees == set([self._d])) or \
450
(not self._homogeneous and max(degrees) <= self._d):
451
return
452
else:
453
raise ValueError('Polynomial is of the wrong degree.')
454
455
456
def _check_covariant(self, method_name, g=None, invariant=False):
457
"""
458
Test whether ``method_name`` actually returns a covariant.
459
460
INPUT:
461
462
- ``method_name`` -- string. The name of the method that
463
returns the invariant / covariant to test.
464
465
- ``g`` -- an `SL(n,\CC)` matrix or ``None`` (default). The
466
test will be to check that the covariant transforms
467
corrently under this special linear group element acting on
468
the homogeneous variables. If ``None``, a random matrix will
469
be picked.
470
471
- ``invariant`` -- boolean. Whether to additionaly test that
472
it is an invariant.
473
474
EXAMPLES::
475
476
sage: R.<a0, a1, a2, a3, a4, x0, x1> = QQ[]
477
sage: p = a0*x1^4 + a1*x1^3*x0 + a2*x1^2*x0^2 + a3*x1*x0^3 + a4*x0^4
478
sage: quartic = invariant_theory.binary_quartic(p, x0, x1)
479
480
sage: quartic._check_covariant('EisensteinE', invariant=True)
481
sage: quartic._check_covariant('h_covariant')
482
483
sage: quartic._check_covariant('h_covariant', invariant=True)
484
Traceback (most recent call last):
485
...
486
AssertionError: Not invariant.
487
"""
488
assert self._homogeneous
489
from sage.matrix.constructor import vector, random_matrix
490
if g is None:
491
F = self._ring.base_ring()
492
g = random_matrix(F, self._n, algorithm='unimodular')
493
v = vector(self.variables())
494
g_v = g*v
495
transform = dict( (v[i], g_v[i]) for i in range(self._n) )
496
# The covariant of the transformed polynomial
497
g_self = self.__class__(self._n, self._d, self.form().subs(transform), self.variables())
498
cov_g = getattr(g_self, method_name)()
499
# The transform of the covariant
500
g_cov = getattr(self, method_name)().subs(transform)
501
# they must be the same
502
assert (g_cov - cov_g).is_zero(), 'Not covariant.'
503
if invariant:
504
cov = getattr(self, method_name)()
505
assert (cov - cov_g).is_zero(), 'Not invariant.'
506
507
508
def __cmp__(self, other):
509
"""
510
Compare ``self`` with ``other``.
511
512
EXAMPLES::
513
514
sage: R.<x,y> = QQ[]
515
sage: quartic = invariant_theory.binary_quartic(x^4+y^4)
516
sage: cmp(quartic, 'foo') == 0
517
False
518
sage: cmp(quartic, quartic)
519
0
520
sage: quartic.__cmp__(quartic)
521
0
522
"""
523
c = cmp(type(self), type(other))
524
if c != 0:
525
return c
526
return cmp(self.coeffs(), other.coeffs())
527
528
529
def _repr_(self):
530
"""
531
Return a string representation.
532
533
OUTPUT:
534
535
String.
536
537
EXAMPLES::
538
539
sage: R.<x,y> = QQ[]
540
sage: quartic = invariant_theory.binary_quartic(x^4+y^4)
541
sage: quartic._repr_()
542
'Binary quartic with coefficients (1, 0, 0, 0, 1)'
543
"""
544
s = ''
545
ary = ['Unary', 'Binary', 'Ternary', 'Quaternary', 'Quinary',
546
'Senary', 'Septenary', 'Octonary', 'Nonary', 'Denary']
547
try:
548
s += ary[self._n-1]
549
except IndexError:
550
s += 'algebraic'
551
ic = ['monic', 'quadratic', 'cubic', 'quartic', 'quintic',
552
'sextic', 'septimic', 'octavic', 'nonic', 'decimic',
553
'undecimic', 'duodecimic']
554
s += ' '
555
try:
556
s += ic[self._d-1]
557
except IndexError:
558
s += 'form'
559
s += ' with coefficients ' + str(self.coeffs())
560
return s
561
562
563
def form(self):
564
"""
565
Return the defining polynomial.
566
567
OUTPUT:
568
569
The polynomial used to define the algebraic form.
570
571
EXAMPLES::
572
573
sage: R.<x,y> = QQ[]
574
sage: quartic = invariant_theory.binary_quartic(x^4+y^4)
575
sage: quartic.form()
576
x^4 + y^4
577
sage: quartic.polynomial()
578
x^4 + y^4
579
"""
580
return self._polynomial
581
582
polynomial = form
583
584
585
def homogenized(self, var='h'):
586
"""
587
Return form as defined by a homogeneous polynomial.
588
589
INPUT:
590
591
- ``var`` -- either a variable name, variable index or a
592
variable (default: ``'h'``).
593
594
OUTPUT:
595
596
The same algebraic form, but defined by a homogeneous
597
polynomial.
598
599
EXAMPLES::
600
601
sage: T.<t> = QQ[]
602
sage: quadratic = invariant_theory.binary_quadratic(t^2 + 2*t + 3)
603
sage: quadratic
604
Binary quadratic with coefficients (1, 3, 2)
605
sage: quadratic.homogenized()
606
Binary quadratic with coefficients (1, 3, 2)
607
sage: quadratic == quadratic.homogenized()
608
True
609
sage: quadratic.form()
610
t^2 + 2*t + 3
611
sage: quadratic.homogenized().form()
612
t^2 + 2*t*h + 3*h^2
613
614
sage: R.<x,y,z> = QQ[]
615
sage: quadratic = invariant_theory.ternary_quadratic(x^2 + 1, [x,y])
616
sage: quadratic.homogenized().form()
617
x^2 + h^2
618
"""
619
if self._homogeneous:
620
return self
621
try:
622
polynomial = self._polynomial.homogenize(var)
623
R = polynomial.parent()
624
variables = map(R, self._variables[0:-1]) + [R(var)]
625
except AttributeError:
626
from sage.rings.all import PolynomialRing
627
R = PolynomialRing(self._ring.base_ring(), [str(self._ring.gen(0)), str(var)])
628
polynomial = R(self._polynomial).homogenize(var)
629
variables = R.gens()
630
return self.__class__(self._n, self._d, polynomial, variables)
631
632
def _extract_coefficients(self, monomials):
633
"""
634
Return the coefficients of ``monomials``.
635
636
INPUT:
637
638
- ``polynomial`` -- the input polynomial
639
640
- ``monomials`` -- a list of all the monomials in the polynomial
641
ring. If less monomials are passed, an exception is thrown.
642
643
OUTPUT:
644
645
A tuple containing the coefficients of the monomials in the given
646
polynomial.
647
648
EXAMPLES::
649
650
sage: from sage.rings.invariant_theory import AlgebraicForm
651
sage: R.<x,y,z,a30,a21,a12,a03,a20,a11,a02,a10,a01,a00> = QQ[]
652
sage: p = ( a30*x^3 + a21*x^2*y + a12*x*y^2 + a03*y^3 + a20*x^2*z +
653
... a11*x*y*z + a02*y^2*z + a10*x*z^2 + a01*y*z^2 + a00*z^3 )
654
sage: base = AlgebraicForm(3, 3, p, [x,y,z])
655
sage: m = [x^3, y^3, z^3, x^2*y, x^2*z, x*y^2, y^2*z, x*z^2, y*z^2, x*y*z]
656
sage: base._extract_coefficients(m)
657
(a30, a03, a00, a21, a20, a12, a02, a10, a01, a11)
658
659
sage: base = AlgebraicForm(3, 3, p.subs(z=1), [x,y])
660
sage: m = [x^3, y^3, 1, x^2*y, x^2, x*y^2, y^2, x, y, x*y]
661
sage: base._extract_coefficients(m)
662
(a30, a03, a00, a21, a20, a12, a02, a10, a01, a11)
663
664
sage: T.<t> = QQ[]
665
sage: univariate = AlgebraicForm(2, 3, t^3+2*t^2+3*t+4)
666
sage: m = [t^3, 1, t, t^2]
667
sage: univariate._extract_coefficients(m)
668
(1, 4, 3, 2)
669
sage: univariate._extract_coefficients(m[1:])
670
Traceback (most recent call last):
671
...
672
ValueError: Less monomials were passed than the form actually has.
673
"""
674
R = self._ring
675
if self._homogeneous:
676
variables = self._variables
677
else:
678
variables = self._variables[0:-1]
679
indices = [ R.gens().index(x) for x in variables ]
680
coeffs = dict()
681
if R.ngens() == 1:
682
# Univariate polynomials
683
assert indices == [0]
684
coefficient_monomial_iter = [(c, R.gen(0)**i) for i,c in
685
enumerate(self._polynomial.padded_list())]
686
def index(monomial):
687
if monomial in R.base_ring():
688
return (0,)
689
return (monomial.exponents()[0],)
690
else:
691
# Multivariate polynomials
692
coefficient_monomial_iter = self._polynomial
693
def index(monomial):
694
if monomial in R.base_ring():
695
return tuple(0 for i in indices)
696
e = monomial.exponents()[0]
697
return tuple(e[i] for i in indices)
698
for c,m in coefficient_monomial_iter:
699
i = index(m)
700
coeffs[i] = c*m + coeffs.pop(i, R.zero())
701
result = tuple(coeffs.pop(index(m), R.zero()) // m for m in monomials)
702
if len(coeffs):
703
raise ValueError('Less monomials were passed than the form actually has.')
704
return result
705
706
707
def coefficients(self):
708
"""
709
Alias for ``coeffs()``.
710
711
See the documentation for ``coeffs()`` for details.
712
713
EXAMPLES::
714
715
sage: R.<a,b,c,d,e,f,g, x,y,z> = QQ[]
716
sage: p = a*x^2 + b*y^2 + c*z^2 + d*x*y + e*x*z + f*y*z
717
sage: q = invariant_theory.quadratic_form(p, x,y,z)
718
sage: q.coefficients()
719
(a, b, c, d, e, f)
720
sage: q.coeffs()
721
(a, b, c, d, e, f)
722
"""
723
return self.coeffs()
724
725
726
def transformed(self, g):
727
"""
728
Return the image under a linear transformation of the variables.
729
730
INPUT:
731
732
- ``g`` -- a `GL(n,\CC)` matrix or a dictionary with the
733
variables as keys. A matrix is used to define the linear
734
transformation of homogeneous variables, a dictionary acts
735
by substitution of the variables.
736
737
OUTPUT:
738
739
A new instance of a subclass of :class:`AlgebraicForm`
740
obtained by replacing the variables of the homogeneous
741
polynomial by their image under ``g``.
742
743
EXAMPLES::
744
745
sage: R.<x,y,z> = QQ[]
746
sage: cubic = invariant_theory.ternary_cubic(x^3 + 2*y^3 + 3*z^3 + 4*x*y*z)
747
sage: cubic.transformed({x:y, y:z, z:x}).form()
748
3*x^3 + y^3 + 4*x*y*z + 2*z^3
749
sage: cyc = matrix([[0,1,0],[0,0,1],[1,0,0]])
750
sage: cubic.transformed(cyc) == cubic.transformed({x:y, y:z, z:x})
751
True
752
sage: g = matrix(QQ, [[1, 0, 0], [-1, 1, -3], [-5, -5, 16]])
753
sage: cubic.transformed(g)
754
Ternary cubic with coefficients (-356, -373, 12234, -1119, 3578, -1151,
755
3582, -11766, -11466, 7360)
756
sage: cubic.transformed(g).transformed(g.inverse()) == cubic
757
True
758
"""
759
form = self.homogenized()
760
if isinstance(g, dict):
761
transform = g
762
else:
763
from sage.modules.all import vector
764
v = vector(self._ring, self._variables)
765
g_v = g*v
766
transform = dict( (v[i], g_v[i]) for i in range(self._n) )
767
# The covariant of the transformed polynomial
768
return self.__class__(self._n, self._d, self.form().subs(transform), self.variables())
769
770
771
######################################################################
772
773
class QuadraticForm(AlgebraicForm):
774
"""
775
Invariant theory of a multivariate quadratic form.
776
777
You should use the :class:`invariant_theory
778
<InvariantTheoryFactory>` factory object to construct instances
779
of this class. See :meth:`~InvariantTheoryFactory.quadratic_form`
780
for details.
781
782
TESTS::
783
784
sage: R.<a,b,c,d,e,f,g, x,y,z> = QQ[]
785
sage: p = a*x^2 + b*y^2 + c*z^2 + d*x*y + e*x*z + f*y*z
786
sage: invariant_theory.quadratic_form(p, x,y,z)
787
Ternary quadratic with coefficients (a, b, c, d, e, f)
788
sage: type(_)
789
<class 'sage.rings.invariant_theory.TernaryQuadratic'>
790
791
sage: R.<a,b,c,d,e,f,g, x,y,z> = QQ[]
792
sage: p = a*x^2 + b*y^2 + c*z^2 + d*x*y + e*x*z + f*y*z
793
sage: invariant_theory.quadratic_form(p, x,y,z)
794
Ternary quadratic with coefficients (a, b, c, d, e, f)
795
sage: type(_)
796
<class 'sage.rings.invariant_theory.TernaryQuadratic'>
797
798
Since we cannot always decide whether the form is homogeneous or
799
not based on the number of variables, you need to explicitly
800
specify it if you want the variables to be treated as
801
inhomogeneous::
802
803
sage: invariant_theory.inhomogeneous_quadratic_form(p.subs(z=1), x,y)
804
Ternary quadratic with coefficients (a, b, c, d, e, f)
805
"""
806
807
def __init__(self, n, d, polynomial, *args):
808
"""
809
The Python constructor.
810
811
TESTS::
812
813
sage: R.<x,y> = QQ[]
814
sage: from sage.rings.invariant_theory import QuadraticForm
815
sage: form = QuadraticForm(2, 2, x^2+2*y^2+3*x*y)
816
sage: form
817
Binary quadratic with coefficients (1, 2, 3)
818
sage: form._check_covariant('discriminant', invariant=True)
819
sage: QuadraticForm(3, 2, x^2+y^2)
820
Ternary quadratic with coefficients (1, 1, 0, 0, 0, 0)
821
"""
822
assert d == 2
823
super(QuadraticForm, self).__init__(n, 2, polynomial, *args)
824
825
826
@cached_method
827
def monomials(self):
828
"""
829
List the basis monomials in the form.
830
831
OUTPUT:
832
833
A tuple of monomials. They are in the same order as
834
:meth:`coeffs`.
835
836
EXAMPLES::
837
838
sage: R.<x,y> = QQ[]
839
sage: quadratic = invariant_theory.quadratic_form(x^2+y^2)
840
sage: quadratic.monomials()
841
(x^2, y^2, x*y)
842
843
sage: quadratic = invariant_theory.inhomogeneous_quadratic_form(x^2+y^2)
844
sage: quadratic.monomials()
845
(x^2, y^2, 1, x*y, x, y)
846
"""
847
var = self._variables
848
def prod(a,b):
849
if a is None and b is None:
850
return self._ring.one()
851
elif a is None:
852
return b
853
elif b is None:
854
return a
855
else:
856
return a*b
857
squares = tuple( prod(x,x) for x in var )
858
mixed = []
859
for i in range(self._n):
860
for j in range(i+1, self._n):
861
mixed.append(prod(var[i], var[j]))
862
mixed = tuple(mixed)
863
return squares + mixed
864
865
866
@cached_method
867
def coeffs(self):
868
r"""
869
The coefficients of a quadratic form.
870
871
Given
872
873
.. math::
874
875
f(x) = \sum_{0\leq i<n} a_i x_i^2 + \sum_{0\leq j <k<n}
876
a_{jk} x_j x_k
877
878
this function returns `a = (a_0, \dots, a_n, a_{00}, a_{01}, \dots, a_{n-1,n})`
879
880
EXAMPLES::
881
882
sage: R.<a,b,c,d,e,f,g, x,y,z> = QQ[]
883
sage: p = a*x^2 + b*y^2 + c*z^2 + d*x*y + e*x*z + f*y*z
884
sage: inv = invariant_theory.quadratic_form(p, x,y,z); inv
885
Ternary quadratic with coefficients (a, b, c, d, e, f)
886
sage: inv.coeffs()
887
(a, b, c, d, e, f)
888
sage: inv.scaled_coeffs()
889
(a, b, c, 1/2*d, 1/2*e, 1/2*f)
890
"""
891
return self._extract_coefficients(self.monomials())
892
893
894
def scaled_coeffs(self):
895
"""
896
The scaled coefficients of a quadratic form.
897
898
Given
899
900
.. math::
901
902
f(x) = \sum_{0\leq i<n} a_i x_i^2 + \sum_{0\leq j <k<n}
903
2 a_{jk} x_j x_k
904
905
this function returns `a = (a_0, \cdots, a_n, a_{00}, a_{01}, \dots, a_{n-1,n})`
906
907
EXAMPLES::
908
909
sage: R.<a,b,c,d,e,f,g, x,y,z> = QQ[]
910
sage: p = a*x^2 + b*y^2 + c*z^2 + d*x*y + e*x*z + f*y*z
911
sage: inv = invariant_theory.quadratic_form(p, x,y,z); inv
912
Ternary quadratic with coefficients (a, b, c, d, e, f)
913
sage: inv.coeffs()
914
(a, b, c, d, e, f)
915
sage: inv.scaled_coeffs()
916
(a, b, c, 1/2*d, 1/2*e, 1/2*f)
917
"""
918
coeff = self.coeffs()
919
squares = coeff[0:self._n]
920
mixed = tuple( c/2 for c in coeff[self._n:] )
921
return squares + mixed
922
923
924
@cached_method
925
def matrix(self):
926
"""
927
Return the quadratic form as a symmetric matrix
928
929
OUTPUT:
930
931
This method returns a symmetric matrix `A` such that the
932
quadratic `Q` equals
933
934
.. math::
935
936
Q(x,y,z,\dots) = (x,y,\dots) A (x,y,\dots)^t
937
938
EXAMPLES::
939
940
sage: R.<x,y,z> = QQ[]
941
sage: quadratic = invariant_theory.ternary_quadratic(x^2+y^2+z^2+x*y)
942
sage: matrix(quadratic)
943
[ 1 1/2 0]
944
[1/2 1 0]
945
[ 0 0 1]
946
sage: quadratic._matrix_() == matrix(quadratic)
947
True
948
"""
949
coeff = self.scaled_coeffs()
950
A = matrix(self._ring, self._n)
951
for i in range(self._n):
952
A[i,i] = coeff[i]
953
ij = self._n
954
for i in range(self._n):
955
for j in range(i+1, self._n):
956
A[i,j] = coeff[ij]
957
A[j,i] = coeff[ij]
958
ij += 1
959
return A
960
961
_matrix_ = matrix
962
963
964
def discriminant(self):
965
"""
966
Return the discriminant of the quadratic form.
967
968
Up to an overall constant factor, this is just the determinant
969
of the defining matrix, see :meth:`matrix`. For a quadratic
970
form in `n` variables, the overall constant is `2^{n-1}` if
971
`n` is odd and `(-1)^{n/2} 2^n` if `n` is even.
972
973
EXAMPLES::
974
975
sage: R.<a,b,c, x,y> = QQ[]
976
sage: p = a*x^2+b*x*y+c*y^2
977
sage: quadratic = invariant_theory.quadratic_form(p, x,y)
978
sage: quadratic.discriminant()
979
b^2 - 4*a*c
980
981
sage: R.<a,b,c,d,e,f,g, x,y,z> = QQ[]
982
sage: p = a*x^2 + b*y^2 + c*z^2 + d*x*y + e*x*z + f*y*z
983
sage: quadratic = invariant_theory.quadratic_form(p, x,y,z)
984
sage: quadratic.discriminant()
985
4*a*b*c - c*d^2 - b*e^2 + d*e*f - a*f^2
986
"""
987
A = 2*self._matrix_()
988
if is_odd(self._n):
989
return A.det() / 2
990
else:
991
return (-1)**(self._n/2) * A.det()
992
993
994
@cached_method
995
def dual(self):
996
"""
997
Return the dual quadratic form.
998
999
OUTPUT:
1000
1001
A new quadratic form (with the same number of variables)
1002
defined by the adjoint matrix.
1003
1004
EXAMPLES::
1005
1006
sage: R.<a,b,c,x,y,z> = QQ[]
1007
sage: cubic = x^2+y^2+z^2
1008
sage: quadratic = invariant_theory.ternary_quadratic(a*x^2+b*y^2+c*z^2, [x,y,z])
1009
sage: quadratic.form()
1010
a*x^2 + b*y^2 + c*z^2
1011
sage: quadratic.dual().form()
1012
b*c*x^2 + a*c*y^2 + a*b*z^2
1013
1014
sage: R.<x,y,z, t> = QQ[]
1015
sage: cubic = x^2+y^2+z^2
1016
sage: quadratic = invariant_theory.ternary_quadratic(x^2+y^2+z^2 + t*x*y, [x,y,z])
1017
sage: quadratic.dual()
1018
Ternary quadratic with coefficients (1, 1, -1/4*t^2 + 1, -t, 0, 0)
1019
1020
sage: R.<x,y, t> = QQ[]
1021
sage: quadratic = invariant_theory.ternary_quadratic(x^2+y^2+1 + t*x*y, [x,y])
1022
sage: quadratic.dual()
1023
Ternary quadratic with coefficients (1, 1, -1/4*t^2 + 1, -t, 0, 0)
1024
1025
TESTS::
1026
1027
sage: R = PolynomialRing(QQ, 'a20,a11,a02,a10,a01,a00,x,y,z', order='lex')
1028
sage: R.inject_variables()
1029
Defining a20, a11, a02, a10, a01, a00, x, y, z
1030
sage: p = ( a20*x^2 + a11*x*y + a02*y^2 +
1031
... a10*x*z + a01*y*z + a00*z^2 )
1032
sage: quadratic = invariant_theory.ternary_quadratic(p, x,y,z)
1033
sage: quadratic.dual().dual().form().factor()
1034
(1/4) *
1035
(a20*x^2 + a11*x*y + a02*y^2 + a10*x*z + a01*y*z + a00*z^2) *
1036
(4*a20*a02*a00 - a20*a01^2 - a11^2*a00 + a11*a10*a01 - a02*a10^2)
1037
1038
sage: R.<w,x,y,z> = QQ[]
1039
sage: q = invariant_theory.quaternary_quadratic(w^2+2*x^2+3*y^2+4*z^2+x*y+5*w*z)
1040
sage: q.form()
1041
w^2 + 2*x^2 + x*y + 3*y^2 + 5*w*z + 4*z^2
1042
sage: q.dual().dual().form().factor()
1043
(42849/256) * (w^2 + 2*x^2 + x*y + 3*y^2 + 5*w*z + 4*z^2)
1044
1045
sage: R.<x,y,z> = QQ[]
1046
sage: q = invariant_theory.quaternary_quadratic(1+2*x^2+3*y^2+4*z^2+x*y+5*z)
1047
sage: q.form()
1048
2*x^2 + x*y + 3*y^2 + 4*z^2 + 5*z + 1
1049
sage: q.dual().dual().form().factor()
1050
(42849/256) * (2*x^2 + x*y + 3*y^2 + 4*z^2 + 5*z + 1)
1051
"""
1052
A = self.matrix()
1053
Aadj = A.adjoint()
1054
if self._homogeneous:
1055
var = self._variables
1056
else:
1057
var = self._variables[0:-1] + (1, )
1058
n = self._n
1059
p = sum([ sum([ Aadj[i,j]*var[i]*var[j] for i in range(n) ]) for j in range(n)])
1060
return invariant_theory.quadratic_form(p, self.variables())
1061
1062
1063
def as_QuadraticForm(self):
1064
"""
1065
Convert into a :class:`~sage.quadratic_forms.quadratic_form.QuadraticForm`.
1066
1067
OUTPUT:
1068
1069
Sage has a special quadratic forms subsystem. This method
1070
converts ``self`` into this
1071
:class:`~sage.quadratic_forms.quadratic_form.QuadraticForm`
1072
representation.
1073
1074
EXAMPLES::
1075
1076
sage: R.<x,y,z> = QQ[]
1077
sage: p = x^2+y^2+z^2+2*x*y+3*x*z
1078
sage: quadratic = invariant_theory.ternary_quadratic(p)
1079
sage: matrix(quadratic)
1080
[ 1 1 3/2]
1081
[ 1 1 0]
1082
[3/2 0 1]
1083
sage: quadratic.as_QuadraticForm()
1084
Quadratic form in 3 variables over Multivariate Polynomial
1085
Ring in x, y, z over Rational Field with coefficients:
1086
[ 1/2 1 3/2 ]
1087
[ * 1/2 0 ]
1088
[ * * 1/2 ]
1089
sage: _.polynomial('X,Y,Z')
1090
X^2 + 2*X*Y + Y^2 + 3*X*Z + Z^2
1091
"""
1092
R = self._ring
1093
B = self._matrix_()
1094
import sage.quadratic_forms.quadratic_form
1095
return sage.quadratic_forms.quadratic_form.QuadraticForm(R, B)
1096
1097
1098
######################################################################
1099
1100
class BinaryQuartic(AlgebraicForm):
1101
"""
1102
Invariant theory of a binary quartic.
1103
1104
You should use the :class:`invariant_theory
1105
<InvariantTheoryFactory>` factory object to construct instances
1106
of this class. See :meth:`~InvariantTheoryFactory.binary_quartic`
1107
for details.
1108
1109
TESTS::
1110
1111
sage: R.<a0, a1, a2, a3, a4, x0, x1> = QQ[]
1112
sage: p = a0*x1^4 + a1*x1^3*x0 + a2*x1^2*x0^2 + a3*x1*x0^3 + a4*x0^4
1113
sage: quartic = invariant_theory.binary_quartic(p, x0, x1)
1114
sage: quartic._check_covariant('form')
1115
sage: quartic._check_covariant('EisensteinD', invariant=True)
1116
sage: quartic._check_covariant('EisensteinE', invariant=True)
1117
sage: quartic._check_covariant('g_covariant')
1118
sage: quartic._check_covariant('h_covariant')
1119
sage: TestSuite(quartic).run()
1120
"""
1121
1122
def __init__(self, n, d, polynomial, *args):
1123
"""
1124
The Python constructor.
1125
1126
TESTS::
1127
1128
sage: R.<x,y> = QQ[]
1129
sage: from sage.rings.invariant_theory import BinaryQuartic
1130
sage: BinaryQuartic(2, 4, x^4+y^4)
1131
Binary quartic with coefficients (1, 0, 0, 0, 1)
1132
"""
1133
assert n == 2 and d == 4
1134
super(BinaryQuartic, self).__init__(2, 4, polynomial, *args)
1135
self._x = self._variables[0]
1136
self._y = self._variables[1]
1137
1138
1139
@cached_method
1140
def monomials(self):
1141
"""
1142
List the basis monomials in the form.
1143
1144
OUTPUT:
1145
1146
A tuple of monomials. They are in the same order as
1147
:meth:`coeffs`.
1148
1149
EXAMPLES::
1150
1151
sage: R.<x,y> = QQ[]
1152
sage: quartic = invariant_theory.binary_quartic(x^4+y^4)
1153
sage: quartic.monomials()
1154
(y^4, x*y^3, x^2*y^2, x^3*y, x^4)
1155
"""
1156
quartic = self._polynomial
1157
x0 = self._x
1158
x1 = self._y
1159
if self._homogeneous:
1160
return (x1**4, x1**3*x0, x1**2*x0**2, x1*x0**3, x0**4)
1161
else:
1162
return (self._ring.one(), x0, x0**2, x0**3, x0**4)
1163
1164
1165
@cached_method
1166
def coeffs(self):
1167
"""
1168
The coefficients of a binary quartic.
1169
1170
Given
1171
1172
.. math::
1173
1174
f(x) = a_0 x_1^4 + a_1 x_0 x_1^3 + a_2 x_0^2 x_1^2 +
1175
a_3 x_0^3 x_1 + a_4 x_0^4
1176
1177
this function returns `a = (a_0, a_1, a_2, a_3, a_4)`
1178
1179
EXAMPLES::
1180
1181
sage: R.<a0, a1, a2, a3, a4, x0, x1> = QQ[]
1182
sage: p = a0*x1^4 + a1*x1^3*x0 + a2*x1^2*x0^2 + a3*x1*x0^3 + a4*x0^4
1183
sage: quartic = invariant_theory.binary_quartic(p, x0, x1)
1184
sage: quartic.coeffs()
1185
(a0, a1, a2, a3, a4)
1186
1187
sage: R.<a0, a1, a2, a3, a4, x> = QQ[]
1188
sage: p = a0 + a1*x + a2*x^2 + a3*x^3 + a4*x^4
1189
sage: quartic = invariant_theory.binary_quartic(p, x)
1190
sage: quartic.coeffs()
1191
(a0, a1, a2, a3, a4)
1192
"""
1193
return self._extract_coefficients(self.monomials())
1194
1195
1196
def scaled_coeffs(self):
1197
"""
1198
The coefficients of a binary quartic.
1199
1200
Given
1201
1202
.. math::
1203
1204
f(x) = a_0 x_1^4 + 4 a_1 x_0 x_1^3 + 6 a_2 x_0^2 x_1^2 +
1205
4 a_3 x_0^3 x_1 + a_4 x_0^4
1206
1207
this function returns `a = (a_0, a_1, a_2, a_3, a_4)`
1208
1209
EXAMPLES::
1210
1211
sage: R.<a0, a1, a2, a3, a4, x0, x1> = QQ[]
1212
sage: quartic = a0*x1^4 + 4*a1*x1^3*x0 + 6*a2*x1^2*x0^2 + 4*a3*x1*x0^3 + a4*x0^4
1213
sage: inv = invariant_theory.binary_quartic(quartic, x0, x1)
1214
sage: inv.scaled_coeffs()
1215
(a0, a1, a2, a3, a4)
1216
1217
sage: R.<a0, a1, a2, a3, a4, x> = QQ[]
1218
sage: quartic = a0 + 4*a1*x + 6*a2*x^2 + 4*a3*x^3 + a4*x^4
1219
sage: inv = invariant_theory.binary_quartic(quartic, x)
1220
sage: inv.scaled_coeffs()
1221
(a0, a1, a2, a3, a4)
1222
"""
1223
coeff = self.coeffs()
1224
return (coeff[0], coeff[1]/4, coeff[2]/6, coeff[3]/4, coeff[4])
1225
1226
1227
@cached_method
1228
def EisensteinD(self):
1229
r"""
1230
One of the Eisenstein invariants of a binary quartic.
1231
1232
OUTPUT:
1233
1234
The Eisenstein D-invariant of the quartic.
1235
1236
.. math::
1237
1238
f(x) = a_0 x_1^4 + 4 a_1 x_0 x_1^3 + 6 a_2 x_0^2 x_1^2 +
1239
4 a_3 x_0^3 x_1 + a_4 x_0^4
1240
\\
1241
\Rightarrow
1242
D(f) = a_0 a_4+3 a_2^2-4 a_1 a_3
1243
1244
EXAMPLES::
1245
1246
sage: R.<a0, a1, a2, a3, a4, x0, x1> = QQ[]
1247
sage: f = a0*x1^4+4*a1*x0*x1^3+6*a2*x0^2*x1^2+4*a3*x0^3*x1+a4*x0^4
1248
sage: inv = invariant_theory.binary_quartic(f, x0, x1)
1249
sage: inv.EisensteinD()
1250
3*a2^2 - 4*a1*a3 + a0*a4
1251
"""
1252
a = self.scaled_coeffs()
1253
assert len(a) == 5
1254
return a[0]*a[4]+3*a[2]**2-4*a[1]*a[3]
1255
1256
1257
@cached_method
1258
def EisensteinE(self):
1259
r"""
1260
One of the Eisenstein invariants of a binary quartic.
1261
1262
OUTPUT:
1263
1264
The Eisenstein E-invariant of the quartic.
1265
1266
.. math::
1267
1268
f(x) = a_0 x_1^4 + 4 a_1 x_0 x_1^3 + 6 a_2 x_0^2 x_1^2 +
1269
4 a_3 x_0^3 x_1 + a_4 x_0^4
1270
\\ \Rightarrow
1271
E(f) = a_0 a_3^2 +a_1^2 a_4 -a_0 a_2 a_4
1272
-2 a_1 a_2 a_3 + a_2^3
1273
1274
EXAMPLES::
1275
1276
sage: R.<a0, a1, a2, a3, a4, x0, x1> = QQ[]
1277
sage: f = a0*x1^4+4*a1*x0*x1^3+6*a2*x0^2*x1^2+4*a3*x0^3*x1+a4*x0^4
1278
sage: inv = invariant_theory.binary_quartic(f, x0, x1)
1279
sage: inv.EisensteinE()
1280
a2^3 - 2*a1*a2*a3 + a0*a3^2 + a1^2*a4 - a0*a2*a4
1281
"""
1282
a = self.scaled_coeffs()
1283
assert len(a) == 5
1284
return a[0]*a[3]**2 +a[1]**2*a[4] -a[0]*a[2]*a[4] -2*a[1]*a[2]*a[3] +a[2]**3
1285
1286
1287
@cached_method
1288
def g_covariant(self):
1289
r"""
1290
The g-covariant of a binary quartic.
1291
1292
OUTPUT:
1293
1294
The g-covariant of the quartic.
1295
1296
.. math::
1297
1298
f(x) = a_0 x_1^4 + 4 a_1 x_0 x_1^3 + 6 a_2 x_0^2 x_1^2 +
1299
4 a_3 x_0^3 x_1 + a_4 x_0^4
1300
\\
1301
\Rightarrow
1302
D(f) = \frac{1}{144}
1303
\begin{pmatrix}
1304
\frac{\partial^2 f}{\partial x \partial x}
1305
\end{pmatrix}
1306
1307
EXAMPLES::
1308
1309
sage: R.<a0, a1, a2, a3, a4, x, y> = QQ[]
1310
sage: p = a0*x^4+4*a1*x^3*y+6*a2*x^2*y^2+4*a3*x*y^3+a4*y^4
1311
sage: inv = invariant_theory.binary_quartic(p, x, y)
1312
sage: g = inv.g_covariant(); g
1313
a1^2*x^4 - a0*a2*x^4 + 2*a1*a2*x^3*y - 2*a0*a3*x^3*y + 3*a2^2*x^2*y^2
1314
- 2*a1*a3*x^2*y^2 - a0*a4*x^2*y^2 + 2*a2*a3*x*y^3
1315
- 2*a1*a4*x*y^3 + a3^2*y^4 - a2*a4*y^4
1316
1317
sage: inv_inhomogeneous = invariant_theory.binary_quartic(p.subs(y=1), x)
1318
sage: inv_inhomogeneous.g_covariant()
1319
a1^2*x^4 - a0*a2*x^4 + 2*a1*a2*x^3 - 2*a0*a3*x^3 + 3*a2^2*x^2
1320
- 2*a1*a3*x^2 - a0*a4*x^2 + 2*a2*a3*x - 2*a1*a4*x + a3^2 - a2*a4
1321
1322
sage: g == 1/144 * (p.derivative(x,y)^2 - p.derivative(x,x)*p.derivative(y,y))
1323
True
1324
"""
1325
a4, a3, a2, a1, a0 = self.scaled_coeffs()
1326
x0 = self._x
1327
x1 = self._y
1328
if self._homogeneous:
1329
xpow = [x0**4, x0**3 * x1, x0**2 * x1**2, x0 * x1**3, x1**4]
1330
else:
1331
xpow = [x0**4, x0**3, x0**2, x0, self._ring.one()]
1332
return (a1**2 - a0*a2)*xpow[0] + \
1333
(2*a1*a2 - 2*a0*a3)*xpow[1] + \
1334
(3*a2**2 - 2*a1*a3 - a0*a4)*xpow[2] + \
1335
(2*a2*a3 - 2*a1*a4)*xpow[3] + \
1336
(a3**2 - a2*a4)*xpow[4]
1337
1338
1339
@cached_method
1340
def h_covariant(self):
1341
r"""
1342
The h-covariant of a binary quartic.
1343
1344
OUTPUT:
1345
1346
The h-covariant of the quartic.
1347
1348
.. math::
1349
1350
f(x) = a_0 x_1^4 + 4 a_1 x_0 x_1^3 + 6 a_2 x_0^2 x_1^2 +
1351
4 a_3 x_0^3 x_1 + a_4 x_0^4
1352
\\
1353
\Rightarrow
1354
D(f) = \frac{1}{144}
1355
\begin{pmatrix}
1356
\frac{\partial^2 f}{\partial x \partial x}
1357
\end{pmatrix}
1358
1359
EXAMPLES::
1360
1361
sage: R.<a0, a1, a2, a3, a4, x, y> = QQ[]
1362
sage: p = a0*x^4+4*a1*x^3*y+6*a2*x^2*y^2+4*a3*x*y^3+a4*y^4
1363
sage: inv = invariant_theory.binary_quartic(p, x, y)
1364
sage: h = inv.h_covariant(); h
1365
-2*a1^3*x^6 + 3*a0*a1*a2*x^6 - a0^2*a3*x^6 - 6*a1^2*a2*x^5*y + 9*a0*a2^2*x^5*y
1366
- 2*a0*a1*a3*x^5*y - a0^2*a4*x^5*y - 10*a1^2*a3*x^4*y^2 + 15*a0*a2*a3*x^4*y^2
1367
- 5*a0*a1*a4*x^4*y^2 + 10*a0*a3^2*x^3*y^3 - 10*a1^2*a4*x^3*y^3
1368
+ 10*a1*a3^2*x^2*y^4 - 15*a1*a2*a4*x^2*y^4 + 5*a0*a3*a4*x^2*y^4
1369
+ 6*a2*a3^2*x*y^5 - 9*a2^2*a4*x*y^5 + 2*a1*a3*a4*x*y^5 + a0*a4^2*x*y^5
1370
+ 2*a3^3*y^6 - 3*a2*a3*a4*y^6 + a1*a4^2*y^6
1371
1372
sage: inv_inhomogeneous = invariant_theory.binary_quartic(p.subs(y=1), x)
1373
sage: inv_inhomogeneous.h_covariant()
1374
-2*a1^3*x^6 + 3*a0*a1*a2*x^6 - a0^2*a3*x^6 - 6*a1^2*a2*x^5 + 9*a0*a2^2*x^5
1375
- 2*a0*a1*a3*x^5 - a0^2*a4*x^5 - 10*a1^2*a3*x^4 + 15*a0*a2*a3*x^4
1376
- 5*a0*a1*a4*x^4 + 10*a0*a3^2*x^3 - 10*a1^2*a4*x^3 + 10*a1*a3^2*x^2
1377
- 15*a1*a2*a4*x^2 + 5*a0*a3*a4*x^2 + 6*a2*a3^2*x - 9*a2^2*a4*x
1378
+ 2*a1*a3*a4*x + a0*a4^2*x + 2*a3^3 - 3*a2*a3*a4 + a1*a4^2
1379
1380
sage: g = inv.g_covariant()
1381
sage: h == 1/8 * (p.derivative(x)*g.derivative(y)-p.derivative(y)*g.derivative(x))
1382
True
1383
"""
1384
a0, a1, a2, a3, a4 = self.scaled_coeffs()
1385
x0 = self._x
1386
x1 = self._y
1387
if self._homogeneous:
1388
xpow = [x0**6, x0**5 * x1, x0**4 * x1**2, x0**3 * x1**3,
1389
x0**2 * x1**4, x0 * x1**5, x1**6]
1390
else:
1391
xpow = [x0**6, x0**5, x0**4, x0**3, x0**2, x0, x0.parent().one()]
1392
return (-2*a3**3 + 3*a2*a3*a4 - a1*a4**2) * xpow[0] + \
1393
(-6*a2*a3**2 + 9*a2**2*a4 - 2*a1*a3*a4 - a0*a4**2) * xpow[1] + \
1394
5 * (-2*a1*a3**2 + 3*a1*a2*a4 - a0*a3*a4) * xpow[2] + \
1395
10 * (-a0*a3**2 + a1**2*a4) * xpow[3] + \
1396
5 * (2*a1**2*a3 - 3*a0*a2*a3 + a0*a1*a4) * xpow[4] + \
1397
(6*a1**2*a2 - 9*a0*a2**2 + 2*a0*a1*a3 + a0**2*a4) * xpow[5] + \
1398
(2*a1**3 - 3*a0*a1*a2 + a0**2*a3) * xpow[6]
1399
1400
1401
######################################################################
1402
def _covariant_conic(A_scaled_coeffs, B_scaled_coeffs, monomials):
1403
"""
1404
Helper function for :meth:`TernaryQuadratic.covariant_conic`
1405
1406
INPUT:
1407
1408
- ``A_scaled_coeffs``, ``B_scaled_coeffs`` -- The scaled
1409
coefficients of the two ternary quadratics.
1410
1411
- ``monomials`` -- The monomials :meth:`~TernaryQuadratic.monomials`.
1412
1413
OUTPUT:
1414
1415
The so-called covariant conic, a ternary quadratic. It is
1416
symmetric under exchange of ``A`` and ``B``.
1417
1418
EXAMPLES::
1419
1420
sage: ring.<x,y,z> = QQ[]
1421
sage: A = invariant_theory.ternary_quadratic(x^2+y^2+z^2)
1422
sage: B = invariant_theory.ternary_quadratic(x*y+x*z+y*z)
1423
sage: from sage.rings.invariant_theory import _covariant_conic
1424
sage: _covariant_conic(A.scaled_coeffs(), B.scaled_coeffs(), A.monomials())
1425
-x*y - x*z - y*z
1426
"""
1427
a0, b0, c0, h0, g0, f0 = A_scaled_coeffs
1428
a1, b1, c1, h1, g1, f1 = B_scaled_coeffs
1429
return (
1430
(b0*c1+c0*b1-2*f0*f1) * monomials[0] +
1431
(a0*c1+c0*a1-2*g0*g1) * monomials[1] +
1432
(a0*b1+b0*a1-2*h0*h1) * monomials[2] +
1433
2*(f0*g1+g0*f1 -c0*h1-h0*c1) * monomials[3] +
1434
2*(h0*f1+f0*h1 -b0*g1-g0*b1) * monomials[4] +
1435
2*(g0*h1+h0*g1 -a0*f1-f0*a1) * monomials[5] )
1436
1437
1438
######################################################################
1439
class TernaryQuadratic(QuadraticForm):
1440
"""
1441
Invariant theory of a ternary quadratic.
1442
1443
You should use the :class:`invariant_theory
1444
<InvariantTheoryFactory>` factory object to construct instances
1445
of this class. See
1446
:meth:`~InvariantTheoryFactory.ternary_quadratic` for details.
1447
1448
TESTS::
1449
1450
sage: R.<x,y,z> = QQ[]
1451
sage: quadratic = invariant_theory.ternary_quadratic(x^2+y^2+z^2)
1452
sage: quadratic
1453
Ternary quadratic with coefficients (1, 1, 1, 0, 0, 0)
1454
sage: TestSuite(quadratic).run()
1455
"""
1456
1457
def __init__(self, n, d, polynomial, *args):
1458
"""
1459
The Python constructor.
1460
1461
INPUT:
1462
1463
See :meth:`~InvariantTheoryFactory.ternary_quadratic`.
1464
1465
TESTS::
1466
1467
sage: R.<x,y,z> = QQ[]
1468
sage: from sage.rings.invariant_theory import TernaryQuadratic
1469
sage: TernaryQuadratic(3, 2, x^2+y^2+z^2)
1470
Ternary quadratic with coefficients (1, 1, 1, 0, 0, 0)
1471
"""
1472
assert n == 3 and d == 2
1473
super(QuadraticForm, self).__init__(3, 2, polynomial, *args)
1474
self._x = self._variables[0]
1475
self._y = self._variables[1]
1476
self._z = self._variables[2]
1477
1478
1479
@cached_method
1480
def monomials(self):
1481
"""
1482
List the basis monomials of the form.
1483
1484
OUTPUT:
1485
1486
A tuple of monomials. They are in the same order as
1487
:meth:`coeffs`.
1488
1489
EXAMPLES::
1490
1491
1492
sage: R.<x,y,z> = QQ[]
1493
sage: quadratic = invariant_theory.ternary_quadratic(x^2+y*z)
1494
sage: quadratic.monomials()
1495
(x^2, y^2, z^2, x*y, x*z, y*z)
1496
"""
1497
R = self._ring
1498
x,y,z = self._x, self._y, self._z
1499
if self._homogeneous:
1500
return (x**2, y**2, z**2, x*y, x*z, y*z)
1501
else:
1502
return (x**2, y**2, R.one(), x*y, x, y)
1503
1504
1505
@cached_method
1506
def coeffs(self):
1507
"""
1508
Return the coefficients of a quadratic.
1509
1510
Given
1511
1512
.. math::
1513
1514
p(x,y) =&\;
1515
a_{20} x^{2} + a_{11} x y + a_{02} y^{2} +
1516
a_{10} x + a_{01} y + a_{00}
1517
1518
this function returns
1519
`a = (a_{20}, a_{02}, a_{00}, a_{11}, a_{10}, a_{01} )`
1520
1521
EXAMPLES::
1522
1523
sage: R.<x,y,z,a20,a11,a02,a10,a01,a00> = QQ[]
1524
sage: p = ( a20*x^2 + a11*x*y + a02*y^2 +
1525
... a10*x*z + a01*y*z + a00*z^2 )
1526
sage: invariant_theory.ternary_quadratic(p, x,y,z).coeffs()
1527
(a20, a02, a00, a11, a10, a01)
1528
sage: invariant_theory.ternary_quadratic(p.subs(z=1), x, y).coeffs()
1529
(a20, a02, a00, a11, a10, a01)
1530
"""
1531
return self._extract_coefficients(self.monomials())
1532
1533
1534
def scaled_coeffs(self):
1535
"""
1536
Return the scaled coefficients of a quadratic.
1537
1538
Given
1539
1540
.. math::
1541
1542
p(x,y) =&\;
1543
a_{20} x^{2} + a_{11} x y + a_{02} y^{2} +
1544
a_{10} x + a_{01} y + a_{00}
1545
1546
this function returns
1547
`a = (a_{20}, a_{02}, a_{00}, a_{11}/2, a_{10}/2, a_{01}/2, )`
1548
1549
EXAMPLES::
1550
1551
sage: R.<x,y,z,a20,a11,a02,a10,a01,a00> = QQ[]
1552
sage: p = ( a20*x^2 + a11*x*y + a02*y^2 +
1553
... a10*x*z + a01*y*z + a00*z^2 )
1554
sage: invariant_theory.ternary_quadratic(p, x,y,z).scaled_coeffs()
1555
(a20, a02, a00, 1/2*a11, 1/2*a10, 1/2*a01)
1556
sage: invariant_theory.ternary_quadratic(p.subs(z=1), x, y).scaled_coeffs()
1557
(a20, a02, a00, 1/2*a11, 1/2*a10, 1/2*a01)
1558
"""
1559
F = self._ring.base_ring()
1560
a200, a020, a002, a110, a101, a011 = self.coeffs()
1561
return (a200, a020, a002, a110/F(2), a101/F(2), a011/F(2))
1562
1563
def covariant_conic(self, other):
1564
"""
1565
Return the ternary quadratic covariant to ``self`` and ``other``.
1566
1567
INPUT:
1568
1569
- ``other`` -- Another ternary quadratic.
1570
1571
OUTPUT:
1572
1573
The so-called covariant conic, a ternary quadratic. It is
1574
symmetric under exchange of ``self`` and ``other``.
1575
1576
EXAMPLES::
1577
1578
sage: ring.<x,y,z> = QQ[]
1579
sage: Q = invariant_theory.ternary_quadratic(x^2+y^2+z^2)
1580
sage: R = invariant_theory.ternary_quadratic(x*y+x*z+y*z)
1581
sage: Q.covariant_conic(R)
1582
-x*y - x*z - y*z
1583
sage: R.covariant_conic(Q)
1584
-x*y - x*z - y*z
1585
1586
TESTS::
1587
1588
sage: R.<a,a_,b,b_,c,c_,f,f_,g,g_,h,h_,x,y,z> = QQ[]
1589
sage: p = ( a*x^2 + 2*h*x*y + b*y^2 +
1590
... 2*g*x*z + 2*f*y*z + c*z^2 )
1591
sage: Q = invariant_theory.ternary_quadratic(p, [x,y,z])
1592
sage: Q.matrix()
1593
[a h g]
1594
[h b f]
1595
[g f c]
1596
sage: p = ( a_*x^2 + 2*h_*x*y + b_*y^2 +
1597
... 2*g_*x*z + 2*f_*y*z + c_*z^2 )
1598
sage: Q_ = invariant_theory.ternary_quadratic(p, [x,y,z])
1599
sage: Q_.matrix()
1600
[a_ h_ g_]
1601
[h_ b_ f_]
1602
[g_ f_ c_]
1603
sage: QQ_ = Q.covariant_conic(Q_)
1604
sage: invariant_theory.ternary_quadratic(QQ_, [x,y,z]).matrix()
1605
[ b_*c + b*c_ - 2*f*f_ f_*g + f*g_ - c_*h - c*h_ -b_*g - b*g_ + f_*h + f*h_]
1606
[ f_*g + f*g_ - c_*h - c*h_ a_*c + a*c_ - 2*g*g_ -a_*f - a*f_ + g_*h + g*h_]
1607
[-b_*g - b*g_ + f_*h + f*h_ -a_*f - a*f_ + g_*h + g*h_ a_*b + a*b_ - 2*h*h_]
1608
"""
1609
return _covariant_conic(self.scaled_coeffs(), other.scaled_coeffs(),
1610
self.monomials())
1611
1612
1613
######################################################################
1614
1615
class TernaryCubic(AlgebraicForm):
1616
"""
1617
Invariant theory of a ternary cubic.
1618
1619
You should use the :class:`invariant_theory
1620
<InvariantTheoryFactory>` factory object to contstruct instances
1621
of this class. See :meth:`~InvariantTheoryFactory.ternary_cubic`
1622
for details.
1623
1624
TESTS::
1625
1626
sage: R.<x,y,z> = QQ[]
1627
sage: cubic = invariant_theory.ternary_cubic(x^3+y^3+z^3)
1628
sage: cubic
1629
Ternary cubic with coefficients (1, 1, 1, 0, 0, 0, 0, 0, 0, 0)
1630
sage: TestSuite(cubic).run()
1631
"""
1632
1633
def __init__(self, n, d, polynomial, *args):
1634
"""
1635
The Python constructor.
1636
1637
TESTS::
1638
1639
sage: R.<x,y,z> = QQ[]
1640
sage: p = 2837*x^3 + 1363*x^2*y + 6709*x^2*z + \
1641
... 5147*x*y^2 + 2769*x*y*z + 912*x*z^2 + 4976*y^3 + \
1642
... 2017*y^2*z + 4589*y*z^2 + 9681*z^3
1643
sage: cubic = invariant_theory.ternary_cubic(p)
1644
sage: cubic._check_covariant('S_invariant', invariant=True)
1645
sage: cubic._check_covariant('T_invariant', invariant=True)
1646
sage: cubic._check_covariant('form')
1647
sage: cubic._check_covariant('Hessian')
1648
sage: cubic._check_covariant('Theta_covariant')
1649
sage: cubic._check_covariant('J_covariant')
1650
"""
1651
assert n == d == 3
1652
super(TernaryCubic, self).__init__(3, 3, polynomial, *args)
1653
self._x = self._variables[0]
1654
self._y = self._variables[1]
1655
self._z = self._variables[2]
1656
1657
1658
@cached_method
1659
def monomials(self):
1660
"""
1661
List the basis monomials of the form.
1662
1663
OUTPUT:
1664
1665
A tuple of monomials. They are in the same order as
1666
:meth:`coeffs`.
1667
1668
EXAMPLES::
1669
1670
sage: R.<x,y,z> = QQ[]
1671
sage: cubic = invariant_theory.ternary_cubic(x^3+y*z^2)
1672
sage: cubic.monomials()
1673
(x^3, y^3, z^3, x^2*y, x^2*z, x*y^2, y^2*z, x*z^2, y*z^2, x*y*z)
1674
"""
1675
R = self._ring
1676
x,y,z = self._x, self._y, self._z
1677
if self._homogeneous:
1678
return (x**3, y**3, z**3, x**2*y, x**2*z, x*y**2,
1679
y**2*z, x*z**2, y*z**2, x*y*z)
1680
else:
1681
return (x**3, y**3, R.one(), x**2*y, x**2, x*y**2,
1682
y**2, x, y, x*y)
1683
1684
1685
@cached_method
1686
def coeffs(self):
1687
r"""
1688
Return the coefficients of a cubic.
1689
1690
Given
1691
1692
.. math::
1693
1694
\begin{split}
1695
p(x,y) =&\;
1696
a_{30} x^{3} + a_{21} x^{2} y + a_{12} x y^{2} +
1697
a_{03} y^{3} + a_{20} x^{2} +
1698
\\ &\;
1699
a_{11} x y +
1700
a_{02} y^{2} + a_{10} x + a_{01} y + a_{00}
1701
\end{split}
1702
1703
this function returns
1704
`a = (a_{30}, a_{03}, a_{00}, a_{21}, a_{20}, a_{12}, a_{02}, a_{10}, a_{01}, a_{11})`
1705
1706
EXAMPLES::
1707
1708
sage: R.<x,y,z,a30,a21,a12,a03,a20,a11,a02,a10,a01,a00> = QQ[]
1709
sage: p = ( a30*x^3 + a21*x^2*y + a12*x*y^2 + a03*y^3 + a20*x^2*z +
1710
... a11*x*y*z + a02*y^2*z + a10*x*z^2 + a01*y*z^2 + a00*z^3 )
1711
sage: invariant_theory.ternary_cubic(p, x,y,z).coeffs()
1712
(a30, a03, a00, a21, a20, a12, a02, a10, a01, a11)
1713
sage: invariant_theory.ternary_cubic(p.subs(z=1), x, y).coeffs()
1714
(a30, a03, a00, a21, a20, a12, a02, a10, a01, a11)
1715
"""
1716
return self._extract_coefficients(self.monomials())
1717
1718
1719
def scaled_coeffs(self):
1720
r"""
1721
Return the coefficients of a cubic.
1722
1723
Compared to :meth:`coeffs`, this method returns rescaled
1724
coefficients that are often used in invariant theory.
1725
1726
Given
1727
1728
.. math::
1729
1730
\begin{split}
1731
p(x,y) =&\;
1732
a_{30} x^{3} + a_{21} x^{2} y + a_{12} x y^{2} +
1733
a_{03} y^{3} + a_{20} x^{2} +
1734
\\ &\;
1735
a_{11} x y +
1736
a_{02} y^{2} + a_{10} x + a_{01} y + a_{00}
1737
\end{split}
1738
1739
this function returns
1740
`a = (a_{30}, a_{03}, a_{00}, a_{21}/3, a_{20}/3, a_{12}/3, a_{02}/3, a_{10}/3, a_{01}/3, a_{11}/6)`
1741
1742
EXAMPLES::
1743
1744
sage: R.<x,y,z,a30,a21,a12,a03,a20,a11,a02,a10,a01,a00> = QQ[]
1745
sage: p = ( a30*x^3 + a21*x^2*y + a12*x*y^2 + a03*y^3 + a20*x^2*z +
1746
... a11*x*y*z + a02*y^2*z + a10*x*z^2 + a01*y*z^2 + a00*z^3 )
1747
sage: invariant_theory.ternary_cubic(p, x,y,z).scaled_coeffs()
1748
(a30, a03, a00, 1/3*a21, 1/3*a20, 1/3*a12, 1/3*a02, 1/3*a10, 1/3*a01, 1/6*a11)
1749
"""
1750
a = self.coeffs()
1751
F = self._ring.base_ring()
1752
return (a[0], a[1], a[2],
1753
1/F(3)*a[3], 1/F(3)*a[4], 1/F(3)*a[5],
1754
1/F(3)*a[6], 1/F(3)*a[7], 1/F(3)*a[8],
1755
1/F(6)*a[9])
1756
1757
1758
def S_invariant(self):
1759
"""
1760
Return the S-invariant.
1761
1762
EXAMPLES::
1763
1764
sage: R.<x,y,z> = QQ[]
1765
sage: cubic = invariant_theory.ternary_cubic(x^2*y+y^3+z^3+x*y*z)
1766
sage: cubic.S_invariant()
1767
-1/1296
1768
"""
1769
a,b,c,a2,a3,b1,b3,c1,c2,m = self.scaled_coeffs()
1770
S = ( a*b*c*m-(b*c*a2*a3+c*a*b1*b3+a*b*c1*c2)
1771
-m*(a*b3*c2+b*c1*a3+c*a2*b1)
1772
+(a*b1*c2**2+a*c1*b3**2+b*a2*c1**2+b*c2*a3**2+c*b3*a2**2+c*a3*b1**2)
1773
-m**4+2*m**2*(b1*c1+c2*a2+a3*b3)
1774
-3*m*(a2*b3*c1+a3*b1*c2)
1775
-(b1**2*c1**2+c2**2*a2**2+a3**2*b3**2)
1776
+(c2*a2*a3*b3+a3*b3*b1*c1+b1*c1*c2*a2) )
1777
return S
1778
1779
1780
def T_invariant(self):
1781
"""
1782
Return the T-invariant.
1783
1784
EXAMPLES::
1785
1786
sage: R.<x,y,z> = QQ[]
1787
sage: cubic = invariant_theory.ternary_cubic(x^3+y^3+z^3)
1788
sage: cubic.T_invariant()
1789
1
1790
1791
sage: R.<x,y,z,t> = GF(7)[]
1792
sage: cubic = invariant_theory.ternary_cubic(x^3+y^3+z^3+t*x*y*z, [x,y,z])
1793
sage: cubic.T_invariant()
1794
-t^6 - t^3 + 1
1795
"""
1796
a,b,c,a2,a3,b1,b3,c1,c2,m = self.scaled_coeffs()
1797
T = ( a**2*b**2*c**2-6*a*b*c*(a*b3*c2+b*c1*a3+c*a2*b1)
1798
-20*a*b*c*m**3+12*a*b*c*m*(b1*c1+c2*a2+a3*b3)
1799
+6*a*b*c*(a2*b3*c1+a3*b1*c2)+
1800
4*(a**2*b*c2**3+a**2*c*b3**3+b**2*c*a3**3+
1801
b**2*a*c1**3+c**2*a*b1**3+c**2*b*a2**3)
1802
+36*m**2*(b*c*a2*a3+c*a*b1*b3+a*b*c1*c2)
1803
-24*m*(b*c*b1*a3**2+b*c*c1*a2**2+c*a*c2*b1**2+c*a*a2*b3**2+a*b*a3*c2**2+
1804
a*b*b3*c1**2)
1805
-3*(a**2*b3**2*c2**2+b**2*c1**2*a3**2+c**2*a2**2*b1**2)+
1806
18*(b*c*b1*c1*a2*a3+c*a*c2*a2*b3*b1+a*b*a3*b3*c1*c2)
1807
-12*(b*c*c2*a3*a2**2+b*c*b3*a2*a3**2+c*a*c1*b3*b1**2+
1808
c*a*a3*b1*b3**2+a*b*a2*c1*c2**2+a*b*b1*c2*c1**2)
1809
-12*m**3*(a*b3*c2+b*c1*a3+c*a2*b1)
1810
+12*m**2*(a*b1*c2**2+a*c1*b3**2+b*a2*c1**2+
1811
b*c2*a3**2+c*b3*a2**2+c*a3*b1**2)
1812
-60*m*(a*b1*b3*c1*c2+b*c1*c2*a2*a3+c*a2*a3*b1*b3)
1813
+12*m*(a*a2*b3*c2**2+a*a3*c2*b3**2+b*b3*c1*a3**2+
1814
b*b1*a3*c1**2+c*c1*a2*b1**2+c*c2*b1*a2**2)
1815
+6*(a*b3*c2+b*c1*a3+c*a2*b1)*(a2*b3*c1+a3*b1*c2)
1816
+24*(a*b1*b3**2*c1**2+a*c1*c2**2*b1**2+b*c2*c1**2*a2**2
1817
+b*a2*a3**2*c2**2+c*a3*a2**2*b3**2+c*b3*b1**2*a3**2)
1818
-12*(a*a2*b1*c2**3+a*a3*c1*b3**3+b*b3*c2*a3**3+b*b1*a2*c1**3
1819
+c*c1*a3*b1**3+c*c2*b3*a2**3)
1820
-8*m**6+24*m**4*(b1*c1+c2*a2+a3*b3)-36*m**3*(a2*b3*c1+a3*b1*c2)
1821
-12*m**2*(b1*c1*c2*a2+c2*a2*a3*b3+a3*b3*b1*c1)
1822
-24*m**2*(b1**2*c1**2+c2**2*a2**2+a3**2*b3**2)
1823
+36*m*(a2*b3*c1+a3*b1*c2)*(b1*c1+c2*a2+a3*b3)
1824
+8*(b1**3*c1**3+c2**3*a2**3+a3**3*b3**3)
1825
-27*(a2**2*b3**2*c1**2+a3**2*b1**2*c2**2)-6*b1*c1*c2*a2*a3*b3
1826
-12*(b1**2*c1**2*c2*a2+b1**2*c1**2*a3*b3+c2**2*a2**2*a3*b3+
1827
c2**2*a2**2*b1*c1+a3**2*b3**2*b1*c1+a3**2*b3**2*c2*a2) )
1828
return T
1829
1830
1831
@cached_method
1832
def polar_conic(self):
1833
"""
1834
Return the polar conic of the cubic.
1835
1836
OUTPUT:
1837
1838
Given the ternary cubic `f(X,Y,Z)`, this method returns the
1839
symmetric matrix `A(x,y,z)` defined by
1840
1841
.. math::
1842
1843
x f_X + y f_Y + z f_Z = (X,Y,Z) \cdot A(x,y,z) \cdot (X,Y,Z)^t
1844
1845
EXAMPLES::
1846
1847
sage: R.<x,y,z,X,Y,Z,a30,a21,a12,a03,a20,a11,a02,a10,a01,a00> = QQ[]
1848
sage: p = ( a30*x^3 + a21*x^2*y + a12*x*y^2 + a03*y^3 + a20*x^2*z +
1849
... a11*x*y*z + a02*y^2*z + a10*x*z^2 + a01*y*z^2 + a00*z^3 )
1850
sage: cubic = invariant_theory.ternary_cubic(p, x,y,z)
1851
sage: cubic.polar_conic()
1852
[ 3*x*a30 + y*a21 + z*a20 x*a21 + y*a12 + 1/2*z*a11 x*a20 + 1/2*y*a11 + z*a10]
1853
[x*a21 + y*a12 + 1/2*z*a11 x*a12 + 3*y*a03 + z*a02 1/2*x*a11 + y*a02 + z*a01]
1854
[x*a20 + 1/2*y*a11 + z*a10 1/2*x*a11 + y*a02 + z*a01 x*a10 + y*a01 + 3*z*a00]
1855
1856
sage: polar_eqn = X*p.derivative(x) + Y*p.derivative(y) + Z*p.derivative(z)
1857
sage: polar = invariant_theory.ternary_quadratic(polar_eqn, [x,y,z])
1858
sage: polar.matrix().subs(X=x,Y=y,Z=z) == cubic.polar_conic()
1859
True
1860
"""
1861
a30, a03, a00, a21, a20, a12, a02, a10, a01, a11 = self.coeffs()
1862
if self._homogeneous:
1863
x,y,z = self.variables()
1864
else:
1865
x,y,z = (self._x, self._y, 1)
1866
F = self._ring.base_ring()
1867
A00 = 3*x*a30 + y*a21 + z*a20
1868
A11 = x*a12 + 3*y*a03 + z*a02
1869
A22 = x*a10 + y*a01 + 3*z*a00
1870
A01 = x*a21 + y*a12 + 1/F(2)*z*a11
1871
A02 = x*a20 + 1/F(2)*y*a11 + z*a10
1872
A12 = 1/F(2)*x*a11 + y*a02 + z*a01
1873
polar = matrix(self._ring, [[A00, A01, A02],[A01, A11, A12],[A02, A12, A22]])
1874
return polar
1875
1876
1877
@cached_method
1878
def Hessian(self):
1879
"""
1880
Return the Hessian covariant.
1881
1882
OUTPUT:
1883
1884
The Hessian matrix multiplied with the conventional
1885
normalization factor `1/216`.
1886
1887
EXAMPLES::
1888
1889
sage: R.<x,y,z> = QQ[]
1890
sage: cubic = invariant_theory.ternary_cubic(x^3+y^3+z^3)
1891
sage: cubic.Hessian()
1892
x*y*z
1893
1894
sage: R.<x,y> = QQ[]
1895
sage: cubic = invariant_theory.ternary_cubic(x^3+y^3+1)
1896
sage: cubic.Hessian()
1897
x*y
1898
"""
1899
a30, a03, a00, a21, a20, a12, a02, a10, a01, a11 = self.coeffs()
1900
if self._homogeneous:
1901
x, y, z = self.variables()
1902
else:
1903
x, y, z = self._x, self._y, 1
1904
Uxx = 6*x*a30 + 2*y*a21 + 2*z*a20
1905
Uxy = 2*x*a21 + 2*y*a12 + z*a11
1906
Uxz = 2*x*a20 + y*a11 + 2*z*a10
1907
Uyy = 2*x*a12 + 6*y*a03 + 2*z*a02
1908
Uyz = x*a11 + 2*y*a02 + 2*z*a01
1909
Uzz = 2*x*a10 + 2*y*a01 + 6*z*a00
1910
H = matrix(self._ring, [[Uxx, Uxy, Uxz],[Uxy, Uyy, Uyz],[Uxz, Uyz, Uzz]])
1911
F = self._ring.base_ring()
1912
return 1/F(216) * H.det()
1913
1914
1915
def Theta_covariant(self):
1916
"""
1917
Return the `\Theta` covariant.
1918
1919
EXAMPLES::
1920
1921
1922
sage: R.<x,y,z> = QQ[]
1923
sage: cubic = invariant_theory.ternary_cubic(x^3+y^3+z^3)
1924
sage: cubic.Theta_covariant()
1925
-x^3*y^3 - x^3*z^3 - y^3*z^3
1926
1927
sage: R.<x,y> = QQ[]
1928
sage: cubic = invariant_theory.ternary_cubic(x^3+y^3+1)
1929
sage: cubic.Theta_covariant()
1930
-x^3*y^3 - x^3 - y^3
1931
1932
sage: R.<x,y,z,a30,a21,a12,a03,a20,a11,a02,a10,a01,a00> = QQ[]
1933
sage: p = ( a30*x^3 + a21*x^2*y + a12*x*y^2 + a03*y^3 + a20*x^2*z +
1934
... a11*x*y*z + a02*y^2*z + a10*x*z^2 + a01*y*z^2 + a00*z^3 )
1935
sage: cubic = invariant_theory.ternary_cubic(p, x,y,z)
1936
sage: len(list(cubic.Theta_covariant()))
1937
6952
1938
"""
1939
U_conic = self.polar_conic().adjoint()
1940
U_coeffs = ( U_conic[0,0], U_conic[1,1], U_conic[2,2],
1941
U_conic[0,1], U_conic[0,2], U_conic[1,2] )
1942
H_conic = TernaryCubic(3, 3, self.Hessian(), self.variables()).polar_conic().adjoint()
1943
H_coeffs = ( H_conic[0,0], H_conic[1,1], H_conic[2,2],
1944
H_conic[0,1], H_conic[0,2], H_conic[1,2] )
1945
quadratic = TernaryQuadratic(3, 2, self._ring.zero(), self.variables())
1946
F = self._ring.base_ring()
1947
return 1/F(9) * _covariant_conic(U_coeffs, H_coeffs, quadratic.monomials())
1948
1949
1950
def J_covariant(self):
1951
"""
1952
Return the J-covariant of the ternary cubic.
1953
1954
EXAMPLES::
1955
1956
sage: R.<x,y,z> = QQ[]
1957
sage: cubic = invariant_theory.ternary_cubic(x^3+y^3+z^3)
1958
sage: cubic.J_covariant()
1959
x^6*y^3 - x^3*y^6 - x^6*z^3 + y^6*z^3 + x^3*z^6 - y^3*z^6
1960
1961
sage: R.<x,y> = QQ[]
1962
sage: cubic = invariant_theory.ternary_cubic(x^3+y^3+1)
1963
sage: cubic.J_covariant()
1964
x^6*y^3 - x^3*y^6 - x^6 + y^6 + x^3 - y^3
1965
"""
1966
F = self._ring.base_ring()
1967
return 1 / F(9) * self._jacobian_determinant(
1968
[self.form(), 3],
1969
[self.Hessian(), 3],
1970
[self.Theta_covariant(), 6])
1971
1972
def syzygy(self, U, S, T, H, Theta, J):
1973
"""
1974
Return the syzygy of the cubic evaluated on the invariants
1975
and covariants.
1976
1977
INPUT:
1978
1979
- ``U``, ``S``, ``T``, ``H``, ``Theta``, ``J`` --
1980
polynomials from the same polynomial ring.
1981
1982
OUTPUT:
1983
1984
0 if evaluated for the form, the S invariant, the T invariant,
1985
the Hessian, the `\Theta` covariant and the J-covariant
1986
of a ternary cubic.
1987
1988
EXAMPLES::
1989
1990
sage: R.<x,y,z> = QQ[]
1991
sage: monomials = (x^3, y^3, z^3, x^2*y, x^2*z, x*y^2,
1992
... y^2*z, x*z^2, y*z^2, x*y*z)
1993
sage: random_poly = sum([ randint(0,10000) * m for m in monomials ])
1994
sage: cubic = invariant_theory.ternary_cubic(random_poly)
1995
sage: U = cubic.form()
1996
sage: S = cubic.S_invariant()
1997
sage: T = cubic.T_invariant()
1998
sage: H = cubic.Hessian()
1999
sage: Theta = cubic.Theta_covariant()
2000
sage: J = cubic.J_covariant()
2001
sage: cubic.syzygy(U, S, T, H, Theta, J)
2002
0
2003
"""
2004
return ( -J**2 + 4*Theta**3 + T*U**2*Theta**2 +
2005
Theta*(-4*S**3*U**4 + 2*S*T*U**3*H - 72*S**2*U**2*H**2
2006
- 18*T*U*H**3 + 108*S*H**4)
2007
-16*S**4*U**5*H - 11*S**2*T*U**4*H**2 -4*T**2*U**3*H**3
2008
+54*S*T*U**2*H**4 -432*S**2*U*H**5 -27*T*H**6 )
2009
2010
2011
######################################################################
2012
2013
class SeveralAlgebraicForms(FormsBase):
2014
"""
2015
The base class of multiple algebraic forms (i.e. homogeneous polynomials).
2016
2017
You should only instantiate the derived classes of this base
2018
class.
2019
2020
See :class:`AlgebraicForm` for the base class of a single
2021
algebraic form.
2022
2023
INPUT:
2024
2025
- ``forms`` -- a list/tuple/iterable of at least one
2026
:class:`AlgebraicForm` object, all with the same number of
2027
variables. Interpreted as multiple homogeneous polynomials in a
2028
common polynomial ring.
2029
2030
EXAMPLES::
2031
2032
sage: from sage.rings.invariant_theory import AlgebraicForm, SeveralAlgebraicForms
2033
sage: R.<x,y> = QQ[]
2034
sage: p = AlgebraicForm(2, 2, x^2, (x,y))
2035
sage: q = AlgebraicForm(2, 2, y^2, (x,y))
2036
sage: pq = SeveralAlgebraicForms([p, q])
2037
"""
2038
2039
def __init__(self, forms):
2040
"""
2041
The Python constructor.
2042
2043
TESTS::
2044
2045
sage: from sage.rings.invariant_theory import AlgebraicForm, SeveralAlgebraicForms
2046
sage: R.<x,y,z> = QQ[]
2047
sage: p = AlgebraicForm(2, 2, x^2 + y^2)
2048
sage: q = AlgebraicForm(2, 3, x^3 + y^3)
2049
sage: r = AlgebraicForm(3, 3, x^3 + y^3 + z^3)
2050
sage: pq = SeveralAlgebraicForms([p, q])
2051
sage: pr = SeveralAlgebraicForms([p, r])
2052
Traceback (most recent call last):
2053
...
2054
ValueError: All forms must be in the same variables.
2055
"""
2056
forms = tuple(forms)
2057
f = forms[0]
2058
super(SeveralAlgebraicForms, self).__init__(f._n, f._homogeneous, f._ring, f._variables)
2059
s = set(f._variables)
2060
if not all(set(f._variables) == s for f in forms):
2061
raise ValueError('All forms must be in the same variables.')
2062
self._forms = forms
2063
2064
def __cmp__(self, other):
2065
"""
2066
Compare ``self`` with ``other``.
2067
2068
EXAMPLES::
2069
2070
sage: R.<x,y> = QQ[]
2071
sage: q1 = invariant_theory.quadratic_form(x^2 + y^2)
2072
sage: q2 = invariant_theory.quadratic_form(x*y)
2073
sage: from sage.rings.invariant_theory import SeveralAlgebraicForms
2074
sage: two_inv = SeveralAlgebraicForms([q1, q2])
2075
sage: cmp(two_inv, 'foo') == 0
2076
False
2077
sage: cmp(two_inv, two_inv)
2078
0
2079
sage: two_inv.__cmp__(two_inv)
2080
0
2081
"""
2082
c = cmp(type(self), type(other))
2083
if c != 0:
2084
return c
2085
return cmp(self._forms, other._forms)
2086
2087
def _repr_(self):
2088
"""
2089
Return a string representation.
2090
2091
EXAMPLES::
2092
2093
sage: R.<x,y> = QQ[]
2094
sage: q1 = invariant_theory.quadratic_form(x^2 + y^2)
2095
sage: q2 = invariant_theory.quadratic_form(x*y)
2096
sage: q3 = invariant_theory.quadratic_form((x + y)^2)
2097
sage: from sage.rings.invariant_theory import SeveralAlgebraicForms
2098
sage: SeveralAlgebraicForms([q1]) # indirect doctest
2099
Binary quadratic with coefficients (1, 1, 0)
2100
sage: SeveralAlgebraicForms([q1, q2]) # indirect doctest
2101
Joint binary quadratic with coefficients (1, 1, 0) and binary
2102
quadratic with coefficients (0, 0, 1)
2103
sage: SeveralAlgebraicForms([q1, q2, q3]) # indirect doctest
2104
Joint binary quadratic with coefficients (1, 1, 0), binary
2105
quadratic with coefficients (0, 0, 1), and binary quadratic
2106
with coefficients (1, 1, 2)
2107
"""
2108
if self.n_forms() == 1:
2109
return self.get_form(0)._repr_()
2110
if self.n_forms() == 2:
2111
return 'Joint ' + self.get_form(0)._repr_().lower() + \
2112
' and ' + self.get_form(1)._repr_().lower()
2113
s = 'Joint '
2114
for i in range(self.n_forms()-1):
2115
s += self.get_form(i)._repr_().lower() + ', '
2116
s += 'and ' + self.get_form(-1)._repr_().lower()
2117
return s
2118
2119
def n_forms(self):
2120
"""
2121
Return the number of forms.
2122
2123
EXAMPLES::
2124
2125
sage: R.<x,y> = QQ[]
2126
sage: q1 = invariant_theory.quadratic_form(x^2 + y^2)
2127
sage: q2 = invariant_theory.quadratic_form(x*y)
2128
sage: from sage.rings.invariant_theory import SeveralAlgebraicForms
2129
sage: q12 = SeveralAlgebraicForms([q1, q2])
2130
sage: q12.n_forms()
2131
2
2132
sage: len(q12) == q12.n_forms() # syntactic sugar
2133
True
2134
"""
2135
return len(self._forms)
2136
2137
__len__ = n_forms
2138
2139
def get_form(self, i):
2140
"""
2141
Return the `i`-th form.
2142
2143
EXAMPLES::
2144
2145
sage: R.<x,y> = QQ[]
2146
sage: q1 = invariant_theory.quadratic_form(x^2 + y^2)
2147
sage: q2 = invariant_theory.quadratic_form(x*y)
2148
sage: from sage.rings.invariant_theory import SeveralAlgebraicForms
2149
sage: q12 = SeveralAlgebraicForms([q1, q2])
2150
sage: q12.get_form(0) is q1
2151
True
2152
sage: q12.get_form(1) is q2
2153
True
2154
sage: q12[0] is q12.get_form(0) # syntactic sugar
2155
True
2156
sage: q12[1] is q12.get_form(1) # syntactic sugar
2157
True
2158
"""
2159
return self._forms[i]
2160
2161
__getitem__ = get_form
2162
2163
2164
def homogenized(self, var='h'):
2165
"""
2166
Return form as defined by a homogeneous polynomial.
2167
2168
INPUT:
2169
2170
- ``var`` -- either a variable name, variable index or a
2171
variable (default: ``'h'``).
2172
2173
OUTPUT:
2174
2175
The same algebraic form, but defined by a homogeneous
2176
polynomial.
2177
2178
EXAMPLES::
2179
2180
sage: R.<x,y,z> = QQ[]
2181
sage: q = invariant_theory.quaternary_biquadratic(x^2+1, y^2+1, [x,y,z])
2182
sage: q
2183
Joint quaternary quadratic with coefficients (1, 0, 0, 1, 0, 0, 0, 0, 0, 0)
2184
and quaternary quadratic with coefficients (0, 1, 0, 1, 0, 0, 0, 0, 0, 0)
2185
sage: q.homogenized()
2186
Joint quaternary quadratic with coefficients (1, 0, 0, 1, 0, 0, 0, 0, 0, 0)
2187
and quaternary quadratic with coefficients (0, 1, 0, 1, 0, 0, 0, 0, 0, 0)
2188
sage: type(q) is type(q.homogenized())
2189
True
2190
"""
2191
if self._homogeneous:
2192
return self
2193
forms = [f.homogenized(var=var) for f in self._forms]
2194
return self.__class__(forms)
2195
2196
2197
def _check_covariant(self, method_name, g=None, invariant=False):
2198
"""
2199
Test whether ``method_name`` actually returns a covariant.
2200
2201
INPUT:
2202
2203
- ``method_name`` -- string. The name of the method that
2204
returns the invariant / covariant to test.
2205
2206
- ``g`` -- a `SL(n,\CC)` matrix or ``None`` (default). The
2207
test will be to check that the covariant transforms
2208
corrently under this special linear group element acting on
2209
the homogeneous variables. If ``None``, a random matrix will
2210
be picked.
2211
2212
- ``invariant`` -- boolean. Whether to additionaly test that
2213
it is an invariant.
2214
2215
EXAMPLES::
2216
2217
sage: R.<x,y,z,w> = QQ[]
2218
sage: q = invariant_theory.quaternary_biquadratic(x^2+y^2+z^2+w^2, x*y+y*z+z*w+x*w)
2219
sage: q._check_covariant('Delta_invariant', invariant=True)
2220
sage: q._check_covariant('T_prime_covariant')
2221
sage: q._check_covariant('T_prime_covariant', invariant=True)
2222
Traceback (most recent call last):
2223
...
2224
AssertionError: Not invariant.
2225
"""
2226
assert self._homogeneous
2227
from sage.matrix.constructor import vector, random_matrix
2228
if g is None:
2229
F = self._ring.base_ring()
2230
g = random_matrix(F, self._n, algorithm='unimodular')
2231
v = vector(self.variables())
2232
g_v = g*v
2233
transform = dict( (v[i], g_v[i]) for i in range(self._n) )
2234
# The covariant of the transformed form
2235
transformed = [f.transformed(transform) for f in self._forms]
2236
g_self = self.__class__(transformed)
2237
cov_g = getattr(g_self, method_name)()
2238
# The transform of the covariant
2239
g_cov = getattr(self, method_name)().subs(transform)
2240
# they must be the same
2241
assert (g_cov - cov_g).is_zero(), 'Not covariant.'
2242
if invariant:
2243
cov = getattr(self, method_name)()
2244
assert (cov - cov_g).is_zero(), 'Not invariant.'
2245
2246
2247
######################################################################
2248
2249
class TwoAlgebraicForms(SeveralAlgebraicForms):
2250
2251
2252
def first(self):
2253
"""
2254
Return the first of the two forms.
2255
2256
OUTPUT:
2257
2258
The first algebraic form used in the definition.
2259
2260
EXAMPLES::
2261
2262
sage: R.<x,y> = QQ[]
2263
sage: q0 = invariant_theory.quadratic_form(x^2 + y^2)
2264
sage: q1 = invariant_theory.quadratic_form(x*y)
2265
sage: from sage.rings.invariant_theory import TwoAlgebraicForms
2266
sage: q = TwoAlgebraicForms([q0, q1])
2267
sage: q.first() is q0
2268
True
2269
sage: q.get_form(0) is q0
2270
True
2271
sage: q.first().polynomial()
2272
x^2 + y^2
2273
"""
2274
return self._forms[0]
2275
2276
2277
def second(self):
2278
"""
2279
Return the second of the two forms.
2280
2281
OUTPUT:
2282
2283
The second form used in the definition.
2284
2285
EXAMPLES::
2286
2287
sage: R.<x,y> = QQ[]
2288
sage: q0 = invariant_theory.quadratic_form(x^2 + y^2)
2289
sage: q1 = invariant_theory.quadratic_form(x*y)
2290
sage: from sage.rings.invariant_theory import TwoAlgebraicForms
2291
sage: q = TwoAlgebraicForms([q0, q1])
2292
sage: q.second() is q1
2293
True
2294
sage: q.get_form(1) is q1
2295
True
2296
sage: q.second().polynomial()
2297
x*y
2298
"""
2299
return self._forms[1]
2300
2301
2302
######################################################################
2303
2304
class TwoQuaternaryQuadratics(TwoAlgebraicForms):
2305
"""
2306
Invariant theory of two quaternary quadratics.
2307
2308
You should use the :class:`invariant_theory
2309
<InvariantTheoryFactory>` factory object to construct instances
2310
of this class. See
2311
:meth:`~InvariantTheoryFactory.quaternary_biquadratics` for
2312
details.
2313
2314
REFERENCES:
2315
2316
.. [Salmon]
2317
G. Salmon: "A Treatise on the Analytic Geometry of Three
2318
Dimensions", section on "Invariants and Covariants of
2319
Systems of Quadrics".
2320
2321
TESTS::
2322
2323
sage: R.<w,x,y,z> = QQ[]
2324
sage: inv = invariant_theory.quaternary_biquadratic(w^2+x^2, y^2+z^2, w, x, y, z)
2325
sage: inv
2326
Joint quaternary quadratic with coefficients (1, 1, 0, 0, 0, 0, 0, 0, 0, 0) and
2327
quaternary quadratic with coefficients (0, 0, 1, 1, 0, 0, 0, 0, 0, 0)
2328
sage: TestSuite(inv).run()
2329
2330
sage: q1 = 73*x^2 + 96*x*y - 11*y^2 - 74*x*z - 10*y*z + 66*z^2 + 4*x + 63*y - 11*z + 57
2331
sage: q2 = 61*x^2 - 100*x*y - 72*y^2 - 38*x*z + 85*y*z + 95*z^2 - 81*x + 39*y + 23*z - 7
2332
sage: biquadratic = invariant_theory.quaternary_biquadratic(q1, q2, [x,y,z]).homogenized()
2333
sage: biquadratic._check_covariant('Delta_invariant', invariant=True)
2334
sage: biquadratic._check_covariant('Delta_prime_invariant', invariant=True)
2335
sage: biquadratic._check_covariant('Theta_invariant', invariant=True)
2336
sage: biquadratic._check_covariant('Theta_prime_invariant', invariant=True)
2337
sage: biquadratic._check_covariant('Phi_invariant', invariant=True)
2338
sage: biquadratic._check_covariant('T_covariant')
2339
sage: biquadratic._check_covariant('T_prime_covariant')
2340
sage: biquadratic._check_covariant('J_covariant')
2341
"""
2342
2343
def Delta_invariant(self):
2344
"""
2345
Return the `\Delta` invariant.
2346
2347
EXAMPLES::
2348
2349
sage: R.<x,y,z,t,a0,a1,a2,a3,b0,b1,b2,b3,b4,b5,A0,A1,A2,A3,B0,B1,B2,B3,B4,B5> = QQ[]
2350
sage: p1 = a0*x^2 + a1*y^2 + a2*z^2 + a3
2351
sage: p1 += b0*x*y + b1*x*z + b2*x + b3*y*z + b4*y + b5*z
2352
sage: p2 = A0*x^2 + A1*y^2 + A2*z^2 + A3
2353
sage: p2 += B0*x*y + B1*x*z + B2*x + B3*y*z + B4*y + B5*z
2354
sage: q = invariant_theory.quaternary_biquadratic(p1, p2, [x, y, z])
2355
sage: coeffs = det(t * q[0].matrix() + q[1].matrix()).polynomial(t).coeffs()
2356
sage: q.Delta_invariant() == coeffs[4]
2357
True
2358
"""
2359
return self.get_form(0).matrix().det()
2360
2361
2362
def Delta_prime_invariant(self):
2363
r"""
2364
Return the `\Delta'` invariant.
2365
2366
EXAMPLES::
2367
2368
sage: R.<x,y,z,t,a0,a1,a2,a3,b0,b1,b2,b3,b4,b5,A0,A1,A2,A3,B0,B1,B2,B3,B4,B5> = QQ[]
2369
sage: p1 = a0*x^2 + a1*y^2 + a2*z^2 + a3
2370
sage: p1 += b0*x*y + b1*x*z + b2*x + b3*y*z + b4*y + b5*z
2371
sage: p2 = A0*x^2 + A1*y^2 + A2*z^2 + A3
2372
sage: p2 += B0*x*y + B1*x*z + B2*x + B3*y*z + B4*y + B5*z
2373
sage: q = invariant_theory.quaternary_biquadratic(p1, p2, [x, y, z])
2374
sage: coeffs = det(t * q[0].matrix() + q[1].matrix()).polynomial(t).coeffs()
2375
sage: q.Delta_prime_invariant() == coeffs[0]
2376
True
2377
"""
2378
return self.get_form(1).matrix().det()
2379
2380
2381
def _Theta_helper(self, scaled_coeffs_1, scaled_coeffs_2):
2382
"""
2383
Internal helper method for :meth:`Theta_invariant` and
2384
:meth:`Theta_prime_invariant`.
2385
2386
TESTS::
2387
2388
sage: R.<w,x,y,z> = QQ[]
2389
sage: inv = invariant_theory.quaternary_biquadratic(w^2+x^2, y^2+z^2, w, x, y, z)
2390
sage: inv._Theta_helper([1]*10, [2]*10)
2391
0
2392
"""
2393
a0, a1, a2, a3, b0, b1, b2, b3, b4, b5 = scaled_coeffs_1
2394
A0, A1, A2, A3, B0, B1, B2, B3, B4, B5 = scaled_coeffs_2
2395
return a1*a2*a3*A0 - a3*b3**2*A0 - a2*b4**2*A0 + 2*b3*b4*b5*A0 - a1*b5**2*A0 \
2396
+ a0*a2*a3*A1 - a3*b1**2*A1 - a2*b2**2*A1 + 2*b1*b2*b5*A1 - a0*b5**2*A1 \
2397
+ a0*a1*a3*A2 - a3*b0**2*A2 - a1*b2**2*A2 + 2*b0*b2*b4*A2 - a0*b4**2*A2 \
2398
+ a0*a1*a2*A3 - a2*b0**2*A3 - a1*b1**2*A3 + 2*b0*b1*b3*A3 - a0*b3**2*A3 \
2399
- 2*a2*a3*b0*B0 + 2*a3*b1*b3*B0 + 2*a2*b2*b4*B0 - 2*b2*b3*b5*B0 \
2400
- 2*b1*b4*b5*B0 + 2*b0*b5**2*B0 - 2*a1*a3*b1*B1 + 2*a3*b0*b3*B1 \
2401
- 2*b2*b3*b4*B1 + 2*b1*b4**2*B1 + 2*a1*b2*b5*B1 - 2*b0*b4*b5*B1 \
2402
- 2*a1*a2*b2*B2 + 2*b2*b3**2*B2 + 2*a2*b0*b4*B2 - 2*b1*b3*b4*B2 \
2403
+ 2*a1*b1*b5*B2 - 2*b0*b3*b5*B2 + 2*a3*b0*b1*B3 - 2*a0*a3*b3*B3 \
2404
+ 2*b2**2*b3*B3 - 2*b1*b2*b4*B3 - 2*b0*b2*b5*B3 + 2*a0*b4*b5*B3 \
2405
+ 2*a2*b0*b2*B4 - 2*b1*b2*b3*B4 - 2*a0*a2*b4*B4 + 2*b1**2*b4*B4 \
2406
- 2*b0*b1*b5*B4 + 2*a0*b3*b5*B4 + 2*a1*b1*b2*B5 - 2*b0*b2*b3*B5 \
2407
- 2*b0*b1*b4*B5 + 2*a0*b3*b4*B5 - 2*a0*a1*b5*B5 + 2*b0**2*b5*B5
2408
2409
2410
def Theta_invariant(self):
2411
r"""
2412
Return the `\Theta` invariant.
2413
2414
EXAMPLES::
2415
2416
sage: R.<x,y,z,t,a0,a1,a2,a3,b0,b1,b2,b3,b4,b5,A0,A1,A2,A3,B0,B1,B2,B3,B4,B5> = QQ[]
2417
sage: p1 = a0*x^2 + a1*y^2 + a2*z^2 + a3
2418
sage: p1 += b0*x*y + b1*x*z + b2*x + b3*y*z + b4*y + b5*z
2419
sage: p2 = A0*x^2 + A1*y^2 + A2*z^2 + A3
2420
sage: p2 += B0*x*y + B1*x*z + B2*x + B3*y*z + B4*y + B5*z
2421
sage: q = invariant_theory.quaternary_biquadratic(p1, p2, [x, y, z])
2422
sage: coeffs = det(t * q[0].matrix() + q[1].matrix()).polynomial(t).coeffs()
2423
sage: q.Theta_invariant() == coeffs[3]
2424
True
2425
"""
2426
return self._Theta_helper(self.get_form(0).scaled_coeffs(), self.get_form(1).scaled_coeffs())
2427
2428
2429
def Theta_prime_invariant(self):
2430
r"""
2431
Return the `\Theta'` invariant.
2432
2433
EXAMPLES::
2434
2435
sage: R.<x,y,z,t,a0,a1,a2,a3,b0,b1,b2,b3,b4,b5,A0,A1,A2,A3,B0,B1,B2,B3,B4,B5> = QQ[]
2436
sage: p1 = a0*x^2 + a1*y^2 + a2*z^2 + a3
2437
sage: p1 += b0*x*y + b1*x*z + b2*x + b3*y*z + b4*y + b5*z
2438
sage: p2 = A0*x^2 + A1*y^2 + A2*z^2 + A3
2439
sage: p2 += B0*x*y + B1*x*z + B2*x + B3*y*z + B4*y + B5*z
2440
sage: q = invariant_theory.quaternary_biquadratic(p1, p2, [x, y, z])
2441
sage: coeffs = det(t * q[0].matrix() + q[1].matrix()).polynomial(t).coeffs()
2442
sage: q.Theta_prime_invariant() == coeffs[1]
2443
True
2444
"""
2445
return self._Theta_helper(self.get_form(1).scaled_coeffs(), self.get_form(0).scaled_coeffs())
2446
2447
2448
def Phi_invariant(self):
2449
"""
2450
Return the `\Phi'` invariant.
2451
2452
EXAMPLES::
2453
2454
sage: R.<x,y,z,t,a0,a1,a2,a3,b0,b1,b2,b3,b4,b5,A0,A1,A2,A3,B0,B1,B2,B3,B4,B5> = QQ[]
2455
sage: p1 = a0*x^2 + a1*y^2 + a2*z^2 + a3
2456
sage: p1 += b0*x*y + b1*x*z + b2*x + b3*y*z + b4*y + b5*z
2457
sage: p2 = A0*x^2 + A1*y^2 + A2*z^2 + A3
2458
sage: p2 += B0*x*y + B1*x*z + B2*x + B3*y*z + B4*y + B5*z
2459
sage: q = invariant_theory.quaternary_biquadratic(p1, p2, [x, y, z])
2460
sage: coeffs = det(t * q[0].matrix() + q[1].matrix()).polynomial(t).coeffs()
2461
sage: q.Phi_invariant() == coeffs[2]
2462
True
2463
"""
2464
a0, a1, a2, a3, b0, b1, b2, b3, b4, b5 = self.get_form(0).scaled_coeffs()
2465
A0, A1, A2, A3, B0, B1, B2, B3, B4, B5 = self.get_form(1).scaled_coeffs()
2466
return a2*a3*A0*A1 - b5**2*A0*A1 + a1*a3*A0*A2 - b4**2*A0*A2 + a0*a3*A1*A2 \
2467
- b2**2*A1*A2 + a1*a2*A0*A3 - b3**2*A0*A3 + a0*a2*A1*A3 - b1**2*A1*A3 \
2468
+ a0*a1*A2*A3 - b0**2*A2*A3 - 2*a3*b0*A2*B0 + 2*b2*b4*A2*B0 - 2*a2*b0*A3*B0 \
2469
+ 2*b1*b3*A3*B0 - a2*a3*B0**2 + b5**2*B0**2 - 2*a3*b1*A1*B1 + 2*b2*b5*A1*B1 \
2470
- 2*a1*b1*A3*B1 + 2*b0*b3*A3*B1 + 2*a3*b3*B0*B1 - 2*b4*b5*B0*B1 - a1*a3*B1**2 \
2471
+ b4**2*B1**2 - 2*a2*b2*A1*B2 + 2*b1*b5*A1*B2 - 2*a1*b2*A2*B2 + 2*b0*b4*A2*B2 \
2472
+ 2*a2*b4*B0*B2 - 2*b3*b5*B0*B2 - 2*b3*b4*B1*B2 + 2*a1*b5*B1*B2 - a1*a2*B2**2 \
2473
+ b3**2*B2**2 - 2*a3*b3*A0*B3 + 2*b4*b5*A0*B3 + 2*b0*b1*A3*B3 - 2*a0*b3*A3*B3 \
2474
+ 2*a3*b1*B0*B3 - 2*b2*b5*B0*B3 + 2*a3*b0*B1*B3 - 2*b2*b4*B1*B3 \
2475
+ 4*b2*b3*B2*B3 - 2*b1*b4*B2*B3 - 2*b0*b5*B2*B3 - a0*a3*B3**2 + b2**2*B3**2 \
2476
- 2*a2*b4*A0*B4 + 2*b3*b5*A0*B4 + 2*b0*b2*A2*B4 - 2*a0*b4*A2*B4 \
2477
+ 2*a2*b2*B0*B4 - 2*b1*b5*B0*B4 - 2*b2*b3*B1*B4 + 4*b1*b4*B1*B4 \
2478
- 2*b0*b5*B1*B4 + 2*a2*b0*B2*B4 - 2*b1*b3*B2*B4 - 2*b1*b2*B3*B4 \
2479
+ 2*a0*b5*B3*B4 - a0*a2*B4**2 + b1**2*B4**2 + 2*b3*b4*A0*B5 - 2*a1*b5*A0*B5 \
2480
+ 2*b1*b2*A1*B5 - 2*a0*b5*A1*B5 - 2*b2*b3*B0*B5 - 2*b1*b4*B0*B5 \
2481
+ 4*b0*b5*B0*B5 + 2*a1*b2*B1*B5 - 2*b0*b4*B1*B5 + 2*a1*b1*B2*B5 \
2482
- 2*b0*b3*B2*B5 - 2*b0*b2*B3*B5 + 2*a0*b4*B3*B5 - 2*b0*b1*B4*B5 \
2483
+ 2*a0*b3*B4*B5 - a0*a1*B5**2 + b0**2*B5**2
2484
2485
2486
def _T_helper(self, scaled_coeffs_1, scaled_coeffs_2):
2487
"""
2488
Internal helper method for :meth:`T_covariant` and
2489
:meth:`T_prime_covariant`.
2490
2491
TESTS::
2492
2493
sage: R.<w,x,y,z> = QQ[]
2494
sage: inv = invariant_theory.quaternary_biquadratic(w^2+x^2, y^2+z^2, w, x, y, z)
2495
sage: inv._T_helper([1]*10, [2]*10)
2496
0
2497
"""
2498
a0, a1, a2, a3, b0, b1, b2, b3, b4, b5 = scaled_coeffs_1
2499
A0, A1, A2, A3, B0, B1, B2, B3, B4, B5 = scaled_coeffs_2
2500
# Construct the entries of the 4x4 matrix T using symmetries:
2501
# cyclic: a0 -> a1 -> a2 -> a3 -> a0, b0->b3->b5->b2->b0, b1->b4->b1
2502
# flip: a0<->a1, b1<->b3, b2<->b4
2503
def T00(a0, a1, a2, a3, b0, b1, b2, b3, b4, b5, A0, A1, A2, A3, B0, B1, B2, B3, B4, B5):
2504
return a0*a3*A0*A1*A2 - b2**2*A0*A1*A2 + a0*a2*A0*A1*A3 - b1**2*A0*A1*A3 \
2505
+ a0*a1*A0*A2*A3 - b0**2*A0*A2*A3 - a0*a3*A2*B0**2 + b2**2*A2*B0**2 \
2506
- a0*a2*A3*B0**2 + b1**2*A3*B0**2 - 2*b0*b1*A3*B0*B1 + 2*a0*b3*A3*B0*B1 \
2507
- a0*a3*A1*B1**2 + b2**2*A1*B1**2 - a0*a1*A3*B1**2 + b0**2*A3*B1**2 \
2508
- 2*b0*b2*A2*B0*B2 + 2*a0*b4*A2*B0*B2 - 2*b1*b2*A1*B1*B2 + 2*a0*b5*A1*B1*B2 \
2509
- a0*a2*A1*B2**2 + b1**2*A1*B2**2 - a0*a1*A2*B2**2 + b0**2*A2*B2**2 \
2510
+ 2*b0*b1*A0*A3*B3 - 2*a0*b3*A0*A3*B3 + 2*a0*a3*B0*B1*B3 - 2*b2**2*B0*B1*B3 \
2511
+ 2*b1*b2*B0*B2*B3 - 2*a0*b5*B0*B2*B3 + 2*b0*b2*B1*B2*B3 - 2*a0*b4*B1*B2*B3 \
2512
- 2*b0*b1*B2**2*B3 + 2*a0*b3*B2**2*B3 - a0*a3*A0*B3**2 + b2**2*A0*B3**2 \
2513
+ 2*b0*b2*A0*A2*B4 - 2*a0*b4*A0*A2*B4 + 2*b1*b2*B0*B1*B4 - 2*a0*b5*B0*B1*B4 \
2514
- 2*b0*b2*B1**2*B4 + 2*a0*b4*B1**2*B4 + 2*a0*a2*B0*B2*B4 - 2*b1**2*B0*B2*B4 \
2515
+ 2*b0*b1*B1*B2*B4 - 2*a0*b3*B1*B2*B4 - 2*b1*b2*A0*B3*B4 + 2*a0*b5*A0*B3*B4 \
2516
- a0*a2*A0*B4**2 + b1**2*A0*B4**2 + 2*b1*b2*A0*A1*B5 - 2*a0*b5*A0*A1*B5 \
2517
- 2*b1*b2*B0**2*B5 + 2*a0*b5*B0**2*B5 + 2*b0*b2*B0*B1*B5 - 2*a0*b4*B0*B1*B5 \
2518
+ 2*b0*b1*B0*B2*B5 - 2*a0*b3*B0*B2*B5 + 2*a0*a1*B1*B2*B5 - 2*b0**2*B1*B2*B5 \
2519
- 2*b0*b2*A0*B3*B5 + 2*a0*b4*A0*B3*B5 - 2*b0*b1*A0*B4*B5 + 2*a0*b3*A0*B4*B5 \
2520
- a0*a1*A0*B5**2 + b0**2*A0*B5**2
2521
def T01(a0, a1, a2, a3, b0, b1, b2, b3, b4, b5, A0, A1, A2, A3, B0, B1, B2, B3, B4, B5):
2522
return a3*b0*A0*A1*A2 - b2*b4*A0*A1*A2 + a2*b0*A0*A1*A3 - b1*b3*A0*A1*A3 \
2523
+ a0*a1*A2*A3*B0 - b0**2*A2*A3*B0 - a3*b0*A2*B0**2 + b2*b4*A2*B0**2 \
2524
- a2*b0*A3*B0**2 + b1*b3*A3*B0**2 - b0*b1*A1*A3*B1 + a0*b3*A1*A3*B1 \
2525
- a1*b1*A3*B0*B1 + b0*b3*A3*B0*B1 - a3*b0*A1*B1**2 + b2*b4*A1*B1**2 \
2526
- b0*b2*A1*A2*B2 + a0*b4*A1*A2*B2 - a1*b2*A2*B0*B2 + b0*b4*A2*B0*B2 \
2527
- b2*b3*A1*B1*B2 - b1*b4*A1*B1*B2 + 2*b0*b5*A1*B1*B2 - a2*b0*A1*B2**2 \
2528
+ b1*b3*A1*B2**2 + a1*b1*A0*A3*B3 - b0*b3*A0*A3*B3 + b0*b1*A3*B0*B3 \
2529
- a0*b3*A3*B0*B3 - a0*a1*A3*B1*B3 + b0**2*A3*B1*B3 + 2*a3*b0*B0*B1*B3 \
2530
- 2*b2*b4*B0*B1*B3 + b2*b3*B0*B2*B3 + b1*b4*B0*B2*B3 - 2*b0*b5*B0*B2*B3 \
2531
+ a1*b2*B1*B2*B3 - b0*b4*B1*B2*B3 - a1*b1*B2**2*B3 + b0*b3*B2**2*B3 \
2532
- a3*b0*A0*B3**2 + b2*b4*A0*B3**2 + b0*b2*B2*B3**2 - a0*b4*B2*B3**2 \
2533
+ a1*b2*A0*A2*B4 - b0*b4*A0*A2*B4 + b0*b2*A2*B0*B4 - a0*b4*A2*B0*B4 \
2534
+ b2*b3*B0*B1*B4 + b1*b4*B0*B1*B4 - 2*b0*b5*B0*B1*B4 - a1*b2*B1**2*B4 \
2535
+ b0*b4*B1**2*B4 - a0*a1*A2*B2*B4 + b0**2*A2*B2*B4 + 2*a2*b0*B0*B2*B4 \
2536
- 2*b1*b3*B0*B2*B4 + a1*b1*B1*B2*B4 - b0*b3*B1*B2*B4 - b2*b3*A0*B3*B4 \
2537
- b1*b4*A0*B3*B4 + 2*b0*b5*A0*B3*B4 - b0*b2*B1*B3*B4 + a0*b4*B1*B3*B4 \
2538
- b0*b1*B2*B3*B4 + a0*b3*B2*B3*B4 - a2*b0*A0*B4**2 + b1*b3*A0*B4**2 \
2539
+ b0*b1*B1*B4**2 - a0*b3*B1*B4**2 + b2*b3*A0*A1*B5 + b1*b4*A0*A1*B5 \
2540
- 2*b0*b5*A0*A1*B5 - b2*b3*B0**2*B5 - b1*b4*B0**2*B5 + 2*b0*b5*B0**2*B5 \
2541
+ b0*b2*A1*B1*B5 - a0*b4*A1*B1*B5 + a1*b2*B0*B1*B5 - b0*b4*B0*B1*B5 \
2542
+ b0*b1*A1*B2*B5 - a0*b3*A1*B2*B5 + a1*b1*B0*B2*B5 - b0*b3*B0*B2*B5 \
2543
- a1*b2*A0*B3*B5 + b0*b4*A0*B3*B5 - b0*b2*B0*B3*B5 + a0*b4*B0*B3*B5 \
2544
+ a0*a1*B2*B3*B5 - b0**2*B2*B3*B5 - a1*b1*A0*B4*B5 + b0*b3*A0*B4*B5 \
2545
- b0*b1*B0*B4*B5 + a0*b3*B0*B4*B5 + a0*a1*B1*B4*B5 - b0**2*B1*B4*B5 \
2546
- a0*a1*B0*B5**2 + b0**2*B0*B5**2
2547
2548
t00 = T00(a0, a1, a2, a3, b0, b1, b2, b3, b4, b5, A0, A1, A2, A3, B0, B1, B2, B3, B4, B5)
2549
t11 = T00(a1, a2, a3, a0, b3, b4, b0, b5, b1, b2, A1, A2, A3, A0, B3, B4, B0, B5, B1, B2)
2550
t22 = T00(a2, a3, a0, a1, b5, b1, b3, b2, b4, b0, A2, A3, A0, A1, B5, B1, B3, B2, B4, B0)
2551
t33 = T00(a3, a0, a1, a2, b2, b4, b5, b0, b1, b3, A3, A0, A1, A2, B2, B4, B5, B0, B1, B3)
2552
t01 = T01(a0, a1, a2, a3, b0, b1, b2, b3, b4, b5, A0, A1, A2, A3, B0, B1, B2, B3, B4, B5)
2553
t12 = T01(a1, a2, a3, a0, b3, b4, b0, b5, b1, b2, A1, A2, A3, A0, B3, B4, B0, B5, B1, B2)
2554
t23 = T01(a2, a3, a0, a1, b5, b1, b3, b2, b4, b0, A2, A3, A0, A1, B5, B1, B3, B2, B4, B0)
2555
t30 = T01(a3, a0, a1, a2, b2, b4, b5, b0, b1, b3, A3, A0, A1, A2, B2, B4, B5, B0, B1, B3)
2556
t02 = T01(a0, a2, a3, a1, b1, b2, b0, b5, b3, b4, A0, A2, A3, A1, B1, B2, B0, B5, B3, B4)
2557
t13 = T01(a1, a3, a0, a2, b4, b0, b3, b2, b5, b1, A1, A3, A0, A2, B4, B0, B3, B2, B5, B1)
2558
if self._homogeneous:
2559
w, x, y, z = self._variables
2560
else:
2561
w, x, y = self._variables[0:3]
2562
z = self._ring.one()
2563
return t00*w*w + 2*t01*w*x + 2*t02*w*y + 2*t30*w*z + t11*x*x + 2*t12*x*y \
2564
+ 2*t13*x*z + t22*y*y + 2*t23*y*z + t33*z*z
2565
2566
2567
def T_covariant(self):
2568
"""
2569
The $T$-covariant.
2570
2571
EXAMPLES::
2572
2573
sage: R.<x,y,z,t,a0,a1,a2,a3,b0,b1,b2,b3,b4,b5,A0,A1,A2,A3,B0,B1,B2,B3,B4,B5> = QQ[]
2574
sage: p1 = a0*x^2 + a1*y^2 + a2*z^2 + a3
2575
sage: p1 += b0*x*y + b1*x*z + b2*x + b3*y*z + b4*y + b5*z
2576
sage: p2 = A0*x^2 + A1*y^2 + A2*z^2 + A3
2577
sage: p2 += B0*x*y + B1*x*z + B2*x + B3*y*z + B4*y + B5*z
2578
sage: q = invariant_theory.quaternary_biquadratic(p1, p2, [x, y, z])
2579
sage: T = invariant_theory.quaternary_quadratic(q.T_covariant(), [x,y,z]).matrix()
2580
sage: M = q[0].matrix().adjoint() + t*q[1].matrix().adjoint()
2581
sage: M = M.adjoint().apply_map( # long time (4s on my thinkpad W530)
2582
....: lambda m: m.coefficient(t))
2583
sage: M == q.Delta_invariant()*T # long time
2584
True
2585
"""
2586
return self._T_helper(self.get_form(0).scaled_coeffs(), self.get_form(1).scaled_coeffs())
2587
2588
2589
def T_prime_covariant(self):
2590
"""
2591
The $T'$-covariant.
2592
2593
EXAMPLES::
2594
2595
sage: R.<x,y,z,t,a0,a1,a2,a3,b0,b1,b2,b3,b4,b5,A0,A1,A2,A3,B0,B1,B2,B3,B4,B5> = QQ[]
2596
sage: p1 = a0*x^2 + a1*y^2 + a2*z^2 + a3
2597
sage: p1 += b0*x*y + b1*x*z + b2*x + b3*y*z + b4*y + b5*z
2598
sage: p2 = A0*x^2 + A1*y^2 + A2*z^2 + A3
2599
sage: p2 += B0*x*y + B1*x*z + B2*x + B3*y*z + B4*y + B5*z
2600
sage: q = invariant_theory.quaternary_biquadratic(p1, p2, [x, y, z])
2601
sage: Tprime = invariant_theory.quaternary_quadratic(
2602
....: q.T_prime_covariant(), [x,y,z]).matrix()
2603
sage: M = q[0].matrix().adjoint() + t*q[1].matrix().adjoint()
2604
sage: M = M.adjoint().apply_map( # long time (4s on my thinkpad W530)
2605
....: lambda m: m.coefficient(t^2))
2606
sage: M == q.Delta_prime_invariant() * Tprime # long time
2607
True
2608
"""
2609
return self._T_helper(self.get_form(1).scaled_coeffs(), self.get_form(0).scaled_coeffs())
2610
2611
2612
def J_covariant(self):
2613
"""
2614
The $J$-covariant.
2615
2616
This is the Jacobian determinant of the two biquadratics, the
2617
$T$-covariant, and the $T'$-covariant with respect to the four
2618
homogeneous variables.
2619
2620
EXAMPLES::
2621
2622
sage: R.<w,x,y,z,a0,a1,a2,a3,A0,A1,A2,A3> = QQ[]
2623
sage: p1 = a0*x^2 + a1*y^2 + a2*z^2 + a3*w^2
2624
sage: p2 = A0*x^2 + A1*y^2 + A2*z^2 + A3*w^2
2625
sage: q = invariant_theory.quaternary_biquadratic(p1, p2, [w, x, y, z])
2626
sage: q.J_covariant().factor()
2627
z * y * x * w * (a3*A2 - a2*A3) * (a3*A1 - a1*A3) * (-a2*A1 + a1*A2)
2628
* (a3*A0 - a0*A3) * (-a2*A0 + a0*A2) * (-a1*A0 + a0*A1)
2629
"""
2630
F = self._ring.base_ring()
2631
return 1/F(16) * self._jacobian_determinant(
2632
[self.first().form(), 2],
2633
[self.second().form(), 2],
2634
[self.T_covariant(), 4],
2635
[self.T_prime_covariant(), 4])
2636
2637
2638
def syzygy(self, Delta, Theta, Phi, Theta_prime, Delta_prime, U, V, T, T_prime, J):
2639
"""
2640
Return the syzygy evaluated on the invariants and covariants.
2641
2642
INPUT:
2643
2644
- ``Delta``, ``Theta``, ``Phi``, ``Theta_prime``,
2645
``Delta_prime``, ``U``, ``V``, ``T``, ``T_prime``, ``J`` --
2646
polynomials from the same polynomial ring.
2647
2648
OUTPUT:
2649
2650
Zero if the ``U`` is the first polynomial, ``V`` the second
2651
polynomial, and the remaining input are the invariants and
2652
covariants of a quaternary biquadratic.
2653
2654
EXAMPLES::
2655
2656
sage: R.<w,x,y,z> = QQ[]
2657
sage: monomials = [x^2, x*y, y^2, x*z, y*z, z^2, x*w, y*w, z*w, w^2]
2658
sage: def q_rnd(): return sum(randint(-1000,1000)*m for m in monomials)
2659
sage: biquadratic = invariant_theory.quaternary_biquadratic(q_rnd(), q_rnd())
2660
sage: Delta = biquadratic.Delta_invariant()
2661
sage: Theta = biquadratic.Theta_invariant()
2662
sage: Phi = biquadratic.Phi_invariant()
2663
sage: Theta_prime = biquadratic.Theta_prime_invariant()
2664
sage: Delta_prime = biquadratic.Delta_prime_invariant()
2665
sage: U = biquadratic.first().polynomial()
2666
sage: V = biquadratic.second().polynomial()
2667
sage: T = biquadratic.T_covariant()
2668
sage: T_prime = biquadratic.T_prime_covariant()
2669
sage: J = biquadratic.J_covariant()
2670
sage: biquadratic.syzygy(Delta, Theta, Phi, Theta_prime, Delta_prime, U, V, T, T_prime, J)
2671
0
2672
2673
If the arguments are not the invariants and covariants then
2674
the output is some (generically non-zero) polynomial::
2675
2676
sage: biquadratic.syzygy(1, 1, 1, 1, 1, 1, 1, 1, 1, x)
2677
-x^2 + 1
2678
"""
2679
return -J**2 + \
2680
Delta * T**4 - Theta * T**3*T_prime + Phi * T**2*T_prime**2 \
2681
- Theta_prime * T*T_prime**3 + Delta_prime * T_prime**4 + \
2682
( (Theta_prime**2 - 2*Delta_prime*Phi) * T_prime**3 -
2683
(Theta_prime*Phi - 3*Theta*Delta_prime) * T_prime**2*T +
2684
(Theta*Theta_prime - 4*Delta*Delta_prime) * T_prime*T**2 -
2685
(Delta*Theta_prime) * T**3
2686
) * U + \
2687
( (Theta**2 - 2*Delta*Phi)*T**3 -
2688
(Theta*Phi - 3*Theta_prime*Delta)*T**2*T_prime +
2689
(Theta*Theta_prime - 4*Delta*Delta_prime)*T*T_prime**2 -
2690
(Delta_prime*Theta)*T_prime**3
2691
)* V + \
2692
( (Delta*Phi*Delta_prime) * T**2 +
2693
(3*Delta*Theta_prime*Delta_prime - Theta*Phi*Delta_prime) * T*T_prime +
2694
(2*Delta*Delta_prime**2 - 2*Theta*Theta_prime*Delta_prime
2695
+ Phi**2*Delta_prime) * T_prime**2
2696
) * U**2 + \
2697
( (Delta*Theta*Delta_prime + 2*Delta*Phi*Theta_prime - Theta**2*Theta_prime) * T**2 +
2698
(4*Delta*Phi*Delta_prime - 3*Theta**2*Delta_prime
2699
- 3*Delta*Theta_prime**2 + Theta*Phi*Theta_prime) * T*T_prime +
2700
(Delta*Theta_prime*Delta_prime + 2*Delta_prime*Phi*Theta
2701
- Theta*Theta_prime**2) * T_prime**2
2702
) * U*V + \
2703
( (2*Delta**2*Delta_prime - 2*Delta*Theta*Theta_prime + Delta*Phi**2) * T**2 +
2704
(3*Delta*Theta*Delta_prime - Delta*Phi*Theta_prime) * T*T_prime +
2705
Delta*Phi*Delta_prime * T_prime**2
2706
) * V**2 + \
2707
( (-Delta*Theta*Delta_prime**2) * T +
2708
(-2*Delta*Phi*Delta_prime**2 + Theta**2*Delta_prime**2) * T_prime
2709
) * U**3 + \
2710
( (4*Delta**2*Delta_prime**2 - Delta*Theta*Theta_prime*Delta_prime
2711
- 2*Delta*Phi**2*Delta_prime + Theta**2*Phi*Delta_prime) * T +
2712
(-5*Delta*Theta*Delta_prime**2 + Delta*Phi*Theta_prime*Delta_prime
2713
+ 2*Theta**2*Theta_prime*Delta_prime - Theta*Phi**2*Delta_prime) * T_prime
2714
) * U**2*V + \
2715
( (-5*Delta**2*Theta_prime*Delta_prime + Delta*Theta*Phi*Delta_prime
2716
+ 2*Delta*Theta*Theta_prime**2 - Delta*Phi**2*Theta_prime) * T +
2717
(4*Delta**2*Delta_prime**2 - Delta*Theta*Theta_prime*Delta_prime
2718
- 2*Delta*Phi**2*Delta_prime + Delta*Phi*Theta_prime**2) * T_prime
2719
) * U*V**2 + \
2720
( (-2*Delta**2*Phi*Delta_prime + Delta**2*Theta_prime**2) * T +
2721
(-Delta**2*Theta_prime*Delta_prime) * T_prime
2722
) * V**3 + \
2723
(Delta**2*Delta_prime**3) * U**4 + \
2724
(-3*Delta**2*Theta_prime*Delta_prime**2 + 3*Delta*Theta*Phi*Delta_prime**2
2725
- Theta**3*Delta_prime**2) * U**3*V + \
2726
(-3*Delta**2*Phi*Delta_prime**2 + 3*Delta*Theta**2*Delta_prime**2
2727
+ 3*Delta**2*Theta_prime**2*Delta_prime
2728
- 3*Delta*Theta*Phi*Theta_prime*Delta_prime
2729
+ Delta*Phi**3*Delta_prime) * U**2*V**2 + \
2730
(-3*Delta**2*Theta*Delta_prime**2 + 3*Delta**2*Phi*Theta_prime*Delta_prime
2731
- Delta**2*Theta_prime**3) * U*V**3 + \
2732
(Delta**3*Delta_prime**2) * V**4
2733
2734
2735
######################################################################
2736
2737
class InvariantTheoryFactory(object):
2738
"""
2739
Factory object for invariants of multilinear forms.
2740
2741
EXAMPLES::
2742
2743
sage: R.<x,y,z> = QQ[]
2744
sage: invariant_theory.ternary_cubic(x^3+y^3+z^3)
2745
Ternary cubic with coefficients (1, 1, 1, 0, 0, 0, 0, 0, 0, 0)
2746
"""
2747
2748
def __repr__(self):
2749
"""
2750
Return a string representation.
2751
2752
OUTPUT:
2753
2754
String.
2755
2756
EXAMPLES::
2757
2758
sage: invariant_theory
2759
<BLANKLINE>
2760
Use the invariant_theory object to construct algebraic forms. These
2761
can then be queried for invariant and covariants. For example,
2762
<BLANKLINE>
2763
s...: R.<x,y,z> = QQ[]
2764
s...: invariant_theory.ternary_cubic(x^3+y^3+z^3)
2765
Ternary cubic with coefficients (1, 1, 1, 0, 0, 0, 0, 0, 0, 0)
2766
s...: invariant_theory.ternary_cubic(x^3+y^3+z^3).J_covariant()
2767
x^6*y^3 - x^3*y^6 - x^6*z^3 + y^6*z^3 + x^3*z^6 - y^3*z^6
2768
"""
2769
return """
2770
Use the invariant_theory object to construct algebraic forms. These
2771
can then be queried for invariant and covariants. For example,
2772
2773
sage: R.<x,y,z> = QQ[]
2774
sage: invariant_theory.ternary_cubic(x^3+y^3+z^3)
2775
Ternary cubic with coefficients (1, 1, 1, 0, 0, 0, 0, 0, 0, 0)
2776
sage: invariant_theory.ternary_cubic(x^3+y^3+z^3).J_covariant()
2777
x^6*y^3 - x^3*y^6 - x^6*z^3 + y^6*z^3 + x^3*z^6 - y^3*z^6
2778
"""
2779
2780
2781
def quadratic_form(self, polynomial, *args):
2782
"""
2783
Invariants of a homogeneous quadratic form.
2784
2785
INPUT:
2786
2787
- ``polynomial`` -- a homogeneous or inhomogeneous quadratic form.
2788
2789
- ``*args`` -- the variables as multiple arguments, or as a
2790
single list/tuple. If the last argument is ``None``, the
2791
cubic is assumed to be inhomogeneous.
2792
2793
EXAMPLES::
2794
2795
sage: R.<x,y,z> = QQ[]
2796
sage: quadratic = x^2+y^2+z^2
2797
sage: inv = invariant_theory.quadratic_form(quadratic)
2798
sage: type(inv)
2799
<class 'sage.rings.invariant_theory.TernaryQuadratic'>
2800
2801
If some of the ring variables are to be treated as coefficients
2802
you need to specify the polynomial variables::
2803
2804
sage: R.<x,y,z, a,b> = QQ[]
2805
sage: quadratic = a*x^2+b*y^2+z^2+2*y*z
2806
sage: invariant_theory.quadratic_form(quadratic, x,y,z)
2807
Ternary quadratic with coefficients (a, b, 1, 0, 0, 2)
2808
sage: invariant_theory.quadratic_form(quadratic, [x,y,z]) # alternate syntax
2809
Ternary quadratic with coefficients (a, b, 1, 0, 0, 2)
2810
2811
Inhomogeneous quadratic forms (see also
2812
:meth:`inhomogeneous_quadratic_form`) can be specified by
2813
passing ``None`` as the last variable::
2814
2815
sage: inhom = quadratic.subs(z=1)
2816
sage: invariant_theory.quadratic_form(inhom, x,y,None)
2817
Ternary quadratic with coefficients (a, b, 1, 0, 0, 2)
2818
"""
2819
variables = _guess_variables(polynomial, *args)
2820
n = len(variables)
2821
if n == 3:
2822
return TernaryQuadratic(3, 2, polynomial, *args)
2823
else:
2824
return QuadraticForm(n, 2, polynomial, *args)
2825
2826
def inhomogeneous_quadratic_form(self, polynomial, *args):
2827
"""
2828
Invariants of an inhomogeneous quadratic form.
2829
2830
INPUT:
2831
2832
- ``polynomial`` -- an inhomogeneous quadratic form.
2833
2834
- ``*args`` -- the variables as multiple arguments, or as a
2835
single list/tuple.
2836
2837
EXAMPLES::
2838
2839
sage: R.<x,y,z> = QQ[]
2840
sage: quadratic = x^2+2*y^2+3*x*y+4*x+5*y+6
2841
sage: inv3 = invariant_theory.inhomogeneous_quadratic_form(quadratic)
2842
sage: type(inv3)
2843
<class 'sage.rings.invariant_theory.TernaryQuadratic'>
2844
sage: inv4 = invariant_theory.inhomogeneous_quadratic_form(x^2+y^2+z^2)
2845
sage: type(inv4)
2846
<class 'sage.rings.invariant_theory.QuadraticForm'>
2847
"""
2848
variables = _guess_variables(polynomial, *args)
2849
n = len(variables) + 1
2850
if n == 3:
2851
return TernaryQuadratic(3, 2, polynomial, *args)
2852
else:
2853
return QuadraticForm(n, 2, polynomial, *args)
2854
2855
def binary_quadratic(self, quadratic, *args):
2856
"""
2857
Invariant theory of a quadratic in two variables.
2858
2859
INPUT:
2860
2861
- ``quadratic`` -- a quadratic form.
2862
2863
- ``x``, ``y`` -- the homogeneous variables. If ``y`` is
2864
``None``, the quadratic is assumed to be inhomogeneous.
2865
2866
REFERENCES:
2867
2868
.. http://en.wikipedia.org/wiki/Invariant_of_a_binary_form
2869
2870
EXAMPLES::
2871
2872
sage: R.<x,y> = QQ[]
2873
sage: invariant_theory.binary_quadratic(x^2+y^2)
2874
Binary quadratic with coefficients (1, 1, 0)
2875
2876
sage: T.<t> = QQ[]
2877
sage: invariant_theory.binary_quadratic(t^2 + 2*t + 1, [t])
2878
Binary quadratic with coefficients (1, 1, 2)
2879
"""
2880
return QuadraticForm(2, 2, quadratic, *args)
2881
2882
def quaternary_quadratic(self, quadratic, *args):
2883
"""
2884
Invariant theory of a quadratic in four variables.
2885
2886
INPUT:
2887
2888
- ``quadratic`` -- a quadratic form.
2889
2890
- ``w``, ``x``, ``y``, ``z`` -- the homogeneous variables. If
2891
``z`` is ``None``, the quadratic is assumed to be
2892
inhomogeneous.
2893
2894
REFERENCES:
2895
2896
.. [WpBinaryForm]
2897
http://en.wikipedia.org/wiki/Invariant_of_a_binary_form
2898
2899
EXAMPLES::
2900
2901
sage: R.<w,x,y,z> = QQ[]
2902
sage: invariant_theory.quaternary_quadratic(w^2+x^2+y^2+z^2)
2903
Quaternary quadratic with coefficients (1, 1, 1, 1, 0, 0, 0, 0, 0, 0)
2904
2905
sage: R.<x,y,z> = QQ[]
2906
sage: invariant_theory.quaternary_quadratic(1+x^2+y^2+z^2)
2907
Quaternary quadratic with coefficients (1, 1, 1, 1, 0, 0, 0, 0, 0, 0)
2908
"""
2909
return QuadraticForm(4, 2, quadratic, *args)
2910
2911
def binary_quartic(self, quartic, *args, **kwds):
2912
"""
2913
Invariant theory of a quartic in two variables.
2914
2915
The algebra of invariants of a quartic form is generated by
2916
invariants `i`, `j` of degrees 2, 3. This ring is naturally
2917
isomorphic to the ring of modular forms of level 1, with the
2918
two generators corresponding to the Eisenstein series `E_4`
2919
(see
2920
:meth:`~sage.rings.invariant_theory.BinaryQuartic.EisensteinD`)
2921
and `E_6` (see
2922
:meth:`~sage.rings.invariant_theory.BinaryQuartic.EisensteinE`). The
2923
algebra of covariants is generated by these two invariants
2924
together with the form `f` of degree 1 and order 4, the
2925
Hessian `g` (see :meth:`~BinaryQuartic.g_covariant`) of degree
2926
2 and order 4, and a covariant `h` (see
2927
:meth:`~BinaryQuartic.h_covariant`) of degree 3 and order
2928
6. They are related by a syzygy
2929
2930
.. math::
2931
2932
j f^3 - g f^2 i + 4 g^3 + h^2 = 0
2933
2934
of degree 6 and order 12.
2935
2936
INPUT:
2937
2938
- ``quartic`` -- a quartic.
2939
2940
- ``x``, ``y`` -- the homogeneous variables. If ``y`` is
2941
``None``, the quartic is assumed to be inhomogeneous.
2942
2943
REFERENCES:
2944
2945
.. http://en.wikipedia.org/wiki/Invariant_of_a_binary_form
2946
2947
EXAMPLES::
2948
2949
sage: R.<x,y> = QQ[]
2950
sage: quartic = invariant_theory.binary_quartic(x^4+y^4)
2951
sage: quartic
2952
Binary quartic with coefficients (1, 0, 0, 0, 1)
2953
sage: type(quartic)
2954
<class 'sage.rings.invariant_theory.BinaryQuartic'>
2955
"""
2956
return BinaryQuartic(2, 4, quartic, *args, **kwds)
2957
2958
def ternary_quadratic(self, quadratic, *args, **kwds):
2959
"""
2960
Invariants of a quadratic in three variables.
2961
2962
INPUT:
2963
2964
- ``quadratic`` -- a homogeneous quadratic in 3 homogeneous
2965
variables, or an inhomogeneous quadratic in 2 variables.
2966
2967
- ``x``, ``y``, ``z`` -- the variables. If ``z`` is ``None``,
2968
the quadratic is assumed to be inhomogeneous.
2969
2970
REFERENCES:
2971
2972
.. http://en.wikipedia.org/wiki/Invariant_of_a_binary_form
2973
2974
EXAMPLES::
2975
2976
sage: R.<x,y,z> = QQ[]
2977
sage: invariant_theory.ternary_quadratic(x^2+y^2+z^2)
2978
Ternary quadratic with coefficients (1, 1, 1, 0, 0, 0)
2979
2980
sage: T.<u, v> = QQ[]
2981
sage: invariant_theory.ternary_quadratic(1+u^2+v^2)
2982
Ternary quadratic with coefficients (1, 1, 1, 0, 0, 0)
2983
2984
sage: quadratic = x^2+y^2+z^2
2985
sage: inv = invariant_theory.ternary_quadratic(quadratic)
2986
sage: type(inv)
2987
<class 'sage.rings.invariant_theory.TernaryQuadratic'>
2988
"""
2989
return TernaryQuadratic(3, 2, quadratic, *args, **kwds)
2990
2991
def ternary_cubic(self, cubic, *args, **kwds):
2992
r"""
2993
Invariants of a cubic in three variables.
2994
2995
The algebra of invariants of a ternary cubic under `SL_3(\CC)`
2996
is a polynomial algebra generated by two invariants `S` (see
2997
:meth:`~sage.rings.invariant_theory.TernaryCubic.S_invariant`)
2998
and T (see
2999
:meth:`~sage.rings.invariant_theory.TernaryCubic.T_invariant`)
3000
of degrees 4 and 6, called Aronhold invariants.
3001
3002
The ring of covariants is given as follows. The identity
3003
covariant U of a ternary cubic has degree 1 and order 3. The
3004
Hessian `H` (see
3005
:meth:`~sage.rings.invariant_theory.TernaryCubic.Hessian`)
3006
is a covariant of ternary cubics of degree 3 and order 3.
3007
There is a covariant `\Theta` (see
3008
:meth:`~sage.rings.invariant_theory.TernaryCubic.Theta_covariant`)
3009
of ternary cubics of degree 8 and order 6 that vanishes on
3010
points `x` lying on the Salmon conic of the polar of `x` with
3011
respect to the curve and its Hessian curve. The Brioschi
3012
covariant `J` (see
3013
:meth:`~sage.rings.invariant_theory.TernaryCubic.J_covariant`)
3014
is the Jacobian of `U`, `\Theta`, and `H` of degree 12, order
3015
9. The algebra of covariants of a ternary cubic is generated
3016
over the ring of invariants by `U`, `\Theta`, `H`, and `J`,
3017
with a relation
3018
3019
.. math::
3020
3021
\begin{split}
3022
J^2 =& 4 \Theta^3 + T U^2 \Theta^2 +
3023
\Theta (-4 S^3 U^4 + 2 S T U^3 H
3024
- 72 S^2 U^2 H^2
3025
\\ &
3026
- 18 T U H^3 + 108 S H^4)
3027
-16 S^4 U^5 H - 11 S^2 T U^4 H^2
3028
\\ &
3029
-4 T^2 U^3 H^3
3030
+54 S T U^2 H^4 -432 S^2 U H^5 -27 T H^6
3031
\end{split}
3032
3033
3034
REFERENCES:
3035
3036
.. [WpTernaryCubic]
3037
http://en.wikipedia.org/wiki/Ternary_cubic
3038
3039
INPUT:
3040
3041
- ``cubic`` -- a homogeneous cubic in 3 homogeneous variables,
3042
or an inhomogeneous cubic in 2 variables.
3043
3044
- ``x``, ``y``, ``z`` -- the variables. If ``z`` is ``None``, the
3045
cubic is assumed to be inhomogeneous.
3046
3047
EXAMPLES::
3048
3049
sage: R.<x,y,z> = QQ[]
3050
sage: cubic = invariant_theory.ternary_cubic(x^3+y^3+z^3)
3051
sage: type(cubic)
3052
<class 'sage.rings.invariant_theory.TernaryCubic'>
3053
"""
3054
return TernaryCubic(3, 3, cubic, *args, **kwds)
3055
3056
def quaternary_biquadratic(self, quadratic1, quadratic2, *args, **kwds):
3057
"""
3058
Invariants of two quadratics in four variables.
3059
3060
INPUT:
3061
3062
- ``quadratic1``, ``quadratic2`` -- two polynomias. Either homogeneous quadratic
3063
in 4 homogeneous variables, or inhomogeneous quadratic
3064
in 3 variables.
3065
3066
- ``w``, ``x``, ``y``, ``z`` -- the variables. If ``z`` is
3067
``None``, the quadratics are assumed to be inhomogeneous.
3068
3069
EXAMPLES::
3070
3071
sage: R.<w,x,y,z> = QQ[]
3072
sage: q1 = w^2+x^2+y^2+z^2
3073
sage: q2 = w*x + y*z
3074
sage: inv = invariant_theory.quaternary_biquadratic(q1, q2)
3075
sage: type(inv)
3076
<class 'sage.rings.invariant_theory.TwoQuaternaryQuadratics'>
3077
3078
Distance between two spheres [Salmon]_ ::
3079
3080
sage: R.<x,y,z, a,b,c, r1,r2> = QQ[]
3081
sage: S1 = -r1^2 + x^2 + y^2 + z^2
3082
sage: S2 = -r2^2 + (x-a)^2 + (y-b)^2 + (z-c)^2
3083
sage: inv = invariant_theory.quaternary_biquadratic(S1, S2, [x, y, z])
3084
sage: inv.Delta_invariant()
3085
-r1^2
3086
sage: inv.Delta_prime_invariant()
3087
-r2^2
3088
sage: inv.Theta_invariant()
3089
a^2 + b^2 + c^2 - 3*r1^2 - r2^2
3090
sage: inv.Theta_prime_invariant()
3091
a^2 + b^2 + c^2 - r1^2 - 3*r2^2
3092
sage: inv.Phi_invariant()
3093
2*a^2 + 2*b^2 + 2*c^2 - 3*r1^2 - 3*r2^2
3094
sage: inv.J_covariant()
3095
0
3096
"""
3097
q1 = QuadraticForm(4, 2, quadratic1, *args, **kwds)
3098
q2 = QuadraticForm(4, 2, quadratic2, *args, **kwds)
3099
return TwoQuaternaryQuadratics([q1, q2])
3100
3101
3102
3103
invariant_theory = InvariantTheoryFactory()
3104
3105
3106