// Magma example
// Compute the algebraic Brauer-Manin obstruction on the K3 surface
// X : X0^4 + X1^4 = 6 X2^4 + 12 X3^4
//
// Define a few useful objects
Q := Rationals();
Z := Integers();
//
// Define 3-dimensional projective space over Q
P3 := ProjectiveSpace(Q,3);
// And the variety X
X := Scheme(P3, X0^4 + X1^4 - 6*X2^4 - 12*X3^4);
X;
//
// Before we do anything else, we should check to see that X has points
// in each Q_v. It is clear that there are real points. For the other
// places, general arguments tell us that we only need to check at 5
// and at the primes dividing the coefficients.
IsLocallySolvable(X,2);
IsLocallySolvable(X,3);
IsLocallySolvable(X,5);
//
// Good. To compute the Brauer-Manin obstruction, the first thing we
// need to do is construct the Picard group and the Galois action on it.
// We know that the Picard group of this surface is generated by the 48
// straight lines that it contains, and it is easy to see that they are
// all defined over the field K formed by adjoining to Q a square root
// of -1, a fourth root of 2 and a fourth root of 3.
//
// Define the polynomial ring Q[t]
_ := PolynomialRing(Q);
// Define the number field generated by three elements
// i satisfying i^2 = -1
// r2 satisfying r2^4 = 2
// r3 satisfying r3^4 = 3
K* := NumberField([t^2+1, t^4-2, t^4-3] : Abs := true);
K;
// and let e be a primitive eighth root of unity in this field.
e := (1+i)/(r2^2);
//
// We now want to know the Galois group Gal(K/Q). We could ask Magma
// to compute it, but that would take a very long time. As it's an
// easy extension (a Kummer extension of Q[i]), we already know three
// generators for the Galois group.
tau := hom< K->K | [-i, r2, r3] >;
sigma2 := hom< K->K | [i, i*r2, r3] >;
sigma3 := hom< K->K | [i, r2, i*r3] >;
//
// Now we can ask Magma to compose these and see what group they
// generate. We get an abstract group Gal together with a map
// Gal -> Aut(K/Q) giving the action.
Gal, GalKAction := GenericGroup([tau, sigma2, sigma3]);
Gal;
#Gal;
// Good. It has order 32.
//
// We want to define the 48 lines on X, but they are only defined over K.
// So we create P3 over K, and base change X into it.
P3K := ProjectiveSpace(K,3);
XK := BaseChange(X, P3K);
//
// Now we define the 48 lines on X; their equations are
// { X0 = e^m a1 X1, X2 = e^n (a3/a2) X3 : m,n in [1,3,5,7] }
// { X0 = e^m a2 X2, X3 = e^n (a1/a3) X1 : m,n in [1,3,5,7] }
// { X0 = e^m a3 X3, X1 = e^n (a2/a1) X2 : m,n in [1,3,5,7] }.
// where a1, a2, a3 are chosen fourth roots of 1, -6 and -12 respectively.
a1 := 1;
a2 := e*r2*r3;
a3 := e*r2^2*r3;
//
lines1 := [ Scheme(P3K, [X0K - (e^m*a1) * X1K, X2K - (e^n*a3/a2) * X3K] )
: m,n in [1,3,5,7] ];
lines2 := [ Scheme(P3K, [X0K - (e^m*a2) * X2K, X3K - (e^n*a1/a3) * X1K] )
: m,n in [1,3,5,7] ];
lines3 := [ Scheme(P3K, [X0K - (e^m*a3) * X3K, X1K - (e^n*a2/a1) * X2K] )
: m,n in [1,3,5,7] ];
//
// Put them all together in one big list
lines := lines1 cat lines2 cat lines3;
#lines;
//
// Check that they do all indeed lie in X
[ l subset XK : l in lines ];
// and that they are all lines
[ Degree(l) : l in lines ];
//
// Next we want to know that Galois action on the lines - that is, a
// subgroup of S48 isomorphic to the Galois group and its permutation
// action on the lines.
//
// First we need to be able to let elements of the Galois group act on
// the lines. At the moment they can only act on elements of K.
//
// Firstly, define a function which computes the image of a polynomial
// over K under an automorphism of K. We just extract the coefficients
// of the polynomial, find their images under the action, and put
// everything back together into a polynomial. If we're careful, it can
// also work for polynomials defined over subfields of K.
AutPolyAction := function(P, a)
k := BaseRing(Parent(P));
m := Monomials(P);
c := Coefficients(P);
return &+[ k!(c[i] @ a) * m[i] : i in [1..#c] ];
end function;
//
// Given that, we can define a function which computes the image of a
// scheme defined over K under an automorphism of K. We list the defining
// polynomials of the scheme Y, act on them using the previous function
// and use the resulting polynomials to define a new scheme.
AutSchemeAction := function(Y, a)
A := AmbientSpace(Y);
return Scheme(A, [ AutPolyAction(P, a)
: P in DefiningPolynomials(Y) ] );
end function;
//
// Now, given an automorphism of K/Q, we can find how it permutes
// the lines.
LAction := func< a |
[ Index(lines, AutSchemeAction(l, a)) : l in lines ] >;
//
// For example, here is what sigma2 does:
LAction(sigma2);
//
// We create the subgroup of S48 generated by the permutations
// corresponding to our three generators of the Galois group.
G := PermutationGroup<48 | [ LAction(tau), LAction(sigma2),
LAction(sigma3) ] >;
G;
#G;
//
// There is an isomorphism from G to Gal
phi := hom< G -> Gal | Gal.1, Gal.2, Gal.3 >;
//
// And for convenience we give a name to the composite homomorphism
// from G to Aut(K).
GKAction := phi * GalKAction;
//
// From on we will do all our group theory in G, rather than Gal - it is
// much faster to work in a permutation group than in a group defined by
// generators and relations, and more algorithms are available.
//
// Now we would like to compute the intersection numbers of all the lines.
// Since they are lines, it's very easy. If two distinct lines meet, they
// have intersection number 1; if they don't meet, they have intersection
// number 0. We can write this as
// l1 . l2 = dim(l1 meet l2) + 1
// since Magma thinks an empty scheme has dimension -1.
// This takes a little while - computing with schemes is sometimes slow.
Int := Matrix([ [ Dimension(l1 meet l2) + 1 : l1 in lines ]
: l2 in lines ]);
// The self-intersection of each line is -2, as we can tell from the
// adjunction formula. We put -2's down the diagonal of the matrix.
for i in [1..48] do Int[i,i] := -2; end for;
//
// This intersection form should be preserved by the action of G on
// the free Z-module generated by the 48 lines. Let's check.
[ P * Int * Transpose(P) eq Int where P is PermutationMatrix(Z, g)
: g in Generators(G) ];
//
// We'll write Lambda for the free Z-module generated by the 48 lines.
// We can create a separate object to represent the corresponding
// G-module, which is Lambda together with the permutation action of G.
LambdaMod := PermutationModule(G, Z);
// Give a name to the homomorphism G -> GL(Z,48)
rhoL := Representation(LambdaMod);
//
// The Picard group of X is generated by the 48 lines, and divisors are
// linearly equivalent if and only if they are numerically equivalent.
// The sums of lines numerically equivalent to zero are those lying in
// the kernel of the intersection matrix.
//
// So we want to create the quotient G-module of Lambda by the kernel
// of Int. But there's no function to do that. First we create the
// quotient of the underlying Z-module, together with the quotient map
// taking a sum of lines to its class in Pic X.
Lambda := VectorSpace(LambdaMod); // poorly-named function
Pic, class := quo< Lambda | Kernel(Int) >;
//
// To compute the G-action on the quotient, we need a matrix representing
// the quotient map...
ClassMatrix := Matrix( [ class(Lambda.i) : i in [1..48] ] );
//
// ... and a section (in this case a *right* inverse) of it.
ClassSection := Solution(ClassMatrix, IdentityMatrix(Z, 20));
//
// Using these, we can work out the action on Pic of each generator of G.
// So we can construct the G-module corresponding to Pic.
PicMod := GModule(G, [ ClassSection * M * ClassMatrix :
M in ActionGenerators(LambdaMod) ] );
//
// We now have done everything we need to compute H^1(Gal(K/Q), Pic X_K).
// In Magma, we first create a cohomology module...
CM := CohomologyModule(G, PicMod);
// ... and then compute the cohomology group.
H1 := CohomologyGroup(CM, 1);
#H1;
//
// This has order 2. The one non-trivial element corresponds to a
// non-trivial algebra in Br_1 X / Br Q.
//
// We hope that our element is split by a cyclic extension of Q.
// To check, we list all the subgroups H of G such that G/H is cyclic.
// For each one, we will compute the cohomology group H^1(H, Pic X_K)
// and the restriction map to it. If the restriction of our element
// is zero, then it is split by the fixed field of H.
//
// First list the proper normal subgroups of G, and check whether
// the quotient is cyclic.
CycSubs := [ H`subgroup : H in Subgroups(G : Al := "Normal")
| H`order lt 32 and IsCyclic( quo< G | H`subgroup> ) ];
#CycSubs;
[ Index(G,H) : H in CycSubs ];
//
// We'll make a list of those subgroups which do give a splitting field.
Split := [];
// Now, for each subgroup...
for H in CycSubs do
// Create a new cohomology module for the restriction to H
CMH := Restriction(CM, H);
// Compute the cohomology group
H1H := CohomologyGroup(CMH, 1);
// Form the restriction map H^1(G, Pic X_K) -> H^1(H, Pic X_K).
res := hom*# H1H |
x:->IdentifyOneCocycle(CMH, OneCocycle(CM,x)) >;
// Now check whether the generator of H1 is in the kernel of this map.
if res(H1.1) eq H1H!0 then Append(~Split, H); end if;
end for;
#Split;
// Two cyclic extensions of Q split our algebra. Here they are:
[ FixedField(K, [ GKAction(h) : h in Generators(H) ]) : H in Split ];
//
// Excellent - two quadratic extensions of Q which split the algebra.
// We'll pick the first one, which is Q(i) =: l.
H := Split[1];
l := FixedField(K, [GKAction(h) : h in Generators(H)]);
l;
//
// The algebra must now be of the form (-1, f), where f is a function
// satisfying (f) = N_l/Q D, with D a non-principal divisor defined over l.
// We'll look for such divisors directly.
//
// Get the action map G -> Aut(Pic)
rhoP := Representation(PicMod);
// Now find the submodule of Pic which is fixed by all the generators
// of H.
PicH := &meet[ Kernel(rhoP(h) - IdentityMatrix(Z,20))
: h in Generators(H) ];
Rank(PicH);
//
// We'd like to know the action of G on PicH. Once again, there is no
// function to do that. We find the inclusion map PicH -> Pic and
// find a section of it, and then use these to get the action on the
// submodule.
inc := Morphism(PicH, Pic);
GPicHAction := hom< G -> GL(3, Z) |
[ Matrix( Solution(inc, [PicH.i * rhoP(G.j) : i in [1..3]] ) )
: j in [1..3] ] >;
GPicHAction;
//
// Now Gal(l/Q) has order 2, so to find its action we just pick any
// element of G which is not in H.
sigma := Rep( [ g : g in Generators(G) | not (g in H) ] );
// We get matrices for the action of norm (sigma + 1) and delta (sigma - 1)
// acting on Pic H.
norm := GPicHAction(sigma) + IdentityMatrix(Z,3);
delta := GPicHAction(sigma) - IdentityMatrix(Z,3);
//
// Now our divisor class will be in the kernel of norm, modulo the
// image of delta. We already know that this will have order 2, since
// H^-1(l/Q, Pic X_H) = H^1(l/Q, Pic X_H) and the latter is the whole
// of H^1(K/Q, Pic X_H) which has order 2.
#quo< Kernel(norm) | Image(delta) >;
//
// Let dd be the image in Pic of a generator of Ker(norm). This is a
// divisor class which is defined over l, is not trivial, but whose
// norm is principal.
dd := inc(Kernel(norm).1);
[ dd * rhoP(h) eq dd : h in Generators(H) ];
dd;
dd + dd * rhoP(sigma);
//
// The divisor class dd is defined over l. We can lift it to Lambda, so
// finding a sum of lines in this class:
d := dd @@ class;
d;
// but this divisor isn't defined over l.
[ d * rhoL(h) eq d : h in Generators(H) ];
//
// In fact there is no sum of lines, in this class, defined over l.
LambdaH := &meet[ Kernel(rhoL(h) - IdentityMatrix(Z,48))
: h in Generators(H) ];
dd in class(LambdaH);
//
// We do need to find a divisor in this class which is defined over l.
// But we know that it can't be a sum of lines. To find it, we will
// first find a sum of lines in the class which is defined over a small
// extension of l. Maybe we can do it with a quadratic extension.
//
// So we list all the subgroups of index 2 in H.
Split := [];
for HH in LowIndexSubgroups(H,2) do
// Find the submodule of Lambda fixed by the subgroup
LambdaHH := &meet[ Kernel(rhoL(h) - IdentityMatrix(Z,48))
: h in Generators(HH) ];
// And check whether dd lies in the image of this in the Picard group
if dd in class(LambdaHH) then Append(~Split, ); end if;
end for;
//
#Split;
// Good news - one of them has worked.
HH, LambdaHH := Explode(Split[1]);
// Let L be the fixed field of HH. It is a quadratic extension of l.
L := FixedField(K, [ GKAction(h) : h in Generators(HH) ] );
L;
//
// Find a sum of lines defined over L which has class dd
// For some reason we can't just pull back under the composite
// map LambdaHH -> Lambda -> Pic, so we form a matrix for this map
// and then ask for a solution to the matrix equation.
DL := Solution( Matrix( [ class(x) : x in Basis(LambdaHH) ] ), dd);
DL;
// This is in terms of a basis for LambdaHH, so we map it into Lambda.
DL := DL * Morphism(LambdaHH, Lambda);
DL;
class(DL) eq dd;
// Good - it is in the right Picard class.
//
// Now we want to find a divisor, equivalent to DL, but defined over l.
// We know that DL is linearly equivalent to its conjugate over l, since
// the class is fixed by Gal(L/l). Let's check this:
//
// Gal(L/l) is generated by the image of any element of H which is
// not in HH.
sigmaLl := Rep( [ h : h in Generators(H) | not(h in HH) ] );
class(DL) eq class(DL * rhoL(sigmaLl));
//
// For what is to come, we need to replace DL by DL + H, where H is a
// plane section. In this case there is an obvious plane section to use:
// those four lines occurring with coefficient -1 lie in a plane, so are
// a plane section of X since they have degree 4.
DLH := DL + Vector([0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
DLH;
//
// Now we have an effective divisor in the class DL + H. Of course it is
// again equivalent to its conjugate. So we should be able to find a
// function defined over L with a zero along DL+H and a pole along its
// conjugate.
//
// We make the schemes corresponding to DL+H and its conjugate.
Dplus := &join[ lines[i] : i in [1..48] | DLH[i] eq 1 ];
Dminus := &join[ lines[i] : i in [1..48] | (DLH * rhoL(sigmaLl))[i] eq 1 ];
Dplus;
Dminus;
//
// At the moment these are still schemes over K as far as Magma is
// concerned. Things might be quicker if we turn them into schemes
// over L. We define projective space over L
P3L := ProjectiveSpace(L,3);
// and base-change our schemes into it.
XL := BaseChange(X,P3L);
Dplus := BaseChange(Dplus, P3L);
Dminus := BaseChange(Dminus, P3L);
//
// Now we find a basis for the vector space of homogeneous forms of
// degree 2 which vanish on Dminus.
L2 := LinearSystem(P3L, 2);
L2Dminus := LinearSystem(L2, Dminus);
L2Dminus;
//
// This is two-dimensional. We'll pick an element in it.
g := L2Dminus.1;
g;
// Now g vanishes on Dminus, but also elsewhere. Let E be the residual
// part.
E := Difference(Scheme(XL, g), Dminus);
// Given that (Dplus - Dminus) is principal, there should be exactly one
// homogeneous form of degree 2, up to scalars, which vanishes on E and
// Dplus. We find it.
L2EDplus := LinearSystem(L2, E join Dplus);
L2EDplus;
// Good.
f := L2EDplus.1;
//
// Now the function f/g must have divisor Dplus - Dminus. Let's check.
Difference(Scheme(XL,f), Scheme(XL,g)) eq Dplus;
Difference(Scheme(XL,g), Scheme(XL,f)) eq Dminus;
//
// We use the function f/g to define a morphism from X to P1, defined
// over L.
P1L := ProjectiveSpace(L,1);
phi := map< XL -> P1L | [f,g] >;
//
// Now the fibre of phi at 0 is (Dplus + E), and the fibre at infinity
// is (Dminus + E).
[0,1] @@ phi eq Dplus join E;
[1,0] @@ phi eq Dminus join E;
//
// The other fibres are of the form (D + E), where D is some divisor
// equivalent to DL + H. We want to find a D which is defined over l.
//
// Here we use a trick. The norm of the function f/g has trivial divisor,
// so it is constant - say N(f/g) = a.
Nf := f * AutPolyAction(f, GKAction(sigmaLl));
Ng := g * AutPolyAction(g, GKAction(sigmaLl));
a := FieldOfFractions(CoordinateRing(XL))!(Nf/Ng);
a;
a in L;
a := L!a;
//
// Now we find an element c of L with that same norm. To do so, we look
// at L as an extension of l and ask Magma to solve the norm equation.
// That there is a solution follows from the fact that X is everywhere
// locally soluble.
Ll := RelativeField(l,L);
b, S := NormEquation(Ll, a);
// Was a solution found?
b;
// And what is it?
c := L!S[1];
c;
//
// Now for the trick: the divisor (phi*(c) - E) is defined over l.
D := Difference([c,1] @@ phi, E);
D;
AutSchemeAction(D, GKAction(sigmaLl)) eq D;
//
// So we finally have it: a divisor D defined over l, not principal,
// such that N_l/Q D is principal.
//
// Well, actually, not quite... remember that plane section?
// In fact D-H is what we want, but we'll leave the plane section
// there for now, because it helps in the next step.
// So actually N_l/Q D is equivalent to 2H.
//
// We want to find the function whose divisor is N_l/Q (D-H). Since
// N_l/Q D is equivalent to 2H, there must be a homogeneous form of
// degree 2 vanishing on it.
//
ND := D join AutSchemeAction(D, GKAction(sigma));
ND;
// This is defined over Q, so we can base change.
ND := BaseChange(ND, P3);
//
L2 := LinearSystem(P3, 2);
LD := LinearSystem(L2, ND);
LD;
//
// Good - there is our quadratic form.
// To turn that into the quaternion algebra we're looking for, we need to
// get rid of that 2*(plane section). Putting X0^2 in the denominator
// will do nicely.
F := LD.1;
f := F / X0^2;
f;
// Now (-1,f) is a quaternion algebra over k(X) which is an Azumaya
// algebra, representing the unique non-trivial class in Br_1 X / Br Q.
// We hope it will give us a Brauer-Manin obstruction to the existence
// of rational points on X.
//
// We may need some more representations of our algebra, because f will
// have zeros and poles in some places. So we find another divisor
// defined over l, equivalent to D, and repeat the process. To do this,
// we need to find another element with the same norm as c. We achieve
// this by multiplying c by an appropriate unit.
UL, u := UnitGroup(L);
UL;
// The unit group has rank 1. Let's find the norms in l of its
// generators...
Norm(Ll!u(UL.1));
Norm(Ll!u(UL.2));
// In both cases it is i. So the subgroup of units of norm 1 is generated
// by the fourth powers of both generators. We'll use the one of infinite
// order.
D2 := Difference([c*u(4*UL.2),1] @@ phi, E);
ND2 := D2 join AutSchemeAction(D2, GKAction(sigma));
ND2 := BaseChange(ND2, P3);
LD2 := LinearSystem(L2, ND2);
F2 := ClearDenominators(LD2.1);
F2;
//
// And another one...
D3 := Difference([c*u(8*UL.2),1] @@ phi, E);
ND3 := D3 join AutSchemeAction(D3, GKAction(sigma));
ND3 := BaseChange(ND3, P3);
LD3 := LinearSystem(L2, ND3);
F3 := ClearDenominators(LD3.1);
F3;
//
// These algebras (-1, F/X0^2), (-1, F2/X0^2), (-1, F3/X0^2) all
// represent the same class in (Br_1 X / Br Q), but they may differ by
// an element of Br Q. We should remember this.
// Note that we could also use (-1, F/X1^2) or (-1, F/X2^2) for example,
// so it will be easy to avoid any particular point lying on a pole.
//
// Between them, these functions are never simultaneously zero anywhere
// on X(Q), hence on any X(Q_v).
IsEmpty(Scheme(X, [F, F2, F3]));
//
// To evaluate the obstruction, it's time to start listing points in X(Q_v)
// for different v.
//
// For primes other than 2, 3 and the real prime, we already know that the
// invariant is going to be zero everywhere - given any point P, there
// will be some representative of the algebra taking a unit value at P,
// and ( unit, unit )_p = 1.
//
// For example, see what happens at p=7. Firstly we create a model of X
// over Z, so that we can base-change it to GF(7).
XZ := Scheme( ProjectiveSpace(Z, 3), DefiningPolynomial(X) );
X7 := BaseChange(XZ, GF(7));
#RationalPoints(X7);
// Does F take the value 0 at any of these points?
#RationalPoints( Scheme(X7, F) );
// No. So the value of F is a unit at every 7-adic point of X, and
// therefore one of (-1, F/X0^2), (-1, F/X1^2), (-1, F/X2^2), (-1, F/X3^2)
// will be a unit at each point. So the invariant is everywhere zero
// on X(Q_7).
//
// What about p=5? Of course -1 is a square in Q_5, so the invariant is
// automatically zero everywhere. But suppose we hadn't noticed that.
X5 := BaseChange(XZ, GF(5));
#RationalPoints(X5);
// Does F ever take the value 0?
#RationalPoints( Scheme(X5, F) );
// Yes, it does. Maybe F and F2 are never simultaneously zero.
#RationalPoints( Scheme(X5, [F, F2]) );
// Hmmm. What about if we throw in F3 as well?
#RationalPoints( Scheme(X5, [F, F2, F3]) );
// Good. Again, at every point, one of our representatives takes a unit
// value. The invariant is everywhere zero.
//
// To look at the interesting primes, we may have to look modulo higher
// powers of p. As Magma can't do this so easily, we have to set up a
// few functions.
//
// A point in P^3(Q_p) looks like
// [a + O(p^r), b + O(p^r), c + O(p^r), d + O(p^r)]
// where a,b,c,d are integers not all divisible by p, and r is some
// positive integer called the precision. We write a little utility
// function to construct these points.
pAdicPoint := func< k,v,r | [ elt< k | x,r > : x in v ] >;
//
// We can use a very inefficient way to list the points in X(Q_p) to
// precision r.
pAdicPoints := function(k,r)
p := Prime(k);
return { P : a,b,c,d in [0..p^r-1] |
(a mod p ne 0 or b mod p ne 0
or c mod p ne 0 or d mod p ne 0)
and P in X
where P is pAdicPoint(k, [a,b,c,d], r) };
end function;
//
// Let's have a look at p=3.
Q3 := pAdicField(3);
points3 := pAdicPoints(Q3, 1);
points3;
// Actually there are only two different projective points here, because
// we're being stupid. But never mind.
// We'll try evaluating our function F.
{ Evaluate(F, P) : P in points3 };
// That's no good for telling us what the invariant is. Maybe one of the
// other representatives will work.
{ [ Evaluate(F,P), Evaluate(F2,P), Evaluate(F3,P)] : P in points3 };
// Still no luck. Rather than looking for more representatives, we'll list
// the points to higher precision.
points9 := pAdicPoints(Q3, 2);
{ Evaluate(F, P) : P in points9 };
// That's more like it. Now the formula for the Hilbert symbol tells us
// that (-1,f) = -1 everywhere on X(Q_3), and so the invariant of
// our Azumaya algebra is everywhere 1/2.
//
// Now we look at p=2.
Q2 := pAdicField(2);
points2 := pAdicPoints(Q2,1);
{ Evaluate(F, P) : P in points2 };
// Knowing that b = 2 (mod 4) is not enough to determine (-1, b)_2. We
// must look deeper.
points4 := pAdicPoints(Q2,2);
{ Evaluate(F, P) : P in points4 };
// Knowing that b = 2 (mod 8) is enough to know that (-1, b)_2 = 1.
// So (-1, F)_2 = 1 everywhere on X(Q_2). In other words, the invariant
// of our algebra is everywhere 0.
// Incidentally, our other two representatives agree exactly.
{ [ Evaluate(F,P), Evaluate(F2,P), Evaluate(F3,P)] : P in points4 };
// If we had gone straight to looking mod 16, this is what we would
// have got:
points16 := pAdicPoints(Q2,4);
{ [ Evaluate(F,P), Evaluate(F2,P), Evaluate(F3,P)] : P in points16 };
// There are different values here - the functions are variously
// 2, 10, -6, -14 mod 32 - but all these have Hilbert symbol 1.
//
// Finally, we need to look at the real locus. The affine piece
// X0 <> 0 is given by
// x^4 = 6y^4 + 12z^4 - 1
// which is connected, as can be seen by sketching it. We know that
// the invariant is constant on connected components, so it is enough
// to evaluate it at one point. We'll take X0 = X2 = X3 = 1.
_ := PolynomialRing(RealField());
P := Evaluate(DefiningPolynomial(X), [1,t,1,1]);
P;
x := Roots(P)[1][1];
x;
Evaluate(F, [1,x,1,1]);
// This is positive, so (-1, F) = 1 on X(R). The invariant is everywhere
// zero.
//
// So we have computed that the invariant of the Azumaya algebra
// (-1, F/X0^2) is constantly zero on X(Q_v) for all v other than 3,
// and that the invariant is constantly 1/2 on X(Q_3). Therefore the
// sum of the invariants is 1/2 on the whole of the adelic points X(A_Q),
// and there can be no rational solution.
//
// Thank you, and have a safe journey home!