diff.red 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. module diff; % Differentiation package.
  2. % Author: Anthony C. Hearn.
  3. % Modifications by: Francis J. Wright.
  4. % Copyright (c) 2000 Anthony C. Hearn. All rights reserved.
  5. fluid '(!*depend frlis!* powlis!* subfg!* wtl!*);
  6. fluid '(!*allowdfint !*dfint);
  7. global '(mcond!*);
  8. % Contains a reference to RPLACD (a table update), commented out.
  9. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  10. % Turning on the switch `allowdfint' allows "differentiation under the
  11. % integral sign", i.e. df(int(y, x), v) -> int(df(y, v), x), if this
  12. % results in a simplification. If the switch `dfint' is also turned
  13. % on then this happens regardless of whether the result simplifies.
  14. % Both switches are off by default.
  15. switch allowdfint, dfint;
  16. deflist('((dfint ((t (rmsubs))))
  17. (allowdfint ((t (progn (put 'int 'dfform 'dfform_int) (rmsubs)))
  18. (nil (remprop 'int 'dfform))))), 'simpfg);
  19. % There is no code to reverse the df-int commutation,
  20. % so no reason to call rmsubs when the switch is turned off.
  21. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  22. % Consider df(u,x,y,z).
  23. % If none of x,y,z are equal to u then the order of differentiation is
  24. % commuted into a canonical form, unless the switch `nocommutedf' is
  25. % turned on, in which case the order of differentiation is not
  26. % commuted at all. The switch `nocommutedf' is off by default.
  27. % If one (or more) of x,y,z is equal to u then the order of
  28. % differentiation is NOT commuted and the derivative is NOT simplified
  29. % to zero, unless the switch `commutedf' is turned on. It is off by
  30. % default. (CRACK needs to turn it on!)
  31. % The new default behaviour should match the behaviour of REDUCE 3.6.
  32. % Turning on the switch `commutedf' should reproduce the default
  33. % behaviour of REDUCE 3.7.
  34. % If `commutedf' is off and the switch `simpnoncomdf' is on then
  35. % simplify df(u,x,u) -> df(u,x,2)/df(u,x), df(u,x,n,u) ->
  36. % df(u,x,n+1)/df(u,x), as suggested by Alain Moussiaux, PROVIDED u
  37. % depends only on the one variable x. This simplification removes the
  38. % non-commutative aspect of the derivative.
  39. switch commutedf, nocommutedf, simpnoncomdf;
  40. % Turning either `commutedf' or `nocommutedf' on turns the other off.
  41. % Turning commutation on or noncommutation off, or turning
  42. % simplification of noncommutative derivatives on, causes
  43. % resimplification.
  44. deflist('((commutedf ((t (off1 'nocommutedf) (rmsubs))))
  45. (nocommutedf ((t (off1 'commutedf)) (nil (rmsubs))))
  46. (simpnoncomdf ((t (rmsubs))))), 'simpfg);
  47. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  48. % If the switch `expanddf' is turned on then REDUCE uses the chain
  49. % rule to expand symbolic derivatives of indirectly dependent
  50. % variables provided the result is unambiguous, i.e. provided there is
  51. % no direct dependence. It is off by default. Thus, for example,
  52. % given
  53. % depend f, u, v; depend {u, v}, x;
  54. % then, if `expanddf' is on,
  55. % df(f,x) -> df(f,u)*df(u,x) + df(f,v)*df(v,x)
  56. % whereas after
  57. % depend f, x;
  58. % df(f,x) does not expand at all (since the result would be ambiguous
  59. % and the algorithm would loop).
  60. % For similar handling in the case of explicit dependence,
  61. % e.g. df(f(u(x),v(x)),x), please use the standard package `DFPART' by
  62. % Herbert Melenk.
  63. switch expanddf;
  64. deflist('((expanddf ((t (rmsubs))))), 'simpfg);
  65. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  66. symbolic procedure simpdf u;
  67. % U is a list of forms, the first an expression and the remainder
  68. % kernels and numbers.
  69. % Value is derivative of first form wrt rest of list.
  70. begin scalar v,x,y,z;
  71. if null subfg!* then return mksq('df . u,1);
  72. v := cdr u;
  73. u := simp!* car u;
  74. a: if null v or null numr u then return u;
  75. x := if null y or y=0 then simp!* car v else y;
  76. if denr x neq 1 or atom numr x
  77. then typerr(prepsq x,"kernel or integer")
  78. else (if domainp z
  79. then if get(car z,'domain!-diff!-fn)
  80. then begin scalar dmode!*,alglist!*;
  81. x := prepf z;
  82. if null prekernp x
  83. then typerr(x,"kernel")
  84. end
  85. else typerr(prepf z,"kernel")
  86. else if null red z and lc z = 1 and ldeg z = 1
  87. then x := mvar z
  88. else typerr(prepf z,"kernel")) where z = numr x;
  89. v := cdr v;
  90. if null v then <<u := diffsq(u,x); go to a>>;
  91. y := simp!* car v;
  92. % At this point, y must be a kernel or equivalent to an integer.
  93. % Any other value is an error.
  94. if null numr y then <<v := cdr v; y := nil; go to a>>
  95. else if not(z := d2int y) then <<u := diffsq(u,x); go to a>>;
  96. v := cdr v;
  97. for i:=1:z do u := diffsq(u,x);
  98. y := nil;
  99. go to a
  100. end;
  101. symbolic procedure d2int u;
  102. if denr u neq 1 then nil
  103. else if numberp(u := numr u) then u
  104. else if not domainp u or not(car u eq '!:rd!:) then nil
  105. else (if abs(float x - u)<!!fleps1 then x else nil)
  106. where x=fix u where u=rd2fl u;
  107. put('df,'simpfn,'simpdf);
  108. symbolic procedure prekernp u;
  109. if atom u then idp u
  110. else idp car u
  111. and null((car u memq '(plus minus times quotient recip))
  112. or ((car u eq 'expt) and fixp caddr u));
  113. symbolic procedure diffsq(u,v);
  114. % U is a standard quotient, V a kernel.
  115. % Value is the standard quotient derivative of U wrt V.
  116. % Algorithm: df(x/y,z)= (x'-(x/y)*y')/y.
  117. multsq(addsq(difff(numr u,v),negsq multsq(u,difff(denr u,v))),
  118. 1 ./ denr u);
  119. symbolic procedure difff(u,v);
  120. %U is a standard form, V a kernel.
  121. %Value is the standard quotient derivative of U wrt V.
  122. % Allow for differentiable domains.
  123. if atom u then nil ./ 1
  124. else if atom car u
  125. then (if diff!-fn then apply2(diff!-fn,u,v) else nil ./ 1)
  126. where (diff!-fn = get(car u,'domain!-diff!-fn))
  127. else addsq(addsq(multpq(lpow u,difff(lc u,v)),
  128. multsq(diffp(lpow u,v),lc u ./ 1)),
  129. difff(red u,v));
  130. symbolic procedure diffp(u,v);
  131. % U is a standard power, V a kernel.
  132. % Value is the standard quotient derivative of U wrt V.
  133. begin scalar n,w,x,y,z; integer m;
  134. n := cdr u; % integer power.
  135. u := car u; % main variable.
  136. % Take care with noncommuting expressions.
  137. if n>1 and noncomp u
  138. then return addsq(multsq(simpdf {u,v},simpexpt {u,n - 1}),
  139. multpq(u .** 1,diffp(u . (n - 1),v)))
  140. else if u eq v and (w := 1 ./ 1) then go to e
  141. else if atom u then go to f
  142. %else if (x := assoc(u,dsubl!*)) and (x := atsoc(v,cdr x))
  143. % and (w := cdr x) then go to e % deriv known.
  144. % DSUBL!* not used for now.
  145. else if (not atom car u and (w:= difff(u,v)))
  146. or (car u eq '!*sq and (w:= diffsq(cadr u,v)))
  147. then go to c % extended kernel found.
  148. else if x := get(car u,'dfform) then return apply3(x,u,v,n)
  149. else if x:= get(car u,dfn_prop u) then nil
  150. else if car u eq 'plus and (w := diffsq(simp u,v))
  151. then go to c
  152. else go to h; % unknown derivative.
  153. y := x;
  154. z := cdr u;
  155. a: w := diffsq(simp car z,v) . w;
  156. if caar w and null car y then go to h; % unknown deriv.
  157. y := cdr y;
  158. z := cdr z;
  159. if z and y then go to a
  160. else if z or y then go to h; % arguments do not match.
  161. y := reverse w;
  162. z := cdr u;
  163. w := nil ./ 1;
  164. b: % computation of kernel derivative.
  165. if caar y
  166. then w := addsq(multsq(car y,simp subla(pair(caar x,z),
  167. cdar x)),
  168. w);
  169. x := cdr x;
  170. y := cdr y;
  171. if y then go to b;
  172. c: % save calculated deriv in case it is used again.
  173. % if x := atsoc(u,dsubl!*) then go to d
  174. % else x := u . nil;
  175. % dsubl!* := x . dsubl!*;
  176. % d: rplacd(x,xadd(v . w,cdr x,t));
  177. e: % allowance for power.
  178. % first check to see if kernel has weight.
  179. if (x := atsoc(u,wtl!*))
  180. then w := multpq('k!* .** (-cdr x),w);
  181. m := n-1;
  182. % Evaluation is far more efficient if results are rationalized.
  183. return rationalizesq if n=1 then w
  184. else if flagp(dmode!*,'convert)
  185. and null(n := int!-equiv!-chk
  186. apply1(get(dmode!*,'i2d),n))
  187. then nil ./ 1
  188. else multsq(!*t2q((u .** m) .* n),w);
  189. f: % Check for possible unused substitution rule.
  190. if not depends(u,v)
  191. and (not (x:= atsoc(u,powlis!*))
  192. or not depends(cadddr x,v))
  193. and null !*depend
  194. then return nil ./ 1;
  195. % Derivative of a dependent identifier; maybe apply chain
  196. % rule. Suppose u(v) = u(a(u),b(u),...), i.e. given
  197. % depend {u}, a, b, {a, b}, v;
  198. % then (essentially) depl!* = ((b v) (a v) (u b a))
  199. if !*expanddf and not(v memq (x:=cdr atsoc(u, depl!*))) then <<
  200. w := nil ./ 1;
  201. for each a in x do
  202. w := addsq(w, multsq(simp{'df,u,a},simp{'df,a,v}));
  203. go to e
  204. >>;
  205. w := list('df,u,v);
  206. w := if x := opmtch w then simp x else mksq(w,1);
  207. go to e;
  208. h: % Final check for possible kernel deriv.
  209. if car u eq 'df then << % multiple derivative
  210. if cadr u eq v then
  211. % (df (df v x y z ...) v) ==> 0 if commutedf
  212. if !*commutedf and null !*depend then return nil ./ 1
  213. else if !*simpnoncomdf and (w:=atsoc(v, depl!*))
  214. and null cddr w % and (cadr w eq (x:=caddr u))
  215. then
  216. % (df (df v x) v) ==> (df v x 2)/(df v x) etc.
  217. % if single independent variable
  218. <<
  219. x := caddr u;
  220. % w := simp {'quotient, {'df,u,x}, {'df,v,x}};
  221. w := quotsq(simp{'df,u,x},simp{'df,v,x});
  222. go to e
  223. >>
  224. else if eqcar(cadr u, 'int) then
  225. % (df (df (int F x) A) v) ==> (df (df (int F x) v) A) ?
  226. % Commute the derivatives to differentiate the integral?
  227. if caddr cadr u eq v then
  228. % Evaluating (df u v) where u = (df (int F v) A)
  229. % Just return (df F A) - derivative absorbed
  230. << w := 'df . cadr cadr u . cddr u; go to j >>
  231. else if !*allowdfint and
  232. % Evaluating (df u v) where u = (df (int F x) A)
  233. % (If dfint is also on then this will not arise!)
  234. % Commute only if the result simplifies:
  235. not_df_p(w := diffsq(simp!* cadr cadr u, v))
  236. then <<
  237. % Generally must re-evaluate the integral (carefully!)
  238. w := 'df . reval{'int, mk!*sq w, caddr cadr u} . cddr u;
  239. go to j >>; % derivative absorbed
  240. if (x := find_sub_df(w:= cadr u . merge!-ind!-vars(u,v),
  241. get('df,'kvalue)))
  242. then <<w := simp car x;
  243. for each el in cdr x do
  244. for i := 1:cdr el do
  245. w := diffsq(w,car el);
  246. go to e>>
  247. else w := 'df . w
  248. >> else w := {'df,u,v};
  249. j: if (x := opmtch w) then w := simp x
  250. else if not depends(u,v) and null !*depend then return nil ./ 1
  251. else w := mksq(w,1);
  252. go to e
  253. end;
  254. symbolic procedure dfform_int(u, v, n);
  255. % Simplify a SINGLE derivative of an integral.
  256. % u = '(int y x) [as main variable of SQ form]
  257. % v = kernel
  258. % n = integer power
  259. % Return SQ form of df(u**n, v) = n*u**(n-1)*df(u, v)
  260. % This routine is called by diffp via the hook
  261. % "if x := get(car u,'dfform) then return apply3(x,u,v,n)".
  262. % It does not necessarily need to use this hook, but it needs to be
  263. % called as an alternative to diffp so that the linearity of
  264. % differentiation has already been applied.
  265. begin scalar result, x, y;
  266. y := simp!* cadr u; % SQ form integrand
  267. x := caddr u; % kernel
  268. result :=
  269. if v eq x then y
  270. % df(int(y,x), x) -> y replacing the let rule in INT.RED
  271. else if not !*intflag!* and % not in the integrator
  272. % If used in the integrator it can cause infinite loops,
  273. % e.g. in df(int(int(f,x),y),x) and df(int(int(f,x),y),y)
  274. !*allowdfint and % must be on for dfint to work
  275. << y := diffsq(y, v); !*dfint or not_df_p y >>
  276. % it has simplified
  277. then simp{'int, mk!*sq y, x} % MUST re-simplify it!!!
  278. % i.e. differentiate under the integral sign
  279. % df(int(y, x), v) -> int(df(y, v), x).
  280. % (Perhaps I should use prepsq - kernels are normally true prefix?)
  281. else !*kk2q{'df, u, v}; % remain unchanged
  282. if not(n=1) then
  283. result := multsq( (((u .** (n-1)) .* n) .+ nil) ./ 1, result);
  284. return result
  285. end;
  286. symbolic procedure not_df_p y;
  287. % True if the SQ form y is not a df kernel.
  288. not(denr y eq 1 and
  289. not domainp (y := numr y) and eqcar(mvar y, 'df));
  290. % Compute a dfn-property name corresponding to the argument number
  291. % of an operator expression. Here we assume that most functions
  292. % will have not more than 3 arguments.
  293. symbolic procedure dfn_prop(w);
  294. (if n=1 then 'dfn else if n=2 then 'dfn2 else if n=3 then 'dfn3
  295. else mkid('dfn,n))
  296. where n=length cdr w;
  297. % The following three functions, and the hooks to this code above, were
  298. % suggested by Gerhard Post and Marcel Roelofs.
  299. symbolic procedure find_sub_df(df_args,df_values);
  300. df_values and
  301. (is_sub_df(df_args,car df_values) or
  302. find_sub_df(df_args,cdr df_values));
  303. symbolic procedure is_sub_df(df_args,df_value);
  304. begin scalar df_set,kernel,n,entry;
  305. if car(df_args) neq cadar(df_value) then return nil; % check fns.
  306. df_args := dot_df_args cdr df_args;
  307. df_set := cddar df_value;
  308. while df_set and df_args do % Check differentiations.
  309. <<kernel := car df_set;
  310. if cdr df_set and fixp(n := cadr df_set)
  311. then df_set := cdr df_set else n := 1;
  312. if (entry := atsoc(kernel,df_args))
  313. and (n := cdr entry-n) geq 0
  314. then rplacd(entry,n) else df_args:=nil;
  315. df_set := cdr df_set>>;
  316. return if df_args then (cadr(df_value) . df_args);
  317. end;
  318. symbolic procedure dot_df_args l;
  319. begin scalar kernel,n,df_args;
  320. while l do
  321. <<kernel := car l;
  322. if cdr l and fixp(n := cadr l) then l := cdr l else n := 1;
  323. df_args := (kernel . n) . df_args;
  324. l := cdr l>>;
  325. return df_args;
  326. end;
  327. symbolic procedure merge!-ind!-vars(u,v);
  328. % Consider (df u v) where u = (df a b c d ...)
  329. % It is non-commuting if a = v or if a in (b c d ...)
  330. % i.e. if a in (v b c d ...)
  331. if !*nocommutedf or
  332. (not !*commutedf and (cadr u memq (v . cddr u)))
  333. then derad!*(v,cddr u) else derad(v,cddr u);
  334. symbolic procedure derad!*(u,v); % Non-commuting derad
  335. %% Return the canonical list of differentiation variables
  336. %% equivalent to v,u, where v is a LIST of previus differentiation
  337. %% variables, when df(df(f(v,u), v), u) is simplified to
  338. %% df(f(v,u), v, u). Essentially just cons u onto v.
  339. reverse
  340. if u eq car(v:=reverse v) then % x,y, y
  341. 2 . v
  342. else if numberp car v and u eq cadr v then % x,y,n, y
  343. (car v + 1) . cdr v
  344. else u . v; % x,y, z
  345. symbolic procedure derad(u,v);
  346. if null v then list u
  347. else if numberp car v then car v . derad(u,cdr v)
  348. else if u=car v then if cdr v and numberp cadr v
  349. then u . (cadr v + 1) . cddr v
  350. else u . 2 . cdr v
  351. else if ordp(u,car v) then u . v
  352. else car v . derad(u,cdr v);
  353. symbolic procedure letdf(u,v,w,x,b);
  354. begin scalar y,z,dfn;
  355. if atom cadr x then go to b
  356. else if not idp caadr x then typerr(caadr x,"operator")
  357. else if not get(caadr x,'simpfn)
  358. then <<redmsg(caadr x,"operator"); mkop caadr x>>;
  359. rmsubs();
  360. dfn := dfn_prop cadr x;
  361. if not(mcond!* eq 't)
  362. or not frlp cdadr x
  363. or null cddr x
  364. or cdddr x
  365. or not frlp cddr x
  366. or not idlistp cdadr x
  367. or repeats cdadr x
  368. or not(caddr x member cdadr x)
  369. then go to b;
  370. z := lpos(caddr x,cdadr x);
  371. if not get(caadr x,dfn)
  372. then put(caadr x,
  373. dfn,
  374. nlist(nil,length cdadr x));
  375. w := get(caadr x,dfn);
  376. if length w neq length cdadr x
  377. then rerror(poly,17,
  378. list("Incompatible DF rule argument length for",
  379. caadr x));
  380. a: if null w or z=0 then return errpri1 u
  381. else if z neq 1
  382. then <<y := car w . y; w := cdr w; z := z-1; go to a>>
  383. else if null b then y := append(reverse y,nil . cdr w)
  384. else y := append(reverse y,(cdadr x . v) . cdr w);
  385. return put(caadr x,dfn,y);
  386. b: %check for dependency;
  387. if caddr x memq frlis!* then return nil
  388. else if idp cadr x and not(cadr x memq frlis!*)
  389. then depend1(cadr x,caddr x,t)
  390. else if not atom cadr x and idp caadr x and frlp cdadr x
  391. then depend1(caadr x,caddr x,t);
  392. return nil
  393. end;
  394. symbolic procedure frlp u;
  395. null u or (car u memq frlis!* and frlp cdr u);
  396. symbolic procedure lpos(u,v);
  397. if u eq car v then 1 else lpos(u,cdr v)+1;
  398. endmodule;
  399. end;