odepatch.red 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. module odepatch$ % Patches to standard REDUCE facilities
  2. % F.J.Wright@Maths.QMW.ac.uk, Time-stamp: <18 September 2000>
  3. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  4. % Integrator patches
  5. % ==================
  6. % Avoid trying to integrate an integral to speed up some odesolve
  7. % calls.
  8. % NB: subst copies, even if no substitution is made. It is therefore
  9. % very likely to destroy uniqueness of kernels!
  10. %% load_package int$
  11. %% apply1('load!-package, 'int)$ % not at compile time!
  12. packages_to_load int$ % not at compile time!
  13. global '(ODESolveOldSimpInt)$
  14. (if not(s eq 'NoIntInt_SimpInt) then ODESolveOldSimpInt := s) where
  15. s = get('int, 'simpfn)$ % to allow reloading
  16. put('int, 'simpfn, 'NoIntInt_SimpInt)$
  17. fluid '(!*NoInt !*Odesolve_NoInt)$
  18. symbolic procedure NoIntInt_SimpInt u;
  19. %% Patch to avoid trying to re-integrate symbolic integrals in the
  20. %% integrand, because it can take forever and achieve nothing!
  21. if !*NoInt then
  22. begin scalar v, varstack!*;
  23. % Based on int/driver/simpint1.
  24. % Varstack* rebound, since FORMLNR use can create recursive
  25. % evaluations. (E.g., with int(cos(x)/x**2,x)).
  26. u := 'int . u;
  27. return if (v := formlnr u) neq u then <<
  28. v := simp subst('int!*, 'int, v);
  29. remakesf numr v ./ remakesf denr v
  30. >> else !*kk2q u
  31. end
  32. else
  33. if atom u or null cdr u or cddr u and (null cdddr u or cddddr u)
  34. then rerror(int,1,"Improper number of arguments to INT")
  35. else if cddr u then simpdint u % header from simpint
  36. else begin scalar car_u, result;
  37. %% put('int, 'simpfn, 'SimpNoInt);
  38. car_u := mk!*sq simp!* car u;
  39. %% car_u := subeval{{'equal, 'Int, 'NoInt}, car_u};
  40. car_u := subst('NoInt, 'Int, car_u);
  41. u := car_u . !*a2k cadr u . nil;
  42. %% Prevent SimpInt from resetting itself!
  43. put('int, 'simpfn, ODESolveOldSimpInt); % assumed & RESET by simpint
  44. result := errorset!*({ODESolveOldSimpInt, mkquote u}, t);
  45. put('int, 'simpfn, 'NoIntInt_SimpInt); % reset INT interface
  46. if errorp result then error1();
  47. return NoInt2Int car result;
  48. %% Does this cause non-unique kernels?
  49. end$
  50. algebraic operator NoInt$ % Inert integration operator
  51. %% symbolic procedure SimpNoInt u;
  52. %% !*kk2q('NoInt . u)$ % remain symbolic
  53. symbolic operator Odesolve!-Int$
  54. symbolic procedure Odesolve!-Int(y, x);
  55. %% Used in SolveLinear1 on ode1 to control integration.
  56. if !*Odesolve_NoInt then formlnr {'NoInt, y, x}
  57. else mk!*sq NoIntInt_SimpInt{y, x}$ % aeval{'int, y, x}$
  58. %% put('Odesolve!-Int, 'simpfn, 'Simp!-Odesolve!-Int)$
  59. %% symbolic procedure Simp!-Odesolve!-Int u;
  60. %% %% Used in SolveLinear1 on ode1 to control integration.
  61. %% if !*Odesolve_NoInt then !*kk2q('NoInt . u) % must eval u!!!
  62. %% else NoIntInt_SimpInt u$ % aeval{'int, y, x}$
  63. symbolic procedure NoInt2Int u;
  64. %% Convert all NoInt's back to Int's, without algebraic evaluation.
  65. subst('Int, 'NoInt, u)$
  66. switch NoIntInt$ !*NoIntInt := t$
  67. put('NoIntInt, 'simpfg,
  68. '((nil (put 'int 'simpfn 'SimpInt) (rmsubs))
  69. (t (put 'int 'simpfn 'NoIntInt_SimpInt))))$
  70. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  71. % Differentiator patches
  72. % ======================
  73. % Differentiate integrals correctly!
  74. % NB: `ON' is flagged ignore and so not compiled, so...
  75. on1 'allowdfint$
  76. % To replace the versions in `reduce/packages/poly/diff.red' once
  77. % tested.
  78. % deflist('((dfint ((t (progn (on1 'allowdfint) (rmsubs)))))), 'simpfg);
  79. symbolic procedure diffp(u,v);
  80. % U is a standard power, V a kernel.
  81. % Value is the standard quotient derivative of U wrt V.
  82. begin scalar n,w,x,y,z; integer m;
  83. n := cdr u; % integer power.
  84. u := car u; % main variable.
  85. if u eq v and (w := 1 ./ 1) then go to e
  86. else if atom u then go to f
  87. %else if (x := assoc(u,dsubl!*)) and (x := atsoc(v,cdr x))
  88. % and (w := cdr x) then go to e % deriv known.
  89. % DSUBL!* not used for now.
  90. else if (not atom car u and (w:= difff(u,v)))
  91. or (car u eq '!*sq and (w:= diffsq(cadr u,v)))
  92. then go to c % extended kernel found.
  93. else if x := get(car u,'dfform) then return apply3(x,u,v,n)
  94. else if x:= get(car u,dfn_prop u) then nil
  95. else if car u eq 'plus and (w := diffsq(simp u,v))
  96. then go to c
  97. else go to h; % unknown derivative.
  98. y := x;
  99. z := cdr u;
  100. a: w := diffsq(simp car z,v) . w;
  101. if caar w and null car y then go to h; % unknown deriv.
  102. y := cdr y;
  103. z := cdr z;
  104. if z and y then go to a
  105. else if z or y then go to h; % arguments do not match.
  106. y := reverse w;
  107. z := cdr u;
  108. w := nil ./ 1;
  109. b: % computation of kernel derivative.
  110. if caar y
  111. then w := addsq(multsq(car y,simp subla(pair(caar x,z),
  112. cdar x)),
  113. w);
  114. x := cdr x;
  115. y := cdr y;
  116. if y then go to b;
  117. c: % save calculated deriv in case it is used again.
  118. % if x := atsoc(u,dsubl!*) then go to d
  119. % else x := u . nil;
  120. % dsubl!* := x . dsubl!*;
  121. % d: rplacd(x,xadd(v . w,cdr x,t));
  122. e: % allowance for power.
  123. % first check to see if kernel has weight.
  124. if (x := atsoc(u,wtl!*))
  125. then w := multpq('k!* .** (-cdr x),w);
  126. m := n-1;
  127. % Evaluation is far more efficient if results are rationalized.
  128. return rationalizesq if n=1 then w
  129. else if flagp(dmode!*,'convert)
  130. and null(n := int!-equiv!-chk
  131. apply1(get(dmode!*,'i2d),n))
  132. then nil ./ 1
  133. else multsq(!*t2q((u .** m) .* n),w);
  134. f: % Check for possible unused substitution rule.
  135. if not depends(u,v)
  136. and (not (x:= atsoc(u,powlis!*))
  137. or not depends(cadddr x,v))
  138. and null !*depend
  139. then return nil ./ 1;
  140. % Derivative of a dependent identifier via the chain rule.
  141. % Suppose u(v) = u(a(v),b(v),...), i.e. given depend {u}, a,
  142. % b, {a, b}, v; then (essentially) depl!* = ((b v) (a v) (u b
  143. % a))
  144. if !*expanddf and not(v memq (x:=cdr atsoc(u, depl!*))) then <<
  145. w := nil ./ 1;
  146. for each a in x do
  147. w := addsq(w, multsq(simp{'df,u,a},simp{'df,a,v}));
  148. go to e
  149. >>;
  150. w := list('df,u,v);
  151. w := if x := opmtch w then simp x else mksq(w,1);
  152. go to e;
  153. h: % Final check for possible kernel deriv.
  154. if car u eq 'df then << % multiple derivative
  155. if cadr u eq v then
  156. % (df (df v x y z ...) v) ==> 0 if commutedf
  157. if !*commutedf and null !*depend then return nil ./ 1
  158. else if !*simpnoncomdf and (w:=atsoc(v, depl!*))
  159. and null cddr w % and (cadr w eq (x:=caddr u))
  160. then
  161. % (df (df v x) v) ==> (df v x 2)/(df v x) etc.
  162. % if single independent variable
  163. <<
  164. x := caddr u;
  165. % w := simp {'quotient, {'df,u,x}, {'df,v,x}};
  166. w := quotsq(simp{'df,u,x},simp{'df,v,x});
  167. go to e
  168. >>
  169. else if eqcar(cadr u, 'int) then
  170. % (df (df (int F x) A) v) ==> (df (df (int F x) v) A) ?
  171. % Commute the derivatives to differentiate the integral?
  172. if caddr cadr u eq v then
  173. % Evaluating (df u v) where u = (df (int F v) A)
  174. % Just return (df F A) - derivative absorbed
  175. << w := 'df . cadr cadr u . cddr u; go to j >>
  176. else if !*allowdfint and
  177. % Evaluating (df u v) where u = (df (int F x) A)
  178. % (If dfint is also on then this will not arise!)
  179. % Commute only if the result simplifies:
  180. not_df_p(w := diffsq(simp!* cadr cadr u, v))
  181. then <<
  182. % Generally must re-evaluate the integral (carefully!)
  183. w := 'df . reval{'int, mk!*sq w, caddr cadr u} . cddr u;
  184. go to j >>; % derivative absorbed
  185. if (x := find_sub_df(w:= cadr u . merge!-ind!-vars(u,v),
  186. get('df,'kvalue)))
  187. then <<w := simp car x;
  188. for each el in cdr x do
  189. for i := 1:cdr el do
  190. w := diffsq(w,car el);
  191. go to e>>
  192. else w := 'df . w
  193. >> else if !*expanddf and not atom cadr u then <<
  194. % Derivative of an algebraic operator u(a(v),...) via the
  195. % chain rule: df(u(v),v) = u_1(a(v),b(v),...)*df(a,v) + ...
  196. x := intern compress nconc(explode car u, '(!! !! !_));
  197. y := cdr u; w := nil ./ 1; m := 0;
  198. for each a in y do
  199. begin scalar b;
  200. m:=m#+1;
  201. if numr(b:=simp{'df,a,v}) then <<
  202. z := mkid(x, m);
  203. put(z, 'simpfn, 'simpiden);
  204. w := addsq(w, multsq(simp(z . y), b))
  205. >>
  206. end;
  207. go to e
  208. >> else w := {'df,u,v};
  209. j: if (x := opmtch w) then w := simp x
  210. else if not depends(u,v) and null !*depend then return nil ./ 1
  211. else w := mksq(w,1);
  212. go to e
  213. end$
  214. symbolic procedure dfform_int(u, v, n);
  215. % Simplify a SINGLE derivative of an integral.
  216. % u = '(int y x) [as main variable of SQ form]
  217. % v = kernel
  218. % n = integer power
  219. % Return SQ form of df(u**n, v) = n*u**(n-1)*df(u, v)
  220. % This routine is called by diffp via the hook
  221. % "if x := get(car u,'dfform) then return apply3(x,u,v,n)".
  222. % It does not necessarily need to use this hook, but it needs to be
  223. % called as an alternative to diffp so that the linearity of
  224. % differentiation has already been applied.
  225. begin scalar result, x, y, dx!/dv;
  226. y := simp!* cadr u; % SQ form integrand
  227. x := caddr u; % kernel
  228. result :=
  229. if v eq x then y
  230. % Special case -- just differentiate the integral:
  231. % df(int(y,x), x) -> y replacing the let rule in INT.RED
  232. else if not !*intflag!* and % not in the integrator
  233. % If used in the integrator it can cause infinite loops,
  234. % e.g. in df(int(int(f,x),y),x) and df(int(int(f,x),y),y)
  235. !*allowdfint and % must be on for dfint to work
  236. <<
  237. % Compute PARTIAL df(y, v), where y must depend on x, so
  238. % if x depends on v, temporarily replace x:
  239. result := if numr(dx!/dv:=diffp(x.**1,v)) then
  240. %% (Subst OK because all kernels.)
  241. subst(x, xx, diffsq(subst(xx, x, y), v)) where
  242. xx = gensym()
  243. else diffsq(y, v);
  244. !*dfint or not_df_p result
  245. >>
  246. then
  247. % Differentiate under the integral sign:
  248. % df(int(y,x), v) -> df(x,v)*y + int(df(y,v), x)
  249. addsq(
  250. multsq(dx!/dv, y),
  251. simp{'int, mk!*sq result, x}) % MUST re-simplify it!!!
  252. % (Perhaps I should use prepsq -
  253. % kernels are normally true prefix?)
  254. else !*kk2q{'df, u, v}; % remain unchanged
  255. if n neq 1 then
  256. result := multsq( (((u .** (n-1)) .* n) .+ nil) ./ 1, result);
  257. return result
  258. end$
  259. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  260. % Solve patches
  261. % =============
  262. % Needed for definition of `mkrootsoftag' function.
  263. packages_to_load solve$ % not at compile time!
  264. endmodule$
  265. end$
  266. % ***** NOT INSTALLED AT PRESENT *****
  267. %% An algebraic solver appropriate to ODESolve, that never returns
  268. %% implicit solutions and returns nil when it fails. Also, it solves
  269. %% quadratics in terms of plus_or_minus.
  270. %% *** NB: This messes up `root_multiplicities'. ***
  271. %% (Later also use the root_of_unity operator where appropriate.
  272. %% Could do this by a wrapper around `solvehipow' in solve/solv1.)
  273. % This switch controls `solve' globally once this file has been
  274. % loaded:
  275. switch plus_or_minus$ % off by default
  276. %% The integrator does not handle integrands containing the
  277. %% `plus_or_minus' operator at all well. This may require some
  278. %% re-writing of the integrator (!). Temporarily, just turn off the
  279. %% use of the `plus_or_minus' operator when necessary.
  280. % This switch controls some `solve' calls locally within `ODESolve':
  281. switch odesolve_plus_or_minus$ % TEMPORARY
  282. % off by default -- it's to odangerous at present!
  283. % !*odesolve_plus_or_minus := t$ % TEMPORARY
  284. symbolic operator AlgSolve$
  285. symbolic procedure AlgSolve(u, v);
  286. %% Return either a list of EXPLICIT solutions of a single scalar
  287. %% expression `u' for variable `v' or nil.
  288. begin scalar soln, tail, !*plus_or_minus;
  289. !*plus_or_minus := t;
  290. tail := cdr(soln := solveeval1{u, v});
  291. while tail do
  292. if car tail eq v then tail := cdr tail
  293. else tail := soln := nil;
  294. return soln
  295. end$
  296. algebraic procedure SolvePM(u, v);
  297. %% Solve a single scalar expression `u' for variable `v', using the
  298. %% `plus_or_minus' operator in the solution of quadratics.
  299. %% *** NB: This messes up `root_multiplicities'. ***
  300. symbolic(solveeval1{u, v}
  301. where !*plus_or_minus := !*odesolve_plus_or_minus)$
  302. %% This is a modified version of a routine in solve/quartic
  303. symbolic procedure solvequadratic(a2,a1,a0);
  304. % A2, a1 and a0 are standard quotients.
  305. % Solves a2*x**2+a1*x+a0=0 for x.
  306. % Returns a list of standard quotient solutions.
  307. % Modified to use root_val to compute numeric roots. SLK.
  308. if !*rounded and numcoef a0 and numcoef a1 and numcoef a2
  309. then for each z in cdr root_val list mkpolyexp2(a2,a1,a0)
  310. collect simp!* z else
  311. begin scalar d;
  312. d := sqrtq subtrsq(quotsqf(exptsq(a1,2),4),multsq(a2,a0));
  313. a1 := quotsqf(negsq a1,2);
  314. return
  315. if !*plus_or_minus then % FJW
  316. list(subs2!* quotsq(addsq(a1,
  317. multsq(!*kk2q newplus_or_minus(),d)),a2))
  318. %% Must uniquefy here until newplus_or_minus does it
  319. %% for itself!
  320. else
  321. list(subs2!* quotsq(addsq(a1,d),a2),
  322. subs2!* quotsq(subtrsq(a1,d),a2))
  323. end$
  324. endmodule$
  325. end$