;; Example of Dixon-EDF. ;; See: Heuristics to Accelerate the Dixon Resultant, Mathematics and Computers in Simulation 77, Issue 4, p. 400-407, April 2008. ;; This example is a system of equations that arises in considering flexibility of quadilaterals, due to Bricard (1896). ;; USE AT YOUR OWN RISK. no warranty. ;; Robert H. Lewis, August 2008 ;; Note added Jan 2, 2012: These procedures may fail if you are working over a quotient field over Z. (This is called "polymodding".) The problem, noted in the manual, ;; is that g = GCD(a,b) may not divide into a or b, due to integer coefficients. In that case, the statement SDivides(g, a) will fail and set a to 1. ;; To illustrate, try some examples when molding out x^2 - 3. (There is no problem when polymodding over Z/p.) &(e = 1); &(m = 1); &(a = 1); &(D = 10); &(t = 1); &_l; ;; adjoin the symbolic variables. parameters first. &(J = b1); &(J = c1); &(J = b2); &(J = c2); &(J = b3); &(J = c3); &(J = a1); &(J = d1); &(J = e1); &(J = a2); &(J = d2); &(J = e2); &(J = a3); &(J = d3); &(J = e3); &(J = t3); &(J = t2); &(J = t1); ;; Dixon is not a builtin command in Fermat. These functions implement it. ;; To use this file of Fermat functions to run Dixon: ;; - start Fermat, read this file with &r. All the functions and arrays will be loaded. ;; Reading stops at the &x command (see manual). ![d will display the array [d], holding the equations. ;; - Type &r again. That will resume reading, executing the function Ma, which invokes Dixon ;; on the array [d], containing the three equations. Since there are three equations, the ;; two highest variables (last added above; i.e. t1 and t2) will be eliminated. Ma stops with ;; the creation of the matrix [m4]. It will be 8x8. ;; - The resultant is a factor of Det[m4]. Type s := Det[m4]: The determinant will be ;; computed by minors because 8 <= 10, and the command &(D = 10) was invoked above. ;; - When you have s, invoke Sqfree(s, [a]) to compute the square-free factors and put them in ;; a newly created array [a]. [a] will be 3x2. The three factors are a[1], a[2], a[3] (i.e, ;; a[1,1], a[2,1], a[3,1]. Fermat stores array in column-major order. You may access any ;; array as if it were linear. So a[4] is the same as a[1,2].), and their exponents are ;; a[4], a[5], a[6]. You should find the desired answer in a[2], with 5685 terms. Note ;; that this does not prove that a[2] is irreducible. ;; - Alternatively, you may type Fin(1,8), then DC. This invokes the function Fin ("finish"), ;; which implements an idea I've developed called EDF to find the resultant without computing the ;; entire determinant of [m4]. When it finishes, the array [num] will contain nn factors of ;; the determinant; usually one of them is the resultant. Here we get nn=10, and num[nn] ;; is the poly of 5685 terms. The product Prod[ num[i] ] is the so called ;; "spurious factor." ;; There are more subtleties than that! Explore! ;; For example, instead of just running Fin(1,n) where m4 is nxn, consider this: ;; for i = 1, n do Fin(i,i); DC; Pre; KD; DC od ;; Main is not as efficient as Mai. Function Main(dd,i,j,n) = { Set up the Dixon method with the entries in array [dd].} n := Deg[dd] - 1; { adjoin n auxiliary variables. Changed May 1998. designed for n=2.} Array nam[2]; nam[1] := 't'; for i = 1, n do nam[2] := i + 96; &(J = [nam]) od; Dix([dd]); { We now have the first Dixon matrix, [m]. } dm := Numer(Det[m]); for i = 1 to n do SDivide(Var(i) - Var(i + n), dm) od; { Now distribute the coefficients of dm to the second matrix, [m1].} Set2(n); { [m1] now exists. Next, create arrays [rowlst] and [colst].} Extract; { Now create the third matrix, [m2], by pulling out the nonzero rows and cols using [rowlst] and [colst]. } Array m2[Deg[rowlst], Deg[collst]]; Mynors([m1], [rowlst], [collst], [m2]); { Cancel stuff } for i = 1, n do nam[2] := i + 96; &(J = -[nam]) od; @(dm, [rowlst], [collst], [nam], [collist], [rowlist], [m1]).; ;; Mai is not as efficient as Ma. Function Mai(dd,i,j,n) = { Set up the Dixon method with the entries in array [dd].} n := Deg[dd] - 1; { adjoin n auxiliary variables. Changed May 1998. designed for n=2.} Array nam[2]; nam[1] := 't'; for i = 1, n do nam[2] := i + 96; &(J = [nam]) od; Dix([dd]); { We now have the first Dixon matrix, [m]. } dm := DMin([m], n); { Now distribute the coefficients of dm to the second matrix, [m1].} Set2(n); { [m1] now exists. Next, create arrays [rowlst] and [colst].} Extract; { Now create the third matrix, [m2], by pulling out the nonzero rows and cols using [rowlst] and [colst]. } Minors([m1], [rowlst], [collst], [m2]); { Cancel stuff } for i = 1, n do nam[2] := i + 96; &(J = -[nam]) od; @(dm, [rowlst], [collst], [nam], [collist], [rowlist], [m1]).; ;; this is the best main driver to run Dixon. Function Ma(dd,n,i,j) = { Set up the Dixon method with the entries in array [dd].} n := Deg[dd] - 1; { adjoin n auxiliary variables. Changed May 1998. designed for n=2.} Array nam[2]; nam[1] := 't'; for i = 1, n do nam[2] := i + 96; &(J = [nam]) od; Dix([dd]); [u] := [m]; for i = n+1, 2, -1 do [u[i,]] := [u[i,]] - [m[i-1,]]; for j = 1, n+1 do SDivide(Var(i-1) - Var(i-1 + n), u[i,j]) od od; Sparse[u]; dm := Det[u]; !!(Terms(^dm)); { Now distribute the coefficients of dm to the second matrix, [m1]. Call Setup or Set2. } Setup(n); { [m1] now exists. Next, create arrays [rowlst] and [colst].} Extract; { Now create the third matrix, [m2], by pulling out the nonzero rows and cols using [rowlst] and [colst]. } Minors([m1], [rowlst], [collst], [m2]); { Cancel stuff } for i = 1, n do nam[2] := i + 96; &(J = -[nam]) od; @(dm, [rowlst], [collst], [nam], [collist], [rowlist], [m1]). ; Function NotzerR(r,j) = for j = 1, col do if m1[r,j] <> 0 then Return(1) fi od; Return(0).; Function SNotzerR(r,j) = for j = [m1]r do Return(1) od; Return(0).; Function Mynors(matrix1,xx,yy,matrix2,i,j,cols) = cols := Deg[yy]; for i from 1 to Deg[xx] do for j from 1 to cols do matrix2[i,j] := matrix1[xx[i],yy[j]] od od.; Function DMin(m, s; n, i, j, p, sum) = { recursive det by minors with dividing known factors. Aug 2002 } { s is the number of vars being eliminated } n := Cols[m]; p := Var(s-n+2) - Var(2*s-n+2); if n = 2 then (m[1,1]*m[2,2] - m[1,2]*m[2,1])\p else Array r[n-1], c[n-1], next[n-1,n-1]; for j = 2,n do r[j-1] := j od; sum := 0; for i= 1, n do for j=1,i-1 do c[j] := j od; for j=i+1, n do c[j-1] := j od; Mynors([m], [r], [c], [next]); if n = s+1 then !!i fi; sum := sum + (-1)^(i-1)*m[1,i]*(DMin([next],s)\p) od; @([r], [c], [next]); sum fi.; ;; Use this one when [m4] is very large and dense. else use Setup. Function Set2(n,time1,time,i,j,k,za,rc,cc) = { n is the number of variables. Create and fill the array [m1]. Aug 2002. } !!' Starting Setup2 Function '; time1 := &T; Array deg[2n]; for i = 1 to 2n do deg[i] := Deg(^dm, i) od; ![deg; !; rows := Prod < i = 1, n > [deg[i]+1]; cols := Prod < i = n + 1, 2n > [deg[i]+1]; Array m1[rows,cols] Sparse; Array prodexp[2n]; for i = 1, n do prodexp[i] := Prod[ deg[j]+1 ] od; for i = n+1, 2n do prodexp[i] := Prod[ deg[j]+1 ] od; Mons(dm, [mon]); { place each monomial in dm in the right spot in [m1] } for i = 1 to Deg[mon] do za := mon[i]; rc := 1 + Sigma [ Deg(za,Var(j))*prodexp[j] ]; cc := 1 + Sigma [ Deg(za,Var(j))*prodexp[j] ]; for k=1,2n do za := za#(Var(k)=1) od; m1[rc,cc] := m1[rc,cc] + za od; @[deg]; !!' Done Setup Function '.; Function LastOK(n,i) = for i from 1 to 12 do if x[n,i] = 0 then Return(0) fi od; Return(1).; Function Cont(xx,n,yy) = yy := xx#(Var(n) = Var(1)); Return(Content(yy)).; Function M(m,k,i,j,n,g,xx,fg,ok) = { improved May 6, 2002, sparse version Aug 2002 } n := Cols[m]; for i = k, n do if i = k then g := 0 fi; { else use previous g. It is usually correct! } fg := 1; for j = [m]i do g := GCD(g, Numer(m[i,j])); if Equal(g, one) then & > fi od; if i > k and g <> 1 then for j = [m]i do xx := Denom(m[i,j]); Killden(m[i,j]); SDivide(g, m[i,j]); { use new // command } m[i,j] := *m[i,j]//*xx od fi; if i > k then { above g might not be whole answer } fg := g; g := 0; for j = [m]i do g := GCD(g, Numer(m[i,j])); if Equal(g, one) then & > fi od else { fg means first g } fg := 1 fi; j := Terms(^g) + Terms(fg) - 1; if g <> 1 and g <> 0 then for j = [m]i do xx := Denom(m[i,j]); Killden(m[i,j]); SDivide(g, m[i,j]); m[i,j] := *m[i,j]//*xx od fi; g := g *fg; if g <> 1 and g <> 0 then nn :+; num[nn] := g fi; { NOW multiply through by lcm[i]! } if lcm[i] <> 1 then for j = [m]i do xx := lcm[i]; SDivide(Denom(m[i,j]), xx); Killden(m[i,j]); { use space saving command, * } m[i,j] := *m[i,j]**xx od fi od.; Function Fill(i,j,k) = k := Cols[m4]; for i = 1, k do for j = 1, k do m4[i,j] := m2[c[1,i],c[2,j]] od od.; Function M3(m,k,i,j,n,h) = { improved May 3, 2002, sparse version Aug 2002 } n := Cols[m]; for i = k, n do lcm[i] := 1; for j = [m]i do SDivide(GCD(lcm[i], Denom(m[i,j])), lcm[i]); { use space saving command, * } lcm[i] := *lcm[i]*Denom(m[i,j]) od; { Do not multiply the row by lcm[i]! M will. } if lcm[i] <> 1 then cnt :+; den[cnt] := lcm[i] fi od.; Function NN(i) = for i = nn, 1, - 1 do if num[i] or num[i] = - 1 then num[i] := - 1; nn:- else &> fi od.; Function Doub(m,i,dim) = { assume dim is even } dim := Deg[m]; !!'Doing Doub'; while i < dim do i:+; m[i] := *m[i]**m[i+1]; m[i+1] := 1; i:+ od; Con([m]).; Function Dix(dd,i,j,k,n,p) = { Changed May 1998 } n := Deg[dd]; Array m[n,n]; for i from 1 to n do for j = 1, n do if i = 1 then m[i,j] := dd[j] else k := i + n - 2; m[i,j] := m[i-1,j]#(Var(k) = Var(i - 1)) fi od od.; Function Disp3(n,m,i,j) = for i = n, m do for j = n, m do if Equal(m4[i,j], zer) then !' 0' else !(Terms(^m4[i,j])) fi od; ! od.; Function Fin(m,n,i,j,h) = M3([m4],1); M([m4],1); STrans[m4]; M3([m4],1); M([m4],1); STrans[m4]; for i = m, n do Pseudet([m4], , i); !!(i, ' ', nn, ' ', cnt); M3([m4], i); M([m4], i); for j = 11*n\12 + 1, n do if Terms(lcm[j]) > 5 then !!Terms(lcm[j]) fi od; if i > 50 and i|3 then NN; h := Con([den]); cnt := cnt - h; if h > 0 then !!('first cnt now ', cnt, ' ', h) fi fi; PDenom; !!'end PDenom'; PDenom2; if i|10 then DoCon; !!'did DoCon'; if nn > 5000 then Doub([num]) fi; if cnt > 5000 then Doub([den]) fi fi; !!('****** finished ', i) od.; Function Disp(n,i,j) = for i = 1, n do for j = 1, n do if Equal(m4[i,j], zer) then !'0 ' else !'* ' fi od; ! od.; Function Con(m,s,t,c,dim) = { Consolidate, remove 1s. careful not to count a 1 twice!! } { Hence the trick of writing -1 instead of 1 } dim := Deg[m]; while True do while True do s:+; if s > dim then Return(c) else if m[s] = 0 then Return(c) fi fi; if m[s] or m[s] = - 1 then & > fi od; t := s; while True do t:+; if t > dim then Return(c) else if m[t] = 0 then Return(c) fi fi; if m[t] <> 1 and m[t] <> - 1 then & > fi od; if m[s] then c:+ fi; m[s] := m[t]; m[t] := - 1 od.; Function PDenom(i,j) = { improved April 3, 2002 } for i = 1, cnt do if not Equal(den[i], one) then for j = 1, nn do if Divides(den[i], num[j]) then SDivide(den[i], num[j]); den[i] := 1; & > fi od fi od.; Function DoCon(h,i) = { May 22, 2002 } h := Con([den]); cnt := cnt - h; if h > 0 then !!('cnt shrunk by ', h) fi; h := Con([num]); if h > 0 then !!('nn shrunk by ', h) fi; nn := nn - h; if nn > 0 then while num[nn] or num[nn] = - 1 do nn:-; if nn = 0 then nn := 1; & > fi od fi; if cnt > 0 then while den[cnt] or den[cnt] = - 1 do cnt:-; if cnt = 0 then cnt := 1; & > fi od fi.; Function Elim(n,m,j,x) = j := 19 - n; x := - Coef(dd[m], Var(j), 0)/Coef(dd[m], Var(j), 1); [dd] := [dd]#(Var(j) = x); ![dd].; Function Erase = @([m1], [temp], [td], [cd], [val], [val2], [dd]); @(dm, cols, time1, rows, [deg], [rowlst], [e], [m], [collst]); @(ro, co, [collist], [rowlist]).; Function PDenom2(i,j,g) = { improved April 3, 2002 } for i = 1, cnt do if not Equal(den[i], one) then for j = 1, nn do g := GCD(den[i], num[j]); if not Equal(g, one) then SDivide(g, num[j]); SDivide(g, den[i]) fi; if Equal(den[i], one) then & > fi od fi od.; Function Next(ar,i,ss) = { ar is an array of n exponents. i is starting point in ar. ss indexes starting point in [deg]. Increment aa to the next sequence of exponents. } ar[i]:+; if ar[i] > deg[i+ss] then ar[i] := 0; if i < n then Next([ar], i + 1, ss) fi fi.; Function NotzerC(c,i) = for i = 1, row do if m1[i,c] <> 0 then Return(1) fi od; Return(0).; Function E(n,m,j,x) = j := 19 - n; x := - Coef(dd[m], Var(j), 0)/Coef(dd[m], Var(j), 1); [dd] := [dd]#(Var(j) = x).; Function SNotzerR2(r,j) = for j = [m1]r do Return(1) od; Return(0).; Function Extract(col,row,i,time1,time2,time) = { Uses array [m1]. Changed May, October 1998. August 2002 } !!' Starting Extract Function '; time1 := &T; col := Cols[m1]; row := Deg[m1]/col; Array rowlist[1]; Array collist[1]; [rowlist] := 0; [collist] := 0; Array temp[1]; for i = 1, row do if SNotzerR(i) then [temp] := i; [rowlist] := [rowlist]_[temp]; @<[rowlist]> fi od; STrans[m1]; for i = 1, col do if SNotzerR2(i) then [temp] := i; [collist] := [collist]_[temp]; @<[collist]> fi od; STrans[m1]; [rowlst] := [rowlist[1,2~]]; [collst] := [collist[1,2~]]; @<[rowlist]>; @<[collist]>; @[temp]; time2 := &T; !!' Done Extract Function '.; ;; probably better to use Set2 if [m4] is very large. Function Setup(n,time1,time,i,j,k,za,zb,mon) = { n is the number of variables. Create and fill the array [m1]. Changed May 1998. } !!' Starting Setup Function '; time1 := &T; Array deg[2n]; for i = 1 to 2n do deg[i] := Deg(^dm, i) od; ![deg; !; rows := Prod < i = 1, n > [deg[i]+1]; cols := Prod < i = n + 1, 2n > [deg[i]+1]; Array m1[rows,cols] Sparse; Array rowexp[n]; Array colexp[n]; [rowexp] := 0; for i = 1 to rows do mon := Prod < j = 1, n > [(Var(j))^rowexp[j]]; if i = 1 then za := dm else za := Mcoef(^dm, mon) fi; for j = 1 to n do if rowexp[j] = 0 then za := za#(Var(j) = 0) fi od; [colexp] := 0; for j = 1 to cols do mon := Prod < k = 1, n > [(Var(k+n))^colexp[k]]; if j = 1 then zb := za else zb := Mcoef(^za, mon) fi; for k = 1 to n do if colexp[k] = 0 then zb := zb#(Var(k + n) = 0) fi od; m1[i,j] := zb; Next([colexp], 1, n) od; Next([rowexp], 1, 0) od; @[rowexp]; @[colexp]; @[deg]; !!' Done Setup Function '.; ;; auxiliary display functions Func N(i) = for i=1,nn do !Terms(^num[i]); !' ' od.; Func D(i) = for i=1,cnt do !Terms(den[i]); !' ' od.; Func N2(i) = for i=1,nn do !!num[i]; ! od.; Func N3(i) = for i=1,nn-1 do !!num[i]; ! od.; Func D2(i) = for i=1,cnt-1 do !den[i]; !' ' od.; Func DC = DoCon(1,Cols[m4]).; Func D3 = Disp3(1, Cols[m4]).; Func P(i) = Prod [ num[i] ].; Func Pre = M3([m4],1); M([m4],1); STrans[m4]; M3([m4],1); M([m4],1); STrans[m4].; ;; use KN at the end to compactify the num list. Func KN(i,j,g) = for i=1,nn do for j=i+1,nn do g:=GCD(num[i],num[j]); if g<>1 then SDivide(g,num[j]); !!(i,j,g) fi od od.; Func KD(i,j,g) = for i=1,nn do for j=1,cnt do g:=GCD(num[i],den[j]); if g<>1 then SDivide(g,num[i]); SDivide(g,den[j]); !!(i,j,g) fi od od.; ;; instead of just running Fin(1,n) where m4 is nxn, consider this: ;; for i= 1, n do Fin(i,i); DC; Pre; KD; DC od nn:=0; cnt:=0; zer:=0; one:=1; ;; Here are the equations! Array d[3]; [d] := [[ a1*t1^2*t2^2 + b1*t1^2 + 2*c1*t1*t2 + d1*t2^2 + e1, a2*t2^2*t3^2 + b2*t2^2 + 2*c2*t2*t3 + d2*t3^2 + e2, a3*t1^2*t3^2 + b3*t1^2 + 2*c3*t1*t3 + d3*t3^2 + e3 ]]; ;; These arrays are used by the Fin method. Array lcm[1100],num[10000],den[10000]; &x; Ma([d]); ;; These commands extract a maximal minor from [m2], creating [m4]. This is the Kapur-Saxena-Yang idea. ;; We drop into the ring Z/44449 to save time. 44449 is a random 'large' prime. &(p=44449: *[m2]); [m3] := [m2]#(t3 = 5,11,-23, 17,29,-31, 37,-43,47, -7,53,59, 61,67,-71); Pseudet([m3],[c]); &(p=_i: *[m2], [m3],[c]); Array m4[Cols[c], Cols[c]] Sparse; Fill; ;; [m4] now exists. Its determinant is a multiple of the resultant. &x;