(*:Name: NumberTheory`LLLalgorithm` *) (*:Summary: The extended Lenstra Lenstra Lovasz algorithm finds *) (* a lattice basis consisting of "short" vectors, *) (* together with a transformation that relates the new *) (* basis with the original set of generators. Instead of *) (* giving a generating set directly, one may also give *) (* its Gram matrix. When the generators are linearly *) (* dependent, a reduced basis of the lattice of relations *) (* ("null space lattice") is also obtained. *) (*:Author: Wilberd van der Kallen, 1998 *) (* http://www.math.uu.nl/people/vdkallen/kallen.html *) (* bugs to: vdkallen@math.uu.nl *) (*:Mathematica Version: 3.0 *) (*:Package Version: 1.111 *) (*:Warnings: *) (* For us a lattice in euclidean n-space need not be of full rank n. *) (*:Sources: For the original LLL paper, see *) (* Mathematische Annalen 261, 515-534 (1982). *) (* For the case of dependent vectors, see *) (* M. Pohst, A modification of the LLL-algorithm, *) (* Journal of Symbolic Computation 4 (1987), 123--128. *) (* Much of the code below relies on: *) (* Henri Cohen, A course in computational Algebraic Number Theory, *) (* Graduate Texts in Mathematics 138, Springer 1993. *) (* :Discussion: This version is a rather minimal version with few *) (* options. It may serve to document the algorithm. *) (* *) (* We add ExtendedLatticeReduce and GramLatticeReduce to the *) (* builtin function LatticeReduce. *) (* *) (* ExtendedLatticeReduce[matrix] *) (* *) (* computes a reduced basis of two lattices. One is the row space *) (* lattice spanned by the rows of the matrix, the other is the null *) (* space lattice, consisting of vectors v that have integer entries *) (* and satisfy v.matrix=0. (Contrary to the function NullSpace, we *) (* are concerned with multiplication from the left here.) *) BeginPackage["LLLalgorithm`"] ExtendedLatticeReduce::usage:= "ExtendedLatticeReduce[{v1, v2, ...}] gives {reduced basis,transformation} where the transformation is an integral matrix T of determinant plus or minus one such that the rows of T.{v1, v2, ...} form a reduced basis, possibly preceded by zero vectors. These zero vectors occur if the integral vectors vi are linearly dependent. In that case the corresponding top part of T is a reduced basis of the `null space lattice'."; GramLatticeReduce::usage:= "GramLatticeReduce[{{vi.vj}}] gives {rank of lattice,transformation} where the transformation is an integral matrix T of determinant plus or minus one such that the rows of T.{v1, v2, ...} form a reduced basis, possibly preceded by zero vectors. These zero vectors occur if the vectors vi are linearly dependent.\n The Gram matrix {{vi.vj}} should have rational entries."; RatioLLLCondition::usage:="RatioLLLCondition is an option in ExtendedLatticeReduce and in GramLatticeReduce. It tells which ratio to use in the criterion for an LLL reduced basis. The default is 3/4, but for some applications one should take it closer to one.\n RatioLLLCondition->{3/4,19/20,99/100} causes three passes, with ratios 3/4, 19/20, 99/100 respectively." (*:Examples: In[1]:= < {{1, -1, -1, 1}, {1, -2, 1, 0}, {-1, -1, 1, 0}, {2, 1, -1, 0}}} In[4]:= transformation.generatingset Out[4]= {{0, 0, 0}, {0, 0, 0}, {2, 1, 0}, {-1, 1, 3}} In[5]:= Take[%,-Length[reducedbasis]] Out[5]= {{2, 1, 0}, {-1, 1, 3}} In[6]:= %==reducedbasis Out[6]= True In[7]:= GramLatticeReduce[generatingset.Transpose[generatingset]]== {Length[reducedbasis],transformation} Out[7]= True In[8]:= reducedbasisnullspace=Take[transformation,Length[transformation]- Length[reducedbasis]] Out[8]= {{1, -1, -1, 1}, {1, -2, 1, 0}} In[9]:= reducedbasisnullspace.generatingset Out[9]= {{0, 0, 0}, {0, 0, 0}} *) Unprotect[ExtendedLatticeReduce,GramLatticeReduce]; Options[ExtendedLatticeReduce]={RatioLLLCondition->3/4}; Options[GramLatticeReduce]={RatioLLLCondition->3/4}; Begin["LLLalgorithm`Private`"] (* The following will be local to this Private block: answer m b mat basis matextendedgcd bbb moreratios coefficient n coefficientiso cond notLLLcond d notLLLcondiso diso post px error py extendedLLL q finished r four rank gcd rat gram ratiolist gramLLL red gramorg rules i setratio incGS swap init swaptail inner swaptailiso integerbody t isodim testgramm testLLLcondition j testm k three kmax trickledown l v la val LLLbranch w LLLratio x LLLratiolist y *) (* MAIN INTERFACE *) ExtendedLatticeReduce[m_,rules___Rule]:= ( LLLratio=(RatioLLLCondition/.{rules}/.Options[ExtendedLatticeReduce]); extendedLLL[m] ) GramLatticeReduce[m_,rules___Rule]:= ( LLLratio=(RatioLLLCondition/.{rules}/.Options[GramLatticeReduce]); gramLLL[m] ) extendedLLL[m_]:= CheckAbort[( testm[m]; integerbody;answer={Take[b.basis,-rank],b} ) ,$Failed ] gramLLL[m_]:= CheckAbort[( testgramm[m]; integerbody;answer={rank,b} ) ,$Failed ] (* SUBROUTINES *) (* ----- Purpose of testm: Test if main argument of ExtendedLatticeReduce is legal. Compute gram matrix. ------- *) testm[m_]:= ( basis=m; (* begin check argument *) error:=(Message[ExtendedLatticeReduce::argint,basis];Abort[]); If[MatrixQ[basis], Map[(If[ #[[0]]===Integer,,error])&,basis,{2}] , error ]; (* end check argument *) gram=basis.Transpose[basis]; If[MatrixQ[gram],,error] (* To catch "matrices" like {{}}, whose transpose is not a matrix. *) ); (* ----- Purpose of testgramm: Test if main argument of GramLatticeReduce is legal. (But positive semi definiteness will have to wait.) Remove denominators in gram matrix. ------- *) testgramm[m_]:= ( gramorg=gram=m; (* begin check argument *) error:=(Message[GramLatticeReduce::argrat,gram];Abort[]); If[MatrixQ[gram], Map[(If[ #[[0]]===Integer || #[[0]]===Rational,,error])&,gram,{2}] , error ]; If[Length[gram]!=Length[gram[[1]]],error]; If[Length[Union@@(gram-Transpose[gram])]>1,error]; (* end check argument *) If[Count[gram,_Rational,{2}]>0, gram=LCM@@(Union@@(Map[Denominator,gram,{2}]))gram ]; ); (* ----- Purpose of integerbody: Body of integer arithmetic algorithm. ------- *) integerbody:= ( For[init; , moreratios , moreratios=(LLLratiolist=!={}) , For[setratio,!finished,,testLLLcondition]; (* main loop *) ]; post; ) (* ----- Purpose of init: Initializing for integer arithmetic. ------- *) init:=( n=Length[gram]; (* Number of vectors *) b=Hold@@(IdentityMatrix[n]); (* Will be the transformation matrix *) d=Hold@@(Range[n+1]); (* Will be a list of denominators *) la=Hold@@(Table[0,{i,n},{j,i-1}]); (* Will be list of numeratos *) (* The Hold wrapper is used here because Part does not have Attribute *) (* HoldFirst. We want to use Part frequently, without re-evaluating *) (* all entries each time. This starts to matter when n gets large. *) diso=d; (* Will also be a list of denominators *) rank=isodim=0;(* isodim is dimension of isotropic subspace *) LLLratiolist=ratiolist[LLLratio];(* The quality ratios. The default is 3/4. *) moreratios=True; kmax=0;(* Number of vectors for which tables are filled. *) k=1; (* The index of the vector on which attention is focussed *) incGS; ); (* ----- Purpose of ratiolist: Interface for the option RatioLLLCondition. ------- *) ratiolist[r_Rational]:={r}; ratiolist[{r__Rational}]:={r}; ratiolist[r_]:=(Message[RatioLLLCondition::argrat,r];Abort[]); (* ----- Purpose of setratio: Process the option RatioLLLCondition and help initializing. ------- *) setratio:=( LLLratio=First[LLLratiolist]; If[(1/4kmax , kmax=k; Do[coefficient[k,j] ,{j,isodim+1,k} ]; Do[coefficientiso[k,j] ,{j,isodim} ]; If[d[[k+1]]<=0,trickledown,rank++] ]; ); (* ----- Purpose of inner: The inner product described by the gram matrix. ------- *) inner[v_,w_]:=v.gram.w;(* used only in one place *) (* ----- Purpose of coefficientiso[k,j]: Computes la[[k,j]] or diso[[k+1]] assuming tables are up to date for smaller indices. It assumes j<=isodim, as it works with the euclidean inner product on the rows of the transformation matrix b. ------- *) coefficientiso[k_,j_]:= ( q=Dot[b[[k]],b[[j]]]; Do[q=(diso[[i+1]]q-la[[k,i]]la[[j,i]])~Quotient~diso[[i]],{i,j-1}]; If[jisodim, as it works with the inner product given by the gram matrix. ------- *) coefficient[k_,j_]:= ( q=inner[b[[k]],b[[j]]]; Do[q=(d[[i+1]]q-la[[k,i]]la[[j,i]])~Quotient~d[[i]],{i,isodim+1,j-1}]; If[jisodim+1 , LLLbranch[notLLLcond]; , LLLbranch[(k!=isodim+1)&¬LLLcondiso] ] ); (* ----- Purpose of LLLbranch[cond]: Depending on cond, take branch in LLL algorithm. ------- *) LLLbranch[cond_]:=(* given k *) If[cond , swap; k=Max[2,k-1] , For[l=k-2,l>0,l--,red[l]]; k++; If[k<=n,incGS,finished=True] ]; (* ----- Purpose of notLLLcond: Tell if the LLL condition is violated for the inner product given by the gram matrix. ----- Purpose of notLLLcondiso: Tell if the LLL condition is violated for the euclidean inner product on the rows of the transformation matrix b. ------- *) notLLLcond:=(* given k *) four*d[[k+1]]d[[k-1]]<(three*d[[k]]^2-four*la[[k,k-1]]^2); notLLLcondiso:=(* given k *) four*diso[[k+1]]diso[[k-1]]<(three*diso[[k]]^2-four*la[[k,k-1]]^2); (* ----- Purpose of red[l]: Subtract appropriate multiple of b[[l]] from b[[k]] and update table la accordingly. ------- *) red[l_]:=(* given k *) ( t=If[l>isodim,d[[l+1]],diso[[l+1]]]; If[Abs[2la[[k,l]]]>t , q=Quotient[2la[[k,l]]+t,2t]; b[[k]]=b[[k]]-q b[[l]]; la[[k,l]]=la[[k,l]]-q t; Do[la[[k,i]]=la[[k,i]]-q la[[l,i]];,{i,l-1}] ] ); (* ----- Purpose of swap: Swap b[[k-1]] with b[[k]] and update tables la, d, diso accordingly. ------- *) swap:=(* given k *) ( {b[[k]],b[[k-1]]}={b[[k-1]],b[[k]]}; Do[{la[[k-1,j]],la[[k,j]]}={la[[k,j]],la[[k-1,j]]},{j,k-2}]; q=la[[k,k-1]]; If[k>isodim , swaptail , swaptailiso ] ); (* ----- Purpose of swaptail: Finish updating tables la and d in a swap. ------- *) swaptail:=(* given k, q *) ( bbb=(d[[k-1]]d[[k+1]]+q^2)~Quotient~d[[k]]; Do[ t=la[[i,k]]; la[[i,k]]=(d[[k+1]]la[[i,k-1]]-q t)~Quotient~d[[k]]; la[[i,k-1]]=(bbb t+q la[[i,k]])~Quotient~d[[k+1]] ,{i,k+1,kmax} ]; d[[k]]=bbb ); (* ----- Purpose of swaptailiso: Finish updating tables la and diso in a swap. ------- *) swaptailiso:=(* given k, q *) ( bbb=(diso[[k-1]]diso[[k+1]]+q^2)~Quotient~diso[[k]]; Do[ t=la[[i,k]]; la[[i,k]]=(diso[[k+1]]la[[i,k-1]]-q t)~Quotient~diso[[k]]; la[[i,k-1]]=(bbb t+q la[[i,k]])~Quotient~diso[[k+1]] ,{i,k+1,kmax} ]; diso[[k]]=bbb ); (* ----- Purpose of matextendedgcd[x,y]: Compute the gcd and the matrix implicit in the euclidean algorithm. ------- *) matextendedgcd[x_,y_]:= ({gcd,{px,py}}=ExtendedGCD[x,y];{{-y~Quotient~gcd,x~Quotient~gcd},{px,py}}); (* ----- Purpose of trickledown: To compute another vector which is isotropic with respect to the inner product given by the gram matrix and to put it before the nonisotropic vectors. To adapt b, isodim, k, la, d, diso to the new situation. ------- *) trickledown:= ( (* *) If[d[[k+1]]<0,Message[GramLatticeReduce::neg,gramorg];Abort[]]; rat=Hold@@(Table[1,{kmax}]); (* Hold@@ is not essential, cf. comment below *) Do[ k--; mat=matextendedgcd[d[[k+1]],la[[k+1,k]]]; rat[[k+1]]=mat[[1,2]]; {b[[k]],b[[k+1]]}=mat.{b[[k]],b[[k+1]]}; Do[{la[[k,j]],la[[k+1,j]]}=mat.{la[[k,j]],la[[k+1,j]]},{j,k-1}]; For[l=k-1,l>0,l--,red[l]]; , {rank} ]; isodim++;(* "null space part" = isotropic part of b is increased *) rat=Rest[FoldList[Times,1,rat]]; (* cumulative volume ratios *) (* Only now do we update d and la *) Do[ d[[k+1]]=(d[[k]])~Quotient~(rat[[k]]^2); q=rat[[k]]rat[[k-1]]; Do[la[[j,k]]=Quotient[la[[j,k-1]],q],{j,k+1,kmax}]; , {k,kmax,isodim+1,-1} ]; d[[isodim+1]]=1; Do[coefficientiso[k,isodim],{k,isodim,kmax}]; Do[red[l],{k,isodim,kmax},{l,k-1,1,-1}]; k=Max[isodim,2] ); (* MORE OF THE INTERFACE *) ExtendedLatticeReduce[m__,{rules___},more___]:= ExtendedLatticeReduce[m,rules,more] ExtendedLatticeReduce[___]:=( Message[ExtendedLatticeReduce::argw,val];$Failed); GramLatticeReduce[m__,{rules___},more___]:= GramLatticeReduce[m,rules,more] GramLatticeReduce[___]:=( Message[GramLatticeReduce::argw];$Failed); (* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *) (* Error messages. *) ExtendedLatticeReduce::argint:= "Argument `1` of ExtendedLatticeReduce should be a matrix with integral entries." ExtendedLatticeReduce::argw:="ExtendedLatticeReduce called with wrong number of arguments. One argument is expected."; GramLatticeReduce::argrat:= "Argument `1` of GramLatticeReduce should be a symmetric matrix with rational entries." GramLatticeReduce::argw:="GramLatticeReduce called with wrong number of arguments. One argument is expected."; GramLatticeReduce::neg:="Gram matrix `1` has negative eigenvalue; it should be positive semi definite." RatioLLLCondition::argrat:= "Value `1` of RatioLLLCondition should be a rational number between 1/4 and 1, or a list of such numbers." (* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *) End[] Protect[ExtendedLatticeReduce,GramLatticeReduce,RatioLLLCondition]; EndPackage[] Null