bounds.red 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. module bounds; % Upper and lower bound of a
  2. % multivariate functional expression in a n-dimensional interval.
  3. % Author: H. Melenk, ZIB, Berlin
  4. % Copyright (c): ZIB Berlin 1991, all rights resrved
  5. put('bounds,'psopfn,'boundseval);
  6. put('bounds!-rd,'psopfn,'boundsevalrd);
  7. put('bounds,'numericfn,'bounds!-rd);
  8. fluid '(boundsdb!* !*msg boundspolynomialdb!*
  9. !*boundsfullcheck boundsknown!* boundsvars!*);
  10. symbolic procedure boundsevalrd u;
  11. begin scalar dmode!*;
  12. setdmode('rounded,t);
  13. return boundseval u;
  14. end;
  15. symbolic procedure boundseval u;
  16. (begin scalar db,y,l,f, boundsvars!*;
  17. u := for each x in u collect reval x;
  18. f := car u; u := cdr u;
  19. if u and eqcar(car u,'list) then
  20. u := for each x in cdar u collect reval x;
  21. for each x in u do
  22. <<if not eqcar(x,'equal) then typerr(x,"domain description");
  23. y := revalnuminterval(caddr x,nil);
  24. l:=list(simp car y,simp cadr y);
  25. boundsvars!*:=append(boundsvars!*,{cadr x});
  26. db := (cadr x . minsq l . maxsq l).db>>;
  27. u := boundseval1(f,db);
  28. return mkinterval(prepsq car u,prepsq cdr u);
  29. end) where !*roundbf=!*roundbf;
  30. symbolic procedure boundserr(a,b);
  31. if not !*msg then error(99,'bounds) else
  32. if a then typerr(a,b) else rederr b;
  33. symbolic procedure boundseval0 (f,db,!*msg);
  34. % internal calling convention:
  35. % f algebraic expression,
  36. % db list of (x . lo . hi), where
  37. % lo and hi represent the current interval for
  38. % variable x as standard quotients.
  39. % result is a pair of standard quotients representing
  40. % the bounds for f.
  41. <<boundsvars!*:=for each x in db collect car x;
  42. boundseval1(f,db)>>;
  43. symbolic procedure boundseval1(u,boundsdb!*);
  44. <<if u member boundsknown!* then !*boundsfullcheck := nil else
  45. << !*boundsfullcheck := t;
  46. if dmode!* = '!:rd!: then boundsknown!* := u.boundsknown!*;
  47. >>;
  48. boundseval2 u>>;
  49. symbolic procedure boundseval2 u;
  50. % Main driver for bounds evaluation
  51. if adomainp u then <<u:=simp u;u.u>> else
  52. boundspolynomial u or
  53. begin scalar v,w,fcn;
  54. if (v:=assoc(u,boundsdb!*))then return cdr v;
  55. if idp u and (fcn:=get(u,dmode!*)) then
  56. <<v:=apply(fcn,nil); v:=(v ./1).(v ./1); goto done>>;
  57. if atom u then goto err;
  58. if car u='expt and fixp caddr u and caddr u>0 then
  59. <<v:=boundsexpt!-int(boundseval2 cadr u,caddr u);
  60. goto done>>;
  61. w := for each y in cdr u collect boundseval2 y;
  62. v:= if cdr w and cddr w and (fcn:=get(car u,'boundsevaln))
  63. then apply1(fcn,w)
  64. else if length w>2 then t
  65. else if (fcn:=get(car u,'boundseval1))
  66. then apply2(fcn,car u,car w)
  67. else if (fcn:=get(car u,'boundseval))
  68. then if null cdr w then apply1(fcn,car w)
  69. else apply2(fcn,car w,cadr w)
  70. else if cdr w then t
  71. else boundsoperator(car u,car w);
  72. if v=t then goto err2;
  73. if null v or
  74. not domainp numr car v or not domainp denr car v or
  75. not domainp numr cdr v or not domainp denr cdr v then goto err;
  76. % cache result for later usage.
  77. done:
  78. boundsdb!*:= (u.v).boundsdb!*;
  79. return v;
  80. err: boundserr(nil,"unbounded in range");
  81. err2: boundserr(nil,"bounds confusion");
  82. end;
  83. symbolic procedure boundsoperator(fcn,u);
  84. % general external interface: function flagged decreasing,
  85. % increasing of explicit bounds given.
  86. if flagp(fcn,'increasing) then boundsincreasingfn(fcn,u) else
  87. if flagp(fcn,'decreasing) then boundsdecreasingfn(fcn,u) else
  88. if get(fcn,'upperbound) and get(fcn,'lowerbound) then
  89. simp get(fcn,'lowerbound) . simp get(fcn,'upperbound)
  90. else nil;
  91. symbolic procedure boundsplus u;
  92. if null cdr u then car u else
  93. boundsplus2(car u,boundsplus cdr u);
  94. symbolic procedure boundsplus2(u,v);
  95. addsq(car u,car v) . addsq(cdr u,cdr v);
  96. put('plus,'boundsevaln,'boundsplus);
  97. put('plus,'boundseval,'boundsplus2);
  98. symbolic procedure boundsdifference(x,y);
  99. subtrsq(car x,cdr y).subtrsq(cdr x,car y);
  100. put('difference,'boundseval,'boundsdifference);
  101. symbolic procedure boundsminus(u);
  102. negsq cdr u . negsq car u;
  103. put('minus,'boundseval,'boundsminus);
  104. symbolic procedure boundstimes u;
  105. if null cdr u then car u else
  106. boundstimes2(car u,boundstimes cdr u);
  107. symbolic procedure boundstimes2(x,y);
  108. begin scalar z;
  109. % first handle simple cases
  110. if not minusf numr car x and not minusf numr car y then
  111. return multsq(car x,car y) . multsq(cdr x,cdr y);
  112. if minusf numr cdr x and minusf numr cdr y then
  113. return multsq(cdr x,cdr y) . multsq(car x,car y);
  114. z:=list(multsq(car x,car y), multsq(car x,cdr y),
  115. multsq(cdr x,car y), multsq(cdr x,cdr y));
  116. return minsq z . maxsq z;
  117. end;
  118. symbolic procedure minsq l;
  119. begin scalar x;
  120. x := car l;
  121. for each y in cdr l do
  122. if minusf numr subtrsq(y,x) then x:=y;
  123. return x;
  124. end;
  125. symbolic procedure maxsq l;
  126. begin scalar x;
  127. x:= car l;
  128. for each y in cdr l do
  129. if minusf numr subtrsq(x,y) then x:=y;
  130. return x;
  131. end;
  132. symbolic procedure sqgreaterp(x,y);
  133. minusf numr subtrsq(y,x);
  134. put('times,'boundsevaln,'boundstimes);
  135. put('times,'boundseval,'boundstimes2);
  136. symbolic procedure boundsexp u;
  137. boundsexpt(b . b,u) where b = simp 'e;
  138. put('exp,'boundseval,'boundsexp);
  139. symbolic procedure boundsexpt!-int(b,ex);
  140. % Form x ** n. Look for even exponents.
  141. begin scalar hi,lo,bh,bl;
  142. bl := exptsq(car b,ex); bh := exptsq(cdr b,ex);
  143. if not evenp ex then return bl.bh;
  144. lo := minusf numr cdr b;
  145. hi := not minusf numr car b;
  146. return
  147. if hi then bl.bh else % both had been positive
  148. if lo then bh.bl else % both had been negative: invert
  149. (nil ./ 1) . maxsq list(bl,bh);
  150. end;
  151. symbolic procedure boundsexpt!-const(b,ex);
  152. % Form x ** constant, including fractional exponents.
  153. begin scalar l,n,m;
  154. n := '(nil . 1);
  155. if sqgreaterp(n,ex) then
  156. return boundsquotient('(1 . 1),boundsexpt!-const(b,negsq ex));
  157. if denr ex = 1 and
  158. (fixp(m:=numr ex)
  159. or eqcar(m,'!:rd!:)
  160. and null addf(numr ex,negf(m := reval{'round,m})))
  161. then return boundsexpt!-int(b,m);
  162. if sqgreaterp(n,car b) then
  163. boundserr(nil,"fractional exponent with negative base");
  164. l := list(bounds!-evalfcn2('expt,car b,ex),
  165. bounds!-evalfcn2('expt,cdr b,ex));
  166. return minsq l . maxsq l;
  167. end;
  168. symbolic procedure boundsexpt(b,e);
  169. if car e=cdr e then boundsexpt!-const(b,car e) else
  170. % Form x ** y. x>0 only.
  171. % Either monotonous growing or falling: pick extremal points.
  172. begin scalar l,n;
  173. n:='(nil . 1);
  174. if sqgreaterp(n,car b) then return nil;
  175. l := list(bounds!-evalfcn2('expt,car b,car e),
  176. bounds!-evalfcn2('expt,car b,cdr e),
  177. bounds!-evalfcn2('expt,cdr b,car e),
  178. bounds!-evalfcn2('expt,cdr b,cdr e));
  179. return minsq l . maxsq l;
  180. end;
  181. symbolic procedure boundsprepsq u; prepsq car u . prepsq cdr u;
  182. put('expt,'boundseval,'boundsexpt);
  183. symbolic procedure boundsquotient(n,d);
  184. begin scalar l;
  185. if boundszero d then boundserr(nil,"unbounded in range");
  186. l := list(quotsq(car n,car d),quotsq(car n,cdr d),
  187. quotsq(cdr n,car d),quotsq(cdr n,cdr d));
  188. return minsq l . maxsq l;
  189. end;
  190. symbolic procedure boundszero u;
  191. % test: 0 in interval u.
  192. not(minusf numr car u = minusf numr cdr u) or
  193. boundszero1 numr car u or boundszero1 numr cdr u;
  194. symbolic procedure boundszero1 f;
  195. % test standard form f for zero.
  196. null f or zerop f or pairp f and apply(get(car f,'zerop),{f});
  197. put('quotient,'boundseval,'boundsquotient);
  198. symbolic procedure boundsabs u;
  199. if evalgreaterp(prepsq car u,0) then u else
  200. if evalgreaterp(0,prepsq cdr u) then negsq cdr u . negsq car u else
  201. (nil ./1) . maxsq list (negsq car u,cdr u);
  202. put('abs,'boundseval,'boundsabs);
  203. symbolic procedure boundsincreasingfn(fn,u);
  204. % Bounds for an increasing function. Out-of -range problems will
  205. % be detected either by the function evaluation or finally by
  206. % the main driver if the result is not an interval with numeric
  207. % bounds.
  208. bounds!-evalfcn1(fn,car u) . bounds!-evalfcn1(fn,cdr u);
  209. put('log,'boundseval1,'boundsincreasingfn);
  210. put('asin,'boundseval1,'boundsincreasingfn);
  211. put('atan,'boundseval1,'boundsincreasingfn);
  212. put('sqrt,'boundseval1,'boundsincreasingfn);
  213. symbolic procedure boundsdecreasingfn(fn,u);
  214. boundsincreasingfn(fn,cdr u.car u);
  215. put('acos,'boundseval1,'boundsdecreasingfn);
  216. put('acot,'boundseval1,'boundsdecreasingfn);
  217. symbolic procedure boundssincos(fcn,n);
  218. % Reason if one of the turn points (n*pi) is in the
  219. % range. If yes, include the corresponding value 1 or -1.
  220. % Otherwise compute the range spanned by the end points.
  221. begin scalar lo,hi,!1pi,!2pi,!3pi,l,m;
  222. lo := car n; hi := cdr n;
  223. <<setdmode('rounded,t);
  224. !1pi := simp 'pi;
  225. setdmode('rounded,nil);
  226. >> where dmode!* =nil;
  227. if not domainp numr !1pi then goto trivial;
  228. !2pi := addsq(!1pi,!1pi); !3pi := addsq(!1pi,!2pi);
  229. % convert sin to cos
  230. if fcn = 'sin then <<m :=multsq(!1pi, -1 ./ 2);
  231. lo := addsq(lo,m); hi := addsq(hi,m)>>;
  232. m := negsq multsq(!2pi,fixsq quotsq(lo,!2pi));
  233. % move interval to main interval
  234. lo:=addsq(lo,m); hi:=addsq(hi,m);
  235. if minusf numr lo then
  236. <<lo := addsq(lo,!2pi); hi := addsq(hi,!2pi)>>;
  237. if null numr lo or sqgreaterp(hi,!2pi) then l:= (1 ./ 1) . l;
  238. if (sqgreaterp(!1pi,lo) and sqgreaterp(hi,!1pi))
  239. or(sqgreaterp(!3pi,lo) and sqgreaterp(hi,!3pi))
  240. then l := (-1 ./ 1) . l;
  241. if l and cdr l then goto trivial;
  242. l:=bounds!-evalfcn1('cos,lo) .bounds!-evalfcn1('cos,hi).l;
  243. if smemq('cos,l) then goto trivial;
  244. return minsq l . maxsq l;
  245. trivial:
  246. return ((-1)./1) . (1 ./ 1);
  247. end;
  248. symbolic procedure fixsq u;
  249. bounds!-evalfcn1('fix,u);
  250. symbolic procedure bounds!-evalfcn1(fcn,u);
  251. (if domainp numr u and denr u=1
  252. and (x:=valuechk(fcn,list numr u))
  253. then x else simp list(fcn,prepsq u)) where x=nil;
  254. symbolic procedure bounds!-evalfcn2(fcn,u,v);
  255. (if domainp numr u and denr u=1 and
  256. domainp numr v and denr v=1
  257. and (x:=valuechk(fcn,list(numr u,numr v)))
  258. then x else simp list(fcn,prepsq u,prepsq v)) where x=nil;
  259. symbolic procedure agreaterp(u,v);
  260. (lambda x;
  261. if not atom denr x or not domainp numr x
  262. then error(99,"number")
  263. else numr x and !:minusp numr x)
  264. simp!* list('difference,v,u);
  265. symbolic procedure boundssin u;
  266. boundssincos('sin,u);
  267. symbolic procedure boundscos u;
  268. boundssincos('cos,u);
  269. put('sin,'boundseval,'boundssin);
  270. put('cos,'boundseval,'boundscos);
  271. symbolic procedure boundstancot(fcn,n);
  272. begin scalar lo,hi,x,ppi;
  273. lo := car n; hi := cdr n;
  274. ppi := rdpi!*() ./ 1;
  275. if sqgreaterp(subtrsq(lo,hi),ppi) then goto no;
  276. lo:=bounds!-evalfcn1(fcn,lo);
  277. hi:=bounds!-evalfcn1(fcn,hi);
  278. if fcn='cot then <<x:=lo;lo:=hi;hi:=x>>;
  279. if not sqgreaterp(lo,hi) then return lo.hi;
  280. no: return boundserr(nil,"unbounded in range");
  281. end;
  282. symbolic procedure boundstan u;
  283. boundstancot('tan,u);
  284. symbolic procedure boundscot u;
  285. boundstancot('cot,u);
  286. put('tan,'boundseval,'boundstan);
  287. put('cot,'boundseval,'boundscot);
  288. symbolic procedure boundsmaxmin u;
  289. if null cdr u then car u else
  290. boundsmaxmin2(car u,boundsmaxmin cdr u);
  291. symbolic procedure boundsmaxmin2(x,y);
  292. (if sqgreaterp(car x,car y) then car y else car x).
  293. (if sqgreaterp(cdr x,cdr y) then cdr x else cdr y);
  294. put('max,'boundsevaln,'boundsmaxmin);
  295. put('min,'boundsevaln,'boundsmaxmin);
  296. put('max,'boundseval,'boundsmaxmin2);
  297. put('min,'boundseval,'boundsmaxmin2);
  298. % for univariate polynomials we look at the extremal points and the
  299. % interval ends. Assuming that the same expression will be investigated
  300. % with different intervals, we store the knowledge about the polynomial.
  301. symbolic procedure boundspolynomial e;
  302. dm!: begin scalar x,u,lo,hi,d,v,l;
  303. if dmode!* neq '!:rd!: then return nil;
  304. if(u:=assoc(e,boundspolynomialdb!*)) then goto old;
  305. if null !*boundsfullcheck or
  306. null(x:=boundspolynomialtest e) then return nil;
  307. d := realroots{reval{'df,e,x}};
  308. u := x. for each s in cdr d collect
  309. <<d:=numr simp caddr s;
  310. d . evaluate(e,{x},{d})>>;
  311. u:=e.u;
  312. boundspolynomialdb!*:=u.boundspolynomialdb!*;
  313. old:
  314. x:=cadr u; u :=cddr u;
  315. v := assoc(x,boundsdb!*);
  316. if null v then return nil;
  317. lo:=numr cadr v; hi:=numr cddr v;
  318. l:={evaluate(e,{x},{lo})./1 , evaluate(e,{x},{hi}) ./1};
  319. for each p in u do
  320. if lo<car p and car p<hi then l:= (cdr p./1).l;
  321. return minsq l . maxsq l;
  322. end;
  323. symbolic procedure boundspolynomialtest e;
  324. (pairp r and car r) where r=boundspolynomialtest1(e,nil);
  325. symbolic procedure boundspolynomialtest1(e,x);
  326. if adomainp e then x or t else
  327. if atom e then boundspolynomialtestvariable(e,x) else
  328. if car e eq 'expt then
  329. fixp caddr e and
  330. boundspolynomialtestvariable(cadr e,x)
  331. else
  332. if car e eq 'minus then boundspolynomialtest1(cadr e,x)
  333. else
  334. if car e eq 'plus or car e eq 'times then
  335. begin scalar r;
  336. e:=cdr e; r:=t;
  337. while e and r do
  338. <<r:=boundspolynomialtest1(car e,x);
  339. if r neq t then x:=r; e:=cdr e>>;
  340. return x or r;
  341. end
  342. else boundspolynomialtestvariable(e,x);
  343. symbolic procedure boundspolynomialtestvariable(e,x);
  344. if x and e=car x then x else
  345. if x then nil else
  346. if member(e, boundsvars!*) then {e};
  347. %--------------------------------------------------------------
  348. %
  349. % support for compilation
  350. %
  351. %--------------------------------------------------------------
  352. fluid '(boundsdb!* boundscompv!* boundscompcode!*);
  353. symbolic procedure boundscompile(e,v);
  354. % compile bounds evaluation function for expression e,
  355. % input intervals v.
  356. begin scalar boundsdb!*,boundscompv!*,boundscompcode!*,u;
  357. boundsdb!*:=for each x in v collect x.x;
  358. u:=boundscompile1(e,nil);
  359. return
  360. {'lambda,v,
  361. 'prog . boundscompv!* .
  362. append(reversip boundscompcode!*,{{'return,u}})};
  363. end;
  364. symbolic procedure boundscompile1(u,flag);
  365. % Main driver for bounds compilation
  366. if adomainp u then <<u:=simp u;mkquote(u.u)>> else
  367. begin scalar v,w,fcn,var;
  368. if (v:=assoc(u,boundsdb!*))then return cdr v;
  369. if idp u and (fcn:=get(u,dmode!*)) then
  370. <<v:=apply(fcn,nil); v:=mkquote((v ./1).(v ./1)); goto done>>;
  371. if atom u then goto err1;
  372. if car u='expt and fixp caddr u and caddr u>0 then
  373. <<v:={'boundsexpt!-int,boundscompile1(cadr u,t),caddr u};
  374. goto done>>;
  375. w := for each y in cdr u collect boundscompile1(y,t);
  376. v:= if length w>2 and (fcn:=get(car u,'boundsevaln))
  377. then {fcn,'list.w} else
  378. if length w>2 then t else
  379. if (fcn:=get(car u,'boundseval1))
  380. then {fcn,mkquote car u,car w} else
  381. if (fcn:=get(car u,'boundseval))
  382. then if null cdr w then {fcn,car w}
  383. else {fcn,car w,cadr w}
  384. else if cdr w then t
  385. else {'boundsoperator,car u,car w};
  386. done:
  387. if v=t then goto err2;
  388. if not flag then return v;
  389. var:=gensym(); boundscompv!* := var.boundscompv!*;
  390. boundscompcode!*:={'setq,var,v}.boundscompcode!*;
  391. % cache result for later usage.
  392. boundsdb!*:= (u.var).boundsdb!*;
  393. return var;
  394. err1: typerr(u,"bounded value");
  395. err2: typerr(u,"expression to be compiled for bounds");
  396. end;
  397. endmodule;
  398. end;