// 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!