diff.red 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. module diff; % Differentiation package.
  2. % Author: Anthony C. Hearn.
  3. % Modifications by: Francis J. Wright.
  4. % Copyright (c) 1991 RAND. All rights reserved.
  5. fluid '(!*allowdfint !*depend !*fjwflag frlis!* powlis!* subfg!* wtl!*);
  6. switch allowdfint, dfint, fjwflag;
  7. !*fjwflag := t; % Let's try it on for a while.
  8. global '(mcond!*);
  9. % Contains a reference to RPLACD (a table update), commented out.
  10. symbolic procedure simpdf u;
  11. % U is a list of forms, the first an expression and the remainder
  12. % kernels and numbers.
  13. % Value is derivative of first form wrt rest of list.
  14. begin scalar v,x,y,z;
  15. if null subfg!* then return mksq('df . u,1);
  16. v := cdr u;
  17. u := simp!* car u;
  18. a: if null v or null numr u then return u;
  19. x := if null y or y=0 then simp!* car v else y;
  20. if denr x neq 1 or atom numr x
  21. then typerr(prepsq x,"kernel or integer")
  22. else (if domainp z
  23. then if get(car z,'domain!-diff!-fn)
  24. then begin scalar dmode!*,alglist!*;
  25. x := prepf z;
  26. if null prekernp x
  27. then typerr(x,"kernel")
  28. end
  29. else typerr(prepf z,"kernel")
  30. else if null red z and lc z = 1 and ldeg z = 1
  31. then x := mvar z
  32. else typerr(prepf z,"kernel")) where z = numr x;
  33. v := cdr v;
  34. if null v then <<u := diffsq(u,x); go to a>>;
  35. y := simp!* car v;
  36. % At this point, y must be a kernel or equivalent to an integer.
  37. % Any other value is an error.
  38. if null numr y then <<v := cdr v; y := nil; go to a>>
  39. else if not(z := d2int y) then <<u := diffsq(u,x); go to a>>;
  40. v := cdr v;
  41. for i:=1:z do u := diffsq(u,x);
  42. y := nil;
  43. go to a
  44. end;
  45. symbolic procedure d2int u;
  46. if denr u neq 1 then nil
  47. else if numberp(u := numr u) then u
  48. else if not domainp u or not(car u eq '!:rd!:) then nil
  49. else (if abs(float x - u)<!!fleps1 then x else nil)
  50. where x=fix u where u=rd2fl u;
  51. put('df,'simpfn,'simpdf);
  52. symbolic procedure prekernp u;
  53. if atom u then idp u
  54. else idp car u
  55. and null((car u memq '(plus minus times quotient recip))
  56. or ((car u eq 'expt) and fixp caddr u));
  57. symbolic procedure diffsq(u,v);
  58. % U is a standard quotient, V a kernel.
  59. % Value is the standard quotient derivative of U wrt V.
  60. % Algorithm: df(x/y,z)= (x'-(x/y)*y')/y.
  61. multsq(addsq(difff(numr u,v),negsq multsq(u,difff(denr u,v))),
  62. 1 ./ denr u);
  63. symbolic procedure difff(u,v);
  64. %U is a standard form, V a kernel.
  65. %Value is the standard quotient derivative of U wrt V.
  66. % Allow for differentiable domains.
  67. if atom u then nil ./ 1
  68. else if atom car u
  69. then (if diff!-fn then apply2(diff!-fn,u,v) else nil ./ 1)
  70. where (diff!-fn = get(car u,'domain!-diff!-fn))
  71. else addsq(addsq(multpq(lpow u,difff(lc u,v)),
  72. multsq(diffp(lpow u,v),lc u ./ 1)),
  73. difff(red u,v));
  74. symbolic procedure diffp(u,v);
  75. % U is a standard power, V a kernel.
  76. % Value is the standard quotient derivative of U wrt V.
  77. begin scalar n,w,x,y,z; integer m;
  78. n := cdr u; % integer power.
  79. u := car u; % main variable.
  80. if u eq v and (w := 1 ./ 1) then go to e
  81. else if atom u then go to f
  82. %else if (x := assoc(u,dsubl!*)) and (x := atsoc(v,cdr x))
  83. % and (w := cdr x) then go to e % deriv known.
  84. % DSUBL!* not used for now.
  85. else if (not atom car u and (w:= difff(u,v)))
  86. or (car u eq '!*sq and (w:= diffsq(cadr u,v)))
  87. then go to c % extended kernel found.
  88. else if x := get(car u,'dfform) then return apply3(x,u,v,n)
  89. else if x:= get(car u,dfn_prop u) then nil
  90. else if car u eq 'plus and (w := diffsq(simp u,v))
  91. then go to c
  92. else go to h; % unknown derivative.
  93. y := x;
  94. z := cdr u;
  95. a: w := diffsq(simp car z,v) . w;
  96. if caar w and null car y then go to h; % unknown deriv.
  97. y := cdr y;
  98. z := cdr z;
  99. if z and y then go to a
  100. else if z or y then go to h; % arguments do not match.
  101. y := reverse w;
  102. z := cdr u;
  103. w := nil ./ 1;
  104. b: % computation of kernel derivative.
  105. if caar y
  106. then w := addsq(multsq(car y,simp subla(pair(caar x,z),
  107. cdar x)),
  108. w);
  109. x := cdr x;
  110. y := cdr y;
  111. if y then go to b;
  112. c: % save calculated deriv in case it is used again.
  113. % if x := atsoc(u,dsubl!*) then go to d
  114. % else x := u . nil;
  115. % dsubl!* := x . dsubl!*;
  116. % d: rplacd(x,xadd(v . w,cdr x,t));
  117. e: % allowance for power.
  118. % first check to see if kernel has weight.
  119. if (x := atsoc(u,wtl!*))
  120. then w := multpq('k!* .** (-cdr x),w);
  121. m := n-1;
  122. % Evaluation is far more efficient if results are rationalized.
  123. return rationalizesq if n=1 then w
  124. else if flagp(dmode!*,'convert)
  125. and null(n := int!-equiv!-chk
  126. apply1(get(dmode!*,'i2d),n))
  127. then nil ./ 1
  128. else multsq(!*t2q((u .** m) .* n),w);
  129. f: % Check for possible unused substitution rule.
  130. if not depends(u,v)
  131. and (not (x:= atsoc(u,powlis!*))
  132. or not depends(cadddr x,v))
  133. and null !*depend
  134. then return nil ./ 1;
  135. w := list('df,u,v);
  136. w := if x := opmtch w then simp x else mksq(w,1);
  137. go to e;
  138. h: % Final check for possible kernel deriv.
  139. if car u eq 'df % multiple derivative
  140. then if depends(cadr u,v)
  141. % FJW - my version of above test was simply as follows. Surely, inner
  142. % derivative will already have simplied to 0 unless v depends on A!
  143. and not(cadr u eq v)
  144. % (df (df v A) v) ==> 0
  145. %% and not(cadr u eq v and not depends(v,caddr u))
  146. %% % (df (df v A) v) ==> 0 unless v depends on A.
  147. then
  148. <<if !*fjwflag and eqcar(cadr u, 'int) then
  149. % (df (df (int F x) A) v) ==> (df (df (int F x) v) A) ?
  150. % Commute the derivatives to differentiate the integral?
  151. if caddr cadr u eq v then
  152. % Evaluating (df u v) where u = (df (int F v) A)
  153. % Just return (df F A) - derivative absorbed
  154. << w := 'df . cadr cadr u . cddr u; go to j >>
  155. else if !*allowdfint and
  156. % Evaluating (df u v) where u = (df (int F x) A)
  157. % (If dfint is also on then this will not arise!)
  158. % Commute only if the result simplifies:
  159. not_df_p(w := diffsq(simp!* cadr cadr u, v))
  160. then <<
  161. % Generally must re-evaluate the integral (carefully!)
  162. % FJW. Bug fix!
  163. % w := aeval{'int, mk!*sq w, caddr cadr u} . cddr u;
  164. w := 'df . reval{'int, mk!*sq w, caddr cadr u} . cddr u;
  165. go to j >>; % derivative absorbed
  166. if (x := find_sub_df(w:= cadr u . derad(v,cddr u),
  167. get('df,'kvalue)))
  168. then <<w := simp car x;
  169. for each el in cdr x do
  170. for i := 1:cdr el do
  171. w := diffsq(w,car el);
  172. go to e>>
  173. else w := 'df . w
  174. >>
  175. else if null !*depend then return nil ./ 1
  176. else w := {'df,u,v}
  177. else w := {'df,u,v};
  178. j: if (x := opmtch w) then w := simp x
  179. else if not depends(u,v) and null !*depend then return nil ./ 1
  180. else w := mksq(w,1);
  181. go to e
  182. end;
  183. deflist('((dfint ((t (rmsubs))))
  184. (allowdfint ((t (progn (put 'int 'dfform 'dfform_int) (rmsubs)))
  185. (nil (remprop 'int 'dfform))))), 'simpfg);
  186. % There is no code to reverse the df-int commutation,
  187. % so no reason to call rmsubs when the switch is turned off.
  188. symbolic procedure dfform_int(u, v, n);
  189. % Simplify a SINGLE derivative of an integral.
  190. % u = '(int y x) [as main variable of SQ form]
  191. % v = kernel
  192. % n = integer power
  193. % Return SQ form of df(u**n, v) = n*u**(n-1)*df(u, v)
  194. % This routine is called by diffp via the hook
  195. % "if x := get(car u,'dfform) then return apply3(x,u,v,n)".
  196. % It does not necessarily need to use this hook, but it needs to be
  197. % called as an alternative to diffp so that the linearity of
  198. % differentiation has already been applied.
  199. begin scalar result, x, y;
  200. y := simp!* cadr u; % SQ form integrand
  201. x := caddr u; % kernel
  202. result :=
  203. if v eq x then y
  204. % df(int(y,x), x) -> y replacing the let rule in INT.RED
  205. else if not !*intflag!* and % not in the integrator
  206. % If used in the integrator it can cause infinite loops,
  207. % e.g. in df(int(int(f,x),y),x) and df(int(int(f,x),y),y)
  208. !*allowdfint and % must be on for dfint to work
  209. << y := diffsq(y, v); !*dfint or not_df_p y >>
  210. % it has simplified
  211. then simp{'int, mk!*sq y, x} % MUST re-simplify it!!!
  212. % i.e. differentiate under the integral sign
  213. % df(int(y, x), v) -> int(df(y, v), x).
  214. % (Perhaps I should use prepsq - kernels are normally true prefix?)
  215. else !*kk2q{'df, u, v}; % remain unchanged
  216. if not(n eq 1) then
  217. result := multsq( (((u .** (n-1)) .* n) .+ nil) ./ 1, result);
  218. return result
  219. end;
  220. symbolic procedure not_df_p y;
  221. % True if the SQ form y is not a df kernel.
  222. not(denr y eq 1 and
  223. not domainp (y := numr y) and eqcar(mvar y, 'df));
  224. % Compute a dfn-property name corresponding to the argument number
  225. % of an operator expression. Here we assume that most functions
  226. % will have not more than 3 arguments.
  227. symbolic procedure dfn_prop(w);
  228. (if n=1 then 'dfn else if n=2 then 'dfn2 else if n=3 then 'dfn3
  229. else mkid('dfn,n))
  230. where n=length cdr w;
  231. % The following three functions, and the hooks to this code above, were
  232. % suggested by Gerhard Post and Marcel Roelofs.
  233. symbolic procedure find_sub_df(df_args,df_values);
  234. df_values and
  235. (is_sub_df(df_args,car df_values) or
  236. find_sub_df(df_args,cdr df_values));
  237. symbolic procedure is_sub_df(df_args,df_value);
  238. begin scalar df_set,kernel,n,entry;
  239. if car(df_args) neq cadar(df_value) then return nil; % check fns.
  240. df_args := dot_df_args cdr df_args;
  241. df_set := cddar df_value;
  242. while df_set and df_args do % Check differentiations.
  243. <<kernel := car df_set;
  244. if cdr df_set and fixp(n := cadr df_set)
  245. then df_set := cdr df_set else n := 1;
  246. if (entry := atsoc(kernel,df_args))
  247. and (n := cdr entry-n) geq 0
  248. then rplacd(entry,n) else df_args:=nil;
  249. df_set := cdr df_set>>;
  250. return if df_args then (cadr(df_value) . df_args);
  251. end;
  252. symbolic procedure dot_df_args l;
  253. begin scalar kernel,n,df_args;
  254. while l do
  255. <<kernel := car l;
  256. if cdr l and fixp(n := cadr l) then l := cdr l else n := 1;
  257. df_args := (kernel . n) . df_args;
  258. l := cdr l>>;
  259. return df_args;
  260. end;
  261. symbolic procedure derad(u,v);
  262. if null v then list u
  263. else if numberp car v then car v . derad(u,cdr v)
  264. else if u=car v then if cdr v and numberp cadr v
  265. then u . (cadr v + 1) . cddr v
  266. else u . 2 . cdr v
  267. else if ordp(u,car v) then u . v
  268. else car v . derad(u,cdr v);
  269. symbolic procedure letdf(u,v,w,x,b);
  270. begin scalar y,z,dfn;
  271. if atom cadr x then go to b
  272. else if not idp caadr x then typerr(caadr x,"operator")
  273. else if not get(caadr x,'simpfn)
  274. then <<redmsg(caadr x,"operator"); mkop caadr x>>;
  275. rmsubs();
  276. dfn := dfn_prop cadr x;
  277. if not(mcond!* eq 't)
  278. or not frlp cdadr x
  279. or null cddr x
  280. or cdddr x
  281. or not frlp cddr x
  282. or not idlistp cdadr x
  283. or repeats cdadr x
  284. or not(caddr x member cdadr x)
  285. then go to b;
  286. z := lpos(caddr x,cdadr x);
  287. if not get(caadr x,dfn)
  288. then put(caadr x,
  289. dfn,
  290. nlist(nil,length cdadr x));
  291. w := get(caadr x,dfn);
  292. if length w neq length cdadr x
  293. then rerror(poly,17,
  294. list("Incompatible DF rule argument length for",
  295. caadr x));
  296. a: if null w or z=0 then return errpri1 u
  297. else if z neq 1
  298. then <<y := car w . y; w := cdr w; z := z-1; go to a>>
  299. else if null b then y := append(reverse y,nil . cdr w)
  300. else y := append(reverse y,(cdadr x . v) . cdr w);
  301. return put(caadr x,dfn,y);
  302. b: %check for dependency;
  303. if caddr x memq frlis!* then return nil
  304. else if idp cadr x and not(cadr x memq frlis!*)
  305. then depend1(cadr x,caddr x,t)
  306. else if not atom cadr x and idp caadr x and frlp cdadr x
  307. then depend1(caadr x,caddr x,t);
  308. return nil
  309. end;
  310. symbolic procedure frlp u;
  311. null u or (car u memq frlis!* and frlp cdr u);
  312. symbolic procedure lpos(u,v);
  313. if u eq car v then 1 else lpos(u,cdr v)+1;
  314. endmodule;
  315. end;