------------------------------------------------------------------- Time-stamp: Description: Quaternions number over Q Authors: Fernando Rodriguez-Villegas villegas@math.utexas.edu University of Texas at Austin Ariel Martin Pacetti apacetti@math.utexas.edu University of texas at Austin ------------------------------------------------------------------- UNDER CONSTRUCTION This file describes the PARI-GP routines in the file qalg.gp. To use: in a GP session issue the command \r qalg.gp (if that's the name you gave the file; you might need more of its path if you GP session is running with another default directory). We consider B to be a quaternion algebra, over the rational numbers. It's always denoted: B=((a,b)/Q) And is represented as Q[1,i,j,k] where i^2 = a, j^2 = b, ij=-ji To represent a such algebra, issue the command: qset(a,b) Where a and b are integers. Then the elements will be represented as vectors [x_1,x_2,x_3,x_4] associates to the number x_1 + x_2 i + x_3 j + x_4 ij Let's say we work with N=43. So, we set it doing: qset(-1,-1) Note that it's the usual Hamilton quaternion algebra. Then you can start with the basic operation as sum: qsum([1,0,2,0],[0,1,1,0]) Which will output [1, 1, 3, 0] You can try taking the product of quaternions: qprod([1,0,2,0],[0,1,1,0]) will output [-2, 1, 1, -2] And we can make the division of quaternions, but there is a difference between right division and left division, because the algebra is not abelian. For example: qrdiv([1,0,2,0],[0,1,1,0]) is the right division, and will output: [1, -1/2, -1/2, 1] And the left division: qldiv([1,0,2,0],[0,1,1,0]) will output: [2/5, 1/5, 1/5, 2/5] And we can compute the inverse of an element (if it exists) as: qinv([1,2,1,4]) will output: [1/22, -1/11, -1/22, -2/11] There are some other commands as square, cinjugate, reduced trace, and reduced norm, which are: qsq([1,2,1,4]) will output: [-20, 4, 2, 8] qconj([1,2,1,4]) will output: [1, -2, -1, -4] qtr([1,2,1,4]) will output: 2 qn([1,2,1,4]) will output: 22 Now we start working with some lattice L. Given a lattice, which is a Z-module of rank 4 over Q, we can assign an integer, which is it's norm, so to avoid computing it a lot of times, all the lattices we consider have it's norm as last entry. If we have a lattice L, for example: L = [[1, 2, 3, 3], [2, 3, 0, 1], [1, 3, 2, 1], [0, 0, 0, 1]] We can construct a lattice, writting: L=qsetlat (L) which will output: [[1, 2, 3, 3], [2, 3, 0, 1], [1, 3, 2, 1], [0, 0, 0, 1], 1] What means that the norm of L is 1, and we can start working with lattices now. For example, given a lattice L, and a vector v = [1,1,1,1] we can ask if v is in L, so we writte: qisin([1,1,1,1],[[1, 2, 3, 3], [2, 3, 0, 1], [1, 3, 2, 1], [0, 0, 0, 1],1]) which will output: 0 That means false (and 1 is true). We can ask for the coordinates of the vector v in the lattice L, writting: qcoord([1,1,1,1],[[1, 2, 3, 3], [2, 3, 0, 1], [1, 3, 2, 1], [0, 0, 0, 1],1]) which will output: [5/7, 3/7, -4/7, -1]~ These are the coordinates of v in the basis for L. Note that the vector is in the lattice if and only if this coordinates are integers. We know that the quaternion algebra, looked as vector space has a bilinear form, given by B(x,y) = trace(x * conjugate (y)). Then given two vectors: v1 = [1,2,3,1] v2 = [2,1,3,0] And compute the bilinear form in them: qbil(v1,v2) which will output: 782 And given a lattice, we can writte the bilinear form associated to the lattice, that is: B_L(x,y)= B(x,y)/N(L). This form written in the lattice basis is: L = [[1, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [1, 1, 1, 1], 1] qgram(L) which will output: [2 2 2 2] [2 4 4 4] [2 4 6 6] [2 4 6 8] If we just need the diagonal of the bilinear form, without computing the hole thing, we can use: qgramdiag(L) which will output: [1, 2, 45, 88] Note that we can make some operation with lattices, for example compute the conjugate: qlatconj(L) which will output: [[1, 0, 0, 0], [1, -1, 0, 0], [1, -1, -1, 0], [1, -1, -1, -1], 1] Or compute the product of two lattices, for example: qlatprod(L,qlatconj(L)) which will output: [[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, -1], 1] Another interesting thing to do, is given two lattices L1, and L2, find the lattice L spanned by L1 and L2. We can do this with: qextendlat([[1, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [1, 1, 1, 1], 1], [[1, 2, 3, 3], [2, 3, 0, 1], [1, 3, 2, 1], [0, 0, 0, 1], 1]) which will output: [[0, 0, 0, 1], [1, 0, 0, 0], [0, 0, -1, 0], [0, -1, 0, 0], 1] Or a little more generally, we can add to a lattice some vectors, and get again a lattice, for example: qextendvec([[0, 0, 0, 1], [1, 0, 0, 0], [0, 0, -1, 0], [0, -1, 0, 0], 1], [[1,3,2,-1],[1/2,0,2,0]]) which will output: [[-1/2, 0, 0, 0], [0, 0, 0, 1], [0, 0, -1, 0], [0, -1, 0, 0], 1/4] Now suppose we have two lattices, and we want to know if they are the same, we can ask: qaresamelat([[1, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [1, 1, 1, 1], 1],[[1, 2, 3, 3], [2, 3, 0, 1], [1, 3, 2, 1], [0, 0, 0, 1], 1]) which will output: 0 A little more complicated problem is to find the intersection of two lattices, we can do this using: qlatint([[1, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [1, 1, 1, 1], 1],[[1, 2, 3, 3], [2, 3, 0, 1], [1, 3, 2, 1], [0, 0, 0, 1], 1]) which will output: [[0, 0, 0, 1], [0, -1, 1, 0], [1, -1, -1, 0], [-2, -2, -1, 0], 1] At the same time, we can change the scale of a lattice, that means change the basis by a scalar, fixing the lattice norm at the same time: qscale (L,2) which will output: [[1/2, 0, 0, 0], [1/2, 1/2, 0, 0], [1/2, 1/2, 1/2, 0], [1/2, 1/2, 1/2,1/2], 1/4] Our main interest is to start working with the problem of orders, so the first thing to do is given a lattice to compute it's right and left order, so we made some extra algorithm (like qcond, qcondz, qrveccond and qlveccond) for solving this problem, but are usefull on themselves, so you can check them on the qalg.gp file. To compute this orders, we have: qrorder([[1, 2, 3, 3], [2, 3, 0, 1], [1, 3, 2, 1], [0, 0, 0, 1], 1]) which will output: [[1, 0, 0, 0], [0, -7, 0, 0], [0, 0, -7, 0], [0, 0, 0, -7], 1] And the other order: qlorder([[1, 2, 3, 3], [2, 3, 0, 1], [1, 3, 2, 1], [0, 0, 0, 1], 1]) which will output: [[1, 0, 0, 0], [0, 0, -7, 0], [0, -7, 0, 0], [0, 0, 0, 7], 1] At last we can see if one lattice is inside another one, with: qlatisin([[1, 2, 3, 3], [2, 3, 0, 1], [1, 3, 2, 1], [0, 0, 0, 1], 1],[[1, 1, 2, 0], [1, -2, 1, 1], [0, 2, 0, 3], [1, 4, 2, 3], 1]) which will output: 0 Or ask if a lattice is an ideal, for example: qlatisideal([[1, 1, 2, 0], [1, -2, 1, 1], [0, 2, 0, 3], [1, 4, 2, 3], 1]) which will ouptut: 0