realroot.red 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. module realroot; % Routines for finding real roots of polynomials,
  2. % using Sturm series, together with iteration.
  3. % Author: Stanley L. Kameny <stan_kameny@rand.org>.
  4. % Version and Date: Mod 1.96, 30 March 1995.
  5. % Copyright (c) 1988,1989,1990,1991,1992,1993,1994,1995.
  6. % Stanley L. Kameny. All Rights Reserved.
  7. Comment modules bfauxil, bfdoer, bfdoer2, complxp, allroot and rootaux
  8. needed also;
  9. exports accupr1, bfnewton, isolatep, schinf, schplus, sgn1, sturm,
  10. sturm0, uniroots;
  11. imports !!mfefix, abs!:, accupr, accuroot, allroots, automod, bdstest,
  12. bfabs, bfdivide, bfeqp, bfleqp, bfloat, bfloatem, bfmax,
  13. bfminus, bfminusp, bfplus, bfrlmult, bfsgn, bfsqrt, bfzp,
  14. ceillog, ckpzro, cpxp, csep, difbf, divbf,
  15. domainp, dsply, eqcar, equal!:, errach, geq, getprec, gfdiff,
  16. gffinitr, gfgetmin, gfrl, gfrootfind, gfsqfrf, gfstorval,
  17. greaterp!:, lastpair, leq, lprim, minbnd1, minprec, mk!*sq,
  18. multroot,
  19. neq, nwterr, nwterrfx, outecho, pconstr, plubf, powerchk,
  20. r2bf, r2flbf, ratdif, ratleqp, ratlessp, ratmax, ratmean,
  21. ratmin, ratminus, ratplus, realrat, rerror, rl2gf, rlval,
  22. round!:mt, sch, schnok, setprec, sgn, stuffr, sturm1, timbf,
  23. trmsg1, trmsg10, trmsg2, trmsg3, trmsg4, trmsg6, trmsg7,
  24. trmsg8, xclp;
  25. global '(!!nfpd !!flim bfhalf!* max!-acc!-incr bfone!* rlval!#);
  26. fluid '(!*gfp !*xnlist !*intp tht!# !*strm lims!# mltr!# pfactor!#
  27. prec!# !*rvar acc!# !*xo !1rp accm!# !*xn intv!# sh!# rprec!# rlrt!#
  28. prm!# pfl!# acfl!# pgcd!#);
  29. fluid '(!*trroot !*bftag !*compxroots !*msg);
  30. flag('(positive negative infinity),'reserved);
  31. global '(limlst!# lm!#);
  32. limlst!# := '(positive negative infinity (minus infinity));
  33. symbolic procedure isolatep p;
  34. begin scalar n,q,zr,a,b,c,ril,va,vb,vc,v0,w,elem,l,u,i,j,lr,ur,
  35. xcli,xclj,ol,ou;
  36. if null sturm p or schinf(-1)-schinf 1=0 then go to ret;
  37. % limits +/-1.0001*maxbound p to give working room for rootfind.
  38. n := car(q := car !*strm);
  39. l := ratminus(u := realrat bfrlmult(1.0001,bfmax p));
  40. if (zr := l=u) and (lr := l) and not lims!# then go to zrt;
  41. if lims!# then
  42. <<i := car lims!#; if cdr lims!# then
  43. <<j := cadr lims!#; % both limits given.
  44. if i eq 'minfty then xcli := t else
  45. <<if xclp i then <<xcli := t; i := cdr i>>;
  46. l := ratmax(l,i)>>;
  47. if j eq 'infty then xclj := t else
  48. <<if xclp j then <<xclj := t; j := cdr j>>;
  49. u := ratmin(u,j)>>;
  50. if zr then if ratlessp(u,l) then go to ret
  51. else go to zrt;
  52. if sgn1(q,l)=0 then % root at l.
  53. <<ol := offsetr(n,l);
  54. if xcli then l := ratplus(l,ol) else lr := l>>;
  55. if l neq u and sgn1(q,u)=0 then % root at u.
  56. <<ou := offsetr(n,u);
  57. if xclj then u := ratdif(u,ou) else ur := u>> >>
  58. else if zr then go to ret else
  59. <<if sgn1(q,ol := realrat 0)=0 then ol := offsetr(n,ol);
  60. if i<0 then u := ratminus ol % negative roots.
  61. else l := ol>> >>; % positive roots.;
  62. n := (va := sch l+if lr then 1 else 0)-(vb := sch u);
  63. trmsg4(n);
  64. if n=0 then go to ret;
  65. if n=1 then ril := list list(l,u)
  66. else for j:=1:n do ril := nil . ril;
  67. v0 := vb+n-1;
  68. if lr then
  69. <<stuffrt(l,u,lr,ol,v0,va,vb,nil,ril);
  70. l := ratplus(lr,ol); va := va-1>>;
  71. if ur then
  72. <<stuffrt(l,u,ur,ou,v0,va,vb,nil,ril);
  73. u := ratdif(ur,ou); vb := vb+1>>;
  74. w := list list(l,u,va,vb);
  75. if n>1 then while w do
  76. <<elem := car w; w := cdr w; a := car elem; b := cadr elem;
  77. va := caddr elem; vb := cadddr elem; c := ratmean(a,b);
  78. if sgn1(q,c)=0 then % c is a root.
  79. w := stuffrt(a,b,c,offsetr(n,c),v0,va,vb,w,ril) else
  80. % root not found; stuff isolating interval and update work.
  81. <<vc := sch c;
  82. if va = vc+1 then <<stuffr(v0-vc,list(a,c),ril)>>;
  83. if va > vc+1 then w := list(a,c,va,vc) . w;
  84. if vb = vc-1 then <<stuffr(v0-vb,list(c,b),ril)>>;
  85. if vb < vc-1 then w := list(c,b,vc,vb) . w>> >>;
  86. ril := for each i in ril collect (car i) . cadr i;
  87. ret: return ril;
  88. zrt: return list (lr . lr) end;
  89. symbolic procedure stuffrt(a,b,c,m,v0,va,vb,w,ril);
  90. begin scalar vcm,vcp; % stuff root and update work.
  91. vcm := 1+(vcp := sch ratplus(c,m));
  92. stuffr(v0-vcp,list(c,c),ril);
  93. if va = vcm+1 then stuffr(v0-vcm,list(a,ratdif(c,m)),ril);
  94. if va > vcm+1 then w := list(a,ratdif(c,m),va,vcm) . w;
  95. if vb = vcp-1 then stuffr(v0-vb,list(ratplus(c,m),b),ril);
  96. if vb < vcp-1 then w := list(ratplus(c,m),b,vcp,vb) . w;
  97. return w end;
  98. symbolic procedure offsetr(n,r);
  99. realrat if n=1 then 1 else minbnd1(!*gfp,mk!*sq r);
  100. symbolic procedure sturm p;
  101. <<if cpxp (p := gffinitr p) then
  102. <<p := car csep p; if not atom p then p := bfloatem p>>;
  103. if not atom p then sturm1(!*gfp := p)>>;
  104. put('sturm,'psopfn,'sturm0);
  105. symbolic procedure sturm0 p;
  106. <<p := sturm ckprec car p; restorefl();
  107. 'list . for each a in p collect if atom a then a else 'list . a>>;
  108. symbolic procedure sgn1(p,r); if atom p then sgn p else
  109. % Evaluate sign of one sturm polynomial for rational r=(u . d)
  110. begin scalar m,c,u,d; u := car r; d := cdr r;
  111. c := 0; m := 1; p := cdr p;
  112. repeat <<c := m*car p + u*c; m := m*d>> until null(p := cdr p);
  113. return sgn c end;
  114. symbolic procedure r2flimbf x;
  115. if acc!#<=!!flim then r2flbf x else r2bf x;
  116. symbolic procedure rootfind(p,i);
  117. % finds real roots in either float or bigfloat modes.
  118. % p is in gfform. i is a pointer to a rational interval pair;
  119. begin scalar p1,p2,px,x1,x2,x3,x0,nx,xr,fg,n,s,sh;
  120. scalar xd,xe,qt,xnlist,pf,pf0; integer m,tht!#;
  121. n := caar lastpair p; !*xnlist := nil;
  122. if car i=cdr i then
  123. <<nx := r2flbf cdr i; go to lg4>>;
  124. xr := ratmean(car i,cdr i);
  125. if !*trroot then
  126. <<write "real root ",list(r2flimbf car i,r2flimbf cdr i);
  127. terpri()>>; trmsg8();
  128. if ratlessp(cdr i,car i) then
  129. errach "lx > hx ***error in roots package";
  130. movebds(i,xr,sh!# := sh := sgn1(!*intp,cdr i));
  131. p2 := gfdiff(p1 := gfdiff p);
  132. lag0: if bndtst (px := rlval(p,nx := r2flbf xr)) then go to tht
  133. else if bfzp px then go to lg4;
  134. lag: % check for proper slope at nx.
  135. if bndtst (x1 := rlval(p1,nx))
  136. or (s := bfsgn x1) neq sh then go to tht;
  137. % if lag not converging, go to newt.
  138. pf := bfabs px; if pf0 and bfleqp(pf0,pf) then go to newt;
  139. gfstorval(pf,nx); x1 := bfabs x1;
  140. if bndtst (x3 := rlval(p2,nx)) then go to tht;
  141. % bigfloat computations: is newton cheaper?
  142. if fg and
  143. <<qt := divbf(px,x1);
  144. xe := timbf(qt,timbf(qt,(divbf(x3,x1))));
  145. equal!:(nx,plubf(nx,timbf(bfhalf!*,xe)))>>
  146. then go to newt;
  147. % check whether laguerre iteration will work.
  148. x2 := difbf(bfrlmult(n-1.0,timbf(x1,x1)),
  149. bfrlmult(n,timbf(px,x3)));
  150. if bfminusp x2 then go to tht;
  151. % nx has met all tests, so continue.
  152. x0 := nx;
  153. xd := divbf(bfrlmult(-n*s,px),
  154. plubf(x1,bfsqrt(bfrlmult(n-1,x2))));
  155. nx := plubf(x0,xd);
  156. lg3: fg := t;
  157. if ratlessp(xr := realrat nx,car i) or ratlessp(cdr i,xr)
  158. then go to tht;
  159. if bndtst (px := rlval(p,nx)) then go to tht;
  160. movebds(i,xr,sh); trmsg2 ('lag,nx,px);
  161. if bdstest i then go to ret;
  162. if bfzp px then go to lg4;
  163. if bfeqp(nx,x0) then <<trmsg3('lag,nx); go to ret>>;
  164. if xnlist and member(nx,xnlist) then go to newt;
  165. xnlist := nx . xnlist; pf0 := pf;
  166. if(m := m+1)<10 or
  167. <<m := 0;
  168. equal!:(bfone!*,round!:mt(divbf(bfloat nx,bfloat x0),13))>>
  169. then go to lag;
  170. tht: nx := tighten(i,p,pf,sh); m := 0;
  171. if !*xnlist then
  172. <<pf0 := nil;
  173. movebds(i,xr := ratmean(car i,cdr i),sh); go to lag0>>;
  174. lg4: trmsg1('lag,nx);
  175. ret: !*xnlist := nil; if not nx then trmsg10 'lag; go to ret2;
  176. newt: nx := bfnewton(p,p1,gfgetmin(),i,4);
  177. ret2: !*xn := rl2gf nx; return nx end;
  178. global '(tentothetenth!*!*);
  179. tentothetenth!*!* := normbf i2bf!: 10000000000;
  180. symbolic procedure bndtst x;
  181. greaterp!: (abs!: x, tentothetenth!*!*);
  182. symbolic procedure movebds(i,xr,sh);
  183. if sgn1(!*intp,xr)=sh then rplacd(i,xr) else rplaca(i,xr);
  184. symbolic procedure tighten(i,p,pf,sh);
  185. begin scalar j,x0,nx,px,sn,x;
  186. nx := car i;
  187. tht0: j := 4;
  188. tht1: x0 := nx; nx := ratmean(car i,cdr i);
  189. if (sn := sgn1(!*intp,nx))=0 then
  190. <<x := r2flbf nx;trmsg1 ('tht,x); go to ret>>;
  191. if 0=car ratdif(nx,x0) then
  192. <<x := r2flbf nx;trmsg3 ('tht,x); go to ret>>;
  193. if sn=sh then rplacd(i,nx) else rplaca(i,nx);
  194. if (sn := bdstest i) then <<x := r2flbf sn; go to ret>>;
  195. if (j := j-1)>0 then go to tht1;
  196. if bndtst (px := rlval(p,x := r2flbf nx)) then
  197. <<j := 4; go to tht1>>;
  198. gfstorval(bfabs px,x);
  199. trmsg2('tht,x,px);
  200. if bfzp px then go to ret
  201. else if pf and bfleqp(pf,bfabs px) then go to tht0
  202. else return x;
  203. ret: !*xnlist := nil; return x end;
  204. symbolic procedure rtsreal(p,s);
  205. % Finds real roots of univariate square-free real polynomial p, using
  206. % sturm series, isolater and rootfind.
  207. begin scalar acr,acs,n,q,r,x,y,!*strm,pr,apr,!*bftag,pfl!#,
  208. acfl!#,xout,x1;
  209. integer accn,accn1,accm!#,prec!#,prm!#; pr := getprec();
  210. !*bftag := rlrt!# := t; pgcd!# := not s;
  211. r := isolatep p; % r is a list of rational number pairs.
  212. if null r then go to ret;
  213. if (n := caar lastpair p)>1 then go to gr1;
  214. y := rootrnd gfrl gfrootfind(p,nil);
  215. if pfactor!# then
  216. <<y := accupr1(y,p); y := (rootrnd car y) . cdr y>>;
  217. % note that rlval!# was set by the last operation of rootrnd.
  218. xout := {if s then (mkdn rlval!#) . acc!#
  219. else if pfactor!# then y
  220. else y . acc!# % this can't happen
  221. };
  222. if !*trroot then terpri(); go to ret;
  223. gr1: !*xo := rl2gf 0;
  224. q := r; acs := acc!#;
  225. lag: % increase accuracy for this root and the next root if current
  226. % accuracy is not sufficient for the interroot interval.
  227. if cdr q then % no test if this is the last real root.
  228. <<setprec acs;
  229. while schnok q do setprec (getprec()+1);
  230. accn1 := getprec()>>;
  231. acc!# := max(acs,accn,accn1);
  232. accn := if accn1>acs then accn1 else 0;
  233. setprec max(rprec!#,acc!#+2);
  234. y := rootfind(p,intv!# := car q); apr := t;
  235. if null y then rerror(roots,8,"Realroots abort");
  236. acc: y := accuroot(gfrl !*xn,p,!*xo);
  237. % if acc!# is insufficient for this root, for any reason,
  238. % increase accuracy and tighten.
  239. if apr then
  240. <<if (acr := accupr(p,!1rp,!*xn))>acc!# then acc!# := acr
  241. else if acr<=acc!# then <<acc!# := acr; apr := nil>>;
  242. go to acc>>;
  243. xout := ((x1 := if s then mkdn rlval!# else y) . acc!#) . xout;
  244. % x is root list. Check for equal roots should fail!
  245. if x and x1=car x then rooterr x1;
  246. x := x1 . x;
  247. dsply y;
  248. acc!# := acs;
  249. if (q := cdr q) then <<accn1 := 0; go to lag>>;
  250. ret: setprec pr; return reverse xout end;
  251. symbolic procedure lval x; if xclp x then cdr x else x;
  252. symbolic procedure lpwr(l,m);
  253. if eqcar(l,'list) then 'list . lpwr(cdr l,m)
  254. else if atom l then l else ((car l)**m) . ((cdr l)**m);
  255. symbolic procedure schnok r;
  256. %true if precision is inadequate to separate two adjacent real roots.
  257. (l neq h and (sch l neq sch r2flbf2r l or sch h neq sch r2flbf2r h))
  258. where l=caar r,h=cdar r;
  259. symbolic procedure limchk x;
  260. <<!!mfefix();
  261. if null (x := for each y in x collect
  262. if member(y,limlst!#) then y
  263. else if eqcar(y := reval y,'list)
  264. then 'list . list limchk1 cadr y
  265. else limchk1 y) then nil
  266. else if x and not cdr x then
  267. if car x eq 'positive then list 1
  268. else if car x eq 'negative then list (-1) else limerr()
  269. else <<x := mkratl x; limchk2(car x,cadr x)>>>>;
  270. symbolic procedure limchk1 y;
  271. if errorp(y := errorset!*({'a2rat,mkquote y},nil))
  272. then rerror(roots,5,"Real root function limits must be real")
  273. else car y;
  274. symbolic procedure limchk2(a,b);
  275. <<if member(a,l) and member(b,l) then if a neq b then nil else limerr()
  276. else if member(a,limlst!#) then
  277. if member(b,limlst!#) then limerr() else limchk2(b,a)
  278. else if member(b,limlst!#) then
  279. if b eq 'negative then list('minfty,mkxcl a)
  280. else if b eq 'positive then list(mkxcl a,'infty)
  281. else if b eq 'infinity then list(a,'infty) else list('minfty,a)
  282. else if ratv b=ratv a and (xclp a or xclp b) then t
  283. else if ratlessp(ratv b,ratv a) then list(b,a) else list(a,b)>>
  284. where l = cddr limlst!#;
  285. symbolic procedure limerr;
  286. rerror(roots,6,"Illegal region specification");
  287. symbolic procedure ratv a; if xclp a then cdr a else a;
  288. symbolic procedure a2rat x;
  289. if numberp x then x . 1
  290. else if atom x then limerr()
  291. else if eqcar(x,'quotient) then
  292. ((if numberp n then n
  293. else if eqcar(n,'minus) then - cadr n
  294. else rerror(roots,10,"illegal limit")) where n=cadr x) . caddr x
  295. else if car x eq '!:rn!: then cdr x
  296. else
  297. ((if car x memq domainlist!* and y then cdr(apply1(y,x))
  298. else limerr())
  299. where y=get(car x,'!:rn!:));
  300. symbolic procedure rlrootno a;
  301. <<mltr!# := t; lims!# := limchk cdr a; a := ckprec car a;
  302. a := rlrtno2 if lims!#=t then 0 else a;
  303. restorefl(); mltr!# := nil; a>>;
  304. put('rlrootno,'psopfn,'rlrootno);
  305. symbolic procedure realroots a;
  306. <<lims!# := limchk cdr a;
  307. uniroots(if lims!#=t then 0 else car a,0)>>;
  308. put('realroots,'psopfn,'realroots);
  309. symbolic procedure isolater p;
  310. <<mltr!# := t; lims!# := limchk cdr p; p := ckprec car p;
  311. p := isolatep if lims!#=t then 0 else p;
  312. restorefl(); mltr!# := nil; outril p>>;
  313. put('isolater,'psopfn,'isolater);
  314. symbolic procedure mkratl l; for each a in l collect
  315. if member(a,limlst!#) then a else
  316. if eqcar(a,'list) then
  317. if member(a := cadr a,limlst!#) then a else mkxcl a
  318. else a;
  319. symbolic procedure exclude x; {'list, x};
  320. symbolic operator exclude;
  321. endmodule;
  322. end;