Given a set of real numbers an integer relation is a set of integers such that
The PSLQ algorithm constructs such a set of numbers by creating a sequence of integer-valued matrices for which
for which at least on component of becomes zero to within some acceptable tolerance. If for some then the column of is our integer vector . The collapse of elements of is rather abrupt, so one can be confident that it signals an actual relation.
Where do integer relations occur?
Trig identities. Let be arbitrary, and construct
These obey an integer relation with .
Algebraic numbers. The real number has a minimal polynomial. Let
then .\\
Establishing formulas. Construct
and let
In 1996 Simon Plouffe used the PSFQ algorithm to discover
which lets you compute any individual hexadecimal digit of without computing previous digits.
Statistical mechanics (arXiv:cond-mat/9811264v3) . Nearest neighbor correlation functions of the hard-squares model exhibit integer relations
and where is a lattice spin, is the spin adjacent to it on the right, is adjacent to on the left, is adjacent below, is adjacent above.
There are simple algorithms such as Brun’s for finding integer relations, but the PSLQ algorithm is superior in every way, since it also gives you a limit on the size of a relation if one exists at all.
Brun’s algorithm is basically the Euclidean; let , and let where is a unit vector with all zero components except for the . Let be the permutation that sorts into descending order
Apply this to ;
Compute where denotes rounding down, and redefine
and repeat the process with a new sorting permutation. What this does is applies integer relation after integer relation (keeping track of them) until , then the final is the desired final integer relation.
; common lisp ; Brun's method for integer relations ; Make sure you use double-floats! (defun Brun (lst iters) (setf foo (copy-list lst)) (setf n (1- (length foo))) (setf b (loop for i from 0 to n collect (loop for j from 0 to n collect 0))) (loop for i from 0 to n do (setf (nth i (nth i b)) 1)) (loop for l from 1 to iters do ;; sort can destroy your list (setf foo2 (sort (copy-list foo) #'>)) (setf y (loop for x in foo2 collect (position x foo :test #'equal))) (setf bn (loop for x in y collect (nth x b))) (rotatef foo foo2) (rotatef b bn) (setf q (truncate (/ (nth 0 foo) (nth 1 foo)))) (rplaca foo (- (nth 0 foo) (* q (nth 1 foo)))) (rplaca b (mapcar #'- (nth 0 b) (mapcar #'(lambda(x) (* x q)) (nth 1 b)))) ) ;; report final list and integer relation (list foo b)) ; Try it... but use double-floats (setf xx (list (cos 0.3d0) (cos 0.9d0) (cos 1.5d0) (expt (cos 0.3d0) 5))) (Brun xx 9) ;; Reports ((2.1221132479043447d-7 3.9446742616799924d-7 6.493240857496918d-8 ;; 5.412337245047638d-16) ;; ((-114 -12361 1324 9675) (23 2459 -264 -1925) (-73 -2833 311 2273) ;; (10 5 1 -16))) ;; Note 10 cos(x)+5 cos(3x)+cos(5x)-16 cos^5(x)=0
Brun’s algorithm is very sensitive to precision limitations, it may be easy to code (write it in your favorite language but use mpfr or Rmpfr) but the resulting program is not much use if the input precision is not at least long double.
You don’t like lisp? Here’s the code in julia. Note that 9 iterations gives the correct integer relation in lisp, but we need 30 in julia for the same input vector, because of lisp’s superior floating point handling.
brun=function(x,iters) X=copy(x) n=size(x)[1] b =zeros(Float64,n,n) for k in 1:n b[k,k]=1.0 end for j in 1:iters p=sortperm(X,rev=true) P=zeros(Float64,n,n) for j in 1:n P[j,p[j]]=1.0 end X=P*X b=P*b r=fld(X[1],X[2]) X[1]=X[1] .- r*X[2] b[1,:]=b[1,:] .- r*b[2,:] end b[n,:], sum(b[n,:] .*x) end v=[cos(3//10),cos(9//10),cos(15//10),cos(3//10)^5] # needs 30 iterations brun(v,30) ([10.0, 5.0, 1.0, -16.0], 1.7763568394002505e-15)