numsolve.red 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. module numsolve; % root finding.
  2. % Author: H. Melenk, ZIB, Berlin
  3. % Copyright (c): ZIB Berlin 1991, all rights resrved
  4. fluid '(!*noequiv accuracy!* !*exptexpand);
  5. global '(iterations!* !*trnumeric erfg!*);
  6. symbolic procedure rdsolveeval u;
  7. % interface function;
  8. begin scalar e,vars,y,z,p,r,erfg,mode,all,!*exptexpand;
  9. integer n;
  10. erfg:= erfg!*; erfg!* := nil;
  11. u := for each x in u collect reval x;
  12. u:=accuracycontrol(u,6,50);
  13. if (all:='all memq u) then u:=delete('all,u);
  14. e :=car u; u :=cdr u;
  15. % construct the function vector.
  16. e:=if eqcar(e,'list) then cdr e else list e;
  17. e := for each f in e collect
  18. if eqexpr (f:=reval f) then !*eqn2a f else f;
  19. n := length e;
  20. % starting point & variables.
  21. if eqcar(car u,'list) then
  22. u := for each x in cdar u collect reval x;
  23. for each x in u do
  24. <<if eqcar(x,'equal) then
  25. <<z:=cadr x; y:=caddr x;
  26. if eqcar(y,'!*interval!*) then mode:='i;
  27. >> else <<z:=x; y:=random 100>>;
  28. vars:=z . vars; p := y . p>>;
  29. vars := reversip vars; p := reversip p;
  30. if not(n=length vars) then
  31. <<
  32. % lprim "numbers of variables and functions don't match;";
  33. % lprim "using steepest descent method instead of Newton";
  34. % return rdsolvestdeval list ('list.e,'list.u,
  35. % {'equal,'accuracy,accuracy!*},
  36. % {'equal,'iterations,iterations!*});
  37. rederr "numbers of variables and functions don't match"
  38. >>;
  39. if mode='i and length vars>1 or mode neq 'i and all then
  40. rederr "mode for num_solve not implemented";
  41. r:=if mode='i then rd!-solve!-interv(e,vars,p,n,all)
  42. else rdnewton0(e,vars,p,n);
  43. erfg!* := erfg;
  44. return r;
  45. end;
  46. put('num_solve,'psopfn,'rdsolveeval);
  47. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  48. %
  49. % finding root in an interval (secant method and related)
  50. %
  51. symbolic procedure rd!-solve!-interv(e,vars,p,n,all);
  52. (begin scalar u,fu,l,fl,x,acc,r,de,w,oldmode;
  53. n := nil;
  54. if length p>1 then typerr('list . p,"single interval");
  55. p:=car p; e:=car e; x:=car vars;
  56. w := {'boundseval,mkquote {e,{'equal,x,p}}};
  57. if not memq(dmode!*,'(!:rd!: !:cr!:))then
  58. <<oldmode:=t; setdmode('rounded,t)>>;
  59. if errorp errorset(w,nil,nil) then
  60. typerr(e,"bounded expression");
  61. acc := !:!:quotient(1,expt(10,accuracy!*));
  62. l:=evaluate(cadr p,nil,nil); u:=evaluate(caddr p,nil,nil);
  63. fl:=evaluateuni(e,x,l); fu:=evaluateuni(e,x,u);
  64. if not all then
  65. dm!:(if zerop fl then return prepf l else
  66. if zerop fu then return prepf u else
  67. if fl*fu<0 then
  68. r := regula!-falsi(e,x,l,fl,u,fu,acc));
  69. if null r then de := reval {'df,e,x};
  70. if null r and not all then
  71. r:=rd!-solve!-trynewton(e,de,x,l,fl,u,fu,acc);
  72. if null r then
  73. r:=rd!-solve!-hardcase(e,de,x,l,fl,u,fu,acc,all);
  74. if oldmode then setdmode('rounded,nil);
  75. if r then return r;
  76. rederr "not applicable";
  77. end) where !*roundbf=!*roundbf;
  78. symbolic procedure make!-rdsolve!-result1(x,u);
  79. 'list.for each r in u collect {'equal,x,prepf r};
  80. symbolic procedure rd!-solve!-trynewton(e,de,x,l,fl,u,fu,acc);
  81. begin scalar r,invde;
  82. invde := {'quotient,1,de};
  83. dm!: <<
  84. if fu*evaluateuni(de,x,u) >0 then
  85. r := rdnewton2({e},{{invde}},{x},acc,{u},'root,l,u);
  86. if null r and fl*evaluateuni(de,x,l) <0 then
  87. r := rdnewton2({e},{{invde}},{x},acc,{l},'root,l,u);
  88. if r and (r:=car r) and l <= r and r <= u then
  89. r := make!-rdsolve!-result1(x,{r}) else r:=nil;
  90. >>;
  91. return r;
  92. end;
  93. symbolic procedure regula!-falsi(e,x,l,fl,u,fu,acc);
  94. make!-rdsolve!-result1(x,
  95. {regula!-falsi1(e,x,l,fl,u,fu,acc,0,1)});
  96. symbolic procedure regula!-falsi1(e,x,l,fl,u,fu,acc,mode1,mode2);
  97. % modified regula falsi: using bisection in order to
  98. % traverse root as often as possible.
  99. dm!: begin scalar m,fm;
  100. if mode1=mode2 then m:=(u+l)/2
  101. else m := l*fu/(fu-fl) + u*fl/(fl-fu);
  102. if (u-l) < acc then return m;
  103. fm := evaluateuni(e,x,m);
  104. if zerop fm then return m;
  105. return if fl*fm<0
  106. then regula!-falsi1(e,x,l,fl,m,fm,acc,nil,mode1)
  107. else regula!-falsi1(e,x,m,fm,u,fu,acc,t,mode1);
  108. end;
  109. symbolic procedure mkminus u; {'minus,u};
  110. symbolic procedure evaluateuni(e,x,p);
  111. evaluate(e,{x},{p});
  112. symbolic procedure dmboundsuni(e,x,l,u);
  113. begin scalar i;
  114. i:=boundseval {e,{'equal,x,{'!*interval!*,prepf l,prepf u} }};
  115. return numr simp cadr i . numr simp caddr i;
  116. end;
  117. symbolic procedure rd!-solve!-hardcase(e,de,x,l,fl,u,fu,acc,all);
  118. dm!: begin scalar il1,il2,pt,iv,r,rr,b,b1,q,d;
  119. integer lev;
  120. il1:={(l.fl) .(u.fu)};
  121. main_loop:
  122. lev:=lev+1; il2:=nil;
  123. if null il1 then goto done;
  124. il1 := reverse il1;
  125. rd!-solve!-hardcase!-print(lev,il1);
  126. loop:
  127. if null il1 then goto bottom;
  128. iv :=car il1; il1:= cdr il1;
  129. l:=caar iv; fl:=cdar iv; u:=cadr iv; fu:=cddr iv;
  130. if (d:=u-l) <0 then goto loop; %empty
  131. if abs fl<acc then<<pt:=l;goto root_found>>;
  132. if abs fu<acc then<<pt:=u;goto root_found>>;
  133. b:=dmboundsuni(de,x,l,u);
  134. % left boundary of interval
  135. if (fl>0 and not((b1:=car b)<0))
  136. or(fl<0 and not((b1:=cdr b)>0))
  137. or not((pt:=l-fl/b1)<u) then goto loop;
  138. if pt=l then goto precerr;
  139. q:=evaluateuni(e,x,pt);
  140. if not all and q*fl<0 then return regula!-falsi(e,x,l,fl,pt,q,acc);
  141. if abs q<acc then goto root_found;
  142. l:=pt; fl:=q;
  143. % right boundary of interval
  144. if (fu>0 and not((b1:=cdr b)>0))
  145. or(fu<0 and not((b1:=car b)<0))
  146. or not((pt:=u-fu/b1)>l) then goto loop;
  147. if pt=u then goto precerr;
  148. q:=evaluateuni(e,x,pt);
  149. if not all and q*fu<0 then return regula!-falsi(e,x,pt,q,u,fu,acc);
  150. if abs q<acc then goto root_found;
  151. u:=pt; fu:=q;
  152. % new point
  153. pt :=(l+u)/2; %midpoint
  154. q:=evaluateuni(e,x,pt);
  155. if not all and q*fu<0 then return regula!-falsi(e,x,l,fl,pt,q,acc);
  156. % generate new intervals
  157. if not(abs q<acc) then
  158. <<il2 :=find!-root!-addinterval(pt.q,u.fu,
  159. find!-root!-addinterval(l.fl,pt.q,il2));
  160. goto loop;
  161. >>;
  162. root_found:
  163. r:=pt.r; if not all then goto done;
  164. rr:=find!-root!-range(e,x,pt,acc);
  165. il2 :=find!-root!-addinterval(cdr rr,u.fu ,
  166. find!-root!-addinterval(l.fl,car rr,il2));
  167. goto loop;
  168. bottom:
  169. il1 :=il2;
  170. goto main_loop;
  171. done:
  172. r:=sort(r,function lessp);
  173. return make!-rdsolve!-result1(x,r);
  174. precerr:
  175. rederr "precision not sufficient for finding all roots";
  176. end;
  177. symbolic procedure rd!-solve!-hardcase!-print(lev,il1);
  178. if !*trnumeric then
  179. << prin2t {"recursion level:",lev,"remaining intervals:",length il1};
  180. writepri( mkquote( 'list.
  181. for each i in il1 collect
  182. {'list,{'!*interval!*,prepf caar i,prepf cadr i},
  183. dm!: prepf(cadr i-caar i)}),
  184. 'only);
  185. >>;
  186. symbolic procedure find!-root!-addinterval(l,h,il);
  187. dm!: <<if car l < car h then il:=(l.h).il; il>>;
  188. symbolic procedure find!-root!-range(e,x,p,acc);
  189. % p is a point in (l .. u) where abs e(x)<acc.
  190. % Find the next interval where e(x) > acc.
  191. dm!:<<
  192. while not(abs(fl:=evaluateuni(e,x,pl:=pl-acc/2))>acc) do;
  193. while not(abs(fu:=evaluateuni(e,x,pu:=pu+acc/2))>acc) do;
  194. (pl.fl).(pu.fu)
  195. >> where pl=p,pu=p,fl=nil,fu:=nil;
  196. if errorp errorset('(!:difference nil nil),nil,nil) then
  197. <<
  198. symbolic procedure !:difference(u,v);
  199. if null u then !:minus v else if null v then u
  200. else if u=v then nil
  201. else if atom u and atom v then u-v
  202. else dcombine(u,v,'difference);
  203. symbolic procedure !:quotient(u,v);
  204. if null u or u=0 then nil
  205. else if null v or v=0 then rerror(poly,12,"Zero divisor")
  206. else if atom u and atom v
  207. % We might also check that remainder is zero in integer case.
  208. then if null dmode!* then u/v else dcombine(u,!:recip v,'times)
  209. else dcombine(u,v,'quotient);
  210. >>;
  211. endmodule;
  212. end;